From 530b38de342a21cce330a32af0c1b66671d335c2 Mon Sep 17 00:00:00 2001 From: "D. Ben Knoble" Date: Fri, 23 Jul 2021 08:59:31 -0400 Subject: [PATCH] Vim popup (#3817) * implement vim popups for preview Details on implementation ------------------------- - we make use of the |popupwin| api - we split implementations (Nvim* vs. Vim* prefix) and call the right one based on has('nvim') - we follow a similar structure in each function, using the relevant API - popup_list, win_execute, popup_settext in VimShow - popup_create in VimCreate - popup_close in VimClose Some differences ---------------- - we DON'T have VimPrepareWindowContent because we use arguments to popup_create for borders, padding, etc., and it also takes care of buffer creation. - we follow the protocol of setting and using w:preview for information, but we only need the ID - InsertEnter is the only autocommand required, because of popup_create's moved argument. Any cursor movement with 'any' will close the popup. This in turns means VimClose is only called from InsertMode, so no mode-restoration necessary - we don't tweak too much in the buffer because vim's popup buffers already have most relevant settings and aren't editable without calling popup functions. - I enabled scrollbars, close buttons, dragging, and resizing - vim popups get as big as they need to by default, so no worrying about truncating/hiding/size Note: we might want to consider changing w:preview to w:ale_preview to avoid clashes if someone else tries to use the same variable * floating window: document that vim supports it * lint: fix indent/cont. lines --- README.md | 7 +++ autoload/ale/floating_preview.vim | 93 +++++++++++++++++++++++++++---- doc/ale.txt | 16 +++--- 3 files changed, 97 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index e0934694..697f2d87 100644 --- a/README.md +++ b/README.md @@ -930,6 +930,13 @@ make it look nicer. let g:ale_floating_window_border = ['│', '─', '╭', '╮', '╯', '╰'] ``` +Since vim's default uses nice unicode characters when possible, you can trick +ale into using that default with + +```vim +let g:ale_floating_window_border = repeat([''], 6) +``` + ### 5.xxii. How can I use ALE and vim-lsp together? diff --git a/autoload/ale/floating_preview.vim b/autoload/ale/floating_preview.vim index 729e04b4..f0bc8f80 100644 --- a/autoload/ale/floating_preview.vim +++ b/autoload/ale/floating_preview.vim @@ -1,26 +1,35 @@ " Author: Jan-Grimo Sobez " Author: Kevin Clark +" Author: D. Ben Knoble " Description: Floating preview window for showing whatever information in. -" Precondition: exists('*nvim_open_win') +" Precondition: exists('*nvim_open_win') || has('popupwin') function! ale#floating_preview#Show(lines, ...) abort - if !exists('*nvim_open_win') + if !exists('*nvim_open_win') && !has('popupwin') execute 'echom ''Floating windows not supported in this vim instance.''' return endif + let l:options = get(a:000, 0, {}) + + if has('nvim') + call s:NvimShow(a:lines, l:options) + else + call s:VimShow(a:lines, l:options) + endif +endfunction + +function! s:NvimShow(lines, options) abort " 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) + call s:NvimCreate(a:options) else call nvim_buf_set_option(w:preview['buffer'], 'modifiable', v:true) endif @@ -30,7 +39,7 @@ function! ale#floating_preview#Show(lines, ...) abort call nvim_set_current_win(w:preview['id']) - for l:command in get(l:options, 'commands', []) + for l:command in get(a:options, 'commands', []) call execute(l:command) endfor @@ -41,13 +50,13 @@ function! ale#floating_preview#Show(lines, ...) abort autocmd! if g:ale_close_preview_on_insert - autocmd CursorMoved,TabLeave,WinLeave,InsertEnter ++once call s:Close() + autocmd CursorMoved,TabLeave,WinLeave,InsertEnter ++once call s:NvimClose() else - autocmd CursorMoved,TabLeave,WinLeave ++once call s:Close() + autocmd CursorMoved,TabLeave,WinLeave ++once call s:NvimClose() endif augroup END - let [l:lines, l:width, l:height] = s:PrepareWindowContent(a:lines) + let [l:lines, l:width, l:height] = s:NvimPrepareWindowContent(a:lines) call nvim_win_set_width(w:preview['id'], l:width) call nvim_win_set_height(w:preview['id'], l:height) @@ -56,7 +65,33 @@ function! ale#floating_preview#Show(lines, ...) abort call nvim_buf_set_option(w:preview['buffer'], 'modifiable', v:false) endfunction -function! s:PrepareWindowContent(lines) abort +function! s:VimShow(lines, options) abort + if g:ale_close_preview_on_insert + " Remove the close autocmd so it doesn't happen mid update + silent! autocmd! ale_floating_preview_window + endif + + " Only create a new window if we need it + if !exists('w:preview') || index(popup_list(), w:preview['id']) is# -1 + call s:VimCreate(a:options) + endif + + " Execute commands in window context + for l:command in get(a:options, 'commands', []) + call win_execute(w:preview['id'], l:command) + endfor + + call popup_settext(w:preview['id'], a:lines) + + if g:ale_close_preview_on_insert + augroup ale_floating_preview_window + autocmd! + autocmd InsertEnter * ++once call s:VimClose() + augroup END + endif +endfunction + +function! s:NvimPrepareWindowContent(lines) abort let l:max_height = 10 let l:width = max(map(copy(a:lines), 'strdisplaywidth(v:val)')) @@ -94,7 +129,7 @@ function! s:PrepareWindowContent(lines) abort return [l:lines, l:width, l:height] endfunction -function! s:Create(options) abort +function! s:NvimCreate(options) abort let l:buffer = nvim_create_buf(v:false, v:false) let l:winid = nvim_open_win(l:buffer, v:false, { \ 'relative': 'cursor', @@ -112,7 +147,32 @@ function! s:Create(options) abort let w:preview = {'id': l:winid, 'buffer': l:buffer} endfunction -function! s:Close() abort +function! s:VimCreate(options) abort + let l:popup_id = popup_create([], { + \ 'line': 'cursor+1', + \ 'col': 'cursor', + \ 'drag': v:true, + \ 'resize': v:true, + \ 'close': 'button', + \ 'padding': [0, 1, 0, 1], + \ 'border': [], + \ 'borderchars': empty(g:ale_floating_window_border) ? [' '] : [ + \ g:ale_floating_window_border[1], + \ g:ale_floating_window_border[0], + \ g:ale_floating_window_border[1], + \ g:ale_floating_window_border[0], + \ g:ale_floating_window_border[2], + \ g:ale_floating_window_border[3], + \ g:ale_floating_window_border[4], + \ g:ale_floating_window_border[5], + \ ], + \ 'moved': 'any', + \ }) + call setbufvar(winbufnr(l:popup_id), '&filetype', get(a:options, 'filetype', 'ale-preview')) + let w:preview = {'id': l:popup_id} +endfunction + +function! s:NvimClose() abort let l:mode = mode() let l:restore_visual = l:mode is# 'v' || l:mode is# 'V' || l:mode is# "\" @@ -132,3 +192,12 @@ function! s:Close() abort normal! gv endif endfunction + +function! s:VimClose() abort + if !exists('w:preview') + return + endif + + call popup_close(w:preview['id']) + unlet w:preview +endfunction diff --git a/doc/ale.txt b/doc/ale.txt index f81bcefb..2d80c322 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -651,9 +651,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. And -|g:ale_floating_window_border| for the border setting. +When using Neovim or Vim with |popupwin|, 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. And |g:ale_floating_window_border| for the border setting. For Vim 8.1+ terminals, mouse hovering is disabled by default. Enabling |balloonexpr| commands in terminals can cause scrolling issues in terminals, @@ -968,8 +968,8 @@ g:ale_detail_to_floating_preview *g: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. + When this option is set to `1`, Neovim or Vim with |popupwin| will use a + floating window for ALEDetail output. g:ale_disable_lsp *g:ale_disable_lsp* @@ -1200,7 +1200,8 @@ 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. + When set to `1`, Neovim or Vim with |popupwin| 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`. @@ -1281,7 +1282,8 @@ g:ale_hover_to_floating_preview *g:ale_hover_to_floating_preview* Type: |Number| Default: `0` - If set to `1`, Neovim will use floating windows for hover messages. + If set to `1`, Neovim or Vim with |popupwin| will use floating windows for + hover messages. g:ale_keep_list_window_open *g:ale_keep_list_window_open*