Skip to content

Commit f3c7021

Browse files
committed
fix: grepping or replacing in cwd instead of the directory of the file
Fixes a bug where grepping or replacing with `<c-s>` or `<c-g>` would pick Neovim's current working directory instead of the directory of the file. This needs another look after sxyazi/yazi#1314 is resolved. Maybe it can be improved.
1 parent 7062de6 commit f3c7021

File tree

5 files changed

+264
-62
lines changed

5 files changed

+264
-62
lines changed

lua/yazi/config.lua

+6-62
Original file line numberDiff line numberDiff line change
@@ -132,25 +132,11 @@ function M.set_keymappings(yazi_buffer, config, context)
132132
if config.keymaps.grep_in_directory ~= false then
133133
vim.keymap.set({ 't' }, config.keymaps.grep_in_directory, function()
134134
keybinding_helpers.select_current_file_and_close_yazi(config, {
135-
on_file_opened = function(_, _, state)
136-
if config.integrations.grep_in_directory == nil then
137-
return
138-
end
139-
140-
config.integrations.grep_in_directory(state.last_directory.filename)
135+
on_file_opened = function(chosen_file, _, _)
136+
keybinding_helpers.grep_in_directory(config, chosen_file)
141137
end,
142138
on_multiple_files_opened = function(chosen_files)
143-
if config.integrations.grep_in_selected_files == nil then
144-
return
145-
end
146-
147-
local plenary_path = require('plenary.path')
148-
local paths = {}
149-
for _, path in ipairs(chosen_files) do
150-
table.insert(paths, plenary_path:new(path))
151-
end
152-
153-
config.integrations.grep_in_selected_files(paths)
139+
keybinding_helpers.grep_in_selected_files(config, chosen_files)
154140
end,
155141
})
156142
end, { buffer = yazi_buffer })
@@ -171,53 +157,11 @@ function M.set_keymappings(yazi_buffer, config, context)
171157
if config.keymaps.replace_in_directory ~= false then
172158
vim.keymap.set({ 't' }, config.keymaps.replace_in_directory, function()
173159
keybinding_helpers.select_current_file_and_close_yazi(config, {
174-
on_file_opened = function(_, _, state)
175-
if config.integrations.replace_in_directory == nil then
176-
return
177-
end
178-
179-
-- search and replace in the directory
180-
local success, result_or_error = pcall(
181-
config.integrations.replace_in_directory,
182-
state.last_directory
183-
)
184-
185-
if not success then
186-
local detail = vim.inspect({
187-
message = 'yazi.nvim: error replacing with grug-far.nvim.',
188-
error = result_or_error,
189-
})
190-
vim.notify(detail, vim.log.levels.WARN)
191-
require('yazi.log'):debug(
192-
vim.inspect({ message = detail, error = result_or_error })
193-
)
194-
end
160+
on_file_opened = function(chosen_file)
161+
keybinding_helpers.replace_in_directory(config, chosen_file)
195162
end,
196163
on_multiple_files_opened = function(chosen_files)
197-
if config.integrations.replace_in_selected_files == nil then
198-
return
199-
end
200-
201-
-- limit the replace operation to the selected files only
202-
local plenary_path = require('plenary.path')
203-
local paths = {}
204-
for _, path in ipairs(chosen_files) do
205-
table.insert(paths, plenary_path:new(path))
206-
end
207-
208-
local success, result_or_error =
209-
pcall(config.integrations.replace_in_selected_files, paths)
210-
211-
if not success then
212-
local detail = vim.inspect({
213-
message = 'yazi.nvim: error replacing with grug-far.nvim.',
214-
error = result_or_error,
215-
})
216-
vim.notify(detail, vim.log.levels.WARN)
217-
require('yazi.log'):debug(
218-
vim.inspect({ message = detail, error = result_or_error })
219-
)
220-
end
164+
keybinding_helpers.replace_in_selected_files(config, chosen_files)
221165
end,
222166
})
223167
end, { buffer = yazi_buffer })

lua/yazi/keybinding_helpers.lua

+76
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,80 @@ function YaziOpenerActions.cycle_open_buffers(context)
132132
)
133133
end
134134

