From 84d26c68a5f437884e835c509f59034e16c4a040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Pankowski?= Date: Tue, 16 Sep 2025 20:48:27 +0200 Subject: [PATCH] vis: add script fzf.sh and use it with just two keys fzf.sh is a wrapper around fzf with mode for fd, rg, git and jj which allow to switch between them with keys --- vis/.config/vis/fzf.sh | 215 ++++++++++++++++++++++++++++++++++++++ vis/.config/vis/visrc.lua | 99 +++++++----------- 2 files changed, 251 insertions(+), 63 deletions(-) create mode 100755 vis/.config/vis/fzf.sh diff --git a/vis/.config/vis/fzf.sh b/vis/.config/vis/fzf.sh new file mode 100755 index 0000000..f7c52d7 --- /dev/null +++ b/vis/.config/vis/fzf.sh @@ -0,0 +1,215 @@ +#!/bin/sh + +prompt=${FZF_PROMPT%> } +cmd=${prompt%+*} +hidden=${prompt#$cmd} + +ripgrep='rg --column --line-number --color=always --smart-case' + +if [ "$1" = list ]; then + case "$FZF_HEADER_LABEL" in + :?*) + path=${FZF_HEADER_LABEL#?} + ;; + *) + path= + ;; + esac + case "$prompt" in + files) + exec fd --type f '' $path + ;; + files+hidden) + exec fd --type f -H '' $path + ;; + search) + exec $ripgrep "$FZF_QUERY" $path + ;; + search+hidden) + exec $ripgrep -. "$FZF_QUERY" $path + ;; + git-files*) + exec git ls-files $path + ;; + git-search*) + exec git grep --column --line-number --color=always "$FZF_QUERY" $path + ;; + jj-files*) + exec jj file list --ignore-working-copy $path + ;; + jj-search*) + exec $ripgrep "$FZF_QUERY" $(jj file list --ignore-working-copy $path) + ;; + esac + exit +fi + +if [ "$1" = preview ]; then + if [ -n "$3" ]; then + start=$(( $3 - $FZF_PREVIEW_LINES / 2 )) + if [ $start -lt 1 ]; then + start=1 + fi + end=$(( $start + $FZF_PREVIEW_LINES )) + exec bat --style=numbers --color=always --highlight-line $3 --line-range $start:$end $2 + else + exec bat --style=numbers --color=always $2 + fi +fi + +reload="reload(sh $0 list || true)" +no_search="rebind(change)+disable-search" +search="unbind(change)+enable-search" + +if [ "$1" = key ]; then + case "$FZF_KEY" in + alt-d) + path=${FZF_HEADER_LABEL#?} + case "$FZF_HEADER_LABEL" in + .?*) + echo "change-header(path: $path)+change-header-label(:$path)+$reload" + ;; + *) + echo "change-header()+change-header-label(.$path)+$reload" + ;; + esac + ;; + alt-f) + case "$cmd" in + *files) + prompt="files$hidden" + ;; + *search) + prompt="${cmd%-*}-files$hidden" + ;; + esac + echo "change-prompt($prompt> )+$search+$reload" + ;; + alt-g) + case "$cmd" in + *files) + prompt="git-files$hidden" + echo "change-prompt($prompt> )+$search+$reload" + ;; + *search) + prompt="git-search$hidden" + echo "change-prompt($prompt> )+$no_search+$reload" + ;; + esac + ;; + alt-h) + if [ -z "$hidden" ]; then + prompt="$cmd+hidden" + else + prompt="$cmd" + fi + echo "change-prompt($prompt> )+$reload" + ;; + alt-j) + case "$cmd" in + *files) + prompt="jj-files$hidden" + echo "change-prompt($prompt> )+$search+$reload" + ;; + *search) + prompt="jj-search$hidden" + echo "change-prompt($prompt> )+$no_search+$reload" + ;; + esac + ;; + alt-s) + case "$cmd" in + *files) + prompt="${cmd%-*}-search$hidden" + ;; + *search) + prompt="search$hidden" + ;; + esac + echo "change-prompt($prompt> )+$no_search+$reload" + ;; + esac + exit +fi + +if [ -n "$2" ]; then + label=".$2" +else + label=".." +fi + +case ${1%+hidden} in +auto-files) + git=$(git rev-parse --show-toplevel 2> /dev/null) + jj=$(jj root --ignore-working-copy 2> /dev/null) + if [ -n "$jj" ] && [ "${git#$jj}" = "$git" -o "$git" = "$jj" ]; then + cmd="jj-files$hidden" + elif [ -n "$git" ] && [ "${jj#$git}" = "$jj" ]; then + cmd="git-files$hidden" + else + cmd="files" + fi + action="$search" + prompt="$cmd> " + ;; +auto-search) + git=$(git rev-parse --show-toplevel 2> /dev/null) + jj=$(jj root --ignore-working-copy 2> /dev/null) + if [ -n "$jj" ] && [ "${git#$jj}" = "$git" -o "$git" = "$jj" ]; then + cmd="jj-search$hidden" + elif [ -n "$git" ] && [ "${jj#$git}" = "$jj" ]; then + cmd="git-search$hidden" + else + cmd="search" + fi + action="$no_search" + prompt="$cmd> " + ;; +files|git-files|jj-files) + cmd="$1" + action="$search" + prompt="$1> " + ;; +search|git-search|jj-search) + cmd="$1" + action="$no_search" + prompt="$1> " + ;; +*) + cmd="files" + action="$search" + prompt="files> " + ;; +esac +key="transform(sh $0 key)" + +HELP='Keys: + +alt-? help +alt-d toggle alternative directory +alt-f switch to files (twice for plain files) +alt-g switch to git +alt-j switch to jj +alt-h toggle hidden +alt-p toggle preview (also to turn off help) +alt-s switch to search (twice for plain search) +alt-u toggle preview window position (right or up) +' + +exec fzf --query "$3" \ + --prompt "$prompt" \ + --header-label "$label" \ + --ansi \ + --delimiter : \ + --ghost ' (Use alt-? for help)' \ + --bind "change:$reload" \ + --bind "start:$reload+$action" \ + --bind "alt-d:$key" \ + --bind "alt-f:$key" \ + --bind "alt-g:$key" \ + --bind "alt-j:$key" \ + --bind "alt-h:$key" \ + --bind "alt-s:$key" \ + --bind "alt-p:change-preview(sh $0 preview {1} {2})+toggle-preview" \ + --bind "alt-u:change-preview-window(up|right)" \ + --bind "alt-?:change-preview(echo '$HELP')+show-preview" diff --git a/vis/.config/vis/visrc.lua b/vis/.config/vis/visrc.lua index 9842a9e..ba3e35d 100644 --- a/vis/.config/vis/visrc.lua +++ b/vis/.config/vis/visrc.lua @@ -66,6 +66,32 @@ local function escape_and_quoted(s) return "'" .. s:gsub("'", "\\'") .. "'" end +local function cmd_action(cmd, action) + local code, out, err = vis:pipe(cmd, true) + if code == 0 then + action(out) + elseif err ~= nil then + vis:info(err) + elseif code ~= 0 then + vis:info('Program exit code ' .. code) + end +end + +local function fzf_sh(arg) + action = function(out) + open_file_pos(out, vis.win.file.modified and 'o' or 'e') + end + local home = os.getenv('HOME') + local path = vis.win.file.path or '' + local dir = path:match('^.*/') or '' + local reg = '' + if reg ~= '"' then + reg = string.gsub(vis.registers[vis.register][1], '%z', '') + end + local cmd = home .. '/.config/vis/fzf.sh ' .. escape_and_quoted(arg) .. ' ' .. escape_and_quoted(dir) .. ' ' .. escape_and_quoted(reg) + cmd_action(cmd, action) +end + local function search(cmd, action) if action == nil then action = function(out) @@ -78,14 +104,7 @@ local function search(cmd, action) cmd = cmd .. ' --query=' .. escape_and_quoted(reg) end end - local code, out, err = vis:pipe(cmd, true) - if code == 0 then - action(out) - elseif err ~= nil then - vis:info(err) - elseif code ~= 0 then - vis:info('Program exit code ' .. code) - end + cmd_action(cmd, action) end local file_slots = {} @@ -178,56 +197,26 @@ vis.events.subscribe(vis.events.INIT, function() vis:map(vis.modes.NORMAL, ' l=', ':lspc-format') for num = 1, 9 do - vis:map(vis.modes.NORMAL, ' s' .. num, function() set_file_slot(num) end, 'set file slot ' .. num) + vis:map(vis.modes.NORMAL, ' r' .. num, function() set_file_slot(num) end, 'set file slot ' .. num) vis:map(vis.modes.NORMAL, ' ' .. num, function() open_file_slot(num, 'e') end, 'open file slot ' .. num) vis:map(vis.modes.NORMAL, ' h' .. num, function() open_file_slot(num, 'o') end, 'open file slot ' .. num) vis:map(vis.modes.NORMAL, ' v' .. num, function() open_file_slot(num, 'vsplit') end, 'open file slot ' .. num) end - vis:map(vis.modes.NORMAL, ' fg', function() - search(fzf_reload(ripgrep)) - end, 'fzf: rg') + vis:map(vis.modes.NORMAL, ' s', function() + fzf_sh('auto-search') + end, 'fzf: search') - vis:map(vis.modes.NORMAL, ' hg', function() - search(fzf_reload(ripgrep .. ' -.')) - end, 'fzf: rg with hidden files') + vis:map(vis.modes.NORMAL, ' f', function() + fzf_sh('auto-files') + end, 'fzf: files') vis:map(vis.modes.NORMAL, ' /', function() search(fzf_reload(ripgrep .. ' --with-filename {q} ' .. escape_and_quoted(vis.win.file.path))) end, 'fzf: rg current file') - vis:map(vis.modes.NORMAL, ' ff', function() - search('fd --type f | fzf') - end, 'fzf: files') - - vis:map(vis.modes.NORMAL, ' fh', function() - search('fd -H --type f | fzf') - end, 'fzf: files with hidden files') - - vis:map(vis.modes.NORMAL, ' df', function() - local path = vis.win.file.path - if path then - local dir = path:match('^.*/') - local arg = escape_and_quoted(dir) - search('fd --type f "" ' .. arg .. ' | fzf') - else - search('fd --type f | fzf') - end - end, 'fzf in file directory') - - vis:map(vis.modes.NORMAL, ' dh', function() - local path = vis.win.file.path - if path then - local dir = path:match('^.*/') - local arg = escape_and_quoted(dir) - search('fd -H --type f "" ' .. arg .. ' | fzf') - else - search('fd -H --type f | fzf') - end - end, 'fzf in file directory with hidden files') - - vis:map(vis.modes.NORMAL, ' sh', function() + vis:map(vis.modes.NORMAL, ' .', function() local shell = os.getenv('SHELL') vis:command('!' .. shell) end, 'run shell') @@ -244,29 +233,13 @@ vis.events.subscribe(vis.events.INIT, function() end end, 'run shell in file directory') - vis:map(vis.modes.NORMAL, ' gf', function() - search('git ls-files | fzf') - end, 'fzf: git files') - - vis:map(vis.modes.NORMAL, ' gg', function() - search(fzf_reload('git grep --column --line-number --color=always')) - end, 'fzf: jj grep') - vis:map(vis.modes.NORMAL, ' gl', function() vis:command('!lazygit') end, 'lazygit') - vis:map(vis.modes.NORMAL, ' jf', function() - search('jj file list | fzf') - end, 'fzf: jj files') - - vis:map(vis.modes.NORMAL, ' jg', function() - search(fzf_reload(ripgrep .. ' {q} $(jj file list | xargs)')) - end, 'fzf: jj grep') - vis:map(vis.modes.NORMAL, ' jl', function() vis:command('!lazyjj') - end, 'fzf open') + end, 'lazyjj') vis:map(vis.modes.NORMAL, ' ', function() vis:command('fzfmru')