扩展 Visual Studio Code - 通用标识方式





0/5 (0投票)
创建与编辑器窗口交互的 Visual Studio Code 扩展的过程
引言
我喜欢自动化我经常执行的任务,而我经常做的一件事就是在 Visual Studio Code 的文档中添加 UUID。通常,我会在 Web 浏览器中打开一个单独的 UUID 生成器页面,在那里生成我需要的条目,然后将它们复制并粘贴到 Code 中。对我来说,这并不是一个理想的任务处理方式,所以我想我应该编写一个可以在 Code 中托管并直接使用的扩展。
在本文中,我们将介绍编写扩展并将其发布到 Visual Studio 市场的整个过程。本文假设您没有任何先前的知识,所以请坐好,放松,开始编码吧。
源
本文的代码可以在 GitHub 上的此处找到。我们正在构建的扩展可在 Visual Studio 市场上的此处获取。
入门
创建 Visual Studio Code 扩展依赖于两个 npm 包,yeoman 和 VS Code Extension Generator。Yeoman 是一个使用生成器脚手架新项目的生态系统。扩展创建过程使用 yeoman 生成一个新项目,这与创建 Angular 项目类似。为了安装这两个项,我们使用以下命令
npm install -g yo generator-code
当我们运行此命令时,生成器会提示我们输入一些信息,这些信息将用于创建最终的项目结构。下图显示了我为本文的扩展提供的输入值。
测试此扩展非常简单,只需在 vscode 中打开代码,然后按 F5 即可打开一个扩展开发主机窗口。要执行扩展,请按 Ctrl+Shift+P 打开命令面板,然后键入 Hello World 来运行扩展(不要忘记按 Enter 来运行它)。默认扩展只是显示一个类似 Hello World 的消息,如下所示
在代码内部(呃,Code)处理代码
代码生成完成后,它会创建一个名为 extension.ts 的文件,其中包含我们扩展的默认实现。开箱即用的代码如下所示
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
// Use the console to output diagnostic information (console.log)
// and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "goldlight-identifier" is now active!');
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
let disposable = vscode.commands.registerCommand
('goldlight-identifier.helloWorld', () => {
// The code you place here will be executed every time your command is executed
// Display a message box to the user
vscode.window.showInformationMessage('Hello World from Goldlight.Identifier!');
});
context.subscriptions.push(disposable);
}
// This method is called when your extension is deactivated
export function deactivate() {}
代码中的注释很好地解释了正在发生的事情。当扩展激活时,它会使用 vscommand.commands.registerCommand
函数注册我们将要执行的命令。当 Code 注册一个命令时,它可以在任何时候被调用。
该方法有趣之处在于,我们有一个执行上下文传递到 activate
函数。基本上,每个扩展都有一个它运行的环境,所以扩展上下文为我们提供了一组实用工具。对于这个扩展,代码创建了一个指向我们命令的可处置项。当扩展被禁用或完全删除时,我们不希望 Code 能够运行它,因此它会被处置并从可用命令列表中删除。
如果我们想执行一些清理操作,可以使用 deactivate 方法来完成。例如,我们可能想将状态保存到数据库,那么我们会在此处触发保存。由于我们将要编写的扩展不需要执行任何清理操作,所以我们将删除该函数;不用担心,这是完全安全的,这样我们就不会通过这样做来破坏我们的扩展稳定性。
此时,值得记住的是,当我们在另一个 Code 实例中搜索我们的扩展时,它被称为 Hello World。Code 如何知道我们的命令叫什么,以及在调用它时运行哪个命令?我们不能依赖我们的 activate 代码来告诉我们命令的名称,因为我们可能在函数中注册了多个命令。
将我们的命令链接到 Code 的关键在于 package.json 文件,特别是 contributes
节点。这个节点为生成的代码列出了以下命令条目。
"contributes": {
"commands": [
{
"command": "goldlight-identifier.helloWorld",
"title": "Hello World"
}
]
},
如前所述,我们可以有多个命令,所以我们为每个命令提供自己的条目。command
部分关联到 activate
函数中的命令名称,而 title
是当人们想使用我们的扩展时显示的条目。为了成为一个好的编码者,并给我们的命令起有意义的名称,让我们重命名命令。为了方便起见,我们将对所有 goldlight-identifier.helloWorld
的实例进行全局搜索和替换,并将其重命名为 goldlight-identifier.createIdentifier
。我们应该看到 package.json 和 extension.ts 文件都已更新为这个新的命令名称。
既然我们已经完成了,让我们更改我们扩展的 title
。我们将它从 Hello World 改为 Generate UUID。
我喜欢键盘快捷键,所以在我打开 package.json 时,我将添加绑定支持。我们将它们添加到 contributes
部分,它们看起来与命令条目非常相似。
"keybindings": [
{
"command": "goldlight-identifier.createIdentifier",
"key": "ctrl+shift+u",
"mac": "cmd+shift+u"
}
]
同样,我们可以有多个按键绑定,所以这一部分允许我们为我们注册的每个命令添加一个绑定。command
告诉我们这个绑定是为哪个命令服务的,而 key
和 mac
条目告诉我们在 PC 上的按键组合是什么。最初,我使用 CTRL/CMD+SHIFT+U 作为按键绑定,但它在 Code 中有其他用途(它显示 Output 窗口),所以我决定使用一个按键组合。如果你不知道什么是按键组合,它是指使用一系列按键来触发一个命令(例如,Ctrl+K+K)。要为此添加一个按键组合,Ctrl+U,U,我们更改 key 和 map 条目,使我们的 keybindings
如下所示
"keybindings": [
{
"command": "goldlight-identifier.createIdentifier",
"key": "ctrl+shift+u",
"mac": "cmd+shift+u"
}
]
在我们完成按键绑定之前,还有最后一件事要做。由于我们想在文本编辑器中替换或添加文本,我们将说我们希望我们的 keybinding
在我们聚焦于读/写编辑器时应用。为此,我们将一个 when
条目添加到我们的绑定中,该条目指定我们希望该绑定在编辑器窗口可写且具有文本焦点时应用。文本焦点意味着光标在编辑器窗口中闪烁,并且编辑器中打开的文档不是只读的。
"keybindings": [
{
"command": "goldlight-identifier. createIdentifier",
"key": "ctrl+u ctrl+u",
"mac": "cmd+u cmd+u",
"when": "editorTextFocus && !editorReadonly"
}
]
由于我们为控制键添加了一个约束来控制何时可以应用命令,所以我们应该有一个类似的约束来控制命令何时出现在命令面板中。不幸的是,这并不是我们可以在命令条目中直接完成的,所以我们需要找到另一种方法。这样做需要对命令面板是什么有一些了解。命令面板是一个显示一系列项(如菜单)的小部件。当我们这样考虑它时,我们就会得到一个提示:命令面板出现在 package.json 中的 menus
条目中。控制 createIdentifier
命令的命令可见性的代码如下所示
"menus": {
"commandPalette": [
{
"command": "goldlight-identifier.createIdentifier",
"when": "editorFocus && !editorReadonly"
}
]
}
注意:与按键绑定不同,when
子句由 editorFocus
控制,而不是 editorTextFocus
。两者的区别在于,editorTextFocus
表示编辑器具有焦点,但当命令面板小部件出现时,我们不再拥有文本焦点,因为小部件现在是主导。这就是为什么我们使用 editorFocus
来控制命令面板条目的原因,因为它不需要光标闪烁。
在编辑器内部
现在我们已经将命令连接到键盘快捷键和命令面板,我们就可以添加代码来创建我们的 UUID 了。我们要做的第一件事是删除 registerCommand
代码块中的预生成代码。我们将在其中放置我们的功能,所以这个区域应该是空的。
现在代码块是空的,我们的命令会获取活动文本编辑器。这是我们要添加 UUID 的文档,所以我们需要获取它的引用。
const editor = vscode.window.activeTextEditor;
if (editor) {
}
现在我们知道我们有一个编辑器窗口,实际实现非常简单。我们想要的是,如果我们没有选择任何文本,就插入一个 UUID;如果选择了文本,就用 UUID 替换文本;或者如果我们有多个选区(例如,我们做了类似这个的操作),就添加多个 UUID。能够做到这一点是因为当编辑器获得焦点时,我们总会有选区。以下代码位于 if (editor
) 块内。
editor.edit((textEditorEdit) => {
editor.selections.forEach((selection) => {
textEditorEdit.replace(selection, randomUUID());
});
});
在这里,我们告诉 Code 我们想要编辑每个选区,并用 randomUUID
替换它。randomUUID
调用需要在编辑器中进行以下导入。
import { randomUUID } from 'crypto';
此时,人们很容易认为我们已经完成了所有需要做的事情。我们有能力从命令面板和键盘快捷键触发在编辑器中添加 UUID。在添加任何附加功能之前,我们将稍微旁侧一步,看看发布扩展需要做什么。毕竟,能够与世界分享它并使用它将是很棒的。
看,妈妈!我是作者。发布我们的扩展。
有两种方法可以将我们的扩展发布到 Visual Studio 市场。两种方式都需要的第一步是安装另一个命令行工具;这个工具用于打包、管理和发布扩展。
npm install -g @vscode/vsce
我们将要采取的路线是使用 Azure DevOps 来管理发布过程。还可以采用另一种方法,即打包扩展,然后直接上传到市场,但我们将假设我们将使用 Azure 完成整个过程。如果您还没有注册 DevOps 功能,请不用担心,它是免费使用的。我们不会涵盖注册 Azure 的内容,因为它非常简单,您应该可以轻松搞定。
为了发布我们的扩展,我们需要一个 个人访问令牌 (PAT),我们需要先创建一个组织。为此,请单击 新建组织 按钮,如下图所示。
同样,这是一个简单的过程,所以我将留给您来完成注册组织。只需选择一个唯一的组织名称,选择您希望它在哪里托管,然后填写验证码。
组织可用后,我们就可以创建个人访问令牌 (PAT) 了。在您的组织中,选择 用户设置 选项,然后在显示的菜单中,选择 个人访问令牌。
在用户设置页面,单击 新建令牌 按钮以打开我们创建 PAT 的对话框。显然,我们想给令牌起一个有意义的名称。在这种情况下,我们将调用它 goldlight.identity
。将 组织 字段更改为 所有可访问的组织,如果尚未选择的话。默认情况下,作用域应设置为 自定义,因此请保持不变。
为了设置市场的范围,我们必须单击“显示所有作用域”链接以显示缺失的作用域。完成此操作后,我们搜索 Marketplace,然后选择 Manage 选项。
我们将最后处理个人便利性部分。如果我们创建一个有效期较短的令牌,我们将不得不定期重新生成它。我们可以通过将 过期 时间更改为 自定义 来更改令牌的持续时间。现在我们可以选择一年内的日期。
在下面的图片中,我展示了我们需要设置的所有内容。一旦我们拥有了所需的一切,就单击 创建 按钮。
我们需要复制创建的密钥。一旦生成,它就无法恢复,所以请务必将其复制到安全的地方。这就是我们稍后将使用的令牌。
简短插曲
有时,我们想将 GitHub 存储库导入我们的 DevOps 安装。如果我们仍在更新 GitHub 代码,而不是现在位于 Azure DevOps 中的代码,我们会很快注意到 Azure 中的代码不会在 GitHub 存储库更新时更改,并且没有直接的方法可以在两者之间同步更改。
现在,我们可以通过构建管道或 GitHub Actions 来强制同步,但我发现同步两者的最简单方法是一个简单的命令行操作,它依赖于 git 命令的能力。只需设置一个远程镜像,该镜像使用命令指向我们的 Azure 代码(这只需要设置一次)。
git remote add --mirror=fetch secondary <<our Azure repository>>
现在,当我们推送了一个提交到 GitHub 后,只需运行以下命令
git push secondary --all
它快速,易于使用,并且是我送给您的礼物,因为它一直是我的一大困扰,Azure 并没有“开箱即用”地支持这一点。
Visual Studio 市场
我们几乎准备好发布我们的扩展了。在这样做之前,我们必须在 Visual Studio 市场中创建一个发布者。我们的 id
应该是唯一的,因为我们即将使用它。出于我们扩展的目的,我创建了一个 id
为 Goldlight-Extensions
的发布者。
注意:我们必须使用与创建 PAT 相同的帐户登录市场。
回到发布
现在我们拥有登录扩展所需的一切。我们将使用以下命令登录市场
vsce login Goldlight-Extensions
这将使用我们在创建市场条目时指定的扩展登录市场。我们会收到提示输入 PAT,所以现在让我们粘贴进去。当我们按 Enter 键时,我们的令牌会与我们的登录进行验证,我们就可以开始了。
在发布扩展之前,我们还有一件事要做。我们要为我们的扩展添加一个图像和一个发布者。打开 package.json 文件,在 version 下方添加一个 img 条目。图像必须指向一个固定的网络位置,所以我们在文件中将其设置为 https://github.com/ohanlon/goldlight-identifier/blob/main/src/goldlight.png。发布者使用我们的市场发布者 id(在此示例中为 Goldlight-Extensions
)。
我们终于可以发布扩展了,可以使用以下命令完成
vsce publish
如果我们登录时没有指定令牌,现在就会提示我们输入。
发布过程现在会经历各种步骤,编译和打包代码。由于我们没有指定存储库,所以我们会收到提示选择是否要继续该过程。目前,我们对此并不太担心,因为存储库只是让人们知道他们可以在哪里贡献代码(如果他们愿意)。我们有一个类似的提示,让我们知道我们没有添加许可证文件。同样,目前我们对此也不太在意。
发布过程完成后,我们的扩展现在可以在市场中使用了。让我们看看它在 Code 中的样子。
我确实提到过另一种发布扩展的方法。在那里我们需要做的是运行以下命令。
vsce package
这将打包我们的扩展,然后我们手动将其上传到市场本身。我更喜欢运行上面描述的登录/发布过程,因为它能节省我的时间。
结论
我们已经从头开始成功创建了一个 vscode 扩展。在此过程中,我们看到了如何添加命令面板支持,以及如何将其链接到按键组合菜单。完成代码后,我们采取了下一步,在 Azure DevOps 中创建了一个个人访问令牌,并使用它发布到 Visual Studio 市场。
我希望您已经达到了对构建自己的 Code 扩展并与世界分享的想法感到兴奋的程度。
历史
- 2023年4月4日:初始版本