Merge from vscode 313ede61cbad8f9dc748907b3384e059ddddb79a (#7436)

* Merge from vscode 313ede61cbad8f9dc748907b3384e059ddddb79a

* fix strict null checks
This commit is contained in:
Anthony Dresser
2019-09-30 23:35:45 -07:00
committed by GitHub
parent 6ab03053a0
commit 084524cd2d
196 changed files with 2927 additions and 2547 deletions

View File

@@ -31,7 +31,7 @@ export interface IWorkspacesService {
_serviceBrand: undefined;
// Management
enterWorkspace(path: URI): Promise<IEnterWorkspaceResult | undefined>;
enterWorkspace(path: URI): Promise<IEnterWorkspaceResult | null>;
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void>;
getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier>;

View File

@@ -23,6 +23,7 @@ import { getSimpleWorkspaceLabel } from 'vs/platform/label/common/label';
import { exists } from 'vs/base/node/pfs';
import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Disposable } from 'vs/base/common/lifecycle';
export const IWorkspacesHistoryMainService = createDecorator<IWorkspacesHistoryMainService>('workspacesHistoryMainService');
@@ -40,7 +41,7 @@ export interface IWorkspacesHistoryMainService {
updateWindowsJumpList(): void;
}
export class WorkspacesHistoryMainService implements IWorkspacesHistoryMainService {
export class WorkspacesHistoryMainService extends Disposable implements IWorkspacesHistoryMainService {
private static readonly MAX_TOTAL_RECENT_ENTRIES = 100;
@@ -60,18 +61,27 @@ export class WorkspacesHistoryMainService implements IWorkspacesHistoryMainServi
private readonly _onRecentlyOpenedChange = new Emitter<void>();
readonly onRecentlyOpenedChange: CommonEvent<void> = this._onRecentlyOpenedChange.event;
private macOSRecentDocumentsUpdater: ThrottledDelayer<void>;
private macOSRecentDocumentsUpdater = this._register(new ThrottledDelayer<void>(800));
constructor(
@IStateService private readonly stateService: IStateService,
@ILogService private readonly logService: ILogService,
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILifecycleMainService lifecycleMainService: ILifecycleMainService
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService
) {
this.macOSRecentDocumentsUpdater = new ThrottledDelayer<void>(800);
super();
lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList());
this.registerListeners();
}
private registerListeners(): void {
// Install window jump list after opening window
this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList());
// Add to history when entering workspace
this._register(this.workspacesMainService.onWorkspaceEntered(event => this.addRecentlyOpened([{ workspace: event.workspace }])));
}
private handleWindowsJumpList(): void {

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder, IEnterWorkspaceResult } from 'vs/platform/workspaces/common/workspaces';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { join, dirname } from 'vs/base/common/path';
import { mkdirp, writeFile, rimrafSync, readdirSync, writeFileSync } from 'vs/base/node/pfs';
@@ -17,31 +17,43 @@ import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { Disposable } from 'vs/base/common/lifecycle';
import { originalFSPath, isEqualOrParent, joinPath } from 'vs/base/common/resources';
import { originalFSPath, isEqualOrParent, joinPath, isEqual, basename } from 'vs/base/common/resources';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { localize } from 'vs/nls';
import product from 'vs/platform/product/common/product';
import { MessageBoxOptions, BrowserWindow } from 'electron';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IBackupMainService } from 'vs/platform/backup/electron-main/backup';
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
import { findWindowOnWorkspace } from 'vs/platform/windows/node/window';
export const IWorkspacesMainService = createDecorator<IWorkspacesMainService>('workspacesMainService');
export interface IWorkspaceEnteredEvent {
window: ICodeWindow;
workspace: IWorkspaceIdentifier;
}
export interface IWorkspacesMainService {
_serviceBrand: undefined;
readonly onUntitledWorkspaceDeleted: Event<IWorkspaceIdentifier>;
readonly onWorkspaceEntered: Event<IWorkspaceEnteredEvent>;
enterWorkspace(intoWindow: ICodeWindow, openedWindows: ICodeWindow[], path: URI): Promise<IEnterWorkspaceResult | null>;
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier;
resolveLocalWorkspaceSync(path: URI): IResolvedWorkspace | null;
isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean;
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void>;
deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void;
getUntitledWorkspacesSync(): IUntitledWorkspaceInfo[];
isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean;
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void>;
resolveLocalWorkspaceSync(path: URI): IResolvedWorkspace | null;
getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier>;
}
@@ -59,9 +71,14 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
private readonly _onUntitledWorkspaceDeleted = this._register(new Emitter<IWorkspaceIdentifier>());
readonly onUntitledWorkspaceDeleted: Event<IWorkspaceIdentifier> = this._onUntitledWorkspaceDeleted.event;
private readonly _onWorkspaceEntered = this._register(new Emitter<IWorkspaceEnteredEvent>());
readonly onWorkspaceEntered: Event<IWorkspaceEnteredEvent> = this._onWorkspaceEntered.event;
constructor(
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILogService private readonly logService: ILogService
@ILogService private readonly logService: ILogService,
@IBackupMainService private readonly backupMainService: IBackupMainService,
@IDialogMainService private readonly dialogMainService: IDialogMainService
) {
super();
@@ -236,6 +253,74 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
}
return untitledWorkspaces;
}
async enterWorkspace(window: ICodeWindow, windows: ICodeWindow[], path: URI): Promise<IEnterWorkspaceResult | null> {
if (!window || !window.win || !window.isReady) {
return null; // return early if the window is not ready or disposed
}
const isValid = await this.isValidTargetWorkspacePath(window, windows, path);
if (!isValid) {
return null; // return early if the workspace is not valid
}
const result = this.doEnterWorkspace(window, getWorkspaceIdentifier(path));
// Emit as event
this._onWorkspaceEntered.fire({ window, workspace: result.workspace });
return result;
}
private async isValidTargetWorkspacePath(window: ICodeWindow, windows: ICodeWindow[], path?: URI): Promise<boolean> {
if (!path) {
return true;
}
if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, path)) {
return false; // window is already opened on a workspace with that path
}
// Prevent overwriting a workspace that is currently opened in another window
if (findWindowOnWorkspace(windows, getWorkspaceIdentifier(path))) {
const options: MessageBoxOptions = {
title: product.nameLong,
type: 'info',
buttons: [localize('ok', "OK")],
message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)),
detail: localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."),
noLink: true
};
await this.dialogMainService.showMessageBox(options, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
return false;
}
return true; // OK
}
private doEnterWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): IEnterWorkspaceResult {
window.focus();
// Register window for backups and migrate current backups over
let backupPath: string | undefined;
if (!window.config.extensionDevelopmentPath) {
backupPath = this.backupMainService.registerWorkspaceBackupSync({ workspace, remoteAuthority: window.remoteAuthority }, window.config.backupPath);
}
// if the window was opened on an untitled workspace, delete it.
if (window.openedWorkspace && this.isUntitledWorkspace(window.openedWorkspace)) {
this.deleteUntitledWorkspaceSync(window.openedWorkspace);
}
// Update window configuration properly based on transition to workspace
window.config.folderUri = undefined;
window.config.workspace = workspace;
window.config.backupPath = backupPath;
return { workspace, backupPath };
}
}
function getWorkspaceId(configPath: URI): string {

View File

@@ -23,13 +23,13 @@ export class WorkspacesService implements AddFirstParameterToFunctions<IWorkspac
//#region Workspace Management
async enterWorkspace(windowId: number, path: URI): Promise<IEnterWorkspaceResult | undefined> {
async enterWorkspace(windowId: number, path: URI): Promise<IEnterWorkspaceResult | null> {
const window = this.windowsMainService.getWindowById(windowId);
if (window) {
return this.windowsMainService.enterWorkspace(window, path);
return this.workspacesMainService.enterWorkspace(window, this.windowsMainService.getWindows(), path);
}
return undefined;
return null;
}
createUntitledWorkspace(windowId: number, folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {

View File

@@ -18,6 +18,7 @@ import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { isWindows } from 'vs/base/common/platform';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { dirname, joinPath } from 'vs/base/common/resources';
import { TestBackupMainService, TestDialogMainService } from 'vs/workbench/test/workbenchTestServices';
suite('WorkspacesMainService', () => {
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'workspacesservice');
@@ -43,7 +44,7 @@ suite('WorkspacesMainService', () => {
let service: WorkspacesMainService;
setup(async () => {
service = new WorkspacesMainService(environmentService, logService);
service = new WorkspacesMainService(environmentService, logService, new TestBackupMainService(), new TestDialogMainService());
// Delete any existing backups completely and then re-create it.
await pfs.rimraf(untitledWorkspacesHomePath, pfs.RimRafMode.MOVE);