-
-
Save noahmayr/ed545a6bcd7c27d19dab1b629af0d144 to your computer and use it in GitHub Desktop.
#!/bin/sh | |
### CONFIG ### | |
changeIdColumn="2" | |
commitIdColumn="3" | |
nodeTypes="@○◆•" | |
delimiterPattern='((?:^[^ ] +)|[^[:alnum:]:.])+' | |
defaultLogArgs="" | |
copyStdinToClipboard="pbcopy" | |
### END CONFIG ### | |
# need to export this so all subshells created by fzf are also going to use sh and the correct PATH env var | |
export SHELL=/bin/sh | |
export PATH="$PATH" | |
command="jj --color=always" | |
ignoreWcCommand="$command --ignore-working-copy" | |
baseLogCommand="$ignoreWcCommand log" | |
reload="toggle-preview+reload($baseLogCommand)+toggle-preview" | |
loadObsLog="reload($ignoreWcCommand obslog -r {$changeIdColumn})" | |
previewCommand="$ignoreWcCommand show --color-words {$commitIdColumn}" | |
selectedOrFocusedRevset="echo {+$commitIdColumn} | tr '[:blank:]' '|'" | |
pipeToPager="2>&1 | less -KR > /dev/tty" | |
logCommand="if [ -z \"\$FZF_BORDER_LABEL\" ]; then $baseLogCommand $defaultLogArgs; else $baseLogCommand -r \"\$FZF_BORDER_LABEL\"; fi" | |
reloadLog="reload($logCommand)+first" | |
rebaseCommand="$selectedOrFocusedRevset | xargs -I% $command rebase" | |
help=$'### NORMAL MODE ### | |
[q] Quit | |
[i] Enter revset query mode | |
[o] Enter obslog mode for focused revision | |
[esc] Reload | |
[enter] Print selected change ids to stdout | |
[u] Undo | |
[s] Status | |
[e] Edit focused | |
[n] New with selected parents | |
[d] Describe focused | |
[a] Abandon selected | |
[U] Rebase selected on trunk | |
[f] Fetch | |
[S] Sync | |
[r] Set selected as parents of @ | |
[p] Add selected as parents of @ | |
[P] Remove selected as parents of @ | |
[c] Copy change id | |
[C] Copy commit id | |
[space] Show change in detail | |
(i) "selected" falls back to whichever revision is focused if there is no selection | |
### REVSET QUERY MODE ### | |
[esc] Go back to normal mode | |
[enter] search for revset, invalid queries will result in empty log (pressing enter with empty query will restore default query) | |
### OBSLOG MODE ### | |
[esc] Go back to normal mode | |
[space] Show commit in detail | |
[c] Copy change id | |
[C] Copy commit id | |
[enter] Print selected commit ids to stdout | |
' | |
normal_header='Press ? for help, q to quit' | |
revset_header='Press ? for help, ctrl+c to quit' | |
obslog_header='Press ? for help, q to quit' | |
$baseLogCommand | | |
fzf --ansi --exact --reverse --no-sort --tiebreak=index --border --no-info --multi --phony --no-mouse --prompt "normal" \ | |
--delimiter "$delimiterPattern" \ | |
--header 'Press ? for help, ctrl+q to quit' \ | |
--preview "$previewCommand" \ | |
--height 100% --preview-window right \ | |
--bind "esc:change-prompt(normal)+rebind(u,S,e,n,d,a,U,f,s,r,p,p,i,o,space,y,Y,q,?)+$reloadLog" \ | |
--bind "change:transform:[[ \$FZF_PROMPT ~= 'revset: ' ]] && echo 'clear-query'" \ | |
--bind "?:execute(echo '$help' $pipeToPager)" \ | |
--bind "u:execute($command undo $pipeToPager)+$reloadLog" \ | |
--bind "s:execute($command status $pipeToPager)+$reloadLog" \ | |
--bind "e:execute($command edit {$commitIdColumn} $pipeToPager)+$reloadLog" \ | |
--bind "n:execute($command new {+$commitIdColumn} $pipeToPager)+$reloadLog" \ | |
--bind "d:execute($command describe {$commitIdColumn} > /dev/tty)+$reloadLog" \ | |
--bind "a:execute($command abandon {$commitIdColumn} $pipeToPager)+$reloadLog" \ | |
--bind "U:execute($rebaseCommand -b 'all:%' -d 'trunk()' $pipeToPager)+$reloadLog" \ | |
--bind "f:execute($command git fetch $pipeToPager)+$reloadLog" \ | |
--bind "S:execute($command git push $pipeToPager)+$reloadLog" \ | |
--bind "r:execute($rebaseCommand -r @ -d 'all:%' $pipeToPager)+$reloadLog" \ | |
--bind "p:execute($rebaseCommand -r @ -d 'all:@- | %' $pipeToPager)+$reloadLog" \ | |
--bind "P:execute($rebaseCommand -r @ -d 'all:@- & ~%' $pipeToPager)+$reloadLog" \ | |
--bind "y:execute-silent(echo {$changeIdColumn} | $copyStdinToClipboard)" \ | |
--bind "Y:execute-silent(echo {$commitIdColumn} | $copyStdinToClipboard)" \ | |
--bind "q:abort" \ | |
--bind "space:execute($command show --ignore-working-copy {$commitIdColumn} $pipeToPager)" \ | |
--bind "o:change-prompt(obslog)+unbind(u,S,e,n,d,a,U,f,s,r,p,p,i,o)+rebind(?,space,y,Y,q)+$loadObsLog+first" \ | |
--bind "i:change-prompt(revset: )+unbind(u,S,e,n,d,a,U,f,s,r,p,p,i,o,space,y,Y,q)+rebind(?)+$reloadLog" \ | |
--bind "enter:transform:if [ \"\$FZF_PROMPT\" = 'revset: ' ]; then echo 'transform-border-label(echo \"\$FZF_QUERY\")+clear-query+$reloadLog'; elif [ \$FZF_PROMPT = 'normal' ]; then echo 'become(echo {+$changeIdColumn})'; else echo 'become(echo {+$commitIdColumn})'; fi" \ | |
--bind "focus:transform:echo {} | grep -q '[$nodeTypes]' && exit || test {fzf:action} = up && echo up || echo down" |
The base of primitives here is excellent. Thanks so much!
I took most of the primitives here, kept my original idea of eval()-ing the query, and made the bindings more vimmish (i learned you could do this using change:clear-query
+ binding to single keys). The result is a huge QoL improvement for me, because I get to keep all of the Emacs-style keybindings to edit the query + I like single key bindings anyway.
#... config and variables
becomeNormal="change-prompt(NORMAL MODE: )+rebind(change,i,j,k,n,E,A,d,U,O,y,Y,space)+unbind(enter)"
becomeInsert="change-prompt(INSERT MODE: )+unbind(change,i,j,k,n,E,A,d,U,O,y,Y,space)+rebind(enter)"
eval $baseLogCommand |
fzf --ansi --exact --reverse --no-sort --no-info --phony --prompt "NORMAL MODE: " \
--delimiter "$delimiterPattern" \
--header 'Press ? for help, ctrl+q to quit' \
--preview "$command --ignore-working-copy show --color-words {$commitIdColumn}" \
--height 100% --preview-window right \
--bind "change:clear-query" \
--bind "j:down" \
--bind "k:up" \
--bind "n:execute-silent($command new {+$commitIdColumn})+$reloadLog" \
--bind "E:execute-silent($command edit {+$commitIdColumn})+$reloadLog" \
--bind "A:execute-silent($command abandon {+$commitIdColumn})+$reloadLog" \
--bind "d:execute-silent($command describe {+$commitIdColumn})+$reloadLog" \
--bind "U:execute-silent($command undo)+$reloadLog" \
--bind "space:execute($command show --ignore-working-copy {$commitIdColumn} | $pipeToPager)" \
--bind "O:change-prompt(OBSLOG MODE)+$loadObsLog+first" \
--bind "y:execute-silent(echo -n {$changeIdColumn} | $copyStdinToClipboard)" \
--bind "Y:execute-silent(echo -n {$commitIdColumn} | $copyStdinToClipboard)" \
--bind "enter:execute(eval {q})+$becomeNormal+$reload" \
--bind "esc:$becomeNormal+$reload" \
--bind "i:$becomeInsert" \
--bind "start:$becomeNormal"
I'll probably post something somewhere sometime.
Updated this one to be more vimlike as well, you can enter revset mode by pressing i
, entering a revset and submitting it with enter
, the revset is then persisted in the FZF_BORDER_LABEL
and can be cleared again by pressing enter with an empty query in revset mode.
obslog is also a separate mode with only a subset of keybinds supported.
I'm thinking about also adding a command mode for your eval needs
notes:
alt-c
/alt-p
which is alsoç
/π
on my keyboard)builtin_change_id_with_hidden_and_divergent_info
to achieve this. Play around with the columns in the config until the correct one is extracted:transform
to all of them to prevent them from working in that mode