入坑VS Code
前,我已是一名久经考验的Emacs
老用户了,所以开始正式使用VS Code
后,我第一时间启用了它的Emacs Keymap
。但不久我便发现,这套键映射缺乏一个重要的快捷键——ctrl-l
。git
在Emacs
中,ctrl-l
对应的命令是recenter-top-bottom
,它用于将光标所在的行轮替地滚动到可视区域(即Emacs
中的window
)的中间、顶部,以及底部(以下图所示)github
这是我高频使用的一个功能,尤为是跳转到函数的定义的首行后,我习惯于连按两次,将其滚动到window
的顶部以便在一屏中看到尽可能多的内容。typescript
为了不重复发明轮子,我先搜索了一番,找到了一个宣称实现了该功能的扩展Recenter Top Bottom
。惋惜的是,安装后并不生效。shell
难道只能委屈本身用鼠标当心翼翼地将光标所在行滚到顶部了吗?固然不是。既然没有开箱即用的,那便本身写一个VS Code
的扩展实现这个功能吧。npm
要想入门VS Code
扩展的开发,官方便提供了一份不错的教程。一个扩展有许多的“八股文”代码,能够用yo
和generator-code
来快速生成json
npm install -g yo generator-code yo code
到这里,便获得了一个名为helloworld
的目录了。用VS Code
打开它,接下来要在其中大展身手。api
VS Code
扩展的核心逻辑定义在文件src/extension.ts
中。在yo
生成的示例代码中,用registerCommand
注册了一个名为helloworld.helloWorld
的命令,其逻辑是简单地在右下角弹出一句Hello VS Code from HelloWorld!
。这个回调函数,即是业务逻辑的落脚点。编辑器
要想实现将光标所在行滚动到中间的功能,首先要知道VS Code
为开发者提供了哪些支持。在摸索了一通从VS Code
的API文档
后,我有了如下的线索:函数
vscode.window.activeTextEditor
能够取得当前聚焦的编辑器——其值可能为空(undefined
);TextEditor
实例的属性.selection.active
能够取得当前光标的位置;TextEditor
实例有一个方法revealRange
能够滚动文原本改变展现的范围,它须要一个vscode.Range
类的实例,以及一个vscode.TextEditorRevealType
类型的枚举值;vscode.TextEditorRevealType.InCenter
的效果是将所给定的范围展现在中间,vscode.TextEditorRevealType.AtTop
则是置顶。有了这些知识储备,实现这样的一个回调函数即是信手拈来的事情了spa
function recenterTop() { const editor = vscode.window.activeTextEditor; if (!editor) { return; } const cursorPosition = editor.selection.active; editor.revealRange(new vscode.Range(cursorPosition, cursorPosition), vscode.TextEditorRevealType.InCenter); }
因为暂时没有配置该命令的快捷键,只能用VS Code
的命令面板来调用
接下来我将实现连续调用两次helloworld.helloWorld
命令,把光标所在行滚动到顶部的效果。在Emacs
中,能够很轻松地知道一个命令是否被连续运行——Emacs
有一个名为last-command
的变量存储着上一个命令的名称,只须要检查其是否等于recenter-top-bottom
便可。但VS Code
没有暴露这么强大的功能,只能另辟蹊径。
个人策略是,若是调用helloworld.helloWorld
时光标的位置,与上一次调用该命令时的位置相同,就认为是连续调用。为此,须要两个在函数recenterTop
以外定义的变量:
previousPosition
负责记录上一次调用recenterTop
时光标的位置,它的初始值为null
;revealType
存储着上一次调整展现范围时传递给TextEditor
实例的revealRange
方法的第二个参数的值,它的初始值也为null
。个人目标是尽可能模拟Emacs
中的recenter-top-bottom
所具有的、交替使用居中、置顶效果的特色,所以:
revealType
为null
,意味着这是第一次调用recenterTop
,那么效果即是居中。不然;recenterTop
后调用过其它命令,效果依然是居中。不然;revealType
已是居中了,就改成置顶。不然;revealType
改成居中。Talk is cheap. Show me the code.
let previousPosition: null|vscode.Position = null; let revealType: null|vscode.TextEditorRevealType = null; function recenterTop() { const editor = vscode.window.activeTextEditor; if (!editor) { return; } const cursorPosition = editor.selection.active; if (!revealType) { revealType = vscode.TextEditorRevealType.InCenter; } else if (previousPosition && !cursorPosition.isEqual(previousPosition)) { revealType = vscode.TextEditorRevealType.InCenter; } else if (revealType === vscode.TextEditorRevealType.InCenter) { revealType = vscode.TextEditorRevealType.AtTop; } else { revealType = vscode.TextEditorRevealType.InCenter; } previousPosition = cursorPosition; editor.revealRange(new vscode.Range(cursorPosition, cursorPosition), revealType); }
经过命令面板来使用不是个人最终目标,经过快捷键才是。根据VS Code
的文档能够知道,只要在package.json
的contributes
对象中,新增名为keybindings
的属性,并定义命令及按键序列便可。
{ // 此处省略其它没必要要的属性 "contributes": { "keybindings":{ // 新增属性 "command": "helloworld.helloWorld", "key": "ctrl+l" } } }
若是看过我以前的文章《手指疼,写点代码缓解一下》的读者应当会记得,我已经从Emacs Keymap
“叛逃”到了Vim Keymap
了。因此,我并无真正用上上述的VS Code
扩展。相反,目前高频使用的是Vim Keymap
内置的z-.
以及z-↵
了——前者用于垂直居中,后者用于置顶。
爱护手指,从使用Vim Keymap
作起。