vscode-powershell: ValidateSet/Register-ArgumentCompleter/Enum completions does not work correctly

Issue Description

I am experiencing a problem with… completions failing for 2nd entry.

ThisWayWeird

Notice that it works on 14th line, and then very same command fails on line 10th.

function Get-GPOZaurrPermissionSummary {
    [cmdletBinding()]
    param(
        [validateSet('AuthenticatedUsers', 'DomainComputers', 'Unknown', 'WellKnownAdministrative', 'NotWellKnown', 'NotWellKnownAdministrative', 'NotAdministrative', 'Administrative', 'All')][string[]] $Type = 'All',
        [validateSet('Allow', 'Deny', 'All')][string] $PermitType = 'All',
        [ValidateSet('GpoApply', 'GpoEdit', 'GpoCustom', 'GpoEditDeleteModifySecurity', 'GpoRead', 'GpoOwner', 'GpoCustomCreate', 'GpoCustomOwner')][string[]] $IncludePermissionType,
        [ValidateSet('GpoApply', 'GpoEdit', 'GpoCustom', 'GpoEditDeleteModifySecurity', 'GpoRead', 'GpoOwner', 'GpoCustomCreate', 'GpoCustomOwner')][string[]] $ExcludePermissionType,
        [Microsoft.GroupPolicy.GPPermissionType[]] $Test,

        [alias('ForestName')][string] $Forest,
        [string[]] $ExcludeDomains,
        [alias('Domain', 'Domains')][string[]] $IncludeDomains,
        [System.Collections.IDictionary] $ExtendedForestInformation,

        [string] $Separator
    )

I tried to replicate it to give you reproducible code but I just can’t. I’m experiencing it across all my modules and I can’t pinpoint it to what I’m doing wrong. It fails the same way for both [Microsoft.GroupPolicy.GPPermissionType[]] which is an enum, but also have the same issue with ValidateSet and Register-ArgumentCompleter that makes me want to cry 😃

Attached Logs

1601583262-5511c227-6bca-4824-9bad-6f29a894ed501601499246405.zip

Follow the instructions in the README about capturing and sending logs.

Environment Information

Visual Studio Code

Name Version
Operating System Windows_NT x64 10.0.19042
VSCode 1.49.2
PowerShell Extension Version 2020.9.0

PowerShell Information

Name Value
PSVersion 5.1.19041.541
PSEdition Desktop
PSCompatibleVersions 1.0 2.0 3.0 4.0 5.0 5.1.19041.541
BuildVersion 10.0.19041.541
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1

Visual Studio Code Extensions

Visual Studio Code Extensions(Click to Expand)
Extension Author Version
better-toml bungcip 0.3.2
csharp ms-dotnettools 1.23.2
errorlens usernamehw 3.2.2
github-linker gimenete 0.2.3
gitlens eamodio 10.2.2
line-endings steditor 1.0.3
LogFileHighlighter emilast 2.9.0
markdown-all-in-one yzhang 3.3.0
material-icon-theme PKief 4.3.0
open-in-browser techer 2.0.0
powershell-preview ms-vscode 2020.9.0
project-manager alefragnani 11.3.0
rainbow-brackets 2gua 0.0.6
run-in-powershell tobysmith568 1.1.0
swdc-vscode softwaredotcom 2.3.12
vscode-markdownlint DavidAnson 0.37.0
vscode-toggle-quotes BriteSnow 0.3.3
vscode-wakatime WakaTime 4.0.8
vscode-yaml redhat 0.11.1
xml DotJoshJohnson 2.5.1

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Comments: 25 (12 by maintainers)

Most upvoted comments

Also here’s an editor command you can use to test if TabExpansion2 gives the same result:

Microsoft.VSCode_profile.ps1
Register-EditorCommand -DisplayName 'Show Engine Completion Results' -Name EditorTabExpansion2 -ScriptBlock {
    param(
        [Microsoft.PowerShell.EditorServices.Extensions.EditorContext, Microsoft.PowerShell.EditorServices]
        $Context
    )
    end {
        function GetLineMap {
            [OutputType([int[]])]
            param([string] $text)
            end {
                $lineStartMap = [System.Collections.Generic.List[int]]::new(100)
                $lineStartMap.Add(0)
                for ($i = 0; $i -lt $text.Length; ++$i) {
                    $c = $text[$i];
    
                    if ($c -eq "`r"[0]) {
                        if (($i + 1) -lt $text.Length -and $text[$i + 1] -eq "`n"[0]) {
                            $i++;
                        }
    
                        $lineStartMap.Add($i + 1)
                    }
    
                    if ($c -eq "`n") {
                        $lineStartMap.Add($i + 1);
                    }
                }
    
                return ,$lineStartMap.ToArray()
            }
        }

        $fileContext = $Context.CurrentFile
        $scriptAst = $fileContext.Ast
        $scriptTokens = $fileContext.Tokens

        $scriptText = $scriptAst.Extent.Text
        $lineStartMap = GetLineMap $scriptText
        $offset = ($lineStartMap[$Context.CursorPosition.Line - 1] + $Context.CursorPosition.Column) - 1
        $newPosition = $scriptAst.Extent.StartScriptPosition.GetType().
            GetMethod(
                <# name: #> 'CloneWithNewOffset',
                <# bindingAttr: #> [System.Reflection.BindingFlags]'NonPublic, Public, Instance',
                <# binder: #> $null,
                <# types: #> [type[]][int],
                <# modifiers: #> $null).
            Invoke($scriptAst.Extent.StartScriptPosition, @($offset))

        $completionMatches = [System.Management.Automation.CommandCompletion]::
            CompleteInput($scriptAst, $scriptTokens, $newPosition, @{}).
            CompletionMatches

        if (-not $completionMatches) {
            Write-Host
            Write-Host 'CompleteInput returned no matches.'
            return
        }

        $completionMatches | Out-Host
    }
}
keybindings.json
    {
        "key": "ctrl+shift+space",
        "command": "PowerShell.InvokeRegisteredEditorCommand",
        "args": { "commandName": "EditorTabExpansion2" },
        "when": "editorTextFocus && editorLangId == 'powershell'",
    },

COol 😄 I will be using the comma workaround in near future.

FYI I updated the editor command above and added a key bind of <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Space</kbd>. Hopefully that makes it a little easier to test this kind of thing.

Also, another work around is adding a pipe, e.g.

# This works, remove the | and it doesn't.
New-Function -Test1 New,<tab> |
somethingelsehere

Doesn’t work for me:

image

Edit… oh it works

image

I guess I could use it that way 💯

As a work around, it seems like if you type an extra <kbd>,</kbd> and tab complete before it it works.

e.g. New-Function -Test1 New -Test2 Other,, > <kbd>Left Arrow</kbd> > <kbd>Tab</kbd>

@SeeminglyScience - well

image

Nothing shows up on line 8

image

@PrzemyslawKlys in the ISE example you showed, the <kbd>,</kbd> character is being used as line continuation. If you look at the syntax highlighting in the ISE you’ll see the second line is being treated as input to the first command.

@vexx32 IndexOf is putting the cursor index at <here>-Test2

Should I open it up in PowerShell repo? as it seems PS5.1 / 7.1 as well.

Quick and dirty repro:

$script = @'
function New-Function {
        [cmdletBinding()]
        param(
            [validateSet('New', 'Old', 'Other')][string[]] $Test1,
            [validateSet('New', 'Old', 'Other')][string[]] $Test2
        )
    }
    New-Function -Test1 New -Test2 
    New-Function -Test1 New, Old -Test2 New
    New-Function -Test1 New, Old -Test2 New
'@

$cursorColumn = $script.IndexOf('-Test2 ')
TabExpansion2 -inputScript $script -cursorColumn $cursorColumn | % completionmatches

The symptom seems to show up essentially when you have the same function used later on in the script, and then you return to an earlier function call to the same function and start to tab complete it. It reproduces in a completely separate console as well, safe to say it’s not the VS Code extension itself.

The completions I get from this are the generic “here’s a list of files from the current dir” sort of completions, completely ignoring the validateset results or not finding them for some reason.

Looks like it’s down to something in PS. 😞

Thanks @PrzemyslawKlys we are trying to determine whether this issue is occurring in PowerShell or in Editor Services, but it is a bit difficult for us to check without the full program. If you take this program and find the offset of where your completion is in relation to the script, you should be able to use tabexpansion2 to determine if the error in completion reproduces.


TabExpansion2 -inputScript @'
import-module MyModule
get-process -Name
get-childitem
'@ -cursorColumn 41 | %{​​​​ $_.CompletionMatches }​​​​