Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,19 @@ require('fff').setup({
preview_scroll_down = '<C-d>',
toggle_debug = '<F2>',
},
hl = {
border = 'FloatBorder',
normal = 'Normal',
cursor = 'CursorLine',
matched = 'IncSearch',
title = 'Title',
prompt = 'Question',
active_file = 'Visual',
frecency = 'Number',
debug = 'Comment',
ui = {
picker = 'default',
hl = {
border = 'FloatBorder',
normal = 'Normal',
cursor = 'CursorLine',
matched = 'IncSearch',
title = 'Title',
prompt = 'Question',
active_file = 'Visual',
frecency = 'Number',
debug = 'Comment',
},
},
frecency = {
enabled = true,
Expand Down
25 changes: 14 additions & 11 deletions doc/fff.nvim.txt
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,20 @@ all available options:
preview_scroll_down = '<C-d>',
toggle_debug = '<F2>',
},
hl = {
border = 'FloatBorder',
normal = 'Normal',
cursor = 'CursorLine',
matched = 'IncSearch',
title = 'Title',
prompt = 'Question',
active_file = 'Visual',
frecency = 'Number',
debug = 'Comment',
},
ui = {
picker = 'default', -- one of 'default' or 'mini' (requires `mini.pick`)
hl = {
border = 'FloatBorder',
normal = 'Normal',
cursor = 'CursorLine',
matched = 'IncSearch',
title = 'Title',
prompt = 'Question',
active_file = 'Visual',
frecency = 'Number',
debug = 'Comment',
},
},
frecency = {
enabled = true,
db_path = vim.fn.stdpath('cache') .. '/fff_nvim',
Expand Down
23 changes: 13 additions & 10 deletions lua/fff/conf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -141,16 +141,19 @@ local function init()
preview_scroll_down = '<C-d>',
toggle_debug = '<F2>',
},
hl = {
border = 'FloatBorder',
normal = 'Normal',
cursor = 'CursorLine',
matched = 'IncSearch',
title = 'Title',
prompt = 'Question',
active_file = 'Visual',
frecency = 'Number',
debug = 'Comment',
ui = {
picker = 'default',
hl = {
border = 'FloatBorder',
normal = 'Normal',
cursor = 'CursorLine',
matched = 'IncSearch',
title = 'Title',
prompt = 'Question',
active_file = 'Visual',
frecency = 'Number',
debug = 'Comment',
},
},
frecency = {
enabled = true,
Expand Down
61 changes: 53 additions & 8 deletions lua/fff/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,27 @@ function M.setup(config) vim.g.fff = config end

--- Find files in current directory
function M.find_files()
local picker_ok, picker_ui = pcall(require, 'fff.picker_ui')
if picker_ok then
picker_ui.open()
local config = require('fff.conf').get()
local picker = config.ui.picker

if picker == 'default' then
local picker_ok, picker_ui = pcall(require, 'fff.ui.default')
if picker_ok then
picker_ui.open()
else
vim.notify('Failed to load picker UI', vim.log.levels.ERROR)
end
elseif picker == 'mini' then
local ok, _ = pcall(require, 'mini.pick')
if not ok then
vim.notify('mini.pick is not installed', vim.log.levels.ERROR)
return
end
local mini_picker = require('fff.ui.mini')
if not mini_picker.is_initialized() then mini_picker.setup() end
MiniPick.registry.fffiles()
else
vim.notify('Failed to load picker UI', vim.log.levels.ERROR)
vim.notify('Unknown picker:' .. picker, vim.log.levels.ERROR)
end
end

Expand Down Expand Up @@ -144,6 +160,19 @@ function M.health_check()
end
end

local optional_plugins = {
{ plugin = 'mini.pick', desc = 'Use mini.pick UI' },
}

for _, dep in ipairs(optional_plugins) do
local ok, _ = pcall(require, dep.plugin)
if not ok then
table.insert(health.messages, string.format('Optional: %s not found (%s)', dep.plugin, dep.desc))
else
table.insert(health.messages, string.format('✓ %s found', dep.plugin))
end
end

if health.ok then
vim.notify('FFF health check passed ✓', vim.log.levels.INFO)
else
Expand All @@ -170,11 +199,27 @@ function M.find_files_in_dir(directory)

M.change_indexing_directory(directory)

local picker_ok, picker_ui = pcall(require, 'fff.picker_ui')
if picker_ok then
picker_ui.open({ title = 'Files in ' .. vim.fn.fnamemodify(directory, ':t') })
local config = require('fff.conf').get()
local picker = config.ui.picker

if picker == 'default' then
local picker_ok, picker_ui = pcall(require, 'fff.picker_ui')
if picker_ok then
picker_ui.open({ title = 'Files in ' .. vim.fn.fnamemodify(directory, ':t') })
else
vim.notify('Failed to load picker UI', vim.log.levels.ERROR)
end
elseif picker == 'mini' then
local ok, _ = pcall(require, 'mini.pick')
if not ok then
vim.notify('mini.pick is not installed', vim.log.levels.ERROR)
return
end
local mini_picker = require('fff.ui.mini')
if not mini_picker.is_initialized() then mini_picker.setup() end
MiniPick.registry.fffiles()
else
vim.notify('Failed to load picker UI', vim.log.levels.ERROR)
vim.notify('Unknown picker:' .. picker, vim.log.levels.ERROR)
end
end

Expand Down
14 changes: 7 additions & 7 deletions lua/fff/picker_ui.lua → lua/fff/ui/default.lua
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ end

--- Setup window options
function M.setup_windows()
local hl = M.state.config.hl
local hl = M.state.config.ui.hl
local win_hl = string.format('Normal:%s,FloatBorder:%s,FloatTitle:%s', hl.normal, hl.border, hl.title)
vim.api.nvim_win_set_option(M.state.input_win, 'wrap', false)
vim.api.nvim_win_set_option(M.state.input_win, 'cursorline', false)
Expand Down Expand Up @@ -853,7 +853,7 @@ function M.render_list()
vim.api.nvim_buf_add_highlight(
M.state.list_buf,
M.state.ns_id,
M.state.config.hl.active_file,
M.state.config.ui.hl.active_file,
cursor_line - 1,
0,
-1
Expand All @@ -866,7 +866,7 @@ function M.render_list()

if remaining_width > 0 then
vim.api.nvim_buf_set_extmark(M.state.list_buf, M.state.ns_id, cursor_line - 1, -1, {
virt_text = { { string.rep(' ', remaining_width), M.state.config.hl.active_file } },
virt_text = { { string.rep(' ', remaining_width), M.state.config.ui.hl.active_file } },
virt_text_pos = 'eol',
})
end
Expand Down Expand Up @@ -906,7 +906,7 @@ function M.render_list()
vim.api.nvim_buf_add_highlight(
M.state.list_buf,
M.state.ns_id,
M.state.config.hl.frecency,
M.state.config.ui.hl.frecency,
line_idx - 1,
star_start - 1,
star_end
Expand All @@ -932,7 +932,7 @@ function M.render_list()
vim.api.nvim_buf_add_highlight(M.state.list_buf, M.state.ns_id, 'Comment', line_idx - 1, 0, -1)
end

local virt_text_hl = is_cursor_line and M.state.config.hl.active_file or 'Comment'
local virt_text_hl = is_cursor_line and M.state.config.ui.hl.active_file or 'Comment'
vim.api.nvim_buf_set_extmark(M.state.list_buf, M.state.ns_id, line_idx - 1, 0, {
virt_text = { { ' (current)', virt_text_hl } },
virt_text_pos = 'right_align',
Expand All @@ -952,7 +952,7 @@ function M.render_list()
end

local final_border_hl = border_hl ~= '' and border_hl
or (is_cursor_line and M.state.config.hl.active_file or '')
or (is_cursor_line and M.state.config.ui.hl.active_file or '')

if final_border_hl ~= '' or is_cursor_line then
vim.api.nvim_buf_set_extmark(M.state.list_buf, M.state.ns_id, line_idx - 1, 0, {
Expand All @@ -967,7 +967,7 @@ function M.render_list()
vim.api.nvim_buf_add_highlight(
M.state.list_buf,
M.state.ns_id,
config.hl.matched or 'IncSearch',
config.ui.hl.matched or 'IncSearch',
line_idx - 1,
match_start - 1,
match_end
Expand Down
102 changes: 102 additions & 0 deletions lua/fff/ui/mini.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---@class PickerItem
---@field text string
---@field path string

local M = {
---@class FFFPickerState
---@field current_file_cache string
state = {},
ns_id = vim.api.nvim_create_namespace('MiniPick FFFiles Picker'),
}

---@param query string|nil
---@return PickerItem[]
local function find(query)
local file_picker = require('fff.file_picker')

query = query or ''
local fff_result = file_picker.search_files(query, 100, 4, M.state.current_file_cache, false)

local items = {}
for _, fff_item in ipairs(fff_result) do
local item = {
text = fff_item.relative_path,
path = fff_item.path,
}
table.insert(items, item)
end

return items
end

---@param items PickerItem[]
local function show(buf_id, items)
local icon_data = {}

-- Show items
local items_to_show = {}
for i, item in ipairs(items) do
local icon, hl, _ = MiniIcons.get('file', item.text)
icon_data[i] = { icon = icon, hl = hl }

items_to_show[i] = string.format('%s %s', icon, item.text)
end
vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, items_to_show)

vim.api.nvim_buf_clear_namespace(buf_id, M.ns_id, 0, -1)

local icon_extmark_opts = { hl_mode = 'combine', priority = 200 }
for i, item in ipairs(items) do
-- Highlight Icons
icon_extmark_opts.hl_group = icon_data[i].hl
icon_extmark_opts.end_row, icon_extmark_opts.end_col = i - 1, 1
vim.api.nvim_buf_set_extmark(buf_id, M.ns_id, i - 1, 0, icon_extmark_opts)
end
end

local function run()
-- Setup fff.nvim
local file_picker = require('fff.file_picker')
if not file_picker.is_initialized() then
local setup_success = file_picker.setup()
if not setup_success then
vim.notify('Could not setup fff.nvim', vim.log.levels.ERROR)
return
end
end

-- Cache current file to deprioritize in fff.nvim
if not M.state.current_file_cache then
local current_buf = vim.api.nvim_get_current_buf()
if current_buf and vim.api.nvim_buf_is_valid(current_buf) then
local current_file = vim.api.nvim_buf_get_name(current_buf)
if current_file ~= '' and vim.fn.filereadable(current_file) == 1 then
local relative_path = vim.fs.relpath(vim.uv.cwd(), current_file)
M.state.current_file_cache = relative_path
else
M.state.current_file_cache = nil
end
end
end

-- Start picker
MiniPick.start({
source = {
name = 'FFFiles',
items = find,
match = function(_, _, query)
local items = find(table.concat(query))
MiniPick.set_picker_items(items, { do_match = false })
end,
show = show,
},
})

M.state.current_file_cache = nil -- Reset cache
end

function M.setup() MiniPick.registry.fffiles = run end

function M.is_initialized() return MiniPick.registry.fffiles ~= nil end

return M