Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol
diffdown.com
1import {Tree, NodeProp} from "@lezer/common"
2import {parser as html} from "@lezer/html"
3import ist from "ist"
4import {parser, parseCode} from "../dist/index.js"
5
6let full = parser.configure(parseCode({
7 codeParser(str) { return !str || str == "html" ? html : null },
8 htmlParser: html.configure({dialect: "noMatch"})
9}))
10
11function findMounts(tree: Tree) {
12 let result = []
13 for (let cur = tree.cursor(); cur.next();) {
14 let mount = cur.tree?.prop(NodeProp.mounted)
15 if (mount) result.push({at: cur.from, mount})
16 }
17 return result
18}
19
20function test(doc: string, ...nest: [string, ...number[]][]) {
21 return () => {
22 let tree = full.parse(doc), mounts = findMounts(tree)
23 ist(mounts.length, nest.length)
24 nest.forEach(([repr, ...ranges], i) => {
25 let {mount, at} = mounts[i]
26 ist(mount.tree.toString(), "Document(" + repr + ")")
27 ist(mount.overlay!.map(r => (r.from + at) + "," + (r.to + at)).join(), ranges.join())
28 })
29 }
30}
31
32describe("Code parsing", () => {
33 it("parses HTML blocks", test(`
34Paragraph
35
36<div id=x>
37 Hello & goodbye
38</div>`, ["Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag),Text,EntityReference,Text,CloseTag(StartCloseTag,TagName,EndTag))", 12, 51]))
39
40 it("parses inline HTML", test(
41 `Paragraph with <em>inline tags</em> in it.`,
42 ["Element(OpenTag(StartTag,TagName,EndTag))", 15, 19],
43 ["CloseTag(StartCloseTag,TagName,EndTag)", 30, 35]))
44
45 it("parses indented code", test(`
46Paragraph.
47
48 <!doctype html>
49 Hi
50`, ["DoctypeDecl,Text", 17, 33, 37, 39]))
51
52 it("parses fenced code", test(`
53Okay
54
55~~~
56<p>
57 Hey
58</p>
59~~~`, ["Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag))", 11, 25]))
60
61 it("allows gaps in fenced code", test(`
62- >~~~
63 ><!doctype html>
64 >yay
65 > ~~~`, ["DoctypeDecl,Text", 11, 27, 30, 33]))
66
67 it("passes fenced code info", test(`
68~~~html
69»
70~~~
71
72~~~python
73False
74~~~`, ["EntityReference", 9, 16]))
75
76 it("can parse disjoint ranges", () => {
77 let tree = parser.parse(`==foo\n==\n==ba==r\n==`, undefined,
78 [{from: 2, to: 6}, {from: 8, to: 9}, {from: 11, to: 13}, {from: 15, to: 17}])
79 ist(tree.toString(), "Document(Paragraph,Paragraph)")
80 ist(tree.length, 15)
81 ist(tree.topNode.firstChild!.from, 0)
82 ist(tree.topNode.firstChild!.to, 3)
83 ist(tree.topNode.lastChild!.from, 9)
84 ist(tree.topNode.lastChild!.to, 14)
85 })
86})