Fixes #92: implements frontmatter syntax

This commit is contained in:
Zef Hemel
2022-10-17 15:48:21 +02:00
parent ce6122458f
commit 7d1a04f392
13 changed files with 316 additions and 50 deletions
+1
View File
@@ -27,6 +27,7 @@ export type {
BlockContext,
LeafBlock,
LeafBlockParser,
Line,
MarkdownConfig,
MarkdownExtension,
} from "@lezer/markdown";
+7 -6
View File
@@ -2,6 +2,7 @@ import { ParseTree } from "$sb/lib/tree.ts";
import type { SyntaxNode } from "./deps.ts";
import type { Language } from "./deps.ts";
export function lezerToParseTree(
text: string,
n: SyntaxNode,
@@ -24,10 +25,10 @@ export function lezerToParseTree(
},
];
} else {
let newChildren: ParseTree[] = [];
const newChildren: ParseTree[] = [];
let index = n.from;
for (let child of children) {
let s = text.substring(index, child.from);
for (const child of children) {
const s = text.substring(index, child.from);
if (s) {
newChildren.push({
from: index + offset,
@@ -38,14 +39,14 @@ export function lezerToParseTree(
newChildren.push(child);
index = child.to!;
}
let s = text.substring(index, n.to);
const s = text.substring(index, n.to);
if (s) {
newChildren.push({ from: index + offset, to: n.to + offset, text: s });
}
children = newChildren;
}
let result: ParseTree = {
const result: ParseTree = {
type: n.name,
from: n.from + offset,
to: n.to + offset,
@@ -60,7 +61,7 @@ export function lezerToParseTree(
}
export function parse(language: Language, text: string): ParseTree {
let tree = lezerToParseTree(text, language.parser.parse(text).topNode);
const 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");
+38
View File
@@ -0,0 +1,38 @@
import { parse } from "./parse_tree.ts";
import buildMarkdown from "./parser.ts";
import { findNodeOfType, renderToText } from "../plug-api/lib/tree.ts";
import { assertEquals, assertNotEquals } from "../test_deps.ts";
const sample1 = `---
type: page
tags:
- hello
- world
---
# This is a doc
Supper`;
const sampleInvalid1 = `---
name: Zef
# This is a doc
Supper`;
Deno.test("Test parser", () => {
const lang = buildMarkdown([]);
let tree = parse(
lang,
sample1,
);
// Check if rendering back to text works
assertEquals(renderToText(tree), sample1);
// console.log("tree", JSON.stringify(tree, null, 2));
let node = findNodeOfType(tree, "FrontMatter");
assertNotEquals(node, undefined);
tree = parse(lang, sampleInvalid1);
node = findNodeOfType(tree, "FrontMatter");
// console.log("Invalid node", node);
assertEquals(node, undefined);
});
+69 -23
View File
@@ -1,17 +1,17 @@
import {
BlockContext,
Language,
LanguageDescription,
LanguageSupport,
LeafBlock,
LeafBlockParser,
Line,
markdown,
MarkdownConfig,
parseCode,
StreamLanguage,
styleTags,
Table,
tags as t,
TaskList,
yamlLanguage,
} from "./deps.ts";
import * as ct from "./customtags.ts";
import {
@@ -92,7 +92,7 @@ export const Comment: MarkdownConfig = {
parseBlock: [
{
name: "Comment",
leaf(cx, leaf) {
leaf(_cx, leaf) {
return /^%%\s/.test(leaf.content) ? new CommentParser() : null;
},
after: "SetextHeading",
@@ -100,34 +100,80 @@ export const Comment: MarkdownConfig = {
],
};
// FrontMatter parser
const lang = StreamLanguage.define(yamlLanguage);
export const FrontMatter: MarkdownConfig = {
defineNodes: [
{ name: "FrontMatter", block: true },
{ name: "FrontMatterMarker" },
{ name: "FrontMatterCode" },
],
parseBlock: [{
name: "FrontMatter",
parse: (cx, line: Line) => {
if (cx.parsedPos !== 0) {
return false;
}
if (line.text !== "---") {
return false;
}
const frontStart = cx.parsedPos;
const elts = [
cx.elt(
"FrontMatterMarker",
cx.parsedPos,
cx.parsedPos + line.text.length + 1,
),
];
cx.nextLine();
const startPos = cx.parsedPos;
let endPos = startPos;
let text = "";
let lastPos = cx.parsedPos;
do {
text += line.text + "\n";
endPos += line.text.length + 1;
cx.nextLine();
if (cx.parsedPos === lastPos) {
// End of file, no progress made, there may be a better way to do this but :shrug:
return false;
}
lastPos = cx.parsedPos;
} while (line.text !== "---");
const yamlTree = lang.parser.parse(text);
elts.push(
cx.elt("FrontMatterCode", startPos, endPos, [
cx.elt(yamlTree, startPos),
]),
);
endPos = cx.parsedPos + line.text.length;
elts.push(cx.elt(
"FrontMatterMarker",
cx.parsedPos,
cx.parsedPos + line.text.length,
));
cx.nextLine();
cx.addElement(cx.elt("FrontMatter", frontStart, endPos, elts));
return true;
},
before: "HorizontalRule",
}],
};
export default function buildMarkdown(mdExtensions: MDExt[]): Language {
return markdown({
extensions: [
WikiLink,
FrontMatter,
TaskList,
Comment,
Strikethrough,
Table,
...mdExtensions.map(mdExtensionSyntaxConfig),
// parseCode({
// codeParser: getCodeParser([
// LanguageDescription.of({
// name: "yaml",
// alias: ["meta", "data"],
// support: new LanguageSupport(StreamLanguage.define(yaml)),
// }),
// LanguageDescription.of({
// name: "javascript",
// alias: ["js"],
// support: new LanguageSupport(javascriptLanguage),
// }),
// LanguageDescription.of({
// name: "typescript",
// alias: ["ts"],
// support: new LanguageSupport(typescriptLanguage),
// }),
// ]),
// }),
{
props: [
styleTags({