沧歌


起风了,唯有努力生存


Vim 配置v3 - Python开发、LSP、FZF

写在前面

距离上一篇VIM的Blog并没有过去多久,主要因为现在的主力深度学习机器限制不能用vscode登录,一怒之下升级了当前的vim配置,用来代替vscode。一些基础知识不再重复,建议先看看之前的vim-Blog-v1vim-Blog-v2

更新的原因主要是vim 8.2支持了异步操作,极大的提高了vim的速度。这篇配置也是适配vim8.2甚至vim9了,为了直接在服务器上写python代码,我在补全上加了LSP支持,此外添加了一些别的辅助插件,一定程度上可以代替vscode工作了。

我在下面的配置算是比较简单轻便,在vim8.2+下测试可用,供大家参考。相比使用nodejs的neovim,这份配置不需要nodejs配置就可以进行python开发。并且也不用再编译老旧的YouCompleteMe了,而是采用全新的LSP协议。现代程度上已经可以替代vscode大部分工作。

下面的配置还是基于vim-nox这个版本的vim。使用命令是sudo apt install vim-nox。这个版本是一个大而全的非GUI版本。很多服务器都是安装好的。

配置方法

插件airline和vim-fern需要一些特殊符号,所以建议安装针对性修改的字体,建议一步到位nerdfont。可以在Google搜索 “字体名+Nerd Font Mono” 查找对应的字体。比如Consolas这个字体,有好心人已经提供了打包好的版本(https://github.com/wclr/my-nerd-fonts.git)。我个人喜欢 Hack Font,这个字体和很多其他开源字体都可以从这里下载https://www.nerdfonts.com/font-downloads。

配置方法很简单,首先在$HOME目录下新建一个文件.vimrc。将下面的内容全部复制进去。 这个配置中我加了很多注释,方便大家学习修改。

" 基础设置
set nocompatible              " 必须,关闭 Vi 兼容模式
filetype off                  " 必须,插件管理前关闭文件类型检测

" ==============================================================================
" vim-plug 插件管理器
" ==============================================================================
" 安装方法:
"   curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
" 常用命令:
"   :PlugStatus    - 查看已安装插件
"   :PlugInstall   - 安装插件
"   :PlugUpdate    - 更新插件
"   :PlugClean     - 删除插件(把安装插件对应行删除,然后执行这个命令即可)
"   :h vim-plug    - 获取帮助

call plug#begin('~/.vim/plugged')
    " 安装插件写在这之后, 使用 GitHub 用户名/仓库名

    " 状态栏美化
    Plug 'vim-airline/vim-airline'

    " 配色方案
    Plug 'altercation/vim-colors-solarized'
    Plug 'tomasr/molokai'

    " vim-fern文件树(nerdtree的替代品,更快)
    Plug 'lambdalisue/vim-fern'                   " 核心插件
    Plug 'lambdalisue/vim-fern-git-status'        " 显示git状态
    Plug 'lambdalisue/vim-nerdfont'               " 和下面两个插件配合,显示图标,并配置色彩
    Plug 'lambdalisue/vim-fern-renderer-nerdfont'
    Plug 'lambdalisue/vim-glyph-palette'
    Plug 'hellolzc/vim-fern-symlink'              " 用 -> 表示符号链接

    " 中文帮助文档
    Plug 'yianwillis/vimcdoc'

    " 快捷键提示
    Plug 'liuchengxu/vim-which-key'

    " CSV 文件高亮
    Plug 'mechatroner/rainbow_csv'

    " LSP 与补全
    Plug 'prabirshrestha/vim-lsp'
    Plug 'prabirshrestha/asyncomplete.vim'
    Plug 'prabirshrestha/asyncomplete-file.vim'    " 补全数据源:文件路径
    Plug 'prabirshrestha/asyncomplete-buffer.vim'  " 补全数据源:当前 Buffer 单词补全
    Plug 'prabirshrestha/asyncomplete-lsp.vim'     " 补全数据源:LSP补全
    " 安装Python LSP服务端命令:pip install python-lsp-server[all]

    " 优雅关闭 Buffer
    Plug 'moll/vim-bbye'

    " fzf 搜索插件, 配合ripgrep实现类似vscode的搜索效果
    Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
    Plug 'junegunn/fzf.vim'

call plug#end()

filetype plugin on            " 重新开启文件类型检测,并加载插件相关设置
let g:plug_threads = 2       " 限制最大并行下载线程数,应对国内网络不佳

" ==============================================================================
" 界面与显示设置
" ==============================================================================
set number                    " 显示行号
syntax on                     " 代码高亮
set mouse=a                   " 启用鼠标
set ttymouse=sgr              " 设置鼠标模式,有的终端需要
set cursorline                " 高亮当前行
" set cursorcolumn            " 高亮当前列(可选)
set laststatus=2              " 永远显示状态栏
set t_Co=256                  " 支持 256 色(XShell 等终端)
set background=dark

" 缩进设置
set tabstop=4                 " Tab 显示宽度
set expandtab                 " 使用空格替代制表符
set softtabstop=4             " 退格键一次删除的缩进空格数
set shiftwidth=4              " 自动缩进宽度

" 搜索与显示
set incsearch                 " 增量搜索
set hlsearch                  " 高亮所有搜索结果
set listchars=eol:¬,tab:>-,trail:~,extends:>,precedes:<
set list                      " 显示不可见字符

" ==============================================================================
" 配色主题
" ==============================================================================
colorscheme molokai
let g:molokai_original = 1    " molokai 原始背景色

" colorscheme desert

" 若使用 solarized 主题,可取消注释以下行
" colorscheme solarized
" let g:solarized_termtrans = 1
" let g:solarized_contrast = "normal"
" let g:solarized_visibility = "normal"



" 调整不可见字符的颜色(使其不那么显眼)
" 非可见字符 eol extends precedes 是由 NonText 高亮组来控制显示颜色的
hi NonText ctermfg=235
" nbsp tab trail 是由 SpecialKey 高亮组来定义颜色的
hi SpecialKey ctermfg=235

" ==============================================================================
" vim-airline 配置
" ==============================================================================
" 打开tabline功能,方便查看Buffer和切换,这个功能比较不错"
" 我还省去了minibufexpl插件,因为我习惯在1个Tab下用多个buffer"
let g:airline#extensions#tabline#enabled = 1        " 启用 tabline
let g:airline#extensions#tabline#buffer_nr_show = 1 " 显示 buffer 编号
let g:airline_powerline_fonts = 1                 " 若使用 Powerline/NerdFont 字体则取消注释

" ==============================================================================
" vim-fern 文件树配置
" ==============================================================================
" 进入 Fern 窗口时禁用行号和相对行号, 去掉fern左侧标记列和折叠列
augroup fern_settings
    autocmd!
    autocmd FileType fern setlocal nonumber norelativenumber
    autocmd FileType fern call s:init_fern()
augroup END

function! s:init_fern() abort
    " 双击左键:文件则打开,折叠文件夹则展开,已展开文件夹则收起
    nmap <buffer><expr> <2-LeftMouse> fern#smart#leaf(
        \ "\<Plug>(fern-action-open)",
        \ "\<Plug>(fern-action-expand)",
        \ "\<Plug>(fern-action-collapse)",
    \ )
    " 在fern窗口中按 H 键切换隐藏文件的显示/隐藏
    nmap <buffer> H <Plug>(fern-action-hidden:toggle)
