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.
This commit is contained in:
Junegunn Choi
2026-08-26 21:25:33 +09:00
parent 091f9c92f9
commit b83a8e5ddd
+16 -11
View File
@@ -651,7 +651,7 @@ endfunction
" window is current, so the entry goes to the window fzf was started from.
" Scopes the options s:goto_entry needs, which every early return has to
" restore, hence the split.
function! s:show_entry(winid, dir, kind, entry) abort
function! s:show_entry(winid, bufnr, dir, kind, entry) abort
if empty(a:entry)
return
endif
@@ -664,7 +664,7 @@ function! s:show_entry(winid, dir, kind, entry) abort
let [&magic, &wrapscan] = [0, 1]
endif
try
call s:goto_entry(a:winid, a:dir, a:kind, a:entry)
call s:goto_entry(a:winid, a:bufnr, a:dir, a:kind, a:entry)
finally
let [&magic, &wrapscan, &acd] = [magic, wrapscan, acd]
endtry
@@ -672,7 +672,7 @@ endfunction
" Parses the entry the way that command's sink does and moves the window to
" it. Runs with the options s:show_entry set.
function! s:goto_entry(winid, dir, kind, entry) abort
function! s:goto_entry(winid, bufnr, dir, kind, entry) abort
let entry = a:entry
if a:kind ==# 'gitstatus'
" ' M path', '?? path', or a rename reported as 'old -> new'
@@ -693,12 +693,14 @@ function! s:goto_entry(winid, dir, kind, entry) abort
let cmds = ['keepalt keepjumps hide buffer '.str2nr(chunks[0]),
\ 'keepjumps '.str2nr(chunks[2]), 'normal! ^zvzz']
elseif a:kind ==# 'blines'
" line number, text, in the buffer the run started from
" line number, text, in the buffer the run started from, which the window
" can have left while fzf was open
let lnum = str2nr(split(entry, "\t", 1)[0])
if lnum <= 0
if lnum <= 0 || !bufexists(a:bufnr)
return
endif
let cmds = ['keepjumps '.lnum, 'normal! ^zvzz']
let cmds = ['keepalt keepjumps hide buffer '.a:bufnr,
\ 'keepjumps '.lnum, 'normal! ^zvzz']
elseif a:kind ==# 'tags'
let parts = split(entry, '\t\zs')
if len(parts) < 3
@@ -716,16 +718,19 @@ function! s:goto_entry(winid, dir, kind, entry) abort
\ 'keepjumps '.excmd, 'normal! ^zvzz']
elseif a:kind ==# 'btags'
let parts = split(entry, "\t")
if len(parts) < 3
if len(parts) < 3 || !bufexists(a:bufnr)
return
endif
let cmds = ['keepjumps '.parts[2], 'normal! zvzz']
let cmds = ['keepalt keepjumps hide buffer '.a:bufnr,
\ 'keepjumps '.parts[2], 'normal! zvzz']
elseif a:kind ==# 'marks'
" A lowercase mark is local to the buffer the run started on
let mark = matchstr(entry, '^\s*\zs\S')
if empty(mark)
if empty(mark) || !bufexists(a:bufnr)
return
endif
let cmds = ['keepalt keepjumps hide normal! `'.mark.'zvzz']
let cmds = ['keepalt keepjumps hide buffer '.a:bufnr,
\ 'keepalt keepjumps hide normal! `'.mark.'zvzz']
elseif a:kind ==# 'changes'
" buffer, offset, line, column, text
let parts = split(entry)
@@ -867,7 +872,7 @@ function! s:add_hints(spec, bang) abort
if !empty(kind) && !empty(key) && s:can_show(a:spec, a:bang, key, claimed)
let dir = get(a:spec, 'dir', '')
let fifo = fzf#vim#ipc#start(function('s:show_entry',
\ [win_getid(), empty(dir) ? getcwd() : dir, kind]))
\ [win_getid(), bufnr(''), empty(dir) ? getcwd() : dir, kind]))
if !empty(fifo)
" Wrap an 'exit' the spec already carries. call() drops the dict of a
" dict function, so the wrapped ones are partials