Merge VS Code 1.21 source code (#1067)

* Initial VS Code 1.21 file copy with patches

* A few more merges

* Post npm install

* Fix batch of build breaks

* Fix more build breaks

* Fix more build errors

* Fix more build breaks

* Runtime fixes 1

* Get connection dialog working with some todos

* Fix a few packaging issues

* Copy several node_modules to package build to fix loader issues

* Fix breaks from master

* A few more fixes

* Make tests pass

* First pass of license header updates

* Second pass of license header updates

* Fix restore dialog issues

* Remove add additional themes menu items

* fix select box issues where the list doesn't show up

* formatting

* Fix editor dispose issue

* Copy over node modules to correct location on all platforms
This commit is contained in:
Karl Burtram
2018-04-04 15:27:51 -07:00
committed by GitHub
parent 5fba3e31b4
commit dafb780987
9412 changed files with 141255 additions and 98813 deletions

View File

@@ -19,13 +19,13 @@ 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, IPathsToWaitFor, IEnterWorkspaceResult } from 'vs/platform/windows/common/windows';
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, ReadyState, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult } 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';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { isEqual } from 'vs/base/common/paths';
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows';
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow } 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';
@@ -35,6 +35,7 @@ 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';
import { Queue } from 'vs/base/common/async';
enum WindowError {
UNRESPONSIVE,
@@ -45,10 +46,6 @@ interface INewWindowState extends ISingleWindowState {
hasDefaultState?: boolean;
}
interface ILegacyWindowState extends IWindowState {
workspacePath?: string;
}
interface IWindowState {
workspace?: IWorkspaceIdentifier;
folderPath?: string;
@@ -56,10 +53,6 @@ interface IWindowState {
uiState: ISingleWindowState;
}
interface ILegacyWindowsState extends IWindowsState {
openedFolders?: IWindowState[];
}
interface IWindowsState {
lastActiveWindow?: IWindowState;
lastPluginDevelopmentHostWindow?: IWindowState;
@@ -116,7 +109,7 @@ export class WindowsManager implements IWindowsMainService {
private windowsState: IWindowsState;
private lastClosedWindowState: IWindowState;
private fileDialog: FileDialog;
private dialogs: Dialogs;
private workspacesManager: WorkspacesManager;
private _onWindowReady = new Emitter<CodeWindow>();
@@ -151,39 +144,12 @@ export class WindowsManager implements IWindowsMainService {
@IInstantiationService private instantiationService: IInstantiationService
) {
this.windowsState = this.stateService.getItem<IWindowsState>(WindowsManager.windowsStateStorageKey) || { openedWindows: [] };
this.fileDialog = new FileDialog(environmentService, telemetryService, stateService, this);
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, this);
this.migrateLegacyWindowState();
}
private migrateLegacyWindowState(): void {
const state: ILegacyWindowsState = this.windowsState;
// TODO@Ben migration from previous openedFolders to new openedWindows property
if (Array.isArray(state.openedFolders) && state.openedFolders.length > 0) {
state.openedWindows = state.openedFolders;
state.openedFolders = void 0;
} else if (!state.openedWindows) {
state.openedWindows = [];
if (!Array.isArray(this.windowsState.openedWindows)) {
this.windowsState.openedWindows = [];
}
// TODO@Ben migration from previous workspacePath in window state to folderPath
const states: ILegacyWindowState[] = [];
states.push(state.lastActiveWindow);
states.push(state.lastPluginDevelopmentHostWindow);
states.push(...state.openedWindows);
states.forEach(state => {
if (!state) {
return;
}
if (typeof state.workspacePath === 'string') {
state.folderPath = state.workspacePath;
state.workspacePath = void 0;
}
});
this.dialogs = new Dialogs(environmentService, telemetryService, stateService, this);
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, this);
}
public ready(initialUserEnv: IProcessEnvironment): void {
@@ -277,7 +243,7 @@ export class WindowsManager implements IWindowsMainService {
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeQuit(0)
//
private onBeforeQuit(): void {
const currentWindowsState: ILegacyWindowsState = {
const currentWindowsState: IWindowsState = {
openedWindows: [],
lastPluginDevelopmentHostWindow: this.windowsState.lastPluginDevelopmentHostWindow,
lastActiveWindow: this.lastClosedWindowState
@@ -403,7 +369,7 @@ export class WindowsManager implements IWindowsMainService {
let foldersToRestore: string[] = [];
let workspacesToRestore: IWorkspaceIdentifier[] = [];
let emptyToRestore: string[] = [];
if (openConfig.initialStartup && !openConfig.cli.extensionDevelopmentPath) {
if (openConfig.initialStartup && !openConfig.cli.extensionDevelopmentPath && !openConfig.cli['disable-restore-windows']) {
foldersToRestore = this.backupMainService.getFolderBackupPaths();
workspacesToRestore = this.backupMainService.getWorkspaceBackups(); // collect from workspaces with hot-exit backups
@@ -825,12 +791,7 @@ export class WindowsManager implements IWindowsMainService {
noLink: true
};
const activeWindow = BrowserWindow.getFocusedWindow();
if (activeWindow) {
dialog.showMessageBox(activeWindow, options);
} else {
dialog.showMessageBox(options);
}
this.dialogs.showMessageBox(options, this.getFocusedWindow());
}
return path;
@@ -940,10 +901,6 @@ export class WindowsManager implements IWindowsMainService {
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
restoreWindows = ((windowConfig && windowConfig.restoreWindows) || 'one') as RestoreWindowsSetting;
if (restoreWindows === 'one' /* default */ && windowConfig && windowConfig.reopenFolders) {
restoreWindows = windowConfig.reopenFolders; // TODO@Ben migration from deprecated window.reopenFolders setting
}
if (['all', 'folders', 'one', 'none'].indexOf(restoreWindows) === -1) {
restoreWindows = 'one';
}
@@ -987,10 +944,14 @@ export class WindowsManager implements IWindowsMainService {
};
}
// Folder
return {
folderPath: candidate
};
// Folder (we check for isDirectory() because e.g. paths like /dev/null
// are neither file nor folder but some external tools might pass them
// over to us)
else if (candidateStat.isDirectory()) {
return {
folderPath: candidate
};
}
}
} catch (error) {
this.historyMainService.removeFromRecentlyOpened([candidate]); // since file does not seem to exist anymore, remove from recent
@@ -1361,7 +1322,10 @@ export class WindowsManager implements IWindowsMainService {
}
if (e.window.config && !!e.window.config.extensionDevelopmentPath) {
return; // do not ask to save workspace when doing extension development
// do not ask to save workspace when doing extension development
// but still delete it.
this.workspacesMainService.deleteUntitledWorkspaceSync(workspace);
return;
}
if (windowClosing && !isMacintosh && this.getWindowCount() === 1) {
@@ -1369,7 +1333,17 @@ export class WindowsManager implements IWindowsMainService {
}
// Handle untitled workspaces with prompt as needed
this.workspacesManager.promptToSaveUntitledWorkspace(e, workspace);
e.veto(this.workspacesManager.promptToSaveUntitledWorkspace(this.getWindowById(e.window.id), workspace).then(veto => {
if (veto) {
return veto;
}
// Bug in electron: somehow we need this timeout so that the window closes properly. That
// might be related to the fact that the untitled workspace prompt shows up async and this
// code can execute before the dialog is fully closed which then blocks the window from closing.
// Issue: https://github.com/Microsoft/vscode/issues/41989
return TPromise.timeout(0).then(() => veto);
}));
}
public focusLastActive(cli: ParsedArgs, context: OpenContext): CodeWindow {
@@ -1457,48 +1431,48 @@ export class WindowsManager implements IWindowsMainService {
// Unresponsive
if (error === WindowError.UNRESPONSIVE) {
const result = dialog.showMessageBox(window.win, {
this.dialogs.showMessageBox({
title: product.nameLong,
type: 'warning',
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
}, window).then(result => {
if (!window.win) {
return; // Return early if the window has been going down already
}
if (result.button === 0) {
window.reload();
} else if (result.button === 2) {
this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually
window.win.destroy(); // make sure to destroy the window as it is unresponsive
}
});
if (!window.win) {
return; // Return early if the window has been going down already
}
if (result === 0) {
window.reload();
} else if (result === 2) {
this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually
window.win.destroy(); // make sure to destroy the window as it is unresponsive
}
}
// Crashed
else {
const result = dialog.showMessageBox(window.win, {
this.dialogs.showMessageBox({
title: product.nameLong,
type: 'warning',
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
}, window).then(result => {
if (!window.win) {
return; // Return early if the window has been going down already
}
if (result.button === 0) {
window.reload();
} else if (result.button === 1) {
this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually
window.win.destroy(); // make sure to destroy the window as it has crashed
}
});
if (!window.win) {
return; // Return early if the window has been going down already
}
if (result === 0) {
window.reload();
} else if (result === 1) {
this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually
window.win.destroy(); // make sure to destroy the window as it has crashed
}
}
}
@@ -1558,7 +1532,19 @@ export class WindowsManager implements IWindowsMainService {
}
}
this.fileDialog.pickAndOpen(internalOptions);
this.dialogs.pickAndOpen(internalOptions);
}
public showMessageBox(options: Electron.MessageBoxOptions, win?: CodeWindow): TPromise<IMessageBoxResult> {
return this.dialogs.showMessageBox(options, win);
}
public showSaveDialog(options: Electron.SaveDialogOptions, win?: CodeWindow): TPromise<string> {
return this.dialogs.showSaveDialog(options, win);
}
public showOpenDialog(options: Electron.OpenDialogOptions, win?: CodeWindow): TPromise<string[]> {
return this.dialogs.showOpenDialog(options, win);
}
public quit(): void {
@@ -1584,20 +1570,25 @@ interface IInternalNativeOpenDialogOptions extends INativeOpenDialogOptions {
pickFiles?: boolean;
}
class FileDialog {
class Dialogs {
private static readonly workingDirPickerStorageKey = 'pickerWorkingDir';
private mapWindowToDialogQueue: Map<number, Queue<any>>;
private noWindowDialogQueue: Queue<any>;
constructor(
private environmentService: IEnvironmentService,
private telemetryService: ITelemetryService,
private stateService: IStateService,
private windowsMainService: IWindowsMainService
private windowsMainService: IWindowsMainService,
) {
this.mapWindowToDialogQueue = new Map<number, Queue<any>>();
this.noWindowDialogQueue = new Queue<any>();
}
public pickAndOpen(options: INativeOpenDialogOptions): void {
this.getFileOrFolderPaths(options, (paths: string[]) => {
this.getFileOrFolderPaths(options).then(paths => {
const numberOfPaths = paths ? paths.length : 0;
// Telemetry
@@ -1623,7 +1614,7 @@ class FileDialog {
});
}
private getFileOrFolderPaths(options: IInternalNativeOpenDialogOptions, clb: (paths: string[]) => void): void {
private getFileOrFolderPaths(options: IInternalNativeOpenDialogOptions): TPromise<string[]> {
// Ensure dialog options
if (!options.dialogOptions) {
@@ -1632,7 +1623,7 @@ class FileDialog {
// Ensure defaultPath
if (!options.dialogOptions.defaultPath) {
options.dialogOptions.defaultPath = this.stateService.getItem<string>(FileDialog.workingDirPickerStorageKey);
options.dialogOptions.defaultPath = this.stateService.getItem<string>(Dialogs.workingDirPickerStorageKey);
}
// Ensure properties
@@ -1654,28 +1645,86 @@ class FileDialog {
// Show Dialog
const focusedWindow = this.windowsMainService.getWindowById(options.windowId) || this.windowsMainService.getFocusedWindow();
let paths = dialog.showOpenDialog(focusedWindow && focusedWindow.win, options.dialogOptions);
if (paths && paths.length > 0) {
if (isMacintosh) {
return this.showOpenDialog(options.dialogOptions, focusedWindow).then(paths => {
if (paths && paths.length > 0) {
// Remember path in storage for next time
this.stateService.setItem(Dialogs.workingDirPickerStorageKey, dirname(paths[0]));
return paths;
}
return void 0;
});
}
private getDialogQueue(window?: ICodeWindow): Queue<any> {
if (!window) {
return this.noWindowDialogQueue;
}
let windowDialogQueue = this.mapWindowToDialogQueue.get(window.id);
if (!windowDialogQueue) {
windowDialogQueue = new Queue<any>();
this.mapWindowToDialogQueue.set(window.id, windowDialogQueue);
}
return windowDialogQueue;
}
public showMessageBox(options: Electron.MessageBoxOptions, window?: ICodeWindow): TPromise<IMessageBoxResult> {
return this.getDialogQueue(window).queue(() => {
return new TPromise((c, e) => {
dialog.showMessageBox(window ? window.win : void 0, options, (response: number, checkboxChecked: boolean) => {
c({ button: response, checkboxChecked });
});
});
});
}
public showSaveDialog(options: Electron.SaveDialogOptions, window?: ICodeWindow): TPromise<string> {
function normalizePath(path: string): string {
if (path && isMacintosh) {
path = normalizeNFC(path); // normalize paths returned from the OS
}
return path;
}
return this.getDialogQueue(window).queue(() => {
return new TPromise((c, e) => {
dialog.showSaveDialog(window ? window.win : void 0, options, path => {
c(normalizePath(path));
});
});
});
}
public showOpenDialog(options: Electron.OpenDialogOptions, window?: ICodeWindow): TPromise<string[]> {
function normalizePaths(paths: string[]): string[] {
if (paths && paths.length > 0 && isMacintosh) {
paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS
}
// Remember path in storage for next time
this.stateService.setItem(FileDialog.workingDirPickerStorageKey, dirname(paths[0]));
// Return
return clb(paths);
return paths;
}
return clb(void (0));
return this.getDialogQueue(window).queue(() => {
return new TPromise((c, e) => {
dialog.showOpenDialog(window ? window.win : void 0, options, paths => {
c(normalizePaths(paths));
});
});
});
}
}
class WorkspacesManager {
constructor(
private workspacesService: IWorkspacesMainService,
private backupService: IBackupMainService,
private workspacesMainService: IWorkspacesMainService,
private backupMainService: IBackupMainService,
private environmentService: IEnvironmentService,
private windowsMainService: IWindowsMainService
) {
@@ -1690,26 +1739,33 @@ class WorkspacesManager {
}
public createAndEnterWorkspace(window: CodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise<IEnterWorkspaceResult> {
if (!window || !window.win || window.readyState !== ReadyState.READY || !this.isValidTargetWorkspacePath(window, path)) {
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);
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: CodeWindow, path?: string): boolean {
private isValidTargetWorkspacePath(window: CodeWindow, path?: string): TPromise<boolean> {
if (!path) {
return true;
return TPromise.wrap(true);
}
if (window.openedWorkspace && window.openedWorkspace.configPath === path) {
return false; // window is already opened on a workspace with that path
return TPromise.wrap(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 })) {
if (findWindowOnWorkspace(this.windowsMainService.getWindows(), { id: this.workspacesMainService.getWorkspaceId(path), configPath: path })) {
const options: Electron.MessageBoxOptions = {
title: product.nameLong,
type: 'info',
@@ -1719,23 +1775,16 @@ class WorkspacesManager {
noLink: true
};
const activeWindow = BrowserWindow.getFocusedWindow();
if (activeWindow) {
dialog.showMessageBox(activeWindow, options);
} else {
dialog.showMessageBox(options);
}
return false;
return this.windowsMainService.showMessageBox(options, this.windowsMainService.getFocusedWindow()).then(() => false);
}
return true; // OK
return TPromise.wrap(true); // OK
}
private doSaveAndOpenWorkspace(window: CodeWindow, workspace: IWorkspaceIdentifier, path?: string): TPromise<IEnterWorkspaceResult> {
let savePromise: TPromise<IWorkspaceIdentifier>;
if (path) {
savePromise = this.workspacesService.saveWorkspace(workspace, path);
savePromise = this.workspacesMainService.saveWorkspace(workspace, path);
} else {
savePromise = TPromise.as(workspace);
}
@@ -1746,7 +1795,7 @@ class WorkspacesManager {
// Register window for backups and migrate current backups over
let backupPath: string;
if (!window.config.extensionDevelopmentPath) {
backupPath = this.backupService.registerWorkspaceBackupSync(workspace, window.config.backupPath);
backupPath = this.backupMainService.registerWorkspaceBackupSync(workspace, window.config.backupPath);
}
// Update window configuration properly based on transition to workspace
@@ -1776,7 +1825,7 @@ class WorkspacesManager {
});
}
public promptToSaveUntitledWorkspace(e: IWindowUnloadEvent, workspace: IWorkspaceIdentifier): void {
public promptToSaveUntitledWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): TPromise<boolean> {
enum ConfirmResult {
SAVE,
DONT_SAVE,
@@ -1810,41 +1859,35 @@ class WorkspacesManager {
options.defaultId = 2;
}
const res = dialog.showMessageBox(e.window.win, options);
return this.windowsMainService.showMessageBox(options, window).then(res => {
switch (buttons[res.button].result) {
switch (buttons[res].result) {
// Cancel: veto unload
case ConfirmResult.CANCEL:
return true;
// Cancel: veto unload
case ConfirmResult.CANCEL:
e.veto(true);
break;
// Don't Save: delete workspace
case ConfirmResult.DONT_SAVE:
this.workspacesMainService.deleteUntitledWorkspaceSync(workspace);
return false;
// 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: {
return this.windowsMainService.showSaveDialog({
buttonLabel: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")),
title: localize('saveWorkspace', "Save Workspace"),
filters: WORKSPACE_FILTER,
defaultPath: this.getUntitledWorkspaceSaveDialogDefaultPath(workspace)
}, window).then(target => {
if (target) {
return this.workspacesMainService.saveWorkspace(workspace, target).then(() => false, () => false);
}
// 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
return true; // keep veto if no target was provided
});
}
}
}
});
}
private getUntitledWorkspaceSaveDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string {
@@ -1853,7 +1896,7 @@ class WorkspacesManager {
return dirname(workspace);
}
const resolvedWorkspace = this.workspacesService.resolveWorkspaceSync(workspace.configPath);
const resolvedWorkspace = this.workspacesMainService.resolveWorkspaceSync(workspace.configPath);
if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) {
for (const folder of resolvedWorkspace.folders) {
if (folder.uri.scheme === Schemas.file) {