// Blazor bridge for Monaco editor. // Exposes window.MonacoBlazor with createEditor / setValue / getValue / dispose / setMarkers. // Lazy-loads Monaco's AMD bundle the first time createEditor is called. (function () { const VS_BASE = "/_content/ScadaLink.CentralUI/lib/monaco/vs"; const editors = {}; let readyPromise = null; function ensureLoaded() { if (readyPromise) return readyPromise; readyPromise = new Promise(function (resolve, reject) { const loader = document.createElement("script"); loader.src = VS_BASE + "/loader.js"; loader.onload = function () { // eslint-disable-next-line no-undef require.config({ paths: { vs: VS_BASE } }); // eslint-disable-next-line no-undef require(["vs/editor/editor.main"], function () { registerCSharpProviders(); resolve(); }); }; loader.onerror = function (e) { reject(e); }; document.head.appendChild(loader); }); return readyPromise; } // ---- Roslyn-backed C# language providers -------------------------------- const KIND_MAP = { Method: 0, Field: 4, Property: 9, Event: 10, Class: 6, Module: 8, Variable: 4, Text: 18 }; function registerCSharpProviders() { // Completion: triggered on ".", "(", "\"" and on demand (Ctrl-Space). monaco.languages.registerCompletionItemProvider("csharp", { triggerCharacters: [".", "(", "\""], provideCompletionItems: async function (model, position) { try { const resp = await fetch("/api/script-analysis/completions", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ codeText: model.getValue(), line: position.lineNumber, column: position.column }) }); if (!resp.ok) return { suggestions: [] }; const data = await resp.json(); const word = model.getWordUntilPosition(position); const range = { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: word.startColumn, endColumn: word.endColumn }; return { suggestions: (data.items || []).map(function (it) { return { label: it.label, insertText: it.insertText, detail: it.detail, kind: KIND_MAP[it.kind] != null ? KIND_MAP[it.kind] : 18, range: range }; }) }; } catch (e) { return { suggestions: [] }; } } }); } async function fetchDiagnostics(code) { try { const resp = await fetch("/api/script-analysis/diagnostics", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ code: code }) }); if (!resp.ok) return []; const data = await resp.json(); return data.markers || []; } catch (e) { return []; } } async function createEditor(id, host, options, dotNetRef) { await ensureLoaded(); if (!host) return; const editor = monaco.editor.create(host, { value: options.value || "", language: options.language || "csharp", theme: "vs", minimap: { enabled: false }, scrollBeyondLastLine: false, automaticLayout: true, fontSize: 13, lineNumbers: "on", renderLineHighlight: "line", readOnly: !!options.readOnly, tabSize: 4, insertSpaces: true, wordWrap: "off", fixedOverflowWidgets: true }); let diagTimer = null; const scheduleDiagnostics = function () { if (diagTimer) clearTimeout(diagTimer); diagTimer = setTimeout(async function () { const markers = await fetchDiagnostics(editor.getValue()); const model = editor.getModel(); if (model) monaco.editor.setModelMarkers(model, "scadalink", markers); }, 500); }; editor.onDidChangeModelContent(function () { const value = editor.getValue(); dotNetRef.invokeMethodAsync("OnValueChanged", value).catch(function () {}); if (options.language === "csharp") scheduleDiagnostics(); }); editors[id] = { editor: editor, dotNetRef: dotNetRef }; // Run an initial diagnostic pass so existing scripts show their markers. if (options.language === "csharp") scheduleDiagnostics(); } function setValue(id, value) { const entry = editors[id]; if (!entry) return; if (entry.editor.getValue() !== value) { entry.editor.setValue(value || ""); } } function getValue(id) { const entry = editors[id]; return entry ? entry.editor.getValue() : null; } function setMarkers(id, markers) { const entry = editors[id]; if (!entry) return; const model = entry.editor.getModel(); if (!model) return; monaco.editor.setModelMarkers(model, "scadalink", markers || []); } function dispose(id) { const entry = editors[id]; if (!entry) return; try { entry.editor.dispose(); } catch (e) {} delete editors[id]; } window.MonacoBlazor = { createEditor: createEditor, setValue: setValue, getValue: getValue, setMarkers: setMarkers, dispose: dispose }; })();