Enable completion of variables across ScriptBlock scopes (#19819)

This commit is contained in:
MartinGC94
2023-06-19 13:30:39 -07:00
committed by GitHub
parent b9b5136f0e
commit 0cbcf7b4d6
2 changed files with 53 additions and 5 deletions
@@ -5375,6 +5375,20 @@ namespace System.Management.Automation
"pv"
};
private static readonly HashSet<string> s_localScopeCommandNames = new(StringComparer.OrdinalIgnoreCase)
{
"Microsoft.PowerShell.Core\\ForEach-Object",
"ForEach-Object",
"foreach",
"%",
"Microsoft.PowerShell.Core\\Where-Object",
"Where-Object",
"where",
"?",
"BeforeAll",
"BeforeEach"
};
private sealed class VariableInfo
{
internal Type LastDeclaredConstraint;
@@ -5616,12 +5630,31 @@ namespace System.Management.Automation
public override AstVisitAction VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst)
{
return scriptBlockExpressionAst != Top ? AstVisitAction.SkipChildren : AstVisitAction.Continue;
}
if (scriptBlockExpressionAst == Top)
{
return AstVisitAction.Continue;
}
public override AstVisitAction VisitScriptBlock(ScriptBlockAst scriptBlockAst)
{
return scriptBlockAst != Top ? AstVisitAction.SkipChildren : AstVisitAction.Continue;
Ast parent = scriptBlockExpressionAst.Parent;
// This loop checks if the scriptblock is used as a command, or an argument for a command, eg: ForEach-Object -Process {$Var1 = "Hello"}, {Var2 = $true}
while (true)
{
if (parent is CommandAst cmdAst)
{
string cmdName = cmdAst.GetCommandName();
return s_localScopeCommandNames.Contains(cmdName)
|| (cmdAst.CommandElements[0] is ScriptBlockExpressionAst && cmdAst.InvocationOperator == TokenKind.Dot)
? AstVisitAction.Continue
: AstVisitAction.SkipChildren;
}
if (parent is not CommandExpressionAst and not PipelineAst and not StatementBlockAst and not ArrayExpressionAst and not ArrayLiteralAst)
{
return AstVisitAction.SkipChildren;
}
parent = parent.Parent;
}
}
}
@@ -274,6 +274,21 @@ switch ($x)
$completionText -join ' ' | Should -BeExactly 'Ascending Descending Expression'
}
It 'Should complete variable assigned in other scriptblock' {
$res = TabExpansion2 -inputScript 'ForEach-Object -Begin {$Test1 = "Hello"} -Process {$Test'
$res.CompletionMatches[0].CompletionText | Should -Be '$Test1'
}
It 'Should complete variable assigned in an array of scriptblocks' {
$res = TabExpansion2 -inputScript 'ForEach-Object -Process @({"Block1"},{$Test1="Hello"});$Test'
$res.CompletionMatches[0].CompletionText | Should -Be '$Test1'
}
It 'Should not complete variable assigned in an ampersand executed scriptblock' {
$res = TabExpansion2 -inputScript '& {$AmpeersandVarCompletionTest = "Hello"};$AmpeersandVarCompletionTes'
$res.CompletionMatches.Count | Should -Be 0
}
context TypeConstructionWithHashtable {
BeforeAll {
class RandomTestType {