Skip to content

fix: suppress zsh NOMATCH errors in completion function - #3

Merged
kjanat merged 5 commits into
masterfrom
claude/fix-completion-glob-pattern-Pucc8
Apr 21, 2026
Merged

kjanat merged 5 commits into
masterfrom
claude/fix-completion-glob-pattern-Pucc8

Conversation

@kjanat

@kjanat kjanat commented Apr 17, 2026

Copy link
Copy Markdown
Owner

Summary

This PR fixes zsh completion errors by disabling the NOMATCH option in the completion function. When _files internals or user zstyles evaluate glob patterns containing unquoted *, they can raise "no matches found" errors into the user's prompt under zsh's default behavior. This change ensures those errors are suppressed.

Key Changes

  • Updated src/complete/grouped.zsh to add -o NO_NOMATCH flag to the emulate -L zsh command in the _clap_dynamic_completer_{NAME} function
  • Added comprehensive test in src/complete/mod.rs to verify the registration script includes the NO_NOMATCH option
  • Updated imports in test module to include GroupedZsh and EnvCompleter

Implementation Details

  • The emulate -L zsh -o NO_NOMATCH command ensures that glob pattern matching failures in completion specs (particularly those tagged with globbed-files) don't leak error messages to the user's prompt
  • The local scope (-L) ensures these settings don't affect the caller's environment
  • Added a new test registration_script_disables_nomatch() that validates the completion registration script contains the required NO_NOMATCH option, with clear error messaging if the assertion fails

https://claude.ai/code/session_015ddYu2FjjcpfJN93mABaT3

@coderabbitai

coderabbitai Bot commented Apr 17, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 94785158-01ae-44b1-a8bd-1a754dd7e80b

📥 Commits

Reviewing files that changed from the base of the PR and between b5f42d9 and 33d9338.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • CHANGELOG.md
  • Cargo.toml
  • src/complete/grouped.zsh
  • src/complete/mod.rs
📜 Recent review details
⏰ Context from checks skipped due to timeout of 18000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (actions)
  • GitHub Check: Analyze (rust)
🧰 Additional context used
📓 Path-based instructions (2)
@(package.json|pyproject.toml|setup.py|Cargo.toml|go.mod|pom.xml|build.gradle|VERSION)

📄 CodeRabbit inference engine (Custom checks)

@(package.json|pyproject.toml|setup.py|Cargo.toml|go.mod|pom.xml|build.gradle|VERSION): If any source code files (excluding tests, docs, CI, markdown, or comments-only changes) are modified, then a version field MUST be updated in one of the following files if present in the repo: package.json, pyproject.toml, setup.py, Cargo.toml, go.mod, pom.xml, build.gradle, or a VERSION file.
The new version MUST follow SemVer (MAJOR.MINOR.PATCH). If the PR introduces breaking changes (removal or renaming of public APIs, changes to function signatures, deleted exported symbols, or incompatible config changes), MAJOR must increment. If it adds backward-compatible functionality, MINOR must increment. If it only fixes bugs without changing public APIs, PATCH must increment.

Files:

  • Cargo.toml
**/CHANGELOG.md

📄 CodeRabbit inference engine (Custom checks)

**/CHANGELOG.md: If any source code files (excluding tests, docs, CI, markdown, or comments-only changes) are modified, CHANGELOG.md MUST also be modified in the same PR.
If a version bump is detected, CHANGELOG.md MUST contain a new section header matching the exact new version number in the format: '## [X.Y.Z] - YYYY-MM-DD'.
If NO version bump is detected, the changes in the PR MUST be added under the existing '## [Unreleased]' section in CHANGELOG.md. The entry MUST describe the changes (e.g., Added, Changed, Fixed, Removed).

Files:

  • CHANGELOG.md
🧠 Learnings (2)
📚 Learning: 2026-03-26T16:14:15.754Z
Learnt from: kjanat
Repo: kjanat/runner PR: 1
File: src/tool/go_task.rs:64-81
Timestamp: 2026-03-26T16:14:15.754Z
Learning: If code uses let-chains (e.g., `if let Some(x) = foo && ... && let Some(y) = bar`), ensure the crate’s `Cargo.toml` sets `package.edition = "2024"`. Rust 2021/earlier should not be used with let-chains; require 2024 specifically for compilation.

Applied to files:

  • Cargo.toml
📚 Learning: 2026-03-26T20:05:44.851Z
Learnt from: kjanat
Repo: kjanat/runner PR: 1
File: src/cmd/mod.rs:67-75
Timestamp: 2026-03-26T20:05:44.851Z
Learning: In Rust, `std::process::Command` does not provide getters for stdio configuration, so whether `Stdio::inherit()` (or other `Stdio::*` settings) was applied cannot be asserted via pure unit tests without spawning a process and inspecting OS-level fds. When reviewing Rust code, do not flag “missing unit test coverage” for stdio configuration on `std::process::Command` as a code issue—treat the explicit `Command::stdin/stdout/stderr` setter calls in the source as the meaningful guarantee.

