mirror of
https://github.com/prabirshrestha/vim-lsp.git
synced 2025-12-14 20:35:59 +01:00
preview information shown in hover at the cursor (#395)
* preview information shown in hover at the cursor * lint * Fix hover float positioning * Integrate vim8.1 popup for hover/preview * lint * Fix hover float positioning above cursor line * Closing and focusing floating preview (nvim) * Renamed variable of preview window id * autocmd events for open/close floats * Closing of floating preview & lightlinefix (vim8.1) * Added doubletap functionality to preview
This commit is contained in:
committed by
Prabir Shrestha
parent
e0f832443b
commit
1790680598
@@ -26,6 +26,8 @@ augroup _lsp_silent_
|
||||
autocmd User lsp_server_init silent
|
||||
autocmd User lsp_server_exit silent
|
||||
autocmd User lsp_complete_done silent
|
||||
autocmd User lsp_float_opened silent
|
||||
autocmd User lsp_float_closed silent
|
||||
augroup END
|
||||
|
||||
function! lsp#log_verbose(...) abort
|
||||
|
||||
@@ -1,18 +1,206 @@
|
||||
let s:supports_floating = exists('*nvim_open_win') || has('patch-8.1.1517')
|
||||
let s:winid = v:false
|
||||
let s:prevwin = v:false
|
||||
let s:preview_data = v:false
|
||||
|
||||
function! lsp#ui#vim#output#closepreview() abort
|
||||
if win_getid() == s:winid
|
||||
" Don't close if window got focus
|
||||
return
|
||||
endif
|
||||
"closing floats in vim8.1 must use popup_close() (nvim could use nvim_win_close but pclose
|
||||
"works)
|
||||
if s:supports_floating && s:winid && g:lsp_preview_float && !has('nvim')
|
||||
call popup_close(s:winid)
|
||||
else
|
||||
pclose
|
||||
endif
|
||||
let s:winid = v:false
|
||||
let s:preview_data = v:false
|
||||
augroup lsp_float_preview_close
|
||||
augroup end
|
||||
autocmd! lsp_float_preview_close CursorMoved,CursorMovedI,VimResized *
|
||||
doautocmd User lsp_float_closed
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#focuspreview() abort
|
||||
" This does not work for vim8.1 popup but will work for nvim and old preview
|
||||
if s:winid
|
||||
if win_getid() != s:winid
|
||||
let s:prevwin = win_getid()
|
||||
call win_gotoid(s:winid)
|
||||
elseif s:prevwin
|
||||
" Temporarily disable hooks
|
||||
" TODO: remove this when closing logic is able to distinguish different move directions
|
||||
autocmd! lsp_float_preview_close CursorMoved,CursorMovedI,VimResized *
|
||||
call win_gotoid(s:prevwin)
|
||||
call s:add_float_closing_hooks()
|
||||
let s:prevwin = v:false
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:bufwidth() abort
|
||||
let width = winwidth(0)
|
||||
let numberwidth = max([&numberwidth, strlen(line('$'))+1])
|
||||
let numwidth = (&number || &relativenumber)? numberwidth : 0
|
||||
let foldwidth = &foldcolumn
|
||||
|
||||
if &signcolumn ==? 'yes'
|
||||
let signwidth = 2
|
||||
elseif &signcolumn ==? 'auto'
|
||||
let signs = execute(printf('sign place buffer=%d', bufnr('')))
|
||||
let signs = split(signs, "\n")
|
||||
let signwidth = len(signs)>2? 2: 0
|
||||
else
|
||||
let signwidth = 0
|
||||
endif
|
||||
return width - numwidth - foldwidth - signwidth
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:get_float_positioning(height, width) abort
|
||||
let l:height = a:height
|
||||
let l:width = a:width
|
||||
" For a start show it below/above the cursor
|
||||
" TODO: add option to configure it 'docked' at the bottom/top/right
|
||||
let l:y = winline()
|
||||
if l:y + l:height >= winheight(0)
|
||||
" Float does not fit
|
||||
if l:y - 2 > l:height
|
||||
" Fits above
|
||||
let l:y = winline() - l:height -1
|
||||
elseif l:y - 2 > winheight(0) - l:y
|
||||
" Take space above cursor
|
||||
let l:y = 1
|
||||
let l:height = winline()-2
|
||||
else
|
||||
" Take space below cursor
|
||||
let l:height = winheight(0) -l:y
|
||||
endif
|
||||
endif
|
||||
let l:col = col('.')
|
||||
" Positioning is not window but screen relative
|
||||
let l:opts = {
|
||||
\ 'relative': 'win',
|
||||
\ 'row': l:y,
|
||||
\ 'col': l:col,
|
||||
\ 'width': l:width,
|
||||
\ 'height': l:height,
|
||||
\ }
|
||||
return l:opts
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#floatingpreview(data) abort
|
||||
if has('nvim')
|
||||
let l:buf = nvim_create_buf(v:false, v:true)
|
||||
call setbufvar(l:buf, '&signcolumn', 'no')
|
||||
|
||||
" Try to get as much pace right-bolow the cursor, but at least 10x10
|
||||
let l:width = max([s:bufwidth(), 10])
|
||||
let l:height = max([&lines - winline() + 1, 10])
|
||||
|
||||
let l:opts = s:get_float_positioning(l:height, l:width)
|
||||
|
||||
let s:winid = nvim_open_win(buf, v:true, l:opts)
|
||||
call nvim_win_set_option(s:winid, 'winhl', 'Normal:Pmenu,NormalNC:Pmenu')
|
||||
call nvim_win_set_option(s:winid, 'foldenable', v:false)
|
||||
call nvim_win_set_option(s:winid, 'wrap', v:true)
|
||||
call nvim_win_set_option(s:winid, 'statusline', '')
|
||||
call nvim_win_set_option(s:winid, 'number', v:false)
|
||||
call nvim_win_set_option(s:winid, 'relativenumber', v:false)
|
||||
call nvim_win_set_option(s:winid, 'cursorline', v:false)
|
||||
" Enable closing the preview with esc, but map only in the scratch buffer
|
||||
nmap <buffer><silent> <esc> :pclose<cr>
|
||||
else
|
||||
let s:winid = popup_atcursor('...', {
|
||||
\ 'moved': 'any',
|
||||
\ 'border': [1, 1, 1, 1],
|
||||
\})
|
||||
endif
|
||||
return s:winid
|
||||
endfunction
|
||||
|
||||
function! s:setcontent(lines, ft) abort
|
||||
if s:supports_floating && g:lsp_preview_float && !has('nvim')
|
||||
" vim popup
|
||||
call setbufline(winbufnr(s:winid), 1, a:lines)
|
||||
let l:lightline_toggle = v:false
|
||||
if exists('#lightline') && !has('nvim')
|
||||
" Lightline does not work in popups but does not recognize it yet.
|
||||
" It is ugly to have an check for an other plugin here, better fix lightline...
|
||||
let l:lightline_toggle = v:true
|
||||
call lightline#disable()
|
||||
endif
|
||||
call win_execute(s:winid, 'setlocal filetype=' . a:ft . '.lsp-hover')
|
||||
if l:lightline_toggle
|
||||
call lightline#enable()
|
||||
endif
|
||||
else
|
||||
" nvim floating
|
||||
call setline(1, a:lines)
|
||||
setlocal readonly nomodifiable
|
||||
let &l:filetype = a:ft . '.lsp-hover'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:adjust_float_placement(bufferlines, maxwidth) abort
|
||||
if has('nvim')
|
||||
let l:win_config = {}
|
||||
let l:height = min([winheight(s:winid), a:bufferlines])
|
||||
let l:width = min([winwidth(s:winid), a:maxwidth])
|
||||
let l:win_config = s:get_float_positioning(l:height, l:width)
|
||||
call nvim_win_set_config(s:winid, l:win_config )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:add_float_closing_hooks() abort
|
||||
if g:lsp_preview_autoclose
|
||||
augroup lsp_float_preview_close
|
||||
autocmd! lsp_float_preview_close CursorMoved,CursorMovedI,VimResized *
|
||||
autocmd CursorMoved,CursorMovedI,VimResized * call lsp#ui#vim#output#closepreview()
|
||||
augroup END
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#getpreviewwinid() abort
|
||||
return s:winid
|
||||
endfunction
|
||||
|
||||
function! s:open_preview(data) abort
|
||||
if s:supports_floating && g:lsp_preview_float
|
||||
let l:winid = lsp#ui#vim#output#floatingpreview(a:data)
|
||||
else
|
||||
execute &previewheight.'new'
|
||||
let l:winid = win_getid()
|
||||
endif
|
||||
return l:winid
|
||||
endfunction
|
||||
|
||||
function! lsp#ui#vim#output#preview(data) abort
|
||||
if s:winid && type(s:preview_data) == type(a:data)
|
||||
\ && s:preview_data == a:data
|
||||
\ && type(g:lsp_preview_doubletap) == 3
|
||||
\ && len(g:lsp_preview_doubletap) >= 1
|
||||
\ && type(g:lsp_preview_doubletap[0]) == 2
|
||||
echo ''
|
||||
return call(g:lsp_preview_doubletap[0], [])
|
||||
endif
|
||||
" Close any previously opened preview window
|
||||
pclose
|
||||
|
||||
let l:current_window_id = win_getid()
|
||||
|
||||
execute &previewheight.'new'
|
||||
let s:winid = s:open_preview(a:data)
|
||||
|
||||
let l:ft = s:append(a:data)
|
||||
" Delete first empty line
|
||||
0delete _
|
||||
let s:preview_data = a:data
|
||||
let l:lines = []
|
||||
let l:ft = s:append(a:data, l:lines)
|
||||
call s:setcontent(l:lines, l:ft)
|
||||
|
||||
setlocal readonly nomodifiable
|
||||
|
||||
let &l:filetype = l:ft . '.lsp-hover'
|
||||
" Get size information while still having the buffer active
|
||||
let l:bufferlines = line('$')
|
||||
let l:maxwidth = max(map(getline(1, '$'), 'strdisplaywidth(v:val)'))
|
||||
|
||||
if g:lsp_preview_keep_focus
|
||||
" restore focus to the previous window
|
||||
@@ -21,28 +209,35 @@ function! lsp#ui#vim#output#preview(data) abort
|
||||
|
||||
echo ''
|
||||
|
||||
if s:supports_floating && s:winid && g:lsp_preview_float
|
||||
if has('nvim')
|
||||
call s:adjust_float_placement(l:bufferlines, l:maxwidth)
|
||||
call s:add_float_closing_hooks()
|
||||
endif
|
||||
doautocmd User lsp_float_opened
|
||||
endif
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! s:append(data) abort
|
||||
function! s:append(data, lines) abort
|
||||
if type(a:data) == type([])
|
||||
for l:entry in a:data
|
||||
call s:append(entry)
|
||||
call s:append(entry, a:lines)
|
||||
endfor
|
||||
|
||||
return 'markdown'
|
||||
elseif type(a:data) == type('')
|
||||
silent put =a:data
|
||||
call extend(a:lines, split(a:data, "\n"))
|
||||
|
||||
return 'markdown'
|
||||
elseif type(a:data) == type({}) && has_key(a:data, 'language')
|
||||
silent put ='```'.a:data.language
|
||||
silent put =a:data.value
|
||||
silent put ='```'
|
||||
call add(a:lines, '```'.a:data.language)
|
||||
call extend(a:lines, split(a:data.value, '\n'))
|
||||
call add(a:lines, '```')
|
||||
|
||||
return 'markdown'
|
||||
elseif type(a:data) == type({}) && has_key(a:data, 'kind')
|
||||
silent put =a:data.value
|
||||
call add(a:lines, a:data.value)
|
||||
|
||||
return a:data.kind ==? 'plaintext' ? 'text' : a:data.kind
|
||||
endif
|
||||
|
||||
@@ -13,6 +13,9 @@ CONTENTS *vim-lsp-contents*
|
||||
g:lsp_diagnostics_enabled |g:lsp_diagnostics_enabled|
|
||||
g:lsp_auto_enable |g:lsp_auto_enable|
|
||||
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|
|
||||
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_diagnostics_echo_cursor |g:lsp_diagnostics_echo_cursor|
|
||||
@@ -53,6 +56,9 @@ CONTENTS *vim-lsp-contents*
|
||||
Autocommands |vim-lsp-autocommands|
|
||||
lsp_complete_done |lsp_complete_done|
|
||||
Mappings |vim-lsp-mappings|
|
||||
<plug>(lsp-preview-close) |<plug>(lsp-preview-close)|
|
||||
<plug>(lsp-preview-focus) |<plug>(lsp-preview-focus)|
|
||||
|
||||
Autocomplete |vim-lsp-autocomplete|
|
||||
omnifunc |vim-lsp-omnifunc|
|
||||
asyncomplete.vim |vim-lsp-asyncomplete|
|
||||
@@ -154,6 +160,68 @@ g:lsp_preview_keep_focus *g:lsp_preview_keep_focus*
|
||||
|
||||
* |preview-window| can be closed using the default vim mapping - `<c-w><c-z>`.
|
||||
|
||||
g:lsp_preview_float *g:lsp_preview_float*
|
||||
Type: |Number|
|
||||
Default: `1`
|
||||
|
||||
If set and nvim_win_open() or popup_create is available, hover information
|
||||
are shown in a floating window as |preview-window| at the cursor position.
|
||||
The |preview-window| is closed automatically on cursor moves, unless it is
|
||||
focused. While focused it may be closed with <esc>.
|
||||
After opening an autocmd User event lsp_float_opened is issued, as well as
|
||||
and lsp_float_closed upon closing. This can be used to alter the preview
|
||||
window (using lsp#ui#vim#output#getpreviewwinid() to get the window id), or
|
||||
setup custom bindings while a preview is open.
|
||||
This feature requires neovim 0.4.0 (current master) or
|
||||
Vim8.1 with has('patch-8.1.1517').
|
||||
|
||||
Example:
|
||||
" Opens preview windows as floating
|
||||
let g:lsp_preview_float = 1
|
||||
|
||||
" Opens preview windows as normal windows
|
||||
let g:lsp_preview_float = 0
|
||||
|
||||
" Close preview window with <esc>
|
||||
autocmd User lsp_float_opened nmap <buffer> <silent> <esc>
|
||||
\ <Plug>(lsp-preview-close)
|
||||
autocmd User lsp_float_closed nunmap <buffer> <esc>
|
||||
|
||||
g:lsp_preview_autoclose *g:lsp_preview_autoclose*
|
||||
Type: |Number|
|
||||
Default: `1`
|
||||
|
||||
Indicates if an opened floating preview shall be automatically closed upon
|
||||
movement of the cursor. If set to 1, the window will close automatically if
|
||||
the cursor is moved and the preview is not focused. If set to 0, it will
|
||||
remain open until explicitly closed (e.g. with <plug>(lsp-preview-close),
|
||||
or <ESC> when focused).
|
||||
|
||||
Example:
|
||||
" Preview closes on cursor move
|
||||
let g:lsp_preview_autoclose = 1
|
||||
|
||||
" Preview remains open and waits for an explicit call
|
||||
let g:lsp_preview_autoclose = 0
|
||||
|
||||
g:lsp_preview_doubletap *g:lsp_preview_doubletap*
|
||||
Type: |List|
|
||||
Default: `[function('lsp#ui#vim#output#focuspreview')]`
|
||||
|
||||
When preview is called twice with the same data while the preview is still
|
||||
open, the function in `lsp_preview_doubletap` is called instead. To disable
|
||||
this and just "refresh" the preview, set to ´0´.
|
||||
|
||||
Example:
|
||||
" Focus preview on repeated preview (does not work for vim8.1 popups)
|
||||
let g:lsp_preview_doubletap = [function('lsp#ui#vim#output#focuspreview')]
|
||||
|
||||
" Closes the preview window on the second call to preview
|
||||
let g:lsp_preview_doubletap = [function('lsp#ui#vim#output#closepreview')]
|
||||
|
||||
" Disables double tap feature; refreshes the preview on consecutive taps
|
||||
let g:lsp_preview_doubletap = 0
|
||||
|
||||
g:lsp_insert_text_enabled *g:lsp_insert_text_enabled*
|
||||
Type: |Number|
|
||||
Default: `1`
|
||||
@@ -566,6 +634,9 @@ Gets the hover information and displays it in the |preview-window|.
|
||||
* |preview-window| can be closed using the default vim mapping - `<c-w><c-z>`.
|
||||
* To control the default focus of |preview-window| for |LspHover|
|
||||
configure |g:lsp_preview_keep_focus|.
|
||||
* If using neovim with nvim_win_open() available, |g:lsp_preview_float| can be
|
||||
set to enable a floating preview at the cursor which is closed automatically
|
||||
on cursormove if not focused and can be closed with <esc> if focused.
|
||||
|
||||
|
||||
LspNextError *LspNextError*
|
||||
@@ -640,6 +711,8 @@ Available plug mappings are following:
|
||||
(lsp-hover)
|
||||
(lsp-next-error)
|
||||
(lsp-next-reference)
|
||||
(lsp-preview-close)
|
||||
(lsp-preview-focus)
|
||||
(lsp-previous-error)
|
||||
(lsp-previous-reference)
|
||||
(lsp-references)
|
||||
@@ -653,6 +726,16 @@ Available plug mappings are following:
|
||||
|
||||
See also |vim-lsp-commands|
|
||||
|
||||
<plug>(lsp-preview-close) *<plug>(lsp-preview-close)*
|
||||
|
||||
Closes an opened preview window
|
||||
|
||||
<plug>(lsp-preview-focus) *<plug>(lsp-preview-focus)*
|
||||
|
||||
Transfers focus to an opened preview window or back to the previous window if
|
||||
focus is already on the preview window.
|
||||
|
||||
|
||||
===============================================================================
|
||||
Autocomplete *vim-lsp-autocomplete*
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
" No usual did_ftplugin header here as we NEED to run this always
|
||||
|
||||
setlocal previewwindow buftype=nofile bufhidden=wipe noswapfile nobuflisted
|
||||
if has('patch-8.1.1517') && g:lsp_preview_float && !has('nvim')
|
||||
" Can not set buftype or popup_close will fail with 'not a popup window'
|
||||
setlocal previewwindow bufhidden=wipe noswapfile nobuflisted
|
||||
else
|
||||
setlocal previewwindow buftype=nofile bufhidden=wipe noswapfile nobuflisted
|
||||
endif
|
||||
setlocal nocursorline nofoldenable
|
||||
|
||||
if has('syntax')
|
||||
|
||||
@@ -25,6 +25,9 @@ let g:lsp_use_event_queue = get(g:, 'lsp_use_event_queue', has('nvim') || has('p
|
||||
let g:lsp_insert_text_enabled= get(g:, 'lsp_insert_text_enabled', 1)
|
||||
let g:lsp_text_edit_enabled = get(g:, 'lsp_text_edit_enabled', has('patch-8.0.1493'))
|
||||
let g:lsp_highlight_references_enabled = get(g:, 'lsp_highlight_references_enabled', 1)
|
||||
let g:lsp_preview_float = get(g:, 'lsp_preview_float', 1)
|
||||
let g:lsp_preview_autoclose = get(g:, 'lsp_preview_autoclose', 1)
|
||||
let g:lsp_preview_doubletap = get(g:, 'lsp_preview_doubletap', [function('lsp#ui#vim#output#focuspreview')])
|
||||
|
||||
let g:lsp_get_vim_completion_item = get(g:, 'lsp_get_vim_completion_item', [function('lsp#omni#default_get_vim_completion_item')])
|
||||
let g:lsp_get_supported_capabilities = get(g:, 'lsp_get_supported_capabilities', [function('lsp#default_get_supported_capabilities')])
|
||||
@@ -64,6 +67,8 @@ nnoremap <plug>(lsp-definition) :<c-u>call lsp#ui#vim#definition()<cr>
|
||||
nnoremap <plug>(lsp-document-symbol) :<c-u>call lsp#ui#vim#document_symbol()<cr>
|
||||
nnoremap <plug>(lsp-document-diagnostics) :<c-u>call lsp#ui#vim#diagnostics#document_diagnostics()<cr>
|
||||
nnoremap <plug>(lsp-hover) :<c-u>call lsp#ui#vim#hover#get_hover_under_cursor()<cr>
|
||||
nnoremap <plug>(lsp-preview-close) :<c-u>call lsp#ui#vim#output#closepreview()<cr>
|
||||
nnoremap <plug>(lsp-preview-focus) :<c-u>call lsp#ui#vim#output#focuspreview()<cr>
|
||||
nnoremap <plug>(lsp-next-error) :<c-u>call lsp#ui#vim#diagnostics#next_error()<cr>
|
||||
nnoremap <plug>(lsp-previous-error) :<c-u>call lsp#ui#vim#diagnostics#previous_error()<cr>
|
||||
nnoremap <plug>(lsp-references) :<c-u>call lsp#ui#vim#references()<cr>
|
||||
|
||||
Reference in New Issue
Block a user