mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-22 17:22:59 -05:00
Initial VS Code 1.19 source merge (#571)
* Initial 1.19 xcopy * Fix yarn build * Fix numerous build breaks * Next batch of build break fixes * More build break fixes * Runtime breaks * Additional post merge fixes * Fix windows setup file * Fix test failures. * Update license header blocks to refer to source eula
This commit is contained in:
@@ -12,12 +12,9 @@ import { readdir, rimraf, stat } from 'vs/base/node/pfs';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import product from 'vs/platform/node/product';
|
||||
|
||||
declare type OnNodeCachedDataArgs = [{ errorCode: string, path: string, detail?: string }, { path: string, length: number }];
|
||||
declare const MonacoEnvironment: { onNodeCachedData: OnNodeCachedDataArgs[] };
|
||||
|
||||
export class NodeCachedDataCleaner {
|
||||
|
||||
private static _DataMaxAge = product.nameLong.indexOf('Insiders') >= 0
|
||||
private static readonly _DataMaxAge = product.nameLong.indexOf('Insiders') >= 0
|
||||
? 1000 * 60 * 60 * 24 * 7 // roughly 1 week
|
||||
: 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months
|
||||
|
||||
|
||||
@@ -89,7 +89,11 @@ function main() {
|
||||
});
|
||||
}
|
||||
|
||||
require(['vs/code/electron-browser/sharedProcessMain'], function () { });
|
||||
require(['vs/code/electron-browser/sharedProcessMain'], function (sharedProcess) {
|
||||
sharedProcess.startup({
|
||||
machineId: configuration.machineId
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import { IRequestService } from 'vs/platform/request/node/request';
|
||||
import { RequestService } from 'vs/platform/request/electron-browser/requestService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { resolveCommonProperties, machineIdStorageKey } from 'vs/platform/telemetry/node/commonProperties';
|
||||
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
|
||||
import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc';
|
||||
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
|
||||
@@ -34,8 +34,17 @@ import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { StorageService, inMemoryLocalStorageInstance } from 'vs/platform/storage/common/storageService';
|
||||
import { createSharedProcessContributions } from 'vs/code/electron-browser/contrib/contributions';
|
||||
import { createLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export interface ISharedProcessConfiguration {
|
||||
readonly machineId: string;
|
||||
}
|
||||
|
||||
export function startup(configuration: ISharedProcessConfiguration) {
|
||||
handshake(configuration);
|
||||
}
|
||||
|
||||
interface ISharedProcessInitData {
|
||||
sharedIPCHandle: string;
|
||||
@@ -66,10 +75,17 @@ class ActiveWindowManager implements IDisposable {
|
||||
|
||||
const eventPrefix = 'monacoworkbench';
|
||||
|
||||
function main(server: Server, initData: ISharedProcessInitData): void {
|
||||
function main(server: Server, initData: ISharedProcessInitData, configuration: ISharedProcessConfiguration): void {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService, initData.args, process.execPath));
|
||||
const environmentService = new EnvironmentService(initData.args, process.execPath);
|
||||
const logService = createLogService('sharedprocess', environmentService);
|
||||
process.once('exit', () => logService.dispose());
|
||||
|
||||
logService.info('main', JSON.stringify(configuration));
|
||||
|
||||
services.set(IEnvironmentService, environmentService);
|
||||
services.set(ILogService, logService);
|
||||
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
|
||||
services.set(IRequestService, new SyncDescriptor(RequestService));
|
||||
|
||||
@@ -100,21 +116,12 @@ function main(server: Server, initData: ISharedProcessInitData): void {
|
||||
|
||||
const services = new ServiceCollection();
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, extensionTestsPath, installSource } = environmentService;
|
||||
const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, installSourcePath } = environmentService;
|
||||
|
||||
if (isBuilt && !extensionDevelopmentPath && !environmentService.args['disable-telemetry'] && product.enableTelemetry) {
|
||||
const disableStorage = !!extensionTestsPath; // never keep any state when running extension tests!
|
||||
const storage = disableStorage ? inMemoryLocalStorageInstance : window.localStorage;
|
||||
const storageService = new StorageService(storage, storage);
|
||||
|
||||
const config: ITelemetryServiceConfig = {
|
||||
appender,
|
||||
commonProperties: resolveCommonProperties(product.commit, pkg.version, installSource)
|
||||
// __GDPR__COMMON__ "common.machineId" : { "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" }
|
||||
.then(result => Object.defineProperty(result, 'common.machineId', {
|
||||
get: () => storageService.get(machineIdStorageKey),
|
||||
enumerable: true
|
||||
})),
|
||||
commonProperties: resolveCommonProperties(product.commit, pkg.version, configuration.machineId, installSourcePath),
|
||||
piiPaths: [appRoot, extensionsPath]
|
||||
};
|
||||
|
||||
@@ -177,15 +184,13 @@ function setupIPC(hook: string): TPromise<Server> {
|
||||
|
||||
function startHandshake(): TPromise<ISharedProcessInitData> {
|
||||
return new TPromise<ISharedProcessInitData>((c, e) => {
|
||||
ipcRenderer.once('handshake:hey there', (_, r) => c(r));
|
||||
ipcRenderer.once('handshake:hey there', (_: any, r: ISharedProcessInitData) => c(r));
|
||||
ipcRenderer.send('handshake:hello');
|
||||
});
|
||||
}
|
||||
|
||||
function handshake(): TPromise<void> {
|
||||
function handshake(configuration: ISharedProcessConfiguration): TPromise<void> {
|
||||
return startHandshake()
|
||||
.then((data) => setupIPC(data.sharedIPCHandle).then(server => main(server, data)))
|
||||
.then(data => setupIPC(data.sharedIPCHandle).then(server => main(server, data, configuration)))
|
||||
.then(() => ipcRenderer.send('handshake:im ready'));
|
||||
}
|
||||
|
||||
handshake();
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IStorageService } from 'vs/platform/storage/node/storage';
|
||||
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';
|
||||
@@ -35,10 +35,7 @@ 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';
|
||||
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
||||
import { CredentialsService } from 'vs/platform/credentials/node/credentialsService';
|
||||
import { CredentialsChannel } from 'vs/platform/credentials/node/credentialsIpc';
|
||||
import { resolveCommonProperties, machineIdStorageKey, machineIdIpcChannel } from 'vs/platform/telemetry/node/commonProperties';
|
||||
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
|
||||
import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import product from 'vs/platform/node/product';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
@@ -56,10 +53,12 @@ import { WorkspacesChannel } from 'vs/platform/workspaces/common/workspacesIpc';
|
||||
import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { dirname, join } from 'path';
|
||||
import { touch } from 'vs/base/node/pfs';
|
||||
import { getMachineId } from 'vs/base/node/id';
|
||||
|
||||
export class CodeApplication {
|
||||
|
||||
private static APP_ICON_REFRESH_KEY = 'macOSAppIconRefresh3';
|
||||
private static readonly APP_ICON_REFRESH_KEY = 'macOSAppIconRefresh3';
|
||||
private static readonly MACHINE_ID_KEY = 'telemetry.machineId';
|
||||
|
||||
private toDispose: IDisposable[];
|
||||
private windowsMainService: IWindowsMainService;
|
||||
@@ -76,9 +75,9 @@ export class CodeApplication {
|
||||
@ILogService private logService: ILogService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@ILifecycleService private lifecycleService: ILifecycleService,
|
||||
@IConfigurationService private configurationService: ConfigurationService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@IHistoryMainService private historyService: IHistoryMainService
|
||||
@IConfigurationService configurationService: ConfigurationService,
|
||||
@IStateService private stateService: IStateService,
|
||||
@IHistoryMainService private historyMainService: IHistoryMainService
|
||||
) {
|
||||
this.toDispose = [mainIpcServer, configurationService];
|
||||
|
||||
@@ -110,7 +109,7 @@ export class CodeApplication {
|
||||
});
|
||||
|
||||
app.on('will-quit', () => {
|
||||
this.logService.log('App#will-quit: disposing resources');
|
||||
this.logService.trace('App#will-quit: disposing resources');
|
||||
|
||||
this.dispose();
|
||||
});
|
||||
@@ -122,7 +121,7 @@ export class CodeApplication {
|
||||
});
|
||||
|
||||
app.on('activate', (event: Event, hasVisibleWindows: boolean) => {
|
||||
this.logService.log('App#activate');
|
||||
this.logService.trace('App#activate');
|
||||
|
||||
// Mac only event: open new window when we get activated
|
||||
if (!hasVisibleWindows && this.windowsMainService) {
|
||||
@@ -133,7 +132,7 @@ export class CodeApplication {
|
||||
const isValidWebviewSource = (source: string) =>
|
||||
!source || (URI.parse(source.toLowerCase()).toString() as any).startsWith(URI.file(this.environmentService.appRoot.toLowerCase()).toString());
|
||||
|
||||
app.on('web-contents-created', (event, 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;
|
||||
@@ -157,7 +156,7 @@ export class CodeApplication {
|
||||
let macOpenFiles: string[] = [];
|
||||
let runningTimeout: number = null;
|
||||
app.on('open-file', (event: Event, path: string) => {
|
||||
this.logService.log('App#open-file: ', path);
|
||||
this.logService.trace('App#open-file: ', path);
|
||||
event.preventDefault();
|
||||
|
||||
// Keep in array because more might come!
|
||||
@@ -188,19 +187,14 @@ export class CodeApplication {
|
||||
this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button
|
||||
});
|
||||
|
||||
ipc.on('vscode:exit', (event, code: number) => {
|
||||
this.logService.log('IPC#vscode:exit', code);
|
||||
ipc.on('vscode:exit', (_event: any, code: number) => {
|
||||
this.logService.trace('IPC#vscode:exit', code);
|
||||
|
||||
this.dispose();
|
||||
this.lifecycleService.kill(code);
|
||||
});
|
||||
|
||||
ipc.on(machineIdIpcChannel, (event, machineId: string) => {
|
||||
this.logService.log('IPC#vscode-machineId');
|
||||
this.storageService.setItem(machineIdStorageKey, machineId);
|
||||
});
|
||||
|
||||
ipc.on('vscode:fetchShellEnv', (event, windowId) => {
|
||||
ipc.on('vscode:fetchShellEnv', (_event: any, windowId: number) => {
|
||||
const { webContents } = BrowserWindow.fromId(windowId);
|
||||
getShellEnvironment().then(shellEnv => {
|
||||
if (!webContents.isDestroyed()) {
|
||||
@@ -215,9 +209,9 @@ export class CodeApplication {
|
||||
});
|
||||
});
|
||||
|
||||
ipc.on('vscode:broadcast', (event, 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.log('IPC#vscode:broadcast', broadcast.channel, broadcast.payload);
|
||||
this.logService.trace('IPC#vscode:broadcast', broadcast.channel, broadcast.payload);
|
||||
|
||||
// Handle specific events on main side
|
||||
this.onBroadcast(broadcast.channel, broadcast.payload);
|
||||
@@ -241,15 +235,15 @@ export class CodeApplication {
|
||||
if (event === 'vscode:changeColorTheme' && typeof payload === 'string') {
|
||||
let data = JSON.parse(payload);
|
||||
|
||||
this.storageService.setItem(CodeWindow.themeStorageKey, data.id);
|
||||
this.storageService.setItem(CodeWindow.themeBackgroundStorageKey, data.background);
|
||||
this.stateService.setItem(CodeWindow.themeStorageKey, data.id);
|
||||
this.stateService.setItem(CodeWindow.themeBackgroundStorageKey, data.background);
|
||||
}
|
||||
}
|
||||
|
||||
public startup(): void {
|
||||
this.logService.log('Starting VS Code in verbose mode');
|
||||
this.logService.log(`from: ${this.environmentService.appRoot}`);
|
||||
this.logService.log('args:', this.environmentService.args);
|
||||
public startup(): TPromise<void> {
|
||||
this.logService.debug('Starting VS Code');
|
||||
this.logService.debug(`from: ${this.environmentService.appRoot}`);
|
||||
this.logService.debug('args:', this.environmentService.args);
|
||||
|
||||
// Make sure we associate the program with the app user model id
|
||||
// This will help Windows to associate the running program with
|
||||
@@ -262,46 +256,62 @@ export class CodeApplication {
|
||||
// Create Electron IPC Server
|
||||
this.electronIpcServer = new ElectronIPCServer();
|
||||
|
||||
// Spawn shared process
|
||||
this.sharedProcess = new SharedProcess(this.environmentService, this.userEnv);
|
||||
this.toDispose.push(this.sharedProcess);
|
||||
this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
|
||||
// Resolve unique machine ID
|
||||
this.logService.trace('Resolving machine identifier...');
|
||||
return this.resolveMachineId().then(machineId => {
|
||||
this.logService.trace(`Resolved machine identifier: ${machineId}`);
|
||||
|
||||
// Services
|
||||
const appInstantiationService = this.initServices();
|
||||
// Spawn shared process
|
||||
this.sharedProcess = new SharedProcess(this.environmentService, machineId, this.userEnv);
|
||||
this.toDispose.push(this.sharedProcess);
|
||||
this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
|
||||
|
||||
// Setup Auth Handler
|
||||
const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
|
||||
this.toDispose.push(authHandler);
|
||||
// Services
|
||||
const appInstantiationService = this.initServices(machineId);
|
||||
|
||||
// Open Windows
|
||||
appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor));
|
||||
// Setup Auth Handler
|
||||
const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
|
||||
this.toDispose.push(authHandler);
|
||||
|
||||
// Post Open Windows Tasks
|
||||
appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
|
||||
// Open Windows
|
||||
appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor));
|
||||
|
||||
// Post Open Windows Tasks
|
||||
appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
|
||||
});
|
||||
}
|
||||
|
||||
private initServices(): IInstantiationService {
|
||||
private resolveMachineId(): TPromise<string> {
|
||||
const machineId = this.stateService.getItem<string>(CodeApplication.MACHINE_ID_KEY);
|
||||
if (machineId) {
|
||||
return TPromise.wrap(machineId);
|
||||
}
|
||||
|
||||
return getMachineId().then(machineId => {
|
||||
|
||||
// Remember in global storage
|
||||
this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId);
|
||||
|
||||
return machineId;
|
||||
});
|
||||
}
|
||||
|
||||
private initServices(machineId: string): IInstantiationService {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
services.set(IUpdateService, new SyncDescriptor(UpdateService));
|
||||
services.set(IWindowsMainService, new SyncDescriptor(WindowsManager));
|
||||
services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, machineId));
|
||||
services.set(IWindowsService, new SyncDescriptor(WindowsService, this.sharedProcess));
|
||||
services.set(ILaunchService, new SyncDescriptor(LaunchService));
|
||||
services.set(ICredentialsService, new SyncDescriptor(CredentialsService));
|
||||
|
||||
// Telemtry
|
||||
if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
|
||||
const channel = getDelayedChannel<ITelemetryAppenderChannel>(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender')));
|
||||
const appender = new TelemetryAppenderClient(channel);
|
||||
const commonProperties = resolveCommonProperties(product.commit, pkg.version, this.environmentService.installSource)
|
||||
// __GDPR__COMMON__ "common.machineId" : { "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" }
|
||||
.then(result => Object.defineProperty(result, 'common.machineId', {
|
||||
get: () => this.storageService.getItem(machineIdStorageKey),
|
||||
enumerable: true
|
||||
}));
|
||||
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);
|
||||
@@ -346,10 +356,6 @@ export class CodeApplication {
|
||||
this.electronIpcServer.registerChannel('windows', windowsChannel);
|
||||
this.sharedProcessClient.done(client => client.registerChannel('windows', windowsChannel));
|
||||
|
||||
const credentialsService = accessor.get(ICredentialsService);
|
||||
const credentialsChannel = new CredentialsChannel(credentialsService);
|
||||
this.electronIpcServer.registerChannel('credentials', credentialsChannel);
|
||||
|
||||
// Lifecycle
|
||||
this.lifecycleService.ready();
|
||||
|
||||
@@ -393,6 +399,7 @@ export class CodeApplication {
|
||||
|
||||
// Ensure Windows foreground love module
|
||||
try {
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
<any>require.__$__nodeRequire('windows-foreground-love');
|
||||
} catch (e) {
|
||||
if (!this.environmentService.isBuilt) {
|
||||
@@ -411,8 +418,8 @@ export class CodeApplication {
|
||||
appInstantiationService.createInstance(CodeMenu);
|
||||
|
||||
// Jump List
|
||||
this.historyService.updateWindowsJumpList();
|
||||
this.historyService.onRecentlyOpenedChange(() => this.historyService.updateWindowsJumpList());
|
||||
this.historyMainService.updateWindowsJumpList();
|
||||
this.historyMainService.onRecentlyOpenedChange(() => this.historyMainService.updateWindowsJumpList());
|
||||
|
||||
// Start shared process here
|
||||
this.sharedProcess.spawn();
|
||||
@@ -420,8 +427,8 @@ export class CodeApplication {
|
||||
// Helps application icon refresh after an update with new icon is installed (macOS)
|
||||
// TODO@Ben remove after a couple of releases
|
||||
if (platform.isMacintosh) {
|
||||
if (!this.storageService.getItem(CodeApplication.APP_ICON_REFRESH_KEY)) {
|
||||
this.storageService.setItem(CodeApplication.APP_ICON_REFRESH_KEY, true);
|
||||
if (!this.stateService.getItem(CodeApplication.APP_ICON_REFRESH_KEY)) {
|
||||
this.stateService.setItem(CodeApplication.APP_ICON_REFRESH_KEY, true);
|
||||
|
||||
// 'exe' => /Applications/Visual Studio Code - Insiders.app/Contents/MacOS/Electron
|
||||
const appPath = dirname(dirname(dirname(app.getPath('exe'))));
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
|
||||
import { fromEventEmitter } from 'vs/base/node/event';
|
||||
import { fromNodeEventEmitter } from 'vs/base/common/event';
|
||||
import { BrowserWindow, app } from 'electron';
|
||||
|
||||
type LoginEvent = {
|
||||
@@ -32,9 +32,9 @@ export class ProxyAuthHandler {
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@IWindowsMainService private windowsService: IWindowsMainService
|
||||
@IWindowsMainService private windowsMainService: IWindowsMainService
|
||||
) {
|
||||
const onLogin = fromEventEmitter<LoginEvent>(app, 'login', (event, webContents, req, authInfo, cb) => ({ event, webContents, req, authInfo, cb }));
|
||||
const onLogin = fromNodeEventEmitter<LoginEvent>(app, 'login', (event, webContents, req, authInfo, cb) => ({ event, webContents, req, authInfo, cb }));
|
||||
onLogin(this.onLogin, this, this.disposables);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export class ProxyAuthHandler {
|
||||
title: 'VS Code'
|
||||
};
|
||||
|
||||
const focusedWindow = this.windowsService.getFocusedWindow();
|
||||
const focusedWindow = this.windowsMainService.getFocusedWindow();
|
||||
|
||||
if (focusedWindow) {
|
||||
opts.parent = focusedWindow.win;
|
||||
|
||||
178
src/vs/code/electron-main/diagnostics.ts
Normal file
178
src/vs/code/electron-main/diagnostics.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { WorkspaceStats, collectWorkspaceStats, collectLaunchConfigs, WorkspaceStatItem } from 'vs/base/node/stats';
|
||||
import { IMainProcessInfo } from 'vs/code/electron-main/launch';
|
||||
import { ProcessItem, listProcesses } from 'vs/base/node/ps';
|
||||
import product from 'vs/platform/node/product';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
import * as os from 'os';
|
||||
import { virtualMachineHint } from 'vs/base/node/id';
|
||||
import { repeat, pad } from 'vs/base/common/strings';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { app } from 'electron';
|
||||
import { basename } from 'path';
|
||||
|
||||
export function printDiagnostics(info: IMainProcessInfo): Promise<any> {
|
||||
return listProcesses(info.mainPID).then(rootProcess => {
|
||||
|
||||
// Environment Info
|
||||
console.log('');
|
||||
console.log(formatEnvironment(info));
|
||||
|
||||
// Process List
|
||||
console.log('');
|
||||
console.log(formatProcessList(info, rootProcess));
|
||||
|
||||
// Workspace Stats
|
||||
if (info.windows.some(window => window.folders && window.folders.length > 0)) {
|
||||
console.log('');
|
||||
console.log('Workspace Stats: ');
|
||||
info.windows.forEach(window => {
|
||||
if (window.folders.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`| Window (${window.title})`);
|
||||
|
||||
window.folders.forEach(folder => {
|
||||
try {
|
||||
const stats = collectWorkspaceStats(folder, ['node_modules', '.git']);
|
||||
let countMessage = `${stats.fileCount} files`;
|
||||
if (stats.maxFilesReached) {
|
||||
countMessage = `more than ${countMessage}`;
|
||||
}
|
||||
console.log(`| Folder (${basename(folder)}): ${countMessage}`);
|
||||
console.log(formatWorkspaceStats(stats));
|
||||
|
||||
const launchConfigs = collectLaunchConfigs(folder);
|
||||
if (launchConfigs.length > 0) {
|
||||
console.log(formatLaunchConfigs(launchConfigs));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`| Error: Unable to collect workpsace stats for folder ${folder} (${error.toString()})`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
console.log('');
|
||||
console.log('');
|
||||
});
|
||||
}
|
||||
|
||||
function formatWorkspaceStats(workspaceStats: WorkspaceStats): string {
|
||||
const output: string[] = [];
|
||||
const lineLength = 60;
|
||||
let col = 0;
|
||||
|
||||
const appendAndWrap = (name: string, count: number) => {
|
||||
const item = ` ${name}(${count})`;
|
||||
|
||||
if (col + item.length > lineLength) {
|
||||
output.push(line);
|
||||
line = '| ';
|
||||
col = line.length;
|
||||
}
|
||||
else {
|
||||
col += item.length;
|
||||
}
|
||||
line += item;
|
||||
};
|
||||
|
||||
|
||||
// File Types
|
||||
let line = '| File types:';
|
||||
const maxShown = 10;
|
||||
let max = workspaceStats.fileTypes.length > maxShown ? maxShown : workspaceStats.fileTypes.length;
|
||||
for (let i = 0; i < max; i++) {
|
||||
const item = workspaceStats.fileTypes[i];
|
||||
appendAndWrap(item.name, item.count);
|
||||
}
|
||||
output.push(line);
|
||||
|
||||
// Conf Files
|
||||
if (workspaceStats.configFiles.length >= 0) {
|
||||
line = '| Conf files:';
|
||||
col = 0;
|
||||
workspaceStats.configFiles.forEach((item) => {
|
||||
appendAndWrap(item.name, item.count);
|
||||
});
|
||||
output.push(line);
|
||||
}
|
||||
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
function formatLaunchConfigs(configs: WorkspaceStatItem[]): string {
|
||||
const output: string[] = [];
|
||||
let line = '| Launch Configs:';
|
||||
configs.forEach(each => {
|
||||
const item = each.count > 1 ? ` ${each.name}(${each.count})` : ` ${each.name}`;
|
||||
line += item;
|
||||
});
|
||||
output.push(line);
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
function formatEnvironment(info: IMainProcessInfo): string {
|
||||
const MB = 1024 * 1024;
|
||||
const GB = 1024 * MB;
|
||||
|
||||
const output: string[] = [];
|
||||
output.push(`Version: ${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`);
|
||||
output.push(`OS Version: ${os.type()} ${os.arch()} ${os.release()})`);
|
||||
const cpus = os.cpus();
|
||||
if (cpus && cpus.length > 0) {
|
||||
output.push(`CPUs: ${cpus[0].model} (${cpus.length} x ${cpus[0].speed})`);
|
||||
}
|
||||
output.push(`Memory (System): ${(os.totalmem() / GB).toFixed(2)}GB (${(os.freemem() / GB).toFixed(2)}GB free)`);
|
||||
if (!isWindows) {
|
||||
output.push(`Load (avg): ${os.loadavg().map(l => Math.round(l)).join(', ')}`); // only provided on Linux/macOS
|
||||
}
|
||||
output.push(`VM: ${Math.round((virtualMachineHint.value() * 100))}%`);
|
||||
output.push(`Screen Reader: ${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`);
|
||||
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
function formatProcessList(info: IMainProcessInfo, rootProcess: ProcessItem): string {
|
||||
const mapPidToWindowTitle = new Map<number, string>();
|
||||
info.windows.forEach(window => mapPidToWindowTitle.set(window.pid, window.title));
|
||||
|
||||
const output: string[] = [];
|
||||
|
||||
output.push('CPU %\tMem MB\tProcess');
|
||||
|
||||
formatProcessItem(mapPidToWindowTitle, output, rootProcess, 0);
|
||||
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
function formatProcessItem(mapPidToWindowTitle: Map<number, string>, output: string[], item: ProcessItem, indent: number): void {
|
||||
const isRoot = (indent === 0);
|
||||
|
||||
const MB = 1024 * 1024;
|
||||
|
||||
// Format name with indent
|
||||
let name: string;
|
||||
if (isRoot) {
|
||||
name = `${product.applicationName} main`;
|
||||
} else {
|
||||
name = `${repeat(' ', indent)} ${item.name}`;
|
||||
|
||||
if (item.name === 'window') {
|
||||
name = `${name} (${mapPidToWindowTitle.get(item.pid)})`;
|
||||
}
|
||||
}
|
||||
const memory = process.platform === 'win32' ? item.mem : (os.totalmem() * (item.mem / 100));
|
||||
output.push(`${pad(Number(item.load.toFixed(0)), 5, ' ')}\t${pad(Number((memory / MB).toFixed(0)), 6, ' ')}\t${name}`);
|
||||
|
||||
// Recurse into children if any
|
||||
if (Array.isArray(item.children)) {
|
||||
item.children.forEach(child => formatProcessItem(mapPidToWindowTitle, output, child, indent + 1));
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import * as nativeKeymap from 'native-keymap';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IStorageService } from 'vs/platform/storage/node/storage';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import Event, { Emitter, once } from 'vs/base/common/event';
|
||||
import { ConfigWatcher } from 'vs/base/node/config';
|
||||
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
|
||||
@@ -48,7 +48,7 @@ export interface IKeybinding {
|
||||
|
||||
export class KeybindingsResolver {
|
||||
|
||||
private static lastKnownKeybindingsMapStorageKey = 'lastKnownKeybindings';
|
||||
private static readonly lastKnownKeybindingsMapStorageKey = 'lastKnownKeybindings';
|
||||
|
||||
private commandIds: Set<string>;
|
||||
private keybindings: { [commandId: string]: IKeybinding };
|
||||
@@ -58,13 +58,13 @@ export class KeybindingsResolver {
|
||||
onKeybindingsChanged: Event<void> = this._onKeybindingsChanged.event;
|
||||
|
||||
constructor(
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@IStateService private stateService: IStateService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IWindowsMainService private windowsService: IWindowsMainService,
|
||||
@IWindowsMainService private windowsMainService: IWindowsMainService,
|
||||
@ILogService private logService: ILogService
|
||||
) {
|
||||
this.commandIds = new Set<string>();
|
||||
this.keybindings = this.storageService.getItem<{ [id: string]: string; }>(KeybindingsResolver.lastKnownKeybindingsMapStorageKey) || Object.create(null);
|
||||
this.keybindings = this.stateService.getItem<{ [id: string]: string; }>(KeybindingsResolver.lastKnownKeybindingsMapStorageKey) || Object.create(null);
|
||||
this.keybindingsWatcher = new ConfigWatcher<IUserFriendlyKeybinding[]>(environmentService.appKeybindingsPath, { changeBufferDelay: 100, onError: error => this.logService.error(error) });
|
||||
|
||||
this.registerListeners();
|
||||
@@ -102,24 +102,24 @@ export class KeybindingsResolver {
|
||||
|
||||
if (keybindingsChanged) {
|
||||
this.keybindings = resolvedKeybindings;
|
||||
this.storageService.setItem(KeybindingsResolver.lastKnownKeybindingsMapStorageKey, this.keybindings); // keep to restore instantly after restart
|
||||
this.stateService.setItem(KeybindingsResolver.lastKnownKeybindingsMapStorageKey, this.keybindings); // keep to restore instantly after restart
|
||||
|
||||
this._onKeybindingsChanged.fire();
|
||||
}
|
||||
});
|
||||
|
||||
// Resolve keybindings when any first window is loaded
|
||||
const onceOnWindowReady = once(this.windowsService.onWindowReady);
|
||||
const onceOnWindowReady = once(this.windowsMainService.onWindowReady);
|
||||
onceOnWindowReady(win => this.resolveKeybindings(win));
|
||||
|
||||
// Resolve keybindings again when keybindings.json changes
|
||||
this.keybindingsWatcher.onDidUpdateConfiguration(() => this.resolveKeybindings());
|
||||
|
||||
// Resolve keybindings when window reloads because an installed extension could have an impact
|
||||
this.windowsService.onWindowReload(() => this.resolveKeybindings());
|
||||
this.windowsMainService.onWindowReload(() => this.resolveKeybindings());
|
||||
}
|
||||
|
||||
private resolveKeybindings(win = this.windowsService.getLastActiveWindow()): void {
|
||||
private resolveKeybindings(win = this.windowsMainService.getLastActiveWindow()): void {
|
||||
if (this.commandIds.size && win) {
|
||||
const commandIds: string[] = [];
|
||||
this.commandIds.forEach(id => commandIds.push(id));
|
||||
|
||||
@@ -15,6 +15,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import { OpenContext } 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';
|
||||
|
||||
export const ID = 'launchService';
|
||||
export const ILaunchService = createDecorator<ILaunchService>(ID);
|
||||
@@ -24,15 +25,28 @@ export interface IStartArguments {
|
||||
userEnv: IProcessEnvironment;
|
||||
}
|
||||
|
||||
export interface IWindowInfo {
|
||||
pid: number;
|
||||
title: string;
|
||||
folders: string[];
|
||||
}
|
||||
|
||||
export interface IMainProcessInfo {
|
||||
mainPID: number;
|
||||
windows: IWindowInfo[];
|
||||
}
|
||||
|
||||
export interface ILaunchService {
|
||||
_serviceBrand: any;
|
||||
start(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise<void>;
|
||||
getMainProcessId(): TPromise<number>;
|
||||
getMainProcessInfo(): TPromise<IMainProcessInfo>;
|
||||
}
|
||||
|
||||
export interface ILaunchChannel extends IChannel {
|
||||
call(command: 'start', arg: IStartArguments): TPromise<void>;
|
||||
call(command: 'get-main-process-id', arg: null): TPromise<any>;
|
||||
call(command: 'get-main-process-info', arg: null): TPromise<any>;
|
||||
call(command: string, arg: any): TPromise<any>;
|
||||
}
|
||||
|
||||
@@ -48,6 +62,9 @@ export class LaunchChannel implements ILaunchChannel {
|
||||
|
||||
case 'get-main-process-id':
|
||||
return this.service.getMainProcessId();
|
||||
|
||||
case 'get-main-process-info':
|
||||
return this.service.getMainProcessInfo();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -67,6 +84,10 @@ export class LaunchChannelClient implements ILaunchService {
|
||||
public getMainProcessId(): TPromise<number> {
|
||||
return this.channel.call('get-main-process-id', null);
|
||||
}
|
||||
|
||||
public getMainProcessInfo(): TPromise<IMainProcessInfo> {
|
||||
return this.channel.call('get-main-process-info', null);
|
||||
}
|
||||
}
|
||||
|
||||
export class LaunchService implements ILaunchService {
|
||||
@@ -75,18 +96,18 @@ export class LaunchService implements ILaunchService {
|
||||
|
||||
constructor(
|
||||
@ILogService private logService: ILogService,
|
||||
@IWindowsMainService private windowsService: IWindowsMainService,
|
||||
@IURLService private urlService: IURLService
|
||||
@IWindowsMainService private windowsMainService: IWindowsMainService,
|
||||
@IURLService private urlService: IURLService,
|
||||
@IWorkspacesMainService private workspacesMainService: IWorkspacesMainService
|
||||
) { }
|
||||
|
||||
public start(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise<void> {
|
||||
this.logService.log('Received data from other instance: ', args, userEnv);
|
||||
this.logService.trace('Received data from other instance: ', args, userEnv);
|
||||
|
||||
// Check early for open-url which is handled in URL service
|
||||
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));
|
||||
const openUrl = (args['open-url'] ? args._urls : []) || [];
|
||||
if (openUrl.length > 0) {
|
||||
openUrl.forEach(url => this.urlService.open(url));
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
@@ -95,13 +116,13 @@ export class LaunchService implements ILaunchService {
|
||||
const context = !!userEnv['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
|
||||
let usedWindows: ICodeWindow[];
|
||||
if (!!args.extensionDevelopmentPath) {
|
||||
this.windowsService.openExtensionDevelopmentHostWindow({ context, cli: args, userEnv });
|
||||
this.windowsMainService.openExtensionDevelopmentHostWindow({ context, cli: args, userEnv });
|
||||
} else if (args._.length === 0 && (args['new-window'] || args['unity-launch'])) {
|
||||
usedWindows = this.windowsService.open({ context, cli: args, userEnv, forceNewWindow: true, forceEmpty: true });
|
||||
usedWindows = this.windowsMainService.open({ context, cli: args, userEnv, forceNewWindow: true, forceEmpty: true });
|
||||
} else if (args._.length === 0) {
|
||||
usedWindows = [this.windowsService.focusLastActive(args, context)];
|
||||
usedWindows = [this.windowsMainService.focusLastActive(args, context)];
|
||||
} else {
|
||||
usedWindows = this.windowsService.open({
|
||||
usedWindows = this.windowsMainService.open({
|
||||
context,
|
||||
cli: args,
|
||||
userEnv,
|
||||
@@ -118,7 +139,7 @@ export class LaunchService implements ILaunchService {
|
||||
// In addition, we poll for the wait marker file to be deleted to return.
|
||||
if (args.wait && usedWindows.length === 1 && usedWindows[0]) {
|
||||
return TPromise.any([
|
||||
this.windowsService.waitForWindowCloseOrLoad(usedWindows[0].id),
|
||||
this.windowsMainService.waitForWindowCloseOrLoad(usedWindows[0].id),
|
||||
whenDeleted(args.waitMarkerFilePath)
|
||||
]).then(() => void 0, () => void 0);
|
||||
}
|
||||
@@ -127,8 +148,40 @@ export class LaunchService implements ILaunchService {
|
||||
}
|
||||
|
||||
public getMainProcessId(): TPromise<number> {
|
||||
this.logService.log('Received request for process ID from other instance.');
|
||||
this.logService.trace('Received request for process ID from other instance.');
|
||||
|
||||
return TPromise.as(process.pid);
|
||||
}
|
||||
|
||||
public getMainProcessInfo(): TPromise<IMainProcessInfo> {
|
||||
this.logService.trace('Received request for main process info from other instance.');
|
||||
|
||||
return TPromise.wrap({
|
||||
mainPID: process.pid,
|
||||
windows: this.windowsMainService.getWindows().map(window => {
|
||||
return this.getWindowInfo(window);
|
||||
})
|
||||
} as IMainProcessInfo);
|
||||
}
|
||||
|
||||
private getWindowInfo(window: ICodeWindow): IWindowInfo {
|
||||
const folders: string[] = [];
|
||||
|
||||
if (window.openedFolderPath) {
|
||||
folders.push(window.openedFolderPath);
|
||||
} else if (window.openedWorkspace) {
|
||||
const rootFolders = this.workspacesMainService.resolveWorkspaceSync(window.openedWorkspace.configPath).folders;
|
||||
rootFolders.forEach(root => {
|
||||
if (root.uri.scheme === 'file') {
|
||||
folders.push(root.uri.fsPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
pid: window.win.webContents.getOSProcessId(),
|
||||
title: window.win.getTitle(),
|
||||
folders
|
||||
} as IWindowInfo;
|
||||
}
|
||||
}
|
||||
@@ -5,22 +5,25 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { app } from 'electron';
|
||||
import { app, dialog } from 'electron';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import product from 'vs/platform/node/product';
|
||||
import * as path from 'path';
|
||||
import { parseMainProcessArgv } from 'vs/platform/environment/node/argv';
|
||||
import { mkdirp } from 'vs/base/node/pfs';
|
||||
import { mkdirp, readdir, rimraf } from 'vs/base/node/pfs';
|
||||
import { validatePaths } from 'vs/code/node/paths';
|
||||
import { LifecycleService, ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ILaunchChannel, LaunchChannelClient } from './launch';
|
||||
import { ILaunchChannel, LaunchChannelClient } from 'vs/code/electron-main/launch';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { ILogService, LogMainService } from 'vs/platform/log/common/log';
|
||||
import { IStorageService, StorageService } from 'vs/platform/storage/node/storage';
|
||||
import { ILogService, ConsoleLogMainService, MultiplexLogService } from 'vs/platform/log/common/log';
|
||||
import { StateService } from 'vs/platform/state/node/stateService';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import { IBackupMainService } from 'vs/platform/backup/common/backup';
|
||||
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
|
||||
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
@@ -37,16 +40,30 @@ import { HistoryMainService } from 'vs/platform/history/electron-main/historyMai
|
||||
import { IHistoryMainService } from 'vs/platform/history/common/history';
|
||||
import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
|
||||
import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { localize } from 'vs/nls';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { createLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { printDiagnostics } from 'vs/code/electron-main/diagnostics';
|
||||
|
||||
function createServices(args: ParsedArgs): IInstantiationService {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService, args, process.execPath));
|
||||
services.set(ILogService, new SyncDescriptor(LogMainService));
|
||||
const environmentService = new EnvironmentService(args, process.execPath);
|
||||
const spdlogService = createLogService('main', environmentService);
|
||||
const consoleLogService = new ConsoleLogMainService(environmentService);
|
||||
const logService = new MultiplexLogService([consoleLogService, spdlogService]);
|
||||
|
||||
process.once('exit', () => logService.dispose());
|
||||
|
||||
// Eventually cleanup
|
||||
setTimeout(() => cleanupOlderLogs(environmentService).then(null, err => console.error(err)), 10000);
|
||||
|
||||
services.set(IEnvironmentService, environmentService);
|
||||
services.set(ILogService, logService);
|
||||
services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService));
|
||||
services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService));
|
||||
services.set(ILifecycleService, new SyncDescriptor(LifecycleService));
|
||||
services.set(IStorageService, new SyncDescriptor(StorageService));
|
||||
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 : []));
|
||||
@@ -55,12 +72,28 @@ function createServices(args: ParsedArgs): IInstantiationService {
|
||||
return new InstantiationService(services, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up older logs, while keeping the 10 most recent ones.
|
||||
*/
|
||||
async function cleanupOlderLogs(environmentService: EnvironmentService): TPromise<void> {
|
||||
const currentLog = path.basename(environmentService.logsPath);
|
||||
const logsRoot = path.dirname(environmentService.logsPath);
|
||||
const children = await readdir(logsRoot);
|
||||
const allSessions = children.filter(name => /^\d{8}T\d{6}$/.test(name));
|
||||
const oldSessions = allSessions.sort().filter((d, i) => d !== currentLog);
|
||||
const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 9));
|
||||
|
||||
await TPromise.join(toDelete.map(name => rimraf(path.join(logsRoot, name))));
|
||||
}
|
||||
|
||||
function createPaths(environmentService: IEnvironmentService): TPromise<any> {
|
||||
const paths = [
|
||||
environmentService.appSettingsHome,
|
||||
environmentService.extensionsPath,
|
||||
environmentService.nodeCachedDataDir
|
||||
environmentService.nodeCachedDataDir,
|
||||
environmentService.logsPath
|
||||
];
|
||||
|
||||
return TPromise.join(paths.map(p => p && mkdirp(p))) as TPromise<any>;
|
||||
}
|
||||
|
||||
@@ -73,11 +106,11 @@ function setupIPC(accessor: ServicesAccessor): TPromise<Server> {
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
|
||||
function allowSetForegroundWindow(service: LaunchChannelClient): TPromise<void> {
|
||||
let promise = TPromise.as<void>(void 0);
|
||||
let promise = TPromise.wrap<void>(void 0);
|
||||
if (platform.isWindows) {
|
||||
promise = service.getMainProcessId()
|
||||
.then(processId => {
|
||||
logService.log('Sending some foreground love to the running instance:', processId);
|
||||
logService.trace('Sending some foreground love to the running instance:', processId);
|
||||
|
||||
try {
|
||||
const { allowSetForegroundWindow } = <any>require.__$__nodeRequire('windows-foreground-love');
|
||||
@@ -93,10 +126,22 @@ function setupIPC(accessor: ServicesAccessor): TPromise<Server> {
|
||||
|
||||
function setup(retry: boolean): TPromise<Server> {
|
||||
return serve(environmentService.mainIPCHandle).then(server => {
|
||||
if (platform.isMacintosh) {
|
||||
app.dock.show(); // dock might be hidden at this case due to a retry
|
||||
|
||||
// Print --status usage info
|
||||
if (environmentService.args.status) {
|
||||
logService.warn('Warning: The --status argument can only be used if Code is already running. Please run it again after Code has started.');
|
||||
throw new ExpectedError('Terminating...');
|
||||
}
|
||||
|
||||
// dock might be hidden at this case due to a retry
|
||||
if (platform.isMacintosh) {
|
||||
app.dock.show();
|
||||
}
|
||||
|
||||
// Set the VSCODE_PID variable here when we are sure we are the first
|
||||
// instance to startup. Otherwise we would wrongly overwrite the PID
|
||||
process.env['VSCODE_PID'] = String(process.pid);
|
||||
|
||||
return server;
|
||||
}, err => {
|
||||
if (err.code !== 'EADDRINUSE') {
|
||||
@@ -121,18 +166,53 @@ function setupIPC(accessor: ServicesAccessor): TPromise<Server> {
|
||||
return TPromise.wrapError<Server>(new Error(msg));
|
||||
}
|
||||
|
||||
logService.log('Sending env to running instance...');
|
||||
// Show a warning dialog after some timeout if it takes long to talk to the other instance
|
||||
// Skip this if we are running with --wait where it is expected that we wait for a while.
|
||||
// Also skip when gathering diagnostics (--status) which can take a longer time.
|
||||
let startupWarningDialogHandle: number;
|
||||
if (!environmentService.wait && !environmentService.status) {
|
||||
startupWarningDialogHandle = setTimeout(() => {
|
||||
showStartupWarningDialog(
|
||||
localize('secondInstanceNoResponse', "Another instance of {0} is running but not responding", product.nameShort),
|
||||
localize('secondInstanceNoResponseDetail', "Please close all other instances and try again.")
|
||||
);
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
const channel = client.getChannel<ILaunchChannel>('launch');
|
||||
const service = new LaunchChannelClient(channel);
|
||||
|
||||
// Process Info
|
||||
if (environmentService.args.status) {
|
||||
return service.getMainProcessInfo().then(info => {
|
||||
return printDiagnostics(info).then(() => TPromise.wrapError(new ExpectedError()));
|
||||
});
|
||||
}
|
||||
|
||||
logService.trace('Sending env to running instance...');
|
||||
|
||||
return allowSetForegroundWindow(service)
|
||||
.then(() => service.start(environmentService.args, process.env))
|
||||
.then(() => client.dispose())
|
||||
.then(() => TPromise.wrapError(new ExpectedError('Sent env to running instance. Terminating...')));
|
||||
.then(() => {
|
||||
|
||||
// Now that we started, make sure the warning dialog is prevented
|
||||
if (startupWarningDialogHandle) {
|
||||
clearTimeout(startupWarningDialogHandle);
|
||||
}
|
||||
|
||||
return TPromise.wrapError(new ExpectedError('Sent env to running instance. Terminating...'));
|
||||
});
|
||||
},
|
||||
err => {
|
||||
if (!retry || platform.isWindows || err.code !== 'ECONNREFUSED') {
|
||||
if (err.code === 'EPERM') {
|
||||
showStartupWarningDialog(
|
||||
localize('secondInstanceAdmin', "A second instance of {0} is already running as administrator.", product.nameShort),
|
||||
localize('secondInstanceAdminDetail', "Please close the other instance and try again.")
|
||||
);
|
||||
}
|
||||
|
||||
return TPromise.wrapError<Server>(err);
|
||||
}
|
||||
|
||||
@@ -142,7 +222,7 @@ function setupIPC(accessor: ServicesAccessor): TPromise<Server> {
|
||||
try {
|
||||
fs.unlinkSync(environmentService.mainIPCHandle);
|
||||
} catch (e) {
|
||||
logService.log('Fatal error deleting obsolete instance handle', e);
|
||||
logService.warn('Could not delete obsolete instance handle', e);
|
||||
return TPromise.wrapError<Server>(e);
|
||||
}
|
||||
|
||||
@@ -155,6 +235,17 @@ function setupIPC(accessor: ServicesAccessor): TPromise<Server> {
|
||||
return setup(true);
|
||||
}
|
||||
|
||||
function showStartupWarningDialog(message: string, detail: string): void {
|
||||
dialog.showMessageBox(null, {
|
||||
title: product.nameLong,
|
||||
type: 'warning',
|
||||
buttons: [mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))],
|
||||
message,
|
||||
detail,
|
||||
noLink: true
|
||||
});
|
||||
}
|
||||
|
||||
function quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void {
|
||||
const logService = accessor.get(ILogService);
|
||||
const lifecycleService = accessor.get(ILifecycleService);
|
||||
@@ -163,14 +254,16 @@ function quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void
|
||||
|
||||
if (reason) {
|
||||
if ((reason as ExpectedError).isExpected) {
|
||||
logService.log(reason.message);
|
||||
if (reason.message) {
|
||||
logService.trace(reason.message);
|
||||
}
|
||||
} else {
|
||||
exitCode = 1; // signal error to the outside
|
||||
|
||||
if (reason.stack) {
|
||||
console.error(reason.stack);
|
||||
logService.error(reason.stack);
|
||||
} else {
|
||||
console.error(`Startup error: ${reason.toString()}`);
|
||||
logService.error(`Startup error: ${reason.toString()}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,19 +291,16 @@ function main() {
|
||||
// Patch `process.env` with the instance's environment
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
const instanceEnv: typeof process.env = {
|
||||
VSCODE_PID: String(process.pid),
|
||||
VSCODE_IPC_HOOK: environmentService.mainIPCHandle,
|
||||
VSCODE_NLS_CONFIG: process.env['VSCODE_NLS_CONFIG']
|
||||
VSCODE_NLS_CONFIG: process.env['VSCODE_NLS_CONFIG'],
|
||||
VSCODE_LOGS: process.env['VSCODE_LOGS']
|
||||
};
|
||||
assign(process.env, instanceEnv);
|
||||
|
||||
// Startup
|
||||
return instantiationService.invokeFunction(a => createPaths(a.get(IEnvironmentService)))
|
||||
.then(() => instantiationService.invokeFunction(setupIPC))
|
||||
.then(mainIpcServer => {
|
||||
const app = instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnv);
|
||||
app.startup();
|
||||
});
|
||||
.then(mainIpcServer => instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnv).startup());
|
||||
}).done(null, err => instantiationService.invokeFunction(quit, err));
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as nls from 'vs/nls';
|
||||
import { isMacintosh, isLinux, isWindows, language } from 'vs/base/common/platform';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem, BrowserWindow } from 'electron';
|
||||
import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem, BrowserWindow, clipboard } from 'electron';
|
||||
import { OpenContext, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { AutoSaveConfiguration } from 'vs/platform/files/common/files';
|
||||
@@ -18,11 +18,11 @@ import { IUpdateService, State as UpdateState } from 'vs/platform/update/common/
|
||||
import product from 'vs/platform/node/product';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { mnemonicMenuLabel as baseMnemonicLabel, unmnemonicLabel, getPathLabel } from 'vs/base/common/labels';
|
||||
import { mnemonicMenuLabel as baseMnemonicLabel, unmnemonicLabel, getPathLabel, mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { KeybindingsResolver } from 'vs/code/electron-main/keyboard';
|
||||
import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IHistoryMainService } from 'vs/platform/history/common/history';
|
||||
import { IWorkspaceIdentifier, IWorkspacesMainService, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
|
||||
interface IExtensionViewlet {
|
||||
id: string;
|
||||
@@ -38,7 +38,7 @@ const telemetryFrom = 'menu';
|
||||
|
||||
export class CodeMenu {
|
||||
|
||||
private static MAX_MENU_RECENT_ENTRIES = 10;
|
||||
private static readonly MAX_MENU_RECENT_ENTRIES = 10;
|
||||
|
||||
private keys = [
|
||||
'files.autoSave',
|
||||
@@ -68,11 +68,10 @@ export class CodeMenu {
|
||||
@IUpdateService private updateService: IUpdateService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IWindowsMainService private windowsService: IWindowsMainService,
|
||||
@IWindowsMainService private windowsMainService: IWindowsMainService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IHistoryMainService private historyService: IHistoryMainService,
|
||||
@IWorkspacesMainService private workspacesService: IWorkspacesMainService
|
||||
@IHistoryMainService private historyMainService: IHistoryMainService
|
||||
) {
|
||||
this.extensionViewlets = [];
|
||||
this.nativeTabMenuItems = [];
|
||||
@@ -93,14 +92,14 @@ export class CodeMenu {
|
||||
});
|
||||
|
||||
// Listen to some events from window service to update menu
|
||||
this.historyService.onRecentlyOpenedChange(() => this.updateMenu());
|
||||
this.windowsService.onWindowsCountChanged(e => this.onWindowsCountChanged(e));
|
||||
this.windowsService.onActiveWindowChanged(() => this.updateWorkspaceMenuItems());
|
||||
this.windowsService.onWindowReady(() => this.updateWorkspaceMenuItems());
|
||||
this.windowsService.onWindowClose(() => this.updateWorkspaceMenuItems());
|
||||
this.historyMainService.onRecentlyOpenedChange(() => this.updateMenu());
|
||||
this.windowsMainService.onWindowsCountChanged(e => this.onWindowsCountChanged(e));
|
||||
this.windowsMainService.onActiveWindowChanged(() => this.updateWorkspaceMenuItems());
|
||||
this.windowsMainService.onWindowReady(() => this.updateWorkspaceMenuItems());
|
||||
this.windowsMainService.onWindowClose(() => this.updateWorkspaceMenuItems());
|
||||
|
||||
// Listen to extension viewlets
|
||||
ipc.on('vscode:extensionViewlets', (event, rawExtensionViewlets) => {
|
||||
ipc.on('vscode:extensionViewlets', (_event: any, rawExtensionViewlets: string) => {
|
||||
let extensionViewlets: IExtensionViewlet[] = [];
|
||||
try {
|
||||
extensionViewlets = JSON.parse(rawExtensionViewlets);
|
||||
@@ -215,7 +214,7 @@ export class CodeMenu {
|
||||
}
|
||||
|
||||
private updateWorkspaceMenuItems(): void {
|
||||
const window = this.windowsService.getLastActiveWindow();
|
||||
const window = this.windowsMainService.getLastActiveWindow();
|
||||
const isInWorkspaceContext = window && !!window.openedWorkspace;
|
||||
const isInFolderContext = window && !!window.openedFolderPath;
|
||||
|
||||
@@ -258,10 +257,11 @@ export class CodeMenu {
|
||||
const viewMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mView', comment: ['&& denotes a mnemonic'] }, "&&View")), submenu: viewMenu });
|
||||
this.setViewMenu(viewMenu);
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Goto
|
||||
const gotoMenu = new Menu();
|
||||
const gotoMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go")), submenu: gotoMenu });
|
||||
this.setGotoMenu(gotoMenu);
|
||||
// const gotoMenu = new Menu();
|
||||
// const gotoMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go")), submenu: gotoMenu });
|
||||
// this.setGotoMenu(gotoMenu);
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Debug
|
||||
@@ -298,8 +298,8 @@ export class CodeMenu {
|
||||
// {{SQL CARBON EDIT}}
|
||||
//menubar.append(selectionMenuItem);
|
||||
menubar.append(viewMenuItem);
|
||||
menubar.append(gotoMenuItem);
|
||||
// {{SQL CARBON EDIT}}
|
||||
//menubar.append(gotoMenuItem);
|
||||
// menubar.append(debugMenuItem);
|
||||
// menubar.append(taskMenuItem);
|
||||
|
||||
@@ -316,7 +316,7 @@ export class CodeMenu {
|
||||
this.appMenuInstalled = true;
|
||||
|
||||
const dockMenu = new Menu();
|
||||
dockMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.DOCK) }));
|
||||
dockMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsMainService.openNewWindow(OpenContext.DOCK) }));
|
||||
|
||||
app.dock.setMenu(dockMenu);
|
||||
}
|
||||
@@ -331,7 +331,13 @@ export class CodeMenu {
|
||||
const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' });
|
||||
const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideothers', accelerator: 'Command+Alt+H' });
|
||||
const showAll = new MenuItem({ label: nls.localize('mShowAll', "Show All"), role: 'unhide' });
|
||||
const quit = new MenuItem(this.likeAction('workbench.action.quit', { label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => this.windowsService.quit() }));
|
||||
const quit = new MenuItem(this.likeAction('workbench.action.quit', {
|
||||
label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => {
|
||||
if (this.windowsMainService.getWindowCount() === 0 || !!this.windowsMainService.getFocusedWindow()) {
|
||||
this.windowsMainService.quit(); // fix for https://github.com/Microsoft/vscode/issues/39191
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
const actions = [about];
|
||||
actions.push(...checkForUpdates);
|
||||
@@ -352,40 +358,40 @@ export class CodeMenu {
|
||||
}
|
||||
|
||||
private setFileMenu(fileMenu: Electron.Menu): void {
|
||||
const hasNoWindows = (this.windowsService.getWindowCount() === 0);
|
||||
const hasNoWindows = (this.windowsMainService.getWindowCount() === 0);
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
let newFile: Electron.MenuItem;
|
||||
if (hasNoWindows) {
|
||||
newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: this.mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New Query")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) }));
|
||||
newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: this.mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New Query")), click: () => this.windowsMainService.openNewWindow(OpenContext.MENU) }));
|
||||
} else {
|
||||
newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New Query"), 'workbench.action.files.newUntitledFile');
|
||||
}
|
||||
|
||||
let open: Electron.MenuItem;
|
||||
if (hasNoWindows) {
|
||||
open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsService.pickFileFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) }));
|
||||
open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsMainService.pickFileFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) }));
|
||||
} else {
|
||||
open = this.createMenuItem(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open..."), ['workbench.action.files.openFileFolder', 'workbench.action.files.openFileFolderInNewWindow']);
|
||||
}
|
||||
|
||||
let openWorkspace: Electron.MenuItem;
|
||||
if (hasNoWindows) {
|
||||
openWorkspace = new MenuItem(this.likeAction('workbench.action.openWorkspace', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open Workspace...")), click: (menuItem, win, event) => this.windowsService.pickWorkspaceAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) }));
|
||||
openWorkspace = new MenuItem(this.likeAction('workbench.action.openWorkspace', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...")), click: (menuItem, win, event) => this.windowsMainService.pickWorkspaceAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) }));
|
||||
} else {
|
||||
openWorkspace = this.createMenuItem(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open Workspace..."), ['workbench.action.openWorkspace', 'workbench.action.openWorkspaceInNewWindow']);
|
||||
openWorkspace = this.createMenuItem(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace..."), ['workbench.action.openWorkspace', 'workbench.action.openWorkspaceInNewWindow']);
|
||||
}
|
||||
|
||||
let openFolder: Electron.MenuItem;
|
||||
if (hasNoWindows) {
|
||||
openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsService.pickFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) }));
|
||||
openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsMainService.pickFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) }));
|
||||
} else {
|
||||
openFolder = this.createMenuItem(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder..."), ['workbench.action.files.openFolder', 'workbench.action.files.openFolderInNewWindow']);
|
||||
}
|
||||
|
||||
let openFile: Electron.MenuItem;
|
||||
if (hasNoWindows) {
|
||||
openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: (menuItem, win, event) => this.windowsService.pickFileAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) }));
|
||||
openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: (menuItem, win, event) => this.windowsMainService.pickFileAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) }));
|
||||
} else {
|
||||
openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), ['workbench.action.files.openFile', 'workbench.action.files.openFileInNewWindow']);
|
||||
}
|
||||
@@ -394,29 +400,28 @@ export class CodeMenu {
|
||||
this.setOpenRecentMenu(openRecentMenu);
|
||||
const openRecent = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent")), submenu: openRecentMenu, enabled: openRecentMenu.items.length > 0 });
|
||||
|
||||
const saveWorkspaceAs = this.createMenuItem(nls.localize({ key: 'miSaveWorkspaceAs', comment: ['&& denotes a mnemonic'] }, "Sa&&ve Workspace As..."), 'workbench.action.saveWorkspaceAs');
|
||||
const addFolder = this.createMenuItem(nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Add Folder to Workspace..."), 'workbench.action.addRootFolder');
|
||||
const saveWorkspaceAs = this.createMenuItem(nls.localize('miSaveWorkspaceAs', "Save Workspace As..."), 'workbench.action.saveWorkspaceAs');
|
||||
const addFolder = this.createMenuItem(nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace..."), 'workbench.action.addRootFolder');
|
||||
|
||||
const saveFile = this.createMenuItem(nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save"), 'workbench.action.files.save');
|
||||
const saveFileAs = this.createMenuItem(nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As..."), 'workbench.action.files.saveAs');
|
||||
const saveAllFiles = this.createMenuItem(nls.localize({ key: 'miSaveAll', comment: ['&& denotes a mnemonic'] }, "Save A&&ll"), 'workbench.action.files.saveAll');
|
||||
|
||||
const autoSaveEnabled = [AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE].some(s => this.currentAutoSaveSetting === s);
|
||||
const autoSave = new MenuItem(this.likeAction('vscode.toggleAutoSave', { label: this.mnemonicLabel(nls.localize('miAutoSave', "Auto Save")), type: 'checkbox', checked: autoSaveEnabled, enabled: this.windowsService.getWindowCount() > 0, click: () => this.windowsService.sendToFocused('vscode.toggleAutoSave') }, false));
|
||||
const installVsixExtension = this.createMenuItem(nls.localize({ key: 'miinstallVsix', comment: ['&& denotes a mnemonic'] }, "Install Extension from VSIX Package"), 'workbench.extensions.action.installVSIX');
|
||||
const autoSave = new MenuItem(this.likeAction('vscode.toggleAutoSave', { label: this.mnemonicLabel(nls.localize('miAutoSave', "Auto Save")), type: 'checkbox', checked: autoSaveEnabled, enabled: this.windowsMainService.getWindowCount() > 0, click: () => this.windowsMainService.sendToFocused('vscode.toggleAutoSave') }, false));
|
||||
|
||||
const preferences = this.getPreferencesMenu();
|
||||
|
||||
const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) }));
|
||||
const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsMainService.openNewWindow(OpenContext.MENU) }));
|
||||
const revertFile = this.createMenuItem(nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), 'workbench.action.files.revert');
|
||||
const closeWindow = new MenuItem(this.likeAction('workbench.action.closeWindow', { label: this.mnemonicLabel(nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window")), click: () => this.windowsService.getLastActiveWindow().win.close(), enabled: this.windowsService.getWindowCount() > 0 }));
|
||||
const closeWindow = new MenuItem(this.likeAction('workbench.action.closeWindow', { label: this.mnemonicLabel(nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window")), click: () => this.windowsMainService.getLastActiveWindow().win.close(), enabled: this.windowsMainService.getWindowCount() > 0 }));
|
||||
|
||||
this.closeWorkspace = this.createMenuItem(nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace"), 'workbench.action.closeFolder');
|
||||
this.closeFolder = this.createMenuItem(nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), 'workbench.action.closeFolder');
|
||||
|
||||
const closeEditor = this.createMenuItem(nls.localize({ key: 'miCloseEditor', comment: ['&& denotes a mnemonic'] }, "&&Close Editor"), 'workbench.action.closeActiveEditor');
|
||||
|
||||
const exit = new MenuItem(this.likeAction('workbench.action.quit', { label: this.mnemonicLabel(nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit")), click: () => this.windowsService.quit() }));
|
||||
const exit = new MenuItem(this.likeAction('workbench.action.quit', { label: this.mnemonicLabel(nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit")), click: () => this.windowsMainService.quit() }));
|
||||
|
||||
this.updateWorkspaceMenuItems();
|
||||
|
||||
@@ -439,8 +444,6 @@ export class CodeMenu {
|
||||
__separator__(),
|
||||
autoSave,
|
||||
__separator__(),
|
||||
installVsixExtension,
|
||||
__separator__(),
|
||||
!isMacintosh ? preferences : null,
|
||||
!isMacintosh ? __separator__() : null,
|
||||
revertFile,
|
||||
@@ -480,7 +483,7 @@ export class CodeMenu {
|
||||
private setOpenRecentMenu(openRecentMenu: Electron.Menu): void {
|
||||
openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), 'workbench.action.reopenClosedEditor'));
|
||||
|
||||
const { workspaces, files } = this.historyService.getRecentlyOpened();
|
||||
const { workspaces, files } = this.historyMainService.getRecentlyOpened();
|
||||
|
||||
// Workspaces
|
||||
if (workspaces.length > 0) {
|
||||
@@ -504,7 +507,7 @@ export class CodeMenu {
|
||||
openRecentMenu.append(__separator__());
|
||||
openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More..."), 'workbench.action.openRecent'));
|
||||
openRecentMenu.append(__separator__());
|
||||
openRecentMenu.append(new MenuItem(this.likeAction('workbench.action.clearRecentFiles', { label: this.mnemonicLabel(nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened")), click: () => this.historyService.clearRecentlyOpened() })));
|
||||
openRecentMenu.append(new MenuItem(this.likeAction('workbench.action.clearRecentFiles', { label: this.mnemonicLabel(nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened")), click: () => this.historyMainService.clearRecentlyOpened() })));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,7 +526,7 @@ export class CodeMenu {
|
||||
label,
|
||||
click: (menuItem, win, event) => {
|
||||
const openInNewWindow = this.isOptionClick(event);
|
||||
const success = this.windowsService.open({
|
||||
const success = this.windowsMainService.open({
|
||||
context: OpenContext.MENU,
|
||||
cli: this.environmentService.args,
|
||||
pathsToOpen: [path], forceNewWindow: openInNewWindow,
|
||||
@@ -531,7 +534,7 @@ export class CodeMenu {
|
||||
}).length > 0;
|
||||
|
||||
if (!success) {
|
||||
this.historyService.removeFromRecentlyOpened([isSingleFolderWorkspaceIdentifier(workspace) ? workspace : workspace.configPath]);
|
||||
this.historyMainService.removeFromRecentlyOpened([isSingleFolderWorkspaceIdentifier(workspace) ? workspace : workspace.configPath]);
|
||||
}
|
||||
}
|
||||
}, false));
|
||||
@@ -616,8 +619,7 @@ export class CodeMenu {
|
||||
private setSelectionMenu(winLinuxEditMenu: Electron.Menu): void {
|
||||
let multiCursorModifierLabel: string;
|
||||
if (this.currentMultiCursorModifierSetting === 'ctrlCmd') {
|
||||
// The default has been overwritten
|
||||
multiCursorModifierLabel = nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor");
|
||||
multiCursorModifierLabel = nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor"); // The default has been overwritten
|
||||
} else {
|
||||
multiCursorModifierLabel = (
|
||||
isMacintosh
|
||||
@@ -670,6 +672,7 @@ export class CodeMenu {
|
||||
selectHighlights,
|
||||
].forEach(item => winLinuxEditMenu.append(item));
|
||||
}
|
||||
// {{SQL CARBON EDIT}}
|
||||
*/
|
||||
|
||||
private setViewMenu(viewMenu: Electron.Menu): void {
|
||||
@@ -701,7 +704,7 @@ export class CodeMenu {
|
||||
|
||||
const commands = this.createMenuItem(nls.localize({ key: 'miCommandPalette', comment: ['&& denotes a mnemonic'] }, "&&Command Palette..."), 'workbench.action.showCommands');
|
||||
|
||||
const fullscreen = new MenuItem(this.withKeybinding('workbench.action.toggleFullScreen', { label: this.mnemonicLabel(nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen")), click: () => this.windowsService.getLastActiveWindow().toggleFullScreen(), enabled: this.windowsService.getWindowCount() > 0 }));
|
||||
const fullscreen = new MenuItem(this.withKeybinding('workbench.action.toggleFullScreen', { label: this.mnemonicLabel(nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen")), click: () => this.windowsMainService.getLastActiveWindow().toggleFullScreen(), enabled: this.windowsMainService.getWindowCount() > 0 }));
|
||||
const toggleZenMode = this.createMenuItem(nls.localize('miToggleZenMode', "Toggle Zen Mode"), 'workbench.action.toggleZenMode');
|
||||
const toggleMenuBar = this.createMenuItem(nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar"), 'workbench.action.toggleMenuBar');
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -908,15 +911,15 @@ export class CodeMenu {
|
||||
}
|
||||
|
||||
private setMacWindowMenu(macWindowMenu: Electron.Menu): void {
|
||||
const minimize = new MenuItem({ label: nls.localize('mMinimize', "Minimize"), role: 'minimize', accelerator: 'Command+M', enabled: this.windowsService.getWindowCount() > 0 });
|
||||
const zoom = new MenuItem({ label: nls.localize('mZoom', "Zoom"), role: 'zoom', enabled: this.windowsService.getWindowCount() > 0 });
|
||||
const bringAllToFront = new MenuItem({ label: nls.localize('mBringToFront', "Bring All to Front"), role: 'front', enabled: this.windowsService.getWindowCount() > 0 });
|
||||
const minimize = new MenuItem({ label: nls.localize('mMinimize', "Minimize"), role: 'minimize', accelerator: 'Command+M', enabled: this.windowsMainService.getWindowCount() > 0 });
|
||||
const zoom = new MenuItem({ label: nls.localize('mZoom', "Zoom"), role: 'zoom', enabled: this.windowsMainService.getWindowCount() > 0 });
|
||||
const bringAllToFront = new MenuItem({ label: nls.localize('mBringToFront', "Bring All to Front"), role: 'front', enabled: this.windowsMainService.getWindowCount() > 0 });
|
||||
const switchWindow = this.createMenuItem(nls.localize({ key: 'miSwitchWindow', comment: ['&& denotes a mnemonic'] }, "Switch &&Window..."), 'workbench.action.switchWindow');
|
||||
|
||||
this.nativeTabMenuItems = [];
|
||||
const nativeTabMenuItems: Electron.MenuItem[] = [];
|
||||
if (this.currentEnableNativeTabs) {
|
||||
const hasMultipleWindows = this.windowsService.getWindowCount() > 1;
|
||||
const hasMultipleWindows = this.windowsMainService.getWindowCount() > 1;
|
||||
|
||||
this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mShowPreviousTab', "Show Previous Tab"), 'workbench.action.showPreviousWindowTab', hasMultipleWindows));
|
||||
this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mShowNextTab', "Show Next Tab"), 'workbench.action.showNextWindowTab', hasMultipleWindows));
|
||||
@@ -939,7 +942,7 @@ export class CodeMenu {
|
||||
}
|
||||
|
||||
private toggleDevTools(): void {
|
||||
const w = this.windowsService.getFocusedWindow();
|
||||
const w = this.windowsMainService.getFocusedWindow();
|
||||
if (w && w.win) {
|
||||
const contents = w.win.webContents;
|
||||
if (w.hasHiddenTitleBarStyle() && !w.win.isFullScreen() && !contents.isDevToolsOpened()) {
|
||||
@@ -954,7 +957,7 @@ export class CodeMenu {
|
||||
const toggleDevToolsItem = new MenuItem(this.likeAction('workbench.action.toggleDevTools', {
|
||||
label: this.mnemonicLabel(nls.localize({ key: 'miToggleDevTools', comment: ['&& denotes a mnemonic'] }, "&&Toggle Developer Tools")),
|
||||
click: () => this.toggleDevTools(),
|
||||
enabled: (this.windowsService.getWindowCount() > 0)
|
||||
enabled: (this.windowsMainService.getWindowCount() > 0)
|
||||
}));
|
||||
|
||||
const showAccessibilityOptions = new MenuItem(this.likeAction('accessibilityOptions', {
|
||||
@@ -967,9 +970,9 @@ export class CodeMenu {
|
||||
|
||||
let reportIssuesItem: Electron.MenuItem = null;
|
||||
if (product.reportIssueUrl) {
|
||||
const label = nls.localize({ key: 'miReportIssues', comment: ['&& denotes a mnemonic'] }, "Report &&Issues");
|
||||
const label = nls.localize({ key: 'miReportIssue', comment: ['&& denotes a mnemonic', 'Translate this to "Report Issue in English" in all languages please!'] }, "Report &&Issue");
|
||||
|
||||
if (this.windowsService.getWindowCount() > 0) {
|
||||
if (this.windowsMainService.getWindowCount() > 0) {
|
||||
reportIssuesItem = this.createMenuItem(label, 'workbench.action.reportIssues');
|
||||
} else {
|
||||
reportIssuesItem = new MenuItem({ label: this.mnemonicLabel(label), click: () => this.openUrl(product.reportIssueUrl, 'openReportIssues') });
|
||||
@@ -1040,27 +1043,22 @@ export class CodeMenu {
|
||||
const showTasks = this.createMenuItem(nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks..."), 'workbench.action.tasks.showTasks');
|
||||
const restartTask = this.createMenuItem(nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task..."), 'workbench.action.tasks.restartTask');
|
||||
const terminateTask = this.createMenuItem(nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task..."), 'workbench.action.tasks.terminate');
|
||||
// const testTask = this.createMenuItem(nls.localize({ key: 'miTestTask', comment: ['&& denotes a mnemonic'] }, "Run Test T&&ask..."), 'workbench.action.tasks.test');
|
||||
// const showTaskLog = this.createMenuItem(nls.localize({ key: 'miShowTaskLog', comment: ['&& denotes a mnemonic'] }, "&&Show Task Log"), 'workbench.action.tasks.showLog');
|
||||
const configureTask = this.createMenuItem(nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks..."), 'workbench.action.tasks.configureTaskRunner');
|
||||
const configureBuildTask = this.createMenuItem(nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task..."), 'workbench.action.tasks.configureDefaultBuildTask');
|
||||
// const configureTestTask = this.createMenuItem(nls.localize({ key: 'miConfigureTestTask', comment: ['&& denotes a mnemonic'] }, "Configure Defau&< Test Task"), 'workbench.action.tasks.configureDefaultTestTask');
|
||||
|
||||
[
|
||||
//__separator__(),
|
||||
runTask,
|
||||
buildTask,
|
||||
// testTask,
|
||||
__separator__(),
|
||||
terminateTask,
|
||||
restartTask,
|
||||
showTasks,
|
||||
__separator__(),
|
||||
//showTaskLog,
|
||||
configureTask,
|
||||
configureBuildTask
|
||||
// configureTestTask
|
||||
].forEach(item => taskMenu.append(item));
|
||||
// {{SQL CARBON EDIT}}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -1135,7 +1133,7 @@ export class CodeMenu {
|
||||
|
||||
this.runActionInRenderer(commandId);
|
||||
};
|
||||
const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsService.getWindowCount() > 0;
|
||||
const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsMainService.getWindowCount() > 0;
|
||||
const checked = typeof arg4 === 'boolean' ? arg4 : false;
|
||||
|
||||
let commandId: string;
|
||||
@@ -1160,11 +1158,11 @@ export class CodeMenu {
|
||||
private createContextAwareMenuItem(label: string, commandId: string, clickHandler: IMenuItemClickHandler): Electron.MenuItem {
|
||||
return new MenuItem(this.withKeybinding(commandId, {
|
||||
label: this.mnemonicLabel(label),
|
||||
enabled: this.windowsService.getWindowCount() > 0,
|
||||
enabled: this.windowsMainService.getWindowCount() > 0,
|
||||
click: () => {
|
||||
|
||||
// No Active Window
|
||||
const activeWindow = this.windowsService.getFocusedWindow();
|
||||
const activeWindow = this.windowsMainService.getFocusedWindow();
|
||||
if (!activeWindow) {
|
||||
return clickHandler.inNoWindow();
|
||||
}
|
||||
@@ -1181,7 +1179,13 @@ export class CodeMenu {
|
||||
}
|
||||
|
||||
private runActionInRenderer(id: string): void {
|
||||
this.windowsService.sendToFocused('vscode:runAction', { id, from: 'menu' } as IRunActionInWindowRequest);
|
||||
// We make sure to not run actions when the window has no focus, this helps
|
||||
// for https://github.com/Microsoft/vscode/issues/25907 and specifically for
|
||||
// https://github.com/Microsoft/vscode/issues/11928
|
||||
const activeWindow = this.windowsMainService.getFocusedWindow();
|
||||
if (activeWindow) {
|
||||
this.windowsMainService.sendToFocused('vscode:runAction', { id, from: 'menu' } as IRunActionInWindowRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private withKeybinding(commandId: string, options: Electron.MenuItemConstructorOptions): Electron.MenuItemConstructorOptions {
|
||||
@@ -1232,25 +1236,36 @@ export class CodeMenu {
|
||||
}
|
||||
|
||||
private openAboutDialog(): void {
|
||||
const lastActiveWindow = this.windowsService.getFocusedWindow() || this.windowsService.getLastActiveWindow();
|
||||
const lastActiveWindow = this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();
|
||||
|
||||
dialog.showMessageBox(lastActiveWindow && lastActiveWindow.win, {
|
||||
const detail = nls.localize('aboutDetail',
|
||||
"Version {0}\nCommit {1}\nDate {2}\nShell {3}\nRenderer {4}\nNode {5}\nArchitecture {6}",
|
||||
app.getVersion(),
|
||||
product.commit || 'Unknown',
|
||||
product.date || 'Unknown',
|
||||
process.versions['electron'],
|
||||
process.versions['chrome'],
|
||||
process.versions['node'],
|
||||
process.arch
|
||||
);
|
||||
|
||||
const buttons = [nls.localize('okButton', "OK")];
|
||||
if (isWindows) {
|
||||
buttons.push(mnemonicButtonLabel(nls.localize({ key: 'copy', comment: ['&& denotes a mnemonic'] }, "&&Copy"))); // https://github.com/Microsoft/vscode/issues/37608
|
||||
}
|
||||
|
||||
const result = dialog.showMessageBox(lastActiveWindow && lastActiveWindow.win, {
|
||||
title: product.nameLong,
|
||||
type: 'info',
|
||||
message: product.nameLong,
|
||||
detail: nls.localize('aboutDetail',
|
||||
"\nVersion {0}\nCommit {1}\nDate {2}\nShell {3}\nRenderer {4}\nNode {5}\nArchitecture {6}",
|
||||
app.getVersion(),
|
||||
product.commit || 'Unknown',
|
||||
product.date || 'Unknown',
|
||||
process.versions['electron'],
|
||||
process.versions['chrome'],
|
||||
process.versions['node'],
|
||||
process.arch
|
||||
),
|
||||
buttons: [nls.localize('okButton', "OK")],
|
||||
detail: `\n${detail}`,
|
||||
buttons,
|
||||
noLink: true
|
||||
}, result => null);
|
||||
});
|
||||
|
||||
if (isWindows && result === 1) {
|
||||
clipboard.writeText(detail);
|
||||
}
|
||||
|
||||
this.reportMenuActionTelemetry('showAboutDialog');
|
||||
}
|
||||
@@ -1262,7 +1277,7 @@ export class CodeMenu {
|
||||
|
||||
private reportMenuActionTelemetry(id: string): void {
|
||||
/* __GDPR__
|
||||
"workbencActionExecuted" : {
|
||||
"workbenchActionExecuted" : {
|
||||
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
|
||||
@@ -10,16 +10,22 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { BrowserWindow, ipcMain } from 'electron';
|
||||
import { PromiseSource } from 'vs/base/common/async';
|
||||
import { ISharedProcess } from 'vs/platform/windows/electron-main/windows';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
|
||||
export class SharedProcess implements ISharedProcess {
|
||||
|
||||
private spawnPromiseSource: PromiseSource<void>;
|
||||
private barrier = new Barrier();
|
||||
|
||||
private window: Electron.BrowserWindow;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private environmentService: IEnvironmentService,
|
||||
private readonly machineId: string,
|
||||
private readonly userEnv: IProcessEnvironment
|
||||
) { }
|
||||
|
||||
@memoize
|
||||
private get _whenReady(): TPromise<void> {
|
||||
this.window = new BrowserWindow({
|
||||
@@ -32,6 +38,7 @@ export class SharedProcess implements ISharedProcess {
|
||||
});
|
||||
const config = assign({
|
||||
appRoot: this.environmentService.appRoot,
|
||||
machineId: this.machineId,
|
||||
nodeCachedDataDir: this.environmentService.nodeCachedDataDir,
|
||||
userEnv: this.userEnv
|
||||
});
|
||||
@@ -65,7 +72,7 @@ export class SharedProcess implements ISharedProcess {
|
||||
}));
|
||||
|
||||
return new TPromise<void>((c, e) => {
|
||||
ipcMain.once('handshake:hello', ({ sender }) => {
|
||||
ipcMain.once('handshake:hello', ({ sender }: { sender: any }) => {
|
||||
sender.send('handshake:hey there', {
|
||||
sharedIPCHandle: this.environmentService.sharedIPCHandle,
|
||||
args: this.environmentService.args
|
||||
@@ -76,22 +83,15 @@ export class SharedProcess implements ISharedProcess {
|
||||
});
|
||||
}
|
||||
|
||||
constructor(
|
||||
private environmentService: IEnvironmentService,
|
||||
private userEnv: IProcessEnvironment
|
||||
) {
|
||||
this.spawnPromiseSource = new PromiseSource<void>();
|
||||
spawn(): void {
|
||||
this.barrier.open();
|
||||
}
|
||||
|
||||
public spawn(): void {
|
||||
this.spawnPromiseSource.complete();
|
||||
whenReady(): TPromise<void> {
|
||||
return this.barrier.wait().then(() => this._whenReady);
|
||||
}
|
||||
|
||||
public whenReady(): TPromise<void> {
|
||||
return this.spawnPromiseSource.value.then(() => this._whenReady);
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
toggle(): void {
|
||||
if (this.window.isVisible()) {
|
||||
this.hide();
|
||||
} else {
|
||||
@@ -99,17 +99,17 @@ export class SharedProcess implements ISharedProcess {
|
||||
}
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
show(): void {
|
||||
this.window.show();
|
||||
this.window.webContents.openDevTools();
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
hide(): void {
|
||||
this.window.webContents.closeDevTools();
|
||||
this.window.hide();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
|
||||
import * as path from 'path';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { stopProfiling } from 'vs/base/node/profiler';
|
||||
import nls = require('vs/nls');
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IStorageService } from 'vs/platform/storage/node/storage';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import { shell, screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage } from 'electron';
|
||||
import { TPromise, TValueCallback } from 'vs/base/common/winjs.base';
|
||||
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
@@ -18,7 +17,6 @@ import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { parseArgs } from 'vs/platform/environment/node/argv';
|
||||
import product from 'vs/platform/node/product';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
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';
|
||||
@@ -26,6 +24,8 @@ import { ICodeWindow } 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;
|
||||
@@ -71,15 +71,15 @@ interface ITouchBarSegment extends Electron.SegmentedControlSegment {
|
||||
|
||||
export class CodeWindow implements ICodeWindow {
|
||||
|
||||
public static themeStorageKey = 'theme';
|
||||
public static themeBackgroundStorageKey = 'themeBackground';
|
||||
public static readonly themeStorageKey = 'theme';
|
||||
public static readonly themeBackgroundStorageKey = 'themeBackground';
|
||||
|
||||
private static DEFAULT_BG_LIGHT = '#FFFFFF';
|
||||
private static DEFAULT_BG_DARK = '#1E1E1E';
|
||||
private static DEFAULT_BG_HC_BLACK = '#000000';
|
||||
private static readonly DEFAULT_BG_LIGHT = '#FFFFFF';
|
||||
private static readonly DEFAULT_BG_DARK = '#1E1E1E';
|
||||
private static readonly DEFAULT_BG_HC_BLACK = '#000000';
|
||||
|
||||
private static MIN_WIDTH = 200;
|
||||
private static MIN_HEIGHT = 120;
|
||||
private static readonly MIN_WIDTH = 200;
|
||||
private static readonly MIN_HEIGHT = 120;
|
||||
|
||||
private hiddenTitleBarStyle: boolean;
|
||||
private showTimeoutHandle: any;
|
||||
@@ -97,6 +97,8 @@ export class CodeWindow implements ICodeWindow {
|
||||
private currentConfig: IWindowConfiguration;
|
||||
private pendingLoadConfig: IWindowConfiguration;
|
||||
|
||||
private marketplaceHeadersPromise: TPromise<object>;
|
||||
|
||||
private touchBarGroups: Electron.TouchBarSegmentedControl[];
|
||||
|
||||
constructor(
|
||||
@@ -104,9 +106,9 @@ export class CodeWindow implements ICodeWindow {
|
||||
@ILogService private logService: ILogService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@IWorkspacesMainService private workspaceService: IWorkspacesMainService,
|
||||
@IBackupMainService private backupService: IBackupMainService
|
||||
@IStateService private stateService: IStateService,
|
||||
@IWorkspacesMainService private workspacesMainService: IWorkspacesMainService,
|
||||
@IBackupMainService private backupMainService: IBackupMainService
|
||||
) {
|
||||
this.touchBarGroups = [];
|
||||
this._lastFocusTime = -1;
|
||||
@@ -123,6 +125,9 @@ export class CodeWindow implements ICodeWindow {
|
||||
// macOS: touch bar support
|
||||
this.createTouchBar();
|
||||
|
||||
// Request handling
|
||||
this.handleMarketplaceRequests();
|
||||
|
||||
// Eventing
|
||||
this.registerListeners();
|
||||
}
|
||||
@@ -160,7 +165,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); // Windows and Mac are better off using the embedded icon(s)
|
||||
}
|
||||
|
||||
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
|
||||
let useNativeTabs = false;
|
||||
if (windowConfig && windowConfig.nativeTabs) {
|
||||
@@ -206,7 +211,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
this.logService.log(`Unexpected error fixing window position on windows with multiple windows: ${err}\n${err.stack}`);
|
||||
this.logService.warn(`Unexpected error fixing window position on windows with multiple windows: ${err}\n${err.stack}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,17 +339,21 @@ export class CodeWindow implements ICodeWindow {
|
||||
return this._readyState;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
|
||||
const headers = {
|
||||
'X-Market-Client-Id': `VSCode ${pkg.version}`,
|
||||
'User-Agent': `VSCode ${pkg.version}`,
|
||||
'X-Market-User-Id': this.environmentService.machineUUID
|
||||
};
|
||||
private handleMarketplaceRequests(): void {
|
||||
|
||||
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => {
|
||||
cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) });
|
||||
// Resolve marketplace headers
|
||||
this.marketplaceHeadersPromise = resolveMarketplaceHeaders(this.environmentService);
|
||||
|
||||
// Inject headers when requests are incoming
|
||||
const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
|
||||
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details: any, cb: any) => {
|
||||
this.marketplaceHeadersPromise.done(headers => {
|
||||
cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Prevent loading of svgs
|
||||
this._win.webContents.session.webRequest.onBeforeRequest(null, (details, callback) => {
|
||||
@@ -358,7 +367,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
return callback({});
|
||||
});
|
||||
|
||||
this._win.webContents.session.webRequest.onHeadersReceived(null, (details, callback) => {
|
||||
this._win.webContents.session.webRequest.onHeadersReceived(null, (details: any, callback: any) => {
|
||||
const contentType: string[] = (details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']) as any;
|
||||
if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) {
|
||||
return callback({ cancel: true });
|
||||
@@ -433,7 +442,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated()));
|
||||
|
||||
// Handle Workspace events
|
||||
this.toDispose.push(this.workspaceService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e)));
|
||||
this.toDispose.push(this.workspacesMainService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e)));
|
||||
}
|
||||
|
||||
private onUntitledWorkspaceDeleted(workspace: IWorkspaceIdentifier): void {
|
||||
@@ -454,14 +463,14 @@ export class CodeWindow implements ICodeWindow {
|
||||
|
||||
// Swipe command support (macOS)
|
||||
if (isMacintosh) {
|
||||
const config = this.configurationService.getConfiguration<IWorkbenchEditorConfiguration>();
|
||||
const config = this.configurationService.getValue<IWorkbenchEditorConfiguration>();
|
||||
if (config && config.workbench && config.workbench.editor && config.workbench.editor.swipeToNavigate) {
|
||||
this.registerNavigationListenerOn('swipe', 'left', 'right', true);
|
||||
} else {
|
||||
this._win.removeAllListeners('swipe');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private registerNavigationListenerOn(command: 'swipe' | 'app-command', back: 'left' | 'browser-backward', forward: 'right' | 'browser-forward', acrossEditors: boolean) {
|
||||
this._win.on(command as 'swipe' /* | 'app-command' */, (e: Electron.Event, cmd: string) => {
|
||||
@@ -496,7 +505,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
|
||||
// Clear Document Edited if needed
|
||||
if (isMacintosh && this._win.isDocumentEdited()) {
|
||||
if (!isReload || !this.backupService.isHotExitEnabled()) {
|
||||
if (!isReload || !this.backupMainService.isHotExitEnabled()) {
|
||||
this._win.setDocumentEdited(false);
|
||||
}
|
||||
}
|
||||
@@ -511,6 +520,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
}
|
||||
|
||||
// Load URL
|
||||
mark('main:loadWindow');
|
||||
this._win.loadURL(this.getUrl(config));
|
||||
|
||||
// Make window visible if it did not open in N seconds because this indicates an error
|
||||
@@ -524,12 +534,6 @@ export class CodeWindow implements ICodeWindow {
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
// (--prof-startup) save profile to disk
|
||||
const { profileStartup } = this.environmentService;
|
||||
if (profileStartup) {
|
||||
stopProfiling(profileStartup.dir, profileStartup.prefix).done(undefined, err => this.logService.error(err));
|
||||
}
|
||||
}
|
||||
|
||||
public reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void {
|
||||
@@ -564,7 +568,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
private getUrl(windowConfiguration: IWindowConfiguration): string {
|
||||
|
||||
// Set zoomlevel
|
||||
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
const zoomLevel = windowConfig && windowConfig.zoomLevel;
|
||||
if (typeof zoomLevel === 'number') {
|
||||
windowConfiguration.zoomLevel = zoomLevel;
|
||||
@@ -582,6 +586,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
windowConfiguration.backgroundColor = this.getBackgroundColor();
|
||||
|
||||
// Perf Counters
|
||||
windowConfiguration.perfEntries = exportEntries();
|
||||
windowConfiguration.perfStartTime = global.perfStartTime;
|
||||
windowConfiguration.perfAppReady = global.perfAppReady;
|
||||
windowConfiguration.perfWindowLoadTime = Date.now();
|
||||
@@ -603,7 +608,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
return 'hc-black';
|
||||
}
|
||||
|
||||
const theme = this.storageService.getItem<string>(CodeWindow.themeStorageKey, 'vs-dark');
|
||||
const theme = this.stateService.getItem<string>(CodeWindow.themeStorageKey, 'vs-dark');
|
||||
|
||||
return theme.split(' ')[0];
|
||||
}
|
||||
@@ -613,7 +618,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
return CodeWindow.DEFAULT_BG_HC_BLACK;
|
||||
}
|
||||
|
||||
const background = this.storageService.getItem<string>(CodeWindow.themeBackgroundStorageKey, null);
|
||||
const background = this.stateService.getItem<string>(CodeWindow.themeBackgroundStorageKey, null);
|
||||
if (!background) {
|
||||
const baseTheme = this.getBaseTheme();
|
||||
|
||||
@@ -624,6 +629,9 @@ export class CodeWindow implements ICodeWindow {
|
||||
}
|
||||
|
||||
public serializeWindowState(): IWindowState {
|
||||
if (!this._win) {
|
||||
return defaultWindowState();
|
||||
}
|
||||
|
||||
// fullscreen gets special treatment
|
||||
if (this._win.isFullScreen()) {
|
||||
@@ -676,7 +684,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
try {
|
||||
state = this.validateWindowState(state);
|
||||
} catch (err) {
|
||||
this.logService.log(`Unexpected error validating window state: ${err}\n${err.stack}`); // somehow display API can be picky about the state to validate
|
||||
this.logService.warn(`Unexpected error validating window state: ${err}\n${err.stack}`); // somehow display API can be picky about the state to validate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -794,7 +802,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
}
|
||||
|
||||
private getMenuBarVisibility(): MenuBarVisibility {
|
||||
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
if (!windowConfig || !windowConfig.menuBarVisibility) {
|
||||
return 'default';
|
||||
}
|
||||
@@ -831,7 +839,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
|
||||
if (notify) {
|
||||
this.send('vscode:showInfoMessage', nls.localize('hiddenMenuBar', "You can still access the menu bar by pressing the **Alt** key."));
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case ('hidden'):
|
||||
@@ -845,7 +853,7 @@ export class CodeWindow implements ICodeWindow {
|
||||
this._win.setAutoHideMenuBar(false);
|
||||
});
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public onWindowTitleDoubleClick(): void {
|
||||
@@ -972,4 +980,4 @@ export class CodeWindow implements ICodeWindow {
|
||||
|
||||
this._win = null; // Important to dereference the window object to allow for GC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as arrays from 'vs/base/common/arrays';
|
||||
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 { IStorageService } from 'vs/platform/storage/node/storage';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import { CodeWindow, IWindowState as ISingleWindowState, defaultWindowState, WindowMode } 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';
|
||||
@@ -107,7 +107,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private static windowsStateStorageKey = 'windowsState';
|
||||
private static readonly windowsStateStorageKey = 'windowsState';
|
||||
|
||||
private static WINDOWS: CodeWindow[] = [];
|
||||
|
||||
@@ -138,21 +138,22 @@ export class WindowsManager implements IWindowsMainService {
|
||||
onWindowsCountChanged: CommonEvent<IWindowsCountChangedEvent> = this._onWindowsCountChanged.event;
|
||||
|
||||
constructor(
|
||||
private readonly machineId: string,
|
||||
@ILogService private logService: ILogService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@IStateService private stateService: IStateService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@ILifecycleService private lifecycleService: ILifecycleService,
|
||||
@IBackupMainService private backupService: IBackupMainService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IBackupMainService private backupMainService: IBackupMainService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IHistoryMainService private historyService: IHistoryMainService,
|
||||
@IWorkspacesMainService private workspacesService: IWorkspacesMainService,
|
||||
@IHistoryMainService private historyMainService: IHistoryMainService,
|
||||
@IWorkspacesMainService private workspacesMainService: IWorkspacesMainService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
this.windowsState = this.storageService.getItem<IWindowsState>(WindowsManager.windowsStateStorageKey) || { openedWindows: [] };
|
||||
this.windowsState = this.stateService.getItem<IWindowsState>(WindowsManager.windowsStateStorageKey) || { openedWindows: [] };
|
||||
|
||||
this.fileDialog = new FileDialog(environmentService, telemetryService, storageService, this);
|
||||
this.workspacesManager = new WorkspacesManager(workspacesService, lifecycleService, backupService, environmentService, this);
|
||||
this.fileDialog = new FileDialog(environmentService, telemetryService, stateService, this);
|
||||
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, this);
|
||||
|
||||
this.migrateLegacyWindowState();
|
||||
}
|
||||
@@ -201,8 +202,8 @@ export class WindowsManager implements IWindowsMainService {
|
||||
});
|
||||
|
||||
// React to workbench loaded events from windows
|
||||
ipc.on('vscode:workbenchLoaded', (event, windowId: number) => {
|
||||
this.logService.log('IPC#vscode-workbenchLoaded');
|
||||
ipc.on('vscode:workbenchLoaded', (_event: any, windowId: number) => {
|
||||
this.logService.trace('IPC#vscode-workbenchLoaded');
|
||||
|
||||
const win = this.getWindowById(windowId);
|
||||
if (win) {
|
||||
@@ -310,7 +311,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
|
||||
// Persist
|
||||
this.storageService.setItem(WindowsManager.windowsStateStorageKey, currentWindowsState);
|
||||
this.stateService.setItem(WindowsManager.windowsStateStorageKey, currentWindowsState);
|
||||
}
|
||||
|
||||
// See note on #onBeforeQuit() for details how these events are flowing
|
||||
@@ -403,12 +404,12 @@ export class WindowsManager implements IWindowsMainService {
|
||||
let workspacesToRestore: IWorkspaceIdentifier[] = [];
|
||||
let emptyToRestore: string[] = [];
|
||||
if (openConfig.initialStartup && !openConfig.cli.extensionDevelopmentPath) {
|
||||
foldersToRestore = this.backupService.getFolderBackupPaths();
|
||||
foldersToRestore = this.backupMainService.getFolderBackupPaths();
|
||||
|
||||
workspacesToRestore = this.backupService.getWorkspaceBackups(); // collect from workspaces with hot-exit backups
|
||||
workspacesToRestore.push(...this.workspacesService.getUntitledWorkspacesSync()); // collect from previous window session
|
||||
workspacesToRestore = this.backupMainService.getWorkspaceBackups(); // collect from workspaces with hot-exit backups
|
||||
workspacesToRestore.push(...this.workspacesMainService.getUntitledWorkspacesSync()); // collect from previous window session
|
||||
|
||||
emptyToRestore = this.backupService.getEmptyWindowBackupPaths();
|
||||
emptyToRestore = this.backupMainService.getEmptyWindowBackupPaths();
|
||||
emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderPath && w.backupPath).map(w => basename(w.backupPath))); // add empty windows with backupPath
|
||||
emptyToRestore = arrays.distinct(emptyToRestore); // prevent duplicates
|
||||
}
|
||||
@@ -475,7 +476,9 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
});
|
||||
|
||||
this.historyService.addRecentlyOpened(recentlyOpenedWorkspaces, recentlyOpenedFiles);
|
||||
if (!this.environmentService.skipAddToRecentlyOpened) {
|
||||
this.historyMainService.addRecentlyOpened(recentlyOpenedWorkspaces, recentlyOpenedFiles);
|
||||
}
|
||||
}
|
||||
|
||||
// If we got started with --wait from the CLI, we need to signal to the outside when the window
|
||||
@@ -541,7 +544,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
context: openConfig.context,
|
||||
filePath: fileToCheck && fileToCheck.filePath,
|
||||
userHome: this.environmentService.userHome,
|
||||
workspaceResolver: workspace => this.workspacesService.resolveWorkspaceSync(workspace.configPath)
|
||||
workspaceResolver: workspace => this.workspacesMainService.resolveWorkspaceSync(workspace.configPath)
|
||||
});
|
||||
|
||||
// Special case: we started with --wait and we got back a folder to open. In this case
|
||||
@@ -796,7 +799,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
if (!openConfig.addMode && isCommandLineOrAPICall) {
|
||||
const foldersToOpen = windowsToOpen.filter(path => !!path.folderPath);
|
||||
if (foldersToOpen.length > 1) {
|
||||
const workspace = this.workspacesService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: URI.file(folder.folderPath) })));
|
||||
const workspace = this.workspacesMainService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: URI.file(folder.folderPath) })));
|
||||
|
||||
// Add workspace and remove folders thereby
|
||||
windowsToOpen.push({ workspace });
|
||||
@@ -934,7 +937,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
if (this.lifecycleService.wasRestarted) {
|
||||
restoreWindows = 'all'; // always reopen all windows when an update was applied
|
||||
} else {
|
||||
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
restoreWindows = ((windowConfig && windowConfig.restoreWindows) || 'one') as RestoreWindowsSetting;
|
||||
|
||||
if (restoreWindows === 'one' /* default */ && windowConfig && windowConfig.reopenFolders) {
|
||||
@@ -970,7 +973,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// Workspace (unless disabled via flag)
|
||||
if (!options || !options.forceOpenWorkspaceAsFile) {
|
||||
const workspace = this.workspacesService.resolveWorkspaceSync(candidate);
|
||||
const workspace = this.workspacesMainService.resolveWorkspaceSync(candidate);
|
||||
if (workspace) {
|
||||
return { workspace: { id: workspace.id, configPath: workspace.configPath } };
|
||||
}
|
||||
@@ -990,7 +993,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
this.historyService.removeFromRecentlyOpened([candidate]); // since file does not seem to exist anymore, remove from recent
|
||||
this.historyMainService.removeFromRecentlyOpened([candidate]); // since file does not seem to exist anymore, remove from recent
|
||||
|
||||
if (options && options.ignoreFileNotFound) {
|
||||
return { filePath: candidate, createFilePath: true }; // assume this is a file that does not yet exist
|
||||
@@ -1003,7 +1006,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
private shouldOpenNewWindow(openConfig: IOpenConfiguration): { openFolderInNewWindow: boolean; openFilesInNewWindow: boolean; } {
|
||||
|
||||
// let the user settings override how folders are open in a new window or same window unless we are forced
|
||||
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
const openFolderInNewWindowConfig = (windowConfig && windowConfig.openFoldersInNewWindow) || 'default' /* default */;
|
||||
const openFilesInNewWindowConfig = (windowConfig && windowConfig.openFilesInNewWindow) || 'off' /* default */;
|
||||
|
||||
@@ -1065,6 +1068,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// Build IWindowConfiguration from config and options
|
||||
const configuration: IWindowConfiguration = mixin({}, options.cli); // inherit all properties from CLI
|
||||
configuration.appRoot = this.environmentService.appRoot;
|
||||
configuration.machineId = this.machineId;
|
||||
configuration.execPath = process.execPath;
|
||||
configuration.userEnv = assign({}, this.initialUserEnv, options.userEnv || {});
|
||||
configuration.isInitialStartup = options.initialStartup;
|
||||
@@ -1094,7 +1098,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// New window
|
||||
if (!window) {
|
||||
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
const state = this.getNewWindowState(configuration);
|
||||
|
||||
// Window state is not from a previous session: only allow fullscreen if we inherit it or user wants fullscreen
|
||||
@@ -1158,11 +1162,11 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// Register window for backups
|
||||
if (!configuration.extensionDevelopmentPath) {
|
||||
if (configuration.workspace) {
|
||||
configuration.backupPath = this.backupService.registerWorkspaceBackupSync(configuration.workspace);
|
||||
configuration.backupPath = this.backupMainService.registerWorkspaceBackupSync(configuration.workspace);
|
||||
} else if (configuration.folderPath) {
|
||||
configuration.backupPath = this.backupService.registerFolderBackupSync(configuration.folderPath);
|
||||
configuration.backupPath = this.backupMainService.registerFolderBackupSync(configuration.folderPath);
|
||||
} else {
|
||||
configuration.backupPath = this.backupService.registerEmptyWindowBackupSync(options.emptyWindowBackupFolder);
|
||||
configuration.backupPath = this.backupMainService.registerEmptyWindowBackupSync(options.emptyWindowBackupFolder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1257,7 +1261,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
state.y = displayToUse.bounds.y + (displayToUse.bounds.height / 2) - (state.height / 2);
|
||||
|
||||
// Check for newWindowDimensions setting and adjust accordingly
|
||||
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
let ensureNoOverlap = true;
|
||||
if (windowConfig && windowConfig.newWindowDimensions) {
|
||||
if (windowConfig.newWindowDimensions === 'maximized') {
|
||||
@@ -1332,7 +1336,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
private doEnterWorkspace(win: CodeWindow, result: IEnterWorkspaceResult): IEnterWorkspaceResult {
|
||||
|
||||
// Mark as recently opened
|
||||
this.historyService.addRecentlyOpened([result.workspace], []);
|
||||
this.historyMainService.addRecentlyOpened([result.workspace], []);
|
||||
|
||||
// Trigger Eevent to indicate load of workspace into window
|
||||
this._onWindowReady.fire(win);
|
||||
@@ -1352,7 +1356,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
|
||||
const workspace = e.window.openedWorkspace;
|
||||
if (!workspace || !this.workspacesService.isUntitledWorkspace(workspace)) {
|
||||
if (!workspace || !this.workspacesMainService.isUntitledWorkspace(workspace)) {
|
||||
return; // only care about untitled workspaces to ask for saving
|
||||
}
|
||||
|
||||
@@ -1453,48 +1457,48 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// Unresponsive
|
||||
if (error === WindowError.UNRESPONSIVE) {
|
||||
dialog.showMessageBox(window.win, {
|
||||
const result = dialog.showMessageBox(window.win, {
|
||||
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
|
||||
}, result => {
|
||||
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
|
||||
}
|
||||
});
|
||||
|
||||
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 {
|
||||
dialog.showMessageBox(window.win, {
|
||||
const result = dialog.showMessageBox(window.win, {
|
||||
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
|
||||
}, result => {
|
||||
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
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1582,12 +1586,12 @@ interface IInternalNativeOpenDialogOptions extends INativeOpenDialogOptions {
|
||||
|
||||
class FileDialog {
|
||||
|
||||
private static workingDirPickerStorageKey = 'pickerWorkingDir';
|
||||
private static readonly workingDirPickerStorageKey = 'pickerWorkingDir';
|
||||
|
||||
constructor(
|
||||
private environmentService: IEnvironmentService,
|
||||
private telemetryService: ITelemetryService,
|
||||
private storageService: IStorageService,
|
||||
private stateService: IStateService,
|
||||
private windowsMainService: IWindowsMainService
|
||||
) {
|
||||
}
|
||||
@@ -1628,7 +1632,7 @@ class FileDialog {
|
||||
|
||||
// Ensure defaultPath
|
||||
if (!options.dialogOptions.defaultPath) {
|
||||
options.dialogOptions.defaultPath = this.storageService.getItem<string>(FileDialog.workingDirPickerStorageKey);
|
||||
options.dialogOptions.defaultPath = this.stateService.getItem<string>(FileDialog.workingDirPickerStorageKey);
|
||||
}
|
||||
|
||||
// Ensure properties
|
||||
@@ -1650,21 +1654,20 @@ class FileDialog {
|
||||
|
||||
// Show Dialog
|
||||
const focusedWindow = this.windowsMainService.getWindowById(options.windowId) || this.windowsMainService.getFocusedWindow();
|
||||
dialog.showOpenDialog(focusedWindow && focusedWindow.win, options.dialogOptions, paths => {
|
||||
if (paths && paths.length > 0) {
|
||||
if (isMacintosh) {
|
||||
paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS
|
||||
}
|
||||
|
||||
// Remember path in storage for next time
|
||||
this.storageService.setItem(FileDialog.workingDirPickerStorageKey, dirname(paths[0]));
|
||||
|
||||
// Return
|
||||
return clb(paths);
|
||||
let paths = dialog.showOpenDialog(focusedWindow && focusedWindow.win, options.dialogOptions);
|
||||
if (paths && paths.length > 0) {
|
||||
if (isMacintosh) {
|
||||
paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS
|
||||
}
|
||||
|
||||
return clb(void (0));
|
||||
});
|
||||
// Remember path in storage for next time
|
||||
this.stateService.setItem(FileDialog.workingDirPickerStorageKey, dirname(paths[0]));
|
||||
|
||||
// Return
|
||||
return clb(paths);
|
||||
}
|
||||
|
||||
return clb(void (0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1672,7 +1675,6 @@ class WorkspacesManager {
|
||||
|
||||
constructor(
|
||||
private workspacesService: IWorkspacesMainService,
|
||||
private lifecycleService: ILifecycleService,
|
||||
private backupService: IBackupMainService,
|
||||
private environmentService: IEnvironmentService,
|
||||
private windowsMainService: IWindowsMainService
|
||||
|
||||
@@ -3,18 +3,21 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import { spawn, ChildProcess } from 'child_process';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { parseCLIProcessArgv, buildHelpMessage } from 'vs/platform/environment/node/argv';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import product from 'vs/platform/node/product';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as paths from 'path';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import { whenDeleted } from 'vs/base/node/pfs';
|
||||
import { findFreePort } from 'vs/base/node/ports';
|
||||
import { resolveTerminalEncoding } from 'vs/base/node/encoding';
|
||||
import * as iconv from 'iconv-lite';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
function shouldSpawnCliProcess(argv: ParsedArgs): boolean {
|
||||
return !!argv['install-source']
|
||||
@@ -27,7 +30,7 @@ interface IMainCli {
|
||||
main: (argv: ParsedArgs) => TPromise<void>;
|
||||
}
|
||||
|
||||
export function main(argv: string[]): TPromise<void> {
|
||||
export async function main(argv: string[]): TPromise<any> {
|
||||
let args: ParsedArgs;
|
||||
|
||||
try {
|
||||
@@ -37,24 +40,127 @@ export function main(argv: string[]): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
// Help
|
||||
if (args.help) {
|
||||
console.log(buildHelpMessage(product.nameLong, product.applicationName, pkg.version));
|
||||
} else if (args.version) {
|
||||
console.log(`${pkg.version}\n${product.commit}`);
|
||||
} else if (shouldSpawnCliProcess(args)) {
|
||||
}
|
||||
|
||||
// Version Info
|
||||
else if (args.version) {
|
||||
console.log(`${pkg.version}\n${product.commit}\n${process.arch}`);
|
||||
}
|
||||
|
||||
// Extensions Management
|
||||
else if (shouldSpawnCliProcess(args)) {
|
||||
const mainCli = new TPromise<IMainCli>(c => require(['vs/code/node/cliProcessMain'], c));
|
||||
return mainCli.then(cli => cli.main(args));
|
||||
} else {
|
||||
}
|
||||
|
||||
// Just Code
|
||||
else {
|
||||
const env = assign({}, process.env, {
|
||||
// this will signal Code that it was spawned from this module
|
||||
'VSCODE_CLI': '1',
|
||||
'VSCODE_CLI': '1', // this will signal Code that it was spawned from this module
|
||||
'ELECTRON_NO_ATTACH_CONSOLE': '1'
|
||||
});
|
||||
|
||||
delete env['ELECTRON_RUN_AS_NODE'];
|
||||
|
||||
if (args.verbose) {
|
||||
const processCallbacks: ((child: ChildProcess) => Thenable<any>)[] = [];
|
||||
|
||||
const verbose = args.verbose || args.status;
|
||||
if (verbose) {
|
||||
env['ELECTRON_ENABLE_LOGGING'] = '1';
|
||||
|
||||
processCallbacks.push(child => {
|
||||
child.stdout.on('data', (data: Buffer) => console.log(data.toString('utf8').trim()));
|
||||
child.stderr.on('data', (data: Buffer) => console.log(data.toString('utf8').trim()));
|
||||
|
||||
return new TPromise<void>(c => child.once('exit', () => c(null)));
|
||||
});
|
||||
}
|
||||
|
||||
let stdinWithoutTty: boolean;
|
||||
try {
|
||||
stdinWithoutTty = !process.stdin.isTTY; // Via https://twitter.com/MylesBorins/status/782009479382626304
|
||||
} catch (error) {
|
||||
// Windows workaround for https://github.com/nodejs/node/issues/11656
|
||||
}
|
||||
|
||||
let stdinFilePath: string;
|
||||
if (stdinWithoutTty) {
|
||||
|
||||
// Read from stdin: we require a single "-" argument to be passed in order to start reading from
|
||||
// stdin. We do this because there is no reliable way to find out if data is piped to stdin. Just
|
||||
// checking for stdin being connected to a TTY is not enough (https://github.com/Microsoft/vscode/issues/40351)
|
||||
if (args._.length === 1 && args._[0] === '-') {
|
||||
|
||||
// remove the "-" argument when we read from stdin
|
||||
args._ = [];
|
||||
argv = argv.filter(a => a !== '-');
|
||||
|
||||
// prepare temp file to read stdin to
|
||||
stdinFilePath = paths.join(os.tmpdir(), `code-stdin-${Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 3)}.txt`);
|
||||
|
||||
// open tmp file for writing
|
||||
let stdinFileError: Error;
|
||||
let stdinFileStream: fs.WriteStream;
|
||||
try {
|
||||
stdinFileStream = fs.createWriteStream(stdinFilePath);
|
||||
} catch (error) {
|
||||
stdinFileError = error;
|
||||
}
|
||||
|
||||
if (!stdinFileError) {
|
||||
|
||||
// Pipe into tmp file using terminals encoding
|
||||
resolveTerminalEncoding(verbose).done(encoding => {
|
||||
const converterStream = iconv.decodeStream(encoding);
|
||||
process.stdin.pipe(converterStream).pipe(stdinFileStream);
|
||||
});
|
||||
|
||||
// Make sure to open tmp file
|
||||
argv.push(stdinFilePath);
|
||||
|
||||
// Enable --wait to get all data and ignore adding this to history
|
||||
argv.push('--wait');
|
||||
argv.push('--skip-add-to-recently-opened');
|
||||
args.wait = true;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
if (stdinFileError) {
|
||||
console.error(`Failed to create file to read via stdin: ${stdinFileError.toString()}`);
|
||||
} else {
|
||||
console.log(`Reading from stdin via: ${stdinFilePath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message
|
||||
// if we detect that data flows into via stdin after a certain timeout.
|
||||
else if (args._.length === 0) {
|
||||
processCallbacks.push(child => new TPromise(c => {
|
||||
const dataListener = () => {
|
||||
if (isWindows) {
|
||||
console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`);
|
||||
} else {
|
||||
console.log(`Run with '${product.applicationName} -' to read from stdin (e.g. 'ps aux | grep code | ${product.applicationName} -').`);
|
||||
}
|
||||
|
||||
c(void 0);
|
||||
};
|
||||
|
||||
// wait for 1s maximum...
|
||||
setTimeout(() => {
|
||||
process.stdin.removeListener('data', dataListener);
|
||||
|
||||
c(void 0);
|
||||
}, 1000);
|
||||
|
||||
// ...but finish early if we detect data
|
||||
process.stdin.once('data', dataListener);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// If we are started with --wait create a random temporary file
|
||||
@@ -73,7 +179,7 @@ export function main(argv: string[]): TPromise<void> {
|
||||
waitMarkerError = error;
|
||||
}
|
||||
|
||||
if (args.verbose) {
|
||||
if (verbose) {
|
||||
if (waitMarkerError) {
|
||||
console.error(`Failed to create marker file for --wait: ${waitMarkerError.toString()}`);
|
||||
} else {
|
||||
@@ -82,26 +188,76 @@ export function main(argv: string[]): TPromise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// If we have been started with `--prof-startup` we need to find free ports to profile
|
||||
// the main process, the renderer, and the extension host. We also disable v8 cached data
|
||||
// to get better profile traces. Last, we listen on stdout for a signal that tells us to
|
||||
// stop profiling.
|
||||
if (args['prof-startup']) {
|
||||
const portMain = await findFreePort(9222, 10, 6000);
|
||||
const portRenderer = await findFreePort(portMain + 1, 10, 6000);
|
||||
const portExthost = await findFreePort(portRenderer + 1, 10, 6000);
|
||||
|
||||
if (!portMain || !portRenderer || !portExthost) {
|
||||
console.error('Failed to find free ports for profiler to connect to do.');
|
||||
return;
|
||||
}
|
||||
|
||||
const filenamePrefix = paths.join(os.homedir(), Math.random().toString(16).slice(-4));
|
||||
|
||||
argv.push(`--inspect-brk=${portMain}`);
|
||||
argv.push(`--remote-debugging-port=${portRenderer}`);
|
||||
argv.push(`--inspect-brk-extensions=${portExthost}`);
|
||||
argv.push(`--prof-startup-prefix`, filenamePrefix);
|
||||
argv.push(`--no-cached-data`);
|
||||
|
||||
fs.writeFileSync(filenamePrefix, argv.slice(-6).join('|'));
|
||||
|
||||
processCallbacks.push(async child => {
|
||||
|
||||
// load and start profiler
|
||||
const profiler = await import('v8-inspect-profiler');
|
||||
const main = await profiler.startProfiling({ port: portMain });
|
||||
const renderer = await profiler.startProfiling({ port: portRenderer, tries: 200 });
|
||||
const extHost = await profiler.startProfiling({ port: portExthost, tries: 300 });
|
||||
|
||||
// wait for the renderer to delete the
|
||||
// marker file
|
||||
whenDeleted(filenamePrefix);
|
||||
|
||||
let profileMain = await main.stop();
|
||||
let profileRenderer = await renderer.stop();
|
||||
let profileExtHost = await extHost.stop();
|
||||
let suffix = '';
|
||||
|
||||
if (!process.env['VSCODE_DEV']) {
|
||||
// when running from a not-development-build we remove
|
||||
// absolute filenames because we don't want to reveal anything
|
||||
// about users. We also append the `.txt` suffix to make it
|
||||
// easier to attach these files to GH issues
|
||||
profileMain = profiler.rewriteAbsolutePaths(profileMain, 'piiRemoved');
|
||||
profileRenderer = profiler.rewriteAbsolutePaths(profileRenderer, 'piiRemoved');
|
||||
profileExtHost = profiler.rewriteAbsolutePaths(profileExtHost, 'piiRemoved');
|
||||
suffix = '.txt';
|
||||
}
|
||||
|
||||
// finally stop profiling and save profiles to disk
|
||||
await profiler.writeProfile(profileMain, `${filenamePrefix}-main.cpuprofile${suffix}`);
|
||||
await profiler.writeProfile(profileRenderer, `${filenamePrefix}-renderer.cpuprofile${suffix}`);
|
||||
await profiler.writeProfile(profileExtHost, `${filenamePrefix}-exthost.cpuprofile${suffix}`);
|
||||
});
|
||||
}
|
||||
|
||||
const options = {
|
||||
detached: true,
|
||||
env
|
||||
};
|
||||
|
||||
if (!args.verbose) {
|
||||
if (!verbose) {
|
||||
options['stdio'] = 'ignore';
|
||||
}
|
||||
|
||||
const child = spawn(process.execPath, argv.slice(2), options);
|
||||
|
||||
if (args.verbose) {
|
||||
child.stdout.on('data', (data: Buffer) => console.log(data.toString('utf8').trim()));
|
||||
child.stderr.on('data', (data: Buffer) => console.log(data.toString('utf8').trim()));
|
||||
}
|
||||
|
||||
if (args.verbose) {
|
||||
return new TPromise<void>(c => child.once('exit', () => c(null)));
|
||||
}
|
||||
|
||||
if (args.wait && waitMarkerFilePath) {
|
||||
return new TPromise<void>(c => {
|
||||
|
||||
@@ -110,8 +266,16 @@ export function main(argv: string[]): TPromise<void> {
|
||||
|
||||
// Complete when wait marker file is deleted
|
||||
whenDeleted(waitMarkerFilePath).done(c, c);
|
||||
}).then(() => {
|
||||
|
||||
// Make sure to delete the tmp stdin file if we have any
|
||||
if (stdinFilePath) {
|
||||
fs.unlinkSync(stdinFilePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return TPromise.join(processCallbacks.map(callback => callback(child)));
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
|
||||
@@ -7,7 +7,6 @@ import { localize } from 'vs/nls';
|
||||
import product from 'vs/platform/node/product';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { sequence } from 'vs/base/common/async';
|
||||
@@ -17,9 +16,9 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { EnvironmentService, getInstallSourcePath } from 'vs/platform/environment/node/environmentService';
|
||||
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { IExtensionManagementService, IExtensionGalleryService, IExtensionManifest, IGalleryExtension, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { ExtensionManagementService, validateLocalExtension } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
@@ -30,9 +29,15 @@ import { RequestService } from 'vs/platform/request/node/requestService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
|
||||
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
|
||||
import { mkdirp } from 'vs/base/node/pfs';
|
||||
import { mkdirp, writeFile } from 'vs/base/node/pfs';
|
||||
import { IChoiceService } from 'vs/platform/message/common/message';
|
||||
import { ChoiceCliService } from 'vs/platform/message/node/messageCli';
|
||||
import { getBaseLabel } from 'vs/base/common/labels';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import { StateService } from 'vs/platform/state/node/stateService';
|
||||
import { createLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
|
||||
const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id);
|
||||
const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id);
|
||||
@@ -76,10 +81,7 @@ class Main {
|
||||
}
|
||||
|
||||
private setInstallSource(installSource: string): TPromise<any> {
|
||||
return new TPromise<void>((c, e) => {
|
||||
const path = getInstallSourcePath(this.environmentService.userDataPath);
|
||||
fs.writeFile(path, installSource.slice(0, 30), 'utf8', err => err ? e(err) : c(null));
|
||||
});
|
||||
return writeFile(this.environmentService.installSourcePath, installSource.slice(0, 30));
|
||||
}
|
||||
|
||||
private listExtensions(showVersions: boolean): TPromise<any> {
|
||||
@@ -95,7 +97,14 @@ class Main {
|
||||
const extension = path.isAbsolute(id) ? id : path.join(process.cwd(), id);
|
||||
|
||||
return this.extensionManagementService.install(extension).then(() => {
|
||||
console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed!", path.basename(extension)));
|
||||
console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed!", getBaseLabel(extension)));
|
||||
}, error => {
|
||||
if (isPromiseCanceledError(error)) {
|
||||
console.log(localize('cancelVsixInstall', "Cancelled installing Extension '{0}'.", getBaseLabel(extension)));
|
||||
return null;
|
||||
} else {
|
||||
return TPromise.wrapError(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -134,7 +143,16 @@ class Main {
|
||||
console.log(localize('installing', "Installing..."));
|
||||
|
||||
return this.extensionManagementService.installFromGallery(extension)
|
||||
.then(() => console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.version)));
|
||||
.then(
|
||||
() => console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.version)),
|
||||
error => {
|
||||
if (isPromiseCanceledError(error)) {
|
||||
console.log(localize('cancelVsixInstall', "Cancelled installing Extension '{0}'.", id));
|
||||
return null;
|
||||
} else {
|
||||
return TPromise.wrapError(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -142,19 +160,31 @@ class Main {
|
||||
return sequence([...vsixTasks, ...galleryTasks]);
|
||||
}
|
||||
|
||||
private uninstallExtension(ids: string[]): TPromise<any> {
|
||||
return sequence(ids.map(id => () => {
|
||||
return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(installed => {
|
||||
const [extension] = installed.filter(e => getId(e.manifest) === id);
|
||||
private uninstallExtension(extensions: string[]): TPromise<any> {
|
||||
async function getExtensionId(extensionDescription: string): TPromise<string> {
|
||||
if (!/\.vsix$/i.test(extensionDescription)) {
|
||||
return extensionDescription;
|
||||
}
|
||||
|
||||
if (!extension) {
|
||||
return TPromise.wrapError(new Error(`${notInstalled(id)}\n${useId}`));
|
||||
}
|
||||
const zipPath = path.isAbsolute(extensionDescription) ? extensionDescription : path.join(process.cwd(), extensionDescription);
|
||||
const manifest = await validateLocalExtension(zipPath);
|
||||
return getId(manifest);
|
||||
}
|
||||
|
||||
console.log(localize('uninstalling', "Uninstalling {0}...", id));
|
||||
return sequence(extensions.map(extension => () => {
|
||||
return getExtensionId(extension).then(id => {
|
||||
return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(installed => {
|
||||
const [extension] = installed.filter(e => getId(e.manifest) === id);
|
||||
|
||||
return this.extensionManagementService.uninstall(extension, true)
|
||||
.then(() => console.log(localize('successUninstall', "Extension '{0}' was successfully uninstalled!", id)));
|
||||
if (!extension) {
|
||||
return TPromise.wrapError(new Error(`${notInstalled(id)}\n${useId}`));
|
||||
}
|
||||
|
||||
console.log(localize('uninstalling', "Uninstalling {0}...", id));
|
||||
|
||||
return this.extensionManagementService.uninstall(extension, true)
|
||||
.then(() => console.log(localize('successUninstall', "Extension '{0}' was successfully uninstalled!", id)));
|
||||
});
|
||||
});
|
||||
}));
|
||||
}
|
||||
@@ -164,15 +194,25 @@ const eventPrefix = 'monacoworkbench';
|
||||
|
||||
export function main(argv: ParsedArgs): TPromise<void> {
|
||||
const services = new ServiceCollection();
|
||||
services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService, argv, process.execPath));
|
||||
|
||||
const environmentService = new EnvironmentService(argv, process.execPath);
|
||||
const logService = createLogService('cli', environmentService);
|
||||
process.once('exit', () => logService.dispose());
|
||||
|
||||
logService.info('main', argv);
|
||||
|
||||
services.set(IEnvironmentService, environmentService);
|
||||
services.set(ILogService, logService);
|
||||
services.set(IStateService, new SyncDescriptor(StateService));
|
||||
|
||||
const instantiationService: IInstantiationService = new InstantiationService(services);
|
||||
|
||||
return instantiationService.invokeFunction(accessor => {
|
||||
const envService = accessor.get(IEnvironmentService);
|
||||
const stateService = accessor.get(IStateService);
|
||||
|
||||
return TPromise.join([envService.appSettingsHome, envService.extensionsPath].map(p => mkdirp(p))).then(() => {
|
||||
const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, installSource } = envService;
|
||||
const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, installSourcePath } = envService;
|
||||
|
||||
const services = new ServiceCollection();
|
||||
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
|
||||
@@ -194,7 +234,7 @@ export function main(argv: ParsedArgs): TPromise<void> {
|
||||
|
||||
const config: ITelemetryServiceConfig = {
|
||||
appender: combinedAppender(...appenders),
|
||||
commonProperties: resolveCommonProperties(product.commit, pkg.version, installSource),
|
||||
commonProperties: resolveCommonProperties(product.commit, pkg.version, stateService.getItem('telemetry.machineId'), installSourcePath),
|
||||
piiPaths: [appRoot, extensionsPath]
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user