From 8c0cebb6e11033602d8704ce5670709a894d13fa Mon Sep 17 00:00:00 2001 From: Ilya Date: Wed, 30 Dec 2020 17:19:28 +0500 Subject: [PATCH 01/25] Implement a completion for View parameter of format cmdlets --- .../CommandCompletion/CompletionCompleters.cs | 100 ++++++++++++++++-- .../TabCompletion/TabCompletion.Tests.ps1 | 31 ++++++ 2 files changed, 125 insertions(+), 6 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 35a7398454a..1a82d4d79be 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -24,6 +24,7 @@ using Microsoft.PowerShell; using Microsoft.PowerShell.Cim; using Microsoft.PowerShell.Commands; +using Microsoft.PowerShell.Commands.Internal.Format; namespace System.Management.Automation { @@ -2282,6 +2283,14 @@ private static void NativeCommandArgumentCompletion( case "Measure-Object": case "Sort-Object": case "Where-Object": + { + if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase)) + { + NativeCompletionMemberName(context, result, commandAst); + } + + break; + } case "Format-Custom": case "Format-List": case "Format-Table": @@ -2291,6 +2300,10 @@ private static void NativeCommandArgumentCompletion( { NativeCompletionMemberName(context, result, commandAst); } + else if (parameterName.Equals("View", StringComparison.OrdinalIgnoreCase)) + { + NativeCompletionFormatViewName(context, boundArguments, result, commandAst, commandName); + } break; } @@ -3785,47 +3798,82 @@ private static void NativeCompletionPathArgument(CompletionContext context, stri result.Add(CompletionResult.Null); } - private static void NativeCompletionMemberName(CompletionContext context, List result, CommandAst commandAst) + private static IEnumerable GetInferenceTypes(CompletionContext context, List result, CommandAst commandAst) { // Command is something like where-object/foreach-object/format-list/etc. where there is a parameter that is a property name // and we want member names based on the input object, which is either the parameter InputObject, or comes from the pipeline. - if (!(commandAst.Parent is PipelineAst pipelineAst)) - return; + if (commandAst.Parent is not PipelineAst pipelineAst) + { + return null; + } int i; for (i = 0; i < pipelineAst.PipelineElements.Count; i++) { if (pipelineAst.PipelineElements[i] == commandAst) + { break; + } } IEnumerable prevType = null; if (i == 0) { + // based on a type of the argument which is binded to 'InputObject' parameter. AstParameterArgumentPair pair; if (!context.PseudoBindingInfo.BoundArguments.TryGetValue("InputObject", out pair) || !pair.ArgumentSpecified) { - return; + return null; } var astPair = pair as AstPair; if (astPair == null || astPair.Argument == null) { - return; + return null; } prevType = AstTypeInference.InferTypeOf(astPair.Argument, context.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval); } else { + // based on OutputTypeAttribute() of the first cmdlet in pipeline. prevType = AstTypeInference.InferTypeOf(pipelineAst.PipelineElements[i - 1], context.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval); } - CompleteMemberByInferredType(context.TypeInferenceContext, prevType, result, context.WordToComplete + "*", filter: IsPropertyMember, isStatic: false); + return prevType; + } + + private static void NativeCompletionMemberName(CompletionContext context, List result, CommandAst commandAst) + { + IEnumerable prevType = GetInferenceTypes(context, result, commandAst); + if (prevType is not null) + { + CompleteMemberByInferredType(context.TypeInferenceContext, prevType, result, context.WordToComplete + "*", filter: IsPropertyMember, isStatic: false); + } + result.Add(CompletionResult.Null); } + private static void NativeCompletionFormatViewName( + CompletionContext context, + Dictionary boundArguments, + List result, + CommandAst commandAst, + string commandName) + { + IEnumerable prevType = NativeCommandArgumentCompletion_InferTypesOfArgument(boundArguments, commandAst, context, "InputObject"); + + if (prevType is not null) + { + var inferTypeNames = prevType.Select(t => t.Name).ToArray(); + CompleteFormatViewByInferredType(context.TypeInferenceContext, inferTypeNames, result, commandName); + } + + result.Add(CompletionResult.Null); + } + + private static void NativeCompletionTypeName(CompletionContext context, List result) { var wordToComplete = context.WordToComplete; @@ -5697,6 +5745,46 @@ private static bool IsInDscContext(ExpressionAst expression) return Ast.GetAncestorAst(expression) != null; } + private static void CompleteFormatViewByInferredType(TypeInferenceContext context, string[] inferredTypeNames, List results, string commandName) + { + var db = context.ExecutionContext.FormatDBManager.GetTypeInfoDataBase(); + + if (db is null) + { + return; + } + + Type controlBodyType = commandName switch + { + "Format-Table" => typeof(TableControlBody), + "Format-List" => typeof(ListControlBody), + "Format-Wide" => typeof(WideControlBody), + "Format-Custom" => typeof(ComplexControlBody), + _ => null + }; + + Diagnostics.Assert(controlBodyType is null, "This should never happen unless a new Format-* cmdlet is added"); + + HashSet uniqueNames = new(); + foreach (ViewDefinition vd in db.viewDefinitionsSection.viewDefinitionList) + { + if (vd is not null && controlBodyType == vd.mainControl.GetType() && vd.appliesTo is not null) + { + foreach (var applyTo in vd.appliesTo.referenceList) + { + foreach (var inferredTypeName in inferredTypeNames) + { + // We use 'StartsWith()' because 'applyTo.Name' can look like "System.Diagnostics.Process#IncludeUserName". + if (applyTo.name.StartsWith(inferredTypeName, StringComparison.OrdinalIgnoreCase) && uniqueNames.Add(vd.name)) + { + results.Add(new CompletionResult(vd.name, vd.name, CompletionResultType.Text, vd.name)); + } + } + } + } + } + } + internal static void CompleteMemberByInferredType(TypeInferenceContext context, IEnumerable inferredTypes, List results, string memberName, Func filter, bool isStatic) { bool extensionMethodsAdded = false; diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 01f58f06bf6..d25c6e90ced 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -301,6 +301,37 @@ Describe "TabCompletion" -Tags CI { $actual | Should -BeExactly $expected } + Context "Format cmdlet's View paramter completion" { + It 'Should complete Get-ChildItem | -View' -TestCases ( + @{ cmd = 'Format-Table'; expected = "$(if ($EnabledExperimentalFeatures.Contains('PSUnixFileStat')) { 'childrenWithUnixStat ' })children childrenWithHardlink" }, + @{ cmd = 'Format-List'; expected = 'children' }, + @{ cmd = 'Format-Wide'; expected = 'children' }, + @{ cmd = 'Format-Custom'; expected = '' } + ) { + param($cmd, $expected) + + # The completion is based on OutputTypeAttribute() of the cmdlet. + $res = TabExpansion2 -inputScript "Get-ChildItem | $cmd -View " -cursorColumn "Get-ChildItem | $cmd -View ".Length + $completionText = $res.CompletionMatches.CompletionText | Sort-Object + $completionText -join ' ' | Should -BeExactly $expected + } + + It 'Should complete $processList = Get-Process; $processList | -View' -TestCases ( + @{ cmd = 'Format-Table'; expected = 'Priority process ProcessModule ProcessWithUserName StartTime' }, + @{ cmd = 'Format-List'; expected = '' }, + @{ cmd = 'Format-Wide'; expected = 'process' }, + @{ cmd = 'Format-Custom'; expected = '' } + ) { + param($cmd, $expected) + + # The completion is based on a type of the argument which is binded to 'InputObject' parameter. + $processList = Get-Process + $res = TabExpansion2 -inputScript "`$processList | $cmd -View " -cursorColumn "`$processList | $cmd -View ".Length + $completionText = $res.CompletionMatches.CompletionText | Sort-Object + $completionText -join ' ' | Should -BeExactly $expected + } + } + Context NativeCommand { BeforeAll { $nativeCommand = (Get-Command -CommandType Application -TotalCount 1).Name From dc9878f09525edb733ce57ac9e28981773293a35 Mon Sep 17 00:00:00 2001 From: Ilya Date: Wed, 30 Dec 2020 17:30:42 +0500 Subject: [PATCH 02/25] Fix style 1 --- .../engine/CommandCompletion/CompletionCompleters.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 1a82d4d79be..706cb74391b 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -3873,7 +3873,6 @@ private static void NativeCompletionFormatViewName( result.Add(CompletionResult.Null); } - private static void NativeCompletionTypeName(CompletionContext context, List result) { var wordToComplete = context.WordToComplete; From d15a52a63d5a8e9e7b341cbfeed17017ca7ec7b5 Mon Sep 17 00:00:00 2001 From: Ilya Date: Wed, 30 Dec 2020 20:00:16 +0500 Subject: [PATCH 03/25] Fix Unix test --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index d25c6e90ced..54d5479a88d 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -303,7 +303,7 @@ Describe "TabCompletion" -Tags CI { Context "Format cmdlet's View paramter completion" { It 'Should complete Get-ChildItem | -View' -TestCases ( - @{ cmd = 'Format-Table'; expected = "$(if ($EnabledExperimentalFeatures.Contains('PSUnixFileStat')) { 'childrenWithUnixStat ' })children childrenWithHardlink" }, + @{ cmd = 'Format-Table'; expected = "children childrenWithHardlink$(if ($EnabledExperimentalFeatures.Contains('PSUnixFileStat')) { ' childrenWithUnixStat' })" }, @{ cmd = 'Format-List'; expected = 'children' }, @{ cmd = 'Format-Wide'; expected = 'children' }, @{ cmd = 'Format-Custom'; expected = '' } From 3eda66bf69b3b9bb36c0be8651248149f4acba98 Mon Sep 17 00:00:00 2001 From: Ilya Date: Tue, 13 Apr 2021 09:22:31 +0500 Subject: [PATCH 04/25] Update src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs Co-authored-by: Robert Holt --- .../engine/CommandCompletion/CompletionCompleters.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 706cb74391b..533eced863a 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -3866,7 +3866,7 @@ private static void NativeCompletionFormatViewName( if (prevType is not null) { - var inferTypeNames = prevType.Select(t => t.Name).ToArray(); + string[] inferTypeNames = prevType.Select(t => t.Name).ToArray(); CompleteFormatViewByInferredType(context.TypeInferenceContext, inferTypeNames, result, commandName); } From 2e0c109dd70b829a915e5b11dccb25d5a181f852 Mon Sep 17 00:00:00 2001 From: Ilya Date: Thu, 3 Jun 2021 19:30:16 +0500 Subject: [PATCH 05/25] Address feedback --- .../CommandCompletion/CompletionCompleters.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 533eced863a..abb09516b03 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -5441,11 +5441,11 @@ private static FunctionDefinitionAst GetCommentHelpFunctionTarget(CompletionCont return null; } - + private static List CompleteCommentParameterValue(CompletionContext context, string wordToComplete) { FunctionDefinitionAst foundFunction = GetCommentHelpFunctionTarget(context); - + ReadOnlyCollection foundParameters = null; if (foundFunction is not null) { @@ -5456,7 +5456,7 @@ private static List CompleteCommentParameterValue(CompletionCo // The helpblock is for a script file foundParameters = scriptAst.ParamBlock?.Parameters; } - + if (foundParameters is null || foundParameters.Count == 0) { return null; @@ -5746,9 +5746,9 @@ private static bool IsInDscContext(ExpressionAst expression) private static void CompleteFormatViewByInferredType(TypeInferenceContext context, string[] inferredTypeNames, List results, string commandName) { - var db = context.ExecutionContext.FormatDBManager.GetTypeInfoDataBase(); + var typeInfoDB = context.ExecutionContext.FormatDBManager.GetTypeInfoDataBase(); - if (db is null) + if (typeInfoDB is null) { return; } @@ -5764,19 +5764,19 @@ private static void CompleteFormatViewByInferredType(TypeInferenceContext contex Diagnostics.Assert(controlBodyType is null, "This should never happen unless a new Format-* cmdlet is added"); - HashSet uniqueNames = new(); - foreach (ViewDefinition vd in db.viewDefinitionsSection.viewDefinitionList) + var uniqueNames = new HashSet(); + foreach (ViewDefinition viewDefinition in typeInfoDB.viewDefinitionsSection.viewDefinitionList) { - if (vd is not null && controlBodyType == vd.mainControl.GetType() && vd.appliesTo is not null) + if (viewDefinition is not null && controlBodyType == viewDefinition.mainControl.GetType() && viewDefinition.appliesTo is not null) { - foreach (var applyTo in vd.appliesTo.referenceList) + foreach (TypeOrGroupReference applyTo in viewDefinition.appliesTo.referenceList) { - foreach (var inferredTypeName in inferredTypeNames) + foreach (string inferredTypeName in inferredTypeNames) { // We use 'StartsWith()' because 'applyTo.Name' can look like "System.Diagnostics.Process#IncludeUserName". - if (applyTo.name.StartsWith(inferredTypeName, StringComparison.OrdinalIgnoreCase) && uniqueNames.Add(vd.name)) + if (applyTo.name.StartsWith(inferredTypeName, StringComparison.OrdinalIgnoreCase) && uniqueNames.Add(viewDefinition.name)) { - results.Add(new CompletionResult(vd.name, vd.name, CompletionResultType.Text, vd.name)); + results.Add(new CompletionResult(viewDefinition.name, viewDefinition.name, CompletionResultType.Text, viewDefinition.name)); } } } @@ -6618,7 +6618,7 @@ internal static List CompleteHelpTopics(CompletionContext cont //search for help files for the current culture + en-US as fallback var searchPaths = new string[] - { + { Path.Combine(userHelpDir, currentCulture), Path.Combine(appHelpDir, currentCulture), Path.Combine(userHelpDir, "en-US"), From 7f59e02b3198033da6b544ed473b4369cb944f24 Mon Sep 17 00:00:00 2001 From: Ilya Date: Tue, 8 Jun 2021 09:26:33 +0500 Subject: [PATCH 06/25] Address feedback 2 --- .../engine/CommandCompletion/CompletionCompleters.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index abb09516b03..bf927456749 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -3798,7 +3798,7 @@ private static void NativeCompletionPathArgument(CompletionContext context, stri result.Add(CompletionResult.Null); } - private static IEnumerable GetInferenceTypes(CompletionContext context, List result, CommandAst commandAst) + private static IEnumerable GetInferenceTypes(CompletionContext context, CommandAst commandAst) { // Command is something like where-object/foreach-object/format-list/etc. where there is a parameter that is a property name // and we want member names based on the input object, which is either the parameter InputObject, or comes from the pipeline. @@ -3846,7 +3846,7 @@ private static IEnumerable GetInferenceTypes(CompletionContext conte private static void NativeCompletionMemberName(CompletionContext context, List result, CommandAst commandAst) { - IEnumerable prevType = GetInferenceTypes(context, result, commandAst); + IEnumerable prevType = GetInferenceTypes(context, commandAst); if (prevType is not null) { CompleteMemberByInferredType(context.TypeInferenceContext, prevType, result, context.WordToComplete + "*", filter: IsPropertyMember, isStatic: false); @@ -5767,7 +5767,7 @@ private static void CompleteFormatViewByInferredType(TypeInferenceContext contex var uniqueNames = new HashSet(); foreach (ViewDefinition viewDefinition in typeInfoDB.viewDefinitionsSection.viewDefinitionList) { - if (viewDefinition is not null && controlBodyType == viewDefinition.mainControl.GetType() && viewDefinition.appliesTo is not null) + if (viewDefinition?.appliesTo is not null && controlBodyType == viewDefinition.mainControl.GetType()) { foreach (TypeOrGroupReference applyTo in viewDefinition.appliesTo.referenceList) { From aed296642f18c5e74c03d0ad6d40f21da5999d3d Mon Sep 17 00:00:00 2001 From: Ilya Date: Fri, 11 Jun 2021 10:06:35 +0500 Subject: [PATCH 07/25] Add support for view names with special characters --- .../CommandCompletion/CompletionCompleters.cs | 8 ++- .../TabCompletion/TabCompletion.Tests.ps1 | 61 ++++++++++++++++++- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index bf927456749..9cc7e16d93e 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -5762,7 +5762,7 @@ private static void CompleteFormatViewByInferredType(TypeInferenceContext contex _ => null }; - Diagnostics.Assert(controlBodyType is null, "This should never happen unless a new Format-* cmdlet is added"); + Diagnostics.Assert(controlBodyType is not null, "This should never happen unless a new Format-* cmdlet is added"); var uniqueNames = new HashSet(); foreach (ViewDefinition viewDefinition in typeInfoDB.viewDefinitionsSection.viewDefinitionList) @@ -5776,7 +5776,11 @@ private static void CompleteFormatViewByInferredType(TypeInferenceContext contex // We use 'StartsWith()' because 'applyTo.Name' can look like "System.Diagnostics.Process#IncludeUserName". if (applyTo.name.StartsWith(inferredTypeName, StringComparison.OrdinalIgnoreCase) && uniqueNames.Add(viewDefinition.name)) { - results.Add(new CompletionResult(viewDefinition.name, viewDefinition.name, CompletionResultType.Text, viewDefinition.name)); + string completionText = viewDefinition.name.IndexOfAny(s_charactersRequiringQuotes) != -1 + ? "'" + viewDefinition.name + "'" + : viewDefinition.name; + + results.Add(new CompletionResult(completionText, viewDefinition.name, CompletionResultType.Text, viewDefinition.name)); } } } diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 54d5479a88d..0399ebb0143 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -302,6 +302,63 @@ Describe "TabCompletion" -Tags CI { } Context "Format cmdlet's View paramter completion" { + BeforeAll { + $viewDefinition = @' + + + + + R A M + + System.Diagnostics.Process + + + + + + 40 + Center + + + + 40 + Center + + + + 40 + Center + + + + + + + Center + Name + + + Center + PagedMemorySize + + + Center + PeakWorkingSet + + + + + + + + +'@ + + $tempViewFile = Join-Path -Path $TestDrive -ChildPath 'processViewDefinition.ps1xml' + Set-Content -LiteralPath $tempViewFile -Value $viewDefinition -Force + Update-TypeData -AppendPath $tempViewFile + } + It 'Should complete Get-ChildItem | -View' -TestCases ( @{ cmd = 'Format-Table'; expected = "children childrenWithHardlink$(if ($EnabledExperimentalFeatures.Contains('PSUnixFileStat')) { ' childrenWithUnixStat' })" }, @{ cmd = 'Format-List'; expected = 'children' }, @@ -317,7 +374,7 @@ Describe "TabCompletion" -Tags CI { } It 'Should complete $processList = Get-Process; $processList | -View' -TestCases ( - @{ cmd = 'Format-Table'; expected = 'Priority process ProcessModule ProcessWithUserName StartTime' }, + @{ cmd = 'Format-Table'; expected = "'R A M' Priority process ProcessModule ProcessWithUserName StartTime" }, @{ cmd = 'Format-List'; expected = '' }, @{ cmd = 'Format-Wide'; expected = 'process' }, @{ cmd = 'Format-Custom'; expected = '' } @@ -1408,7 +1465,7 @@ dir -Recurse ` ## Save original culture and temporarily set it to da-DK because there's no localized help for da-DK. $OriginalCulture = [cultureinfo]::CurrentCulture [cultureinfo]::CurrentCulture="da-DK" - + $res = TabExpansion2 -inputScript 'get-help about_spla' -cursorColumn 'get-help about_spla'.Length $res.CompletionMatches | Should -HaveCount 1 $res.CompletionMatches[0].CompletionText | Should -BeExactly 'about_Splatting' From 37de1a3af2a10f74497fbc72eb3930807b1d470b Mon Sep 17 00:00:00 2001 From: Ilya Date: Fri, 11 Jun 2021 12:42:40 +0500 Subject: [PATCH 08/25] Fix typo --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 0399ebb0143..caa71c7067e 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -356,7 +356,7 @@ Describe "TabCompletion" -Tags CI { $tempViewFile = Join-Path -Path $TestDrive -ChildPath 'processViewDefinition.ps1xml' Set-Content -LiteralPath $tempViewFile -Value $viewDefinition -Force - Update-TypeData -AppendPath $tempViewFile + Update-FormatData -AppendPath $tempViewFile } It 'Should complete Get-ChildItem | -View' -TestCases ( From 84986f4e1258c4dca1aba2806fb9190efc6b6c15 Mon Sep 17 00:00:00 2001 From: Ilya Date: Fri, 11 Jun 2021 16:13:47 +0500 Subject: [PATCH 09/25] Add support quotes and wildcards --- .../CommandCompletion/CompletionCompleters.cs | 22 +++++++++++++------ .../TabCompletion/TabCompletion.Tests.ps1 | 13 ++++++----- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 9cc7e16d93e..96ee4e981e4 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -3867,7 +3867,7 @@ private static void NativeCompletionFormatViewName( if (prevType is not null) { string[] inferTypeNames = prevType.Select(t => t.Name).ToArray(); - CompleteFormatViewByInferredType(context.TypeInferenceContext, inferTypeNames, result, commandName); + CompleteFormatViewByInferredType(context, inferTypeNames, result, commandName); } result.Add(CompletionResult.Null); @@ -5744,9 +5744,9 @@ private static bool IsInDscContext(ExpressionAst expression) return Ast.GetAncestorAst(expression) != null; } - private static void CompleteFormatViewByInferredType(TypeInferenceContext context, string[] inferredTypeNames, List results, string commandName) + private static void CompleteFormatViewByInferredType(CompletionContext context, string[] inferredTypeNames, List results, string commandName) { - var typeInfoDB = context.ExecutionContext.FormatDBManager.GetTypeInfoDataBase(); + var typeInfoDB = context.TypeInferenceContext.ExecutionContext.FormatDBManager.GetTypeInfoDataBase(); if (typeInfoDB is null) { @@ -5764,6 +5764,10 @@ private static void CompleteFormatViewByInferredType(TypeInferenceContext contex Diagnostics.Assert(controlBodyType is not null, "This should never happen unless a new Format-* cmdlet is added"); + var wordToComplete = context.WordToComplete; + var quote = HandleDoubleAndSingleQuote(ref wordToComplete); + WildcardPattern viewPattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); + var uniqueNames = new HashSet(); foreach (ViewDefinition viewDefinition in typeInfoDB.viewDefinitionsSection.viewDefinitionList) { @@ -5774,11 +5778,15 @@ private static void CompleteFormatViewByInferredType(TypeInferenceContext contex foreach (string inferredTypeName in inferredTypeNames) { // We use 'StartsWith()' because 'applyTo.Name' can look like "System.Diagnostics.Process#IncludeUserName". - if (applyTo.name.StartsWith(inferredTypeName, StringComparison.OrdinalIgnoreCase) && uniqueNames.Add(viewDefinition.name)) + if (applyTo.name.StartsWith(inferredTypeName, StringComparison.OrdinalIgnoreCase) + && uniqueNames.Add(viewDefinition.name) + && viewPattern.IsMatch(viewDefinition.name)) { - string completionText = viewDefinition.name.IndexOfAny(s_charactersRequiringQuotes) != -1 - ? "'" + viewDefinition.name + "'" - : viewDefinition.name; + string completionText = quote != string.Empty + ? quote + viewDefinition.name + quote + : viewDefinition.name.IndexOfAny(s_charactersRequiringQuotes) != -1 + ? "'" + viewDefinition.name + "'" + : viewDefinition.name; results.Add(new CompletionResult(completionText, viewDefinition.name, CompletionResultType.Text, viewDefinition.name)); } diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index caa71c7067e..cdeb17e26a1 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -374,16 +374,19 @@ Describe "TabCompletion" -Tags CI { } It 'Should complete $processList = Get-Process; $processList | -View' -TestCases ( - @{ cmd = 'Format-Table'; expected = "'R A M' Priority process ProcessModule ProcessWithUserName StartTime" }, - @{ cmd = 'Format-List'; expected = '' }, - @{ cmd = 'Format-Wide'; expected = 'process' }, - @{ cmd = 'Format-Custom'; expected = '' } + @{ cmd = 'Format-Table -View '; expected = "'R A M' Priority process ProcessModule ProcessWithUserName StartTime" }, + @{ cmd = 'Format-List -View '; expected = '' }, + @{ cmd = 'Format-Wide -View '; expected = 'process' }, + @{ cmd = 'Format-Custom -View '; expected = '' }, + @{ cmd = 'Format-Table -View S'; expected = "StartTime" }, + @{ cmd = "Format-Table -View 'S"; expected = "'StartTime'" }, + @{ cmd = "Format-Table -View R"; expected = "'R A M'" } ) { param($cmd, $expected) # The completion is based on a type of the argument which is binded to 'InputObject' parameter. $processList = Get-Process - $res = TabExpansion2 -inputScript "`$processList | $cmd -View " -cursorColumn "`$processList | $cmd -View ".Length + $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd -View ".Length $completionText = $res.CompletionMatches.CompletionText | Sort-Object $completionText -join ' ' | Should -BeExactly $expected } From aab8b49ad1680122e1c6f477ffb144e11d3c55bd Mon Sep 17 00:00:00 2001 From: Ilya Date: Fri, 11 Jun 2021 16:47:21 +0500 Subject: [PATCH 10/25] Fix typo --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index cdeb17e26a1..2177fd85dc7 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -386,7 +386,7 @@ Describe "TabCompletion" -Tags CI { # The completion is based on a type of the argument which is binded to 'InputObject' parameter. $processList = Get-Process - $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd -View ".Length + $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd".Length $completionText = $res.CompletionMatches.CompletionText | Sort-Object $completionText -join ' ' | Should -BeExactly $expected } From 43e023587264f28e91f1de622a7fce746f7f258e Mon Sep 17 00:00:00 2001 From: Ilya Date: Fri, 11 Jun 2021 18:32:47 +0500 Subject: [PATCH 11/25] Remove temp file --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 2177fd85dc7..4ae8028612a 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -357,6 +357,8 @@ Describe "TabCompletion" -Tags CI { $tempViewFile = Join-Path -Path $TestDrive -ChildPath 'processViewDefinition.ps1xml' Set-Content -LiteralPath $tempViewFile -Value $viewDefinition -Force Update-FormatData -AppendPath $tempViewFile + + Remove-Item -LiteralPath $tempViewFile -Force -ErrorAction SilentlyContinue } It 'Should complete Get-ChildItem | -View' -TestCases ( From b4c790070ed38d90f649575098e459bf038b6ac5 Mon Sep 17 00:00:00 2001 From: Ilya Date: Fri, 11 Jun 2021 23:53:19 +0500 Subject: [PATCH 12/25] Improve quoting --- .../engine/CommandCompletion/CompletionCompleters.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 96ee4e981e4..a69e2fb2585 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -5782,11 +5782,13 @@ private static void CompleteFormatViewByInferredType(CompletionContext context, && uniqueNames.Add(viewDefinition.name) && viewPattern.IsMatch(viewDefinition.name)) { - string completionText = quote != string.Empty - ? quote + viewDefinition.name + quote - : viewDefinition.name.IndexOfAny(s_charactersRequiringQuotes) != -1 - ? "'" + viewDefinition.name + "'" - : viewDefinition.name; + string completionText = viewDefinition.name; + var quoteInUse = quote == string.Empty ? "'" : quote; + if (quoteInUse == "'") + { + completionText = completionText.Replace("'", "''"); + } + completionText = quoteInUse + completionText + quoteInUse; results.Add(new CompletionResult(completionText, viewDefinition.name, CompletionResultType.Text, viewDefinition.name)); } From 80af4da476317a040b70235d9312baa5db9d8894 Mon Sep 17 00:00:00 2001 From: Ilya Date: Fri, 11 Jun 2021 23:53:36 +0500 Subject: [PATCH 13/25] Update test --- .../TabCompletion/TabCompletion.Tests.ps1 | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 4ae8028612a..8d048c6d990 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -356,7 +356,12 @@ Describe "TabCompletion" -Tags CI { $tempViewFile = Join-Path -Path $TestDrive -ChildPath 'processViewDefinition.ps1xml' Set-Content -LiteralPath $tempViewFile -Value $viewDefinition -Force - Update-FormatData -AppendPath $tempViewFile + + $ps = [PowerShell]::Create() + $null = $ps.AddScript("Update-FormatData -AppendPath $tempViewFile") + $ps.Invoke() + $ps.HadErrors | Should -BeFalse + $ps.Commands.Clear() Remove-Item -LiteralPath $tempViewFile -Force -ErrorAction SilentlyContinue } @@ -375,6 +380,20 @@ Describe "TabCompletion" -Tags CI { $completionText -join ' ' | Should -BeExactly $expected } + It 'Should complete Get-ChildItem | -View' -TestCases ( + @{ cmd = 'Format-Table'; expected = "children childrenWithHardlink$(if ($EnabledExperimentalFeatures.Contains('PSUnixFileStat')) { ' childrenWithUnixStat' })" }, + @{ cmd = 'Format-List'; expected = 'children' }, + @{ cmd = 'Format-Wide'; expected = 'children' }, + @{ cmd = 'Format-Custom'; expected = '' } + ) { + param($cmd, $expected) + + # The completion is based on OutputTypeAttribute() of the cmdlet. + $res = TabExpansion2 -inputScript "Get-ChildItem | $cmd -View " -cursorColumn "Get-ChildItem | $cmd -View ".Length + $completionText = $res.CompletionMatches.CompletionText | Sort-Object + $completionText -join ' ' | Should -BeExactly $expected + } + It 'Should complete $processList = Get-Process; $processList | -View' -TestCases ( @{ cmd = 'Format-Table -View '; expected = "'R A M' Priority process ProcessModule ProcessWithUserName StartTime" }, @{ cmd = 'Format-List -View '; expected = '' }, @@ -386,11 +405,18 @@ Describe "TabCompletion" -Tags CI { ) { param($cmd, $expected) - # The completion is based on a type of the argument which is binded to 'InputObject' parameter. - $processList = Get-Process - $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd".Length - $completionText = $res.CompletionMatches.CompletionText | Sort-Object - $completionText -join ' ' | Should -BeExactly $expected + $ps.AddScript({ + param ($cmd) + $processList=Get-Process + $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd".Length + $completionText = $res.CompletionMatches.CompletionText | Sort-Object + $completionText -join ' ' + }).AddArgument($cmd) + + $result = $ps.Invoke() + $ps.Commands.Clear() + + $result | Should -BeExactly $expected } } From 24080629be6001e48907e29e3d69b3d7d088b676 Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 00:43:09 +0500 Subject: [PATCH 14/25] Fix typo --- .../Host/TabCompletion/TabCompletion.Tests.ps1 | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 8d048c6d990..bfbb8a40164 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -380,21 +380,7 @@ Describe "TabCompletion" -Tags CI { $completionText -join ' ' | Should -BeExactly $expected } - It 'Should complete Get-ChildItem | -View' -TestCases ( - @{ cmd = 'Format-Table'; expected = "children childrenWithHardlink$(if ($EnabledExperimentalFeatures.Contains('PSUnixFileStat')) { ' childrenWithUnixStat' })" }, - @{ cmd = 'Format-List'; expected = 'children' }, - @{ cmd = 'Format-Wide'; expected = 'children' }, - @{ cmd = 'Format-Custom'; expected = '' } - ) { - param($cmd, $expected) - - # The completion is based on OutputTypeAttribute() of the cmdlet. - $res = TabExpansion2 -inputScript "Get-ChildItem | $cmd -View " -cursorColumn "Get-ChildItem | $cmd -View ".Length - $completionText = $res.CompletionMatches.CompletionText | Sort-Object - $completionText -join ' ' | Should -BeExactly $expected - } - - It 'Should complete $processList = Get-Process; $processList | -View' -TestCases ( + It 'Should complete $processList = Get-Process; $processList | ' -TestCases ( @{ cmd = 'Format-Table -View '; expected = "'R A M' Priority process ProcessModule ProcessWithUserName StartTime" }, @{ cmd = 'Format-List -View '; expected = '' }, @{ cmd = 'Format-Wide -View '; expected = 'process' }, @@ -407,7 +393,7 @@ Describe "TabCompletion" -Tags CI { $ps.AddScript({ param ($cmd) - $processList=Get-Process + $processList = Get-Process $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd".Length $completionText = $res.CompletionMatches.CompletionText | Sort-Object $completionText -join ' ' From ed1f48a6e05b8ff011f01a291baaec8da75c3623 Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 09:42:23 +0500 Subject: [PATCH 15/25] Fix quoting --- .../engine/CommandCompletion/CompletionCompleters.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index a69e2fb2585..04b41ab2234 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -5783,7 +5783,7 @@ private static void CompleteFormatViewByInferredType(CompletionContext context, && viewPattern.IsMatch(viewDefinition.name)) { string completionText = viewDefinition.name; - var quoteInUse = quote == string.Empty ? "'" : quote; + var quoteInUse = quote == string.Empty && viewDefinition.name.IndexOfAny(s_charactersRequiringQuotes) != -1 ? "'" : quote; if (quoteInUse == "'") { completionText = completionText.Replace("'", "''"); From a4e7ee329a22d39a72e2a4f596512ccb7afe3756 Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 10:22:20 +0500 Subject: [PATCH 16/25] Debug test --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index bfbb8a40164..6c058d74222 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -391,7 +391,7 @@ Describe "TabCompletion" -Tags CI { ) { param($cmd, $expected) - $ps.AddScript({ + $null = $ps.AddScript({ param ($cmd) $processList = Get-Process $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd".Length @@ -401,7 +401,7 @@ Describe "TabCompletion" -Tags CI { $result = $ps.Invoke() $ps.Commands.Clear() - +Write-Verbose -Message $result $result | Should -BeExactly $expected } } From c2666469802ce831b2dffb6bc106bb7ee4fc5f0b Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 10:59:49 +0500 Subject: [PATCH 17/25] Debug test 2 --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 6c058d74222..8bcc98cd8e5 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -401,7 +401,7 @@ Describe "TabCompletion" -Tags CI { $result = $ps.Invoke() $ps.Commands.Clear() -Write-Verbose -Message $result +Write-Verbose -Message $result[0] $result | Should -BeExactly $expected } } From d98960f4db1f80c08b551c351b91df5715c69911 Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 11:51:51 +0500 Subject: [PATCH 18/25] Debug test 3 --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 8bcc98cd8e5..e5a56c17de1 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -396,13 +396,14 @@ Describe "TabCompletion" -Tags CI { $processList = Get-Process $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd".Length $completionText = $res.CompletionMatches.CompletionText | Sort-Object - $completionText -join ' ' + $completionText }).AddArgument($cmd) $result = $ps.Invoke() $ps.Commands.Clear() -Write-Verbose -Message $result[0] - $result | Should -BeExactly $expected +$VerbosePreference = "Continue" +$result | out-string | write-verbose + $result -join ' ' | Should -BeExactly $expected } } From 9ca09af339455ce8ade672a1c18b913d73e0629e Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 13:14:45 +0500 Subject: [PATCH 19/25] Debug test 4 --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index e5a56c17de1..bd08321da82 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -403,6 +403,9 @@ Describe "TabCompletion" -Tags CI { $ps.Commands.Clear() $VerbosePreference = "Continue" $result | out-string | write-verbose +$result | Sort-Object | out-string | write-verbose +$result | out-string | Sort-Object | write-verbose + $result -join ' ' | Should -BeExactly $expected } } From a1b94e291375046c35633979148375d913a5136d Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 13:47:43 +0500 Subject: [PATCH 20/25] Debug test 5 --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index bd08321da82..244b52be2d5 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -403,8 +403,9 @@ Describe "TabCompletion" -Tags CI { $ps.Commands.Clear() $VerbosePreference = "Continue" $result | out-string | write-verbose -$result | Sort-Object | out-string | write-verbose -$result | out-string | Sort-Object | write-verbose +$result | Sort-Object -Culture "en-US" | out-string | write-verbose +$result | out-string | Sort-Object -Culture "en-US" | write-verbose +Get-Culture | write-verbose $result -join ' ' | Should -BeExactly $expected } From 6252dfaa1f71141e56ce294eb6e36582216a744a Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 18:10:55 +0500 Subject: [PATCH 21/25] Debug test 6 --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 244b52be2d5..c05fba3aace 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -395,7 +395,7 @@ Describe "TabCompletion" -Tags CI { param ($cmd) $processList = Get-Process $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd".Length - $completionText = $res.CompletionMatches.CompletionText | Sort-Object + $completionText = $res.CompletionMatches | Select-Object -ExpandProperty CompletionText | Sort-Object $completionText }).AddArgument($cmd) From e05587a80f5308b2656c3661217e34270d78b2b4 Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 18:58:56 +0500 Subject: [PATCH 22/25] Debug test 7 --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index c05fba3aace..e137ff6bc04 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -395,7 +395,7 @@ Describe "TabCompletion" -Tags CI { param ($cmd) $processList = Get-Process $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd".Length - $completionText = $res.CompletionMatches | Select-Object -ExpandProperty CompletionText | Sort-Object + $completionText = $res.CompletionMatches.CompletionText $completionText }).AddArgument($cmd) @@ -403,8 +403,9 @@ Describe "TabCompletion" -Tags CI { $ps.Commands.Clear() $VerbosePreference = "Continue" $result | out-string | write-verbose -$result | Sort-Object -Culture "en-US" | out-string | write-verbose -$result | out-string | Sort-Object -Culture "en-US" | write-verbose +$result | ForEach-Object { $_ } | Sort-Object -Culture "en-US" | out-string | write-verbose +[array]::Sort($result) +$result | write-verbose Get-Culture | write-verbose $result -join ' ' | Should -BeExactly $expected From 600fe25228ce82e2cf7ffb8b16b05e0d9122d5ea Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 22:47:22 +0500 Subject: [PATCH 23/25] Debug test 8 --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index e137ff6bc04..9d214d9e872 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -407,7 +407,8 @@ $result | ForEach-Object { $_ } | Sort-Object -Culture "en-US" | out-string | wr [array]::Sort($result) $result | write-verbose Get-Culture | write-verbose - +$a="'R A M'", "Priority", "process", "ProcessModule", "ProcessWithUserName", "StartTime" +$a | sort | write-verbose $result -join ' ' | Should -BeExactly $expected } } From 45ef40a32df9915a3beffd01a03cb4def97f672a Mon Sep 17 00:00:00 2001 From: Ilya Date: Sat, 12 Jun 2021 23:28:09 +0500 Subject: [PATCH 24/25] Debug test 9 --- .../Host/TabCompletion/TabCompletion.Tests.ps1 | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 9d214d9e872..990d1dd3b66 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -381,7 +381,7 @@ Describe "TabCompletion" -Tags CI { } It 'Should complete $processList = Get-Process; $processList | ' -TestCases ( - @{ cmd = 'Format-Table -View '; expected = "'R A M' Priority process ProcessModule ProcessWithUserName StartTime" }, + @{ cmd = 'Format-Table -View '; expected = "'R A M'", "Priority", "process", "ProcessModule", "ProcessWithUserName", "StartTime" }, @{ cmd = 'Format-List -View '; expected = '' }, @{ cmd = 'Format-Wide -View '; expected = 'process' }, @{ cmd = 'Format-Custom -View '; expected = '' }, @@ -395,20 +395,13 @@ Describe "TabCompletion" -Tags CI { param ($cmd) $processList = Get-Process $res = TabExpansion2 -inputScript "`$processList | $cmd" -cursorColumn "`$processList | $cmd".Length - $completionText = $res.CompletionMatches.CompletionText + $completionText = $res.CompletionMatches.CompletionText | Sort-Object $completionText }).AddArgument($cmd) $result = $ps.Invoke() $ps.Commands.Clear() -$VerbosePreference = "Continue" -$result | out-string | write-verbose -$result | ForEach-Object { $_ } | Sort-Object -Culture "en-US" | out-string | write-verbose -[array]::Sort($result) -$result | write-verbose -Get-Culture | write-verbose -$a="'R A M'", "Priority", "process", "ProcessModule", "ProcessWithUserName", "StartTime" -$a | sort | write-verbose + $expected = ($expected | Sort-Object) -join ' ' $result -join ' ' | Should -BeExactly $expected } } From dcb2dee7d6265515692d066ece7a247674a0f86a Mon Sep 17 00:00:00 2001 From: Robert Holt Date: Tue, 13 Jul 2021 09:14:38 -0700 Subject: [PATCH 25/25] Update src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs --- .../engine/CommandCompletion/CompletionCompleters.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 04b41ab2234..c0bb4374b85 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -5783,12 +5783,11 @@ private static void CompleteFormatViewByInferredType(CompletionContext context, && viewPattern.IsMatch(viewDefinition.name)) { string completionText = viewDefinition.name; - var quoteInUse = quote == string.Empty && viewDefinition.name.IndexOfAny(s_charactersRequiringQuotes) != -1 ? "'" : quote; - if (quoteInUse == "'") + // If the string is quoted or if it contains characters that need quoting, quote it in single quotes + if (quote != string.Empty || viewDefinition.name.IndexOfAny(s_charactersRequiringQuotes) != -1) { - completionText = completionText.Replace("'", "''"); + completionText = "'" + completionText.Replace("'", "''") + "'"; } - completionText = quoteInUse + completionText + quoteInUse; results.Add(new CompletionResult(completionText, viewDefinition.name, CompletionResultType.Text, viewDefinition.name)); }