86 lines
3.2 KiB
JavaScript
86 lines
3.2 KiB
JavaScript
// Thin glue around Monaco (+ monaco-vim), both loaded lazily from a CDN.
|
|
const MONACO = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.0/min';
|
|
const VIM = 'https://cdn.jsdelivr.net/npm/monaco-vim@0.4.2/dist/monaco-vim';
|
|
|
|
let monacoReady = null;
|
|
let editor = null;
|
|
let vimMode = null;
|
|
let vimLib = null;
|
|
let statusEl = null;
|
|
let applyCb = null;
|
|
|
|
function loadMonaco() {
|
|
monacoReady ??= new Promise((resolve, reject) => {
|
|
window.MonacoEnvironment = {
|
|
getWorkerUrl: () => URL.createObjectURL(new Blob(
|
|
[`self.MonacoEnvironment={baseUrl:'${MONACO}/'};importScripts('${MONACO}/vs/base/worker/workerMain.js');`],
|
|
{ type: 'text/javascript' })),
|
|
};
|
|
const s = document.createElement('script');
|
|
s.src = MONACO + '/vs/loader.js';
|
|
s.onload = () => {
|
|
require.config({ paths: { vs: MONACO + '/vs' } });
|
|
require(['vs/editor/editor.main'], () => resolve(window.monaco), reject);
|
|
};
|
|
s.onerror = () => { monacoReady = null; reject(new Error('could not load the editor from the CDN')); };
|
|
document.head.appendChild(s);
|
|
});
|
|
return monacoReady;
|
|
}
|
|
|
|
export async function editor_mount(container, status, src, vim, onApply) {
|
|
await loadMonaco();
|
|
if (editor) return;
|
|
statusEl = status;
|
|
applyCb = onApply;
|
|
editor = monaco.editor.create(container, {
|
|
value: src, language: 'rust', theme: 'vs-dark',
|
|
automaticLayout: true, minimap: { enabled: false }, fontSize: 13, tabSize: 4,
|
|
});
|
|
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => applyCb());
|
|
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => applyCb());
|
|
if (vim) await editor_set_vim(true);
|
|
}
|
|
|
|
export function editor_focus() { editor?.focus(); }
|
|
|
|
export function editor_value() { return editor ? editor.getValue() : ''; }
|
|
|
|
export async function editor_set_vim(on) {
|
|
if (!editor) return;
|
|
if (!on) {
|
|
vimMode?.dispose();
|
|
vimMode = null;
|
|
statusEl.textContent = '';
|
|
return;
|
|
}
|
|
vimLib ??= await new Promise((resolve, reject) => {
|
|
require.config({ paths: { 'monaco-vim': VIM } });
|
|
require(['monaco-vim'], resolve, reject);
|
|
});
|
|
if (vimMode) return;
|
|
vimMode = vimLib.initVimMode(editor, statusEl);
|
|
vimLib.VimMode.Vim.defineEx('write', 'w', () => applyCb());
|
|
editor.focus();
|
|
}
|
|
|
|
// Turns Rune diagnostics ("behavior:LINE:COL") into editor markers; empty text clears them.
|
|
export function editor_show_errors(text) {
|
|
if (!editor) return;
|
|
const model = editor.getModel();
|
|
const markers = [];
|
|
for (const block of text.split(/\n(?=error:|warning:)/)) {
|
|
const m = /behavior:(\d+):(\d+)/.exec(block);
|
|
if (!m) continue;
|
|
const line = +m[1], col = +m[2];
|
|
const word = model.getWordAtPosition({ lineNumber: line, column: col });
|
|
markers.push({
|
|
severity: block.startsWith('warning') ? monaco.MarkerSeverity.Warning : monaco.MarkerSeverity.Error,
|
|
message: block.split('\n')[0].replace(/^(error|warning): /, ''),
|
|
startLineNumber: line, startColumn: col,
|
|
endLineNumber: line, endColumn: word ? word.endColumn : col + 1,
|
|
});
|
|
}
|
|
monaco.editor.setModelMarkers(model, 'rune', markers);
|
|
}
|