Skip to content

feat(helix): add optional Steel plugin client - #94

Draft
icorbrey wants to merge 2 commits into
jennings:mainfrom
icorbrey-contrib:icorbrey/helix-plugin
Draft

feat(helix): add optional Steel plugin client#94
icorbrey wants to merge 2 commits into
jennings:mainfrom
icorbrey-contrib:icorbrey/helix-plugin

Conversation

@icorbrey

Copy link
Copy Markdown

Yo. I have not looked at a lick of this code nor tested it, probably not the best idea to run it yet.

icorbrey added 2 commits July 28, 2026 18:17
Adds clients/helix-steel/ for people running a Steel-enabled Helix
build (mattwparas/helix, branch steel-event-system — Steel is not
yet in mainline Helix; helix-editor/helix#8675 is still open).

cogs/badjuju-core.scm holds pure logic (window classification, JJ:
shortcut-line detection, file:// URI stripping, cursor-arg
construction) with no editor-context dependency, unit-tested
directly against a plain `steel` interpreter. cogs/badjuju.scm wraps
every badjuju.* server command as a named Steel command and adds:

- Auto-open after state-changing commands. Vanilla Helix code
  actions discard the workspace/executeCommand JSON result
  (helix-view::execute_lsp_command only logs errors on it, by
  design — Helix expects state changes via workspace/applyEdit
  instead), which is why the plain languages.toml client documents
  manual `:open` as a known limitation. jj-execute! opens the
  returned URI itself.
- RET context dispatch in log.jujutsu (apply a JJ: shortcut vs.
  goto-definition), matching the Neovim/Emacs clients — something
  plain Helix has no keybinding layer to express at all.
- A `tab`-prefixed keymap submenu for magit-style single-letter
  access. Deliberately not a flat top-level scheme like Kakoune/
  Neovim/Emacs ship: Steels extension/label keymap is a static
  override for every buffer sharing the .jujutsu extension, with no
  per-window-kind fallback to a native binding the way those
  clients per-buffer keying has. A flat letter scheme would silently
  break normal editing in describe.jujutsu and hunk-edit.jujutsu,
  which are real text-entry buffers. RET and tab both have no
  default Helix normal-mode binding, so nesting everything else
  under tab costs one keystroke instead of any collision risk.

Testing gap this fills: the plain languages.toml Helix client has no
Steel logic to test, so this is the first Scheme code in the repo.
test/badjuju-test.scm unit-tests badjuju-core.scm directly.
test/smoke.scm loads the real badjuju.scm against stub helix/*
modules (test/fixtures/helix/) and calls every exported command
across status/log/squash/describe window kinds — this caught three
real bugs during development (a missing `helix/misc.scm` require
for send-lsp-command, a missing `set-scratch-buffer-name!` stub
surfacing the same gap, and `(void)` used as a call instead of the
bare value `void`, which is not callable in Steel). No Steel-enabled
Helix build exists in this repos CI, so neither suite proves
in-editor behavior — thats documented explicitly in the README
rather than implied by a passing test.

CI: new "Helix (Steel)" job installs the standalone `steel`
interpreter (steel-interpreter on crates.io, same 0.8.2 the tests
were developed against) and hard-fails via `steel --version` if
setup silently no-ops, mirroring the Neovim/Emacs jobs guard against
run.sh soft-skipping when the tool is missing.
`new` ("Create a new scratch buffer") is a typed command from
helix/commands.scm (imported with the `helix.` prefix), not a static
command from helix/static.scm (imported with `helix.static.`).
`(helix.static.new)` is a FreeIdentifier that only surfaces when Steel
compiles the whole file at load time, not when jj-show-scratch! is
actually called -- which is why the standalone-steel smoke test in
test/smoke.scm never caught it: its fixtures/helix/static.scm stub
defined its own `new` to make the call succeed, masking that the real
static.scm has no such export.

Caught by driving a real Steel-enabled `hx` (mattwparas/helix,
steel-event-system-derived megamerge) through `expect` and capturing
the TUI error popup via a terminal emulator (pyte) -- the popup is the
only place present_error_inside_engine_context surfaces a load error;
it is never logged. Confirmed the fix by the presence of
"Finished loading init.scm!" in the log, which only prints on the
Ok(_) arm of the whole-file eval.

Testing gap this exposes: the smoke test stubs every helix/* module
by hand instead of validating call sites against real exports, so a
misattributed module for any helix.*/helix.static.* call is invisible
to it. Left as a known limitation in the plugin README rather than
fixed here -- doing so properly means either vendoring the real
static.scm/commands.scm export lists to diff against, or dropping the
hand-written stubs for a real Steel-enabled hx in CI, neither of which
is a quick follow-up.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant