UNPKG

sussudio

Version:

An unofficial VS Code Internal API

270 lines (269 loc) 13.9 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; import { BrowserWindow } from 'electron'; import { Emitter } from "../../../base/common/event.mjs"; import { parse } from "../../../base/common/json.mjs"; import { mnemonicButtonLabel } from "../../../base/common/labels.mjs"; import { Disposable } from "../../../base/common/lifecycle.mjs"; import { Schemas } from "../../../base/common/network.mjs"; import { dirname, join } from "../../../base/common/path.mjs"; import { basename, extUriBiasedIgnorePathCase, joinPath, originalFSPath } from "../../../base/common/resources.mjs"; import { withNullAsUndefined } from "../../../base/common/types.mjs"; import { Promises } from "../../../base/node/pfs.mjs"; import { localize } from "../../../nls.mjs"; import { IBackupMainService } from "../../backup/electron-main/backup.mjs"; import { IDialogMainService } from "../../dialogs/electron-main/dialogMainService.mjs"; import { IEnvironmentMainService } from "../../environment/electron-main/environmentMainService.mjs"; import { createDecorator } from "../../instantiation/common/instantiation.mjs"; import { ILogService } from "../../log/common/log.mjs"; import { IProductService } from "../../product/common/productService.mjs"; import { IUserDataProfilesMainService } from "../../userDataProfile/electron-main/userDataProfile.mjs"; import { findWindowOnWorkspaceOrFolder } from "../../windows/electron-main/windowsFinder.mjs"; import { isWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, isUntitledWorkspace } from "../../workspace/common/workspace.mjs"; import { getStoredWorkspaceFolder, isStoredWorkspaceFolder, toWorkspaceFolders } from "../common/workspaces.mjs"; import { getWorkspaceIdentifier } from "../node/workspaces.mjs"; export const IWorkspacesManagementMainService = createDecorator('workspacesManagementMainService'); let WorkspacesManagementMainService = class WorkspacesManagementMainService extends Disposable { environmentMainService; logService; userDataProfilesMainService; backupMainService; dialogMainService; productService; _onDidDeleteUntitledWorkspace = this._register(new Emitter()); onDidDeleteUntitledWorkspace = this._onDidDeleteUntitledWorkspace.event; _onDidEnterWorkspace = this._register(new Emitter()); onDidEnterWorkspace = this._onDidEnterWorkspace.event; untitledWorkspacesHome = this.environmentMainService.untitledWorkspacesHome; // local URI that contains all untitled workspaces untitledWorkspaces = []; constructor(environmentMainService, logService, userDataProfilesMainService, backupMainService, dialogMainService, productService) { super(); this.environmentMainService = environmentMainService; this.logService = logService; this.userDataProfilesMainService = userDataProfilesMainService; this.backupMainService = backupMainService; this.dialogMainService = dialogMainService; this.productService = productService; } async initialize() { // Reset this.untitledWorkspaces = []; // Resolve untitled workspaces try { const untitledWorkspacePaths = (await Promises.readdir(this.untitledWorkspacesHome.fsPath)).map(folder => joinPath(this.untitledWorkspacesHome, folder, UNTITLED_WORKSPACE_NAME)); for (const untitledWorkspacePath of untitledWorkspacePaths) { const workspace = getWorkspaceIdentifier(untitledWorkspacePath); const resolvedWorkspace = await this.resolveLocalWorkspace(untitledWorkspacePath); if (!resolvedWorkspace) { await this.deleteUntitledWorkspace(workspace); } else { this.untitledWorkspaces.push({ workspace, remoteAuthority: resolvedWorkspace.remoteAuthority }); } } } catch (error) { if (error.code !== 'ENOENT') { this.logService.warn(`Unable to read folders in ${this.untitledWorkspacesHome} (${error}).`); } } } resolveLocalWorkspace(uri) { return this.doResolveLocalWorkspace(uri, path => Promises.readFile(path, 'utf8')); } doResolveLocalWorkspace(uri, contentsFn) { if (!this.isWorkspacePath(uri)) { return undefined; // does not look like a valid workspace config file } if (uri.scheme !== Schemas.file) { return undefined; } try { const contents = contentsFn(uri.fsPath); if (contents instanceof Promise) { return contents.then(value => this.doResolveWorkspace(uri, value), error => undefined /* invalid workspace */); } else { return this.doResolveWorkspace(uri, contents); } } catch { return undefined; // invalid workspace } } isWorkspacePath(uri) { return isUntitledWorkspace(uri, this.environmentMainService) || hasWorkspaceFileExtension(uri); } doResolveWorkspace(path, contents) { try { const workspace = this.doParseStoredWorkspace(path, contents); const workspaceIdentifier = getWorkspaceIdentifier(path); return { id: workspaceIdentifier.id, configPath: workspaceIdentifier.configPath, folders: toWorkspaceFolders(workspace.folders, workspaceIdentifier.configPath, extUriBiasedIgnorePathCase), remoteAuthority: workspace.remoteAuthority, transient: workspace.transient }; } catch (error) { this.logService.warn(error.toString()); } return undefined; } doParseStoredWorkspace(path, contents) { // Parse workspace file const storedWorkspace = parse(contents); // use fault tolerant parser // Filter out folders which do not have a path or uri set if (storedWorkspace && Array.isArray(storedWorkspace.folders)) { storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder)); } else { throw new Error(`${path.toString(true)} looks like an invalid workspace file.`); } return storedWorkspace; } async createUntitledWorkspace(folders, remoteAuthority) { const { workspace, storedWorkspace } = this.newUntitledWorkspace(folders, remoteAuthority); const configPath = workspace.configPath.fsPath; await Promises.mkdir(dirname(configPath), { recursive: true }); await Promises.writeFile(configPath, JSON.stringify(storedWorkspace, null, '\t')); this.untitledWorkspaces.push({ workspace, remoteAuthority }); return workspace; } newUntitledWorkspace(folders = [], remoteAuthority) { const randomId = (Date.now() + Math.round(Math.random() * 1000)).toString(); const untitledWorkspaceConfigFolder = joinPath(this.untitledWorkspacesHome, randomId); const untitledWorkspaceConfigPath = joinPath(untitledWorkspaceConfigFolder, UNTITLED_WORKSPACE_NAME); const storedWorkspaceFolder = []; for (const folder of folders) { storedWorkspaceFolder.push(getStoredWorkspaceFolder(folder.uri, true, folder.name, untitledWorkspaceConfigFolder, extUriBiasedIgnorePathCase)); } return { workspace: getWorkspaceIdentifier(untitledWorkspaceConfigPath), storedWorkspace: { folders: storedWorkspaceFolder, remoteAuthority } }; } async getWorkspaceIdentifier(configPath) { return getWorkspaceIdentifier(configPath); } isUntitledWorkspace(workspace) { return isUntitledWorkspace(workspace.configPath, this.environmentMainService); } async deleteUntitledWorkspace(workspace) { if (!this.isUntitledWorkspace(workspace)) { return; // only supported for untitled workspaces } // Delete from disk await this.doDeleteUntitledWorkspace(workspace); // unset workspace from profiles if (this.userDataProfilesMainService.isEnabled()) { this.userDataProfilesMainService.unsetWorkspace(workspace); } // Event this._onDidDeleteUntitledWorkspace.fire(workspace); } async doDeleteUntitledWorkspace(workspace) { const configPath = originalFSPath(workspace.configPath); try { // Delete Workspace await Promises.rm(dirname(configPath)); // Mark Workspace Storage to be deleted const workspaceStoragePath = join(this.environmentMainService.workspaceStorageHome.fsPath, workspace.id); if (await Promises.exists(workspaceStoragePath)) { await Promises.writeFile(join(workspaceStoragePath, 'obsolete'), ''); } // Remove from list this.untitledWorkspaces = this.untitledWorkspaces.filter(untitledWorkspace => untitledWorkspace.workspace.id !== workspace.id); } catch (error) { this.logService.warn(`Unable to delete untitled workspace ${configPath} (${error}).`); } } getUntitledWorkspaces() { return this.untitledWorkspaces; } async enterWorkspace(window, windows, path) { if (!window || !window.win || !window.isReady) { return undefined; // return early if the window is not ready or disposed } const isValid = await this.isValidTargetWorkspacePath(window, windows, path); if (!isValid) { return undefined; // return early if the workspace is not valid } const result = await this.doEnterWorkspace(window, getWorkspaceIdentifier(path)); if (!result) { return undefined; } // Emit as event this._onDidEnterWorkspace.fire({ window, workspace: result.workspace }); return result; } async isValidTargetWorkspacePath(window, windows, workspacePath) { if (!workspacePath) { return true; } if (isWorkspaceIdentifier(window.openedWorkspace) && extUriBiasedIgnorePathCase.isEqual(window.openedWorkspace.configPath, workspacePath)) { return false; // window is already opened on a workspace with that path } // Prevent overwriting a workspace that is currently opened in another window if (findWindowOnWorkspaceOrFolder(windows, workspacePath)) { const options = { title: this.productService.nameLong, type: 'info', buttons: [mnemonicButtonLabel(localize({ key: 'ok', comment: ['&& denotes a mnemonic'] }, "&&OK"))], message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(workspacePath)), detail: localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."), noLink: true, defaultId: 0 }; await this.dialogMainService.showMessageBox(options, withNullAsUndefined(BrowserWindow.getFocusedWindow())); return false; } return true; // OK } async doEnterWorkspace(window, workspace) { if (!window.config) { return undefined; } window.focus(); // Register window for backups and migrate current backups over let backupPath; if (!window.config.extensionDevelopmentPath) { if (window.config.backupPath) { backupPath = await this.backupMainService.registerWorkspaceBackup({ workspace, remoteAuthority: window.remoteAuthority }, window.config.backupPath); } else { backupPath = this.backupMainService.registerWorkspaceBackup({ workspace, remoteAuthority: window.remoteAuthority }); } } // if the window was opened on an untitled workspace, delete it. if (isWorkspaceIdentifier(window.openedWorkspace) && this.isUntitledWorkspace(window.openedWorkspace)) { await this.deleteUntitledWorkspace(window.openedWorkspace); } // Update window configuration properly based on transition to workspace window.config.workspace = workspace; window.config.backupPath = backupPath; return { workspace, backupPath }; } }; WorkspacesManagementMainService = __decorate([ __param(0, IEnvironmentMainService), __param(1, ILogService), __param(2, IUserDataProfilesMainService), __param(3, IBackupMainService), __param(4, IDialogMainService), __param(5, IProductService) ], WorkspacesManagementMainService); export { WorkspacesManagementMainService };