From b83a8e5ddd6908a6624ccc22ecda0c18636ce788 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Wed, 26 Aug 2026 21:25:33 +0900 Subject: [PATCH] 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. --- autoload/fzf/vim.vim | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/autoload/fzf/vim.vim b/autoload/fzf/vim.vim index 098a448..e205959 100755 --- a/autoload/fzf/vim.vim +++ b/autoload/fzf/vim.vim @@ -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