Skip to content

feat(go): register the completion with each of the five shells - #1006

Merged
jdx merged 3 commits into
go/complete-requestfrom
go/complete-script
Aug 18, 2026
Merged

jdx merged 3 commits into
go/complete-requestfrom
go/complete-script

Conversation

@jdx

@jdx jdx commented Aug 17, 2026

Copy link
Copy Markdown
Owner

The binary can answer a completion request; this is what makes a shell ask. argv.Script(bin, shell) returns the script to source or drop in $fpath.

Each is a handful of lines, because the thinking is on the other side: hand over the line and the cursor, present what comes back the way this shell presents things, and pass the position to the shell's own path completion if the marker appeared.

What it replaces

mise's current scripts hard-fail unless the separate usage CLI is installed, dump a spec into $XDG_CACHE_HOME, prune stale spec files by age, and shell out to usage complete-word on every Tab. None of that is here — the binary was compiled with the tables.

Ported, not reinvented

Script for script from argv/src/script.rs, so the two frameworks are indistinguishable to a shell and a fix to one shell's quirks is a fix for both. The awkward parts came with them:

  • bash sets compopt -o filenames from inside the function, because whether paths belong here is not known until the answer comes back.
  • zsh builds its own display column. _describe groups matches sharing a :-separated prefix and shows one per group, which collapses mise's release:create, release:docs-sync and release:pr into a single entry.
  • fish gets -f so it offers no filenames of its own, and calls __fish_complete_path itself when the answer says paths belong.
  • nushell escapes a binary's name rather than flattening it: flattening put foo-bar and foo+bar under one identifier, and two scripts loaded together each completed the other's binary.
  • PowerShell subtracts the extent's start from the cursor, because $cursorPosition counts from the whole buffer while Extent.Text is one command's span, and after a ; the two do not share an origin.

Every shell is handed text already cut at its own cursor, so no offset has to travel between two things that count in different units.

A name that cannot be registered

Script panics on one. zsh's #compdef line is read by compinit before shell quoting exists, so a name that is not one plain shell word cannot be registered by anyone — and the name comes from a spec its author wrote, so this is a mistake surfacing where it can be fixed rather than at a prompt.

What is checked

The scripts are asserted as text: each calls the same hidden command with its own --shell, registers under a name it actually defines, watches for the marker the renderer writes, and leaves no placeholder behind. What a script does is a shell's business, and that behaviour is where the port came from.

Running them in real shells is the obvious next test — CI already installs bash, zsh, fish and pwsh for usage-cli's completion tests — and it wants its own change rather than being smuggled in here.

Verified

cargo test --all --all-features, clippy, cargo fmt --check, go test ./..., go vet, prettier, and mise run gen-go produces no diff.

🤖 Generated with Claude Code


Note

Low Risk
New optional completion-script generation and docs; no changes to parsing, binding, or runtime CLI behavior beyond what adopters explicitly install.

Overview
Adds argv.Script(bin, shell), which emits the bash/zsh/fish/nushell/PowerShell snippets that register tab completion by calling the binary’s hidden __complete_word__ handler (no external usage CLI, no cached spec).

