PowerShell cmdlet parameter value tab completion

后端 未结 3 1178
暖寄归人
暖寄归人 2020-12-30 06:40

How do you implement the parameter tab completion for PowerShell functions or cmdlets like Get-Service and Get-Process in PowerShell 3.0?

I realise ValidateSet works

相关标签:
3条回答
  • 2020-12-30 07:09

    Classically, I used regex.

    for example,

    function TabExpansion {
    
        param($line, $lastWord) 
    
        if ( $line -match '(-(\w+))\s+([^-]*$)' )
        {
        ### Resolve Command name & parameter name
            $_param = $matches[2] + '*'
            $_opt = $Matches[3].Split(" ,")[-1] + '*'
            $_base = $Matches[3].Substring(0,$Matches[3].Length-$Matches[3].Split(" ,")[-1].length)
    
            $_cmdlet = [regex]::Split($line, '[|;=]')[-1]
    
            if ($_cmdlet -match '\{([^\{\}]*)$')
            {
                $_cmdlet = $matches[1]
            }
    
            if ($_cmdlet -match '\(([^()]*)$')
            {
                $_cmdlet = $matches[1]
            }
    
            $_cmdlet = $_cmdlet.Trim().Split()[0]
    
            $_cmdlet = @(Get-Command -type 'Cmdlet,Alias,Function,Filter,ExternalScript' $_cmdlet)[0]
    
            while ($_cmdlet.CommandType -eq 'alias')
            {
                $_cmdlet = @(Get-Command -type 'Cmdlet,Alias,Function,Filter,ExternalScript' $_cmdlet.Definition)[0]
            }
    
        ### Currently target is Get-Alias & "-Name" parameter
    
            if ( "Get-Alias" -eq $_cmdlet.Name -and "Name" -like $_param )
            {
               Get-Alias -Name $_opt | % { $_.Name } | sort | % { $_base + ($_ -replace '\s','` ') }
               break;
            }
        }
    }
    

    Reference http://gallery.technet.microsoft.com/scriptcenter/005d8bc7-5163-4a25-ad0d-25cffa90faf5


    Posh-git renames TabExpansion to TabExpansionBackup in GitTabExpansion.ps1.
    And posh-git's redifined TabExpansion calls original TabExpansion(TabExpansionBackup) when completions don't match with git commands.
    So all you have to do is redefine TabExpansionBackup.

    (cat .\GitTabExpansion.ps1 | select -last 18)
    ============================== GitTabExpansion.ps1 ==============================

    if (Test-Path Function:\TabExpansion) {
        Rename-Item Function:\TabExpansion TabExpansionBackup
    }
    
    function TabExpansion($line, $lastWord) {
        $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart()
    
        switch -regex ($lastBlock) {
            # Execute git tab completion for all git-related commands
            "^$(Get-AliasPattern git) (.*)" { GitTabExpansion $lastBlock }
            "^$(Get-AliasPattern tgit) (.*)" { GitTabExpansion $lastBlock }
    
            # Fall back on existing tab expansion
            default { if (Test-Path Function:\TabExpansionBackup) { TabExpansionBackup $line $lastWord } }
        }
    }
    

    ===============================================================================

    Redefine TabExpansionBackup(original TabExpansion)

    function TabExpansionBackup {
        ...
    
        ### Resolve Command name & parameter name
    
        ...
    
        ### Currently target is Get-Alias & "-Name" parameter
    
        ...
    }
    
    0 讨论(0)
  • 2020-12-30 07:31

    I puzzled over this for a while, because I wanted to do the same thing. I put together something that I'm really happy with.

    You can add ValidateSet attributes from a DynamicParam. Here's an example of where I've generated my ValidateSet on-the-fly from an xml file. See the "ValidateSetAttribute" in the following code:

    function Foo() {
        [CmdletBinding()]
        Param ()
        DynamicParam {
            #
            # The "modules" param
            #
            $modulesAttributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
    
            # [parameter(mandatory=...,
            #     ...
            # )]
            $modulesParameterAttribute = new-object System.Management.Automation.ParameterAttribute
            $modulesParameterAttribute.Mandatory = $true
            $modulesParameterAttribute.HelpMessage = "Enter one or more module names, separated by commas"
            $modulesAttributeCollection.Add($modulesParameterAttribute)    
    
            # [ValidateSet[(...)]
            $moduleNames = @()
            foreach($moduleXmlInfo in Select-Xml -Path "C:\Path\to\my\xmlFile.xml" -XPath "//enlistment[@name=""wp""]/module") {
                $moduleNames += $moduleXmlInfo.Node.Attributes["name"].Value
            }
            $modulesValidateSetAttribute = New-Object -type System.Management.Automation.ValidateSetAttribute($moduleNames)
            $modulesAttributeCollection.Add($modulesValidateSetAttribute)
    
            # Remaining boilerplate
            $modulesRuntimeDefinedParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("modules", [String[]], $modulesAttributeCollection)
    
            $paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
            $paramDictionary.Add("modules", $modulesRuntimeDefinedParam)
            return $paramDictionary
        }
        process {
            # Do stuff
        }
    }
    

    With that, I can type

    Foo -modules M<press tab>
    

    and it will tab-complete "MarcusModule" if that module was in the XML file. Furthermore, I can edit the XML file and the tab-completion behavior will immediately change; you don't have to re-import the function.

    0 讨论(0)
  • 2020-12-30 07:36

    Check the TabExpansionPlusPlus module on github, written by a former PowerShell team magician.

    https://github.com/lzybkr/TabExpansionPlusPlus#readme

    0 讨论(0)
提交回复
热议问题