Skip to content

Commit 214853d

Browse files
authored
Merge pull request #325 from maralla/code-action
Support code action
2 parents 2601955 + c481bbe commit 214853d

8 files changed

Lines changed: 221 additions & 14 deletions

File tree

autoload/completor.vim

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,37 @@ function! completor#do(action, ...) range
141141
let args = a:000
142142
endif
143143

144-
let meta = {'range': [a:firstline, a:lastline]}
144+
let start_col = 0
145+
let end_col = 0
146+
147+
if mode() ==# 'v'
148+
let start_col = col("'<")
149+
let end_col = col("'>")
150+
endif
151+
152+
if start_col == 0
153+
let start_col = col('.')
154+
endif
155+
156+
if end_col == 0
157+
let end_col = col('.')
158+
endif
159+
160+
let meta = {
161+
\ 'range': [a:firstline, a:lastline],
162+
\ 'text_range': {
163+
\ 'start': {'line': a:firstline-1, 'col': start_col-1},
164+
\ 'end': {'line': a:lastline-1, 'col': end_col-1}
165+
\ }
166+
\ }
145167
let status = completor#action#current_status()
146168

169+
if !empty(args) && type(args[-1]) == v:t_dict
170+
let args[-1]['meta'] = meta
171+
else
172+
let args = args + [{'meta': meta}]
173+
endif
174+
147175
let s:timer = timer_start(g:completor_completion_delay, {t->s:do_action(action, meta, status, args)})
148176
return ''
149177
endfunction

autoload/completor/action.vim

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,14 @@ function! completor#action#trigger(items)
345345
silent edit!
346346
elseif action ==# 'format'
347347
call s:format(items)
348+
elseif action ==# 'menu'
349+
if !empty(items)
350+
if completor#support_popup()
351+
call completor#popup#menu(s:action, items)
352+
else
353+
echo "popup window not supported"
354+
endif
355+
endif
348356
elseif action ==# 'view'
349357
if !empty(items)
350358
if completor#support_popup()

autoload/completor/popup.vim

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ func completor#popup#view(content, ft)
561561
\ zindex: 32000,
562562
\ filter: function('s:scroll_filter'),
563563
\ filtermode: 'n',
564-
\ padding: [1, 1, 1, 1],
564+
\ padding: [0, 1, 0, 1],
565565
\ border: [1, 1, 1, 1],
566566
\ borderchars: ['', '', '', '', '', '', '', ''],
567567
\ })
@@ -571,6 +571,52 @@ func completor#popup#view(content, ft)
571571
endfunc
572572

573573

574+
" content: [{'title': 'xxx', 'data': 'json-string'}, ...]
575+
func completor#popup#menu(action, content)
576+
let items = []
577+
for item in a:content
578+
call add(items, item['title'])
579+
endfor
580+
581+
call popup_menu(items, #{
582+
\ moved: 'any',
583+
\ pos: 'botleft',
584+
\ line: 'cursor-1',
585+
\ col: 'cursor',
586+
\ zindex: 32000,
587+
\ padding: [0, 0, 0, 0],
588+
\ border: [1, 1, 1, 1],
589+
\ borderchars: ['', '', '', '', '', '', '', ''],
590+
\ callback: {id, result -> s:menu_callback(result, a:action, a:content)},
591+
\ filter: function('s:menu_filter')
592+
\ })
593+
endfunc
594+
595+
596+
func s:menu_filter(id, key)
597+
let k = a:key
598+
if a:key == "\<C-j>"
599+
let k = "\<DOWN>"
600+
elseif a:key == "\<C-k>"
601+
let k = "\<UP>"
602+
elseif a:key == "q"
603+
let k = "\<ESC>"
604+
endif
605+
606+
return popup_filter_menu(a:id, k)
607+
endfunc
608+
609+
610+
func s:menu_callback(result, action, content)
611+
if a:result < 0
612+
return
613+
endif
614+
615+
let data = a:content[a:result-1].data
616+
call completor#do(a:action .. '_callback', data)
617+
endfunc
618+
619+
574620
func s:scroll_filter(id, key)
575621
if a:key == "\<DOWN>" || a:key == "\<C-j>"
576622
call win_execute(a:id, "normal! \<C-d>")

