Complete/GitRoot.ps1
using namespace System.Management.Automation; function Complete-Git { [CmdletBinding(PositionalBinding = $false)] [OutputType([CompletionResult[]])] param( [Parameter(Mandatory, ParameterSetName = 'String')] [string[]] [AllowEmptyCollection()] [AllowEmptyString()] $Words, [Parameter(ParameterSetName = 'String')] [int] $CurrentIndex = -1, [Parameter(Mandatory, ParameterSetName = 'Ast')] [Language.CommandAst] [AllowEmptyCollection()] [AllowEmptyString()] $CommandAst, [Parameter(Mandatory, ParameterSetName = 'Ast')] [int] $CursorPosition ) if ($PSCmdlet.ParameterSetName -eq 'Ast') { $Words, $CurrentIndex = buildWords $CommandAst $CursorPosition } if ($CurrentIndex -lt 0) { $CurrentIndex = $Words.Length - 1 } return Complete-GitCommandLine ([CommandLineContext]::new($Words, $CurrentIndex)) } class CommandOption { [string] $Short [string] $Long [string] $Description [string] $Value CommandOption ([string]$short, [string]$long, $description, $value) { $this.Short = $Short $this.Long = $Long $this.Description = $Description $this.Value = $Value } [CompletionResult] ToLongCompletion([string]$Prefix) { if ($this.Long -and $this.Long.StartsWith($Prefix)) { return [CompletionResult]::new( $this.Long, $this.Long + "$(if($this.Value){" $($this.Value)"})", 'ParameterName', "$(if($this.Description){$this.Description}else{$this.Long})" ) } return $null } [CompletionResult] ToShortCompletion() { if ($this.Short) { return [CompletionResult]::new( $this.Short, $this.Short + "$(if($this.Value){" $($this.Value)"})", 'ParameterName', "$(if($this.Description){$this.Description}else{$this.Short})" ) } return $null } } function New-CommandOption { [CmdletBinding()] param ( [string]$Short = '', [string]$Long = '', [string]$Desc = '', [string]$Value = '' ) [CommandOption]::new($Short, $Long, $Desc, $Value) } $gitGlobalOptions = @( ( New-CommandOption -Short '-v' -Long '--version' ` -Desc 'Prints the Git suite version' ), ( New-CommandOption -Short '-h' -Long '--help' ` -Desc 'Prints the helps. If --all is given then all available commands are printed' ), ( New-CommandOption -Short '-C' ` -Value '<path>' ` -Desc 'Run as if git was started in <path> instead of the current working directory' ), ( New-CommandOption -Short '-c' ` -Value '<name>=<value>' ` -Desc 'Pass a configuration parameter to the command' ), ( New-CommandOption -Long '--config-env' ` -Value '<name>=<envvar>' ` -Desc 'Like -c <name>=<value>, give configuration variable <name> a value, where <envvar> is the name of an environment variable from which to retrieve the value' ), ( New-CommandOption -Long '--exec-path' ` -Value '<path>' ` -Desc 'Path to wherever your core Git programs are installed' ), ( New-CommandOption -Long '--html-path' ` -Desc "Print the path, without trailing slash, where Git’s HTML documentation is installed and exit" ), ( New-CommandOption -Long '--man-path' ` -Desc 'Print the manpath for the man pages for this version of Git and exit' ), ( New-CommandOption -Long '--info-path' ` -Desc 'Print the path where the Info files documenting this version of Git are installed and exit' ), ( New-CommandOption -Short '-p' -Long '--paginate' ` -Desc 'Pipe all output into less (or if set, $PAGER) if standard output is a terminal' ), ( New-CommandOption -Short '-P' -Long '--no-pager' ` -Desc 'Do not pipe Git output into a pager' ), ( New-CommandOption -Long '--git-dir' ` -Desc 'Set the path to the repository (".git" directory)' ), ( New-CommandOption -Long '--work-tree' ` -Value '<path>' ` -Desc 'Set the path to the working tree' ), ( New-CommandOption -Long '--namespace' ` -Value '<path>' ` -Desc 'Set the Git namespace' ), ( New-CommandOption -Long '--bare' ` -Desc 'Treat the repository as a bare repository' ), ( New-CommandOption -Long '--no-replace-objects' ` -Desc 'Do not use replacement refs to replace Git objects' ), ( New-CommandOption -Long '--no-lazy-fetch' ` -Desc 'Do not fetch missing objects from the promisor remote on demand' ), ( New-CommandOption -Long '--literal-pathspecs' ` -Desc 'Treat pathspecs literally (i.e. no globbing, no pathspec magic)' ), ( New-CommandOption -Long '--glob-pathspecs' ` -Desc 'Add "glob" magic to all pathspec' ), ( New-CommandOption -Long '--noglob-pathspecs' ` -Desc 'Add "literal" magic to all pathspec' ), ( New-CommandOption -Long '--icase-pathspecs' ` -Desc 'Add "icase" magic to all pathspec' ), ( New-CommandOption -Long '--no-optional-locks' ` -Desc 'Do not perform optional operations that require locks' ), ( New-CommandOption -Long '--list-cmds' ` -Value '<group>[,<group>…]' ` -Desc 'List commands by group' ), ( New-CommandOption -Long '--no-replace-objects' ` -Desc 'List commands by group' ), ( New-CommandOption -Long '--attr-source' ` -Value '<tree-ish>' ` -Desc 'Read gitattributes from <tree-ish> instead of the worktree' ) ) function resolveAliasContext { [CmdletBinding()] [OutputType([CommandLineContext])] param ( [CommandLineContext][Parameter(Position = 0, Mandatory)]$Context ) # Avoid infinite loop for ($i = 20; $Context.Command -and $i; $i--) { $aliasValue = gitGetAlias $Context.Command if (!$aliasValue) { return $Context } [string[]]$resolved = gitParseShellArgs $aliasValue if (!$Context.ReplaceCommand($resolved)) { break } } return $Context } function Complete-GitCommandLine { [CmdletBinding(PositionalBinding = $false)] [OutputType([CompletionResult[]])] param( [CommandLineContext][Parameter(Position = 0, Mandatory)]$Context ) $Context = resolveAliasContext $Context try { Set-Variable 'Context' $Context -Scope 'Script' [string] $Current = $Context.CurrentWord() if ($Context.Command) { try { $completeSubcommandFunc = "Complete-GitSubCommand-$($Context.Command)" . $completeSubcommandFunc $Context } catch { Complete-GitSubCommandCommon $Context } return } switch -Wildcard -CaseSensitive ($Context.PreviousWord()) { { $_ -cin @('-C', '--work-tree', '--git-dir', '--') } { # these need a path argument return } '-c' { return completeConfigOptionVariableNameAndValue -Current $Current } '--namespace' { # we don't support completing these options' arguments return } } if ($Current -eq '-') { $gitGlobalOptions | ForEach-Object { $_.ToShortCompletion() } | Where-Object { $_ } return } elseif ($Current -like '--*') { $gitGlobalOptions | ForEach-Object { $_.ToLongCompletion($Current) } | Where-Object { $_ } return } $aliases = @{} foreach ($a in (gitListAliases)) { $aliases[$a.Name] = "[alias] $($a.Value)" } listCommands | completeList -Current $Current -DescriptionBuilder { $a = $aliases[$_] if ($a) { $a } else { Get-GitCommandDescription $_ } } -ResultType Text } finally { Remove-Variable 'Context' -Scope 'Script' } } |