From 0882f96daf940c4d17358aa76f3ccf213a7df3b2 Mon Sep 17 00:00:00 2001 From: Rodrigo Santa Cruz Date: Thu, 16 Jan 2025 02:33:55 -0500 Subject: [PATCH] feat: option ignore_paths to avoid running on certain paths (#105) 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 --- README.md | 9 ++++ integration-tests/MyTestDirectory.ts | 7 +++ .../e2e/blink-ripgrep/basic_spec.cy.ts | 44 +++++++++++++++++++ .../config-modifications/set_ignore_paths.lua | 6 +++ lua/blink-ripgrep/init.lua | 38 +++++++++++++--- lua/blink-ripgrep/ripgrep_command.lua | 22 +++++++--- 6 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 integration-tests/test-environment/config-modifications/set_ignore_paths.lua diff --git a/README.md b/README.md index aed3ae0..19fe7fb 100644 --- a/README.md +++ b/README.md @@ -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, diff --git a/integration-tests/MyTestDirectory.ts b/integration-tests/MyTestDirectory.ts index 73a8683..1957ec1 100644 --- a/integration-tests/MyTestDirectory.ts +++ b/integration-tests/MyTestDirectory.ts @@ -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"), @@ -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", diff --git a/integration-tests/cypress/e2e/blink-ripgrep/basic_spec.cy.ts b/integration-tests/cypress/e2e/blink-ripgrep/basic_spec.cy.ts index c849617..5c5c8de 100644 --- a/integration-tests/cypress/e2e/blink-ripgrep/basic_spec.cy.ts +++ b/integration-tests/cypress/e2e/blink-ripgrep/basic_spec.cy.ts @@ -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", () => { diff --git a/integration-tests/test-environment/config-modifications/set_ignore_paths.lua b/integration-tests/test-environment/config-modifications/set_ignore_paths.lua new file mode 100644 index 0000000..ee3d003 --- /dev/null +++ b/integration-tests/test-environment/config-modifications/set_ignore_paths.lua @@ -0,0 +1,6 @@ +---@param paths string[] +function _G.set_ignore_paths(paths) + require("blink-ripgrep").setup({ + ignore_paths = paths, + }) +end diff --git a/lua/blink-ripgrep/init.lua b/lua/blink-ripgrep/init.lua index da863ac..2552985 100644 --- a/lua/blink-ripgrep/init.lua +++ b/lua/blink-ripgrep/init.lua @@ -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 = {} @@ -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 @@ -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() diff --git a/lua/blink-ripgrep/ripgrep_command.lua b/lua/blink-ripgrep/ripgrep_command.lua index 1f31788..316cc11 100644 --- a/lua/blink-ripgrep/ripgrep_command.lua +++ b/lua/blink-ripgrep/ripgrep_command.lua @@ -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", @@ -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