Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol
diffdown.com
1import {SyntaxNode, Parser, Input, parseMixed, SyntaxNodeRef} from "@lezer/common"
2import {Type, MarkdownExtension} from "./markdown"
3
4function leftOverSpace(node: SyntaxNode, from: number, to: number) {
5 let ranges = []
6 for (let n = node.firstChild, pos = from;; n = n.nextSibling) {
7 let nextPos = n ? n.from : to
8 if (nextPos > pos) ranges.push({from: pos, to: nextPos})
9 if (!n) break
10 pos = n.to
11 }
12 return ranges
13}
14
15/// Create a Markdown extension to enable nested parsing on code
16/// blocks and/or embedded HTML.
17export function parseCode(config: {
18 /// When provided, this will be used to parse the content of code
19 /// blocks. `info` is the string after the opening ` ``` ` marker,
20 /// or the empty string if there is no such info or this is an
21 /// indented code block. If there is a parser available for the
22 /// code, it should return a function that can construct the
23 /// [parse](https://lezer.codemirror.net/docs/ref/#common.PartialParse).
24 codeParser?: (info: string) => null | Parser
25 /// The parser used to parse HTML tags (both block and inline).
26 htmlParser?: Parser,
27}): MarkdownExtension {
28 let {codeParser, htmlParser} = config
29 let wrap = parseMixed((node: SyntaxNodeRef, input: Input) => {
30 let id = node.type.id
31 if (codeParser && (id == Type.CodeBlock || id == Type.FencedCode)) {
32 let info = ""
33 if (id == Type.FencedCode) {
34 let infoNode = node.node.getChild(Type.CodeInfo)
35 if (infoNode) info = input.read(infoNode.from, infoNode.to)
36 }
37 let parser = codeParser(info)
38 if (parser)
39 return {parser, overlay: node => node.type.id == Type.CodeText, bracketed: id == Type.FencedCode}
40 } else if (htmlParser && (id == Type.HTMLBlock || id == Type.HTMLTag || id == Type.CommentBlock)) {
41 return {parser: htmlParser, overlay: leftOverSpace(node.node, node.from, node.to)}
42 }
43 return null
44 })
45 return {wrap}
46}