Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -1993,16 +1993,27 @@ private static void NativeCommandArgumentCompletion(
CompletionContext context,
Dictionary<string, AstParameterArgumentPair> boundArguments = null)
{
if (string.IsNullOrEmpty(commandName))
string parameterName = parameter.Name;

// Fall back to the commandAst command name if a command name is not found. This can be caused by a script block or AST with the matching function definition being passed to CompleteInput
// This allows for editors and other tools using CompleteInput with Script/AST definations to get values from RegisteredArgumentCompleters to better match the console experience.
// See issue https://github.com/PowerShell/PowerShell/issues/10567
string actualCommandName = string.IsNullOrEmpty(commandName)
? commandAst.GetCommandName()
: commandName;

if (string.IsNullOrEmpty(actualCommandName))
{
return;
}

var parameterName = parameter.Name;
var customCompleter = GetCustomArgumentCompleter(
"CustomArgumentCompleters",
new[] { commandName + ":" + parameterName, parameterName },
context);
string parameterFullName = $"{actualCommandName}:{parameterName}";

ScriptBlock customCompleter = GetCustomArgumentCompleter(
"CustomArgumentCompleters",
new[] { parameterFullName, parameterName },
context);

if (customCompleter != null)
{
if (InvokeScriptArgumentCompleter(
Expand Down
26 changes: 26 additions & 0 deletions test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,32 @@ Describe "TabCompletion" -Tags CI {
$res.CompletionMatches[1].CompletionText | Should -BeExactly 'dog'
}

It "Tab completion for ArgumentCompleter when AST is passed to CompleteInput" {
$scriptBl = {
function Test-Completion {
param (
[String]$TestVal
)
}
[scriptblock]$completer = {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

@('Val1', 'Val2')
}
Register-ArgumentCompleter -CommandName Test-Completion -ParameterName TestVal -ScriptBlock $completer
}
$pwsh = [PowerShell]::Create()
$pwsh.AddScript($scriptBl)
$pwsh.Invoke()
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't seem right. After invoking, Test-Completion would already been defined, and it's not the scenario you are aiming at -- AST or Script has matching function definition.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My understanding of this is as follows:

The script block is executed to define the Test-Completion function and argument completer in the runspace.(As the runspace should provide the registered argument completers when in an editor).

From there, we are using the $script variable for completion input(Similar to how VSCode utilizes CompleteInput) as the issue arises when the function is defined in the $script block being passed to CompleteInput


$completeInput_Input = $scriptBl.ToString()
$completeInput_Input += "`nTest-Completion -TestVal "
$res = [System.Management.Automation.CommandCompletion]::CompleteInput($completeInput_Input, $completeInput_Input.Length, $null, $pwsh)
$res.CompletionMatches | Should -HaveCount 2
$res.CompletionMatches[0].CompletionText | Should -BeExactly 'Val1'
$res.CompletionMatches[1].CompletionText | Should -BeExactly 'Val2'
}

It "Tab completion for enum type parameter of a custom function" {
function baz ([consolecolor]$name, [ValidateSet('cat','dog')]$p){}
$inputStr = "baz -name "
Expand Down