はじめに
公式サンプルにVSCode APIを使ったサンプルがあるので、いくつか使いそうなものをピックアップしてみました。
今回は、Decorations
API を使ってハイライトや文字色などの装飾をする機能を付けます。
前回
VSCode拡張機能を作ってみる1 - インストールとHelloWorld!
VSCode拡張機能を作ってみる2 - 入力補完
事前準備
まず、空のdecorator.ts
ファイルを作成し、extension.ts
で読み込んでおきます。
ファイルを分けたかったので、公式から結構変更がありますが、やっていることは同じです。
エディタの変更が合った場合とテキストの変更があった場合に、Decolator処理を実行します。
import * as vscode from 'vscode';
import Decorator from './decorator';
export function activate(context: vscode.ExtensionContext) {
let decorator = new Decorator();
vscode.window.onDidChangeActiveTextEditor(editor => {
decorator.changeActiveTextEditor(editor);
}, null, context.subscriptions);
vscode.workspace.onDidChangeTextDocument(event => {
decorator.changeTextDocument(event);
}, null, context.subscriptions);
}
export function deactivate() {}
エディタの変更が合った場合にはchangeActiveTextEditor
、テキストの変更があった場合にはchangeTextDocument
を実行しています。
どちらもtimeout設定を経由してupdateDecorations
のメイン処理を実行しています。
constructorでsetDecorators
にあるスタイル設定を事前におこなっています。
import * as vscode from 'vscode';
export default class Decolator
{
private activeEditor = vscode.window.activeTextEditor;
private timeout: NodeJS.Timer | undefined = undefined;
private decorationTypes: vscode.TextEditorDecorationType[] = [];
constructor() {
this.setDecorators();
if (this.activeEditor) {
this.triggerUpdateDecorations();
}
}
public changeActiveTextEditor(editor: vscode.TextEditor | undefined): void {
this.activeEditor = editor;
if (editor) {
this.triggerUpdateDecorations();
}
}
public changeTextDocument(event: vscode.TextDocumentChangeEvent): void {
if (this.activeEditor && event.document === this.activeEditor.document) {
this.triggerUpdateDecorations();
}
}
private triggerUpdateDecorations(): void {
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = undefined;
}
this.timeout = setTimeout(() => {
this.updateDecorations();
}, 500);
}
private updateDecorations(): void {
if (!this.activeEditor) {
return;
}
// ここでDecorationsを実行します
}
private setDecorators(): void {
// ここでスタイルの設定をおこないます
}
}
数値の装飾サンプル
まずは公式サンプルにある数値の装飾をしてみます。
スタイルの設定が2パターンあります。
smallNumberDecorationType
はハードコーディングされていますが、
largeNumberDecorationType
はpackage.json
のcontributes.colors
に定義された情報を参照しています。
そうしておけば、ユーザー設定で色を再定義できます。
private setDecorators(): void {
let smallNumberDecorationType = vscode.window.createTextEditorDecorationType({
borderWidth: '1px',
borderStyle: 'solid',
overviewRulerColor: 'blue',
overviewRulerLane: vscode.OverviewRulerLane.Right,
light: {
// this color will be used in light color themes
borderColor: 'darkblue'
},
dark: {
// this color will be used in dark color themes
borderColor: 'lightblue'
}
});
this.decorationTypes.push(smallNumberDecorationType);
let largeNumberDecorationType = vscode.window.createTextEditorDecorationType({
cursor: 'crosshair',
// use a themable color. See package.json for the declaration and default values.
backgroundColor: { id: 'myextension.largeNumberBackground' }
});
this.decorationTypes.push(largeNumberDecorationType);
}
regExで指定した数字を正規表現で取得し、3以下ならsmallNumbers
、それ以外はlargeNumbers
として対象ヵ所のrangeとhoverMessageの設定をしています。
最後にsetDecorations
で対象ヵ所にスタイルを反映することができます。
private updateDecorations(): void {
if (!this.activeEditor) {
return;
}
const regEx = /\d+/g;
const text = this.activeEditor.document.getText();
const smallNumbers: vscode.DecorationOptions[] = [];
const largeNumbers: vscode.DecorationOptions[] = [];
let match;
while ((match = regEx.exec(text))) {
const startPos = this.activeEditor.document.positionAt(match.index);
const endPos = this.activeEditor.document.positionAt(match.index + match[0].length);
const decoration = { range: new vscode.Range(startPos, endPos), hoverMessage: 'Number **' + match[0] + '**' };
if (match[0].length < 3) {
smallNumbers.push(decoration);
} else {
largeNumbers.push(decoration);
}
}
this.activeEditor.setDecorations(this.decorationTypes[0], smallNumbers);
this.activeEditor.setDecorations(this.decorationTypes[1], largeNumbers);
}
まとめ
highlightの拡張機能はたくさんあるので、あえて作ることは少ないとは思うが、オレオレ言語だったり、そのほかの作りたい拡張機能と組み合わせると便利なものになりそう。