"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TextEditorEdit = exports.TextEditorOptionsExt = exports.TextEditorExt = void 0;
const types_impl_1 = require("./types-impl");
const errors_1 = require("../common/errors");
const assert_1 = require("../common/assert");
const Converter = require("./type-converters");
class TextEditorExt {
    constructor(proxy, id, document, _selections, options, _visibleRanges, viewColumn) {
        this.proxy = proxy;
        this.id = id;
        this._selections = _selections;
        this._visibleRanges = _visibleRanges;
        this.disposed = false;
        this._options = new TextEditorOptionsExt(proxy, id, options);
        this._viewColumn = viewColumn;
        this._document = document;
    }
    get document() {
        return this._document.document;
    }
    set document(doc) {
        throw (0, errors_1.readonly)('Document');
    }
    acceptViewColumn(val) {
        this._viewColumn = val;
    }
    dispose() {
        this.disposed = true;
    }
    get options() {
        return this._options;
    }
    set options(val) {
        if (!this.disposed) {
            this._options.assign(val);
        }
    }
    acceptOptions(options) {
        (0, assert_1.ok)(!this.disposed);
        this._options.accept(options);
    }
    get selection() {
        return this._selections && this._selections[0];
    }
    set selection(val) {
        if (!(val instanceof types_impl_1.Selection)) {
            throw (0, errors_1.illegalArgument)('selection');
        }
        this._selections = [val];
        this.trySetSelection();
    }
    trySetSelection() {
        const selection = this._selections.map(Converter.fromSelection);
        return this.runOnProxy(() => this.proxy.$trySetSelections(this.id, selection));
    }
    get selections() {
        return this._selections;
    }
    set selections(val) {
        if (!Array.isArray(val) || val.some(s => !(s instanceof types_impl_1.Selection))) {
            throw (0, errors_1.illegalArgument)('selections');
        }
        this._selections = val;
        this.trySetSelection();
    }
    acceptSelections(selections) {
        (0, assert_1.ok)(!this.disposed);
        this._selections = selections;
    }
    get visibleRanges() {
        return this._visibleRanges;
    }
    set visibleRanges(val) {
        throw (0, errors_1.readonly)('visibleRanges');
    }
    acceptVisibleRanges(range) {
        (0, assert_1.ok)(!this.disposed);
        this._visibleRanges = range;
    }
    get viewColumn() {
        return this._viewColumn;
    }
    set viewColumn(value) {
        throw (0, errors_1.readonly)('viewColumn');
    }
    _acceptViewColumn(value) {
        (0, assert_1.ok)(!this.disposed);
        this._viewColumn = value;
    }
    // eslint-disable-next-line max-len
    edit(callback, options = { undoStopBefore: true, undoStopAfter: true }) {
        if (this.disposed) {
            return Promise.reject(new Error('TextEditor#edit not possible on closed editor'));
        }
        const edit = new TextEditorEdit(this._document.document, options);
        callback(edit);
        return this.applyEdit(edit);
    }
    // eslint-disable-next-line max-len
    insertSnippet(snippet, location, options = { undoStopBefore: true, undoStopAfter: true }) {
        if (this.disposed) {
            return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors'));
        }
        let ranges;
        if (!location || (Array.isArray(location) && location.length === 0)) {
            ranges = this._selections.map(s => Converter.fromRange(s));
        }
        else if (location instanceof types_impl_1.Position) {
            const { lineNumber, column } = Converter.fromPosition(location);
            ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }];
        }
        else if (location instanceof types_impl_1.Range) {
            ranges = [Converter.fromRange(location)];
        }
        else {
            ranges = [];
            for (const posOrRange of location) {
                if (posOrRange instanceof types_impl_1.Range) {
                    ranges.push(Converter.fromRange(posOrRange));
                }
                else {
                    const { lineNumber, column } = Converter.fromPosition(posOrRange);
                    ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column });
                }
            }
        }
        return this.proxy.$tryInsertSnippet(this.id, snippet.value, ranges, options);
    }
    setDecorations(decorationType, rangesOrOptions) {
        this.runOnProxy(() => {
            if (Converter.isDecorationOptionsArr(rangesOrOptions)) {
                return this.proxy.$trySetDecorations(this.id, decorationType.key, Converter.fromRangeOrRangeWithMessage(rangesOrOptions));
            }
            else {
                const ranges = new Array(4 * rangesOrOptions.length);
                const len = rangesOrOptions.length;
                for (let i = 0; i < len; i++) {
                    const range = rangesOrOptions[i];
                    ranges[4 * i] = range.start.line + 1;
                    ranges[4 * i + 1] = range.start.character + 1;
                    ranges[4 * i + 2] = range.end.line + 1;
                    ranges[4 * i + 3] = range.end.character + 1;
                }
                return this.proxy.$trySetDecorationsFast(this.id, decorationType.key, ranges);
            }
        });
    }
    revealRange(range, revealType) {
        this.runOnProxy(() => this.proxy.$tryRevealRange(this.id, Converter.fromRange(range), (revealType || types_impl_1.TextEditorRevealType.Default)));
    }
    applyEdit(edit) {
        const editData = edit.finalize();
        const editRanges = editData.edits.map(e => e.range);
        editRanges.sort((a, b) => {
            if (a.end.line === b.end.line) {
                if (a.end.character === b.end.character) {
                    if (a.start.line === b.start.line) {
                        return a.start.character - b.start.character;
                    }
                    return a.start.line - b.start.line;
                }
                return a.end.character - b.end.character;
            }
            return a.end.line - b.end.line;
        });
        const count = editRanges.length - 1;
        for (let i = 0; i < count; i++) {
            const rangeEnd = editRanges[i].end;
            const nextRangeStart = editRanges[i + 1].start;
            if (nextRangeStart.isBefore(rangeEnd)) {
                return Promise.reject(new Error('Overlapping ranges are not allowed!'));
            }
        }
        // prepare data for serialization
        const edits = editData.edits.map(e => ({
            range: Converter.fromRange(e.range),
            text: e.text,
            forceMoveMarkers: e.forceMoveMarkers
        }));
        return this.proxy.$tryApplyEdits(this.id, editData.documentVersionId, edits, {
            setEndOfLine: editData.setEndOfLine,
            undoStopBefore: editData.undoStopBefore,
            undoStopAfter: editData.undoStopAfter
        });
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    runOnProxy(callback) {
        if (this.disposed) {
            console.warn('TextEditor is disposed!');
            return Promise.resolve(undefined);
        }
        return callback().then(() => this, err => {
            if (!(err instanceof Error && err.name === 'DISPOSED')) {
                console.warn(err);
            }
            return undefined;
        });
    }
    show(column) {
        this.proxy.$tryShowTextDocument(this.document.uri, {
            viewColumn: column,
            preview: true,
        });
    }
    hide() {
        this.proxy.$tryHideEditor(this.id);
    }
}
exports.TextEditorExt = TextEditorExt;
class TextEditorOptionsExt {
    constructor(proxy, id, source) {
        this.proxy = proxy;
        this.id = id;
        this.accept(source);
    }
    accept(source) {
        this._tabSize = source.tabSize;
        this._indentSize = source.indentSize;
        this._insertSpace = source.insertSpaces;
        this._cursorStyle = source.cursorStyle;
        this._lineNumbers = source.lineNumbers;
    }
    get tabSize() {
        return this._tabSize;
    }
    set tabSize(val) {
        const tabSize = this.validateTabSize(val);
        if (!tabSize) {
            return; // ignore invalid values
        }
        if (typeof tabSize === 'number') {
            if (this._tabSize === tabSize) {
                return;
            }
            this._tabSize = tabSize;
        }
        warnOnError(this.proxy.$trySetOptions(this.id, {
            tabSize
        }));
    }
    validateTabSize(val) {
        if (val === 'auto') {
            return 'auto';
        }
        if (typeof val === 'number') {
            const r = Math.floor(val);
            return r > 0 ? r : undefined;
        }
        if (typeof val === 'string') {
            const r = parseInt(val, undefined);
            if (isNaN(r)) {
                return undefined;
            }
            return r > 0 ? r : undefined;
        }
        return undefined;
    }
    get indentSize() {
        if (this._indentSize === 'tabSize') {
            return this.tabSize;
        }
        return this._indentSize;
    }
    set indentSize(val) {
        const indentSize = this.validateIndentSize(val);
        if (!indentSize) {
            return; // ignore invalid values
        }
        if (typeof indentSize === 'number') {
            if (this._indentSize === indentSize) {
                return;
            }
            this._indentSize = indentSize;
        }
        else if (val === 'tabSize') {
            this._indentSize = val;
        }
        warnOnError(this.proxy.$trySetOptions(this.id, {
            indentSize
        }));
    }
    validateIndentSize(val) {
        if (val === 'tabSize') {
            return 'tabSize';
        }
        if (typeof val === 'number') {
            const r = Math.floor(val);
            return r > 0 ? r : undefined;
        }
        if (typeof val === 'string') {
            const r = parseInt(val, undefined);
            if (isNaN(r)) {
                return undefined;
            }
            return r > 0 ? r : undefined;
        }
        return undefined;
    }
    get insertSpaces() {
        return this._insertSpace;
    }
    set insertSpaces(val) {
        const insertSpaces = this.validateInsertSpaces(val);
        if (typeof insertSpaces === 'boolean') {
            if (this._insertSpace === insertSpaces) {
                return;
            }
            this._insertSpace = insertSpaces;
        }
        warnOnError(this.proxy.$trySetOptions(this.id, { insertSpaces }));
    }
    validateInsertSpaces(val) {
        if (val === 'auto') {
            return 'auto';
        }
        return val === 'false' ? false : Boolean(val);
    }
    get cursorStyle() {
        return this._cursorStyle;
    }
    set cursorStyle(val) {
        if (this._cursorStyle === val) {
            return;
        }
        this._cursorStyle = val;
        warnOnError(this.proxy.$trySetOptions(this.id, { cursorStyle: val }));
    }
    get lineNumbers() {
        return this._lineNumbers;
    }
    set lineNumbers(val) {
        if (this._lineNumbers === val) {
            return;
        }
        this._lineNumbers = val;
        warnOnError(this.proxy.$trySetOptions(this.id, { lineNumbers: val }));
    }
    assign(newOptions) {
        const configurationUpdate = {};
        let hasUpdate = false;
        if (typeof newOptions.tabSize !== 'undefined') {
            const tabSize = this.validateTabSize(newOptions.tabSize);
            if (tabSize === 'auto') {
                hasUpdate = true;
                configurationUpdate.tabSize = tabSize;
            }
            else if (typeof tabSize === 'number' && this._tabSize !== tabSize) {
                this._tabSize = tabSize;
                hasUpdate = true;
                configurationUpdate.tabSize = tabSize;
            }
        }
        if (typeof newOptions.indentSize !== 'undefined') {
            const indentSize = this.validateIndentSize(newOptions.indentSize);
            if (indentSize === 'tabSize') {
                hasUpdate = true;
                configurationUpdate.indentSize = indentSize;
            }
            else if (typeof indentSize === 'number' && this._indentSize !== indentSize) {
                // reflect the new indentSize value immediately
                this._indentSize = indentSize;
                hasUpdate = true;
                configurationUpdate.indentSize = indentSize;
            }
        }
        if (typeof newOptions.insertSpaces !== 'undefined') {
            const insertSpaces = this.validateInsertSpaces(newOptions.insertSpaces);
            if (insertSpaces === 'auto') {
                hasUpdate = true;
                configurationUpdate.insertSpaces = insertSpaces;
            }
            else if (this.insertSpaces !== insertSpaces) {
                this.insertSpaces = insertSpaces;
                hasUpdate = true;
                configurationUpdate.insertSpaces = insertSpaces;
            }
        }
        if (typeof newOptions.cursorStyle !== 'undefined') {
            if (this._cursorStyle !== newOptions.cursorStyle) {
                this._cursorStyle = newOptions.cursorStyle;
                hasUpdate = true;
                configurationUpdate.cursorStyle = newOptions.cursorStyle;
            }
        }
        if (typeof newOptions.lineNumbers !== 'undefined') {
            if (this._lineNumbers !== newOptions.lineNumbers) {
                this._lineNumbers = newOptions.lineNumbers;
                hasUpdate = true;
                configurationUpdate.lineNumbers = newOptions.lineNumbers;
            }
        }
        if (hasUpdate) {
            warnOnError(this.proxy.$trySetOptions(this.id, configurationUpdate));
        }
    }
}
exports.TextEditorOptionsExt = TextEditorOptionsExt;
class TextEditorEdit {
    constructor(document, options) {
        this.document = document;
        this.documentVersionId = document.version;
        this.collectedEdits = [];
        this.eol = 0;
        this.undoStopBefore = options.undoStopBefore;
        this.undoStopAfter = options.undoStopAfter;
    }
    finalize() {
        return {
            documentVersionId: this.documentVersionId,
            edits: this.collectedEdits,
            setEndOfLine: this.eol,
            undoStopAfter: this.undoStopAfter,
            undoStopBefore: this.undoStopBefore
        };
    }
    replace(location, val) {
        let range;
        if (location instanceof types_impl_1.Position) {
            range = new types_impl_1.Range(location, location);
        }
        else if (location instanceof types_impl_1.Range) {
            range = location;
        }
        else {
            throw new Error('Unknown location');
        }
        this.addEdit(range, val, false);
    }
    insert(location, val) {
        this.addEdit(new types_impl_1.Range(location, location), val, true);
    }
    delete(location) {
        let range;
        if (location instanceof types_impl_1.Range) {
            range = location;
        }
        else {
            throw new Error('Unknown location');
        }
        this.addEdit(range, undefined, true);
    }
    setEndOfLine(endOfLine) {
        if (endOfLine !== types_impl_1.EndOfLine.CRLF && endOfLine !== types_impl_1.EndOfLine.LF) {
            throw (0, errors_1.illegalArgument)('endOfLine');
        }
        this.eol = endOfLine;
    }
    addEdit(range, text, moveMarkers) {
        const validatedRange = this.document.validateRange(range);
        this.collectedEdits.push({
            range: validatedRange,
            forceMoveMarkers: moveMarkers,
            text: text
        });
    }
}
exports.TextEditorEdit = TextEditorEdit;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function warnOnError(promise) {
    promise.then(undefined, err => {
        console.warn(err);
    });
}
//# sourceMappingURL=text-editor.js.map