Skip to content

Commit 337d834

Browse files
authored
Include command argument labels in Code Lens item's text (fix #1118) (#1119)
* fix index out of range error when no item in quickpick list * update embedded quickpick to cf41eecb983c41e5fc45e83291b551a85fe554d3 * generate subtitle of codelens item from command arguments' labels * add tests for lsp#ui#vim#code_lens#_get_subtitle()
1 parent 51adba8 commit 337d834

File tree

3 files changed

+163
-5
lines changed

3 files changed

+163
-5
lines changed

autoload/lsp/internal/ui/quickpick.vim

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
" https://github.com/prabirshrestha/quickpick.vim#37e29b28f65d3ae344ec8eaf7ce2d5f87e2f4b90
1+
" https://github.com/prabirshrestha/quickpick.vim#cf41eecb983c41e5fc45e83291b551a85fe554d3
22
" :QuickpickEmbed path=autoload/lsp/internal/ui/quickpick.vim namespace=lsp#internal#ui#quickpick prefix=lsp-quickpick
33

44
let s:has_timer = exists('*timer_start') && exists('*timer_stop')
@@ -268,10 +268,11 @@ endfunction
268268
function! s:on_accept() abort
269269
if win_gotoid(s:state['resultswinid'])
270270
let l:index = line('.') - 1 " line is 1 index, list is 0 index
271-
if l:index < 0
271+
let l:fitems = s:state['fitems']
272+
if l:index < 0 || len(l:fitems) <= l:index
272273
let l:items = []
273274
else
274-
let l:items = [s:state['fitems'][l:index]]
275+
let l:items = [l:fitems[l:index]]
275276
endif
276277
call win_gotoid(s:state['winid'])
277278
call s:notify('accept', { 'items': l:items })

autoload/lsp/ui/vim/code_lens.vim

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,36 @@ function! s:chooseCodeLens(items, bufnr) abort
7474
return lsp#callbag#create(function('s:quickpick_open', [a:items, a:bufnr]))
7575
endfunction
7676

77+
function! lsp#ui#vim#code_lens#_get_subtitle(item) abort
78+
" Since element of arguments property of Command interface is defined as any in LSP spec, it is
79+
" up to the language server implementation.
80+
" Currently this only impacts rust-analyzer. See #1118 for more details.
81+
82+
if !has_key(a:item['codelens']['command'], 'arguments')
83+
return ''
84+
endif
85+
86+
let l:arguments = a:item['codelens']['command']['arguments']
87+
for l:argument in l:arguments
88+
if type(l:argument) != type({}) || !has_key(l:argument, 'label')
89+
return ''
90+
endif
91+
endfor
92+
93+
return ': ' . join(map(copy(l:arguments), 'v:val["label"]'), ' > ')
94+
endfunction
95+
7796
function! s:quickpick_open(items, bufnr, next, error, complete) abort
7897
if empty(a:items)
7998
return lsp#callbag#empty()
8099
endif
81100

82101
let l:items = []
83102
for l:item in a:items
84-
let l:title = printf("[%s] %s\t| L%s:%s",
103+
let l:title = printf("[%s] %s%s\t| L%s:%s",
85104
\ l:item['server'],
86105
\ l:item['codelens']['command']['title'],
106+
\ lsp#ui#vim#code_lens#_get_subtitle(l:item),
87107
\ lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['codelens']['range']['start']),
88108
\ getbufline(a:bufnr, lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['codelens']['range']['start']))[0])
89109
call add(l:items, { 'title': l:title, 'item': l:item })
@@ -105,7 +125,10 @@ endfunction
105125

106126
function! s:quickpick_accept(next, error, complete, data, ...) abort
107127
call lsp#internal#ui#quickpick#close()
108-
call a:next(a:data['items'][0]['item'])
128+
let l:items = a:data['items']
129+
if len(l:items) > 0
130+
call a:next(l:items[0]['item'])
131+
endif
109132
call a:complete()
110133
endfunction
111134

