プログラムによる言語機能
プログラムによる言語機能(Programmatic Language Features)は、vscode.languages.* APIによって機能するスマート編集機能のセットです。Visual Studio Codeで動的な言語機能を提供するには、2つの一般的な方法があります。例として、ホバー(Hover)を取り上げてみましょう。
vscode.languages.registerHoverProvider('javascript', {
provideHover(document, position, token) {
return {
contents: ['Hover Content']
};
}
});
上記のように、vscode.languages.registerHoverProvider APIを使用すると、JavaScriptファイルにホバー内容を簡単に追加できます。この拡張機能がアクティブ化された後、JavaScriptコードの上にホバーするたびに、VS CodeはJavaScript用のすべてのHoverProviderに問い合わせ、結果をホバーウィジェットに表示します。以下の言語機能の一覧とGIF画像を見れば、拡張機能に必要なVS Code APIやLSPメソッドを簡単に見つけることができます。
別の方法として、Language Server Protocol(言語サーバープロトコル)に対応したLanguage Server(言語サーバー)を実装する方法があります。その仕組みは以下の通りです。
- 拡張機能がJavaScript用のLanguage ClientとLanguage Serverを提供します。
- Language Clientは他のVS Code拡張機能と同様に機能し、Node.jsのExtension Host(拡張機能ホスト)コンテキストで実行されます。アクティブ化されると、別のプロセスでLanguage Serverを起動し、Language Server Protocolを通じて通信します。
- VS CodeでJavaScriptコードの上にホバーします。
- VS CodeがLanguage Clientにホバーを通知します。
- Language ClientはLanguage Serverにホバー結果を問い合わせ、それをVS Codeに返します。
- VS Codeがホバー結果をホバーウィジェットに表示します。
このプロセスはより複雑に見えますが、2つの大きなメリットがあります。
- Language Serverは任意の言語で記述できます。
- Language Serverを再利用して、複数のエディターにスマート編集機能を提供できます。
さらに詳しいガイドについては、Language Serverの拡張機能ガイドをご覧ください。
言語機能の一覧
この一覧には、各言語機能について以下の項目が含まれています。
- VS Codeにおける言語機能のイメージ図
- 関連するVS Code API
- 関連するLSPメソッド
診断情報の提供 (Diagnostics)
診断情報(Diagnostics)は、コードの問題点を示す方法です。

Language Server Protocol
言語サーバーは textDocument/publishDiagnostics メッセージを言語クライアントに送信します。このメッセージには、リソースの URI に対する診断項目の配列が含まれます。
注意: クライアントはサーバーに診断情報を要求しません。サーバーが診断情報をクライアントにプッシュします。
直接実装
let diagnosticCollection: vscode.DiagnosticCollection;
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(getDisposable());
diagnosticCollection = vscode.languages.createDiagnosticCollection('go');
ctx.subscriptions.push(diagnosticCollection);
...
}
function onChange() {
let uri = document.uri;
check(uri.fsPath, goConfig).then(errors => {
diagnosticCollection.clear();
let diagnosticMap: Map<string, vscode.Diagnostic[]> = new Map();
errors.forEach(error => {
let canonicalFile = vscode.Uri.file(error.file).toString();
let range = new vscode.Range(error.line-1, error.startColumn, error.line-1, error.endColumn);
let diagnostics = diagnosticMap.get(canonicalFile);
if (!diagnostics) { diagnostics = []; }
diagnostics.push(new vscode.Diagnostic(range, error.msg, error.severity));
diagnosticMap.set(canonicalFile, diagnostics);
});
diagnosticMap.forEach((diags, file) => {
diagnosticCollection.set(vscode.Uri.parse(file), diags);
});
})
}
基本
開いているエディターの診断情報を報告します。最小限の要件として、これは保存するたびに行われる必要があります。より望ましいのは、エディターの未保存の内容に基づいて診断が計算されることです。
高度な機能
開いているエディターだけでなく、開いているフォルダー内のすべてのリソースについて、それらがエディターで開かれたことがあるかどうかに関係なく、診断情報を報告します。
コード補完候補の表示
コード補完は、文脈に応じた候補をユーザーに提案します。

