From b8e8ef911399307a07985a6b8cc8e370c48c97e5 Mon Sep 17 00:00:00 2001 From: Adrien Jaguenet Date: Mon, 22 Jun 2026 18:31:23 +0200 Subject: [PATCH] overhaul: modular architecture, tests, doc generation, warning system, and UX improvements - Modular block files: split into blocks-algo, blocks-data, blocks-proc - New modules: sections.js (labelled canvas areas), proc-browser.js (procedure tree modal), lib-loader.js (lazy library loading), templates.js (project templates), errno-data.js (errno table) - Test infrastructure: browser test suite (28 tests) and Node pure-function tests (75 tests) - Warning system: unified .block-warn for disconnected inputs, unconnected error hooks, and zero-exit-in-error-path - Error path highlighting: red flow paths and block borders for error-only subgraphs - Input hooks: hidden by default, shown during linking; outputs always at bottom - Block editor modal: pencil icon, colour picker for accent, compact field labels - Doc generation: standalone node script extracts JSDoc -> docs/API.md (124 declarations) - Persistence: save/restore sections, errno selection, block colours - Fixes: _segmentsIntersect bug, WCEnd crash from closest(this), drawpath guard on detached elements --- ARCHITECTURE.md | 11 +- app.js | 439 +++++----- blocks-algo.js | 180 +++- blocks-data.js | 305 +++++-- blocks-proc.js | 427 ++++++++- docs/API.md | 1132 ++++++++++++++++++++++++ draw.js | 63 +- errno-data.js | 137 +++ error.js | 8 +- helpers.js | 349 ++++++-- icons/bash.svg | 1 + icons/stop-circle.svg | 4 + index.html | 189 +++- lib-icons/c.svg | 4 + lib-icons/gl.svg | 1 + lib-icons/glad.svg | 4 + lib-icons/glfw.svg | 4 + lib-icons/sdl3.svg | 4 + lib-loader.js | 136 +++ libraries/gl.json | 136 +++ libraries/glad.json | 10 + libraries/glfw.json | 46 + libraries/highlight/c.min.js | 40 + libraries/highlight/default.min.css | 9 + libraries/highlight/highlight.min.js | 1213 ++++++++++++++++++++++++++ libraries/index.json | 11 + libraries/math.json | 88 ++ libraries/sdl3.json | 238 +++++ libraries/stdio.json | 40 + libraries/stdlib.json | 67 ++ libraries/string.json | 18 + libraries/time.json | 18 + persist.js | 101 ++- proc-browser.js | 360 ++++++++ scripts/generate-docs.js | 158 ++++ sections.js | 667 ++++++++++++++ server.js | 42 + shell.nix | 54 ++ style.css | 908 +++++++++++++++++-- templates.js | 455 ++++++++++ tests/app/test-runner.js | 566 ++++++++++++ tests/node/test-pure.js | 674 ++++++++++++++ wcblock.js | 176 +++- wcprogram.js | 167 ++-- 44 files changed, 9064 insertions(+), 596 deletions(-) create mode 100644 docs/API.md create mode 100644 errno-data.js create mode 100644 icons/bash.svg create mode 100644 icons/stop-circle.svg create mode 100644 lib-icons/c.svg create mode 100644 lib-icons/gl.svg create mode 100644 lib-icons/glad.svg create mode 100644 lib-icons/glfw.svg create mode 100644 lib-icons/sdl3.svg create mode 100644 lib-loader.js create mode 100644 libraries/gl.json create mode 100644 libraries/glad.json create mode 100644 libraries/glfw.json create mode 100644 libraries/highlight/c.min.js create mode 100644 libraries/highlight/default.min.css create mode 100644 libraries/highlight/highlight.min.js create mode 100644 libraries/index.json create mode 100644 libraries/math.json create mode 100644 libraries/sdl3.json create mode 100644 libraries/stdio.json create mode 100644 libraries/stdlib.json create mode 100644 libraries/string.json create mode 100644 libraries/time.json create mode 100644 proc-browser.js create mode 100644 scripts/generate-docs.js create mode 100644 sections.js create mode 100644 server.js create mode 100644 shell.nix create mode 100644 templates.js create mode 100644 tests/app/test-runner.js create mode 100644 tests/node/test-pure.js diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9839f83..1246e97 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -98,6 +98,7 @@ Collectively they define two class hierarchies: - Two-click hook link system (`connectedCallback` → `_setupHook(hook)` per hook button) - Flow tracking (`wcForeFlow`, `wcNextFlow` arrays of SVG path elements) - Code generation (`generateCode()`, `generateNextCode()`) +- Validation via `_require(selector)` — throws a descriptive `Error` if a required child element is missing from the template, failing early instead of producing `Cannot read properties of null` - Selection on click (`.selected` class, blue box-shadow ring) - Deletion via X button (top-right, shown on hover) or Delete key - X button omitted for `WCStart` / `WCEnd`; their `delete()` shows an error @@ -113,8 +114,9 @@ Collectively they define two class hierarchies: | `WCUnit` | SI unit definition | none (inert hook) | unit comment | | `WCBoolean` | Boolean variable | none (inert hook) | `int name = 0/1;` | | `WCScalar` | Scalar variable with quantity + unit | none (inert hook) | `type name = value;` | +| `WCVector` | N-dimensional vector with quantity + unit | none (inert hook) | `type name[N] = {v0, v1, ...};` | | `WCString` | String variable (char* + length) | none (inert hook) | `string name = {"text", len};` | -| `WCArray` | Typed C array | none | `type name[] = {values};` | +| `WCArray` | Typed C array (supports vector stride) | none | `type name[size];` | | `WCVarRef` | Read-only variable reference in algorithm view | 1 data out | `""` (visual only) | | `WCExpression` | MathJax-rendered expression | dynamic data-in (per variable), 1 data out | `double name = formula;` | | `WCProcedure` | Procedure definition with typed params | none (inert hook) | `""` (comments only) | @@ -124,7 +126,7 @@ Collectively they define two class hierarchies: - `build()` — creates mandatory START/END blocks - `makeNode(tag)` — deep-clones a template by registry lookup, assigns unique ID - `newNode(tag, parent)` — creates a node and appends to the given parent (or registry default) -- Convenience methods: `newDecisionNode`, `newExpressionNode`, `newVarRefNode`, `newOutputNode`, `newProcCallNode`, `newUnitNode`, `newQuantityNode`, `newBooleanNode`, `newScalarNode`, `newStringNode`, `newArrayNode`, `newProcedureNode` +- Convenience methods: `newDecisionNode`, `newExpressionNode`, `newVarRefNode`, `newOutputNode`, `newProcCallNode`, `newUnitNode`, `newQuantityNode`, `newBooleanNode`, `newScalarNode`, `newVectorNode`, `newStringNode`, `newArrayNode`, `newProcedureNode` - `generateCode()` — traverses all blocks and produces complete C source ### `persist.js` — Workspace persistence (localStorage) @@ -258,12 +260,13 @@ WCExpression uses MathJax v3 (`tex-mml-chtml.js`) for client-side LaTeX renderin - **Fallback**: If MathJax is unavailable, `.expr-rendered` shows the raw LaTeX text - **Persistence**: Only the LaTeX string is saved/restored; variable hooks are regenerated from it on load -## Data Type System (WCBoolean / WCScalar / WCString) +## Data Type System (WCBoolean / WCScalar / WCVector / WCString) -Variable blocks come in three distinct types, **not** interchangeable: +Variable blocks come in four distinct types, **not** interchangeable: - **WCBoolean** (`wc-boolean`): A boolean variable. Has a name and a true/false value selector. No unit, no SI conversion, no quantity dropdown. Generates `int name = 0/1;`. - **WCScalar** (`wc-scalar`): A typed scalar variable. Has a name, numeric value, unit dropdown, and quantity selector with SI conversion display. Generates `quantityName name = siValue;`. +- **WCVector** (`wc-vector`): An N-dimensional vector of scalars. Has a name, dimension N, space-separated values, unit dropdown, and quantity selector with SI conversion display. Generates `type name[N] = {v0, v1, ...};`. Data hook type reports as `"scalar"` (varref outputs element values). Index-aware: WCAssign with an index hook assigns to `name[idx]`; without an index hook assigns to `name[0]` or broadcasts via loop. - **WCString** (`wc-string`): A string variable. Has a name and a text value. Generates `string name = {"text", length};`. The `string` struct (`{char *data; long int length;}`) is emitted once before variable declarations when any string blocks exist. Unlike the previous unified `WCDatum` block, there is no way to switch between types — they are separate custom elements with distinct templates and classes. diff --git a/app.js b/app.js index 9b60846..4de3557 100644 --- a/app.js +++ b/app.js @@ -69,13 +69,8 @@ function createDefaultMeasurements() { { fullname: "katal", type: "catalytic_activity", suffix: "kat", si: "1" }, ]; - const COLS = 4, COL_W = 250, ROW_H = 190, PAD = 10; - const typeRows = Math.ceil(typeDefs.length / COLS); - - const typeTags = typeDefs.map((t, i) => { + const typeTags = typeDefs.map(t => { const el = prog.newQuantityNode(); - el.style.left = PAD + (i % COLS) * COL_W + "px"; - el.style.top = PAD + Math.floor(i / COLS) * ROW_H + "px"; el.querySelector(".quantity-name").value = t.name; for (let j = 0; j < 7; j++) { el.querySelector(`.dim-${j}`).value = t.dims[j]; @@ -84,10 +79,15 @@ function createDefaultMeasurements() { return el; }); - const unitTags = unitDefs.map((u, i) => { + /* Sub-header separating quantities from units */ + const unitHeader = document.createElement("div"); + unitHeader.className = "list-header"; + unitHeader.style.borderTop = "2px solid var(--border)"; + unitHeader.textContent = "Units — Fullname   Type   Suffix   SI Factor   Resolved"; + $id("data-area").appendChild(unitHeader); + + const unitTags = unitDefs.map(u => { const el = prog.newUnitNode(); - el.style.left = PAD + (i % COLS) * COL_W + "px"; - el.style.top = PAD + (typeRows + Math.floor(i / COLS)) * ROW_H + "px"; el.querySelector(".unit-fullname").value = u.fullname; el.querySelector(".unit-type").value = u.type; el.querySelector(".unit-suffix").value = u.suffix; @@ -116,154 +116,47 @@ function createDefaultMeasurements() { * @returns {HTMLElement} the #procedures-area element inside the new pane */ function addLibTab(libName, makeActive) { + const tabBar = document.querySelector(".lib-tab-bar"); + if (!tabBar) { showError("Missing .lib-tab-bar element"); return null; } + const paneContainer = $id("pane-procedures"); + if (!paneContainer) { showError("Missing #pane-procedures element"); return null; } + const tab = document.createElement("button"); tab.className = "lib-tab" + (makeActive ? " active" : ""); tab.dataset.libName = libName; tab.textContent = libName; tab.addEventListener("click", () => switchLibTab(libName)); - document.querySelector(".lib-tab-bar").appendChild(tab); + tabBar.appendChild(tab); const pane = document.createElement("div"); pane.className = "lib-pane" + (makeActive ? " active" : ""); pane.dataset.libName = libName; + const hdr = document.createElement("div"); + hdr.className = "list-header"; + hdr.textContent = "Procedure \u00a0 Inputs \u2192 Outputs"; + pane.appendChild(hdr); const area = document.createElement("div"); area.className = "procedures-area"; pane.appendChild(area); - $id("pane-procedures").appendChild(pane); + paneContainer.appendChild(pane); return area; } -// ── Default library creation ────────────────────────────────────────────────── - /** - * Creates the default "stdio" library with a "puts" procedure - * (one string input param, no outputs). + * Creates library tabs from the index, then lazy-loads procedures on first click. */ -function createDefaultLibraries() { - const prog = $id("main-program"); - const libName = "stdio"; - addLibTab(libName, true); - const proc = prog.newProcedureNode(libName); - if (proc) { - proc.querySelector(".proc-name").value = "puts"; - proc._addParamRow(proc.querySelector(".proc-inputs"), "in"); - const row = proc.querySelector(".proc-inputs .proc-param-row"); - if (row) { - row.querySelector(".proc-param-name").value = "str"; - row.querySelector(".proc-param-type").value = "string"; - } +async function initLibraries() { + const index = await loadLibraryIndex(); + let first = true; + for (const entry of index) { + addLibTab(entry.id, first); + first = false; } - refreshProcOptions(); -} - - -// ── OpenGL default procedures ───────────────────────────────────────────────── - -/** - * Creates param rows on a procedure block and sets their name/type values. - * Follows the same pattern as WCProcedure.restoreData(). - * @param {HTMLElement} proc - a wc-procedure element - * @param {[string, string][]} inputs - array of [name, type] pairs - * @param {[string, string][]} outputs - array of [name, type] pairs - */ -function _addProcParams(proc, inputs, outputs) { - for (const [name, type] of inputs) { - proc._addParamRow(proc.querySelector(".proc-inputs"), "in"); - const rows = proc.querySelectorAll(".proc-inputs .proc-param-row"); - const last = rows[rows.length - 1]; - last.querySelector(".proc-param-name").value = name; - last.querySelector(".proc-param-type").value = type; - } - for (const [name, type] of outputs) { - proc._addParamRow(proc.querySelector(".proc-outputs"), "out"); - const rows = proc.querySelectorAll(".proc-outputs .proc-param-row"); - const last = rows[rows.length - 1]; - last.querySelector(".proc-param-name").value = name; - last.querySelector(".proc-param-type").value = type; - } -} - -/** - * Creates default OpenGL procedure definitions organised into three libraries: - * - * glfw — GLFW window management (init, create, swap, poll, close, terminate) - * glad — OpenGL loader initialisation (gladLoadGL) - * gl — OpenGL core functions for a Hello Triangle pipeline - * (shader compile/link, buffer/VAO setup, draw, clear, cleanup) - * - * Also creates a default "vertices" float array with Hello Triangle vertex data - * in the Data tab so the user has a starting point. - * - * Code generation note: - * Output parameters are passed with a `&` prefix in the generated C code - * (e.g. `glCreateShader(type, &shader);`). For OpenGL functions that - * natively return values rather than taking pointer outputs, the generated - * code uses this convention and may need a thin wrapper. - */ -function createDefaultOpenGLProcedures() { - const prog = $id("main-program"); - - const PROC_DEFS = [ - // ── glfw: window management ── - { lib: "glfw", name: "glfwInit", inputs: [], outputs: [["result","int"]] }, - { lib: "glfw", name: "glfwCreateWindow", inputs: [["width","int"],["height","int"],["title","string"]], outputs: [["window","int"]] }, - { lib: "glfw", name: "glfwMakeContextCurrent", inputs: [["window","int"]], outputs: [] }, - { lib: "glfw", name: "glfwSwapBuffers", inputs: [["window","int"]], outputs: [] }, - { lib: "glfw", name: "glfwPollEvents", inputs: [], outputs: [] }, - { lib: "glfw", name: "glfwWindowShouldClose", inputs: [["window","int"]], outputs: [["result","int"]] }, - { lib: "glfw", name: "glfwTerminate", inputs: [], outputs: [] }, - // ── glad: OpenGL loader ── - { lib: "glad", name: "gladLoadGL", inputs: [], outputs: [["result","int"]] }, - // ── gl: OpenGL core Hello Triangle ── - { lib: "gl", name: "glCreateShader", inputs: [["type","unsigned int"]], outputs: [["shader","unsigned int"]] }, - { lib: "gl", name: "glShaderSource", inputs: [["shader","unsigned int"],["count","int"],["source","string"]], outputs: [] }, - { lib: "gl", name: "glCompileShader", inputs: [["shader","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glGetShaderiv", inputs: [["shader","unsigned int"],["pname","unsigned int"]], outputs: [["params","int"]] }, - { lib: "gl", name: "glCreateProgram", inputs: [], outputs: [["program","unsigned int"]] }, - { lib: "gl", name: "glAttachShader", inputs: [["program","unsigned int"],["shader","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glLinkProgram", inputs: [["program","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glGetProgramiv", inputs: [["program","unsigned int"],["pname","unsigned int"]], outputs: [["params","int"]] }, - { lib: "gl", name: "glGenVertexArrays", inputs: [["n","unsigned int"]], outputs: [["arrays","unsigned int"]] }, - { lib: "gl", name: "glBindVertexArray", inputs: [["array","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glGenBuffers", inputs: [["n","unsigned int"]], outputs: [["buffers","unsigned int"]] }, - { lib: "gl", name: "glBindBuffer", inputs: [["target","unsigned int"],["buffer","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glBufferData", inputs: [["target","unsigned int"],["size","int"],["data","string"],["usage","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glVertexAttribPointer", inputs: [["index","unsigned int"],["size","int"],["type","unsigned int"],["normalized","int"],["stride","int"],["pointer","int"]], outputs: [] }, - { lib: "gl", name: "glEnableVertexAttribArray", inputs: [["index","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glUseProgram", inputs: [["program","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glDrawArrays", inputs: [["mode","unsigned int"],["first","int"],["count","int"]], outputs: [] }, - { lib: "gl", name: "glClear", inputs: [["mask","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glClearColor", inputs: [["red","float"],["green","float"],["blue","float"],["alpha","float"]], outputs: [] }, - { lib: "gl", name: "glDeleteShader", inputs: [["shader","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glDeleteProgram", inputs: [["program","unsigned int"]], outputs: [] }, - { lib: "gl", name: "glViewport", inputs: [["x","int"],["y","int"],["width","int"],["height","int"]], outputs: [] }, - ]; - - // Create each library tab once, then populate procedures - const libsDone = {}; - for (const pd of PROC_DEFS) { - if (!libsDone[pd.lib]) { - libsDone[pd.lib] = true; - addLibTab(pd.lib, false); - } - const proc = prog.newProcedureNode(pd.lib); - if (proc) { - proc.querySelector(".proc-name").value = pd.name; - _addProcParams(proc, pd.inputs, pd.outputs); - } - } - refreshProcOptions(); - - // ── Default vertex array for Hello Triangle ── - const arr = prog.newArrayNode(); - if (arr) { - arr.restoreData({ - name: "vertices", - type: "float", - values: ["-0.5", "-0.5", "0.0", "0.5", "-0.5", "0.0", "0.0", "0.5", "0.0"] - }); + if (index.length > 0) { + switchLibTab(index[0].id); + loadLibrary(index[0].id); } } @@ -279,7 +172,7 @@ function switchTab(name) { btn.classList.toggle("active", btn.dataset.tab === name); } for (const pane of document.querySelectorAll(".tab-pane")) { - pane.classList.toggle("active", pane.id === "pane-" + name); + pane.classList.toggle("active", pane.dataset.tab === name); } } @@ -288,9 +181,10 @@ function switchTab(name) { /** Wires all static button click handlers (replaces inline onclick attributes). */ function setupUI() { - function closeVariableSubmenu() { - const sub = document.querySelector(".submenu[data-action='variable-submenu']"); - if (sub) sub.classList.remove("open"); + function closeAllSubmenus() { + for (const sub of document.querySelectorAll(".submenu.open")) { + sub.classList.remove("open"); + } } /* Toolbar block-creation helpers */ @@ -302,118 +196,210 @@ function setupUI() { array: refreshAll, quantity: refreshAll, unit: refreshAll, - proccall: refreshAll, assign: refreshAll }; function handleBlockAction(btn) { const tag = BLOCK_ACTIONS[btn.dataset.action]; if (!tag) return false; const prog = $id("main-program"); + + /* Procedure call: show browser modal first, create block only on selection */ + if (btn.dataset.action === "proccall") { + showProcedureBrowser("", (qname) => { + if (qname) { + const el = prog.newNode(tag); + if (el) { + el.setProcedure(qname); + refreshAll(); + } + } + }); + return true; + } + const el = prog.newNode(tag); if (el && ACTION_REFRESH[btn.dataset.action]) ACTION_REFRESH[btn.dataset.action](); return true; } /* Algorithm pane toolbar */ - const algoToolbar = $id("pane-algorithm").querySelector(".pane-toolbar"); - algoToolbar.addEventListener("click", e => { - const btn = e.target.closest("button"); - if (btn && handleBlockAction(btn)) return; - }); + const algoToolbarPane = $id("pane-algorithm"); + const algoToolbar = algoToolbarPane && algoToolbarPane.querySelector(".pane-toolbar"); + if (algoToolbar) { + algoToolbar.addEventListener("click", e => { + const btn = e.target.closest("button"); + if (!btn) return; + if (handleBlockAction(btn)) return; + if (btn.dataset.action === "add-section" && typeof enterSectionCreateMode === "function") { + enterSectionCreateMode(); + } + }); + } /* Data pane toolbar */ - const dataToolbar = $id("pane-data").querySelector(".pane-toolbar"); - dataToolbar.addEventListener("click", e => { - const btn = e.target.closest("button"); - if (!btn) return; - const action = btn.dataset.action; - const prog = $id("main-program"); - if (action === "variable") { - const sub = document.querySelector(".submenu[data-action='variable-submenu']"); - if (sub) sub.classList.toggle("open"); - } else if (handleBlockAction(btn)) { - closeVariableSubmenu(); - } - }); + const dataToolbarPane = $id("pane-data"); + const dataToolbar = dataToolbarPane && dataToolbarPane.querySelector(".pane-toolbar"); + if (dataToolbar) { + dataToolbar.addEventListener("click", e => { + const btn = e.target.closest("button"); + if (!btn) return; + const prog = $id("main-program"); + if (btn.dataset.action === "variable") { + const sub = document.querySelector(".submenu[data-action='variable-submenu']"); + if (sub) sub.classList.toggle("open"); + } else if (handleBlockAction(btn)) { + closeAllSubmenus(); + } + }); + } /* Measurements pane toolbar */ - const measToolbar = $id("pane-measurements").querySelector(".pane-toolbar"); - measToolbar.addEventListener("click", e => { - const btn = e.target.closest("button"); - if (btn) handleBlockAction(btn); - }); + const measToolbarPane = $id("pane-measurements"); + const measToolbar = measToolbarPane && measToolbarPane.querySelector(".pane-toolbar"); + if (measToolbar) { + measToolbar.addEventListener("click", e => { + const btn = e.target.closest("button"); + if (btn) handleBlockAction(btn); + }); + } /* Procedures pane toolbar */ - const procToolbar = $id("pane-procedures").querySelector(".pane-toolbar"); - procToolbar.addEventListener("click", e => { - const btn = e.target.closest("button"); + const procToolbarPane = $id("pane-procedures"); + const procToolbar = procToolbarPane && procToolbarPane.querySelector(".pane-toolbar"); + if (procToolbar) { + procToolbar.addEventListener("click", e => { + const btn = e.target.closest("button"); + if (!btn) return; + const prog = $id("main-program"); + + if (btn.dataset.action === "add-library") { + const libName = prompt("Library name:", "mylib"); + if (!libName) return; + markLibLoaded(libName); + addLibTab(libName, false); + switchLibTab(libName); + } + + if (btn.dataset.action === "add-procedure") { + const activeLibPane = document.querySelector(".lib-pane.active"); + if (!activeLibPane) return; + const el = prog.newNode("wc-procedure", activeLibPane.querySelector(".procedures-area")); + } + + if (btn.dataset.action === "add-library-index") { + const sub = document.querySelector(".submenu[data-action='library-index-submenu']"); + if (!sub) return; + const index = getLibraryIndex(); + const available = index.filter(e => !isLibraryLoaded(e.id)); + if (available.length === 0) { + showError("All libraries from the index are already loaded."); + return; + } + sub.innerHTML = available.map(e => + `` + ).join(""); + if (sub.classList.contains("open")) { + sub.classList.remove("open"); + } else { + closeAllSubmenus(); + sub.classList.add("open"); + } + return; + } + }); + } + + /* Library index submenu item click — load selected library */ + document.addEventListener("click", e => { + const btn = e.target.closest(".submenu[data-action='library-index-submenu'] button"); if (!btn) return; - const prog = $id("main-program"); - - if (btn.dataset.action === "add-library") { - const libName = prompt("Library name:", "mylib"); - if (!libName) return; - addLibTab(libName, false); - switchLibTab(libName); - } - - if (btn.dataset.action === "add-procedure") { - const activeLibPane = document.querySelector(".lib-pane.active"); - if (!activeLibPane) return; - const el = prog.newNode("wc-procedure", activeLibPane.querySelector("#procedures-area")); - if (el) refreshProcOptions(); - } + const libId = btn.dataset.libId; + if (!libId) return; + document.querySelector(".submenu[data-action='library-index-submenu']").classList.remove("open"); + addLibTab(libId, false); + loadLibrary(libId); + switchLibTab(libId); }); - /* Lib tab bar click (delegated) */ - document.querySelector(".lib-tab-bar").addEventListener("click", e => { - const tab = e.target.closest(".lib-tab"); - if (tab) switchLibTab(tab.dataset.libName); - }); + /* Lib tab bar click (delegated) — lazy-load libraries on first click */ + const libTabBar = document.querySelector(".lib-tab-bar"); + if (libTabBar) { + libTabBar.addEventListener("click", async e => { + const tab = e.target.closest(".lib-tab"); + if (!tab) return; + const libId = tab.dataset.libName; + if (!isLibraryLoaded(libId)) { + tab.textContent = libId + " \u2026"; + try { + await loadLibrary(libId); + } catch (e) { + tab.textContent = libId; + } + } + switchLibTab(libId); + }); + } /* Global toolbar */ const globalToolbar = document.querySelector(".global-toolbar"); - globalToolbar.addEventListener("click", e => { - const btn = e.target.closest("button"); - if (!btn) return; - if (btn.dataset.action === "generate") $id("main-program").generateCode(); - if (btn.dataset.action === "save") saveWorkspace(); - if (btn.dataset.action === "load") loadWorkspace(); - if (btn.dataset.action === "new") newWorkspace(); - }); + if (globalToolbar) { + globalToolbar.addEventListener("click", e => { + const btn = e.target.closest("button"); + if (!btn) return; + const prog = $id("main-program"); + if (!prog) { showError("Main program element not found"); return; } + if (btn.dataset.action === "generate") prog.generateCode(); + if (btn.dataset.action === "save") saveWorkspace(); + if (btn.dataset.action === "load") loadWorkspace(); + if (btn.dataset.action === "new") newWorkspace(); + if (btn.dataset.action === "copy-code") copyCode(); + if (btn.dataset.action === "run-tests") { + if (window.APP_TEST_SUITE) APP_TEST_SUITE.run(); + } + }); + } /* Tab bar */ - document.querySelector(".tab-bar").addEventListener("click", e => { - const btn = e.target.closest(".tab-btn"); - if (btn) switchTab(btn.dataset.tab); - closeVariableSubmenu(); - }); + const tabBar = document.querySelector(".tab-bar"); + if (tabBar) { + tabBar.addEventListener("click", e => { + const btn = e.target.closest(".tab-btn"); + if (btn) switchTab(btn.dataset.tab); + closeAllSubmenus(); + }); + } - /* Close variable submenu on outside click */ + /* Close all submenus on outside click */ document.addEventListener("click", e => { - if (!e.target.closest(".btn-group")) closeVariableSubmenu(); + if (!e.target.closest(".btn-group")) closeAllSubmenus(); }); /* Click anywhere in draw-area (except hooks, which stopPropagation) cancels pending link */ - $id("draw-area").addEventListener("click", resetLinkage); + const drawArea = $id("draw-area"); + if (drawArea) drawArea.addEventListener("click", resetLinkage); /* Click on an SVG connection path deletes it */ - $id("connections").addEventListener("click", e => { - const path = e.target.closest("path.flow, path.flow-data"); - if (!path) return; - e.stopPropagation(); - for (const el of document.querySelectorAll(".selected")) el.classList.remove("selected"); - resetLinkage(); - deleteLink(path); - }); + const connectionsSvg = $id("connections"); + if (connectionsSvg) { + connectionsSvg.addEventListener("click", e => { + const path = e.target.closest("path.flow, path.flow-data"); + if (!path) return; + e.stopPropagation(); + for (const el of document.querySelectorAll(".selected")) el.classList.remove("selected"); + resetLinkage(); + deleteLink(path); + }); + } /* Error modal dismiss */ - $("#error-overlay").addEventListener("click", closeError); + const errorOverlay = $("#error-overlay"); + if (errorOverlay) errorOverlay.addEventListener("click", closeError); /* Keyboard: Delete key removes selected block */ document.addEventListener("keydown", ev => { if (ev.key !== "Delete" && ev.key !== "Del") return; if (ev.target.tagName === "INPUT" || ev.target.tagName === "TEXTAREA" || ev.target.tagName === "SELECT") return; + if (window.LOGIC_SECTIONS && LOGIC_SECTIONS.editingId) return; const sel = document.querySelector(".selected"); if (sel && typeof sel.delete === "function") { ev.preventDefault(); @@ -438,14 +424,29 @@ function setupUI() { * wires up all UI event listeners. */ function main() { - $id("main-program").build(); + const prog = $id("main-program"); + if (!prog) { showError("Missing #main-program element"); return; } + window.prog = prog; + prog.build(); createDefaultMeasurements(); - createDefaultLibraries(); - createDefaultOpenGLProcedures(); - if (localStorage.getItem(STORAGE_KEY)) { - loadWorkspace(); + + /* Create default vertex array for Hello Triangle */ + const arr = prog.newArrayNode(); + if (arr) { + arr.restoreData({ + name: "vertices", + type: "float", + values: ["-0.5", "-0.5", "0.0", "0.5", "-0.5", "0.0", "0.0", "0.5", "0.0"] + }); } - setupUI(); + + /* Load library index and create tabs (procedures load on demand) */ + initLibraries().then(() => { + if (localStorage.getItem(STORAGE_KEY)) { + loadWorkspace(); + } + setupUI(); + }); } main(); diff --git a/blocks-algo.js b/blocks-algo.js index 65a9ed5..6218f6a 100644 --- a/blocks-algo.js +++ b/blocks-algo.js @@ -15,6 +15,7 @@ // ── WCStart — program entry point ────────────────────────────────────────── +/** Program entry point. Cannot be deleted. */ class WCStart extends WCBlock { delete() { showError("Cannot delete the start block."); } } @@ -22,14 +23,185 @@ class WCStart extends WCBlock { // ── WCEnd — program exit ─────────────────────────────────────────────────── +/** Program exit block with errno autocomplete. At least one must return 0. */ class WCEnd extends WCBlock { - delete() { showError("Cannot delete the end block."); } - generateOwnCode() { return ""; } + connectedCallback() { + super.connectedCallback(); + this._errnoItems = _ERRNO_ITEMS; + this._selectedCode = 0; + this._el.input = this._require(".end-errno-input"); + this._el.suggestions = this._require(".end-errno-suggestions"); + this._el.desc = this._require(".end-desc-text"); + + this._el.input.addEventListener("input", () => this._onInput()); + this._el.input.addEventListener("focus", () => { if (this._el.suggestions.children.length > 0) this._showSuggestions(); }); + this._el.input.addEventListener("keydown", ev => this._onKeyDown(ev)); + this._el.suggestions.addEventListener("mousedown", ev => { + const sug = ev.target.closest(".end-errno-suggestion"); + if (sug) { + const code = parseInt(sug.dataset.code); + if (!isNaN(code)) this._selectByCode(code); + } + }); + document.addEventListener("click", ev => { + if (!this.contains(ev.target)) this._hideSuggestions(); + }); + + this._el.input.value = "0"; + this._selectByCode(0); + } + + _onInput() { + const q = this._el.input.value.trim().toLowerCase(); + if (!q) { + this._renderSuggestions(this._errnoItems); + this._showSuggestions(); + return; + } + const qCode = parseInt(q); + const matches = this._errnoItems.filter(item => + (!isNaN(qCode) && item.code === qCode) || + item.name.toLowerCase().includes(q) || + item.desc.toLowerCase().includes(q) + ); + this._renderSuggestions(matches); + this._showSuggestions(); + } + + _renderSuggestions(items) { + this._el.suggestions.innerHTML = ""; + for (const item of items) this._addSuggestion(item); + } + + _addSuggestion(item) { + const div = document.createElement("div"); + div.className = "end-errno-suggestion"; + div.dataset.code = item.code; + const codeSpan = document.createElement("span"); + codeSpan.className = "end-suggestion-code"; + codeSpan.textContent = item.code + " " + item.name; + div.appendChild(codeSpan); + const descSpan = document.createElement("span"); + descSpan.className = "end-suggestion-desc"; + descSpan.textContent = item.desc; + div.appendChild(descSpan); + this._el.suggestions.appendChild(div); + } + + _showSuggestions() { this._el.suggestions.style.display = "block"; } + _hideSuggestions() { this._el.suggestions.style.display = "none"; } + + _onKeyDown(ev) { + if (ev.key === "ArrowDown" || ev.key === "ArrowUp") { + ev.preventDefault(); + const items = [...this._el.suggestions.querySelectorAll(".end-errno-suggestion")]; + let idx = items.findIndex(el => el.classList.contains("highlighted")); + if (ev.key === "ArrowDown") idx = Math.min(idx + 1, items.length - 1); + else idx = Math.max(idx - 1, 0); + items.forEach(el => el.classList.remove("highlighted")); + if (items[idx]) { + items[idx].classList.add("highlighted"); + items[idx].scrollIntoView({ block: "nearest" }); + } + } + if (ev.key === "Enter") { + const hl = this._el.suggestions.querySelector(".highlighted"); + if (hl) { + ev.preventDefault(); + this._selectByCode(parseInt(hl.dataset.code)); + } + } + if (ev.key === "Escape") this._hideSuggestions(); + } + + _selectByCode(code) { + this._selectedCode = code; + const item = this._errnoItems.find(i => i.code === code); + if (item) { + this._el.input.value = code + " " + item.name; + this._el.desc.textContent = item.desc; + this.dataset.exitError = code !== 0 ? "true" : ""; + } else { + this._el.input.value = code + " error"; + this._el.desc.textContent = "unknown error"; + this.dataset.exitError = "true"; + } + this._hideSuggestions(); + } + + _enforceZeroEnd() { + if (this._selectedCode === 0) return; + const others = document.querySelectorAll("wc-end"); + for (const other of others) { + if (other !== this && other._selectedCode === 0) return; + } + this._selectByCode(0); + } + + _hasOtherZero() { + const others = document.querySelectorAll("wc-end"); + for (const other of others) { + if (other !== this && other._selectedCode === 0) return true; + } + return false; + } + + delete() { + if (this.id === "main-end") { showError("Cannot delete the default end block."); return; } + if (!this._hasOtherZero()) { showError("At least one end block must return 0."); return; } + super.delete(); + setTimeout(() => { + for (const end of document.querySelectorAll("wc-end")) end._enforceZeroEnd(); + }, 0); + } + + _genStringFrees() { + const freed = new Set(); + let code = ""; + for (const el of document.querySelectorAll("#variable-area > [data-block]")) { + if (el.dataType === "string") { + const name = resolveSourceName(el); + if (name && !freed.has(name)) { + freed.add(name); + code += ` free(${name}.data);\n`; + } + } + } + return code; + } + + generateOwnCode() { + const code = this._selectedCode; + if (code === 0) { + const frees = this._genStringFrees(); + if (frees) return `\n${frees} return 0;\n`; + return "\n return 0;\n"; + } + return `\n exit(${code});\n`; + } + + saveData() { return { exitCode: this._selectedCode }; } + restoreData(d) { + this._selectByCode(d.exitCode !== undefined ? d.exitCode : 0); + } } +function _buildErrnoItems() { + const items = [ + { code: 0, name: "success", desc: "success" }, + { code: 255, name: "error", desc: "generic error" }, + ]; + for (const e of ERRNO_LIST) { + items.push({ code: e.code, name: e.name, desc: e.desc }); + } + return items; +} +const _ERRNO_ITEMS = _buildErrnoItems(); + // ── WCOutput — printf statement ──────────────────────────────────────────── +/** Output description block. Provides inline documentation without generating code. */ class WCOutput extends WCBlock { connectedCallback() { super.connectedCallback(); @@ -48,6 +220,7 @@ class WCOutput extends WCBlock { // ── WCDecision — conditional branch ──────────────────────────────────────── +/** Conditional branch block; splits flow into yes/no branches and emits if/else C code. */ class WCDecision extends WCBlock { connectedCallback() { super.connectedCallback(); @@ -129,6 +302,7 @@ class WCDecision extends WCBlock { // ── WCAssign — variable assignment block ─────────────────────────────────── +/** Variable assignment block; writes to scalar/array/vector/string targets with data hooks. */ class WCAssign extends WCBlock { connectedCallback() { super.connectedCallback(); @@ -238,6 +412,7 @@ class WCAssign extends WCBlock { // ── WCVarRef — read-only variable reference with data output hook ────────── +/** Read-only variable reference with data output hook; syncs type from selected variable. */ class WCVarRef extends WCBlock { connectedCallback() { super.connectedCallback(); @@ -271,6 +446,7 @@ class WCVarRef extends WCBlock { // ── WCExpression — mathematical expression with MathJax rendering & data hooks ── +/** Math expression block with LaTeX rendering via MathJax and dynamic variable data hooks. */ class WCExpression extends WCBlock { get dataType() { const el = this._el?.latex || this.querySelector(".expr-latex"); diff --git a/blocks-data.js b/blocks-data.js index 08ba20d..0788f1b 100644 --- a/blocks-data.js +++ b/blocks-data.js @@ -1,7 +1,7 @@ // ── Data & Measurement tab block definitions ─────────────────────────────── // // Blocks that live in the Data tab (variable area) and Measurements tab: -// WCQuantity, WCUnit, WCBoolean, WCScalar, WCString, WCArray +// WCQuantity, WCUnit, WCBoolean, WCScalar, WCVector, WCString, WCArray // // Each extends WCBlock (from wcblock.js) and provides generateOwnCode(). // @@ -16,11 +16,12 @@ // ── WCQuantity — measurement quantity definition ─────────────────────────── +/** Measurement quantity definition: name, 7 SI base dimensions, min/max range, default unit. */ class WCQuantity extends WCBlock { connectedCallback() { super.connectedCallback(); - this._el.name = this.querySelector(".quantity-name"); - this._el.dims = Array.from({ length: 7 }, (_, i) => this.querySelector(`.dim-${i}`)); + this._el.name = this._require(".quantity-name"); + this._el.dims = Array.from({ length: 7 }, (_, i) => this._require(`.dim-${i}`)); this._el.min = this.querySelector(".quantity-min"); this._el.max = this.querySelector(".quantity-max"); this._el.defaultUnit = this.querySelector(".quantity-default-unit"); @@ -34,9 +35,8 @@ class WCQuantity extends WCBlock { } for (const sel of this.querySelectorAll("select")) { sel.addEventListener("change", () => { - for (const d of $id("variable-area").querySelectorAll("wc-scalar")) { - d.updateSiDisplay(); - } + const va = $id("variable-area"); + if (va) for (const d of va.querySelectorAll("wc-scalar")) d.updateSiDisplay(); }); } setTimeout(() => { refreshQuantityOptions(); refreshDefaultUnitOptions(); }, 0); @@ -52,25 +52,26 @@ class WCQuantity extends WCBlock { }; } restoreData(d) { - this.querySelector(".quantity-name").value = d.name || ""; - for (let i = 0; i < 7; i++) this.querySelector(`.dim-${i}`).value = d.dims ? d.dims[i] : "0"; - this.querySelector(".quantity-min").value = d.min || ""; - this.querySelector(".quantity-max").value = d.max || ""; - this.querySelector(".quantity-default-unit").value = d.defaultUnit || ""; + this._el.name.value = d.name || ""; + for (let i = 0; i < 7; i++) { if (this._el.dims[i]) this._el.dims[i].value = d.dims ? d.dims[i] : "0"; } + if (this._el.min) this._el.min.value = d.min || ""; + if (this._el.max) this._el.max.value = d.max || ""; + if (this._el.defaultUnit) this._el.defaultUnit.value = d.defaultUnit || ""; } } // ── WCUnit — unit of measurement ─────────────────────────────────────────── +/** Unit of measurement: name, associated quantity, suffix, SI factor, recursive SI resolution. */ class WCUnit extends WCBlock { connectedCallback() { super.connectedCallback(); - this._el.fullname = this.querySelector(".unit-fullname"); - this._el.type = this.querySelector(".unit-type"); - this._el.suffix = this.querySelector(".unit-suffix"); - this._el.si = this.querySelector(".unit-si"); - this._el.resolved = this.querySelector(".unit-resolved"); + this._el.fullname = this._require(".unit-fullname"); + this._el.type = this._require(".unit-type"); + this._el.suffix = this._require(".unit-suffix"); + this._el.si = this._require(".unit-si"); + this._el.resolved = this._require(".unit-resolved"); for (const inp of this.querySelectorAll("input")) { inp.addEventListener("input", () => { @@ -118,10 +119,10 @@ class WCUnit extends WCBlock { }; } restoreData(d) { - this.querySelector(".unit-fullname").value = d.fullname || ""; - this.querySelector(".unit-type").value = d.type || ""; - this.querySelector(".unit-suffix").value = d.suffix || ""; - this.querySelector(".unit-si").value = d.si || "1"; + this._el.fullname.value = d.fullname || ""; + this._el.type.value = d.type || ""; + this._el.suffix.value = d.suffix || ""; + this._el.si.value = d.si || "1"; } updateSiDisplay() { @@ -135,15 +136,14 @@ class WCUnit extends WCBlock { // ── WCBoolean — boolean variable ─────────────────────────────────────────── +/** Boolean variable: name, true/false value, optional comment. Generates C `int`. */ class WCBoolean extends WCBlock { - get dataType() { return "boolean"; } - connectedCallback() { super.connectedCallback(); - this._el.name = this.querySelector(".var-name"); - this._el.value = this.querySelector(".var-value"); - this._el.comment = this.querySelector(".var-comment"); - this._el.cname = this.querySelector(".var-cname"); + this._el.name = this._require(".var-name"); + this._el.value = this._require(".var-value"); + this._el.comment = this._require(".var-comment"); + this._el.cname = this._require(".var-cname"); this._el.name.addEventListener("input", () => { this._el.cname.textContent = toCName(this._el.name.value); }); @@ -151,9 +151,9 @@ class WCBoolean extends WCBlock { saveData() { return { name: this._el.name.value, value: this._el.value.value, comment: this._el.comment.value }; } restoreData(d) { - this.querySelector(".var-name").value = d.name || ""; - this.querySelector(".var-value").value = d.value || "false"; - this.querySelector(".var-comment").value = d.comment || ""; + this._el.name.value = d.name || ""; + this._el.value.value = d.value || "false"; + this._el.comment.value = d.comment || ""; this._el.cname.textContent = toCName(this._el.name.value); } @@ -169,18 +169,18 @@ class WCBoolean extends WCBlock { // ── WCScalar — typed scalar variable with quantity, unit, and SI conversion ── +/** Typed scalar variable with quantity, unit, SI conversion, and auto-sync between value and unit. */ class WCScalar extends WCBlock { get dataType() { return "scalar"; } - connectedCallback() { super.connectedCallback(); - this._el.name = this.querySelector(".var-name"); - this._el.value = this.querySelector(".var-value"); - this._el.unit = this.querySelector(".var-unit"); - this._el.type = this.querySelector(".var-type"); - this._el.si = this.querySelector(".var-si"); - this._el.comment = this.querySelector(".var-comment"); - this._el.cname = this.querySelector(".var-cname"); + this._el.name = this._require(".var-name"); + this._el.value = this._require(".var-value"); + this._el.unit = this._require(".var-unit"); + this._el.type = this._require(".var-type"); + this._el.si = this._require(".var-si"); + this._el.comment = this._require(".var-comment"); + this._el.cname = this._require(".var-cname"); this._el.name.addEventListener("input", () => { this._el.cname.textContent = toCName(this._el.name.value); @@ -265,11 +265,11 @@ class WCScalar extends WCBlock { }; } restoreData(d) { - this.querySelector(".var-name").value = d.name || ""; - this.querySelector(".var-value").value = d.value || ""; - this.querySelector(".var-type").value = d.type || ""; - this.querySelector(".var-unit").value = d.unit || ""; - this.querySelector(".var-comment").value = d.comment || ""; + this._el.name.value = d.name || ""; + this._el.value.value = d.value || ""; + this._el.type.value = d.type || ""; + this._el.unit.value = d.unit || ""; + this._el.comment.value = d.comment || ""; this._el.cname.textContent = toCName(this._el.name.value); } @@ -285,17 +285,154 @@ class WCScalar extends WCBlock { } -// ── WCString — string variable ───────────────────────────────────────────── - -class WCString extends WCBlock { - get dataType() { return "string"; } +// ── WCVector — N-dimensional vector with quantity, unit, and SI conversion ── +/** N-dimensional vector variable with quantity, unit, SI conversion; generates C array. */ +class WCVector extends WCBlock { + get dataType() { return "vector"; } connectedCallback() { super.connectedCallback(); - this._el.name = this.querySelector(".var-name"); - this._el.value = this.querySelector(".var-value"); - this._el.comment = this.querySelector(".var-comment"); - this._el.cname = this.querySelector(".var-cname"); + this._el.name = this._require(".var-name"); + this._el.dim = this._require(".var-dim"); + this._el.value = this._require(".var-value"); + this._el.unit = this._require(".var-unit"); + this._el.type = this._require(".var-type"); + this._el.si = this._require(".var-si"); + this._el.comment = this._require(".var-comment"); + this._el.cname = this._require(".var-cname"); + + this._el.name.addEventListener("input", () => { + this._el.cname.textContent = toCName(this._el.name.value); + }); + + setTimeout(() => { + refreshQuantityOptions(); + refreshUnitOptions(); + this.updateSiDisplay(); + }, 0); + + this._el.value.addEventListener("input", () => { + this.syncUnitFromValue(); + this.updateSiDisplay(); + }); + this._el.unit.addEventListener("change", () => { + this.syncValueFromUnit(); + this.updateSiDisplay(); + }); + this._el.type.addEventListener("change", () => { + refreshUnitOptions(); + this.updateSiDisplay(); + }); + } + + getDim() { + return parseInt(this._el.dim.value) || 3; + } + + parseValue() { + const { numVal, unitSuffix } = splitValueAndUnit(this._el.value.value, 0); + return { numVal, unitSuffix }; + } + + siValue() { + const { numVal, unitSuffix } = this.parseValue(); + if (!unitSuffix) return numVal; + const quantityName = this._el.type.value; + for (const u of $id("data-area").querySelectorAll("wc-unit")) { + if ( + u.querySelector(".unit-type").value === quantityName && + u.querySelector(".unit-suffix").value === unitSuffix + ) { + return numVal * u.getSiFactor(); + } + } + return numVal; + } + + syncUnitFromValue() { + const { unitSuffix } = this.parseValue(); + if (unitSuffix && [...this._el.unit.options].some(o => o.value === unitSuffix)) { + this._el.unit.value = unitSuffix; + } + } + + syncValueFromUnit() { + const unitSuffix = this._el.unit.value; + const vals = this._el.value.value.trim().split(/\s+/); + if (unitSuffix && vals.length > 0 && !isNaN(parseFloat(vals[0]))) { + const first = parseFloat(vals[0]); + if (vals.length <= 2) { + this._el.value.value = first + " " + unitSuffix; + } + } + } + + updateSiDisplay() { + const typeName = this._el.type.value; + const { unitSuffix } = this.parseValue(); + if (unitSuffix && typeName) { + const si = this.siValue(); + const defaultUnit = getDefaultUnitSuffix(typeName); + this._el.si.textContent = defaultUnit + ? `= ${si} ${defaultUnit}` + : `= ${si} SI`; + } else { + this._el.si.textContent = ""; + } + } + + saveData() { + return { + name: this._el.name.value, + dim: parseInt(this._el.dim.value) || 3, + value: this._el.value.value, + type: this._el.type.value, + unit: this._el.unit.value, + comment: this._el.comment.value + }; + } + restoreData(d) { + this._el.name.value = d.name || ""; + this._el.dim.value = d.dim || 3; + this._el.value.value = d.value || ""; + this._el.type.value = d.type || ""; + this._el.unit.value = d.unit || ""; + this._el.comment.value = d.comment || ""; + this._el.cname.textContent = toCName(this._el.name.value); + } + + generateOwnCode() { + const name = toCName(this._el.name.value) || "v"; + const typeName = this._el.type.value; + const dim = this.getDim(); + const prefix = commentPrefix(this._el.name.value, this._el.comment.value); + const baseType = typeName || "double"; + const raw = this._el.value.value.trim(); + if (raw) { + const parts = raw.split(/\s+/).filter(s => s); + const siFactor = this.parseValue().unitSuffix ? this.siValue() / this.parseValue().numVal : 1; + const siParts = parts.map(p => { + const n = parseFloat(p); + return isNaN(n) ? 0 : n * siFactor; + }); + return `${prefix}${baseType} ${name}[${dim}] = {${siParts.join(", ")}};\n`; + } + return `${prefix}${baseType} ${name}[${dim}];\n`; + } +} + + +// ── WCString — string variable ───────────────────────────────────────────── + +/** String variable: name, value. Generates C `string` struct with malloc/memcpy. */ +class WCString extends WCBlock { + get dataType() { return "string"; } + connectedCallback() { + super.connectedCallback(); + this._el.name = this._require(".var-name"); + this._el.value = this._require(".var-value"); + this._el.comment = this._require(".var-comment"); + this._el.cname = this._require(".var-cname"); this._el.name.addEventListener("input", () => { this._el.cname.textContent = toCName(this._el.name.value); }); @@ -303,9 +440,9 @@ class WCString extends WCBlock { saveData() { return { name: this._el.name.value, value: this._el.value.value, comment: this._el.comment.value }; } restoreData(d) { - this.querySelector(".var-name").value = d.name || ""; - this.querySelector(".var-value").value = d.value || ""; - this.querySelector(".var-comment").value = d.comment || ""; + this._el.name.value = d.name || ""; + this._el.value.value = d.value || ""; + this._el.comment.value = d.comment || ""; this._el.cname.textContent = toCName(this._el.name.value); } @@ -322,27 +459,26 @@ class WCString extends WCBlock { // ── WCArray — typed C array variable ─────────────────────────────────────── +/** Typed C array variable with dynamic element list and optional vector dimension. */ class WCArray extends WCBlock { get dataType() { return "array"; } - - constructor() { - super(); - this._values = []; - } - connectedCallback() { super.connectedCallback(); - this._el.name = this.querySelector(".var-name"); - this._el.type = this.querySelector(".array-type"); - this._el.elements = this.querySelector(".array-elements"); - this._el.count = this.querySelector(".array-count"); - this._el.comment = this.querySelector(".var-comment"); - this._el.cname = this.querySelector(".var-cname"); + this._el.name = this._require(".var-name"); + this._el.type = this._require(".array-type"); + this._el.elements = this._require(".array-elements"); + this._el.count = this._require(".array-count"); + this._el.comment = this._require(".var-comment"); + this._el.cname = this._require(".var-cname"); + this._el.vecDim = this.querySelector(".array-vec-dim"); this._el.name.addEventListener("input", () => { this._el.cname.textContent = toCName(this._el.name.value); refreshVarRefOptions(); }); + if (this._el.vecDim) { + this._el.vecDim.addEventListener("input", () => this._updateCount()); + } setTimeout(() => this._renderElements(), 0); } @@ -441,17 +577,37 @@ class WCArray extends WCBlock { _addElement(value) { this._values.push(value); - this._renderElements(); + const row = this._makeElementRow(this._values.length - 1); + this._el.elements.insertBefore(row, this._el.elements.lastChild); + const newRow = this._el.elements.lastChild; + newRow.querySelector(".array-element-idx").textContent = this._values.length; + this._updateCount(); } _removeElement(idx) { this._values.splice(idx, 1); - this._renderElements(); + const row = this._el.elements.querySelector(`[data-idx="${idx}"]`); + if (row) row.remove(); + const rows = this._el.elements.children; + for (let i = 0; i < rows.length - 1; i++) { + rows[i].dataset.idx = i; + rows[i].querySelector(".array-element-idx").textContent = i; + } + this._updateCount(); } _updateCount() { const n = this._values.length; - this._el.count.textContent = n + " element" + (n === 1 ? "" : "s"); + const dim = this.getVecDim(); + if (dim > 1) { + this._el.count.textContent = n + " element" + (n === 1 ? "" : "s") + " (\u00D7" + dim + " = " + (n * dim) + ")"; + } else { + this._el.count.textContent = n + " element" + (n === 1 ? "" : "s"); + } + } + + getVecDim() { + return this._el.vecDim ? (parseInt(this._el.vecDim.value) || 1) : 1; } saveData() { @@ -459,7 +615,8 @@ class WCArray extends WCBlock { name: this._el.name.value, type: this._el.type.value, values: [...this._values], - comment: this._el.comment.value + comment: this._el.comment.value, + vecDim: this.getVecDim() }; } restoreData(d) { @@ -467,6 +624,7 @@ class WCArray extends WCBlock { this._el.type.value = d.type || "float"; this._el.comment.value = d.comment || ""; this._el.cname.textContent = toCName(this._el.name.value); + if (this._el.vecDim) this._el.vecDim.value = d.vecDim || 1; if (Array.isArray(d.values)) { this._values = [...d.values]; } else if (typeof d.values === "string" && d.values) { @@ -480,11 +638,14 @@ class WCArray extends WCBlock { generateOwnCode() { const name = toCName(this._el.name.value) || "arr"; const type = this._el.type.value || "float"; + const dim = this.getVecDim(); + const count = this._values.length || 1; + const totalSize = dim > 1 ? dim + "*" + count : count; const elements = this._values.filter(s => s).join(", "); const prefix = commentPrefix(this._el.name.value, this._el.comment.value); - if (elements) { - return `${prefix}${type} ${name}[] = {${elements}};\n`; + if (elements && dim <= 1) { + return `${prefix}${type} ${name}[${totalSize}] = {${elements}};\n`; } - return `${prefix}${type} ${name}[1];\n`; + return `${prefix}${type} ${name}[${totalSize}];\n`; } } diff --git a/blocks-proc.js b/blocks-proc.js index 299b36e..8128482 100644 --- a/blocks-proc.js +++ b/blocks-proc.js @@ -8,46 +8,71 @@ // // Load order dependencies: // wcblock.js → WCBlock base class -// helpers.js → refreshProcOptions, findDataSource, resolveSourceName +// helpers.js → findDataSource, resolveSourceName, escapeString, toCName, commentPrefix // dom.js → $id // draw.js → deleteLink +/** @type {Object} Maps library IDs to icon image paths for procedure call blocks. */ +const LIB_ICONS = { + stdio: "lib-icons/c.svg", + stdlib: "lib-icons/c.svg", + string: "lib-icons/c.svg", + math: "lib-icons/c.svg", +}; + // ── WCProcedure — procedure definition ───────────────────────────────────── +/** Procedure definition block with inputs/outputs/description/return-style. Lives in the Procedures tab. */ class WCProcedure extends WCBlock { connectedCallback() { super.connectedCallback(); - this._el.name = this.querySelector(".proc-name"); - this._el.inputs = this.querySelector(".proc-inputs"); - this._el.outputs = this.querySelector(".proc-outputs"); + this._el.naturalName = this._require(".proc-natural-name"); + this._el.techName = this._require(".proc-tech-name"); + this._el.inputs = this._require(".proc-inputs"); + this._el.outputs = this._require(".proc-outputs"); + this._el.description = this.querySelector(".proc-description"); + this._el.descBody = this.querySelector(".proc-desc-body"); - this.querySelector(".proc-add-input").addEventListener("click", () => { + this._require(".proc-add-input").addEventListener("click", () => { + if (this.dataset.readonly) return; this._addParamRow(this._el.inputs, "in"); }); - this.querySelector(".proc-add-output").addEventListener("click", () => { + this._require(".proc-add-output").addEventListener("click", () => { + if (this.dataset.readonly) return; this._addParamRow(this._el.outputs, "out"); }); - this._el.name.addEventListener("input", () => refreshProcOptions()); + this._el.naturalName.addEventListener("input", () => { + if (!this.dataset.readonly) { + this._el.techName.textContent = toCName(this._el.naturalName.value) || "proc"; + } + }); + + this._el.returnStyle = this.querySelector(".proc-return-style"); + if (this._el.returnStyle) { + this.dataset.returnStyle = this._el.returnStyle.value; + this._el.returnStyle.addEventListener("change", () => { + if (this.dataset.readonly) return; + this.dataset.returnStyle = this._el.returnStyle.value; + }); + } } _addParamRow(container, dir) { const row = document.createElement("div"); row.className = "proc-param-row"; + const readonly = this.dataset.readonly; row.innerHTML = - `` + - `` + - ``; - row.querySelector(".proc-param-del").addEventListener("click", () => { - row.remove(); - refreshProcOptions(); - }); - for (const inp of row.querySelectorAll("input")) { - inp.addEventListener("input", () => refreshProcOptions()); + `` + + `` + + (readonly ? "" : ``); + if (!readonly) { + row.querySelector(".proc-param-del").addEventListener("click", () => { + row.remove(); + }); } container.appendChild(row); - refreshProcOptions(); } getLibrary() { @@ -56,7 +81,7 @@ class WCProcedure extends WCBlock { } getQualifiedName() { - return this.getLibrary() + "/" + (this._el.name.value || "unnamed"); + return this.getLibrary() + "/" + (this._el.techName.textContent || "unnamed"); } getInputParams() { @@ -77,13 +102,28 @@ class WCProcedure extends WCBlock { saveData() { return { library: this.getLibrary(), - name: this._el.name.value, + naturalName: this._el.naturalName.value, + name: this._el.techName.textContent, inputs: this.getInputParams(), - outputs: this.getOutputParams() + outputs: this.getOutputParams(), + returnStyle: this._el.returnStyle ? this._el.returnStyle.value : (this.dataset.returnStyle || "pointer"), + readonly: !!this.dataset.readonly, + formatString: this.dataset.formatString !== undefined, + description: this._el.descBody ? this._el.descBody.textContent : "", + descOpen: this._el.description ? this._el.description.open : false }; } restoreData(d) { - this._el.name.value = d.name || "proc"; + this._el.naturalName.value = d.naturalName || ""; + this._el.techName.textContent = d.name || "proc"; + if (d.readonly) this.dataset.readonly = "true"; + if (d.formatString) this.dataset.formatString = "true"; + if (d.returnStyle) { + this.dataset.returnStyle = d.returnStyle; + if (this._el.returnStyle) this._el.returnStyle.value = d.returnStyle; + } + if (this._el.descBody && d.description) this._el.descBody.textContent = d.description; + if (this._el.description && d.descOpen) this._el.description.open = true; for (const p of (d.inputs || [])) { this._addParamRow(this._el.inputs, "in"); const rows = this._el.inputs.querySelectorAll(".proc-param-row"); @@ -106,20 +146,91 @@ class WCProcedure extends WCBlock { // ── WCProcCall — procedure call block ────────────────────────────────────── +/** @type {RegExp} Matches C printf-format specifiers, capturing the type character. */ +const _FMT_SPEC_RE = /%(?:[+\- #0]*\d*(?:\.\d+)?)([diufFeEgGxXoscpaAn%])/g; + +/** + * Maps a printf format specifier character to a C data type. + * @param {string} spec Single format character (e.g. "d", "f", "s") + * @returns {string} "int", "double", or "string" + */ +function _specDataType(spec) { + if ("di".includes(spec)) return "int"; + if ("u".includes(spec)) return "int"; + if ("fFeEgGaA".includes(spec)) return "double"; + if ("s".includes(spec)) return "string"; + return "int"; +} + +/** Procedure call block with dynamic argument fields, format-string support, and error-handling hooks. Lives in the Algorithm tab. */ class WCProcCall extends WCBlock { connectedCallback() { super.connectedCallback(); - this._el.select = this.querySelector(".proccall-select"); - this._el.nameInput = this.querySelector(".proccall-name"); - this._el.inputs = this.querySelector(".proccall-inputs"); - this._el.outputs = this.querySelector(".proccall-outputs"); + this._selectedProc = ""; + this._fmtProc = null; + this._fmtInput = null; + this._fmtObserver = null; + this._el.icon = this._require(".proccall-icon"); + this._el.natName = this._require(".proccall-nat-name"); + this._el.techName = this._require(".proccall-tech-name"); + this._el.changeBtn = this._require(".proccall-change-btn"); + this._el.nameInput = this._require(".proccall-name"); + this._el.nameRow = this._require(".proccall-name-row"); + this._el.inputs = this._require(".proccall-inputs"); + this._el.outputs = this._require(".proccall-outputs"); + this._el.errHook = this.querySelector(".proccall-error-hook"); + this._el.fmtargs = this._require(".proccall-fmtargs"); + this._el.fmtargsList = this._require(".proccall-fmtargs-list"); + this._el.addFmtArgBtn = this._require(".proccall-add-fmtarg"); + this._el.addFmtArgBtn.addEventListener("click", () => this._addFormatArg()); - this._el.select.addEventListener("change", () => this._rebuildParams()); + const openBrowser = () => { + const current = this._selectedProc; + showProcedureBrowser(current, (qname) => { + if (qname) this.setProcedure(qname); + }); + }; + + this._el.natName.addEventListener("click", openBrowser); + this._el.changeBtn.addEventListener("click", openBrowser); this._initTimer = setTimeout(() => this._rebuildParams(), 0); } + setProcedure(qname) { + this._selectedProc = qname || ""; + const libName = qname ? qname.split("/")[0] : ""; + if (this._el.natName) { + if (qname) { + let natName = qname.split("/")[1] || qname; + let techName = qname.split("/")[1] || qname; + for (const proc of document.querySelectorAll("wc-procedure")) { + if (proc.getQualifiedName() === qname) { + const nat = proc.querySelector(".proc-natural-name"); + if (nat && nat.value) natName = nat.value; + techName = (proc.querySelector(".proc-tech-name") || {}).textContent || techName; + break; + } + } + this._el.natName.textContent = natName; + this._el.natName.title = qname; + this._el.techName.textContent = techName; + this._el.techName.title = qname; + this._el.icon.src = LIB_ICONS[libName] || ""; + this._el.icon.alt = libName; + } else { + this._el.natName.textContent = "(select procedure)"; + this._el.natName.title = ""; + this._el.techName.textContent = ""; + this._el.techName.title = ""; + this._el.icon.src = ""; + this._el.icon.alt = ""; + } + } + this._rebuildParams(); + } + _rebuildParams() { - const qname = this._el.select.dataset.restoredValue || this._el.select.value; + const qname = this._selectedProc; for (const container of [this._el.inputs, this._el.outputs]) { for (const hook of container.querySelectorAll(".hook")) { @@ -134,19 +245,173 @@ class WCProcCall extends WCBlock { this._el.inputs.innerHTML = ""; this._el.outputs.innerHTML = ""; - if (!qname) return; - - for (const proc of document.querySelectorAll("wc-procedure")) { - if (proc.getQualifiedName() === qname) { - for (const p of proc.getInputParams()) { - this._createArgField(this._el.inputs, p, "in"); + let returnStyle = "pointer"; + if (qname) { + for (const proc of document.querySelectorAll("wc-procedure")) { + if (proc.getQualifiedName() === qname) { + returnStyle = proc.dataset.returnStyle || "pointer"; + this._cachedReturnStyle = returnStyle; + for (const p of proc.getInputParams()) { + this._createArgField(this._el.inputs, p, "in"); + } + if (returnStyle !== "error") { + for (const p of proc.getOutputParams()) { + this._createArgField(this._el.outputs, p, "out"); + } + } + break; } - for (const p of proc.getOutputParams()) { - this._createArgField(this._el.outputs, p, "out"); - } - break; } } + + this._setupFormatStringArgs(); + + const isError = returnStyle === "error"; + const hasReturnValue = returnStyle === "error" || returnStyle === "value"; + if (this._el.errHook) { + this._el.errHook.classList.toggle("visible", hasReturnValue); + } + if (this._el.nameRow) { + this._el.nameRow.style.display = isError ? "none" : ""; + } + } + + _setupFormatStringArgs() { + const qname = this._selectedProc; + let fmtProc = null; + if (qname) { + for (const proc of document.querySelectorAll("wc-procedure")) { + if (proc.getQualifiedName() === qname && proc.dataset.formatString !== undefined) { + fmtProc = proc; + break; + } + } + } + this._fmtProc = fmtProc; + + if (this._fmtObserver) { this._fmtObserver.disconnect(); this._fmtObserver = null; } + + if (!fmtProc) { + this._el.fmtargs.style.display = "none"; + this._el.addFmtArgBtn.style.display = "none"; + this._el.fmtargsList.innerHTML = ""; + return; + } + + this._el.fmtargs.style.display = ""; + this._fmtInput = this._el.inputs.querySelector(".proccall-arg-input"); + + if (this._fmtInput) { + this._fmtInput.addEventListener("input", () => this._rebuildFormatArgs()); + } + + this._fmtObserver = new MutationObserver(() => { + if (this._isFmtDynamic()) this._ensureFmtUnconnected(); + }); + this._fmtObserver.observe(this._el.fmtargsList, { attributes: true, attributeFilter: ["data-connected"], subtree: true }); + + this._rebuildFormatArgs(); + } + + _isFmtDynamic() { + if (!this._fmtInput) return false; + const hook = this._fmtInput.parentElement.querySelector(".hook"); + return hook ? findDataSource(hook) !== null : false; + } + + _rebuildFormatArgs() { + this._el.fmtargsList.innerHTML = ""; + const dynamic = this._isFmtDynamic(); + this._el.addFmtArgBtn.style.display = dynamic ? "" : "none"; + + if (dynamic) { + this._ensureFmtUnconnected(); + return; + } + + const fmt = this._fmtInput ? this._fmtInput.value : ""; + const specs = this._parseFormatSpecifiers(fmt); + for (let i = 0; i < specs.length; i++) { + this._createFormatArg(specs[i], i); + } + } + + _parseFormatSpecifiers(fmt) { + const specs = []; + let m; + _FMT_SPEC_RE.lastIndex = 0; + while ((m = _FMT_SPEC_RE.exec(fmt)) !== null) { + if (m[1] === "%") continue; + specs.push({ full: m[0], spec: m[1] }); + } + return specs; + } + + _createFormatArg(spec, index) { + const row = document.createElement("div"); + row.className = "proccall-arg-row"; + + const hook = document.createElement("button"); + hook.className = "hook"; + hook.dataset.direction = "in"; + hook.dataset.linkType = "data"; + hook.dataset.type = _specDataType(spec.spec); + hook.dataset.exit = "left"; + hook.title = `expects ${_specDataType(spec.spec)}`; + + const label = document.createElement("span"); + label.className = "proccall-arg-label"; + label.textContent = `arg${index + 1}:`; + + const input = document.createElement("input"); + input.className = "proccall-arg-input"; + input.dataset.dir = "in"; + input.dataset.paramName = `fmtArg${index}`; + input.placeholder = spec.full; + + row.appendChild(hook); + row.appendChild(label); + row.appendChild(input); + this._el.fmtargsList.appendChild(row); + this._setupHook(hook); + } + + _addFormatArg() { + const idx = this._el.fmtargsList.querySelectorAll(".proccall-arg-row").length; + const row = document.createElement("div"); + row.className = "proccall-arg-row"; + + const hook = document.createElement("button"); + hook.className = "hook"; + hook.dataset.direction = "in"; + hook.dataset.linkType = "data"; + hook.dataset.type = "scalar"; + hook.dataset.exit = "left"; + hook.title = "expects scalar"; + + const label = document.createElement("span"); + label.className = "proccall-arg-label"; + label.textContent = `arg${idx + 1}:`; + + const input = document.createElement("input"); + input.className = "proccall-arg-input"; + input.dataset.dir = "in"; + input.dataset.paramName = `fmtArg${idx}`; + input.placeholder = `arg${idx + 1}`; + + row.appendChild(hook); + row.appendChild(label); + row.appendChild(input); + this._el.fmtargsList.appendChild(row); + this._setupHook(hook); + this._ensureFmtUnconnected(); + } + + _ensureFmtUnconnected() { + if (!this._isFmtDynamic()) return; + const hooks = this._el.fmtargsList.querySelectorAll(".hook"); + const hasFree = [...hooks].some(h => !h.dataset.connected); + if (!hasFree) this._addFormatArg(); } _createArgField(container, param, dir) { @@ -192,24 +457,92 @@ class WCProcCall extends WCBlock { return block.dataType === "string" ? name + ".data" : name; } + generateCode(visited = new Set()) { + if (visited.has(this)) return ""; + visited.add(this); + const own = this.generateOwnCode(); + if (this._el.errHook && this._el.errHook.classList.contains("visible") && + Array.from(this.wcNextFlow).some(p => p.fromHook === this._el.errHook)) { + return own; + } + return own + this.generateNextCode(visited); + } + generateOwnCode() { - const qname = this._el.select.value; + const qname = this._selectedProc; if (!qname) return ""; const parts = qname.split("/"); const procName = parts[1] || qname; + let returnStyle = this._cachedReturnStyle || "pointer"; + const inputs = []; const outputs = []; for (const row of this._el.inputs.querySelectorAll(".proccall-arg-row")) { const hook = row.querySelector(".hook"); const inp = row.querySelector(".proccall-arg-input"); const source = hook ? findDataSource(hook) : null; - inputs.push(source ? this._sourceName(source) : (inp.value || inp.placeholder)); + if (source) { + inputs.push(this._sourceName(source)); + } else { + let val = inp.value || inp.placeholder; + if (inp.dataset.paramType === "string") val = `"${escapeString(val)}"`; + inputs.push(val); + } } for (const row of this._el.outputs.querySelectorAll(".proccall-arg-row")) { const inp = row.querySelector(".proccall-arg-input"); outputs.push("&" + (inp.value || inp.placeholder)); } + if (this._fmtProc) { + for (const row of this._el.fmtargsList.querySelectorAll(".proccall-arg-row")) { + const hook = row.querySelector(".hook"); + const inp = row.querySelector(".proccall-arg-input"); + const source = hook ? findDataSource(hook) : null; + if (source) { + inputs.push(this._sourceName(source)); + } else { + let val = inp.value || inp.placeholder; + if (hook.dataset.type === "string") val = `"${escapeString(val)}"`; + inputs.push(val); + } + } + } + + const errPaths = this._el.errHook && this._el.errHook.classList.contains("visible") + ? Array.from(this.wcNextFlow).filter(p => p.fromHook === this._el.errHook) + : []; + const hasErrPath = errPaths.length > 0; + + if (returnStyle === "error") { + const call = `${procName}(${inputs.join(", ")})`; + if (hasErrPath) { + const errCode = errPaths[0].toElement.generateCode(new Set()); + let okCode = ""; + for (const p of this.wcNextFlow) { + if (p.fromHook !== this._el.errHook) { + okCode += p.toElement.generateCode(new Set()); + } + } + return `\nif (${call} != 0) {\n${errCode}} else {\n${okCode}}\n`; + } + return `\n${call};\n`; + } + + if (returnStyle === "value" && outputs.length === 1) { + const outVar = outputs[0].replace(/^&/, ""); + if (hasErrPath) { + const errCode = errPaths[0].toElement.generateCode(new Set()); + let okCode = ""; + for (const p of this.wcNextFlow) { + if (p.fromHook !== this._el.errHook) { + okCode += p.toElement.generateCode(new Set()); + } + } + return `\nif ((${outVar} = ${procName}(${inputs.join(", ")})) != 0) {\n${errCode}} else {\n${okCode}}\n`; + } + return `\n${procName}(${inputs.join(", ")});\n`; + } const args = [...inputs, ...outputs].join(", "); return `\n${procName}(${args});\n`; } @@ -219,19 +552,27 @@ class WCProcCall extends WCBlock { for (const inp of this.querySelectorAll(".proccall-arg-input")) { args[inp.dataset.dir + ":" + inp.dataset.paramName] = inp.value; } - return { selected: this._el.select.value, name: this._el.nameInput.value, args }; + return { selected: this._selectedProc, name: this._el.nameInput.value, args }; } restoreData(d) { if (this._initTimer) { clearTimeout(this._initTimer); this._initTimer = null; } - this._el.select.dataset.restoredValue = d.selected || ""; - this._el.select.value = d.selected || ""; + this.setProcedure(d.selected || ""); this._el.nameInput.value = d.name || ""; - this._rebuildParams(); if (d.args) { for (const inp of this.querySelectorAll(".proccall-arg-input")) { const key = inp.dataset.dir + ":" + inp.dataset.paramName; if (d.args[key] !== undefined) inp.value = d.args[key]; } } + if (this._fmtProc && d.args) { + this._rebuildFormatArgs(); + for (const inp of this.querySelectorAll(".proccall-arg-input")) { + const key = inp.dataset.dir + ":" + inp.dataset.paramName; + if (key.startsWith("in:fmtArg") && d.args[key] !== undefined) { + inp.value = d.args[key]; + } + } + } } } + \ No newline at end of file diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..68ecbff --- /dev/null +++ b/docs/API.md @@ -0,0 +1,1132 @@ +# API Reference + +Generated from JSDoc annotations in 16 source files. + + +--- +## dom.js + +### `const` svgNamespace +*Line 19* + +SVG namespace URI, required when creating SVG elements via JS. + +### `const` STORAGE_KEY +*Line 22* + +localStorage key for workspace persistence (save/load/new). + + +--- +## helpers.js + +### `function` splitValueAndUnit +*Line 29* + +Parses a string like "5 km" into a numeric value and an optional unit suffix. +Falls back to parsing the entire string as a bare number when no suffix is present. + +**Parameters:** + +| Name | Type | Description | +|------|------|------------| +| `str` | `string` | - e.g. "5 km", "42", "" | +| `defaultNum` | `number` | - fallback when parseFloat yields NaN | + +**Returns:** `{ numVal: number, unitSuffix: string|null ` — } + +### `function` rebuildSelect +*Line 50* + +Replaces all options in a + + +
+
+
@@ -383,6 +457,53 @@
+ + +
+
+
+ Select Procedure + +
+
+
+ +
+
+ + + + + + + + + + + + diff --git a/lib-icons/c.svg b/lib-icons/c.svg new file mode 100644 index 0000000..9234e43 --- /dev/null +++ b/lib-icons/c.svg @@ -0,0 +1,4 @@ + + + C + diff --git a/lib-icons/gl.svg b/lib-icons/gl.svg new file mode 100644 index 0000000..6a24954 --- /dev/null +++ b/lib-icons/gl.svg @@ -0,0 +1 @@ +OpenGL \ No newline at end of file diff --git a/lib-icons/glad.svg b/lib-icons/glad.svg new file mode 100644 index 0000000..2bf3024 --- /dev/null +++ b/lib-icons/glad.svg @@ -0,0 +1,4 @@ + + + GLAD + diff --git a/lib-icons/glfw.svg b/lib-icons/glfw.svg new file mode 100644 index 0000000..6e197a1 --- /dev/null +++ b/lib-icons/glfw.svg @@ -0,0 +1,4 @@ + + + GLFW + diff --git a/lib-icons/sdl3.svg b/lib-icons/sdl3.svg new file mode 100644 index 0000000..2df7c8f --- /dev/null +++ b/lib-icons/sdl3.svg @@ -0,0 +1,4 @@ + + + SDL3 + diff --git a/lib-loader.js b/lib-loader.js new file mode 100644 index 0000000..3a7cf30 --- /dev/null +++ b/lib-loader.js @@ -0,0 +1,136 @@ +// ── On-demand library loader ──────────────────────────────────────────────── +// +// Fetches procedure definitions from JSON files in the libraries/ directory. +// At startup only the index is loaded; individual libraries are fetched when +// the user clicks their tab for the first time. +// +// Depends on: app.js (addLibTab, deriveNaturalName) +// blocks-proc.js (WCProcedure) +// wcprogram.js (BLOCK_REGISTRY) + + +/** @type {Set} Tracks which libraries have been loaded (prevents re-loading). */ +const LOADED_LIBS = new Set(); +/** @type {Array<{id:string, name:string, group?:string}>} */ +let LIB_INDEX = []; + +/** + * Creates param rows on a procedure block and sets their name/type values. + * Follows the same pattern as WCProcedure.restoreData(). + * @param {HTMLElement} proc A wc-procedure element + * @param {Array<[string,string]>} inputs Array of [name, type] pairs + * @param {Array<[string,string]>} outputs Array of [name, type] pairs + */ +function _addProcParams(proc, inputs, outputs) { + for (const [name, type] of inputs) { + proc._addParamRow(proc.querySelector(".proc-inputs"), "in"); + const rows = proc.querySelectorAll(".proc-inputs .proc-param-row"); + const last = rows[rows.length - 1]; + last.querySelector(".proc-param-name").value = name; + last.querySelector(".proc-param-type").value = type; + } + for (const [name, type] of outputs) { + proc._addParamRow(proc.querySelector(".proc-outputs"), "out"); + const rows = proc.querySelectorAll(".proc-outputs .proc-param-row"); + const last = rows[rows.length - 1]; + last.querySelector(".proc-param-name").value = name; + last.querySelector(".proc-param-type").value = type; + } +} + +/** + * Fetches libraries/index.json and returns the library metadata array. + * Called once at startup. + * @returns {Promise>} + */ +async function loadLibraryIndex() { + try { + const res = await fetch("libraries/index.json"); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + LIB_INDEX = await res.json(); + } catch (e) { + showError("Could not load library index: " + e.message); + LIB_INDEX = []; + } + return LIB_INDEX; +} + + +/** + * Loads a single library by its id (e.g. "stdio", "sdl3"). + * Creates the tab if it doesn't exist yet, then populates all procedures. + * Idempotent — subsequent calls for the same lib are no-ops. + */ +async function loadLibrary(libId) { + if (LOADED_LIBS.has(libId)) return; + LOADED_LIBS.add(libId); + + const prog = $id("main-program"); + if (!prog) return; + + let data; + try { + const res = await fetch(`libraries/${libId}.json`); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + data = await res.json(); + } catch (e) { + showError(`Could not load library "${libId}": ${e.message}`); + return; + } + + const procs = data.procedures; + if (!procs || !procs.length) return; + + /* Mark this lib as the active tab if it's the first one loaded */ + const isFirst = LOADED_LIBS.size === 1; + + /* Ensure the tab exists — but don't duplicate if already created from index */ + if (!document.querySelector(`.lib-tab[data-lib-name="${libId}"]`)) { + addLibTab(libId, isFirst); + } + + for (const pd of procs) { + const proc = prog.newProcedureNode(libId); + if (!proc) continue; + proc.querySelector(".proc-tech-name").textContent = pd.name; + proc.querySelector(".proc-natural-name").value = pd.naturalName || deriveNaturalName(pd.name); + if (pd.desc) { + const db = proc.querySelector(".proc-desc-body"); + if (db) db.textContent = pd.desc; + } + proc.dataset.readonly = "true"; + if (pd.returnStyle === "value") proc.dataset.returnStyle = "value"; + if (pd.returnStyle === "error") proc.dataset.returnStyle = "error"; + if (pd.formatString) proc.dataset.formatString = "true"; + _addProcParams(proc, pd.inputs || [], pd.outputs || []); + } + +} + + +/** + * Returns the loaded library index (synchronous after loadLibraryIndex). + * @returns {Array<{id:string, name:string, group?:string}>} + */ +function getLibraryIndex() { + return LIB_INDEX; +} + + +/** + * Checks if a library has been loaded yet. + * @param {string} libId + * @returns {boolean} + */ +function isLibraryLoaded(libId) { + return LOADED_LIBS.has(libId); +} + + +/** + * Marks a library as loaded (for user-created libs that don't have JSON files). + * @param {string} libId + */ +function markLibLoaded(libId) { + LOADED_LIBS.add(libId); +} diff --git a/libraries/gl.json b/libraries/gl.json new file mode 100644 index 0000000..fc17bf7 --- /dev/null +++ b/libraries/gl.json @@ -0,0 +1,136 @@ +{ + "procedures": [ + { + "name": "glCreateShader", + "inputs": [["type","unsigned int"]], + "outputs": [["shader","unsigned int"]], + "desc": "Creates an empty shader object of the specified type." + }, + { + "name": "glShaderSource", + "inputs": [["shader","unsigned int"],["count","int"],["source","string"]], + "outputs": [], + "desc": "Sets the source code for a shader object." + }, + { + "name": "glCompileShader", + "inputs": [["shader","unsigned int"]], + "outputs": [], + "desc": "Compiles the source code of a shader object." + }, + { + "name": "glGetShaderiv", + "inputs": [["shader","unsigned int"],["pname","unsigned int"]], + "outputs": [["params","int"]], + "desc": "Retrieves a parameter value from a shader object." + }, + { + "name": "glCreateProgram", + "inputs": [], + "outputs": [["program","unsigned int"]], + "desc": "Creates an empty shader program object." + }, + { + "name": "glAttachShader", + "inputs": [["program","unsigned int"],["shader","unsigned int"]], + "outputs": [], + "desc": "Attaches a shader object to a shader program." + }, + { + "name": "glLinkProgram", + "inputs": [["program","unsigned int"]], + "outputs": [], + "desc": "Links the attached shaders into an executable program." + }, + { + "name": "glGetProgramiv", + "inputs": [["program","unsigned int"],["pname","unsigned int"]], + "outputs": [["params","int"]], + "desc": "Retrieves a parameter value from a program object." + }, + { + "name": "glGenVertexArrays", + "inputs": [["n","unsigned int"]], + "outputs": [["arrays","unsigned int"]], + "desc": "Generates vertex array object (VAO) names." + }, + { + "name": "glBindVertexArray", + "inputs": [["array","unsigned int"]], + "outputs": [], + "desc": "Binds a VAO for use in subsequent vertex operations." + }, + { + "name": "glGenBuffers", + "inputs": [["n","unsigned int"]], + "outputs": [["buffers","unsigned int"]], + "desc": "Generates buffer object names." + }, + { + "name": "glBindBuffer", + "inputs": [["target","unsigned int"],["buffer","unsigned int"]], + "outputs": [], + "desc": "Binds a buffer object to a target." + }, + { + "name": "glBufferData", + "inputs": [["target","unsigned int"],["size","int"],["data","string"],["usage","unsigned int"]], + "outputs": [], + "desc": "Uploads vertex data to the currently bound buffer." + }, + { + "name": "glVertexAttribPointer", + "inputs": [["index","unsigned int"],["size","int"],["type","unsigned int"],["normalized","int"],["stride","int"],["pointer","int"]], + "outputs": [], + "desc": "Defines how vertex data is mapped to a shader attribute." + }, + { + "name": "glEnableVertexAttribArray", + "inputs": [["index","unsigned int"]], + "outputs": [], + "desc": "Enables a vertex attribute array for rendering." + }, + { + "name": "glUseProgram", + "inputs": [["program","unsigned int"]], + "outputs": [], + "desc": "Activates a shader program for rendering." + }, + { + "name": "glDrawArrays", + "inputs": [["mode","unsigned int"],["first","int"],["count","int"]], + "outputs": [], + "desc": "Renders primitives from the currently bound vertex data." + }, + { + "name": "glClear", + "inputs": [["mask","unsigned int"]], + "outputs": [], + "desc": "Clears the specified buffers (color, depth, stencil)." + }, + { + "name": "glClearColor", + "inputs": [["red","float"],["green","float"],["blue","float"],["alpha","float"]], + "outputs": [], + "desc": "Sets the clear color used when clearing the color buffer." + }, + { + "name": "glDeleteShader", + "inputs": [["shader","unsigned int"]], + "outputs": [], + "desc": "Deletes a shader object and frees its resources." + }, + { + "name": "glDeleteProgram", + "inputs": [["program","unsigned int"]], + "outputs": [], + "desc": "Deletes a shader program and frees its resources." + }, + { + "name": "glViewport", + "inputs": [["x","int"],["y","int"],["width","int"],["height","int"]], + "outputs": [], + "desc": "Sets the viewport rectangle for rendering." + } + ] +} diff --git a/libraries/glad.json b/libraries/glad.json new file mode 100644 index 0000000..6787923 --- /dev/null +++ b/libraries/glad.json @@ -0,0 +1,10 @@ +{ + "procedures": [ + { + "name": "gladLoadGL", + "inputs": [], + "outputs": [["result","int"]], + "desc": "Loads all OpenGL function pointers after context creation." + } + ] +} diff --git a/libraries/glfw.json b/libraries/glfw.json new file mode 100644 index 0000000..dfaa694 --- /dev/null +++ b/libraries/glfw.json @@ -0,0 +1,46 @@ +{ + "procedures": [ + { + "name": "glfwInit", + "inputs": [], + "outputs": [["result","int"]], + "desc": "Initializes the GLFW library for window and input management." + }, + { + "name": "glfwCreateWindow", + "inputs": [["width","int"],["height","int"],["title","string"]], + "outputs": [["window","int"]], + "desc": "Creates a window with the specified dimensions and title." + }, + { + "name": "glfwMakeContextCurrent", + "inputs": [["window","int"]], + "outputs": [], + "desc": "Sets the OpenGL context of the given window as current." + }, + { + "name": "glfwSwapBuffers", + "inputs": [["window","int"]], + "outputs": [], + "desc": "Swaps the front and back buffers of the window." + }, + { + "name": "glfwPollEvents", + "inputs": [], + "outputs": [], + "desc": "Processes pending window events (keyboard, mouse, etc.)." + }, + { + "name": "glfwWindowShouldClose", + "inputs": [["window","int"]], + "outputs": [["result","int"]], + "desc": "Checks if the window close button has been pressed." + }, + { + "name": "glfwTerminate", + "inputs": [], + "outputs": [], + "desc": "Cleans up GLFW resources and terminates the library." + } + ] +} diff --git a/libraries/highlight/c.min.js b/libraries/highlight/c.min.js new file mode 100644 index 0000000..cb2d348 --- /dev/null +++ b/libraries/highlight/c.min.js @@ -0,0 +1,40 @@ +/*! `c` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const n=e.regex,t=e.COMMENT("//","$",{ +contains:[{begin:/\\\n/}] +}),s="decltype\\(auto\\)",a="[a-zA-Z_]\\w*::",r="("+s+"|"+n.optional(a)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",i={ +className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ +match:/\batomic_[a-z]{3,6}\b/}]},l={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(l,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(a)+e.IDENT_RE,relevance:0 +},g=n.optional(a)+e.IDENT_RE+"\\s*\\(",u={ +keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], +type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"], +literal:"true false NULL", +built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" +},m=[c,i,t,e.C_BLOCK_COMMENT_MODE,o,l],_={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:m.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:m.concat(["self"]),relevance:0}]),relevance:0},p={ +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:s,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})], +relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/, +keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,l,o,i,{begin:/\(/, +end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,l,o,i] +}]},i,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"=]/,contains:[{ +beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c, +strings:l,keywords:u}}}})();hljs.registerLanguage("c",e)})(); \ No newline at end of file diff --git a/libraries/highlight/default.min.css b/libraries/highlight/default.min.css new file mode 100644 index 0000000..a75ea91 --- /dev/null +++ b/libraries/highlight/default.min.css @@ -0,0 +1,9 @@ +/*! + Theme: Default + Description: Original highlight.js style + Author: (c) Ivan Sagalaev + Maintainer: @highlightjs/core-team + Website: https://highlightjs.org/ + License: see project LICENSE + Touched: 2021 +*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f3f3f3;color:#444}.hljs-comment{color:#697070}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:#695}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} \ No newline at end of file diff --git a/libraries/highlight/highlight.min.js b/libraries/highlight/highlight.min.js new file mode 100644 index 0000000..5d699ae --- /dev/null +++ b/libraries/highlight/highlight.min.js @@ -0,0 +1,1213 @@ +/*! + Highlight.js v11.9.0 (git: f47103d4f1) + (c) 2006-2023 undefined and other contributors + License: BSD-3-Clause + */ +var hljs=function(){"use strict";function e(n){ +return n instanceof Map?n.clear=n.delete=n.set=()=>{ +throw Error("map is read-only")}:n instanceof Set&&(n.add=n.clear=n.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(n),Object.getOwnPropertyNames(n).forEach((t=>{ +const a=n[t],i=typeof a;"object"!==i&&"function"!==i||Object.isFrozen(a)||e(a) +})),n}class n{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function t(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function a(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n] +;return n.forEach((e=>{for(const n in e)t[n]=e[n]})),t}const i=e=>!!e.scope +;class r{constructor(e,n){ +this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){ +this.buffer+=t(e)}openNode(e){if(!i(e))return;const n=((e,{prefix:n})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const t=e.split(".") +;return[`${n}${t.shift()}`,...t.map(((e,n)=>`${e}${"_".repeat(n+1)}`))].join(" ") +}return`${n}${e}`})(e.scope,{prefix:this.classPrefix});this.span(n)} +closeNode(e){i(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const s=(e={})=>{const n={children:[]} +;return Object.assign(n,e),n};class o{constructor(){ +this.rootNode=s(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const n=s({scope:e}) +;this.add(n),this.stack.push(n)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){ +return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n), +n.children.forEach((n=>this._walk(e,n))),e.closeNode(n)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +o._collapse(e)})))}}class l extends o{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,n){const t=e.root +;n&&(t.scope="language:"+n),this.add(t)}toHTML(){ +return new r(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function c(e){ +return e?"string"==typeof e?e:e.source:null}function d(e){return b("(?=",e,")")} +function g(e){return b("(?:",e,")*")}function u(e){return b("(?:",e,")?")} +function b(...e){return e.map((e=>c(e))).join("")}function m(...e){const n=(e=>{ +const n=e[e.length-1] +;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} +})(e);return"("+(n.capture?"":"?:")+e.map((e=>c(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const _=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function h(e,{joinWith:n}){let t=0;return e.map((e=>{t+=1;const n=t +;let a=c(e),i="";for(;a.length>0;){const e=_.exec(a);if(!e){i+=a;break} +i+=a.substring(0,e.index), +a=a.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+(Number(e[1])+n):(i+=e[0], +"("===e[0]&&t++)}return i})).map((e=>`(${e})`)).join(n)} +const f="[a-zA-Z]\\w*",E="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",N="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",v={ +begin:"\\\\[\\s\\S]",relevance:0},O={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[v]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[v]},x=(e,n,t={})=>{const i=a({scope:"comment",begin:e,end:n, +contains:[]},t);i.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const r=m("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return i.contains.push({begin:b(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i +},M=x("//","$"),S=x("/\\*","\\*/"),A=x("#","$");var C=Object.freeze({ +__proto__:null,APOS_STRING_MODE:O,BACKSLASH_ESCAPE:v,BINARY_NUMBER_MODE:{ +scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:x, +C_BLOCK_COMMENT_MODE:S,C_LINE_COMMENT_MODE:M,C_NUMBER_MODE:{scope:"number", +begin:N,relevance:0},C_NUMBER_RE:N,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}}),HASH_COMMENT_MODE:A,IDENT_RE:f, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+E,relevance:0}, +NUMBER_MODE:{scope:"number",begin:y,relevance:0},NUMBER_RE:y, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[v,{begin:/\[/,end:/\]/,relevance:0,contains:[v]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const n=/^#![ ]*\// +;return e.binary&&(e.begin=b(n,/.*\b/,e.binary,/\b.*/)),a({scope:"meta",begin:n, +end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:f,relevance:0},UNDERSCORE_IDENT_RE:E, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:E,relevance:0}});function T(e,n){ +"."===e.input[e.index-1]&&n.ignoreMatch()}function R(e,n){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function D(e,n){ +n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=T,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function I(e,n){ +Array.isArray(e.illegal)&&(e.illegal=m(...e.illegal))}function L(e,n){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function B(e,n){ +void 0===e.relevance&&(e.relevance=1)}const $=(e,n)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const t=Object.assign({},e);Object.keys(e).forEach((n=>{delete e[n] +})),e.keywords=t.keywords,e.begin=b(t.beforeMatch,d(t.begin)),e.starts={ +relevance:0,contains:[Object.assign(t,{endsParent:!0})] +},e.relevance=0,delete t.beforeMatch +},z=["of","and","for","in","not","or","if","then","parent","list","value"],F="keyword" +;function U(e,n,t=F){const a=Object.create(null) +;return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach((t=>{ +Object.assign(a,U(e[t],n,t))})),a;function i(e,t){ +n&&(t=t.map((e=>e.toLowerCase()))),t.forEach((n=>{const t=n.split("|") +;a[t[0]]=[e,j(t[0],t[1])]}))}}function j(e,n){ +return n?Number(n):(e=>z.includes(e.toLowerCase()))(e)?0:1}const P={},K=e=>{ +console.error(e)},H=(e,...n)=>{console.log("WARN: "+e,...n)},q=(e,n)=>{ +P[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),P[`${e}/${n}`]=!0) +},G=Error();function Z(e,n,{key:t}){let a=0;const i=e[t],r={},s={} +;for(let e=1;e<=n.length;e++)s[e+a]=i[e],r[e+a]=!0,a+=p(n[e-1]) +;e[t]=s,e[t]._emit=r,e[t]._multi=!0}function W(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +G +;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"), +G;Z(e,e.begin,{key:"beginScope"}),e.begin=h(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +G +;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"), +G;Z(e,e.end,{key:"endScope"}),e.end=h(e.end,{joinWith:""})}})(e)}function Q(e){ +function n(n,t){ +return RegExp(c(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":"")) +}class t{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,n){ +n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(h(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const n=this.matcherRe.exec(e);if(!n)return null +;const t=n.findIndex(((e,n)=>n>0&&void 0!==e)),a=this.matchIndexes[t] +;return n.splice(0,t),Object.assign(n,a)}}class i{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t +;return this.rules.slice(e).forEach((([e,t])=>n.addRule(e,t))), +n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){ +this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){ +const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex +;let t=n.exec(e) +;if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{ +const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)} +return t&&(this.regexIndex+=t.position+1, +this.regexIndex===this.count&&this.considerAll()),t}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=a(e.classNameAliases||{}),function t(r,s){const o=r +;if(r.isCompiled)return o +;[R,L,W,$].forEach((e=>e(r,s))),e.compilerExtensions.forEach((e=>e(r,s))), +r.__beforeBegin=null,[D,I,B].forEach((e=>e(r,s))),r.isCompiled=!0;let l=null +;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), +l=r.keywords.$pattern, +delete r.keywords.$pattern),l=l||/\w+/,r.keywords&&(r.keywords=U(r.keywords,e.case_insensitive)), +o.keywordPatternRe=n(l,!0), +s&&(r.begin||(r.begin=/\B|\b/),o.beginRe=n(o.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), +r.end&&(o.endRe=n(o.end)), +o.terminatorEnd=c(o.end)||"",r.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(r.end?"|":"")+s.terminatorEnd)), +r.illegal&&(o.illegalRe=n(r.illegal)), +r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((n=>a(e,{ +variants:null},n)))),e.cachedVariants?e.cachedVariants:X(e)?a(e,{ +starts:e.starts?a(e.starts):null +}):Object.isFrozen(e)?a(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{t(e,o) +})),r.starts&&t(r.starts,s),o.matcher=(e=>{const n=new i +;return e.contains.forEach((e=>n.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n})(o),o}(e)}function X(e){ +return!!e&&(e.endsWithParent||X(e.starts))}class V extends Error{ +constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}} +const J=t,Y=a,ee=Symbol("nomatch"),ne=t=>{ +const a=Object.create(null),i=Object.create(null),r=[];let s=!0 +;const o="Could not find the language '{}', did you forget to load/include a language module?",c={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:l};function _(e){ +return p.noHighlightRe.test(e)}function h(e,n,t){let a="",i="" +;"object"==typeof n?(a=e, +t=n.ignoreIllegals,i=n.language):(q("10.7.0","highlight(lang, code, ...args) has been deprecated."), +q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +i=e,a=n),void 0===t&&(t=!0);const r={code:a,language:i};x("before:highlight",r) +;const s=r.result?r.result:f(r.language,r.code,t) +;return s.code=r.code,x("after:highlight",s),s}function f(e,t,i,r){ +const l=Object.create(null);function c(){if(!x.keywords)return void S.addText(A) +;let e=0;x.keywordPatternRe.lastIndex=0;let n=x.keywordPatternRe.exec(A),t="" +;for(;n;){t+=A.substring(e,n.index) +;const i=w.case_insensitive?n[0].toLowerCase():n[0],r=(a=i,x.keywords[a]);if(r){ +const[e,a]=r +;if(S.addText(t),t="",l[i]=(l[i]||0)+1,l[i]<=7&&(C+=a),e.startsWith("_"))t+=n[0];else{ +const t=w.classNameAliases[e]||e;g(n[0],t)}}else t+=n[0] +;e=x.keywordPatternRe.lastIndex,n=x.keywordPatternRe.exec(A)}var a +;t+=A.substring(e),S.addText(t)}function d(){null!=x.subLanguage?(()=>{ +if(""===A)return;let e=null;if("string"==typeof x.subLanguage){ +if(!a[x.subLanguage])return void S.addText(A) +;e=f(x.subLanguage,A,!0,M[x.subLanguage]),M[x.subLanguage]=e._top +}else e=E(A,x.subLanguage.length?x.subLanguage:null) +;x.relevance>0&&(C+=e.relevance),S.__addSublanguage(e._emitter,e.language) +})():c(),A=""}function g(e,n){ +""!==e&&(S.startScope(n),S.addText(e),S.endScope())}function u(e,n){let t=1 +;const a=n.length-1;for(;t<=a;){if(!e._emit[t]){t++;continue} +const a=w.classNameAliases[e[t]]||e[t],i=n[t];a?g(i,a):(A=i,c(),A=""),t++}} +function b(e,n){ +return e.scope&&"string"==typeof e.scope&&S.openNode(w.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(g(A,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +A=""):e.beginScope._multi&&(u(e.beginScope,n),A="")),x=Object.create(e,{parent:{ +value:x}}),x}function m(e,t,a){let i=((e,n)=>{const t=e&&e.exec(n) +;return t&&0===t.index})(e.endRe,a);if(i){if(e["on:end"]){const a=new n(e) +;e["on:end"](t,a),a.isMatchIgnored&&(i=!1)}if(i){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return m(e.parent,t,a)}function _(e){ +return 0===x.matcher.regexIndex?(A+=e[0],1):(D=!0,0)}function h(e){ +const n=e[0],a=t.substring(e.index),i=m(x,e,a);if(!i)return ee;const r=x +;x.endScope&&x.endScope._wrap?(d(), +g(n,x.endScope._wrap)):x.endScope&&x.endScope._multi?(d(), +u(x.endScope,e)):r.skip?A+=n:(r.returnEnd||r.excludeEnd||(A+=n), +d(),r.excludeEnd&&(A=n));do{ +x.scope&&S.closeNode(),x.skip||x.subLanguage||(C+=x.relevance),x=x.parent +}while(x!==i.parent);return i.starts&&b(i.starts,e),r.returnEnd?0:n.length} +let y={};function N(a,r){const o=r&&r[0];if(A+=a,null==o)return d(),0 +;if("begin"===y.type&&"end"===r.type&&y.index===r.index&&""===o){ +if(A+=t.slice(r.index,r.index+1),!s){const n=Error(`0 width match regex (${e})`) +;throw n.languageName=e,n.badRule=y.rule,n}return 1} +if(y=r,"begin"===r.type)return(e=>{ +const t=e[0],a=e.rule,i=new n(a),r=[a.__beforeBegin,a["on:begin"]] +;for(const n of r)if(n&&(n(e,i),i.isMatchIgnored))return _(t) +;return a.skip?A+=t:(a.excludeBegin&&(A+=t), +d(),a.returnBegin||a.excludeBegin||(A=t)),b(a,e),a.returnBegin?0:t.length})(r) +;if("illegal"===r.type&&!i){ +const e=Error('Illegal lexeme "'+o+'" for mode "'+(x.scope||"")+'"') +;throw e.mode=x,e}if("end"===r.type){const e=h(r);if(e!==ee)return e} +if("illegal"===r.type&&""===o)return 1 +;if(R>1e5&&R>3*r.index)throw Error("potential infinite loop, way more iterations than matches") +;return A+=o,o.length}const w=v(e) +;if(!w)throw K(o.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const O=Q(w);let k="",x=r||O;const M={},S=new p.__emitter(p);(()=>{const e=[] +;for(let n=x;n!==w;n=n.parent)n.scope&&e.unshift(n.scope) +;e.forEach((e=>S.openNode(e)))})();let A="",C=0,T=0,R=0,D=!1;try{ +if(w.__emitTokens)w.__emitTokens(t,S);else{for(x.matcher.considerAll();;){ +R++,D?D=!1:x.matcher.considerAll(),x.matcher.lastIndex=T +;const e=x.matcher.exec(t);if(!e)break;const n=N(t.substring(T,e.index),e) +;T=e.index+n}N(t.substring(T))}return S.finalize(),k=S.toHTML(),{language:e, +value:k,relevance:C,illegal:!1,_emitter:S,_top:x}}catch(n){ +if(n.message&&n.message.includes("Illegal"))return{language:e,value:J(t), +illegal:!0,relevance:0,_illegalBy:{message:n.message,index:T, +context:t.slice(T-100,T+100),mode:n.mode,resultSoFar:k},_emitter:S};if(s)return{ +language:e,value:J(t),illegal:!1,relevance:0,errorRaised:n,_emitter:S,_top:x} +;throw n}}function E(e,n){n=n||p.languages||Object.keys(a);const t=(e=>{ +const n={value:J(e),illegal:!1,relevance:0,_top:c,_emitter:new p.__emitter(p)} +;return n._emitter.addText(e),n})(e),i=n.filter(v).filter(k).map((n=>f(n,e,!1))) +;i.unshift(t);const r=i.sort(((e,n)=>{ +if(e.relevance!==n.relevance)return n.relevance-e.relevance +;if(e.language&&n.language){if(v(e.language).supersetOf===n.language)return 1 +;if(v(n.language).supersetOf===e.language)return-1}return 0})),[s,o]=r,l=s +;return l.secondBest=o,l}function y(e){let n=null;const t=(e=>{ +let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"" +;const t=p.languageDetectRe.exec(n);if(t){const n=v(t[1]) +;return n||(H(o.replace("{}",t[1])), +H("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"} +return n.split(/\s+/).find((e=>_(e)||v(e)))})(e);if(_(t))return +;if(x("before:highlightElement",{el:e,language:t +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new V("One of your code blocks includes unescaped HTML.",e.innerHTML) +;n=e;const a=n.textContent,r=t?h(a,{language:t,ignoreIllegals:!0}):E(a) +;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,n,t)=>{const a=n&&i[n]||t +;e.classList.add("hljs"),e.classList.add("language-"+a) +})(e,t,r.language),e.result={language:r.language,re:r.relevance, +relevance:r.relevance},r.secondBest&&(e.secondBest={ +language:r.secondBest.language,relevance:r.secondBest.relevance +}),x("after:highlightElement",{el:e,result:r,text:a})}let N=!1;function w(){ +"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(y):N=!0 +}function v(e){return e=(e||"").toLowerCase(),a[e]||a[i[e]]} +function O(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +i[e.toLowerCase()]=n}))}function k(e){const n=v(e) +;return n&&!n.disableAutodetect}function x(e,n){const t=e;r.forEach((e=>{ +e[t]&&e[t](n)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +N&&w()}),!1),Object.assign(t,{highlight:h,highlightAuto:E,highlightAll:w, +highlightElement:y, +highlightBlock:e=>(q("10.7.0","highlightBlock will be removed entirely in v12.0"), +q("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{p=Y(p,e)}, +initHighlighting:()=>{ +w(),q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +w(),q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,n)=>{let i=null;try{i=n(t)}catch(n){ +if(K("Language definition for '{}' could not be registered.".replace("{}",e)), +!s)throw n;K(n),i=c} +i.name||(i.name=e),a[e]=i,i.rawDefinition=n.bind(null,t),i.aliases&&O(i.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete a[e] +;for(const n of Object.keys(i))i[n]===e&&delete i[n]}, +listLanguages:()=>Object.keys(a),getLanguage:v,registerAliases:O, +autoDetection:k,inherit:Y,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{ +e["before:highlightBlock"](Object.assign({block:n.el},n)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{ +e["after:highlightBlock"](Object.assign({block:n.el},n))})})(e),r.push(e)}, +removePlugin:e=>{const n=r.indexOf(e);-1!==n&&r.splice(n,1)}}),t.debugMode=()=>{ +s=!1},t.safeMode=()=>{s=!0},t.versionString="11.9.0",t.regex={concat:b, +lookahead:d,either:m,optional:u,anyNumberOfTimes:g} +;for(const n in C)"object"==typeof C[n]&&e(C[n]);return Object.assign(t,C),t +},te=ne({});te.newInstance=()=>ne({});var ae=te;const ie=e=>({IMPORTANT:{ +scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{ +scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/}, +FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/}, +ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} +}),re=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],se=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],oe=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],le=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],ce=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse(),de=oe.concat(le) +;var ge="[0-9](_*[0-9])*",ue=`\\.(${ge})`,be="[0-9a-fA-F](_*[0-9a-fA-F])*",me={ +className:"number",variants:[{ +begin:`(\\b(${ge})((${ue})|\\.)?|(${ue}))[eE][+-]?(${ge})[fFdD]?\\b`},{ +begin:`\\b(${ge})((${ue})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ +begin:`(${ue})[fFdD]?\\b`},{begin:`\\b(${ge})[fFdD]\\b`},{ +begin:`\\b0[xX]((${be})\\.?|(${be})?\\.(${be}))[pP][+-]?(${ge})[fFdD]?\\b`},{ +begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${be})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};function pe(e,n,t){return-1===t?"":e.replace(n,(a=>pe(e,n,t-1)))} +const _e="[A-Za-z$_][0-9A-Za-z$_]*",he=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],fe=["true","false","null","undefined","NaN","Infinity"],Ee=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],ye=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],Ne=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],we=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],ve=[].concat(Ne,Ee,ye) +;function Oe(e){const n=e.regex,t=_e,a={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const t=e[0].length+e.index,a=e.input[t] +;if("<"===a||","===a)return void n.ignoreMatch();let i +;">"===a&&(((e,{after:n})=>{const t="",M={ +match:[/const|var|let/,/\s+/,t,/\s*/,/=\s*/,/(async\s*)?/,n.lookahead(x)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[f]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{ +PARAMS_CONTAINS:h,CLASS_REFERENCE:y},illegal:/#(?![$_A-z])/, +contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,d,g,u,b,m,{match:/\$\d+/},l,y,{ +className:"attr",begin:t+n.lookahead(":"),relevance:0},M,{ +begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[m,e.REGEXP_MODE,{ +className:"function",begin:x,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:i,contains:h}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:a.begin, +"on:begin":a.isTrulyOpeningTag,end:a.end}],subLanguage:"xml",contains:[{ +begin:a.begin,end:a.end,skip:!0,contains:["self"]}]}]},N,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[f,e.inherit(e.TITLE_MODE,{begin:t, +className:"title.function"})]},{match:/\.\.\./,relevance:0},O,{match:"\\$"+t, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[f]},w,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},E,k,{match:/\$[(.]/}]}} +const ke=e=>b(/\b/,e,/\w$/.test(e)?/\b/:/\B/),xe=["Protocol","Type"].map(ke),Me=["init","self"].map(ke),Se=["Any","Self"],Ae=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","borrowing","break","case","catch","class","consume","consuming","continue","convenience","copy","default","defer","deinit","didSet","distributed","do","dynamic","each","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","macro","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Ce=["false","nil","true"],Te=["assignment","associativity","higherThan","left","lowerThan","none","right"],Re=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],De=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],Ie=m(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),Le=m(Ie,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),Be=b(Ie,Le,"*"),$e=m(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),ze=m($e,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),Fe=b($e,ze,"*"),Ue=b(/[A-Z]/,ze,"*"),je=["attached","autoclosure",b(/convention\(/,m("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",b(/objc\(/,Fe,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],Pe=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] +;var Ke=Object.freeze({__proto__:null,grmr_bash:e=>{const n=e.regex,t={},a={ +begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]} +;Object.assign(t,{className:"variable",variants:[{ +begin:n.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const i={ +className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},r={ +begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/, +end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,t,i]};i.contains.push(s);const o={begin:/\$?\(\(/, +end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t] +},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 +}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, +contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ +name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/, +keyword:["if","then","else","elif","fi","for","while","until","in","do","done","case","esac","function","select"], +literal:["true","false"], +built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"] +},contains:[l,e.SHEBANG(),c,o,e.HASH_COMMENT_MODE,r,{match:/(\/[a-z._-]+)+/},s,{ +match:/\\"/},{className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}}, +grmr_c:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ +match:/\batomic_[a-z]{3,6}\b/}]},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], +type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"], +literal:"true false NULL", +built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" +},b=[c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],m={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:b.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:b.concat(["self"]),relevance:0}]),relevance:0},p={ +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})], +relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/, +keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/, +end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s] +}]},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"=]/,contains:[{ +beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c, +strings:o,keywords:u}}},grmr_cpp:e=>{const n=e.regex,t=e.COMMENT("//","$",{ +contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="(?!struct)("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",begin:"\\b[a-z\\d_]*_t\\b"},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"], +keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"], +literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"], +_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"] +},b={className:"function.dispatch",relevance:0,keywords:{ +_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"] +}, +begin:n.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,n.lookahead(/(<[^<>]+>|)\s*\(/)) +},m=[b,c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],p={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:m.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:m.concat(["self"]),relevance:0}]),relevance:0},_={className:"function", +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[d],relevance:0},{begin:/::/,relevance:0},{ +begin:/:/,endsWithParent:!0,contains:[o,l]},{relevance:0,match:/,/},{ +className:"params",begin:/\(/,end:/\)/,keywords:u,relevance:0, +contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/,end:/\)/,keywords:u, +relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s]}] +},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C++", +aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:u,illegal:"",keywords:u,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:u},{ +match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/], +className:{1:"keyword",3:"title.class"}}])}},grmr_csharp:e=>{const n={ +keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]), +built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"], +literal:["default","false","null","true"]},t=e.inherit(e.TITLE_MODE,{ +begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{ +begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},i={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] +},r=e.inherit(i,{illegal:/\n/}),s={className:"subst",begin:/\{/,end:/\}/, +keywords:n},o=e.inherit(s,{illegal:/\n/}),l={className:"string",begin:/\$"/, +end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ +},e.BACKSLASH_ESCAPE,o]},c={className:"string",begin:/\$@"/,end:'"',contains:[{ +begin:/\{\{/},{begin:/\}\}/},{begin:'""'},s]},d=e.inherit(c,{illegal:/\n/, +contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},o]}) +;s.contains=[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE], +o.contains=[d,l,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{ +illegal:/\n/})];const g={variants:[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},u={begin:"<",end:">",contains:[{beginKeywords:"in out"},t] +},b=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",m={ +begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], +keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, +contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ +begin:"\x3c!--|--\x3e"},{begin:""}]}] +}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", +end:"$",keywords:{ +keyword:"if else elif endif define undef warning error line region endregion pragma checksum" +}},g,a,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, +illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" +},t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", +relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", +begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ +className:"string",begin:/"/,end:/"/}]},{ +beginKeywords:"new return throw await else",relevance:0},{className:"function", +begin:"("+b+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ +beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", +relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +contains:[e.TITLE_MODE,u],relevance:0},{match:/\(\)/},{className:"params", +begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, +contains:[g,a,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},m]}},grmr_css:e=>{ +const n=e.regex,t=ie(e),a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{ +name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{ +keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"}, +contains:[t.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/ +},t.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0 +},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+oe.join("|")+")"},{begin:":(:)?("+le.join("|")+")"}] +},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b"},{ +begin:/:/,end:/[;}{]/, +contains:[t.BLOCK_COMMENT,t.HEXCOLOR,t.IMPORTANT,t.CSS_NUMBER_MODE,...a,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[...a,{className:"string",begin:/[^)]/,endsWithParent:!0, +excludeEnd:!0}]},t.FUNCTION_DISPATCH]},{begin:n.lookahead(/@/),end:"[{;]", +relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ +},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ +$pattern:/[a-z-]+/,keyword:"and or not only",attribute:se.join(" ")},contains:[{ +begin:/[a-z-]+(?=:)/,className:"attribute"},...a,t.CSS_NUMBER_MODE]}]},{ +className:"selector-tag",begin:"\\b("+re.join("|")+")\\b"}]}},grmr_diff:e=>{ +const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{ +className:"meta",relevance:10, +match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) +},{className:"comment",variants:[{ +begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), +end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]}},grmr_go:e=>{const n={ +keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"], +type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], +literal:["true","false","iota","nil"], +built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] +};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{const n=e.regex;return{name:"GraphQL",aliases:["gql"], +case_insensitive:!0,disableAutodetect:!1,keywords:{ +keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"], +literal:["true","false","null"]}, +contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ +scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation", +begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/, +end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{ +scope:"symbol",begin:n.concat(/[_A-Za-z][_0-9A-Za-z]*/,n.lookahead(/\s*:/)), +relevance:0}],illegal:[/[;<']/,/BEGIN/]}},grmr_ini:e=>{const n=e.regex,t={ +className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{ +begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/, +end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{ +begin:/\$\{(.*?)\}/}]},r={className:"literal", +begin:/\bon|off|true|false|yes|no\b/},s={className:"string", +contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{ +begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}] +},o={begin:/\[/,end:/\]/,contains:[a,r,i,s,t,"self"],relevance:0 +},l=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{ +name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, +contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{ +begin:n.concat(l,"(\\s*\\.\\s*",l,")*",n.lookahead(/\s*=\s*[^#\s]/)), +className:"attr",starts:{end:/$/,contains:[a,o,r,i,s,t]}}]}},grmr_java:e=>{ +const n=e.regex,t="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",a=t+pe("(?:<"+t+"~~~(?:\\s*,\\s*"+t+"~~~)*>)?",/~~~/g,2),i={ +keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits"], +literal:["false","true","null"], +type:["char","boolean","long","float","int","byte","short","double"], +built_in:["super","this"]},r={className:"meta",begin:"@"+t,contains:[{ +begin:/\(/,end:/\)/,contains:["self"]}]},s={className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} +;return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, +relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ +begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, +className:"string",contains:[e.BACKSLASH_ESCAPE] +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ +match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,t],className:{ +1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ +begin:[n.concat(/(?!else)/,t),/\s+/,t,/\s+/,/=(?!=)/],className:{1:"type", +3:"variable",5:"operator"}},{begin:[/record/,/\s+/,t],className:{1:"keyword", +3:"title.class"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"new throw return else",relevance:0},{ +begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ +2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0, +contains:[r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,me,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},me,r]}},grmr_javascript:Oe, +grmr_json:e=>{const n=["true","false","null"],t={scope:"literal", +beginKeywords:n.join(" ")};return{name:"JSON",keywords:{literal:n},contains:[{ +className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{ +match:/[{}[\],:]/,className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,t,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}},grmr_kotlin:e=>{const n={ +keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", +built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", +literal:"true false null"},t={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" +},a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},i={ +className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", +variants:[{begin:'"""',end:'"""(?=[^"])',contains:[i,a]},{begin:"'",end:"'", +illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, +contains:[e.BACKSLASH_ESCAPE,i,a]}]};a.contains.push(r);const s={ +className:"meta", +begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" +},o={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, +end:/\)/,contains:[e.inherit(r,{className:"string"}),"self"]}] +},l=me,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={ +variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, +contains:[]}]},g=d;return g.variants[1].contains=[d],d.variants[1].contains=[g], +{name:"Kotlin",aliases:["kt","kts"],keywords:n, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", +begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword", +begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", +begin:/@\w+/}]}},t,s,o,{className:"function",beginKeywords:"fun",end:"[(]|$", +returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ +begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, +keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, +endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, +endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0 +},e.C_LINE_COMMENT_MODE,c,s,o,r,e.C_NUMBER_MODE]},c]},{ +begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{ +3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, +illegal:"extends implements",contains:[{ +beginKeywords:"public protected internal private constructor" +},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, +excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/, +excludeBegin:!0,returnEnd:!0},s,o]},r,{className:"meta",begin:"^#!/usr/bin/env", +end:"$",illegal:"\n"},l]}},grmr_less:e=>{ +const n=ie(e),t=de,a="[\\w-]+",i="("+a+"|@\\{"+a+"\\})",r=[],s=[],o=e=>({ +className:"string",begin:"~?"+e+".*?"+e}),l=(e,n,t)=>({className:e,begin:n, +relevance:t}),c={$pattern:/[a-z-]+/,keyword:"and or not only", +attribute:se.join(" ")},d={begin:"\\(",end:"\\)",contains:s,keywords:c, +relevance:0} +;s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,o("'"),o('"'),n.CSS_NUMBER_MODE,{ +begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", +excludeEnd:!0} +},n.HEXCOLOR,d,l("variable","@@?"+a,10),l("variable","@\\{"+a+"\\}"),l("built_in","~?`[^`]*?`"),{ +className:"attribute",begin:a+"\\s*:",end:":",returnBegin:!0,excludeEnd:!0 +},n.IMPORTANT,{beginKeywords:"and not"},n.FUNCTION_DISPATCH);const g=s.concat({ +begin:/\{/,end:/\}/,contains:r}),u={beginKeywords:"when",endsWithParent:!0, +contains:[{beginKeywords:"and not"}].concat(s)},b={begin:i+"\\s*:", +returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/ +},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b", +end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}] +},m={className:"keyword", +begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", +starts:{end:"[;{}]",keywords:c,returnEnd:!0,contains:s,relevance:0}},p={ +className:"variable",variants:[{begin:"@"+a+"\\s*:",relevance:15},{begin:"@"+a +}],starts:{end:"[;}]",returnEnd:!0,contains:g}},_={variants:[{ +begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:i,end:/\{/}],returnBegin:!0, +returnEnd:!0,illegal:"[<='$\"]",relevance:0, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,u,l("keyword","all\\b"),l("variable","@\\{"+a+"\\}"),{ +begin:"\\b("+re.join("|")+")\\b",className:"selector-tag" +},n.CSS_NUMBER_MODE,l("selector-tag",i,0),l("selector-id","#"+i),l("selector-class","\\."+i,0),l("selector-tag","&",0),n.ATTRIBUTE_SELECTOR_MODE,{ +className:"selector-pseudo",begin:":("+oe.join("|")+")"},{ +className:"selector-pseudo",begin:":(:)?("+le.join("|")+")"},{begin:/\(/, +end:/\)/,relevance:0,contains:g},{begin:"!important"},n.FUNCTION_DISPATCH]},h={ +begin:a+":(:)?"+`(${t.join("|")})`,returnBegin:!0,contains:[_]} +;return r.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,m,p,h,b,_,u,n.FUNCTION_DISPATCH), +{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:r}}, +grmr_lua:e=>{const n="\\[=*\\[",t="\\]=*\\]",a={begin:n,end:t,contains:["self"] +},i=[e.COMMENT("--(?!"+n+")","$"),e.COMMENT("--"+n,t,{contains:[a],relevance:10 +})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, +literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:i}].concat(i) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:n,end:t,contains:[a],relevance:5}])}},grmr_makefile:e=>{const n={ +className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{ +const n={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},t={ +variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},a={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},i={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},r=e.inherit(a,{contains:[] +}),s=e.inherit(i,{contains:[]});a.contains.push(s),i.contains.push(r) +;let o=[n,t];return[a,i,r,s].forEach((e=>{e.contains=e.contains.concat(o) +})),o=o.concat(a,i),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:o}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},a,i,{className:"quote",begin:"^>\\s+",contains:o, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},t,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}},grmr_objectivec:e=>{ +const n=/[a-zA-Z@][a-zA-Z0-9_]*/,t={$pattern:n, +keyword:["@interface","@class","@protocol","@implementation"]};return{ +name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], +keywords:{"variable.language":["this","super"],$pattern:n, +keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"], +literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"], +built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"], +type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"] +},illegal:"/,end:/$/,illegal:"\\n" +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class", +begin:"("+t.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:t, +contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE, +relevance:0}]}},grmr_perl:e=>{const n=e.regex,t=/[dualxmsipngr]{0,12}/,a={ +$pattern:/[\w.]+/, +keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0" +},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},r={begin:/->\{/, +end:/\}/},s={variants:[{begin:/\$\d/},{ +begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") +},{begin:/[$%@][^\s\w{]/,relevance:0}] +},o=[e.BACKSLASH_ESCAPE,i,s],l=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],c=(e,a,i="\\1")=>{ +const r="\\1"===i?i:n.concat(i,a) +;return n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,r,/(?:\\.|[^\\\/])*?/,i,t) +},d=(e,a,i)=>n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,i,t),g=[s,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{ +endsWithParent:!0}),r,{className:"string",contains:o,variants:[{ +begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", +end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ +begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", +relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", +contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{ +begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number", +begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", +relevance:0},{ +begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", +keywords:"split return print reverse grep",relevance:0, +contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{ +begin:c("s|tr|y",n.either(...l,{capture:!0}))},{begin:c("s|tr|y","\\(","\\)")},{ +begin:c("s|tr|y","\\[","\\]")},{begin:c("s|tr|y","\\{","\\}")}],relevance:2},{ +className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{ +begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",n.either(...l,{capture:!0 +}),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{ +begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub", +end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{ +begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$", +subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}] +}];return i.contains=g,r.contains=g,{name:"Perl",aliases:["pl","pm"],keywords:a, +contains:g}},grmr_php:e=>{ +const n=e.regex,t=/(?![A-Za-z0-9])(?![$])/,a=n.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,t),i=n.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,t),r={ +scope:"variable",match:"\\$+"+a},s={scope:"subst",variants:[{begin:/\$\w+/},{ +begin:/\{\$/,end:/\}/}]},o=e.inherit(e.APOS_STRING_MODE,{illegal:null +}),l="[ \t\n]",c={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{ +illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(s)}),o,{ +begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/, +contains:e.QUOTE_STRING_MODE.contains.concat(s),"on:begin":(e,n)=>{ +n.data._beginMatch=e[1]||e[2]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}},e.END_SAME_AS_BEGIN({ +begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/})]},d={scope:"number",variants:[{ +begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{ +begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{ +begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?" +}],relevance:0 +},g=["false","null","true"],u=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],b=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],m={ +keyword:u,literal:(e=>{const n=[];return e.forEach((e=>{ +n.push(e),e.toLowerCase()===e?n.push(e.toUpperCase()):n.push(e.toLowerCase()) +})),n})(g),built_in:b},p=e=>e.map((e=>e.replace(/\|\d+$/,""))),_={variants:[{ +match:[/new/,n.concat(l,"+"),n.concat("(?!",p(b).join("\\b|"),"\\b)"),i],scope:{ +1:"keyword",4:"title.class"}}]},h=n.concat(a,"\\b(?!\\()"),f={variants:[{ +match:[n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{2:"variable.constant" +}},{match:[/::/,/class/],scope:{2:"variable.language"}},{ +match:[i,n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{1:"title.class", +3:"variable.constant"}},{match:[i,n.concat("::",n.lookahead(/(?!class\b)/))], +scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class", +3:"variable.language"}}]},E={scope:"attr", +match:n.concat(a,n.lookahead(":"),n.lookahead(/(?!::)/))},y={relevance:0, +begin:/\(/,end:/\)/,keywords:m,contains:[E,r,f,e.C_BLOCK_COMMENT_MODE,c,d,_] +},N={relevance:0, +match:[/\b/,n.concat("(?!fn\\b|function\\b|",p(u).join("\\b|"),"|",p(b).join("\\b|"),"\\b)"),a,n.concat(l,"*"),n.lookahead(/(?=\()/)], +scope:{3:"title.function.invoke"},contains:[y]};y.contains.push(N) +;const w=[E,f,e.C_BLOCK_COMMENT_MODE,c,d,_];return{case_insensitive:!1, +keywords:m,contains:[{begin:n.concat(/#\[\s*/,i),beginScope:"meta",end:/]/, +endScope:"meta",keywords:{literal:g,keyword:["new","array"]},contains:[{ +begin:/\[/,end:/]/,keywords:{literal:g,keyword:["new","array"]}, +contains:["self",...w]},...w,{scope:"meta",match:i}] +},e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{ +scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/, +keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE, +contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{ +begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{ +begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},r,N,f,{ +match:[/const/,/\s/,a],scope:{1:"keyword",3:"variable.constant"}},_,{ +scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/, +excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use" +},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params", +begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:m, +contains:["self",r,f,e.C_BLOCK_COMMENT_MODE,c,d]}]},{scope:"class",variants:[{ +beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait", +illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{ +beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ +beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/, +contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{ +beginKeywords:"use",relevance:0,end:";",contains:[{ +match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},c,d]} +},grmr_php_template:e=>({name:"PHP template",subLanguage:"xml",contains:[{ +begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*", +end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0 +},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null, +skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null, +contains:null,skip:!0})]}]}),grmr_plaintext:e=>({name:"Plain text", +aliases:["text","txt"],disableAutodetect:!0}),grmr_python:e=>{ +const n=e.regex,t=/[\p{XID_Start}_]\p{XID_Continue}*/u,a=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={ +$pattern:/[A-Za-z]\w+|__\w+__/,keyword:a, +built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], +literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], +type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] +},r={className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/, +end:/\}/,keywords:i,illegal:/#/},o={begin:/\{\{/,relevance:0},l={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, +end:/"""/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([uU]|[rR])'/,end:/'/, +relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ +begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, +end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, +contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},c="[0-9](_?[0-9])*",d=`(\\b(${c}))?\\.(${c})|\\b(${c})\\.`,g="\\b|"+a.join("|"),u={ +className:"number",relevance:0,variants:[{ +begin:`(\\b(${c})|(${d}))[eE][+-]?(${c})[jJ]?(?=${g})`},{begin:`(${d})[jJ]?`},{ +begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${g})`},{ +begin:`\\b0[bB](_?[01])+[lL]?(?=${g})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${g})` +},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${g})`},{begin:`\\b(${c})[jJ](?=${g})` +}]},b={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i, +contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={ +className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/, +end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i, +contains:["self",r,u,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,u,r],{ +name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i, +illegal:/(<\/|\?)|=>/,contains:[r,u,{begin:/\bself\b/},{beginKeywords:"if", +relevance:0},l,b,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,t],scope:{ +1:"keyword",3:"title.function"},contains:[m]},{variants:[{ +match:[/\bclass/,/\s+/,t,/\s*/,/\(\s*/,t,/\s*\)/]},{match:[/\bclass/,/\s+/,t]}], +scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{ +className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[u,m,l]}]}}, +grmr_python_repl:e=>({aliases:["pycon"],contains:[{className:"meta.prompt", +starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{ +begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}),grmr_r:e=>{ +const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,a=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,r=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/) +;return{name:"R",keywords:{$pattern:t, +keyword:"function if in break next repeat else for while", +literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", +built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" +},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/, +starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)), +endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{ +scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0 +}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}] +}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE], +variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', +relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{ +1:"operator",2:"number"},match:[i,a]},{scope:{1:"operator",2:"number"}, +match:[/%[^%]*%/,a]},{scope:{1:"punctuation",2:"number"},match:[r,a]},{scope:{ +2:"number"},match:[/[^a-zA-Z0-9._]|^/,a]}]},{scope:{3:"operator"}, +match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{ +match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:r},{begin:"`",end:"`", +contains:[{begin:/\\./}]}]}},grmr_ruby:e=>{ +const n=e.regex,t="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",a=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(a,/(::\w+)*/),r={ +"variable.constant":["__FILE__","__LINE__","__ENCODING__"], +"variable.language":["self","super"], +keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield","include","extend","prepend","public","private","protected","raise","throw"], +built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"], +literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},o={ +begin:"#<",end:">"},l=[e.COMMENT("#","$",{contains:[s] +}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10 +}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/, +end:/\}/,keywords:r},d={className:"string",contains:[e.BACKSLASH_ESCAPE,c], +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{ +begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{ +begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//, +end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{ +begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{ +begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ +begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ +begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ +begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)), +contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, +contains:[e.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",u={className:"number", +relevance:0,variants:[{ +begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ +begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" +},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ +begin:"\\b0(_?[0-7])+r?i?\\b"}]},b={variants:[{match:/\(\)/},{ +className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0, +keywords:r}]},m=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{ +match:[/\b(class|module)\s+/,i]}],scope:{2:"title.class", +4:"title.class.inherited"},keywords:r},{match:[/(include|extend)\s+/,i],scope:{ +2:"title.class"},keywords:r},{relevance:0,match:[i,/\.new[. (]/],scope:{ +1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},{relevance:0,match:a,scope:"title.class"},{ +match:[/def/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[b]},{ +begin:e.IDENT_RE+"::"},{className:"symbol", +begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", +begin:":(?!\\s)",contains:[d,{begin:t}],relevance:0},u,{className:"variable", +begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ +className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0, +relevance:0,keywords:r},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*", +keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c], +illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{ +begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[", +end:"\\][a-z]*"}]}].concat(o,l),relevance:0}].concat(o,l) +;c.contains=m,b.contains=m;const p=[{begin:/^\s*=>/,starts:{end:"$",contains:m} +},{className:"meta.prompt", +begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", +starts:{end:"$",keywords:r,contains:m}}];return l.unshift(o),{name:"Ruby", +aliases:["rb","gemspec","podspec","thor","irb"],keywords:r,illegal:/\/\*/, +contains:[e.SHEBANG({binary:"ruby"})].concat(p).concat(l).concat(m)}}, +grmr_rust:e=>{const n=e.regex,t={className:"title.function.invoke",relevance:0, +begin:n.concat(/\b/,/(?!let|for|while|if|else|match\b)/,e.IDENT_RE,n.lookahead(/\s*\(/)) +},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],r=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"] +;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:r, +keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"], +literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},t]}}, +grmr_scss:e=>{const n=ie(e),t=le,a=oe,i="@[a-z-]+",r={className:"variable", +begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS", +case_insensitive:!0,illegal:"[=/|']", +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,n.CSS_NUMBER_MODE,{ +className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ +className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 +},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", +begin:"\\b("+re.join("|")+")\\b",relevance:0},{className:"selector-pseudo", +begin:":("+a.join("|")+")"},{className:"selector-pseudo", +begin:":(:)?("+t.join("|")+")"},r,{begin:/\(/,end:/\)/, +contains:[n.CSS_NUMBER_MODE]},n.CSS_VARIABLE,{className:"attribute", +begin:"\\b("+ce.join("|")+")\\b"},{ +begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" +},{begin:/:/,end:/[;}{]/,relevance:0, +contains:[n.BLOCK_COMMENT,r,n.HEXCOLOR,n.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.IMPORTANT,n.FUNCTION_DISPATCH] +},{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{ +begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, +keyword:"and or not only",attribute:se.join(" ")},contains:[{begin:i, +className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" +},r,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE] +},n.FUNCTION_DISPATCH]}},grmr_shell:e=>({name:"Shell Session", +aliases:["console","shellsession"],contains:[{className:"meta.prompt", +begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, +subLanguage:"bash"}}]}),grmr_sql:e=>{ +const n=e.regex,t=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],r=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=r,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!r.includes(e))),c={ +begin:n.concat(/\b/,n.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}} +;return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ +$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:n,when:t}={})=>{const a=t +;return n=n||[],e.map((e=>e.match(/\|\d+$/)||n.includes(e)?e:a(e)?e+"|0":e)) +})(l,{when:e=>e.length<3}),literal:a,type:i, +built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] +},contains:[{begin:n.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/, +keyword:l.concat(s),literal:a,type:i}},{className:"type", +begin:n.either("double precision","large object","with timezone","without timezone") +},c,{className:"variable",begin:/@[a-z0-9][a-z0-9_]*/},{className:"string", +variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/, +contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{ +className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/, +relevance:0}]}},grmr_swift:e=>{const n={match:/\s+/,relevance:0 +},t=e.COMMENT("/\\*","\\*/",{contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],i={ +match:[/\./,m(...xe,...Me)],className:{2:"keyword"}},r={match:b(/\./,m(...Ae)), +relevance:0},s=Ae.filter((e=>"string"==typeof e)).concat(["_|0"]),o={variants:[{ +className:"keyword", +match:m(...Ae.filter((e=>"string"!=typeof e)).concat(Se).map(ke),...Me)}]},l={ +$pattern:m(/\b\w+/,/#\w+/),keyword:s.concat(Re),literal:Ce},c=[i,r,o],g=[{ +match:b(/\./,m(...De)),relevance:0},{className:"built_in", +match:b(/\b/,m(...De),/(?=\()/)}],u={match:/->/,relevance:0},p=[u,{ +className:"operator",relevance:0,variants:[{match:Be},{match:`\\.(\\.|${Le})+`}] +}],_="([0-9]_*)+",h="([0-9a-fA-F]_*)+",f={className:"number",relevance:0, +variants:[{match:`\\b(${_})(\\.(${_}))?([eE][+-]?(${_}))?\\b`},{ +match:`\\b0x(${h})(\\.(${h}))?([pP][+-]?(${_}))?\\b`},{match:/\b0o([0-7]_*)+\b/ +},{match:/\b0b([01]_*)+\b/}]},E=(e="")=>({className:"subst",variants:[{ +match:b(/\\/,e,/[0\\tnr"']/)},{match:b(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}] +}),y=(e="")=>({className:"subst",match:b(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/) +}),N=(e="")=>({className:"subst",label:"interpol",begin:b(/\\/,e,/\(/),end:/\)/ +}),w=(e="")=>({begin:b(e,/"""/),end:b(/"""/,e),contains:[E(e),y(e),N(e)] +}),v=(e="")=>({begin:b(e,/"/),end:b(/"/,e),contains:[E(e),N(e)]}),O={ +className:"string", +variants:[w(),w("#"),w("##"),w("###"),v(),v("#"),v("##"),v("###")] +},k=[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0, +contains:[e.BACKSLASH_ESCAPE]}],x={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//, +contains:k},M=e=>{const n=b(e,/\//),t=b(/\//,e);return{begin:n,end:t, +contains:[...k,{scope:"comment",begin:`#(?!.*${t})`,end:/$/}]}},S={ +scope:"regexp",variants:[M("###"),M("##"),M("#"),x]},A={match:b(/`/,Fe,/`/) +},C=[A,{className:"variable",match:/\$\d+/},{className:"variable", +match:`\\$${ze}+`}],T=[{match:/(@|#(un)?)available/,scope:"keyword",starts:{ +contains:[{begin:/\(/,end:/\)/,keywords:Pe,contains:[...p,f,O]}]}},{ +scope:"keyword",match:b(/@/,m(...je))},{scope:"meta",match:b(/@/,Fe)}],R={ +match:d(/\b[A-Z]/),relevance:0,contains:[{className:"type", +match:b(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,ze,"+") +},{className:"type",match:Ue,relevance:0},{match:/[?!]+/,relevance:0},{ +match:/\.\.\./,relevance:0},{match:b(/\s+&\s+/,d(Ue)),relevance:0}]},D={ +begin://,keywords:l,contains:[...a,...c,...T,u,R]};R.contains.push(D) +;const I={begin:/\(/,end:/\)/,relevance:0,keywords:l,contains:["self",{ +match:b(Fe,/\s*:/),keywords:"_|0",relevance:0 +},...a,S,...c,...g,...p,f,O,...C,...T,R]},L={begin://, +keywords:"repeat each",contains:[...a,R]},B={begin:/\(/,end:/\)/,keywords:l, +contains:[{begin:m(d(b(Fe,/\s*:/)),d(b(Fe,/\s+/,Fe,/\s*:/))),end:/:/, +relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params", +match:Fe}]},...a,...c,...p,f,O,...T,R,I],endsParent:!0,illegal:/["']/},$={ +match:[/(func|macro)/,/\s+/,m(A.match,Fe,Be)],className:{1:"keyword", +3:"title.function"},contains:[L,B,n],illegal:[/\[/,/%/]},z={ +match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"}, +contains:[L,B,n],illegal:/\[|%/},F={match:[/operator/,/\s+/,Be],className:{ +1:"keyword",3:"title"}},U={begin:[/precedencegroup/,/\s+/,Ue],className:{ +1:"keyword",3:"title"},contains:[R],keywords:[...Te,...Ce],end:/}/} +;for(const e of O.variants){const n=e.contains.find((e=>"interpol"===e.label)) +;n.keywords=l;const t=[...c,...g,...p,f,O,...C];n.contains=[...t,{begin:/\(/, +end:/\)/,contains:["self",...t]}]}return{name:"Swift",keywords:l, +contains:[...a,$,z,{beginKeywords:"struct protocol class extension enum actor", +end:"\\{",excludeEnd:!0,keywords:l,contains:[e.inherit(e.TITLE_MODE,{ +className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...c] +},F,U,{beginKeywords:"import",end:/$/,contains:[...a],relevance:0 +},S,...c,...g,...p,f,O,...C,...T,R,I]}},grmr_typescript:e=>{ +const n=Oe(e),t=_e,a=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],i={ +beginKeywords:"namespace",end:/\{/,excludeEnd:!0, +contains:[n.exports.CLASS_REFERENCE]},r={beginKeywords:"interface",end:/\{/, +excludeEnd:!0,keywords:{keyword:"interface extends",built_in:a}, +contains:[n.exports.CLASS_REFERENCE]},s={$pattern:_e, +keyword:he.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]), +literal:fe,built_in:ve.concat(a),"variable.language":we},o={className:"meta", +begin:"@"+t},l=(e,n,t)=>{const a=e.contains.findIndex((e=>e.label===n)) +;if(-1===a)throw Error("can not find mode to replace");e.contains.splice(a,1,t)} +;return Object.assign(n.keywords,s), +n.exports.PARAMS_CONTAINS.push(o),n.contains=n.contains.concat([o,i,r]), +l(n,"shebang",e.SHEBANG()),l(n,"use_strict",{className:"meta",relevance:10, +begin:/^\s*['"]use strict['"]/ +}),n.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(n,{ +name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),n},grmr_vbnet:e=>{ +const n=e.regex,t=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,i=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,s={ +className:"literal",variants:[{begin:n.concat(/# */,n.either(a,t),/ *#/)},{ +begin:n.concat(/# */,r,/ *#/)},{begin:n.concat(/# */,i,/ *#/)},{ +begin:n.concat(/# */,n.either(a,t),/ +/,n.either(i,r),/ *#/)}] +},o=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}] +}),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]}) +;return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0, +classNameAliases:{label:"symbol"},keywords:{ +keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", +built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", +type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", +literal:"true false nothing"}, +illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ +className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, +end:/"/,illegal:/\n/,contains:[{begin:/""/}]},s,{className:"number",relevance:0, +variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ +},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ +begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ +className:"label",begin:/^\w+:/},o,l,{className:"meta", +begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, +end:/$/,keywords:{ +keyword:"const disable else elseif enable end externalsource if region then"}, +contains:[l]}]}},grmr_wasm:e=>{e.regex;const n=e.COMMENT(/\(;/,/;\)/) +;return n.contains.push("self"),{name:"WebAssembly",keywords:{$pattern:/[\w.]+/, +keyword:["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"] +},contains:[e.COMMENT(/;;/,/$/),n,{match:[/(?:offset|align)/,/\s*/,/=/], +className:{1:"keyword",3:"operator"}},{className:"variable",begin:/\$[\w_]+/},{ +match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},{ +begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword", +3:"title.function"}},e.QUOTE_STRING_MODE,{match:/(i32|i64|f32|f64)(?!\.)/, +className:"type"},{className:"keyword", +match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/ +},{className:"number",relevance:0, +match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/ +}]}},grmr_xml:e=>{ +const n=e.regex,t=n.concat(/[\p{L}_]/u,n.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),a={ +className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/, +contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] +},r=e.inherit(i,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{ +className:"string"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),l={ +endsWithParent:!0,illegal:/`]+/}]}]}]};return{ +name:"HTML, XML", +aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], +case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,o,s,r,{begin:/\[/,end:/\]/,contains:[{ +className:"meta",begin://,contains:[i,r,o,s]}]}] +},e.COMMENT(//,{relevance:10}),{begin://, +relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, +relevance:10,contains:[o]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"style"},contains:[l],starts:{ +end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"script"},contains:[l],starts:{ +end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ +className:"tag",begin:/<>|<\/>/},{className:"tag", +begin:n.concat(//,/>/,/\s/)))), +end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:l}]},{ +className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(t,/>/))),contains:[{ +className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]} +},grmr_yaml:e=>{ +const n="true false yes no null",t="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={ +className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ +},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", +variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(a,{ +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),r={ +end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},s={begin:/\{/, +end:/\}/,contains:[r],illegal:"\\n",relevance:0},o={begin:"\\[",end:"\\]", +contains:[r],illegal:"\\n",relevance:0},l=[{className:"attr",variants:[{ +begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ +begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", +relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+t},{className:"type", +begin:"!<"+t+">"},{className:"type",begin:"!"+t},{className:"type",begin:"!!"+t +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},s,o,a],c=[...l] +;return c.pop(),c.push(i),r.contains=c,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:l}}});const He=ae;for(const e of Object.keys(Ke)){ +const n=e.replace("grmr_","").replace("_","-");He.registerLanguage(n,Ke[e])} +return He}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); \ No newline at end of file diff --git a/libraries/index.json b/libraries/index.json new file mode 100644 index 0000000..f0e2d11 --- /dev/null +++ b/libraries/index.json @@ -0,0 +1,11 @@ +[ + { "id": "stdio", "name": "stdio.h", "group": "C Standard Library" }, + { "id": "stdlib", "name": "stdlib.h", "group": "C Standard Library" }, + { "id": "string", "name": "string.h", "group": "C Standard Library" }, + { "id": "math", "name": "math.h", "group": "C Standard Library" }, + { "id": "time", "name": "time.h", "group": "C Standard Library" }, + { "id": "glfw", "name": "GLFW", "group": "OpenGL" }, + { "id": "glad", "name": "glad", "group": "OpenGL" }, + { "id": "gl", "name": "OpenGL", "group": "OpenGL" }, + { "id": "sdl3", "name": "SDL3", "group": "SDL3" } +] diff --git a/libraries/math.json b/libraries/math.json new file mode 100644 index 0000000..5dfb523 --- /dev/null +++ b/libraries/math.json @@ -0,0 +1,88 @@ +{ + "procedures": [ + { + "name": "sin", + "inputs": [["x","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Returns the sine of an angle in radians." + }, + { + "name": "cos", + "inputs": [["x","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Returns the cosine of an angle in radians." + }, + { + "name": "tan", + "inputs": [["x","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Returns the tangent of an angle in radians." + }, + { + "name": "sqrt", + "inputs": [["x","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Returns the square root of a number." + }, + { + "name": "pow", + "inputs": [["x","float"],["y","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Returns a number raised to a power." + }, + { + "name": "exp", + "inputs": [["x","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Returns e raised to the given power." + }, + { + "name": "log", + "inputs": [["x","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Returns the natural logarithm of a number." + }, + { + "name": "log10", + "inputs": [["x","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Returns the base-10 logarithm of a number." + }, + { + "name": "fabs", + "inputs": [["x","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Returns the absolute value of a floating-point number." + }, + { + "name": "floor", + "inputs": [["x","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Rounds a number down to the nearest integer." + }, + { + "name": "ceil", + "inputs": [["x","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Rounds a number up to the nearest integer." + }, + { + "name": "fmod", + "inputs": [["x","float"],["y","float"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Returns the floating-point remainder of division." + } + ] +} diff --git a/libraries/sdl3.json b/libraries/sdl3.json new file mode 100644 index 0000000..ecb4234 --- /dev/null +++ b/libraries/sdl3.json @@ -0,0 +1,238 @@ +{ + "procedures": [ + { + "name": "SDL_Init", + "inputs": [["flags","unsigned int"]], + "outputs": [], + "returnStyle": "error", + "desc": "Initializes SDL subsystems (video, audio, etc.) with the given flags." + }, + { + "name": "SDL_Quit", + "inputs": [], + "outputs": [], + "returnStyle": "void", + "desc": "Shuts down all SDL subsystems and cleans up resources." + }, + { + "name": "SDL_SetAppMetadata", + "inputs": [["name","string"],["version","string"],["identifier","string"]], + "outputs": [], + "returnStyle": "error", + "desc": "Sets the application name, version, and identifier." + }, + { + "name": "SDL_CreateWindow", + "inputs": [["title","string"],["w","int"],["h","int"],["flags","unsigned int"]], + "outputs": [["window","int"]], + "returnStyle": "value", + "desc": "Creates a window with the given title, size, and flags." + }, + { + "name": "SDL_DestroyWindow", + "inputs": [["window","int"]], + "outputs": [], + "returnStyle": "void", + "desc": "Destroys a window and frees its resources." + }, + { + "name": "SDL_SetWindowTitle", + "inputs": [["window","int"],["title","string"]], + "outputs": [], + "returnStyle": "error", + "desc": "Changes the title of a window." + }, + { + "name": "SDL_GetWindowSize", + "inputs": [["window","int"]], + "outputs": [["w","int"],["h","int"]], + "desc": "Retrieves the current width and height of a window." + }, + { + "name": "SDL_ShowWindow", + "inputs": [["window","int"]], + "outputs": [], + "returnStyle": "error", + "desc": "Makes a hidden window visible on screen." + }, + { + "name": "SDL_HideWindow", + "inputs": [["window","int"]], + "outputs": [], + "returnStyle": "error", + "desc": "Hides a window from view." + }, + { + "name": "SDL_RaiseWindow", + "inputs": [["window","int"]], + "outputs": [], + "returnStyle": "error", + "desc": "Brings a window to the front of the display." + }, + { + "name": "SDL_MaximizeWindow", + "inputs": [["window","int"]], + "outputs": [], + "returnStyle": "error", + "desc": "Maximizes a window to fill the screen." + }, + { + "name": "SDL_MinimizeWindow", + "inputs": [["window","int"]], + "outputs": [], + "returnStyle": "error", + "desc": "Minimizes a window to the taskbar." + }, + { + "name": "SDL_RestoreWindow", + "inputs": [["window","int"]], + "outputs": [], + "returnStyle": "error", + "desc": "Restores a window from maximized or minimized state." + }, + { + "name": "SDL_CreateRenderer", + "inputs": [["window","int"],["name","string"]], + "outputs": [["renderer","int"]], + "returnStyle": "value", + "desc": "Creates a 2D renderer for the given window." + }, + { + "name": "SDL_DestroyRenderer", + "inputs": [["renderer","int"]], + "outputs": [], + "returnStyle": "void", + "desc": "Destroys a renderer and frees its resources." + }, + { + "name": "SDL_RenderClear", + "inputs": [["renderer","int"]], + "outputs": [], + "returnStyle": "error", + "desc": "Clears the render target with the current draw color." + }, + { + "name": "SDL_RenderPresent", + "inputs": [["renderer","int"]], + "outputs": [], + "returnStyle": "error", + "desc": "Presents the rendered frame to the screen." + }, + { + "name": "SDL_SetRenderDrawColor", + "inputs": [["renderer","int"],["r","int"],["g","int"],["b","int"],["a","int"]], + "outputs": [], + "returnStyle": "error", + "desc": "Sets the color used for drawing operations." + }, + { + "name": "SDL_RenderPoint", + "inputs": [["renderer","int"],["x","float"],["y","float"]], + "outputs": [], + "returnStyle": "error", + "desc": "Draws a single point at the given coordinates." + }, + { + "name": "SDL_RenderLine", + "inputs": [["renderer","int"],["x1","float"],["y1","float"],["x2","float"],["y2","float"]], + "outputs": [], + "returnStyle": "error", + "desc": "Draws a line between two points." + }, + { + "name": "SDL_RenderRect", + "inputs": [["renderer","int"],["x","float"],["y","float"],["w","float"],["h","float"]], + "outputs": [], + "returnStyle": "error", + "desc": "Draws a rectangle outline at the given position and size." + }, + { + "name": "SDL_RenderFillRect", + "inputs": [["renderer","int"],["x","float"],["y","float"],["w","float"],["h","float"]], + "outputs": [], + "returnStyle": "error", + "desc": "Draws a filled rectangle at the given position and size." + }, + { + "name": "SDL_PollEvent", + "inputs": [], + "outputs": [["event","int"]], + "desc": "Polls for the next pending input event." + }, + { + "name": "SDL_WaitEvent", + "inputs": [], + "outputs": [["event","int"]], + "desc": "Waits indefinitely for the next input event." + }, + { + "name": "SDL_PumpEvents", + "inputs": [], + "outputs": [], + "returnStyle": "void", + "desc": "Processes pending events without blocking." + }, + { + "name": "SDL_FlushEvents", + "inputs": [["minType","unsigned int"],["maxType","unsigned int"]], + "outputs": [], + "returnStyle": "void", + "desc": "Discards all pending events in a given range." + }, + { + "name": "SDL_GetTicks", + "inputs": [], + "outputs": [["ticks","unsigned int"]], + "returnStyle": "value", + "desc": "Returns milliseconds since SDL initialization." + }, + { + "name": "SDL_Delay", + "inputs": [["ms","unsigned int"]], + "outputs": [], + "returnStyle": "void", + "desc": "Pauses execution for the given number of milliseconds." + }, + { + "name": "SDL_GetKeyboardState", + "inputs": [], + "outputs": [["state","string"]], + "desc": "Returns a snapshot of the current keyboard state." + }, + { + "name": "SDL_GetModState", + "inputs": [], + "outputs": [["mod","unsigned int"]], + "returnStyle": "value", + "desc": "Returns the current modifier key state (Shift, Ctrl, Alt)." + }, + { + "name": "SDL_GetError", + "inputs": [], + "outputs": [["msg","string"]], + "returnStyle": "value", + "desc": "Returns the last error message as a string." + }, + { + "name": "SDL_Log", + "inputs": [["fmt","string"]], + "outputs": [], + "returnStyle": "void", + "desc": "Outputs a log message via SDL's logging system." + }, + { + "name": "SDL_GetTicksNS", + "inputs": [], + "outputs": [["ticks","unsigned int"]], + "returnStyle": "value", + "desc": "Returns nanoseconds since SDL initialization." + }, + { + "name": "SDL_DelayNS", + "inputs": [["ns","unsigned int"]], + "outputs": [], + "returnStyle": "void", + "desc": "Pauses execution for the given number of nanoseconds." + } + ] +} diff --git a/libraries/stdio.json b/libraries/stdio.json new file mode 100644 index 0000000..c9797d3 --- /dev/null +++ b/libraries/stdio.json @@ -0,0 +1,40 @@ +{ + "procedures": [ + { + "name": "puts", + "inputs": [["str","string"]], + "outputs": [], + "returnStyle": "void", + "desc": "Outputs a string followed by a newline to the console." + }, + { + "name": "printf", + "inputs": [["fmt","string"]], + "outputs": [["result","int"]], + "returnStyle": "value", + "formatString": true, + "desc": "Outputs a formatted string with placeholders to the console. Dynamically creates data inputs for each % specifier in the format string." + }, + { + "name": "getchar", + "inputs": [], + "outputs": [["result","int"]], + "returnStyle": "value", + "desc": "Reads a single character from the keyboard input." + }, + { + "name": "putchar", + "inputs": [["c","int"]], + "outputs": [["result","int"]], + "returnStyle": "value", + "desc": "Writes a single character to the console output." + }, + { + "name": "perror", + "inputs": [["msg","string"]], + "outputs": [], + "returnStyle": "void", + "desc": "Prints an error message describing the last error to stderr." + } + ] +} diff --git a/libraries/stdlib.json b/libraries/stdlib.json new file mode 100644 index 0000000..6f5f0f4 --- /dev/null +++ b/libraries/stdlib.json @@ -0,0 +1,67 @@ +{ + "procedures": [ + { + "name": "malloc", + "inputs": [["size","int"]], + "outputs": [["ptr","int"]], + "returnStyle": "value", + "desc": "Allocates a block of memory of the given size." + }, + { + "name": "free", + "inputs": [["ptr","int"]], + "outputs": [], + "returnStyle": "void", + "desc": "Releases a previously allocated memory block." + }, + { + "name": "exit", + "inputs": [["status","int"]], + "outputs": [], + "returnStyle": "void", + "desc": "Terminates the program immediately with the given status code." + }, + { + "name": "rand", + "inputs": [], + "outputs": [["result","int"]], + "returnStyle": "value", + "desc": "Returns a random integer between 0 and RAND_MAX." + }, + { + "name": "srand", + "inputs": [["seed","unsigned int"]], + "outputs": [], + "returnStyle": "void", + "desc": "Seeds the random number generator with the given value." + }, + { + "name": "atoi", + "inputs": [["str","string"]], + "outputs": [["result","int"]], + "returnStyle": "value", + "desc": "Converts a string to an integer." + }, + { + "name": "atof", + "inputs": [["str","string"]], + "outputs": [["result","float"]], + "returnStyle": "value", + "desc": "Converts a string to a floating-point number." + }, + { + "name": "abs", + "inputs": [["n","int"]], + "outputs": [["result","int"]], + "returnStyle": "value", + "desc": "Returns the absolute value of an integer." + }, + { + "name": "system", + "inputs": [["cmd","string"]], + "outputs": [["result","int"]], + "returnStyle": "value", + "desc": "Executes a shell command." + } + ] +} diff --git a/libraries/string.json b/libraries/string.json new file mode 100644 index 0000000..b7c667a --- /dev/null +++ b/libraries/string.json @@ -0,0 +1,18 @@ +{ + "procedures": [ + { + "name": "strlen", + "inputs": [["str","string"]], + "outputs": [["result","int"]], + "returnStyle": "value", + "desc": "Returns the length of a string." + }, + { + "name": "strcmp", + "inputs": [["s1","string"],["s2","string"]], + "outputs": [["result","int"]], + "returnStyle": "value", + "desc": "Compares two strings lexicographically." + } + ] +} diff --git a/libraries/time.json b/libraries/time.json new file mode 100644 index 0000000..6414411 --- /dev/null +++ b/libraries/time.json @@ -0,0 +1,18 @@ +{ + "procedures": [ + { + "name": "time", + "inputs": [["timer","int"]], + "outputs": [["result","int"]], + "returnStyle": "value", + "desc": "Returns the current calendar time in seconds." + }, + { + "name": "clock", + "inputs": [], + "outputs": [["result","int"]], + "returnStyle": "value", + "desc": "Returns the processor time used by the program." + } + ] +} diff --git a/persist.js b/persist.js index ab92b78..07144a2 100644 --- a/persist.js +++ b/persist.js @@ -32,26 +32,38 @@ function saveBlockData(block) { */ function saveWorkspace() { const prog = $id("main-program"); + const dataArea = $id("data-area"); + const varArea = $id("variable-area"); + const connectionsSvg = $id("connections"); + if (!prog || !dataArea || !varArea || !connectionsSvg) { + showError("Cannot save: missing core UI elements"); + return; + } + const state = { version: 1, counter: prog.counter, blocks: { algorithm: [], measurements: [], data: [], procedures: [] }, - connections: [] + connections: [], + sections: typeof saveSections === "function" ? saveSections() : [] }; for (const block of prog.children) { + if (!block.saveData) continue; state.blocks.algorithm.push({ id: block.id, tag: block.tagName.toLowerCase(), data: saveBlockData(block) }); } - for (const block of $id("data-area").children) { + for (const block of dataArea.children) { + if (!block.saveData) continue; state.blocks.measurements.push({ id: block.id, tag: block.tagName.toLowerCase(), data: saveBlockData(block) }); } - for (const block of $id("variable-area").children) { + for (const block of varArea.children) { + if (!block.saveData) continue; state.blocks.data.push({ id: block.id, tag: block.tagName.toLowerCase(), data: saveBlockData(block) @@ -63,11 +75,11 @@ function saveWorkspace() { data: saveBlockData(block) }); } - for (const path of $id("connections").querySelectorAll("path.flow, path.flow-data")) { + for (const path of connectionsSvg.querySelectorAll("path.flow, path.flow-data")) { state.connections.push({ - fromBlock: path.fromElement.id, + fromBlock: path.fromElement ? path.fromElement.id : "", fromHook: path.fromHook ? (path.fromHook.id || null) : null, - toBlock: path.toElement.id, + toBlock: path.toElement ? path.toElement.id : "", toHook: path.toHook ? (path.toHook.id || null) : null }); } @@ -85,12 +97,17 @@ function saveWorkspace() { function restoreBlock(info) { const reg = BLOCK_REGISTRY[info.tag]; if (!reg) return null; + if (!info.data) { showError("Missing data for block " + info.id); return null; } - const frag = document.importNode(document.querySelector(reg.template).content, true); + const tmpl = document.querySelector(reg.template); + if (!tmpl) { showError("Missing template for " + info.tag); return null; } + const frag = document.importNode(tmpl.content, true); const el = frag.firstElementChild; + if (!el) { showError("Empty template for " + info.tag); return null; } el.id = info.id; - el.style.left = info.data.x + "px"; - el.style.top = info.data.y + "px"; + /* Only set position for algorithm tab blocks (they have flow connections) */ + if (info.data.x !== undefined) el.style.left = info.data.x + "px"; + if (info.data.y !== undefined) el.style.top = info.data.y + "px"; return el; } @@ -118,55 +135,64 @@ function loadWorkspace() { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return; let state; - try { state = JSON.parse(raw); } catch { return; } + try { state = JSON.parse(raw); } catch { showError("Could not load workspace: saved data is corrupted."); return; } const prog = $id("main-program"); const dataArea = $id("data-area"); const varArea = $id("variable-area"); const connectionsSvg = $id("connections"); + if (!prog || !dataArea || !varArea || !connectionsSvg) { + showError("Missing core UI elements for workspace load"); + return; + } + /* ── 1. Tear down existing state ── */ - for (const p of connectionsSvg.querySelectorAll("path.flow, path.flow-data")) p.remove(); - for (const block of document.querySelectorAll("[data-block]")) { - block.wcForeFlow = []; - block.wcNextFlow = []; - } - for (const block of [...prog.children]) { - if (block.id !== "main-start" && block.id !== "main-end") block.remove(); - } - dataArea.innerHTML = ""; - varArea.innerHTML = ""; - for (const pane of document.querySelectorAll(".lib-pane")) { - const pa = pane.querySelector(".procedures-area"); - if (pa) pa.innerHTML = ""; - } - document.querySelector(".lib-tab-bar").innerHTML = ""; + _clearWorkspace({ keepStorage: true }); prog.counter = state.counter; /* ── 2. Restore measurements (types + units) ── */ restoreGroup(state.blocks.measurements, dataArea); + const firstUnit = dataArea.querySelector("wc-unit"); + if (firstUnit) { + let unitHeader = dataArea.querySelector(".list-header"); + if (!unitHeader) { + unitHeader = document.createElement("div"); + unitHeader.className = "list-header"; + unitHeader.style.borderTop = "2px solid var(--border)"; + unitHeader.textContent = "Units \u2014 Fullname \u00a0 Type \u00a0 Suffix \u00a0 SI Factor \u00a0 Resolved"; + } + dataArea.insertBefore(unitHeader, firstUnit); + } /* ── 3. Restore procedures — BEFORE algorithm (WCProcCall depends on them) ── */ const libMap = {}; const procEls = []; + const paneProcedures = $id("pane-procedures"); + const procTabBar = document.querySelector(".lib-tab-bar"); for (const info of state.blocks.procedures) { const lib = (info.data && info.data.library) || "unknown"; if (!libMap[lib]) { + markLibLoaded(lib); const tab = document.createElement("button"); tab.className = "lib-tab"; tab.dataset.libName = lib; tab.textContent = lib; tab.addEventListener("click", () => switchLibTab(lib)); - document.querySelector(".lib-tab-bar").appendChild(tab); + if (procTabBar) procTabBar.appendChild(tab); const pane = document.createElement("div"); pane.className = "lib-pane"; pane.dataset.libName = lib; + const hdr = document.createElement("div"); + hdr.className = "list-header"; + hdr.textContent = "Procedure \u00a0 Inputs \u2192 Outputs"; + pane.appendChild(hdr); const pa = document.createElement("div"); pa.className = "procedures-area"; pane.appendChild(pa); - $id("pane-procedures").appendChild(pane); + if (paneProcedures) paneProcedures.appendChild(pane); libMap[lib] = pa; } @@ -204,10 +230,10 @@ function loadWorkspace() { if (!fromBlock || !toBlock) continue; const fromHook = conn.fromHook - ? fromBlock.querySelector("#" + conn.fromHook) + ? (fromBlock.querySelector('[id="' + conn.fromHook + '"]') || fromBlock.querySelector('[data-direction="out"]') || fromBlock.querySelector(".hook")) : (fromBlock.querySelector('[data-direction="out"]') || fromBlock.querySelector(".hook")); const toHook = conn.toHook - ? toBlock.querySelector("#" + conn.toHook) + ? (toBlock.querySelector('[id="' + conn.toHook + '"]') || toBlock.querySelector('[data-direction="in"]') || toBlock.querySelector(".hook")) : (toBlock.querySelector('[data-direction="in"]') || toBlock.querySelector(".hook")); if (!fromHook || !toHook) continue; @@ -225,10 +251,15 @@ function loadWorkspace() { drawpath(path); } - /* ── 7. Refresh dropdowns & SI displays (after all blocks + connections exist) ── */ + /* ── 7. Restore logic sections ── */ + if (state.sections && typeof restoreSections === "function") { + restoreSections(state.sections); + } + + /* ── 8. Refresh dropdowns & SI displays (after all blocks + connections exist) ── */ refreshAll(); - for (const u of dataArea.querySelectorAll("wc-unit")) u.updateSiDisplay(); - for (const d of varArea.querySelectorAll("wc-scalar")) d.updateSiDisplay(); + if (dataArea) for (const u of dataArea.querySelectorAll("wc-unit")) u.updateSiDisplay(); + if (varArea) for (const d of varArea.querySelectorAll("wc-scalar")) d.updateSiDisplay(); } @@ -237,7 +268,7 @@ function loadWorkspace() { * returning the app to its pristine default state. */ function newWorkspace() { - if (!confirm("Clear the entire workspace and start fresh?")) return; - localStorage.removeItem(STORAGE_KEY); - location.reload(); + if (typeof showNewProjectModal === "function") { + showNewProjectModal(); + } } diff --git a/proc-browser.js b/proc-browser.js new file mode 100644 index 0000000..1c1a900 --- /dev/null +++ b/proc-browser.js @@ -0,0 +1,360 @@ +// ── Procedure Browser Modal ──────────────────────────────────────────────── +// +// A modal popup that displays a searchable, collapsible tree of all available +// procedures (user-defined and library-provided). Replaces the old dropdown +//