外部命令补全器
补全器
Carapace 补全器
let carapace_completer = {|spans|
carapace $spans.0 nushell ...$spans | from json
}Fish 补全器
此补全器将使用 fish shell 来处理补全。Fish 为许多流行工具和命令提供了开箱即用的补全功能。
let fish_completer = {|spans|
fish --command $"complete '--do-complete=($spans | str replace --all "'" "\\'" | str join ' ')'"
| from tsv --flexible --noheaders --no-infer
| rename value description
| update value {|row|
let value = $row.value
let need_quote = ['\' ',' '[' ']' '(' ')' ' ' '\t' "'" '"' "`"] | any {$in in $value}
if ($need_quote and ($value | path exists)) {
let expanded_path = if ($value starts-with ~) {$value | path expand --no-symlink} else {$value}
$'"($expanded_path | str replace --all "\"" "\\\"")"'
} else {$value}
}
}关于此命令的几点说明:
- Fish 补全器将返回文本行,每行包含用制表符分隔的
value和description。description可能缺失,在这种情况下,value后面不会有制表符。如果发生这种情况,from tsv会失败,因此我们添加--flexible标志。 - Fish 补全器的输出不包含标题(列名),因此我们添加
--noheaders以防止from tsv将第一行视为标题,然后使用rename为列命名。 --no-infer是可选的。from tsv将推断结果的数据类型,因此像某些 git 哈希这样的数值将被推断为数字。--no-infer将保持所有内容为字符串。在实践中没有区别,但如果单独运行补全器,它将打印更一致的输出。- 由于 fish 仅支持 POSIX 样式的文件路径转义(
file\ name.txt等),由 fish 完成的文件路径在外部命令上不会被正确引用或转义。Nushell 不解析 POSIX 转义,因此我们需要手动进行此转换,例如通过测试项目是否为有效路径,如示例所示。为了最小化路径查找的开销,我们首先检查字符串中常见的转义字符。如果字符串需要转义,并且它是文件系统上的路径,则该值用双引号括起来。在双引号文件路径之前,我们扩展路径开头的任何 ~,以便补全继续工作。这种简单的方法并不完美,但它应该覆盖 99.9% 的使用情况。
多重补全器
有时,单个外部补全器不够灵活。幸运的是,可以根据需要将多个补全器组合成一个。以下示例对所有命令使用 $default_completer,除了在记录中明确定义的命令:
let multiple_completers = {|spans|
match $spans.0 {
ls => $ls_completer
git => $git_completer
_ => $default_completer
} | do $in $spans
}注意 在上面的示例中,
$spans.0是当时运行的命令。补全器将匹配所需的补全器,并回退到$default_completer。
- 如果我们尝试自动补全
git <tab>,spans将是[git ""]。match $spans.0 { ... }将返回$git_completer。- 如果我们尝试自动补全
other_command <tab>,spans将是[other_command ""]。匹配将回退到默认情况(_)并返回$default_completer。
故障排除
别名补全
Nushell 目前有一个 bug,自动补全不适用于别名。可以通过在补全器开头添加以下代码片段来解决此问题:
# 如果当前命令是别名,获取其扩展
let expanded_alias = (scope aliases | where name == $spans.0 | get -i 0 | get -i expansion)
# 覆盖
let spans = (if $expanded_alias != null {
# 将扩展别名的第一个单词放在 span 的开头
$spans | skip 1 | prepend ($expanded_alias | split row " " | take 1)
} else { $spans })此代码将获取第一个 span,找到匹配的第一个别名,并用别名扩展替换命令的开头。
使用 carapace 时出现 ERR unknown shorthand flag
当提供不支持的标志时,Carapace 将返回此错误。例如,使用 cargo -1:
| value | description |
|---|---|
| -1ERR | unknown shorthand flag: "1" in -1 |
| -1_ |
解决此问题涉及手动检查值以过滤掉它:
let carapace_completer = {|spans: list<string>|
carapace $spans.0 nushell ...$spans
| from json
| if ($in | default [] | where value == $"($spans | last)ERR" | is-empty) { $in } else { null }
}整合所有内容
这是一个外部补全器定义可能看起来的示例:
let fish_completer = ...
let carapace_completer = {|spans: list<string>|
carapace $spans.0 nushell ...$spans
| from json
| if ($in | default [] | where value =~ '^-.*ERR$' | is-empty) { $in } else { null }
}
# 此补全器默认使用 carapace
let external_completer = {|spans|
let expanded_alias = scope aliases
| where name == $spans.0
| get -o 0.expansion
let spans = if $expanded_alias != null {
$spans
| skip 1
| prepend ($expanded_alias | split row ' ' | take 1)
} else {
$spans
}
match $spans.0 {
# carapace 补全对于 nu 不正确
nu => $fish_completer
# fish 以更好的方式补全提交和分支名称
git => $fish_completer
# carapace 没有 asdf 的补全
asdf => $fish_completer
_ => $carapace_completer
} | do $in $spans
}
$env.config = {
# ...
completions: {
external: {
enable: true
completer: $external_completer
}
}
# ...
}