]> git.madduck.net Git - etc/vim.git/blob - .vim/bundle/vim-lsp/autoload/lsp/ui/vim/code_action.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge commit '294584081929424aec883f90c7d6515b3743358d' as '.vim/bundle/vim-lsp-ale'
[etc/vim.git] / .vim / bundle / vim-lsp / autoload / lsp / ui / vim / code_action.vim
1 " vint: -ProhibitUnusedVariable
2
3 function! lsp#ui#vim#code_action#complete(input, command, len) abort
4     let l:server_names = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_code_action_provider(v:val)')
5     let l:kinds = []
6     for l:server_name in l:server_names
7         let l:kinds += lsp#capabilities#get_code_action_kinds(l:server_name)
8     endfor
9     return filter(copy(l:kinds), { _, kind -> kind =~ '^' . a:input })
10 endfunction
11
12 "
13 " @param option = {
14 "   selection: v:true | v:false = Provide by CommandLine like `:'<,'>LspCodeAction`
15 "   sync: v:true | v:false      = Specify enable synchronous request. Example use case is `BufWritePre`
16 "   query: string               = Specify code action kind query. If query provided and then filtered code action is only one, invoke code action immediately.
17 "   ui: 'float' | 'preview'
18 " }
19 "
20 function! lsp#ui#vim#code_action#do(option) abort
21     let l:selection = get(a:option, 'selection', v:false)
22     let l:sync = get(a:option, 'sync', v:false)
23     let l:query = get(a:option, 'query', '')
24     let l:ui = get(a:option, 'ui', g:lsp_code_action_ui)
25     if empty(l:ui)
26         let l:ui = lsp#utils#_has_popup_menu() ? 'float' : 'preview'
27     endif
28
29     let l:server_names = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_code_action_provider(v:val)')
30     if len(l:server_names) == 0
31         return lsp#utils#error('Code action not supported for ' . &filetype)
32     endif
33
34     if l:selection
35         let l:range = lsp#utils#range#_get_recent_visual_range()
36     else
37         let l:range = lsp#utils#range#_get_current_line_range()
38     endif
39
40     let l:ctx = {
41     \ 'count': len(l:server_names),
42     \ 'results': [],
43     \}
44     let l:bufnr = bufnr('%')
45     let l:command_id = lsp#_new_command()
46     for l:server_name in l:server_names
47         let l:diagnostic = lsp#internal#diagnostics#under_cursor#get_diagnostic({'server': l:server_name})
48         call lsp#send_request(l:server_name, {
49                     \ 'method': 'textDocument/codeAction',
50                     \ 'params': {
51                     \   'textDocument': lsp#get_text_document_identifier(),
52                     \   'range': empty(l:diagnostic) || l:selection ? l:range : l:diagnostic['range'],
53                     \   'context': {
54                     \       'diagnostics' : empty(l:diagnostic) ? [] : [l:diagnostic],
55                     \       'only': ['', 'quickfix', 'refactor', 'refactor.extract', 'refactor.inline', 'refactor.rewrite', 'source', 'source.organizeImports'],
56                     \   },
57                     \ },
58                     \ 'sync': l:sync,
59                     \ 'on_notification': function('s:handle_code_action', [l:ui, l:ctx, l:server_name, l:command_id, l:sync, l:query, l:bufnr]),
60                     \ })
61     endfor
62     echo 'Retrieving code actions ...'
63 endfunction
64
65 function! s:handle_code_action(ui, ctx, server_name, command_id, sync, query, bufnr, data) abort
66     " Ignore old request.
67     if a:command_id != lsp#_last_command()
68         return
69     endif
70
71     call add(a:ctx['results'], {
72     \    'server_name': a:server_name,
73     \    'data': a:data,
74     \})
75     let a:ctx['count'] -= 1
76     if a:ctx['count'] ># 0
77         return
78     endif
79
80     let l:total_code_actions = []
81     for l:result in a:ctx['results']
82         let l:server_name = l:result['server_name']
83         let l:data = l:result['data']
84         " Check response error.
85         if lsp#client#is_error(l:data['response'])
86             call lsp#utils#error('Failed to CodeAction for ' . l:server_name . ': ' . lsp#client#error_message(l:data['response']))
87             continue
88         endif
89
90         " Check code actions.
91         let l:code_actions = l:data['response']['result']
92
93         " Filter code actions.
94         if !empty(a:query)
95             let l:code_actions = filter(l:code_actions, { _, action -> get(action, 'kind', '') =~# '^' . a:query })
96         endif
97         if empty(l:code_actions)
98             continue
99         endif
100
101         for l:code_action in l:code_actions
102             let l:item = {
103             \   'server_name': l:server_name,
104             \   'code_action': l:code_action,
105             \ }
106             if get(l:code_action, 'isPreferred', v:false)
107                 let l:total_code_actions = [l:item] + l:total_code_actions
108             else
109                 call add(l:total_code_actions, l:item)
110             endif
111         endfor
112     endfor
113
114     if len(l:total_code_actions) == 0
115         echo 'No code actions found'
116         return
117     endif
118     call lsp#log('s:handle_code_action', l:total_code_actions)
119
120     if len(l:total_code_actions) == 1 && !empty(a:query)
121         let l:action = l:total_code_actions[0]
122         if s:handle_disabled_action(l:action) | return | endif
123         " Clear 'Retrieving code actions ...' message
124         echo ''
125         call s:handle_one_code_action(l:action['server_name'], a:sync, a:bufnr, l:action['code_action'])
126         return
127     endif
128
129     " Prompt to choose code actions when empty query provided.
130     let l:items = []
131     for l:action in l:total_code_actions
132         let l:title = printf('[%s] %s', l:action['server_name'], l:action['code_action']['title'])
133         if has_key(l:action['code_action'], 'kind') && l:action['code_action']['kind'] !=# ''
134             let l:title .= ' (' . l:action['code_action']['kind'] . ')'
135         endif
136         call add(l:items, { 'title': l:title, 'item': l:action })
137     endfor
138
139     if lsp#utils#_has_popup_menu() && a:ui ==? 'float'
140         call lsp#internal#ui#popupmenu#open({
141             \   'title': 'Code actions',
142             \   'items': mapnew(l:items, { idx, item -> item.title}),
143             \   'pos': 'topleft',
144             \   'line': 'cursor+1',
145             \   'col': 'cursor',
146             \   'callback': funcref('s:popup_accept_code_action', [a:sync, a:bufnr, l:items]),
147             \ })
148     else
149         call lsp#internal#ui#quickpick#open({
150             \ 'items': l:items,
151             \ 'key': 'title',
152             \ 'on_accept': funcref('s:quickpick_accept_code_action', [a:sync, a:bufnr]),
153             \ })
154     endif
155 endfunction
156
157 function! s:popup_accept_code_action(sync, bufnr, items, id, selected, ...) abort
158     if a:selected <= 0 | return | endif
159     let l:item = a:items[a:selected - 1]['item']
160     if s:handle_disabled_action(l:item) | return | endif
161     call s:handle_one_code_action(l:item['server_name'], a:sync, a:bufnr, l:item['code_action'])
162     execute('doautocmd <nomodeline> User lsp_float_closed')
163 endfunction
164
165 function! s:quickpick_accept_code_action(sync, bufnr, data, ...) abort
166     call lsp#internal#ui#quickpick#close()
167     if empty(a:data['items']) | return | endif
168     let l:selected = a:data['items'][0]['item']
169     if s:handle_disabled_action(l:selected) | return | endif
170     call s:handle_one_code_action(l:selected['server_name'], a:sync, a:bufnr, l:selected['code_action'])
171 endfunction
172
173 function! s:handle_disabled_action(code_action) abort
174     if has_key(a:code_action, 'disabled')
175         echo 'This action is disabled: ' . a:code_action['disabled']['reason']
176         return v:true
177     endif
178     return v:false
179 endfunction
180
181 function! s:handle_one_code_action(server_name, sync, bufnr, command_or_code_action) abort
182     " has WorkspaceEdit.
183     if has_key(a:command_or_code_action, 'edit')
184         call lsp#utils#workspace_edit#apply_workspace_edit(a:command_or_code_action['edit'])
185     endif
186
187     " Command.
188     if has_key(a:command_or_code_action, 'command') && type(a:command_or_code_action['command']) == type('')
189         call lsp#ui#vim#execute_command#_execute({
190         \   'server_name': a:server_name,
191         \   'command_name': get(a:command_or_code_action, 'command', ''),
192         \   'command_args': get(a:command_or_code_action, 'arguments', v:null),
193         \   'sync': a:sync,
194         \   'bufnr': a:bufnr,
195         \ })
196
197     " has Command.
198     elseif has_key(a:command_or_code_action, 'command') && type(a:command_or_code_action['command']) == type({})
199         call lsp#ui#vim#execute_command#_execute({
200         \   'server_name': a:server_name,
201         \   'command_name': get(a:command_or_code_action['command'], 'command', ''),
202         \   'command_args': get(a:command_or_code_action['command'], 'arguments', v:null),
203         \   'sync': a:sync,
204         \   'bufnr': a:bufnr,
205         \ })
206     endif
207 endfunction