﻿---
title: NuShell命令补全指南
date: 2025-02-11
excerpt: Nushell配置Carapace实现命令智能补全，支持Git、Docker等常用工具，大幅提升终端操作效率
tags:
  - Terminal
  - Shell
  - Carapace
  - Workflow
  - Nushell
  - CLI
cover: https://assets.vluv.space/cover/ToolChain/carapace.webp
---

[Nushell](https://www.nushell.sh/) 是一款基于 Rust 构建的现代 Shell。相比 Fish（仅在 UNIX 平台可用），它天然跨平台（Windows / Linux / macOS），在不同系统间共享一套配置极其方便，社区活跃且迭代迅速。

但代价呢？Fish 有着更成熟的补全生态，例如它原生支持对 Claude Code 进行补全，Nushell 社区[对其的支持](https://github.com/nushell/nu_scripts/tree/main/custom-completions/claude)则稍晚一些。

## 我全都要.webp

好消息是，在 Nushell 中我们可以通过两种方案实现补全，且二者完美共存：

1. **Custom Completions**：手写补全脚本，可参考 [Nushell 社区脚本库](https://github.com/nushell/nu_scripts/tree/main/custom-completions)
2. **External Completers**：桥接外部补全工具（如 Fish、Carapace、Zoxide）

本文主要介绍第二种方式——让你在 Nushell 里也能复用 Fish 的补全能力。

## 整体架构

在 `completions.nu` 中，我们将补全系统拆分为三个模块：

1. **桥接（Bridge）**：连接外部补全系统（Fish / Carapace）
2. **别名解析（Normalization）**：将 alias 还原为真实命令
3. **分发调度（Dispatcher）**：根据命令类型选择最合适的补全器

### Step 1. 桥接 Fish 与 Carapace

许多成熟的命令行工具已经为其他 Shell 提供了高质量补全：

- Fish 的 `complete --do-complete`
- Carapace 的跨 Shell 补全接口

```sh
$ fish --command "complete --do-complete 'git switch or'"
origin/HEAD    Remote Branch
origin/dev     Remote Branch
origin/main    Remote Branch

$ carapace git nushell git switch ''
[{"value":"switch ","display":"switch","description":"Switch branches","style":{"fg":"blue"}}]
```

借助 Nushell 提供的 **External Completer API**，我们可以将这些能力整合进 Nushell：

```nu
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}
    }
}

let carapace_completer = {|spans: list<string>|
    CARAPACE_LENIENT=1 carapace $spans.0 nushell ...$spans | from json
}
```

### Step 2. 处理别名

假设你定义了：

```nu
alias g = git
```

大多数补全器无法识别 `g`，因此不会触发 Git 补全。我们可以通过 `scope aliases` 查询当前命令是否为别名，如果是，则还原为真实命令后再交给补全器处理。

例如 `g switch` 会被转换为 `git switch`，再执行补全。具体实现见下一步的 dispatcher。

### Step 3. 分发调度

不同补全器的质量因工具而异：

- 某些工具在 Fish 中补全最完整（如 `git`、`bun`）
- 其他工具则更适合 Carapace

因此采用 **分发策略**：指定命令走 Fish，其余走 Carapace。

下面的 dispatcher 同时完成了 Step 2 的别名解析和 Step 3 的分发逻辑：

```nu
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 {
        nu | tv | bun | git | rclone => $fish_completer
        _ => $carapace_completer
    } | do $in $spans
}
```

---

### Step 4. 启用 External Completer

最后在 Nushell 配置中启用外部补全即可：

```nu
$env.config.completions = {
  case_sensitive: false
  quick: true
  partial: true
  algorithm: "prefix"
  external: {
    enable: true
    completer: $external_completer
  }
  use_ls_colors: true
}
```

还可以进一步配置 `menu` 和 `keybindings` 以获得更丝滑的体验，参考我的配置仓库 [Efterklang/dotfiles](https://github.com/Efterklang/dotfiles)。

## 效果演示

配置完成后，输入 `ssh` 按 `Tab` 即可自动列出 `~/.ssh/config` 中的远程主机，`git` 补全也一应俱全：

![nu_completion](https://assets.vluv.space/nu_completion.gif)
