Administrator
发布于 2024-03-31 / 94 阅读
0
0

nvim + coc + clangd 开发配置

背景

这里介绍一个C++ nvim编辑器的入门配置方案,个人感觉使用效率上还是比clion和vscode有的一拼的。

这里总体使用nvim + coc + clangd的方案

nvim在lsp的集成和社区活跃度上面会更好一点,我们实现编辑器的功能能更容易的找到通用的解决方案和插件的选择也相对更方便。

clangd 是一个C++的lsp服务,能提供代码解析、代码提示、跳转、引用查找、补全、宏展开的功能,这里使用clangd

ccls则是一个C++的另一个lsp服务,相比clangd,ccls会通过compile db文件对项目做完整的扫描

coc是vim上面的一个插件,但是其提供ccls插件集成、其他语言lsp插件集成、snippet插件的集成,提供一个统一的代码、文本补全的入口,可以大幅度的提供我们开发的效率和体验

除了coc + clnagd之外,还有一个常见的方案是使用nvim-lspconfig + lsp(clangd/ccls/youcompleme),nvim-lspconfig 是nvim专门提供的官方的lsp插件,更加轻量化,有很多开发者也在转向nvim-lspconfig,在补全、跳转、查找这些功能上面和coc基本一致,但是coc能够更方便的集成其他的补全插件,这里在功能上基本拉不开差距的场景下,我个人还是更倾向于使用coc

nvim的使用

首先,在实行这套方案之前,个人还是十分建议对vim的使用有一定的了解,这里可以参考这份清单,上面罗列了最常用的部分vim的命令和使用,这里其实也不用全都掌握,了解一些最最常见的用法也能把开发中90%的场景的效率拉高
https://vim.rtorr.com/lang/zh_cn

nvim的配置文件

nvim提供了两种配置文件的方式,而且可以混用,一种是vim原生支持的viml,一种则是nvim专门支持的lua脚本,viml的语法和配置更简单,尤其在一些命令快捷键的配置,但是nvim很多的插件都需要lua脚本去配置,所以这里主要使用viml脚本,同时混用一部分lua脚本。
这里先设置一些常用的配置

配置文件的位置

如果使用的是mac / linux,nvim的配置文件在下面的这个目录,如果没有直接手动创建一个即可

touch ~/.config/nvim/init.vim

常用的配置

配置init.vim问家增加一些配置

set ignorecase " 设置 ignorecase:使搜索不区分大小写。 
set hlsearch " 高亮搜索结果。
set incsearch " 增量搜索:当输入时逐步显示搜索匹配项。
set tabstop=2 " 设置制表符宽度为 2 列。
set softtabstop=2 "
Softtabstop:将多个空格视为制表符停止位,以便正确处理退格。
set expandtab " 将制表符扩展为空格。
set shiftwidth=2 " 设置自动缩进的缩进宽度为 2 列。 
set autoindent " 自动缩进:新行的缩进与上一行相同。 
set number " 显示行号。 set wildmode=full " 配置 wildmenu 补全行为。
set cc=120 " 为了编码风格,将文本宽度设置为 120 列。 
set mouse=a " 启用鼠标支持(可点击区域)。
set cursorline " 高亮当前光标所在行。 

leader 键配置

nvim有个特殊的键叫leader键,主要用于一些快捷键配置的时候提供一个前缀,虽然没有leader键也能实现大部分的快捷键的配置,但是基于leader键位可以更好的组合一些快捷键位的功能
这里设置leader键为空格

let g:mapleader = " "

常用的快捷键映射

在 Neovim 中,可以使用不同的命令来创建三种不同模式下的非递归按键映射:

  • 在 Normal 模式下使用 nnoremap 命令。
  • 在 Visual 模式下使用 xnoremap 命令。
  • 在 Insert 模式下使用 inoremap 命令。
nnoremap <leader>h ^
" Normal 模式下,将 leader + h 映射为将光标移动到当前行的行首
nnoremap l $
" bNbormal 模式下,将 l 映射为将光标移动到当前行的行尾
nnoremap <leader>b %
" Normal 模式下,将 leader + b 映射为将光标移动到当前行的匹配括号的对应位置
xmap <leader>h ^
" Visual 模式下,将 leader + h 映射为将选中文本的光标移动到第一个字符
xmap <leader>l $
" Visual 模式下,将 leader + l 映射为将选中文本的光标移动到最后一个字符
xmap <leader>b %
" Visual 模式下,将 leader + b 映射为将选中文本的光标移动到匹配括号的对应位
nnoremap <leader>pn :bNext
" Normal 模式下,将 leader + b + n 映射为切换到下一个缓冲区                   
nnoremap <leader>pp :bprevious 
" Normal 模式下,将 leader + b + p 映射为切换到上一个缓冲区