Applied to files:

  • src/complete/mod.rs
🔇 Additional comments (6)
Cargo.toml (1)

3-3: Patch bump is right.

Aye, this be the correct SemVer move for a bugfix-only PR: 0.4.0 → 0.4.1, clean and proper.

As per coding guidelines "@(package.json|pyproject.toml|setup.py|Cargo.toml|go.mod|pom.xml|build.gradle|VERSION): If any source code files ... are modified, then a version field MUST be updated ... If it only fixes bugs without changing public APIs, PATCH must increment."

src/complete/grouped.zsh (2)

25-36: Aye, this finally scopes the zsh options correctly.

Line 36 fixes the nasty shell-state bleed and the _files qualifier breakage in one shot: NULL_GLOB kills the unmatched-glob mess, and EXTENDED_GLOB keeps zsh’s own pattern syntax working. Nicely done.


58-67: Good call switching to precommand noglob.

Line 67 uses the only sane scope here: protect the _files arguments from call-site expansion without hobbling _path_files internals. That’s the fix for the literal *(-/) garbage leaking into completion.

src/complete/mod.rs (2)

340-342: Test wiring is correct.

Aye, pulling EnvCompleter and GroupedZsh into scope keeps these assertions aimed at the real registration path instead of some copied fixture. That’s the tidy way to pin this bug.


564-625: These regressions pin the right brittle bits.

Happy to see this nailed down. The tests guard both failure modes that mattered here: the exact emulate options and the switch from setopt noglob to noglob _files. That’s solid coverage for this fix.

CHANGELOG.md (1)

16-45: Release notes are in sync.

Aye, this is shipshape: the 0.4.1 entry matches the patch bump, the fixes described line up with the zsh changes, and the compare links were updated instead of being left to rot.

As per coding guidelines "**/CHANGELOG.md: If any source code files ... are modified, CHANGELOG.md MUST also be modified in the same PR. If a version bump is detected, CHANGELOG.md MUST contain a new section header matching the exact new version number in the format: '## [X.Y.Z] - YYYY-MM-DD'."

Also applies to: 207-208


📝 Walkthrough

fix: suppress zsh NOMATCH errors in completion using NULL_GLOB and scoped noglob

Unmatched glob patterns in zsh completion could surface "no matches found"
errors or leave literal unmatched patterns (e.g. *(/) / *(-/)) in the
command line when _files internals or user zstyles evaluated unquoted glob
patterns.

Resolve this by scoping NULL_GLOB and EXTENDED_GLOB to the completion function
and using a precommand noglob modifier when delegating to _files so
glob expansion remains enabled inside _files itself while unmatched-glob
cases are silently dropped.

Changes:

  • src/complete/grouped.zsh
    • Start _clap_dynamic_completer_{NAME}() with
      emulate -L zsh -o NULL_GLOB -o EXTENDED_GLOB to silence "no matches
      found" errors and ensure _files qualifier patterns parse.
    • When delegating to _files, invoke it as noglob _files ${=__runner_flags}
      (precommand modifier) instead of using a function-scoped setopt noglob
      so _files internals still perform their own glob expansion and NULL_GLOB
      prevents residue being handed back to the prompt.
  • src/complete/mod.rs (tests & registration)
    • Ensure the embedded grouped.zsh template is emitted with the intended
      emulate options and noglob precommand usage via GroupedZsh::write_registration.
    • Import EnvCompleter and GroupedZsh in the test module so the registration
      script generation is exercised by tests.
  • CHANGELOG.md
    • Add a 0.4.1 entry documenting the fix and explain the interaction between
      NULL_GLOB, noglob precommand, and EXTENDED_GLOB.
  • Cargo.toml
    • Bump crate version to 0.4.1.

This confines the behavior change to the completion function only, preserving
normal shell globbing outside completion while preventing unmatched-glob
errors and literal residue from appearing during completion.

Walkthrough

Zsh completion now runs emulate -L zsh -o NULL_GLOB -o EXTENDED_GLOB inside the dynamic completer and delegates directory completion with noglob _files (replacing function-scoped setopt noglob). Tests and changelog updated; crate version bumped to 0.4.1.

Changes

Cohort / File(s) Summary
Zsh completer
src/complete/grouped.zsh
_clap_dynamic_completer_{NAME}() now invokes emulate -L zsh -o NULL_GLOB -o EXTENDED_GLOB. Replaced setopt noglob + _files ... with noglob _files ... precommand modifier to avoid leaking glob residues while preserving _files internals.
Tests
src/complete/mod.rs
Updated imports to bring GroupedZsh into scope and added two regression tests: one asserts the registration script includes emulate -L zsh -o NULL_GLOB -o EXTENDED_GLOB; the other asserts path-hint delegation uses noglob _files and that no non-comment setopt noglob remains.
Release metadata & changelog
CHANGELOG.md, Cargo.toml
Bumped crate version to 0.4.1; added 0.4.1 changelog entry describing NULL_GLOB/EXTENDED_GLOB scoping and noglob _files change; updated compare links ([Unreleased] → v0.4.1...HEAD, added [0.4.1]).

