Complete/CompletionUtil.ps1
# Copyright (C) 2024 kzrnm # Based on git-completion.bash (https://github.com/git/git/blob/HEAD/contrib/completion/git-completion.bash). # Distributed under the GNU General Public License, version 2.0. using namespace System.Management.Automation; function completeList { [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = 'Default')] [OutputType([CompletionResult[]])] param( [AllowEmptyString()] [string] $Current = '', [Parameter(ParameterSetName = 'Default')] [Parameter(Mandatory, ParameterSetName = 'Prefix')] [AllowEmptyString()] [string] $Prefix = '', [string] $Suffix = '', [scriptblock] $DescriptionBuilder = $null, [CompletionResultType] $ResultType = [CompletionResultType]::ParameterName, [Parameter(ParameterSetName = 'Prefix')] [switch] $RemovePrefix, $Exclude = $null, [Parameter(ValueFromPipeline)] [string] $Candidate ) begin { if ($RemovePrefix -and $Current.StartsWith($Prefix)) { $Current = $Current.Substring($Prefix.Length) } if ($Exclude) { $ExcludeSet = [System.Collections.Generic.HashSet[string]]::new() foreach ($e in $Exclude) { $ExcludeSet.Add($e) | Out-Null } } } process { if ((!$Current) -or $Candidate.StartsWith($Current)) { $Completion = "$Prefix$Candidate$Suffix" if ($ExcludeSet -and $ExcludeSet.Contains($Completion)) { return } $desc = $null if ($DescriptionBuilder) { $desc = [string]$DescriptionBuilder.InvokeWithContext( $null, [psvariable]::new('_', $Candidate), @($Candidate) ) } if (!$desc) { $desc = "$Candidate" } $ListItem = $Candidate [CompletionResult]::new( $Completion, $ListItem, $ResultType, $desc ) } } } function filterCompletionResult { param ( [Parameter(ValueFromPipeline)] [CompletionResult] $Completion, [Parameter(Position = 0)] [string] $Current = '' ) process { if ($Completion.ListItemText.StartsWith($Current)) { $Completion } } } # Generates completion reply, appending a space to possible completion words, # if necessary. # It accepts 1 to 4 arguments: # 1: List of possible completion words. # 2: A prefix to be added to each possible completion word (optional). # 3: Generate possible completion matches for this word (optional). # 4: A suffix to be appended to each possible completion word (optional). function gitcomp { [OutputType([CompletionResult[]])] [CmdletBinding(PositionalBinding = $false)] param( [Parameter(Mandatory)] [AllowEmptyString()] $Current, [string]$Prefix = '', [string]$Suffix = '', [scriptblock] $DescriptionBuilder = $null, [Parameter(ValueFromPipeline)] [string] $Candidate ) begin { switch -Wildcard ($Current) { '*=' { $Type = -1 } '--no-*' { $Type = 1 } Default { $Type = 0 } } function buildDescription { param ( [Parameter(Position = 0)] [string] $Candidate ) $desc = $null if ($DescriptionBuilder) { $desc = [string]$DescriptionBuilder.InvokeWithContext( $null, [psvariable]::new('_', $Candidate), @($Candidate) ) } if (!$desc) { $desc = "$c" } return $desc } } process { $cw = "$Candidate$Suffix" $c = "$Prefix$cw" switch ($Type) { -1 { } 1 { if ($cw.StartsWith($Current)) { [CompletionResult]::new( $c, $c, 'ParameterName', (buildDescription $Candidate) ) } } Default { if ($Candidate -eq '--') { if ('--no-'.StartsWith($Current)) { [CompletionResult]::new( "--no-", "--no-...$Suffix", 'Text', "--no-...$Suffix" ) } $Type = -1 } else { if ($cw.StartsWith($Current)) { [CompletionResult]::new( $c, $c, 'ParameterName', (buildDescription $Candidate) ) } } } } } } function buildWords { [CmdletBinding(PositionalBinding)] param($CommandAst, $CursorPosition) $ws = [System.Collections.Generic.List[string]]::new($CommandAst.CommandElements.Count + 2) $CurrentIndex = 0 for ($i = 0; $i -lt $CommandAst.CommandElements.Count; $i++) { $cmd = $CommandAst.CommandElements[$i] $extent = $cmd.Extent if ($null -eq $cmd.Value) { $text = $extent.Text } else { $text = $cmd.Value } if (!$CurrentIndex -and ($CursorPosition -le $extent.EndOffset)) { $CurrentIndex = $i if ($CursorPosition -le $extent.StartOffset) { $ws.Add('') } } $ws.Add($text) } if (!$CurrentIndex) { $CurrentIndex = $ws.Count $ws.Add('') } return $ws.ToArray(), $CurrentIndex } |