一些常用的命令快捷配置

coc + clangd插件配置

和vscode类似,插件可以极大的提供开发的体验和效率,这里vim的插件生态也是十分的强大,因此我们之类很多常见都需要安装插件来实现。

vim-plug配置

为了方便集成vim的插件,我们首先安装一个插件管理的工具,这里大部分的插件管理工具的语法配置都差不多,可以用vim-plug也可以用vundle
vim-plug 的github链接: https://github.com/junegunn/vim-plug
这里根据vim-plug的README安装即可

unix/linux 下面Neovim的安装:
在shell中执行

sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
       https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'

简单安装一个主题做一下验证
注意:下面的主题依赖24-bit 色彩的终端,mac自带的终端是不支持的,配置主题后在terminal中打开会存在问题,建议使用tabby/iterm2或者其他支持24-bit的终端
配置init.vim

call plug#begin()                           
 Plug 'folke/tokyonight.nvim'                       
call plug#end()

在vim中命令执行:

:PlugInstall

在init.vim中配置主题

colorscheme tokyonight

Peek 2024-04-01 00-18

coc + clangd配置

clangd 安装

  • arch linux 安装,直接安装clang 即可
    sudo pacman -S clang
    
  • 其他发行版,可以自行查询有无clangd的包
  • 通过github release 安装
    https://github.com/clangd/clangd/releases
    直接下载github的clangd二进制文件,ln 到/usr/bin即可
  • 如果glibc 版本比较低,且无法随意升级glibc的话,可能无法运行github下载的clangd 二进制文件,这时候需要手动编译一份了

安装好之后测试一下是否OK

$ clangd 
clangd is a language server that provides IDE-like features to editors
It should be used via an editor plugin rather than invoked directly. For more information, see:
        https://clangd.llvm.org/
        https://microsoft.github.io/language-server-protocol/
clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable.
I[00:06:07.966] clangd version 17.0.6
I[00:06:07.966] Features: linux
I[00:06:07.966] PID: 3040
I[00:06:07.966] Working directory: /home/prh/workspace/leetcode
I[00:06:07.966] argv[0]: clangd
I[00:06:07.967] Starting LSP over stdin/stdout

coc 安装

安装coc,修改init.vim增加插件的配置

call plug#begin()                           
 Plug 'folke/tokyonight.nvim'
 Plug 'neoclide/coc.nvim', {'branch': 'release'} 
call plug#end()

配置修改好之后,保存退出,执行 :PlugInstall 安装COC

clangd 插件安装

安装好coc之后,在vim中执行 :CocInstall coc-clangd 安装coc的clangd插件
在nvim中执行 :CocConfig 会打开coc的配置文件,配置一些clangd的参数
这里的配置可以参考:https://github.com/clangd/coc-clangd
其中 :CocInstall coc-clangd 的配置就是calngd的配置,可以通过在shell中执行 clangd --help 查看,这里配置一些常见的参数
--background-index :配置后台构建索引
--clang-tidy:开启clang-tiry
-j=32 :后台异步解析的工作数(可以根据机器配置自行决定)

{
  "clangd.enabled": true,
  "clangd.path": "/usr/bin/clangd",
  "clangd.arguments": [
    "--background-index",
    "--clang-tidy",
    "-j=32"
  ]
} 
 clangd --help  
OVERVIEW: clangd is a language server that provides IDE-like features to editors.

It should be used via an editor plugin rather than invoked directly. For more information, see:
        https://clangd.llvm.org/
        https://microsoft.github.io/language-server-protocol/

clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable.

USAGE: clangd [options]

OPTIONS:

Generic Options:

  --help                              - Display available options (--help-hidden for more)
  --help-list                         - Display list of available options (--help-list-hidden for more)
  --version                           - Display the version of this program

clangd compilation flags options:

  --compile-commands-dir=<string>     - Specify a path to look for compile_commands.json. If path is invalid, clangd will look in the current directory and parent paths of each source file
  --query-driver=<string>             - Comma separated list of globs for white-listing gcc-compatible drivers that are safe to execute. Drivers matching any of these globs will be used to extract system includes. e.g. /usr/bin/**/clang-*,/path/to/repo/**/g++-*

.......

coc键位配置

coc 和 clangd 配置好之后我们配置一些coc补全键位的映射
https://github.com/neoclide/coc.nvim
在init.nvim添加下面的内容,下面就是coc默认的键位,建议还是加到配置文件中,看看和其他键位有没有冲突

