Merge VS Code 1.23.1 (#1520)

This commit is contained in:
Matt Irvine
2018-06-05 11:24:51 -07:00
committed by GitHub
parent e3baf5c443
commit 0c58f09e59
3651 changed files with 74249 additions and 48599 deletions

View File

@@ -5,10 +5,10 @@
'use strict';
import { app, ipcMain as ipc, BrowserWindow } from 'electron';
import { app, ipcMain as ipc } from 'electron';
import * as platform from 'vs/base/common/platform';
import { WindowsManager } from 'vs/code/electron-main/windows';
import { IWindowsService, OpenContext } from 'vs/platform/windows/common/windows';
import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows';
import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc';
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
@@ -29,7 +29,7 @@ import { IStateService } from 'vs/platform/state/common/state';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IURLService } from 'vs/platform/url/common/url';
import { URLChannel } from 'vs/platform/url/common/urlIpc';
import { URLHandlerChannelClient, URLServiceChannel } from 'vs/platform/url/common/urlIpc';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
@@ -59,6 +59,8 @@ import { IssueChannel } from 'vs/platform/issue/common/issueIpc';
import { IssueService } from 'vs/platform/issue/electron-main/issueService';
import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc';
import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener';
import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
export class CodeApplication {
@@ -126,7 +128,7 @@ export class CodeApplication {
return srcUri.startsWith(URI.file(this.environmentService.appRoot.toLowerCase()).toString());
};
app.on('web-contents-created', (_event: any, contents) => {
app.on('web-contents-created', (event: any, contents) => {
contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => {
delete webPreferences.preload;
webPreferences.nodeIntegration = false;
@@ -181,15 +183,15 @@ export class CodeApplication {
this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button
});
ipc.on('vscode:exit', (_event: any, code: number) => {
ipc.on('vscode:exit', (event: any, code: number) => {
this.logService.trace('IPC#vscode:exit', code);
this.dispose();
this.lifecycleService.kill(code);
});
ipc.on('vscode:fetchShellEnv', (_event: any, windowId: number) => {
const { webContents } = BrowserWindow.fromId(windowId);
ipc.on('vscode:fetchShellEnv', event => {
const webContents = event.sender.webContents;
getShellEnvironment().then(shellEnv => {
if (!webContents.isDestroyed()) {
webContents.send('vscode:acceptShellEnv', shellEnv);
@@ -203,7 +205,7 @@ export class CodeApplication {
});
});
ipc.on('vscode:broadcast', (_event: any, windowId: number, broadcast: { channel: string; payload: any; }) => {
ipc.on('vscode:broadcast', (event: any, windowId: number, broadcast: { channel: string; payload: any; }) => {
if (this.windowsMainService && broadcast.channel && !isUndefinedOrNull(broadcast.payload)) {
this.logService.trace('IPC#vscode:broadcast', broadcast.channel, broadcast.payload);
@@ -277,22 +279,34 @@ export class CodeApplication {
this.logService.trace(`Resolved machine identifier: ${machineId}`);
// Spawn shared process
this.sharedProcess = new SharedProcess(this.environmentService, machineId, this.userEnv, this.logService);
this.toDispose.push(this.sharedProcess);
this.sharedProcess = new SharedProcess(this.environmentService, this.lifecycleService, this.logService, machineId, this.userEnv);
this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
// Services
const appInstantiationService = this.initServices(machineId);
// Setup Auth Handler
const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
this.toDispose.push(authHandler);
let promise: TPromise<any> = TPromise.as(null);
// Open Windows
appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor));
// Create driver
if (this.environmentService.driverHandle) {
serveDriver(this.electronIpcServer, this.environmentService.driverHandle, appInstantiationService).then(server => {
this.logService.info('Driver started at:', this.environmentService.driverHandle);
this.toDispose.push(server);
});
}
// Post Open Windows Tasks
appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
return promise.then(() => {
// Setup Auth Handler
const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
this.toDispose.push(authHandler);
// Open Windows
appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor));
// Post Open Windows Tasks
appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
});
});
}
@@ -325,7 +339,7 @@ export class CodeApplication {
services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, machineId));
services.set(IWindowsService, new SyncDescriptor(WindowsService, this.sharedProcess));
services.set(ILaunchService, new SyncDescriptor(LaunchService));
services.set(IIssueService, new SyncDescriptor(IssueService, machineId));
services.set(IIssueService, new SyncDescriptor(IssueService, machineId, this.userEnv));
// Telemtry
if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
@@ -346,16 +360,6 @@ export class CodeApplication {
private openFirstWindow(accessor: ServicesAccessor): void {
const appInstantiationService = accessor.get(IInstantiationService);
// TODO@Joao: unfold this
this.windowsMainService = accessor.get(IWindowsMainService);
// TODO@Joao: so ugly...
this.windowsMainService.onWindowsCountChanged(e => {
if (!platform.isMacintosh && e.newCount === 0) {
this.sharedProcess.dispose();
}
});
// Register more Main IPC services
const launchService = accessor.get(ILaunchService);
const launchChannel = new LaunchChannel(launchService);
@@ -366,10 +370,6 @@ export class CodeApplication {
const updateChannel = new UpdateChannel(updateService);
this.electronIpcServer.registerChannel('update', updateChannel);
const urlService = accessor.get(IURLService);
const urlChannel = appInstantiationService.createInstance(URLChannel, urlService);
this.electronIpcServer.registerChannel('url', urlChannel);
const issueService = accessor.get(IIssueService);
const issueChannel = new IssueChannel(issueService);
this.electronIpcServer.registerChannel('issue', issueChannel);
@@ -383,6 +383,10 @@ export class CodeApplication {
this.electronIpcServer.registerChannel('windows', windowsChannel);
this.sharedProcessClient.done(client => client.registerChannel('windows', windowsChannel));
const urlService = accessor.get(IURLService);
const urlChannel = new URLServiceChannel(urlService);
this.electronIpcServer.registerChannel('url', urlChannel);
// Log level management
const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
this.electronIpcServer.registerChannel('loglevel', logLevelChannel);
@@ -392,15 +396,51 @@ export class CodeApplication {
this.lifecycleService.ready();
// Propagate to clients
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this
const args = this.environmentService.args;
// Create a URL handler which forwards to the last active window
const activeWindowManager = new ActiveWindowManager(windowsService);
const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', { route: () => activeWindowManager.activeClientId });
const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);
// On Mac, Code can be running without any open windows, so we must create a window to handle urls,
// if there is none
if (platform.isMacintosh) {
const environmentService = accessor.get(IEnvironmentService);
urlService.registerHandler({
async handleURL(uri: URI): TPromise<boolean> {
if (windowsMainService.getWindowCount() === 0) {
const cli = { ...environmentService.args, goto: true };
const [window] = windowsMainService.open({ context: OpenContext.API, cli, forceEmpty: true });
return window.ready().then(() => urlService.open(uri));
}
return false;
}
});
}
// Register the multiple URL handker
urlService.registerHandler(multiplexURLHandler);
// Watch Electron URLs and forward them to the UrlService
const urls = args['open-url'] ? args._urls : [];
const urlListener = new ElectronURLListener(urls, urlService, this.windowsMainService);
this.toDispose.push(urlListener);
this.windowsMainService.ready(this.userEnv);
// Open our first window
const args = this.environmentService.args;
const macOpenFiles = (<any>global).macOpenFiles as string[];
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
if (args['new-window'] && args._.length === 0) {
this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths
} else if (global.macOpenFiles && global.macOpenFiles.length && (!args._ || !args._.length)) {
this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup
} else if (macOpenFiles && macOpenFiles.length && (!args._ || !args._.length)) {
this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, pathsToOpen: macOpenFiles, initialStartup: true }); // mac: open-file event received on startup
} else {
this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!args._.length && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli
}

View File

@@ -29,6 +29,7 @@ export interface SystemInfo {
VM: string;
'Screen Reader': string;
'Process Argv': string;
'GPU Status': Electron.GPUFeatureStatus;
}
export interface ProcessInfo {
@@ -92,7 +93,8 @@ export function getSystemInfo(info: IMainProcessInfo): SystemInfo {
'Memory (System)': `${(os.totalmem() / GB).toFixed(2)}GB (${(os.freemem() / GB).toFixed(2)}GB free)`,
VM: `${Math.round((virtualMachineHint.value() * 100))}%`,
'Screen Reader': `${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`,
'Process Argv': `${info.mainArguments.join(' ')}`
'Process Argv': `${info.mainArguments.join(' ')}`,
'GPU Status': app.getGPUFeatureStatus()
};
const cpus = os.cpus();
@@ -208,7 +210,14 @@ function formatLaunchConfigs(configs: WorkspaceStatItem[]): string {
return output.join('\n');
}
function formatEnvironment(info: IMainProcessInfo): string {
function expandGPUFeatures(): string {
const gpuFeatures = app.getGPUFeatureStatus();
const longestFeatureName = Math.max(...Object.keys(gpuFeatures).map(feature => feature.length));
// Make columns aligned by adding spaces after feature name
return Object.keys(gpuFeatures).map(feature => `${feature}: ${repeat(' ', longestFeatureName - feature.length)} ${gpuFeatures[feature]}`).join('\n ');
}
export function formatEnvironment(info: IMainProcessInfo): string {
const MB = 1024 * 1024;
const GB = 1024 * MB;
@@ -226,6 +235,7 @@ function formatEnvironment(info: IMainProcessInfo): string {
output.push(`VM: ${Math.round((virtualMachineHint.value() * 100))}%`);
output.push(`Screen Reader: ${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`);
output.push(`Process Argv: ${info.mainArguments.join(' ')}`);
output.push(`GPU Status: ${expandGPUFeatures()}`);
return output.join('\n');
}

View File

@@ -8,7 +8,7 @@
import * as nativeKeymap from 'native-keymap';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IStateService } from 'vs/platform/state/common/state';
import Event, { Emitter, once } from 'vs/base/common/event';
import { Event, Emitter, once } from 'vs/base/common/event';
import { ConfigWatcher } from 'vs/base/node/config';
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -20,7 +20,7 @@ export class KeyboardLayoutMonitor {
public static readonly INSTANCE = new KeyboardLayoutMonitor();
private _emitter: Emitter<void>;
private readonly _emitter: Emitter<void>;
private _registered: boolean;
private constructor() {

View File

@@ -9,14 +9,16 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { ILogService } from 'vs/platform/log/common/log';
import { IURLService } from 'vs/platform/url/common/url';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform';
import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { OpenContext } from 'vs/platform/windows/common/windows';
import { OpenContext, IWindowSettings } from 'vs/platform/windows/common/windows';
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { whenDeleted } from 'vs/base/node/pfs';
import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
import { Schemas } from 'vs/base/common/network';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import URI from 'vs/base/common/uri';
export const ID = 'launchService';
export const ILaunchService = createDecorator<ILaunchService>(ID);
@@ -38,6 +40,24 @@ export interface IMainProcessInfo {
windows: IWindowInfo[];
}
function parseOpenUrl(args: ParsedArgs): URI[] {
if (args['open-url'] && args._urls && args._urls.length > 0) {
// --open-url must contain -- followed by the url(s)
// process.argv is used over args._ as args._ are resolved to file paths at this point
return args._urls
.map(url => {
try {
return URI.parse(url);
} catch (err) {
return null;
}
})
.filter(uri => !!uri);
}
return [];
}
export interface ILaunchService {
_serviceBrand: any;
start(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise<void>;
@@ -110,14 +130,32 @@ export class LaunchService implements ILaunchService {
@IWindowsMainService private windowsMainService: IWindowsMainService,
@IURLService private urlService: IURLService,
@IWorkspacesMainService private workspacesMainService: IWorkspacesMainService,
@IEnvironmentService private readonly environmentService: IEnvironmentService
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IConfigurationService private readonly configurationService: IConfigurationService
) { }
public start(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise<void> {
this.logService.trace('Received data from other instance: ', args, userEnv);
const urlsToOpen = parseOpenUrl(args);
// Check early for open-url which is handled in URL service
if (this.shouldOpenUrl(args)) {
if (urlsToOpen.length) {
let whenWindowReady = TPromise.as<any>(null);
// Create a window if there is none
if (this.windowsMainService.getWindowCount() === 0) {
const window = this.windowsMainService.openNewWindow(OpenContext.DESKTOP)[0];
whenWindowReady = window.ready();
}
// Make sure a window is open, ready to receive the url event
whenWindowReady.then(() => {
for (const url of urlsToOpen) {
this.urlService.open(url);
}
});
return TPromise.as(null);
}
@@ -125,28 +163,54 @@ export class LaunchService implements ILaunchService {
return this.startOpenWindow(args, userEnv);
}
private shouldOpenUrl(args: ParsedArgs): boolean {
if (args['open-url'] && args._urls && args._urls.length > 0) {
// --open-url must contain -- followed by the url(s)
// process.argv is used over args._ as args._ are resolved to file paths at this point
args._urls.forEach(url => this.urlService.open(url));
return true;
}
return false;
}
private startOpenWindow(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise<void> {
const context = !!userEnv['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
let usedWindows: ICodeWindow[];
// Special case extension development
if (!!args.extensionDevelopmentPath) {
this.windowsMainService.openExtensionDevelopmentHostWindow({ context, cli: args, userEnv });
} else if (args._.length === 0 && (args['new-window'] || args['unity-launch'])) {
usedWindows = this.windowsMainService.open({ context, cli: args, userEnv, forceNewWindow: true, forceEmpty: true });
} else if (args._.length === 0) {
usedWindows = [this.windowsMainService.focusLastActive(args, context)];
} else {
}
// Start without file/folder arguments
else if (args._.length === 0) {
let openNewWindow = false;
// Force new window
if (args['new-window'] || args['unity-launch']) {
openNewWindow = true;
}
// Force reuse window
else if (args['reuse-window']) {
openNewWindow = false;
}
// Otherwise check for settings
else {
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
const openWithoutArgumentsInNewWindowConfig = (windowConfig && windowConfig.openWithoutArgumentsInNewWindow) || 'default' /* default */;
switch (openWithoutArgumentsInNewWindowConfig) {
case 'on':
openNewWindow = true;
break;
case 'off':
openNewWindow = false;
break;
default:
openNewWindow = !isMacintosh; // prefer to restore running instance on macOS
}
}
if (openNewWindow) {
usedWindows = this.windowsMainService.open({ context, cli: args, userEnv, forceNewWindow: true, forceEmpty: true });
} else {
usedWindows = [this.windowsMainService.focusLastActive(args, context)];
}
}
// Start with file/folder arguments
else {
usedWindows = this.windowsMainService.open({
context,
cli: args,

View File

@@ -34,7 +34,7 @@ import { ConfigurationService } from 'vs/platform/configuration/node/configurati
import { IRequestService } from 'vs/platform/request/node/request';
import { RequestService } from 'vs/platform/request/electron-main/requestService';
import { IURLService } from 'vs/platform/url/common/url';
import { URLService } from 'vs/platform/url/electron-main/urlService';
import { URLService } from 'vs/platform/url/common/urlService';
import * as fs from 'original-fs';
import { CodeApplication } from 'vs/code/electron-main/app';
import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService';
@@ -48,8 +48,8 @@ import { printDiagnostics } from 'vs/code/electron-main/diagnostics';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
import { uploadLogs } from 'vs/code/electron-main/logUploader';
import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
import { ChoiceCliService } from 'vs/platform/dialogs/node/choiceCli';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { CommandLineDialogService } from 'vs/platform/dialogs/node/dialogService';
function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService {
const services = new ServiceCollection();
@@ -71,9 +71,9 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I
services.set(IStateService, new SyncDescriptor(StateService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IURLService, new SyncDescriptor(URLService, args['open-url'] ? args._urls : []));
services.set(IURLService, new SyncDescriptor(URLService));
services.set(IBackupMainService, new SyncDescriptor(BackupMainService));
services.set(IChoiceService, new SyncDescriptor(ChoiceCliService));
services.set(IDialogService, new SyncDescriptor(CommandLineDialogService));
return new InstantiationService(services, true);
}

View File

@@ -100,7 +100,7 @@ export class CodeMenu {
this.windowsMainService.onWindowClose(() => this.updateWorkspaceMenuItems());
// Listen to extension viewlets
ipc.on('vscode:extensionViewlets', (_event: any, rawExtensionViewlets: string) => {
ipc.on('vscode:extensionViewlets', (event: any, rawExtensionViewlets: string) => {
let extensionViewlets: IExtensionViewlet[] = [];
try {
extensionViewlets = JSON.parse(rawExtensionViewlets);
@@ -465,7 +465,7 @@ export class CodeMenu {
}
private getPreferencesMenu(): Electron.MenuItem {
const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openGlobalSettings');
const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openSettings');
const kebindingSettings = this.createMenuItem(nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts"), 'workbench.action.openGlobalKeybindings');
// {{SQL CARBON EDIT}}
// const keymapExtensions = this.createMenuItem(nls.localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions"), 'workbench.extensions.action.showRecommendedKeymapExtensions');
@@ -892,6 +892,7 @@ export class CodeMenu {
breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Conditional Breakpoint..."), 'editor.debug.action.conditionalBreakpoint'));
breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miColumnBreakpoint', comment: ['&& denotes a mnemonic'] }, "C&&olumn Breakpoint"), 'editor.debug.action.toggleColumnBreakpoint'));
breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint..."), 'workbench.debug.viewlet.action.addFunctionBreakpointAction'));
breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "&&Logpoint..."), 'editor.debug.action.toggleLogPoint'));
const newBreakpoints = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&New Breakpoint")), submenu: breakpointsMenu });
const enableAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Enable All Breakpoints"), 'workbench.debug.viewlet.action.enableAllBreakpoints');
const disableAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints"), 'workbench.debug.viewlet.action.disableAllBreakpoints');
@@ -980,6 +981,8 @@ export class CodeMenu {
}
}, false));
const openProcessExplorer = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer")), click: () => this.runActionInRenderer('workbench.action.openProcessExplorer') });
let reportIssuesItem: Electron.MenuItem = null;
if (product.reportIssueUrl) {
const label = nls.localize({ key: 'miReportIssue', comment: ['&& denotes a mnemonic', 'Translate this to "Report Issue in English" in all languages please!'] }, "Report &&Issue");
@@ -1042,7 +1045,8 @@ export class CodeMenu {
}) : null,
(product.licenseUrl || product.privacyStatementUrl) ? __separator__() : null,
toggleDevToolsItem,
isWindows && product.quality !== 'stable' ? showAccessibilityOptions : null
openProcessExplorer,
isWindows && product.quality !== 'stable' ? showAccessibilityOptions : null,
]).forEach(item => helpMenu.append(item));
if (!isMacintosh) {
@@ -1085,7 +1089,7 @@ export class CodeMenu {
}
private openAccessibilityOptions(): void {
let win = new BrowserWindow({
const win = new BrowserWindow({
alwaysOnTop: true,
skipTaskbar: true,
resizable: false,

View File

@@ -5,7 +5,6 @@
import { assign } from 'vs/base/common/objects';
import { memoize } from 'vs/base/common/decorators';
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { TPromise } from 'vs/base/common/winjs.base';
import { IProcessEnvironment } from 'vs/base/common/platform';
@@ -13,20 +12,22 @@ import { BrowserWindow, ipcMain } from 'electron';
import { ISharedProcess } from 'vs/platform/windows/electron-main/windows';
import { Barrier } from 'vs/base/common/async';
import { ILogService } from 'vs/platform/log/common/log';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
export class SharedProcess implements ISharedProcess {
private barrier = new Barrier();
private window: Electron.BrowserWindow;
private disposables: IDisposable[] = [];
constructor(
private environmentService: IEnvironmentService,
private readonly environmentService: IEnvironmentService,
private readonly lifecycleService: ILifecycleService,
private readonly logService: ILogService,
private readonly machineId: string,
private readonly userEnv: IProcessEnvironment,
private readonly logService: ILogService
) { }
) {
}
@memoize
private get _whenReady(): TPromise<void> {
@@ -50,16 +51,27 @@ export class SharedProcess implements ISharedProcess {
// Prevent the window from dying
const onClose = (e: Event) => {
this.logService.trace('SharedProcess#close prevented');
// We never allow to close the shared process unless we get explicitly disposed()
e.preventDefault();
// Still hide the window though if visible
if (this.window.isVisible()) {
e.preventDefault();
this.window.hide();
}
};
this.window.on('close', onClose);
this.disposables.push(toDisposable(() => this.window.removeListener('close', onClose)));
this.disposables.push(toDisposable(() => {
this.lifecycleService.onShutdown(() => {
// Shut the shared process down when we are quitting
//
// Note: because we veto the window close, we must first remove our veto.
// Otherwise the application would never quit because the shared process
// window is refusing to close!
//
this.window.removeListener('close', onClose);
// Electron seems to crash on Windows without this setTimeout :|
setTimeout(() => {
@@ -71,7 +83,7 @@ export class SharedProcess implements ISharedProcess {
this.window = null;
}, 0);
}));
});
return new TPromise<void>((c, e) => {
ipcMain.once('handshake:hello', ({ sender }: { sender: any }) => {
@@ -111,8 +123,4 @@ export class SharedProcess implements ISharedProcess {
this.window.webContents.closeDevTools();
this.window.hide();
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}

View File

@@ -7,7 +7,7 @@
import * as path from 'path';
import * as objects from 'vs/base/common/objects';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { IStateService } from 'vs/platform/state/common/state';
import { shell, screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage } from 'electron';
@@ -20,35 +20,19 @@ import product from 'vs/platform/node/product';
import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
import { ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows';
import { IWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
import { IBackupMainService } from 'vs/platform/backup/common/backup';
import { ICommandAction } from 'vs/platform/actions/common/actions';
import { mark, exportEntries } from 'vs/base/common/performance';
import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/node/extensionGalleryService';
export interface IWindowState {
width?: number;
height?: number;
x?: number;
y?: number;
mode?: WindowMode;
display?: number;
}
export interface IWindowCreationOptions {
state: IWindowState;
extensionDevelopmentPath?: string;
isExtensionTestHost?: boolean;
}
export enum WindowMode {
Maximized,
Normal,
Minimized, // not used anymore, but also cannot remove due to existing stored UI state (needs migration)
Fullscreen
}
export const defaultWindowState = function (mode = WindowMode.Normal): IWindowState {
return {
width: 1024,
@@ -92,7 +76,7 @@ export class CodeWindow implements ICodeWindow {
private toDispose: IDisposable[];
private representedFilename: string;
private whenReadyCallbacks: TValueCallback<CodeWindow>[];
private whenReadyCallbacks: TValueCallback<ICodeWindow>[];
private currentConfig: IWindowConfiguration;
private pendingLoadConfig: IWindowConfiguration;
@@ -167,8 +151,16 @@ export class CodeWindow implements ICodeWindow {
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
if (isMacintosh) {
options.acceptFirstMouse = true; // enabled by default
if (windowConfig && windowConfig.clickThroughInactive === false) {
options.acceptFirstMouse = false;
}
}
let useNativeTabs = false;
if (windowConfig && windowConfig.nativeTabs) {
if (isMacintosh && windowConfig && windowConfig.nativeTabs === true) {
options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs
useNativeTabs = true;
}
@@ -219,15 +211,6 @@ export class CodeWindow implements ICodeWindow {
this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any
}
// Set relaunch command
if (isWindows && product.win32AppUserModelId && typeof this._win.setAppDetails === 'function') {
this._win.setAppDetails({
appId: product.win32AppUserModelId,
relaunchCommand: `"${process.execPath}" -n`,
relaunchDisplayName: product.nameLong
});
}
if (isFullscreenOrMaximized) {
this._win.maximize();
@@ -324,8 +307,8 @@ export class CodeWindow implements ICodeWindow {
}
}
public ready(): TPromise<CodeWindow> {
return new TPromise<CodeWindow>((c) => {
public ready(): TPromise<ICodeWindow> {
return new TPromise<ICodeWindow>((c) => {
if (this._readyState === ReadyState.READY) {
return c(this);
}
@@ -443,6 +426,34 @@ export class CodeWindow implements ICodeWindow {
// Handle Workspace events
this.toDispose.push(this.workspacesMainService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e)));
// TODO@Ben workaround for https://github.com/Microsoft/vscode/issues/13612
// It looks like smooth scrolling disappears as soon as the window is minimized
// and maximized again. Touching some window properties "fixes" it, like toggling
// the visibility of the menu.
if (isWindows) {
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
if (windowConfig && windowConfig.smoothScrollingWorkaround === true) {
let minimized = false;
const restoreSmoothScrolling = () => {
if (minimized) {
const visibility = this.getMenuBarVisibility();
const temporaryVisibility: MenuBarVisibility = (visibility === 'hidden' || visibility === 'toggle') ? 'default' : 'hidden';
setTimeout(() => {
this.doSetMenuBarVisibility(temporaryVisibility);
this.doSetMenuBarVisibility(visibility);
}, 0);
}
minimized = false;
};
this._win.on('minimize', () => minimized = true);
this._win.on('restore', () => restoreSmoothScrolling());
this._win.on('maximize', () => restoreSmoothScrolling());
}
}
}
private onUntitledWorkspaceDeleted(workspace: IWorkspaceIdentifier): void {
@@ -599,7 +610,7 @@ export class CodeWindow implements ICodeWindow {
// Perf Counters
windowConfiguration.perfEntries = exportEntries();
windowConfiguration.perfStartTime = global.perfStartTime;
windowConfiguration.perfStartTime = (<any>global).perfStartTime;
windowConfiguration.perfWindowLoadTime = Date.now();
// Config (combination of process.argv and window configuration)
@@ -826,11 +837,32 @@ export class CodeWindow implements ICodeWindow {
return menuBarVisibility;
}
public setMenuBarVisibility(visibility: MenuBarVisibility, notify: boolean = true): void {
private setMenuBarVisibility(visibility: MenuBarVisibility, notify: boolean = true): void {
if (isMacintosh) {
return; // ignore for macOS platform
}
if (visibility === 'toggle') {
if (notify) {
this.send('vscode:showInfoMessage', nls.localize('hiddenMenuBar', "You can still access the menu bar by pressing the Alt-key."));
}
}
if (visibility === 'hidden') {
// for some weird reason that I have no explanation for, the menu bar is not hiding when calling
// this without timeout (see https://github.com/Microsoft/vscode/issues/19777). there seems to be
// a timing issue with us opening the first window and the menu bar getting created. somehow the
// fact that we want to hide the menu without being able to bring it back via Alt key makes Electron
// still show the menu. Unable to reproduce from a simple Hello World application though...
setTimeout(() => {
this.doSetMenuBarVisibility(visibility);
});
} else {
this.doSetMenuBarVisibility(visibility);
}
}
private doSetMenuBarVisibility(visibility: MenuBarVisibility): void {
const isFullscreen = this._win.isFullScreen();
switch (visibility) {
@@ -847,22 +879,11 @@ export class CodeWindow implements ICodeWindow {
case ('toggle'):
this._win.setMenuBarVisibility(false);
this._win.setAutoHideMenuBar(true);
if (notify) {
this.send('vscode:showInfoMessage', nls.localize('hiddenMenuBar', "You can still access the menu bar by pressing the Alt-key."));
}
break;
case ('hidden'):
// for some weird reason that I have no explanation for, the menu bar is not hiding when calling
// this without timeout (see https://github.com/Microsoft/vscode/issues/19777). there seems to be
// a timing issue with us opening the first window and the menu bar getting created. somehow the
// fact that we want to hide the menu without being able to bring it back via Alt key makes Electron
// still show the menu. Unable to reproduce from a simple Hello World application though...
setTimeout(() => {
this._win.setMenuBarVisibility(false);
this._win.setAutoHideMenuBar(false);
});
this._win.setMenuBarVisibility(false);
this._win.setAutoHideMenuBar(false);
break;
}
}
@@ -907,7 +928,9 @@ export class CodeWindow implements ICodeWindow {
}
public send(channel: string, ...args: any[]): void {
this._win.webContents.send(channel, ...args);
if (this._win) {
this._win.webContents.send(channel, ...args);
}
}
public updateTouchBar(groups: ICommandAction[][]): void {

View File

@@ -13,7 +13,7 @@ import { assign, mixin, equals } from 'vs/base/common/objects';
import { IBackupMainService } from 'vs/platform/backup/common/backup';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { IStateService } from 'vs/platform/state/common/state';
import { CodeWindow, IWindowState as ISingleWindowState, defaultWindowState, WindowMode } from 'vs/code/electron-main/window';
import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, app } from 'electron';
import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/paths';
import { ILifecycleService, UnloadReason, IWindowUnloadEvent } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
@@ -21,11 +21,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ILogService } from 'vs/platform/log/common/log';
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 { Event as 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, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
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';
@@ -36,6 +36,7 @@ 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';
import { exists } from 'vs/base/node/pfs';
enum WindowError {
UNRESPONSIVE,
@@ -76,7 +77,7 @@ interface IOpenBrowserWindowOptions {
filesToWait?: IPathsToWaitFor;
forceNewWindow?: boolean;
windowToUse?: CodeWindow;
windowToUse?: ICodeWindow;
emptyWindowBackupFolder?: string;
}
@@ -102,7 +103,7 @@ export class WindowsManager implements IWindowsMainService {
private static readonly windowsStateStorageKey = 'windowsState';
private static WINDOWS: CodeWindow[] = [];
private static WINDOWS: ICodeWindow[] = [];
private initialUserEnv: IProcessEnvironment;
@@ -112,8 +113,8 @@ export class WindowsManager implements IWindowsMainService {
private dialogs: Dialogs;
private workspacesManager: WorkspacesManager;
private _onWindowReady = new Emitter<CodeWindow>();
onWindowReady: CommonEvent<CodeWindow> = this._onWindowReady.event;
private _onWindowReady = new Emitter<ICodeWindow>();
onWindowReady: CommonEvent<ICodeWindow> = this._onWindowReady.event;
private _onWindowClose = new Emitter<number>();
onWindowClose: CommonEvent<number> = this._onWindowClose.event;
@@ -121,8 +122,8 @@ export class WindowsManager implements IWindowsMainService {
private _onWindowLoad = new Emitter<number>();
onWindowLoad: CommonEvent<number> = this._onWindowLoad.event;
private _onActiveWindowChanged = new Emitter<CodeWindow>();
onActiveWindowChanged: CommonEvent<CodeWindow> = this._onActiveWindowChanged.event;
private _onActiveWindowChanged = new Emitter<ICodeWindow>();
onActiveWindowChanged: CommonEvent<ICodeWindow> = this._onActiveWindowChanged.event;
private _onWindowReload = new Emitter<number>();
onWindowReload: CommonEvent<number> = this._onWindowReload.event;
@@ -168,7 +169,7 @@ export class WindowsManager implements IWindowsMainService {
});
// React to workbench loaded events from windows
ipc.on('vscode:workbenchLoaded', (_event: any, windowId: number) => {
ipc.on('vscode:workbenchLoaded', (event: any, windowId: number) => {
this.logService.trace('IPC#vscode-workbenchLoaded');
const win = this.getWindowById(windowId);
@@ -193,8 +194,8 @@ export class WindowsManager implements IWindowsMainService {
// Handle various lifecycle events around windows
this.lifecycleService.onBeforeWindowUnload(e => this.onBeforeWindowUnload(e));
this.lifecycleService.onBeforeWindowClose(win => this.onBeforeWindowClose(win as CodeWindow));
this.lifecycleService.onBeforeQuit(() => this.onBeforeQuit());
this.lifecycleService.onBeforeWindowClose(win => this.onBeforeWindowClose(win as ICodeWindow));
this.lifecycleService.onBeforeShutdown(() => this.onBeforeShutdown());
this.onWindowsCountChanged(e => {
if (e.newCount - e.oldCount > 0) {
// clear last closed window state when a new window opens. this helps on macOS where
@@ -205,12 +206,12 @@ export class WindowsManager implements IWindowsMainService {
});
}
// Note that onBeforeQuit() and onBeforeWindowClose() are fired in different order depending on the OS:
// Note that onBeforeShutdown() and onBeforeWindowClose() are fired in different order depending on the OS:
// - macOS: since the app will not quit when closing the last window, you will always first get
// the onBeforeQuit() event followed by N onbeforeWindowClose() events for each window
// the onBeforeShutdown() event followed by N onbeforeWindowClose() events for each window
// - other: on other OS, closing the last window will quit the app so the order depends on the
// user interaction: closing the last window will first trigger onBeforeWindowClose()
// and then onBeforeQuit(). Using the quit action however will first issue onBeforeQuit()
// and then onBeforeShutdown(). Using the quit action however will first issue onBeforeShutdown()
// and then onBeforeWindowClose().
//
// Here is the behaviour on different OS dependig on action taken (Electron 1.7.x):
@@ -219,30 +220,30 @@ export class WindowsManager implements IWindowsMainService {
// - quit(N): quit application with N windows opened
// - close(1): close one window via the window close button
// - closeAll: close all windows via the taskbar command
// - onBeforeQuit(N): number of windows reported in this event handler
// - onBeforeShutdown(N): number of windows reported in this event handler
// - onBeforeWindowClose(N, M): number of windows reported and quitRequested boolean in this event handler
//
// macOS
// - quit(1): onBeforeQuit(1), onBeforeWindowClose(1, true)
// - quit(2): onBeforeQuit(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
// - quit(0): onBeforeQuit(0)
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
// - quit(0): onBeforeShutdown(0)
// - close(1): onBeforeWindowClose(1, false)
//
// Windows
// - quit(1): onBeforeQuit(1), onBeforeWindowClose(1, true)
// - quit(2): onBeforeQuit(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
// - close(1): onBeforeWindowClose(2, false)[not last window]
// - close(1): onBeforeWindowClose(1, false), onBeforequit(0)[last window]
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeQuit(0)
// - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window]
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0)
//
// Linux
// - quit(1): onBeforeQuit(1), onBeforeWindowClose(1, true)
// - quit(2): onBeforeQuit(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
// - close(1): onBeforeWindowClose(2, false)[not last window]
// - close(1): onBeforeWindowClose(1, false), onBeforequit(0)[last window]
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeQuit(0)
// - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window]
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0)
//
private onBeforeQuit(): void {
private onBeforeShutdown(): void {
const currentWindowsState: IWindowsState = {
openedWindows: [],
lastPluginDevelopmentHostWindow: this.windowsState.lastPluginDevelopmentHostWindow,
@@ -280,9 +281,9 @@ export class WindowsManager implements IWindowsMainService {
this.stateService.setItem(WindowsManager.windowsStateStorageKey, currentWindowsState);
}
// See note on #onBeforeQuit() for details how these events are flowing
private onBeforeWindowClose(win: CodeWindow): void {
if (this.lifecycleService.isQuitRequested()) {
// See note on #onBeforeShutdown() for details how these events are flowing
private onBeforeWindowClose(win: ICodeWindow): void {
if (this.lifecycleService.isQuitRequested) {
return; // during quit, many windows close in parallel so let it be handled in the before-quit handler
}
@@ -313,7 +314,7 @@ export class WindowsManager implements IWindowsMainService {
}
}
private toWindowState(win: CodeWindow): IWindowState {
private toWindowState(win: ICodeWindow): IWindowState {
return {
workspace: win.openedWorkspace,
folderPath: win.openedFolderPath,
@@ -322,7 +323,7 @@ export class WindowsManager implements IWindowsMainService {
};
}
public open(openConfig: IOpenConfiguration): CodeWindow[] {
public open(openConfig: IOpenConfiguration): ICodeWindow[] {
openConfig = this.validateOpenConfig(openConfig);
let pathsToOpen = this.getPathsToOpen(openConfig);
@@ -481,7 +482,7 @@ export class WindowsManager implements IWindowsMainService {
filesToWait: IPathsToWaitFor,
foldersToAdd: IPath[]
) {
const usedWindows: CodeWindow[] = [];
const usedWindows: ICodeWindow[] = [];
// Settings can decide if files/folders open in new window or not
let { openFolderInNewWindow, openFilesInNewWindow } = this.shouldOpenNewWindow(openConfig);
@@ -536,7 +537,7 @@ export class WindowsManager implements IWindowsMainService {
else {
// Do open files
usedWindows.push(this.doOpenFilesInExistingWindow(bestWindowOrFolder, filesToOpen, filesToCreate, filesToDiff, filesToWait));
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, filesToOpen, filesToCreate, filesToDiff, filesToWait));
// Reset these because we handled them
filesToOpen = [];
@@ -582,7 +583,7 @@ export class WindowsManager implements IWindowsMainService {
const windowOnWorkspace = windowsOnWorkspace[0];
// Do open files
usedWindows.push(this.doOpenFilesInExistingWindow(windowOnWorkspace, filesToOpen, filesToCreate, filesToDiff, filesToWait));
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, filesToOpen, filesToCreate, filesToDiff, filesToWait));
// Reset these because we handled them
filesToOpen = [];
@@ -622,7 +623,7 @@ export class WindowsManager implements IWindowsMainService {
const windowOnFolderPath = windowsOnFolderPath[0];
// Do open files
usedWindows.push(this.doOpenFilesInExistingWindow(windowOnFolderPath, filesToOpen, filesToCreate, filesToDiff, filesToWait));
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, filesToOpen, filesToCreate, filesToDiff, filesToWait));
// Reset these because we handled them
filesToOpen = [];
@@ -694,17 +695,18 @@ export class WindowsManager implements IWindowsMainService {
return arrays.distinct(usedWindows);
}
private doOpenFilesInExistingWindow(window: CodeWindow, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[], filesToWait: IPathsToWaitFor): CodeWindow {
private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[], filesToWait: IPathsToWaitFor): ICodeWindow {
window.focus(); // make sure window has focus
window.ready().then(readyWindow => {
readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff, filesToWait });
const termProgram = configuration.userEnv ? configuration.userEnv['TERM_PROGRAM'] : void 0;
readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff, filesToWait, termProgram });
});
return window;
}
private doAddFoldersToExistingWidow(window: CodeWindow, foldersToAdd: IPath[]): CodeWindow {
private doAddFoldersToExistingWidow(window: ICodeWindow, foldersToAdd: IPath[]): ICodeWindow {
window.focus(); // make sure window has focus
window.ready().then(readyWindow => {
@@ -714,7 +716,7 @@ export class WindowsManager implements IWindowsMainService {
return window;
}
private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, openInNewWindow: boolean, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[], filesToWait: IPathsToWaitFor, windowToUse?: CodeWindow): CodeWindow {
private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, openInNewWindow: boolean, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[], filesToWait: IPathsToWaitFor, windowToUse?: ICodeWindow): ICodeWindow {
const browserWindow = this.openInBrowserWindow({
userEnv: openConfig.userEnv,
cli: openConfig.cli,
@@ -981,10 +983,22 @@ export class WindowsManager implements IWindowsMainService {
if (openConfig.forceNewWindow || openConfig.forceReuseWindow) {
openFilesInNewWindow = openConfig.forceNewWindow && !openConfig.forceReuseWindow;
} else {
if (openConfig.context === OpenContext.DOCK) {
openFilesInNewWindow = true; // only on macOS do we allow to open files in a new window if this is triggered via DOCK context
// macOS: by default we open files in a new window if this is triggered via DOCK context
if (isMacintosh) {
if (openConfig.context === OpenContext.DOCK) {
openFilesInNewWindow = true;
}
}
// Linux/Windows: by default we open files in the new window unless triggered via DIALOG or MENU context
else {
if (openConfig.context !== OpenContext.DIALOG && openConfig.context !== OpenContext.MENU) {
openFilesInNewWindow = true;
}
}
// finally check for overrides of default
if (!openConfig.cli.extensionDevelopmentPath && (openFilesInNewWindowConfig === 'on' || openFilesInNewWindowConfig === 'off')) {
openFilesInNewWindow = (openFilesInNewWindowConfig === 'on');
}
@@ -1024,7 +1038,7 @@ export class WindowsManager implements IWindowsMainService {
this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: openConfig.cli._.length === 0, userEnv: openConfig.userEnv });
}
private openInBrowserWindow(options: IOpenBrowserWindowOptions): CodeWindow {
private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow {
// Build IWindowConfiguration from config and options
const configuration: IWindowConfiguration = mixin({}, options.cli); // inherit all properties from CLI
@@ -1049,7 +1063,7 @@ export class WindowsManager implements IWindowsMainService {
configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupFolder);
}
let window: CodeWindow;
let window: ICodeWindow;
if (!options.forceNewWindow) {
window = options.windowToUse || this.getLastActiveWindow();
if (window) {
@@ -1217,9 +1231,12 @@ export class WindowsManager implements IWindowsMainService {
}
}
// Compute x/y based on display bounds
// Note: important to use Math.round() because Electron does not seem to be too happy about
// display coordinates that are not absolute numbers.
let state = defaultWindowState() as INewWindowState;
state.x = displayToUse.bounds.x + (displayToUse.bounds.width / 2) - (state.width / 2);
state.y = displayToUse.bounds.y + (displayToUse.bounds.height / 2) - (state.height / 2);
state.x = Math.round(displayToUse.bounds.x + (displayToUse.bounds.width / 2) - (state.width / 2));
state.y = Math.round(displayToUse.bounds.y + (displayToUse.bounds.height / 2) - (state.height / 2));
// Check for newWindowDimensions setting and adjust accordingly
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
@@ -1266,7 +1283,7 @@ export class WindowsManager implements IWindowsMainService {
return state;
}
public reload(win: CodeWindow, cli?: ParsedArgs): void {
public reload(win: ICodeWindow, cli?: ParsedArgs): void {
// Only reload when the window has not vetoed this
this.lifecycleService.unload(win, UnloadReason.RELOAD).done(veto => {
@@ -1279,22 +1296,22 @@ export class WindowsManager implements IWindowsMainService {
});
}
public closeWorkspace(win: CodeWindow): void {
public closeWorkspace(win: ICodeWindow): void {
this.openInBrowserWindow({
cli: this.environmentService.args,
windowToUse: win
});
}
public saveAndEnterWorkspace(win: CodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
public saveAndEnterWorkspace(win: ICodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
return this.workspacesManager.saveAndEnterWorkspace(win, path).then(result => this.doEnterWorkspace(win, result));
}
public createAndEnterWorkspace(win: CodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise<IEnterWorkspaceResult> {
public 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: CodeWindow, result: IEnterWorkspaceResult): IEnterWorkspaceResult {
private doEnterWorkspace(win: ICodeWindow, result: IEnterWorkspaceResult): IEnterWorkspaceResult {
// Mark as recently opened
this.historyMainService.addRecentlyOpened([result.workspace], []);
@@ -1346,7 +1363,7 @@ export class WindowsManager implements IWindowsMainService {
}));
}
public focusLastActive(cli: ParsedArgs, context: OpenContext): CodeWindow {
public focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow {
const lastActive = this.getLastActiveWindow();
if (lastActive) {
lastActive.focus();
@@ -1358,12 +1375,12 @@ export class WindowsManager implements IWindowsMainService {
return this.open({ context, cli, forceEmpty: true })[0];
}
public getLastActiveWindow(): CodeWindow {
public getLastActiveWindow(): ICodeWindow {
return getLastActiveWindow(WindowsManager.WINDOWS);
}
public openNewWindow(context: OpenContext): void {
this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true });
public openNewWindow(context: OpenContext): ICodeWindow[] {
return this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true });
}
public waitForWindowCloseOrLoad(windowId: number): TPromise<void> {
@@ -1400,7 +1417,7 @@ export class WindowsManager implements IWindowsMainService {
});
}
public getFocusedWindow(): CodeWindow {
public getFocusedWindow(): ICodeWindow {
const win = BrowserWindow.getFocusedWindow();
if (win) {
return this.getWindowById(win.id);
@@ -1409,7 +1426,7 @@ export class WindowsManager implements IWindowsMainService {
return null;
}
public getWindowById(windowId: number): CodeWindow {
public getWindowById(windowId: number): ICodeWindow {
const res = WindowsManager.WINDOWS.filter(w => w.id === windowId);
if (res && res.length === 1) {
return res[0];
@@ -1418,7 +1435,7 @@ export class WindowsManager implements IWindowsMainService {
return null;
}
public getWindows(): CodeWindow[] {
public getWindows(): ICodeWindow[] {
return WindowsManager.WINDOWS;
}
@@ -1426,7 +1443,7 @@ export class WindowsManager implements IWindowsMainService {
return WindowsManager.WINDOWS.length;
}
private onWindowError(window: CodeWindow, error: WindowError): void {
private onWindowError(window: ICodeWindow, error: WindowError): void {
this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive');
// Unresponsive
@@ -1476,7 +1493,7 @@ export class WindowsManager implements IWindowsMainService {
}
}
private onWindowClosed(win: CodeWindow): void {
private onWindowClosed(win: ICodeWindow): void {
// Tell window
win.dispose();
@@ -1524,6 +1541,7 @@ export class WindowsManager implements IWindowsMainService {
if (!internalOptions.telemetryEventName) {
if (pickFolders && pickFiles) {
// __GDPR__TODO__ classify event
internalOptions.telemetryEventName = 'openFileFolder';
} else if (pickFolders) {
internalOptions.telemetryEventName = 'openFolder';
@@ -1535,15 +1553,15 @@ export class WindowsManager implements IWindowsMainService {
this.dialogs.pickAndOpen(internalOptions);
}
public showMessageBox(options: Electron.MessageBoxOptions, win?: CodeWindow): TPromise<IMessageBoxResult> {
public showMessageBox(options: Electron.MessageBoxOptions, win?: ICodeWindow): TPromise<IMessageBoxResult> {
return this.dialogs.showMessageBox(options, win);
}
public showSaveDialog(options: Electron.SaveDialogOptions, win?: CodeWindow): TPromise<string> {
public showSaveDialog(options: Electron.SaveDialogOptions, win?: ICodeWindow): TPromise<string> {
return this.dialogs.showSaveDialog(options, win);
}
public showOpenDialog(options: Electron.OpenDialogOptions, win?: CodeWindow): TPromise<string[]> {
public showOpenDialog(options: Electron.OpenDialogOptions, win?: ICodeWindow): TPromise<string[]> {
return this.dialogs.showOpenDialog(options, win);
}
@@ -1684,6 +1702,7 @@ class Dialogs {
}
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
@@ -1702,6 +1721,7 @@ class Dialogs {
}
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
@@ -1712,8 +1732,22 @@ class Dialogs {
return this.getDialogQueue(window).queue(() => {
return new TPromise((c, e) => {
dialog.showOpenDialog(window ? window.win : void 0, options, paths => {
c(normalizePaths(paths));
// Ensure the path exists (if provided)
let validatePathPromise: TPromise<void> = TPromise.as(void 0);
if (options.defaultPath) {
validatePathPromise = exists(options.defaultPath).then(exists => {
if (!exists) {
options.defaultPath = void 0;
}
});
}
// Show dialog and wrap as promise
validatePathPromise.then(() => {
dialog.showOpenDialog(window ? window.win : void 0, options, paths => {
c(normalizePaths(paths));
});
});
});
});
@@ -1730,7 +1764,7 @@ class WorkspacesManager {
) {
}
public saveAndEnterWorkspace(window: CodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
public saveAndEnterWorkspace(window: ICodeWindow, 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
}
@@ -1738,7 +1772,7 @@ class WorkspacesManager {
return this.doSaveAndOpenWorkspace(window, window.openedWorkspace, path);
}
public createAndEnterWorkspace(window: CodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise<IEnterWorkspaceResult> {
public createAndEnterWorkspace(window: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise<IEnterWorkspaceResult> {
if (!window || !window.win || window.readyState !== ReadyState.READY) {
return TPromise.as(null); // return early if the window is not ready or disposed
}
@@ -1755,7 +1789,7 @@ class WorkspacesManager {
}
private isValidTargetWorkspacePath(window: CodeWindow, path?: string): TPromise<boolean> {
private isValidTargetWorkspacePath(window: ICodeWindow, path?: string): TPromise<boolean> {
if (!path) {
return TPromise.wrap(true);
}
@@ -1781,7 +1815,7 @@ class WorkspacesManager {
return TPromise.wrap(true); // OK
}
private doSaveAndOpenWorkspace(window: CodeWindow, workspace: IWorkspaceIdentifier, path?: string): TPromise<IEnterWorkspaceResult> {
private doSaveAndOpenWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier, path?: string): TPromise<IEnterWorkspaceResult> {
let savePromise: TPromise<IWorkspaceIdentifier>;
if (path) {
savePromise = this.workspacesMainService.saveWorkspace(workspace, path);