- Notifications
You must be signed in to change notification settings - Fork7.7k
Don't complete duplicate command names#21113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Don't complete duplicate command names#21113
Uh oh!
There was an error while loading.Please reload this page.
Conversation
… to add the module prefix if there's any ambiguity about which command will run.
This PR has Quantification details
Why proper sizing of changes mattersOptimal pull request sizes drive a better predictable PR flow as they strike a
What can I do to optimize my changes
How to interpret the change counts in git diff output
Was this comment helpful?👍 :ok_hand: :thumbsdown: (Email) |
This comment was marked as outdated.
This comment was marked as outdated.
This PR has Quantification details
Why proper sizing of changes mattersOptimal pull request sizes drive a better predictable PR flow as they strike a
What can I do to optimize my changes
How to interpret the change counts in git diff output
Was this comment helpful?👍 :ok_hand: :thumbsdown: (Email) |
Uh oh!
There was an error while loading.Please reload this page.
src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
results.Add(GetCommandNameCompletionResult(completionName, commandList[0], addAmpersandIfNecessary, quote)); | ||
_ = modulesWithCommand.Add(commandInfo.ModuleName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
What ifcommandInfo.ModuleName
is null or empty? Are we supposed to add to the set in that case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The Getter for ModuleName never returns null, it will be an empty string if there's no module. An empty string is fine because it's just a count of the places where the command can be found and if that's 2 or more it should be handled.
if ((commandInfo.CommandType != CommandTypes.Alias && commandInfo.CommandMetadata.CommandType is not null) | ||
|| (commandInfo.CommandType == CommandTypes.Alias && commandInfo.Definition is not null)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Can you please explain this check a little bit? I'm not sure if I understand what it's checking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
It's checking whether or not the source module of the command has been imported. I remember having some trouble finding a way to check this so if you know of a better and more obvious way to do the check then that would be great.
if (commandInfoArray[0].CommandType == CommandTypes.Application | ||
|| importedModules.Count == 1 | ||
|| moduleCount < 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Can you please explain this check too? EspeciallymoduleCount < 2
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
It's to check if the short name for the command can be used. If the first element is an application then we know there's no conflicting commands/aliases (because of the command precedence in PS).
If there's just 1 module imported then the short name refers to that module (and it will be the first element in the list) and of course if there's less than 2 unique modules exporting that command then we can use the short name because it can only refer to that module.
if (commandInfo.CommandType == CommandTypes.Application) | ||
{ | ||
endResults.Add(GetCommandNameCompletionResult(commandInfo.Definition, commandInfo, addAmpersandIfNecessary, quote)); | ||
results.Add(GetCommandNameCompletionResult(commandInfo.Definition, commandInfo, addAmpersandIfNecessary, quote)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Hmm, you are not usingendResults
anymore, meaning that you are not putting all the module-name-prefixed results at the end. Can you confirm if that expected and why? Also, if it's expected, you should remove theendResults
declaration above and the use of it below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Yes that's intentional. It seemed weird to move commands with the same name far apart from each other so the user has to tab through the entire list to find the command alternatives. For example if I want the VMWare version ofGet-VMHost
and I type inGet-VMH<Tab>
and end up with the Hyper-V version, why shouldn't the next result then be the VMWare version?
Co-authored-by: Dongbo Wang <dongbow@microsoft.com>
Add comments.Remove unused list.
If PSRL remove the duplicates itself why do we need remove them in Engine? I guess it could be useful sometimes to see all options. |
PowerShell is also used in editors where the editors may or may not remove duplicates. The PSReadLine duplicate removal logic was flawed and was removed here:PowerShell/PSReadLine#3897 though in the latest PowerShell preview it still seems to deduplicate... Anyway, regardless of what PSReadLine does, the duplicate results aren't useful because the completion text is exactly the same as you can see in the example in the PR description. Module qualified commands will still be shown as needed. |
@daxian-dbw@andyleejordan I'd want to get a confirmation that it is a right change. |
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
{ | ||
var commandInfo = commandList[0] as CommandInfo; | ||
if (commandInfo != null && !string.IsNullOrEmpty(commandInfo.Prefix)) | ||
var commandInfo = (CommandInfo)commandList[i]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
It seems there can bestring
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
In what scenario though?MakeCommandsUnique
is only called in one place using thecommandInfos
received from runningGet-Command
with some predefined parameters. These parameters always result in a CommandInfo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I don't know a scenario but I see line 308 and then line 315.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I looked at the code forGet-Command
. It seems the only scenario where it outputs strings is if you use theSyntax
parameter, which we don't do here. So the string handling is either becauseGet-Command
used to have some other scenario where it would output strings, or a mistake/misunderstanding in the original completion implementation. Either way there's no reason for the new code to try and handle strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Sorry, I don't think about handling the strings. I have concern about throwing. So suggestion is to useas
and check for not null. Since we ignore all exceptions in the file it is more reliable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
But if there's no scenario where it would be a string then there's no way it would ever throw? I mean you wouldn't suggest to add a null check for:var x = 1; if (x == 1) {}
because there's no scenario where it can happen, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The important thing is that the code abovecan create a string and we will get an exception. The fact that this method is used so that there can be no strings does not guarantee that this will not change in the future.Every change must be reliable. If you clean the code above later, you can remove this check. But it should be reliable now.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
6661f6f
intoPowerShell:masterUh oh!
There was an error while loading.Please reload this page.
microsoft-github-policy-servicebot commentedMar 4, 2025 • edited by unfurl-linksbot
Loading Uh oh!
There was an error while loading.Please reload this page.
edited by unfurl-linksbot
Uh oh!
There was an error while loading.Please reload this page.
📣 Hey@MartinGC94, how did we do? We would love to hear your feedback with the link below! 🗣️ 🔗https://aka.ms/PSRepoFeedback |
Uh oh!
There was an error while loading.Please reload this page.
PR Summary
Update the duplicate command name check to also take the module name into account when deciding whether or not to add a command with the module name as a prefix.
Before:
After:
PR Context
Fixes#6765
Get-Command -All
returns duplicate commands if multiple versions of the same module exist, or if 2 different modules include the same command name so the tab completion needs to handle this scenario.PR Checklist
.h
,.cpp
,.cs
,.ps1
and.psm1
files have the correct copyright headerWIP:
or[ WIP ]
to the beginning of the title (theWIP
bot will keep its status check atPending
while the prefix is present) and remove the prefix when the PR is ready.(which runs in a different PS Host).