UsefulArgumentCompleters.psm1

using namespace System
using namespace System.Collections.Generic
using namespace System.Management.Automation
using namespace System.Management.Automation.Language
class CompletionHelper
{
    # Chars that PowerShell treats as quote chars
    hidden static [char[]] $QuoteChars = [char[]]@(
        [char]"'"
        [char]'"'
        [char]0x2018 # left single quotation mark
        [char]0x2019 # right single quotation mark
        [char]0x201a # single low-9 quotation mark
        [char]0x201b # single high-reversed-9 quotation mark
        [char]0x201c # left double quotation mark
        [char]0x201d # right double quotation mark
        [char]0x201E # low double left quote used in german.
    )

    # Chars that cannot be used at the start of a barequote string
    hidden static [char[]] $BadStartChars = [char[]]@(
        [char]'<'
        [char]'>'
        [char]'#'
        [char]'&'
        [char]'-'
        [char]0x2013 # EnDash
        [char]0x2014 # EmDash
        [char]0x2015 # HorizontalBar
    )

    # Chars that cause issues when used anywhere in a barequote string
    hidden static [char[]] $BadChars = [char[]]@(
        [char]'$'
        [char]'`' # Backtick
        [char]' ' # Space
        [char]'"'
        [char]','
        [char]';'
        [char]'('
        [char]')'
        [char]'{'
        [char]'}'
        [CompletionHelper]::QuoteChars
    )

    # Cache for saving expensive or static object collections used for completions
    hidden static [Dictionary[string,Object[]]] $ObjectCache = ([Dictionary[string,Object[]]]::new([System.StringComparer]::OrdinalIgnoreCase))

    static [string] AddQuotesIfNeeded([string] $CompletionText)
    {
        $Result = if ($CompletionText.Contains("'"))
        {
            "'$($CompletionText.Replace("'","''"))'"
        }
        elseif ($CompletionText.IndexOfAny([CompletionHelper]::BadStartChars) -eq 0 -or $CompletionText.IndexOfAny([CompletionHelper]::BadChars) -ne -1)
        {
            "'$CompletionText'"
        }
        else
        {
            $CompletionText
        }
         return $Result
    }

    static [string] TrimQuotes([string] $Text)
    {
        return $Text.Trim([CompletionHelper]::QuoteChars)
    }

    static [CompletionResult] NewParamCompletionResult([string] $Text)
    {
        return [CompletionResult]::new(
            [CompletionHelper]::AddQuotesIfNeeded($Text),
            $Text,
            [CompletionResultType]::ParameterValue,
            $Text
        )
    }

    static [CompletionResult] NewParamCompletionResult([string] $Text, [string]$ToolTip)
    {
        return [CompletionResult]::new(
            [CompletionHelper]::AddQuotesIfNeeded($Text),
            $Text,
            [CompletionResultType]::ParameterValue,
            $ToolTip
        )
    }

    # Validates that the string is a valid PowerShell command with no nested expressions
    hidden static [bool] CommandIsSafe([string] $Command)
    {
        $ParsedTokens = $null
        $Errors = $null
        $null = [Parser]::ParseInput($Command, [ref]$ParsedTokens, [ref]$Errors)
        if ($Errors.Count -gt 0)
        {
            return $false
        }
        foreach ($Token in $ParsedTokens)
        {
            if
            (
                $Token -isnot [StringLiteralToken] -and
                $Token -isnot [ParameterToken] -and
                $Token.Kind -ne [TokenKind]::EndOfInput -and
                $Token.Kind -ne [TokenKind]::Identifier
            )
            {
                return $false
            }
        }
        return $true
    }

