I broke up with neovim....vim is my best friend now
This commit is contained in:
65
dot_vim/plugged/vim-lsp/autoload/health/lsp.vim
Normal file
65
dot_vim/plugged/vim-lsp/autoload/health/lsp.vim
Normal file
@@ -0,0 +1,65 @@
|
||||
function! s:BuildConfigBlock(section, info) abort
|
||||
let l:block = get(a:info, a:section, '')
|
||||
if !empty(l:block)
|
||||
return printf("### %s\n%s\n", a:section, l:block)
|
||||
endif
|
||||
return ''
|
||||
endf
|
||||
|
||||
|
||||
function! health#lsp#check() abort
|
||||
call health#report_start('server status')
|
||||
let l:server_status = lsp#collect_server_status()
|
||||
|
||||
let l:has_printed = v:false
|
||||
for l:k in sort(keys(l:server_status))
|
||||
let l:report = l:server_status[l:k]
|
||||
|
||||
let l:status_msg = printf('%s: %s', l:k, l:report.status)
|
||||
if l:report.status == 'running'
|
||||
call health#report_ok(l:status_msg)
|
||||
elseif l:report.status == 'failed'
|
||||
call health#report_error(l:status_msg, 'See :help g:lsp_log_verbose to debug server failure.')
|
||||
else
|
||||
call health#report_warn(l:status_msg)
|
||||
endif
|
||||
let l:has_printed = v:true
|
||||
endfor
|
||||
|
||||
if !l:has_printed
|
||||
call health#report_warn('no servers connected')
|
||||
endif
|
||||
|
||||
for l:k in sort(keys(l:server_status))
|
||||
call health#report_start(printf('server configuration: %s', l:k))
|
||||
let l:report = l:server_status[l:k]
|
||||
|
||||
let l:msg = "\t\n"
|
||||
let l:msg .= s:BuildConfigBlock('allowlist', l:report.info)
|
||||
let l:msg .= s:BuildConfigBlock('blocklist', l:report.info)
|
||||
let l:cfg = get(l:report.info, 'workspace_config', '')
|
||||
if !empty(l:cfg)
|
||||
if get(g:, 'loaded_scriptease', 0)
|
||||
let l:cfg = scriptease#dump(l:cfg, {'width': &columns-1})
|
||||
else
|
||||
let l:cfg = json_encode(l:cfg)
|
||||
" Add some whitespace to make it readable.
|
||||
let l:cfg = substitute(l:cfg, '[,{(\[]', "&\n\t", 'g')
|
||||
let l:cfg = substitute(l:cfg, '":', '& ', 'g')
|
||||
let l:cfg = substitute(l:cfg, '\v[})\]]+', "\n&", 'g')
|
||||
let l:cfg = substitute(l:cfg, '\n\s*\n', "\n", 'g')
|
||||
endif
|
||||
let l:msg .= printf("### workspace_config\n```json\n%s\n```", l:cfg)
|
||||
endif
|
||||
call health#report_info(l:msg)
|
||||
endfor
|
||||
|
||||
call health#report_start('Performance')
|
||||
if lsp#utils#has_lua() && g:lsp_use_lua
|
||||
call health#report_ok('Using lua for faster performance.')
|
||||
else
|
||||
call health#report_warn('Missing requirements to enable lua for faster performance.')
|
||||
endif
|
||||
|
||||
endf
|
||||
|
||||
1332
dot_vim/plugged/vim-lsp/autoload/lsp.vim
Normal file
1332
dot_vim/plugged/vim-lsp/autoload/lsp.vim
Normal file
File diff suppressed because it is too large
Load Diff
1700
dot_vim/plugged/vim-lsp/autoload/lsp/callbag.vim
Normal file
1700
dot_vim/plugged/vim-lsp/autoload/lsp/callbag.vim
Normal file
File diff suppressed because it is too large
Load Diff
198
dot_vim/plugged/vim-lsp/autoload/lsp/capabilities.vim
Normal file
198
dot_vim/plugged/vim-lsp/autoload/lsp/capabilities.vim
Normal file
@@ -0,0 +1,198 @@
|
||||
function! s:has_provider(server_name, ...) abort
|
||||
let l:value = lsp#get_server_capabilities(a:server_name)
|
||||
for l:provider in a:000
|
||||
if empty(l:value) || type(l:value) != type({}) || !has_key(l:value, l:provider)
|
||||
return 0
|
||||
endif
|
||||
let l:value = l:value[l:provider]
|
||||
endfor
|
||||
return (type(l:value) == type(v:true) && l:value == v:true) || type(l:value) == type({})
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_declaration_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'declarationProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_definition_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'definitionProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_references_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'referencesProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_hover_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'hoverProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_rename_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'renameProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_rename_prepare_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'renameProvider', 'prepareProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_workspace_folders_change_notifications(server_name) abort
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server_name)
|
||||
if type(l:capabilities) == type({}) && !empty(l:capabilities)
|
||||
let l:workspace = get(l:capabilities, 'workspace', {})
|
||||
if type(l:workspace) == type({}) && !empty(l:workspace)
|
||||
let l:workspace_folders = get(l:workspace, 'workspaceFolders', {})
|
||||
if type(l:workspace_folders) == type({}) && !empty(l:workspace_folders)
|
||||
if get(l:workspace_folders, 'supported', v:false) && get(l:workspace_folders, 'changeNotifications', '') ==# 'workspace/didChangeWorkspaceFolders'
|
||||
return v:true
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
return v:false
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_document_formatting_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'documentFormattingProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_document_range_formatting_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'documentRangeFormattingProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_document_symbol_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'documentSymbolProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_workspace_symbol_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'workspaceSymbolProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_implementation_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'implementationProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_code_action_provider(server_name) abort
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server_name)
|
||||
if !empty(l:capabilities) && has_key(l:capabilities, 'codeActionProvider')
|
||||
if type(l:capabilities['codeActionProvider']) == type({})
|
||||
if has_key(l:capabilities['codeActionProvider'], 'codeActionKinds') && type(l:capabilities['codeActionProvider']['codeActionKinds']) == type([])
|
||||
return len(l:capabilities['codeActionProvider']['codeActionKinds']) != 0
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
return s:has_provider(a:server_name, 'codeActionProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_code_lens_provider(server_name) abort
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server_name)
|
||||
if !empty(l:capabilities) && has_key(l:capabilities, 'codeLensProvider')
|
||||
return 1
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_type_definition_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'typeDefinitionProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_type_hierarchy_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'typeHierarchyProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_document_highlight_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'documentHighlightProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_folding_range_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'foldingRangeProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_call_hierarchy_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'callHierarchyProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_semantic_tokens(server_name) abort
|
||||
return s:has_provider(a:server_name, 'semanticTokensProvider')
|
||||
endfunction
|
||||
|
||||
" [supports_did_save (boolean), { 'includeText': boolean }]
|
||||
function! lsp#capabilities#get_text_document_save_registration_options(server_name) abort
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server_name)
|
||||
if !empty(l:capabilities) && has_key(l:capabilities, 'textDocumentSync')
|
||||
if type(l:capabilities['textDocumentSync']) == type({})
|
||||
let l:save_options = get(l:capabilities['textDocumentSync'], 'save', 0)
|
||||
if type(l:save_options) == type({})
|
||||
return [1, {'includeText': get(l:save_options, 'includeText', 0)}]
|
||||
else
|
||||
return [l:save_options ? 1 : 0, {'includeText': 0 }]
|
||||
endif
|
||||
else
|
||||
return [1, { 'includeText': 0 }]
|
||||
endif
|
||||
endif
|
||||
return [0, { 'includeText': 0 }]
|
||||
endfunction
|
||||
|
||||
" supports_did_change (boolean)
|
||||
function! lsp#capabilities#get_text_document_change_sync_kind(server_name) abort
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server_name)
|
||||
if !empty(l:capabilities) && has_key(l:capabilities, 'textDocumentSync')
|
||||
if type(l:capabilities['textDocumentSync']) == type({})
|
||||
if has_key(l:capabilities['textDocumentSync'], 'change') && type(l:capabilities['textDocumentSync']['change']) == type(1)
|
||||
let l:val = l:capabilities['textDocumentSync']['change']
|
||||
return l:val >= 0 && l:val <= 2 ? l:val : 1
|
||||
else
|
||||
return 1
|
||||
endif
|
||||
elseif type(l:capabilities['textDocumentSync']) == type(1)
|
||||
return l:capabilities['textDocumentSync']
|
||||
else
|
||||
return 1
|
||||
endif
|
||||
endif
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_signature_help_provider(server_name) abort
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server_name)
|
||||
if !empty(l:capabilities) && has_key(l:capabilities, 'signatureHelpProvider')
|
||||
return 1
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#get_signature_help_trigger_characters(server_name) abort
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server_name)
|
||||
if !empty(l:capabilities) && has_key(l:capabilities, 'signatureHelpProvider')
|
||||
let l:trigger_chars = []
|
||||
if type(l:capabilities['signatureHelpProvider']) == type({})
|
||||
if has_key(l:capabilities['signatureHelpProvider'], 'triggerCharacters')
|
||||
let l:trigger_chars = l:capabilities['signatureHelpProvider']['triggerCharacters']
|
||||
endif
|
||||
" If retriggerChars exist, just treat them like triggerChars.
|
||||
if has_key(l:capabilities['signatureHelpProvider'], 'retriggerCharacters')
|
||||
let l:trigger_chars += l:capabilities['signatureHelpProvider']['retriggerCharacters']
|
||||
endif
|
||||
return l:trigger_chars
|
||||
endif
|
||||
endif
|
||||
return []
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#get_code_action_kinds(server_name) abort
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server_name)
|
||||
if !empty(l:capabilities) && has_key(l:capabilities, 'codeActionProvider')
|
||||
if type(l:capabilities['codeActionProvider']) == type({})
|
||||
if has_key(l:capabilities['codeActionProvider'], 'codeActionKinds') && type(l:capabilities['codeActionProvider']['codeActionKinds']) == type([])
|
||||
return l:capabilities['codeActionProvider']['codeActionKinds']
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
return []
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_completion_resolve_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'completionProvider', 'resolveProvider')
|
||||
endfunction
|
||||
|
||||
function! lsp#capabilities#has_inlay_hint_provider(server_name) abort
|
||||
return s:has_provider(a:server_name, 'inlayHintProvider')
|
||||
endfunction
|
||||
465
dot_vim/plugged/vim-lsp/autoload/lsp/client.vim
Normal file
465
dot_vim/plugged/vim-lsp/autoload/lsp/client.vim
Normal file
@@ -0,0 +1,465 @@
|
||||
let s:save_cpo = &cpoptions
|
||||
set cpoptions&vim
|
||||
|
||||
let s:clients = {} " { client_id: ctx }
|
||||
|
||||
" Vars used by native lsp
|
||||
let s:jobidseq = 0
|
||||
|
||||
function! s:create_context(client_id, opts) abort
|
||||
if a:client_id <= 0
|
||||
return {}
|
||||
endif
|
||||
|
||||
let l:ctx = {
|
||||
\ 'opts': a:opts,
|
||||
\ 'buffer': '',
|
||||
\ 'content-length': -1,
|
||||
\ 'requests': {},
|
||||
\ 'request_sequence': 0,
|
||||
\ 'on_notifications': {},
|
||||
\ }
|
||||
|
||||
let s:clients[a:client_id] = l:ctx
|
||||
|
||||
return l:ctx
|
||||
endfunction
|
||||
|
||||
function! s:dispose_context(client_id) abort
|
||||
if a:client_id > 0
|
||||
if has_key(s:clients, a:client_id)
|
||||
unlet s:clients[a:client_id]
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_stdout(id, data, event) abort
|
||||
let l:ctx = get(s:clients, a:id, {})
|
||||
|
||||
if empty(l:ctx)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:ctx['buffer'] .= a:data
|
||||
|
||||
while 1
|
||||
if l:ctx['content-length'] < 0
|
||||
" wait for all headers to arrive
|
||||
let l:header_end_index = stridx(l:ctx['buffer'], "\r\n\r\n")
|
||||
if l:header_end_index < 0
|
||||
" no headers found
|
||||
return
|
||||
endif
|
||||
let l:headers = l:ctx['buffer'][:l:header_end_index - 1]
|
||||
let l:ctx['content-length'] = s:get_content_length(l:headers)
|
||||
if l:ctx['content-length'] < 0
|
||||
" invalid content-length
|
||||
call lsp#log('on_stdout', a:id, 'invalid content-length')
|
||||
call s:lsp_stop(a:id)
|
||||
return
|
||||
endif
|
||||
let l:ctx['buffer'] = l:ctx['buffer'][l:header_end_index + 4:] " 4 = len(\r\n\r\n)
|
||||
endif
|
||||
|
||||
if len(l:ctx['buffer']) < l:ctx['content-length']
|
||||
" incomplete message, wait for next buffer to arrive
|
||||
return
|
||||
endif
|
||||
|
||||
" we have full message
|
||||
let l:response_str = l:ctx['buffer'][:l:ctx['content-length'] - 1]
|
||||
let l:ctx['content-length'] = -1
|
||||
|
||||
try
|
||||
let l:response = json_decode(l:response_str)
|
||||
catch
|
||||
call lsp#log('s:on_stdout json_decode failed', v:exception)
|
||||
endtry
|
||||
|
||||
let l:ctx['buffer'] = l:ctx['buffer'][len(l:response_str):]
|
||||
|
||||
if exists('l:response')
|
||||
" call appropriate callbacks
|
||||
let l:on_notification_data = { 'response': l:response }
|
||||
if has_key(l:response, 'method') && has_key(l:response, 'id')
|
||||
" it is a request from a server
|
||||
let l:request = l:response
|
||||
if has_key(l:ctx['opts'], 'on_request')
|
||||
call l:ctx['opts']['on_request'](a:id, l:request)
|
||||
endif
|
||||
elseif has_key(l:response, 'id')
|
||||
" it is a request->response
|
||||
if !(type(l:response['id']) == type(0) || type(l:response['id']) == type(''))
|
||||
" response['id'] can be number | string | null based on the spec
|
||||
call lsp#log('invalid response id. ignoring message', l:response)
|
||||
continue
|
||||
endif
|
||||
if has_key(l:ctx['requests'], l:response['id'])
|
||||
let l:on_notification_data['request'] = l:ctx['requests'][l:response['id']]
|
||||
endif
|
||||
if has_key(l:ctx['opts'], 'on_notification')
|
||||
" call client's on_notification first
|
||||
try
|
||||
call l:ctx['opts']['on_notification'](a:id, l:on_notification_data, 'on_notification')
|
||||
catch
|
||||
call lsp#log('s:on_stdout client option on_notification() error', v:exception, v:throwpoint)
|
||||
endtry
|
||||
endif
|
||||
if has_key(l:ctx['on_notifications'], l:response['id'])
|
||||
" call lsp#client#send({ 'on_notification }) second
|
||||
try
|
||||
call l:ctx['on_notifications'][l:response['id']](a:id, l:on_notification_data, 'on_notification')
|
||||
catch
|
||||
call lsp#log('s:on_stdout client request on_notification() error', v:exception, v:throwpoint)
|
||||
endtry
|
||||
unlet l:ctx['on_notifications'][l:response['id']]
|
||||
endif
|
||||
if has_key(l:ctx['requests'], l:response['id'])
|
||||
unlet l:ctx['requests'][l:response['id']]
|
||||
else
|
||||
call lsp#log('cannot find the request corresponding to response: ', l:response)
|
||||
endif
|
||||
else
|
||||
" it is a notification
|
||||
if has_key(l:ctx['opts'], 'on_notification')
|
||||
try
|
||||
call l:ctx['opts']['on_notification'](a:id, l:on_notification_data, 'on_notification')
|
||||
catch
|
||||
call lsp#log('s:on_stdout on_notification() error', v:exception, v:throwpoint)
|
||||
endtry
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
if empty(l:response_str)
|
||||
" buffer is empty, wait for next message to arrive
|
||||
return
|
||||
endif
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
function! s:get_content_length(headers) abort
|
||||
for l:header in split(a:headers, "\r\n")
|
||||
let l:kvp = split(l:header, ':')
|
||||
if len(l:kvp) == 2
|
||||
if l:kvp[0] =~? '^Content-Length'
|
||||
return str2nr(l:kvp[1], 10)
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
return -1
|
||||
endfunction
|
||||
|
||||
function! s:on_stderr(id, data, event) abort
|
||||
let l:ctx = get(s:clients, a:id, {})
|
||||
if empty(l:ctx)
|
||||
return
|
||||
endif
|
||||
if has_key(l:ctx['opts'], 'on_stderr')
|
||||
try
|
||||
call l:ctx['opts']['on_stderr'](a:id, a:data, a:event)
|
||||
catch
|
||||
call lsp#log('s:on_stderr exception', v:exception, v:throwpoint)
|
||||
echom v:exception
|
||||
endtry
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_exit(id, status, event) abort
|
||||
let l:ctx = get(s:clients, a:id, {})
|
||||
if empty(l:ctx)
|
||||
return
|
||||
endif
|
||||
if has_key(l:ctx['opts'], 'on_exit')
|
||||
try
|
||||
call l:ctx['opts']['on_exit'](a:id, a:status, a:event)
|
||||
catch
|
||||
call lsp#log('s:on_exit exception', v:exception, v:throwpoint)
|
||||
echom v:exception
|
||||
endtry
|
||||
endif
|
||||
call s:dispose_context(a:id)
|
||||
endfunction
|
||||
|
||||
function! s:lsp_start(opts) abort
|
||||
let l:opts = {
|
||||
\ 'on_stdout': function('s:on_stdout'),
|
||||
\ 'on_stderr': function('s:on_stderr'),
|
||||
\ 'on_exit': function('s:on_exit'),
|
||||
\ 'normalize': 'string'
|
||||
\ }
|
||||
if has_key(a:opts, 'env')
|
||||
let l:opts.env = a:opts.env
|
||||
endif
|
||||
|
||||
if has_key(a:opts, 'cmd')
|
||||
let l:client_id = lsp#utils#job#start(a:opts.cmd, l:opts)
|
||||
elseif has_key(a:opts, 'tcp')
|
||||
let l:client_id = lsp#utils#job#connect(a:opts.tcp, l:opts)
|
||||
else
|
||||
return -1
|
||||
endif
|
||||
|
||||
let l:ctx = s:create_context(l:client_id, a:opts)
|
||||
let l:ctx['id'] = l:client_id
|
||||
|
||||
return l:client_id
|
||||
endfunction
|
||||
|
||||
function! s:lsp_stop(id) abort
|
||||
call lsp#utils#job#stop(a:id)
|
||||
endfunction
|
||||
|
||||
let s:send_type_request = 1
|
||||
let s:send_type_notification = 2
|
||||
let s:send_type_response = 3
|
||||
function! s:lsp_send(id, opts, type) abort " opts = { id?, method?, result?, params?, on_notification }
|
||||
let l:ctx = get(s:clients, a:id, {})
|
||||
if empty(l:ctx) | return -1 | endif
|
||||
|
||||
let l:request = { 'jsonrpc': '2.0' }
|
||||
|
||||
if (a:type == s:send_type_request)
|
||||
let l:ctx['request_sequence'] = l:ctx['request_sequence'] + 1
|
||||
let l:request['id'] = l:ctx['request_sequence']
|
||||
let l:ctx['requests'][l:request['id']] = l:request
|
||||
if has_key(a:opts, 'on_notification')
|
||||
let l:ctx['on_notifications'][l:request['id']] = a:opts['on_notification']
|
||||
endif
|
||||
endif
|
||||
|
||||
if has_key(a:opts, 'id')
|
||||
let l:request['id'] = a:opts['id']
|
||||
endif
|
||||
if has_key(a:opts, 'method')
|
||||
let l:request['method'] = a:opts['method']
|
||||
endif
|
||||
if has_key(a:opts, 'params')
|
||||
let l:request['params'] = a:opts['params']
|
||||
endif
|
||||
if has_key(a:opts, 'result')
|
||||
let l:request['result'] = a:opts['result']
|
||||
endif
|
||||
if has_key(a:opts, 'error')
|
||||
let l:request['error'] = a:opts['error']
|
||||
endif
|
||||
|
||||
let l:json = json_encode(l:request)
|
||||
let l:payload = 'Content-Length: ' . len(l:json) . "\r\n\r\n" . l:json
|
||||
|
||||
call lsp#utils#job#send(a:id, l:payload)
|
||||
|
||||
if (a:type == s:send_type_request)
|
||||
let l:id = l:request['id']
|
||||
if get(a:opts, 'sync', 0) !=# 0
|
||||
let l:timeout = get(a:opts, 'sync_timeout', -1)
|
||||
if lsp#utils#_wait(l:timeout, {-> !has_key(l:ctx['requests'], l:request['id'])}, 1) == -1
|
||||
throw 'lsp#client: timeout'
|
||||
endif
|
||||
endif
|
||||
return l:id
|
||||
else
|
||||
return 0
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:lsp_get_last_request_id(id) abort
|
||||
return s:clients[a:id]['request_sequence']
|
||||
endfunction
|
||||
|
||||
function! s:lsp_is_error(obj_or_response) abort
|
||||
let l:vt = type(a:obj_or_response)
|
||||
if l:vt == type('')
|
||||
return len(a:obj_or_response) > 0
|
||||
elseif l:vt == type({})
|
||||
return has_key(a:obj_or_response, 'error')
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:is_server_instantiated_notification(notification) abort
|
||||
return !has_key(a:notification, 'request')
|
||||
endfunction
|
||||
|
||||
function! s:native_out_cb(cbctx, channel, response) abort
|
||||
if !has_key(a:cbctx, 'ctx') | return | endif
|
||||
let l:ctx = a:cbctx['ctx']
|
||||
if has_key(a:response, 'method') && has_key(a:response, 'id')
|
||||
" it is a request from a server
|
||||
let l:request = a:response
|
||||
if has_key(l:ctx['opts'], 'on_request')
|
||||
call l:ctx['opts']['on_request'](l:ctx['id'], l:request)
|
||||
endif
|
||||
elseif !has_key(a:response, 'id') && has_key(l:ctx['opts'], 'on_notification')
|
||||
" it is a notification
|
||||
let l:on_notification_data = { 'response': a:response }
|
||||
try
|
||||
call l:ctx['opts']['on_notification'](l:ctx['id'], l:on_notification_data, 'on_notification')
|
||||
catch
|
||||
call lsp#log('s:native_notification_callback on_notification() error', v:exception, v:throwpoint)
|
||||
endtry
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:native_err_cb(cbctx, channel, response) abort
|
||||
if !has_key(a:cbctx, 'ctx') | return | endif
|
||||
let l:ctx = a:cbctx['ctx']
|
||||
if has_key(l:ctx['opts'], 'on_stderr')
|
||||
try
|
||||
call l:ctx['opts']['on_stderr'](l:ctx['id'], a:response, 'stderr')
|
||||
catch
|
||||
call lsp#log('s:on_stderr exception', v:exception, v:throwpoint)
|
||||
echom v:exception
|
||||
endtry
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" public apis {{{
|
||||
|
||||
function! lsp#client#start(opts) abort
|
||||
if g:lsp_use_native_client && lsp#utils#has_native_lsp_client()
|
||||
if has_key(a:opts, 'cmd')
|
||||
let l:cbctx = {}
|
||||
let l:jobopt = { 'in_mode': 'lsp', 'out_mode': 'lsp', 'noblock': 1,
|
||||
\ 'out_cb': function('s:native_out_cb', [l:cbctx]),
|
||||
\ 'err_cb': function('s:native_err_cb', [l:cbctx]),
|
||||
\ }
|
||||
if has_key(a:opts, 'cwd') | let l:jobopt['cwd'] = a:opts['cwd'] | endif
|
||||
if has_key(a:opts, 'env') | let l:jobopt['env'] = a:opts['env'] | endif
|
||||
let s:jobidseq += 1
|
||||
let l:jobid = s:jobidseq " jobid == clientid
|
||||
call lsp#log_verbose('using native lsp client')
|
||||
let l:job = job_start(a:opts['cmd'], l:jobopt)
|
||||
if job_status(l:job) !=? 'run' | return -1 | endif
|
||||
let l:ctx = s:create_context(l:jobid, a:opts)
|
||||
let l:ctx['id'] = l:jobid
|
||||
let l:ctx['job'] = l:job
|
||||
let l:ctx['channel'] = job_getchannel(l:job)
|
||||
let l:cbctx['ctx'] = l:ctx
|
||||
return l:jobid
|
||||
elseif has_key(a:opts, 'tcp')
|
||||
" add support for tcp
|
||||
call lsp#log('tcp not supported when using native lsp client')
|
||||
return -1
|
||||
endif
|
||||
endif
|
||||
return s:lsp_start(a:opts)
|
||||
endfunction
|
||||
|
||||
function! lsp#client#stop(client_id) abort
|
||||
if g:lsp_use_native_client && lsp#utils#has_native_lsp_client()
|
||||
let l:ctx = get(s:clients, a:client_id, {})
|
||||
if empty(l:ctx) | return | endif
|
||||
call job_stop(l:ctx['job'])
|
||||
else
|
||||
return s:lsp_stop(a:client_id)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#client#send_request(client_id, opts) abort
|
||||
if g:lsp_use_native_client && lsp#utils#has_native_lsp_client()
|
||||
let l:ctx = get(s:clients, a:client_id, {})
|
||||
if empty(l:ctx) | return -1 | endif
|
||||
let l:request = {}
|
||||
" id shouldn't be passed to request as vim will overwrite it. refer to :h language-server-protocol
|
||||
if has_key(a:opts, 'method') | let l:request['method'] = a:opts['method'] | endif
|
||||
if has_key(a:opts, 'params') | let l:request['params'] = a:opts['params'] | endif
|
||||
|
||||
call ch_sendexpr(l:ctx['channel'], l:request, { 'callback': function('s:on_response_native', [l:ctx, l:request]) })
|
||||
let l:ctx['requests'][l:request['id']] = l:request
|
||||
if has_key(a:opts, 'on_notification')
|
||||
let l:ctx['on_notifications'][l:request['id']] = a:opts['on_notification']
|
||||
endif
|
||||
let l:ctx['request_sequence'] = l:request['id']
|
||||
return l:request['id']
|
||||
else
|
||||
return s:lsp_send(a:client_id, a:opts, s:send_type_request)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_response_native(ctx, request, channel, response) abort
|
||||
" request -> response
|
||||
let l:on_notification_data = { 'response': a:response, 'request': a:request }
|
||||
if has_key(a:ctx['opts'], 'on_notification')
|
||||
" call client's on_notification first
|
||||
try
|
||||
call a:ctx['opts']['on_notification'](a:ctx['id'], l:on_notification_data, 'on_notification')
|
||||
catch
|
||||
call lsp#log('s:on_response_native client option on_notification() error', v:exception, v:throwpoint)
|
||||
endtry
|
||||
endif
|
||||
if has_key(a:ctx['on_notifications'], a:request['id'])
|
||||
" call lsp#client#send({ 'on_notification' }) second
|
||||
try
|
||||
call a:ctx['on_notifications'][a:request['id']](a:ctx['id'], l:on_notification_data, 'on_notification')
|
||||
catch
|
||||
call lsp#log('s:on_response_native client request on_notification() error', v:exception, v:throwpoint, a:request, a:response)
|
||||
endtry
|
||||
unlet a:ctx['on_notifications'][a:response['id']]
|
||||
if has_key(a:ctx['requests'], a:response['id'])
|
||||
unlet a:ctx['requests'][a:response['id']]
|
||||
else
|
||||
call lsp#log('cannot find the request corresponding to response: ', a:response)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#client#send_notification(client_id, opts) abort
|
||||
if g:lsp_use_native_client && lsp#utils#has_native_lsp_client()
|
||||
let l:ctx = get(s:clients, a:client_id, {})
|
||||
if empty(l:ctx) | return -1 | endif
|
||||
let l:request = {}
|
||||
if has_key(a:opts, 'method') | let l:request['method'] = a:opts['method'] | endif
|
||||
if has_key(a:opts, 'params') | let l:request['params'] = a:opts['params'] | endif
|
||||
call ch_sendexpr(l:ctx['channel'], l:request)
|
||||
return 0
|
||||
else
|
||||
return s:lsp_send(a:client_id, a:opts, s:send_type_notification)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#client#send_response(client_id, opts) abort
|
||||
if g:lsp_use_native_client && lsp#utils#has_native_lsp_client()
|
||||
let l:ctx = get(s:clients, a:client_id, {})
|
||||
if empty(l:ctx) | return -1 | endif
|
||||
let l:request = {}
|
||||
if has_key(a:opts, 'id') | let l:request['id'] = a:opts['id'] | endif
|
||||
if has_key(a:opts, 'result') | let l:request['result'] = a:opts['result'] | endif
|
||||
if has_key(a:opts, 'error') | let l:request['error'] = a:opts['error'] | endif
|
||||
call ch_sendexpr(l:ctx['channel'], l:request)
|
||||
return 0
|
||||
else
|
||||
return s:lsp_send(a:client_id, a:opts, s:send_type_response)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#client#get_last_request_id(client_id) abort
|
||||
return s:lsp_get_last_request_id(a:client_id)
|
||||
endfunction
|
||||
|
||||
function! lsp#client#is_error(obj_or_response) abort
|
||||
return s:lsp_is_error(a:obj_or_response)
|
||||
endfunction
|
||||
|
||||
function! lsp#client#error_message(obj_or_response) abort
|
||||
try
|
||||
return a:obj_or_response['error']['data']['message']
|
||||
catch
|
||||
endtry
|
||||
try
|
||||
return a:obj_or_response['error']['message']
|
||||
catch
|
||||
endtry
|
||||
return string(a:obj_or_response)
|
||||
endfunction
|
||||
|
||||
function! lsp#client#is_server_instantiated_notification(notification) abort
|
||||
return s:is_server_instantiated_notification(a:notification)
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
let &cpoptions = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
" vim sw=4 ts=4 et
|
||||
@@ -0,0 +1,207 @@
|
||||
" https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
|
||||
let s:enabled = 0
|
||||
|
||||
let s:Markdown = vital#lsp#import('VS.Vim.Syntax.Markdown')
|
||||
let s:MarkupContent = vital#lsp#import('VS.LSP.MarkupContent')
|
||||
let s:FloatingWindow = vital#lsp#import('VS.Vim.Window.FloatingWindow')
|
||||
let s:Window = vital#lsp#import('VS.Vim.Window')
|
||||
let s:Buffer = vital#lsp#import('VS.Vim.Buffer')
|
||||
|
||||
function! lsp#internal#completion#documentation#_enable() abort
|
||||
" don't even bother registering if the feature is disabled
|
||||
if !g:lsp_completion_documentation_enabled | return | endif
|
||||
|
||||
if !s:FloatingWindow.is_available() | return | endif
|
||||
if !exists('##CompleteChanged') | return | endif
|
||||
|
||||
if s:enabled | return | endif
|
||||
let s:enabled = 1
|
||||
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#merge(
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent('CompleteChanged'),
|
||||
\ lsp#callbag#filter({_->g:lsp_completion_documentation_enabled}),
|
||||
\ lsp#callbag#map({->copy(v:event)}),
|
||||
\ lsp#callbag#debounceTime(g:lsp_completion_documentation_delay),
|
||||
\ lsp#callbag#switchMap({event->
|
||||
\ lsp#callbag#pipe(
|
||||
\ s:resolve_completion(event),
|
||||
\ lsp#callbag#tap({managed_user_data->s:show_floating_window(event, managed_user_data)}),
|
||||
\ lsp#callbag#takeUntil(lsp#callbag#fromEvent('CompleteDone'))
|
||||
\ )
|
||||
\ })
|
||||
\ ),
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent('CompleteDone'),
|
||||
\ lsp#callbag#tap({_->s:close_floating_window(v:false)}),
|
||||
\ )
|
||||
\ ),
|
||||
\ lsp#callbag#subscribe(),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! s:resolve_completion(event) abort
|
||||
let l:managed_user_data = lsp#omni#get_managed_user_data_from_completed_item(a:event['completed_item'])
|
||||
if empty(l:managed_user_data)
|
||||
return lsp#callbag#of({})
|
||||
endif
|
||||
|
||||
let l:completion_item = l:managed_user_data['completion_item']
|
||||
|
||||
if has_key(l:completion_item, 'documentation')
|
||||
return lsp#callbag#of(l:managed_user_data)
|
||||
elseif lsp#capabilities#has_completion_resolve_provider(l:managed_user_data['server_name'])
|
||||
return lsp#callbag#pipe(
|
||||
\ lsp#request(l:managed_user_data['server_name'], {
|
||||
\ 'method': 'completionItem/resolve',
|
||||
\ 'params': l:completion_item,
|
||||
\ }),
|
||||
\ lsp#callbag#map({x->{
|
||||
\ 'server_name': l:managed_user_data['server_name'],
|
||||
\ 'completion_item': x['response']['result'],
|
||||
\ 'complete_position': l:managed_user_data['complete_position'],
|
||||
\ }})
|
||||
\ )
|
||||
else
|
||||
return lsp#callbag#of(l:managed_user_data)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:show_floating_window(event, managed_user_data) abort
|
||||
if empty(a:managed_user_data) || !pumvisible()
|
||||
call s:close_floating_window(v:true)
|
||||
return
|
||||
endif
|
||||
let l:completion_item = a:managed_user_data['completion_item']
|
||||
|
||||
let l:contents = []
|
||||
|
||||
" Add detail field if provided.
|
||||
if type(get(l:completion_item, 'detail', v:null)) == type('')
|
||||
if !empty(l:completion_item.detail)
|
||||
let l:detail = s:MarkupContent.normalize({
|
||||
\ 'language': &filetype,
|
||||
\ 'value': l:completion_item['detail'],
|
||||
\ }, {
|
||||
\ 'compact': !g:lsp_preview_fixup_conceal
|
||||
\ })
|
||||
let l:contents += [l:detail]
|
||||
endif
|
||||
endif
|
||||
|
||||
" Add documentation filed if provided.
|
||||
let l:documentation = s:MarkupContent.normalize(get(l:completion_item, 'documentation', ''), {
|
||||
\ 'compact': !g:lsp_preview_fixup_conceal
|
||||
\ })
|
||||
if !empty(l:documentation)
|
||||
let l:contents += [l:documentation]
|
||||
endif
|
||||
|
||||
" Ignore if contents is empty.
|
||||
if empty(l:contents)
|
||||
return s:close_floating_window(v:true)
|
||||
endif
|
||||
|
||||
" Update contents.
|
||||
let l:doc_win = s:get_doc_win()
|
||||
call deletebufline(l:doc_win.get_bufnr(), 1, '$')
|
||||
call setbufline(l:doc_win.get_bufnr(), 1, lsp#utils#_split_by_eol(join(l:contents, "\n\n")))
|
||||
|
||||
" Calculate layout.
|
||||
if g:lsp_float_max_width >= 1
|
||||
let l:maxwidth = g:lsp_float_max_width
|
||||
elseif g:lsp_float_max_width == 0
|
||||
let l:maxwidth = &columns
|
||||
else
|
||||
let l:maxwidth = float2nr(&columns * 0.4)
|
||||
endif
|
||||
let l:size = l:doc_win.get_size({
|
||||
\ 'maxwidth': l:maxwidth,
|
||||
\ 'maxheight': float2nr(&lines * 0.4),
|
||||
\ })
|
||||
let l:margin_right = &columns - 1 - (float2nr(a:event.col) + float2nr(a:event.width) + 1 + (a:event.scrollbar ? 1 : 0))
|
||||
let l:margin_left = float2nr(a:event.col) - 3
|
||||
if l:size.width < l:margin_right
|
||||
" do nothing
|
||||
elseif l:margin_left <= l:margin_right
|
||||
let l:size.width = l:margin_right
|
||||
else
|
||||
let l:size.width = l:margin_left
|
||||
endif
|
||||
let l:pos = s:compute_position(a:event, l:size)
|
||||
if empty(l:pos)
|
||||
call s:close_floating_window(v:true)
|
||||
return
|
||||
endif
|
||||
|
||||
" Show popupmenu and apply markdown syntax.
|
||||
call l:doc_win.open({
|
||||
\ 'row': l:pos[0] + 1,
|
||||
\ 'col': l:pos[1] + 1,
|
||||
\ 'width': l:size.width,
|
||||
\ 'height': l:size.height,
|
||||
\ 'border': v:true,
|
||||
\ 'topline': 1,
|
||||
\ })
|
||||
call s:Window.do(l:doc_win.get_winid(), { -> s:Markdown.apply() })
|
||||
endfunction
|
||||
|
||||
function! s:close_floating_window(force) abort
|
||||
" Ignore `CompleteDone` if it occurred by `complete()` because in this case, the popup menu will re-appear immediately.
|
||||
let l:ctx = {}
|
||||
function! l:ctx.callback(force) abort
|
||||
if !pumvisible() || a:force
|
||||
call s:get_doc_win().close()
|
||||
endif
|
||||
endfunction
|
||||
call timer_start(1, { -> l:ctx.callback(a:force) })
|
||||
endfunction
|
||||
|
||||
function! s:compute_position(event, size) abort
|
||||
let l:col_if_right = a:event.col + a:event.width + 1 + (a:event.scrollbar ? 1 : 0)
|
||||
let l:col_if_left = a:event.col - a:size.width - 2
|
||||
|
||||
if a:size.width >= (&columns - l:col_if_right)
|
||||
let l:col = l:col_if_left
|
||||
else
|
||||
let l:col = l:col_if_right
|
||||
endif
|
||||
|
||||
if l:col <= 0
|
||||
return []
|
||||
endif
|
||||
if &columns <= l:col + a:size.width
|
||||
return []
|
||||
endif
|
||||
|
||||
return [a:event.row, l:col]
|
||||
endfunction
|
||||
|
||||
function! s:get_doc_win() abort
|
||||
if exists('s:doc_win')
|
||||
return s:doc_win
|
||||
endif
|
||||
|
||||
let s:doc_win = s:FloatingWindow.new({
|
||||
\ 'on_opened': { -> execute('doautocmd <nomodeline> User lsp_float_opened') },
|
||||
\ 'on_closed': { -> execute('doautocmd <nomodeline> User lsp_float_closed') }
|
||||
\ })
|
||||
call s:doc_win.set_var('&wrap', 1)
|
||||
call s:doc_win.set_var('&conceallevel', 2)
|
||||
noautocmd silent let l:bufnr = s:Buffer.create()
|
||||
call s:doc_win.set_bufnr(l:bufnr)
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&buftype', 'nofile')
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&bufhidden', 'hide')
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&buflisted', 0)
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&swapfile', 0)
|
||||
return s:doc_win
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#completion#documentation#_disable() abort
|
||||
if !s:enabled | return | endif
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
@@ -0,0 +1,20 @@
|
||||
function! lsp#internal#diagnostics#_enable() abort
|
||||
" don't even bother registering if the feature is disabled
|
||||
if !g:lsp_diagnostics_enabled | return | endif
|
||||
|
||||
call lsp#internal#diagnostics#state#_enable() " Needs to be the first one to register
|
||||
call lsp#internal#diagnostics#echo#_enable()
|
||||
call lsp#internal#diagnostics#highlights#_enable()
|
||||
call lsp#internal#diagnostics#float#_enable()
|
||||
call lsp#internal#diagnostics#signs#_enable()
|
||||
call lsp#internal#diagnostics#virtual_text#_enable()
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#_disable() abort
|
||||
call lsp#internal#diagnostics#echo#_disable()
|
||||
call lsp#internal#diagnostics#float#_disable()
|
||||
call lsp#internal#diagnostics#highlights#_disable()
|
||||
call lsp#internal#diagnostics#virtual_text#_disable()
|
||||
call lsp#internal#diagnostics#signs#_disable()
|
||||
call lsp#internal#diagnostics#state#_disable() " Needs to be the last one to unregister
|
||||
endfunction
|
||||
@@ -0,0 +1,40 @@
|
||||
" options = {
|
||||
" buffers: '1' " optional string, defaults to current buffer, '*' for all buffers
|
||||
" }
|
||||
function! lsp#internal#diagnostics#document_diagnostics_command#do(options) abort
|
||||
if !g:lsp_diagnostics_enabled
|
||||
call lsp#utils#error(':LspDocumentDiagnostics g:lsp_diagnostics_enabled must be enabled')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:buffers = get(a:options, 'buffers', '')
|
||||
|
||||
let l:filtered_diagnostics = {}
|
||||
|
||||
if l:buffers ==# '*'
|
||||
let l:filtered_diagnostics = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_uri_and_server()
|
||||
else
|
||||
let l:uri = lsp#utils#get_buffer_uri()
|
||||
if !empty(l:uri)
|
||||
let l:filtered_diagnostics[l:uri] = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri)
|
||||
endif
|
||||
endif
|
||||
|
||||
let l:result = []
|
||||
for [l:uri, l:value] in items(l:filtered_diagnostics)
|
||||
if lsp#internal#diagnostics#state#_is_enabled_for_buffer(bufnr(lsp#utils#uri_to_path(l:uri)))
|
||||
for l:diagnostics in values(l:value)
|
||||
let l:result += lsp#ui#vim#utils#diagnostics_to_loc_list({ 'response': l:diagnostics })
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
|
||||
if empty(l:result)
|
||||
call lsp#utils#error('No diagnostics results')
|
||||
return
|
||||
else
|
||||
call setloclist(0, l:result)
|
||||
echo 'Retrieved diagnostics results'
|
||||
botright lopen
|
||||
endif
|
||||
endfunction
|
||||
@@ -0,0 +1,41 @@
|
||||
" internal state for whether it is enabled or not to avoid multiple subscriptions
|
||||
let s:enabled = 0
|
||||
|
||||
function! lsp#internal#diagnostics#echo#_enable() abort
|
||||
" don't even bother registering if the feature is disabled
|
||||
if !g:lsp_diagnostics_echo_cursor | return | endif
|
||||
|
||||
if s:enabled | return | endif
|
||||
let s:enabled = 1
|
||||
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent(['CursorMoved']),
|
||||
\ lsp#callbag#filter({_->g:lsp_diagnostics_echo_cursor}),
|
||||
\ lsp#callbag#debounceTime(g:lsp_diagnostics_echo_delay),
|
||||
\ lsp#callbag#map({_->{'bufnr': bufnr('%'), 'curpos': getcurpos()[0:2], 'changedtick': b:changedtick }}),
|
||||
\ lsp#callbag#distinctUntilChanged({a,b -> a['bufnr'] == b['bufnr'] && a['curpos'] == b['curpos'] && a['changedtick'] == b['changedtick']}),
|
||||
\ lsp#callbag#filter({_->mode() is# 'n'}),
|
||||
\ lsp#callbag#filter({_->getbufvar(bufnr('%'), '&buftype') !=# 'terminal' }),
|
||||
\ lsp#callbag#map({_->lsp#internal#diagnostics#under_cursor#get_diagnostic()}),
|
||||
\ lsp#callbag#subscribe({x->s:echo(x)}),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#echo#_disable() abort
|
||||
if !s:enabled | return | endif
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
let s:enabled = 0
|
||||
endfunction
|
||||
|
||||
function! s:echo(diagnostic) abort
|
||||
if !empty(a:diagnostic) && has_key(a:diagnostic, 'message')
|
||||
call lsp#utils#echo_with_truncation('LSP: '. substitute(a:diagnostic['message'], '\n\+', ' ', 'g'))
|
||||
let s:displaying_message = 1
|
||||
elseif get(s:, 'displaying_message', 0)
|
||||
call lsp#utils#echo_with_truncation('')
|
||||
let s:displaying_message = 0
|
||||
endif
|
||||
endfunction
|
||||
@@ -0,0 +1,26 @@
|
||||
" Return first error line or v:null if there are no errors
|
||||
" available.
|
||||
" options = {
|
||||
" 'bufnr': '', " optional
|
||||
" }
|
||||
function! lsp#internal#diagnostics#first_line#get_first_error_line(options) abort
|
||||
let l:bufnr = get(a:options, 'bufnr', bufnr('%'))
|
||||
|
||||
if !lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr)
|
||||
return v:null
|
||||
endif
|
||||
|
||||
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
|
||||
let l:diagnostics_by_server = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri)
|
||||
|
||||
let l:first_error_line = v:null
|
||||
for l:diagnostics_response in values(l:diagnostics_by_server)
|
||||
for l:item in lsp#utils#iteratable(l:diagnostics_response['params']['diagnostics'])
|
||||
let l:severity = get(l:item, 'severity', 1)
|
||||
if l:severity ==# 1 && (l:first_error_line ==# v:null || l:first_error_line ># l:item['range']['start']['line'])
|
||||
let l:first_error_line = l:item['range']['start']['line']
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
return l:first_error_line ==# v:null ? v:null : l:first_error_line + 1
|
||||
endfunction
|
||||
@@ -0,0 +1,123 @@
|
||||
" internal state for whether it is enabled or not to avoid multiple subscriptions
|
||||
let s:enabled = 0
|
||||
|
||||
let s:Markdown = vital#lsp#import('VS.Vim.Syntax.Markdown')
|
||||
let s:MarkupContent = vital#lsp#import('VS.LSP.MarkupContent')
|
||||
let s:FloatingWindow = vital#lsp#import('VS.Vim.Window.FloatingWindow')
|
||||
let s:Window = vital#lsp#import('VS.Vim.Window')
|
||||
let s:Buffer = vital#lsp#import('VS.Vim.Buffer')
|
||||
|
||||
function! lsp#internal#diagnostics#float#_enable() abort
|
||||
" don't even bother registering if the feature is disabled
|
||||
if !lsp#ui#vim#output#float_supported() | return | endif
|
||||
if !g:lsp_diagnostics_float_cursor | return | endif
|
||||
|
||||
if s:enabled | return | endif
|
||||
let s:enabled = 1
|
||||
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#merge(
|
||||
\ lsp#callbag#fromEvent(['CursorMoved', 'CursorHold']),
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent(['InsertEnter']),
|
||||
\ lsp#callbag#filter({_->!g:lsp_diagnostics_float_insert_mode_enabled}),
|
||||
\ lsp#callbag#tap({_->s:hide_float()}),
|
||||
\ )
|
||||
\ ),
|
||||
\ lsp#callbag#filter({_->g:lsp_diagnostics_float_cursor}),
|
||||
\ lsp#callbag#tap({_->s:hide_float()}),
|
||||
\ lsp#callbag#debounceTime(g:lsp_diagnostics_float_delay),
|
||||
\ lsp#callbag#map({_->{'bufnr': bufnr('%'), 'curpos': getcurpos()[0:2], 'changedtick': b:changedtick }}),
|
||||
\ lsp#callbag#distinctUntilChanged({a,b -> a['bufnr'] == b['bufnr'] && a['curpos'] == b['curpos'] && a['changedtick'] == b['changedtick']}),
|
||||
\ lsp#callbag#filter({_->mode() is# 'n'}),
|
||||
\ lsp#callbag#filter({_->getbufvar(bufnr('%'), '&buftype') !=# 'terminal' }),
|
||||
\ lsp#callbag#map({_->lsp#internal#diagnostics#under_cursor#get_diagnostic()}),
|
||||
\ lsp#callbag#subscribe({x->s:show_float(x)}),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#float#_disable() abort
|
||||
if !s:enabled | return | endif
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
let s:enabled = 0
|
||||
endfunction
|
||||
|
||||
function! s:show_float(diagnostic) abort
|
||||
let l:doc_win = s:get_doc_win()
|
||||
if !empty(a:diagnostic) && has_key(a:diagnostic, 'message')
|
||||
" Update contents.
|
||||
call deletebufline(l:doc_win.get_bufnr(), 1, '$')
|
||||
call setbufline(l:doc_win.get_bufnr(), 1, lsp#utils#_split_by_eol(a:diagnostic['message']))
|
||||
|
||||
" Compute size.
|
||||
if g:lsp_float_max_width >= 1
|
||||
let l:maxwidth = g:lsp_float_max_width
|
||||
elseif g:lsp_float_max_width == 0
|
||||
let l:maxwidth = &columns
|
||||
else
|
||||
let l:maxwidth = float2nr(&columns * 0.4)
|
||||
endif
|
||||
let l:size = l:doc_win.get_size({
|
||||
\ 'maxwidth': l:maxwidth,
|
||||
\ 'maxheight': float2nr(&lines * 0.4),
|
||||
\ })
|
||||
|
||||
" Compute position.
|
||||
let l:pos = s:compute_position(l:size)
|
||||
|
||||
" Open window.
|
||||
call l:doc_win.open({
|
||||
\ 'row': l:pos[0],
|
||||
\ 'col': l:pos[1],
|
||||
\ 'width': l:size.width,
|
||||
\ 'height': l:size.height,
|
||||
\ 'border': v:true,
|
||||
\ 'topline': 1,
|
||||
\ })
|
||||
else
|
||||
call s:hide_float()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:hide_float() abort
|
||||
let l:doc_win = s:get_doc_win()
|
||||
call l:doc_win.close()
|
||||
endfunction
|
||||
|
||||
function! s:get_doc_win() abort
|
||||
if exists('s:doc_win')
|
||||
return s:doc_win
|
||||
endif
|
||||
|
||||
let s:doc_win = s:FloatingWindow.new({
|
||||
\ 'on_opened': { -> execute('doautocmd <nomodeline> User lsp_float_opened') },
|
||||
\ 'on_closed': { -> execute('doautocmd <nomodeline> User lsp_float_closed') }
|
||||
\ })
|
||||
call s:doc_win.set_var('&wrap', 1)
|
||||
call s:doc_win.set_var('&conceallevel', 2)
|
||||
noautocmd silent let l:bufnr = s:Buffer.create()
|
||||
call s:doc_win.set_bufnr(l:bufnr)
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&buftype', 'nofile')
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&bufhidden', 'hide')
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&buflisted', 0)
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&swapfile', 0)
|
||||
return s:doc_win
|
||||
endfunction
|
||||
|
||||
function! s:compute_position(size) abort
|
||||
let l:pos = screenpos(0, line('.'), col('.'))
|
||||
if l:pos.row == 0 && l:pos.col == 0
|
||||
let l:pos = {'curscol': screencol(), 'row': screenrow()}
|
||||
endif
|
||||
let l:pos = [l:pos.row + 1, l:pos.curscol + 1]
|
||||
if l:pos[0] + a:size.height > &lines
|
||||
let l:pos[0] = l:pos[0] - a:size.height - 3
|
||||
endif
|
||||
if l:pos[1] + a:size.width > &columns
|
||||
let l:pos[1] = l:pos[1] - a:size.width - 3
|
||||
endif
|
||||
return l:pos
|
||||
endfunction
|
||||
@@ -0,0 +1,207 @@
|
||||
" internal state for whether it is enabled or not to avoid multiple subscriptions
|
||||
let s:enabled = 0
|
||||
let s:namespace_id = '' " will be set when enabled
|
||||
|
||||
let s:severity_sign_names_mapping = {
|
||||
\ 1: 'LspError',
|
||||
\ 2: 'LspWarning',
|
||||
\ 3: 'LspInformation',
|
||||
\ 4: 'LspHint',
|
||||
\ }
|
||||
|
||||
if !hlexists('LspErrorHighlight')
|
||||
highlight link LspErrorHighlight Error
|
||||
endif
|
||||
|
||||
if !hlexists('LspWarningHighlight')
|
||||
highlight link LspWarningHighlight Todo
|
||||
endif
|
||||
|
||||
if !hlexists('LspInformationHighlight')
|
||||
highlight link LspInformationHighlight Normal
|
||||
endif
|
||||
|
||||
if !hlexists('LspHintHighlight')
|
||||
highlight link LspHintHighlight Normal
|
||||
endif
|
||||
|
||||
function! lsp#internal#diagnostics#highlights#_enable() abort
|
||||
" don't even bother registering if the feature is disabled
|
||||
if !lsp#utils#_has_highlights() | return | endif
|
||||
if !g:lsp_diagnostics_highlights_enabled | return | endif
|
||||
|
||||
if s:enabled | return | endif
|
||||
let s:enabled = 1
|
||||
|
||||
if empty(s:namespace_id)
|
||||
if has('nvim')
|
||||
let s:namespace_id = nvim_create_namespace('vim_lsp_diagnostics_highlights')
|
||||
else
|
||||
let s:namespace_id = 'vim_lsp_diagnostics_highlights'
|
||||
for l:severity in keys(s:severity_sign_names_mapping)
|
||||
let l:hl_group = s:severity_sign_names_mapping[l:severity] . 'Highlight'
|
||||
call prop_type_add(s:get_prop_type_name(l:severity),
|
||||
\ {'highlight': l:hl_group, 'combine': v:true, 'priority': lsp#internal#textprop#priority('diagnostics_highlight') })
|
||||
endfor
|
||||
endif
|
||||
endif
|
||||
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#merge(
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'server') && has_key(x, 'response')
|
||||
\ && has_key(x['response'], 'method') && x['response']['method'] ==# '$/vimlsp/lsp_diagnostics_updated'
|
||||
\ && !lsp#client#is_error(x['response'])}),
|
||||
\ lsp#callbag#map({x->x['response']['params']}),
|
||||
\ ),
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent(['InsertEnter', 'InsertLeave']),
|
||||
\ lsp#callbag#filter({_->!g:lsp_diagnostics_highlights_insert_mode_enabled}),
|
||||
\ lsp#callbag#map({_->{ 'uri': lsp#utils#get_buffer_uri() }}),
|
||||
\ ),
|
||||
\ ),
|
||||
\ lsp#callbag#filter({_->g:lsp_diagnostics_highlights_enabled}),
|
||||
\ lsp#callbag#debounceTime(g:lsp_diagnostics_highlights_delay),
|
||||
\ lsp#callbag#tap({x->s:clear_highlights(x)}),
|
||||
\ lsp#callbag#tap({x->s:set_highlights(x)}),
|
||||
\ lsp#callbag#subscribe(),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#highlights#_disable() abort
|
||||
if !s:enabled | return | endif
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
call s:clear_all_highlights()
|
||||
let s:enabled = 0
|
||||
endfunction
|
||||
|
||||
function! s:get_prop_type_name(severity) abort
|
||||
return s:namespace_id . '_' . get(s:severity_sign_names_mapping, a:severity, 'LspError')
|
||||
endfunction
|
||||
|
||||
function! s:clear_all_highlights() abort
|
||||
for l:bufnr in range(1, bufnr('$'))
|
||||
if bufexists(l:bufnr) && bufloaded(l:bufnr)
|
||||
if has('nvim')
|
||||
call nvim_buf_clear_namespace(l:bufnr, s:namespace_id, 0, -1)
|
||||
else
|
||||
for l:severity in keys(s:severity_sign_names_mapping)
|
||||
try
|
||||
" TODO: need to check for valid range before calling prop_add
|
||||
" See https://github.com/prabirshrestha/vim-lsp/pull/721
|
||||
silent! call prop_remove({
|
||||
\ 'type': s:get_prop_type_name(l:severity),
|
||||
\ 'bufnr': l:bufnr,
|
||||
\ 'all': v:true })
|
||||
catch
|
||||
call lsp#log('diagnostics', 'clear_all_highlights', 'prop_remove', v:exception, v:throwpoint)
|
||||
endtry
|
||||
endfor
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:clear_highlights(params) abort
|
||||
" TODO: optimize by looking at params
|
||||
call s:clear_all_highlights()
|
||||
endfunction
|
||||
|
||||
function! s:set_highlights(params) abort
|
||||
" TODO: optimize by looking at params
|
||||
if !g:lsp_diagnostics_highlights_insert_mode_enabled
|
||||
if mode()[0] ==# 'i' | return | endif
|
||||
endif
|
||||
|
||||
for l:bufnr in range(1, bufnr('$'))
|
||||
if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr) && bufexists(l:bufnr) && bufloaded(l:bufnr)
|
||||
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
|
||||
for [l:server, l:diagnostics_response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri))
|
||||
call s:place_highlights(l:server, l:diagnostics_response, l:bufnr)
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:place_highlights(server, diagnostics_response, bufnr) abort
|
||||
" TODO: make diagnostics highlights same across vim and neovim
|
||||
for l:item in lsp#utils#iteratable(a:diagnostics_response['params']['diagnostics'])
|
||||
let [l:start_line, l:start_col] = lsp#utils#position#lsp_to_vim(a:bufnr, l:item['range']['start'])
|
||||
let [l:end_line, l:end_col] = lsp#utils#position#lsp_to_vim(a:bufnr, l:item['range']['end'])
|
||||
let l:severity = get(l:item, 'severity', 3)
|
||||
let l:hl_group = get(s:severity_sign_names_mapping, l:severity, 'LspError') . 'Highlight'
|
||||
if has('nvim')
|
||||
for l:line in range(l:start_line, l:end_line)
|
||||
if l:line == l:start_line
|
||||
let l:highlight_start_col = l:start_col
|
||||
else
|
||||
let l:highlight_start_col = 1
|
||||
endif
|
||||
|
||||
if l:line == l:end_line
|
||||
let l:highlight_end_col = l:end_col
|
||||
else
|
||||
" neovim treats -1 as end of line, special handle it later
|
||||
" when calling nvim_buf_add_higlight
|
||||
let l:highlight_end_col = -1
|
||||
endif
|
||||
|
||||
if l:start_line == l:end_line && l:highlight_start_col == l:highlight_end_col
|
||||
" higlighting same start col and end col on same line
|
||||
" doesn't work so use -1 for start col
|
||||
let l:highlight_start_col -= 1
|
||||
if l:highlight_start_col <= 0
|
||||
let l:highlight_start_col = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
call nvim_buf_add_highlight(a:bufnr, s:namespace_id, l:hl_group,
|
||||
\ l:line - 1, l:highlight_start_col - 1, l:highlight_end_col == -1 ? -1 : l:highlight_end_col)
|
||||
endfor
|
||||
else
|
||||
if l:start_line == l:end_line
|
||||
try
|
||||
" TODO: need to check for valid range before calling prop_add
|
||||
" See https://github.com/prabirshrestha/vim-lsp/pull/721
|
||||
silent! call prop_add(l:start_line, l:start_col, {
|
||||
\ 'end_col': l:end_col,
|
||||
\ 'bufnr': a:bufnr,
|
||||
\ 'type': s:get_prop_type_name(l:severity),
|
||||
\ })
|
||||
catch
|
||||
call lsp#log('diagnostics', 'place_highlights', 'prop_add', v:exception, v:throwpoint)
|
||||
endtry
|
||||
else
|
||||
for l:line in range(l:start_line, l:end_line)
|
||||
if l:line == l:start_line
|
||||
let l:highlight_start_col = l:start_col
|
||||
else
|
||||
let l:highlight_start_col = 1
|
||||
endif
|
||||
|
||||
if l:line == l:end_line
|
||||
let l:highlight_end_col = l:end_col
|
||||
else
|
||||
let l:highlight_end_col = strlen(getbufline(a:bufnr, l:line, l:line)[0]) + 1
|
||||
endif
|
||||
|
||||
try
|
||||
" TODO: need to check for valid range before calling prop_add
|
||||
" See https://github.com/prabirshrestha/vim-lsp/pull/721
|
||||
silent! call prop_add(l:line, l:highlight_start_col, {
|
||||
\ 'end_col': l:highlight_end_col,
|
||||
\ 'bufnr': a:bufnr,
|
||||
\ 'type': s:get_prop_type_name(l:severity),
|
||||
\ })
|
||||
catch
|
||||
call lsp#log('diagnostics', 'place_highlights', 'prop_add', v:exception, v:throwpoint)
|
||||
endtry
|
||||
endfor
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
@@ -0,0 +1,199 @@
|
||||
function! s:severity_of(diagnostic) abort
|
||||
return get(a:diagnostic, 'severity', 1)
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#movement#_next_error(...) abort
|
||||
let l:diagnostics = filter(s:get_all_buffer_diagnostics(),
|
||||
\ {_, diagnostic -> s:severity_of(diagnostic) ==# 1 })
|
||||
let l:options = lsp#utils#parse_command_options(a:000)
|
||||
call s:next_diagnostic(l:diagnostics, l:options)
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#movement#_next_warning(...) abort
|
||||
let l:diagnostics = filter(s:get_all_buffer_diagnostics(),
|
||||
\ {_, diagnostic -> s:severity_of(diagnostic) ==# 2 })
|
||||
let l:options = lsp#utils#parse_command_options(a:000)
|
||||
call s:next_diagnostic(l:diagnostics, l:options)
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#movement#_next_diagnostics(...) abort
|
||||
let l:options = lsp#utils#parse_command_options(a:000)
|
||||
call s:next_diagnostic(s:get_all_buffer_diagnostics(), l:options)
|
||||
endfunction
|
||||
|
||||
function! s:next_diagnostic(diagnostics, options) abort
|
||||
if !len(a:diagnostics)
|
||||
return
|
||||
endif
|
||||
call sort(a:diagnostics, 's:compare_diagnostics')
|
||||
|
||||
let l:wrap = 1
|
||||
if has_key(a:options, 'wrap')
|
||||
let l:wrap = a:options['wrap']
|
||||
endif
|
||||
|
||||
let l:view = winsaveview()
|
||||
let l:next_line = 0
|
||||
let l:next_col = 0
|
||||
for l:diagnostic in a:diagnostics
|
||||
let [l:line, l:col] = lsp#utils#position#lsp_to_vim('%', l:diagnostic['range']['start'])
|
||||
if l:line > l:view['lnum']
|
||||
\ || (l:line == l:view['lnum'] && l:col > l:view['col'] + 1)
|
||||
let l:next_line = l:line
|
||||
let l:next_col = l:col - 1
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
if l:next_line == 0
|
||||
if !l:wrap
|
||||
return
|
||||
endif
|
||||
" Wrap to start
|
||||
let [l:next_line, l:next_col] = lsp#utils#position#lsp_to_vim('%', a:diagnostics[0]['range']['start'])
|
||||
let l:next_col -= 1
|
||||
endif
|
||||
|
||||
let l:view['lnum'] = l:next_line
|
||||
let l:view['col'] = l:next_col
|
||||
let l:view['topline'] = 1
|
||||
let l:height = winheight(0)
|
||||
let l:totalnum = line('$')
|
||||
if l:totalnum > l:height
|
||||
let l:half = l:height / 2
|
||||
if l:totalnum - l:half < l:view['lnum']
|
||||
let l:view['topline'] = l:totalnum - l:height + 1
|
||||
else
|
||||
let l:view['topline'] = l:view['lnum'] - l:half
|
||||
endif
|
||||
endif
|
||||
call winrestview(l:view)
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#movement#_previous_error(...) abort
|
||||
let l:diagnostics = filter(s:get_all_buffer_diagnostics(),
|
||||
\ {_, diagnostic -> s:severity_of(diagnostic) ==# 1 })
|
||||
let l:options = lsp#utils#parse_command_options(a:000)
|
||||
call s:previous_diagnostic(l:diagnostics, l:options)
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#movement#_previous_warning(...) abort
|
||||
let l:options = lsp#utils#parse_command_options(a:000)
|
||||
let l:diagnostics = filter(s:get_all_buffer_diagnostics(),
|
||||
\ {_, diagnostic -> s:severity_of(diagnostic) ==# 2 })
|
||||
call s:previous_diagnostic(l:diagnostics, l:options)
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#movement#_previous_diagnostics(...) abort
|
||||
let l:options = lsp#utils#parse_command_options(a:000)
|
||||
call s:previous_diagnostic(s:get_all_buffer_diagnostics(), l:options)
|
||||
endfunction
|
||||
|
||||
function! s:previous_diagnostic(diagnostics, options) abort
|
||||
if !len(a:diagnostics)
|
||||
return
|
||||
endif
|
||||
call sort(a:diagnostics, 's:compare_diagnostics')
|
||||
|
||||
let l:wrap = 1
|
||||
if has_key(a:options, 'wrap')
|
||||
let l:wrap = a:options['wrap']
|
||||
endif
|
||||
|
||||
let l:view = winsaveview()
|
||||
let l:next_line = 0
|
||||
let l:next_col = 0
|
||||
let l:index = len(a:diagnostics) - 1
|
||||
while l:index >= 0
|
||||
let [l:line, l:col] = lsp#utils#position#lsp_to_vim('%', a:diagnostics[l:index]['range']['start'])
|
||||
if l:line < l:view['lnum']
|
||||
\ || (l:line == l:view['lnum'] && l:col < l:view['col'])
|
||||
let l:next_line = l:line
|
||||
let l:next_col = l:col - 1
|
||||
break
|
||||
endif
|
||||
let l:index = l:index - 1
|
||||
endwhile
|
||||
|
||||
if l:next_line == 0
|
||||
if !l:wrap
|
||||
return
|
||||
endif
|
||||
" Wrap to end
|
||||
let [l:next_line, l:next_col] = lsp#utils#position#lsp_to_vim('%', a:diagnostics[-1]['range']['start'])
|
||||
let l:next_col -= 1
|
||||
endif
|
||||
|
||||
let l:view['lnum'] = l:next_line
|
||||
let l:view['col'] = l:next_col
|
||||
let l:view['topline'] = 1
|
||||
let l:height = winheight(0)
|
||||
let l:totalnum = line('$')
|
||||
if l:totalnum > l:height
|
||||
let l:half = l:height / 2
|
||||
if l:totalnum - l:half < l:view['lnum']
|
||||
let l:view['topline'] = l:totalnum - l:height + 1
|
||||
else
|
||||
let l:view['topline'] = l:view['lnum'] - l:half
|
||||
endif
|
||||
endif
|
||||
call winrestview(l:view)
|
||||
endfunction
|
||||
|
||||
function! s:get_diagnostics(uri) abort
|
||||
if has_key(s:diagnostics, a:uri)
|
||||
return [1, s:diagnostics[a:uri]]
|
||||
else
|
||||
if s:is_win
|
||||
" vim in windows always uses upper case for drive letter, so use lowercase in case lang server uses lowercase
|
||||
" https://github.com/theia-ide/typescript-language-server/issues/23
|
||||
let l:uri = substitute(a:uri, '^' . a:uri[:8], tolower(a:uri[:8]), '')
|
||||
if has_key(s:diagnostics, l:uri)
|
||||
return [1, s:diagnostics[l:uri]]
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
return [0, {}]
|
||||
endfunction
|
||||
|
||||
" Get diagnostics for the current buffer URI from all servers
|
||||
function! s:get_all_buffer_diagnostics(...) abort
|
||||
let l:server = get(a:000, 0, '')
|
||||
|
||||
let l:bufnr = bufnr('%')
|
||||
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
|
||||
|
||||
if !lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr)
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:diagnostics_by_server = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri)
|
||||
let l:diagnostics = []
|
||||
if empty(l:server)
|
||||
for l:item in values(l:diagnostics_by_server)
|
||||
let l:diagnostics += lsp#utils#iteratable(l:item['params']['diagnostics'])
|
||||
endfor
|
||||
else
|
||||
if has_key(l:diagnostics_by_server, l:server)
|
||||
let l:diagnostics = lsp#utils#iteratable(l:diagnostics_by_server[l:server]['params']['diagnostics'])
|
||||
endif
|
||||
endif
|
||||
|
||||
return l:diagnostics
|
||||
endfunction
|
||||
|
||||
function! s:compare_diagnostics(d1, d2) abort
|
||||
let l:range1 = a:d1['range']
|
||||
let l:line1 = l:range1['start']['line'] + 1
|
||||
let l:col1 = l:range1['start']['character'] + 1
|
||||
let l:range2 = a:d2['range']
|
||||
let l:line2 = l:range2['start']['line'] + 1
|
||||
let l:col2 = l:range2['start']['character'] + 1
|
||||
|
||||
if l:line1 == l:line2
|
||||
return l:col1 == l:col2 ? 0 : l:col1 > l:col2 ? 1 : -1
|
||||
else
|
||||
return l:line1 > l:line2 ? 1 : -1
|
||||
endif
|
||||
endfunction
|
||||
" vim sw=4 ts=4 et
|
||||
@@ -0,0 +1,151 @@
|
||||
" internal state for whether it is enabled or not to avoid multiple subscriptions
|
||||
let s:enabled = 0
|
||||
let s:sign_group = 'vim_lsp'
|
||||
|
||||
let s:severity_sign_names_mapping = {
|
||||
\ 1: 'LspError',
|
||||
\ 2: 'LspWarning',
|
||||
\ 3: 'LspInformation',
|
||||
\ 4: 'LspHint',
|
||||
\ }
|
||||
|
||||
if !hlexists('LspErrorText')
|
||||
highlight link LspErrorText Error
|
||||
endif
|
||||
|
||||
if !hlexists('LspWarningText')
|
||||
highlight link LspWarningText Todo
|
||||
endif
|
||||
|
||||
if !hlexists('LspInformationText')
|
||||
highlight link LspInformationText Normal
|
||||
endif
|
||||
|
||||
if !hlexists('LspHintText')
|
||||
highlight link LspHintText Normal
|
||||
endif
|
||||
|
||||
function! lsp#internal#diagnostics#signs#_enable() abort
|
||||
" don't even bother registering if the feature is disabled
|
||||
if !lsp#utils#_has_signs() | return | endif
|
||||
if !g:lsp_diagnostics_signs_enabled | return | endif
|
||||
|
||||
if s:enabled | return | endif
|
||||
let s:enabled = 1
|
||||
|
||||
call s:define_sign('LspError', 'E>', g:lsp_diagnostics_signs_error)
|
||||
call s:define_sign('LspWarning', 'W>', g:lsp_diagnostics_signs_warning)
|
||||
call s:define_sign('LspInformation', 'I>', g:lsp_diagnostics_signs_information)
|
||||
call s:define_sign('LspHint', 'H>', g:lsp_diagnostics_signs_hint)
|
||||
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#merge(
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'server') && has_key(x, 'response')
|
||||
\ && has_key(x['response'], 'method') && x['response']['method'] ==# '$/vimlsp/lsp_diagnostics_updated'
|
||||
\ && !lsp#client#is_error(x['response'])}),
|
||||
\ lsp#callbag#map({x->x['response']['params']}),
|
||||
\ ),
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent(['InsertEnter', 'InsertLeave']),
|
||||
\ lsp#callbag#filter({_->!g:lsp_diagnostics_signs_insert_mode_enabled}),
|
||||
\ lsp#callbag#map({_->{ 'uri': lsp#utils#get_buffer_uri() }}),
|
||||
\ ),
|
||||
\ ),
|
||||
\ lsp#callbag#filter({_->g:lsp_diagnostics_signs_enabled}),
|
||||
\ lsp#callbag#debounceTime(g:lsp_diagnostics_signs_delay),
|
||||
\ lsp#callbag#tap({x->s:clear_signs(x)}),
|
||||
\ lsp#callbag#tap({x->s:set_signs(x)}),
|
||||
\ lsp#callbag#subscribe(),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#signs#_disable() abort
|
||||
if !s:enabled | return | endif
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
call s:clear_all_signs()
|
||||
call s:undefine_signs()
|
||||
let s:enabled = 0
|
||||
endfunction
|
||||
|
||||
" Set default sign text to handle case when user provides empty dict
|
||||
function! s:define_sign(sign_name, sign_default_text, sign_options) abort
|
||||
let l:options = {
|
||||
\ 'text': get(a:sign_options, 'text', a:sign_default_text),
|
||||
\ 'texthl': a:sign_name . 'Text',
|
||||
\ 'linehl': a:sign_name . 'Line',
|
||||
\ }
|
||||
let l:sign_icon = get(a:sign_options, 'icon', '')
|
||||
if !empty(l:sign_icon)
|
||||
let l:options['icon'] = l:sign_icon
|
||||
endif
|
||||
call sign_define(a:sign_name, l:options)
|
||||
endfunction
|
||||
|
||||
function! s:undefine_signs() abort
|
||||
call sign_undefine('LspError')
|
||||
call sign_undefine('LspWarning')
|
||||
call sign_undefine('LspInformation')
|
||||
call sign_undefine('LspHint')
|
||||
endfunction
|
||||
|
||||
function! s:clear_all_signs() abort
|
||||
call sign_unplace(s:sign_group)
|
||||
endfunction
|
||||
|
||||
" params => {
|
||||
" server: '' " optional
|
||||
" uri: '' " optional
|
||||
" }
|
||||
function! s:clear_signs(params) abort
|
||||
" TODO: optimize by looking at params
|
||||
call s:clear_all_signs()
|
||||
endfunction
|
||||
|
||||
" params => {
|
||||
" server: '' " optional
|
||||
" uri: '' " optional
|
||||
" }
|
||||
function! s:set_signs(params) abort
|
||||
" TODO: optimize by looking at params
|
||||
if !g:lsp_diagnostics_signs_insert_mode_enabled
|
||||
if mode()[0] ==# 'i' | return | endif
|
||||
endif
|
||||
|
||||
for l:bufnr in range(1, bufnr('$'))
|
||||
if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr) && bufexists(l:bufnr) && bufloaded(l:bufnr)
|
||||
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
|
||||
for [l:server, l:diagnostics_response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri))
|
||||
call s:place_signs(l:server, l:diagnostics_response, l:bufnr)
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:place_signs(server, diagnostics_response, bufnr) abort
|
||||
for l:item in lsp#utils#iteratable(a:diagnostics_response['params']['diagnostics'])
|
||||
let l:line = lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['range']['start'])
|
||||
|
||||
" Some language servers report an unexpected EOF one line past the end
|
||||
" key 'linecount' may be missing.
|
||||
if has_key(getbufinfo(a:bufnr)[0], 'linecount')
|
||||
if l:line == getbufinfo(a:bufnr)[0].linecount + 1
|
||||
let l:line = l:line - 1
|
||||
endif
|
||||
endif
|
||||
|
||||
if has_key(l:item, 'severity') && !empty(l:item['severity'])
|
||||
let l:sign_name = get(s:severity_sign_names_mapping, l:item['severity'], 'LspError')
|
||||
let l:sign_priority = get(g:lsp_diagnostics_signs_priority_map, l:sign_name, g:lsp_diagnostics_signs_priority)
|
||||
let l:sign_priority = get(g:lsp_diagnostics_signs_priority_map,
|
||||
\ a:server . '_' . l:sign_name, l:sign_priority)
|
||||
" pass 0 and let vim generate sign id
|
||||
let l:sign_id = sign_place(0, s:sign_group, l:sign_name, a:bufnr,
|
||||
\{ 'lnum': l:line, 'priority': l:sign_priority })
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
@@ -0,0 +1,173 @@
|
||||
" https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic
|
||||
"
|
||||
" Refer to https://github.com/microsoft/language-server-protocol/pull/1019 on normalization of urls.
|
||||
" {
|
||||
" 'normalized_uri': {
|
||||
" 'server_name': {
|
||||
" 'method': 'textDocument/publishDiagnostics',
|
||||
" 'params': {
|
||||
" 'uri': 'uri', " this uri is not normalized and is exactly what server returns
|
||||
" 'dignostics': [ " array can never be null but can be empty
|
||||
" https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic
|
||||
" { range, message, severity?, code?, codeDesciption?, source?, tags?, relatedInformation?, data? }
|
||||
" ]
|
||||
" }
|
||||
" }
|
||||
" }
|
||||
" Note: Do not remove when buffer unloads or doesn't exist since some server
|
||||
" may send diagnsotics information regardless of textDocument/didOpen.
|
||||
" buffer state is removed when server exits.
|
||||
" TODO: reset buffer state when server initializes. ignoring for now for perf.
|
||||
let s:diagnostics_state = {}
|
||||
|
||||
" internal state for whether it is enabled or not to avoid multiple subscriptions
|
||||
let s:enabled = 0
|
||||
|
||||
let s:diagnostic_kinds = {
|
||||
\ 1: 'error',
|
||||
\ 2: 'warning',
|
||||
\ 3: 'information',
|
||||
\ 4: 'hint',
|
||||
\ }
|
||||
|
||||
function! lsp#internal#diagnostics#state#_enable() abort
|
||||
" don't even bother registering if the feature is disabled
|
||||
if !g:lsp_diagnostics_enabled | return | endif
|
||||
|
||||
if s:enabled | return | endif
|
||||
let s:enabled = 1
|
||||
|
||||
call lsp#internal#diagnostics#state#_reset()
|
||||
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#merge(
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'server') && has_key(x, 'response')
|
||||
\ && get(x['response'], 'method', '') ==# 'textDocument/publishDiagnostics'}),
|
||||
\ lsp#callbag#tap({x->s:on_text_documentation_publish_diagnostics(x['server'], x['response'])}),
|
||||
\ ),
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'server') && has_key(x, 'response')
|
||||
\ && get(x['response'], 'method', '') ==# '$/vimlsp/lsp_server_exit' }),
|
||||
\ lsp#callbag#tap({x->s:on_exit(x['response'])}),
|
||||
\ ),
|
||||
\ ),
|
||||
\ lsp#callbag#subscribe(),
|
||||
\ )
|
||||
|
||||
call s:notify_diagnostics_update()
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#state#_disable() abort
|
||||
if !s:enabled | return | endif
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
call lsp#internal#diagnostics#state#_reset()
|
||||
call s:notify_diagnostics_update()
|
||||
let s:enabled = 0
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#state#_reset() abort
|
||||
let s:diagnostics_state = {}
|
||||
let s:diagnostics_disabled_buffers = {}
|
||||
endfunction
|
||||
|
||||
" callers should always treat the return value as immutable
|
||||
" @return {
|
||||
" 'servername': response
|
||||
" }
|
||||
function! lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(uri) abort
|
||||
return get(s:diagnostics_state, lsp#utils#normalize_uri(a:uri), {})
|
||||
endfunction
|
||||
|
||||
" callers should always treat the return value as immutable.
|
||||
" callers should treat uri as normalized via lsp#utils#normalize_uri
|
||||
" @return {
|
||||
" 'normalized_uri': {
|
||||
" 'servername': response
|
||||
" }
|
||||
" }
|
||||
function! lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_uri_and_server() abort
|
||||
return s:diagnostics_state
|
||||
endfunction
|
||||
|
||||
function! s:on_text_documentation_publish_diagnostics(server, response) abort
|
||||
if lsp#client#is_error(a:response) | return | endif
|
||||
let l:normalized_uri = lsp#utils#normalize_uri(a:response['params']['uri'])
|
||||
if !has_key(s:diagnostics_state, l:normalized_uri)
|
||||
let s:diagnostics_state[l:normalized_uri] = {}
|
||||
endif
|
||||
let s:diagnostics_state[l:normalized_uri][a:server] = a:response
|
||||
call s:notify_diagnostics_update(a:server, l:normalized_uri)
|
||||
endfunction
|
||||
|
||||
function! s:on_exit(response) abort
|
||||
let l:server = a:response['params']['server']
|
||||
let l:notify = 0
|
||||
for [l:key, l:value] in items(s:diagnostics_state)
|
||||
if has_key(l:value, l:server)
|
||||
let l:notify = 1
|
||||
call remove(l:value, l:server)
|
||||
endif
|
||||
endfor
|
||||
if l:notify | call s:notify_diagnostics_update(l:server) | endif
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#state#_force_notify_buffer(buffer) abort
|
||||
" TODO: optimize buffer only
|
||||
call s:notify_diagnostics_update()
|
||||
endfunction
|
||||
|
||||
" call s:notify_diagnostics_update()
|
||||
" call s:notify_diagnostics_update('server')
|
||||
" call s:notify_diagnostics_update('server', 'uri')
|
||||
function! s:notify_diagnostics_update(...) abort
|
||||
let l:data = { 'server': '$vimlsp', 'response': { 'method': '$/vimlsp/lsp_diagnostics_updated', 'params': {} } }
|
||||
" if a:0 > 0 | let l:data['response']['params']['server'] = a:1 | endif
|
||||
" if a:0 > 1 | let l:data['response']['params']['uri'] = a:2 | endif
|
||||
call lsp#stream(1, l:data)
|
||||
doautocmd <nomodeline> User lsp_diagnostics_updated
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#state#_enable_for_buffer(bufnr) abort
|
||||
if getbufvar(a:bufnr, 'lsp_diagnostics_enabled', 1) == 0
|
||||
call setbufvar(a:bufnr, 'lsp_diagnostics_enabled', 1)
|
||||
call s:notify_diagnostics_update()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#state#_disable_for_buffer(bufnr) abort
|
||||
if getbufvar(a:bufnr, 'lsp_diagnostics_enabled', 1) != 0
|
||||
call setbufvar(a:bufnr, 'lsp_diagnostics_enabled', 0)
|
||||
call s:notify_diagnostics_update()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#state#_is_enabled_for_buffer(bufnr) abort
|
||||
return getbufvar(a:bufnr, 'lsp_diagnostics_enabled', 1) == 1
|
||||
endfunction
|
||||
|
||||
" Return dict with diagnostic counts for the specified buffer
|
||||
" { 'error': 1, 'warning': 0, 'information': 0, 'hint': 0 }
|
||||
function! lsp#internal#diagnostics#state#_get_diagnostics_count_for_buffer(bufnr) abort
|
||||
let l:counts = {
|
||||
\ 'error': 0,
|
||||
\ 'warning': 0,
|
||||
\ 'information': 0,
|
||||
\ 'hint': 0,
|
||||
\ }
|
||||
if lsp#internal#diagnostics#state#_is_enabled_for_buffer(a:bufnr)
|
||||
let l:uri = lsp#utils#get_buffer_uri(a:bufnr)
|
||||
for [l:_, l:response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri))
|
||||
for l:diagnostic in lsp#utils#iteratable(l:response['params']['diagnostics'])
|
||||
let l:key = get(s:diagnostic_kinds, get(l:diagnostic, 'severity', 1) , 'error')
|
||||
let l:counts[l:key] += 1
|
||||
endfor
|
||||
endfor
|
||||
end
|
||||
return l:counts
|
||||
endfunction
|
||||
@@ -0,0 +1,48 @@
|
||||
" Returns a diagnostic object, or empty dictionary if no diagnostics are
|
||||
" available.
|
||||
" options = {
|
||||
" 'server': '', " optional
|
||||
" }
|
||||
function! lsp#internal#diagnostics#under_cursor#get_diagnostic(...) abort
|
||||
let l:options = get(a:000, 0, {})
|
||||
let l:server = get(l:options, 'server', '')
|
||||
let l:bufnr = bufnr('%')
|
||||
|
||||
if !lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr)
|
||||
return {}
|
||||
endif
|
||||
|
||||
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
|
||||
|
||||
let l:diagnostics_by_server = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri)
|
||||
let l:diagnostics = []
|
||||
if empty(l:server)
|
||||
for l:item in values(l:diagnostics_by_server)
|
||||
let l:diagnostics += lsp#utils#iteratable(l:item['params']['diagnostics'])
|
||||
endfor
|
||||
else
|
||||
if has_key(l:diagnostics_by_server, l:server)
|
||||
let l:diagnostics = lsp#utils#iteratable(l:diagnostics_by_server[l:server]['params']['diagnostics'])
|
||||
endif
|
||||
endif
|
||||
|
||||
let l:line = line('.')
|
||||
let l:col = col('.')
|
||||
|
||||
let l:closest_diagnostic = {}
|
||||
let l:closest_distance = -1
|
||||
|
||||
for l:diagnostic in l:diagnostics
|
||||
let [l:start_line, l:start_col] = lsp#utils#position#lsp_to_vim('%', l:diagnostic['range']['start'])
|
||||
|
||||
if l:line == l:start_line
|
||||
let l:distance = abs(l:start_col - l:col)
|
||||
if l:closest_distance < 0 || l:distance < l:closest_distance
|
||||
let l:closest_diagnostic = l:diagnostic
|
||||
let l:closest_distance = l:distance
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:closest_diagnostic
|
||||
endfunction
|
||||
@@ -0,0 +1,191 @@
|
||||
" internal state for whether it is enabled or not to avoid multiple subscriptions
|
||||
let s:enabled = 0
|
||||
let s:namespace_id = '' " will be set when enabled
|
||||
let s:severity_sign_names_mapping = {
|
||||
\ 1: 'LspError',
|
||||
\ 2: 'LspWarning',
|
||||
\ 3: 'LspInformation',
|
||||
\ 4: 'LspHint',
|
||||
\ }
|
||||
|
||||
if !hlexists('LspErrorVirtualText')
|
||||
if !hlexists('LspErrorText')
|
||||
highlight link LspErrorVirtualText Error
|
||||
else
|
||||
highlight link LspErrorVirtualText LspErrorText
|
||||
endif
|
||||
endif
|
||||
|
||||
if !hlexists('LspWarningVirtualText')
|
||||
if !hlexists('LspWarningText')
|
||||
highlight link LspWarningVirtualText Todo
|
||||
else
|
||||
highlight link LspWarningVirtualText LspWarningText
|
||||
endif
|
||||
endif
|
||||
|
||||
if !hlexists('LspInformationVirtualText')
|
||||
if !hlexists('LspInformationText')
|
||||
highlight link LspInformationVirtualText Normal
|
||||
else
|
||||
highlight link LspInformationVirtualText LspInformationText
|
||||
endif
|
||||
endif
|
||||
|
||||
if !hlexists('LspHintVirtualText')
|
||||
if !hlexists('LspHintText')
|
||||
highlight link LspHintVirtualText Normal
|
||||
else
|
||||
highlight link LspHintVirtualText LspHintText
|
||||
endif
|
||||
endif
|
||||
|
||||
function! lsp#internal#diagnostics#virtual_text#_enable() abort
|
||||
" don't even bother registering if the feature is disabled
|
||||
if !lsp#utils#_has_nvim_virtual_text() && !lsp#utils#_has_vim_virtual_text() | return | endif
|
||||
if !g:lsp_diagnostics_virtual_text_enabled | return | endif
|
||||
|
||||
if s:enabled | return | endif
|
||||
let s:enabled = 1
|
||||
|
||||
if has('nvim')
|
||||
if empty(s:namespace_id)
|
||||
let s:namespace_id = nvim_create_namespace('vim_lsp_diagnostic_virtual_text')
|
||||
endif
|
||||
else
|
||||
if index(prop_type_list(), 'vim_lsp_LspError_virtual_text') ==# -1
|
||||
call prop_type_add('vim_lsp_LspError_virtual_text', { 'highlight': 'LspErrorVirtualText' })
|
||||
call prop_type_add('vim_lsp_LspWarning_virtual_text', { 'highlight': 'LspWarningVirtualText' })
|
||||
call prop_type_add('vim_lsp_LspInformation_virtual_text', { 'highlight': 'LspInformationVirtualText' })
|
||||
call prop_type_add('vim_lsp_LspHint_virtual_text', { 'highlight': 'LspHintVirtualText' })
|
||||
endif
|
||||
endif
|
||||
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#merge(
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'server') && has_key(x, 'response')
|
||||
\ && has_key(x['response'], 'method') && x['response']['method'] ==# '$/vimlsp/lsp_diagnostics_updated'
|
||||
\ && !lsp#client#is_error(x['response'])}),
|
||||
\ lsp#callbag#map({x->x['response']['params']}),
|
||||
\ ),
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent(['InsertEnter', 'InsertLeave']),
|
||||
\ lsp#callbag#filter({_->!g:lsp_diagnostics_virtual_text_insert_mode_enabled}),
|
||||
\ lsp#callbag#map({_->{ 'uri': lsp#utils#get_buffer_uri() }}),
|
||||
\ ),
|
||||
\ ),
|
||||
\ lsp#callbag#filter({_->g:lsp_diagnostics_virtual_text_enabled}),
|
||||
\ lsp#callbag#debounceTime(g:lsp_diagnostics_virtual_text_delay),
|
||||
\ lsp#callbag#tap({x->s:clear_virtual_text(x)}),
|
||||
\ lsp#callbag#tap({x->s:set_virtual_text(x)}),
|
||||
\ lsp#callbag#subscribe(),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#diagnostics#virtual_text#_disable() abort
|
||||
if !s:enabled | return | endif
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
call s:clear_all_virtual_text()
|
||||
let s:enabled = 0
|
||||
endfunction
|
||||
|
||||
function! s:clear_all_virtual_text() abort
|
||||
if has('nvim')
|
||||
for l:bufnr in nvim_list_bufs()
|
||||
if bufexists(l:bufnr) && bufloaded(l:bufnr)
|
||||
call nvim_buf_clear_namespace(l:bufnr, s:namespace_id, 0, -1)
|
||||
endif
|
||||
endfor
|
||||
else
|
||||
let l:types = ['vim_lsp_LspError_virtual_text', 'vim_lsp_LspWarning_virtual_text', 'vim_lsp_LspInformation_virtual_text', 'vim_lsp_LspHint_virtual_text']
|
||||
for l:bufnr in map(copy(getbufinfo()), 'v:val.bufnr')
|
||||
if lsp#utils#_has_prop_remove_types()
|
||||
call prop_remove({'types': l:types, 'bufnr': l:bufnr, 'all': v:true})
|
||||
else
|
||||
for l:type in l:types
|
||||
call prop_remove({'type': l:type, 'bufnr': l:bufnr, 'all': v:true})
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" params => {
|
||||
" server: '' " optional
|
||||
" uri: '' " optional
|
||||
" }
|
||||
function! s:clear_virtual_text(params) abort
|
||||
" TODO: optimize by looking at params
|
||||
call s:clear_all_virtual_text()
|
||||
endfunction
|
||||
|
||||
" params => {
|
||||
" server: '' " optional
|
||||
" uri: '' " optional
|
||||
" }
|
||||
function! s:set_virtual_text(params) abort
|
||||
" TODO: optimize by looking at params
|
||||
if !g:lsp_diagnostics_virtual_text_insert_mode_enabled
|
||||
if mode()[0] ==# 'i' | return | endif
|
||||
endif
|
||||
|
||||
if has('nvim')
|
||||
for l:bufnr in nvim_list_bufs()
|
||||
if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr) && bufexists(l:bufnr) && bufloaded(l:bufnr)
|
||||
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
|
||||
for [l:server, l:diagnostics_response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri))
|
||||
call s:place_virtual_text(l:server, l:diagnostics_response, l:bufnr)
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
else
|
||||
for l:bufnr in map(copy(getbufinfo()), 'v:val.bufnr')
|
||||
if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr) && bufexists(l:bufnr) && bufloaded(l:bufnr)
|
||||
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
|
||||
for [l:server, l:diagnostics_response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri))
|
||||
call s:place_virtual_text(l:server, l:diagnostics_response, l:bufnr)
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:place_virtual_text(server, diagnostics_response, bufnr) abort
|
||||
for l:item in lsp#utils#iteratable(a:diagnostics_response['params']['diagnostics'])
|
||||
let l:line = lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['range']['start'])
|
||||
let l:name = get(s:severity_sign_names_mapping, get(l:item, 'severity', 3), 'LspError')
|
||||
let l:text = g:lsp_diagnostics_virtual_text_prefix . l:item['message']
|
||||
|
||||
" Some language servers report an unexpected EOF one line past the end
|
||||
if l:line == getbufinfo(a:bufnr)[0].linecount + 1
|
||||
let l:line = l:line - 1
|
||||
endif
|
||||
|
||||
if has('nvim')
|
||||
let l:hl_name = l:name . 'VirtualText'
|
||||
" need to do -1 for virtual text
|
||||
call nvim_buf_set_virtual_text(a:bufnr, s:namespace_id, l:line - 1,
|
||||
\ [[l:text, l:hl_name]], {})
|
||||
else
|
||||
" it's an error to add virtual text on lines that don't exist
|
||||
" anymore due to async processing, just skip such diagnostics
|
||||
if l:line <= getbufinfo(a:bufnr)[0].linecount
|
||||
let l:type = 'vim_lsp_' . l:name . '_virtual_text'
|
||||
call prop_remove({'all': v:true, 'type': l:type, 'bufnr': a:bufnr}, l:line)
|
||||
call prop_add(
|
||||
\ l:line, 0,
|
||||
\ {
|
||||
\ 'type': l:type, 'text': l:text, 'bufnr': a:bufnr,
|
||||
\ 'text_align': g:lsp_diagnostics_virtual_text_align,
|
||||
\ 'text_padding_left': g:lsp_diagnostics_virtual_text_padding_left,
|
||||
\ 'text_wrap': g:lsp_diagnostics_virtual_text_wrap,
|
||||
\ })
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
@@ -0,0 +1,131 @@
|
||||
" https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction
|
||||
|
||||
" internal state for whether it is enabled or not to avoid multiple subscriptions
|
||||
let s:enabled = 0
|
||||
|
||||
let s:sign_group = 'vim_lsp_document_code_action_signs'
|
||||
|
||||
if !hlexists('LspCodeActionText')
|
||||
highlight link LspCodeActionText Normal
|
||||
endif
|
||||
|
||||
function! lsp#internal#document_code_action#signs#_enable() abort
|
||||
if !lsp#utils#_has_signs() | return | endif
|
||||
" don't even bother registering if the feature is disabled
|
||||
if !g:lsp_document_code_action_signs_enabled | return | endif
|
||||
|
||||
if s:enabled | return | endif
|
||||
let s:enabled = 1
|
||||
|
||||
call s:define_sign('LspCodeAction', 'A>', g:lsp_document_code_action_signs_hint)
|
||||
|
||||
" Note:
|
||||
" - update CodeAction signs when CusorMoved or CursorHold
|
||||
" - clear signs when InsertEnter or BufLeave
|
||||
" - debounce code action requests
|
||||
" - automatically switch to latest code action request via switchMap()
|
||||
" - cancel code action request via takeUntil() when BufLeave
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#merge(
|
||||
\ lsp#callbag#fromEvent(['CursorMoved', 'CursorHold']),
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent(['InsertEnter', 'BufLeave']),
|
||||
\ lsp#callbag#tap({_ -> s:clear_signs() }),
|
||||
\ )
|
||||
\ ),
|
||||
\ lsp#callbag#filter({_ -> g:lsp_document_code_action_signs_enabled }),
|
||||
\ lsp#callbag#debounceTime(g:lsp_document_code_action_signs_delay),
|
||||
\ lsp#callbag#map({_->{'bufnr': bufnr('%'), 'curpos': getcurpos()[0:2], 'changedtick': b:changedtick }}),
|
||||
\ lsp#callbag#distinctUntilChanged({a,b -> a['bufnr'] == b['bufnr'] && a['curpos'] == b['curpos'] && a['changedtick'] == b['changedtick']}),
|
||||
\ lsp#callbag#filter({_->mode() is# 'n' && getbufvar(bufnr('%'), '&buftype') !=# 'terminal' }),
|
||||
\ lsp#callbag#switchMap({_->
|
||||
\ lsp#callbag#pipe(
|
||||
\ s:send_request(),
|
||||
\ lsp#callbag#materialize(),
|
||||
\ lsp#callbag#filter({x->lsp#callbag#isNextNotification(x)}),
|
||||
\ lsp#callbag#map({x->x['value']}),
|
||||
\ lsp#callbag#takeUntil(
|
||||
\ lsp#callbag#fromEvent('BufLeave')
|
||||
\ )
|
||||
\ )
|
||||
\ }),
|
||||
\ lsp#callbag#subscribe({x->s:set_signs(x)}),
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#document_code_action#signs#_disable() abort
|
||||
if !s:enabled | return | endif
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:send_request() abort
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_code_action_provider(v:val)')
|
||||
|
||||
if empty(l:servers)
|
||||
return lsp#callbag#empty()
|
||||
endif
|
||||
|
||||
let l:range = lsp#utils#range#_get_current_line_range()
|
||||
return lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromList(l:servers),
|
||||
\ lsp#callbag#flatMap({server->
|
||||
\ lsp#request(server, {
|
||||
\ 'method': 'textDocument/codeAction',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'range': l:range,
|
||||
\ 'context': {
|
||||
\ 'diagnostics': [],
|
||||
\ 'only': ['', 'quickfix', 'refactor', 'refactor.extract', 'refactor.inline', 'refactor.rewrite'],
|
||||
\ }
|
||||
\ }
|
||||
\ })
|
||||
\ }),
|
||||
\ lsp#callbag#filter({x-> !lsp#client#is_error(x['response']) && !empty(x['response']['result'])}),
|
||||
\ lsp#callbag#take(1),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! s:clear_signs() abort
|
||||
call sign_unplace(s:sign_group)
|
||||
endfunction
|
||||
|
||||
function! s:set_signs(data) abort
|
||||
call s:clear_signs()
|
||||
|
||||
if lsp#client#is_error(a:data['response']) | return | endif
|
||||
|
||||
if empty(a:data['response']['result'])
|
||||
return
|
||||
endif
|
||||
|
||||
let l:bufnr = bufnr(lsp#utils#uri_to_path(a:data['request']['params']['textDocument']['uri']))
|
||||
call s:place_signs(a:data, l:bufnr)
|
||||
endfunction
|
||||
|
||||
" Set default sign text to handle case when user provides empty dict
|
||||
function! s:define_sign(sign_name, sign_default_text, sign_options) abort
|
||||
let l:options = {
|
||||
\ 'text': get(a:sign_options, 'text', a:sign_default_text),
|
||||
\ 'texthl': a:sign_name . 'Text',
|
||||
\ 'linehl': a:sign_name . 'Line',
|
||||
\ }
|
||||
let l:sign_icon = get(a:sign_options, 'icon', '')
|
||||
if !empty(l:sign_icon)
|
||||
let l:options['icon'] = l:sign_icon
|
||||
endif
|
||||
call sign_define(a:sign_name, l:options)
|
||||
endfunction
|
||||
|
||||
function! s:place_signs(data, bufnr) abort
|
||||
if !bufexists(a:bufnr) || !bufloaded(a:bufnr)
|
||||
return
|
||||
endif
|
||||
let l:sign_priority = g:lsp_document_code_action_signs_priority
|
||||
let l:line = lsp#utils#position#lsp_line_to_vim(a:bufnr, a:data['request']['params']['range']['start'])
|
||||
let l:sign_id = sign_place(0, s:sign_group, 'LspCodeAction', a:bufnr,
|
||||
\ { 'lnum': l:line, 'priority': l:sign_priority })
|
||||
endfunction
|
||||
@@ -0,0 +1,86 @@
|
||||
" options - {
|
||||
" bufnr: bufnr('%') " required
|
||||
" server - 'server_name' " optional
|
||||
" sync: 0 " optional, defaults to 0 (async)
|
||||
" }
|
||||
function! lsp#internal#document_formatting#format(options) abort
|
||||
let l:mode = mode()
|
||||
if l:mode =~# '[vV]' || l:mode ==# "\<C-V>"
|
||||
return lsp#internal#document_range_formatting#format(a:options)
|
||||
endif
|
||||
|
||||
if has_key(a:options, 'server')
|
||||
let l:servers = [a:options['server']]
|
||||
else
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_document_formatting_provider(v:val)')
|
||||
endif
|
||||
|
||||
if len(l:servers) == 0
|
||||
let l:filetype = getbufvar(a:options['bufnr'], '&filetype')
|
||||
call lsp#utils#error('textDocument/formatting not supported for ' . l:filetype)
|
||||
return
|
||||
endif
|
||||
|
||||
" TODO: ask user to select server for formatting if there are multiple servers
|
||||
let l:server = l:servers[0]
|
||||
|
||||
redraw | echo 'Formatting Document ...'
|
||||
|
||||
call lsp#_new_command()
|
||||
|
||||
let l:request = {
|
||||
\ 'method': 'textDocument/formatting',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(a:options['bufnr']),
|
||||
\ 'options': {
|
||||
\ 'tabSize': lsp#utils#buffer#get_indent_size(a:options['bufnr']),
|
||||
\ 'insertSpaces': getbufvar(a:options['bufnr'], '&expandtab') ? v:true : v:false,
|
||||
\ }
|
||||
\ },
|
||||
\ 'bufnr': a:options['bufnr'],
|
||||
\ }
|
||||
|
||||
if get(a:options, 'sync', 0) == 1
|
||||
try
|
||||
let l:x = lsp#callbag#pipe(
|
||||
\ lsp#request(l:server, l:request),
|
||||
\ lsp#callbag#takeUntil(lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'command')}),
|
||||
\ )),
|
||||
\ lsp#callbag#toList(),
|
||||
\ ).wait({ 'sleep': get(a:options, 'sleep', 1), 'timeout': get(a:options, 'timeout', g:lsp_format_sync_timeout) })
|
||||
call s:format_next(l:x[0])
|
||||
call s:format_complete()
|
||||
catch
|
||||
call s:format_error(v:exception . ' ' . v:throwpoint)
|
||||
endtry
|
||||
else
|
||||
return lsp#callbag#pipe(
|
||||
\ lsp#request(l:server, l:request),
|
||||
\ lsp#callbag#takeUntil(lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'command')}),
|
||||
\ )),
|
||||
\ lsp#callbag#subscribe({
|
||||
\ 'next':{x->s:format_next(x)},
|
||||
\ 'error': {x->s:format_error(e)},
|
||||
\ 'complete': {->s:format_complete()},
|
||||
\ }),
|
||||
\ )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:format_next(x) abort
|
||||
if lsp#client#is_error(a:x['response']) | return | endif
|
||||
call lsp#utils#text_edit#apply_text_edits(a:x['request']['params']['textDocument']['uri'], get(a:x['response'], 'result', ''))
|
||||
endfunction
|
||||
|
||||
function! s:format_error(e) abort
|
||||
call lsp#log('Formatting Document Failed', a:e)
|
||||
call lsp#utils#error('Formatting Document Failed.' . (type(a:e) == type('') ? a:e : ''))
|
||||
endfunction
|
||||
|
||||
function! s:format_complete() abort
|
||||
redraw | echo 'Formatting Document complete'
|
||||
endfunction
|
||||
@@ -0,0 +1,238 @@
|
||||
let s:use_vim_textprops = lsp#utils#_has_textprops() && !has('nvim')
|
||||
let s:prop_id = 11
|
||||
|
||||
function! lsp#internal#document_highlight#_enable() abort
|
||||
" don't event bother registering if the feature is disabled
|
||||
if !g:lsp_document_highlight_enabled | return | endif
|
||||
|
||||
" Highlight group for references
|
||||
if !hlexists('lspReference')
|
||||
highlight link lspReference CursorColumn
|
||||
endif
|
||||
|
||||
" Note:
|
||||
" - update highlight references when CusorMoved or CursorHold
|
||||
" - clear highlights when InsertEnter or BufLeave
|
||||
" - debounce highlight requests
|
||||
" - automatically switch to latest highlight request via switchMap()
|
||||
" - cancel highlight request via takeUntil() when BufLeave
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#merge(
|
||||
\ lsp#callbag#fromEvent(['CursorMoved', 'CursorHold']),
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent(['InsertEnter', 'BufLeave']),
|
||||
\ lsp#callbag#tap({_ -> s:clear_highlights() }),
|
||||
\ )
|
||||
\ ),
|
||||
\ lsp#callbag#filter({_ -> g:lsp_document_highlight_enabled }),
|
||||
\ lsp#callbag#debounceTime(g:lsp_document_highlight_delay),
|
||||
\ lsp#callbag#map({_->{'bufnr': bufnr('%'), 'curpos': getcurpos()[0:2], 'changedtick': b:changedtick }}),
|
||||
\ lsp#callbag#distinctUntilChanged({a,b -> a['bufnr'] == b['bufnr'] && a['curpos'] == b['curpos'] && a['changedtick'] == b['changedtick']}),
|
||||
\ lsp#callbag#filter({_->mode() is# 'n' && getbufvar(bufnr('%'), '&buftype') !=# 'terminal' }),
|
||||
\ lsp#callbag#switchMap({_->
|
||||
\ lsp#callbag#pipe(
|
||||
\ s:send_highlight_request(),
|
||||
\ lsp#callbag#materialize(),
|
||||
\ lsp#callbag#filter({x->lsp#callbag#isNextNotification(x)}),
|
||||
\ lsp#callbag#map({x->x['value']}),
|
||||
\ lsp#callbag#takeUntil(
|
||||
\ lsp#callbag#fromEvent('BufLeave')
|
||||
\ )
|
||||
\ )
|
||||
\ }),
|
||||
\ lsp#callbag#filter({_->mode() is# 'n'}),
|
||||
\ lsp#callbag#subscribe({x->s:set_highlights(x)}),
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#document_highlight#_disable() abort
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:send_highlight_request() abort
|
||||
let l:capability = 'lsp#capabilities#has_document_highlight_provider(v:val)'
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capability)
|
||||
|
||||
if empty(l:servers)
|
||||
return lsp#callbag#empty()
|
||||
endif
|
||||
|
||||
return lsp#request(l:servers[0], {
|
||||
\ 'method': 'textDocument/documentHighlight',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'position': lsp#get_position(),
|
||||
\ },
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
function! s:set_highlights(data) abort
|
||||
let l:bufnr = bufnr('%')
|
||||
|
||||
call s:clear_highlights()
|
||||
|
||||
if mode() !=# 'n' | return | endif
|
||||
|
||||
if lsp#client#is_error(a:data['response']) | return | endif
|
||||
|
||||
" Get references from the response
|
||||
let l:reference_list = a:data['response']['result']
|
||||
if empty(l:reference_list)
|
||||
return
|
||||
endif
|
||||
|
||||
" Convert references to vim positions
|
||||
let l:position_list = []
|
||||
for l:reference in l:reference_list
|
||||
call extend(l:position_list, lsp#utils#range#lsp_to_vim(l:bufnr, l:reference['range']))
|
||||
endfor
|
||||
|
||||
call sort(l:position_list, function('s:compare_positions'))
|
||||
|
||||
" Ignore response if the cursor is not over a reference anymore
|
||||
if s:in_reference(l:position_list) == -1 | return | endif
|
||||
|
||||
" Store references
|
||||
if s:use_vim_textprops
|
||||
let b:lsp_reference_positions = l:position_list
|
||||
let b:lsp_reference_matches = []
|
||||
else
|
||||
let w:lsp_reference_positions = l:position_list
|
||||
let w:lsp_reference_matches = []
|
||||
endif
|
||||
|
||||
" Apply highlights to the buffer
|
||||
call s:init_reference_highlight(l:bufnr)
|
||||
if s:use_vim_textprops
|
||||
for l:position in l:position_list
|
||||
try
|
||||
" TODO: need to check for valid range before calling prop_add
|
||||
" See https://github.com/prabirshrestha/vim-lsp/pull/721
|
||||
silent! call prop_add(l:position[0], l:position[1], {
|
||||
\ 'id': s:prop_id,
|
||||
\ 'bufnr': l:bufnr,
|
||||
\ 'length': l:position[2],
|
||||
\ 'type': 'vim-lsp-reference-highlight'})
|
||||
call add(b:lsp_reference_matches, l:position[0])
|
||||
catch
|
||||
call lsp#log('document_highlight', 'set_highlights', v:exception, v:throwpoint)
|
||||
endtry
|
||||
endfor
|
||||
else
|
||||
for l:position in l:position_list
|
||||
let l:match = matchaddpos('lspReference', [l:position], -5)
|
||||
call add(w:lsp_reference_matches, l:match)
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:clear_highlights() abort
|
||||
if s:use_vim_textprops
|
||||
if exists('b:lsp_reference_matches')
|
||||
let l:bufnr = bufnr('%')
|
||||
for l:line in b:lsp_reference_matches
|
||||
silent! call prop_remove(
|
||||
\ {'id': s:prop_id,
|
||||
\ 'bufnr': l:bufnr,
|
||||
\ 'all': v:true}, l:line)
|
||||
endfor
|
||||
unlet b:lsp_reference_matches
|
||||
unlet b:lsp_reference_positions
|
||||
endif
|
||||
else
|
||||
if exists('w:lsp_reference_matches')
|
||||
for l:match in w:lsp_reference_matches
|
||||
silent! call matchdelete(l:match)
|
||||
endfor
|
||||
unlet w:lsp_reference_matches
|
||||
unlet w:lsp_reference_positions
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Compare two positions
|
||||
function! s:compare_positions(p1, p2) abort
|
||||
let l:line_1 = a:p1[0]
|
||||
let l:line_2 = a:p2[0]
|
||||
if l:line_1 != l:line_2
|
||||
return l:line_1 > l:line_2 ? 1 : -1
|
||||
endif
|
||||
let l:col_1 = a:p1[1]
|
||||
let l:col_2 = a:p2[1]
|
||||
return l:col_1 - l:col_2
|
||||
endfunction
|
||||
|
||||
" If the cursor is over a reference, return its index in
|
||||
" the array. Otherwise, return -1.
|
||||
function! s:in_reference(reference_list) abort
|
||||
let l:line = line('.')
|
||||
let l:column = col('.')
|
||||
let l:index = 0
|
||||
for l:position in a:reference_list
|
||||
if l:line == l:position[0] &&
|
||||
\ l:column >= l:position[1] &&
|
||||
\ l:column < l:position[1] + l:position[2]
|
||||
return l:index
|
||||
endif
|
||||
let l:index += 1
|
||||
endfor
|
||||
return -1
|
||||
endfunction
|
||||
|
||||
function! s:init_reference_highlight(buf) abort
|
||||
if s:use_vim_textprops
|
||||
let l:props = {
|
||||
\ 'bufnr': a:buf,
|
||||
\ 'highlight': 'lspReference',
|
||||
\ 'combine': v:true,
|
||||
\ 'priority': lsp#internal#textprop#priority('document_highlight')
|
||||
\ }
|
||||
if prop_type_get('vim-lsp-reference-highlight', { 'bufnr': a:buf }) == {}
|
||||
call prop_type_add('vim-lsp-reference-highlight', l:props)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Cyclically move between references by `offset` occurrences.
|
||||
function! lsp#internal#document_highlight#jump(offset) abort
|
||||
if s:use_vim_textprops && !exists('b:lsp_reference_positions') ||
|
||||
\ !s:use_vim_textprops && !exists('w:lsp_reference_positions')
|
||||
echohl WarningMsg
|
||||
echom 'References not available'
|
||||
echohl None
|
||||
return
|
||||
endif
|
||||
|
||||
" Get index of reference under cursor
|
||||
let l:index = s:use_vim_textprops ? s:in_reference(b:lsp_reference_positions) : s:in_reference(w:lsp_reference_positions)
|
||||
if l:index < 0
|
||||
return
|
||||
endif
|
||||
|
||||
let l:n = s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions)
|
||||
let l:index += a:offset
|
||||
|
||||
" Show a message when reaching TOP/BOTTOM of the file
|
||||
if l:index < 0
|
||||
echohl WarningMsg
|
||||
echom 'search hit TOP, continuing at BOTTOM'
|
||||
echohl None
|
||||
elseif l:index >= (s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions))
|
||||
echohl WarningMsg
|
||||
echom 'search hit BOTTOM, continuing at TOP'
|
||||
echohl None
|
||||
endif
|
||||
|
||||
" Wrap index
|
||||
if l:index < 0 || l:index >= (s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions))
|
||||
let l:index = (l:index % l:n + l:n) % l:n
|
||||
endif
|
||||
|
||||
" Jump
|
||||
let l:target = (s:use_vim_textprops ? b:lsp_reference_positions : w:lsp_reference_positions)[l:index][0:1]
|
||||
normal! m`
|
||||
call cursor(l:target[0], l:target[1])
|
||||
endfunction
|
||||
@@ -0,0 +1,278 @@
|
||||
" https://microsoft.github.io/language-server-protocol/specification#textDocument_hover
|
||||
|
||||
let s:Markdown = vital#lsp#import('VS.Vim.Syntax.Markdown')
|
||||
let s:MarkupContent = vital#lsp#import('VS.LSP.MarkupContent')
|
||||
let s:FloatingWindow = vital#lsp#import('VS.Vim.Window.FloatingWindow')
|
||||
let s:Window = vital#lsp#import('VS.Vim.Window')
|
||||
let s:Buffer = vital#lsp#import('VS.Vim.Buffer')
|
||||
|
||||
" options - {
|
||||
" server - 'server_name' " optional
|
||||
" ui - 'float' | 'preview'
|
||||
" }
|
||||
function! lsp#internal#document_hover#under_cursor#do(options) abort
|
||||
let l:bufnr = bufnr('%')
|
||||
let l:ui = get(a:options, 'ui', g:lsp_hover_ui)
|
||||
if empty(l:ui)
|
||||
let l:ui = s:FloatingWindow.is_available() ? 'float' : 'preview'
|
||||
endif
|
||||
|
||||
if l:ui ==# 'float'
|
||||
let l:doc_win = s:get_doc_win()
|
||||
if l:doc_win.is_visible()
|
||||
if bufnr('%') ==# l:doc_win.get_bufnr()
|
||||
call s:close_floating_window()
|
||||
else
|
||||
call l:doc_win.enter()
|
||||
inoremap <buffer><silent> <Plug>(lsp-float-close) <ESC>:<C-u>call <SID>close_floating_window()<CR>
|
||||
nnoremap <buffer><silent> <Plug>(lsp-float-close) :<C-u>call <SID>close_floating_window()<CR>
|
||||
execute('doautocmd <nomodeline> User lsp_float_focused')
|
||||
if !hasmapto('<Plug>(lsp-float-close)')
|
||||
imap <silent> <buffer> <C-c> <Plug>(lsp-float-close)
|
||||
nmap <silent> <buffer> <C-c> <Plug>(lsp-float-close)
|
||||
endif
|
||||
endif
|
||||
return
|
||||
endif
|
||||
endif
|
||||
|
||||
if has_key(a:options, 'server')
|
||||
let l:servers = [a:options['server']]
|
||||
else
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_hover_provider(v:val)')
|
||||
endif
|
||||
|
||||
if len(l:servers) == 0
|
||||
let l:filetype = getbufvar(l:bufnr, '&filetype')
|
||||
call lsp#utils#error('textDocument/hover not supported for ' . l:filetype)
|
||||
return
|
||||
endif
|
||||
|
||||
redraw | echo 'Retrieving hover ...'
|
||||
|
||||
call lsp#_new_command()
|
||||
|
||||
" TODO: ask user to select server for formatting if there are multiple servers
|
||||
let l:request = {
|
||||
\ 'method': 'textDocument/hover',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'position': lsp#get_position(),
|
||||
\ },
|
||||
\ }
|
||||
call lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromList(l:servers),
|
||||
\ lsp#callbag#flatMap({server->
|
||||
\ lsp#request(server, l:request)
|
||||
\ }),
|
||||
\ lsp#callbag#tap({x->s:show_hover(l:ui, x['server_name'], x['request'], x['response'])}),
|
||||
\ lsp#callbag#takeUntil(lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'command')}),
|
||||
\ )),
|
||||
\ lsp#callbag#subscribe(),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#document_hover#under_cursor#getpreviewwinid() abort
|
||||
if exists('s:doc_win')
|
||||
return s:doc_win.get_winid()
|
||||
endif
|
||||
return v:null
|
||||
endfunction
|
||||
|
||||
function! s:show_hover(ui, server_name, request, response) abort
|
||||
if !has_key(a:response, 'result') || empty(a:response['result']) ||
|
||||
\ empty(a:response['result']['contents'])
|
||||
call lsp#utils#error('No hover information found in server - ' . a:server_name)
|
||||
return
|
||||
endif
|
||||
|
||||
echo ''
|
||||
|
||||
if s:FloatingWindow.is_available() && a:ui ==? 'float'
|
||||
call s:show_floating_window(a:server_name, a:request, a:response)
|
||||
else
|
||||
call s:show_preview_window(a:server_name, a:request, a:response)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:show_preview_window(server_name, request, response) abort
|
||||
let l:contents = s:get_contents(a:response['result']['contents'])
|
||||
|
||||
" Ignore if contents is empty.
|
||||
if empty(l:contents)
|
||||
call lsp#utils#error('Empty contents for LspHover')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:lines = lsp#utils#_split_by_eol(join(l:contents, "\n\n"))
|
||||
let l:view = winsaveview()
|
||||
let l:alternate=@#
|
||||
silent! pclose
|
||||
sp LspHoverPreview
|
||||
execute 'resize '.min([len(l:lines), &previewheight])
|
||||
set previewwindow
|
||||
setlocal conceallevel=2
|
||||
setlocal bufhidden=hide
|
||||
setlocal nobuflisted
|
||||
setlocal buftype=nofile
|
||||
setlocal noswapfile
|
||||
%d _
|
||||
call setline(1, l:lines)
|
||||
call s:Window.do(win_getid(), {->s:Markdown.apply()})
|
||||
execute "normal \<c-w>p"
|
||||
call winrestview(l:view)
|
||||
let @#=l:alternate
|
||||
endfunction
|
||||
|
||||
function! s:show_floating_window(server_name, request, response) abort
|
||||
call s:close_floating_window()
|
||||
|
||||
let l:contents = s:get_contents(a:response['result']['contents'])
|
||||
|
||||
" Ignore if contents is empty.
|
||||
if empty(l:contents)
|
||||
return s:close_floating_window()
|
||||
endif
|
||||
|
||||
" Update contents.
|
||||
let l:doc_win = s:get_doc_win()
|
||||
silent! call deletebufline(l:doc_win.get_bufnr(), 1, '$')
|
||||
call setbufline(l:doc_win.get_bufnr(), 1, lsp#utils#_split_by_eol(join(l:contents, "\n\n")))
|
||||
|
||||
" Calculate layout.
|
||||
if g:lsp_float_max_width >= 1
|
||||
let l:maxwidth = g:lsp_float_max_width
|
||||
elseif g:lsp_float_max_width == 0
|
||||
let l:maxwidth = &columns
|
||||
else
|
||||
let l:maxwidth = float2nr(&columns * 0.4)
|
||||
endif
|
||||
let l:size = l:doc_win.get_size({
|
||||
\ 'maxwidth': l:maxwidth,
|
||||
\ 'maxheight': float2nr(&lines * 0.4),
|
||||
\ })
|
||||
let l:pos = s:compute_position(l:size)
|
||||
if empty(l:pos)
|
||||
call s:close_floating_window()
|
||||
return
|
||||
endif
|
||||
|
||||
execute printf('augroup vim_lsp_hover_close_on_move_%d', bufnr('%'))
|
||||
autocmd!
|
||||
execute printf('autocmd InsertEnter,BufLeave,CursorMoved <buffer> call s:close_floating_window_on_move(%s)', getcurpos())
|
||||
augroup END
|
||||
|
||||
" Show popupmenu and apply markdown syntax.
|
||||
call l:doc_win.open({
|
||||
\ 'row': l:pos[0],
|
||||
\ 'col': l:pos[1],
|
||||
\ 'width': l:size.width,
|
||||
\ 'height': l:size.height,
|
||||
\ 'border': v:true,
|
||||
\ })
|
||||
call s:Window.do(l:doc_win.get_winid(), { -> s:Markdown.apply() })
|
||||
|
||||
" Format contents to fit window
|
||||
call setbufvar(l:doc_win.get_bufnr(), '&textwidth', l:size.width)
|
||||
call s:Window.do(l:doc_win.get_winid(), { -> s:format_window() })
|
||||
endfunction
|
||||
|
||||
function! s:format_window() abort
|
||||
global/^/normal! gqgq
|
||||
endfunction
|
||||
|
||||
function! s:get_contents(contents) abort
|
||||
if type(a:contents) == type('')
|
||||
return [a:contents]
|
||||
elseif type(a:contents) == type([])
|
||||
let l:result = []
|
||||
for l:content in a:contents
|
||||
let l:result += s:get_contents(l:content)
|
||||
endfor
|
||||
return l:result
|
||||
elseif type(a:contents) == type({})
|
||||
if has_key(a:contents, 'value')
|
||||
if has_key(a:contents, 'kind')
|
||||
if a:contents['kind'] ==? 'markdown'
|
||||
let l:detail = s:MarkupContent.normalize(a:contents['value'], {
|
||||
\ 'compact': !g:lsp_preview_fixup_conceal
|
||||
\ })
|
||||
return [l:detail]
|
||||
else
|
||||
return [a:contents['value']]
|
||||
endif
|
||||
elseif has_key(a:contents, 'language')
|
||||
let l:detail = s:MarkupContent.normalize(a:contents, {
|
||||
\ 'compact': !g:lsp_preview_fixup_conceal
|
||||
\ })
|
||||
return [l:detail]
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:close_floating_window() abort
|
||||
call s:get_doc_win().close()
|
||||
endfunction
|
||||
|
||||
function! s:close_floating_window_on_move(curpos) abort
|
||||
if a:curpos != getcurpos() | call s:close_floating_window() | endif
|
||||
endf
|
||||
|
||||
function! s:on_opened() abort
|
||||
inoremap <buffer><silent> <Plug>(lsp-float-close) <ESC>:<C-u>call <SID>close_floating_window()<CR>
|
||||
nnoremap <buffer><silent> <Plug>(lsp-float-close) :<C-u>call <SID>close_floating_window()<CR>
|
||||
execute('doautocmd <nomodeline> User lsp_float_opened')
|
||||
if !hasmapto('<Plug>(lsp-float-close)')
|
||||
imap <silent> <buffer> <C-c> <Plug>(lsp-float-close)
|
||||
nmap <silent> <buffer> <C-c> <Plug>(lsp-float-close)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_closed() abort
|
||||
execute('doautocmd <nomodeline> User lsp_float_closed')
|
||||
endfunction
|
||||
|
||||
function! s:get_doc_win() abort
|
||||
if exists('s:doc_win')
|
||||
return s:doc_win
|
||||
endif
|
||||
|
||||
let s:doc_win = s:FloatingWindow.new({
|
||||
\ 'on_opened': function('s:on_opened'),
|
||||
\ 'on_closed': function('s:on_closed')
|
||||
\ })
|
||||
call s:doc_win.set_var('&wrap', 1)
|
||||
call s:doc_win.set_var('&conceallevel', 2)
|
||||
call s:doc_win.set_bufnr(s:Buffer.create())
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&buftype', 'nofile')
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&bufhidden', 'hide')
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&buflisted', 0)
|
||||
call setbufvar(s:doc_win.get_bufnr(), '&swapfile', 0)
|
||||
return s:doc_win
|
||||
endfunction
|
||||
|
||||
function! s:compute_position(size) abort
|
||||
let l:pos = screenpos(0, line('.'), col('.'))
|
||||
if l:pos.row == 0 && l:pos.col == 0
|
||||
" workaround for float position
|
||||
let l:pos = {'curscol': wincol(), 'row': winline()}
|
||||
endif
|
||||
let l:pos = [l:pos.row + 1, l:pos.curscol + 1]
|
||||
if l:pos[0] + a:size.height > &lines
|
||||
let l:pos[0] = l:pos[0] - a:size.height - 3
|
||||
endif
|
||||
if l:pos[1] + a:size.width > &columns
|
||||
let l:pos[1] = l:pos[1] - a:size.width - 3
|
||||
endif
|
||||
return l:pos
|
||||
endfunction
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
" options - {
|
||||
" bufnr: bufnr('%') " required
|
||||
" type: '' " optional: defaults to visualmode(). overridden by opfunc
|
||||
" server - 'server_name' " optional
|
||||
" sync: 0 " optional, defaults to 0 (async)
|
||||
" }
|
||||
function! lsp#internal#document_range_formatting#format(options) abort
|
||||
if has_key(a:options, 'server')
|
||||
let l:servers = [a:options['server']]
|
||||
else
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_document_range_formatting_provider(v:val)')
|
||||
endif
|
||||
|
||||
if len(l:servers) == 0
|
||||
let l:filetype = getbufvar(a:options['bufnr'], '&filetype')
|
||||
call lsp#utils#error('textDocument/rangeFormatting not supported for ' . l:filetype)
|
||||
return
|
||||
endif
|
||||
|
||||
" TODO: ask user to select server for formatting if there are multiple servers
|
||||
let l:server = l:servers[0]
|
||||
|
||||
redraw | echo 'Formatting Document Range ...'
|
||||
|
||||
call lsp#_new_command()
|
||||
|
||||
let [l:start_lnum, l:start_col, l:end_lnum, l:end_col] = s:get_selection_pos(get(a:options, 'type', visualmode()))
|
||||
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)
|
||||
|
||||
let l:request = {
|
||||
\ 'method': 'textDocument/rangeFormatting',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(a:options['bufnr']),
|
||||
\ 'range': {
|
||||
\ 'start': { 'line': l:start_lnum - 1, 'character': l:start_char },
|
||||
\ 'end': { 'line': l:end_lnum - 1, 'character': l:end_char },
|
||||
\ },
|
||||
\ 'options': {
|
||||
\ 'tabSize': lsp#utils#buffer#get_indent_size(a:options['bufnr']),
|
||||
\ 'insertSpaces': getbufvar(a:options['bufnr'], '&expandtab') ? v:true : v:false,
|
||||
\ }
|
||||
\ },
|
||||
\ 'bufnr': a:options['bufnr'],
|
||||
\ }
|
||||
|
||||
if get(a:options, 'sync', 0) == 1
|
||||
try
|
||||
let l:x = lsp#callbag#pipe(
|
||||
\ lsp#request(l:server, l:request),
|
||||
\ lsp#callbag#takeUntil(lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'command')}),
|
||||
\ )),
|
||||
\ lsp#callbag#toList(),
|
||||
\ ).wait({ 'sleep': get(a:options, 'sleep', 1), 'timeout': get(a:options, 'timeout', g:lsp_format_sync_timeout) })
|
||||
call s:format_next(l:x[0])
|
||||
call s:format_complete()
|
||||
catch
|
||||
call s:format_error(v:exception . ' ' . v:throwpoint)
|
||||
endtry
|
||||
else
|
||||
return lsp#callbag#pipe(
|
||||
\ lsp#request(l:server, l:request),
|
||||
\ lsp#callbag#takeUntil(lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'command')}),
|
||||
\ )),
|
||||
\ lsp#callbag#subscribe({
|
||||
\ 'next':{x->s:format_next(x)},
|
||||
\ 'error': {x->s:format_error(e)},
|
||||
\ 'complete': {->s:format_complete()},
|
||||
\ }),
|
||||
\ )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:format_next(x) abort
|
||||
if lsp#client#is_error(a:x['response']) | return | endif
|
||||
call lsp#utils#text_edit#apply_text_edits(a:x['request']['params']['textDocument']['uri'], a:x['response']['result'])
|
||||
endfunction
|
||||
|
||||
function! s:format_error(e) abort
|
||||
call lsp#log('Formatting Document Range Failed', a:e)
|
||||
call lsp#utils#error('Formatting Document Range Failed.' . (type(a:e) == type('') ? a:e : ''))
|
||||
endfunction
|
||||
|
||||
function! s:format_complete() abort
|
||||
redraw | echo 'Formatting Document Range complete'
|
||||
endfunction
|
||||
|
||||
function! s:get_selection_pos(type) abort
|
||||
" TODO: support bufnr
|
||||
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! lsp#internal#document_range_formatting#opfunc(type) abort
|
||||
call lsp#internal#document_range_formatting#format({
|
||||
\ 'type': a:type,
|
||||
\ 'bufnr': bufnr('%'),
|
||||
\ })
|
||||
endfunction
|
||||
@@ -0,0 +1,76 @@
|
||||
" https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol
|
||||
" options - {
|
||||
" bufnr: bufnr('%') " optional
|
||||
" server - 'server_name' " optional
|
||||
" }
|
||||
function! lsp#internal#document_symbol#search#do(options) abort
|
||||
let l:bufnr = get(a:options, 'bufnr', bufnr('%'))
|
||||
if has_key(a:options, 'server')
|
||||
let l:servers = [a:options['server']]
|
||||
else
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_document_symbol_provider(v:val)')
|
||||
endif
|
||||
|
||||
if len(l:servers) == 0
|
||||
let l:filetype = getbufvar(l:bufnr, '&filetype')
|
||||
call lsp#utils#error('textDocument/documentSymbol not supported for ' . l:filetype)
|
||||
return
|
||||
endif
|
||||
|
||||
redraw | echo 'Retrieving document symbols ...'
|
||||
|
||||
call lsp#internal#ui#quickpick#open({
|
||||
\ 'items': [],
|
||||
\ 'busy': 1,
|
||||
\ 'input': '',
|
||||
\ 'key': 'text',
|
||||
\ 'on_accept': function('s:on_accept'),
|
||||
\ 'on_close': function('s:on_close'),
|
||||
\ })
|
||||
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromList(l:servers),
|
||||
\ lsp#callbag#flatMap({server->
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#request(server, {
|
||||
\ 'method': 'textDocument/documentSymbol',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(l:bufnr),
|
||||
\ },
|
||||
\ }),
|
||||
\ lsp#callbag#map({x->{'server': server, 'request': x['request'], 'response': x['response']}}),
|
||||
\ )
|
||||
\ }),
|
||||
\ lsp#callbag#scan({acc, curr->add(acc, curr)}, []),
|
||||
\ lsp#callbag#tap({x->s:update_ui_items(x)}),
|
||||
\ lsp#callbag#subscribe({
|
||||
\ 'complete':{->lsp#internal#ui#quickpick#busy(0)},
|
||||
\ 'error':{e->s:on_error(e)},
|
||||
\ }),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! s:update_ui_items(x) abort
|
||||
let l:items = []
|
||||
for l:i in a:x
|
||||
let l:items += lsp#ui#vim#utils#symbols_to_loc_list(l:i['server'], l:i)
|
||||
endfor
|
||||
call lsp#internal#ui#quickpick#items(l:items)
|
||||
endfunction
|
||||
|
||||
function! s:on_accept(data, ...) abort
|
||||
call lsp#internal#ui#quickpick#close()
|
||||
call lsp#utils#location#_open_vim_list_item(a:data['items'][0], '')
|
||||
endfunction
|
||||
|
||||
function! s:on_close(...) abort
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_error(e) abort
|
||||
call lsp#internal#ui#quickpick#close()
|
||||
call lsp#log('LspDocumentSymbolSearch error', a:e)
|
||||
endfunction
|
||||
127
dot_vim/plugged/vim-lsp/autoload/lsp/internal/inlay_hints.vim
Normal file
127
dot_vim/plugged/vim-lsp/autoload/lsp/internal/inlay_hints.vim
Normal file
@@ -0,0 +1,127 @@
|
||||
let s:use_vim_textprops = lsp#utils#_has_vim_virtual_text() && !has('nvim')
|
||||
|
||||
function! s:set_inlay_hints(data) abort
|
||||
let l:bufnr = bufnr('%')
|
||||
|
||||
call s:clear_inlay_hints()
|
||||
|
||||
if mode() !=# 'n' | return | endif
|
||||
|
||||
if lsp#client#is_error(a:data['response']) | return | endif
|
||||
|
||||
" Get hints from the response
|
||||
let l:hints = a:data['response']['result']
|
||||
if empty(l:hints)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:not_curline = s:has_inlay_hints_mode('!curline')
|
||||
for l:hint in l:hints
|
||||
if l:not_curline && l:hint.position.line+1 ==# line('.')
|
||||
continue
|
||||
endif
|
||||
let l:label = ''
|
||||
if type(l:hint.label) ==# v:t_list
|
||||
let l:label = join(map(copy(l:hint.label), {_,v -> v.value}), '')
|
||||
else
|
||||
let l:label = l:hint.label
|
||||
endif
|
||||
let l:text = (get(l:hint, 'paddingLeft', v:false) ? ' ' : '') . l:label . (get(l:hint, 'paddingRight', v:false) ? ' ' : '')
|
||||
if !has_key(l:hint, 'kind') || l:hint.kind ==# 1
|
||||
call prop_add(l:hint.position.line+1, l:hint.position.character+1, {'type': 'vim_lsp_inlay_hint_type', 'text': l:text, 'bufnr': l:bufnr})
|
||||
elseif l:hint.kind ==# 2
|
||||
call prop_add(l:hint.position.line+1, l:hint.position.character+1, {'type': 'vim_lsp_inlay_hint_parameter', 'text': l:text, 'bufnr': l:bufnr})
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:init_inlay_hints() abort
|
||||
if index(prop_type_list(), 'vim_lsp_inlay_hint_type') ==# -1
|
||||
call prop_type_add('vim_lsp_inlay_hint_type', { 'highlight': 'lspInlayHintsType' })
|
||||
call prop_type_add('vim_lsp_inlay_hint_parameter', { 'highlight': 'lspInlayHintsParameter' })
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#inlay_hints#_disable() abort
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:clear_inlay_hints() abort
|
||||
let l:bufnr = bufnr('%')
|
||||
call prop_remove({'type': 'vim_lsp_inlay_hint_type', 'bufnr': l:bufnr, 'all': v:true})
|
||||
call prop_remove({'type': 'vim_lsp_inlay_hint_parameter', 'bufnr': l:bufnr, 'all': v:true})
|
||||
endfunction
|
||||
|
||||
function! s:has_inlay_hints_mode(value) abort
|
||||
let l:m = get(g:, 'lsp_inlay_hints_mode', {})
|
||||
if type(l:m) != v:t_dict | return v:false | endif
|
||||
if mode() ==# 'i'
|
||||
let l:a = get(l:m, 'insert', [])
|
||||
elseif mode() ==# 'n'
|
||||
let l:a = get(l:m, 'normal', [])
|
||||
else
|
||||
return v:false
|
||||
endif
|
||||
if type(l:a) != v:t_list | return v:false | endif
|
||||
return index(l:a, a:value) != -1 ? v:true : v:false
|
||||
endfunction
|
||||
|
||||
function! s:send_inlay_hints_request() abort
|
||||
let l:capability = 'lsp#capabilities#has_inlay_hint_provider(v:val)'
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capability)
|
||||
|
||||
if empty(l:servers)
|
||||
return lsp#callbag#empty()
|
||||
endif
|
||||
|
||||
if s:has_inlay_hints_mode('curline')
|
||||
let l:range = lsp#utils#range#get_range_curline()
|
||||
else
|
||||
let l:range = lsp#utils#range#get_range()
|
||||
endif
|
||||
return lsp#request(l:servers[0], {
|
||||
\ 'method': 'textDocument/inlayHint',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'range': l:range,
|
||||
\ },
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#inlay_hints#_enable() abort
|
||||
if !s:use_vim_textprops | return | endif
|
||||
if !g:lsp_inlay_hints_enabled | return | endif
|
||||
|
||||
if !hlexists('lspInlayHintsType')
|
||||
highlight link lspInlayHintsType Label
|
||||
endif
|
||||
if !hlexists('lspInlayHintsParameter')
|
||||
highlight link lspInlayHintsParameter Todo
|
||||
endif
|
||||
|
||||
call s:init_inlay_hints()
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#merge(
|
||||
\ lsp#callbag#fromEvent(['CursorMoved', 'CursorHold']),
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent(['InsertEnter', 'BufLeave']),
|
||||
\ lsp#callbag#tap({_ -> s:clear_inlay_hints() }),
|
||||
\ )
|
||||
\ ),
|
||||
\ lsp#callbag#filter({_ -> g:lsp_inlay_hints_enabled }),
|
||||
\ lsp#callbag#debounceTime(g:lsp_inlay_hints_delay),
|
||||
\ lsp#callbag#filter({_->getbufvar(bufnr('%'), '&buftype') !~# '^(help\|terminal\|prompt\|popup)$'}),
|
||||
\ lsp#callbag#switchMap({_->
|
||||
\ lsp#callbag#pipe(
|
||||
\ s:send_inlay_hints_request(),
|
||||
\ lsp#callbag#materialize(),
|
||||
\ lsp#callbag#filter({x->lsp#callbag#isNextNotification(x)}),
|
||||
\ lsp#callbag#map({x->x['value']})
|
||||
\ )
|
||||
\ }),
|
||||
\ lsp#callbag#subscribe({x->s:set_inlay_hints(x)}),
|
||||
\)
|
||||
endfunction
|
||||
411
dot_vim/plugged/vim-lsp/autoload/lsp/internal/semantic.vim
Normal file
411
dot_vim/plugged/vim-lsp/autoload/lsp/internal/semantic.vim
Normal file
@@ -0,0 +1,411 @@
|
||||
let s:use_vim_textprops = lsp#utils#_has_textprops() && !has('nvim')
|
||||
let s:use_nvim_highlight = lsp#utils#_has_nvim_buf_highlight()
|
||||
let s:textprop_cache = 'vim-lsp-semantic-cache'
|
||||
|
||||
if s:use_nvim_highlight
|
||||
let s:namespace_id = nvim_create_namespace('vim-lsp-semantic')
|
||||
endif
|
||||
|
||||
" Global functions {{{1
|
||||
function! lsp#internal#semantic#is_enabled() abort
|
||||
return g:lsp_semantic_enabled && (s:use_vim_textprops || s:use_nvim_highlight) ? v:true : v:false
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#semantic#_enable() abort
|
||||
if !lsp#internal#semantic#is_enabled() | return | endif
|
||||
|
||||
augroup lsp#internal#semantic
|
||||
autocmd!
|
||||
au User lsp_buffer_enabled call s:on_lsp_buffer_enabled()
|
||||
augroup END
|
||||
|
||||
let l:events = [['User', 'lsp_buffer_enabled'], 'TextChanged', 'TextChangedI']
|
||||
if exists('##TextChangedP')
|
||||
call add(l:events, 'TextChangedP')
|
||||
endif
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent(l:events),
|
||||
\ lsp#callbag#filter({_->lsp#internal#semantic#is_enabled()}),
|
||||
\ lsp#callbag#debounceTime(g:lsp_semantic_delay),
|
||||
\ lsp#callbag#filter({_->index(['help', 'terminal', 'prompt', 'popup'], getbufvar(bufnr('%'), '&buftype')) ==# -1}),
|
||||
\ lsp#callbag#filter({_->!lsp#utils#is_large_window(win_getid())}),
|
||||
\ lsp#callbag#switchMap({_->
|
||||
\ lsp#callbag#pipe(
|
||||
\ s:semantic_request(),
|
||||
\ lsp#callbag#materialize(),
|
||||
\ lsp#callbag#filter({x->lsp#callbag#isNextNotification(x)}),
|
||||
\ lsp#callbag#map({x->x['value']})
|
||||
\ )
|
||||
\ }),
|
||||
\ lsp#callbag#subscribe({x->s:handle_semantic_request(x)})
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#semantic#_disable() abort
|
||||
augroup lsp#internal#semantic
|
||||
autocmd!
|
||||
augroup END
|
||||
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#semantic#get_legend(server) abort
|
||||
if !lsp#capabilities#has_semantic_tokens(a:server)
|
||||
return {'tokenTypes': [], 'tokenModifiers': []}
|
||||
endif
|
||||
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server)
|
||||
return l:capabilities['semanticTokensProvider']['legend']
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#semantic#get_token_types() abort
|
||||
let l:capability = 'lsp#capabilities#has_semantic_tokens(v:val)'
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capability)
|
||||
|
||||
if empty(l:servers)
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:legend = lsp#internal#semantic#get_legend(l:servers[0])
|
||||
let l:token_types = l:legend['tokenTypes']
|
||||
call map(l:token_types, {_, type -> toupper(type[0]) . type[1:]})
|
||||
return l:token_types
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#semantic#get_token_modifiers() abort
|
||||
let l:capability = 'lsp#capabilities#has_semantic_tokens(v:val)'
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capability)
|
||||
|
||||
if empty(l:servers)
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:legend = lsp#internal#semantic#get_legend(l:servers[0])
|
||||
let l:token_modifiers = l:legend['tokenModifiers']
|
||||
call map(l:token_modifiers, {_, modifier -> toupper(modifier[0]) . modifier[1:]})
|
||||
return l:token_modifiers
|
||||
endfunction
|
||||
|
||||
function! s:on_lsp_buffer_enabled() abort
|
||||
augroup lsp#internal#semantic
|
||||
if !exists('#BufUnload#<buffer>')
|
||||
execute 'au BufUnload <buffer> call setbufvar(' . bufnr() . ', ''lsp_semantic_previous_result_id'', '''')'
|
||||
endif
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
function! s:supports_full_semantic_request(server) abort
|
||||
if !lsp#capabilities#has_semantic_tokens(a:server)
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server)['semanticTokensProvider']
|
||||
if !has_key(l:capabilities, 'full')
|
||||
return v:false
|
||||
endif
|
||||
|
||||
if type(l:capabilities['full']) ==# v:t_dict
|
||||
return v:true
|
||||
endif
|
||||
|
||||
return l:capabilities['full']
|
||||
endfunction
|
||||
|
||||
function! s:supports_delta_semantic_request(server) abort
|
||||
if !lsp#capabilities#has_semantic_tokens(a:server)
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server)['semanticTokensProvider']
|
||||
if !has_key(l:capabilities, 'full')
|
||||
return v:false
|
||||
endif
|
||||
|
||||
if type(l:capabilities['full']) !=# v:t_dict
|
||||
return v:false
|
||||
endif
|
||||
|
||||
if !has_key(l:capabilities['full'], 'delta')
|
||||
return v:false
|
||||
endif
|
||||
|
||||
return l:capabilities['full']['delta']
|
||||
endfunction
|
||||
|
||||
function! s:get_server() abort
|
||||
let l:capability = 's:supports_delta_semantic_request(v:val)'
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capability)
|
||||
if empty(l:servers)
|
||||
let l:capability = 's:supports_full_semantic_request(v:val)'
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capability)
|
||||
endif
|
||||
if empty(l:servers)
|
||||
return ''
|
||||
endif
|
||||
return l:servers[0]
|
||||
endfunction
|
||||
|
||||
function! s:semantic_request() abort
|
||||
let l:server = s:get_server()
|
||||
if l:server ==# ''
|
||||
return lsp#callbag#empty()
|
||||
endif
|
||||
|
||||
if (s:supports_delta_semantic_request(l:server)
|
||||
\ && getbufvar(bufnr(), 'lsp_semantic_previous_result_id') !=# '')
|
||||
return s:delta_semantic_request(l:server)
|
||||
else
|
||||
return s:full_semantic_request(l:server)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:full_semantic_request(server) abort
|
||||
return lsp#request(a:server, {
|
||||
\ 'method': 'textDocument/semanticTokens/full',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier()
|
||||
\ }})
|
||||
endfunction
|
||||
|
||||
function! s:delta_semantic_request(server) abort
|
||||
return lsp#request(a:server, {
|
||||
\ 'method': 'textDocument/semanticTokens/full/delta',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'previousResultId': getbufvar(bufname(), 'lsp_semantic_previous_result_id', 0)
|
||||
\ }})
|
||||
endfunction
|
||||
|
||||
" Highlight helper functions {{{1
|
||||
function! s:handle_semantic_request(data) abort
|
||||
if lsp#client#is_error(a:data['response'])
|
||||
call lsp#log('Skipping semantic highlight: response is invalid')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:server = a:data['server_name']
|
||||
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)
|
||||
|
||||
" Skip if the buffer doesn't exist. This might happen when a buffer is
|
||||
" opened and quickly deleted.
|
||||
if !bufloaded(l:bufnr) | return | endif
|
||||
|
||||
if has_key(a:data['response']['result'], 'data')
|
||||
call s:handle_semantic_tokens_response(l:server, l:bufnr, a:data['response']['result'])
|
||||
elseif has_key(a:data['response']['result'], 'edits')
|
||||
call s:handle_semantic_tokens_delta_response(l:server, l:bufnr, a:data['response']['result'])
|
||||
else
|
||||
" Don't update previous result ID if we could not update local copy
|
||||
call lsp#log('SemanticHighlight: unsupported semanticTokens method')
|
||||
return
|
||||
endif
|
||||
|
||||
if has_key(a:data['response']['result'], 'resultId')
|
||||
call setbufvar(l:bufnr, 'lsp_semantic_previous_result_id', a:data['response']['result']['resultId'])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:handle_semantic_tokens_response(server, buf, result) abort
|
||||
let l:highlights = {}
|
||||
let l:legend = lsp#internal#semantic#get_legend(a:server)
|
||||
for l:token in s:decode_tokens(a:result['data'])
|
||||
let [l:key, l:value] = s:add_highlight(a:server, l:legend, a:buf, l:token)
|
||||
let l:highlights[l:key] = get(l:highlights, l:key, []) + l:value
|
||||
endfor
|
||||
call s:apply_highlights(a:server, a:buf, l:highlights)
|
||||
|
||||
call setbufvar(a:buf, 'lsp_semantic_local_data', a:result['data'])
|
||||
endfunction
|
||||
|
||||
function! s:startpos_compare(edit1, edit2) abort
|
||||
return a:edit1[0] == a:edit2[0] ? 0 : a:edit1[0] > a:edit2[0] ? -1 : 1
|
||||
endfunction
|
||||
|
||||
function! s:handle_semantic_tokens_delta_response(server, buf, result) abort
|
||||
" The locations given in the edit are all referenced to the state before
|
||||
" any are applied and sorting is not required from the server,
|
||||
" therefore the edits must be sorted before applying.
|
||||
let l:edits = a:result['edits']
|
||||
call sort(l:edits, function('s:startpos_compare'))
|
||||
|
||||
let l:localdata = getbufvar(a:buf, 'lsp_semantic_local_data')
|
||||
for l:edit in l:edits
|
||||
let l:insertdata = get(l:edit, 'data', [])
|
||||
let l:localdata = l:localdata[:l:edit['start'] - 1]
|
||||
\ + l:insertdata
|
||||
\ + l:localdata[l:edit['start'] + l:edit['deleteCount']:]
|
||||
endfor
|
||||
call setbufvar(a:buf, 'lsp_semantic_local_data', l:localdata)
|
||||
|
||||
let l:highlights = {}
|
||||
let l:legend = lsp#internal#semantic#get_legend(a:server)
|
||||
for l:token in s:decode_tokens(l:localdata)
|
||||
let [l:key, l:value] = s:add_highlight(a:server, l:legend, a:buf, l:token)
|
||||
let l:highlights[l:key] = get(l:highlights, l:key, []) + l:value
|
||||
endfor
|
||||
call s:apply_highlights(a:server, a:buf, l:highlights)
|
||||
endfunction
|
||||
|
||||
function! s:decode_tokens(data) abort
|
||||
let l:tokens = []
|
||||
|
||||
let l:i = 0
|
||||
let l:line = 0
|
||||
let l:char = 0
|
||||
while l:i < len(a:data)
|
||||
let l:line = l:line + a:data[l:i]
|
||||
if a:data[l:i] > 0
|
||||
let l:char = 0
|
||||
endif
|
||||
let l:char = l:char + a:data[l:i + 1]
|
||||
|
||||
call add(l:tokens, {
|
||||
\ 'pos': {'line': l:line, 'character': l:char},
|
||||
\ 'length': a:data[l:i + 2],
|
||||
\ 'token_idx': a:data[l:i + 3],
|
||||
\ 'token_modifiers': a:data[l:i + 4]
|
||||
\ })
|
||||
|
||||
let l:i = l:i + 5
|
||||
endwhile
|
||||
|
||||
return l:tokens
|
||||
endfunction
|
||||
|
||||
function! s:clear_highlights(server, buf) abort
|
||||
if s:use_vim_textprops
|
||||
let l:BeginsWith = {str, prefix -> str[0:len(prefix) - 1] ==# prefix}
|
||||
let l:IsSemanticTextprop = {_, textprop -> l:BeginsWith(textprop, s:textprop_type_prefix)}
|
||||
let l:textprop_types = prop_type_list()
|
||||
call filter(l:textprop_types, l:IsSemanticTextprop)
|
||||
for l:textprop_type in l:textprop_types
|
||||
silent! call prop_remove({'type': l:textprop_type, 'bufnr': a:buf, 'all': v:true})
|
||||
endfor
|
||||
elseif s:use_nvim_highlight
|
||||
call nvim_buf_clear_namespace(a:buf, s:namespace_id, 0, line('$'))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:add_highlight(server, legend, buf, token) abort
|
||||
let l:startpos = lsp#utils#position#lsp_to_vim(a:buf, a:token['pos'])
|
||||
let l:endpos = a:token['pos']
|
||||
let l:endpos['character'] = l:endpos['character'] + a:token['length']
|
||||
let l:endpos = lsp#utils#position#lsp_to_vim(a:buf, l:endpos)
|
||||
|
||||
if s:use_vim_textprops
|
||||
let l:textprop_name = s:get_textprop_type(a:server, a:legend, a:token['token_idx'], a:token['token_modifiers'])
|
||||
return [l:textprop_name, [[l:startpos[0], l:startpos[1], l:endpos[0], l:endpos[1]]]]
|
||||
elseif s:use_nvim_highlight
|
||||
let l:char = a:token['pos']['character']
|
||||
let l:hl_name = s:get_hl_group(a:server, a:legend, a:token['token_idx'], a:token['token_modifiers'])
|
||||
return [l:hl_name, [[l:startpos[0] - 1, l:startpos[1] - 1, l:endpos[1] - 1]]]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:apply_highlights(server, buf, highlights) abort
|
||||
call s:clear_highlights(a:server, a:buf)
|
||||
|
||||
if s:use_vim_textprops
|
||||
for [l:type, l:prop_list] in items(a:highlights)
|
||||
call prop_add_list({'type': l:type, 'bufnr': a:buf}, l:prop_list)
|
||||
endfor
|
||||
elseif s:use_nvim_highlight
|
||||
call lsp#log(a:highlights)
|
||||
for [l:hl_name, l:instances] in items(a:highlights)
|
||||
for l:instance in l:instances
|
||||
let [l:line, l:startcol, l:endcol] = l:instance
|
||||
try
|
||||
call nvim_buf_add_highlight(a:buf, s:namespace_id, l:hl_name, l:line, l:startcol, l:endcol)
|
||||
catch
|
||||
call lsp#log('SemanticHighlight: error while adding ' . l:hl_name . ' highlight on line ' . l:line)
|
||||
endtry
|
||||
endfor
|
||||
endfor
|
||||
end
|
||||
endfunction
|
||||
|
||||
let s:hl_group_prefix = 'LspSemantic'
|
||||
|
||||
let s:default_highlight_groups = {
|
||||
\ s:hl_group_prefix . 'Type': 'Type',
|
||||
\ s:hl_group_prefix . 'Class': 'Type',
|
||||
\ s:hl_group_prefix . 'Enum': 'Type',
|
||||
\ s:hl_group_prefix . 'Interface': 'TypeDef',
|
||||
\ s:hl_group_prefix . 'Struct': 'Type',
|
||||
\ s:hl_group_prefix . 'TypeParameter': 'Type',
|
||||
\ s:hl_group_prefix . 'Parameter': 'Identifier',
|
||||
\ s:hl_group_prefix . 'Variable': 'Identifier',
|
||||
\ s:hl_group_prefix . 'Property': 'Identifier',
|
||||
\ s:hl_group_prefix . 'EnumMember': 'Constant',
|
||||
\ s:hl_group_prefix . 'Event': 'Identifier',
|
||||
\ s:hl_group_prefix . 'Function': 'Function',
|
||||
\ s:hl_group_prefix . 'Method': 'Function',
|
||||
\ s:hl_group_prefix . 'Macro': 'Macro',
|
||||
\ s:hl_group_prefix . 'Keyword': 'Keyword',
|
||||
\ s:hl_group_prefix . 'Modifier': 'Type',
|
||||
\ s:hl_group_prefix . 'Comment': 'Comment',
|
||||
\ s:hl_group_prefix . 'String': 'String',
|
||||
\ s:hl_group_prefix . 'Number': 'Number',
|
||||
\ s:hl_group_prefix . 'Regexp': 'String',
|
||||
\ s:hl_group_prefix . 'Operator': 'Operator',
|
||||
\ s:hl_group_prefix . 'Decorator': 'Macro'
|
||||
\ }
|
||||
|
||||
function! s:get_hl_group(server, legend, token_idx, token_modifiers) abort
|
||||
" get highlight group name
|
||||
let l:Capitalise = {str -> toupper(str[0]) . str[1:]}
|
||||
let l:token_name = l:Capitalise(a:legend['tokenTypes'][a:token_idx])
|
||||
let l:token_modifiers = []
|
||||
for l:modifier_idx in range(len(a:legend['tokenModifiers']))
|
||||
" float2nr(pow(2, a)) is 1 << a
|
||||
if and(a:token_modifiers, float2nr(pow(2, l:modifier_idx)))
|
||||
let l:modifier_name = a:legend['tokenModifiers'][l:modifier_idx]
|
||||
call add(l:token_modifiers, l:Capitalise(l:modifier_name))
|
||||
endif
|
||||
endfor
|
||||
call sort(l:token_modifiers)
|
||||
let l:hl_group = s:hl_group_prefix
|
||||
\ . reduce(l:token_modifiers, {acc, val -> acc . val}, '')
|
||||
\ . l:token_name
|
||||
|
||||
" create the highlight group if it does not already exist
|
||||
if !hlexists(l:hl_group)
|
||||
if has_key(s:default_highlight_groups, l:hl_group)
|
||||
exec 'highlight link' l:hl_group s:default_highlight_groups[l:hl_group]
|
||||
else
|
||||
if a:token_modifiers != 0
|
||||
let l:base_hl_group = s:get_hl_group(a:server, a:legend, a:token_idx, 0)
|
||||
exec 'highlight link' l:hl_group l:base_hl_group
|
||||
else
|
||||
exec 'highlight link' l:hl_group 'Normal'
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
return l:hl_group
|
||||
endfunction
|
||||
|
||||
let s:textprop_type_prefix = 'vim-lsp-semantic-'
|
||||
|
||||
function! s:get_textprop_type(server, legend, token_idx, token_modifiers) abort
|
||||
" get textprop type name
|
||||
let l:textprop_type = s:textprop_type_prefix . a:server . '-' . a:token_idx . '-' . a:token_modifiers
|
||||
|
||||
" create the textprop type if it does not already exist
|
||||
if prop_type_get(l:textprop_type) ==# {}
|
||||
let l:hl_group = s:get_hl_group(a:server, a:legend, a:token_idx, a:token_modifiers)
|
||||
silent! call prop_type_add(l:textprop_type, {
|
||||
\ 'highlight': l:hl_group,
|
||||
\ 'combine': v:true,
|
||||
\ 'priority': lsp#internal#textprop#priority('semantic')})
|
||||
endif
|
||||
|
||||
return l:textprop_type
|
||||
endfunction
|
||||
|
||||
" vim: fdm=marker
|
||||
@@ -0,0 +1,74 @@
|
||||
let s:ErrorType = 1
|
||||
let s:WarningType = 2
|
||||
let s:InfoType = 3
|
||||
let s:LogType = 4
|
||||
|
||||
function! lsp#internal#show_message#_enable() abort
|
||||
if g:lsp_show_message_log_level ==# 'none' | return | endif
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->
|
||||
\ g:lsp_show_message_log_level !=# 'none' &&
|
||||
\ has_key(x, 'response') && has_key(x['response'], 'method')
|
||||
\ && x['response']['method'] ==# 'window/showMessage'
|
||||
\ }),
|
||||
\ lsp#callbag#tap({x->s:handle_show_message(x['server'], x['response']['params'])}),
|
||||
\ lsp#callbag#subscribe({ 'error': function('s:on_error') }),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#show_message#_disable() abort
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_error(e) abort
|
||||
call lsp#log('lsp#internal#show_message error', a:e)
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:handle_show_message(server, params) abort
|
||||
let l:level = s:name_to_level(g:lsp_show_message_log_level)
|
||||
let l:type = a:params['type']
|
||||
if l:level < l:type
|
||||
return
|
||||
endif
|
||||
|
||||
let l:message = a:params['message']
|
||||
try
|
||||
if l:type == s:ErrorType
|
||||
echohl ErrorMsg
|
||||
elseif l:type == s:WarningType
|
||||
echohl WarningMsg
|
||||
endif
|
||||
echom printf('%s: %s: %s', a:server, s:type_to_name(l:type), l:message)
|
||||
finally
|
||||
echohl None
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:name_to_level(name) abort
|
||||
if a:name ==# 'none'
|
||||
return 0
|
||||
elseif a:name ==# 'error'
|
||||
return s:ErrorType
|
||||
elseif a:name ==# 'warn' || a:name ==# 'warning'
|
||||
return s:WarningType
|
||||
elseif a:name ==# 'info'
|
||||
return s:InfoType
|
||||
elseif a:name ==# 'log'
|
||||
return s:LogType
|
||||
else
|
||||
return 0
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:type_to_name(type) abort
|
||||
return get(['unknown', 'error', 'warning', 'info', 'log'], a:type, 'unknown')
|
||||
endfunction
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
function! lsp#internal#show_message_request#_enable() abort
|
||||
if !g:lsp_show_message_request_enabled | return | endif
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->
|
||||
\ g:lsp_show_message_request_enabled &&
|
||||
\ has_key(x, 'request') && !has_key(x, 'response') &&
|
||||
\ has_key(x['request'], 'method') && x['request']['method'] ==# 'window/showMessageRequest'
|
||||
\ }),
|
||||
\ lsp#callbag#map({x->s:show_message_request(x['server'], x['request'])}),
|
||||
\ lsp#callbag#map({x->s:send_message_response(x['server'], x['request'], x['action'])}),
|
||||
\ lsp#callbag#flatten(),
|
||||
\ lsp#callbag#materialize(),
|
||||
\ lsp#callbag#subscribe({ 'error': function('s:on_error') }),
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#show_message_request#_disable() abort
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_error(e) abort
|
||||
call lsp#log('lsp#internal#show_message_request error', a:e)
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:show_message_request(server_name, request) abort
|
||||
let l:params = a:request['params']
|
||||
|
||||
let l:selected_action = v:null
|
||||
|
||||
if has_key(l:params, 'actions') && !empty(l:params['actions'])
|
||||
let l:options = map(copy(l:params['actions']), {i, action ->
|
||||
\ printf('%d - [%s] %s', i + 1, a:server_name, action['title'])
|
||||
\ })
|
||||
let l:index = inputlist([l:params['message']] + l:options)
|
||||
if l:index > 0 && l:index <= len(l:index)
|
||||
let l:selected_action = l:params['actions'][l:index - 1]
|
||||
endif
|
||||
else
|
||||
echom l:params['message']
|
||||
endif
|
||||
|
||||
return { 'server': a:server_name, 'request': a:request, 'action': l:selected_action }
|
||||
endfunction
|
||||
|
||||
function! s:send_message_response(server_name, request, action) abort
|
||||
return lsp#request(a:server_name, {
|
||||
\ 'id': a:request['id'],
|
||||
\ 'result': a:action
|
||||
\})
|
||||
endfunction
|
||||
13
dot_vim/plugged/vim-lsp/autoload/lsp/internal/textprop.vim
Normal file
13
dot_vim/plugged/vim-lsp/autoload/lsp/internal/textprop.vim
Normal file
@@ -0,0 +1,13 @@
|
||||
" TODO: currently, quickpick is generated via vim-quickpick, 'quickpick' is
|
||||
" not used.
|
||||
let s:priorities = {
|
||||
\ 'quickpick': 1,
|
||||
\ 'folding': 2,
|
||||
\ 'semantic': 3,
|
||||
\ 'diagnostics_highlight': 4,
|
||||
\ 'document_highlight': 5,
|
||||
\}
|
||||
|
||||
function! lsp#internal#textprop#priority(name) abort
|
||||
return get(s:priorities, a:name, 0)
|
||||
endfunction
|
||||
@@ -0,0 +1,91 @@
|
||||
function! lsp#internal#type_hierarchy#show() abort
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_type_hierarchy_provider(v:val)')
|
||||
let l:command_id = lsp#_new_command()
|
||||
|
||||
if len(l:servers) == 0
|
||||
return lsp#utils#error('Retrieving type hierarchy not supported for ' . &filetype)
|
||||
endif
|
||||
|
||||
let l:ctx = { 'counter': len(l:servers), 'list':[], 'last_command_id': l:command_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! s:handle_type_hierarchy(ctx, server, type, data) abort "ctx = {counter, list, last_command_id}
|
||||
if a:ctx['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
|
||||
|
||||
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
|
||||
call lsp#utils#tagstack#_update()
|
||||
call lsp#utils#location#_open_lsp_location(a:hierarchyitem)
|
||||
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
|
||||
@@ -0,0 +1,39 @@
|
||||
let s:Markdown = vital#lsp#import('VS.Vim.Syntax.Markdown')
|
||||
let s:Window = vital#lsp#import('VS.Vim.Window')
|
||||
|
||||
function! lsp#internal#ui#popupmenu#open(opt) abort
|
||||
let l:Callback = remove(a:opt, 'callback')
|
||||
let l:items = remove(a:opt, 'items')
|
||||
|
||||
let l:items_with_shortcuts= map(l:items, {
|
||||
\ idx, item -> ((idx < 9) ? '['.(idx+1).'] ' : '').item
|
||||
\ })
|
||||
|
||||
function! Filter(id, key) abort closure
|
||||
if a:key >= 1 && a:key <= len(l:items)
|
||||
call popup_close(a:id, a:key)
|
||||
elseif a:key ==# "\<C-j>"
|
||||
call win_execute(a:id, 'normal! j')
|
||||
elseif a:key ==# "\<C-k>"
|
||||
call win_execute(a:id, 'normal! k')
|
||||
else
|
||||
return popup_filter_menu(a:id, a:key)
|
||||
endif
|
||||
|
||||
return v:true
|
||||
endfunction
|
||||
|
||||
let l:popup_opt = extend({
|
||||
\ 'callback': funcref('s:callback', [l:Callback]),
|
||||
\ 'filter': funcref('Filter'),
|
||||
\ }, a:opt)
|
||||
|
||||
let l:winid = popup_menu(l:items_with_shortcuts, l:popup_opt)
|
||||
call s:Window.do(l:winid, { -> s:Markdown.apply() })
|
||||
execute('doautocmd <nomodeline> User lsp_float_opened')
|
||||
endfunction
|
||||
|
||||
function! s:callback(callback, id, selected) abort
|
||||
call a:callback(a:id, a:selected)
|
||||
execute('doautocmd <nomodeline> User lsp_float_closed')
|
||||
endfunction
|
||||
461
dot_vim/plugged/vim-lsp/autoload/lsp/internal/ui/quickpick.vim
Normal file
461
dot_vim/plugged/vim-lsp/autoload/lsp/internal/ui/quickpick.vim
Normal file
@@ -0,0 +1,461 @@
|
||||
" https://github.com/prabirshrestha/quickpick.vim#968f00787c1a118228aee869351e754bec555298
|
||||
" :QuickpickEmbed path=autoload/lsp/internal/ui/quickpick.vim namespace=lsp#internal#ui#quickpick prefix=lsp-quickpick
|
||||
|
||||
let s:has_timer = exists('*timer_start') && exists('*timer_stop')
|
||||
let s:has_matchfuzzy = exists('*matchfuzzy')
|
||||
let s:has_matchfuzzypos = exists('*matchfuzzypos')
|
||||
let s:has_proptype = exists('*prop_type_add') && exists('*prop_type_delete')
|
||||
|
||||
"
|
||||
" is_floating
|
||||
"
|
||||
if has('nvim')
|
||||
function! s:is_floating(winid) abort
|
||||
if !s:win_exists(a:winid)
|
||||
return 0
|
||||
endif
|
||||
let l:config = nvim_win_get_config(a:winid)
|
||||
return empty(l:config) || !empty(get(l:config, 'relative', ''))
|
||||
endfunction
|
||||
else
|
||||
function! s:is_floating(winid) abort
|
||||
return s:win_exists(a:winid) && win_id2win(a:winid) == 0
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! s:win_exists(winid) abort
|
||||
return winheight(a:winid) != -1
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#ui#quickpick#open(opt) abort
|
||||
call lsp#internal#ui#quickpick#close() " hide existing picker if exists
|
||||
|
||||
" when key is empty, item is a string else it is a dict
|
||||
" fitems is filtered items and is the item that is filtered
|
||||
let s:state = extend({
|
||||
\ 'items': [],
|
||||
\ 'highlights': [],
|
||||
\ 'fitems': [],
|
||||
\ 'key': '',
|
||||
\ 'busy': 0,
|
||||
\ 'busyframes': ['-', '\', '|', '/'],
|
||||
\ 'filetype': 'lsp-quickpick',
|
||||
\ 'promptfiletype': 'lsp-quickpick-filter',
|
||||
\ 'input': '',
|
||||
\ 'maxheight': 10,
|
||||
\ 'debounce': 250,
|
||||
\ 'filter': 1,
|
||||
\ }, a:opt)
|
||||
|
||||
let s:inputecharpre = 0
|
||||
let s:state['busyframe'] = 0
|
||||
|
||||
let s:state['bufnr'] = bufnr('%')
|
||||
let s:state['winid'] = win_getid()
|
||||
let s:state['wininfo'] = getwininfo()
|
||||
|
||||
" create result buffer
|
||||
exe printf('keepalt botright 3new %s', s:state['filetype'])
|
||||
let s:state['resultsbufnr'] = bufnr('%')
|
||||
let s:state['resultswinid'] = win_getid()
|
||||
if s:has_proptype
|
||||
call prop_type_add('highlight', { 'highlight': 'Directory', 'bufnr': s:state['resultsbufnr'] })
|
||||
endif
|
||||
|
||||
" create prompt buffer
|
||||
exe printf('keepalt botright 1new %s', s:state['promptfiletype'])
|
||||
let s:state['promptbufnr'] = bufnr('%')
|
||||
let s:state['promptwinid'] = win_getid()
|
||||
|
||||
call win_gotoid(s:state['resultswinid'])
|
||||
call s:set_buffer_options()
|
||||
setlocal cursorline
|
||||
call s:update_items()
|
||||
exec printf('setlocal filetype=' . s:state['filetype'])
|
||||
call s:notify('open', { 'bufnr': s:state['bufnr'], 'winid': s:state['winid'] , 'resultsbufnr': s:state['resultsbufnr'], 'resultswinid': s:state['resultswinid'] })
|
||||
|
||||
call win_gotoid(s:state['promptwinid'])
|
||||
call s:set_buffer_options()
|
||||
call setline(1, s:state['input'])
|
||||
|
||||
" map keys
|
||||
inoremap <buffer><silent> <Plug>(lsp-quickpick-accept) <ESC>:<C-u>call <SID>on_accept()<CR>
|
||||
nnoremap <buffer><silent> <Plug>(lsp-quickpick-accept) :<C-u>call <SID>on_accept()<CR>
|
||||
|
||||
inoremap <buffer><silent> <Plug>(lsp-quickpick-close) <ESC>:<C-u>call lsp#internal#ui#quickpick#close()<CR>
|
||||
nnoremap <buffer><silent> <Plug>(lsp-quickpick-close) :<C-u>call lsp#internal#ui#quickpick#close()<CR>
|
||||
|
||||
inoremap <buffer><silent> <Plug>(lsp-quickpick-cancel) <ESC>:<C-u>call <SID>on_cancel()<CR>
|
||||
nnoremap <buffer><silent> <Plug>(lsp-quickpick-cancel) :<C-u>call <SID>on_cancel()<CR>
|
||||
|
||||
inoremap <buffer><silent> <Plug>(lsp-quickpick-move-next) <C-o>:<C-u>call <SID>on_move_next()<CR>
|
||||
nnoremap <buffer><silent> <Plug>(lsp-quickpick-move-next) :<C-u>call <SID>on_move_next()<CR>
|
||||
|
||||
inoremap <buffer><silent> <Plug>(lsp-quickpick-move-previous) <C-o>:<C-u>call <SID>on_move_previous()<CR>
|
||||
nnoremap <buffer><silent> <Plug>(lsp-quickpick-move-previous) :<C-u>call <SID>on_move_previous()<CR>
|
||||
|
||||
exec printf('setlocal filetype=' . s:state['promptfiletype'])
|
||||
|
||||
if !hasmapto('<Plug>(lsp-quickpick-accept)')
|
||||
imap <buffer><cr> <Plug>(lsp-quickpick-accept)
|
||||
nmap <buffer><cr> <Plug>(lsp-quickpick-accept)
|
||||
endif
|
||||
|
||||
if !hasmapto('<Plug>(lsp-quickpick-cancel)')
|
||||
imap <silent> <buffer> <C-c> <Plug>(lsp-quickpick-cancel)
|
||||
map <silent> <buffer> <C-c> <Plug>(lsp-quickpick-cancel)
|
||||
imap <silent> <buffer> <Esc> <Plug>(lsp-quickpick-cancel)
|
||||
map <silent> <buffer> <Esc> <Plug>(lsp-quickpick-cancel)
|
||||
endif
|
||||
|
||||
if !hasmapto('<Plug>(lsp-quickpick-move-next)')
|
||||
imap <silent> <buffer> <C-n> <Plug>(lsp-quickpick-move-next)
|
||||
nmap <silent> <buffer> <C-n> <Plug>(lsp-quickpick-move-next)
|
||||
imap <silent> <buffer> <C-j> <Plug>(lsp-quickpick-move-next)
|
||||
nmap <silent> <buffer> <C-j> <Plug>(lsp-quickpick-move-next)
|
||||
endif
|
||||
|
||||
if !hasmapto('<Plug>(lsp-quickpick-move-previous)')
|
||||
imap <silent> <buffer> <C-p> <Plug>(lsp-quickpick-move-previous)
|
||||
nmap <silent> <buffer> <C-p> <Plug>(lsp-quickpick-move-previous)
|
||||
imap <silent> <buffer> <C-k> <Plug>(lsp-quickpick-move-previous)
|
||||
nmap <silent> <buffer> <C-k> <Plug>(lsp-quickpick-move-previous)
|
||||
endif
|
||||
|
||||
call cursor(line('$'), 0)
|
||||
call feedkeys('i', 'n')
|
||||
|
||||
augroup lsp#internal#ui#quickpick
|
||||
autocmd!
|
||||
autocmd InsertCharPre <buffer> call s:on_insertcharpre()
|
||||
autocmd TextChangedI <buffer> call s:on_inputchanged()
|
||||
autocmd InsertEnter <buffer> call s:on_insertenter()
|
||||
autocmd InsertLeave <buffer> call s:on_insertleave()
|
||||
|
||||
if exists('##TextChangedP')
|
||||
autocmd TextChangedP <buffer> call s:on_inputchanged()
|
||||
endif
|
||||
augroup END
|
||||
|
||||
call s:notify_items()
|
||||
call s:notify_selection()
|
||||
call lsp#internal#ui#quickpick#busy(s:state['busy'])
|
||||
endfunction
|
||||
|
||||
function! s:set_buffer_options() abort
|
||||
" set buffer options
|
||||
abc <buffer>
|
||||
setlocal bufhidden=unload " unload buf when no longer displayed
|
||||
setlocal buftype=nofile " buffer is not related to any file<Paste>
|
||||
setlocal noswapfile " don't create swap file
|
||||
setlocal nowrap " don't soft-wrap
|
||||
setlocal nonumber " don't show line numbers
|
||||
setlocal nolist " don't use list mode (visible tabs etc)
|
||||
setlocal foldcolumn=0 " don't show a fold column at side
|
||||
setlocal foldlevel=99 " don't fold anything
|
||||
setlocal nospell " spell checking off
|
||||
setlocal nobuflisted " don't show up in the buffer list
|
||||
setlocal textwidth=0 " don't hardwarp (break long lines)
|
||||
setlocal nocursorline " highlight the line cursor is off
|
||||
setlocal nocursorcolumn " disable cursor column
|
||||
setlocal noundofile " don't enable undo
|
||||
setlocal winfixheight
|
||||
if exists('+colorcolumn') | setlocal colorcolumn=0 | endif
|
||||
if exists('+relativenumber') | setlocal norelativenumber | endif
|
||||
setlocal signcolumn=yes " for prompt
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#ui#quickpick#close() abort
|
||||
if !exists('s:state')
|
||||
return
|
||||
endif
|
||||
|
||||
call lsp#internal#ui#quickpick#busy(0)
|
||||
|
||||
call win_gotoid(s:state['winid'])
|
||||
call s:notify('close', { 'bufnr': s:state['bufnr'], 'winid': s:state['winid'], 'resultsbufnr': s:state['resultsbufnr'], 'resultswinid': s:state['winid'] })
|
||||
|
||||
augroup lsp#internal#ui#quickpick
|
||||
autocmd!
|
||||
augroup END
|
||||
|
||||
exe 'silent! bunload! ' . s:state['promptbufnr']
|
||||
exe 'silent! bunload! ' . s:state['resultsbufnr']
|
||||
call s:restore_windows()
|
||||
|
||||
let s:inputecharpre = 0
|
||||
|
||||
unlet s:state
|
||||
endfunction
|
||||
|
||||
function! s:restore_windows() abort
|
||||
let [l:tabnr, l:_] = win_id2tabwin(s:state['winid'])
|
||||
if l:tabnr == 0
|
||||
return
|
||||
endif
|
||||
|
||||
let l:Resizable = {_, info ->
|
||||
\ info.tabnr == l:tabnr &&
|
||||
\ s:win_exists(info.winid) &&
|
||||
\ !s:is_floating(info.winid)
|
||||
\ }
|
||||
let l:wins_to_resize = sort(filter(s:state['wininfo'], l:Resizable), {l, r -> l.winnr - r.winnr})
|
||||
let l:open_winids_to_resize = map(filter(getwininfo(), l:Resizable), {_, info -> info.winid})
|
||||
|
||||
let l:resize_cmd = ''
|
||||
for l:info in l:wins_to_resize
|
||||
if index(l:open_winids_to_resize, l:info.winid) == -1
|
||||
return
|
||||
endif
|
||||
|
||||
let l:resize_cmd .= printf('%dresize %d | vert %dresize %d |', l:info.winnr, l:info.height, l:info.winnr, l:info.width)
|
||||
endfor
|
||||
|
||||
" winrestcmd repeats :resize commands twice after patch-8.2.2631.
|
||||
" To simulate this behavior, execute the :resize commands twice.
|
||||
" see https://github.com/vim/vim/issues/7988
|
||||
exe 'silent! ' . l:resize_cmd . l:resize_cmd
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#ui#quickpick#items(items) abort
|
||||
let s:state['items'] = a:items
|
||||
call s:update_items()
|
||||
call s:notify_items()
|
||||
call s:notify_selection()
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#ui#quickpick#busy(busy) abort
|
||||
let s:state['busy'] = a:busy
|
||||
if a:busy
|
||||
if !has_key(s:state, 'busytimer')
|
||||
let s:state['busyframe'] = 0
|
||||
let s:state['busytimer'] = timer_start(60, function('s:busy_tick'), { 'repeat': -1 })
|
||||
endif
|
||||
else
|
||||
if has_key(s:state, 'busytimer')
|
||||
call timer_stop(s:state['busytimer'])
|
||||
call remove(s:state, 'busytimer')
|
||||
redraw
|
||||
echohl None
|
||||
echo ''
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#ui#quickpick#results_winid() abort
|
||||
if exists('s:state')
|
||||
return s:state['resultswinid']
|
||||
else
|
||||
return 0
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:busy_tick(...) abort
|
||||
let s:state['busyframe'] = s:state['busyframe'] + 1
|
||||
if s:state['busyframe'] >= len(s:state['busyframes'])
|
||||
let s:state['busyframe'] = 0
|
||||
endif
|
||||
redraw
|
||||
echohl Question | echon s:state['busyframes'][s:state['busyframe']]
|
||||
echohl None
|
||||
endfunction
|
||||
|
||||
function! s:update_items() abort
|
||||
call s:win_execute(s:state['resultswinid'], 'silent! %delete _')
|
||||
|
||||
let s:state['highlights'] = []
|
||||
|
||||
if s:state['filter'] " if filter is enabled
|
||||
if empty(s:trim(s:state['input']))
|
||||
let s:state['fitems'] = s:state['items']
|
||||
else
|
||||
if empty(s:state['key']) " item is string
|
||||
if s:has_matchfuzzypos
|
||||
let l:matchfuzzyresult = matchfuzzypos(s:state['items'], s:state['input'])
|
||||
let l:fitems = l:matchfuzzyresult[0]
|
||||
let l:highlights = l:matchfuzzyresult[1]
|
||||
let s:state['fitems'] = l:fitems
|
||||
let s:state['highlights'] = l:highlights
|
||||
elseif s:has_matchfuzzy
|
||||
let s:state['fitems'] = matchfuzzy(s:state['items'], s:state['input'])
|
||||
else
|
||||
let s:state['fitems'] = filter(copy(s:state['items']), 'stridx(toupper(v:val), toupper(s:state["input"])) >= 0')
|
||||
endif
|
||||
else " item is dict
|
||||
if s:has_matchfuzzypos
|
||||
" vim requires matchfuzzypos to have highlights.
|
||||
" matchfuzzy only patch doesn't support dict search
|
||||
let l:matchfuzzyresult = matchfuzzypos(s:state['items'], s:state['input'], { 'key': s:state['key'] })
|
||||
let l:fitems = l:matchfuzzyresult[0]
|
||||
let l:highlights = l:matchfuzzyresult[1]
|
||||
let s:state['fitems'] = l:fitems
|
||||
let s:state['highlights'] = l:highlights
|
||||
else
|
||||
let s:state['fitems'] = filter(copy(s:state['items']), 'stridx(toupper(v:val[s:state["key"]]), toupper(s:state["input"])) >= 0')
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
else " if filter is disabled
|
||||
let s:state['fitems'] = s:state['items']
|
||||
endif
|
||||
|
||||
|
||||
if empty(s:state['key']) " item is string
|
||||
let l:lines = s:state['fitems']
|
||||
else " item is dict
|
||||
let l:lines = map(copy(s:state['fitems']), 'v:val[s:state["key"]]')
|
||||
endif
|
||||
|
||||
call setbufline(s:state['resultsbufnr'], 1, l:lines)
|
||||
|
||||
if s:has_proptype && !empty(s:state['highlights'])
|
||||
let l:i = 0
|
||||
for l:line in s:state['highlights']
|
||||
for l:pos in l:line
|
||||
let l:cs = split(getbufline(s:state['resultsbufnr'], l:i + 1)[0], '\zs')
|
||||
let l:mpos = strlen(join(l:cs[: l:pos - 1], ''))
|
||||
let l:len = strlen(l:cs[l:pos])
|
||||
call prop_add(l:i + 1, l:mpos + 1, { 'length': l:len, 'type': 'highlight', 'bufnr': s:state['resultsbufnr'] })
|
||||
endfor
|
||||
let l:i += 1
|
||||
endfor
|
||||
endif
|
||||
|
||||
call s:win_execute(s:state['resultswinid'], printf('resize %d', min([len(s:state['fitems']), s:state['maxheight']])))
|
||||
call s:win_execute(s:state['promptwinid'], 'resize 1')
|
||||
endfunction
|
||||
|
||||
function! s:on_accept() abort
|
||||
if win_gotoid(s:state['resultswinid'])
|
||||
let l:index = line('.') - 1 " line is 1 index, list is 0 index
|
||||
let l:fitems = s:state['fitems']
|
||||
if l:index < 0 || len(l:fitems) <= l:index
|
||||
let l:items = []
|
||||
else
|
||||
let l:items = [l:fitems[l:index]]
|
||||
endif
|
||||
call win_gotoid(s:state['winid'])
|
||||
call s:notify('accept', { 'items': l:items })
|
||||
end
|
||||
endfunction
|
||||
|
||||
function! s:on_cancel() abort
|
||||
call win_gotoid(s:state['winid'])
|
||||
call s:notify('cancel', {})
|
||||
call lsp#internal#ui#quickpick#close()
|
||||
endfunction
|
||||
|
||||
function! s:on_move_next() abort
|
||||
let l:col = col('.')
|
||||
call s:win_execute(s:state['resultswinid'], 'normal! j')
|
||||
call s:notify_selection()
|
||||
endfunction
|
||||
|
||||
function! s:on_move_previous() abort
|
||||
let l:col = col('.')
|
||||
call s:win_execute(s:state['resultswinid'], 'normal! k')
|
||||
call s:notify_selection()
|
||||
endfunction
|
||||
|
||||
function! s:notify_items() abort
|
||||
" items could be huge, so don't send the items as part of data
|
||||
call s:notify('items', { 'bufnr': s:state['bufnr'], 'winid': s:state['winid'], 'resultsbufnr': s:state['resultsbufnr'], 'resultswinid': s:state['resultswinid'] })
|
||||
endfunction
|
||||
|
||||
function! s:notify_selection() abort
|
||||
let l:original_winid = win_getid()
|
||||
call win_gotoid(s:state['resultswinid'])
|
||||
let l:index = line('.') - 1 " line is 1 based, list is 0 based
|
||||
if l:index < 0 || ((l:index + 1) > len(s:state['fitems']))
|
||||
let l:items = []
|
||||
else
|
||||
let l:items = [s:state['fitems'][l:index]]
|
||||
endif
|
||||
let l:data = {
|
||||
\ 'bufnr': s:state['bufnr'],
|
||||
\ 'winid': s:state['winid'],
|
||||
\ 'resultsbufnr': s:state['resultsbufnr'],
|
||||
\ 'resultswinid': s:state['resultswinid'],
|
||||
\ 'items': l:items,
|
||||
\ }
|
||||
noautocmd call win_gotoid(s:state['winid'])
|
||||
call s:notify('selection', l:data)
|
||||
noautocmd call win_gotoid(l:original_winid)
|
||||
endfunction
|
||||
|
||||
function! s:on_inputchanged() abort
|
||||
if s:inputecharpre
|
||||
if s:has_timer && s:state['debounce'] > 0
|
||||
call s:debounce_onchange()
|
||||
else
|
||||
call s:notify_onchange()
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_insertcharpre() abort
|
||||
let s:inputecharpre = 1
|
||||
endfunction
|
||||
|
||||
function! s:on_insertenter() abort
|
||||
let s:inputecharpre = 0
|
||||
endfunction
|
||||
|
||||
function! s:on_insertleave() abort
|
||||
if s:has_timer && has_key(s:state, 'debounce_onchange_timer')
|
||||
call timer_stop(s:state['debounce_onchange_timer'])
|
||||
call remove(s:state, 'debounce_onchange_timer')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:debounce_onchange() abort
|
||||
if has_key(s:state, 'debounce_onchange_timer')
|
||||
call timer_stop(s:state['debounce_onchange_timer'])
|
||||
call remove(s:state, 'debounce_onchange_timer')
|
||||
endif
|
||||
let s:state['debounce_onchange_timer'] = timer_start(s:state['debounce'], function('s:notify_onchange'))
|
||||
endfunction
|
||||
|
||||
function! s:notify_onchange(...) abort
|
||||
let s:state['input'] = getbufline(s:state['promptbufnr'], 1)[0]
|
||||
call s:notify('change', { 'input': s:state['input'] })
|
||||
if s:state['filter']
|
||||
call s:update_items()
|
||||
call s:notify_selection()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:notify(name, data) abort
|
||||
if has_key(s:state, 'on_event') | call s:state['on_event'](a:data, a:name) | endif
|
||||
if has_key(s:state, 'on_' . a:name) | call s:state['on_' . a:name](a:data, a:name) | endif
|
||||
endfunction
|
||||
|
||||
if exists('*win_execute')
|
||||
function! s:win_execute(win_id, cmd) abort
|
||||
call win_execute(a:win_id, a:cmd)
|
||||
endfunction
|
||||
else
|
||||
function! s:win_execute(winid, cmd) abort
|
||||
let l:original_winid = win_getid()
|
||||
if l:original_winid == a:winid
|
||||
exec a:cmd
|
||||
else
|
||||
if win_gotoid(a:winid)
|
||||
exec a:cmd
|
||||
call win_gotoid(l:original_winid)
|
||||
end
|
||||
endif
|
||||
endfunction
|
||||
endif
|
||||
|
||||
if exists('*trim')
|
||||
function! s:trim(str) abort
|
||||
return trim(a:str)
|
||||
endfunction
|
||||
else
|
||||
function! s:trim(str) abort
|
||||
return substitute(a:str, '^\s*\|\s*$', '', 'g')
|
||||
endfunction
|
||||
endif
|
||||
|
||||
" vim: set sw=2 ts=2 sts=2 et tw=78 foldmarker={{{,}}} foldmethod=marker spell:
|
||||
@@ -0,0 +1,70 @@
|
||||
" https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
|
||||
|
||||
let s:progress_ui = []
|
||||
let s:enabled = 0
|
||||
|
||||
function! lsp#internal#work_done_progress#_enable() abort
|
||||
if !g:lsp_work_done_progress_enabled | return | endif
|
||||
|
||||
if s:enabled | return | endif
|
||||
let s:enabled = 1
|
||||
let s:progress_ui = []
|
||||
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#stream(),
|
||||
\ lsp#callbag#filter({x->has_key(x, 'response') && has_key(x['response'], 'method')
|
||||
\ && x['response']['method'] ==# '$/progress' && has_key(x['response'], 'params')
|
||||
\ && has_key(x['response']['params'], 'value') && type(x['response']['params']['value']) == type({})}),
|
||||
\ lsp#callbag#subscribe({'next': {x->s:handle_work_done_progress(x['server'], x['response'])}})
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! s:handle_work_done_progress(server, response) abort
|
||||
let l:value = a:response['params']['value']
|
||||
let l:token = a:response['params']['token']
|
||||
let l:new = {
|
||||
\ 'server': a:server,
|
||||
\ 'token': l:token,
|
||||
\ 'title': '',
|
||||
\ 'message': '',
|
||||
\ }
|
||||
|
||||
if l:value['kind'] ==# 'end'
|
||||
let l:new['message'] = ''
|
||||
let l:new['percentage'] = 100
|
||||
call filter(s:progress_ui, {_, x->x['token'] !=# l:token || x['server'] !=# a:server})
|
||||
elseif l:value['kind'] ==# 'begin'
|
||||
let l:new['title'] = l:value['title']
|
||||
call filter(s:progress_ui, {_, x->x['token'] !=# l:token || x['server'] !=# a:server})
|
||||
call insert(s:progress_ui, l:new)
|
||||
elseif l:value['kind'] ==# 'report'
|
||||
let l:new['message'] = get(l:value, 'message', '')
|
||||
if has_key(l:value, 'percentage')
|
||||
" l:value['percentage'] is uinteger in specification.
|
||||
" But some implementation return float. (e.g. clangd11)
|
||||
" So we round it.
|
||||
let l:new['percentage'] = float2nr(l:value['percentage'] + 0.5)
|
||||
endif
|
||||
let l:idx = match(s:progress_ui, l:token)
|
||||
let l:new['title'] = s:progress_ui[l:idx]['title']
|
||||
call filter(s:progress_ui, {_, x->x['token'] !=# l:token || x['server'] !=# a:server})
|
||||
call insert(s:progress_ui, l:new)
|
||||
endif
|
||||
doautocmd <nomodeline> User lsp_progress_updated
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#work_done_progress#_disable() abort
|
||||
if !s:enabled | return | endif
|
||||
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
|
||||
let s:enabled = 0
|
||||
let s:progress_ui = []
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#work_done_progress#get_progress() abort
|
||||
return s:progress_ui
|
||||
endfunction
|
||||
@@ -0,0 +1,94 @@
|
||||
" https://microsoft.github.io/language-server-protocol/specification#workspace_symbol
|
||||
" options - {
|
||||
" bufnr: bufnr('%') " optional
|
||||
" server - 'server_name' " optional
|
||||
" query: '' " optional
|
||||
" }
|
||||
function! lsp#internal#workspace_symbol#search#do(options) abort
|
||||
if has_key(a:options, 'server')
|
||||
let l:servers = [a:options['server']]
|
||||
else
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_document_symbol_provider(v:val)')
|
||||
endif
|
||||
|
||||
if len(l:servers) == 0
|
||||
echom 'textDocument/workspaceSymbol not supported'
|
||||
call lsp#utils#error('textDocument/workspaceSymbol not supported')
|
||||
return
|
||||
endif
|
||||
|
||||
redraw | echo 'Retrieving workspace symbols ...'
|
||||
|
||||
let l:TextChangeSubject = lsp#callbag#makeSubject()
|
||||
|
||||
" use callbag debounce instead of quickpick debounce
|
||||
call lsp#internal#ui#quickpick#open({
|
||||
\ 'items': [],
|
||||
\ 'input': get(a:options, 'query', ''),
|
||||
\ 'key': 'text',
|
||||
\ 'debounce': 0,
|
||||
\ 'on_change': function('s:on_change', [l:TextChangeSubject]),
|
||||
\ 'on_accept': function('s:on_accept'),
|
||||
\ 'on_close': function('s:on_close'),
|
||||
\ })
|
||||
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ l:TextChangeSubject,
|
||||
\ lsp#callbag#debounceTime(250),
|
||||
\ lsp#callbag#distinctUntilChanged(),
|
||||
\ lsp#callbag#switchMap({query->
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromList(l:servers),
|
||||
\ lsp#callbag#tap({_->lsp#internal#ui#quickpick#busy(1)}),
|
||||
\ lsp#callbag#flatMap({server->
|
||||
\ lsp#callbag#pipe(
|
||||
\ lsp#request(server, {
|
||||
\ 'method': 'workspace/symbol',
|
||||
\ 'params': {
|
||||
\ 'query': query
|
||||
\ }
|
||||
\ }),
|
||||
\ lsp#callbag#map({x->{'server': server, 'request': x['request'], 'response': x['response']}}),
|
||||
\ )
|
||||
\ }),
|
||||
\ lsp#callbag#scan({acc, curr->add(acc, curr)}, []),
|
||||
\ lsp#callbag#tap({x->s:update_ui_items(x)}),
|
||||
\ lsp#callbag#tap({'complete': {->lsp#internal#ui#quickpick#busy(0)}}),
|
||||
\ )
|
||||
\ }),
|
||||
\ lsp#callbag#subscribe({
|
||||
\ 'error': {e->s:on_error(e)},
|
||||
\ }),
|
||||
\ )
|
||||
" Notify empty query. Some servers may not return results when query is empty
|
||||
call l:TextChangeSubject(1, '')
|
||||
endfunction
|
||||
|
||||
function! s:on_change(TextChangeSubject, data, ...) abort
|
||||
call a:TextChangeSubject(1, a:data['input'])
|
||||
endfunction
|
||||
|
||||
function! s:update_ui_items(x) abort
|
||||
let l:items = []
|
||||
for l:i in a:x
|
||||
let l:items += lsp#ui#vim#utils#symbols_to_loc_list(l:i['server'], l:i)
|
||||
endfor
|
||||
call lsp#internal#ui#quickpick#items(l:items)
|
||||
endfunction
|
||||
|
||||
function! s:on_accept(data, name) abort
|
||||
call lsp#internal#ui#quickpick#close()
|
||||
call lsp#utils#location#_open_vim_list_item(a:data['items'][0], '')
|
||||
endfunction
|
||||
|
||||
function! s:on_close(...) abort
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_error(e) abort
|
||||
call lsp#internal#ui#quickpick#close()
|
||||
call lsp#log('LspWorkspaceSymbolSearch error', a:e)
|
||||
endfunction
|
||||
453
dot_vim/plugged/vim-lsp/autoload/lsp/omni.vim
Normal file
453
dot_vim/plugged/vim-lsp/autoload/lsp/omni.vim
Normal file
@@ -0,0 +1,453 @@
|
||||
" vint: -ProhibitUnusedVariable
|
||||
|
||||
" constants {{{
|
||||
let s:t_dict = type({})
|
||||
|
||||
let s:default_completion_item_kinds = {
|
||||
\ '1': 'text',
|
||||
\ '2': 'method',
|
||||
\ '3': 'function',
|
||||
\ '4': 'constructor',
|
||||
\ '5': 'field',
|
||||
\ '6': 'variable',
|
||||
\ '7': 'class',
|
||||
\ '8': 'interface',
|
||||
\ '9': 'module',
|
||||
\ '10': 'property',
|
||||
\ '11': 'unit',
|
||||
\ '12': 'value',
|
||||
\ '13': 'enum',
|
||||
\ '14': 'keyword',
|
||||
\ '15': 'snippet',
|
||||
\ '16': 'color',
|
||||
\ '17': 'file',
|
||||
\ '18': 'reference',
|
||||
\ '19': 'folder',
|
||||
\ '20': 'enum member',
|
||||
\ '21': 'constant',
|
||||
\ '22': 'struct',
|
||||
\ '23': 'event',
|
||||
\ '24': 'operator',
|
||||
\ '25': 'type parameter',
|
||||
\ }
|
||||
|
||||
let s:completion_item_kinds = {}
|
||||
|
||||
let s:completion_status_success = 'success'
|
||||
let s:completion_status_failed = 'failed'
|
||||
let s:completion_status_pending = 'pending'
|
||||
|
||||
let s:is_user_data_support = has('patch-8.0.1493')
|
||||
let s:managed_user_data_key_base = 0
|
||||
let s:managed_user_data_map = {}
|
||||
|
||||
" }}}
|
||||
|
||||
" completion state
|
||||
let s:completion = {'counter': 0, 'status': '', 'matches': []}
|
||||
|
||||
function! lsp#omni#complete(findstart, base) abort
|
||||
let l:info = s:find_complete_servers()
|
||||
if empty(l:info['server_names'])
|
||||
return a:findstart ? -1 : []
|
||||
endif
|
||||
|
||||
if a:findstart
|
||||
return col('.')
|
||||
else
|
||||
if !g:lsp_async_completion
|
||||
let s:completion['status'] = s:completion_status_pending
|
||||
endif
|
||||
|
||||
let l:left = strpart(getline('.'), 0, col('.')-1)
|
||||
|
||||
" Initialize the default startcol. It will be updated if the completion items has textEdit.
|
||||
let s:completion['startcol'] = s:get_startcol(l:left, l:info['server_names'])
|
||||
|
||||
" The `l:info` variable will be filled with completion results after request was finished.
|
||||
call s:send_completion_request(l:info)
|
||||
|
||||
if g:lsp_async_completion
|
||||
" If g:lsp_async_completion == v:true, the `s:display_completions` " will be called by `s:send_completion_request`.
|
||||
redraw
|
||||
return exists('v:none') ? v:none : []
|
||||
else
|
||||
" Wait for finished the textDocument/completion request and then call `s:display_completions` explicitly.
|
||||
call lsp#utils#_wait(-1, {-> s:completion['status'] isnot# s:completion_status_pending || complete_check()}, 10)
|
||||
call timer_start(0, { timer -> s:display_completions(timer, l:info) })
|
||||
|
||||
return exists('v:none') ? v:none : []
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:get_filter_label(item) abort
|
||||
return lsp#utils#_trim(a:item['word'])
|
||||
endfunction
|
||||
|
||||
function! s:prefix_filter(item, last_typed_word) abort
|
||||
let l:label = s:get_filter_label(a:item)
|
||||
|
||||
if g:lsp_ignorecase
|
||||
return stridx(tolower(l:label), tolower(a:last_typed_word)) == 0
|
||||
else
|
||||
return stridx(l:label, a:last_typed_word) == 0
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:contains_filter(item, last_typed_word) abort
|
||||
let l:label = s:get_filter_label(a:item)
|
||||
|
||||
if g:lsp_ignorecase
|
||||
return stridx(tolower(l:label), tolower(a:last_typed_word)) >= 0
|
||||
else
|
||||
return stridx(l:label, a:last_typed_word) >= 0
|
||||
endif
|
||||
endfunction
|
||||
|
||||
let s:pair = {
|
||||
\ '"': '"',
|
||||
\ '''': '''',
|
||||
\ '{': '}',
|
||||
\ '(': ')',
|
||||
\ '[': ']',
|
||||
\}
|
||||
|
||||
function! s:display_completions(timer, info) abort
|
||||
" TODO: Allow multiple servers
|
||||
let l:server_name = a:info['server_names'][0]
|
||||
let l:server_info = lsp#get_server_info(l:server_name)
|
||||
|
||||
let l:current_line = strpart(getline('.'), 0, col('.') - 1)
|
||||
let l:last_typed_word = strpart(l:current_line, s:completion['startcol'] - 1)
|
||||
|
||||
let l:filter = has_key(l:server_info, 'config') && has_key(l:server_info['config'], 'filter') ? l:server_info['config']['filter'] : { 'name': 'prefix' }
|
||||
if l:filter['name'] ==? 'prefix'
|
||||
let s:completion['matches'] = filter(s:completion['matches'], {_, item -> s:prefix_filter(item, l:last_typed_word)})
|
||||
if has_key(s:pair, l:last_typed_word[0])
|
||||
let [l:lhs, l:rhs] = [l:last_typed_word[0], s:pair[l:last_typed_word[0]]]
|
||||
for l:item in s:completion['matches']
|
||||
let l:str = l:item['word']
|
||||
if len(l:str) > 1 && l:str[0] ==# l:lhs && l:str[-1:] ==# l:rhs
|
||||
let l:item['word'] = l:str[:-2]
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
elseif l:filter['name'] ==? 'contains'
|
||||
let s:completion['matches'] = filter(s:completion['matches'], {_, item -> s:contains_filter(item, l:last_typed_word)})
|
||||
endif
|
||||
|
||||
let s:completion['status'] = ''
|
||||
|
||||
if mode() is# 'i'
|
||||
call complete(s:completion['startcol'], s:completion['matches'])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:handle_omnicompletion(server_name, complete_counter, info, data) abort
|
||||
if s:completion['counter'] != a:complete_counter
|
||||
" ignore old completion results
|
||||
return
|
||||
endif
|
||||
|
||||
if lsp#client#is_error(a:data) || !has_key(a:data, 'response') || !has_key(a:data['response'], 'result')
|
||||
let s:completion['status'] = s:completion_status_failed
|
||||
return
|
||||
endif
|
||||
|
||||
let l:result = s:get_completion_result(a:server_name, a:data)
|
||||
let s:completion['matches'] = l:result['items']
|
||||
let s:completion['startcol'] = min([l:result['startcol'], s:completion['startcol']])
|
||||
let s:completion['status'] = s:completion_status_success
|
||||
|
||||
if g:lsp_async_completion
|
||||
call s:display_completions(0, a:info)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#omni#get_kind_text(completion_item, ...) abort
|
||||
let l:server = get(a:, 1, '')
|
||||
if empty(l:server) " server name
|
||||
let l:completion_item_kinds = s:default_completion_item_kinds
|
||||
else
|
||||
if !has_key(s:completion_item_kinds, l:server)
|
||||
let l:server_info = lsp#get_server_info(l:server)
|
||||
if has_key (l:server_info, 'config') && has_key(l:server_info['config'], 'completion_item_kinds')
|
||||
let s:completion_item_kinds[l:server] = extend(copy(s:default_completion_item_kinds), l:server_info['config']['completion_item_kinds'])
|
||||
else
|
||||
let s:completion_item_kinds[l:server] = s:default_completion_item_kinds
|
||||
endif
|
||||
endif
|
||||
let l:completion_item_kinds = s:completion_item_kinds[l:server]
|
||||
endif
|
||||
|
||||
return has_key(a:completion_item, 'kind') && has_key(l:completion_item_kinds, a:completion_item['kind'])
|
||||
\ ? l:completion_item_kinds[a:completion_item['kind']] : ''
|
||||
endfunction
|
||||
|
||||
function! s:get_kind_text_mappings(server) abort
|
||||
let l:server_name = a:server['name']
|
||||
if has_key(s:completion_item_kinds, l:server_name)
|
||||
return s:completion_item_kinds[l:server_name]
|
||||
else
|
||||
if has_key(a:server, 'config') && has_key(a:server['config'], 'completion_item_kinds')
|
||||
let s:completion_item_kinds[l:server_name] = extend(copy(s:default_completion_item_kinds), a:server['config']['completion_item_kinds'])
|
||||
else
|
||||
let s:completion_item_kinds[l:server_name] = s:default_completion_item_kinds
|
||||
endif
|
||||
return s:completion_item_kinds[l:server_name]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" auxiliary functions {{{
|
||||
|
||||
function! s:find_complete_servers() abort
|
||||
let l:server_names = []
|
||||
for l:server_name in lsp#get_allowed_servers()
|
||||
let l:init_capabilities = lsp#get_server_capabilities(l:server_name)
|
||||
if has_key(l:init_capabilities, 'completionProvider')
|
||||
" TODO: support triggerCharacters
|
||||
call add(l:server_names, l:server_name)
|
||||
endif
|
||||
endfor
|
||||
|
||||
return { 'server_names': l:server_names }
|
||||
endfunction
|
||||
|
||||
function! s:send_completion_request(info) abort
|
||||
let s:completion['counter'] = s:completion['counter'] + 1
|
||||
let l:server_name = a:info['server_names'][0]
|
||||
" TODO: support multiple servers
|
||||
call lsp#send_request(l:server_name, {
|
||||
\ 'method': 'textDocument/completion',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'position': lsp#get_position(),
|
||||
\ 'context': { 'triggerKind': 1 },
|
||||
\ },
|
||||
\ 'on_notification': function('s:handle_omnicompletion', [l:server_name, s:completion['counter'], a:info]),
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
function! s:get_completion_result(server_name, data) abort
|
||||
let l:result = a:data['response']['result']
|
||||
|
||||
let l:options = {
|
||||
\ 'server': lsp#get_server_info(a:server_name),
|
||||
\ 'position': lsp#get_position(),
|
||||
\ 'response': a:data['response'],
|
||||
\ }
|
||||
|
||||
return lsp#omni#get_vim_completion_items(l:options)
|
||||
endfunction
|
||||
|
||||
function! s:sort_by_sorttext(i1, i2) abort
|
||||
let l:text1 = get(a:i1, 'sortText')
|
||||
let l:text2 = get(a:i2, 'sortText')
|
||||
|
||||
" sortText is possibly empty string
|
||||
let l:text1 = !empty(l:text1) ? l:text1 : a:i1['label']
|
||||
let l:text2 = !empty(l:text2) ? l:text2 : a:i2['label']
|
||||
|
||||
if g:lsp_ignorecase
|
||||
return l:text1 ==? l:text2 ? 0 : l:text1 >? l:text2 ? 1 : -1
|
||||
else
|
||||
return l:text1 ==# l:text2 ? 0 : l:text1 ># l:text2 ? 1 : -1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Create vim's completed items from LSP response.
|
||||
"
|
||||
" options = {
|
||||
" server: {}, " needs to be server_info and not server_name
|
||||
" position: lsp#get_position(),
|
||||
" response: {}, " needs to be the entire lsp response. errors need to be
|
||||
" handled before calling the fuction
|
||||
" }
|
||||
"
|
||||
" * The returned` startcol` may be the same as the cursor position, in which case you need to decide which one to use.
|
||||
"
|
||||
" @return { 'items': v:completed_item[], 'incomplete': v:t_bool, 'startcol': number }
|
||||
"
|
||||
function! lsp#omni#get_vim_completion_items(options) abort
|
||||
let l:server = a:options['server']
|
||||
let l:server_name = l:server['name']
|
||||
let l:kind_text_mappings = s:get_kind_text_mappings(l:server)
|
||||
let l:complete_position = a:options['position']
|
||||
let l:current_line = getline('.')
|
||||
let l:default_startcol = s:get_startcol(strcharpart(l:current_line, 0, l:complete_position['character']), [l:server_name])
|
||||
let l:default_start_character = strchars(strpart(l:current_line, 0, l:default_startcol - 1))
|
||||
let l:refresh_pattern = s:get_refresh_pattern([l:server_name])
|
||||
|
||||
let l:result = a:options['response']['result']
|
||||
if type(l:result) == type([])
|
||||
let l:items = l:result
|
||||
let l:incomplete = 0
|
||||
elseif type(l:result) == type({})
|
||||
let l:items = l:result['items']
|
||||
let l:incomplete = l:result['isIncomplete']
|
||||
else
|
||||
let l:items = []
|
||||
let l:incomplete = 0
|
||||
endif
|
||||
|
||||
let l:sort = has_key(l:server, 'config') && has_key(l:server['config'], 'sort') ? l:server['config']['sort'] : v:null
|
||||
|
||||
if len(l:items) > 0 && type(l:sort) == s:t_dict && len(l:items) <= l:sort['max']
|
||||
" If first item contains sortText, maybe we can use sortText
|
||||
call sort(l:items, function('s:sort_by_sorttext'))
|
||||
endif
|
||||
|
||||
let l:start_character = l:complete_position['character']
|
||||
|
||||
let l:start_characters = [] " The mapping of item specific start_character.
|
||||
let l:vim_complete_items = []
|
||||
for l:completion_item in l:items
|
||||
let l:expandable = get(l:completion_item, 'insertTextFormat', 1) == 2
|
||||
let l:vim_complete_item = {
|
||||
\ 'kind': get(l:kind_text_mappings, get(l:completion_item, 'kind', '') , ''),
|
||||
\ 'dup': 1,
|
||||
\ 'empty': 1,
|
||||
\ 'icase': 1,
|
||||
\ }
|
||||
let l:range = lsp#utils#text_edit#get_range(get(l:completion_item, 'textEdit', {}))
|
||||
if has_key(l:completion_item, 'textEdit') && type(l:completion_item['textEdit']) == s:t_dict && !empty(l:range) && has_key(l:completion_item['textEdit'], 'newText')
|
||||
let l:text_edit_new_text = l:completion_item['textEdit']['newText']
|
||||
if has_key(l:completion_item, 'filterText') && !empty(l:completion_item['filterText']) && matchstr(l:text_edit_new_text, '^' . l:refresh_pattern) ==# ''
|
||||
" Use filterText as word.
|
||||
let l:vim_complete_item['word'] = l:completion_item['filterText']
|
||||
else
|
||||
" Use textEdit.newText as word.
|
||||
let l:vim_complete_item['word'] = l:text_edit_new_text
|
||||
endif
|
||||
|
||||
" Fix overlapped text if needed.
|
||||
let l:item_start_character = l:range['start']['character']
|
||||
if l:item_start_character < l:default_start_character
|
||||
" Add already typed word. The typescript-language-server returns `[Symbol]` item for the line of `Hoo.|`. So we should add `.` (`.[Symbol]`) .
|
||||
let l:overlap_text = strcharpart(l:current_line, l:item_start_character, l:default_start_character - l:item_start_character)
|
||||
if stridx(l:vim_complete_item['word'], l:overlap_text) != 0
|
||||
let l:vim_complete_item['word'] = l:overlap_text . l:vim_complete_item['word']
|
||||
endif
|
||||
endif
|
||||
let l:start_character = min([l:item_start_character, l:start_character])
|
||||
let l:start_characters += [l:item_start_character]
|
||||
elseif has_key(l:completion_item, 'insertText') && !empty(l:completion_item['insertText'])
|
||||
let l:vim_complete_item['word'] = l:completion_item['insertText']
|
||||
let l:start_characters += [l:default_start_character]
|
||||
else
|
||||
let l:vim_complete_item['word'] = l:completion_item['label']
|
||||
let l:start_characters += [l:default_start_character]
|
||||
endif
|
||||
|
||||
if l:expandable
|
||||
let l:vim_complete_item['word'] = lsp#utils#make_valid_word(substitute(l:vim_complete_item['word'], '\$[0-9]\+\|\${\%(\\.\|[^}]\)\+}', '', 'g'))
|
||||
let l:vim_complete_item['abbr'] = l:completion_item['label'] . '~'
|
||||
else
|
||||
let l:vim_complete_item['abbr'] = l:completion_item['label']
|
||||
endif
|
||||
|
||||
if s:is_user_data_support
|
||||
let l:vim_complete_item['user_data'] = s:create_user_data(l:completion_item, l:server_name, l:complete_position, l:start_characters[len(l:start_characters) - 1])
|
||||
endif
|
||||
|
||||
let l:vim_complete_items += [l:vim_complete_item]
|
||||
endfor
|
||||
|
||||
" Add the additional text for startcol correction.
|
||||
if l:start_character != l:default_start_character
|
||||
for l:i in range(len(l:start_characters))
|
||||
let l:item_start_character = l:start_characters[l:i]
|
||||
if l:start_character < l:item_start_character
|
||||
let l:item = l:vim_complete_items[l:i]
|
||||
let l:item['word'] = strcharpart(l:current_line, l:start_character, l:item_start_character - l:start_character) . l:item['word']
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
let l:startcol = lsp#utils#position#lsp_character_to_vim('%', { 'line': l:complete_position['line'], 'character': l:start_character })
|
||||
|
||||
return { 'items': l:vim_complete_items, 'incomplete': l:incomplete, 'startcol': l:startcol }
|
||||
endfunction
|
||||
|
||||
"
|
||||
" Clear internal user_data map.
|
||||
"
|
||||
" This function should call at `CompleteDone` only if not empty `v:completed_item`.
|
||||
"
|
||||
function! lsp#omni#_clear_managed_user_data_map() abort
|
||||
let s:managed_user_data_key_base = 0
|
||||
let s:managed_user_data_map = {}
|
||||
endfunction
|
||||
|
||||
"
|
||||
" create item's user_data.
|
||||
"
|
||||
function! s:create_user_data(completion_item, server_name, complete_position, start_character) abort
|
||||
let l:user_data_key = s:create_user_data_key(s:managed_user_data_key_base)
|
||||
let s:managed_user_data_map[l:user_data_key] = {
|
||||
\ 'complete_position': a:complete_position,
|
||||
\ 'server_name': a:server_name,
|
||||
\ 'completion_item': a:completion_item,
|
||||
\ 'start_character': a:start_character,
|
||||
\ }
|
||||
let s:managed_user_data_key_base += 1
|
||||
return l:user_data_key
|
||||
endfunction
|
||||
|
||||
function! lsp#omni#get_managed_user_data_from_completed_item(completed_item) abort
|
||||
" the item has no user_data.
|
||||
if !has_key(a:completed_item, 'user_data')
|
||||
return {}
|
||||
endif
|
||||
|
||||
let l:user_data_string = get(a:completed_item, 'user_data', '')
|
||||
if type(l:user_data_string) != type('')
|
||||
return {}
|
||||
endif
|
||||
|
||||
" Check managed user_data.
|
||||
if has_key(s:managed_user_data_map, l:user_data_string)
|
||||
return s:managed_user_data_map[l:user_data_string]
|
||||
endif
|
||||
|
||||
" Check json.
|
||||
if stridx(l:user_data_string, '"vim-lsp/key"') != -1
|
||||
try
|
||||
let l:user_data = json_decode(l:user_data_string)
|
||||
if has_key(l:user_data, 'vim-lsp/key')
|
||||
let l:user_data_key = s:create_user_data_key(l:user_data['vim-lsp/key'])
|
||||
if has_key(s:managed_user_data_map, l:user_data_key)
|
||||
return s:managed_user_data_map[l:user_data_key]
|
||||
endif
|
||||
endif
|
||||
catch /.*/
|
||||
endtry
|
||||
endif
|
||||
return {}
|
||||
endfunction
|
||||
|
||||
function! lsp#omni#get_completion_item_kinds() abort
|
||||
return map(keys(s:default_completion_item_kinds), {idx, key -> str2nr(key)})
|
||||
endfunction
|
||||
|
||||
function! s:create_user_data_key(base) abort
|
||||
return '{"vim-lsp/key":"' . a:base . '"}'
|
||||
endfunction
|
||||
|
||||
function! s:get_startcol(left, server_names) abort
|
||||
" Initialize the default startcol. It will be updated if the completion items has textEdit.
|
||||
let l:startcol = 1 + matchstrpos(a:left, s:get_refresh_pattern(a:server_names))[1]
|
||||
return l:startcol == 0 ? strlen(a:left) + 1 : l:startcol
|
||||
endfunction
|
||||
|
||||
function! s:get_refresh_pattern(server_names) abort
|
||||
for l:server_name in a:server_names
|
||||
let l:server_info = lsp#get_server_info(l:server_name)
|
||||
if has_key(l:server_info, 'config') && has_key(l:server_info['config'], 'refresh_pattern')
|
||||
return l:server_info['config']['refresh_pattern']
|
||||
endif
|
||||
endfor
|
||||
return '\(\k\+$\)'
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
157
dot_vim/plugged/vim-lsp/autoload/lsp/tag.vim
Normal file
157
dot_vim/plugged/vim-lsp/autoload/lsp/tag.vim
Normal file
@@ -0,0 +1,157 @@
|
||||
let s:tag_kind_priority = ['definition', 'declaration', 'implementation', 'type definition']
|
||||
|
||||
function! s:not_supported(what) abort
|
||||
call lsp#log(a:what . ' not supported for ' . &filetype)
|
||||
endfunction
|
||||
|
||||
function! s:location_to_tag(loc) abort
|
||||
if has_key(a:loc, 'targetUri')
|
||||
let l:uri = a:loc['targetUri']
|
||||
let l:range = a:loc['targetRange']
|
||||
else
|
||||
let l:uri = a:loc['uri']
|
||||
let l:range = a:loc['range']
|
||||
endif
|
||||
|
||||
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, l:range['start'])
|
||||
return {
|
||||
\ 'filename': l:path,
|
||||
\ 'cmd': printf('/\%%%dl\%%%dc/', l:line, l:col)
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! s:handle_locations(ctx, server, type, data) abort
|
||||
try
|
||||
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']))
|
||||
return
|
||||
endif
|
||||
|
||||
let l:list = a:ctx['list']
|
||||
let l:result = a:data['response']['result']
|
||||
if type(l:result) != type([])
|
||||
let l:result = [l:result]
|
||||
endif
|
||||
for l:loc in l:result
|
||||
let l:tag = s:location_to_tag(l:loc)
|
||||
if !empty(l:tag)
|
||||
call add(l:list, extend(l:tag, { 'name': a:ctx['pattern'], 'kind': a:type }))
|
||||
endif
|
||||
endfor
|
||||
finally
|
||||
let a:ctx['counter'] -= 1
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:handle_symbols(ctx, server, data) abort
|
||||
try
|
||||
if lsp#client#is_error(a:data['response']) || !has_key(a:data['response'], 'result')
|
||||
call lsp#utils#error('Failed to retrieve workspace symbols for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
|
||||
return
|
||||
endif
|
||||
|
||||
let l:list = a:ctx['list']
|
||||
for l:symbol in a:data['response']['result']
|
||||
let l:tag = s:location_to_tag(l:symbol['location'])
|
||||
if empty(l:tag)
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:tag['name'] = l:symbol['name']
|
||||
if has_key(l:symbol, 'kind')
|
||||
let l:tag['kind'] = lsp#ui#vim#utils#_get_symbol_text_from_kind(a:server, l:symbol['kind'])
|
||||
endif
|
||||
call add(l:list, l:tag)
|
||||
endfor
|
||||
finally
|
||||
let a:ctx['counter'] -= 1
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:tag_view_sub(ctx, method, params) abort
|
||||
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)
|
||||
if empty(l:servers)
|
||||
call s:not_supported('retrieving ' . l:operation)
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let a:ctx['counter'] += len(l:servers)
|
||||
for l:server in l:servers
|
||||
call lsp#send_request(l:server, {
|
||||
\ 'method': 'textDocument/'.a:method,
|
||||
\ 'params': a:params,
|
||||
\ 'on_notification': function('s:handle_locations', [a:ctx, l:server, l:operation])
|
||||
\})
|
||||
endfor
|
||||
return v:true
|
||||
endfunction
|
||||
|
||||
function! s:tag_view(ctx) abort
|
||||
let l:params = {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'position': lsp#get_position(),
|
||||
\ }
|
||||
return !empty(filter(copy(g:lsp_tagfunc_source_methods),
|
||||
\ {_, m -> s:tag_view_sub(a:ctx, m, l:params)}))
|
||||
endfunction
|
||||
|
||||
function! s:tag_search(ctx) abort
|
||||
let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_workspace_symbol_provider(v:val)')
|
||||
if empty(l:servers)
|
||||
call s:not_supported('retrieving workspace symbols')
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let a:ctx['counter'] = len(l:servers)
|
||||
for l:server in l:servers
|
||||
call lsp#send_request(l:server, {
|
||||
\ 'method': 'workspace/symbol',
|
||||
\ 'params': { 'query': a:ctx['pattern'] },
|
||||
\ 'on_notification': function('s:handle_symbols', [a:ctx, l:server])
|
||||
\ })
|
||||
endfor
|
||||
return v:true
|
||||
endfunction
|
||||
|
||||
function! s:compare_tags(path, a, b) abort
|
||||
" TODO: custom tag sorting, maybe?
|
||||
if a:a['filename'] !=# a:b['filename']
|
||||
if a:a['filename'] ==# a:path
|
||||
return -1
|
||||
elseif a:b['filename'] ==# a:path
|
||||
return 1
|
||||
endif
|
||||
endif
|
||||
let l:rank_a = index(s:tag_kind_priority, get(a:a, 'kind', ''))
|
||||
let l:rank_b = index(s:tag_kind_priority, get(a:b, 'kind', ''))
|
||||
if l:rank_a != l:rank_b
|
||||
return l:rank_a < l:rank_b ? -1 : 1
|
||||
endif
|
||||
if a:a['filename'] !=# a:b['filename']
|
||||
return a:a['filename'] <# a:b['filename'] ? -1 : 1
|
||||
endif
|
||||
return str2nr(a:a['cmd']) - str2nr(a:b['cmd'])
|
||||
endfunction
|
||||
|
||||
function! lsp#tag#tagfunc(pattern, flags, info) abort
|
||||
if stridx(a:flags, 'i') >= 0
|
||||
return v:null
|
||||
endif
|
||||
|
||||
let l:ctx = { 'pattern': a:pattern, 'counter': 0, 'list': [] }
|
||||
if !(stridx(a:flags, 'c') >= 0 ? s:tag_view(l:ctx) : s:tag_search(l:ctx))
|
||||
" No supported methods so use builtin tag source
|
||||
return v:null
|
||||
endif
|
||||
call lsp#utils#_wait(-1, {-> l:ctx['counter'] == 0}, 50)
|
||||
call sort(l:ctx['list'], function('s:compare_tags', [a:info['buf_ffname']]))
|
||||
return l:ctx['list']
|
||||
endfunction
|
||||
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
|
||||
523
dot_vim/plugged/vim-lsp/autoload/lsp/utils.vim
Normal file
523
dot_vim/plugged/vim-lsp/autoload/lsp/utils.vim
Normal file
@@ -0,0 +1,523 @@
|
||||
let s:has_lua = has('nvim-0.4.0') || (has('lua') && has('patch-8.2.0775'))
|
||||
function! lsp#utils#has_lua() abort
|
||||
return s:has_lua
|
||||
endfunction
|
||||
|
||||
let s:has_native_lsp_client = !has('nvim') && has('patch-8.2.4780')
|
||||
function! lsp#utils#has_native_lsp_client() abort
|
||||
return s:has_native_lsp_client
|
||||
endfunction
|
||||
|
||||
let s:has_virtual_text = exists('*nvim_buf_set_virtual_text') && exists('*nvim_create_namespace')
|
||||
function! lsp#utils#_has_nvim_virtual_text() abort
|
||||
return s:has_virtual_text
|
||||
endfunction
|
||||
|
||||
let s:has_signs = exists('*sign_define') && (has('nvim') || has('patch-8.1.0772'))
|
||||
function! lsp#utils#_has_signs() abort
|
||||
return s:has_signs
|
||||
endfunction
|
||||
|
||||
let s:has_nvim_buf_highlight = exists('*nvim_buf_add_highlight') && has('nvim')
|
||||
function! lsp#utils#_has_nvim_buf_highlight() abort
|
||||
return s:has_nvim_buf_highlight
|
||||
endfunction
|
||||
|
||||
" https://github.com/prabirshrestha/vim-lsp/issues/399#issuecomment-500585549
|
||||
let s:has_textprops = exists('*prop_add') && has('patch-8.1.1035')
|
||||
function! lsp#utils#_has_textprops() abort
|
||||
return s:has_textprops
|
||||
endfunction
|
||||
|
||||
let s:has_vim9textprops = exists('*prop_add') && has('patch-9.0.0178')
|
||||
function! lsp#utils#_has_vim_virtual_text() abort
|
||||
return s:has_vim9textprops
|
||||
endfunction
|
||||
|
||||
let s:has_prop_remove_types = exists('*prop_remove') && has('patch-9.0.0233')
|
||||
function! lsp#utils#_has_prop_remove_types() abort
|
||||
return s:has_prop_remove_types
|
||||
endfunction
|
||||
|
||||
let s:has_higlights = has('nvim') ? lsp#utils#_has_nvim_buf_highlight() : lsp#utils#_has_textprops()
|
||||
function! lsp#utils#_has_highlights() abort
|
||||
return s:has_higlights
|
||||
endfunction
|
||||
|
||||
let s:has_popup_menu = exists('*popup_menu')
|
||||
function! lsp#utils#_has_popup_menu() abort
|
||||
return s:has_popup_menu
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#is_file_uri(uri) abort
|
||||
return stridx(a:uri, 'file:///') == 0
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#is_remote_uri(uri) abort
|
||||
return a:uri =~# '^\w\+::' || a:uri =~# '^[a-z][a-z0-9+.-]*://'
|
||||
endfunction
|
||||
|
||||
function! s:decode_uri(uri) abort
|
||||
let l:ret = substitute(a:uri, '[?#].*', '', '')
|
||||
return substitute(l:ret, '%\(\x\x\)', '\=printf("%c", str2nr(submatch(1), 16))', 'g')
|
||||
endfunction
|
||||
|
||||
function! s:urlencode_char(c) abort
|
||||
return printf('%%%02X', char2nr(a:c))
|
||||
endfunction
|
||||
|
||||
function! s:get_prefix(path) abort
|
||||
return matchstr(a:path, '\(^\w\+::\|^\w\+://\)')
|
||||
endfunction
|
||||
|
||||
function! s:encode_uri(path, start_pos_encode, default_prefix) abort
|
||||
let l:prefix = s:get_prefix(a:path)
|
||||
let l:path = a:path[len(l:prefix):]
|
||||
if len(l:prefix) == 0
|
||||
let l:prefix = a:default_prefix
|
||||
endif
|
||||
|
||||
let l:result = strpart(a:path, 0, a:start_pos_encode)
|
||||
|
||||
for l:i in range(a:start_pos_encode, len(l:path) - 1)
|
||||
" Don't encode '/' here, `path` is expected to be a valid path.
|
||||
if l:path[l:i] =~# '^[a-zA-Z0-9_.~/@-]$'
|
||||
let l:result .= l:path[l:i]
|
||||
else
|
||||
let l:result .= s:urlencode_char(l:path[l:i])
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:prefix . l:result
|
||||
endfunction
|
||||
|
||||
let s:path_to_uri_cache = {}
|
||||
if has('win32') || has('win64') || has('win32unix')
|
||||
function! lsp#utils#path_to_uri(path) abort
|
||||
if has_key(s:path_to_uri_cache, a:path)
|
||||
return s:path_to_uri_cache[a:path]
|
||||
endif
|
||||
|
||||
if empty(a:path) || lsp#utils#is_remote_uri(a:path)
|
||||
let s:path_to_uri_cache[a:path] = a:path
|
||||
return s:path_to_uri_cache[a:path]
|
||||
else
|
||||
" Transform cygwin paths to windows paths
|
||||
let l:path = a:path
|
||||
if has('win32unix')
|
||||
let l:path = substitute(a:path, '\c^/\([a-z]\)/', '\U\1:/', '')
|
||||
endif
|
||||
|
||||
" You must not encode the volume information on the path if
|
||||
" present
|
||||
let l:end_pos_volume = matchstrpos(l:path, '\c[A-Z]:')[2]
|
||||
|
||||
if l:end_pos_volume == -1
|
||||
let l:end_pos_volume = 0
|
||||
endif
|
||||
|
||||
let s:path_to_uri_cache[l:path] = s:encode_uri(substitute(l:path, '\', '/', 'g'), l:end_pos_volume, 'file:///')
|
||||
return s:path_to_uri_cache[l:path]
|
||||
endif
|
||||
endfunction
|
||||
else
|
||||
function! lsp#utils#path_to_uri(path) abort
|
||||
if has_key(s:path_to_uri_cache, a:path)
|
||||
return s:path_to_uri_cache[a:path]
|
||||
endif
|
||||
|
||||
if empty(a:path) || lsp#utils#is_remote_uri(a:path)
|
||||
let s:path_to_uri_cache[a:path] = a:path
|
||||
return s:path_to_uri_cache[a:path]
|
||||
else
|
||||
let s:path_to_uri_cache[a:path] = s:encode_uri(a:path, 0, 'file://')
|
||||
return s:path_to_uri_cache[a:path]
|
||||
endif
|
||||
endfunction
|
||||
endif
|
||||
|
||||
let s:uri_to_path_cache = {}
|
||||
if has('win32') || has('win64') || has('win32unix')
|
||||
function! lsp#utils#uri_to_path(uri) abort
|
||||
if has_key(s:uri_to_path_cache, a:uri)
|
||||
return s:uri_to_path_cache[a:uri]
|
||||
endif
|
||||
|
||||
let l:path = substitute(s:decode_uri(a:uri[len('file:///'):]), '/', '\\', 'g')
|
||||
|
||||
" Transform windows paths to cygwin paths
|
||||
if has('win32unix')
|
||||
let l:path = substitute(l:path, '\c^\([A-Z]\):\\', '/\l\1/', '')
|
||||
let l:path = substitute(l:path, '\\', '/', 'g')
|
||||
endif
|
||||
|
||||
let s:uri_to_path_cache[a:uri] = l:path
|
||||
return s:uri_to_path_cache[a:uri]
|
||||
endfunction
|
||||
else
|
||||
function! lsp#utils#uri_to_path(uri) abort
|
||||
if has_key(s:uri_to_path_cache, a:uri)
|
||||
return s:uri_to_path_cache[a:uri]
|
||||
endif
|
||||
|
||||
let s:uri_to_path_cache[a:uri] = s:decode_uri(a:uri[len('file://'):])
|
||||
return s:uri_to_path_cache[a:uri]
|
||||
endfunction
|
||||
endif
|
||||
|
||||
if has('win32') || has('win64')
|
||||
function! lsp#utils#normalize_uri(uri) abort
|
||||
" Refer to https://github.com/microsoft/language-server-protocol/pull/1019 on normalization of urls.
|
||||
" TODO: after the discussion is settled, modify this function.
|
||||
let l:ret = substitute(a:uri, '^file:///[a-zA-Z]\zs%3[aA]', ':', '')
|
||||
return substitute(l:ret, '^file:///\zs\([A-Z]\)', "\\=tolower(submatch(1))", '')
|
||||
endfunction
|
||||
else
|
||||
function! lsp#utils#normalize_uri(uri) abort
|
||||
return a:uri
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! lsp#utils#get_default_root_uri() abort
|
||||
return lsp#utils#path_to_uri(getcwd())
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#get_buffer_path(...) abort
|
||||
return expand((a:0 > 0 ? '#' . a:1 : '%') . ':p')
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#get_buffer_uri(...) abort
|
||||
let l:name = a:0 > 0 ? bufname(a:1) : expand('%')
|
||||
if empty(l:name)
|
||||
let l:nr = a:0 > 0 ? a:1 : bufnr('%')
|
||||
let l:name = printf('%s/__NO_NAME_%d__', getcwd(), l:nr)
|
||||
endif
|
||||
return lsp#utils#path_to_uri(fnamemodify(l:name, ':p'))
|
||||
endfunction
|
||||
|
||||
" Find a nearest to a `path` parent directory `directoryname` by traversing the filesystem upwards
|
||||
function! lsp#utils#find_nearest_parent_directory(path, directoryname) abort
|
||||
let l:relative_path = finddir(a:directoryname, a:path . ';')
|
||||
|
||||
if !empty(l:relative_path)
|
||||
return fnamemodify(l:relative_path, ':p')
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Find a nearest to a `path` parent filename `filename` by traversing the filesystem upwards
|
||||
function! lsp#utils#find_nearest_parent_file(path, filename) abort
|
||||
let l:relative_path = findfile(a:filename, a:path . ';')
|
||||
|
||||
if !empty(l:relative_path)
|
||||
return fnamemodify(l:relative_path, ':p')
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#_compare_nearest_path(matches, lhs, rhs) abort
|
||||
let l:llhs = len(a:lhs)
|
||||
let l:lrhs = len(a:rhs)
|
||||
if l:llhs ># l:lrhs
|
||||
return -1
|
||||
elseif l:llhs <# l:lrhs
|
||||
return 1
|
||||
endif
|
||||
if a:matches[a:lhs] ># a:matches[a:rhs]
|
||||
return -1
|
||||
elseif a:matches[a:lhs] <# a:matches[a:rhs]
|
||||
return 1
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#_nearest_path(matches) abort
|
||||
return empty(a:matches) ?
|
||||
\ '' :
|
||||
\ sort(keys(a:matches), function('lsp#utils#_compare_nearest_path', [a:matches]))[0]
|
||||
endfunction
|
||||
|
||||
" Find a nearest to a `path` parent filename `filename` by traversing the filesystem upwards
|
||||
" The filename ending with '/' or '\' will be regarded as directory name,
|
||||
" otherwith as file name
|
||||
function! lsp#utils#find_nearest_parent_file_directory(path, filename) abort
|
||||
if type(a:filename) == 3
|
||||
let l:matched_paths = {}
|
||||
for l:current_name in a:filename
|
||||
let l:path = lsp#utils#find_nearest_parent_file_directory(a:path, l:current_name)
|
||||
|
||||
if !empty(l:path)
|
||||
if has_key(l:matched_paths, l:path)
|
||||
let l:matched_paths[l:path] += 1
|
||||
else
|
||||
let l:matched_paths[l:path] = 1
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
return lsp#utils#_nearest_path(l:matched_paths)
|
||||
elseif type(a:filename) == 1
|
||||
if a:filename[-1:] ==# '/' || a:filename[-1:] ==# '\'
|
||||
let l:modify_str = ':p:h:h'
|
||||
let l:path = lsp#utils#find_nearest_parent_directory(a:path, a:filename[:-2])
|
||||
else
|
||||
let l:modify_str = ':p:h'
|
||||
let l:path = lsp#utils#find_nearest_parent_file(a:path, a:filename)
|
||||
endif
|
||||
|
||||
return empty(l:path) ? '' : fnamemodify(l:path, l:modify_str)
|
||||
else
|
||||
echoerr "The type of argument \"filename\" must be String or List"
|
||||
endif
|
||||
endfunction
|
||||
|
||||
if exists('*matchstrpos')
|
||||
function! lsp#utils#matchstrpos(expr, pattern) abort
|
||||
return matchstrpos(a:expr, a:pattern)
|
||||
endfunction
|
||||
else
|
||||
function! lsp#utils#matchstrpos(expr, pattern) abort
|
||||
return [matchstr(a:expr, a:pattern), match(a:expr, a:pattern), matchend(a:expr, a:pattern)]
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! lsp#utils#empty_complete(...) abort
|
||||
return []
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#error(msg) abort
|
||||
echohl ErrorMsg
|
||||
echom a:msg
|
||||
echohl NONE
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#warning(msg) abort
|
||||
echohl WarningMsg
|
||||
echom a:msg
|
||||
echohl NONE
|
||||
endfunction
|
||||
|
||||
|
||||
function! lsp#utils#echo_with_truncation(msg) abort
|
||||
let l:msg = a:msg
|
||||
|
||||
if &laststatus == 0 || (&laststatus == 1 && tabpagewinnr(tabpagenr(), '$') == 1)
|
||||
let l:winwidth = winwidth(0)
|
||||
|
||||
if &ruler
|
||||
let l:winwidth -= 18
|
||||
endif
|
||||
else
|
||||
let l:winwidth = &columns
|
||||
endif
|
||||
|
||||
if &showcmd
|
||||
let l:winwidth -= 12
|
||||
endif
|
||||
|
||||
if l:winwidth > 5 && l:winwidth < strdisplaywidth(l:msg)
|
||||
let l:msg = l:msg[:l:winwidth - 5] . '...'
|
||||
endif
|
||||
|
||||
exec 'echo l:msg'
|
||||
endfunction
|
||||
|
||||
" Convert a byte-index (1-based) to a character-index (0-based)
|
||||
" This function requires a buffer specifier (expr, see :help bufname()),
|
||||
" a line number (lnum, 1-based), and a byte-index (char, 1-based).
|
||||
function! lsp#utils#to_char(expr, lnum, col) abort
|
||||
let l:lines = getbufline(a:expr, a:lnum)
|
||||
if l:lines == []
|
||||
if type(a:expr) != v:t_string || !filereadable(a:expr)
|
||||
" invalid a:expr
|
||||
return a:col - 1
|
||||
endif
|
||||
" a:expr is a file that is not yet loaded as a buffer
|
||||
let l:lines = readfile(a:expr, '', a:lnum)
|
||||
endif
|
||||
let l:linestr = l:lines[-1]
|
||||
return strchars(strpart(l:linestr, 0, a:col - 1))
|
||||
endfunction
|
||||
|
||||
function! s:get_base64_alphabet() abort
|
||||
let l:alphabet = []
|
||||
|
||||
" Uppercase letters
|
||||
for l:c in range(char2nr('A'), char2nr('Z'))
|
||||
call add(l:alphabet, nr2char(l:c))
|
||||
endfor
|
||||
|
||||
" Lowercase letters
|
||||
for l:c in range(char2nr('a'), char2nr('z'))
|
||||
call add(l:alphabet, nr2char(l:c))
|
||||
endfor
|
||||
|
||||
" Numbers
|
||||
for l:c in range(char2nr('0'), char2nr('9'))
|
||||
call add(l:alphabet, nr2char(l:c))
|
||||
endfor
|
||||
|
||||
" Symbols
|
||||
call add(l:alphabet, '+')
|
||||
call add(l:alphabet, '/')
|
||||
|
||||
return l:alphabet
|
||||
endfunction
|
||||
|
||||
if exists('*trim')
|
||||
function! lsp#utils#_trim(string) abort
|
||||
return trim(a:string)
|
||||
endfunction
|
||||
else
|
||||
function! lsp#utils#_trim(string) abort
|
||||
return substitute(a:string, '^\s*\|\s*$', '', 'g')
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! lsp#utils#_get_before_line() abort
|
||||
let l:text = getline('.')
|
||||
let l:idx = min([strlen(l:text), col('.') - 2])
|
||||
let l:idx = max([l:idx, -1])
|
||||
if l:idx == -1
|
||||
return ''
|
||||
endif
|
||||
return l:text[0 : l:idx]
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#_get_before_char_skip_white() abort
|
||||
let l:current_lnum = line('.')
|
||||
|
||||
let l:lnum = l:current_lnum
|
||||
while l:lnum > 0
|
||||
if l:lnum == l:current_lnum
|
||||
let l:text = lsp#utils#_get_before_line()
|
||||
else
|
||||
let l:text = getline(l:lnum)
|
||||
endif
|
||||
let l:match = matchlist(l:text, '\([^[:blank:]]\)\s*$')
|
||||
if get(l:match, 1, v:null) isnot v:null
|
||||
return l:match[1]
|
||||
endif
|
||||
let l:lnum -= 1
|
||||
endwhile
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
let s:alphabet = s:get_base64_alphabet()
|
||||
|
||||
function! lsp#utils#base64_decode(data) abort
|
||||
let l:ret = []
|
||||
|
||||
" Process base64 string in chunks of 4 chars
|
||||
for l:group in split(a:data, '.\{4}\zs')
|
||||
let l:group_dec = 0
|
||||
|
||||
" Convert 4 chars to 3 octets
|
||||
for l:char in split(l:group, '\zs')
|
||||
let l:group_dec = l:group_dec * 64
|
||||
let l:group_dec += max([index(s:alphabet, l:char), 0])
|
||||
endfor
|
||||
|
||||
" Split the number representing the 3 octets into the individual
|
||||
" octets
|
||||
let l:octets = []
|
||||
let l:i = 0
|
||||
while l:i < 3
|
||||
call add(l:octets, l:group_dec % 256)
|
||||
let l:group_dec = l:group_dec / 256
|
||||
let l:i += 1
|
||||
endwhile
|
||||
|
||||
call extend(l:ret, reverse(l:octets))
|
||||
endfor
|
||||
|
||||
" Handle padding
|
||||
if len(a:data) >= 2
|
||||
if strpart(a:data, len(a:data) - 2) ==# '=='
|
||||
call remove(l:ret, -2, -1)
|
||||
elseif strpart(a:data, len(a:data) - 1) ==# '='
|
||||
call remove(l:ret, -1, -1)
|
||||
endif
|
||||
endif
|
||||
|
||||
return l:ret
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#make_valid_word(str) abort
|
||||
let l:str = substitute(a:str, '\$[0-9]\+\|\${\%(\\.\|[^}]\)\+}', '', 'g')
|
||||
let l:str = substitute(l:str, '\\\(.\)', '\1', 'g')
|
||||
let l:valid = matchstr(l:str, '^[^"'' (<{\[\t\r\n]\+')
|
||||
if empty(l:valid)
|
||||
return l:str
|
||||
endif
|
||||
if l:valid =~# ':$'
|
||||
return l:valid[:-2]
|
||||
endif
|
||||
return l:valid
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#_split_by_eol(text) abort
|
||||
return split(a:text, '\r\n\|\r\|\n', v:true)
|
||||
endfunction
|
||||
|
||||
" parse command options like "-key" or "-key=value"
|
||||
function! lsp#utils#parse_command_options(params) abort
|
||||
let l:result = {}
|
||||
for l:param in a:params
|
||||
let l:match = matchlist(l:param, '-\{1,2}\zs\([^=]*\)\(=\(.*\)\)\?\m')
|
||||
let l:result[l:match[1]] = l:match[3]
|
||||
endfor
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#is_large_window(winid) abort
|
||||
let l:buffer_size = line2byte(line('$', a:winid))
|
||||
return g:lsp_max_buffer_size >= 0 && l:buffer_size >= g:lsp_max_buffer_size
|
||||
endfunction
|
||||
|
||||
" polyfill for the neovim wait function
|
||||
if exists('*wait')
|
||||
function! lsp#utils#_wait(timeout, condition, ...) abort
|
||||
if type(a:timeout) != type(0)
|
||||
return -3
|
||||
endif
|
||||
if type(get(a:000, 0, 0)) != type(0)
|
||||
return -3
|
||||
endif
|
||||
while 1
|
||||
let l:result=call('wait', extend([a:timeout, a:condition], a:000))
|
||||
if l:result != -3 " ignore spurious errors
|
||||
return l:result
|
||||
endif
|
||||
endwhile
|
||||
endfunction
|
||||
else
|
||||
function! lsp#utils#_wait(timeout, condition, ...) abort
|
||||
try
|
||||
let l:timeout = a:timeout / 1000.0
|
||||
let l:interval = get(a:000, 0, 200)
|
||||
let l:Condition = a:condition
|
||||
if type(l:Condition) != type(function('eval'))
|
||||
let l:Condition = function('eval', l:Condition)
|
||||
endif
|
||||
let l:start = reltime()
|
||||
while l:timeout < 0 || reltimefloat(reltime(l:start)) < l:timeout
|
||||
if l:Condition()
|
||||
return 0
|
||||
endif
|
||||
|
||||
execute 'sleep ' . l:interval . 'm'
|
||||
endwhile
|
||||
return -1
|
||||
catch /^Vim:Interrupt$/
|
||||
return -2
|
||||
endtry
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! lsp#utils#iteratable(list) abort
|
||||
return type(a:list) !=# v:t_list ? [] : a:list
|
||||
endfunction
|
||||
42
dot_vim/plugged/vim-lsp/autoload/lsp/utils/args.vim
Normal file
42
dot_vim/plugged/vim-lsp/autoload/lsp/utils/args.vim
Normal file
@@ -0,0 +1,42 @@
|
||||
function! lsp#utils#args#_parse(args, opt, remainder_key) abort
|
||||
let l:result = {}
|
||||
let l:is_opts = v:true
|
||||
let l:remainder = []
|
||||
for l:item in split(a:args, ' ')
|
||||
if l:item[:1] !=# '--'
|
||||
let l:is_opts = v:false
|
||||
endif
|
||||
|
||||
if l:is_opts == v:false
|
||||
call add(l:remainder, l:item)
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:parts = split(l:item, '=')
|
||||
let l:key = l:parts[0]
|
||||
let l:value = get(l:parts, 1, '')
|
||||
let l:key = l:key[2:]
|
||||
|
||||
if has_key(a:opt, l:key)
|
||||
if has_key(a:opt[l:key], 'type')
|
||||
let l:type = a:opt[l:key]['type']
|
||||
if l:type == type(v:true)
|
||||
if l:value ==# 'false' || l:value ==# '0' || l:value ==# ''
|
||||
let l:value = 0
|
||||
else
|
||||
let l:value = 1
|
||||
endif
|
||||
elseif l:type ==# type(0)
|
||||
let l:value = str2nr(l:value)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
let l:result[l:key] = l:value
|
||||
endfor
|
||||
|
||||
if a:remainder_key != v:null
|
||||
let l:result[a:remainder_key] = join(l:remainder)
|
||||
endif
|
||||
|
||||
return l:result
|
||||
endfunction
|
||||
74
dot_vim/plugged/vim-lsp/autoload/lsp/utils/buffer.vim
Normal file
74
dot_vim/plugged/vim-lsp/autoload/lsp/utils/buffer.vim
Normal file
@@ -0,0 +1,74 @@
|
||||
let s:fixendofline_exists = exists('+fixendofline')
|
||||
|
||||
function! s:get_fixendofline(buf) abort
|
||||
let l:eol = getbufvar(a:buf, '&endofline')
|
||||
let l:binary = getbufvar(a:buf, '&binary')
|
||||
|
||||
if s:fixendofline_exists
|
||||
let l:fixeol = getbufvar(a:buf, '&fixendofline')
|
||||
|
||||
if !l:binary
|
||||
" When 'binary' is off and 'fixeol' is on, 'endofline' is not used
|
||||
"
|
||||
" When 'binary' is off and 'fixeol' is off, 'endofline' is used to
|
||||
" remember the presence of a <EOL>
|
||||
return l:fixeol || l:eol
|
||||
else
|
||||
" When 'binary' is on, the value of 'fixeol' doesn't matter
|
||||
return l:eol
|
||||
endif
|
||||
else
|
||||
" When 'binary' is off the value of 'endofline' is not used
|
||||
"
|
||||
" When 'binary' is on 'endofline' is used to remember the presence of
|
||||
" a <EOL>
|
||||
return !l:binary || l:eol
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#buffer#_get_fixendofline(bufnr) abort
|
||||
return s:get_fixendofline(a:bufnr)
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#buffer#_get_lines(buf) abort
|
||||
let l:lines = getbufline(a:buf, 1, '$')
|
||||
if s:get_fixendofline(a:buf)
|
||||
let l:lines += ['']
|
||||
endif
|
||||
return l:lines
|
||||
endfunction
|
||||
|
||||
" @params {location} = {
|
||||
" 'uri': 'file://....',
|
||||
" 'range': {
|
||||
" 'start': { 'line': 1, 'character': 1 },
|
||||
" 'end': { 'line': 1, 'character': 1 },
|
||||
" }
|
||||
" }
|
||||
function! lsp#utils#buffer#_open_lsp_location(location) abort
|
||||
let l:path = lsp#utils#uri_to_path(a:location['uri'])
|
||||
let l:bufnr = bufnr(l:path)
|
||||
|
||||
let [l:start_line, l:start_col] = lsp#utils#position#lsp_to_vim(l:bufnr, a:location['range']['start'])
|
||||
let [l:end_line, l:end_col] = lsp#utils#position#lsp_to_vim(l:bufnr, a:location['range']['end'])
|
||||
|
||||
normal! m'
|
||||
if &modified && !&hidden
|
||||
let l:cmd = l:bufnr !=# -1 ? 'sb ' . l:bufnr : 'split ' . fnameescape(l:path)
|
||||
else
|
||||
let l:cmd = l:bufnr !=# -1 ? 'b ' . l:bufnr : 'edit ' . fnameescape(l:path)
|
||||
endif
|
||||
execute l:cmd . ' | call cursor('.l:start_line.','.l:start_col.')'
|
||||
|
||||
normal! V
|
||||
call setpos("'<", [l:bufnr, l:start_line, l:start_col])
|
||||
call setpos("'>", [l:bufnr, l:end_line, l:end_col])
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#buffer#get_indent_size(bufnr) abort
|
||||
let l:shiftwidth = getbufvar(a:bufnr, '&shiftwidth')
|
||||
if getbufvar(a:bufnr, '&shiftwidth')
|
||||
return l:shiftwidth
|
||||
endif
|
||||
return getbufvar(a:bufnr, '&tabstop')
|
||||
endfunction
|
||||
165
dot_vim/plugged/vim-lsp/autoload/lsp/utils/diff.vim
Normal file
165
dot_vim/plugged/vim-lsp/autoload/lsp/utils/diff.vim
Normal file
@@ -0,0 +1,165 @@
|
||||
" This is copied from https://github.com/natebosch/vim-lsc/blob/master/autoload/lsc/diff.vim
|
||||
"
|
||||
" Computes a simplistic diff between [old] and [new].
|
||||
"
|
||||
" Returns a dict with keys `range`, `rangeLength`, and `text` matching the LSP
|
||||
" definition of `TextDocumentContentChangeEvent`.
|
||||
"
|
||||
" Finds a single change between the common prefix, and common postfix.
|
||||
let s:has_lua = has('nvim-0.4.0') || (has('lua') && has('patch-8.2.0775'))
|
||||
" lua array and neovim vim list index starts with 1 while vim lists starts with 0.
|
||||
" starting patch-8.2.1066 vim lists array index was changed to start with 1.
|
||||
let s:lua_array_start_index = has('nvim-0.4.0') || has('patch-8.2.1066')
|
||||
|
||||
function! s:init_lua() abort
|
||||
lua <<EOF
|
||||
-- Returns a zero-based index of the last line that is different between
|
||||
-- old and new. If old and new are not zero indexed, pass offset to indicate
|
||||
-- the index base.
|
||||
function vimlsp_last_difference(old, new, offset, line_count)
|
||||
for i = 0, line_count - 1 do
|
||||
if old[#old - i + offset] ~= new[#new - i + offset] then
|
||||
return -1 * i
|
||||
end
|
||||
end
|
||||
return -1 * line_count
|
||||
end
|
||||
-- Returns a zero-based index of the first line that is different between
|
||||
-- old and new. If old and new are not zero indexed, pass offset to indicate
|
||||
-- the index base.
|
||||
function vimlsp_first_difference(old, new, offset, line_count)
|
||||
for i = 0, line_count - 1 do
|
||||
if old[i + offset] ~= new[i + offset] then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return line_count - 1
|
||||
end
|
||||
EOF
|
||||
let s:lua = 1
|
||||
endfunction
|
||||
|
||||
if s:has_lua && !exists('s:lua')
|
||||
call s:init_lua()
|
||||
endif
|
||||
|
||||
function! lsp#utils#diff#compute(old, new) abort
|
||||
let [l:start_line, l:start_char] = s:FirstDifference(a:old, a:new)
|
||||
let [l:end_line, l:end_char] =
|
||||
\ s:LastDifference(a:old[l:start_line :], a:new[l:start_line :], l:start_char)
|
||||
|
||||
let l:text = s:ExtractText(a:new, l:start_line, l:start_char, l:end_line, l:end_char)
|
||||
let l:length = s:Length(a:old, l:start_line, l:start_char, l:end_line, l:end_char)
|
||||
|
||||
let l:adj_end_line = len(a:old) + l:end_line
|
||||
let l:adj_end_char = l:end_line == 0 ? 0 : strchars(a:old[l:end_line]) + l:end_char + 1
|
||||
|
||||
let l:result = { 'range': {'start': {'line': l:start_line, 'character': l:start_char},
|
||||
\ 'end': {'line': l:adj_end_line, 'character': l:adj_end_char}},
|
||||
\ 'text': l:text,
|
||||
\ 'rangeLength': l:length,
|
||||
\}
|
||||
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
" Finds the line and character of the first different character between two
|
||||
" list of Strings.
|
||||
function! s:FirstDifference(old, new) abort
|
||||
let l:line_count = min([len(a:old), len(a:new)])
|
||||
if l:line_count == 0 | return [0, 0] | endif
|
||||
if g:lsp_use_lua && s:has_lua
|
||||
let l:eval = has('nvim') ? 'vim.api.nvim_eval' : 'vim.eval'
|
||||
let l:i = luaeval('vimlsp_first_difference('
|
||||
\.l:eval.'("a:old"),'.l:eval.'("a:new"),'.s:lua_array_start_index.','.l:line_count.')')
|
||||
else
|
||||
for l:i in range(l:line_count)
|
||||
if a:old[l:i] !=# a:new[l:i] | break | endif
|
||||
endfor
|
||||
endif
|
||||
if l:i >= l:line_count
|
||||
return [l:line_count - 1, strchars(a:old[l:line_count - 1])]
|
||||
endif
|
||||
let l:old_line = a:old[l:i]
|
||||
let l:new_line = a:new[l:i]
|
||||
let l:length = min([strchars(l:old_line), strchars(l:new_line)])
|
||||
let l:j = 0
|
||||
while l:j < l:length
|
||||
if strgetchar(l:old_line, l:j) != strgetchar(l:new_line, l:j) | break | endif
|
||||
let l:j += 1
|
||||
endwhile
|
||||
return [l:i, l:j]
|
||||
endfunction
|
||||
|
||||
function! s:LastDifference(old, new, start_char) abort
|
||||
let l:line_count = min([len(a:old), len(a:new)])
|
||||
if l:line_count == 0 | return [0, 0] | endif
|
||||
if g:lsp_use_lua && s:has_lua
|
||||
let l:eval = has('nvim') ? 'vim.api.nvim_eval' : 'vim.eval'
|
||||
let l:i = luaeval('vimlsp_last_difference('
|
||||
\.l:eval.'("a:old"),'.l:eval.'("a:new"),'.s:lua_array_start_index.','.l:line_count.')')
|
||||
else
|
||||
for l:i in range(-1, -1 * l:line_count, -1)
|
||||
if a:old[l:i] !=# a:new[l:i] | break | endif
|
||||
endfor
|
||||
endif
|
||||
if l:i <= -1 * l:line_count
|
||||
let l:i = -1 * l:line_count
|
||||
let l:old_line = strcharpart(a:old[l:i], a:start_char)
|
||||
let l:new_line = strcharpart(a:new[l:i], a:start_char)
|
||||
else
|
||||
let l:old_line = a:old[l:i]
|
||||
let l:new_line = a:new[l:i]
|
||||
endif
|
||||
let l:old_line_length = strchars(l:old_line)
|
||||
let l:new_line_length = strchars(l:new_line)
|
||||
let l:length = min([l:old_line_length, l:new_line_length])
|
||||
let l:j = -1
|
||||
while l:j >= -1 * l:length
|
||||
if strgetchar(l:old_line, l:old_line_length + l:j) !=
|
||||
\ strgetchar(l:new_line, l:new_line_length + l:j)
|
||||
break
|
||||
endif
|
||||
let l:j -= 1
|
||||
endwhile
|
||||
return [l:i, l:j]
|
||||
endfunction
|
||||
|
||||
function! s:ExtractText(lines, start_line, start_char, end_line, end_char) abort
|
||||
if a:start_line == len(a:lines) + a:end_line
|
||||
if a:end_line == 0 | return '' | endif
|
||||
let l:line = a:lines[a:start_line]
|
||||
let l:length = strchars(l:line) + a:end_char - a:start_char + 1
|
||||
return strcharpart(l:line, a:start_char, l:length)
|
||||
endif
|
||||
let l:result = strcharpart(a:lines[a:start_line], a:start_char) . "\n"
|
||||
for l:line in a:lines[a:start_line + 1:a:end_line - 1]
|
||||
let l:result .= l:line . "\n"
|
||||
endfor
|
||||
if a:end_line != 0
|
||||
let l:line = a:lines[a:end_line]
|
||||
let l:length = strchars(l:line) + a:end_char + 1
|
||||
let l:result .= strcharpart(l:line, 0, l:length)
|
||||
endif
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
function! s:Length(lines, start_line, start_char, end_line, end_char) abort
|
||||
let l:adj_end_line = len(a:lines) + a:end_line
|
||||
if l:adj_end_line >= len(a:lines)
|
||||
let l:adj_end_char = a:end_char - 1
|
||||
else
|
||||
let l:adj_end_char = strchars(a:lines[l:adj_end_line]) + a:end_char
|
||||
endif
|
||||
if a:start_line == l:adj_end_line
|
||||
return l:adj_end_char - a:start_char + 1
|
||||
endif
|
||||
let l:result = strchars(a:lines[a:start_line]) - a:start_char + 1
|
||||
let l:line = a:start_line + 1
|
||||
while l:line < l:adj_end_line
|
||||
let l:result += strchars(a:lines[l:line]) + 1
|
||||
let l:line += 1
|
||||
endwhile
|
||||
let l:result += l:adj_end_char + 1
|
||||
return l:result
|
||||
endfunction
|
||||
409
dot_vim/plugged/vim-lsp/autoload/lsp/utils/job.vim
Normal file
409
dot_vim/plugged/vim-lsp/autoload/lsp/utils/job.vim
Normal file
@@ -0,0 +1,409 @@
|
||||
" https://github.com/prabirshrestha/async.vim#2082d13bb195f3203d41a308b89417426a7deca1 (dirty)
|
||||
" :AsyncEmbed path=./autoload/lsp/utils/job.vim namespace=lsp#utils#job
|
||||
|
||||
" Author: Prabir Shrestha <mail at prabir dot me>
|
||||
" Website: https://github.com/prabirshrestha/async.vim
|
||||
" License: The MIT License {{{
|
||||
" The MIT License (MIT)
|
||||
"
|
||||
" Copyright (c) 2016 Prabir Shrestha
|
||||
"
|
||||
" Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
" of this software and associated documentation files (the "Software"), to deal
|
||||
" in the Software without restriction, including without limitation the rights
|
||||
" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
" copies of the Software, and to permit persons to whom the Software is
|
||||
" furnished to do so, subject to the following conditions:
|
||||
"
|
||||
" The above copyright notice and this permission notice shall be included in all
|
||||
" copies or substantial portions of the Software.
|
||||
"
|
||||
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
" SOFTWARE.
|
||||
" }}}
|
||||
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
let s:jobidseq = 0
|
||||
let s:jobs = {} " { job, opts, type: 'vimjob|nvimjob'}
|
||||
let s:job_type_nvimjob = 'nvimjob'
|
||||
let s:job_type_vimjob = 'vimjob'
|
||||
let s:job_error_unsupported_job_type = -2 " unsupported job type
|
||||
|
||||
function! s:noop(...) abort
|
||||
endfunction
|
||||
|
||||
function! s:job_supported_types() abort
|
||||
let l:supported_types = []
|
||||
if has('nvim')
|
||||
let l:supported_types += [s:job_type_nvimjob]
|
||||
endif
|
||||
if !has('nvim') && has('job') && has('channel') && has('lambda')
|
||||
let l:supported_types += [s:job_type_vimjob]
|
||||
endif
|
||||
return l:supported_types
|
||||
endfunction
|
||||
|
||||
function! s:job_supports_type(type) abort
|
||||
return index(s:job_supported_types(), a:type) >= 0
|
||||
endfunction
|
||||
|
||||
function! s:out_cb(jobid, opts, job, data) abort
|
||||
call a:opts.on_stdout(a:jobid, a:data, 'stdout')
|
||||
endfunction
|
||||
|
||||
function! s:out_cb_array(jobid, opts, job, data) abort
|
||||
call a:opts.on_stdout(a:jobid, split(a:data, "\n", 1), 'stdout')
|
||||
endfunction
|
||||
|
||||
function! s:err_cb(jobid, opts, job, data) abort
|
||||
call a:opts.on_stderr(a:jobid, a:data, 'stderr')
|
||||
endfunction
|
||||
|
||||
function! s:err_cb_array(jobid, opts, job, data) abort
|
||||
call a:opts.on_stderr(a:jobid, split(a:data, "\n", 1), 'stderr')
|
||||
endfunction
|
||||
|
||||
function! s:exit_cb(jobid, opts, job, status) abort
|
||||
if has_key(a:opts, 'on_exit')
|
||||
call a:opts.on_exit(a:jobid, a:status, 'exit')
|
||||
endif
|
||||
if has_key(s:jobs, a:jobid)
|
||||
call remove(s:jobs, a:jobid)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:on_stdout(jobid, data, event) abort
|
||||
let l:jobinfo = s:jobs[a:jobid]
|
||||
call l:jobinfo.opts.on_stdout(a:jobid, a:data, a:event)
|
||||
endfunction
|
||||
|
||||
function! s:on_stdout_string(jobid, data, event) abort
|
||||
let l:jobinfo = s:jobs[a:jobid]
|
||||
call l:jobinfo.opts.on_stdout(a:jobid, join(a:data, "\n"), a:event)
|
||||
endfunction
|
||||
|
||||
function! s:on_stderr(jobid, data, event) abort
|
||||
let l:jobinfo = s:jobs[a:jobid]
|
||||
call l:jobinfo.opts.on_stderr(a:jobid, a:data, a:event)
|
||||
endfunction
|
||||
|
||||
function! s:on_stderr_string(jobid, data, event) abort
|
||||
let l:jobinfo = s:jobs[a:jobid]
|
||||
call l:jobinfo.opts.on_stderr(a:jobid, join(a:data, "\n"), a:event)
|
||||
endfunction
|
||||
|
||||
function! s:on_exit(jobid, status, event) abort
|
||||
if has_key(s:jobs, a:jobid)
|
||||
let l:jobinfo = s:jobs[a:jobid]
|
||||
if has_key(l:jobinfo.opts, 'on_exit')
|
||||
call l:jobinfo.opts.on_exit(a:jobid, a:status, a:event)
|
||||
endif
|
||||
if has_key(s:jobs, a:jobid)
|
||||
call remove(s:jobs, a:jobid)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:job_start(cmd, opts) abort
|
||||
let l:jobtypes = s:job_supported_types()
|
||||
let l:jobtype = ''
|
||||
|
||||
if has_key(a:opts, 'type')
|
||||
if type(a:opts.type) == type('')
|
||||
if !s:job_supports_type(a:opts.type)
|
||||
return s:job_error_unsupported_job_type
|
||||
endif
|
||||
let l:jobtype = a:opts.type
|
||||
else
|
||||
let l:jobtypes = a:opts.type
|
||||
endif
|
||||
endif
|
||||
|
||||
if empty(l:jobtype)
|
||||
" find the best jobtype
|
||||
for l:jobtype2 in l:jobtypes
|
||||
if s:job_supports_type(l:jobtype2)
|
||||
let l:jobtype = l:jobtype2
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
if l:jobtype ==? ''
|
||||
return s:job_error_unsupported_job_type
|
||||
endif
|
||||
|
||||
" options shared by both vim and neovim
|
||||
let l:jobopt = {}
|
||||
if has_key(a:opts, 'cwd')
|
||||
let l:jobopt.cwd = a:opts.cwd
|
||||
endif
|
||||
if has_key(a:opts, 'env')
|
||||
let l:jobopt.env = a:opts.env
|
||||
endif
|
||||
|
||||
let l:normalize = get(a:opts, 'normalize', 'array') " array/string/raw
|
||||
|
||||
if l:jobtype == s:job_type_nvimjob
|
||||
if l:normalize ==# 'string'
|
||||
let l:jobopt['on_stdout'] = has_key(a:opts, 'on_stdout') ? function('s:on_stdout_string') : function('s:noop')
|
||||
let l:jobopt['on_stderr'] = has_key(a:opts, 'on_stderr') ? function('s:on_stderr_string') : function('s:noop')
|
||||
else " array or raw
|
||||
let l:jobopt['on_stdout'] = has_key(a:opts, 'on_stdout') ? function('s:on_stdout') : function('s:noop')
|
||||
let l:jobopt['on_stderr'] = has_key(a:opts, 'on_stderr') ? function('s:on_stderr') : function('s:noop')
|
||||
endif
|
||||
call extend(l:jobopt, { 'on_exit': function('s:on_exit') })
|
||||
let l:job = jobstart(a:cmd, l:jobopt)
|
||||
if l:job <= 0
|
||||
return l:job
|
||||
endif
|
||||
let l:jobid = l:job " nvimjobid and internal jobid is same
|
||||
let s:jobs[l:jobid] = {
|
||||
\ 'type': s:job_type_nvimjob,
|
||||
\ 'opts': a:opts,
|
||||
\ }
|
||||
let s:jobs[l:jobid].job = l:job
|
||||
elseif l:jobtype == s:job_type_vimjob
|
||||
let s:jobidseq = s:jobidseq + 1
|
||||
let l:jobid = s:jobidseq
|
||||
if l:normalize ==# 'array'
|
||||
let l:jobopt['out_cb'] = has_key(a:opts, 'on_stdout') ? function('s:out_cb_array', [l:jobid, a:opts]) : function('s:noop')
|
||||
let l:jobopt['err_cb'] = has_key(a:opts, 'on_stderr') ? function('s:err_cb_array', [l:jobid, a:opts]) : function('s:noop')
|
||||
else " raw or string
|
||||
let l:jobopt['out_cb'] = has_key(a:opts, 'on_stdout') ? function('s:out_cb', [l:jobid, a:opts]) : function('s:noop')
|
||||
let l:jobopt['err_cb'] = has_key(a:opts, 'on_stderr') ? function('s:err_cb', [l:jobid, a:opts]) : function('s:noop')
|
||||
endif
|
||||
call extend(l:jobopt, {
|
||||
\ 'exit_cb': function('s:exit_cb', [l:jobid, a:opts]),
|
||||
\ 'mode': 'raw',
|
||||
\ })
|
||||
if has('patch-8.1.889')
|
||||
let l:jobopt['noblock'] = 1
|
||||
endif
|
||||
let l:job = job_start(a:cmd, l:jobopt)
|
||||
if job_status(l:job) !=? 'run'
|
||||
return -1
|
||||
endif
|
||||
let s:jobs[l:jobid] = {
|
||||
\ 'type': s:job_type_vimjob,
|
||||
\ 'opts': a:opts,
|
||||
\ 'job': l:job,
|
||||
\ 'channel': job_getchannel(l:job),
|
||||
\ 'buffer': ''
|
||||
\ }
|
||||
else
|
||||
return s:job_error_unsupported_job_type
|
||||
endif
|
||||
|
||||
return l:jobid
|
||||
endfunction
|
||||
|
||||
function! s:job_stop(jobid) abort
|
||||
if has_key(s:jobs, a:jobid)
|
||||
let l:jobinfo = s:jobs[a:jobid]
|
||||
if l:jobinfo.type == s:job_type_nvimjob
|
||||
" See: vital-Whisky/System.Job
|
||||
try
|
||||
call jobstop(a:jobid)
|
||||
catch /^Vim\%((\a\+)\)\=:E900/
|
||||
" NOTE:
|
||||
" Vim does not raise exception even the job has already closed so fail
|
||||
" silently for 'E900: Invalid job id' exception
|
||||
endtry
|
||||
elseif l:jobinfo.type == s:job_type_vimjob
|
||||
if type(s:jobs[a:jobid].job) == v:t_job
|
||||
call job_stop(s:jobs[a:jobid].job)
|
||||
elseif type(s:jobs[a:jobid].job) == v:t_channel
|
||||
call ch_close(s:jobs[a:jobid].job)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:job_send(jobid, data, opts) abort
|
||||
let l:jobinfo = s:jobs[a:jobid]
|
||||
let l:close_stdin = get(a:opts, 'close_stdin', 0)
|
||||
if l:jobinfo.type == s:job_type_nvimjob
|
||||
call jobsend(a:jobid, a:data)
|
||||
if l:close_stdin
|
||||
call chanclose(a:jobid, 'stdin')
|
||||
endif
|
||||
elseif l:jobinfo.type == s:job_type_vimjob
|
||||
" There is no easy way to know when ch_sendraw() finishes writing data
|
||||
" on a non-blocking channels -- has('patch-8.1.889') -- and because of
|
||||
" this, we cannot safely call ch_close_in(). So when we find ourselves
|
||||
" in this situation (i.e. noblock=1 and close stdin after send) we fall
|
||||
" back to using s:flush_vim_sendraw() and wait for transmit buffer to be
|
||||
" empty
|
||||
"
|
||||
" Ref: https://groups.google.com/d/topic/vim_dev/UNNulkqb60k/discussion
|
||||
if has('patch-8.1.818') && (!has('patch-8.1.889') || !l:close_stdin)
|
||||
call ch_sendraw(l:jobinfo.channel, a:data)
|
||||
else
|
||||
let l:jobinfo.buffer .= a:data
|
||||
call s:flush_vim_sendraw(a:jobid, v:null)
|
||||
endif
|
||||
if l:close_stdin
|
||||
while len(l:jobinfo.buffer) != 0
|
||||
sleep 1m
|
||||
endwhile
|
||||
call ch_close_in(l:jobinfo.channel)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:flush_vim_sendraw(jobid, timer) abort
|
||||
" https://github.com/vim/vim/issues/2548
|
||||
" https://github.com/natebosch/vim-lsc/issues/67#issuecomment-357469091
|
||||
let l:jobinfo = s:jobs[a:jobid]
|
||||
sleep 1m
|
||||
if len(l:jobinfo.buffer) <= 4096
|
||||
call ch_sendraw(l:jobinfo.channel, l:jobinfo.buffer)
|
||||
let l:jobinfo.buffer = ''
|
||||
else
|
||||
let l:to_send = l:jobinfo.buffer[:4095]
|
||||
let l:jobinfo.buffer = l:jobinfo.buffer[4096:]
|
||||
call ch_sendraw(l:jobinfo.channel, l:to_send)
|
||||
call timer_start(1, function('s:flush_vim_sendraw', [a:jobid]))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:job_wait_single(jobid, timeout, start) abort
|
||||
if !has_key(s:jobs, a:jobid)
|
||||
return -3
|
||||
endif
|
||||
|
||||
let l:jobinfo = s:jobs[a:jobid]
|
||||
if l:jobinfo.type == s:job_type_nvimjob
|
||||
let l:timeout = a:timeout - reltimefloat(reltime(a:start)) * 1000
|
||||
return jobwait([a:jobid], float2nr(l:timeout))[0]
|
||||
elseif l:jobinfo.type == s:job_type_vimjob
|
||||
let l:timeout = a:timeout / 1000.0
|
||||
try
|
||||
while l:timeout < 0 || reltimefloat(reltime(a:start)) < l:timeout
|
||||
let l:info = job_info(l:jobinfo.job)
|
||||
if l:info.status ==# 'dead'
|
||||
return l:info.exitval
|
||||
elseif l:info.status ==# 'fail'
|
||||
return -3
|
||||
endif
|
||||
sleep 1m
|
||||
endwhile
|
||||
catch /^Vim:Interrupt$/
|
||||
return -2
|
||||
endtry
|
||||
endif
|
||||
return -1
|
||||
endfunction
|
||||
|
||||
function! s:job_wait(jobids, timeout) abort
|
||||
let l:start = reltime()
|
||||
let l:exitcode = 0
|
||||
let l:ret = []
|
||||
for l:jobid in a:jobids
|
||||
if l:exitcode != -2 " Not interrupted.
|
||||
let l:exitcode = s:job_wait_single(l:jobid, a:timeout, l:start)
|
||||
endif
|
||||
let l:ret += [l:exitcode]
|
||||
endfor
|
||||
return l:ret
|
||||
endfunction
|
||||
|
||||
function! s:job_pid(jobid) abort
|
||||
if !has_key(s:jobs, a:jobid)
|
||||
return 0
|
||||
endif
|
||||
|
||||
let l:jobinfo = s:jobs[a:jobid]
|
||||
if l:jobinfo.type == s:job_type_nvimjob
|
||||
return jobpid(a:jobid)
|
||||
elseif l:jobinfo.type == s:job_type_vimjob
|
||||
let l:vimjobinfo = job_info(a:jobid)
|
||||
if type(l:vimjobinfo) == type({}) && has_key(l:vimjobinfo, 'process')
|
||||
return l:vimjobinfo['process']
|
||||
endif
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! s:callback_cb(jobid, opts, ch, data) abort
|
||||
if has_key(a:opts, 'on_stdout')
|
||||
call a:opts.on_stdout(a:jobid, a:data, 'stdout')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:callback_cb_array(jobid, opts, ch, data) abort
|
||||
if has_key(a:opts, 'on_stdout')
|
||||
call a:opts.on_stdout(a:jobid, split(a:data, "\n", 1), 'stdout')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:close_cb(jobid, opts, ch) abort
|
||||
if has_key(a:opts, 'on_exit')
|
||||
call a:opts.on_exit(a:jobid, 'closed', 'exit')
|
||||
endif
|
||||
if has_key(s:jobs, a:jobid)
|
||||
call remove(s:jobs, a:jobid)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" public apis {{{
|
||||
function! lsp#utils#job#start(cmd, opts) abort
|
||||
return s:job_start(a:cmd, a:opts)
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#job#stop(jobid) abort
|
||||
call s:job_stop(a:jobid)
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#job#send(jobid, data, ...) abort
|
||||
let l:opts = get(a:000, 0, {})
|
||||
call s:job_send(a:jobid, a:data, l:opts)
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#job#wait(jobids, ...) abort
|
||||
let l:timeout = get(a:000, 0, -1)
|
||||
return s:job_wait(a:jobids, l:timeout)
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#job#pid(jobid) abort
|
||||
return s:job_pid(a:jobid)
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#job#connect(addr, opts) abort
|
||||
let s:jobidseq = s:jobidseq + 1
|
||||
let l:jobid = s:jobidseq
|
||||
let l:retry = 0
|
||||
let l:normalize = get(a:opts, 'normalize', 'array') " array/string/raw
|
||||
while l:retry < 5
|
||||
let l:ch = ch_open(a:addr, {'waittime': 1000})
|
||||
call ch_setoptions(l:ch, {
|
||||
\ 'callback': function(l:normalize ==# 'array' ? 's:callback_cb_array' : 's:callback_cb', [l:jobid, a:opts]),
|
||||
\ 'close_cb': function('s:close_cb', [l:jobid, a:opts]),
|
||||
\ 'mode': 'raw',
|
||||
\})
|
||||
if ch_status(l:ch) ==# 'open'
|
||||
break
|
||||
endif
|
||||
sleep 100m
|
||||
let l:retry += 1
|
||||
endwhile
|
||||
let s:jobs[l:jobid] = {
|
||||
\ 'type': s:job_type_vimjob,
|
||||
\ 'opts': a:opts,
|
||||
\ 'job': l:ch,
|
||||
\ 'channel': l:ch,
|
||||
\ 'buffer': ''
|
||||
\}
|
||||
return l:jobid
|
||||
endfunction
|
||||
" }}}
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
135
dot_vim/plugged/vim-lsp/autoload/lsp/utils/location.vim
Normal file
135
dot_vim/plugged/vim-lsp/autoload/lsp/utils/location.vim
Normal file
@@ -0,0 +1,135 @@
|
||||
function! s:open_location(path, line, col, ...) abort
|
||||
normal! m'
|
||||
let l:mods = a:0 ? a:1 : ''
|
||||
let l:buffer = bufnr(a:path)
|
||||
if l:mods ==# '' && &modified && !&hidden && l:buffer != bufnr('%')
|
||||
let l:mods = &splitbelow ? 'rightbelow' : 'leftabove'
|
||||
endif
|
||||
if l:mods ==# ''
|
||||
if l:buffer == bufnr('%')
|
||||
let l:cmd = ''
|
||||
else
|
||||
let l:cmd = (l:buffer !=# -1 ? 'b ' . l:buffer : 'edit ' . fnameescape(a:path)) . ' | '
|
||||
endif
|
||||
else
|
||||
let l:cmd = l:mods . ' ' . (l:buffer !=# -1 ? 'sb ' . l:buffer : 'split ' . fnameescape(a:path)) . ' | '
|
||||
endif
|
||||
execute l:cmd . 'call cursor('.a:line.','.a:col.')'
|
||||
endfunction
|
||||
|
||||
" @param location = {
|
||||
" 'filename',
|
||||
" 'lnum',
|
||||
" 'col',
|
||||
" }
|
||||
function! lsp#utils#location#_open_vim_list_item(location, mods) abort
|
||||
call s:open_location(a:location['filename'], a:location['lnum'], a:location['col'], a:mods)
|
||||
endfunction
|
||||
|
||||
" @params {location} = {
|
||||
" 'uri': 'file://....',
|
||||
" 'range': {
|
||||
" 'start': { 'line': 1, 'character': 1 },
|
||||
" 'end': { 'line': 1, 'character': 1 },
|
||||
" }
|
||||
" }
|
||||
function! lsp#utils#location#_open_lsp_location(location) abort
|
||||
let l:path = lsp#utils#uri_to_path(a:location['uri'])
|
||||
let l:bufnr = bufnr(l:path)
|
||||
|
||||
let [l:start_line, l:start_col] = lsp#utils#position#lsp_to_vim(l:bufnr, a:location['range']['start'])
|
||||
let [l:end_line, l:end_col] = lsp#utils#position#lsp_to_vim(l:bufnr, a:location['range']['end'])
|
||||
|
||||
call s:open_location(l:path, l:start_line, l:start_col)
|
||||
|
||||
normal! V
|
||||
call setpos("'<", [l:bufnr, l:start_line, l:start_col])
|
||||
call setpos("'>", [l:bufnr, l:end_line, l:end_col])
|
||||
endfunction
|
||||
|
||||
" @param loc = Location | LocationLink
|
||||
" @param cache = {} empty dict
|
||||
" @returns {
|
||||
" 'filename',
|
||||
" 'lnum',
|
||||
" 'col',
|
||||
" 'text',
|
||||
" 'viewstart?',
|
||||
" 'viewend?',
|
||||
" }
|
||||
function! s:lsp_location_item_to_vim(loc, cache) abort
|
||||
if has_key(a:loc, 'targetUri') " LocationLink
|
||||
let l:uri = a:loc['targetUri']
|
||||
let l:range = a:loc['targetSelectionRange']
|
||||
let l:use_link = 1
|
||||
else " Location
|
||||
let l:uri = a:loc['uri']
|
||||
let l:range = a:loc['range']
|
||||
let l:use_link = 0
|
||||
endif
|
||||
|
||||
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, l:range['start'])
|
||||
|
||||
let l:index = l:line - 1
|
||||
if has_key(a:cache, l:path)
|
||||
let l:text = a:cache[l:path][l:index]
|
||||
else
|
||||
let l:contents = getbufline(l:path, 1, '$')
|
||||
if !empty(l:contents)
|
||||
let l:text = get(l:contents, l:index, '')
|
||||
else
|
||||
let l:contents = readfile(l:path)
|
||||
let a:cache[l:path] = l:contents
|
||||
let l:text = get(l:contents, l:index, '')
|
||||
endif
|
||||
endif
|
||||
|
||||
if l:use_link
|
||||
" viewstart/end decremented to account for incrementing in _lsp_to_vim
|
||||
return {
|
||||
\ 'filename': l:path,
|
||||
\ 'lnum': l:line,
|
||||
\ 'col': l:col,
|
||||
\ 'text': l:text,
|
||||
\ 'viewstart': lsp#utils#position#lsp_to_vim(l:path, a:loc['targetRange']['start'])[0] - 1,
|
||||
\ 'viewend': lsp#utils#position#lsp_to_vim(l:path, a:loc['targetRange']['end'])[0] - 1,
|
||||
\ }
|
||||
else
|
||||
return {
|
||||
\ 'filename': l:path,
|
||||
\ 'lnum': l:line,
|
||||
\ 'col': l:col,
|
||||
\ 'text': l:text,
|
||||
\ }
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" @summary Use this to convert loc to vim list that is compatible with
|
||||
" quickfix and locllist items
|
||||
" @param loc = v:null | Location | Location[] | LocationLink
|
||||
" @returns []
|
||||
function! lsp#utils#location#_lsp_to_vim_list(loc) abort
|
||||
let l:result = []
|
||||
let l:cache = {}
|
||||
if empty(a:loc) " v:null
|
||||
return l:result
|
||||
elseif type(a:loc) == type([]) " Location[]
|
||||
for l:location in a:loc
|
||||
let l:vim_loc = s:lsp_location_item_to_vim(l:location, l:cache)
|
||||
if !empty(l:vim_loc) " https:// uri will return empty
|
||||
call add(l:result, l:vim_loc)
|
||||
endif
|
||||
endfor
|
||||
else " Location or LocationLink
|
||||
let l:vim_loc = s:lsp_location_item_to_vim(a:loc, l:cache)
|
||||
if !empty(l:vim_loc) " https:// uri will return empty
|
||||
call add(l:result, l:vim_loc)
|
||||
endif
|
||||
endif
|
||||
return l:result
|
||||
endfunction
|
||||
91
dot_vim/plugged/vim-lsp/autoload/lsp/utils/position.vim
Normal file
91
dot_vim/plugged/vim-lsp/autoload/lsp/utils/position.vim
Normal file
@@ -0,0 +1,91 @@
|
||||
" This function can be error prone if the caller forgets to use +1 to vim line
|
||||
" so use lsp#utils#position#lsp_to_vim instead
|
||||
" Convert a character-index (0-based) to byte-index (1-based)
|
||||
" This function requires a buffer specifier (expr, see :help bufname()),
|
||||
" a line number (lnum, 1-based), and a character-index (char, 0-based).
|
||||
function! s:to_col(expr, lnum, char) abort
|
||||
let l:lines = getbufline(a:expr, a:lnum)
|
||||
if l:lines == []
|
||||
if type(a:expr) != v:t_string || !filereadable(a:expr)
|
||||
" invalid a:expr
|
||||
return a:char + 1
|
||||
endif
|
||||
" a:expr is a file that is not yet loaded as a buffer
|
||||
let l:lines = readfile(a:expr, '', a:lnum)
|
||||
if l:lines == []
|
||||
" when the file is empty. a:char should be 0 in the case
|
||||
return a:char + 1
|
||||
endif
|
||||
endif
|
||||
let l:linestr = l:lines[-1]
|
||||
return strlen(strcharpart(l:linestr, 0, a:char)) + 1
|
||||
endfunction
|
||||
|
||||
" The inverse version of `s:to_col`.
|
||||
" Convert [lnum, col] to LSP's `Position`.
|
||||
function! s:to_char(expr, lnum, col) abort
|
||||
let l:lines = getbufline(a:expr, a:lnum)
|
||||
if l:lines == []
|
||||
if type(a:expr) != v:t_string || !filereadable(a:expr)
|
||||
" invalid a:expr
|
||||
return a:col - 1
|
||||
endif
|
||||
" a:expr is a file that is not yet loaded as a buffer
|
||||
let l:lines = readfile(a:expr, '', a:lnum)
|
||||
endif
|
||||
let l:linestr = l:lines[-1]
|
||||
return strchars(strpart(l:linestr, 0, a:col - 1))
|
||||
endfunction
|
||||
|
||||
" @param expr = see :help bufname()
|
||||
" @param position = {
|
||||
" 'line': 1,
|
||||
" 'character': 1
|
||||
" }
|
||||
" @returns [
|
||||
" line,
|
||||
" col
|
||||
" ]
|
||||
function! lsp#utils#position#lsp_to_vim(expr, position) abort
|
||||
let l:line = lsp#utils#position#lsp_line_to_vim(a:expr, a:position)
|
||||
let l:col = lsp#utils#position#lsp_character_to_vim(a:expr, a:position)
|
||||
return [l:line, l:col]
|
||||
endfunction
|
||||
|
||||
" @param expr = see :help bufname()
|
||||
" @param position = {
|
||||
" 'line': 1,
|
||||
" 'character': 1
|
||||
" }
|
||||
" @returns
|
||||
" line
|
||||
function! lsp#utils#position#lsp_line_to_vim(expr, position) abort
|
||||
return a:position['line'] + 1
|
||||
endfunction
|
||||
|
||||
" @param expr = see :help bufname()
|
||||
" @param position = {
|
||||
" 'line': 1,
|
||||
" 'character': 1
|
||||
" }
|
||||
" @returns
|
||||
" line
|
||||
function! lsp#utils#position#lsp_character_to_vim(expr, position) abort
|
||||
let l:line = a:position['line'] + 1 " optimize function overhead by not calling lsp_line_to_vim
|
||||
let l:char = a:position['character']
|
||||
return s:to_col(a:expr, l:line, l:char)
|
||||
endfunction
|
||||
|
||||
" @param expr = :help bufname()
|
||||
" @param pos = [lnum, col]
|
||||
" @returns {
|
||||
" 'line': line,
|
||||
" 'character': character
|
||||
" }
|
||||
function! lsp#utils#position#vim_to_lsp(expr, pos) abort
|
||||
return {
|
||||
\ 'line': a:pos[0] - 1,
|
||||
\ 'character': s:to_char(a:expr, a:pos[0], a:pos[1])
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
82
dot_vim/plugged/vim-lsp/autoload/lsp/utils/range.vim
Normal file
82
dot_vim/plugged/vim-lsp/autoload/lsp/utils/range.vim
Normal file
@@ -0,0 +1,82 @@
|
||||
"
|
||||
" Returns recent visual-mode range.
|
||||
"
|
||||
function! lsp#utils#range#_get_recent_visual_range() abort
|
||||
let l:start_pos = getpos("'<")[1 : 2]
|
||||
let l:end_pos = getpos("'>")[1 : 2]
|
||||
let l:end_pos[1] += 1 " To exclusive
|
||||
|
||||
" Fix line selection.
|
||||
let l:end_line = getline(l:end_pos[0])
|
||||
if l:end_pos[1] > strlen(l:end_line)
|
||||
let l:end_pos[1] = strlen(l:end_line) + 1
|
||||
endif
|
||||
|
||||
let l:range = {}
|
||||
let l:range['start'] = lsp#utils#position#vim_to_lsp('%', l:start_pos)
|
||||
let l:range['end'] = lsp#utils#position#vim_to_lsp('%', l:end_pos)
|
||||
return l:range
|
||||
endfunction
|
||||
|
||||
"
|
||||
" Returns current line range.
|
||||
"
|
||||
function! lsp#utils#range#_get_current_line_range() abort
|
||||
let l:pos = getpos('.')[1 : 2]
|
||||
let l:range = {}
|
||||
let l:range['start'] = lsp#utils#position#vim_to_lsp('%', l:pos)
|
||||
let l:range['end'] = lsp#utils#position#vim_to_lsp('%', [l:pos[0], l:pos[1] + strlen(getline(l:pos[0])) + 1])
|
||||
return l:range
|
||||
endfunction
|
||||
|
||||
" Convert a LSP range to one or more vim match positions.
|
||||
" If the range spans over multiple lines, break it down to multiple
|
||||
" positions, one for each line.
|
||||
" Return a list of positions.
|
||||
function! lsp#utils#range#lsp_to_vim(bufnr, range) abort
|
||||
let l:position = []
|
||||
|
||||
let [l:start_line, l:start_col] = lsp#utils#position#lsp_to_vim(a:bufnr, a:range['start'])
|
||||
let [l:end_line, l:end_col] = lsp#utils#position#lsp_to_vim(a:bufnr, a:range['end'])
|
||||
if l:end_line == l:start_line
|
||||
let l:position = [[
|
||||
\ l:start_line,
|
||||
\ l:start_col,
|
||||
\ l:end_col - l:start_col
|
||||
\ ]]
|
||||
else
|
||||
" First line
|
||||
let l:position = [[
|
||||
\ l:start_line,
|
||||
\ l:start_col,
|
||||
\ 999
|
||||
\ ]]
|
||||
|
||||
" Last line
|
||||
call add(l:position, [
|
||||
\ l:end_line,
|
||||
\ 1,
|
||||
\ l:end_col
|
||||
\ ])
|
||||
|
||||
" Lines in the middle
|
||||
let l:middle_lines = map(
|
||||
\ range(l:start_line + 1, l:end_line - 1),
|
||||
\ {_, l -> [l, 0, 999]}
|
||||
\ )
|
||||
|
||||
call extend(l:position, l:middle_lines)
|
||||
endif
|
||||
|
||||
return l:position
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#range#get_range() abort
|
||||
let l:char = lsp#utils#to_char('%', line('$'), col('$'))
|
||||
return {'start': {'line': 0, 'character': 0}, 'end': {'line': line('$')-1, 'character': l:char}}
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#range#get_range_curline() abort
|
||||
let l:char = lsp#utils#to_char('%', line('.'), col('$'))
|
||||
return {'start': {'line': line('.')-1, 'character': 0}, 'end': {'line': line('.')-1, 'character': l:char}}
|
||||
endfunction
|
||||
19
dot_vim/plugged/vim-lsp/autoload/lsp/utils/step.vim
Normal file
19
dot_vim/plugged/vim-lsp/autoload/lsp/utils/step.vim
Normal file
@@ -0,0 +1,19 @@
|
||||
function! s:next(steps, current_index, result) abort
|
||||
if len(a:steps) == a:current_index
|
||||
return
|
||||
endif
|
||||
let l:Step = a:steps[a:current_index]
|
||||
let l:ctx = {
|
||||
\ 'callback': function('s:callback', [a:steps, a:current_index]),
|
||||
\ 'result': a:result
|
||||
\ }
|
||||
call call(l:Step, [l:ctx])
|
||||
endfunction
|
||||
|
||||
function! s:callback(steps, current_index, ...) abort
|
||||
call s:next(a:steps, a:current_index + 1, a:000)
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#step#start(steps) abort
|
||||
call s:next(a:steps, 0, [])
|
||||
endfunction
|
||||
33
dot_vim/plugged/vim-lsp/autoload/lsp/utils/tagstack.vim
Normal file
33
dot_vim/plugged/vim-lsp/autoload/lsp/utils/tagstack.vim
Normal file
@@ -0,0 +1,33 @@
|
||||
if exists('*gettagstack') && exists('*settagstack')
|
||||
function! lsp#utils#tagstack#_update() 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
|
||||
else
|
||||
function! lsp#utils#tagstack#_update() abort
|
||||
" do nothing
|
||||
endfunction
|
||||
endif
|
||||
229
dot_vim/plugged/vim-lsp/autoload/lsp/utils/text_edit.vim
Normal file
229
dot_vim/plugged/vim-lsp/autoload/lsp/utils/text_edit.vim
Normal file
@@ -0,0 +1,229 @@
|
||||
function! lsp#utils#text_edit#get_range(text_edit) abort
|
||||
if type(a:text_edit) != v:t_dict
|
||||
return v:null
|
||||
endif
|
||||
let l:insert = get(a:text_edit, 'insert', v:null)
|
||||
if type(l:insert) == v:t_dict
|
||||
return l:insert
|
||||
endif
|
||||
return get(a:text_edit, 'range', v:null)
|
||||
endfunction
|
||||
|
||||
function! lsp#utils#text_edit#apply_text_edits(uri, text_edits) abort
|
||||
let l:current_bufname = bufname('%')
|
||||
let l:target_bufname = lsp#utils#uri_to_path(a:uri)
|
||||
let l:cursor_position = lsp#get_position()
|
||||
|
||||
call s:_switch(l:target_bufname)
|
||||
for l:text_edit in s:_normalize(a:text_edits)
|
||||
call s:_apply(bufnr(l:target_bufname), l:text_edit, l:cursor_position)
|
||||
endfor
|
||||
call s:_switch(l:current_bufname)
|
||||
|
||||
if bufnr(l:current_bufname) == bufnr(l:target_bufname)
|
||||
call cursor(lsp#utils#position#lsp_to_vim('%', l:cursor_position))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" @summary Use this to convert textedit to vim list that is compatible with
|
||||
" quickfix and locllist items
|
||||
" @param uri = DocumentUri
|
||||
" @param text_edit = TextEdit | TextEdit[]
|
||||
" @returns []
|
||||
function! lsp#utils#text_edit#_lsp_to_vim_list(uri, text_edit) abort
|
||||
let l:result = []
|
||||
let l:cache = {}
|
||||
if type(a:text_edit) == type([]) " TextEdit[]
|
||||
for l:text_edit in a:text_edit
|
||||
let l:vim_loc = s:lsp_text_edit_item_to_vim(a:uri, l:text_edit, l:cache)
|
||||
if !empty(l:vim_loc)
|
||||
call add(l:result, l:vim_loc)
|
||||
endif
|
||||
endfor
|
||||
else " TextEdit
|
||||
let l:vim_loc = s:lsp_text_edit_item_to_vim(a:uri, a:text_edit, l:cache)
|
||||
if !empty(l:vim_loc)
|
||||
call add(l:result, l:vim_loc)
|
||||
endif
|
||||
endif
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
" @param uri = DocumentUri
|
||||
" @param text_edit = TextEdit
|
||||
" @param cache = {} empty dict
|
||||
" @returns {
|
||||
" 'filename',
|
||||
" 'lnum',
|
||||
" 'col',
|
||||
" 'text',
|
||||
" }
|
||||
function! s:lsp_text_edit_item_to_vim(uri, text_edit, cache) abort
|
||||
if !lsp#utils#is_file_uri(a:uri)
|
||||
return v:null
|
||||
endif
|
||||
|
||||
let l:path = lsp#utils#uri_to_path(a:uri)
|
||||
let l:range = a:text_edit['range']
|
||||
let [l:line, l:col] = lsp#utils#position#lsp_to_vim(l:path, l:range['start'])
|
||||
|
||||
let l:index = l:line - 1
|
||||
if has_key(a:cache, l:path)
|
||||
let l:text = a:cache[l:path][l:index]
|
||||
else
|
||||
let l:contents = getbufline(l:path, 1, '$')
|
||||
if !empty(l:contents)
|
||||
let l:text = get(l:contents, l:index, '')
|
||||
else
|
||||
let l:contents = readfile(l:path)
|
||||
let a:cache[l:path] = l:contents
|
||||
let l:text = get(l:contents, l:index, '')
|
||||
endif
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'filename': l:path,
|
||||
\ 'lnum': l:line,
|
||||
\ 'col': l:col,
|
||||
\ 'text': l:text
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _apply
|
||||
"
|
||||
function! s:_apply(bufnr, text_edit, cursor_position) abort
|
||||
" create before/after line.
|
||||
let l:start_line = getline(a:text_edit['range']['start']['line'] + 1)
|
||||
let l:end_line = getline(a:text_edit['range']['end']['line'] + 1)
|
||||
let l:before_line = strcharpart(l:start_line, 0, a:text_edit['range']['start']['character'])
|
||||
let l:after_line = strcharpart(l:end_line, a:text_edit['range']['end']['character'], strchars(l:end_line) - a:text_edit['range']['end']['character'])
|
||||
|
||||
" create new lines.
|
||||
let l:new_lines = lsp#utils#_split_by_eol(a:text_edit['newText'])
|
||||
let l:new_lines[0] = l:before_line . l:new_lines[0]
|
||||
let l:new_lines[-1] = l:new_lines[-1] . l:after_line
|
||||
|
||||
" save length.
|
||||
let l:new_lines_len = len(l:new_lines)
|
||||
let l:range_len = (a:text_edit['range']['end']['line'] - a:text_edit['range']['start']['line']) + 1
|
||||
|
||||
" fixendofline
|
||||
let l:buffer_length = len(getbufline(a:bufnr, '^', '$'))
|
||||
let l:should_fixendofline = lsp#utils#buffer#_get_fixendofline(a:bufnr)
|
||||
let l:should_fixendofline = l:should_fixendofline && l:new_lines[-1] ==# ''
|
||||
let l:should_fixendofline = l:should_fixendofline && l:buffer_length <= a:text_edit['range']['end']['line']
|
||||
let l:should_fixendofline = l:should_fixendofline && a:text_edit['range']['end']['character'] == 0
|
||||
if l:should_fixendofline
|
||||
call remove(l:new_lines, -1)
|
||||
endif
|
||||
|
||||
" fix cursor pos
|
||||
if a:text_edit['range']['end']['line'] < a:cursor_position['line']
|
||||
" fix cursor line
|
||||
let a:cursor_position['line'] += l:new_lines_len - l:range_len
|
||||
elseif a:text_edit['range']['end']['line'] == a:cursor_position['line'] && a:text_edit['range']['end']['character'] <= a:cursor_position['character']
|
||||
" fix cursor line and col
|
||||
let a:cursor_position['line'] += l:new_lines_len - l:range_len
|
||||
let l:end_character = strchars(l:new_lines[-1]) - strchars(l:after_line)
|
||||
let l:end_offset = a:cursor_position['character'] - a:text_edit['range']['end']['character']
|
||||
let a:cursor_position['character'] = l:end_character + l:end_offset
|
||||
endif
|
||||
|
||||
" append or delete lines.
|
||||
if l:new_lines_len > l:range_len
|
||||
call append(a:text_edit['range']['start']['line'], repeat([''], l:new_lines_len - l:range_len))
|
||||
elseif l:new_lines_len < l:range_len
|
||||
let l:offset = l:range_len - l:new_lines_len
|
||||
call s:delete(a:bufnr, a:text_edit['range']['start']['line'] + 1, a:text_edit['range']['start']['line'] + l:offset)
|
||||
endif
|
||||
|
||||
" set lines.
|
||||
call setline(a:text_edit['range']['start']['line'] + 1, l:new_lines)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _normalize
|
||||
"
|
||||
function! s:_normalize(text_edits) abort
|
||||
let l:text_edits = type(a:text_edits) == type([]) ? a:text_edits : [a:text_edits]
|
||||
let l:text_edits = filter(copy(l:text_edits), { _, text_edit -> type(text_edit) == type({}) })
|
||||
let l:text_edits = s:_range(l:text_edits)
|
||||
let l:text_edits = sort(copy(l:text_edits), function('s:_compare', [], {}))
|
||||
let l:text_edits = s:_check(l:text_edits)
|
||||
return reverse(l:text_edits)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _range
|
||||
"
|
||||
function! s:_range(text_edits) abort
|
||||
for l:text_edit in a:text_edits
|
||||
if l:text_edit.range.start.line > l:text_edit.range.end.line || (
|
||||
\ l:text_edit.range.start.line == l:text_edit.range.end.line &&
|
||||
\ l:text_edit.range.start.character > l:text_edit.range.end.character
|
||||
\ )
|
||||
let l:text_edit.range = { 'start': l:text_edit.range.end, 'end': l:text_edit.range.start }
|
||||
endif
|
||||
endfor
|
||||
return a:text_edits
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _check
|
||||
"
|
||||
" LSP Spec says `multiple text edits can not overlap those ranges`.
|
||||
" This function check it. But does not throw error.
|
||||
"
|
||||
function! s:_check(text_edits) abort
|
||||
if len(a:text_edits) > 1
|
||||
let l:range = a:text_edits[0].range
|
||||
for l:text_edit in a:text_edits[1 : -1]
|
||||
if l:range.end.line > l:text_edit.range.start.line || (
|
||||
\ l:range.end.line == l:text_edit.range.start.line &&
|
||||
\ l:range.end.character > l:text_edit.range.start.character
|
||||
\ )
|
||||
call lsp#log('text_edit: range overlapped.')
|
||||
endif
|
||||
let l:range = l:text_edit.range
|
||||
endfor
|
||||
endif
|
||||
return a:text_edits
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _compare
|
||||
"
|
||||
function! s:_compare(text_edit1, text_edit2) abort
|
||||
let l:diff = a:text_edit1.range.start.line - a:text_edit2.range.start.line
|
||||
if l:diff == 0
|
||||
return a:text_edit1.range.start.character - a:text_edit2.range.start.character
|
||||
endif
|
||||
return l:diff
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _switch
|
||||
"
|
||||
function! s:_switch(path) abort
|
||||
if bufnr(a:path) >= 0
|
||||
execute printf('keepalt keepjumps %sbuffer!', bufnr(a:path))
|
||||
else
|
||||
execute printf('keepalt keepjumps edit! %s', fnameescape(a:path))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" delete
|
||||
"
|
||||
function! s:delete(bufnr, start, end) abort
|
||||
if exists('*deletebufline')
|
||||
call deletebufline(a:bufnr, a:start, a:end)
|
||||
else
|
||||
let l:foldenable = &foldenable
|
||||
setlocal nofoldenable
|
||||
execute printf('%s,%sdelete _', a:start, a:end)
|
||||
let &foldenable = l:foldenable
|
||||
endif
|
||||
endfunction
|
||||
|
||||
295
dot_vim/plugged/vim-lsp/autoload/lsp/utils/tree.vim
Normal file
295
dot_vim/plugged/vim-lsp/autoload/lsp/utils/tree.vim
Normal file
@@ -0,0 +1,295 @@
|
||||
" This file is part of an installation of vim-yggdrasil, a vim/neovim tree viewer library.
|
||||
" The source code of vim-yggdrasil is available at https://github.com/m-pilia/vim-yggdrasil
|
||||
"
|
||||
" vim-yggdrasil is free software, distributed under the MIT license.
|
||||
" The full license is available at https://github.com/m-pilia/vim-yggdrasil/blob/master/LICENSE
|
||||
"
|
||||
" Yggdrasil version (git SHA-1): 043d0ab53dcdd0d91b7c7cd205791d64d4ed9624
|
||||
"
|
||||
" This installation was generated on 2020-03-15T14:47:27-0700 with the following vim command:
|
||||
" :YggdrasilPlant -plugin_dir=./ -namespace=lsp/utils
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
" Callback to retrieve the tree item representation of an object.
|
||||
function! s:node_get_tree_item_cb(node, object, status, tree_item) abort
|
||||
if a:status ==? 'success'
|
||||
let l:new_node = s:node_new(a:node.tree, a:object, a:tree_item, a:node)
|
||||
call add(a:node.children, l:new_node)
|
||||
call s:tree_render(l:new_node.tree)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Callback to retrieve the children objects of a node.
|
||||
function! s:node_get_children_cb(node, status, childObjectList) abort
|
||||
for l:childObject in a:childObjectList
|
||||
let l:Callback = function('s:node_get_tree_item_cb', [a:node, l:childObject])
|
||||
call a:node.tree.provider.getTreeItem(l:Callback, l:childObject)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
" Set the node to be collapsed or expanded.
|
||||
"
|
||||
" When {collapsed} evaluates to 0 the node is expanded, when it is 1 the node is
|
||||
" collapsed, when it is equal to -1 the node is toggled (it is expanded if it
|
||||
" was collapsed, and vice versa).
|
||||
function! s:node_set_collapsed(collapsed) dict abort
|
||||
let l:self.collapsed = a:collapsed < 0 ? !l:self.collapsed : !!a:collapsed
|
||||
endfunction
|
||||
|
||||
" Given a funcref {Condition}, return a list of all nodes in the subtree of
|
||||
" {node} for which {Condition} evaluates to v:true.
|
||||
function! s:search_subtree(node, Condition) abort
|
||||
if a:Condition(a:node)
|
||||
return [a:node]
|
||||
endif
|
||||
if len(a:node.children) < 1
|
||||
return []
|
||||
endif
|
||||
let l:result = []
|
||||
for l:child in a:node.children
|
||||
let l:result = l:result + s:search_subtree(l:child, a:Condition)
|
||||
endfor
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
" Execute the action associated to a node
|
||||
function! s:node_exec() dict abort
|
||||
if has_key(l:self.tree_item, 'command')
|
||||
call l:self.tree_item.command()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Return the depth level of the node in the tree. The level is defined
|
||||
" recursively: the root has depth 0, and each node has depth equal to the depth
|
||||
" of its parent increased by 1.
|
||||
function! s:node_level() dict abort
|
||||
if l:self.parent == {}
|
||||
return 0
|
||||
endif
|
||||
return 1 + l:self.parent.level()
|
||||
endf
|
||||
|
||||
" Return the string representation of the node. The {level} argument represents
|
||||
" the depth level of the node in the tree and it is passed for convenience, to
|
||||
" simplify the implementation and to avoid re-computing the depth.
|
||||
function! s:node_render(level) dict abort
|
||||
let l:indent = repeat(' ', 2 * a:level)
|
||||
let l:mark = '• '
|
||||
|
||||
if len(l:self.children) > 0 || l:self.lazy_open != v:false
|
||||
let l:mark = l:self.collapsed ? '▸ ' : '▾ '
|
||||
endif
|
||||
|
||||
let l:label = split(l:self.tree_item.label, "\n")
|
||||
call extend(l:self.tree.index, map(range(len(l:label)), 'l:self'))
|
||||
|
||||
let l:repr = l:indent . l:mark . l:label[0]
|
||||
\ . join(map(l:label[1:], {_, l -> "\n" . l:indent . ' ' . l}))
|
||||
|
||||
let l:lines = [l:repr]
|
||||
if !l:self.collapsed
|
||||
if l:self.lazy_open
|
||||
let l:self.lazy_open = v:false
|
||||
let l:Callback = function('s:node_get_children_cb', [l:self])
|
||||
call l:self.tree.provider.getChildren(l:Callback, l:self.object)
|
||||
endif
|
||||
for l:child in l:self.children
|
||||
call add(l:lines, l:child.render(a:level + 1))
|
||||
endfor
|
||||
endif
|
||||
|
||||
return join(l:lines, "\n")
|
||||
endfunction
|
||||
|
||||
" Insert a new node in the tree, internally represented by a unique progressive
|
||||
" integer identifier {id}. The node represents a certain {object} (children of
|
||||
" {parent}) belonging to a given {tree}, having an associated action to be
|
||||
" triggered on execution defined by the function object {exec}. If {collapsed}
|
||||
" is true the node will be rendered as collapsed in the view. If {lazy_open} is
|
||||
" true, the children of the node will be fetched when the node is expanded by
|
||||
" the user.
|
||||
function! s:node_new(tree, object, tree_item, parent) abort
|
||||
let a:tree.maxid += 1
|
||||
return {
|
||||
\ 'id': a:tree.maxid,
|
||||
\ 'tree': a:tree,
|
||||
\ 'object': a:object,
|
||||
\ 'tree_item': a:tree_item,
|
||||
\ 'parent': a:parent,
|
||||
\ 'collapsed': a:tree_item.collapsibleState ==? 'collapsed',
|
||||
\ 'lazy_open': a:tree_item.collapsibleState !=? 'none',
|
||||
\ 'children': [],
|
||||
\ 'level': function('s:node_level'),
|
||||
\ 'exec': function('s:node_exec'),
|
||||
\ 'set_collapsed': function('s:node_set_collapsed'),
|
||||
\ 'render': function('s:node_render'),
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
" Callback that sets the root node of a given {tree}, creating a new node
|
||||
" with a {tree_item} representation for the given {object}. If {status} is
|
||||
" equal to 'success', the root node is set and the tree view is updated
|
||||
" accordingly, otherwise nothing happens.
|
||||
function! s:tree_set_root_cb(tree, object, status, tree_item) abort
|
||||
if a:status ==? 'success'
|
||||
let a:tree.maxid = -1
|
||||
let a:tree.root = s:node_new(a:tree, a:object, a:tree_item, {})
|
||||
call s:tree_render(a:tree)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Return the node currently under the cursor from the given {tree}.
|
||||
function! s:get_node_under_cursor(tree) abort
|
||||
let l:index = min([line('.'), len(a:tree.index) - 1])
|
||||
return a:tree.index[l:index]
|
||||
endfunction
|
||||
|
||||
" Expand or collapse the node under cursor, and render the tree.
|
||||
" Please refer to *s:node_set_collapsed()* for details about the
|
||||
" arguments and behaviour.
|
||||
function! s:tree_set_collapsed_under_cursor(collapsed) dict abort
|
||||
let l:node = s:get_node_under_cursor(l:self)
|
||||
call l:node.set_collapsed(a:collapsed)
|
||||
call s:tree_render(l:self)
|
||||
endfunction
|
||||
|
||||
" Run the action associated to the node currently under the cursor.
|
||||
function! s:tree_exec_node_under_cursor() dict abort
|
||||
call s:get_node_under_cursor(l:self).exec()
|
||||
endfunction
|
||||
|
||||
" Render the {tree}. This will replace the content of the buffer with the
|
||||
" tree view. Clear the index, setting it to a list containing a guard
|
||||
" value for index 0 (line numbers are one-based).
|
||||
function! s:tree_render(tree) abort
|
||||
if &filetype !=# 'lsp-tree'
|
||||
return
|
||||
endif
|
||||
|
||||
let l:cursor = getpos('.')
|
||||
let a:tree.index = [-1]
|
||||
let l:text = a:tree.root.render(0)
|
||||
|
||||
setlocal modifiable
|
||||
silent 1,$delete _
|
||||
silent 0put=l:text
|
||||
$d
|
||||
setlocal nomodifiable
|
||||
|
||||
call setpos('.', l:cursor)
|
||||
endfunction
|
||||
|
||||
" If {status} equals 'success', update all nodes of {tree} representing
|
||||
" an {obect} with given {tree_item} representation.
|
||||
function! s:node_update(tree, object, status, tree_item) abort
|
||||
if a:status !=? 'success'
|
||||
return
|
||||
endif
|
||||
for l:node in s:search_subtree(a:tree.root, {n -> n.object == a:object})
|
||||
let l:node.tree_item = a:tree_item
|
||||
let l:node.children = []
|
||||
let l:node.lazy_open = a:tree_item.collapsibleState !=? 'none'
|
||||
endfor
|
||||
call s:tree_render(a:tree)
|
||||
endfunction
|
||||
|
||||
" Update the view if nodes have changed. If called with no arguments,
|
||||
" update the whole tree. If called with an {object} as argument, update
|
||||
" all the subtrees of nodes corresponding to {object}.
|
||||
function! s:tree_update(...) dict abort
|
||||
if a:0 < 1
|
||||
call l:self.provider.getChildren({status, obj ->
|
||||
\ l:self.provider.getTreeItem(function('s:tree_set_root_cb', [l:self, obj[0]]), obj[0])})
|
||||
else
|
||||
call l:self.provider.getTreeItem(function('s:node_update', [l:self, a:1]), a:1)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Destroy the tree view. Wipe out the buffer containing it.
|
||||
function! s:tree_wipe() dict abort
|
||||
execute 'bwipeout' . l:self.bufnr
|
||||
endfunction
|
||||
|
||||
" Apply syntax to an lsp-tree buffer
|
||||
function! s:filetype_syntax() abort
|
||||
syntax clear
|
||||
syntax match LspTreeMarkLeaf "•" contained
|
||||
syntax match LspTreeMarkCollapsed "▸" contained
|
||||
syntax match LspTreeMarkExpanded "▾" contained
|
||||
syntax match LspTreeNode "\v^(\s|[▸▾•])*.*"
|
||||
\ contains=LspTreeMarkLeaf,LspTreeMarkCollapsed,LspTreeMarkExpanded
|
||||
|
||||
highlight def link LspTreeMarkLeaf Type
|
||||
highlight def link LspTreeMarkExpanded Type
|
||||
highlight def link LspTreeMarkCollapsed Macro
|
||||
endfunction
|
||||
|
||||
" Apply local settings to an lsp-tree buffer
|
||||
function! s:filetype_settings() abort
|
||||
setlocal bufhidden=wipe
|
||||
setlocal buftype=nofile
|
||||
setlocal foldcolumn=0
|
||||
setlocal foldmethod=manual
|
||||
setlocal nobuflisted
|
||||
setlocal nofoldenable
|
||||
setlocal nohlsearch
|
||||
setlocal nolist
|
||||
setlocal nomodifiable
|
||||
setlocal nonumber
|
||||
setlocal nospell
|
||||
setlocal noswapfile
|
||||
setlocal nowrap
|
||||
|
||||
nnoremap <silent> <buffer> <Plug>(lsp-tree-toggle-node)
|
||||
\ :call b:lsp_tree.set_collapsed_under_cursor(-1)<cr>
|
||||
|
||||
nnoremap <silent> <buffer> <Plug>(lsp-tree-open-node)
|
||||
\ :call b:lsp_tree.set_collapsed_under_cursor(v:false)<cr>
|
||||
|
||||
nnoremap <silent> <buffer> <Plug>(lsp-tree-close-node)
|
||||
\ :call b:lsp_tree.set_collapsed_under_cursor(v:true)<cr>
|
||||
|
||||
nnoremap <silent> <buffer> <Plug>(lsp-tree-execute-node)
|
||||
\ :call b:lsp_tree.exec_node_under_cursor()<cr>
|
||||
|
||||
nnoremap <silent> <buffer> <Plug>(lsp-tree-wipe-tree)
|
||||
\ :call b:lsp_tree.wipe()<cr>
|
||||
|
||||
if !exists('g:lsp_tree_no_default_maps')
|
||||
nmap <silent> <buffer> o <Plug>(lsp-tree-toggle-node)
|
||||
nmap <silent> <buffer> <cr> <Plug>(lsp-tree-execute-node)
|
||||
nmap <silent> <buffer> q <Plug>(lsp-tree-wipe-tree)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Turns the current buffer into an lsp-tree tree view. Tree data is retrieved
|
||||
" from the given {provider}, and the state of the tree is stored in a
|
||||
" buffer-local variable called b:lsp_tree.
|
||||
"
|
||||
" The {bufnr} stores the buffer number of the view, {maxid} is the highest
|
||||
" known internal identifier of the nodes. The {index} is a list that
|
||||
" maps line numbers to nodes.
|
||||
function! lsp#utils#tree#new(provider) abort
|
||||
let b:lsp_tree = {
|
||||
\ 'bufnr': bufnr('%'),
|
||||
\ 'maxid': -1,
|
||||
\ 'root': {},
|
||||
\ 'index': [],
|
||||
\ 'provider': a:provider,
|
||||
\ 'set_collapsed_under_cursor': function('s:tree_set_collapsed_under_cursor'),
|
||||
\ 'exec_node_under_cursor': function('s:tree_exec_node_under_cursor'),
|
||||
\ 'update': function('s:tree_update'),
|
||||
\ 'wipe': function('s:tree_wipe'),
|
||||
\ }
|
||||
|
||||
augroup vim_lsp_tree
|
||||
autocmd!
|
||||
autocmd FileType lsp-tree call s:filetype_syntax() | call s:filetype_settings()
|
||||
autocmd BufEnter <buffer> call s:tree_render(b:lsp_tree)
|
||||
augroup END
|
||||
|
||||
setlocal filetype=lsp-tree
|
||||
|
||||
call b:lsp_tree.update()
|
||||
endfunction
|
||||
@@ -0,0 +1,14 @@
|
||||
function! lsp#utils#workspace_config#get_value(server_name, item) abort
|
||||
try
|
||||
let l:server_info = lsp#get_server_info(a:server_name)
|
||||
let l:config = l:server_info['workspace_config']
|
||||
|
||||
for l:section in split(a:item['section'], '\.')
|
||||
let l:config = l:config[l:section]
|
||||
endfor
|
||||
|
||||
return l:config
|
||||
catch
|
||||
return v:null
|
||||
endtry
|
||||
endfunction
|
||||
@@ -0,0 +1,27 @@
|
||||
" Applies WorkspaceEdit changes.
|
||||
function! lsp#utils#workspace_edit#apply_workspace_edit(workspace_edit) abort
|
||||
let l:loclist_items = []
|
||||
|
||||
if has_key(a:workspace_edit, 'documentChanges')
|
||||
for l:text_document_edit in a:workspace_edit['documentChanges']
|
||||
let l:loclist_items += s:_apply(l:text_document_edit['textDocument']['uri'], l:text_document_edit['edits'])
|
||||
endfor
|
||||
elseif has_key(a:workspace_edit, 'changes')
|
||||
for [l:uri, l:text_edits] in items(a:workspace_edit['changes'])
|
||||
let l:loclist_items += s:_apply(l:uri, l:text_edits)
|
||||
endfor
|
||||
endif
|
||||
|
||||
if g:lsp_show_workspace_edits
|
||||
call setloclist(0, l:loclist_items, 'r')
|
||||
execute 'lopen'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _apply
|
||||
"
|
||||
function! s:_apply(uri, text_edits) abort
|
||||
call lsp#utils#text_edit#apply_text_edits(a:uri, a:text_edits)
|
||||
return lsp#utils#text_edit#_lsp_to_vim_list(a:uri, a:text_edits)
|
||||
endfunction
|
||||
9
dot_vim/plugged/vim-lsp/autoload/vital/_lsp.vim
Normal file
9
dot_vim/plugged/vim-lsp/autoload/vital/_lsp.vim
Normal file
@@ -0,0 +1,9 @@
|
||||
let s:_plugin_name = expand('<sfile>:t:r')
|
||||
|
||||
function! vital#{s:_plugin_name}#new() abort
|
||||
return vital#{s:_plugin_name[1:]}#new()
|
||||
endfunction
|
||||
|
||||
function! vital#{s:_plugin_name}#function(funcname) abort
|
||||
silent! return function(a:funcname)
|
||||
endfunction
|
||||
@@ -0,0 +1,66 @@
|
||||
" ___vital___
|
||||
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
|
||||
" Do not modify the code nor insert new lines before '" ___vital___'
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_lsp#VS#LSP#MarkupContent#import() abort', printf("return map({'_vital_depends': '', 'normalize': '', '_vital_loaded': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
" ___vital___
|
||||
"
|
||||
" _vital_loaded
|
||||
"
|
||||
function! s:_vital_loaded(V) abort
|
||||
let s:Text = a:V.import('VS.LSP.Text')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _vital_depends
|
||||
"
|
||||
function! s:_vital_depends() abort
|
||||
return ['VS.LSP.Text']
|
||||
endfunction
|
||||
|
||||
"
|
||||
" normalize
|
||||
"
|
||||
function! s:normalize(markup_content, ...) abort
|
||||
let l:option = get(a:000, 0, {})
|
||||
let l:option.compact = get(l:option, 'compact', v:true)
|
||||
|
||||
let l:normalized = ''
|
||||
if type(a:markup_content) == type('')
|
||||
let l:normalized = a:markup_content
|
||||
elseif type(a:markup_content) == type([])
|
||||
let l:normalized = join(a:markup_content, "\n")
|
||||
elseif type(a:markup_content) == type({})
|
||||
let l:normalized = a:markup_content.value
|
||||
if has_key(a:markup_content, 'language')
|
||||
let l:normalized = join([
|
||||
\ '```' . a:markup_content.language,
|
||||
\ l:normalized,
|
||||
\ '```'
|
||||
\ ], "\n")
|
||||
endif
|
||||
endif
|
||||
let l:normalized = s:Text.normalize_eol(l:normalized)
|
||||
let l:normalized = s:_format(l:normalized, l:option.compact)
|
||||
return l:normalized
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _format
|
||||
"
|
||||
function! s:_format(string, compact) abort
|
||||
let l:string = a:string
|
||||
if a:compact
|
||||
let l:string = substitute(l:string, "\\%(\\s\\|\n\\)*```\\s*\\(\\w\\+\\)\\%(\\s\\|\n\\)\\+", "\n\n```\\1 ", 'g')
|
||||
let l:string = substitute(l:string, "\\%(\\s\\|\n\\)\\+```\\%(\\s*\\%(\\%$\\|\n\\)\\)\\+", " ```\n\n", 'g')
|
||||
else
|
||||
let l:string = substitute(l:string, "```\n\\zs\\%(\\s\\|\n\\)\\+", "", 'g')
|
||||
endif
|
||||
let l:string = substitute(l:string, "\\%^\\%(\\s\\|\n\\)*", '', 'g')
|
||||
let l:string = substitute(l:string, "\\%(\\s\\|\n\\)*\\%$", '', 'g')
|
||||
return l:string
|
||||
endfunction
|
||||
|
||||
23
dot_vim/plugged/vim-lsp/autoload/vital/_lsp/VS/LSP/Text.vim
Normal file
23
dot_vim/plugged/vim-lsp/autoload/vital/_lsp/VS/LSP/Text.vim
Normal file
@@ -0,0 +1,23 @@
|
||||
" ___vital___
|
||||
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
|
||||
" Do not modify the code nor insert new lines before '" ___vital___'
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_lsp#VS#LSP#Text#import() abort', printf("return map({'normalize_eol': '', 'split_by_eol': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
" ___vital___
|
||||
"
|
||||
" normalize_eol
|
||||
"
|
||||
function! s:normalize_eol(text) abort
|
||||
return substitute(a:text, "\r\n\\|\r", "\n", 'g')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" split_by_eol
|
||||
"
|
||||
function! s:split_by_eol(text) abort
|
||||
return split(a:text, "\r\n\\|\r\\|\n", v:true)
|
||||
endfunction
|
||||
|
||||
140
dot_vim/plugged/vim-lsp/autoload/vital/_lsp/VS/Vim/Buffer.vim
Normal file
140
dot_vim/plugged/vim-lsp/autoload/vital/_lsp/VS/Vim/Buffer.vim
Normal file
@@ -0,0 +1,140 @@
|
||||
" ___vital___
|
||||
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
|
||||
" Do not modify the code nor insert new lines before '" ___vital___'
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_lsp#VS#Vim#Buffer#import() abort', printf("return map({'add': '', 'do': '', 'create': '', 'get_line_count': '', 'pseudo': '', 'ensure': '', 'load': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
" ___vital___
|
||||
let s:Do = { -> {} }
|
||||
|
||||
let g:___VS_Vim_Buffer_id = get(g:, '___VS_Vim_Buffer_id', 0)
|
||||
|
||||
"
|
||||
" get_line_count
|
||||
"
|
||||
if exists('*nvim_buf_line_count')
|
||||
function! s:get_line_count(bufnr) abort
|
||||
return nvim_buf_line_count(a:bufnr)
|
||||
endfunction
|
||||
elseif has('patch-8.2.0019')
|
||||
function! s:get_line_count(bufnr) abort
|
||||
return getbufinfo(a:bufnr)[0].linecount
|
||||
endfunction
|
||||
else
|
||||
function! s:get_line_count(bufnr) abort
|
||||
if bufnr('%') == bufnr(a:bufnr)
|
||||
return line('$')
|
||||
endif
|
||||
return len(getbufline(a:bufnr, '^', '$'))
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" create
|
||||
"
|
||||
function! s:create(...) abort
|
||||
let g:___VS_Vim_Buffer_id += 1
|
||||
let l:bufname = printf('VS.Vim.Buffer: %s: %s',
|
||||
\ g:___VS_Vim_Buffer_id,
|
||||
\ get(a:000, 0, 'VS.Vim.Buffer.Default')
|
||||
\ )
|
||||
return s:load(l:bufname)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" ensure
|
||||
"
|
||||
function! s:ensure(expr) abort
|
||||
if !bufexists(a:expr)
|
||||
if type(a:expr) == type(0)
|
||||
throw printf('VS.Vim.Buffer: `%s` is not valid expr.', a:expr)
|
||||
endif
|
||||
call s:add(a:expr)
|
||||
endif
|
||||
return bufnr(a:expr)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" add
|
||||
"
|
||||
if exists('*bufadd')
|
||||
function! s:add(name) abort
|
||||
let l:bufnr = bufadd(a:name)
|
||||
call setbufvar(l:bufnr, '&buflisted', 1)
|
||||
endfunction
|
||||
else
|
||||
function! s:add(name) abort
|
||||
badd `=a:name`
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" load
|
||||
"
|
||||
if exists('*bufload')
|
||||
function! s:load(expr) abort
|
||||
let l:bufnr = s:ensure(a:expr)
|
||||
if !bufloaded(l:bufnr)
|
||||
call bufload(l:bufnr)
|
||||
endif
|
||||
return l:bufnr
|
||||
endfunction
|
||||
else
|
||||
function! s:load(expr) abort
|
||||
let l:curr_bufnr = bufnr('%')
|
||||
try
|
||||
let l:bufnr = s:ensure(a:expr)
|
||||
execute printf('keepalt keepjumps silent %sbuffer', l:bufnr)
|
||||
catch /.*/
|
||||
echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
|
||||
finally
|
||||
execute printf('noautocmd keepalt keepjumps silent %sbuffer', l:curr_bufnr)
|
||||
endtry
|
||||
return l:bufnr
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" do
|
||||
"
|
||||
function! s:do(bufnr, func) abort
|
||||
let l:curr_bufnr = bufnr('%')
|
||||
if l:curr_bufnr == a:bufnr
|
||||
call a:func()
|
||||
return
|
||||
endif
|
||||
|
||||
try
|
||||
execute printf('noautocmd keepalt keepjumps silent %sbuffer', a:bufnr)
|
||||
call a:func()
|
||||
catch /.*/
|
||||
echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
|
||||
finally
|
||||
execute printf('noautocmd keepalt keepjumps silent %sbuffer', l:curr_bufnr)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
"
|
||||
" pseudo
|
||||
"
|
||||
function! s:pseudo(filepath) abort
|
||||
if !filereadable(a:filepath)
|
||||
throw printf('VS.Vim.Buffer: `%s` is not valid filepath.', a:filepath)
|
||||
endif
|
||||
|
||||
" create pseudo buffer
|
||||
let l:bufname = printf('VSVimBufferPseudo://%s', a:filepath)
|
||||
if bufexists(l:bufname)
|
||||
return s:ensure(l:bufname)
|
||||
endif
|
||||
|
||||
let l:bufnr = s:ensure(l:bufname)
|
||||
let l:group = printf('VS_Vim_Buffer_pseudo:%s', l:bufnr)
|
||||
execute printf('augroup %s', l:group)
|
||||
execute printf('autocmd BufReadCmd <buffer=%s> call setline(1, readfile(bufname("%")[20 : -1])) | try | filetype detect | catch /.*/ | endtry | augroup %s | autocmd! | augroup END', l:bufnr, l:group)
|
||||
augroup END
|
||||
return l:bufnr
|
||||
endfunction
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
" ___vital___
|
||||
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
|
||||
" Do not modify the code nor insert new lines before '" ___vital___'
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_lsp#VS#Vim#Syntax#Markdown#import() abort', printf("return map({'apply': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
" ___vital___
|
||||
"
|
||||
" apply
|
||||
"
|
||||
" TODO: Refactor
|
||||
"
|
||||
function! s:apply(...) abort
|
||||
let l:args = get(a:000, 0, {})
|
||||
let l:text = has_key(l:args, 'text') ? l:args.text : getbufline('%', 1, '$')
|
||||
let l:text = type(l:text) == v:t_list ? join(l:text, "\n") : l:text
|
||||
|
||||
call s:_execute('syntax sync clear')
|
||||
if !exists('b:___VS_Vim_Syntax_Markdown')
|
||||
" Avoid automatic highlighting by built-in runtime syntax.
|
||||
if !has_key(g:, 'markdown_fenced_languages')
|
||||
call s:_execute('runtime! syntax/markdown.vim')
|
||||
else
|
||||
let l:markdown_fenced_languages = g:markdown_fenced_languages
|
||||
unlet g:markdown_fenced_languages
|
||||
call s:_execute('runtime! syntax/markdown.vim')
|
||||
let g:markdown_fenced_languages = l:markdown_fenced_languages
|
||||
endif
|
||||
|
||||
" Remove markdownCodeBlock because we support it manually.
|
||||
call s:_clear('markdownCodeBlock') " runtime
|
||||
call s:_clear('mkdCode') " plasticboy/vim-markdown
|
||||
|
||||
" Modify markdownCode (`codes...`)
|
||||
call s:_clear('markdownCode')
|
||||
syntax region markdownCode matchgroup=Conceal start=/\%(``\)\@!`/ matchgroup=Conceal end=/\%(``\)\@!`/ containedin=TOP keepend concealends
|
||||
|
||||
" Modify markdownEscape (_bold\_text_) @see nvim's syntax/lsp_markdown.vim
|
||||
call s:_clear('markdownEscape')
|
||||
syntax region markdownEscape matchgroup=markdownEscape start=/\\\ze[\\\x60*{}\[\]()#+\-,.!_>~|"$%&'\/:;<=?@^ ]/ end=/./ containedin=ALL keepend oneline concealends
|
||||
|
||||
" Add syntax for basic html entities.
|
||||
syntax match vital_vs_vim_syntax_markdown_entities_lt /</ containedin=ALL conceal cchar=<
|
||||
syntax match vital_vs_vim_syntax_markdown_entities_gt />/ containedin=ALL conceal cchar=>
|
||||
syntax match vital_vs_vim_syntax_markdown_entities_amp /&/ containedin=ALL conceal cchar=&
|
||||
syntax match vital_vs_vim_syntax_markdown_entities_quot /"/ containedin=ALL conceal cchar="
|
||||
syntax match vital_vs_vim_syntax_markdown_entities_nbsp / / containedin=ALL conceal cchar=
|
||||
|
||||
let b:___VS_Vim_Syntax_Markdown = {}
|
||||
let b:___VS_Vim_Syntax_Markdown.marks = {}
|
||||
let b:___VS_Vim_Syntax_Markdown.filetypes = {}
|
||||
endif
|
||||
|
||||
for [l:mark, l:filetype] in items(s:_get_filetype_map(l:text))
|
||||
try
|
||||
let l:mark_group = substitute(toupper(l:mark), '\.', '_', 'g')
|
||||
if has_key(b:___VS_Vim_Syntax_Markdown.marks, l:mark_group)
|
||||
continue
|
||||
endif
|
||||
let b:___VS_Vim_Syntax_Markdown.marks[l:mark_group] = v:true
|
||||
|
||||
let l:filetype_group = substitute(toupper(l:filetype), '\.', '_', 'g')
|
||||
if !has_key(b:___VS_Vim_Syntax_Markdown.filetypes, l:filetype_group)
|
||||
call s:_execute('syntax include @%s syntax/%s.vim', l:filetype_group, l:filetype)
|
||||
let b:___VS_Vim_Syntax_Markdown.filetypes[l:filetype_group] = v:true
|
||||
endif
|
||||
|
||||
call s:_execute('syntax region %s matchgroup=Conceal start=/%s/ matchgroup=Conceal end=/%s/ contains=@%s containedin=TOP keepend concealends',
|
||||
\ l:mark_group,
|
||||
\ printf('```\s*%s\s*', l:mark),
|
||||
\ '```\s*\%(' . "\n" . '\|$\)',
|
||||
\ l:filetype_group
|
||||
\ )
|
||||
catch /.*/
|
||||
unsilent echomsg printf('Fail to apply "syntax/%s.vim". Add "let g:markdown_fenced_languages = ["%s=$FILETYPE"]" to enable syntax', l:filetype, l:filetype)
|
||||
endtry
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _clear
|
||||
"
|
||||
function! s:_clear(group) abort
|
||||
try
|
||||
execute printf('silent! syntax clear %s', a:group)
|
||||
catch /.*/
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _execute
|
||||
"
|
||||
function! s:_execute(command, ...) abort
|
||||
let b:current_syntax = ''
|
||||
unlet b:current_syntax
|
||||
|
||||
let g:main_syntax = ''
|
||||
unlet g:main_syntax
|
||||
|
||||
execute call('printf', [a:command] + a:000)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _get_filetype_map
|
||||
"
|
||||
function! s:_get_filetype_map(text) abort
|
||||
let l:filetype_map = {}
|
||||
for l:mark in s:_find_marks(a:text)
|
||||
let l:filetype_map[l:mark] = s:_get_filetype_from_mark(l:mark)
|
||||
endfor
|
||||
return l:filetype_map
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _find_marks
|
||||
"
|
||||
function! s:_find_marks(text) abort
|
||||
let l:marks = {}
|
||||
|
||||
" find from buffer contents.
|
||||
let l:text = a:text
|
||||
let l:pos = 0
|
||||
while 1
|
||||
let l:match = matchstrpos(l:text, '```\s*\zs\w\+', l:pos, 1)
|
||||
if empty(l:match[0])
|
||||
break
|
||||
endif
|
||||
let l:marks[l:match[0]] = v:true
|
||||
let l:pos = l:match[2]
|
||||
endwhile
|
||||
|
||||
return keys(l:marks)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _get_filetype_from_mark
|
||||
"
|
||||
function! s:_get_filetype_from_mark(mark) abort
|
||||
for l:config in get(g:, 'markdown_fenced_languages', [])
|
||||
if l:config !~# '='
|
||||
if l:config ==# a:mark
|
||||
return a:mark
|
||||
endif
|
||||
else
|
||||
let l:config = split(l:config, '=')
|
||||
if l:config[0] ==# a:mark
|
||||
return l:config[1]
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
return a:mark
|
||||
endfunction
|
||||
|
||||
157
dot_vim/plugged/vim-lsp/autoload/vital/_lsp/VS/Vim/Window.vim
Normal file
157
dot_vim/plugged/vim-lsp/autoload/vital/_lsp/VS/Vim/Window.vim
Normal file
@@ -0,0 +1,157 @@
|
||||
" ___vital___
|
||||
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
|
||||
" Do not modify the code nor insert new lines before '" ___vital___'
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_lsp#VS#Vim#Window#import() abort', printf("return map({'info': '', 'do': '', 'is_floating': '', 'find': '', 'scroll': '', 'screenpos': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
" ___vital___
|
||||
let s:Do = { -> {} }
|
||||
|
||||
"
|
||||
" do
|
||||
"
|
||||
function! s:do(winid, func) abort
|
||||
let l:curr_winid = win_getid()
|
||||
if l:curr_winid == a:winid
|
||||
call a:func()
|
||||
return
|
||||
endif
|
||||
|
||||
if !has('nvim') && exists('*win_execute')
|
||||
let s:Do = a:func
|
||||
try
|
||||
noautocmd keepalt keepjumps call win_execute(a:winid, 'call s:Do()')
|
||||
catch /.*/
|
||||
echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
|
||||
endtry
|
||||
unlet s:Do
|
||||
return
|
||||
endif
|
||||
|
||||
noautocmd keepalt keepjumps call win_gotoid(a:winid)
|
||||
try
|
||||
call a:func()
|
||||
catch /.*/
|
||||
echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
|
||||
endtry
|
||||
noautocmd keepalt keepjumps call win_gotoid(l:curr_winid)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" info
|
||||
"
|
||||
if has('nvim')
|
||||
function! s:info(winid) abort
|
||||
let l:info = getwininfo(a:winid)[0]
|
||||
return {
|
||||
\ 'width': l:info.width,
|
||||
\ 'height': l:info.height,
|
||||
\ 'topline': l:info.topline,
|
||||
\ }
|
||||
endfunction
|
||||
else
|
||||
function! s:info(winid) abort
|
||||
if s:is_floating(a:winid)
|
||||
let l:info = popup_getpos(a:winid)
|
||||
return {
|
||||
\ 'width': l:info.width,
|
||||
\ 'height': l:info.height,
|
||||
\ 'topline': l:info.firstline
|
||||
\ }
|
||||
endif
|
||||
|
||||
let l:ctx = {}
|
||||
let l:ctx.info = {}
|
||||
function! l:ctx.callback() abort
|
||||
let self.info.width = winwidth(0)
|
||||
let self.info.height = winheight(0)
|
||||
let self.info.topline = line('w0')
|
||||
endfunction
|
||||
call s:do(a:winid, { -> l:ctx.callback() })
|
||||
return l:ctx.info
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" find
|
||||
"
|
||||
function! s:find(callback) abort
|
||||
let l:winids = []
|
||||
let l:winids += map(range(1, tabpagewinnr(tabpagenr(), '$')), 'win_getid(v:val)')
|
||||
let l:winids += s:_get_visible_popup_winids()
|
||||
return filter(l:winids, 'a:callback(v:val)')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" is_floating
|
||||
"
|
||||
if has('nvim')
|
||||
function! s:is_floating(winid) abort
|
||||
let l:config = nvim_win_get_config(a:winid)
|
||||
return empty(l:config) || !empty(get(l:config, 'relative', ''))
|
||||
endfunction
|
||||
else
|
||||
function! s:is_floating(winid) abort
|
||||
return winheight(a:winid) != -1 && win_id2win(a:winid) == 0
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" scroll
|
||||
"
|
||||
function! s:scroll(winid, topline) abort
|
||||
let l:ctx = {}
|
||||
function! l:ctx.callback(winid, topline) abort
|
||||
let l:wininfo = s:info(a:winid)
|
||||
let l:topline = a:topline
|
||||
let l:topline = min([l:topline, line('$') - l:wininfo.height + 1])
|
||||
let l:topline = max([l:topline, 1])
|
||||
|
||||
if l:topline == l:wininfo.topline
|
||||
return
|
||||
endif
|
||||
|
||||
if !has('nvim') && s:is_floating(a:winid)
|
||||
call popup_setoptions(a:winid, {
|
||||
\ 'firstline': l:topline,
|
||||
\ })
|
||||
else
|
||||
let l:delta = l:topline - l:wininfo.topline
|
||||
let l:key = l:delta > 0 ? "\<C-e>" : "\<C-y>"
|
||||
execute printf('noautocmd silent normal! %s', repeat(l:key, abs(l:delta)))
|
||||
endif
|
||||
endfunction
|
||||
call s:do(a:winid, { -> l:ctx.callback(a:winid, a:topline) })
|
||||
endfunction
|
||||
|
||||
"
|
||||
" screenpos
|
||||
"
|
||||
" @param {[number, number]} pos - position on the current buffer.
|
||||
"
|
||||
function! s:screenpos(pos) abort
|
||||
let l:y = a:pos[0]
|
||||
let l:x = a:pos[1] + get(a:pos, 2, 0)
|
||||
|
||||
let l:view = winsaveview()
|
||||
let l:scroll_x = l:view.leftcol
|
||||
let l:scroll_y = l:view.topline
|
||||
|
||||
let l:winpos = win_screenpos(win_getid())
|
||||
let l:y = l:winpos[0] + l:y - l:scroll_y
|
||||
let l:x = l:winpos[1] + l:x - l:scroll_x
|
||||
return [l:y, l:x + (wincol() - virtcol('.')) - 1]
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _get_visible_popup_winids
|
||||
"
|
||||
function! s:_get_visible_popup_winids() abort
|
||||
if !exists('*popup_list')
|
||||
return []
|
||||
endif
|
||||
return filter(popup_list(), 'popup_getpos(v:val).visible')
|
||||
endfunction
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
" ___vital___
|
||||
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
|
||||
" Do not modify the code nor insert new lines before '" ___vital___'
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
execute join(['function! vital#_lsp#VS#Vim#Window#FloatingWindow#import() abort', printf("return map({'_vital_depends': '', 'is_available': '', 'new': '', '_vital_loaded': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
|
||||
delfunction s:_SID
|
||||
" ___vital___
|
||||
"
|
||||
" _vital_loaded
|
||||
"
|
||||
function! s:_vital_loaded(V) abort
|
||||
let s:Window = a:V.import('VS.Vim.Window')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _vital_depends
|
||||
"
|
||||
function! s:_vital_depends() abort
|
||||
return ['VS.Vim.Window']
|
||||
endfunction
|
||||
|
||||
"
|
||||
" managed floating windows.
|
||||
"
|
||||
let s:floating_windows = {}
|
||||
|
||||
"
|
||||
" is_available
|
||||
"
|
||||
function! s:is_available() abort
|
||||
if has('nvim')
|
||||
return v:true
|
||||
endif
|
||||
return exists('*popup_create') && exists('*popup_close') && exists('*popup_move') && exists('*popup_getpos')
|
||||
endfunction
|
||||
|
||||
"
|
||||
" new
|
||||
"
|
||||
function! s:new(...) abort
|
||||
call s:_init()
|
||||
|
||||
return s:FloatingWindow.new(get(a:000, 0, {}))
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _notify_opened
|
||||
"
|
||||
" @param {number} winid
|
||||
" @param {VS.Vim.Window.FloatingWindow} floating_window
|
||||
"
|
||||
function! s:_notify_opened(winid, floating_window) abort
|
||||
let s:floating_windows[a:winid] = a:floating_window
|
||||
call a:floating_window._on_opened()
|
||||
endfunction
|
||||
|
||||
"
|
||||
" _notify_closed
|
||||
"
|
||||
function! s:_notify_closed() abort
|
||||
for [l:winid, l:floating_window] in items(s:floating_windows)
|
||||
if winheight(l:winid) == -1
|
||||
call l:floating_window._on_closed()
|
||||
unlet s:floating_windows[l:winid]
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
let s:FloatingWindow = {}
|
||||
|
||||
"
|
||||
" new
|
||||
"
|
||||
" @param {function?} args.on_opened
|
||||
" @param {function?} args.on_closed
|
||||
"
|
||||
function! s:FloatingWindow.new(args) abort
|
||||
return extend(deepcopy(s:FloatingWindow), {
|
||||
\ '_winid': v:null,
|
||||
\ '_bufnr': v:null,
|
||||
\ '_vars': {},
|
||||
\ '_on_opened': get(a:args, 'on_opened', { -> {} }),
|
||||
\ '_on_closed': get(a:args, 'on_closed', { -> {} }),
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
"
|
||||
" get_size
|
||||
"
|
||||
" @param {number?} args.minwidth
|
||||
" @param {number?} args.maxwidth
|
||||
" @param {number?} args.minheight
|
||||
" @param {number?} args.maxheight
|
||||
" @param {boolean?} args.wrap
|
||||
"
|
||||
function! s:FloatingWindow.get_size(args) abort
|
||||
if self._bufnr is# v:null
|
||||
throw 'VS.Vim.Window.FloatingWindow: Failed to detect bufnr.'
|
||||
endif
|
||||
|
||||
let l:maxwidth = get(a:args, 'maxwidth', -1)
|
||||
let l:minwidth = get(a:args, 'minwidth', -1)
|
||||
let l:maxheight = get(a:args, 'maxheight', -1)
|
||||
let l:minheight = get(a:args, 'minheight', -1)
|
||||
let l:lines = getbufline(self._bufnr, '^', '$')
|
||||
|
||||
" width
|
||||
let l:width = 0
|
||||
for l:line in l:lines
|
||||
let l:width = max([l:width, strdisplaywidth(l:line)])
|
||||
endfor
|
||||
|
||||
let l:width = l:minwidth == -1 ? l:width : max([l:minwidth, l:width])
|
||||
let l:width = l:maxwidth == -1 ? l:width : min([l:maxwidth, l:width])
|
||||
|
||||
" height
|
||||
if get(a:args, 'wrap', get(self._vars, '&wrap', 0))
|
||||
let l:height = 0
|
||||
for l:line in l:lines
|
||||
let l:height += max([1, float2nr(ceil(strdisplaywidth(l:line) / str2float('' . l:width)))])
|
||||
endfor
|
||||
else
|
||||
let l:height = len(l:lines)
|
||||
endif
|
||||
let l:height = l:minheight == -1 ? l:height : max([l:minheight, l:height])
|
||||
let l:height = l:maxheight == -1 ? l:height : min([l:maxheight, l:height])
|
||||
|
||||
return {
|
||||
\ 'width': max([1, l:width]),
|
||||
\ 'height': max([1, l:height]),
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
"
|
||||
" set_bufnr
|
||||
"
|
||||
" @param {number} bufnr
|
||||
"
|
||||
function! s:FloatingWindow.set_bufnr(bufnr) abort
|
||||
let self._bufnr = a:bufnr
|
||||
endfunction
|
||||
|
||||
"
|
||||
" get_bufnr
|
||||
"
|
||||
function! s:FloatingWindow.get_bufnr() abort
|
||||
return self._bufnr
|
||||
endfunction
|
||||
|
||||
"
|
||||
" get_winid
|
||||
"
|
||||
function! s:FloatingWindow.get_winid() abort
|
||||
if self.is_visible()
|
||||
return self._winid
|
||||
endif
|
||||
return v:null
|
||||
endfunction
|
||||
|
||||
"
|
||||
" info
|
||||
"
|
||||
function! s:FloatingWindow.info() abort
|
||||
if self.is_visible()
|
||||
return s:_info(self._winid)
|
||||
endif
|
||||
return v:null
|
||||
endfunction
|
||||
|
||||
"
|
||||
" set_var
|
||||
"
|
||||
" @param {string} key
|
||||
" @param {unknown} value
|
||||
"
|
||||
function! s:FloatingWindow.set_var(key, value) abort
|
||||
let self._vars[a:key] = a:value
|
||||
if self.is_visible()
|
||||
call setwinvar(self._winid, a:key, a:value)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" get_var
|
||||
"
|
||||
" @param {string} key
|
||||
"
|
||||
function! s:FloatingWindow.get_var(key) abort
|
||||
return self._vars[a:key]
|
||||
endfunction
|
||||
|
||||
"
|
||||
" open
|
||||
"
|
||||
" @param {number} args.row 0-based indexing
|
||||
" @param {number} args.col 0-based indexing
|
||||
" @param {number} args.width
|
||||
" @param {number} args.height
|
||||
" @param {boolean|[string]?} args.border - boolean, or list of characters
|
||||
" clockwise from top-left (same as nvim_open_win() in neovim)
|
||||
" @param {number?} args.topline
|
||||
" @param {string?} args.origin - topleft/topright/botleft/botright
|
||||
"
|
||||
function! s:FloatingWindow.open(args) abort
|
||||
let l:style = {
|
||||
\ 'row': a:args.row,
|
||||
\ 'col': a:args.col,
|
||||
\ 'width': a:args.width,
|
||||
\ 'height': a:args.height,
|
||||
\ 'border': get(a:args, 'border', v:false),
|
||||
\ 'topline': get(a:args, 'topline', 1),
|
||||
\ 'origin': get(a:args, 'origin', 'topleft'),
|
||||
\ }
|
||||
|
||||
let l:will_move = self.is_visible()
|
||||
if l:will_move
|
||||
let self._winid = s:_move(self, self._winid, self._bufnr, l:style)
|
||||
else
|
||||
let self._winid = s:_open(self._bufnr, l:style, { -> self._on_closed() })
|
||||
endif
|
||||
for [l:key, l:value] in items(self._vars)
|
||||
call setwinvar(self._winid, l:key, l:value)
|
||||
endfor
|
||||
if !l:will_move
|
||||
call s:_notify_opened(self._winid, self)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"
|
||||
" close
|
||||
"
|
||||
function! s:FloatingWindow.close() abort
|
||||
if self.is_visible()
|
||||
call s:_close(self._winid)
|
||||
endif
|
||||
let self._winid = v:null
|
||||
endfunction
|
||||
|
||||
"
|
||||
" enter
|
||||
"
|
||||
function! s:FloatingWindow.enter() abort
|
||||
call s:_enter(self._winid)
|
||||
endfunction
|
||||
|
||||
"
|
||||
" is_visible
|
||||
"
|
||||
function! s:FloatingWindow.is_visible() abort
|
||||
return s:_exists(self._winid) ? v:true : v:false
|
||||
endfunction
|
||||
|
||||
"
|
||||
" open
|
||||
"
|
||||
if has('nvim')
|
||||
function! s:_open(bufnr, style, callback) abort
|
||||
let l:winid = nvim_open_win(a:bufnr, v:false, s:_style(a:style))
|
||||
call s:Window.scroll(l:winid, a:style.topline)
|
||||
return l:winid
|
||||
endfunction
|
||||
else
|
||||
function! s:_open(bufnr, style, callback) abort
|
||||
return popup_create(a:bufnr, extend(s:_style(a:style), {
|
||||
\ 'callback': a:callback,
|
||||
\ }, 'force'))
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" close
|
||||
"
|
||||
if has('nvim')
|
||||
function! s:_close(winid) abort
|
||||
call nvim_win_close(a:winid, v:true)
|
||||
call s:_notify_closed()
|
||||
endfunction
|
||||
else
|
||||
function! s:_close(winid) abort
|
||||
call popup_close(a:winid)
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" move
|
||||
"
|
||||
if has('nvim')
|
||||
function! s:_move(self, winid, bufnr, style) abort
|
||||
call nvim_win_set_config(a:winid, s:_style(a:style))
|
||||
if a:bufnr != nvim_win_get_buf(a:winid)
|
||||
call nvim_win_set_buf(a:winid, a:bufnr)
|
||||
endif
|
||||
call s:Window.scroll(a:winid, a:style.topline)
|
||||
return a:winid
|
||||
endfunction
|
||||
else
|
||||
function! s:_move(self, winid, bufnr, style) abort
|
||||
" vim's popup window can't change bufnr so open new popup in here.
|
||||
if a:bufnr != winbufnr(a:winid)
|
||||
let l:On_closed = a:self._on_closed
|
||||
let a:self._on_closed = { -> {} }
|
||||
call s:_close(a:winid)
|
||||
let a:self._on_closed = l:On_closed
|
||||
return s:_open(a:bufnr, a:style, { -> a:self._on_closed() })
|
||||
endif
|
||||
let l:style = s:_style(a:style)
|
||||
call popup_move(a:winid, l:style)
|
||||
call popup_setoptions(a:winid, l:style)
|
||||
return a:winid
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" enter
|
||||
"
|
||||
if has('nvim')
|
||||
function! s:_enter(winid) abort
|
||||
call win_gotoid(a:winid)
|
||||
endfunction
|
||||
else
|
||||
function! s:_enter(winid) abort
|
||||
" not supported.
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" exists
|
||||
"
|
||||
if has('nvim')
|
||||
function! s:_exists(winid) abort
|
||||
try
|
||||
return type(a:winid) == type(0) && nvim_win_is_valid(a:winid) && nvim_win_get_number(a:winid) != -1
|
||||
catch /.*/
|
||||
return v:false
|
||||
endtry
|
||||
endfunction
|
||||
else
|
||||
function! s:_exists(winid) abort
|
||||
return type(a:winid) == type(0) && winheight(a:winid) != -1
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" info
|
||||
"
|
||||
if has('nvim')
|
||||
function! s:_info(winid) abort
|
||||
let l:info = getwininfo(a:winid)[0]
|
||||
return {
|
||||
\ 'row': l:info.winrow,
|
||||
\ 'col': l:info.wincol,
|
||||
\ 'width': l:info.width,
|
||||
\ 'height': l:info.height,
|
||||
\ 'topline': l:info.topline,
|
||||
\ }
|
||||
endfunction
|
||||
else
|
||||
function! s:_info(winid) abort
|
||||
let l:pos = popup_getpos(a:winid)
|
||||
return {
|
||||
\ 'row': l:pos.core_line,
|
||||
\ 'col': l:pos.core_col,
|
||||
\ 'width': l:pos.core_width,
|
||||
\ 'height': l:pos.core_height,
|
||||
\ 'topline': l:pos.firstline,
|
||||
\ }
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" style
|
||||
"
|
||||
if has('nvim')
|
||||
function! s:_style(style) abort
|
||||
let l:style = s:_resolve_origin(a:style)
|
||||
let l:style = s:_resolve_border(l:style)
|
||||
let l:style = {
|
||||
\ 'relative': 'editor',
|
||||
\ 'row': l:style.row - 1,
|
||||
\ 'col': l:style.col - 1,
|
||||
\ 'width': l:style.width,
|
||||
\ 'height': l:style.height,
|
||||
\ 'focusable': v:true,
|
||||
\ 'style': 'minimal',
|
||||
\ 'border': has_key(l:style, 'border') ? l:style.border : 'none',
|
||||
\ }
|
||||
if !exists('*win_execute') " We can't detect neovim features via patch version so we try it by function existence.
|
||||
unlet l:style.border
|
||||
endif
|
||||
return l:style
|
||||
endfunction
|
||||
else
|
||||
function! s:_style(style) abort
|
||||
let l:style = s:_resolve_origin(a:style)
|
||||
let l:style = s:_resolve_border(l:style)
|
||||
return {
|
||||
\ 'line': l:style.row,
|
||||
\ 'col': l:style.col,
|
||||
\ 'pos': 'topleft',
|
||||
\ 'wrap': v:false,
|
||||
\ 'moved': [0, 0, 0],
|
||||
\ 'scrollbar': 0,
|
||||
\ 'maxwidth': l:style.width,
|
||||
\ 'maxheight': l:style.height,
|
||||
\ 'minwidth': l:style.width,
|
||||
\ 'minheight': l:style.height,
|
||||
\ 'tabpage': 0,
|
||||
\ 'firstline': l:style.topline,
|
||||
\ 'padding': [0, 0, 0, 0],
|
||||
\ 'border': has_key(l:style, 'border') ? [1, 1, 1, 1] : [0, 0, 0, 0],
|
||||
\ 'borderchars': get(l:style, 'border', []),
|
||||
\ 'fixed': v:true,
|
||||
\ }
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" _resolve_origin
|
||||
"
|
||||
function! s:_resolve_origin(style) abort
|
||||
if index(['topleft', 'topright', 'bottomleft', 'bottomright', 'topcenter', 'bottomcenter'], a:style.origin) == -1
|
||||
let a:style.origin = 'topleft'
|
||||
endif
|
||||
|
||||
if a:style.origin ==# 'topleft'
|
||||
let a:style.col = a:style.col
|
||||
let a:style.row = a:style.row
|
||||
elseif a:style.origin ==# 'topright'
|
||||
let a:style.col = a:style.col - (a:style.width - 1)
|
||||
let a:style.row = a:style.row
|
||||
elseif a:style.origin ==# 'bottomleft'
|
||||
let a:style.col = a:style.col
|
||||
let a:style.row = a:style.row - (a:style.height - 1)
|
||||
elseif a:style.origin ==# 'bottomright'
|
||||
let a:style.col = a:style.col - (a:style.width - 1)
|
||||
let a:style.row = a:style.row - (a:style.height - 1)
|
||||
elseif a:style.origin ==# 'topcenter'
|
||||
let a:style.col = a:style.col - float2nr(a:style.width / 2)
|
||||
let a:style.row = a:style.row
|
||||
elseif a:style.origin ==# 'bottomcenter'
|
||||
let a:style.col = a:style.col - float2nr(a:style.width / 2)
|
||||
let a:style.row = a:style.row - (a:style.height - 1)
|
||||
elseif a:style.origin ==# 'centercenter'
|
||||
let a:style.col = a:style.col - float2nr(a:style.width / 2)
|
||||
let a:style.row = a:style.row - float2nr(a:style.height / 2)
|
||||
endif
|
||||
return a:style
|
||||
endfunction
|
||||
|
||||
if has('nvim')
|
||||
function! s:_resolve_border(style) abort
|
||||
let l:border = get(a:style, 'border', v:null)
|
||||
if !empty(l:border)
|
||||
if type(l:border) != type([])
|
||||
if &ambiwidth ==# 'single'
|
||||
let a:style.border = ['┌', '─', '┐', '│', '┘', '─', '└', '│']
|
||||
else
|
||||
let a:style.border = ['+', '-', '+', '|', '+', '-', '+', '|']
|
||||
endif
|
||||
endif
|
||||
elseif has_key(a:style, 'border')
|
||||
unlet a:style.border
|
||||
endif
|
||||
return a:style
|
||||
endfunction
|
||||
else
|
||||
function! s:_resolve_border(style) abort
|
||||
let l:border = get(a:style, 'border', v:null)
|
||||
if !empty(get(a:style, 'border', v:null))
|
||||
if type(l:border) != type([])
|
||||
if &ambiwidth ==# 'single'
|
||||
let a:style.border = ['─', '│', '─', '│', '┌', '┐', '┘', '└']
|
||||
else
|
||||
let a:style.border = ['-', '|', '-', '|', '+', '+', '+', '+']
|
||||
endif
|
||||
else
|
||||
" Emulate nvim behavior for lists of 1/2/4 elements
|
||||
let l:topleft = l:border[0]
|
||||
let l:top = get(l:border, 1, l:topleft)
|
||||
let l:topright = get(l:border, 2, l:topleft)
|
||||
let l:right = get(l:border, 3, l:top)
|
||||
let l:bottomright = get(l:border, 4, l:topleft)
|
||||
let l:bottom = get(l:border, 5, l:top)
|
||||
let l:bottomleft = get(l:border, 6, l:topright)
|
||||
let l:left = get(l:border, 7, l:right)
|
||||
let a:style.border = [
|
||||
\ l:top, l:right, l:bottom, l:left,
|
||||
\ l:topleft, l:topright, l:bottomright, l:bottomleft,
|
||||
\ ]
|
||||
endif
|
||||
elseif has_key(a:style, 'border')
|
||||
unlet a:style.border
|
||||
endif
|
||||
return a:style
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"
|
||||
" init
|
||||
"
|
||||
let s:has_init = v:false
|
||||
let s:filepath = expand('<sfile>:p')
|
||||
function! s:_init() abort
|
||||
if s:has_init || !has('nvim')
|
||||
return
|
||||
endif
|
||||
let s:has_init = v:true
|
||||
execute printf('augroup VS_Vim_Window_FloatingWindow:%s', s:filepath)
|
||||
autocmd!
|
||||
autocmd WinEnter * call <SID>_notify_closed()
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
334
dot_vim/plugged/vim-lsp/autoload/vital/lsp.vim
Normal file
334
dot_vim/plugged/vim-lsp/autoload/vital/lsp.vim
Normal file
@@ -0,0 +1,334 @@
|
||||
let s:plugin_name = expand('<sfile>:t:r')
|
||||
let s:vital_base_dir = expand('<sfile>:h')
|
||||
let s:project_root = expand('<sfile>:h:h:h')
|
||||
let s:is_vital_vim = s:plugin_name is# 'vital'
|
||||
|
||||
let s:loaded = {}
|
||||
let s:cache_sid = {}
|
||||
|
||||
function! vital#{s:plugin_name}#new() abort
|
||||
return s:new(s:plugin_name)
|
||||
endfunction
|
||||
|
||||
function! vital#{s:plugin_name}#import(...) abort
|
||||
if !exists('s:V')
|
||||
let s:V = s:new(s:plugin_name)
|
||||
endif
|
||||
return call(s:V.import, a:000, s:V)
|
||||
endfunction
|
||||
|
||||
let s:Vital = {}
|
||||
|
||||
function! s:new(plugin_name) abort
|
||||
let base = deepcopy(s:Vital)
|
||||
let base._plugin_name = a:plugin_name
|
||||
return base
|
||||
endfunction
|
||||
|
||||
function! s:vital_files() abort
|
||||
if !exists('s:vital_files')
|
||||
let s:vital_files = map(
|
||||
\ s:is_vital_vim ? s:_global_vital_files() : s:_self_vital_files(),
|
||||
\ 'fnamemodify(v:val, ":p:gs?[\\\\/]?/?")')
|
||||
endif
|
||||
return copy(s:vital_files)
|
||||
endfunction
|
||||
let s:Vital.vital_files = function('s:vital_files')
|
||||
|
||||
function! s:import(name, ...) abort dict
|
||||
let target = {}
|
||||
let functions = []
|
||||
for a in a:000
|
||||
if type(a) == type({})
|
||||
let target = a
|
||||
elseif type(a) == type([])
|
||||
let functions = a
|
||||
endif
|
||||
unlet a
|
||||
endfor
|
||||
let module = self._import(a:name)
|
||||
if empty(functions)
|
||||
call extend(target, module, 'keep')
|
||||
else
|
||||
for f in functions
|
||||
if has_key(module, f) && !has_key(target, f)
|
||||
let target[f] = module[f]
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
return target
|
||||
endfunction
|
||||
let s:Vital.import = function('s:import')
|
||||
|
||||
function! s:load(...) abort dict
|
||||
for arg in a:000
|
||||
let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg]
|
||||
let target = split(join(as, ''), '\W\+')
|
||||
let dict = self
|
||||
let dict_type = type({})
|
||||
while !empty(target)
|
||||
let ns = remove(target, 0)
|
||||
if !has_key(dict, ns)
|
||||
let dict[ns] = {}
|
||||
endif
|
||||
if type(dict[ns]) == dict_type
|
||||
let dict = dict[ns]
|
||||
else
|
||||
unlet dict
|
||||
break
|
||||
endif
|
||||
endwhile
|
||||
if exists('dict')
|
||||
call extend(dict, self._import(name))
|
||||
endif
|
||||
unlet arg
|
||||
endfor
|
||||
return self
|
||||
endfunction
|
||||
let s:Vital.load = function('s:load')
|
||||
|
||||
function! s:unload() abort dict
|
||||
let s:loaded = {}
|
||||
let s:cache_sid = {}
|
||||
unlet! s:vital_files
|
||||
endfunction
|
||||
let s:Vital.unload = function('s:unload')
|
||||
|
||||
function! s:exists(name) abort dict
|
||||
if a:name !~# '\v^\u\w*%(\.\u\w*)*$'
|
||||
throw 'vital: Invalid module name: ' . a:name
|
||||
endif
|
||||
return s:_module_path(a:name) isnot# ''
|
||||
endfunction
|
||||
let s:Vital.exists = function('s:exists')
|
||||
|
||||
function! s:search(pattern) abort dict
|
||||
let paths = s:_extract_files(a:pattern, self.vital_files())
|
||||
let modules = sort(map(paths, 's:_file2module(v:val)'))
|
||||
return uniq(modules)
|
||||
endfunction
|
||||
let s:Vital.search = function('s:search')
|
||||
|
||||
function! s:plugin_name() abort dict
|
||||
return self._plugin_name
|
||||
endfunction
|
||||
let s:Vital.plugin_name = function('s:plugin_name')
|
||||
|
||||
function! s:_self_vital_files() abort
|
||||
let builtin = printf('%s/__%s__/', s:vital_base_dir, s:plugin_name)
|
||||
let installed = printf('%s/_%s/', s:vital_base_dir, s:plugin_name)
|
||||
let base = builtin . ',' . installed
|
||||
return split(globpath(base, '**/*.vim', 1), "\n")
|
||||
endfunction
|
||||
|
||||
function! s:_global_vital_files() abort
|
||||
let pattern = 'autoload/vital/__*__/**/*.vim'
|
||||
return split(globpath(&runtimepath, pattern, 1), "\n")
|
||||
endfunction
|
||||
|
||||
function! s:_extract_files(pattern, files) abort
|
||||
let tr = {'.': '/', '*': '[^/]*', '**': '.*'}
|
||||
let target = substitute(a:pattern, '\.\|\*\*\?', '\=tr[submatch(0)]', 'g')
|
||||
let regexp = printf('autoload/vital/[^/]\+/%s.vim$', target)
|
||||
return filter(a:files, 'v:val =~# regexp')
|
||||
endfunction
|
||||
|
||||
function! s:_file2module(file) abort
|
||||
let filename = fnamemodify(a:file, ':p:gs?[\\/]?/?')
|
||||
let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$')
|
||||
return join(split(tail, '[\\/]\+'), '.')
|
||||
endfunction
|
||||
|
||||
" @param {string} name e.g. Data.List
|
||||
function! s:_import(name) abort dict
|
||||
if has_key(s:loaded, a:name)
|
||||
return copy(s:loaded[a:name])
|
||||
endif
|
||||
let module = self._get_module(a:name)
|
||||
if has_key(module, '_vital_created')
|
||||
call module._vital_created(module)
|
||||
endif
|
||||
let export_module = filter(copy(module), 'v:key =~# "^\\a"')
|
||||
" Cache module before calling module._vital_loaded() to avoid cyclic
|
||||
" dependences but remove the cache if module._vital_loaded() fails.
|
||||
" let s:loaded[a:name] = export_module
|
||||
let s:loaded[a:name] = export_module
|
||||
if has_key(module, '_vital_loaded')
|
||||
try
|
||||
call module._vital_loaded(vital#{s:plugin_name}#new())
|
||||
catch
|
||||
unlet s:loaded[a:name]
|
||||
throw 'vital: fail to call ._vital_loaded(): ' . v:exception . " from:\n" . s:_format_throwpoint(v:throwpoint)
|
||||
endtry
|
||||
endif
|
||||
return copy(s:loaded[a:name])
|
||||
endfunction
|
||||
let s:Vital._import = function('s:_import')
|
||||
|
||||
function! s:_format_throwpoint(throwpoint) abort
|
||||
let funcs = []
|
||||
let stack = matchstr(a:throwpoint, '^function \zs.*, .\{-} \d\+$')
|
||||
for line in split(stack, '\.\.')
|
||||
let m = matchlist(line, '^\(.\+\)\%(\[\(\d\+\)\]\|, .\{-} \(\d\+\)\)$')
|
||||
if !empty(m)
|
||||
let [name, lnum, lnum2] = m[1:3]
|
||||
if empty(lnum)
|
||||
let lnum = lnum2
|
||||
endif
|
||||
let info = s:_get_func_info(name)
|
||||
if !empty(info)
|
||||
let attrs = empty(info.attrs) ? '' : join([''] + info.attrs)
|
||||
let flnum = info.lnum == 0 ? '' : printf(' Line:%d', info.lnum + lnum)
|
||||
call add(funcs, printf('function %s(...)%s Line:%d (%s%s)',
|
||||
\ info.funcname, attrs, lnum, info.filename, flnum))
|
||||
continue
|
||||
endif
|
||||
endif
|
||||
" fallback when function information cannot be detected
|
||||
call add(funcs, line)
|
||||
endfor
|
||||
return join(funcs, "\n")
|
||||
endfunction
|
||||
|
||||
" @vimlint(EVL102, 1, l:_)
|
||||
" @vimlint(EVL102, 1, l:__)
|
||||
function! s:_get_func_info(name) abort
|
||||
let name = a:name
|
||||
if a:name =~# '^\d\+$' " is anonymous-function
|
||||
let name = printf('{%s}', a:name)
|
||||
elseif a:name =~# '^<lambda>\d\+$' " is lambda-function
|
||||
let name = printf("{'%s'}", a:name)
|
||||
endif
|
||||
if !exists('*' . name)
|
||||
return {}
|
||||
endif
|
||||
let body = execute(printf('verbose function %s', name))
|
||||
let lines = split(body, "\n")
|
||||
let signature = matchstr(lines[0], '^\s*\zs.*')
|
||||
let [_, file, lnum; __] = matchlist(lines[1],
|
||||
\ '^\t\%(Last set from\|.\{-}:\)\s*\zs\(.\{-}\)\%( \S\+ \(\d\+\)\)\?$')
|
||||
return {
|
||||
\ 'filename': substitute(file, '[/\\]\+', '/', 'g'),
|
||||
\ 'lnum': 0 + lnum,
|
||||
\ 'funcname': a:name,
|
||||
\ 'arguments': split(matchstr(signature, '(\zs.*\ze)'), '\s*,\s*'),
|
||||
\ 'attrs': filter(['dict', 'abort', 'range', 'closure'], 'signature =~# (").*" . v:val)'),
|
||||
\ }
|
||||
endfunction
|
||||
" @vimlint(EVL102, 0, l:__)
|
||||
" @vimlint(EVL102, 0, l:_)
|
||||
|
||||
" s:_get_module() returns module object wihch has all script local functions.
|
||||
function! s:_get_module(name) abort dict
|
||||
let funcname = s:_import_func_name(self.plugin_name(), a:name)
|
||||
try
|
||||
return call(funcname, [])
|
||||
catch /^Vim\%((\a\+)\)\?:E117:/
|
||||
return s:_get_builtin_module(a:name)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:_get_builtin_module(name) abort
|
||||
return s:sid2sfuncs(s:_module_sid(a:name))
|
||||
endfunction
|
||||
|
||||
if s:is_vital_vim
|
||||
" For vital.vim, we can use s:_get_builtin_module directly
|
||||
let s:Vital._get_module = function('s:_get_builtin_module')
|
||||
else
|
||||
let s:Vital._get_module = function('s:_get_module')
|
||||
endif
|
||||
|
||||
function! s:_import_func_name(plugin_name, module_name) abort
|
||||
return printf('vital#_%s#%s#import', a:plugin_name, s:_dot_to_sharp(a:module_name))
|
||||
endfunction
|
||||
|
||||
function! s:_module_sid(name) abort
|
||||
let path = s:_module_path(a:name)
|
||||
if !filereadable(path)
|
||||
throw 'vital: module not found: ' . a:name
|
||||
endif
|
||||
let vital_dir = s:is_vital_vim ? '__\w\+__' : printf('_\{1,2}%s\%%(__\)\?', s:plugin_name)
|
||||
let base = join([vital_dir, ''], '[/\\]\+')
|
||||
let p = base . substitute('' . a:name, '\.', '[/\\\\]\\+', 'g')
|
||||
let sid = s:_sid(path, p)
|
||||
if !sid
|
||||
call s:_source(path)
|
||||
let sid = s:_sid(path, p)
|
||||
if !sid
|
||||
throw printf('vital: cannot get <SID> from path: %s', path)
|
||||
endif
|
||||
endif
|
||||
return sid
|
||||
endfunction
|
||||
|
||||
function! s:_module_path(name) abort
|
||||
return get(s:_extract_files(a:name, s:vital_files()), 0, '')
|
||||
endfunction
|
||||
|
||||
function! s:_module_sid_base_dir() abort
|
||||
return s:is_vital_vim ? &rtp : s:project_root
|
||||
endfunction
|
||||
|
||||
function! s:_dot_to_sharp(name) abort
|
||||
return substitute(a:name, '\.', '#', 'g')
|
||||
endfunction
|
||||
|
||||
function! s:_source(path) abort
|
||||
execute 'source' fnameescape(a:path)
|
||||
endfunction
|
||||
|
||||
" @vimlint(EVL102, 1, l:_)
|
||||
" @vimlint(EVL102, 1, l:__)
|
||||
function! s:_sid(path, filter_pattern) abort
|
||||
let unified_path = s:_unify_path(a:path)
|
||||
if has_key(s:cache_sid, unified_path)
|
||||
return s:cache_sid[unified_path]
|
||||
endif
|
||||
for line in filter(split(execute(':scriptnames'), "\n"), 'v:val =~# a:filter_pattern')
|
||||
let [_, sid, path; __] = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$')
|
||||
if s:_unify_path(path) is# unified_path
|
||||
let s:cache_sid[unified_path] = sid
|
||||
return s:cache_sid[unified_path]
|
||||
endif
|
||||
endfor
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
if filereadable(expand('<sfile>:r') . '.VIM') " is case-insensitive or not
|
||||
let s:_unify_path_cache = {}
|
||||
" resolve() is slow, so we cache results.
|
||||
" Note: On windows, vim can't expand path names from 8.3 formats.
|
||||
" So if getting full path via <sfile> and $HOME was set as 8.3 format,
|
||||
" vital load duplicated scripts. Below's :~ avoid this issue.
|
||||
function! s:_unify_path(path) abort
|
||||
if has_key(s:_unify_path_cache, a:path)
|
||||
return s:_unify_path_cache[a:path]
|
||||
endif
|
||||
let value = tolower(fnamemodify(resolve(fnamemodify(
|
||||
\ a:path, ':p')), ':~:gs?[\\/]?/?'))
|
||||
let s:_unify_path_cache[a:path] = value
|
||||
return value
|
||||
endfunction
|
||||
else
|
||||
function! s:_unify_path(path) abort
|
||||
return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?'))
|
||||
endfunction
|
||||
endif
|
||||
|
||||
" copied and modified from Vim.ScriptLocal
|
||||
let s:SNR = join(map(range(len("\<SNR>")), '"[\\x" . printf("%0x", char2nr("\<SNR>"[v:val])) . "]"'), '')
|
||||
function! s:sid2sfuncs(sid) abort
|
||||
let fs = split(execute(printf(':function /^%s%s_', s:SNR, a:sid)), "\n")
|
||||
let r = {}
|
||||
let pattern = printf('\m^function\s<SNR>%d_\zs\w\{-}\ze(', a:sid)
|
||||
for fname in map(fs, 'matchstr(v:val, pattern)')
|
||||
let r[fname] = function(s:_sfuncname(a:sid, fname))
|
||||
endfor
|
||||
return r
|
||||
endfunction
|
||||
|
||||
"" Return funcname of script local functions with SID
|
||||
function! s:_sfuncname(sid, funcname) abort
|
||||
return printf('<SNR>%s_%s', a:sid, a:funcname)
|
||||
endfunction
|
||||
8
dot_vim/plugged/vim-lsp/autoload/vital/lsp.vital
Normal file
8
dot_vim/plugged/vim-lsp/autoload/vital/lsp.vital
Normal file
@@ -0,0 +1,8 @@
|
||||
lsp
|
||||
b1e91b41f5028d65fa3d31a425ff21591d5d957f
|
||||
|
||||
VS.LSP.MarkupContent
|
||||
VS.Vim.Window.FloatingWindow
|
||||
VS.Vim.Syntax.Markdown
|
||||
VS.Vim.Buffer
|
||||
VS.Vim.Window
|
||||
Reference in New Issue
Block a user