Materialized query parser redo

pull/3/head
Zef Hemel 2022-04-11 20:34:09 +02:00
parent d649009dd2
commit b3c3302970
31 changed files with 1182 additions and 667 deletions

View File

@ -13,4 +13,14 @@ export type SilverBulletHooks = CommandHookT &
CronHookT &
EventHookT;
export type Manifest = plugos.Manifest<SilverBulletHooks>;
export type SyntaxExtensions = {
syntax?: { [key: string]: NodeDef };
};
export type NodeDef = {
firstCharacters: string[];
regex: string;
styles: { [key: string]: string };
};
export type Manifest = plugos.Manifest<SilverBulletHooks> & SyntaxExtensions;

84
common/parse_tree.ts Normal file
View File

@ -0,0 +1,84 @@
import type { SyntaxNode } from "@lezer/common";
import type { Language } from "@codemirror/language";
import { ParseTree } from "./tree";
export function lezerToParseTree(
text: string,
n: SyntaxNode,
offset = 0
): ParseTree {
let children: ParseTree[] = [];
let nodeText: string | undefined;
let child = n.firstChild;
while (child) {
children.push(lezerToParseTree(text, child));
child = child.nextSibling;
}
if (children.length === 0) {
children = [
{
from: n.from + offset,
to: n.to + offset,
text: text.substring(n.from, n.to),
},
];
} else {
let newChildren: ParseTree[] = [];
let index = n.from;
for (let child of children) {
let s = text.substring(index, child.from);
if (s) {
newChildren.push({
from: index + offset,
to: child.from! + offset,
text: s,
});
}
newChildren.push(child);
index = child.to!;
}
let s = text.substring(index, n.to);
if (s) {
newChildren.push({ from: index + offset, to: n.to + offset, text: s });
}
children = newChildren;
}
let result: ParseTree = {
type: n.name,
from: n.from + offset,
to: n.to + offset,
};
if (children.length > 0) {
result.children = children;
}
if (nodeText) {
result.text = nodeText;
}
return result;
}
export function parse(language: Language, text: string): ParseTree {
let tree = lezerToParseTree(text, language.parser.parse(text).topNode);
// replaceNodesMatching(tree, (n): MarkdownTree | undefined | null => {
// if (n.type === "FencedCode") {
// let infoN = findNodeMatching(n, (n) => n.type === "CodeInfo");
// let language = infoN!.children![0].text;
// let textN = findNodeMatching(n, (n) => n.type === "CodeText");
// let text = textN!.children![0].text!;
//
// console.log(language, text);
// switch (language) {
// case "yaml":
// let parsed = StreamLanguage.define(yaml).parser.parse(text);
// let subTree = treeToAST(text, parsed.topNode, n.from);
// // console.log(JSON.stringify(subTree, null, 2));
// subTree.type = "yaml";
// return subTree;
// }
// }
// return;
// });
return tree;
}

View File

