@@ -7,6 +7,9 @@ local server_status = require('rustaceanvim.server_status')
7
7
local cargo = require (' rustaceanvim.cargo' )
8
8
local os = require (' rustaceanvim.os' )
9
9
10
+ --- Local rustc targets cache
11
+ local rustc_targets_cache = nil
12
+
10
13
local function override_apply_text_edits ()
11
14
local old_func = vim .lsp .util .apply_text_edits
12
15
--- @diagnostic disable-next-line
@@ -93,6 +96,71 @@ local function configure_file_watcher(server_cfg)
93
96
end
94
97
end
95
98
99
+ --- Handles retrieving rustc target archs and running on_valid callback
100
+ --- to perform certain actions using the retrieved targets.
101
+ --- @param on_valid function (rustc_targets )
102
+ local function validate_rustc_target (on_valid )
103
+ local on_exit = function (result )
104
+ -- use cache if available.
105
+ if rustc_targets_cache then
106
+ return on_valid (rustc_targets_cache )
107
+ end
108
+
109
+ if result .code ~= 0 then
110
+ error (' Failed to retrieve rustc targets: ' .. result .stderr )
111
+ end
112
+
113
+ rustc_targets_cache = {}
114
+ for line in result .stdout :gmatch (' [^\r\n ]+' ) do
115
+ rustc_targets_cache [line ] = true
116
+ end
117
+
118
+ return on_valid (rustc_targets_cache )
119
+ end
120
+
121
+ -- Call vim.system with on_exit callback to avoid blocking Neovim's event loop.
122
+ vim .system ({ ' rustc' , ' --print' , ' target-list' }, { text = true }, on_exit )
123
+ end
124
+
125
+ --- LSP restart internal implementations
126
+ --- @param bufnr ? number
127
+ --- @param callback ? function (client ) Optional callback to run for each client before restarting.
128
+ --- @return number | nil client_id
129
+ local function restart (bufnr , callback )
130
+ bufnr = bufnr or vim .api .nvim_get_current_buf ()
131
+ local clients = M .stop (bufnr )
132
+ local timer , _ , _ = vim .uv .new_timer ()
133
+ if not timer then
134
+ vim .notify (' Failed to init timer for LSP client restart.' , vim .log .levels .ERROR )
135
+ return
136
+ end
137
+ local attempts_to_live = 50
138
+ local stopped_client_count = 0
139
+ timer :start (200 , 100 , function ()
140
+ for _ , client in ipairs (clients ) do
141
+ if client :is_stopped () then
142
+ stopped_client_count = stopped_client_count + 1
143
+ vim .schedule (function ()
144
+ -- Execute the callback, if provided, for additional actions before restarting
145
+ if callback then
146
+ callback (client )
147
+ end
148
+ M .start (bufnr )
149
+ end )
150
+ end
151
+ end
152
+ if stopped_client_count >= # clients then
153
+ timer :stop ()
154
+ attempts_to_live = 0
155
+ elseif attempts_to_live <= 0 then
156
+ vim .notify (' rustaceanvim.lsp: Could not restart all LSP clients.' , vim .log .levels .ERROR )
157
+ timer :stop ()
158
+ attempts_to_live = 0
159
+ end
160
+ attempts_to_live = attempts_to_live - 1
161
+ end )
162
+ end
163
+
96
164
--- @class rustaceanvim.lsp.StartConfig : rustaceanvim.lsp.ClientConfig
97
165
--- @field root_dir string | nil
98
166
--- @field init_options ? table
@@ -249,39 +317,50 @@ M.reload_settings = function(bufnr)
249
317
return clients
250
318
end
251
319
320
+ --- Updates the target architecture setting for the LSP client associated with the given buffer.
321
+ --- @param bufnr ? number The buffer number , defaults to the current buffer
322
+ --- @param target ? string The target architecture. Defaults to nil (the current buffer ' s target if not provided).
323
+ M .set_target_arch = function (bufnr , target )
324
+ local on_update_target = function (client )
325
+ -- Get current user's rust-analyzer target
326
+ local current_target = vim .tbl_get (client , ' config' , ' settings' , ' rust-analyzer' , ' cargo' , ' target' )
327
+
328
+ if not target then
329
+ if not current_target then
330
+ vim .notify (' Using default OS target architecture.' , vim .log .levels .INFO )
331
+ else
332
+ vim .notify (' Target architecture is already set to the default OS target.' , vim .log .levels .INFO )
333
+ end
334
+ return
335
+ end
336
+
337
+ --- on_valid callback handles the main functionality in changing system's arch
338
+ --- by checking if rustc targets contains user's target or if user's provided target is nil.
339
+ --- Notifies user on unrecognized target arch request
340
+ local on_valid = function (rustc_tgs )
341
+ if target == nil or rustc_tgs [target ] then
342
+ client .settings [' rust-analyzer' ].cargo .target = target
343
+ client .notify (' workspace/didChangeConfiguration' , { settings = client .config .settings })
344
+ vim .notify (' Target architecture updated successfully to: ' .. target , vim .log .levels .INFO )
345
+ return
346
+ else
347
+ vim .notify (' Invalid target architecture provided: ' .. tostring (target ), vim .log .levels .ERROR )
348
+ return
349
+ end
350
+ end
351
+
352
+ return validate_rustc_target (on_valid )
353
+ end
354
+
355
+ restart (bufnr , on_update_target )
356
+ end
357
+
252
358
--- Restart the LSP client.
253
359
--- Fails silently if the buffer's filetype is not one of the filetypes specified in the config.
254
360
--- @param bufnr ? number The buffer number (optional ), defaults to the current buffer
255
361
--- @return number | nil client_id The LSP client ID after restart
256
362
M .restart = function (bufnr )
257
- bufnr = bufnr or vim .api .nvim_get_current_buf ()
258
- local clients = M .stop (bufnr )
259
- local timer , _ , _ = vim .uv .new_timer ()
260
- if not timer then
261
- -- TODO: Log error when logging is implemented
262
- return
263
- end
264
- local attempts_to_live = 50
265
- local stopped_client_count = 0
266
- timer :start (200 , 100 , function ()
267
- for _ , client in ipairs (clients ) do
268
- if client :is_stopped () then
269
- stopped_client_count = stopped_client_count + 1
270
- vim .schedule (function ()
271
- M .start (bufnr )
272
- end )
273
- end
274
- end
275
- if stopped_client_count >= # clients then
276
- timer :stop ()
277
- attempts_to_live = 0
278
- elseif attempts_to_live <= 0 then
279
- vim .notify (' rustaceanvim.lsp: Could not restart all LSP clients.' , vim .log .levels .ERROR )
280
- timer :stop ()
281
- attempts_to_live = 0
282
- end
283
- attempts_to_live = attempts_to_live - 1
284
- end )
363
+ M .restart (bufnr )
285
364
end
286
365
287
366
--- @enum RustAnalyzerCmd
@@ -290,11 +369,13 @@ local RustAnalyzerCmd = {
290
369
stop = ' stop' ,
291
370
restart = ' restart' ,
292
371
reload_settings = ' reloadSettings' ,
372
+ target = ' target' ,
293
373
}
294
374
295
375
local function rust_analyzer_cmd (opts )
296
376
local fargs = opts .fargs
297
377
local cmd = fargs [1 ]
378
+ local arch = fargs [2 ]
298
379
--- @cast cmd RustAnalyzerCmd
299
380
if cmd == RustAnalyzerCmd .start then
300
381
M .start ()
@@ -304,12 +385,14 @@ local function rust_analyzer_cmd(opts)
304
385
M .restart ()
305
386
elseif cmd == RustAnalyzerCmd .reload_settings then
306
387
M .reload_settings ()
388
+ elseif cmd == RustAnalyzerCmd .target then
389
+ M .set_target_arch (nil , arch )
307
390
end
308
391
end
309
392
310
393
vim .api .nvim_create_user_command (' RustAnalyzer' , rust_analyzer_cmd , {
311
394
nargs = ' +' ,
312
- desc = ' Starts or stops the rust-analyzer LSP client' ,
395
+ desc = ' Starts, stops the rust-analyzer LSP client or changes the target ' ,
313
396
complete = function (arg_lead , cmdline , _ )
314
397
local clients = rust_analyzer .get_active_rustaceanvim_clients ()
315
398
--- @type RustAnalyzerCmd[]
0 commit comments