Language Server Protocol
initialize メソッドの応答で、言語サーバーは補完を提供すること、および計算された補完項目に関する追加情報を提供するための completionItem/resolve メソッドをサポートしているかどうかを通知する必要があります。
{
...
"capabilities" : {
"completionProvider" : {
"resolveProvider": "true",
"triggerCharacters": [ '.' ]
}
...
}
}
直接実装
class GoCompletionItemProvider implements vscode.CompletionItemProvider {
public provideCompletionItems(
document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken):
Thenable<vscode.CompletionItem[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(getDisposable());
ctx.subscriptions.push(
vscode.languages.registerCompletionItemProvider(
GO_MODE, new GoCompletionItemProvider(), '.', '\"'));
...
}
基本
resolve プロバイダーをサポートしません。
高度な機能
ユーザーが選択した補完候補の追加情報を計算する resolve プロバイダーをサポートします。この情報は選択された項目と並んで表示されます。
インライン補完の表示
インライン補完は、複数トークンの提案をエディターに直接表示します(ゴーストテキスト)。

直接実装
vscode.languages.registerInlineCompletionItemProvider({ language: 'javascript' }, {
provideInlineCompletionItems(document, position, context, token) {
const result: vscode.InlineCompletionList = {
items: [],
commands: [],
};
...
return result;
}
});
完全な例は、インライン補完サンプルの拡張機能で確認できます。
基本
特定の言語に対して、現在の行の内容に基づく、よく知られたパターンリストに対してのみインライン補完を返します。
高度な機能
ドキュメント全体またはワークスペース内のコンテンツ、およびより複雑なパターンに基づいてインライン補完を返します。
ホバーの表示
ホバーは、マウスカーソルの下にあるシンボル/オブジェクトに関する情報を表示します。通常、これはシンボルの型と説明です。

Language Server Protocol
initialize メソッドの応答で、言語サーバーはホバーを提供することを通知する必要があります。
{
...
"capabilities" : {
"hoverProvider" : "true",
...
}
}
さらに、言語サーバーは textDocument/hover リクエストに応答する必要があります。
直接実装
class GoHoverProvider implements HoverProvider {
public provideHover(
document: TextDocument, position: Position, token: CancellationToken):
Thenable<Hover> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerHoverProvider(
GO_MODE, new GoHoverProvider()));
...
}
基本
型情報を表示し、利用可能な場合はドキュメント(説明文)を含めます。
高度な機能
コードを着色するのと同じスタイルでメソッドのシグネチャを着色します。
関数とメソッドのシグネチャのヘルプ
ユーザーが関数やメソッドを入力したときに、呼び出されている関数/メソッドに関する情報を表示します。

Language Server Protocol
initialize メソッドの応答で、言語サーバーはシグネチャヘルプを提供することを通知する必要があります。
{
...
"capabilities" : {
"signatureHelpProvider" : {
"triggerCharacters": [ '(' ]
}
...
}
}
さらに、言語サーバーは textDocument/signatureHelp リクエストに応答する必要があります。
直接実装
class GoSignatureHelpProvider implements SignatureHelpProvider {
public provideSignatureHelp(
document: TextDocument, position: Position, token: CancellationToken):
Promise<SignatureHelp> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerSignatureHelpProvider(
GO_MODE, new GoSignatureHelpProvider(), '(', ','));
...
}
基本
シグネチャヘルプに関数またはメソッドのパラメーターのドキュメントが含まれていることを確認します。
高度な機能
追加事項なし。
シンボルの定義の表示
ユーザーが、変数/関数/メソッドが使用されているまさにその場所で、それらの定義を表示できるようにします。

