"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.wrapPerLine = exports.wrapToBlock = void 0;
exports.getBlockExtraLineBreaks = getBlockExtraLineBreaks;
exports.replaceOrInsertAfter = replaceOrInsertAfter;
exports.inlineWrapTo = inlineWrapTo;
exports.toggleInlineMarkupFactory = toggleInlineMarkupFactory;
exports.iterateOverRangeLines = iterateOverRangeLines;
const state_1 = require("@codemirror/state");
function getBlockExtraLineBreaks(state, { from: fromLine, to: toLine }) {
    let lineBreaksBefore = 0;
    if (fromLine.number === 1 || state.doc.line(fromLine.number - 1).length === 0)
        lineBreaksBefore = 0;
    else
        lineBreaksBefore = 1;
    let lineBreaksAfter = 0;
    if (toLine.number === state.doc.lines || state.doc.line(fromLine.number + 1).length === 0)
        lineBreaksAfter = 0;
    else
        lineBreaksAfter = 1;
    return { before: lineBreaksBefore, after: lineBreaksAfter };
}
function replaceOrInsertAfter(state, markup) {
    const selrange = state.selection.main;
    if (isFullLinesSelection(state.doc, selrange)) {
        const extraBreaks = getBlockExtraLineBreaks(state, {
            from: state.doc.lineAt(selrange.from),
            to: state.doc.lineAt(selrange.to),
        });
        return state.replaceSelection(state.lineBreak.repeat(extraBreaks.before) +
            markup +
            state.lineBreak.repeat(extraBreaks.after));
    }
    else {
        const insert = state.lineBreak.repeat(2) + markup + state.lineBreak.repeat(2);
        const from = state.doc.lineAt(selrange.to).to;
        const selAnchor = from + insert.length - 2;
        return { changes: { from, insert }, selection: { anchor: selAnchor } };
    }
}
function isFullLinesSelection(doc, range) {
    const fromLine = doc.lineAt(range.from);
    const toLine = doc.lineAt(range.to);
    return range.from <= fromLine.from && range.to >= toLine.to;
}
const wrapToBlock = (before, after, perLine) => {
    return ({ state, dispatch }) => {
        const beforeText = typeof before === 'function' ? before(state) : before;
        const afterText = typeof after === 'function' ? after(state) : after;
        const selrange = state.selection.main;
        const fromLine = state.doc.lineAt(selrange.from);
        const toLine = state.doc.lineAt(selrange.to);
        const extraBreaks = getBlockExtraLineBreaks(state, { from: fromLine, to: toLine });
        const beforeInsertion = state.lineBreak.repeat(extraBreaks.before) + beforeText;
        const afterInsertion = afterText + state.lineBreak.repeat(extraBreaks.after);
        const changeSpec = [{ from: fromLine.from, insert: beforeInsertion }];
        const isEmptyLine = fromLine.number === toLine.number && fromLine.length === 0;
        let cursorShift = selrange.head + beforeInsertion.length;
        if (perLine) {
            const lineBeforeText = typeof perLine.before === 'function' ? perLine.before(state) : perLine.before;
            const lineAfterText = typeof perLine.after === 'function' ? perLine.after(state) : perLine.after;
            iterateOverRangeLines(state.doc, selrange, (line) => {
                if (perLine.skipEmptyLine && line.length === 0)
                    return;
                if (lineBeforeText) {
                    changeSpec.push({ from: line.from, insert: lineBeforeText });
                    if (isEmptyLine)
                        cursorShift += lineBeforeText.length;
                }
                if (lineAfterText)
                    changeSpec.push({ from: line.to, insert: lineAfterText });
            });
        }
        changeSpec.push({ from: toLine.to, insert: afterInsertion });
        const changes = state.changes(changeSpec);
        dispatch(state.update({
            changes,
            selection: isEmptyLine
                ? state_1.EditorSelection.single(cursorShift)
                : state.selection.map(changes),
        }));
        return true;
    };
};
exports.wrapToBlock = wrapToBlock;
function inlineWrapTo(before, after = before) {
    return ({ state, dispatch }) => {
        const trSpec = state.changeByRange((range) => {
            const changes = state.changes([
                { from: range.from, insert: before },
                { from: range.to, insert: after },
            ]);
            return {
                changes,
                range: range.empty
                    ? state_1.EditorSelection.range(range.anchor + before.length, range.head + before.length, range.goalColumn, range.bidiLevel ?? undefined)
                    : range.map(changes),
            };
        });
        dispatch(state.update(trSpec));
        return true;
    };
}
function toggleInlineMarkupFactory(markup) {
    const [before, after] = typeof markup === 'string'
        ? [markup, markup]
        : [markup.before, markup.after ?? markup.before];
    const beforeLength = before.length;
    const afterLength = after.length;
    return ({ state, dispatch }) => {
        const tr = state.changeByRange((range) => {
            const hasMarkupBefore = state.sliceDoc(range.from - beforeLength, range.from) === before;
            const hasMarkupAfter = state.sliceDoc(range.to, range.to + afterLength) === after;
            const changeSpec = [];
            if (hasMarkupBefore && hasMarkupAfter) {
                changeSpec.push({ from: range.from - beforeLength, to: range.from, insert: '' }, { from: range.to, to: range.to + afterLength, insert: '' });
            }
            else {
                if (!hasMarkupBefore) {
                    changeSpec.push({
                        from: range.from,
                        insert: before,
                    });
                }
                if (!hasMarkupAfter) {
                    changeSpec.push({
                        from: range.to,
                        insert: after,
                    });
                }
            }
            const changes = state.changes(changeSpec);
            return {
                changes,
                range: range.empty && !hasMarkupBefore
                    ? state_1.EditorSelection.range(range.anchor + beforeLength, range.head + beforeLength, range.goalColumn, range.bidiLevel ?? undefined)
                    : range.map(changes),
            };
        });
        tr.scrollIntoView = true;
        dispatch(state.update(tr));
        return true;
    };
}
const wrapPerLine = ({ beforeText: before, skipEmptyLine = true }) => ({ state, dispatch }) => {
    const tr = state.changeByRange((range) => {
        const changes = [];
        const isSingleEmptyLine = range.anchor === range.head && state.doc.lineAt(range.head).length === 0;
        if (isSingleEmptyLine) {
            changes.push({ from: range.head, insert: before });
        }
        else {
            iterateOverRangeLines(state.doc, range, (line) => {
                if (skipEmptyLine && line.length === 0)
                    return;
                changes.push({ from: line.from, insert: before });
            });
        }
        const changeSet = state.changes(changes);
        return { changes: changeSet, range: range.map(changeSet, 1) };
    });
    dispatch(state.update(tr));
    return true;
};
exports.wrapPerLine = wrapPerLine;
function iterateOverRangeLines(doc, range, fn) {
    const from = doc.lineAt(range.from).number;
    const to = doc.lineAt(range.to).number;
    for (let i = from; i <= to; i++) {
        fn(doc.line(i));
    }
}
//# sourceMappingURL=helpers.js.map
