Skip to content

Commit

Permalink
feat(api): allow external modules to hook into :Rocks sync (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcjkb authored Dec 17, 2023
1 parent c1ef187 commit 9b66e52
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 38 deletions.
66 changes: 59 additions & 7 deletions doc/rocks.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ rocks.nvim ··································
rocks.nvim commands ··········································· |rocks.commands|
rocks.nvim configuration ········································ |rocks.config|
rocks.nvim Lua API ················································· |rocks.api|
rocks.nvim logging API ············································· |rocks.log|

==============================================================================
rocks.nvim *rocks*
Expand Down Expand Up @@ -62,19 +63,19 @@ The Lua API for rocks.nvim.
Intended for use by modules that extend this plugin.


Rock *Rock*

Fields: ~
{name} (string)
{version} (string)


rock_name *rock_name*

Type: ~
string


Rock *Rock*

Fields: ~
{name} (rock_name)
{version} (string)


api.try_get_cached_rocks() *api.try_get_cached_rocks*
Tries to get the cached rocks.
Returns an empty list if the cache has not been populated
Expand Down Expand Up @@ -154,4 +155,55 @@ api.register_rocks_subcommand({name}, {cmd})
{cmd} (RocksCmd)


RockSpec *RockSpec*


{ name: rock_name, version?: string, [string]: V }

Specification for a rock in rocks.toml.

rock_handler_callback *rock_handler_callback*

Type: ~
fun(report_progress:fun(message:string),report_error:fun(message:string))


A function that operates on the rock, syncing it with the entry in rocks.toml

RockHandler *RockHandler*

Fields: ~
{get_sync_callback} (fun(spec:RockSpec):rock_handler_callback|nil) Return a function that installs or updates the rock, or `nil` if the handler cannot or does not need to sync the rock.
{get_prune_callback} (fun(specs:table<rock_name,RockSpec>):rock_handler_callback|nil) Return a function that prunes unused rocks, or `nil` if the handler cannot or does not need to prune any rocks.


api.register_rock_handler({handler}) *api.register_rock_handler*

Parameters: ~
{handler} (RockHandler)


==============================================================================
rocks.nvim logging API *rocks.log*


The logging interface for rocks.nvim.
Intended to be used by external modules.


log.trace() *log.trace*


log.debug() *log.debug*


log.info() *log.info*


log.warn() *log.warn*


log.error() *log.error*


vim:tw=78:ts=8:noet:ft=help:norl:
28 changes: 25 additions & 3 deletions lua/rocks/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
-- Homepage: https://github.com/nvim-neorocks/rocks.nvim
-- Maintainer: NTBBloodbath <[email protected]>

---@alias rock_name string

---@class Rock
---@field name string
---@field name rock_name
---@field version string

---@alias rock_name string

local api = {}

local cache = require("rocks.cache")
Expand All @@ -33,6 +33,7 @@ local fzy = require("rocks.fzy")
local luarocks = require("rocks.luarocks")
local nio = require("nio")
local state = require("rocks.state")
local operations = require("rocks.operations")

---Tries to get the cached rocks.
---Returns an empty list if the cache has not been populated
Expand Down Expand Up @@ -117,4 +118,25 @@ function api.register_rocks_subcommand(name, cmd)
commands.register_subcommand(name, cmd)
end

---@class RockSpec: { name: rock_name, version?: string, [string]: any }
---@brief [[
--- { name: rock_name, version?: string, [string]: V }
---
---Specification for a rock in rocks.toml.
---@brief ]]

---@alias rock_handler_callback fun(report_progress: fun(message: string), report_error: fun(message: string))
---@brief [[
---A function that operates on the rock, syncing it with the entry in rocks.toml
---@brief ]]

---@class RockHandler
---@field get_sync_callback fun(spec: RockSpec):rock_handler_callback|nil Return a function that installs or updates the rock, or `nil` if the handler cannot or does not need to sync the rock.
---@field get_prune_callback fun(specs: table<rock_name, RockSpec>):rock_handler_callback|nil Return a function that prunes unused rocks, or `nil` if the handler cannot or does not need to prune any rocks.

---@param handler RockHandler
function api.register_rock_handler(handler)
operations.register_handler(handler)
end

return api
44 changes: 27 additions & 17 deletions lua/rocks/log.lua
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
---@mod rocks.log rocks.nvim logging
---@mod rocks.log rocks.nvim logging API
---
---@brief [[
---
---The internal logging interface for rocks.nvim
---The logging interface for rocks.nvim.
---Intended to be used by external modules.
---
---@brief ]]

Expand All @@ -15,15 +16,21 @@
-- Homepage: https://github.com/nvim-neorocks/rocks.nvim
-- Maintainer: NTBBloodbath <[email protected]>

local log = {
-- NOTE: These functions are initialised as empty for type checking purposes
-- and implemented later.
trace = function(_) end,
debug = function(_) end,
info = function(_) end,
warn = function(_) end,
error = function(_) end,
}
local log = {}

-- NOTE: These functions are initialised as empty for type checking purposes
-- and implemented later.

---@type fun(any)
function log.trace(_) end
---@type fun(any)
function log.debug(_) end
---@type fun(any)
function log.info(_) end
---@type fun(any)
function log.warn(_) end
---@type fun(any)
function log.error(_) end

local LARGE = 1e9

Expand All @@ -37,20 +44,22 @@ end
local logfilename = vim.fn.tempname() .. "-rocks-nvim.log"

---Get the rocks.nvim log file path.
---@package
---@return string filepath
function log.get_logfile()
return logfilename
end

---Open the rocks.nvim log file.
---@package
function log.open_logfile()
vim.cmd.e(log.get_logfile())
end

local logfile, openerr
--- @private
--- Opens log file. Returns true if file is open, false on error
--- @return boolean
---@private
---Opens log file. Returns true if file is open, false on error
---@return boolean
local function open_logfile()
-- Try to open file only once
if logfile then
Expand Down Expand Up @@ -79,9 +88,10 @@ local function open_logfile()
return true
end

--- Set the log level
--- @param level (string|integer) The log level
--- @see vim.log.levels
---Set the log level
---@param level (string|integer) The log level
---@see vim.log.levels
---@package
function log.set_level(level)
local log_levels = vim.deepcopy(vim.log.levels)
vim.tbl_add_reverse_lookup(log_levels)
Expand Down
71 changes: 61 additions & 10 deletions lua/rocks/operations.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,34 @@ local progress = require("fidget.progress")

local operations = {}

---@type RockHandler[]
local _handlers = {}

---@param handler RockHandler
function operations.register_handler(handler)
table.insert(_handlers, handler)
end

---@param spec RockSpec
---@return fun() | nil
local function get_sync_handler_callback(spec)
return vim.iter(_handlers)
:map(function(handler)
---@cast handler RockHandler
local get_callback = handler.get_sync_callback
return get_callback and get_callback(spec)
end)
:find(function(callback)
return callback ~= nil
end)
end

---@class (exact) Future
---@field wait fun() Wait in an async context. Does not block in a sync context
---@field wait_sync fun() Wait in a sync context

---@alias rock_config_table { [string]: Rock|string }
---@alias rock_table { [string]: Rock }
---@alias rock_config_table { [rock_name]: Rock|string }
---@alias rock_table { [rock_name]: Rock }

---Decode the user rocks from rocks.toml, creating a default config file if it does not exist
---@return { rocks?: rock_config_table, plugins?: rock_config_table }
Expand Down Expand Up @@ -180,7 +202,7 @@ end)
--- - Installs missing rocks
--- - Ensures that the correct versions are installed
--- - Uninstalls unneeded rocks
---@param user_rocks? { [string]: Rock|string } loaded from rocks.toml if `nil`
---@param user_rocks? table<rock_name, RockSpec|string> loaded from rocks.toml if `nil`
operations.sync = function(user_rocks)
log.info("syncing...")
nio.run(function()
Expand All @@ -203,7 +225,11 @@ operations.sync = function(user_rocks)
})
)
end

