mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Refresh master with initial release/0.24 snapshot (#332)
* Initial port of release/0.24 source code * Fix additional headers * Fix a typo in launch.json
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as path from 'path';
|
||||
import { basename, normalize, join, dirname } from 'path';
|
||||
import * as fs from 'original-fs';
|
||||
import { localize } from 'vs/nls';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
@@ -19,7 +19,7 @@ import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/pa
|
||||
import { ILifecycleService, UnloadReason, IWindowUnloadEvent } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, ReadyState } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, ReadyState, IPathsToWaitFor, IEnterWorkspaceResult } from 'vs/platform/windows/common/windows';
|
||||
import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderPath } from 'vs/code/node/windowsFinder';
|
||||
import CommonEvent, { Emitter } from 'vs/base/common/event';
|
||||
import product from 'vs/platform/node/product';
|
||||
@@ -29,9 +29,12 @@ import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent } fr
|
||||
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, ISingleFolderWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspacesMainService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, IWorkspaceFolderCreationData } 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';
|
||||
import { normalizeNFC } from 'vs/base/common/strings';
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
||||
enum WindowError {
|
||||
UNRESPONSIVE,
|
||||
@@ -77,6 +80,7 @@ interface IOpenBrowserWindowOptions {
|
||||
filesToOpen?: IPath[];
|
||||
filesToCreate?: IPath[];
|
||||
filesToDiff?: IPath[];
|
||||
filesToWait?: IPathsToWaitFor;
|
||||
|
||||
forceNewWindow?: boolean;
|
||||
windowToUse?: CodeWindow;
|
||||
@@ -113,6 +117,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
private lastClosedWindowState: IWindowState;
|
||||
|
||||
private fileDialog: FileDialog;
|
||||
private workspacesManager: WorkspacesManager;
|
||||
|
||||
private _onWindowReady = new Emitter<CodeWindow>();
|
||||
onWindowReady: CommonEvent<CodeWindow> = this._onWindowReady.event;
|
||||
@@ -120,6 +125,9 @@ export class WindowsManager implements IWindowsMainService {
|
||||
private _onWindowClose = new Emitter<number>();
|
||||
onWindowClose: CommonEvent<number> = this._onWindowClose.event;
|
||||
|
||||
private _onWindowLoad = new Emitter<number>();
|
||||
onWindowLoad: CommonEvent<number> = this._onWindowLoad.event;
|
||||
|
||||
private _onActiveWindowChanged = new Emitter<CodeWindow>();
|
||||
onActiveWindowChanged: CommonEvent<CodeWindow> = this._onActiveWindowChanged.event;
|
||||
|
||||
@@ -142,7 +150,9 @@ export class WindowsManager implements IWindowsMainService {
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
this.windowsState = this.storageService.getItem<IWindowsState>(WindowsManager.windowsStateStorageKey) || { openedWindows: [] };
|
||||
|
||||
this.fileDialog = new FileDialog(environmentService, telemetryService, storageService, this);
|
||||
this.workspacesManager = new WorkspacesManager(workspacesService, lifecycleService, backupService, environmentService, this);
|
||||
|
||||
this.migrateLegacyWindowState();
|
||||
}
|
||||
@@ -172,11 +182,6 @@ export class WindowsManager implements IWindowsMainService {
|
||||
state.folderPath = state.workspacePath;
|
||||
state.workspacePath = void 0;
|
||||
}
|
||||
|
||||
// TODO@Ben migration to new workspace ID
|
||||
if (state.workspace) {
|
||||
state.workspace.id = this.workspacesService.getWorkspaceId(state.workspace.configPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -273,7 +278,6 @@ export class WindowsManager implements IWindowsMainService {
|
||||
private onBeforeQuit(): void {
|
||||
const currentWindowsState: ILegacyWindowsState = {
|
||||
openedWindows: [],
|
||||
openedFolders: [], // TODO@Ben migration so that old clients do not fail over data (prevents NPEs)
|
||||
lastPluginDevelopmentHostWindow: this.windowsState.lastPluginDevelopmentHostWindow,
|
||||
lastActiveWindow: this.lastClosedWindowState
|
||||
};
|
||||
@@ -358,8 +362,8 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// When run with --add, take the folders that are to be opened as
|
||||
// folders that should be added to the currently active window.
|
||||
let foldersToAdd = [];
|
||||
if (openConfig.addMode && product.quality !== 'stable') { // TODO@Ben multi root
|
||||
let foldersToAdd: IPath[] = [];
|
||||
if (openConfig.addMode) {
|
||||
foldersToAdd = pathsToOpen.filter(path => !!path.folderPath).map(path => ({ filePath: path.folderPath }));
|
||||
pathsToOpen = pathsToOpen.filter(path => !path.folderPath);
|
||||
}
|
||||
@@ -376,6 +380,12 @@ export class WindowsManager implements IWindowsMainService {
|
||||
filesToCreate = []; // diff ignores other files that do not exist
|
||||
}
|
||||
|
||||
// When run with --wait, make sure we keep the paths to wait for
|
||||
let filesToWait: IPathsToWaitFor;
|
||||
if (openConfig.cli.wait && openConfig.cli.waitMarkerFilePath) {
|
||||
filesToWait = { paths: [...filesToDiff, ...filesToOpen, ...filesToCreate], waitMarkerFilePath: openConfig.cli.waitMarkerFilePath };
|
||||
}
|
||||
|
||||
//
|
||||
// These are windows to open to show workspaces
|
||||
//
|
||||
@@ -399,7 +409,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
workspacesToRestore.push(...this.workspacesService.getUntitledWorkspacesSync()); // collect from previous window session
|
||||
|
||||
emptyToRestore = this.backupService.getEmptyWindowBackupPaths();
|
||||
emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderPath && w.backupPath).map(w => path.basename(w.backupPath))); // add empty windows with backupPath
|
||||
emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderPath && w.backupPath).map(w => basename(w.backupPath))); // add empty windows with backupPath
|
||||
emptyToRestore = arrays.distinct(emptyToRestore); // prevent duplicates
|
||||
}
|
||||
|
||||
@@ -409,13 +419,45 @@ export class WindowsManager implements IWindowsMainService {
|
||||
const emptyToOpen = pathsToOpen.filter(win => !win.workspace && !win.folderPath && !win.filePath && !win.backupPath).length;
|
||||
|
||||
// Open based on config
|
||||
const usedWindows = this.doOpen(openConfig, workspacesToOpen, workspacesToRestore, foldersToOpen, foldersToRestore, emptyToRestore, emptyToOpen, filesToOpen, filesToCreate, filesToDiff, foldersToAdd);
|
||||
const usedWindows = this.doOpen(openConfig, workspacesToOpen, workspacesToRestore, foldersToOpen, foldersToRestore, emptyToRestore, emptyToOpen, filesToOpen, filesToCreate, filesToDiff, filesToWait, foldersToAdd);
|
||||
|
||||
// Make sure the last active window gets focus if we opened multiple
|
||||
if (usedWindows.length > 1 && this.windowsState.lastActiveWindow) {
|
||||
let lastActiveWindw = usedWindows.filter(w => w.backupPath === this.windowsState.lastActiveWindow.backupPath);
|
||||
if (lastActiveWindw.length) {
|
||||
lastActiveWindw[0].focus();
|
||||
// Make sure to pass focus to the most relevant of the windows if we open multiple
|
||||
if (usedWindows.length > 1) {
|
||||
let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && (!openConfig.pathsToOpen || !openConfig.pathsToOpen.length);
|
||||
let focusLastOpened = true;
|
||||
let focusLastWindow = true;
|
||||
|
||||
// 1.) focus last active window if we are not instructed to open any paths
|
||||
if (focusLastActive) {
|
||||
const lastActiveWindw = usedWindows.filter(w => w.backupPath === this.windowsState.lastActiveWindow.backupPath);
|
||||
if (lastActiveWindw.length) {
|
||||
lastActiveWindw[0].focus();
|
||||
focusLastOpened = false;
|
||||
focusLastWindow = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 2.) if instructed to open paths, focus last window which is not restored
|
||||
if (focusLastOpened) {
|
||||
for (let i = usedWindows.length - 1; i >= 0; i--) {
|
||||
const usedWindow = usedWindows[i];
|
||||
if (
|
||||
(usedWindow.openedWorkspace && workspacesToRestore.some(workspace => workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace
|
||||
(usedWindow.openedFolderPath && foldersToRestore.some(folder => folder === usedWindow.openedFolderPath)) || // skip over restored folder
|
||||
(usedWindow.backupPath && emptyToRestore.some(empty => empty === basename(usedWindow.backupPath))) // skip over restored empty window
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
usedWindow.focus();
|
||||
focusLastWindow = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 3.) finally, always ensure to have at least last used window focused
|
||||
if (focusLastWindow) {
|
||||
usedWindows[usedWindows.length - 1].focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,10 +479,10 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
|
||||
// If we got started with --wait from the CLI, we need to signal to the outside when the window
|
||||
// used for the edit operation is closed so that the waiting process can continue. We do this by
|
||||
// deleting the waitMarkerFilePath.
|
||||
// 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.waitForWindowClose(usedWindows[0].id).done(() => fs.unlink(openConfig.cli.waitMarkerFilePath, error => void 0));
|
||||
this.waitForWindowCloseOrLoad(usedWindows[0].id).done(() => fs.unlink(openConfig.cli.waitMarkerFilePath, error => void 0));
|
||||
}
|
||||
|
||||
return usedWindows;
|
||||
@@ -467,6 +509,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
filesToOpen: IPath[],
|
||||
filesToCreate: IPath[],
|
||||
filesToDiff: IPath[],
|
||||
filesToWait: IPathsToWaitFor,
|
||||
foldersToAdd: IPath[]
|
||||
) {
|
||||
const usedWindows: CodeWindow[] = [];
|
||||
@@ -491,7 +534,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// Find suitable window or folder path to open files in
|
||||
const fileToCheck = filesToOpen[0] || filesToCreate[0] || filesToDiff[0];
|
||||
const bestWindowOrFolder = findBestWindowOrFolderForFile({
|
||||
let bestWindowOrFolder = findBestWindowOrFolderForFile({
|
||||
windows: WindowsManager.WINDOWS,
|
||||
newWindow: openFilesInNewWindow,
|
||||
reuseWindow: openConfig.forceReuseWindow,
|
||||
@@ -501,6 +544,12 @@ export class WindowsManager implements IWindowsMainService {
|
||||
workspaceResolver: workspace => this.workspacesService.resolveWorkspaceSync(workspace.configPath)
|
||||
});
|
||||
|
||||
// Special case: we started with --wait and we got back a folder to open. In this case
|
||||
// we actually prefer to not open the folder but operate purely on the file.
|
||||
if (typeof bestWindowOrFolder === 'string' && filesToWait) {
|
||||
bestWindowOrFolder = !openFilesInNewWindow ? this.getLastActiveWindow() : null;
|
||||
}
|
||||
|
||||
// We found a window to open the files in
|
||||
if (bestWindowOrFolder instanceof CodeWindow) {
|
||||
|
||||
@@ -518,12 +567,13 @@ export class WindowsManager implements IWindowsMainService {
|
||||
else {
|
||||
|
||||
// Do open files
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(bestWindowOrFolder, filesToOpen, filesToCreate, filesToDiff));
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(bestWindowOrFolder, filesToOpen, filesToCreate, filesToDiff, filesToWait));
|
||||
|
||||
// Reset these because we handled them
|
||||
filesToOpen = [];
|
||||
filesToCreate = [];
|
||||
filesToDiff = [];
|
||||
filesToWait = void 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,6 +591,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
filesToOpen,
|
||||
filesToCreate,
|
||||
filesToDiff,
|
||||
filesToWait,
|
||||
forceNewWindow: true
|
||||
}));
|
||||
|
||||
@@ -548,11 +599,12 @@ export class WindowsManager implements IWindowsMainService {
|
||||
filesToOpen = [];
|
||||
filesToCreate = [];
|
||||
filesToDiff = [];
|
||||
filesToWait = void 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle workspaces to open (instructed and to restore)
|
||||
const allWorkspacesToOpen = arrays.distinct([...workspacesToOpen, ...workspacesToRestore], workspace => workspace.id); // prevent duplicates
|
||||
const allWorkspacesToOpen = arrays.distinct([...workspacesToRestore, ...workspacesToOpen], workspace => workspace.id); // prevent duplicates
|
||||
if (allWorkspacesToOpen.length > 0) {
|
||||
|
||||
// Check for existing instances
|
||||
@@ -561,12 +613,13 @@ export class WindowsManager implements IWindowsMainService {
|
||||
const windowOnWorkspace = windowsOnWorkspace[0];
|
||||
|
||||
// Do open files
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(windowOnWorkspace, filesToOpen, filesToCreate, filesToDiff));
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(windowOnWorkspace, filesToOpen, filesToCreate, filesToDiff, filesToWait));
|
||||
|
||||
// Reset these because we handled them
|
||||
filesToOpen = [];
|
||||
filesToCreate = [];
|
||||
filesToDiff = [];
|
||||
filesToWait = void 0;
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
}
|
||||
@@ -578,19 +631,20 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
|
||||
// Do open folder
|
||||
usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { workspace: workspaceToOpen }, openFolderInNewWindow, filesToOpen, filesToCreate, filesToDiff));
|
||||
usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { workspace: workspaceToOpen }, openFolderInNewWindow, filesToOpen, filesToCreate, filesToDiff, filesToWait));
|
||||
|
||||
// Reset these because we handled them
|
||||
filesToOpen = [];
|
||||
filesToCreate = [];
|
||||
filesToDiff = [];
|
||||
filesToWait = void 0;
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
});
|
||||
}
|
||||
|
||||
// Handle folders to open (instructed and to restore)
|
||||
const allFoldersToOpen = arrays.distinct([...foldersToOpen, ...foldersToRestore], folder => isLinux ? folder : folder.toLowerCase()); // prevent duplicates
|
||||
const allFoldersToOpen = arrays.distinct([...foldersToRestore, ...foldersToOpen], folder => isLinux ? folder : folder.toLowerCase()); // prevent duplicates
|
||||
if (allFoldersToOpen.length > 0) {
|
||||
|
||||
// Check for existing instances
|
||||
@@ -599,12 +653,13 @@ export class WindowsManager implements IWindowsMainService {
|
||||
const windowOnFolderPath = windowsOnFolderPath[0];
|
||||
|
||||
// Do open files
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(windowOnFolderPath, filesToOpen, filesToCreate, filesToDiff));
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(windowOnFolderPath, filesToOpen, filesToCreate, filesToDiff, filesToWait));
|
||||
|
||||
// Reset these because we handled them
|
||||
filesToOpen = [];
|
||||
filesToCreate = [];
|
||||
filesToDiff = [];
|
||||
filesToWait = void 0;
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
}
|
||||
@@ -616,12 +671,13 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
|
||||
// Do open folder
|
||||
usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderPath: folderToOpen }, openFolderInNewWindow, filesToOpen, filesToCreate, filesToDiff));
|
||||
usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderPath: folderToOpen }, openFolderInNewWindow, filesToOpen, filesToCreate, filesToDiff, filesToWait));
|
||||
|
||||
// Reset these because we handled them
|
||||
filesToOpen = [];
|
||||
filesToCreate = [];
|
||||
filesToDiff = [];
|
||||
filesToWait = void 0;
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
});
|
||||
@@ -637,6 +693,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
filesToOpen,
|
||||
filesToCreate,
|
||||
filesToDiff,
|
||||
filesToWait,
|
||||
forceNewWindow: true,
|
||||
emptyWindowBackupFolder
|
||||
}));
|
||||
@@ -645,6 +702,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
filesToOpen = [];
|
||||
filesToCreate = [];
|
||||
filesToDiff = [];
|
||||
filesToWait = void 0;
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
});
|
||||
@@ -667,11 +725,11 @@ export class WindowsManager implements IWindowsMainService {
|
||||
return arrays.distinct(usedWindows);
|
||||
}
|
||||
|
||||
private doOpenFilesInExistingWindow(window: CodeWindow, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[]): CodeWindow {
|
||||
private doOpenFilesInExistingWindow(window: CodeWindow, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[], filesToWait: IPathsToWaitFor): CodeWindow {
|
||||
window.focus(); // make sure window has focus
|
||||
|
||||
window.ready().then(readyWindow => {
|
||||
readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff });
|
||||
readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff, filesToWait });
|
||||
});
|
||||
|
||||
return window;
|
||||
@@ -687,7 +745,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
return window;
|
||||
}
|
||||
|
||||
private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, openInNewWindow: boolean, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[], windowToUse?: CodeWindow): CodeWindow {
|
||||
private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, openInNewWindow: boolean, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[], filesToWait: IPathsToWaitFor, windowToUse?: CodeWindow): CodeWindow {
|
||||
const browserWindow = this.openInBrowserWindow({
|
||||
userEnv: openConfig.userEnv,
|
||||
cli: openConfig.cli,
|
||||
@@ -697,6 +755,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
filesToOpen,
|
||||
filesToCreate,
|
||||
filesToDiff,
|
||||
filesToWait,
|
||||
forceNewWindow: openInNewWindow,
|
||||
windowToUse
|
||||
});
|
||||
@@ -734,10 +793,10 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// This will ensure to open these folders in one window instead of multiple
|
||||
// If we are in addMode, we should not do this because in that case all
|
||||
// folders should be added to the existing window.
|
||||
if (!openConfig.addMode && isCommandLineOrAPICall && product.quality !== 'stable') { // TODO@Ben multi root
|
||||
if (!openConfig.addMode && isCommandLineOrAPICall) {
|
||||
const foldersToOpen = windowsToOpen.filter(path => !!path.folderPath);
|
||||
if (foldersToOpen.length > 1) {
|
||||
const workspace = this.workspacesService.createWorkspaceSync(foldersToOpen.map(folder => folder.folderPath));
|
||||
const workspace = this.workspacesService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: URI.file(folder.folderPath) })));
|
||||
|
||||
// Add workspace and remove folders thereby
|
||||
windowsToOpen.push({ workspace });
|
||||
@@ -754,7 +813,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// Warn if the requested path to open does not exist
|
||||
if (!path) {
|
||||
const options: Electron.ShowMessageBoxOptions = {
|
||||
const options: Electron.MessageBoxOptions = {
|
||||
title: product.nameLong,
|
||||
type: 'info',
|
||||
buttons: [localize('ok', "OK")],
|
||||
@@ -879,7 +938,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
restoreWindows = ((windowConfig && windowConfig.restoreWindows) || 'one') as RestoreWindowsSetting;
|
||||
|
||||
if (restoreWindows === 'one' /* default */ && windowConfig && windowConfig.reopenFolders) {
|
||||
restoreWindows = windowConfig.reopenFolders; // TODO@Ben migration
|
||||
restoreWindows = windowConfig.reopenFolders; // TODO@Ben migration from deprecated window.reopenFolders setting
|
||||
}
|
||||
|
||||
if (['all', 'folders', 'one', 'none'].indexOf(restoreWindows) === -1) {
|
||||
@@ -903,7 +962,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
anyPath = parsedPath.path;
|
||||
}
|
||||
|
||||
const candidate = path.normalize(anyPath);
|
||||
const candidate = normalize(anyPath);
|
||||
try {
|
||||
const candidateStat = fs.statSync(candidate);
|
||||
if (candidateStat) {
|
||||
@@ -1014,6 +1073,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
configuration.filesToOpen = options.filesToOpen;
|
||||
configuration.filesToCreate = options.filesToCreate;
|
||||
configuration.filesToDiff = options.filesToDiff;
|
||||
configuration.filesToWait = options.filesToWait;
|
||||
configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir;
|
||||
|
||||
// if we know the backup folder upfront (for empty windows to restore), we can set it
|
||||
@@ -1021,7 +1081,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// For all other cases we first call into registerEmptyWindowBackupSync() to set it before
|
||||
// loading the window.
|
||||
if (options.emptyWindowBackupFolder) {
|
||||
configuration.backupPath = path.join(this.environmentService.backupHome, options.emptyWindowBackupFolder);
|
||||
configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupFolder);
|
||||
}
|
||||
|
||||
let window: CodeWindow;
|
||||
@@ -1108,6 +1168,9 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// Load it
|
||||
window.load(configuration);
|
||||
|
||||
// Signal event
|
||||
this._onWindowLoad.fire(window.id);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1258,97 +1321,32 @@ export class WindowsManager implements IWindowsMainService {
|
||||
});
|
||||
}
|
||||
|
||||
public saveAndOpenWorkspace(window: CodeWindow, path: string): TPromise<void> {
|
||||
if (!window || !window.win || window.readyState !== ReadyState.READY || !window.openedWorkspace || !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);
|
||||
public saveAndEnterWorkspace(win: CodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
|
||||
return this.workspacesManager.saveAndEnterWorkspace(win, path).then(result => this.doEnterWorkspace(win, result));
|
||||
}
|
||||
|
||||
public createAndOpenWorkspace(window: CodeWindow, folders?: string[], path?: string): TPromise<void> {
|
||||
if (!window || !window.win || window.readyState !== ReadyState.READY) {
|
||||
return TPromise.as(null); // return early if the window is not ready or disposed
|
||||
}
|
||||
|
||||
return this.workspacesService.createWorkspace(folders).then(workspace => {
|
||||
return this.doSaveAndOpenWorkspace(window, workspace, path);
|
||||
});
|
||||
public createAndEnterWorkspace(win: CodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise<IEnterWorkspaceResult> {
|
||||
return this.workspacesManager.createAndEnterWorkspace(win, folders, path).then(result => this.doEnterWorkspace(win, result));
|
||||
}
|
||||
|
||||
private doSaveAndOpenWorkspace(window: CodeWindow, workspace: IWorkspaceIdentifier, path?: string): TPromise<void> {
|
||||
let savePromise: TPromise<IWorkspaceIdentifier>;
|
||||
if (path) {
|
||||
savePromise = this.workspacesService.saveWorkspace(workspace, path);
|
||||
} else {
|
||||
savePromise = TPromise.as(workspace);
|
||||
}
|
||||
private doEnterWorkspace(win: CodeWindow, result: IEnterWorkspaceResult): IEnterWorkspaceResult {
|
||||
|
||||
return savePromise.then(workspace => {
|
||||
window.focus();
|
||||
// Mark as recently opened
|
||||
this.historyService.addRecentlyOpened([result.workspace], []);
|
||||
|
||||
// Only open workspace when the window has not vetoed this
|
||||
return this.lifecycleService.unload(window, UnloadReason.RELOAD, workspace).done(veto => {
|
||||
if (!veto) {
|
||||
// Trigger Eevent to indicate load of workspace into window
|
||||
this._onWindowReady.fire(win);
|
||||
|
||||
// Register window for backups and migrate current backups over
|
||||
let backupPath: string;
|
||||
if (window.config && !window.config.extensionDevelopmentPath) {
|
||||
backupPath = this.backupService.registerWorkspaceBackupSync(workspace, window.config.backupPath);
|
||||
}
|
||||
|
||||
// Craft a new window configuration to use for the transition
|
||||
const configuration: IWindowConfiguration = mixin({}, window.config);
|
||||
configuration.folderPath = void 0;
|
||||
configuration.workspace = workspace;
|
||||
configuration.backupPath = backupPath;
|
||||
|
||||
// Reload
|
||||
window.reload(configuration);
|
||||
}
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public openWorkspace(window: CodeWindow = this.getLastActiveWindow()): void {
|
||||
let defaultPath: string;
|
||||
if (window && window.openedWorkspace && !this.workspacesService.isUntitledWorkspace(window.openedWorkspace)) {
|
||||
defaultPath = path.dirname(window.openedWorkspace.configPath);
|
||||
} else {
|
||||
defaultPath = this.getWorkspaceDialogDefaultPath(window ? (window.openedWorkspace || window.openedFolderPath) : void 0);
|
||||
}
|
||||
|
||||
this.pickFileAndOpen({
|
||||
windowId: window ? window.id : void 0,
|
||||
dialogOptions: {
|
||||
buttonLabel: mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open")),
|
||||
title: localize('openWorkspaceTitle', "Open Workspace"),
|
||||
filters: WORKSPACE_FILTER,
|
||||
properties: ['openFile'],
|
||||
defaultPath
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getWorkspaceDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string {
|
||||
let defaultPath: string;
|
||||
if (workspace) {
|
||||
if (isSingleFolderWorkspaceIdentifier(workspace)) {
|
||||
defaultPath = path.dirname(workspace);
|
||||
} else {
|
||||
const resolvedWorkspace = this.workspacesService.resolveWorkspaceSync(workspace.configPath);
|
||||
if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) {
|
||||
defaultPath = path.dirname(resolvedWorkspace.folders[0].path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defaultPath;
|
||||
public pickWorkspaceAndOpen(options: INativeOpenDialogOptions): void {
|
||||
this.workspacesManager.pickWorkspaceAndOpen(options);
|
||||
}
|
||||
|
||||
private onBeforeWindowUnload(e: IWindowUnloadEvent): void {
|
||||
const windowClosing = e.reason === UnloadReason.CLOSE;
|
||||
const windowLoading = e.reason === UnloadReason.LOAD;
|
||||
const windowClosing = (e.reason === UnloadReason.CLOSE);
|
||||
const windowLoading = (e.reason === UnloadReason.LOAD);
|
||||
if (!windowClosing && !windowLoading) {
|
||||
return; // only interested when window is closing or loading
|
||||
}
|
||||
@@ -1358,78 +1356,16 @@ export class WindowsManager implements IWindowsMainService {
|
||||
return; // only care about untitled workspaces to ask for saving
|
||||
}
|
||||
|
||||
if (e.window.config && !!e.window.config.extensionDevelopmentPath) {
|
||||
return; // do not ask to save workspace when doing extension development
|
||||
}
|
||||
|
||||
if (windowClosing && !isMacintosh && this.getWindowCount() === 1) {
|
||||
return; // Windows/Linux: quits when last window is closed, so do not ask then
|
||||
}
|
||||
|
||||
this.promptToSaveUntitledWorkspace(e, workspace);
|
||||
}
|
||||
|
||||
private promptToSaveUntitledWorkspace(e: IWindowUnloadEvent, workspace: IWorkspaceIdentifier): void {
|
||||
enum ConfirmResult {
|
||||
SAVE,
|
||||
DONT_SAVE,
|
||||
CANCEL
|
||||
}
|
||||
|
||||
const save = { label: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), result: ConfirmResult.SAVE };
|
||||
const dontSave = { label: mnemonicButtonLabel(localize({ key: 'doNotSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save")), result: ConfirmResult.DONT_SAVE };
|
||||
const cancel = { label: localize('cancel', "Cancel"), result: ConfirmResult.CANCEL };
|
||||
|
||||
const buttons: { label: string; result: ConfirmResult; }[] = [];
|
||||
if (isWindows) {
|
||||
buttons.push(save, dontSave, cancel);
|
||||
} else if (isLinux) {
|
||||
buttons.push(dontSave, cancel, save);
|
||||
} else {
|
||||
buttons.push(save, cancel, dontSave);
|
||||
}
|
||||
|
||||
const options: Electron.ShowMessageBoxOptions = {
|
||||
title: this.environmentService.appNameLong,
|
||||
message: localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"),
|
||||
detail: localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."),
|
||||
noLink: true,
|
||||
type: 'warning',
|
||||
buttons: buttons.map(button => button.label),
|
||||
cancelId: buttons.indexOf(cancel)
|
||||
};
|
||||
|
||||
if (isLinux) {
|
||||
options.defaultId = 2;
|
||||
}
|
||||
|
||||
const res = dialog.showMessageBox(e.window.win, options);
|
||||
|
||||
switch (buttons[res].result) {
|
||||
|
||||
// Cancel: veto unload
|
||||
case ConfirmResult.CANCEL:
|
||||
e.veto(true);
|
||||
break;
|
||||
|
||||
// Don't Save: delete workspace
|
||||
case ConfirmResult.DONT_SAVE:
|
||||
this.workspacesService.deleteUntitledWorkspaceSync(workspace);
|
||||
e.veto(false);
|
||||
break;
|
||||
|
||||
// Save: save workspace, but do not veto unload
|
||||
case ConfirmResult.SAVE: {
|
||||
const target = dialog.showSaveDialog(e.window.win, {
|
||||
buttonLabel: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")),
|
||||
title: localize('saveWorkspace', "Save Workspace"),
|
||||
filters: WORKSPACE_FILTER,
|
||||
defaultPath: this.getWorkspaceDialogDefaultPath(workspace)
|
||||
});
|
||||
|
||||
if (target) {
|
||||
e.veto(this.workspacesService.saveWorkspace(workspace, target).then(() => false, () => false));
|
||||
} else {
|
||||
e.veto(true); // keep veto if no target was provided
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle untitled workspaces with prompt as needed
|
||||
this.workspacesManager.promptToSaveUntitledWorkspace(e, workspace);
|
||||
}
|
||||
|
||||
public focusLastActive(cli: ParsedArgs, context: OpenContext): CodeWindow {
|
||||
@@ -1452,14 +1388,19 @@ export class WindowsManager implements IWindowsMainService {
|
||||
this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true });
|
||||
}
|
||||
|
||||
public waitForWindowClose(windowId: number): TPromise<void> {
|
||||
public waitForWindowCloseOrLoad(windowId: number): TPromise<void> {
|
||||
return new TPromise<void>(c => {
|
||||
const toDispose = this.onWindowClose(id => {
|
||||
function handler(id: number) {
|
||||
if (id === windowId) {
|
||||
toDispose.dispose();
|
||||
closeListener.dispose();
|
||||
loadListener.dispose();
|
||||
|
||||
c(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const closeListener = this.onWindowClose(id => handler(id));
|
||||
const loadListener = this.onWindowLoad(id => handler(id));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1515,7 +1456,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
dialog.showMessageBox(window.win, {
|
||||
title: product.nameLong,
|
||||
type: 'warning',
|
||||
buttons: [localize('reopen', "Reopen"), localize('wait', "Keep Waiting"), localize('close', "Close")],
|
||||
buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'wait', comment: ['&& denotes a mnemonic'] }, "&&Keep Waiting")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))],
|
||||
message: localize('appStalled', "The window is no longer responding"),
|
||||
detail: localize('appStalledDetail', "You can reopen or close the window or keep waiting."),
|
||||
noLink: true
|
||||
@@ -1538,7 +1479,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
dialog.showMessageBox(window.win, {
|
||||
title: product.nameLong,
|
||||
type: 'warning',
|
||||
buttons: [localize('reopen', "Reopen"), localize('close', "Close")],
|
||||
buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))],
|
||||
message: localize('appCrashed', "The window has crashed"),
|
||||
detail: localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."),
|
||||
noLink: true
|
||||
@@ -1657,6 +1598,7 @@ class FileDialog {
|
||||
|
||||
// Telemetry
|
||||
if (options.telemetryEventName) {
|
||||
// __GDPR__TODO__ Dynamic event names and dynamic properties. Can not be registered statically.
|
||||
this.telemetryService.publicLog(options.telemetryEventName, {
|
||||
...options.telemetryExtraData,
|
||||
outcome: numberOfPaths ? 'success' : 'canceled',
|
||||
@@ -1677,7 +1619,7 @@ class FileDialog {
|
||||
});
|
||||
}
|
||||
|
||||
public getFileOrFolderPaths(options: IInternalNativeOpenDialogOptions, clb: (paths: string[]) => void): void {
|
||||
private getFileOrFolderPaths(options: IInternalNativeOpenDialogOptions, clb: (paths: string[]) => void): void {
|
||||
|
||||
// Ensure dialog options
|
||||
if (!options.dialogOptions) {
|
||||
@@ -1702,13 +1644,20 @@ class FileDialog {
|
||||
options.dialogOptions.properties = ['multiSelections', options.pickFolders ? 'openDirectory' : 'openFile', 'createDirectory'];
|
||||
}
|
||||
|
||||
if (isMacintosh) {
|
||||
options.dialogOptions.properties.push('treatPackageAsDirectory'); // always drill into .app files
|
||||
}
|
||||
|
||||
// Show Dialog
|
||||
const focusedWindow = this.windowsMainService.getWindowById(options.windowId) || this.windowsMainService.getFocusedWindow();
|
||||
dialog.showOpenDialog(focusedWindow && focusedWindow.win, options.dialogOptions, paths => {
|
||||
if (paths && paths.length > 0) {
|
||||
if (isMacintosh) {
|
||||
paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS
|
||||
}
|
||||
|
||||
// Remember path in storage for next time
|
||||
this.storageService.setItem(FileDialog.workingDirPickerStorageKey, path.dirname(paths[0]));
|
||||
this.storageService.setItem(FileDialog.workingDirPickerStorageKey, dirname(paths[0]));
|
||||
|
||||
// Return
|
||||
return clb(paths);
|
||||
@@ -1717,4 +1666,201 @@ class FileDialog {
|
||||
return clb(void (0));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WorkspacesManager {
|
||||
|
||||
constructor(
|
||||
private workspacesService: IWorkspacesMainService,
|
||||
private lifecycleService: ILifecycleService,
|
||||
private backupService: IBackupMainService,
|
||||
private environmentService: IEnvironmentService,
|
||||
private windowsMainService: IWindowsMainService
|
||||
) {
|
||||
}
|
||||
|
||||
public saveAndEnterWorkspace(window: CodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
|
||||
if (!window || !window.win || window.readyState !== ReadyState.READY || !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);
|
||||
}
|
||||
|
||||
public createAndEnterWorkspace(window: CodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise<IEnterWorkspaceResult> {
|
||||
if (!window || !window.win || window.readyState !== ReadyState.READY || !this.isValidTargetWorkspacePath(window, path)) {
|
||||
return TPromise.as(null); // return early if the window is not ready or disposed
|
||||
}
|
||||
|
||||
return this.workspacesService.createWorkspace(folders).then(workspace => {
|
||||
return this.doSaveAndOpenWorkspace(window, workspace, path);
|
||||
});
|
||||
}
|
||||
|
||||
private isValidTargetWorkspacePath(window: CodeWindow, path?: string): boolean {
|
||||
if (!path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (window.openedWorkspace && 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(this.windowsMainService.getWindows(), { id: this.workspacesService.getWorkspaceId(path), configPath: path })) {
|
||||
const options: Electron.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
|
||||
};
|
||||
|
||||
const activeWindow = BrowserWindow.getFocusedWindow();
|
||||
if (activeWindow) {
|
||||
dialog.showMessageBox(activeWindow, options);
|
||||
} else {
|
||||
dialog.showMessageBox(options);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // OK
|
||||
}
|
||||
|
||||
private doSaveAndOpenWorkspace(window: CodeWindow, workspace: IWorkspaceIdentifier, path?: string): TPromise<IEnterWorkspaceResult> {
|
||||
let savePromise: TPromise<IWorkspaceIdentifier>;
|
||||
if (path) {
|
||||
savePromise = this.workspacesService.saveWorkspace(workspace, path);
|
||||
} else {
|
||||
savePromise = TPromise.as(workspace);
|
||||
}
|
||||
|
||||
return savePromise.then(workspace => {
|
||||
window.focus();
|
||||
|
||||
// Register window for backups and migrate current backups over
|
||||
let backupPath: string;
|
||||
if (!window.config.extensionDevelopmentPath) {
|
||||
backupPath = this.backupService.registerWorkspaceBackupSync(workspace, window.config.backupPath);
|
||||
}
|
||||
|
||||
// Update window configuration properly based on transition to workspace
|
||||
window.config.folderPath = void 0;
|
||||
window.config.workspace = workspace;
|
||||
window.config.backupPath = backupPath;
|
||||
|
||||
return { workspace, backupPath };
|
||||
});
|
||||
}
|
||||
|
||||
public pickWorkspaceAndOpen(options: INativeOpenDialogOptions): void {
|
||||
const window = this.windowsMainService.getWindowById(options.windowId) || this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();
|
||||
|
||||
this.windowsMainService.pickFileAndOpen({
|
||||
windowId: window ? window.id : void 0,
|
||||
dialogOptions: {
|
||||
buttonLabel: mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open")),
|
||||
title: localize('openWorkspaceTitle', "Open Workspace"),
|
||||
filters: WORKSPACE_FILTER,
|
||||
properties: ['openFile'],
|
||||
defaultPath: options.dialogOptions && options.dialogOptions.defaultPath
|
||||
},
|
||||
forceNewWindow: options.forceNewWindow,
|
||||
telemetryEventName: options.telemetryEventName,
|
||||
telemetryExtraData: options.telemetryExtraData
|
||||
});
|
||||
}
|
||||
|
||||
public promptToSaveUntitledWorkspace(e: IWindowUnloadEvent, workspace: IWorkspaceIdentifier): void {
|
||||
enum ConfirmResult {
|
||||
SAVE,
|
||||
DONT_SAVE,
|
||||
CANCEL
|
||||
}
|
||||
|
||||
const save = { label: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), result: ConfirmResult.SAVE };
|
||||
const dontSave = { label: mnemonicButtonLabel(localize({ key: 'doNotSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save")), result: ConfirmResult.DONT_SAVE };
|
||||
const cancel = { label: localize('cancel', "Cancel"), result: ConfirmResult.CANCEL };
|
||||
|
||||
const buttons: { label: string; result: ConfirmResult; }[] = [];
|
||||
if (isWindows) {
|
||||
buttons.push(save, dontSave, cancel);
|
||||
} else if (isLinux) {
|
||||
buttons.push(dontSave, cancel, save);
|
||||
} else {
|
||||
buttons.push(save, cancel, dontSave);
|
||||
}
|
||||
|
||||
const options: Electron.MessageBoxOptions = {
|
||||
title: this.environmentService.appNameLong,
|
||||
message: localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"),
|
||||
detail: localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."),
|
||||
noLink: true,
|
||||
type: 'warning',
|
||||
buttons: buttons.map(button => button.label),
|
||||
cancelId: buttons.indexOf(cancel)
|
||||
};
|
||||
|
||||
if (isLinux) {
|
||||
options.defaultId = 2;
|
||||
}
|
||||
|
||||
const res = dialog.showMessageBox(e.window.win, options);
|
||||
|
||||
switch (buttons[res].result) {
|
||||
|
||||
// Cancel: veto unload
|
||||
case ConfirmResult.CANCEL:
|
||||
e.veto(true);
|
||||
break;
|
||||
|
||||
// Don't Save: delete workspace
|
||||
case ConfirmResult.DONT_SAVE:
|
||||
this.workspacesService.deleteUntitledWorkspaceSync(workspace);
|
||||
e.veto(false);
|
||||
break;
|
||||
|
||||
// Save: save workspace, but do not veto unload
|
||||
case ConfirmResult.SAVE: {
|
||||
let target = dialog.showSaveDialog(e.window.win, {
|
||||
buttonLabel: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")),
|
||||
title: localize('saveWorkspace', "Save Workspace"),
|
||||
filters: WORKSPACE_FILTER,
|
||||
defaultPath: this.getUntitledWorkspaceSaveDialogDefaultPath(workspace)
|
||||
});
|
||||
|
||||
if (target) {
|
||||
if (isMacintosh) {
|
||||
target = normalizeNFC(target); // normalize paths returned from the OS
|
||||
}
|
||||
|
||||
e.veto(this.workspacesService.saveWorkspace(workspace, target).then(() => false, () => false));
|
||||
} else {
|
||||
e.veto(true); // keep veto if no target was provided
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getUntitledWorkspaceSaveDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string {
|
||||
if (workspace) {
|
||||
if (isSingleFolderWorkspaceIdentifier(workspace)) {
|
||||
return dirname(workspace);
|
||||
}
|
||||
|
||||
const resolvedWorkspace = this.workspacesService.resolveWorkspaceSync(workspace.configPath);
|
||||
if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) {
|
||||
for (const folder of resolvedWorkspace.folders) {
|
||||
if (folder.uri.scheme === Schemas.file) {
|
||||
return dirname(folder.uri.fsPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user