# core/converters/edit.converter.ts

> 55 lines of code and 14 definitions.

Tree: GovLab Quality
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-converters-edit-converter-ts
Source text: https://banes-lab.com/source/quality/core/converters/edit.converter.ts.txt

Listed in [core/converters](https://banes-lab.com/api/source/quality/core/converters.md), after [core/converters/css.converter.ts](https://banes-lab.com/source/quality/core/converters/css.converter.ts.md) and before [core/converters/emitter.converter.ts](https://banes-lab.com/source/quality/core/converters/emitter.converter.ts.md).

## Definitions

- `applyRangeFixes` (lexical_declaration, line 28)
- `overlaps` (lexical_declaration, line 24)
- `applyCuratedSuggestions` (lexical_declaration, line 40, exported)
- `entryFor` (lexical_declaration, line 5)
- `chosenFix` (lexical_declaration, line 14)
- `suggestions` (lexical_declaration, line 15)
- `[only]` (lexical_declaration, line 17)
- `picked` (lexical_declaration, line 20)
- `applied` (lexical_declaration, line 29)
- `out` (lexical_declaration, line 30)
- `consumed` (lexical_declaration, line 44, exported)
- `fixes` (lexical_declaration, line 45, exported)
- `entry` (lexical_declaration, line 47, exported)
- `fix` (lexical_declaration, line 48, exported)

## Contained in

- [core/converters](https://banes-lab.com/anatomy/quality/folder-quality-core-converters.md)

## Linked from

- [core/converters](https://banes-lab.com/anatomy/quality/folder-quality-core-converters.md)

## Source

```typescript
import type { CuratedApplyResult, CuratedResult, CuratedSuggestion, RangeFix } from "#types/edit.types";
import { CURATED_SUGGESTIONS } from "#configuration/constants/eslint.constants";
import type { Linter } from "eslint";

const entryFor = function entryFor(message: Linter.LintMessage): CuratedSuggestion | null {
    return (
        CURATED_SUGGESTIONS.find(
            (entry) =>
                message.ruleId === entry.ruleId && (entry.messageId === null || message.messageId === entry.messageId),
        ) ?? null
    );
};

const chosenFix = function chosenFix(message: Linter.LintMessage, entry: CuratedSuggestion): RangeFix | null {
    const suggestions = message.suggestions ?? [];
    if (entry.pick === "sole") {
        const [only] = suggestions;
        return suggestions.length === 1 && only !== undefined ? { range: only.fix.range, text: only.fix.text } : null;
    }
    const picked = suggestions.find((suggestion) => suggestion.fix.text.trimStart().startsWith(entry.prefix));
    return picked === undefined ? null : { range: picked.fix.range, text: picked.fix.text };
};

const overlaps = function overlaps(a: RangeFix, b: RangeFix): boolean {
    return a.range[0] < b.range[1] && b.range[0] < a.range[1];
};

const applyRangeFixes = function applyRangeFixes(source: string, fixes: readonly RangeFix[]): string {
    const applied: RangeFix[] = [];
    let out = source;
    for (const fix of fixes.toSorted((a, b) => b.range[0] - a.range[0])) {
        if (!applied.some((prior) => overlaps(prior, fix))) {
            applied.push(fix);
            out = out.slice(0, fix.range[0]) + fix.text + out.slice(fix.range[1]);
        }
    }
    return out;
};

export const applyCuratedSuggestions = function applyCuratedSuggestions(
    result: CuratedResult,
    source: string,
): CuratedApplyResult | null {
    const consumed = new Set<Linter.LintMessage>();
    const fixes: RangeFix[] = [];
    for (const message of result.messages) {
        const entry = entryFor(message);
        const fix = entry === null ? null : chosenFix(message, entry);
        if (fix !== null) {
            fixes.push(fix);
            consumed.add(message);
        }
    }
    if (fixes.length === 0) {
        return null;
    }
    result.messages = result.messages.filter((message) => !consumed.has(message));
    result.errorCount -= consumed.size;
    return { appliedCount: consumed.size, output: applyRangeFixes(source, fixes) };
};
```
