Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:27:16 +08:00

305 lines
12 KiB
Lua

local M = {}
-- Git sign border characters per status
M.git_border_chars = {
untracked = '┆', -- Dotted vertical line
ignored = '┆', -- Dotted vertical line
unknown = '┆',
modified = '┃', -- Vertical line
deleted = '▁', -- Bottom horizontal line
renamed = '┃', -- Vertical line
staged_new = '┃', -- Vertical line
staged_modified = '┃', -- Vertical line
staged_deleted = '▁', -- Bottom horizontal line
clean = '',
clear = '',
}
local git_text_highlights_cache = nil
local git_border_highlights_cache = nil
local git_border_highlights_selected_cache = nil
-- Cache for cursor-blended git border highlights, keyed by git_status.
-- Cleared on setup() since it depends on user's Cursor/Visual colours.
local git_cursor_border_cache = {}
local function ensure_git_cache()
if git_text_highlights_cache then return end
local config = require('fff.conf').get()
git_text_highlights_cache = {
untracked = config.hl.git_untracked,
modified = config.hl.git_modified,
deleted = config.hl.git_deleted,
renamed = config.hl.git_renamed,
staged_new = config.hl.git_staged,
staged_modified = config.hl.git_staged,
staged_deleted = config.hl.git_staged,
ignored = config.hl.git_ignored,
clean = '',
clear = '',
unknown = config.hl.git_untracked,
}
git_border_highlights_cache = {
untracked = config.hl.git_sign_untracked,
modified = config.hl.git_sign_modified,
deleted = config.hl.git_sign_deleted,
renamed = config.hl.git_sign_renamed,
staged_new = config.hl.git_sign_staged,
staged_modified = config.hl.git_sign_staged,
staged_deleted = config.hl.git_sign_staged,
ignored = config.hl.git_sign_ignored,
clean = '',
clear = '',
unknown = config.hl.git_sign_untracked,
}
git_border_highlights_selected_cache = {
untracked = config.hl.git_sign_untracked_selected,
modified = config.hl.git_sign_modified_selected,
deleted = config.hl.git_sign_deleted_selected,
renamed = config.hl.git_sign_renamed_selected,
staged_new = config.hl.git_sign_staged_selected,
staged_modified = config.hl.git_sign_staged_selected,
staged_deleted = config.hl.git_sign_staged_selected,
ignored = config.hl.git_sign_ignored_selected,
clean = '',
clear = '',
unknown = config.hl.git_sign_untracked_selected,
}
end
--- Get git sign border character for a status
--- @param git_status string Git status
--- @return string Border character
function M.get_git_border_char(git_status) return M.git_border_chars[git_status] or '' end
--- Get highlight group for git status text (filename)
--- @param git_status string Git status
--- @return string Highlight group name
function M.get_git_text_highlight(git_status)
ensure_git_cache()
return git_text_highlights_cache and git_text_highlights_cache[git_status] or ''
end
--- Get sign-column border highlight group for git status
--- @param git_status string Git status
--- @return string Highlight group name
function M.get_git_border_highlight(git_status)
ensure_git_cache()
return git_border_highlights_cache and git_border_highlights_cache[git_status] or ''
end
--- Get sign-column border highlight group for git status when row is selected
--- @param git_status string Git status
--- @return string Highlight group name
function M.get_git_border_highlight_selected(git_status)
ensure_git_cache()
return git_border_highlights_selected_cache and git_border_highlights_selected_cache[git_status] or ''
end
--- Whether a git status warrants a sign-column border at all
--- @param git_status string Git status
--- @return boolean
function M.should_show_git_border(git_status)
return git_status == 'untracked'
or git_status == 'modified'
or git_status == 'staged_new'
or git_status == 'staged_modified'
or git_status == 'deleted'
or git_status == 'staged_deleted'
or git_status == 'renamed'
end
--- Resolve the appropriate git sign highlight for a row.
--- When the row is under the cursor, blends the git border foreground onto
--- the cursor highlight's background so the sign reads as part of the cursor line.
--- @param git_status string Git status
--- @param is_cursor boolean Whether the row is the current cursor row
--- @param cursor_hl string Cursor highlight group name
--- @return string Highlight group name (may be a generated group)
function M.get_git_sign_highlight(git_status, is_cursor, cursor_hl)
if not is_cursor then return M.get_git_border_highlight(git_status) end
local base_hl = M.get_git_border_highlight_selected(git_status)
if not base_hl or base_hl == '' then return cursor_hl end
local cached = git_cursor_border_cache[git_status]
if cached then return cached end
local base_id = vim.fn.synIDtrans(vim.fn.hlID(base_hl))
local cursor_id = vim.fn.synIDtrans(vim.fn.hlID(cursor_hl))
local border_fg_gui = vim.fn.synIDattr(base_id, 'fg', 'gui')
local border_fg_cterm = vim.fn.synIDattr(base_id, 'fg', 'cterm')
local cursor_bg_gui = vim.fn.synIDattr(cursor_id, 'bg', 'gui')
local cursor_bg_cterm = vim.fn.synIDattr(cursor_id, 'bg', 'cterm')
local has_gui = border_fg_gui ~= '' and cursor_bg_gui ~= ''
local has_cterm = border_fg_cterm ~= '' and cursor_bg_cterm ~= ''
if not has_gui and not has_cterm then
git_cursor_border_cache[git_status] = base_hl
return base_hl
end
local blended_name = 'FFFGitBorderSelected_' .. git_status
local hl_opts = {}
if has_gui then
hl_opts.fg = border_fg_gui
hl_opts.bg = cursor_bg_gui
end
if has_cterm then
hl_opts.ctermfg = tonumber(border_fg_cterm)
hl_opts.ctermbg = tonumber(cursor_bg_cterm)
end
vim.api.nvim_set_hl(0, blended_name, hl_opts)
git_cursor_border_cache[git_status] = blended_name
return blended_name
end
function M.setup()
-- Reset caches so highlights pick up updated config / colorscheme values.
git_text_highlights_cache = nil
git_border_highlights_cache = nil
git_border_highlights_selected_cache = nil
git_cursor_border_cache = {}
vim.cmd([[
" Symbol highlights
highlight default FFFGitStaged guifg=#10B981 ctermfg=2
highlight default FFFGitModified guifg=#F59E0B ctermfg=3
highlight default FFFGitDeleted guifg=#EF4444 ctermfg=1
highlight default FFFGitRenamed guifg=#8B5CF6 ctermfg=5
highlight default FFFGitUntracked guifg=#10B981 ctermfg=2
highlight default FFFGitIgnored guifg=#4B5563 ctermfg=8
" Thin border highlights
highlight default FFFGitSignStaged guifg=#10B981 ctermfg=2
highlight default FFFGitSignModified guifg=#F59E0B ctermfg=3
highlight default FFFGitSignDeleted guifg=#EF4444 ctermfg=1
highlight default FFFGitSignRenamed guifg=#8B5CF6 ctermfg=5
highlight default FFFGitSignUntracked guifg=#10B981 ctermfg=2
highlight default FFFGitSignIgnored guifg=#4B5563 ctermfg=8
" Fallback to GitSigns highlights if they exist
highlight default link FFFGitSignStaged GitSignsAdd
highlight default link FFFGitSignModified GitSignsChange
highlight default link FFFGitSignDeleted GitSignsDelete
highlight default link FFFGitSignUntracked GitSignsAdd
" File info panel highlights — defaults link to good fallbacks; users can override.
" FFFFileInfoValue is set explicitly below (Normal's fg only) so it doesn't
" import Normal's bg into the float (float content uses NormalFloat).
highlight default link FFFFileInfoSection Title
highlight default link FFFFileInfoSeparator FloatBorder
highlight default link FFFFileInfoLabel Comment
highlight default link FFFFileInfoValueDim NonText
highlight default link FFFFileInfoSize Number
highlight default link FFFFileInfoType Type
highlight default link FFFFileInfoPath Directory
highlight default link FFFFileInfoScorePos DiagnosticOk
highlight default link FFFFileInfoScoreNeg DiagnosticError
" Bold for the total score and the match-type tag.
highlight default FFFFileInfoTotalScore gui=bold cterm=bold
highlight default FFFFileInfoMatchType gui=bold cterm=bold
]])
-- Resolve an attribute by walking the link chain.
local function resolve_hl_attr(name, attr)
local hl = vim.api.nvim_get_hl(0, { name = name, link = false })
if hl[attr] ~= nil then return hl[attr] end
local linked = vim.api.nvim_get_hl(0, { name = name })
if linked.link then return resolve_hl_attr(linked.link, attr) end
return nil
end
-- File info value text: copy Normal's fg only. Linking to Normal would
-- carry Normal.bg into the float (whose content uses NormalFloat), creating
-- visible patches behind plain values.
local normal_fg = resolve_hl_attr('Normal', 'fg')
if normal_fg then
vim.api.nvim_set_hl(0, 'FFFFileInfoValue', { fg = normal_fg, default = true })
else
vim.api.nvim_set_hl(0, 'FFFFileInfoValue', { link = 'NormalFloat', default = true })
end
-- Resolve link target's fg so we can combine bold + colour (link + gui=bold
-- can't be set in one nvim_set_hl call).
local function bold_with_fallback(name, fallback_groups)
for _, fb in ipairs(fallback_groups) do
local hl = vim.api.nvim_get_hl(0, { name = fb, link = false })
if hl and hl.fg then
vim.api.nvim_set_hl(0, name, { fg = hl.fg, bold = true, default = true })
return
end
end
end
bold_with_fallback('FFFFileInfoTotalScore', { 'Number', 'Constant', 'Identifier' })
bold_with_fallback('FFFFileInfoMatchType', { 'Special', 'Statement', 'Keyword' })
-- Highlights for git signs both for selected and normal states
local git_highlights = {
{ 'FFFGitSignStaged', 'FFFGitSignStagedSelected', '#10B981', 2 },
{ 'FFFGitSignModified', 'FFFGitSignModifiedSelected', '#F59E0B', 3 },
{ 'FFFGitSignDeleted', 'FFFGitSignDeletedSelected', '#EF4444', 1 },
{ 'FFFGitSignRenamed', 'FFFGitSignRenamedSelected', '#8B5CF6', 5 },
{ 'FFFGitSignUntracked', 'FFFGitSignUntrackedSelected', '#10B981', 2 },
{ 'FFFGitSignIgnored', 'FFFGitSignIgnoredSelected', '#4B5563', 8 },
}
local visual_bg_gui = vim.fn.synIDattr(vim.fn.synIDtrans(vim.fn.hlID('Visual')), 'bg', 'gui')
local visual_bg_cterm = vim.fn.synIDattr(vim.fn.synIDtrans(vim.fn.hlID('Visual')), 'bg', 'cterm')
for _, hl in ipairs(git_highlights) do
local _, selected_hl, gui_fg, cterm_fg = hl[1], hl[2], hl[3], hl[4]
local gui_bg = visual_bg_gui ~= '' and visual_bg_gui or 'NONE'
local cterm_bg = visual_bg_cterm ~= '' and visual_bg_cterm or 'NONE'
vim.cmd(
string.format(
'highlight default %s guifg=%s guibg=%s ctermfg=%d ctermbg=%s',
selected_hl,
gui_fg,
gui_bg,
cterm_fg,
cterm_bg
)
)
end
-- Selection highlight - use Directory/Number colors (better than green 'Added')
vim.cmd('highlight default link FFFSelected Directory')
local dir_fg_gui = vim.fn.synIDattr(vim.fn.synIDtrans(vim.fn.hlID('Directory')), 'fg', 'gui')
local dir_fg_cterm = vim.fn.synIDattr(vim.fn.synIDtrans(vim.fn.hlID('Directory')), 'fg', 'cterm')
if dir_fg_gui == '' or dir_fg_gui == '-1' then
-- Directory not defined, try Number
dir_fg_gui = vim.fn.synIDattr(vim.fn.synIDtrans(vim.fn.hlID('Number')), 'fg', 'gui')
dir_fg_cterm = vim.fn.synIDattr(vim.fn.synIDtrans(vim.fn.hlID('Number')), 'fg', 'cterm')
end
-- Fallback to blue if neither Directory nor Number have colors
local is_dark_bg = vim.o.background == 'dark'
local gui_fg = dir_fg_gui ~= '' and dir_fg_gui or (is_dark_bg and '#60A5FA' or '#0369A1')
local cterm_fg = dir_fg_cterm ~= '' and dir_fg_cterm or (is_dark_bg and '12' or '4')
local gui_bg = visual_bg_gui ~= '' and visual_bg_gui or 'NONE'
local cterm_bg = visual_bg_cterm ~= '' and visual_bg_cterm or 'NONE'
-- Create combined highlight: Directory/Number foreground + Visual background
vim.cmd(
string.format(
'highlight default FFFSelectedActive guifg=%s guibg=%s ctermfg=%s ctermbg=%s',
gui_fg,
gui_bg,
cterm_fg,
cterm_bg
)
)
end
return M