135+
---@param config YaziConfig
136+
---@param chosen_file string
137+
---@return nil
138+
function YaziOpenerActions.grep_in_directory(config, chosen_file)
139+
if config.integrations.grep_in_directory == nil then
140+
return
141+
end
142+
local last_directory = utils.dir_of(chosen_file).filename
143+
config.integrations.grep_in_directory(last_directory)
144+
end
145+
146+
---@param config YaziConfig
147+
---@param chosen_files string[]
148+
function YaziOpenerActions.grep_in_selected_files(config, chosen_files)
149+
if config.integrations.grep_in_selected_files == nil then
150+
return
151+
end
152+
153+
local plenary_path = require('plenary.path')
154+
local paths = {}
155+
for _, path in ipairs(chosen_files) do
156+
table.insert(paths, plenary_path:new(path))
157+
end
158+
159+
config.integrations.grep_in_selected_files(paths)
160+
end
161+
162+
---@param config YaziConfig
163+
---@param chosen_file string
164+
function YaziOpenerActions.replace_in_directory(config, chosen_file)
165+
if config.integrations.replace_in_directory == nil then
166+
return
167+
end
168+
169+
local last_directory = utils.dir_of(chosen_file)
170+
-- search and replace in the directory
171+
local success, result_or_error =
172+
pcall(config.integrations.replace_in_directory, last_directory)
173+
174+
if not success then
175+
local detail = vim.inspect({
176+
message = 'yazi.nvim: error replacing with grug-far.nvim.',
177+
error = result_or_error,
178+
})
179+
vim.notify(detail, vim.log.levels.WARN)
180+
Log:debug(vim.inspect({ message = detail, error = result_or_error }))
181+
end
182+
end
183+
184+
---@param config YaziConfig
185+
---@param chosen_files string[]
186+
function YaziOpenerActions.replace_in_selected_files(config, chosen_files)
187+
if config.integrations.replace_in_selected_files == nil then
188+
return
189+
end
190+
191+
-- limit the replace operation to the selected files only
192+
local plenary_path = require('plenary.path')
193+
local paths = {}
194+
for _, path in ipairs(chosen_files) do
195+
table.insert(paths, plenary_path:new(path))
196+
end
197+
198+
local success, result_or_error =
199+
pcall(config.integrations.replace_in_selected_files, paths)
200+
201+
if not success then
202+
local detail = vim.inspect({
203+
message = 'yazi.nvim: error replacing with grug-far.nvim.',
204+
error = result_or_error,
205+
})
206+
vim.notify(detail, vim.log.levels.WARN)
207+
Log:debug(vim.inspect({ message = detail, error = result_or_error }))
208+
end
209+
end
210+
135211
return YaziOpenerActions

lua/yazi/utils.lua

+15
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,21 @@ function M.selected_file_path(path)
8080
return plenary_path:new(path)
8181
end
8282

83+
---@param file_path string
84+
---@return Path
85+
function M.dir_of(file_path)
86+
---@type Path
87+
local path = plenary_path:new(file_path)
88+
if path:is_dir() then
89+
return path
90+
else
91+
local parent = path:parent()
92+
assert(type(parent) == 'table', 'parent must be a table')
93+
94+
return parent
95+
end
96+
end
97+
8398
-- Returns parsed events from the yazi events file
8499
---@param events_file_lines string[]
85100
function M.parse_events(events_file_lines)

spec/yazi/dir_of_spec.lua

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
local assert = require('luassert')
2+
local utils = require('yazi.utils')
3+
4+
describe('dir_of helper function', function()
5+
describe(
6+
"when files and directories don't exist on disk (only in Neovim)",
7+
function()
8+
it('can detect the dir of a file', function()
9+
local d = utils.dir_of('/my-tmp/file1')
10+
assert.is_equal('/my-tmp', d.filename)
11+
end)
12+
13+
it('can detect the dir of a directory', function()
14+
-- I think it just thinks directories are files because it cannot know
15+
-- better. But this is still a good default.
16+
local d = utils.dir_of('/my-tmp/dir1')
17+
assert.is_equal('/my-tmp', d.filename)
18+
end)
19+
end
20+
)
21+
22+
describe('when files and directories exist on disk', function()
23+
local base_dir = os.tmpname() -- create a temporary file with a unique name
24+
25+
before_each(function()
26+
assert(
27+
base_dir:match('/tmp/'),
28+
"base_dir is not under `/tmp/`, it's too dangerous to continue"
29+
)
30+
os.remove(base_dir)
31+
vim.fn.mkdir(base_dir, 'p')
32+
end)
33+
34+
after_each(function()
35+
vim.fn.delete(base_dir, 'rf')
36+
end)
37+
38+
it('can get the directory of a file', function()
39+
local file = vim.fs.joinpath(base_dir, 'abc.txt')
40+
local d = utils.dir_of(file)
41+
assert.is_equal(base_dir, d.filename)
42+
end)
43+
44+
it('can get the directory of a directory', function()
45+
local dir = vim.fs.joinpath(base_dir, 'dir1')
46+
vim.fn.mkdir(dir)
47+
local d = utils.dir_of(dir)
48+
49+
assert.is_equal(dir, d.filename)
50+
end)
51+
end)
52+
end)

