From ac6ba5b97a56560610bdd1c98640ffe286c73929 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Sun, 20 Oct 2024 15:25:18 +0200 Subject: [PATCH] type: define precise type for Event.annot This enables API users to identify the events returned by `parseEvents` in a type-safe manner. --- src/attributes.ts | 15 +++++++++- src/block.ts | 63 +++++++++++++++++++++++++++++---------- src/event.ts | 8 ++++- src/inline.ts | 75 +++++++++++++++++++++++++++++++++++++++-------- src/parse.ts | 2 +- 5 files changed, 131 insertions(+), 32 deletions(-) diff --git a/src/attributes.ts b/src/attributes.ts index 5abb818..6005ead 100644 --- a/src/attributes.ts +++ b/src/attributes.ts @@ -246,6 +246,19 @@ handlers[State.SCANNING_QUOTED_VALUE_CONTINUATION] = function(parser : Attribute } } +export type AttrAnnot = + | "attr_class_marker" + | "attr_equal_marker" + | "attr_id_marker" + | "attr_quote_marker" + | "attr_space" + | "class" + | "comment" + | "id" + | "key" + | "value" + ; + class AttributeParser { subject : string; state : State; @@ -261,7 +274,7 @@ class AttributeParser { this.matches = [] } - addEvent(startpos : number, endpos : number, annot : string) { + addEvent(startpos : number, endpos : number, annot : AttrAnnot) { this.matches.push({ startpos: startpos, endpos: endpos, annot: annot }); } diff --git a/src/block.ts b/src/block.ts index 3acbd67..f86a3ec 100644 --- a/src/block.ts +++ b/src/block.ts @@ -1,8 +1,8 @@ -import { Event } from "./event"; +import { Annot, Event } from "./event"; import { Options, Warning } from "./options"; -import { AttributeParser } from "./attributes"; +import { AttrAnnot, AttributeParser } from "./attributes"; import { pattern, find } from "./find"; -import { InlineParser } from "./inline"; +import { InlineAnnot, InlineParser } from "./inline"; // Return array of list styles that match a marker. // In ambiguous cases we return multiple values. @@ -59,7 +59,7 @@ const pattListMarker = pattern("(:?[-*+:]|\\([0-9]+\\)|[0-9]+[.)]|[ivxlcdmIVXLCD const pattTaskListMarker = pattern("[*+-] \\[[Xx ]\\][ \\t\\r\\n]"); type EventIterator = { - next: () => { value: Event, done: boolean }; + next: () => { done: false, value: Event } | { done: true, value: Omit }; } enum ContentType { @@ -106,6 +106,45 @@ class Container { } } +export type BlockAnnot = + | `+${BlockType}` + | `-${BlockType}` + | `+list|${string}` + | `+list_item|${string}` + | "-list" + | "-list_item" + | "blankline" + | "checkbox_checked" + | "checkbox_unchecked" + | "class" + | "code_language" + | "note_label" + | "raw_format" + | "reference_key" + | "reference_value" + | "separator_center" + | "separator_default" + | "separator_left" + | "separator_right" + | "str" + | "thematic_break" + ; + +type BlockType = + | "block_attributes" + | "block_quote" + | "caption" + | "cell" + | "code_block" + | "div" + | "footnote" + | "heading" + | "para" + | "reference_definition" + | "row" + | "table" + ; + class EventParser { options: Options; warn: (warning : Warning) => void; @@ -422,11 +461,7 @@ class EventParser { const data = { styles: styles, indent: this.indent }; // adding container will close others this.addContainer(new Container(spec, data)); - let annot = "+list"; - for (const style of styles) { - annot = annot + "|" + style; - } - this.addMatch(sp, ep - 1, annot); + this.addMatch(sp, ep - 1, `+list|${styles.join("|")}`); return true; }, close: () => { @@ -467,11 +502,7 @@ class EventParser { const data = { styles: styles, indent: this.indent }; // adding container will close others this.addContainer(new Container(spec, data)); - let annot = "+list_item"; - for (const style of styles) { - annot = annot + "|" + style; - } - this.addMatch(sp, ep - 1, annot); + this.addMatch(sp, ep - 1, `+list_item|${styles.join("|")}`); this.pos = ep; if (checkbox) { @@ -752,7 +783,7 @@ class EventParser { } } - addMatch(startpos: number, endpos: number, annot: string): void { + addMatch(startpos: number, endpos: number, annot: Annot): void { this.matches.push({ startpos: Math.min(startpos, this.maxoffset), endpos: Math.min(endpos, this.maxoffset), @@ -884,7 +915,7 @@ class EventParser { const m = find(this.subject, pattRowSep, p); if (m !== null) { const [left, right, trailing] = m.captures; - let st = "separator_default"; + let st: BlockAnnot = "separator_default"; if (left.length > 0 && right.length > 0) { st = "separator_center"; } else if (right.length > 0) { diff --git a/src/event.ts b/src/event.ts index 9f4fa8b..f3c831a 100644 --- a/src/event.ts +++ b/src/event.ts @@ -1,7 +1,13 @@ +import { AttrAnnot } from "./attributes"; +import { BlockAnnot } from "./block"; +import { InlineAnnot } from "./inline"; + +export type Annot = BlockAnnot | InlineAnnot | AttrAnnot; + interface Event { startpos : number; endpos : number; - annot : string; + annot : Annot; } diff --git a/src/inline.ts b/src/inline.ts index a8085d1..5dd46a6 100644 --- a/src/inline.ts +++ b/src/inline.ts @@ -103,9 +103,9 @@ const alwaysTrue = function() { return true; }; const betweenMatched = function( c: string, - annotation: string, - defaultmatchFirst: string, - defaultmatchAlt: string | null, + annotation: InlineType, + defaultmatchFirst: InlineAnnot, + defaultmatchAlt: InlineAnnot | null, opentest: ((self: InlineParser, pos: number) => boolean)) { let defaultmatch = defaultmatchFirst; @@ -149,9 +149,9 @@ const betweenMatched = function( const opener = openers[openers.length - 1]; if (opener.endpos !== pos - 1) { // exclude empty emph self.clearOpeners(opener.startpos, pos); - self.addMatch(opener.startpos, opener.endpos, "+" + annotation, + self.addMatch(opener.startpos, opener.endpos, `+${annotation}`, opener.matchIndex); - self.addMatch(pos, endcloser, "-" + annotation); + self.addMatch(pos, endcloser, `-${annotation}`); return endcloser + 1; } } @@ -526,6 +526,56 @@ const matchers = { } }; +export type InlineAnnot = + | `+${InlineType}` + | `-${InlineType}` + | "ellipses" + | "em_dash" + | "en_dash" + | "escape" + | "footnote_reference" + | "hard_break" + | "image_marker" + | "left_double_quote" + | "left_single_quote" + | "non_breaking_space" + | "open_marker" + | "raw_format" + | "right_double_quote" + | "right_single_quote" + | "soft_break" + | "str" + | "symb" + ; + +type InlineType = + | VerbatimType + | "attributes" + | "delete" + | "destination" + | "display_math" + | "double_quoted" + | "email" + | "emph" + | "imagetext" + | "insert" + | "linktext" + | "mark" + | "reference" + | "single_quoted" + | "span" + | "strong" + | "subscript" + | "superscript" + | "url" + ; + +type VerbatimType = + | "display_math" + | "inline_math" + | "verbatim" + ; + class InlineParser { options: Options; warn: (warning : Warning) => void; @@ -533,7 +583,7 @@ class InlineParser { matches: Event[]; openers: OpenerMap; // map from opener type to Opener[] in reverse order verbatim: number; // parsing a verbatim span to be ended by N backticks - verbatimType: string; // math or regular + verbatimType?: VerbatimType; // math or regular destination: boolean; // parsing link destination? firstpos: number; // position of first slice lastpos: number; // position of last slice @@ -551,7 +601,6 @@ class InlineParser { this.matches = []; // array of matches this.openers = {}; this.verbatim = 0; - this.verbatimType = ""; this.destination = false; this.firstpos = -1; this.lastpos = 0; @@ -563,7 +612,7 @@ class InlineParser { } addMatch(startpos: number, endpos: number, - annot: string, matchIndex?: number): void { + annot: InlineAnnot, matchIndex?: number): void { const match = { startpos: startpos, endpos: endpos, annot: annot }; if (matchIndex !== undefined) { // insert into the proper place, replacing placeholder @@ -629,7 +678,7 @@ class InlineParser { this.matches.push({ startpos: last.endpos, endpos: last.endpos, - annot: "-" + this.verbatimType + annot: `-${this.verbatimType!}` }); } return this.matches; @@ -638,7 +687,7 @@ class InlineParser { addOpener(name: string, startpos: number, endpos: number, - defaultAnnot: string) : void { + defaultAnnot: InlineAnnot) : void { if (!this.openers[name]) { this.openers[name] = []; } @@ -727,7 +776,7 @@ class InlineParser { const attrMatches = this.attributeParser.matches; // add attribute matches for (const match of attrMatches) { - this.addMatch(match.startpos, match.endpos, match.annot); + this.matches.push(match); } this.addMatch(ep, ep, "-attributes"); // restore state to prior to adding attribute parser @@ -787,11 +836,11 @@ class InlineParser { // check for raw attribute const m2 = find(subject, pattRawAttribute, endchar + 1, endpos); if (m2 && this.verbatimType === "verbatim") { // raw - this.addMatch(pos, endchar, "-" + this.verbatimType); + this.addMatch(pos, endchar, `-${this.verbatimType}`); this.addMatch(m2.startpos, m2.endpos, "raw_format"); pos = m2.endpos + 1; } else { - this.addMatch(pos, endchar, "-" + this.verbatimType); + this.addMatch(pos, endchar, `-${this.verbatimType!}`); pos = endchar + 1; } this.verbatim = 0; diff --git a/src/parse.ts b/src/parse.ts index 273a57d..2552b83 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -1196,7 +1196,7 @@ const parse = function(input: string, options: ParseOptions = {}): Doc { ep = getSourceLoc(event.endpos); pos = { start: sp, end: ep }; } - let annot = event.annot; + let annot: string = event.annot; let suffixes: string[] = []; if (event.annot.includes("|")) { const parts = event.annot.split("|");