---
title: Neovim：语言服务、补全与格式化
url: https://doc.liz6.com/tools/nvim/04-language-tools
locale: zh
area: tools
tags:
- Neovim
- 开发工具
date: 2026-09-15
modified: 2026-09-15
description: 理解 LSP、补全界面、Treesitter、格式化器和调试器的边界，按文件类型与项目根目录定位开发工具故障。
---

# Neovim：语言服务、补全与格式化

“代码有颜色”“能补全”“能跳到定义”和“能运行测试”来自不同组件。只有明确每一层负责什么，才能在某个功能失效时检查正确位置。

## 一次打开文件触发了什么

| 组件 | 输入与职责 | 不负责什么 |
| --- | --- | --- |
| 文件类型检测 | 根据文件识别 `rust`、`lua` 等类型 | 不保证对应工具已安装 |
| Treesitter parser | 解析语法结构，服务高亮、选区等功能 | 不等于项目类型检查 |
| LSP 客户端与服务器 | 同步文档，提供定义、引用、诊断、重命名 | 不自动执行全部测试 |
| 补全界面 | 聚合并展示 LSP、片段等候选 | 不产生所有语言语义 |
| 格式化器 | 按规则重排文本 | 不证明行为正确 |
| DAP 客户端与 adapter | 协调断点、变量和运行控制 | 不替代 SDK、可执行文件与调试符号 |

LazyVim 的语言 extra 连接这些组件中的一部分。标为 optional 的插件只有在安装后才会获得对应配置，外部运行时与项目依赖也要单独准备。[LazyVim extras](https://www.lazyvim.org/extras)

## 先从一个语言开始

以 Rust 为例，先在终端确认项目能够通过 `cargo check`，再在 `:LazyExtras` 中启用 Rust 支持。若命令行本身无法找到工具链、解析依赖或编译项目，先解决这些问题，避免把所有错误归到编辑器。

打开 `Cargo.toml` 所在项目中的 `.rs` 文件，检查：

```vim
:set filetype?
:pwd
:lua print(vim.inspect(vim.lsp.get_clients({ bufnr = 0 })))
```

预期文件类型为 `rust`，当前 buffer 有相应语言客户端。`:pwd` 只是编辑器工作目录；LSP 根目录还应检查客户端的配置，二者不一定相同。[Neovim LSP](https://neovim.io/doc/user/lsp/)

大型 workspace 中，服务器可能以外层工作区作为根。只打开一个从项目中复制出来的文件，则可能缺少依赖和模块上下文。

## 验证语言能力

将光标放在项目中的函数调用处，依次检查定义、引用、悬浮文档和重命名。LazyVim 提供对应键位，但调试时也可以直接调用 API：

```vim
:lua vim.lsp.buf.definition()
:lua vim.lsp.buf.references()
:lua vim.lsp.buf.hover()
```

没有跳转结果不一定是客户端未启动：目标可能是关键字、不可解析的依赖或未索引的文件。先用一个已知存在的本地函数确认基本路径。

重命名会改动多个位置。操作后查看受影响 buffer，保存需要保留的修改，再通过 `git diff` 审阅。不要把候选框关闭或重命名请求返回成功当作最终正确性检查。

## 谁来格式化

LazyVim 常用 Conform 管理格式化。执行 `:ConformInfo` 可以查看当前文件配置了哪些 formatter、是否可执行及日志位置。

学习阶段可在 `lua/config/options.lua` 关闭保存时格式化：

```lua
vim.g.autoformat = false
```

需要时通过 LazyVim 的格式化动作触发，再检查 diff。项目提交了 `.editorconfig`、`.clang-format`、`rustfmt.toml` 等规则时，应优先遵循项目约定。

对 Lua 增加 StyLua 的插件覆盖示例，保存为 `lua/plugins/formatting.lua`：

```lua
return {
  {
    "stevearc/conform.nvim",
    opts = function(_, opts)
      opts.formatters_by_ft = opts.formatters_by_ft or {}
      opts.formatters_by_ft.lua = { "stylua" }
    end,
  },
}
```

它配置“使用哪个程序”，不代替 StyLua 的安装。不要再添加另一套保存自动命令同时调用 LSP 格式化，否则一次保存可能重复格式化或发生规则冲突。

## 诊断与测试不是同一层验证

LSP 可以指出语法、类型或静态分析问题，但边界条件和业务行为需要测试。编辑器没有红线并不证明程序符合需求。[项目工作流](05-project-workflow.md)用一个能正常编译、却在边界值上失败的函数演示这个区别。

调试则还需要可启动的目标、正确工作目录、参数和 adapter。先让相同目标在命令行运行，再配置 DAP；Rust、Go、C# 和 Java 使用不同的调试后端，不能只安装 nvim-dap 就期待所有语言都可调试。

## 排错顺序

1. **文件类型**：`:set filetype?` 是否符合实际文件？
2. **项目上下文**：是否从正确项目打开，配置文件和依赖是否完整？
3. **可执行文件**：`:lua print(vim.fn.exepath("rust-analyzer"))` 是否有结果？某些插件使用自己指定的路径，应继续检查其配置。
4. **客户端附着**：当前 buffer 是否有服务器，日志是否报告启动失败？
5. **具体功能**：服务器是否支持该请求，问题是否仅发生在某个文件或依赖？
6. **格式化与调试**：分别查 Conform、DAP 的工具和日志，不重复安装 LSP 碰运气。

每次只改变一个条件。能用日志和最小项目解释结果，再将修复合回日常配置。
