ace2c2a7be8da3f2e27e1b73d7db96bf68316a4528323481221285c1941b024e5aa6d2340faebfd4b2e287c389336328fab155416d7850e358ff38bcc08920 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. ###-begin-{pkgname}-completion-###
  2. Register-ArgumentCompleter -CommandName '{pkgname}' -ScriptBlock {
  3. param(
  4. $WordToComplete,
  5. $CommandAst,
  6. $CursorPosition
  7. )
  8. function __{pkgname}_debug {
  9. if ($env:BASH_COMP_DEBUG_FILE) {
  10. "$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE"
  11. }
  12. }
  13. filter __{pkgname}_escapeStringWithSpecialChars {
  14. $_ -replace '\s|#|@|\$|;|,|''|\{|\}|\(|\)|"|`|\||<|>|&','`$&'
  15. }
  16. # Get the current command line and convert into a string
  17. $Command = $CommandAst.CommandElements
  18. $Command = "$Command"
  19. __{pkgname}_debug ""
  20. __{pkgname}_debug "========= starting completion logic =========="
  21. __{pkgname}_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition"
  22. # The user could have moved the cursor backwards on the command-line.
  23. # We need to trigger completion from the $CursorPosition location, so we need
  24. # to truncate the command-line ($Command) up to the $CursorPosition location.
  25. # Make sure the $Command is longer then the $CursorPosition before we truncate.
  26. # This happens because the $Command does not include the last space.
  27. if ($Command.Length -gt $CursorPosition) {
  28. $Command=$Command.Substring(0,$CursorPosition)
  29. }
  30. __{pkgname}_debug "Truncated command: $Command"
  31. # Prepare the command to request completions for the program.
  32. # Split the command at the first space to separate the program and arguments.
  33. $Program,$Arguments = $Command.Split(" ",2)
  34. $RequestComp="$Program completion-server"
  35. __{pkgname}_debug "RequestComp: $RequestComp"
  36. # we cannot use $WordToComplete because it
  37. # has the wrong values if the cursor was moved
  38. # so use the last argument
  39. if ($WordToComplete -ne "" ) {
  40. $WordToComplete = $Arguments.Split(" ")[-1]
  41. }
  42. __{pkgname}_debug "New WordToComplete: $WordToComplete"
  43. # Check for flag with equal sign
  44. $IsEqualFlag = ($WordToComplete -Like "--*=*" )
  45. if ( $IsEqualFlag ) {
  46. __{pkgname}_debug "Completing equal sign flag"
  47. # Remove the flag part
  48. $Flag,$WordToComplete = $WordToComplete.Split("=",2)
  49. }
  50. if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) {
  51. # If the last parameter is complete (there is a space following it)
  52. # We add an extra empty parameter so we can indicate this to the go method.
  53. __{pkgname}_debug "Adding extra empty parameter"
  54. # We need to use `"`" to pass an empty argument a "" or '' does not work!!!
  55. $Command="$Command" + ' `"`"'
  56. }
  57. __{pkgname}_debug "Calling $RequestComp"
  58. $oldenv = ($env:SHELL, $env:COMP_CWORD, $env:COMP_LINE, $env:COMP_POINT)
  59. $env:SHELL = "pwsh"
  60. $env:COMP_CWORD = $Command.Split(" ").Count - 1
  61. $env:COMP_POINT = $CursorPosition
  62. $env:COMP_LINE = $Command
  63. try {
  64. #call the command store the output in $out and redirect stderr and stdout to null
  65. # $Out is an array contains each line per element
  66. Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null
  67. } finally {
  68. ($env:SHELL, $env:COMP_CWORD, $env:COMP_LINE, $env:COMP_POINT) = $oldenv
  69. }
  70. __{pkgname}_debug "The completions are: $Out"
  71. $Longest = 0
  72. $Values = $Out | ForEach-Object {
  73. #Split the output in name and description
  74. $Name, $Description = $_.Split("`t",2)
  75. __{pkgname}_debug "Name: $Name Description: $Description"
  76. # Look for the longest completion so that we can format things nicely
  77. if ($Longest -lt $Name.Length) {
  78. $Longest = $Name.Length
  79. }
  80. # Set the description to a one space string if there is none set.
  81. # This is needed because the CompletionResult does not accept an empty string as argument
  82. if (-Not $Description) {
  83. $Description = " "
  84. }
  85. @{Name="$Name";Description="$Description"}
  86. }
  87. $Space = " "
  88. $Values = $Values | Where-Object {
  89. # filter the result
  90. if (-not $WordToComplete.StartsWith("-") -and $_.Name.StartsWith("-")) {
  91. # skip flag completions unless a dash is present
  92. return
  93. } else {
  94. $_.Name -like "$WordToComplete*"
  95. }
  96. # Join the flag back if we have an equal sign flag
  97. if ( $IsEqualFlag ) {
  98. __{pkgname}_debug "Join the equal sign flag back to the completion value"
  99. $_.Name = $Flag + "=" + $_.Name
  100. }
  101. }
  102. # Get the current mode
  103. $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function
  104. __{pkgname}_debug "Mode: $Mode"
  105. $Values | ForEach-Object {
  106. # store temporary because switch will overwrite $_
  107. $comp = $_
  108. # PowerShell supports three different completion modes
  109. # - TabCompleteNext (default windows style - on each key press the next option is displayed)
  110. # - Complete (works like bash)
  111. # - MenuComplete (works like zsh)
  112. # You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function <mode>
  113. # CompletionResult Arguments:
  114. # 1) CompletionText text to be used as the auto completion result
  115. # 2) ListItemText text to be displayed in the suggestion list
  116. # 3) ResultType type of completion result
  117. # 4) ToolTip text for the tooltip with details about the object
  118. switch ($Mode) {
  119. # bash like
  120. "Complete" {
  121. if ($Values.Length -eq 1) {
  122. __{pkgname}_debug "Only one completion left"
  123. # insert space after value
  124. [System.Management.Automation.CompletionResult]::new($($comp.Name | __{pkgname}_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
  125. } else {
  126. # Add the proper number of spaces to align the descriptions
  127. while($comp.Name.Length -lt $Longest) {
  128. $comp.Name = $comp.Name + " "
  129. }
  130. # Check for empty description and only add parentheses if needed
  131. if ($($comp.Description) -eq " " ) {
  132. $Description = ""
  133. } else {
  134. $Description = " ($($comp.Description))"
  135. }
  136. [System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)")
  137. }
  138. }
  139. # zsh like
  140. "MenuComplete" {
  141. # insert space after value
  142. # MenuComplete will automatically show the ToolTip of
  143. # the highlighted value at the bottom of the suggestions.
  144. [System.Management.Automation.CompletionResult]::new($($comp.Name | __{pkgname}_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
  145. }
  146. # TabCompleteNext and in case we get something unknown
  147. Default {
  148. # Like MenuComplete but we don't want to add a space here because
  149. # the user need to press space anyway to get the completion.
  150. # Description will not be shown because that's not possible with TabCompleteNext
  151. [System.Management.Automation.CompletionResult]::new($($comp.Name | __{pkgname}_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
  152. }
  153. }
  154. }
  155. }
  156. ###-end-{pkgname}-completion-###