Skip to content

fix(zsh): show all matches when subcommand names contain :#666

Open
zeitlinger wants to merge 2 commits into
jdx:mainfrom
zeitlinger:fix-zsh-completion-colon-grouping
Open

fix(zsh): show all matches when subcommand names contain :#666
zeitlinger wants to merge 2 commits into
jdx:mainfrom
zeitlinger:fix-zsh-completion-colon-grouping

Conversation

@zeitlinger
Copy link
Copy Markdown

@zeitlinger zeitlinger commented Jun 4, 2026

Summary

_describe groups matches that share a \:-escaped prefix and surfaces only one entry per group. With several subcommands sharing a colon prefix (e.g. release:create, release:docs-sync, release:pr, release:update) the menu only lists release:create and silently drops the rest. The same collapse happens when a plain lint exists alongside lint:fix.

Repro

bin "testcli"
cmd "release:create" help="Create release"
cmd "release:docs-sync" help="Refresh docs"
cmd "release:pr" help="Open release PR"
cmd "release:update" help="Update release state"
cmd "lint" help="Run lints"
cmd "lint:fix" help="Auto-fix lints"

Generate zsh completion, tab testcli <TAB> — before this PR, only release:create and lint appear. After: all six show.

Discovered via jdx/mise where users have tasks named release:create, release:docs-sync, release:pr, release:update — most of them disappeared from completion.

Fix

Build the display column ourselves from the (insert, description) pair and call compadd directly so every match is offered, with descriptions padded to the longest value for column alignment.

Test plan

  • Existing cargo test suite passes locally (cargo test --release)
  • Snapshots updated (lib/src/complete/snapshots/...)
  • New regression test test_zsh_completion_includes_all_colon_subcommands feeds a colon-heavy spec through real zsh + zpty and asserts every subcommand appears in the menu

This PR was authored with assistance from an AI coding agent.

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Improved zsh shell completion output formatting for better menu display and handling of subcommand names containing special characters.

`_describe` groups matches that share a `\:`-escaped prefix and surfaces
only one entry per group. For specs that have several subcommands sharing
a colon prefix (e.g. `release:create`, `release:docs-sync`, `release:pr`,
`release:update`), the menu only lists `release:create` and silently drops
the rest. The same collapse happens when a plain `lint` exists alongside
`lint:fix`.

Build the display column ourselves from the (insert, description) pair
and call `compadd` directly so every match is offered, with descriptions
aligned to the longest value.

Regression test: feeds a colon-heavy spec through real zsh + zpty and
asserts every subcommand appears in the menu output.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR refactors zsh completion output formatting: complete-word now emits three tab-separated columns (value, description, shell-quoted insert) instead of constructing _describe-formatted strings, and the zsh script generator is updated to parse and render completions using compadd directly. All unit and integration tests are updated to validate the new format.

Changes

Zsh Completion Format Refactoring

Layer / File(s) Summary
Complete-word zsh output format
cli/src/cli/complete_word.rs
The zsh completion branch now emits three tab-separated columns (raw value, description, shell-quoted insert for compadd -Q) instead of computing a _describe-formatted value:description string. The private zsh_escape helper is removed as it is no longer needed.
Zsh script generation for 3-column format
lib/src/complete/zsh.rs
The zsh completion script template is refactored to split the three-column complete-word output into separate values, descs, and inserts arrays, aligns descriptions by longest value width, and invokes compadd directly with -d _usage_display and -a inserts instead of delegating to _describe.
Complete-word unit tests for 3-column output
cli/tests/complete_word.rs
Unit tests are replaced to validate that zsh emits three tab-separated columns per completion, covering both cases where descriptions are populated and where descriptions remain empty.
Integration test coverage and regression validation
cli/tests/shell_completions_integration.rs
Integration tests are updated to expect the new three-column layout in complete-word output and verify that the generated zsh script correctly parses columns and uses compadd with pre-quoted inserts. A new regression test validates that colon-containing subcommand names (e.g., release:*, lint:*) all appear in the completion menu under real zpty-driven zsh execution.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 Three columns now dance,
where two did once suffice—
zsh tabs align all.
No more escapes to arrange,
pure data flows in space.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main fix: resolving a zsh completion issue where subcommand names containing colons were not all displayed. This directly reflects the core problem and solution in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 4, 2026

Greptile Summary

