mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-25 09:35:37 -05:00
Merge VS Code 1.31.1 (#4283)
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol } from 'electron';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor } from 'electron';
|
||||
import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform';
|
||||
import { WindowsManager } from 'vs/code/electron-main/windows';
|
||||
import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows';
|
||||
import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc';
|
||||
@@ -38,7 +38,6 @@ import pkg from 'vs/platform/node/package';
|
||||
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IHistoryMainService } from 'vs/platform/history/common/history';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
@@ -61,11 +60,10 @@ import { connectRemoteAgentManagement, RemoteAgentConnectionContext } from 'vs/p
|
||||
import { IMenubarService } from 'vs/platform/menubar/common/menubar';
|
||||
import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService';
|
||||
import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc';
|
||||
import { ILabelService, RegisterFormatterEvent } from 'vs/platform/label/common/label';
|
||||
import { hasArgs } from 'vs/platform/environment/node/argv';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu';
|
||||
import { THEME_STORAGE_KEY, THEME_BG_STORAGE_KEY } from 'vs/code/electron-main/theme';
|
||||
import { storeBackgroundColor } from 'vs/code/electron-main/theme';
|
||||
import { nativeSep, join } from 'vs/base/common/paths';
|
||||
import { homedir } from 'os';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -76,9 +74,12 @@ import { SnapUpdateService } from 'vs/platform/update/electron-main/updateServic
|
||||
import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService';
|
||||
import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { CodeMenu } from 'sql/workbench/electron-browser/menus';
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
|
||||
import { IBackupMainService } from 'vs/platform/backup/common/backup';
|
||||
import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService';
|
||||
import { URLService } from 'vs/platform/url/common/urlService';
|
||||
import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
|
||||
|
||||
export class CodeApplication extends Disposable {
|
||||
|
||||
@@ -89,19 +90,17 @@ export class CodeApplication extends Disposable {
|
||||
private electronIpcServer: ElectronIPCServer;
|
||||
|
||||
private sharedProcess: SharedProcess;
|
||||
private sharedProcessClient: TPromise<Client>;
|
||||
private sharedProcessClient: Promise<Client>;
|
||||
|
||||
constructor(
|
||||
private mainIpcServer: Server,
|
||||
private userEnv: platform.IProcessEnvironment,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@ILogService private logService: ILogService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@ILifecycleService private lifecycleService: ILifecycleService,
|
||||
@IConfigurationService private configurationService: ConfigurationService,
|
||||
@IStateService private stateService: IStateService,
|
||||
@IHistoryMainService private historyMainService: IHistoryMainService,
|
||||
@ILabelService private labelService: ILabelService
|
||||
private userEnv: IProcessEnvironment,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@ILifecycleService private readonly lifecycleService: ILifecycleService,
|
||||
@IConfigurationService private readonly configurationService: ConfigurationService,
|
||||
@IStateService private readonly stateService: IStateService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -144,12 +143,27 @@ export class CodeApplication extends Disposable {
|
||||
app.on('web-contents-created', (event: any, contents) => {
|
||||
contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => {
|
||||
|
||||
const isValidWebviewSource = (source: string): boolean => {
|
||||
if (!source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') {
|
||||
return true;
|
||||
}
|
||||
|
||||
const srcUri = URI.parse(source).fsPath.toLowerCase();
|
||||
const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();
|
||||
|
||||
return startsWith(srcUri, rootUri + nativeSep);
|
||||
};
|
||||
|
||||
// Ensure defaults
|
||||
delete webPreferences.preload;
|
||||
webPreferences.nodeIntegration = false;
|
||||
|
||||
// Verify URLs being loaded
|
||||
if (this.isValidWebviewSource(params.src) && this.isValidWebviewSource(webPreferences.preloadURL)) {
|
||||
if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -174,85 +188,6 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
});
|
||||
|
||||
const connectionPool: Map<string, ActiveConnection> = new Map<string, ActiveConnection>();
|
||||
|
||||
class ActiveConnection {
|
||||
private _authority: string;
|
||||
private _client: TPromise<Client<RemoteAgentConnectionContext>>;
|
||||
private _disposeRunner: RunOnceScheduler;
|
||||
|
||||
constructor(authority: string, host: string, port: number) {
|
||||
this._authority = authority;
|
||||
this._client = connectRemoteAgentManagement(authority, host, port, `main`);
|
||||
this._disposeRunner = new RunOnceScheduler(() => this._dispose(), 5000);
|
||||
}
|
||||
|
||||
private _dispose(): void {
|
||||
this._disposeRunner.dispose();
|
||||
connectionPool.delete(this._authority);
|
||||
this._client.then((connection) => {
|
||||
connection.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
public getClient(): TPromise<Client<RemoteAgentConnectionContext>> {
|
||||
this._disposeRunner.schedule();
|
||||
return this._client;
|
||||
}
|
||||
}
|
||||
|
||||
const resolvedAuthorities = new Map<string, ResolvedAuthority>();
|
||||
ipc.on('vscode:remoteAuthorityResolved', (event: any, data: ResolvedAuthority) => {
|
||||
resolvedAuthorities.set(data.authority, data);
|
||||
});
|
||||
const resolveAuthority = (authority: string): ResolvedAuthority | null => {
|
||||
if (authority.indexOf('+') >= 0) {
|
||||
if (resolvedAuthorities.has(authority)) {
|
||||
return resolvedAuthorities.get(authority);
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
const [host, strPort] = authority.split(':');
|
||||
const port = parseInt(strPort, 10);
|
||||
return { authority, host, port, syncExtensions: false };
|
||||
}
|
||||
};
|
||||
|
||||
protocol.registerBufferProtocol(REMOTE_HOST_SCHEME, async (request, callback) => {
|
||||
if (request.method !== 'GET') {
|
||||
return callback(null);
|
||||
}
|
||||
const uri = URI.parse(request.url);
|
||||
|
||||
let activeConnection: ActiveConnection = null;
|
||||
if (connectionPool.has(uri.authority)) {
|
||||
activeConnection = connectionPool.get(uri.authority);
|
||||
} else {
|
||||
let resolvedAuthority = resolveAuthority(uri.authority);
|
||||
if (!resolvedAuthority) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port);
|
||||
connectionPool.set(uri.authority, activeConnection);
|
||||
}
|
||||
try {
|
||||
const rawClient = await activeConnection.getClient();
|
||||
if (connectionPool.has(uri.authority)) { // not disposed in the meantime
|
||||
const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
|
||||
|
||||
// TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider`
|
||||
const fileContents = await channel.call<Uint8Array>('readFile', [uri]);
|
||||
callback(Buffer.from(fileContents));
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
} catch (err) {
|
||||
errors.onUnexpectedError(err);
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
|
||||
let macOpenFileURIs: URI[] = [];
|
||||
let runningTimeout: any = null;
|
||||
app.on('open-file', (event: Event, path: string) => {
|
||||
@@ -321,44 +256,18 @@ export class CodeApplication extends Disposable {
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('vscode:labelRegisterFormatter', (event: any, data: RegisterFormatterEvent) => {
|
||||
this.labelService.registerFormatter(data.selector, data.formatter);
|
||||
});
|
||||
ipc.on('vscode:toggleDevTools', (event: Event) => event.sender.toggleDevTools());
|
||||
ipc.on('vscode:openDevTools', (event: Event) => event.sender.openDevTools());
|
||||
|
||||
ipc.on('vscode:toggleDevTools', (event: Event) => {
|
||||
event.sender.toggleDevTools();
|
||||
});
|
||||
ipc.on('vscode:reloadWindow', (event: Event) => event.sender.reload());
|
||||
|
||||
ipc.on('vscode:openDevTools', (event: Event) => {
|
||||
event.sender.openDevTools();
|
||||
});
|
||||
|
||||
ipc.on('vscode:reloadWindow', (event: Event) => {
|
||||
event.sender.reload();
|
||||
});
|
||||
|
||||
// Keyboard layout changes
|
||||
KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => {
|
||||
powerMonitor.on('resume', () => { // After waking up from sleep
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
|
||||
this.windowsMainService.sendToAll('vscode:osResume', undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private isValidWebviewSource(source: string): boolean {
|
||||
if (!source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') {
|
||||
return true;
|
||||
}
|
||||
|
||||
const srcUri: any = URI.parse(source).fsPath.toLowerCase();
|
||||
const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();
|
||||
return srcUri.startsWith(rootUri + nativeSep);
|
||||
}
|
||||
|
||||
private onUnexpectedError(err: Error): void {
|
||||
if (err) {
|
||||
|
||||
@@ -384,14 +293,11 @@ export class CodeApplication extends Disposable {
|
||||
|
||||
// Theme changes
|
||||
if (event === 'vscode:changeColorTheme' && typeof payload === 'string') {
|
||||
let data = JSON.parse(payload);
|
||||
|
||||
this.stateService.setItem(THEME_STORAGE_KEY, data.baseTheme);
|
||||
this.stateService.setItem(THEME_BG_STORAGE_KEY, data.background);
|
||||
storeBackgroundColor(this.stateService, JSON.parse(payload));
|
||||
}
|
||||
}
|
||||
|
||||
startup(): TPromise<void> {
|
||||
startup(): Promise<void> {
|
||||
this.logService.debug('Starting VS Code');
|
||||
this.logService.debug(`from: ${this.environmentService.appRoot}`);
|
||||
this.logService.debug('args:', this.environmentService.args);
|
||||
@@ -400,7 +306,7 @@ export class CodeApplication extends Disposable {
|
||||
// This will help Windows to associate the running program with
|
||||
// any shortcut that is pinned to the taskbar and prevent showing
|
||||
// two icons in the taskbar for the same app.
|
||||
if (platform.isWindows && product.win32AppUserModelId) {
|
||||
if (isWindows && product.win32AppUserModelId) {
|
||||
app.setAppUserModelId(product.win32AppUserModelId);
|
||||
}
|
||||
|
||||
@@ -411,7 +317,7 @@ export class CodeApplication extends Disposable {
|
||||
// Explicitly opt out of the patch here before creating any windows.
|
||||
// See: https://github.com/Microsoft/vscode/issues/35361#issuecomment-399794085
|
||||
try {
|
||||
if (platform.isMacintosh && this.configurationService.getValue<boolean>('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) {
|
||||
if (isMacintosh && this.configurationService.getValue<boolean>('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) {
|
||||
systemPreferences.setUserDefault('NSUseImprovedLayoutPass', 'boolean', true as any);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -421,9 +327,7 @@ export class CodeApplication extends Disposable {
|
||||
// Create Electron IPC Server
|
||||
this.electronIpcServer = new ElectronIPCServer();
|
||||
|
||||
// Resolve unique machine ID
|
||||
this.logService.trace('Resolving machine identifier...');
|
||||
return this.resolveMachineId().then(machineId => {
|
||||
const startupWithMachineId = (machineId: string) => {
|
||||
this.logService.trace(`Resolved machine identifier: ${machineId}`);
|
||||
|
||||
// Spawn shared process
|
||||
@@ -456,6 +360,28 @@ export class CodeApplication extends Disposable {
|
||||
this.stopTracingEventually(windows);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Resolve unique machine ID
|
||||
this.logService.trace('Resolving machine identifier...');
|
||||
const resolvedMachineId = this.resolveMachineId();
|
||||
if (typeof resolvedMachineId === 'string') {
|
||||
return startupWithMachineId(resolvedMachineId);
|
||||
} else {
|
||||
return resolvedMachineId.then(machineId => startupWithMachineId(machineId));
|
||||
}
|
||||
}
|
||||
|
||||
private resolveMachineId(): string | Promise<string> {
|
||||
const machineId = this.stateService.getItem<string>(CodeApplication.MACHINE_ID_KEY);
|
||||
if (machineId) {
|
||||
return machineId;
|
||||
}
|
||||
|
||||
return getMachineId().then(machineId => {
|
||||
this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId);
|
||||
|
||||
return machineId;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -488,35 +414,20 @@ export class CodeApplication extends Disposable {
|
||||
const timeoutHandle = setTimeout(() => stopRecording(true), 30000);
|
||||
|
||||
// Wait for all windows to get ready and stop tracing then
|
||||
TPromise.join(windows.map(window => window.ready())).then(() => {
|
||||
Promise.all(windows.map(window => window.ready())).then(() => {
|
||||
clearTimeout(timeoutHandle);
|
||||
stopRecording(false);
|
||||
});
|
||||
}
|
||||
|
||||
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): Thenable<IInstantiationService> {
|
||||
private initServices(machineId: string): Promise<IInstantiationService> {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
|
||||
} else if (process.platform === 'linux') {
|
||||
if (process.env.SNAP && process.env.SNAP_REVISION) {
|
||||
services.set(IUpdateService, new SyncDescriptor(SnapUpdateService));
|
||||
services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION]));
|
||||
} else {
|
||||
services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
|
||||
}
|
||||
@@ -530,6 +441,10 @@ export class CodeApplication extends Disposable {
|
||||
services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv]));
|
||||
services.set(IMenubarService, new SyncDescriptor(MenubarService));
|
||||
services.set(IStorageMainService, new SyncDescriptor(StorageMainService));
|
||||
services.set(IBackupMainService, new SyncDescriptor(BackupMainService));
|
||||
services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService));
|
||||
services.set(IURLService, new SyncDescriptor(URLService));
|
||||
services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService));
|
||||
|
||||
// Telemetry
|
||||
if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
|
||||
@@ -546,17 +461,20 @@ export class CodeApplication extends Disposable {
|
||||
|
||||
const appInstantiationService = this.instantiationService.createChild(services);
|
||||
|
||||
return appInstantiationService.invokeFunction(accessor => this.initStorageService(accessor)).then(() => appInstantiationService);
|
||||
return appInstantiationService.invokeFunction(accessor => Promise.all([
|
||||
this.initStorageService(accessor),
|
||||
this.initBackupService(accessor)
|
||||
])).then(() => appInstantiationService);
|
||||
}
|
||||
|
||||
private initStorageService(accessor: ServicesAccessor): Thenable<void> {
|
||||
private initStorageService(accessor: ServicesAccessor): Promise<void> {
|
||||
const storageMainService = accessor.get(IStorageMainService) as StorageMainService;
|
||||
|
||||
// Ensure to close storage on shutdown
|
||||
this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close()));
|
||||
|
||||
// Initialize storage service
|
||||
return storageMainService.initialize().then(void 0, error => {
|
||||
return storageMainService.initialize().then(undefined, error => {
|
||||
errors.onUnexpectedError(error);
|
||||
this.logService.error(error);
|
||||
}).then(() => {
|
||||
@@ -586,6 +504,12 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private initBackupService(accessor: ServicesAccessor): Promise<void> {
|
||||
const backupMainService = accessor.get(IBackupMainService) as BackupMainService;
|
||||
|
||||
return backupMainService.initialize();
|
||||
}
|
||||
|
||||
private openFirstWindow(accessor: ServicesAccessor): ICodeWindow[] {
|
||||
const appInstantiationService = accessor.get(IInstantiationService);
|
||||
|
||||
@@ -635,8 +559,6 @@ export class CodeApplication extends Disposable {
|
||||
// Propagate to clients
|
||||
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this
|
||||
|
||||
const args = this.environmentService.args;
|
||||
|
||||
// Create a URL handler which forwards to the last active window
|
||||
const activeWindowManager = new ActiveWindowManager(windowsService);
|
||||
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
|
||||
@@ -645,11 +567,11 @@ export class CodeApplication extends Disposable {
|
||||
|
||||
// On Mac, Code can be running without any open windows, so we must create a window to handle urls,
|
||||
// if there is none
|
||||
if (platform.isMacintosh) {
|
||||
if (isMacintosh) {
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
|
||||
urlService.registerHandler({
|
||||
handleURL(uri: URI): TPromise<boolean> {
|
||||
handleURL(uri: URI): Promise<boolean> {
|
||||
if (windowsMainService.getWindowCount() === 0) {
|
||||
const cli = { ...environmentService.args, goto: true };
|
||||
const [window] = windowsMainService.open({ context: OpenContext.API, cli, forceEmpty: true });
|
||||
@@ -657,7 +579,7 @@ export class CodeApplication extends Disposable {
|
||||
return window.ready().then(() => urlService.open(uri));
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -666,6 +588,7 @@ export class CodeApplication extends Disposable {
|
||||
urlService.registerHandler(multiplexURLHandler);
|
||||
|
||||
// Watch Electron URLs and forward them to the UrlService
|
||||
const args = this.environmentService.args;
|
||||
const urls = args['open-url'] ? args._urls : [];
|
||||
const urlListener = new ElectronURLListener(urls, urlService, this.windowsMainService);
|
||||
this._register(urlListener);
|
||||
@@ -692,9 +615,10 @@ export class CodeApplication extends Disposable {
|
||||
|
||||
private afterWindowOpen(accessor: ServicesAccessor): void {
|
||||
const windowsMainService = accessor.get(IWindowsMainService);
|
||||
const historyMainService = accessor.get(IHistoryMainService);
|
||||
|
||||
let windowsMutex: Mutex | null = null;
|
||||
if (platform.isWindows) {
|
||||
if (isWindows) {
|
||||
|
||||
// Setup Windows mutex
|
||||
try {
|
||||
@@ -716,7 +640,7 @@ export class CodeApplication extends Disposable {
|
||||
// Ensure Windows foreground love module
|
||||
try {
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
<any>require.__$__nodeRequire('windows-foreground-love');
|
||||
require.__$__nodeRequire('windows-foreground-love');
|
||||
} catch (e) {
|
||||
if (!this.environmentService.isBuilt) {
|
||||
windowsMainService.showMessageBox({
|
||||
@@ -730,21 +654,105 @@ export class CodeApplication extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} - Use static menu for now
|
||||
// Install Menu
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
if (platform.isMacintosh || configurationService.getValue<string>('window.titleBarStyle') !== 'custom') {
|
||||
instantiationService.createInstance(CodeMenu);
|
||||
}
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
// Remote Authorities
|
||||
this.handleRemoteAuthorities();
|
||||
|
||||
// Keyboard layout changes
|
||||
KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => {
|
||||
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
|
||||
});
|
||||
|
||||
// Jump List
|
||||
this.historyMainService.updateWindowsJumpList();
|
||||
this.historyMainService.onRecentlyOpenedChange(() => this.historyMainService.updateWindowsJumpList());
|
||||
historyMainService.updateWindowsJumpList();
|
||||
historyMainService.onRecentlyOpenedChange(() => historyMainService.updateWindowsJumpList());
|
||||
|
||||
// Start shared process after a while
|
||||
const sharedProcessSpawn = this._register(new RunOnceScheduler(() => getShellEnvironment().then(userEnv => this.sharedProcess.spawn(userEnv)), 3000));
|
||||
sharedProcessSpawn.schedule();
|
||||
}
|
||||
|
||||
private handleRemoteAuthorities(): void {
|
||||
const connectionPool: Map<string, ActiveConnection> = new Map<string, ActiveConnection>();
|
||||
|
||||
const isBuilt = this.environmentService.isBuilt;
|
||||
|
||||
class ActiveConnection {
|
||||
private _authority: string;
|
||||
private _client: Promise<Client<RemoteAgentConnectionContext>>;
|
||||
private _disposeRunner: RunOnceScheduler;
|
||||
|
||||
constructor(authority: string, host: string, port: number) {
|
||||
this._authority = authority;
|
||||
this._client = connectRemoteAgentManagement(authority, host, port, `main`, isBuilt);
|
||||
this._disposeRunner = new RunOnceScheduler(() => this._dispose(), 5000);
|
||||
}
|
||||
|
||||
private _dispose(): void {
|
||||
this._disposeRunner.dispose();
|
||||
connectionPool.delete(this._authority);
|
||||
this._client.then((connection) => {
|
||||
connection.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
public getClient(): Promise<Client<RemoteAgentConnectionContext>> {
|
||||
this._disposeRunner.schedule();
|
||||
return this._client;
|
||||
}
|
||||
}
|
||||
|
||||
const resolvedAuthorities = new Map<string, ResolvedAuthority>();
|
||||
ipc.on('vscode:remoteAuthorityResolved', (event: any, data: ResolvedAuthority) => {
|
||||
resolvedAuthorities.set(data.authority, data);
|
||||
});
|
||||
|
||||
const resolveAuthority = (authority: string): ResolvedAuthority | null => {
|
||||
if (authority.indexOf('+') >= 0) {
|
||||
if (resolvedAuthorities.has(authority)) {
|
||||
return resolvedAuthorities.get(authority);
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
const [host, strPort] = authority.split(':');
|
||||
const port = parseInt(strPort, 10);
|
||||
return { authority, host, port, syncExtensions: false };
|
||||
}
|
||||
};
|
||||
|
||||
protocol.registerBufferProtocol(REMOTE_HOST_SCHEME, async (request, callback) => {
|
||||
if (request.method !== 'GET') {
|
||||
return callback(null);
|
||||
}
|
||||
const uri = URI.parse(request.url);
|
||||
|
||||
let activeConnection: ActiveConnection = null;
|
||||
if (connectionPool.has(uri.authority)) {
|
||||
activeConnection = connectionPool.get(uri.authority);
|
||||
} else {
|
||||
let resolvedAuthority = resolveAuthority(uri.authority);
|
||||
if (!resolvedAuthority) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port);
|
||||
connectionPool.set(uri.authority, activeConnection);
|
||||
}
|
||||
try {
|
||||
const rawClient = await activeConnection.getClient();
|
||||
if (connectionPool.has(uri.authority)) { // not disposed in the meantime
|
||||
const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
|
||||
|
||||
// TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider`
|
||||
const fileContents = await channel.call<Uint8Array>('readFile', [uri]);
|
||||
callback(Buffer.from(fileContents));
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
} catch (err) {
|
||||
errors.onUnexpectedError(err);
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
|
||||
import { fromNodeEventEmitter } from 'vs/base/common/event';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { BrowserWindow, app } from 'electron';
|
||||
|
||||
type LoginEvent = {
|
||||
@@ -30,9 +30,9 @@ export class ProxyAuthHandler {
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@IWindowsMainService private windowsMainService: IWindowsMainService
|
||||
@IWindowsMainService private readonly windowsMainService: IWindowsMainService
|
||||
) {
|
||||
const onLogin = fromNodeEventEmitter<LoginEvent>(app, 'login', (event, webContents, req, authInfo, cb) => ({ event, webContents, req, authInfo, cb }));
|
||||
const onLogin = Event.fromNodeEventEmitter<LoginEvent>(app, 'login', (event, webContents, req, authInfo, cb) => ({ event, webContents, req, authInfo, cb }));
|
||||
onLogin(this.onLogin, this, this.disposables);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,8 @@ 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, readdir, rimraf } from 'vs/base/node/pfs';
|
||||
import { parseMainProcessArgv } from 'vs/platform/environment/node/argvHelper';
|
||||
import { mkdirp } 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';
|
||||
@@ -22,22 +21,14 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { ILogService, ConsoleLogMainService, MultiplexLogService, getLogLevel } 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';
|
||||
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
|
||||
import { IRequestService } from 'vs/platform/request/node/request';
|
||||
import { RequestService } from 'vs/platform/request/electron-main/requestService';
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import { URLService } from 'vs/platform/url/common/urlService';
|
||||
import * as fs from 'fs';
|
||||
import { CodeApplication } from 'vs/code/electron-main/app';
|
||||
import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService';
|
||||
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 { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
@@ -45,75 +36,19 @@ import { IDiagnosticsService, DiagnosticsService } from 'vs/platform/diagnostics
|
||||
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
|
||||
import { uploadLogs } from 'vs/code/electron-main/logUploader';
|
||||
import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { CommandLineDialogService } from 'vs/platform/dialogs/node/dialogService';
|
||||
import { ILabelService, LabelService } from 'vs/platform/label/common/label';
|
||||
import { createWaitMarkerFile } from 'vs/code/node/wait';
|
||||
|
||||
function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
const environmentService = new EnvironmentService(args, process.execPath);
|
||||
|
||||
const logService = new MultiplexLogService([new ConsoleLogMainService(getLogLevel(environmentService)), bufferLogService]);
|
||||
process.once('exit', () => logService.dispose());
|
||||
setTimeout(() => cleanupOlderLogs(environmentService).then(null, err => console.error(err)), 10000);
|
||||
|
||||
services.set(IEnvironmentService, environmentService);
|
||||
services.set(ILabelService, new LabelService(environmentService, void 0, void 0));
|
||||
services.set(ILogService, logService);
|
||||
services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService));
|
||||
services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService));
|
||||
services.set(ILifecycleService, new SyncDescriptor(LifecycleService));
|
||||
services.set(IStateService, new SyncDescriptor(StateService));
|
||||
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
|
||||
services.set(IRequestService, new SyncDescriptor(RequestService));
|
||||
services.set(IURLService, new SyncDescriptor(URLService));
|
||||
services.set(IBackupMainService, new SyncDescriptor(BackupMainService));
|
||||
services.set(IDialogService, new SyncDescriptor(CommandLineDialogService));
|
||||
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService));
|
||||
|
||||
return new InstantiationService(services, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up older logs, while keeping the 10 most recent ones.
|
||||
*/
|
||||
async function cleanupOlderLogs(environmentService: EnvironmentService): Promise<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 Promise.all(toDelete.map(name => rimraf(path.join(logsRoot, name))));
|
||||
}
|
||||
|
||||
function createPaths(environmentService: IEnvironmentService): Thenable<any> {
|
||||
const paths = [
|
||||
environmentService.extensionsPath,
|
||||
environmentService.nodeCachedDataDir,
|
||||
environmentService.logsPath,
|
||||
environmentService.globalStorageHome,
|
||||
environmentService.workspaceStorageHome
|
||||
];
|
||||
|
||||
return Promise.all(paths.map(path => path && mkdirp(path)));
|
||||
}
|
||||
|
||||
class ExpectedError extends Error {
|
||||
public readonly isExpected = true;
|
||||
readonly isExpected = true;
|
||||
}
|
||||
|
||||
function setupIPC(accessor: ServicesAccessor): Thenable<Server> {
|
||||
function setupIPC(accessor: ServicesAccessor): Promise<Server> {
|
||||
const logService = accessor.get(ILogService);
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
const requestService = accessor.get(IRequestService);
|
||||
const diagnosticsService = accessor.get(IDiagnosticsService);
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
|
||||
function allowSetForegroundWindow(service: LaunchChannelClient): Thenable<void> {
|
||||
let promise: Thenable<void> = Promise.resolve();
|
||||
function allowSetForegroundWindow(service: LaunchChannelClient): Promise<void> {
|
||||
let promise: Promise<void> = Promise.resolve();
|
||||
if (platform.isWindows) {
|
||||
promise = service.getMainProcessId()
|
||||
.then(processId => {
|
||||
@@ -131,7 +66,7 @@ function setupIPC(accessor: ServicesAccessor): Thenable<Server> {
|
||||
return promise;
|
||||
}
|
||||
|
||||
function setup(retry: boolean): Thenable<Server> {
|
||||
function setup(retry: boolean): Promise<Server> {
|
||||
return serve(environmentService.mainIPCHandle).then(server => {
|
||||
|
||||
// Print --status usage info
|
||||
@@ -157,7 +92,15 @@ function setupIPC(accessor: ServicesAccessor): Thenable<Server> {
|
||||
|
||||
return server;
|
||||
}, err => {
|
||||
|
||||
// Handle unexpected errors (the only expected error is EADDRINUSE that
|
||||
// indicates a second instance of Code is running)
|
||||
if (err.code !== 'EADDRINUSE') {
|
||||
|
||||
// Show a dialog for errors that can be resolved by the user
|
||||
handleStartupDataDirError(environmentService, err);
|
||||
|
||||
// Any other runtime error is just printed to the console
|
||||
return Promise.reject<Server>(err);
|
||||
}
|
||||
|
||||
@@ -198,14 +141,18 @@ function setupIPC(accessor: ServicesAccessor): Thenable<Server> {
|
||||
// Process Info
|
||||
if (environmentService.args.status) {
|
||||
return service.getMainProcessInfo().then(info => {
|
||||
return diagnosticsService.printDiagnostics(info).then(() => Promise.reject(new ExpectedError()));
|
||||
return instantiationService.invokeFunction(accessor => {
|
||||
return accessor.get(IDiagnosticsService).printDiagnostics(info).then(() => Promise.reject(new ExpectedError()));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Log uploader
|
||||
if (typeof environmentService.args['upload-logs'] !== 'undefined') {
|
||||
return uploadLogs(service, requestService, environmentService)
|
||||
.then(() => Promise.reject(new ExpectedError()));
|
||||
return instantiationService.invokeFunction(accessor => {
|
||||
return uploadLogs(service, accessor.get(IRequestService), environmentService)
|
||||
.then(() => Promise.reject(new ExpectedError()));
|
||||
});
|
||||
}
|
||||
|
||||
logService.trace('Sending env to running instance...');
|
||||
@@ -265,6 +212,15 @@ function showStartupWarningDialog(message: string, detail: string): void {
|
||||
});
|
||||
}
|
||||
|
||||
function handleStartupDataDirError(environmentService: IEnvironmentService, error): void {
|
||||
if (error.code === 'EACCES' || error.code === 'EPERM') {
|
||||
showStartupWarningDialog(
|
||||
localize('startupDataDirError', "Unable to write program user data."),
|
||||
localize('startupDataDirErrorDetail', "Please make sure the directory {0} is writeable.", environmentService.userDataPath)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void {
|
||||
const logService = accessor.get(ILogService);
|
||||
const lifecycleService = accessor.get(ILifecycleService);
|
||||
@@ -316,14 +272,20 @@ function startup(args: ParsedArgs): void {
|
||||
const instantiationService = createServices(args, bufferLogService);
|
||||
instantiationService.invokeFunction(accessor => {
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
const stateService = accessor.get(IStateService);
|
||||
|
||||
// Patch `process.env` with the instance's environment
|
||||
const instanceEnvironment = patchEnvironment(environmentService);
|
||||
|
||||
// Startup
|
||||
return instantiationService
|
||||
.invokeFunction(a => createPaths(a.get(IEnvironmentService)))
|
||||
.then(() => instantiationService.invokeFunction(setupIPC))
|
||||
return initServices(environmentService, stateService as StateService)
|
||||
.then(() => instantiationService.invokeFunction(setupIPC), error => {
|
||||
|
||||
// Show a dialog for errors that can be resolved by the user
|
||||
handleStartupDataDirError(environmentService, error);
|
||||
|
||||
return Promise.reject(error);
|
||||
})
|
||||
.then(mainIpcServer => {
|
||||
bufferLogService.logger = createSpdLogService('main', bufferLogService.getLevel(), environmentService.logsPath);
|
||||
|
||||
@@ -332,6 +294,43 @@ function startup(args: ParsedArgs): void {
|
||||
}).then(null, err => instantiationService.invokeFunction(quit, err));
|
||||
}
|
||||
|
||||
function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
const environmentService = new EnvironmentService(args, process.execPath);
|
||||
|
||||
const logService = new MultiplexLogService([new ConsoleLogMainService(getLogLevel(environmentService)), bufferLogService]);
|
||||
process.once('exit', () => logService.dispose());
|
||||
|
||||
services.set(IEnvironmentService, environmentService);
|
||||
services.set(ILogService, logService);
|
||||
services.set(ILifecycleService, new SyncDescriptor(LifecycleService));
|
||||
services.set(IStateService, new SyncDescriptor(StateService));
|
||||
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
|
||||
services.set(IRequestService, new SyncDescriptor(RequestService));
|
||||
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService));
|
||||
|
||||
return new InstantiationService(services, true);
|
||||
}
|
||||
|
||||
function initServices(environmentService: IEnvironmentService, stateService: StateService): Promise<any> {
|
||||
|
||||
// Ensure paths for environment service exist
|
||||
const environmentServiceInitialization = Promise.all([
|
||||
environmentService.extensionsPath,
|
||||
environmentService.nodeCachedDataDir,
|
||||
environmentService.logsPath,
|
||||
environmentService.globalStorageHome,
|
||||
environmentService.workspaceStorageHome,
|
||||
environmentService.backupHome
|
||||
].map(path => path && mkdirp(path)));
|
||||
|
||||
// State service
|
||||
const stateServiceInitialization = stateService.init();
|
||||
|
||||
return Promise.all([environmentServiceInitialization, stateServiceInitialization]);
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
|
||||
// Set the error handler early enough so that we are not getting the
|
||||
@@ -347,7 +346,7 @@ function main(): void {
|
||||
console.error(err.message);
|
||||
app.exit(1);
|
||||
|
||||
return void 0;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If we are started with --wait create a random temporary file
|
||||
|
||||
@@ -105,7 +105,7 @@ export class SharedProcess implements ISharedProcess {
|
||||
});
|
||||
|
||||
disposables.push(toDisposable(() => sender.send('handshake:goodbye')));
|
||||
ipcMain.once('handshake:im ready', () => c(void 0));
|
||||
ipcMain.once('handshake:im ready', () => c(undefined));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,12 +7,17 @@ import { isWindows, isMacintosh } from 'vs/base/common/platform';
|
||||
import { systemPreferences } from 'electron';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
|
||||
export const DEFAULT_BG_LIGHT = '#FFFFFF';
|
||||
export const DEFAULT_BG_DARK = '#1E1E1E';
|
||||
export const DEFAULT_BG_HC_BLACK = '#000000';
|
||||
const DEFAULT_BG_LIGHT = '#FFFFFF';
|
||||
const DEFAULT_BG_DARK = '#1E1E1E';
|
||||
const DEFAULT_BG_HC_BLACK = '#000000';
|
||||
|
||||
export const THEME_STORAGE_KEY = 'theme';
|
||||
export const THEME_BG_STORAGE_KEY = 'themeBackground';
|
||||
const THEME_STORAGE_KEY = 'theme';
|
||||
const THEME_BG_STORAGE_KEY = 'themeBackground';
|
||||
|
||||
export function storeBackgroundColor(stateService: IStateService, data: { baseTheme: string, background: string }): void {
|
||||
stateService.setItem(THEME_STORAGE_KEY, data.baseTheme);
|
||||
stateService.setItem(THEME_BG_STORAGE_KEY, data.background);
|
||||
}
|
||||
|
||||
export function getBackgroundColor(stateService: IStateService): string {
|
||||
if (isWindows && systemPreferences.isInvertedColorScheme()) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import * as perf from 'vs/base/common/performance';
|
||||
import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/node/extensionGalleryService';
|
||||
import { getBackgroundColor } from 'vs/code/electron-main/theme';
|
||||
import { IStorageMainService } from 'vs/platform/storage/node/storageMainService';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
export interface IWindowCreationOptions {
|
||||
state: IWindowState;
|
||||
@@ -74,19 +75,19 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
private currentConfig: IWindowConfiguration;
|
||||
private pendingLoadConfig: IWindowConfiguration;
|
||||
|
||||
private marketplaceHeadersPromise: Thenable<object>;
|
||||
private marketplaceHeadersPromise: Promise<object>;
|
||||
|
||||
private touchBarGroups: Electron.TouchBarSegmentedControl[];
|
||||
|
||||
constructor(
|
||||
config: IWindowCreationOptions,
|
||||
@ILogService private logService: ILogService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IStateService private stateService: IStateService,
|
||||
@IWorkspacesMainService private workspacesMainService: IWorkspacesMainService,
|
||||
@IBackupMainService private backupMainService: IBackupMainService,
|
||||
@IStorageMainService private storageMainService: IStorageMainService
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IStateService private readonly stateService: IStateService,
|
||||
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
|
||||
@IBackupMainService private readonly backupMainService: IBackupMainService,
|
||||
@IStorageMainService private readonly storageMainService: IStorageMainService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -256,19 +257,19 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
}
|
||||
|
||||
get backupPath(): string {
|
||||
return this.currentConfig ? this.currentConfig.backupPath : void 0;
|
||||
return this.currentConfig ? this.currentConfig.backupPath : undefined;
|
||||
}
|
||||
|
||||
get openedWorkspace(): IWorkspaceIdentifier {
|
||||
return this.currentConfig ? this.currentConfig.workspace : void 0;
|
||||
return this.currentConfig ? this.currentConfig.workspace : undefined;
|
||||
}
|
||||
|
||||
get openedFolderUri(): URI {
|
||||
return this.currentConfig ? this.currentConfig.folderUri : void 0;
|
||||
return this.currentConfig ? this.currentConfig.folderUri : undefined;
|
||||
}
|
||||
|
||||
get remoteAuthority(): string {
|
||||
return this.currentConfig ? this.currentConfig.remoteAuthority : void 0;
|
||||
return this.currentConfig ? this.currentConfig.remoteAuthority : undefined;
|
||||
}
|
||||
|
||||
setReady(): void {
|
||||
@@ -276,11 +277,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
|
||||
// inform all waiting promises that we are ready now
|
||||
while (this.whenReadyCallbacks.length) {
|
||||
this.whenReadyCallbacks.pop()(this);
|
||||
this.whenReadyCallbacks.pop()!(this);
|
||||
}
|
||||
}
|
||||
|
||||
ready(): Thenable<ICodeWindow> {
|
||||
ready(): Promise<ICodeWindow> {
|
||||
return new Promise<ICodeWindow>(resolve => {
|
||||
if (this.isReady) {
|
||||
return resolve(this);
|
||||
@@ -363,17 +364,31 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
this._lastFocusTime = Date.now();
|
||||
});
|
||||
|
||||
// Simple fullscreen doesn't resize automatically when the resolution changes
|
||||
// Simple fullscreen doesn't resize automatically when the resolution changes so as a workaround
|
||||
// we need to detect when display metrics change or displays are added/removed and toggle the
|
||||
// fullscreen manually.
|
||||
if (isMacintosh) {
|
||||
const displayMetricsChangedListener = () => {
|
||||
if (this.isFullScreen() && !this.useNativeFullScreen()) {
|
||||
const simpleFullScreenScheduler = this._register(new RunOnceScheduler(() => {
|
||||
if (!this._win) {
|
||||
return; // disposed
|
||||
}
|
||||
|
||||
if (!this.useNativeFullScreen() && this.isFullScreen()) {
|
||||
this.setFullScreen(false);
|
||||
this.setFullScreen(true);
|
||||
}
|
||||
};
|
||||
}, 100));
|
||||
|
||||
screen.addListener('display-metrics-changed', displayMetricsChangedListener);
|
||||
this._register(toDisposable(() => screen.removeListener('display-metrics-changed', displayMetricsChangedListener)));
|
||||
const displayChangedListener = () => simpleFullScreenScheduler.schedule();
|
||||
|
||||
screen.on('display-metrics-changed', displayChangedListener);
|
||||
this._register(toDisposable(() => screen.removeListener('display-metrics-changed', displayChangedListener)));
|
||||
|
||||
screen.on('display-added', displayChangedListener);
|
||||
this._register(toDisposable(() => screen.removeListener('display-added', displayChangedListener)));
|
||||
|
||||
screen.on('display-removed', displayChangedListener);
|
||||
this._register(toDisposable(() => screen.removeListener('display-removed', displayChangedListener)));
|
||||
}
|
||||
|
||||
// Window (Un)Maximize
|
||||
@@ -412,34 +427,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
|
||||
// Handle Workspace events
|
||||
this._register(this.workspacesMainService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e)));
|
||||
|
||||
// TODO@Ben workaround for https://github.com/Microsoft/vscode/issues/13612
|
||||
// It looks like smooth scrolling disappears as soon as the window is minimized
|
||||
// and maximized again. Touching some window properties "fixes" it, like toggling
|
||||
// the visibility of the menu.
|
||||
if (isWindows) {
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
if (windowConfig && windowConfig.smoothScrollingWorkaround === true) {
|
||||
let minimized = false;
|
||||
|
||||
const restoreSmoothScrolling = () => {
|
||||
if (minimized) {
|
||||
const visibility = this.getMenuBarVisibility();
|
||||
const temporaryVisibility: MenuBarVisibility = (visibility === 'hidden' || visibility === 'toggle') ? 'default' : 'hidden';
|
||||
setTimeout(() => {
|
||||
this.doSetMenuBarVisibility(temporaryVisibility);
|
||||
this.doSetMenuBarVisibility(visibility);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
minimized = false;
|
||||
};
|
||||
|
||||
this._win.on('minimize', () => minimized = true);
|
||||
this._win.on('restore', () => restoreSmoothScrolling());
|
||||
this._win.on('maximize', () => restoreSmoothScrolling());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onUntitledWorkspaceDeleted(workspace: IWorkspaceIdentifier): void {
|
||||
@@ -447,7 +434,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
// Make sure to update our workspace config if we detect that it
|
||||
// was deleted
|
||||
if (this.openedWorkspace && this.openedWorkspace.id === workspace.id) {
|
||||
this.currentConfig.workspace = void 0;
|
||||
this.currentConfig.workspace = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,12 +533,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
}
|
||||
}
|
||||
|
||||
reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void {
|
||||
reload(configurationIn?: IWindowConfiguration, cli?: ParsedArgs): void {
|
||||
|
||||
// If config is not provided, copy our current one
|
||||
if (!configuration) {
|
||||
configuration = objects.mixin({}, this.currentConfig);
|
||||
}
|
||||
const configuration = configurationIn ? configurationIn : objects.mixin({}, this.currentConfig);
|
||||
|
||||
// Delete some properties we do not want during reload
|
||||
delete configuration.filesToOpen;
|
||||
@@ -608,13 +593,13 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
windowConfiguration.perfEntries = perf.exportEntries();
|
||||
|
||||
// Parts splash
|
||||
windowConfiguration.partsSplashData = this.storageMainService.get('parts-splash-data', void 0);
|
||||
windowConfiguration.partsSplashData = this.storageMainService.get('parts-splash-data', undefined);
|
||||
|
||||
// Config (combination of process.argv and window configuration)
|
||||
const environment = parseArgs(process.argv);
|
||||
const config = objects.assign(environment, windowConfiguration);
|
||||
for (let key in config) {
|
||||
if (config[key] === void 0 || config[key] === null || config[key] === '' || config[key] === false) {
|
||||
if (config[key] === undefined || config[key] === null || config[key] === '' || config[key] === false) {
|
||||
delete config[key]; // only send over properties that have a true value
|
||||
}
|
||||
}
|
||||
@@ -654,7 +639,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
|
||||
const res = {
|
||||
mode: WindowMode.Fullscreen,
|
||||
display: display ? display.id : void 0,
|
||||
display: display ? display.id : undefined,
|
||||
|
||||
// Still carry over window dimensions from previous sessions
|
||||
// if we can compute it in fullscreen state.
|
||||
@@ -716,7 +701,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
return state;
|
||||
}
|
||||
|
||||
private validateWindowState(state: IWindowState): IWindowState {
|
||||
private validateWindowState(state: IWindowState): IWindowState | null {
|
||||
if (!state) {
|
||||
return null;
|
||||
}
|
||||
@@ -1035,17 +1020,17 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
|
||||
private createTouchBarGroupSegments(items: ISerializableCommandAction[] = []): ITouchBarSegment[] {
|
||||
const segments: ITouchBarSegment[] = items.map(item => {
|
||||
let icon: Electron.NativeImage;
|
||||
let icon: Electron.NativeImage | undefined;
|
||||
if (item.iconLocation && item.iconLocation.dark.scheme === 'file') {
|
||||
icon = nativeImage.createFromPath(URI.revive(item.iconLocation.dark).fsPath);
|
||||
if (icon.isEmpty()) {
|
||||
icon = void 0;
|
||||
icon = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
label: !icon ? item.title as string : void 0,
|
||||
label: !icon ? item.title as string : undefined,
|
||||
icon
|
||||
};
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
|
||||
import { hasArgs, asArray } from 'vs/platform/environment/node/argv';
|
||||
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, app } from 'electron';
|
||||
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences } from 'electron';
|
||||
import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/paths';
|
||||
import { ILifecycleService, UnloadReason, IWindowUnloadEvent, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -26,8 +26,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IHistoryMainService } from 'vs/platform/history/common/history';
|
||||
import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
@@ -35,7 +34,7 @@ import { normalizeNFC } from 'vs/base/common/normalization';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Queue, timeout } from 'vs/base/common/async';
|
||||
import { exists } from 'vs/base/node/pfs';
|
||||
import { getComparisonKey, isEqual, normalizePath } from 'vs/base/common/resources';
|
||||
import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename } from 'vs/base/common/resources';
|
||||
import { endsWith } from 'vs/base/common/strings';
|
||||
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
|
||||
|
||||
@@ -146,27 +145,21 @@ export class WindowsManager implements IWindowsMainService {
|
||||
private _onWindowLoad = new Emitter<number>();
|
||||
onWindowLoad: CommonEvent<number> = this._onWindowLoad.event;
|
||||
|
||||
private _onActiveWindowChanged = new Emitter<ICodeWindow>();
|
||||
onActiveWindowChanged: CommonEvent<ICodeWindow> = this._onActiveWindowChanged.event;
|
||||
|
||||
private _onWindowReload = new Emitter<number>();
|
||||
onWindowReload: CommonEvent<number> = this._onWindowReload.event;
|
||||
|
||||
private _onWindowsCountChanged = new Emitter<IWindowsCountChangedEvent>();
|
||||
onWindowsCountChanged: CommonEvent<IWindowsCountChangedEvent> = this._onWindowsCountChanged.event;
|
||||
|
||||
constructor(
|
||||
private readonly machineId: string,
|
||||
@ILogService private logService: ILogService,
|
||||
@IStateService private stateService: IStateService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@ILifecycleService private lifecycleService: ILifecycleService,
|
||||
@IBackupMainService private backupMainService: IBackupMainService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IHistoryMainService private historyMainService: IHistoryMainService,
|
||||
@IWorkspacesMainService private workspacesMainService: IWorkspacesMainService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IStateService private readonly stateService: IStateService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@ILifecycleService private readonly lifecycleService: ILifecycleService,
|
||||
@IBackupMainService private readonly backupMainService: IBackupMainService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IHistoryMainService private readonly historyMainService: IHistoryMainService,
|
||||
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
this.windowsState = this.getWindowsState();
|
||||
if (!Array.isArray(this.windowsState.openedWindows)) {
|
||||
@@ -174,7 +167,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
|
||||
this.dialogs = new Dialogs(environmentService, telemetryService, stateService, this);
|
||||
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, this);
|
||||
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, historyMainService, this);
|
||||
}
|
||||
|
||||
private getWindowsState(): IWindowsState {
|
||||
@@ -209,13 +202,6 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// React to windows focus changes
|
||||
app.on('browser-window-focus', () => {
|
||||
setTimeout(() => {
|
||||
this._onActiveWindowChanged.fire(this.getLastActiveWindow());
|
||||
});
|
||||
});
|
||||
|
||||
// React to workbench ready events from windows
|
||||
ipc.on('vscode:workbenchReady', (event: any, windowId: number) => {
|
||||
this.logService.trace('IPC#vscode-workbenchReady');
|
||||
@@ -249,7 +235,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// clear last closed window state when a new window opens. this helps on macOS where
|
||||
// otherwise closing the last window, opening a new window and then quitting would
|
||||
// use the state of the previously closed window when restarting.
|
||||
this.lastClosedWindowState = void 0;
|
||||
this.lastClosedWindowState = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -387,7 +373,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
|
||||
// collect all file inputs
|
||||
let fileInputs: IFileInputs = void 0;
|
||||
let fileInputs: IFileInputs = undefined;
|
||||
for (const path of pathsToOpen) {
|
||||
if (path.fileUri) {
|
||||
if (!fileInputs) {
|
||||
@@ -492,8 +478,8 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// Remember in recent document list (unless this opens for extension development)
|
||||
// Also do not add paths when files are opened for diffing, only if opened individually
|
||||
if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.cli.diff) {
|
||||
const recentlyOpenedWorkspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[] = [];
|
||||
if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.diffMode) {
|
||||
const recentlyOpenedWorkspaces: Array<IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier> = [];
|
||||
const recentlyOpenedFiles: URI[] = [];
|
||||
|
||||
pathsToOpen.forEach(win => {
|
||||
@@ -513,7 +499,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// used for the edit operation is closed or loaded to a different folder so that the waiting
|
||||
// process can continue. We do this by deleting the waitMarkerFilePath.
|
||||
if (openConfig.context === OpenContext.CLI && openConfig.cli.wait && openConfig.cli.waitMarkerFilePath && usedWindows.length === 1 && usedWindows[0]) {
|
||||
this.waitForWindowCloseOrLoad(usedWindows[0].id).then(() => fs.unlink(openConfig.cli.waitMarkerFilePath, error => void 0));
|
||||
this.waitForWindowCloseOrLoad(usedWindows[0].id).then(() => fs.unlink(openConfig.cli.waitMarkerFilePath, error => undefined));
|
||||
}
|
||||
|
||||
return usedWindows;
|
||||
@@ -552,9 +538,6 @@ export class WindowsManager implements IWindowsMainService {
|
||||
if (lastActiveWindow) {
|
||||
usedWindows.push(this.doAddFoldersToExistingWindow(lastActiveWindow, foldersToAdd));
|
||||
}
|
||||
|
||||
// Reset because we handled them
|
||||
foldersToAdd = [];
|
||||
}
|
||||
|
||||
// Handle files to open/diff or to create when we dont open a folder and we do not restore any folder/untitled from hot-exit
|
||||
@@ -595,7 +578,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, fileInputs));
|
||||
|
||||
// Reset these because we handled them
|
||||
fileInputs = void 0;
|
||||
fileInputs = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,7 +595,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}));
|
||||
|
||||
// Reset these because we handled them
|
||||
fileInputs = void 0;
|
||||
fileInputs = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,14 +607,14 @@ export class WindowsManager implements IWindowsMainService {
|
||||
const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, workspaceToOpen)));
|
||||
if (windowsOnWorkspace.length > 0) {
|
||||
const windowOnWorkspace = windowsOnWorkspace[0];
|
||||
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : void 0;
|
||||
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : undefined;
|
||||
|
||||
// Do open files
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, fileInputsForWindow));
|
||||
|
||||
// Reset these because we handled them
|
||||
if (fileInputsForWindow) {
|
||||
fileInputs = void 0;
|
||||
fileInputs = undefined;
|
||||
}
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
@@ -643,14 +626,14 @@ export class WindowsManager implements IWindowsMainService {
|
||||
return; // ignore folders that are already open
|
||||
}
|
||||
|
||||
const fileInputsForWindow = (fileInputs && !fileInputs.remoteAuthority) ? fileInputs : void 0;
|
||||
const fileInputsForWindow = (fileInputs && !fileInputs.remoteAuthority) ? fileInputs : undefined;
|
||||
|
||||
// Do open folder
|
||||
usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { workspace: workspaceToOpen }, openFolderInNewWindow, fileInputsForWindow));
|
||||
|
||||
// Reset these because we handled them
|
||||
if (fileInputsForWindow) {
|
||||
fileInputs = void 0;
|
||||
fileInputs = undefined;
|
||||
}
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
@@ -666,14 +649,14 @@ export class WindowsManager implements IWindowsMainService {
|
||||
const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, folderToOpen)));
|
||||
if (windowsOnFolderPath.length > 0) {
|
||||
const windowOnFolderPath = windowsOnFolderPath[0];
|
||||
const fileInputsForWindow = fileInputs && fileInputs.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : void 0;
|
||||
const fileInputsForWindow = fileInputs && fileInputs.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : undefined;
|
||||
|
||||
// Do open files
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, fileInputsForWindow));
|
||||
|
||||
// Reset these because we handled them
|
||||
if (fileInputsForWindow) {
|
||||
fileInputs = void 0;
|
||||
fileInputs = undefined;
|
||||
}
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
@@ -687,14 +670,14 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
|
||||
const remoteAuthority = getRemoteAuthority(folderToOpen);
|
||||
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : void 0;
|
||||
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : undefined;
|
||||
|
||||
// Do open folder
|
||||
usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderUri: folderToOpen, remoteAuthority }, openFolderInNewWindow, fileInputsForWindow));
|
||||
|
||||
// Reset these because we handled them
|
||||
if (fileInputsForWindow) {
|
||||
fileInputs = void 0;
|
||||
fileInputs = undefined;
|
||||
}
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
@@ -705,7 +688,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
if (emptyToRestore.length > 0) {
|
||||
emptyToRestore.forEach(emptyWindowBackupInfo => {
|
||||
const remoteAuthority = emptyWindowBackupInfo.remoteAuthority;
|
||||
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : void 0;
|
||||
const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : undefined;
|
||||
|
||||
usedWindows.push(this.openInBrowserWindow({
|
||||
userEnv: openConfig.userEnv,
|
||||
@@ -720,7 +703,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// Reset these because we handled them
|
||||
if (fileInputsForWindow) {
|
||||
fileInputs = void 0;
|
||||
fileInputs = undefined;
|
||||
}
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
@@ -732,7 +715,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
if (fileInputs && !emptyToOpen) {
|
||||
emptyToOpen++;
|
||||
}
|
||||
const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || void 0);
|
||||
const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined);
|
||||
for (let i = 0; i < emptyToOpen; i++) {
|
||||
usedWindows.push(this.openInBrowserWindow({
|
||||
userEnv: openConfig.userEnv,
|
||||
@@ -745,7 +728,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}));
|
||||
|
||||
// Reset these because we handled them
|
||||
fileInputs = void 0;
|
||||
fileInputs = undefined;
|
||||
openFolderInNewWindow = true; // any other window to open must open in new window then
|
||||
}
|
||||
}
|
||||
@@ -834,8 +817,8 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// folders should be added to the existing window.
|
||||
if (!openConfig.addMode && isCommandLineOrAPICall) {
|
||||
const foldersToOpen = windowsToOpen.filter(path => !!path.folderUri);
|
||||
if (foldersToOpen.length > 1) {
|
||||
const workspace = this.workspacesMainService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri })));
|
||||
if (foldersToOpen.length > 1 && foldersToOpen.every(f => f.folderUri.scheme === Schemas.file)) {
|
||||
const workspace = this.workspacesMainService.createUntitledWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri })));
|
||||
|
||||
// Add workspace and remove folders thereby
|
||||
windowsToOpen.push({ workspace, remoteAuthority: foldersToOpen[0].remoteAuthority });
|
||||
@@ -886,7 +869,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
private doExtractPathsFromCLI(cli: ParsedArgs): IPath[] {
|
||||
const pathsToOpen: IPathToOpen[] = [];
|
||||
const parseOptions: IPathParseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto, remoteAuthority: cli.remote || void 0 };
|
||||
const parseOptions: IPathParseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto, remoteAuthority: cli.remote || undefined };
|
||||
|
||||
// folder uris
|
||||
const folderUris = asArray(cli['folder-uri']);
|
||||
@@ -1017,8 +1000,9 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// normalize URI
|
||||
uri = normalizePath(uri);
|
||||
if (endsWith(uri.path, '/')) {
|
||||
uri = uri.with({ path: uri.path.substr(0, uri.path.length - 1) });
|
||||
const uriPath = uri.path;
|
||||
if (uriPath.length > 2 && endsWith(uriPath, '/')) {
|
||||
uri = uri.with({ path: uriPath.substr(0, uriPath.length - 1) });
|
||||
}
|
||||
if (isFile) {
|
||||
if (options && options.gotoLineMode) {
|
||||
@@ -1074,8 +1058,8 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// File
|
||||
return {
|
||||
fileUri: URI.file(candidate),
|
||||
lineNumber: gotoLineMode ? parsedPath.line : void 0,
|
||||
columnNumber: gotoLineMode ? parsedPath.column : void 0,
|
||||
lineNumber: gotoLineMode ? parsedPath.line : undefined,
|
||||
columnNumber: gotoLineMode ? parsedPath.column : undefined,
|
||||
remoteAuthority
|
||||
};
|
||||
}
|
||||
@@ -1473,10 +1457,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// Only reload when the window has not vetoed this
|
||||
this.lifecycleService.unload(win, UnloadReason.RELOAD).then(veto => {
|
||||
if (!veto) {
|
||||
win.reload(void 0, cli);
|
||||
|
||||
// Emit
|
||||
this._onWindowReload.fire(win.id);
|
||||
win.reload(undefined, cli);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1489,18 +1470,10 @@ export class WindowsManager implements IWindowsMainService {
|
||||
});
|
||||
}
|
||||
|
||||
saveAndEnterWorkspace(win: ICodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
|
||||
return this.workspacesManager.saveAndEnterWorkspace(win, path).then(result => this.doEnterWorkspace(win, result));
|
||||
}
|
||||
|
||||
enterWorkspace(win: ICodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
|
||||
enterWorkspace(win: ICodeWindow, path: URI): Promise<IEnterWorkspaceResult> {
|
||||
return this.workspacesManager.enterWorkspace(win, path).then(result => this.doEnterWorkspace(win, result));
|
||||
}
|
||||
|
||||
createAndEnterWorkspace(win: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise<IEnterWorkspaceResult> {
|
||||
return this.workspacesManager.createAndEnterWorkspace(win, folders, path).then(result => this.doEnterWorkspace(win, result));
|
||||
}
|
||||
|
||||
private doEnterWorkspace(win: ICodeWindow, result: IEnterWorkspaceResult): IEnterWorkspaceResult {
|
||||
|
||||
// Mark as recently opened
|
||||
@@ -1575,7 +1548,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
openNewWindow(context: OpenContext, options?: INewWindowOptions): ICodeWindow[] {
|
||||
let cli = this.environmentService.args;
|
||||
let remote = options && options.remoteAuthority || void 0;
|
||||
let remote = options && options.remoteAuthority || undefined;
|
||||
if (cli && (cli.remote !== remote)) {
|
||||
cli = { ...cli, remote };
|
||||
}
|
||||
@@ -1586,7 +1559,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
return this.open({ context, cli: this.environmentService.args, forceNewTabbedWindow: true, forceEmpty: true });
|
||||
}
|
||||
|
||||
waitForWindowCloseOrLoad(windowId: number): Thenable<void> {
|
||||
waitForWindowCloseOrLoad(windowId: number): Promise<void> {
|
||||
return new Promise<void>(resolve => {
|
||||
function handler(id: number) {
|
||||
if (id === windowId) {
|
||||
@@ -1658,6 +1631,17 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// Unresponsive
|
||||
if (error === WindowError.UNRESPONSIVE) {
|
||||
if (window.isExtensionDevelopmentHost || window.isExtensionTestHost || (window.win && window.win.webContents && window.win.webContents.isDevToolsOpened())) {
|
||||
// TODO@Ben Workaround for https://github.com/Microsoft/vscode/issues/56994
|
||||
// In certain cases the window can report unresponsiveness because a breakpoint was hit
|
||||
// and the process is stopped executing. The most typical cases are:
|
||||
// - devtools are opened and debugging happens
|
||||
// - window is an extensions development host that is being debugged
|
||||
// - window is an extension test development host that is being debugged
|
||||
return;
|
||||
}
|
||||
|
||||
// Show Dialog
|
||||
this.dialogs.showMessageBox({
|
||||
title: product.nameLong,
|
||||
type: 'warning',
|
||||
@@ -1763,15 +1747,15 @@ export class WindowsManager implements IWindowsMainService {
|
||||
this.dialogs.pickAndOpen(internalOptions);
|
||||
}
|
||||
|
||||
showMessageBox(options: Electron.MessageBoxOptions, win?: ICodeWindow): Thenable<IMessageBoxResult> {
|
||||
showMessageBox(options: Electron.MessageBoxOptions, win?: ICodeWindow): Promise<IMessageBoxResult> {
|
||||
return this.dialogs.showMessageBox(options, win);
|
||||
}
|
||||
|
||||
showSaveDialog(options: Electron.SaveDialogOptions, win?: ICodeWindow): Thenable<string> {
|
||||
showSaveDialog(options: Electron.SaveDialogOptions, win?: ICodeWindow): Promise<string> {
|
||||
return this.dialogs.showSaveDialog(options, win);
|
||||
}
|
||||
|
||||
showOpenDialog(options: Electron.OpenDialogOptions, win?: ICodeWindow): Thenable<string[]> {
|
||||
showOpenDialog(options: Electron.OpenDialogOptions, win?: ICodeWindow): Promise<string[]> {
|
||||
return this.dialogs.showOpenDialog(options, win);
|
||||
}
|
||||
|
||||
@@ -1833,6 +1817,7 @@ class Dialogs {
|
||||
if (numberOfPaths) {
|
||||
this.windowsMainService.open({
|
||||
context: OpenContext.DIALOG,
|
||||
contextWindowId: options.windowId,
|
||||
cli: this.environmentService.args,
|
||||
urisToOpen: paths,
|
||||
forceNewWindow: options.forceNewWindow,
|
||||
@@ -1842,7 +1827,7 @@ class Dialogs {
|
||||
});
|
||||
}
|
||||
|
||||
private getFileOrFolderUris(options: IInternalNativeOpenDialogOptions): TPromise<URI[]> {
|
||||
private getFileOrFolderUris(options: IInternalNativeOpenDialogOptions): Promise<URI[]> {
|
||||
|
||||
// Ensure dialog options
|
||||
if (!options.dialogOptions) {
|
||||
@@ -1856,7 +1841,7 @@ class Dialogs {
|
||||
|
||||
// Ensure properties
|
||||
if (typeof options.pickFiles === 'boolean' || typeof options.pickFolders === 'boolean') {
|
||||
options.dialogOptions.properties = void 0; // let it override based on the booleans
|
||||
options.dialogOptions.properties = undefined; // let it override based on the booleans
|
||||
|
||||
if (options.pickFiles && options.pickFolders) {
|
||||
options.dialogOptions.properties = ['multiSelections', 'openDirectory', 'openFile', 'createDirectory'];
|
||||
@@ -1883,7 +1868,7 @@ class Dialogs {
|
||||
return paths.map(path => URI.file(path));
|
||||
}
|
||||
|
||||
return void 0;
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1901,17 +1886,17 @@ class Dialogs {
|
||||
return windowDialogQueue;
|
||||
}
|
||||
|
||||
showMessageBox(options: Electron.MessageBoxOptions, window?: ICodeWindow): Thenable<IMessageBoxResult> {
|
||||
showMessageBox(options: Electron.MessageBoxOptions, window?: ICodeWindow): Promise<IMessageBoxResult> {
|
||||
return this.getDialogQueue(window).queue(() => {
|
||||
return new Promise(resolve => {
|
||||
dialog.showMessageBox(window ? window.win : void 0, options, (response: number, checkboxChecked: boolean) => {
|
||||
dialog.showMessageBox(window ? window.win : undefined, options, (response: number, checkboxChecked: boolean) => {
|
||||
resolve({ button: response, checkboxChecked });
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showSaveDialog(options: Electron.SaveDialogOptions, window?: ICodeWindow): Thenable<string> {
|
||||
showSaveDialog(options: Electron.SaveDialogOptions, window?: ICodeWindow): Promise<string> {
|
||||
|
||||
function normalizePath(path: string): string {
|
||||
if (path && isMacintosh) {
|
||||
@@ -1923,14 +1908,14 @@ class Dialogs {
|
||||
|
||||
return this.getDialogQueue(window).queue(() => {
|
||||
return new Promise(resolve => {
|
||||
dialog.showSaveDialog(window ? window.win : void 0, options, path => {
|
||||
dialog.showSaveDialog(window ? window.win : undefined, options, path => {
|
||||
resolve(normalizePath(path));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showOpenDialog(options: Electron.OpenDialogOptions, window?: ICodeWindow): Thenable<string[]> {
|
||||
showOpenDialog(options: Electron.OpenDialogOptions, window?: ICodeWindow): Promise<string[]> {
|
||||
|
||||
function normalizePaths(paths: string[]): string[] {
|
||||
if (paths && paths.length > 0 && isMacintosh) {
|
||||
@@ -1948,14 +1933,14 @@ class Dialogs {
|
||||
if (options.defaultPath) {
|
||||
validatePathPromise = exists(options.defaultPath).then(exists => {
|
||||
if (!exists) {
|
||||
options.defaultPath = void 0;
|
||||
options.defaultPath = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Show dialog and wrap as promise
|
||||
validatePathPromise.then(() => {
|
||||
dialog.showOpenDialog(window ? window.win : void 0, options, paths => {
|
||||
dialog.showOpenDialog(window ? window.win : undefined, options, paths => {
|
||||
resolve(normalizePaths(paths));
|
||||
});
|
||||
});
|
||||
@@ -1970,68 +1955,42 @@ class WorkspacesManager {
|
||||
private workspacesMainService: IWorkspacesMainService,
|
||||
private backupMainService: IBackupMainService,
|
||||
private environmentService: IEnvironmentService,
|
||||
private windowsMainService: IWindowsMainService
|
||||
private historyMainService: IHistoryMainService,
|
||||
private windowsMainService: IWindowsMainService,
|
||||
) {
|
||||
}
|
||||
|
||||
saveAndEnterWorkspace(window: ICodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
|
||||
if (!window || !window.win || !window.isReady || !window.openedWorkspace || !path || !this.isValidTargetWorkspacePath(window, path)) {
|
||||
return TPromise.as(null); // return early if the window is not ready or disposed or does not have a workspace
|
||||
}
|
||||
|
||||
return this.doSaveAndOpenWorkspace(window, window.openedWorkspace, path);
|
||||
}
|
||||
|
||||
enterWorkspace(window: ICodeWindow, path: string): TPromise<IEnterWorkspaceResult> {
|
||||
enterWorkspace(window: ICodeWindow, path: URI): Promise<IEnterWorkspaceResult | null> {
|
||||
if (!window || !window.win || !window.isReady) {
|
||||
return TPromise.as(null); // return early if the window is not ready or disposed
|
||||
return Promise.resolve(null); // return early if the window is not ready or disposed
|
||||
}
|
||||
|
||||
return this.isValidTargetWorkspacePath(window, path).then(isValid => {
|
||||
if (!isValid) {
|
||||
return TPromise.as<IEnterWorkspaceResult>(null); // return early if the workspace is not valid
|
||||
return null; // return early if the workspace is not valid
|
||||
}
|
||||
|
||||
return this.workspacesMainService.resolveWorkspace(path).then(workspace => {
|
||||
return this.doOpenWorkspace(window, workspace);
|
||||
});
|
||||
const workspaceIdentifier = this.workspacesMainService.getWorkspaceIdentifier(path);
|
||||
return this.doOpenWorkspace(window, workspaceIdentifier);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
createAndEnterWorkspace(window: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise<IEnterWorkspaceResult> {
|
||||
if (!window || !window.win || !window.isReady) {
|
||||
return TPromise.as(null); // return early if the window is not ready or disposed
|
||||
}
|
||||
|
||||
return this.isValidTargetWorkspacePath(window, path).then(isValid => {
|
||||
if (!isValid) {
|
||||
return TPromise.as(null); // return early if the workspace is not valid
|
||||
}
|
||||
|
||||
return this.workspacesMainService.createWorkspace(folders).then(workspace => {
|
||||
return this.doSaveAndOpenWorkspace(window, workspace, path);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private isValidTargetWorkspacePath(window: ICodeWindow, path?: string): TPromise<boolean> {
|
||||
private isValidTargetWorkspacePath(window: ICodeWindow, path?: URI): Promise<boolean> {
|
||||
if (!path) {
|
||||
return TPromise.wrap(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
if (window.openedWorkspace && window.openedWorkspace.configPath === path) {
|
||||
return TPromise.wrap(false); // window is already opened on a workspace with that path
|
||||
if (window.openedWorkspace && isEqual(URI.file(window.openedWorkspace.configPath), path)) {
|
||||
return Promise.resolve(false); // window is already opened on a workspace with that path
|
||||
}
|
||||
|
||||
// Prevent overwriting a workspace that is currently opened in another window
|
||||
if (findWindowOnWorkspace(this.windowsMainService.getWindows(), { id: this.workspacesMainService.getWorkspaceId(path), configPath: path })) {
|
||||
if (findWindowOnWorkspace(this.windowsMainService.getWindows(), this.workspacesMainService.getWorkspaceIdentifier(path))) {
|
||||
const options: Electron.MessageBoxOptions = {
|
||||
title: product.nameLong,
|
||||
type: 'info',
|
||||
buttons: [localize('ok', "OK")],
|
||||
message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)),
|
||||
message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", resourcesBasename(path)),
|
||||
detail: localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."),
|
||||
noLink: true
|
||||
};
|
||||
@@ -2039,18 +1998,7 @@ class WorkspacesManager {
|
||||
return this.windowsMainService.showMessageBox(options, this.windowsMainService.getFocusedWindow()).then(() => false);
|
||||
}
|
||||
|
||||
return TPromise.wrap(true); // OK
|
||||
}
|
||||
|
||||
private doSaveAndOpenWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier, path?: string): TPromise<IEnterWorkspaceResult> {
|
||||
let savePromise: TPromise<IWorkspaceIdentifier>;
|
||||
if (path) {
|
||||
savePromise = this.workspacesMainService.saveWorkspace(workspace, path);
|
||||
} else {
|
||||
savePromise = TPromise.as(workspace);
|
||||
}
|
||||
|
||||
return savePromise.then(workspace => this.doOpenWorkspace(window, workspace));
|
||||
return Promise.resolve(true); // OK
|
||||
}
|
||||
|
||||
private doOpenWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): IEnterWorkspaceResult {
|
||||
@@ -2062,8 +2010,13 @@ class WorkspacesManager {
|
||||
backupPath = this.backupMainService.registerWorkspaceBackupSync(workspace, window.config.backupPath);
|
||||
}
|
||||
|
||||
// if the window was opened on an untitled workspace, delete it.
|
||||
if (window.openedWorkspace && this.workspacesMainService.isUntitledWorkspace(window.openedWorkspace)) {
|
||||
this.workspacesMainService.deleteUntitledWorkspaceSync(window.openedWorkspace);
|
||||
}
|
||||
|
||||
// Update window configuration properly based on transition to workspace
|
||||
window.config.folderUri = void 0;
|
||||
window.config.folderUri = undefined;
|
||||
window.config.workspace = workspace;
|
||||
window.config.backupPath = backupPath;
|
||||
|
||||
@@ -2074,7 +2027,7 @@ class WorkspacesManager {
|
||||
const window = this.windowsMainService.getWindowById(options.windowId) || this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();
|
||||
|
||||
this.windowsMainService.pickFileAndOpen({
|
||||
windowId: window ? window.id : void 0,
|
||||
windowId: window ? window.id : undefined,
|
||||
dialogOptions: {
|
||||
buttonLabel: mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open")),
|
||||
title: localize('openWorkspaceTitle', "Open Workspace"),
|
||||
@@ -2088,7 +2041,7 @@ class WorkspacesManager {
|
||||
});
|
||||
}
|
||||
|
||||
promptToSaveUntitledWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): TPromise<boolean> {
|
||||
promptToSaveUntitledWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): Promise<boolean> {
|
||||
enum ConfirmResult {
|
||||
SAVE,
|
||||
DONT_SAVE,
|
||||
@@ -2143,7 +2096,11 @@ class WorkspacesManager {
|
||||
defaultPath: this.getUntitledWorkspaceSaveDialogDefaultPath(workspace)
|
||||
}, window).then(target => {
|
||||
if (target) {
|
||||
return this.workspacesMainService.saveWorkspace(workspace, target).then(() => false, () => false);
|
||||
return this.workspacesMainService.saveWorkspaceAs(workspace, target).then(savedWorkspace => {
|
||||
this.historyMainService.addRecentlyOpened([savedWorkspace], []);
|
||||
this.workspacesMainService.deleteUntitledWorkspaceSync(workspace);
|
||||
return false;
|
||||
}, () => false);
|
||||
}
|
||||
|
||||
return true; // keep veto if no target was provided
|
||||
@@ -2156,7 +2113,7 @@ class WorkspacesManager {
|
||||
private getUntitledWorkspaceSaveDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string {
|
||||
if (workspace) {
|
||||
if (isSingleFolderWorkspaceIdentifier(workspace)) {
|
||||
return workspace.scheme === Schemas.file ? dirname(workspace.fsPath) : void 0;
|
||||
return workspace.scheme === Schemas.file ? dirname(workspace.fsPath) : undefined;
|
||||
}
|
||||
|
||||
const resolvedWorkspace = this.workspacesMainService.resolveWorkspaceSync(workspace.configPath);
|
||||
@@ -2169,6 +2126,6 @@ class WorkspacesManager {
|
||||
}
|
||||
}
|
||||
|
||||
return void 0;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user