completers/rustup_completer/rustup_completer.ps1
|
Set-StrictMode -Version 2.0 function Get-RustupCache { if (Test-Path -LiteralPath variable:script:RustupCompleterCache) { return $script:RustupCompleterCache } $cache = @{ CommandPath = $null HelpByKey = @{} Toolchains = $null Installed = $null Targets = @{} Components = @{} HostTriples = $null Profiles = @('minimal', 'default', 'complete') AutoSelfUpdate = @('enable', 'disable', 'check-only') AutoInstall = @('enable', 'disable') Shells = @('bash', 'elvish', 'fish', 'powershell', 'zsh') CompletionApps = @('rustup', 'cargo') } Set-Variable -Name RustupCompleterCache -Scope Script -Value $cache $cache } function Resolve-RustupCommandPath { $cache = Get-RustupCache if ($cache.CommandPath) { return $cache.CommandPath } $command = Get-Command -Name 'rustup.exe', 'rustup' -ErrorAction Ignore | Select-Object -First 1 if ($command) { $cache.CommandPath = if ($command.Source) { $command.Source } else { $command.Name } } $cache.CommandPath } function Invoke-RustupText { param([string[]]$Arguments) $commandPath = Resolve-RustupCommandPath if (-not $commandPath) { return @() } try { @(& $commandPath @Arguments 2>$null | ForEach-Object { $_ -replace '\e\[[0-9;?]*[ -/]*[@-~]', '' }) } catch { @() } } function Get-RustupHelpDefinition { param([string[]]$CommandPath) $normalizedPath = @($CommandPath) $cache = Get-RustupCache $key = if ($normalizedPath.Count -gt 0) { $normalizedPath -join ' ' } else { '<root>' } if ($cache.HelpByKey.ContainsKey($key)) { return $cache.HelpByKey[$key] } if ($normalizedPath.Count -gt 0 -and $normalizedPath[0] -eq 'help') { # 'rustup help <path>' takes the same subcommand names as '<path>' itself # and no options; 'rustup help --help' only prints a clap error. $target = Get-RustupHelpDefinition -CommandPath @($normalizedPath | Select-Object -Skip 1) $definition = @{ Key = $key CommandPath = $normalizedPath Commands = @($target.Commands) CommandMap = $target.CommandMap Options = @() OptionMap = @{} } $cache.HelpByKey[$key] = $definition return $definition } $arguments = @() if ($normalizedPath.Count -gt 0) { $arguments += $normalizedPath } $arguments += '--help' $lines = @(Invoke-RustupText -Arguments $arguments) $definition = @{ Key = $key CommandPath = $normalizedPath Commands = @() CommandMap = @{} Options = @() OptionMap = @{} } $section = '' foreach ($line in $lines) { if ($line -match '^Commands:$') { $section = 'Commands' continue } if ($line -match '^Options:$') { $section = 'Options' continue } if ($line -match '^[A-Z][A-Za-z ]+:$') { $section = '' continue } switch ($section) { 'Commands' { if ($line -match '^\s{2,}([A-Za-z][\w-]*)\s{2,}(.*)$') { $name = $matches[1] $description = $matches[2].Trim() $entry = [pscustomobject]@{ Name = $name Description = $description } $definition.Commands += $entry $definition.CommandMap[$name] = $entry } } 'Options' { if ($line -match '^\s{2,}(.+?)\s{2,}(.*)$') { $spec = $matches[1].Trim() $description = $matches[2].Trim() $names = @([regex]::Matches($spec, '(?:^|,\s*)((?:--?|\+)[A-Za-z0-9][A-Za-z0-9_-]*|/\?)') | ForEach-Object { $_.Groups[1].Value.Trim() }) if ($names.Count -eq 0) { continue } $takesValue = $spec -match '<[^>]+>' $valuePlaceholder = $null if ($takesValue -and $spec -match '<([^>]+)>') { $valuePlaceholder = $matches[1] } $entry = [pscustomobject]@{ Names = $names PrimaryName = $names[-1] Description = $description TakesValue = $takesValue ValuePlaceholder = $valuePlaceholder } $definition.Options += $entry foreach ($name in $names) { $definition.OptionMap[$name] = $entry } } } } } $cache.HelpByKey[$key] = $definition $definition } function New-RustupCompletionResult { param( [string]$CompletionText, [string]$ListItemText, [string]$ResultType, [string]$ToolTip ) if ([string]::IsNullOrWhiteSpace($ListItemText)) { $ListItemText = $CompletionText } if ([string]::IsNullOrWhiteSpace($ToolTip)) { $ToolTip = $CompletionText } [System.Management.Automation.CompletionResult]::new( $CompletionText, $ListItemText, $ResultType, $ToolTip ) } function ConvertTo-RustupQuotedValue { param( [string]$Value, [bool]$AlwaysQuote = $false ) if ([string]::IsNullOrEmpty($Value)) { return $Value } if (($AlwaysQuote -or $Value -match '\s') -and -not ($Value.StartsWith('"') -and $Value.EndsWith('"'))) { return '"' + $Value.Replace('`', '``').Replace('"', '`"') + '"' } $Value } function Remove-RustupOuterQuotes { param([string]$Value) if ($null -eq $Value) { return '' } $Value.Trim([char[]]@([char]34, [char]39)) } function Get-RustupCurrentToken { param( [string]$Line, [int]$CursorPosition, [string]$Fallback ) if ([string]::IsNullOrWhiteSpace($Line)) { return $Fallback } $safeCursor = [Math]::Min([Math]::Max($CursorPosition, 0), $Line.Length) $prefix = $Line.Substring(0, $safeCursor) if ($prefix -match '\s$') { return '' } $parts = @([regex]::Matches($prefix, '"[^"]*"|''[^'']*''|\S+') | ForEach-Object { $_.Value }) if ($parts.Count -gt 0) { return $parts[-1] } $Fallback } function Get-RustupTokensBeforeCursor { param( [System.Management.Automation.Language.CommandAst]$CommandAst, [int]$CursorPosition ) $tokens = New-Object System.Collections.Generic.List[string] foreach ($element in $CommandAst.CommandElements | Select-Object -Skip 1) { if ($element.Extent.EndOffset -lt $CursorPosition) { $tokens.Add($element.Extent.Text) } } @($tokens.ToArray()) } function Get-RustupKnownToolchains { $cache = Get-RustupCache if ($cache.Toolchains) { return $cache.Toolchains } $names = New-Object System.Collections.Generic.List[string] foreach ($line in (Invoke-RustupText -Arguments @('toolchain', 'list'))) { if ($line -match '^([^\s]+)') { $value = $matches[1].Trim() if (-not [string]::IsNullOrWhiteSpace($value) -and -not $names.Contains($value)) { $names.Add($value) } } } $cache.Installed = @($names.ToArray()) foreach ($fallback in @('stable', 'beta', 'nightly')) { if (-not $names.Contains($fallback)) { $names.Add($fallback) } } $cache.Toolchains = @($names.ToArray()) $cache.Toolchains } function Resolve-RustupScopeToolchain { param([string]$Name) # Only an installed toolchain scopes a list: 'stable' or '1.95.0' match # their host-qualified names. Anything else keeps the unscoped list, so a # lookup never asks rustup about (or auto-installs) a missing toolchain. $Name = Remove-RustupOuterQuotes -Value $Name if ([string]::IsNullOrWhiteSpace($Name)) { return $null } $null = Get-RustupKnownToolchains foreach ($installed in @((Get-RustupCache).Installed)) { if ($installed -ceq $Name -or $installed.StartsWith($Name + '-', [System.StringComparison]::Ordinal)) { return $Name } } $null } function Get-RustupTargetValues { param( [bool]$InstalledOnly, [string]$Toolchain ) $cache = Get-RustupCache $key = $(if ($InstalledOnly) { 'installed' } else { 'all' }) + '|' + $Toolchain if ($cache.Targets.ContainsKey($key)) { return $cache.Targets[$key] } $args = @('target', 'list') if ($InstalledOnly) { $args += '--installed' } $scopeArgs = if ($Toolchain) { @('--toolchain', $Toolchain) } else { @() } $values = New-Object System.Collections.Generic.List[string] foreach ($line in (Invoke-RustupText -Arguments ($args + $scopeArgs))) { if ($line -match '^([^\s]+)') { $value = $matches[1].Trim() if (-not [string]::IsNullOrWhiteSpace($value) -and -not $values.Contains($value)) { $values.Add($value) } } } if (-not $InstalledOnly -and -not $values.Contains('all')) { $values.Add('all') } $cache.Targets[$key] = @($values.ToArray()) $cache.Targets[$key] } function Get-RustupComponentValues { param( [bool]$InstalledOnly, [string]$Toolchain ) $cache = Get-RustupCache $key = $(if ($InstalledOnly) { 'installed' } else { 'all' }) + '|' + $Toolchain if ($cache.Components.ContainsKey($key)) { return $cache.Components[$key] } $args = @('component', 'list') if ($InstalledOnly) { $args += '--installed' } $scopeArgs = if ($Toolchain) { @('--toolchain', $Toolchain) } else { @() } $values = New-Object System.Collections.Generic.List[string] foreach ($line in (Invoke-RustupText -Arguments ($args + $scopeArgs))) { if ($line -match '^([^\s]+)') { $value = $matches[1].Trim() if (-not [string]::IsNullOrWhiteSpace($value) -and -not $values.Contains($value)) { $values.Add($value) } } } $cache.Components[$key] = @($values.ToArray()) $cache.Components[$key] } function Get-RustupHostTriples { $cache = Get-RustupCache if ($cache.HostTriples) { return $cache.HostTriples } $values = New-Object System.Collections.Generic.List[string] foreach ($toolchain in (Get-RustupKnownToolchains)) { if ($toolchain -match '-([A-Za-z0-9_\.-]+)$') { $hostTriple = $matches[1] if (-not $values.Contains($hostTriple)) { $values.Add($hostTriple) } } } foreach ($target in (Get-RustupTargetValues -InstalledOnly $true)) { if (-not $values.Contains($target)) { $values.Add($target) } } $cache.HostTriples = @($values.ToArray()) $cache.HostTriples } function Get-RustupPathCompletions { param([string]$InputPath) $cleanInput = Remove-RustupOuterQuotes -Value $InputPath $alwaysQuote = -not [string]::IsNullOrEmpty($InputPath) -and ($InputPath.StartsWith('"') -or $InputPath.StartsWith("'")) if ([string]::IsNullOrWhiteSpace($cleanInput)) { $parent = '.' $leaf = '' } elseif ($cleanInput -match '[\\/]$') { $parent = $cleanInput $leaf = '' } else { $parent = Split-Path -Path $cleanInput -Parent if ([string]::IsNullOrWhiteSpace($parent)) { $parent = '.' } $leaf = Split-Path -Path $cleanInput -Leaf } $inputIsRooted = -not [string]::IsNullOrWhiteSpace($cleanInput) -and [System.IO.Path]::IsPathRooted($cleanInput) $items = @(Get-ChildItem -LiteralPath $parent -ErrorAction SilentlyContinue) $items = $items | Where-Object { $_.Name -like ([System.Management.Automation.WildcardPattern]::Escape($leaf) + '*') } foreach ($item in ($items | Sort-Object -Property @{ Expression = 'PSIsContainer'; Descending = $true }, Name)) { if ($inputIsRooted) { $pathText = Join-Path -Path $parent -ChildPath $item.Name } elseif ($parent -eq '.' -or [string]::IsNullOrWhiteSpace($cleanInput)) { $pathText = $item.Name } else { $pathText = Join-Path -Path $parent -ChildPath $item.Name } if ($item.PSIsContainer -and -not $pathText.EndsWith('\')) { $pathText += '\' } $quoted = ConvertTo-RustupQuotedValue -Value $pathText -AlwaysQuote $alwaysQuote $type = if ($item.PSIsContainer) { 'ProviderContainer' } else { 'ParameterValue' } New-RustupCompletionResult -CompletionText $quoted -ListItemText $pathText -ResultType $type -ToolTip $item.FullName } } function Get-RustupValueCatalog { param( [string[]]$ContextPath, [string]$OptionName, [bool]$IsPositional, [int]$PositionalIndex = -1, [string]$Toolchain ) $key = if ($ContextPath.Count -gt 0) { $ContextPath -join ' ' } else { '<root>' } switch ($key) { '<root>' { } 'install' { if ($IsPositional) { return @(Get-RustupKnownToolchains) } switch ($OptionName) { '--profile' { return @((Get-RustupCache).Profiles) } '--target' { return @(Get-RustupTargetValues -InstalledOnly $false) } '--component' { return @(Get-RustupComponentValues -InstalledOnly $false) } } } 'toolchain install' { if ($IsPositional) { return @(Get-RustupKnownToolchains) } switch ($OptionName) { '--profile' { return @((Get-RustupCache).Profiles) } '--target' { return @(Get-RustupTargetValues -InstalledOnly $false) } '--component' { return @(Get-RustupComponentValues -InstalledOnly $false) } } } 'toolchain uninstall' { if ($IsPositional) { return @(Get-RustupKnownToolchains) } } 'uninstall' { if ($IsPositional) { return @(Get-RustupKnownToolchains) } } 'default' { if ($IsPositional) { return @('none') + @(Get-RustupKnownToolchains) } } 'update' { if ($IsPositional) { return @(Get-RustupKnownToolchains) } } 'target add' { if ($IsPositional) { return @(Get-RustupTargetValues -InstalledOnly $false -Toolchain $Toolchain) } if ($OptionName -eq '--toolchain') { return @(Get-RustupKnownToolchains) } } 'target remove' { if ($IsPositional) { return @(Get-RustupTargetValues -InstalledOnly $true -Toolchain $Toolchain) } if ($OptionName -eq '--toolchain') { return @(Get-RustupKnownToolchains) } } 'target list' { if ($OptionName -eq '--toolchain') { return @(Get-RustupKnownToolchains) } } 'component add' { if ($IsPositional) { return @(Get-RustupComponentValues -InstalledOnly $false -Toolchain $Toolchain) } switch ($OptionName) { '--toolchain' { return @(Get-RustupKnownToolchains) } '--target' { return @(Get-RustupTargetValues -InstalledOnly $false -Toolchain $Toolchain) } } } 'component remove' { if ($IsPositional) { return @(Get-RustupComponentValues -InstalledOnly $true -Toolchain $Toolchain) } switch ($OptionName) { '--toolchain' { return @(Get-RustupKnownToolchains) } '--target' { return @(Get-RustupTargetValues -InstalledOnly $true -Toolchain $Toolchain) } } } 'component list' { if ($OptionName -eq '--toolchain') { return @(Get-RustupKnownToolchains) } } 'toolchain link' { if (-not $OptionName -and $IsPositional) { return @('<toolchain-name>') } } 'override set' { if ($IsPositional) { return @(Get-RustupKnownToolchains) } } 'run' { if ($IsPositional -and $PositionalIndex -eq 0) { return @(Get-RustupKnownToolchains) } } 'doc' { if ($OptionName -eq '--toolchain') { return @(Get-RustupKnownToolchains) } } 'which' { if ($OptionName -eq '--toolchain') { return @(Get-RustupKnownToolchains) } if ($IsPositional) { return @('cargo', 'rustc', 'rustdoc', 'rustfmt', 'clippy-driver') } } 'completions' { if ($IsPositional -and $PositionalIndex -eq 0) { return @((Get-RustupCache).Shells) } if ($IsPositional -and $PositionalIndex -eq 1) { return @((Get-RustupCache).CompletionApps) } } 'set profile' { if ($IsPositional) { return @((Get-RustupCache).Profiles) } } 'set auto-self-update' { if ($IsPositional) { return @((Get-RustupCache).AutoSelfUpdate) } } 'set auto-install' { if ($IsPositional) { return @((Get-RustupCache).AutoInstall) } } 'set default-host' { if ($IsPositional) { return @(Get-RustupHostTriples) } } } @() } function Get-RustupPlaceholderText { param( [string[]]$ContextPath, [string]$OptionName, [bool]$IsPositional, [int]$PositionalIndex ) $key = if ($ContextPath.Count -gt 0) { $ContextPath -join ' ' } else { '<root>' } switch ($key) { '<root>' { } 'toolchain link' { if ($IsPositional -and $PositionalIndex -eq 0) { return '<toolchain-name>' } if ($IsPositional -and $PositionalIndex -eq 1) { return '<path>' } } 'override set' { if ($OptionName -eq '--path') { return '<path>' } } 'override unset' { if ($OptionName -eq '--path') { return '<path>' } } 'run' { if ($IsPositional -and $PositionalIndex -eq 1) { return '<command>' } if ($IsPositional -and $PositionalIndex -ge 2) { return '<argument>' } } 'doc' { if ($IsPositional) { return '<topic>' } } } if ($OptionName -eq '--path') { return '<path>' } $null } function Get-RustupCommandState { param([string[]]$TokensBeforeCurrent) $path = New-Object System.Collections.Generic.List[string] $positionals = New-Object System.Collections.Generic.List[string] $pendingOption = $null $pendingOptionPath = @() $optionValues = @{} $toolchainOverride = $null foreach ($rawToken in $TokensBeforeCurrent) { $token = Remove-RustupOuterQuotes -Value $rawToken if ([string]::IsNullOrWhiteSpace($token)) { continue } if ($pendingOption) { $optionValues[$pendingOption] = $token $pendingOption = $null $pendingOptionPath = @() continue } $definition = Get-RustupHelpDefinition -CommandPath @($path.ToArray()) if ($token.StartsWith('-') -or $token -eq '/?') { $attached = [regex]::Match($token, '^(?<name>--[^=]+)=(?<value>.*)$') if ($attached.Success) { # --opt=value carries its value, so nothing is pending afterwards. $optionValues[$attached.Groups['name'].Value] = $attached.Groups['value'].Value } elseif ($definition.OptionMap.ContainsKey($token)) { $entry = $definition.OptionMap[$token] if ($entry.TakesValue) { $pendingOption = $entry.PrimaryName $pendingOptionPath = @($path.ToArray()) } } continue } if ($token.StartsWith('+') -and $path.Count -eq 0 -and $positionals.Count -eq 0 -and -not $toolchainOverride) { # '+toolchain' selects the toolchain for the rest of the line; it is # not an operand and must not hide the subcommands. $toolchainOverride = $token continue } if ($definition.CommandMap.ContainsKey($token) -and $positionals.Count -eq 0) { $path.Add($token) continue } $positionals.Add($token) } [pscustomobject]@{ ContextPath = @($path.ToArray()) Positionals = @($positionals.ToArray()) PendingOption = $pendingOption PendingOptionPath = $pendingOptionPath OptionValues = $optionValues ToolchainOverride = $toolchainOverride } } function Get-RustupNamedCompletions { param( [string[]]$Values, [string]$CurrentWord, [string]$ToolTip, [string]$ResultType ) $cleanCurrent = Remove-RustupOuterQuotes -Value $CurrentWord foreach ($value in $Values) { if ($value.StartsWith($cleanCurrent, [System.StringComparison]::OrdinalIgnoreCase)) { New-RustupCompletionResult -CompletionText $value -ListItemText $value -ResultType $ResultType -ToolTip $ToolTip } } } function Get-RustupValueCompletions { param( [string[]]$ContextPath, [string]$OptionName, [bool]$IsPositional, [int]$PositionalIndex, [string]$CurrentWord, [string]$Toolchain ) $values = @(Get-RustupValueCatalog -ContextPath $ContextPath -OptionName $OptionName -IsPositional $IsPositional -PositionalIndex $PositionalIndex -Toolchain $Toolchain) if ($values.Count -gt 0) { return @(Get-RustupNamedCompletions -Values $values -CurrentWord $CurrentWord -ToolTip 'rustup value' -ResultType 'ParameterValue') } $placeholder = Get-RustupPlaceholderText -ContextPath $ContextPath -OptionName $OptionName -IsPositional $IsPositional -PositionalIndex $PositionalIndex if ($placeholder) { if ([string]::IsNullOrWhiteSpace($CurrentWord) -or $placeholder.StartsWith($CurrentWord, [System.StringComparison]::OrdinalIgnoreCase)) { return @( New-RustupCompletionResult -CompletionText $placeholder -ListItemText $placeholder -ResultType 'ParameterValue' -ToolTip ('rustup placeholder for ' + $placeholder) ) } } @() } function Complete-Rustup { param( [string]$wordToComplete, [System.Management.Automation.Language.CommandAst]$commandAst, [int]$cursorPosition ) $currentWord = if ($cursorPosition -gt $commandAst.Extent.EndOffset) { '' } else { Get-RustupCurrentToken -Line $commandAst.ToString() -CursorPosition ($cursorPosition - $commandAst.Extent.StartOffset) -Fallback $wordToComplete } $tokensBeforeCurrent = @(Get-RustupTokensBeforeCursor -CommandAst $commandAst -CursorPosition $cursorPosition) if (($tokensBeforeCurrent.Count -eq 0) -and ($currentWord -match '^(?i)rustup(?:\.exe)?$')) { $currentWord = '' } elseif ([string]::IsNullOrEmpty($wordToComplete) -and -not [string]::IsNullOrEmpty($currentWord)) { $tokensBeforeCurrent = @($tokensBeforeCurrent + $currentWord) $currentWord = '' } $state = Get-RustupCommandState -TokensBeforeCurrent $tokensBeforeCurrent $contextPath = @( if ($state.PendingOption) { $state.PendingOptionPath } else { $state.ContextPath } ) $definition = Get-RustupHelpDefinition -CommandPath $contextPath # '--toolchain <name>' wins over a leading '+name'; either one scopes the # component and target lists. $scopeToolchain = if ($state.OptionValues.ContainsKey('--toolchain')) { $state.OptionValues['--toolchain'] } elseif ($state.ToolchainOverride) { $state.ToolchainOverride.Substring(1) } $scopeToolchain = Resolve-RustupScopeToolchain -Name $scopeToolchain if ($state.PendingOption) { if ($state.PendingOption -eq '--path') { return @(Get-RustupPathCompletions -InputPath $currentWord) } return @(Get-RustupValueCompletions -ContextPath $contextPath -OptionName $state.PendingOption -IsPositional $false -PositionalIndex -1 -CurrentWord $currentWord -Toolchain $scopeToolchain) } if (-not [string]::IsNullOrEmpty($currentWord) -and $currentWord.StartsWith('+') -and $state.ContextPath.Count -eq 0 -and $state.Positionals.Count -eq 0 -and -not $state.ToolchainOverride) { $typedToolchain = $currentWord.Substring(1) $overrideResults = New-Object System.Collections.Generic.List[object] foreach ($toolchain in (Get-RustupKnownToolchains)) { if ($toolchain.StartsWith($typedToolchain, [System.StringComparison]::OrdinalIgnoreCase)) { $overrideResults.Add((New-RustupCompletionResult -CompletionText ('+' + $toolchain) -ListItemText ('+' + $toolchain) -ResultType 'ParameterValue' -ToolTip 'Release channel or custom toolchain to use for this invocation.')) } } return @($overrideResults.ToArray()) } if (-not [string]::IsNullOrEmpty($currentWord) -and ($currentWord.StartsWith('-') -or $currentWord -eq '/')) { $cleanCurrent = Remove-RustupOuterQuotes -Value $currentWord $attached = [regex]::Match($cleanCurrent, '^(?<name>--[^=]+)=(?<value>.*)$') if ($attached.Success) { # Attached form: complete the value while keeping the '--opt=' prefix. $attachedName = $attached.Groups['name'].Value $attachedValue = $attached.Groups['value'].Value $attachedPrefix = $attachedName + '=' if (-not $definition.OptionMap.ContainsKey($attachedName) -or -not $definition.OptionMap[$attachedName].TakesValue) { return @() } $primaryName = $definition.OptionMap[$attachedName].PrimaryName if ($primaryName -eq '--path') { return @(Get-RustupPathCompletions -InputPath $attachedValue | ForEach-Object { New-RustupCompletionResult -CompletionText ($attachedPrefix + $_.CompletionText) -ListItemText $_.ListItemText -ResultType $_.ResultType -ToolTip $_.ToolTip }) } return @(Get-RustupValueCompletions -ContextPath $contextPath -OptionName $primaryName -IsPositional $false -PositionalIndex -1 -CurrentWord $attachedValue -Toolchain $scopeToolchain | ForEach-Object { New-RustupCompletionResult -CompletionText ($attachedPrefix + $_.CompletionText) -ListItemText $_.ListItemText -ResultType $_.ResultType -ToolTip $_.ToolTip }) } $optionResults = New-Object System.Collections.Generic.List[object] foreach ($option in $definition.Options) { foreach ($name in $option.Names) { if ($name.StartsWith($cleanCurrent, [System.StringComparison]::OrdinalIgnoreCase)) { $optionResults.Add((New-RustupCompletionResult -CompletionText $name -ListItemText $name -ResultType 'ParameterName' -ToolTip $option.Description)) } } } return @($optionResults.ToArray()) } if (($state.ContextPath -join ' ') -eq 'toolchain link' -and $state.Positionals.Count -eq 1) { return @(Get-RustupPathCompletions -InputPath $currentWord) } if ([string]::IsNullOrEmpty($currentWord) -and (($state.ContextPath -join ' ') -eq 'run') -and $state.Positionals.Count -ge 2) { return @( New-RustupCompletionResult -CompletionText '<argument>' -ListItemText '<argument>' -ResultType 'ParameterValue' -ToolTip 'Additional argument for the command run through rustup.' ) } $results = New-Object System.Collections.Generic.List[object] $valueResults = @(Get-RustupValueCompletions -ContextPath $state.ContextPath -OptionName $null -IsPositional $true -PositionalIndex $state.Positionals.Count -CurrentWord $currentWord -Toolchain $scopeToolchain) foreach ($item in $valueResults) { $results.Add($item) } if ($state.Positionals.Count -eq 0) { foreach ($command in $definition.Commands) { if ($command.Name.StartsWith($currentWord, [System.StringComparison]::OrdinalIgnoreCase)) { $results.Add((New-RustupCompletionResult -CompletionText $command.Name -ListItemText $command.Name -ResultType 'ParameterValue' -ToolTip $command.Description)) } } } if ([string]::IsNullOrEmpty($currentWord)) { foreach ($option in $definition.Options) { foreach ($name in $option.Names) { $results.Add((New-RustupCompletionResult -CompletionText $name -ListItemText $name -ResultType 'ParameterName' -ToolTip $option.Description)) } } } @($results.ToArray()) } Register-ArgumentCompleter -Native -CommandName @('rustup', 'rustup.exe') -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) Complete-Rustup -wordToComplete $wordToComplete -commandAst $commandAst -cursorPosition $cursorPosition } |