Language Server Protocol
initialize メソッドの応答で、言語サーバーは定義への移動(goto-definition)の場所を提供することを通知する必要があります。
{
...
"capabilities" : {
"definitionProvider" : "true"
...
}
}
さらに、言語サーバーは textDocument/definition リクエストに応答する必要があります。
直接実装
class GoDefinitionProvider implements vscode.DefinitionProvider {
public provideDefinition(
document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken):
Thenable<vscode.Location> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerDefinitionProvider(
GO_MODE, new GoDefinitionProvider()));
...
}
基本
シンボルがあいまいな場合は、複数の定義を表示できます。
高度な機能
追加事項なし。
シンボルのすべての参照の検索
特定の変数/関数/メソッド/シンボルが使用されているすべてのソースコードの場所をユーザーが表示できるようにします。

Language Server Protocol
initialize メソッドの応答で、言語サーバーはシンボル参照場所を提供することを通知する必要があります。
{
...
"capabilities" : {
"referencesProvider" : "true"
...
}
}
さらに、言語サーバーは textDocument/references リクエストに応答する必要があります。
直接実装
class GoReferenceProvider implements vscode.ReferenceProvider {
public provideReferences(
document: vscode.TextDocument, position: vscode.Position,
options: { includeDeclaration: boolean }, token: vscode.CancellationToken):
Thenable<vscode.Location[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerReferenceProvider(
GO_MODE, new GoReferenceProvider()));
...
}
基本
すべての参照の場所(リソースのURIと範囲)を返します。
高度な機能
追加事項なし。
ドキュメント内のシンボルのすべての出現箇所のハイライト
開いているエディター内で、シンボルが出現するすべての場所をユーザーが表示できるようにします。

Language Server Protocol
initialize メソッドの応答で、言語サーバーはドキュメント内シンボル場所を提供することを通知する必要があります。
{
...
"capabilities" : {
"documentHighlightProvider" : "true"
...
}
}
さらに、言語サーバーは textDocument/documentHighlight リクエストに応答する必要があります。
直接実装
class GoDocumentHighlightProvider implements vscode.DocumentHighlightProvider {
public provideDocumentHighlights(
document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken):
vscode.DocumentHighlight[] | Thenable<vscode.DocumentHighlight[]>;
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerDocumentHighlightProvider(
GO_MODE, new GoDocumentHighlightProvider()));
...
}
基本
参照が見つかったエディタードキュメント内の範囲を返します。
高度な機能
追加事項なし。
ドキュメント内のすべてのシンボル定義の表示
開いているエディター内の任意のシンボル定義にユーザーが素早く移動できるようにします。

Language Server Protocol
initialize メソッドの応答で、言語サーバーはドキュメント内シンボル場所を提供することを通知する必要があります。
{
...
"capabilities" : {
"documentSymbolProvider" : "true"
...
}
}
さらに、言語サーバーは textDocument/documentSymbol リクエストに応答する必要があります。
直接実装
class GoDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
public provideDocumentSymbols(
document: vscode.TextDocument, token: vscode.CancellationToken):
Thenable<vscode.SymbolInformation[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerDocumentSymbolProvider(
GO_MODE, new GoDocumentSymbolProvider()));
...
}
基本
ドキュメント内のすべてのシンボルを返します。変数、関数、クラス、メソッドなどのシンボルの種類を定義します。
高度な機能
追加事項なし。
フォルダー内のすべてのシンボル定義の表示
VS Codeで開いているフォルダー(ワークスペース)内の任意の場所にあるシンボル定義に、ユーザーが素早く移動できるようにします。

Language Server Protocol
initialize メソッドの応答で、言語サーバーはグローバルなシンボル場所を提供することを通知する必要があります。
{
...
"capabilities" : {
"workspaceSymbolProvider" : "true"
...
}
}
さらに、言語サーバーは workspace/symbol リクエストに応答する必要があります。
直接実装
class GoWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider {
public provideWorkspaceSymbols(
query: string, token: vscode.CancellationToken):
Thenable<vscode.SymbolInformation[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerWorkspaceSymbolProvider(
new GoWorkspaceSymbolProvider()));
...
}
基本
開いているフォルダー内のソースコードによって定義されているすべてのシンボルを返します。変数、関数、クラス、メソッドなどのシンボルの種類を定義します。
高度な機能
追加事項なし。
エラーまたは警告に対する可能なアクション
エラーや警告のすぐ横で、可能な修正アクションをユーザーに提供します。アクションが利用可能な場合、エラーや警告の横に電球アイコンが表示されます。ユーザーが電球をクリックすると、利用可能なコードアクション(Code Actions)のリストが表示されます。