local function report_progress(message)
progress_handle:report({
message = message,
})
end
if user_rocks == nil then
-- Read or create a new config file and decode it
-- NOTE: This does not use parse_user_rocks because we decode with toml, not toml-edit
Expand All @@ -217,13 +243,16 @@ operations.sync = function(user_rocks)
for name, data in pairs(user_rocks) do
-- TODO(vhyrro): Good error checking
if type(data) == "string" then
---@type RockSpec
user_rocks[name] = {
name = name,
version = data,
}
else
user_rocks[name].name = name
end
end
---@cast user_rocks rock_table
---@cast user_rocks table<rock_name, RockSpec>

local installed_rocks = state.installed_rocks()

Expand All @@ -233,33 +262,47 @@ operations.sync = function(user_rocks)
---@diagnostic disable-next-line: invisible
local key_list = nio.fn.keys(vim.tbl_deep_extend("force", installed_rocks, user_rocks))

local external_actions = vim.empty_dict()
---@cast external_actions rock_handler_callback[]
local to_install = vim.empty_dict()
---@cast to_install string[]
local to_updowngrade = vim.empty_dict()
---@cast to_updowngrade string[]
local to_prune = vim.empty_dict()
---@cast to_prune string[]
for _, key in ipairs(key_list) do
if user_rocks[key] and not installed_rocks[key] then
local user_rock = user_rocks[key]
local callback = user_rock and get_sync_handler_callback(user_rock)
if callback then
table.insert(external_actions, callback)
elseif user_rocks and not installed_rocks[key] then
table.insert(to_install, key)
elseif
user_rocks[key]
user_rock
and user_rock.version
and installed_rocks[key]
and user_rocks[key].version ~= installed_rocks[key].version
and user_rock.version ~= installed_rocks[key].version
then
table.insert(to_updowngrade, key)
elseif not user_rocks[key] and installed_rocks[key] then
elseif not user_rock and installed_rocks[key] then
table.insert(to_prune, key)
end
end

local ct = 1
local action_count = #to_install + #to_updowngrade + #to_prune
---@diagnostic disable-next-line: invisible
local action_count = #to_install + #to_updowngrade + #to_prune + #external_actions

local function get_progress_percentage()
return get_percentage(ct, action_count)
end

-- Sync actions handled by external modules that have registered handlers
for _, callback in ipairs(external_actions) do
ct = ct + 1
callback(report_progress, report_error)
end

for _, key in ipairs(to_install) do
nio.scheduler()
progress_handle:report({
Expand Down Expand Up @@ -328,6 +371,14 @@ operations.sync = function(user_rocks)
end
end

-- Tell external handlers to prune their rocks
for _, handler in pairs(_handlers) do
local callback = handler.get_prune_callback(user_rocks)
if callback then
callback(report_progress, report_error)
end
end

---@type string[]
local prunable_rocks = vim.iter(to_prune)
:filter(function(key)
Expand Down
2 changes: 1 addition & 1 deletion nix/test-overlay.nix
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
];
text = ''
mkdir -p doc
lemmy-help lua/rocks/{init,commands,config/init,api}.lua > doc/rocks.txt
lemmy-help lua/rocks/{init,commands,config/init,api,log}.lua > doc/rocks.txt
'';
};
in {
Expand Down

0 comments on commit 9b66e52

Please sign in to comment.