From 7ceb58b2aadfcf0f5e99da83626cf88d282159b2 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Tue, 4 Feb 2020 00:35:57 +0900 Subject: [PATCH] [vim] Popup window support for both Vim and Neovim e.g. let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } } Based on the code from https://github.com/junegunn/fzf.vim/issues/821#issuecomment-581273191 by @lacygoill. --- README-VIM.md | 56 +++++++++++++++------------------------- doc/fzf.txt | 47 +++++++++++++++++----------------- plugin/fzf.vim | 69 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 111 insertions(+), 61 deletions(-) diff --git a/README-VIM.md b/README-VIM.md index 554d748a..103b8e52 100644 --- a/README-VIM.md +++ b/README-VIM.md @@ -184,6 +184,7 @@ The following table summarizes the available options. | `dir` | string | Working directory | | `up`/`down`/`left`/`right` | number/string | (Layout) Window position and size (e.g. `20`, `50%`) | | `window` (Vim 8 / Neovim) | string | (Layout) Command to open fzf window (e.g. `vertical aboveleft 30new`) | +| `window` (Vim 8 / Neovim) | dict | (Layout) Popup window settings (e.g. `{'width': 0.9, 'height': 0.6}`) | `options` entry can be either a string or a list. For simple cases, string should suffice, but prefer to use list type to avoid escaping issues. @@ -193,6 +194,16 @@ call fzf#run({'options': '--reverse --prompt "C:\\Program Files\\"'}) call fzf#run({'options': ['--reverse', '--prompt', 'C:\Program Files\']}) ``` +When `window` entry is a dictionary, fzf will start in a popup window. The +following options are allowed: + +- Required: + - `width` [float] + - `height` [float] +- Optional: + - `highlight` [string default `'Comment'`]: Highlight group for border + - `rounded` [boolean default `v:true`]: Use rounded border + `fzf#wrap` ---------- @@ -276,44 +287,17 @@ The latest versions of Vim and Neovim include builtin terminal emulator - On Terminal Vim with a non-default layout - `call fzf#run({'left': '30%'})` or `let g:fzf_layout = {'left': '30%'}` -#### Starting fzf in Neovim floating window +#### Starting fzf in a popup window ```vim -" Using floating windows of Neovim to start fzf -if has('nvim') - function! FloatingFZF(width, height, border_highlight) - function! s:create_float(hl, opts) - let buf = nvim_create_buf(v:false, v:true) - let opts = extend({'relative': 'editor', 'style': 'minimal'}, a:opts) - let win = nvim_open_win(buf, v:true, opts) - call setwinvar(win, '&winhighlight', 'NormalFloat:'.a:hl) - call setwinvar(win, '&colorcolumn', '') - return buf - endfunction - - " Size and position - let width = float2nr(&columns * a:width) - let height = float2nr(&lines * a:height) - let row = float2nr((&lines - height) / 2) - let col = float2nr((&columns - width) / 2) - - " Border - let top = '╭' . repeat('─', width - 2) . '╮' - let mid = '│' . repeat(' ', width - 2) . '│' - let bot = '╰' . repeat('─', width - 2) . '╯' - let border = [top] + repeat([mid], height - 2) + [bot] - - " Draw frame - let s:frame = s:create_float(a:border_highlight, {'row': row, 'col': col, 'width': width, 'height': height}) - call nvim_buf_set_lines(s:frame, 0, -1, v:true, border) - - " Draw viewport - call s:create_float('Normal', {'row': row + 1, 'col': col + 2, 'width': width - 4, 'height': height - 2}) - autocmd BufWipeout execute 'bwipeout' s:frame - endfunction - - let g:fzf_layout = { 'window': 'call FloatingFZF(0.9, 0.6, "Comment")' } -endif +" Required: +" - width [float] +" - height [float] +" +" Optional: +" - highlight [string default 'Comment']: Highlight group for border +" - rounded [boolean default v:true]: Use rounded border +let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } } ``` #### Hide statusline diff --git a/doc/fzf.txt b/doc/fzf.txt index d0744c73..51f0d43f 100644 --- a/doc/fzf.txt +++ b/doc/fzf.txt @@ -1,4 +1,4 @@ -fzf.txt fzf Last change: November 23 2019 +fzf.txt fzf Last change: February 3 2020 FZF - TABLE OF CONTENTS *fzf* *fzf-toc* ============================================================================== @@ -11,7 +11,7 @@ FZF - TABLE OF CONTENTS *fzf* *fzf-to fzf#wrap Tips fzf inside terminal buffer - Starting fzf in Neovim floating window + Starting fzf in a popup window Hide statusline License @@ -204,6 +204,7 @@ The following table summarizes the available options. `dir` | string | Working directory `up` / `down` / `left` / `right` | number/string | (Layout) Window position and size (e.g. `20` , `50%` ) `window` (Vim 8 / Neovim) | string | (Layout) Command to open fzf window (e.g. `vertical aboveleft 30new` ) + `window` (Vim 8 / Neovim) | dict | (Layout) Popup window settings (e.g. `{'width': 0.9, 'height': 0.6}` ) ---------------------------+---------------+---------------------------------------------------------------------- `options` entry can be either a string or a list. For simple cases, string @@ -212,6 +213,16 @@ should suffice, but prefer to use list type to avoid escaping issues. call fzf#run({'options': '--reverse --prompt "C:\\Program Files\\"'}) call fzf#run({'options': ['--reverse', '--prompt', 'C:\Program Files\']}) < +When `window` entry is a dictionary, fzf will start in a popup window. The +following options are allowed: + + - Required: + - `width` [float] + - `height` [float] + - Optional: + - `highlight` [string default `'Comment'`]: Highlight group for border + - `rounded` [boolean default `v:true`]: Use rounded border + FZF#WRAP ============================================================================== @@ -291,29 +302,17 @@ The latest versions of Vim and Neovim include builtin terminal emulator - `call fzf#run({'left': '30%'})` or `let g:fzf_layout = {'left': '30%'}` -Starting fzf in Neovim floating window~ - *fzf-starting-fzf-in-neovim-floating-window* +Starting fzf in a popup window~ + *fzf-starting-fzf-in-a-popup-window* > - " Using floating windows of Neovim to start fzf - if has('nvim') - let $FZF_DEFAULT_OPTS .= ' --border --margin=0,2' - - function! FloatingFZF() - let width = float2nr(&columns * 0.9) - let height = float2nr(&lines * 0.6) - let opts = { 'relative': 'editor', - \ 'row': (&lines - height) / 2, - \ 'col': (&columns - width) / 2, - \ 'width': width, - \ 'height': height } - - let win = nvim_open_win(nvim_create_buf(v:false, v:true), v:true, opts) - call setwinvar(win, '&winhighlight', 'NormalFloat:Normal') - endfunction - - let g:fzf_layout = { 'window': 'call FloatingFZF()' } - endif - + " Required: + " - width [float] + " - height [float] + " + " Optional: + " - highlight [string default 'Comment']: Highlight group for border + " - rounded [boolean default v:true]: Use rounded border + let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } } < Hide statusline~ diff --git a/plugin/fzf.vim b/plugin/fzf.vim index 21b7d66b..a646432b 100644 --- a/plugin/fzf.vim +++ b/plugin/fzf.vim @@ -650,7 +650,11 @@ function! s:split(dict) let ppos = s:getpos() try if s:present(a:dict, 'window') - execute 'keepalt' a:dict.window + if type(a:dict.window) == type({}) + call s:popup(a:dict.window) + else + execute 'keepalt' a:dict.window + endif elseif !s:splittable(a:dict) execute (tabpagenr()-1).'tabnew' else @@ -798,6 +802,69 @@ function! s:callback(dict, lines) abort endif endfunction +if has('nvim') + function s:create_popup(hl, opts) abort + let buf = nvim_create_buf(v:false, v:true) + let opts = extend({'relative': 'editor', 'style': 'minimal'}, a:opts) + let border = has_key(opts, 'border') ? remove(opts, 'border') : [] + let win = nvim_open_win(buf, v:true, opts) + call setwinvar(win, '&winhighlight', 'NormalFloat:'..a:hl) + call setwinvar(win, '&colorcolumn', '') + if !empty(border) + call nvim_buf_set_lines(buf, 0, -1, v:true, border) + endif + return buf + endfunction +else + function! s:create_popup(hl, opts) abort + let is_frame = has_key(a:opts, 'border') + let buf = is_frame ? '' : term_start(&shell, #{hidden: 1}) + let id = popup_create(buf, #{ + \ line: a:opts.row, + \ col: a:opts.col, + \ minwidth: a:opts.width, + \ minheight: a:opts.height, + \ zindex: 50 - is_frame, + \ }) + + if is_frame + call setwinvar(id, '&wincolor', a:hl) + call setbufline(winbufnr(id), 1, a:opts.border) + execute 'autocmd BufWipeout * ++once call popup_close('..id..')' + else + execute 'autocmd BufWipeout * ++once bwipeout! '..buf + endif + return winbufnr(id) + endfunction +endif + +function! s:popup(opts) abort + " Size and position + let width = float2nr(&columns * a:opts.width) + let height = float2nr(&lines * a:opts.height) + let row = float2nr((&lines - height) / 2) + let col = float2nr((&columns - width) / 2) + + " Border + let edges = get(a:opts, 'rounded', 1) ? ['╭', '╮', '╰', '╯'] : ['┌', '┐', '└', '┘'] + let bar = repeat('─', width - 2) + let top = edges[0] .. bar .. edges[1] + let mid = '│' .. repeat(' ', width - 2) .. '│' + let bot = edges[2] .. bar .. edges[3] + let border = [top] + repeat([mid], height - 2) + [bot] + + let highlight = get(a:opts, 'highlight', 'Comment') + let frame = s:create_popup(highlight, { + \ 'row': row, 'col': col, 'width': width, 'height': height, 'border': border + \ }) + call s:create_popup('Normal', { + \ 'row': row + 1, 'col': col + 2, 'width': width - 4, 'height': height - 2 + \ }) + if has('nvim') + execute 'autocmd BufWipeout bwipeout '..frame + endif +endfunction + let s:default_action = { \ 'ctrl-t': 'tab split', \ 'ctrl-x': 'split',