博客
关于我
使用 Vim 搭建 C/C++ 开发环境
阅读量:440 次
发布时间:2019-03-06

本文共 8927 字,大约阅读时间需要 29 分钟。

Vim 开发环境搭建指南

刚接触 Vim 的同学往往因为无法搭建开发环境而“从入门到放弃”,本文旨在帮助这些同学搭建开发环境,聚焦于最核心的开发需求,忽略配色调字体等细节。如果需要开箱即用的 Vim 配置,可以参考相关文档。

插件管理

在 Vim 中,插件只是一些脚本,存放在特定的目录中,运行时将它们所在的目录加入到 runtimepath 中。Vim 8 内置了插件管理功能,但不支持高级管理功能。Vimmers 实现了多个插件管理器,可以自动下载、更新、安装插件,还可以延迟加载、按需加载,提高启动速度。

上古时期流行手动或使用插件管理器,但这些方式已经落伍。以下是目前比较流行的三个插件管理器:

  • vim-plug:简单易用高效,是目前最流行的插件管理器。
  • dein.vim:功能强大,但使用复杂。
  • Other插件管理器:没有用过不做评价。
  • 以上三款插件管理器风格各不相同,都有大量用户,功能相当完善,根据自己的喜好选取即可。推荐新手选择 vim-plug,启动时间特别敏感的同学可以考虑 dein.vim。我的配置在安装 70 余个插件的情况下,启动仅需 60 余秒。

    使用 vim-plug 安装插件

    .vimrc 中写入以下代码:

    call plug#begin('~/.vim/plugged') " 插件目录Plug 'junegunn/vim-easy-align'  " 用户名/插件名,默认从 github 下载安装Plug 'https://github.com/junegunn/vim-github-dashboard.git' " 从特定 URL 下载安装call plug#end()

    使用 dein.vim 安装插件

    .vimrc 中写入以下代码:

    set runtimepath+=~/.vim/plugged/repos/github.com/Shougo/dein.vim " 将 dein.vim 添加到 runtimepathif dein#load_state('~/.vim/plugged') " 参数是插件目录  call dein#begin(general#plugin_dir)  call dein#add('~/.vim/plugged/repos/github.com/Shougo/dein.vim') " 安装 dein.vim  call dein#add('junegunn/vim-easy-align') " 用户名/插件名,默认从 github 下载安装  call dein#end()  call dein#save_state()endifif dein#check_install() " 自动安装未安装的插件  call dein#install()endif

    在安装插件的代码后,加上这两行设置:

    filetype plugin indent onsyntax on

    这样可以确保特定于文件类型的插件正常工作。

    代码补全

    最简单的代码补全方式是利用生成 tag 文件,补全插件解析 tag 文件进行补全,这种方式有以下两个好处:

  • 最小依赖
  • 高效可靠,适用于任何规模的项目
  • 基于 tag 的补全不够智能,后来又诞生了 life-changer 级别的补全插件,可以提供 IDE 级别的代码补全。但 YouCompleteMe 不是开箱即用的,需要下载依赖并编译,并且耦合度比较大,只支持特定语言(主要是 C++)。

    目前体验补全体验最好的方式是基于 LSP(Language Server protocol)的方案。LSP 是一套通信协议,遵从 LSP 规范的客户端(各种编辑器/IDE)可以通过众多 LSP 服务端按协议标准进行通信,由客户端完成用户界面相关的事情,由服务端提编程语言相关的:补全,定义引用查找,诊断,帮助文档,重构等服务。LSP 的架构图如下:

    有了 LSP,不同的 IDE/编辑器只需要实现 LSP 客户端,专心改进用户体验,所有补全的工作都交给 LSP 服务器。使用基于 LSP 的方案,用户可以在多种语言间无缝切换,让 Vim 支持所有语言(只要有 LSP 实现),用户只需要做以下两件事:

  • 选择 LSP 客户端
  • 选择 LSP 服务器端
  • 目前 LSP 客户端有以下几种选择:

  • neovim-night 内置的 LSP 客户端,使用 Lua 语言配置,参考文档。
  • YouCompleteMe 也提供了对 LSP 的支持。
  • coc.nvim 使用 Typescript 开发,是目前最流行、最强大的 LSP 客户端,已经发展成了一个 Vim 插件平台,存在大量基于 coc.nvim 开发的插件(coc 拓展),推荐大家使用 coc.nvim。
  • coc.nvim 依赖于 node.js,但 node.js 已经不再支持 32 位机,因此最新的 coc.nvim 很可能无法在 32 位机上运行,请考虑其他几种方案。

    错误检查

    目前错误检查有两种方案:

  • 定时调用外部程序实现实时错误检测
  • 使用 LSP
  • 如果使用 coc.nvim,不需要额外配置,开箱即用。coc.nvim 使用 LSP 进行错误检查,不够灵活,无法使用 linter 实时检测代码。

    基于外部程序的方案非常灵活,比如,可以在基础的错误检测之外同时使用 clang-tidy 等工具进行检测。目前这种方案最好的插件是 ,并且 ale 可以与 coc.nvim 共存,用 ale 做实时错误检查,coc.nvim 做补全。如果没有特殊需求,直接使用 coc.nvim 即可。

    符号索引

    LSP 已经提供了符号索引的功能,可以方便地跳转到定义/引用。通常 LSP 的功能已经够用,但 LSP 存在以下缺点:

  • 仅支持单一语言,在多语言项目中无法工作
  • LSP 的符号索引功能有限
  • 比如可能存在汇编和 C 混合的项目,汇编定义了一个变量在 C 中读写,LSP 无法理解汇编,找不到变量定义的地方。

    我们可以使用静态代码索引工具,克服 LSP 的以上缺点。目前静态代码索引最好的方案是 ctags 和 混合使用,具体的方法参考韦应笑的深度文章,不再赘述。

    任务系统

    在古老的 Vim 工作流中,项目的构建一直是个老大难的问题,要么手动完成,要么自己写简单的脚本完成,VSCode 引入任务系统解决了这个问题,韦易笑大佬的和 又将 VSCode 的任务系统引入到了 Vim 中,彻底改变了 Vim 的工作流。这充分体现了 Vim 的优势,Vim 用户非常乐于吸收别的编辑器的优点,让 Vim 变得更好。

    asyncrun.vim 让用户可以异步运行 shell 命令,asynctasks 让用户可以将常用的命令写入到配置文件中(~/.vim/tasks.ini 或项目根目录中的 tasks.ini),一次编写多次使用。详细的使用方法请参考插件的中文文档。基本配置如下:

    " 将终端放到 tab 中let g:asynctasks_term_pos = 'tab'" 设置 quickfix 大小let g:asyncrun_open = 10" 设置项目根目录标志" 实际上,许多插件都使用这种方法定位根目录,因此可以定一个变量 g:rootmarks,let g:asyncrun_rootmarks = ['.compile_commands.json', '.ccls', '.git']

    以构建 CMake 项目为例,我需要以不同的模式(Debug/Release)执行 CMake,编译项目,可能还会删除二进制目录,利用这两个 life-changer 级别的插件,可以实现一键配置、编译、运行、清理目录。

    语法高亮

    基于正则表达式的语法高亮在 C++ 这种语法非常复杂的语言上表现的很差,2021 年可以彻底抛弃掉这种老掉牙的高亮方案了。请使用 nvim-treesitter 是 neovim 移植的 treesitter(是的,从别的编辑器超过来的),基于语义高亮代码,性能强,容错好。

    配置代码如下:

    lua" Tips:您可以在 vimrc 中进行判断,在 Vim 中使用 vim-lsp-cxx-highlight,在 neovim-nightly 中使用 nvim-treesitter,可以参考我配置中的 init.vim 和 autoload/tools.vim。

    文件操作

    许多 Vim 外的编辑器用户喜欢使用文件树定位项目文件,但 Vimmer 更喜欢使用模糊查找插件定位文件。尽管如此,文件树也并非一无用处,在浏览自己不熟悉的项目时,文件树插件可以帮助我们了解项目结构。

    NERDtree 虽然是最经典的文件树插件,但在许多介绍 Vim 的文章中被骂的狗血临头。许多人批评 NERDtree 性能差,在 Linux 这种规模的项目中会直接卡死,但应付中小型项目绰绰有余。

    LeaderF 是国人开发的一款模糊查找插件,性能最强,并且支持许多插件。配置如下:

    let g:Lf_PreviewResult = {    \ 'File': 0,    \ 'Buffer': 0,    \ 'Mru': 0,    \ 'Tag': 1,    \ 'BufTag': 1,    \ 'Function': 1,    \ 'Line': 0,    \ 'Colorscheme': 0,    \ 'Rg': 1,    \ 'Gtags': 1}let g:Lf_PreviewInPopup = 1 " 在 popup 窗口中预览结果let g:Lf_PreviewCode = 1 " 预览代码let g:Lf_RootMarkers = ['.root', 'compile_command.json', '.git'] " 你的根目录标志let g:Lf_WorkingDirectoryMode = 'A' " 设置 LeaderF 工作目录为项目根目录,如果不在项目中,则为当前目录let g:Lf_ShortcutF = "
    f"let g:Lf_ShortcutB = "
    bl"nnoremap
    p :LeaderfFunctionAll
    " 搜索函数nnoremap
    l :LeaderfBufTagAll
    " 搜索缓冲区中的 tagnnnoremap
    d :LeaderfTag
    " 搜索项目中的 tagnnnoremap
    h :LeaderfHelp
    " 搜索 vim helpnnoremap
    rg :Leaderf rg
    " 调用 ripgrep 查找字符串

    现在,只要按下 <div></div> <leader><div></div> f,即使是 Linux 这种级别的项目,也能在一瞬间切换到目标文件。

    既然 LeaderF 的模糊搜索功能如此强大,能不能让 LeaderF 搜索我们定义的 asynctask.vim 任务?答案当然是可以的!以下是如何在 LeaderF 中搜索任务的配置:

    function! s:lf_task_source(...)    let rows = asynctasks#source(&columns * 48 / 100)    let source = []    for row in rows        let name = row[0]        let source += [name . '  ' . row[1] . '  : ' . row[2]]    endfor    return sourceendfunctionfunction! s:lf_task_accept(line, arg)    let pos = stridx(a:line, '<')    if pos < 0        return    endif    let name = strpart(a:line, 0, pos)    let name = substitute(name, '^\s*\(.\{-}\)\s*$', '\1', '')    if name != ''        exec "AsyncTask " . name    endifendfunctionfunction! s:lf_task_digest(line, mode)    let pos = stridx(a:line, '<')    if pos < 0        return [a:line, 0]    endif    let name = strpart(a:line, 0, pos)    return [name, 0]endfunctionlet g:Lf_Extensions = get(g:, 'Lf_Extensions', {})let g:Lf_Extensions.task = {    \ 'source': string(function('s:lf_task_source'))[10:-3],    \ 'accept': string(function('s:lf_task_accept'))[10:-3],    \ 'get_digest': string(function('s:lf_task_digest'))[10:-3],    \ 'highlights_def': {        \     'Lf_hl_funcScope': '^\S\+',        \     'Lf_hl_funcDirname': '^\S\+\s*\zs<.*>\ze\s*:',        \ },    \ }nnoremap 
    T :Leaderf task
    "
    T 模糊搜索任务

    调试

    调试一直是 Vim 的弱点,最近 Debug Adapter Protocol(DAP)的提出带来了一些改变。YouCompleteMe 的主要开发者 puremourning 创建了 dap.vim,这是目前最强的 Vim 调试插件,仍处于开发阶段,您如果有兴趣的话可以参考我的博客。

    Git

    Tpope 的 timetravel.vim 让 Git 工作流在 Vim 中顺畅无比,使用 gitgutter 让 Git 状态在侧边栏展示。

    command! -bang -nargs=* -complete=file Make AsyncRun -program=make @     
    " 异步 git push git-gutterlet g:gitgutter_map_keys = 0nmap ghp
    (GitGutterPreviewHunk) " 预览修改(diff)nmap ghs
    (GitGutterStageHunk) " 暂存修改nmap ghu
    (GitGutterUndoHunk) " 丢弃修改nmap [c
    (GitGutterPrevHunk) " 上一处修改nmap ]c
    (GitGutterNextHunk) " 下一处修改

    格式化

    注释请使用 ally/vim-format,它易于拓展,可以支持所有文件类型。vim-format 会根据文件类型执行对应的格式化命令,C/C++ 默认使用 clang-format,所以您只需要将 .clang-format 放到项目根目录即可。

    定义一个快捷键快速格式化代码:

    nnoremap 
    bf :Autoformat

    注释

    目前最流行的注释/反注释是 nerdcommenter,相比于 vim-commentary 功能更强,拓展性更好,因此推荐使用 nerdcommenter。

    " Add spaces after comment delimiters by defaultlet g:NERDSpaceDelims = 1" Align line-wise comment delimiters both sideslet g:NERDDefaultAlign = 'both'" Allow commenting and inverting empty lines (useful when commenting a region)let g:NERDCommentEmptyLines = 1" Enable trimming of trailing whitespace when uncommentinglet g:NERDTrimTrailingWhitespace = 1" Enable NERDCommenterToggle to check all selected lines is commented or notlet g:NERDToggleCheckAllLines = 1" Useful for comment argumentlet g:NERDAllowAnyVisualDelims = 0" Usefull when comment argumentlet g:NERDAltDelims_asm = 1

    nerdcommenter 默认的快捷键请参考文档。请不要再蜗牛一样地用 :help 命令查看文档,用 <div></div> <leader><div></div> h 模糊搜索!

    转载地址:http://imdyz.baihongyu.com/

    你可能感兴趣的文章
    npm install 卡着不动的解决方法
    查看>>
    npm install 报错 EEXIST File exists 的解决方法
    查看>>
    npm install 报错 ERR_SOCKET_TIMEOUT 的解决方法
    查看>>
    npm install 报错 fatal: unable to connect to github.com 的解决方法
    查看>>
    npm install 报错 no such file or directory 的解决方法
    查看>>
    npm install 权限问题
    查看>>
    npm install报错,证书验证失败unable to get local issuer certificate
    查看>>
    npm install无法生成node_modules的解决方法
    查看>>
    npm install的--save和--save-dev使用说明
    查看>>
    npm node pm2相关问题
    查看>>
    npm run build 失败Compiler server unexpectedly exited with code: null and signal: SIGBUS
    查看>>
    npm run build报Cannot find module错误的解决方法
    查看>>
    npm run build部署到云服务器中的Nginx(图文配置)
    查看>>
    npm run dev 报错PS ‘vite‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。
    查看>>
    npm scripts 使用指南
    查看>>
    npm should be run outside of the node repl, in your normal shell
    查看>>
    npm start运行了什么
    查看>>
    npm WARN deprecated core-js@2.6.12 core-js@<3.3 is no longer maintained and not recommended for usa
    查看>>
    npm 下载依赖慢的解决方案(亲测有效)
    查看>>
    npm 安装依赖过程中报错:Error: Can‘t find Python executable “python“, you can set the PYTHON env variable
    查看>>