    static [Object[]] GetCachedResults([string] $Command, [bool] $ValidateInput)
    {
        $Result = $null
        if ([CompletionHelper]::ObjectCache.TryGetValue($Command, [ref] $Result))
        {
            return $Result
        }
        $Result = if (!$ValidateInput -or [CompletionHelper]::CommandIsSafe($Command))
        {
            try
            {
                Invoke-Expression -Command $Command
            }
            catch
            {
                return $null
            }
        }
        else
        {
            return $null
        } 
        [CompletionHelper]::ObjectCache.Add($Command, $Result)
        return $Result
    }
}
Register-ArgumentCompleter -CommandName Get-ADUser,Get-ADComputer,Get-ADGroup,Get-ADOrganizationalUnit,Get-ADServiceAccount,Get-ADReplicationSite -ParameterName Properties -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $Values = @{
        'Get-ADUser' = @(
            'AccountExpirationDate'
            'accountExpires'
            'AccountLockoutTime'
            'AccountNotDelegated'
            'adminCount'
            'AllowReversiblePasswordEncryption'
            'AuthenticationPolicy'
            'AuthenticationPolicySilo'
            'BadLogonCount'
            'badPasswordTime'
            'badPwdCount'
            'c'
            'CannotChangePassword'
            'CanonicalName'
            'Certificates'
            'City'
            'CN'
            'co'
            'codePage'
            'Company'
            'CompoundIdentitySupported'
            'Country'
            'countryCode'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Department'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'Division'
            'DoesNotRequirePreAuth'
            'dSCorePropagationData'
            'EmailAddress'
            'EmployeeID'
            'EmployeeNumber'
            'Enabled'
            'Fax'
            'garbageCollPeriod'
            'GivenName'
            'HomeDirectory'
            'HomedirRequired'
            'HomeDrive'
            'HomePage'
            'HomePhone'
            'Initials'
            'instanceType'
            'internetEncoding'
            'isDeleted'
            'KerberosEncryptionType'
            'l'
            'LastBadPasswordAttempt'
            'LastKnownParent'
            'lastLogoff'
            'lastLogon'
            'LastLogonDate'
            'lastLogonTimestamp'
            'legacyExchangeDN'
            'LockedOut'
            'lockoutTime'
            'logonCount'
            'LogonWorkstations'
            'mail'
            'mailNickname'
            'Manager'
            'MemberOf'
            'MNSLogonAccount'
            'mobile'
            'MobilePhone'
            'Modified'
            'modifyTimeStamp'
            'mS-DS-ConsistencyGuid'
            'msDS-ExternalDirectoryObjectId'
            'msDS-KeyCredentialLink'
            'msDS-User-Account-Control-Computed'
            'msExchAddressBookFlags'
            'msExchArchiveQuota'
            'msExchArchiveWarnQuota'
            'msExchBypassAudit'
            'msExchCalendarLoggingQuota'
            'msExchDumpsterQuota'
            'msExchDumpsterWarningQuota'
            'msExchGroupSecurityFlags'
            'msExchMailboxAuditEnable'
            'msExchMailboxAuditLogAgeLimit'
            'msExchMailboxFolderSet'
            'msExchMDBRulesQuota'
            'msExchModerationFlags'
            'msExchPoliciesIncluded'
            'msExchProvisioningFlags'
            'msExchRecipientDisplayType'
            'msExchRecipientSoftDeletedStatus'
            'msExchRecipientTypeDetails'
            'msExchRemoteRecipientType'
            'msExchSafeSendersHash'
            'msExchTransportRecipientSettingsFlags'
            'msExchUMDtmfMap'
            'msExchUMEnabledFlags2'
            'msExchUserAccountControl'
            'msExchVersion'
            'msRTCSIP-DeploymentLocator'
            'msRTCSIP-FederationEnabled'
            'msRTCSIP-InternetAccessEnabled'
            'msRTCSIP-Line'
            'msRTCSIP-OptionFlags'
            'msRTCSIP-PrimaryUserAddress'
            'msRTCSIP-UserEnabled'
            'msTSExpireDate'
            'msTSLicenseVersion'
            'msTSLicenseVersion2'
            'msTSLicenseVersion3'
            'msTSManagingLS'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'objectSid'
            'Office'
            'OfficePhone'
            'Organization'
            'OtherName'
            'PasswordExpired'
            'PasswordLastSet'
            'PasswordNeverExpires'
            'PasswordNotRequired'
            'physicalDeliveryOfficeName'
            'POBox'
            'PostalCode'
            'PrimaryGroup'
            'primaryGroupID'
            'PrincipalsAllowedToDelegateToAccount'
            'ProfilePath'
            'ProtectedFromAccidentalDeletion'
            'protocolSettings'
            'proxyAddresses'
            'pwdLastSet'
            'SamAccountName'
            'sAMAccountType'
            'ScriptPath'
            'sDRightsEffective'
            'ServicePrincipalNames'
            'showInAddressBook'
            'SID'
            'SIDHistory'
            'SmartcardLogonRequired'
            'sn'
            'State'
            'StreetAddress'
            'Surname'
            'targetAddress'
            'telephoneNumber'
            'Title'
            'TrustedForDelegation'
            'TrustedToAuthForDelegation'
            'UseDESKeyOnly'
            'userAccountControl'
            'userCertificate'
            'UserPrincipalName'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
        )
        'Get-ADComputer' = @(
            'AccountExpirationDate'
            'accountExpires'
            'AccountLockoutTime'
            'AccountNotDelegated'
            'AllowReversiblePasswordEncryption'
            'AuthenticationPolicy'
            'AuthenticationPolicySilo'
            'BadLogonCount'
            'badPasswordTime'
            'badPwdCount'
            'CannotChangePassword'
            'CanonicalName'
            'Certificates'
            'CN'
            'codePage'
            'CompoundIdentitySupported'
            'countryCode'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'DNSHostName'
            'DoesNotRequirePreAuth'
            'dSCorePropagationData'
            'Enabled'
            'HomedirRequired'
            'HomePage'
            'instanceType'
            'IPv4Address'
            'IPv6Address'
            'isCriticalSystemObject'
            'isDeleted'
            'KerberosEncryptionType'
            'LastBadPasswordAttempt'
            'LastKnownParent'
            'lastLogoff'
            'lastLogon'
            'LastLogonDate'
            'lastLogonTimestamp'
            'localPolicyFlags'
            'Location'
            'LockedOut'
            'logonCount'
            'ManagedBy'
            'MemberOf'
            'MNSLogonAccount'
            'Modified'
            'modifyTimeStamp'
            'ms-Mcs-AdmPwdExpirationTime'
            'msDS-SupportedEncryptionTypes'
            'msDS-User-Account-Control-Computed'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'objectSid'
            'OperatingSystem'
            'OperatingSystemHotfix'
            'OperatingSystemServicePack'
            'OperatingSystemVersion'
            'PasswordExpired'
            'PasswordLastSet'
            'PasswordNeverExpires'
            'PasswordNotRequired'
            'PrimaryGroup'
            'primaryGroupID'
            'PrincipalsAllowedToDelegateToAccount'
            'ProtectedFromAccidentalDeletion'
            'PSShowComputerName'
            'pwdLastSet'
            'SamAccountName'
            'sAMAccountType'
            'sDRightsEffective'
            'ServiceAccount'
            'servicePrincipalName'
            'ServicePrincipalNames'
            'SID'
            'SIDHistory'
            'TrustedForDelegation'
            'TrustedToAuthForDelegation'
            'UseDESKeyOnly'
            'userAccountControl'
            'userCertificate'
            'UserPrincipalName'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
        )
        'Get-ADGroup' = @(
            'adminCount'
            'CanonicalName'
            'CN'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'dSCorePropagationData'
            'GroupCategory'
            'GroupScope'
            'groupType'
            'HomePage'
            'instanceType'
            'isCriticalSystemObject'
            'isDeleted'
            'LastKnownParent'
            'ManagedBy'
            'member'
            'MemberOf'
            'Members'
            'Modified'
            'modifyTimeStamp'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'objectSid'
            'ProtectedFromAccidentalDeletion'
            'SamAccountName'
            'sAMAccountType'
            'sDRightsEffective'
            'SID'
            'SIDHistory'
            'systemFlags'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
        )
        'Get-ADOrganizationalUnit' = @(
            'CanonicalName'
            'City'
            'CN'
            'Country'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'dSCorePropagationData'
            'instanceType'
            'isDeleted'
            'LastKnownParent'
            'LinkedGroupPolicyObjects'
            'ManagedBy'
            'Modified'
            'modifyTimeStamp'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'ou'
            'PostalCode'
            'ProtectedFromAccidentalDeletion'
            'sDRightsEffective'
            'State'
            'StreetAddress'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
        )
        'Get-ADServiceAccount' = @(
            'AccountExpirationDate'
            'accountExpires'
            'AccountLockoutTime'
            'AccountNotDelegated'
            'AllowReversiblePasswordEncryption'
            'AuthenticationPolicy'
            'AuthenticationPolicySilo'
            'BadLogonCount'
            'badPasswordTime'
            'badPwdCount'
            'CannotChangePassword'
            'CanonicalName'
            'Certificates'
            'CN'
            'codePage'
            'CompoundIdentitySupported'
            'countryCode'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'DNSHostName'
            'DoesNotRequirePreAuth'
            'dSCorePropagationData'
            'Enabled'
            'HomedirRequired'
            'HomePage'
            'HostComputers'
            'instanceType'
            'isCriticalSystemObject'
            'isDeleted'
            'KerberosEncryptionType'
            'LastBadPasswordAttempt'
            'LastKnownParent'
            'lastLogoff'
            'lastLogon'
            'LastLogonDate'
            'lastLogonTimestamp'
            'localPolicyFlags'
            'LockedOut'
            'logonCount'
            'ManagedPasswordIntervalInDays'
            'MemberOf'
            'MNSLogonAccount'
            'Modified'
            'modifyTimeStamp'
            'msDS-GroupMSAMembership'
            'msDS-ManagedPasswordId'
            'msDS-ManagedPasswordInterval'
            'msDS-ManagedPasswordPreviousId'
            'msDS-SupportedEncryptionTypes'
            'msDS-User-Account-Control-Computed'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'objectSid'
            'PasswordExpired'
            'PasswordLastSet'
            'PasswordNeverExpires'
            'PasswordNotRequired'
            'PrimaryGroup'
            'primaryGroupID'
            'PrincipalsAllowedToDelegateToAccount'
            'PrincipalsAllowedToRetrieveManagedPassword'
            'ProtectedFromAccidentalDeletion'
            'pwdLastSet'
            'SamAccountName'
            'sAMAccountType'
            'sDRightsEffective'
            'ServicePrincipalNames'
            'SID'
            'SIDHistory'
            'TrustedForDelegation'
            'TrustedToAuthForDelegation'
            'UseDESKeyOnly'
            'userAccountControl'
            'userCertificate'
            'UserPrincipalName'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
        )
        'Get-ADReplicationSite' = @(
            'AutomaticInterSiteTopologyGenerationEnabled'
            'AutomaticTopologyGenerationEnabled'
            'CanonicalName'
            'CN'
            'Created'
            'createTimeStamp'
            'Deleted'
            'Description'
            'DisplayName'
            'DistinguishedName'
            'dSCorePropagationData'
            'instanceType'
            'InterSiteTopologyGenerator'
            'isDeleted'
            'LastKnownParent'
            'ManagedBy'
            'Modified'
            'modifyTimeStamp'
            'msExchServerSiteBL'
            'Name'
            'nTSecurityDescriptor'
            'ObjectCategory'
            'ObjectClass'
            'ObjectGUID'
            'ProtectedFromAccidentalDeletion'
            'RedundantServerTopologyEnabled'
            'ReplicationSchedule'
            'ScheduleHashingEnabled'
            'sDRightsEffective'
            'showInAdvancedViewOnly'
            'Subnets'
            'systemFlags'
            'TopologyCleanupEnabled'
            'TopologyDetectStaleEnabled'
            'TopologyMinimumHopsEnabled'
            'UniversalGroupCachingEnabled'
            'UniversalGroupCachingRefreshSite'
            'uSNChanged'
            'uSNCreated'
            'whenChanged'
            'whenCreated'
            'WindowsServer2000BridgeheadSelectionMethodEnabled'
            'WindowsServer2000KCCISTGSelectionBehaviorEnabled'
            'WindowsServer2003KCCBehaviorEnabled'
            'WindowsServer2003KCCIgnoreScheduleEnabled'
            'WindowsServer2003KCCSiteLinkBridgingEnabled'
        )
    }
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Item in $Values[$commandName])
    {
        if ($Item -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Item)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Package in [CompletionHelper]::GetCachedResults('Get-AppxPackage | Sort-Object -Property Name', $false))
    {
        if ($null -eq $Package)
        {
            continue
        }

        $MatchText = if ($parameterName -eq "Package")
        {
            $Package.PackageFullName
        }
        else
        {
            $Package.Name
        }

        if ($MatchText -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($MatchText, $Package.Name)
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Name    -CommandName Get-AppxPackage
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Package -CommandName Get-AppxPackageManifest,Move-AppxPackage,Remove-AppxPackage,Reset-AppxPackage
Register-ArgumentCompleter -CommandName Get-AppxPackage -ParameterName Publisher -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Publisher in [CompletionHelper]::GetCachedResults('Get-AppxPackage | Select-Object -ExpandProperty Publisher | Sort-Object -Unique', $false))
    {
        if ($null -eq $Publisher)
        {
            continue
        }

        if ($Publisher -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Publisher)
        }
    }
}
Register-ArgumentCompleter -CommandName Get-ComputerInfo -ParameterName Property -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Property in [Microsoft.PowerShell.Commands.ComputerInfo].GetProperties() | Sort-Object -Property Name)
    {
        if ($Property.Name -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Property.Name)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Culture in [CompletionHelper]::GetCachedResults('[cultureinfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures)', $false))
    {
        if ($null -eq $Culture)
        {
            continue
        }

        if ($Culture.Name -eq [string]::Empty -and $Culture.DisplayName -like $WildcardInput)
        {
            if ($commandName -in 'Get-InstalledLanguage','Install-Language','Set-SystemPreferredUILanguage','Uninstall-Language')
            {
                continue
            }
            [CompletionResult]::new(
                "''",
                $Culture.DisplayName,
                [CompletionResultType]::ParameterValue,
                $Culture.DisplayName
            )
            continue
        }

        if ($Culture.Name -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Culture.Name, $Culture.DisplayName)
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName UICulture    -CommandName Update-Help,Save-Help,New-PSSessionOption
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Culture      -CommandName New-PSSessionOption
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Language     -CommandName Set-WinUILanguageOverride,Get-InstalledLanguage,Install-Language,Set-SystemPreferredUILanguage,Uninstall-Language
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName SystemLocale -CommandName Set-WinSystemLocale
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName CultureInfo  -CommandName Set-Culture
Register-ArgumentCompleter -CommandName Disconnect-VIServer -ParameterName Server -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Item in $global:DefaultVIServers.Name)
    {
        if ($Item -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Item)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Disk in [CompletionHelper]::GetCachedResults("Get-Disk", $false))
    {
        $MatchText = if ($parameterName -eq "Number" -or $parameterName -eq "DiskNumber")
        {
            [string]$Disk.DiskNumber
        }
        elseif ($parameterName -eq 'UniqueId')
        {
            $Disk.UniqueId
        }
        elseif ($parameterName -eq 'SerialNumber')
        {
            $Disk.SerialNumber
        }
        else
        {
            $Disk.FriendlyName
        }


        if ($MatchText.Length -gt 0 -and $MatchText -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($MatchText, "Number: $($Disk.Number) Model: $($Disk.Model) Location: $($Disk.Location)")
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName SerialNumber -CommandName Get-Disk
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName UniqueId -CommandName @(
    'Clear-Disk'
    'Get-Disk'
    'Initialize-Disk'
    'Set-Disk'
    'Update-Disk'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName FriendlyName -CommandName @(
    'Clear-Disk'
    'Get-Disk'
    'Initialize-Disk'
    'Update-Disk'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Number -CommandName @(
    'Clear-Disk'
    'Get-Disk'
    'Initialize-Disk'
    'Set-Disk'
    'Update-Disk'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName DiskNumber -CommandName @(
    'Add-PartitionAccessPath'
    'Disable-StorageHighAvailability'
    'Enable-StorageHighAvailability'
    'Get-Partition'
    'Get-PartitionSupportedSize'
    'New-Partition'
    'New-Volume'
    'Remove-Partition'
    'Remove-PartitionAccessPath'
    'Resize-Partition'
    'Set-Partition'
)
Register-ArgumentCompleter -CommandName @(
    'Copy-NetFirewallRule'
    'Copy-NetIPsecMainModeCryptoSet'
    'Copy-NetIPsecMainModeRule'
    'Copy-NetIPsecPhase1AuthSet'
    'Copy-NetIPsecPhase2AuthSet'
    'Copy-NetIPsecQuickModeCryptoSet'
    'Copy-NetIPsecRule'
    'Disable-NetFirewallRule'
    'Disable-NetIPsecMainModeRule'
    'Disable-NetIPsecRule'
    'Enable-NetFirewallRule'
    'Enable-NetIPsecMainModeRule'
    'Enable-NetIPsecRule'
    'Get-NetFirewallAddressFilter'
    'Get-NetFirewallApplicationFilter'
    'Get-NetFirewallDynamicKeywordAddress'
    'Get-NetFirewallInterfaceFilter'
    'Get-NetFirewallInterfaceTypeFilter'
    'Get-NetFirewallPortFilter'
    'Get-NetFirewallProfile'
    'Get-NetFirewallRule'
    'Get-NetFirewallSecurityFilter'
    'Get-NetFirewallServiceFilter'
    'Get-NetFirewallSetting'
    'Get-NetIPsecMainModeCryptoSet'
    'Get-NetIPsecMainModeRule'
    'Get-NetIPsecPhase1AuthSet'
    'Get-NetIPsecPhase2AuthSet'
    'Get-NetIPsecQuickModeCryptoSet'
    'Get-NetIPsecRule'
    'New-NetFirewallRule'
    'New-NetIPsecMainModeCryptoSet'
    'New-NetIPsecMainModeRule'
    'New-NetIPsecPhase1AuthSet'
    'New-NetIPsecPhase2AuthSet'
    'New-NetIPsecQuickModeCryptoSet'
    'New-NetIPsecRule'
    'Open-NetGPO'
    'Remove-NetFirewallDynamicKeywordAddress'
    'Remove-NetFirewallRule'
    'Remove-NetIPsecMainModeCryptoSet'
    'Remove-NetIPsecMainModeRule'
    'Remove-NetIPsecPhase1AuthSet'
    'Remove-NetIPsecPhase2AuthSet'
    'Remove-NetIPsecQuickModeCryptoSet'
    'Remove-NetIPsecRule'
    'Rename-NetFirewallRule'
    'Rename-NetIPsecMainModeCryptoSet'
    'Rename-NetIPsecMainModeRule'
    'Rename-NetIPsecPhase1AuthSet'
    'Rename-NetIPsecPhase2AuthSet'
    'Rename-NetIPsecQuickModeCryptoSet'
    'Rename-NetIPsecRule'
    'Set-NetFirewallAddressFilter'
    'Set-NetFirewallApplicationFilter'
    'Set-NetFirewallInterfaceFilter'
    'Set-NetFirewallInterfaceTypeFilter'
    'Set-NetFirewallPortFilter'
    'Set-NetFirewallProfile'
    'Set-NetFirewallRule'
    'Set-NetFirewallSecurityFilter'
    'Set-NetFirewallServiceFilter'
    'Set-NetFirewallSetting'
    'Set-NetIPsecMainModeCryptoSet'
    'Set-NetIPsecMainModeRule'
    'Set-NetIPsecPhase1AuthSet'
    'Set-NetIPsecPhase2AuthSet'
    'Set-NetIPsecQuickModeCryptoSet'
    'Set-NetIPsecRule'
    'Show-NetFirewallRule'
    'Show-NetIPsecRule'
    'Sync-NetIPsecRule'
    'Update-NetIPsecRule'
    'Get-DAPolicyChange'
) -ParameterName PolicyStore -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    $ValidValues = @(
        if ($commandName -ne "Open-NetGPO")
        {
            'PersistentStore'
            'ActiveStore'
            'RSOP'
            'SystemDefaults'
            'StaticServiceStore'
            'ConfigurableServiceStore'
        }
        [CompletionHelper]::GetCachedResults('Get-GPO -All', $false)
    )
    foreach ($PolicyStore in $ValidValues)
    {
        if ($null -eq $PolicyStore)
        {
            continue
        }
        $PolicyStoreName = if ($PolicyStore -is [string])
        {
            $PolicyStore
        }
        else
        {
            "$($PolicyStore.DomainName)\$($PolicyStore.DisplayName)"
        }
        
        if ($PolicyStoreName -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($PolicyStoreName)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    $KnownPorts = @(
        @{Name="Any"                    ;Value="Any"             ;ToolTip='Any port'}
        @{Name="RPC"                    ;Value="RPC"             ;ToolTip='TCP port 49152-65535. RPC dynamic port range.'}
        @{Name="RPCEPMap"               ;Value="RPCEPMap"        ;ToolTip='TCP Port 135. RPC Endpoint mapper.'}
        if ($parameterName -eq "LocalPort")
        {
            @{Name="PlayToDiscovery"    ;Value="PlayToDiscovery" ;ToolTip='PlayToDiscovery'}
            @{Name="Teredo"             ;Value="Teredo"          ;ToolTip='Teredo'}
            @{Name="IPHTTPSIn"          ;Value="IPHTTPSIn"       ;ToolTip='IPHTTPSIn'}
            @{Name="IPHTTPSOut"         ;Value="IPHTTPSOut"      ;ToolTip='IPHTTPSOut'}
        }
        else
        {
            @{Name="IPHTTPS"            ;Value="IPHTTPS"      ;ToolTip='IPHTTPS'}
        }
        @{Name="FTPData"                ;Value="20"              ;ToolTip='TCP Port 20. FTP (File Transfer Protocol) data transfer.'}
        @{Name="FTPControl"             ;Value="21"              ;ToolTip='TCP Port 21. FTP (File Transfer Protocol) control.'}
        @{Name="SSH"                    ;Value="22"              ;ToolTip='TCP port 22. SSH (Secure Shell).'}
        @{Name="SCP"                    ;Value="22"              ;ToolTip='TCP port 22. SCP (Secure Copy).'} #Obviously it's the same as SSH, but it's more convenient to have multiple text options.
        @{Name="SFTP_SSH"               ;Value="22"              ;ToolTip='TCP port 22. SFTP (SSH File Transfer Protocol).'}
        @{Name="Telnet"                 ;Value="23"              ;ToolTip='TCP port 23. Telnet.'}
        @{Name="SMTP"                   ;Value="25"              ;ToolTip='TCP port 25. SMTP (Simple Mail Transfer Protocol).'}
        @{Name="DNS"                    ;Value="53"              ;ToolTip='TCP and UDP port 53. DNS (Domain Name System)'}
        @{Name="DHCPServer"             ;Value="67"              ;ToolTip='UDP port 67. DHCP (Dynamic Host Configuration Protocol) server port'}
        @{Name="DHCPClient"             ;Value="68"              ;ToolTip='UDP port 68. DHCP (Dynamic Host Configuration Protocol) client port'}
        @{Name="TFTP"                   ;Value="69"              ;ToolTip='UDP port 69. TFTP (Trivial File Transfer Protocol)'}
        @{Name="HTTP"                   ;Value="80"              ;ToolTip='TCP and UDP port 80. HTTP (Hypertext Transfer Protocol)'}
        @{Name="Kerberos"               ;Value="88"              ;ToolTip='TCP and UDP port 88. Kerberos authentication.'}
        @{Name="SFTP"                   ;Value="115"             ;ToolTip='TCP port 115. SFTP (Simple File Transfer Protocol)'}
        @{Name="NTP"                    ;Value="123"             ;ToolTip='UDP port 123. NTP (Network Time Protocol)'}
        @{Name="NetBiosNameService"     ;Value="137"             ;ToolTip='TCP and UDP port 137. Netbios name registration and resolution.'}
        @{Name="NetBiosDatagramService" ;Value="138"             ;ToolTip='UDP port 138. Netbios datagram service.'}
        @{Name="NetBiosSessionService"  ;Value="139"             ;ToolTip='TCP port 139. Netbios session service.'}
        @{Name="IMAP"                   ;Value="143"             ;ToolTip='TCP port 143. IMAP (Internet Message Access Protocol).'}
        @{Name="SNMP"                   ;Value="161"             ;ToolTip='UDP port 161. SNMP (Simple Network Management Protocol)'}
        @{Name="SNMPTrap"               ;Value="162"             ;ToolTip='TCP and UDP port 162. SNMP (Simple Network Management Protocol) trap'}
        @{Name="LDAP"                   ;Value="389"             ;ToolTip='TCP port 389. LDAP (Lightweight Directory Access Protocol)'}
        @{Name="HTTPS"                  ;Value="443"             ;ToolTip='TCP and UDP port 443. HTTPS (Hypertext Transfer Protocol Secure)'}
        @{Name="SMB"                    ;Value="445"             ;ToolTip='TCP and UDP port 445. SMB (Server Message Block)'}
        @{Name="KerberosSetPassword"    ;Value="464"             ;ToolTip='TCP and UDP port 464. Kerberos update password.'}
        @{Name="LDAPS"                  ;Value="636"             ;ToolTip='TCP port 636. LDAPS (Lightweight Directory Access Protocol over TLS/SSL)'}
        @{Name="IMAPS"                  ;Value="993"             ;ToolTip='TCP port 993. IMAPS (Internet Message Access Protocol over TLS/SSL)'}
        @{Name="MSSQL"                  ;Value="1433"            ;ToolTip='TCP and UDP port 1433. Default instance MS SQL server port.'}
        @{Name="KMS"                    ;Value="1688"            ;ToolTip='TCP port 1688. Default KMS (Key management service) port.'}
        @{Name="LDAPGC"                 ;Value="3268"            ;ToolTip='TCP and UDP port 3268. LDAP global catalog.'}
        @{Name="LDAPSGC"                ;Value="3269"            ;ToolTip='TCP and UDP port 3269. LDAPS global catalog.'}
        @{Name="RDP"                    ;Value="3389"            ;ToolTip='TCP and UDP port 3389. Default RDP port.'}
        @{Name="WinRM"                  ;Value="5985"            ;ToolTip='TCP port 5985. Default WinRM port.'}
        @{Name="WinRMHttps"             ;Value="5986"            ;ToolTip='TCP port 5986. Default WinRM HTTPs port.'}
    )
    foreach ($Port in $KnownPorts)
    {
        if ($Port['Name'] -like $WildcardInput)
        {
            [CompletionResult]::new(
                $Port['Value'],
                $Port['Name'],
                [CompletionResultType]::ParameterValue,
                $Port['ToolTip']
            )
        }
    }
}
$Commands = @(
    'Find-NetIPsecRule'
    'New-NetFirewallRule'
    'New-NetIPsecRule'
    'Set-NetFirewallPortFilter'
    'Set-NetFirewallRule'
    'Set-NetIPsecRule'
)
Register-ArgumentCompleter -CommandName $Commands -ParameterName LocalPort -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName $Commands -ParameterName RemotePort -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName @(
    'Find-NetIPsecRule'
    'Get-NetFirewallPortFilter'
    'New-NetFirewallRule'
    'New-NetIPsecRule'
    'Set-NetFirewallPortFilter'
    'Set-NetFirewallRule'
    'Set-NetIPsecRule'
) -ParameterName Protocol -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    $KnownProtocols = @(
        @{Name="Any";        Value="Any"}
        @{Name="TCP";        Value="TCP"}
        @{Name="UDP";        Value="UDP"}
        @{Name="ICMPv4";     Value="ICMPv4"}
        @{Name="ICMPv6";     Value="ICMPv6"}
        @{Name="HOPOPT";     Value="0"}
        @{Name="IGMP";       Value="2"}
        @{Name="IPv6";       Value="41"}
        @{Name="IPv6-Route"; Value="43"}
        @{Name="IPv6-Frag";  Value="44"}
        @{Name="GRE";        Value="47"}
        @{Name="IPv6-NoNxt"; Value="59"}
        @{Name="IPv6-Opts";  Value="60"}
        @{Name="VRRP";       Value="112"}
        @{Name="PGM";        Value="113"}
        @{Name="L2TP";       Value="115"}
    )
    foreach ($Protocol in $KnownProtocols)
    {
        if ($Protocol['Name'] -like $WildcardInput)
        {
            [CompletionResult]::new(
                $Protocol['Value'],
                $Protocol['Name'],
                [CompletionResultType]::ParameterValue,
                $Protocol['Value']
            )
        }
    }
}
Register-ArgumentCompleter -CommandName @(
    'Get-NetFirewallServiceFilter'
    'New-NetFirewallRule'
    'Set-NetFirewallRule'
    'Set-NetFirewallServiceFilter'
) -ParameterName Service -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    
    if ("Any" -like $WildcardInput)
    {
        [CompletionHelper]::NewParamCompletionResult("Any")
    }
    foreach ($Service in [CompletionHelper]::GetCachedResults('Get-Service', $false))
    {
        if ($null -eq $Service)
        {
            continue
        }
        if ($Service.Name -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Service.Name, $Service.DisplayName)
        }
    }
}
Register-ArgumentCompleter -CommandName Set-WinHomeLocation -ParameterName GeoId -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    $AllRegions = [CompletionHelper]::GetCachedResults(@'
foreach ($Culture in [cultureinfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures))
{
    if ($Culture.IsNeutralCulture -or $Culture.Name -eq "")
    {
        continue
    }
    [System.Globalization.RegionInfo]::new($Culture.Name)
}
'@
, $false)

    foreach ($RegionInfo in $AllRegions)
    {
        if ($null -eq $RegionInfo)
        {
            continue
        }

        if ($RegionInfo.EnglishName -like $WildcardInput)
        {
            [CompletionResult]::new(
                $RegionInfo.GeoId,
                $RegionInfo.EnglishName,
                [CompletionResultType]::ParameterValue,
                $RegionInfo.EnglishName
            )
        }
    }
}
Register-ArgumentCompleter -CommandName Get-Command -ParameterName ParameterName -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    if ($fakeBoundParameters.ContainsKey('ParameterName'))
    {
        $fakeBoundParameters.Remove('ParameterName')
    }

    if ($fakeBoundParameters.Count -eq 0)
    {
        return
    }

    foreach ($ParameterName in (Get-Command @fakeBoundParameters).Parameters.Keys | Sort-Object -Unique)
    {
        if ($parameterName -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($parameterName)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    $Values = switch ($parameterName)
    {
        'LocalAddress'
        {
            Get-NetTCPConnection -LocalAddress $WildcardInput | Select-Object -ExpandProperty LocalAddress | Sort-Object -Unique
            break
        }
        'RemoteAddress'
        {
            Get-NetTCPConnection -RemoteAddress $WildcardInput | Select-Object -ExpandProperty RemoteAddress | Sort-Object -Unique
            break
        }
        'LocalPort'
        {
            Get-NetTCPConnection | Select-Object -ExpandProperty LocalPort | Sort-Object -Unique
            break
        }
        'RemotePort'
        {
            Get-NetTCPConnection | Select-Object -ExpandProperty RemotePort | Sort-Object -Unique
            break
        }
    }
    foreach ($Value in $Values)
    {
        if ($Value -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Value)
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -CommandName Get-NetTCPConnection -ParameterName LocalAddress
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -CommandName Get-NetTCPConnection -ParameterName RemoteAddress
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -CommandName Get-NetTCPConnection -ParameterName LocalPort
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -CommandName Get-NetTCPConnection -ParameterName RemotePort
Register-ArgumentCompleter -CommandName Get-NetTCPConnection -ParameterName OwningProcess -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Process in Get-Process -Name $WildcardInput)
    {
        $ListItem = "$($Process.Name) $($Process.Id)"
        if ($ListItem -like $WildcardInput)
        {
            [CompletionResult]::new(
                $Process.Id,
                $ListItem,
                [CompletionResultType]::ParameterValue,
                $ListItem
            )
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($TimeZone in [CompletionHelper]::GetCachedResults('Get-TimeZone -ListAvailable', $false))
    {
        if ($null -eq $TimeZone)
        {
            continue
        }
        $MatchText = if ($parameterName -eq "Name")
        {
            $TimeZone.StandardName
        }
        else
        {
            $TimeZone.Id
        }

        if ($MatchText -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($MatchText, $TimeZone.DisplayName)
        }
    }
}
Register-ArgumentCompleter -CommandName Get-TimeZone,Set-TimeZone -ParameterName Name -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Get-TimeZone,Set-TimeZone -ParameterName Id -ScriptBlock $ScriptBlock
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Group in Get-LocalGroup)
    {
        $MatchText = if ($parameterName -eq "Name")
        {
            $Group.Name
        }
        else
        {
            $Group.SID.Value
        }

        if ($MatchText -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($MatchText, "$($Group.Description)")
        }
    }
}
Register-ArgumentCompleter -CommandName Get-LocalGroup,Remove-LocalGroup,Rename-LocalGroup,Set-LocalGroup,Add-LocalGroupMember,Get-LocalGroupMember,Remove-LocalGroupMember -ParameterName Name -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Get-LocalGroup,Remove-LocalGroup,Rename-LocalGroup,Set-LocalGroup,Add-LocalGroupMember,Get-LocalGroupMember,Remove-LocalGroupMember -ParameterName SID -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Add-LocalGroupMember -ParameterName Member -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    $GroupsAndUsers = @(
        Get-LocalGroup -Name $WildcardInput -ErrorAction Ignore
        Get-LocalUser -Name $WildcardInput -ErrorAction Ignore
    )
    foreach ($Item in $GroupsAndUsers)
    {
        [CompletionHelper]::NewParamCompletionResult($Item.Name, $Item.ObjectClass)
    }
}
Register-ArgumentCompleter -CommandName Get-LocalGroupMember,Remove-LocalGroupMember -ParameterName Member -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $GroupMembers = if ($null -ne $fakeBoundParameters['Name'])
    {
        Get-LocalGroupMember -Name $fakeBoundParameters['Name'] -ErrorAction Ignore
    }
    elseif ($null -ne $fakeBoundParameters['SID'])
    {
        Get-LocalGroupMember -SID $fakeBoundParameters['SID'] -ErrorAction Ignore
    }

    if ($null -eq $GroupMembers)
    {
        return
    }

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Member in $GroupMembers)
    {
        if ($Member.Name -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Member.Name, $Member.ObjectClass)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($User in Get-LocalUser)
    {
        $MatchText = if ($parameterName -eq "Name")
        {
            $User.Name
        }
        else
        {
            $User.SID
        }

        if ($MatchText -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($MatchText, "$($User.Name) $($User.FullName)")
        }
    }
}
Register-ArgumentCompleter -CommandName Get-LocalUser,Set-LocalUser,Enable-LocalUser,Disable-LocalUser,Remove-LocalUser,Rename-LocalUser -ParameterName Name -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Get-LocalUser,Set-LocalUser,Enable-LocalUser,Disable-LocalUser,Remove-LocalUser,Rename-LocalUser -ParameterName SID -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Update-Module,Uninstall-Module -ParameterName Name -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Module in [CompletionHelper]::GetCachedResults('Get-Module -ListAvailable | Where-Object -Property RepositorySourceLocation -NE $null | Select-Object -ExpandProperty Name | Sort-Object -Unique', $false))
    {
        if ($null -eq $Module)
        {
            continue
        }
        if ($Module -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Module)
        }
    }
}
Register-ArgumentCompleter -CommandName Find-Module,Install-Module,Save-Module -ParameterName Name -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Module in [CompletionHelper]::GetCachedResults("Import-Csv -Delimiter ',' -LiteralPath '$PSScriptRoot\PSGallery.csv'", $false))
    {
        if ($null -eq $Module)
        {
            continue
        }
        if ($Module.Name -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Module.Name, "CompanyName: $($Module.CompanyName)")
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Adapter in [CompletionHelper]::GetCachedResults("Get-NetAdapter", $false))
    {
        if ($null -eq $Adapter)
        {
            continue
        }
        if ($Adapter.Name -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Adapter.Name, $Adapter.InterfaceDescription)
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName InterfaceAlias -CommandName @(
    'Get-DnsClient'
    'Get-DnsClientServerAddress'
    'Get-NetConnectionProfile'
    'Get-NetIPAddress'
    'Get-NetIPConfiguration'
    'Get-NetIPInterface'
    'Get-NetNeighbor'
    'Get-NetRoute'
    'New-NetFirewallRule'
    'New-NetIPAddress'
    'New-NetIPsecRule'
    'New-NetNeighbor'
    'New-NetRoute'
    'New-SmbMultichannelConstraint'
    'Remove-NetIPAddress'
    'Remove-NetNeighbor'
    'Remove-NetRoute'
    'Remove-SmbMultichannelConstraint'
    'Set-DhcpServerv4Binding'
    'Set-DhcpServerv6Binding'
    'Set-DnsClient'
    'Set-DnsClientServerAddress'
    'Set-NetConnectionProfile'
    'Set-NetFirewallInterfaceFilter'
    'Set-NetFirewallRule'
    'Set-NetIPAddress'
    'Set-NetIPConfiguration'
    'Set-NetIPInterface'
    'Set-NetIPsecRule'
    'Set-NetNeighbor'
    'Set-NetRoute'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Name -CommandName @(
    'Add-NetLbfoTeamMember'
    'Add-NetLbfoTeamNic'
    'Disable-NetAdapter'
    'Disable-NetAdapterBinding'
    'Disable-NetAdapterChecksumOffload'
    'Disable-NetAdapterEncapsulatedPacketTaskOffload'
    'Disable-NetAdapterIPsecOffload'
    'Disable-NetAdapterLso'
    'Disable-NetAdapterPacketDirect'
    'Disable-NetAdapterPowerManagement'
    'Disable-NetAdapterQos'
    'Disable-NetAdapterRdma'
    'Disable-NetAdapterRsc'
    'Disable-NetAdapterRss'
    'Disable-NetAdapterSriov'
    'Disable-NetAdapterUso'
    'Disable-NetAdapterVmq'
    'Enable-NetAdapter'
    'Enable-NetAdapterBinding'
    'Enable-NetAdapterChecksumOffload'
    'Enable-NetAdapterEncapsulatedPacketTaskOffload'
    'Enable-NetAdapterIPsecOffload'
    'Enable-NetAdapterLso'
    'Enable-NetAdapterPacketDirect'
    'Enable-NetAdapterPowerManagement'
    'Enable-NetAdapterQos'
    'Enable-NetAdapterRdma'
    'Enable-NetAdapterRsc'
    'Enable-NetAdapterRss'
    'Enable-NetAdapterSriov'
    'Enable-NetAdapterUso'
    'Enable-NetAdapterVmq'
    'Get-NetAdapter'
    'Get-NetAdapterAdvancedProperty'
    'Get-NetAdapterBinding'
    'Get-NetAdapterChecksumOffload'
    'Get-NetAdapterEncapsulatedPacketTaskOffload'
    'Get-NetAdapterHardwareInfo'
    'Get-NetAdapterIPsecOffload'
    'Get-NetAdapterLso'
    'Get-NetAdapterPacketDirect'
    'Get-NetAdapterPowerManagement'
    'Get-NetAdapterQos'
    'Get-NetAdapterRdma'
    'Get-NetAdapterRsc'
    'Get-NetAdapterRss'
    'Get-NetAdapterSriov'
    'Get-NetAdapterSriovVf'
    'Get-NetAdapterStatistics'
    'Get-NetAdapterUso'
    'Get-NetAdapterVmq'
    'Get-NetAdapterVmqQueue'
    'Get-NetAdapterVPort'
    'Get-NetLbfoTeamMember'
    'Get-NetLbfoTeamNic'
    'New-NetAdapterAdvancedProperty'
    'Remove-NetAdapterAdvancedProperty'
    'Remove-NetLbfoTeamMember'
    'Rename-NetAdapter'
    'Reset-NetAdapterAdvancedProperty'
    'Restart-NetAdapter'
    'Set-NetAdapter'
    'Set-NetAdapterAdvancedProperty'
    'Set-NetAdapterBinding'
    'Set-NetAdapterChecksumOffload'
    'Set-NetAdapterEncapsulatedPacketTaskOffload'
    'Set-NetAdapterIPsecOffload'
    'Set-NetAdapterLso'
    'Set-NetAdapterPacketDirect'
    'Set-NetAdapterPowerManagement'
    'Set-NetAdapterQos'
    'Set-NetAdapterRdma'
    'Set-NetAdapterRsc'
    'Set-NetAdapterRss'
    'Set-NetAdapterSriov'
    'Set-NetAdapterUso'
    'Set-NetAdapterVmq'
    'Set-NetLbfoTeamMember'
    'Set-NetLbfoTeamNic'
)
Register-ArgumentCompleter -CommandName New-Object -ParameterName ComObject -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Item in [CompletionHelper]::GetCachedResults("Get-CimInstance -ClassName Win32_ClassicCOMClassSetting -Filter 'VersionIndependentProgId is not null or ProgId is not null' -Property ProgId, VersionIndependentProgId, Caption", $false))
    {
        $Id = if (![string]::IsNullOrEmpty($Item.VersionIndependentProgId))
        {
            $Item.VersionIndependentProgId
        }
        else
        {
            $Item.ProgId
        }

        $Tooltip = if (![string]::IsNullOrEmpty($Item.Caption))
        {
            $Item.Caption
        }
        else
        {
            $Id
        }

        if ($Id -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Id, $Tooltip)
        }
    }
}
Register-ArgumentCompleter -CommandName New-Partition -ParameterName GptType -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $Types = @(
        @{Name = "Recovery"; Guid="{de94bba4-06d1-4d40-a16a-bfd50179d6ac}"}
        @{Name = "EFI";      Guid="{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}"}
        @{Name = "MSR";      Guid="{e3c9e316-0b5c-4db8-817d-f92df00215ae}"}
        @{Name = "Basic";    Guid="{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"}
    )

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Item in $Types)
    {
        if ($Item['Name'] -like $WildcardInput)
        {
            [CompletionResult]::new(
                "'$($Item['Guid'])'",
                $Item['Name'],
                [CompletionResultType]::ParameterValue,
                $Item['Name']
            )
        }
    }
}
Register-ArgumentCompleter -CommandName Enable-WindowsOptionalFeature,Disable-WindowsOptionalFeature,Get-WindowsOptionalFeature -ParameterName FeatureName -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $FoundFeatures = if ($fakeBoundParameters["Path"])
    {
        [CompletionHelper]::GetCachedResults("Get-WindowsOptionalFeature -Path '$($fakeBoundParameters['Path'])'", $true)
    }
    else
    {
        [CompletionHelper]::GetCachedResults('Get-WindowsOptionalFeature -Online', $false)
    }

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Feature in $FoundFeatures)
    {
        if ($null -eq $Feature)
        {
            continue
        }
        if ($Feature.FeatureName -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Feature.FeatureName)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $CommandToFind = if ($CommandName -eq 'Register-ArgumentCompleter')
    {
        $fakeBoundParameters['CommandName']
    }
    elseif ($CommandName -eq 'Get-Help')
    {
        $fakeBoundParameters['Name']
    }

    if ([string]::IsNullOrEmpty($CommandToFind))
    {
        return
    }

    $CommandInfo = Get-Command -Name $CommandToFind
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Key in $CommandInfo.Parameters.Keys)
    {
        if ($Key -notlike $WildcardInput)
        {
            continue
        }

        [CompletionHelper]::NewParamCompletionResult($Key)
    }
}

Register-ArgumentCompleter -CommandName Register-ArgumentCompleter -ParameterName ParameterName -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Get-Help -ParameterName Parameter -ScriptBlock $ScriptBlock
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Counter in [CompletionHelper]::GetCachedResults("Get-Counter -ListSet * | Sort-Object -Property CounterSetName", $false))
    {
        if ($null -eq $Counter)
        {
            continue
        }
        if ($parameterName -eq "ListSet")
        {
            if ($Counter.CounterSetName -like $WildcardInput)
            {
                [CompletionHelper]::NewParamCompletionResult($Counter.CounterSetName)
            }
        }
        else
        {
            foreach ($Path in $Counter.Paths)
            {
                if ($Path -like $WildcardInput)
                {
                    [CompletionHelper]::NewParamCompletionResult($Path)
                }
            }
        }        
    }
}
Register-ArgumentCompleter -CommandName Get-Counter -ParameterName ListSet -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Get-Counter -ParameterName Counter -ScriptBlock $ScriptBlock
Register-ArgumentCompleter -CommandName Get-PnpDevice -ParameterName Class -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Class in [CompletionHelper]::GetCachedResults("Get-PnpDevice | Select-Object -ExpandProperty Class | Sort-Object -Unique", $false))
    {
        if ($null -eq $Class)
        {
            continue
        }
        if ($Class -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Class)
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Repository in Get-PSRepository -Name $WildcardInput)
    {
        [CompletionHelper]::NewParamCompletionResult($Repository.Name, $Repository.SourceLocation)
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Name -CommandName Set-PSRepository
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($SessConfig in [CompletionHelper]::GetCachedResults("Get-PSSessionConfiguration", $false))
    {
        if ($SessConfig.Name -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($SessConfig.Name)
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Name -CommandName "Disable-PSSessionConfiguration", "Enable-PSSessionConfiguration", "Get-PSSessionConfiguration", "Set-PSSessionConfiguration", "Unregister-PSSessionConfiguration"
Register-ArgumentCompleter -CommandName @(
    'Get-ADComputer'
    'Get-ADFineGrainedPasswordPolicy'
    'Get-ADGroup'
    'Get-ADObject'
    'Get-ADOptionalFeature'
    'Get-ADOrganizationalUnit'
    'Get-ADServiceAccount'
    'Get-ADUser'
    'Search-ADAccount'
) -ParameterName SearchBase -ScriptBlock {
    #This is not actually an argument completer, it's more like a CLI container navigator
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $Prefix = 'Microsoft.ActiveDirectory.Management.dll\ActiveDirectory:://RootDSE/'
    $TrimmedWord = [CompletionHelper]::TrimQuotes($wordToComplete)
    $PathsToSearch = if ($TrimmedWord.Length -eq 0)
    {
        [CompletionHelper]::GetCachedResults('(Get-ADDomain).DistinguishedName', $false) | ForEach-Object -Process {
            if ($null -ne $_)
            {
                $Prefix + $_
            }
        }
    }
    else
    {
        $Prefix + $TrimmedWord.Trim('/','\')
    }

    foreach ($Item in Get-ChildItem -LiteralPath $PathsToSearch)
    {
        if ($Item.PSIsContainer)
        {
            # ParameterValue is used over ProviderContainer because otherwise PSReadLine will add a trailing slash
            [CompletionResult]::new(
                "'$($Item.distinguishedName)'",
                $Item.Name,
                [CompletionResultType]::ParameterValue,
                $Item.DistinguishedName
            )
        }
    }
}
Register-ArgumentCompleter -CommandName Split-WindowsImage -ParameterName FileSize -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    
    $SizeConfigs = @(
        @{Name = "Fat32"; Size="4096"}
    )

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Item in $SizeConfigs)
    {
        if ($Item['Name'] -like $WildcardInput)
        {
            [CompletionResult]::new(
                $Item['Size'],
                $Item['Name'],
                [CompletionResultType]::ParameterValue,
                "$($Item['Size'])MB for $($Item['Name'])"
            )
        }
    }
}
Register-ArgumentCompleter -CommandName Test-NetConnection -ParameterName Port -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    $KnownPorts = @(
        @{Name="FTPData"                ;Value="20"   ;ToolTip='TCP Port 20. FTP (File Transfer Protocol) data transfer.'}
        @{Name="FTPControl"             ;Value="21"   ;ToolTip='TCP Port 21. FTP (File Transfer Protocol) control.'}
        @{Name="SSH"                    ;Value="22"   ;ToolTip='TCP port 22. SSH (Secure Shell).'}
        @{Name="SCP"                    ;Value="22"   ;ToolTip='TCP port 22. SCP (Secure Copy).'} #Obviously it's the same as SSH, but it's more convenient to have multiple text options.
        @{Name="SFTP_SSH"               ;Value="22"   ;ToolTip='TCP port 22. SFTP (SSH File Transfer Protocol).'}
        @{Name="Telnet"                 ;Value="23"   ;ToolTip='TCP port 23. Telnet.'}
        @{Name="SMTP"                   ;Value="25"   ;ToolTip='TCP port 25. SMTP (Simple Mail Transfer Protocol).'}
        @{Name="DNS"                    ;Value="53"   ;ToolTip='TCP and UDP port 53. DNS (Domain Name System)'}
        @{Name="HTTP"                   ;Value="80"   ;ToolTip='TCP and UDP port 80. HTTP (Hypertext Transfer Protocol)'}
        @{Name="Kerberos"               ;Value="88"   ;ToolTip='TCP and UDP port 88. Kerberos authentication.'}
        @{Name="SFTP"                   ;Value="115"  ;ToolTip='TCP port 115. SFTP (Simple File Transfer Protocol)'}
        @{Name="RPCEPMap"               ;Value="135"  ;ToolTip='TCP Port 135. RPC Endpoint mapper.'}
        @{Name="NetBiosNameService"     ;Value="137"  ;ToolTip='TCP and UDP port 137. Netbios name registration and resolution.'}
        @{Name="NetBiosSessionService"  ;Value="139"  ;ToolTip='TCP port 139. Netbios session service.'}
        @{Name="IMAP"                   ;Value="143"  ;ToolTip='TCP port 143. IMAP (Internet Message Access Protocol).'}
        @{Name="SNMPTrap"               ;Value="162"  ;ToolTip='TCP and UDP port 162. SNMP (Simple Network Management Protocol) trap'}
        @{Name="LDAP"                   ;Value="389"  ;ToolTip='TCP port 389. LDAP (Lightweight Directory Access Protocol)'}
        @{Name="HTTPS"                  ;Value="443"  ;ToolTip='TCP and UDP port 443. HTTPS (Hypertext Transfer Protocol Secure)'}
        @{Name="SMB"                    ;Value="445"  ;ToolTip='TCP and UDP port 445. SMB (Server Message Block)'}
        @{Name="KerberosSetPassword"    ;Value="464"  ;ToolTip='TCP and UDP port 464. Kerberos update password.'}
        @{Name="LDAPS"                  ;Value="636"  ;ToolTip='TCP port 636. LDAPS (Lightweight Directory Access Protocol over TLS/SSL)'}
        @{Name="IMAPS"                  ;Value="993"  ;ToolTip='TCP port 993. IMAPS (Internet Message Access Protocol over TLS/SSL)'}
        @{Name="MSSQL"                  ;Value="1433" ;ToolTip='TCP and UDP port 1433. Default instance MS SQL server port.'}
        @{Name="KMS"                    ;Value="1688" ;ToolTip='TCP port 1688. Default KMS (Key management service) port.'}
        @{Name="LDAPGC"                 ;Value="3268" ;ToolTip='TCP and UDP port 3268. LDAP global catalog.'}
        @{Name="LDAPSGC"                ;Value="3269" ;ToolTip='TCP and UDP port 3269. LDAPS global catalog.'}
        @{Name="RDP"                    ;Value="3389" ;ToolTip='TCP and UDP port 3389. Default RDP port.'}
        @{Name="WinRM"                  ;Value="5985" ;ToolTip='TCP port 5985. Default WinRM port.'}
        @{Name="WinRMHttps"             ;Value="5986" ;ToolTip='TCP port 5986. Default WinRM HTTPs port.'}
    )
    foreach ($Port in $KnownPorts)
    {
        if ($Port['Name'] -like $WildcardInput)
        {
            [CompletionResult]::new(
                $Port['Value'],
                $Port['Name'],
                [CompletionResultType]::ParameterValue,
                $Port['ToolTip']
            )
        }
    }
}
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    $FoundVolumes = if ($parameterName -eq "FileSystemLabel")
    {
        Get-Volume -FileSystemLabel $WildcardInput
    }
    else
    {
        Get-Volume
    }

    foreach ($Volume in $FoundVolumes)
    {
        $MatchText = if ($parameterName -eq "FileSystemLabel")
        {
            $Volume.FileSystemLabel
        }
        else
        {
            [string]$Volume.DriveLetter
        }

        if ($MatchText.Length -gt 0 -and $MatchText -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($MatchText, $Volume.FileSystemLabel)
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName FileSystemLabel -CommandName @(
    'Debug-Volume'
    'Format-Volume'
    'Get-DedupProperties'
    'Get-SupportedClusterSizes'
    'Get-SupportedFileSystems'
    'Get-Volume'
    'Get-VolumeCorruptionCount'
    'Get-VolumeScrubPolicy'
    'Optimize-Volume'
    'Repair-Volume'
    'Set-Volume'
    'Set-VolumeScrubPolicy'
    'Write-VolumeCache'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName DriveLetter -CommandName @(
    'Debug-Volume'
    'Format-Volume'
    'Get-DedupProperties'
    'Get-Volume'
    'Get-VolumeCorruptionCount'
    'Get-VolumeScrubPolicy'
    'Optimize-Volume'
    'Repair-Volume'
    'Set-Volume'
    'Set-VolumeScrubPolicy'
    'Write-VolumeCache'
    'Clear-RecycleBin'
)
Register-ArgumentCompleter -CommandName Add-WindowsCapability,Get-WindowsCapability,Remove-WindowsCapability -ParameterName Name -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $FoundFeatures = if ($fakeBoundParameters["Path"])
    {
        [CompletionHelper]::GetCachedResults("Get-WindowsCapability -Path '$($fakeBoundParameters['Path'])'", $true)
    }
    else
    {
        [CompletionHelper]::GetCachedResults('Get-WindowsCapability -Online', $false)
    }
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Feature in $FoundFeatures)
    {
        if ($null -eq $Feature)
        {
            continue
        }
        if ($Feature.Name -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Feature.Name)
        }
    }
}
Register-ArgumentCompleter -CommandName Export-WindowsImage,New-WindowsImage -ParameterName CompressionType -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($CompressionType in "None","Fast","Max")
    {
        if ($CompressionType -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($CompressionType)
        }
    }
}
$Scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Log in [CompletionHelper]::GetCachedResults('Get-WinEvent -ListLog *', $false))
    {
        if ($null -eq $Log)
        {
            continue
        }
        if ($Log.LogName -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Log.LogName)
        }
    }
}
Register-ArgumentCompleter -CommandName Get-WinEvent -ParameterName LogName -ScriptBlock $Scriptblock
Register-ArgumentCompleter -CommandName Get-WinEvent -ParameterName ListLog -ScriptBlock $Scriptblock
$Scriptblock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Log in [CompletionHelper]::GetCachedResults('Get-WinEvent -ListProvider *', $false))
    {
        if ($null -eq $Log)
        {
            continue
        }
        if ($Log.Name -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Log.Name)
        }
    }
}
Register-ArgumentCompleter -CommandName Get-WinEvent,New-WinEvent -ParameterName ProviderName -ScriptBlock $Scriptblock
Register-ArgumentCompleter -CommandName Get-WinEvent -ParameterName ListProvider -ScriptBlock $Scriptblock
Register-ArgumentCompleter -Native -CommandName winget -ScriptBlock {
    param($wordToComplete, $commandAst, $cursorPosition)

    $word = [CompletionHelper]::TrimQuotes($wordToComplete)
    $ast = $commandAst.ToString().Replace('"', '""')
    $ParameterNames = [HashSet[string]]::new(
        [string[]]('install', 'show', 'source', 'search', 'list', 'upgrade', 'uninstall', 'hash', 'validate', 'settings', 'features', 'experimental', 'complete', 'export', 'import', 'pin', 'configure', 'download', 'error', 'resume', 'repair', 'font', 'dscv3'),
        [StringComparer]::OrdinalIgnoreCase
    )

    foreach ($Item in winget.exe complete --word="$word" --commandline $ast --position $cursorPosition)
    {
        $Kind = if ($ParameterNames.Contains($Item) -or $Item.StartsWith('--'))
        {
            [CompletionResultType]::ParameterName
        }
        else
        {
            [CompletionResultType]::ParameterValue
        }

        [CompletionResult]::new($Item, $Item, $Kind, $Item)
    }
}
<#
.SYNOPSIS
    Lists all the argument completers supported by this module.
 
