Skip to content

Commit

Permalink
feat: allow canceling the search with project_root_fallback = false (
Browse files Browse the repository at this point in the history
…#112)

Co-authored-by: Joshua Cold <joshzcold@gmail.com>
  • Loading branch information
mikavilpas and joshzcold authored Jan 19, 2025
1 parent d93f974 commit c693690
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 5 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ return {
-- - { ".git", "package.json", ".root" }
project_root_marker = ".git",

-- Enable fallback to neovim cwd if project_root_marker is not
-- found. Default: `true`, which means to use the cwd.
project_root_fallback = true,

-- The casing to use for the search in a format that ripgrep
-- accepts. Defaults to "--ignore-case". See `rg --help` for all the
-- available options ripgrep supports, but you can try
Expand Down
14 changes: 14 additions & 0 deletions integration-tests/MyTestDirectory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export const MyTestDirectorySchema = z.object({
name: z.literal("config-modifications/"),
type: z.literal("directory"),
contents: z.object({
"disable_project_root_fallback.lua": z.object({
name: z.literal("disable_project_root_fallback.lua"),
type: z.literal("file"),
extension: z.literal("lua"),
stem: z.literal("disable_project_root_fallback."),
}),
"don't_use_debug_mode.lua": z.object({
name: z.literal("don't_use_debug_mode.lua"),
type: z.literal("file"),
Expand All @@ -66,6 +72,12 @@ export const MyTestDirectorySchema = z.object({
extension: z.literal("lua"),
stem: z.literal("use_manual_mode."),
}),
"use_not_found_project_root.lua": z.object({
name: z.literal("use_not_found_project_root.lua"),
type: z.literal("file"),
extension: z.literal("lua"),
stem: z.literal("use_not_found_project_root."),
}),
}),
}),
"initial-file.txt": z.object({
Expand Down Expand Up @@ -162,10 +174,12 @@ export const testDirectoryFiles = z.enum([
".config/nvim/prepare.lua",
".config/nvim",
".config",
"config-modifications/disable_project_root_fallback.lua",
"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/use_not_found_project_root.lua",
"config-modifications",
"initial-file.txt",
"limited/dir with spaces/file with spaces.txt",
Expand Down
52 changes: 52 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 @@ -199,6 +199,58 @@ describe("searching inside projects", () => {
})
})

it("does not search if the project root is not found", () => {
cy.visit("/")
cy.startNeovim({
filename: "limited/subproject/file1.lua",
startupScriptModifications: [
"use_not_found_project_root.lua",
"disable_project_root_fallback.lua",
],
}).then((nvim) => {
// when opening a file from a subproject, the search should be limited to
// the nearest .git directory (only the files in the same project should
// be searched)
cy.contains("This is text from file1.lua")
createFakeGitDirectoriesToLimitRipgrepScope()

// make sure the preconditions for this case are met
nvim.runLuaCode({
luaCode: `assert(require("blink-ripgrep").config.project_root_fallback == false)`,
})

// search for something that was found in the previous test (so we know
// it should be found)
cy.typeIntoTerminal("cc")
cy.typeIntoTerminal("some")

// because the project root is not found, the search should not have
// found anything
cy.contains("here").should("not.exist")

nvim
.runLuaCode({
luaCode: `return _G.blink_ripgrep_invocations`,
})
.should((result) => {
expect(result.value).to.eql([
["ignored-because-no-command"],
["ignored-because-no-command"],
])
})

nvim.runExCommand({ command: "messages" }).then((result) => {
// make sure the search was logged to be skipped due to not finding the
// root directory, etc. basically we want to double check it was
// skipped for this exact reason and not due to some other possible
// bug
expect(result.value).to.contain(
"no command returned, skipping the search",
)
})
})
})

describe("custom ripgrep options", () => {
it("allows using a custom search_casing when searching", () => {
cy.visit("/")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require("blink-ripgrep").setup({
project_root_fallback = false,
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require("blink-ripgrep").setup({
project_root_marker = ".this_will_not_be_found",
})
27 changes: 24 additions & 3 deletions lua/blink-ripgrep/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@

---@class blink-ripgrep.Options
---@field prefix_min_len? number # The minimum length of the current word to start searching (if the word is shorter than this, the search will not start)
---@field get_command? fun(context: blink.cmp.Context, prefix: string): string[] # Changing this might break things - if you need some customization, please open an issue 🙂
---@field get_command? fun(context: blink.cmp.Context, prefix: string): blink-ripgrep.RipgrepCommand | nil # Changing this might break things - if you need some customization, please open an issue 🙂
---@field get_prefix? fun(context: blink.cmp.Context): string
---@field context_size? number # The number of lines to show around each match in the preview (documentation) window. For example, 5 means to show 5 lines before, then the match, and another 5 lines after the match.
---@field max_filesize? string # The maximum file size that ripgrep should include in its search. Examples: "1024" (bytes by default), "200K", "1M", "1G"
---@field search_casing? string # The casing to use for the search in a format that ripgrep accepts. Defaults to "--ignore-case". See `rg --help` for all the available options ripgrep supports, but you can try "--case-sensitive" or "--smart-case".
---@field additional_rg_options? string[] # (advanced) Any options you want to give to ripgrep. See `rg -h` for a list of all available options.
---@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 project_root_fallback? boolean # Enable fallback to neovim cwd if project_root_marker is not found. Default: `true`, which means to use the cwd.
---@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): blink-ripgrep.RipgrepCommand
---@field get_command fun(context: blink.cmp.Context, prefix: string): blink-ripgrep.RipgrepCommand | nil
---@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 @@ -57,6 +58,7 @@ RgSource.config = {
fallback_to_regex_highlighting = true,
project_root_marker = ".git",
ignore_paths = {},
project_root_fallback = true,
}

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

---@type blink-ripgrep.RipgrepCommand
---@type blink-ripgrep.RipgrepCommand | nil
local cmd
if self.get_command then
-- custom command provided by the user
Expand All @@ -178,6 +180,25 @@ function RgSource:get_completions(context, resolve)
cmd = command_module.get_command(prefix, RgSource.config)
end

if cmd == nil then
if RgSource.config.debug then
vim.api.nvim_exec2(
"echomsg 'no command returned, skipping the search'",
{}
)
-- selene: allow(global_usage)
_G.blink_ripgrep_invocations = _G.blink_ripgrep_invocations or {}
-- selene: allow(global_usage)
table.insert(
_G.blink_ripgrep_invocations,
{ "ignored-because-no-command" }
)
end

resolve()
return
end

if vim.tbl_contains(RgSource.config.ignore_paths, cmd.root) then
if RgSource.config.debug then
vim.api.nvim_exec2(
Expand Down
12 changes: 10 additions & 2 deletions lua/blink-ripgrep/ripgrep_command.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ RipgrepCommand.__index = RipgrepCommand

---@param prefix string
---@param options blink-ripgrep.Options
---@return blink-ripgrep.RipgrepCommand
---@return blink-ripgrep.RipgrepCommand | nil, string? # The command to run, or an error message.
---@nodiscard
function RipgrepCommand.get_command(prefix, options)
local cmd = {
Expand All @@ -27,7 +27,15 @@ function RipgrepCommand.get_command(prefix, options)
table.insert(cmd, "--")
table.insert(cmd, prefix .. "[\\w_-]+")

local root = (vim.fs.root(0, options.project_root_marker) or vim.fn.getcwd())
local root = vim.fs.root(0, options.project_root_marker)
if options.project_root_fallback then
root = root or vim.fn.getcwd()
end
if root == nil then
return nil,
"Could not find project root, and project_root_fallback is disabled."
end

table.insert(cmd, root)

local command = setmetatable({
Expand Down
22 changes: 22 additions & 0 deletions spec/blink-ripgrep/get_command_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe("get_command", function()
})
---@diagnostic disable-next-line: missing-fields
local cmd = RipgrepCommand.get_command("hello", plugin.config)
assert(cmd)

-- don't compare the last item (the directory) as that changes depending on
-- the test environment (such as individual developers' machines or ci)
Expand All @@ -38,6 +39,7 @@ describe("get_command", function()
local plugin = blink_ripgrep.new({ context_size = 9 })
---@diagnostic disable-next-line: missing-fields
local cmd = RipgrepCommand.get_command("hello", plugin.config)
assert(cmd)

table.remove(cmd.command)
assert.are_same(cmd.command, {
Expand All @@ -57,6 +59,7 @@ describe("get_command", function()
local plugin = blink_ripgrep.new({ max_filesize = "2M" })
---@diagnostic disable-next-line: missing-fields
local cmd = RipgrepCommand.get_command("hello", plugin.config)
assert(cmd)

table.remove(cmd.command)
assert.are_same(cmd.command, {
Expand All @@ -76,6 +79,7 @@ describe("get_command", function()
local plugin = blink_ripgrep.new({ search_casing = "--smart-case" })
---@diagnostic disable-next-line: missing-fields
local cmd = RipgrepCommand.get_command("hello", plugin.config)
assert(cmd)

table.remove(cmd.command)
assert.are_same(cmd.command, {
Expand All @@ -90,4 +94,22 @@ describe("get_command", function()
"hello[\\w_-]+",
})
end)

it(
"allows disabling completion when project_root_fallback is disabled",
function()
local plugin = blink_ripgrep.new({
project_root_fallback = false,
project_root_marker = { ".notfound" },
})
---@diagnostic disable-next-line: missing-fields
local cmd, error_message =
RipgrepCommand.get_command("hello", plugin.config)
assert.are_same(cmd, nil)
assert.are_same(
error_message,
"Could not find project root, and project_root_fallback is disabled."
)
end
)
end)

0 comments on commit c693690

Please sign in to comment.