pythonx/completers/lsp/__init__.py

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
import os
77
import json
88
import io
9-
from completor import Completor, vim, import_completer, get
9+
from completor import Completor, vim, get
1010
from completor.compat import to_unicode
1111

1212
from . import ext
1313
from .models import Initialize, DidOpen, Completion, DidChange, DidSave, \
1414
Definition, Format, Rename, Hover, Initialized, Implementation, \
1515
References, DidChangeConfiguration, Symbol, Signature, DocumentSymbol, \
16-
CodeAction
16+
CodeAction, CodeResolve
1717
from .action import gen_jump_list, get_completion_word, gen_hover_doc, \
1818
filter_items, parse_symbols, rename
1919
from .utils import gen_uri
@@ -128,7 +128,46 @@ def rename_request(self, name):
128128

129129
def code_action(self, action):
130130
uri = gen_uri(self.filename)
131-
c = CodeAction(uri, action)
131+
meta = (self.current_options or {}).get(b'meta', {})
132+
c = CodeAction(uri, action, text_range=meta.get('text_range'))
133+
req_id, req = c.to_request()
134+
self.current_id = req_id
135+
return req
136+
137+
def code_action_callback(self, data):
138+
if not data:
139+
return ''
140+
141+
try:
142+
data = json.loads(data[0])
143+
except Exception as e:
144+
logger.exception(e)
145+
return ''
146+
147+
edit = data.get('edit')
148+
if edit:
149+
uri = gen_uri(self.filename)
150+
changes = edit.get('changes')
151+
edits = None
152+
if changes:
153+
edits = changes.get(uri)
154+
else:
155+
changes = edit.get('documentChanges')
156+
if not changes:
157+
return
158+
for change in changes:
159+
if change['textDocument']['uri'] == uri:
160+
edits = change['edits']
161+
break
162+
163+
if not edits:
164+
return ''
165+
166+
res = vim.Dictionary(data=[edits], action='format')
167+
self.trigger_action(res)
168+
return ''
169+
170+
c = CodeResolve(params=data)
132171
req_id, req = c.to_request()
133172
self.current_id = req_id
134173
return req
@@ -173,6 +212,11 @@ def _gen_action_args(self, action, args):
173212
return ''
174213
return self.code_action([args[0]])
175214

215+
if action == b'code_action_callback':
216+
if not args:
217+
return ''
218+
return self.code_action_callback([args[0]])
219+
176220
if action == b'hover':
177221
return self.position_request(Hover)
178222

@@ -185,7 +229,7 @@ def _gen_action_args(self, action, args):
185229
return ''
186230

187231
def gen_request(self, action, args):
188-
self.current_options = None
232+
self.current_options = {}
189233
if args:
190234
last = args[-1]
191235
if isinstance(last, (dict, vim.Dictionary)):
@@ -350,12 +394,38 @@ def on_code_action(self, data):
350394
if not item:
351395
return []
352396

353-
item = item[0]
354-
if not item or 'edit' not in item:
397+
items = []
398+
try:
399+
for v in item:
400+
data = json.dumps(v)
401+
items.append({'title': v['title'], 'data': json.dumps(v)})
402+
except Exception as e:
403+
logger.exception(e)
355404
return []
356405

357-
rename(item['edit'])
358-
return vim.Dictionary(data=[], action='rename')
406+
return vim.Dictionary(data=items, action='menu')
407+
408+
# item = item[0]
409+
# if not item or 'edit' not in item:
410+
# return []
411+
#
412+
# rename(item['edit'])
413+
# return vim.Dictionary(data=[], action='rename')
414+
415+
def on_code_action_callback(self, data):
416+
logger.info("code_action_callback -> %r", data)
417+
if not data:
418+
return []
419+
420+
try:
421+
changes = data[0]['edit']['documentChanges']
422+
if len(changes) != 1:
423+
return []
424+
except Exception as e:
425+
logger.exception(e)
426+
return []
427+
428+
return vim.Dictionary(data=[changes[0]['edits']], action='format')
359429

