Skip to content

Commit 5eff9e9

Browse files
committed
New: Show-Command, Trace-Command, Many Examples
1 parent fe0af31 commit 5eff9e9

File tree

9 files changed

+727
-0
lines changed

9 files changed

+727
-0
lines changed

Completers/Build-ExternalExamples.ps1

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# todo: should be a task
2+
<#
3+
Idealy:
4+
Ninmonkey.Console\Get-NativeCommand 'gh'
5+
#>
6+
# Get-NativeCommand -
7+
# Get-NativeCommand 'gh'
8+
# gh completion --shell powershell
9+
10+
& {
11+
$ExportBase = Get-Item -ea stop (Join-Path "$PSScriptRoot" 'ExternalExamples')
12+
13+
Label 'gh' 'github cli' | Write-Information
14+
if (Ninmonkey.Console\Get-NativeCommand 'gh') {
15+
# -ea Ignore) {
16+
Ninmonkey.Console\Invoke-NativeCommand -CommandName 'gh' -ArgumentList @(
17+
'completion'
18+
'--shell'
19+
'powershell'
20+
) | Set-Content -Path (Join-Path $ExportBase 'gh.ps1')
21+
$Dest = (Join-Path $ExportBase 'rg.ps1')
22+
Label 'wrote' "'$Dest'" | Write-Information
23+
}
24+
25+
26+
Label 'rg' 'RipGrep' | Write-Information
27+
28+
if (Ninmonkey.Console\Get-NativeCommand 'rg') {
29+
# -ea Ignore) {
30+
$completerFromChoco = Get-ChildItem "${Env:ChocolateyInstall}\lib\ripgrep" -Recurse _rg.ps1 | Select-Object -First 1
31+
if ($completerFromChoco) {
32+
Copy-Item $completerFromChoco -Destination (Join-Path $ExportBase 'rg.ps1')
33+
$Dest = (Join-Path $ExportBase 'rg.ps1')
34+
Label 'wrote' "'$Dest'" | Write-Information
35+
}
36+
}
37+
Label 'Build Completers' 'Done.' | Write-Information
38+
} | ForEach-Object tostring

Completers/ExternalExamples/gh.ps1

+225
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# powershell completion for gh -*- shell-script -*-
2+
3+
function __gh_debug {
4+
if ($env:BASH_COMP_DEBUG_FILE) {
5+
"$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE"
6+
}
7+
}
8+
9+
filter __gh_escapeStringWithSpecialChars {
10+
$_ -replace '\s|#|@|\$|;|,|''|\{|\}|\(|\)|"|`|\||<|>|&','`$&'
11+
}
12+
13+
Register-ArgumentCompleter -CommandName 'gh' -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+
__gh_debug ""
25+
__gh_debug "========= starting completion logic =========="
26+
__gh_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+
__gh_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+
__gh_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+
__gh_debug "New WordToComplete: $WordToComplete"
57+
58+
59+
# Check for flag with equal sign
60+
$IsEqualFlag = ($WordToComplete -Like "--*=*" )
61+
if ( $IsEqualFlag ) {
62+
__gh_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+
__gh_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+
__gh_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+
__gh_debug "The completion directive is: $Directive"
88+
89+
# remove directive (last element) from out
90+
$Out = $Out | Where-Object { $_ -ne $Out[-1] }
91+
__gh_debug "The completions are: $Out"
92+
93+
if (($Directive -band $ShellCompDirectiveError) -ne 0 ) {
94+
# Error code. No completion.
95+
__gh_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+
__gh_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+
__gh_debug "ShellCompDirectiveNoSpace is called"
123+
$Space = ""
124+
}
125+
126+
if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
127+
__gh_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+
__gh_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+
__gh_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+
__gh_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+
__gh_debug "Only one completion left"
186+
187+
# insert space after value
188+
[System.Management.Automation.CompletionResult]::new($($comp.Name | __gh_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 | __gh_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 | __gh_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
221+
}
222+
}
223+
224+
}
225+
}

0 commit comments

Comments
 (0)