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
213 lines
7.2 KiB
Lua
213 lines
7.2 KiB
Lua
--- FFF.nvim File Picker - High-performance file picker for Neovim
|
|
--- Uses advanced fuzzy search algorithm with frecency scoring
|
|
|
|
local M = {}
|
|
local fuzzy = require('fff.core').ensure_initialized()
|
|
|
|
M.state = {
|
|
initialized = false,
|
|
base_path = nil,
|
|
last_scan_time = 0,
|
|
}
|
|
|
|
function M.setup()
|
|
local config = require('fff.conf').get()
|
|
M.state.initialized = true
|
|
M.state.base_path = config.base_path
|
|
|
|
return true
|
|
end
|
|
|
|
--- Trigger scan of files in the current directory (asynchronous)
|
|
function M.scan_files()
|
|
if not M.state.initialized then return end
|
|
|
|
local ok, result = pcall(fuzzy.scan_files)
|
|
if not ok then
|
|
vim.notify('Failed to trigger file scan: ' .. tostring(result), vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
|
|
M.state.last_scan_time = os.time()
|
|
end
|
|
|
|
--- Search files with fuzzy matching using blink.cmp's advanced algorithm
|
|
--- Results are always returned in descending order (best scores first)
|
|
--- @param query string Search query
|
|
--- @param max_results number|nil Maximum number of results (optional)
|
|
--- @param max_threads number|nil Maximum number of threads (optional)
|
|
--- @param current_file string|nil Path to current file to deprioritize (optional)
|
|
--- @param min_combo_count_override number|nil Optional override for min_combo_count (nil uses config)
|
|
--- @return table List of matching files
|
|
function M.search_files(query, current_file, max_results, max_threads, min_combo_count_override)
|
|
-- Delegate to paginated version with offset=0 and limit=max_results
|
|
return M.search_files_paginated(query, current_file, max_threads, min_combo_count_override, 0, max_results)
|
|
end
|
|
|
|
--- Search files with pagination support
|
|
--- Results are always returned in descending order (best scores first)
|
|
--- @param query string Search query
|
|
--- @param current_file string|nil Path to current file to deprioritize (optional)
|
|
--- @param max_threads number|nil Maximum number of threads to use
|
|
--- @param min_combo_count_override number|nil Optional override for min_combo_count (nil uses config)
|
|
--- @param page_index number Page index (0-based: 0, 1, 2, ...)
|
|
--- @param page_size number|nil Items per page (nil uses config default)
|
|
--- @return table List of matching files
|
|
function M.search_files_paginated(query, current_file, max_threads, min_combo_count_override, page_index, page_size)
|
|
local config = require('fff.conf').get()
|
|
if not M.state.initialized then return {} end
|
|
|
|
max_threads = max_threads or config.max_threads or 4
|
|
page_index = page_index or 0
|
|
page_size = page_size or 0
|
|
|
|
local min_combo_count = min_combo_count_override
|
|
if min_combo_count == nil then min_combo_count = config.history and config.history.min_combo_count or 3 end
|
|
|
|
local combo_boost_score_multiplier = config.history and config.history.combo_boost_score_multiplier or 100
|
|
|
|
-- Convert page_index to offset (Rust expects offset in items, not page number)
|
|
local offset = page_index * page_size
|
|
|
|
local ok, search_result = pcall(
|
|
fuzzy.fuzzy_search_files,
|
|
query,
|
|
max_threads,
|
|
current_file,
|
|
combo_boost_score_multiplier,
|
|
min_combo_count,
|
|
offset,
|
|
page_size
|
|
)
|
|
|
|
if not ok then
|
|
vim.notify('Failed to search files: ' .. tostring(search_result), vim.log.levels.ERROR)
|
|
return {}
|
|
end
|
|
|
|
M.state.last_search_result = search_result
|
|
return search_result.items
|
|
end
|
|
|
|
--- Get the last search result metadata
|
|
--- @return table Search metadata with total_matched and total_files
|
|
function M.get_search_metadata()
|
|
if not M.state.last_search_result then return { total_matched = 0, total_files = 0 } end
|
|
return {
|
|
total_matched = M.state.last_search_result.total_matched,
|
|
total_files = M.state.last_search_result.total_files,
|
|
}
|
|
end
|
|
|
|
--- Get location data from the last search result
|
|
--- @return table|nil Location data if available
|
|
function M.get_search_location()
|
|
if not M.state.last_search_result then return nil end
|
|
return M.state.last_search_result.location
|
|
end
|
|
|
|
--- Get score information for a file by index (1-based)
|
|
--- @param index number The index of the file in the last search results
|
|
--- @return table|nil Score information or nil if not available
|
|
function M.get_file_score(index)
|
|
if not M.state.last_search_result or not M.state.last_search_result.scores then return nil end
|
|
|
|
return M.state.last_search_result.scores[index]
|
|
end
|
|
|
|
--- Record file access for frecency tracking
|
|
--- @param file_path string Path to the file that was accessed
|
|
function M.track_access(file_path)
|
|
if not M.state.initialized then return end
|
|
|
|
local ok, result = pcall(fuzzy.track_access, file_path)
|
|
if not ok then vim.notify('Failed to record file access: ' .. tostring(result), vim.log.levels.WARN) end
|
|
end
|
|
|
|
--- Get file content for preview
|
|
--- @param file_path string Path to the file
|
|
--- @return string|nil File content or nil if failed
|
|
function M.get_file_preview(file_path)
|
|
local preview = require('fff.file_picker.preview')
|
|
|
|
-- Create a temporary buffer to get the preview
|
|
local temp_buf = vim.api.nvim_create_buf(false, true)
|
|
local success = preview.preview(file_path, temp_buf)
|
|
|
|
if not success then
|
|
vim.api.nvim_buf_delete(temp_buf, { force = true })
|
|
return nil
|
|
end
|
|
|
|
local lines = vim.api.nvim_buf_get_lines(temp_buf, 0, -1, false)
|
|
vim.api.nvim_buf_delete(temp_buf, { force = true })
|
|
|
|
return table.concat(lines, '\n')
|
|
end
|
|
|
|
--- Check if file picker is initialized
|
|
--- @return boolean
|
|
function M.is_initialized() return M.state.initialized end
|
|
|
|
--- Get scan progress information
|
|
--- @return table Progress information with scanned_files_count, is_scanning
|
|
function M.get_scan_progress()
|
|
if not M.state.initialized then return { total_files = 0, scanned_files_count = 0, is_scanning = false } end
|
|
|
|
local ok, result = pcall(fuzzy.get_scan_progress)
|
|
if not ok then
|
|
vim.notify('Failed to get scan progress: ' .. tostring(result), vim.log.levels.WARN)
|
|
return { scanned_files_count = 0, is_scanning = false }
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
--- Refresh git status on cached files (call after git status loading completes)
|
|
--- @return table List of files with updated git status
|
|
function M.refresh_git_status()
|
|
if not M.state.initialized then return {} end
|
|
|
|
local ok, result = pcall(fuzzy.refresh_git_status)
|
|
if not ok then
|
|
vim.notify('Failed to refresh git status: ' .. tostring(result), vim.log.levels.WARN)
|
|
return {}
|
|
end
|
|
|
|
-- Update our cache
|
|
return result
|
|
end
|
|
|
|
--- Stop background git status monitoring
|
|
--- @return boolean Success status
|
|
function M.stop_background_monitor()
|
|
if not M.state.initialized then return false end
|
|
|
|
local ok, result = pcall(fuzzy.stop_background_monitor)
|
|
if not ok then
|
|
vim.notify('Failed to stop background monitor: ' .. tostring(result), vim.log.levels.WARN)
|
|
return false
|
|
end
|
|
return result
|
|
end
|
|
|
|
--- Wait for initial scan to complete
|
|
--- @param timeout_ms number Optional timeout in milliseconds (default 5000)
|
|
--- @return boolean True if scan completed, false if timed out
|
|
function M.wait_for_initial_scan(timeout_ms)
|
|
if not M.state.initialized then return false end
|
|
|
|
local ok, result = pcall(fuzzy.wait_for_initial_scan, timeout_ms)
|
|
if not ok then
|
|
vim.notify('Failed to wait for initial scan: ' .. tostring(result), vim.log.levels.WARN)
|
|
return false
|
|
end
|
|
return result
|
|
end
|
|
|
|
--- Get current state
|
|
--- @return table
|
|
function M.get_state() return M.state end
|
|
|
|
return M
|