360430
def on_hover(self, data):
361431
logger.info("hover -> %r", data)

pythonx/completers/lsp/ext/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,8 @@ def on_data(action, ft, data):
1515
if not data:
1616
return []
1717

18+
if ft == 'rust':
19+
from .rust import on_data
20+
return on_data(action, data)
21+
1822
return vim.Dictionary(data=data, action='view', ft=ft)

pythonx/completers/lsp/ext/rust.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf-8 -*-
22

3+
from completor import vim
34
from ..models import Completion
45

56

@@ -8,13 +9,28 @@ def get_action_handler(action):
89
return ViewHir
910
if action == b'view_mir':
1011
return ViewMir
12+
if action == b'expand_macro':
13+
return ExpandMacro
1114

1215
return ''
1316

1417

18+
def on_data(action, data):
19+
if action == b'expand_macro':
20+
try:
21+
data = [data[0]['expansion']]
22+
except Exception:
23+
return []
24+
return vim.Dictionary(data=data, action='view', ft='rust')
25+
26+
1527
class ViewHir(Completion):
1628
method = 'rust-analyzer/viewHir'
1729

1830

1931
class ViewMir(Completion):
2032
method = 'rust-analyzer/viewMir'
33+
34+
35+
class ExpandMacro(Completion):
36+
method = 'rust-analyzer/expandMacro'

pythonx/completers/lsp/models.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,17 @@ def to_dict(self):
7676
'snippetSupport': False,
7777
'commitCharactersSupport': False,
7878
}
79-
}
79+
},
80+
'codeAction': {
81+
'codeActionLiteralSupport': {
82+
'codeActionKind': {
83+
'valueSet': ['quickfix'],
84+
}
85+
},
86+
'resolveSupport': {
87+
'properties': ['edit'],
88+
}
89+
},
8090
}
8191
},
8292
'rootUri': self.workspace[0]['uri'],
@@ -198,24 +208,45 @@ def to_dict(self):
198208
class CodeAction(Base):
199209
method = 'textDocument/codeAction'
200210

201-
def __init__(self, uri, action=None):
211+
def __init__(self, uri, action=None, text_range=None):
202212
self.uri = uri
203213
self.action = action
214+
self.text_range = text_range
204215

205216
def to_dict(self):
206217
r = {
207218
'textDocument': {
208219
'uri': self.uri
209220
},
210-
'context': {}
221+
'context': {
222+
'diagnostics': []
223+
}
211224
}
212225

213226
if self.action is not None:
214227
r['context']['only'] = self.action
215228

229+
if self.text_range:
230+
start = self.text_range[b'start']
231+
end = self.text_range[b'end']
232+
r['range'] = {
233+
'start': {'line': start[b'line'], 'character': start[b'col']},
234+
'end': {'line': end[b'line'], 'character': end[b'col']}
235+
}
236+
216237
return r
217238

218239

240+
class CodeResolve(Base):
241+
method = 'codeAction/resolve'
242+
243+
def __init__(self, params):
244+
self.params = params
245+
246+
def to_dict(self):
247+
return self.params
248+
249+
219250
class DocumentSymbol(Format):
220251
method = 'textDocument/documentSymbol'
221252

pythonx/completor/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,12 @@ def handle_stream(self, name, action, msg):
276276
res = self.on_stream(action, msg)
277277
if res is None:
278278
return
279+
280+
self.trigger_action(res)
281+
282+
def trigger_action(self, data):
279283
try:
280-
vim_action_trigger(res)
284+
vim_action_trigger(data)
281285
except vim.error as e:
282286
logger.exception(e)
283287

0 commit comments

Comments
 (0)