.PARAMETER CommandName
    Filters the displayed argument completers to the specified commands.
 
.PARAMETER ParameterName
    Filters the displayed argument completers to the specified parameters.
#>

function Get-UsefulArgumentCompleter
{
    Param
    (
        [Parameter()]
        [SupportsWildcards()]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
            $Commands = [CompletionHelper]::GetCachedResults("Import-Csv -Path '$PSScriptRoot\ArgumentCompleters.csv' -Delimiter ','", $false) | Select-Object -ExpandProperty CommandName | Sort-Object -Unique
            foreach ($Item in $Commands)
            {
                if ($Item -like $WildcardInput)
                {
                    [CompletionHelper]::NewParamCompletionResult($Item)
                }
            }
        })]
        [string[]]
        $CommandName = "*",

        [Parameter()]
        [SupportsWildcards()]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
            $Parameters = [CompletionHelper]::GetCachedResults("Import-Csv -Path '$PSScriptRoot\ArgumentCompleters.csv' -Delimiter ','", $false) | Select-Object -ExpandProperty ParameterName | Sort-Object -Unique
            foreach ($Item in $Parameters)
            {
                if ($Item -like $WildcardInput)
                {
                    [CompletionHelper]::NewParamCompletionResult($Item)
                }
            }
        })]
        [string[]]
        $ParameterName = "*"
    )
    End
    {
        foreach ($Completer in [CompletionHelper]::GetCachedResults("Import-Csv -Path '$PSScriptRoot\ArgumentCompleters.csv' -Delimiter ','", $false))
        {
            $CommandMatch = $false
            foreach ($Name in $CommandName)
            {
                if ($Completer.CommandName -like $Name)
                {
                    $CommandMatch = $true
                    break
                }
            }

            if (!$CommandMatch)
            {
                continue
            }

            foreach ($Name in $ParameterName)
            {
                if ($Completer.ParameterName -like $Name)
                {
                    $Completer
                    break
                }
            }
        }
    }
}
<#
.SYNOPSIS
    Imports optional argument completers.
    This is intended to handle argument completers that may conflict with other commands such as Hyper-V VS PowerCLI commands.
 
