Skip to content

Commit

Permalink
fix: grepping or replacing in cwd instead of the directory of the file (
Browse files Browse the repository at this point in the history
#293)

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.
  • Loading branch information
mikavilpas authored Jul 30, 2024
1 parent 7062de6 commit aee19fb
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 62 deletions.
68 changes: 6 additions & 62 deletions lua/yazi/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -132,25 +132,11 @@ function M.set_keymappings(yazi_buffer, config, context)
if config.keymaps.grep_in_directory ~= false then
vim.keymap.set({ 't' }, config.keymaps.grep_in_directory, function()
keybinding_helpers.select_current_file_and_close_yazi(config, {
on_file_opened = function(_, _, state)
if config.integrations.grep_in_directory == nil then
return
end

config.integrations.grep_in_directory(state.last_directory.filename)
on_file_opened = function(chosen_file, _, _)
keybinding_helpers.grep_in_directory(config, chosen_file)
end,
on_multiple_files_opened = function(chosen_files)
if config.integrations.grep_in_selected_files == nil then
return
end

local plenary_path = require('plenary.path')
local paths = {}
for _, path in ipairs(chosen_files) do
table.insert(paths, plenary_path:new(path))
end

config.integrations.grep_in_selected_files(paths)
keybinding_helpers.grep_in_selected_files(config, chosen_files)
end,
})
end, { buffer = yazi_buffer })
Expand All @@ -171,53 +157,11 @@ function M.set_keymappings(yazi_buffer, config, context)
if config.keymaps.replace_in_directory ~= false then
vim.keymap.set({ 't' }, config.keymaps.replace_in_directory, function()
keybinding_helpers.select_current_file_and_close_yazi(config, {
on_file_opened = function(_, _, state)
if config.integrations.replace_in_directory == nil then
return
end

-- search and replace in the directory
local success, result_or_error = pcall(
config.integrations.replace_in_directory,
state.last_directory
)

if not success then
local detail = vim.inspect({
message = 'yazi.nvim: error replacing with grug-far.nvim.',
error = result_or_error,
})
vim.notify(detail, vim.log.levels.WARN)
require('yazi.log'):debug(
vim.inspect({ message = detail, error = result_or_error })
)
end
on_file_opened = function(chosen_file)
keybinding_helpers.replace_in_directory(config, chosen_file)
end,
on_multiple_files_opened = function(chosen_files)
if config.integrations.replace_in_selected_files == nil then
return
end

-- limit the replace operation to the selected files only
local plenary_path = require('plenary.path')
local paths = {}
for _, path in ipairs(chosen_files) do
table.insert(paths, plenary_path:new(path))
end

local success, result_or_error =
pcall(config.integrations.replace_in_selected_files, paths)

if not success then
local detail = vim.inspect({
message = 'yazi.nvim: error replacing with grug-far.nvim.',
error = result_or_error,
})
vim.notify(detail, vim.log.levels.WARN)
require('yazi.log'):debug(
vim.inspect({ message = detail, error = result_or_error })
)
end
keybinding_helpers.replace_in_selected_files(config, chosen_files)
end,
})
end, { buffer = yazi_buffer })
Expand Down
76 changes: 76 additions & 0 deletions lua/yazi/keybinding_helpers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,80 @@ function YaziOpenerActions.cycle_open_buffers(context)
)
end

---@param config YaziConfig
---@param chosen_file string
---@return nil
function YaziOpenerActions.grep_in_directory(config, chosen_file)
if config.integrations.grep_in_directory == nil then
return
end
local last_directory = utils.dir_of(chosen_file).filename
config.integrations.grep_in_directory(last_directory)
end

---@param config YaziConfig
---@param chosen_files string[]
function YaziOpenerActions.grep_in_selected_files(config, chosen_files)
if config.integrations.grep_in_selected_files == nil then
return
end

local plenary_path = require('plenary.path')
local paths = {}
for _, path in ipairs(chosen_files) do
table.insert(paths, plenary_path:new(path))
end

config.integrations.grep_in_selected_files(paths)
end

---@param config YaziConfig
---@param chosen_file string
function YaziOpenerActions.replace_in_directory(config, chosen_file)
if config.integrations.replace_in_directory == nil then
return
end

local last_directory = utils.dir_of(chosen_file)
-- search and replace in the directory
local success, result_or_error =
pcall(config.integrations.replace_in_directory, last_directory)

if not success then
local detail = vim.inspect({
message = 'yazi.nvim: error replacing with grug-far.nvim.',
error = result_or_error,
})
vim.notify(detail, vim.log.levels.WARN)
Log:debug(vim.inspect({ message = detail, error = result_or_error }))
end
end

---@param config YaziConfig
---@param chosen_files string[]
function YaziOpenerActions.replace_in_selected_files(config, chosen_files)
if config.integrations.replace_in_selected_files == nil then
return
end

-- limit the replace operation to the selected files only
local plenary_path = require('plenary.path')
local paths = {}
for _, path in ipairs(chosen_files) do
table.insert(paths, plenary_path:new(path))
end

local success, result_or_error =
pcall(config.integrations.replace_in_selected_files, paths)