endfunction

" 默认显示隐藏文件 (1为显示, 0为隐藏)
let g:fern#default_hidden = 1

" 启用 glyph-palette(图标支持, 需要NerdFont); 未安装时跳过避免 E117
if exists('*glyph_palette#apply')
    augroup my_glyph_palette
        autocmd! *
        autocmd FileType fern call glyph_palette#apply()
        autocmd FileType fall-list call glyph_palette#apply()
        autocmd FileType nerdtree,startify call glyph_palette#apply()
    augroup END
endif
let g:fern#renderer = "nerdfont"    " 若使用 NerdFont 渲染则取消注释

" fern-symlink: 符号链接装饰配置(见 :help fern-symlink)
" let g:fern_symlink#arrow = ' ➜ '              " Unicode 箭头(默认 ' -> ')
" let g:fern_symlink#target_modifier = ':.'     " 相对 cwd 显示目标
" highlight FernSymlinkTarget ctermfg=51 cterm=italic

" ==============================================================================
" 功能键映射 (F1 - F6)
" ==============================================================================
" 禁用 F1 帮助
" can type :help on my own, thanks.  Protect your fat fingers from the evils of <F1>
nnoremap <F1> <Esc>

" F2: 切换文件树
nnoremap <F2> :Fern . -drawer -toggle<CR>