.PARAMETER OptionalCompleter
    Name of completer to load.
#>

function Import-UsefulArgumentCompleterSet
{
    Param
    (
        [Parameter(Mandatory)]
        [Alias("Completer")]
        [ValidateSet("Hyperv")]
        [string[]]
        $OptionalCompleter
    )
    End
    {
        switch ($OptionalCompleter)
        {
            'HyperV'
            {
                & $Script:HypervCompleters
                break
            }
        }
    }
}

if ($PSEdition -eq 'Desktop')
{
Register-ArgumentCompleter -CommandName Get-CimInstance -ParameterName Property -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    if (!$fakeBoundParameters['ClassName'])
    {
        return
    }

    $GetCimClassParams = @{ClassName = $fakeBoundParameters['ClassName']}
    if ($fakeBoundParameters['Namespace'])
    {
        $GetCimClassParams.Add('Namespace',$fakeBoundParameters['Namespace'])
    }
    $FoundClass = Get-CimClass @GetCimClassParams -ErrorAction Ignore

    if (!$FoundClass)
    {
        return
    }

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + "*"
    foreach ($Item in $FoundClass.CimClassProperties)
    {
        if ($Item.Name -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Item.Name, "$($Item.Name) [$($Item.CimType)]")
        }
    }
}
Register-ArgumentCompleter -CommandName New-ItemProperty -ParameterName PropertyType -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    
    $ResolveArgument = if ($null -ne $fakeBoundParameters['Path'])
    {
        @{LiteralPath = $fakeBoundParameters['Path']}
    }
    elseif ($null -ne $fakeBoundParameters['LiteralPath'])
    {
        @{LiteralPath = $fakeBoundParameters['LiteralPath']}
    }
    else
    {
        @{Path = '.\'}
    }
    
    $Values = switch ((Resolve-Path @ResolveArgument | Select-Object -First 1).Provider.Name)
    {
        'Registry'
        {
            @(
                @{Name = "String";      ToolTip = "A normal string."}
                @{Name = "ExpandString";ToolTip = "A string that contains unexpanded references to environment variables that are expanded when the value is retrieved."}
                @{Name = "Binary";      ToolTip = "Binary data in any form."}
                @{Name = "DWord";       ToolTip = "A 32-bit binary number."}
                @{Name = "MultiString"; ToolTip = "An array of strings."}
                @{Name = "Qword";       ToolTip = "A 64-bit binary number"}
                @{Name = "Unknown";     ToolTip = "An unsupported registry data type"}
            )
            break
        }
        Default
        {
            return
        }
    }

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Item in $Values)
    {
        if ($Item['Name'] -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Item['Name'], $Item['ToolTip'])
        }
    }
}
Register-ArgumentCompleter -CommandName Start-Process -ParameterName Verb -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    $FilePath = $fakeBoundParameters['FilePath']
    if ([string]::IsNullOrWhiteSpace($FilePath))
    {
        return
    }

    $Executable = [CompletionHelper]::GetCachedResults("Get-Command -Name '$FilePath' -CommandType Application -ErrorAction Ignore", $true)
    if ($null -eq $Executable)
    {
        return
    }

    $CommandInfo = [CompletionHelper]::GetCachedResults("[System.Diagnostics.ProcessStartInfo]::new('A$($Executable.Extension)')", $false)

    foreach ($Verb in $CommandInfo.Verbs)
    {
        if ($Verb -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Verb)
        }
    }
}
Register-ArgumentCompleter -CommandName Update-Help -ParameterName Module -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Module in [CompletionHelper]::GetCachedResults('Get-Module -ListAvailable | Where-Object -Property HelpInfoUri -NE $null | Select-Object -ExpandProperty Name | Sort-Object -Unique', $false))
    {
        if ($null -eq $Module)
        {
            continue
        }
        if ($Module -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Module)
        }
    }
}
Register-ArgumentCompleter -CommandName Clear-Variable,Get-Variable,New-Variable,Remove-Variable,Set-Variable -ParameterName Scope -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    foreach ($Scope in "Global", "Local", "Script" )
    {
        if ($Scope -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Scope)
        }
    }
}
Register-ArgumentCompleter -CommandName Get-Command,Get-Verb -ParameterName Verb -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    foreach ($Verb in [CompletionHelper]::GetCachedResults('Get-Verb', $false))
    {
        if ($null -eq $Verb)
        {
            continue
        }
        if ($Verb.Verb -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Verb.Verb, $Verb.Description)
        }
    }
}

