From 1b8b8d6a3dea5e562f6bc831e062fbe534ed1edc Mon Sep 17 00:00:00 2001 From: Axel Forsman Date: Tue, 18 Jun 2019 21:39:18 +0200 Subject: [PATCH 1/6] Add popup for displaying off-screen matches Replaces the status line with popup windows for displaying off-screen matches. This employs the new, currently unstable, popup feature of Vim version 8.1. The popup is displayed at the top of the window for matches above the view, and respectively at the bottom for matches below. The implementation is only a rough sketch without much thought to configurability or code clarity. One designated popup is created per-window and stored in a window-local variable. It is then moved and showed/hid depending on the match or lack thereof. Window movements are not handled. --- autoload/matchup/matchparen.vim | 43 +++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/autoload/matchup/matchparen.vim b/autoload/matchup/matchparen.vim index b48dc34..b790f4b 100644 --- a/autoload/matchup/matchparen.vim +++ b/autoload/matchup/matchparen.vim @@ -136,6 +136,9 @@ function! s:matchparen.clear() abort dict " {{{1 unlet! w:matchup_match_id_list endif + if exists('w:match_popup') + call popup_hide(w:match_popup) + endif if exists('w:matchup_oldstatus') let &l:statusline = w:matchup_oldstatus unlet w:matchup_oldstatus @@ -486,6 +489,19 @@ function s:matchparen.transmute_reset() abort dict endif endfunction +" }}}1 +function! s:init_popup() abort " {{{1 + if exists('w:match_popup') + " echoerr 'Match popup already exists in this window.' + return + endif + " Create a popup and store its winid + let w:match_popup = popup_create('', { + \ 'hidden': v:true, + \ }) + call popup_hide(w:match_popup) " TODO 'hidden' in popup_create-usage unimplemented +endfunction + " }}}1 function! s:do_offscreen(current) " {{{1 let l:offscreen = {} @@ -502,7 +518,8 @@ function! s:do_offscreen(current) " {{{1 if empty(l:offscreen) | return | endif - call s:do_offscreen_statusline(l:offscreen) + " call s:do_offscreen_statusline(l:offscreen) + call s:do_offscreen_popup(l:offscreen) endfunction " }}}1 @@ -522,7 +539,7 @@ function! s:do_offscreen_statusline(offscreen) " {{{1 let w:matchup_oldstatus = &l:statusline endif if !g:matchup_matchparen_status_offscreen_manual - let &l:statusline = w:matchup_statusline + let &l:statusline = w:matchup_statusline endif if exists('#User#MatchupOffscreenEnter') @@ -530,6 +547,28 @@ function! s:do_offscreen_statusline(offscreen) " {{{1 endif endfunction +" }}}1 +function! s:do_offscreen_popup(offscreen) " {{{1 + call s:init_popup() " TODO Do in WinNew autocmd + + " Screen position of top-left corner of current window + let [l:row, l:col] = win_screenpos(winnr()) + let l:col += (&number || &relativenumber ? &numberwidth : 0) + \ + &foldcolumn " TODO Take signcolumn into consideration + let l:height = winheight(0) " Height of current window + let l:line = a:offscreen.lnum < line('.') ? l:row : l:row + l:height - 1 + if l:line == winline() | return | endif " If popup would overlap with cursor + + call popup_move(w:match_popup, { + \ 'line': line, + \ 'col': col, + \ 'maxheight': 1, + \ }) + " Set popup text + call setbufline(winbufnr(w:match_popup), 1, getline(a:offscreen.lnum)) + call popup_show(w:match_popup) +endfunction + " }}}1 function! MatchupStatusOffscreen() " {{{1 From 7009d8f098e28a1601675c08b413f2408c502ddc Mon Sep 17 00:00:00 2001 From: Axel Forsman Date: Thu, 27 Jun 2019 17:20:22 +0200 Subject: [PATCH 2/6] Fix use only one global popup Previously one popup per window was created to be able to show offscreen matches in each window. Moving away from that since it would just be annoying. --- autoload/matchup/matchparen.vim | 37 ++++++++++++++------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/autoload/matchup/matchparen.vim b/autoload/matchup/matchparen.vim index b790f4b..3d41954 100644 --- a/autoload/matchup/matchparen.vim +++ b/autoload/matchup/matchparen.vim @@ -49,6 +49,8 @@ function! matchup#matchparen#enable() " {{{1 autocmd InsertLeave * call s:matchparen.highlight(1) augroup END + call s:init_match_popup() + if has('vim_starting') " prevent this from autoloading during timer callback at startup if g:matchup_matchparen_deferred @@ -136,9 +138,7 @@ function! s:matchparen.clear() abort dict " {{{1 unlet! w:matchup_match_id_list endif - if exists('w:match_popup') - call popup_hide(w:match_popup) - endif + call popup_hide(s:match_popup) if exists('w:matchup_oldstatus') let &l:statusline = w:matchup_oldstatus unlet w:matchup_oldstatus @@ -489,19 +489,6 @@ function s:matchparen.transmute_reset() abort dict endif endfunction -" }}}1 -function! s:init_popup() abort " {{{1 - if exists('w:match_popup') - " echoerr 'Match popup already exists in this window.' - return - endif - " Create a popup and store its winid - let w:match_popup = popup_create('', { - \ 'hidden': v:true, - \ }) - call popup_hide(w:match_popup) " TODO 'hidden' in popup_create-usage unimplemented -endfunction - " }}}1 function! s:do_offscreen(current) " {{{1 let l:offscreen = {} @@ -548,9 +535,17 @@ function! s:do_offscreen_statusline(offscreen) " {{{1 endfunction " }}}1 -function! s:do_offscreen_popup(offscreen) " {{{1 - call s:init_popup() " TODO Do in WinNew autocmd +function! s:init_match_popup() abort " {{{1 + call assert_false(exists('s:match_popup'), 'Popup already exists.') + " Create a popup and store its winid + let s:match_popup = popup_create('', { + \ 'hidden': v:true, + \ }) + call popup_hide(s:match_popup) " TODO 'hidden' in popup_create-usage unimplemented +endfunction +" }}}1 +function! s:do_offscreen_popup(offscreen) " {{{1 " Screen position of top-left corner of current window let [l:row, l:col] = win_screenpos(winnr()) let l:col += (&number || &relativenumber ? &numberwidth : 0) @@ -559,14 +554,14 @@ function! s:do_offscreen_popup(offscreen) " {{{1 let l:line = a:offscreen.lnum < line('.') ? l:row : l:row + l:height - 1 if l:line == winline() | return | endif " If popup would overlap with cursor - call popup_move(w:match_popup, { + call popup_move(s:match_popup, { \ 'line': line, \ 'col': col, \ 'maxheight': 1, \ }) " Set popup text - call setbufline(winbufnr(w:match_popup), 1, getline(a:offscreen.lnum)) - call popup_show(w:match_popup) + call setbufline(winbufnr(s:match_popup), 1, getline(a:offscreen.lnum)) + call popup_show(s:match_popup) endfunction " }}}1 From d1ac1d0c5d5a812ce74662dd5782e15ba7cafdae Mon Sep 17 00:00:00 2001 From: Rafael Bodill Date: Sun, 1 Sep 2019 21:39:38 +0300 Subject: [PATCH 3/6] Introduce new "popup" match display Using Neovim's floating window, or Vim 8.x popup. --- autoload/matchup/matchparen.vim | 81 +++++++++++++++++++++++++++++++++ doc/matchup.txt | 4 +- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/autoload/matchup/matchparen.vim b/autoload/matchup/matchparen.vim index 6abaf9c..be93fb3 100644 --- a/autoload/matchup/matchparen.vim +++ b/autoload/matchup/matchparen.vim @@ -9,6 +9,8 @@ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim +let s:winid = 0 + function! matchup#matchparen#init_module() " {{{1 if !g:matchup_matchparen_enabled | return | endif @@ -508,6 +510,8 @@ function! s:do_offscreen(current, method) " {{{1 call s:do_offscreen_statusline(l:offscreen, 0) elseif a:method ==# 'status_manual' call s:do_offscreen_statusline(l:offscreen, 1) + elseif a:method ==# 'popup' + call s:do_offscreen_popup(l:offscreen) endif endfunction @@ -536,7 +540,84 @@ function! s:do_offscreen_statusline(offscreen, manual) " {{{1 endfunction " }}}1 +function! s:do_offscreen_popup(offscreen) " {{{1 + let l:original_filetype = &filetype + if exists('*nvim_open_win') + " neovim floating window + call s:close_floating_win() + + " Set default width and height for now. + let buf = nvim_create_buf(v:false, v:false) + let s:winid = nvim_open_win(buf, v:false, { + \ 'relative': 'cursor', + \ 'row': 1, + \ 'col': 0, + \ 'width': 42, + \ 'height': &previewheight, + \ 'style': 'minimal' + \ }) + + call nvim_buf_set_var(buf, 'cursorword', 0) + call nvim_buf_set_option(buf, 'filetype', l:original_filetype) + call nvim_buf_set_option(buf, 'buftype', 'nofile') + call nvim_buf_set_option(buf, 'bufhidden', 'delete') + call nvim_buf_set_option(buf, 'swapfile', v:false) + + " assumes cursor is in original window + autocmd matchup_matchparen CursorMoved ++once + \ call s:close_floating_win() + + elseif exists('*popup_create') + " vim8 popup + let s:winid = popup_create('', { + \ 'line': 'cursor+1', + \ 'col': 'cursor', + \ 'moved': 'any', + \ }) + + call setbufvar(winbufnr(s:winid), 'cursorword', 0) + call setbufvar(winbufnr(s:winid), '&filetype', l:original_filetype) + endif + + call s:populate_floating_win(a:offscreen) +endfunction + +" }}}1 +function! s:populate_floating_win(offscreen) " {{{1 + let l:adjust = matchup#quirks#status_adjust(a:offscreen) + let l:lnum = a:offscreen.lnum + l:adjust + let l:line = getline(l:lnum) + + let l:body = split(l:line, '\n') + let body_length = len(l:body) + let height = min([body_length, &previewheight]) + + if exists('*nvim_open_win') + " neovim floating win + let width = max(map(copy(l:body), 'strdisplaywidth(v:val)')) + call nvim_win_set_width(s:winid, width) + call nvim_win_set_height(s:winid, height) + + call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, []) + call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, l:body) + call nvim_win_set_cursor(s:winid, [1,0]) + + elseif exists('*popup_create') + " vim8 popup + call popup_settext(s:winid, l:body) + endif +endfunction + +" }}}1 +function! s:close_floating_win() " {{{1 + if win_id2win(s:winid) > 0 + execute win_id2win(s:winid) . 'wincmd c' + endif + let s:winid = 0 +endfunction + +" }}}1 function! MatchupStatusOffscreen() " {{{1 return substitute(get(w:, 'matchup_statusline', ''), \ '%<\|%#\w*#', '', 'g') diff --git a/doc/matchup.txt b/doc/matchup.txt index d137188..a0c4314 100644 --- a/doc/matchup.txt +++ b/doc/matchup.txt @@ -612,7 +612,7 @@ Module matchparen~ Sets the method to use to show off-screen matches. Possible values are: - `'status' (default): Replace the |status-line| for off-screen matches. + `'status'` (default): Replace the |status-line| for off-screen matches. If a match is off of the screen, the line belonging to that match will be displayed syntax-highlighted in the status line along with the line number @@ -623,6 +623,8 @@ Module matchparen~ `'status_manual'`: Compute the status-line but do not display it (future extension). + `'popup'`: Use neovim floating window or vim8 popup to show match. + scrolloff~ When enabled, off-screen matches will not be shown in the statusline while the cursor is at the screen edge (respects the value of 'scrolloff'). From 588333bec462257ce932e2f71850f7c24b55e15e Mon Sep 17 00:00:00 2001 From: "Andy K. Massimino" Date: Thu, 5 Sep 2019 19:57:17 -0400 Subject: [PATCH 4/6] Include number in popup --- autoload/matchup/matchparen.vim | 46 +++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/autoload/matchup/matchparen.vim b/autoload/matchup/matchparen.vim index d2b13a4..906095b 100644 --- a/autoload/matchup/matchparen.vim +++ b/autoload/matchup/matchparen.vim @@ -45,7 +45,7 @@ function! matchup#matchparen#enable() " {{{1 endif autocmd BufReadPost * call s:matchparen.transmute_reset() autocmd WinLeave,BufLeave * call s:matchparen.clear() - autocmd InsertEnter,Insertchange * call s:matchparen.highlight(1, 1) + autocmd InsertEnter,InsertChange * call s:matchparen.highlight(1, 1) autocmd InsertLeave * call s:matchparen.highlight(1) augroup END @@ -138,7 +138,10 @@ function! s:matchparen.clear() abort dict " {{{1 unlet! w:matchup_match_id_list endif - call popup_hide(s:match_popup) + if exists('s:match_popup') + call popup_hide(s:match_popup) + endif + if exists('w:matchup_oldstatus') let &l:statusline = w:matchup_oldstatus unlet w:matchup_oldstatus @@ -542,31 +545,42 @@ endfunction " }}}1 function! s:init_match_popup() abort " {{{1 - call assert_false(exists('s:match_popup'), 'Popup already exists.') - " Create a popup and store its winid + if exists('s:match_popup') + return + endif + + " create a popup and store its winid let s:match_popup = popup_create('', { \ 'hidden': v:true, - \ }) - call popup_hide(s:match_popup) " TODO 'hidden' in popup_create-usage unimplemented + \}) + + " in case 'hidden' in popup_create-usage is unimplemented + call popup_hide(s:match_popup) endfunction " }}}1 function! s:do_offscreen_popup(offscreen) " {{{1 - " Screen position of top-left corner of current window + " screen position of top-left corner of current window let [l:row, l:col] = win_screenpos(winnr()) - let l:col += (&number || &relativenumber ? &numberwidth : 0) - \ + &foldcolumn " TODO Take signcolumn into consideration - let l:height = winheight(0) " Height of current window + let l:height = winheight(0) " height of current window let l:line = a:offscreen.lnum < line('.') ? l:row : l:row + l:height - 1 - if l:line == winline() | return | endif " If popup would overlap with cursor + + " if popup would overlap with cursor + if l:line == winline() | return | endif call popup_move(s:match_popup, { - \ 'line': line, - \ 'col': col, + \ 'line': l:line, + \ 'col': l:col, \ 'maxheight': 1, - \ }) - " Set popup text - call setbufline(winbufnr(s:match_popup), 1, getline(a:offscreen.lnum)) + \}) + + " set popup text + let l:text = '' + if &number || &relativenumber + let l:text = printf('%*S ', wincol()-virtcol('.')-1, a:offscreen.lnum) + endif + let l:text .= getline(a:offscreen.lnum) . ' ' + call setbufline(winbufnr(s:match_popup), 1, l:text) call popup_show(s:match_popup) endfunction From 7ff09d9372b33acb539bfac201face9762e902fe Mon Sep 17 00:00:00 2001 From: "Andy K. Massimino" Date: Fri, 6 Sep 2019 19:27:58 -0400 Subject: [PATCH 5/6] Use floating window strategy for nvim --- autoload/matchup/matchparen.vim | 46 +++++++++++++-------------------- doc/matchup.txt | 3 ++- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/autoload/matchup/matchparen.vim b/autoload/matchup/matchparen.vim index 01bc877..a4e060b 100644 --- a/autoload/matchup/matchparen.vim +++ b/autoload/matchup/matchparen.vim @@ -9,8 +9,6 @@ scriptencoding utf-8 let s:save_cpo = &cpo set cpo&vim -let s:winid = 0 - function! matchup#matchparen#init_module() " {{{1 if !g:matchup_matchparen_enabled | return | endif @@ -517,7 +515,11 @@ function! s:do_offscreen(current, method) " {{{1 elseif a:method ==# 'status_manual' call s:do_offscreen_statusline(l:offscreen, 1) elseif a:method ==# 'popup' - call s:do_offscreen_popup(l:offscreen) + if has('nvim') + call s:do_offscreen_popup_nvim(l:offscreen) + else + call s:do_offscreen_popup(l:offscreen) + endif endif endfunction @@ -547,7 +549,7 @@ endfunction " }}}1 function! s:init_match_popup() abort " {{{1 - if exists('s:match_popup') + if !exists('*popup_create') || exists('s:match_popup') return endif @@ -613,18 +615,7 @@ function! s:do_offscreen_popup_nvim(offscreen) " {{{1 " assumes cursor is in original window autocmd matchup_matchparen CursorMoved ++once - \ call s:close_floating_win() - - elseif exists('*popup_create') - " vim8 popup - let s:winid = popup_create('', { - \ 'line': 'cursor+1', - \ 'col': 'cursor', - \ 'moved': 'any', - \ }) - - call setbufvar(winbufnr(s:winid), 'cursorword', 0) - call setbufvar(winbufnr(s:winid), '&filetype', l:original_filetype) + \ call s:close_floating_win() endif call s:populate_floating_win(a:offscreen) @@ -643,25 +634,24 @@ function! s:populate_floating_win(offscreen) " {{{1 if exists('*nvim_open_win') " neovim floating win let width = max(map(copy(l:body), 'strdisplaywidth(v:val)')) - call nvim_win_set_width(s:winid, width) - call nvim_win_set_height(s:winid, height) + call nvim_win_set_width(s:float_id, width) + call nvim_win_set_height(s:float_id, height) - call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, []) - call nvim_buf_set_lines(winbufnr(s:winid), 0, -1, v:false, l:body) - call nvim_win_set_cursor(s:winid, [1,0]) - - elseif exists('*popup_create') - " vim8 popup - call popup_settext(s:winid, l:body) + call nvim_buf_set_lines(winbufnr(s:float_id), 0, -1, v:false, []) + call nvim_buf_set_lines(winbufnr(s:float_id), 0, -1, v:false, l:body) + call nvim_win_set_cursor(s:float_id, [1,0]) endif endfunction " }}}1 function! s:close_floating_win() " {{{1 - if win_id2win(s:winid) > 0 - execute win_id2win(s:winid) . 'wincmd c' + if !exists('s:float_id') + return endif - let s:winid = 0 + if win_id2win(s:float_id) > 0 + execute win_id2win(s:float_id) . 'wincmd c' + endif + let s:float_id = 0 endfunction " }}}1 diff --git a/doc/matchup.txt b/doc/matchup.txt index a0c4314..682e1bb 100644 --- a/doc/matchup.txt +++ b/doc/matchup.txt @@ -623,7 +623,8 @@ Module matchparen~ `'status_manual'`: Compute the status-line but do not display it (future extension). - `'popup'`: Use neovim floating window or vim8 popup to show match. + `'popup'`: Use a popup window (requires at least vim 8.1.1406) or + a floating window (in neovim) to show the off-screen match. scrolloff~ When enabled, off-screen matches will not be shown in the statusline while From c1ab8c9e8e86e036a5dce6a4051c053ee904ab70 Mon Sep 17 00:00:00 2001 From: "Andy K. Massimino" Date: Fri, 6 Sep 2019 22:50:35 -0400 Subject: [PATCH 6/6] Use line adjustment for popup --- autoload/matchup/matchparen.vim | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/autoload/matchup/matchparen.vim b/autoload/matchup/matchparen.vim index a4e060b..f517ee2 100644 --- a/autoload/matchup/matchparen.vim +++ b/autoload/matchup/matchparen.vim @@ -567,7 +567,9 @@ function! s:do_offscreen_popup(offscreen) " {{{1 " screen position of top-left corner of current window let [l:row, l:col] = win_screenpos(winnr()) let l:height = winheight(0) " height of current window - let l:line = a:offscreen.lnum < line('.') ? l:row : l:row + l:height - 1 + let l:adjust = matchup#quirks#status_adjust(a:offscreen) + let l:lnum = a:offscreen.lnum + l:adjust + let l:line = l:lnum < line('.') ? l:row : l:row + l:height - 1 " if popup would overlap with cursor if l:line == winline() | return | endif @@ -581,9 +583,12 @@ function! s:do_offscreen_popup(offscreen) " {{{1 " set popup text let l:text = '' if &number || &relativenumber - let l:text = printf('%*S ', wincol()-virtcol('.')-1, a:offscreen.lnum) + let l:text = printf('%*S ', wincol()-virtcol('.')-1, l:lnum) + endif + let l:text .= getline(l:lnum) . ' ' + if l:adjust + let l:text .= '… ' . a:offscreen.match . ' ' endif - let l:text .= getline(a:offscreen.lnum) . ' ' call setbufline(winbufnr(s:match_popup), 1, l:text) call popup_show(s:match_popup) endfunction @@ -605,7 +610,7 @@ function! s:do_offscreen_popup_nvim(offscreen) " {{{1 \ 'width': 42, \ 'height': &previewheight, \ 'style': 'minimal' - \ }) + \}) call nvim_buf_set_var(buf, 'cursorword', 0) call nvim_buf_set_option(buf, 'filetype', l:original_filetype)