9

年轻人的第一个VSCode扩展

 3 years ago
source link: https://segmentfault.com/a/1190000038778678
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

年轻人的第一个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做起。

阅读原文


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK