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 @@ -327,6 +327,7 @@ internal List<CompletionResult> GetResultHelper(CompletionContext completionCont

case TokenKind.Multiply:
case TokenKind.Generic:
case TokenKind.MinusMinus: // for native commands '--'
case TokenKind.Identifier:
result = GetResultForIdentifier(completionContext, ref replacementIndex, ref replacementLength, isQuotedString);
break;
Expand Down Expand Up @@ -1086,7 +1087,7 @@ private List<CompletionResult> GetResultForEnumPropertyValueOfDSCResource(
{
if (!String.IsNullOrEmpty(dynamicKeywordAst.ElementName))
{
StringBuilder sb = new StringBuilder("[");
StringBuilder sb = new StringBuilder("[", 50);
sb.Append(dynamicKeywordAst.Keyword.Keyword);
sb.Append("]");
sb.Append(dynamicKeywordAst.ElementName);
Expand Down Expand Up @@ -1353,7 +1354,8 @@ private List<CompletionResult> GetResultForIdentifier(CompletionContext completi
var lastAst = completionContext.RelatedAsts.Last();

List<CompletionResult> result = null;
completionContext.WordToComplete = tokenAtCursor.Text;
var tokenAtCursorText = tokenAtCursor.Text;
completionContext.WordToComplete = tokenAtCursorText;

var strConst = lastAst as StringConstantExpressionAst;
if (strConst != null)
Expand Down Expand Up @@ -1419,7 +1421,7 @@ private List<CompletionResult> GetResultForIdentifier(CompletionContext completi
{
Ast cursorAst = null;
var cursorPosition = (InternalScriptPosition)_cursorPosition;
int offsetBeforeCmdName = cursorPosition.Offset - tokenAtCursor.Text.Length;
int offsetBeforeCmdName = cursorPosition.Offset - tokenAtCursorText.Length;
if (offsetBeforeCmdName >= 0)
{
var cursorBeforeCmdName = cursorPosition.CloneWithNewOffset(offsetBeforeCmdName);
Expand All @@ -1431,10 +1433,10 @@ private List<CompletionResult> GetResultForIdentifier(CompletionContext completi
cursorAst.Extent.EndLineNumber == tokenAtCursor.Extent.StartLineNumber &&
cursorAst.Extent.EndColumnNumber == tokenAtCursor.Extent.StartColumnNumber)
{
if (tokenAtCursor.Text.IndexOfAny(Utils.Separators.Directory) == 0)
if (tokenAtCursorText.IndexOfAny(Utils.Separators.Directory) == 0)
{
string wordToComplete =
CompletionCompleters.ConcatenateStringPathArguments(cursorAst as CommandElementAst, tokenAtCursor.Text, completionContext);
CompletionCompleters.ConcatenateStringPathArguments(cursorAst as CommandElementAst, tokenAtCursorText, completionContext);
if (wordToComplete != null)
{
completionContext.WordToComplete = wordToComplete;
Expand All @@ -1452,7 +1454,7 @@ private List<CompletionResult> GetResultForIdentifier(CompletionContext completi
string fullPath = variableAst != null
? CompletionCompleters.CombineVariableWithPartialPath(
variableAst: variableAst,
extraText: tokenAtCursor.Text,
extraText: tokenAtCursorText,
executionContext: completionContext.ExecutionContext)
: null;

Expand Down Expand Up @@ -1538,16 +1540,27 @@ private List<CompletionResult> GetResultForIdentifier(CompletionContext completi
return result;
}

if (tokenAtCursor.Text.Length == 1 && tokenAtCursor.Text[0].IsDash() && (lastAst.Parent is CommandAst || lastAst.Parent is DynamicKeywordStatementAst))
var isSingleDash = tokenAtCursorText.Length == 1 && tokenAtCursorText[0].IsDash();
var isDoubleDash = tokenAtCursorText.Length == 2 && tokenAtCursorText[0].IsDash() && tokenAtCursorText[1].IsDash();
var isParentCommandOrDynamicKeyword = (lastAst.Parent is CommandAst || lastAst.Parent is DynamicKeywordStatementAst);
if ((isSingleDash || isDoubleDash) && isParentCommandOrDynamicKeyword)
{
// When it's the content of a quoted string, we only handle variable/member completion
if (isQuotedString) { return result; }
return CompletionCompleters.CompleteCommandParameter(completionContext);
if (isSingleDash)
{
if (isQuotedString) { return result; }
var res = CompletionCompleters.CompleteCommandParameter(completionContext);
if (res.Count != 0)
{
return res;
}
}
return CompletionCompleters.CompleteCommandArgument(completionContext);
}

TokenKind memberOperator = TokenKind.Unknown;
bool isMemberCompletion = (lastAst.Parent is MemberExpressionAst);
bool isStatic = isMemberCompletion ? ((MemberExpressionAst)lastAst.Parent).Static : false;
bool isStatic = isMemberCompletion && ((MemberExpressionAst)lastAst.Parent).Static;
bool isWildcard = false;

if (!isMemberCompletion)
Expand All @@ -1556,12 +1569,12 @@ private List<CompletionResult> GetResultForIdentifier(CompletionContext completi
// We need to know if the previous element before the token is adjacent because
// we don't have a MemberExpressionAst, we might have 2 command arguments.

if (tokenAtCursor.Text.Equals(TokenKind.Dot.Text(), StringComparison.Ordinal))
if (tokenAtCursorText.Equals(TokenKind.Dot.Text(), StringComparison.Ordinal))
{
memberOperator = TokenKind.Dot;
isMemberCompletion = true;
}
else if (tokenAtCursor.Text.Equals(TokenKind.ColonColon.Text(), StringComparison.Ordinal))
else if (tokenAtCursorText.Equals(TokenKind.ColonColon.Text(), StringComparison.Ordinal))
{
memberOperator = TokenKind.ColonColon;
isMemberCompletion = true;
Expand Down Expand Up @@ -1607,7 +1620,7 @@ private List<CompletionResult> GetResultForIdentifier(CompletionContext completi
{
if (!isWildcard && memberOperator != TokenKind.Unknown)
{
replacementIndex += tokenAtCursor.Text.Length;
replacementIndex += tokenAtCursorText.Length;
replacementLength = 0;
}
return result;
Expand Down Expand Up @@ -1637,7 +1650,7 @@ private List<CompletionResult> GetResultForIdentifier(CompletionContext completi
completionContext.WordToComplete = wordToComplete;
}
}
else if (tokenAtCursor.Text.IndexOfAny(Utils.Separators.Directory) == 0)
else if (tokenAtCursorText.IndexOfAny(Utils.Separators.Directory) == 0)
{
var command = lastAst.Parent as CommandBaseAst;
if (command != null && command.Redirections.Any())
Expand All @@ -1648,7 +1661,7 @@ private List<CompletionResult> GetResultForIdentifier(CompletionContext completi
fileRedirection.Extent.EndColumnNumber == lastAst.Extent.StartColumnNumber)
{
string wordToComplete =
CompletionCompleters.ConcatenateStringPathArguments(fileRedirection.Location, tokenAtCursor.Text, completionContext);
CompletionCompleters.ConcatenateStringPathArguments(fileRedirection.Location, tokenAtCursorText, completionContext);

if (wordToComplete != null)
{
Expand Down
72 changes: 70 additions & 2 deletions test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ Describe "TabCompletion" -Tags CI {
$res.CompletionMatches[0].CompletionText | Should be 'System'
}


It 'Should complete format-table hashtable' {
$res = TabExpansion2 -inputScript 'Get-ChildItem | Format-Table @{ ' -cursorColumn 'Get-ChildItem | Format-Table @{ '.Length
$res.CompletionMatches.Count | Should Be 5
Expand Down Expand Up @@ -101,6 +100,75 @@ Describe "TabCompletion" -Tags CI {

It 'Should complete keyword' -skip {
$res = TabExpansion2 -inputScript 'using nam' -cursorColumn 'using nam'.Length
$res.CompletionMatches[0].CompletionText | Should be 'namespace'
$res.CompletionMatches[0].CompletionText | Should Be 'namespace'
}

Context NativeCommand {
BeforeAll {
$nativeCommand = (Get-Command -CommandType Application -TotalCount 1).Name
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might be wiser to create an external command on testdrive and change the path, but that's a tiny bit annoying to make it portable, so I'll merge as is.

}
It 'Completes native commands with -' {
Register-ArgumentCompleter -Native -CommandName $nativeCommand -ScriptBlock {
param($wordToComplete, $ast, $cursorColumn)
if ($wordToComplete -eq '-') {
return "-flag"
}
else {
return "unexpected wordtocomplete"
}
}
$line = "$nativeCommand -"
$res = TabExpansion2 -inputScript $line -cursorColumn $line.Length
$res.CompletionMatches.Count | Should Be 1
$res.CompletionMatches.CompletionText | Should Be "-flag"
}

It 'Completes native commands with --' {
Register-ArgumentCompleter -Native -CommandName $nativeCommand -ScriptBlock {
param($wordToComplete, $ast, $cursorColumn)
if ($wordToComplete -eq '--') {
return "--flag"
}
else {
return "unexpected wordtocomplete"
}
}
$line = "$nativeCommand --"
$res = TabExpansion2 -inputScript $line -cursorColumn $line.Length
$res.CompletionMatches.Count | Should Be 1
$res.CompletionMatches.CompletionText | Should Be "--flag"
}

It 'Completes native commands with --f' {
Register-ArgumentCompleter -Native -CommandName $nativeCommand -ScriptBlock {
param($wordToComplete, $ast, $cursorColumn)
if ($wordToComplete -eq '--f') {
return "--flag"
}
else {
return "unexpected wordtocomplete"
}
}
$line = "$nativeCommand --f"
$res = TaBexpansion2 -inputScript $line -cursorColumn $line.Length
$res.CompletionMatches.Count | Should Be 1
$res.CompletionMatches.CompletionText | Should Be "--flag"
}

It 'Completes native commands with -o' {
Register-ArgumentCompleter -Native -CommandName $nativeCommand -ScriptBlock {
param($wordToComplete, $ast, $cursorColumn)
if ($wordToComplete -eq '-o') {
return "-option"
}
else {
return "unexpected wordtocomplete"
}
}
$line = "$nativeCommand -o"
$res = TaBexpansion2 -inputScript $line -cursorColumn $line.Length
$res.CompletionMatches.Count | Should Be 1
$res.CompletionMatches.CompletionText | Should Be "-option"
}
}
}