Skip to content

Commit 7c8a941

Browse files
sQVedmtrKovalenko
andauthored
feat: flexible layout customization with dynamic positioning (#69)
* feat: implement prompt_position layout configuration Allows users to position the input prompt at 'top' or 'bottom' of the picker window. When prompt is at top, best matches appear at the top (closest to prompt). When prompt is at bottom (default), best matches appear at the bottom. - Add prompt_position config option with validation - Adapt list rendering order based on prompt position - Fix cursor navigation to work correctly with both layouts - Optimize array reversal for better performance - Extract helper function to reduce code duplication * feat: restructure configuration with layout object Move width/height into layout namespace for better organization. Adds max_threads field and reorganizes config hierarchy to be more intuitive and extensible. BREAKING CHANGE: config.width/height moved to config.layout.width/height * feat: implement flexible configuration deprecation system Replace hardcoded deprecation handling with rule-based system for easier maintenance. Each deprecation is now defined as a simple rule with old_path, new_path, and message. Enables easy addition of future config deprecations without touching core migration logic. * refactor: extract config resolution utilities to improve maintainability Replace repetitive inline pcall functions with centralized utilities in fff.utils. Eliminates code duplication and improves error handling consistency across dynamic configuration resolution. * feat: add flexible configuration deprecation system Implement structured deprecation rules for configuration migration, enabling smooth transition from legacy config options while maintaining backward compatibility and clear user warnings. * docs: update documentation for comprehensive layout positioning Document all supported preview positions (left, right, top, bottom) and preview_size parameter, removing outdated limitation claims and ensuring configuration examples match current capabilities. * docs: add comprehensive documentation for all configuration options Document complete configuration API including dynamic functions, all preview settings, logging, frecency, UI options, and automatic migration system. Ensures users understand both static and responsive configuration patterns. * style: align comments in documentation for improved readability Standardize comment alignment across configuration examples in README.md and doc/fff.nvim.txt to create consistent visual structure and make configuration options easier to scan and understand. * style: comprehensively align all comments in documentation Standardize comment alignment across all code blocks in both README.md and doc/fff.nvim.txt including configuration examples, method calls, and keymap examples for consistent visual structure and readability. * style: align comment formatting in documentation Standardize comment alignment for better readability and consistency across README and generated vimdoc files. * chore: run stylua * fix(ui): standardize layout coordinate calculations across preview positions Resolves inconsistent window positioning that caused preview windows to be misaligned or hidden in certain layout combinations (e.g., prompt_position='top' with preview_position='bottom'). Standardizes the coordinate calculation logic to ensure proper alignment and separation between windows for all four preview positions while maintaining the original +3 border/separator spacing. * feat: move prompt position handling to Rust layer * chore: run cargo fmt * fix: correct item selection when prompt position is bottom Fixes cursor positioning calculation to properly highlight the selected item when using bottom prompt layout. The cursor line calculation now accounts for the inverted display order in bottom mode. * WIP: fix sorting * feat: Fix ordering logic * fix configuration * stylua --------- Co-authored-by: Dmitriy Kovalenko <dmtr.kovalenko@outlook.com>
1 parent 4e1a94c commit 7c8a941

14 files changed

Lines changed: 774 additions & 371 deletions

File tree

README.md

Lines changed: 62 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -72,110 +72,69 @@ FFF.nvim comes with sensible defaults. Here's the complete configuration with al
7272

7373
```lua
7474
require('fff').setup({
75-
-- Core settings
76-
base_path = vim.fn.getcwd(), -- Base directory for file indexing
77-
max_results = 100, -- Maximum search results to display
78-
max_threads = 4, -- Maximum threads for fuzzy search
79-
prompt = '🪿 ', -- Input prompt symbol
80-
title = 'FFF Files', -- Window title
81-
ui_enabled = true, -- Enable UI (default: true)
82-
83-
-- Window dimensions
84-
width = 0.8, -- Window width as fraction of screen
85-
height = 0.8, -- Window height as fraction of screen
86-
87-
-- Preview configuration
88-
preview = {
89-
enabled = true, -- Enable preview pane
90-
width = 0.5, -- Preview width as fraction of window
91-
max_lines = 5000, -- Maximum lines to load
92-
max_size = 10 * 1024 * 1024, -- Maximum file size (10MB)
93-
imagemagick_info_format_str = '%m: %wx%h, %[colorspace], %q-bit', -- ImageMagick info format
94-
line_numbers = false, -- Show line numbers in preview
95-
wrap_lines = false, -- Wrap long lines
96-
show_file_info = true, -- Show file info header
97-
binary_file_threshold = 1024, -- Bytes to check for binary detection
98-
filetypes = { -- Per-filetype settings
99-
svg = { wrap_lines = true },
100-
markdown = { wrap_lines = true },
101-
text = { wrap_lines = true },
102-
log = { tail_lines = 100 },
75+
base_path = vim.fn.getcwd(),
76+
prompt = '🪿 ',
77+
title = 'FFFiles',
78+
max_results = 100,
79+
max_threads = 4,
80+
layout = {
81+
height = 0.8,
82+
width = 0.8,
83+
prompt_position = 'bottom', -- or 'top'
84+
preview_position = 'right', -- or 'left', 'right', 'top', 'bottom'
85+
preview_size = 0.5,
10386
},
104-
},
105-
106-
-- Layout configuration (alternative to width/height)
107-
layout = {
108-
prompt_position = 'top', -- Position of prompt ('top' or 'bottom')
109-
preview_position = 'right', -- Position of preview ('right' or 'left')
110-
preview_width = 0.4, -- Width of preview pane
111-
height = 0.8, -- Window height
112-
width = 0.8, -- Window width
113-
},
114-
115-
-- Keymaps
116-
keymaps = {
117-
close = '<Esc>',
118-
select = '<CR>',
119-
select_split = '<C-s>',
120-
select_vsplit = '<C-v>',
121-
select_tab = '<C-t>',
122-
move_up = { '<Up>', '<C-p>' }, -- Multiple bindings supported
123-
move_down = { '<Down>', '<C-n>' },
124-
preview_scroll_up = '<C-u>',
125-
preview_scroll_down = '<C-d>',
126-
toggle_debug = '<F2>', -- Toggle debug scores display
127-
},
128-
129-
-- Highlight groups
130-
hl = {
131-
border = 'FloatBorder',
132-
normal = 'Normal',
133-
cursor = 'CursorLine',
134-
matched = 'IncSearch',
135-
title = 'Title',
136-
prompt = 'Question',
137-
active_file = 'Visual',
138-
frecency = 'Number',
139-
debug = 'Comment',
140-
},
141-
142-
-- Frecency tracking (track file access patterns)
143-
frecency = {
144-
enabled = true, -- Enable frecency tracking
145-
db_path = vim.fn.stdpath('cache') .. '/fff_nvim', -- Database location
146-
},
147-
148-
-- Logging configuration
149-
logging = {
150-
enabled = true, -- Enable logging
151-
log_file = vim.fn.stdpath('log') .. '/fff.log', -- Log file location
152-
log_level = 'info', -- Log level (debug, info, warn, error)
153-
},
154-
155-
-- UI appearance
156-
ui = {
157-
wrap_paths = true, -- Wrap long file paths in list
158-
wrap_indent = 2, -- Indentation for wrapped paths
159-
max_path_width = 80, -- Maximum path width before wrapping
160-
},
161-
162-
-- Image preview (requires terminal with image support)
163-
image_preview = {
164-
enabled = true, -- Enable image previews
165-
max_width = 80, -- Maximum image width in columns
166-
max_height = 24, -- Maximum image height in lines
167-
},
168-
169-
-- Icons
170-
icons = {
171-
enabled = true, -- Enable file icons
172-
},
173-
174-
-- Debug options
175-
debug = {
176-
enabled = false, -- Enable debug mode
177-
show_scores = false, -- Show scoring information (toggle with F2)
178-
},
87+
preview = {
88+
enabled = true,
89+
max_size = 10 * 1024 * 1024, -- Do not try to read files larger than 10MB
90+
chunk_size = 8192, -- Bytes per chunk for dynamic loading (8kb - fits ~100-200 lines)
91+
binary_file_threshold = 1024, -- amount of bytes to scan for binary content (set 0 to disable)
92+
imagemagick_info_format_str = '%m: %wx%h, %[colorspace], %q-bit',
93+
line_numbers = false,
94+
wrap_lines = false,
95+
show_file_info = true,
96+
filetypes = {
97+
svg = { wrap_lines = true },
98+
markdown = { wrap_lines = true },
99+
text = { wrap_lines = true },
100+
},
101+
},
102+
keymaps = {
103+
close = '<Esc>',
104+
select = '<CR>',
105+
select_split = '<C-s>',
106+
select_vsplit = '<C-v>',
107+
select_tab = '<C-t>',
108+
move_up = { '<Up>', '<C-p>' },
109+
move_down = { '<Down>', '<C-n>' },
110+
preview_scroll_up = '<C-u>',
111+
preview_scroll_down = '<C-d>',
112+
toggle_debug = '<F2>',
113+
},
114+
hl = {
115+
border = 'FloatBorder',
116+
normal = 'Normal',
117+
cursor = 'CursorLine',
118+
matched = 'IncSearch',
119+
title = 'Title',
120+
prompt = 'Question',
121+
active_file = 'Visual',
122+
frecency = 'Number',
123+
debug = 'Comment',
124+
},
125+
frecency = {
126+
enabled = true,
127+
db_path = vim.fn.stdpath('cache') .. '/fff_nvim',
128+
},
129+
debug = {
130+
enabled = false, -- Set to true to show scores in the UI
131+
show_scores = false,
132+
},
133+
logging = {
134+
enabled = true,
135+
log_file = vim.fn.stdpath('log') .. '/fff.log',
136+
log_level = 'info',
137+
}
179138
})
180139
```
181140

doc/fff.nvim.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ all available options:
114114
layout = {
115115
prompt_position = 'top', -- Position of prompt ('top' or 'bottom')
116116
preview_position = 'right', -- Position of preview ('right' or 'left')
117-
preview_width = 0.4, -- Width of preview pane
117+
preview_width = 0.5, -- Width of preview pane
118118
height = 0.8, -- Window height
119119
width = 0.8, -- Window width
120120
},

lua/fff/file_picker/init.lua

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ function M.setup(config)
4141
preview_down = '<C-d>',
4242
},
4343
layout = {
44-
prompt_position = 'top',
44+
prompt_position = 'bottom',
4545
preview_position = 'right',
46-
preview_width = 0.4,
46+
preview_size = 0.4,
4747
height = 0.8,
4848
width = 0.8,
4949
},
@@ -84,15 +84,18 @@ end
8484
--- Search files with fuzzy matching using blink.cmp's advanced algorithm
8585
--- @param query string Search query
8686
--- @param max_results number Maximum number of results (optional)
87+
--- @param max_threads number Maximum number of threads (optional)
8788
--- @param current_file string|nil Path to current file to deprioritize (optional)
89+
--- @param reverse_order boolean Reverse order of results
8890
--- @return table List of matching files
89-
function M.search_files(query, max_results, max_threads, current_file)
91+
function M.search_files(query, max_results, max_threads, current_file, reverse_order)
9092
if not M.state.initialized then return {} end
9193

9294
max_results = max_results or M.config.max_results
9395
max_threads = max_threads or M.config.max_threads
9496

95-
local ok, search_result = pcall(fuzzy.fuzzy_search_files, query, max_results, max_threads, current_file)
97+
local ok, search_result =
98+
pcall(fuzzy.fuzzy_search_files, query, max_results, max_threads, current_file, reverse_order)
9699
if not ok then
97100
vim.notify('Failed to search files: ' .. tostring(search_result), vim.log.levels.ERROR)
98101
return {}

lua/fff/file_picker/preview.lua

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,11 @@ function M.is_binary_file_async(file_path, callback)
288288
end
289289
end
290290

291-
-- Check file content asynchronously
291+
if M.config.binary_file_threshold <= 0 then
292+
callback(false)
293+
return
294+
end
295+
292296
vim.uv.fs_open(file_path, 'r', 438, function(err, fd)
293297
if err or not fd then
294298
callback(false)

0 commit comments

Comments
 (0)