Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions autoload/ale/floating_preview.vim
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ function! s:VimShow(lines, options) abort
endfor

call popup_settext(w:preview['id'], a:lines)
call s:VimPinWidth(w:preview['id'], a:lines)

if g:ale_close_preview_on_insert
augroup ale_floating_preview_window
Expand Down Expand Up @@ -144,6 +145,56 @@ function! s:NvimCreate(options) abort
let w:preview = {'id': l:winid, 'buffer': l:buffer}
endfunction

" Before 9.2.0419 Vim sizes popups by the lines in view, so the width changes
" while scrolling. Size the popup by all of the lines instead.
function! s:VimPinWidth(winid, lines) abort
let l:user_opts = s:GetPopupOpts()

if has('patch-9.2.0419')
\|| has_key(l:user_opts, 'minwidth')
\|| has_key(l:user_opts, 'maxwidth')
return
endif

call popup_setoptions(a:winid, {
\ 'minwidth': max(map(copy(a:lines), 'strdisplaywidth(v:val)')),
\})
endfunction

" Vim's scroll keys scroll the popup while it is open and has more to show.
" Every key runs as CTRL-D or CTRL-U, which stop at the edges of the text, so
" the popup never scrolls into blank space or shrinks.
function! s:VimFilter(winid, key) abort
if a:key is# "\<Esc>"
call popup_close(a:winid)

return 1
endif

let l:pos = popup_getpos(a:winid)
let l:half = max([1, l:pos.core_height / 2])
let l:command = get({
\ "\<C-e>": "1\<C-d>",
\ "\<C-y>": "1\<C-u>",
\ "\<C-d>": l:half . "\<C-d>",
\ "\<C-u>": l:half . "\<C-u>",
\ "\<C-f>": l:pos.core_height . "\<C-d>",
\ "\<C-b>": l:pos.core_height . "\<C-u>",
\}, a:key, '')

" Let keys through if everything is already visible.
if empty(l:command)
\|| (l:pos.firstline is# 1 && l:pos.lastline >= line('$', a:winid))
return 0
endif

call win_execute(a:winid, 'normal! ' . l:command)
" Vim 8.2 can scroll wrapped text too far if keys arrive faster than redraws.
redraw

return 1
endfunction

function! s:VimCreate(options) abort
" default options
let l:popup_opts = extend({
Expand All @@ -164,6 +215,8 @@ function! s:VimCreate(options) abort
\ get(g:ale_floating_window_border, 4, '+'),
\ get(g:ale_floating_window_border, 5, '+'),
\ ],
\ 'filter': function('s:VimFilter'),
\ 'filtermode': 'n',
\ 'moved': 'any',
\ }, s:GetPopupOpts())

Expand Down
5 changes: 5 additions & 0 deletions doc/ale.txt
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,11 @@ or |g:ale_floating_preview| is set to `true` or `1`, the hover information
will show in a floating window. The borders of the floating preview window can
be customized by setting |g:ale_floating_window_border|.

In Vim, <Esc> closes a floating preview. If it has more text than fits, then
|CTRL-E|, |CTRL-Y|, |CTRL-D|, |CTRL-U|, |CTRL-F|, and |CTRL-B| scroll the
preview instead of the buffer. Set `filter` in
|g:ale_floating_preview_popup_opts| to change this. See |popup-filter|.

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
Expand Down
122 changes: 122 additions & 0 deletions test/test_floating_preview_popupwin.vader
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
Before:
Save g:ale_close_preview_on_insert
Save g:ale_floating_preview_popup_opts

let g:ale_close_preview_on_insert = 0
let g:ale_floating_preview_popup_opts = {'maxheight': 4}

runtime autoload/ale/floating_preview.vim

" Press keys through the popup filter, and return the first line shown
" after each key along with the height of the popup.
function! Press(id, keys) abort
let l:result = []

for l:key in a:keys
call call(popup_getoptions(a:id).filter, [a:id, l:key])
call add(l:result, [
\ popup_getpos(a:id).firstline,
\ popup_getpos(a:id).core_height,
\])
endfor

return l:result
endfunction

After:
Restore

if has('popupwin')
call popup_clear(1)
endif

unlet! w:preview
delfunction Press

Execute(Scroll keys should scroll Vim popups and stop at the edges):
if has('popupwin')
let id = ale#floating_preview#Show(map(range(1, 10), 'string(v:val)'))

AssertEqual
\ [[2, 4], [4, 4], [7, 4], [7, 4], [5, 4], [4, 4], [1, 4], [1, 4]],
\ Press(id, [
\ "\<C-e>", "\<C-d>", "\<C-f>", "\<C-d>",
\ "\<C-u>", "\<C-y>", "\<C-b>", "\<C-u>",
\ ])
endif

Execute(Scrolling should reach the end of wrapped text):
if has('popupwin')
let g:ale_floating_preview_popup_opts = {'maxheight': 4, 'maxwidth': 20}
let id = ale#floating_preview#Show(
\ ['first', repeat('a', 50), repeat('b', 50), 'last']
\)

call Press(id, repeat(["\<C-e>"], 5))

AssertEqual
\ [3, 4, 4],
\ [
\ popup_getpos(id).firstline,
\ popup_getpos(id).lastline,
\ popup_getpos(id).core_height,
\ ]
endif

Execute(Keys should pass through when there is nothing to scroll):
if has('popupwin')
let id = ale#floating_preview#Show(['one', 'two'])
let Filter = popup_getoptions(id).filter
" Vim draws the popup before keys are typed.
redraw

AssertEqual 0, Filter(id, "\<C-d>")
AssertEqual 0, Filter(id, "\<C-e>")
endif

Execute(Other keys should pass through):
if has('popupwin')
let id = ale#floating_preview#Show(map(range(1, 10), 'string(v:val)'))
let Filter = popup_getoptions(id).filter

AssertEqual 0, Filter(id, 'j')
AssertEqual 0, Filter(id, "\<C-n>")
AssertEqual 1, popup_getpos(id).firstline
endif

Execute(Escape should close the popup):
if has('popupwin')
let id = ale#floating_preview#Show(['one'])

AssertEqual 1, call(popup_getoptions(id).filter, [id, "\<Esc>"])
AssertEqual -1, index(popup_list(), id)
endif

Execute(Typed keys should reach the filter in normal mode):
if has('popupwin')
let id = ale#floating_preview#Show(map(range(1, 10), 'string(v:val)'))

call feedkeys("\<C-d>", 'xt')

AssertEqual 3, popup_getpos(id).firstline
endif

Execute(The popup width should cover all lines on older Vim versions):
if has('popupwin')
let id = ale#floating_preview#Show(
\ map(range(1, 9), 'string(v:val)') + [repeat('x', 30)]
\)

AssertEqual
\ has('patch-9.2.0419') ? 0 : 30,
\ popup_getoptions(id).minwidth
endif

Execute(The popup width should not be changed when users set it):
if has('popupwin')
let g:ale_floating_preview_popup_opts = {'maxheight': 4, 'maxwidth': 10}
let id = ale#floating_preview#Show([repeat('x', 30)])

AssertEqual 0, popup_getoptions(id).minwidth
AssertEqual 10, popup_getoptions(id).maxwidth
endif
Loading