Language Server Protocol
initialize メソッドの応答で、言語サーバーはコードアクションを提供することを通知する必要があります。
{
...
"capabilities" : {
"codeActionProvider" : "true"
...
}
}
さらに、言語サーバーは textDocument/codeAction リクエストに応答する必要があります。
直接実装
class GoCodeActionProvider implements vscode.CodeActionProvider<vscode.CodeAction> {
public provideCodeActions(
document: vscode.TextDocument, range: vscode.Range | vscode.Selection,
context: vscode.CodeActionContext, token: vscode.CancellationToken):
Thenable<vscode.CodeAction[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerCodeActionsProvider(
GO_MODE, new GoCodeActionProvider()));
...
}
基本
エラー/警告を修正するアクションのためのコードアクションを提供します。
高度な機能
さらに、リファクタリングなどのソースコード操作アクションを提供します。例えば、メソッドの抽出 (Extract Method) などです。
CodeLens - ソースコード内での実行可能なコンテキスト情報の表示
ソースコードの行間に挿入されて表示される、実行可能で文脈に応じた情報をユーザーに提供します。

Language Server Protocol
initialize メソッドの応答で、言語サーバーは CodeLens の結果を提供すること、および CodeLens をコマンドにバインドするための codeLens/resolve メソッドをサポートしているかどうかを通知する必要があります。
{
...
"capabilities" : {
"codeLensProvider" : {
"resolveProvider": "true"
}
...
}
}
さらに、言語サーバーは textDocument/codeLens リクエストに応答する必要があります。
直接実装
class GoCodeLensProvider implements vscode.CodeLensProvider {
public provideCodeLenses(document: TextDocument, token: CancellationToken):
CodeLens[] | Thenable<CodeLens[]> {
...
}
public resolveCodeLens?(codeLens: CodeLens, token: CancellationToken):
CodeLens | Thenable<CodeLens> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerCodeLensProvider(
GO_MODE, new GoCodeLensProvider()));
...
}
基本
ドキュメントで利用可能な CodeLens の結果を定義します。
高度な機能
codeLens/resolveに応答することで、CodeLens の結果をコマンドにバインドします。
カラーデコレーターの表示
ユーザーがドキュメント内の色をプレビューおよび変更できるようにします。

Language Server Protocol
initialize メソッドの応答で、言語サーバーは色情報を提供することを通知する必要があります。
{
...
"capabilities" : {
"colorProvider" : "true"
...
}
}
さらに、言語サーバーは textDocument/documentColor および textDocument/colorPresentation リクエストに応答する必要があります。
直接実装
class GoColorProvider implements vscode.DocumentColorProvider {
public provideDocumentColors(
document: vscode.TextDocument, token: vscode.CancellationToken):
Thenable<vscode.ColorInformation[]> {
...
}
public provideColorPresentations(
color: Color, context: { document: TextDocument, range: Range }, token: vscode.CancellationToken):
Thenable<vscode.ColorPresentation[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerColorProvider(
GO_MODE, new GoColorProvider()));
...
}
基本
ドキュメント内のすべての色の参照を返します。サポートされているカラーフォーマット(例: rgb(...)、hsl(...))のカラープレゼンテーションを提供します。
高度な機能
追加事項なし。
エディター内でのソースコードの整形(フォーマット)
ユーザーにドキュメント全体の整形(フォーマット)のサポートを提供します。

