# rusty-nvim **Repository Path**: johnsong2016_admin/rusty-nvim ## Basic Information - **Project Name**: rusty-nvim - **Description**: Rusty Neovim - A modern Neovim configuration with Rust LSP support - **Primary Language**: Unknown - **License**: Unlicense - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-09 - **Last Updated**: 2026-08-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
# 🦀 rusty-nvim **开箱即用的 Rust Neovim 配置。** 基于 NvChad · rustaceanvim · blink.cmp · LuaSnip · nvim-dap
[![Neovim](https://img.shields.io/badge/Neovim-0.10+-57A143?logo=neovim&logoColor=white)](https://neovim.io) [![Rust](https://img.shields.io/badge/Rust-stable-CE422B?logo=rust&logoColor=white)](https://www.rust-lang.org) [![License](https://img.shields.io/badge/License-Unlicense-blue)](./LICENSE) [![Snippets](https://img.shields.io/badge/Snippets-269%20Rust-orange)](./nvim/luasnippets/rust.lua) [![Gitee stars](https://gitee.com/johnsong2016_admin/rusty-nvim/badge/star.svg?theme=dark)](https://gitee.com/johnsong2016_admin/rusty-nvim/stargazers) [![Platform](https://img.shields.io/badge/Platform-macOS%20%7C%20Linux%20%7C%20WSL-lightgrey)](#-前置依赖)
--- ## 📖 目录 - [✨ 特性总览](#-特性总览) - [📸 预览](#-预览) - [📋 前置依赖](#-前置依赖) - [🚀 安装](#-安装) - [🗑 卸载](#-卸载) - [⚡ 快速上手](#-快速上手) - [🎬 实操示例](#-实操示例) - [⌨ 完整快捷键](#-完整快捷键) - [📝 Snippet 列表](#-snippet-列表) - [📁 目录结构](#-目录结构) - [🔧 自定义](#-自定义) - [❓ FAQ](#-faq) - [🙏 致谢](#-致谢) --- ## ✨ 特性总览 | | 特性 | 说明 | |:---:|------|------| | 🎯 | **零配置** | 克隆即用,Mason 自动安装 `rust-analyzer` / `codelldb` / 格式化器 | | 🧠 | **智能补全** | `blink.cmp` + `LuaSnip`,Tab 导航列表 + 跳转 snippet 占位符,LSP/snippet 智能去重 | | 🦀 | **全功能 LSP** | `rustaceanvim`:内存布局 hover、clippy 诊断、代码透镜、宏展开、joinLines | | 🔨 | **保存自动重跑** | `rr` 启动后,每次保存 `.rs` 自动 `cargo run` | | 🐛 | **断点调试** | `codelldb` 集成,跨平台(macOS / Linux / Windows / WSL) | | 🧪 | **测试集成** | `neotest` + rustaceanvim adapter,单键运行 / 调试光标处测试 | | 📦 | **依赖管理** | `crates.nvim` 在 `Cargo.toml` 中升级 / 降级 / 查看 features | | 📝 | **269 Rust Snippet** | 标准库宏、函数定义、控制流、迭代器链、设计模式、unsafe/FFI、tokio 异步、serde、Trait 实现、错误类型、闭包、泛型、内存操作 | | 🔍 | **全局搜索** | Telescope + ripgrep + LSP 符号 / 引用 / 调用链 | | 📊 | **诊断面板** | `trouble.nvim` 工作区诊断、引用、调用链可视化 | | 🌳 | **Treesitter** | 代码对象选取 / 跳转 / 交换 (Rust, TOML, HTML, CSS, JS/TS, TSX) | | 🌐 | **前端支持** | HTML、CSS、JavaScript、TypeScript、Tailwind CSS — LSP + Prettier 格式化 | | 💅 | **精美 UI** | NvChad 主题、光标色条、inlay hints、语义高亮 | --- ## 📸 预览

编辑 Rust 源文件
neovim rust 截图

保存后自动重跑 cargo run
cargo run 演示

Snippet 展开与 Tab 跳转
snippet 演示

--- ## 📋 前置依赖 ### 必需(必须手动安装) | 依赖 | 最低版本 | macOS | Ubuntu / Debian | Fedora | Arch | |:-----|:--------:|:------|:----------------|:-------|:-----| | [Neovim](https://github.com/neovim/neovim) | **0.10+** | `brew install neovim` | [安装指南](https://github.com/neovim/neovim/wiki/Installing-Neovim) | `dnf install neovim` | `pacman -S neovim` | | [Rust 工具链](https://rustup.rs) | stable | `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \| sh` | 同左 | 同左 | 同左 | | [ripgrep](https://github.com/BurntSushi/ripgrep) | 任意 | `brew install ripgrep` | `apt install ripgrep` | `dnf install ripgrep` | `pacman -S ripgrep` | | [git](https://git-scm.com) | 2.20+ | `brew install git` | `apt install git` | `dnf install git` | `pacman -S git` | | **Nerd Font** | 任意 | `brew install --cask font-jetbrains-mono-nerd-font` | [下载](https://www.nerdfonts.com/font-downloads) | 同左 | 同左 | > ⚠ **必须安装 Nerd Font 并设为终端字体**,否则图标显示为乱码方块。
📦 一键安装所有依赖(点击展开) **macOS:** ```bash brew install neovim ripgrep git fd node brew install --cask font-jetbrains-mono-nerd-font curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` **Ubuntu / Debian:** ```bash sudo apt update sudo apt install -y ripgrep git fd-find nodejs # Neovim 0.10+ 可能需要 PPA 或 AppImage: curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.appimage chmod u+x nvim-linux-x86_64.appimage sudo mv nvim-linux-x86_64.appimage /usr/local/bin/nvim curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` **Fedora:** ```bash sudo dnf install -y neovim ripgrep git fd-find nodejs curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` **Arch:** ```bash sudo pacman -S --needed neovim ripgrep git fd nodejs rustup rustup default stable ```
### 推荐 | 依赖 | 用途 | 安装 | |:-----|:-----|:-----| | [fd](https://github.com/sharkdp/fd) | Telescope 文件搜索加速 | `brew install fd` / `apt install fd-find` | | [Node.js](https://nodejs.org) 18+ | 部分 Mason 工具依赖 | `brew install node` / `apt install nodejs` | ### 自动安装(无需手动) 首次启动 3 秒后由 `mason-tool-installer` 自动安装: | 工具 | 用途 | |:-----|:-----| | `rust-analyzer` | Rust 语言服务器 (LSP) | | `codelldb` | Rust 调试器 (DAP) | | `stylua` | Lua 格式化 | | `taplo` | TOML 格式化 | | `biome` | JSON 格式化 | | `prettier` | HTML / CSS / JS / TS 格式化 | | `typescript-language-server` | JavaScript & TypeScript LSP | | `tailwindcss-language-server` | Tailwind CSS LSP | --- ## 🚀 安装 ### 方式一:一行命令(推荐) ```bash curl -fsSL https://gitee.com/johnsong2016_admin/rusty-nvim/raw/main/install.sh | bash ``` ### 方式二:克隆 + 运行脚本 ```bash git clone https://gitee.com/johnsong2016_admin/rusty-nvim.git cd rusty-nvim && bash install.sh ``` ### 方式三:手动安装 ```bash # 备份已有配置 mv ~/.config/nvim ~/.config/nvim.bak 2>/dev/null || true # 克隆并仅复制 nvim/ 配置文件夹 git clone https://gitee.com/johnsong2016_admin/rusty-nvim.git /tmp/rusty-nvim cp -r /tmp/rusty-nvim/nvim/. ~/.config/nvim/ rm -rf /tmp/rusty-nvim # 启动 — 首次运行自动安装所有插件 nvim ```
🖥 Windows / WSL 安装说明 **WSL(推荐 Windows 用户使用):** ```bash wsl --install # WSL 内: sudo apt update && sudo apt install -y ripgrep git fd-find curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # 通过 AppImage 安装 Neovim 0.10+(见前置依赖) curl -fsSL https://gitee.com/johnsong2016_admin/rusty-nvim/raw/main/install.sh | bash nvim ``` **原生 Windows**(实验性 — 路径为 `%LOCALAPPDATA%\nvim`): ```powershell git clone https://gitee.com/johnsong2016_admin/rusty-nvim.git $env:TEMP\rusty-nvim New-Item -ItemType Directory -Force $env:LOCALAPPDATA\nvim copy-item -Path $env:TEMP\rusty-nvim\nvim\* -Destination $env:LOCALAPPDATA\nvim -Recurse nvim ``` > ⚠ 原生 Windows 未经充分测试,推荐使用 WSL。
### 首次启动流程 ``` 启动 nvim ├─ nvdash 启动画面显示 RUSTY ASCII art ├─ lazy.nvim 自动 clone 所有插件 (~30秒) ├─ Treesitter 自动安装解析器: │ ├─ rust, toml, lua │ └─ html, css, javascript, typescript, tsx └─ 3秒后 mason-tool-installer 自动安装: ├─ rust-analyzer, codelldb ├─ stylua, taplo, biome ├─ prettier (HTML/CSS/JS/TS 格式化) ├─ typescript-language-server ├─ tailwindcss-language-server └─ ✅ 完成!打开 .rs 或 .tsx 文件即可使用 ``` > 💡 **安装后插件有问题?** 如果插件安装失败或行为异常,在 Neovim 中执行以下命令: > - `:Lazy sync` — 重新安装缺失插件并清理过期插件 > - `:Lazy update` — 更新所有插件到最新版本 > - `:Lazy` — 打开插件管理界面查看状态 > - `:Mason` — 检查 LSP / DAP / 格式化器安装状态 --- ## 🗑 卸载 ### 方式一:一行命令(推荐) ```bash curl -fsSL https://gitee.com/johnsong2016_admin/rusty-nvim/raw/main/uninstall.sh | bash ``` ### 方式二:克隆 + 运行脚本 ```bash cd ~/.config/nvim && bash uninstall.sh ``` 跳过确认提示: ```bash bash uninstall.sh --force ``` ### 方式三:手动删除 ```bash rm -rf ~/.config/nvim{,.bak} rm -rf ~/.local/share/nvim{,.bak} rm -rf ~/.local/state/nvim{,.bak} rm -rf ~/.cache/nvim{,.bak} ``` > 以上命令会彻底删除配置、插件、缓存、运行状态及所有备份。不会卸载 Neovim 本身。 --- ## ⚡ 快速上手 安装完成后,60 秒验证一切正常: ```bash # 1. 创建 Rust 项目 cargo new hello_rusty && cd hello_rusty # 2. 用 Neovim 打开 nvim src/main.rs # 3. 等待 ~10s rust-analyzer 索引完成(看状态栏) # 4. 试试这些快捷键: # K → hover(类型信息 + 内存布局) # rr → cargo run(底部终端打开) # ca → Code Action 菜单 # ff → 搜索文件 # fw → 全局搜索内容 # e → 打开文件树 ``` --- ## 🎬 实操示例 > `` = 空格键 ### 示例 1:从零创建 Rust 项目并运行 ```bash cargo new hello_world cd hello_world nvim src/main.rs ``` ```rust // 在 nvim 中编辑 src/main.rs // 1. 输入 "println" → 补全菜单出现 → Enter 确认 // 自动展开为: println!(""); // ^ 光标在这里,输入格式字符串 // 2. 输入 "hello world" println!("hello world"); // 3. 按 jk 回到 Normal 模式 // 4. 按 rr 运行 → 底部终端弹出 cargo run // 输出: hello world // 5. 修改代码后按 保存 → 终端自动 Ctrl+C 并重新 cargo run ``` ![cargo-run-demo](./gifs/cargo-run-demo.gif) --- ### 示例 2:用 Snippet 快速写测试 ```rust // 1. 输入 "testmod" → Enter → 展开为完整测试模块: #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { todo!() } } // 2. Tab → 光标跳到 "it_works" → 改名为 "test_add" // 3. Tab → 光标跳到 "todo!()" → 改写测试逻辑 // 4. 输入 "assert_eq" → Enter → 展开为: // assert_eq!(left, right); // 5. Tab → 光标在 "left" → 输入 1 + 1 // 6. Tab → 光标在 "right" → 输入 2 // 最终结果: #[cfg(test)] mod tests { use super::*; #[test] fn test_add() { assert_eq!(1 + 1, 2); } } // 7. tt → 运行光标处测试 → neotest 显示 ✓ ``` ![snippet-demo](./gifs/snippet-demo.gif) --- ### 示例 3:调试断点 ```rust fn factorial(n: u32) -> u32 { if n <= 1 { 1 } else { n * factorial(n - 1) } } fn main() { let result = factorial(5); println!("5! = {}", result); } ``` ``` 操作步骤: 1. 光标移到 factorial 函数体内的 "n * factorial(n - 1)" 行 2. db → 当前行出现红点(断点) 3. dc → 启动调试 → 程序停在断点 4. DAP UI 自动打开: 左侧变量面板, 下方调用栈 5. dj → 单步跳过 → 变量面板更新 6. dl → 单步进入 → 进入 factorial 函数 7. dk → 单步跳出 8. de → 终止调试 ``` --- ### 示例 4:诊断错误并修复 ```rust let numbers = vec![1, 2, 3]; let sum: i32 = numbers.sum(); // ← rust-analyzer 标黄警告 ``` ``` 操作步骤: 1. ]t → 跳到下一个诊断 → 光标停在被诊断的行 2. K → 弹出 hover → 显示完整错误信息和修复建议 3. ca → Code Action 菜单 → 选择修复建议 高级操作: 4. cd → 拷贝当前行诊断消息到剪贴板 5. xx → 打开诊断面板 → 查看全项目所有问题 6. ]t / [t → 在诊断面板中逐个跳转 ``` --- ### 示例 5:Git 工作流 ``` 操作步骤: 1. gt → 打开 Git status (Telescope) → 查看改动的文件 2. 选择文件 → Enter → 跳到文件 3. ]h → 跳到下一个修改块 (hunk) 4. hp → 预览修改块 → 弹出 diff 窗口 5. hs → 暂存该修改块 6. hr → 撤销该修改块 7. hb → 查看当前行的 git blame 8. cm → 打开 Git commit 历史 (Telescope) ``` --- ### 示例 6:依赖管理 ```toml # Cargo.toml [dependencies] serde = "1.0" # ← 光标停在这行 tokio = "1.0" ``` ``` 操作步骤: 1. 打开 Cargo.toml → crates.nvim 自动显示最新版本 2. 光标移到 serde 行 3. Cu → 升级 serde 到最新版 4. CU → 升级所有依赖 5. Cf → 查看 serde 的 features 列表 6. Co → 在浏览器打开 docs.rs/serde 7. Cr → 打开 serde 的 Gitee/GitHub 仓库 ``` --- ### 更多实操场景
🔍 用 Hover 查看内存布局 ```rust struct User { name: String, age: u32, emails: Vec, } ``` ``` 1. 光标停在 "User" 上 2. K → 弹出 hover 窗口 显示: struct 定义 + 字段类型 底部: 内存布局 (size/offset/alignment/padding/niches) 3. → 向下滚动文档 4. → 向上滚动 5. jk → 关闭 hover ```
🔧 展开宏 ```rust // println! 是宏,想看展开后的代码: 1. 光标停在 println! 上 2. re → 弹出宏展开窗口 ```
📞 查看调用链 ``` 1. 光标停在函数名上 2. fI → 谁调用了这个函数 (incoming calls) 3. fO → 这个函数调用了谁 (outgoing calls) 4. Telescope 弹出列表 → 选择条目 → 跳转 ```
🌳 用 Treesitter 选取函数 ``` 1. 在 Normal 模式下 2. vaf → 可视选取整个函数(含函数签名和函数体) 3. vif → 可视选取函数内部(不含函数签名) 4. ]f → 跳到下一个函数 5. [f → 跳到上一个函数 6. na → 交换当前参数和下一个参数 ```
--- ## ⌨ 完整快捷键 > `` = 空格键 | `` = Ctrl+x | `` = Shift+Tab ### 🔥 核心高频键 | 键 | 功能 | |:---:|------| | `jk` | 退出插入模式 | | `` | 保存(静默,无 hit-enter 提示) | | `;` | 进入命令模式 | | `gd` | 跳转定义 | | `K` | Hover — 类型 / 文档 / 内存布局 | | `ca` | Code Action | | `` | 重命名(实时预览引用) | | `rr` | cargo run(保存自动重跑) | | `rc` | cargo check | | `rf` | fly check(clippy 即时检查) | | `rt` | cargo test | | `tt` | 运行光标处测试 | | `db` | 切换断点 | | `dc` | 开始 / 继续调试 | | `Tab` | 补全列表下一个 / Snippet 占位符跳转 | | `ff` | 搜索文件名 | | `fw` | 全局搜索内容 | | `e` | 打开 / 关闭文件树 | | `/` | 注释切换 | | `fm` | 格式化 | | `xx` | 诊断面板 | | `cd` | 拷贝诊断到剪贴板 | | `ra` | 重命名(实时预览引用) | | `]t` / `[t` | 下一个 / 上一个诊断 | ### 补全与 Snippet | 键 | 功能 | |:---:|------| | `Tab` | 补全列表下一个 / Snippet 占位符跳转 | | `S-Tab` | 补全列表上一个 / Snippet 占位符回退 | | `` | 确认补全(菜单可见时确认,否则换行) | | `` | 强制跳 Snippet 占位符(无视菜单可见性) | | `` | 手动触发补全 + 文档 | | `` | 关闭补全菜单 | | `` | 显示签名帮助 | | `` / `` | 文档上 / 下滚动 | | `` / `` | 签名上 / 下滚动 | ### 代码导航 | 键 | 功能 | |:---:|------| | `gd` | 跳转定义 | | `gD` | 跳转声明 | | `D` | 跳转类型定义 | | `gr` | 查找引用 | | `gi` | 查找实现 (trait 实现) | | `fs` | 当前文件符号大纲 | | `fS` | 全项目符号搜索 | | `fr` | Telescope 查找引用 | | `fI` | 谁调用了当前函数 (incoming calls) | | `fO` | 当前函数调用了谁 (outgoing calls) | | `rp` | 跳到父模块 | ### Rust 开发 #### 运行与构建 | 键 | 功能 | |:---:|------| | `rr` | cargo run(复用终端,保存自动重跑) | | `rq` | 终止 cargo 终端 | | `rc` | cargo check | | `rf` | fly check(clippy) | | `rt` | cargo test | | `rl` | 列出所有可运行 target | #### 代码智能 | 键 | 功能 | |:---:|------| | `rh` | Hover actions(运行 / 调试 / 跳转) | | `re` | 展开宏 | | `rd` | 打开 docs.rs 文档 | | `rx` | 解释当前错误 | | `rj` | 智能合并多行 | | `rp` | 跳到父模块 | | `rC` | 打开 Cargo.toml | | `rs` | 查看语法树 | | `rw` | 重新加载 workspace | | `rT` | 查找相关测试 | | `rR` | 跳转相关诊断 | | `rD` | 渲染诊断 | | `rg` | 列出可调试 target(DAP) | | `rmu` | 上移字段 / 方法 | | `rmd` | 下移字段 / 方法 | | `ri` | 切换 inlay hints | | `ra` | 重命名(实时预览引用) | ### 测试与调试 #### 测试 (neotest) | 键 | 功能 | |:---:|------| | `tt` | 运行光标处测试 | | `tf` | 运行当前文件所有测试 | | `td` | 调试光标处测试 | | `ts` | 测试树面板 | | `to` | 查看测试输出 | | `]T` / `[T` | 下一个 / 上一个失败测试 | #### 调试 (DAP) | 键 | 功能 | |:---:|------| | `db` | 切换断点 | | `dd` | 条件断点 | | `dc` | 开始 / 继续调试 | | `dl` | 单步进入 | | `dj` | 单步跳过 | | `dk` | 单步跳出 | | `de` | 终止调试 | | `dr` | 重跑上次 | | `dt` | 列出可调试 testables | ### 诊断与重构 | 键 | 功能 | |:---:|------| | `xx` | 全项目诊断面板 | | `xX` | 当前文件诊断面板 | | `cd` | 拷贝当前行诊断到剪贴板 | | `]t` / `[t` | 下一个 / 上一个诊断 | | `ca` | Code Action | | `` | 重命名(实时预览) | | `fm` | 格式化文件 | | `xr` | 引用面板 | | `xi` / `xo` | 调用链(入 / 出) | | `xs` | 符号面板 | | `xq` | Quickfix 面板 | ### Git | 键 | 功能 | |:---:|------| | `]h` / `[h` | 下一个 / 上一个修改块 | | `hs` | 暂存修改块 | | `hr` | 撤销修改块 | | `hp` | 预览修改块 | | `hb` | 行 blame | | `hd` | Diff 当前文件 | | `ht` | 切换行 blame | | `gt` | Git status | | `cm` | Git commit 历史 | | `gb` | 当前文件 commit 历史 | | `gB` | 分支切换 | ### 文件与搜索 | 键 | 功能 | |:---:|------| | `e` | 打开 / 关闭文件树 | | `ff` | 搜索文件名 | | `fa` | 搜索所有文件(含隐藏) | | `fw` | 全局搜索内容 | | `fb` | 搜索已打开 buffer | | `fo` | 最近打开的文件 | | `fz` | 当前 buffer 模糊搜索 | ### 窗口与 Buffer | 键 | 功能 | |:---:|------| | `` / `` | 左 / 右窗口 | | `` / `` | 下 / 上窗口 | | `s` | 水平分屏 | | `v` | 垂直分屏 | | `c` | 关闭窗口 | | `o` | 关闭其他窗口 | | `Tab` / `S-Tab` | 下一个 / 上一个 buffer (Normal 模式) | | `x` | 关闭 buffer | | `b` | 新建 buffer | ### 终端 | 键 | 功能 | |:---:|------| | `` | 切换浮动终端 | | `` | 切换垂直终端 | | `` | 切换水平终端 | | `h` | 新建水平终端 | | `v` | 新建垂直终端 | | `` | 终端模式退出到 Normal | ### Crates 依赖管理(Cargo.toml 中) | 键 | 功能 | |:---:|------| | `Cu` | 升级当前依赖 | | `CU` | 升级所有依赖 | | `Cd` | 降级当前依赖 | | `Cf` | 查看 crate features | | `Co` | 打开 crate 文档 | | `Cr` | 打开 crate 仓库 | | `Ca` | 刷新 crate 信息 | ### Treesitter 代码对象
📋 完整 Treesitter 快捷键(点击展开) #### 选取(可视模式 / operator-pending) | 键 | 对象 | |:---:|------| | `af` / `if` | 函数(含 / 不含签名) | | `ac` / `ic` | 类 | | `aa` / `ia` | 参数 | | `al` / `il` | 循环 | | `ab` / `ib` | 代码块 | | `aC` / `iC` | 注释 | | `am` / `im` | 函数调用 | | `as` | 语句 | #### 跳转 | 键 | 目标 | |:---:|------| | `]f` / `[f` | 下一个 / 上一个函数 | | `]k` / `[k` | 下一个 / 上一个类 | | `]a` / `[a` | 下一个 / 上一个参数 | | `]l` / `[l` | 下一个 / 上一个循环 | | `]s` / `[s` | 下一个 / 上一个语句 | | `]m` / `[m` | 下一个 / 上一个调用 | #### 交换 | 键 | 功能 | |:---:|------| | `na` | 交换参数(向后) | | `nf` | 交换函数(向后) | | `nk` | 交换类(向后) | | `Na` | 交换参数(向前) | | `Nf` | 交换函数(向前) | | `Nk` | 交换类(向前) | #### Surround (mini.surround) | 键 | 功能 | |:---:|------| | `sa{char}` | 添加 surround(如 `sa"` 加引号) | | `sd{char}` | 删除 surround | | `sr{old}{new}` | 替换 surround | | `sf{char}` | 查找右侧 surround | | `sF{char}` | 查找左侧 surround | | `sh{char}` | 高亮 surround |
> 完整快捷键速查表另见 [KEYMAPS_zh.md](KEYMAPS_zh.md)。 --- ## 📝 Snippet 列表 > 输入触发词 → Enter 确认补全 → Tab 跳转占位符 → S-Tab 回退 > > 仅加载 Rust snippet,其他语言的 snippet(friendly-snippets、VSCode、snipmate)已禁用。 ### 标准库宏 | 触发词 | 展开结果 | |--------|---------| | `println` | `println!("");` (光标在引号内) | | `printlnf` | `println!("", args);` (两个占位符) | | `eprintln` | `eprintln!("");` | | `dbg` | `dbg!();` | | `assert_eq` | `assert_eq!(left, right);` (Tab 跳 left→right) | | `assert_ne` | `assert_ne!(left, right);` | | `vec` | `vec![];` | | `format` | `format!("")` | | `todo` | `todo!()` | | `panic` | `panic!("");` | | `matches` | `matches!(expr, pattern)` | | `cfg` | `cfg!()` | | `env` | `env!("")` | | `include_str` | `include_str!("")` | ### 属性 | 触发词 | 展开结果 | |--------|---------| | `derive` | `#[derive()]` | | `derive_debug` | `#[derive(Debug)]` | | `derive_clone` | `#[derive(Clone)]` | | `derive_copy` | `#[derive(Copy, Clone)]` | | `derive_default` | `#[derive(Default)]` | | `derive_eq` | `#[derive(PartialEq, Eq)]` | | `derive_serde` | `#[derive(serde::Serialize, serde::Deserialize)]` | | `derive_all` | `#[derive(Debug, Clone, PartialEq, Eq, Hash)]` | | `inline` | `#[inline]` | | `must_use` | `#[must_use]` | | `no_std` | `#![no_std]` | ### 函数定义 | 触发词 | 展开结果 | |--------|---------| | `fn` | `fn name(args) -> Ret { todo!() }` (4 个占位符) | | `pfn` | `pub fn name(args) -> Ret { todo!() }` | | `afn` | `async fn name(args) -> Ret { todo!() }` | | `pafn` | `pub async fn name(args) -> Ret { todo!() }` | | `main` | `fn main() { }` | | `extern_fn` | `extern "C" fn name(...) -> RetType { }` | | `unsafe_fn` | `unsafe fn name(...) -> Ret { }` | | `resultfn` | `fn name() -> Result { }` | | `optionfn` | `fn name() -> Option { }` | ### 类型定义 | 触发词 | 展开结果 | |--------|---------| | `struct` | `#[derive(Debug)] struct Name { field: Type }` | | `struct_tuple` | `struct Name(Type);` | | `struct_unit` | `struct Name;` | | `enum` | `#[derive(Debug)] enum Name { Variant1, Variant2 }` | | `impl` | `impl Type { }` | | `trait` | `trait Name { }` | | `traitimpl` | `impl Trait for Type { }` | | `mod` | `mod name { }` | | `const` | `const NAME: Type = init;` | | `typealias` | `type Alias = Type;` | | `error_enum` | `#[derive(Debug, thiserror::Error)] enum Error { }` | ### 控制流 | 触发词 | 展开结果 | |--------|---------| | `if` | `if condition { todo!() }` | | `iflet` | `if let Some(x) = expr { todo!() }` | | `while` | `while condition { todo!() }` | | `for` | `for pat in expr { todo!() }` | | `loop` | `loop { }` | | `match` | `match expr { Pattern => todo!(), _ => todo!() }` | | `match_opt` | `match expr { Some(x) => ..., None => ... }` | | `match_res` | `match expr { Ok(val) => ..., Err(e) => ... }` | | `unsafe_block` | `unsafe { }` | ### 测试 | 触发词 | 展开结果 | |--------|---------| | `test` | `#[test] fn name() { todo!() }` | | `testmod` | `#[cfg(test)] mod tests { use super::*; #[test] fn it_works() { } }` | | `tokiotest` | `#[tokio::test] async fn name() { }` | ### 错误处理 | 触发词 | 展开结果 | |--------|---------| | `some` / `none` | `Some()` / `None` | | `ok` / `err` | `Ok()` / `Err()` | | `context` | `.context("")` | | `with_context` | `.with_context(\|\| "")?` | | `map_err` | `.map_err(\|e\| todo!())` | | `unwrap_or` | `.unwrap_or(default)` | | `question` | `?` | | `anyhow_result` | `anyhow::Result` | | `anyhow_bail` | `anyhow::bail!("msg");` | | `anyhow_ensure` | `anyhow::ensure!(cond, "msg");` | ### 集合 | 触发词 | 展开结果 | |--------|---------| | `hashmap` | `let mut map: HashMap = HashMap::new();` | | `btreemap` | `let mut map: BTreeMap = BTreeMap::new();` | | `hashset` | `let mut set: HashSet = HashSet::new();` | | `vecnew` | `let v: Vec = Vec::new();` | | `entry` | `.entry(key).or_insert(default)` | ### 迭代器链式调用 | 触发词 | 展开结果 | |--------|---------| | `itermap` | `.iter().map(\|x\| todo!())` | | `iterfilter` | `.iter().filter(\|x\| todo!())` | | `iterfold` | `.iter().fold(init, \|acc, x\| todo!())` | | `itercollect` | `.iter().collect::>()` | | `iterenum` | `.iter().enumerate()` | | `iterzip` | `.iter().zip(other)` | | `iterany` | `.iter().any(\|x\| todo!())` | | `iterall` | `.iter().all(\|x\| todo!())` | | `itersum` | `.iter().sum::()` | | `itermax` | `.iter().max()` | ### 并发 | 触发词 | 展开结果 | |--------|---------| | `arc` | `Arc::new(value)` | | `arcmutex` | `Arc::new(Mutex::new(value))` | | `channel` | `let (tx, rx) = mpsc::channel();` | | `send` | `tx.send(value).unwrap();` | | `atomic` | `AtomicUsize::new(0)` | | `thread_spawn` | `std::thread::spawn(move \|\| { });` | ### 设计模式 | 触发词 | 展开结果 | |--------|---------| | `builder` | 完整 Builder 模式(struct + impl + new + setter + build) | | `newtype` | Newtype 模式(struct + new + inner) | | `display` | `impl Display for Type { ... }` | | `defaultimpl` | `impl Default for Type { ... }` | | `fromimpl` | `impl From for Dest { ... }` | ### 生命周期与泛型 | 触发词 | 展开结果 | |--------|---------| | `lifefn` | `fn name<'a>(x: &'a T) -> &'a U { }` | | `genfn` | `fn name(x: T) -> Ret { }` | | `genstruct` | `struct Name { field: T }` | | `genimpl` | `impl Name { }` | | `where` | `where T: Trait` | | `const_generic` | `struct Name { }` | | `assoc_type` | `type Item = T;` | ### Unsafe / FFI | 触发词 | 展开结果 | |--------|---------| | `unsafe_fn` | `unsafe fn name(...) -> Ret { }` | | `unsafe_block` | `unsafe { }` | | `unsafe_impl` | `unsafe impl Trait for Type { }` | | `static_mut` | `static mut NAME: Type = init;` | ### Tokio 异步 | 触发词 | 展开结果 | |--------|---------| | `tokio_main` | `#[tokio::main] async fn main()` | | `tokio_select` | `tokio::select! { ... }` | | `tokio_join` | `tokio::join!(a, b)` | | `tokio_try_join` | `tokio::try_join!(a, b)` | | `pin` | `Pin>` | | `pin_box` | `Box::pin(async { })` | | `tokio_sleep` | `tokio::time::sleep(Duration).await` | | `tokio_interval` | `tokio::time::interval(Duration)` | ### Serde 属性 | 触发词 | 展开结果 | |--------|---------| | `serde_rename` | `#[serde(rename = "")]` | | `serde_rename_all` | `#[serde(rename_all = "")]` | | `serde_skip` | `#[serde(skip)]` | | `serde_default` | `#[serde(default)]` | | `serde_flatten` | `#[serde(flatten)]` | | `serde_with` | `#[serde(with = "")]` | | `serde_skip_if` | `#[serde(skip_serializing_if = "")]` | | `serde_tag` | `#[serde(tag = "")]` | ### 智能指针 | 触发词 | 展开结果 | |--------|---------| | `refcell` | `RefCell::new(value)` | | `cell` | `Cell::new(value)` | | `rc` | `Rc::new(value)` | | `phantom` | `PhantomData::` | | `once_cell` | `static VAR: OnceLock = OnceLock::new();` | | `lazy_lock` | `static VAR: LazyLock = LazyLock::new(\|\| init);` | ### Trait 实现 | 触发词 | 展开结果 | |--------|---------| | `iterator_impl` | `impl Iterator for Type { type Item; fn next }` | | `index_impl` | `impl Index for Type { }` | | `deref_impl` | `impl Deref for Type { }` | | `drop_impl` | `impl Drop for Type { }` | | `fromstr_impl` | `impl FromStr for Type { }` | | `clone_impl` | `impl Clone for Type { }` | | `partial_eq_impl` | `impl PartialEq for Type { }` | ### use 语句 | 触发词 | 展开结果 | |--------|---------| | `use_std` | `use std::...;` | | `use_crate` | `use crate::...;` | | `use_super` | `use super::...;` | | `use_self` | `use self::...;` | | `use_prelude` | `use crate::prelude::*;` | ### 绑定 / 解构 / 返回 / 内存
📋 更多 snippet(点击展开) | 触发词 | 展开结果 | |--------|---------| | `let_mut` | `let mut x = init;` | | `let_ref` | `let x = &init;` | | `let_mut_ref` | `let x = &mut init;` | | `destruct_tuple` | `let (a, b) = tuple;` | | `destruct_struct` | `let Foo { x, y } = foo;` | | `return_ok` | `return Ok(value);` | | `return_err` | `return Err(...);` | | `return_some` / `return_none` | `return Some(...)` / `return None;` | | `as_int` | `as i32` | | `try_into` | `.try_into().unwrap()` | | `into` / `from` | `.into()` / `From::from(value)` | | `transmute` | `std::mem::transmute(value)` | | `mem_swap` | `std::mem::swap(&mut a, &mut b);` | | `mem_take` | `std::mem::take(&mut value)` | | `size_of` / `align_of` | `std::mem::size_of::()` 等 | | `closure` | `\|args\| expr` | | `closure_move` | `move \|args\| expr` | | `range` | `start..end` | | `range_inclusive` | `start..=end` | | `cfg_test` | `#[cfg(test)]` | | `cfg_feature` | `#[cfg(feature = "")]` | | `cold` | `#[cold]` | | `track_caller` | `#[track_caller]` | | `doc` | `/// ` (行文档注释) | | `docmod` | `//! ` (模块文档注释) |
> 完整 269 个 snippet 列表另见 [KEYMAPS_zh.md](KEYMAPS_zh.md)。 --- ## 📁 目录结构 ``` ~/.config/nvim/ # 安装后的配置(从仓库的 nvim/ 文件夹复制) ├── init.lua # 入口: bootstrap lazy.nvim + 加载模块 ├── .stylua.toml # Lua 格式化配置 (stylua) ├── lua/ │ ├── autocmds.lua # 自动命令: 保存重跑 / inlay hints / 光标色条 │ ├── chadrc.lua # NvChad 主题 / UI / nvdash 启动画面配置 │ ├── configs/ │ │ ├── conform.lua # 格式化: rustfmt / stylua / taplo / biome / prettier │ │ ├── lazy.lua # lazy.nvim 配置 │ │ └── lspconfig.lua # LSP: html / cssls / ts_ls / tailwindcss │ ├── mappings.lua # 全部自定义快捷键 │ ├── options.lua # Neovim 选项 │ └── plugins/ │ └── init.lua # 所有插件配置 ├── luasnippets/ │ └── rust.lua # Rust 全量自定义 snippet (269, 仅 Rust) └── lazy-lock.json # 插件版本锁定 ``` **仓库结构**(从 Gitee 克隆的完整内容): ``` rusty-nvim/ ├── nvim/ # ← 纯 Neovim 配置(复制到 ~/.config/nvim/) │ ├── init.lua │ ├── .stylua.toml │ ├── lazy-lock.json │ ├── lua/ # ... (与上方安装后的布局相同) │ └── luasnippets/ ├── install.sh # 一键安装脚本 ├── uninstall.sh # 一键卸载脚本 ├── gifs/ # README 演示 GIF ├── KEYMAPS.md / KEYMAPS_zh.md # 快捷键速查表 ├── README.md / README_zh.md # 文档 └── LICENSE ``` ### luasnippets/ 为什么放在 nvim/ 根目录? 这是 NvChad 的约定路径。`init.lua` 中通过 `vim.g.lua_snippets_path` 指向它,LuaSnip 的 `from_lua` loader 期望一个独立的文件目录(每个 `.lua` 文件返回一个 filetype 的 snippet 表),而不是 Lua 模块路径。放在配置根目录是正确的。 --- ## 🔧 自定义 ### 更改主题 编辑 `lua/chadrc.lua`: ```lua M.base46 = { theme = "onedark", -- "catppuccin", "tokyonight", "gruvbox" 等 } ``` ### 修改启动画面 编辑 `lua/chadrc.lua` 中的 `M.nvdash.header` 表: ```lua M.nvdash = { load_on_startup = true, header = { " 你的 ASCII art ", }, buttons = { { txt = "▸ Find File", keys = "ff", cmd = "Telescope find_files" }, -- 添加或删除按钮 { txt = "▸ Quit", keys = "q", cmd = "qa" }, }, } ``` ### 添加自己的 snippet 在 `luasnippets/` 下新建文件,例如 `luasnippets/python.lua`: ```lua local ls = require("luasnip") local parse = ls.parser.parse_snippet return { parse({ trig = "ifmain" }, 'if __name__ == "__main__":\n $0'), } ``` ### 调整 rust-analyzer 配置 编辑 `lua/plugins/init.lua` 中 `rustaceanvim` 的 `default_settings` 部分。 ### 添加新的 Mason 工具 编辑 `lua/plugins/init.lua` 中 `mason-tool-installer` 的 `ensure_installed` 列表。 --- ## ❓ FAQ
首次打开 .rs 文件后没有补全? rust-analyzer 正在索引项目,看状态栏的进度。大型项目可能需要 10-30 秒。
图标显示为方块/问号? 未安装 Nerd Font。运行 `brew install --cask font-jetbrains-mono-nerd-font`,然后在终端设置中选为默认字体。
<leader>rr 没反应? 需要先在 Rust 项目目录(含 `Cargo.toml`)中打开文件。脚本会自动向上查找 package 目录。
调试器报错? codelldb 可能还在安装中。运行 `:Mason` 查看状态,或手动 `:MasonInstall codelldb`。
Tab 有时导航列表,有时跳 snippet,怎么区分? 菜单可见时 Tab 导航列表;菜单不可见且在 snippet 中时跳占位符。如果菜单挡住了 snippet 跳转,用 `` 强制跳。
如何更新所有插件? 运行 `:Lazy sync` — 更新所有插件、安装缺失插件并清理过期插件。 如果只想更新版本而不增删插件:`:Lazy update`。
如何更新 rust-analyzer 等工具? 运行 `:MasonUpdate` 或 `:Mason` 然后按 `U`。
:w 保存后底部有 "3L, 46B written" 提示? 不会。配置中用 `cnoreabbrev` 把 `:w` 自动变成了 `silent! w`,消除了 hit-enter 提示。
保存 .rs 文件后终端自动重跑? 只有在你先按了 `rr` 之后才启用。按 `rq` 可停止自动重跑。
--- ## 🙏 致谢 - [NvChad](https://github.com/NvChad/NvChad) — UI 框架 - [rustaceanvim](https://github.com/mrcjkb/rustaceanvim) — Rust LSP 集成 - [blink.cmp](https://github.com/saghen/blink.cmp) — 补全引擎 - [LuaSnip](https://github.com/L3MON4D3/LuaSnip) — Snippet 引擎 - [nvim-dap](https://github.com/mfussenegger/nvim-dap) — 调试适配器 - [Telescope](https://github.com/nvim-telescope/telescope.nvim) — 模糊搜索 - [trouble.nvim](https://github.com/folke/trouble.nvim) — 诊断面板 - [crates.nvim](https://github.com/saecki/crates.nvim) — 依赖管理 - [neotest](https://github.com/nvim-neotest/neotest) — 测试运行框架 - [which-key.nvim](https://github.com/folke/which-key.nvim) — 快捷键发现 - [mini.surround](https://github.com/echasnovski/mini.nvim) — Surround 操作 ---
**如果这个配置对你有帮助,欢迎 ⭐ Star!** [GitHub 镜像](https://github.com/lisering/rusty-nvim)