Premier commit déjà bien avancé
This commit is contained in:
14
frontend/node_modules/@lezer/markdown/test/compare-tree.ts
generated
vendored
Normal file
14
frontend/node_modules/@lezer/markdown/test/compare-tree.ts
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
import {Tree} from "@lezer/common"
|
||||
|
||||
export function compareTree(a: Tree, b: Tree) {
|
||||
let curA = a.cursor(), curB = b.cursor()
|
||||
for (;;) {
|
||||
let mismatch = null, next = false
|
||||
if (curA.type != curB.type) mismatch = `Node type mismatch (${curA.name} vs ${curB.name})`
|
||||
else if (curA.from != curB.from) mismatch = `Start pos mismatch for ${curA.name}: ${curA.from} vs ${curB.from}`
|
||||
else if (curA.to != curB.to) mismatch = `End pos mismatch for ${curA.name}: ${curA.to} vs ${curB.to}`
|
||||
else if ((next = curA.next()) != curB.next()) mismatch = `Tree size mismatch`
|
||||
if (mismatch) throw new Error(`${mismatch}\n ${a}\n ${b}`)
|
||||
if (!next) break
|
||||
}
|
||||
}
|
||||
79
frontend/node_modules/@lezer/markdown/test/spec.ts
generated
vendored
Normal file
79
frontend/node_modules/@lezer/markdown/test/spec.ts
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
import {Tree} from "@lezer/common"
|
||||
import {MarkdownParser} from ".."
|
||||
|
||||
const abbrev: {[abbr: string]: string} = {
|
||||
__proto__: null as any,
|
||||
CB: "CodeBlock",
|
||||
FC: "FencedCode",
|
||||
Q: "Blockquote",
|
||||
HR: "HorizontalRule",
|
||||
BL: "BulletList",
|
||||
OL: "OrderedList",
|
||||
LI: "ListItem",
|
||||
H1: "ATXHeading1",
|
||||
H2: "ATXHeading2",
|
||||
H3: "ATXHeading3",
|
||||
H4: "ATXHeading4",
|
||||
H5: "ATXHeading5",
|
||||
H6: "ATXHeading6",
|
||||
SH1: "SetextHeading1",
|
||||
SH2: "SetextHeading2",
|
||||
HB: "HTMLBlock",
|
||||
PI: "ProcessingInstructionBlock",
|
||||
CMB: "CommentBlock",
|
||||
LR: "LinkReference",
|
||||
P: "Paragraph",
|
||||
Esc: "Escape",
|
||||
Ent: "Entity",
|
||||
BR: "HardBreak",
|
||||
Em: "Emphasis",
|
||||
St: "StrongEmphasis",
|
||||
Ln: "Link",
|
||||
Al: "Autolink",
|
||||
Im: "Image",
|
||||
C: "InlineCode",
|
||||
HT: "HTMLTag",
|
||||
CM: "Comment",
|
||||
Pi: "ProcessingInstruction",
|
||||
h: "HeaderMark",
|
||||
q: "QuoteMark",
|
||||
l: "ListMark",
|
||||
L: "LinkMark",
|
||||
e: "EmphasisMark",
|
||||
c: "CodeMark",
|
||||
cI: "CodeInfo",
|
||||
cT: "CodeText",
|
||||
LT: "LinkTitle",
|
||||
LL: "LinkLabel"
|
||||
}
|
||||
|
||||
export class SpecParser {
|
||||
constructor(readonly parser: MarkdownParser, readonly localAbbrev?: {[name: string]: string}) {}
|
||||
|
||||
type(name: string) {
|
||||
name = (this.localAbbrev && this.localAbbrev[name]) || abbrev[name] || name
|
||||
return this.parser.nodeSet.types.find(t => t.name == name)?.id
|
||||
}
|
||||
|
||||
parse(spec: string, specName: string) {
|
||||
let doc = "", buffer = [], stack: number[] = []
|
||||
for (let pos = 0; pos < spec.length; pos++) {
|
||||
let ch = spec[pos]
|
||||
if (ch == "{") {
|
||||
let name = /^(\w+):/.exec(spec.slice(pos + 1)), tag = name && this.type(name[1])
|
||||
if (tag == null) throw new Error(`Invalid node opening mark at ${pos} in ${specName}`)
|
||||
pos += name![0].length
|
||||
stack.push(tag, doc.length, buffer.length)
|
||||
} else if (ch == "}") {
|
||||
if (!stack.length) throw new Error(`Mismatched node close mark at ${pos} in ${specName}`)
|
||||
let bufStart = stack.pop()!, from = stack.pop()!, type = stack.pop()!
|
||||
buffer.push(type, from, doc.length, 4 + buffer.length - bufStart)
|
||||
} else {
|
||||
doc += ch
|
||||
}
|
||||
}
|
||||
if (stack.length) throw new Error(`Unclosed node in ${specName}`)
|
||||
return {tree: Tree.build({buffer, nodeSet: this.parser.nodeSet, topID: this.type("Document")!, length: doc.length}), doc}
|
||||
}
|
||||
}
|
||||
|
||||
269
frontend/node_modules/@lezer/markdown/test/test-extension.ts
generated
vendored
Normal file
269
frontend/node_modules/@lezer/markdown/test/test-extension.ts
generated
vendored
Normal file
@ -0,0 +1,269 @@
|
||||
import {parser as cmParser, GFM, Subscript, Superscript, Emoji} from "../dist/index.js"
|
||||
import {compareTree} from "./compare-tree.js"
|
||||
import {SpecParser} from "./spec.js"
|
||||
|
||||
const parser = cmParser.configure([GFM, Subscript, Superscript, Emoji])
|
||||
|
||||
const specParser = new SpecParser(parser, {
|
||||
__proto__: null as any,
|
||||
Th: "Strikethrough",
|
||||
tm: "StrikethroughMark",
|
||||
TB: "Table",
|
||||
TH: "TableHeader",
|
||||
TR: "TableRow",
|
||||
TC: "TableCell",
|
||||
tb: "TableDelimiter",
|
||||
T: "Task",
|
||||
t: "TaskMarker",
|
||||
Sub: "Subscript",
|
||||
sub: "SubscriptMark",
|
||||
Sup: "Superscript",
|
||||
sup: "SuperscriptMark",
|
||||
ji: "Emoji"
|
||||
})
|
||||
|
||||
function test(name: string, spec: string, p = parser) {
|
||||
it(name, () => {
|
||||
let {tree, doc} = specParser.parse(spec, name)
|
||||
compareTree(p.parse(doc), tree)
|
||||
})
|
||||
}
|
||||
|
||||
describe("Extension", () => {
|
||||
test("Tables (example 198)", `
|
||||
{TB:{TH:{tb:|} {TC:foo} {tb:|} {TC:bar} {tb:|}}
|
||||
{tb:| --- | --- |}
|
||||
{TR:{tb:|} {TC:baz} {tb:|} {TC:bim} {tb:|}}}`)
|
||||
|
||||
test("Tables (example 199)", `
|
||||
{TB:{TH:{tb:|} {TC:abc} {tb:|} {TC:defghi} {tb:|}}
|
||||
{tb::-: | -----------:}
|
||||
{TR:{TC:bar} {tb:|} {TC:baz}}}`)
|
||||
|
||||
test("Tables (example 200)", `
|
||||
{TB:{TH:{tb:|} {TC:f{Esc:\\|}oo} {tb:|}}
|
||||
{tb:| ------ |}
|
||||
{TR:{tb:|} {TC:b {C:{c:\`}\\|{c:\`}} az} {tb:|}}
|
||||
{TR:{tb:|} {TC:b {St:{e:**}{Esc:\\|}{e:**}} im} {tb:|}}}`)
|
||||
|
||||
test("Tables (example 201)", `
|
||||
{TB:{TH:{tb:|} {TC:abc} {tb:|} {TC:def} {tb:|}}
|
||||
{tb:| --- | --- |}
|
||||
{TR:{tb:|} {TC:bar} {tb:|} {TC:baz} {tb:|}}}
|
||||
{Q:{q:>} {P:bar}}`)
|
||||
|
||||
test("Tables (example 202)", `
|
||||
{TB:{TH:{tb:|} {TC:abc} {tb:|} {TC:def} {tb:|}}
|
||||
{tb:| --- | --- |}
|
||||
{TR:{tb:|} {TC:bar} {tb:|} {TC:baz} {tb:|}}
|
||||
{TR:{TC:bar}}}
|
||||
|
||||
{P:bar}`)
|
||||
|
||||
test("Tables (example 203)", `
|
||||
{P:| abc | def |
|
||||
| --- |
|
||||
| bar |}`)
|
||||
|
||||
test("Tables (example 204)", `
|
||||
{TB:{TH:{tb:|} {TC:abc} {tb:|} {TC:def} {tb:|}}
|
||||
{tb:| --- | --- |}
|
||||
{TR:{tb:|} {TC:bar} {tb:|}}
|
||||
{TR:{tb:|} {TC:bar} {tb:|} {TC:baz} {tb:|} {TC:boo} {tb:|}}}`)
|
||||
|
||||
test("Tables (example 205)", `
|
||||
{TB:{TH:{tb:|} {TC:abc} {tb:|} {TC:def} {tb:|}}
|
||||
{tb:| --- | --- |}}`)
|
||||
|
||||
test("Tables (in blockquote)", `
|
||||
{Q:{q:>} {TB:{TH:{tb:|} {TC:one} {tb:|} {TC:two} {tb:|}}
|
||||
{q:>} {tb:| --- | --- |}
|
||||
{q:>} {TR:{tb:|} {TC:123} {tb:|} {TC:456} {tb:|}}}
|
||||
{q:>}
|
||||
{q:>} {P:Okay}}`)
|
||||
|
||||
test("Tables (empty header)", `
|
||||
{TB:{TH:{tb:|} {tb:|} {tb:|}}
|
||||
{tb:| :-: | :-: |}
|
||||
{TR:{tb:|} {TC:One} {tb:|} {TC:Two} {tb:|}}}`)
|
||||
|
||||
test("Tables (end paragraph)", `
|
||||
{P:Hello}
|
||||
{TB:{TH:{tb:|} {TC:foo} {tb:|} {TC:bar} {tb:|}}
|
||||
{tb:| --- | --- |}
|
||||
{TR:{tb:|} {TC:baz} {tb:|} {TC:bim} {tb:|}}}`)
|
||||
|
||||
test("Tables (invalid tables don't end paragraph)", `
|
||||
{P:Hello
|
||||
| abc | def |
|
||||
| --- |
|
||||
| bar |}`)
|
||||
|
||||
test("Task list (example 279)", `
|
||||
{BL:{LI:{l:-} {T:{t:[ ]} foo}}
|
||||
{LI:{l:-} {T:{t:[x]} bar}}}`)
|
||||
|
||||
test("Task list (example 280)", `
|
||||
{BL:{LI:{l:-} {T:{t:[x]} foo}
|
||||
{BL:{LI:{l:-} {T:{t:[ ]} bar}}
|
||||
{LI:{l:-} {T:{t:[x]} baz}}}}
|
||||
{LI:{l:-} {T:{t:[ ]} bim}}}`)
|
||||
|
||||
test("Autolink (example 622)", `
|
||||
{P:{URL:www.commonmark.org}}`)
|
||||
|
||||
test("Autolink (example 623)", `
|
||||
{P:Visit {URL:www.commonmark.org/help} for more information.}`)
|
||||
|
||||
test("Autolink (example 624)", `
|
||||
{P:Visit {URL:www.commonmark.org}.}
|
||||
|
||||
{P:Visit {URL:www.commonmark.org/a.b}.}`)
|
||||
|
||||
test("Autolink (example 625)", `
|
||||
{P:{URL:www.google.com/search?q=Markup+(business)}}
|
||||
|
||||
{P:{URL:www.google.com/search?q=Markup+(business)}))}
|
||||
|
||||
{P:({URL:www.google.com/search?q=Markup+(business)})}
|
||||
|
||||
{P:({URL:www.google.com/search?q=Markup+(business)}}`)
|
||||
|
||||
test("Autolink (example 626)", `
|
||||
{P:{URL:www.google.com/search?q=(business))+ok}}`)
|
||||
|
||||
test("Autolink (example 627)", `
|
||||
{P:{URL:www.google.com/search?q=commonmark&hl=en}}
|
||||
|
||||
{P:{URL:www.google.com/search?q=commonmark}{Entity:&hl;}}`)
|
||||
|
||||
test("Autolink (example 628)", `
|
||||
{P:{URL:www.commonmark.org/he}<lp}`)
|
||||
|
||||
test("Autolink (example 629)", `
|
||||
{P:{URL:http://commonmark.org}}
|
||||
|
||||
{P:(Visit {URL:https://encrypted.google.com/search?q=Markup+(business)})}`)
|
||||
|
||||
test("Autolink (example 630)", `
|
||||
{P:{URL:foo@bar.baz}}`)
|
||||
|
||||
test("Autolink (example 631)", `
|
||||
{P:hello@mail+xyz.example isn't valid, but {URL:hello+xyz@mail.example} is.}`)
|
||||
|
||||
test("Autolink (example 632)", `
|
||||
{P:{URL:a.b-c_d@a.b}}
|
||||
|
||||
{P:{URL:a.b-c_d@a.b}.}
|
||||
|
||||
{P:a.b-c_d@a.b-}
|
||||
|
||||
{P:a.b-c_d@a.b_}`)
|
||||
|
||||
test("Autolink (example 633)", `
|
||||
{P:{URL:mailto:foo@bar.baz}}
|
||||
|
||||
{P:{URL:mailto:a.b-c_d@a.b}}
|
||||
|
||||
{P:{URL:mailto:a.b-c_d@a.b}.}
|
||||
|
||||
{P:{URL:mailto:a.b-c_d@a.b}/}
|
||||
|
||||
{P:mailto:a.b-c_d@a.b-}
|
||||
|
||||
{P:mailto:a.b-c_d@a.b_}
|
||||
|
||||
{P:{URL:xmpp:foo@bar.baz}}
|
||||
|
||||
{P:{URL:xmpp:foo@bar.baz}.}`)
|
||||
|
||||
test("Autolink (example 634)", `
|
||||
{P:{URL:xmpp:foo@bar.baz/txt}}
|
||||
|
||||
{P:{URL:xmpp:foo@bar.baz/txt@bin}}
|
||||
|
||||
{P:{URL:xmpp:foo@bar.baz/txt@bin.com}}`)
|
||||
|
||||
test("Autolink (example 635)", `
|
||||
{P:{URL:xmpp:foo@bar.baz/txt}/bin}`)
|
||||
|
||||
test("Task list (in ordered list)", `
|
||||
{OL:{LI:{l:1.} {T:{t:[X]} Okay}}}`)
|
||||
|
||||
test("Task list (versus table)", `
|
||||
{BL:{LI:{l:-} {TB:{TH:{TC:[ ] foo} {tb:|} {TC:bar}}
|
||||
{tb:--- | ---}}}}`)
|
||||
|
||||
test("Task list (versus setext header)", `
|
||||
{OL:{LI:{l:1.} {SH1:{Ln:{L:[}X{L:]}} foo
|
||||
{h:===}}}}`)
|
||||
|
||||
test("Strikethrough (example 491)", `
|
||||
{P:{Th:{tm:~~}Hi{tm:~~}} Hello, world!}`)
|
||||
|
||||
test("Strikethrough (example 492)", `
|
||||
{P:This ~~has a}
|
||||
|
||||
{P:new paragraph~~.}`)
|
||||
|
||||
test("Strikethrough (nested)", `
|
||||
{P:Nesting {St:{e:**}with {Th:{tm:~~}emphasis{tm:~~}}{e:**}}.}`)
|
||||
|
||||
test("Strikethrough (overlapping)", `
|
||||
{P:One {St:{e:**}two ~~three{e:**}} four~~}
|
||||
|
||||
{P:One {Th:{tm:~~}two **three{tm:~~}} four**}`)
|
||||
|
||||
test("Strikethrough (escaped)", `
|
||||
{P:A {Esc:\\~}~b c~~}`)
|
||||
|
||||
test("Strikethrough around spaces", `
|
||||
{P:One ~~ two~~ three {Th:{tm:~~}.foo.{tm:~~}} a~~.foo.~~a {Th:{tm:~~}blah{tm:~~}}.}`)
|
||||
|
||||
test("Subscript", `
|
||||
{P:One {Sub:{sub:~}two{sub:~}} {Em:{e:*}one {Sub:{sub:~}two{sub:~}}{e:*}}}`)
|
||||
|
||||
test("Subscript (no spaces)", `
|
||||
{P:One ~two three~}`)
|
||||
|
||||
test("Subscript (escapes)", `
|
||||
{P:One {Sub:{sub:~}two{Esc:\\ }th{Esc:\\~}ree{sub:~}}}`)
|
||||
|
||||
test("Superscript", `
|
||||
{P:One {Sup:{sup:^}two{sup:^}} {Em:{e:*}one {Sup:{sup:^}two{sup:^}}{e:*}}}`)
|
||||
|
||||
test("Superscript (no spaces)", `
|
||||
{P:One ^two three^}`)
|
||||
|
||||
test("Superscript (escapes)", `
|
||||
{P:One {Sup:{sup:^}two{Esc:\\ }th{Esc:\\^}ree{sup:^}}}`)
|
||||
|
||||
test("Emoji", `
|
||||
{P:Hello {ji::smile:} {ji::100:}}`)
|
||||
|
||||
test("Emoji (format)", `
|
||||
{P:Hello :smi le: :1.00: ::}`)
|
||||
|
||||
test("Disable syntax", `
|
||||
{BL:{LI:{l:-} {P:List still {Em:{e:*}works{e:*}}}}}
|
||||
|
||||
{P:> No quote, no ^sup^}
|
||||
|
||||
{P:No setext either
|
||||
===}`, parser.configure({remove: ["Superscript", "Blockquote", "SetextHeading"]}))
|
||||
|
||||
test("Autolink (.co.uk)", `
|
||||
{P:{URL:www.blah.co.uk/path}}`)
|
||||
|
||||
test("Autolink (email .co.uk)", `
|
||||
{P:{URL:foo@bar.co.uk}}`)
|
||||
|
||||
test("Autolink (http://www.foo-bar.com/)", `
|
||||
{P:{URL:http://www.foo-bar.com/}}`)
|
||||
|
||||
test("Autolink (exclude underscores)", `
|
||||
{P:http://www.foo_/ http://foo_.com}`)
|
||||
|
||||
test("Autolink (in image)", `
|
||||
{P:{Im:{L:![}Link: {URL:http://foo.com/}{L:]}{L:(}{URL:x.jpg}{L:)}}}`)
|
||||
})
|
||||
259
frontend/node_modules/@lezer/markdown/test/test-incremental.ts
generated
vendored
Normal file
259
frontend/node_modules/@lezer/markdown/test/test-incremental.ts
generated
vendored
Normal file
@ -0,0 +1,259 @@
|
||||
import {Tree, TreeFragment} from "@lezer/common"
|
||||
import ist from "ist"
|
||||
import {parser} from "../dist/index.js"
|
||||
import {compareTree} from "./compare-tree.js"
|
||||
|
||||
let doc1 = `
|
||||
Header
|
||||
---
|
||||
One **two**
|
||||
three *four*
|
||||
five.
|
||||
|
||||
> Start of quote
|
||||
>
|
||||
> 1. Nested list
|
||||
>
|
||||
> 2. More content
|
||||
> inside the [list][link]
|
||||
>
|
||||
> Continued item
|
||||
>
|
||||
> ~~~
|
||||
> Block of code
|
||||
> ~~~
|
||||
>
|
||||
> 3. And so on
|
||||
|
||||
[link]: /ref
|
||||
[another]: /one
|
||||
And a final paragraph.
|
||||
***
|
||||
The end.
|
||||
`
|
||||
|
||||
type ChangeSpec = {from: number, to?: number, insert?: string}[]
|
||||
|
||||
class State {
|
||||
constructor(readonly doc: string,
|
||||
readonly tree: Tree,
|
||||
readonly fragments: readonly TreeFragment[]) {}
|
||||
|
||||
static start(doc: string) {
|
||||
let tree = parser.parse(doc)
|
||||
return new State(doc, tree, TreeFragment.addTree(tree))
|
||||
}
|
||||
|
||||
update(changes: ChangeSpec, reparse = true) {
|
||||
let changed = [], doc = this.doc, off = 0
|
||||
for (let {from, to = from, insert = ""} of changes) {
|
||||
doc = doc.slice(0, from) + insert + doc.slice(to)
|
||||
changed.push({fromA: from - off, toA: to - off, fromB: from, toB: from + insert.length})
|
||||
off += insert.length - (to - from)
|
||||
}
|
||||
let fragments = TreeFragment.applyChanges(this.fragments, changed, 2)
|
||||
if (!reparse) return new State(doc, Tree.empty, fragments)
|
||||
let tree = parser.parse(doc, fragments)
|
||||
return new State(doc, tree, TreeFragment.addTree(tree, fragments))
|
||||
}
|
||||
}
|
||||
|
||||
let _state1: State | null = null, state1 = () => _state1 || (_state1 = State.start(doc1))
|
||||
|
||||
function overlap(a: Tree, b: Tree) {
|
||||
let inA = new Set<Tree>(), shared = 0, sharingTo = 0
|
||||
for (let cur = a.cursor(); cur.next();) if (cur.tree) inA.add(cur.tree)
|
||||
for (let cur = b.cursor(); cur.next();) if (cur.tree && inA.has(cur.tree) && cur.type.is("Block") && cur.from >= sharingTo) {
|
||||
shared += cur.to - cur.from
|
||||
sharingTo = cur.to
|
||||
}
|
||||
return Math.round(shared * 100 / b.length)
|
||||
}
|
||||
|
||||
function testChange(change: ChangeSpec, reuse = 10) {
|
||||
let state = state1().update(change)
|
||||
compareTree(state.tree, parser.parse(state.doc))
|
||||
if (reuse) ist(overlap(state.tree, state1().tree), reuse, ">")
|
||||
}
|
||||
|
||||
describe("Markdown incremental parsing", () => {
|
||||
it("can produce the proper tree", () => {
|
||||
// Replace 'three' with 'bears'
|
||||
let state = state1().update([{from: 24, to: 29, insert: "bears"}])
|
||||
compareTree(state.tree, state1().tree)
|
||||
})
|
||||
|
||||
it("reuses nodes from the previous parse", () => {
|
||||
// Replace 'three' with 'bears'
|
||||
let state = state1().update([{from: 24, to: 29, insert: "bears"}])
|
||||
ist(overlap(state1().tree, state.tree), 80, ">")
|
||||
})
|
||||
|
||||
it("can reuse content for a change in a block context", () => {
|
||||
// Replace 'content' with 'monkeys'
|
||||
let state = state1().update([{from: 92, to: 99, insert: "monkeys"}])
|
||||
compareTree(state.tree, state1().tree)
|
||||
ist(overlap(state1().tree, state.tree), 20, ">")
|
||||
})
|
||||
|
||||
it("can handle deleting a quote mark", () => testChange([{from: 82, to: 83}]))
|
||||
|
||||
it("can handle adding to a quoted block", () => testChange([{from: 37, insert: "> "}, {from: 45, insert: "> "}]))
|
||||
|
||||
it("can handle a change in a post-linkref paragraph", () => testChange([{from: 249, to: 251}]))
|
||||
|
||||
it("can handle a change in a paragraph-adjacent linkrefs", () => testChange([{from: 230, to: 231}]))
|
||||
|
||||
it("can deal with multiple changes applied separately", () => {
|
||||
let state = state1().update([{from: 190, to: 191}], false).update([{from: 30, insert: "hi\n\nyou"}])
|
||||
compareTree(state.tree, parser.parse(state.doc))
|
||||
ist(overlap(state.tree, state1().tree), 20, ">")
|
||||
})
|
||||
|
||||
it("works when a change happens directly after a block", () => testChange([{from: 150, to: 167}]))
|
||||
|
||||
it("works when a change deletes a blank line after a paragraph", () => testChange([{from: 207, to: 213}]))
|
||||
|
||||
it("doesn't get confused by removing paragraph-breaking markup", () => testChange([{from: 264, to: 265}]))
|
||||
|
||||
function r(n: number) { return Math.floor(Math.random() * n) }
|
||||
function rStr(len: number) {
|
||||
let result = "", chars = "\n>x-"
|
||||
while (result.length < len) result += chars[r(chars.length)]
|
||||
return result
|
||||
}
|
||||
|
||||
it("survives random changes", () => {
|
||||
for (let i = 0, l = doc1.length; i < 20; i++) {
|
||||
let c = 1 + r(4), changes = []
|
||||
for (let i = 0, rFrom = 0; i < c; i++) {
|
||||
let rTo = rFrom + Math.floor((l - rFrom) / (c - i))
|
||||
let from = rFrom + r(rTo - rFrom - 1), to = r(2) == 1 ? from : from + r(Math.min(rTo - from, 20))
|
||||
let iR = r(3), insert = iR == 0 && from != to ? "" : iR == 1 ? "\n\n" : rStr(r(5) + 1)
|
||||
changes.push({from, to, insert})
|
||||
l += insert.length - (to - from)
|
||||
rFrom = to + insert.length
|
||||
}
|
||||
testChange(changes, 0)
|
||||
}
|
||||
})
|
||||
|
||||
it("can handle large documents", () => {
|
||||
let doc = doc1.repeat(50)
|
||||
let state = State.start(doc)
|
||||
let newState = state.update([{from: doc.length >> 1, insert: "a\n\nb"}])
|
||||
ist(overlap(state.tree, newState.tree), 90, ">")
|
||||
})
|
||||
|
||||
it("properly re-parses a continued indented code block", () => {
|
||||
let state = State.start(`
|
||||
One paragraph to create a bit of string length here
|
||||
|
||||
Code
|
||||
Block
|
||||
|
||||
|
||||
|
||||
Another paragraph that is long enough to create a fragment
|
||||
`).update([{from: 76, insert: " "}])
|
||||
compareTree(state.tree, parser.parse(state.doc))
|
||||
})
|
||||
|
||||
it("properly re-parses a continued list", () => {
|
||||
let state = State.start(`
|
||||
One paragraph to create a bit of string length here
|
||||
|
||||
* List
|
||||
|
||||
|
||||
|
||||
More content
|
||||
|
||||
Another paragraph that is long enough to create a fragment
|
||||
`).update([{from: 65, insert: " * "}])
|
||||
compareTree(state.tree, parser.parse(state.doc))
|
||||
})
|
||||
|
||||
it("can recover from incremental parses that stop in the middle of a list", () => {
|
||||
let doc = `
|
||||
1. I am a list item with ***some* emphasized
|
||||
content inside** and the parser hopefully stops
|
||||
parsing after me.
|
||||
|
||||
2. Oh no the list continues.
|
||||
`
|
||||
let parse = parser.startParse(doc), tree
|
||||
parse.advance()
|
||||
ist(parse.parsedPos, doc.length, "<")
|
||||
parse.stopAt(parse.parsedPos)
|
||||
while (!(tree = parse.advance())) {}
|
||||
let state = new State(doc, tree, TreeFragment.addTree(tree)).update([])
|
||||
ist(state.tree.topNode.lastChild!.from, 1)
|
||||
})
|
||||
|
||||
it("can reuse list items", () => {
|
||||
let start = State.start(" - List item\n".repeat(100))
|
||||
let state = start.update([{from: 18, to: 19}])
|
||||
ist(overlap(start.tree, state.tree), 80, ">")
|
||||
})
|
||||
|
||||
it("returns a tree starting at the first range", () => {
|
||||
let result = parser.parse("foo\n\nbar", [], [{from: 5, to: 8}])
|
||||
ist(result.toString(), "Document(Paragraph)")
|
||||
ist(result.length, 3)
|
||||
ist(result.positions[0], 0)
|
||||
})
|
||||
|
||||
it("Allows gaps in the input", () => {
|
||||
let doc = `
|
||||
The first X *y* X<
|
||||
|
||||
>X paragraph.
|
||||
|
||||
- And *a X<*>X list*
|
||||
`
|
||||
let tree = parser.parse(doc, [], [{from: 0, to: 11}, {from: 12, to: 17}, {from: 23, to: 46}, {from: 51, to: 58}])
|
||||
ist(tree.toString(),
|
||||
"Document(Paragraph(Emphasis(EmphasisMark,EmphasisMark)),BulletList(ListItem(ListMark,Paragraph(Emphasis(EmphasisMark,EmphasisMark)))))")
|
||||
ist(tree.length, doc.length)
|
||||
let top = tree.topNode, first = top.firstChild!
|
||||
ist(first.name, "Paragraph")
|
||||
ist(first.from, 1)
|
||||
ist(first.to, 34)
|
||||
let last = top.lastChild!.lastChild!.lastChild!, em = last.lastChild!
|
||||
ist(last.name, "Paragraph")
|
||||
ist(last.from, 39)
|
||||
ist(last.to, 57)
|
||||
ist(em.name, "Emphasis")
|
||||
ist(em.from, 43)
|
||||
ist(em.to, 57)
|
||||
})
|
||||
|
||||
it("can reuse nodes at the end of the document", () => {
|
||||
let doc = `* List item
|
||||
|
||||
~~~js
|
||||
function foo() {
|
||||
return false
|
||||
}
|
||||
~~~
|
||||
`
|
||||
let tree = parser.parse(doc)
|
||||
let ins = 11
|
||||
let doc2 = doc.slice(0, ins) + "\n* " + doc.slice(ins)
|
||||
let fragments = TreeFragment.applyChanges(TreeFragment.addTree(tree), [{fromA: ins, toA: ins, fromB: ins, toB: ins + 3}])
|
||||
let tree2 = parser.parse(doc2, fragments)
|
||||
ist(tree2.topNode.lastChild!.tree, tree.topNode.lastChild!.tree)
|
||||
})
|
||||
|
||||
it("places reused nodes at the right position when there are gaps before them", () => {
|
||||
let doc = " {{}}\nb\n{{}}"
|
||||
let ast1 = parser.parse(doc, undefined, [{from: 0, to: 1}, {from: 5, to: 8}])
|
||||
let frag = TreeFragment.applyChanges(TreeFragment.addTree(ast1), [{fromA: 0, toA: 0, fromB: 0, toB: 1}])
|
||||
let ast2 = parser.parse(" " + doc, frag, [{from: 0, to: 2}, {from: 6, to: 9}])
|
||||
ist(ast2.toString(), "Document(Paragraph)")
|
||||
let p = ast2.topNode.firstChild!
|
||||
ist(p.from, 7)
|
||||
ist(p.to, 8)
|
||||
})
|
||||
})
|
||||
3568
frontend/node_modules/@lezer/markdown/test/test-markdown.ts
generated
vendored
Normal file
3568
frontend/node_modules/@lezer/markdown/test/test-markdown.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
86
frontend/node_modules/@lezer/markdown/test/test-nesting.ts
generated
vendored
Normal file
86
frontend/node_modules/@lezer/markdown/test/test-nesting.ts
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
import {Tree, NodeProp} from "@lezer/common"
|
||||
import {parser as html} from "@lezer/html"
|
||||
import ist from "ist"
|
||||
import {parser, parseCode} from "../dist/index.js"
|
||||
|
||||
let full = parser.configure(parseCode({
|
||||
codeParser(str) { return !str || str == "html" ? html : null },
|
||||
htmlParser: html.configure({dialect: "noMatch"})
|
||||
}))
|
||||
|
||||
function findMounts(tree: Tree) {
|
||||
let result = []
|
||||
for (let cur = tree.cursor(); cur.next();) {
|
||||
let mount = cur.tree?.prop(NodeProp.mounted)
|
||||
if (mount) result.push({at: cur.from, mount})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function test(doc: string, ...nest: [string, ...number[]][]) {
|
||||
return () => {
|
||||
let tree = full.parse(doc), mounts = findMounts(tree)
|
||||
ist(mounts.length, nest.length)
|
||||
nest.forEach(([repr, ...ranges], i) => {
|
||||
let {mount, at} = mounts[i]
|
||||
ist(mount.tree.toString(), "Document(" + repr + ")")
|
||||
ist(mount.overlay!.map(r => (r.from + at) + "," + (r.to + at)).join(), ranges.join())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
describe("Code parsing", () => {
|
||||
it("parses HTML blocks", test(`
|
||||
Paragraph
|
||||
|
||||
<div id=x>
|
||||
Hello & goodbye
|
||||
</div>`, ["Element(OpenTag(StartTag,TagName,Attribute(AttributeName,Is,UnquotedAttributeValue),EndTag),Text,EntityReference,Text,CloseTag(StartCloseTag,TagName,EndTag))", 12, 51]))
|
||||
|
||||
it("parses inline HTML", test(
|
||||
`Paragraph with <em>inline tags</em> in it.`,
|
||||
["Element(OpenTag(StartTag,TagName,EndTag))", 15, 19],
|
||||
["CloseTag(StartCloseTag,TagName,EndTag)", 30, 35]))
|
||||
|
||||
it("parses indented code", test(`
|
||||
Paragraph.
|
||||
|
||||
<!doctype html>
|
||||
Hi
|
||||
`, ["DoctypeDecl,Text", 17, 33, 37, 39]))
|
||||
|
||||
it("parses fenced code", test(`
|
||||
Okay
|
||||
|
||||
~~~
|
||||
<p>
|
||||
Hey
|
||||
</p>
|
||||
~~~`, ["Element(OpenTag(StartTag,TagName,EndTag),Text,CloseTag(StartCloseTag,TagName,EndTag))", 11, 25]))
|
||||
|
||||
it("allows gaps in fenced code", test(`
|
||||
- >~~~
|
||||
><!doctype html>
|
||||
>yay
|
||||
> ~~~`, ["DoctypeDecl,Text", 11, 27, 30, 33]))
|
||||
|
||||
it("passes fenced code info", test(`
|
||||
~~~html
|
||||
»
|
||||
~~~
|
||||
|
||||
~~~python
|
||||
False
|
||||
~~~`, ["EntityReference", 9, 16]))
|
||||
|
||||
it("can parse disjoint ranges", () => {
|
||||
let tree = parser.parse(`==foo\n==\n==ba==r\n==`, undefined,
|
||||
[{from: 2, to: 6}, {from: 8, to: 9}, {from: 11, to: 13}, {from: 15, to: 17}])
|
||||
ist(tree.toString(), "Document(Paragraph,Paragraph)")
|
||||
ist(tree.length, 15)
|
||||
ist(tree.topNode.firstChild!.from, 0)
|
||||
ist(tree.topNode.firstChild!.to, 3)
|
||||
ist(tree.topNode.lastChild!.from, 9)
|
||||
ist(tree.topNode.lastChild!.to, 14)
|
||||
})
|
||||
})
|
||||
12
frontend/node_modules/@lezer/markdown/test/tsconfig.json
generated
vendored
Normal file
12
frontend/node_modules/@lezer/markdown/test/tsconfig.json
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["es2017"],
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"strict": true,
|
||||
"target": "es6",
|
||||
"newLine": "lf",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user