Poem

Avast, ye globby scourge, be quelled at last,
Emulate locks the storm, the errors past.
Noglob stands afore _files on the prow,
Completion neat — tidy code, take a bow. 🏴‍☠️

🚥 Pre-merge checks | ✅ 8
✅ Passed checks (8 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: suppress zsh NOMATCH errors in completion function' clearly describes the main change in the PR and follows conventional commit guidelines with a 'fix:' prefix.
Description check ✅ Passed The description thoroughly explains the PR's purpose, key changes, and implementation details—all directly related to the zsh completion fixes 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.
Changelog Update ✅ Passed CHANGELOG.md properly updated with version 0.4.1 section matching Cargo.toml bump, comprehensive Fixed entries documenting all code changes, correct [X.Y.Z] - YYYY-MM-DD format, and updated version links.
Semver Version Bump Validation ✅ Passed PR correctly bumps version from 0.4.0 to 0.4.1 (PATCH increment) in Cargo.toml, matching SemVer requirements for bug fixes with no public API changes.
Agents.Md Documentation Updated ✅ Passed No AGENTS.md file exists in the repository, so the check condition is not applicable. The custom check instructions explicitly state the requirement only applies if an AGENTS.md file exists.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • LINEAR integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

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

@kjanat kjanat changed the title Suppress zsh NOMATCH errors in completion function fix: suppress zsh NOMATCH errors in completion function Apr 18, 2026
@kjanat kjanat self-assigned this Apr 18, 2026
@kjanat kjanat added the bug Something isn't working label Apr 18, 2026
@kjanat
kjanat marked this pull request as ready for review April 21, 2026 10:15
kjanat added 5 commits April 21, 2026 12:16
`emulate -L zsh` restored the default NOMATCH=on, which caused
`_files` internals and user zstyles (e.g. specs tagged `globbed-files`)
to leak `no matches found: *:globbed-files` errors into the user's
prompt whenever an unquoted glob in those specs failed to match.

Scope NO_NOMATCH to the completion function via the same `emulate -L`
call so those errors stay silent without affecting caller options.
Bump crate version to 0.4.1 and document the zsh completion NOMATCH
suppression fix in CHANGELOG.md under the new release section.
`emulate -L zsh -o NO_NOMATCH` silenced `no matches found`, but under
NO_NOMATCH an unmatched glob survives as a literal: `_files -/` expands
`*(/)` in a directory with no subdirectories, and that `*(/)` string
then leaks back as the completion candidate — the user ends up with
`runner --dir *(/)` typed into the prompt.

Switch to `NULL_GLOB`, which drops unmatched globs silently: no error,
no literal residue, and real matches still expand normally. Update the
registration-script test to assert the new option, and expand the
CHANGELOG entry to describe both symptoms the fix now covers.
`runner --dir <TAB>` in a directory with no subdirs was (a) typing a
literal `*(-/)` glob qualifier into the prompt and (b) spewing
`bad pattern: *(#q-/):globbed-files` on every TAB. Two stacked bugs
in the path-hint delegation:

- `setopt noglob` (added earlier to keep `*(*)` literal on the
  ExecutablePath call) disabled globbing function-wide, so zsh's own
  `_path_files` could not expand `*(-/)` via its internal
  `tmp1=( $~tmp1 )` — the pattern survived and reached `compadd` as
  a candidate. Swap the option for a `noglob` precommand modifier on
  the `_files` call itself: still blocks call-site expansion of
  `*(*)`, leaves `_path_files`'s internal globbing untouched so
  `NULL_GLOB` can drop the no-match case cleanly.
- `emulate -L zsh` resets to plain-zsh defaults with `EXTENDED_GLOB`
  off, but `_files` builds `*(#q-/)` qualifiers and uses `(#b)`
  backreferences throughout. Opt back in with `-o EXTENDED_GLOB` so
  its `eval` of the pattern spec parses instead of erroring.

Pin both with tests so the fragile interaction between the caller's
`emulate` scope, `noglob`, and `_files`'s internal globbing can't
silently regress again.
@kjanat
kjanat force-pushed the claude/fix-completion-glob-pattern-Pucc8 branch from b5f42d9 to 33d9338 Compare April 21, 2026 10:19
@kjanat
kjanat merged commit 5775756 into master Apr 21, 2026
6 of 7 checks passed
@kjanat
kjanat deleted the claude/fix-completion-glob-pattern-Pucc8 branch April 21, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant