Visual Studio 代码中的语言可以扩展吗?
Can a language in Visual Studio Code be extended?
场景
我有 JSON 文件描述了一系列要执行的任务,其中每个任务都可以引用 JSON 文件中的其他任务和对象。
{
"tasks": [
{ "id": "first", "action": "doSomething()", "result": {} },
{ "id": "second", "action": "doSomething(${id:first.result})", "result": {} },
]
}
我想要 JSON 模式验证和自定义语言文本效果,例如关键字着色,甚至 "Go to definition" 支持 JSON.
中的字符串
我能做什么
我可以创建一个扩展,为文件扩展名“*.foo.json”指定一个 JSON 架构。如果 vscode 将文件识别为 JSON 文件,这会在编辑器中提供模式验证和代码完成。
我还可以在“*.foo.json”文件的扩展中创建一种新的 "foo" 语言,该文件在 JSON 字符串中具有自定义关键字颜色。为此,我创建了一个从 JSON.tmLanguage.json 复制的 TextMate (*.tmLanguage.json) 文件,然后修改 "stringcontent" 定义。
问题
问题是模式验证和提示只有在我在状态栏中选择 "JSON" 作为文件类型时才有效,而自定义文本着色只有在我选择 "foo" 时才有效在状态栏中作为文件类型。
有没有办法同时拥有两者?我能以某种方式扩展 vscode 中的 JSON 语言处理吗?
对于一些 help from the vscode team,下面的代码可以正常工作。
package.json
...
"activationEvents": [
"onLanguage:json",
"onLanguage:jsonc"
],
"main": "./src/extension",
"dependencies": {
"jsonc": "^0.1.0",
"jsonc-parser": "^1.0.0",
"vscode-nls": "^3.2.1"
},
...
src/extension.js
'use strict';
const path = require( 'path' );
const vscode = require( 'vscode' );
const { getLocation, visit, parse, ParseError, ParseErrorCode } = require( 'jsonc-parser' );
module.exports = {
activate
};
let pendingFooJsonDecoration;
const decoration = vscode.window.createTextEditorDecorationType( {
color: '#04f1f9' // something like cyan
} );
// wire up *.foo.json decorations
function activate ( context /* vscode.ExtensionContext */) {
// decorate when changing the active editor editor
context.subscriptions.push( vscode.window.onDidChangeActiveTextEditor( editor => updateFooJsonDecorations( editor ), null, context.subscriptions ) );
// decorate when the document changes
context.subscriptions.push( vscode.workspace.onDidChangeTextDocument( event => {
if ( vscode.window.activeTextEditor && event.document === vscode.window.activeTextEditor.document ) {
if ( pendingFooJsonDecoration ) {
clearTimeout( pendingFooJsonDecoration );
}
pendingFooJsonDecoration = setTimeout( () => updateFooJsonDecorations( vscode.window.activeTextEditor ), 1000);
}
}, null, context.subscriptions ) );
// decorate the active editor now
updateFooJsonDecorations( vscode.window.activeTextEditor );
// decorate when then cursor moves
context.subscriptions.push( new EditorEventHandler() );
}
const substitutionRegex = /$\{[\w\:\.]+\}/g;
function updateFooJsonDecorations ( editor /* vscode.TextEditor */ ) {
if ( !editor || !path.basename( editor.document.fileName ).endsWith( '.foo.json' ) ) {
return;
}
const ranges /* vscode.Range[] */ = [];
visit( editor.document.getText(), {
onLiteralValue: ( value, offset, length ) => {
const matches = [];
let match;
while ( ( match = substitutionRegex.exec( value ) ) !== null) {
matches.push( match );
const start = offset + match.index + 1;
const end = match.index + 1 + offset + match[ 0 ].length;
ranges.push( new vscode.Range( editor.document.positionAt( start ), editor.document.positionAt( end ) ) );
}
}
});
editor.setDecorations( decoration, ranges );
}
class EditorEventHandler {
constructor () {
let subscriptions /*: Disposable[] */ = [];
vscode.window.onDidChangeTextEditorSelection( ( e /* TextEditorSelectionChangeEvent */ ) => {
if ( e.textEditor === vscode.window.activeTextEditor) {
updateFooJsonDecorations( e.textEditor );
}
}, this, subscriptions );
this._disposable = vscode.Disposable.from( ...subscriptions );
}
dispose () {
this._disposable.dispose();
}
}
场景
我有 JSON 文件描述了一系列要执行的任务,其中每个任务都可以引用 JSON 文件中的其他任务和对象。
{
"tasks": [
{ "id": "first", "action": "doSomething()", "result": {} },
{ "id": "second", "action": "doSomething(${id:first.result})", "result": {} },
]
}
我想要 JSON 模式验证和自定义语言文本效果,例如关键字着色,甚至 "Go to definition" 支持 JSON.
中的字符串我能做什么
我可以创建一个扩展,为文件扩展名“*.foo.json”指定一个 JSON 架构。如果 vscode 将文件识别为 JSON 文件,这会在编辑器中提供模式验证和代码完成。
我还可以在“*.foo.json”文件的扩展中创建一种新的 "foo" 语言,该文件在 JSON 字符串中具有自定义关键字颜色。为此,我创建了一个从 JSON.tmLanguage.json 复制的 TextMate (*.tmLanguage.json) 文件,然后修改 "stringcontent" 定义。
问题
问题是模式验证和提示只有在我在状态栏中选择 "JSON" 作为文件类型时才有效,而自定义文本着色只有在我选择 "foo" 时才有效在状态栏中作为文件类型。
有没有办法同时拥有两者?我能以某种方式扩展 vscode 中的 JSON 语言处理吗?
对于一些 help from the vscode team,下面的代码可以正常工作。
package.json
...
"activationEvents": [
"onLanguage:json",
"onLanguage:jsonc"
],
"main": "./src/extension",
"dependencies": {
"jsonc": "^0.1.0",
"jsonc-parser": "^1.0.0",
"vscode-nls": "^3.2.1"
},
...
src/extension.js
'use strict';
const path = require( 'path' );
const vscode = require( 'vscode' );
const { getLocation, visit, parse, ParseError, ParseErrorCode } = require( 'jsonc-parser' );
module.exports = {
activate
};
let pendingFooJsonDecoration;
const decoration = vscode.window.createTextEditorDecorationType( {
color: '#04f1f9' // something like cyan
} );
// wire up *.foo.json decorations
function activate ( context /* vscode.ExtensionContext */) {
// decorate when changing the active editor editor
context.subscriptions.push( vscode.window.onDidChangeActiveTextEditor( editor => updateFooJsonDecorations( editor ), null, context.subscriptions ) );
// decorate when the document changes
context.subscriptions.push( vscode.workspace.onDidChangeTextDocument( event => {
if ( vscode.window.activeTextEditor && event.document === vscode.window.activeTextEditor.document ) {
if ( pendingFooJsonDecoration ) {
clearTimeout( pendingFooJsonDecoration );
}
pendingFooJsonDecoration = setTimeout( () => updateFooJsonDecorations( vscode.window.activeTextEditor ), 1000);
}
}, null, context.subscriptions ) );
// decorate the active editor now
updateFooJsonDecorations( vscode.window.activeTextEditor );
// decorate when then cursor moves
context.subscriptions.push( new EditorEventHandler() );
}
const substitutionRegex = /$\{[\w\:\.]+\}/g;
function updateFooJsonDecorations ( editor /* vscode.TextEditor */ ) {
if ( !editor || !path.basename( editor.document.fileName ).endsWith( '.foo.json' ) ) {
return;
}
const ranges /* vscode.Range[] */ = [];
visit( editor.document.getText(), {
onLiteralValue: ( value, offset, length ) => {
const matches = [];
let match;
while ( ( match = substitutionRegex.exec( value ) ) !== null) {
matches.push( match );
const start = offset + match.index + 1;
const end = match.index + 1 + offset + match[ 0 ].length;
ranges.push( new vscode.Range( editor.document.positionAt( start ), editor.document.positionAt( end ) ) );
}
}
});
editor.setDecorations( decoration, ranges );
}
class EditorEventHandler {
constructor () {
let subscriptions /*: Disposable[] */ = [];
vscode.window.onDidChangeTextEditorSelection( ( e /* TextEditorSelectionChangeEvent */ ) => {
if ( e.textEditor === vscode.window.activeTextEditor) {
updateFooJsonDecorations( e.textEditor );
}
}, this, subscriptions );
this._disposable = vscode.Disposable.from( ...subscriptions );
}
dispose () {
this._disposable.dispose();
}
}