Skip to content

Commit

Permalink
feat: option ignore_paths to avoid running on certain paths (#105)
Browse files Browse the repository at this point in the history
Issue
=====

It's not possible to not search with blink-ripgrep.nvim while
maintaining the ability to search with ripgrep (outside of Neovim).

Although ripgrep supports many ways of ignoring paths, and blink-ripgrep
is affected by those, they will always affect both ripgrep and
blink-ripgrep at the same time.

Users cannot maintain the ability to search with ripgrep in the terminal
and use blink-ripgrep in Neovim at the same time.

Solution
========

Add a new option `ignore_paths` to the configuration of blink-ripgrep.
These can be set to absolute paths (resolved by `project_root_marker`).

When a search is started, the plugin will check if the root path is
ignored, and will not search if that's the case.

BREAKING CHANGE: The `get_command` function, which provides a custom way
to generate the entire ripgrep command, must now return a table with the
command and the root path.

---------

Co-authored-by: Mika Vilpas <mika.vilpas@gmail.com>
  • Loading branch information
rodrigoscc and mikavilpas authored Jan 16, 2025
1 parent f9f5624 commit 0882f96
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 13 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ return {
-- that is bundled in Neovim.
fallback_to_regex_highlighting = true,

-- Absolute root paths where the rg command will not be executed.
-- Usually you want to exclude paths using gitignore files or
-- ripgrep specific ignore files, but this can be used to only
-- ignore the paths in blink-ripgrep.nvim, maintaining the ability
-- to use ripgrep for those paths on the command line. If you need
-- to find out where the searches are executed, enable `debug` and
-- look at `:messages`.
ignore_paths = {},

-- Show debug information in `:messages` that can help in
-- diagnosing issues with the plugin.
debug = false,
Expand Down
7 changes: 7 additions & 0 deletions integration-tests/MyTestDirectory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export const MyTestDirectorySchema = z.object({
extension: z.literal("lua"),
stem: z.literal("don't_use_debug_mode."),
}),
"set_ignore_paths.lua": z.object({
name: z.literal("set_ignore_paths.lua"),
type: z.literal("file"),
extension: z.literal("lua"),
stem: z.literal("set_ignore_paths."),
}),
"use_case_sensitive_search.lua": z.object({
name: z.literal("use_case_sensitive_search.lua"),
type: z.literal("file"),
Expand Down Expand Up @@ -157,6 +163,7 @@ export const testDirectoryFiles = z.enum([
".config/nvim",
".config",
"config-modifications/don't_use_debug_mode.lua",
"config-modifications/set_ignore_paths.lua",
"config-modifications/use_case_sensitive_search.lua",
"config-modifications/use_manual_mode.lua",
"config-modifications",
Expand Down
44 changes: 44 additions & 0 deletions integration-tests/cypress/e2e/blink-ripgrep/basic_spec.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,50 @@ describe("the basics", () => {
cy.contains("Hippopotamus" + "234 (rg)")
})
})

it("does not search in ignore_paths", () => {
// By default, the paths ignored via git and ripgrep are also automatically
// ignored by blink-ripgrep.nvim, without any extra features (this is a
// ripgrep feature). However, the user may want to ignore some paths from
// blink-ripgrep.nvim specifically. Here we test that feature.
cy.visit("/")
cy.startNeovim({
filename: "limited/subproject/file1.lua",
startupScriptModifications: ["set_ignore_paths.lua"],
}).then((nvim) => {
// wait until text on the start screen is visible
cy.contains("This is text from file1.lua")
createFakeGitDirectoriesToLimitRipgrepScope()
const ignorePath = nvim.dir.rootPathAbsolute + "/limited"
nvim.runLuaCode({
luaCode: `_G.set_ignore_paths({ "${ignorePath}" })`,
})

// clear the current line and enter insert mode
cy.typeIntoTerminal("cc")

// this will match text from ../../../test-environment/other-file.lua
//
// If the plugin works, this text should show up as a suggestion.
cy.typeIntoTerminal("hip")

nvim
.runLuaCode({
luaCode: `return _G.blink_ripgrep_invocations`,
})
.should((result) => {
// ripgrep should only have been invoked once
expect(result.value).to.be.an("array")
expect(result.value).to.have.length(1)

const invocations = (result.value as string[][])[0]
const invocation = invocations[0]
expect(invocation).to.eql("ignored")
})

cy.contains("Hippopotamus" + "234 (rg)").should("not.exist")
})
})
})

describe("the match context", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---@param paths string[]
function _G.set_ignore_paths(paths)
require("blink-ripgrep").setup({
ignore_paths = paths,
})
end
38 changes: 31 additions & 7 deletions lua/blink-ripgrep/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
---@field fallback_to_regex_highlighting? boolean # (default: true) When a result is found for a file whose filetype does not have a treesitter parser installed, fall back to regex based highlighting that is bundled in Neovim.
---@field project_root_marker? unknown # Specifies how to find the root of the project where the ripgrep search will start from. Accepts the same options as the marker given to `:h vim.fs.root()` which offers many possibilities for configuration. Defaults to ".git".
---@field debug? boolean # Show debug information in `:messages` that can help in diagnosing issues with the plugin.
---@field ignore_paths? string[] # Absolute root paths where the rg command will not be executed. Usually you want to exclude paths using gitignore files or ripgrep specific ignore files, but this can be used to only ignore the paths in blink-ripgrep.nvim, maintaining the ability to use ripgrep for those paths on the command line. If you need to find out where the searches are executed, enable `debug` and look at `:messages`.

---@class blink-ripgrep.RgSource : blink.cmp.Source
---@field get_command fun(context: blink.cmp.Context, prefix: string): string[]
---@field get_command fun(context: blink.cmp.Context, prefix: string): blink-ripgrep.RipgrepCommand
---@field get_prefix fun(context: blink.cmp.Context): string
---@field get_completions? fun(self: blink.cmp.Source, context: blink.cmp.Context, callback: fun(response: blink.cmp.CompletionResponse | nil)): nil
local RgSource = {}
Expand Down Expand Up @@ -55,6 +56,7 @@ RgSource.config = {
search_casing = "--ignore-case",
fallback_to_regex_highlighting = true,
project_root_marker = ".git",
ignore_paths = {},
}

-- set up default options so that they are used by the next search
Expand Down Expand Up @@ -165,26 +167,48 @@ function RgSource:get_completions(context, resolve)
return
end

---@type blink-ripgrep.RipgrepCommand
local cmd
if self.get_command then
-- custom command provided by the user
cmd = self.get_command(context, prefix)
else
-- builtin default command
local command = require("blink-ripgrep.ripgrep_command")
cmd = command.get_command(prefix, RgSource.config)
local command_module = require("blink-ripgrep.ripgrep_command")
cmd = command_module.get_command(prefix, RgSource.config)
end

if vim.tbl_contains(RgSource.config.ignore_paths, cmd.root) then
if RgSource.config.debug then
vim.api.nvim_exec2(
string.format("echomsg 'skipping search in ignored path %s'", cmd.root),
{}
)
end
resolve()

if RgSource.config.debug then
command.debugify_for_shell(cmd)
require("blink-ripgrep.visualization").flash_search_prefix(prefix)
-- selene: allow(global_usage)
_G.blink_ripgrep_invocations = _G.blink_ripgrep_invocations or {}
-- selene: allow(global_usage)
table.insert(_G.blink_ripgrep_invocations, cmd)
table.insert(_G.blink_ripgrep_invocations, { "ignored", cmd.root })
return
end
end

local rg = vim.system(cmd, nil, function(result)
if RgSource.config.debug then
if cmd.debugify_for_shell then
cmd:debugify_for_shell()
end

require("blink-ripgrep.visualization").flash_search_prefix(prefix)
-- selene: allow(global_usage)
_G.blink_ripgrep_invocations = _G.blink_ripgrep_invocations or {}
-- selene: allow(global_usage)
table.insert(_G.blink_ripgrep_invocations, cmd)
end

local rg = vim.system(cmd.command, nil, function(result)
vim.schedule(function()
if result.code ~= 0 then
resolve()
Expand Down
22 changes: 16 additions & 6 deletions lua/blink-ripgrep/ripgrep_command.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
---@class blink-ripgrep.RipgrepCommand
---@field command string[]
---@field root string
---@field debugify_for_shell? fun(self):nil # Echo the command to the messages buffer for debugging purposes.
local RipgrepCommand = {}
RipgrepCommand.__index = RipgrepCommand

---@param prefix string
---@param options blink-ripgrep.Options
---@return blink-ripgrep.RipgrepCommand
---@nodiscard
function RipgrepCommand.get_command(prefix, options)
local cmd = {
"rg",
Expand All @@ -23,16 +30,19 @@ function RipgrepCommand.get_command(prefix, options)
local root = (vim.fs.root(0, options.project_root_marker) or vim.fn.getcwd())
table.insert(cmd, root)

return cmd
local command = setmetatable({
command = cmd,
root = root,
}, RipgrepCommand)

return command
end

-- Change the command from `get_command` to a string that can be executed in a
-- shell
---@param cmd string[]
function RipgrepCommand.debugify_for_shell(cmd)
-- Print the command to :messages for debugging purposes.
function RipgrepCommand:debugify_for_shell()
-- print the command to :messages for hacky debugging, but don't show it
-- in the ui so that it doesn't interrupt the user's work
local debug_cmd = vim.deepcopy(cmd)
local debug_cmd = vim.deepcopy(self.command)

-- The pattern is not compatible with shell syntax, so escape it
-- separately. The user should be able to copy paste it into their posix
Expand Down

0 comments on commit 0882f96

Please sign in to comment.