The unpac monorepo manager self-hosting as a monorepo using unpac
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

completion: add support for pwsh

Close #214, close 245

authored by

Brian Ward and committed by
Daniel Bünzli
7f157e69 8ce20086

+464 -11
+8 -3
vendor/opam/cmdliner/B0.ml
··· 63 63 B0_unit.of_action "update-cmdliner-data" @@ fun env _ ~args:_ -> 64 64 let bash = B0_env.in_scope_dir env ~/"src/tool/bash-completion.sh" in 65 65 let zsh = B0_env.in_scope_dir env ~/"src/tool/zsh-completion.sh" in 66 + let pwsh = B0_env.in_scope_dir env ~/"src/tool/pwsh-completion.ps1" in 66 67 let ml = B0_env.in_scope_dir env ~/"src/tool/cmdliner_data.ml" in 67 68 let* bash = Os.File.read bash in 68 69 let bash = String.replace_first ~sub:"_cmdliner_generic" ~by:"%s" bash in 69 70 let* zsh = Os.File.read zsh in 70 71 let zsh = String.replace_first ~sub:"_cmdliner_generic" ~by:"%s" zsh in 72 + let* pwsh = Os.File.read pwsh in 73 + let pwsh = String.replace_first ~sub:"_cmdliner_generic" ~by:"%s" pwsh in 71 74 let src = Fmt.str 72 75 "let strf = Printf.sprintf\n\n\ 73 - let bash_generic_completion fun_name = strf\n{|%s\ 76 + let bash_generic_completion fun_name = strf \n{|%s\ 77 + |} fun_name\n\n\ 78 + let zsh_generic_completion fun_name = strf \n{|%s\ 74 79 |} fun_name\n\n\ 75 - let zsh_generic_completion fun_name = strf\n{|%s\ 76 - |} fun_name" bash zsh 80 + let pwsh_generic_completion fun_name = strf \n{|%s\ 81 + |} fun_name" bash zsh pwsh 77 82 in 78 83 Os.File.write ~force:true ~make_path:false ml src 79 84
+3
vendor/opam/cmdliner/CHANGES.md
··· 1 + 2 + - completion: add support for powershell (#214) 3 + Thanks to Brian Ward for the work. 1 4 2 5 - cmdliner tool: add support for generating standalone completion 3 6 scripts via the `--standalone-completion`. Can be used if your
+5 -4
vendor/opam/cmdliner/DEVEL.md
··· 15 15 16 16 source ./src/tool/zsh-completion.sh # zsh 17 17 source ./src/tool/bash-completion.sh # bash 18 - 19 - This replaces the generic completion function used by tool completion 18 + . ./src/tool/pwsh-completion.ps1 # powershell 19 + 20 + This replaces the generic completion function used by tool completion 20 21 scripts with the new definition. Trying to complete tools should now 21 - use the new definitions. To test the file, directory and path completions 22 - you can also use this test (invoke with `--help`) 22 + use the new definitions. To test the file, directory and path 23 + completions you can also use this test (invoke with `--help`) 23 24 24 25 b0 -- test_shell 25 26
+37 -3
vendor/opam/cmdliner/doc/cli.mld
··· 183 183 184 184 The completion process happens via a {{!completion_protocol}protocol} 185 185 which is interpreted by generic shell completion scripts that are 186 - installed by the library. For now the [zsh] and [bash] shells are 187 - supported. 186 + installed by the library. For now the [zsh], [bash], and PowerShell 187 + ([pwsh]) shells are supported. 188 188 189 189 Tool developers can easily {{!install_tool_completion}install} 190 190 completion definitions that invoke these completion scripts. Tool ··· 295 295 are on [< 2.12] and in [2.12] [_completion_loader] simply calls 296 296 [_comp_load]. 297 297 298 + {3:user_pwsh For PowerShell ([pwsh])} 299 + 300 + PowerShell Core supports tab completion but lacks the kind of 301 + discovery mechanism that other shells have for these scripts. 302 + 303 + Therefore, you need to explicitly source the completion script 304 + for cmdliner based tools. This can be done in the 305 + {{:https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles} [$profile]} 306 + file by adding something like the following: 307 + 308 + {@ps1[ 309 + # first, load the generic cmdliner completion function 310 + . "$(opam var share)/powershell/cmdliner-generic.ps1" 311 + # then, register each tool you want completions for 312 + # for example: 313 + . "$(opam var share)/powershell/cmdliner_completion.ps1" 314 + . "$(opam var share)/powershell/odoc_completion.ps1" 315 + # ... 316 + ]} 317 + 318 + Note that these instruction do not react dynamically to [opam] 319 + switches changes so you may see odd completion behaviours when you do 320 + so, see this {{:https://github.com/ocaml/opam/issues/6427}this opam 321 + issue}. 322 + 323 + With this setup, if you are using a cmdliner based tool named 324 + [thetool] that did not {{!install_tool_completion}install} a completion 325 + definition. You can always do it yourself by invoking: 326 + 327 + {@ps1[ 328 + Register-ArgumentCompleter -Native -CommandName thetool -ScriptBlock $Global:_cmdliner_generic 329 + ]} 330 + 298 331 {2:install_completion Install} 299 332 300 333 Completion scripts need to be installed in subdirectories of a ··· 310 343 311 344 {ul 312 345 {- For [zsh] it is [$SHAREDIR/zsh/site-functions]} 313 - {- For [bash] it is [$SHAREDIR/bash-completion/completions]}} 346 + {- For [bash] it is [$SHAREDIR/bash-completion/completions]} 347 + {- For [pwsh] it is [$SHAREDIR/powershell]}} 314 348 315 349 If that is unsatisfying you can output the completion scripts directly 316 350 where you want with the [cmdliner generic-completion] and
+194
vendor/opam/cmdliner/src/tool/cmdliner_data.ml
··· 154 154 _describe -V unsorted completions -U 155 155 fi 156 156 } 157 + |} fun_name 158 + 159 + let pwsh_generic_completion fun_name = strf 160 + {|<# 161 + Note: PowerShell swallows all errors in tab completion functions. 162 + If you are hacking on this file and get unexpected results (like 163 + file completions where you don't expect them), inspect the most 164 + recent errors with `Get-Error -Newest N` for some small N. 165 + #> 166 + 167 + $Global:%s = { 168 + param( 169 + $wordToComplete, 170 + $commandAst, 171 + $cursorPosition 172 + ) 173 + 174 + $exe = $commandAst.CommandElements[0].Extent.Text 175 + $otherArgs = "" 176 + 177 + $seenWordToComplete = $false 178 + foreach ($elem in $commandAst.CommandElements | Select-Object -Skip 1) { 179 + $text = $elem.Extent.Text 180 + if ($otherArgs -ne "") { 181 + $otherArgs += " " 182 + } 183 + if ($text -eq $wordToComplete) { 184 + $otherArgs += "--__complete=$text" 185 + $seenWordToComplete = $true 186 + } 187 + else { 188 + $otherArgs += $text 189 + } 190 + } 191 + # usually if wordToComplete is not in commandAst, it's because it's an empty string 192 + if (-not $seenWordToComplete) { 193 + $otherArgs += " --__complete=$wordToComplete" 194 + } 195 + 196 + $completions = Invoke-Expression "$exe --__complete $otherArgs" 197 + 198 + $version = $completions[0] 199 + if ($version -ne "1") { 200 + throw "Unsupported cmdliner completion protocol: $version" 201 + } 202 + 203 + $prefix = "" 204 + $pattern = $wordToComplete 205 + if ($wordToComplete -match '^\-\-') { 206 + # take everything before '=' as prefix for long options 207 + $parts = $wordToComplete -split '=', 2 208 + $prefix = $parts[0] + '=' 209 + $pattern = $parts[1] 210 + } 211 + elseif ($wordToComplete -match '^\-') { 212 + # take first two characters of wordToComplete as prefix for short options 213 + $prefix = $wordToComplete.Substring(0, [math]::Min(2, $wordToComplete.Length)) 214 + $pattern = $wordToComplete.Substring(2) 215 + } 216 + 217 + $CompletionResults = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new() 218 + 219 + $idx = 1 220 + $group = "" 221 + while ($idx -lt $completions.Count) { 222 + $type = $completions[$idx] 223 + $idx += 1 224 + 225 + switch ($type) { 226 + "group" { 227 + $group = $completions[$idx] 228 + $idx += 1 229 + } 230 + "item" { 231 + $item = $completions[$idx] 232 + $idx += 1 233 + 234 + $itemDoc = "" 235 + while ($true) { 236 + $line = $completions[$idx] 237 + $idx += 1 238 + if ($line -eq "item-end") { 239 + break 240 + } 241 + if ($itemDoc -ne "") { 242 + $itemDoc += "`n" 243 + } 244 + $itemDoc += $line 245 + } 246 + # avoid null tooltip error 247 + if ($itemDoc -eq "") { 248 + $itemDoc = $item 249 + } 250 + 251 + $completionItem = $item 252 + # re-add prefix for things like --foo= 253 + if ($group -eq "Values") { 254 + $item = $prefix + $item 255 + } 256 + 257 + $CompletionResults.Add( 258 + [System.Management.Automation.CompletionResult]::new( 259 + $item, 260 + $completionItem, 261 + 'ParameterValue', 262 + $itemDoc)) 263 + } 264 + "files" { 265 + [Management.Automation.CompletionCompleters]::CompleteFilename( 266 + $pattern 267 + ) | ForEach-Object { 268 + $CompletionResults.Add( 269 + [System.Management.Automation.CompletionResult]::new( 270 + $prefix + $_.CompletionText, 271 + $_.ListItemText, 272 + $_.ResultType, 273 + $_.ToolTip)) 274 + } 275 + } 276 + "dirs" { 277 + [Management.Automation.CompletionCompleters]::CompleteFilename( 278 + $pattern 279 + ) | Where-Object { 280 + Test-Path $_.CompletionText -PathType Container 281 + } | ForEach-Object { 282 + $CompletionResults.Add( 283 + [System.Management.Automation.CompletionResult]::new( 284 + $prefix + $_.CompletionText, 285 + $_.ListItemText, 286 + $_.ResultType, 287 + $_.ToolTip)) 288 + } 289 + } 290 + "restart" { 291 + $newCommand = "" 292 + $seenDoubleDash = $false 293 + 294 + $newCursorPosition = 0 295 + for ($i = 1; $i -lt $commandAst.CommandElements.Count; $i++) { 296 + $elem = $commandAst.CommandElements[$i] 297 + if ($seenDoubleDash) { 298 + if ($newCommand -ne "") { 299 + $newCommand += " " 300 + } 301 + $newCommand += $elem.Extent.Text 302 + if ($elem.Extent.Text -eq $wordToComplete) { 303 + $newCursorPosition = $newCommand.Length 304 + } 305 + } 306 + if ($elem.Extent.Text -eq "--") { 307 + $seenDoubleDash = $true 308 + } 309 + } 310 + 311 + if ($newCursorPosition -eq 0) { 312 + $newCommand += " " 313 + $newCursorPosition = $newCommand.Length 314 + } 315 + 316 + $newCommandCompletions = TabExpansion2 -inputScript $newCommand -cursorColumn $newCursorPosition 317 + 318 + $newCommandCompletions.CompletionMatches | ForEach-Object { 319 + $CompletionResults.Add($_) 320 + } 321 + } 322 + "message" { 323 + $msg = "" 324 + while ($true) { 325 + $line = $completions[$idx] 326 + $idx += 1 327 + if ($line -eq "message-end") { 328 + break 329 + } 330 + if ($msg -ne "") { 331 + $msg += "`n" 332 + } 333 + $msg += $line 334 + } 335 + Write-Output "$msg" | Out-Host 336 + } 337 + default { 338 + throw "Unknown completion type: $type" 339 + } 340 + } 341 + } 342 + 343 + if ($CompletionResults.Count -gt 0) { 344 + return $CompletionResults 345 + } 346 + else { 347 + # supress default behavior (file completion) when there are no completions 348 + return $null 349 + } 350 + } 157 351 |} fun_name
+26 -1
vendor/opam/cmdliner/src/tool/cmdliner_main.ml
··· 314 314 |} toolname fun_def fun_name 315 315 end 316 316 317 - let shells : shell list = [(module Bash); (module Zsh)] 317 + module Pwsh = struct 318 + let name = "pwsh" 319 + let sharedir = "powershell" 320 + let generic_script_name = "_cmdliner_generic.ps1" 321 + let generic_completion = 322 + Cmdliner_data.pwsh_generic_completion "_cmdliner_generic" 323 + 324 + let tool_script_name ~toolname = strf "%s_completion.ps1" toolname 325 + let tool_completion ~toolname ~standalone = 326 + if not standalone then 327 + strf 328 + {|Register-ArgumentCompleter -Native -CommandName %s -ScriptBlock $Global:_cmdliner_generic 329 + |} toolname 330 + else 331 + let munge s = String.map (function '-' -> '_' | c -> c) s in 332 + let fun_name = strf "_%s_cmdliner" (munge toolname) in 333 + let fun_def = Cmdliner_data.pwsh_generic_completion fun_name in 334 + strf 335 + {| 336 + %s 337 + 338 + Register-ArgumentCompleter -Native -CommandName %s -ScriptBlock %s 339 + |} fun_def toolname fun_name 340 + end 341 + 342 + let shells : shell list = [(module Bash); (module Zsh); (module Pwsh)] 318 343 319 344 let generic_completion (module Shell : SHELL) = 320 345 with_binary_stdout @@ fun () ->
+191
vendor/opam/cmdliner/src/tool/pwsh-completion.ps1
··· 1 + <# 2 + Note: PowerShell swallows all errors in tab completion functions. 3 + If you are hacking on this file and get unexpected results (like 4 + file completions where you don't expect them), inspect the most 5 + recent errors with `Get-Error -Newest N` for some small N. 6 + #> 7 + 8 + $Global:_cmdliner_generic = { 9 + param( 10 + $wordToComplete, 11 + $commandAst, 12 + $cursorPosition 13 + ) 14 + 15 + $exe = $commandAst.CommandElements[0].Extent.Text 16 + $otherArgs = "" 17 + 18 + $seenWordToComplete = $false 19 + foreach ($elem in $commandAst.CommandElements | Select-Object -Skip 1) { 20 + $text = $elem.Extent.Text 21 + if ($otherArgs -ne "") { 22 + $otherArgs += " " 23 + } 24 + if ($text -eq $wordToComplete) { 25 + $otherArgs += "--__complete=$text" 26 + $seenWordToComplete = $true 27 + } 28 + else { 29 + $otherArgs += $text 30 + } 31 + } 32 + # usually if wordToComplete is not in commandAst, it's because it's an empty string 33 + if (-not $seenWordToComplete) { 34 + $otherArgs += " --__complete=$wordToComplete" 35 + } 36 + 37 + $completions = Invoke-Expression "$exe --__complete $otherArgs" 38 + 39 + $version = $completions[0] 40 + if ($version -ne "1") { 41 + throw "Unsupported cmdliner completion protocol: $version" 42 + } 43 + 44 + $prefix = "" 45 + $pattern = $wordToComplete 46 + if ($wordToComplete -match '^\-\-') { 47 + # take everything before '=' as prefix for long options 48 + $parts = $wordToComplete -split '=', 2 49 + $prefix = $parts[0] + '=' 50 + $pattern = $parts[1] 51 + } 52 + elseif ($wordToComplete -match '^\-') { 53 + # take first two characters of wordToComplete as prefix for short options 54 + $prefix = $wordToComplete.Substring(0, [math]::Min(2, $wordToComplete.Length)) 55 + $pattern = $wordToComplete.Substring(2) 56 + } 57 + 58 + $CompletionResults = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new() 59 + 60 + $idx = 1 61 + $group = "" 62 + while ($idx -lt $completions.Count) { 63 + $type = $completions[$idx] 64 + $idx += 1 65 + 66 + switch ($type) { 67 + "group" { 68 + $group = $completions[$idx] 69 + $idx += 1 70 + } 71 + "item" { 72 + $item = $completions[$idx] 73 + $idx += 1 74 + 75 + $itemDoc = "" 76 + while ($true) { 77 + $line = $completions[$idx] 78 + $idx += 1 79 + if ($line -eq "item-end") { 80 + break 81 + } 82 + if ($itemDoc -ne "") { 83 + $itemDoc += "`n" 84 + } 85 + $itemDoc += $line 86 + } 87 + # avoid null tooltip error 88 + if ($itemDoc -eq "") { 89 + $itemDoc = $item 90 + } 91 + 92 + $completionItem = $item 93 + # re-add prefix for things like --foo= 94 + if ($group -eq "Values") { 95 + $item = $prefix + $item 96 + } 97 + 98 + $CompletionResults.Add( 99 + [System.Management.Automation.CompletionResult]::new( 100 + $item, 101 + $completionItem, 102 + 'ParameterValue', 103 + $itemDoc)) 104 + } 105 + "files" { 106 + [Management.Automation.CompletionCompleters]::CompleteFilename( 107 + $pattern 108 + ) | ForEach-Object { 109 + $CompletionResults.Add( 110 + [System.Management.Automation.CompletionResult]::new( 111 + $prefix + $_.CompletionText, 112 + $_.ListItemText, 113 + $_.ResultType, 114 + $_.ToolTip)) 115 + } 116 + } 117 + "dirs" { 118 + [Management.Automation.CompletionCompleters]::CompleteFilename( 119 + $pattern 120 + ) | Where-Object { 121 + Test-Path $_.CompletionText -PathType Container 122 + } | ForEach-Object { 123 + $CompletionResults.Add( 124 + [System.Management.Automation.CompletionResult]::new( 125 + $prefix + $_.CompletionText, 126 + $_.ListItemText, 127 + $_.ResultType, 128 + $_.ToolTip)) 129 + } 130 + } 131 + "restart" { 132 + $newCommand = "" 133 + $seenDoubleDash = $false 134 + 135 + $newCursorPosition = 0 136 + for ($i = 1; $i -lt $commandAst.CommandElements.Count; $i++) { 137 + $elem = $commandAst.CommandElements[$i] 138 + if ($seenDoubleDash) { 139 + if ($newCommand -ne "") { 140 + $newCommand += " " 141 + } 142 + $newCommand += $elem.Extent.Text 143 + if ($elem.Extent.Text -eq $wordToComplete) { 144 + $newCursorPosition = $newCommand.Length 145 + } 146 + } 147 + if ($elem.Extent.Text -eq "--") { 148 + $seenDoubleDash = $true 149 + } 150 + } 151 + 152 + if ($newCursorPosition -eq 0) { 153 + $newCommand += " " 154 + $newCursorPosition = $newCommand.Length 155 + } 156 + 157 + $newCommandCompletions = TabExpansion2 -inputScript $newCommand -cursorColumn $newCursorPosition 158 + 159 + $newCommandCompletions.CompletionMatches | ForEach-Object { 160 + $CompletionResults.Add($_) 161 + } 162 + } 163 + "message" { 164 + $msg = "" 165 + while ($true) { 166 + $line = $completions[$idx] 167 + $idx += 1 168 + if ($line -eq "message-end") { 169 + break 170 + } 171 + if ($msg -ne "") { 172 + $msg += "`n" 173 + } 174 + $msg += $line 175 + } 176 + Write-Output "$msg" | Out-Host 177 + } 178 + default { 179 + throw "Unknown completion type: $type" 180 + } 181 + } 182 + } 183 + 184 + if ($CompletionResults.Count -gt 0) { 185 + return $CompletionResults 186 + } 187 + else { 188 + # supress default behavior (file completion) when there are no completions 189 + return $null 190 + } 191 + }