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
13Register-ArgumentCompleter -CommandName 'git-bug' -ScriptBlock {
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
44 # Prepare the command to request completions for the program.
45 # Split the command at the first space to separate the program and arguments.
46 $Program,$Arguments = $Command.Split(" ",2)
47 $RequestComp="$Program __completeNoDesc $Arguments"
48 __git-bug_debug "RequestComp: $RequestComp"
49
50 # we cannot use $WordToComplete because it
51 # has the wrong values if the cursor was moved
52 # so use the last argument
53 if ($WordToComplete -ne "" ) {
54 $WordToComplete = $Arguments.Split(" ")[-1]
55 }
56 __git-bug_debug "New WordToComplete: $WordToComplete"
57
58
59 # Check for flag with equal sign
60 $IsEqualFlag = ($WordToComplete -Like "--*=*" )
61 if ( $IsEqualFlag ) {
62 __git-bug_debug "Completing equal sign flag"
63 # Remove the flag part
64 $Flag,$WordToComplete = $WordToComplete.Split("=",2)
65 }
66
67 if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) {
68 # If the last parameter is complete (there is a space following it)
69 # We add an extra empty parameter so we can indicate this to the go method.
70 __git-bug_debug "Adding extra empty parameter"
71 # We need to use `"`" to pass an empty argument a "" or '' does not work!!!
72 $RequestComp="$RequestComp" + ' `"`"'
73 }
74
75 __git-bug_debug "Calling $RequestComp"
76 #call the command store the output in $out and redirect stderr and stdout to null
77 # $Out is an array contains each line per element
78 Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null
79
80
81 # get directive from last line
82 [int]$Directive = $Out[-1].TrimStart(':')
83 if ($Directive -eq "") {
84 # There is no directive specified
85 $Directive = 0
86 }
87 __git-bug_debug "The completion directive is: $Directive"
88
89 # remove directive (last element) from out
90 $Out = $Out | Where-Object { $_ -ne $Out[-1] }
91 __git-bug_debug "The completions are: $Out"
92
93 if (($Directive -band $ShellCompDirectiveError) -ne 0 ) {
94 # Error code. No completion.
95 __git-bug_debug "Received error from custom completion go code"
96 return
97 }
98
99 $Longest = 0
100 $Values = $Out | ForEach-Object {
101 #Split the output in name and description
102 $Name, $Description = $_.Split("`t",2)
103 __git-bug_debug "Name: $Name Description: $Description"
104
105 # Look for the longest completion so that we can format things nicely
106 if ($Longest -lt $Name.Length) {
107 $Longest = $Name.Length
108 }
109
110 # Set the description to a one space string if there is none set.
111 # This is needed because the CompletionResult does not accept an empty string as argument
112 if (-Not $Description) {
113 $Description = " "
114 }
115 @{Name="$Name";Description="$Description"}
116 }
117
118
119 $Space = " "
120 if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) {
121 # remove the space here
122 __git-bug_debug "ShellCompDirectiveNoSpace is called"
123 $Space = ""
124 }
125
126 if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
127 __git-bug_debug "ShellCompDirectiveNoFileComp is called"
128
129 if ($Values.Length -eq 0) {
130 # Just print an empty string here so the
131 # shell does not start to complete paths.
132 # We cannot use CompletionResult here because
133 # it does not accept an empty string as argument.
134 ""
135 return
136 }
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 a 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 # Get the current mode
159 $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function
160 __git-bug_debug "Mode: $Mode"
161
162 $Values | ForEach-Object {
163
164 # store temporay because switch will overwrite $_
165 $comp = $_
166
167 # PowerShell supports three different completion modes
168 # - TabCompleteNext (default windows style - on each key press the next option is displayed)
169 # - Complete (works like bash)
170 # - MenuComplete (works like zsh)
171 # You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function <mode>
172
173 # CompletionResult Arguments:
174 # 1) CompletionText text to be used as the auto completion result
175 # 2) ListItemText text to be displayed in the suggestion list
176 # 3) ResultType type of completion result
177 # 4) ToolTip text for the tooltip with details about the object
178
179 switch ($Mode) {
180
181 # bash like
182 "Complete" {
183
184 if ($Values.Length -eq 1) {
185 __git-bug_debug "Only one completion left"
186
187 # insert space after value
188 [System.Management.Automation.CompletionResult]::new($($comp.Name | __git-bug_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
189
190 } else {
191 # Add the proper number of spaces to align the descriptions
192 while($comp.Name.Length -lt $Longest) {
193 $comp.Name = $comp.Name + " "
194 }
195
196 # Check for empty description and only add parentheses if needed
197 if ($($comp.Description) -eq " " ) {
198 $Description = ""
199 } else {
200 $Description = " ($($comp.Description))"
201 }
202
203 [System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)")
204 }
205 }
206
207 # zsh like
208 "MenuComplete" {
209 # insert space after value
210 # MenuComplete will automatically show the ToolTip of
211 # the highlighted value at the bottom of the suggestions.
212 [System.Management.Automation.CompletionResult]::new($($comp.Name | __git-bug_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
213 }
214
215 # TabCompleteNext and in case we get something unknown
216 Default {
217 # Like MenuComplete but we don't want to add a space here because
218 # the user need to press space anyway to get the completion.
219 # Description will not be shown because thats not possible with TabCompleteNext
220 [System.Management.Automation.CompletionResult]::new($($comp.Name | __git-bug_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
221 }
222 }
223
224 }
225}