completers/wsl_completer/wsl_completer.ps1
|
# PowerShell Argument Completer for wsl.exe # Provides tab completion for common wsl subcommands and options. # This script is self‑contained and registers a native completer for the `wsl` command. Set-StrictMode -Version Latest function Get-WslDistributionNames { if (-not (Get-Command wsl -ErrorAction Ignore)) { return } wsl -l -q 2>$null | ForEach-Object { $_.Replace([string][char]0, '').Trim() } | Where-Object { $_ } } function Get-WslDistributionIds { $lxssPath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss' if (-not (Test-Path -LiteralPath $lxssPath)) { return } Get-ChildItem -LiteralPath $lxssPath -ErrorAction SilentlyContinue | ForEach-Object { $distributionId = $_.PSChildName $parsedGuid = [guid]::Empty if ([guid]::TryParse($distributionId, [ref] $parsedGuid)) { $distributionId } } } function New-WslCompletionResult { param( [string[]] $Values, [string] $WordToComplete, [ValidateSet('ParameterName', 'ParameterValue')] [string] $ResultType = 'ParameterValue' ) $seen = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) foreach ($value in $Values) { if ([string]::IsNullOrWhiteSpace($value)) { continue } if ($value -notlike "$WordToComplete*") { continue } if (-not $seen.Add($value)) { continue } [System.Management.Automation.CompletionResult]::new($value, $value, $ResultType, $value) } } function Complete-WslNative { param($wordToComplete, $commandAst, $cursorPosition) # Ensure wsl.exe is available if (-not (Get-Command wsl -ErrorAction Ignore)) { return } $line = $commandAst.ToString() $tokens = @([regex]::Matches($line, '\S+') | ForEach-Object { $_.Value }) $hasTrailingSpace = ($line -match '\s$') -or (($cursorPosition - $commandAst.Extent.StartOffset) -gt $line.Length) if ($hasTrailingSpace) { $completedTokens = $tokens } elseif ($tokens.Count -gt 1) { $completedTokens = @($tokens[0..($tokens.Count - 2)]) } else { $completedTokens = @() } if ($completedTokens.Count -gt 1) { $argumentTokens = @($completedTokens[1..($completedTokens.Count - 1)]) } else { $argumentTokens = @() } $previousToken = if ($argumentTokens.Count -gt 0) { $argumentTokens[-1] } else { '' } $topLevelSwitches = @( '--', '--cd', '--debug-shell', '--distribution', '-d', '--distribution-id', '--exec', '-e', '--export', '--help', '--import', '--import-in-place', '--install', '--list', '-l', '--manage', '--mount', '--set-default', '-s', '--set-default-version', '--set-version', '--shell-type', '--shutdown', '--status', '--system', '--terminate', '-t', '--uninstall', '--unmount', '--unregister', '--update', '--user', '-u', '--version', '-v' ) $listSwitches = @('--all', '--running', '--quiet', '-q', '--verbose', '-v', '--online', '-o') $modeSwitches = @{ '--install' = @('--no-launch', '--web-download', '--no-distribution', '--enable-wsl1', '--from-file', '--name', '--location', '--version', '--legacy') '--mount' = @('--vhd', '--bare', '--name', '--type', '--options', '--partition') '--export' = @('--vhd', '--format') '--import' = @('--version', '--vhd') '--shutdown' = @('--force') '--update' = @('--pre-release') '--list' = $listSwitches '-l' = $listSwitches '--manage' = @('--move', '--resize', '--set-default-user', '--set-sparse', '-s') } $enumValues = @{ '--set-default-version' = @('1', '2') '--shell-type' = @('standard', 'login', 'none') '--set-sparse' = @('true', 'false') '--format' = @('tar', 'tar.gz', 'tar.xz', 'vhd') '--type' = @('ext4', 'drvfs') '--user' = @('root') '-u' = @('root') } $distributionValueSwitches = @('--distribution', '-d', '--export', '--manage', '--set-default', '--set-version', '--terminate', '-t', '--unregister') $mode = $null foreach ($token in $argumentTokens) { if ($modeSwitches.ContainsKey($token)) { $mode = $token break } } if ($previousToken -eq '-s') { if ($mode -eq '--manage') { New-WslCompletionResult -Values @('true', 'false') -WordToComplete $wordToComplete return } if ($argumentTokens.Count -eq 1) { New-WslCompletionResult -Values (Get-WslDistributionNames) -WordToComplete $wordToComplete return } } if ($previousToken -eq '--version' -and $mode -in @('--install', '--import')) { New-WslCompletionResult -Values @('1', '2') -WordToComplete $wordToComplete return } if ($enumValues.ContainsKey($previousToken)) { New-WslCompletionResult -Values $enumValues[$previousToken] -WordToComplete $wordToComplete return } if ($argumentTokens.Count -eq 2 -and $argumentTokens[0] -eq '--set-version') { New-WslCompletionResult -Values @('1', '2') -WordToComplete $wordToComplete return } if ($previousToken -eq '--distribution-id') { New-WslCompletionResult -Values (Get-WslDistributionIds) -WordToComplete $wordToComplete return } if ($previousToken -in $distributionValueSwitches) { New-WslCompletionResult -Values (Get-WslDistributionNames) -WordToComplete $wordToComplete return } if ($previousToken -in @('--from-file', '--location', '--move', '--cd', '--name', '--options', '--partition', '--resize', '--set-default-user')) { return } if ($mode) { $operandSlot = -not ($wordToComplete -like '-*') -and $argumentTokens.Count -eq 1 if ($operandSlot -and $mode -in @('--import', '--mount')) { return } if (-not ($wordToComplete -like '-*') -and $mode -eq '--import' -and $argumentTokens.Count -le 3) { return } if (-not ($wordToComplete -like '-*') -and $mode -eq '--export' -and $argumentTokens.Count -eq 2) { return } New-WslCompletionResult -Values $modeSwitches[$mode] -WordToComplete $wordToComplete -ResultType ParameterName return } New-WslCompletionResult -Values $topLevelSwitches -WordToComplete $wordToComplete -ResultType ParameterName } Register-ArgumentCompleter -Native -CommandName @('wsl', 'wsl.exe') -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) Complete-WslNative -wordToComplete $wordToComplete -commandAst $commandAst -cursorPosition $cursorPosition } |