UNPKG

katex

Version:

Fast math typesetting for the web.

517 lines (508 loc) 14.3 kB
import {NON_ATOMS} from "./symbols"; import type SourceLocation from "./SourceLocation"; import type {AlignSpec, ColSeparationType} from "./environments/array"; import type {Atom} from "./symbols"; import type {Mode, StyleStr} from "./types"; import type {Token} from "./Token"; import type {Measurement} from "./units"; export type NodeType = keyof ParseNodeTypes; export type ParseNode<TYPE extends NodeType> = ParseNodeTypes[TYPE]; // ParseNode's corresponding to Symbol `Group`s in symbols.js. export type SymbolParseNode = ParseNode<"atom"> | ParseNode<"accent-token"> | ParseNode<"mathord"> | ParseNode<"op-token"> | ParseNode<"spacing"> | ParseNode<"textord">; // ParseNode from `Parser.formatUnsupportedCmd` export type UnsupportedCmdParseNode = ParseNode<"color">; // Union of all possible `ParseNode<>` types. export type AnyParseNode = ParseNodeTypes[keyof ParseNodeTypes]; // Map from `NodeType` to the corresponding `ParseNode`. type ParseNodeTypes = { "array": { type: "array"; mode: Mode; loc?: SourceLocation | null | undefined; colSeparationType?: ColSeparationType; hskipBeforeAndAfter?: boolean; addJot?: boolean; cols?: AlignSpec[]; arraystretch: number; body: AnyParseNode[][]; // List of rows in the (2D) array. rowGaps: (Measurement | null | undefined)[]; hLinesBeforeRow: Array<boolean[]>; // Whether each row should be automatically numbered, or an explicit tag tags?: (boolean | AnyParseNode[])[]; leqno?: boolean; isCD?: boolean; }; "cdlabel": { type: "cdlabel"; mode: Mode; loc?: SourceLocation | null | undefined; side: string; label: AnyParseNode; }; "cdlabelparent": { type: "cdlabelparent"; mode: Mode; loc?: SourceLocation | null | undefined; fragment: AnyParseNode; }; "color": { type: "color"; mode: Mode; loc?: SourceLocation | null | undefined; color: string; body: AnyParseNode[]; }; "color-token": { type: "color-token"; mode: Mode; loc?: SourceLocation | null | undefined; color: string; }; // To avoid requiring run-time type assertions, this more carefully captures // the requirements on the fields per the op.js htmlBuilder logic: // - `body` and `value` are NEVER set simultaneously. // - When `symbol` is true, `body` is set. "op": { type: "op"; mode: Mode; loc?: SourceLocation | null | undefined; limits: boolean; alwaysHandleSupSub?: boolean; suppressBaseShift?: boolean; parentIsSupSub: boolean; symbol: boolean; name: string; body?: void; } | { type: "op"; mode: Mode; loc?: SourceLocation | null | undefined; limits: boolean; alwaysHandleSupSub?: boolean; suppressBaseShift?: boolean; parentIsSupSub: boolean; symbol: false; // If 'symbol' is true, `body` *must* be set. name?: void; body: AnyParseNode[]; }; "ordgroup": { type: "ordgroup"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode[]; semisimple?: boolean; }; "raw": { type: "raw"; mode: Mode; loc?: SourceLocation | null | undefined; string: string; }; "size": { type: "size"; mode: Mode; loc?: SourceLocation | null | undefined; value: Measurement; isBlank: boolean; }; "styling": { type: "styling"; mode: Mode; loc?: SourceLocation | null | undefined; style: StyleStr; body: AnyParseNode[]; }; "supsub": { type: "supsub"; mode: Mode; loc?: SourceLocation | null | undefined; base: AnyParseNode | null | undefined; sup?: AnyParseNode | null | undefined; sub?: AnyParseNode | null | undefined; }; "tag": { type: "tag"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode[]; tag: AnyParseNode[]; }; "text": { type: "text"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode[]; font?: string; }; "url": { type: "url"; mode: Mode; loc?: SourceLocation | null | undefined; url: string; }; "verb": { type: "verb"; mode: Mode; loc?: SourceLocation | null | undefined; body: string; star: boolean; }; // From symbol groups, constructed in Parser.js via `symbols` lookup. // (Some of these have "-token" suffix to distinguish them from existing // `ParseNode` types.) "atom": { type: "atom"; family: Atom; mode: Mode; loc?: SourceLocation | null | undefined; text: string; }; "mathord": { type: "mathord"; mode: Mode; loc?: SourceLocation | null | undefined; text: string; }; "spacing": { type: "spacing"; mode: Mode; loc?: SourceLocation | null | undefined; text: string; }; "textord": { type: "textord"; mode: Mode; loc?: SourceLocation | null | undefined; text: string; }; // These "-token" types don't have corresponding HTML/MathML builders. "accent-token": { type: "accent-token"; mode: Mode; loc?: SourceLocation | null | undefined; text: string; }; "op-token": { type: "op-token"; mode: Mode; loc?: SourceLocation | null | undefined; text: string; }; // From functions.js and functions/*.js. See also "color", "op", "styling", // and "text" above. "accent": { type: "accent"; mode: Mode; loc?: SourceLocation | null | undefined; label: string; isStretchy?: boolean; isShifty?: boolean; base: AnyParseNode; }; "accentUnder": { type: "accentUnder"; mode: Mode; loc?: SourceLocation | null | undefined; label: string; isStretchy?: boolean; isShifty?: boolean; base: AnyParseNode; }; "cr": { type: "cr"; mode: Mode; loc?: SourceLocation | null | undefined; newLine: boolean; size: Measurement | null | undefined; }; "delimsizing": { type: "delimsizing"; mode: Mode; loc?: SourceLocation | null | undefined; size: 1 | 2 | 3 | 4; mclass: "mopen" | "mclose" | "mrel" | "mord"; delim: string; }; "enclose": { type: "enclose"; mode: Mode; loc?: SourceLocation | null | undefined; label: string; backgroundColor?: string; borderColor?: string; body: AnyParseNode; }; "environment": { type: "environment"; mode: Mode; loc?: SourceLocation | null | undefined; name: string; nameGroup: AnyParseNode; }; "font": { type: "font"; mode: Mode; loc?: SourceLocation | null | undefined; font: string; body: AnyParseNode; }; "genfrac": { type: "genfrac"; mode: Mode; loc?: SourceLocation | null | undefined; continued: boolean; numer: AnyParseNode; denom: AnyParseNode; hasBarLine: boolean; leftDelim: string | null | undefined; rightDelim: string | null | undefined; barSize: Measurement | null; }; "hbox": { type: "hbox"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode[]; }; "horizBrace": { type: "horizBrace"; mode: Mode; loc?: SourceLocation | null | undefined; label: string; isOver: boolean; base: AnyParseNode; }; "href": { type: "href"; mode: Mode; loc?: SourceLocation | null | undefined; href: string; body: AnyParseNode[]; }; "html": { type: "html"; mode: Mode; loc?: SourceLocation | null | undefined; attributes: Record<string, string>; body: AnyParseNode[]; }; "htmlmathml": { type: "htmlmathml"; mode: Mode; loc?: SourceLocation | null | undefined; html: AnyParseNode[]; mathml: AnyParseNode[]; }; "includegraphics": { type: "includegraphics"; mode: Mode; loc?: SourceLocation | null | undefined; alt: string; width: Measurement; height: Measurement; totalheight: Measurement; src: string; }; "infix": { type: "infix"; mode: Mode; loc?: SourceLocation | null | undefined; replaceWith: string; size?: Measurement; token: Token | null | undefined; }; "internal": { type: "internal"; mode: Mode; loc?: SourceLocation | null | undefined; }; "kern": { type: "kern"; mode: Mode; loc?: SourceLocation | null | undefined; dimension: Measurement; }; "lap": { type: "lap"; mode: Mode; loc?: SourceLocation | null | undefined; alignment: string; body: AnyParseNode; }; "leftright": { type: "leftright"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode[]; left: string; right: string; rightColor: string | null | undefined; // undefined means "inherit" }; "leftright-right": { type: "leftright-right"; mode: Mode; loc?: SourceLocation | null | undefined; delim: string; color: string | null | undefined; // undefined means "inherit" }; "mathchoice": { type: "mathchoice"; mode: Mode; loc?: SourceLocation | null | undefined; display: AnyParseNode[]; text: AnyParseNode[]; script: AnyParseNode[]; scriptscript: AnyParseNode[]; }; "middle": { type: "middle"; mode: Mode; loc?: SourceLocation | null | undefined; delim: string; }; "mclass": { type: "mclass"; mode: Mode; loc?: SourceLocation | null | undefined; mclass: string; body: AnyParseNode[]; isCharacterBox: boolean; }; "operatorname": { type: "operatorname"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode[]; alwaysHandleSupSub: boolean; limits: boolean; parentIsSupSub: boolean; }; "overline": { type: "overline"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode; }; "phantom": { type: "phantom"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode[]; }; "vphantom": { type: "vphantom"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode; }; "pmb": { type: "pmb"; mode: Mode; loc?: SourceLocation | null | undefined; mclass: string; body: AnyParseNode[]; }; "raisebox": { type: "raisebox"; mode: Mode; loc?: SourceLocation | null | undefined; dy: Measurement; body: AnyParseNode; }; "rule": { type: "rule"; mode: Mode; loc?: SourceLocation | null | undefined; shift: Measurement | null | undefined; width: Measurement; height: Measurement; }; "sizing": { type: "sizing"; mode: Mode; loc?: SourceLocation | null | undefined; size: number; body: AnyParseNode[]; }; "smash": { type: "smash"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode; smashHeight: boolean; smashDepth: boolean; }; "sqrt": { type: "sqrt"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode; index: AnyParseNode | null | undefined; }; "underline": { type: "underline"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode; }; "vcenter": { type: "vcenter"; mode: Mode; loc?: SourceLocation | null | undefined; body: AnyParseNode; }; "xArrow": { type: "xArrow"; mode: Mode; loc?: SourceLocation | null | undefined; label: string; body: AnyParseNode; below: AnyParseNode | null | undefined; }; }; /** * Asserts that the node is of the given type and returns it with stricter * typing. Throws if the node's type does not match. */ export function assertNodeType<NODETYPE extends NodeType>( node: AnyParseNode | null | undefined, type: NODETYPE, ): ParseNode<NODETYPE> { if (!node || node.type !== type) { throw new Error( `Expected node of type ${type}, but got ` + (node ? `node of type ${node.type}` : String(node))); } return node as ParseNode<NODETYPE>; } /** * Returns the node more strictly typed iff it is of the given type. Otherwise, * returns null. */ export function assertSymbolNodeType(node: AnyParseNode | null | undefined): SymbolParseNode { const typedNode = checkSymbolNodeType(node); if (!typedNode) { throw new Error( `Expected node of symbol group type, but got ` + (node ? `node of type ${node.type}` : String(node))); } return typedNode; } /** * Returns the node more strictly typed iff it is of the given type. Otherwise, * returns null. */ export function checkSymbolNodeType(node: AnyParseNode | null | undefined): SymbolParseNode | null | undefined { if (node && (node.type === "atom" || NON_ATOMS.hasOwnProperty(node.type))) { return node as SymbolParseNode; } return null; }