The scripts mirror usage-argv’s script.rs: each shell truncates the line at its own cursor, maps RenderAnswer file/dir markers into native path completion, and handles shell-specific edge cases (zsh #compdef ordering, fish printf vs echo, nushell identifier escaping and chaining the prior external completer, PowerShell extent-relative cursor). Script panics if the binary name is not a single “plain” shell word, since zsh cannot register quoted names in #compdef.

go/README.md is updated so “what is missing” no longer lists shell scripts—only spec run= completion that shells out on Tab remains out of scope.

script_test.go asserts wiring (correct command/registration, markers, placeholders, nushell collision rules) as generated text, not live shell runs.

Reviewed by Cursor Bugbot for commit 86ee4db. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 05ca27eb-cef0-48a1-b784-77abbdccea04

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread go/argv/script.go
@jdx
jdx force-pushed the go/complete-script branch from f4bda01 to 20ad038 Compare August 17, 2026 23:54

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 20ad038. Configure here.

Comment thread go/argv/script.go Outdated
Comment thread go/argv/script.go Outdated
jdx and others added 3 commits August 18, 2026 00:03
The binary can answer a completion request; this is what makes a shell ask. Each
script is a handful of lines, because the thinking is on the other side: hand
over the line and the cursor, present what comes back the way this shell presents
things, and pass the position to the shell's own path completion if the marker
appeared.

What it replaces is worth stating. mise's current scripts hard-fail unless the
separate `usage` CLI is installed, dump a spec into `$XDG_CACHE_HOME`, prune
stale spec files by age, and shell out to `usage complete-word` on every Tab.
None of that is here — the binary was compiled with the tables.

Ported script for script from usage-argv's, so the two frameworks are
indistinguishable to a shell and a fix to one shell's quirks is a fix for both.
The awkward parts came with them: bash sets `compopt -o filenames` from inside
the function because whether paths belong is not known until the answer comes
back; zsh builds its own display column, because `_describe` groups matches
sharing a `:` prefix and would collapse mise's `release:*` tasks into one entry;
nushell escapes a binary's name rather than flattening it, since flattening put
`foo-bar` and `foo+bar` under one identifier and two scripts loaded together
completed each other's binary.

A name that is not one plain shell word panics rather than being written into a
script. zsh's `#compdef` line is read by `compinit` before shell quoting exists,
so there is nowhere to put a quote — the name comes from a spec its author wrote,
and this is a mistake surfacing where it can be fixed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The function was defined and never hooked up, so the script was a no-op: sourced,
sourced correctly, and doing nothing at all. Dropped on the way over from
usage-argv, whose version ends with the block that installs it.

Chained rather than assigned. nushell has one external completer for the whole
shell, so replacing it would silently stop completing every other tool the user's
config had set up — the previous one is kept and called for any binary but this
one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…s data

Two shells reading a script's own text as instructions.

`compinit` looks for `#compdef` on the *first line* of a file in `$fpath`, and
the generated header was above it — so the drop-in half of what the script
documents never registered. Sourcing it still worked, through the `compdef` call
at the bottom, which is why it looked fine.

And fish's `echo` reads a leading `-n`, `-e`, `-s` or `-E` as its own option, so
a CLI with a `-n` had that candidate swallowed on the way to the prompt. `printf`
takes its format first and everything after it as data.

Both came over from usage-argv's scripts, which have them too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jdx
jdx force-pushed the go/complete-script branch from 20ad038 to 86ee4db Compare August 18, 2026 00:04
@github-actions

Copy link
Copy Markdown
Contributor

Instruction counts

Nothing was compared, and so nothing was gated. No series appears on both sides: either the base has no measurements recorded, or the two were measured on different runner classes, which are deliberately not comparable — counts shift between machine types by more than a real regression does.

New, nothing to compare against: markdown on bamboo-v2-ubuntu24.04-x64-30vcpu-24gb-rust1.97.1, startup on bamboo-v2-ubuntu24.04-x64-30vcpu-24gb-rust1.97.1

Only instruction counts gate. Wall clock is shown for context — on identical hardware it moves 4-20% run to run.

Measured by tak — instruction-counted CLI benchmarks, stored in this repository's git notes.

Shadow comparison

Parsing mise use -g node@20 against a shadow of mise's committed spec.
Reported, not gated: the shadow grows as the derive learns to express more, so
what to watch is the ratio rather than either column.

framework instructions, cold parse vs usage
usage 4216
argh 6292 1.5x
clap 5893640 1397x
bpaf 21917948 5198x
                                              min       p01       p10    median
usage-rs: argv -> struct                      191       195       198       202  ns
argh: argv -> struct                          280       284       288       293  ns
clap: build tree + parse -> struct         486777    487491    491704    500162  ns
bpaf: build parser + parse -> struct      1619222   1619222   1628760   1680758  ns

usage: argv -> struct                             209 ns      0.21 µs
clap: build tree + parse -> struct             496827 ns    496.83 µs
clap: parse -> struct, tree reused              23299 ns     23.30 µs
clap: build tree only                          307304 ns    307.30 µs

86ee4dbd0739 vs 1968b2e69433 · measured on the runner, not pushed to the history.

@jdx
jdx merged commit d72abda into main Aug 18, 2026
10 checks passed
@jdx
jdx deleted the go/complete-script branch August 18, 2026 01:00
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