summaryrefslogtreecommitdiff
path: root/nvim/lua/plugins/rust.lua
blob: 8813700154c3e1f14b3afb929c4d8ba2ac65c444 (plain)
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
-- rustaceanvim / rust-analyzer, matched to the VSCodium setup in ../../vscode/settings.json:
-- clippy instead of cargo check, rustfmt on save, source.fixAll on save, hard tabs.
--
-- Drop at: ~/.config/nvim/lua/plugins/rust.lua, and delete the rustaceanvim block
-- from lua/plugins/init.lua — two lazy.nvim specs for the same plugin will fight.

local timeout_ms = 2000

-- Equivalent of VS Code's "editor.codeActionsOnSave": { "source.fixAll.rust-analyzer" }.
--
-- vim.lsp.buf.code_action{apply=true} is asynchronous, so from a BufWritePre autocmd
-- the buffer is usually written before the edit arrives. buf_request_sync blocks,
-- which is what makes this actually run before the write.
--
-- Same caveat as in VS Code: this applies rust-analyzer's own fixes only. Lints that
-- come from cargo check or clippy are not included — those still need `cargo fix` or
-- `cargo clippy --fix` on the command line.
local function apply_fix_all(bufnr)
	local client = vim.lsp.get_clients({ bufnr = bufnr, name = "rust-analyzer" })[1]
	if not client then
		return
	end

	local params = vim.lsp.util.make_range_params(0, client.offset_encoding)
	params.context = { only = { "source.fixAll" }, diagnostics = {} }

	local responses = vim.lsp.buf_request_sync(bufnr, "textDocument/codeAction", params, timeout_ms)
	if not responses then
		return
	end

	for _, response in pairs(responses) do
		for _, action in ipairs(response.result or {}) do
			-- A server may return the action unresolved, carrying only `data`;
			-- the edit then has to be fetched with a second request.
			if not action.edit and action.data then
				local resolved =
					client:request_sync("codeAction/resolve", action, timeout_ms, bufnr)
				action = (resolved and resolved.result) or action
			end
			if action.edit then
				vim.lsp.util.apply_workspace_edit(action.edit, client.offset_encoding)
			end
		end
	end
end

vim.api.nvim_create_autocmd("BufWritePre", {
	group = vim.api.nvim_create_augroup("rust_on_save", { clear = true }),
	pattern = "*.rs",
	callback = function(args)
		apply_fix_all(args.buf)
		-- VS Code's "editor.formatOnSave" for [rust]: rustfmt, driven by rust-analyzer.
		vim.lsp.buf.format({
			bufnr = args.buf,
			timeout_ms = timeout_ms,
			filter = function(client)
				return client.name == "rust-analyzer"
			end,
		})
	end,
})

return {
	{
		"mrcjkb/rustaceanvim",
		version = "^6",
		-- No `ft = { "rust" }` here. It is dead weight next to lazy = false, and
		-- rustaceanvim's own docs advise against lazy-loading it by filetype:
		-- it installs the FileType handler that starts the LSP itself.
		lazy = false,
		config = function()
			vim.g.rustaceanvim = {
				server = {
					on_attach = function(client, bufnr)
						require("nvchad.configs.lspconfig").on_attach(client, bufnr)
					end,
					default_settings = {
						["rust-analyzer"] = {
							cargo = { allFeatures = true },
							checkOnSave = true,
							-- Matches "rust-analyzer.check.command": "clippy".
							check = { command = "clippy" },
						},
					},
				},
			}
		end,
	},
}