Fixes a zsh tab-completion bug where _describe's colon-prefix grouping caused only one entry per release:*/lint:* family to appear. The fix switches from a two-column _describe protocol to a three-column compadd -l -d approach where the display string is built manually.

  • complete-word --shell zsh now emits value\tdescription\tinsert (three columns) instead of display\tinsert (two columns), eliminating the need for \:/\(/\[ escaping.
  • The generated zsh template replaces _describe with a manual padding loop and compadd -l -d _usage_display -U -Q -S '' -a inserts, ensuring every match is surfaced regardless of colon prefixes.
  • The two previously flagged P1 regressions (shell-quoted insert used as display label, description extraction failing for quoted values) are both resolved: the else branch now correctly uses values[_usage_i], and descriptions come from a dedicated descs column rather than being stripped from a combined display string.

Confidence Score: 4/5

The core logic change is correct and the two blocking regressions from the previous review round are resolved; only a stale doc comment remains.

The two previously flagged regressions (wrong display label in the else branch, description extraction failure for shell-quoted values) are both fixed. The remaining finding is a stale doc comment on render_completion_loop that still describes the old two-column _describe protocol, which could mislead future contributors.

lib/src/complete/zsh.rs — doc comment on render_completion_loop is stale

Important Files Changed

Filename Overview
lib/src/complete/zsh.rs Core fix: replaces _describe with compadd -l -d and manually built display strings; stale doc comment on render_completion_loop still describes the old two-column format
cli/src/cli/complete_word.rs Output format changed from two to three tab-separated columns (value, description, insert); zsh_escape helper removed as no longer needed
cli/tests/shell_completions_integration.rs Tests updated for three-column format; new regression test validates colon-prefixed subcommands all appear via real zsh + zpty
cli/tests/complete_word.rs Unit tests updated to reflect three-column output; no more \:/\(/\[ escaping assertions
lib/src/complete/snapshots/usage__complete__zsh__tests__complete_zsh.snap Snapshot updated to reflect new compadd-based loop template replacing _describe

Reviews (2): Last reviewed commit: "fix(zsh): emit three columns (value, des..." | Re-trigger Greptile

…desc`

Greptile flagged that the previous fix used `inserts[i]` (which can be
shell-quoted, e.g. `'Alice Bob'`) as the prefix to strip from the
`zsh_escape`-d display string. For any value containing a space the
strip silently failed and the menu showed `'Alice Bob'  -- Alice Bob`.

Switch the contract between `complete-word --shell zsh` and the
generated template to emit three tab-separated columns — raw value,
raw description, shell-quoted insert. The zsh template reads the three
fields, builds the formatted display directly from value + description
(no `\:`/`\(`/`\[` escaping needed), and feeds the inserts to compadd.

Drops the now-unused `zsh_escape` helper.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
@zeitlinger
Copy link
Copy Markdown
Author

Both flagged issues are valid. Switched the contract between complete-word --shell zsh and the generated template to three tab-separated columns (raw value, raw description, shell-quoted insert) instead of the value:description format. The template now builds the display directly from columns 1 + 2 and feeds column 3 to compadd — no \:/\(/\[ escaping needed, no prefix-strip ambiguity. Fixed in d34bb1f.

This comment was generated by an AI coding assistant.

@zeitlinger zeitlinger marked this pull request as ready for review June 5, 2026 11:55
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
lib/src/complete/zsh.rs (1)

19-26: ⚡ Quick win

Update the function rustdoc to match the new 3-column contract.

The header comment above render_completion_loop still describes the old 2-column _describe flow, which now conflicts with this implementation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/src/complete/zsh.rs` around lines 19 - 26, Update the rustdoc for
render_completion_loop to describe the current 3-column contract (raw value,
description, shell-quoted insert) instead of the old 2-column `_describe` flow:
explain that `complete-word --shell zsh` emits three tab-separated columns, that
the function builds the formatted display (using `template` and aligned `descs`)
and calls `compadd` directly so every match is offered (not collapsed), and note
the handling of escaped prefixes like `\:`; reference `render_completion_loop`,
`template`, and `compadd` in the comment so readers can locate the related
logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lib/src/complete/zsh.rs`:
- Around line 19-26: Update the rustdoc for render_completion_loop to describe
the current 3-column contract (raw value, description, shell-quoted insert)
instead of the old 2-column `_describe` flow: explain that `complete-word
--shell zsh` emits three tab-separated columns, that the function builds the
formatted display (using `template` and aligned `descs`) and calls `compadd`
directly so every match is offered (not collapsed), and note the handling of
escaped prefixes like `\:`; reference `render_completion_loop`, `template`, and
`compadd` in the comment so readers can locate the related logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0acab893-7a8d-4dfe-8291-864c4643a5b0

📥 Commits

Reviewing files that changed from the base of the PR and between 163804e and d34bb1f.

⛔ Files ignored due to path filters (4)
  • lib/src/complete/snapshots/usage__complete__zsh__tests__complete_zsh-2.snap is excluded by !**/*.snap
  • lib/src/complete/snapshots/usage__complete__zsh__tests__complete_zsh-3.snap is excluded by !**/*.snap
  • lib/src/complete/snapshots/usage__complete__zsh__tests__complete_zsh.snap is excluded by !**/*.snap
  • lib/src/complete/snapshots/usage__complete__zsh__tests__complete_zsh_init.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • cli/src/cli/complete_word.rs
  • cli/tests/complete_word.rs
  • cli/tests/shell_completions_integration.rs
  • lib/src/complete/zsh.rs

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