Skip to content

Commit 47b85a2

Browse files
phanenibhagwan
authored andcommitted
fix(hide): selection is empty when do action on "multi-selected && zero-matched"
* Regression of 6e61cef: `not tonumber(idx)` imply list is empty, but select may not empty. * Don't patch `"" -> nil` when user have it own `v.field_index` which is unpredictable. * If user set `field_index = "{q} {n} xxx"`, we shouldn't remove it.
1 parent 13ec251 commit 47b85a2

2 files changed

Lines changed: 58 additions & 48 deletions

File tree

lua/fzf-lua/core.lua

Lines changed: 41 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,45 @@ M.set_header = function(opts, hdr_tbl)
11291129
return opts
11301130
end
11311131

1132+
1133+
-- Use both {q} and {+} as field indexes so we can update last query when
1134+
-- executing the action, without this we lose the last query on "hide" as
1135+
-- the process never terminates and `--print-query` isn't being printed
1136+
-- When no entry selected (with {q} {+}), {+} will be forced expand to ''
1137+
-- Use {n} to know if we really select an empty string, or there's just no selected
1138+
local patch_shell_action = function(v, opts)
1139+
local field_index = v.field_index == false and "" or v.field_index or "{+}"
1140+
local overide_f_idx
1141+
if not field_index:match("^{q} {n}") then
1142+
field_index = "{q} {n} " .. field_index
1143+
overide_f_idx = true
1144+
end
1145+
-- replace the action with shell cmd proxy to the original action
1146+
return shell.raw_action(function(items, _, _)
1147+
assert(field_index:match("^{q} {n}"))
1148+
local query, idx = unpack(items, 1, 2)
1149+
config.resume_set("query", query, opts)
1150+
if overide_f_idx then
1151+
table.remove(items, 1)
1152+
table.remove(items, 1)
1153+
end
1154+
-- fix side effect of "{q} {+}": {+} is forced expanded to ""
1155+
-- only when: user didn't set v.field_index (otherwise it can be complex/unpredictable)
1156+
-- {n} used to determine if "zero-selected && zero-match", then patch: "" -> nil
1157+
if not v.field_index then
1158+
-- When no item is matching (empty list or non-matching query)
1159+
-- both {n} and {+} are expanded to "".
1160+
-- NOTE1: older versions of fzf don't expand {n} to "" (without match)
1161+
-- in such case the (empty) items table will be in `items[2]` (#1833)
1162+
-- NOTE2: on Windows, no match {n} is expanded to '' (#1836)
1163+
local zero_matched = not tonumber(idx)
1164+
local zero_selected = #items == 0 or (#items == 1 and #items[1] == 0)
1165+
items = (zero_matched and zero_selected) and {} or items
1166+
end
1167+
v.fn(items, opts)
1168+
end, field_index, opts.debug)
1169+
end
1170+
11321171
-- converts actions defined with "reload=true" to use fzf's `reload` bind
11331172
-- provides a better UI experience without a visible interface refresh
11341173
---@param reload_cmd content
@@ -1196,30 +1235,7 @@ M.convert_reload_actions = function(reload_cmd, opts)
11961235
if type(v) == "table" and v.reload then
11971236
-- Modified actions should not be considered in `actions.expect`
11981237
opts.actions[k]._ignore = true
1199-
-- Use both {q} and {+} as field indexes so we can update last query when
1200-
-- executing the action, without this we lose the last query on "hide" as
1201-
-- the process never terminates and `--print-query` isn't being printed
1202-
-- When no entry selected (with {q} {+}), {+} will be forced expand to ''
1203-
-- Use {n} to know if we really select an empty string, or there's just no selected
1204-
local field_index = v.field_index == false and "" or v.field_index or "{q} {n} {+}"
1205-
if not field_index:match("^{q} {n}") then
1206-
field_index = "{q} {n} " .. field_index
1207-
end
1208-
-- replace the action with shell cmd proxy to the original action
1209-
local shell_action = shell.raw_action(function(items, _, _)
1210-
if field_index:match("^{q} {n}") then
1211-
local query = table.remove(items, 1)
1212-
config.resume_set("query", query, opts)
1213-
local idx = table.remove(items, 1)
1214-
-- When no item is matching (empty list or non-matching query)
1215-
-- both {n} and {+} are expanded to "".
1216-
-- NOTE1: older versions of fzf don't expand {n} to "" (without match)
1217-
-- in such case the (empty) items table will be in `items[2]` (#1833)
1218-
-- NOTE2: on Windows, no match {n} is expanded to '' (#1836)
1219-
items = not tonumber(idx) and {} or items
1220-
end
1221-
v.fn(items, opts)
1222-
end, field_index, opts.debug)
1238+
local shell_action = patch_shell_action(v, opts)
12231239
if type(v.prefix) == "string" and not v.prefix:match("%+$") then
12241240
v.prefix = v.prefix .. "+"
12251241
end
@@ -1267,30 +1283,7 @@ M.convert_exec_silent_actions = function(opts)
12671283
assert(type(v.fn) == "function")
12681284
-- Modified actions should not be considered in `actions.expect`
12691285
opts.actions[k]._ignore = true
1270-
-- Use both {q} and {+} as field indexes so we can update last query when
1271-
-- executing the action, without this we lose the last query on "hide" as
1272-
-- the process never terminates and `--print-query` isn't being printed
1273-
-- When no entry selected (with {q} {+}), {+} will be forced expand to ''
1274-
-- Use {n} to know if we really select an empty string, or there's just no selected
1275-
local field_index = v.field_index == false and "" or v.field_index or "{q} {n} {+}"
1276-
if not field_index:match("^{q} {n}") then
1277-
field_index = "{q} {n} " .. field_index
1278-
end
1279-
-- replace the action with shell cmd proxy to the original action
1280-
local shell_action = shell.raw_action(function(items, _, _)
1281-
if field_index:match("^{q} {n}") then
1282-
local query = table.remove(items, 1)
1283-
config.resume_set("query", query, opts)
1284-
local idx = table.remove(items, 1)
1285-
-- When no item is matching (empty list or non-matching query)
1286-
-- both {n} and {+} are expanded to "".
1287-
-- NOTE1: older versions of fzf don't expand {n} to "" (without match)
1288-
-- in such case the (empty) items table will be in `items[2]` (#1833)
1289-
-- NOTE2: on Windows, no match {n} is expanded to '' (#1836)
1290-
items = not tonumber(idx) and {} or items
1291-
end
1292-
v.fn(items, opts)
1293-
end, field_index, opts.debug)
1286+
local shell_action = patch_shell_action(v, opts)
12941287
if type(v.prefix) == "string" and not v.prefix:match("%+$") then
12951288
v.prefix = v.prefix .. "+"
12961289
end

tests/win_spec.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,21 @@ T["win"]["hide"]["can resume after close CTX win (#1936)"] = function()
101101
child.type_keys("<c-j>")
102102
end
103103

104+
T["win"]["hide"]["actions on multi-select but zero-match #1961"] = function()
105+
reload({ "hide" })
106+
eq(child.lua_get([[_G._fzf_lua_on_create]]), vim.NIL)
107+
child.lua([[FzfLua.files{
108+
-- profile = "hide",
109+
query = "README.md",
110+
fzf_opts = { ["--multi"] = true },
111+
}]])
112+
-- not work with `profile = "hide"`?
113+
child.wait_until(function() return child.lua_get([[_G._fzf_load_called]]) == true end)
114+
child.type_keys([[<tab>]])
115+
child.type_keys([[a-non-exist-file]])
116+
child.type_keys([[<cr>]])
117+
child.wait_until(function() return child.lua_get([[_G._fzf_lua_on_create]]) == vim.NIL end)
118+
eq("README.md", vim.fs.basename(child.lua_get([[vim.api.nvim_buf_get_name(0)]])))
119+
end
120+
104121
return T

0 commit comments

Comments
 (0)