:::info
更新:neovim0.6-blogs 👈
注意:由于 nvim0.5(目前最新稳定版为 0.6) 直接支持 lsp,且使用 lua 插件系统,故以下内容适用于 vim 和 nvim0.4。如果你使用 nvim0.5 以上的版本,也可以参考这里的一些设置。
:::
前言
前段时间发现一篇 2021 年用什么 IDE 开发 Rust ?文章,梳理了大部分 IDE。
我最感兴趣的是这张图:https://areweideyet.com/(Rust 的特色口号:Are we xx yet?)
这个网站推荐的 Vim / Neovim 重要插件有:
Important packages | Important Neovim-only packages | |
---|---|---|
- rust.vim - coc-rls (relying on Coc) - YouCompleteMe-rust - vim-racer |
- syntastic - vim-snippets (relying on UltiSnips) - ncm2-racer (relying on ncm2) |
- lldb.nvim (unmaintained as of 2018-03-11) - nvim-cm-racer (relying on nvim-completion-manager, which is unmaintained as of 2018-04-18) |
仔细一看,截止 2019-07-15😓 有些 IDE 和插件的介绍也都四五年前了 : (
anyway 想找个深度体验+完整介绍的最新文章没找到。索性自己尝试和记录。
比如图里面罗列的功能出自哪个插件、哪个插件提供的哪个功能最佳,新手肯定非常关心。(只有新手最懂新手bushi)
关于 vim,我有几个踩过的坑需要说明一下:
- spacevim 安装成功之后未报错,但是进入 vim 之后配置没有生效(可能默认不是
~/.vimrc
?),也没找到相关的解决办法,再加上依赖庞大(需要 py、lua、字体之类的),一点也不精简,因此放弃。 - 鉴于 YouCompleteMe 实在难装,一大堆难下载的依赖,那几个 py 三方库依赖非要去 github 下载,放弃。
- rust-analyzer 是最新的 Rust IDE 辅助工具,支持各大编辑器,对 vim 的支持有多种方式。其中 coc.nvim 方式 简单、纯粹且强大,非常棒! p.s. coc.nvim / coc-snippets / coc-rust-analyzer 竟然全是国人开发的 👍,还不赶紧支持一下,去点个 ⭐ 也行啊~~
★ 由于 vim 的配置文件在 vim 和 neovim 下不同,这里介绍一下:
vim: ~/.vimrc
neovim: ~/.config/nvim/init.vim
笔者之前用的 vim + vundle 插件管理,后面换成了 neovim + vim-plug。系统环境:腾讯云服务器 ubuntu focal 20.04
注意:
Rust 专用插件不是仅支持我写的功能,而是我觉得这个功能最好用,或者我没找到其他的功能怎么用。
比如 rust-analyzer 文档写了很多功能 (features) ,拿 vscode 举例(毕竟 vscode 最大程度上支持 RA),对其他 IDE 说得不清楚,那么只能去看具体实现的插件的使用说明。
如果有小伙伴补充,欢迎 鼠标选中文字评论 / 在下方留言 ,我加上。
语法增强 rust.vim
官网:https://github.com/rust-lang/rust.vim
官方提供的 vim 插件,主要增强 vim 对 rust 的语法识别。安装之后把以下内容添加到 vim 的配置文件。
" === rust.vim 配置 ===
syntax enable
filetype plugin indent on
" 保存时代码自动格式化
let g:rustfmt_autosave = 1
" 手动调用格式化, Visual 模式下局部格式化,Normal 模式下当前文件内容格式化
" 有时候代码有错误时,rust.vim 不会调用格式化,手动格式化就很方便
vnoremap <leader>ft :RustFmtRange<CR>
nnoremap <leader>ft :RustFmt<CR>
" 设置编译运行 (来自 rust.vim,加命令行参数则使用命令 `:RustRun!`)
nnoremap <M-r> :RustRun<CR>
" 使用 `:verbose nmap <M-t>` 检测 Alt-t是否被占用
" 使用 `:verbose nmap` 则显示所有快捷键绑定信息
nnoremap <M-t> :RustTest<CR>
使用 :h Rust
查看 rust.vim
的说明。支持在 Rust 文件中使用以下 vim 命令:
:RustEmitAsm
:RustEmitIr
:RustExpand
:RustFmt
:RustFmtRange
:RustInfo
:RustInfoToClipboard
:RustInfoToFile
:RustPlay
:RustRun
:RustTest
以及 cargo 命令的 vim 命令(可以直接跟参数)
COMMANDS rust-commands
Invoking Cargo
--------------
This plug defines very simple shortcuts for invoking Cargo from with Vim.
:Cargo <args> :Cargo
Runs 'cargo' with the provided arguments.
:Cbuild <args> :Cbuild
Shortcut for 'cargo build`.
:Cclean <args> :Cclean
Shortcut for 'cargo clean`.
:Cdoc <args> :Cdoc
Shortcut for 'cargo doc`.
:Cinit <args> :Cinit
Shortcut for 'cargo init`.
:Crun <args> :Crun
Shortcut for 'cargo run`.
:Ctest <args> :Ctest
Shortcut for 'cargo test`.
:Cupdate <args> :Cupdate
Shortcut for 'cargo update`.
:Cbench <args> :Cbench
Shortcut for 'cargo bench`.
:Csearch <args> :Csearch
Shortcut for 'cargo search`.
:Cpublish <args> :Cpublish
Shortcut for 'cargo publish`.
:Cinstall <args> :Cinstall
Shortcut for 'cargo install`.
:Cruntarget <args> :Cruntarget
Shortcut for 'cargo run --bin' or 'cargo run --example',
depending on the currently open buffer.
关于在 vim 中编译运行或测试 Rust :
:RustRun
主要用于把单个 rs 文件编译运行到临时文件夹(但是识别不了Cargo.toml
配置,所以不适合调用 crate 的情况);:RustRun!
带参数编译运行。:RustTest
只运行当前光标所在的测试,或者:RustTest!
运行所有测试。它会开启窗口,并且可以控制开启窗口的方式。- RA 和 Coc-RA 提供的命令:
:CocCommand rust-analyzer.run
这会让你选择以哪些方式编译,编译方式比较少,所以如果你常用它提供的那些方式,是个不错的选择。但大部分情况下你会使用后面几种方式。 - vim 命令:比如
:!cargo run
;如果你需要复杂的编译命令,但又不需要记录此次运行结果,那么推荐这种方式,比如!cargo test --test one-test -- --nocapture
会在 cmd window 显示测试结果。 - cargo 命令的 vim 命令:比如
:Crun
、:Ctest test --test one-test -- --nocapture
。等价于 vim 命令,好处在于运行结果不会像执行:!cargo xx
那样按任意键关闭,而是开启窗口,让你手动关闭。 使用 quickfix 窗口:推荐搭配 syntastic (quickfix 窗口的增强插件)一起使用
方法一:在配置文件中写入以下内容,那么可以直接使用
:make
命令代替:!cargo
或:Crun
之类的命令,而且常驻开启 quickfix 窗口,也就是说,你可以直接跳转到出错文件的准确位置。适合于逐一排查报错,或者经常查看报错。强烈推荐!autocmd FileType rust compiler cargo
autocmd QuickFixCmdPost [^l]* nested cwindow
autocmd QuickFixCmdPost l* nested lwindow
方法二:使用 asyncrun.vim 异步运行/测试项目,而且可以指定输出模式
AsyncRun -mode=async cargo test --test one test
。
- 使用 vim 的内置终端(
:terminal
),如果你需要历史编译记录 👉 指路介绍
1、2、5、6 方法一 需要 rust.vim 插件。
关于根据语法识别来配置高亮:笔者自己的 color scheme 配置
代码片段 coc-snippets
Always async, never slows you down. 始终保持异步,永不减慢您的速度。
coc 系的插件非常强大。。。有了它,完全享受在 vim 编辑文件的飞速当中。
snippet 相关链接:coc-snippets 官网 | ultisnips 官方 | vim-snippets 官方: UltiSnips | ultisnip 完整案例 | 笔者的不定期更新:coc-snippets 用法
:CocInstall coc-snippets
进行安装(👉 coc.nvim 安装指路):CocCommand snippets.editSnippets
自定义当前打开文件类型的 snippets(若需定义所有类型文件,编辑.config/coc/ultisnips/all.snippets
即可)- 快捷键:
- 使用
Ctrl-j
进行下一个填写; - 使用
Ctrl-k
进行上一个填写; - 在
.vimrc
中添加vmap <m-x> <Plug>(coc-snippets-select)
来实现可视模式下利用Alt-X
使用 ultisnips 的 visual 功能(把选中的内容直接放置在 snippet 内部) - 使用
<tab>
键触发补全、补全确认、snippet 展开和跳转下一个(trigger completion, completion confirm, snippet expand and jump like VSCode),需把以下内容加入配置文件: ``` inoremap\ pumvisible() ? coc#_select_confirm() : \ coc#expandableOrJumpable() ? “\ =coc#rpc#request(‘doKeymap’, [‘snippets-expand-jump’,’’])\ “ : \ check_back_space() ? “\ “ : \ coc#refresh()
- 使用
function! s:check_back_space() abort let col = col(‘.’) - 1 return !col || getline(‘.’)[col - 1] =~# ‘\s’ endfunction
let g:coc_snippet_next = ‘
4. coc-rust-analyzer 也具有 snippet 功能,但是暂时无法自定义?(RA 加载更慢一些,别担心影响编辑,是异步加载啦),且 snippet 方式等于加强版的补全(支持动态占位符,但不是所有的提示都支持占位符),因此使用专门的 snippet 工具。
4. coc-snippets 不仅仅支持 大部分 ultisnips 使用方式,还支持 snipmate 、VSCode snippets 方式。ultisnips 最大亮点:插值、自定义 python 函数。
<br />visual 插入方式:<br />
<a name="d5ac0ee8917d1b00f4c50e7d22a87642"></a>
# 代码 补全 | 检查 | 跳转 利器
RA 官方说明:[rust-analyzer: coc-rust-analyzer](https://rust-analyzer.github.io/manual.html#coc-rust-analyzer)<br />笔者的成品:<br />
<a name="dc2adf76707bded35571a777d0735da8"></a>
## 安装 coc-rust-analyzer
coc.nvim([👉 coc 安装指路](https://www.yuque.com/zhoujiping/programming/rust-vim-settings#120e57340f95ce705cf584bb5b1ae2c6))、rustc 和 cargo 都已经安装好之后:
1. 终端输入 `vim` 进入 vim,输入 `:CocInstall coc-rust-analyzer` 自动下载和安装(甚至更新) rust-analyzer 工具,因此无需提前安装 rust-analyzer;
1. 初次使用时,进入一个 rs 文件之后,vim 会询问是否下载 rust-analyzer,选择 yes 就可以使用了;
1. 更多参数和配置见 [coc-rust-analyzer](https://github.com/fannheyward/coc-rust-analyzer)、[Using-coc-extensions](https://github.com/neoclide/coc.nvim/wiki/Using-coc-extensions)
<a name="4774546a831a0b6942ac5101a59c02b1"></a>
## 更新 coc-rust-analyzer
一般来说,在启用 coc-rust-analyzer 并且打开 rust 文件时,会弹出窗口提示更新 coc-rust-analyzer 和 rust-analyzer。<br />此外还使用以下命令让插件更新:<br />`:CocUpdate` 更新 coc-rust-analyzer 插件<br />`:CocCommand rust-analyzer.upgrade` 更新 rust-analyzer 程序
<a name="d5266d704f38127c7378a6ba52041d04"></a>
## 手动更新 rust-analyzer
因为 rust-analyzer 更新频繁, `coc-rust-analyzer` 自动更新机制对访问 github 慢的用户来说不友好,并且每次打开文件都会弹窗提示更新,使用以下方式来不提醒更新,并且手动下载最新的 rust-analyzer :<br />在 vim 输入 `:CocConfig` 或者直接编辑 `~/.config/nvim/coc-settings.json` 文件,添加
```json
{
"rust-analyzer.updates.prompt": false,
"rust-analyzer.server.path": "/home/ubuntu/.config/coc/extensions/coc-rust-analyzer-data/rust-analyzer"
}
rust-analyzer 下载地址:https://github.com/rust-analyzer/rust-analyzer/releases
ubuntu 系统下载 rust-analyzer-x86_64-unknown-linux-gnu.gz
,解压、添加执行权限、重命名:
gunzip rust-analyzer-x86_64-unknown-linux-gnu.gz
chmod +x rust-analyzer-x86_64-unknown-linux-gnu
mv rust-analyzer-x86_64-unknown-linux-gnu rust-analyzer
受 分享一种安装/更新/切换 rust-analyzer 版本的方法 启发,翻看确认了一下 RA 文档,竟然错过了 rustup 这么好的工具。上述的笨办法以下三步到位:
切换到 / 增加 nightly 版本的 Rust,并增加 rust-analyzer-preview 二进制组件支持:
rustup default nightly # rustup default 可以查看默认的 Rust 版本
rustup component add rust-analyzer-preview # 注意 rust-analyzer 只在 Rust 默认版本是 nightly 时生效
在 vim 输入
:CocConfig
或者直接编辑~/.config/nvim/coc-settings.json
文件,添加以下配置:{
"rust-analyzer.updates.prompt": false,
"rust-analyzer.server.path": "~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rust-analyzer"
}
使用
rustup update
的时候会自动更新 rustup component,所以更新 Rust 的时候直接更新 RA功能介绍
默认开启的功能
代码诊断检查:
- 支持以下命令
:CocCommand xx
命令。
例如在 neovim 中默认会设置 行内类型提示 (inlay type hint),也就是rust-analyzer.run
rust-analyzer.ssr
rust-analyzer.upgrade
rust-analyzer.viewHir
rust-analyzer.openDocs
rust-analyzer.joinLines
rust-analyzer.peekTests
rust-analyzer.syntaxTree
rust-analyzer.memoryUsage
rust-analyzer.expandMacro
rust-analyzer.explainError
rust-analyzer.parentModule
rust-analyzer.matchingBrace
rust-analyzer.openCargoToml
rust-analyzer.serverVersion
rust-analyzer.analyzerStatus
rust-analyzer.reloadWorkspace
rust-analyzer.toggleInlayHints
rust-analyzer.echoRunCommandLine
rust-analyzer.reload
三角形后的蓝色部分。
在复制源代码的时候暂时不需要这个提示,就可以在普通模式下输入以下命令来触发。
如果想要复制出行内类型的提示,可以修改 Coc 配置文件::CocCommand rust-analyzer.toggleInlayHints
在 vim 里面,命令转换成快捷键是非常基础和简单的。配置文件写入以下内容:{
"rust-analyzer.inlayHints.chainingHintsSeparator": "// ",
"rust-analyzer.inlayHints.typeHintsSeparator": "// ",
}
" 设置触发/关闭行内类型提示
nnoremap <F4> :CocCommand rust-analyzer.toggleInlayHints<CR>
♥ 各种定义跳转
coc.nvim 支持各式跳转:coc-key-mappings。coc.nvim readme 文档给出例子:
这里的:" === coc.nvim 设置 ===
" coc-definition 和 racer 提供的 rust-def 功能是一样的,快捷键冲突,因此只要设置一个就好
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)
type-definition 指 一个 变量 / 参数 / 字段 的类型定义;
implementation 指 structs / enums / traits 的 impl block;
references 指 变量使用的地方。
rust-analyzer 不支持 declaration。Rust 貌似没有 delcaration 的概念(笔者暂时不清楚这个问题)。
一般来说 declaration 和 definition 是同时进行,几乎没有区别。 C 语言中可以先 declaration 再 definition,参考 difference between definition and declaration。
拓展:
不建议使用 racer-vim♥ hover 悬浮文档
.config/nvim/coc-settings.json
配置的相关设置:{
// rust-doc hover 悬浮文档的相关配置
"rust-analyzer.hoverActions.linksInHover": true,
"hover.floatMaxHeight": 20 ,
"hover.floatMaxWidth": 80,
}
设置唤起 hover 的快捷键:
" 设置 hover 悬浮文档
nnoremap <silent> <leader>h :call CocActionAsync('doHover')<CR>
使用
<C-f>
和<C-b>
进行翻页,快捷键映射见 coc.nvim 使用 部分- 使用
<C-w-w>
让光标在 hover 和 编辑窗口跳转,无需映射neovim 的 floating/popup window 高度设置有 bug: 如果手动设置的 winheight 会导致弹出框高度变成 winheight ,从而覆盖掉 coc-settings.json 的配置。所以暂时不能设置 winheight 。 见 issue:https://github.com/neoclide/coc.nvim/issues/3058、https://github.com/neovim/neovim/pull/13303
♥ 实用的快捷键
" 设置变量重命名:批量改变变量的名称
nmap <leader>rn <Plug>(coc-rename)
" 设置 hover 悬浮文档
nnoremap <silent> <leader>h :call CocActionAsync('doHover')<CR>
" 设置行内类型提示
nnoremap <F4> :CocCommand rust-analyzer.toggleInlayHints<CR>
" 设置编译运行 (来自 rust.vim,加命令行参数则使用 :RustRun!)
nnoremap <M-r> :RustRun<CR>
" 控制 Coc event 启用,比如需要复制内容的时候,暂时关闭代码诊断干扰
nnoremap <leader>ee :CocEnable<CR>
nnoremap <leader>ed :CocDisable<CR>
" 不保存的情况下进行格式化
" nnoremap <leader>rf :call CocAction('format')<CR>
nmap <leader>ft <Plug>(coc-format)
" 重构:把光标下的变量/trait/类型所有相关的代码提取出来,在左窗口统一修改
nmap <leader>rf <Plug>(coc-refactor)
" 一些诊断信息跳转
nmap <leader>dn <Plug>(coc-diagnostic-next)
nmap <leader>dp <Plug>(coc-diagnostic-prev)
nmap <leader>en <Plug>(coc-diagnostic-next-error)
nmap <leader>ep <Plug>(coc-diagnostic-prev-error)
nmap <leader>fx <Plug>(coc-fix-current)
nmap <leader>fj <Plug>(coc-float-jump)
" nmap <leader>di <Plug>(coc-diagnostic-info)
" nmap <leader>fh <Up><Plug>(coc-float-hide)
" selection:选择函数(内部、全部)
xmap if <Plug>(coc-funcobj-i)
omap if <Plug>(coc-funcobj-i)
xmap af <Plug>(coc-funcobj-a)
omap af <Plug>(coc-funcobj-a)
" 从后向前选择
vmap <leader>rs <Plug>(coc-range-select)
nmap <leader>rs <Plug>(coc-range-select)
笔者关于按键映射的完整设置(写得很乱,仅供参考):
" === vim 基本按键设置 ===
" F2 触发 paste 模式开关
set pastetoggle=<F2>
" F3 触发搜索高亮开关
nnoremap <F3> :set hlsearch!<CR>
" 触发行号
nnoremap <leader>nb :set number!<CR>
" 清除文件内容 delete all
" nnoremap <leader>da :execute "normal! ggVGDI"<CR>
nnoremap <leader>da ggVGD<F2>I
" 跳转缓冲区
nnoremap <leader>bn :bn<CR>
nnoremap <leader>bp :bp<CR>
nnoremap <leader>bd :bd<CR>
nnoremap <leader>bs :buffers<CR>
" 保存、退出
nnoremap <leader>w :w<CR>
nnoremap <leader>q :q<CR>
nnoremap <leader>wq :wq<CR>
nnoremap <leader>wbd :w \| :bd<CR>
" set term=xterm-256color
" set nu! "显示行数
set autoindent "自动缩进
"let g:terminal_key="<C-0>"
let g:terminal_height=10
let g:rustfmt_autosave = 1
let mapleader ="\\"
" === vim-easy-align 按键设置 ===
" Start interactive EasyAlign in visual mode (e.g. vipga)
xmap ga <Plug>(EasyAlign)
" Start interactive EasyAlign for a motion/text object (e.g. gaip)
nmap ga <Plug>(EasyAlign)
" === racer 设置 ===
" 跳转定义等操作时自动保存缓存
set hidden
" 设置 racer 路径
let g:racer_cmd = "/home/ubuntu/.cargo/bin/racer"
" Racer 的补全方式和 (coc-)rust-analyzer 智能补全不太一样,使用的是 vim 的
" Omni completion 补全方式,需要手动触发,快捷键是 <c-n> <c-o>,并不推荐
" vim-racer 的补全功能
" 补全时展示完整的函数定义,比如参数和返回类型
let g:racer_experimental_completer = 1
" 补全时添加括号
let g:racer_insert_paren = 1
" 跳转定义快捷键,十分有用
augroup Racer
autocmd!
autocmd FileType rust nmap <buffer> gd <Plug>(rust-def)
" autocmd FileType rust nmap <buffer> gs <Plug>(rust-def-split)
autocmd FileType rust nmap <buffer> gx <Plug>(rust-def-vertical)
" autocmd FileType rust nmap <buffer> gt <Plug>(rust-def-tab)
autocmd FileType rust nmap <buffer> <leader>gd <Plug>(rust-doc)
autocmd FileType rust nmap <buffer> <leader>gD <Plug>(rust-doc-tab)
augroup END
" === coc.nvim 按键设置 ===
" https://github.com/neoclide/coc.nvim/blob/ec2835fdddb623f22d48e1a58c6c928154b28098/doc/coc.txt#L1060
" GoTo code navigation.
" Rust-analyzer 貌似暂不支持 declaration 和 implementation 跳转
" 关于 definition 和 doc 跳转的部分已经在 Racer.vim 设置了
" nmap <silent> <leader>g= <Plug>(coc-declaration)
nmap <silent> <leader>gy <Plug>(coc-type-definition)
nmap <silent> <leader>gi <Plug>(coc-implementation)
nmap <silent> <leader>gr <Plug>(coc-references)
" 一些实用的设置
" 设置变量重命名:批量改变变量的名称
nmap <leader>rn <Plug>(coc-rename)
" 设置 hover 悬浮文档
nnoremap <silent> <leader>h :call CocActionAsync('doHover')<CR>
" 设置行内类型提示
nnoremap <F4> :CocCommand rust-analyzer.toggleInlayHints<CR>
" 设置编译运行 (来自 rust.vim,加命令行参数则使用 :RustRun!)
nnoremap <M-r> :RustRun<CR>
" 控制 Coc event 启用,比如需要复制内容的时候,暂时关闭代码诊断干扰
nnoremap <leader>ee :CocEnable<CR>
nnoremap <leader>ed :CocDisable<CR>
" 不保存的情况下进行格式化
" nnoremap <leader>rf :call CocAction('format')<CR>
nmap <leader>ft <Plug>(coc-format)
" 重构:把光标下的变量/trait/类型所有相关的代码提取出来,在左窗口统一修改
nmap <leader>rf <Plug>(coc-refactor)
" 一些诊断信息跳转
" nmap <leader>di <Plug>(coc-diagnostic-info)
nmap <leader>dn <Plug>(coc-diagnostic-next)
nmap <leader>dp <Plug>(coc-diagnostic-prev)
nmap <leader>en <Plug>(coc-diagnostic-next-error)
nmap <leader>ep <Plug>(coc-diagnostic-prev-error)
nmap <leader>fx <Plug>(coc-fix-current)
" nmap <leader>fh <Up><Plug>(coc-float-hide)
nmap <leader>fj <Plug>(coc-float-jump)
" selection:选择函数(内部、全部)
xmap if <Plug>(coc-funcobj-i)
omap if <Plug>(coc-funcobj-i)
xmap af <Plug>(coc-funcobj-a)
omap af <Plug>(coc-funcobj-a)
" 从后向前选择
vmap <leader>rs <Plug>(coc-range-select)
nmap <leader>rs <Plug>(coc-range-select)
" 回退上一步选择
vmap <leader>bs <Plug>(coc-range-select-backward)
" rust analyzer 不支持
" vmap <leader>fs <Plug>(coc-format-selected)
" nmap <leader>fs <Plug>(coc-format-selected)
" xmap ic <Plug>(coc-classobj-i)
" omap ic <Plug>(coc-classobj-i)
" xmap ac <Plug>(coc-classobj-a)
" omap ac <Plug>(coc-classobj-a)
" === coc-rust-analyzer 按键设置===
" 它会列出当前所有可执行的方式
nnoremap <leader>rr :CocCommand rust-analyzer.run<CR>
nnoremap <leader>re :CocCommand rust-analyzer.reload<CR>
" nnoremap <leader>sd :CocCommand rust-analyzer.openDocs<CR>
nnoremap <leader>ct :CocCommand rust-analyzer.openCargoToml<CR>
" === coc-snippets 按键设置===
" Use <C-l> for trigger snippet expand.
imap <C-l> <Plug>(coc-snippets-expand)
" Use <m-x> for convert visual selected code to snippet
vmap <m-x> <Plug>(coc-snippets-select)
" 设置 tab > trigger completion, completion confirm, snippet expand and jump like VSCode.
inoremap <silent><expr> <TAB>
\ pumvisible() ? coc#_select_confirm() :
\ coc#expandableOrJumpable() ? "\<C-r>=coc#rpc#request('doKeymap', ['snippets-expand-jump',''])\<CR>" :
\ <SID>check_back_space() ? "\<TAB>" :
\ coc#refresh()
function! s:check_back_space() abort
let col = col('.') - 1
return !col || getline('.')[col - 1] =~# '\s'
endfunction
let g:coc_snippet_next = '<tab>'
" === nerdcommenter ===
" Create default mappings
let g:NERDCreateDefaultMappings = 1
" Add spaces after comment delimiters by default
let g:NERDSpaceDelims = 1
" Use compact syntax for prettified multi-line comments
let g:NERDCompactSexyComs = 1
" Align line-wise comment delimiters flush left instead of following code indentation
let g:NERDDefaultAlign = 'left'
" Set a language to use its alternate delimiters by default
" let g:NERDAltDelims_java = 1
" Add your own custom formats or override the defaults
" let g:NERDCustomDelimiters = { 'rust': { 'left': '/*', 'right': '*/', 'leftAlt': '///','rightAlt': '' } }
let g:NERDCustomDelimiters = { 'rust': { 'left': '//', 'right': '', 'leftAlt': '///','rightAlt': '' } }
" Allow commenting and inverting empty lines (useful when commenting a region)
let g:NERDCommentEmptyLines = 1
" Enable trimming of trailing whitespace when uncommenting
let g:NERDTrimTrailingWhitespace = 1
" Enable NERDCommenterToggle to check all selected lines is commented or not
let g:NERDToggleCheckAllLines = 1
" ban & override the nested comment
" let NERDDefaultNesting = 0
" nnoremap <silent> <leader>cc :call NERDComment('n', 'toggle')<CR>
" Specifies if trailing whitespace should be deleted when uncommenting
let NERDTrimTrailingWhitespace = 1
" === :W :Q 命令 ===
" https://stackoverflow.com/questions/10590165/is-there-a-way-in-vim-to-make-w-to-do-the-same-thing-as-w
" 注意这只会允许单独的 :W 和 :Q ,而不会 :WQ :Wq :wQ
" command! -bang -range=% -complete=file -nargs=* W <line1>,<line2>write<bang> <args>
" command! -bang Q quit<bang>
command! WQ wq
command! Wq wq
command! W w
command! Q q
" command! E e
一些通用的插件
参考:7款优秀Vim插件帮你打造完美IDE
除此之外有些插件虽然很棒,但我不常用,就不再详细介绍了:vim-easy-align、tabular、vim-css-color
vim-plug 安装方式 + github 加速镜像: Plugin 'https://github.com.cnpmjs.org/作者/插件仓库名.git
nerdcommenter
官网:https://github.com/preservim/nerdcommenter
\cc 注释当前行和选中行
\cn 没有发现和\cc有区别
\c<空格> 如果被选区域有部分被注释,则对被选区域执行取消注释操作,其它情况执行反转注释操作
\cm 对被选区域用一对注释符进行注释,前面的注释对每一行都会添加注释
\ci 执行反转注释操作,选中区域注释部分取消注释,非注释部分添加注释
\cs 添加性感的注释,代码开头介绍部分通常使用该注释
\cy 添加注释,并复制被添加注释的部分
\c$ 注释当前光标到改行结尾的内容
\cA 跳转到该行结尾添加注释,并进入编辑模式
\ca 转换注释的方式,比如: /**/和//
\cl \cb 左对齐和左右对其,左右对其主要针对/**/
\cu 取消注释
注意 _\_
是 _<leader>_
键,使用命令 _:echo mapleader_
来查看具体是哪个键。若显示 _Undefined variable: mapleader_
则说明没有设置,使用命令 _:let mapleader="\\"_
或者在 _.vimrc_
中添加 _let mapleader ="\\"_
,把 _\_
设置成 _<leader>_
键。
autopair
官网:https://github.com/jiangmiao/auto-pairs
针对括号、引号等使用时的补全增强插件,让这些符号环境使用人性化。与 vim-surround 一起使用体验更佳 ~
{'(':')', '[':']', '{':'}',"'":"'",'"':'"', "`":"`", '```':'```', '"""':'"""', "'''":"'''"}
【更新】原版本在 2019 年停止开发了。但是有些细节不是很好,比如删除半个括号之后智能匹配不上,经常让光标跳转到下一个括号、过于严格的括号补全。
其 fork 版本:https://github.com/LunarWatcher/auto-pairs 更对我喜好,适度补全。
vim-surround
官网:https://github.com/tpope/vim-surround
增删改 括号、引号、XML 标签 的利器。i
是为了编辑这些环境内的文字,而 s
就是为了编辑双侧环境。
以字符 "Hello world!"
为例,并且都在普通模式下(按 Esc
)
- 增:
- 对光标所在的单词(以空格或者标点符号隔开的内容)增加环境:光标移动在单词的一个字符上,按
ysiw
+目标环境
(ysiw
的记法:you surround in word),如 光标移到H
上,按ysiw)
得到"(Hello) world!"
- 对一整行增加环境:光标移动在某一行,按
yss
+目标环境
,如yss}
、yss<div>
- 对光标所在的单词(以空格或者标点符号隔开的内容)增加环境:光标移动在单词的一个字符上,按
- 删:
- 光标移动在
"Hello world!"
任何一个字符上,输入ds"
就可把"
删除 ,从而得到Hello world!
- 删除的方式从内到外,比如
({ Hello } world!)
删除{}
和()
,只需把光标移到{ Hello }
某个字符上,使用ds{ds(
(或者ds}ds)
之类的)得到Hello world!
- 光标移动在
- 改:修改单词或一行两边的环境:光标移动在
"Hello world!"
任何一个字符上,按cs
+当前环境
+目标环境
即可。例如:- 输入
cs"'
就可把"
改成'
,从而得到'Hello world!'
- 输入
cs"]
就可把"
改成]
,从而得到[Hello world!]
- 输入
cs"[
就可把"
改成[
,从而得到[ Hello world! ]
- 针对 xml tag,输入
cs"<p>
则得到<p>Hello world!</p>
- 输入
注意: _{}_
_[]_
_()_
三种括号在增、改的时候,左右环境是不一样的,左环境会在单词左右两侧增加一个空格再放入环境中,右环境直接把单词放进环境里;但是在删的时候使用左环境或者右环境都可以把环境删除。
相关讨论:What does the “y” stand for in “ysiw”?
git in vim
todo
- 封装 git:https://github.com/tpope/vim-fugitive
- git diff 工具:https://github.com/airblade/vim-gitgutter
- 仿照 emacs 的 magit:https://github.com/TimUntersberger/neogit
- 基于 GitHub 的工作流插件:https://github.com/pwntester/octo.nvim
coc.nvim 作者在 vim 中使用 git 的一些经验:https://zhuanlan.zhihu.com/p/26137257
Rust for Vim
gnvim
未尝试
GUI for neovim, without any web bloat. 为 neovim 打造的 GUI,且无 web 膨胀。
基于 gtk 、使用 Rust 开发的 neovim GUI,因为不基于 electron,所以没有体积膨胀问题。
https://github.com/vhakulinen/gnvim
minimap.vim
国人开发的代码迷你图:https://github.com/wfxr/minimap.vim、https://github.com/wfxr/code-minimap
需要安装 code-minimap 工具(由 rust 编写的命令行小工具,有二进制版本,解压之后只需要把可执行文件放入系统环境里面),再安装 minimap.vim 插件。
目前至少要 vim8.2 才能顺利安装,默认仓库的版本为 8.1,需要添加私人仓库来更新到最新的 8.2,参考文章 《在 Ubuntu 上安装Vim 8.2》。
进入 vim 使用 :Minimap
开启迷你图;使用 :MinimapClose
关闭迷你图。以及一些写入 vimrc 的参数设置。
顺便介绍一个用 rust 写的跨平台终端命令的 benchmark tool:hyperfine(作者在 repo 上还加入了 py 可视化分析的脚本代码)
colorscheme 配置
highlight
命令 :hi
( :highlight
的简写)可以展示当前所有配色,ctermfg 表示 cterm 下的前景色(字体颜色),ctermbg 表示背景色。参考:vim 语法高亮
命令 :hi 项目
可以查询某个项目的配色。
命令 :hi 项目 ctermfg=Black ctermbg=White
之类的可用于临时预览,确定之后修改配置:重新打开所编辑的文件;或者直接在编辑文件的窗口 :source 修改的配置文件
让配置生效。
笔者关于 highlight 设置如下:
set termguicolors
" 弹出菜单
hi Pmenu ctermbg=LightRed
hi PmenuSbar ctermbg=LightRed
hi PmenuSel ctermbg=LightCyan
hi PmenuThumb ctermbg=LightCyan
" 字调整
hi Keyword ctermfg=Red
hi Function ctermfg=DarkBlue
hi Type ctermfg=Yellow ctermbg=Black cterm=none
hi CocHintSign ctermfg=Brown cterm=underline
hi Comment ctermfg=DarkGray cterm=italic
hi Identifier cterm=bold ctermfg=Blue
" hi Macro ctermfg=Yellow ctermbg=Black cterm=bold
hi Macro ctermfg=Red cterm=bold
" Delimiter
hi Special ctermfg=Magenta
hi String ctermfg=Gray
hi Operator ctermfg=Red cterm=bold
hi LineNr ctermfg=White
" === GUI ===
" 弹出菜单
hi Pmenu guibg=#0A54A0
" hi PmenuSbar guibg=#0A54A0
hi! link PmenuSbar Pmenu
hi PmenuSel guibg=#F69300
" hi PmenuThumb guibg=#F69300
hi! link PmenuThumb PmenuSel
" 字调整
hi Keyword guifg=#FFA89A
hi Function guifg=#58D0F2 guibg=Black
hi Type guifg=#54F685 gui=bold
hi CocHintSign guifg=#f06292 gui=underline
hi Comment guifg=#80cbc4 guibg=none gui=italic
hi Identifier gui=none guifg=#FFFF4D gui=bold
" hi Identifier gui=none guifg=#F6C000
" hi Macro guifg=Yellow guibg=Black gui=bold
hi Macro guifg=#FD3F3F gui=bold
" Delimiter
hi Special guifg=#FFA89A
hi String guifg=#9e9e9e
hi Operator guifg=#f44336 gui=bold
hi LineNr guifg=Grey70
hi Normal guifg=#F3FCFE guibg=Black
hi! link SpecialComment Comment
" hi rustFuncName guifg=Red
hi Folded guibg=#292A00 guifg=#FFFF00
也可修改别人的主题,参考:vim-colorschemes 修改
statusline
- vim-fugitive 让 vim 集成 git,从而可以让 statusline 显示当前文件所处的 git 分支
- vim-gitgutter 统计 diff 增、改、删 次数
- coc.nvim 、 rust-analyzer 、coc-rust-analyzer 提供四种诊断信息个数统计
- buffer 窗口统计可能不太准确
- BTW:vim-airline 很美观,但是让我的 vim 变得有些笨重 —— 高 CPU、光标移动延迟略高、statusline 过于刷新(每次使用切换模式它都要换一次颜色)
todo itchyny/lightline.vim:A light and configurable statusline/tabline plugin for Vim
" === statusline (native) ===
" https://stackoverflow.com/questions/5375240/a-more-useful-statusline-in-vim
set statusline=
set statusline+=%1*\[%n/%{NrBufs('loaded')}] " buffernr/loadednr
set statusline+=%2*\ %<%f\ " file+path
set statusline+=%3*\ %{fugitive#head()!=''?''.fugitive#head().'':'\ '}\ " fugitive for branch
set statusline+=%3*%{GitStatus()}\ " gitgutter for diff
set statusline+=%4*\ %{StatusDiagnostic()} " thanks to coc-status
set statusline+=%5*%=\ %Y\ " fileType
set statusline+=%6*\ r:%l/%L\ %3p%%\ " rownumber/total (%)
set statusline+=%7*\ c:%2c\ " colnr
set statusline+=%8*\ %m%r%w\ " Modified? Readonly? Top/bot
hi User1 guifg=#ffffff guibg=none
hi User2 guifg=#ffffff guibg=#2A9A72
hi User3 guifg=#000000 guibg=#F4905C
hi User4 guifg=#ffdad8 guibg=#880c0e
hi User5 guifg=#0A54A0 guibg=none gui=bold
hi User6 guifg=#ffffff guibg=none
hi User7 guifg=#ffffff guibg=none
hi User8 guifg=#ffffff guibg=none gui=reverse
function! NrBufs(buf_type)
" listed / loaded / modified
let key = 'buf' . $buf_type
return len(getbufinfo({key:1}))
endfunction
function! StatusDiagnostic() abort
let info = get(b:, 'coc_diagnostic_info', {})
if empty(info) | return '' | endif
let msgs = []
if get(info, 'error', 0)
call add(msgs, 'E' . info['error'])
endif
if get(info, 'warning', 0)
call add(msgs, 'W' . info['warning'])
endif
if get(info, 'hint', 0)
call add(msgs, 'H' . info['hint'])
endif
if get(info, 'information', 0)
call add(msgs, 'I' . info['information'])
endif
return join(msgs, ' ') . ' ' . get(g:, 'coc_status', '')
endfunction
function! GitStatus()
let [a,m,r] = GitGutterGetHunkSummary()
return printf('+%d ~%d -%d', a, m, r)
endfunction
常用命令
关于注释
Rust 的注释多种多样,分享几个处理注释太多问题的技巧:
如果想快速去除掉代码中的注释,删除所有以
//
开头注释的行(其他类型的注释只需修改正则表达式即可,\v
可以让特殊符号不进行转义)::g/\v^\s{0,}\/\/ .*\n/d
折叠以
//
开头注释,包括文档注释:使用 vim 的代码折叠功能autocmd FileType rust set foldmethod=expr foldexpr=getline(v:lnum)=~'^\\s*//'
结合跳转定义功能,看源码的时候不再让大段注释占据视野:
更完美的解决办法见 关于折叠 ↓关于折叠
有了前面两个 Rust 语法支持插件,我们可以使用 syntax 的 foldmethod,所以直接设置
autocmd Filetype rust set foldmethod=syntax
即可。
一级折叠(按zM
折叠到一级):
二级折叠(在一级折叠基础上按zr
展开到二级):
但是语法折叠无法对注释折叠,加上 vim 不支持多个 foldmethod 同时生效,所以采用快捷键映射:" === 代码折叠 ===
" autocmd Filetype rust set foldmethod=syntax
" 由于无法同时存在多个 foldmethod,所以可以通过快捷键来设置不同方式的折叠
nnoremap <leader>fs :set foldmethod=syntax<cr>
autocmd FileType rust set foldmethod=expr foldexpr=getline(v:lnum)=~'^\\s*//'
nnoremap <leader>fe :set foldmethod=expr foldexpr=getline(v:lnum)=~'^\\s*//'<cr>
" 更改原先行数在前的折叠样式
" 第一行源代码 ........... 折叠行数 [折叠行数占总行数百分比] +--
" http://gregsexton.org/2011/03/27/improving-the-text-displayed-in-a-vim-fold.html
function! CustomFoldText() abort
"get first non-blank line
let fs = v:foldstart
while getline(fs) =~ '^\s*$' | let fs = nextnonblank(fs + 1)
endwhile
if fs > v:foldend
let line = getline(v:foldstart)
else
let line = substitute(getline(fs), '\t', repeat(' ', &tabstop), 'g')
endif
let w = winwidth(0) - &foldcolumn - (&number ? 8 : 0)
let foldSize = 1 + v:foldend - v:foldstart
let foldSizeStr = " " . foldSize . " lines "
let foldLevelStr = repeat("+-", v:foldlevel)
let lineCount = line("$")
let foldPercentage = printf("[%2.0f%%] ", (foldSize*1.0)/lineCount*100)
let expansionString = repeat(".", w - strwidth(foldSizeStr.line.foldLevelStr.foldPercentage)-1)
return line . " " . expansionString . foldSizeStr . foldPercentage . foldLevelStr
endfunction
set foldtext=CustomFoldText()
hi Folded guibg=#292A00 guifg=#FFFF00
- 默认进入
.rs
文件时,对//
开头的注释进行折叠,而且使用//
进行多行注释保存时,会自动折叠成一行(前提在foldmethod=expr
下) - 修改了 vim 默认的折叠文字样式 (foldtext),看起来更舒服:首行折叠文字位置不变、添加折叠的百分比并进行了同级对齐、
+-
的个数代表所处的折叠级数 - 修改了折叠的颜色样式
- 使用
<leader>fe
启用foldmethod=expr
方式,只对注释折叠 - 使用
<leader>fs
启用foldmethod=syntax
方式,只对语法折叠 - 此外,折叠还能带来批量操作,比如光标移动到折叠成一行的代码上,
dd
命令删除折叠的代码;还可以当作锚点来跳转(使用zj
和zk
)
最终效果:
关于 vim 代码折叠的介绍(foldmethod、快捷键):
- https://yianwillis.github.io/vimcdoc/doc/fold.html
- https://blog.csdn.net/qq_27968607/article/details/60956584
coc.nvim
安装
coc.nvim 是基于 node 的异步插件管理器,coc 系的插件都是用命令:CocInstall
安装的。终端安装 node:
安装 vim 的插件都会用到 vim 通用的 plug 管理器,一般是在配置文件里面加入curl -sL install-node.now.sh/lts | bash
Plug(in) 'github | 网址 | 本地 等来源'
内容来安装。coc.nvim 用 vim 插件的方式安装(套娃)。
推荐使用异步的 vim-plug:👉 vim-plug 安装指路。如果使用 vundle 管理器,把Plug
替换成Plugin
。
以下是 vim-plug 安装 coc.nvim 插件方式:
或者使用加速的下载镜像(需要手动进入安装路径,从 node 安装 coc 工具):Plug 'neoclide/coc.nvim', {'branch': 'release'}
" 网速好的话到这一步就结束了
然后进入 coc.nvim 的安装路径,比如Plug 'https://hub.fastgit.org/neoclide/coc.nvim'
cd ~/.config/nvim/autoload/plugged/coc.nvim/
yarn install --frozen-lockfile
怎么解决从github下载资源慢的问题? - ailx10的回答 - 知乎 https://www.zhihu.com/question/276143842/answer/732220179
使用
支持的命令: | :CocAction
:CocCommand
:CocConfig
:CocDiagnostics
:CocDisable
:CocEnable | :CocFirst
:CocFix
:CocInfo
:CocInstall
:CocLast
:CocList | :CocListCancel
:CocListResume
:CocLocalConfig
:CocNext
:CocOpenLog
:CocPrev | :CocRebuild
:CocRestart
:CocSearch
:CocStart
:CocUninstall
:CocUpdate | :CocUpdateSync
:CocWatch | | —- | —- | —- | —- | —- |使用
:CocConfig
命令;或者编辑~/.config/nvim/coc-settings.json
文件即可。结合 coc-rust-analyer 和 coc-snippet,笔者的配置如下:{
"coc.preferences.previewMaxHeight": 20,
"dialog.maxHeight": 10 ,
"hover.floatMaxHeight": 10 ,
"hover.floatMaxWidth": 80,
"list.maxPreviewHeight": 20 ,
"notification.maxHeight": 20 ,
"rust-analyzer.diagnostics.enable": false,
"rust-analyzer.diagnostics.enableExperimental": false,
"rust-analyzer.hoverActions.linksInHover": true,
"rust-analyzer.inlayHints.chainingHints": true,
"rust-analyzer.inlayHints.chainingHintsSeparator": "// ", // "‣"
"rust-analyzer.inlayHints.typeHints": true,
"rust-analyzer.inlayHints.typeHintsSeparator": "// ", // "‣"
"rust-analyzer.procMacro.enable": true,
"rust-analyzer.server.path": "/home/ubuntu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rust-analyzer",
"rust-analyzer.updates.prompt": false,
"signature.maxWindowWidth": 20 ,
"snippets.shortcut": "Coc",
"suggest.enablePreselect":true,
"diagnostic.errorSign":"E",
"diagnostic.hintSign":"H",
"diagnostic.infoSign":"I",
"diagnostic.warningSign":"W",
}
项目内所有文件中搜索文字:
:CocSearch 被搜索的内容
。借助[ripgrep](https://github.com/BurntSushi/ripgrep)
。参考 coc 文档。- 自定义 config 参数查询
:h coc-config-suggest
- 弹出框 (popup menu) 或 浮动窗口 (floating window) 设置
<C-f>
和<C-b>
控制上下滚动 (scroll) 翻页:" Remap <C-f> and <C-b> for scroll float windows/popups.
if has('nvim-0.4.0') || has('patch-8.2.0750')
nnoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? coc#float#scroll(1) : "\<C-f>"
nnoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? coc#float#scroll(0) : "\<C-b>"
inoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? "\<c-r>=coc#float#scroll(1)\<cr>" : "\<Right>"
inoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? "\<c-r>=coc#float#scroll(0)\<cr>" : "\<Left>"
vnoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? coc#float#scroll(1) : "\<C-f>"
vnoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? coc#float#scroll(0) : "\<C-b>"
endif
总结