shared/selectors/document.selector.ts

shared/selectors/document.selector.ts is a file in GovLab Extension Host. 92 lines of code and 29 definitions.

import type { DocumentText } from "../../types/writing.types.ts";

const LINE = "\n";
const FENCE = "```";
const FRONTMATTER = "---";
const CODE_MARK = "`";
const CODE_WORD = "code";
const CELL = "|";
const RULE_CELL_CHARS: ReadonlySet<string> = new Set(["-", ":", " "]);
const SPACE = " ";
const CODE_KEY = "code";
const POINTER = "/";
const LOCATION = ":";
const FRAGMENT = "#";

export const withoutCode = function withoutCode(text: string): string {
    let output = "";
    let inside = false;
    for (const char of text) {
        if (char === CODE_MARK) {
            output += inside ? "" : CODE_WORD;
            inside = !inside;
            continue;
        }
        output += inside ? "" : char;
    }
    return output;
};

const isRuleCell = function isRuleCell(cell: string): boolean {
    for (let at = 0; at < cell.length; at += 1) {
        if (!RULE_CELL_CHARS.has(cell.charAt(at))) {
            return false;
        }
    }
    return true;
};

const lineTexts = function lineTexts(line: string): readonly string[] {
    const prose = withoutCode(line).trim();
    if (!prose.startsWith(CELL)) {
        return [prose];
    }
    return prose
        .split(CELL)
        .map((cell) => cell.trim())
        .filter((cell) => cell.length > 0 && !isRuleCell(cell));
};

const frontmatterEnd = function frontmatterEnd(lines: readonly string[]): number {
    return lines[0]?.trim() === FRONTMATTER ? lines.indexOf(FRONTMATTER, 1) + 1 : 0;
};

export const markdownTexts = function markdownTexts(label: string, content: string): DocumentText[] {
    const lines = content.split(LINE).map((line) => line.trimEnd());
    const start = frontmatterEnd(lines);
    const texts: DocumentText[] = [];
    let fenced = false;
    for (const [index, line] of lines.entries()) {
        const body = index >= start;
        const fence = body && line.trimStart().startsWith(FENCE);
        fenced = fence ? !fenced : fenced;
        if (body && !fence && !fenced && line.trim().length > 0) {
            const at = `${label}${LOCATION}${String(index + 1)}`;
            texts.push(...lineTexts(line).map((text) => ({ at, text })));
        }
    }
    return texts;
};

const unfencedLines = function unfencedLines(value: string): string[] {
    const lines: string[] = [];
    let fenced = false;
    for (const line of value.split(LINE)) {
        const fence = line.trimStart().startsWith(FENCE);
        fenced = fence ? !fenced : fenced;
        if (!fence && !fenced) {
            lines.push(line);
        }
    }
    return lines;
};

export const jsonTexts = function jsonTexts(label: string, value: unknown, pointer = ""): DocumentText[] {
    if (typeof value === "string") {
        const at = `${label}${FRAGMENT}${pointer}`;
        return unfencedLines(value)
            .filter((line) => line.includes(SPACE))
            .map((line) => ({ at, text: withoutCode(line) }));
    }
    if (Array.isArray(value)) {
        return value.flatMap((item, index) => jsonTexts(label, item, `${pointer}${POINTER}${String(index)}`));
    }
    if (typeof value !== "object" || value === null) {
        return [];
    }
    return Object.entries(value).flatMap(([key, item]) =>
        key === CODE_KEY ? [] : jsonTexts(label, item, `${pointer}${POINTER}${key}`),
    );
};