" https://raw.githubusercontent.com/neoclide/coc.nvim/master/doc/coc-example-config.vim

" May need for Vim (not Neovim) since coc.nvim calculates byte offset by count
" utf-8 byte sequence
set encoding=utf-8
" Some servers have issues with backup files, see #649
set nobackup
set nowritebackup

" Having longer updatetime (default is 4000 ms = 4s) leads to noticeable
" delays and poor user experience
set updatetime=300

" Always show the signcolumn, otherwise it would shift the text each time
" diagnostics appear/become resolved
set signcolumn=yes

" Use tab for trigger completion with characters ahead and navigate
" NOTE: There's always complete item selected by default, you may want to enable
" no select by `"suggest.noselect": true` in your configuration file
" NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
" other plugin before putting this into your config
inoremap <silent><expr> <TAB>
      \ coc#pum#visible() ? coc#pum#next(1) :
      \ CheckBackspace() ? "\<Tab>" :
      \ coc#refresh()
inoremap <expr><S-TAB> coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>"
" Make <CR> to accept selected completion item or notify coc.nvim to format
" <C-g>u breaks current undo, please make your own choice
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm()
                              \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
function! CheckBackspace() abort
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~# '\s'
endfunction

" Use <c-space> to trigger completion
if has('nvim')
  inoremap <silent><expr> <c-space> coc#refresh()
else
  inoremap <silent><expr> <c-@> coc#refresh()
endif

" Use `[g` and `]g` to navigate diagnostics
" Use `:CocDiagnostics` to get all diagnostics of current buffer in location list
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)
" GoTo code navigation
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)
" Use K to show documentation in preview window
nnoremap <silent> K :call ShowDocumentation()<CR>
function! ShowDocumentation()
  if CocAction('hasProvider', 'hover')
    call CocActionAsync('doHover')
  else
    call feedkeys('K', 'in')
  endif
endfunction

" Highlight the symbol and its references when holding the cursor
autocmd CursorHold * silent call CocActionAsync('highlight')

" Symbol renaming
nmap <leader>rn <Plug>(coc-rename)
" Formatting selected code
xmap <leader>f  <Plug>(coc-format-selected)
nmap <leader>f  <Plug>(coc-format-selected)
augroup mygroup
  autocmd!
  " Setup formatexpr specified filetype(s)
  autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected')
  " Update signature help on jump placeholder
  autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')
augroup end

" Applying code actions to the selected code block
" Example: `<leader>aap` for current paragraph
xmap <leader>a  <Plug>(coc-codeaction-selected)
nmap <leader>a  <Plug>(coc-codeaction-selected)
" Remap keys for applying code actions at the cursor position
nmap <leader>ac  <Plug>(coc-codeaction-cursor)
" Remap keys for apply code actions affect whole buffer
nmap <leader>as  <Plug>(coc-codeaction-source)
" Apply the most preferred quickfix action to fix diagnostic on the current line
nmap <leader>qf  <Plug>(coc-fix-current)
" Remap keys for applying refactor code actions
nmap <silent> <leader>re <Plug>(coc-codeaction-refactor)
xmap <silent> <leader>r  <Plug>(coc-codeaction-refactor-selected)
nmap <silent> <leader>r  <Plug>(coc-codeaction-refactor-selected)
" Run the Code Lens action on the current line
nmap <leader>cl  <Plug>(coc-codelens-action)
" Map function and class text objects
" NOTE: Requires 'textDocument.documentSymbol' support from the language server
xmap if <Plug>(coc-funcobj-i)
omap if <Plug>(coc-funcobj-i)
xmap af <Plug>(coc-funcobj-a)
omap af <Plug>(coc-funcobj-a)
xmap ic <Plug>(coc-classobj-i)
omap ic <Plug>(coc-classobj-i)
xmap ac <Plug>(coc-classobj-a)
omap ac <Plug>(coc-classobj-a)
" Remap <C-f> and <C-b> to 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

" Mappings for CoCList
" Show all diagnostics
nnoremap <silent><nowait> <space>a  :<C-u>CocList diagnostics<cr>
" Manage extensions
nnoremap <silent><nowait> <space>e  :<C-u>CocList extensions<cr>
" Show commands
nnoremap <silent><nowait> <space>c  :<C-u>CocList commands<cr>
" Find symbol of current document
nnoremap <silent><nowait> <space>o  :<C-u>CocList outline<cr>
" Search workspace symbols
nnoremap <silent><nowait> <space>s  :<C-u>CocList -I symbols<cr>
" Do default action for next item
nnoremap <silent><nowait> <space>j  :<C-u>CocNext<CR>
" Do default action for previous item
nnoremap <silent><nowait> <space>k  :<C-u>CocPrev<CR>
" Resume latest coc list
nnoremap <silent><nowait> <space>p  :<C-u>CocListResume<CR>

