* Add nvim floating window hover support * Add configuration for float to replace preview * preview#ShowFloating: qualify local variables * Configure floating preview usecases individually Also: * Extract floating preview to its own file. * Ignore 'stay_here' option. Moving into the floating preview window seems confusing at best. * Re-use existing floating preview window if it's still up. * Flush out floating preview documentation. * Watch cursor position changes per window Floating previews open a new window, so when that window is written to, it moves briefly there at a different position than the original window. This makes repeated positions detected when positions are tracked at a s: level. Instead, we change the variable to window scoped, which only fires a message if the cursor has changed from the last position in *that window*. * g:ale_floating_preview cleanup * floating_preview: add ALEDetail tests * Fix fecs test missing runtime call * Add ALEHover floating preview tests Co-authored-by: Jan-Grimo Sobez <jan-grimo.sobez@phys.chem.ethz.ch>
This commit is contained in:
parent
97ce2423b0
commit
39f393ef07
8 changed files with 330 additions and 7 deletions
|
@ -9,7 +9,6 @@ let g:ale_echo_delay = get(g:, 'ale_echo_delay', 10)
|
|||
let g:ale_echo_msg_format = get(g:, 'ale_echo_msg_format', '%code: %%s')
|
||||
|
||||
let s:cursor_timer = -1
|
||||
let s:last_pos = [0, 0, 0]
|
||||
|
||||
function! ale#cursor#TruncatedEcho(original_message) abort
|
||||
let l:message = a:original_message
|
||||
|
@ -118,14 +117,18 @@ function! ale#cursor#EchoCursorWarningWithDelay() abort
|
|||
|
||||
let l:pos = getpos('.')[0:2]
|
||||
|
||||
if !exists('w:last_pos')
|
||||
let w:last_pos = [0, 0, 0]
|
||||
endif
|
||||
|
||||
" Check the current buffer, line, and column number against the last
|
||||
" recorded position. If the position has actually changed, *then*
|
||||
" we should echo something. Otherwise we can end up doing processing
|
||||
" the echo message far too frequently.
|
||||
if l:pos != s:last_pos
|
||||
if l:pos != w:last_pos
|
||||
let l:delay = ale#Var(l:buffer, 'echo_delay')
|
||||
|
||||
let s:last_pos = l:pos
|
||||
let w:last_pos = l:pos
|
||||
let s:cursor_timer = timer_start(
|
||||
\ l:delay,
|
||||
\ function('ale#cursor#EchoCursorWarning')
|
||||
|
@ -139,11 +142,16 @@ function! s:ShowCursorDetailForItem(loc, options) abort
|
|||
let s:last_detailed_line = line('.')
|
||||
let l:message = get(a:loc, 'detail', a:loc.text)
|
||||
let l:lines = split(l:message, "\n")
|
||||
call ale#preview#Show(l:lines, {'stay_here': l:stay_here})
|
||||
|
||||
" Clear the echo message if we manually displayed details.
|
||||
if !l:stay_here
|
||||
execute 'echo'
|
||||
if g:ale_floating_preview || g:ale_detail_to_floating_preview
|
||||
call ale#floating_preview#Show(l:lines)
|
||||
else
|
||||
call ale#preview#Show(l:lines, {'stay_here': l:stay_here})
|
||||
|
||||
" Clear the echo message if we manually displayed details.
|
||||
if !l:stay_here
|
||||
execute 'echo'
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
|
91
autoload/ale/floating_preview.vim
Normal file
91
autoload/ale/floating_preview.vim
Normal file
|
@ -0,0 +1,91 @@
|
|||
" Author: Jan-Grimo Sobez <jan-grimo.sobez@phys.chem.ethz.ch>
|
||||
" Author: Kevin Clark <kevin.clark@gmail.com>
|
||||
" Description: Floating preview window for showing whatever information in.
|
||||
|
||||
" Precondition: exists('*nvim_open_win')
|
||||
|
||||
function! ale#floating_preview#Show(lines, ...) abort
|
||||
if !exists('*nvim_open_win')
|
||||
execute 'echom ''Floating windows not supported in this vim instance.'''
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
" Remove the close autocmd so it doesn't happen mid update
|
||||
augroup ale_floating_preview_window
|
||||
autocmd!
|
||||
augroup END
|
||||
|
||||
let l:options = get(a:000, 0, {})
|
||||
|
||||
" Only create a new window if we need it
|
||||
if !exists('w:preview') || index(nvim_list_wins(), w:preview['id']) is# -1
|
||||
call s:Create(l:options)
|
||||
else
|
||||
call nvim_buf_set_option(w:preview['buffer'], 'modifiable', v:true)
|
||||
endif
|
||||
|
||||
" Execute commands in window context
|
||||
let l:parent_window = nvim_get_current_win()
|
||||
|
||||
call nvim_set_current_win(w:preview['id'])
|
||||
|
||||
for l:command in get(l:options, 'commands', [])
|
||||
call execute(l:command)
|
||||
endfor
|
||||
|
||||
call nvim_set_current_win(l:parent_window)
|
||||
|
||||
" Return to parent context on move
|
||||
augroup ale_floating_preview_window
|
||||
autocmd!
|
||||
|
||||
if g:ale_close_preview_on_insert
|
||||
autocmd CursorMoved,TabLeave,WinLeave,InsertEnter <buffer> ++once call s:Close()
|
||||
else
|
||||
autocmd CursorMoved,TabLeave,WinLeave <buffer> ++once call s:Close()
|
||||
endif
|
||||
augroup END
|
||||
|
||||
let l:width = max(map(copy(a:lines), 'strdisplaywidth(v:val)'))
|
||||
let l:height = min([len(a:lines), 10])
|
||||
call nvim_win_set_width(w:preview['id'], l:width)
|
||||
call nvim_win_set_height(w:preview['id'], l:height)
|
||||
|
||||
call nvim_buf_set_lines(w:preview['buffer'], 0, -1, v:false, a:lines)
|
||||
call nvim_buf_set_option(w:preview['buffer'], 'modified', v:false)
|
||||
call nvim_buf_set_option(w:preview['buffer'], 'modifiable', v:false)
|
||||
endfunction
|
||||
|
||||
function! s:Create(options) abort
|
||||
let l:buffer = nvim_create_buf(v:false, v:false)
|
||||
let l:winid = nvim_open_win(l:buffer, v:false, {
|
||||
\ 'relative': 'cursor',
|
||||
\ 'row': 1,
|
||||
\ 'col': 0,
|
||||
\ 'width': 42,
|
||||
\ 'height': 4,
|
||||
\ 'style': 'minimal'
|
||||
\ })
|
||||
call nvim_buf_set_option(l:buffer, 'buftype', 'acwrite')
|
||||
call nvim_buf_set_option(l:buffer, 'bufhidden', 'delete')
|
||||
call nvim_buf_set_option(l:buffer, 'swapfile', v:false)
|
||||
call nvim_buf_set_option(l:buffer, 'filetype', get(a:options, 'filetype', 'ale-preview'))
|
||||
|
||||
let w:preview = {'id': l:winid, 'buffer': l:buffer}
|
||||
endfunction
|
||||
|
||||
function! s:Close() abort
|
||||
if !exists('w:preview')
|
||||
return
|
||||
endif
|
||||
|
||||
call setbufvar(w:preview['buffer'], '&modified', 0)
|
||||
|
||||
if win_id2win(w:preview['id']) > 0
|
||||
execute win_id2win(w:preview['id']).'wincmd c'
|
||||
endif
|
||||
|
||||
unlet w:preview
|
||||
endfunction
|
||||
|
|
@ -46,6 +46,10 @@ function! ale#hover#HandleTSServerResponse(conn_id, response) abort
|
|||
call balloon_show(a:response.body.displayString)
|
||||
elseif get(l:options, 'truncated_echo', 0)
|
||||
call ale#cursor#TruncatedEcho(split(a:response.body.displayString, "\n")[0])
|
||||
elseif g:ale_hover_to_floating_preview || g:ale_floating_preview
|
||||
call ale#floating_preview#Show(split(a:response.body.displayString, "\n"), {
|
||||
\ 'filetype': 'ale-preview.message',
|
||||
\})
|
||||
elseif g:ale_hover_to_preview
|
||||
call ale#preview#Show(split(a:response.body.displayString, "\n"), {
|
||||
\ 'filetype': 'ale-preview.message',
|
||||
|
@ -226,6 +230,11 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort
|
|||
call balloon_show(join(l:lines, "\n"))
|
||||
elseif get(l:options, 'truncated_echo', 0)
|
||||
call ale#cursor#TruncatedEcho(l:lines[0])
|
||||
elseif g:ale_hover_to_floating_preview || g:ale_floating_preview
|
||||
call ale#floating_preview#Show(l:lines, {
|
||||
\ 'filetype': 'ale-preview.message',
|
||||
\ 'commands': l:commands,
|
||||
\})
|
||||
elseif g:ale_hover_to_preview
|
||||
call ale#preview#Show(l:lines, {
|
||||
\ 'filetype': 'ale-preview.message',
|
||||
|
|
30
doc/ale.txt
30
doc/ale.txt
|
@ -646,6 +646,9 @@ problem will be displayed in a balloon instead of hover information.
|
|||
Hover information can be displayed in the preview window instead by setting
|
||||
|g:ale_hover_to_preview| to `1`.
|
||||
|
||||
When using Neovim, if |g:ale_hover_to_floating_preview| or |g:ale_floating_preview|
|
||||
is set to 1, the hover information will show in a floating window.
|
||||
|
||||
For Vim 8.1+ terminals, mouse hovering is disabled by default. Enabling
|
||||
|balloonexpr| commands in terminals can cause scrolling issues in terminals,
|
||||
so ALE will not attempt to show balloons unless |g:ale_set_balloons| is set to
|
||||
|
@ -954,6 +957,15 @@ g:ale_default_navigation *g:ale_default_navigation*
|
|||
buffer, such as for |ALEFindReferences|, or |ALEGoToDefinition|.
|
||||
|
||||
|
||||
g:ale_detail_to_floating_preview *g:ale_detail_to_floating_preview*
|
||||
*b:ale_detail_to_floating_preview*
|
||||
Type: |Number|
|
||||
Default: `0`
|
||||
|
||||
When this option is set to `1`, Neovim will use a floating window for
|
||||
ALEDetail output.
|
||||
|
||||
|
||||
g:ale_disable_lsp *g:ale_disable_lsp*
|
||||
*b:ale_disable_lsp*
|
||||
|
||||
|
@ -1177,6 +1189,16 @@ g:ale_fix_on_save_ignore *g:ale_fix_on_save_ignore*
|
|||
let g:ale_fix_on_save_ignore = [g:AddBar]
|
||||
<
|
||||
|
||||
g:ale_floating_preview *g:ale_floating_preview*
|
||||
|
||||
Type: |Number|
|
||||
Default: `0`
|
||||
|
||||
When set to `1`, Neovim will use a floating window for ale's preview window.
|
||||
This is equivalent to setting |g:ale_hover_to_floating_preview| and
|
||||
|g:ale_detail_to_floating_preview| to `1`.
|
||||
|
||||
|
||||
g:ale_history_enabled *g:ale_history_enabled*
|
||||
|
||||
Type: |Number|
|
||||
|
@ -1235,6 +1257,14 @@ g:ale_hover_to_preview *g:ale_hover_to_preview*
|
|||
instead of in balloons or the message line.
|
||||
|
||||
|
||||
g:ale_hover_to_floating_preview *g:ale_hover_to_floating_preview*
|
||||
*b:ale_hover_to_floating_preview*
|
||||
Type: |Number|
|
||||
Default: `0`
|
||||
|
||||
If set to `1`, Neovim will use floating windows for hover messages.
|
||||
|
||||
|
||||
g:ale_keep_list_window_open *g:ale_keep_list_window_open*
|
||||
*b:ale_keep_list_window_open*
|
||||
Type: |Number|
|
||||
|
|
|
@ -138,6 +138,15 @@ let g:ale_set_balloons = get(g:, 'ale_set_balloons', has('balloon_eval') && has(
|
|||
" Use preview window for hover messages.
|
||||
let g:ale_hover_to_preview = get(g:, 'ale_hover_to_preview', 0)
|
||||
|
||||
" Float preview windows in Neovim
|
||||
let g:ale_floating_preview = get(g:, 'ale_floating_preview', 0)
|
||||
|
||||
" Hovers use floating windows in Neovim
|
||||
let g:ale_hover_to_floating_preview = get(g:, 'ale_hover_to_floating_preview', 0)
|
||||
|
||||
" Detail uses floating windows in Neovim
|
||||
let g:ale_detail_to_floating_preview = get(g:, 'ale_detail_to_floating_preview', 0)
|
||||
|
||||
" This flag can be set to 0 to disable warnings for trailing whitespace
|
||||
let g:ale_warn_about_trailing_whitespace = get(g:, 'ale_warn_about_trailing_whitespace', 1)
|
||||
" This flag can be set to 0 to disable warnings for trailing blank lines
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
Before:
|
||||
call ale#assert#SetUpLinterTest('javascript', 'fecs')
|
||||
runtime autoload/ale/handlers/fecs.vim
|
||||
|
||||
After:
|
||||
call ale#assert#TearDownLinterTest()
|
||||
|
|
92
test/test_floating_preview.vader
Normal file
92
test/test_floating_preview.vader
Normal file
|
@ -0,0 +1,92 @@
|
|||
Before:
|
||||
let g:ale_floating_preview = 0
|
||||
let g:ale_hover_to_floating_preview = 0
|
||||
let g:ale_detail_to_floating_preview = 0
|
||||
|
||||
runtime autoload/ale/floating_preview.vim
|
||||
|
||||
let g:floated_lines = []
|
||||
let g:floating_preview_show_called = 0
|
||||
|
||||
" Stub out so we can track the call
|
||||
function! ale#floating_preview#Show(lines, ...) abort
|
||||
let g:floating_preview_show_called = 1
|
||||
let g:floated_lines = a:lines
|
||||
endfunction
|
||||
|
||||
let g:ale_buffer_info = {
|
||||
\ bufnr('%'): {
|
||||
\ 'loclist': [
|
||||
\ {
|
||||
\ 'lnum': 1,
|
||||
\ 'col': 10,
|
||||
\ 'bufnr': bufnr('%'),
|
||||
\ 'vcol': 0,
|
||||
\ 'linter_name': 'notalinter',
|
||||
\ 'nr': -1,
|
||||
\ 'type': 'E',
|
||||
\ 'code': 'semi',
|
||||
\ 'text': "Missing semicolon.\r",
|
||||
\ 'detail': "Every statement should end with a semicolon\nsecond line",
|
||||
\ },
|
||||
\ ],
|
||||
\ }
|
||||
\}
|
||||
|
||||
call ale#linter#Reset()
|
||||
call ale#linter#PreventLoading('javascript')
|
||||
|
||||
After:
|
||||
Restore
|
||||
|
||||
let g:ale_floating_preview = 0
|
||||
let g:ale_hover_to_floating_preview = 0
|
||||
let g:ale_detail_to_floating_preview = 0
|
||||
|
||||
call cursor(1, 1)
|
||||
|
||||
let g:ale_buffer_info = {}
|
||||
|
||||
" Close the preview window if it's open.
|
||||
if &filetype is# 'ale-preview'
|
||||
noautocmd :q!
|
||||
endif
|
||||
|
||||
call ale#linter#Reset()
|
||||
|
||||
|
||||
Given javascript(A file with warnings/errors):
|
||||
var x = 3 + 12345678
|
||||
var x = 5*2 + parseInt("10");
|
||||
// comment
|
||||
|
||||
Execute(Floating preview is used with ALEDetail when g:ale_floating_preview set):
|
||||
let g:ale_floating_preview = 1
|
||||
|
||||
call cursor(1, 10)
|
||||
|
||||
ALEDetail
|
||||
|
||||
let expected = ["Every statement should end with a semicolon", "second line"]
|
||||
|
||||
AssertEqual 1, g:floating_preview_show_called
|
||||
AssertEqual expected, g:floated_lines
|
||||
|
||||
Execute(Floating preview is used with ALEDetail when g:ale_detail_to_floating_preview set):
|
||||
let g:ale_detail_to_floating_preview = 1
|
||||
|
||||
call cursor(1, 10)
|
||||
|
||||
ALEDetail
|
||||
|
||||
let expected = ["Every statement should end with a semicolon", "second line"]
|
||||
|
||||
AssertEqual 1, g:floating_preview_show_called
|
||||
AssertEqual expected, g:floated_lines
|
||||
|
||||
Execute(Floating preview is not used with ALEDetail by default):
|
||||
call cursor(1, 10)
|
||||
|
||||
ALEDetail
|
||||
|
||||
AssertEqual 0, g:floating_preview_show_called
|
|
@ -7,9 +7,25 @@ Before:
|
|||
let g:item_list = []
|
||||
let g:show_message_arg_list = []
|
||||
|
||||
let g:ale_floating_preview = 0
|
||||
let g:ale_hover_to_floating_preview = 0
|
||||
let g:ale_detail_to_floating_preview = 0
|
||||
|
||||
runtime autoload/ale/linter.vim
|
||||
runtime autoload/ale/lsp.vim
|
||||
runtime autoload/ale/lsp_linter.vim
|
||||
runtime autoload/ale/util.vim
|
||||
runtime autoload/ale/floating_preview.vim
|
||||
runtime autoload/ale/hover.vim
|
||||
|
||||
let g:floated_lines = []
|
||||
let g:floating_preview_show_called = 0
|
||||
|
||||
" Stub out so we can track the call
|
||||
function! ale#floating_preview#Show(lines, ...) abort
|
||||
let g:floating_preview_show_called = 1
|
||||
let g:floated_lines = a:lines
|
||||
endfunction
|
||||
|
||||
function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
|
||||
let g:Callback = a:callback
|
||||
|
@ -50,6 +66,7 @@ Before:
|
|||
\)
|
||||
endfunction
|
||||
|
||||
|
||||
After:
|
||||
call ale#hover#SetMap({})
|
||||
call ale#test#RestoreDirectory()
|
||||
|
@ -65,6 +82,7 @@ After:
|
|||
runtime autoload/ale/lsp_linter.vim
|
||||
runtime autoload/ale/lsp.vim
|
||||
runtime autoload/ale/util.vim
|
||||
runtime autoload/ale/floating_preview.vim
|
||||
|
||||
Given python(Some Python file):
|
||||
foo
|
||||
|
@ -168,6 +186,28 @@ Execute(LSP hover response with lists of strings and marked strings should be ha
|
|||
\], g:show_message_arg_list
|
||||
AssertEqual {}, ale#hover#GetMap()
|
||||
|
||||
Execute(LSP hover with ale_floating_preview should float):
|
||||
let g:ale_floating_preview = 1
|
||||
|
||||
call HandleValidLSPResult({'contents': "the message\ncontinuing"})
|
||||
|
||||
AssertEqual 1, g:floating_preview_show_called
|
||||
AssertEqual ["the message", "continuing"], g:floated_lines
|
||||
|
||||
Execute(LSP hover ale_hover_to_floating_preview should float):
|
||||
let g:ale_hover_to_floating_preview = 1
|
||||
|
||||
call HandleValidLSPResult({'contents': "the message\ncontinuing"})
|
||||
|
||||
AssertEqual 1, g:floating_preview_show_called
|
||||
AssertEqual ["the message", "continuing"], g:floated_lines
|
||||
|
||||
|
||||
Execute(LSP hover by default should not float):
|
||||
call HandleValidLSPResult({'contents': "the message\ncontinuing"})
|
||||
|
||||
AssertEqual 0, g:floating_preview_show_called
|
||||
|
||||
Execute(tsserver responses for documentation requests should be handled):
|
||||
call ale#hover#SetMap({3: {'show_documentation': 1, 'buffer': bufnr('')}})
|
||||
|
||||
|
@ -187,3 +227,46 @@ Execute(tsserver responses for documentation requests should be handled):
|
|||
" The preview window should show the text.
|
||||
AssertEqual ['foo is a very good method'], ale#test#GetPreviewWindowText()
|
||||
silent! pclose
|
||||
|
||||
Execute(hover with show_documentation should be in the preview window, not floating):
|
||||
let g:ale_hover_to_floating_preview = 1
|
||||
let g:ale_floating_preview = 1
|
||||
|
||||
call ale#hover#SetMap({3: {'show_documentation': 1, 'buffer': bufnr('')}})
|
||||
|
||||
call ale#hover#HandleTSServerResponse(
|
||||
\ 1,
|
||||
\ {
|
||||
\ 'command': 'quickinfo',
|
||||
\ 'request_seq': 3,
|
||||
\ 'success': v:true,
|
||||
\ 'body': {
|
||||
\ 'documentation': 'foo is a very good method',
|
||||
\ 'displayString': 'foo bar ',
|
||||
\ },
|
||||
\ }
|
||||
\)
|
||||
|
||||
let expected = ["Every statement should end with a semicolon", "second line"]
|
||||
|
||||
AssertEqual 0, g:floating_preview_show_called
|
||||
|
||||
Execute(TSServer hover without show_documentation and ale_floating_preview should float):
|
||||
let g:ale_floating_preview = 1
|
||||
|
||||
call ale#hover#SetMap({3: {'buffer': bufnr('')}})
|
||||
|
||||
call ale#hover#HandleTSServerResponse(
|
||||
\ 1,
|
||||
\ {
|
||||
\ 'command': 'quickinfo',
|
||||
\ 'request_seq': 3,
|
||||
\ 'success': v:true,
|
||||
\ 'body': {
|
||||
\ 'displayString': "the message\ncontinuing",
|
||||
\ },
|
||||
\ }
|
||||
\)
|
||||
|
||||
AssertEqual 1, g:floating_preview_show_called
|
||||
AssertEqual ["the message", "continuing"], g:floated_lines
|
||||
|
|
Reference in a new issue