Skip to content
This repository was archived by the owner on Jan 3, 2024. It is now read-only.

Commit 52cca9d

Browse files
committed
vim.ui.select for code_action_group
1 parent 71d2cf6 commit 52cca9d

File tree

1 file changed

+50
-280
lines changed

1 file changed

+50
-280
lines changed

lua/rust-tools/code_action_group.lua

Lines changed: 50 additions & 280 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
local utils = require("rust-tools.utils.utils")
21
local M = {}
32

43
---@private
5-
function M.apply_action(action, client, ctx)
4+
local function apply_action(action, client, ctx)
65
if action.edit then
76
vim.lsp.util.apply_workspace_edit(action.edit, client.offset_encoding)
87
end
@@ -20,8 +19,8 @@ function M.apply_action(action, client, ctx)
2019
end
2120

2221
---@private
23-
function M.on_user_choice(action_tuple, ctx)
24-
if not action_tuple then
22+
local function on_user_choice(action, ctx)
23+
if not action then
2524
return
2625
end
2726
-- textDocument/codeAction can return either Command[] or CodeAction[]
@@ -36,8 +35,7 @@ function M.on_user_choice(action_tuple, ctx)
3635
-- command: string
3736
-- arguments?: any[]
3837
--
39-
local client = vim.lsp.get_client_by_id(action_tuple[1])
40-
local action = action_tuple[2]
38+
local client = vim.lsp.get_client_by_id(action.client_id)
4139
local code_action_provider = nil
4240
if vim.fn.has("nvim-0.8.0") then
4341
code_action_provider = client.server_capabilities.codeActionProvider
@@ -55,313 +53,85 @@ function M.on_user_choice(action_tuple, ctx)
5553
vim.notify(err.code .. ": " .. err.message, vim.log.levels.ERROR)
5654
return
5755
end
58-
M.apply_action(resolved_action, client, ctx)
56+
apply_action(resolved_action, client, ctx)
5957
end)
6058
else
61-
M.apply_action(action, client, ctx)
59+
apply_action(action, client, ctx)
6260
end
6361
end
6462

65-
local function compute_width(action_tuples, is_group)
66-
local width = 0
67-
68-
for _, value in pairs(action_tuples) do
69-
local action = value[2]
70-
local text = action.title
71-
72-
if is_group and action.group then
73-
text = action.group .. ""
74-
end
75-
local len = string.len(text)
76-
if len > width then
77-
width = len
78-
end
79-
end
80-
81-
return { width = width + 5 }
82-
end
83-
84-
local function on_primary_enter_press()
85-
if M.state.secondary.winnr then
86-
vim.api.nvim_set_current_win(M.state.secondary.winnr)
87-
return
88-
end
89-
90-
local line = vim.api.nvim_win_get_cursor(M.state.secondary.winnr or 0)[1]
91-
92-
for _, value in ipairs(M.state.actions.ungrouped) do
93-
if value[2].idx == line then
94-
M.on_user_choice(value, M.state.ctx)
95-
end
63+
local select_code_action_results = function(results, ctx)
64+
if results.error then
65+
vim.notify(results.error, vim.log.levels.ERROR)
9666
end
9767

98-
M.cleanup()
99-
end
100-
101-
local function on_primary_quit()
102-
M.cleanup()
103-
end
104-
105-
local function on_code_action_results(results, ctx)
106-
M.state.ctx = ctx
107-
10868
local action_tuples = {}
10969
for client_id, result in pairs(results) do
11070
for _, action in pairs(result.result or {}) do
111-
table.insert(action_tuples, { client_id, action })
71+
action.client_id = client_id
72+
table.insert(action_tuples, action)
11273
end
11374
end
11475
if #action_tuples == 0 then
11576
vim.notify("No code actions available", vim.log.levels.INFO)
11677
return
11778
end
11879

119-
M.state.primary.geometry = compute_width(action_tuples, true)
120-
121-
M.state.actions.grouped = {}
122-
123-
M.state.actions.ungrouped = {}
124-
125-
for _, value in ipairs(action_tuples) do
126-
local action = value[2]
127-
80+
local entries, groups = {}, {}
81+
for _, action in ipairs(action_tuples) do
12882
-- Some clippy lints may have newlines in them
12983
action.title = string.gsub(action.title, "[\n\r]+", " ")
13084

