mirror of
https://github.com/prabirshrestha/vim-lsp.git
synced 2025-12-14 20:35:59 +01:00
Add public api get window/workDoneProgress (#979)
* impl lsp#get_progress() for statusline plugin. * add workDoneProgress spec link * refactor s:handle_work_done_progress() * [workDoneProgress] prevent to subscribe multiple times * [workDoneProgress] Fixed s:lsp_progress['percentage'] to always be float * [workDoneProgress] support multiple progress registration. * [workDoneProgress] Fixed s:lsp_progress['percentage'] to uinteger * [workDoneProgress] fix for vint * [workDoneProgress] rename variable * [workDoneProgress] add test * [workDoneProgress] write document * [workDoneProgress] initialize s:progress_ui when enable/disable * [workDoneProgress] refactor test code * [workDoneProgress] add lsp_progress_updated * [workDoneProgress] fix typo * [workDoneProgress] refactor token handling * [workDoneProgress] Fixed differences from specifications(messages->message) * [workDoneProgress] fix test
This commit is contained in:
@@ -31,6 +31,7 @@ augroup _lsp_silent_
|
||||
autocmd User lsp_float_closed silent
|
||||
autocmd User lsp_buffer_enabled silent
|
||||
autocmd User lsp_diagnostics_updated silent
|
||||
autocmd User lsp_progress_updated silent
|
||||
augroup END
|
||||
|
||||
function! lsp#log_verbose(...) abort
|
||||
@@ -65,6 +66,7 @@ function! lsp#enable() abort
|
||||
call lsp#internal#document_highlight#_enable()
|
||||
call lsp#internal#diagnostics#_enable()
|
||||
call lsp#internal#show_message_request#_enable()
|
||||
call lsp#internal#work_done_progress#_enable()
|
||||
call s:register_events()
|
||||
endfunction
|
||||
|
||||
@@ -79,6 +81,7 @@ function! lsp#disable() abort
|
||||
call lsp#internal#document_highlight#_disable()
|
||||
call lsp#internal#diagnostics#_disable()
|
||||
call lsp#internal#show_message_request#_disable()
|
||||
call lsp#internal#work_done_progress#_disable()
|
||||
call s:unregister_events()
|
||||
let s:enabled = 0
|
||||
endfunction
|
||||
@@ -1132,6 +1135,15 @@ function! lsp#get_buffer_first_error_line() abort
|
||||
return lsp#ui#vim#diagnostics#get_buffer_first_error_line()
|
||||
endfunction
|
||||
|
||||
" Return UI list with window/workDoneProgress
|
||||
" The list is most recently update order.
|
||||
" [{ 'server': 'clangd', 'token': 'backgroundIndexProgress', 'title': 'indexing', 'messages': '50/100', 'percentage': 50 },
|
||||
" { 'server': 'rust-analyzer', 'token': 'rustAnalyzer/indexing', 'title': 'indexing', 'messages': '9/262 (std)', 'percentage': 3 }]
|
||||
" 'percentage': 0 - 100 or not exist
|
||||
function! lsp#get_progress() abort
|
||||
return lsp#internal#work_done_progress#get_progress()
|
||||
endfunction
|
||||
|
||||
function! s:merge_dict(dict_old, dict_new) abort
|
||||
for l:key in keys(a:dict_new)
|
||||
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
|
||||
|
||||
70
autoload/lsp/internal/work_done_progress.vim
Normal file
70
autoload/lsp/internal/work_done_progress.vim
Normal file
@@ -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
|
||||
@@ -77,6 +77,7 @@ CONTENTS *vim-lsp-contents*
|
||||
|lsp#utils#find_nearest_parent_file_directory()|
|
||||
lsp#get_buffer_diagnostics_counts() |lsp#get_buffer_diagnostics_counts()|
|
||||
lsp#get_buffer_first_error_line() |lsp#get_buffer_first_error_line()|
|
||||
lsp#get_progress() |lsp#get_progress()|
|
||||
Commands |vim-lsp-commands|
|
||||
LspCodeAction |:LspCodeAction|
|
||||
LspCodeActionSync |:LspCodeActionSync|
|
||||
@@ -125,6 +126,7 @@ CONTENTS *vim-lsp-contents*
|
||||
lsp_server_exit |lsp_server_exit|
|
||||
lsp_buffer_enabled |lsp_buffer_enabled|
|
||||
lsp_diagnostics_updated |lsp_diagnostics_updated|
|
||||
lsp_progress_updated |lsp_progress_updated|
|
||||
Mappings |vim-lsp-mappings|
|
||||
<plug>(lsp-preview-close) |<plug>(lsp-preview-close)|
|
||||
<plug>(lsp-preview-focus) |<plug>(lsp-preview-focus)|
|
||||
@@ -1273,6 +1275,23 @@ Get line number of first error in current buffer.
|
||||
|
||||
Returns |Number| or |v:null| if there are no errors.
|
||||
|
||||
lsp#get_progress() *lsp#get_progress()*
|
||||
|
||||
Return UI |List| of |Dict| with window/workDoneProgress
|
||||
The |List| is most recently update order.
|
||||
The |Dict| has keys as follows.
|
||||
* server
|
||||
Type: |String|
|
||||
* token
|
||||
Type: |String|
|
||||
* title
|
||||
Type: |String|
|
||||
* messages
|
||||
Type: |String|
|
||||
* percentage
|
||||
Type: |Number|
|
||||
0 - 100 or not exist
|
||||
|
||||
==============================================================================
|
||||
Commands *vim-lsp-commands*
|
||||
|
||||
@@ -1557,6 +1576,10 @@ processed by vim-lsp.
|
||||
autocmd User lsp_diagnostics_updated call DoSomething()
|
||||
augroup END
|
||||
<
|
||||
lsp_progress_updated *lsp_progress_updated*
|
||||
|
||||
This autocommand is run after every time after progress updated and
|
||||
processed by vim-lsp. Used for statusline plugins.
|
||||
|
||||
==============================================================================
|
||||
Mappings *vim-lsp-mappings*
|
||||
|
||||
92
test/lsp/utils/work_done_progress.vimspec
Normal file
92
test/lsp/utils/work_done_progress.vimspec
Normal file
@@ -0,0 +1,92 @@
|
||||
Describe lsp#internal#work_done_progress
|
||||
Before each
|
||||
let g:lsp_work_done_progress_enabled = 1
|
||||
call lsp#internal#work_done_progress#_enable()
|
||||
End
|
||||
|
||||
After each
|
||||
let g:lsp_work_done_progress_enabled = 0
|
||||
call lsp#internal#work_done_progress#_disable()
|
||||
End
|
||||
|
||||
It should be able to subscribe to $progress stream
|
||||
let l:server1_response1 = {'method': '$/progress', 'params':{'token':'token text','value':{'kind':'begin', 'title':'title'}}}
|
||||
let l:server1_response2 = {'method': '$/progress', 'params':{'token':'token text','value':{'percentage':50,'message':'test message','kind':'report'}}}
|
||||
let l:server1_response3 = {'method': '$/progress', 'params':{'token':'token text','value':{'kind':'end'}}}
|
||||
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(), [])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server1', 'response': l:server1_response1 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(),
|
||||
\ [{'message': '', 'token': 'token text', 'title': 'title', 'server': 'server1'}])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server1', 'response': l:server1_response2 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(),
|
||||
\ [{'message': 'test message', 'token': 'token text', 'percentage': 50, 'title': 'title', 'server': 'server1'}])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server1', 'response': l:server1_response3 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(), [])
|
||||
End
|
||||
|
||||
It should be able to subscribe to multi $progress stream
|
||||
let l:server1_response1 = {'method': '$/progress', 'params':{'token':'token text','value':{'kind':'begin', 'title':'title1'}}}
|
||||
let l:server1_response2 = {'method': '$/progress', 'params':{'token':'token text','value':{'percentage':50,'message':'msg1','kind':'report'}}}
|
||||
let l:server1_response3 = {'method': '$/progress', 'params':{'token':'token text','value':{'percentage':90,'message':'msg1','kind':'report'}}}
|
||||
let l:server1_response4 = {'method': '$/progress', 'params':{'token':'token text','value':{'kind':'end'}}}
|
||||
let l:server2_response1 = {'method': '$/progress', 'params':{'token':'server2_token','value':{'kind':'begin', 'title':'title2'}}}
|
||||
let l:server2_response2 = {'method': '$/progress', 'params':{'token':'server2_token','value':{'percentage':0,'message':'msg2','kind':'report'}}}
|
||||
let l:server2_response3 = {'method': '$/progress', 'params':{'token':'server2_token','value':{'kind':'end'}}}
|
||||
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(), [])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server1', 'response': l:server1_response1 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(),
|
||||
\ [{'message': '', 'token': 'token text', 'title': 'title1', 'server': 'server1'}])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server2', 'response': l:server2_response1 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(),
|
||||
\ [{'message': '', 'token': 'server2_token', 'title': 'title2', 'server': 'server2'},
|
||||
\ {'message': '', 'token': 'token text', 'title': 'title1', 'server': 'server1'}])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server2', 'response': l:server2_response2 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(),
|
||||
\ [{'message': 'msg2', 'token': 'server2_token', 'percentage':0, 'title': 'title2', 'server': 'server2'},
|
||||
\ {'message': '', 'token': 'token text', 'title': 'title1', 'server': 'server1'}])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server1', 'response': l:server1_response2 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(),
|
||||
\ [{'message': 'msg1', 'token': 'token text', 'percentage':50, 'title': 'title1', 'server': 'server1'},
|
||||
\ {'message': 'msg2', 'token': 'server2_token', 'percentage':0, 'title': 'title2', 'server': 'server2'}])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server1', 'response': l:server1_response3 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(),
|
||||
\ [{'message': 'msg1', 'token': 'token text', 'percentage':90, 'title': 'title1', 'server': 'server1'},
|
||||
\ {'message': 'msg2', 'token': 'server2_token', 'percentage':0, 'title': 'title2', 'server': 'server2'}])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server1', 'response': l:server1_response4 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(),
|
||||
\ [{'message': 'msg2', 'token': 'server2_token', 'percentage':0, 'title': 'title2', 'server': 'server2'}])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server2', 'response': l:server2_response3 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(), [])
|
||||
End
|
||||
|
||||
It should be returned correctly even if percentage and message do not exist.
|
||||
let l:server1_response1 = {'method': '$/progress', 'params':{'token':'token text','value':{'kind':'begin', 'title':'title'}}}
|
||||
let l:server1_response2 = {'method': '$/progress', 'params':{'token':'token text','value':{'kind':'report'}}}
|
||||
let l:server1_response3 = {'method': '$/progress', 'params':{'token':'token text','value':{'kind':'end'}}}
|
||||
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(), [])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server1', 'response': l:server1_response1 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(),
|
||||
\ [{'message': '', 'token': 'token text', 'title': 'title', 'server': 'server1'}])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server1', 'response': l:server1_response2 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(),
|
||||
\ [{'message': '', 'token': 'token text', 'title': 'title', 'server': 'server1'}])
|
||||
|
||||
call lsp#stream(1, { 'server': 'server1', 'response': l:server1_response3 })
|
||||
Assert Equals(lsp#internal#work_done_progress#get_progress(), [])
|
||||
End
|
||||
End
|
||||
Reference in New Issue
Block a user