Merge branch 'master' into fix-vim-popup-conceal

This commit is contained in:
hrsh7th
2022-12-03 10:37:18 +09:00
committed by GitHub
14 changed files with 321 additions and 92 deletions

View File

@@ -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
View 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

View File

@@ -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,
\ },
\ }

View File

@@ -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'])

View File

@@ -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.

View File

@@ -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,

View 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

View File

@@ -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

View File

@@ -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']

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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>