[Console] Fix various completion edge cases
| Q | A |
|---|---|
| Branch? | 5.4 |
| Bug fix? | yes |
| New feature? | no |
| Deprecations? | no |
| Tickets | Fix #... |
| License | MIT |
| Doc PR | symfony/symfony-docs#... |
Refs https://github.com/composer/composer/issues/11029
There are two things I am trying to fix here:
composer require yiisoft/yii-[TAB]works, butcomposer req yiisoft/yii-[TAB]does not workcomposer upsuggetsupgrade updatebut both are aliases to the same command, so I fixed it to make it always just suggest one name, which lets the shell complete tocomposer updateand then I can tab again to get more relevant completions.
The fixes work almost.. I have one problem for which I need help from someone (maybe @wouterj?).
See the changes in CompleteCommand. The current code is doing smth like:
- try to find a command
- if none found, autocomplete command names
- if a command is found and the completion does not match the command name/aliases, suggest command name
- otherwise bind the command and forward completion to the command
This is problematic because it breaks the 1st case I mentioned above, and it also is just fairly dodgy if you are trying to complete an arg with the command's name as input (e.g. composer require require) it will kinda stay stuck on suggesting the command's name even tho you are completing another arg here.
So I am trying to change it to a flow like:
- try to find a command
- if a command is found check where the completion input/caret is.
- if the caret is at the end of the command name, complete that
- otherwise bind the command and then check what can be completed
But it seems like CompletionInput does not work correctly there.. see these few examples from the completion debug output after I added some extra logging:
$command = $this->findCommand($completionInput, $output);
$this->log(' START, completing command name ("'.$completionInput->getCompletionName().'/'.$completionInput->getCompletionType().'")');
if (null !== $command && !$completionInput->mustSuggestArgumentValuesFor('command')) {
$command->mergeApplicationDefinition();
$completionInput->bind($command->getDefinition());
$this->log(' BOUND, completing command name ("'.$completionInput->getCompletionName().'/'.$completionInput->getCompletionType().'")');
}
Input: ("|" indicates the cursor position)
c req|
Command:
/var/www/composer/bin/composer _complete -sbash -c1 -S2.4.999-dev+source -ic -ireq
Messages:
START, completing command name ("command/argument_value")
Input: ("|" indicates the cursor position)
c require |
Command:
/var/www/composer/bin/composer _complete -sbash -c2 -S2.4.999-dev+source -ic -irequire
Messages:
START, completing command name ("/none")
BOUND, completing command name ("packages/argument_value")
Input: ("|" indicates the cursor position)
c require foo|
Command:
/var/www/composer/bin/composer _complete -sbash -c2 -S2.4.999-dev+source -ic -irequire -ifoo
Messages:
START, completing command name ("command/argument_value")
As you can see when the input is c req| it completes the command argument as it should, then with c require | it also works as it should, detecting the space and moving to none completion. But then as soon as you have some more text it goes back to think it is completing the command argument and that makes no sense.
I am too dumb or tired to figure out what's going on in the CompletionInput class or its tests, so I'd appreciate input there :)
Hey!
I think @technojit has recently worked with this code. Maybe they can help review this?
Cheers!
Carsonbot
I am too dumb or tired to figure out what's going on in the CompletionInput class or its tests, so I'd appreciate input there :)
The CompletionInput is a monster, so you sure shouldn't feel dumb if you cannot follow it (it also took me an hour to relearn enough of the logic to find the issue).
In fact, you've found a bug that also exists on 5.4, I've submitted a fix: #47726 When that one is merged up, this one should work correctly.
I've played a lot with this PR locally in order to find this bug. The behavior looks very promising to me! Thanks for working on these edge-cases.