[WIP] Popup documentation (#507)

* Show documentation in Vim popup

* Use timer

* Reuse logic from output.vim

* Change default text

* Rename functions

* Refactor

* Use v:event

* Add comment

* Remove log

* Refactor

* Implement documentation in Neovim

* Cleanup Neovim popup

* Let Neovim float take all available space

* Extract get_size_info

* Reuse sizing logic from output.vim

* Fix Neovim positioning being reset

* Update autoload/lsp/ui/vim/documentation.vim

Co-Authored-By: Christian Clason <christian.clason@uni-due.de>

* Make vint happy

* Retrigger Travis CI

* add g:lsp_documentation_float flag

* fix lint issues

Co-authored-by: Christian Clason <christian.clason@uni-due.de>
Co-authored-by: Prabir Shrestha <mail@prabir.me>
This commit is contained in:
Thomas Faingnaert
2020-06-20 21:21:25 +02:00
committed by GitHub
parent b39f18ab8c
commit a3673dde78
5 changed files with 132 additions and 36 deletions

View File

@@ -802,6 +802,8 @@ function! s:handle_initialize(server_name, data) abort
call l:Init_callback(a:data)
endfor
call lsp#ui#vim#documentation#setup()
doautocmd User lsp_server_init
endfunction

View File

@@ -0,0 +1,76 @@
let s:use_vim_popup = has('patch-8.1.1517') && !has('nvim')
let s:use_nvim_float = exists('*nvim_open_win') && has('nvim')
let s:last_popup_id = -1
function! s:complete_done() abort
if !g:lsp_documentation_float | return | endif
" Use a timer to avoid textlock (see :h textlock).
let l:event = deepcopy(v:event)
call timer_start(0, {-> s:show_documentation(l:event)})
endfunction
function! s:show_documentation(event) abort
call s:close_popup()
if !has_key(a:event['completed_item'], 'info') || empty(a:event['completed_item']['info'])
return
endif
let l:right = wincol() < winwidth(0) / 2
" TODO: Neovim
if l:right
let l:line = a:event['row'] + 1
let l:col = a:event['col'] + a:event['width'] + 1 + (a:event['scrollbar'] ? 1 : 0)
else
let l:line = a:event['row'] + 1
let l:col = a:event['col'] - 1
endif
" TODO: Support markdown
let l:data = split(a:event['completed_item']['info'], '\n')
let l:lines = []
let l:syntax_lines = []
let l:ft = lsp#ui#vim#output#append(l:data, l:lines, l:syntax_lines)
let l:current_win_id = win_getid()
if s:use_vim_popup
let s:last_popup_id = popup_create('(no documentation available)', {'line': l:line, 'col': l:col, 'pos': l:right ? 'topleft' : 'topright', 'padding': [0, 1, 0, 1]})
elseif s:use_nvim_float
let l:height = winheight(0) - l:line + 1
let l:width = l:right ? winwidth(0) - l:col + 1 : l:col
let s:last_popup_id = lsp#ui#vim#output#floatingpreview([])
call nvim_win_set_config(s:last_popup_id, {'relative': 'win', 'anchor': l:right ? 'NW' : 'NE', 'row': l:line - 1, 'col': l:col - 1, 'height': l:height, 'width': l:width})
endif
call setbufvar(winbufnr(s:last_popup_id), 'lsp_syntax_highlights', l:syntax_lines)
call setbufvar(winbufnr(s:last_popup_id), 'lsp_do_conceal', 1)
call lsp#ui#vim#output#setcontent(s:last_popup_id, l:lines, l:ft)
let [l:bufferlines, l:maxwidth] = lsp#ui#vim#output#get_size_info()
call win_gotoid(l:current_win_id)
if s:use_nvim_float
call lsp#ui#vim#output#adjust_float_placement(l:bufferlines, l:maxwidth)
call nvim_win_set_config(s:last_popup_id, {'relative': 'win', 'row': l:line - 1, 'col': l:col - 1})
endif
endfunction
function! s:close_popup() abort
if s:last_popup_id >= 0
if s:use_vim_popup | call popup_close(s:last_popup_id) | endif
if s:use_nvim_float | call nvim_win_close(s:last_popup_id, 1) | endif
let s:last_popup_id = -1
endif
endfunction
function! lsp#ui#vim#documentation#setup() abort
augroup lsp_documentation_popup
autocmd!
autocmd CompleteChanged * call s:complete_done()
autocmd CompleteDone * call s:close_popup()
augroup end
endfunction

View File

@@ -149,10 +149,10 @@ function! lsp#ui#vim#output#floatingpreview(data) abort
return s:winid
endfunction
function! s:setcontent(lines, ft) abort
function! lsp#ui#vim#output#setcontent(winid, lines, ft) abort
if s:use_vim_popup
" vim popup
call setbufline(winbufnr(s:winid), 1, a:lines)
call setbufline(winbufnr(a:winid), 1, a:lines)
call win_execute(s:winid, 'setlocal filetype=' . a:ft . '.lsp-hover')
else
" nvim floating or preview
@@ -162,7 +162,7 @@ function! s:setcontent(lines, ft) abort
endif
endfunction
function! s:adjust_float_placement(bufferlines, maxwidth) abort
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])
@@ -279,6 +279,27 @@ function! s:align_preview(options) abort
endif
endfunction
function! lsp#ui#vim#output#get_size_info() abort
" Get size information while still having the buffer active
let l:maxwidth = max(map(getline(1, '$'), 'strdisplaywidth(v:val)'))
if g:lsp_preview_max_width > 0
let l:bufferlines = 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 getline(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
let l:bufferlines = line('$')
endif
return [l:bufferlines, l:maxwidth]
return [l:bufferlines, l:maxwidth]
endfunction
function! lsp#ui#vim#output#float_supported() abort
return s:use_vim_popup || s:use_nvim_float
endfunction
@@ -307,7 +328,7 @@ function! lsp#ui#vim#output#preview(server, data, options) abort
let s:preview_data = a:data
let l:lines = []
let l:syntax_lines = []
let l:ft = s:append(a:data, l:lines, l:syntax_lines)
let l:ft = lsp#ui#vim#output#append(a:data, l:lines, l:syntax_lines)
" If the server response is empty content, we don't display anything.
if empty(l:lines) && empty(l:syntax_lines)
@@ -328,23 +349,9 @@ function! lsp#ui#vim#output#preview(server, data, options) abort
call setbufvar(winbufnr(s:winid), 'lsp_syntax_highlights', l:syntax_lines)
call setbufvar(winbufnr(s:winid), 'lsp_do_conceal', l:do_conceal)
call s:setcontent(l:lines, l:ft)
call lsp#ui#vim#output#setcontent(s:winid, l:lines, l:ft)
" Get size information while still having the buffer active
let l:maxwidth = max(map(getline(1, '$'), 'strdisplaywidth(v:val)'))
if g:lsp_preview_max_width > 0
let l:bufferlines = 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 getline(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
let l:bufferlines = line('$')
endif
let [l:bufferlines, l:maxwidth] = lsp#ui#vim#output#get_size_info()
if s:use_preview
" Set statusline
@@ -361,17 +368,16 @@ function! lsp#ui#vim#output#preview(server, data, options) abort
echo ''
if s:winid && (s:use_vim_popup || s:use_nvim_float)
if s:use_nvim_float
" Neovim floats
call s: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 User lsp_float_opened
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 User lsp_float_opened
endif
if !g:lsp_preview_keep_focus
@@ -385,10 +391,10 @@ function! s:escape_string_for_display(str) abort
return substitute(substitute(a:str, '\r\n', '\n', 'g'), '\r', '\n', 'g')
endfunction
function! s:append(data, lines, syntax_lines) abort
if type(a:data) ==# type([])
function! lsp#ui#vim#output#append(data, lines, syntax_lines) abort
if type(a:data) == type([])
for l:entry in a:data
call s:append(l:entry, a:lines, a:syntax_lines)
call lsp#ui#vim#output#append(l:entry, a:lines, a:syntax_lines)
endfor
return 'markdown'

View File

@@ -20,10 +20,11 @@ CONTENTS *vim-lsp-contents*
g:lsp_preview_doubletap |g:lsp_preview_doubletap|
g:lsp_insert_text_enabled |g:lsp_insert_text_enabled|
g:lsp_text_edit_enabled |g:lsp_text_edit_enabled|
g:lsp_documentation_float |g:lsp_documentation_float|
g:lsp_diagnostics_echo_cursor |g:lsp_diagnostics_echo_cursor|
g:lsp_diagnostics_echo_delay |g:lsp_diagnostics_echo_delay|
g:lsp_diagnostics_float_cursor |g:lsp_diagnostics_float_cursor|
g:lsp_diagnostics_float_delay |g:lsp_diagnostics_float_delay|
g:lsp_diagnostics_float_cursor |g:lsp_diagnostics_float_cursor|
g:lsp_diagnostics_float_delay |g:lsp_diagnostics_float_delay|
g:lsp_signs_enabled |g:lsp_signs_enabled|
g:lsp_signs_priority |g:lsp_signs_priority|
g:lsp_signs_priority_map |g:lsp_signs_priority_map|
@@ -368,6 +369,16 @@ g:lsp_text_edit_enabled *g:lsp_text_edit_enabled*
let g:lsp_text_edit_enabled = 1
let g:lsp_text_edit_enabled = 0
g:lsp_documentation_float *g:lsp_documentation_float*
Type: |Number|
Default: `1`
Enables floating window documentation for complete items.
Example: >
let g:lsp_documentation_float = 1
let g:lsp_documentation_float = 0
g:lsp_diagnostics_echo_cursor *g:lsp_diagnostics_echo_cursor*
Type: |Number|
Default: `0`

View File

@@ -21,6 +21,7 @@ let g:lsp_signs_information = get(g:, 'lsp_signs_information', {})
let g:lsp_signs_hint = get(g:, 'lsp_signs_hint', {})
let g:lsp_signs_priority = get(g:, 'lsp_signs_priority', 10)
let g:lsp_signs_priority_map = get(g:, 'lsp_signs_priority_map', {})
let g:lsp_documentation_float = get(g:, 'lsp_documentation_float', 1)
let g:lsp_diagnostics_enabled = get(g:, 'lsp_diagnostics_enabled', 1)
let g:lsp_diagnostics_echo_cursor = get(g:, 'lsp_diagnostics_echo_cursor', 0)
let g:lsp_diagnostics_echo_delay = get(g:, 'lsp_diagnostics_echo_delay', 500)