import { Tag } from "@codemirror/highlight"; import type { MarkdownConfig } from "@lezer/markdown"; import { System } from "@plugos/plugos/system"; import { Manifest } from "@silverbulletmd/common/manifest"; export type MDExt = { // unicode char code for efficiency .charCodeAt(0) firstCharCodes: number[]; regex: RegExp; nodeType: string; tag: Tag; styles: { [key: string]: string }; }; export function mdExtensionSyntaxConfig({ regex, firstCharCodes, nodeType, }: MDExt): MarkdownConfig { return { defineNodes: [nodeType], parseInline: [ { name: nodeType, parse(cx, next, pos) { if (!firstCharCodes.includes(next)) { return -1; } let match = regex.exec(cx.slice(pos, cx.end)); if (!match) { return -1; } return cx.addElement(cx.elt(nodeType, pos, pos + match[0].length)); }, // after: "Emphasis", }, ], }; } export function mdExtensionStyleTags({ nodeType, tag }: MDExt): { [selector: string]: Tag | readonly Tag[]; } { return { [nodeType]: tag, }; } export function loadMarkdownExtensions(system: System): MDExt[] { let mdExtensions: MDExt[] = []; for (let plug of system.loadedPlugs.values()) { let manifest = plug.manifest as Manifest; if (manifest.syntax) { for (let [nodeType, def] of Object.entries(manifest.syntax)) { mdExtensions.push({ nodeType, tag: Tag.define(), firstCharCodes: def.firstCharacters.map((ch) => ch.charCodeAt(0)), regex: new RegExp("^" + def.regex), styles: def.styles, }); } } } return mdExtensions; }