Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,12 @@ internal List<CompletionResult> GetResultHelper(CompletionContext completionCont
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);
}

Expand Down Expand Up @@ -774,6 +780,12 @@ internal List<CompletionResult> GetResultHelper(CompletionContext completionCont
}
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;
Expand Down Expand Up @@ -998,6 +1010,7 @@ internal List<CompletionResult> GetResultHelper(CompletionContext completionCont
result = GetResultForEnumPropertyValueOfDSCResource(completionContext, string.Empty, ref replacementIndex, ref replacementLength, out _);
break;
}

case TokenKind.Break:
case TokenKind.Continue:
{
Expand All @@ -1008,6 +1021,10 @@ internal List<CompletionResult> GetResultHelper(CompletionContext completionCont
}
break;
}

case TokenKind.Using:
return CompleteUsingKeywords(completionContext.CursorPosition.Offset, _tokens, ref replacementIndex, ref replacementLength);

case TokenKind.Dot:
case TokenKind.ColonColon:
case TokenKind.QuestionDot:
Expand Down Expand Up @@ -1956,10 +1973,10 @@ private static List<CompletionResult> GetResultForIdentifierInConfiguration(

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;

Expand Down Expand Up @@ -2487,5 +2504,81 @@ private static List<CompletionResult> CompleteLoopLabel(CompletionContext comple

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"
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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>
16 changes: 15 additions & 1 deletion test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down