mirror of
https://github.com/prabirshrestha/vim-lsp.git
synced 2025-12-14 20:35:59 +01:00
771 lines
27 KiB
VimL
771 lines
27 KiB
VimL
let s:last_req_id = 0
|
|
|
|
function! s:not_supported(what) abort
|
|
return lsp#utils#error(a:what.' not supported for '.&filetype)
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#implementation(in_preview) abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_implementation_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
call setqflist([])
|
|
|
|
if len(l:servers) == 0
|
|
call s:not_supported('Retrieving implementation')
|
|
return
|
|
endif
|
|
let l:ctx = { 'counter': len(l:servers), 'list':[], 'last_req_id': s:last_req_id, 'jump_if_one': 1, 'in_preview': a:in_preview }
|
|
for l:server in l:servers
|
|
call lsp#send_request(l:server, {
|
|
\ 'method': 'textDocument/implementation',
|
|
\ 'params': {
|
|
\ 'textDocument': lsp#get_text_document_identifier(),
|
|
\ 'position': lsp#get_position(),
|
|
\ },
|
|
\ 'on_notification': function('s:handle_location', [l:ctx, l:server, 'implementation']),
|
|
\ })
|
|
endfor
|
|
|
|
echo 'Retrieving implementation ...'
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#type_definition(in_preview) abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_type_definition_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
call setqflist([])
|
|
|
|
if len(l:servers) == 0
|
|
call s:not_supported('Retrieving type definition')
|
|
return
|
|
endif
|
|
let l:ctx = { 'counter': len(l:servers), 'list':[], 'last_req_id': s:last_req_id, 'jump_if_one': 1, 'in_preview': a:in_preview }
|
|
for l:server in l:servers
|
|
call lsp#send_request(l:server, {
|
|
\ 'method': 'textDocument/typeDefinition',
|
|
\ 'params': {
|
|
\ 'textDocument': lsp#get_text_document_identifier(),
|
|
\ 'position': lsp#get_position(),
|
|
\ },
|
|
\ 'on_notification': function('s:handle_location', [l:ctx, l:server, 'type definition']),
|
|
\ })
|
|
endfor
|
|
|
|
echo 'Retrieving type definition ...'
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#type_hierarchy() abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_type_hierarchy_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
|
|
if len(l:servers) == 0
|
|
call s:not_supported('Retrieving type hierarchy')
|
|
return
|
|
endif
|
|
let l:ctx = { 'counter': len(l:servers), 'list':[], 'last_req_id': s:last_req_id }
|
|
" direction 0 children, 1 parent, 2 both
|
|
for l:server in l:servers
|
|
call lsp#send_request(l:server, {
|
|
\ 'method': 'textDocument/typeHierarchy',
|
|
\ 'params': {
|
|
\ 'textDocument': lsp#get_text_document_identifier(),
|
|
\ 'position': lsp#get_position(),
|
|
\ 'direction': 2,
|
|
\ 'resolve': 1,
|
|
\ },
|
|
\ 'on_notification': function('s:handle_type_hierarchy', [l:ctx, l:server, 'type hierarchy']),
|
|
\ })
|
|
endfor
|
|
|
|
echo 'Retrieving type hierarchy ...'
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#declaration(in_preview) abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_declaration_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
call setqflist([])
|
|
|
|
if len(l:servers) == 0
|
|
call s:not_supported('Retrieving declaration')
|
|
return
|
|
endif
|
|
|
|
let l:ctx = { 'counter': len(l:servers), 'list':[], 'last_req_id': s:last_req_id, 'jump_if_one': 1, 'in_preview': a:in_preview }
|
|
for l:server in l:servers
|
|
call lsp#send_request(l:server, {
|
|
\ 'method': 'textDocument/declaration',
|
|
\ 'params': {
|
|
\ 'textDocument': lsp#get_text_document_identifier(),
|
|
\ 'position': lsp#get_position(),
|
|
\ },
|
|
\ 'on_notification': function('s:handle_location', [l:ctx, l:server, 'declaration']),
|
|
\ })
|
|
endfor
|
|
|
|
echo 'Retrieving declaration ...'
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#definition(in_preview) abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_definition_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
call setqflist([])
|
|
|
|
if len(l:servers) == 0
|
|
call s:not_supported('Retrieving definition')
|
|
return
|
|
endif
|
|
|
|
let l:ctx = { 'counter': len(l:servers), 'list':[], 'last_req_id': s:last_req_id, 'jump_if_one': 1, 'in_preview': a:in_preview }
|
|
for l:server in l:servers
|
|
call lsp#send_request(l:server, {
|
|
\ 'method': 'textDocument/definition',
|
|
\ 'params': {
|
|
\ 'textDocument': lsp#get_text_document_identifier(),
|
|
\ 'position': lsp#get_position(),
|
|
\ },
|
|
\ 'on_notification': function('s:handle_location', [l:ctx, l:server, 'definition']),
|
|
\ })
|
|
endfor
|
|
|
|
echo 'Retrieving definition ...'
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#references() abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_references_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
|
|
call setqflist([])
|
|
|
|
let l:ctx = { 'counter': len(l:servers), 'list':[], 'last_req_id': s:last_req_id, 'jump_if_one': 0, 'in_preview': 0 }
|
|
if len(l:servers) == 0
|
|
call s:not_supported('Retrieving references')
|
|
return
|
|
endif
|
|
|
|
for l:server in l:servers
|
|
call lsp#send_request(l:server, {
|
|
\ 'method': 'textDocument/references',
|
|
\ 'params': {
|
|
\ 'textDocument': lsp#get_text_document_identifier(),
|
|
\ 'position': lsp#get_position(),
|
|
\ 'context': {'includeDeclaration': v:false},
|
|
\ },
|
|
\ 'on_notification': function('s:handle_location', [l:ctx, l:server, 'references']),
|
|
\ })
|
|
endfor
|
|
|
|
echo 'Retrieving references ...'
|
|
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, s:last_req_id, 'rename']),
|
|
\ })
|
|
|
|
echo ' ... Renaming ...'
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#rename() abort
|
|
let l:servers = filter(lsp#get_whitelisted_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_whitelisted_servers(), 'lsp#capabilities#has_rename_provider(v:val)')
|
|
let l:prepare_support = 0
|
|
endif
|
|
|
|
let s:last_req_id = s:last_req_id + 1
|
|
|
|
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, s:last_req_id, 'rename_prepare']),
|
|
\ })
|
|
return
|
|
endif
|
|
|
|
call s:rename(l:server, input('new name: ', expand('<cword>')), lsp#get_position())
|
|
endfunction
|
|
|
|
function! s:document_format(sync) abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_document_formatting_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
|
|
if len(l:servers) == 0
|
|
call s:not_supported('Document formatting')
|
|
return
|
|
endif
|
|
|
|
" TODO: ask user to select server for formatting
|
|
let l:server = l:servers[0]
|
|
redraw | echo 'Formatting document ...'
|
|
call lsp#send_request(l:server, {
|
|
\ 'method': 'textDocument/formatting',
|
|
\ 'params': {
|
|
\ 'textDocument': lsp#get_text_document_identifier(),
|
|
\ 'options': {
|
|
\ 'tabSize': getbufvar(bufnr('%'), '&tabstop'),
|
|
\ 'insertSpaces': getbufvar(bufnr('%'), '&expandtab') ? v:true : v:false,
|
|
\ },
|
|
\ },
|
|
\ 'sync': a:sync,
|
|
\ 'on_notification': function('s:handle_text_edit', [l:server, s:last_req_id, 'document format']),
|
|
\ })
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#document_format_sync() abort
|
|
let l:mode = mode()
|
|
if l:mode =~# '[vV]' || l:mode ==# "\<C-V>"
|
|
return s:document_format_range(1)
|
|
endif
|
|
return s:document_format(1)
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#document_format() abort
|
|
let l:mode = mode()
|
|
if l:mode =~# '[vV]' || l:mode ==# "\<C-V>"
|
|
return s:document_format_range(0)
|
|
endif
|
|
return s:document_format(0)
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#stop_server(...) abort
|
|
let l:name = get(a:000, 0, '')
|
|
for l:server in lsp#get_whitelisted_servers()
|
|
if !empty(l:name) && l:server != l:name
|
|
continue
|
|
endif
|
|
echo 'Stopping' l:server 'server ...'
|
|
call lsp#stop_server(server)
|
|
endfor
|
|
endfunction
|
|
|
|
function! s:get_selection_pos(type) abort
|
|
if a:type ==? 'v'
|
|
let l:start_pos = getpos("'<")[1:2]
|
|
let l:end_pos = getpos("'>")[1:2]
|
|
" fix end_pos column (see :h getpos() and :h 'selection')
|
|
let l:end_line = getline(l:end_pos[0])
|
|
let l:offset = (&selection ==# 'inclusive' ? 1 : 2)
|
|
let l:end_pos[1] = len(l:end_line[:l:end_pos[1]-l:offset])
|
|
" edge case: single character selected with selection=exclusive
|
|
if l:start_pos[0] == l:end_pos[0] && l:start_pos[1] > l:end_pos[1]
|
|
let l:end_pos[1] = l:start_pos[1]
|
|
endif
|
|
elseif a:type ==? 'line'
|
|
let l:start_pos = [line("'["), 1]
|
|
let l:end_lnum = line("']")
|
|
let l:end_pos = [line("']"), len(getline(l:end_lnum))]
|
|
elseif a:type ==? 'char'
|
|
let l:start_pos = getpos("'[")[1:2]
|
|
let l:end_pos = getpos("']")[1:2]
|
|
else
|
|
let l:start_pos = [0, 0]
|
|
let l:end_pos = [0, 0]
|
|
endif
|
|
|
|
return l:start_pos + l:end_pos
|
|
endfunction
|
|
|
|
function! s:document_format_range(sync, type) abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_document_range_formatting_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
|
|
if len(l:servers) == 0
|
|
call s:not_supported('Document range formatting')
|
|
return
|
|
endif
|
|
|
|
" TODO: ask user to select server for formatting
|
|
let l:server = l:servers[0]
|
|
|
|
let [l:start_lnum, l:start_col, l:end_lnum, l:end_col] = s:get_selection_pos(a:type)
|
|
let l:start_char = lsp#utils#to_char('%', l:start_lnum, l:start_col)
|
|
let l:end_char = lsp#utils#to_char('%', l:end_lnum, l:end_col)
|
|
redraw | echo 'Formatting document range ...'
|
|
call lsp#send_request(l:server, {
|
|
\ 'method': 'textDocument/rangeFormatting',
|
|
\ 'params': {
|
|
\ 'textDocument': lsp#get_text_document_identifier(),
|
|
\ 'range': {
|
|
\ 'start': { 'line': l:start_lnum - 1, 'character': l:start_char },
|
|
\ 'end': { 'line': l:end_lnum - 1, 'character': l:end_char },
|
|
\ },
|
|
\ 'options': {
|
|
\ 'tabSize': getbufvar(bufnr('%'), '&shiftwidth'),
|
|
\ 'insertSpaces': getbufvar(bufnr('%'), '&expandtab') ? v:true : v:false,
|
|
\ },
|
|
\ },
|
|
\ 'sync': a:sync,
|
|
\ 'on_notification': function('s:handle_text_edit', [l:server, s:last_req_id, 'range format']),
|
|
\ })
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#document_range_format_sync() abort
|
|
return s:document_format_range(1, visualmode())
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#document_range_format() abort
|
|
return s:document_format_range(0, visualmode())
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#document_range_format_opfunc(type) abort
|
|
return s:document_format_range(1, a:type)
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#workspace_symbol() abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_workspace_symbol_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
|
|
call setqflist([])
|
|
|
|
if len(l:servers) == 0
|
|
call s:not_supported('Retrieving workspace symbols')
|
|
return
|
|
endif
|
|
|
|
let l:query = input('query>')
|
|
|
|
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, s:last_req_id, 'workspaceSymbol']),
|
|
\ })
|
|
endfor
|
|
|
|
echo 'Retrieving workspace symbols ...'
|
|
endfunction
|
|
|
|
function! lsp#ui#vim#document_symbol() abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_document_symbol_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
|
|
call setqflist([])
|
|
|
|
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, s:last_req_id, 'documentSymbol']),
|
|
\ })
|
|
endfor
|
|
|
|
echo 'Retrieving document symbols ...'
|
|
endfunction
|
|
|
|
" Returns currently selected range. If nothing is selected, returns empty
|
|
" dictionary.
|
|
"
|
|
" @returns
|
|
" Range - https://microsoft.github.io/language-server-protocol/specification#range
|
|
function! s:get_visual_selection_range() abort
|
|
" TODO: unify this method with s:get_visual_selection_pos()
|
|
let [l:line_start, l:column_start] = getpos("'<")[1:2]
|
|
let [l:line_end, l:column_end] = getpos("'>")[1:2]
|
|
call lsp#log([l:line_start, l:column_start, l:line_end, l:column_end])
|
|
if l:line_start == 0
|
|
return {}
|
|
endif
|
|
" For line selection, column_end is a very large number, so trim it to
|
|
" number of characters in this line.
|
|
if l:column_end - 1 > len(getline(l:line_end))
|
|
let l:column_end = len(getline(l:line_end)) + 1
|
|
endif
|
|
let l:char_start = lsp#utils#to_char('%', l:line_start, l:column_start)
|
|
let l:char_end = lsp#utils#to_char('%', l:line_end, l:column_end)
|
|
return {
|
|
\ 'start': { 'line': l:line_start - 1, 'character': l:char_start },
|
|
\ 'end': { 'line': l:line_end - 1, 'character': l:char_end },
|
|
\}
|
|
endfunction
|
|
|
|
" https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction
|
|
function! lsp#ui#vim#code_action() abort
|
|
let l:servers = filter(lsp#get_whitelisted_servers(), 'lsp#capabilities#has_code_action_provider(v:val)')
|
|
let s:last_req_id = s:last_req_id + 1
|
|
let l:diagnostic = lsp#ui#vim#diagnostics#get_diagnostics_under_cursor()
|
|
|
|
if len(l:servers) == 0
|
|
call s:not_supported('Code action')
|
|
return
|
|
endif
|
|
|
|
let l:range = s:get_visual_selection_range()
|
|
if empty(l:range)
|
|
if empty(l:diagnostic)
|
|
echo 'No diagnostics found under the cursors'
|
|
return
|
|
else
|
|
let l:range = l:diagnostic['range']
|
|
let l:diagnostics = [l:diagnostic]
|
|
end
|
|
else
|
|
let l:diagnostics = []
|
|
endif
|
|
|
|
for l:server in l:servers
|
|
call lsp#send_request(l:server, {
|
|
\ 'method': 'textDocument/codeAction',
|
|
\ 'params': {
|
|
\ 'textDocument': lsp#get_text_document_identifier(),
|
|
\ 'range': l:range,
|
|
\ 'context': {
|
|
\ 'diagnostics' : l:diagnostics,
|
|
\ 'only': ['', 'quickfix', 'refactor', 'refactor.extract', 'refactor.inline', 'refactor.rewrite', 'source', 'source.organizeImports'],
|
|
\ },
|
|
\ },
|
|
\ 'on_notification': function('s:handle_code_action', [l:server, s:last_req_id, 'codeAction']),
|
|
\ })
|
|
endfor
|
|
|
|
echo 'Retrieving code actions ...'
|
|
endfunction
|
|
|
|
function! s:handle_symbol(server, last_req_id, type, data) abort
|
|
if a:last_req_id != s:last_req_id
|
|
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)
|
|
|
|
call setqflist(l:list)
|
|
|
|
if empty(l:list)
|
|
call lsp#utils#error('No ' . a:type .' found')
|
|
else
|
|
echo 'Retrieved ' . a:type
|
|
botright copen
|
|
endif
|
|
endfunction
|
|
|
|
function! s:update_tagstack() abort
|
|
let l:bufnr = bufnr('%')
|
|
let l:item = {'bufnr': l:bufnr, 'from': [l:bufnr, line('.'), col('.'), 0], 'tagname': expand('<cword>')}
|
|
let l:winid = win_getid()
|
|
|
|
let l:stack = gettagstack(l:winid)
|
|
if l:stack['length'] == l:stack['curidx']
|
|
" Replace the last items with item.
|
|
let l:action = 'r'
|
|
let l:stack['items'][l:stack['curidx']-1] = l:item
|
|
elseif l:stack['length'] > l:stack['curidx']
|
|
" Replace items after used items with item.
|
|
let l:action = 'r'
|
|
if l:stack['curidx'] > 1
|
|
let l:stack['items'] = add(l:stack['items'][:l:stack['curidx']-2], l:item)
|
|
else
|
|
let l:stack['items'] = [l:item]
|
|
endif
|
|
else
|
|
" Append item.
|
|
let l:action = 'a'
|
|
let l:stack['items'] = [l:item]
|
|
endif
|
|
let l:stack['curidx'] += 1
|
|
|
|
call settagstack(l:winid, l:stack, l:action)
|
|
endfunction
|
|
|
|
function! s:handle_location(ctx, server, type, data) abort "ctx = {counter, list, jump_if_one, last_req_id, in_preview}
|
|
if a:ctx['last_req_id'] != s:last_req_id
|
|
return
|
|
endif
|
|
|
|
let a:ctx['counter'] = a:ctx['counter'] - 1
|
|
|
|
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']))
|
|
else
|
|
let a:ctx['list'] = a:ctx['list'] + lsp#ui#vim#utils#locations_to_loc_list(a:data)
|
|
endif
|
|
|
|
if a:ctx['counter'] == 0
|
|
if empty(a:ctx['list'])
|
|
call lsp#utils#error('No ' . a:type .' found')
|
|
else
|
|
if exists('*gettagstack') && exists('*settagstack')
|
|
call s:update_tagstack()
|
|
endif
|
|
|
|
let l:loc = a:ctx['list'][0]
|
|
|
|
if len(a:ctx['list']) == 1 && a:ctx['jump_if_one'] && !a:ctx['in_preview']
|
|
normal! m'
|
|
let l:buffer = bufnr(l:loc['filename'])
|
|
if &modified && !&hidden
|
|
let l:cmd = l:buffer !=# -1 ? 'sb ' . l:buffer : 'split ' . fnameescape(l:loc['filename'])
|
|
else
|
|
let l:cmd = l:buffer !=# -1 ? 'b ' . l:buffer : 'edit ' . fnameescape(l:loc['filename'])
|
|
endif
|
|
execute l:cmd . ' | call cursor('.l:loc['lnum'].','.l:loc['col'].')'
|
|
echo 'Retrieved ' . a:type
|
|
redraw
|
|
elseif !a:ctx['in_preview']
|
|
call setqflist(a:ctx['list'])
|
|
echo 'Retrieved ' . a:type
|
|
botright copen
|
|
else
|
|
let l:lines = readfile(fnameescape(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_req_id, type, data) abort
|
|
if a:last_req_id != s:last_req_id
|
|
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:range = a:data['response']['result']
|
|
let l:lines = getline(1, '$')
|
|
let l:start_line = l:range['start']['line'] + 1
|
|
let l:start_char = l:range['start']['character']
|
|
let l:start_col = lsp#utils#to_col('%', l:start_line, l:start_char)
|
|
let l:end_line = l:range['end']['line'] + 1
|
|
let l:end_char = l:range['end']['character']
|
|
let l:end_col = lsp#utils#to_col('%', l:end_line, l:end_char)
|
|
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_req_id, type, data) abort
|
|
if a:last_req_id != s:last_req_id
|
|
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_req_id, type, data) abort
|
|
if a:last_req_id != s:last_req_id
|
|
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! s:handle_code_action(server, last_req_id, type, data) abort
|
|
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
|
|
|
|
let l:codeActions = a:data['response']['result']
|
|
|
|
let l:index = 0
|
|
let l:choices = []
|
|
|
|
call lsp#log('s:handle_code_action', l:codeActions)
|
|
|
|
if len(l:codeActions) == 0
|
|
echo 'No code actions found'
|
|
return
|
|
endif
|
|
|
|
while l:index < len(l:codeActions)
|
|
call add(l:choices, string(l:index + 1) . ' - ' . l:codeActions[index]['title'])
|
|
|
|
let l:index += 1
|
|
endwhile
|
|
|
|
let l:choice = inputlist(l:choices)
|
|
|
|
if l:choice > 0 && l:choice <= l:index
|
|
call s:execute_command_or_code_action(a:server, l:codeActions[l:choice - 1])
|
|
endif
|
|
endfunction
|
|
|
|
function! s:handle_type_hierarchy(ctx, server, type, data) abort "ctx = {counter, list, last_req_id}
|
|
if a:ctx['last_req_id'] != s:last_req_id
|
|
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
|
|
|
|
if empty(a:data['response']['result'])
|
|
echo 'No type hierarchy found'
|
|
return
|
|
endif
|
|
|
|
" Create new buffer in a split
|
|
let l:position = 'topleft'
|
|
let l:orientation = 'new'
|
|
exec l:position . ' ' . 10 . l:orientation
|
|
|
|
let l:provider = {
|
|
\ 'root': a:data['response']['result'],
|
|
\ 'root_state': 'expanded',
|
|
\ 'bufnr': bufnr('%'),
|
|
\ 'getChildren': function('s:get_children_for_tree_hierarchy'),
|
|
\ 'getParent': function('s:get_parent_for_tree_hierarchy'),
|
|
\ 'getTreeItem': function('s:get_treeitem_for_tree_hierarchy'),
|
|
\ }
|
|
|
|
call lsp#utils#tree#new(l:provider)
|
|
|
|
echo 'Retrieved type hierarchy'
|
|
endfunction
|
|
|
|
function! s:hierarchyitem_to_treeitem(hierarchyitem) abort
|
|
return {
|
|
\ 'id': a:hierarchyitem,
|
|
\ 'label': a:hierarchyitem['name'],
|
|
\ 'command': function('s:hierarchy_treeitem_command', [a:hierarchyitem]),
|
|
\ 'collapsibleState': has_key(a:hierarchyitem, 'parents') && !empty(a:hierarchyitem['parents']) ? 'expanded' : 'none',
|
|
\ }
|
|
endfunction
|
|
|
|
function! s:hierarchy_treeitem_command(hierarchyitem) abort
|
|
bwipeout
|
|
|
|
let l:path = lsp#utils#uri_to_path(a:hierarchyitem['uri'])
|
|
let l:line = a:hierarchyitem['range']['start']['line'] + 1
|
|
let l:char = a:hierarchyitem['range']['start']['character']
|
|
let l:col = lsp#utils#to_col(l:path, l:line, l:char)
|
|
|
|
let l:buffer = bufnr(l:path)
|
|
if &modified && !&hidden
|
|
let l:cmd = l:buffer !=# -1 ? 'sb ' . l:buffer : 'split ' . fnameescape(l:path)
|
|
else
|
|
echom 'edit'
|
|
let l:cmd = l:buffer !=# -1 ? 'b ' . l:buffer : 'edit ' . fnameescape(l:path)
|
|
endif
|
|
execute l:cmd . ' | call cursor('.l:line.','.l:col.')'
|
|
endfunction
|
|
|
|
function! s:get_children_for_tree_hierarchy(Callback, ...) dict abort
|
|
if a:0 == 0
|
|
call a:Callback('success', [l:self['root']])
|
|
return
|
|
else
|
|
call a:Callback('success', a:1['parents'])
|
|
endif
|
|
endfunction
|
|
|
|
function! s:get_parent_for_tree_hierarchy(...) dict abort
|
|
" TODO
|
|
endfunction
|
|
|
|
function! s:get_treeitem_for_tree_hierarchy(Callback, object) dict abort
|
|
call a:Callback('success', s:hierarchyitem_to_treeitem(a:object))
|
|
endfunction
|
|
|
|
" @params
|
|
" server - string
|
|
" comand_or_code_action - Command | CodeAction
|
|
function! s:execute_command_or_code_action(server, command_or_code_action) abort
|
|
if has_key(a:command_or_code_action, 'command') && type(a:command_or_code_action['command']) == type('')
|
|
let l:command = a:command_or_code_action
|
|
call s:execute_command(a:server, l:command)
|
|
else
|
|
let l:code_action = a:command_or_code_action
|
|
if has_key(l:code_action, 'edit')
|
|
call lsp#utils#workspace_edit#apply_workspace_edit(a:command_or_code_action['edit'])
|
|
endif
|
|
if has_key(l:code_action, 'command')
|
|
call s:execute_command(a:server, l:code_action['command'])
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
" Sends workspace/executeCommand with given command.
|
|
" @params
|
|
" server - string
|
|
" command - https://microsoft.github.io/language-server-protocol/specification#command
|
|
function! s:execute_command(server, command) abort
|
|
let l:params = {'command': a:command['command']}
|
|
if has_key(a:command, 'arguments')
|
|
let l:params['arguments'] = a:command['arguments']
|
|
endif
|
|
call lsp#send_request(a:server, {
|
|
\ 'method': 'workspace/executeCommand',
|
|
\ 'params': l:params,
|
|
\ })
|
|
endfunction
|
|
|
|
|