@ -1,10 +1,12 @@
import { SysCallMapping } from "../../plugos/system";
import { MarkdownTree, parse } from "../tree";
import { parse } from "../parse_tree";
import { Language } from "@codemirror/language";
import type { ParseTree } from "../tree";
export function markdownSyscalls(): SysCallMapping {
export function markdownSyscalls(lang: Language): SysCallMapping {
return {
"markdown.parseMarkdown": (ctx, text: string): MarkdownTree => {
return parse(text);
"markdown.parseMarkdown": (ctx, text: string): ParseTree => {
return parse(lang, text);
},
};
}

View File

@ -1,14 +1,15 @@
import { expect, test } from "@jest/globals";
import { parse } from "../../common/tree";
import { parse } from "./parse_tree";
import {
addParentPointers,
collectNodesMatching,
findParentMatching,
nodeAtPos,
removeParentPointers,
renderMarkdown,
renderToText,
replaceNodesMatching
} from "./tree";
import wikiMarkdownLang from "../webapp/parser";
const mdTest1 = `
# Heading
@ -46,7 +47,8 @@ name: something
`;
test("Run a Node sandbox", async () => {
let mdTree = parse(mdTest1);
const lang = wikiMarkdownLang([]);
let mdTree = parse(lang, mdTest1);
addParentPointers(mdTree);
// console.log(JSON.stringify(mdTree, null, 2));
let wikiLink = nodeAtPos(mdTree, mdTest1.indexOf("Wiki Page"))!;
@ -59,7 +61,7 @@ test("Run a Node sandbox", async () => {
expect(allTodos.length).toBe(2);
// Render back into markdown should be equivalent
expect(renderMarkdown(mdTree)).toBe(mdTest1);
expect(renderToText(mdTree)).toBe(mdTest1);
removeParentPointers(mdTree);
replaceNodesMatching(mdTree, (n) => {
@ -70,6 +72,6 @@ test("Run a Node sandbox", async () => {
}
});
console.log(JSON.stringify(mdTree, null, 2));
let mdTree3 = parse(mdTest3);
let mdTree3 = parse(lang, mdTest3);
console.log(JSON.stringify(mdTree3, null, 2));
});

View File

@ -1,87 +1,137 @@
import { SyntaxNode } from "@lezer/common";
import wikiMarkdownLang from "../webapp/parser";
export type MarkdownTree = {
export type ParseTree = {
type?: string; // undefined === text node
from?: number;
to?: number;
text?: string;
children?: MarkdownTree[];
children?: ParseTree[];
// Only present after running addParentPointers
parent?: ParseTree;
};
function treeToAST(text: string, n: SyntaxNode, offset = 0): MarkdownTree {
let children: MarkdownTree[] = [];
let nodeText: string | undefined;
let child = n.firstChild;
while (child) {
children.push(treeToAST(text, child));
child = child.nextSibling;
export function addParentPointers(tree: ParseTree) {
if (!tree.children) {
return;
}
for (let child of tree.children) {
child.parent = tree;
addParentPointers(child);
}
}
if (children.length === 0) {
children = [
{
from: n.from + offset,
to: n.to + offset,
text: text.substring(n.from, n.to),
},
];
} else {
let newChildren: MarkdownTree[] | string = [];
let index = n.from;
export function removeParentPointers(tree: ParseTree) {
delete tree.parent;
if (!tree.children) {
return;
}
for (let child of tree.children) {
removeParentPointers(child);
}
}
export function findParentMatching(
tree: ParseTree,
matchFn: (tree: ParseTree) => boolean
): ParseTree | null {
let node = tree.parent;
while (node) {
if (matchFn(node)) {
return node;
}
node = node.parent;
}
return null;
}
export function collectNodesOfType(
tree: ParseTree,
nodeType: string
): ParseTree[] {
return collectNodesMatching(tree, (n) => n.type === nodeType);
}
export function collectNodesMatching(
tree: ParseTree,
matchFn: (tree: ParseTree) => boolean
): ParseTree[] {
if (matchFn(tree)) {
return [tree];
}
let results: ParseTree[] = [];
if (tree.children) {
for (let child of tree.children) {
results = [...results, ...collectNodesMatching(child, matchFn)];
}
}
return results;
}
// return value: returning undefined = not matched, continue, null = delete, new node = replace
export function replaceNodesMatching(
tree: ParseTree,
substituteFn: (tree: ParseTree) => ParseTree | null | undefined
) {
if (tree.children) {
let children = tree.children.slice();
for (let child of children) {
let s = text.substring(index, child.from);
if (s) {
newChildren.push({
from: index + offset,
to: child.from! + offset,
text: s,
});
let subst = substituteFn(child);
if (subst !== undefined) {
let pos = tree.children.indexOf(child);
if (subst) {
tree.children.splice(pos, 1, subst);
} else {
// null = delete
tree.children.splice(pos, 1);
}
} else {
replaceNodesMatching(child, substituteFn);
}
newChildren.push(child);
index = child.to!;
}
let s = text.substring(index, n.to);
if (s) {
newChildren.push({ from: index + offset, to: n.to + offset, text: s });
}
children = newChildren;
}
let result: MarkdownTree = {
type: n.name,
from: n.from + offset,
to: n.to + offset,
};
if (children.length > 0) {
result.children = children;
}
if (nodeText) {
result.text = nodeText;
}
return result;
}
export function parse(text: string): MarkdownTree {
let tree = treeToAST(text, wikiMarkdownLang.parser.parse(text).topNode);
// replaceNodesMatching(tree, (n): MarkdownTree | undefined | null => {
// if (n.type === "FencedCode") {
// let infoN = findNodeMatching(n, (n) => n.type === "CodeInfo");
// let language = infoN!.children![0].text;
// let textN = findNodeMatching(n, (n) => n.type === "CodeText");
// let text = textN!.children![0].text!;
//
// console.log(language, text);
// switch (language) {
// case "yaml":
// let parsed = StreamLanguage.define(yaml).parser.parse(text);
// let subTree = treeToAST(text, parsed.topNode, n.from);
// // console.log(JSON.stringify(subTree, null, 2));
// subTree.type = "yaml";
// return subTree;
// }
// }
// return;
// });
return tree;
export function findNodeMatching(
tree: ParseTree,
matchFn: (tree: ParseTree) => boolean
): ParseTree | null {
return collectNodesMatching(tree, matchFn)[0];
}
export function findNodeOfType(
tree: ParseTree,
nodeType: string
): ParseTree | null {
return collectNodesMatching(tree, (n) => n.type === nodeType)[0];
}
// Finds non-text node at position
export function nodeAtPos(tree: ParseTree, pos: number): ParseTree | null {
if (pos < tree.from! || pos > tree.to!) {
return null;
}
if (!tree.children) {
return tree;
}
for (let child of tree.children) {
let n = nodeAtPos(child, pos);
if (n && n.text !== undefined) {
// Got a text node, let's return its parent
return tree;
} else if (n) {
// Got it
return n;
}
}
return null;
}
// Turn ParseTree back into text
export function renderToText(tree: ParseTree): string {
let pieces: string[] = [];
if (tree.text !== undefined) {
return tree.text;
}
for (let child of tree.children!) {
pieces.push(renderToText(child));
}
return pieces.join("");
}

View File

@ -36,7 +36,8 @@
},
"test": {
"source": [
"plugs/lib/tree.test.ts",
"common/tree.test.ts",
"plugs/query/engine.test.ts",
"common/spaces/sync.test.ts"
],
"includeNodeModules": ["@codemirror/legacy-modes"],

View File

@ -1,6 +1,7 @@
import { syscall } from "./syscall";
import type { MarkdownTree } from "../common/tree";
export async function parseMarkdown(text: string): Promise<MarkdownTree> {
import type { ParseTree } from "../common/tree";
export async function parseMarkdown(text: string): Promise<ParseTree> {
return syscall("markdown.parseMarkdown", text);
}

View File

@ -1,3 +1,23 @@
syntax:
HashTag:
firstCharacters:
- "#"
regex: "#[A-Za-z\\.]+"
styles:
color: blue
AtMention:
firstCharacters:
- "@"
regex: "@[A-Za-z\\.]+"
styles:
color: blue
URL:
firstCharacters:
- "h"
regex: "https?:\\/\\/[-a-zA-Z0-9@:%._\\+~#=]{1,256}([-a-zA-Z0-9()@:%_\\+.~#?&=\\/]*)"
styles:
color: "#0330cb"
textDecoration: underline
functions:
clearPageIndex:
path: "./page.ts:clearPageIndex"
@ -55,13 +75,13 @@ functions:
path: "./dates.ts:indexDates"
events:
- page:index
updateMaterializedQueriesOnPage:
path: ./materialized_queries.ts:updateMaterializedQueriesOnPage
updateMaterializedQueriesCommand:
path: ./materialized_queries.ts:updateMaterializedQueriesCommand
parseServerCommand:
path: ./page.ts:parseServerPageCommand
command:
name: "Materialized Queries: Update"
parseCommand:
name: "Debug: Parse Document on Server"
parsePage:
path: ./page.ts:parsePage
parseCommand:
path: ./page.ts:parsePageCommand
command:
name: Parse Document
name: "Debug: Parse Document"

View File

@ -1,7 +1,7 @@
import { insertAtCursor } from "plugos-silverbullet-syscall/editor";
import { IndexEvent } from "../../webapp/app_event";
import { batchSet } from "plugos-silverbullet-syscall";
import { whiteOutQueries } from "./materialized_queries";
import { whiteOutQueries } from "../query/materialized_queries";
const dateMatchRegex = /(\d{4}\-\d{2}\-\d{2})/g;
@ -23,12 +23,16 @@ export async function indexDates({ name, text }: IndexEvent) {
await batchSet(name, dates);
}
export function niceDate(d: Date): string {
return new Date().toISOString().split("T")[0];
}
export async function insertToday() {
await insertAtCursor(new Date().toISOString().split("T")[0]);
await insertAtCursor(niceDate(new Date()));
}
export async function insertTomorrow() {
let d = new Date();
d.setDate(d.getDate() + 1);
await insertAtCursor(d.toISOString().split("T")[0]);
await insertAtCursor(niceDate(d));
}

View File

@ -1,9 +1,9 @@
import { IndexEvent } from "../../webapp/app_event";
import { whiteOutQueries } from "./materialized_queries";
import { whiteOutQueries } from "../query/materialized_queries";
import { batchSet } from "plugos-silverbullet-syscall/index";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import { collectNodesMatching, MarkdownTree, renderMarkdown } from "../lib/tree";
import { collectNodesMatching, ParseTree, renderToText } from "../../common/tree";
type Item = {
item: string;
@ -23,16 +23,16 @@ export async function indexItems({ name, text }: IndexEvent) {
if (!n.children) {
return;
}
let textNodes: MarkdownTree[] = [];
let textNodes: ParseTree[] = [];
let nested: string | undefined;
for (let child of n.children!.slice(1)) {
if (child.type === "OrderedList" || child.type === "BulletList") {
nested = renderMarkdown(child);
nested = renderToText(child);
break;
}
textNodes.push(child);
}
let item = textNodes.map(renderMarkdown).join("").trim();
let item = textNodes.map(renderToText).join("").trim();
let value: Item = {
item,
};

View File

@ -1,13 +1,11 @@
import { ClickEvent } from "../../webapp/app_event";
import { updateMaterializedQueriesCommand } from "./materialized_queries";
import { getCursor, getText, navigate as navigateTo, openUrl } from "plugos-silverbullet-syscall/editor";
import { taskToggleAtPos } from "../tasks/task";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import { MarkdownTree, nodeAtPos } from "../lib/tree";
import { nodeAtPos, ParseTree } from "../../common/tree";
const materializedQueryPrefix = /<!--\s*#query\s+/;
async function actionClickOrActionEnter(mdTree: MarkdownTree | null) {
async function actionClickOrActionEnter(mdTree: ParseTree | null) {
if (!mdTree) {
return;
}
@ -24,17 +22,9 @@ async function actionClickOrActionEnter(mdTree: MarkdownTree | null) {
case "URL":
await openUrl(mdTree.children![0].text!);
break;
case "CommentBlock":
if (mdTree.children![0].text!.match(materializedQueryPrefix)) {
await updateMaterializedQueriesCommand();
}
break;
case "Link":
await openUrl(mdTree.children![4].children![0].text!);
break;
case "TaskMarker":
await taskToggleAtPos(mdTree.from! + 1);
break;
}
}
@ -45,9 +35,11 @@ export async function linkNavigate() {
}
export async function clickNavigate(event: ClickEvent) {
// Navigate by default, don't navigate when Ctrl or Cmd is held
if (event.ctrlKey || event.metaKey) {
let mdTree = await parseMarkdown(await getText());
let newNode = nodeAtPos(mdTree, event.pos);
await actionClickOrActionEnter(newNode);
return;
}
let mdTree = await parseMarkdown(await getText());
let newNode = nodeAtPos(mdTree, event.pos);
await actionClickOrActionEnter(newNode);
}

View File

@ -21,10 +21,10 @@ import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import {
addParentPointers,
collectNodesMatching,
MarkdownTree,
renderMarkdown,
ParseTree,
renderToText,
replaceNodesMatching
} from "../lib/tree";
} from "../../common/tree";
export async function indexLinks({ name, text }: IndexEvent) {
let backLinks: { key: string; value: string }[] = [];
@ -93,7 +93,7 @@ export async function renamePage() {
}
let mdTree = await parseMarkdown(text);
addParentPointers(mdTree);
replaceNodesMatching(mdTree, (n): MarkdownTree | undefined | null => {
replaceNodesMatching(mdTree, (n): ParseTree | undefined | null => {
if (n.type === "WikiLinkPage") {
let pageName = n.children![0].text!;
if (pageName === oldName) {
@ -110,7 +110,7 @@ export async function renamePage() {
return;
});
// let newText = text.replaceAll(`[[${oldName}]]`, `[[${newName}]]`);
let newText = renderMarkdown(mdTree);
let newText = renderToText(mdTree);
if (text !== newText) {
console.log("Changes made, saving...");
await writePage(pageToUpdate, newText);
@ -179,6 +179,14 @@ export async function clearPageIndex(page: string) {
await clearPageIndexForPage(page);
}
export async function parsePage() {
console.log(await parseMarkdown(await getText()));
export async function parseServerPageCommand() {
console.log(await invokeFunction("server", "parsePage", await getText()));
}
export async function parsePageCommand() {
parsePage(await getText());
}
export async function parsePage(text: string) {
console.log("AST", JSON.stringify(await parseMarkdown(text), null, 2));
}

View File

@ -1,124 +0,0 @@
export type MarkdownTree = {
type?: string; // undefined === text node
from?: number;
to?: number;
text?: string;
children?: MarkdownTree[];
parent?: MarkdownTree;
};
export function addParentPointers(mdTree: MarkdownTree) {
if (!mdTree.children) {
return;
}
for (let child of mdTree.children) {
child.parent = mdTree;
addParentPointers(child);
}
}
export function removeParentPointers(mdTree: MarkdownTree) {
delete mdTree.parent;
if (!mdTree.children) {
return;
}
for (let child of mdTree.children) {
removeParentPointers(child);
}
}
export function findParentMatching(
mdTree: MarkdownTree,
matchFn: (mdTree: MarkdownTree) => boolean
): MarkdownTree | null {
let node = mdTree.parent;
while (node) {
if (matchFn(node)) {
return node;
}
node = node.parent;
}
return null;
}
export function collectNodesMatching(
mdTree: MarkdownTree,
matchFn: (mdTree: MarkdownTree) => boolean
): MarkdownTree[] {
if (matchFn(mdTree)) {
return [mdTree];
}
let results: MarkdownTree[] = [];
if (mdTree.children) {
for (let child of mdTree.children) {
results = [...results, ...collectNodesMatching(child, matchFn)];
}
}
return results;
}
// return value: returning undefined = not matched, continue, null = delete, new node = replace
export function replaceNodesMatching(
mdTree: MarkdownTree,
substituteFn: (mdTree: MarkdownTree) => MarkdownTree | null | undefined
) {
if (mdTree.children) {
for (let child of mdTree.children) {
let subst = substituteFn(child);
if (subst !== undefined) {
let pos = mdTree.children.indexOf(child);
if (subst) {
mdTree.children.splice(pos, 1, subst);
} else {
// null = delete
mdTree.children.splice(pos, 1);
}
} else {
replaceNodesMatching(child, substituteFn);
}
}
}
}
export function findNodeMatching(
mdTree: MarkdownTree,
matchFn: (mdTree: MarkdownTree) => boolean
): MarkdownTree | null {
return collectNodesMatching(mdTree, matchFn)[0];
}
// Finds non-text node at position
export function nodeAtPos(
mdTree: MarkdownTree,
pos: number
): MarkdownTree | null {
if (pos < mdTree.from! || pos > mdTree.to!) {
return null;
}
if (!mdTree.children) {
return mdTree;
}
for (let child of mdTree.children) {
let n = nodeAtPos(child, pos);
if (n && n.text !== undefined) {
// Got a text node, let's return its parent
return mdTree;
} else if (n) {
// Got it
return n;
}
}
return null;
}
// Turn MarkdownTree back into regular markdown text
export function renderMarkdown(mdTree: MarkdownTree): string {
let pieces: string[] = [];
if (mdTree.text !== undefined) {
return mdTree.text;
}
for (let child of mdTree.children!) {
pieces.push(renderMarkdown(child));
}
return pieces.join("");
}

View File

@ -2,7 +2,7 @@ import MarkdownIt from "markdown-it";
import { getText, hideRhs, showRhs } from "plugos-silverbullet-syscall/editor";
import * as clientStore from "plugos-silverbullet-syscall/clientStore";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import { renderMarkdown, replaceNodesMatching } from "../lib/tree";
import { renderToText, replaceNodesMatching } from "../../common/tree";
const css = `
<style>
@ -79,7 +79,7 @@ export async function cleanMarkdown(text: string) {
return null;
}
});
let html = md.render(renderMarkdown(mdTree));
let html = md.render(renderToText(mdTree));
return html;
}

View File

@ -9,8 +9,12 @@
"version": "1.0.0",
"dependencies": {
"@jest/globals": "^27.5.1",
"@lezer/generator": "^0.15.4",
"@lezer/lr": "^0.15.8",
"@types/yaml": "^1.9.7",
"plugos-silverbullet-syscall": "file:../plugos-silverbullet-syscall",
"plugos-syscall": "file:../plugos-syscall"
"plugos-syscall": "file:../plugos-syscall",
"yaml": "^2.0.0"
}
},
"../plugos-silverbullet-syscall": {
@ -173,6 +177,31 @@
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
}
},
"node_modules/@lezer/common": {
"version": "0.15.12",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz",
"integrity": "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig=="
},
"node_modules/@lezer/generator": {
"version": "0.15.4",
"resolved": "https://registry.npmjs.org/@lezer/generator/-/generator-0.15.4.tgz",
"integrity": "sha512-9bBwU2TzKMBQ6OCEDevuMNWGOBKlkq5YIGEhjrz9pb3MLb+oYYR4dVFZ7ehwLcDoSecsSA7PdlAy0thJO5pt2w==",
"dependencies": {
"@lezer/common": "^0.15.0",
"@lezer/lr": "^0.15.0"
},
"bin": {
"lezer-generator": "dist/lezer-generator.cjs"
}
},
"node_modules/@lezer/lr": {
"version": "0.15.8",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz",
"integrity": "sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==",
"dependencies": {
"@lezer/common": "^0.15.0"
}
},
"node_modules/@sinonjs/commons": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz",
@ -220,6 +249,15 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
},
"node_modules/@types/yaml": {
"version": "1.9.7",
"resolved": "https://registry.npmjs.org/@types/yaml/-/yaml-1.9.7.tgz",
"integrity": "sha512-8WMXRDD1D+wCohjfslHDgICd2JtMATZU8CkhH8LVJqcJs6dyYj5TGptzP8wApbmEullGBSsCEzzap73DQ1HJaA==",
"deprecated": "This is a stub types definition. yaml provides its own type definitions, so you do not need this installed.",
"dependencies": {
"yaml": "*"
}
},
"node_modules/@types/yargs": {
"version": "16.0.4",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
@ -560,6 +598,14 @@
"engines": {
"node": ">=4"
}
},
"node_modules/yaml": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0.tgz",
"integrity": "sha512-JbfdlHKGP2Ik9IHylzWlGd4pPK++EU46/IxMykphS2ZKw7a7h+dHNmcXObLgpRDriBY+rpWslldikckX8oruWQ==",
"engines": {
"node": ">= 14"
}
}
},
"dependencies": {
@ -683,6 +729,28 @@
"chalk": "^4.0.0"
}
},
"@lezer/common": {
"version": "0.15.12",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz",
"integrity": "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig=="
},
"@lezer/generator": {
"version": "0.15.4",
"resolved": "https://registry.npmjs.org/@lezer/generator/-/generator-0.15.4.tgz",
"integrity": "sha512-9bBwU2TzKMBQ6OCEDevuMNWGOBKlkq5YIGEhjrz9pb3MLb+oYYR4dVFZ7ehwLcDoSecsSA7PdlAy0thJO5pt2w==",
"requires": {
"@lezer/common": "^0.15.0",
"@lezer/lr": "^0.15.0"
}
},
"@lezer/lr": {
"version": "0.15.8",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz",
"integrity": "sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==",
"requires": {
"@lezer/common": "^0.15.0"
}
},
"@sinonjs/commons": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz",
@ -730,6 +798,14 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
},
"@types/yaml": {
"version": "1.9.7",
"resolved": "https://registry.npmjs.org/@types/yaml/-/yaml-1.9.7.tgz",
"integrity": "sha512-8WMXRDD1D+wCohjfslHDgICd2JtMATZU8CkhH8LVJqcJs6dyYj5TGptzP8wApbmEullGBSsCEzzap73DQ1HJaA==",
"requires": {
"yaml": "*"
}
},
"@types/yargs": {
"version": "16.0.4",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
@ -980,6 +1056,11 @@
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
},
"yaml": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0.tgz",
"integrity": "sha512-JbfdlHKGP2Ik9IHylzWlGd4pPK++EU46/IxMykphS2ZKw7a7h+dHNmcXObLgpRDriBY+rpWslldikckX8oruWQ=="
}
}
}

View File

@ -1,8 +1,13 @@
{
"name": "plugs",
"version": "1.0.0",
"scripts": {
"generate": "lezer-generator query/query.grammar -o query/parse-query.js"
},
"dependencies": {
"@jest/globals": "^27.5.1",
"@lezer/generator": "^0.15.4",
"@lezer/lr": "^0.15.8",
"@types/yaml": "^1.9.7",
"plugos-silverbullet-syscall": "file:../plugos-silverbullet-syscall",
"plugos-syscall": "file:../plugos-syscall",

View File

@ -0,0 +1,70 @@
import { expect, test } from "@jest/globals";
import { applyQuery, parseQuery } from "./engine";
test("Test parser", () => {
let parsedBasicQuery = parseQuery(`page`);
expect(parsedBasicQuery.table).toBe("page");
let parsedQuery1 = parseQuery(
`task where completed = false and dueDate <= "{{today}}" order by dueDate desc limit 5`
);
expect(parsedQuery1.table).toBe("task");
expect(parsedQuery1.orderBy).toBe("dueDate");
expect(parsedQuery1.orderDesc).toBe(true);
expect(parsedQuery1.limit).toBe(5);
expect(parsedQuery1.filter.length).toBe(2);
expect(parsedQuery1.filter[0]).toStrictEqual({
op: "=",
prop: "completed",
value: false,
});
expect(parsedQuery1.filter[1]).toStrictEqual({
op: "<=",
prop: "dueDate",
value: "{{today}}",
});
let parsedQuery2 = parseQuery(`page where name like "interview/%"`);
expect(parsedQuery2.table).toBe("page");
expect(parsedQuery2.filter.length).toBe(1);
expect(parsedQuery2.filter[0]).toStrictEqual({
op: "like",
prop: "name",
value: "interview/%",
});
});
test("Test performing the queries", () => {
let data: any[] = [
{ name: "interview/My Interview", lastModified: 1 },
{ name: "interview/My Interview 2", lastModified: 2 },
{ name: "Pete", age: 38 },
{ name: "Angie", age: 28 },
];
expect(applyQuery(`page where name like "interview/%"`, data)).toStrictEqual([
{ name: "interview/My Interview", lastModified: 1 },
{ name: "interview/My Interview 2", lastModified: 2 },
]);
expect(
applyQuery(`page where name like "interview/%" order by lastModified`, data)
).toStrictEqual([
{ name: "interview/My Interview", lastModified: 1 },
{ name: "interview/My Interview 2", lastModified: 2 },
]);
expect(
applyQuery(
`page where name like "interview/%" order by lastModified desc`,
data
)
).toStrictEqual([
{ name: "interview/My Interview 2", lastModified: 2 },
{ name: "interview/My Interview", lastModified: 1 },
]);
expect(applyQuery(`page where age > 30`, data)).toStrictEqual([
{ name: "Pete", age: 38 },
]);
expect(applyQuery(`page where age > 28 and age < 38`, data)).toStrictEqual(
[]
);
});

154
plugs/query/engine.ts Normal file
View File

@ -0,0 +1,154 @@
import { collectNodesOfType, findNodeOfType, replaceNodesMatching } from "../../common/tree";
import { lezerToParseTree } from "../../common/parse_tree";
// @ts-ignore
import { parser } from "./parse-query";
type Filter = {
op: string;
prop: string;
value: any;
};
type ParsedQuery = {
table: string;
orderBy?: string;
orderDesc?: boolean;
limit?: number;
filter: Filter[];
};
export function parseQuery(query: string): ParsedQuery {
let n = lezerToParseTree(query, parser.parse(query).topNode);
// Clean the tree a bit
replaceNodesMatching(n, (n) => {
if (!n.type) {
let trimmed = n.text!.trim();
if (!trimmed) {
return null;
}
n.text = trimmed;
}
});
let queryNode = n.children![0];
let parsedQuery: ParsedQuery = {
table: queryNode.children![0].children![0].text!,
filter: [],
};
let orderByNode = findNodeOfType(queryNode, "OrderClause");
if (orderByNode) {
let nameNode = findNodeOfType(orderByNode, "Name");
parsedQuery.orderBy = nameNode!.children![0].text!;
let orderNode = findNodeOfType(orderByNode, "Order");
parsedQuery.orderDesc = orderNode
? orderNode.children![0].text! === "desc"
: false;
}
let limitNode = findNodeOfType(queryNode, "LimitClause");
if (limitNode) {
let nameNode = findNodeOfType(limitNode, "Number");
parsedQuery.limit = +nameNode!.children![0].text!;
}
let filterNodes = collectNodesOfType(queryNode, "FilterExpr");
for (let filterNode of filterNodes) {
let val: any = undefined;
let valNode = filterNode.children![2].children![0];
switch (valNode.type) {
case "Number":
val = valNode.children![0].text!;
break;
case "Bool":
val = valNode.children![0].text! === "true";
break;
case "Name":
val = valNode.children![0].text!;
break;
case "String":
val = valNode.children![0].text!;
val = val.substring(1, val.length - 1);
break;
}
let f: Filter = {
prop: filterNode.children![0].children![0].text!,
op: filterNode.children![1].text!,
value: val,
};
parsedQuery.filter.push(f);
}
// console.log(JSON.stringify(queryNode, null, 2));
return parsedQuery;
}
export function applyQuery(query: string, records: any[]): any {
const parsedQuery = parseQuery(query);
let resultRecords: any[] = [];
if (parsedQuery.filter.length === 0) {
resultRecords = records.slice();
} else {
recordLoop: for (let record of records) {
for (let { op, prop, value } of parsedQuery.filter) {
switch (op) {
case "=":
if (!(record[prop] === value)) {
continue recordLoop;
}
break;
case "!=":
if (!(record[prop] !== value)) {
continue recordLoop;
}
break;
case "<":
if (!(record[prop] < value)) {
continue recordLoop;
}
break;
case "<=":
if (!(record[prop] <= value)) {
continue recordLoop;
}
break;
case ">":
if (!(record[prop] > value)) {
continue recordLoop;
}
break;
case ">=":
if (!(record[prop] >= value)) {
continue recordLoop;
}
break;
case "like":
let re = new RegExp(value.replaceAll("%", ".*"));
if (!re.exec(record[prop])) {
continue recordLoop;
}
break;
}
}
resultRecords.push(record);
}
}
// Now the sorting
if (parsedQuery.orderBy) {
resultRecords = resultRecords.sort((a: any, b: any) => {
const orderBy = parsedQuery.orderBy!;
const orderDesc = parsedQuery.orderDesc!;
if (a[orderBy] === b[orderBy]) {
return 0;
}
if (a[orderBy] < b[orderBy]) {
return orderDesc ? 1 : -1;
} else {
return orderDesc ? -1 : 1;
}
});
}
if (parsedQuery.limit) {
resultRecords = resultRecords.slice(0, parsedQuery.limit);
}
return resultRecords;
}

View File

@ -3,12 +3,16 @@ import { flashNotification, getCurrentPage, reloadPage, save } from "plugos-silv
import { listPages, readPage, writePage } from "plugos-silverbullet-syscall/space";
import { invokeFunction } from "plugos-silverbullet-syscall/system";
import { scanPrefixGlobal } from "plugos-silverbullet-syscall";
import { niceDate } from "../core/dates";
export const queryRegex =
/(<!--\s*#query\s+(?<table>\w+)\s*(filter\s+["'](?<filter>[^"']+)["'])?\s*\s*(order by\s+(?<orderBy>\w+)(?<orderDesc>\s+desc)?)?(group by\s+(?<groupBy>\w+))?\s*(limit\s+(?<limit>\d+))?\s*-->)(.+?)(<!--\s*#end\s*-->)/gs;
export const newQueryRegex =
/<!--\s*#query\s+(.+?)(?=\s*-->)-->(.+?)<!--\s*#end\s*-->/gs;
export function whiteOutQueries(text: string): string {
return text.replaceAll(queryRegex, (match) =>
return text.replaceAll(newQueryRegex, (match) =>
new Array(match.length + 1).join(" ")
);
}
@ -40,6 +44,17 @@ export async function updateMaterializedQueriesCommand() {
await flashNotification("Updated materialized queries");
}
function replaceTemplateVars(s: string): string {
return s.replaceAll(/\{\{(\w+)\}\}/g, (match, v) => {
switch (v) {
case "today":
return niceDate(new Date());
break;
}
return match;
});
}
// Called from client, running on server
export async function updateMaterializedQueriesOnPage(pageName: string) {
let { text } = await readPage(pageName);
@ -50,6 +65,7 @@ export async function updateMaterializedQueriesOnPage(pageName: string) {
const startQuery = args[0];
const endQuery = args[args.length - 4];
let results = [];
filter = filter && replaceTemplateVars(filter);
switch (table) {
case "page":
let pages = await listPages();
@ -94,21 +110,17 @@ export async function updateMaterializedQueriesOnPage(pageName: string) {
return `${startQuery}\n${results.sort().join("\n")}\n${endQuery}`;
case "link":
let uniqueLinks = new Set<string>();
console.log("Here!!");
for (let { key, page, value: name } of await scanPrefixGlobal(
`pl:${pageName}:`
)) {
console.log("Here!!");
let [, pos] = key.split(":");
if (!filter || (filter && name.includes(filter))) {
uniqueLinks.add(name);
}
}
console.log("Here!!");
for (const uniqueResult of uniqueLinks) {
results.push(`* [[${uniqueResult}]]`);
}
console.log("Here!!");
return `${startQuery}\n${results.sort().join("\n")}\n${endQuery}`;
case "item":
for (let {

44
plugs/query/query.grammar Normal file
View File

@ -0,0 +1,44 @@
@precedence { logic @left }
@top Program { Query }
Query {
Name (WhereClause | OrderClause | LimitClause)*
}
WhereClause { "where" LogicalExpr }
OrderClause { "order" "by" Name Order? }
LimitClause { "limit" Number }
Order {
"desc" | "asc"
}
Value { Number | String | Bool }
LogicalExpr { AndExpr | FilterExpr }
AndExpr { FilterExpr !logic "and" FilterExpr }
FilterExpr {
Name "<" Value
| Name "<=" Value
| Name "=" Value
| Name "!=" Value
| Name ">=" Value
| Name ">" Value
| Name "like" Value
}
@skip { space }
Bool {
"true" | "false"
}
@tokens {
space { std.whitespace+ }
Name { std.asciiLetter+ }
String { "\"" ![\"]* "\"" }
Number { std.digit+ }
}

View File

@ -0,0 +1,7 @@
functions:
updateMaterializedQueriesOnPage:
path: ./materialized_queries.ts:updateMaterializedQueriesOnPage
updateMaterializedQueriesCommand:
path: ./materialized_queries.ts:updateMaterializedQueriesCommand
command:
name: "Materialized Queries: Update"

View File

@ -1,16 +1,23 @@
import type { ClickEvent } from "../../webapp/app_event";
import { IndexEvent } from "../../webapp/app_event";
import { whiteOutQueries } from "../core/materialized_queries";
import { whiteOutQueries } from "../query/materialized_queries";
import { batchSet } from "plugos-silverbullet-syscall/index";
import { readPage, writePage } from "plugos-silverbullet-syscall/space";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import { dispatch, getText } from "plugos-silverbullet-syscall/editor";
import { addParentPointers, collectNodesMatching, nodeAtPos, renderMarkdown } from "../lib/tree";
import {
addParentPointers,
collectNodesMatching,
collectNodesOfType,
nodeAtPos,
renderToText
} from "../../common/tree";
type Task = {
task: string;
complete: boolean;
deadline?: string;
pos?: number;
nested?: string;
};
@ -21,23 +28,29 @@ export async function indexTasks({ name, text }: IndexEvent) {
text = whiteOutQueries(text);
let mdTree = await parseMarkdown(text);
addParentPointers(mdTree);
collectNodesMatching(mdTree, (n) => n.type === "Task").forEach((n) => {
let task = n.children!.slice(1).map(renderMarkdown).join("").trim();
collectNodesOfType(mdTree, "Task").forEach((n) => {
let task = n.children!.slice(1).map(renderToText).join("").trim();
let complete = n.children![0].children![0].text! !== "[ ]";
let value: Task = {
task,
complete,
};
let deadlineNodes = collectNodesOfType(n, "DeadlineDate");
if (deadlineNodes.length > 0) {
value.deadline = deadlineNodes[0].children![0].text!.replace(/📅\s*/, "");
}
let taskIndex = n.parent!.children!.indexOf(n);
let nestedItems = n.parent!.children!.slice(taskIndex + 1);
if (nestedItems.length > 0) {
value.nested = nestedItems.map(renderMarkdown).join("").trim();
value.nested = nestedItems.map(renderToText).join("").trim();
}
tasks.push({
key: `task:${n.from}`,
value,
});
// console.log("Task", value);
});
console.log("Found", tasks.length, "task(s)");
@ -95,9 +108,9 @@ export async function taskToggleAtPos(pos: number) {
taskMarkerNode.children![0].text = changeTo;
console.log(
"This will be the new marker",
renderMarkdown(taskMarkerNode)
renderToText(taskMarkerNode)
);
text = renderMarkdown(referenceMdTree);
text = renderToText(referenceMdTree);
console.log("Updated reference paged text", text);
await writePage(page, text);
}

View File

@ -1,3 +1,22 @@
syntax:
DeadlineDate:
firstCharacters:
- "📅"
regex: "📅\\s*\\d{4}\\-\\d{2}\\-\\d{2}"
styles:
backgroundColor: "rgba(22,22,22,0.07)"
CompletedDate:
firstCharacters:
- "✅"
regex: "✅\\s*\\d{4}\\-\\d{2}\\-\\d{2}"
styles:
backgroundColor: "rgba(22,22,22,0.07)"
RepeatInterval:
firstCharacters:
- "🔁"
regex: "🔁\\s*every\\s+\\w+"
styles:
backgroundColor: "rgba(22,22,22,0.07)"
functions:
indexTasks:
path: "./task.ts:indexTasks"

View File

@ -3,396 +3,418 @@
"@babel/code-frame@^7.12.13":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz"
integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
"integrity" "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg=="
"resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz"
"version" "7.16.7"
dependencies:
"@babel/highlight" "^7.16.7"
"@babel/helper-validator-identifier@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz"
integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
"integrity" "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
"resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz"
"version" "7.16.7"
"@babel/highlight@^7.16.7":
version "7.16.10"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz"
integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==
"integrity" "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw=="
"resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz"
"version" "7.16.10"
dependencies:
"@babel/helper-validator-identifier" "^7.16.7"
chalk "^2.0.0"
js-tokens "^4.0.0"
"chalk" "^2.0.0"
"js-tokens" "^4.0.0"
"@jest/environment@^27.5.1":
version "27.5.1"
resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz"
integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==
"integrity" "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA=="
"resolved" "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz"
"version" "27.5.1"
dependencies:
"@jest/fake-timers" "^27.5.1"
"@jest/types" "^27.5.1"
"@types/node" "*"
jest-mock "^27.5.1"
"jest-mock" "^27.5.1"
"@jest/fake-timers@^27.5.1":
version "27.5.1"
resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz"
integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==
"integrity" "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ=="
"resolved" "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz"
"version" "27.5.1"
dependencies:
"@jest/types" "^27.5.1"
"@sinonjs/fake-timers" "^8.0.1"
"@types/node" "*"
jest-message-util "^27.5.1"
jest-mock "^27.5.1"
jest-util "^27.5.1"
"jest-message-util" "^27.5.1"
"jest-mock" "^27.5.1"
"jest-util" "^27.5.1"
"@jest/globals@^27.5.1":
version "27.5.1"
resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz"
integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==
"integrity" "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q=="
"resolved" "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz"
"version" "27.5.1"
dependencies:
"@jest/environment" "^27.5.1"
"@jest/types" "^27.5.1"
expect "^27.5.1"
"expect" "^27.5.1"
"@jest/types@^27.5.1":
version "27.5.1"
resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz"
integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==
"integrity" "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw=="
"resolved" "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz"
"version" "27.5.1"
dependencies:
"@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^3.0.0"
"@types/node" "*"
"@types/yargs" "^16.0.0"
chalk "^4.0.0"
"chalk" "^4.0.0"
"@lezer/common@^0.15.0":
"integrity" "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig=="
"resolved" "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz"
"version" "0.15.12"
"@lezer/generator@^0.15.4":
"integrity" "sha512-9bBwU2TzKMBQ6OCEDevuMNWGOBKlkq5YIGEhjrz9pb3MLb+oYYR4dVFZ7ehwLcDoSecsSA7PdlAy0thJO5pt2w=="
"resolved" "https://registry.npmjs.org/@lezer/generator/-/generator-0.15.4.tgz"
"version" "0.15.4"
dependencies:
"@lezer/common" "^0.15.0"
"@lezer/lr" "^0.15.0"
"@lezer/lr@^0.15.0", "@lezer/lr@^0.15.8":
"integrity" "sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg=="
"resolved" "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz"
"version" "0.15.8"
dependencies:
"@lezer/common" "^0.15.0"
"@sinonjs/commons@^1.7.0":
version "1.8.3"
resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz"
integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==
"integrity" "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ=="
"resolved" "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz"
"version" "1.8.3"
dependencies:
type-detect "4.0.8"
"type-detect" "4.0.8"
"@sinonjs/fake-timers@^8.0.1":
version "8.1.0"
resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz"
integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==
"integrity" "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg=="
"resolved" "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz"
"version" "8.1.0"
dependencies:
"@sinonjs/commons" "^1.7.0"
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.4"
resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz"
integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==
"integrity" "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g=="
"resolved" "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz"
"version" "2.0.4"
"@types/istanbul-lib-report@*":
version "3.0.0"
resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz"
integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==
"integrity" "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg=="
"resolved" "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz"
"version" "3.0.0"
dependencies:
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-reports@^3.0.0":
version "3.0.1"
resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz"
integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==
"integrity" "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw=="
"resolved" "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz"
"version" "3.0.1"
dependencies:
"@types/istanbul-lib-report" "*"
"@types/node@*":
version "17.0.23"
resolved "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz"
integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==
"integrity" "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw=="
"resolved" "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz"
"version" "17.0.23"
"@types/stack-utils@^2.0.0":
version "2.0.1"
resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
"integrity" "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
"resolved" "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz"
"version" "2.0.1"
"@types/yaml@^1.9.7":
version "1.9.7"
resolved "https://registry.yarnpkg.com/@types/yaml/-/yaml-1.9.7.tgz#2331f36e0aac91311a63d33eb026c21687729679"
integrity sha512-8WMXRDD1D+wCohjfslHDgICd2JtMATZU8CkhH8LVJqcJs6dyYj5TGptzP8wApbmEullGBSsCEzzap73DQ1HJaA==
"integrity" "sha512-8WMXRDD1D+wCohjfslHDgICd2JtMATZU8CkhH8LVJqcJs6dyYj5TGptzP8wApbmEullGBSsCEzzap73DQ1HJaA=="
"resolved" "https://registry.npmjs.org/@types/yaml/-/yaml-1.9.7.tgz"
"version" "1.9.7"
dependencies:
yaml "*"
"yaml" "*"
"@types/yargs-parser@*":
version "21.0.0"
resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz"
integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==
"integrity" "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA=="
"resolved" "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz"
"version" "21.0.0"
"@types/yargs@^16.0.0":
version "16.0.4"
resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz"
integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==
"integrity" "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw=="
"resolved" "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz"
"version" "16.0.4"
dependencies:
"@types/yargs-parser" "*"
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
"ansi-regex@^5.0.1":
"integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
"resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz"
"version" "5.0.1"
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
"ansi-styles@^3.2.1":
"integrity" "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="
"resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz"
"version" "3.2.1"
dependencies:
color-convert "^1.9.0"
"color-convert" "^1.9.0"
ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
"ansi-styles@^4.1.0":
"integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="
"resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
"version" "4.3.0"
dependencies:
color-convert "^2.0.1"
"color-convert" "^2.0.1"
ansi-styles@^5.0.0:
version "5.2.0"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
"ansi-styles@^5.0.0":
"integrity" "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="
"resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz"
"version" "5.2.0"
braces@^3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
"braces@^3.0.2":
"integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A=="
"resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz"
"version" "3.0.2"
dependencies:
fill-range "^7.0.1"
"fill-range" "^7.0.1"
chalk@^2.0.0:
version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
"chalk@^2.0.0":
"integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="
"resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
"version" "2.4.2"
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
"ansi-styles" "^3.2.1"
"escape-string-regexp" "^1.0.5"
"supports-color" "^5.3.0"
chalk@^4.0.0:
version "4.1.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
"chalk@^4.0.0":
"integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="
"resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
"version" "4.1.2"
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
"ansi-styles" "^4.1.0"
"supports-color" "^7.1.0"
ci-info@^3.2.0:
version "3.3.0"
resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz"
integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==
"ci-info@^3.2.0":
"integrity" "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw=="
"resolved" "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz"
"version" "3.3.0"
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
"color-convert@^1.9.0":
"integrity" "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="
"resolved" "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
"version" "1.9.3"
dependencies:
color-name "1.1.3"
"color-name" "1.1.3"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
"color-convert@^2.0.1":
"integrity" "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="
"resolved" "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz"
"version" "2.0.1"
dependencies:
color-name "~1.1.4"
"color-name" "~1.1.4"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
"color-name@~1.1.4":
"integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
"resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
"version" "1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
"color-name@1.1.3":
"integrity" "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
"resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
"version" "1.1.3"
diff-sequences@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz"
integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
"diff-sequences@^27.5.1":
"integrity" "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ=="
"resolved" "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz"
"version" "27.5.1"
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
"escape-string-regexp@^1.0.5":
"integrity" "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
"resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
"version" "1.0.5"
escape-string-regexp@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
"escape-string-regexp@^2.0.0":
"integrity" "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="
"resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz"
"version" "2.0.0"
expect@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz"
integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==
"expect@^27.5.1":
"integrity" "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw=="
"resolved" "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz"
"version" "27.5.1"
dependencies:
"@jest/types" "^27.5.1"
jest-get-type "^27.5.1"
jest-matcher-utils "^27.5.1"
jest-message-util "^27.5.1"
"jest-get-type" "^27.5.1"
"jest-matcher-utils" "^27.5.1"
"jest-message-util" "^27.5.1"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
"fill-range@^7.0.1":
"integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ=="
"resolved" "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
"version" "7.0.1"
dependencies:
to-regex-range "^5.0.1"
"to-regex-range" "^5.0.1"
graceful-fs@^4.2.9:
version "4.2.9"
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz"
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
"graceful-fs@^4.2.9":
"integrity" "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ=="
"resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz"
"version" "4.2.9"
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
"has-flag@^3.0.0":
"integrity" "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
"resolved" "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
"version" "3.0.0"
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
"has-flag@^4.0.0":
"integrity" "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
"resolved" "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
"version" "4.0.0"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
"is-number@^7.0.0":
"integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
"resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
"version" "7.0.0"
jest-diff@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz"
integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==
"jest-diff@^27.5.1":
"integrity" "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw=="
"resolved" "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz"
"version" "27.5.1"
dependencies:
chalk "^4.0.0"
diff-sequences "^27.5.1"
jest-get-type "^27.5.1"
pretty-format "^27.5.1"
"chalk" "^4.0.0"
"diff-sequences" "^27.5.1"
"jest-get-type" "^27.5.1"
"pretty-format" "^27.5.1"
jest-get-type@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz"
integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==
"jest-get-type@^27.5.1":
"integrity" "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw=="
"resolved" "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz"
"version" "27.5.1"
jest-matcher-utils@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz"
integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
"jest-matcher-utils@^27.5.1":
"integrity" "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw=="
"resolved" "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz"
"version" "27.5.1"
dependencies:
chalk "^4.0.0"
jest-diff "^27.5.1"
jest-get-type "^27.5.1"
pretty-format "^27.5.1"
"chalk" "^4.0.0"
"jest-diff" "^27.5.1"
"jest-get-type" "^27.5.1"
"pretty-format" "^27.5.1"
jest-message-util@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz"
integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==
"jest-message-util@^27.5.1":
"integrity" "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g=="
"resolved" "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz"
"version" "27.5.1"
dependencies:
"@babel/code-frame" "^7.12.13"
"@jest/types" "^27.5.1"
"@types/stack-utils" "^2.0.0"
chalk "^4.0.0"
graceful-fs "^4.2.9"
micromatch "^4.0.4"
pretty-format "^27.5.1"
slash "^3.0.0"
stack-utils "^2.0.3"
"chalk" "^4.0.0"
"graceful-fs" "^4.2.9"
"micromatch" "^4.0.4"
"pretty-format" "^27.5.1"
"slash" "^3.0.0"
"stack-utils" "^2.0.3"
jest-mock@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz"
integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==
"jest-mock@^27.5.1":
"integrity" "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og=="
"resolved" "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz"
"version" "27.5.1"
dependencies:
"@jest/types" "^27.5.1"
"@types/node" "*"
jest-util@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz"
integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==
"jest-util@^27.5.1":
"integrity" "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw=="
"resolved" "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz"
"version" "27.5.1"
dependencies:
"@jest/types" "^27.5.1"
"@types/node" "*"
chalk "^4.0.0"
ci-info "^3.2.0"
graceful-fs "^4.2.9"
picomatch "^2.2.3"
"chalk" "^4.0.0"
"ci-info" "^3.2.0"
"graceful-fs" "^4.2.9"
"picomatch" "^2.2.3"
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
"js-tokens@^4.0.0":
"integrity" "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
"resolved" "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
"version" "4.0.0"
micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
"micromatch@^4.0.4":
"integrity" "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA=="
"resolved" "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz"
"version" "4.0.5"
dependencies:
braces "^3.0.2"
picomatch "^2.3.1"
"braces" "^3.0.2"
"picomatch" "^2.3.1"
picomatch@^2.2.3, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
"picomatch@^2.2.3", "picomatch@^2.3.1":
"integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
"resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
"version" "2.3.1"
"plugos-silverbullet-syscall@file:../plugos-silverbullet-syscall":
version "1.0.0"
"resolved" "file:../plugos-silverbullet-syscall"
"version" "1.0.0"
"plugos-syscall@file:../plugos-syscall":
version "1.0.0"
"resolved" "file:../plugos-syscall"
"version" "1.0.0"
pretty-format@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz"
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
"pretty-format@^27.5.1":
"integrity" "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="
"resolved" "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz"
"version" "27.5.1"
dependencies:
ansi-regex "^5.0.1"
ansi-styles "^5.0.0"
react-is "^17.0.1"
"ansi-regex" "^5.0.1"
"ansi-styles" "^5.0.0"
"react-is" "^17.0.1"
react-is@^17.0.1:
version "17.0.2"
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
"react-is@^17.0.1":
"integrity" "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
"resolved" "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
"version" "17.0.2"
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
"slash@^3.0.0":
"integrity" "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
"resolved" "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
"version" "3.0.0"
stack-utils@^2.0.3:
version "2.0.5"
resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz"
integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==
"stack-utils@^2.0.3":
"integrity" "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA=="
"resolved" "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz"
"version" "2.0.5"
dependencies:
escape-string-regexp "^2.0.0"
"escape-string-regexp" "^2.0.0"
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
"supports-color@^5.3.0":
"integrity" "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="
"resolved" "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
"version" "5.5.0"
dependencies:
has-flag "^3.0.0"
"has-flag" "^3.0.0"
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
"supports-color@^7.1.0":
"integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="
"resolved" "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz"
"version" "7.2.0"
dependencies:
has-flag "^4.0.0"
"has-flag" "^4.0.0"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
"to-regex-range@^5.0.1":
"integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="
"resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
"version" "5.0.1"
dependencies:
is-number "^7.0.0"
"is-number" "^7.0.0"
type-detect@4.0.8:
version "4.0.8"
resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
"type-detect@4.0.8":
"integrity" "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
"resolved" "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz"
"version" "4.0.8"
yaml@*, yaml@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.0.tgz#cbc588ad58e0cd924cd3f5f2b1a9485103048e25"
integrity sha512-JbfdlHKGP2Ik9IHylzWlGd4pPK++EU46/IxMykphS2ZKw7a7h+dHNmcXObLgpRDriBY+rpWslldikckX8oruWQ==
"yaml@*", "yaml@^2.0.0":
"integrity" "sha512-JbfdlHKGP2Ik9IHylzWlGd4pPK++EU46/IxMykphS2ZKw7a7h+dHNmcXObLgpRDriBY+rpWslldikckX8oruWQ=="
"resolved" "https://registry.npmjs.org/yaml/-/yaml-2.0.0.tgz"
"version" "2.0.0"

View File

@ -17,10 +17,12 @@ import { NodeCronHook } from "../plugos/hooks/node_cron";
import { markdownSyscalls } from "../common/syscalls/markdown";
import { EventedSpacePrimitives } from "../common/spaces/evented_space_primitives";
import { Space } from "../common/spaces/space";
import { safeRun } from "../webapp/util";
import { safeRun, throttle } from "../webapp/util";
import { createSandbox } from "../plugos/environments/node_sandbox";
import { jwtSyscalls } from "../plugos/syscalls/jwt";
import { fetchSyscalls } from "../plugos/syscalls/fetch.node";
import buildMarkdown from "../webapp/parser";
import { loadMarkdownExtensions } from "../webapp/markdown_ext";
export class ExpressServer {
app: Express;
@ -66,23 +68,29 @@ export class ExpressServer {
system.registerSyscalls([], pageIndexSyscalls(this.db));
system.registerSyscalls([], spaceSyscalls(this.space));
system.registerSyscalls([], eventSyscalls(this.eventHook));
system.registerSyscalls([], markdownSyscalls());
system.registerSyscalls([], markdownSyscalls(buildMarkdown([])));
system.registerSyscalls([], fetchSyscalls());
system.registerSyscalls([], jwtSyscalls());
system.addHook(new EndpointHook(app, "/_/"));
let throttledRebuildMdExtensions = throttle(() => {
this.rebuildMdExtensions();
}, 100);
this.space.on({
plugLoaded: (plugName, plug) => {
safeRun(async () => {
console.log("Plug load", plugName);
await system.load(plugName, plug, createSandbox);
});
throttledRebuildMdExtensions();
},
plugUnloaded: (plugName) => {
safeRun(async () => {
console.log("Plug unload", plugName);
await system.unload(plugName);
});
throttledRebuildMdExtensions();
},
});
@ -92,6 +100,13 @@ export class ExpressServer {
this.space.updatePageListAsync();
}
rebuildMdExtensions() {
this.system.registerSyscalls(
[],
markdownSyscalls(buildMarkdown(loadMarkdownExtensions(this.system)))
);
}
async init() {
console.log("Setting up router");

View File

@ -27,6 +27,7 @@ import { lineWrapper } from "./line_wrapper";
import { markdown } from "./markdown";
import { PathPageNavigator } from "./navigator";
import customMarkDown from "./parser";
import buildMarkdown from "./parser";
import reducer from "./reducer";
import { smartQuoteKeymap } from "./smart_quotes";
import { Space } from "../common/spaces/space";
@ -48,6 +49,7 @@ import { pasteLinkExtension } from "./editor_paste";
import { markdownSyscalls } from "../common/syscalls/markdown";
import { clientStoreSyscalls } from "./syscalls/clientStore";
import { StatusBar } from "./components/status_bar";
import { loadMarkdownExtensions, MDExt } from "./markdown_ext";
class PageState {
scrollTop: number;
@ -77,6 +79,7 @@ export class Editor implements AppEventDispatcher {
this.eventHook.dispatchEvent("editor:updated");
}, 1000);
private system = new System<SilverBulletHooks>("client");
private mdExtensions: MDExt[] = [];
constructor(space: Space, parent: Element) {
this.space = space;
@ -118,7 +121,10 @@ export class Editor implements AppEventDispatcher {
this.system.registerSyscalls([], spaceSyscalls(this));
this.system.registerSyscalls([], indexerSyscalls(this.space));
this.system.registerSyscalls([], systemSyscalls(this.space));
this.system.registerSyscalls([], markdownSyscalls());
this.system.registerSyscalls(
[],
markdownSyscalls(buildMarkdown(this.mdExtensions))
);
this.system.registerSyscalls([], clientStoreSyscalls());
}
@ -270,7 +276,7 @@ export class Editor implements AppEventDispatcher {
history(),
drawSelection(),
dropCursor(),
customMarkdownStyle,
customMarkdownStyle(this.mdExtensions),
bracketMatching(),
closeBrackets(),
autocompletion({
@ -387,7 +393,7 @@ export class Editor implements AppEventDispatcher {
),
pasteLinkExtension,
markdown({
base: customMarkDown,
base: customMarkDown(this.mdExtensions),
}),
],
});
@ -396,6 +402,14 @@ export class Editor implements AppEventDispatcher {
rebuildEditorState() {
const editorView = this.editorView;
if (editorView && this.currentPage) {
this.mdExtensions = loadMarkdownExtensions(this.system);
// And reload the syscalls to use the new syntax extensions
this.system.registerSyscalls(
[],
markdownSyscalls(buildMarkdown(this.mdExtensions))
);
editorView.setState(
this.createEditorState(this.currentPage, editorView.state.sliceDoc())
);

View File

@ -1,5 +1,7 @@
import { ViewPlugin, ViewUpdate } from "@codemirror/view";
import { urlRegexp } from "./parser";
const urlRegexp =
/^https?:\/\/[-a-zA-Z0-9@:%._\+~#=]{1,256}([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
export const pasteLinkExtension = ViewPlugin.fromClass(
class {

66
webapp/markdown_ext.ts Normal file
View File

@ -0,0 +1,66 @@
import { Tag } from "@codemirror/highlight";
import type { MarkdownConfig } from "@lezer/markdown";
import { System } from "../plugos/system";
import { Manifest } from "../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<any>): 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;
}

View File

@ -1,11 +1,12 @@
import { styleTags, tags as t } from "@codemirror/highlight";
import { styleTags } from "@codemirror/highlight";
import { BlockContext, LeafBlock, LeafBlockParser, MarkdownConfig, parseCode, TaskList } from "@lezer/markdown";
import { commonmark, getCodeParser, mkLang } from "./markdown/markdown";
import * as ct from "./customtags";
import { LanguageDescription, LanguageSupport } from "@codemirror/language";
import { Language, LanguageDescription, LanguageSupport } from "@codemirror/language";
import { StreamLanguage } from "@codemirror/stream-parser";
import { yaml } from "@codemirror/legacy-modes/mode/yaml";
import { javascriptLanguage } from "@codemirror/lang-javascript";
import { MDExt, mdExtensionStyleTags, mdExtensionSyntaxConfig } from "./markdown_ext";
export const pageLinkRegex = /^\[\[([^\]]+)\]\]/;
@ -37,51 +38,6 @@ const WikiLink: MarkdownConfig = {
],
};
const AtMention: MarkdownConfig = {
defineNodes: ["AtMention"],
parseInline: [
{
name: "AtMention",
parse(cx, next, pos) {
let match: RegExpMatchArray | null;
if (
next != 64 /* '@' */ ||
!(match = /^[A-Za-z\.]+/.exec(cx.slice(pos + 1, cx.end)))
) {
return -1;
}
return cx.addElement(
cx.elt("AtMention", pos, pos + 1 + match[0].length)
);
},
after: "Emphasis",
},
],
};
export const urlRegexp =
/^https?:\/\/[-a-zA-Z0-9@:%._\+~#=]{1,256}([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
const UnmarkedUrl: MarkdownConfig = {
defineNodes: ["URL"],
parseInline: [
{
name: "URL",
parse(cx, next, pos) {
let match: RegExpMatchArray | null;
if (
next != 104 /* 'h' */ ||
!(match = urlRegexp.exec(cx.slice(pos, cx.end)))
) {
return -1;
}
return cx.addElement(cx.elt("URL", pos, pos + match[0].length));
},
after: "Emphasis",
},
],
};
class CommentParser implements LeafBlockParser {
nextLine() {
return false;
@ -111,60 +67,40 @@ export const Comment: MarkdownConfig = {
],
};
const TagLink: MarkdownConfig = {
defineNodes: ["TagLink"],
parseInline: [
{
name: "TagLink",
parse(cx, next, pos) {
let match: RegExpMatchArray | null;
if (
next != 35 /* '#' */ ||
!(match = /^[A-Za-z\.]+/.exec(cx.slice(pos + 1, cx.end)))
) {
return -1;
}
return cx.addElement(cx.elt("TagLink", pos, pos + 1 + match[0].length));
export default function buildMarkdown(mdExtensions: MDExt[]): Language {
return mkLang(
commonmark.configure([
WikiLink,
TaskList,
Comment,
...mdExtensions.map(mdExtensionSyntaxConfig),
parseCode({
codeParser: getCodeParser([
LanguageDescription.of({
name: "yaml",
support: new LanguageSupport(StreamLanguage.define(yaml)),
}),
LanguageDescription.of({
name: "javascript",
alias: ["js"],
support: new LanguageSupport(javascriptLanguage),
}),
]),
}),
{
props: [
styleTags({
WikiLink: ct.WikiLinkTag,
WikiLinkPage: ct.WikiLinkPageTag,
Task: ct.TaskTag,
TaskMarker: ct.TaskMarkerTag,
Comment: ct.CommentTag,
}),
...mdExtensions.map((mdExt) =>
styleTags(mdExtensionStyleTags(mdExt))
),
],
},
after: "Emphasis",
},
],
};
const WikiMarkdown = commonmark.configure([
WikiLink,
AtMention,
// TagLink,
TaskList,
UnmarkedUrl,
Comment,
parseCode({
codeParser: getCodeParser([
LanguageDescription.of({
name: "yaml",
support: new LanguageSupport(StreamLanguage.define(yaml)),
}),
LanguageDescription.of({
name: "javascript",
alias: ["js"],
support: new LanguageSupport(javascriptLanguage),
}),
]),
}),
{
props: [
styleTags({
WikiLink: ct.WikiLinkTag,
WikiLinkPage: ct.WikiLinkPageTag,
AtMention: ct.MentionTag,
TagLink: ct.TagTag,
Task: ct.TaskTag,
TaskMarker: ct.TaskMarkerTag,
Url: t.url,
Comment: ct.CommentTag,
}),
],
},
]);
export default mkLang(WikiMarkdown);
])
);
}

View File

@ -19,7 +19,6 @@ async function activate() {
self.addEventListener("activate", (e) => e.waitUntil(activate()));
self.addEventListener("fetch", (event: any) => {
return;
event.respondWith(
caches.open(version).then(async (cache) => {
let parsedUrl = new URL(event.request.url);

View File

@ -1,38 +1,44 @@
import { HighlightStyle, tags as t } from "@codemirror/highlight";
import * as ct from "./customtags";
import { MDExt } from "./markdown_ext";
export default HighlightStyle.define([
{ tag: t.heading1, class: "h1" },
{ tag: t.heading2, class: "h2" },
{ tag: t.heading3, class: "h3" },
{ tag: t.link, class: "link" },
{ tag: t.meta, class: "meta" },
{ tag: t.quote, class: "quote" },
{ tag: t.monospace, class: "code" },
{ tag: t.url, class: "url" },
{ tag: ct.WikiLinkTag, class: "wiki-link" },
{ tag: ct.WikiLinkPageTag, class: "wiki-link-page" },
{ tag: ct.TagTag, class: "tag" },
{ tag: ct.MentionTag, class: "mention" },
{ tag: ct.TaskTag, class: "task" },
{ tag: ct.TaskMarkerTag, class: "task-marker" },
{ tag: ct.CommentTag, class: "comment" },
{ tag: ct.CommentMarkerTag, class: "comment-marker" },
{ tag: t.emphasis, class: "emphasis" },
{ tag: t.strong, class: "strong" },
{ tag: t.atom, class: "atom" },
{ tag: t.bool, class: "bool" },
{ tag: t.url, class: "url" },
{ tag: t.inserted, class: "inserted" },
{ tag: t.deleted, class: "deleted" },
{ tag: t.literal, class: "literal" },
{ tag: t.list, class: "list" },
{ tag: t.definition, class: "li" },
{ tag: t.string, class: "string" },
{ tag: t.number, class: "number" },
{ tag: [t.regexp, t.escape, t.special(t.string)], class: "string2" },
{ tag: t.variableName, class: "variableName" },
{ tag: t.comment, class: "comment" },
{ tag: t.invalid, class: "invalid" },
{ tag: t.punctuation, class: "punctuation" },
]);
export default function highlightStyles(mdExtension: MDExt[]) {
return HighlightStyle.define([
{ tag: t.heading1, class: "h1" },
{ tag: t.heading2, class: "h2" },
{ tag: t.heading3, class: "h3" },
{ tag: t.link, class: "link" },
{ tag: t.meta, class: "meta" },
{ tag: t.quote, class: "quote" },
{ tag: t.monospace, class: "code" },
{ tag: t.url, class: "url" },
{ tag: ct.WikiLinkTag, class: "wiki-link" },
{ tag: ct.WikiLinkPageTag, class: "wiki-link-page" },
{ tag: ct.TagTag, class: "tag" },
{ tag: ct.MentionTag, class: "mention" },
{ tag: ct.TaskTag, class: "task" },
{ tag: ct.TaskMarkerTag, class: "task-marker" },
{ tag: ct.CommentTag, class: "comment" },
{ tag: ct.CommentMarkerTag, class: "comment-marker" },
{ tag: t.emphasis, class: "emphasis" },
{ tag: t.strong, class: "strong" },
{ tag: t.atom, class: "atom" },
{ tag: t.bool, class: "bool" },
{ tag: t.url, class: "url" },
{ tag: t.inserted, class: "inserted" },
{ tag: t.deleted, class: "deleted" },
{ tag: t.literal, class: "literal" },
{ tag: t.list, class: "list" },
{ tag: t.definition, class: "li" },
{ tag: t.string, class: "string" },
{ tag: t.number, class: "number" },
{ tag: [t.regexp, t.escape, t.special(t.string)], class: "string2" },
{ tag: t.variableName, class: "variableName" },
{ tag: t.comment, class: "comment" },
{ tag: t.invalid, class: "invalid" },
{ tag: t.punctuation, class: "punctuation" },
...mdExtension.map((mdExt) => {
return { tag: mdExt.tag, ...mdExt.styles };
}),
]);
}