上面的配置文件有点长,但是只需要记住最需要用的按键

" 查找函数/类定义
nnoremap <silent><nowait> <space>s  :<C-u>CocList -I symbols<cr>
"  `[g` / `]g` 跳转到前一个/后一个 代码问题点 
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)
" gd 跳转到当前函数/变量的定义位置
nmap <silent> gd <Plug>(coc-definition)
" gd 跳转到当前的类型定义
nmap <silent> gy <Plug>(coc-type-definition)
" 查看当前接口定义子类 / 虚函数 的实现
nmap <silent> gi <Plug>(coc-implementation)
" 查看当前变量函数的引用位置
nmap <silent> gr <Plug>(coc-references)
" 查看当前函数的定义或者注释
nnoremap <silent> K :call ShowDocumentation()<CR>
" TAB
选择补全的内容

compile_commands.json 生成

clangd或者ccls的lsp服务在处理比较大型的项目时都依赖compile_database.json文件,这里可以用bearcompiledbcmake来生成这个文件

https://github.com/rizsotto/Bear
https://github.com/nickdiego/compiledb
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ../

这里使用compiledb,只需要在项目编译好之后执行compile_database.json的生成命令即可,将生成的compile_commands.json放到项目的根目录,如果已经是在根目录生成的,compile_commands.json应该已经在根目录了

make
compiledb make

Peek 2024-04-01 00-112

coc 代码片段插件

有时候我们写一些常用的代码比如for 循环,main函数等等代码片段往往是重复的,我们可以使用一些代码片段的插件来实现
https://github.com/SirVer/ultisnips

call plug#begin()
 Plug 'folke/tokyonight.nvim'
 Plug 'neoclide/coc.nvim', {'branch' : 'release'}
 Plug 'SirVer/ultisnips'
 Plug 'honza/vim-snippets'
call plug#end()

执行完 :PlugInstall 之后,再安装一个coc-snippets的插件 :CocInstall coc-snippets
https://github.com/neoclide/coc-snippets

同时为了避免ultisnips原有的tab键位补全影响我们上面coc配置的tab选择补全项,这里配置ultisnips的tab键位补全为其他键位,修改init.nvim插件

let g:UltiSnipsExpandTrigger="<Nop>"
let g:UltiSnipsJumpForwardTrigger="<Nop>"
let g:UltiSnipsJumpBackwardTrigger="<Nop>"

coc + clangd 效果演示

Peek 2024-04-01 02-48

其他插件

这里列举我使用的一部分常见的插件,感兴趣的朋友可以直接通过对应的github链接查看

call plug#begin()
" 主题dracula
 Plug 'dracula/vim'
" 主题tokyonight
 Plug 'folke/tokyonight.nvim'
" 主题 one
 Plug 'rakr/vim-one'

" 目录树以及图标
 Plug 'nvim-tree/nvim-tree.lua'
 Plug 'nvim-tree/nvim-web-devicons'
" 下方导航栏以及对应主题
 Plug 'vim-airline/vim-airline'
 Plug 'vim-airline/vim-airline-themes'

" 全局搜索以及相关插件
 Plug 'nvim-lua/popup.nvim'
 Plug 'nvim-lua/plenary.nvim'
 Plug 'nvim-telescope/telescope.nvim', {'do': ':UpdateRemotePlugins'}

" coc
 Plug 'neoclide/coc.nvim', {'branch': 'release'}

" 启动屏幕
 Plug 'mhinz/vim-startify'

" 括号匹配
 Plug 'jiangmiao/auto-pairs'

" 快速注释
 Plug 'preservim/nerdcommenter'

" 导航栏美化
 Plug 'akinsho/bufferline.nvim'

" 快速打开终端
 Plug 'akinsho/toggleterm.nvim', {'tag' : '*'}

" 不同颜色的括号
 Plug 'luochen1990/rainbow'

" git相关功能
 Plug 'lewis6991/gitsigns.nvim'

" 查看代码改动
 Plug 'sindrets/diffview.nvim'

" 代码结构查看
 Plug 'liuchengxu/vista.vim'   " overview for code

" 代码缩进导航线
 Plug 'lukas-reineke/indent-blankline.nvim' " indent line

" 代码片段补全
 Plug 'SirVer/ultisnips'
 Plug 'honza/vim-snippets'

call plug#end()

评论