vsocde-nvim
使用vim会让人上瘾,同时它也将治好你的vim崇拜症。
——鲁迅
vscode-nvim是我现在使用vscode时必装的一个插件了,它比vscode-vim更加的强大,它不再仅仅是一个vim模拟器,而是真正的将nvim集成到了vscode中。
鉴于目前除开官方文档所提供的资料外,vsocde-nvim插件的使用在各大中文平台网站很难搜索到有用的信息,因此这里专门写一篇新手向的vscode-nvim插件使用教程,也算是对vim推广做出一点薄弱的贡献。
在开始介绍这款插件之前,有必要了解一下它的设计模式:
- NORMAL、COMMAND、VISUAL:nvim将会正式接管这些模式,这意味着这些模式的热键映射你必须放在~/.config/nvim/init.vim文件中才会生效
- INSERT:由vscode接管此模式,这意味着要想在该模式下映射的键位生效,你必须将设置存放在vsocde热键keybindings.json文件中,直接定义在nvim的配置文件中是不生效的
好了,如果你之前用过vscode-vim插件,那么你应该体会到他们2者的差别了。
vscode-vim插件只是作为一个vim模拟器,它允许将所有的vim配置在settings.json中,这对vscode-nvim来说是不可理喻的。
这种差异化来源于nvim对vim原本做出的改进,使之能够被完整的嵌入进一个GUI中。
快速上手
准备工作
nvim全称是neo vim,它是一个vim的分支版本,相较于vim来说它更强大也更优秀,因此对于现在很多将工作环境完全迁徙到终端下的程序员来说,nvim是一个更好的选择。
vscode-nvim插件依赖nvim,且nvim版本必须大于0.5,所以在开始之前我们要先安装一下nvim:
# 1.下载nvim安装包
$ wget https://github.com/neovim/neovim/releases/download/stable/nvim-macos.tar.gz
# 2.解压到指定的目录
$ tar xzf ./nvim-macos.tar.gz -C /usr/local/application/
$ mv /usr/local/application/nvim-osx64/ /usr/local/application/nvim
# 3.将nvim添加到系统环境变量,你可以创建软链接或者直接编辑/etc/profile文件
PATH=/usr/local/application/nvim/bin:$PATH
下载完成nvim之后,我们可以到vscode的插件商店中下载vscode-nvim插件:
下一步仅针对MAC平台用户,你需要在使用vscode-nvim模式时禁用掉MAC的重复键:
$ defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false
同时,在vsocde-nvim的官方文档中也详细进了说明,禁用editor.scrollBeyondLastLine能为我们带来更好的编辑体验:
The extension works best if editor.scrollBeyondLastLine is disabled.
怎么做呢?我们只需要打开vscode的settings.json文件加入这一行即可:
// 控制编辑器是否可以将光标移动到最后一行以后或者最上一行以上,这对vim模式下的gg、G命令来说更加友好
"editor.scrollBeyondLastLine": false,
另外个人十分推荐在使用vscode-nvim插件时在vsocde中开启相对行号,请打开settings.json进行配置:
// 设置相对行号
"editor.lineNumbers": "relative",
链接nvim
现在我们初始工作做完了,nvim有了,vscode-nvim插件也有了。
接下来就需要将它们两者链接起来,使之互相之间产生关系,你需要根据使用平台的不同在settings.json中加入下面这个配置项:
// Windows
"vscode-neovim.neovimExecutablePaths.win32": "D:\\Application\\Neovim\\bin\\nvim.exe",
// Linux
"vscode-neovim.neovimExecutablePaths.linux": "/usr/local/application/nvim/bin/nvim",
// Mac
"vscode-neovim.neovimExecutablePaths.darwin": "/usr/local/application/nvim/bin/nvim",
我使用的是Mac平台,并且已经将nvim添加到环境变量中了,所以直接进行下面这样的设置即可,Linux平台同理:
"vscode-neovim.neovimExecutablePaths.darwin": "nvim",
设置完成之后重启vscode生效。
启动模式
不乏有一些之前使用过nvim的用户对nvim本身设置了很多选项。
但其实这些选项可能会对vsocde-nvim的性能产生影响,为了屏蔽这些影响,vscode-nvim官方也贴心的编写了vimscript来区分你的nvim启动模式。
我们只需要将这段vimscript放置在~/.config/nvim/init.vim文件中即可。
- Ps:~/.config/nvim/init.vim文件是nvim的配置文件,如果不存在,那么请创建它
如下所示:
" 选择启动模式、作为vscode扩展启动、或者作为本体启动
" 时所加载的配置项
if exists('g:vscode')
" VSCode extension
else
" ordinary neovim
endif
举一个简单的例子,我下面有3个nvim选项:
- set mouse=a:支持鼠标操作,我希望在nvim正常启动时加载
- set nobackup:不创建备份文件,我希望在nvim作为vscode-nvim插件依赖进行启动时加载
- let mapleader = "\<space>":设置前导键,我希望无论nvim在那种情况下启动都进行加载
这个时候就可以对~/.config/nvim/init.vim文件进行这样的编辑:
if exists('g:vscode')
" 以vsocde-nvim插件作为依赖启动时加载的配置项
set nobackup
else
" 以正常模式启动nvim时加载的配置项
set mouse=a
endif
" 始终都会加载的配置项
let mapleader = "\<space>"
另外对于一些插件来说,你也可以单独的指定它们的启动模式,如:
" inside plug#begin:
" use normal easymotion when in vim mode
Plug 'easymotion/vim-easymotion', Cond(!exists('g:vscode'))
" use vscode easymotion when in vscode mode
Plug 'asvetliakov/vim-easymotion', Cond(exists('g:vscode'), { 'as': 'vsc-easymotion' })
性能影响
以下在nvim中的设置可能会对vsocde-nvim插件产生性能影响:
-- 任何经常渲染装饰器的东西:
· 行号扩展(VSCode 内置了对普通/相对行号的支持)
· 缩进指南扩展(VSCode 有内置的缩进指南)
· 括号荧光笔扩展(VSCode 有内置功能)
-- 延迟扩展主机的 VSCode 扩展,如“Bracket Pair Colorizer”
-- 增加延迟并导致性能问题的 VIM 插件。
· 确保禁用不需要的插件,因为其中许多插件对 vscode 没有意义并且可能会导致问题。
· 您不需要任何代码、突出显示、完成、lsp 插件以及任何生成窗口/缓冲区(nerdtree 和类似的)、模糊查找器等的插件。
· 许多导航/文本对象/编辑插件应该没问题。
使用差异
在使用vscode-nvim插件时和使用原生nvim时还是有一定的区别的。
这一点官方文档上也说明的很详细:
- 例如命令模式下的:e、:w、:q、:sp系列、:tab系列等命令实际上都不是调用的nvim指令完成的,而是调用的vscode指令,不要像在nvim中一样使用它们,比如ctrl+r命令在nvim下是redo命令,而在vscode下则不会有任何效果,你可以在后面通过更改热键来改变它们
- 可视模式不会产生vscode选择,因此任何需要选择的vscode命令都不起作用。为了绕过拐角,通过默认热键(f1/ctrl/cmd+shift+p)从可视模式调用vscode命令选择器将vim选择转换为真正的vscode选择。对于某些命令(例如注释和格式设置),此转换也会自动完成。如果您使用一些自定义映射来调用依赖于真实vscode选择的vscode命令,您可以使用VSCodeNotifyRange/VSCodeNotifyRangePo(第一个逐行,后一个字符),它将在调用命令之前将vim视觉模式选择转换为vscode选择
- 当您键入某些命令时,它们可能会替换另一个命令,例如:write将替换为:Write.这是正常的。
- 滚动是由VSCode完成的。<C-d> / <C-u> / etc略有不同
- 编辑器自定义(相对行号、滚动条等)由VSCode处理
- 点重复(.)略有不同-在更改范围内移动光标不会破坏重复序列。在Neovim中,如果您abc<cursor>在插入模式下输入,则将光标移至a<cursor>bc并1在此处输入重复序列将是1.但是在vscode中它会是a1bc.另一个区别是,当您在插入模式下删除某些文本时,点重复仅从右到左起作用,这意味着在运行点重复时它会将Del键视为BS键
热键设置
让vscode影响nvim
对于INSERT模式下的热键,我们需要将它设置在vscode的keybindings.json文件中,一个最常见的例子就是设置jj或者jk退出INSERT模式:
{
"command": "vscode-neovim.compositeEscape1",
"key": "j",
"when": "neovim.mode == insert && editorTextFocus",
"args": "j"
}
让nvim影响vscode
nvim除开可以定义自己本身的热键外,还可以通过一些辅助性函数间接的调用vscode下的功能。
我们只例举1个最常用的函数:
- VSCodeNotify(command, ...):使用可选参数非阻塞的调用vscode命令
其他的辅助性函数你可以从官方文档中找到他们的定义,这里不再进行例举。
它有什么作用呢?比如在Mac平台下,我们需要切换选项卡视图可以通过vscode的快捷键来进行控制:
- cmd + shift + [
- cmd + shift + ]
也可以通过nvim的命令来进行控制:
- :tabp
- :tabn
- gt
- gT
有多种切换方式,用哪一种好呢?
其实vscode-nvim官方推荐将这类操作交由vscode本身来做,所以你就可以在~/.config/nvim/init.vim文件中这样进行定义:
nnoremap gt <Cmd>call VSCodeNotify('workbench.action.previousEditor')<CR>
nnoremap gT <Cmd>call VSCodeNotify('workbench.action.nextEditor')<CR>
这些命令你可以从vsocde的键盘快捷键中复制命令ID获得。
另外vsocde-nvim官方也做了一些让vscode-nvim转接vscode行为的命令,如下链接所示,这些都是官方做好了的转接行为,不需要我们重复的在~/.config/nvim/init.vim文件中进行定义:
- 有关滚动命令参考,请参阅vscode-scrolling.vim
- 有关文件命令参考,请参阅vscode-file-commands.vim
- 有关选项卡命令参考,请参阅vscode-tab-commands.vim
- 有关窗口命令参考,请参阅vscode-window-commands.vim
或者你也可以直接从官方文档中的《绑定》一章节入手查看命令或快捷键。
取消vscode热键
其实很多人放弃vscode下使用nvim或者vim插件的原因绝大一部分是因为热键冲突的问题。
比如,在vsocde中ctrl+a是全选,但是这个全选对于vim模式下的操作来说没有任何意义。
所以取消全部的vscode热键,让vscode-nvim来接管所有的操作逻辑是非常不错的选择。
对于取消全部的vscode热键,可以选择Disable default keybindings插件来完成。
- 注意,在你设置你的vscode-nvim插件时,可通过disable default keybindings插件来设置效果
- 当设置完成vscode-nvim全键盘操作方式后,应当关闭disable default keybindings插件的功能,否则后续安装新的插件后快捷方式仍然不可用
个人的热键方案
对于代码编辑区域的一些命令,则还是交由vim进行完成。
1)全局命令,vscode下直接重新映射:
ctrl + g c :显示命令面板
ctrl + g p :显示打开、转到文件
ctrl + g n :新建vscode实例
ctrl + g q :关闭vsocde实例
ctrl + g s :打开设置页面
ctrl + g k :打开热键映射
2)文件命令,vscode下直接重新映射:
ctrl + f n :新建文件
ctrl + f o :打开文件
ctrl + f e :另存为
ctrl + f s :保存文件
ctrl + f w :保存所有文件
ctrl + f q :关闭文件
ctrl + f a :关闭所有文件
:tabnew : 等同于 ctrl + f n
:saveas : 等同于 ctrl + f e
:w : 等同于 ctrl + f s
:q : 等同于 ctrl + f q
:wa : 等同于 ctrl + f w
:qa : 等同于 ctrl + f a
:x : 等同于 ctrl + f s + ctrl + f q
:xa : 等同于 ctrl + f w + ctrl + f a
3)面板与侧边栏命令,vscode下直接重新映射:
ctrl + [ :隐藏侧边栏
alt + [ :隐藏面板
ctrl + 1 :显示文件资源管理器
ctrl + 2 :显示全局搜索
ctrl + 3 :显示debug
ctrl + 4 :显示版本控制
ctrl + 5 :显示扩展商店
ctrl + 6 :显示远程资源管理
ctrl + 7 :显示测试
ctrl + 8 :显示sqltoos
ctrl + 9 :显示docker
alt + 1 :显示问题
alt + 2 :显示输出
alt + 3 :显示终端
alt + 4 :显示调试控制台
alt + 5 :显示SQL CONSOLE
3)编辑器命令,其实就是窗口命令:
ctrl + q :关闭当前选项卡
ctrl + e :聚焦在第一个选项卡中
ctrl + , :切换到上一个选项卡
ctrl + . :切换到下一个选项卡
ctrl + w s :拆分一个上下分屏
ctrl + w v :拆分一个左右分屏
ctrl + w h :将光标向左移动1屏
ctrl + w l :将光标向右移动1屏
ctrl + w j :将光标向下移动1屏
ctrl + w k :将光标向上移动1屏
以下是代码区域的一些编辑,尽量根据vim原生操作进行定义:
zR : 展开所有的折叠(这里必须转接vscode命令,vim本身的zR会失效)
zM : 关闭所有的折叠(这里必须转接vscode命令,vim本身的zM会失效)
zo : 展开当前下折叠(这里必须转接vscode命令,vim本身的zo会失效)
zc : 关闭当前下折叠(这里必须转接vscode命令,vim本身的zo会失效)
zz : 切换折叠(原生vim的zz不是切换折叠)
gc : 切换行注释
gC : 切换块注释
ctrl + / :切换行注释
gh : 获得帮助
gd : 跳转到定义
gi : 跳转到之前的编辑位置
% : 跳转到匹配的括号
ctrl + o :后退
ctrl + i :前进
g[ : 跳转到上一个问题
g] : 跳转到下一个问题
uu : 撤销上一步操作
ctrl + u : 重做上一步操作
ctrl + h : 触发帮助提示
ctrl + j : 触发参数提示
ctrl + k : 触发建议提示
ctrl + n : 选择下一个建议、或者让子选项卡向下移动
ctrl + p : 选择上一个建议、或者让子选项卡向上移动
enter : 选择当前建议
tab : 选择下一个建议
ctrl + u : 跳转到下一页(nvim本身是跳转半屏,这里改为跳转一屏)
ctrl + d : 跳转到上一页(nvim本身是跳转半屏,这里改为跳转一屏)
ctrl + alt + l : 格式化代码(个人习惯)
ctrl + = : 放大字体
ctrl + - : 缩小字体
我的keybindings
注意,我的键盘上cmd和ctrl做了互换,所以下面的cmd就默认看做为ctrl,而ctrl看做win/cmd即可。
[
// ---------------- 自定义设置项 ---------------------
// 选择全部, 在文本焦距点中失效 ++++++++++
{
"key": "cmd+a",
"command": "editor.action.selectAll",
"when": "!editorTextFocus"
},
// 格式化文档
{
"key": "cmd+alt+l",
"command": "editor.action.formatDocument",
"when": "editorHasDocumentFormattingProvider && editorTextFocus && !editorReadonly && !inCompositeEditor"
},
// 放大缩小编辑器字体
{
"key": "cmd+=",
"command": "editor.action.fontZoomIn"
},
{
"key": "cmd+-",
"command": "editor.action.fontZoomOut"
},
// 全局命令:显示命令面板
{
"key": "cmd+g c",
"command": "workbench.action.showCommands"
},
// 全局命令:快速打开、转到文件
{
"key": "cmd+g p",
"command": "workbench.action.quickOpen"
},
// 全局命令:新建vscode实例
{
"key": "cmd+g n",
"command": "workbench.action.newWindow"
},
// 全局命令:关闭vscode实例
{
"key": "cmd+g q",
"command": "workbench.action.closeWindow"
},
// 全局命令: 打开设置页面
{
"key": "cmd+g s",
"command": "workbench.action.openSettings"
},
// 全局命令:打开热键映射
{
"key": "cmd+g k",
"command": "workbench.action.openGlobalKeybindings"
},
// 文件命令:新建文件
{
"key": "cmd+f n",
"command": "welcome.showNewFileEntries",
},
// 文件命令:打开文件
{
"key": "cmd+f o",
"command": "workbench.action.files.openFileFolder"
},
// 文件命令:另存为文件
{
"key": "cmd+f e",
"command": "workbench.action.files.saveAs"
},
// 文件命令:保存文件
{
"key": "cmd+f s",
"command": "workbench.action.files.save"
},
// 文件命令:保存所有文件
{
"key": "cmd+f w",
"command": "workbench.action.files.saveAll"
},
// 文件命令:关闭文件
{
"key": "cmd+f q",
"command": "workbench.action.closeActiveEditor"
},
// 文件命令:关闭所有文件
{
"key": "cmd+f a",
"command": "workbench.action.closeAllEditors"
},
// 侧边动栏命令
// 隐藏侧边栏
{
"key": "cmd+[",
"command": "workbench.action.toggleSidebarVisibility"
},
// 显示文件资源管理器
{
"key": "cmd+1",
"command": "workbench.files.action.focusFilesExplorer"
},
// 显示全局搜索
{
"key": "cmd+2",
"command": "workbench.action.replaceInFiles",
},
// 显示debug
{
"key": "cmd+3",
"command": "workbench.view.debug",
"when": "viewContainer.workbench.view.debug.enabled"
},
// 显源代码管理(版本控制)
{
"key": "cmd+4",
"command": "workbench.view.scm",
"when": "workbench.scm.active"
},
// 显示插件商店
{
"key": "cmd+5",
"command": "workbench.view.extensions",
"when": "viewContainer.workbench.view.extensions.enabled"
},
// 显示远程资源管理
{
"key": "cmd+6",
"command": "workbench.view.remote"
},
// 显示测试
{
"key": "cmd+7",
"command": "workbench.view.testing.focus"
},
// 显示SQLTOOLS
{
"key": "cmd+8",
"command": "workbench.view.extension.sqltoolsActivityBarContainer"
},
// 显示docker
{
"key": "cmd+9",
"command": "workbench.view.extension.dockerView"
},
// 面板命令
// 隐藏面板
{
"key": "alt+[",
"command": "workbench.action.togglePanel"
},
// 显示问题
{
"key": "alt+1",
"command": "workbench.panel.markers.view.focus"
},
// 显示输出
{
"key": "alt+2",
"command": "workbench.action.output.toggleOutput",
"when": "workbench.panel.output.active"
},
// 显示终端
{
"key": "alt+3",
"command": "workbench.action.terminal.toggleTerminal",
"when": "terminal.active"
},
// 显示调试控制台
{
"key": "alt+4",
"command": "workbench.debug.action.toggleRepl",
"when": "workbench.panel.repl.view.active"
},
// 显示SQL CONSOLE
{
"key": "alt+5",
"command": "workbench.view.extension.sqltoolsPanelContainer"
},
// 选项卡设置项
// 将焦点汇聚在代码编辑区
{
"key": "cmd+e",
"command": "workbench.action.focusFirstEditorGroup"
},
// 关闭当前前选项卡
{
"key": "cmd+q",
"command": "workbench.action.closeActiveEditor"
},
// 切换到上一个选项卡
{
"key": "cmd+,",
"command": "workbench.action.previousEditor"
},
// 切换到下一个选项卡
{
"key": "cmd+.",
"command": "workbench.action.nextEditor"
},
// 拆分一个上下分屏
{
"key": "cmd+w s",
"command": "workbench.action.splitEditorDown"
},
// 拆分一个左右分屏
{
"key": "cmd+w v",
"command": "workbench.action.splitEditor"
},
// 将光标向左移动1
{
"key": "cmd+w h",
"command": "workbench.action.focusLeftGroup"
},
// 将光标向右移动1屏
{
"key": "cmd+w l",
"command": "workbench.action.focusRightGroup"
},
// 将光向下移动1屏
{
"key": "cmd+w j",
"command": "workbench.action.focusBelowGroup"
},
// 将光标向上移动1屏
{
"key": "cmd+w k",
"command": "workbench.action.focusAboveGroup"
},
// 编辑器命令
{
"key": "cmd+r",
"command": "undo"
},
// 后退
{
"key": "cmd+o",
"command": "workbench.action.navigateBack",
},
// 前进
{
"key": "cmd+i",
"command": "workbench.action.navigateForward",
},
// 触发悬停
{
"key": "cmd+h",
"command": "editor.action.showHover",
"when": "editorTextFocus"
},
// 触发参数提示
{
"key": "cmd+j",
"command": "editor.action.triggerParameterHints",
"when": "editorHasSignatureHelpProvider && editorTextFocus"
},
{
"key": "cmd+j",
"command": "closeParameterHints",
"when": "editorFocus && parameterHintsVisible"
},
// 触发建议
{
"key": "cmd+k",
"command": "editor.action.triggerSuggest",
"when": "editorHasCompletionItemProvider && textInputFocus && !editorReadonly"
},
// 关闭建议
{
"key": "cmd+k",
"command": "hideSuggestWidget",
"when": "suggestWidgetVisible && textInputFocus"
},
// 选择下一个建议, 或者让内联内容向下滚动
{
"key": "cmd+n",
"command": "selectNextSuggestion",
"when": "suggestWidgetMultipleSuggestions && suggestWidgetVisible && textInputFocus"
},
{
"key": "",
"command": "",
},
// 选择上一个建议,或者让内联内容向上滚动
{
"key": "cmd+p",
"command": "selectPrevSuggestion",
"when": "suggestWidgetMultipleSuggestions && suggestWidgetVisible && textInputFocus"
},
{
"key": "",
"command": "",
},
// 跳转到上一页
{
"key": "cmd+u",
"command": "scrollPageUp",
},
// 跳转到下一页
{
"key": "cmd+d",
"command": "scrollPageDown",
},
// 中英翻译并替换
{
"key": "cmd+t",
"command": "translates.replace",
},
{
"key": "i",
"command": "explorer.newFolder",
"when": "filesExplorerFocus && !inputFocus"
},
// ---------------- 禁用操作 ---------------
// 选择全部, 在文本焦距点中失效
{
"key": "cmd+a",
"command": "editor.action.selectAll",
"when": "!editorTextFocus"
},
{
"key": "cmd+a",
"command": "-editor.action.selectAll"
},
// 禁用cmd+p cmd+n
{
"key": "cmd+p",
"command": "-workbench.action.quickOpen"
},
{
"key": "cmd+n",
"command": "-workbench.action.files.newUntitledFile"
},
{
"key": "cmd+a",
"command": "-extension.vim_cmd+a",
"when": "editorTextFocus && vim.active && vim.use<D-a> && !inDebugRepl && vim.mode != 'Insert'"
},
]
我的init.vim文件
注意,我的键盘上cmd和ctrl做了互换,所以下面的cmd就默认看做为ctrl,而ctrl看做win/cmd即可。
如下所示:
热键设置中文图
以下是vscode内置热键的中文图。
1)Windows平台:
2)Mac平台:
切换输入法
if executable('im-select')
autocmd InsertLeave * :call system("im-select com.apple.keylayout.ABC")
autocmd CmdlineLeave * :call system("im-select com.apple.keylayout.ABC")
endif
- im-select:获取或者设置当前输入法
vim-plug
基本介绍
快速移动
修改包裹
参考文档
问题1):无法在悬浮窗口中滚动内容
问题2):无法切换C-R中的转到定义右侧面板
问题3):值得记录,在资源管理器中按下i新增目录