# tools/core/transformers/segment.transformer.ts

> 184 lines of code and 17 definitions.

Tree: Coordination tree
Language: typescript
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-transformers-segment-transformer-ts
Source text: https://banes-lab.com/assets/sources/source.10325673727f6adc32297cc37b53439fcbc4d8e34e4a1f9b444fb3cc70b49b3d.generated.txt

## Definitions

- `editsFromFieldValues` (lexical_declaration, line 34, exported)
- `editsFromInlines` (lexical_declaration, line 5, exported)
- `editsFromFieldPrefixes` (lexical_declaration, line 70, exported)
- `editsFromInlinePrefixes` (lexical_declaration, line 127, exported)
- `RenameMap` (type_alias_declaration, line 3, exported)
- `valueStart` (lexical_declaration, line 112, exported)
- `edits` (lexical_declaration, line 132, exported)
- `replacement` (lexical_declaration, line 144, exported)
- `matched` (lexical_declaration, line 151, exported)
- `i` (lexical_declaration, line 152, exported)
- `ApplyResult` (interface_declaration, line 180, exported)
- `applyEdits` (lexical_declaration, line 186, exported)
- `ordered` (lexical_declaration, line 187, exported)
- `applied` (lexical_declaration, line 189, exported)
- `rejected` (lexical_declaration, line 190, exported)
- `text` (lexical_declaration, line 191, exported)
- `boundary` (lexical_declaration, line 192, exported)

## Used by

- [tools/core/entrypoints/segment.entrypoint.ts](https://banes-lab.com/source/coordination/tools/core/entrypoints/segment.entrypoint.ts.md)
- [tools/core/transformers/reference.transformer.ts](https://banes-lab.com/source/coordination/tools/core/transformers/reference.transformer.ts.md)

## Source

```typescript
import type { Document, Edit, Inline } from "../types/segment.types.ts";

export type RenameMap = Readonly<Record<string, string>>;

export const editsFromInlines = function editsFromInlines(
    document: Document,
    map: RenameMap,
    kinds: readonly Inline["kind"][],
): Edit[] {
    const edits: Edit[] = [];

    for (const segment of document.segments) {
        for (const inline of segment.inlines) {
            if (!kinds.includes(inline.kind)) {
                continue;
            }
            const replacement = map[inline.value];
            if (replacement === undefined) {
                continue;
            }
            edits.push({
                end: inline.end,
                path: document.path,
                reason: `${inline.kind}: ${inline.value} → ${replacement}`,
                replacement,
                start: inline.start,
            });
        }
    }

    return edits;
};

export const editsFromFieldValues = function editsFromFieldValues(
    document: Document,
    key: string,
    map: RenameMap,
): Edit[] {
    const edits: Edit[] = [];

    for (const segment of document.segments) {
        if (segment.kind !== "field" && segment.kind !== "frontmatter-field") {
            continue;
        }
        if (segment.key !== key) {
            continue;
        }
        if (segment.value === undefined) {
            continue;
        }

        const replacement = map[segment.value];
        if (replacement === undefined) {
            continue;
        }

        const valueStart = segment.end - segment.value.length;
        edits.push({
            end: segment.end,
            path: document.path,
            reason: `${key}: ${segment.value} → ${replacement}`,
            replacement,
            start: valueStart,
        });
    }

    return edits;
};

export const editsFromFieldPrefixes = function editsFromFieldPrefixes(
    document: Document,
    keys: readonly string[],
    map: RenameMap,
): Edit[] {
    const edits: Edit[] = [];

    for (const segment of document.segments) {
        if (segment.kind !== "field" && segment.kind !== "frontmatter-field") {
            continue;
        }
        if (segment.key === undefined || !keys.includes(segment.key)) {
            continue;
        }
        if (segment.value === undefined || segment.value.length === 0) {
            continue;
        }

        for (const from of Object.keys(map)) {
            const replacement = map[from];
            if (replacement === undefined) {
                continue;
            }
            if (segment.value.length <= from.length) {
                continue;
            }
            let matched = true;
            for (let i = 0; i < from.length; i += 1) {
                if (segment.value[i] !== from[i]) {
                    matched = false;
                    break;
                }
            }
            if (!matched) {
                continue;
            }

            const boundary = segment.value[from.length];
            if (boundary !== "#" && boundary !== " ") {
                continue;
            }

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

    return edits;
};

export const editsFromInlinePrefixes = function editsFromInlinePrefixes(
    document: Document,
    map: RenameMap,
    kinds: readonly Inline["kind"][],
): Edit[] {
    const edits: Edit[] = [];

    for (const segment of document.segments) {
        for (const inline of segment.inlines) {
            if (!kinds.includes(inline.kind)) {
                continue;
            }
            if (map[inline.value] !== undefined) {
                continue;
            }

            for (const from of Object.keys(map)) {
                const replacement = map[from];
                if (replacement === undefined) {
                    continue;
                }
                if (inline.value.length <= from.length) {
                    continue;
                }
                let matched = true;
                for (let i = 0; i < from.length; i += 1) {
                    if (inline.value[i] !== from[i]) {
                        matched = false;
                        break;
                    }
                }
                if (!matched) {
                    continue;
                }
                if (inline.value[from.length] !== "#") {
                    continue;
                }

                edits.push({
                    end: inline.start + from.length,
                    path: document.path,
                    reason: `${inline.kind} prefix: ${from} → ${replacement}`,
                    replacement,
                    start: inline.start,
                });
                break;
            }
        }
    }

    return edits;
};

export interface ApplyResult {
    readonly text: string;
    readonly applied: readonly Edit[];
    readonly rejected: readonly Edit[];
}

export const applyEdits = function applyEdits(source: string, edits: readonly Edit[]): ApplyResult {
    const ordered = [...edits].sort((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) {
        if (edit.end > boundary) {
            rejected.push(edit);
            continue;
        }
        if (edit.start < 0 || edit.end < edit.start || edit.end > text.length) {
            rejected.push(edit);
            continue;
        }
        text = text.slice(0, edit.start) + edit.replacement + text.slice(edit.end);
        applied.push(edit);
        boundary = edit.start;
    }

    return { applied, rejected, text };
};
```
