1b279d4d10
e2e Tests / e2e (macos-latest) (push) Waiting to run
e2e Tests / e2e (windows-latest) (push) Waiting to run
Nix CI / check (push) Waiting to run
Python CI / Python bindings (macos-latest) (push) Waiting to run
Python CI / Python bindings (windows-latest) (push) Waiting to run
Build & Publish / Build Neovim aarch64-apple-darwin (push) Waiting to run
Build & Publish / Build Neovim aarch64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build Neovim x86_64-apple-darwin (push) Waiting to run
Build & Publish / Build Neovim x86_64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build C FFI aarch64-apple-darwin (push) Waiting to run
Build & Publish / Build C FFI aarch64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build C FFI x86_64-apple-darwin (push) Waiting to run
Build & Publish / Build C FFI x86_64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build MCP x86_64-apple-darwin (push) Waiting to run
Build & Publish / Build MCP x86_64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build Python wheels aarch64 (macos-latest) (push) Waiting to run
Build & Publish / Build Python wheels x86_64 (macos-latest) (push) Waiting to run
Build & Publish / Build Python wheels x86_64 (windows-latest) (push) Waiting to run
Build & Publish / Release (push) Blocked by required conditions
Build & Publish / Publish Python wheels to PyPI (push) Blocked by required conditions
Build & Publish / Publish Rust crates (push) Blocked by required conditions
Build & Publish / Publish npm packages (push) Blocked by required conditions
Rust CI / Test (macos-latest) (push) Waiting to run
Rust CI / Test (windows-latest) (push) Waiting to run
Rust CI / Fuzz Tests (macos-latest) (push) Waiting to run
Rust CI / Fuzz Tests (windows-latest) (push) Waiting to run
Build & Publish / Build MCP aarch64-apple-darwin (push) Waiting to run
Build & Publish / Build MCP aarch64-pc-windows-msvc (push) Waiting to run
Lua CI / lua-language-server type check (push) Failing after 1s
Lua CI / luacheck lint (push) Failing after 1s
Python CI / Python bindings (ubuntu-latest) (push) Failing after 1s
Build & Publish / Build Neovim aarch64-linux-android (push) Failing after 3s
Build & Publish / Build Neovim aarch64-unknown-linux-gnu (push) Failing after 0s
Build & Publish / Build Neovim aarch64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build Neovim x86_64-unknown-linux-gnu (push) Failing after 1s
Build & Publish / Build Neovim x86_64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build C FFI aarch64-linux-android (push) Failing after 1s
Build & Publish / Build C FFI aarch64-unknown-linux-gnu (push) Failing after 1s
Build & Publish / Build C FFI aarch64-unknown-linux-musl (push) Failing after 0s
Build & Publish / Build C FFI x86_64-unknown-linux-gnu (push) Failing after 1s
Build & Publish / Build C FFI x86_64-unknown-linux-musl (push) Failing after 3s
Build & Publish / Build MCP aarch64-unknown-linux-gnu (push) Failing after 1s
Build & Publish / Build MCP aarch64-unknown-linux-musl (push) Failing after 0s
Build & Publish / Build MCP x86_64-unknown-linux-gnu (push) Failing after 2s
Rust CI / Fuzz Tests (ubuntu-latest) (push) Failing after 1s
Rust CI / Build i686-unknown-linux-gnu (push) Failing after 1s
Rust CI / cargo fmt (push) Failing after 1s
Build & Publish / Build MCP x86_64-unknown-linux-musl (push) Failing after 4s
Build & Publish / Build Python wheels aarch64 (ubuntu-latest) (push) Failing after 1s
Build & Publish / Build Python wheels x86_64 (ubuntu-latest) (push) Failing after 1s
Build & Publish / Build Python sdist (push) Failing after 0s
Rust CI / Test (ubuntu-latest) (push) Failing after 1s
Rust CI / cargo clippy (push) Failing after 1s
Spelling / Spell Check with Typos (push) Failing after 1s
Stylua / Check lua files using Stylua (push) Failing after 1s
e2e Tests / e2e (ubuntu-latest) (push) Failing after 4s
e2e Tests / e2e (alpine-musl) (push) Successful in 58m54s
218 lines
7.1 KiB
Lua
218 lines
7.1 KiB
Lua
--- File Renderer
|
|
--- Simple renderer for file items with 2 functions: render_line and apply_highlights
|
|
local M = {}
|
|
|
|
--- File Item structure from Rust
|
|
--- @class FileItem
|
|
--- @field path string Absolute file path
|
|
--- @field relative_path string Relative file path from base directory
|
|
--- @field name string File name
|
|
--- @field extension string File extension
|
|
--- @field size number File size in bytes
|
|
--- @field modified number Last modified timestamp
|
|
--- @field total_frecency_score number Total frecency score
|
|
--- @field access_frecency_score number Access-based frecency score
|
|
--- @field modification_frecency_score number Modification-based frecency score
|
|
--- @field git_status string|nil Git status string (e.g. 'modified', 'untracked') if file is in git repo
|
|
|
|
--- Render a file item line
|
|
--- @param item FileItem File item from Rust
|
|
--- @param ctx ListRenderContext Render context with all state
|
|
--- @param item_idx number|nil 1-based item index in ctx.items
|
|
--- @return string[] Array of line strings (always exactly 1)
|
|
function M.render_line(item, ctx, item_idx) -- luacheck: ignore item_idx
|
|
local icons = require('fff.file_picker.icons')
|
|
local lines = {}
|
|
|
|
local icon, _ = icons.get_icon(item.name, item.extension, false)
|
|
|
|
-- Build frecency indicator (debug mode only)
|
|
local frecency = ''
|
|
if ctx.debug_enabled then
|
|
local total = item.total_frecency_score or 0
|
|
local access = item.access_frecency_score or 0
|
|
local mod = item.modification_frecency_score or 0
|
|
|
|
if total > 0 then
|
|
local indicator = ''
|
|
if mod >= 6 then
|
|
indicator = '🔥'
|
|
elseif access >= 4 then
|
|
indicator = '⭐️'
|
|
elseif total >= 3 then
|
|
indicator = '✨'
|
|
elseif total >= 1 then
|
|
indicator = '•'
|
|
end
|
|
frecency = string.format(' %s%d', indicator, total)
|
|
end
|
|
end
|
|
|
|
-- Format filename and path
|
|
-- Don't reserve space for frecency - path takes priority
|
|
local icon_width = icon and (vim.fn.strdisplaywidth(icon) + 1) or 0
|
|
local available_width = math.max(ctx.max_path_width - icon_width, 40)
|
|
local filename, dir_path = ctx.format_file_display(item, available_width)
|
|
|
|
-- Build line
|
|
local line = icon and string.format('%s %s %s%s', icon, filename, dir_path, frecency)
|
|
or string.format('%s %s%s', filename, dir_path, frecency)
|
|
|
|
local padding = math.max(0, ctx.win_width - vim.fn.strdisplaywidth(line) + 5)
|
|
table.insert(lines, line .. string.rep(' ', padding))
|
|
|
|
return lines
|
|
end
|
|
|
|
--- Apply highlights to a rendered line
|
|
--- @param item FileItem File item from Rust
|
|
--- @param ctx ListRenderContext Render context with all state
|
|
--- @param item_idx number Item index (1-based)
|
|
--- @param buf number Buffer handle
|
|
--- @param ns_id number Namespace ID
|
|
--- @param line_idx number 1-based line index in buffer
|
|
--- @param line_content string The actual line content
|
|
function M.apply_highlights(item, ctx, item_idx, buf, ns_id, line_idx, line_content)
|
|
local icons = require('fff.file_picker.icons')
|
|
local highlights = require('fff.highlights')
|
|
local file_picker = require('fff.file_picker')
|
|
|
|
local is_cursor = (ctx.cursor == item_idx)
|
|
local score = file_picker.get_file_score(item_idx)
|
|
local is_current_file = score and score.current_file_penalty and score.current_file_penalty < 0
|
|
|
|
-- Get icon and paths
|
|
local icon, icon_hl_group = icons.get_icon(item.name, item.extension, false)
|
|
local icon_width = icon and (vim.fn.strdisplaywidth(icon) + 1) or 0
|
|
local available_width = math.max(ctx.max_path_width - icon_width, 40)
|
|
local filename, dir_path = ctx.format_file_display(item, available_width)
|
|
|
|
-- 1. Cursor highlight
|
|
if is_cursor then
|
|
vim.api.nvim_buf_set_extmark(buf, ns_id, line_idx - 1, 0, {
|
|
end_col = 0,
|
|
end_row = line_idx,
|
|
hl_group = ctx.config.hl.cursor,
|
|
hl_eol = true,
|
|
priority = 100,
|
|
})
|
|
end
|
|
|
|
-- 2. Icon
|
|
if icon and icon_hl_group and vim.fn.strdisplaywidth(icon) > 0 then
|
|
local icon_hl = is_current_file and 'Comment' or icon_hl_group
|
|
vim.api.nvim_buf_set_extmark(
|
|
buf,
|
|
ns_id,
|
|
line_idx - 1,
|
|
0,
|
|
{ end_col = vim.fn.strdisplaywidth(icon), hl_group = icon_hl }
|
|
)
|
|
end
|
|
|
|
-- 3. Git text color (filename)
|
|
if ctx.config.git and ctx.config.git.status_text_color and icon and #filename > 0 then
|
|
local git_text_hl = item.git_status and highlights.get_git_text_highlight(item.git_status) or nil
|
|
if git_text_hl and git_text_hl ~= '' and not is_current_file then
|
|
local filename_start = #icon + 1
|
|
vim.api.nvim_buf_set_extmark(
|
|
buf,
|
|
ns_id,
|
|
line_idx - 1,
|
|
filename_start,
|
|
{ end_col = filename_start + #filename, hl_group = git_text_hl }
|
|
)
|
|
end
|
|
end
|
|
|
|
-- 4. Frecency indicator
|
|
if ctx.debug_enabled then
|
|
local start_pos, end_pos = line_content:find('[⭐️🔥✨•]%d+')
|
|
if start_pos and end_pos then
|
|
vim.api.nvim_buf_set_extmark(
|
|
buf,
|
|
ns_id,
|
|
line_idx - 1,
|
|
start_pos - 1,
|
|
{ end_col = end_pos, hl_group = ctx.config.hl.frecency }
|
|
)
|
|
end
|
|
end
|
|
|
|
-- 5. Directory path (dimmed)
|
|
if #filename > 0 and #dir_path > 0 then
|
|
local prefix_len = #filename + 1 -- filename bytes + space
|
|
if icon then
|
|
prefix_len = prefix_len + #icon + 1 -- if icon add icon bytes + space
|
|
end
|
|
vim.api.nvim_buf_set_extmark(
|
|
buf,
|
|
ns_id,
|
|
line_idx - 1,
|
|
prefix_len,
|
|
{ end_col = prefix_len + #dir_path, hl_group = ctx.config.hl.directory_path }
|
|
)
|
|
end
|
|
|
|
-- 6. Current file
|
|
if is_current_file then
|
|
local hl
|
|
if is_cursor then
|
|
hl = ctx.config.hl.cursor
|
|
else
|
|
hl = 'Comment'
|
|
end
|
|
|
|
vim.api.nvim_buf_set_extmark(buf, ns_id, line_idx - 1, 0, {
|
|
virt_text = { { ' ' .. ctx.config.file_picker.current_file_label, hl } },
|
|
virt_text_pos = 'right_align',
|
|
})
|
|
end
|
|
|
|
-- 7. Git sign
|
|
if item.git_status and highlights.should_show_git_border(item.git_status) then
|
|
local border_char = highlights.get_git_border_char(item.git_status)
|
|
local border_hl = highlights.get_git_sign_highlight(item.git_status, is_cursor, ctx.config.hl.cursor)
|
|
|
|
if border_hl and border_hl ~= '' then
|
|
vim.api.nvim_buf_set_extmark(buf, ns_id, line_idx - 1, 0, {
|
|
sign_text = border_char,
|
|
sign_hl_group = border_hl,
|
|
priority = 1000,
|
|
})
|
|
end
|
|
elseif is_cursor then
|
|
vim.api.nvim_buf_set_extmark(buf, ns_id, line_idx - 1, 0, {
|
|
sign_text = ' ',
|
|
sign_hl_group = ctx.config.hl.cursor,
|
|
priority = 1000,
|
|
})
|
|
end
|
|
|
|
-- 8. Selection
|
|
if ctx.selected_files and ctx.selected_files[item.relative_path] then
|
|
local selection_hl = is_cursor and ctx.config.hl.selected_active or ctx.config.hl.selected
|
|
vim.api.nvim_buf_set_extmark(buf, ns_id, line_idx - 1, 0, {
|
|
sign_text = '▊',
|
|
sign_hl_group = selection_hl,
|
|
priority = 1001,
|
|
})
|
|
end
|
|
|
|
-- 9. Query match
|
|
if ctx.query and ctx.query ~= '' then
|
|
local match_start, match_end = string.find(line_content, ctx.query, 1, true)
|
|
if match_start and match_end then
|
|
vim.api.nvim_buf_set_extmark(
|
|
buf,
|
|
ns_id,
|
|
line_idx - 1,
|
|
match_start - 1,
|
|
{ end_col = match_end, hl_group = ctx.config.hl.matched or 'IncSearch' }
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
return M
|