将 Vim 打造成轻量级IDE

一直以来对 Vim 的使用都停留在简单使用,最近突然对远程开发有了些兴趣,便想着是不是可以将 Vim 打造成一个 IDE 来使用,找了找相关的资料发现可行,于是便有了以下折腾的记录。

IDE 定义

维基百科 中 IDE 全称 Integrated Development Environment 意为集成开发环境,可以辅助编写源代码文本、并编译打包成为可用的程序。

Vim 作为文本编辑器是足够的,但作为 IDE 则缺乏相应的能力,但好在 Vim 作为一款古老编辑器依然在更新迭代,其插件特性为其带来成为 IDE 的无限可能。

插件安装

插件管理

为了保证插件使用的干净有序,插件管理工具是必要的,目前常用的管理工具是vim-plug,安装步骤如下

1
2
curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

由于国内网络问题,可能会出现 plug.vim 文件下载失败的情况,有条件的同学可以在 github 上将对应文件提前下载下来,之后放在对应目录。

插件选用

有了插件管理工具,该安装什么插件呢?这里推荐使用 coc.nvim,它有如下一些特性

  • 快捷高效,使用 node 做异步处理,提示和响应比较快
  • 扩展性强,拥有插件机制且生态繁荣。是的你没看错,它作为一个插件 内部还能支持插件,套娃了属于是

如何用安装使用它呢?在 coc.nvim 官方有一个安装教程,可以看看,这里粗略介绍下

由于 coc.nvim 使用了node 作为运行和插件机制,所以需要安装配置 node 环境,下载 node 安装包,将 node 程序目录添加至 PATH 中

接着编辑 ~/.vimrc 或者 ~/.vim/vimrc,将 vim-plug 配置插件 coc.nvim ,示例如下

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

保存退出后打开任意一个 Vim 窗口,用命令模式 :PlugInstall 进行插件安装,此时会弹出提示框报告安装进度,待安装完毕后退出即可。

关于vim-plug 的其他命令还有

  • 插件清理 :PlugClean
  • 插件升级 :PlugUpdate

配置定义

安装之后的使用是生涩的不够丝滑,问题在于我们缺乏对 Vim 的调教配置,这方面 coc.nvim 官方很是贴心的为新手用户提供了一份样板配置,同样的编辑 ~/.vimrc 或 ~/.vim/vimrc,在文件后方追加如下官方的样板配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
" Some servers have issues with backup files, see #649.
set nobackup
set nowritebackup

" Having longer updatetime (default is 4000 ms = 4 s) 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 codeAction to the selected region.
" Example: `<leader>aap` for current paragraph
xmap <leader>a <Plug>(coc-codeaction-selected)
nmap <leader>a <Plug>(coc-codeaction-selected)

" Remap keys for applying codeAction to the current buffer.
nmap <leader>ac <Plug>(coc-codeaction)
" Apply AutoFix to problem on the current line.
nmap <leader>qf <Plug>(coc-fix-current)

" 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> for scroll float windows/popupsif has('nvim-0.4.0') || has('patch-8.2.0750')
if has('nvim-0.4.0') || has('patch-8.2.0750')
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

" Use CTRL-S for selections ranges.
" Requires 'textDocument/selectionRange' support of language server.
nmap <silent> <C-s> <Plug>(coc-range-select)
xmap <silent> <C-s> <Plug>(coc-range-select)

" Add `:Format` command to format current buffer.
command! -nargs=0 Format :call CocActionAsync('format')

" Add `:Fold` command to fold current buffer.
command! -nargs=? Fold :call CocAction('fold', <f-args>)

" Add `:OR` command for organize imports of the current buffer.
command! -nargs=0 OR :call CocActionAsync('runCommand', 'editor.action.organizeImport')

" Add (Neo)Vim's native statusline support.
" NOTE: Please see `:h coc-status` for integrations with external plugins that
" provide custom statusline: lightline.vim, vim-airline.
set statusline^=%{coc#status()}%{get(b:,'coc_current_function','')}

" 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>

即可实现自动弹窗选中以及一些快捷键的绑定,配置比较长虽然有注释但可能有些人看不太懂其中的配置含义,这里挑几个典型简单介绍下

1
nmap <silent> gd <Plug>(coc-definition) 

此配置意为在普通模式下输入 gd 可以跳转到定义部分

1
2
" Add `:Format` command to format current buffer
command! -nargs=0 Format :call CocActionAsync('format')

此配置含义意为在命令模式中 :Format,执行后会触发 Coc 异步的格式化任务

1
2
" Symbol renaming.
nmap <leader>rn <Plug>(coc-rename)

此配置意为普通模式下输入\rn 重命名符号

关于 Vim 的配置部分可以再参考参考官方文档进行深入的学习,这里不做过多的赘述。

插件的插件

前面讲了 coc.nvim 自己同样支持插件,这其中不乏一些优秀的插件,其插件的配置和列表可以插件支持中找到。这里列举下用的一些插件,如

  • coc-snippets 其包含大量常用的提示片段,弹窗使用舒适
  • cos-git 提示文件在git中的版本管理状态
  • coc-explorer 文件管理器,可以在侧边栏展示文件,也可以搜索管理文件
  • coc-sh 编写 shell 脚本提示舒适
  • coc-json 编写 json 脚本提示舒适
  • coc-go 基于 gopls 的提示,用来开发 go,也是我捣鼓的主要目的

关于 coc.nvim 插件有如下命令,在 Vim 命令模式下

  • 插件安装 :CocInstall coc-json
  • 插件升级 :CocUpdate
  • 插件卸载:CocUninstall coc-json

写在最后

其实在18年的时候写过一篇关于Vim的文章,现在回头看显得无比稚嫩,那个时候仅仅掌握了对 Vim 的基本使用命令,只算是踏入了一只脚在 Vim 中,如今结合插件和自定义的配置才算是真正的打开了 Vim 的大门,希望这篇文章能帮助到你。