-
-
Save noahmayr/ed545a6bcd7c27d19dab1b629af0d144 to your computer and use it in GitHub Desktop.
Interactive `jj` with `fzf`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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" |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.I'll probably post something somewhere sometime.