Remove "syntax" support from plugs
This commit is contained in:
@@ -17,6 +17,12 @@ export const AttributeTag = Tag.define();
|
||||
export const AttributeNameTag = Tag.define();
|
||||
export const AttributeValueTag = Tag.define();
|
||||
|
||||
export const NamedAnchorTag = Tag.define();
|
||||
|
||||
export const TaskTag = Tag.define();
|
||||
export const TaskMarkTag = Tag.define();
|
||||
export const TaskStateTag = Tag.define();
|
||||
export const TaskDeadlineTag = Tag.define();
|
||||
|
||||
export const HashtagTag = Tag.define();
|
||||
export const NakedURLTag = Tag.define();
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import { Tag } from "../deps.ts";
|
||||
import type { MarkdownConfig } from "../deps.ts";
|
||||
import { System } from "../../plugos/system.ts";
|
||||
import { Manifest, NodeDef } from "../manifest.ts";
|
||||
|
||||
export type MDExt = {
|
||||
// unicode char code for efficiency .charCodeAt(0)
|
||||
firstCharCodes: number[];
|
||||
regex: RegExp;
|
||||
nodeType: string;
|
||||
tag: Tag;
|
||||
styles?: { [key: string]: string };
|
||||
className?: 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;
|
||||
}
|
||||
const 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[] {
|
||||
const mdExtensions: MDExt[] = [];
|
||||
for (const plug of system.loadedPlugs.values()) {
|
||||
const manifest = plug.manifest as Manifest;
|
||||
if (manifest.syntax) {
|
||||
for (const [nodeType, def] of Object.entries(manifest.syntax)) {
|
||||
mdExtensions.push(nodeDefToMDExt(nodeType, def));
|
||||
}
|
||||
}
|
||||
}
|
||||
return mdExtensions;
|
||||
}
|
||||
|
||||
export function nodeDefToMDExt(nodeType: string, def: NodeDef): MDExt {
|
||||
return {
|
||||
nodeType,
|
||||
tag: Tag.define(),
|
||||
firstCharCodes: def.firstCharacters.map((ch) => ch.charCodeAt(0)),
|
||||
regex: new RegExp("^" + def.regex),
|
||||
styles: def.styles,
|
||||
className: def.className,
|
||||
};
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { parse } from "./parse_tree.ts";
|
||||
import buildMarkdown from "./parser.ts";
|
||||
import {
|
||||
collectNodesOfType,
|
||||
findNodeOfType,
|
||||
renderToText,
|
||||
} from "../../plug-api/lib/tree.ts";
|
||||
import { assertEquals, assertNotEquals } from "../../test_deps.ts";
|
||||
import { extendedMarkdownLanguage } from "./parser.ts";
|
||||
|
||||
const sample1 = `---
|
||||
type: page
|
||||
@@ -26,8 +26,7 @@ name: Zef
|
||||
Supper`;
|
||||
|
||||
Deno.test("Test parser", () => {
|
||||
const lang = buildMarkdown([]);
|
||||
let tree = parse(lang, sample1);
|
||||
let tree = parse(extendedMarkdownLanguage, sample1);
|
||||
// console.log("tree", JSON.stringify(tree, null, 2));
|
||||
// Check if rendering back to text works
|
||||
assertEquals(renderToText(tree), sample1);
|
||||
@@ -45,7 +44,7 @@ Deno.test("Test parser", () => {
|
||||
// Find frontmatter
|
||||
let node = findNodeOfType(tree, "FrontMatter");
|
||||
assertNotEquals(node, undefined);
|
||||
tree = parse(lang, sampleInvalid1);
|
||||
tree = parse(extendedMarkdownLanguage, sampleInvalid1);
|
||||
node = findNodeOfType(tree, "FrontMatter");
|
||||
// console.log("Invalid node", node);
|
||||
assertEquals(node, undefined);
|
||||
@@ -62,8 +61,7 @@ And one with nested brackets: [array: [1, 2, 3]]
|
||||
`;
|
||||
|
||||
Deno.test("Test inline attribute syntax", () => {
|
||||
const lang = buildMarkdown([]);
|
||||
const tree = parse(lang, inlineAttributeSample);
|
||||
const tree = parse(extendedMarkdownLanguage, inlineAttributeSample);
|
||||
// console.log("Attribute parsed", JSON.stringify(tree, null, 2));
|
||||
const attributes = collectNodesOfType(tree, "Attribute");
|
||||
let nameNode = findNodeOfType(attributes[0], "AttributeName");
|
||||
@@ -89,8 +87,7 @@ const multiStatusTaskExample = `
|
||||
`;
|
||||
|
||||
Deno.test("Test multi-status tasks", () => {
|
||||
const lang = buildMarkdown([]);
|
||||
const tree = parse(lang, multiStatusTaskExample);
|
||||
const tree = parse(extendedMarkdownLanguage, multiStatusTaskExample);
|
||||
// console.log("Tasks parsed", JSON.stringify(tree, null, 2));
|
||||
const tasks = collectNodesOfType(tree, "Task");
|
||||
assertEquals(tasks.length, 3);
|
||||
@@ -107,8 +104,7 @@ const commandLinkSample = `
|
||||
`;
|
||||
|
||||
Deno.test("Test command links", () => {
|
||||
const lang = buildMarkdown([]);
|
||||
const tree = parse(lang, commandLinkSample);
|
||||
const tree = parse(extendedMarkdownLanguage, commandLinkSample);
|
||||
const commands = collectNodesOfType(tree, "CommandLink");
|
||||
console.log("Command links parsed", JSON.stringify(commands, null, 2));
|
||||
assertEquals(commands.length, 3);
|
||||
@@ -125,8 +121,7 @@ const commandLinkArgsSample = `
|
||||
`;
|
||||
|
||||
Deno.test("Test command link arguments", () => {
|
||||
const lang = buildMarkdown([]);
|
||||
const tree = parse(lang, commandLinkArgsSample);
|
||||
const tree = parse(extendedMarkdownLanguage, commandLinkArgsSample);
|
||||
const commands = collectNodesOfType(tree, "CommandLink");
|
||||
assertEquals(commands.length, 2);
|
||||
|
||||
@@ -138,7 +133,6 @@ Deno.test("Test command link arguments", () => {
|
||||
});
|
||||
|
||||
Deno.test("Test template directives", () => {
|
||||
const lang = buildMarkdown([]);
|
||||
const tree = parse(lang, `Hello there {{name}}!`);
|
||||
const tree = parse(extendedMarkdownLanguage, `Hello there {{name}}!`);
|
||||
console.log("Template directive", JSON.stringify(tree, null, 2));
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
BlockContext,
|
||||
Language,
|
||||
LeafBlock,
|
||||
LeafBlockParser,
|
||||
Line,
|
||||
@@ -9,16 +8,14 @@ import {
|
||||
StreamLanguage,
|
||||
Strikethrough,
|
||||
styleTags,
|
||||
Tag,
|
||||
tags as t,
|
||||
yamlLanguage,
|
||||
} from "../deps.ts";
|
||||
import * as ct from "./customtags.ts";
|
||||
import { HashtagTag, TaskDeadlineTag } from "./customtags.ts";
|
||||
import { NakedURLTag } from "./customtags.ts";
|
||||
import { TaskList } from "./extended_task.ts";
|
||||
import {
|
||||
MDExt,
|
||||
mdExtensionStyleTags,
|
||||
mdExtensionSyntaxConfig,
|
||||
} from "./markdown_ext.ts";
|
||||
|
||||
export const pageLinkRegex = /^\[\[([^\]\|]+)(\|([^\]]+))?\]\]/;
|
||||
|
||||
@@ -313,6 +310,77 @@ export const Comment: MarkdownConfig = {
|
||||
],
|
||||
};
|
||||
|
||||
type RegexParserExtension = {
|
||||
// unicode char code for efficiency .charCodeAt(0)
|
||||
firstCharCode: number;
|
||||
regex: RegExp;
|
||||
nodeType: string;
|
||||
tag: Tag;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function regexParser({
|
||||
regex,
|
||||
firstCharCode,
|
||||
nodeType,
|
||||
}: RegexParserExtension): MarkdownConfig {
|
||||
return {
|
||||
defineNodes: [nodeType],
|
||||
parseInline: [
|
||||
{
|
||||
name: nodeType,
|
||||
parse(cx, next, pos) {
|
||||
if (firstCharCode !== next) {
|
||||
return -1;
|
||||
}
|
||||
const match = regex.exec(cx.slice(pos, cx.end));
|
||||
if (!match) {
|
||||
return -1;
|
||||
}
|
||||
return cx.addElement(cx.elt(nodeType, pos, pos + match[0].length));
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const NakedURL = regexParser(
|
||||
{
|
||||
firstCharCode: 104, // h
|
||||
regex:
|
||||
/^https?:\/\/[-a-zA-Z0-9@:%._\+~#=]{1,256}([-a-zA-Z0-9()@:%_\+.~#?&=\/]*)/,
|
||||
nodeType: "NakedURL",
|
||||
className: "sb-naked-url",
|
||||
tag: NakedURLTag,
|
||||
},
|
||||
);
|
||||
|
||||
const Hashtag = regexParser(
|
||||
{
|
||||
firstCharCode: 35, // #
|
||||
regex: /^#[^#\d\s\[\]]+\w+/,
|
||||
nodeType: "Hashtag",
|
||||
className: "sb-hashtag",
|
||||
tag: ct.HashtagTag,
|
||||
},
|
||||
);
|
||||
|
||||
const TaskDeadline = regexParser({
|
||||
firstCharCode: 55357, // 📅
|
||||
regex: /^📅\s*\d{4}\-\d{2}\-\d{2}/,
|
||||
className: "sb-task-deadline",
|
||||
nodeType: "DeadlineDate",
|
||||
tag: ct.TaskDeadlineTag,
|
||||
});
|
||||
|
||||
const NamedAnchor = regexParser({
|
||||
firstCharCode: 36, // $
|
||||
regex: /^\$[a-zA-Z\.\-\/]+[\w\.\-\/]*/,
|
||||
className: "sb-named-anchor",
|
||||
nodeType: "NamedAnchor",
|
||||
tag: ct.NamedAnchorTag,
|
||||
});
|
||||
|
||||
import { Table } from "./table_parser.ts";
|
||||
import { foldNodeProp } from "@codemirror/language";
|
||||
|
||||
@@ -379,54 +447,56 @@ export const FrontMatter: MarkdownConfig = {
|
||||
}],
|
||||
};
|
||||
|
||||
export default function buildMarkdown(mdExtensions: MDExt[]): Language {
|
||||
return markdown({
|
||||
extensions: [
|
||||
WikiLink,
|
||||
CommandLink,
|
||||
Attribute,
|
||||
FrontMatter,
|
||||
TaskList,
|
||||
Comment,
|
||||
Highlight,
|
||||
TemplateDirective,
|
||||
Strikethrough,
|
||||
Table,
|
||||
...mdExtensions.map(mdExtensionSyntaxConfig),
|
||||
{
|
||||
props: [
|
||||
foldNodeProp.add({
|
||||
// Don't fold at the list level
|
||||
BulletList: () => null,
|
||||
OrderedList: () => null,
|
||||
// Fold list items
|
||||
ListItem: (tree, state) => ({
|
||||
from: state.doc.lineAt(tree.from).to,
|
||||
to: tree.to,
|
||||
}),
|
||||
// Fold frontmatter
|
||||
FrontMatter: (tree) => ({
|
||||
from: tree.from,
|
||||
to: tree.to,
|
||||
}),
|
||||
export const extendedMarkdownLanguage = markdown({
|
||||
extensions: [
|
||||
WikiLink,
|
||||
CommandLink,
|
||||
Attribute,
|
||||
FrontMatter,
|
||||
TaskList,
|
||||
Comment,
|
||||
Highlight,
|
||||
TemplateDirective,
|
||||
Strikethrough,
|
||||
Table,
|
||||
NakedURL,
|
||||
Hashtag,
|
||||
TaskDeadline,
|
||||
NamedAnchor,
|
||||
{
|
||||
props: [
|
||||
foldNodeProp.add({
|
||||
// Don't fold at the list level
|
||||
BulletList: () => null,
|
||||
OrderedList: () => null,
|
||||
// Fold list items
|
||||
ListItem: (tree, state) => ({
|
||||
from: state.doc.lineAt(tree.from).to,
|
||||
to: tree.to,
|
||||
}),
|
||||
// Fold frontmatter
|
||||
FrontMatter: (tree) => ({
|
||||
from: tree.from,
|
||||
to: tree.to,
|
||||
}),
|
||||
}),
|
||||
|
||||
styleTags({
|
||||
Task: ct.TaskTag,
|
||||
TaskMark: ct.TaskMarkTag,
|
||||
Comment: ct.CommentTag,
|
||||
"TableDelimiter SubscriptMark SuperscriptMark StrikethroughMark":
|
||||
t.processingInstruction,
|
||||
"TableHeader/...": t.heading,
|
||||
TableCell: t.content,
|
||||
CodeInfo: ct.CodeInfoTag,
|
||||
HorizontalRule: ct.HorizontalRuleTag,
|
||||
}),
|
||||
...mdExtensions.map((mdExt) =>
|
||||
styleTags(mdExtensionStyleTags(mdExt))
|
||||
),
|
||||
],
|
||||
},
|
||||
],
|
||||
}).language;
|
||||
}
|
||||
styleTags({
|
||||
Task: ct.TaskTag,
|
||||
TaskMark: ct.TaskMarkTag,
|
||||
Comment: ct.CommentTag,
|
||||
"TableDelimiter SubscriptMark SuperscriptMark StrikethroughMark":
|
||||
t.processingInstruction,
|
||||
"TableHeader/...": t.heading,
|
||||
TableCell: t.content,
|
||||
CodeInfo: ct.CodeInfoTag,
|
||||
HorizontalRule: ct.HorizontalRuleTag,
|
||||
Hashtag: ct.HashtagTag,
|
||||
NakedURL: ct.NakedURLTag,
|
||||
DeadlineDate: ct.TaskDeadlineTag,
|
||||
NamedAnchor: ct.NamedAnchorTag,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
}).language;
|
||||
|
||||
Reference in New Issue
Block a user