I broke up with neovim....vim is my best friend now
This commit is contained in:
497
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim.vim
Normal file
497
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim.vim
Normal file
@@ -0,0 +1,497 @@
|
||||
function! s:not_supported(what) abort
|
||||
return lsp#utils#error(printf("%s not supported for filetype '%s'", a:what, &filetype))
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#implementation(in_preview, ...) abort
|
||||
let l:ctx = { 'in_preview': a:in_preview }
|
||||
if a:0
|
||||
let l:ctx['mods'] = a:1
|
||||
endif
|
||||
call s:list_location('implementation', l:ctx)
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#type_definition(in_preview, ...) abort
|
||||
let l:ctx = { 'in_preview': a:in_preview }
|
||||
if a:0
|
||||
let l:ctx['mods'] = a:1
|
||||
endif
|
||||
call s:list_location('typeDefinition', l:ctx)
|
||||
endfunction
|
||||
|
||||
|
||||
function! lsp#ui#vim#declaration(in_preview, ...) abort
|
||||
let l:ctx = { 'in_preview': a:in_preview }
|
||||
if a:0
|
||||
let l:ctx['mods'] = a:1
|
||||
endif
|
||||
call s:list_location('declaration', l:ctx)
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#definition(in_preview, ...) abort
|
||||
let l:ctx = { 'in_preview': a:in_preview }
|
||||
if a:0
|
||||
let l:ctx['mods'] = a:1
|
||||
endif
|
||||
call s:list_location('definition', l:ctx)
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#references() abort
|
||||
let l:ctx = { 'jump_if_one': 0 }
|
||||
let l:request_params = { 'context': { 'includeDeclaration': v:false } }
|
||||
call s:list_location('references', l:ctx, l:request_params)
|
||||
endfunction
|
||||
|
||||
function! s:list_location(method, ctx, ...) abort
|
||||
" typeDefinition => type definition
|
||||
let l:operation = substitute(a:method, '\u', ' \l\0', 'g')
|
||||
|
||||
let l:capabilities_func = printf('lsp#capabilities#has_%s_provider(v:val)', substitute(l:operation, ' ', '_', 'g'))
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capabilities_func)
|
||||
let l:command_id = lsp#_new_command()
|
||||
|
||||
|
||||
let l:ctx = extend({ 'counter': len(l:servers), 'list':[], 'last_command_id': l:command_id, 'jump_if_one': 1, 'mods': '', 'in_preview': 0 }, a:ctx)
|
||||
if len(l:servers) == 0
|
||||
call s:not_supported('Retrieving ' . l:operation)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:params = {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'position': lsp#get_position(),
|
||||
\ }
|
||||
if a:0
|
||||
call extend(l:params, a:1)
|
||||
endif
|
||||
for l:server in l:servers
|
||||
call lsp#send_request(l:server, {
|
||||
\ 'method': 'textDocument/' . a:method,
|
||||
\ 'params': l:params,
|
||||
\ 'on_notification': function('s:handle_location', [l:ctx, l:server, l:operation]),
|
||||
\ })
|
||||
endfor
|
||||
|
||||
echo printf('Retrieving %s ...', l:operation)
|
||||
endfunction
|
||||
|
||||
function! s:rename(server, new_name, pos) abort
|
||||
if empty(a:new_name)
|
||||
echo '... Renaming aborted ...'
|
||||
return
|
||||
endif
|
||||
|
||||
" needs to flush existing open buffers
|
||||
call lsp#send_request(a:server, {
|
||||
\ 'method': 'textDocument/rename',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'position': a:pos,
|
||||
\ 'newName': a:new_name,
|
||||
\ },
|
||||
\ 'on_notification': function('s:handle_workspace_edit', [a:server, lsp#_last_command(), 'rename']),
|
||||
\ })
|
||||
|
||||
echo ' ... Renaming ...'
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#rename() abort
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_rename_prepare_provider(v:val)')
|
||||
let l:prepare_support = 1
|
||||
if len(l:servers) == 0
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_rename_provider(v:val)')
|
||||
let l:prepare_support = 0
|
||||
endif
|
||||
|
||||
let l:command_id = lsp#_new_command()
|
||||
|
||||
if len(l:servers) == 0
|
||||
call s:not_supported('Renaming')
|
||||
return
|
||||
endif
|
||||
|
||||
" TODO: ask the user which server it should use to rename if there are multiple
|
||||
let l:server = l:servers[0]
|
||||
|
||||
if l:prepare_support
|
||||
call lsp#send_request(l:server, {
|
||||
\ 'method': 'textDocument/prepareRename',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'position': lsp#get_position(),
|
||||
\ },
|
||||
\ 'on_notification': function('s:handle_rename_prepare', [l:server, l:command_id, 'rename_prepare', expand('<cword>'), lsp#get_position()]),
|
||||
\ })
|
||||
return
|
||||
endif
|
||||
|
||||
call s:rename(l:server, input('new name: ', expand('<cword>')), lsp#get_position())
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#stop_server(...) abort
|
||||
let l:name = get(a:000, 0, '')
|
||||
for l:server in lsp#get_allowed_servers()
|
||||
if !empty(l:name) && l:server != l:name
|
||||
continue
|
||||
endif
|
||||
echo 'Stopping' l:server 'server ...'
|
||||
call lsp#stop_server(l:server)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#workspace_symbol(query) abort
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_workspace_symbol_provider(v:val)')
|
||||
let l:command_id = lsp#_new_command()
|
||||
|
||||
if len(l:servers) == 0
|
||||
call s:not_supported('Retrieving workspace symbols')
|
||||
return
|
||||
endif
|
||||
|
||||
if !empty(a:query)
|
||||
let l:query = a:query
|
||||
else
|
||||
let l:query = inputdialog('query>', '', "\<ESC>")
|
||||
if l:query ==# "\<ESC>"
|
||||
return
|
||||
endif
|
||||
endif
|
||||
|
||||
for l:server in l:servers
|
||||
call lsp#send_request(l:server, {
|
||||
\ 'method': 'workspace/symbol',
|
||||
\ 'params': {
|
||||
\ 'query': l:query,
|
||||
\ },
|
||||
\ 'on_notification': function('s:handle_symbol', [l:server, l:command_id, 'workspaceSymbol']),
|
||||
\ })
|
||||
endfor
|
||||
|
||||
redraw
|
||||
echo 'Retrieving workspace symbols ...'
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#document_symbol() abort
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_document_symbol_provider(v:val)')
|
||||
let l:command_id = lsp#_new_command()
|
||||
|
||||
if len(l:servers) == 0
|
||||
call s:not_supported('Retrieving symbols')
|
||||
return
|
||||
endif
|
||||
|
||||
for l:server in l:servers
|
||||
call lsp#send_request(l:server, {
|
||||
\ 'method': 'textDocument/documentSymbol',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ },
|
||||
\ 'on_notification': function('s:handle_symbol', [l:server, l:command_id, 'documentSymbol']),
|
||||
\ })
|
||||
endfor
|
||||
|
||||
echo 'Retrieving document symbols ...'
|
||||
endfunction
|
||||
|
||||
function! s:handle_symbol(server, last_command_id, type, data) abort
|
||||
if a:last_command_id != lsp#_last_command()
|
||||
return
|
||||
endif
|
||||
|
||||
if lsp#client#is_error(a:data['response'])
|
||||
call lsp#utils#error('Failed to retrieve '. a:type . ' for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
|
||||
return
|
||||
endif
|
||||
|
||||
let l:list = lsp#ui#vim#utils#symbols_to_loc_list(a:server, a:data)
|
||||
|
||||
if has('patch-8.2.2147')
|
||||
call setqflist(l:list)
|
||||
call setqflist([], 'a', {'title': a:type})
|
||||
else
|
||||
call setqflist([])
|
||||
call setqflist(l:list)
|
||||
endif
|
||||
|
||||
if empty(l:list)
|
||||
call lsp#utils#error('No ' . a:type .' found')
|
||||
else
|
||||
echo 'Retrieved ' . a:type
|
||||
botright copen
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:handle_location(ctx, server, type, data) abort "ctx = {counter, list, last_command_id, jump_if_one, mods, in_preview}
|
||||
if a:ctx['last_command_id'] != lsp#_last_command()
|
||||
return
|
||||
endif
|
||||
|
||||
let a:ctx['counter'] = a:ctx['counter'] - 1
|
||||
|
||||
if lsp#client#is_error(a:data['response']) || !has_key(a:data['response'], 'result')
|
||||
call lsp#utils#error('Failed to retrieve '. a:type . ' for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
|
||||
else
|
||||
let a:ctx['list'] = a:ctx['list'] + lsp#utils#location#_lsp_to_vim_list(a:data['response']['result'])
|
||||
endif
|
||||
|
||||
if a:ctx['counter'] == 0
|
||||
if empty(a:ctx['list'])
|
||||
call lsp#utils#error('No ' . a:type .' found')
|
||||
else
|
||||
call lsp#utils#tagstack#_update()
|
||||
|
||||
let l:loc = a:ctx['list'][0]
|
||||
|
||||
if len(a:ctx['list']) == 1 && a:ctx['jump_if_one'] && !a:ctx['in_preview']
|
||||
call lsp#utils#location#_open_vim_list_item(l:loc, a:ctx['mods'])
|
||||
echo 'Retrieved ' . a:type
|
||||
redraw
|
||||
elseif !a:ctx['in_preview']
|
||||
call setqflist([])
|
||||
call setqflist(a:ctx['list'])
|
||||
echo 'Retrieved ' . a:type
|
||||
botright copen
|
||||
else
|
||||
let l:lines = readfile(l:loc['filename'])
|
||||
if has_key(l:loc,'viewstart') " showing a locationLink
|
||||
let l:view = l:lines[l:loc['viewstart'] : l:loc['viewend']]
|
||||
call lsp#ui#vim#output#preview(a:server, l:view, {
|
||||
\ 'statusline': ' LSP Peek ' . a:type,
|
||||
\ 'filetype': &filetype
|
||||
\ })
|
||||
else " showing a location
|
||||
call lsp#ui#vim#output#preview(a:server, l:lines, {
|
||||
\ 'statusline': ' LSP Peek ' . a:type,
|
||||
\ 'cursor': { 'line': l:loc['lnum'], 'col': l:loc['col'], 'align': g:lsp_peek_alignment },
|
||||
\ 'filetype': &filetype
|
||||
\ })
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:handle_rename_prepare(server, last_command_id, type, cword, position, data) abort
|
||||
if a:last_command_id != lsp#_last_command()
|
||||
return
|
||||
endif
|
||||
|
||||
if lsp#client#is_error(a:data['response'])
|
||||
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']
|
||||
|
||||
" 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'])
|
||||
if l:start_line ==# l:end_line
|
||||
let l:name = l:lines[l:start_line - 1][l:start_col - 1 : l:end_col - 2]
|
||||
else
|
||||
let l:name = l:lines[l:start_line - 1][l:start_col - 1 :]
|
||||
for l:i in range(l:start_line, l:end_line - 2)
|
||||
let l:name .= "\n" . l:lines[l:i]
|
||||
endfor
|
||||
if l:end_col - 2 < 0
|
||||
let l:name .= "\n"
|
||||
else
|
||||
let l:name .= l:lines[l:end_line - 1][: l:end_col - 2]
|
||||
endif
|
||||
endif
|
||||
|
||||
call timer_start(1, {x->s:rename(a:server, input('new name: ', l:name), l:range['start'])})
|
||||
endfunction
|
||||
|
||||
function! s:handle_workspace_edit(server, last_command_id, type, data) abort
|
||||
if a:last_command_id != lsp#_last_command()
|
||||
return
|
||||
endif
|
||||
|
||||
if lsp#client#is_error(a:data['response'])
|
||||
call lsp#utils#error('Failed to retrieve '. a:type . ' for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
|
||||
return
|
||||
endif
|
||||
|
||||
call lsp#utils#workspace_edit#apply_workspace_edit(a:data['response']['result'])
|
||||
|
||||
echo 'Renamed'
|
||||
endfunction
|
||||
|
||||
function! s:handle_text_edit(server, last_command_id, type, data) abort
|
||||
if a:last_command_id != lsp#_last_command()
|
||||
return
|
||||
endif
|
||||
|
||||
if lsp#client#is_error(a:data['response'])
|
||||
call lsp#utils#error('Failed to '. a:type . ' for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
|
||||
return
|
||||
endif
|
||||
|
||||
call lsp#utils#text_edit#apply_text_edits(a:data['request']['params']['textDocument']['uri'], a:data['response']['result'])
|
||||
|
||||
redraw | echo 'Document formatted'
|
||||
endfunction
|
||||
|
||||
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
|
||||
call lsp#ui#vim#code_lens#do({
|
||||
\ 'sync': v:false,
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#add_tree_call_hierarchy_incoming() abort
|
||||
let l:ctx = { 'add_tree': v:true }
|
||||
call lsp#ui#vim#call_hierarchy_incoming(l:ctx)
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#call_hierarchy_incoming(ctx) abort
|
||||
let l:ctx = extend({ 'method': 'incomingCalls', 'key': 'from' }, a:ctx)
|
||||
call s:prepare_call_hierarchy(l:ctx)
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#call_hierarchy_outgoing() abort
|
||||
let l:ctx = { 'method': 'outgoingCalls', 'key': 'to' }
|
||||
call s:prepare_call_hierarchy(l:ctx)
|
||||
endfunction
|
||||
|
||||
function! s:prepare_call_hierarchy(ctx) abort
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_call_hierarchy_provider(v:val)')
|
||||
let l:command_id = lsp#_new_command()
|
||||
|
||||
let l:ctx = extend({ 'counter': len(l:servers), 'list':[], 'last_command_id': l:command_id }, a:ctx)
|
||||
if len(l:servers) == 0
|
||||
call s:not_supported('Retrieving call hierarchy')
|
||||
return
|
||||
endif
|
||||
|
||||
for l:server in l:servers
|
||||
call lsp#send_request(l:server, {
|
||||
\ 'method': 'textDocument/prepareCallHierarchy',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'position': lsp#get_position(),
|
||||
\ },
|
||||
\ 'on_notification': function('s:handle_prepare_call_hierarchy', [l:ctx, l:server, 'prepare_call_hierarchy']),
|
||||
\ })
|
||||
endfor
|
||||
|
||||
echo 'Preparing call hierarchy ...'
|
||||
endfunction
|
||||
|
||||
function! s:handle_prepare_call_hierarchy(ctx, server, type, data) abort
|
||||
if a:ctx['last_command_id'] != lsp#_last_command()
|
||||
return
|
||||
endif
|
||||
|
||||
if lsp#client#is_error(a:data['response']) || !has_key(a:data['response'], 'result')
|
||||
call lsp#utils#error('Failed to '. a:type . ' for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
|
||||
return
|
||||
endif
|
||||
if empty(a:data['response']['result'])
|
||||
call lsp#utils#warning('Failed to '. a:type . ' for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
|
||||
return
|
||||
endif
|
||||
|
||||
for l:item in a:data['response']['result']
|
||||
call s:call_hierarchy(a:ctx, a:server, l:item)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:call_hierarchy(ctx, server, item) abort
|
||||
call lsp#send_request(a:server, {
|
||||
\ 'method': 'callHierarchy/' . a:ctx['method'],
|
||||
\ 'params': {
|
||||
\ 'item': a:item,
|
||||
\ },
|
||||
\ 'on_notification': function('s:handle_call_hierarchy', [a:ctx, a:server, 'call_hierarchy']),
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
function! s:handle_call_hierarchy(ctx, server, type, data) abort
|
||||
if a:ctx['last_command_id'] != lsp#_last_command()
|
||||
return
|
||||
endif
|
||||
|
||||
let a:ctx['counter'] = a:ctx['counter'] - 1
|
||||
|
||||
if lsp#client#is_error(a:data['response']) || !has_key(a:data['response'], 'result')
|
||||
call lsp#utils#error('Failed to retrieve '. a:type . ' for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
|
||||
elseif a:data['response']['result'] isnot v:null
|
||||
for l:item in a:data['response']['result']
|
||||
let l:loc = s:hierarchy_item_to_vim(l:item[a:ctx['key']], a:server)
|
||||
if l:loc isnot v:null
|
||||
let a:ctx['list'] += [l:loc]
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
if a:ctx['counter'] == 0
|
||||
if empty(a:ctx['list'])
|
||||
call lsp#utils#error('No ' . a:type .' found')
|
||||
else
|
||||
call lsp#utils#tagstack#_update()
|
||||
if get(a:ctx, 'add_tree', v:false)
|
||||
let l:qf = getqflist({'idx' : 0, 'items': []})
|
||||
let l:pos = l:qf.idx
|
||||
let l:parent = l:qf.items
|
||||
let l:level = count(l:parent[l:pos-1].text, g:lsp_tree_incoming_prefix)
|
||||
let a:ctx['list'] = extend(l:parent, map(a:ctx['list'], 'extend(v:val, {"text": repeat("' . g:lsp_tree_incoming_prefix . '", l:level+1) . v:val.text})'), l:pos)
|
||||
endif
|
||||
call setqflist([])
|
||||
call setqflist(a:ctx['list'])
|
||||
echo 'Retrieved ' . a:type
|
||||
botright copen
|
||||
if get(a:ctx, 'add_tree', v:false)
|
||||
" move the cursor to the newly added item
|
||||
execute l:pos + 1
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:hierarchy_item_to_vim(item, server) abort
|
||||
let l:uri = a:item['uri']
|
||||
if !lsp#utils#is_file_uri(l:uri)
|
||||
return v:null
|
||||
endif
|
||||
|
||||
let l:path = lsp#utils#uri_to_path(l:uri)
|
||||
let [l:line, l:col] = lsp#utils#position#lsp_to_vim(l:path, a:item['range']['start'])
|
||||
let l:text = '[' . lsp#ui#vim#utils#_get_symbol_text_from_kind(a:server, a:item['kind']) . '] ' . a:item['name']
|
||||
if has_key(a:item, 'detail')
|
||||
let l:text .= ": " . a:item['detail']
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'filename': l:path,
|
||||
\ 'lnum': l:line,
|
||||
\ 'col': l:col,
|
||||
\ 'text': l:text,
|
||||
\ }
|
||||
endfunction
|
||||
207
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/code_action.vim
Normal file
207
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/code_action.vim
Normal file
@@ -0,0 +1,207 @@
|
||||
" vint: -ProhibitUnusedVariable
|
||||
|
||||
function! lsp#ui#vim#code_action#complete(input, command, len) abort
|
||||
let l:server_names = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_code_action_provider(v:val)')
|
||||
let l:kinds = []
|
||||
for l:server_name in l:server_names
|
||||
let l:kinds += lsp#capabilities#get_code_action_kinds(l:server_name)
|
||||
endfor
|
||||
return filter(copy(l:kinds), { _, kind -> kind =~ '^' . a:input })
|
||||
endfunction
|
||||
|
||||
"
|
||||
" @param option = {
|
||||
" 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
|
||||
return lsp#utils#error('Code action not supported for ' . &filetype)
|
||||
endif
|
||||
|
||||
if l:selection
|
||||
let l:range = lsp#utils#range#_get_recent_visual_range()
|
||||
else
|
||||
let l:range = lsp#utils#range#_get_current_line_range()
|
||||
endif
|
||||
|
||||
let l:ctx = {
|
||||
\ 'count': len(l:server_names),
|
||||
\ 'results': [],
|
||||
\}
|
||||
let l:bufnr = bufnr('%')
|
||||
let l:command_id = lsp#_new_command()
|
||||
for l:server_name in l:server_names
|
||||
let l:diagnostic = lsp#internal#diagnostics#under_cursor#get_diagnostic({'server': l:server_name})
|
||||
call lsp#send_request(l:server_name, {
|
||||
\ 'method': 'textDocument/codeAction',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'range': empty(l:diagnostic) || l:selection ? l:range : l:diagnostic['range'],
|
||||
\ 'context': {
|
||||
\ 'diagnostics' : empty(l:diagnostic) ? [] : [l:diagnostic],
|
||||
\ 'only': ['', 'quickfix', 'refactor', 'refactor.extract', 'refactor.inline', 'refactor.rewrite', 'source', 'source.organizeImports'],
|
||||
\ },
|
||||
\ },
|
||||
\ 'sync': l:sync,
|
||||
\ '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(ui, ctx, server_name, command_id, sync, query, bufnr, data) abort
|
||||
" Ignore old request.
|
||||
if a:command_id != lsp#_last_command()
|
||||
return
|
||||
endif
|
||||
|
||||
call add(a:ctx['results'], {
|
||||
\ 'server_name': a:server_name,
|
||||
\ 'data': a:data,
|
||||
\})
|
||||
let a:ctx['count'] -= 1
|
||||
if a:ctx['count'] ># 0
|
||||
return
|
||||
endif
|
||||
|
||||
let l:total_code_actions = []
|
||||
for l:result in a:ctx['results']
|
||||
let l:server_name = l:result['server_name']
|
||||
let l:data = l:result['data']
|
||||
" Check response error.
|
||||
if lsp#client#is_error(l:data['response'])
|
||||
call lsp#utils#error('Failed to CodeAction for ' . l:server_name . ': ' . lsp#client#error_message(l:data['response']))
|
||||
continue
|
||||
endif
|
||||
|
||||
" Check code actions.
|
||||
let l:code_actions = l:data['response']['result']
|
||||
|
||||
" Filter code actions.
|
||||
if !empty(a:query)
|
||||
let l:code_actions = filter(l:code_actions, { _, action -> get(action, 'kind', '') =~# '^' . a:query })
|
||||
endif
|
||||
if empty(l:code_actions)
|
||||
continue
|
||||
endif
|
||||
|
||||
for l:code_action in l:code_actions
|
||||
let l:item = {
|
||||
\ 'server_name': l:server_name,
|
||||
\ 'code_action': l:code_action,
|
||||
\ }
|
||||
if get(l:code_action, 'isPreferred', v:false)
|
||||
let l:total_code_actions = [l:item] + l:total_code_actions
|
||||
else
|
||||
call add(l:total_code_actions, l:item)
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
if len(l:total_code_actions) == 0
|
||||
echo 'No code actions found'
|
||||
return
|
||||
endif
|
||||
call lsp#log('s:handle_code_action', l:total_code_actions)
|
||||
|
||||
if len(l:total_code_actions) == 1 && !empty(a:query)
|
||||
let l:action = l:total_code_actions[0]
|
||||
if s:handle_disabled_action(l:action) | return | endif
|
||||
" Clear 'Retrieving code actions ...' message
|
||||
echo ''
|
||||
call s:handle_one_code_action(l:action['server_name'], a:sync, a:bufnr, l:action['code_action'])
|
||||
return
|
||||
endif
|
||||
|
||||
" Prompt to choose code actions when empty query provided.
|
||||
let l:items = []
|
||||
for l:action in l:total_code_actions
|
||||
let l:title = printf('[%s] %s', l:action['server_name'], l:action['code_action']['title'])
|
||||
if has_key(l:action['code_action'], 'kind') && l:action['code_action']['kind'] !=# ''
|
||||
let l:title .= ' (' . l:action['code_action']['kind'] . ')'
|
||||
endif
|
||||
call add(l:items, { 'title': l:title, 'item': l:action })
|
||||
endfor
|
||||
|
||||
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: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']
|
||||
if s:handle_disabled_action(l:selected) | return | endif
|
||||
call s:handle_one_code_action(l:selected['server_name'], a:sync, a:bufnr, l:selected['code_action'])
|
||||
endfunction
|
||||
|
||||
function! s:handle_disabled_action(code_action) abort
|
||||
if has_key(a:code_action, 'disabled')
|
||||
echo 'This action is disabled: ' . a:code_action['disabled']['reason']
|
||||
return v:true
|
||||
endif
|
||||
return v:false
|
||||
endfunction
|
||||
|
||||
function! s:handle_one_code_action(server_name, sync, bufnr, command_or_code_action) abort
|
||||
" has WorkspaceEdit.
|
||||
if has_key(a:command_or_code_action, 'edit')
|
||||
call lsp#utils#workspace_edit#apply_workspace_edit(a:command_or_code_action['edit'])
|
||||
endif
|
||||
|
||||
" Command.
|
||||
if has_key(a:command_or_code_action, 'command') && type(a:command_or_code_action['command']) == type('')
|
||||
call lsp#ui#vim#execute_command#_execute({
|
||||
\ 'server_name': a:server_name,
|
||||
\ 'command_name': get(a:command_or_code_action, 'command', ''),
|
||||
\ 'command_args': get(a:command_or_code_action, 'arguments', v:null),
|
||||
\ 'sync': a:sync,
|
||||
\ 'bufnr': a:bufnr,
|
||||
\ })
|
||||
|
||||
" has Command.
|
||||
elseif has_key(a:command_or_code_action, 'command') && type(a:command_or_code_action['command']) == type({})
|
||||
call lsp#ui#vim#execute_command#_execute({
|
||||
\ 'server_name': a:server_name,
|
||||
\ 'command_name': get(a:command_or_code_action['command'], 'command', ''),
|
||||
\ 'command_args': get(a:command_or_code_action['command'], 'arguments', v:null),
|
||||
\ 'sync': a:sync,
|
||||
\ 'bufnr': a:bufnr,
|
||||
\ })
|
||||
endif
|
||||
endfunction
|
||||
137
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/code_lens.vim
Normal file
137
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/code_lens.vim
Normal file
@@ -0,0 +1,137 @@
|
||||
" https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens
|
||||
|
||||
" @param option = {
|
||||
" }
|
||||
"
|
||||
function! lsp#ui#vim#code_lens#do(option) abort
|
||||
let l:sync = get(a:option, 'sync', v:false)
|
||||
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_code_lens_provider(v:val)')
|
||||
if len(l:servers) == 0
|
||||
return lsp#utils#error('Code lens not supported for ' . &filetype)
|
||||
endif
|
||||
|
||||
redraw | echo 'Retrieving codelens ...'
|
||||
|
||||
let l:bufnr = bufnr('%')
|
||||
|
||||
call lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromList(l:servers),
|
||||
\ lsp#callbag#flatMap({server->
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#request(server, {
|
||||
\ 'method': 'textDocument/codeLens',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(l:bufnr),
|
||||
\ },
|
||||
\ }),
|
||||
\ lsp#callbag#map({x->x['response']['result']}),
|
||||
\ lsp#callbag#filter({codelenses->!empty(codelenses)}),
|
||||
\ lsp#callbag#flatMap({codelenses->
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromList(codelenses),
|
||||
\ lsp#callbag#flatMap({codelens->
|
||||
\ has_key(codelens, 'command') ? lsp#callbag#of(codelens) : s:resolve_codelens(server, codelens)}),
|
||||
\ )
|
||||
\ }),
|
||||
\ lsp#callbag#map({codelens->{ 'server': server, 'codelens': codelens }}),
|
||||
\ )
|
||||
\ }),
|
||||
\ lsp#callbag#reduce({acc,curr->add(acc, curr)}, []),
|
||||
\ lsp#callbag#flatMap({x->s:chooseCodeLens(x, l:bufnr)}),
|
||||
\ lsp#callbag#tap({x-> lsp#ui#vim#execute_command#_execute({
|
||||
\ 'server_name': x['server'],
|
||||
\ 'command_name': get(x['codelens']['command'], 'command', ''),
|
||||
\ 'command_args': get(x['codelens']['command'], 'arguments', v:null),
|
||||
\ 'bufnr': l:bufnr,
|
||||
\ })}),
|
||||
\ lsp#callbag#takeUntil(lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'command')}),
|
||||
\ )),
|
||||
\ lsp#callbag#subscribe({
|
||||
\ 'error': {e->lsp#utils#error('Error running codelens ' . json_encode(e))},
|
||||
\ }),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! s:resolve_codelens(server, codelens) abort
|
||||
" TODO: return callbag#lsp#empty() if codelens resolve not supported by server
|
||||
return lsp#callbag#pipe(
|
||||
\ lsp#request(a:server, {
|
||||
\ 'method': 'codeLens/resolve',
|
||||
\ 'params': a:codelens
|
||||
\ }),
|
||||
\ lsp#callbag#map({x->x['response']['result']}),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! s:chooseCodeLens(items, bufnr) abort
|
||||
redraw | echo 'Select codelens:'
|
||||
if empty(a:items)
|
||||
return lsp#callbag#throwError('No codelens found')
|
||||
endif
|
||||
return lsp#callbag#create(function('s:quickpick_open', [a:items, a:bufnr]))
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#code_lens#_get_subtitle(item) abort
|
||||
" Since element of arguments property of Command interface is defined as any in LSP spec, it is
|
||||
" up to the language server implementation.
|
||||
" Currently this only impacts rust-analyzer. See #1118 for more details.
|
||||
|
||||
if !has_key(a:item['codelens']['command'], 'arguments')
|
||||
return ''
|
||||
endif
|
||||
|
||||
let l:arguments = a:item['codelens']['command']['arguments']
|
||||
for l:argument in l:arguments
|
||||
if type(l:argument) != type({}) || !has_key(l:argument, 'label')
|
||||
return ''
|
||||
endif
|
||||
endfor
|
||||
|
||||
return ': ' . join(map(copy(l:arguments), 'v:val["label"]'), ' > ')
|
||||
endfunction
|
||||
|
||||
function! s:quickpick_open(items, bufnr, next, error, complete) abort
|
||||
if empty(a:items)
|
||||
return lsp#callbag#empty()
|
||||
endif
|
||||
|
||||
let l:items = []
|
||||
for l:item in a:items
|
||||
let l:title = printf("[%s] %s%s\t| L%s:%s",
|
||||
\ l:item['server'],
|
||||
\ l:item['codelens']['command']['title'],
|
||||
\ lsp#ui#vim#code_lens#_get_subtitle(l:item),
|
||||
\ lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['codelens']['range']['start']),
|
||||
\ getbufline(a:bufnr, lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['codelens']['range']['start']))[0])
|
||||
call add(l:items, { 'title': l:title, 'item': l:item })
|
||||
endfor
|
||||
|
||||
call lsp#internal#ui#quickpick#open({
|
||||
\ 'items': l:items,
|
||||
\ 'key': 'title',
|
||||
\ 'on_accept': function('s:quickpick_accept', [a:next, a:error, a:complete]),
|
||||
\ 'on_cancel': function('s:quickpick_cancel', [a:next, a:error, a:complete]),
|
||||
\ })
|
||||
|
||||
return function('s:quickpick_dispose')
|
||||
endfunction
|
||||
|
||||
function! s:quickpick_dispose() abort
|
||||
call lsp#internal#ui#quickpick#close()
|
||||
endfunction
|
||||
|
||||
function! s:quickpick_accept(next, error, complete, data, ...) abort
|
||||
call lsp#internal#ui#quickpick#close()
|
||||
let l:items = a:data['items']
|
||||
if len(l:items) > 0
|
||||
call a:next(l:items[0]['item'])
|
||||
endif
|
||||
call a:complete()
|
||||
endfunction
|
||||
|
||||
function! s:quickpick_cancel(next, error, complete, ...) abort
|
||||
call a:complete()
|
||||
endfunction
|
||||
303
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/completion.vim
Normal file
303
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/completion.vim
Normal file
@@ -0,0 +1,303 @@
|
||||
" vint: -ProhibitUnusedVariable
|
||||
"
|
||||
let s:context = {}
|
||||
|
||||
function! lsp#ui#vim#completion#_setup() abort
|
||||
augroup lsp_ui_vim_completion
|
||||
autocmd!
|
||||
autocmd CompleteDone * call s:on_complete_done()
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#completion#_disable() abort
|
||||
augroup lsp_ui_vim_completion
|
||||
autocmd!
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
"
|
||||
" After CompleteDone, v:complete_item's word has been inserted into the line.
|
||||
" Yet not inserted commit characters.
|
||||
"
|
||||
" below example uses | as cursor position.
|
||||
"
|
||||
" 1. `call getbuf|`<C-x><C-o>
|
||||
" 2. select `getbufline` item.
|
||||
" 3. Insert commit characters. e.g. `(`
|
||||
" 4. fire CompleteDone, then the line is `call getbufline|`
|
||||
" 5. call feedkeys to call `s:on_complete_done_after`
|
||||
" 6. then the line is `call getbufline(|` in `s:on_complete_done_after`
|
||||
"
|
||||
function! s:on_complete_done() abort
|
||||
" Sometimes, vim occurs `CompleteDone` unexpectedly.
|
||||
" We try to detect it by checking empty completed_item.
|
||||
if empty(v:completed_item) || get(v:completed_item, 'word', '') ==# '' && get(v:completed_item, 'abbr', '') ==# ''
|
||||
doautocmd <nomodeline> User lsp_complete_done
|
||||
return
|
||||
endif
|
||||
|
||||
" Try to get managed user_data.
|
||||
let l:managed_user_data = lsp#omni#get_managed_user_data_from_completed_item(v:completed_item)
|
||||
|
||||
" Clear managed user_data.
|
||||
call lsp#omni#_clear_managed_user_data_map()
|
||||
|
||||
" If managed user_data does not exists, skip it.
|
||||
if empty(l:managed_user_data)
|
||||
doautocmd <nomodeline> User lsp_complete_done
|
||||
return
|
||||
endif
|
||||
|
||||
let s:context['done_line'] = getline('.')
|
||||
let s:context['done_line_nr'] = line('.')
|
||||
let s:context['completed_item'] = copy(v:completed_item)
|
||||
let s:context['done_position'] = lsp#utils#position#vim_to_lsp('%', getpos('.')[1 : 2])
|
||||
let s:context['complete_position'] = l:managed_user_data['complete_position']
|
||||
let s:context['server_name'] = l:managed_user_data['server_name']
|
||||
let s:context['completion_item'] = l:managed_user_data['completion_item']
|
||||
let s:context['start_character'] = l:managed_user_data['start_character']
|
||||
call feedkeys(printf("\<C-r>=<SNR>%d_on_complete_done_after()\<CR>", s:SID()), 'n')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" Apply textEdit or insertText(snippet) and additionalTextEdits.
|
||||
"
|
||||
function! s:on_complete_done_after() abort
|
||||
" Clear message line. feedkeys above leave garbage on message line.
|
||||
echo ''
|
||||
|
||||
" Ignore process if the mode() is not insert-mode after feedkeys.
|
||||
if mode(1)[0] !=# 'i'
|
||||
return ''
|
||||
endif
|
||||
|
||||
let l:done_line = s:context['done_line']
|
||||
let l:done_line_nr = s:context['done_line_nr']
|
||||
let l:completed_item = s:context['completed_item']
|
||||
let l:done_position = s:context['done_position']
|
||||
let l:complete_position = s:context['complete_position']
|
||||
let l:server_name = s:context['server_name']
|
||||
let l:completion_item = s:context['completion_item']
|
||||
let l:start_character = s:context['start_character']
|
||||
|
||||
" check the commit characters are <BS> or <C-w>.
|
||||
if line('.') ==# l:done_line_nr && strlen(getline('.')) < strlen(l:done_line)
|
||||
doautocmd <nomodeline> User lsp_complete_done
|
||||
return ''
|
||||
endif
|
||||
|
||||
" Do nothing if text_edit is disabled.
|
||||
if !g:lsp_text_edit_enabled
|
||||
doautocmd <nomodeline> User lsp_complete_done
|
||||
return ''
|
||||
endif
|
||||
|
||||
let l:completion_item = s:resolve_completion_item(l:completion_item, l:server_name)
|
||||
|
||||
" clear completed string if need.
|
||||
let l:is_expandable = s:is_expandable(l:done_line, l:done_position, l:complete_position, l:completion_item, l:completed_item)
|
||||
if l:is_expandable
|
||||
call s:clear_auto_inserted_text(l:done_line, l:done_position, l:complete_position)
|
||||
endif
|
||||
|
||||
" apply additionalTextEdits.
|
||||
if has_key(l:completion_item, 'additionalTextEdits') && !empty(l:completion_item['additionalTextEdits'])
|
||||
call lsp#utils#text_edit#apply_text_edits(lsp#utils#get_buffer_uri(bufnr('%')), l:completion_item['additionalTextEdits'])
|
||||
endif
|
||||
|
||||
" snippet or textEdit.
|
||||
if l:is_expandable
|
||||
" At this timing, the cursor may have been moved by additionalTextEdit, so we use overflow information instead of textEdit itself.
|
||||
if type(get(l:completion_item, 'textEdit', v:null)) == type({})
|
||||
let l:range = lsp#utils#text_edit#get_range(l:completion_item['textEdit'])
|
||||
let l:overflow_before = max([0, l:start_character - l:range['start']['character']])
|
||||
let l:overflow_after = max([0, l:range['end']['character'] - l:complete_position['character']])
|
||||
let l:text = l:completion_item['textEdit']['newText']
|
||||
else
|
||||
let l:overflow_before = 0
|
||||
let l:overflow_after = 0
|
||||
let l:text = s:get_completion_text(l:completion_item)
|
||||
endif
|
||||
|
||||
" apply snipept or text_edit
|
||||
let l:position = lsp#utils#position#vim_to_lsp('%', getpos('.')[1 : 2])
|
||||
let l:range = {
|
||||
\ 'start': {
|
||||
\ 'line': l:position['line'],
|
||||
\ 'character': l:position['character'] - (l:complete_position['character'] - l:start_character) - l:overflow_before,
|
||||
\ },
|
||||
\ 'end': {
|
||||
\ 'line': l:position['line'],
|
||||
\ 'character': l:position['character'] + l:overflow_after,
|
||||
\ }
|
||||
\ }
|
||||
|
||||
if get(l:completion_item, 'insertTextFormat', 1) == 2
|
||||
" insert Snippet.
|
||||
call lsp#utils#text_edit#apply_text_edits('%', [{ 'range': l:range, 'newText': '' }])
|
||||
call cursor(lsp#utils#position#lsp_to_vim('%', l:range['start']))
|
||||
if exists('g:lsp_snippet_expand') && len(g:lsp_snippet_expand) > 0
|
||||
call g:lsp_snippet_expand[0]({ 'snippet': l:text })
|
||||
else
|
||||
call s:simple_expand_text(l:text)
|
||||
endif
|
||||
else
|
||||
" apply TextEdit.
|
||||
call lsp#utils#text_edit#apply_text_edits('%', [{ 'range': l:range, 'newText': l:text }])
|
||||
|
||||
" The VSCode always apply completion word as snippet.
|
||||
" It means we should place cursor to end of new inserted text as snippet does.
|
||||
let l:lines = lsp#utils#_split_by_eol(l:text)
|
||||
let l:start = l:range.start
|
||||
let l:start.line += len(l:lines) - 1
|
||||
let l:start.character += strchars(l:lines[-1])
|
||||
call cursor(lsp#utils#position#lsp_to_vim('%', l:start))
|
||||
endif
|
||||
endif
|
||||
|
||||
doautocmd <nomodeline> User lsp_complete_done
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
"
|
||||
" is_expandable
|
||||
"
|
||||
function! s:is_expandable(done_line, done_position, complete_position, completion_item, completed_item) abort
|
||||
if get(a:completion_item, 'textEdit', v:null) isnot# v:null
|
||||
let l:range = lsp#utils#text_edit#get_range(a:completion_item['textEdit'])
|
||||
if l:range['start']['line'] != l:range['end']['line']
|
||||
return v:true
|
||||
endif
|
||||
|
||||
" compute if textEdit will change text.
|
||||
let l:completed_before = strcharpart(a:done_line, 0, a:complete_position['character'])
|
||||
let l:completed_after = strcharpart(a:done_line, a:done_position['character'], strchars(a:done_line) - a:done_position['character'])
|
||||
let l:completed_line = l:completed_before . l:completed_after
|
||||
let l:text_edit_before = strcharpart(l:completed_line, 0, l:range['start']['character'])
|
||||
let l:text_edit_after = strcharpart(l:completed_line, l:range['end']['character'], strchars(l:completed_line) - l:range['end']['character'])
|
||||
return a:done_line !=# l:text_edit_before . s:trim_unmeaning_tabstop(a:completion_item['textEdit']['newText']) . l:text_edit_after
|
||||
endif
|
||||
return s:get_completion_text(a:completion_item) !=# s:trim_unmeaning_tabstop(a:completed_item['word'])
|
||||
endfunction
|
||||
|
||||
"
|
||||
" trim_unmeaning_tabstop
|
||||
"
|
||||
function! s:trim_unmeaning_tabstop(text) abort
|
||||
return substitute(a:text, '\%(\$0\|\${0}\)$', '', 'g')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" Try `completionItem/resolve` if it possible.
|
||||
"
|
||||
function! s:resolve_completion_item(completion_item, server_name) abort
|
||||
" server_name is not provided.
|
||||
if empty(a:server_name)
|
||||
return a:completion_item
|
||||
endif
|
||||
|
||||
" check server capabilities.
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server_name)
|
||||
if !has_key(l:capabilities, 'completionProvider')
|
||||
\ || type(l:capabilities['completionProvider']) != v:t_dict
|
||||
\ || !has_key(l:capabilities['completionProvider'], 'resolveProvider')
|
||||
\ || !l:capabilities['completionProvider']['resolveProvider']
|
||||
return a:completion_item
|
||||
endif
|
||||
|
||||
let l:ctx = {}
|
||||
let l:ctx['response'] = {}
|
||||
function! l:ctx['callback'](data) abort
|
||||
let l:self['response'] = a:data['response']
|
||||
endfunction
|
||||
|
||||
try
|
||||
call lsp#send_request(a:server_name, {
|
||||
\ 'method': 'completionItem/resolve',
|
||||
\ 'params': a:completion_item,
|
||||
\ 'sync': 1,
|
||||
\ 'sync_timeout': g:lsp_completion_resolve_timeout,
|
||||
\ 'on_notification': function(l:ctx['callback'], [], l:ctx)
|
||||
\ })
|
||||
catch /.*/
|
||||
call lsp#log('s:resolve_completion_item', 'request timeout.')
|
||||
endtry
|
||||
|
||||
if empty(l:ctx['response'])
|
||||
return a:completion_item
|
||||
endif
|
||||
|
||||
if lsp#client#is_error(l:ctx['response'])
|
||||
return a:completion_item
|
||||
endif
|
||||
|
||||
if empty(l:ctx['response']['result'])
|
||||
return a:completion_item
|
||||
endif
|
||||
|
||||
return l:ctx['response']['result']
|
||||
endfunction
|
||||
|
||||
"
|
||||
" Remove additional inserted text
|
||||
"
|
||||
" LSP server knows only `complete_position` so we should remove inserted text until complete_position.
|
||||
"
|
||||
function! s:clear_auto_inserted_text(done_line, done_position, complete_position) abort
|
||||
let l:before = strcharpart(a:done_line, 0, a:complete_position['character'])
|
||||
let l:after = strcharpart(a:done_line, a:done_position['character'], (strchars(a:done_line) - a:done_position['character']))
|
||||
call setline('.', l:before . l:after)
|
||||
call cursor([a:done_position['line'] + 1, strlen(l:before) + 1])
|
||||
endfunction
|
||||
|
||||
"
|
||||
" Expand text
|
||||
"
|
||||
function! s:simple_expand_text(text) abort
|
||||
let l:pos = {
|
||||
\ 'line': line('.') - 1,
|
||||
\ 'character': lsp#utils#to_char('%', line('.'), col('.'))
|
||||
\ }
|
||||
|
||||
" Remove placeholders and get first placeholder position that use to cursor position.
|
||||
" e.g. `|getbufline(${1:expr}, ${2:lnum})${0}` to getbufline(|,)
|
||||
let l:text = substitute(a:text, '\$\%({[0-9]\+\%(:\(\\.\|[^}]\+\)*\)}\|[0-9]\+\)', '\=substitute(submatch(1), "\\", "", "g")', 'g')
|
||||
let l:offset = match(a:text, '\$\%({[0-9]\+\%(:\(\\.\|[^}]\+\)*\)}\|[0-9]\+\)')
|
||||
if l:offset == -1
|
||||
let l:offset = strchars(l:text)
|
||||
endif
|
||||
|
||||
call lsp#utils#text_edit#apply_text_edits(lsp#utils#get_buffer_uri(bufnr('%')), [{
|
||||
\ 'range': {
|
||||
\ 'start': l:pos,
|
||||
\ 'end': l:pos
|
||||
\ },
|
||||
\ 'newText': l:text
|
||||
\ }])
|
||||
|
||||
let l:pos = lsp#utils#position#lsp_to_vim('%', {
|
||||
\ 'line': l:pos['line'],
|
||||
\ 'character': l:pos['character'] + l:offset
|
||||
\ })
|
||||
call cursor(l:pos)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" Get completion text from CompletionItem. Fallback to label when insertText
|
||||
" is falsy
|
||||
"
|
||||
function! s:get_completion_text(completion_item) abort
|
||||
let l:text = get(a:completion_item, 'insertText', '')
|
||||
if empty(l:text)
|
||||
let l:text = a:completion_item['label']
|
||||
endif
|
||||
return l:text
|
||||
endfunction
|
||||
|
||||
"
|
||||
" Get script id that uses to call `s:` function in feedkeys.
|
||||
"
|
||||
function! s:SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
|
||||
endfunction
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
let s:commands = {}
|
||||
|
||||
"
|
||||
" @param {name} = string
|
||||
" @param {callback} = funcref
|
||||
"
|
||||
function! lsp#ui#vim#execute_command#_register(command_name, callback) abort
|
||||
if has_key(s:commands, a:command_name)
|
||||
throw printf('lsp#ui#vim#execute_command#_register_command: %s already registered.', a:command_name)
|
||||
endif
|
||||
|
||||
let s:commands[a:command_name] = a:callback
|
||||
endfunction
|
||||
|
||||
"
|
||||
" TODO: This method does not handle any return value.
|
||||
"
|
||||
function! lsp#ui#vim#execute_command#_execute(params) abort
|
||||
let l:command_name = a:params['command_name']
|
||||
let l:command_args = get(a:params, 'command_args', v:null)
|
||||
let l:server_name = get(a:params, 'server_name', '')
|
||||
let l:bufnr = get(a:params, 'bufnr', -1)
|
||||
let l:sync = get(a:params, 'sync', v:false)
|
||||
|
||||
" create command.
|
||||
let l:command = { 'command': l:command_name }
|
||||
if l:command_args isnot v:null
|
||||
let l:command['arguments'] = l:command_args
|
||||
endif
|
||||
|
||||
" execute command on local.
|
||||
if has_key(s:commands, l:command_name)
|
||||
try
|
||||
call s:commands[l:command_name]({
|
||||
\ 'bufnr': l:bufnr,
|
||||
\ 'server_name': l:server_name,
|
||||
\ 'command': l:command,
|
||||
\ })
|
||||
catch /.*/
|
||||
call lsp#utils#error(printf('Execute command failed: %s', string(a:params)))
|
||||
endtry
|
||||
return
|
||||
endif
|
||||
|
||||
" execute command on server.
|
||||
if !empty(l:server_name)
|
||||
call lsp#send_request(l:server_name, {
|
||||
\ 'method': 'workspace/executeCommand',
|
||||
\ 'params': l:command,
|
||||
\ 'sync': l:sync,
|
||||
\ 'on_notification': function('s:handle_execute_command', [l:server_name, l:command]),
|
||||
\ })
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" handle workspace/executeCommand response
|
||||
"
|
||||
function! s:handle_execute_command(server_name, command, data) abort
|
||||
if lsp#client#is_error(a:data['response'])
|
||||
call lsp#utils#error('Execute command failed on ' . a:server_name . ': ' . string(a:command) . ' -> ' . string(a:data))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
201
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/folding.vim
Normal file
201
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/folding.vim
Normal file
@@ -0,0 +1,201 @@
|
||||
let s:folding_ranges = {}
|
||||
let s:textprop_name = 'vim-lsp-folding-linenr'
|
||||
|
||||
function! s:find_servers() abort
|
||||
return filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_folding_range_provider(v:val)')
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#folding#fold(sync) abort
|
||||
let l:servers = s:find_servers()
|
||||
|
||||
if len(l:servers) == 0
|
||||
call lsp#utils#error('Folding not supported for ' . &filetype)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:server = l:servers[0]
|
||||
call lsp#ui#vim#folding#send_request(l:server, bufnr('%'), a:sync)
|
||||
endfunction
|
||||
|
||||
function! s:set_textprops(buf) abort
|
||||
" Use zero-width text properties to act as a sort of "mark" in the buffer.
|
||||
" This is used to remember the line numbers at the time the request was
|
||||
" sent. We will let Vim handle updating the line numbers when the user
|
||||
" inserts or deletes text.
|
||||
|
||||
" Skip if the buffer doesn't exist. This might happen when a buffer is
|
||||
" opened and quickly deleted.
|
||||
if !bufloaded(a:buf) | return | endif
|
||||
|
||||
" Create text property, if not already defined
|
||||
silent! call prop_type_add(s:textprop_name, {'bufnr': a:buf, 'priority': lsp#internal#textprop#priority('folding')})
|
||||
|
||||
let l:line_count = s:get_line_count_buf(a:buf)
|
||||
|
||||
" First, clear all markers from the previous run
|
||||
call prop_remove({'type': s:textprop_name, 'bufnr': a:buf}, 1, l:line_count)
|
||||
|
||||
" Add markers to each line
|
||||
let l:i = 1
|
||||
while l:i <= l:line_count
|
||||
call prop_add(l:i, 1, {'bufnr': a:buf, 'type': s:textprop_name, 'id': l:i})
|
||||
let l:i += 1
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! s:get_line_count_buf(buf) abort
|
||||
if !has('patch-8.1.1967')
|
||||
return line('$')
|
||||
endif
|
||||
let l:winids = win_findbuf(a:buf)
|
||||
return empty(l:winids) ? line('$') : line('$', l:winids[0])
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#folding#send_request(server_name, buf, sync) abort
|
||||
if !lsp#capabilities#has_folding_range_provider(a:server_name)
|
||||
return
|
||||
endif
|
||||
|
||||
if !g:lsp_fold_enabled
|
||||
call lsp#log('Skip sending fold request: folding was disabled explicitly')
|
||||
return
|
||||
endif
|
||||
|
||||
if has('textprop')
|
||||
call s:set_textprops(a:buf)
|
||||
endif
|
||||
|
||||
call lsp#send_request(a:server_name, {
|
||||
\ 'method': 'textDocument/foldingRange',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(a:buf)
|
||||
\ },
|
||||
\ 'on_notification': function('s:handle_fold_request', [a:server_name]),
|
||||
\ 'sync': a:sync,
|
||||
\ 'bufnr': a:buf
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
function! s:foldexpr(server, buf, linenr) abort
|
||||
let l:foldlevel = 0
|
||||
let l:prefix = ''
|
||||
|
||||
for l:folding_range in s:folding_ranges[a:server][a:buf]
|
||||
if type(l:folding_range) == type({}) &&
|
||||
\ has_key(l:folding_range, 'startLine') &&
|
||||
\ has_key(l:folding_range, 'endLine')
|
||||
let l:start = l:folding_range['startLine'] + 1
|
||||
let l:end = l:folding_range['endLine'] + 1
|
||||
|
||||
if (l:start <= a:linenr) && (a:linenr <= l:end)
|
||||
let l:foldlevel += 1
|
||||
endif
|
||||
|
||||
if l:start == a:linenr
|
||||
let l:prefix = '>'
|
||||
elseif l:end == a:linenr
|
||||
let l:prefix = '<'
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Only return marker if a fold starts/ends at this line.
|
||||
" Otherwise, return '='.
|
||||
return (l:prefix ==# '') ? '=' : (l:prefix . l:foldlevel)
|
||||
endfunction
|
||||
|
||||
" Searches for text property of the correct type on the given line.
|
||||
" Returns the original linenr on success, or -1 if no textprop of the correct
|
||||
" type is associated with this line.
|
||||
function! s:get_textprop_line(linenr) abort
|
||||
let l:props = filter(prop_list(a:linenr), {idx, prop -> prop['type'] ==# s:textprop_name})
|
||||
|
||||
if empty(l:props)
|
||||
return -1
|
||||
else
|
||||
return l:props[0]['id']
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#folding#foldexpr() abort
|
||||
let l:servers = s:find_servers()
|
||||
|
||||
if len(l:servers) == 0
|
||||
return
|
||||
endif
|
||||
|
||||
let l:server = l:servers[0]
|
||||
|
||||
if has('textprop')
|
||||
" Does the current line have a textprop with original line info?
|
||||
let l:textprop_line = s:get_textprop_line(v:lnum)
|
||||
|
||||
if l:textprop_line == -1
|
||||
" No information for current line available, so use indent for
|
||||
" previous line.
|
||||
return '='
|
||||
else
|
||||
" Info available, use foldexpr as it would be with original line
|
||||
" number
|
||||
return s:foldexpr(l:server, bufnr('%'), l:textprop_line)
|
||||
endif
|
||||
else
|
||||
return s:foldexpr(l:server, bufnr('%'), v:lnum)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#folding#foldtext() abort
|
||||
let l:num_lines = v:foldend - v:foldstart + 1
|
||||
let l:summary = getline(v:foldstart) . '...'
|
||||
|
||||
" Join all lines in the fold
|
||||
let l:combined_lines = ''
|
||||
let l:i = v:foldstart
|
||||
while l:i <= v:foldend
|
||||
let l:combined_lines .= getline(l:i) . ' '
|
||||
let l:i += 1
|
||||
endwhile
|
||||
|
||||
" Check if we're in a comment
|
||||
let l:comment_regex = '\V' . substitute(&l:commentstring, '%s', '\\.\\*', '')
|
||||
if l:combined_lines =~? l:comment_regex
|
||||
let l:summary = l:combined_lines
|
||||
endif
|
||||
|
||||
return l:summary . ' (' . l:num_lines . ' ' . (l:num_lines == 1 ? 'line' : 'lines') . ') '
|
||||
endfunction
|
||||
|
||||
function! s:handle_fold_request(server, data) abort
|
||||
if lsp#client#is_error(a:data) || !has_key(a:data, 'response') || !has_key(a:data['response'], 'result')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:result = a:data['response']['result']
|
||||
|
||||
if type(l:result) != type([])
|
||||
return
|
||||
endif
|
||||
|
||||
let l:uri = a:data['request']['params']['textDocument']['uri']
|
||||
let l:path = lsp#utils#uri_to_path(l:uri)
|
||||
let l:bufnr = bufnr(l:path)
|
||||
|
||||
if l:bufnr < 0
|
||||
return
|
||||
endif
|
||||
|
||||
if !has_key(s:folding_ranges, a:server)
|
||||
let s:folding_ranges[a:server] = {}
|
||||
endif
|
||||
let s:folding_ranges[a:server][l:bufnr] = l:result
|
||||
|
||||
" Set 'foldmethod' back to 'expr', which forces a re-evaluation of
|
||||
" 'foldexpr'. Only do this if the user hasn't changed 'foldmethod',
|
||||
" and this is the correct buffer.
|
||||
for l:winid in win_findbuf(l:bufnr)
|
||||
if getwinvar(l:winid, '&foldmethod') ==# 'expr'
|
||||
call setwinvar(l:winid, '&foldmethod', 'expr')
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
450
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/output.vim
Normal file
450
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/output.vim
Normal file
@@ -0,0 +1,450 @@
|
||||
let s:use_vim_popup = has('patch-8.1.1517') && g:lsp_preview_float && !has('nvim')
|
||||
let s:use_nvim_float = exists('*nvim_open_win') && g:lsp_preview_float && has('nvim')
|
||||
let s:use_preview = !s:use_vim_popup && !s:use_nvim_float
|
||||
|
||||
function! s:import_modules() abort
|
||||
if exists('s:Markdown') | return | endif
|
||||
let s:Markdown = vital#lsp#import('VS.Vim.Syntax.Markdown')
|
||||
let s:MarkupContent = vital#lsp#import('VS.LSP.MarkupContent')
|
||||
let s:Window = vital#lsp#import('VS.Vim.Window')
|
||||
let s:Text = vital#lsp#import('VS.LSP.Text')
|
||||
endfunction
|
||||
|
||||
let s:winid = v:false
|
||||
let s:prevwin = v:false
|
||||
let s:preview_data = v:false
|
||||
|
||||
function! s:vim_popup_closed(...) abort
|
||||
let s:preview_data = v:false
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#closepreview() abort
|
||||
if win_getid() ==# s:winid
|
||||
" Don't close if window got focus
|
||||
return
|
||||
endif
|
||||
|
||||
if s:winid == v:false
|
||||
return
|
||||
endif
|
||||
|
||||
"closing floats in vim8.1 must use popup_close()
|
||||
"nvim must use nvim_win_close. pclose is not reliable and does not always work
|
||||
if s:use_vim_popup && s:winid
|
||||
call popup_close(s:winid)
|
||||
elseif s:use_nvim_float && s:winid
|
||||
silent! call nvim_win_close(s:winid, 0)
|
||||
else
|
||||
pclose
|
||||
endif
|
||||
let s:winid = v:false
|
||||
let s:preview_data = v:false
|
||||
augroup lsp_float_preview_close
|
||||
augroup end
|
||||
autocmd! lsp_float_preview_close CursorMoved,CursorMovedI,VimResized *
|
||||
doautocmd <nomodeline> User lsp_float_closed
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#focuspreview() abort
|
||||
if s:is_cmdwin()
|
||||
return
|
||||
endif
|
||||
|
||||
" This does not work for vim8.1 popup but will work for nvim and old preview
|
||||
if s:winid
|
||||
if win_getid() !=# s:winid
|
||||
let s:prevwin = win_getid()
|
||||
call win_gotoid(s:winid)
|
||||
elseif s:prevwin
|
||||
" Temporarily disable hooks
|
||||
" TODO: remove this when closing logic is able to distinguish different move directions
|
||||
autocmd! lsp_float_preview_close CursorMoved,CursorMovedI,VimResized *
|
||||
call win_gotoid(s:prevwin)
|
||||
call s:add_float_closing_hooks()
|
||||
let s:prevwin = v:false
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:bufwidth() abort
|
||||
let l:width = winwidth(0)
|
||||
let l:numberwidth = max([&numberwidth, strlen(line('$'))+1])
|
||||
let l:numwidth = (&number || &relativenumber)? l:numberwidth : 0
|
||||
let l:foldwidth = &foldcolumn
|
||||
|
||||
if &signcolumn ==? 'yes'
|
||||
let l:signwidth = 2
|
||||
elseif &signcolumn ==? 'auto'
|
||||
let l:signs = execute(printf('sign place buffer=%d', bufnr('')))
|
||||
let l:signs = split(l:signs, "\n")
|
||||
let l:signwidth = len(l:signs)>2? 2: 0
|
||||
else
|
||||
let l:signwidth = 0
|
||||
endif
|
||||
return l:width - l:numwidth - l:foldwidth - l:signwidth
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:get_float_positioning(height, width) abort
|
||||
let l:height = a:height
|
||||
let l:width = a:width
|
||||
" TODO: add option to configure it 'docked' at the bottom/top/right
|
||||
|
||||
" NOTE: screencol() and screenrow() start from (1,1)
|
||||
" but the popup window co-ordinates start from (0,0)
|
||||
" Very convenient!
|
||||
" For a simple single-line 'tooltip', the following
|
||||
" two lines are enough to determine the position
|
||||
|
||||
let l:col = screencol()
|
||||
let l:row = screenrow()
|
||||
|
||||
let l:height = min([l:height, max([&lines - &cmdheight - l:row, &previewheight])])
|
||||
|
||||
let l:style = 'minimal'
|
||||
" Positioning is not window but screen relative
|
||||
let l:opts = {
|
||||
\ 'relative': 'editor',
|
||||
\ 'row': l:row,
|
||||
\ 'col': l:col,
|
||||
\ 'width': l:width,
|
||||
\ 'height': l:height,
|
||||
\ 'style': l:style,
|
||||
\ }
|
||||
return l:opts
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#floatingpreview(data) abort
|
||||
if s:use_nvim_float
|
||||
let l:buf = nvim_create_buf(v:false, v:true)
|
||||
|
||||
" Try to get as much space around the cursor, but at least 10x10
|
||||
let l:width = max([s:bufwidth(), 10])
|
||||
let l:height = max([&lines - winline() + 1, winline() - 1, 10])
|
||||
|
||||
if g:lsp_preview_max_height > 0
|
||||
let l:height = min([g:lsp_preview_max_height, l:height])
|
||||
endif
|
||||
|
||||
let l:opts = s:get_float_positioning(l:height, l:width)
|
||||
|
||||
let s:winid = nvim_open_win(l:buf, v:false, l:opts)
|
||||
call nvim_win_set_option(s:winid, 'winhl', 'Normal:Pmenu,NormalNC:Pmenu')
|
||||
call nvim_win_set_option(s:winid, 'foldenable', v:false)
|
||||
call nvim_win_set_option(s:winid, 'wrap', v:true)
|
||||
call nvim_win_set_option(s:winid, 'statusline', '')
|
||||
call nvim_win_set_option(s:winid, 'number', v:false)
|
||||
call nvim_win_set_option(s:winid, 'relativenumber', v:false)
|
||||
call nvim_win_set_option(s:winid, 'cursorline', v:false)
|
||||
call nvim_win_set_option(s:winid, 'cursorcolumn', v:false)
|
||||
call nvim_win_set_option(s:winid, 'colorcolumn', '')
|
||||
call nvim_win_set_option(s:winid, 'signcolumn', 'no')
|
||||
" Enable closing the preview with esc, but map only in the scratch buffer
|
||||
call nvim_buf_set_keymap(l:buf, 'n', '<esc>', ':pclose<cr>', {'silent': v:true})
|
||||
elseif s:use_vim_popup
|
||||
let l:options = {
|
||||
\ 'moved': 'any',
|
||||
\ 'border': [1, 1, 1, 1],
|
||||
\ 'callback': function('s:vim_popup_closed')
|
||||
\ }
|
||||
|
||||
if g:lsp_preview_max_width > 0
|
||||
let l:options['maxwidth'] = g:lsp_preview_max_width
|
||||
endif
|
||||
|
||||
if g:lsp_preview_max_height > 0
|
||||
let l:options['maxheight'] = g:lsp_preview_max_height
|
||||
endif
|
||||
|
||||
let s:winid = popup_atcursor('...', l:options)
|
||||
endif
|
||||
return s:winid
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#setcontent(winid, lines, ft) abort
|
||||
if s:use_vim_popup
|
||||
" vim popup
|
||||
call setbufline(winbufnr(a:winid), 1, a:lines)
|
||||
call setbufvar(winbufnr(a:winid), '&filetype', a:ft . '.lsp-hover')
|
||||
elseif s:use_nvim_float
|
||||
" nvim floating
|
||||
call nvim_buf_set_lines(winbufnr(a:winid), 0, -1, v:false, a:lines)
|
||||
call nvim_buf_set_option(winbufnr(a:winid), 'readonly', v:true)
|
||||
call nvim_buf_set_option(winbufnr(a:winid), 'modifiable', v:false)
|
||||
call nvim_buf_set_option(winbufnr(a:winid), 'filetype', a:ft.'.lsp-hover')
|
||||
call nvim_win_set_cursor(a:winid, [1, 0])
|
||||
elseif s:use_preview
|
||||
" preview window
|
||||
call setbufline(winbufnr(a:winid), 1, a:lines)
|
||||
call setbufvar(winbufnr(a:winid), '&filetype', a:ft . '.lsp-hover')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#adjust_float_placement(bufferlines, maxwidth) abort
|
||||
if s:use_nvim_float
|
||||
let l:win_config = {}
|
||||
let l:height = min([winheight(s:winid), a:bufferlines])
|
||||
let l:width = min([winwidth(s:winid), a:maxwidth])
|
||||
let l:win_config = s:get_float_positioning(l:height, l:width)
|
||||
call nvim_win_set_config(s:winid, l:win_config )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:add_float_closing_hooks() abort
|
||||
if g:lsp_preview_autoclose
|
||||
augroup lsp_float_preview_close
|
||||
autocmd! lsp_float_preview_close CursorMoved,CursorMovedI,VimResized *
|
||||
autocmd CursorMoved,CursorMovedI,VimResized * call lsp#ui#vim#output#closepreview()
|
||||
augroup END
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#getpreviewwinid() abort
|
||||
return s:winid
|
||||
endfunction
|
||||
|
||||
function! s:open_preview(data) abort
|
||||
if s:use_vim_popup || s:use_nvim_float
|
||||
let l:winid = lsp#ui#vim#output#floatingpreview(a:data)
|
||||
else
|
||||
execute &previewheight.'new'
|
||||
let l:winid = win_getid()
|
||||
endif
|
||||
return l:winid
|
||||
endfunction
|
||||
|
||||
function! s:set_cursor(current_window_id, options) abort
|
||||
if !has_key(a:options, 'cursor')
|
||||
return
|
||||
endif
|
||||
|
||||
if s:use_nvim_float
|
||||
" Neovim floats
|
||||
" Go back to the preview window to set the cursor
|
||||
call win_gotoid(s:winid)
|
||||
let l:old_scrolloff = &scrolloff
|
||||
let &scrolloff = 0
|
||||
|
||||
call nvim_win_set_cursor(s:winid, [a:options['cursor']['line'], a:options['cursor']['col']])
|
||||
call s:align_preview(a:options)
|
||||
|
||||
" Finally, go back to the original window
|
||||
call win_gotoid(a:current_window_id)
|
||||
|
||||
let &scrolloff = l:old_scrolloff
|
||||
elseif s:use_vim_popup
|
||||
" Vim popups
|
||||
function! AlignVimPopup(timer) closure abort
|
||||
call s:align_preview(a:options)
|
||||
endfunction
|
||||
call timer_start(0, function('AlignVimPopup'))
|
||||
else
|
||||
" Preview
|
||||
" Don't use 'scrolloff', it might mess up the cursor's position
|
||||
let &l:scrolloff = 0
|
||||
call cursor(a:options['cursor']['line'], a:options['cursor']['col'])
|
||||
call s:align_preview(a:options)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:align_preview(options) abort
|
||||
if !has_key(a:options, 'cursor') ||
|
||||
\ !has_key(a:options['cursor'], 'align')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:align = a:options['cursor']['align']
|
||||
|
||||
if s:use_vim_popup
|
||||
" Vim popups
|
||||
let l:pos = popup_getpos(s:winid)
|
||||
let l:below = winline() < winheight(0) / 2
|
||||
if l:below
|
||||
let l:height = min([l:pos['core_height'], winheight(0) - winline() - 2])
|
||||
else
|
||||
let l:height = min([l:pos['core_height'], winline() - 3])
|
||||
endif
|
||||
let l:width = l:pos['core_width']
|
||||
|
||||
let l:options = {
|
||||
\ 'minwidth': l:width,
|
||||
\ 'maxwidth': l:width,
|
||||
\ 'minheight': l:height,
|
||||
\ 'maxheight': l:height,
|
||||
\ 'pos': l:below ? 'topleft' : 'botleft',
|
||||
\ 'line': l:below ? 'cursor+1' : 'cursor-1'
|
||||
\ }
|
||||
|
||||
if l:align ==? 'top'
|
||||
let l:options['firstline'] = a:options['cursor']['line']
|
||||
elseif l:align ==? 'center'
|
||||
let l:options['firstline'] = a:options['cursor']['line'] - (l:height - 1) / 2
|
||||
elseif l:align ==? 'bottom'
|
||||
let l:options['firstline'] = a:options['cursor']['line'] - l:height + 1
|
||||
endif
|
||||
|
||||
call popup_setoptions(s:winid, l:options)
|
||||
redraw!
|
||||
else
|
||||
" Preview and Neovim floats
|
||||
if l:align ==? 'top'
|
||||
normal! zt
|
||||
elseif l:align ==? 'center'
|
||||
normal! zz
|
||||
elseif l:align ==? 'bottom'
|
||||
normal! zb
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#get_size_info(winid) abort
|
||||
" Get size information while still having the buffer active
|
||||
let l:buffer = winbufnr(a:winid)
|
||||
let l:maxwidth = max(map(getbufline(l:buffer, 1, '$'), 'strdisplaywidth(v:val)'))
|
||||
let l:bufferlines = 0
|
||||
if g:lsp_preview_max_width > 0
|
||||
let l:maxwidth = min([g:lsp_preview_max_width, l:maxwidth])
|
||||
|
||||
" Determine, for each line, how many "virtual" lines it spans, and add
|
||||
" these together for all lines in the buffer
|
||||
for l:line in getbufline(l:buffer, 1, '$')
|
||||
let l:num_lines = str2nr(string(ceil(strdisplaywidth(l:line) * 1.0 / g:lsp_preview_max_width)))
|
||||
let l:bufferlines += max([l:num_lines, 1])
|
||||
endfor
|
||||
else
|
||||
if s:use_vim_popup
|
||||
let l:bufferlines = line('$', a:winid)
|
||||
elseif s:use_nvim_float
|
||||
let l:bufferlines = nvim_buf_line_count(winbufnr(a:winid))
|
||||
endif
|
||||
endif
|
||||
|
||||
return [l:bufferlines, l:maxwidth]
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#float_supported() abort
|
||||
return s:use_vim_popup || s:use_nvim_float
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#preview(server, data, options) abort
|
||||
if s:is_cmdwin()
|
||||
return
|
||||
endif
|
||||
|
||||
if s:winid && type(s:preview_data) ==# type(a:data)
|
||||
\ && s:preview_data ==# a:data
|
||||
\ && type(g:lsp_preview_doubletap) ==# 3
|
||||
\ && len(g:lsp_preview_doubletap) >= 1
|
||||
\ && type(g:lsp_preview_doubletap[0]) ==# 2
|
||||
\ && index(['i', 's'], mode()[0]) ==# -1
|
||||
echo ''
|
||||
return call(g:lsp_preview_doubletap[0], [])
|
||||
endif
|
||||
" Close any previously opened preview window
|
||||
call lsp#ui#vim#output#closepreview()
|
||||
|
||||
let l:current_window_id = win_getid()
|
||||
|
||||
let s:winid = s:open_preview(a:data)
|
||||
|
||||
let s:preview_data = a:data
|
||||
let l:lines = []
|
||||
let l:syntax_lines = []
|
||||
let l:ft = lsp#ui#vim#output#append(a:data, l:lines, l:syntax_lines)
|
||||
|
||||
if has_key(a:options, 'filetype')
|
||||
let l:ft = a:options['filetype']
|
||||
endif
|
||||
|
||||
let l:do_conceal = g:lsp_hover_conceal
|
||||
let l:server_info = a:server !=# '' ? lsp#get_server_info(a:server) : {}
|
||||
let l:config = get(l:server_info, 'config', {})
|
||||
let l:do_conceal = get(l:config, 'hover_conceal', l:do_conceal)
|
||||
|
||||
call setbufvar(winbufnr(s:winid), 'lsp_syntax_highlights', l:syntax_lines)
|
||||
call setbufvar(winbufnr(s:winid), 'lsp_do_conceal', l:do_conceal)
|
||||
call lsp#ui#vim#output#setcontent(s:winid, l:lines, l:ft)
|
||||
|
||||
let [l:bufferlines, l:maxwidth] = lsp#ui#vim#output#get_size_info(s:winid)
|
||||
|
||||
if s:use_preview
|
||||
" Set statusline
|
||||
if has_key(a:options, 'statusline')
|
||||
let &l:statusline = a:options['statusline']
|
||||
endif
|
||||
|
||||
call s:set_cursor(l:current_window_id, a:options)
|
||||
endif
|
||||
|
||||
" Go to the previous window to adjust positioning
|
||||
call win_gotoid(l:current_window_id)
|
||||
|
||||
echo ''
|
||||
|
||||
if s:winid && (s:use_vim_popup || s:use_nvim_float)
|
||||
if s:use_nvim_float
|
||||
" Neovim floats
|
||||
call lsp#ui#vim#output#adjust_float_placement(l:bufferlines, l:maxwidth)
|
||||
call s:set_cursor(l:current_window_id, a:options)
|
||||
call s:add_float_closing_hooks()
|
||||
elseif s:use_vim_popup
|
||||
" Vim popups
|
||||
call s:set_cursor(l:current_window_id, a:options)
|
||||
endif
|
||||
doautocmd <nomodeline> User lsp_float_opened
|
||||
endif
|
||||
|
||||
if l:ft ==? 'markdown'
|
||||
call s:import_modules()
|
||||
call s:Window.do(s:winid, {->s:Markdown.apply()})
|
||||
endif
|
||||
|
||||
if !g:lsp_preview_keep_focus
|
||||
" set the focus to the preview window
|
||||
call win_gotoid(s:winid)
|
||||
endif
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! s:escape_string_for_display(str) abort
|
||||
return substitute(substitute(a:str, '\r\n', '\n', 'g'), '\r', '\n', 'g')
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#append(data, lines, syntax_lines) abort
|
||||
if type(a:data) == type([])
|
||||
for l:entry in a:data
|
||||
call lsp#ui#vim#output#append(l:entry, a:lines, a:syntax_lines)
|
||||
endfor
|
||||
|
||||
return 'markdown'
|
||||
elseif type(a:data) ==# type('')
|
||||
call extend(a:lines, split(s:escape_string_for_display(a:data), "\n", v:true))
|
||||
return 'markdown'
|
||||
elseif type(a:data) ==# type({}) && has_key(a:data, 'language')
|
||||
let l:new_lines = split(s:escape_string_for_display(a:data.value), '\n')
|
||||
|
||||
let l:i = 1
|
||||
while l:i <= len(l:new_lines)
|
||||
call add(a:syntax_lines, { 'line': len(a:lines) + l:i, 'language': a:data.language })
|
||||
let l:i += 1
|
||||
endwhile
|
||||
|
||||
call extend(a:lines, l:new_lines)
|
||||
return 'markdown'
|
||||
elseif type(a:data) ==# type({}) && has_key(a:data, 'kind')
|
||||
if a:data.kind ==? 'markdown'
|
||||
call s:import_modules()
|
||||
let l:detail = s:MarkupContent.normalize(a:data.value, {
|
||||
\ 'compact': !g:lsp_preview_fixup_conceal
|
||||
\ })
|
||||
call extend(a:lines, s:Text.split_by_eol(l:detail))
|
||||
else
|
||||
call extend(a:lines, split(s:escape_string_for_display(a:data.value), '\n', v:true))
|
||||
endif
|
||||
return a:data.kind ==? 'plaintext' ? 'text' : a:data.kind
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:is_cmdwin() abort
|
||||
return getcmdwintype() !=# ''
|
||||
endfunction
|
||||
160
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/signature_help.vim
Normal file
160
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/signature_help.vim
Normal file
@@ -0,0 +1,160 @@
|
||||
" vint: -ProhibitUnusedVariable
|
||||
let s:debounce_timer_id = 0
|
||||
|
||||
function! s:not_supported(what) abort
|
||||
return lsp#utils#error(a:what.' not supported for '.&filetype)
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#signature_help#get_signature_help_under_cursor() abort
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_signature_help_provider(v:val)')
|
||||
|
||||
if len(l:servers) == 0
|
||||
call s:not_supported('Retrieving signature help')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:position = lsp#get_position()
|
||||
for l:server in l:servers
|
||||
call lsp#send_request(l:server, {
|
||||
\ 'method': 'textDocument/signatureHelp',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'position': l:position,
|
||||
\ },
|
||||
\ 'on_notification': function('s:handle_signature_help', [l:server]),
|
||||
\ })
|
||||
endfor
|
||||
|
||||
call lsp#log('Retrieving signature help')
|
||||
return
|
||||
endfunction
|
||||
|
||||
function! s:handle_signature_help(server, data) abort
|
||||
if lsp#client#is_error(a:data['response'])
|
||||
call lsp#utils#error('Failed to retrieve signature help information for ' . a:server)
|
||||
return
|
||||
endif
|
||||
|
||||
if !has_key(a:data['response'], 'result')
|
||||
return
|
||||
endif
|
||||
|
||||
if !empty(a:data['response']['result']) && !empty(a:data['response']['result']['signatures'])
|
||||
" Get current signature.
|
||||
let l:signatures = get(a:data['response']['result'], 'signatures', [])
|
||||
let l:signature_index = get(a:data['response']['result'], 'activeSignature', 0)
|
||||
let l:signature = get(l:signatures, l:signature_index, {})
|
||||
if empty(l:signature)
|
||||
return
|
||||
endif
|
||||
|
||||
" Signature label.
|
||||
let l:label = l:signature['label']
|
||||
|
||||
" Mark current parameter.
|
||||
if has_key(a:data['response']['result'], 'activeParameter')
|
||||
let l:parameters = get(l:signature, 'parameters', [])
|
||||
let l:parameter_index = a:data['response']['result']['activeParameter']
|
||||
let l:parameter = get(l:parameters, l:parameter_index, {})
|
||||
let l:parameter_label = s:get_parameter_label(l:signature, l:parameter)
|
||||
if !empty(l:parameter_label)
|
||||
let l:label = substitute(l:label, '\V\(' . escape(l:parameter_label, '\/?') . '\)', '`\1`', 'g')
|
||||
endif
|
||||
endif
|
||||
|
||||
let l:contents = [l:label]
|
||||
|
||||
if exists('l:parameter')
|
||||
let l:parameter_doc = s:get_parameter_doc(l:parameter)
|
||||
if !empty(l:parameter_doc)
|
||||
call add(l:contents, '')
|
||||
call add(l:contents, l:parameter_doc)
|
||||
call add(l:contents, '')
|
||||
endif
|
||||
endif
|
||||
|
||||
if has_key(l:signature, 'documentation')
|
||||
call add(l:contents, l:signature['documentation'])
|
||||
endif
|
||||
|
||||
call lsp#ui#vim#output#preview(a:server, l:contents, {'statusline': ' LSP SignatureHelp'})
|
||||
return
|
||||
else
|
||||
" signature help is used while inserting. So this must be graceful.
|
||||
"call lsp#utils#error('No signature help information found')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:get_parameter_label(signature, parameter) abort
|
||||
if has_key(a:parameter, 'label')
|
||||
if type(a:parameter['label']) == type([])
|
||||
let l:string_range = a:parameter['label']
|
||||
return strcharpart(
|
||||
\ a:signature['label'],
|
||||
\ l:string_range[0],
|
||||
\ l:string_range[1] - l:string_range[0])
|
||||
endif
|
||||
return a:parameter['label']
|
||||
endif
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! s:get_parameter_doc(parameter) abort
|
||||
if !has_key(a:parameter, 'documentation')
|
||||
return ''
|
||||
endif
|
||||
|
||||
let l:doc = copy(a:parameter['documentation'])
|
||||
if type(l:doc) == type({})
|
||||
let l:doc['value'] = printf('***%s*** - %s', a:parameter['label'], l:doc['value'])
|
||||
return l:doc
|
||||
endif
|
||||
return printf('***%s*** - %s', a:parameter['label'], l:doc)
|
||||
endfunction
|
||||
|
||||
function! s:on_cursor_moved() abort
|
||||
let l:bufnr = bufnr('%')
|
||||
call timer_stop(s:debounce_timer_id)
|
||||
if g:lsp_signature_help_enabled
|
||||
let s:debounce_timer_id = timer_start(g:lsp_signature_help_delay, function('s:on_text_changed_after', [l:bufnr]), { 'repeat': 1 })
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_text_changed_after(bufnr, timer) abort
|
||||
if bufnr('%') != a:bufnr
|
||||
return
|
||||
endif
|
||||
if index(['i', 's'], mode()[0]) == -1
|
||||
return
|
||||
endif
|
||||
if win_id2win(lsp#ui#vim#output#getpreviewwinid()) >= 1
|
||||
return
|
||||
endif
|
||||
|
||||
" Cache trigger chars since this loop is heavy
|
||||
let l:chars = get(b:, 'lsp_signature_help_trigger_character', [])
|
||||
if empty(l:chars)
|
||||
for l:server_name in lsp#get_allowed_servers(a:bufnr)
|
||||
let l:chars += lsp#capabilities#get_signature_help_trigger_characters(l:server_name)
|
||||
endfor
|
||||
let b:lsp_signature_help_trigger_character = l:chars
|
||||
endif
|
||||
|
||||
if index(l:chars, lsp#utils#_get_before_char_skip_white()) >= 0
|
||||
call lsp#ui#vim#signature_help#get_signature_help_under_cursor()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#signature_help#setup() abort
|
||||
augroup _lsp_signature_help_
|
||||
autocmd!
|
||||
autocmd CursorMoved,CursorMovedI * call s:on_cursor_moved()
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#signature_help#_disable() abort
|
||||
augroup _lsp_signature_help_
|
||||
autocmd!
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
164
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/utils.vim
Normal file
164
dot_vim/plugged/vim-lsp/autoload/lsp/ui/vim/utils.vim
Normal file
@@ -0,0 +1,164 @@
|
||||
let s:default_symbol_kinds = {
|
||||
\ '1': 'file',
|
||||
\ '2': 'module',
|
||||
\ '3': 'namespace',
|
||||
\ '4': 'package',
|
||||
\ '5': 'class',
|
||||
\ '6': 'method',
|
||||
\ '7': 'property',
|
||||
\ '8': 'field',
|
||||
\ '9': 'constructor',
|
||||
\ '10': 'enum',
|
||||
\ '11': 'interface',
|
||||
\ '12': 'function',
|
||||
\ '13': 'variable',
|
||||
\ '14': 'constant',
|
||||
\ '15': 'string',
|
||||
\ '16': 'number',
|
||||
\ '17': 'boolean',
|
||||
\ '18': 'array',
|
||||
\ '19': 'object',
|
||||
\ '20': 'key',
|
||||
\ '21': 'null',
|
||||
\ '22': 'enum member',
|
||||
\ '23': 'struct',
|
||||
\ '24': 'event',
|
||||
\ '25': 'operator',
|
||||
\ '26': 'type parameter',
|
||||
\ }
|
||||
|
||||
let s:symbol_kinds = {}
|
||||
|
||||
let s:diagnostic_severity = {
|
||||
\ 1: 'Error',
|
||||
\ 2: 'Warning',
|
||||
\ 3: 'Information',
|
||||
\ 4: 'Hint',
|
||||
\ }
|
||||
|
||||
function! s:symbols_to_loc_list_children(server, path, list, symbols, depth) abort
|
||||
for l:symbol in a:symbols
|
||||
let [l:line, l:col] = lsp#utils#position#lsp_to_vim(a:path, l:symbol['range']['start'])
|
||||
|
||||
call add(a:list, {
|
||||
\ 'filename': a:path,
|
||||
\ 'lnum': l:line,
|
||||
\ 'col': l:col,
|
||||
\ 'text': lsp#ui#vim#utils#_get_symbol_text_from_kind(a:server, l:symbol['kind']) . ' : ' . printf('%' . a:depth. 's', ' ') . l:symbol['name'],
|
||||
\ })
|
||||
if has_key(l:symbol, 'children') && !empty(l:symbol['children'])
|
||||
call s:symbols_to_loc_list_children(a:server, a:path, a:list, l:symbol['children'], a:depth + 1)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#utils#symbols_to_loc_list(server, result) abort
|
||||
if !has_key(a:result['response'], 'result')
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:list = []
|
||||
|
||||
let l:locations = type(a:result['response']['result']) == type({}) ? [a:result['response']['result']] : a:result['response']['result']
|
||||
|
||||
if !empty(l:locations) " some servers also return null so check to make sure it isn't empty
|
||||
for l:symbol in a:result['response']['result']
|
||||
if has_key(l:symbol, 'location')
|
||||
let l:location = l:symbol['location']
|
||||
if lsp#utils#is_file_uri(l:location['uri'])
|
||||
let l:path = lsp#utils#uri_to_path(l:location['uri'])
|
||||
let [l:line, l:col] = lsp#utils#position#lsp_to_vim(l:path, l:location['range']['start'])
|
||||
call add(l:list, {
|
||||
\ 'filename': l:path,
|
||||
\ 'lnum': l:line,
|
||||
\ 'col': l:col,
|
||||
\ 'text': lsp#ui#vim#utils#_get_symbol_text_from_kind(a:server, l:symbol['kind']) . ' : ' . l:symbol['name'],
|
||||
\ })
|
||||
endif
|
||||
else
|
||||
let l:location = a:result['request']['params']['textDocument']['uri']
|
||||
if lsp#utils#is_file_uri(l:location)
|
||||
let l:path = lsp#utils#uri_to_path(l:location)
|
||||
let [l:line, l:col] = lsp#utils#position#lsp_to_vim(l:path, l:symbol['range']['start'])
|
||||
call add(l:list, {
|
||||
\ 'filename': l:path,
|
||||
\ 'lnum': l:line,
|
||||
\ 'col': l:col,
|
||||
\ 'text': lsp#ui#vim#utils#_get_symbol_text_from_kind(a:server, l:symbol['kind']) . ' : ' . l:symbol['name'],
|
||||
\ })
|
||||
if has_key(l:symbol, 'children') && !empty(l:symbol['children'])
|
||||
call s:symbols_to_loc_list_children(a:server, l:path, l:list, l:symbol['children'], 1)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
return l:list
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#utils#diagnostics_to_loc_list(result) abort
|
||||
if !has_key(a:result['response'], 'params')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:uri = a:result['response']['params']['uri']
|
||||
let l:diagnostics = lsp#utils#iteratable(a:result['response']['params']['diagnostics'])
|
||||
|
||||
let l:list = []
|
||||
|
||||
if !empty(l:diagnostics) && lsp#utils#is_file_uri(l:uri)
|
||||
let l:path = lsp#utils#uri_to_path(l:uri)
|
||||
for l:item in l:diagnostics
|
||||
let l:severity_text = ''
|
||||
if has_key(l:item, 'severity') && !empty(l:item['severity'])
|
||||
let l:severity_text = s:get_diagnostic_severity_text(l:item['severity'])
|
||||
endif
|
||||
let l:text = ''
|
||||
if has_key(l:item, 'source') && !empty(l:item['source'])
|
||||
let l:text .= l:item['source'] . ':'
|
||||
endif
|
||||
if l:severity_text !=# ''
|
||||
let l:text .= l:severity_text . ':'
|
||||
endif
|
||||
if has_key(l:item, 'code') && !empty(l:item['code'])
|
||||
let l:text .= l:item['code'] . ':'
|
||||
endif
|
||||
let l:text .= l:item['message']
|
||||
let [l:line, l:col] = lsp#utils#position#lsp_to_vim(l:path, l:item['range']['start'])
|
||||
let l:location_item = {
|
||||
\ 'filename': l:path,
|
||||
\ 'lnum': l:line,
|
||||
\ 'col': l:col,
|
||||
\ 'text': l:text,
|
||||
\ }
|
||||
if l:severity_text !=# ''
|
||||
" 'E' for error, 'W' for warning, 'I' for information, 'H' for hint
|
||||
let l:location_item['type'] = l:severity_text[0]
|
||||
endif
|
||||
call add(l:list, l:location_item)
|
||||
endfor
|
||||
endif
|
||||
|
||||
return l:list
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#utils#_get_symbol_text_from_kind(server, kind) abort
|
||||
if !has_key(s:symbol_kinds, a:server)
|
||||
let l:server_info = lsp#get_server_info(a:server)
|
||||
if has_key (l:server_info, 'config') && has_key(l:server_info['config'], 'symbol_kinds')
|
||||
let s:symbol_kinds[a:server] = extend(copy(s:default_symbol_kinds), l:server_info['config']['symbol_kinds'])
|
||||
else
|
||||
let s:symbol_kinds[a:server] = s:default_symbol_kinds
|
||||
endif
|
||||
endif
|
||||
return get(s:symbol_kinds[a:server], a:kind, 'unknown symbol ' . a:kind)
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#utils#get_symbol_kinds() abort
|
||||
return map(keys(s:default_symbol_kinds), {idx, key -> str2nr(key)})
|
||||
endfunction
|
||||
|
||||
function! s:get_diagnostic_severity_text(severity) abort
|
||||
return s:diagnostic_severity[a:severity]
|
||||
endfunction
|
||||
Reference in New Issue
Block a user