Kakoune

The Kakoune client lives in clients/kakoune/ in the repo. It uses kak-lsp to talk to the badjuju LSP server and exposes all operations as :JJ* commands with an optional keymap via Kakoune user-modes.

Requirements

  • Kakoune 2023.08.05+
  • kak-lsp 0.14+ on your PATH
  • jj on your PATH
  • badjuju binary on your PATH (see Getting Started)

Install

plug.kak

plug "jennings/bad-juju" subset [clients/kakoune/badjuju.kak] config %{
    # Optional: switch to vim-profile chords (default: magit)
    # set-option global badjuju_keymap_profile vim
}

Manual clone

Symlink the entry point into your autoload directory:

ln -s /absolute/path/to/bad-juju/clients/kakoune/badjuju.kak \
      ~/.config/kak/autoload/badjuju.kak

Or source it directly from your kakrc:

source '/absolute/path/to/bad-juju/clients/kakoune/badjuju.kak'

Setup

1. Configure kak-lsp

Merge the kak-lsp.toml snippet from this directory into ~/.config/kak-lsp/kak-lsp.toml:

cat /path/to/bad-juju/clients/kakoune/kak-lsp.toml \
    >> ~/.config/kak-lsp/kak-lsp.toml

Then start kak-lsp in your kakrc (if not already):

eval %sh{ kak-lsp --kakoune -s $kak_session }

2. Choose a keymap profile

The default is magit (single-letter bindings). To switch to the vim profile (double-letter chords), set the option before sourcing the plugin:

set-option global badjuju_keymap_profile vim
source '/path/to/badjuju.kak'

Or in plug.kak config:

plug "jennings/bad-juju" ... config %{
    set-option global badjuju_keymap_profile vim
}

Opening buffers

The canonical one-liner opens the working-copy status view in Kakoune:

kak "$(badjuju status)"

Similarly for other views:

kak "$(badjuju log)"
kak "$(badjuju diff)"
kak "$(badjuju diff --revision abc123)"

Commands

CommandDescription
:JJStatusOpen .jj/badjuju/status.jujutsu
:JJLog [revset]Open the log buffer
:JJLogFileOpen per-file log for the file at cursor
:JJDescribe [revision]Edit a commit message (default: @)
:JJDiff [revision]Open a change diff (updates on amend)
:JJDiffCommit [revision]Open a pinned commit diff
:JJNewCreate a new change
:JJNextMove @ to the next child
:JJPrevMove @ to the previous parent
:JJRefreshRefresh the current badjuju buffer
:JJSquashSquash file at cursor into its parent
:JJUnsquashUnsquash file at cursor from parent
:JJUndoUndo the last jj operation
:JJAbandon [revision]Abandon a revision (default: @ at cursor)
:JJEdit [revision]Move @ to this revision
:JJFetchRun jj git fetch
:JJPush [!]Run jj git push (! for --force-with-lease)
:JJCancelCancel pending squash or rebase

Commands auto-start the LSP for the current workspace if it isn't already running.

Keymap reference

With a *.jujutsu buffer active, press <space> to enter the badjuju user-mode. The bindings below use the magit profile (default).

magit profile — status.jujutsu

KeyAction
RRefresh
nNew change
LOpen log
fGit fetch
pGit push
PGit push --force-with-lease
UUndo
aAbandon revision at cursor
eEdit commit at cursor (move @)
dDiff change at cursor (updates on amend)
DDiff commit at cursor (pinned)
sSelect squash source/dest (two-step)
SSquash file at cursor into parent
uUnsquash file at cursor
xCancel pending operation
qClose buffer
bBookmark… (chord)
rRebase… (chord)
cCommit… (chord)
?Show key binding help

magit profile — log.jujutsu

KeyAction
RRefresh
nNew change
UUndo
aAbandon revision at cursor
eEdit commit at cursor (move @)
dDiff change at cursor (updates on amend)
DDiff commit at cursor (pinned)
sSelect squash source/dest (two-step)
xCancel pending operation
qClose buffer
bBookmark… (chord)
rRebase… (chord)
cCommit… (chord)
?Show key binding help

magit profile — diff.jujutsu

KeyAction
RRefresh
qClose buffer
?Show key binding help

magit profile — squash window

KeyAction
sToggle hunk/file at cursor
eEdit hunk before squashing
aSelect all changes
ADeselect all changes
qClose buffer
?Show key binding help

magit profile — describe.jujutsu

KeyAction
<C-c><C-c>Save and close (finalize commit message)
<C-c><C-k>Abort (close without saving)
?Show key binding help

Chord workflows

Bookmark (b prefix in magit / bb prefix in vim)

KeyAction
cCreate bookmark (prompts for name)
mMove bookmark (prompts for name)
dDelete bookmark (prompts for name)
tTrack remote bookmark (prompts for name@remote)
fForget bookmark (prompts for name)

Rebase (r / rr)

KeyAction
sMark source with --source
rMark source with --revisions
bMark source with --branch
oComplete: rebase onto this commit
AComplete: insert after this commit
BComplete: insert before this commit

Commit transient (c / cc)

KeyAction
wReword — open describe.jujutsu
nNew child commit

Commit-to-commit squash

  1. Press s on the source commit in status or log → server marks it.
  2. Press s on the destination commit → server opens the squash window.
  3. In the squash window, use s/e/a/A to manage hunks.
  4. :w finalizes the squash; :q! aborts.

vim profile

Double-letter chords inspired by Fugitive. Enable with:

set-option global badjuju_keymap_profile vim
KeyAction
nnNew change
llOpen log
eeEdit commit at cursor (move @)
ddDescribe commit
ddDiff change at cursor (updates on amend)
DDDiff commit at cursor (pinned)
ssSquash source/dest (two-step)
SSSquash file at cursor
uuUnsquash file at cursor
UUUndo
aaAbandon revision
ffGit fetch
ppGit push
PPGit push --force-with-lease
RRRefresh
qqClose buffer
x / xxCancel pending operation
bb + c/m/d/t/fBookmark chord
rr + s/r/b/o/A/BRebase chord
cc + w/nCommit transient chord

Save flow

  • describe.jujutsu: edit the commit message, then :w runs jj describe -m <message>. <C-c><C-c> saves and closes; <C-c><C-k> aborts (magit profile).
  • hunk-edit-*.jujutsu: move hunks between SELECTED/REMAINING sections, then :w applies the selection via jj squash.

Customization

Override the <space> leader

Re-bind <space> after sourcing the plugin:

hook global WinSetOption filetype=jujutsu %{
    map window normal <tab> ': enter-user-mode badjuju-status<ret>'
}

Disable the default keymap

Set the profile to any unrecognized value; the source block in badjuju.kak will then source keymap-magit.kak (fallback). To truly disable, edit the sourcing logic or just don't map <space>:

hook global WinSetOption filetype=jujutsu %{
    unmap window normal <space>
}

Troubleshooting

kak-lsp not attaching to jujutsu buffers

Check that the [language.jujutsu] section is present in ~/.config/kak-lsp/kak-lsp.toml and that kak-lsp is running:

:lsp-show-server

:w on describe.jujutsu has no effect

Make sure include_text_on_save = true is set in your kak-lsp.toml snippet. Without it, the server receives the save event but no text.

Auto-reload not firing after mutations

workspace/applyEdit is the mechanism the server uses to push updated content to open buffers. Verify kak-lsp is attached (lsp-show-server) and that it supports apply-edit (kak-lsp 0.14+).