Skip to content

Commit 18de02e

Browse files
committed
refactor(actions): buf edit using bufadd|nvim_win_set_buf
Use `vim.fn.bufadd` with `vim.api.nvim_win_set_buf` instead of `:b|e`. this way we can determine if the cwd changed after certain split commands, e.g. "tabnew" with auto-rooters (#1854) Also prepares for using file edit actions from other windows with `utils.CTX().winid`. [re-]fixes #1854
1 parent 5d2ba91 commit 18de02e

1 file changed

Lines changed: 64 additions & 66 deletions

File tree

lua/fzf-lua/actions.lua

Lines changed: 64 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,8 @@ end
142142
---@param _vimcmd string
143143
---@param selected string[]
144144
---@param opts fzf-lua.Config
145-
---@param pcall_vimcmd boolean?
146145
---@return string?
147-
M.vimcmd_entry = function(_vimcmd, selected, opts, pcall_vimcmd)
146+
M.vimcmd_entry = function(_vimcmd, selected, opts)
148147
for i, sel in ipairs(selected) do
149148
(function()
150149
-- Lua 5.1 goto compatiblity hack (function wrap)
@@ -164,45 +163,9 @@ M.vimcmd_entry = function(_vimcmd, selected, opts, pcall_vimcmd)
164163
-- technically we should never get to the `uv.cwd()` fallback
165164
fullpath = path.join({ opts.cwd or opts._cwd or uv.cwd(), fullpath })
166165
end
167-
-- always open files relative to the current win/tab cwd (#1854)
168-
local relpath = path.relative_to(fullpath, uv.cwd())
169-
-- opts.__CTX isn't guaranteed by API users (#1414)
170-
local CTX = opts.__CTX or utils.CTX()
171-
local target_equals_current =
172-
(entry.bufnr and entry.bufnr == CTX.bufnr or path.equals(fullpath, CTX.bname))
173-
-- we open a new buffer on tabs so target is always different (#1785)
174-
and not _vimcmd:match("^tabnew")
175-
local vimcmd = (function()
176-
-- Do not execute "edit" commands if we already have the same buffer/file open
177-
-- or if we are dealing with a URI as it's open with `vim.lsp.util.show_document`
178-
if _vimcmd == "<auto>" and (entry.uri or target_equals_current) then
179-
return nil
180-
end
181-
-- Same buffer splits and URI entries only execute the split cmd
182-
-- after a split we land in the same buffer, remove the piped edit
183-
-- e.g. "vsplit | e" -> "vsplit" (#1677)
184-
if _vimcmd:match("| <auto>") and (entry.uri or target_equals_current) then
185-
return _vimcmd:gsub("| <auto>", "")
186-
end
187-
-- Replace "<auto>" based on entry being buffer or filename
188-
return _vimcmd:gsub("<auto>", entry.bufnr and entry.bufname and "b" or "e")
189-
end)()
190-
-- ":b" and ":e" commands replace the current buffer
191-
local will_replace_curbuf = vimcmd == "e" or vimcmd == "b"
192-
if will_replace_curbuf
193-
and not vim.o.hidden
194-
and not vim.o.autowriteall
195-
and utils.buffer_is_dirty(nil, false, true) then
196-
-- when `:set nohidden`, confirm with the user when trying to switch
197-
-- from a dirty buffer, abort if declined, save buffer if requested
198-
if utils.save_dialog(nil) then
199-
vimcmd = vimcmd .. "!"
200-
else
201-
return
202-
end
203-
end
204-
if will_replace_curbuf and utils.wo.winfixbuf
205-
then
166+
-- <auto> (without prefix, formerly `:b|e`) replace the current buffer
167+
local vimcmd, will_replace_curbuf = _vimcmd, _vimcmd == "<auto>"
168+
if will_replace_curbuf and utils.wo.winfixbuf then
206169
utils.warn("'winfixbuf' is set for current window, will open in a split.")
207170
vimcmd = "split | " .. vimcmd
208171
end
@@ -213,29 +176,53 @@ M.vimcmd_entry = function(_vimcmd, selected, opts, pcall_vimcmd)
213176
vim.cmd("normal! m`")
214177
end
215178
if vimcmd then
216-
-- Killing term buffers requires "!" (#1078)
217-
if entry.terminal and vimcmd == "bd" then
218-
vimcmd = vimcmd .. "!"
219-
end
179+
local cmd, is_buf_edit = vimcmd:gsub("|?%s-<auto>$", "")
180+
-- Command could have been "<auto>", in which case do nothing
181+
-- as we only have to load the buffer into the current window
182+
if #cmd > 0 then vim.cmd(cmd) end
220183
-- URI entries only execute new buffers (new|vnew|tabnew)
221-
if not entry.uri and not target_equals_current then
222-
-- Force full paths when `autochdir=true` (#882)
223-
vimcmd = string.format("%s %s", vimcmd, (function()
224-
-- `:argdel|:argadd` uses only paths
225-
-- argdel only accepts relative path (#1949)
226-
if vimcmd:match("^arg") then return path.relative_to(entry.path, uv.cwd()) end
227-
if entry.bufnr then return tostring(entry.bufnr) end
184+
-- and later use `utils.jump_to_location` to load the buffer
185+
if not entry.uri and is_buf_edit > 0 then
186+
local bufnr = (function()
187+
-- Is the requested buffer is already loaded by the (split) command?
188+
local curbuf = vim.api.nvim_win_get_buf(0)
189+
local curbname = vim.api.nvim_buf_get_name(curbuf)
190+
if entry.bufnr == curbuf or path.equals(curbname, fullpath) then return end
191+
-- Entry always contains bufnr
192+
if entry.bufnr then return entry.bufnr end
193+
-- Always open files relative to the current win/tab cwd (#1854)
228194
-- We normalize the path or Windows will fail with directories starting
229195
-- with special characters, for example "C:\app\(web)" will be translated
230196
-- by neovim to "c:\app(web)" (#1082)
231-
return vim.fn.fnameescape(path.normalize(relpath))
232-
end)())
233-
end
234-
if pcall_vimcmd ~= false then
235-
local ok, err = pcall(function() vim.cmd(vimcmd) end)
236-
if not ok then utils.error("':%s' failed: %s", vimcmd, err) end
237-
else
238-
vim.cmd(vimcmd)
197+
local relpath = path.normalize(path.relative_to(fullpath, uv.cwd()))
198+
local bufnr = vim.fn.bufadd(relpath)
199+
if bufnr == 0 and not opts.silent then
200+
utils.warn("Unable to add buffer %s", relpath)
201+
return
202+
else
203+
vim.bo[bufnr].buflisted = true
204+
return bufnr
205+
end
206+
end)()
207+
if tonumber(bufnr) then
208+
-- If current buffer is an unnamed empty buffer (e.g. "new"), wipe on switch
209+
if will_replace_curbuf
210+
and vim.bo.buftype == ""
211+
and vim.bo.filetype == ""
212+
and vim.api.nvim_buf_line_count(0) == 1
213+
and vim.api.nvim_buf_get_lines(0, 0, -1, false)[1] == ""
214+
and vim.api.nvim_buf_get_name(0) == ""
215+
then
216+
vim.bo.bufhidden = "wipe"
217+
end
218+
vim.fn.bufload(bufnr)
219+
local ok, _ = pcall(vim.api.nvim_win_set_buf, 0, bufnr)
220+
-- When `:set nohidden && set confirm`, neovim will invoke the save dialog
221+
-- and confirm with the user when trying to switch from a dirty buffer, if
222+
-- user cancelles the save dialog pcall will fail with:
223+
-- Vim:E37: No write since last change (add ! to override)
224+
if not ok then return end
225+
end
239226
end
240227
end
241228
-- Reload actions from fzf's (buf/arg del, etc) window end here
@@ -468,18 +455,29 @@ M.buf_del = function(selected, opts)
468455
end
469456
end
470457

458+
local function arg_exec(cmd, selected, opts)
459+
for _, sel in ipairs(selected) do
460+
(function()
461+
local entry = path.entry_to_file(sel, opts)
462+
local relpath = entry.bufname or entry.path
463+
assert(relpath, "entry doesn't contain filepath")
464+
if not relpath then return end
465+
if path.is_absolute(relpath) then
466+
relpath = path.relative_to(relpath, vim.uv.cwd())
467+
end
468+
vim.cmd(cmd .. " " .. relpath)
469+
end)()
470+
end
471+
end
472+
471473
M.arg_add = function(selected, opts)
472-
local vimcmd = "argadd"
473-
M.vimcmd_entry(vimcmd, selected, opts)
474+
arg_exec("argadd", selected, opts)
474475
---@diagnostic disable-next-line: param-type-mismatch
475476
pcall(vim.cmd, "argdedupe")
476477
end
477478

478479
M.arg_del = function(selected, opts)
479-
local vimcmd = "argdel"
480-
-- since we don't dedup argdel can fail if file is added
481-
-- more than once into the arglist
482-
M.vimcmd_entry(vimcmd, selected, opts, true)
480+
arg_exec("argdel", selected, opts)
483481
end
484482

485483
M.colorscheme = function(selected, opts)

0 commit comments

Comments
 (0)