26 Commits
Author SHA1 Message Date
Junegunn Choi 5a9f08fb36 Show a directory entry
:Locate returns directories and its sink opens them, but the readable-file
guard made the key silently do nothing for one.
2026-08-27 00:02:54 +09:00
Junegunn Choi a92f1cce23 Reuse s:edit_cmds for a tag in the file already shown
The tag branch edited the file unconditionally, unlike every other kind, so
showing a tag in the window's own buffer reloaded it and reset the undo
history. s:tags_sink does not re-edit either.
2026-08-27 00:02:54 +09:00
Junegunn Choi 05b2324b75 Canonicalize the two fzf key aliases before comparing
fzf reads 'return' as 'enter' and 'alt-return' as 'alt-enter', so
show_key='alt-return' passed the check against the default paste key and
--expect closed fzf while the footer advertised Show. ctrl-m is not an alias,
it goes through the generic ctrl- branch to its own event.
2026-08-27 00:01:26 +09:00
Junegunn Choi c63f25a2ba Document that a command's own key withholds the show key
Missed when the check learned about them. Also hyphenate 'comma-separated'.
2026-08-26 23:40:32 +09:00
Junegunn Choi e541ce6037 Keep a newline in an entry from splitting the fifo payload
The fifo is read a line at a time, but ':GFiles' reads NUL separated names
from 'git ls-files -z', so an entry can hold a newline. It arrived as two
callbacks, and a fragment that named another tracked file opened that one
instead.

The binding swaps the newline for \1 and s:show_entry puts it back, so a
multi-line entry stays one payload.
2026-08-26 23:28:07 +09:00
Junegunn Choi cb6ea5e06f Prefer a relative entry over tilde expansion in s:in_dir
Only :History reports a path as '~/...', but every command went through the
same branch, so an entry under a directory literally named '~' resolved to
the home directory and Show opened the wrong file or nothing. Resolve
against the run directory first and fall back to the home reading.
2026-08-26 22:47:46 +09:00
Junegunn Choi 4ae5a659f8 Fold g:fzf_action keys before the show key check
fzf#wrap turns them into --expect, which fzf matches with the same case
folding as a binding, so g:fzf_action = {'CTRL-O': 'split'} slipped past the
check. The footer advertised Show while --expect closed fzf instead.
2026-08-26 22:46:09 +09:00
Junegunn Choi b83a8e5ddd Show a buffer-local entry in the buffer the run started on
BLines, BTags and a lowercase mark carry a position but no buffer, so they
landed wherever the window had got to. The window can leave that buffer
while fzf is open, including through another run's show key, and BLines then
applied its line number to the wrong buffer, clamped to its length.

