Skip to content

Instantly share code, notes, and snippets.

@bassamsdata
Created December 19, 2024 21:43
Show Gist options
  • Save bassamsdata/6df08d045973ca21d9fd06999221919f to your computer and use it in GitHub Desktop.
Save bassamsdata/6df08d045973ca21d9fd06999221919f to your computer and use it in GitHub Desktop.
MultiGrep integration for Mini.Pick in neovim using ripgrep
-- Thanks to TJ for his Telescope implemntation of MultiGrep - This is the same but for Mini.Pick
-- youtube ref: https://www.youtube.com/watch?v=xdXE1tOT-qg
local MiniPick = require("mini.pick")
local M = {}
local symbol = "::"
local function create_multigrep_picker()
return function()
local process
local set_items_opts = { do_match = false }
---@diagnostic disable-next-line: undefined-field
local spawn_opts = { cwd = vim.uv.cwd() }
local match = function(_, _, query)
-- Kill previous process
---@diagnostic disable-next-line: undefined-field
pcall(vim.loop.process_kill, process)
-- For empty query, explicitly set empty items to indicate "not working"
if #query == 0 then
return MiniPick.set_picker_items({}, set_items_opts)
end
-- Get the full query string
local full_query = table.concat(query)
-- Split on symbol
local search_pattern, file_pattern =
---@diagnostic disable-next-line: deprecated
unpack(vim.split(full_query, symbol, { plain = true }))
-- Build command
local command = {
"rg",
"--color=never",
"--no-heading",
"--with-filename",
"--line-number",
"--column",
"--smart-case",
}
-- Add search pattern
if search_pattern and search_pattern ~= "" then
table.insert(command, "-e")
table.insert(command, search_pattern)
end
-- Add file/dir pattern if provided
if file_pattern and file_pattern ~= "" then
table.insert(command, "-g")
table.insert(command, file_pattern)
end
process = MiniPick.set_picker_items_from_cli(command, {
postprocess = function(lines)
local results = {}
for _, line in ipairs(lines) do
if line ~= "" then
-- I had nightmare doing this line, I hope there will be a better way
local file, lnum, col, text = line:match("([^:]+):(%d+):(%d+):(.*)")
if file then
-- Format the item in a way that default_choose can handle - yay
results[#results + 1] = {
path = file,
lnum = tonumber(lnum),
col = tonumber(col),
text = line,
}
end
end
end
return results
end,
set_items_opts = set_items_opts,
spawn_opts = spawn_opts,
})
end
return MiniPick.start({
source = {
items = {},
name = "Multi Grep",
match = match,
show = function(buf_id, items_to_show, query)
MiniPick.default_show(buf_id, items_to_show, query, { show_icons = true })
end,
choose = MiniPick.default_choose,
},
})
end
end
M.setup = function()
MiniPick.registry.multigrep = create_multigrep_picker()
vim.keymap.set("n", "<leader>fm", function()
MiniPick.registry.multigrep()
end)
end
return M

then in your mini.nvim or mini.pick you can add:

require("multi-grep").setup()

conserdring that the code for multigrep is added to a file nvim/lua/multi-grep.lua, of course you can put the file wherever you want.

you can ofcourse change the symbol to anything like @ or :: you want except empty spaces. and you can change the keymap at the end of the file as well.

@echasnovski
Copy link

Really nice implementation! Hera are a couple of things I noticed:

  • Doing unpack(vim.split(full_query, symbol, { plain = true })) is a bit much if you know that there will always be two parts. Something like full_query:match('^(.-)' .. symbol .. '(.*)$') looks better and should be slightly more performant. Be sure to escape special Lua pattern characters if different symbol is used: like %.%. instead of ...
  • The "I had nightmare doing this line, I hope there will be a better way" comment is justified, but I don't think there is a better way to extract parts based on known mask. The default usage of ":" is a bit not robust (will not work with ":" in file name, for example), so 'mini.pick' for some time uses \000 as separator. Here are some relevant parts: extraction and rg arguments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment