/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
import { O as OffsetRange, i as initWasm, g as getWasm, D as DefaultLinesDiffComputer, R as RangeMapping, a as Range, b as DetailedLineRangeMapping, L as LineRange, c as LinesDiff, w as wireToLinesDiff } from './wire.js';
export { d as _LineRangeMapping } from './wire.js';

/**
 * A pair of ranges, one in `original`, one in `modified`.
 */
class OffsetRangeMapping {
    original;
    modified;
    constructor(original, modified) {
        this.original = original;
        this.modified = modified;
    }
    toString() {
        return `${this.original} <-> ${this.modified}`;
    }
}

/**
 * A single atomic replacement: replace `range` in the input with `newText`.
 */
class StringReplacement {
    range;
    newText;
    constructor(range, newText) {
        this.range = range;
        this.newText = newText;
    }
    get isEmpty() {
        return this.range.isEmpty && this.newText.length === 0;
    }
    toString() {
        return `${this.range}->${JSON.stringify(this.newText)}`;
    }
}
/**
 * A set of replacements applied in parallel to one input string.
 *
 * Invariants:
 *  - replacements are sorted by `range.start`
 *  - replacements do not overlap (touching is allowed)
 *  - all ranges live in the same coordinate space (the input passed to `apply`)
 */
class StringEdit {
    replacements;
    static empty = new StringEdit([]);
    static single(replacement) {
        return new StringEdit([replacement]);
    }
    /**
     * Validates that replacements are sorted and non-overlapping.
     */
    static of(replacements) {
        for (let i = 1; i < replacements.length; i++) {
            const prev = replacements[i - 1];
            const cur = replacements[i];
            if (prev.range.endExclusive > cur.range.start) {
                throw new Error(`StringEdit.of: replacements must be sorted and non-overlapping (` +
                    `${prev.range} vs ${cur.range})`);
            }
        }
        return new StringEdit(replacements);
    }
    constructor(replacements) {
        this.replacements = replacements;
    }
    get isEmpty() {
        return this.replacements.length === 0;
    }
    apply(text) {
        if (this.replacements.length === 0) {
            return text;
        }
        const parts = [];
        let cursor = 0;
        for (const r of this.replacements) {
            parts.push(text.substring(cursor, r.range.start));
            parts.push(r.newText);
            cursor = r.range.endExclusive;
        }
        parts.push(text.substring(cursor));
        return parts.join('');
    }
    /**
     * Returns an edit `E` such that `E.apply(this.apply(input)) === input`.
     */
    inverse(input) {
        const out = [];
        let delta = 0;
        for (const r of this.replacements) {
            const newStart = r.range.start + delta;
            const newEnd = newStart + r.newText.length;
            const oldText = input.substring(r.range.start, r.range.endExclusive);
            out.push(new StringReplacement(new OffsetRange(newStart, newEnd), oldText));
            delta += r.newText.length - r.range.length;
        }
        return new StringEdit(out);
    }
    /**
     * Joins touching replacements and drops no-ops.
     */
    normalize() {
        const out = [];
        for (const r of this.replacements) {
            if (r.isEmpty) {
                continue;
            }
            const prev = out[out.length - 1];
            if (prev && prev.range.endExclusive === r.range.start) {
                out[out.length - 1] = new StringReplacement(new OffsetRange(prev.range.start, r.range.endExclusive), prev.newText + r.newText);
            }
            else {
                out.push(r);
            }
        }
        return new StringEdit(out);
    }
    toString() {
        return `StringEdit[${this.replacements.map(r => r.toString()).join(', ')}]`;
    }
}

/**
 * An atomic replacement carrying user-defined metadata.
 */
class AnnotatedStringReplacement {
    range;
    newText;
    data;
    constructor(range, newText, data) {
        this.range = range;
        this.newText = newText;
        this.data = data;
    }
    get isEmpty() {
        return this.range.isEmpty && this.newText.length === 0;
    }
    toString() {
        return `${this.range}->${JSON.stringify(this.newText)}#${this.data}`;
    }
}
/**
 * A set of annotated replacements applied in parallel.
 *
 * Invariants: same as `StringEdit` (sorted by `range.start`, non-overlapping,
 * shared coordinate space).
 */
class AnnotatedStringEdit {
    replacements;
    static empty() {
        return new AnnotatedStringEdit([]);
    }
    static single(replacement) {
        return new AnnotatedStringEdit([replacement]);
    }
    static of(replacements) {
        for (let i = 1; i < replacements.length; i++) {
            const prev = replacements[i - 1];
            const cur = replacements[i];
            if (prev.range.endExclusive > cur.range.start) {
                throw new Error(`AnnotatedStringEdit.of: replacements must be sorted and non-overlapping (` +
                    `${prev.range} vs ${cur.range})`);
            }
        }
        return new AnnotatedStringEdit(replacements);
    }
    constructor(replacements) {
        this.replacements = replacements;
    }
    get isEmpty() {
        return this.replacements.length === 0;
    }
    apply(text) {
        if (this.replacements.length === 0) {
            return text;
        }
        const parts = [];
        let cursor = 0;
        for (const r of this.replacements) {
            parts.push(text.substring(cursor, r.range.start));
            parts.push(r.newText);
            cursor = r.range.endExclusive;
        }
        parts.push(text.substring(cursor));
        return parts.join('');
    }
    inverse(input, invertData) {
        const out = [];
        let delta = 0;
        for (const r of this.replacements) {
            const newStart = r.range.start + delta;
            const newEnd = newStart + r.newText.length;
            const oldText = input.substring(r.range.start, r.range.endExclusive);
            out.push(new AnnotatedStringReplacement(new OffsetRange(newStart, newEnd), oldText, invertData(r.data)));
            delta += r.newText.length - r.range.length;
        }
        return new AnnotatedStringEdit(out);
    }
    /**
     * Joins touching replacements when their annotations join. Drops no-ops.
     */
    normalize() {
        const out = [];
        for (const r of this.replacements) {
            if (r.isEmpty) {
                continue;
            }
            const prev = out[out.length - 1];
            if (prev && prev.range.endExclusive === r.range.start) {
                const joined = prev.data.join(r.data);
                if (joined !== undefined) {
                    out[out.length - 1] = new AnnotatedStringReplacement(new OffsetRange(prev.range.start, r.range.endExclusive), prev.newText + r.newText, joined);
                    continue;
                }
            }
            out.push(r);
        }
        return new AnnotatedStringEdit(out);
    }
    mapData(f) {
        return new AnnotatedStringEdit(this.replacements.map(r => new AnnotatedStringReplacement(r.range, r.newText, f(r.data))));
    }
    stripData() {
        return StringEdit.of(this.replacements.map(r => new StringReplacement(r.range, r.newText)));
    }
    toString() {
        return `AnnotatedStringEdit[${this.replacements.map(r => r.toString()).join(', ')}]`;
    }
}
/**
 * Diff result annotation.
 *
 * Carries flags describing the nature of the replacement. For now only
 * `isFormattingChange` is exposed; more flags (e.g. move source/target,
 * semantic category) can be added later.
 */
class DiffAnnotation {
    isFormattingChange;
    static change = new DiffAnnotation(false);
    static formatting = new DiffAnnotation(true);
    constructor(
    /** Whether the replacement is purely a formatting change (whitespace, indentation, etc.). */
    isFormattingChange) {
        this.isFormattingChange = isFormattingChange;
    }
    join(other) {
        if (this.isFormattingChange !== other.isFormattingChange) {
            return undefined;
        }
        return this;
    }
    toString() {
        return this.isFormattingChange ? 'formatting' : 'change';
    }
}

/**
 * A block of text that was moved between two locations.
 *
 * `range` locates the block in `original` and in `modified` (offsets in the
 * respective full texts). `innerEdit` describes the changes inside the moved
 * block, with offsets local to the slice (i.e. `0` is the start of the slice).
 */
class Move {
    range;
    innerEdit;
    constructor(range, innerEdit) {
        this.range = range;
        this.innerEdit = innerEdit;
    }
    toString() {
        return `Move ${this.range}` + (this.innerEdit.isEmpty ? '' : ` (${this.innerEdit})`);
    }
}

/**
 * Output of a diff computation.
 */
class DiffResult {
    edits;
    moves;
    hitTimeout;
    constructor(
    /**
     * Parallel set of replacements transforming `original` into `modified`,
     * excluding any replacements that are part of a `Move`.
     *
     * When no moves are computed: `edits.apply(original) === modified`.
     */
    edits, 
    /**
     * Detected text moves. Empty unless `DiffOptions.computeMoves` is set.
     */
    moves, 
    /**
     * `true` if the algorithm aborted because of `maxComputationTimeMs`.
     * The result is still valid but may be coarser than ideal.
     */
    hitTimeout) {
        this.edits = edits;
        this.moves = moves;
        this.hitTimeout = hitTimeout;
    }
}

async function createDiffComputer(options = {}) {
    if (options.useWasm) {
        await initWasm();
        return new _WasmDiffComputer(options.algorithm);
    }
    return new _TsDiffComputer(options.algorithm);
}
// ============================================================================
// TS-backed computer
// ============================================================================
class _TsDiffComputer {
    _algorithm;
    _impl = new DefaultLinesDiffComputer();
    constructor(_algorithm) {
        this._algorithm = _algorithm;
    }
    computeDiff(original, modified, options) {
        const originalLines = original.split('\n');
        const modifiedLines = modified.split('\n');
        const internal = {
            maxComputationTimeMs: options?.maxComputationTimeMs ?? 0,
            ignoreTrimWhitespace: options?.ignoreTrimWhitespace ?? false,
            computeMoves: options?.computeMoves ?? false,
            extendToSubwords: options?.extendToSubwords ?? false,
        };
        const linesDiff = this._impl.computeDiff(originalLines, modifiedLines, internal);
        void this._algorithm; // currently a hint; auto-selection lives inside DefaultLinesDiffComputer
        return _adaptLinesDiff(linesDiff, original, modified, originalLines, modifiedLines);
    }
}
// ============================================================================
// WASM-backed computer
// ============================================================================
class _WasmDiffComputer {
    _algorithm;
    constructor(_algorithm) {
        this._algorithm = _algorithm;
    }
    computeDiff(original, modified, options) {
        const originalLines = original.split('\n');
        const modifiedLines = modified.split('\n');
        const wasmOptions = {};
        if (options?.maxComputationTimeMs !== undefined)
            wasmOptions.maxComputationTimeMs = options.maxComputationTimeMs;
        if (options?.ignoreTrimWhitespace !== undefined)
            wasmOptions.ignoreTrimWhitespace = options.ignoreTrimWhitespace;
        if (options?.computeMoves !== undefined)
            wasmOptions.computeMoves = options.computeMoves;
        if (options?.extendToSubwords !== undefined)
            wasmOptions.extendToSubwords = options.extendToSubwords;
        const wasm = getWasm();
        const wasmResult = wasm.computeDiffLines(originalLines, modifiedLines, wasmOptions);
        void this._algorithm;
        const linesDiff = _wasmToLinesDiff(wasmResult);
        return _adaptLinesDiff(linesDiff, original, modified, originalLines, modifiedLines);
    }
}
function _wasmToLinesDiff(wasmResult) {
    const changes = wasmResult.changes.map((c) => {
        const inner = c.innerChanges?.map((ic) => new RangeMapping(new Range(ic.originalStartLine, ic.originalStartColumn, ic.originalEndLine, ic.originalEndColumn), new Range(ic.modifiedStartLine, ic.modifiedStartColumn, ic.modifiedEndLine, ic.modifiedEndColumn)));
        return new DetailedLineRangeMapping(new LineRange(c.original.startLineNumber, c.original.endLineNumberExclusive), new LineRange(c.modified.startLineNumber, c.modified.endLineNumberExclusive), inner);
    });
    return new LinesDiff(changes, [], wasmResult.hitTimeout);
}
// ============================================================================
// LinesDiff -> DiffResult adapter
// ============================================================================
/** @internal — exported for the worker-side client. */
function _adaptLinesDiff(linesDiff, original, modified, originalLines, modifiedLines) {
    const originalLineStarts = _computeLineStarts(originalLines);
    const modifiedLineStarts = _computeLineStarts(modifiedLines);
    const replacements = [];
    for (const change of linesDiff.changes) {
        _appendChangeReplacements(change, original, modified, originalLines, modifiedLines, originalLineStarts, modifiedLineStarts, replacements);
    }
    const edits = AnnotatedStringEdit.of(replacements);
    const moves = linesDiff.moves.map(m => _adaptMove(m, original, modified, originalLines, modifiedLines, originalLineStarts, modifiedLineStarts));
    return new DiffResult(edits, moves, linesDiff.hitTimeout);
}
function _appendChangeReplacements(change, original, modified, originalLines, modifiedLines, originalLineStarts, modifiedLineStarts, out) {
    if (change.innerChanges && change.innerChanges.length > 0) {
        for (const ic of change.innerChanges) {
            const origRange = _rangeToOffsetRange(ic.originalRange, original, originalLines, originalLineStarts);
            const modRange = _rangeToOffsetRange(ic.modifiedRange, modified, modifiedLines, modifiedLineStarts);
            const newText = modified.substring(modRange.start, modRange.endExclusive);
            out.push(new AnnotatedStringReplacement(origRange, newText, DiffAnnotation.change));
        }
        return;
    }
    // Fallback: no inner detail — replace the whole line range.
    const origRange = _lineRangeToOffsetRange(change.original.startLineNumber, change.original.endLineNumberExclusive, original, originalLineStarts);
    const modRange = _lineRangeToOffsetRange(change.modified.startLineNumber, change.modified.endLineNumberExclusive, modified, modifiedLineStarts);
    const newText = modified.substring(modRange.start, modRange.endExclusive);
    out.push(new AnnotatedStringReplacement(origRange, newText, DiffAnnotation.change));
}
function _adaptMove(move, original, modified, originalLines, modifiedLines, originalLineStarts, modifiedLineStarts) {
    const origRange = _lineRangeToOffsetRange(move.lineRangeMapping.original.startLineNumber, move.lineRangeMapping.original.endLineNumberExclusive, original, originalLineStarts);
    const modRange = _lineRangeToOffsetRange(move.lineRangeMapping.modified.startLineNumber, move.lineRangeMapping.modified.endLineNumberExclusive, modified, modifiedLineStarts);
    // Build inner edit with offsets local to the moved slices.
    const inner = [];
    for (const change of move.changes) {
        if (change.innerChanges && change.innerChanges.length > 0) {
            for (const ic of change.innerChanges) {
                const globalOrig = _rangeToOffsetRange(ic.originalRange, original, originalLines, originalLineStarts);
                const globalMod = _rangeToOffsetRange(ic.modifiedRange, modified, modifiedLines, modifiedLineStarts);
                inner.push(new StringReplacement(new OffsetRange(globalOrig.start - origRange.start, globalOrig.endExclusive - origRange.start), modified.substring(globalMod.start, globalMod.endExclusive)));
            }
        }
        else {
            const globalOrig = _lineRangeToOffsetRange(change.original.startLineNumber, change.original.endLineNumberExclusive, original, originalLineStarts);
            const globalMod = _lineRangeToOffsetRange(change.modified.startLineNumber, change.modified.endLineNumberExclusive, modified, modifiedLineStarts);
            inner.push(new StringReplacement(new OffsetRange(globalOrig.start - origRange.start, globalOrig.endExclusive - origRange.start), modified.substring(globalMod.start, globalMod.endExclusive)));
        }
    }
    return new Move(new OffsetRangeMapping(origRange, modRange), StringEdit.of(inner));
}
// ----- offset/line helpers -----
function _computeLineStarts(lines) {
    // Offsets correspond to `lines.join('\n')`, which equals the input string
    // when the input was split by `\n`.
    const starts = new Array(lines.length + 1);
    starts[0] = 0;
    for (let i = 0; i < lines.length; i++) {
        starts[i + 1] = starts[i] + lines[i].length + 1; // +1 for the '\n'
    }
    // `starts[lines.length]` is one past the synthetic trailing newline; the
    // real text ends one earlier when there is no trailing newline (which is
    // what `split('\n')` produces for non-newline-terminated input).
    return starts;
}
function _lineRangeToOffsetRange(startLineNumber, endLineNumberExclusive, text, lineStarts) {
    const start = _lineColToOffset(startLineNumber, 1, text, lineStarts);
    const end = _lineColToOffset(endLineNumberExclusive, 1, text, lineStarts);
    return new OffsetRange(start, end);
}
function _rangeToOffsetRange(range, text, lines, lineStarts) {
    const start = _lineColToOffset(range.startLineNumber, range.startColumn, text, lineStarts);
    const end = _lineColToOffset(range.endLineNumber, range.endColumn, text, lineStarts);
    return new OffsetRange(start, end);
}
function _lineColToOffset(lineNumber, column, text, lineStarts) {
    // `lineNumber` and `column` are 1-based; `endLineNumberExclusive` can be
    // `lines.length + 1` (one past the last line).
    if (lineNumber <= 0) {
        return 0;
    }
    if (lineNumber > lineStarts.length) {
        return text.length;
    }
    const base = lineStarts[lineNumber - 1];
    const offset = base + (column - 1);
    return Math.min(Math.max(offset, 0), text.length);
}

/**
 * Main-thread client for the diff worker. Exposes an async `IAsyncDiffComputer`.
 *
 * Inputs travel as `string[]` via structured-clone postMessage. The response
 * is a small JSON-friendly `WireLinesDiff`; the main thread reconstructs the
 * full `LinesDiff` and runs `_adaptLinesDiff` locally (it already has the
 * input strings).
 */
async function createWorkerDiffComputer(opts = {}) {
    const worker = opts.workerFactory ? opts.workerFactory() : defaultWorkerFactory();
    const backend = opts.useWasm ? 'wasm' : 'ts';
    const computer = new _WorkerDiffComputer(worker, backend);
    await computer.init();
    return computer;
}
function defaultWorkerFactory() {
    // The `?esm` marker is auto-discovered by `@vscode/*-plugin-esm-url`
    // bundler plugins; they emit `worker.ts` as a sibling `worker.js` bundle
    // and rewrite this URL accordingly.
    return new Worker(new URL('worker.js?esm', import.meta.url), { type: 'module' });
}
class _WorkerDiffComputer {
    _worker;
    _backend;
    _nextId = 1;
    _pending = new Map();
    _pendingInit = null;
    _disposed = false;
    constructor(_worker, _backend) {
        this._worker = _worker;
        this._backend = _backend;
        this._worker.addEventListener('message', (ev) => {
            this._onMessage(ev.data);
        });
        this._worker.addEventListener('error', (ev) => {
            this._failAll(new Error(`Worker error: ${ev.message ?? 'unknown'}`));
        });
    }
    init() {
        const id = this._nextId++;
        const req = { kind: 'init', id, backend: this._backend };
        return new Promise((resolve, reject) => {
            this._pendingInit = { resolve, reject };
            this._worker.postMessage(req);
        });
    }
    computeDiff(original, modified, options = {}) {
        if (this._disposed) {
            return Promise.reject(new Error('Worker computer disposed'));
        }
        const id = this._nextId++;
        const originalLines = original.split('\n');
        const modifiedLines = modified.split('\n');
        const msg = {
            kind: 'computeDiff',
            id,
            backend: this._backend,
            originalLines,
            modifiedLines,
            options,
        };
        return new Promise((resolve, reject) => {
            this._pending.set(id, {
                resolve,
                reject,
                original,
                modified,
                originalLines,
                modifiedLines,
            });
            this._worker.postMessage(msg);
        });
    }
    dispose() {
        if (this._disposed)
            return;
        this._disposed = true;
        this._failAll(new Error('Worker computer disposed'));
        this._worker.terminate();
    }
    _onMessage(msg) {
        if (msg.kind === 'init') {
            const pending = this._pendingInit;
            this._pendingInit = null;
            pending?.resolve();
            return;
        }
        const entry = this._pending.get(msg.id);
        if (!entry) {
            // Init failure is reported on the init id with `kind === 'computeDiff'`
            // and `ok === false`; route to the init promise if it's still pending.
            if (!msg.ok && this._pendingInit) {
                const p = this._pendingInit;
                this._pendingInit = null;
                p.reject(new Error(msg.error));
            }
            return;
        }
        this._pending.delete(msg.id);
        if (!msg.ok) {
            entry.reject(new Error(msg.error));
            return;
        }
        try {
            const linesDiff = wireToLinesDiff(msg.result);
            const result = _adaptLinesDiff(linesDiff, entry.original, entry.modified, entry.originalLines, entry.modifiedLines);
            entry.resolve(result);
        }
        catch (err) {
            entry.reject(err instanceof Error ? err : new Error(String(err)));
        }
    }
    _failAll(err) {
        for (const p of this._pending.values())
            p.reject(err);
        this._pending.clear();
        if (this._pendingInit) {
            this._pendingInit.reject(err);
            this._pendingInit = null;
        }
    }
}

export { AnnotatedStringEdit, AnnotatedStringReplacement, DiffAnnotation, DiffResult, Move, OffsetRange, OffsetRangeMapping, StringEdit, StringReplacement, DefaultLinesDiffComputer as _DefaultLinesDiffComputer, DetailedLineRangeMapping as _DetailedLineRangeMapping, LineRange as _LineRange, LinesDiff as _LinesDiff, RangeMapping as _RangeMapping, getWasm as _getWasm, initWasm as _initWasm, createDiffComputer, createWorkerDiffComputer };//# sourceMappingURL=https://github.com/VSCodium/sourcemaps/releases/download/stable-4c0b0c6cc561d2d3636d1ec250935431876ce4dc/node_modules-@vscode-diff-dist-index.js.map
