d5f207424b
e2e Tests / e2e (macos-latest) (push) Waiting to run
Nix CI / check (push) Waiting to run
Python CI / Python bindings (macos-latest) (push) Waiting to run
e2e Tests / e2e (windows-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 aarch64-apple-darwin (push) Waiting to run
Build & Publish / Build MCP aarch64-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 / Fuzz Tests (windows-latest) (push) Waiting to run
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
Python CI / Python bindings (ubuntu-latest) (push) Failing after 0s
Build & Publish / Build Neovim aarch64-linux-android (push) Failing after 0s
Build & Publish / Build Neovim aarch64-unknown-linux-gnu (push) Failing after 0s
Build & Publish / Build MCP x86_64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build Python wheels aarch64 (ubuntu-latest) (push) Failing after 0s
Build & Publish / Build Python wheels x86_64 (ubuntu-latest) (push) Failing after 1s
Build & Publish / Build Python sdist (push) Failing after 1s
Rust CI / Fuzz Tests (ubuntu-latest) (push) Failing after 1s
Rust CI / Build i686-unknown-linux-gnu (push) Failing after 0s
Rust CI / cargo clippy (push) Failing after 1s
e2e Tests / e2e (ubuntu-latest) (push) Failing after 1s
Lua CI / luacheck lint (push) Failing after 1s
Build & Publish / Build Neovim aarch64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build Neovim x86_64-unknown-linux-gnu (push) Failing after 0s
Build & Publish / Build Neovim x86_64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build C FFI aarch64-linux-android (push) Failing after 0s
Build & Publish / Build C FFI aarch64-unknown-linux-gnu (push) Failing after 0s
Build & Publish / Build C FFI aarch64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build C FFI x86_64-unknown-linux-gnu (push) Failing after 0s
Build & Publish / Build C FFI x86_64-unknown-linux-musl (push) Failing after 1s
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 1s
Spelling / Spell Check with Typos (push) Failing after 0s
Rust CI / Test (ubuntu-latest) (push) Failing after 0s
Rust CI / cargo fmt (push) Failing after 0s
Stylua / Check lua files using Stylua (push) Failing after 1s
Lua CI / lua-language-server type check (push) Failing after 12m29s
e2e Tests / e2e (alpine-musl) (push) Successful in 57m31s
237 lines
7.1 KiB
Lua
237 lines
7.1 KiB
Lua
local M = {}
|
|
|
|
local preview = require('fff.file_picker.preview')
|
|
local utils = require('fff.utils')
|
|
local picker_ui_state = require('fff.picker_ui.picker_ui_state')
|
|
|
|
local canonicalize_fff_path = utils.canonicalize_fff_path
|
|
|
|
-- Parent module reference (set by picker_ui.lua during initialization).
|
|
---@type table
|
|
local P = nil
|
|
|
|
function M.init(parent_module) P = parent_module end
|
|
|
|
local S = picker_ui_state.state
|
|
|
|
function M.close_preview_timer(timer)
|
|
timer = timer or S.preview_timer
|
|
if not timer then return end
|
|
|
|
if S.preview_timer == timer then S.preview_timer = nil end
|
|
if timer:is_closing() then return end
|
|
|
|
timer:stop()
|
|
timer:close()
|
|
end
|
|
|
|
function M.update_preview_debounced()
|
|
M.close_preview_timer()
|
|
|
|
local timer = vim.uv.new_timer()
|
|
S.preview_timer = timer
|
|
timer:start(
|
|
S.preview_debounce_ms,
|
|
0,
|
|
vim.schedule_wrap(function()
|
|
local is_current = S.preview_timer == timer
|
|
M.close_preview_timer(timer)
|
|
if is_current and P.state.active then M.update_preview() end
|
|
end)
|
|
)
|
|
end
|
|
|
|
--- Smart preview update for cursor movement.
|
|
function M.update_preview_smart()
|
|
if not S.preview_visible then return end
|
|
if not P.state.active then return end
|
|
|
|
local items = S.filtered_items
|
|
if #items == 0 or S.cursor > #items then
|
|
M.update_preview()
|
|
return
|
|
end
|
|
|
|
local item = items[S.cursor]
|
|
if not item then
|
|
M.update_preview()
|
|
return
|
|
end
|
|
|
|
if S.last_preview_file == item.relative_path then
|
|
M.update_preview()
|
|
return
|
|
end
|
|
|
|
M.update_preview_debounced()
|
|
end
|
|
|
|
function M.update_preview_title(item, location)
|
|
if not S.preview_win or not vim.api.nvim_win_is_valid(S.preview_win) then return end
|
|
|
|
local relative_path = item.relative_path
|
|
local max_title_width = vim.api.nvim_win_get_width(S.preview_win)
|
|
|
|
local suffix = ''
|
|
local is_grep_item = S.mode == 'grep' or S.suggestion_source == 'grep'
|
|
if is_grep_item and location and location.line then suffix = ':' .. tostring(location.line) end
|
|
|
|
local display_path = relative_path .. suffix
|
|
local title
|
|
|
|
if #display_path + 2 <= max_title_width then
|
|
title = string.format(' %s ', display_path)
|
|
else
|
|
local available_chars = max_title_width - 2
|
|
|
|
local filename = vim.fn.fnamemodify(relative_path, ':t') .. suffix
|
|
if available_chars <= 3 then
|
|
title = filename
|
|
else
|
|
if #filename + 5 <= available_chars then
|
|
local normalized_path = vim.fs.normalize(relative_path)
|
|
local path_parts = vim.split(normalized_path, '[/\\]', { plain = false })
|
|
|
|
local segments = {}
|
|
for _, part in ipairs(path_parts) do
|
|
if part ~= '' then table.insert(segments, part) end
|
|
end
|
|
|
|
segments[#segments] = vim.fn.fnamemodify(relative_path, ':t') .. suffix
|
|
|
|
local segments_to_show = { segments[#segments] }
|
|
local current_length = #segments_to_show[1] + 4
|
|
|
|
for i = #segments - 1, 1, -1 do
|
|
local segment = segments[i]
|
|
local new_length = current_length + #segment + 1
|
|
|
|
if new_length <= available_chars then
|
|
table.insert(segments_to_show, 1, segment)
|
|
current_length = new_length
|
|
else
|
|
break
|
|
end
|
|
end
|
|
|
|
if #segments_to_show == #segments then
|
|
title = string.format(' %s ', table.concat(segments_to_show, '/'))
|
|
else
|
|
title = string.format(' ../%s ', table.concat(segments_to_show, '/'))
|
|
end
|
|
else
|
|
local truncated = filename:sub(1, available_chars - 3) .. '...'
|
|
title = string.format(' %s ', truncated)
|
|
end
|
|
end
|
|
end
|
|
|
|
vim.api.nvim_win_set_config(S.preview_win, {
|
|
title = title,
|
|
title_pos = 'left',
|
|
})
|
|
end
|
|
|
|
function M.update_preview()
|
|
if not S.preview_visible then return end
|
|
if not P.state.active then return end
|
|
|
|
local items = S.filtered_items
|
|
if #items == 0 or S.cursor > #items then
|
|
M.clear_preview()
|
|
S.last_preview_file = nil
|
|
S.last_preview_location = nil
|
|
return
|
|
end
|
|
|
|
local item = items[S.cursor]
|
|
if not item then
|
|
M.clear_preview()
|
|
S.last_preview_file = nil
|
|
S.last_preview_location = nil
|
|
return
|
|
end
|
|
|
|
local effective_location = S.location
|
|
|
|
if not effective_location and S.query and S.query ~= '' then
|
|
local line_str = S.query:match(':(%d+)$')
|
|
if line_str then
|
|
local line_num = tonumber(line_str)
|
|
if line_num and line_num > 0 then
|
|
local l, c = S.query:match(':(%d+):(%d+)$')
|
|
if l then
|
|
effective_location = { line = tonumber(l), col = tonumber(c) }
|
|
else
|
|
effective_location = { line = line_num }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local is_grep_item = S.mode == 'grep' or S.suggestion_source == 'grep'
|
|
if is_grep_item and item.line_number and item.line_number > 0 then
|
|
effective_location = { line = item.line_number }
|
|
if item.col and item.col > 0 then effective_location.col = item.col + 1 end
|
|
effective_location.grep_query = S.query
|
|
if S.grep_mode == 'fuzzy' and item.match_ranges then effective_location.fuzzy_match_ranges = item.match_ranges end
|
|
end
|
|
|
|
local location_changed = not vim.deep_equal(S.last_preview_location, effective_location)
|
|
|
|
if S.last_preview_file == item.relative_path and not location_changed then return end
|
|
|
|
if S.last_preview_file == item.relative_path and location_changed then
|
|
S.last_preview_location = effective_location and vim.deepcopy(effective_location) or nil
|
|
preview.state.location = effective_location
|
|
if is_grep_item and effective_location and effective_location.line then
|
|
M.update_preview_title(item, effective_location)
|
|
end
|
|
if S.preview_buf and vim.api.nvim_buf_is_valid(S.preview_buf) then
|
|
preview.apply_location_highlighting(S.preview_buf)
|
|
end
|
|
return
|
|
end
|
|
|
|
preview.clear()
|
|
|
|
S.last_preview_file = item.relative_path
|
|
S.last_preview_location = effective_location and vim.deepcopy(effective_location) or nil
|
|
|
|
M.update_preview_title(item, effective_location)
|
|
|
|
if S.file_info_buf then
|
|
preview.update_file_info_buffer(item, S.file_info_buf, S.cursor, S.preview_win)
|
|
if S.file_info_win and vim.api.nvim_win_is_valid(S.file_info_win) then
|
|
local rel = item.relative_path or item.path or ''
|
|
pcall(vim.api.nvim_win_set_config, S.file_info_win, { title = ' ' .. rel .. ' ', title_pos = 'left' })
|
|
end
|
|
end
|
|
|
|
preview.set_preview_window(S.preview_win)
|
|
preview.preview(canonicalize_fff_path(item.relative_path), S.preview_buf, effective_location, item.is_binary)
|
|
end
|
|
|
|
function M.clear_preview()
|
|
if not P.state.active then return end
|
|
if not S.preview_visible then return end
|
|
|
|
vim.api.nvim_win_set_config(S.preview_win, {
|
|
title = ' Preview ',
|
|
title_pos = 'left',
|
|
})
|
|
|
|
if S.file_info_buf then
|
|
vim.api.nvim_set_option_value('modifiable', true, { buf = S.file_info_buf })
|
|
vim.api.nvim_buf_set_lines(S.file_info_buf, 0, -1, false, {})
|
|
vim.api.nvim_set_option_value('modifiable', false, { buf = S.file_info_buf })
|
|
pcall(vim.api.nvim_buf_clear_namespace, S.file_info_buf, preview.file_info_ns, 0, -1)
|
|
end
|
|
|
|
vim.api.nvim_set_option_value('modifiable', true, { buf = S.preview_buf })
|
|
vim.api.nvim_buf_set_lines(S.preview_buf, 0, -1, false, { 'No preview available' })
|
|
vim.api.nvim_set_option_value('modifiable', false, { buf = S.preview_buf })
|
|
end
|
|
|
|
return M
|