Revert "Merge from vscode 81d7885dc2e9dc617e1522697a2966bc4025a45d (#5949)" (#5983)

This reverts commit d15a3fcc98.
This commit is contained in:
Karl Burtram
2019-06-11 12:35:58 -07:00
committed by GitHub
parent 95a50b7892
commit 5a7562a37b
926 changed files with 11394 additions and 19540 deletions

View File

@@ -9,7 +9,7 @@ import { WindowsManager } from 'vs/code/electron-main/windows';
import { IWindowsService, OpenContext, ActiveWindowManager, IURIToOpen } from 'vs/platform/windows/common/windows';
import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc';
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { ILifecycleService, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { getShellEnvironment } from 'vs/code/node/shellEnv';
import { IUpdateService } from 'vs/platform/update/common/update';
import { UpdateChannel } from 'vs/platform/update/node/updateIpc';
@@ -36,10 +36,12 @@ import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
import product from 'vs/platform/product/node/product';
import pkg from 'vs/platform/product/node/package';
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { IHistoryMainService } from 'vs/platform/history/common/history';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types';
import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard';
import { URI } from 'vs/base/common/uri';
import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc';
import { IWorkspacesMainService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
@@ -61,6 +63,7 @@ import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc';
import { hasArgs } from 'vs/platform/environment/node/argv';
import { RunOnceScheduler } from 'vs/base/common/async';
import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu';
import { storeBackgroundColor } from 'vs/code/electron-main/theme';
import { homedir } from 'os';
import { join, sep } from 'vs/base/common/path';
import { localize } from 'vs/nls';
@@ -85,7 +88,12 @@ export class CodeApplication extends Disposable {
private static readonly MACHINE_ID_KEY = 'telemetry.machineId';
private windowsMainService: IWindowsMainService | undefined;
private windowsMainService: IWindowsMainService;
private electronIpcServer: ElectronIPCServer;
private sharedProcess: SharedProcess;
private sharedProcessClient: Promise<Client>;
constructor(
private readonly mainIpcServer: Server,
@@ -94,11 +102,14 @@ export class CodeApplication extends Disposable {
@ILogService private readonly logService: ILogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IConfigurationService private readonly configurationService: ConfigurationService,
@IStateService private readonly stateService: IStateService
) {
super();
this._register(mainIpcServer);
this._register(configurationService);
this.registerListeners();
}
@@ -109,12 +120,12 @@ export class CodeApplication extends Disposable {
process.on('uncaughtException', err => this.onUnexpectedError(err));
process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason));
// Dispose on shutdown
this.lifecycleService.onWillShutdown(() => this.dispose());
// Contextmenu via IPC support
registerContextMenuListener();
// Dispose on shutdown
this.lifecycleService.onWillShutdown(() => this.dispose());
app.on('accessibility-support-changed', (event: Event, accessibilitySupportEnabled: boolean) => {
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled);
@@ -187,7 +198,7 @@ export class CodeApplication extends Disposable {
event.preventDefault();
// Keep in array because more might come!
macOpenFileURIs.push(this.getURIToOpenFromPathSync(path));
macOpenFileURIs.push(getURIToOpenFromPathSync(path));
// Clear previous handler if any
if (runningTimeout !== null) {
@@ -204,7 +215,6 @@ export class CodeApplication extends Disposable {
urisToOpen: macOpenFileURIs,
preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
});
macOpenFileURIs = [];
runningTimeout = null;
}
@@ -212,9 +222,7 @@ export class CodeApplication extends Disposable {
});
app.on('new-window-for-tab', () => {
if (this.windowsMainService) {
this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button
}
this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button
});
ipc.on('vscode:exit', (event: Event, code: number) => {
@@ -224,26 +232,37 @@ export class CodeApplication extends Disposable {
this.lifecycleService.kill(code);
});
ipc.on('vscode:fetchShellEnv', async (event: Event) => {
ipc.on('vscode:fetchShellEnv', (event: Event) => {
const webContents = event.sender;
try {
const shellEnv = await getShellEnvironment(this.logService, this.environmentService);
getShellEnvironment(this.logService).then(shellEnv => {
if (!webContents.isDestroyed()) {
webContents.send('vscode:acceptShellEnv', shellEnv);
}
} catch (error) {
}, err => {
if (!webContents.isDestroyed()) {
webContents.send('vscode:acceptShellEnv', {});
}
this.logService.error('Error fetching shell env', error);
this.logService.error('Error fetching shell env', err);
});
});
ipc.on('vscode:broadcast', (event: Event, windowId: number, broadcast: { channel: string; payload: object; }) => {
if (this.windowsMainService && broadcast.channel && !isUndefinedOrNull(broadcast.payload)) {
this.logService.trace('IPC#vscode:broadcast', broadcast.channel, broadcast.payload);
// Handle specific events on main side
this.onBroadcast(broadcast.channel, broadcast.payload);
// Send to all windows (except sender window)
this.windowsMainService.sendToAll('vscode:broadcast', broadcast, [windowId]);
}
});
ipc.on('vscode:extensionHostDebug', (_: Event, windowId: number, broadcast: any) => {
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:extensionHostDebug', broadcast, [windowId]); // Send to all windows (except sender window)
// Send to all windows (except sender window)
this.windowsMainService.sendToAll('vscode:extensionHostDebug', broadcast, [windowId]);
}
});
@@ -252,28 +271,11 @@ export class CodeApplication extends Disposable {
ipc.on('vscode:reloadWindow', (event: Event) => event.sender.reload());
// After waking up from sleep (after window opened)
(async () => {
await this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen);
powerMonitor.on('resume', () => {
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:osResume', undefined);
}
});
})();
// Keyboard layout changes (after window opened)
(async () => {
await this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen);
const nativeKeymap = await import('native-keymap');
nativeKeymap.onDidChangeKeyboardLayout(() => {
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
}
});
})();
powerMonitor.on('resume', () => { // After waking up from sleep
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:osResume', undefined);
}
});
}
private onUnexpectedError(err: Error): void {
@@ -297,7 +299,15 @@ export class CodeApplication extends Disposable {
}
}
async startup(): Promise<void> {
private onBroadcast(event: string, payload: object): void {
// Theme changes
if (event === 'vscode:changeColorTheme' && typeof payload === 'string') {
storeBackgroundColor(this.stateService, JSON.parse(payload));
}
}
startup(): Promise<void> {
this.logService.debug('Starting VS Code');
this.logService.debug(`from: ${this.environmentService.appRoot}`);
this.logService.debug('args:', this.environmentService.args);
@@ -325,122 +335,64 @@ export class CodeApplication extends Disposable {
}
// Create Electron IPC Server
const electronIpcServer = new ElectronIPCServer();
this.electronIpcServer = new ElectronIPCServer();
const startupWithMachineId = (machineId: string) => {
this.logService.trace(`Resolved machine identifier: ${machineId}`);
// Spawn shared process
this.sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
// Services
return this.initServices(machineId).then(appInstantiationService => {
// Create driver
if (this.environmentService.driverHandle) {
serveDriver(this.electronIpcServer, this.environmentService.driverHandle, this.environmentService, appInstantiationService).then(server => {
this.logService.info('Driver started at:', this.environmentService.driverHandle);
this._register(server);
});
}
// Setup Auth Handler
const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
this._register(authHandler);
// Open Windows
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor));
// Post Open Windows Tasks
appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
// Tracing: Stop tracing after windows are ready if enabled
if (this.environmentService.args.trace) {
this.stopTracingEventually(windows);
}
});
};
// Resolve unique machine ID
this.logService.trace('Resolving machine identifier...');
const machineId = await this.resolveMachineId();
this.logService.trace(`Resolved machine identifier: ${machineId}`);
// Spawn shared process after the first window has opened and 3s have passed
const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
const sharedProcessClient = sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
this._register(new RunOnceScheduler(async () => {
const userEnv = await getShellEnvironment(this.logService, this.environmentService);
sharedProcess.spawn(userEnv);
}, 3000)).schedule();
});
// Services
const appInstantiationService = await this.createServices(machineId, sharedProcess, sharedProcessClient);
// Create driver
if (this.environmentService.driverHandle) {
(async () => {
const server = await serveDriver(electronIpcServer, this.environmentService.driverHandle!, this.environmentService, appInstantiationService);
this.logService.info('Driver started at:', this.environmentService.driverHandle);
this._register(server);
})();
}
// Setup Auth Handler
const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
this._register(authHandler);
// Open Windows
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient));
// Post Open Windows Tasks
this.afterWindowOpen();
// Tracing: Stop tracing after windows are ready if enabled
if (this.environmentService.args.trace) {
this.stopTracingEventually(windows);
}
}
private async resolveMachineId(): Promise<string> {
// We cache the machineId for faster lookups on startup
// and resolve it only once initially if not cached
let machineId = this.stateService.getItem<string>(CodeApplication.MACHINE_ID_KEY);
if (!machineId) {
machineId = await getMachineId();
this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId);
}
return machineId;
}
private async createServices(machineId: string, sharedProcess: SharedProcess, sharedProcessClient: Promise<Client<string>>): Promise<IInstantiationService> {
const services = new ServiceCollection();
switch (process.platform) {
case 'win32':
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
break;
case 'linux':
if (process.env.SNAP && process.env.SNAP_REVISION) {
services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION]));
} else {
services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
}
break;
case 'darwin':
services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService));
break;
}
services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId, this.userEnv]));
services.set(IWindowsService, new SyncDescriptor(WindowsService, [sharedProcess]));
services.set(ILaunchService, new SyncDescriptor(LaunchService));
services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv]));
services.set(IMenubarService, new SyncDescriptor(MenubarService));
const storageMainService = new StorageMainService(this.logService, this.environmentService);
services.set(IStorageMainService, storageMainService);
this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close()));
const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService);
services.set(IBackupMainService, backupMainService);
services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService));
services.set(IURLService, new SyncDescriptor(URLService));
services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService));
// Telemetry
if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
const channel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('telemetryAppender')));
const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService));
const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, this.environmentService.installSourcePath);
const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths };
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
const resolvedMachineId = this.resolveMachineId();
if (typeof resolvedMachineId === 'string') {
return startupWithMachineId(resolvedMachineId);
} else {
services.set(ITelemetryService, NullTelemetryService);
return resolvedMachineId.then(machineId => startupWithMachineId(machineId));
}
}
private resolveMachineId(): string | Promise<string> {
const machineId = this.stateService.getItem<string>(CodeApplication.MACHINE_ID_KEY);
if (machineId) {
return machineId;
}
// Init services that require it
await backupMainService.initialize();
return getMachineId().then(machineId => {
this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId);
return this.instantiationService.createChild(services);
return machineId;
});
}
private stopTracingEventually(windows: ICodeWindow[]): void {
@@ -456,14 +408,12 @@ export class CodeApplication extends Disposable {
contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => {
if (!timeout) {
if (this.windowsMainService) {
this.windowsMainService.showMessageBox({
type: 'info',
message: localize('trace.message', "Successfully created trace."),
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
buttons: [localize('trace.ok', "Ok")]
}, this.windowsMainService.getLastActiveWindow());
}
this.windowsMainService.showMessageBox({
type: 'info',
message: localize('trace.message', "Successfully created trace."),
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
buttons: [localize('trace.ok', "Ok")]
}, this.windowsMainService.getLastActiveWindow());
} else {
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
}
@@ -480,7 +430,72 @@ export class CodeApplication extends Disposable {
});
}
private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise<Client<string>>): ICodeWindow[] {
private initServices(machineId: string): Promise<IInstantiationService> {
const services = new ServiceCollection();
if (process.platform === 'win32') {
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
} else if (process.platform === 'linux') {
if (process.env.SNAP && process.env.SNAP_REVISION) {
services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION]));
} else {
services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
}
} else if (process.platform === 'darwin') {
services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService));
}
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, this.userEnv]));
services.set(IMenubarService, new SyncDescriptor(MenubarService));
services.set(IStorageMainService, new SyncDescriptor(StorageMainService));
services.set(IBackupMainService, new SyncDescriptor(BackupMainService));
services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService));
services.set(IURLService, new SyncDescriptor(URLService));
services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService));
// Telemetry
if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
const channel = getDelayedChannel(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender')));
const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService));
const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, this.environmentService.installSourcePath);
const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths };
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
} else {
services.set(ITelemetryService, NullTelemetryService);
}
const appInstantiationService = this.instantiationService.createChild(services);
// Init services that require it
return appInstantiationService.invokeFunction(accessor => Promise.all([
this.initStorageService(accessor),
this.initBackupService(accessor)
])).then(() => appInstantiationService);
}
private initStorageService(accessor: ServicesAccessor): Promise<void> {
const storageMainService = accessor.get(IStorageMainService) as StorageMainService;
// Ensure to close storage on shutdown
this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close()));
return Promise.resolve();
}
private initBackupService(accessor: ServicesAccessor): Promise<void> {
const backupMainService = accessor.get(IBackupMainService) as BackupMainService;
return backupMainService.initialize();
}
private openFirstWindow(accessor: ServicesAccessor): ICodeWindow[] {
const appInstantiationService = accessor.get(IInstantiationService);
// Register more Main IPC services
const launchService = accessor.get(ILaunchService);
@@ -490,40 +505,40 @@ export class CodeApplication extends Disposable {
// Register more Electron IPC services
const updateService = accessor.get(IUpdateService);
const updateChannel = new UpdateChannel(updateService);
electronIpcServer.registerChannel('update', updateChannel);
this.electronIpcServer.registerChannel('update', updateChannel);
const issueService = accessor.get(IIssueService);
const issueChannel = new IssueChannel(issueService);
electronIpcServer.registerChannel('issue', issueChannel);
this.electronIpcServer.registerChannel('issue', issueChannel);
const workspacesService = accessor.get(IWorkspacesMainService);
const workspacesChannel = new WorkspacesChannel(workspacesService);
electronIpcServer.registerChannel('workspaces', workspacesChannel);
const workspacesChannel = appInstantiationService.createInstance(WorkspacesChannel, workspacesService);
this.electronIpcServer.registerChannel('workspaces', workspacesChannel);
const windowsService = accessor.get(IWindowsService);
const windowsChannel = new WindowsChannel(windowsService);
electronIpcServer.registerChannel('windows', windowsChannel);
sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));
this.electronIpcServer.registerChannel('windows', windowsChannel);
this.sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));
const menubarService = accessor.get(IMenubarService);
const menubarChannel = new MenubarChannel(menubarService);
electronIpcServer.registerChannel('menubar', menubarChannel);
this.electronIpcServer.registerChannel('menubar', menubarChannel);
const urlService = accessor.get(IURLService);
const urlChannel = new URLServiceChannel(urlService);
electronIpcServer.registerChannel('url', urlChannel);
this.electronIpcServer.registerChannel('url', urlChannel);
const storageMainService = accessor.get(IStorageMainService);
const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService as StorageMainService));
electronIpcServer.registerChannel('storage', storageChannel);
this.electronIpcServer.registerChannel('storage', storageChannel);
// Log level management
const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
electronIpcServer.registerChannel('loglevel', logLevelChannel);
sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel));
this.electronIpcServer.registerChannel('loglevel', logLevelChannel);
this.sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel));
// Signal phase: ready (services set)
this.lifecycleService.phase = LifecycleMainPhase.Ready;
// Lifecycle
(this.lifecycleService as LifecycleService).ready();
// Propagate to clients
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this
@@ -531,7 +546,7 @@ export class CodeApplication extends Disposable {
// Create a URL handler which forwards to the last active window
const activeWindowManager = new ActiveWindowManager(windowsService);
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', activeWindowRouter);
const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', activeWindowRouter);
const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);
// On Mac, Code can be running without any open windows, so we must create a window to handle urls,
@@ -540,17 +555,15 @@ export class CodeApplication extends Disposable {
const environmentService = accessor.get(IEnvironmentService);
urlService.registerHandler({
async handleURL(uri: URI): Promise<boolean> {
handleURL(uri: URI): Promise<boolean> {
if (windowsMainService.getWindowCount() === 0) {
const cli = { ...environmentService.args, goto: true };
const [window] = windowsMainService.open({ context: OpenContext.API, cli, forceEmpty: true });
await window.ready();
return urlService.open(uri);
return window.ready().then(() => urlService.open(uri));
}
return false;
return Promise.resolve(false);
}
});
}
@@ -561,9 +574,11 @@ export class CodeApplication extends Disposable {
// Watch Electron URLs and forward them to the UrlService
const args = this.environmentService.args;
const urls = args['open-url'] ? args._urls : [];
const urlListener = new ElectronURLListener(urls || [], urlService, windowsMainService);
const urlListener = new ElectronURLListener(urls || [], urlService, this.windowsMainService);
this._register(urlListener);
this.windowsMainService.ready(this.userEnv);
// Open our first window
const macOpenFiles: string[] = (<any>global).macOpenFiles;
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
@@ -573,9 +588,9 @@ export class CodeApplication extends Disposable {
const noRecentEntry = args['skip-add-to-recently-opened'] === true;
const waitMarkerFileURI = args.wait && args.waitMarkerFilePath ? URI.file(args.waitMarkerFilePath) : undefined;
// new window if "-n" was used without paths
if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
return windowsMainService.open({
// new window if "-n" was used without paths
return this.windowsMainService.open({
context,
cli: args,
forceNewWindow: true,
@@ -586,12 +601,12 @@ export class CodeApplication extends Disposable {
});
}
// mac: open-file event received on startup
if (macOpenFiles && macOpenFiles.length && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
return windowsMainService.open({
// mac: open-file event received on startup
return this.windowsMainService.open({
context: OpenContext.DOCK,
cli: args,
urisToOpen: macOpenFiles.map(file => this.getURIToOpenFromPathSync(file)),
urisToOpen: macOpenFiles.map(getURIToOpenFromPathSync),
noRecentEntry,
waitMarkerFileURI,
initialStartup: true
@@ -599,7 +614,7 @@ export class CodeApplication extends Disposable {
}
// default: read paths from cli
return windowsMainService.open({
return this.windowsMainService.open({
context,
cli: args,
forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']),
@@ -610,30 +625,61 @@ export class CodeApplication extends Disposable {
});
}
private getURIToOpenFromPathSync(path: string): IURIToOpen {
try {
const fileStat = statSync(path);
if (fileStat.isDirectory()) {
return { folderUri: URI.file(path) };
private afterWindowOpen(accessor: ServicesAccessor): void {
const windowsMainService = accessor.get(IWindowsMainService);
const historyMainService = accessor.get(IHistoryMainService);
if (isWindows) {
// Setup Windows mutex
try {
const Mutex = (require.__$__nodeRequire('windows-mutex') as any).Mutex;
const windowsMutex = new Mutex(product.win32MutexName);
this._register(toDisposable(() => windowsMutex.release()));
} catch (e) {
if (!this.environmentService.isBuilt) {
windowsMainService.showMessageBox({
title: product.nameLong,
type: 'warning',
message: 'Failed to load windows-mutex!',
detail: e.toString(),
noLink: true
});
}
}
if (hasWorkspaceFileExtension(path)) {
return { workspaceUri: URI.file(path) };
// Ensure Windows foreground love module
try {
// tslint:disable-next-line:no-unused-expression
require.__$__nodeRequire('windows-foreground-love');
} catch (e) {
if (!this.environmentService.isBuilt) {
windowsMainService.showMessageBox({
title: product.nameLong,
type: 'warning',
message: 'Failed to load windows-foreground-love!',
detail: e.toString(),
noLink: true
});
}
}
} catch (error) {
// ignore errors
}
return { fileUri: URI.file(path) };
}
private afterWindowOpen(): void {
// Signal phase: after window open
this.lifecycleService.phase = LifecycleMainPhase.AfterWindowOpen;
// Remote Authorities
this.handleRemoteAuthorities();
// Keyboard layout changes
KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => {
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
});
// Jump List
historyMainService.updateWindowsJumpList();
historyMainService.onRecentlyOpenedChange(() => historyMainService.updateWindowsJumpList());
// Start shared process after a while
const sharedProcessSpawn = this._register(new RunOnceScheduler(() => getShellEnvironment(this.logService).then(userEnv => this.sharedProcess.spawn(userEnv)), 3000));
sharedProcessSpawn.schedule();
}
private handleRemoteAuthorities(): void {
@@ -648,9 +694,8 @@ export class CodeApplication extends Disposable {
constructor(authority: string, host: string, port: number) {
this._authority = authority;
const options: IConnectionOptions = {
isBuilt,
isBuilt: isBuilt,
commit: product.commit,
webSocketFactory: nodeWebSocketFactory,
addressProvider: {
@@ -659,7 +704,6 @@ export class CodeApplication extends Disposable {
}
}
};
this._connection = connectRemoteAgentManagement(options, authority, `main`);
this._disposeRunner = new RunOnceScheduler(() => this.dispose(), 5000);
}
@@ -667,13 +711,14 @@ export class CodeApplication extends Disposable {
dispose(): void {
this._disposeRunner.dispose();
connectionPool.delete(this._authority);
this._connection.then(connection => connection.dispose());
this._connection.then((connection) => {
connection.dispose();
});
}
async getClient(): Promise<Client<RemoteAgentConnectionContext>> {
this._disposeRunner.schedule();
const connection = await this._connection;
return connection.client;
}
}
@@ -681,9 +726,7 @@ export class CodeApplication extends Disposable {
const resolvedAuthorities = new Map<string, ResolvedAuthority>();
ipc.on('vscode:remoteAuthorityResolved', (event: Electron.Event, data: ResolvedAuthority) => {
this.logService.info('Received resolved authority', data.authority);
resolvedAuthorities.set(data.authority, data);
// Make sure to close and remove any existing connections
if (connectionPool.has(data.authority)) {
connectionPool.get(data.authority)!.dispose();
@@ -692,19 +735,15 @@ export class CodeApplication extends Disposable {
const resolveAuthority = (authority: string): ResolvedAuthority | null => {
this.logService.info('Resolving authority', authority);
if (authority.indexOf('+') >= 0) {
if (resolvedAuthorities.has(authority)) {
return withUndefinedAsNull(resolvedAuthorities.get(authority));
}
this.logService.info('Didnot find resolved authority for', authority);
return null;
} else {
const [host, strPort] = authority.split(':');
const port = parseInt(strPort, 10);
return { authority, host, port };
}
};
@@ -713,7 +752,6 @@ export class CodeApplication extends Disposable {
if (request.method !== 'GET') {
return callback(undefined);
}
const uri = URI.parse(request.url);
let activeConnection: ActiveConnection | undefined;
@@ -725,11 +763,9 @@ export class CodeApplication extends Disposable {
callback(undefined);
return;
}
activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port);
connectionPool.set(uri.authority, activeConnection);
}
try {
const rawClient = await activeConnection!.getClient();
if (connectionPool.has(uri.authority)) { // not disposed in the meantime
@@ -748,3 +784,16 @@ export class CodeApplication extends Disposable {
});
}
}
function getURIToOpenFromPathSync(path: string): IURIToOpen {
try {
const fileStat = statSync(path);
if (fileStat.isDirectory()) {
return { folderUri: URI.file(path) };
} else if (hasWorkspaceFileExtension(path)) {
return { workspaceUri: URI.file(path) };
}
} catch (error) {
}
return { fileUri: URI.file(path) };
}