Add completion for Using keywords (#16514)

This commit is contained in:
MartinGC94
2022-12-20 13:26:43 +05:00
committed by GitHub
parent bbb03f350b
commit 9a7db33ee0
3 changed files with 133 additions and 2 deletions
@@ -423,6 +423,12 @@ namespace System.Management.Automation
case TokenKind.Identifier:
if (!tokenAtCursor.TokenFlags.HasFlag(TokenFlags.TypeName))
{
result = CompleteUsingKeywords(completionContext.CursorPosition.Offset, _tokens, ref replacementIndex, ref replacementLength);
if (result is not null)
{
return result;
}
result = GetResultForIdentifier(completionContext, ref replacementIndex, ref replacementLength, isQuotedString);
}
@@ -774,6 +780,12 @@ namespace System.Management.Automation
}
break;
default:
result = CompleteUsingKeywords(completionContext.CursorPosition.Offset, _tokens, ref replacementIndex, ref replacementLength);
if (result is not null)
{
return result;
}
if ((tokenAtCursor.TokenFlags & TokenFlags.Keyword) != 0)
{
completionContext.WordToComplete = tokenAtCursor.Text;
@@ -998,6 +1010,7 @@ namespace System.Management.Automation
result = GetResultForEnumPropertyValueOfDSCResource(completionContext, string.Empty, ref replacementIndex, ref replacementLength, out _);
break;
}
case TokenKind.Break:
case TokenKind.Continue:
{
@@ -1008,6 +1021,10 @@ namespace System.Management.Automation
}
break;
}
case TokenKind.Using:
return CompleteUsingKeywords(completionContext.CursorPosition.Offset, _tokens, ref replacementIndex, ref replacementLength);
case TokenKind.Dot:
case TokenKind.ColonColon:
case TokenKind.QuestionDot:
@@ -1956,10 +1973,10 @@ namespace System.Management.Automation
private static List<CompletionResult> GetResultForIdentifier(CompletionContext completionContext, ref int replacementIndex, ref int replacementLength, bool isQuotedString)
{
List<CompletionResult> result = null;
var tokenAtCursor = completionContext.TokenAtCursor;
var lastAst = completionContext.RelatedAsts.Last();
List<CompletionResult> result = null;
var tokenAtCursorText = tokenAtCursor.Text;
completionContext.WordToComplete = tokenAtCursorText;
@@ -2487,5 +2504,81 @@ namespace System.Management.Automation
return result;
}
private static List<CompletionResult> CompleteUsingKeywords(int cursorOffset, Token[] tokens, ref int replacementIndex, ref int replacementLength)
{
var result = new List<CompletionResult>();
Token tokenBeforeCursor = null;
Token tokenAtCursor = null;
for (int i = tokens.Length - 1; i >= 0; i--)
{
if (tokens[i].Extent.EndOffset < cursorOffset && tokens[i].Kind != TokenKind.LineContinuation)
{
tokenBeforeCursor = tokens[i];
break;
}
else if (tokens[i].Extent.StartOffset <= cursorOffset && tokens[i].Extent.EndOffset >= cursorOffset && tokens[i].Kind != TokenKind.LineContinuation)
{
tokenAtCursor = tokens[i];
}
}
if (tokenBeforeCursor is not null && tokenBeforeCursor.Kind == TokenKind.Using)
{
string wordToComplete = null;
if (tokenAtCursor is not null)
{
replacementIndex = tokenAtCursor.Extent.StartOffset;
replacementLength = tokenAtCursor.Extent.Text.Length;
wordToComplete = tokenAtCursor.Text;
}
else
{
replacementIndex = cursorOffset;
replacementLength = 0;
}
foreach (var keyword in s_usingKeywords)
{
if (string.IsNullOrEmpty(wordToComplete) || keyword.StartsWith(wordToComplete, StringComparison.OrdinalIgnoreCase))
{
result.Add(new CompletionResult(keyword, keyword, CompletionResultType.Keyword, GetUsingKeywordToolTip(keyword)));
}
}
}
if (result.Count > 0)
{
return result;
}
return null;
}
private static string GetUsingKeywordToolTip(string keyword)
{
switch (keyword)
{
case "assembly":
return TabCompletionStrings.AssemblyKeywordDescription;
case "module":
return TabCompletionStrings.ModuleKeywordDescription;
case "namespace":
return TabCompletionStrings.NamespaceKeywordDescription;
case "type":
return TabCompletionStrings.TypeKeywordDescription;
default:
return null;
}
}
private static readonly string[] s_usingKeywords = new string[]
{
"assembly",
"module",
"namespace",
"type"
};
}
}
@@ -329,4 +329,28 @@
<data name="shrOperatorDescription" xml:space="preserve">
<value>Shift Right bit operator. Inserts zero in the left-most bit position. For signed values, sign bit is preserved.</value>
</data>
<data name="AssemblyKeywordDescription" xml:space="preserve">
<value>Specifies the path to a .NET assembly to load.
using assembly &lt;.NET-assembly-path&gt;</value>
</data>
<data name="ModuleKeywordDescription" xml:space="preserve">
<value>Specifies a PowerShell module to load classes from.
using module &lt;ModuleName or Path&gt;
using module &lt;ModuleSpecification hashtable&gt;</value>
</data>
<data name="NamespaceKeywordDescription" xml:space="preserve">
<value>Specifies a .NET namespace to resolve types from or a namespace alias.
using namespace &lt;.NET-namespace&gt;
using namespace &lt;AliasName&gt; = &lt;.NET-namespace&gt;</value>
</data>
<data name="TypeKeywordDescription" xml:space="preserve">
<value>Specifies an alias for a .NET Type.
using type &lt;AliasName&gt; = &lt;.NET-type&gt;</value>
</data>
</root>
@@ -341,11 +341,25 @@ switch ($x)
$res.CompletionMatches.Count | Should -BeGreaterThan 0
}
It 'Should complete keyword' -Skip {
It 'Should complete keyword with partial input' {
$res = TabExpansion2 -inputScript 'using nam' -cursorColumn 'using nam'.Length
$res.CompletionMatches[0].CompletionText | Should -BeExactly 'namespace'
}
It 'Should complete keyword with no input' {
$res = TabExpansion2 -inputScript 'using ' -cursorColumn 'using '.Length
$res.CompletionMatches.CompletionText | Should -BeExactly 'assembly','module','namespace','type'
}
It 'Should complete keyword with no input after line continuation' {
$InputScript = @'
using `
'@
$res = TabExpansion2 -inputScript $InputScript -cursorColumn $InputScript.Length
$res.CompletionMatches.CompletionText | Should -BeExactly 'assembly','module','namespace','type'
}
It 'Should first suggest -Full and then -Functionality when using Get-Help -Fu<tab>' -Skip {
$res = TabExpansion2 -inputScript 'Get-Help -Fu' -cursorColumn 'Get-Help -Fu'.Length
$res.CompletionMatches[0].CompletionText | Should -BeExactly '-Full'