Register-ArgumentCompleter -CommandName Get-Command -ParameterName Noun -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'

    if ($fakeBoundParameters.ContainsKey('Noun'))
    {
        $fakeBoundParameters.Remove('Noun')
    }

    foreach ($Noun in (Get-Command @fakeBoundParameters).Noun | Sort-Object -Unique)
    {
        if ($Noun -like $WildcardInput)
        {
            [CompletionHelper]::NewParamCompletionResult($Noun)
        }
    }
}
}

if ($PSEdition -eq 'Core')
{
}

$Script:HypervCompleters = {
$ScriptBlock = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $WildcardInput = [CompletionHelper]::TrimQuotes($wordToComplete) + '*'
    
    $FoundVMs = if ($parameterName -eq "VMName" -or $parameterName -eq "Name")
    {
        Hyper-V\Get-VM -Name $WildcardInput -ErrorAction Ignore
    }
    elseif ($parameterName -eq 'VMId' -or $parameterName -eq 'Id')
    {
        Hyper-V\Get-VM -ErrorAction Ignore | Where-Object -FilterScript {$_.Id.Guid -like $WildcardInput}
    }
    else
    {
        return
    }

    foreach ($VM in $FoundVMs)
    {
        $MatchText = if ($parameterName -eq "Name" -or $parameterName -eq "VMName")
        {
            $VM.Name
        }
        else
        {
            "{$($VM.Id.Guid)}"
        }

        if ($MatchText -like $WildcardInput)
        {
            [CompletionResult]::new(
                [CompletionHelper]::AddQuotesIfNeeded($MatchText),
                $VM.Name,
                [CompletionResultType]::ParameterValue,
                $VM.Name
            )
        }
    }
}
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName VMName -CommandName @(
    'Add-VMAssignableDevice'
    'Add-VMDvdDrive'
    'Add-VMFibreChannelHba'
    'Add-VMGpuPartitionAdapter'
    'Add-VMHardDiskDrive'
    'Add-VMKeyStorageDrive'
    'Add-VMNetworkAdapter'
    'Add-VMNetworkAdapterAcl'
    'Add-VMNetworkAdapterExtendedAcl'
    'Add-VMNetworkAdapterRoutingDomainMapping'
    'Add-VMPmemController'
    'Add-VMRemoteFx3dVideoAdapter'
    'Add-VMScsiController'
    'Add-VMSwitchExtensionPortFeature'
    'Complete-VMFailover'
    'Connect-VMNetworkAdapter'
    'Disable-VMConsoleSupport'
    'Disable-VMIntegrationService'
    'Disable-VMResourceMetering'
    'Disable-VMTPM'
    'Disconnect-VMNetworkAdapter'
    'Enable-VMConsoleSupport'
    'Enable-VMIntegrationService'
    'Enable-VMReplication'
    'Enable-VMResourceMetering'
    'Enable-VMTPM'
    'Enter-PSSession'
    'Export-VMSnapshot'
    'Get-PSSession'
    'Get-VMAssignableDevice'
    'Get-VMBios'
    'Get-VMComPort'
    'Get-VMConnectAccess'
    'Get-VMDvdDrive'
    'Get-VMFibreChannelHba'
    'Get-VMFirmware'
    'Get-VMFloppyDiskDrive'
    'Get-VMGpuPartitionAdapter'
    'Get-VMHardDiskDrive'
    'Get-VMIdeController'
    'Get-VMIntegrationService'
    'Get-VMKeyProtector'
    'Get-VMKeyStorageDrive'
    'Get-VMMemory'
    'Get-VMNetworkAdapter'
    'Get-VMNetworkAdapterAcl'
    'Get-VMNetworkAdapterExtendedAcl'
    'Get-VMNetworkAdapterFailoverConfiguration'
    'Get-VMNetworkAdapterIsolation'
    'Get-VMNetworkAdapterRdma'
    'Get-VMNetworkAdapterRoutingDomainMapping'
    'Get-VMNetworkAdapterTeamMapping'
    'Get-VMNetworkAdapterVlan'
    'Get-VMPmemController'
    'Get-VMProcessor'
    'Get-VMRemoteFx3dVideoAdapter'
    'Get-VMReplication'
    'Get-VMScsiController'
    'Get-VMSecurity'
    'Get-VMSnapshot'
    'Get-VMStorageSettings'
    'Get-VMSwitchExtensionPortData'
    'Get-VMSwitchExtensionPortFeature'
    'Get-VMVideo'
    'Grant-VMConnectAccess'
    'Import-VMInitialReplication'
    'Invoke-Command'
    'Measure-VMReplication'
    'New-PSSession'
    'Remove-PSSession'
    'Remove-VMAssignableDevice'
    'Remove-VMDvdDrive'
    'Remove-VMFibreChannelHba'
    'Remove-VMGpuPartitionAdapter'
    'Remove-VMHardDiskDrive'
    'Remove-VMKeyStorageDrive'
    'Remove-VMNetworkAdapter'
    'Remove-VMNetworkAdapterAcl'
    'Remove-VMNetworkAdapterExtendedAcl'
    'Remove-VMNetworkAdapterRoutingDomainMapping'
    'Remove-VMNetworkAdapterTeamMapping'
    'Remove-VMPmemController'
    'Remove-VMRemoteFx3dVideoAdapter'
    'Remove-VMReplication'
    'Remove-VMSavedState'
    'Remove-VMScsiController'
    'Remove-VMSnapshot'
    'Remove-VMSwitchExtensionPortFeature'
    'Rename-VMNetworkAdapter'
    'Rename-VMSnapshot'
    'Reset-VMReplicationStatistics'
    'Reset-VMResourceMetering'
    'Restore-VMSnapshot'
    'Resume-VMReplication'
    'Revoke-VMConnectAccess'
    'Set-VMBios'
    'Set-VMComPort'
    'Set-VMDvdDrive'
    'Set-VMFibreChannelHba'
    'Set-VMFirmware'
    'Set-VMFloppyDiskDrive'
    'Set-VMGpuPartitionAdapter'
    'Set-VMHardDiskDrive'
    'Set-VMKeyProtector'
    'Set-VMKeyStorageDrive'
    'Set-VMMemory'
    'Set-VMNetworkAdapter'
    'Set-VMNetworkAdapterFailoverConfiguration'
    'Set-VMNetworkAdapterIsolation'
    'Set-VMNetworkAdapterRdma'
    'Set-VMNetworkAdapterRoutingDomainMapping'
    'Set-VMNetworkAdapterTeamMapping'
    'Set-VMNetworkAdapterVlan'
    'Set-VMProcessor'
    'Set-VMRemoteFx3dVideoAdapter'
    'Set-VMReplication'
    'Set-VMSecurity'
    'Set-VMSecurityPolicy'
    'Set-VMStorageSettings'
    'Set-VMSwitchExtensionPortFeature'
    'Set-VMVideo'
    'Start-VMFailover'
    'Start-VMInitialReplication'
    'Stop-VMFailover'
    'Stop-VMInitialReplication'
    'Stop-VMReplication'
    'Suspend-VMReplication'
    'Test-VMNetworkAdapter'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Name -CommandName @(
    'Checkpoint-VM'
    'Copy-VMFile'
    'Debug-VM'
    'Export-VM'
    'Get-VM'
    'Measure-VM'
    'Move-VMStorage'
    'New-VM'
    'Remove-VM'
    'Rename-VM'
    'Restart-VM'
    'Resume-VM'
    'Save-VM'
    'Set-VM'
    'Start-VM'
    'Stop-VM'
    'Suspend-VM'
    'Update-VMVersion'
    'Wait-VM'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName VMId -CommandName @(
    'Enter-PSSession'
    'Get-PSSession'
    'Get-VHD'
    'Get-VMConnectAccess'
    'Grant-VMConnectAccess'
    'Invoke-Command'
    'New-PSSession'
    'Remove-PSSession'
    'Revoke-VMConnectAccess'
)
Register-ArgumentCompleter -ScriptBlock $ScriptBlock -ParameterName Id -CommandName Get-VM
}