" F3: 切换显示行号与不可见字符
function! s:toggle_number_info()
    set list!
    set number!
endfunction
nnoremap <F3> :call <SID>toggle_number_info()<CR>

" F4: 切换自动换行(保留物理行移动)
function! s:toggle_wrap()
    if &wrap
        echo "Wrap OFF"
        setlocal nowrap
        set virtualedit=all
        silent! nunmap <buffer> <Up>
        silent! nunmap <buffer> <Down>
        silent! nunmap <buffer> <Home>
        silent! nunmap <buffer> <End>
        silent! iunmap <buffer> <Up>
        silent! iunmap <buffer> <Down>
        silent! iunmap <buffer> <Home>
        silent! iunmap <buffer> <End>
    else
        echo "Wrap ON"
        setlocal wrap linebreak nolist
        set virtualedit=
        setlocal display+=lastline
        noremap  <buffer> <silent> <Up>   gk
        noremap  <buffer> <silent> <Down> gj
        noremap  <buffer> <silent> <Home> g<Home>
        noremap  <buffer> <silent> <End>  g<End>
        inoremap <buffer> <silent> <Up>   <C-o>gk
        inoremap <buffer> <silent> <Down> <C-o>gj
        inoremap <buffer> <silent> <Home> <C-o>g<Home>
        inoremap <buffer> <silent> <End>  <C-o>g<End>
    endif
endfunction
nnoremap <F4> :call <SID>toggle_wrap()<CR>

" F5: 粘贴模式开关(适用于粘贴带格式的代码)
set pastetoggle=<F5>
augroup paste_mode
    autocmd!
    autocmd InsertLeave * set nopaste
augroup END

