mirror of
https://github.com/prabirshrestha/vim-lsp.git
synced 2025-12-14 20:35:59 +01:00
Merge branch 'master' into fix-vim-popup-conceal
This commit is contained in:
15
README.md
15
README.md
@@ -18,11 +18,11 @@ use vim compiled with lua or neovim v0.4.0+
|
||||
## Registering servers
|
||||
|
||||
```viml
|
||||
if executable('pyls')
|
||||
" pip install python-language-server
|
||||
if executable('pylsp')
|
||||
" pip install python-lsp-server
|
||||
au User lsp_setup call lsp#register_server({
|
||||
\ 'name': 'pyls',
|
||||
\ 'cmd': {server_info->['pyls']},
|
||||
\ 'name': 'pylsp',
|
||||
\ 'cmd': {server_info->['pylsp']},
|
||||
\ 'allowlist': ['python'],
|
||||
\ })
|
||||
endif
|
||||
@@ -183,6 +183,13 @@ let g:lsp_log_file = expand('~/vim-lsp.log')
|
||||
let g:asyncomplete_log_file = expand('~/asyncomplete.log')
|
||||
```
|
||||
|
||||
You can get detailed status on your servers using `:CheckHealth` -- built into neovim or in a plugin on vim:
|
||||
|
||||
```vim
|
||||
if !has('nvim') | Plug 'rhysd/vim-healthcheck' | endif
|
||||
CheckHealth
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
[vim-themis](https://github.com/thinca/vim-themis) is used for testing. To run
|
||||
|
||||
65
autoload/health/lsp.vim
Normal file
65
autoload/health/lsp.vim
Normal file
@@ -0,0 +1,65 @@
|
||||
function! s:BuildConfigBlock(section, info) abort
|
||||
let l:block = get(a:info, a:section, '')
|
||||
if !empty(l:block)
|
||||
return printf("### %s\n%s\n", a:section, l:block)
|
||||
endif
|
||||
return ''
|
||||
endf
|
||||
|
||||
|
||||
function! health#lsp#check() abort
|
||||
call health#report_start('server status')
|
||||
let l:server_status = lsp#collect_server_status()
|
||||
|
||||
let l:has_printed = v:false
|
||||
for l:k in sort(keys(l:server_status))
|
||||
let l:report = l:server_status[l:k]
|
||||
|
||||
let l:status_msg = printf('%s: %s', l:k, l:report.status)
|
||||
if l:report.status == 'running'
|
||||
call health#report_ok(l:status_msg)
|
||||
elseif l:report.status == 'failed'
|
||||
call health#report_error(l:status_msg, 'See :help g:lsp_log_verbose to debug server failure.')
|
||||
else
|
||||
call health#report_warn(l:status_msg)
|
||||
endif
|
||||
let l:has_printed = v:true
|
||||
endfor
|
||||
|
||||
if !l:has_printed
|
||||
call health#report_warn('no servers connected')
|
||||
endif
|
||||
|
||||
for l:k in sort(keys(l:server_status))
|
||||
call health#report_start(printf('server configuration: %s', l:k))
|
||||
let l:report = l:server_status[l:k]
|
||||
|
||||
let l:msg = "\t\n"
|
||||
let l:msg .= s:BuildConfigBlock('allowlist', l:report.info)
|
||||
let l:msg .= s:BuildConfigBlock('blocklist', l:report.info)
|
||||
let l:cfg = get(l:report.info, 'workspace_config', '')
|
||||
if !empty(l:cfg)
|
||||
if get(g:, 'loaded_scriptease', 0)
|
||||
let l:cfg = scriptease#dump(l:cfg, {'width': &columns-1})
|
||||
else
|
||||
let l:cfg = json_encode(l:cfg)
|
||||
" Add some whitespace to make it readable.
|
||||
let l:cfg = substitute(l:cfg, '[,{(\[]', "&\n\t", 'g')
|
||||
let l:cfg = substitute(l:cfg, '":', '& ', 'g')
|
||||
let l:cfg = substitute(l:cfg, '\v[})\]]+', "\n&", 'g')
|
||||
let l:cfg = substitute(l:cfg, '\n\s*\n', "\n", 'g')
|
||||
endif
|
||||
let l:msg .= printf("### workspace_config\n```json\n%s\n```", l:cfg)
|
||||
endif
|
||||
call health#report_info(l:msg)
|
||||
endfor
|
||||
|
||||
call health#report_start('Performance')
|
||||
if lsp#utils#has_lua() && g:lsp_use_lua
|
||||
call health#report_ok('Using lua for faster performance.')
|
||||
else
|
||||
call health#report_warn('Missing requirements to enable lua for faster performance.')
|
||||
endif
|
||||
|
||||
endf
|
||||
|
||||
@@ -14,7 +14,7 @@ let s:notification_callbacks = [] " { name, callback }
|
||||
" "bingo": [ "first-line", "next-line", ... ]
|
||||
" },
|
||||
" 2: {
|
||||
" "pyls": [ "first-line", "next-line", ... ]
|
||||
" "pylsp": [ "first-line", "next-line", ... ]
|
||||
" }
|
||||
" }
|
||||
let s:file_content = {}
|
||||
@@ -148,7 +148,22 @@ let s:color_map = {
|
||||
\ 'not running': 'Comment'
|
||||
\}
|
||||
|
||||
" Print the current status of all servers (if called with no arguments)
|
||||
" Collect the current status of all servers
|
||||
function! lsp#collect_server_status() abort
|
||||
let l:results = {}
|
||||
for l:k in keys(s:servers)
|
||||
let l:status = s:server_status(l:k)
|
||||
" Copy to prevent callers from corrupting our config.
|
||||
let l:info = deepcopy(s:servers[l:k].server_info)
|
||||
let l:results[l:k] = {
|
||||
\ 'status': l:status,
|
||||
\ 'info': l:info,
|
||||
\ }
|
||||
endfor
|
||||
return l:results
|
||||
endfunction
|
||||
|
||||
" Print the current status of all servers
|
||||
function! lsp#print_server_status() abort
|
||||
for l:k in sort(keys(s:servers))
|
||||
let l:status = s:server_status(l:k)
|
||||
@@ -157,6 +172,15 @@ function! lsp#print_server_status() abort
|
||||
echon l:status
|
||||
echohl None
|
||||
echo ''
|
||||
if &verbose
|
||||
let l:cfg = { 'workspace_config': s:servers[l:k].server_info.workspace_config }
|
||||
if get(g:, 'loaded_scriptease', 0)
|
||||
call scriptease#pp_command(0, -1, l:cfg)
|
||||
else
|
||||
echo json_encode(l:cfg)
|
||||
endif
|
||||
echo ''
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
@@ -484,6 +508,9 @@ function! lsp#default_get_supported_capabilities(server_info) abort
|
||||
" Sorted alphabetically
|
||||
return {
|
||||
\ 'textDocument': {
|
||||
\ 'callHierarchy': {
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ },
|
||||
\ 'codeAction': {
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ 'codeActionLiteralSupport': {
|
||||
@@ -548,12 +575,20 @@ function! lsp#default_get_supported_capabilities(server_info) abort
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ 'linkSupport' : v:true
|
||||
\ },
|
||||
\ 'publishDiagnostics': {
|
||||
\ 'relatedInformation': v:true,
|
||||
\ },
|
||||
\ 'rangeFormatting': {
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ },
|
||||
\ 'references': {
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ },
|
||||
\ 'rename': {
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ 'prepareSupport': v:true,
|
||||
\ 'prepareSupportDefaultBehavior': 1
|
||||
\ },
|
||||
\ 'semanticTokens': {
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ 'requests': {
|
||||
@@ -576,8 +611,8 @@ function! lsp#default_get_supported_capabilities(server_info) abort
|
||||
\ 'multilineTokenSupport': v:false,
|
||||
\ 'serverCancelSupport': v:false
|
||||
\ },
|
||||
\ 'publishDiagnostics': {
|
||||
\ 'relatedInformation': v:true,
|
||||
\ 'signatureHelp': {
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ },
|
||||
\ 'synchronization': {
|
||||
\ 'didSave': v:true,
|
||||
@@ -585,13 +620,13 @@ function! lsp#default_get_supported_capabilities(server_info) abort
|
||||
\ 'willSave': v:false,
|
||||
\ 'willSaveWaitUntil': v:false,
|
||||
\ },
|
||||
\ 'typeHierarchy': {
|
||||
\ 'dynamicRegistration': v:false
|
||||
\ },
|
||||
\ 'typeDefinition': {
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ 'linkSupport' : v:true
|
||||
\ },
|
||||
\ 'typeHierarchy': {
|
||||
\ 'dynamicRegistration': v:false
|
||||
\ },
|
||||
\ },
|
||||
\ 'window': {
|
||||
\ 'workDoneProgress': g:lsp_work_done_progress_enabled ? v:true : v:false,
|
||||
@@ -599,6 +634,9 @@ function! lsp#default_get_supported_capabilities(server_info) abort
|
||||
\ 'workspace': {
|
||||
\ 'applyEdit': v:true,
|
||||
\ 'configuration': v:true,
|
||||
\ 'symbol': {
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ },
|
||||
\ 'workspaceFolders': g:lsp_experimental_workspace_folders ? v:true : v:false,
|
||||
\ },
|
||||
\ }
|
||||
|
||||
@@ -131,8 +131,11 @@ function! s:place_signs(server, diagnostics_response, bufnr) abort
|
||||
let l:line = lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['range']['start'])
|
||||
|
||||
" Some language servers report an unexpected EOF one line past the end
|
||||
if l:line == getbufinfo(a:bufnr)[0].linecount + 1
|
||||
let l:line = l:line - 1
|
||||
" key 'linecount' may be missing.
|
||||
if has_key(getbufinfo(a:bufnr)[0], 'linecount')
|
||||
if l:line == getbufinfo(a:bufnr)[0].linecount + 1
|
||||
let l:line = l:line - 1
|
||||
endif
|
||||
endif
|
||||
|
||||
if has_key(l:item, 'severity') && !empty(l:item['severity'])
|
||||
|
||||
@@ -183,20 +183,16 @@ function! s:in_reference(reference_list) abort
|
||||
endfunction
|
||||
|
||||
function! s:init_reference_highlight(buf) abort
|
||||
if !empty(getbufvar(a:buf, 'lsp_did_reference_setup'))
|
||||
return
|
||||
endif
|
||||
|
||||
if s:use_vim_textprops
|
||||
call prop_type_add('vim-lsp-reference-highlight', {
|
||||
let l:props = {
|
||||
\ 'bufnr': a:buf,
|
||||
\ 'highlight': 'lspReference',
|
||||
\ 'combine': v:true,
|
||||
\ 'priority': lsp#internal#textprop#priority('document_highlight')
|
||||
\ })
|
||||
\ }
|
||||
call prop_type_delete('vim-lsp-reference-highlight', l:props)
|
||||
call prop_type_add('vim-lsp-reference-highlight', l:props)
|
||||
endif
|
||||
|
||||
call setbufvar(a:buf, 'lsp_did_reference_setup', 1)
|
||||
endfunction
|
||||
|
||||
" Cyclically move between references by `offset` occurrences.
|
||||
|
||||
@@ -27,7 +27,7 @@ function! lsp#internal#semantic#_enable() abort
|
||||
\ lsp#callbag#fromEvent(l:events),
|
||||
\ lsp#callbag#filter({_->lsp#internal#semantic#is_enabled()}),
|
||||
\ lsp#callbag#debounceTime(g:lsp_semantic_delay),
|
||||
\ lsp#callbag#filter({_->getbufvar(bufnr('%'), '&buftype') !~# '^(help\|terminal\|prompt\|popup)$'}),
|
||||
\ lsp#callbag#filter({_->index(['help', 'terminal', 'prompt', 'popup'], getbufvar(bufnr('%'), '&buftype')) ==# -1}),
|
||||
\ lsp#callbag#filter({_->!lsp#utils#is_large_window(win_getid())}),
|
||||
\ lsp#callbag#switchMap({_->
|
||||
\ lsp#callbag#pipe(
|
||||
@@ -212,8 +212,10 @@ endfunction
|
||||
|
||||
function! s:handle_semantic_tokens_response(server, buf, result) abort
|
||||
let l:highlights = {}
|
||||
let l:legend = lsp#internal#semantic#get_legend(a:server)
|
||||
for l:token in s:decode_tokens(a:result['data'])
|
||||
let l:highlights = s:add_highlight(l:highlights, a:server, a:buf, l:token)
|
||||
let [l:key, l:value] = s:add_highlight(a:server, l:legend, a:buf, l:token)
|
||||
let l:highlights[l:key] = get(l:highlights, l:key, []) + l:value
|
||||
endfor
|
||||
call s:apply_highlights(a:server, a:buf, l:highlights)
|
||||
|
||||
@@ -241,8 +243,10 @@ function! s:handle_semantic_tokens_delta_response(server, buf, result) abort
|
||||
call setbufvar(a:buf, 'lsp_semantic_local_data', l:localdata)
|
||||
|
||||
let l:highlights = {}
|
||||
let l:legend = lsp#internal#semantic#get_legend(a:server)
|
||||
for l:token in s:decode_tokens(l:localdata)
|
||||
let l:highlights = s:add_highlight(l:highlights, a:server, a:buf, l:token)
|
||||
let [l:key, l:value] = s:add_highlight(a:server, l:legend, a:buf, l:token)
|
||||
let l:highlights[l:key] = get(l:highlights, l:key, []) + l:value
|
||||
endfor
|
||||
call s:apply_highlights(a:server, a:buf, l:highlights)
|
||||
endfunction
|
||||
@@ -275,36 +279,32 @@ endfunction
|
||||
|
||||
function! s:clear_highlights(server, buf) abort
|
||||
if s:use_vim_textprops
|
||||
let l:IsSemanticTextprop = {textprop -> textprop['type'] =~ '^' . s:textprop_type_prefix . '.*'}
|
||||
let l:textprop_types = prop_list(1, {'bufnr': a:buf, 'end_lnum': -1})
|
||||
let l:BeginsWith = {str, prefix -> str[0:len(prefix) - 1] ==# prefix}
|
||||
let l:IsSemanticTextprop = {_, textprop -> l:BeginsWith(textprop, s:textprop_type_prefix)}
|
||||
let l:textprop_types = prop_type_list()
|
||||
call filter(l:textprop_types, l:IsSemanticTextprop)
|
||||
for l:textprop_type in l:textprop_types
|
||||
silent! call prop_remove({'type': l:textprop_type, 'bufnr': a:buf, 'all': v:true}, 1, line('$'))
|
||||
silent! call prop_remove({'type': l:textprop_type, 'bufnr': a:buf, 'all': v:true})
|
||||
endfor
|
||||
elseif s:use_nvim_highlight
|
||||
call nvim_buf_clear_namespace(a:buf, s:namespace_id, 0, line('$'))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:add_highlight(highlights, server, buf, token) abort
|
||||
let l:legend = lsp#internal#semantic#get_legend(a:server)
|
||||
function! s:add_highlight(server, legend, buf, token) abort
|
||||
let l:startpos = lsp#utils#position#lsp_to_vim(a:buf, a:token['pos'])
|
||||
let l:endpos = a:token['pos']
|
||||
let l:endpos['character'] = l:endpos['character'] + a:token['length']
|
||||
let l:endpos = lsp#utils#position#lsp_to_vim(a:buf, l:endpos)
|
||||
|
||||
if s:use_vim_textprops
|
||||
let l:textprop_name = s:get_textprop_type(a:server, a:token['token_idx'], a:token['token_modifiers'])
|
||||
let a:highlights[l:textprop_name] = get(a:highlights, l:textprop_name, [])
|
||||
\ + [[l:startpos[0], l:startpos[1], l:endpos[0], l:endpos[1]]]
|
||||
let l:textprop_name = s:get_textprop_type(a:server, a:legend, a:token['token_idx'], a:token['token_modifiers'])
|
||||
return [l:textprop_name, [[l:startpos[0], l:startpos[1], l:endpos[0], l:endpos[1]]]]
|
||||
elseif s:use_nvim_highlight
|
||||
let l:char = a:token['pos']['character']
|
||||
let l:hl_name = s:get_hl_group(a:server, a:token['token_idx'], a:token['token_modifiers'])
|
||||
let a:highlights[l:hl_name] = get(a:highlights, l:hl_name, [])
|
||||
\ + [[l:startpos[0] - 1, l:startpos[1] - 1, l:endpos[1] - 1]]
|
||||
let l:hl_name = s:get_hl_group(a:server, a:legend, a:token['token_idx'], a:token['token_modifiers'])
|
||||
return [l:hl_name, [[l:startpos[0] - 1, l:startpos[1] - 1, l:endpos[1] - 1]]]
|
||||
endif
|
||||
|
||||
return a:highlights
|
||||
endfunction
|
||||
|
||||
function! s:apply_highlights(server, buf, highlights) abort
|
||||
@@ -342,28 +342,29 @@ let s:default_highlight_groups = {
|
||||
\ s:hl_group_prefix . 'Variable': 'Identifier',
|
||||
\ s:hl_group_prefix . 'Property': 'Identifier',
|
||||
\ s:hl_group_prefix . 'EnumMember': 'Constant',
|
||||
\ s:hl_group_prefix . 'Events': 'Identifier',
|
||||
\ s:hl_group_prefix . 'Event': 'Identifier',
|
||||
\ s:hl_group_prefix . 'Function': 'Function',
|
||||
\ s:hl_group_prefix . 'Method': 'Function',
|
||||
\ s:hl_group_prefix . 'Macro': 'Macro',
|
||||
\ s:hl_group_prefix . 'Keyword': 'Keyword',
|
||||
\ s:hl_group_prefix . 'Modifier': 'Type',
|
||||
\ s:hl_group_prefix . 'Comment': 'Comment',
|
||||
\ s:hl_group_prefix . 'String': 'String',
|
||||
\ s:hl_group_prefix . 'Number': 'Number',
|
||||
\ s:hl_group_prefix . 'Regexp': 'String',
|
||||
\ s:hl_group_prefix . 'Operator': 'Operator'
|
||||
\ s:hl_group_prefix . 'Operator': 'Operator',
|
||||
\ s:hl_group_prefix . 'Decorator': 'Macro'
|
||||
\ }
|
||||
|
||||
function! s:get_hl_group(server, token_idx, token_modifiers) abort
|
||||
function! s:get_hl_group(server, legend, token_idx, token_modifiers) abort
|
||||
" get highlight group name
|
||||
let l:legend = lsp#internal#semantic#get_legend(a:server)
|
||||
let l:Capitalise = {str -> toupper(str[0]) . str[1:]}
|
||||
let l:token_name = l:Capitalise(l:legend['tokenTypes'][a:token_idx])
|
||||
let l:token_name = l:Capitalise(a:legend['tokenTypes'][a:token_idx])
|
||||
let l:token_modifiers = []
|
||||
for l:modifier_idx in range(len(l:legend['tokenModifiers']))
|
||||
for l:modifier_idx in range(len(a:legend['tokenModifiers']))
|
||||
" float2nr(pow(2, a)) is 1 << a
|
||||
if and(a:token_modifiers, float2nr(pow(2, l:modifier_idx)))
|
||||
let l:modifier_name = l:legend['tokenModifiers'][l:modifier_idx]
|
||||
let l:modifier_name = a:legend['tokenModifiers'][l:modifier_idx]
|
||||
call add(l:token_modifiers, l:Capitalise(l:modifier_name))
|
||||
endif
|
||||
endfor
|
||||
@@ -378,7 +379,7 @@ function! s:get_hl_group(server, token_idx, token_modifiers) abort
|
||||
exec 'highlight link' l:hl_group s:default_highlight_groups[l:hl_group]
|
||||
else
|
||||
if a:token_modifiers != 0
|
||||
let l:base_hl_group = s:get_hl_group(a:server, a:token_idx, 0)
|
||||
let l:base_hl_group = s:get_hl_group(a:server, a:legend, a:token_idx, 0)
|
||||
exec 'highlight link' l:hl_group l:base_hl_group
|
||||
else
|
||||
exec 'highlight link' l:hl_group 'Normal'
|
||||
@@ -391,13 +392,13 @@ endfunction
|
||||
|
||||
let s:textprop_type_prefix = 'vim-lsp-semantic-'
|
||||
|
||||
function! s:get_textprop_type(server, token_idx, token_modifiers) abort
|
||||
function! s:get_textprop_type(server, legend, token_idx, token_modifiers) abort
|
||||
" get textprop type name
|
||||
let l:textprop_type = s:textprop_type_prefix . a:server . '-' . a:token_idx . '-' . a:token_modifiers
|
||||
|
||||
" create the textprop type if it does not already exist
|
||||
if prop_type_get(l:textprop_type) ==# {}
|
||||
let l:hl_group = s:get_hl_group(a:server, a:token_idx, a:token_modifiers)
|
||||
let l:hl_group = s:get_hl_group(a:server, a:legend, a:token_idx, a:token_modifiers)
|
||||
silent! call prop_type_add(l:textprop_type, {
|
||||
\ 'highlight': l:hl_group,
|
||||
\ 'combine': v:true,
|
||||
|
||||
39
autoload/lsp/internal/ui/popupmenu.vim
Normal file
39
autoload/lsp/internal/ui/popupmenu.vim
Normal file
@@ -0,0 +1,39 @@
|
||||
let s:Markdown = vital#lsp#import('VS.Vim.Syntax.Markdown')
|
||||
let s:Window = vital#lsp#import('VS.Vim.Window')
|
||||
|
||||
function! lsp#internal#ui#popupmenu#open(opt) abort
|
||||
let l:Callback = remove(a:opt, 'callback')
|
||||
let l:items = remove(a:opt, 'items')
|
||||
|
||||
let l:items_with_shortcuts= map(l:items, {
|
||||
\ idx, item -> ((idx < 9) ? '['.(idx+1).'] ' : '').item
|
||||
\ })
|
||||
|
||||
function! Filter(id, key) abort closure
|
||||
if a:key >= 1 && a:key <= len(l:items)
|
||||
call popup_close(a:id, a:key)
|
||||
elseif a:key ==# "\<C-j>"
|
||||
call win_execute(a:id, 'normal! j')
|
||||
elseif a:key ==# "\<C-k>"
|
||||
call win_execute(a:id, 'normal! k')
|
||||
else
|
||||
return popup_filter_menu(a:id, a:key)
|
||||
endif
|
||||
|
||||
return v:true
|
||||
endfunction
|
||||
|
||||
let l:popup_opt = extend({
|
||||
\ 'callback': funcref('s:callback', [l:Callback]),
|
||||
\ 'filter': funcref('Filter'),
|
||||
\ }, a:opt)
|
||||
|
||||
let l:winid = popup_menu(l:items_with_shortcuts, l:popup_opt)
|
||||
call s:Window.do(l:winid, { -> s:Markdown.apply() })
|
||||
execute('doautocmd <nomodeline> User lsp_float_opened')
|
||||
endfunction
|
||||
|
||||
function! s:callback(callback, id, selected) abort
|
||||
call a:callback(a:id, a:selected)
|
||||
execute('doautocmd <nomodeline> User lsp_float_closed')
|
||||
endfunction
|
||||
@@ -119,7 +119,7 @@ function! lsp#ui#vim#rename() abort
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'position': lsp#get_position(),
|
||||
\ },
|
||||
\ 'on_notification': function('s:handle_rename_prepare', [l:server, l:command_id, 'rename_prepare']),
|
||||
\ 'on_notification': function('s:handle_rename_prepare', [l:server, l:command_id, 'rename_prepare', expand('<cword>'), lsp#get_position()]),
|
||||
\ })
|
||||
return
|
||||
endif
|
||||
@@ -270,7 +270,7 @@ function! s:handle_location(ctx, server, type, data) abort "ctx = {counter, list
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:handle_rename_prepare(server, last_command_id, type, data) abort
|
||||
function! s:handle_rename_prepare(server, last_command_id, type, cword, position, data) abort
|
||||
if a:last_command_id != lsp#_last_command()
|
||||
return
|
||||
endif
|
||||
@@ -279,8 +279,28 @@ function! s:handle_rename_prepare(server, last_command_id, type, data) abort
|
||||
call lsp#utils#error('Failed to retrieve '. a:type . ' for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
|
||||
return
|
||||
endif
|
||||
let l:result = a:data['response']['result']
|
||||
|
||||
let l:range = a:data['response']['result']
|
||||
" Check response: null.
|
||||
if empty(l:result)
|
||||
echo 'The ' . a:server . ' returns for ' . a:type . ' (The rename request may be invalid at the given position).'
|
||||
return
|
||||
endif
|
||||
|
||||
" Check response: { defaultBehavior: boolean }.
|
||||
if has_key(l:result, 'defaultBehavior')
|
||||
call timer_start(1, {x->s:rename(a:server, input('new name: ', a:cword), a:position)})
|
||||
return
|
||||
endif
|
||||
|
||||
" Check response: { placeholder: string }
|
||||
if has_key(l:result, 'placeholder') && !empty(l:result['placeholder'])
|
||||
call timer_start(1, {x->s:rename(a:server, input('new name: ', a:cword), a:position)})
|
||||
return
|
||||
endif
|
||||
|
||||
" Check response: { range: Range } | Range
|
||||
let l:range = get(l:result, 'range', l:result)
|
||||
let l:lines = getline(1, '$')
|
||||
let [l:start_line, l:start_col] = lsp#utils#position#lsp_to_vim('%', l:range['start'])
|
||||
let [l:end_line, l:end_col] = lsp#utils#position#lsp_to_vim('%', l:range['end'])
|
||||
@@ -331,12 +351,12 @@ function! s:handle_text_edit(server, last_command_id, type, data) abort
|
||||
redraw | echo 'Document formatted'
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#code_action() abort
|
||||
call lsp#ui#vim#code_action#do({
|
||||
function! lsp#ui#vim#code_action(opts) abort
|
||||
call lsp#ui#vim#code_action#do(extend({
|
||||
\ 'sync': v:false,
|
||||
\ 'selection': v:false,
|
||||
\ 'query': '',
|
||||
\ })
|
||||
\ }, a:opts))
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#code_lens() abort
|
||||
|
||||
@@ -14,12 +14,17 @@ endfunction
|
||||
" selection: v:true | v:false = Provide by CommandLine like `:'<,'>LspCodeAction`
|
||||
" sync: v:true | v:false = Specify enable synchronous request. Example use case is `BufWritePre`
|
||||
" query: string = Specify code action kind query. If query provided and then filtered code action is only one, invoke code action immediately.
|
||||
" ui: 'float' | 'preview'
|
||||
" }
|
||||
"
|
||||
function! lsp#ui#vim#code_action#do(option) abort
|
||||
let l:selection = get(a:option, 'selection', v:false)
|
||||
let l:sync = get(a:option, 'sync', v:false)
|
||||
let l:query = get(a:option, 'query', '')
|
||||
let l:ui = get(a:option, 'ui', g:lsp_code_action_ui)
|
||||
if empty(l:ui)
|
||||
let l:ui = lsp#utils#_has_popup_menu() ? 'float' : 'preview'
|
||||
endif
|
||||
|
||||
let l:server_names = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_code_action_provider(v:val)')
|
||||
if len(l:server_names) == 0
|
||||
@@ -51,13 +56,13 @@ function! lsp#ui#vim#code_action#do(option) abort
|
||||
\ },
|
||||
\ },
|
||||
\ 'sync': l:sync,
|
||||
\ 'on_notification': function('s:handle_code_action', [l:ctx, l:server_name, l:command_id, l:sync, l:query, l:bufnr]),
|
||||
\ 'on_notification': function('s:handle_code_action', [l:ui, l:ctx, l:server_name, l:command_id, l:sync, l:query, l:bufnr]),
|
||||
\ })
|
||||
endfor
|
||||
echo 'Retrieving code actions ...'
|
||||
endfunction
|
||||
|
||||
function! s:handle_code_action(ctx, server_name, command_id, sync, query, bufnr, data) abort
|
||||
function! s:handle_code_action(ui, ctx, server_name, command_id, sync, query, bufnr, data) abort
|
||||
" Ignore old request.
|
||||
if a:command_id != lsp#_last_command()
|
||||
return
|
||||
@@ -130,14 +135,34 @@ function! s:handle_code_action(ctx, server_name, command_id, sync, query, bufnr,
|
||||
endif
|
||||
call add(l:items, { 'title': l:title, 'item': l:action })
|
||||
endfor
|
||||
call lsp#internal#ui#quickpick#open({
|
||||
\ 'items': l:items,
|
||||
\ 'key': 'title',
|
||||
\ 'on_accept': funcref('s:accept_code_action', [a:sync, a:bufnr]),
|
||||
\ })
|
||||
|
||||
if lsp#utils#_has_popup_menu() && a:ui ==? 'float'
|
||||
call lsp#internal#ui#popupmenu#open({
|
||||
\ 'title': 'Code actions',
|
||||
\ 'items': mapnew(l:items, { idx, item -> item.title}),
|
||||
\ 'pos': 'topleft',
|
||||
\ 'line': 'cursor+1',
|
||||
\ 'col': 'cursor',
|
||||
\ 'callback': funcref('s:popup_accept_code_action', [a:sync, a:bufnr, l:items]),
|
||||
\ })
|
||||
else
|
||||
call lsp#internal#ui#quickpick#open({
|
||||
\ 'items': l:items,
|
||||
\ 'key': 'title',
|
||||
\ 'on_accept': funcref('s:quickpick_accept_code_action', [a:sync, a:bufnr]),
|
||||
\ })
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:accept_code_action(sync, bufnr, data, ...) abort
|
||||
function! s:popup_accept_code_action(sync, bufnr, items, id, selected, ...) abort
|
||||
if a:selected <= 0 | return | endif
|
||||
let l:item = a:items[a:selected - 1]['item']
|
||||
if s:handle_disabled_action(l:item) | return | endif
|
||||
call s:handle_one_code_action(l:item['server_name'], a:sync, a:bufnr, l:item['code_action'])
|
||||
execute('doautocmd <nomodeline> User lsp_float_closed')
|
||||
endfunction
|
||||
|
||||
function! s:quickpick_accept_code_action(sync, bufnr, data, ...) abort
|
||||
call lsp#internal#ui#quickpick#close()
|
||||
if empty(a:data['items']) | return | endif
|
||||
let l:selected = a:data['items'][0]['item']
|
||||
|
||||
@@ -34,6 +34,11 @@ function! lsp#utils#_has_highlights() abort
|
||||
return s:has_higlights
|
||||
endfunction
|
||||
|
||||
let s:has_popup_menu = exists('*popup_menu')
|
||||
function! lsp#utils#_has_popup_menu() abort
|
||||
return s:has_popup_menu
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#is_file_uri(uri) abort
|
||||
return stridx(a:uri, 'file:///') == 0
|
||||
endfunction
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
function! lsp#utils#args#_parse(args, opt) abort
|
||||
function! lsp#utils#args#_parse(args, opt, remainder_key) abort
|
||||
let l:result = {}
|
||||
let l:is_opts = v:true
|
||||
let l:remainder = []
|
||||
for l:item in split(a:args, ' ')
|
||||
if l:item[:1] !=# '--'
|
||||
let l:is_opts = v:false
|
||||
endif
|
||||
|
||||
if l:is_opts == v:false
|
||||
call add(l:remainder, l:item)
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:parts = split(l:item, '=')
|
||||
let l:key = l:parts[0]
|
||||
let l:value = get(l:parts, 1, '')
|
||||
let l:key = l:key[2:]
|
||||
|
||||
if has_key(a:opt, l:key)
|
||||
if has_key(a:opt[l:key], 'type')
|
||||
let l:type = a:opt[l:key]['type']
|
||||
@@ -21,5 +33,10 @@ function! lsp#utils#args#_parse(args, opt) abort
|
||||
endif
|
||||
let l:result[l:key] = l:value
|
||||
endfor
|
||||
|
||||
if a:remainder_key != v:null
|
||||
let l:result[a:remainder_key] = join(l:remainder)
|
||||
endif
|
||||
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
@@ -11,6 +11,7 @@ CONTENTS *vim-lsp-contents*
|
||||
Configure |vim-lsp-configure|
|
||||
vim-lsp-settings |vim-lsp-settings_plugin|
|
||||
Wiki |vim-lsp-configure-wiki|
|
||||
Health Check |vim-lsp-healthcheck|
|
||||
Options |vim-lsp-options|
|
||||
g:lsp_auto_enable |g:lsp_auto_enable|
|
||||
g:lsp_preview_keep_focus |g:lsp_preview_keep_focus|
|
||||
@@ -218,19 +219,19 @@ vim-lsp doesn't ship with any language servers. The user is responsible for
|
||||
configuring the language servers correctly.
|
||||
|
||||
Here is an example of configuring the python language server protocol based
|
||||
on pyls (https://github.com/palantir/python-language-server)
|
||||
on pylsp (https://github.com/python-lsp/python-lsp-server)
|
||||
|
||||
1. Make sure the language server is available locally in the machine.
|
||||
For python, pip package manager can be used to install the language server.
|
||||
>
|
||||
pip install python-language-server
|
||||
pip install python-lsp-server
|
||||
|
||||
2. Register the language server in your .vimrc
|
||||
>
|
||||
if (executable('pyls'))
|
||||
if (executable('pylsp'))
|
||||
au User lsp_setup call lsp#register_server({
|
||||
\ 'name': 'pyls',
|
||||
\ 'cmd': {server_info->['pyls']},
|
||||
\ 'name': 'pylsp',
|
||||
\ 'cmd': {server_info->['pylsp']},
|
||||
\ 'allowlist': ['python']
|
||||
\ })
|
||||
endif
|
||||
@@ -271,6 +272,13 @@ to automatically register various language servers.
|
||||
Plug 'prabirshrestha/vim-lsp'
|
||||
Plug 'mattn/vim-lsp-settings'
|
||||
|
||||
HEALTH CHECK *vim-lsp-healthcheck*
|
||||
vim-lsp supports the |:CheckHealth| command which can be useful when debugging
|
||||
lsp configuration issues.
|
||||
|
||||
This command is included in neovim and implemented in vim with the
|
||||
[vim-healthcheck](https://github.com/rhysd/vim-healthcheck) plugin.
|
||||
|
||||
WIKI *vim-lsp-configure-wiki*
|
||||
For documentation on how to configure other language servers refer
|
||||
to https://github.com/prabirshrestha/vim-lsp/wiki/Servers
|
||||
@@ -1094,7 +1102,7 @@ Used to register the language server with vim-lsp. This method takes
|
||||
one parameter which is a vim |dict| and is referred to as |vim-lsp-server_info|
|
||||
|
||||
Example: >
|
||||
if (executable('pyls'))
|
||||
if (executable('pylsp'))
|
||||
au User lsp_setup call lsp#register_server({
|
||||
\ 'name': 'name-of-server',
|
||||
\ 'cmd': {server_info->['server-exectuable']},
|
||||
@@ -1146,7 +1154,7 @@ The vim |dict| containing information about the server.
|
||||
can be determined statically and |vim-lsp-server_info| is not necessary.
|
||||
|
||||
Example: >
|
||||
'cmd': ['pyls']
|
||||
'cmd': ['pylsp']
|
||||
<
|
||||
Function can be complex based on custom requirements.
|
||||
For example:
|
||||
@@ -1199,7 +1207,7 @@ The vim |dict| containing information about the server.
|
||||
initialization. Configuration settings are language-server specific.
|
||||
|
||||
Example: >
|
||||
'workspace_config': {'pyls': {'plugins': \
|
||||
'workspace_config': {'pylsp': {'plugins': \
|
||||
{'pydocstyle': {'enabled': v:true}}}}
|
||||
<
|
||||
* languageId:
|
||||
@@ -1570,7 +1578,7 @@ LspCallHierarchyOutgoing *:LspCallHierarchyOutgoing*
|
||||
|
||||
Find outgoing call hierarchy for the symbol under cursor.
|
||||
|
||||
LspCodeAction [{CodeActionKind}] *:LspCodeAction*
|
||||
LspCodeAction [--ui=float|preview] [{CodeActionKind}] *:LspCodeAction*
|
||||
|
||||
Gets a list of possible commands that can be applied to a file so it can be
|
||||
fixed (quick fix).
|
||||
@@ -1578,7 +1586,7 @@ fixed (quick fix).
|
||||
If the optional {CodeActionKind} specified, will invoke code action
|
||||
immediately when matched code action is one only.
|
||||
|
||||
LspCodeActionSync [{CodeActionKind}] *:LspCodeActionSync*
|
||||
LspCodeActionSync [--ui=float|preview] [{CodeActionKind}] *:LspCodeActionSync*
|
||||
|
||||
Same as |:LspCodeAction| but synchronous. Useful when running |:autocmd|
|
||||
commands such as organize imports before save.
|
||||
@@ -1787,7 +1795,9 @@ Servers may choose to return empty results if the search query is empty.
|
||||
|
||||
LspStatus *:LspStatus*
|
||||
|
||||
Prints the status of all registered servers.
|
||||
Prints the status of all registered servers. Use `:verbose LspStatus` to
|
||||
additionally show each server's workspace_config.
|
||||
See also |vim-lsp-healthcheck|.
|
||||
|
||||
LspStopServer *:LspStopServer*
|
||||
|
||||
@@ -1882,6 +1892,8 @@ Available plug mappings are following:
|
||||
nnoremap <plug>(lsp-call-hierarchy-incoming)
|
||||
nnoremap <plug>(lsp-call-hierarchy-outgoing)
|
||||
nnoremap <plug>(lsp-code-action)
|
||||
nnoremap <plug>(lsp-code-action-float)
|
||||
nnoremap <plug>(lsp-code-action-preview)
|
||||
nnoremap <plug>(lsp-code-lens)
|
||||
nnoremap <plug>(lsp-declaration)
|
||||
nnoremap <plug>(lsp-peek-declaration)
|
||||
|
||||
@@ -25,11 +25,11 @@ inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"
|
||||
inoremap <expr> <cr> pumvisible() ? "\<C-y>\<cr>" : "\<cr>"
|
||||
autocmd! CompleteDone * if pumvisible() == 0 | pclose | endif
|
||||
|
||||
if executable('pyls')
|
||||
" pip install python-language-server
|
||||
if executable('pylsp')
|
||||
" pip install python-lsp-server
|
||||
au User lsp_setup call lsp#register_server({
|
||||
\ 'name': 'pyls',
|
||||
\ 'cmd': {server_info->['pyls']},
|
||||
\ 'name': 'pylsp',
|
||||
\ 'cmd': {server_info->['pylsp']},
|
||||
\ 'allowlist': ['python'],
|
||||
\ })
|
||||
endif
|
||||
|
||||
@@ -75,6 +75,7 @@ let g:lsp_work_done_progress_enabled = get(g:, 'lsp_work_done_progress_enabled',
|
||||
let g:lsp_untitled_buffer_enabled = get(g:, 'lsp_untitled_buffer_enabled', 1)
|
||||
let g:lsp_inlay_hints_enabled = get(g:, 'lsp_inlay_hints_enabled', 0)
|
||||
let g:lsp_inlay_hints_delay = get(g:, 'lsp_inlay_hints_delay', 350)
|
||||
let g:lsp_code_action_ui = get(g:, 'lsp_code_action_ui', 'preview')
|
||||
|
||||
let g:lsp_get_supported_capabilities = get(g:, 'lsp_get_supported_capabilities', [function('lsp#default_get_supported_capabilities')])
|
||||
|
||||
@@ -90,16 +91,14 @@ endif
|
||||
command! LspAddTreeCallHierarchyIncoming call lsp#ui#vim#add_tree_call_hierarchy_incoming()
|
||||
command! LspCallHierarchyIncoming call lsp#ui#vim#call_hierarchy_incoming({})
|
||||
command! LspCallHierarchyOutgoing call lsp#ui#vim#call_hierarchy_outgoing()
|
||||
command! -range -nargs=* -complete=customlist,lsp#ui#vim#code_action#complete LspCodeAction call lsp#ui#vim#code_action#do({
|
||||
\ 'sync': v:false,
|
||||
\ 'selection': <range> != 0,
|
||||
\ 'query': '<args>'
|
||||
\ })
|
||||
command! -range -nargs=* -complete=customlist,lsp#ui#vim#code_action#complete LspCodeActionSync call lsp#ui#vim#code_action#do({
|
||||
\ 'sync': v:true,
|
||||
\ 'selection': <range> != 0,
|
||||
\ 'query': '<args>'
|
||||
\ })
|
||||
command! -range -nargs=* -complete=customlist,lsp#ui#vim#code_action#complete LspCodeAction call lsp#ui#vim#code_action#do(
|
||||
\ extend({ 'sync': v:false, 'selection': <range> != 0 }, lsp#utils#args#_parse(<q-args>, {
|
||||
\ 'ui': { 'type': type('') },
|
||||
\ }, 'query')))
|
||||
command! -range -nargs=* -complete=customlist,lsp#ui#vim#code_action#complete LspCodeActionSync call lsp#ui#vim#code_action#do(
|
||||
\ extend({ 'sync': v:true, 'selection': <range> != 0 }, lsp#utils#args#_parse(<q-args>, {
|
||||
\ 'ui': { 'type': type('') },
|
||||
\ }, 'query')))
|
||||
command! LspCodeLens call lsp#ui#vim#code_lens#do({})
|
||||
command! LspDeclaration call lsp#ui#vim#declaration(0, <q-mods>)
|
||||
command! LspPeekDeclaration call lsp#ui#vim#declaration(1)
|
||||
@@ -110,11 +109,11 @@ command! LspDocumentSymbolSearch call lsp#internal#document_symbol#search#do({})
|
||||
command! -nargs=? LspDocumentDiagnostics call lsp#internal#diagnostics#document_diagnostics_command#do(
|
||||
\ extend({}, lsp#utils#args#_parse(<q-args>, {
|
||||
\ 'buffers': {'type': type('')},
|
||||
\ })))
|
||||
\ }, v:null)))
|
||||
command! -nargs=? -complete=customlist,lsp#utils#empty_complete LspHover call lsp#internal#document_hover#under_cursor#do(
|
||||
\ extend({}, lsp#utils#args#_parse(<q-args>, {
|
||||
\ 'ui': { 'type': type('') },
|
||||
\ })))
|
||||
\ }, v:null)))
|
||||
command! -nargs=* LspNextError call lsp#internal#diagnostics#movement#_next_error(<f-args>)
|
||||
command! -nargs=* LspPreviousError call lsp#internal#diagnostics#movement#_previous_error(<f-args>)
|
||||
command! -nargs=* LspNextWarning call lsp#internal#diagnostics#movement#_next_warning(<f-args>)
|
||||
@@ -133,13 +132,13 @@ command! -range -nargs=? LspDocumentFormatSync call lsp#internal#document_format
|
||||
\ extend({'bufnr': bufnr('%'), 'sync': 1 }, lsp#utils#args#_parse(<q-args>, {
|
||||
\ 'timeout': {'type': type(0)},
|
||||
\ 'sleep': {'type': type(0)},
|
||||
\ })))
|
||||
\ }, v:null)))
|
||||
command! -range LspDocumentRangeFormat call lsp#internal#document_range_formatting#format({ 'bufnr': bufnr('%') })
|
||||
command! -range -nargs=? LspDocumentRangeFormatSync call lsp#internal#document_range_formatting#format(
|
||||
\ extend({'bufnr': bufnr('%'), 'sync': 1 }, lsp#utils#args#_parse(<q-args>, {
|
||||
\ 'timeout': {'type': type(0)},
|
||||
\ 'sleep': {'type': type(0)},
|
||||
\ })))
|
||||
\ }, v:null)))
|
||||
command! LspImplementation call lsp#ui#vim#implementation(0, <q-mods>)
|
||||
command! LspPeekImplementation call lsp#ui#vim#implementation(1)
|
||||
command! -nargs=0 LspStatus call lsp#print_server_status()
|
||||
@@ -154,7 +153,9 @@ command! -nargs=0 LspSemanticTokenModifiers echo lsp#internal#semantic#get_token
|
||||
|
||||
nnoremap <silent> <plug>(lsp-call-hierarchy-incoming) :<c-u>call lsp#ui#vim#call_hierarchy_incoming({})<cr>
|
||||
nnoremap <silent> <plug>(lsp-call-hierarchy-outgoing) :<c-u>call lsp#ui#vim#call_hierarchy_outgoing()<cr>
|
||||
nnoremap <silent> <plug>(lsp-code-action) :<c-u>call lsp#ui#vim#code_action()<cr>
|
||||
nnoremap <silent> <plug>(lsp-code-action) :<c-u>call lsp#ui#vim#code_action({})<cr>
|
||||
nnoremap <silent> <plug>(lsp-code-action-float) :<c-u>call lsp#ui#vim#code_action({ 'ui': 'float' })<cr>
|
||||
nnoremap <silent> <plug>(lsp-code-action-preview) :<c-u>call lsp#ui#vim#code_action({ 'ui': 'preview' })<cr>
|
||||
nnoremap <silent> <plug>(lsp-code-lens) :<c-u>call lsp#ui#vim#code_lens()<cr>
|
||||
nnoremap <silent> <plug>(lsp-declaration) :<c-u>call lsp#ui#vim#declaration(0)<cr>
|
||||
nnoremap <silent> <plug>(lsp-peek-declaration) :<c-u>call lsp#ui#vim#declaration(1)<cr>
|
||||
|
||||
Reference in New Issue
Block a user