The callback carries the buffer alongside the window and returns to it
first, or does nothing if it is gone.
2026-08-26 21:25:33 +09:00
Junegunn Choi 091f9c92f9 Withhold the show key from a key the command binds
Setting show_key to a key a command binds for its own hint, ctrl-alt-x on
Buffers or alt-a on Rg, advertised both in the footer while the command's
binding won by coming later in the options. '_hint' entries now carry the
fzf key names behind the label, which is what the check compares, since a
label like 'A-a/d' cannot be.
2026-08-26 20:52:47 +09:00
Junegunn Choi a246483a09 Add ctrl-o to show the entry without closing fzf
Files, GFiles, GFiles?, Rg/Ag, Buffers, History, Locate, Lines, BLines,
Tags, BTags, Marks and Changes. Writes the entry to an fzf#vim#ipc fifo, so
it needs the asynchronous fzf#run from junegunn/fzf@5cb7bab7
(junegunn/fzf#4897, 0.74.4), which the g:loaded_fzf check detects.

Commands declare '_show' for the entry kind and '_hint' for footer keys, so
the footer and the binding cannot disagree. s:can_show withholds both when
the run would block or the window to open in is not visible.

g:fzf_vim.show_key configures the key, empty to turn it off, comma separated
to bind several ('ctrl-o,double-click'). Named for its 'Show' footer label,
since Enter already occupies 'Open' there. Only the first key is labelled,
and a clash on any of them drops all of them, so the footer cannot list a
key that does nothing. Binding Enter drops its 'Open' hint too, since fzf
accepts on Enter rather than through g:fzf_action.

s:show_entry parses each entry the way that command's sink does and runs
win_execute() in the window fzf started from. That keeps the focus in fzf
and works from a popup, which win_gotoid() cannot leave. It opens with
'keepalt keepjumps hide', so stepping through entries leaves the alternate
file and the jumplist unchanged.

fzf#run is asynchronous, so runs can overlap, and a second run would stop
the first's ipc channel and overwrite the script locals its sink reads back
afterwards. Channels are now keyed by fifo path and per-run state is bound
into the callback instead, which is why Buffers, Colors, Jumps, Maps,
Helptags and complete change here.
2026-08-26 18:21:17 +09:00
Junegunn Choi d2a59a992a Use --footer for key binding hints
- Buffers: CTRL-ALT-X replaces SHIFT-DELETE to unload buffers
- History:, History/, Commits: use --scheme=history
- Bump min fzf version to 0.63.0 for --footer
2026-06-12 09:06:10 +09:00
sachit 356608e2ae Show lua colorschemes in :Colors command (#1619) 2026-06-02 11:09:22 +09:00
Junegunn Choi 7fd26e3609 Experiment: Replace '--info inline' with '--info inline-right' (#1617) 2026-05-31 23:41:34 +09:00
Junegunn Choi c2b97c62b9 Add ALT-ENTER to paste selected items into the buffer
- New fzf#vim#paste inserts selections instead of opening them
  - First item after cursor (space-padded), rest on following lines
  - Leading whitespace stripped only when pasting after text
- Commands opt in via `_paste`; key configurable with g:fzf_vim.paste_key
- Skip the key when buffer is not &modifiable
- Per command, paste:
  - path: Files, GFiles, GFiles?, Buffers, History, Locate
  - line text: Lines, BLines, Marks, Changes
  - matched text: Rg, Ag, Grep
  - tag name: Tags, BTags
  - commit hash: Commits, BCommits
2026-05-31 23:26:55 +09:00
Junegunn Choi e5b53de3d3 [Buffers] Allow selecting the current buffer
But place the initial cursor on the second item so quick switching is
still possible as before.
2026-05-24 10:51:04 +09:00
Junegunn Choi 11d44b1805 Make the appearance of History: and History/ consistent 2026-05-24 10:27:28 +09:00
Junegunn Choi b9b98ac574 Commands: Fix quotation mistake 2026-05-21 20:35:33 +09:00
Junegunn Choi 31e338e314 Buffers: SHIFT-DELETE to unload buffers
Close #75
Close #733
2026-05-21 19:04:45 +09:00
Junegunn Choi 28ed21ef3e Some facelift 2026-05-21 19:00:40 +09:00
Junegunn Choi b9624aa012 Do not display statusline when fzf is running in a floating window
Related: #1615
2026-04-10 21:30:27 +09:00
Junegunn Choi 34a564c81f Update README 2026-02-02 09:42:52 +09:00
Enno ddc14a6a54 Avoid cooked terminal mode (#1604)
as `:help system()` states

		When prepended by |:silent| the terminal will not be set to
		cooked mode.  This is meant to be used for commands that do
		not need the user to type.  It avoids stray characters showing
		up on the screen which require |CTRL-L| to remove. >
			:silent let f = system('ls *.vim')
2025-12-09 08:49:13 +09:00
Junegunn Choi 879db51d09 Update README.md 2025-09-03 09:59:37 +09:00
phanium 3725f364cc Fix cmd hisotry correctly (#1594) 2025-06-20 17:04:48 +09:00
Junegunn Choi 98dcd77a18 Fix dictionary alignments 2025-06-08 10:51:25 +09:00
Scott LaiandJunegunn Choi a2ed9fb885 Add :BMarks command (#1593)
And allow passing arguments of :marks to :Marks command. Close #1158.

Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2025-06-08 10:48:17 +09:00
5 changed files with 883 additions and 155 deletions
+45
View File
@@ -79,6 +79,7 @@ Commands
| `:BTags [QUERY]` | Tags in the current buffer |
| `:Changes` | Changelist across all open buffers |
| `:Marks` | Marks |
| `:BMarks` | Marks in the current buffer |
| `:Jumps` | Jumps |
| `:Windows` | Windows |
| `:Locate PATTERN` | `locate` command output |
@@ -95,6 +96,20 @@ Commands
- Most commands support `CTRL-T` / `CTRL-X` / `CTRL-V` key
bindings to open in a new tab, a new split, or in a new vertical split
- Most commands support `ALT-ENTER` to insert the selected items into the
current buffer instead of opening them. The inserted text depends on the
command: file path (`:Files`, `:GFiles`, `:Buffers`, `:History`, `:Locate`),
line content (`:Lines`, `:BLines`, `:Marks`, `:Changes`), matched text
(`:Rg`, `:Ag`, `:Grep`), tag name (`:Tags`, `:BTags`), or commit hash
(`:Commits`, `:BCommits`)
- Most commands support `CTRL-O` to open the current entry in the window fzf
was started from, leaving fzf open, so you can look through several entries
in a single run (`:Files`, `:GFiles`, `:Buffers`, `:History`, `:Locate`,
`:Rg`, `:Ag`, `:Grep`, `:Lines`, `:BLines`, `:Tags`, `:BTags`, `:Marks`,
`:Changes`). Requires fzf 0.74.4 or later, and is not offered in fullscreen
mode, with a layout that leaves no other window (`enew`, `tabnew`), on
Windows, or when the key is already used by `g:fzf_vim.paste_key`,
`g:fzf_action`, or the command itself, as `CTRL-ALT-X` is in `:Buffers`
- Bang-versions of the commands (e.g. `Ag!`) will open fzf in fullscreen
- You can set `g:fzf_vim.command_prefix` to give the same prefix to the commands
- e.g. `let g:fzf_vim.command_prefix = 'Fzf'` and you have `FzfFiles`, etc.
@@ -155,6 +170,36 @@ let g:fzf_vim.preview_window = []
" let g:fzf_vim.preview_bash = 'C:\Git\bin\bash.exe'
```
#### Paste key
Most commands support a key to insert the selected items into the current
buffer instead of opening them (see the command list above for what each
command inserts). Customize it with `g:fzf_vim.paste_key`.
```vim
" Key to insert the selected items into the current buffer (default: 'alt-enter')
let g:fzf_vim.paste_key = 'alt-enter'
```
#### Show key
Most commands support a key that opens the current entry in the window fzf
was started from and leaves fzf open, so you can look through several entries
in one run (see the command list above). It is labelled `Show` in the footer
to set it apart from `Enter`, which opens the entry and closes fzf. Customize
it with `g:fzf_vim.show_key`, or set it to an empty string to turn the key
off. A comma-separated list binds every key in it. It takes the key over from
`$FZF_DEFAULT_OPTS`, so pick another key or turn it off to keep a binding of
your own.
```vim
" Key to open the current entry, leaving fzf open (default: 'ctrl-o')
let g:fzf_vim.show_key = 'ctrl-o'
" Use CTRL-O and double-click
let g:fzf_vim.show_key = 'ctrl-o,double-click'
```
#### Command-level options
```vim
+702 -113
View File
File diff suppressed because it is too large Load Diff
+72 -31
View File
@@ -28,6 +28,9 @@ function! s:warn(message)
return 0
endfunction
" Channels in flight, keyed by fifo path, so concurrent fzf runs do not clash
let s:channels = {}
function! fzf#vim#ipc#start(Callback)
if !exists('*job_start') && !exists('*jobstart')
call s:warn('job_start/jobstart function not supported')
@@ -39,57 +42,95 @@ function! fzf#vim#ipc#start(Callback)
return ''
endif
call fzf#vim#ipc#stop()
let g:fzf_ipc = { 'fifo': tempname(), 'callback': a:Callback }
if !filereadable(g:fzf_ipc.fifo)
call system('mkfifo '..shellescape(g:fzf_ipc.fifo))
if v:shell_error
let fifo = tempname()
call system('mkfifo '..shellescape(fifo))
if v:shell_error
if !exists('s:mkfifo_failed')
let s:mkfifo_failed = 1
call s:warn('Failed to create fifo')
endif
return ''
endif
call fzf#vim#ipc#restart()
let s:channels[fifo] = { 'fifo': fifo, 'callback': a:Callback }
call fzf#vim#ipc#restart(fifo)
if !s:running(s:channels[fifo].job)
call fzf#vim#ipc#stop(fifo)
return ''
endif
return g:fzf_ipc.fifo
return fifo
endfunction
function! fzf#vim#ipc#restart()
if !exists('g:fzf_ipc')
" Nvim hands over everything one read produced, which can be several messages
" as well as the trailing empty element. Vim's out_cb passes one line at a
" time, so deliver line by line and both behave the same.
function! s:deliver(Callback, lines)
for line in a:lines
if !empty(line)
call call(a:Callback, [line])
endif
endfor
endfunction
" The reader is gone, so leaving the channel registered would block the next
" write on a fifo nothing reads
function! s:drop(fifo)
if has_key(s:channels, a:fifo)
call delete(remove(s:channels, a:fifo).fifo)
endif
endfunction
function! fzf#vim#ipc#restart(fifo)
if !has_key(s:channels, a:fifo)
throw 'fzf#vim#ipc not started'
endif
let Callback = g:fzf_ipc.callback
let chan = s:channels[a:fifo]
let Callback = chan.callback
let fifo = a:fifo
if exists('*job_start')
let g:fzf_ipc.job = job_start(
\ ['cat', g:fzf_ipc.fifo],
\ {'out_cb': { _, msg -> call(Callback, [msg]) },
\ 'exit_cb': { _, status -> status == 0 ? fzf#vim#ipc#restart() : '' }}
let chan.job = job_start(
\ ['cat', fifo],
\ {'out_cb': { _, msg -> has_key(s:channels, fifo) ? call(Callback, [msg]) : '' },
\ 'exit_cb': { _, status -> status == 0 && has_key(s:channels, fifo) ? fzf#vim#ipc#restart(fifo) : s:drop(fifo) }}
\ )
else
let eof = ['']
let g:fzf_ipc.job = jobstart(
\ ['cat', g:fzf_ipc.fifo],
let chan.job = jobstart(
\ ['cat', fifo],
\ {'stdout_buffered': 1,
\ 'on_stdout': { j, msg, e -> msg != eof ? call(Callback, msg) : '' },
\ 'on_exit': { j, status, e -> status == 0 ? fzf#vim#ipc#restart() : '' }}
\ 'on_stdout': { j, msg, e -> has_key(s:channels, fifo) ? s:deliver(Callback, msg) : '' },
\ 'on_exit': { j, status, e -> status == 0 && has_key(s:channels, fifo) ? fzf#vim#ipc#restart(fifo) : s:drop(fifo) }}
\ )
endif
endfunction
function! fzf#vim#ipc#stop()
if !exists('g:fzf_ipc')
" Whether the job handle refers to a live job. Nvim returns -1 or 0 instead of
" a handle when it cannot start one
function! s:running(job)
if exists('*job_status')
return job_status(a:job) ==# 'run'
endif
return a:job > 0
endfunction
function! fzf#vim#ipc#stop(fifo)
if !has_key(s:channels, a:fifo)
return
endif
let job = g:fzf_ipc.job
if exists('*job_stop')
call job_stop(job)
else
call jobstop(job)
call jobwait([job])
endif
" Drop it first, so the exit handler does not restart the job
let chan = remove(s:channels, a:fifo)
" Never throw from here, this runs as part of fzf's exit handling
try
if exists('*job_stop')
call job_stop(chan.job)
else
call jobstop(chan.job)
call jobwait([chan.job])
endif
catch
endtry
call delete(g:fzf_ipc.fifo)
unlet g:fzf_ipc
call delete(chan.fifo)
endfunction
+49 -3
View File
@@ -1,4 +1,4 @@
fzf-vim.txt fzf-vim Last change: March 3 2025
fzf-vim.txt fzf-vim Last change: May 31 2026
FZF-VIM - TABLE OF CONTENTS *fzf-vim* *fzf-vim-toc*
==============================================================================
@@ -110,8 +110,8 @@ COMMANDS *fzf-vim-commands*
==============================================================================
*:Files* *:GFiles* *:Buffers* *:Colors* *:Ag* *:Rg* *:RG* *:Lines* *:BLines* *:Tags* *:BTags*
*:Changes* *:Marks* *:Jumps* *:Windows* *:Locate* *:History* *:Snippets* *:Commits* *:BCommits*
*:Commands* *:Maps* *:Helptags* *:Filetypes*
*:Changes* *:Marks* *:BMarks* *:Jumps* *:Windows* *:Locate* *:History* *:Snippets* *:Commits*
*:BCommits* *:Commands* *:Maps* *:Helptags* *:Filetypes*
-----------------------+--------------------------------------------------------------------------------------
Command | List ~
@@ -130,6 +130,7 @@ COMMANDS *fzf-vim-commands*
`:BTags [QUERY]` | Tags in the current buffer
`:Changes` | Changelist across all open buffers
`:Marks` | Marks
`:BMarks` | Marks in the current buffer
`:Jumps` | Jumps
`:Windows` | Windows
`:Locate PATTERN` | `locate` command output
@@ -149,6 +150,19 @@ COMMANDS *fzf-vim-commands*
- Most commands support CTRL-T / CTRL-X / CTRL-V key bindings to open in a new
tab, a new split, or in a new vertical split
- Most commands support ALT-ENTER to insert the selected items into the
current buffer instead of opening them. The inserted text depends on the
command: file path (Files, GFiles, Buffers, History, Locate), line content
(Lines, BLines, Marks, Changes), matched text (Rg, Ag, Grep), tag name
(Tags, BTags), or commit hash (Commits, BCommits)
- Most commands support CTRL-O to open the current entry in the window fzf
was started from, leaving fzf open, so you can look through several
entries in a single run (Files, GFiles, Buffers, History, Locate, Rg, Ag,
Grep, Lines, BLines, Tags, BTags, Marks, Changes). Requires fzf 0.74.4 or
later, and is not offered in fullscreen mode, with a layout that leaves no
other window (enew, tabnew), on Windows, or when the key is already used
by g:fzf_vim.paste_key, g:fzf_action, or the command itself, as
CTRL-ALT-X is in :Buffers
- Bang-versions of the commands (e.g. `Ag!`) will open fzf in fullscreen
- You can set `g:fzf_vim.command_prefix` to give the same prefix to the commands
- e.g. `let g:fzf_vim.command_prefix = 'Fzf'` and you have `FzfFiles`, etc.
@@ -223,6 +237,38 @@ behavior with `g:fzf_vim.preview_window`. Here are some examples:
" let g:fzf_vim.preview_bash = 'C:\Git\bin\bash.exe'
<
Paste key~
*fzf-vim-paste-key*
*g:fzf_vim.paste_key*
Most commands support a key to insert the selected items into the current
buffer instead of opening them. Customize it with `g:fzf_vim.paste_key`.
>
" Key to insert the selected items into the current buffer
" (default: 'alt-enter')
let g:fzf_vim.paste_key = 'alt-enter'
<
Show key~
*fzf-vim-show-key*
*g:fzf_vim.show_key*
Most commands support a key that opens the current entry in the window fzf was
started from and leaves fzf open, so you can look through several entries in
one run. It is labelled Show in the footer to set it apart from Enter, which
opens the entry and closes fzf. Customize it with `g:fzf_vim.show_key`, or set
it to an empty string to turn the key off. A comma-separated list binds every
key in it. It takes the key over from `$FZF_DEFAULT_OPTS`, so pick another key
or turn it off to keep a binding of your own.
>
" Key to open the current entry, leaving fzf open
" (default: 'ctrl-o')
let g:fzf_vim.show_key = 'ctrl-o'
" Use CTRL-O and double-click
let g:fzf_vim.show_key = 'ctrl-o,double-click'
<
Command-level options~
*fzf-vim-command-level-options*
+15 -8
View File
@@ -67,7 +67,8 @@ call s:defs([
\'command! -bar -bang Snippets call fzf#vim#snippets(<bang>0)',
\'command! -bar -bang Commands call fzf#vim#commands(<bang>0)',
\'command! -bar -bang Jumps call fzf#vim#jumps(fzf#vim#with_preview({ "placeholder": "{2..4}"}), <bang>0)',
\'command! -bar -bang Marks call fzf#vim#marks(<bang>0)',
\'command! -bar -bang -nargs=* Marks call fzf#vim#marks(<q-args>, <bang>0)',
\'command! -bar -bang -nargs=* BMarks call fzf#vim#marks("abcdefghijklmnopqrstuvwxyz", <bang>0)',
\'command! -bar -bang Changes call fzf#vim#changes(<bang>0)',
\'command! -bar -bang Helptags call fzf#vim#helptags(fzf#vim#with_preview({ "placeholder": "--tag {2}:{3}:{4}" }), <bang>0)',
\'command! -bar -bang Windows call fzf#vim#windows(fzf#vim#with_preview({ "placeholder": "{2}" }), <bang>0)',
@@ -97,14 +98,20 @@ if (has('nvim') || has('terminal') && has('patch-8.0.995')) && (s:conf('statusli
if exists('#User#FzfStatusLine')
doautocmd User FzfStatusLine
else
if $TERM !~ "256color"
highlight default fzf1 ctermfg=1 ctermbg=8 guifg=#E12672 guibg=#565656
highlight default fzf2 ctermfg=2 ctermbg=8 guifg=#BCDDBD guibg=#565656
highlight default fzf3 ctermfg=7 ctermbg=8 guifg=#D9D9D9 guibg=#565656
" Do not display statusline when fzf is running in a floating window
" (e.g. nvim-fzf)
if has('nvim') && len(nvim_win_get_config(0).relative)
return
endif
if has('termguicolors') && &termguicolors || $TERM =~ "256color"
highlight default fzf1 ctermfg=161 ctermbg=238 guifg=#E12672 guibg=#565656 gui=nocombine
highlight default fzf2 ctermfg=151 ctermbg=238 guifg=#BCDDBD guibg=#565656 gui=nocombine
highlight default fzf3 ctermfg=252 ctermbg=238 guifg=#D9D9D9 guibg=#565656 gui=nocombine
else
highlight default fzf1 ctermfg=161 ctermbg=238 guifg=#E12672 guibg=#565656
highlight default fzf2 ctermfg=151 ctermbg=238 guifg=#BCDDBD guibg=#565656
highlight default fzf3 ctermfg=252 ctermbg=238 guifg=#D9D9D9 guibg=#565656
highlight default fzf1 ctermfg=1 ctermbg=8 guifg=#E12672 guibg=#565656 gui=nocombine
highlight default fzf2 ctermfg=2 ctermbg=8 guifg=#BCDDBD guibg=#565656 gui=nocombine
highlight default fzf3 ctermfg=7 ctermbg=8 guifg=#D9D9D9 guibg=#565656 gui=nocombine
endif
setlocal statusline=%#fzf1#\ >\ %#fzf2#fz%#fzf3#f
endif