" 自动检测终端粘贴,xterm 等支持括号粘贴模式 (Bracketed Paste Mode)
function! XTermPasteBegin()
    set pastetoggle=<Esc>[201~
    set paste
    return ""
endfunction
inoremap <special> <expr> <Esc>[200~ XTermPasteBegin()

" F6: 切换语法高亮(大文件时关闭以提升性能)
nnoremap <F6> :if exists('syntax_on')<Bar>syntax off<Bar>else<Bar>syntax on<Bar>endif<CR>

" ==============================================================================
" Leader 键设置
" ==============================================================================
" 将 Leader key 设置为空格键
let mapleader = " "
nnoremap <Leader>w :w<CR>

" 使用 Leader + 数字 快速切换 buffer (替代ctrl+6)
nnoremap <Leader>1 :buffer 1<CR>
nnoremap <Leader>2 :buffer 2<CR>
nnoremap <Leader>3 :buffer 3<CR>
nnoremap <Leader>4 :buffer 4<CR>
nnoremap <Leader>5 :buffer 5<CR>
nnoremap <Leader>6 :buffer 6<CR>
nnoremap <Leader>7 :buffer 7<CR>
nnoremap <Leader>8 :buffer 8<CR>
nnoremap <Leader>9 :buffer 9<CR>
nnoremap <Leader>0 :buffer 10<CR>

" 优雅关闭当前 buffer(使用 vim-bbye)
nnoremap <Leader>q :Bdelete<CR>

" 文件树切换(与 F2 功能相同,类似neovim效果)
nnoremap <Leader>e :Fern . -drawer -toggle<CR>
" 切换显示行号和不可见字符 (与 F3 功能相同,在F3被占用时使用)
nnoremap <Leader>h :call <SID>toggle_number_info()<CR>
" 切换自动换行(与 F4 功能相同,在F4被占用时使用)
nnoremap <Leader>z :call <SID>toggle_wrap()<CR>

" WhichKey 提示
nnoremap <silent> <leader> :WhichKey '<Space>'<CR>
vnoremap <silent> <leader> :WhichKeyVisual '<Space>'<CR>

" ==============================================================================
" Asyncomplete 补全配置
" ==============================================================================
" 使用 Tab 和 Shift-Tab 在弹出的补全菜单中上下切换
inoremap <expr> <Tab>   pumvisible() ? "\<C-n>" : "\<Tab>"
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"

" 使用回车键确认补全,如果菜单未弹出则正常换行
inoremap <expr> <cr> pumvisible() ? asyncomplete#close_popup() : "\<cr>"

" 注册补全源(文件路径 + 当前 buffer 单词)
augroup asyncomplete_setup
    autocmd!
    " 注册文件路径补全 (输入 ./ 或 / 时触发)
    autocmd User asyncomplete_setup call asyncomplete#register_source(
        \ asyncomplete#sources#file#get_source_options({
            \ 'name': 'file',
            \ 'allowlist': ['*'],
            \ 'priority': 10,
            \ 'completor': function('asyncomplete#sources#file#completor')
        \ }))

    " 注册 Buffer 单词补全 (扫描当前文件内的词汇)
    " 限制最大扫描 5MB,防止读取超大文本或矩阵数据时卡顿
    autocmd User asyncomplete_setup call asyncomplete#register_source(
        \ asyncomplete#sources#buffer#get_source_options({
            \ 'name': 'buffer',
            \ 'allowlist': ['*'],
            \ 'priority': 5,
            \ 'completor': function('asyncomplete#sources#buffer#completor'),
            \ 'config': {
                \ 'max_buffer_size': 5000000,
            \ }
        \ }))
augroup END

" ==============================================================================
" vim-lsp 配置(以 Python 的 pylsp 为例)
" ==============================================================================
" 开启诊断提示,光标停留在错误处时在底部命令行显示具体信息(如 Flake8 警告)
let g:lsp_diagnostics_enabled = 1
let g:lsp_diagnostics_echo_cursor = 1

" 注册纯 Python 编写的 pylsp
" 需要先安装服务端:pip install python-lsp-server[all]
if executable('pylsp')
    augroup lsp_python
        autocmd!
        autocmd User lsp_setup call lsp#register_server({
            \ 'name': 'pylsp',
            \ 'cmd': {server_info->['pylsp']},
            \ 'allowlist': ['python'],
            \ 'workspace_config': {
                \ 'pylsp': {
                    \ 'plugins': {
                        \ 'pycodestyle': { 'maxLineLength': 120 },
                        \ 'flake8': { 'maxLineLength': 120 }
                    \ }
                \ }
            \ }
        \ })
    augroup END
endif

" LSP 快捷键(仅在支持的语言 buffer 中生效)
function! s:on_lsp_buffer_enabled() abort
    setlocal omnifunc=lsp#complete
    setlocal signcolumn=yes " 始终显示左侧标记列,防止出现错误提示时屏幕抖动
    if exists('+tagfunc') | setlocal tagfunc=lsp#tagfunc | endif

    " 跳转到定义处
    nmap <buffer> gd <plug>(lsp-definition)
    " 跳转到引用处
    nmap <buffer> gr <plug>(lsp-references)
    " 查看函数或方法的文档说明 (悬浮提示)
    nmap <buffer> K <plug>(lsp-hover)
    " 批量重命名变量
    nmap <buffer> <leader>rn <plug>(lsp-rename)
    " 格式化当前文件代码
    nmap <buffer> <leader>f <plug>(lsp-document-format)
endfunction

augroup lsp_keymaps
    autocmd!
    autocmd User lsp_buffer_enabled call s:on_lsp_buffer_enabled()