spec/yazi/keybinding_helpers_spec.lua

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
local assert = require('luassert')
2+
local config_module = require('yazi.config')
3+
local keybinding_helpers = require('yazi.keybinding_helpers')
4+
local match = require('luassert.match')
5+
local stub = require('luassert.stub')
6+
7+
describe('keybinding_helpers', function()
8+
describe('grep_in_directory', function()
9+
it('should grep in the parent directory for a file', function()
10+
local config = config_module.default()
11+
local s = stub(config.integrations, 'grep_in_directory')
12+
13+
keybinding_helpers.grep_in_directory(config, '/tmp/file')
14+
15+
assert.stub(s).was_called_with('/tmp')
16+
end)
17+
18+
it('should grep in the directory when a directory is passed', function()
19+
local config = config_module.default()
20+
local s = stub(config.integrations, 'grep_in_directory')
21+
22+
keybinding_helpers.grep_in_directory(config, '/tmp')
23+
24+
assert.stub(s).was_called_with('/tmp')
25+
end)
26+
27+
it('should not crash if the integration is disabled', function()
28+
local config = config_module.default()
29+
config.integrations.grep_in_directory = nil
30+
31+
keybinding_helpers.grep_in_directory(config, '/tmp/file')
32+
end)
33+
end)
34+
35+
describe('grep_in_selected_files', function()
36+
it('should call `integrations.grep_in_selected_files`', function()
37+
local config = config_module.default()
38+
39+
local results = {}
40+
config.integrations.grep_in_selected_files = function(paths)
41+
results = paths
42+
end
43+
44+
keybinding_helpers.grep_in_selected_files(
45+
config,
46+
{ '/tmp/file1', '/tmp/file2' }
47+
)
48+
49+
assert.equals(2, #results)
50+
assert.are.same(
51+
{ '/tmp/file1', '/tmp/file2' },
52+
vim
53+
.iter(results)
54+
:map(function(a)
55+
return a.filename
56+
end)
57+
:totable()
58+
)
59+
end)
60+
end)
61+
62+
describe('replace_in_directory', function()
63+
it(
64+
"when a file is passed, should replace in the file's directory",
65+
function()
66+
local config = config_module.default()
67+
68+
local stub_replace = stub(config.integrations, 'replace_in_directory')
69+
70+
keybinding_helpers.replace_in_directory(config, '/tmp/file')
71+
72+
assert.stub(stub_replace).was_called_with(match.is_truthy())
73+
assert.equals('/tmp', stub_replace.calls[1].vals[1].filename)
74+
end
75+
)
76+
77+
it('when a directory is passed, should replace in the directory', function()
78+
local config = config_module.default()
79+
80+
local stub_replace = stub(config.integrations, 'replace_in_directory')
81+
82+
keybinding_helpers.replace_in_directory(config, '/tmp')
83+
84+
assert.stub(stub_replace).was_called_with(match.is_truthy())
85+
assert.equals('/tmp', stub_replace.calls[1].vals[1].filename)
86+
end)
87+
end)
88+
89+
describe('replace_in_selected_files', function()
90+
it('should call `integrations.replace_in_selected_files`', function()
91+
local config = config_module.default()
92+
93+
local results = {}
94+
config.integrations.replace_in_selected_files = function(paths)
95+
results = paths
96+
end
97+
98+
keybinding_helpers.replace_in_selected_files(
99+
config,
100+
{ '/tmp/file1', '/tmp/file2' }
101+
)
102+
103+
assert.equals(2, #results)
104+
assert.are.same(
105+
{ '/tmp/file1', '/tmp/file2' },
106+
vim
107+
.iter(results)
108+
:map(function(a)
109+
return a.filename
110+
end)
111+
:totable()
112+
)
113+
end)
114+
end)
115+
end)

0 commit comments

Comments
 (0)