Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cac2ec7130 | |||
| 5a898066d2 | |||
| 7cdc71d5a1 | |||
| 54f96dade0 | |||
| 56ea1404f1 | |||
| 43100e3150 | |||
| ac7cd106b3 | |||
| d27a9bc8fd | |||
| 8905981871 | |||
| 381992457b | |||
| a9b2f5bba2 | |||
| 61c3cc7420 | |||
| 829e05b353 | |||
| a9cac80ea9 | |||
| 7b1fab33be |
@@ -1,9 +1,8 @@
|
||||
name: Release
|
||||
name: Prebuild
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
branches: [main, feat/prebuild]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -18,44 +17,47 @@ jobs:
|
||||
# Glibc 2.21
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
artifact_name: target/x86_64-unknown-linux-gnu/release/libfff_fuzzy.so
|
||||
artifact_name: target/x86_64-unknown-linux-gnu/release/libfff_nvim.so
|
||||
- os: ubuntu-latest
|
||||
target: aarch64-unknown-linux-gnu
|
||||
artifact_name: target/aarch64-unknown-linux-gnu/release/libfff_fuzzy.so
|
||||
artifact_name: target/aarch64-unknown-linux-gnu/release/libfff_nvim.so
|
||||
# Musl 1.2.3
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-musl
|
||||
artifact_name: target/x86_64-unknown-linux-musl/release/libfff_fuzzy.so
|
||||
artifact_name: target/x86_64-unknown-linux-musl/release/libfff_nvim.so
|
||||
- os: ubuntu-latest
|
||||
target: aarch64-unknown-linux-musl
|
||||
artifact_name: target/aarch64-unknown-linux-musl/release/libfff_fuzzy.so
|
||||
# Android (Termux)
|
||||
- os: ubuntu-latest
|
||||
target: aarch64-linux-android
|
||||
artifact_name: target/aarch64-linux-android/release/libfff_fuzzy.so
|
||||
artifact_name: target/aarch64-unknown-linux-musl/release/libfff_nvim.so
|
||||
# # Android (Termux)
|
||||
# - os: ubuntu-latest
|
||||
# target: aarch64-linux-android
|
||||
# artifact_name: target/aarch64-linux-android/release/libfff_nvim.so
|
||||
|
||||
## macOS builds
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
artifact_name: target/x86_64-apple-darwin/release/libfff_fuzzy.dylib
|
||||
artifact_name: target/x86_64-apple-darwin/release/libfff_nvim.dylib
|
||||
- os: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
artifact_name: target/aarch64-apple-darwin/release/libfff_fuzzy.dylib
|
||||
artifact_name: target/aarch64-apple-darwin/release/libfff_nvim.dylib
|
||||
|
||||
## Windows builds
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
artifact_name: target/x86_64-pc-windows-msvc/release/fff_fuzzy.dll
|
||||
artifact_name: target/x86_64-pc-windows-msvc/release/fff_nvim.dll
|
||||
- os: windows-latest
|
||||
target: aarch64-pc-windows-msvc
|
||||
artifact_name: target/aarch64-pc-windows-msvc/release/fff_nvim.dll
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set Rust toolchain
|
||||
if: contains(matrix.target, 'linux')
|
||||
# https://github.com/rust-cross/cargo-zigbuild/issues/327
|
||||
run: echo -e '[toolchain]\nchannel = "nightly-2025-02-19"' > rust-toolchain.toml
|
||||
# - name: Set Rust toolchain
|
||||
# if: contains(matrix.target, 'linux')
|
||||
# # https://github.com/rust-cross/cargo-zigbuild/issues/327
|
||||
# run: echo -e '[toolchain]\nchannel = "nightly-2025-02-19"' > rust-toolchain.toml
|
||||
|
||||
- name: Install Rust
|
||||
run: |
|
||||
@@ -88,7 +90,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
path: ${{ matrix.target }}.*
|
||||
path: ${{ matrix.target }}*
|
||||
|
||||
release:
|
||||
name: Release
|
||||
@@ -97,22 +99,37 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./binaries
|
||||
|
||||
- name: Generate checksums
|
||||
working-directory: ./binaries
|
||||
run: |
|
||||
ls -a
|
||||
for file in ./**/*; do
|
||||
sha256sum "$file" > "${file}.sha256"
|
||||
done
|
||||
|
||||
- name: Prepare tag
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
sha="$(git rev-parse --short HEAD)"
|
||||
echo "tag=$sha" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Release Assets
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
name: ${{ github.ref_name }}
|
||||
tag_name: ${{ github.ref_name }}
|
||||
name: "${{ steps.vars.outputs.tag }}"
|
||||
tag_name: "${{ steps.vars.outputs.tag }}"
|
||||
token: ${{ github.token }}
|
||||
files: ./**/*
|
||||
files: ./binaries/**/*
|
||||
draft: false
|
||||
prerelease: false
|
||||
generate_release_notes: true
|
||||
prerelease: true
|
||||
generate_release_notes: false
|
||||
body: |
|
||||
Nightly release from commit: ${{ github.sha }}
|
||||
|
||||
Generated
+2
-3
@@ -805,11 +805,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "neo_frizbee"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dedc43c9781dfa8c9584d3e50d68ce956b928b01ae0ad1da2014be5a322c20f8"
|
||||
checksum = "021c5b347f5d2a54823afe6bd29b5079d53e9eb50c1ee38a7dab82e6ea07c8f8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"multiversion",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ ignore = "0.4.22"
|
||||
mimalloc = "0.1.47"
|
||||
|
||||
mlua = { version = "0.11.1", features = ["module", "luajit"] }
|
||||
neo_frizbee = { version = "0.6.0" }
|
||||
neo_frizbee = { version = "0.7.0" }
|
||||
notify = "8.1.0"
|
||||
notify-debouncer-mini = "0.7"
|
||||
once_cell = "1.20.2"
|
||||
|
||||
@@ -49,8 +49,12 @@ FFF.nvim requires:
|
||||
```lua
|
||||
{
|
||||
'dmtrKovalenko/fff.nvim',
|
||||
build = 'cargo build --release',
|
||||
-- or if you are using nixos
|
||||
build = function()
|
||||
-- this will download prebuild binary or try to use existing rustup toolchain to build from source
|
||||
-- (if you are using lazy you can use gb for rebuilding a plugin if needed)
|
||||
require("fff.download").download_or_build_binary()
|
||||
end,
|
||||
-- if you are using nixos
|
||||
-- build = "nix run .#release",
|
||||
opts = { -- (optional)
|
||||
debug = {
|
||||
|
||||
+7
-3
@@ -1,4 +1,4 @@
|
||||
*fff.nvim.txt* For Neovim >= 0.8.0 Last change: 2025 August 25
|
||||
*fff.nvim.txt* For Neovim >= 0.8.0 Last change: 2025 September 16
|
||||
|
||||
==============================================================================
|
||||
Table of Contents *fff.nvim-table-of-contents*
|
||||
@@ -54,8 +54,12 @@ LAZY.NVIM
|
||||
>lua
|
||||
{
|
||||
'dmtrKovalenko/fff.nvim',
|
||||
build = 'cargo build --release',
|
||||
-- or if you are using nixos
|
||||
build = function()
|
||||
-- this will download prebuild binary or try to use existing rustup toolchain to build from source
|
||||
-- (if you are using lazy you can use gb for rebuilding a plugin if needed)
|
||||
require("fff.download").download_or_build_binary()
|
||||
end,
|
||||
-- if you are using nixos
|
||||
-- build = "nix run .#release",
|
||||
opts = { -- (optional)
|
||||
debug = {
|
||||
|
||||
Generated
+3
-3
@@ -64,11 +64,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1754016903,
|
||||
"narHash": "sha256-mRB5OOx7H5kFwW8Qtc/7dO3qHsBQtZ/eYQEj93/Noo8=",
|
||||
"lastModified": 1756866691,
|
||||
"narHash": "sha256-YWJsM0HfdFLcaoP5OeyzjX6MjGnJ0Acm+bg1QN8MKjo=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "ddd488184f01603b712ddbb6dc9fe0b8447eb7fc",
|
||||
"rev": "fb6dab6f320291a8edd31c1d67f078c6f7384a02",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
local M = {}
|
||||
local system = require('fff.utils.system')
|
||||
local uv = vim and vim.uv or require('luv')
|
||||
|
||||
local GITHUB_REPO = 'dmtrKovalenko/fff.nvim'
|
||||
|
||||
local function get_current_version(plugin_dir, callback)
|
||||
vim.system({ 'git', 'rev-parse', '--short', 'HEAD' }, { cwd = plugin_dir }, function(result)
|
||||
if result.code ~= 0 or not result.stdout or result.stdout == '' then
|
||||
callback(nil)
|
||||
return
|
||||
end
|
||||
callback(result.stdout:gsub('%s+', ''))
|
||||
end)
|
||||
end
|
||||
|
||||
local function get_binary_dir(plugin_dir) return plugin_dir .. '/../target' end
|
||||
|
||||
local function get_binary_path(plugin_dir)
|
||||
local binary_dir = get_binary_dir(plugin_dir)
|
||||
local extension = system.get_lib_extension()
|
||||
return binary_dir .. '/libfff_nvim.' .. extension
|
||||
end
|
||||
|
||||
local function binary_exists(plugin_dir)
|
||||
local binary_path = get_binary_path(plugin_dir)
|
||||
local stat = uv.fs_stat(binary_path)
|
||||
return stat and stat.type == 'file'
|
||||
end
|
||||
|
||||
local function download_file(url, output_path, opts, callback)
|
||||
opts = opts or {}
|
||||
|
||||
local dir = vim.fn.fnamemodify(output_path, ':h')
|
||||
uv.fs_mkdir(dir, 493, function(err) -- 493 = 0755 octal
|
||||
if err and not err:match('EEXIST') then
|
||||
callback(false, 'Failed to create directory: ' .. err)
|
||||
return
|
||||
end
|
||||
|
||||
local curl_args = {
|
||||
'curl',
|
||||
'--fail',
|
||||
'--location',
|
||||
'--silent',
|
||||
'--show-error',
|
||||
'--output',
|
||||
output_path,
|
||||
}
|
||||
|
||||
if opts.proxy then
|
||||
table.insert(curl_args, '--proxy')
|
||||
table.insert(curl_args, opts.proxy)
|
||||
end
|
||||
|
||||
table.insert(curl_args, url)
|
||||
vim.system(curl_args, {}, function(result)
|
||||
if result.code ~= 0 then
|
||||
callback(false, 'Failed to download: ' .. (result.stderr or 'unknown error'))
|
||||
return
|
||||
end
|
||||
callback(true, nil)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local function download_from_github(version, binary_path, opts, callback)
|
||||
opts = opts or {}
|
||||
|
||||
local triple = system.get_triple()
|
||||
local extension = system.get_lib_extension()
|
||||
local binary_name = triple .. '.' .. extension
|
||||
local url = string.format('https://github.com/%s/releases/download/%s/%s', GITHUB_REPO, version, binary_name)
|
||||
vim.schedule(function()
|
||||
vim.notify(string.format('Downloading fff.nvim binary for ' .. version), vim.log.levels.INFO)
|
||||
vim.notify(string.format('Do not open fff until you see a success notification.'), vim.log.levels.WARN)
|
||||
end)
|
||||
|
||||
download_file(url, binary_path, {
|
||||
proxy = opts.proxy,
|
||||
extra_curl_args = opts.extra_curl_args,
|
||||
}, function(success, err)
|
||||
if not success then
|
||||
callback(false, err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Verify the binary can be loaded
|
||||
local ok, err_msg = pcall(function() package.loadlib(binary_path, 'luaopen_fff_nvim') end)
|
||||
|
||||
if not ok then
|
||||
uv.fs_unlink(binary_path)
|
||||
callback(false, 'Downloaded binary is not valid: ' .. (err_msg or 'unknown error'))
|
||||
return
|
||||
end
|
||||
|
||||
vim.schedule(function() vim.notify('fff.nvim binary downloaded successfully!', vim.log.levels.INFO) end)
|
||||
callback(true, nil)
|
||||
end)
|
||||
end
|
||||
|
||||
function M.ensure_downloaded(opts, callback)
|
||||
opts = opts or {}
|
||||
local plugin_dir = vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':h:h')
|
||||
|
||||
if binary_exists(plugin_dir) and not opts.force then
|
||||
callback(true, nil)
|
||||
return
|
||||
end
|
||||
|
||||
local function on_version(target_version)
|
||||
if not target_version then
|
||||
callback(false, 'Could not determine target version')
|
||||
return
|
||||
end
|
||||
|
||||
local binary_path = get_binary_path(plugin_dir)
|
||||
download_from_github(target_version, binary_path, opts, callback)
|
||||
end
|
||||
|
||||
if opts.version then
|
||||
on_version(opts.version)
|
||||
else
|
||||
get_current_version(plugin_dir, on_version)
|
||||
end
|
||||
end
|
||||
|
||||
function M.download_binary(callback)
|
||||
M.ensure_downloaded({ force = true }, function(success, err)
|
||||
if not success then
|
||||
if callback then
|
||||
callback(false, err)
|
||||
else
|
||||
error('Failed to download fff.nvim binary: ' .. (err or 'unknown error'))
|
||||
end
|
||||
return
|
||||
end
|
||||
if callback then callback(true, nil) end
|
||||
end)
|
||||
end
|
||||
|
||||
function M.build_binary(callback)
|
||||
local plugin_dir = vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':h:h')
|
||||
local has_rustup = vim.fn.executable('rustup') == 1
|
||||
if not has_rustup then
|
||||
callback(
|
||||
false,
|
||||
'rustup is not found. It is required to build the fff.nvim binary. Install it from https://rustup.rs/'
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
vim.system({ 'cargo', 'build', '--release' }, { cwd = plugin_dir }, function(result)
|
||||
if result.code ~= 0 then
|
||||
callback(false, 'Failed to build rust binary: ' .. (result.stderr or 'unknown error'))
|
||||
return
|
||||
end
|
||||
callback(true, nil)
|
||||
end)
|
||||
end
|
||||
|
||||
function M.download_or_build_binary()
|
||||
M.ensure_downloaded({ force = true }, function(download_success, download_error)
|
||||
if download_success then return end
|
||||
|
||||
vim.schedule(
|
||||
function()
|
||||
vim.notify(
|
||||
'Error downloading binary: ' .. (download_error or 'unknown error') .. '\nTrying cargo build --release\n',
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
end
|
||||
)
|
||||
|
||||
M.build_binary(function(build_success, build_error)
|
||||
if not build_success then
|
||||
error('Failed to build fff.nvim binary. Build error: ' .. (build_error or 'unknown error'))
|
||||
else
|
||||
vim.schedule(function() vim.notify('fff.nvim binary built successfully!', vim.log.levels.INFO) end)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
function M.get_binary_path()
|
||||
local plugin_dir = vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':h:h')
|
||||
return get_binary_path(plugin_dir)
|
||||
end
|
||||
|
||||
return M
|
||||
+115
-20
@@ -432,7 +432,7 @@ end
|
||||
function M.setup_buffers()
|
||||
vim.api.nvim_buf_set_name(M.state.input_buf, 'fffile search')
|
||||
vim.api.nvim_buf_set_name(M.state.list_buf, 'fffiles list')
|
||||
vim.api.nvim_buf_set_name(M.state.preview_buf, 'fffile preview')
|
||||
if M.enabled_preview() then vim.api.nvim_buf_set_name(M.state.preview_buf, 'fffile preview') end
|
||||
|
||||
vim.api.nvim_buf_set_option(M.state.input_buf, 'buftype', 'prompt')
|
||||
vim.api.nvim_buf_set_option(M.state.input_buf, 'filetype', 'fff_input')
|
||||
@@ -463,9 +463,9 @@ function M.setup_buffers()
|
||||
end
|
||||
end
|
||||
|
||||
--- Setup window options
|
||||
function M.setup_windows()
|
||||
local hl = M.state.config.hl
|
||||
local win_hl = string.format('Normal:%s,FloatBorder:%s,FloatTitle:%s', hl.normal, hl.border, hl.title)
|
||||
|
||||
vim.api.nvim_win_set_option(M.state.input_win, 'wrap', false)
|
||||
vim.api.nvim_win_set_option(M.state.input_win, 'cursorline', false)
|
||||
@@ -473,6 +473,7 @@ function M.setup_windows()
|
||||
vim.api.nvim_win_set_option(M.state.input_win, 'relativenumber', false)
|
||||
vim.api.nvim_win_set_option(M.state.input_win, 'signcolumn', 'no')
|
||||
vim.api.nvim_win_set_option(M.state.input_win, 'foldcolumn', '0')
|
||||
vim.api.nvim_win_set_option(M.state.input_win, 'winhighlight', win_hl)
|
||||
|
||||
vim.api.nvim_win_set_option(M.state.list_win, 'wrap', false)
|
||||
vim.api.nvim_win_set_option(M.state.list_win, 'cursorline', false)
|
||||
@@ -480,6 +481,7 @@ function M.setup_windows()
|
||||
vim.api.nvim_win_set_option(M.state.list_win, 'relativenumber', false)
|
||||
vim.api.nvim_win_set_option(M.state.list_win, 'signcolumn', 'yes:1') -- Enable signcolumn for git status borders
|
||||
vim.api.nvim_win_set_option(M.state.list_win, 'foldcolumn', '0')
|
||||
vim.api.nvim_win_set_option(M.state.list_win, 'winhighlight', win_hl)
|
||||
|
||||
if M.enabled_preview() then
|
||||
vim.api.nvim_win_set_option(M.state.preview_win, 'wrap', false)
|
||||
@@ -488,7 +490,56 @@ function M.setup_windows()
|
||||
vim.api.nvim_win_set_option(M.state.preview_win, 'relativenumber', false)
|
||||
vim.api.nvim_win_set_option(M.state.preview_win, 'signcolumn', 'no')
|
||||
vim.api.nvim_win_set_option(M.state.preview_win, 'foldcolumn', '0')
|
||||
vim.api.nvim_win_set_option(M.state.preview_win, 'winhighlight', win_hl)
|
||||
end
|
||||
|
||||
local picker_group = vim.api.nvim_create_augroup('fff_picker_focus', { clear = true })
|
||||
local picker_windows = nil
|
||||
|
||||
if M.enabled_preview() then
|
||||
picker_windows = { M.state.input_win, M.state.preview_win, M.state.list_win }
|
||||
else
|
||||
picker_windows = { M.state.input_win, M.state.list_win }
|
||||
end
|
||||
|
||||
if M.state.preview_win then table.insert(picker_windows, M.state.preview_win) end
|
||||
if M.state.file_info_win then table.insert(picker_windows, M.state.file_info_win) end
|
||||
|
||||
vim.api.nvim_create_autocmd('WinLeave', {
|
||||
group = picker_group,
|
||||
callback = function()
|
||||
if not M.state.active then return end
|
||||
|
||||
local current_win = vim.api.nvim_get_current_win()
|
||||
local is_picker_window = false
|
||||
for _, win in ipairs(picker_windows) do
|
||||
if win and vim.api.nvim_win_is_valid(win) and current_win == win then
|
||||
is_picker_window = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- if we current focused on picker window and leaving it
|
||||
if is_picker_window then
|
||||
vim.defer_fn(function()
|
||||
if not M.state.active then return end
|
||||
|
||||
local new_win = vim.api.nvim_get_current_win()
|
||||
local entering_picker_window = false
|
||||
|
||||
for _, win in ipairs(picker_windows) do
|
||||
if win and vim.api.nvim_win_is_valid(win) and new_win == win then
|
||||
entering_picker_window = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not entering_picker_window then M.close() end
|
||||
end, 10)
|
||||
end
|
||||
end,
|
||||
desc = 'Close picker when focus leaves picker windows',
|
||||
})
|
||||
end
|
||||
|
||||
local function set_keymap(mode, keys, handler, opts)
|
||||
@@ -958,6 +1009,18 @@ function M.render_list()
|
||||
priority = 1000,
|
||||
})
|
||||
end
|
||||
|
||||
local match_start, match_end = string.find(line_content, M.state.query, 1)
|
||||
if match_start and match_end then
|
||||
vim.api.nvim_buf_add_highlight(
|
||||
M.state.list_buf,
|
||||
M.state.ns_id,
|
||||
config.hl.matched or 'IncSearch',
|
||||
line_idx - 1,
|
||||
match_start - 1,
|
||||
match_end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1156,6 +1219,42 @@ function M.scroll_preview_down()
|
||||
preview.scroll(scroll_lines)
|
||||
end
|
||||
|
||||
--- Find the first visible window with a normal file buffer
|
||||
--- @return number|nil Window ID of the first suitable window, or nil if none found
|
||||
local function find_suitable_window()
|
||||
local current_tabpage = vim.api.nvim_get_current_tabpage()
|
||||
local windows = vim.api.nvim_tabpage_list_wins(current_tabpage)
|
||||
|
||||
for _, win in ipairs(windows) do
|
||||
if vim.api.nvim_win_is_valid(win) then
|
||||
local buf = vim.api.nvim_win_get_buf(win)
|
||||
if vim.api.nvim_buf_is_valid(buf) then
|
||||
local buftype = vim.api.nvim_buf_get_option(buf, 'buftype')
|
||||
local modifiable = vim.api.nvim_buf_get_option(buf, 'modifiable')
|
||||
local filetype = vim.api.nvim_buf_get_option(buf, 'filetype')
|
||||
|
||||
local is_picker_window = (
|
||||
win == M.state.input_win
|
||||
or win == M.state.list_win
|
||||
or win == M.state.preview_win
|
||||
or win == M.state.file_info_win
|
||||
)
|
||||
|
||||
if
|
||||
(buftype == '' or buftype == 'acwrite')
|
||||
and modifiable
|
||||
and not is_picker_window
|
||||
and filetype ~= 'undotree'
|
||||
then
|
||||
return win
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function M.select(action)
|
||||
if not M.state.active then return end
|
||||
|
||||
@@ -1172,6 +1271,16 @@ function M.select(action)
|
||||
M.close()
|
||||
|
||||
if action == 'edit' then
|
||||
local current_buf = vim.api.nvim_get_current_buf()
|
||||
local current_buftype = vim.api.nvim_buf_get_option(current_buf, 'buftype')
|
||||
local current_buf_modifiable = vim.api.nvim_buf_get_option(current_buf, 'modifiable')
|
||||
|
||||
-- If current active buffer is not a normal buffer we find a suitable window with a tab otherwise opening a new split
|
||||
if current_buftype ~= '' or not current_buf_modifiable then
|
||||
local suitable_win = find_suitable_window()
|
||||
if suitable_win then vim.api.nvim_set_current_win(suitable_win) end
|
||||
end
|
||||
|
||||
vim.cmd('edit ' .. vim.fn.fnameescape(relative_path))
|
||||
elseif action == 'split' then
|
||||
vim.cmd('split ' .. vim.fn.fnameescape(relative_path))
|
||||
@@ -1238,47 +1347,33 @@ function M.close()
|
||||
M.state.search_timer:close()
|
||||
M.state.search_timer = nil
|
||||
end
|
||||
|
||||
-- Clean up picker focus autocmds
|
||||
pcall(vim.api.nvim_del_augroup_by_name, 'fff_picker_focus')
|
||||
end
|
||||
|
||||
function M.open(opts)
|
||||
if M.state.active then return end
|
||||
|
||||
-- Get base path first
|
||||
local base_path = opts and opts.cwd or vim.fn.getcwd()
|
||||
|
||||
-- Capture current file before creating UI (which changes current buffer)
|
||||
local current_buf = vim.api.nvim_get_current_buf()
|
||||
if current_buf and vim.api.nvim_buf_is_valid(current_buf) then
|
||||
local current_file = vim.api.nvim_buf_get_name(current_buf)
|
||||
if current_file ~= '' and vim.fn.filereadable(current_file) == 1 then
|
||||
local absolute_path = vim.fn.fnamemodify(current_file, ':p')
|
||||
-- Convert to relative path from base_path
|
||||
local relative_path =
|
||||
vim.fn.fnamemodify(vim.fn.resolve(absolute_path), ':s?' .. vim.fn.escape(base_path, '\\') .. '/??')
|
||||
M.state.current_file_cache = relative_path
|
||||
vim.notify(
|
||||
'DEBUG: Current file captured (relative): ' .. tostring(M.state.current_file_cache),
|
||||
vim.log.levels.INFO
|
||||
)
|
||||
else
|
||||
M.state.current_file_cache = nil
|
||||
end
|
||||
else
|
||||
vim.notify('DEBUG: No valid current buffer found', vim.log.levels.INFO)
|
||||
M.state.current_file_cache = nil
|
||||
end
|
||||
|
||||
if not file_picker.is_initialized() then
|
||||
local config = {
|
||||
base_path = base_path,
|
||||
max_results = 100,
|
||||
frecency = {
|
||||
enabled = true,
|
||||
db_path = vim.fn.stdpath('cache') .. '/fff_nvim',
|
||||
},
|
||||
}
|
||||
|
||||
if not file_picker.setup(config) then
|
||||
if not file_picker.setup() then
|
||||
vim.notify('Failed to initialize file picker', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
local download = require('fff.download')
|
||||
|
||||
--- @return string
|
||||
local function get_lib_extension()
|
||||
if jit.os:lower() == 'mac' or jit.os:lower() == 'osx' then return '.dylib' end
|
||||
@@ -10,6 +12,7 @@ end
|
||||
local base_path = debug.getinfo(1).source:match('@?(.*/)')
|
||||
|
||||
local paths = {
|
||||
download.get_binary_path(),
|
||||
base_path .. '../../../target/release/lib?' .. get_lib_extension(),
|
||||
base_path .. '../../../target/release/?' .. get_lib_extension(),
|
||||
}
|
||||
@@ -24,7 +27,7 @@ package.cpath = package.cpath .. ';' .. table.concat(paths, ';')
|
||||
|
||||
local ok, backend = pcall(require, 'fff_nvim')
|
||||
if not ok then
|
||||
error('Failed to load fff rust backend. Make sure that it has been built with `cargo build --release`')
|
||||
error('Failed to load fff rust backend. Make sure that it has been downloaded or built with `cargo build --release`')
|
||||
end
|
||||
|
||||
return backend
|
||||
|
||||
+13
-11
@@ -5,6 +5,7 @@ use crate::{
|
||||
path_utils::calculate_distance_penalty,
|
||||
types::{FileItem, Score, ScoringContext},
|
||||
};
|
||||
use neo_frizbee::Scoring;
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn match_and_score_files<'a>(
|
||||
@@ -19,10 +20,15 @@ pub fn match_and_score_files<'a>(
|
||||
return (vec![], vec![], 0);
|
||||
}
|
||||
|
||||
let options = neo_frizbee::Options {
|
||||
let has_uppercase_letter = context.query.chars().any(|c| c.is_uppercase());
|
||||
let options = neo_frizbee::Config {
|
||||
prefilter: true,
|
||||
max_typos: Some(context.max_typos),
|
||||
sort: false,
|
||||
scoring: Scoring {
|
||||
capitalization_bonus: if has_uppercase_letter { 8 } else { 0 },
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
|
||||
let query_contains_path_separator = context.query.contains(MAIN_SEPARATOR);
|
||||
@@ -32,7 +38,7 @@ pub fn match_and_score_files<'a>(
|
||||
context.query,
|
||||
haystack.len()
|
||||
);
|
||||
let path_matches = neo_frizbee::match_list(context.query, &haystack, options);
|
||||
let path_matches = neo_frizbee::match_list(context.query, &haystack, &options);
|
||||
tracing::debug!(
|
||||
"Matched {} files for query '{}'",
|
||||
path_matches.len(),
|
||||
@@ -44,11 +50,7 @@ pub fn match_and_score_files<'a>(
|
||||
// instead of spawning a separate matching process, but it's okay for the beta
|
||||
let haystack_of_filenames = path_matches
|
||||
.par_iter()
|
||||
.filter_map(|m| {
|
||||
files
|
||||
.get(m.index_in_haystack as usize)
|
||||
.map(|f| f.file_name.as_str())
|
||||
})
|
||||
.filter_map(|m| files.get(m.index as usize).map(|f| f.file_name.as_str()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// if there is a / in the query we don't even match filenames
|
||||
@@ -58,11 +60,11 @@ pub fn match_and_score_files<'a>(
|
||||
let mut list = neo_frizbee::match_list_parallel(
|
||||
context.query,
|
||||
&haystack_of_filenames,
|
||||
options,
|
||||
&options,
|
||||
context.max_threads,
|
||||
);
|
||||
|
||||
list.par_sort_unstable_by_key(|m| m.index_in_haystack);
|
||||
list.par_sort_unstable_by_key(|m| m.index);
|
||||
|
||||
list
|
||||
};
|
||||
@@ -72,7 +74,7 @@ pub fn match_and_score_files<'a>(
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, path_match)| {
|
||||
let file_idx = path_match.index_in_haystack as usize;
|
||||
let file_idx = path_match.index as usize;
|
||||
let file = &files[file_idx];
|
||||
|
||||
let mut base_score = path_match.score as i32;
|
||||
@@ -83,7 +85,7 @@ pub fn match_and_score_files<'a>(
|
||||
let filename_match = filename_matches
|
||||
.get(next_filename_match_index)
|
||||
.and_then(|m| {
|
||||
if m.index_in_haystack == index as u32 {
|
||||
if m.index == index as u32 {
|
||||
next_filename_match_index += 1;
|
||||
Some(m)
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
local M = {}
|
||||
|
||||
local uv = vim and vim.uv or require('luv')
|
||||
|
||||
-- Get the system triple (target triple for the current platform)
|
||||
function M.get_triple()
|
||||
local os_name = uv.os_uname().sysname:lower()
|
||||
local arch = uv.os_uname().machine:lower()
|
||||
|
||||
-- Normalize OS name
|
||||
if os_name == 'darwin' then
|
||||
os_name = 'apple-darwin'
|
||||
elseif os_name == 'linux' then
|
||||
-- Detect if we're on musl or glibc
|
||||
local handle = io.popen('ldd --version 2>&1')
|
||||
if handle then
|
||||
local output = handle:read('*a')
|
||||
handle:close()
|
||||
if output and output:match('musl') then
|
||||
os_name = 'unknown-linux-musl'
|
||||
else
|
||||
os_name = 'unknown-linux-gnu'
|
||||
end
|
||||
else
|
||||
os_name = 'unknown-linux-gnu'
|
||||
end
|
||||
elseif os_name:match('windows') or os_name:match('mingw') or os_name:match('msys') then
|
||||
os_name = 'pc-windows-msvc'
|
||||
end
|
||||
|
||||
-- Normalize architecture
|
||||
if arch == 'x86_64' or arch == 'amd64' then
|
||||
arch = 'x86_64'
|
||||
elseif arch == 'aarch64' or arch == 'arm64' then
|
||||
arch = 'aarch64'
|
||||
elseif arch:match('^arm') then
|
||||
arch = 'arm'
|
||||
end
|
||||
|
||||
return arch .. '-' .. os_name
|
||||
end
|
||||
|
||||
-- Get the library extension for the current platform
|
||||
function M.get_lib_extension()
|
||||
local os_name = uv.os_uname().sysname:lower()
|
||||
if os_name == 'darwin' then
|
||||
return 'dylib'
|
||||
elseif os_name:match('windows') or os_name:match('mingw') or os_name:match('msys') then
|
||||
return 'dll'
|
||||
else
|
||||
return 'so'
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2025-07-27"
|
||||
channel = "nightly-2025-09-01"
|
||||
components = [
|
||||
"clippy-preview",
|
||||
"rustfmt-preview",
|
||||
|
||||
Reference in New Issue
Block a user