mirror of
https://github.com/prabirshrestha/vim-lsp.git
synced 2025-12-14 20:35:59 +01:00
Prefer native lsp api when in vim. (#1362)
* add g:lsp_experimental_native_lsp flag to enable native vim lsp client * send request and correctly call on_notification * add support for native client in lsp#client#send_notification * Add native lsp client support for lsp#client#send_response * add native notification support * add support for native stop job * handle error response for document_formatting.vim and code_action.vim * add missing return for code action * fix indent * undo code action error * check for a:response to be of type dict as stderr will send string * use out_cb and err_cb instead of channel callback * bump has_native_client version check to 8.2.4780 which fixes LSP parsing messages when partial data is received * rename g:lsp_experimental_native_lsp to g:lsp_use_native_client * fix s:native_err_cb * pass env * disable lsp_use_native_client * add lsp#utils#has_native_lsp_client() * clean up log * handle requests from server * fix lint issues * update docs
This commit is contained in:
@@ -449,7 +449,7 @@ function! s:ensure_start(buf, server_name, cb) abort
|
||||
let l:server_info = l:server['server_info']
|
||||
if l:server['lsp_id'] > 0
|
||||
let l:msg = s:new_rpc_success('server already started', { 'server_name': a:server_name })
|
||||
call lsp#log(l:msg)
|
||||
call lsp#log_verbose(l:msg)
|
||||
call a:cb(l:msg)
|
||||
return
|
||||
endif
|
||||
@@ -647,7 +647,7 @@ function! s:ensure_init(buf, server_name, cb) abort
|
||||
|
||||
if has_key(l:server, 'init_result')
|
||||
let l:msg = s:new_rpc_success('lsp server already initialized', { 'server_name': a:server_name, 'init_result': l:server['init_result'] })
|
||||
call lsp#log(l:msg)
|
||||
call lsp#log_verbose(l:msg)
|
||||
call a:cb(l:msg)
|
||||
return
|
||||
endif
|
||||
@@ -721,7 +721,7 @@ function! s:ensure_conf(buf, server_name, cb) abort
|
||||
\ })
|
||||
endif
|
||||
let l:msg = s:new_rpc_success('configuration sent', { 'server_name': a:server_name })
|
||||
call lsp#log(l:msg)
|
||||
call lsp#log_verbose(l:msg)
|
||||
call a:cb(l:msg)
|
||||
endfunction
|
||||
|
||||
@@ -768,7 +768,7 @@ function! s:ensure_changed(buf, server_name, cb) abort
|
||||
|
||||
if l:buffer_info['changed_tick'] == l:changed_tick
|
||||
let l:msg = s:new_rpc_success('not dirty', { 'server_name': a:server_name, 'path': l:path })
|
||||
call lsp#log(l:msg)
|
||||
call lsp#log_verbose(l:msg)
|
||||
call a:cb(l:msg)
|
||||
return
|
||||
endif
|
||||
@@ -805,7 +805,7 @@ function! s:ensure_open(buf, server_name, cb) abort
|
||||
|
||||
if has_key(l:buffers, l:path)
|
||||
let l:msg = s:new_rpc_success('already opened', { 'server_name': a:server_name, 'path': l:path })
|
||||
call lsp#log(l:msg)
|
||||
call lsp#log_verbose(l:msg)
|
||||
call a:cb(l:msg)
|
||||
return
|
||||
endif
|
||||
|
||||
@@ -3,6 +3,9 @@ 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 {}
|
||||
@@ -212,9 +215,7 @@ 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
|
||||
if empty(l:ctx) | return -1 | endif
|
||||
|
||||
let l:request = { 'jsonrpc': '2.0' }
|
||||
|
||||
@@ -281,26 +282,156 @@ 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
|
||||
|
||||
@@ -3,6 +3,11 @@ 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
|
||||
|
||||
@@ -14,6 +14,7 @@ CONTENTS *vim-lsp-contents*
|
||||
Health Check |vim-lsp-healthcheck|
|
||||
Options |vim-lsp-options|
|
||||
g:lsp_auto_enable |g:lsp_auto_enable|
|
||||
g:lsp_use_native_client |g:lsp_use_native_client|
|
||||
g:lsp_preview_keep_focus |g:lsp_preview_keep_focus|
|
||||
g:lsp_preview_float |g:lsp_preview_float|
|
||||
g:lsp_preview_autoclose |g:lsp_preview_autoclose|
|
||||
@@ -207,6 +208,9 @@ http://downloads.sourceforge.net/luabinaries/lua-5.3.2_Win32_dllw4_lib.zip
|
||||
64bit:
|
||||
http://downloads.sourceforge.net/luabinaries/lua-5.3.2_Win64_dllw4_lib.zip
|
||||
|
||||
If you are using vim set `let g:lsp_use_native_client = 1` and make sure you
|
||||
are running vim 8.2.4780+.
|
||||
|
||||
Set |g:lsp_semantic_enabled| to 0.
|
||||
|
||||
Set |g:lsp_format_sync_timeout| to a reasonable value such as `1000`.
|
||||
@@ -297,6 +301,18 @@ g:lsp_auto_enable *g:lsp_auto_enable*
|
||||
let g:lsp_auto_enable = 1
|
||||
let g:lsp_auto_enable = 0
|
||||
|
||||
g:lsp_use_native_client *g:lsp_use_native_client*
|
||||
Type: |Number|
|
||||
Default: `0`
|
||||
|
||||
Enable native lsp client support for vim 8.2.4780+. No impact for neovim.
|
||||
TCP language servers are not supported and should be set to 0 if one is
|
||||
used.
|
||||
|
||||
Example: >
|
||||
let g:lsp_use_native_client = 1
|
||||
let g:lsp_use_native_client = 0
|
||||
|
||||
g:lsp_preview_keep_focus *g:lsp_preview_keep_focus*
|
||||
Type: |Number|
|
||||
Default: `1`
|
||||
|
||||
@@ -4,6 +4,7 @@ endif
|
||||
let g:lsp_loaded = 1
|
||||
|
||||
let g:lsp_use_lua = get(g:, 'lsp_use_lua', has('nvim-0.4.0') || (has('lua') && has('patch-8.2.0775')))
|
||||
let g:lsp_use_native_client = get(g:, 'lsp_use_native_client', 0)
|
||||
let g:lsp_auto_enable = get(g:, 'lsp_auto_enable', 1)
|
||||
let g:lsp_async_completion = get(g:, 'lsp_async_completion', 0)
|
||||
let g:lsp_log_file = get(g:, 'lsp_log_file', '')
|
||||
|
||||
Reference in New Issue
Block a user