Merge VS Code 1.31.1 (#4283)

This commit is contained in:
Matt Irvine
2019-03-15 13:09:45 -07:00
committed by GitHub
parent 7d31575149
commit 86bac90001
1716 changed files with 53308 additions and 48375 deletions

View File

@@ -13,7 +13,7 @@ import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/
import { IStateService } from 'vs/platform/state/common/state';
import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
import { hasArgs, asArray } from 'vs/platform/environment/node/argv';
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, app } from 'electron';
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences } from 'electron';
import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/paths';
import { ILifecycleService, UnloadReason, IWindowUnloadEvent, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -26,8 +26,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows';
import { IHistoryMainService } from 'vs/platform/history/common/history';
import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
import { TPromise } from 'vs/base/common/winjs.base';
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { Schemas } from 'vs/base/common/network';
@@ -35,7 +34,7 @@ import { normalizeNFC } from 'vs/base/common/normalization';
import { URI } from 'vs/base/common/uri';
import { Queue, timeout } from 'vs/base/common/async';
import { exists } from 'vs/base/node/pfs';
import { getComparisonKey, isEqual, normalizePath } from 'vs/base/common/resources';
import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename } from 'vs/base/common/resources';
import { endsWith } from 'vs/base/common/strings';
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
@@ -146,27 +145,21 @@ export class WindowsManager implements IWindowsMainService {
private _onWindowLoad = new Emitter<number>();
onWindowLoad: CommonEvent<number> = this._onWindowLoad.event;
private _onActiveWindowChanged = new Emitter<ICodeWindow>();
onActiveWindowChanged: CommonEvent<ICodeWindow> = this._onActiveWindowChanged.event;
private _onWindowReload = new Emitter<number>();
onWindowReload: CommonEvent<number> = this._onWindowReload.event;
private _onWindowsCountChanged = new Emitter<IWindowsCountChangedEvent>();
onWindowsCountChanged: CommonEvent<IWindowsCountChangedEvent> = this._onWindowsCountChanged.event;
constructor(
private readonly machineId: string,
@ILogService private logService: ILogService,
@IStateService private stateService: IStateService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ILifecycleService private lifecycleService: ILifecycleService,
@IBackupMainService private backupMainService: IBackupMainService,
@ITelemetryService private telemetryService: ITelemetryService,
@IConfigurationService private configurationService: IConfigurationService,
@IHistoryMainService private historyMainService: IHistoryMainService,
@IWorkspacesMainService private workspacesMainService: IWorkspacesMainService,
@IInstantiationService private instantiationService: IInstantiationService
@ILogService private readonly logService: ILogService,
@IStateService private readonly stateService: IStateService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IBackupMainService private readonly backupMainService: IBackupMainService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IHistoryMainService private readonly historyMainService: IHistoryMainService,
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
this.windowsState = this.getWindowsState();
if (!Array.isArray(this.windowsState.openedWindows)) {
@@ -174,7 +167,7 @@ export class WindowsManager implements IWindowsMainService {
}
this.dialogs = new Dialogs(environmentService, telemetryService, stateService, this);
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, this);
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, historyMainService, this);
}
private getWindowsState(): IWindowsState {
@@ -209,13 +202,6 @@ export class WindowsManager implements IWindowsMainService {
private registerListeners(): void {
// React to windows focus changes
app.on('browser-window-focus', () => {
setTimeout(() => {
this._onActiveWindowChanged.fire(this.getLastActiveWindow());
});
});
// React to workbench ready events from windows
ipc.on('vscode:workbenchReady', (event: any, windowId: number) => {
this.logService.trace('IPC#vscode-workbenchReady');
@@ -249,7 +235,7 @@ export class WindowsManager implements IWindowsMainService {
// clear last closed window state when a new window opens. this helps on macOS where
// otherwise closing the last window, opening a new window and then quitting would
// use the state of the previously closed window when restarting.
this.lastClosedWindowState = void 0;
this.lastClosedWindowState = undefined;
}
});
}
@@ -387,7 +373,7 @@ export class WindowsManager implements IWindowsMainService {
}
// collect all file inputs
let fileInputs: IFileInputs = void 0;
let fileInputs: IFileInputs = undefined;
for (const path of pathsToOpen) {
if (path.fileUri) {
if (!fileInputs) {
@@ -492,8 +478,8 @@ export class WindowsManager implements IWindowsMainService {
// Remember in recent document list (unless this opens for extension development)
// Also do not add paths when files are opened for diffing, only if opened individually
if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.cli.diff) {
const recentlyOpenedWorkspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[] = [];
if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.diffMode) {
const recentlyOpenedWorkspaces: Array<IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier> = [];
const recentlyOpenedFiles: URI[] = [];
pathsToOpen.forEach(win => {
@@ -513,7 +499,7 @@ export class WindowsManager implements IWindowsMainService {
// used for the edit operation is closed or loaded to a different folder so that the waiting
// process can continue. We do this by deleting the waitMarkerFilePath.
if (openConfig.context === OpenContext.CLI && openConfig.cli.wait && openConfig.cli.waitMarkerFilePath && usedWindows.length === 1 && usedWindows[0]) {
this.waitForWindowCloseOrLoad(usedWindows[0].id).then(() => fs.unlink(openConfig.cli.waitMarkerFilePath, error => void 0));
this.waitForWindowCloseOrLoad(usedWindows[0].id).then(() => fs.unlink(openConfig.cli.waitMarkerFilePath, error => undefined));
}
return usedWindows;
@@ -552,9 +538,6 @@ export class WindowsManager implements IWindowsMainService {
if (lastActiveWindow) {
usedWindows.push(this.doAddFoldersToExistingWindow(lastActiveWindow, foldersToAdd));
}
// Reset because we handled them
foldersToAdd = [];
}
// Handle files to open/diff or to create when we dont open a folder and we do not restore any folder/untitled from hot-exit
@@ -595,7 +578,7 @@ export class WindowsManager implements IWindowsMainService {
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, fileInputs));
// Reset these because we handled them
fileInputs = void 0;
fileInputs = undefined;
}
}
@@ -612,7 +595,7 @@ export class WindowsManager implements IWindowsMainService {
}));
// Reset these because we handled them
fileInputs = void 0;
fileInputs = undefined;
}
}
@@ -624,14 +607,14 @@ export class WindowsManager implements IWindowsMainService {
const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, workspaceToOpen)));
if (windowsOnWorkspace.length > 0) {
const windowOnWorkspace = windowsOnWorkspace[0];
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : void 0;
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : undefined;
// Do open files
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, fileInputsForWindow));
// Reset these because we handled them
if (fileInputsForWindow) {
fileInputs = void 0;
fileInputs = undefined;
}
openFolderInNewWindow = true; // any other folders to open must open in new window then
@@ -643,14 +626,14 @@ export class WindowsManager implements IWindowsMainService {
return; // ignore folders that are already open
}
const fileInputsForWindow = (fileInputs && !fileInputs.remoteAuthority) ? fileInputs : void 0;
const fileInputsForWindow = (fileInputs && !fileInputs.remoteAuthority) ? fileInputs : undefined;
// Do open folder
usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { workspace: workspaceToOpen }, openFolderInNewWindow, fileInputsForWindow));
// Reset these because we handled them
if (fileInputsForWindow) {
fileInputs = void 0;
fileInputs = undefined;
}
openFolderInNewWindow = true; // any other folders to open must open in new window then
@@ -666,14 +649,14 @@ export class WindowsManager implements IWindowsMainService {
const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, folderToOpen)));
if (windowsOnFolderPath.length > 0) {
const windowOnFolderPath = windowsOnFolderPath[0];
const fileInputsForWindow = fileInputs && fileInputs.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : void 0;
const fileInputsForWindow = fileInputs && fileInputs.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : undefined;
// Do open files
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, fileInputsForWindow));
// Reset these because we handled them
if (fileInputsForWindow) {
fileInputs = void 0;
fileInputs = undefined;
}
openFolderInNewWindow = true; // any other folders to open must open in new window then
@@ -687,14 +670,14 @@ export class WindowsManager implements IWindowsMainService {
}
const remoteAuthority = getRemoteAuthority(folderToOpen);
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : void 0;
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : undefined;
// Do open folder
usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderUri: folderToOpen, remoteAuthority }, openFolderInNewWindow, fileInputsForWindow));
// Reset these because we handled them
if (fileInputsForWindow) {
fileInputs = void 0;
fileInputs = undefined;
}
openFolderInNewWindow = true; // any other folders to open must open in new window then
@@ -705,7 +688,7 @@ export class WindowsManager implements IWindowsMainService {
if (emptyToRestore.length > 0) {
emptyToRestore.forEach(emptyWindowBackupInfo => {
const remoteAuthority = emptyWindowBackupInfo.remoteAuthority;
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : void 0;
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : undefined;
usedWindows.push(this.openInBrowserWindow({
userEnv: openConfig.userEnv,
@@ -720,7 +703,7 @@ export class WindowsManager implements IWindowsMainService {
// Reset these because we handled them
if (fileInputsForWindow) {
fileInputs = void 0;
fileInputs = undefined;
}
openFolderInNewWindow = true; // any other folders to open must open in new window then
@@ -732,7 +715,7 @@ export class WindowsManager implements IWindowsMainService {
if (fileInputs && !emptyToOpen) {
emptyToOpen++;
}
const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || void 0);
const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined);
for (let i = 0; i < emptyToOpen; i++) {
usedWindows.push(this.openInBrowserWindow({
userEnv: openConfig.userEnv,
@@ -745,7 +728,7 @@ export class WindowsManager implements IWindowsMainService {
}));
// Reset these because we handled them
fileInputs = void 0;
fileInputs = undefined;
openFolderInNewWindow = true; // any other window to open must open in new window then
}
}
@@ -834,8 +817,8 @@ export class WindowsManager implements IWindowsMainService {
// folders should be added to the existing window.
if (!openConfig.addMode && isCommandLineOrAPICall) {
const foldersToOpen = windowsToOpen.filter(path => !!path.folderUri);
if (foldersToOpen.length > 1) {
const workspace = this.workspacesMainService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri })));
if (foldersToOpen.length > 1 && foldersToOpen.every(f => f.folderUri.scheme === Schemas.file)) {
const workspace = this.workspacesMainService.createUntitledWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri })));
// Add workspace and remove folders thereby
windowsToOpen.push({ workspace, remoteAuthority: foldersToOpen[0].remoteAuthority });
@@ -886,7 +869,7 @@ export class WindowsManager implements IWindowsMainService {
private doExtractPathsFromCLI(cli: ParsedArgs): IPath[] {
const pathsToOpen: IPathToOpen[] = [];
const parseOptions: IPathParseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto, remoteAuthority: cli.remote || void 0 };
const parseOptions: IPathParseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto, remoteAuthority: cli.remote || undefined };
// folder uris
const folderUris = asArray(cli['folder-uri']);
@@ -1017,8 +1000,9 @@ export class WindowsManager implements IWindowsMainService {
// normalize URI
uri = normalizePath(uri);
if (endsWith(uri.path, '/')) {
uri = uri.with({ path: uri.path.substr(0, uri.path.length - 1) });
const uriPath = uri.path;
if (uriPath.length > 2 && endsWith(uriPath, '/')) {
uri = uri.with({ path: uriPath.substr(0, uriPath.length - 1) });
}
if (isFile) {
if (options && options.gotoLineMode) {
@@ -1074,8 +1058,8 @@ export class WindowsManager implements IWindowsMainService {
// File
return {
fileUri: URI.file(candidate),
lineNumber: gotoLineMode ? parsedPath.line : void 0,
columnNumber: gotoLineMode ? parsedPath.column : void 0,
lineNumber: gotoLineMode ? parsedPath.line : undefined,
columnNumber: gotoLineMode ? parsedPath.column : undefined,
remoteAuthority
};
}
@@ -1473,10 +1457,7 @@ export class WindowsManager implements IWindowsMainService {
// Only reload when the window has not vetoed this
this.lifecycleService.unload(win, UnloadReason.RELOAD).then(veto => {
if (!veto) {
win.reload(void 0, cli);
// Emit
this._onWindowReload.fire(win.id);
win.reload(undefined, cli);
}
});
}
@@ -1489,18 +1470,10 @@ export class WindowsManager implements IWindowsMainService {
});
}
saveAndEnterWorkspace(win: ICodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
return this.workspacesManager.saveAndEnterWorkspace(win, path).then(result => this.doEnterWorkspace(win, result));
}
enterWorkspace(win: ICodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
enterWorkspace(win: ICodeWindow, path: URI): Promise<IEnterWorkspaceResult> {
return this.workspacesManager.enterWorkspace(win, path).then(result => this.doEnterWorkspace(win, result));
}
createAndEnterWorkspace(win: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise<IEnterWorkspaceResult> {
return this.workspacesManager.createAndEnterWorkspace(win, folders, path).then(result => this.doEnterWorkspace(win, result));
}
private doEnterWorkspace(win: ICodeWindow, result: IEnterWorkspaceResult): IEnterWorkspaceResult {
// Mark as recently opened
@@ -1575,7 +1548,7 @@ export class WindowsManager implements IWindowsMainService {
openNewWindow(context: OpenContext, options?: INewWindowOptions): ICodeWindow[] {
let cli = this.environmentService.args;
let remote = options && options.remoteAuthority || void 0;
let remote = options && options.remoteAuthority || undefined;
if (cli && (cli.remote !== remote)) {
cli = { ...cli, remote };
}
@@ -1586,7 +1559,7 @@ export class WindowsManager implements IWindowsMainService {
return this.open({ context, cli: this.environmentService.args, forceNewTabbedWindow: true, forceEmpty: true });
}
waitForWindowCloseOrLoad(windowId: number): Thenable<void> {
waitForWindowCloseOrLoad(windowId: number): Promise<void> {
return new Promise<void>(resolve => {
function handler(id: number) {
if (id === windowId) {
@@ -1658,6 +1631,17 @@ export class WindowsManager implements IWindowsMainService {
// Unresponsive
if (error === WindowError.UNRESPONSIVE) {
if (window.isExtensionDevelopmentHost || window.isExtensionTestHost || (window.win && window.win.webContents && window.win.webContents.isDevToolsOpened())) {
// TODO@Ben Workaround for https://github.com/Microsoft/vscode/issues/56994
// In certain cases the window can report unresponsiveness because a breakpoint was hit
// and the process is stopped executing. The most typical cases are:
// - devtools are opened and debugging happens
// - window is an extensions development host that is being debugged
// - window is an extension test development host that is being debugged
return;
}
// Show Dialog
this.dialogs.showMessageBox({
title: product.nameLong,
type: 'warning',
@@ -1763,15 +1747,15 @@ export class WindowsManager implements IWindowsMainService {
this.dialogs.pickAndOpen(internalOptions);
}
showMessageBox(options: Electron.MessageBoxOptions, win?: ICodeWindow): Thenable<IMessageBoxResult> {
showMessageBox(options: Electron.MessageBoxOptions, win?: ICodeWindow): Promise<IMessageBoxResult> {
return this.dialogs.showMessageBox(options, win);
}
showSaveDialog(options: Electron.SaveDialogOptions, win?: ICodeWindow): Thenable<string> {
showSaveDialog(options: Electron.SaveDialogOptions, win?: ICodeWindow): Promise<string> {
return this.dialogs.showSaveDialog(options, win);
}
showOpenDialog(options: Electron.OpenDialogOptions, win?: ICodeWindow): Thenable<string[]> {
showOpenDialog(options: Electron.OpenDialogOptions, win?: ICodeWindow): Promise<string[]> {
return this.dialogs.showOpenDialog(options, win);
}
@@ -1833,6 +1817,7 @@ class Dialogs {
if (numberOfPaths) {
this.windowsMainService.open({
context: OpenContext.DIALOG,
contextWindowId: options.windowId,
cli: this.environmentService.args,
urisToOpen: paths,
forceNewWindow: options.forceNewWindow,
@@ -1842,7 +1827,7 @@ class Dialogs {
});
}
private getFileOrFolderUris(options: IInternalNativeOpenDialogOptions): TPromise<URI[]> {
private getFileOrFolderUris(options: IInternalNativeOpenDialogOptions): Promise<URI[]> {
// Ensure dialog options
if (!options.dialogOptions) {
@@ -1856,7 +1841,7 @@ class Dialogs {
// Ensure properties
if (typeof options.pickFiles === 'boolean' || typeof options.pickFolders === 'boolean') {
options.dialogOptions.properties = void 0; // let it override based on the booleans
options.dialogOptions.properties = undefined; // let it override based on the booleans
if (options.pickFiles && options.pickFolders) {
options.dialogOptions.properties = ['multiSelections', 'openDirectory', 'openFile', 'createDirectory'];
@@ -1883,7 +1868,7 @@ class Dialogs {
return paths.map(path => URI.file(path));
}
return void 0;
return undefined;
});
}
@@ -1901,17 +1886,17 @@ class Dialogs {
return windowDialogQueue;
}
showMessageBox(options: Electron.MessageBoxOptions, window?: ICodeWindow): Thenable<IMessageBoxResult> {
showMessageBox(options: Electron.MessageBoxOptions, window?: ICodeWindow): Promise<IMessageBoxResult> {
return this.getDialogQueue(window).queue(() => {
return new Promise(resolve => {
dialog.showMessageBox(window ? window.win : void 0, options, (response: number, checkboxChecked: boolean) => {
dialog.showMessageBox(window ? window.win : undefined, options, (response: number, checkboxChecked: boolean) => {
resolve({ button: response, checkboxChecked });
});
});
});
}
showSaveDialog(options: Electron.SaveDialogOptions, window?: ICodeWindow): Thenable<string> {
showSaveDialog(options: Electron.SaveDialogOptions, window?: ICodeWindow): Promise<string> {
function normalizePath(path: string): string {
if (path && isMacintosh) {
@@ -1923,14 +1908,14 @@ class Dialogs {
return this.getDialogQueue(window).queue(() => {
return new Promise(resolve => {
dialog.showSaveDialog(window ? window.win : void 0, options, path => {
dialog.showSaveDialog(window ? window.win : undefined, options, path => {
resolve(normalizePath(path));
});
});
});
}
showOpenDialog(options: Electron.OpenDialogOptions, window?: ICodeWindow): Thenable<string[]> {
showOpenDialog(options: Electron.OpenDialogOptions, window?: ICodeWindow): Promise<string[]> {
function normalizePaths(paths: string[]): string[] {
if (paths && paths.length > 0 && isMacintosh) {
@@ -1948,14 +1933,14 @@ class Dialogs {
if (options.defaultPath) {
validatePathPromise = exists(options.defaultPath).then(exists => {
if (!exists) {
options.defaultPath = void 0;
options.defaultPath = undefined;
}
});
}
// Show dialog and wrap as promise
validatePathPromise.then(() => {
dialog.showOpenDialog(window ? window.win : void 0, options, paths => {
dialog.showOpenDialog(window ? window.win : undefined, options, paths => {
resolve(normalizePaths(paths));
});
});
@@ -1970,68 +1955,42 @@ class WorkspacesManager {
private workspacesMainService: IWorkspacesMainService,
private backupMainService: IBackupMainService,
private environmentService: IEnvironmentService,
private windowsMainService: IWindowsMainService
private historyMainService: IHistoryMainService,
private windowsMainService: IWindowsMainService,
) {
}
saveAndEnterWorkspace(window: ICodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
if (!window || !window.win || !window.isReady || !window.openedWorkspace || !path || !this.isValidTargetWorkspacePath(window, path)) {
return TPromise.as(null); // return early if the window is not ready or disposed or does not have a workspace
}
return this.doSaveAndOpenWorkspace(window, window.openedWorkspace, path);
}
enterWorkspace(window: ICodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
enterWorkspace(window: ICodeWindow, path: URI): Promise<IEnterWorkspaceResult | null> {
if (!window || !window.win || !window.isReady) {
return TPromise.as(null); // return early if the window is not ready or disposed
return Promise.resolve(null); // return early if the window is not ready or disposed
}
return this.isValidTargetWorkspacePath(window, path).then(isValid => {
if (!isValid) {
return TPromise.as<IEnterWorkspaceResult>(null); // return early if the workspace is not valid
return null; // return early if the workspace is not valid
}
return this.workspacesMainService.resolveWorkspace(path).then(workspace => {
return this.doOpenWorkspace(window, workspace);
});
const workspaceIdentifier = this.workspacesMainService.getWorkspaceIdentifier(path);
return this.doOpenWorkspace(window, workspaceIdentifier);
});
}
createAndEnterWorkspace(window: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise<IEnterWorkspaceResult> {
if (!window || !window.win || !window.isReady) {
return TPromise.as(null); // return early if the window is not ready or disposed
}
return this.isValidTargetWorkspacePath(window, path).then(isValid => {
if (!isValid) {
return TPromise.as(null); // return early if the workspace is not valid
}
return this.workspacesMainService.createWorkspace(folders).then(workspace => {
return this.doSaveAndOpenWorkspace(window, workspace, path);
});
});
}
private isValidTargetWorkspacePath(window: ICodeWindow, path?: string): TPromise<boolean> {
private isValidTargetWorkspacePath(window: ICodeWindow, path?: URI): Promise<boolean> {
if (!path) {
return TPromise.wrap(true);
return Promise.resolve(true);
}
if (window.openedWorkspace && window.openedWorkspace.configPath === path) {
return TPromise.wrap(false); // window is already opened on a workspace with that path
if (window.openedWorkspace && isEqual(URI.file(window.openedWorkspace.configPath), path)) {
return Promise.resolve(false); // window is already opened on a workspace with that path
}
// Prevent overwriting a workspace that is currently opened in another window
if (findWindowOnWorkspace(this.windowsMainService.getWindows(), { id: this.workspacesMainService.getWorkspaceId(path), configPath: path })) {
if (findWindowOnWorkspace(this.windowsMainService.getWindows(), this.workspacesMainService.getWorkspaceIdentifier(path))) {
const options: Electron.MessageBoxOptions = {
title: product.nameLong,
type: 'info',
buttons: [localize('ok', "OK")],
message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)),
message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", resourcesBasename(path)),
detail: localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."),
noLink: true
};
@@ -2039,18 +1998,7 @@ class WorkspacesManager {
return this.windowsMainService.showMessageBox(options, this.windowsMainService.getFocusedWindow()).then(() => false);
}
return TPromise.wrap(true); // OK
}
private doSaveAndOpenWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier, path?: string): TPromise<IEnterWorkspaceResult> {
let savePromise: TPromise<IWorkspaceIdentifier>;
if (path) {
savePromise = this.workspacesMainService.saveWorkspace(workspace, path);
} else {
savePromise = TPromise.as(workspace);
}
return savePromise.then(workspace => this.doOpenWorkspace(window, workspace));
return Promise.resolve(true); // OK
}
private doOpenWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): IEnterWorkspaceResult {
@@ -2062,8 +2010,13 @@ class WorkspacesManager {
backupPath = this.backupMainService.registerWorkspaceBackupSync(workspace, window.config.backupPath);
}
// if the window was opened on an untitled workspace, delete it.
if (window.openedWorkspace && this.workspacesMainService.isUntitledWorkspace(window.openedWorkspace)) {
this.workspacesMainService.deleteUntitledWorkspaceSync(window.openedWorkspace);
}
// Update window configuration properly based on transition to workspace
window.config.folderUri = void 0;
window.config.folderUri = undefined;
window.config.workspace = workspace;
window.config.backupPath = backupPath;
@@ -2074,7 +2027,7 @@ class WorkspacesManager {
const window = this.windowsMainService.getWindowById(options.windowId) || this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();
this.windowsMainService.pickFileAndOpen({
windowId: window ? window.id : void 0,
windowId: window ? window.id : undefined,
dialogOptions: {
buttonLabel: mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open")),
title: localize('openWorkspaceTitle', "Open Workspace"),
@@ -2088,7 +2041,7 @@ class WorkspacesManager {
});
}
promptToSaveUntitledWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): TPromise<boolean> {
promptToSaveUntitledWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): Promise<boolean> {
enum ConfirmResult {
SAVE,
DONT_SAVE,
@@ -2143,7 +2096,11 @@ class WorkspacesManager {
defaultPath: this.getUntitledWorkspaceSaveDialogDefaultPath(workspace)
}, window).then(target => {
if (target) {
return this.workspacesMainService.saveWorkspace(workspace, target).then(() => false, () => false);
return this.workspacesMainService.saveWorkspaceAs(workspace, target).then(savedWorkspace => {
this.historyMainService.addRecentlyOpened([savedWorkspace], []);
this.workspacesMainService.deleteUntitledWorkspaceSync(workspace);
return false;
}, () => false);
}
return true; // keep veto if no target was provided
@@ -2156,7 +2113,7 @@ class WorkspacesManager {
private getUntitledWorkspaceSaveDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string {
if (workspace) {
if (isSingleFolderWorkspaceIdentifier(workspace)) {
return workspace.scheme === Schemas.file ? dirname(workspace.fsPath) : void 0;
return workspace.scheme === Schemas.file ? dirname(workspace.fsPath) : undefined;
}
const resolvedWorkspace = this.workspacesMainService.resolveWorkspaceSync(workspace.configPath);
@@ -2169,6 +2126,6 @@ class WorkspacesManager {
}
}
return void 0;
return undefined;
}
}