import { Node } from "prosemirror-model"; import { ellipsifyEnd, ellipsifyStart } from "./ellipsify"; import { mappableTextBetween } from "./mappableTextBetween"; /** * A container for holding a snippet of text with before and after text to give * context. This is useful for taking a snapshot of tagged content to store and * present to the user. */ export class PlainSingleLine { constructor(public readonly textBefore: string, public readonly text: string, public readonly textAfter: string) {} public eq(other: PlainSingleLine): boolean { return this.textBefore === other.textBefore && this.text === other.text && this.textAfter === other.textAfter; } } export interface EncodeToPlainSingleLineConstraints { textAfter: number; textBefore: number; text: number; } /** * Encode a range of a ProseMirror node to text. * * "Plain single line" refers to the formatting choices that have been made with * this function: * * - No new lines are included. * - Machine-readable formatting (e.g. HTML) is not used. * - Blocks are flattened into adjacent space-separated text. * */ export function encodeToPlainSingleLine( node: Node, options: { /** * from→to range in the node to encode. If omitted the entire node will be * encoded. Out-of-range values are constrained to the size of the node, and * `constraints` limits are enforced. */ range?: [number, number]; /** * Text-length constraints to apply for each segment of text. If `text` is * exceeded, `textAfter` will be empty. */ constraints?: EncodeToPlainSingleLineConstraints; } = {} ): PlainSingleLine { const rangeMin = 0; const rangeMax = node.nodeSize - 2; const { range = [rangeMin, rangeMax], constraints } = options; const safeRange = [Math.max(range[0], rangeMin), Math.min(range[1], rangeMax)]; const textRaw = textBetween(node, safeRange[0], safeRange[1]); const text = constraints !== undefined ? textRaw.slice(0, constraints.text) : textRaw; const textBeforeRaw = textBetween(node, rangeMin, safeRange[0]); const textBefore = constraints !== undefined ? ellipsifyStart(textBeforeRaw, constraints.textBefore) : textBeforeRaw; const textAfterRaw = textRaw.length > text.length ? "" : textBetween(node, safeRange[1], rangeMax); const textAfter = constraints !== undefined ? ellipsifyEnd(textAfterRaw, constraints.textAfter) : textAfterRaw; return new PlainSingleLine(textBefore, text, textAfter); } function textBetween(node: Node, from: number, to: number): string { return mappableTextBetween(node, from, to, { block: " ", leaf: " " }).text; }