diff --git a/lua/rustaceanvim/lsp/init.lua b/lua/rustaceanvim/lsp/init.lua index 2147c385..38a89cae 100644 --- a/lua/rustaceanvim/lsp/init.lua +++ b/lua/rustaceanvim/lsp/init.lua @@ -6,9 +6,7 @@ local rust_analyzer = require('rustaceanvim.rust_analyzer') 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 rustc = require('rustaceanvim.rustc') local function override_apply_text_edits() local old_func = vim.lsp.util.apply_text_edits @@ -96,42 +94,14 @@ 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 bufnr? number +---@param bufnr? number The buffer number, defaults to the current buffer +---@param exclude_rustc_target? string 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(bufnr, callback) +local function restart(bufnr, exclude_rustc_target, callback) bufnr = bufnr or vim.api.nvim_get_current_buf() - local clients = M.stop(bufnr) + local clients = M.stop(bufnr, exclude_rustc_target) local timer, _, _ = vim.uv.new_timer() if not timer then vim.notify('Failed to init timer for LSP client restart.', vim.log.levels.ERROR) @@ -285,10 +255,11 @@ end ---Stop the LSP client. ---@param bufnr? number The buffer number, defaults to the current buffer +---@param exclude_rustc_target? string 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) +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) + local clients = rust_analyzer.get_active_rustaceanvim_clients(bufnr, { exclude_rustc_target = exclude_rustc_target }) vim.lsp.stop_client(clients) if type(clients) == 'table' then ---@cast clients vim.lsp.Client[] @@ -322,24 +293,13 @@ end ---Updates the target architecture setting for the LSP client associated with the given buffer. ---@param bufnr? number The buffer number, defaults to the current buffer ----@param target? string The target architecture. Defaults to nil(the current buffer's target if not provided). +---@param target? string Cargo target triple (e.g., 'x86_64-unknown-linux-gnu') to filter rust-analyzer clients M.set_target_arch = function(bufnr, target) + target = target or rustc.DEFAULT_RUSTC_TARGET ---@param client vim.lsp.Client - restart(bufnr, function(client) - -- Get current user's rust-analyzer target - local current_target = vim.tbl_get(client, 'config', 'settings', 'rust-analyzer', 'cargo', 'target') - - if not target then - if not current_target then - vim.notify('Using default OS target architecture.', vim.log.levels.INFO) - else - vim.notify('Target architecture is already set to the default OS target.', vim.log.levels.INFO) - end - return - end - - with_rustc_target_architectures(function(rustc_targets) - if target == nil or rustc_targets[target] then + restart(bufnr, target, function(client) + rustc.with_rustc_target_architectures(function(rustc_targets) + if rustc_targets[target] then client.settings['rust-analyzer'].cargo.target = target client.notify('workspace/didChangeConfiguration', { settings = client.config.settings }) vim.notify('Target architecture updated successfully to: ' .. target, vim.log.levels.INFO) @@ -354,10 +314,9 @@ end ---Restart the LSP client. ---Fails silently if the buffer's filetype is not one of the filetypes specified in the config. ----@param bufnr? number The buffer number (optional), defaults to the current buffer ---@return number|nil client_id The LSP client ID after restart -M.restart = function(bufnr) - M.restart(bufnr) +M.restart = function() + return restart() end ---@enum RustAnalyzerCmd @@ -372,7 +331,6 @@ local RustAnalyzerCmd = { local function rust_analyzer_cmd(opts) local fargs = opts.fargs local cmd = fargs[1] - local arch = fargs[2] ---@cast cmd RustAnalyzerCmd if cmd == RustAnalyzerCmd.start then M.start() @@ -383,7 +341,8 @@ local function rust_analyzer_cmd(opts) elseif cmd == RustAnalyzerCmd.reload_settings then M.reload_settings() elseif cmd == RustAnalyzerCmd.target then - M.set_target_arch(nil, arch) + local target_arch = fargs[2] + M.set_target_arch(target_arch) end end diff --git a/lua/rustaceanvim/rust_analyzer.lua b/lua/rustaceanvim/rust_analyzer.lua index 1ecdec06..219600fa 100644 --- a/lua/rustaceanvim/rust_analyzer.lua +++ b/lua/rustaceanvim/rust_analyzer.lua @@ -1,22 +1,51 @@ ---@mod rustaceanvim.rust_analyzer Functions for interacting with rust-analyzer local os = require('rustaceanvim.os') +local rustc = require('rustaceanvim.rustc') ---@class rustaceanvim.rust-analyzer.ClientAdapter local M = {} +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 + M.os_rustc_target = host + break + end + end + end + end) +end + +---@class rustaceanvim.lsp.get_clients.Filter: vim.lsp.get_clients.Filter +---@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? vim.lsp.get_clients.Filter +---@param filter? rustaceanvim.lsp.get_clients.Filter ---@return vim.lsp.Client[] M.get_active_rustaceanvim_clients = function(bufnr, filter) ---@type vim.lsp.get_clients.Filter - filter = vim.tbl_deep_extend('force', filter or {}, { + local client_filter = vim.tbl_deep_extend('force', filter or {}, { name = 'rust-analyzer', }) if bufnr then - filter.bufnr = bufnr + client_filter.bufnr = bufnr + end + local clients = vim.lsp.get_clients(client_filter) + if filter and filter.exclude_rustc_target then + clients = vim.tbl_filter(function(client) + local cargo_target = vim.tbl_get(client, 'config', 'settings', 'rust-analyzer', 'cargo', 'target') + if filter.exclude_rustc_target == rustc.DEFAULT_RUSTC_TARGET and cargo_target == nil then + return false + end + return cargo_target ~= filter.exclude_rustc_target + end, clients) end - return vim.lsp.get_clients(filter) + + return clients end ---@param method string LSP method name diff --git a/lua/rustaceanvim/rustc.lua b/lua/rustaceanvim/rustc.lua new file mode 100644 index 00000000..f9c43e3f --- /dev/null +++ b/lua/rustaceanvim/rustc.lua @@ -0,0 +1,39 @@ +local M = {} + +--- 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. +M.DEFAULT_RUSTC_TARGET = 'OS' + +---Local rustc targets cache +local rustc_targets_cache = nil + +---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 }, + ---@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 + +return M