From 7f6e26f66ab68fa46ae99514d2a6f59708c289ad Mon Sep 17 00:00:00 2001 From: Marc Jakobi Date: Wed, 28 Feb 2024 23:32:24 +0100 Subject: [PATCH] feat(api): ability to hook into `:Rocks install` and `:Rocks update` (#165) --- doc/rocks.txt | 33 ++- flake.lock | 518 ++++++++++++++++++++++++++++++++++++++- flake.nix | 6 + lua/rocks/api.lua | 24 +- lua/rocks/commands.lua | 2 +- lua/rocks/operations.lua | 120 +++++++-- nix/plugin-overlay.nix | 3 + 7 files changed, 676 insertions(+), 30 deletions(-) diff --git a/doc/rocks.txt b/doc/rocks.txt index f2914857..14d6516e 100644 --- a/doc/rocks.txt +++ b/doc/rocks.txt @@ -146,7 +146,7 @@ RockSpec *RockSpec* { name: rock_name, version?: string, opt?: boolean, [string]: V } -Specification for a rock in rocks.toml. +Specification for a rock in rocks.toml. May be extended by external modules. RocksToml *RocksToml* @@ -187,19 +187,44 @@ api.register_rocks_subcommand({name}, {cmd}) {cmd} (RocksCmd) +rock_config_table *rock_config_table* + + Type: ~ + table + + +rock_version *rock_version* + + Type: ~ + string + + +MutRocksTomlRef *MutRocksTomlRef* + + Fields: ~ + {rocks?} (rock_config_table) + {plugins?} (rock_config_table) + + + { rocks?: rock_config_table, plugins?: rocks_command_tbl, [string]: V } + +A mutable Lua representation of rocks.toml. May be extended by external modules. + 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 +An async callback that handles an operation on a rock. 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_handler_callback|nil) Return a function that prunes unused rocks, or `nil` if the handler cannot or does not need to prune any rocks. + {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_handler_callback|nil) Return a function that prunes unused rocks, or `nil` if the handler cannot or does not need to prune any rocks. + {get_install_callback?} (fun(rocks_toml:MutRocksTomlRef,arg_list:string[]):rock_handler_callback|nil) Return a function that installs a rock, or `nil` if the handler cannot install this rock. The `rocks_toml` table is mutable, and should be updated with the installed rock by the returned callback. + {get_update_callbacks?} (fun(rocks_toml:MutRocksTomlRef):rock_handler_callback[]) Return a list of functions that update user rocks, or an empty list if the handler cannot or does not need to update any rocks. The `rocks_toml` table is mutable, and should be updated by the returned callbacks. api.register_rock_handler({handler}) *api.register_rock_handler* diff --git a/flake.lock b/flake.lock index d77cdceb..b80afb41 100644 --- a/flake.lock +++ b/flake.lock @@ -48,6 +48,54 @@ "type": "github" } }, + "flake-compat_4": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_5": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_6": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -84,6 +132,42 @@ "type": "github" } }, + "flake-parts_3": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_3" + }, + "locked": { + "lastModified": 1706830856, + "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_4": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_4" + }, + "locked": { + "lastModified": 1706830856, + "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -156,6 +240,78 @@ "type": "github" } }, + "flake-utils_5": { + "inputs": { + "systems": "systems_5" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_6": { + "inputs": { + "systems": "systems_6" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_7": { + "inputs": { + "systems": "systems_7" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_8": { + "inputs": { + "systems": "systems_8" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "gen-luarc": { "inputs": { "flake-parts": "flake-parts_2", @@ -175,6 +331,25 @@ "type": "github" } }, + "gen-luarc_2": { + "inputs": { + "flake-parts": "flake-parts_4", + "nixpkgs": "nixpkgs_4" + }, + "locked": { + "lastModified": 1708688915, + "narHash": "sha256-Vcfbdo2IOEiimRnehGLUM5l2VEIjZYZdKS0sjYWwfb4=", + "owner": "mrcjkb", + "repo": "nix-gen-luarc-json", + "rev": "6eb62734dae84e5f79368dfc545b3fff305df754", + "type": "github" + }, + "original": { + "owner": "mrcjkb", + "repo": "nix-gen-luarc-json", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ @@ -218,6 +393,51 @@ "type": "github" } }, + "gitignore_3": { + "inputs": { + "nixpkgs": [ + "rocks-git", + "neorocks", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1703887061, + "narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gitignore_4": { + "inputs": { + "nixpkgs": [ + "rocks-git", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1703887061, + "narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "neorocks": { "inputs": { "flake-compat": "flake-compat", @@ -240,6 +460,28 @@ "type": "github" } }, + "neorocks_2": { + "inputs": { + "flake-compat": "flake-compat_4", + "flake-utils": "flake-utils_5", + "neovim-nightly": "neovim-nightly_2", + "nixpkgs": "nixpkgs_5", + "pre-commit-hooks": "pre-commit-hooks_3" + }, + "locked": { + "lastModified": 1708924959, + "narHash": "sha256-LdJ0YVsa06Q94+pE2m4P97d9N0PB0HC67wzjtzGO1KM=", + "owner": "nvim-neorocks", + "repo": "neorocks", + "rev": "8ce3f0ac2034d36b4dc058bd188cede27f96e569", + "type": "github" + }, + "original": { + "owner": "nvim-neorocks", + "repo": "neorocks", + "type": "github" + } + }, "neovim-nightly": { "inputs": { "flake-utils": "flake-utils_2", @@ -264,6 +506,31 @@ "type": "github" } }, + "neovim-nightly_2": { + "inputs": { + "flake-utils": "flake-utils_6", + "nixpkgs": [ + "rocks-git", + "neorocks", + "nixpkgs" + ] + }, + "locked": { + "dir": "contrib", + "lastModified": 1708913519, + "narHash": "sha256-S5d8OvfvPitMZPGa3BWhLvR8MjCkjpUVIc48auALFJs=", + "owner": "neovim", + "repo": "neovim", + "rev": "8b4e26915612caf2d143edca31919cae18a848a1", + "type": "github" + }, + "original": { + "dir": "contrib", + "owner": "neovim", + "repo": "neovim", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1708475490, @@ -316,6 +583,42 @@ "type": "github" } }, + "nixpkgs-lib_3": { + "locked": { + "dir": "lib", + "lastModified": 1706550542, + "narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "97b17f32362e475016f942bbdfda4a4a72a8a652", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib_4": { + "locked": { + "dir": "lib", + "lastModified": 1706550542, + "narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "97b17f32362e475016f942bbdfda4a4a72a8a652", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-stable": { "locked": { "lastModified": 1704874635, @@ -348,6 +651,38 @@ "type": "github" } }, + "nixpkgs-stable_3": { + "locked": { + "lastModified": 1704874635, + "narHash": "sha256-YWuCrtsty5vVZvu+7BchAxmcYzTMfolSPP5io8+WYCg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3dc440faeee9e889fe2d1b4d25ad0f430d449356", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable_4": { + "locked": { + "lastModified": 1704874635, + "narHash": "sha256-YWuCrtsty5vVZvu+7BchAxmcYzTMfolSPP5io8+WYCg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3dc440faeee9e889fe2d1b4d25ad0f430d449356", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_2": { "locked": { "lastModified": 1708692673, @@ -379,6 +714,53 @@ "type": "github" } }, + "nixpkgs_4": { + "locked": { + "lastModified": 1708475490, + "narHash": "sha256-g1v0TsWBQPX97ziznfJdWhgMyMGtoBFs102xSYO4syU=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "0e74ca98a74bc7270d28838369593635a5db3260", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_5": { + "locked": { + "lastModified": 1708847675, + "narHash": "sha256-RUZ7KEs/a4EzRELYDGnRB6i7M1Izii3JD/LyzH0c6Tg=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "2a34566b67bef34c551f204063faeecc444ae9da", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_6": { + "locked": { + "lastModified": 1708984012, + "narHash": "sha256-LX1RmWe4im1zja3A3dQLR71ErerySLemuBlnHqtu8Sg=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "9f6ae93dc822b0777f13d959eb9803e5c2a1b2a6", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, "pre-commit-hooks": { "inputs": { "flake-compat": "flake-compat_2", @@ -428,13 +810,87 @@ "type": "github" } }, + "pre-commit-hooks_3": { + "inputs": { + "flake-compat": "flake-compat_5", + "flake-utils": "flake-utils_7", + "gitignore": "gitignore_3", + "nixpkgs": [ + "rocks-git", + "neorocks", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable_3" + }, + "locked": { + "lastModified": 1708018599, + "narHash": "sha256-M+Ng6+SePmA8g06CmUZWi1AjG2tFBX9WCXElBHEKnyM=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "5df5a70ad7575f6601d91f0efec95dd9bc619431", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "pre-commit-hooks_4": { + "inputs": { + "flake-compat": "flake-compat_6", + "flake-utils": "flake-utils_8", + "gitignore": "gitignore_4", + "nixpkgs": [ + "rocks-git", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable_4" + }, + "locked": { + "lastModified": 1708018599, + "narHash": "sha256-M+Ng6+SePmA8g06CmUZWi1AjG2tFBX9WCXElBHEKnyM=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "5df5a70ad7575f6601d91f0efec95dd9bc619431", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "rocks-git": { + "inputs": { + "flake-parts": "flake-parts_3", + "gen-luarc": "gen-luarc_2", + "neorocks": "neorocks_2", + "nixpkgs": "nixpkgs_6", + "pre-commit-hooks": "pre-commit-hooks_4" + }, + "locked": { + "lastModified": 1708986075, + "narHash": "sha256-5cKV+QaGXBhh9xbBWsdv9wEMIhpTbNxlOy0/tfy/O5s=", + "owner": "nvim-neorocks", + "repo": "rocks-git.nvim", + "rev": "a61ca4fbba7dfdc30131b2698fb4e5ab4babb0e5", + "type": "github" + }, + "original": { + "owner": "nvim-neorocks", + "repo": "rocks-git.nvim", + "type": "github" + } + }, "root": { "inputs": { "flake-parts": "flake-parts", "gen-luarc": "gen-luarc", "neorocks": "neorocks", "nixpkgs": "nixpkgs_3", - "pre-commit-hooks": "pre-commit-hooks_2" + "pre-commit-hooks": "pre-commit-hooks_2", + "rocks-git": "rocks-git" } }, "systems": { @@ -496,6 +952,66 @@ "repo": "default", "type": "github" } + }, + "systems_5": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_6": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_7": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_8": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 551f033a..97108ff2 100644 --- a/flake.nix +++ b/flake.nix @@ -13,6 +13,10 @@ gen-luarc.url = "github:mrcjkb/nix-gen-luarc-json"; + rocks-git = { + url = "github:nvim-neorocks/rocks-git.nvim"; + }; + flake-parts.url = "github:hercules-ci/flake-parts"; pre-commit-hooks = { @@ -26,6 +30,7 @@ nixpkgs, neorocks, gen-luarc, + rocks-git, flake-parts, pre-commit-hooks, ... @@ -57,6 +62,7 @@ overlays = [ neorocks.overlays.default gen-luarc.overlays.default + rocks-git.overlays.default plugin-overlay test-overlay ]; diff --git a/lua/rocks/api.lua b/lua/rocks/api.lua index ac0e090b..3d1c2ef0 100644 --- a/lua/rocks/api.lua +++ b/lua/rocks/api.lua @@ -102,7 +102,7 @@ end ---@brief [[ --- { name: rock_name, version?: string, opt?: boolean, [string]: V } --- ----Specification for a rock in rocks.toml. +---Specification for a rock in rocks.toml. May be extended by external modules. ---@brief ]] ---@class RocksToml: { rocks?: RockSpec[], plugins?: RockSpec[], [string]: unknown } @@ -137,14 +137,30 @@ function api.register_rocks_subcommand(name, cmd) commands.register_subcommand(name, cmd) end +---@alias rock_config_table table + +---@alias rock_version string + +---@class MutRocksTomlRef +---@field rocks? rock_config_table +---@field plugins? rock_config_table + +---@brief [[ +--- { rocks?: rock_config_table, plugins?: rocks_command_tbl, [string]: V } +--- +---A mutable Lua representation of rocks.toml. May be extended by external modules. +---@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 +---An async callback that handles an operation on a rock. ---@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_handler_callback|nil Return a function that prunes unused rocks, or `nil` if the handler cannot or does not need to prune any rocks. +---@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_handler_callback|nil Return a function that prunes unused rocks, or `nil` if the handler cannot or does not need to prune any rocks. +---@field get_install_callback? fun(rocks_toml: MutRocksTomlRef, arg_list: string[]):rock_handler_callback|nil Return a function that installs a rock, or `nil` if the handler cannot install this rock. The `rocks_toml` table is mutable, and should be updated with the installed rock by the returned callback. +---@field get_update_callbacks? fun(rocks_toml: MutRocksTomlRef):rock_handler_callback[] Return a list of functions that update user rocks, or an empty list if the handler cannot or does not need to update any rocks. The `rocks_toml` table is mutable, and should be updated by the returned callbacks. ---@param handler RockHandler function api.register_rock_handler(handler) diff --git a/lua/rocks/commands.lua b/lua/rocks/commands.lua index b1be0ff3..109b3973 100644 --- a/lua/rocks/commands.lua +++ b/lua/rocks/commands.lua @@ -110,7 +110,7 @@ local rocks_command_tbl = { return end local package, version = args[1], args[2] - require("rocks.operations").add(package, version) + require("rocks.operations").add(args, package, version) end, complete = function(query) local name, version_query = query:match("([^%s]+)%s(.+)$") diff --git a/lua/rocks/operations.lua b/lua/rocks/operations.lua index ef48c13b..16ba267b 100644 --- a/lua/rocks/operations.lua +++ b/lua/rocks/operations.lua @@ -37,30 +37,69 @@ function operations.register_handler(handler) table.insert(_handlers, handler) end +--- `vim.schedule` a callback in an async context, +--- waiting for it to be executed +---@type async fun(func: fun()) +local vim_schedule_nio_wait = nio.create(function(func) + ---@cast func fun() + local future = nio.control.future() + vim.schedule(function() + func() + future.set(true) + end) + future.wait() +end, 1) + +---@param rocks_toml_ref MutRocksTomlRef +---@param arg_list string[] +---@return rock_handler_callback | nil +local function get_install_handler_callback(rocks_toml_ref, arg_list) + return vim.iter(_handlers) + :map(function(handler) + ---@cast handler RockHandler + local get_callback = handler.get_install_callback + return type(get_callback) == "function" and get_callback(rocks_toml_ref, arg_list) + end) + :find(function(callback) + return callback ~= nil + end) +end + ---@param spec RockSpec ----@return fun() | nil +---@return rock_handler_callback | 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) + return type(get_callback) == "function" and get_callback(spec) end) :find(function(callback) return callback ~= nil end) end +---@param rocks_toml_ref MutRocksTomlRef +---@return rock_handler_callback[] +local function get_update_handler_callbacks(rocks_toml_ref) + return vim.iter(_handlers) + :map(function(handler) + ---@cast handler RockHandler + return handler.get_update_callbacks(rocks_toml_ref) or {} + end) + :flatten() + :totable() +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 { [rock_name]: Rock|string } ----@alias rock_table { [rock_name]: Rock } +---@alias rock_table table ---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 } -local function parse_user_rocks() +---@return MutRocksTomlRef +local function parse_rocks_toml() local config_file = fs.read_or_create(config.config_path, constants.DEFAULT_CONFIG) return require("toml_edit").parse(config_file) end @@ -373,7 +412,7 @@ operations.sync = function(user_rocks) -- Tell external handlers to prune their rocks for _, handler in pairs(_handlers) do - local callback = handler.get_prune_callback(user_rocks) + local callback = type(handler.get_prune_callback) == "function" and handler.get_prune_callback(user_rocks) if callback then callback(report_progress, report_error) end @@ -471,9 +510,12 @@ operations.update = function() ) end - local user_rocks = parse_user_rocks() + local user_rocks = parse_rocks_toml() local outdated_rocks = state.outdated_rocks() + local external_update_handlers = get_update_handler_callbacks(user_rocks) + + local total_update_count = #outdated_rocks + #external_update_handlers nio.scheduler() @@ -502,21 +544,35 @@ operations.update = function() end progress_handle:report({ message = ("Updated %s: %s -> %s"):format(rock.name, rock.version, rock.target_version), - percentage = get_percentage(ct, #outdated_rocks), + percentage = get_percentage(ct, total_update_count), }) else report_error(("Failed to update %s."):format(rock.name)) progress_handle:report({ - percentage = get_percentage(ct, #outdated_rocks), + percentage = get_percentage(ct, total_update_count), + }) + end + end + for _, handler in pairs(external_update_handlers) do + local function report_progress(message) + progress_handle:report({ + message = message, }) end + handler(report_progress, report_error) + progress_handle:report({ + percentage = get_percentage(ct, total_update_count), + }) + ct = ct + 1 end - if vim.tbl_isempty(outdated_rocks) then - nio.scheduler() + if vim.tbl_isempty(outdated_rocks) and vim.tbl_isempty(external_update_handlers) then progress_handle:report({ message = "Nothing to update!", percentage = 100 }) + else + vim_schedule_nio_wait(function() + fs.write_file(config.config_path, "w", tostring(user_rocks)) + end) end - fs.write_file(config.config_path, "w", tostring(user_rocks)) nio.scheduler() if not vim.tbl_isempty(error_handles) then local message = "Update completed with errors!" @@ -543,22 +599,47 @@ operations.update = function() end --- Adds a new rock and updates the `rocks.toml` file ----@param rock_name string #The rock name +---@param arg_list string[] #Argument list, potentially used by external handlers +---@param rock_name rock_name #The rock name ---@param version? string #The version of the rock to use -operations.add = function(rock_name, version) +operations.add = function(arg_list, rock_name, version) local progress_handle = progress.handle.create({ title = "Installing", - message = version and ("%s -> %s"):format(rock_name, version) or rock_name, lsp_client = { name = constants.ROCKS_NVIM }, }) nio.run(function() + local user_rocks = parse_rocks_toml() + local handler = get_install_handler_callback(user_rocks, arg_list) + if type(handler) == "function" then + local function report_progress(message) + progress_handle:report({ + message = message, + }) + end + local function report_error(message) + progress_handle:report({ + title = "Error", + message = message, + }) + progress_handle:cancel() + end + handler(report_progress, report_error) + vim_schedule_nio_wait(function() + fs.write_file(config.config_path, "w", tostring(user_rocks)) + progress_handle:finish() + end) + return + end + progress_handle:report({ + message = version and ("%s -> %s"):format(rock_name, version) or rock_name, + }) local future = operations.install({ name = rock_name, version = version, }) local success, installed_rock = pcall(future.wait) - vim.schedule(function() + vim_schedule_nio_wait(function() if not success then progress_handle:report({ title = "Error", @@ -572,7 +653,6 @@ operations.add = function(rock_name, version) message = ("%s -> %s"):format(installed_rock.name, installed_rock.version), percentage = 100, }) - local user_rocks = parse_user_rocks() -- FIXME(vhyrro): This currently works in a half-baked way. -- The `toml-edit` libary will create a new empty table here, but if you were to try -- and populate the table upfront then none of the values will be registered by `toml-edit`. @@ -618,7 +698,7 @@ operations.prune = function(rock_name) lsp_client = { name = constants.ROCKS_NVIM }, }) nio.run(function() - local user_config = parse_user_rocks() + local user_config = parse_rocks_toml() if user_config.plugins then user_config.plugins[rock_name] = nil end @@ -629,7 +709,7 @@ operations.prune = function(rock_name) ---@diagnostic disable-next-line: invisible nio.fn.keys(vim.tbl_deep_extend("force", user_config.rocks or {}, user_config.plugins or {})) local success = operations.remove_recursive(rock_name, user_rock_names, progress_handle) - vim.schedule(function() + vim_schedule_nio_wait(function() fs.write_file(config.config_path, "w", tostring(user_config)) if success then progress_handle:finish() diff --git a/nix/plugin-overlay.nix b/nix/plugin-overlay.nix index 00b1c82d..7fd502e5 100644 --- a/nix/plugin-overlay.nix +++ b/nix/plugin-overlay.nix @@ -162,6 +162,9 @@ in { withPython3 = true; viAlias = false; vimAlias = false; + plugins = [ + final.vimPlugins.rocks-git-nvim + ]; }; runtimeDeps = with final; [ lua5_1