From e1550559e3484f312e336335f8df914a2057e372 Mon Sep 17 00:00:00 2001 From: Harivansh Rathi Date: Fri, 20 Mar 2026 16:39:49 -0400 Subject: [PATCH] fzf --- config/nvim/lua/config/fzf_reload.lua | 24 +++++ config/nvim/lua/plugins/fzf.lua | 132 +++++++++++++------------- config/nvim/lua/theme.lua | 2 + home/default.nix | 1 + home/fzf.nix | 10 ++ home/scripts.nix | 5 +- lib/theme.nix | 16 +++- scripts/default.nix | 4 + scripts/theme.sh | 6 +- 9 files changed, 132 insertions(+), 68 deletions(-) create mode 100644 config/nvim/lua/config/fzf_reload.lua create mode 100644 home/fzf.nix diff --git a/config/nvim/lua/config/fzf_reload.lua b/config/nvim/lua/config/fzf_reload.lua new file mode 100644 index 0000000..0023717 --- /dev/null +++ b/config/nvim/lua/config/fzf_reload.lua @@ -0,0 +1,24 @@ +local M = {} +M.opts = nil + +function M.setup(opts) M.opts = vim.deepcopy(opts or {}) end + +function M.reload() + local path = vim.env.FZF_DEFAULT_OPTS_FILE or vim.fn.expand "~/.config/fzf/themes/theme" + if vim.fn.filereadable(path) == 0 then return end + + local lines = vim.fn.readfile(path) + if not lines or #lines == 0 or not M.opts then return end + + local colors = {} + for color_spec in table.concat(lines, "\n"):gmatch "%-%-color=([^%s]+)" do + for key, value in color_spec:gmatch "([^:,]+):([^,]+)" do + colors[key] = value + end + end + + M.opts.fzf_colors = colors + require("fzf-lua").setup(M.opts) +end + +return M diff --git a/config/nvim/lua/plugins/fzf.lua b/config/nvim/lua/plugins/fzf.lua index d95a03f..c4a6635 100644 --- a/config/nvim/lua/plugins/fzf.lua +++ b/config/nvim/lua/plugins/fzf.lua @@ -1,74 +1,76 @@ ---@param kind 'issue'|'pr' ---@param state 'all'|'open'|'closed' local function gh_picker(kind, state) - if vim.fn.executable('gh') ~= 1 then - vim.notify('gh CLI not found', vim.log.levels.WARN) - return - end - local next_state = ({ all = 'open', open = 'closed', closed = 'all' })[state] - local label = kind == 'pr' and 'PRs' or 'Issues' - require('fzf-lua').fzf_exec(('gh %s list --limit 100 --state %s'):format(kind, state), { - prompt = ('%s (%s)> '):format(label, state), - header = ':: to toggle all/open/closed', - actions = { - ['default'] = function(selected) - local num = selected[1]:match('^#?(%d+)') - if num then - vim.system({ 'gh', kind, 'view', num, '--web' }) - end - end, - ['ctrl-o'] = function() - gh_picker(kind, next_state) - end, - }, - }) + if vim.fn.executable "gh" ~= 1 then + vim.notify("gh CLI not found", vim.log.levels.WARN) + return + end + local next_state = ({ all = "open", open = "closed", closed = "all" })[state] + local label = kind == "pr" and "PRs" or "Issues" + require("fzf-lua").fzf_exec(("gh %s list --limit 100 --state %s"):format(kind, state), { + prompt = ("%s (%s)> "):format(label, state), + header = ":: to toggle all/open/closed", + actions = { + ["default"] = function(selected) + local num = selected[1]:match "^#?(%d+)" + if num then vim.system { "gh", kind, "view", num, "--web" } end + end, + ["ctrl-o"] = function() gh_picker(kind, next_state) end, + }, + }) end return { - 'ibhagwan/fzf-lua', - dependencies = { 'nvim-tree/nvim-web-devicons' }, - config = function(_, opts) - require('fzf-lua').setup(opts) + "ibhagwan/fzf-lua", + dependencies = { "nvim-tree/nvim-web-devicons" }, + config = function(_, opts) + local fzf = require "fzf-lua" + fzf.setup(opts) - map('n', '', function() - local fzf = require('fzf-lua') - local git_dir = vim.fn.system('git rev-parse --git-dir 2>/dev/null'):gsub('\n', '') - if vim.v.shell_error == 0 and git_dir ~= '' then - fzf.git_files() - else - fzf.files() - end - end) - map('n', 'ff', 'FzfLua files') - map('n', 'fg', 'FzfLua live_grep') - map('n', 'fb', 'FzfLua buffers') - map('n', 'fh', 'FzfLua help_tags') - map('n', 'fr', 'FzfLua resume') - map('n', 'fo', 'FzfLua oldfiles') - map('n', 'fc', 'FzfLua commands') - map('n', 'fk', 'FzfLua keymaps') - map('n', 'f/', 'FzfLua search_history') - map('n', 'f:', 'FzfLua command_history') - map('n', 'fe', 'FzfLua files cwd=~/.config') - map('n', 'gq', 'FzfLua quickfix') - map('n', 'gl', 'FzfLua loclist') - map('n', 'GB', 'FzfLua git_branches') - map('n', 'Gc', 'FzfLua git_commits') - map('n', 'Gs', 'FzfLua git_status') - map('n', 'Gp', function() gh_picker('pr', 'open') end) - map('n', 'Gi', function() gh_picker('issue', 'open') end) - end, - opts = { - 'default-title', - winopts = { - border = 'single', - preview = { - layout = 'vertical', - vertical = 'down:50%', - }, - }, - fzf_opts = { - ['--layout'] = 'reverse', - }, + local ok, fzf_reload = pcall(require, "config.fzf_reload") + if ok then + fzf_reload.setup(opts) + fzf_reload.reload() + end + + map("n", "", function() + local git_dir = vim.fn.system("git rev-parse --git-dir 2>/dev/null"):gsub("\n", "") + if vim.v.shell_error == 0 and git_dir ~= "" then + fzf.git_files() + else + fzf.files() + end + end) + map("n", "ff", "FzfLua files") + map("n", "fg", "FzfLua live_grep") + map("n", "fb", "FzfLua buffers") + map("n", "fh", "FzfLua help_tags") + map("n", "fr", "FzfLua resume") + map("n", "fo", "FzfLua oldfiles") + map("n", "fc", "FzfLua commands") + map("n", "fk", "FzfLua keymaps") + map("n", "f/", "FzfLua search_history") + map("n", "f:", "FzfLua command_history") + map("n", "fe", "FzfLua files cwd=~/.config") + map("n", "gq", "FzfLua quickfix") + map("n", "gl", "FzfLua loclist") + map("n", "GB", "FzfLua git_branches") + map("n", "Gc", "FzfLua git_commits") + map("n", "Gs", "FzfLua git_status") + map("n", "Gp", function() gh_picker("pr", "open") end) + map("n", "Gi", function() gh_picker("issue", "open") end) + end, + opts = { + "default-title", + winopts = { + border = "single", + preview = { + layout = "vertical", + vertical = "down:50%", + }, }, + fzf_opts = { + ["--layout"] = "reverse", + }, + }, } diff --git a/config/nvim/lua/theme.lua b/config/nvim/lua/theme.lua index 46903e8..3799192 100644 --- a/config/nvim/lua/theme.lua +++ b/config/nvim/lua/theme.lua @@ -71,6 +71,8 @@ function M.apply(mode) vim.g.cozybox_theme_mode = next_mode apply_cozybox_overrides() + local ok_reload, fzf_reload = pcall(require, "config.fzf_reload") + if ok_reload then pcall(fzf_reload.reload) end vim.schedule(function() local ok, lualine = pcall(require, "lualine") if ok then pcall(lualine.refresh, { place = { "statusline" } }) end diff --git a/home/default.nix b/home/default.nix index 542f450..84eed47 100644 --- a/home/default.nix +++ b/home/default.nix @@ -3,6 +3,7 @@ ./bat.nix ./claude.nix ./codex.nix + ./fzf.nix ./gcloud.nix ./gh.nix ./ghostty.nix diff --git a/home/fzf.nix b/home/fzf.nix new file mode 100644 index 0000000..c0f5ee4 --- /dev/null +++ b/home/fzf.nix @@ -0,0 +1,10 @@ +{config, ...}: let + theme = import ../lib/theme.nix {inherit config;}; +in { + home.sessionVariables = { + FZF_DEFAULT_OPTS_FILE = theme.paths.fzfCurrentFile; + }; + + xdg.configFile."fzf/themes/cozybox-dark".text = theme.renderFzf "dark"; + xdg.configFile."fzf/themes/cozybox-light".text = theme.renderFzf "light"; +} diff --git a/home/scripts.nix b/home/scripts.nix index 2e7a5c2..bccc35b 100644 --- a/home/scripts.nix +++ b/home/scripts.nix @@ -9,7 +9,7 @@ in { home.packages = builtins.attrValues customScripts.packages; home.activation.initializeThemeState = lib.hm.dag.entryAfter ["writeBoundary"] '' - mkdir -p "${customScripts.theme.paths.stateDir}" "${customScripts.theme.paths.ghosttyDir}" "${customScripts.theme.paths.tmuxDir}" + mkdir -p "${customScripts.theme.paths.stateDir}" "${customScripts.theme.paths.fzfDir}" "${customScripts.theme.paths.ghosttyDir}" "${customScripts.theme.paths.tmuxDir}" if [[ -f "${customScripts.theme.paths.stateFile}" ]]; then mode=$(tr -d '[:space:]' < "${customScripts.theme.paths.stateFile}") @@ -20,16 +20,19 @@ in { case "$mode" in light) + fzf_target="${customScripts.theme.paths.fzfDir}/cozybox-light" ghostty_target="${customScripts.theme.paths.ghosttyDir}/cozybox-light" tmux_target="${customScripts.tmuxConfigs.light}" ;; *) printf '%s\n' "${customScripts.theme.defaultMode}" > "${customScripts.theme.paths.stateFile}" + fzf_target="${customScripts.theme.paths.fzfDir}/cozybox-dark" ghostty_target="${customScripts.theme.paths.ghosttyDir}/cozybox-dark" tmux_target="${customScripts.tmuxConfigs.dark}" ;; esac + ln -sfn "$fzf_target" "${customScripts.theme.paths.fzfCurrentFile}" ln -sfn "$ghostty_target" "${customScripts.theme.paths.ghosttyCurrentFile}" ln -sfn "$tmux_target" "${customScripts.theme.paths.tmuxCurrentFile}" ''; diff --git a/lib/theme.nix b/lib/theme.nix index a6dee5a..d87c919 100644 --- a/lib/theme.nix +++ b/lib/theme.nix @@ -3,6 +3,8 @@ paths = { stateDir = "${config.xdg.stateHome}/theme"; stateFile = "${config.xdg.stateHome}/theme/current"; + fzfDir = "${config.xdg.configHome}/fzf/themes"; + fzfCurrentFile = "${config.xdg.configHome}/fzf/themes/theme"; ghosttyDir = "${config.xdg.configHome}/ghostty/themes"; ghosttyCurrentFile = "${config.xdg.configHome}/ghostty/themes/cozybox-current"; tmuxDir = "${config.xdg.configHome}/tmux/theme"; @@ -20,6 +22,8 @@ foreground = "#ebdbb2"; text = "#d4be98"; mutedText = "#7c6f64"; + blue = "#5b84de"; + green = "#8ec97c"; purple = "#d3869b"; border = "#181818"; palette = [ @@ -52,6 +56,8 @@ foreground = "#3c3836"; text = "#3c3836"; mutedText = "#665c54"; + blue = "#076678"; + green = "#427b58"; purple = "#d3869b"; border = "#e7e7e7"; palette = [ @@ -104,6 +110,14 @@ set-option -g pane-border-style fg=${theme.border} set-option -g pane-active-border-style fg=${theme.border} ''; + + renderFzf = mode: let + theme = themes.${mode}; + in '' + --color=fg:${theme.text},bg:${theme.background},hl:${theme.blue} + --color=fg+:${theme.text},bg+:${theme.surface},hl+:${theme.blue} + --color=info:${theme.green},prompt:${theme.blue},pointer:${theme.text},marker:${theme.green},spinner:${theme.text} + ''; in { - inherit defaultMode paths renderGhostty renderTmux themes; + inherit defaultMode paths renderFzf renderGhostty renderTmux themes; } diff --git a/scripts/default.nix b/scripts/default.nix index 9a73fa5..7735e03 100644 --- a/scripts/default.nix +++ b/scripts/default.nix @@ -69,6 +69,10 @@ "@DEFAULT_MODE@" = theme.defaultMode; "@STATE_DIR@" = theme.paths.stateDir; "@STATE_FILE@" = theme.paths.stateFile; + "@FZF_DIR@" = theme.paths.fzfDir; + "@FZF_CURRENT_FILE@" = theme.paths.fzfCurrentFile; + "@FZF_DARK_FILE@" = "${theme.paths.fzfDir}/cozybox-dark"; + "@FZF_LIGHT_FILE@" = "${theme.paths.fzfDir}/cozybox-light"; "@GHOSTTY_DIR@" = theme.paths.ghosttyDir; "@GHOSTTY_CURRENT_FILE@" = theme.paths.ghosttyCurrentFile; "@GHOSTTY_DARK_FILE@" = "${theme.paths.ghosttyDir}/cozybox-dark"; diff --git a/scripts/theme.sh b/scripts/theme.sh index 972c2f6..2a1c4fa 100644 --- a/scripts/theme.sh +++ b/scripts/theme.sh @@ -16,17 +16,20 @@ read_mode() { link_mode_assets() { local mode="$1" + local fzf_target local ghostty_target local tmux_target local apple_dark_mode case "$mode" in dark) + fzf_target="@FZF_DARK_FILE@" ghostty_target="@GHOSTTY_DARK_FILE@" tmux_target="@TMUX_DARK_FILE@" apple_dark_mode=true ;; light) + fzf_target="@FZF_LIGHT_FILE@" ghostty_target="@GHOSTTY_LIGHT_FILE@" tmux_target="@TMUX_LIGHT_FILE@" apple_dark_mode=false @@ -37,8 +40,9 @@ link_mode_assets() { ;; esac - mkdir -p "@STATE_DIR@" "@GHOSTTY_DIR@" "@TMUX_DIR@" + mkdir -p "@STATE_DIR@" "@FZF_DIR@" "@GHOSTTY_DIR@" "@TMUX_DIR@" printf '%s\n' "$mode" > "@STATE_FILE@" + ln -sfn "$fzf_target" "@FZF_CURRENT_FILE@" ln -sfn "$ghostty_target" "@GHOSTTY_CURRENT_FILE@" ln -sfn "$tmux_target" "@TMUX_CURRENT_FILE@"