Language Server Protocol
initialize メソッドの応答で、言語サーバーはドキュメントの整形を提供することを通知する必要があります。
{
...
"capabilities" : {
"documentFormattingProvider" : "true"
...
}
}
さらに、言語サーバーは textDocument/formatting リクエストに応答する必要があります。
直接実装
class GoDocumentFormatter implements vscode.DocumentFormattingEditProvider {
provideDocumentFormattingEdits(
document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken)
: vscode.ProviderResult<vscode.TextEdit[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerDocumentFormattingEditProvider(
GO_MODE, new GoDocumentFormatter()));
...
}
基本
整形(フォーマット)サポートを提供しない。
高度な機能
ソースコードが整形された結果として、常に可能な限り最小のテキスト編集(テキストの変更差分)を返す必要があります。これは、診断結果などのマーカーが正しく調整され、失われないようにするために不可欠です。
エディター内での選択された行の整形(フォーマット)
ユーザーに、ドキュメント内の選択された行の範囲を整形するサポートを提供します。

Language Server Protocol
initialize メソッドの応答で、言語サーバーは行範囲の整形サポートを提供することを通知する必要があります。
{
...
"capabilities" : {
"documentRangeFormattingProvider" : "true"
...
}
}
さらに、言語サーバーは textDocument/rangeFormatting リクエストに応答する必要があります。
直接実装
class GoDocumentRangeFormatter implements vscode.DocumentRangeFormattingEditProvider{
public provideDocumentRangeFormattingEdits(
document: vscode.TextDocument, range: vscode.Range,
options: vscode.FormattingOptions, token: vscode.CancellationToken):
vscode.ProviderResult<vscode.TextEdit[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerDocumentRangeFormattingEditProvider(
GO_MODE, new GoDocumentRangeFormatter()));
...
}
基本
整形(フォーマット)サポートを提供しない。
高度な機能
ソースコードが整形された結果として、常に可能な限り最小のテキスト編集を返す必要があります。これは、診断結果などのマーカーが正しく調整され、失われないようにするために不可欠です。
ユーザーの入力に合わせてコードを逐次整形する(オンタイプフォーマット)
ユーザーが入力する際、テキストを整形するサポートを提供します。
注意: ユーザーの設定である editor.formatOnType によって、入力中にソースコードを整形するかどうかが制御されます。

Language Server Protocol
initialize メソッドの応答で、言語サーバーはユーザーの入力に合わせた整形を提供することを通知する必要があります。また、どの文字が入力されたときに整形をトリガーすべきかをクライアントに伝える必要もあります。moreTriggerCharacters はオプションです。
{
...
"capabilities" : {
"documentOnTypeFormattingProvider" : {
"firstTriggerCharacter": "}",
"moreTriggerCharacter": [";", ","]
}
...
}
}
さらに、言語サーバーは textDocument/onTypeFormatting リクエストに応答する必要があります。
直接実装
class GoOnTypingFormatter implements vscode.OnTypeFormattingEditProvider{
public provideOnTypeFormattingEdits(
document: vscode.TextDocument, position: vscode.Position,
ch: string, options: vscode.FormattingOptions, token: vscode.CancellationToken):
vscode.ProviderResult<vscode.TextEdit[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerOnTypeFormattingEditProvider(
GO_MODE, new GoOnTypingFormatter()));
...
}
基本
整形(フォーマット)サポートを提供しない。
高度な機能
ソースコードが整形された結果として、常に可能な限り最小のテキスト編集を返す必要があります。これは、診断結果などのマーカーが正しく調整され、失われないようにするために不可欠です。
シンボルの名前変更
ユーザーがシンボルの名前を変更し、そのシンボルへのすべての参照を更新できるようにします。

Language Server Protocol
initialize メソッドの応答で、言語サーバーは名前変更の機能を提供することを通知する必要があります。
{
...
"capabilities" : {
"renameProvider" : "true"
...
}
}
さらに、言語サーバーは textDocument/rename リクエストに応答する必要があります。
直接実装
class GoRenameProvider implements vscode.RenameProvider {
public provideRenameEdits(
document: vscode.TextDocument, position: vscode.Position,
newName: string, token: vscode.CancellationToken):
Thenable<vscode.WorkspaceEdit> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerRenameProvider(
GO_MODE, new GoRenameProvider()));
...
}
基本
名前変更のサポートを提供しない。
高度な機能
実行する必要があるすべてのワークスペース編集のリスト(例えば、そのシンボルへの参照を含むすべてのファイルにまたがるすべての編集など)を返します。