diff --git a/lua/rocks/cache.lua b/lua/rocks/cache.lua index 2bc87661..17058ad4 100644 --- a/lua/rocks/cache.lua +++ b/lua/rocks/cache.lua @@ -19,11 +19,15 @@ local cache = {} local luarocks = require("rocks.luarocks") local state = require("rocks.state") +local constants = require("rocks.constants") local nio = require("nio") ----@type { [string]: Rock[] } | nil +---@type { [rock_name]: Rock[] } | nil local _cached_rocks = nil +---@type { [rock_name]: Rock[] } | nil +local _cached_dev_binaries = nil + ---Used for completions only ---@type string[] | nil local _removable_rock_cache = nil @@ -84,4 +88,41 @@ cache.invalidate_removable_rocks = function() _removable_rock_cache = nil end +---Search the cache for rocks-binaries-dev rocks. +---Repopulates the cache and runs a second search if not found +---@type async fun(rock_name: string, version: string?) +cache.search_binary_dev_rocks = nio.create(function(rock_name, version) + ---@cast rock_name rock_name + ---@cast version string + local function search_cache() + local rocks + if _cached_dev_binaries then + rocks = _cached_dev_binaries[rock_name] + end + return rocks + and vim.iter(rocks):any(function(rock) + ---@cast rock Rock + if version == "dev" then + version = "scm" + end + return not version or rock.version == version + end) + end + local found = search_cache() + if found then + return found + end + local future = nio.control.future() + luarocks.search_all(function(result) + if not vim.tbl_isempty(result) then + _cached_dev_binaries = result + end + future.set(true) + end, { + servers = constants.ROCKS_BINARIES_DEV, + }) + future.wait() + return search_cache() +end, 2) + return cache diff --git a/lua/rocks/constants.lua b/lua/rocks/constants.lua index e7e8415c..f64dc70b 100644 --- a/lua/rocks/constants.lua +++ b/lua/rocks/constants.lua @@ -51,6 +51,27 @@ constants.DEFAULT_CONFIG = string.format( ---@type string constants.ROCKS_NVIM = "rocks.nvim" +---@alias server_url string +---@alias only_server_url string + +--- WARNING: The servers are prioritised by luarocks in the reverse order +--- in which they are passed +---@type server_url[] +constants.ROCKS_SERVERS = { + "https://luarocks.org/manifests/neorocks/", + "https://nvim-neorocks.github.io/rocks-binaries/", +} + +---@type only_server_url +constants.ROCKS_BINARIES_DEV = "https://nvim-neorocks.github.io/rocks-binaries-dev/" + +---@type server_url[] +constants.DEV_SERVERS = { + constants.ROCKS_BINARIES_DEV, +} + +constants.ALL_SERVERS = vim.list_extend(constants.DEV_SERVERS, constants.ROCKS_SERVERS) + return constants --- constants.lua diff --git a/lua/rocks/luarocks.lua b/lua/rocks/luarocks.lua index 92afdd5f..615b31fd 100644 --- a/lua/rocks/luarocks.lua +++ b/lua/rocks/luarocks.lua @@ -23,11 +23,28 @@ local log = require("rocks.log") local nio = require("nio") ---@class LuarocksCliOpts: vim.SystemOpts +---@field servers? server_url[] | only_server_url ---@field synchronized? boolean Whether to wait for and acquire a lock (recommended for file system IO, default: `true`) local lock = nio.control.future() lock.set(true) -- initialise as unlocked +--- --only-server if `servers` is a `string`, otherwise --server for each element +---@param servers server_url[]|only_server_url|nil +---@return string[] +local function mk_server_args(servers) + if type(servers) == "string" then + ---@cast servers string + return { ("--only-server='%s'"):format(servers) } + end + return vim.iter(servers or {}) + :map(function(server) + ---@cast server string + return ("--server='%s'"):format(server) + end) + :totable() +end + ---@param args string[] luarocks CLI arguments ---@param on_exit (function|nil) Called asynchronously when the luarocks command exits. --- asynchronously. Receives SystemCompleted object, see return of SystemObj:wait(). @@ -58,29 +75,36 @@ luarocks.cli = function(args, on_exit, opts) LUAROCKS_CONFIG = "", TREE_SITTER_LANGUAGE_VERSION = tostring(vim.treesitter.language_version), }) - local luarocks_cmd = vim.list_extend({ + local luarocks_cmd = { config.luarocks_binary, "--lua-version=" .. constants.LUA_VERSION, "--tree=" .. config.rocks_path, - -- WARNING: The servers are prioritised by luarocks in the reverse order - -- in which they are passed - "--server='https://luarocks.org/manifests/neorocks'", - "--server='https://nvim-neorocks.github.io/rocks-binaries/'", - }, args) + } + luarocks_cmd = vim.list_extend(luarocks_cmd, mk_server_args(opts.servers)) + luarocks_cmd = vim.list_extend(luarocks_cmd, args) log.info(luarocks_cmd) return vim.system(luarocks_cmd, opts, on_exit_wrapped) end +---@class LuarocksSearchOpts +---@field dev? boolean Include dev manifest? Default: false +---@field servers? server_url[]|only_server_url Optional servers. Defaults to constants.ROCKS_SERVERS + ---Search luarocks.org for all packages. ----@type async fun(callback: fun(rocks_table: { [string]: Rock } )) -luarocks.search_all = nio.create(function(callback) +---@type async fun(callback: fun(rocks_table: { [string]: Rock } ), opts?: LuarocksSearchOpts) +luarocks.search_all = nio.create(function(callback, opts) + ---@cast opts LuarocksSearchOpts | nil local rocks_table = vim.empty_dict() ---@cast rocks_table { [string]: Rock } local future = nio.control.future() - luarocks.cli({ "search", "--porcelain", "--all", "--dev" }, function(obj) + local cmd = { "search", "--porcelain", "--all" } + if opts and opts.dev then + table.insert(cmd, "--dev") + end + luarocks.cli(cmd, function(obj) ---@cast obj vim.SystemCompleted future.set(obj) - end, { text = true, synchronized = false }) + end, { text = true, synchronized = false, servers = opts and opts.servers or constants.ALL_SERVERS }) ---@type vim.SystemCompleted local obj = future.wait() local result = obj.stdout @@ -88,7 +112,7 @@ luarocks.search_all = nio.create(function(callback) callback(vim.empty_dict()) return end - for name, version in result:gmatch("(%S+)%s+(%S+)%srockspec%s+[^\n]+") do + for name, version in result:gmatch("(%S+)%s+(%S+)%s+[^\n]+") do if name ~= "lua" then local rock_list = rocks_table[name] or vim.empty_dict() ---@cast rock_list Rock[] diff --git a/lua/rocks/operations/helpers.lua b/lua/rocks/operations/helpers.lua index ea189cea..831e6cf1 100644 --- a/lua/rocks/operations/helpers.lua +++ b/lua/rocks/operations/helpers.lua @@ -17,6 +17,7 @@ ---@brief ]] local luarocks = require("rocks.luarocks") +local constants = require("rocks.constants") local config = require("rocks.config.internal") local runtime = require("rocks.runtime") local adapter = require("rocks.adapter") @@ -45,10 +46,18 @@ helpers.install = function(rock_spec, progress_handle) "install", name, } + local servers = {} + vim.list_extend(servers, constants.ROCKS_SERVERS) if version then -- If specified version is dev then install the `scm-1` version of the rock if version == "dev" or version == "scm" then - table.insert(install_cmd, 2, "--dev") + if cache.search_binary_dev_rocks(rock_spec.name, version) then + -- Rock found on rocks-binaries-dev + table.insert(servers, constants.ROCKS_BINARIES_DEV) + else + -- Search dev manifest + table.insert(install_cmd, 2, "--dev") + end else table.insert(install_cmd, version) end @@ -84,7 +93,9 @@ helpers.install = function(rock_spec, progress_handle) future.set(installed_rock) end - end) + end, { + servers = servers, + }) return { wait = future.wait, wait_sync = function() diff --git a/lua/rocks/operations/init.lua b/lua/rocks/operations/init.lua index b6c5e527..83749547 100644 --- a/lua/rocks/operations/init.lua +++ b/lua/rocks/operations/init.lua @@ -513,9 +513,9 @@ operations.add = function(arg_list, rock_name, version) user_rocks.plugins = plugins end - -- Set installed version as `scm-1` if development version has been installed + -- Set installed version as `scm` if development version has been installed if version == "dev" then - installed_rock.version = "scm-1" + installed_rock.version = "scm" end local user_rock = user_rocks.plugins[rock_name] if user_rock and user_rock.version then diff --git a/lua/rocks/state.lua b/lua/rocks/state.lua index 738bf2a6..0f8e3e9b 100644 --- a/lua/rocks/state.lua +++ b/lua/rocks/state.lua @@ -18,6 +18,7 @@ local state = {} local luarocks = require("rocks.luarocks") +local constants = require("rocks.constants") local log = require("rocks.log") local nio = require("nio") @@ -66,7 +67,7 @@ state.outdated_rocks = nio.create(function() else future.set(obj.stdout) end - end, { text = true }) + end, { text = true, servers = constants.ALL_SERVERS }) local installed_rock_list = future.wait()