Skip to content

Commit

Permalink
feat: add fzf-lua integration for grepping in files and dirs
Browse files Browse the repository at this point in the history
Previously, it was only possible to use Telescope to grep (search for
text) in files and directories. This commit adds support for using
fzf-lua.nvim.

The default continues to be Telescope, but users can now set the
following values to opt into using fzf-lua:

```lua
---@type YaziConfig
{
  -- ... other settings
  integrations = {
    grep_in_directory = "fzf-lua",
    grep_in_selected_files = "fzf-lua",
  },
}
```
  • Loading branch information
mikavilpas committed Dec 15, 2024
1 parent 6ea5bea commit 21d5a81
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 23 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,21 @@ jobs:
- uses: actions/[email protected]
- name: Set up dependencies
run: |
# ripgrep is a telescope dependency
# ripgrep is a dependency of telescope and fzf-lua
which rg || {
sudo apt-get install ripgrep
}
# realpath is used to resolve relative paths
# fd is a dependency of telescope and fzf-lua
# https://github.com/sharkdp/fd?tab=readme-ov-file#on-ubuntu
sudo apt-get install fd-find
# make sure it's available as `fd` - there seems to be some conflict in Ubuntu
which fd || {
sudo ln -s $(which fdfind) /usr/local/bin/fd
}
# realpath is used to resolve relative paths in yazi.nvim
which realpath || {
# just fail with an error message if realpath is not found
echo "realpath is not installed, but it should be part of GNU coreutils and included in Ubuntu"
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ open yazi in a floating window in Neovim.
vertical split, a horizontal split, a new tab, as quickfix items...
- Integrations to other plugins and tools, if they are installed:

- For [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim): you
can grep/search in the directory yazi is in
- For [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) and
[fzf-lua.nvim](https://github.com/ibhagwan/fzf-lua): you can grep/search in
the directory yazi is in. Select some files to limit the search to those
files only.
- For [grug-far.nvim](https://github.com/MagicDuck/grug-far.nvim): you can
search and replace in the directory yazi is in
- Copy the relative path from the start file to the currently hovered file.
Expand Down Expand Up @@ -313,7 +315,10 @@ These are the default keybindings that are available when yazi is open:
separately:
- `<c-s>`: search in the current yazi directory using
[telescope](https://github.com/nvim-telescope/telescope.nvim)'s `live_grep`,
if available.
if available. Optionally you can use
[fzf-lua.nvim](https://github.com/ibhagwan/fzf-lua) or provide your own
implementation - see the instructions in the configuration section for more
info.
- if multiple files/directories are selected in yazi, the search and replace
will only be done in the selected files/directories
- `<c-g>`: search and replace in the current yazi directory using
Expand Down
7 changes: 7 additions & 0 deletions integration-tests/MyTestDirectory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ export const MyTestDirectorySchema = z.object({
extension: z.literal("lua"),
stem: z.literal("modify_yazi_config_log_yazi_closed_successfully."),
}),
"modify_yazi_config_use_fzf_lua.lua": z.object({
name: z.literal("modify_yazi_config_use_fzf_lua.lua"),
type: z.literal("file"),
extension: z.literal("lua"),
stem: z.literal("modify_yazi_config_use_fzf_lua."),
}),
"modify_yazi_config_use_ya_emit_reveal.lua": z.object({
name: z.literal("modify_yazi_config_use_ya_emit_reveal.lua"),
type: z.literal("file"),
Expand Down Expand Up @@ -284,6 +290,7 @@ export const testDirectoryFiles = z.enum([
"config-modifications/modify_yazi_config_and_set_help_key.lua",
"config-modifications/modify_yazi_config_do_not_use_ya_emit_open.lua",
"config-modifications/modify_yazi_config_log_yazi_closed_successfully.lua",
"config-modifications/modify_yazi_config_use_fzf_lua.lua",
"config-modifications/modify_yazi_config_use_ya_emit_reveal.lua",
"config-modifications/notify_custom_events.lua",
"config-modifications/notify_hover_events.lua",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,85 @@ describe("telescope integration (search)", () => {
})
})
})

describe("fzf-lua integration (grep)", () => {
beforeEach(() => {
cy.visit("/")
})

it("can use fzf-lua.nvim to search in the current directory", () => {
cy.startNeovim({
filename: "routes/posts.$postId/adjacent-file.txt",
startupScriptModifications: ["modify_yazi_config_use_fzf_lua.lua"],
}).then((dir) => {
cy.contains("this file is adjacent-file.txt")
cy.typeIntoTerminal("{upArrow}")
cy.contains(
dir.contents.routes.contents["posts.$postId"].contents["route.tsx"]
.name,
)

cy.typeIntoTerminal("{control+s}")

// wait for fzf-lua to be visible
cy.contains("to Fuzzy Search")

cy.typeIntoTerminal("this")

// results should be visible
cy.contains(
dir.contents.routes.contents["posts.$postId"].contents[
"should-be-excluded-file.txt"
].name,
)

// results from outside the directory should not be visible. This
// verifies the search is limited to the current directory
cy.contains(dir.contents["initial-file.txt"].name).should("not.exist")
})
})

it("can use fzf-lua.nvim to search, limited to the selected files only", () => {
// https://github.com/ibhagwan/fzf-lua
cy.startNeovim({
filename: "routes/posts.$postId/route.tsx",
startupScriptModifications: ["modify_yazi_config_use_fzf_lua.lua"],
}).then((dir) => {
// wait until the file contents are visible
cy.contains("02c67730-6b74-4b7c-af61-fe5844fdc3d7")

cy.typeIntoTerminal("{upArrow}")
cy.contains(
dir.contents.routes.contents["posts.$postId"].contents["route.tsx"]
.name,
)

// select the current file and the file below. There are three files in
// this directory so two will be selected and one will be left
// unselected
cy.typeIntoTerminal("vk")
cy.typeIntoTerminal("{control+s}")

// telescope should be open now
cy.contains("to Fuzzy Search")

// search for some file content. This should match
// ../../../test-environment/routes/posts.$postId/adjacent-file.txt
cy.typeIntoTerminal("this")

// some results should be visible
cy.contains(
dir.contents.routes.contents["posts.$postId"].contents[
"adjacent-file.txt"
].name,
)
cy.contains("02c67730-6b74-4b7c-af61-fe5844fdc3d7")

cy.contains(
dir.contents.routes.contents["posts.$postId"].contents[
"should-be-excluded-file.txt"
].name,
).should("not.exist")
})
})
})
1 change: 1 addition & 0 deletions integration-tests/test-environment/.config/nvim/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ local plugins = {
},
},
},
{ "ibhagwan/fzf-lua" },
{ "catppuccin/nvim", name = "catppuccin", priority = 1000 },
{ "https://github.com/MagicDuck/grug-far.nvim", opts = {} },
{ "folke/snacks.nvim", opts = {} },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---@module "yazi"

require("yazi").setup(
---@type YaziConfig
{
integrations = {
grep_in_selected_files = "fzf-lua",
grep_in_directory = "fzf-lua",
},
}
)
32 changes: 18 additions & 14 deletions lua/yazi/keybinding_helpers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,14 @@ function YaziOpenerActions.grep_in_directory(config, chosen_file)
prompt_title = "Grep in " .. last_directory,
cwd = last_directory,
})
return
elseif config.integrations.grep_in_directory == "fzf-lua" then
require("fzf-lua").live_grep({
search_paths = { last_directory },
})
else
-- the user has a custom implementation. Call it.
config.integrations.grep_in_directory(last_directory)
end

-- the user has a custom implementation. Call it.
config.integrations.grep_in_directory(last_directory)
end

---@param config YaziConfig
Expand All @@ -196,23 +199,24 @@ function YaziOpenerActions.grep_in_selected_files(config, chosen_files)
table.insert(paths, plenary_path:new(path))
end

if config.integrations.grep_in_selected_files == "telescope" then
---@type string[]
local files = {}
for _, path in ipairs(paths) do
files[#files + 1] = path:make_relative(vim.uv.cwd()):gsub(" ", "\\ ")
end
---@type string[]
local files = {}
for _, path in ipairs(paths) do
files[#files + 1] = path:make_relative(vim.uv.cwd()):gsub(" ", "\\ ")
end

if config.integrations.grep_in_selected_files == "telescope" then
require("telescope.builtin").live_grep({
search = "",
prompt_title = string.format("Grep in %d paths", #files),
search_dirs = files,
})
return
elseif config.integrations.grep_in_selected_files == "fzf-lua" then
require("fzf-lua").live_grep({ search_paths = files })
else
-- the user has a custom implementation. Call it.
config.integrations.grep_in_selected_files(paths)
end

-- the user has a custom implementation. Call it.
config.integrations.grep_in_selected_files(paths)
end

---@param config YaziConfig
Expand Down
4 changes: 2 additions & 2 deletions lua/yazi/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@
---@field public yazi_opened_multiple_files fun(chosen_files: string[], config: YaziConfig, state: YaziClosedState): nil

---@class (exact) YaziConfigIntegrations # Defines settings for integrations with other plugins and tools
---@field public grep_in_directory? "telescope" | fun(directory: string): nil "implementation to be called when the user wants to grep in a directory. Defaults to `"telescope"`"
---@field public grep_in_selected_files? "telescope" | fun(selected_files: Path[]): nil "called to grep on files that were selected in yazi. Defaults to `"telescope"`"
---@field public grep_in_directory? "telescope" | "fzf-lua" | fun(directory: string): nil "implementation to be called when the user wants to grep in a directory. Defaults to `"telescope"`"
---@field public grep_in_selected_files? "telescope" | "fzf-lua" | fun(selected_files: Path[]): nil "called to grep on files that were selected in yazi. Defaults to `"telescope"`"
---@field public replace_in_directory? fun(directory: Path, selected_files?: Path[]): nil "called to start a replacement operation on some directory; by default uses grug-far.nvim"
---@field public replace_in_selected_files? fun(selected_files?: Path[]): nil "called to start a replacement operation on files that were selected in yazi; by default uses grug-far.nvim"
---@field public resolve_relative_path_application? string "the application that will be used to resolve relative paths. By default, this is GNU `realpath` on Linux and `grealpath` on macOS"
Expand Down
9 changes: 7 additions & 2 deletions spec/yazi/keybinding_helpers_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local keybinding_helpers = require("yazi.keybinding_helpers")
local match = require("luassert.match")
local plenary_path = require("plenary.path")
local stub = require("luassert.stub")
local spy = require("luassert.spy")

describe("keybinding_helpers", function()
local vim_cmd_stub
Expand All @@ -25,7 +26,9 @@ 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")
local s = spy.new(function() end)
---@diagnostic disable-next-line: assign-type-mismatch
config.integrations.grep_in_directory = s

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

Expand All @@ -34,7 +37,9 @@ describe("keybinding_helpers", function()

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")
local s = spy.new(function() end)
---@diagnostic disable-next-line: assign-type-mismatch
config.integrations.grep_in_directory = s

keybinding_helpers.grep_in_directory(config, "/tmp")

Expand Down

0 comments on commit 21d5a81

Please sign in to comment.