1
- local utils = require (" rust-tools.utils.utils" )
2
1
local M = {}
3
2
4
3
--- @private
5
- function M . apply_action (action , client , ctx )
4
+ local function apply_action (action , client , ctx )
6
5
if action .edit then
7
6
vim .lsp .util .apply_workspace_edit (action .edit , client .offset_encoding )
8
7
end
@@ -20,8 +19,8 @@ function M.apply_action(action, client, ctx)
20
19
end
21
20
22
21
--- @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
25
24
return
26
25
end
27
26
-- textDocument/codeAction can return either Command[] or CodeAction[]
@@ -36,8 +35,7 @@ function M.on_user_choice(action_tuple, ctx)
36
35
-- command: string
37
36
-- arguments?: any[]
38
37
--
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 )
41
39
local code_action_provider = nil
42
40
if vim .fn .has (" nvim-0.8.0" ) then
43
41
code_action_provider = client .server_capabilities .codeActionProvider
@@ -55,313 +53,85 @@ function M.on_user_choice(action_tuple, ctx)
55
53
vim .notify (err .code .. " : " .. err .message , vim .log .levels .ERROR )
56
54
return
57
55
end
58
- M . apply_action (resolved_action , client , ctx )
56
+ apply_action (resolved_action , client , ctx )
59
57
end )
60
58
else
61
- M . apply_action (action , client , ctx )
59
+ apply_action (action , client , ctx )
62
60
end
63
61
end
64
62
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 )
96
66
end
97
67
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
-
108
68
local action_tuples = {}
109
69
for client_id , result in pairs (results ) do
110
70
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 )
112
73
end
113
74
end
114
75
if # action_tuples == 0 then
115
76
vim .notify (" No code actions available" , vim .log .levels .INFO )
116
77
return
117
78
end
118
79
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
128
82
-- Some clippy lints may have newlines in them
129
83
action .title = string.gsub (action .title , " [\n\r ]+" , " " )
130
84
131
85
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 )
134
91
end
135
92
136
- table.insert (M . state . actions . grouped [action .group ].actions , value )
93
+ table.insert (groups [action .group ].actions , action )
137
94
else
138
- table.insert (M . state . actions . ungrouped , value )
95
+ table.insert (entries , action )
139
96
end
140
97
end
141
98
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
197
103
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
329
106
return
330
107
end
331
108
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 )
335
131
end
336
- end
132
+ end )
337
133
end
338
134
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
-
365
135
function M .code_action_group ()
366
136
local context = {}
367
137
context .diagnostics = vim .lsp .diagnostic .get_line_diagnostics ()
@@ -373,7 +143,7 @@ function M.code_action_group()
373
143
" textDocument/codeAction" ,
374
144
params ,
375
145
function (results )
376
- on_code_action_results (
146
+ select_code_action_results (
377
147
results ,
378
148
{ bufnr = 0 , method = " textDocument/codeAction" , params = params }
379
149
)
0 commit comments