// ==UserScript==
// @name C3 Monaco Custom Syntax Highlighting - construct.net
// @namespace Violentmonkey Scripts
// @match https://editor.construct.net/*
// @grant none
// @version 1.0
// @author Clovelt
// @description 25/2/2025, 17:02:33
// @license MIT
// ==/UserScript==
const myCustomTokenizer = {
defaultToken: 'invalid',
keywords: [
'text', 'choice', 'wait', 'goto', 'comment',
'ShakeScreen', 'Overlay', 'psychicEffect', 'tweenInteractable', 'setLayoutEffect', 'setLayerEffect',
'setAnim', 'fadeInteractable', 'setSpriteAnim', 'invertUI', 'interactablesSetShake', 'interactablesShake', 'interactablesGlow', 'interactablesGlowColor',
'playSound', 'playMusic', 'setAudioEffect',
'dialog.toggleShow', 'toggleLayerVisible', 'goToLayout',
'setDialogLines', 'saveGame', 'questTrackerAdd', 'questTrackerRemove',
'pan', 'panInt', 'panBack'
],
operators: [
'=', '<', '<=', '>', '>=', '==', '!=', '?', ':', '|', '&'
],
symbols: /[=<>!?|&]+/,
tokenizer: {
root: [
[/^#.*/, 'keyword'], // Secciones en negrita
[/^:.*/, 'comment'], // Comentarios (líneas que comienzan con ":")
[/^(\w+)(?=\s*:)/, 'variable.parameter'], // Nombres de hablantes en azul
[/\b[a-zA-Z_]\w*(?=\s*\()/, 'keyword'], // Funciones en color diferente
[/\b[a-zA-Z_]\w*\b/, 'identifier'], // Identificadores generales (evita conflicto con funciones)
[/\(/, { token: 'delimiter.parenthesis', bracket: '@open' }],
[/\)/, { token: 'delimiter.parenthesis', bracket: '@close' }],
[/"([^"\\]|\\.)*"/, 'string.special'], // Cadenas en funciones en naranja
[/"/, { token: 'string.quote', bracket: '@open', next: '@string' }],
[/\/\*/, 'comment', '@comment'],
[/\/\/.*$/, 'comment'],
[/[^:\n]+/, ''], // Diálogo normal sin formato especial
],
comment: [
[/[^*]+/, 'comment'],
[/\*\//, 'comment', '@pop'],
[/./, 'comment']
],
string: [
[/[^\\"]+/, 'string'],
[/\\./, 'string.escape'],
[/"/, { token: 'string.quote', bracket: '@close', next: '@pop' }]
],
whitespace: [
[/[ \t\r\n]+/, 'white'],
[/\/\*/, 'comment', '@comment'],
[/\/\/.*$/, 'comment']
],
},
};
const waitForMonaco = setInterval(() => {
if (typeof MonacoEnvironment !== 'undefined' && MonacoEnvironment.monaco) {
clearInterval(waitForMonaco); // Stop checking once Monaco is ready
(async () => {
const monaco = MonacoEnvironment.monaco;
// Retrieve all languages
const allLangs = await monaco.languages.getLanguages();
const jsLangDef = allLangs.find(({ id }) => id === 'javascript');
if (!jsLangDef) {
console.error("JavaScript language not found!");
return;
}
// Load the language configuration and tokenizer
const { conf, language: jsLang } = await jsLangDef.loader();
if (!jsLang) {
console.error("Failed to load JavaScript language configuration.");
return;
}
// Apply the stored tokenizer
for (let key in myCustomTokenizer) {
const value = myCustomTokenizer[key];
if (key === 'tokenizer') {
for (let category in value) {
const tokenDefs = value[category];
if (!jsLang.tokenizer.hasOwnProperty(category)) {
jsLang.tokenizer[category] = [];
}
if (Array.isArray(tokenDefs)) {
jsLang.tokenizer[category].unshift(...tokenDefs);
}
}
} else if (Array.isArray(value)) {
if (!jsLang.hasOwnProperty(key)) {
jsLang[key] = [];
}
jsLang[key].unshift(...value);
}
}
console.log("Custom tokenizer applied successfully!");
})();
}
}, 100); // Check every 100ms until Monaco is available