Skip to content

Commit d820692

Browse files
authored
Improve the completion for attribute arguments (PowerShell#25129)
1 parent 650189e commit d820692

File tree

3 files changed

+89
-3
lines changed

3 files changed

+89
-3
lines changed

src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs

+58-2
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,19 @@ internal List<CompletionResult> GetResultHelper(CompletionContext completionCont
693693
return completions;
694694
}
695695
}
696+
else if (lastAst is VariableExpressionAst && lastAst.Parent is ParameterAst paramAst && paramAst.Attributes.Count > 0)
697+
{
698+
foreach (AttributeBaseAst attribute in paramAst.Attributes)
699+
{
700+
if (IsCursorWithinOrJustAfterExtent(_cursorPosition, attribute.Extent))
701+
{
702+
completionContext.ReplacementIndex = replacementIndex += tokenAtCursor.Text.Length;
703+
completionContext.ReplacementLength = replacementLength = 0;
704+
result = GetResultForAttributeArgument(completionContext, ref replacementIndex, ref replacementLength);
705+
break;
706+
}
707+
}
708+
}
696709
else
697710
{
698711
// Handle scenarios such as 'configuration foo { File ab { Attributes ='
@@ -928,6 +941,18 @@ internal List<CompletionResult> GetResultHelper(CompletionContext completionCont
928941
{
929942
result = GetResultForAttributeArgument(completionContext, ref replacementIndex, ref replacementLength);
930943
}
944+
945+
if (lastAst is VariableExpressionAst && lastAst.Parent is ParameterAst paramAst && paramAst.Attributes.Count > 0)
946+
{
947+
foreach (AttributeBaseAst attribute in paramAst.Attributes)
948+
{
949+
if (IsCursorWithinOrJustAfterExtent(_cursorPosition, attribute.Extent))
950+
{
951+
result = GetResultForAttributeArgument(completionContext, ref replacementIndex, ref replacementLength);
952+
break;
953+
}
954+
}
955+
}
931956
break;
932957

933958
case TokenKind.Ieq:
@@ -1003,6 +1028,21 @@ internal List<CompletionResult> GetResultHelper(CompletionContext completionCont
10031028
break;
10041029
}
10051030

1031+
if (lastAst is VariableExpressionAst && lastAst.Parent is ParameterAst paramAst && paramAst.Attributes.Count > 0)
1032+
{
1033+
foreach (AttributeBaseAst attribute in paramAst.Attributes)
1034+
{
1035+
if (IsCursorWithinOrJustAfterExtent(_cursorPosition, attribute.Extent))
1036+
{
1037+
completionContext.ReplacementLength = replacementLength = 0;
1038+
result = GetResultForAttributeArgument(completionContext, ref replacementIndex, ref replacementLength);
1039+
break;
1040+
}
1041+
}
1042+
1043+
break;
1044+
}
1045+
10061046
result = GetResultForEnumPropertyValueOfDSCResource(completionContext, string.Empty, ref replacementIndex, ref replacementLength, out _);
10071047
break;
10081048
}
@@ -2100,9 +2140,25 @@ private static List<CompletionResult> GetResultForIdentifier(CompletionContext c
21002140
}
21012141
}
21022142
}
2103-
if (completionContext.TokenAtCursor.TokenFlags == TokenFlags.MemberName && (lastAst is NamedAttributeArgumentAst || lastAst.Parent is NamedAttributeArgumentAst))
2143+
2144+
if (completionContext.TokenAtCursor.TokenFlags == TokenFlags.MemberName)
21042145
{
2105-
result = GetResultForAttributeArgument(completionContext, ref replacementIndex, ref replacementLength);
2146+
if (lastAst is NamedAttributeArgumentAst || lastAst.Parent is NamedAttributeArgumentAst)
2147+
{
2148+
result = GetResultForAttributeArgument(completionContext, ref replacementIndex, ref replacementLength);
2149+
}
2150+
else if (lastAst is VariableExpressionAst && lastAst.Parent is ParameterAst paramAst && paramAst.Attributes.Count > 0)
2151+
{
2152+
foreach (AttributeBaseAst attribute in paramAst.Attributes)
2153+
{
2154+
if (IsCursorWithinOrJustAfterExtent(completionContext.CursorPosition, attribute.Extent))
2155+
{
2156+
result = GetResultForAttributeArgument(completionContext, ref replacementIndex, ref replacementLength);
2157+
break;
2158+
}
2159+
}
2160+
}
2161+
21062162
if (result is not null)
21072163
{
21082164
return result;

src/System.Management.Automation/engine/parser/Parser.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7154,7 +7154,7 @@ private ExpressionAst UnaryExpressionRule(bool endNumberOnTernaryOpChars = false
71547154
ParserStrings.UnexpectedAttribute,
71557155
lastAttribute.TypeName.FullName);
71567156

7157-
return new ErrorExpressionAst(ExtentOf(token, lastAttribute));
7157+
return new ErrorExpressionAst(ExtentOf(token, lastAttribute), attributes);
71587158
}
71597159

71607160
expr = new AttributedExpressionAst(ExtentOf(lastAttribute, child), lastAttribute, child);

test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1

+30
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,36 @@ ConstructorTestClass(int i, bool b)
901901
$diffs | Should -BeNullOrEmpty
902902
}
903903

904+
It 'Should complete attribute argument in incomplete param block' {
905+
$res = TabExpansion2 -inputScript 'param([ValidatePattern('
906+
$Expected = ([ValidatePattern].GetProperties() | Where-Object {$_.CanWrite}).Name -join ','
907+
$res.CompletionMatches.CompletionText -join ',' | Should -BeExactly $Expected
908+
}
909+
910+
It 'Should complete attribute argument in incomplete param block on new line' {
911+
$TestString = @'
912+
param([ValidatePattern(
913+
^)])
914+
'@
915+
$CursorIndex = $TestString.IndexOf('^')
916+
$res = TabExpansion2 -cursorColumn $CursorIndex -inputScript $TestString.Remove($CursorIndex, 1)
917+
$Expected = ([ValidatePattern].GetProperties() | Where-Object {$_.CanWrite}).Name -join ','
918+
$res.CompletionMatches.CompletionText -join ',' | Should -BeExactly $Expected
919+
}
920+
921+
It 'Should complete attribute argument with partially written name in incomplete param block' {
922+
$TestString = 'param([ValidatePattern(op^)]'
923+
$CursorIndex = $TestString.IndexOf('^')
924+
$res = TabExpansion2 -cursorColumn $CursorIndex -inputScript $TestString.Remove($CursorIndex, 1)
925+
$res.CompletionMatches[0].CompletionText | Should -BeExactly 'Options'
926+
}
927+
928+
It 'Should complete attribute argument for incomplete standalone attribute' {
929+
$res = TabExpansion2 -inputScript '[ValidatePattern('
930+
$Expected = ([ValidatePattern].GetProperties() | Where-Object {$_.CanWrite}).Name -join ','
931+
$res.CompletionMatches.CompletionText -join ',' | Should -BeExactly $Expected
932+
}
933+
904934
It 'Should complete argument for second parameter' {
905935
$res = TabExpansion2 -inputScript 'Get-ChildItem -Path $HOME -ErrorAction '
906936
$res.CompletionMatches[0].CompletionText | Should -BeExactly Break

0 commit comments

Comments
 (0)