augroup END

然后,就如注释里所说的,首先安装插件管理器vim-plug

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

之后用vim命令打开vim,忽视报错信息,输入:PlugInstall安装插件。 如果遇到网络问题安装失败,按R再安装一次即可。

上述的vimrc配置完之后,默认启动nerdfont支持。如果nerdfont没有办法安装,可以注释代码中下面两行。这样vim-fern会用+号和竖线来显示文件树。并且,让airline不使用powerline特殊符号。

let g:airline_powerline_fonts = 1 " 若使用 Powerline/NerdFont 字体则取消注释
let g:fern#renderer = "nerdfont" " 若使用 NerdFont 渲染则取消注释

至此安装结束,效果如图

vim-screenshot

改进总结

文件树 vim-fern

nerd-tree 改成 vim-fern: 在之前我使用nerd-tree时,nerd-tree在打开包含很多文件的目录时容易导致主界面卡死。vim-fern采用多线程异步处理,是nerd-tree的很好的替代。注意,vim-fern 用 快捷键l 打开, h 折叠。在这份配置里,我也配置了双击打开和折叠的快捷键。这份配置默认显示隐藏文件,并通过nerdfont配置了图标和颜色。我还开发了vim-fern-symlink插件,用来复刻nerdtree的符号链接效果,欢迎大家使用^_^

自动补全 vim-lsp

YouCompleteMe已经逐渐过时,这里我已经放弃,改用vim-lsp方案来补全,现代且强大。对于Python补全,需要先在启动vim的Python环境中安装LSP服务端:pip install python-lsp-server[all]。之后就可以像vscode一样直接补全啦。还支持函数提示和跳转到函数定义呢!

补全效果图: vim-lsp

工作区搜索 fzf+ripgrep

fzf 搜索插件, 配合ripgrep可以实现类似vscode的搜索效果。ripgrep如果服务器上没有安装,可以直接下载对应的二进制文件,放到当前用户PATH中,在终端中使用 :Rg 即可搜索文件内容,使用 :Files 即可搜索文件名,效果直追vscode。

搜索示意图如下,先输入 :Rg 再在搜索框里输入cuda 会在当前工作目录中所有文件内容中,搜索 cuda 这个单词。如果需要精确匹配,需要加一个单引号,如'cuda

vim-fzf

自定义的快捷键和快捷键提示

Vim中的键是一个用户可自定义的前缀键,用于组合创建私人快捷键,默认是反斜杠 ` \ ` 。常用配置为空格键(`Space` )。现在配置文件中对leader key做了一些设置,更方便了。

同时,加入了快捷键提示。WhichKey插件(vim-which-key)是一个极佳的快捷键提示工具,能动态显示以开头的映射,极大降低记忆负担。

切换查看buffers

注意在vim中buffer和tab不是一个概念。功能类似浏览器标签页的是buffer (在上述配置方案中显示在最上方)。vim默认使用 :bn, :bp, :b <buffer编号>, :b name, 和 ctrl-6 可以在buffer之间切换。 还有<buffer编号>ctrl-6 ,可以切换到编号为<buffer编号>的buffer。使用 :ls列出所有buffers。 在这次的配置中,切换buffer支持使用<space><buffer编号>,比默认方便了很多。

其他改进

相比之前,还有的改动如下:

  • vundle –> vim-plug:同样升级多线程下载支持。
  • 配置文件风格重新整理并前后统一。
  • 加入原生中文文档插件。
更早的文章

Vim 配置更新

写在前面距离上一篇VIM的Blog已经过去很久了,现在我的常用工作环境基本都是vim8.2甚至vim9了。此时这篇Blog就是将我的配置重新更新一番。一些基础知识就不再重复,建议先看看之前的那篇Blog。在服务器上做深度学习开发时,我最常用的还是vscode+remotessh,由于这套东西相当大,vim在我的工作之中仍是必装工具。我试用过neovim,确实非常现代化,lazynvim也非常...…

vim linux继续阅读