Add fff.nvim and replace snacks file picker with it
This commit is contained in:
parent
de289309f7
commit
e3cb974b12
4 changed files with 229 additions and 1 deletions
|
|
@ -7,6 +7,7 @@
|
||||||
"codesnap.nvim": { "branch": "main", "commit": "be6d6b9a3b5e6999edbda76b16dace03d9bfcd3d" },
|
"codesnap.nvim": { "branch": "main", "commit": "be6d6b9a3b5e6999edbda76b16dace03d9bfcd3d" },
|
||||||
"copilot.vim": { "branch": "release", "commit": "f3d66c148aa60ad04c0a21d3e0a776459de09eb2" },
|
"copilot.vim": { "branch": "release", "commit": "f3d66c148aa60ad04c0a21d3e0a776459de09eb2" },
|
||||||
"diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" },
|
"diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" },
|
||||||
|
"fff.nvim": { "branch": "main", "commit": "a9cac80ea99eeb5882d11bdb3747b7dad8c92040" },
|
||||||
"friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" },
|
"friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" },
|
||||||
"gitsigns.nvim": { "branch": "main", "commit": "6e3c66548035e50db7bd8e360a29aec6620c3641" },
|
"gitsigns.nvim": { "branch": "main", "commit": "6e3c66548035e50db7bd8e360a29aec6620c3641" },
|
||||||
"goto-preview": { "branch": "main", "commit": "b5eb40a425caf6f8cff08aa40f2cfc0f0b0bda2c" },
|
"goto-preview": { "branch": "main", "commit": "b5eb40a425caf6f8cff08aa40f2cfc0f0b0bda2c" },
|
||||||
|
|
|
||||||
203
nvim/lua/fff_snacks_picker.lua
Normal file
203
nvim/lua/fff_snacks_picker.lua
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local STAGED_STATUSES = {
|
||||||
|
staged_new = true,
|
||||||
|
staged_modified = true,
|
||||||
|
staged_deleted = true,
|
||||||
|
renamed = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local STATUS_MAP = {
|
||||||
|
untracked = "untracked",
|
||||||
|
modified = "modified",
|
||||||
|
deleted = "deleted",
|
||||||
|
renamed = "renamed",
|
||||||
|
staged_new = "added",
|
||||||
|
staged_modified = "modified",
|
||||||
|
staged_deleted = "deleted",
|
||||||
|
ignored = "ignored",
|
||||||
|
unknown = "untracked",
|
||||||
|
}
|
||||||
|
|
||||||
|
local STATUS_ICONS = {
|
||||||
|
untracked = "?",
|
||||||
|
ignored = "!",
|
||||||
|
}
|
||||||
|
|
||||||
|
---@class FFFState
|
||||||
|
---@field current_file_cache? string
|
||||||
|
---@field file_picker? table
|
||||||
|
M.state = {}
|
||||||
|
|
||||||
|
---Get the current file path if valid
|
||||||
|
---@return string|nil
|
||||||
|
local function get_current_file()
|
||||||
|
local current_buf = vim.api.nvim_get_current_buf()
|
||||||
|
if not (current_buf and vim.api.nvim_buf_is_valid(current_buf)) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_file = vim.api.nvim_buf_get_name(current_buf)
|
||||||
|
return (current_file ~= "" and vim.fn.filereadable(current_file) == 1) and current_file or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
---Create git status object
|
||||||
|
---@param git_status string
|
||||||
|
---@return table|nil
|
||||||
|
local function create_git_status(git_status)
|
||||||
|
local mapped_status = STATUS_MAP[git_status]
|
||||||
|
if not mapped_status then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
status = mapped_status,
|
||||||
|
staged = STAGED_STATUSES[git_status] or false,
|
||||||
|
unmerged = git_status == "unmerged",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get or initialize file picker
|
||||||
|
---@return table|nil
|
||||||
|
local function get_file_picker()
|
||||||
|
if M.state.file_picker then
|
||||||
|
return M.state.file_picker
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, file_picker = pcall(require, "fff.file_picker")
|
||||||
|
if not ok then
|
||||||
|
vim.notify("Failed to load fff.file_picker: " .. file_picker, vim.log.levels.ERROR)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
M.state.file_picker = file_picker
|
||||||
|
return file_picker
|
||||||
|
end
|
||||||
|
|
||||||
|
---Format git status highlight group name
|
||||||
|
---@param status table
|
||||||
|
---@return string
|
||||||
|
local function get_status_highlight(status)
|
||||||
|
if status.unmerged then
|
||||||
|
return "SnacksPickerGitStatusUnmerged"
|
||||||
|
elseif status.staged then
|
||||||
|
return "SnacksPickerGitStatusStaged"
|
||||||
|
else
|
||||||
|
local status_name = status.status
|
||||||
|
return "SnacksPickerGitStatus" .. status_name:sub(1, 1):upper() .. status_name:sub(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get status icon text
|
||||||
|
---@param status_name string
|
||||||
|
---@return string
|
||||||
|
local function get_status_icon(status_name)
|
||||||
|
return STATUS_ICONS[status_name] or status_name:sub(1, 1):upper()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function finder(_, ctx)
|
||||||
|
local file_picker = get_file_picker()
|
||||||
|
if not file_picker then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cache current file only once per session
|
||||||
|
if not M.state.current_file_cache then
|
||||||
|
M.state.current_file_cache = get_current_file()
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, fff_result = pcall(
|
||||||
|
file_picker.search_files,
|
||||||
|
ctx.filter.search,
|
||||||
|
100,
|
||||||
|
4,
|
||||||
|
M.state.current_file_cache,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
if not ok then
|
||||||
|
vim.notify("FFF search failed: " .. fff_result, vim.log.levels.ERROR)
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local items = {}
|
||||||
|
for _, fff_item in ipairs(fff_result) do
|
||||||
|
local item = {
|
||||||
|
text = fff_item.name,
|
||||||
|
file = fff_item.path,
|
||||||
|
score = fff_item.total_frecency_score,
|
||||||
|
status = create_git_status(fff_item.git_status),
|
||||||
|
}
|
||||||
|
table.insert(items, item)
|
||||||
|
end
|
||||||
|
|
||||||
|
return items
|
||||||
|
end
|
||||||
|
|
||||||
|
local function on_close()
|
||||||
|
M.state.current_file_cache = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function format_file_git_status(item, _)
|
||||||
|
local status = item.status
|
||||||
|
local hl = get_status_highlight(status)
|
||||||
|
local icon = get_status_icon(status.status)
|
||||||
|
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
col = 0,
|
||||||
|
virt_text = { { icon, hl }, { " " } },
|
||||||
|
virt_text_pos = "right_align",
|
||||||
|
hl_mode = "combine",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function format(item, picker)
|
||||||
|
local ret = {}
|
||||||
|
|
||||||
|
if item.label then
|
||||||
|
vim.list_extend(ret, {
|
||||||
|
{ item.label, "SnacksPickerLabel" },
|
||||||
|
{ " ", virtual = true }
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
if item.status then
|
||||||
|
vim.list_extend(ret, format_file_git_status(item, picker))
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.list_extend(ret, require("snacks.picker.format").filename(item, picker))
|
||||||
|
|
||||||
|
if item.line then
|
||||||
|
Snacks.picker.highlight.format(item, item.line, ret)
|
||||||
|
table.insert(ret, { " " })
|
||||||
|
end
|
||||||
|
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.fff()
|
||||||
|
local file_picker = get_file_picker()
|
||||||
|
if not file_picker then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not file_picker.is_initialized() then
|
||||||
|
local setup_success = file_picker.setup()
|
||||||
|
if not setup_success then
|
||||||
|
vim.notify("Failed to initialize file picker", vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Snacks.picker {
|
||||||
|
title = "FFFiles",
|
||||||
|
finder = finder,
|
||||||
|
on_close = on_close,
|
||||||
|
format = format,
|
||||||
|
live = true,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
14
nvim/lua/plugs/fff.lua
Normal file
14
nvim/lua/plugs/fff.lua
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
return {
|
||||||
|
"dmtrKovalenko/fff.nvim",
|
||||||
|
build = "nix run .#release",
|
||||||
|
-- No need to lazy-load with lazy.nvim.
|
||||||
|
-- This plugin initializes itself lazily.
|
||||||
|
lazy = false,
|
||||||
|
keys = {
|
||||||
|
{
|
||||||
|
"ff", -- try it if you didn't it is a banger keybinding for a picker
|
||||||
|
function() require('fff').find_files() end,
|
||||||
|
desc = 'FFFind files',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
local fff_picker = require("fff_snacks_picker")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"folke/snacks.nvim",
|
"folke/snacks.nvim",
|
||||||
priority = 1000,
|
priority = 1000,
|
||||||
|
|
@ -17,6 +19,14 @@ return {
|
||||||
},
|
},
|
||||||
picker = {
|
picker = {
|
||||||
enabled = true,
|
enabled = true,
|
||||||
|
ui_select = true,
|
||||||
|
formatters = {
|
||||||
|
filename_first = true,
|
||||||
|
truncate = 40,
|
||||||
|
filename_only = false,
|
||||||
|
icon_width = 2,
|
||||||
|
git_status_hl = true,
|
||||||
|
},
|
||||||
sources = {
|
sources = {
|
||||||
recent = {
|
recent = {
|
||||||
filter = {
|
filter = {
|
||||||
|
|
@ -136,7 +146,7 @@ return {
|
||||||
|
|
||||||
{ "<leader>z", function() Snacks.zen() end, },
|
{ "<leader>z", function() Snacks.zen() end, },
|
||||||
|
|
||||||
{ "<leader>f", function() Snacks.picker.smart() end, },
|
{ "<leader>f", function() fff_picker.fff() end, },
|
||||||
{ "<leader>g", function() Snacks.picker.grep() end, },
|
{ "<leader>g", function() Snacks.picker.grep() end, },
|
||||||
{ "<leader>b", function() Snacks.picker.buffers() end, },
|
{ "<leader>b", function() Snacks.picker.buffers() end, },
|
||||||
{ "<leader>l", function() Snacks.picker.git_log_file() end, },
|
{ "<leader>l", function() Snacks.picker.git_log_file() end, },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue