fix: CI and couple issues

This commit is contained in:
Dmitriy Kovalenko
2025-08-02 00:48:06 +02:00
parent 198ab9430a
commit 8358f100ec
3 changed files with 262 additions and 80 deletions
+151 -43
View File
@@ -62,62 +62,170 @@ FFF.nvim requires:
}
```
#### packer.nvim
## Default Configuration
```lua
use {
'dmtrKovalenko/fff.nvim',
requires = { 'MunifTanjim/nui.nvim' },
run = 'cargo build --release',
config = function()
require("fff").setup()
end
require("fff").setup({
width = 0.8,
height = 0.8,
preview_width = 0.5,
prompt = '🪿 ',
title = 'FFF Files',
max_results = 60, -- Maximum number of search results
max_threads = 4, -- Maximum threads for fuzzy search
keymaps = {
close = '<Esc>',
select = '<CR>',
select_split = '<C-s>',
select_vsplit = '<C-v>',
select_tab = '<C-t>',
move_up = { '<Up>', '<C-p>' },
move_down = { '<Down>', '<C-n>' },
preview_scroll_up = '<C-u>',
preview_scroll_down = '<C-d>',
},
hl = {
border = 'FloatBorder',
normal = 'Normal',
cursor = 'CursorLine',
matched = 'IncSearch',
title = 'Title',
prompt = 'Question',
active_file = 'Visual',
frecency = 'Number',
debug = 'Comment',
},
})
```
### Default Configuration
FFF.nvim comes with sensible defaults. Here's the complete default configuration:
```lua
require("fff").setup({
-- UI dimensions and appearance
width = 0.8, -- Window width as fraction of screen
height = 0.8, -- Window height as fraction of screen
preview_width = 0.5, -- Preview pane width as fraction of picker
prompt = '🪿 ', -- Input prompt symbol
title = 'FFF Files', -- Window title
max_results = 60, -- Maximum search results to display
max_threads = 4, -- Maximum threads for fuzzy search
-- Key mappings (supports both single keys and arrays for multiple bindings)
keymaps = {
close = '<Esc>',
select = '<CR>',
select_split = '<C-s>',
select_vsplit = '<C-v>',
select_tab = '<C-t>',
move_up = { '<Up>', '<C-p>' }, -- Multiple bindings supported
move_down = { '<Down>', '<C-n>' }, -- Multiple bindings supported
preview_scroll_up = '<C-u>',
preview_scroll_down = '<C-d>',
},
-- Highlight groups
hl = {
border = 'FloatBorder',
normal = 'Normal',
cursor = 'CursorLine',
matched = 'IncSearch',
title = 'Title',
prompt = 'Question',
active_file = 'Visual',
frecency = 'Number',
debug = 'Comment',
},
-- Debug options
debug = {
show_scores = false, -- Toggle with F2 or :FFFDebug
},
})
```
### Key Features
#### Multiple Key Bindings
You can assign multiple key combinations to the same action:
```lua
keymaps = {
move_up = { '<Up>', '<C-p>', '<C-k>' }, -- Three ways to move up
close = { '<Esc>', '<C-c>' }, -- Two ways to close
select = '<CR>', -- Single binding still works
}
```
#### Multiline Paste Support
The input field automatically handles multiline clipboard content by joining all lines into a single search query. This is particularly useful when copying file paths from terminal output.
#### Debug Mode
Toggle scoring information display:
- Press `F2` while in the picker
- Use `:FFFDebug` command
- Enable by default with `debug.show_scores = true`
````
#### vim-plug
```vim
Plug 'MunifTanjim/nui.nvim'
Plug 'dmtrKovalenko/fff.nvim', { 'do': 'cargo build --release' }
```
````
### Beta configuration
## Configuration
I hope you'll help us to tweak the algorithm and imporve the user experience by sharing your scores and usecasese 🫡
### Default Configuration
FFF.nvim comes with sensible defaults. Here's the complete default configuration:
```lua
{
"dmtrKovalenko/fff.nvim",
build = "cargo build --release",
config = function()
require("fff").setup({
-- UI configuration
ui = {
width = 0.8,
height = 0.8,
border = "rounded",
},
-- File picker options
picker = {
ignore_patterns = { ".git/", "node_modules/", "target/" },
show_hidden = false,
},
-- Frecency settings
frecency = {
enabled = true,
max_entries = 2000,
},
})
end,
keys = {
{
"<leader>ff",
function()
require("fff").toggle()
end,
desc = "Toggle FFF",
},
require("fff").setup({
-- UI dimensions and appearance
width = 0.8, -- Window width as fraction of screen
height = 0.8, -- Window height as fraction of screen
preview_width = 0.5, -- Preview pane width as fraction of picker
prompt = '🪿 ', -- Input prompt symbol
title = 'FFF Files', -- Window title
max_results = 60, -- Maximum search results to display
max_threads = 4, -- Maximum threads for fuzzy search
keymaps = {
close = '<Esc>',
select = '<CR>',
select_split = '<C-s>',
select_vsplit = '<C-v>',
select_tab = '<C-t>',
move_up = { '<Up>', '<C-p>' }, -- Multiple bindings supported
move_down = { '<Down>', '<C-n>' }, -- Multiple bindings supported
preview_scroll_up = '<C-u>',
preview_scroll_down = '<C-d>',
},
}
hl = {
border = 'FloatBorder',
normal = 'Normal',
cursor = 'CursorLine',
matched = 'IncSearch',
title = 'Title',
prompt = 'Question',
active_file = 'Visual',
frecency = 'Number',
debug = 'Comment',
},
debug = {
show_scores = true, -- We hope for your collaboratio
},
})
```
View File
+111 -37
View File
@@ -44,7 +44,7 @@ local default_config = {
width = 0.8,
height = 0.8,
preview_width = 0.5,
prompt = '> ',
prompt = '🪿 ',
title = 'FFF Files',
max_results = 60, -- Maximum number of search results
max_threads = 4, -- Maximum threads for fuzzy search
@@ -55,14 +55,10 @@ local default_config = {
select_split = '<C-s>',
select_vsplit = '<C-v>',
select_tab = '<C-t>',
move_up = '<Up>',
move_down = '<Down>',
move_up_alt = '<C-p>', -- Ctrl+p for up (like in many CLI tools)
move_down_alt = '<C-n>', -- Ctrl+n for down (like in many CLI tools)
move_up_vim = 'k', -- k/j only work in normal mode
move_down_vim = 'j', -- k/j only work in normal mode
preview_scroll_up = '<C-u>', -- Scroll preview up by half window height
preview_scroll_down = '<C-d>', -- Scroll preview down by half window height
move_up = { '<Up>', '<C-p>' },
move_down = { '<Down>', '<C-n>' },
preview_scroll_up = '<C-u>',
preview_scroll_down = '<C-d>',
},
hl = {
@@ -229,23 +225,57 @@ function M.setup_windows()
vim.api.nvim_win_set_option(M.state.preview_win, 'foldcolumn', '0')
end
local function normalize_keys(keys)
if type(keys) == 'string' then
return { keys }
elseif type(keys) == 'table' then
return keys
else
return {}
end
end
--- Setup keymaps
function M.setup_keymaps()
local keymaps = M.state.config.keymaps
local input_opts = { buffer = M.state.input_buf, noremap = true, silent = true }
vim.keymap.set('i', keymaps.close, M.close, input_opts)
vim.keymap.set('i', keymaps.select, M.select, input_opts)
vim.keymap.set('i', keymaps.select_split, function() M.select('split') end, input_opts)
vim.keymap.set('i', keymaps.select_vsplit, function() M.select('vsplit') end, input_opts)
vim.keymap.set('i', keymaps.select_tab, function() M.select('tab') end, input_opts)
vim.keymap.set('i', keymaps.move_up, M.move_up, input_opts)
vim.keymap.set('i', keymaps.move_down, M.move_down, input_opts)
vim.keymap.set('i', keymaps.move_up_alt, M.move_up, input_opts) -- Ctrl+p
vim.keymap.set('i', keymaps.move_down_alt, M.move_down, input_opts) -- Ctrl+n
vim.keymap.set('i', keymaps.preview_scroll_up, M.scroll_preview_up, input_opts)
vim.keymap.set('i', keymaps.preview_scroll_down, M.scroll_preview_down, input_opts)
for _, key in ipairs(normalize_keys(keymaps.close)) do
vim.keymap.set('i', key, M.close, input_opts)
end
for _, key in ipairs(normalize_keys(keymaps.select)) do
vim.keymap.set('i', key, M.select, input_opts)
end
for _, key in ipairs(normalize_keys(keymaps.select_split)) do
vim.keymap.set('i', key, function() M.select('split') end, input_opts)
end
for _, key in ipairs(normalize_keys(keymaps.select_vsplit)) do
vim.keymap.set('i', key, function() M.select('vsplit') end, input_opts)
end
for _, key in ipairs(normalize_keys(keymaps.select_tab)) do
vim.keymap.set('i', key, function() M.select('tab') end, input_opts)
end
for _, key in ipairs(normalize_keys(keymaps.move_up)) do
vim.keymap.set('i', key, M.move_up, input_opts)
end
for _, key in ipairs(normalize_keys(keymaps.move_down)) do
vim.keymap.set('i', key, M.move_down, input_opts)
end
for _, key in ipairs(normalize_keys(keymaps.preview_scroll_up)) do
vim.keymap.set('i', key, M.scroll_preview_up, input_opts)
end
for _, key in ipairs(normalize_keys(keymaps.preview_scroll_down)) do
vim.keymap.set('i', key, M.scroll_preview_down, input_opts)
end
vim.keymap.set('i', '<C-w>', function()
local col = vim.fn.col('.') - 1
@@ -269,19 +299,41 @@ function M.setup_keymaps()
local list_opts = { buffer = M.state.list_buf, noremap = true, silent = true }
vim.keymap.set('n', keymaps.close, M.close, list_opts)
vim.keymap.set('n', keymaps.select, M.select, list_opts)
vim.keymap.set('n', keymaps.select_split, function() M.select('split') end, list_opts)
vim.keymap.set('n', keymaps.select_vsplit, function() M.select('vsplit') end, list_opts)
vim.keymap.set('n', keymaps.select_tab, function() M.select('tab') end, list_opts)
vim.keymap.set('n', keymaps.move_up, M.move_up, list_opts)
vim.keymap.set('n', keymaps.move_down, M.move_down, list_opts)
vim.keymap.set('n', keymaps.move_up_alt, M.move_up, list_opts) -- Ctrl+p
vim.keymap.set('n', keymaps.move_down_alt, M.move_down, list_opts) -- Ctrl+n
vim.keymap.set('n', keymaps.move_up_vim, M.move_up, list_opts) -- k (normal mode only)
vim.keymap.set('n', keymaps.move_down_vim, M.move_down, list_opts) -- j (normal mode only)
vim.keymap.set('n', keymaps.preview_scroll_up, M.scroll_preview_up, list_opts)
vim.keymap.set('n', keymaps.preview_scroll_down, M.scroll_preview_down, list_opts)
for _, key in ipairs(normalize_keys(keymaps.close)) do
vim.keymap.set('n', key, M.close, list_opts)
end
for _, key in ipairs(normalize_keys(keymaps.select)) do
vim.keymap.set('n', key, M.select, list_opts)
end
for _, key in ipairs(normalize_keys(keymaps.select_split)) do
vim.keymap.set('n', key, function() M.select('split') end, list_opts)
end
for _, key in ipairs(normalize_keys(keymaps.select_vsplit)) do
vim.keymap.set('n', key, function() M.select('vsplit') end, list_opts)
end
for _, key in ipairs(normalize_keys(keymaps.select_tab)) do
vim.keymap.set('n', key, function() M.select('tab') end, list_opts)
end
for _, key in ipairs(normalize_keys(keymaps.move_up)) do
vim.keymap.set('n', key, M.move_up, list_opts)
end
for _, key in ipairs(normalize_keys(keymaps.move_down)) do
vim.keymap.set('n', key, M.move_down, list_opts)
end
for _, key in ipairs(normalize_keys(keymaps.preview_scroll_up)) do
vim.keymap.set('n', key, M.scroll_preview_up, list_opts)
end
for _, key in ipairs(normalize_keys(keymaps.preview_scroll_down)) do
vim.keymap.set('n', key, M.scroll_preview_down, list_opts)
end
vim.api.nvim_buf_attach(M.state.input_buf, false, {
on_lines = function()
@@ -394,14 +446,36 @@ end
function M.on_input_change()
if not M.state.active then return end
local full_line = vim.fn.getline('.')
local lines = vim.api.nvim_buf_get_lines(M.state.input_buf, 0, -1, false)
local prompt_len = #M.state.config.prompt
local query = ''
if full_line:sub(1, prompt_len) == M.state.config.prompt then query = full_line:sub(prompt_len + 1) end
if #lines > 1 then
-- join without any separator because it is a use case for a path copy from the terminal buffer
local all_text = table.concat(lines, '')
if all_text:sub(1, prompt_len) == M.state.config.prompt then
query = all_text:sub(prompt_len + 1)
else
query = all_text
end
M.state.query = query -- DEBUG
query = query:gsub('\r', ''):match('^%s*(.-)%s*$') or ''
vim.api.nvim_buf_set_option(M.state.input_buf, 'modifiable', true)
vim.api.nvim_buf_set_lines(M.state.input_buf, 0, -1, false, { M.state.config.prompt .. query })
-- Move cursor to end
vim.schedule(function()
if M.state.active and M.state.input_win and vim.api.nvim_win_is_valid(M.state.input_win) then
vim.api.nvim_win_set_cursor(M.state.input_win, { 1, prompt_len + #query })
end
end)
else
local full_line = lines[1] or ''
if full_line:sub(1, prompt_len) == M.state.config.prompt then query = full_line:sub(prompt_len + 1) end
end
M.state.query = query
if M.state.search_timer then
M.state.search_timer:stop()