Skip to content

fix: request completions with "__complete" so a double dash can't run the command - #2397

Draft
suzuki-shunsuke wants to merge 38 commits into
mainfrom
fix/completion-request-marker
Draft

fix: request completions with "__complete" so a double dash can't run the command#2397
suzuki-shunsuke wants to merge 38 commits into
mainfrom
fix/completion-request-marker

Conversation

@suzuki-shunsuke

@suzuki-shunsuke suzuki-shunsuke commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

What type of PR is this?

  • bug

What this PR does / why we need it:

A completion request appended to the end of the command line cannot be told apart from a positional argument that happens to look like one, and after -- that is exactly what it is. Both readings are required, and they contradict each other:

$ aqua exec -- foo --generate-shell-completion   # pass it on to foo (#1932)
$ aqua exec -- foo <TAB>                         # complete, run nothing (#1993)

The shell sends the same argv either way, so no reading of that argv can serve both. #1932 was fixed by choosing the first reading, which is why #1993 is still open: on a command line holding --, pressing tab runs the command. With today's main (v3.10.1-10-gc6f4cf7e), typing app exec -- git push origin main<TAB> runs git push origin.

This PR moves the request to where -- cannot reach it — the first argument:

<cmd> __complete <word>... <word being completed>
  • completion.go: adds completionCommandRequest (__complete) and marks completionFlag deprecated.
  • help.go: checkShellCompleteFlag becomes parseShellCompleteRequest. It understands both forms and records, on the root command, the word being completed and whether a -- precedes it. Every run replaces that record, so a Command answering several requests carries nothing from one into the next. An app declaring a command named __complete keeps it, and stops being completable in exchange.
  • help.go: nothing is suggested past a --, since those words are positional arguments of whatever the command runs. The rule lives in runCompletion rather than in DefaultCompleteWithFlags, so a command carrying a ShellComplete of its own keeps it without having to know about a -- it cannot see. Suggesting nothing is not the same as declining the request: the run is still a completion, so the action never executes. Where the request names the word being completed, that word is used instead of guessing it from the position of the arguments.
  • help.go: the deprecated form no longer strips the flag off a command line holding --. Leaving it in place is what Shell completion works wrongly even if double dash -- are included in arguments #1932 asked for, and fix: show flag completions when completing a double-dash prefix #2316 regressed it by returning arguments[:pos] while declining the request.
  • command.go, command_run.go: the root command holds one *completionRequest, replaced by every run, and the call site. A Before is not run for a request past a -- either: there is nothing to prepare for a completion that is not going to happen.
  • autocomplete/*: all four scripts send the new request, including the word under the cursor, empty or not. That word was guesswork until now, because the scripts sent it only when it started with -, which is why cmd --<TAB> and cmd -- <TAB> arrived as the same request.
  • autocomplete/bash_autocomplete: builds the request as an array and runs it directly instead of evaling a joined string, so a word holding a space reaches the command as the single word it is, and a line holding a $(...) is no longer executed by pressing the tab key. The words come from the words/cword that _init_completion reassembles rather than from COMP_WORDS, which splits --opt=value into three on COMP_WORDBREAKS while the candidates are filtered against the whole of it. A command typed as ~/bin/app is still run as the path it stands for: eval expanded that as a side effect, and it is the one expansion worth keeping, so it is put back without evaluating anything.
  • autocomplete/{bash,zsh,fish}: a command typed as ~/bin/app is run as the path it stands for. eval expanded it as a side effect of re-parsing the line; all three dispatch the completion for such a line and then fail to run the command without it. PowerShell resolves the tilde when it looks the command up, so its script does nothing there.
  • autocomplete/*: one level of quoting comes off each word, the way the shell takes it off before handing a word to a command. eval and Invoke-Expression used to do that as a side effect of re-parsing the line; dropping them dropped it, and app sub "hello world" asked the app about a word holding the two quote characters. A word whose quote is still open is sent without the opening quote, which all four shells now agree on.
  • autocomplete/powershell_autocomplete.ps1: uses the real -Native completer signature. The old parameter list was shifted by one ($commandName was the word being completed and $wordToComplete was the CommandAst), which worked only because interpolating the AST yields the command line. The words now come from CommandAst.CommandElements, and which of them is being completed comes from the cursor rather than from a comparison with $wordToComplete, which PowerShell hands over normalized: an unfinished "hello arrives as "hello", matches no element as written, and would be sent twice. Reading the cursor also leaves out what follows it, so completing in the middle of a line no longer asks about the words after the cursor.

Scripts generated before this change keep working, with the ambiguity they cannot escape: a command line holding -- is answered as an ordinary run. Regenerating the script and sourcing it again is what closes #1993 for a given app.

Answering a completion no longer evaluates any part of the command line: eval and Invoke-Expression re-parsed it, so a $(...) typed on the line ran on the tab key. That is the same shape of problem as #1993, and it goes away with them.

No public API changes; go run scripts/build.go v3diff is clean.

Which issue(s) this PR fixes:

Fixes #1993

Special notes for your reviewer:

  • Most of this branch — the code, the tests, the docs and this description — was written by Claude Code (Claude Opus 5), with me driving and reviewing it. I have read every line and take responsibility for it. Nothing here about shell behavior is assumed: the COMP_WORDS values, what each shell hands its completion function and what the command ends up receiving were all measured, and the review the branch went through is what turned up the differences the tests now cover.
  • The deprecated form keeps today's behavior on a command line holding --, which means it still runs the command there. That is not an oversight: it is the Shell completion works wrongly even if double dash -- are included in arguments #1932 reading, and it is the only reading available without a signal the shell alone can send. The one change is that the flag is no longer swallowed, which also makes an accidental run more likely to fail loudly than to quietly do the real thing.
  • Existing ShellComplete functions see an unchanged cmd.Args(). The word being completed is kept in the arguments when it starts with - and left out otherwise, which is the shape the old scripts produced. Functions that branch on whether the last argument starts with - therefore behave identically under both forms. The word itself is recorded separately, unexported for now; exposing it would let a ShellComplete function tell -c <TAB> from -c<TAB>, which seems worth a follow-up rather than this PR.
  • fix completion of "--" #2205 touches the same code. It is a different approach to the same -- handling and has been conflicting since 2025-11; I am happy to close this in favor of a rebase of that one if you prefer, though it does not address the request being imitable in the first place.
  • Follow-ups this makes possible, deliberately left out: Auto-completion with unfinished partial flags (--XXX<TAB> returns no completions #2248 (--pa<TAB> returns nothing), and the args[argsLen-2] indexing in DefaultCompleteWithFlags, which is only correct for the root command and so misses flag suggestions after a positional argument on a subcommand. A request naming the word being completed does not go through that indexing at all, so both are now questions about the deprecated form alone.
  • Nothing is suggested past a -- for any command now, where before it was what the default completion func happened to do. That makes it a guarantee of the library rather than a default, and it closes the door on a wrapper handing its completions to the command it runs (myapp exec -- git pu<TAB> offering what git would offer). No released version has done that — before fix: show flag completions when completing a double-dash prefix #2316 the wrapper answered with its own completions, and since then it has run with the flag stripped off — but the deprecated form does now that it passes the flag on, so a script regenerated for this PR loses something a script for this PR would have had. That is deliberate: delegation only works by running the wrapper, which is the tab key running a command, which is what Autocomplete after double dash (--) executing command action.  #1993 is about. The docs now say so.
  • Still open, and older than this PR: the bash script depends on bash-completion for its word splitting, and its fallback calls another bash-completion function, so on a machine without it the completion returns quietly rather than saying why.
  • The CI job installs what ubuntu-24.04 has, which is bash 5.2 and fish 3.7; the shells driven while writing this were bash 5.3, zsh 5.9, fish 4.8.1 and pwsh 7.6.4. The macOS job has no bash-completion and no fish, so it skips those, and CLI_SHELL_TESTS_REQUIRED is set only on ubuntu.
  • Windows PowerShell 5.1 drops an empty argument on the way to a native command, which is how the word being completed is sent when the cursor sits on a fresh word. PowerShell 7.0 to 7.2 do the same on Windows unless $PSNativeCommandArgumentPassing is Standard, which the script now asks for; 5.1 has no such setting and is documented instead.
  • Still open, and not made worse here: the cur that bash filters the candidates against still holds the quoting, so a quoted word is asked about correctly and then filtered against "hello wo, which matches nothing. Taking the quotes off cur means quoting the candidates back before they are inserted, which is a change of its own.

Testing

go test ./..., golangci-lint run, go run scripts/build.go v3diff and gfmrun all pass.

completion_shell_test.go runs the generated scripts in the shells they are written for and checks the request each one builds, with the command it asks recording the arguments it receives. Asserting on the text of a script only says that a line was written, not what the shell does with it, and every difference found while reviewing this branch lived in that gap: a word bash splits on COMP_WORDBREAKS, a word arriving with its quotes, a word PowerShell hands over normalized and so sent twice, a command substitution the old scripts executed.

The words bash puts in COMP_WORDS are written out per case as measured in bash 5.3 with a completion function that dumps them, rather than worked out in the test: a driver that splits the line itself checks its own idea of what bash does, and one that lets eval do it takes the quotes off, joins --opt=va back together and runs the command substitution, which is three of the cases gone.

TestCompletionScriptsSyntax renders each script for an app named my-app and my.app and asks the shell to parse it. A shell function name may hold a -; a variable name may not, so a script putting the app name in a variable breaks for those apps and breaks the whole file, since sourcing stops at the syntax error before anything is registered.

The zsh run is the one assumption left: driving its completion system needs a pseudo terminal, so words and CURRENT are filled with zsh's own tokenizer instead. bash gets the words as measured, and fish and PowerShell go through the entry point the shell itself uses.

A shell that is not installed skips, and bash additionally skips without bash-completion, whose word splitting the script depends on. Because a skip is silent, the ubuntu CI job installs bash-completion, zsh and fish and sets CLI_SHELL_TESTS_REQUIRED=bash,zsh,fish, which turns "not installed" into a failure for exactly those. pwsh comes with the runner image and is left optional, so a change of image cannot fail the job over a shell nobody asked it to cover.

Other new Go tests:

  • Test_parseShellCompleteRequest: both request forms, including the word being completed, the terminator, an app owning a __complete command, and the Shell completion works wrongly even if double dash -- are included in arguments #1932 pass-through.
  • TestCompletionRequestNeverRunsAction: an action that records having run, asserted not to, for a request with and without a --. This is the Autocomplete after double dash (--) executing command action.  #1993 regression test.
  • TestCompletionRequestAfterDoubleDash: no suggestion past a --, while a -- being completed still gets flags.
  • TestCompletionDeprecatedRequestPassedOnAfterDoubleDash: the wrapper receives the flag instead of the wrapper answering with its own completions.
  • TestCompletionRequestKeepsArgsShape: a ShellComplete function sees the same cmd.Args() under both forms.
  • TestCompletionRequestStateIsPerRun: a Command answering several requests answers each on its own terms.
  • TestCompletionCustomShellCompleteNotRunPastDoubleDash: a command carrying its own completion func suggests nothing past a --.
  • TestCompletionRequestIgnoredWhenDisabled: an app that has not enabled shell completion receives __complete as the positional argument it is.
  • TestCompletionRequestNestedSubcommand: a request is answered by the command it names, however deep, including a flag being completed after a positional argument.
  • TestCompletionBeforeNotRunPastDoubleDash: a Before is not run for a request past a --, and still is for one the command answers.
  • Per-shell assertions that the generated scripts send the new request.

The scripts were also driven in the real shells (bash 5.3, zsh 5.9, fish 4.8.1, pwsh 7.6.4) against a test app whose exec action writes a sentinel file. In every shell, app <TAB>, app g<TAB>, app -<TAB> and app get --<TAB> produce the expected candidates, and app exec -- echo hi<TAB> produces none and leaves the sentinel unwritten. With a script generated before this change, the same app still completes exactly as it does today.

Finally, an app with a custom ShellComplete (ghtkn) was built against this branch: its completions are unchanged, and ghtkn exec -- git push origin main<TAB> no longer runs git push origin.

Release Notes

Shell completion scripts now ask for completions with a `__complete` first argument instead of a trailing `--generate-shell-completion` flag, so pressing tab on a command line containing `--` no longer runs the command. They no longer re-parse the command line to build that request either, so a `$(...)` on it is no longer executed by pressing the tab key. Regenerate the completion script and source it again after upgrading: scripts generated by earlier versions keep working, but on such a line they run the app, since after a `--` the request is a positional argument that reaches the action along with the rest of the line. Enabling shell completion reserves `__complete` as an app's first argument.

A completion request appended to the end of the command line cannot be told
apart from a positional argument that looks like one, and after "--" that is
exactly what it is. The two readings are both required:

  $ aqua exec -- foo --generate-shell-completion   # pass it on to foo (#1932)
  $ aqua exec -- foo <TAB>                         # complete, run nothing (#1993)

The shell sends the same argv either way, so no reading of it can serve both,
and #1932 was fixed at the cost of #1993: a command line holding "--" runs
instead of completing. Pressing tab on 'app exec -- git push origin main'
runs 'git push origin' today.

The request is now named in the first argument, where "--" cannot reach it:

  <cmd> __complete <word>... <word being completed>

Everything the shell has typed follows, and the word under the cursor comes
last, empty or not. That word was guesswork until now, since the scripts sent
it only when it started with "-", which is why "cmd --<TAB>" and
"cmd -- <TAB>" arrived as the same request. The word being completed after a
"--" gets no suggestion, because it is a positional argument of whatever the
command runs, but the request is still a completion, so nothing is run.

The deprecated form keeps working for scripts generated before this change,
with the ambiguity it cannot escape: a command line holding "--" is answered
as an ordinary run. It no longer swallows the flag while doing so, which is
what #1932 asked for and what #2316 regressed by stripping it. Regenerating
the script and sourcing it again is what closes #1993 for good.

The request form shadows nothing: an app with a command of that name is left
to answer for itself.

Fixes #1993
The word being completed and the "--" before it were recorded on the root
command by the request that carried them, and by nothing else, so a Command
answering a second request still held what the first one said. A shell runs
one request per process and never sees it; a test, a REPL or an embedded use
answers several through one Command and does: a request past a "--" made
every later request answer as though it were past one too.

The two fields become one, which every run replaces, so there is one place
that has to be right rather than two that have to agree.
The rule that the words after a "--" belong to whatever the command runs was
kept by DefaultCompleteWithFlags alone, so a command carrying a ShellComplete
of its own went on suggesting past the terminator. That left the rule as
something every app had to reimplement, from a "--" it cannot see: the parsed
arguments no longer say where the terminator was.

Answering with nothing before the completion func runs applies it to every
command instead. The action still does not run, which is what a shell asking
for completions needs either way.
Bash splits the command line on COMP_WORDBREAKS, which holds "=" and ":", so
COMP_WORDS turns "--opt=value" into three words. __init_completion is called
with -n "=:" precisely to put those back together, and the candidates are
filtered against the cur it returns, but the request was built from the raw
COMP_WORDS: pressing tab on "app --opt=va" asked the app about "--opt", "="
and "va" as three arguments while filtering the answer against "--opt=va".
zsh, which does not split there, sent one word for the same line.

The words also arrive quoted, as typed: "app run \"hello world\" " asked the
app about a word holding the two quote characters. eval used to take those
off as a side effect of re-parsing the line, and dropping eval dropped that
too. They are now taken off by walking the word, so a line holding $(...) is
still not executed by pressing the tab key.
The words arrive as typed, so "app run \"hello world\" " asked the app about
a word holding the two quote characters. (Q) takes one level of quoting off
each of them, which is what the shell would do before handing a word to a
command.

The command's stderr went to the terminal, where it lands in the middle of
the prompt: the other three scripts redirect it, and this one now does too.
The word under the cursor was found by comparing each element of the command
line with $wordToComplete, which PowerShell hands over normalized: an
unfinished "hello arrives as "hello", matches no element as written, and was
therefore sent twice, once as a word of its own and once as the word being
completed. The command was asked about a command line one argument longer
than the one on screen, which is the opposite of what naming the word is for.

The cursor says which element it is, and reading it also leaves out whatever
follows: completing in the middle of a line asked about the words after the
cursor too.

The words come from the parsed element rather than from its source text, so
one level of quoting comes off, the way the shell would take it off before
handing a word to a command. Nothing is expanded while doing so: the value of
a $(...) or a $env:VAR is no longer worked out to answer a completion, which
Invoke-Expression used to do by re-parsing the whole line.
A word whose quote is still open, as in 'app run "hello wo', has no closing
quote for (Q) to take off with it, so zsh sent the opening quote along with
the word. bash and PowerShell send "hello wo" without it for the same line,
and a command filtering candidates by what has been typed finds nothing for a
word that begins with a quote character.
The script tests asserted on the text of the generated script, which says
that a line was written, not what the shell does with it. Every difference
found while reviewing this branch lived in that gap: a word bash splits on
COMP_WORDBREAKS, a word arriving with its quotes, a word PowerShell hands
over normalized and so sent twice.

Each script is now sourced in its shell and asked to complete a command line,
with the command it asks recording the arguments it receives. A shell that is
not installed skips, so this costs nothing on a machine without it, and -short
skips all of them.

It found one more difference while being written: fish sent the word being
completed with the quote the user had opened, where the other three take it
off.
Tests in this package add flags of their own to the standard flag set, so
testing.Short finds it unparsed and panics. It passed under -run because
nothing had touched the flag set by then.
Setting EnableShellCompletion takes the first argument for a completion
request, which an app taking free-form positional arguments no longer
receives. Only an app declaring a command of that name was said to be
affected, which is the guard, not the reservation.

A script generated before this change also behaves differently on a command
line holding a "--": the request stays on the line as a positional argument,
so the run it starts usually ends in an Incorrect Usage message.
Nothing said that an app which has not enabled shell completion receives
"__complete" as the positional argument it is, which is the other half of
reserving it.

Nothing reached past one level of subcommands either. The last case there is
the one the arguments alone cannot answer: a flag being completed after a
positional argument, where the deprecated request form looks one word too far
back and suggests commands instead.
The driver built COMP_WORDS with eval, which is not what bash does with a
command line and got all three of the cases that matter wrong. eval took the
quotes off before the script saw them, so the case named "a quoted word" never
reached the script's dequoting and would pass with it deleted. eval joined
"--opt=va" into one word, so the reassembly the case is named after was never
exercised. And eval ran the command substitution the case is there to prove is
not run, creating the file the assertion looks for: on a machine with
bash-completion, that case failed.

The words are now written out as bash produces them, measured with a
completion function that dumps COMP_WORDS, so what the driver feeds the script
is what bash would.
Nothing installed bash-completion, zsh or fish, so the tests that run the
generated scripts skipped, and a skip reads the same as a pass. The ubuntu job
installs them and sets CLI_SHELL_TESTS_REQUIRED, which turns "this shell is
not here" into a failure, so the coverage cannot go away without anyone
noticing. Elsewhere the variable is unset and a missing shell still skips.
Every word but the first went through the dequoting, so a command whose path
holds a space, and is therefore typed quoted, was run under a name with the
quote characters in it: nothing ran and the completion returned empty. bash
takes them off words[0] the same way.
The completion func stopped being run past the terminator, but the Before
chain in front of it did not: a Before with a side effect fired on every tab
key there, to produce an answer that is always empty. There is nothing to
prepare for a completion that is not going to happen.
Not running the completion func past the terminator makes "no suggestion
there" a guarantee of the library rather than what the default func happened
to do, and it means a command wrapping another cannot hand its completions on.
Neither was written down.
CLI_SHELL_TESTS_REQUIRED turned a missing shell into a failure, whatever it
was, and the ubuntu job installs three of the four: pwsh comes with the runner
image, so a change of image would have failed the job over a shell nobody had
asked it to cover. It now names the shells it requires.
…e required names

Two holes in the harness, both of which report something other than what is
true.

The bash driver looked for a bash-completion file and assumed that finding one
meant it could be used. bash-completion 2.12 and later need bash 4.2, so on
macOS, where /bin/bash is 3.2 and the file usually comes from Homebrew,
sourcing it fails, the word-splitting helpers are never defined, and the
script returns without asking anything. What the contributor saw was a syntax
error from inside bash-completion and a failing assertion, for a change that
has nothing to do with either. It now asks that bash whether the helpers end
up defined, and skips saying so when they do not.

CLI_SHELL_TESTS_REQUIRED matched names against the shells driven here and
ignored anything else, so a typo in it required nothing at all and the CI
guarantee it exists to provide would have gone away quietly. Names that match
no driver now fail.
Skipping inside the loop ended the search at the first file that was there
but unusable, so a machine holding both a bash-completion its bash cannot
source and an older one it can would have skipped over the one that works.
The unusable ones are collected instead, and named in the skip when none of
them turned out to be usable.
eval expanded the tilde as a side effect of re-parsing the command line, and
dropping eval dropped that too: the completion looked for a command whose name
begins with a tilde, found none, and offered nothing. bash still calls the
function, since it falls back to the compspec for the last path component.

The tilde is put back on its own rather than by evaluating anything, so a
"$(...)" on the line is still not run: it is the one expansion worth having,
and the raw word decides, because a quoted "~" is not a home directory to the
shell either.
The note said that pressing tab on a command line holding a "--" usually ends
in an Incorrect Usage message. It does not: after a "--" the request is a
positional argument, so it reaches the action along with the rest of the line
and the run completes as though enter had been pressed. Saying that it usually
errors reads as though the danger were mostly theoretical, when it is the one
#1993 is about.

The Customization heading no longer had any customization under it, since what
it introduced is now the request form, which an app cannot rename: the script
and the app have to agree on the name.

Two comments were claiming more than they check: what "--" means when a flag
takes it as its value, which is not knowable before the flags are parsed, and
what the zsh driver fills words and CURRENT with, which is zsh's tokenizer
standing in for a completion system that needs a terminal to drive.
The assertion that the first word of the request is the dequoted one went away
with the tilde fix, and nothing else looked at it: every case typed the command
as a bare "app", so a script sending the quotes along with the name would have
passed. A case types it as 'app' instead, which all four shells answer.

PowerShell needs the call operator to read a quoted word as a command at all,
so that shell gets the same line written the way it would be typed there.
The exported GenerateShellCompletionFlag is referenced by nothing but a test:
the request was found by a hardcoded name then, as it is now, so replacing it
never changed what was detected. The old wording said it "may be redefined if
desired", and the rewrite carried that premise over as a contrast.

The note about what happens on an old script also said the run completes as
though enter had been pressed, which holds unless the command declares how
many arguments it takes, in which case the extra one is rejected.
Declaring how many arguments a command takes was named as the case where the
flag left on the line by an old script is rejected. It is not one: an argument
too many is not an error, so the run completes with the extra one in
cmd.Args(). Reading that there is a shape of app the danger does not reach is
worse than reading nothing, since the shape is the one many apps have.
The quoted command word was written into bashWords as though bash had been
measured producing it. It cannot: a command word holding a quote matches no
compspec, so bash completes it as a file name and never calls the function.
Measured with a completion function that records being called, 'app' su and
"app" su are not dispatched, while ~/bin/app su is, which is what the tilde
case relies on.

Leaving the field nil skips that shell, and says why in the case, so the
comment promising measured values stays true of every value there.
Skipping bash when a case has no COMP_WORDS made "left out on purpose" and
"forgot to measure them" the same thing. A case that forgot them used to fail
loudly, on a bad array subscript; after that change it quietly covered one
shell fewer, which is what CLI_SHELL_TESTS_REQUIRED exists to prevent a level
up.

The reason is a field now, so the skip has to be asked for, and a case with
neither the words nor a reason fails as it did before.
The paragraph said that only the app's own action can turn the flag down. A
typed argument list turns it down first: an IntArgs takes the flag as a number
and reports that it is not one. Since neither the count nor the type nor the
action can be relied on to stop it, the paragraph now says that much and no
more.
It opened with "nothing rejects the request" and closed with a typed argument
list rejecting it. The first sentence was about the flag alone until the
qualifier came off while removing an exception that did not exist, and the
exception that does exist came back to a sentence that no longer allowed for
one. The flag is what the first sentence is about again, and what happens
after it reaches the action is the app's to decide.
CLI_SHELL_TESTS_REQUIRED is read in this file and set in the workflow, neither
of which is "one directory up" from here. What the comment is comparing is
what each one covers: a shell against a case.
A function name may hold a "-" or a "."; a variable name may not. Putting the
app name in the two new variables therefore broke every app named like
docker-compose or golangci-lint, and broke it completely: sourcing stops at
the syntax error, before any function is defined or any completion registered.
main puts the name in function names only, so this was a regression.

The variables go back to fixed names and are declared local by the entry
point, which is what keeps two apps from sharing them — the reason the name
was put there in the first place. The two arrays that were already global get
the same treatment.

A syntax check over the generated scripts covers the names that broke it: a
test app has always been called "app" here, so nothing looked at a name a
shell reads specially.
The linter wants the multi-line argument list wrapped.
The tilde was put back for bash, where dropping eval had taken it away, and
left alone everywhere else. All three of the others dispatch the completion
for such a line and then fail to run the command: measured for zsh and fish
by driving them, and for PowerShell by the review, where Invoke-Expression
used to expand it as a side effect of re-parsing the line.

zsh and fish are checked by driving them with HOME pointed at the app. The
PowerShell line is not: pwsh on this machine crashes before running anything
at the moment, so that one shell is reasoned about rather than measured.
fish answers with nothing when string unescape cannot read a word, such as
one ending in a lone backslash, so the request arrived with an empty word and
the command answered as though a fresh one had been started. The word as
typed is a better answer than no word; the other three pass a word they
cannot take apart through unchanged.

PowerShell drops an empty argument on the way to a native command unless the
argument passing mode is the one 7.3 made the default, which turns the same
line into the same silent misreading. The script asks for that mode where the
variable exists. Windows PowerShell 5.1 has none, and is documented instead,
together with the "--" a flag takes as its value, which no shell can answer.
The assertions still named the variables after the app, which is what broke
for an app named with a "-", and one of them is now what says that the name
stays out of them.
Marking an unexported constant deprecated tells no user and no linter
anything. What is deprecated is the request form the scripts send, which is
what the comment describes now.
The tilde was put back in all four scripts on the strength of a measurement
that does not show what it looked like: "& '~/no-such'" reports the name it
was given, tilde and all, because there is no such command, not because the
tilde went unresolved. With a command that exists, PowerShell finds
"~/bin/app" on its own.

Measured by taking the block out again and completing "~/bin/app sub ": the
command still runs. bash, zsh and fish all fail the same test without their
own handling, which is why they keep it.
The tilde was put back for zsh and fish with nothing but a hand-run to say
so, right after a blocker that came from a test never seeing an app name a
shell reads specially. The bash-only test becomes a loop over the four, which
the drivers already express: the command sits under a moved HOME and nowhere
on PATH, so a shell that passes the word on as written finds nothing and the
request never arrives. PowerShell is in it as the other kind of check, that
its script needs to do nothing for the same line.

A shell that cannot start with HOME moved skips, saying so: a machine can get
its shells from a tool manager keeping what it needs under the real home.
The probe skipped through t.Skip, which walks past the one thing this file
has for a silent skip: a shell named in CLI_SHELL_TESTS_REQUIRED is supposed
to fail rather than disappear. Named or not, every shell went quiet and the
run said ok.

Measured by making the probe fail on purpose: without the variable all four
skip, and with bash,zsh,fish required the run fails.
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.

Autocomplete after double dash (--) executing command action.

1 participant