diff --git a/lua/rustaceanvim/lsp/init.lua b/lua/rustaceanvim/lsp/init.lua index 6a5e933b..b815cf8f 100644 --- a/lua/rustaceanvim/lsp/init.lua +++ b/lua/rustaceanvim/lsp/init.lua @@ -7,6 +7,9 @@ local server_status = require('rustaceanvim.server_status') local cargo = require('rustaceanvim.cargo') local os = require('rustaceanvim.os') +---Local rustc targets cache +local rustc_targets_cache = nil + local function override_apply_text_edits() local old_func = vim.lsp.util.apply_text_edits ---@diagnostic disable-next-line @@ -93,8 +96,37 @@ local function configure_file_watcher(server_cfg) end end +---Handles retrieving rustc target architectures and running the passed in callback +---to perform certain actions using the retrieved targets. +---@param callback fun(targets: string[]) +local function with_rustc_target_architectures(callback) + if rustc_targets_cache then + return callback(rustc_targets_cache) + end + vim.system( + { 'rustc', '--print', 'target-list' }, + { text = true }, + ---@param result vim.SystemCompleted + function(result) + if result.code ~= 0 then + error('Failed to retrieve rustc targets: ' .. result.stderr) + end + rustc_targets_cache = vim.iter(result.stdout:gmatch('[^\r\n]+')):fold( + {}, + ---@param acc table + ---@param target string + function(acc, target) + acc[target] = true + return acc + end + ) + return callback(rustc_targets_cache) + end + ) +end + ---LSP restart internal implementations ----@param exclude_rustc_target? string Optional target architecture. Clients with target won't be restarted. +---@param exclude_rustc_target? string|nil Cargo target triple (e.g., 'x86_64-unknown-linux-gnu') to filter rust-analyzer clients ---@param callback? fun(client: vim.lsp.Client) Optional callback to run for each client before restarting. ---@return number|nil client_id local function restart(exclude_rustc_target, callback) @@ -253,11 +285,14 @@ end ---Stop the LSP client. ---@param bufnr? number The buffer number, defaults to the current buffer ----@param exclude_rustc_target? string Optional target architecture. Clients with target won't be stopped. +---@param exclude_rustc_target? string|nil Cargo target triple (e.g., 'x86_64-unknown-linux-gnu') to filter rust-analyzer clients ---@return vim.lsp.Client[] clients A list of clients that will be stopped M.stop = function(bufnr, exclude_rustc_target) bufnr = bufnr or vim.api.nvim_get_current_buf() - local clients = rust_analyzer.get_active_rustaceanvim_clients(bufnr, { exclude_rustc_target = exclude_rustc_target }) + local clients = rust_analyzer.get_active_rustaceanvim_clients( + bufnr, + { exclude_rustc_target = exclude_rustc_target or DEFAULT_RUSTC_TARGET } + ) vim.lsp.stop_client(clients) if type(clients) == 'table' then ---@cast clients vim.lsp.Client[] @@ -289,22 +324,21 @@ M.reload_settings = function() end ---Updates LSP client target architecture setting. ----@param target_arch? string string The target architecture, defaults to the current buffer's target if not provided. -M.set_target_arch = function(target_arch) - -- Load target architectures if not already available - if not rust_analyzer.rustc_target_archs or rust_analyzer.os_rustc_target then - rust_analyzer.load_target_archs() - end - target_arch = target_arch or rust_analyzer.os_rustc_target - if not rust_analyzer.rustc_target_archs[target_arch] then - vim.notify('Invalid target architecture provided: ' .. tostring(target_arch), vim.log.levels.ERROR) - return - end +---@param exclude_rustc_target? string|nil Cargo target triple (e.g., 'x86_64-unknown-linux-gnu') to filter rust-analyzer clients +M.set_target_arch = function(exclude_rustc_target) ---@param client vim.lsp.Client - restart(target_arch, function(client) - client.settings['rust-analyzer'].cargo.target = target_arch - client.notify('workspace/didChangeConfiguration', { settings = client.config.settings }) - vim.notify('Target architecture updated successfully to: ' .. target_arch, vim.log.levels.INFO) + restart(exclude_rustc_target, function(client) + with_rustc_target_architectures(function(rustc_targets) + if rustc_targets[exclude_rustc_target] then + client.settings['rust-analyzer'].cargo.target = exclude_rustc_target + client.notify('workspace/didchangeconfiguration', { settings = client.config.settings }) + vim.notify('target architecture updated successfully to: ' .. exclude_rustc_target, vim.log.levels.info) + return + else + vim.notify('invalid target architecture provided: ' .. tostring(exclude_rustc_target), vim.log.levels.error) + return + end + end) end) end @@ -357,11 +391,4 @@ vim.api.nvim_create_user_command('RustAnalyzer', rust_analyzer_cmd, { end, }) -vim.api.nvim_create_autocmd('BufEnter', { - once = true, - callback = function() - rust_analyzer.load_target_archs() - end, -}) - return M diff --git a/lua/rustaceanvim/rust_analyzer.lua b/lua/rustaceanvim/rust_analyzer.lua index 4c32f289..21a7a19d 100644 --- a/lua/rustaceanvim/rust_analyzer.lua +++ b/lua/rustaceanvim/rust_analyzer.lua @@ -5,20 +5,16 @@ local os = require('rustaceanvim.os') ---@class rustaceanvim.rust-analyzer.ClientAdapter local M = {} ----Local OS rustc target architecture -M.os_rustc_target_arch = nil ----Local rustc target architectures -M.rustc_target_archs = nil +--- Default target value for rustc when no specific target is provided. +--- Used as a fallback to let rustc determine the appropriate target based on the OS. +DEFAULT_RUSTC_TARGET = 'OS' -local load_os_rustc_target_arch = function() - vim.system( - { 'rustc', '-Vv' }, - { text = true }, - ---@param result vim.SystemCompleted - function(result) - if result.code ~= 0 then - error('Failed to retrieve OS rustc target: ' .. result.stderr) - end +---Local rustc targets cache +local rustc_targets_cache = nil + +M.load_os_rustc_target = function() + vim.system({ 'rustc', '-Vv' }, { text = true }, function(result) + if result.code == 0 then for line in result.stdout:gmatch('[^\r\n]+') do local host = line:match('^host:%s*(.+)$') if host then @@ -27,10 +23,16 @@ local load_os_rustc_target_arch = function() end end end - ) + end) end -local load_rustc_target_archs = function() +---Handles retrieving rustc target architectures and running the passed in callback +---to perform certain actions using the retrieved targets. +---@param callback fun(targets: string[]) +M.with_rustc_target_architectures = function(callback) + if rustc_targets_cache then + return callback(rustc_targets_cache) + end vim.system( { 'rustc', '--print', 'target-list' }, { text = true }, @@ -39,21 +41,22 @@ local load_rustc_target_archs = function() if result.code ~= 0 then error('Failed to retrieve rustc targets: ' .. result.stderr) end - M.rustc_target_archs = vim.iter(result.stdout:gmatch('[^\r\n]+')):fold({}, function(acc, target) - acc[target] = true - return acc - end) + rustc_targets_cache = vim.iter(result.stdout:gmatch('[^\r\n]+')):fold( + {}, + ---@param acc table + ---@param target string + function(acc, target) + acc[target] = true + return acc + end + ) + return callback(rustc_targets_cache) end ) end -M.load_target_archs = function() - load_os_rustc_target_arch() - load_rustc_target_archs() -end - ---@class rustaceanvim.lsp.get_clients.Filter: vim.lsp.get_clients.Filter ----@field exclude_rustc_target? string Optional cargo target to filter rust-analyzer clients +---@field exclude_rustc_target? string Cargo target triple (e.g., 'x86_64-unknown-linux-gnu') to filter rust-analyzer clients ---@param bufnr number | nil 0 for the current buffer, `nil` for no buffer filter ---@param filter? rustaceanvim.lsp.get_clients.Filter @@ -69,16 +72,14 @@ M.get_active_rustaceanvim_clients = function(bufnr, filter) local clients = vim.lsp.get_clients(client_filter) if filter and filter.exclude_rustc_target then clients = vim.tbl_filter(function(client) - --Rust analyzer uses nil for default OS target arch in config if not explicitly defined - local cargo_target_or_default = vim.tbl_get(client, 'config', 'settings', 'rust-analyzer', 'cargo', 'target') - or M.os_rustc_target - if cargo_target_or_default ~= filter.exclude_rustc_target then - return true + local cargo_target = vim.tbl_get(client, 'config', 'settings', 'rust-analyzer', 'cargo', 'target') + if filter.exclude_rustc_target == DEFAULT_RUSTC_TARGET and cargo_target == nil then + return false end - vim.notify('Target architecture is already set to the default OS target.', vim.log.levels.INFO) - return false + return cargo_target ~= filter.exclude_rustc_target end, clients) end + return clients end @@ -127,7 +128,6 @@ M.get_client_for_file = function(file_path, method) end end end - ---@param method string LSP method name ---@param params table|nil Parameters to send to the server M.notify = function(method, params)