]> git.madduck.net Git - etc/vim.git/blob - .vim/bundle/vim-lsp/autoload/lsp.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge commit 'a39f715c13be3352193ffd9c5b7536b8786eff64' as '.vim/bundle/vim-lsp'
[etc/vim.git] / .vim / bundle / vim-lsp / autoload / lsp.vim
1 let s:enabled = 0
2 let s:already_setup = 0
3 let s:Stream = lsp#callbag#makeSubject()
4 " workspace_folders = { 'uri': { uri, name } }
5 let s:servers = {} " { lsp_id, server_info, workspace_folders, init_callbacks, init_result, buffers: { path: { changed_tick } }
6 let s:last_command_id = 0
7 let s:notification_callbacks = [] " { name, callback }
8
9 " This hold previous content for each language servers to make
10 " DidChangeTextDocumentParams. The key is buffer numbers:
11 "    {
12 "      1: {
13 "        "golsp": [ "first-line", "next-line", ... ],
14 "        "bingo": [ "first-line", "next-line", ... ]
15 "      },
16 "      2: {
17 "        "pylsp": [ "first-line", "next-line", ... ]
18 "      }
19 "    }
20 let s:file_content = {}
21
22 " do nothing, place it here only to avoid the message
23 augroup _lsp_silent_
24     autocmd!
25     autocmd User lsp_setup silent
26     autocmd User lsp_register_server silent
27     autocmd User lsp_unregister_server silent
28     autocmd User lsp_server_init silent
29     autocmd User lsp_server_exit silent
30     autocmd User lsp_complete_done silent
31     autocmd User lsp_float_opened silent
32     autocmd User lsp_float_closed silent
33     autocmd User lsp_float_focused silent
34     autocmd User lsp_buffer_enabled silent
35     autocmd User lsp_diagnostics_updated silent
36     autocmd User lsp_progress_updated silent
37 augroup END
38
39 function! lsp#log_verbose(...) abort
40     if g:lsp_log_verbose
41         call call(function('lsp#log'), a:000)
42     endif
43 endfunction
44
45 function! lsp#log(...) abort
46     if !empty(g:lsp_log_file)
47         call writefile([strftime('%c') . ':' . json_encode(a:000)], g:lsp_log_file, 'a')
48     endif
49 endfunction
50
51 function! lsp#enable() abort
52     if s:enabled
53         return
54     endif
55     if !s:already_setup
56         doautocmd <nomodeline> User lsp_setup
57         let s:already_setup = 1
58     endif
59     let s:enabled = 1
60     if g:lsp_signature_help_enabled
61         call lsp#ui#vim#signature_help#setup()
62     endif
63     call lsp#ui#vim#completion#_setup()
64     call lsp#internal#document_highlight#_enable()
65     call lsp#internal#diagnostics#_enable()
66     call lsp#internal#document_code_action#signs#_enable()
67     call lsp#internal#semantic#_enable()
68     call lsp#internal#show_message_request#_enable()
69     call lsp#internal#show_message#_enable()
70     call lsp#internal#work_done_progress#_enable()
71     call lsp#internal#completion#documentation#_enable()
72     call lsp#internal#inlay_hints#_enable()
73     call s:register_events()
74 endfunction
75
76 function! lsp#disable() abort
77     if !s:enabled
78         return
79     endif
80     call lsp#ui#vim#signature_help#_disable()
81     call lsp#ui#vim#completion#_disable()
82     call lsp#internal#document_highlight#_disable()
83     call lsp#internal#diagnostics#_disable()
84     call lsp#internal#document_code_action#signs#_disable()
85     call lsp#internal#semantic#_disable()
86     call lsp#internal#show_message_request#_disable()
87     call lsp#internal#show_message#_disable()
88     call lsp#internal#work_done_progress#_disable()
89     call lsp#internal#completion#documentation#_disable()
90     call s:unregister_events()
91     let s:enabled = 0
92 endfunction
93
94 function! lsp#get_server_names() abort
95     return keys(s:servers)
96 endfunction
97
98 function! lsp#is_valid_server_name(name) abort
99     return has_key(s:servers, a:name)
100 endfunction
101
102 function! lsp#get_server_info(server_name) abort
103     return get(get(s:servers, a:server_name, {}), 'server_info', {})
104 endfunction
105
106 function! lsp#get_server_root_uri(server_name) abort
107     return get(s:servers[a:server_name]['server_info'], '_root_uri_resolved', '')
108 endfunction
109
110 function! lsp#get_server_capabilities(server_name) abort
111     let l:server = s:servers[a:server_name]
112     return has_key(l:server, 'init_result') ? l:server['init_result']['result']['capabilities'] : {}
113 endfunction
114
115 function! s:server_status(server_name) abort
116     if !has_key(s:servers, a:server_name)
117         return 'unknown server'
118     endif
119     let l:server = s:servers[a:server_name]
120     if has_key(l:server, 'exited')
121         return 'exited'
122     endif
123     if has_key(l:server, 'init_callbacks')
124         return 'starting'
125     endif
126     if has_key(l:server, 'failed')
127         return 'failed'
128     endif
129     if has_key(l:server, 'init_result')
130         return 'running'
131     endif
132     return 'not running'
133 endfunction
134
135 function! lsp#is_server_running(name) abort
136     if !has_key(s:servers, a:name)
137       return 0
138     endif
139
140     let l:server = s:servers[a:name]
141
142     return has_key(l:server, 'init_result')
143         \ && !has_key(l:server, 'exited')
144         \ && !has_key(l:server, 'init_callbacks')
145         \ && !has_key(l:server, 'failed')
146 endfunction
147
148 " Returns the current status of all servers (if called with no arguments) or
149 " the given server (if given an argument). Can be one of "unknown server",
150 " "exited", "starting", "failed", "running", "not running"
151 function! lsp#get_server_status(...) abort
152     if a:0 == 0
153         let l:strs = map(keys(s:servers), {k, v -> v . ': ' . s:server_status(v)})
154         return join(l:strs, "\n")
155     else
156         return s:server_status(a:1)
157     endif
158 endfunction
159
160 let s:color_map = {
161 \ 'exited': 'Error',
162 \ 'starting': 'MoreMsg',
163 \ 'failed': 'WarningMsg',
164 \ 'running': 'Keyword',
165 \ 'not running': 'Comment'
166 \}
167
168 " Collect the current status of all servers
169 function! lsp#collect_server_status() abort
170     let l:results = {}
171     for l:k in keys(s:servers)
172         let l:status = s:server_status(l:k)
173         " Copy to prevent callers from corrupting our config.
174         let l:info = deepcopy(s:servers[l:k].server_info)
175         let l:results[l:k] = {
176             \ 'status': l:status,
177             \ 'info': l:info,
178             \ }
179     endfor
180     return l:results
181 endfunction
182
183 " Print the current status of all servers
184 function! lsp#print_server_status() abort
185     for l:k in sort(keys(s:servers))
186         let l:status = s:server_status(l:k)
187         echon l:k . ': '
188         exec 'echohl' s:color_map[l:status]
189         echon l:status
190         echohl None
191         echo ''
192         if &verbose
193             let l:cfg = { 'workspace_config': s:servers[l:k].server_info.workspace_config }
194             if get(g:, 'loaded_scriptease', 0)
195                 call scriptease#pp_command(0, -1, l:cfg)
196             else
197                 echo json_encode(l:cfg)
198             endif
199             echo ''
200         endif
201     endfor
202 endfunction
203
204 " @params {server_info} = {
205 "   'name': 'go-langserver',        " required, must be unique
206 "   'allowlist': ['go'],            " optional, array of filetypes to allow, * for all filetypes
207 "   'blocklist': [],                " optional, array of filetypes to block, * for all filetypes,
208 "   'cmd': {server_info->['go-langserver]} " function that takes server_info and returns array of cmd and args, return empty if you don't want to start the server
209 " }
210 function! lsp#register_server(server_info) abort
211     let l:server_name = a:server_info['name']
212     if has_key(s:servers, l:server_name)
213         call lsp#log('lsp#register_server', 'server already registered', l:server_name)
214     endif
215     " NOTE: workspace_folders is dict for faster lookup instead of array
216     let s:servers[l:server_name] = {
217         \ 'server_info': a:server_info,
218         \ 'lsp_id': 0,
219         \ 'buffers': {},
220         \ 'workspace_folders': {},
221         \ }
222     call lsp#log('lsp#register_server', 'server registered', l:server_name)
223     doautocmd <nomodeline> User lsp_register_server
224 endfunction
225
226 "
227 " lsp#register_command
228 "
229 " @param {command_name} = string
230 " @param {callback} = funcref
231 "
232 function! lsp#register_command(command_name, callback) abort
233     call lsp#ui#vim#execute_command#_register(a:command_name, a:callback)
234 endfunction
235
236 function! lsp#register_notifications(name, callback) abort
237     call add(s:notification_callbacks, { 'name': a:name, 'callback': a:callback })
238 endfunction
239
240 function! lsp#unregister_notifications(name) abort
241     " TODO
242 endfunction
243
244 function! lsp#stop_server(server_name) abort
245     if has_key(s:servers, a:server_name) && s:servers[a:server_name]['lsp_id'] > 0
246         call lsp#client#stop(s:servers[a:server_name]['lsp_id'])
247     endif
248 endfunction
249
250 function! s:register_events() abort
251     augroup lsp
252         autocmd!
253         autocmd BufNewFile * call s:on_text_document_did_open()
254         autocmd BufReadPost * call s:on_text_document_did_open()
255         autocmd BufWritePost * call s:on_text_document_did_save()
256         autocmd BufWinLeave * call s:on_text_document_did_close()
257         autocmd BufWipeout * call s:on_buf_wipeout(expand('<afile>'))
258         autocmd InsertLeave * call s:on_text_document_did_change()
259         autocmd TextChanged * call s:on_text_document_did_change()
260         if exists('##TextChangedP')
261             autocmd TextChangedP * call s:on_text_document_did_change()
262         endif
263         if g:lsp_untitled_buffer_enabled
264             autocmd FileType * call s:on_filetype_changed(bufnr(expand('<afile>')))
265         endif
266     augroup END
267
268     for l:bufnr in range(1, bufnr('$'))
269         if bufexists(l:bufnr) && bufloaded(l:bufnr)
270             call s:on_text_document_did_open(l:bufnr)
271         endif
272     endfor
273 endfunction
274
275 function! s:unregister_events() abort
276     augroup lsp
277         autocmd!
278     augroup END
279     doautocmd <nomodeline> User lsp_unregister_server
280 endfunction
281
282 function! s:on_filetype_changed(buf) abort
283   call s:on_buf_wipeout(a:buf)
284   " TODO: stop unused servers
285   call s:on_text_document_did_open()
286 endfunction
287
288 function! s:on_text_document_did_open(...) abort
289     let l:buf = a:0 > 0 ? a:1 : bufnr('%')
290     if getbufvar(l:buf, '&buftype') ==# 'terminal' | return | endif
291     if getcmdwintype() !=# '' | return | endif
292     call lsp#log('s:on_text_document_did_open()', l:buf, &filetype, getcwd(), lsp#utils#get_buffer_uri(l:buf))
293
294     " Some language server notify diagnostics to the buffer that has not been loaded yet.
295     " This diagnostics was stored `autoload/lsp/internal/diagnostics/state.vim` but not highlighted.
296     " So we should refresh highlights when buffer opened.
297     call lsp#internal#diagnostics#state#_force_notify_buffer(l:buf)
298
299     for l:server_name in lsp#get_allowed_servers(l:buf)
300         call s:ensure_flush(l:buf, l:server_name, function('s:fire_lsp_buffer_enabled', [l:server_name, l:buf]))
301     endfor
302 endfunction
303
304 function! lsp#activate() abort
305   call s:on_text_document_did_open()
306 endfunction
307
308 function! s:on_text_document_did_save() abort
309     let l:buf = bufnr('%')
310     if getbufvar(l:buf, '&buftype') ==# 'terminal' | return | endif
311     call lsp#log('s:on_text_document_did_save()', l:buf)
312     for l:server_name in lsp#get_allowed_servers(l:buf)
313         if g:lsp_text_document_did_save_delay >= 0
314             " We delay the callback by one loop iteration as calls to ensure_flush
315             " can introduce mmap'd file locks that linger on Windows and collide
316             " with the second lang server call preventing saves (see #455)
317             call s:ensure_flush(l:buf, l:server_name, {result->timer_start(g:lsp_text_document_did_save_delay, {timer->s:call_did_save(l:buf, l:server_name, result, function('s:Noop'))})})
318         else
319             call s:ensure_flush(l:buf, l:server_name, {result->s:call_did_save(l:buf, l:server_name, result, function('s:Noop'))})
320         endif
321     endfor
322 endfunction
323
324 function! s:on_text_document_did_change() abort
325     let l:buf = bufnr('%')
326     if getbufvar(l:buf, '&buftype') ==# 'terminal' | return | endif
327     call lsp#log('s:on_text_document_did_change()', l:buf)
328     call s:add_didchange_queue(l:buf)
329 endfunction
330
331 function! s:call_did_save(buf, server_name, result, cb) abort
332     if lsp#client#is_error(a:result['response'])
333         return
334     endif
335
336     let l:server = s:servers[a:server_name]
337     let l:path = lsp#utils#get_buffer_uri(a:buf)
338
339     let [l:supports_did_save, l:did_save_options] = lsp#capabilities#get_text_document_save_registration_options(a:server_name)
340     if !l:supports_did_save
341         let l:msg = s:new_rpc_success('---> ignoring textDocument/didSave. not supported by server', { 'server_name': a:server_name, 'path': l:path })
342         call lsp#log(l:msg)
343         call a:cb(l:msg)
344         return
345     endif
346
347     call s:update_file_content(a:buf, a:server_name, lsp#utils#buffer#_get_lines(a:buf))
348
349     let l:buffers = l:server['buffers']
350     let l:buffer_info = l:buffers[l:path]
351
352     let l:params = {
353         \ 'textDocument': s:get_text_document_identifier(a:buf),
354         \ }
355
356     if l:did_save_options['includeText']
357         let l:params['text'] = s:get_text_document_text(a:buf, a:server_name)
358     endif
359     call s:send_notification(a:server_name, {
360         \ 'method': 'textDocument/didSave',
361         \ 'params': l:params,
362         \ })
363
364     let l:msg = s:new_rpc_success('textDocument/didSave sent', { 'server_name': a:server_name, 'path': l:path })
365     call lsp#log(l:msg)
366     call a:cb(l:msg)
367 endfunction
368
369 function! s:on_text_document_did_close() abort
370     let l:buf = bufnr('%')
371     if getbufvar(l:buf, '&buftype') ==# 'terminal' | return | endif
372     call lsp#log('s:on_text_document_did_close()', l:buf)
373 endfunction
374
375 function! s:get_last_file_content(buf, server_name) abort
376     if has_key(s:file_content, a:buf) && has_key(s:file_content[a:buf], a:server_name)
377         return s:file_content[a:buf][a:server_name]
378     endif
379     return []
380 endfunction
381
382 function! s:update_file_content(buf, server_name, new) abort
383     if !has_key(s:file_content, a:buf)
384         let s:file_content[a:buf] = {}
385     endif
386     call lsp#log('s:update_file_content()', a:buf)
387     let s:file_content[a:buf][a:server_name] = a:new
388 endfunction
389
390 function! s:on_buf_wipeout(buf) abort
391     if has_key(s:file_content, a:buf)
392         call remove(s:file_content, a:buf)
393     endif
394 endfunction
395
396 function! s:ensure_flush_all(buf, server_names) abort
397     for l:server_name in a:server_names
398         call s:ensure_flush(a:buf, l:server_name, function('s:Noop'))
399     endfor
400 endfunction
401
402 function! s:fire_lsp_buffer_enabled(server_name, buf, ...) abort
403     if a:buf == bufnr('%')
404         doautocmd <nomodeline> User lsp_buffer_enabled
405     else
406         " Not using ++once in autocmd for compatibility of VIM8.0
407         let l:cmd = printf('autocmd BufEnter <buffer=%d> doautocmd <nomodeline> User lsp_buffer_enabled', a:buf)
408         exec printf('augroup _lsp_fire_buffer_enabled | exec "%s | autocmd! _lsp_fire_buffer_enabled BufEnter <buffer>" | augroup END', l:cmd)
409     endif
410 endfunction
411
412 function! s:Noop(...) abort
413 endfunction
414
415 function! s:is_step_error(s) abort
416     return lsp#client#is_error(a:s.result[0]['response'])
417 endfunction
418
419 function! s:throw_step_error(s) abort
420     call a:s.callback(a:s.result[0])
421 endfunction
422
423 function! s:new_rpc_success(message, data) abort
424     return {
425         \ 'response': {
426         \   'message': a:message,
427         \   'data': extend({ '__data__': 'vim-lsp'}, a:data),
428         \ }
429         \ }
430 endfunction
431
432 function! s:new_rpc_error(message, data) abort
433     return {
434         \ 'response': {
435         \   'error': {
436         \       'code': 0,
437         \       'message': a:message,
438         \       'data': extend({ '__error__': 'vim-lsp'}, a:data),
439         \   },
440         \ }
441         \ }
442 endfunction
443
444 function! s:ensure_flush(buf, server_name, cb) abort
445     call lsp#utils#step#start([
446         \ {s->s:ensure_start(a:buf, a:server_name, s.callback)},
447         \ {s->s:is_step_error(s) ? s:throw_step_error(s) : s:ensure_init(a:buf, a:server_name, s.callback)},
448         \ {s->s:is_step_error(s) ? s:throw_step_error(s) : s:ensure_conf(a:buf, a:server_name, s.callback)},
449         \ {s->s:is_step_error(s) ? s:throw_step_error(s) : s:ensure_open(a:buf, a:server_name, s.callback)},
450         \ {s->s:is_step_error(s) ? s:throw_step_error(s) : s:ensure_changed(a:buf, a:server_name, s.callback)},
451         \ {s->a:cb(s.result[0])}
452         \ ])
453 endfunction
454
455 function! s:ensure_start(buf, server_name, cb) abort
456     let l:path = lsp#utils#get_buffer_path(a:buf)
457
458     if lsp#utils#is_remote_uri(l:path) || !has_key(s:servers, a:server_name)
459         let l:msg = s:new_rpc_error('ignoring start server due to remote uri', { 'server_name': a:server_name, 'uri': l:path})
460         call lsp#log(l:msg)
461         call a:cb(l:msg)
462         return
463     endif
464
465     let l:server = s:servers[a:server_name]
466     let l:server_info = l:server['server_info']
467     if l:server['lsp_id'] > 0
468         let l:msg = s:new_rpc_success('server already started', { 'server_name': a:server_name })
469         call lsp#log_verbose(l:msg)
470         call a:cb(l:msg)
471         return
472     endif
473
474     if has_key(l:server_info, 'tcp')
475         let l:tcp = l:server_info['tcp'](l:server_info)
476         let l:lsp_id = lsp#client#start({
477             \ 'tcp': l:tcp,
478             \ 'on_stderr': function('s:on_stderr', [a:server_name]),
479             \ 'on_exit': function('s:on_exit', [a:server_name]),
480             \ 'on_notification': function('s:on_notification', [a:server_name]),
481             \ 'on_request': function('s:on_request', [a:server_name]),
482             \ })
483     elseif has_key(l:server_info, 'cmd')
484         let l:cmd_type = type(l:server_info['cmd'])
485         if l:cmd_type == v:t_list
486             let l:cmd = l:server_info['cmd']
487         else
488             let l:cmd = l:server_info['cmd'](l:server_info)
489         endif
490
491         if empty(l:cmd)
492             let l:msg = s:new_rpc_error('ignore server start since cmd is empty', { 'server_name': a:server_name })
493             call lsp#log(l:msg)
494             call a:cb(l:msg)
495             return
496         endif
497
498         call lsp#log('Starting server', a:server_name, l:cmd)
499         let l:opts = {
500         \ 'cmd': l:cmd,
501         \ 'on_stderr': function('s:on_stderr', [a:server_name]),
502         \ 'on_exit': function('s:on_exit', [a:server_name]),
503         \ 'on_notification': function('s:on_notification', [a:server_name]),
504         \ 'on_request': function('s:on_request', [a:server_name]),
505         \ }
506         if has_key(l:server_info, 'env')
507           let l:opts.env = l:server_info.env
508         endif
509         let l:lsp_id = lsp#client#start(l:opts)
510     endif
511
512     if l:lsp_id > 0
513         let l:server['lsp_id'] = l:lsp_id
514         let l:msg = s:new_rpc_success('started lsp server successfully', { 'server_name': a:server_name, 'lsp_id': l:lsp_id })
515         call lsp#log(l:msg)
516         call a:cb(l:msg)
517     else
518         let l:msg = s:new_rpc_error('failed to start server', { 'server_name': a:server_name, 'cmd': l:cmd })
519         call lsp#log(l:msg)
520         call a:cb(l:msg)
521     endif
522 endfunction
523
524 function! lsp#default_get_supported_capabilities(server_info) abort
525     " Sorted alphabetically
526     return {
527     \   'textDocument': {
528     \       'callHierarchy': {
529     \           'dynamicRegistration': v:false,
530     \       },
531     \       'codeAction': {
532     \         'dynamicRegistration': v:false,
533     \         'codeActionLiteralSupport': {
534     \           'codeActionKind': {
535     \             'valueSet': ['', 'quickfix', 'refactor', 'refactor.extract', 'refactor.inline', 'refactor.rewrite', 'source', 'source.organizeImports'],
536     \           }
537     \         },
538     \         'isPreferredSupport': v:true,
539     \         'disabledSupport': v:true,
540     \       },
541     \       'codeLens': {
542     \           'dynamicRegistration': v:false,
543     \       },
544     \       'completion': {
545     \           'dynamicRegistration': v:false,
546     \           'completionItem': {
547     \              'documentationFormat': ['markdown', 'plaintext'],
548     \              'snippetSupport': v:false,
549     \              'resolveSupport': {
550     \                  'properties': ['additionalTextEdits']
551     \              }
552     \           },
553     \           'completionItemKind': {
554     \              'valueSet': lsp#omni#get_completion_item_kinds()
555     \           }
556     \       },
557     \       'declaration': {
558     \           'dynamicRegistration': v:false,
559     \           'linkSupport' : v:true
560     \       },
561     \       'definition': {
562     \           'dynamicRegistration': v:false,
563     \           'linkSupport' : v:true
564     \       },
565     \       'documentHighlight': {
566     \           'dynamicRegistration': v:false,
567     \       },
568     \       'documentSymbol': {
569     \           'dynamicRegistration': v:false,
570     \           'symbolKind': {
571     \              'valueSet': lsp#ui#vim#utils#get_symbol_kinds()
572     \           },
573     \           'hierarchicalDocumentSymbolSupport': v:false,
574     \           'labelSupport': v:false
575     \       },
576     \       'foldingRange': {
577     \           'dynamicRegistration': v:false,
578     \           'lineFoldingOnly': v:true,
579     \           'rangeLimit': 5000,
580     \       },
581     \       'formatting': {
582     \           'dynamicRegistration': v:false,
583     \       },
584     \       'hover': {
585     \           'dynamicRegistration': v:false,
586     \           'contentFormat': ['markdown', 'plaintext'],
587     \       },
588     \       'inlayHint': {
589     \           'dynamicRegistration': v:false,
590     \       },
591     \       'implementation': {
592     \           'dynamicRegistration': v:false,
593     \           'linkSupport' : v:true
594     \       },
595     \       'publishDiagnostics': {
596     \           'relatedInformation': v:true,
597     \       },
598     \       'rangeFormatting': {
599     \           'dynamicRegistration': v:false,
600     \       },
601     \       'references': {
602     \           'dynamicRegistration': v:false,
603     \       },
604     \       'rename': {
605     \           'dynamicRegistration': v:false,
606     \           'prepareSupport': v:true,
607     \           'prepareSupportDefaultBehavior': 1
608     \       },
609     \       'semanticTokens': {
610     \           'dynamicRegistration': v:false,
611     \           'requests': {
612     \               'range': v:false,
613     \               'full': lsp#internal#semantic#is_enabled()
614     \                     ? {'delta': v:true}
615     \                     : v:false
616     \
617     \           },
618     \           'tokenTypes': [
619     \               'type', 'class', 'enum', 'interface', 'struct',
620     \               'typeParameter', 'parameter', 'variable', 'property',
621     \               'enumMember', 'event', 'function', 'method', 'macro',
622     \               'keyword', 'modifier', 'comment', 'string', 'number',
623     \               'regexp', 'operator'
624     \           ],
625     \           'tokenModifiers': [],
626     \           'formats': ['relative'],
627     \           'overlappingTokenSupport': v:false,
628     \           'multilineTokenSupport': v:false,
629     \           'serverCancelSupport': v:false
630     \       },
631     \       'signatureHelp': {
632     \           'dynamicRegistration': v:false,
633     \       },
634     \       'synchronization': {
635     \           'didSave': v:true,
636     \           'dynamicRegistration': v:false,
637     \           'willSave': v:false,
638     \           'willSaveWaitUntil': v:false,
639     \       },
640     \       'typeDefinition': {
641     \           'dynamicRegistration': v:false,
642     \           'linkSupport' : v:true
643     \       },
644     \       'typeHierarchy': {
645     \           'dynamicRegistration': v:false
646     \       },
647     \   },
648     \   'window': {
649     \       'workDoneProgress': g:lsp_work_done_progress_enabled ? v:true : v:false,
650     \   },
651     \   'workspace': {
652     \       'applyEdit': v:true,
653     \       'configuration': v:true,
654     \       'symbol': {
655     \           'dynamicRegistration': v:false,
656     \       },
657     \       'workspaceFolders': g:lsp_experimental_workspace_folders ? v:true : v:false,
658     \   },
659     \ }
660 endfunction
661
662 function! s:ensure_init(buf, server_name, cb) abort
663     let l:server = s:servers[a:server_name]
664
665     if has_key(l:server, 'init_result')
666         let l:msg = s:new_rpc_success('lsp server already initialized', { 'server_name': a:server_name, 'init_result': l:server['init_result'] })
667         call lsp#log_verbose(l:msg)
668         call a:cb(l:msg)
669         return
670     endif
671
672     if has_key(l:server, 'init_callbacks')
673         " waiting for initialize response
674         call add(l:server['init_callbacks'], a:cb)
675         let l:msg = s:new_rpc_success('waiting for lsp server to initialize', { 'server_name': a:server_name })
676         call lsp#log(l:msg)
677         return
678     endif
679
680     " server has already started, but not initialized
681
682     let l:server_info = l:server['server_info']
683     let l:root_uri = has_key(l:server_info, 'root_uri') ?  l:server_info['root_uri'](l:server_info) : ''
684     if empty(l:root_uri)
685         let l:msg = s:new_rpc_error('ignore initialization lsp server due to empty root_uri', { 'server_name': a:server_name, 'lsp_id': l:server['lsp_id'] })
686         call lsp#log(l:msg)
687         let l:root_uri = lsp#utils#get_default_root_uri()
688     endif
689     let l:server['server_info']['_root_uri_resolved'] = l:root_uri
690     let l:server['workspace_folders'][l:root_uri] = { 'name': l:root_uri, 'uri': l:root_uri }
691
692     if has_key(l:server_info, 'capabilities')
693         let l:capabilities = l:server_info['capabilities']
694     else
695         let l:capabilities = call(g:lsp_get_supported_capabilities[0], [l:server_info])
696     endif
697
698     let l:request = {
699     \   'method': 'initialize',
700     \   'params': {
701     \     'processId': getpid(),
702     \     'clientInfo': { 'name': 'vim-lsp' },
703     \     'capabilities': l:capabilities,
704     \     'rootUri': l:root_uri,
705     \     'rootPath': lsp#utils#uri_to_path(l:root_uri),
706     \     'trace': 'off',
707     \   },
708     \ }
709     
710     let l:workspace_capabilities = get(l:capabilities, 'workspace', {})
711     if get(l:workspace_capabilities, 'workspaceFolders', v:false)
712         " TODO: extract folder name for l:root_uri
713         let l:server_info['workspaceFolders'] = [
714             \ { 'uri': l:root_uri, 'name': l:root_uri }
715             \ ]
716         let l:request['params']['workspaceFolders'] = l:server_info['workspaceFolders']
717     endif
718
719     if has_key(l:server_info, 'initialization_options')
720         let l:request.params['initializationOptions'] = l:server_info['initialization_options']
721     endif
722
723     let l:server['init_callbacks'] = [a:cb]
724
725     call s:send_request(a:server_name, l:request)
726 endfunction
727
728 function! s:ensure_conf(buf, server_name, cb) abort
729     let l:server = s:servers[a:server_name]
730     let l:server_info = l:server['server_info']
731     if has_key(l:server_info, 'workspace_config') && !get(l:server_info, '_workspace_config_sent', v:false)
732         let l:server_info['_workspace_config_sent'] = v:true
733         call s:send_notification(a:server_name, {
734             \ 'method': 'workspace/didChangeConfiguration',
735             \ 'params': {
736             \   'settings': lsp#utils#workspace_config#get(a:server_name),
737             \ }
738             \ })
739     endif
740     let l:msg = s:new_rpc_success('configuration sent', { 'server_name': a:server_name })
741     call lsp#log_verbose(l:msg)
742     call a:cb(l:msg)
743 endfunction
744
745 function! s:text_changes(buf, server_name) abort
746     let l:sync_kind = lsp#capabilities#get_text_document_change_sync_kind(a:server_name)
747     " When syncKind is None, return null for contentChanges.
748     if l:sync_kind == 0
749         return v:null
750     endif
751
752     " When syncKind is Incremental and previous content is saved.
753     if l:sync_kind == 2 && has_key(s:file_content, a:buf) && has_key(s:file_content[a:buf], a:server_name)
754         " compute diff
755         let l:old_content = s:get_last_file_content(a:buf, a:server_name)
756         let l:new_content = lsp#utils#buffer#_get_lines(a:buf)
757         let l:changes = lsp#utils#diff#compute(l:old_content, l:new_content)
758         if empty(l:changes.text) && l:changes.rangeLength ==# 0
759             return []
760         endif
761         call s:update_file_content(a:buf, a:server_name, l:new_content)
762         return [l:changes]
763     endif
764
765     let l:new_content = lsp#utils#buffer#_get_lines(a:buf)
766     let l:changes = {'text': join(l:new_content, "\n")}
767     call s:update_file_content(a:buf, a:server_name, l:new_content)
768     return [l:changes]
769 endfunction
770
771 function! s:ensure_changed(buf, server_name, cb) abort
772     let l:server = s:servers[a:server_name]
773     let l:path = lsp#utils#get_buffer_uri(a:buf)
774
775     let l:buffers = l:server['buffers']
776     if !has_key(l:buffers, l:path)
777         let l:msg = s:new_rpc_success('file is not managed', { 'server_name': a:server_name, 'path': l:path })
778         call lsp#log(l:msg)
779         call a:cb(l:msg)
780         return
781     endif
782     let l:buffer_info = l:buffers[l:path]
783
784     let l:changed_tick = getbufvar(a:buf, 'changedtick')
785
786     if l:buffer_info['changed_tick'] == l:changed_tick
787         let l:msg = s:new_rpc_success('not dirty', { 'server_name': a:server_name, 'path': l:path })
788         call lsp#log_verbose(l:msg)
789         call a:cb(l:msg)
790         return
791     endif
792
793     let l:buffer_info['changed_tick'] = l:changed_tick
794     let l:buffer_info['version'] = l:buffer_info['version'] + 1
795
796     call s:send_notification(a:server_name, {
797         \ 'method': 'textDocument/didChange',
798         \ 'params': {
799         \   'textDocument': s:get_versioned_text_document_identifier(a:buf, l:buffer_info),
800         \   'contentChanges': s:text_changes(a:buf, a:server_name),
801         \ }
802         \ })
803     call lsp#ui#vim#folding#send_request(a:server_name, a:buf, 0)
804
805     let l:msg = s:new_rpc_success('textDocument/didChange sent', { 'server_name': a:server_name, 'path': l:path })
806     call lsp#log(l:msg)
807     call a:cb(l:msg)
808 endfunction
809
810 function! s:ensure_open(buf, server_name, cb) abort
811     let l:server = s:servers[a:server_name]
812     let l:path = lsp#utils#get_buffer_uri(a:buf)
813
814     if empty(l:path)
815         let l:msg = s:new_rpc_success('ignore open since not a valid uri', { 'server_name': a:server_name, 'path': l:path })
816         call lsp#log(l:msg)
817         call a:cb(l:msg)
818         return
819     endif
820
821     let l:buffers = l:server['buffers']
822
823     if has_key(l:buffers, l:path)
824         let l:msg = s:new_rpc_success('already opened', { 'server_name': a:server_name, 'path': l:path })
825         call lsp#log_verbose(l:msg)
826         call a:cb(l:msg)
827         return
828     endif
829
830     if lsp#capabilities#has_workspace_folders_change_notifications(a:server_name)
831         call s:workspace_add_folder(a:server_name)
832     endif
833
834     call s:update_file_content(a:buf, a:server_name, lsp#utils#buffer#_get_lines(a:buf))
835
836     let l:buffer_info = { 'changed_tick': getbufvar(a:buf, 'changedtick'), 'version': 1, 'uri': l:path }
837     let l:buffers[l:path] = l:buffer_info
838
839     call s:send_notification(a:server_name, {
840         \ 'method': 'textDocument/didOpen',
841         \ 'params': {
842         \   'textDocument': s:get_text_document(a:buf, a:server_name, l:buffer_info)
843         \ },
844         \ })
845
846     call lsp#ui#vim#folding#send_request(a:server_name, a:buf, 0)
847
848     let l:msg = s:new_rpc_success('textDocument/open sent', { 'server_name': a:server_name, 'path': l:path, 'filetype': getbufvar(a:buf, '&filetype') })
849     call lsp#log(l:msg)
850     call a:cb(l:msg)
851 endfunction
852
853 function! s:workspace_add_folder(server_name) abort
854     if !g:lsp_experimental_workspace_folders | return | endif
855     let l:server = s:servers[a:server_name]
856     let l:server_info = l:server['server_info']
857     let l:root_uri = has_key(l:server_info, 'root_uri') ?  l:server_info['root_uri'](l:server_info) : lsp#utils#get_default_root_uri()
858     if !has_key(l:server['workspace_folders'], l:root_uri)
859         let l:workspace_folder = { 'name': l:root_uri, 'uri': l:root_uri }
860         call lsp#log('adding workspace folder', a:server_name, l:workspace_folder)
861         call s:send_notification(a:server_name, {
862             \ 'method': 'workspace/didChangeWorkspaceFolders',
863             \ 'params': {
864             \    'added': [l:workspace_folder],
865             \  }
866             \ })
867         let l:server['workspace_folders'][l:root_uri] = l:workspace_folder
868     endif
869 endfunction
870
871 function! s:send_request(server_name, data) abort
872     let l:lsp_id = s:servers[a:server_name]['lsp_id']
873     let l:data = copy(a:data)
874     if has_key(l:data, 'on_notification')
875         let l:data['on_notification'] = '---funcref---'
876     endif
877     call lsp#log_verbose('--->', l:lsp_id, a:server_name, l:data)
878     return lsp#client#send_request(l:lsp_id, a:data)
879 endfunction
880
881 function! s:send_notification(server_name, data) abort
882     let l:lsp_id = s:servers[a:server_name]['lsp_id']
883     let l:data = copy(a:data)
884     if has_key(l:data, 'on_notification')
885         let l:data['on_notification'] = '---funcref---'
886     endif
887     call lsp#log_verbose('--->', l:lsp_id, a:server_name, l:data)
888     call lsp#client#send_notification(l:lsp_id, a:data)
889 endfunction
890
891 function! s:send_response(server_name, data) abort
892     let l:lsp_id = s:servers[a:server_name]['lsp_id']
893     let l:data = copy(a:data)
894     call lsp#log_verbose('--->', l:lsp_id, a:server_name, l:data)
895     call lsp#client#send_response(l:lsp_id, a:data)
896 endfunction
897
898 function! s:on_stderr(server_name, id, data, event) abort
899     call lsp#log_verbose('<---(stderr)', a:id, a:server_name, a:data)
900 endfunction
901
902 function! s:on_exit(server_name, id, data, event) abort
903     call lsp#log('s:on_exit', a:id, a:server_name, 'exited', a:data)
904     if has_key(s:servers, a:server_name)
905         let l:server = s:servers[a:server_name]
906         let l:server['lsp_id'] = 0
907         let l:server['buffers'] = {}
908         let l:server['exited'] = 1
909         if has_key(l:server, 'init_result')
910             unlet l:server['init_result']
911         endif
912         let l:server['workspace_folders'] = {}
913         call lsp#stream(1, { 'server': '$vimlsp',
914             \ 'response': { 'method': '$/vimlsp/lsp_server_exit', 'params': { 'server': a:server_name } } })
915         doautocmd <nomodeline> User lsp_server_exit
916     endif
917 endfunction
918
919 function! s:on_notification(server_name, id, data, event) abort
920     call lsp#log_verbose('<---', a:id, a:server_name, a:data)
921     let l:response = a:data['response']
922     let l:server = s:servers[a:server_name]
923     let l:server_info = l:server['server_info']
924
925     let l:stream_data = { 'server': a:server_name, 'response': l:response }
926     if has_key(a:data, 'request')
927         let l:stream_data['request'] = a:data['request']
928     endif
929     call lsp#stream(1, l:stream_data) " notify stream before callbacks
930
931     if !lsp#client#is_server_instantiated_notification(a:data)
932         let l:request = a:data['request']
933         let l:method = l:request['method']
934         if l:method ==# 'initialize'
935             call s:handle_initialize(a:server_name, a:data)
936         endif
937     endif
938
939     for l:callback_info in s:notification_callbacks
940         call l:callback_info.callback(a:server_name, a:data)
941     endfor
942 endfunction
943
944 function! s:on_request(server_name, id, request) abort
945     call lsp#log_verbose('<---', 's:on_request', a:id, a:request)
946
947     let l:stream_data = { 'server': a:server_name, 'request': a:request }
948     call lsp#stream(1, l:stream_data) " notify stream before callbacks
949
950     if a:request['method'] ==# 'workspace/applyEdit'
951         call lsp#utils#workspace_edit#apply_workspace_edit(a:request['params']['edit'])
952         call s:send_response(a:server_name, { 'id': a:request['id'], 'result': { 'applied': v:true } })
953     elseif a:request['method'] ==# 'workspace/configuration'
954         let l:config = lsp#utils#workspace_config#get(a:server_name)
955         let l:response_items = map(a:request['params']['items'], { key, val -> lsp#utils#workspace_config#projection(l:config, val) })
956         call s:send_response(a:server_name, { 'id': a:request['id'], 'result': l:response_items })
957     elseif a:request['method'] ==# 'workspace/workspaceFolders'
958         let l:server_info = s:servers[a:server_name]['server_info']
959         if has_key(l:server_info, 'workspaceFolders')
960             call s:send_response(a:server_name, { 'id': a:request['id'], 'result': l:server_info['workspaceFolders']})
961         endif
962     elseif a:request['method'] ==# 'window/workDoneProgress/create'
963         call s:send_response(a:server_name, { 'id': a:request['id'], 'result': v:null})
964     else
965         " TODO: for now comment this out until we figure out a better solution.
966         " We need to comment this out so that others outside of vim-lsp can
967         " hook into the stream and provide their own response.
968         " " Error returned according to json-rpc specification.
969         " call s:send_response(a:server_name, { 'id': a:request['id'], 'error': { 'code': -32601, 'message': 'Method not found' } })
970     endif
971 endfunction
972
973 function! s:handle_initialize(server_name, data) abort
974     let l:response = a:data['response']
975     let l:server = s:servers[a:server_name]
976
977     if has_key(l:server, 'exited')
978         unlet l:server['exited']
979     endif
980
981     let l:init_callbacks = l:server['init_callbacks']
982     unlet l:server['init_callbacks']
983
984     if !lsp#client#is_error(l:response)
985         let l:server['init_result'] = l:response
986         " Delete cache of trigger chars
987         if has_key(b:, 'lsp_signature_help_trigger_character')
988             unlet b:lsp_signature_help_trigger_character
989         endif
990     else
991         let l:server['failed'] = l:response['error']
992         call lsp#utils#error('Failed to initialize ' . a:server_name . ' with error ' . l:response['error']['code'] . ': ' . l:response['error']['message'])
993     endif
994
995     call s:send_notification(a:server_name, { 'method': 'initialized', 'params': {} })
996
997     for l:Init_callback in l:init_callbacks
998         call l:Init_callback(a:data)
999     endfor
1000
1001     doautocmd <nomodeline> User lsp_server_init
1002 endfunction
1003
1004 function! lsp#get_whitelisted_servers(...) abort
1005     return call(function('lsp#get_allowed_servers'), a:000)
1006 endfunction
1007
1008 " call lsp#get_allowed_servers()
1009 " call lsp#get_allowed_servers(bufnr('%'))
1010 " call lsp#get_allowed_servers('typescript')
1011 function! lsp#get_allowed_servers(...) abort
1012     if a:0 == 0
1013         let l:buffer_filetype = &filetype
1014     else
1015         if type(a:1) == type('')
1016             let l:buffer_filetype = a:1
1017         else
1018             let l:buffer_filetype = getbufvar(a:1, '&filetype')
1019         endif
1020     endif
1021
1022     " TODO: cache active servers per buffer
1023     let l:active_servers = []
1024
1025     for l:server_name in keys(s:servers)
1026         let l:server_info = s:servers[l:server_name]['server_info']
1027         let l:blocked = 0
1028
1029         if has_key(l:server_info, 'blocklist')
1030             let l:blocklistkey = 'blocklist'
1031         else
1032             let l:blocklistkey = 'blacklist'
1033         endif
1034         if has_key(l:server_info, l:blocklistkey)
1035             for l:filetype in l:server_info[l:blocklistkey]
1036                 if l:filetype ==? l:buffer_filetype || l:filetype ==# '*'
1037                     let l:blocked = 1
1038                     break
1039                 endif
1040             endfor
1041         endif
1042
1043         if l:blocked
1044             continue
1045         endif
1046
1047         if has_key(l:server_info, 'allowlist')
1048             let l:allowlistkey = 'allowlist'
1049         else
1050             let l:allowlistkey = 'whitelist'
1051         endif
1052         if has_key(l:server_info, l:allowlistkey)
1053             for l:filetype in l:server_info[l:allowlistkey]
1054                 if l:filetype ==? l:buffer_filetype || l:filetype ==# '*'
1055                     let l:active_servers += [l:server_name]
1056                     break
1057                 endif
1058             endfor
1059         endif
1060     endfor
1061
1062     return l:active_servers
1063 endfunction
1064
1065 function! s:get_text_document_text(buf, server_name) abort
1066     return join(s:get_last_file_content(a:buf, a:server_name), "\n")
1067 endfunction
1068
1069 function! s:get_text_document(buf, server_name, buffer_info) abort
1070     let l:server = s:servers[a:server_name]
1071     let l:server_info = l:server['server_info']
1072     let l:language_id = has_key(l:server_info, 'languageId') ?  l:server_info['languageId'](l:server_info) : getbufvar(a:buf, '&filetype')
1073     return {
1074         \ 'uri': lsp#utils#get_buffer_uri(a:buf),
1075         \ 'languageId': l:language_id,
1076         \ 'version': a:buffer_info['version'],
1077         \ 'text': s:get_text_document_text(a:buf, a:server_name),
1078         \ }
1079 endfunction
1080
1081 function! lsp#get_text_document_identifier(...) abort
1082     let l:buf = a:0 > 0 ? a:1 : bufnr('%')
1083     return { 'uri': lsp#utils#get_buffer_uri(l:buf) }
1084 endfunction
1085
1086 function! lsp#get_position(...) abort
1087     let l:line = line('.')
1088     let l:char = lsp#utils#to_char('%', l:line, col('.'))
1089     return { 'line': l:line - 1, 'character': l:char }
1090 endfunction
1091
1092 function! s:get_text_document_identifier(buf) abort
1093     return { 'uri': lsp#utils#get_buffer_uri(a:buf) }
1094 endfunction
1095
1096 function! s:get_versioned_text_document_identifier(buf, buffer_info) abort
1097     return {
1098         \ 'uri': lsp#utils#get_buffer_uri(a:buf),
1099         \ 'version': a:buffer_info['version'],
1100         \ }
1101 endfunction
1102
1103 " lsp#stream {{{
1104 "
1105 " example 1:
1106 "
1107 " function! s:on_textDocumentDiagnostics(x) abort
1108 "   echom 'Diagnostics for ' . a:x['server'] . ' ' . json_encode(a:x['response'])
1109 " endfunction
1110 "
1111 " au User lsp_setup call lsp#callbag#pipe(
1112 "    \ lsp#stream(),
1113 "    \ lsp#callbag#filter({x-> has_key(x, 'response') && !has_key(x['response'], 'error') && get(x['response'], 'method', '') == 'textDocument/publishDiagnostics'}),
1114 "    \ lsp#callbag#subscribe({ 'next':{x->s:on_textDocumentDiagnostics(x)} }),
1115 "    \ )
1116 "
1117 " example 2:
1118 " call lsp#stream(1, { 'command': 'DocumentFormat' })
1119 function! lsp#stream(...) abort
1120     if a:0 == 0
1121         return lsp#callbag#share(s:Stream)
1122     else
1123         call s:Stream(a:1, a:2)
1124     endif
1125 endfunction
1126 " }}}
1127
1128 " lsp#request {{{
1129 function! lsp#request(server_name, request) abort
1130     let l:ctx = {
1131         \ 'server_name': a:server_name,
1132         \ 'request': copy(a:request),
1133         \ 'request_id': 0,
1134         \ 'done': 0,
1135         \ 'cancelled': 0,
1136         \ }
1137     return lsp#callbag#create(function('s:request_create', [l:ctx]))
1138 endfunction
1139
1140 function! s:request_create(ctx, next, error, complete) abort
1141     let a:ctx['next'] = a:next
1142     let a:ctx['error'] = a:error
1143     let a:ctx['complete'] = a:complete
1144     let a:ctx['bufnr'] = get(a:ctx['request'], 'bufnr', bufnr('%'))
1145     let a:ctx['request']['on_notification'] = function('s:request_on_notification', [a:ctx])
1146     call lsp#utils#step#start([
1147         \ {s->s:ensure_flush(a:ctx['bufnr'], a:ctx['server_name'], s.callback)},
1148         \ {s->s:is_step_error(s) ? s:request_error(a:ctx, s.result[0]) : s:request_send(a:ctx) },
1149         \ ])
1150     return function('s:request_cancel', [a:ctx])
1151 endfunction
1152
1153 function! s:request_send(ctx) abort
1154     if a:ctx['cancelled'] | return | endif " caller already unsubscribed so don't bother sending request
1155     let a:ctx['request_id'] = s:send_request(a:ctx['server_name'], a:ctx['request'])
1156 endfunction
1157
1158 function! s:request_error(ctx, error) abort
1159     if a:ctx['cancelled'] | return | endif " caller already unsubscribed so don't bother notifying
1160     let a:ctx['done'] = 1
1161     call a:ctx['error'](a:error)
1162 endfunction
1163
1164 function! s:request_on_notification(ctx, id, data, event) abort
1165     if a:ctx['cancelled'] | return | endif " caller already unsubscribed so don't bother notifying
1166     let a:ctx['done'] = 1
1167     call a:ctx['next'](extend({ 'server_name': a:ctx['server_name'] }, a:data))
1168     call a:ctx['complete']()
1169 endfunction
1170
1171 function! s:request_cancel(ctx) abort
1172     if a:ctx['cancelled'] | return | endif
1173     let a:ctx['cancelled'] = 1
1174     if a:ctx['request_id'] <= 0 || a:ctx['done'] | return | endif " we have not made the request yet or request is complete, so nothing to cancel
1175     if lsp#get_server_status(a:ctx['server_name']) !=# 'running' | return | endif " if server is not running we cant send the request
1176     " send the actual cancel request
1177     let a:ctx['dispose'] = lsp#callbag#pipe(
1178         \ lsp#notification(a:ctx['server_name'], {
1179         \   'method': '$/cancelRequest',
1180         \   'params': { 'id': a:ctx['request_id'] },
1181         \ }),
1182         \ lsp#callbag#subscribe({
1183         \   'error':{e->s:send_request_dispose(a:ctx)},
1184         \   'complete':{->s:send_request_dispose(a:ctx)},
1185         \ })
1186         \)
1187 endfunction
1188
1189 function! lsp#send_request(server_name, request) abort
1190     let l:ctx = {
1191         \ 'server_name': a:server_name,
1192         \ 'request': copy(a:request),
1193         \ 'cb': has_key(a:request, 'on_notification') ? a:request['on_notification'] : function('s:Noop'),
1194         \ }
1195     let l:ctx['dispose'] = lsp#callbag#pipe(
1196         \ lsp#request(a:server_name, a:request),
1197         \ lsp#callbag#subscribe({
1198         \   'next':{d->l:ctx['cb'](d)},
1199         \   'error':{e->s:send_request_error(l:ctx, e)},
1200         \   'complete':{->s:send_request_dispose(l:ctx)},
1201         \ })
1202         \)
1203 endfunction
1204
1205 function! s:send_request_dispose(ctx) abort
1206     " dispose function may not have been created so check before calling
1207     if has_key(a:ctx, 'dispose')
1208         call a:ctx['dispose']()
1209     endif
1210 endfunction
1211
1212 function! s:send_request_error(ctx, error) abort
1213     call a:ctx['cb'](a:error)
1214     call s:send_request_dispose(a:ctx)
1215 endfunction
1216 " }}}
1217
1218 " lsp#notification {{{
1219 function! lsp#notification(server_name, request) abort
1220     return lsp#callbag#lazy(function('s:send_notification', [a:server_name, a:request]))
1221 endfunction
1222 " }}}
1223
1224 " omnicompletion
1225 function! lsp#complete(...) abort
1226     return call('lsp#omni#complete', a:000)
1227 endfunction
1228
1229 function! lsp#tagfunc(...) abort
1230     return call('lsp#tag#tagfunc', a:000)
1231 endfunction
1232
1233 let s:didchange_queue = []
1234 let s:didchange_timer = -1
1235
1236 function! s:add_didchange_queue(buf) abort
1237     if g:lsp_use_event_queue == 0
1238         for l:server_name in lsp#get_allowed_servers(a:buf)
1239             call s:ensure_flush(a:buf, l:server_name, function('s:Noop'))
1240         endfor
1241         return
1242     endif
1243     if index(s:didchange_queue, a:buf) == -1
1244         call add(s:didchange_queue, a:buf)
1245     endif
1246     call lsp#log('s:send_didchange_queue() will be triggered')
1247     call timer_stop(s:didchange_timer)
1248     let l:lazy = &updatetime > 1000 ? &updatetime : 1000
1249     let s:didchange_timer = timer_start(l:lazy, function('s:send_didchange_queue'))
1250 endfunction
1251
1252 function! s:send_didchange_queue(...) abort
1253     call lsp#log('s:send_event_queue()')
1254     for l:buf in s:didchange_queue
1255         if !bufexists(l:buf)
1256             continue
1257         endif
1258         for l:server_name in lsp#get_allowed_servers(l:buf)
1259             call s:ensure_flush(l:buf, l:server_name, function('s:Noop'))
1260         endfor
1261     endfor
1262     let s:didchange_queue = []
1263 endfunction
1264
1265 function! lsp#enable_diagnostics_for_buffer(...) abort
1266     let l:bufnr = a:0 > 0 ? a:1 : bufnr('%')
1267     call lsp#internal#diagnostics#state#_enable_for_buffer(l:bufnr)
1268 endfunction
1269
1270 function! lsp#disable_diagnostics_for_buffer(...) abort
1271     let l:bufnr = a:0 > 0 ? a:1 : bufnr('%')
1272     call lsp#internal#diagnostics#state#_disable_for_buffer(l:bufnr)
1273 endfunction
1274
1275 " Return dict with diagnostic counts for current buffer
1276 " { 'error': 1, 'warning': 0, 'information': 0, 'hint': 0 }
1277 function! lsp#get_buffer_diagnostics_counts() abort
1278     return lsp#internal#diagnostics#state#_get_diagnostics_count_for_buffer(bufnr('%'))
1279 endfunction
1280
1281 " Return first error line or v:null if there are no errors
1282 function! lsp#get_buffer_first_error_line() abort
1283     return lsp#internal#diagnostics#first_line#get_first_error_line({'bufnr': bufnr('%')})
1284 endfunction
1285
1286 " Return UI list with window/workDoneProgress
1287 " The list is most recently update order.
1288 " [{ 'server': 'clangd', 'token': 'backgroundIndexProgress', 'title': 'indexing', 'messages': '50/100', 'percentage': 50 },
1289 "  { 'server': 'rust-analyzer', 'token': 'rustAnalyzer/indexing', 'title': 'indexing', 'messages': '9/262 (std)', 'percentage': 3 }]
1290 " 'percentage': 0 - 100 or not exist
1291 function! lsp#get_progress() abort
1292     return lsp#internal#work_done_progress#get_progress()
1293 endfunction
1294
1295 function! lsp#document_hover_preview_winid() abort
1296     return lsp#internal#document_hover#under_cursor#getpreviewwinid()
1297 endfunction
1298
1299 "
1300 " Scroll vim-lsp related windows.
1301 "
1302 " NOTE: This method can be used to <expr> mapping.
1303 "
1304 function! lsp#scroll(count) abort
1305     let l:ctx = {}
1306     function! l:ctx.callback(count) abort
1307         let l:Window = vital#lsp#import('VS.Vim.Window')
1308         for l:winid in l:Window.find({ winid -> l:Window.is_floating(winid) })
1309             call l:Window.scroll(l:winid, l:Window.info(l:winid).topline + a:count)
1310         endfor
1311     endfunction
1312     call timer_start(0, { -> l:ctx.callback(a:count) })
1313     return "\<Ignore>"
1314 endfunction
1315
1316 function! s:merge_dict(dict_old, dict_new) abort
1317     for l:key in keys(a:dict_new)
1318         if has_key(a:dict_old, l:key) && type(a:dict_old[l:key]) == v:t_dict && type(a:dict_new[l:key]) == v:t_dict
1319             call s:merge_dict(a:dict_old[l:key], a:dict_new[l:key])
1320         else
1321             let a:dict_old[l:key] = a:dict_new[l:key]
1322         endif
1323     endfor
1324 endfunction
1325
1326 function! lsp#update_workspace_config(server_name, workspace_config) abort
1327     let l:server = s:servers[a:server_name]
1328     let l:server_info = l:server['server_info']
1329     if has_key(l:server_info, 'workspace_config')
1330         if type(l:server_info['workspace_config']) == v:t_func
1331             call lsp#utils#error('''workspace_config'' is a function, so
1332                   \ lsp#update_workspace_config() can not be used.  Either
1333                   \ replace function with a dictionary, or adjust the value
1334                   \ generated by the function as necessary.')
1335             return
1336         endif
1337         call s:merge_dict(l:server_info['workspace_config'], a:workspace_config)
1338     else
1339         let l:server_info['workspace_config'] = a:workspace_config
1340     endif
1341     let l:server_info['_workspace_config_sent'] = v:false
1342     call s:ensure_conf(bufnr('%'), a:server_name, function('s:Noop'))
1343 endfunction
1344
1345 function! lsp#server_complete(lead, line, pos) abort
1346     return filter(sort(keys(s:servers)), 'stridx(v:val, a:lead)==0 && has_key(s:servers[v:val], "init_result")')
1347 endfunction
1348
1349 function! lsp#server_complete_running(lead, line, pos) abort
1350     let l:all_servers = sort(keys(s:servers))
1351     return filter(l:all_servers, {idx, name ->
1352         \ stridx(name, a:lead) == 0 && lsp#is_server_running(name)
1353         \ })
1354 endfunction
1355
1356 function! lsp#_new_command() abort
1357     let s:last_command_id += 1
1358     call lsp#stream(1, { 'command': 1 })
1359     return s:last_command_id
1360 endfunction
1361
1362 function! lsp#_last_command() abort
1363     return s:last_command_id
1364 endfunction