"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtHostWorkspace = void 0;
exports.createWorkspaceApiFactory = createWorkspaceApiFactory;
exports.toWorkspaceFolder = toWorkspaceFolder;
const tslib_1 = require("tslib");
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 ext_types_1 = require("../../../common/vscode/ext-types");
const { Path, relative, normalize } = ide_core_common_1.path;
function createWorkspaceApiFactory(extHostWorkspace, extHostPreference, extHostDocument, extHostNotebook, extHostFileSystem, extHostFileSystemEvent, extHostTasks, extension) {
    const workspace = {
        rootPath: extHostWorkspace.rootPath,
        name: extHostWorkspace.name,
        asRelativePath: (pathOrUri, includeWorkspaceFolder) => extHostWorkspace.getRelativePath(pathOrUri, includeWorkspaceFolder),
        updateWorkspaceFolders: (start, deleteCount, ...workspaceFoldersToAdd) => extHostWorkspace.updateWorkspaceFolders(start, deleteCount || 0, ...workspaceFoldersToAdd),
        onDidChangeWorkspaceFolders: extHostWorkspace.onDidChangeWorkspaceFolders,
        getWorkspaceFolder: (uri, resolveParent) => extHostWorkspace.getWorkspaceFolder(uri, resolveParent),
        workspaceFolders: extHostWorkspace.workspaceFolders,
        getConfiguration: (section, resource, extensionId) => extHostPreference.getConfiguration(section, resource, extensionId),
        onDidChangeConfiguration: (listener, thisArgs, disposables) => extHostPreference.onDidChangeConfiguration(listener, thisArgs, disposables),
        get isTrusted() {
            return true;
        },
        requestWorkspaceTrust: (_options) => true,
        onDidGrantWorkspaceTrust: ide_core_common_1.Event.None,
        openTextDocument: extHostDocument.openTextDocument.bind(extHostDocument),
        onDidOpenTextDocument: extHostDocument.onDidOpenTextDocument.bind(extHostDocument),
        onDidCloseTextDocument: extHostDocument.onDidCloseTextDocument.bind(extHostDocument),
        onDidChangeTextDocument: extHostDocument.onDidChangeTextDocument.bind(extHostDocument),
        onWillSaveTextDocument: extHostDocument.onWillSaveTextDocument.bind(extHostDocument),
        onDidSaveTextDocument: extHostDocument.onDidSaveTextDocument.bind(extHostDocument),
        registerTextDocumentContentProvider: extHostDocument.registerTextDocumentContentProvider.bind(extHostDocument),
        registerTaskProvider: (type, provider) => {
            // eslint-disable-next-line no-console
            console.warn(false, '[Deprecated warning]: Use the corresponding function on the `tasks` namespace instead');
            return extHostTasks.registerTaskProvider(type, provider, extension);
        },
        applyEdit: (edit, metadata) => extHostWorkspace.applyEdit(edit, metadata),
        get textDocuments() {
            return extHostDocument.getAllDocument();
        },
        registerFileSystemProvider(scheme, provider, options) {
            return extHostFileSystem.registerFileSystemProvider(scheme, provider, options);
        },
        get fs() {
            return extHostFileSystem.fileSystem;
        },
        createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete) => extHostFileSystemEvent.createFileSystemWatcher(TypeConverts.fromGlobPattern(pattern), ignoreCreate, ignoreChange, ignoreDelete),
        onDidCreateFiles: (listener, thisArg, disposables) => extHostFileSystemEvent.onDidCreateFile(listener, thisArg, disposables),
        onDidDeleteFiles: (listener, thisArg, disposables) => extHostFileSystemEvent.onDidDeleteFile(listener, thisArg, disposables),
        onDidRenameFiles: (listener, thisArg, disposables) => extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables),
        onWillCreateFiles: (listener, thisArg, disposables) => extHostFileSystemEvent.getOnWillCreateFileEvent(extension)(listener, thisArg, disposables),
        onWillDeleteFiles: (listener, thisArg, disposables) => extHostFileSystemEvent.getOnWillDeleteFileEvent(extension)(listener, thisArg, disposables),
        onWillRenameFiles: (listener, thisArg, disposables) => extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables),
        onDidRenameFile: extHostWorkspace.onDidRenameFile,
        save: (uri) => extHostWorkspace.save(uri),
        saveAs: (uri) => extHostWorkspace.saveAs(uri),
        saveAll: () => extHostWorkspace.saveAll(),
        findFiles: (include, exclude, maxResults, token) => extHostWorkspace.findFiles(TypeConverts.GlobPattern.from(include), TypeConverts.GlobPattern.from(exclude), maxResults, null, token),
        // Notebook API
        onDidOpenNotebookDocument: extHostNotebook.onDidOpenNotebookDocument.bind(extHostNotebook),
        onDidChangeNotebookDocument: extHostNotebook.onDidChangeNotebookDocument.bind(extHostNotebook),
        onDidCloseNotebookDocument: extHostNotebook.onDidCloseNotebookDocument.bind(extHostNotebook),
        onDidSaveNotebookDocument: extHostNotebook.onDidSaveNotebookDocument.bind(extHostNotebook),
        get notebookDocuments() {
            return extHostNotebook.notebookDocuments;
        },
        // empty handler for compatibility with the experimental API , see https://github.com/opensumi/core/issues/2424
        registerTimelineProvider: () => (0, ide_core_common_1.toDisposable)(() => { }),
        registerPortAttributesProvider: () => (0, ide_core_common_1.toDisposable)(() => { }),
        registerEditSessionIdentityProvider: () => (0, ide_core_common_1.toDisposable)(() => { }),
        onWillCreateEditSessionIdentity: () => (0, ide_core_common_1.toDisposable)(() => { }),
    };
    return workspace;
}
function toWorkspaceFolder(folder, workspaceToName = {}) {
    return {
        uri: ext_types_1.Uri.revive(folder.uri),
        name: folder.name ||
            (folder.uri.path ? workspaceToName[folder.uri.toString()] : workspaceToName[folder.uri.toString() + '/']),
        index: folder.index,
    };
}
class ExtHostWorkspace {
    constructor(rpcProtocol, extHostMessage, extHostDoc) {
        this.extHostDoc = extHostDoc;
        this.workspaceFoldersChangedEmitter = new ide_core_common_1.Emitter();
        this.onDidChangeWorkspaceFolders = this.workspaceFoldersChangedEmitter.event;
        this._workspaceFolder = [];
        this._onDidRenameFile = new ide_core_common_1.Emitter();
        this.onDidRenameFile = this._onDidRenameFile.event;
        this.workspaceToName = {};
        this.messageService = extHostMessage;
        this.rpcProtocol = rpcProtocol;
        this.proxy = this.rpcProtocol.getProxy(vscode_1.MainThreadAPIIdentifier.MainThreadWorkspace);
    }
    /**
     * FileStat 转 vscode.WorkspaceFolder
     * @private
     * @param {FileStat} root
     * @param {number} index
     * @returns {vscode.WorkspaceFolder}
     * @memberof ExtHostWorkspace
     */
    toWorkspaceFolder(root, index) {
        const uri = ext_types_1.Uri.parse(root.uri);
        const path = new Path(uri.path);
        return {
            uri,
            name: path.base,
            index,
        };
    }
    deltaFolders(currentFolders = [], newFolders = []) {
        const added = this.foldersDiff(newFolders, currentFolders);
        const removed = this.foldersDiff(currentFolders, newFolders);
        return { added, removed };
    }
    foldersDiff(folder1 = [], folder2 = []) {
        const map = new Map();
        folder1.forEach((folder) => map.set(folder.uri.toString(), folder));
        folder2.forEach((folder) => map.delete(folder.uri.toString()));
        return folder1.filter((folder) => map.has(folder.uri.toString()));
    }
    get workspaceFolders() {
        return this.folders;
    }
    get rootPath() {
        const folder = this.folders && this.folders[0];
        return folder && folder.uri.fsPath;
    }
    get name() {
        const folder = this.folders && this.folders[0];
        return folder && folder.name;
    }
    getRelativePath(pathOrUri, includeWorkspaceFolder) {
        let path;
        if (typeof pathOrUri === 'string') {
            path = pathOrUri;
        }
        else if (typeof pathOrUri !== 'undefined') {
            path = pathOrUri.fsPath;
        }
        if (!path) {
            return path;
        }
        const folder = this.getWorkspaceFolder(typeof pathOrUri === 'string' ? ext_types_1.Uri.file(pathOrUri) : pathOrUri, true);
        if (!folder) {
            return path;
        }
        if (typeof includeWorkspaceFolder === 'undefined') {
            includeWorkspaceFolder = this.folders.length > 1;
        }
        let result = relative(folder.uri.fsPath, path);
        if (includeWorkspaceFolder) {
            result = `${folder.name}/${result}`;
        }
        return normalize(result);
    }
    updateWorkspaceFolders(start, deleteCount, ...workspaceFoldersToAdd) {
        const rootsToAdd = new Set();
        if (Array.isArray(workspaceFoldersToAdd)) {
            workspaceFoldersToAdd.forEach((folderToAdd) => {
                const uri = ext_types_1.Uri.isUri(folderToAdd.uri) && folderToAdd.uri.toString();
                if (uri && !rootsToAdd.has(uri)) {
                    rootsToAdd.add(uri);
                    if (folderToAdd.name) {
                        this.workspaceToName[uri.toString()] = folderToAdd.name;
                    }
                }
            });
        }
        if ([start, deleteCount].some((i) => typeof i !== 'number' || i < 0)) {
            return false;
        }
        if (deleteCount === 0 && rootsToAdd.size === 0) {
            return false;
        }
        const currentWorkspaceFolders = this.workspaceFolders || [];
        if (start + deleteCount > currentWorkspaceFolders.length) {
            return false;
        }
        // 数据层模拟执行updateWorkspaceFolders操作以验证有效性
        const newWorkspaceFolders = currentWorkspaceFolders.slice(0);
        newWorkspaceFolders.splice(start, deleteCount, ...[...rootsToAdd].map((uri) => ({ uri: ext_types_1.Uri.parse(uri) })));
        for (let i = 0; i < newWorkspaceFolders.length; i++) {
            const folder = newWorkspaceFolders[i];
            if (newWorkspaceFolders.some((otherFolder, index) => index !== i && folder.uri.toString() === otherFolder.uri.toString())) {
                return false; // 不能重复添加相同的文件夹
            }
        }
        const { added, removed } = this.deltaFolders(currentWorkspaceFolders, newWorkspaceFolders);
        if (added.length === 0 && removed.length === 0) {
            return false; // 无需任何更改
        }
        // 通知主进程更新对应目录
        this.proxy
            .$updateWorkspaceFolders(start, deleteCount, this.workspaceToName, ...rootsToAdd)
            .then(undefined, (error) => this.messageService.showMessage(ide_core_common_1.MessageType.Error, `Failed to update workspace folders: ${error}`));
        return true;
    }
    $onWorkspaceFoldersChanged(event) {
        const newRoots = event.roots || [];
        const newFolders = newRoots.map((root, index) => this.toWorkspaceFolder(root, index));
        const delta = this.deltaFolders(this.folders, newFolders);
        this.folders = newFolders;
        this.workspaceFoldersChangedEmitter.fire(delta);
    }
    getWorkspaceFolder(uri, resolveParent) {
        if (!this.folders || !this.folders.length) {
            return undefined;
        }
        function dirname(resource) {
            if (resource.scheme === ide_core_common_1.Schemes.file) {
                return ext_types_1.Uri.file(ide_core_common_1.path.dirname(resource.fsPath));
            }
            return resource.with({
                path: ide_core_common_1.path.dirname(resource.path),
            });
        }
        if (resolveParent && this.hasFolder(uri)) {
            uri = dirname(uri);
        }
        const resourcePath = uri.toString();
        let workspaceFolder;
        for (const folder of this.folders) {
            const folderPath = folder.uri.toString();
            if (resourcePath === folderPath) {
                return toWorkspaceFolder(folder, this.workspaceToName);
            }
            if (resourcePath.startsWith(folderPath) &&
                resourcePath[folderPath.length] === '/' &&
                (!workspaceFolder || folderPath.length > workspaceFolder.uri.toString().length)) {
                workspaceFolder = folder;
            }
        }
        return workspaceFolder;
    }
    // 用于直接获取工作区列表
    resolveWorkspaceFolder() {
        if (!this.folders || !this.folders.length) {
            return undefined;
        }
        const workspaceFolders = [];
        for (const folder of this.folders) {
            workspaceFolders === null || workspaceFolders === void 0 ? void 0 : workspaceFolders.push(toWorkspaceFolder(folder, this.workspaceToName));
        }
        return workspaceFolders;
    }
    hasFolder(uri) {
        if (!this.folders) {
            return false;
        }
        return this.folders.some((folder) => folder.uri.toString() === uri.toString());
    }
    applyEdit(edit, metadata) {
        const dto = TypeConverts.WorkspaceEdit.from(edit, this.extHostDoc);
        return this.proxy.$tryApplyWorkspaceEdit(dto, metadata);
    }
    save(uri) {
        return this.proxy.$save(uri);
    }
    saveAs(uri) {
        return this.proxy.$saveAs(uri);
    }
    saveAll() {
        return this.proxy.$saveAll();
    }
    async $didRenameFile(oldUri, newUri) {
        this._onDidRenameFile.fire({
            oldUri: ext_types_1.Uri.revive(oldUri),
            newUri: ext_types_1.Uri.revive(newUri),
        });
    }
    findFiles(include, exclude, maxResults, extensionId, token = ide_core_common_1.CancellationToken.None) {
        let includePattern;
        let includeFolder;
        if (include) {
            if (typeof include === 'string') {
                includePattern = include;
            }
            else {
                includePattern = include.pattern;
                // include.base must be an absolute path
                includeFolder = ext_types_1.Uri.file(include.base);
            }
        }
        let excludePatternOrDisregardExcludes;
        if (exclude === null) {
            excludePatternOrDisregardExcludes = false;
        }
        else if (exclude) {
            if (typeof exclude === 'string') {
                excludePatternOrDisregardExcludes = exclude;
            }
            else {
                excludePatternOrDisregardExcludes = exclude.pattern;
            }
        }
        if (token && token.isCancellationRequested) {
            return Promise.resolve([]);
        }
        return this.proxy
            .$startFileSearch(includePattern || '*', {
            cwd: includeFolder ? includeFolder.fsPath : this.rootPath,
            absolute: true,
        }, excludePatternOrDisregardExcludes, maxResults, token)
            .then((files) => files.map((file) => ext_types_1.Uri.file(file)));
    }
}
exports.ExtHostWorkspace = ExtHostWorkspace;
//# sourceMappingURL=ext.host.workspace.js.map