"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MainThreadEditorService = void 0;
exports.cursorStyleToString = cursorStyleToString;
const tslib_1 = require("tslib");
const merge_1 = tslib_1.__importDefault(require("lodash/merge"));
const throttle_1 = tslib_1.__importDefault(require("lodash/throttle"));
const di_1 = require("@opensumi/di");
const ide_connection_1 = require("@opensumi/ide-connection");
const static_resource_1 = require("@opensumi/ide-core-browser/lib/static-resource");
const ide_core_common_1 = require("@opensumi/ide-core-common");
const ide_editor_1 = require("@opensumi/ide-editor");
const browser_1 = require("@opensumi/ide-editor/lib/browser");
const editor_collection_service_1 = require("@opensumi/ide-editor/lib/browser/editor-collection.service");
const workbench_editor_service_1 = require("@opensumi/ide-editor/lib/browser/workbench-editor.service");
const monaco = tslib_1.__importStar(require("@opensumi/ide-monaco"));
const model_1 = require("@opensumi/monaco-editor-core/esm/vs/editor/common/services/model");
const standaloneServices_1 = require("@opensumi/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices");
const vscode_1 = require("../../../common/vscode");
const converter_1 = require("../../../common/vscode/converter");
const main_thread_doc_1 = require("./main.thread.doc");
let MainThreadEditorService = class MainThreadEditorService extends ide_core_common_1.WithEventBus {
    constructor(rpcProtocol, documents) {
        super();
        this.rpcProtocol = rpcProtocol;
        this.documents = documents;
        this.propertiesChangeCache = new Map();
        this.triggerPropertiesChange = (0, throttle_1.default)(() => {
            const changes = [];
            this.propertiesChangeCache.forEach((change) => {
                changes.push(change);
            });
            this.propertiesChangeCache.clear();
            this.proxy.$acceptPropertiesChanges(changes);
        }, 16, {
            leading: true,
            trailing: true,
        });
        this.proxy = this.rpcProtocol.getProxy(vscode_1.ExtHostAPIIdentifier.ExtHostEditors);
        this.$getInitialState().then((change) => {
            this.proxy.$acceptChange(change);
        });
    }
    async $getInitialState() {
        this.startEvents();
        const editors = this.editorService.editorGroups
            .map((group) => {
            if (group.currentOpenType && isEditor(group.currentOpenType)) {
                const editor = group.currentEditor;
                if (!editor.currentDocumentModel) {
                    return undefined;
                }
                if (!this.documents.isDocSyncEnabled(editor.currentDocumentModel.uri)) {
                    return undefined;
                }
                return {
                    id: getTextEditorId(group, group.currentResource.uri),
                    uri: editor.currentDocumentModel.uri.toString(),
                    selections: editor.getSelections() || [],
                    options: getEditorOption(editor.monacoEditor),
                    viewColumn: getViewColumn(group),
                    visibleRanges: editor.monacoEditor.getVisibleRanges(),
                };
            }
        })
            .filter((c) => !!c);
        const activedEditor = this.editorService.currentResource &&
            editors.find((e) => e.uri === this.editorService.currentResource.uri.toString());
        return {
            created: editors,
            actived: activedEditor && this.documents.isDocSyncEnabled(activedEditor.uri) && activedEditor.id,
        };
    }
    async $closeEditor(id) {
        const editor = this.getEditor(id);
        if (editor) {
            const group = this.getGroup(id);
            group.close(group.currentResource.uri);
        }
    }
    async $revealRange(id, range, type) {
        const editor = this.getEditor(id);
        if (editor) {
            switch (type) {
                case vscode_1.TextEditorRevealType.InCenter:
                    return editor.monacoEditor.revealRangeInCenter(range);
                case vscode_1.TextEditorRevealType.AtTop:
                    return editor.monacoEditor.revealRangeAtTop(range);
                case vscode_1.TextEditorRevealType.InCenterIfOutsideViewport:
                    return editor.monacoEditor.revealRangeInCenterIfOutsideViewport(range);
                default:
                    return editor.monacoEditor.revealRange(range);
            }
        }
    }
    async $createTextEditorDecorationType(key, options) {
        this.resolveIconPaths(options);
        this.resolveIconPaths(options.dark);
        this.resolveIconPaths(options.light);
        this.decorationService.createTextEditorDecorationType(options, key);
    }
    resolveIconPaths(options) {
        if (!options) {
            return options;
        }
        if (options.gutterIconPath) {
            let uri;
            if (typeof options.gutterIconPath === 'string') {
                uri = ide_core_common_1.URI.file(options.gutterIconPath);
            }
            else {
                uri = ide_core_common_1.URI.from(options.gutterIconPath);
            }
            options.gutterIconPath = this.staticResourceService.resolveStaticResource(uri).toString();
        }
    }
    async $deleteTextEditorDecorationType(key) {
        const type = this.decorationService.getTextEditorDecorationType(key);
        if (type) {
            type.dispose();
        }
    }
    async $applyDecoration(id, decorationKey, options) {
        const editor = this.getEditor(id);
        if (editor) {
            editor.applyDecoration(decorationKey, options);
        }
    }
    async $insertSnippet(id, snippet, ranges = [], options = { undoStopAfter: true, undoStopBefore: true }) {
        const editor = this.getEditor(id);
        if (editor) {
            editor.insertSnippet(snippet, ranges, options);
        }
    }
    async $updateOptions(id, update) {
        const editor = this.getEditor(id);
        if (editor) {
            await this.setConfiguration(editor.monacoEditor, update);
        }
    }
    getEditor(id) {
        var _a, _b;
        const group = this.getGroup(id);
        if (!group) {
            return;
        }
        const currentResource = group.currentResource;
        if (currentResource && group.currentOpenType && isEditor(group.currentOpenType)) {
            if (id === getTextEditorId(group, currentResource.uri)) {
                return group.currentEditor;
            }
            if (((_a = group.currentOpenType) === null || _a === void 0 ? void 0 : _a.type) === browser_1.EditorOpenType.diff &&
                id === getTextEditorId(group, currentResource.metadata.original, 'original')) {
                return group.diffEditor.originalEditor;
            }
            if (((_b = group.currentOpenType) === null || _b === void 0 ? void 0 : _b.type) === browser_1.EditorOpenType.diff &&
                id === getTextEditorId(group, currentResource.metadata.modified, 'modified')) {
                return group.diffEditor.modifiedEditor;
            }
        }
    }
    getGroup(id) {
        const groupId = getGroupIdFromTextEditorId(id);
        const group = this.editorService.getEditorGroup(groupId);
        return group;
    }
    /**
     * 按 id 缓存 change, 每次 change 都会合并到缓存中，debounce 发送给插件进程
     */
    batchPropertiesChanges(change) {
        const { id } = change;
        let propertiesChange = this.propertiesChangeCache.get(id);
        if (!propertiesChange) {
            propertiesChange = {};
        }
        propertiesChange = (0, merge_1.default)(propertiesChange, change);
        this.propertiesChangeCache.set(id, propertiesChange);
        this.triggerPropertiesChange();
    }
    startEvents() {
        this.addDispose([
            this.eventBus.on(browser_1.EditorGroupChangeEvent, (event) => {
                const payload = event.payload;
                if (!resourceEquals(payload.newResource, payload.oldResource) ||
                    !openTypeEquals(payload.newOpenType, payload.oldOpenType)) {
                    const change = {};
                    if (payload.newOpenType &&
                        (payload.newOpenType.type === browser_1.EditorOpenType.code || payload.newOpenType.type === browser_1.EditorOpenType.diff)) {
                        const editor = payload.group.currentEditor;
                        if (!editor.currentDocumentModel) {
                            // noop
                        }
                        else if (!this.documents.isDocSyncEnabled(editor.currentDocumentModel.uri)) {
                            // noop
                        }
                        else {
                            change.created = [];
                            if (payload.newOpenType.type === browser_1.EditorOpenType.diff) {
                                const diffOriginalEditor = payload.group.diffEditor.originalEditor;
                                const diffMorifidedEditor = payload.group.diffEditor.modifiedEditor;
                                const originalEditorId = getTextEditorId(payload.group, payload.newResource.metadata.original, 'original');
                                change.created.push({
                                    id: originalEditorId,
                                    uri: diffOriginalEditor.currentUri.toString(),
                                    selections: diffOriginalEditor.getSelections() || [],
                                    options: getEditorOption(diffOriginalEditor.monacoEditor),
                                    viewColumn: getViewColumn(payload.group),
                                    visibleRanges: diffOriginalEditor.monacoEditor.getVisibleRanges(),
                                });
                                const modifiedEditorId = getTextEditorId(payload.group, payload.newResource.metadata.modified, 'modified');
                                change.created.push({
                                    id: modifiedEditorId,
                                    uri: diffMorifidedEditor.currentUri.toString(),
                                    selections: diffMorifidedEditor.getSelections() || [],
                                    options: getEditorOption(diffMorifidedEditor.monacoEditor),
                                    viewColumn: getViewColumn(payload.group),
                                    visibleRanges: diffMorifidedEditor.monacoEditor.getVisibleRanges(),
                                });
                            }
                            else {
                                change.created.push({
                                    id: getTextEditorId(payload.group, payload.newResource.uri),
                                    uri: editor.currentDocumentModel.uri.toString(),
                                    selections: editor.getSelections() || [],
                                    options: getEditorOption(editor.monacoEditor),
                                    viewColumn: getViewColumn(payload.group),
                                    visibleRanges: editor.monacoEditor.getVisibleRanges(),
                                });
                            }
                        }
                        // 来自切换打开类型
                        if (resourceEquals(payload.newResource, payload.oldResource) &&
                            !openTypeEquals(payload.newOpenType, payload.oldOpenType) &&
                            payload.newResource === this.editorService.currentResource) {
                            change.actived = getTextEditorId(payload.group, payload.newResource.uri);
                        }
                    }
                    if (payload.oldOpenType &&
                        (payload.oldOpenType.type === browser_1.EditorOpenType.code || payload.oldOpenType.type === browser_1.EditorOpenType.diff)) {
                        change.removed = [];
                        if (payload.oldOpenType.type === browser_1.EditorOpenType.diff) {
                            change.removed.push(getTextEditorId(payload.group, payload.oldResource.metadata.original, 'original'));
                            change.removed.push(getTextEditorId(payload.group, payload.oldResource.metadata.modified, 'modified'));
                        }
                        else {
                            change.removed = [getTextEditorId(payload.group, payload.oldResource.uri)];
                        }
                    }
                    this.proxy.$acceptChange(change);
                }
            }),
            this.editorService.onActiveEditorUriChange((uri) => {
                if (uri &&
                    this.editorService.currentEditorGroup &&
                    isEditor(this.editorService.currentEditorGroup.currentOpenType)) {
                    this.acceptCurrentEditor(uri);
                }
                else {
                    this.proxy.$acceptChange({
                        actived: '-1',
                    });
                }
            }),
            this.eventBus.on(browser_1.EditorConfigurationChangedEvent, (e) => {
                const editorId = getTextEditorId(e.payload.group, e.payload.resource.uri);
                if (e.payload.group.currentEditor && e.payload.group.currentEditor.monacoEditor.getModel()) {
                    this.batchPropertiesChanges({
                        id: editorId,
                        options: getEditorOption(e.payload.group.currentEditor.monacoEditor),
                    });
                }
            }),
            this.eventBus.on(browser_1.EditorSelectionChangeEvent, (e) => {
                const editorId = getTextEditorId(e.payload.group, e.payload.editorUri, e.payload.side);
                this.batchPropertiesChanges({
                    id: editorId,
                    selections: {
                        selections: e.payload.selections,
                        source: e.payload.source,
                    },
                });
                this.acceptCurrentEditor(e.payload.editorUri);
            }),
            this.eventBus.on(browser_1.EditorVisibleChangeEvent, (e) => {
                const editorId = getTextEditorId(e.payload.group, e.payload.resource.uri);
                this.batchPropertiesChanges({
                    id: editorId,
                    visibleRanges: e.payload.visibleRanges,
                });
            }),
            this.eventBus.on(browser_1.EditorGroupIndexChangedEvent, (e) => {
                if (isGroupEditorState(e.payload.group)) {
                    const editorId = getTextEditorId(e.payload.group, e.payload.group.currentResource.uri);
                    this.batchPropertiesChanges({
                        id: editorId,
                        viewColumn: getViewColumn(e.payload.group),
                    });
                }
            }),
        ]);
    }
    acceptCurrentEditor(uri) {
        var _a, _b, _c, _d, _e;
        let side;
        const isDiffOriginal = ((_a = this.editorService.currentEditorGroup.currentOpenType) === null || _a === void 0 ? void 0 : _a.type) === browser_1.EditorOpenType.diff &&
            ((_b = this.editorService.currentEditorGroup.diffEditor.originalEditor.currentUri) === null || _b === void 0 ? void 0 : _b.isEqual(uri));
        const isDiffMorified = ((_c = this.editorService.currentEditorGroup.currentOpenType) === null || _c === void 0 ? void 0 : _c.type) === browser_1.EditorOpenType.diff &&
            ((_d = this.editorService.currentEditorGroup.diffEditor.modifiedEditor.currentUri) === null || _d === void 0 ? void 0 : _d.isEqual(uri));
        if (isDiffOriginal) {
            side = 'original';
        }
        if (isDiffMorified) {
            side = 'modified';
        }
        if (side) {
            this.proxy.$acceptChange({
                actived: getTextEditorId(this.editorService.currentEditorGroup, uri, side),
            });
        }
        else {
            // 这里 id 还是兼容旧逻辑不做改动
            this.proxy.$acceptChange({
                actived: getTextEditorId(this.editorService.currentEditorGroup, (_e = this.editorService.currentEditorGroup.currentResource) === null || _e === void 0 ? void 0 : _e.uri),
            });
        }
    }
    async $applyEdits(id, documentVersionId, edits, options) {
        const editor = this.getEditor(id);
        if (editor && editor.currentDocumentModel) {
            const model = editor.currentDocumentModel.getMonacoModel();
            if (model && model.getVersionId() === documentVersionId) {
                if (typeof options.setEndOfLine !== 'undefined') {
                    model.pushEOL(options.setEndOfLine);
                }
                const transformedEdits = edits.map((edit) => ({
                    range: monaco.Range.lift(edit.range),
                    text: edit.text,
                    forceMoveMarkers: edit.forceMoveMarkers,
                }));
                if (options.undoStopBefore) {
                    editor.monacoEditor.pushUndoStop();
                }
                editor.monacoEditor.executeEdits('MainThreadTextEditor', transformedEdits);
                if (options.undoStopAfter) {
                    editor.monacoEditor.pushUndoStop();
                }
                return true;
            }
            else {
                return false;
            }
        }
        else {
            return false;
        }
    }
    async $openResource(uri, options) {
        options.forceOpenType = { type: browser_1.EditorOpenType.code };
        options.focus = true;
        options = {
            ...options,
            ...(0, converter_1.viewColumnToResourceOpenOptions)(options.viewColumn),
        };
        const result = await this.editorService.open(new ide_core_common_1.URI(uri), options);
        if (result) {
            return getTextEditorId(result.group, result.resource.uri);
        }
        throw new Error('Editor Open uri ' + uri.toString() + ' Failed');
    }
    $setSelections(id, selections) {
        if (!this.getEditor(id)) {
            return Promise.reject(`No Such TextEditor: ${id}`);
        }
        this.getEditor(id).setSelections(selections);
        return Promise.resolve();
    }
    setConfiguration(codeEditor, newConfiguration) {
        if (codeEditor.getModel()) {
            this._setIndentConfiguration(codeEditor.getModel(), newConfiguration);
        }
        if (!codeEditor) {
            return;
        }
        if (newConfiguration.cursorStyle) {
            const newCursorStyle = cursorStyleToString(newConfiguration.cursorStyle);
            codeEditor.updateOptions({
                cursorStyle: newCursorStyle,
            });
        }
        if (typeof newConfiguration.lineNumbers !== 'undefined') {
            let lineNumbers;
            switch (newConfiguration.lineNumbers) {
                case 1 /* RenderLineNumbersType.On */:
                    lineNumbers = 'on';
                    break;
                case 2 /* RenderLineNumbersType.Relative */:
                    lineNumbers = 'relative';
                    break;
                case 3 /* RenderLineNumbersType.Interval */:
                    lineNumbers = 'interval';
                    break;
                default:
                    lineNumbers = 'off';
            }
            codeEditor.updateOptions({
                lineNumbers,
            });
        }
    }
    $getDiffInformation(id) {
        const editor = this.getEditor(id);
        if (!editor) {
            return Promise.reject(new Error('No such TextEditor'));
        }
        const codeEditor = editor.monacoEditor;
        if (!codeEditor) {
            return Promise.reject(new Error('No such CodeEditor'));
        }
        const codeEditorId = codeEditor.getId();
        const diffEditors = this.codeEditorService.listDiffEditors();
        const [diffEditor] = diffEditors.filter((d) => d.originalEditor.getId() === codeEditorId || d.modifiedEditor.getId() === codeEditorId);
        if (diffEditor) {
            const diffChanges = diffEditor.monacoDiffEditor.getLineChanges();
            if (!diffChanges) {
                return Promise.resolve([]);
            }
            return Promise.resolve(diffChanges.map((change) => {
                var _a;
                return [
                    change.originalStartLineNumber,
                    change.originalEndLineNumber,
                    change.modifiedStartLineNumber,
                    change.modifiedEndLineNumber,
                    (_a = change.charChanges) === null || _a === void 0 ? void 0 : _a.map((charChange) => [
                        charChange.originalStartLineNumber,
                        charChange.originalStartColumn,
                        charChange.originalEndLineNumber,
                        charChange.originalEndColumn,
                        charChange.modifiedStartLineNumber,
                        charChange.modifiedStartColumn,
                        charChange.modifiedEndLineNumber,
                        charChange.modifiedEndColumn,
                    ]),
                ];
            }));
        }
        const dirtyDiffContribution = codeEditor.getContribution('editor.contrib.dirtydiff');
        if (dirtyDiffContribution) {
            return Promise.resolve(dirtyDiffContribution.getChanges());
        }
        return Promise.resolve([]);
    }
    _setIndentConfiguration(model, newConfiguration) {
        const creationOpts = standaloneServices_1.StandaloneServices.get(model_1.IModelService).getCreationOptions(model.getLanguageId(), model.uri, model.isForSimpleWidget);
        if (newConfiguration.tabSize === 'auto' || newConfiguration.insertSpaces === 'auto') {
            // one of the options was set to 'auto' => detect indentation
            let insertSpaces = creationOpts.insertSpaces;
            let tabSize = creationOpts.tabSize;
            if (newConfiguration.insertSpaces !== 'auto' && typeof newConfiguration.insertSpaces !== 'undefined') {
                insertSpaces = newConfiguration.insertSpaces;
            }
            if (newConfiguration.tabSize !== 'auto' && typeof newConfiguration.tabSize !== 'undefined') {
                tabSize = newConfiguration.tabSize;
            }
            model.detectIndentation(insertSpaces, tabSize);
            return;
        }
        const newOpts = {};
        if (typeof newConfiguration.insertSpaces !== 'undefined') {
            newOpts.insertSpaces = newConfiguration.insertSpaces;
        }
        if (typeof newConfiguration.tabSize !== 'undefined') {
            newOpts.tabSize = newConfiguration.tabSize;
        }
        if (typeof newConfiguration.indentSize !== 'undefined') {
            if (newConfiguration.indentSize === 'tabSize') {
                newOpts.indentSize = newOpts.tabSize || creationOpts.tabSize;
            }
            else {
                newOpts.indentSize = newConfiguration.indentSize;
            }
        }
        model.updateOptions(newOpts);
    }
};
exports.MainThreadEditorService = MainThreadEditorService;
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_editor_1.WorkbenchEditorService),
    tslib_1.__metadata("design:type", workbench_editor_service_1.WorkbenchEditorServiceImpl)
], MainThreadEditorService.prototype, "editorService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(browser_1.IEditorDecorationCollectionService),
    tslib_1.__metadata("design:type", Object)
], MainThreadEditorService.prototype, "decorationService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_editor_1.EditorCollectionService),
    tslib_1.__metadata("design:type", editor_collection_service_1.EditorCollectionServiceImpl)
], MainThreadEditorService.prototype, "codeEditorService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(static_resource_1.StaticResourceService),
    tslib_1.__metadata("design:type", static_resource_1.StaticResourceService)
], MainThreadEditorService.prototype, "staticResourceService", void 0);
exports.MainThreadEditorService = MainThreadEditorService = tslib_1.__decorate([
    (0, di_1.Injectable)({ multiple: true }),
    tslib_1.__param(0, (0, di_1.Optional)(Symbol())),
    tslib_1.__metadata("design:paramtypes", [Object, main_thread_doc_1.MainThreadExtensionDocumentData])
], MainThreadEditorService);
function getTextEditorId(group, uri, side) {
    return group.name + '.' + (side ? `${side}.` : '') + uri.toString();
}
function getGroupIdFromTextEditorId(id) {
    return id.substr(0, id.indexOf('.'));
}
function getEditorOption(editor) {
    const modelOptions = editor.getModel().getOptions();
    return {
        tabSize: modelOptions.tabSize,
        indentSize: modelOptions.indentSize,
        insertSpaces: modelOptions.insertSpaces,
        cursorStyle: editor.getOption(monaco.editor.EditorOption.cursorStyle),
        // 这里之前取 lineNumbers 配置项的值，现在改成取 renderType，是为了跟之前保持返回值一致
        lineNumbers: editor.getOption(monaco.editor.EditorOption.lineNumbers)
            .renderType,
    };
}
function isEditor(openType) {
    if (!openType) {
        return false;
    }
    return openType.type === browser_1.EditorOpenType.code || openType.type === browser_1.EditorOpenType.diff;
}
function isGroupEditorState(group) {
    return group.currentOpenType && isEditor(group.currentOpenType);
}
function getViewColumn(group) {
    return group.index + 1;
}
function resourceEquals(r1, r2) {
    if (!r1 && !r2) {
        return true;
    }
    if (r1 && r2 && r1.uri.isEqual(r2.uri)) {
        return true;
    }
    return false;
}
function openTypeEquals(r1, r2) {
    if (!r1 && !r2) {
        return true;
    }
    if (r1 && r2 && r1.type === r2.type && r1.componentId === r2.componentId) {
        return true;
    }
    return false;
}
function cursorStyleToString(cursorStyle) {
    if (cursorStyle === vscode_1.TextEditorCursorStyle.Line) {
        return 'line';
    }
    else if (cursorStyle === vscode_1.TextEditorCursorStyle.Block) {
        return 'block';
    }
    else if (cursorStyle === vscode_1.TextEditorCursorStyle.Underline) {
        return 'underline';
    }
    else if (cursorStyle === vscode_1.TextEditorCursorStyle.LineThin) {
        return 'line-thin';
    }
    else if (cursorStyle === vscode_1.TextEditorCursorStyle.BlockOutline) {
        return 'block-outline';
    }
    else if (cursorStyle === vscode_1.TextEditorCursorStyle.UnderlineThin) {
        return 'underline-thin';
    }
    else {
        throw new Error('cursorStyleToString: Unknown cursorStyle');
    }
}
//# sourceMappingURL=main.thread.editor.js.map