@unified-latex/unified-latex-util-replace
Version:
Functions for modifying a unified-latex AST
1 lines • 32.4 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","names":[],"sources":["../libs/replace-node.ts","../libs/utils/significant-node.ts","../libs/utils/join-without-excess-whitespace.ts","../libs/utils/wrap-significant-content.ts","../libs/utils/replace-streaming-command-in-array.ts","../libs/replace-streaming-command.ts","../libs/replace-node-during-visit.ts","../libs/unified-latex-streaming-command.ts","../index.ts"],"sourcesContent":["import * as Ast from \"@unified-latex/unified-latex-types\";\nimport { visit, VisitInfo } from \"@unified-latex/unified-latex-util-visit\";\n\n/**\n * Recursively replace nodes in `ast`. The `visitor` function is called on each node. If\n * `visitor` returns a node or an array of nodes, those nodes replace the node passed to `visitor`.\n * If `null` is returned, the node is deleted. If `undefined` is returned, no replacement happens.\n */\nexport function replaceNode(\n ast: Ast.Ast,\n visitor: (\n node: Ast.Node | Ast.Argument,\n info: VisitInfo\n ) =>\n | Ast.Node\n | Ast.Argument\n | (Ast.Node | Ast.Argument)[]\n | null\n | undefined\n | void\n) {\n visit(ast, {\n leave: (node, info) => {\n let replacement = visitor(node, info);\n // Returning `undefined` or the same node means we shouldn't replace that node\n if (typeof replacement === \"undefined\" || replacement === node) {\n return;\n }\n\n if (!info.containingArray || info.index == null) {\n throw new Error(\n \"Trying to replace node, but cannot find containing array\"\n );\n }\n\n if (\n replacement === null ||\n (Array.isArray(replacement) && replacement.length === 0)\n ) {\n // A null return means that we delete the current node\n info.containingArray.splice(info.index, 1);\n return info.index;\n }\n\n if (!Array.isArray(replacement)) {\n replacement = [replacement];\n }\n\n info.containingArray.splice(info.index, 1, ...replacement);\n // We don't want to *reprocess* the nodes we just inserted into the array,\n // lest we get stuck in a recursive loop if the replacement contains the original.\n // Thus we jump to the index after our replacements.\n return info.index + replacement.length;\n },\n });\n}\n","import * as Ast from \"@unified-latex/unified-latex-types\";\nimport { match } from \"@unified-latex/unified-latex-util-match\";\n\n/**\n * Returns the first non-whitespace/non-comment node in `nodes`. If there is no such\n * node, `null` is returned.\n */\nexport function firstSignificantNode(\n nodes: Ast.Node[],\n parbreaksAreInsignificant?: boolean\n): Ast.Node | null {\n const index = firstSignificantNodeIndex(nodes, parbreaksAreInsignificant);\n if (index == null) {\n return null;\n }\n return nodes[index];\n}\n\n/**\n * Returns the last non-whitespace/non-comment node in `nodes`. If there is no such\n * node, `null` is returned.\n */\nexport function lastSignificantNode(\n nodes: Ast.Node[],\n parbreaksAreInsignificant?: boolean\n): Ast.Node | null {\n const index = lastSignificantNodeIndex(nodes, parbreaksAreInsignificant);\n if (index == null) {\n return null;\n }\n return nodes[index];\n}\n\n/**\n * Returns the index of the last non-whitespace/non-comment node in `nodes`. If there is no such\n * node, `null` is returned.\n */\nexport function lastSignificantNodeIndex(\n nodes: Ast.Node[],\n parbreaksAreInsignificant?: boolean\n): number | undefined {\n for (let i = nodes.length - 1; i >= 0; i--) {\n const node = nodes[i];\n if (\n match.whitespace(node) ||\n match.comment(node) ||\n (parbreaksAreInsignificant && match.parbreak(node))\n ) {\n continue;\n }\n return i;\n }\n return undefined;\n}\n\n/**\n * Returns the index of the first non-whitespace/non-comment node in `nodes`. If there is no such\n * node, `null` is returned.\n */\nexport function firstSignificantNodeIndex(\n nodes: Ast.Node[],\n parbreaksAreInsignificant?: boolean\n): number | undefined {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n if (\n match.whitespace(node) ||\n match.comment(node) ||\n (parbreaksAreInsignificant && match.parbreak(node))\n ) {\n continue;\n }\n return i;\n }\n return undefined;\n}\n","import * as Ast from \"@unified-latex/unified-latex-types\";\nimport { match } from \"@unified-latex/unified-latex-util-match\";\nimport { trimStart } from \"@unified-latex/unified-latex-util-trim\";\n\n/**\n * Is the node space-like? I.e., is it whitespace or\n * a comment with leading whitespace?\n */\nfunction isSpaceLike(node: Ast.Node): boolean {\n return (\n match.whitespace(node) ||\n (match.comment(node) && Boolean(node.leadingWhitespace))\n );\n}\n\n/**\n * Similar to `head.push(...tail)` except that whitespace at the start\n * of `tail` and the end of `head` is collapsed.\n */\nexport function joinWithoutExcessWhitespace(\n head: Ast.Node[],\n tail: Ast.Node[]\n): void {\n if (tail.length === 0) {\n return;\n }\n if (head.length === 0) {\n head.push(...tail);\n return;\n }\n const headEnd = head[head.length - 1];\n const tailStart = tail[0];\n // Whitespace we can just trim off from either end\n if (match.whitespace(headEnd) && match.whitespace(tailStart)) {\n head.push(...tail.slice(1));\n return;\n }\n // If there's no whitespace at one of the ends, no need to worry\n // unless `tailStart` is a comment, in which case it should \"eat\"\n // the whitespace\n if (!isSpaceLike(headEnd) || !isSpaceLike(tailStart)) {\n if (match.whitespace(headEnd) && match.comment(tailStart)) {\n const comment: Ast.Comment = {\n type: \"comment\",\n content: tailStart.content,\n sameline: true,\n leadingWhitespace: true,\n };\n tail = tail.slice(1);\n trimStart(tail);\n head.pop();\n head.push(comment, ...tail);\n return;\n }\n head.push(...tail);\n return;\n }\n\n // If we're here, we have a comment with leading whitespace on one side\n // and whitespace/comments on the other.\n if (match.comment(headEnd) && match.comment(tailStart)) {\n if (tailStart.leadingWhitespace || tailStart.sameline) {\n head.push(\n { type: \"comment\", content: tailStart.content },\n ...tail.slice(1)\n );\n return;\n }\n head.push(...tail);\n return;\n }\n\n // Exactly one side is a comment, so we should trim the whitespace and keep the comment,\n // but make sure the comment has leading whitespace!\n let comment = match.comment(headEnd) ? headEnd : tailStart;\n if (!match.comment(comment)) {\n throw new Error(\n `Expected a comment but found ${JSON.stringify(comment)}`\n );\n }\n\n if (!comment.leadingWhitespace || !comment.sameline) {\n comment = {\n type: \"comment\",\n content: comment.content,\n leadingWhitespace: true,\n sameline: true,\n };\n }\n\n head.pop();\n head.push(comment, ...tail.slice(1));\n}\n","import * as Ast from \"@unified-latex/unified-latex-types\";\nimport { match } from \"@unified-latex/unified-latex-util-match\";\n\n/**\n * Wraps `content` in the specified wrapper. This command is roughly equivalent to\n * `wrapper(content)` except that leading and trailing whitespace and comments are extracted\n * from `content` and moved to the front or back of the return array. For example,\n * `[\" \", \"foo\", \"bar\", \"% xxx\"]` -> `[\" \", wrapped([\"foo\", \"bar\"]), \"% xxx\"]`.\n *\n */\nexport function wrapSignificantContent(\n content: Ast.Node[],\n wrapper: (content: Ast.Node[]) => Ast.Node[] | Ast.Node\n): Ast.Node[] {\n let hoistUntil = 0;\n let hoistAfter = content.length;\n for (let i = 0; i < content.length; i++) {\n if (match.whitespace(content[i]) || match.comment(content[i])) {\n hoistUntil = i + 1;\n continue;\n }\n break;\n }\n for (let j = content.length - 1; j >= 0; j--) {\n if (match.whitespace(content[j]) || match.comment(content[j])) {\n hoistAfter = j;\n continue;\n }\n break;\n }\n\n if (hoistUntil === 0 && hoistAfter === content.length) {\n return ensureArray(wrapper(content));\n }\n\n const frontMatter = content.slice(0, hoistUntil);\n const middle = content.slice(hoistUntil, hoistAfter);\n const backMatter = content.slice(hoistAfter, content.length);\n\n return frontMatter.concat(wrapper(middle), backMatter);\n}\n\nfunction ensureArray(x: Ast.Node | Ast.Node[]) {\n if (!Array.isArray(x)) {\n return [x];\n }\n return x;\n}\n","import * as Ast from \"@unified-latex/unified-latex-types\";\nimport { trimEnd, trimStart } from \"@unified-latex/unified-latex-util-trim\";\nimport { joinWithoutExcessWhitespace } from \"./join-without-excess-whitespace\";\nimport { wrapSignificantContent } from \"./wrap-significant-content\";\n\n/**\n * Replace commands identified by `isStreamingCommand` with the return value of `replacer`.\n * E.g., the array `[head, streamingCommand, ...tail]` will become `[head, replacer(tail, streamingCommand)]`.\n * This function does not split based on parbreaks/etc.. It is right-associative and returns\n * the streaming commands that were encountered.\n */\nexport function replaceStreamingCommandInArray(\n nodes: Ast.Node[],\n isStreamingCommand: (node: any) => node is Ast.Macro,\n replacer: (\n content: Ast.Node[],\n streamingCommand: Ast.Macro\n ) => Ast.Node | Ast.Node[]\n): { foundStreamingCommands: Ast.Node[] } {\n // Streaming commands that come at the end don't do anything,\n // so we should remove them\n while (nodes.length > 0 && isStreamingCommand(nodes[nodes.length - 1])) {\n nodes.pop();\n trimEnd(nodes);\n }\n\n const foundStreamingCommands: Ast.Node[] = [];\n\n for (let i = nodes.length - 1; i >= 0; i--) {\n const node = nodes[i];\n if (isStreamingCommand(node)) {\n const wrapper = (content: Ast.Node[]) => replacer(content, node);\n let tail = nodes.slice(i + 1);\n // Streaming commands are followed by whitespace, which becomes unneeded when the commands are replaced.\n trimStart(tail);\n tail = wrapSignificantContent(tail, wrapper);\n foundStreamingCommands.push(node);\n\n // Trim off what we're about to replace!\n nodes.splice(i);\n\n joinWithoutExcessWhitespace(nodes, tail);\n }\n }\n\n return { foundStreamingCommands };\n}\n","import * as Ast from \"@unified-latex/unified-latex-types\";\nimport { match } from \"@unified-latex/unified-latex-util-match\";\nimport {\n splitOnCondition,\n unsplitOnMacro,\n} from \"@unified-latex/unified-latex-util-split\";\nimport {\n trim,\n trimEnd,\n trimStart,\n} from \"@unified-latex/unified-latex-util-trim\";\nimport { firstSignificantNode } from \"./utils/significant-node\";\nimport { replaceStreamingCommandInArray } from \"./utils/replace-streaming-command-in-array\";\nimport { wrapSignificantContent } from \"./utils/wrap-significant-content\";\n\ntype Replacer = (nodes: Ast.Node[]) => Ast.Node[];\n\n/**\n * Process streaming commands in a group. If needed, \"escape\" the group.\n * For example, `{\\bfseries xx}` -> `\\textbf{xx}`, but `{foo \\bfseries xx}` -> `{foo \\textbf{xx}}`.\n */\nexport function replaceStreamingCommandInGroup(\n group: Ast.Group,\n isStreamingCommand: (node: any) => node is Ast.Macro,\n replacer: (\n content: Ast.Node[],\n streamingCommand: Ast.Macro\n ) => Ast.Node | Ast.Node[],\n options?: {\n macrosThatBreakPars?: string[];\n environmentsThatDontBreakPars?: string[];\n }\n): Ast.Node[] {\n const content = group.content;\n // If the group started with a streaming command, we want to pop\n // out of the group. E.g. `{\\bfseries foo}` -> `\\textbf{foo}` and not `{\\textbf{foo}}`\n let popFromGroup = isStreamingCommand(firstSignificantNode(content));\n\n let innerProcessed = replaceStreamingCommand(\n content,\n isStreamingCommand,\n replacer,\n options\n );\n\n // If the group consisted of just streaming commands (for some reason...)\n // it should be eliminated\n if (innerProcessed.length === 0) {\n return [];\n }\n\n if (popFromGroup) {\n return innerProcessed;\n } else {\n return [{ type: \"group\", content: innerProcessed }];\n }\n}\n\n/**\n * Given a group or a node array, look for streaming commands (e.g., `\\bfseries`) and replace them\n * with the specified macro. The \"arguments\" of the streaming command are passed to `replacer` and the return\n * value of `replacer` is inserted into the stream.\n *\n * By default, this command will split at parbreaks (since commands like `\\textbf{...} do not accept parbreaks in their\n * contents) and call `replacer` multiple times, once per paragraph.\n *\n * Commands are also split at environments and at any macros listed in `macrosThatBreakPars`.\n */\nexport function replaceStreamingCommand(\n ast: Ast.Group | Ast.Node[],\n isStreamingCommand: (node: any) => node is Ast.Macro,\n replacer: (\n content: Ast.Node[],\n streamingCommand: Ast.Macro\n ) => Ast.Node | Ast.Node[],\n options?: {\n macrosThatBreakPars?: string[];\n environmentsThatDontBreakPars?: string[];\n }\n): Ast.Node[] {\n if (typeof isStreamingCommand !== \"function\") {\n throw new Error(\n `'isStreamingCommand' must be a function, not '${typeof isStreamingCommand}'`\n );\n }\n\n const {\n macrosThatBreakPars = [\n \"part\",\n \"chapter\",\n \"section\",\n \"subsection\",\n \"subsubsection\",\n \"vspace\",\n \"smallskip\",\n \"medskip\",\n \"bigskip\",\n \"hfill\",\n ],\n environmentsThatDontBreakPars = [],\n } = options || {};\n\n let processedContent: Ast.Node[] = [];\n if (match.group(ast)) {\n processedContent = replaceStreamingCommandInGroup(\n ast,\n isStreamingCommand,\n replacer\n );\n }\n\n if (Array.isArray(ast)) {\n // Streaming commands that come at the end of a sequence of nodes don't do anything.\n // They also will consume whitespace, so we should remove them and the whitespace.\n const nodes = ast;\n let scanIndex = nodes.length;\n let sliceIndex = scanIndex;\n while (\n scanIndex > 0 &&\n (isStreamingCommand(nodes[scanIndex - 1]) ||\n match.whitespace(nodes[scanIndex - 1]))\n ) {\n scanIndex--;\n if (isStreamingCommand(nodes[scanIndex])) {\n sliceIndex = scanIndex;\n }\n }\n if (sliceIndex !== nodes.length) {\n nodes.splice(sliceIndex);\n }\n\n const macroThatBreaks = match.createMacroMatcher(macrosThatBreakPars);\n const envThatDoesntBreak = match.createEnvironmentMatcher(\n environmentsThatDontBreakPars\n );\n // A \"par\" is anything that a streaming command shouldn't wrap and breaks the stream.\n // This includes regular pars, but also environments and special macros like \\section\n const isPar = (node: Ast.Node) =>\n match.parbreak(node) ||\n match.macro(node, \"par\") ||\n macroThatBreaks(node) ||\n (match.environment(node) && !envThatDoesntBreak(node)) ||\n node.type === \"displaymath\";\n\n // We split on both a parbreak and a literal `\\par`. But we will\n // normalize everything to be parbreaks\n const splitByPar = splitOnCondition(nodes, isPar);\n splitByPar.separators = splitByPar.separators.map((sep) =>\n match.macro(sep, \"par\") ? { type: \"parbreak\" } : sep\n );\n\n const replacers: Replacer[] = [];\n let segments = splitByPar.segments.map((segment) => {\n if (segment.length === 0) {\n return segment;\n }\n function applyAccumulatedReplacers(nodes: Ast.Node[]): Ast.Node[] {\n if (replacers.length === 0) {\n return nodes;\n }\n return wrapSignificantContent(\n nodes,\n composeReplacers(replacers)\n );\n }\n\n const { foundStreamingCommands } = replaceStreamingCommandInArray(\n segment,\n isStreamingCommand,\n replacer\n );\n\n // All streaming commands in `segment` have now been replaced. However,\n // there might be commands from the previous paragraphs that should wrap\n // the current segment!\n const ret = applyAccumulatedReplacers(segment);\n\n // Any streaming commands from this segment will carry over to the next,\n // so keep track of them.\n foundStreamingCommands.forEach((macro) => {\n replacers.push((nodes: Ast.Node[]) => {\n const ret = replacer(nodes, macro as Ast.Macro);\n if (!Array.isArray(ret)) {\n return [ret];\n }\n return ret;\n });\n });\n\n return ret;\n });\n\n // Leading/trailing whitespace was hoisted in front/back of each replacer.\n // Since we're separated by parbreaks, we can safely trim all that whitespace.\n if (segments.length > 1) {\n segments.forEach((segment, i) => {\n if (i === 0) {\n trimEnd(segment);\n } else if (i === segments.length - 1) {\n trimStart(segment);\n } else {\n trim(segment);\n }\n });\n }\n\n processedContent = unsplitOnMacro({\n segments: segments,\n macros: splitByPar.separators,\n });\n }\n\n return processedContent;\n}\n\n/**\n * Given a sequence of replacer functions `[f, g, h]` return\n * `h \\circ g \\circ f`\n *\n * @param {((nodes: Ast.Node[]) => Ast.Node)[]} replacers\n * @returns {(nodes: Ast.Node[]) => Ast.Node}\n */\nfunction composeReplacers(replacers: Replacer[]): Replacer {\n if (replacers.length === 0) {\n throw new Error(\"Cannot compose zero replacement functions\");\n }\n return (nodes: Ast.Node[]) => {\n let ret = nodes;\n for (let i = 0; i < replacers.length; i++) {\n const func = replacers[i];\n ret = func(ret);\n }\n return ret;\n };\n}\n","import { VisitInfo } from \"@unified-latex/unified-latex-util-visit\";\nimport * as Ast from \"@unified-latex/unified-latex-types\";\n\n/**\n * Replaces the current node with `replacement`. It is assumed that the current\n * node is in an array that is a child of a parent element. If this is not the case,\n * the function will error.\n */\nexport function replaceNodeDuringVisit(\n replacement: Ast.Node | Ast.Argument | (Ast.Node | Ast.Argument)[],\n info: VisitInfo\n) {\n const parent = info.parents[0];\n if (!parent) {\n throw new Error(`Cannot replace node: parent not found`);\n }\n const container = parent[info.key as keyof typeof parent] as\n | (Ast.Node | Ast.Argument)[]\n | undefined;\n if (!Array.isArray(container)) {\n throw new Error(`Cannot replace node: containing array not found`);\n }\n if (info.index == null) {\n throw new Error(`Cannot replace node: node index undefined`);\n }\n if (!Array.isArray(replacement)) {\n container[info.index] = replacement;\n } else {\n container.splice(info.index, 1, ...replacement);\n }\n}\n","import { Plugin } from \"unified\";\nimport * as Ast from \"@unified-latex/unified-latex-types\";\nimport { match } from \"@unified-latex/unified-latex-util-match\";\nimport { trimEnd, trimStart } from \"@unified-latex/unified-latex-util-trim\";\nimport { visit } from \"@unified-latex/unified-latex-util-visit\";\nimport { replaceStreamingCommand } from \"./replace-streaming-command\";\nimport { replaceNodeDuringVisit } from \"./replace-node-during-visit\";\n\ntype PluginOptions = {\n replacers: Record<\n string,\n (\n content: Ast.Node[],\n streamingCommand: Ast.Macro\n ) => Ast.Node | Ast.Node[]\n >;\n};\n\n/**\n * Unified plugin to replace all found streaming commands with their argument-style equivalents.\n * This only applies to sections of the tree with no math ancestor.\n *\n * @param options.replacer A record of macro names and replacer functions. A replacer function accepts content and the original streaming command and is expected to return the argument-style command. It may be called multiple times per streaming command.\n */\nexport const unifiedLatexReplaceStreamingCommands: Plugin<\n PluginOptions[],\n Ast.Root,\n Ast.Root\n> = function unifiedLatexReplaceStreamingCommands(options) {\n const { replacers = {} } = options || {};\n const isReplaceable = match.createMacroMatcher(replacers);\n return (tree) => {\n // We traverse all groups before we replace nodes in other contexts\n visit(\n tree,\n (group, info) => {\n if (\n info.context.hasMathModeAncestor ||\n !group.content.some(isReplaceable)\n ) {\n return;\n }\n\n let fixed = replaceStreamingCommand(\n group,\n isReplaceable,\n (content, command) => {\n return replacers[command.content](content, command);\n }\n );\n\n // We cannot replace the node unless we can access the containing array.\n if (!info.containingArray || info.index == null) {\n return;\n }\n\n // `fixed` may consist of only whitespace. If this is the case,\n // surrounding whitespace must trimmed before\n // inserting the group's contents.\n const prevToken = info.containingArray[info.index - 1];\n const nextToken = info.containingArray[info.index + 1];\n if (\n match.whitespaceLike(prevToken) &&\n match.whitespaceLike(fixed[0])\n ) {\n trimStart(fixed);\n }\n if (\n match.whitespaceLike(nextToken) &&\n match.whitespaceLike(fixed[fixed.length - 1])\n ) {\n trimEnd(fixed);\n }\n replaceNodeDuringVisit(fixed, info);\n },\n { test: match.group }\n );\n\n visit(\n tree,\n (nodes, info) => {\n if (\n info.context.hasMathModeAncestor ||\n !nodes.some(isReplaceable)\n ) {\n return;\n }\n\n const replaced = replaceStreamingCommand(\n nodes,\n isReplaceable,\n (content, command) => {\n return replacers[command.content](content, command);\n }\n );\n\n // If we get back a different array than we input, the replacement did\n // not happen in-place. In this case we need to manipulate `nodes`.\n if (replaced !== nodes) {\n nodes.length = 0;\n nodes.push(...replaced);\n }\n },\n { includeArrays: true, test: Array.isArray }\n );\n };\n};\n","export * from \"./libs/replace-node\";\nexport * from \"./libs/unified-latex-streaming-command\";\nexport * from \"./libs/replace-streaming-command\";\nexport * from \"./libs/replace-node-during-visit\";\nexport * from \"./libs/utils/significant-node\";\n\n// NOTE: The docstring comment must be the last item in the index.ts file!\n/**\n * ## What is this?\n *\n * Functions to help modify a `unified-latex` Abstract Syntax Tree (AST).\n *\n * ## When should I use this?\n *\n * If you want to recursively replace particular AST nodes.\n */\n"],"mappings":"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"}