From f0f56b956961a4520fd10a6797f493d9d04735d7 Mon Sep 17 00:00:00 2001 From: Andy Stewart Date: Tue, 27 Aug 2019 16:02:51 +0100 Subject: [PATCH] Use floating windows for hunk previews on Neovim. --- README.mkd | 6 ++ autoload/gitgutter/hunk.vim | 123 ++++++++++++++++++++++++++++++------ doc/gitgutter.txt | 7 ++ plugin/gitgutter.vim | 4 ++ 4 files changed, 120 insertions(+), 20 deletions(-) diff --git a/README.mkd b/README.mkd index 3261f90..fa4d91a 100644 --- a/README.mkd +++ b/README.mkd @@ -269,6 +269,7 @@ You can customise: * Whether vim-gitgutter runs asynchronously (defaults to yes) * Whether to clobber or preserve non-gitgutter signs * The priority of gitgutter's signs. +* Whether to use a floating window for hunk previews (Neovim only) Please note that vim-gitgutter won't override any colours or highlights you've set in your colorscheme. @@ -454,6 +455,11 @@ let g:gitgutter_async = 0 ``` +#### To use floating windows for hunk previews + +Add `let g:gitgutter_preview_win_floating = 1` to your vimrc. This only takes effect on Neovim. + + ### Extensions #### Operate on every line in a hunk diff --git a/autoload/gitgutter/hunk.vim b/autoload/gitgutter/hunk.vim index f03ab57..38bfce2 100644 --- a/autoload/gitgutter/hunk.vim +++ b/autoload/gitgutter/hunk.vim @@ -1,3 +1,5 @@ +let s:winid = 0 + function! gitgutter#hunk#set_hunks(bufnr, hunks) abort call gitgutter#utility#setbufvar(a:bufnr, 'hunks', a:hunks) call s:reset_summary(a:bufnr) @@ -192,7 +194,7 @@ endfunction function! s:hunk_op(op, ...) let bufnr = bufnr('') - if &previewwindow + if s:in_hunk_preview_window() if string(a:op) =~ '_stage' " combine hunk-body in preview window with updated hunk-header let hunk_body = getline(1, '$') @@ -214,8 +216,8 @@ function! s:hunk_op(op, ...) let hunk_diff = join(hunk_header + hunk_body, "\n")."\n" - wincmd p - pclose + call s:goto_original_window() + call s:close_hunk_preview_window() call s:stage(hunk_diff) endif @@ -297,10 +299,12 @@ function! s:preview(hunk_diff) let header = lines[0:4] let body = lines[5:] - call s:goto_hunk_preview_window() + call s:open_hunk_preview_window() call s:populate_hunk_preview_window(header, body) call s:enable_staging_from_hunk_preview_window() - call s:goto_original_window() + if &previewwindow + call s:goto_original_window() + endif endfunction @@ -372,36 +376,115 @@ function! s:line_adjustment_for_current_hunk() abort endfunction -function! s:goto_hunk_preview_window() - silent! wincmd P - if !&previewwindow - noautocmd execute g:gitgutter_preview_win_location &previewheight 'new' - set previewwindow +function! s:in_hunk_preview_window() + if g:gitgutter_preview_win_floating + return win_id2win(s:winid) == winnr() + else + return &previewwindow endif endfunction -function! s:populate_hunk_preview_window(header, body) - let b:hunk_header = a:header +" Floating window: does not move cursor to floating window. +" Preview window: moves cursor to preview window. +function! s:open_hunk_preview_window() + if g:gitgutter_preview_win_floating + call s:close_hunk_preview_window() - let body_length = len(a:body) - let previewheight = min([body_length, &previewheight]) - execute 'resize' previewheight + let buf = nvim_create_buf(v:false, v:false) + " Set default width and height for now. + 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_option(buf, 'filetype', 'diff') + 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) - setlocal noreadonly modifiable filetype=diff buftype=nofile bufhidden=delete noswapfile - execute "%delete_" - call setline(1, a:body) - normal! gg + " Assumes cursor is in original window. + autocmd CursorMoved ++once call s:close_hunk_preview_window() + + return + endif + + silent! wincmd P + if !&previewwindow + noautocmd execute g:gitgutter_preview_win_location &previewheight 'new' + set previewwindow + setlocal filetype=diff buftype=nofile bufhidden=delete + " Reset some defaults in case someone else has changed them. + setlocal noreadonly modifiable noswapfile + endif endfunction +" Floating window: does not care where cursor is. +" Preview window: assumes cursor is in preview window. +function! s:populate_hunk_preview_window(header, body) + let body_length = len(a:body) + let height = min([body_length, &previewheight]) + + if g:gitgutter_preview_win_floating + " Assumes cursor is not in previewing window. + call nvim_buf_set_var(winbufnr(s:winid), 'hunk_header', a:header) + + let width = max(map(copy(a: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, a:body) + call nvim_win_set_cursor( s:winid, [1,0]) + + else + let b:hunk_header = a:header + execute 'resize' height + + execute "%delete_" + call setline(1, a:body) + normal! gg + endif +endfunction + + +" Floating window: does not care where cursor is. +" Preview window: assumes cursor is in preview window. function! s:enable_staging_from_hunk_preview_window() + if g:gitgutter_preview_win_floating + " Move cursor to previewing window without triggering autocmd which closes it. + " This is necessary because there is no way to set a cabbrev in an arbitrary buffer. + execute 'noautocmd' win_id2win(s:winid).'wincmd w' + endif + cnoreabbrev w getcmdtype() == ':' && getcmdline() == 'w' ? 'GitGutterStageHunk' : 'w' - " Staging hunk from the preview window closes the window anyway. + " Staging hunk from the previewing window closes the window anyway. cnoreabbrev wq getcmdtype() == ':' && getcmdline() == 'wq' ? 'GitGutterStageHunk' : 'wq' + + if g:gitgutter_preview_win_floating + " Move cursor back without triggering autocmd. + noautocmd wincmd p + endif endfunction function! s:goto_original_window() noautocmd wincmd p endfunction + + +function! s:close_hunk_preview_window() + if g:gitgutter_preview_win_floating + if win_id2win(s:winid) > 0 + execute win_id2win(s:winid).'wincmd c' + endif + let s:winid = 0 + return + endif + + pclose +endfunction diff --git a/doc/gitgutter.txt b/doc/gitgutter.txt index 5dafcbb..672efcf 100644 --- a/doc/gitgutter.txt +++ b/doc/gitgutter.txt @@ -437,6 +437,13 @@ it in your |vimrc|: User-defined (graphical Vim) highlight SignColumn guibg={whatever} + *g:gitgutter_preview_win_floating* +Default: 1 (Neovim) + 0 (Vim) + +Whether to use floating windows for hunk previews. Only takes effect on +Neovim. + *g:gitgutter_terminal_reports_focus* Default: 1 diff --git a/plugin/gitgutter.vim b/plugin/gitgutter.vim index 0901f0b..328b20f 100644 --- a/plugin/gitgutter.vim +++ b/plugin/gitgutter.vim @@ -23,6 +23,10 @@ function! s:set(var, default) abort endfunction call s:set('g:gitgutter_preview_win_location', 'bo') +if !exists('*nvim_open_win') + let g:gitgutter_preview_win_floating = 0 +endif +call s:set('g:gitgutter_preview_win_floating', 1) call s:set('g:gitgutter_enabled', 1) call s:set('g:gitgutter_max_signs', 500) call s:set('g:gitgutter_signs', 1)