# 示例 Word Count
如果你还没有接触过你的第一个插件章节,我们建议你先去了解一下。
本篇示例将会告诉你,如何制作一个Markdown辅助编辑工具。开始之前,我们先了解一下本篇你将接触到的插件功能:当编辑Makrdown文件时状态栏会显示编辑区字数,如果你编辑文档或者打开了另一个md文件字数也会随之改变。
TIP
如果你碰到了什么问题,可以在这里下载完整的项目进行调试
# 本节要点
本章将通过三个部分让你了解vscode有关的概念:
如果你还不熟悉生成插件的步骤,请先了解之前的章节:生成插件-运行Yo
就像你之前在示例:Hello-world中做的一样,使用F5
或者Cmd + R
运行该项目。
# 更新状态栏
将下列代码更新到extension.ts
中。这段代码声明了一个WordCounter
类用于控制文本计数并显示到状态栏中,我们依然用了"Hello World"这个命令来执行updateWordCount
。
// 'vscode'模块包含了VS Code扩展性API
// 导入你要用到的扩展类
import {
window,
commands,
Disposable,
ExtensionContext,
StatusBarAlignment,
StatusBarItem, TextDocument
} from 'vscode';
// 当你的插件激活时,会调用这个方法。'activation'是package.json中定义好的activation events
export function activate(context: ExtensionContext) {
// 用console对象输出诊断和错误信息(console.log / console.error).
// 下面这行代码,只会在你的插件激活时执行一次
console.log('Congratulations, your extension "WordCount" is now active!');
// 新建一个字数计数器
let wordCounter = new WordCounter();
let disposable = commands.registerCommand('extension.sayHello', () => {
wordCounter.updateWordCount();
});
// 把disposables添加到一个列表中,以便关闭插件时释放资源
context.subscriptions.push(wordCounter);
context.subscriptions.push(disposable);
}
class WordCounter {
private _statusBarItem: StatusBarItem = window.createStatusBarItem(StatusBarAlignment.Left);
public updateWordCount() {
// 获取当前编辑器
let editor = window.activeTextEditor;
if (!editor) {
this._statusBarItem.hide();
return;
}
let doc = editor.document;
// 只对markdown文件生效
if (doc.languageId === "markdown") {
let wordCount = this._getWordCount(doc);
// 更新状态栏
this._statusBarItem.text = wordCount !== 1 ? `${wordCount} Words` : '1 Word';
this._statusBarItem.show();
} else {
this._statusBarItem.hide();
}
}
public _getWordCount(doc: TextDocument): number {
let docContent = doc.getText();
// 去除多余空格
docContent = docContent.replace(/(< ([^>]+)<)/g, '').replace(/\s+/g, ' ');
docContent = docContent.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
let wordCount = 0;
if (docContent !== "") {
wordCount = docContent.split(" ").length;
}
return wordCount;
}
dispose() {
this._statusBarItem.dispose();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
现在让我们试试更新插件吧。
在VS Code中(非开发主机窗口)按下F5
或者直接按重启按钮。假设你已经打开了一个markdown文件,然后和Hello-World示例一样,我们在命令面板中输入Hello World
启动插件。
很棒,我们接下来做更cool的事情——实时更新字数。
# 订阅事件
我们先来看看事件中的类方法:
onDidChangeTextEditorSelection
- 鼠标位置变动时触发。onDidChangeActiveTextEditor
- 激活编辑器(打开的编辑器)切换的时候触发。
为了实现这个目标,我们给extension.ts
添加一个新类,订阅上述事件然后让WordCounter
更新字数。
TIP
在实现时,你需要注意我们是如何把**消息订阅( subscription )转换为释放器( Disposables )**来管理的,它将监听并释放自己。
根据下列代码,将WordCounterController
类添加到extension.ts
文件底部。
class WordCounterController {
private _wordCounter: WordCounter;
private _disposable: Disposable;
constructor(wordCounter: WordCounter) {
this._wordCounter = wordCounter;
// 订阅 文本选区变更 和 编辑器激活事件
let subscriptions: Disposable[] = [];
window.onDidChangeTextEditorSelection(this._onEvent, this, subscriptions);
window.onDidChangeActiveTextEditor(this._onEvent, this, subscriptions);
// 为当前文件更新计数器
this._wordCounter.updateWordCount();
// 把两个事件订阅器整合成一个临时容器
this._disposable = Disposable.from(...subscriptions);
}
dispose() {
this._disposable.dispose();
}
private _onEvent() {
this._wordCounter.updateWordCount();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
我们不希望通过执行命令才启动词汇计数插件,而是markdown文件一打开插件就应该启动。
首先,我们将active
函数替换成这样:
// 输出诊断信息
console.log('Congratulations, your extension "WordCount" is now active!');
// 新建一个词汇计数器
let wordCounter = new WordCounter();
let controller = new WordCounterController(wordCounter);
// 插件关闭时,释放器会自动释放
context.subscriptions.push(controller);
context.subscriptions.push(wordCounter);
2
3
4
5
6
7
8
9
10
然后,我们必须保证Markdown
文件打开时才激活。在之前的示例中,我们通过extension.sayHello
命令激活插件,而我们现在用不到了,删除package.json
文件中原来的contributes
键。
"contributes": {
"commands":
[{
"command": "extension.sayHello",
"title": "Hello World"
}
]
}
2
3
4
5
6
7
8
用下面的代码替换掉:
"activationEvents": [
"onLanguage:markdown"
]
2
3
为onLanguage: ${language}配置一个语言,在这里是"markdown" 。这样一来打开这一类文件就会触发这个事件了。
重启插件(按下Cmd + R
或者重启按钮),然后新建一个.md
文件,你应该能看到下图的效果。
如果你在active
函数上打了断点,你应该能看到markdown文件被打开时只触发了一次。WordCountController
构造器运行之后,订阅了编辑器事件,这样我们整个插件就正常运行了。
# 自定义状态栏
VS Code允许你定制状态栏的颜色、图标、提示文本等额外样式。如果你不清楚状态栏相关的API,你可以查看该类型的代码提示,你也可以通过vscode.d.ts
VS Code扩展性API查看,这个文件就在你生成的项目文件夹里,在编辑器中打开node_modules\vscode\vscode.d.ts
,你能看到完整的扩展性 API和注释。
更新StatusBarItem接口的代码
// 更新状态栏
this._statusBarItem.text = wordCount !== 1 ? `$(pencil) ${wordCount} Words` : '$(pencil) 1 Word';
this._statusBarItem.show();
2
3
这样就在计数左边显示了一个Github Oction的pencil
图标
# 释放插件资源
现在,我们来深入了解一下VS Code是怎么通过释放器(Disposables)控制资源的。
当一个插件被激活,它会传入一个ExtensionContext
对象, 这个对象有一个用于订阅释放器(Disposable)的subscriptions
方法。插件将=释放器添加到这个订阅列表中,VS Code则会在插件关闭的时候释放这些对象。
很多能生成工作区(workspace)或者UI对象的VS Code API(比如:registerCommand
)会自动返回一个释放器,我们可以直接调用他们的dispose方法释放UI元素。
事件则有所不同,比如onDid*
这类事件订阅器会返回一个释放器。插件通过释放事件的释放器(Disposable)来取消已经订阅的事件。在我们的例子里,WordCountController
把事件订阅的释放器直接保存到自己的释放器列表中,在插件关闭时释放。
// 订阅选区变动事件和编辑器激活事件
let subscriptions: Disposable[] = [];
window.onDidChangeTextEditorSelection(this._onEvent, this, subscriptions);
window.onDidChangeActiveTextEditor(this._onEvent, this, subscriptions);
// 把两个事件订阅器生成一个组合的临时容器
this._disposable = Disposable.from(...subscriptions);
2
3
4
5
6
7
# 在本地安装你的插件
到目前为止,你的插件都还跑在插件开发模式中,要想让你的插件在正常的VS Code中运行起来将你的插件复制到.vscode/extensions
目录下。
# 发布插件
参阅分享插件
# 下一步
插件生成器 - 学习Yo Code插件生成器的更多选项
Extenstion API - 插件API概览
发布插件 - 学会如何在应用市场发布一个公共插件
编辑器 API - 学习更多有关文档, 文档编辑器和编辑的内容
更多插件示例 - 在插件示例列表学习其他用法