if not success then
local detail = vim.inspect({
message = 'yazi.nvim: error replacing with grug-far.nvim.',
error = result_or_error,
})
vim.notify(detail, vim.log.levels.WARN)
Log:debug(vim.inspect({ message = detail, error = result_or_error }))
end
end

return YaziOpenerActions
15 changes: 15 additions & 0 deletions lua/yazi/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,21 @@ function M.selected_file_path(path)
return plenary_path:new(path)
end

---@param file_path string
---@return Path
function M.dir_of(file_path)
---@type Path
local path = plenary_path:new(file_path)
if path:is_dir() then
return path
else
local parent = path:parent()
assert(type(parent) == 'table', 'parent must be a table')

return parent
end
end

-- Returns parsed events from the yazi events file
---@param events_file_lines string[]
function M.parse_events(events_file_lines)
Expand Down
52 changes: 52 additions & 0 deletions spec/yazi/dir_of_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
local assert = require('luassert')
local utils = require('yazi.utils')

describe('dir_of helper function', function()
describe(
"when files and directories don't exist on disk (only in Neovim)",
function()
it('can detect the dir of a file', function()
local d = utils.dir_of('/my-tmp/file1')
assert.is_equal('/my-tmp', d.filename)
end)

it('can detect the dir of a directory', function()
-- I think it just thinks directories are files because it cannot know
-- better. But this is still a good default.
local d = utils.dir_of('/my-tmp/dir1')
assert.is_equal('/my-tmp', d.filename)
end)
end
)

describe('when files and directories exist on disk', function()
local base_dir = os.tmpname() -- create a temporary file with a unique name

before_each(function()
assert(
base_dir:match('/tmp/'),
"base_dir is not under `/tmp/`, it's too dangerous to continue"
)
os.remove(base_dir)
vim.fn.mkdir(base_dir, 'p')
end)

after_each(function()
vim.fn.delete(base_dir, 'rf')
end)

it('can get the directory of a file', function()
local file = vim.fs.joinpath(base_dir, 'abc.txt')
local d = utils.dir_of(file)
assert.is_equal(base_dir, d.filename)
end)

it('can get the directory of a directory', function()
local dir = vim.fs.joinpath(base_dir, 'dir1')
vim.fn.mkdir(dir)
local d = utils.dir_of(dir)

assert.is_equal(dir, d.filename)
end)
end)
end)
115 changes: 115 additions & 0 deletions spec/yazi/keybinding_helpers_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
local assert = require('luassert')
local config_module = require('yazi.config')
local keybinding_helpers = require('yazi.keybinding_helpers')
local match = require('luassert.match')
local stub = require('luassert.stub')

describe('keybinding_helpers', function()
describe('grep_in_directory', function()
it('should grep in the parent directory for a file', function()
local config = config_module.default()
local s = stub(config.integrations, 'grep_in_directory')

keybinding_helpers.grep_in_directory(config, '/tmp/file')

assert.stub(s).was_called_with('/tmp')
end)

it('should grep in the directory when a directory is passed', function()
local config = config_module.default()
local s = stub(config.integrations, 'grep_in_directory')

keybinding_helpers.grep_in_directory(config, '/tmp')

assert.stub(s).was_called_with('/tmp')
end)

it('should not crash if the integration is disabled', function()
local config = config_module.default()
config.integrations.grep_in_directory = nil

keybinding_helpers.grep_in_directory(config, '/tmp/file')
end)
end)

describe('grep_in_selected_files', function()
it('should call `integrations.grep_in_selected_files`', function()
local config = config_module.default()

local results = {}
config.integrations.grep_in_selected_files = function(paths)
results = paths
end

keybinding_helpers.grep_in_selected_files(
config,
{ '/tmp/file1', '/tmp/file2' }
)

assert.equals(2, #results)
assert.are.same(
{ '/tmp/file1', '/tmp/file2' },
vim
.iter(results)
:map(function(a)
return a.filename
end)
:totable()
)
end)
end)

describe('replace_in_directory', function()
it(
"when a file is passed, should replace in the file's directory",
function()
local config = config_module.default()

local stub_replace = stub(config.integrations, 'replace_in_directory')

keybinding_helpers.replace_in_directory(config, '/tmp/file')

assert.stub(stub_replace).was_called_with(match.is_truthy())
assert.equals('/tmp', stub_replace.calls[1].vals[1].filename)
end
)

it('when a directory is passed, should replace in the directory', function()
local config = config_module.default()

local stub_replace = stub(config.integrations, 'replace_in_directory')

keybinding_helpers.replace_in_directory(config, '/tmp')

assert.stub(stub_replace).was_called_with(match.is_truthy())
assert.equals('/tmp', stub_replace.calls[1].vals[1].filename)
end)
end)

describe('replace_in_selected_files', function()
it('should call `integrations.replace_in_selected_files`', function()
local config = config_module.default()

local results = {}
config.integrations.replace_in_selected_files = function(paths)
results = paths
end

keybinding_helpers.replace_in_selected_files(
config,
{ '/tmp/file1', '/tmp/file2' }
)

assert.equals(2, #results)
assert.are.same(
{ '/tmp/file1', '/tmp/file2' },
vim
.iter(results)
:map(function(a)
return a.filename
end)
:totable()
)
end)
end)
end)

0 comments on commit aee19fb

Please sign in to comment.