13185
if action.group then
132-
if not M.state.actions.grouped[action.group] then
133-
M.state.actions.grouped[action.group] = { actions = {}, idx = nil }
86+
if not groups[action.group] then
87+
local group =
88+
{ title = action.group .. "", actions = {}, idx = nil }
89+
groups[action.group] = group
90+
table.insert(entries, group)
13491
end
13592

136-
table.insert(M.state.actions.grouped[action.group].actions, value)
93+
table.insert(groups[action.group].actions, action)
13794
else
138-
table.insert(M.state.actions.ungrouped, value)
95+
table.insert(entries, action)
13996
end
14097
end
14198

142-
M.state.primary.bufnr = vim.api.nvim_create_buf(false, true)
143-
M.state.primary.winnr = vim.api.nvim_open_win(M.state.primary.bufnr, true, {
144-
relative = "cursor",
145-
width = M.state.primary.geometry.width,
146-
height = vim.tbl_count(M.state.actions.grouped)
147-
+ vim.tbl_count(M.state.actions.ungrouped),
148-
focusable = true,
149-
border = "rounded",
150-
row = 1,
151-
col = 0,
152-
})
153-
154-
local idx = 1
155-
for key, value in pairs(M.state.actions.grouped) do
156-
value.idx = idx
157-
vim.api.nvim_buf_set_lines(
158-
M.state.primary.bufnr,
159-
-1,
160-
-1,
161-
false,
162-
{ key .. "" }
163-
)
164-
idx = idx + 1
165-
end
166-
167-
for _, value in pairs(M.state.actions.ungrouped) do
168-
local action = value[2]
169-
value[2].idx = idx
170-
vim.api.nvim_buf_set_lines(
171-
M.state.primary.bufnr,
172-
-1,
173-
-1,
174-
false,
175-
{ action.title }
176-
)
177-
idx = idx + 1
178-
end
179-
180-
vim.api.nvim_buf_set_lines(M.state.primary.bufnr, 0, 1, false, {})
181-
182-
vim.keymap.set(
183-
"n",
184-
"<CR>",
185-
on_primary_enter_press,
186-
{ buffer = M.state.primary.bufnr }
187-
)
188-
189-
vim.keymap.set("n", "q", on_primary_quit, { buffer = M.state.primary.bufnr })
190-
191-
M.codeactionify_window_buffer(M.state.primary.winnr, M.state.primary.bufnr)
192-
193-
vim.api.nvim_buf_attach(M.state.primary.bufnr, false, {
194-
on_detach = function(_, _)
195-
M.state.primary.clear()
196-
vim.schedule(M.cleanup)
99+
vim.ui.select(entries, {
100+
prompt = "Code Actions:",
101+
format_item = function(item)
102+
return item.title
197103
end,
198-
})
199-
200-
vim.api.nvim_create_autocmd("CursorMoved", {
201-
buffer = M.state.primary.bufnr,
202-
callback = M.on_cursor_move,
203-
})
204-
205-
vim.cmd("redraw")
206-
end
207-
208-
function M.codeactionify_window_buffer(winnr, bufnr)
209-
vim.api.nvim_buf_set_option(bufnr, "modifiable", false)
210-
vim.api.nvim_buf_set_option(bufnr, "bufhidden", "delete")
211-
vim.api.nvim_buf_set_option(bufnr, "buftype", "nofile")
212-
213-
vim.api.nvim_win_set_option(winnr, "nu", true)
214-
vim.api.nvim_win_set_option(winnr, "rnu", false)
215-
vim.api.nvim_win_set_option(winnr, "cul", true)
216-
end
217-
218-
local function on_secondary_enter_press()
219-
local line = vim.api.nvim_win_get_cursor(M.state.secondary.winnr)[1]
220-
local active_group = nil
221-
222-
for _, value in pairs(M.state.actions.grouped) do
223-
if value.idx == M.state.active_group_index then
224-
active_group = value
225-
break
226-
end
227-
end
228-
229-
if active_group then
230-
for _, value in pairs(active_group.actions) do
231-
if value[2].idx == line then
232-
M.on_user_choice(value, M.state.ctx)
233-
end
234-
end
235-
end
236-
237-
M.cleanup()
238-
end
239-
240-
local function on_secondary_quit()
241-
local winnr = M.state.secondary.winnr
242-
-- we clear first because if we close the window first, the cursor moved
243-
-- autocmd of the first buffer gets called which then sees that
244-
-- M.state.secondary.winnr exists (when it shouldnt because it is closed)
245-
-- and errors out
246-
M.state.secondary.clear()
247-
248-
utils.close_win(winnr)
249-
end
250-
251-
function M.cleanup()
252-
if M.state.primary.winnr then
253-
utils.close_win(M.state.primary.winnr)
254-
M.state.primary.clear()
255-
end
256-
257-
if M.state.secondary.winnr then
258-
utils.close_win(M.state.secondary.winnr)
259-
M.state.secondary.clear()
260-
end
261-
262-
M.state.actions = {}
263-
M.state.active_group_index = nil
264-
M.state.ctx = {}
265-
end
266-
267-
function M.on_cursor_move()
268-
local line = vim.api.nvim_win_get_cursor(M.state.primary.winnr)[1]
269-
270-
for _, value in pairs(M.state.actions.grouped) do
271-
if value.idx == line then
272-
M.state.active_group_index = line
273-
274-
if M.state.secondary.winnr then
275-
utils.close_win(M.state.secondary.winnr)
276-
M.state.secondary.clear()
277-
end
278-
279-
M.state.secondary.geometry = compute_width(value.actions, false)
280-
281-
M.state.secondary.bufnr = vim.api.nvim_create_buf(false, true)
282-
M.state.secondary.winnr =
283-
vim.api.nvim_open_win(M.state.secondary.bufnr, false, {
284-
relative = "win",
285-
win = M.state.primary.winnr,
286-
width = M.state.secondary.geometry.width,
287-
height = #value.actions,
288-
focusable = true,
289-
border = "rounded",
290-
row = line - 2,
291-
col = M.state.primary.geometry.width + 1,
292-
})
293-
294-
local idx = 1
295-
for _, inner_value in pairs(value.actions) do
296-
local action = inner_value[2]
297-
action.idx = idx
298-
vim.api.nvim_buf_set_lines(
299-
M.state.secondary.bufnr,
300-
-1,
301-
-1,
302-
false,
303-
{ action.title }
304-
)
305-
idx = idx + 1
306-
end
307-
308-
vim.api.nvim_buf_set_lines(M.state.secondary.bufnr, 0, 1, false, {})
309-
310-
M.codeactionify_window_buffer(
311-
M.state.secondary.winnr,
312-
M.state.secondary.bufnr
313-
)
314-
315-
vim.keymap.set(
316-
"n",
317-
"<CR>",
318-
on_secondary_enter_press,
319-
{ buffer = M.state.secondary.bufnr }
320-
)
321-
322-
vim.keymap.set(
323-
"n",
324-
"q",
325-
on_secondary_quit,
326-
{ buffer = M.state.secondary.bufnr }
327-
)
328-
104+
}, function(selected)
105+
if not selected then
329106
return
330107
end
331108

332-
if M.state.secondary.winnr then
333-
utils.close_win(M.state.secondary.winnr)
334-
M.state.secondary.clear()
109+
if selected.actions then
110+
vim.schedule(function()
111+
vim.ui.select(selected.actions, {
112+
prompt = selected.title .. ": ",
113+
format_item = function(item)
114+
return item.title
115+
end,
116+
}, function(selected_group)
117+
if not selected_group then
118+
return
119+
end
120+
print(vim.inspect(selected_group))
121+
122+
vim.schedule(function()
123+
on_user_choice(selected_group, ctx)
124+
end)
125+
end)
126+
end)
127+
else
128+
vim.schedule(function()
129+
on_user_choice(selected, ctx)
130+
end)
335131
end
336-
end
132+
end)
337133
end
338134

339-
M.state = {
340-
ctx = {},
341-
actions = {},
342-
active_group_index = nil,
343-
primary = {
344-
bufnr = nil,
345-
winnr = nil,
346-
geometry = nil,
347-
clear = function()
348-
M.state.primary.geometry = nil
349-
M.state.primary.bufnr = nil
350-
M.state.primary.winnr = nil
351-
end,
352-
},
353-
secondary = {
354-
bufnr = nil,
355-
winnr = nil,
356-
geometry = nil,
357-
clear = function()
358-
M.state.secondary.geometry = nil
359-
M.state.secondary.bufnr = nil
360-
M.state.secondary.winnr = nil
361-
end,
362-
},
363-
}
364-
365135
function M.code_action_group()
366136
local context = {}
367137
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
@@ -373,7 +143,7 @@ function M.code_action_group()
373143
"textDocument/codeAction",
374144
params,
375145
function(results)
376-
on_code_action_results(
146+
select_code_action_results(
377147
results,
378148
{ bufnr = 0, method = "textDocument/codeAction", params = params }
379149
)

0 commit comments

Comments
 (0)