diff --git a/test/playground.generated/extending-language-services-semantic-tokens-provider-example.html b/test/playground.generated/extending-language-services-semantic-tokens-provider-example.html new file mode 100644 index 0000000000..c680a975df --- /dev/null +++ b/test/playground.generated/extending-language-services-semantic-tokens-provider-example.html @@ -0,0 +1,190 @@ + + + + + + + + + +[<< BACK]
+THIS IS A GENERATED FILE VIA gulp generate-test-samples + +
+ +
+
+ + +
+ + + +
+
+ + + + + + \ No newline at end of file diff --git a/test/playground.generated/index.html b/test/playground.generated/index.html index 77a2dc938d..5f539f2399 100644 --- a/test/playground.generated/index.html +++ b/test/playground.generated/index.html @@ -33,6 +33,7 @@ Extending Language Services > Symbols provider example
Extending Language Services > Folding provider example
Extending Language Services > Hover provider example
+Extending Language Services > Semantic tokens provider example
Extending Language Services > Configure JavaScript defaults
Extending Language Services > Configure JSON defaults diff --git a/website/playground/new-samples/all.js b/website/playground/new-samples/all.js index ec1190e32e..df3f786c98 100644 --- a/website/playground/new-samples/all.js +++ b/website/playground/new-samples/all.js @@ -157,6 +157,12 @@ var PLAY_SAMPLES = [ id: "extending-language-services-hover-provider-example", path: "extending-language-services/hover-provider-example" }, + { + chapter: "Extending Language Services", + name: "Semantic tokens provider example", + id: "extending-language-services-semantic-tokens-provider-example", + path: "extending-language-services/semantic-tokens-provider-example" + }, { chapter: "Extending Language Services", name: "Configure JavaScript defaults", diff --git a/website/playground/new-samples/extending-language-services/semantic-tokens-provider-example/sample.css b/website/playground/new-samples/extending-language-services/semantic-tokens-provider-example/sample.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/website/playground/new-samples/extending-language-services/semantic-tokens-provider-example/sample.html b/website/playground/new-samples/extending-language-services/semantic-tokens-provider-example/sample.html new file mode 100644 index 0000000000..b2e43e28ec --- /dev/null +++ b/website/playground/new-samples/extending-language-services/semantic-tokens-provider-example/sample.html @@ -0,0 +1 @@ +
diff --git a/website/playground/new-samples/extending-language-services/semantic-tokens-provider-example/sample.js b/website/playground/new-samples/extending-language-services/semantic-tokens-provider-example/sample.js new file mode 100644 index 0000000000..790173cac7 --- /dev/null +++ b/website/playground/new-samples/extending-language-services/semantic-tokens-provider-example/sample.js @@ -0,0 +1,146 @@ +/** @type {monaco.languages.SemanticTokensLegend} */ +const legend = { + tokenTypes: [ + 'comment', 'string', 'keyword', 'number', 'regexp', 'operator', 'namespace', + 'type', 'struct', 'class', 'interface', 'enum', 'typeParameter', 'function', + 'member', 'macro', 'variable', 'parameter', 'property', 'label' + ], + tokenModifiers: [ + 'declaration', 'documentation', 'readonly', 'static', 'abstract', 'deprecated', + 'modification', 'async' + ] +}; + +/** @type {(type: string)=>number} */ +function getType(type) { + return legend.tokenTypes.indexOf(type); +} + +/** @type {(modifier: string[]|string|null)=>number} */ +function getModifier(modifiers) { + if (typeof modifiers === 'string') { + modifiers = [modifiers]; + } + if (Array.isArray(modifiers)) { + let nModifiers = 0; + for (let modifier of modifiers) { + nModifier = legend.tokenModifiers.indexOf(modifier); + if (nModifier > -1) { + nModifiers |= (1 << nModifier) >>> 0; + } + } + return nModifiers; + } else { + return 0; + } +} + +const tokenPattern = new RegExp('(?<=\\[)([a-zA-Z]+)((?:\\.[a-zA-Z]+)*)(?=\\])', 'g'); + +monaco.languages.registerDocumentSemanticTokensProvider('plaintext', { + getLegend: function () { + return legend; + }, + provideDocumentSemanticTokens: function (model, lastResultId, token) { + const lines = model.getLinesContent(); + + /** @type {number[]} */ + const data = []; + + let prevLine = 0; + let prevChar = 0; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + for (let match = null; match = tokenPattern.exec(line);) { + // translate token and modifiers to number representations + let type = getType(match[1]); + if (type === -1) { + continue; + } + let modifier = match[2].length + ? getModifier(match[2].split('.').slice(1)) + : 0; + + data.push( + // translate line to deltaLine + i - prevLine, + // for the same line, translate start to deltaStart + prevLine === i ? match.index - prevChar : match.index, + match[0].length, + type, + modifier + ); + + prevLine = i; + prevChar = match.index; + } + } + return { + data: new Uint32Array(data), + resultId: null + }; + }, + releaseDocumentSemanticTokens: function (resultId) { } +}); + +// add some missing tokens +monaco.editor.defineTheme('myCustomTheme', { + base: 'vs', + inherit: true, + rules: [ + { token: 'comment', foreground: 'aaaaaa', fontStyle: 'italic' }, + { token: 'keyword', foreground: 'ce63eb' }, + { token: 'operator', foreground: '000000' }, + { token: 'namespace', foreground: '66afce' }, + + { token: 'type', foreground: '1db010' }, + { token: 'struct', foreground: '0000ff' }, + { token: 'class', foreground: '0000ff', fontStyle: 'bold' }, + { token: 'interface', foreground: '007700', fontStyle: 'bold' }, + { token: 'enum', foreground: '0077ff', fontStyle: 'bold' }, + { token: 'typeParameter', foreground: '1db010' }, + { token: 'function', foreground: '94763a' }, + + { token: 'member', foreground: '94763a' }, + { token: 'macro', foreground: '615a60' }, + { token: 'variable', foreground: '3e5bbf' }, + { token: 'parameter', foreground: '3e5bbf' }, + { token: 'property', foreground: '3e5bbf' }, + { token: 'label', foreground: '615a60' }, + + { token: 'type.static', fontStyle: 'bold' }, + { token: 'class.static', foreground: 'ff0000', fontStyle: 'bold' } + ] +}); + +const editor = monaco.editor.create(document.getElementById("container"), { + value: [ + 'Available token types:', + ' [comment] [string] [keyword] [number] [regexp] [operator] [namespace]', + ' [type] [struct] [class] [interface] [enum] [typeParameter] [function]', + ' [member] [macro] [variable] [parameter] [property] [label]', + '', + 'Available token modifiers:', + ' [type.declaration] [type.documentation] [type.member] [type.static]', + ' [type.abstract] [type.deprecated] [type.modification] [type.async]', + '', + 'Some examples:', + ' [class.static.token] [type.static.abstract]', + ' [class.static.token] [type.static]', + '', + ' [struct]', + '', + ' [function.private]', + '', + 'An error case:', + ' [notInLegend]' + ].join('\n'), + language: "plaintext", + theme: 'myCustomTheme', + // semantic tokens provider is disabled by default + 'semanticHighlighting.enabled': true +}); + +