Big delay for display highlighting with SemanticTokensProvider
See original GitHub issueReproducible in vscode.dev or in VS Code Desktop?
- Not reproducible in vscode.dev or VS Code Desktop
Reproducible in the monaco editor playground?
- Not reproducible in the monaco editor playground
Monaco Editor Playground Code
Expand Monaco Editor Playground Code
/** @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) {
const 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();
console.time('highlight')
/** @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;
}
}
console.timeEnd('highlight')
return {
data: new Uint32Array(data),
resultId: null
};
},
releaseDocumentSemanticTokens: function (resultId) {}
});
// add some missing tokens
monaco.editor.defineTheme('myCustomTheme', {
base: 'vs',
inherit: true,
colors: {},
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
});
setInterval(() => {
editor.getModel().setValue(`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]`)
}, 1000)
Actual Behavior
Way to reproduce it without code changing on monaco example semanticTokensProvider
Way with synthetic setValue https://user-images.githubusercontent.com/56319745/146181894-da3392c3-0c67-4e52-9ed0-59b9813e30f1.mov
Actually time of recalculating new tokens very small, but highlighting has a delay for displaying and that’s visible to the eye
Expected Behavior
MonarchTokensProvider has a delay noticeably less
As we see on video, we can’t hardly notice that the value has changed
Code:
Expand Monarch Code
// Register a new language
monaco.languages.register({ id: 'mySpecialLanguage' });
// Register a tokens provider for the language
monaco.languages.setMonarchTokensProvider('mySpecialLanguage', {
tokenizer: {
root: [
[/\[error.*/, 'custom-error'],
[/\[notice.*/, 'custom-notice'],
[/\[info.*/, 'custom-info'],
[/\[[a-zA-Z 0-9:]+\]/, 'custom-date']
]
}
});
// Define a new theme that contains only rules that match this language
monaco.editor.defineTheme('myCoolTheme', {
base: 'vs',
inherit: false,
rules: [
{ token: 'custom-info', foreground: '808080' },
{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' },
{ token: 'custom-notice', foreground: 'FFA500' },
{ token: 'custom-date', foreground: '008800' }
],
colors: {
'editor.foreground': '#000000'
}
});
// Register a completion item provider for the new language
monaco.languages.registerCompletionItemProvider('mySpecialLanguage', {
provideCompletionItems: () => {
var suggestions = [
{
label: 'simpleText',
kind: monaco.languages.CompletionItemKind.Text,
insertText: 'simpleText'
},
{
label: 'testing',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'testing(${1:condition})',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
},
{
label: 'ifelse',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: ['if (${1:condition}) {', '\t$0', '} else {', '\t', '}'].join('\n'),
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'If-Else Statement'
}
];
return { suggestions: suggestions };
}
});
const editor = monaco.editor.create(document.getElementById('container'), {
theme: 'myCoolTheme',
value: getCode(),
language: 'mySpecialLanguage'
});
function getCode() {
return [
'[Sun Mar 7 16:02:00 2004] [notice] Apache/1.3.29 (Unix) configured -- resuming normal operations',
'[Sun Mar 7 16:02:00 2004] [info] Server built: Feb 27 2004 13:56:37',
'[Sun Mar 7 16:02:00 2004] [notice] Accept mutex: sysvsem (Default: sysvsem)',
'[Sun Mar 7 16:05:49 2004] [info] [client xx.xx.xx.xx] (104)Connection',
'[Sun Mar 7 16:45:56 2004] [info] [client xx.xx.xx.xx] (104)Connection ',
'[Sun Mar 7 17:13:50 2004] [info] [client xx.xx.xx.xx] (104)Connection ',
'[Sun Mar 7 17:21:44 2004] [info] [client xx.xx.xx.xx] (104)Connection ',
'[Sun Mar 7 17:23:53 2004] statistics: Use of uninitialized ',
"[Sun Mar 7 17:23:53 2004] statistics: Can't create file /home/httpd/twiki/data/Main/WebStatistics.txt - Permission denied",
'<11>httpd[25859]: [error] [client xx.xx.xx.xx]'
].join('\n');
}
setInterval(() => editor.getModel().setValue(getCode()), 1000)
Additional Context
By the way that’s noticeable when data coming from outside and we need to update editor value, not by user editing.
Is there any way to do something about this?
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:6 (3 by maintainers)
Top Results From Across the Web
Reduce delay on semantic tokens provider pull at moment file ...
From #86168, I understand the reason for a delay while typing (which is hardly noticeable), but could this not be reduced for the...
Read more >Syntax Highlight Guide | Visual Studio Code Extension API
Syntax highlighting determines the color and style of source code displayed in the Visual Studio Code editor. It is responsible for colorizing keywords...
Read more >Performance - LSP Mode - LSP support for Emacs
Use M-x lsp-doctor to validate if your lsp-mode is properly configured. ... Benchmarks show that Emacs 27 is ~15 times faster than Emacs...
Read more >15 Great Moments in Rain Delay History - Bleacher Report
We've seen plenty of great moments during a rain delay, along with a number of ways to pass the time, but these are...
Read more >Monaco editor custom token provider behaviour - Stack Overflow
Thanks, I'd already tried that a while ago, but the problem is that there's a pretty noticeable delay between typing and highlighting, even...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@aaliakseyenka There’s no fundamental reason. It’s just a heuristic which we have in almost all async providers where non-explicit (i.e. automatic) provider requests are made 300ms or 500ms after typing stops, to avoid invoking the providers too often (i.e. with each keystroke). In this particular case, the constants are:
I wonder why do you want this? Are you perhaps having a language where you only register a semantic tokens provider and don’t register a tokens provider ?
@alexdima thanks for doc update - that’s helpful