乐趣区

关于editor:年轻人的第一个VSCode扩展

序言

入坑 VS Code 前,我曾经是一名久经考验的 Emacs 老用户了,因而开始正式应用 VS Code 后,我第一工夫启用了它的Emacs Keymap。但不久我便发现,这套键映射短少一个重要的快捷键——ctrl-l

Emacs 中,ctrl-l对应的命令是 recenter-top-bottom,它用于将光标所在的行轮替地滚动到可视区域(即Emacs 中的window)的两头、顶部,以及底部(如下图所示)

这是我高频应用的一个性能,尤其是跳转到函数的定义的首行后,我习惯于连按两次,将其滚动到 window 的顶部以便在一屏中看到尽量多的内容。

为了防止反复创造轮子,我先搜寻了一番,找到了一个声称实现了该性能的扩大Recenter Top Bottom。惋惜的是,装置后并不失效。

难道只能冤屈本人用鼠标小心翼翼地将光标所在行滚到顶部了吗?当然不是。既然没有开箱即用的,那便本人写一个 VS Code 的扩大实现这个性能吧。

年轻人的第一个 VS Code 扩大

创立 VS Code 扩大的我的项目

要想入门 VS Code 扩大的开发,官网便提供了一份不错的教程。一个扩大有许多的“八股文”代码,能够用 yogenerator-code来疾速生成

npm install -g yo generator-code
yo code

到这里,便失去了一个名为 helloworld 的目录了。用 VS Code 关上它,接下来要在其中大展身手。

实现将光标所在行垂直居中的性能

VS Code扩大的外围逻辑定义在文件 src/extension.ts 中。在 yo 生成的示例代码中,用 registerCommand 注册了一个名为 helloworld.helloWorld 的命令,其逻辑是简略地在右下角弹出一句Hello VS Code from HelloWorld!。这个回调函数,便是业务逻辑的落脚点。

要想实现将光标所在行滚动到两头的性能,首先要晓得 VS Code 为开发者提供了哪些反对。在摸索了一通从 VS CodeAPI 文档 后,我有了以下的线索:

  1. 通过 vscode.window.activeTextEditor 能够获得以后聚焦的编辑器——其值可能为空(undefined);
  2. TextEditor实例的属性 .selection.active 能够获得以后光标的地位;
  3. TextEditor实例有一个办法 revealRange 能够滚动文原本扭转展现的范畴,它须要一个 vscode.Range 类的实例,以及一个 vscode.TextEditorRevealType 类型的枚举值;
  4. vscode.TextEditorRevealType.InCenter的成果是将所给定的范畴展现在两头,vscode.TextEditorRevealType.AtTop则是置顶。

有了这些常识储备,实现这样的一个回调函数便是信手拈来的事件了

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 之外定义的变量:

  1. previousPosition负责记录上一次调用 recenterTop 时光标的地位,它的初始值为null
  2. revealType存储着上一次调整展现范畴时传递给 TextEditor 实例的 revealRange 办法的第二个参数的值,它的初始值也为null

我的指标是尽量模仿 Emacs 中的 recenter-top-bottom 所具备的、交替应用居中、置顶成果的特点,因而:

  1. 如果 revealTypenull,意味着这是第一次调用recenterTop,那么成果便是居中。否则;
  2. 如果这一次与上一次的光标地位不同,意味着在上一次调用 recenterTop 后调用过其它命令,成果仍然是居中。否则;
  3. 如果 revealType 曾经是居中了,就改为置顶。否则;
  4. 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.jsoncontributes对象中,新增名为 keybindings 的属性,并定义命令及按键序列即可。

{
  // 此处省略其它不必要的属性
  "contributes": {
    "keybindings":{ // 新增属性
      "command": "helloworld.helloWorld",
      "key": "ctrl+l"
    }
  }
}

后记

如果看过我之前的文章《手指疼,写点代码缓解一下》的读者该当会记得,我曾经从 Emacs Keymap“叛逃”到了Vim Keymap 了。所以,我并没有真正用上上述的 VS Code 扩大。相同,目前高频应用的是 Vim Keymap 内置的 z-. 以及 z-↵ 了——前者用于垂直居中,后者用于置顶。

爱护手指,从应用 Vim Keymap 做起。

浏览原文

退出移动版