"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtHostTextEditorOptions = exports.TextEditorData = exports.ExtHostTextEditorDecorationType = exports.ExtensionHostEditorService = void 0;
exports.toIRange = toIRange;
const tslib_1 = require("tslib");
const debounce_1 = tslib_1.__importDefault(require("lodash/debounce"));
const ide_core_common_1 = require("@opensumi/ide-core-common");
const vscode_1 = require("../../../../common/vscode");
const TypeConverts = tslib_1.__importStar(require("../../../../common/vscode/converter"));
const editor_1 = require("../../../../common/vscode/editor");
const ext_types_1 = require("../../../../common/vscode/ext-types");
const edit_builder_1 = require("./edit.builder");
class ExtensionHostEditorService {
    constructor(rpcProtocol, documents) {
        this.documents = documents;
        this._editors = new Map();
        this.decorationIdCount = 0;
        this._onDidChangeActiveTextEditor = new ide_core_common_1.Emitter();
        this._onDidChangeVisibleTextEditors = new ide_core_common_1.Emitter();
        this._onDidChangeTextEditorSelection = new ide_core_common_1.Emitter();
        this._onDidChangeTextEditorVisibleRanges = new ide_core_common_1.Emitter();
        this._onDidChangeTextEditorOptions = new ide_core_common_1.Emitter();
        this._onDidChangeTextEditorViewColumn = new ide_core_common_1.Emitter();
        this.onDidChangeActiveTextEditor = this._onDidChangeActiveTextEditor.event;
        this.onDidChangeVisibleTextEditors = this._onDidChangeVisibleTextEditors.event;
        this.onDidChangeTextEditorSelection = this._onDidChangeTextEditorSelection.event;
        this.onDidChangeTextEditorVisibleRanges = this._onDidChangeTextEditorVisibleRanges.event;
        this.onDidChangeTextEditorOptions = this._onDidChangeTextEditorOptions.event;
        this.onDidChangeTextEditorViewColumn = this._onDidChangeTextEditorViewColumn.event;
        this._onEditorCreated = new ide_core_common_1.Emitter();
        this.onEditorCreated = this._onEditorCreated.event;
        this._proxy = rpcProtocol.getProxy(vscode_1.MainThreadAPIIdentifier.MainThreadEditors);
    }
    $acceptChange(change) {
        if (change.created) {
            change.created.forEach((created) => {
                this._editors.set(created.id, new TextEditorData(created, this, this.documents));
                this._onEditorCreated.fire(created.id);
                if (!change.actived && created.id === this._activeEditorId) {
                    if (this.activeEditor) {
                        this._onDidChangeActiveTextEditor.fire(this.activeEditor ? this.activeEditor.textEditor : undefined);
                    }
                }
            });
        }
        if (change.removed) {
            change.removed.forEach((id) => {
                this._editors.delete(id);
            });
        }
        if (change.actived) {
            if (change.actived === '-1') {
                this._activeEditorId = undefined;
                this._onDidChangeActiveTextEditor.fire(undefined);
            }
            else {
                this._activeEditorId = change.actived;
                if (this.activeEditor) {
                    this._onDidChangeActiveTextEditor.fire(this.activeEditor ? this.activeEditor.textEditor : undefined);
                }
            }
        }
        if (change.created || change.removed) {
            this._onDidChangeVisibleTextEditors.fire(this.visibleEditors);
        }
    }
    async openResource(uri, options) {
        const id = await this._proxy.$openResource(uri.toString(), options);
        if (this.getEditor(id)) {
            return this.getEditor(id).textEditor;
        }
        else {
            return new Promise((resolve, reject) => {
                let resolved = false;
                const disposer = this.onEditorCreated((created) => {
                    if (created === id && this.getEditor(id)) {
                        resolve(this.getEditor(id).textEditor);
                        resolved = true;
                        disposer.dispose();
                    }
                });
                setTimeout(() => {
                    if (!resolved) {
                        reject(new Error(`Timout opening textDocument uri ${uri.toString()}`));
                    }
                }, 5000);
            });
        }
    }
    async showTextDocument(documentOrUri, columnOrOptions, preserveFocus) {
        let uri;
        if (ext_types_1.Uri.isUri(documentOrUri)) {
            uri = documentOrUri;
        }
        else {
            uri = documentOrUri.uri;
        }
        let options;
        if (typeof columnOrOptions === 'number') {
            options = {
                ...TypeConverts.viewColumnToResourceOpenOptions(columnOrOptions),
                preserveFocus,
            };
        }
        else if (typeof columnOrOptions === 'object') {
            options = {
                ...TypeConverts.viewColumnToResourceOpenOptions(columnOrOptions.viewColumn),
                preserveFocus: columnOrOptions.preserveFocus,
                range: typeof columnOrOptions.selection === 'object'
                    ? TypeConverts.Range.from(columnOrOptions.selection)
                    : undefined,
                preview: typeof columnOrOptions.preview === 'boolean' ? columnOrOptions.preview : undefined,
            };
        }
        else {
            options = {
                preserveFocus: false,
            };
        }
        return this.openResource(uri, options);
    }
    $acceptPropertiesChanges(changes) {
        for (const change of changes) {
            if (this._editors.get(change.id)) {
                this._editors.get(change.id).acceptStatusChange(change);
            }
        }
    }
    getEditor(id) {
        return this._editors.get(id);
    }
    get activeEditor() {
        if (!this._activeEditorId) {
            return undefined;
        }
        else {
            return this._editors.get(this._activeEditorId);
        }
    }
    get visibleEditors() {
        return Array.from(this._editors.values()).map((e) => e.textEditor);
    }
    closeEditor(editor) {
        if (editor.id !== this._activeEditorId) {
            return;
        }
        this._proxy.$closeEditor(editor.id);
    }
    getNextId() {
        this.decorationIdCount++;
        return 'textEditor-decoration-' + this.decorationIdCount;
    }
    createTextEditorDecorationType(extensionId, options) {
        const resolved = TypeConverts.DecorationRenderOptions.from(options);
        // 添加 extensionId 以更好定位是哪个插件创建的decoration
        const key = extensionId.replace(/\./g, '-') + '-' + this.getNextId();
        this._proxy.$createTextEditorDecorationType(key, resolved);
        return new ExtHostTextEditorDecorationType(key, this._proxy);
    }
    async getDiffInformation(id) {
        const lineChanges = await this._proxy.$getDiffInformation(id);
        return lineChanges.map((change) => ({
            originalStartLineNumber: change[0],
            originalEndLineNumber: change[1],
            modifiedStartLineNumber: change[2],
            modifiedEndLineNumber: change[3],
        }));
    }
}
exports.ExtensionHostEditorService = ExtensionHostEditorService;
class ExtHostTextEditorDecorationType extends ide_core_common_1.Disposable {
    constructor(key, _proxy) {
        super();
        this.key = key;
        this.addDispose({
            dispose: () => {
                _proxy.$deleteTextEditorDecorationType(key);
            },
        });
    }
}
exports.ExtHostTextEditorDecorationType = ExtHostTextEditorDecorationType;
class TextEditorData {
    constructor(created, editorService, documents) {
        this.editorService = editorService;
        this.documents = documents;
        this.doSetSelection = (0, debounce_1.default)(() => {
            this.editorService._proxy.$setSelections(this.id, this.selections.map((selection) => TypeConverts.Selection.from(selection)));
        }, 50, { maxWait: 200, leading: true, trailing: true });
        this.uri = ext_types_1.Uri.parse(created.uri);
        this.id = created.id;
        this._acceptSelections(created.selections);
        this._acceptVisibleRanges(created.visibleRanges);
        this._acceptViewColumn(created.viewColumn);
        this.options = new ExtHostTextEditorOptions(this.editorService._proxy, this.id, created.options);
    }
    edit(callback, options = { undoStopBefore: true, undoStopAfter: true }) {
        const document = this.documents.getDocument(this.uri);
        if (!document) {
            throw new Error('document not found when editing');
        }
        const edit = new edit_builder_1.TextEditorEdit(document, options);
        callback(edit);
        return this._applyEdit(edit);
    }
    _applyEdit(editBuilder) {
        const editData = editBuilder.finalize();
        // return when there is nothing to do
        if (editData.edits.length === 0 && !editData.setEndOfLine) {
            return Promise.resolve(true);
        }
        // check that the edits are not overlapping (i.e. illegal)
        const editRanges = editData.edits.map((edit) => edit.range);
        // sort ascending (by end and then by start)
        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;
        });
        // check that no edits are overlapping
        for (let i = 0, count = editRanges.length - 1; i < count; i++) {
            const rangeEnd = editRanges[i].end;
            const nextRangeStart = editRanges[i + 1].start;
            if (nextRangeStart.isBefore(rangeEnd)) {
                // overlapping ranges
                return Promise.reject(new Error('Overlapping ranges are not allowed!'));
            }
        }
        // prepare data for serialization
        const edits = editData.edits.map((edit) => ({
            range: TypeConverts.Range.from(edit.range),
            text: edit.text,
            forceMoveMarkers: edit.forceMoveMarkers,
        }));
        return this.editorService._proxy.$applyEdits(this.id, editData.documentVersionId, edits, {
            setEndOfLine: typeof editData.setEndOfLine === 'number' ? TypeConverts.EndOfLine.from(editData.setEndOfLine) : undefined,
            undoStopBefore: editData.undoStopBefore,
            undoStopAfter: editData.undoStopAfter,
        });
    }
    async insertSnippet(snippet, location, options) {
        try {
            let _location = [];
            if (!location || (Array.isArray(location) && location.length === 0)) {
                _location = this.selections.map((s) => TypeConverts.Range.from(s));
            }
            else if (location instanceof ext_types_1.Position) {
                const { lineNumber, column } = TypeConverts.fromPosition(location);
                _location = [
                    { startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column },
                ];
            }
            else if (location instanceof ext_types_1.Range) {
                _location = [TypeConverts.Range.from(location)];
            }
            else {
                _location = [];
                for (const posOrRange of location) {
                    if (posOrRange instanceof ext_types_1.Range) {
                        _location.push(TypeConverts.Range.from(posOrRange));
                    }
                    else {
                        const { lineNumber, column } = TypeConverts.fromPosition(posOrRange);
                        _location.push({
                            startLineNumber: lineNumber,
                            startColumn: column,
                            endLineNumber: lineNumber,
                            endColumn: column,
                        });
                    }
                }
            }
            this.editorService._proxy.$insertSnippet(this.id, snippet.value, _location, options);
            return true;
        }
        catch (e) {
            (0, ide_core_common_1.getDebugLogger)().error(e);
            return false;
        }
    }
    setDecorations(decorationType, rangesOrOptions) {
        if (decorationType.disposed) {
            (0, ide_core_common_1.getDebugLogger)().warn(`decorationType with key ${decorationType.key} has been disposed!`);
            return;
        }
        let resolved = [];
        if (rangesOrOptions.length !== 0) {
            if (ext_types_1.Range.isRange(rangesOrOptions[0])) {
                resolved = rangesOrOptions.map((r) => ({
                    range: TypeConverts.Range.from(r),
                }));
            }
            else if (ext_types_1.Range.isRange(rangesOrOptions[0].range)) {
                resolved = rangesOrOptions.map((r) => ({
                    range: TypeConverts.Range.from(r.range),
                    renderOptions: r.renderOptions ? TypeConverts.DecorationRenderOptions.from(r.renderOptions) : undefined,
                    hoverMessage: r.hoverMessage,
                }));
            }
        }
        this.editorService._proxy.$applyDecoration(this.id, decorationType.key, resolved);
    }
    revealRange(range, revealType) {
        this.editorService._proxy.$revealRange(this.id, TypeConverts.Range.from(range), revealType);
    }
    show(column) {
        (0, ide_core_common_1.getDebugLogger)().warn('TextEditor.show is Deprecated');
    }
    hide() {
        this.editorService.closeEditor(this);
    }
    _acceptSelections(selections = []) {
        this.selections = selections.map((selection) => TypeConverts.Selection.to(selection));
    }
    _acceptOptions(options) {
        this.options._accept(options);
    }
    _acceptVisibleRanges(value) {
        this.visibleRanges = value.map((v) => TypeConverts.Range.to(v)).filter((v) => !!v);
    }
    _acceptViewColumn(value) {
        this.viewColumn = value;
    }
    acceptStatusChange(change) {
        if (change.selections) {
            this._acceptSelections(change.selections.selections);
            this.editorService._onDidChangeTextEditorSelection.fire({
                kind: editor_1.TextEditorSelectionChangeKind.fromValue(change.selections.source),
                selections: this.selections,
                textEditor: this.textEditor,
            });
        }
        if (change.options) {
            this._acceptOptions(change.options);
            this.editorService._onDidChangeTextEditorOptions.fire({
                textEditor: this.textEditor,
                options: this.options,
            });
        }
        if (change.visibleRanges) {
            this._acceptVisibleRanges(change.visibleRanges);
            this.editorService._onDidChangeTextEditorVisibleRanges.fire({
                textEditor: this.textEditor,
                visibleRanges: this.visibleRanges,
            });
        }
        if (change.viewColumn) {
            this._acceptViewColumn(change.viewColumn);
            this.editorService._onDidChangeTextEditorViewColumn.fire({
                textEditor: this.textEditor,
                viewColumn: this.viewColumn,
            });
        }
    }
    get textEditor() {
        if (!this._textEditor) {
            const data = this;
            this._textEditor = {
                // vscode 有这个属性，但是接口未定义
                get id() {
                    return data.id;
                },
                get document() {
                    return data.documents.getDocument(data.uri);
                },
                set selection(val) {
                    data.selections = [val];
                    data.doSetSelection();
                },
                get selections() {
                    return data.selections;
                },
                set selections(val) {
                    data.selections = val;
                    data.editorService._proxy.$setSelections(data.id, data.selections.map((selection) => TypeConverts.Selection.from(selection)));
                },
                get selection() {
                    return data.selections && data.selections[0];
                },
                get options() {
                    return data.options;
                },
                get visibleRanges() {
                    return data.visibleRanges;
                },
                get viewColumn() {
                    return data.viewColumn;
                },
                set options(value) {
                    data.options.assign(value);
                    //
                },
                edit: data.edit.bind(data),
                insertSnippet: data.insertSnippet.bind(data),
                setDecorations: data.setDecorations.bind(data),
                revealRange: data.revealRange.bind(data),
                show: data.show.bind(data),
                hide: data.hide.bind(data),
            };
        }
        return this._textEditor;
    }
}
exports.TextEditorData = TextEditorData;
function toIRange(range) {
    if (ext_types_1.Range.isRange(range)) {
        // vscode.Range
        return TypeConverts.Range.from(range);
    }
    else if (ext_types_1.Selection.isSelection(range)) {
        // vscode.Selection
        const r = range;
        if (r.active.isBeforeOrEqual(r.anchor)) {
            return {
                startLineNumber: r.active.line + 1,
                startColumn: r.active.character + 1,
                endLineNumber: r.anchor.line + 1,
                endColumn: r.anchor.character + 1,
            };
        }
        else {
            return {
                startLineNumber: r.anchor.line + 1,
                startColumn: r.anchor.character + 1,
                endLineNumber: r.active.line + 1,
                endColumn: r.active.character + 1,
            };
        }
    }
    else if (ext_types_1.Position.isPosition(range)) {
        const r = range;
        return {
            startLineNumber: r.line + 1,
            startColumn: r.character + 1,
            endLineNumber: r.line + 1,
            endColumn: r.character + 1,
        };
    }
    return {
        startLineNumber: 1,
        startColumn: 1,
        endLineNumber: 1,
        endColumn: 1,
    };
}
class ExtHostTextEditorOptions {
    constructor(proxy, id, source) {
        this._proxy = proxy;
        this._id = id;
        this._accept(source);
    }
    _accept(source) {
        this._tabSize = source.tabSize;
        this._indentSize = source.indentSize;
        this._insertSpaces = source.insertSpaces;
        this._cursorStyle = source.cursorStyle;
        this._lineNumbers = TypeConverts.TextEditorLineNumbersStyle.to(source.lineNumbers);
    }
    get tabSize() {
        return this._tabSize;
    }
    set tabSize(value) {
        const tabSize = this._validateTabSize(value);
        if (tabSize === null) {
            // ignore invalid call
            return;
        }
        if (typeof tabSize === 'number') {
            if (this._tabSize === tabSize) {
                // nothing to do
                return;
            }
            // reflect the new tabSize value immediately
            this._tabSize = tabSize;
        }
        this._proxy.$updateOptions(this._id, {
            tabSize,
        });
    }
    _validateTabSize(value) {
        if (value === 'auto') {
            return 'auto';
        }
        if (typeof value === 'number') {
            const r = Math.floor(value);
            return r > 0 ? r : null;
        }
        if (typeof value === 'string') {
            const r = parseInt(value, 10);
            if (isNaN(r)) {
                return null;
            }
            return r > 0 ? r : null;
        }
        return null;
    }
    get indentSize() {
        return this._indentSize;
    }
    set indentSize(value) {
        const indentSize = this._validateIndentSize(value);
        if (indentSize === null) {
            // ignore invalid call
            return;
        }
        if (typeof indentSize === 'number') {
            if (this._indentSize === indentSize) {
                // nothing to do
                return;
            }
            // reflect the new indentSize value immediately
            this._indentSize = indentSize;
        }
        this._proxy.$updateOptions(this._id, {
            indentSize,
        });
    }
    _validateIndentSize(value) {
        if (value === 'tabSize') {
            return 'tabSize';
        }
        if (typeof value === 'number') {
            const r = Math.floor(value);
            return r > 0 ? r : null;
        }
        if (typeof value === 'string') {
            const r = parseInt(value, 10);
            if (isNaN(r)) {
                return null;
            }
            return r > 0 ? r : null;
        }
        return null;
    }
    get insertSpaces() {
        return this._insertSpaces;
    }
    set insertSpaces(value) {
        const insertSpaces = this._validateInsertSpaces(value);
        if (typeof insertSpaces === 'boolean') {
            if (this._insertSpaces === insertSpaces) {
                // nothing to do
                return;
            }
            // reflect the new insertSpaces value immediately
            this._insertSpaces = insertSpaces;
        }
        this._proxy.$updateOptions(this._id, {
            insertSpaces,
        });
    }
    _validateInsertSpaces(value) {
        if (value === 'auto') {
            return 'auto';
        }
        return value === 'false' ? false : Boolean(value);
    }
    get cursorStyle() {
        return this._cursorStyle;
    }
    set cursorStyle(value) {
        if (this._cursorStyle === value) {
            // nothing to do
            return;
        }
        this._cursorStyle = value;
        this._proxy.$updateOptions(this._id, {
            cursorStyle: value,
        });
    }
    get lineNumbers() {
        return this._lineNumbers;
    }
    set lineNumbers(value) {
        if (this._lineNumbers === value) {
            // nothing to do
            return;
        }
        this._lineNumbers = value;
        this._proxy.$updateOptions(this._id, {
            lineNumbers: TypeConverts.TextEditorLineNumbersStyle.from(value),
        });
    }
    assign(newOptions) {
        const bulkConfigurationUpdate = {};
        let hasUpdate = false;
        if (typeof newOptions.tabSize !== 'undefined') {
            const tabSize = this._validateTabSize(newOptions.tabSize);
            if (tabSize === 'auto') {
                hasUpdate = true;
                bulkConfigurationUpdate.tabSize = tabSize;
            }
            else if (typeof tabSize === 'number' && this._tabSize !== tabSize) {
                // reflect the new tabSize value immediately
                this._tabSize = tabSize;
                hasUpdate = true;
                bulkConfigurationUpdate.tabSize = tabSize;
            }
        }
        // if (typeof newOptions.indentSize !== 'undefined') {
        // 	const indentSize = this._validateIndentSize(newOptions.indentSize);
        // 	if (indentSize === 'tabSize') {
        // 		hasUpdate = true;
        // 		bulkConfigurationUpdate.indentSize = indentSize;
        // 	} else if (typeof indentSize === 'number' && this._indentSize !== indentSize) {
        // 		// reflect the new indentSize value immediately
        // 		this._indentSize = indentSize;
        // 		hasUpdate = true;
        // 		bulkConfigurationUpdate.indentSize = indentSize;
        // 	}
        // }
        if (typeof newOptions.insertSpaces !== 'undefined') {
            const insertSpaces = this._validateInsertSpaces(newOptions.insertSpaces);
            if (insertSpaces === 'auto') {
                hasUpdate = true;
                bulkConfigurationUpdate.insertSpaces = insertSpaces;
            }
            else if (this._insertSpaces !== insertSpaces) {
                // reflect the new insertSpaces value immediately
                this._insertSpaces = insertSpaces;
                hasUpdate = true;
                bulkConfigurationUpdate.insertSpaces = insertSpaces;
            }
        }
        if (typeof newOptions.cursorStyle !== 'undefined') {
            if (this._cursorStyle !== newOptions.cursorStyle) {
                this._cursorStyle = newOptions.cursorStyle;
                hasUpdate = true;
                bulkConfigurationUpdate.cursorStyle = newOptions.cursorStyle;
            }
        }
        if (typeof newOptions.lineNumbers !== 'undefined') {
            if (this._lineNumbers !== newOptions.lineNumbers) {
                this._lineNumbers = newOptions.lineNumbers;
                hasUpdate = true;
                bulkConfigurationUpdate.lineNumbers = TypeConverts.TextEditorLineNumbersStyle.from(newOptions.lineNumbers);
            }
        }
        if (hasUpdate) {
            this._proxy.$updateOptions(this._id, bulkConfigurationUpdate);
        }
    }
}
exports.ExtHostTextEditorOptions = ExtHostTextEditorOptions;
//# sourceMappingURL=editor.host.js.map