test/lsp/ui/vim/code_lens.vimspec

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
Describe lsp#uivim#code_lens
2+
Describe lsp#ui#vim#code_lens#_get_subtitle
3+
It should generate subtitle from response of rust-analyzer
4+
" Example response of Code Lens extracted from #1118
5+
let item = {
6+
\ 'codelens': {
7+
\ 'command': {
8+
\ 'arguments': [
9+
\ {
10+
\ 'args': {
11+
\ 'cargoArgs': ['test', '--package', 'tmp', '--lib'],
12+
\ 'cargoExtraArgs': [],
13+
\ 'executableArgs': ['tests::it_works', '--exact', '--nocapture'],
14+
\ 'overrideCargo': v:null,
15+
\ 'workspaceRoot': '/tmp'
16+
\ },
17+
\ 'kind': 'cargo',
18+
\ 'label': 'test tests::it_works',
19+
\ 'location': {
20+
\ 'targetRange': {'end': {'character': 5, 'line': 14}, 'start': {'character': 4, 'line': 11}},
21+
\ 'targetSelectionRange': {'end': {'character': 15, 'line': 12}, 'start': {'character': 7, 'line': 12}},
22+
\ 'targetUri': 'file:////tmp/src/lib.rs'
23+
\ }
24+
\ }
25+
\ ],
26+
\ 'command': 'rust-analyzer.runSingle',
27+
\ 'title': '▶︎ Run Test'
28+
\ },
29+
\ 'range': {'end': {'character': 5, 'line': 14}, 'start': {'character': 4, 'line': 11}}
30+
\ },
31+
\ 'server': 'rust-analyzer'
32+
\ }
33+
34+
let subtitle = lsp#ui#vim#code_lens#_get_subtitle(item)
35+
Assert Equals(subtitle, ': test tests::it_works')
36+
End
37+
38+
It should generate subtitle from multiple labels of command arguments
39+
let item = {
40+
\ 'codelens': {
41+
\ 'command': {
42+
\ 'arguments': [
43+
\ {
44+
\ 'args': {},
45+
\ 'kind': 'kind1',
46+
\ 'label': 'do command1',
47+
\ 'location': {}
48+
\ },
49+
\ {
50+
\ 'args': {},
51+
\ 'kind': 'kind2',
52+
\ 'label': 'do command2',
53+
\ 'location': {}
54+
\ }
55+
\ ],
56+
\ 'command': 'server.someCommand',
57+
\ 'title': 'lens title'
58+
\ },
59+
\ 'range': {'end': {'character': 5, 'line': 14}, 'start': {'character': 4, 'line': 11}}
60+
\ },
61+
\ 'server': 'rust-analyzer'
62+
\ }
63+
64+
let subtitle = lsp#ui#vim#code_lens#_get_subtitle(item)
65+
Assert Equals(subtitle, ': do command1 > do command2')
66+
End
67+
68+
It should return empty string when 'arguments' field is not found
69+
let item = {
70+
\ 'codelens': {
71+
\ 'command': {
72+
\ 'command': 'server.someCommand',
73+
\ 'title': 'lens title'
74+
\ },
75+
\ 'range': {'end': {'character': 5, 'line': 14}, 'start': {'character': 4, 'line': 11}}
76+
\ },
77+
\ 'server': 'rust-analyzer'
78+
\ }
79+
80+
let subtitle = lsp#ui#vim#code_lens#_get_subtitle(item)
81+
Assert Equals(subtitle, '')
82+
End
83+
84+
It should return empty string when 'arguments' field is not an object
85+
let item = {
86+
\ 'codelens': {
87+
\ 'command': {
88+
\ 'arguments': [
89+
\ 'command1',
90+
\ 'command2',
91+
\ 'command3'
92+
\ ],
93+
\ 'command': 'server.someCommand',
94+
\ 'title': 'lens title'
95+
\ },
96+
\ 'range': {'end': {'character': 5, 'line': 14}, 'start': {'character': 4, 'line': 11}}
97+
\ },
98+
\ 'server': 'rust-analyzer'
99+
\ }
100+
101+
let subtitle = lsp#ui#vim#code_lens#_get_subtitle(item)
102+
Assert Equals(subtitle, '')
103+
End
104+
105+
It should return empty string when at least one of elements in 'arguments' field does not have 'label' field
106+
let item = {
107+
\ 'codelens': {
108+
\ 'command': {
109+
\ 'arguments': [
110+
\ {
111+
\ 'args': {},
112+
\ 'kind': 'kind1',
113+
\ 'label': 'do command1',
114+
\ 'location': {}
115+
\ },
116+
\ {
117+
\ 'args': {},
118+
\ 'kind': 'kind2',
119+
\ 'location': {}
120+
\ }
121+
\ ],
122+
\ 'command': 'server.someCommand',
123+
\ 'title': 'lens title'
124+
\ },
125+
\ 'range': {'end': {'character': 5, 'line': 14}, 'start': {'character': 4, 'line': 11}}
126+
\ },
127+
\ 'server': 'rust-analyzer'
128+
\ }
129+
130+
let subtitle = lsp#ui#vim#code_lens#_get_subtitle(item)
131+
Assert Equals(subtitle, '')
132+
End
133+
End
134+
End

0 commit comments

Comments
 (0)