"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MainThreadExtensionDocumentData = void 0;
const tslib_1 = require("tslib");
const di_1 = require("@opensumi/di");
const ide_connection_1 = require("@opensumi/ide-connection");
const ide_core_browser_1 = require("@opensumi/ide-core-browser");
const services_1 = require("@opensumi/ide-core-browser/lib/services");
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 untitled_resource_1 = require("@opensumi/ide-editor/lib/browser/untitled-resource");
const ide_file_service_1 = require("@opensumi/ide-file-service");
const vscode_1 = require("../../../common/vscode");
const DEFAULT_EXT_HOLD_DOC_REF_MAX_AGE = 1000 * 60 * 3; // 插件进程openDocument持有的最长时间
const DEFAULT_EXT_HOLD_DOC_REF_MIN_AGE = 1000 * 20; // 插件进程openDocument持有的最短时间，防止bounce
const DEFAULT_EXT_HOLD_DOC_REF_LENGTH = 1024 * 1024 * 80; // 插件进程openDocument持有的最长长度
let ExtensionEditorDocumentProvider = class ExtensionEditorDocumentProvider {
    constructor(proxy) {
        this.proxy = proxy;
        this.onDidChangeContentEmitter = new ide_core_common_1.Emitter();
        this.onDidChangeContent = this.onDidChangeContentEmitter.event;
        this.schemes = new Set();
    }
    registerScheme(scheme) {
        this.schemes.add(scheme);
    }
    unregisterScheme(scheme) {
        this.schemes.delete(scheme);
    }
    handlesScheme(scheme) {
        return this.schemes.has(scheme);
    }
    provideEditorDocumentModelContent(uri, encoding) {
        return this.proxy.$provideTextDocumentContent(uri.toString(), encoding);
    }
    isReadonly(uri) {
        return true;
    }
};
ExtensionEditorDocumentProvider = tslib_1.__decorate([
    (0, di_1.Injectable)({ multiple: true }),
    tslib_1.__metadata("design:paramtypes", [Object])
], ExtensionEditorDocumentProvider);
let MainThreadExtensionDocumentData = class MainThreadExtensionDocumentData extends ide_core_common_1.WithEventBus {
    isDocSyncEnabled(uri) {
        var _a;
        const uriString = uri.toString();
        if (!this.docSyncEnabled.has(uriString)) {
            const docRef = this.docManager.getModelReference(new ide_core_common_1.URI(uriString), 'mainthread doc size');
            if (docRef) {
                this.docSyncEnabled.set(uriString, docRef.instance.getMonacoModel().getValueLength() <
                    this.preference.getValid('editor.docExtHostSyncMaxSize', 4 * 1024 * 1024 * 1024));
                docRef.dispose();
            }
        }
        return (_a = this.docSyncEnabled.get(uriString)) !== null && _a !== void 0 ? _a : false;
    }
    isDocIgnored(uri) {
        return uri.codeUri.scheme === ide_core_common_1.Schemes.walkThroughSnippet;
    }
    constructor(rpcProtocol) {
        super();
        this.rpcProtocol = rpcProtocol;
        this.editorDisposers = new Map();
        this.extHoldDocuments = new LimitedMainThreadDocumentCollection();
        this.docSyncEnabled = new ide_core_common_1.LRUMap(200, 100);
        this.proxy = this.rpcProtocol.getProxy(vscode_1.ExtHostAPIIdentifier.ExtHostDocuments);
        this.provider = this.injector.get(ExtensionEditorDocumentProvider, [this.proxy]);
        this.contentRegistry.registerEditorDocumentModelContentProvider(this.provider);
        // sync
        this.docManager.getAllModels().map((doc) => {
            if (!this.isDocSyncEnabled(doc.uri)) {
                return;
            }
            this.proxy.$fireModelOpenedEvent({
                uri: doc.uri.toString(),
                lines: doc.getText().split(doc.eol),
                eol: doc.eol,
                dirty: doc.dirty,
                languageId: doc.languageId,
                versionId: doc.getMonacoModel().getVersionId(),
            });
        });
    }
    onEditorDocumentModelContentChangeEvent(e) {
        if (!this.isDocSyncEnabled(e.payload.uri) || this.isDocIgnored(e.payload.uri)) {
            return;
        }
        this.proxy.$fireModelChangedEvent({
            changes: e.payload.changes,
            uri: e.payload.uri.toString(),
            eol: e.payload.eol,
            dirty: e.payload.dirty,
            versionId: e.payload.versionId,
            isRedoing: e.payload.isRedoing,
            isUndoing: e.payload.isUndoing,
        });
    }
    async onEditorDocumentModelWillSaveEvent(e) {
        if (!this.isDocSyncEnabled(e.payload.uri) || this.isDocIgnored(e.payload.uri)) {
            return;
        }
        await this.proxy.$fireModelWillSaveEvent({
            uri: e.payload.uri.toString(),
            reason: e.payload.reason,
            dirty: e.payload.dirty,
        });
    }
    onEditorDocumentModelOptionChangedEvent(e) {
        if (!this.isDocSyncEnabled(e.payload.uri) || this.isDocIgnored(e.payload.uri)) {
            return;
        }
        this.proxy.$fireModelOptionsChangedEvent({
            encoding: e.payload.encoding,
            uri: e.payload.uri.toString(),
            languageId: e.payload.languageId,
            dirty: e.payload.dirty,
        });
    }
    onEditorDocumentModelContentCreationEvent(e) {
        if (!this.isDocSyncEnabled(e.payload.uri) || this.isDocIgnored(e.payload.uri)) {
            return;
        }
        this.proxy.$fireModelOpenedEvent({
            uri: e.payload.uri.toString(),
            lines: e.payload.content.split(e.payload.eol),
            eol: e.payload.eol,
            dirty: false,
            languageId: e.payload.languageId,
            versionId: e.payload.versionId,
        });
    }
    onEditorDocumentModelRemovedEvent(e) {
        if (!this.isDocSyncEnabled(e.payload) || this.isDocIgnored(e.payload)) {
            return;
        }
        this.proxy.$fireModelRemovedEvent({
            uri: e.payload.toString(),
        });
    }
    onEditorDocumentModelSavingEvent(e) {
        if (!this.isDocSyncEnabled(e.payload) || this.isDocIgnored(e.payload)) {
            return;
        }
        this.proxy.$fireModelSavedEvent({
            uri: e.payload.toString(),
        });
    }
    async $tryCreateDocument(options) {
        if (!options) {
            options = {};
        }
        const { language, content } = options;
        const docRef = await this.docManager.createModelReference(new ide_core_common_1.URI(`${ide_core_common_1.Schemes.untitled}://temp/Untitled-` + this.tempDocIdCount.id), 'ext-create-document');
        if (language) {
            docRef.instance.languageId = language;
        }
        if (!(0, ide_core_common_1.isUndefinedOrNull)(content)) {
            docRef.instance.updateContent(content);
        }
        return docRef.instance.uri.toString();
    }
    async $tryOpenDocument(uri) {
        const docRef = await this.docManager.createModelReference(new ide_core_common_1.URI(uri), 'ext-open-document');
        this.extHoldDocuments.add(docRef);
    }
    async $trySaveDocument(uri) {
        const docRef = await this.docManager.getModelReference(new ide_core_common_1.URI(uri), 'ext-saving-document');
        if (docRef) {
            try {
                return docRef.instance.save(true);
            }
            finally {
                docRef.dispose();
            }
        }
        return false;
    }
    async $fireTextDocumentChangedEvent(uri) {
        this.provider.onDidChangeContentEmitter.fire(new ide_core_common_1.URI(uri));
    }
    $unregisterDocumentProviderWithScheme(scheme) {
        if (this.editorDisposers.has(scheme)) {
            this.editorDisposers.get(scheme).dispose();
            this.editorDisposers.delete(scheme);
        }
    }
    $registerDocumentProviderWithScheme(scheme) {
        this.$unregisterDocumentProviderWithScheme(scheme);
        const disposer = new ide_core_common_1.Disposable();
        disposer.addDispose(this.resourceService.registerResourceProvider({
            scheme,
            provideResource: async (uri) => Promise.all([this.labelService.getName(uri), this.labelService.getIcon(uri)]).then(([name, icon]) => ({
                name,
                icon,
                uri,
                metadata: null,
            })),
        }));
        disposer.addDispose(this.editorComponentRegistry.registerEditorComponentResolver(scheme, (resource, results) => {
            if (this.fileServiceClient.handlesScheme(scheme)) {
                // 有插件会同时注册 documentProvider 和 fileSystem，如果这里注册了 fileSystem，就不再添加打开类型
                return;
            }
            results.push({
                type: browser_1.EditorOpenType.code,
                readonly: true,
            });
        }));
        disposer.addDispose({
            dispose: () => {
                this.provider.unregisterScheme(scheme);
            },
        });
        this.provider.registerScheme(scheme);
        this.editorDisposers.set(scheme, disposer);
    }
    dispose() {
        super.dispose();
        for (const disposable of this.editorDisposers.values()) {
            disposable.dispose();
        }
        this.editorDisposers.clear();
    }
};
exports.MainThreadExtensionDocumentData = MainThreadExtensionDocumentData;
tslib_1.__decorate([
    (0, di_1.Autowired)(),
    tslib_1.__metadata("design:type", untitled_resource_1.UntitledDocumentIdCounter)
], MainThreadExtensionDocumentData.prototype, "tempDocIdCount", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(browser_1.IEditorDocumentModelService),
    tslib_1.__metadata("design:type", Object)
], MainThreadExtensionDocumentData.prototype, "docManager", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(browser_1.IEditorDocumentModelContentRegistry),
    tslib_1.__metadata("design:type", Object)
], MainThreadExtensionDocumentData.prototype, "contentRegistry", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(),
    tslib_1.__metadata("design:type", ide_editor_1.ResourceService)
], MainThreadExtensionDocumentData.prototype, "resourceService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(),
    tslib_1.__metadata("design:type", browser_1.EditorComponentRegistry)
], MainThreadExtensionDocumentData.prototype, "editorComponentRegistry", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(),
    tslib_1.__metadata("design:type", services_1.LabelService)
], MainThreadExtensionDocumentData.prototype, "labelService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(di_1.INJECTOR_TOKEN),
    tslib_1.__metadata("design:type", di_1.Injector)
], MainThreadExtensionDocumentData.prototype, "injector", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.PreferenceService),
    tslib_1.__metadata("design:type", Object)
], MainThreadExtensionDocumentData.prototype, "preference", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_file_service_1.IFileServiceClient),
    tslib_1.__metadata("design:type", Object)
], MainThreadExtensionDocumentData.prototype, "fileServiceClient", void 0);
tslib_1.__decorate([
    (0, ide_core_common_1.OnEvent)(browser_1.EditorDocumentModelContentChangedEvent),
    tslib_1.__metadata("design:type", Function),
    tslib_1.__metadata("design:paramtypes", [browser_1.EditorDocumentModelContentChangedEvent]),
    tslib_1.__metadata("design:returntype", void 0)
], MainThreadExtensionDocumentData.prototype, "onEditorDocumentModelContentChangeEvent", null);
tslib_1.__decorate([
    (0, ide_core_common_1.OnEvent)(browser_1.EditorDocumentModelWillSaveEvent),
    tslib_1.__metadata("design:type", Function),
    tslib_1.__metadata("design:paramtypes", [browser_1.EditorDocumentModelWillSaveEvent]),
    tslib_1.__metadata("design:returntype", Promise)
], MainThreadExtensionDocumentData.prototype, "onEditorDocumentModelWillSaveEvent", null);
tslib_1.__decorate([
    (0, ide_core_common_1.OnEvent)(browser_1.EditorDocumentModelOptionChangedEvent),
    tslib_1.__metadata("design:type", Function),
    tslib_1.__metadata("design:paramtypes", [browser_1.EditorDocumentModelOptionChangedEvent]),
    tslib_1.__metadata("design:returntype", void 0)
], MainThreadExtensionDocumentData.prototype, "onEditorDocumentModelOptionChangedEvent", null);
tslib_1.__decorate([
    (0, ide_core_common_1.OnEvent)(browser_1.EditorDocumentModelCreationEvent),
    tslib_1.__metadata("design:type", Function),
    tslib_1.__metadata("design:paramtypes", [browser_1.EditorDocumentModelCreationEvent]),
    tslib_1.__metadata("design:returntype", void 0)
], MainThreadExtensionDocumentData.prototype, "onEditorDocumentModelContentCreationEvent", null);
tslib_1.__decorate([
    (0, ide_core_common_1.OnEvent)(browser_1.EditorDocumentModelRemovalEvent),
    tslib_1.__metadata("design:type", Function),
    tslib_1.__metadata("design:paramtypes", [browser_1.EditorDocumentModelRemovalEvent]),
    tslib_1.__metadata("design:returntype", void 0)
], MainThreadExtensionDocumentData.prototype, "onEditorDocumentModelRemovedEvent", null);
tslib_1.__decorate([
    (0, ide_core_common_1.OnEvent)(browser_1.EditorDocumentModelSavedEvent),
    tslib_1.__metadata("design:type", Function),
    tslib_1.__metadata("design:paramtypes", [browser_1.EditorDocumentModelSavedEvent]),
    tslib_1.__metadata("design:returntype", void 0)
], MainThreadExtensionDocumentData.prototype, "onEditorDocumentModelSavingEvent", null);
exports.MainThreadExtensionDocumentData = MainThreadExtensionDocumentData = tslib_1.__decorate([
    (0, di_1.Injectable)({ multiple: true }),
    tslib_1.__param(0, (0, di_1.Optional)(Symbol())),
    tslib_1.__metadata("design:paramtypes", [Object])
], MainThreadExtensionDocumentData);
class LimitedMainThreadDocumentCollection {
    constructor() {
        this.maxLength = DEFAULT_EXT_HOLD_DOC_REF_LENGTH;
        this.maxAge = DEFAULT_EXT_HOLD_DOC_REF_MAX_AGE;
        this.minAge = DEFAULT_EXT_HOLD_DOC_REF_MIN_AGE;
        this.refs = [];
        this.length = 0;
    }
    add(docRef) {
        const length = docRef.instance.getText().length; // 只使用openDocument时的length，这个length之后可能会改变，但不管
        this.length += length;
        let maxTimeout = null;
        const ref = {
            ref: docRef,
            dispose: () => {
                const index = this.refs.indexOf(ref);
                if (index !== 0) {
                    this.length -= length;
                    docRef.dispose();
                    clearTimeout(maxTimeout);
                    this.refs.splice(index, 1);
                }
            },
            createTimeStamp: new Date().getTime(),
        };
        maxTimeout = setTimeout(() => {
            ref.dispose();
        }, this.maxAge);
        this.refs.push(ref);
        this.clean();
    }
    clean() {
        // 这里如果只有一个ref，就不dispose了，不然容易出现反复打开关闭
        while (this.length > this.maxLength && this.refs.length > 1) {
            const toDispose = this.refs[0];
            if (toDispose.createTimeStamp + this.minAge > new Date().getTime()) {
                break; // 持有时间太短
            }
            this.disposeFirst();
        }
    }
    disposeFirst() {
        const toDispose = this.refs.shift();
        if (toDispose) {
            toDispose.dispose();
        }
    }
}
//# sourceMappingURL=main.thread.doc.js.map