tools/core/transformers/segment.transformer.ts

tools/core/transformers/segment.transformer.ts is a file in Coordination Surface. 133 lines of code and 22 definitions.

import type { ApplyResult, Document, Edit, Inline, RenameMap, Segment } from "../types/segment.types.ts";

const FIELD_KINDS: ReadonlySet<Segment["kind"]> = new Set(["field", "frontmatter-field"]);

const FIELD_BOUNDARIES: readonly string[] = ["#", " "];

const INLINE_BOUNDARIES: readonly string[] = ["#"];

const prefixOf = function prefixOf(
    value: string,
    map: RenameMap,
    boundaries: readonly string[],
): readonly [string, string] | null {
    const found = Object.entries(map).find(
        ([from]) =>
            value.length > from.length && value.startsWith(from) && boundaries.includes(value.charAt(from.length)),
    );
    return found ?? null;
};

export const editsFromInlines = function editsFromInlines(
    document: Document,
    map: RenameMap,
    kinds: readonly Inline["kind"][],
): Edit[] {
    return document.segments.flatMap((segment) =>
        segment.inlines
            .filter((inline) => kinds.includes(inline.kind))
            .flatMap((inline) => {
                const replacement = map[inline.value];
                return replacement === undefined
                    ? []
                    : [
                          {
                              end: inline.end,
                              path: document.path,
                              reason: `${inline.kind}: ${inline.value} → ${replacement}`,
                              replacement,
                              start: inline.start,
                          },
                      ];
            }),
    );
};

export const editsFromFieldValues = function editsFromFieldValues(
    document: Document,
    key: string,
    map: RenameMap,
): Edit[] {
    return document.segments
        .filter((segment) => FIELD_KINDS.has(segment.kind) && segment.key === key)
        .flatMap((segment) => {
            const { value } = segment;
            const replacement = value === undefined ? undefined : map[value];
            return value === undefined || replacement === undefined
                ? []
                : [
                      {
                          end: segment.end,
                          path: document.path,
                          reason: `${key}: ${value} → ${replacement}`,
                          replacement,
                          start: segment.end - value.length,
                      },
                  ];
        });
};

export const editsFromFieldPrefixes = function editsFromFieldPrefixes(
    document: Document,
    keys: readonly string[],
    map: RenameMap,
): Edit[] {
    return document.segments
        .filter((segment) => FIELD_KINDS.has(segment.kind))
        .filter((segment) => segment.key !== undefined && keys.includes(segment.key))
        .flatMap((segment) => {
            const value = segment.value ?? "";
            const prefix = prefixOf(value, map, FIELD_BOUNDARIES);
            if (prefix === null) {
                return [];
            }

            const [from, replacement] = prefix;
            const valueStart = segment.end - value.length;
            return [
                {
                    end: valueStart + from.length,
                    path: document.path,
                    reason: `${String(segment.key)} prefix: ${from} → ${replacement}`,
                    replacement,
                    start: valueStart,
                },
            ];
        });
};

export const editsFromInlinePrefixes = function editsFromInlinePrefixes(
    document: Document,
    map: RenameMap,
    kinds: readonly Inline["kind"][],
): Edit[] {
    return document.segments.flatMap((segment) =>
        segment.inlines
            .filter((inline) => kinds.includes(inline.kind) && map[inline.value] === undefined)
            .flatMap((inline) => {
                const prefix = prefixOf(inline.value, map, INLINE_BOUNDARIES);
                if (prefix === null) {
                    return [];
                }

                const [from, replacement] = prefix;
                return [
                    {
                        end: inline.start + from.length,
                        path: document.path,
                        reason: `${inline.kind} prefix: ${from} → ${replacement}`,
                        replacement,
                        start: inline.start,
                    },
                ];
            }),
    );
};

export const applyEdits = function applyEdits(source: string, edits: readonly Edit[]): ApplyResult {
    const ordered = edits.toSorted((a, b) => b.start - a.start || b.end - a.end);

    const applied: Edit[] = [];
    const rejected: Edit[] = [];
    let text = source;
    let boundary = source.length;

    for (const edit of ordered) {
        const malformed = edit.start < 0 || edit.end < edit.start || edit.end > text.length;
        if (edit.end > boundary || malformed) {
            rejected.push(edit);
        } else {
            text = text.slice(0, edit.start) + edit.replacement + text.slice(edit.end);
            applied.push(edit);
            boundary = edit.start;
        }
    }

    return { applied, rejected, text };
};