A plugin for using jfind as a neovim fuzzy finder. Works for macos, linux, and windows via WSL.
- jfind (Required)
- fdfind (Recommended as a faster alternative to
find
) - ripgrep (Recommended as a faster alternative to
grep
) - bat (Recommended as a syntax highlighted alternative to
cat
)
You can install jfind with this one liner. You will need git, cmake, and make.
git clone https://github.com/jake-stewart/jfind && cd jfind && cmake -S . -B build && cd build && sudo make install
If you are migrating to 2.0 from an earlier version, make sure to recompile jfind!
{ "jake-stewart/jfind.nvim", branch = "2.0" }
Plug "jake-stewart/jfind.nvim", { "branch": "2.0" }
call dein#add("jake-stewart/jfind.nvim", { "rev": "2.0" })
use { "jake-stewart/jfind.nvim", branch = "2.0" }
local jfind = require("jfind")
local key = require("jfind.key")
jfind.setup({
exclude = {
".git",
".idea",
".vscode",
".sass-cache",
".class",
"__pycache__",
"node_modules",
"target",
"build",
"tmp",
"assets",
"dist",
"public",
"*.iml",
"*.meta"
},
windowBorder = true,
tmux = true,
});
-- fuzzy file search can be started simply with
vim.keymap.set("n", "<c-f>", jfind.findFile)
-- or you can provide more customization
-- for more information, read the "Lua Jfind Interface" section
vim.keymap.set("n", "<c-f>", function()
jfind.findFile({
formatPaths = true,
hidden = true,
queryPosition = "top",
preview = true,
previewPosition = "right",
callback = {
[key.DEFAULT] = vim.cmd.edit,
[key.CTRL_S] = vim.cmd.split,
[key.CTRL_V] = vim.cmd.vsplit,
}
})
end)
-- make sure to rebuld jfind if you want live grep
vim.keymap.set("n", "<leader><c-f>", function()
jfind.liveGrep({
exclude = {"*.hpp"}, -- overrides setup excludes
hidden = true, -- grep hidden files/directories
caseSensitivity = "smart", -- sensitive, insensitive, smart
-- will use vim settings by default
preview = true,
previewPosition = "top",
callback = {
[key.DEFAULT] = jfind.editGotoLine,
[key.CTRL_B] = jfind.splitGotoLine,
[key.CTRL_N] = jfind.vsplitGotoLine,
}
})
end)
Option | Description |
---|---|
tmux |
a boolean of whether a tmux window is preferred over a neovim window. If tmux is not active, then this value is ignored. Default is false . |
exclude |
a list of strings of files/directories that should be ignored. Entries can contain wildcard matching (e.g. *.png ). Default is an empty list. |
maxWidth |
An integer of how large in width the jfind can be as fullscreen until it becomes a popup window. default is 120 . |
maxHeight |
An integer of how large in height the jfind can be as fullscreen until it becomes a popup window. default is 28 . |
windowBorder |
A boolean of whether jfind should be surrounded with a border. Default is true . |
This section is useful if you want to create your own fuzzy finders using jfind, or if you want to understand the configuration better.
This plugin provides the jfind()
function, which can be accessed via
require("jfind").jfind
. This function opens a tmux or nvim popup based on
user configuration, performs fuzzy finding, and calls a provided callback with
the result if there is one.
Below is an example usage of jfind()
. It takes a script, which in this case
is the seq
command. It also provides an argument to the seq
command.
Upon completion, the result of the fuzzy finding will be printed using the
provided print
callback. The seq
command could just as easily have been a
path to a shell script, as is the case for findFile()
.
local jfind = require("jfind")
jfind.jfind({
script = "seq",
args = {100},
callback = print
})
Instead of a program/script, you can provide a list of strings with the input option. This is useful for generating the input data in lua instead of a shell script, since a shell script does not have access to neovim state.
local jfind = require("jfind")
jfind.jfind({
input = {"one", "two", "three", "four"},
callback = print
})
The callback option is either a function or a table. You can provide a table
if you want different actions for different keybinds. For example, you may want
to vertically split when pressing <c-v>
on an item. Below is an example of
having multiple keybindings.
local jfind = require("jfind")
local key = require("jfind.key")
jfind.jfind({
script = "ls",
callback = {
[key.DEFAULT] = vim.cmd.edit,
[key.CTRL_V] = vim.cmd.vsplit,
[key.CTRL_S] = vim.cmd.split
}
})
the key.DEFAULT
applies to the user hitting enter or double clicking on an
item, unless overridden.
You may have noticed that the builtin findFile()
accepts an option called
formatPaths
. When this option is true, the jfind window has two columns,
where the one on the right shows the full path, but is not searchable. These
are called hints. They are useful for separating what the user is searching for
from the result we want.
For instance, when I am searching for a path, I do not want to search the full
~/projects/foo/bar/baz/item/item.java
, I just want to search the final
item/item.java
. In this case, the item/item.java
would be the search item,
and ~/projects/foo/bar/baz/item/item.java
would be the hint. We can then use
the hint when actually editing the file, since trying to edit item/item.java
is missing its hierarchy.
local jfind = require("jfind")
jfind.jfind({
input = {"item one", "hint one", "item two", "hint two"},
hints = true,
callback = function(result)
print("result: " .. result)
end
})
You can run a command on the selected item and use the output as a preview for that item. You will need "figlet" to run the following example:
local jfind = require("jfind")
jfind.jfind({
input = {1, 2, 3, 4, 5},
preview = "figlet",
previewPosition = "right",
callback = function(result)
print("result: " .. result)
end
})
Sometimes it may be useful to wrap each callback in a function. This can save needing the same boilerplate for every callback.
local jfind = require("jfind")
local key = require("jfind.key")
jfind.jfind({
input = get_buffers(),
hints = true,
callbackWrapper = function(callback, path)
-- do something to path, and then call the provided callback
callback(path)
end,
callback = {
[key.DEFAULT] = vim.cmd.edit,
[key.CTRL_S] = vim.cmd.split,
[key.CTRL_V] = vim.cmd.vsplit,
}
})
This example combines it all together to create a fuzzy finder for open buffers.
local jfind = require("jfind")
local key = require("jfind.key")
local function get_buffers()
local buffers = {}
for i, buf_hndl in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_is_loaded(buf_hndl) then
local path = vim.api.nvim_buf_get_name(buf_hndl)
if path ~= nil and path ~= "" then
buffers[i * 2 - 1] = jfind.formatPath(path)
buffers[i * 2] = path
end
end
end
return buffers
end
jfind.jfind({
input = get_buffers(),
hints = true,
callback = {
[key.DEFAULT] = vim.cmd.edit,
[key.CTRL_S] = vim.cmd.split,
[key.CTRL_V] = vim.cmd.vsplit,
}
})
liveGrep()
uses grep and ripgrep for the matching process.
Every time you type a key in jfind, a new process is spawned and fed the current query.
You can do the same thing with the command
option in jfind.jfind()
.
A good example to play with would be the following:
jfind.jfind({
command = "seq {}",
callback = print
})