forked from mrcjkb/rustaceanvim
-
Notifications
You must be signed in to change notification settings - Fork 0
/
internal.lua
430 lines (394 loc) · 13.9 KB
/
internal.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
local types = require('rustaceanvim.types.internal')
local cargo = require('rustaceanvim.cargo')
local compat = require('rustaceanvim.compat')
local config = require('rustaceanvim.config')
local executors = require('rustaceanvim.executors')
local os = require('rustaceanvim.os')
local server_config = require('rustaceanvim.config.server')
local RustaceanConfig
---@class RustAnalyzerInitializedStatusInternal : RustAnalyzerInitializedStatus
---@field health lsp_server_health_status
---@field quiescent boolean inactive?
---
---@param dap_adapter DapExecutableConfig | DapServerConfig | disable
---@return boolean
local function should_enable_dap_config_value(dap_adapter)
local adapter = types.evaluate(dap_adapter)
if adapter == false then
return false
end
return vim.fn.executable('rustc') == 1
end
---@param adapter DapServerConfig | DapExecutableConfig
local function is_codelldb_adapter(adapter)
return adapter.type == 'server'
end
---@param adapter DapServerConfig | DapExecutableConfig
local function is_lldb_adapter(adapter)
return adapter.type == 'executable'
end
---@param type string
---@return DapClientConfig
local function load_dap_configuration(type)
-- default
---@type DapClientConfig
local dap_config = {
name = 'Rust debug client',
type = type,
request = 'launch',
stopOnEntry = false,
}
---@diagnostic disable-next-line: different-requires
local dap = require('dap')
-- Load configurations from a `launch.json`.
-- It is necessary to check for changes in the `dap.configurations` table, as
-- `load_launchjs` does not return anything, it loads directly into `dap.configurations`.
local pre_launch = vim.deepcopy(dap.configurations) or {}
require('dap.ext.vscode').load_launchjs(nil, { lldb = { 'rust' }, codelldb = { 'rust' } })
for name, configuration_entries in pairs(dap.configurations) do
if pre_launch[name] == nil or not vim.deep_equal(pre_launch[name], configuration_entries) then
-- `configurations` are tables of `configuration` entries
-- use the first `configuration` that matches
for _, entry in pairs(configuration_entries) do
---@cast entry DapClientConfig
if entry.type == type then
dap_config = entry
break
end
end
end
end
return dap_config
end
---@return RustaceanExecutor
local function get_crate_test_executor()
if vim.fn.has('nvim-0.10.0') == 1 then
return executors.background
else
return executors.termopen
end
end
---@return RustaceanExecutor
local function get_test_executor()
if package.loaded['rustaceanvim.neotest'] ~= nil then
-- neotest has been set up with rustaceanvim as an adapter
return executors.neotest
end
return get_crate_test_executor()
end
---@class RustaceanConfig
local RustaceanDefaultConfig = {
---@class RustaceanToolsConfig
tools = {
--- how to execute terminal commands
--- options right now: termopen / quickfix / toggleterm / vimux
---@type RustaceanExecutor
executor = executors.termopen,
---@type RustaceanExecutor
test_executor = get_test_executor(),
---@type RustaceanExecutor
crate_test_executor = get_crate_test_executor(),
---@type string | nil
cargo_override = nil,
---@type boolean
enable_nextest = true,
---@type boolean
enable_clippy = true,
--- callback to execute once rust-analyzer is done initializing the workspace
--- The callback receives one parameter indicating the `health` of the server: "ok" | "warning" | "error"
---@type fun(health:RustAnalyzerInitializedStatus) | nil
on_initialized = nil,
--- automatically call RustReloadWorkspace when writing to a Cargo.toml file.
---@type boolean
reload_workspace_from_cargo_toml = true,
--- options same as lsp hover
---@see vim.lsp.util.open_floating_preview
---@class RustaceanHoverActionsConfig
hover_actions = {
--- whether to replace Neovim's built-in `vim.lsp.buf.hover`.
---@type boolean
replace_builtin_hover = true,
},
code_actions = {
--- text appended to a group action
---@type string
group_icon = ' ▶',
--- whether to fall back to `vim.ui.select` if there are no grouped code actions
---@type boolean
ui_select_fallback = false,
},
--- options same as lsp hover
---@see vim.lsp.util.open_floating_preview
---@see vim.api.nvim_open_win
---@type table Options applied to floating windows.
float_win_config = {
--- whether the window gets automatically focused
--- default: false
---@type boolean
auto_focus = false,
--- whether splits opened from floating preview are vertical
--- default: false
---@type 'horizontal' | 'vertical'
open_split = 'horizontal',
},
--- settings for showing the crate graph based on graphviz and the dot
--- command
---@class RustaceanCrateGraphConfig
crate_graph = {
-- backend used for displaying the graph
-- see: https://graphviz.org/docs/outputs/
-- default: x11
---@type string
backend = 'x11',
-- where to store the output, nil for no output stored (relative
-- path from pwd)
-- default: nil
---@type string | nil
output = nil,
-- true for all crates.io and external crates, false only the local
-- crates
-- default: true
---@type boolean
full = true,
-- List of backends found on: https://graphviz.org/docs/outputs/
-- Is used for input validation and autocompletion
-- Last updated: 2021-08-26
---@type string[]
enabled_graphviz_backends = {
'bmp',
'cgimage',
'canon',
'dot',
'gv',
'xdot',
'xdot1.2',
'xdot1.4',
'eps',
'exr',
'fig',
'gd',
'gd2',
'gif',
'gtk',
'ico',
'cmap',
'ismap',
'imap',
'cmapx',
'imap_np',
'cmapx_np',
'jpg',
'jpeg',
'jpe',
'jp2',
'json',
'json0',
'dot_json',
'xdot_json',
'pdf',
'pic',
'pct',
'pict',
'plain',
'plain-ext',
'png',
'pov',
'ps',
'ps2',
'psd',
'sgi',
'svg',
'svgz',
'tga',
'tiff',
'tif',
'tk',
'vml',
'vmlz',
'wbmp',
'webp',
'xlib',
'x11',
},
---@type string | nil
pipe = nil,
},
---@type fun(url:string):nil
open_url = function(url)
require('rustaceanvim.os').open_url(url)
end,
---settings for rustc
---@class RustaceanRustcConfig
rustc = {
---@type string
edition = '2021',
},
},
--- all the opts to send to the LSP client
--- these override the defaults set by rust-tools.nvim
---@diagnostic disable-next-line: undefined-doc-class
---@class RustaceanLspClientConfig: vim.lsp.ClientConfig
server = {
---@type lsp.ClientCapabilities
capabilities = server_config.create_client_capabilities(),
---@type boolean | fun(bufnr: integer):boolean Whether to automatically attach the LSP client.
---Defaults to `true` if the `rust-analyzer` executable is found.
auto_attach = function(bufnr)
if #vim.bo[bufnr].buftype > 0 then
return false
end
local path = vim.api.nvim_buf_get_name(bufnr)
if not os.is_valid_file_path(path) then
return false
end
local cmd = types.evaluate(RustaceanConfig.server.cmd)
---@cast cmd string[]
local rs_bin = cmd[1]
return vim.fn.executable(rs_bin) == 1
end,
---@type string[] | fun():string[]
cmd = function()
return { 'rust-analyzer', '--log-file', RustaceanConfig.server.logfile }
end,
---@type string | fun(filename: string, default: fun(filename: string):string|nil):string|nil
root_dir = cargo.get_root_dir,
--- standalone file support
--- setting it to false may improve startup time
---@type boolean
standalone = true,
---@type string The path to the rust-analyzer log file.
logfile = vim.fn.tempname() .. '-rust-analyzer.log',
---@type table | (fun(project_root:string|nil, default_settings: table|nil):table) -- The rust-analyzer settings or a function that creates them.
settings = function(project_root, default_settings)
return server_config.load_rust_analyzer_settings(project_root, { default_settings = default_settings })
end,
--- @type table
default_settings = {
--- options to send to rust-analyzer
--- See: https://rust-analyzer.github.io/manual.html#configuration
--- @type table
['rust-analyzer'] = {},
},
---@type boolean Whether to search (upward from the buffer) for rust-analyzer settings in .vscode/settings json.
load_vscode_settings = false,
},
--- debugging stuff
--- @class RustaceanDapConfig
dap = {
--- @type boolean Whether to autoload nvim-dap configurations when rust-analyzer has attached?
autoload_configurations = vim.fn.has('nvim-0.10.0') == 1, -- Compiling the debug build cannot be run asynchronously on Neovim < 0.10
--- @type DapExecutableConfig | DapServerConfig | disable | fun():(DapExecutableConfig | DapServerConfig | disable)
adapter = function()
--- @type DapExecutableConfig | DapServerConfig | disable
local result = false
local has_mason, mason_registry = pcall(require, 'mason-registry')
if has_mason and mason_registry.is_installed('codelldb') then
local codelldb_package = mason_registry.get_package('codelldb')
local mason_codelldb_path = compat.joinpath(codelldb_package:get_install_path(), 'extension')
local codelldb_path = compat.joinpath(mason_codelldb_path, 'adapter', 'codelldb')
local liblldb_path = compat.joinpath(mason_codelldb_path, 'lldb', 'lib', 'liblldb')
local shell = require('rustaceanvim.shell')
if shell.is_windows() then
codelldb_path = codelldb_path .. '.exe'
liblldb_path = compat.joinpath(mason_codelldb_path, 'lldb', 'bin', 'liblldb.dll')
else
liblldb_path = liblldb_path .. (shell.is_macos() and '.dylib' or '.so')
end
result = config.get_codelldb_adapter(codelldb_path, liblldb_path)
elseif vim.fn.executable('codelldb') == 1 then
---@cast result DapServerConfig
result = {
type = 'server',
host = '127.0.0.1',
port = '${port}',
executable = {
command = 'codelldb',
args = { '--port', '${port}' },
},
}
else
local has_lldb_dap = vim.fn.executable('lldb-dap') == 1
local has_lldb_vscode = vim.fn.executable('lldb-vscode') == 1
if not has_lldb_dap and not has_lldb_vscode then
return result
end
local command = has_lldb_dap and 'lldb-dap' or 'lldb-vscode'
---@cast result DapExecutableConfig
result = {
type = 'executable',
command = command,
name = 'lldb',
}
end
return result
end,
--- Accommodate dynamically-linked targets by passing library paths to lldb.
---@type boolean | fun():boolean
add_dynamic_library_paths = function()
return should_enable_dap_config_value(RustaceanConfig.dap.adapter)
end,
--- Auto-generate a source map for the standard library.
---@type boolean | fun():boolean
auto_generate_source_map = function()
return should_enable_dap_config_value(RustaceanConfig.dap.adapter)
end,
--- Get Rust types via initCommands (rustlib/etc/lldb_commands).
---@type boolean | fun():boolean
load_rust_types = function()
if not should_enable_dap_config_value(RustaceanConfig.dap.adapter) then
return false
end
local adapter = types.evaluate(RustaceanConfig.dap.adapter)
---@cast adapter DapExecutableConfig | DapServerConfig | disable
return adapter ~= false and is_lldb_adapter(adapter)
end,
--- @type DapClientConfig | disable | fun():(DapClientConfig | disable)
configuration = function()
local ok, _ = pcall(require, 'dap')
if not ok then
return false
end
local adapter = types.evaluate(RustaceanConfig.dap.adapter)
---@cast adapter DapExecutableConfig | DapServerConfig | disable
if adapter == false then
return false
end
---@cast adapter DapExecutableConfig | DapServerConfig
local type = is_codelldb_adapter(adapter) and 'codelldb' or 'lldb'
return load_dap_configuration(type)
end,
},
-- debug info
was_g_rustaceanvim_sourced = vim.g.rustaceanvim ~= nil,
}
local rustaceanvim = vim.g.rustaceanvim or {}
local opts = type(rustaceanvim) == 'function' and rustaceanvim() or rustaceanvim
for _, executor in pairs { 'executor', 'test_executor', 'crate_test_executor' } do
if opts.tools and opts.tools[executor] and type(opts.tools[executor]) == 'string' then
opts.tools[executor] = assert(executors[opts.tools[executor]], 'Unknown RustaceanExecutor')
end
end
---@type RustaceanConfig
RustaceanConfig = vim.tbl_deep_extend('force', {}, RustaceanDefaultConfig, opts)
-- Override user dap.adapter config in a backward compatible way
if opts.dap and opts.dap.adapter then
local user_adapter = opts.dap.adapter
local default_adapter = types.evaluate(RustaceanConfig.dap.adapter)
if
type(user_adapter) == 'table'
and type(default_adapter) == 'table'
and user_adapter.type == default_adapter.type
then
---@diagnostic disable-next-line: inject-field
RustaceanConfig.dap.adapter = vim.tbl_deep_extend('force', default_adapter, user_adapter)
elseif user_adapter ~= nil then
---@diagnostic disable-next-line: inject-field
RustaceanConfig.dap.adapter = user_adapter
end
end
local check = require('rustaceanvim.config.check')
local ok, err = check.validate(RustaceanConfig)
if not ok then
vim.notify('rustaceanvim: ' .. err, vim.log.levels.ERROR)
end
return RustaceanConfig