let editor = null;
let currentFileType = 'module';
export async function initEditor(containerId) {
return new Promise((resolve, reject) => {
if (typeof require === 'undefined') {
console.error('Monaco Editor loader not found!');
reject(new Error('Monaco Editor loader not found'));
return;
}
require.config({
paths: {
'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs'
}
});
require(['vs/editor/editor.main'], function() {
registerVB6Language();
editor = monaco.editor.create(document.getElementById(containerId), {
value: getDefaultCode(),
language: 'vb6',
theme: getCurrentTheme(),
automaticLayout: true,
fontSize: 14,
lineNumbers: 'on',
minimap: {
enabled: true
},
scrollBeyondLastLine: false,
wordWrap: 'on',
formatOnPaste: true,
formatOnType: true,
tabSize: 4,
insertSpaces: true,
matchBrackets: 'always'
});
editor.onDidChangeModelContent(() => {
updateEditorStats();
handleCodeChange();
});
editor.onDidChangeCursorPosition((e) => {
updateCursorPosition(e.position);
const event = new CustomEvent('editorCursorPositionChange', {
detail: {
lineNumber: e.position.lineNumber,
column: e.position.column
}
});
document.dispatchEvent(event);
});
console.log('✅ Monaco Editor initialized');
resolve(editor);
});
});
}
function registerVB6Language() {
monaco.languages.register({ id: 'vb6' });
monaco.languages.setMonarchTokensProvider('vb6', {
keywords: ["AdressOf", "Access", "Alias", "As",
"AppActivate", "Append", "Attribute",
"Base", "Beep", "Begin", "Binary", "Boolean",
"ByRef", "Byte", "ByVal", "Call", "Case",
"ChDir", "ChDrive", "Class", "Close",
"Compare", "Const", "Currency", "Date",
"Decimal", "Declare", "DefBool", "DefByte",
"DefCur", "DefDate", "DefDbl", "DefDec",
"DefInt", "DefLng", "DefObj", "DefSng",
"DefStr", "DefVar", "DeleteSetting",
"Dim", "Double", "Do", "Each",
"ElseIf", "Else", "Empty", "End",
"Enum", "Erase", "Error",
"Event", "Exit", "Explicit", "False",
"FileCopy", "For", "Friend", "Function",
"Get", "GoSub", "Goto", "If", "Implements",
"Input", "Integer", "In", "Is",
"Kill", "Len", "Let", "Lib",
"Line", "Lock", "Load", "Unload", "Long",
"Loop", "LSet", "Me", "Mid",
"MidB", "MkDir", "Module",
"Name", "New", "Next",
"Output", "Null", "Object",
"On", "Open", "Optional", "Option",
"ParamArray", "Preserve", "Print",
"Private", "Property", "Public", "Put",
"RaiseEvent", "Random", "Randomize",
"Read", "ReDim", "Reset", "Resume",
"Return", "RmDir", "RSet", "SavePicture",
"SaveSetting", "Seek", "Select",
"SendKeys", "SetAttr", "Set", "Single",
"Static", "Step", "Stop", "String",
"Sub", "Text", "Database", "Then",
"Time", "To", "True", "Type", "Unlock",
"Until", "Variant", "Version", "Wend",
"While", "Width", "WithEvents", "With",
"Write",
],
operators: [
'=', '>', '<', '<=', '>=', '<>', '+', '-', '*', '/', '\\',
'^', '&', 'And', 'Or', 'Not', 'Xor', 'Eqv', 'Imp', 'Mod',
],
ignoreCase: true,
tokenizer: {
root: [
[/'.*$/, 'comment'],
[/^REM\s+.*$/, 'comment'],
[/"([^"\\]|\\.)*$/, 'string.invalid'], [/"/, 'string', '@string'],
[/\b\d+\.?\d*[#!@%&]?\b/, 'number'],
[/&H[0-9A-Fa-f]+/, 'number.hex'],
[/&O[0-7]+/, 'number.octal'],
[/\b(?:Sub|Function|Property|End)\b/, 'keyword.control'],
[/\b(?:If|Then|Else|ElseIf|Select|Case|For|Do|While|Loop|Next|Exit|GoTo|GoSub|On|Resume)\b/, 'keyword.control'],
[/@?[a-zA-Z_]\w*/, {
cases: {
'@keywords': 'keyword',
'@default': 'identifier'
}
}],
[/[=<>!+\-*\/\\^&]/, 'operator'],
[/_$/, 'operator'],
[/[()[\]]/, 'delimiter.bracket'],
[/[,.:;]/, 'delimiter'],
],
string: [
[/[^\\"]+/, 'string'],
[/\"\"/, 'string.escape'],
[/"/, 'string', '@pop']
],
},
});
monaco.editor.defineTheme('vb6-dark', {
base: 'vs-dark',
inherit: true,
rules: [
{ token: 'comment', foreground: '6A9955' },
{ token: 'keyword', foreground: '569CD6' },
{ token: 'keyword.control', foreground: 'C586C0' },
{ token: 'string', foreground: 'CE9178' },
{ token: 'number', foreground: 'B5CEA8' },
{ token: 'operator', foreground: 'D4D4D4' },
],
colors: {}
});
monaco.editor.defineTheme('vb6-light', {
base: 'vs',
inherit: true,
rules: [
{ token: 'comment', foreground: '008000' },
{ token: 'keyword', foreground: '0000FF' },
{ token: 'keyword.control', foreground: 'AF00DB' },
{ token: 'string', foreground: 'A31515' },
{ token: 'number', foreground: '098658' },
{ token: 'operator', foreground: '000000' },
],
colors: {}
});
}
function getDefaultCode() {
return `' VB6Parse Playground
' Enter your VB6 code here and click Parse
Option Explicit
Public Sub HelloWorld()
MsgBox "Hello, World!"
End Sub
`;
}
function getCurrentTheme() {
const theme = document.documentElement.getAttribute('data-theme');
return theme === 'dark' ? 'vb6-dark' : 'vb6-light';
}
export function updateEditorTheme() {
if (editor) {
monaco.editor.setTheme(getCurrentTheme());
}
}
export function getEditorContent() {
return editor ? editor.getValue() : '';
}
export function setEditorContent(code) {
if (editor) {
editor.setValue(code);
}
}
export function clearEditor() {
if (editor) {
editor.setValue('');
}
}
function updateEditorStats() {
if (!editor) return;
const model = editor.getModel();
const lineCount = model.getLineCount();
const charCount = model.getValueLength();
const statsElement = document.getElementById('code-stats');
if (statsElement) {
statsElement.textContent = `Lines: ${lineCount} | Chars: ${charCount}`;
}
}
function updateCursorPosition(position) {
const statusElement = document.getElementById('editor-line-col');
if (statusElement) {
statusElement.textContent = `Ln ${position.lineNumber}, Col ${position.column}`;
}
}
function handleCodeChange() {
const event = new CustomEvent('editorContentChanged');
document.dispatchEvent(event);
}
let currentDecorations = [];
export function highlightRange(startLine, startCol, endLine, endCol) {
if (!editor) return;
currentDecorations = editor.deltaDecorations(currentDecorations, []);
currentDecorations = editor.deltaDecorations([], [
{
range: new monaco.Range(startLine, startCol, endLine, endCol),
options: {
className: 'cst-node-highlight',
isWholeLine: false
}
}
]);
editor.revealRangeInCenter({
startLineNumber: startLine,
startColumn: startCol,
endLineNumber: endLine,
endColumn: endCol
});
}
export function clearHighlight() {
if (!editor) return;
currentDecorations = editor.deltaDecorations(currentDecorations, []);
}
export function setCursorToPosition(line, column) {
if (!editor) return;
editor.setPosition({
lineNumber: line,
column: column
});
editor.revealPositionInCenter({
lineNumber: line,
column: column
});
editor.focus();
}
export function byteOffsetToPosition(byteOffset) {
if (!editor) return { lineNumber: 1, column: 1 };
const model = editor.getModel();
const content = model.getValue();
let currentOffset = 0;
let line = 1;
let column = 1;
for (let i = 0; i < content.length && currentOffset < byteOffset; i++) {
if (content[i] === '\n') {
line++;
column = 1;
} else {
column++;
}
currentOffset++;
}
return { lineNumber: line, column: column };
}
export function setFileType(fileType) {
currentFileType = fileType;
}
export function getEditor() {
return editor;
}
export function saveToLocalStorage() {
if (editor) {
const content = editor.getValue();
localStorage.setItem('vb6parse-playground-code', content);
localStorage.setItem('vb6parse-playground-filetype', currentFileType);
}
}
export function loadFromLocalStorage() {
const content = localStorage.getItem('vb6parse-playground-code');
const fileType = localStorage.getItem('vb6parse-playground-filetype');
if (content) {
setEditorContent(content);
}
if (fileType) {
setFileType(fileType);
}
}