1# powershell completion for git-bug -*- shell-script -*-
2
3function __git-bug_debug {
4 if ($env:BASH_COMP_DEBUG_FILE) {
5 "$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE"
6 }
7}
8
9filter __git-bug_escapeStringWithSpecialChars {
10 $_ -replace '\s|#|@|\$|;|,|''|\{|\}|\(|\)|"|`|\||<|>|&','`$&'
11}
12
13[scriptblock]$__git_bugCompleterBlock = {
14 param(
15 $WordToComplete,
16 $CommandAst,
17 $CursorPosition
18 )
19
20 # Get the current command line and convert into a string
21 $Command = $CommandAst.CommandElements
22 $Command = "$Command"
23
24 __git-bug_debug ""
25 __git-bug_debug "========= starting completion logic =========="
26 __git-bug_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition"
27
28 # The user could have moved the cursor backwards on the command-line.
29 # We need to trigger completion from the $CursorPosition location, so we need
30 # to truncate the command-line ($Command) up to the $CursorPosition location.
31 # Make sure the $Command is longer then the $CursorPosition before we truncate.
32 # This happens because the $Command does not include the last space.
33 if ($Command.Length -gt $CursorPosition) {
34 $Command=$Command.Substring(0,$CursorPosition)
35 }
36 __git-bug_debug "Truncated command: $Command"
37
38 $ShellCompDirectiveError=1
39 $ShellCompDirectiveNoSpace=2
40 $ShellCompDirectiveNoFileComp=4
41 $ShellCompDirectiveFilterFileExt=8
42 $ShellCompDirectiveFilterDirs=16
43 $ShellCompDirectiveKeepOrder=32
44
45 # Prepare the command to request completions for the program.
46 # Split the command at the first space to separate the program and arguments.
47 $Program,$Arguments = $Command.Split(" ",2)
48
49 $RequestComp="$Program __completeNoDesc $Arguments"
50 __git-bug_debug "RequestComp: $RequestComp"
51
52 # we cannot use $WordToComplete because it
53 # has the wrong values if the cursor was moved
54 # so use the last argument
55 if ($WordToComplete -ne "" ) {
56 $WordToComplete = $Arguments.Split(" ")[-1]
57 }
58 __git-bug_debug "New WordToComplete: $WordToComplete"
59
60
61 # Check for flag with equal sign
62 $IsEqualFlag = ($WordToComplete -Like "--*=*" )
63 if ( $IsEqualFlag ) {
64 __git-bug_debug "Completing equal sign flag"
65 # Remove the flag part
66 $Flag,$WordToComplete = $WordToComplete.Split("=",2)
67 }
68
69 if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) {
70 # If the last parameter is complete (there is a space following it)
71 # We add an extra empty parameter so we can indicate this to the go method.
72 __git-bug_debug "Adding extra empty parameter"
73 # PowerShell 7.2+ changed the way how the arguments are passed to executables,
74 # so for pre-7.2 or when Legacy argument passing is enabled we need to use
75 # `"`" to pass an empty argument, a "" or '' does not work!!!
76 if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or
77 ($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or
78 (($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and
79 $PSNativeCommandArgumentPassing -eq 'Legacy')) {
80 $RequestComp="$RequestComp" + ' `"`"'
81 } else {
82 $RequestComp="$RequestComp" + ' ""'
83 }
84 }
85
86 __git-bug_debug "Calling $RequestComp"
87 # First disable ActiveHelp which is not supported for Powershell
88 $env:GIT_BUG_ACTIVE_HELP=0
89
90 #call the command store the output in $out and redirect stderr and stdout to null
91 # $Out is an array contains each line per element
92 Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null
93
94 # get directive from last line
95 [int]$Directive = $Out[-1].TrimStart(':')
96 if ($Directive -eq "") {
97 # There is no directive specified
98 $Directive = 0
99 }
100 __git-bug_debug "The completion directive is: $Directive"
101
102 # remove directive (last element) from out
103 $Out = $Out | Where-Object { $_ -ne $Out[-1] }
104 __git-bug_debug "The completions are: $Out"
105
106 if (($Directive -band $ShellCompDirectiveError) -ne 0 ) {
107 # Error code. No completion.
108 __git-bug_debug "Received error from custom completion go code"
109 return
110 }
111
112 $Longest = 0
113 [Array]$Values = $Out | ForEach-Object {
114 #Split the output in name and description
115 $Name, $Description = $_.Split("`t",2)
116 __git-bug_debug "Name: $Name Description: $Description"
117
118 # Look for the longest completion so that we can format things nicely
119 if ($Longest -lt $Name.Length) {
120 $Longest = $Name.Length
121 }
122
123 # Set the description to a one space string if there is none set.
124 # This is needed because the CompletionResult does not accept an empty string as argument
125 if (-Not $Description) {
126 $Description = " "
127 }
128 @{Name="$Name";Description="$Description"}
129 }
130
131
132 $Space = " "
133 if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) {
134 # remove the space here
135 __git-bug_debug "ShellCompDirectiveNoSpace is called"
136 $Space = ""
137 }
138
139 if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or
140 (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) {
141 __git-bug_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported"
142
143 # return here to prevent the completion of the extensions
144 return
145 }
146
147 $Values = $Values | Where-Object {
148 # filter the result
149 $_.Name -like "$WordToComplete*"
150
151 # Join the flag back if we have an equal sign flag
152 if ( $IsEqualFlag ) {
153 __git-bug_debug "Join the equal sign flag back to the completion value"
154 $_.Name = $Flag + "=" + $_.Name
155 }
156 }
157
158 # we sort the values in ascending order by name if keep order isn't passed
159 if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) {
160 $Values = $Values | Sort-Object -Property Name
161 }
162
163 if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
164 __git-bug_debug "ShellCompDirectiveNoFileComp is called"
165
166 if ($Values.Length -eq 0) {
167 # Just print an empty string here so the
168 # shell does not start to complete paths.
169 # We cannot use CompletionResult here because
170 # it does not accept an empty string as argument.
171 ""
172 return
173 }
174 }
175
176 # Get the current mode
177 $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function
178 __git-bug_debug "Mode: $Mode"
179
180 $Values | ForEach-Object {
181
182 # store temporary because switch will overwrite $_
183 $comp = $_
184
185 # PowerShell supports three different completion modes
186 # - TabCompleteNext (default windows style - on each key press the next option is displayed)
187 # - Complete (works like bash)
188 # - MenuComplete (works like zsh)
189 # You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function <mode>
190
191 # CompletionResult Arguments:
192 # 1) CompletionText text to be used as the auto completion result
193 # 2) ListItemText text to be displayed in the suggestion list
194 # 3) ResultType type of completion result
195 # 4) ToolTip text for the tooltip with details about the object
196
197 switch ($Mode) {
198
199 # bash like
200 "Complete" {
201
202 if ($Values.Length -eq 1) {
203 __git-bug_debug "Only one completion left"
204
205 # insert space after value
206 [System.Management.Automation.CompletionResult]::new($($comp.Name | __git-bug_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
207
208 } else {
209 # Add the proper number of spaces to align the descriptions
210 while($comp.Name.Length -lt $Longest) {
211 $comp.Name = $comp.Name + " "
212 }
213
214 # Check for empty description and only add parentheses if needed
215 if ($($comp.Description) -eq " " ) {
216 $Description = ""
217 } else {
218 $Description = " ($($comp.Description))"
219 }
220
221 [System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)")
222 }
223 }
224
225 # zsh like
226 "MenuComplete" {
227 # insert space after value
228 # MenuComplete will automatically show the ToolTip of
229 # the highlighted value at the bottom of the suggestions.
230 [System.Management.Automation.CompletionResult]::new($($comp.Name | __git-bug_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
231 }
232
233 # TabCompleteNext and in case we get something unknown
234 Default {
235 # Like MenuComplete but we don't want to add a space here because
236 # the user need to press space anyway to get the completion.
237 # Description will not be shown because that's not possible with TabCompleteNext
238 [System.Management.Automation.CompletionResult]::new($($comp.Name | __git-bug_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
239 }
240 }
241
242 }
243}
244
245Register-ArgumentCompleter -CommandName 'git-bug' -ScriptBlock $__git_bugCompleterBlock