-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathargument_completer.ps1
186 lines (169 loc) · 8.6 KB
/
argument_completer.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# 获取具有补全脚本的 Shell 列表。
function Get-FountShellListWithCompleter {
param([string]$Username)
# 获取指定用户或所有用户的可用 Shell 列表。
Get-FountPartList -parttype shells -Username $Username |
# 筛选出存在 argument_completer.ps1 脚本的 Shell。
Where-Object {
$shellDir = Get-FountPartDirectory -Username $Username -parttype shells -partname $_
Test-Path (Join-Path -Path $shellDir -ChildPath "argument_completer.ps1") -PathType Leaf
}
}
# 移除哈希表中的指定键及其子键(递归)。
function Remove-ArgumentNode {
param([hashtable]$Node, [string]$Key)
# 如果哈希表中存在指定的键,则移除该键。
if ($Node.ContainsKey($Key)) { $Node.Remove($Key) }
# 遍历哈希表中的所有键。
foreach ($mapperkey in $Node.Keys) {
# 如果当前键对应的值是哈希表,则递归调用 Remove-ArgumentNode 函数。
if ($Node[$mapperkey] -is [hashtable]) { Remove-ArgumentNode -Node $Node[$mapperkey] -Key $Key }
}
}
# 处理 'runshell' 子命令的参数补全。
function Invoke-RunshellCompletion {
param(
[string]$WordToComplete,
[System.Management.Automation.Language.CommandAst]$CommandAst,
[int]$CursorPosition,
[int]$runshellIndex, # 'runshell' 命令在 CommandAst 中的索引。
[int]$Argindex # 当前参数在 CommandAst 中的索引。
)
# 提取用户名和 shellname,处理它们可能还不存在的情况。
# $runshellIndex + 1 是用户名的可能位置。
# $runshellIndex + 2 是 shellname 的可能位置。
# 如果索引在 CommandAst 的范围内,并且元素是字符串常量,则提取其值。
$username = if ($runshellIndex + 1 -lt $CommandAst.CommandElements.Count -and $CommandAst.CommandElements[$runshellIndex + 1] -is [System.Management.Automation.Language.StringConstantExpressionAst]) {
$CommandAst.CommandElements[$runshellIndex + 1].Value
}
$shellname = if ($runshellIndex + 2 -lt $CommandAst.CommandElements.Count -and $CommandAst.CommandElements[$runshellIndex + 2] -is [System.Management.Automation.Language.StringConstantExpressionAst]) {
$CommandAst.CommandElements[$runshellIndex + 2].Value
}
# 补全用户名。
# 如果当前参数是 'runshell' 之后的第一个参数,则补全用户名。
if (($ArgIndex - $runshellIndex) -eq 1) {
return Get-FountUserList | Where-Object { $_.StartsWith($WordToComplete) }
}
# 补全 shellname。
# 如果当前参数是 'runshell' 之后的第二个参数,则补全 shellname。
if (($ArgIndex - $runshellIndex) -eq 2) {
return Get-FountShellListWithCompleter $username | Where-Object { $_.StartsWith($WordToComplete) }
}
# 委托给 shell 的 argument_completer.ps1 脚本处理后续参数。
# 获取 shell 目录的路径。
$shellDir = Get-FountPartDirectory -Username $username -parttype shells -partname $shellname
# 如果 shell 目录存在,并且包含 argument_completer.ps1 脚本(-PathType Leaf 检查是否为文件),则执行该脚本。
if (Test-Path (Join-Path -Path $shellDir -ChildPath "argument_completer.ps1") -PathType Leaf) {
# 使用 '&' 调用操作符执行脚本,并传递必要的参数。
& (Join-Path -Path $shellDir -ChildPath "argument_completer.ps1") $username $WordToComplete $CommandAst $CursorPosition $runshellIndex $Argindex
}
}
# 定义 Fount 工具的参数结构。
$ArgumentStructure = @{
Root = @{
# 根节点包含的参数。
Parameters = 'background', 'geneexe', 'init', 'keepalive', 'runshell', 'shutdown'
# 'background' 参数的子参数。
background = @{
Parameters = 'geneexe', 'init', 'keepalive', 'runshell', 'shutdown'
# 'geneexe' 参数的处理程序(仅限 Windows)。
geneexe = { if ($IsWindows) { Get-ChildItem -Path "$($WordToComplete)*" -File | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.FullName, $_.Name, 'File', $_.FullName) } } }
init = $null # 没有补全逻辑。
# 'keepalive' 参数的子参数和处理程序。
keepalive = @{ Parameters = 'debug', 'runshell'; debug = @{ Parameters = 'runshell'; runshell = ${function:Invoke-RunshellCompletion} }; runshell = ${function:Invoke-RunshellCompletion} }
# 'runshell' 参数的处理程序。
runshell = ${function:Invoke-RunshellCompletion}
shutdown = $null
}
geneexe = { if ($IsWindows) { Get-ChildItem -Path "$($WordToComplete)*" -File | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.FullName, $_.Name, 'File', $_.FullName) } } }
init = $null
keepalive = @{ Parameters = 'debug', 'runshell'; debug = @{ Parameters = 'runshell'; runshell = ${function:Invoke-RunshellCompletion} }; runshell = ${function:Invoke-RunshellCompletion} }
debug = @{ Parameters = 'runshell'; runshell = ${function:Invoke-RunshellCompletion} }
runshell = ${function:Invoke-RunshellCompletion}
shutdown = $null
}
}
# 如果不是 Windows 系统,则移除 'geneexe' 参数。
if (-not $IsWindows) {
Remove-ArgumentNode -Node $ArgumentStructure.Root -Key 'geneexe'
}
# Fount 工具的主参数补全函数。
function Get-FountArgumentCompletion {
param(
[string]$WordToComplete,
[System.Management.Automation.Language.CommandAst]$CommandAst,
[int]$CursorPosition
)
try {
# 改进的提取 $realWordToComplete 的逻辑。 这段代码尝试更准确地确定用户正在输入的单词。
$WordToComplete = "$CommandAst" # 首先将整个命令字符串赋值给 $WordToComplete。
# 循环添加空格,直到 $WordToComplete 的长度大于等于光标位置。这确保了后续的字符串分割可以正确工作,即使光标后面有空格。
while ($CursorPosition -gt $WordToComplete.Length) {
$WordToComplete += ' '
}
# 将 $WordToComplete 截取到光标位置,然后按空格分割,得到一个单词数组。
$CommandWords = $WordToComplete.Substring(0, [Math]::Min($WordToComplete.Length, $CursorPosition)) -split '\s+'
# $Argindex 是当前参数的索引。
$Argindex = $CommandWords.Count - 1
# $WordToComplete 现在是用户正在输入的单词。
$WordToComplete = $CommandWords[-1]
# 从参数结构的根节点开始。
$currentLevel = $ArgumentStructure.Root
# 跳过命令名称(例如 'fount')。
$ArgCommandElements = $CommandAst.CommandElements | Select-Object -Skip 1
$rootIndex = 0 # 用于跟踪runshell参数的索引
# 遍历命令参数。
foreach ($element in $ArgCommandElements) {
# 如果元素是字符串常量。
if ($element -is [System.Management.Automation.Language.StringConstantExpressionAst]) {
$argValue = $element.Value
# 如果当前级别是哈希表,并且包含该参数值。
if ($currentLevel -is [hashtable] -and $currentLevel.ContainsKey($argValue)) {
# 移动到下一级。
$currentLevel = $currentLevel[$argValue]
$rootIndex += 1
}
# 否则,如果当前级别是哈希表,并且包含 "Parameters" 键,并且 "Parameters" 包含该参数值, 则不做任何事情,因为我们已经处理了有效的参数。
elseif ($currentLevel -is [hashtable] -and $currentLevel.ContainsKey("Parameters") -and ($currentLevel.Parameters -contains $argValue)) {
}
# 否则,参数无效,退出循环。
else {
break
}
}
# 否则,参数不是字符串常量,退出循环。
else {
break
}
}
# 如果当前级别是哈希表。
if ($currentLevel -is [hashtable]) {
# 如果包含 "Parameters" 键。
if ($currentLevel.ContainsKey("Parameters")) {
# 获取匹配的参数。
$parameters = $currentLevel.Parameters
$matchingParameters = $parameters | Where-Object { $_.StartsWith($WordToComplete) }
# 返回匹配的参数。
$matchingParameters
return
}
return
}
# 否则,如果当前级别是脚本块。
elseif ($currentLevel -is [scriptblock]) {
# 执行脚本块,并传递必要的参数。
& $currentLevel $WordToComplete $CommandAst $CursorPosition $rootIndex $Argindex | ForEach-Object { $_ }
return
}
}
catch {
# 捕获并显示错误信息。
Write-Host "Error in Get-FountArgumentCompletion: $_" -ForegroundColor Red
}
}
# 注册参数补全函数。
# 为 'fount'、'fount.ps1'、'fount.cmd' 和 'fount.exe' 命令注册 Get-FountArgumentCompletion 函数。
Register-ArgumentCompleter -CommandName fount -ScriptBlock ${function:Get-FountArgumentCompletion} -Native
Register-ArgumentCompleter -CommandName fount.ps1 -ScriptBlock ${function:Get-FountArgumentCompletion} -Native
Register-ArgumentCompleter -CommandName fount.cmd -ScriptBlock ${function:Get-FountArgumentCompletion} -Native
Register-ArgumentCompleter -CommandName fount.exe -ScriptBlock ${function:Get-FountArgumentCompletion} -Native