Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f (#7282)

* Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f

* fix various icon issues

* fix preview features
This commit is contained in:
Anthony Dresser
2019-09-19 21:50:52 -07:00
committed by GitHub
parent 9d3d64eef3
commit db498db0a8
459 changed files with 10195 additions and 7528 deletions

View File

@@ -93,6 +93,7 @@ export const enum MenuId {
SearchContext,
StatusBarWindowIndicatorMenu,
TouchBarContext,
TitleBarContext,
ViewItemContext,
ViewTitle,
ObjectExplorerItemContext, // {{SQL CARBON EDIT}}

View File

@@ -6,27 +6,10 @@
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { URI } from 'vs/base/common/uri';
export interface ISerializedWorkspace { id: string; configURIPath: string; remoteAuthority?: string; }
export interface IBackupWorkspacesFormat {
rootURIWorkspaces: ISerializedWorkspace[];
folderURIWorkspaces: string[];
emptyWorkspaceInfos: IEmptyWindowBackupInfo[];
// deprecated
folderWorkspaces?: string[]; // use folderURIWorkspaces instead
emptyWorkspaces?: string[];
rootWorkspaces?: { id: string, configPath: string }[]; // use rootURIWorkspaces instead
}
import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
export const IBackupMainService = createDecorator<IBackupMainService>('backupMainService');
export interface IEmptyWindowBackupInfo {
backupFolder: string;
remoteAuthority?: string;
}
export interface IWorkspaceBackupInfo {
workspace: IWorkspaceIdentifier;
remoteAuthority?: string;
@@ -48,4 +31,4 @@ export interface IBackupMainService {
unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void;
unregisterFolderBackupSync(folderUri: URI): void;
unregisterEmptyWindowBackupSync(backupFolder: string): void;
}
}

View File

@@ -9,7 +9,8 @@ import * as path from 'vs/base/common/path';
import * as platform from 'vs/base/common/platform';
import { writeFileSync, writeFile, readFile, readdir, exists, rimraf, rename, RimRafMode } from 'vs/base/node/pfs';
import * as arrays from 'vs/base/common/arrays';
import { IBackupMainService, IBackupWorkspacesFormat, IEmptyWindowBackupInfo, IWorkspaceBackupInfo } from 'vs/platform/backup/common/backup';
import { IBackupMainService, IWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup';
import { IBackupWorkspacesFormat, IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IFilesConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files';

View File

@@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface ISerializedWorkspace { id: string; configURIPath: string; remoteAuthority?: string; }
export interface IBackupWorkspacesFormat {
rootURIWorkspaces: ISerializedWorkspace[];
folderURIWorkspaces: string[];
emptyWorkspaceInfos: IEmptyWindowBackupInfo[];
// deprecated
folderWorkspaces?: string[]; // use folderURIWorkspaces instead
emptyWorkspaces?: string[];
rootWorkspaces?: { id: string, configPath: string }[]; // use rootURIWorkspaces instead
}
export interface IEmptyWindowBackupInfo {
backupFolder: string;
remoteAuthority?: string;
}

View File

@@ -9,11 +9,12 @@ import * as fs from 'fs';
import * as os from 'os';
import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs';
import { URI as Uri, URI } from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
import { IBackupWorkspacesFormat, ISerializedWorkspace, IWorkspaceBackupInfo } from 'vs/platform/backup/common/backup';
import { IWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup';
import { IBackupWorkspacesFormat, ISerializedWorkspace } from 'vs/platform/backup/node/backup';
import { HotExitConfiguration } from 'vs/platform/files/common/files';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ConsoleLogMainService } from 'vs/platform/log/common/log';
@@ -24,7 +25,7 @@ import { Schemas } from 'vs/base/common/network';
suite('BackupMainService', () => {
function assertEqualUris(actual: Uri[], expected: Uri[]) {
function assertEqualUris(actual: URI[], expected: URI[]) {
assert.deepEqual(actual.map(a => a.toString()), expected.map(a => a.toString()));
}
@@ -43,12 +44,12 @@ suite('BackupMainService', () => {
this.workspacesJsonPath = backupWorkspacesPath;
}
public toBackupPath(arg: Uri | string): string {
const id = arg instanceof Uri ? super.getFolderHash(arg) : arg;
public toBackupPath(arg: URI | string): string {
const id = arg instanceof URI ? super.getFolderHash(arg) : arg;
return path.join(this.backupHome, id);
}
public getFolderHash(folderUri: Uri): string {
public getFolderHash(folderUri: URI): string {
return super.getFolderHash(folderUri);
}
@@ -81,7 +82,7 @@ suite('BackupMainService', () => {
};
}
async function ensureFolderExists(uri: Uri): Promise<void> {
async function ensureFolderExists(uri: URI): Promise<void> {
if (!fs.existsSync(uri.fsPath)) {
fs.mkdirSync(uri.fsPath);
}
@@ -110,10 +111,10 @@ suite('BackupMainService', () => {
return platform.isLinux ? p : p.toLowerCase();
}
const fooFile = Uri.file(platform.isWindows ? 'C:\\foo' : '/foo');
const barFile = Uri.file(platform.isWindows ? 'C:\\bar' : '/bar');
const fooFile = URI.file(platform.isWindows ? 'C:\\foo' : '/foo');
const barFile = URI.file(platform.isWindows ? 'C:\\bar' : '/bar');
const existingTestFolder1 = Uri.file(path.join(parentDir, 'folder1'));
const existingTestFolder1 = URI.file(path.join(parentDir, 'folder1'));
let service: TestBackupMainService;
let configService: TestConfigurationService;
@@ -231,7 +232,7 @@ suite('BackupMainService', () => {
const backupPathToMigrate = service.toBackupPath(fooFile);
fs.mkdirSync(backupPathToMigrate);
fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data');
service.registerFolderBackupSync(Uri.file(backupPathToMigrate));
service.registerFolderBackupSync(URI.file(backupPathToMigrate));
const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspaceBackupInfo(barFile.fsPath), backupPathToMigrate);
@@ -247,12 +248,12 @@ suite('BackupMainService', () => {
const backupPathToMigrate = service.toBackupPath(fooFile);
fs.mkdirSync(backupPathToMigrate);
fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data');
service.registerFolderBackupSync(Uri.file(backupPathToMigrate));
service.registerFolderBackupSync(URI.file(backupPathToMigrate));
const backupPathToPreserve = service.toBackupPath(barFile);
fs.mkdirSync(backupPathToPreserve);
fs.writeFileSync(path.join(backupPathToPreserve, 'backup.txt'), 'Some Data');
service.registerFolderBackupSync(Uri.file(backupPathToPreserve));
service.registerFolderBackupSync(URI.file(backupPathToPreserve));
const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspaceBackupInfo(barFile.fsPath), backupPathToMigrate);
@@ -270,8 +271,8 @@ suite('BackupMainService', () => {
test('migration folder path to URI makes sure to preserve existing backups', async () => {
let path1 = path.join(parentDir, 'folder1');
let path2 = path.join(parentDir, 'FOLDER2');
let uri1 = Uri.file(path1);
let uri2 = Uri.file(path2);
let uri1 = URI.file(path1);
let uri2 = URI.file(path2);
if (!fs.existsSync(path1)) {
fs.mkdirSync(path1);
@@ -372,8 +373,8 @@ suite('BackupMainService', () => {
});
test('getFolderBackupPaths() should return [] when files.hotExit = "onExitAndWindowClose"', async () => {
service.registerFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase()));
assertEqualUris(service.getFolderBackupPaths(), [Uri.file(fooFile.fsPath.toUpperCase())]);
service.registerFolderBackupSync(URI.file(fooFile.fsPath.toUpperCase()));
assertEqualUris(service.getFolderBackupPaths(), [URI.file(fooFile.fsPath.toUpperCase())]);
configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE);
await service.initialize();
assertEqualUris(service.getFolderBackupPaths(), []);
@@ -591,11 +592,11 @@ suite('BackupMainService', () => {
});
test('should always store the workspace path in workspaces.json using the case given, regardless of whether the file system is case-sensitive (folder workspace)', () => {
service.registerFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase()));
assertEqualUris(service.getFolderBackupPaths(), [Uri.file(fooFile.fsPath.toUpperCase())]);
service.registerFolderBackupSync(URI.file(fooFile.fsPath.toUpperCase()));
assertEqualUris(service.getFolderBackupPaths(), [URI.file(fooFile.fsPath.toUpperCase())]);
return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => {
const json = <IBackupWorkspacesFormat>JSON.parse(buffer);
assert.deepEqual(json.folderURIWorkspaces, [Uri.file(fooFile.fsPath.toUpperCase()).toString()]);
assert.deepEqual(json.folderURIWorkspaces, [URI.file(fooFile.fsPath.toUpperCase()).toString()]);
});
});
@@ -681,11 +682,11 @@ suite('BackupMainService', () => {
}
if (platform.isMacintosh) {
assert.equal(service.getFolderHash(Uri.file('/foo')), service.getFolderHash(Uri.file('/FOO')));
assert.equal(service.getFolderHash(URI.file('/foo')), service.getFolderHash(URI.file('/FOO')));
}
if (platform.isWindows) {
assert.equal(service.getFolderHash(Uri.file('c:\\foo')), service.getFolderHash(Uri.file('C:\\FOO')));
assert.equal(service.getFolderHash(URI.file('c:\\foo')), service.getFolderHash(URI.file('C:\\FOO')));
}
});
});
@@ -693,7 +694,7 @@ suite('BackupMainService', () => {
suite('mixed path casing', () => {
test('should handle case insensitive paths properly (registerWindowForBackupsSync) (folder workspace)', () => {
service.registerFolderBackupSync(fooFile);
service.registerFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase()));
service.registerFolderBackupSync(URI.file(fooFile.fsPath.toUpperCase()));
if (platform.isLinux) {
assert.equal(service.getFolderBackupPaths().length, 2);
@@ -722,7 +723,7 @@ suite('BackupMainService', () => {
// mixed case
service.registerFolderBackupSync(fooFile);
service.unregisterFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase()));
service.unregisterFolderBackupSync(URI.file(fooFile.fsPath.toUpperCase()));
if (platform.isLinux) {
assert.equal(service.getFolderBackupPaths().length, 1);

View File

@@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ICredentialsService = createDecorator<ICredentialsService>('ICredentialsService');
export interface ICredentialsService {
_serviceBrand: undefined;
getPassword(service: string, account: string): Promise<string | null>;
setPassword(service: string, account: string, password: string): Promise<void>;
deletePassword(service: string, account: string): Promise<boolean>;
findPassword(service: string): Promise<string | null>;
findCredentials(service: string): Promise<Array<{ account: string, password: string }>>;
}

View File

@@ -6,6 +6,8 @@
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { IRemoteConsoleLog } from 'vs/base/common/console';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { IProcessEnvironment } from 'vs/base/common/platform';
export const IExtensionHostDebugService = createDecorator<IExtensionHostDebugService>('extensionHostDebugService');
@@ -37,17 +39,19 @@ export interface IExtensionHostDebugService {
_serviceBrand: undefined;
reload(sessionId: string): void;
onReload: Event<IReloadSessionEvent>;
readonly onReload: Event<IReloadSessionEvent>;
close(sessionId: string): void;
onClose: Event<ICloseSessionEvent>;
readonly onClose: Event<ICloseSessionEvent>;
attachSession(sessionId: string, port: number, subId?: string): void;
onAttachSession: Event<IAttachSessionEvent>;
readonly onAttachSession: Event<IAttachSessionEvent>;
logToSession(sessionId: string, log: IRemoteConsoleLog): void;
onLogToSession: Event<ILogToSessionEvent>;
readonly onLogToSession: Event<ILogToSessionEvent>;
terminateSession(sessionId: string, subId?: string): void;
onTerminateSession: Event<ITerminateSessionEvent>;
readonly onTerminateSession: Event<ITerminateSessionEvent>;
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void>;
}

View File

@@ -8,6 +8,8 @@ import { IReloadSessionEvent, ICloseSessionEvent, IAttachSessionEvent, ILogToSes
import { Event, Emitter } from 'vs/base/common/event';
import { IRemoteConsoleLog } from 'vs/base/common/console';
import { Disposable } from 'vs/base/common/lifecycle';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { IProcessEnvironment } from 'vs/base/common/platform';
export class ExtensionHostDebugBroadcastChannel<TContext> implements IServerChannel<TContext> {
@@ -99,4 +101,10 @@ export class ExtensionHostDebugChannelClient extends Disposable implements IExte
get onTerminateSession(): Event<ITerminateSessionEvent> {
return this.channel.listen('terminate');
}
}
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void> {
// TODO@Isidor
//return this.channel.call('openExtensionDevelopmentHostWindow', [args, env]);
return Promise.resolve();
}
}

View File

@@ -7,7 +7,7 @@ import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IRemoteDiagnosticInfo, IRemoteDiagnosticError, SystemInfo, PerformanceInfo } from 'vs/platform/diagnostics/common/diagnostics';
import { IDiagnosticsService } from './diagnosticsService';
import { Event } from 'vs/base/common/event';
import { IMainProcessInfo } from 'vs/platform/launch/common/launchService';
import { IMainProcessInfo } from 'vs/platform/launch/common/launch';
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
export class DiagnosticsChannel implements IServerChannel {

View File

@@ -9,13 +9,12 @@ import { readdir, stat, exists, readFile } from 'fs';
import { join, basename } from 'vs/base/common/path';
import { parse, ParseError } from 'vs/base/common/json';
import { listProcesses } from 'vs/base/node/ps';
import product from 'vs/platform/product/node/product';
import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/common/product';
import { repeat, pad } from 'vs/base/common/strings';
import { isWindows } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { ProcessItem } from 'vs/base/common/processes';
import { IMainProcessInfo } from 'vs/platform/launch/common/launchService';
import { IMainProcessInfo } from 'vs/platform/launch/common/launch';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
@@ -267,7 +266,7 @@ export class DiagnosticsService implements IDiagnosticsService {
const GB = 1024 * MB;
const output: string[] = [];
output.push(`Version: ${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`);
output.push(`Version: ${product.nameShort} ${product.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`);
output.push(`OS Version: ${osLib.type()} ${osLib.arch()} ${osLib.release()}`);
const cpus = osLib.cpus();
if (cpus && cpus.length > 0) {

View File

@@ -1,116 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { IDialogService, IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult } from 'vs/platform/dialogs/common/dialogs';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { ILogService } from 'vs/platform/log/common/log';
import Severity from 'vs/base/common/severity';
import { Dialog } from 'vs/base/browser/ui/dialog/dialog';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachDialogStyler } from 'vs/platform/theme/common/styler';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { EventHelper } from 'vs/base/browser/dom';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
export class DialogService implements IDialogService {
_serviceBrand: undefined;
private allowableCommands = ['copy', 'cut'];
constructor(
@ILogService private readonly logService: ILogService,
@ILayoutService private readonly layoutService: ILayoutService,
@IThemeService private readonly themeService: IThemeService,
@IKeybindingService private readonly keybindingService: IKeybindingService
) { }
async confirm(confirmation: IConfirmation): Promise<IConfirmationResult> {
this.logService.trace('DialogService#confirm', confirmation.message);
const buttons: string[] = [];
if (confirmation.primaryButton) {
buttons.push(confirmation.primaryButton);
} else {
buttons.push(nls.localize({ key: 'yesButton', comment: ['&& denotes a mnemonic'] }, "&&Yes"));
}
if (confirmation.secondaryButton) {
buttons.push(confirmation.secondaryButton);
} else if (typeof confirmation.secondaryButton === 'undefined') {
buttons.push(nls.localize('cancelButton', "Cancel"));
}
const dialogDisposables = new DisposableStore();
const dialog = new Dialog(
this.layoutService.container,
confirmation.message,
buttons,
{
detail: confirmation.detail,
cancelId: 1,
type: confirmation.type,
keyEventProcessor: (event: StandardKeyboardEvent) => {
const resolved = this.keybindingService.softDispatch(event, this.layoutService.container);
if (resolved && resolved.commandId) {
if (this.allowableCommands.indexOf(resolved.commandId) === -1) {
EventHelper.stop(event, true);
}
}
},
checkboxChecked: confirmation.checkbox ? confirmation.checkbox.checked : undefined,
checkboxLabel: confirmation.checkbox ? confirmation.checkbox.label : undefined
});
dialogDisposables.add(dialog);
dialogDisposables.add(attachDialogStyler(dialog, this.themeService));
const result = await dialog.show();
dialogDisposables.dispose();
return { confirmed: result.button === 0, checkboxChecked: result.checkboxChecked };
}
private getDialogType(severity: Severity): DialogType {
return (severity === Severity.Info) ? 'question' : (severity === Severity.Error) ? 'error' : (severity === Severity.Warning) ? 'warning' : 'none';
}
async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise<IShowResult> {
this.logService.trace('DialogService#show', message);
const dialogDisposables = new DisposableStore();
const dialog = new Dialog(
this.layoutService.container,
message,
buttons,
{
detail: options ? options.detail : undefined,
cancelId: options ? options.cancelId : undefined,
type: this.getDialogType(severity),
keyEventProcessor: (event: StandardKeyboardEvent) => {
const resolved = this.keybindingService.softDispatch(event, this.layoutService.container);
if (resolved && resolved.commandId) {
if (this.allowableCommands.indexOf(resolved.commandId) === -1) {
EventHelper.stop(event, true);
}
}
},
checkboxLabel: options && options.checkbox ? options.checkbox.label : undefined,
checkboxChecked: options && options.checkbox ? options.checkbox.checked : undefined
});
dialogDisposables.add(dialog);
dialogDisposables.add(attachDialogStyler(dialog, this.themeService));
const result = await dialog.show();
dialogDisposables.dispose();
return {
choice: result.button,
checkboxChecked: result.checkboxChecked
};
}
}

View File

@@ -172,6 +172,11 @@ export interface IDialogService {
* option then promise with index `0` is returned.
*/
show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise<IShowResult>;
/**
* Present the about dialog to the user.
*/
about(): Promise<void>;
}
export const IFileDialogService = createDecorator<IFileDialogService>('fileDialogService');
@@ -235,11 +240,10 @@ export interface IFileDialogService {
* Shows a open file dialog and returns the chosen file URI.
*/
showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined>;
}
const MAX_CONFIRM_FILES = 10;
export function getConfirmMessage(start: string, resourcesToConfirm: URI[]): string {
export function getConfirmMessage(start: string, resourcesToConfirm: readonly URI[]): string {
const message = [start];
message.push('');
message.push(...resourcesToConfirm.slice(0, MAX_CONFIRM_FILES).map(r => basename(r)));

View File

@@ -3,9 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IDialogService, IConfirmation, IConfirmationResult, IShowResult } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { Event } from 'vs/base/common/event';
export class DialogChannel implements IServerChannel {
@@ -20,22 +19,8 @@ export class DialogChannel implements IServerChannel {
switch (command) {
case 'show': return this.dialogService.show(args![0], args![1], args![2]);
case 'confirm': return this.dialogService.confirm(args![0]);
case 'about': return this.dialogService.about();
}
return Promise.reject(new Error('invalid command'));
}
}
export class DialogChannelClient implements IDialogService {
_serviceBrand: undefined;
constructor(private channel: IChannel) { }
show(severity: Severity, message: string, options: string[]): Promise<IShowResult> {
return this.channel.call('show', [severity, message, options]);
}
confirm(confirmation: IConfirmation): Promise<IConfirmationResult> {
return this.channel.call('confirm', [confirmation]);
}
}

View File

@@ -34,7 +34,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
constructor(
private windowServer: IPCServer,
private options: IDriverOptions,
@IWindowsMainService private readonly windowsService: IWindowsMainService
@IWindowsMainService private readonly windowsMainService: IWindowsMainService
) { }
async registerWindowDriver(windowId: number): Promise<IDriverOptions> {
@@ -49,7 +49,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
}
async getWindowIds(): Promise<number[]> {
return this.windowsService.getWindows()
return this.windowsMainService.getWindows()
.map(w => w.id)
.filter(id => this.registeredWindowIds.has(id) && !this.reloadingWindowIds.has(id));
}
@@ -57,7 +57,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
async capturePage(windowId: number): Promise<string> {
await this.whenUnfrozen(windowId);
const window = this.windowsService.getWindowById(windowId);
const window = this.windowsMainService.getWindowById(windowId);
if (!window) {
throw new Error('Invalid window');
}
@@ -69,16 +69,16 @@ export class Driver implements IDriver, IWindowDriverRegistry {
async reloadWindow(windowId: number): Promise<void> {
await this.whenUnfrozen(windowId);
const window = this.windowsService.getWindowById(windowId);
const window = this.windowsMainService.getWindowById(windowId);
if (!window) {
throw new Error('Invalid window');
}
this.reloadingWindowIds.add(windowId);
this.windowsService.reload(window);
this.windowsMainService.reload(window);
}
async exitApplication(): Promise<void> {
return this.windowsService.quit();
return this.windowsMainService.quit();
}
async dispatchKeybinding(windowId: number, keybinding: string): Promise<void> {
@@ -96,7 +96,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
throw new Error('ScanCodeBindings not supported');
}
const window = this.windowsService.getWindowById(windowId);
const window = this.windowsMainService.getWindowById(windowId);
if (!window) {
throw new Error('Invalid window');
}

View File

@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IElectronService } from 'vs/platform/electron/node/electron';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { createSimpleChannelProxy } from 'vs/platform/ipc/node/simpleIpcProxy';
export class ElectronService {
_serviceBrand: undefined;
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
return createSimpleChannelProxy<IElectronService>(mainProcessService.getChannel('electron'));
}
}

View File

@@ -0,0 +1,47 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IElectronService } from 'vs/platform/electron/node/electron';
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { MessageBoxOptions, MessageBoxReturnValue, shell } from 'electron';
export class ElectronMainService implements IElectronService {
_serviceBrand: undefined;
constructor(
@IWindowsMainService private readonly windowsMainService: IWindowsMainService
) {
}
//#region Window
private get window(): ICodeWindow | undefined {
return this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();
}
async windowCount(): Promise<number> {
return this.windowsMainService.getWindowCount();
}
//#endregion
//#region Other
async showMessageBox(options: MessageBoxOptions): Promise<MessageBoxReturnValue> {
const result = await this.windowsMainService.showMessageBox(options, this.window);
return {
response: result.button,
checkboxChecked: !!result.checkboxChecked
};
}
async showItemInFolder(path: string): Promise<void> {
shell.showItemInFolder(path);
}
//#endregion
}

View File

@@ -0,0 +1,23 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MessageBoxOptions, MessageBoxReturnValue } from 'electron';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IElectronService = createDecorator<IElectronService>('electronService');
export interface IElectronService {
_serviceBrand: undefined;
// Window
windowCount(): Promise<number>;
// Dialogs
showMessageBox(options: MessageBoxOptions): Promise<MessageBoxReturnValue>;
// OS
showItemInFolder(path: string): Promise<void>;
}

View File

@@ -123,6 +123,7 @@ export interface IEnvironmentService {
// user roaming data
userRoamingDataHome: URI;
settingsResource: URI;
settingsSyncPreviewResource: URI;
keybindingsResource: URI;
keyboardLayoutResource: URI;
localeResource: URI;

View File

@@ -10,8 +10,7 @@ import * as os from 'os';
import * as path from 'vs/base/common/path';
import * as resources from 'vs/base/common/resources';
import { memoize } from 'vs/base/common/decorators';
import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/node/product';
import product from 'vs/platform/product/common/product';
import { toLocalISOString } from 'vs/base/common/date';
import { isWindows, isLinux } from 'vs/base/common/platform';
import { getPathFromAmdModule } from 'vs/base/common/amd';
@@ -26,16 +25,16 @@ function getNixIPCHandle(userDataPath: string, type: string): string {
if (xdgRuntimeDir && !vscodePortable) {
const scope = crypto.createHash('md5').update(userDataPath).digest('hex').substr(0, 8);
return path.join(xdgRuntimeDir, `vscode-${scope}-${pkg.version}-${type}.sock`);
return path.join(xdgRuntimeDir, `vscode-${scope}-${product.version}-${type}.sock`);
}
return path.join(userDataPath, `${pkg.version}-${type}.sock`);
return path.join(userDataPath, `${product.version}-${type}.sock`);
}
function getWin32IPCHandle(userDataPath: string, type: string): string {
const scope = crypto.createHash('md5').update(userDataPath).digest('hex');
return `\\\\.\\pipe\\${scope}-${pkg.version}-${type}-sock`;
return `\\\\.\\pipe\\${scope}-${product.version}-${type}-sock`;
}
function getIPCHandle(userDataPath: string, type: string): string {
@@ -117,6 +116,9 @@ export class EnvironmentService implements IEnvironmentService {
@memoize
get settingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'settings.json'); }
@memoize
get settingsSyncPreviewResource(): URI { return resources.joinPath(this.userRoamingDataHome, '.settings.json'); }
@memoize
get machineSettingsHome(): URI { return URI.file(path.join(this.userDataPath, 'Machine')); }

View File

@@ -26,7 +26,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { IProductService } from 'vs/platform/product/common/product';
import { IProductService } from 'vs/platform/product/common/productService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { optional } from 'vs/platform/instantiation/common/instantiation';

View File

@@ -28,7 +28,7 @@ import { Limiter, createCancelablePromise, CancelablePromise, Queue } from 'vs/b
import { Event, Emitter } from 'vs/base/common/event';
import * as semver from 'semver-umd';
import { URI } from 'vs/base/common/uri';
import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/common/product';
import { isMacintosh, isWindows } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/extensionsManifestCache';
@@ -46,9 +46,6 @@ import { getPathFromAmdModule } from 'vs/base/common/amd';
import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions';
// {{SQL CARBON EDIT}
import product from 'vs/platform/product/node/product';
const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem';
const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser';
const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled';
@@ -210,8 +207,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) };
let operation: InstallOperation = InstallOperation.Install;
// {{SQL CARBON EDIT - Check VSCode and ADS version}}
if (manifest.engines && ((manifest.engines.vscode && !isEngineValid(manifest.engines.vscode, product.vscodeVersion)) || (manifest.engines.azdata && !isEngineValid(manifest.engines.azdata, pkg.version)))) {
return Promise.reject(new Error(nls.localize('incompatible', "Unable to install version '{2}' of extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", identifier.id, pkg.version, manifest.version)));
if (manifest.engines && ((manifest.engines.vscode && !isEngineValid(manifest.engines.vscode, product.vscodeVersion)) || (manifest.engines.azdata && !isEngineValid(manifest.engines.azdata, product.version)))) {
return Promise.reject(new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", identifier.id, product.version)));
}
const identifierWithVersion = new ExtensionIdentifierWithVersion(identifier, manifest.version);
return this.getInstalled(ExtensionType.User)
@@ -376,7 +373,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
const compatibleExtension = await this.galleryService.getCompatibleExtension(extension);
if (!compatibleExtension) {
return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install '{0}' extension because it is not compatible with the current version of VS Code (version {1}).", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE));
return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install '{0}' extension because it is not compatible with the current version of VS Code (version {1}).", extension.identifier.id, product.version), INSTALL_ERROR_INCOMPATIBLE));
}
return compatibleExtension;

View File

@@ -18,7 +18,7 @@ import { FileService } from 'vs/platform/files/common/fileService';
import { NullLogService } from 'vs/platform/log/common/log';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/common/product';
suite('Extension Gallery Service', () => {
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'extensiongalleryservice');
@@ -53,10 +53,10 @@ suite('Extension Gallery Service', () => {
const args = ['--user-data-dir', marketplaceHome];
const environmentService = new EnvironmentService(parseArgs(args, OPTIONS), process.execPath);
return resolveMarketplaceHeaders(pkg.version, environmentService, fileService).then(headers => {
return resolveMarketplaceHeaders(product.version, environmentService, fileService).then(headers => {
assert.ok(isUUID(headers['X-Market-User-Id']));
return resolveMarketplaceHeaders(pkg.version, environmentService, fileService).then(headers2 => {
return resolveMarketplaceHeaders(product.version, environmentService, fileService).then(headers2 => {
assert.equal(headers['X-Market-User-Id'], headers2['X-Market-User-Id']);
});
});

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as chokidar from 'vscode-chokidar';
import * as chokidar from 'chokidar';
import * as fs from 'fs';
import * as gracefulFs from 'graceful-fs';
gracefulFs.gracefulify(fs);

View File

@@ -3,13 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event as CommonEvent } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { URI } from 'vs/base/common/uri';
import { IPath } from 'vs/platform/windows/common/windows';
export const IHistoryMainService = createDecorator<IHistoryMainService>('historyMainService');
export interface IRecentlyOpened {
workspaces: Array<IRecentWorkspace | IRecentFolder>;
@@ -44,17 +39,3 @@ export function isRecentFolder(curr: IRecent): curr is IRecentFolder {
export function isRecentFile(curr: IRecent): curr is IRecentFile {
return curr.hasOwnProperty('fileUri');
}
export interface IHistoryMainService {
_serviceBrand: undefined;
onRecentlyOpenedChange: CommonEvent<void>;
addRecentlyOpened(recents: IRecent[]): void;
getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier, currentFolder?: ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened;
removeFromRecentlyOpened(paths: URI[]): void;
clearRecentlyOpened(): void;
updateWindowsJumpList(): void;
}

View File

@@ -12,8 +12,9 @@ import { getBaseLabel, getPathLabel } from 'vs/base/common/labels';
import { IPath } from 'vs/platform/windows/common/windows';
import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
import { isWindows, isMacintosh } from 'vs/base/common/platform';
import { IWorkspaceIdentifier, IWorkspacesMainService, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IHistoryMainService, IRecentlyOpened, isRecentWorkspace, isRecentFolder, IRecent, isRecentFile, IRecentFolder, IRecentWorkspace, IRecentFile } from 'vs/platform/history/common/history';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { IRecentlyOpened, isRecentWorkspace, isRecentFolder, IRecent, isRecentFile, IRecentFolder, IRecentWorkspace, IRecentFile } from 'vs/platform/history/common/history';
import { ThrottledDelayer } from 'vs/base/common/async';
import { isEqual as areResourcesEqual, dirname, originalFSPath, basename } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
@@ -22,7 +23,24 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { getSimpleWorkspaceLabel } from 'vs/platform/label/common/label';
import { toStoreData, restoreRecentlyOpened, RecentlyOpenedStorageData } from 'vs/platform/history/common/historyStorage';
import { exists } from 'vs/base/node/pfs';
import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IHistoryMainService = createDecorator<IHistoryMainService>('historyMainService');
export interface IHistoryMainService {
_serviceBrand: undefined;
onRecentlyOpenedChange: CommonEvent<void>;
addRecentlyOpened(recents: IRecent[]): void;
getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier, currentFolder?: ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened;
removeFromRecentlyOpened(paths: URI[]): void;
clearRecentlyOpened(): void;
updateWindowsJumpList(): void;
}
export class HistoryMainService implements IHistoryMainService {
@@ -51,11 +69,11 @@ export class HistoryMainService implements IHistoryMainService {
@ILogService private readonly logService: ILogService,
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILifecycleService lifecycleService: ILifecycleService
@ILifecycleMainService lifecycleMainService: ILifecycleMainService
) {
this.macOSRecentDocumentsUpdater = new ThrottledDelayer<void>(800);
lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList());
lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList());
}
private handleWindowsJumpList(): void {

View File

@@ -0,0 +1,48 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
//
// Use both `SimpleServiceProxyChannel` and `createSimpleChannelProxy`
// for a very basic process <=> process communication over methods.
//
export class SimpleServiceProxyChannel implements IServerChannel {
private service: { [key: string]: unknown };
constructor(service: unknown) {
this.service = service as { [key: string]: unknown };
}
listen<T>(_: unknown, event: string): Event<T> {
throw new Error(`Events are currently unsupported by SimpleServiceProxyChannel: ${event}`);
}
call(_: unknown, command: string, args: any[]): Promise<any> {
const target = this.service[command];
if (typeof target === 'function') {
return target.apply(this.service, args);
}
throw new Error(`Method not found: ${command}`);
}
}
export function createSimpleChannelProxy<T>(channel: IChannel): T {
return new Proxy({}, {
get(_target, propKey, _receiver) {
if (typeof propKey === 'string') {
return function (...args: any[]) {
return channel.call(propKey, args);
};
}
throw new Error(`Unable to provide main channel proxy implementation for: ${String(propKey)}`);
}
}) as T;
}

View File

@@ -3,29 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IIssueService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/node/issue';
import { IIssueService } from 'vs/platform/issue/node/issue';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { createSimpleChannelProxy } from 'vs/platform/ipc/node/simpleIpcProxy';
export class IssueService implements IIssueService {
export class IssueService {
_serviceBrand: undefined;
private channel: IChannel;
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
this.channel = mainProcessService.getChannel('issue');
}
openReporter(data: IssueReporterData): Promise<void> {
return this.channel.call('openIssueReporter', data);
}
openProcessExplorer(data: ProcessExplorerData): Promise<void> {
return this.channel.call('openProcessExplorer', data);
}
getSystemStatus(): Promise<string> {
return this.channel.call('getSystemStatus');
return createSimpleChannelProxy<IIssueService>(mainProcessService.getChannel('issue'));
}
}

View File

@@ -7,8 +7,8 @@ import { localize } from 'vs/nls';
import * as objects from 'vs/base/common/objects';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/node/issue';
import { BrowserWindow, ipcMain, screen, Event, dialog } from 'electron';
import { ILaunchService } from 'vs/platform/launch/electron-main/launchService';
import { BrowserWindow, ipcMain, screen, dialog } from 'electron';
import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchService';
import { PerformanceInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -20,7 +20,7 @@ import { listProcesses } from 'vs/base/node/ps';
const DEFAULT_BACKGROUND_COLOR = '#1E1E1E';
export class IssueService implements IIssueService {
export class IssueMainService implements IIssueService {
_serviceBrand: undefined;
_issueWindow: BrowserWindow | null = null;
_issueParentWindow: BrowserWindow | null = null;
@@ -31,7 +31,7 @@ export class IssueService implements IIssueService {
private machineId: string,
private userEnv: IProcessEnvironment,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILaunchService private readonly launchService: ILaunchService,
@ILaunchMainService private readonly launchMainService: ILaunchMainService,
@ILogService private readonly logService: ILogService,
@IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService,
@IWindowsService private readonly windowsService: IWindowsService
@@ -40,8 +40,8 @@ export class IssueService implements IIssueService {
}
private registerListeners(): void {
ipcMain.on('vscode:issueSystemInfoRequest', async (event: Event) => {
Promise.all([this.launchService.getMainProcessInfo(), this.launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })])
ipcMain.on('vscode:issueSystemInfoRequest', async (event: Electron.IpcMainEvent) => {
Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })])
.then(result => {
const [info, remoteData] = result;
this.diagnosticsService.getSystemInfo(info, remoteData).then(msg => {
@@ -50,13 +50,13 @@ export class IssueService implements IIssueService {
});
});
ipcMain.on('vscode:listProcesses', async (event: Event) => {
ipcMain.on('vscode:listProcesses', async (event: Electron.IpcMainEvent) => {
const processes = [];
try {
const mainPid = await this.launchService.getMainProcessId();
const mainPid = await this.launchMainService.getMainProcessId();
processes.push({ name: localize('local', "Local"), rootProcess: await listProcesses(mainPid) });
(await this.launchService.getRemoteDiagnostics({ includeProcesses: true }))
(await this.launchMainService.getRemoteDiagnostics({ includeProcesses: true }))
.forEach(data => {
if (isRemoteDiagnosticError(data)) {
processes.push({
@@ -79,7 +79,7 @@ export class IssueService implements IIssueService {
event.sender.send('vscode:listProcessesResponse', processes);
});
ipcMain.on('vscode:issueReporterClipboard', (event: Event) => {
ipcMain.on('vscode:issueReporterClipboard', (event: Electron.IpcMainEvent) => {
const messageOptions = {
message: localize('issueReporterWriteToClipboard', "There is too much data to send to GitHub. Would you like to write the information to the clipboard so that it can be pasted?"),
type: 'warning',
@@ -90,13 +90,14 @@ export class IssueService implements IIssueService {
};
if (this._issueWindow) {
dialog.showMessageBox(this._issueWindow, messageOptions, response => {
event.sender.send('vscode:issueReporterClipboardResponse', response === 0);
});
dialog.showMessageBox(this._issueWindow, messageOptions)
.then(result => {
event.sender.send('vscode:issueReporterClipboardResponse', result.response === 0);
});
}
});
ipcMain.on('vscode:issuePerformanceInfoRequest', (event: Event) => {
ipcMain.on('vscode:issuePerformanceInfoRequest', (event: Electron.IpcMainEvent) => {
this.getPerformanceInfo().then(msg => {
event.sender.send('vscode:issuePerformanceInfoResponse', msg);
});
@@ -113,14 +114,15 @@ export class IssueService implements IIssueService {
};
if (this._issueWindow) {
dialog.showMessageBox(this._issueWindow, messageOptions, (response) => {
if (response === 0) {
if (this._issueWindow) {
this._issueWindow.destroy();
this._issueWindow = null;
dialog.showMessageBox(this._issueWindow, messageOptions)
.then(result => {
if (result.response === 0) {
if (this._issueWindow) {
this._issueWindow.destroy();
this._issueWindow = null;
}
}
}
});
});
}
});
@@ -148,14 +150,14 @@ export class IssueService implements IIssueService {
this.windowsService.openExternal(arg);
});
ipcMain.on('vscode:closeIssueReporter', (event: Event) => {
ipcMain.on('vscode:closeIssueReporter', (event: Electron.IpcMainEvent) => {
if (this._issueWindow) {
this._issueWindow.close();
}
});
ipcMain.on('windowsInfoRequest', (event: Event) => {
this.launchService.getMainProcessInfo().then(info => {
ipcMain.on('windowsInfoRequest', (event: Electron.IpcMainEvent) => {
this.launchMainService.getMainProcessInfo().then(info => {
event.sender.send('vscode:windowsInfoResponse', info.windows);
});
});
@@ -178,7 +180,10 @@ export class IssueService implements IIssueService {
x: position.x,
y: position.y,
title: localize('issueReporter', "Issue Reporter"),
backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR
backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR,
webPreferences: {
nodeIntegration: true
}
});
this._issueWindow.setMenuBarVisibility(false); // workaround for now, until a menu is implemented
@@ -224,7 +229,10 @@ export class IssueService implements IIssueService {
x: position.x,
y: position.y,
backgroundColor: data.styles.backgroundColor,
title: localize('processExplorer', "Process Explorer")
title: localize('processExplorer', "Process Explorer"),
webPreferences: {
nodeIntegration: true
}
});
this._processExplorerWindow.setMenuBarVisibility(false);
@@ -260,7 +268,7 @@ export class IssueService implements IIssueService {
}
public async getSystemStatus(): Promise<string> {
return Promise.all([this.launchService.getMainProcessInfo(), this.launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })])
return Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })])
.then(result => {
const [info, remoteData] = result;
return this.diagnosticsService.getDiagnostics(info, remoteData);
@@ -337,7 +345,7 @@ export class IssueService implements IIssueService {
private getPerformanceInfo(): Promise<PerformanceInfo> {
return new Promise(async (resolve, reject) => {
Promise.all([this.launchService.getMainProcessInfo(), this.launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true })])
Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true })])
.then(result => {
const [info, remoteData] = result;
this.diagnosticsService.getPerformanceInfo(info, remoteData)

View File

@@ -1,30 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { IIssueService } from 'vs/platform/issue/node/issue';
export class IssueChannel implements IServerChannel {
constructor(private service: IIssueService) { }
listen<T>(_: unknown, event: string): Event<T> {
throw new Error(`Event not found: ${event}`);
}
call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'openIssueReporter':
return this.service.openReporter(arg);
case 'openProcessExplorer':
return this.service.openProcessExplorer(arg);
case 'getSystemStatus':
return this.service.getSystemStatus();
}
throw new Error(`Call not found: ${command}`);
}
}

View File

@@ -2,6 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { UriComponents } from 'vs/base/common/uri';
export interface IWindowInfo {
@@ -18,4 +19,4 @@ export interface IMainProcessInfo {
windows: IWindowInfo[];
screenReader: boolean;
gpuFeatureStatus: any;
}
}

View File

@@ -12,17 +12,17 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { OpenContext, IWindowSettings } from 'vs/platform/windows/common/windows';
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { whenDeleted } from 'vs/base/node/pfs';
import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { URI } from 'vs/base/common/uri';
import { BrowserWindow, ipcMain, Event as IpcEvent, app } from 'electron';
import { Event } from 'vs/base/common/event';
import { coalesce } from 'vs/base/common/arrays';
import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/common/launchService';
import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/common/launch';
export const ID = 'launchService';
export const ILaunchService = createDecorator<ILaunchService>(ID);
export const ID = 'launchMainService';
export const ILaunchMainService = createDecorator<ILaunchMainService>(ID);
export interface IStartArguments {
args: ParsedArgs;
@@ -51,7 +51,7 @@ function parseOpenUrl(args: ParsedArgs): URI[] {
return [];
}
export interface ILaunchService {
export interface ILaunchMainService {
_serviceBrand: undefined;
start(args: ParsedArgs, userEnv: IProcessEnvironment): Promise<void>;
getMainProcessId(): Promise<number>;
@@ -62,7 +62,7 @@ export interface ILaunchService {
export class LaunchChannel implements IServerChannel {
constructor(private service: ILaunchService) { }
constructor(private service: ILaunchMainService) { }
listen<T>(_: unknown, event: string): Event<T> {
throw new Error(`Event not found: ${event}`);
@@ -91,7 +91,7 @@ export class LaunchChannel implements IServerChannel {
}
}
export class LaunchChannelClient implements ILaunchService {
export class LaunchChannelClient implements ILaunchMainService {
_serviceBrand: undefined;
@@ -118,7 +118,7 @@ export class LaunchChannelClient implements ILaunchService {
}
}
export class LaunchService implements ILaunchService {
export class LaunchMainService implements ILaunchMainService {
_serviceBrand: undefined;
@@ -276,7 +276,7 @@ export class LaunchService implements ILaunchService {
mainPID: process.pid,
mainArguments: process.argv.slice(1),
windows,
screenReader: app.isAccessibilitySupportEnabled(),
screenReader: !!app.accessibilitySupportEnabled,
gpuFeatureStatus: app.getGPUFeatureStatus()
});
}

View File

@@ -19,7 +19,7 @@ export class LifecycleService extends AbstractLifecycleService {
_serviceBrand: undefined;
private shutdownReason: ShutdownReason;
private shutdownReason: ShutdownReason | undefined;
constructor(
@INotificationService private readonly notificationService: INotificationService,

View File

@@ -14,7 +14,7 @@ import { isMacintosh, isWindows } from 'vs/base/common/platform';
import { Disposable } from 'vs/base/common/lifecycle';
import { Barrier } from 'vs/base/common/async';
export const ILifecycleService = createDecorator<ILifecycleService>('lifecycleService');
export const ILifecycleMainService = createDecorator<ILifecycleMainService>('lifecycleMainService');
export const enum UnloadReason {
CLOSE = 1,
@@ -38,7 +38,7 @@ export interface ShutdownEvent {
join(promise: Promise<void>): void;
}
export interface ILifecycleService {
export interface ILifecycleMainService {
_serviceBrand: undefined;
@@ -129,7 +129,7 @@ export const enum LifecycleMainPhase {
AfterWindowOpen = 3
}
export class LifecycleService extends Disposable implements ILifecycleService {
export class LifecycleMainService extends Disposable implements ILifecycleMainService {
_serviceBrand: undefined;
@@ -178,10 +178,10 @@ export class LifecycleService extends Disposable implements ILifecycleService {
}
private handleRestarted(): void {
this._wasRestarted = !!this.stateService.getItem(LifecycleService.QUIT_FROM_RESTART_MARKER);
this._wasRestarted = !!this.stateService.getItem(LifecycleMainService.QUIT_FROM_RESTART_MARKER);
if (this._wasRestarted) {
this.stateService.removeItem(LifecycleService.QUIT_FROM_RESTART_MARKER); // remove the marker right after if found
this.stateService.removeItem(LifecycleMainService.QUIT_FROM_RESTART_MARKER); // remove the marker right after if found
}
}
@@ -468,7 +468,7 @@ export class LifecycleService extends Disposable implements ILifecycleService {
// Remember the reason for quit was to restart
if (fromUpdate) {
this.stateService.setItem(LifecycleService.QUIT_FROM_RESTART_MARKER, true);
this.stateService.setItem(LifecycleMainService.QUIT_FROM_RESTART_MARKER, true);
}
this.pendingQuitPromise = new Promise(resolve => {
@@ -507,7 +507,7 @@ export class LifecycleService extends Disposable implements ILifecycleService {
if (!quitVetoed) {
// Remember the reason for quit was to restart
this.stateService.setItem(LifecycleService.QUIT_FROM_RESTART_MARKER, true);
this.stateService.setItem(LifecycleMainService.QUIT_FROM_RESTART_MARKER, true);
// Windows: we are about to restart and as such we need to restore the original
// current working directory we had on startup to get the exact same startup

View File

@@ -12,7 +12,7 @@ import { Queue } from 'vs/base/common/async';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ILogService } from 'vs/platform/log/common/log';
import { isValidLocalization, ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations';
import product from 'vs/platform/product/node/product';
import product from 'vs/platform/product/common/product';
import { distinct, equals } from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { Schemas } from 'vs/base/common/network';

View File

@@ -8,6 +8,7 @@ import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { isWindows } from 'vs/base/common/platform';
import { Event, Emitter } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { LoggerChannelClient } from 'vs/platform/log/common/logIpc';
export const ILogService = createServiceDecorator<ILogService>('logService');
@@ -183,6 +184,54 @@ export class ConsoleLogService extends AbstractLogService implements ILogService
dispose(): void { }
}
export class ConsoleLogInMainService extends AbstractLogService implements ILogService {
_serviceBrand: undefined;
constructor(private readonly client: LoggerChannelClient, logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super();
this.setLevel(logLevel);
}
trace(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Trace) {
this.client.consoleLog('trace', [message, ...args]);
}
}
debug(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Debug) {
this.client.consoleLog('debug', [message, ...args]);
}
}
info(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Info) {
this.client.consoleLog('info', [message, ...args]);
}
}
warn(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Warning) {
this.client.consoleLog('warn', [message, ...args]);
}
}
error(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Error) {
this.client.consoleLog('error', [message, ...args]);
}
}
critical(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Critical) {
this.client.consoleLog('critical', [message, ...args]);
}
}
dispose(): void { }
}
export class MultiplexLogService extends AbstractLogService implements ILogService {
_serviceBrand: undefined;
@@ -326,4 +375,4 @@ export function getLogLevel(environmentService: IEnvironmentService): LogLevel {
}
}
return DEFAULT_LOG_LEVEL;
}
}

View File

@@ -7,7 +7,7 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { LogLevel, ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
import { Event } from 'vs/base/common/event';
export class LogLevelSetterChannel implements IServerChannel {
export class LoggerChannel implements IServerChannel {
onDidChangeLogLevel: Event<LogLevel>;
@@ -26,13 +26,32 @@ export class LogLevelSetterChannel implements IServerChannel {
call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'setLevel': this.service.setLevel(arg); return Promise.resolve();
case 'consoleLog': this.consoleLog(arg[0], arg[1]); return Promise.resolve();
}
throw new Error(`Call not found: ${command}`);
}
private consoleLog(severity: string, args: string[]): void {
let consoleFn = console.log;
switch (severity) {
case 'error':
consoleFn = console.error;
break;
case 'warn':
consoleFn = console.warn;
break;
case 'info':
consoleFn = console.info;
break;
}
consoleFn.call(console, ...args);
}
}
export class LogLevelSetterChannelClient {
export class LoggerChannelClient {
constructor(private channel: IChannel) { }
@@ -43,12 +62,16 @@ export class LogLevelSetterChannelClient {
setLevel(level: LogLevel): void {
this.channel.call('setLevel', level);
}
consoleLog(severity: string, args: string[]): void {
this.channel.call('consoleLog', [severity, args]);
}
}
export class FollowerLogService extends DelegatedLogService implements ILogService {
_serviceBrand: undefined;
constructor(private master: LogLevelSetterChannelClient, logService: ILogService) {
constructor(private master: LoggerChannelClient, logService: ILogService) {
super(logService);
this._register(master.onDidChangeLogLevel(level => logService.setLevel(level)));
}
@@ -56,4 +79,4 @@ export class FollowerLogService extends DelegatedLogService implements ILogServi
setLevel(level: LogLevel): void {
this.master.setLevel(level);
}
}
}

View File

@@ -11,16 +11,16 @@ import { OpenContext, IRunActionInWindowRequest, getTitleBarStyle, IRunKeybindin
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IUpdateService, StateType } from 'vs/platform/update/common/update';
import product from 'vs/platform/product/node/product';
import product from 'vs/platform/product/common/product';
import { RunOnceScheduler } from 'vs/base/common/async';
import { ILogService } from 'vs/platform/log/common/log';
import { mnemonicMenuLabel as baseMnemonicLabel } from 'vs/base/common/labels';
import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows';
import { IHistoryMainService } from 'vs/platform/history/common/history';
import { IHistoryMainService } from 'vs/platform/history/electron-main/historyMainService';
import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction, IMenubarMenu, isMenubarMenuItemUriAction } from 'vs/platform/menubar/node/menubar';
import { URI } from 'vs/base/common/uri';
import { IStateService } from 'vs/platform/state/common/state';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
const telemetryFrom = 'menu';
@@ -68,7 +68,7 @@ export class Menubar {
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IHistoryMainService private readonly historyMainService: IHistoryMainService,
@IStateService private readonly stateService: IStateService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@ILogService private readonly logService: ILogService
) {
this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0);
@@ -160,7 +160,7 @@ export class Menubar {
private registerListeners(): void {
// Keep flag when app quits
this.lifecycleService.onWillShutdown(() => this.willShutdown = true);
this.lifecycleMainService.onWillShutdown(() => this.willShutdown = true);
// // Listen to some events from window service to update menu
this.windowsMainService.onWindowsCountChanged(e => this.onWindowsCountChanged(e));
@@ -368,7 +368,7 @@ export class Menubar {
const servicesMenu = new Menu();
const services = new MenuItem({ label: nls.localize('mServices', "Services"), role: 'services', submenu: servicesMenu });
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 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: () => {
@@ -503,7 +503,7 @@ export class Menubar {
}, false));
}
private isOptionClick(event: Electron.Event): boolean {
private isOptionClick(event: Electron.KeyboardEvent): boolean {
return !!(event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey))));
}
@@ -597,7 +597,7 @@ export class Menubar {
}
}
private static _menuItemIsTriggeredViaKeybinding(event: Electron.Event, userSettingsLabel: string): boolean {
private static _menuItemIsTriggeredViaKeybinding(event: Electron.KeyboardEvent, userSettingsLabel: string): boolean {
// The event coming in from Electron will inform us only about the modifier keys pressed.
// The strategy here is to check if the modifier keys match those of the keybinding,
// since it is highly unlikely to use modifier keys when clicking with the mouse

View File

@@ -8,7 +8,8 @@ import { Menubar } from 'vs/platform/menubar/electron-main/menubar';
import { ILogService } from 'vs/platform/log/common/log';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
export class MenubarService implements IMenubarService {
export class MenubarMainService implements IMenubarService {
_serviceBrand: undefined;
private _menubar: Menubar;
@@ -30,4 +31,4 @@ export class MenubarService implements IMenubarService {
return Promise.resolve(undefined);
}
}
}

View File

@@ -5,7 +5,7 @@
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
export const IOpenerService = createDecorator<IOpenerService>('openerService');
@@ -18,6 +18,10 @@ export interface IValidator {
shouldOpen(resource: URI): Promise<boolean>;
}
export interface IExternalUriResolver {
resolveExternalUri(resource: URI): Promise<URI>;
}
export interface IOpenerService {
_serviceBrand: undefined;
@@ -29,10 +33,15 @@ export interface IOpenerService {
/**
* Register a participant that can validate if the URI resource be opened.
* validators are run before openers.
* Validators are run before openers.
*/
registerValidator(validator: IValidator): IDisposable;
/**
* Register a participant that can resolve an external URI resource to be opened.
*/
registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable;
/**
* Opens a resource, like a webaddress, a document uri, or executes command.
*
@@ -45,7 +54,8 @@ export interface IOpenerService {
export const NullOpenerService: IOpenerService = Object.freeze({
_serviceBrand: undefined,
registerOpener() { return { dispose() { } }; },
registerValidator() { return { dispose() { } }; },
registerOpener() { return Disposable.None; },
registerValidator() { return Disposable.None; },
registerExternalUriResolver() { return Disposable.None; },
open() { return Promise.resolve(false); },
});

View File

@@ -1,21 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IProductConfiguration } from 'vs/platform/product/common/product';
import { assign } from 'vs/base/common/objects';
// Built time configuration (do NOT modify)
const product = { /*BUILD->INSERT_PRODUCT_CONFIGURATION*/ } as IProductConfiguration;
// Running out of sources
if (Object.keys(product).length === 0) {
assign(product, {
version: '1.39.0-dev',
nameLong: 'Visual Studio Code Web Dev',
nameShort: 'VSCode Web Dev'
});
}
export default product;

View File

@@ -3,125 +3,57 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IProductConfiguration } from 'vs/platform/product/common/productService';
import { assign } from 'vs/base/common/objects';
import { isWeb } from 'vs/base/common/platform';
import * as path from 'vs/base/common/path';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { env } from 'vs/base/common/process';
export const IProductService = createDecorator<IProductService>('productService');
let product: IProductConfiguration;
export interface IProductService extends Readonly<IProductConfiguration> {
// Web
if (isWeb) {
_serviceBrand: undefined;
// Built time configuration (do NOT modify)
product = { /*BUILD->INSERT_PRODUCT_CONFIGURATION*/ } as IProductConfiguration;
// Running out of sources
if (Object.keys(product).length === 0) {
assign(product, {
version: '1.39.0-dev',
nameLong: 'Visual Studio Code Web Dev',
nameShort: 'VSCode Web Dev'
});
}
}
export interface IProductConfiguration {
readonly version: string;
readonly date?: string;
readonly quality?: string;
readonly commit?: string;
// Node: AMD loader
else if (typeof require !== 'undefined' && typeof require.__$__nodeRequire === 'function') {
readonly nameShort: string;
readonly nameLong: string;
// Obtain values from product.json and package.json
const rootPath = path.dirname(getPathFromAmdModule(require, ''));
readonly win32AppUserModelId?: string;
readonly win32MutexName?: string;
readonly applicationName: string;
product = assign({}, require.__$__nodeRequire(path.join(rootPath, 'product.json')) as IProductConfiguration);
const pkg = require.__$__nodeRequire(path.join(rootPath, 'package.json')) as { version: string; };
readonly urlProtocol: string;
readonly dataFolderName: string;
// Running out of sources
if (env['VSCODE_DEV']) {
assign(product, {
nameShort: `${product.nameShort} Dev`,
nameLong: `${product.nameLong} Dev`,
dataFolderName: `${product.dataFolderName}-dev`
});
}
readonly downloadUrl?: string;
readonly updateUrl?: string;
readonly target?: string;
readonly settingsSearchBuildId?: number;
readonly settingsSearchUrl?: string;
readonly experimentsUrl?: string;
readonly extensionsGallery?: {
readonly serviceUrl: string;
readonly itemUrl: string;
readonly controlUrl: string;
readonly recommendationsUrl: string;
};
readonly extensionTips?: { [id: string]: string; };
readonly extensionImportantTips?: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; };
readonly exeBasedExtensionTips?: { [id: string]: IExeBasedExtensionTip; };
readonly extensionKeywords?: { [extension: string]: readonly string[]; };
readonly keymapExtensionTips?: readonly string[];
readonly recommendedExtensions: string[]; // {{SQL CARBON EDIT}}
readonly recommendedExtensionsByScenario: { [area: string]: Array<string> }; // {{SQL CARBON EDIT}}
readonly vscodeVersion: string; // {{SQL CARBON EDIT}} add vscode version
readonly gettingStartedUrl: string; // {SQL CARBON EDIT}
readonly crashReporter?: {
readonly companyName: string;
readonly productName: string;
};
readonly welcomePage?: string;
readonly enableTelemetry?: boolean;
readonly aiConfig?: {
readonly asimovKey: string;
};
readonly sendASmile?: {
readonly reportIssueUrl: string,
readonly requestFeatureUrl: string
};
readonly documentationUrl?: string;
readonly releaseNotesUrl?: string;
readonly keyboardShortcutsUrlMac?: string;
readonly keyboardShortcutsUrlLinux?: string;
readonly keyboardShortcutsUrlWin?: string;
readonly introductoryVideosUrl?: string;
readonly tipsAndTricksUrl?: string;
readonly newsletterSignupUrl?: string;
readonly twitterUrl?: string;
readonly requestFeatureUrl?: string;
readonly reportIssueUrl?: string;
readonly licenseUrl?: string;
readonly privacyStatementUrl?: string;
readonly telemetryOptOutUrl?: string;
readonly npsSurveyUrl?: string;
readonly surveys?: readonly ISurveyData[];
readonly checksums?: { [path: string]: string; };
readonly checksumFailMoreInfoUrl?: string;
readonly hockeyApp?: {
readonly 'win32-ia32': string;
readonly 'win32-x64': string;
readonly 'linux-x64': string;
readonly 'darwin': string;
};
readonly portable?: string;
readonly uiExtensions?: readonly string[];
readonly extensionAllowedProposedApi?: readonly string[];
readonly msftInternalDomains?: string[];
readonly linkProtectionTrustedDomains?: readonly string[];
assign(product, {
version: pkg.version
});
}
export interface IExeBasedExtensionTip {
friendlyName: string;
windowsPath?: string;
recommendations: readonly string[];
important?: boolean;
exeFriendlyName?: string;
// Unknown
else {
throw new Error('Unable to resolve product configuration');
}
export interface ISurveyData {
surveyId: string;
surveyUrl: string;
languageId: string;
editCount: number;
userProbability: number;
}
export default product;

View File

@@ -0,0 +1,128 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IProductService = createDecorator<IProductService>('productService');
export interface IProductService extends Readonly<IProductConfiguration> {
_serviceBrand: undefined;
}
export interface IProductConfiguration {
readonly version: string;
readonly date?: string;
readonly quality?: string;
readonly commit?: string;
readonly nameShort: string;
readonly nameLong: string;
readonly win32AppUserModelId?: string;
readonly win32MutexName?: string;
readonly applicationName: string;
readonly urlProtocol: string;
readonly dataFolderName: string;
readonly downloadUrl?: string;
readonly updateUrl?: string;
readonly target?: string;
readonly settingsSearchBuildId?: number;
readonly settingsSearchUrl?: string;
readonly experimentsUrl?: string;
readonly extensionsGallery?: {
readonly serviceUrl: string;
readonly itemUrl: string;
readonly controlUrl: string;
readonly recommendationsUrl: string;
};
readonly extensionTips?: { [id: string]: string; };
readonly extensionImportantTips?: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; };
readonly exeBasedExtensionTips?: { [id: string]: IExeBasedExtensionTip; };
readonly extensionKeywords?: { [extension: string]: readonly string[]; };
readonly keymapExtensionTips?: readonly string[];
readonly recommendedExtensions: string[]; // {{SQL CARBON EDIT}}
readonly recommendedExtensionsByScenario: { [area: string]: Array<string> }; // {{SQL CARBON EDIT}}
readonly vscodeVersion: string; // {{SQL CARBON EDIT}} add vscode version
readonly gettingStartedUrl: string; // {SQL CARBON EDIT}
readonly crashReporter?: {
readonly companyName: string;
readonly productName: string;
};
readonly welcomePage?: string;
readonly enableTelemetry?: boolean;
readonly aiConfig?: {
readonly asimovKey: string;
};
readonly sendASmile?: {
readonly reportIssueUrl: string,
readonly requestFeatureUrl: string
};
readonly documentationUrl?: string;
readonly releaseNotesUrl?: string;
readonly keyboardShortcutsUrlMac?: string;
readonly keyboardShortcutsUrlLinux?: string;
readonly keyboardShortcutsUrlWin?: string;
readonly introductoryVideosUrl?: string;
readonly tipsAndTricksUrl?: string;
readonly newsletterSignupUrl?: string;
readonly twitterUrl?: string;
readonly requestFeatureUrl?: string;
readonly reportIssueUrl?: string;
readonly licenseUrl?: string;
readonly privacyStatementUrl?: string;
readonly telemetryOptOutUrl?: string;
readonly npsSurveyUrl?: string;
readonly surveys?: readonly ISurveyData[];
readonly checksums?: { [path: string]: string; };
readonly checksumFailMoreInfoUrl?: string;
readonly hockeyApp?: {
readonly 'win32-ia32': string;
readonly 'win32-x64': string;
readonly 'linux-x64': string;
readonly 'darwin': string;
};
readonly portable?: string;
readonly uiExtensions?: readonly string[];
readonly extensionAllowedProposedApi?: readonly string[];
readonly msftInternalDomains?: string[];
readonly linkProtectionTrustedDomains?: readonly string[];
readonly settingsSyncStoreUrl?: string;
}
export interface IExeBasedExtensionTip {
friendlyName: string;
windowsPath?: string;
recommendations: readonly string[];
important?: boolean;
exeFriendlyName?: string;
}
export interface ISurveyData {
surveyId: string;
surveyUrl: string;
languageId: string;
editCount: number;
userProbability: number;
}

View File

@@ -1,16 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'vs/base/common/path';
import { getPathFromAmdModule } from 'vs/base/common/amd';
export interface IPackageConfiguration {
name: string;
version: string;
}
const rootPath = path.dirname(getPathFromAmdModule(require, ''));
const packageJsonPath = path.join(rootPath, 'package.json');
export default require.__$__nodeRequire(packageJsonPath) as IPackageConfiguration;

View File

@@ -1,28 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'vs/base/common/path';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { IProductConfiguration } from 'vs/platform/product/common/product';
import pkg from 'vs/platform/product/node/package';
import { assign } from 'vs/base/common/objects';
const rootPath = path.dirname(getPathFromAmdModule(require, ''));
const productJsonPath = path.join(rootPath, 'product.json');
const product = require.__$__nodeRequire(productJsonPath) as IProductConfiguration;
if (process.env['VSCODE_DEV']) {
assign(product, {
nameShort: `${product.nameShort} Dev`,
nameLong: `${product.nameLong} Dev`,
dataFolderName: `${product.dataFolderName}-dev`
});
}
assign(product, {
version: pkg.version
});
export default product;

View File

@@ -8,6 +8,9 @@ import { ISocket } from 'vs/base/parts/ipc/common/ipc.net';
import { VSBuffer } from 'vs/base/common/buffer';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import * as dom from 'vs/base/browser/dom';
import { RunOnceScheduler } from 'vs/base/common/async';
import { RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
export interface IWebSocketFactory {
create(url: string): IWebSocket;
@@ -23,27 +26,34 @@ export interface IWebSocket {
close(): void;
}
class BrowserWebSocket implements IWebSocket {
class BrowserWebSocket extends Disposable implements IWebSocket {
private readonly _onData = new Emitter<ArrayBuffer>();
public readonly onData = this._onData.event;
public readonly onOpen: Event<void>;
public readonly onClose: Event<void>;
public readonly onError: Event<any>;
private readonly _onClose = this._register(new Emitter<void>());
public readonly onClose = this._onClose.event;
private readonly _onError = this._register(new Emitter<any>());
public readonly onError = this._onError.event;
private readonly _socket: WebSocket;
private readonly _fileReader: FileReader;
private readonly _queue: Blob[];
private _isReading: boolean;
private _isClosed: boolean;
private readonly _socketMessageListener: (ev: MessageEvent) => void;
constructor(socket: WebSocket) {
super();
this._socket = socket;
this._fileReader = new FileReader();
this._queue = [];
this._isReading = false;
this._isClosed = false;
this._fileReader.onload = (event) => {
this._isReading = false;
@@ -71,17 +81,79 @@ class BrowserWebSocket implements IWebSocket {
this._socket.addEventListener('message', this._socketMessageListener);
this.onOpen = Event.fromDOMEventEmitter(this._socket, 'open');
this.onClose = Event.fromDOMEventEmitter(this._socket, 'close');
this.onError = Event.fromDOMEventEmitter(this._socket, 'error');
// WebSockets emit error events that do not contain any real information
// Our only chance of getting to the root cause of an error is to
// listen to the close event which gives out some real information:
// - https://www.w3.org/TR/websockets/#closeevent
// - https://tools.ietf.org/html/rfc6455#section-11.7
//
// But the error event is emitted before the close event, so we therefore
// delay the error event processing in the hope of receiving a close event
// with more information
let pendingErrorEvent: any | null = null;
const sendPendingErrorNow = () => {
const err = pendingErrorEvent;
pendingErrorEvent = null;
this._onError.fire(err);
};
const errorRunner = this._register(new RunOnceScheduler(sendPendingErrorNow, 0));
const sendErrorSoon = (err: any) => {
errorRunner.cancel();
pendingErrorEvent = err;
errorRunner.schedule();
};
const sendErrorNow = (err: any) => {
errorRunner.cancel();
pendingErrorEvent = err;
sendPendingErrorNow();
};
this._register(dom.addDisposableListener(this._socket, 'close', (e: CloseEvent) => {
this._isClosed = true;
if (pendingErrorEvent) {
if (!window.navigator.onLine) {
// The browser is offline => this is a temporary error which might resolve itself
sendErrorNow(new RemoteAuthorityResolverError('Browser is offline', RemoteAuthorityResolverErrorCode.TemporarilyNotAvailable, e));
} else {
// An error event is pending
// The browser appears to be online...
if (!e.wasClean) {
// Let's be optimistic and hope that perhaps the server could not be reached or something
sendErrorNow(new RemoteAuthorityResolverError(e.reason || `WebSocket close with status code ${e.code}`, RemoteAuthorityResolverErrorCode.TemporarilyNotAvailable, e));
} else {
// this was a clean close => send existing error
errorRunner.cancel();
sendPendingErrorNow();
}
}
}
this._onClose.fire();
}));
this._register(dom.addDisposableListener(this._socket, 'error', sendErrorSoon));
}
send(data: ArrayBuffer | ArrayBufferView): void {
if (this._isClosed) {
// Refuse to write data to closed WebSocket...
return;
}
this._socket.send(data);
}
close(): void {
this._isClosed = true;
this._socket.close();
this._socket.removeEventListener('message', this._socketMessageListener);
this.dispose();
}
}

View File

@@ -5,14 +5,14 @@
import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { RemoteAuthorities } from 'vs/base/common/network';
import { URI, UriComponents } from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverService {
_serviceBrand: undefined;
constructor(
resourceUriProvider: ((uri: URI) => UriComponents) | undefined
resourceUriProvider: ((uri: URI) => URI) | undefined
) {
if (resourceUriProvider) {
RemoteAuthorities.setDelegate(resourceUriProvider);

View File

@@ -13,7 +13,7 @@ function getRawRequest(options: IRequestOptions): IRawRequestFunction {
return net.request as any as IRawRequestFunction;
}
export class RequestService extends NodeRequestService {
export class RequestMainService extends NodeRequestService {
request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
return super.request(assign({}, options || {}, { getRawRequest }), token);

View File

@@ -150,7 +150,7 @@ export class BrowserStorageService extends Disposable implements IStorageService
}
async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise<void> {
// TODO@ben implement storage migration in web
throw new Error('Migrating storage is currently unsupported in Web');
}
close(): void {

View File

@@ -8,7 +8,7 @@ import * as os from 'os';
import * as uuid from 'vs/base/common/uuid';
import { readFile } from 'vs/base/node/pfs';
import product from 'vs/platform/product/node/product'; // {{SQL CARBON EDIT}}
import product from 'vs/platform/product/common/product'; // {{SQL CARBON EDIT}}
const productObject = product; // {{SQL CARBON EDIT}}
export async function resolveCommonProperties(commit: string | undefined, version: string | undefined, machineId: string | undefined, msftInternalDomains: string[] | undefined, installSourcePath: string, product?: string): Promise<{ [name: string]: string | boolean | undefined; }> {
const result: { [name: string]: string | boolean | undefined; } = Object.create(null);

View File

@@ -8,7 +8,7 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper
import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
import { cleanRemoteAuthority } from 'vs/platform/telemetry/common/telemetryUtils';
import product from 'vs/platform/product/node/product'; // {{ SQL CARBON EDIT }}
import product from 'vs/platform/product/common/product'; // {{ SQL CARBON EDIT }}
export async function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string | undefined, version: string | undefined, machineId: string, msftInternalDomains: string[] | undefined, installSourcePath: string, remoteAuthority?: string): Promise<{ [name: string]: string | boolean | undefined }> {
const result = await resolveCommonProperties(commit, version, machineId, msftInternalDomains, installSourcePath);

View File

@@ -189,6 +189,8 @@ export const activeContrastBorder = registerColor('contrastActiveBorder', { ligh
export const selectionBackground = registerColor('selection.background', { light: null, dark: null, hc: null }, nls.localize('selectionBackground', "The background color of text selections in the workbench (e.g. for input fields or text areas). Note that this does not apply to selections within the editor."));
export const iconForeground = registerColor('icon.foreground', { light: '#424242', dark: '#C5C5C5', hc: '#FFFFFF' }, nls.localize('iconForeground', "The default color for icons in the workbench."));
// ------ text colors
export const textSeparatorForeground = registerColor('textSeparator.foreground', { light: '#0000002e', dark: '#ffffff2e', hc: Color.black }, nls.localize('textSeparatorForeground', "Color for text separators."));

View File

@@ -41,7 +41,7 @@ export class ThemeMainService implements IThemeMainService {
this.stateService.setItem(THEME_BG_STORAGE_KEY, data.background);
}
public getBackgroundColor(): string {
getBackgroundColor(): string {
if (isWindows && systemPreferences.isInvertedColorScheme()) {
return DEFAULT_BG_HC_BLACK;
}
@@ -64,4 +64,4 @@ export class ThemeMainService implements IThemeMainService {
return background;
}
}
}

View File

@@ -6,8 +6,8 @@
import { Event, Emitter } from 'vs/base/common/event';
import { timeout } from 'vs/base/common/async';
import { IConfigurationService, getMigratedSettingValue } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import product from 'vs/platform/product/node/product';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import product from 'vs/platform/product/common/product';
import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
@@ -44,7 +44,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
}
constructor(
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IConfigurationService protected configurationService: IConfigurationService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IRequestService protected requestService: IRequestService,
@@ -152,7 +152,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
this.logService.trace('update#quitAndInstall(): before lifecycle quit()');
this.lifecycleService.quit(true /* from update */).then(vetod => {
this.lifecycleMainService.quit(true /* from update */).then(vetod => {
this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`);
if (vetod) {
return;

View File

@@ -8,7 +8,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { memoize } from 'vs/base/common/decorators';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { State, IUpdate, StateType, UpdateType } from 'vs/platform/update/common/update';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -28,14 +28,14 @@ export class DarwinUpdateService extends AbstractUpdateService {
@memoize private get onRawUpdateDownloaded(): Event<IUpdate> { return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-downloaded', (_, releaseNotes, version, date) => ({ releaseNotes, version, productVersion: version, date })); }
constructor(
@ILifecycleService lifecycleService: ILifecycleService,
@ILifecycleMainService lifecycleMainService: ILifecycleMainService,
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService requestService: IRequestService,
@ILogService logService: ILogService
) {
super(lifecycleService, configurationService, environmentService, requestService, logService);
super(lifecycleMainService, configurationService, environmentService, requestService, logService);
this.onRawError(this.onError, this, this.disposables);
this.onRawUpdateAvailable(this.onUpdateAvailable, this, this.disposables);
this.onRawUpdateDownloaded(this.onUpdateDownloaded, this, this.disposables);

View File

@@ -3,9 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import product from 'vs/platform/product/node/product';
import product from 'vs/platform/product/common/product';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { State, IUpdate, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -20,14 +20,14 @@ export class LinuxUpdateService extends AbstractUpdateService {
_serviceBrand: undefined;
constructor(
@ILifecycleService lifecycleService: ILifecycleService,
@ILifecycleMainService lifecycleMainService: ILifecycleMainService,
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService requestService: IRequestService,
@ILogService logService: ILogService
) {
super(lifecycleService, configurationService, environmentService, requestService, logService);
super(lifecycleMainService, configurationService, environmentService, requestService, logService);
}
protected buildUpdateFeedUrl(quality: string): string {

View File

@@ -5,7 +5,7 @@
import { Event, Emitter } from 'vs/base/common/event';
import { timeout } from 'vs/base/common/async';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
@@ -35,7 +35,7 @@ abstract class AbstractUpdateService2 implements IUpdateService {
}
constructor(
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IEnvironmentService environmentService: IEnvironmentService,
@ILogService protected logService: ILogService,
) {
@@ -106,7 +106,7 @@ abstract class AbstractUpdateService2 implements IUpdateService {
this.logService.trace('update#quitAndInstall(): before lifecycle quit()');
this.lifecycleService.quit(true /* from update */).then(vetod => {
this.lifecycleMainService.quit(true /* from update */).then(vetod => {
this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`);
if (vetod) {
return;
@@ -139,12 +139,12 @@ export class SnapUpdateService extends AbstractUpdateService2 {
constructor(
private snap: string,
private snapRevision: string,
@ILifecycleService lifecycleService: ILifecycleService,
@ILifecycleMainService lifecycleMainService: ILifecycleMainService,
@IEnvironmentService environmentService: IEnvironmentService,
@ILogService logService: ILogService,
@ITelemetryService private readonly telemetryService: ITelemetryService
) {
super(lifecycleService, environmentService, logService);
super(lifecycleMainService, environmentService, logService);
const watcher = watch(path.dirname(this.snap));
const onChange = Event.fromNodeEventEmitter(watcher, 'change', (_, fileName: string) => fileName);
@@ -152,7 +152,7 @@ export class SnapUpdateService extends AbstractUpdateService2 {
const onDebouncedCurrentChange = Event.debounce(onCurrentChange, (_, e) => e, 2000);
const listener = onDebouncedCurrentChange(this.checkForUpdates, this);
lifecycleService.onWillShutdown(() => {
lifecycleMainService.onWillShutdown(() => {
listener.dispose();
watcher.close();
});

View File

@@ -8,8 +8,8 @@ import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs';
import { memoize } from 'vs/base/common/decorators';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import product from 'vs/platform/product/node/product';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import product from 'vs/platform/product/common/product';
import { State, IUpdate, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -61,7 +61,7 @@ export class Win32UpdateService extends AbstractUpdateService {
}
constructor(
@ILifecycleService lifecycleService: ILifecycleService,
@ILifecycleMainService lifecycleMainService: ILifecycleMainService,
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@@ -69,7 +69,7 @@ export class Win32UpdateService extends AbstractUpdateService {
@ILogService logService: ILogService,
@IFileService private readonly fileService: IFileService
) {
super(lifecycleService, configurationService, environmentService, requestService, logService);
super(lifecycleMainService, configurationService, environmentService, requestService, logService);
if (getUpdateType() === UpdateType.Setup) {
/* __GDPR__

View File

@@ -5,7 +5,7 @@
import { Event } from 'vs/base/common/event';
import { IURLService } from 'vs/platform/url/common/url';
import product from 'vs/platform/product/node/product';
import product from 'vs/platform/product/common/product';
import { app } from 'electron';
import { URI } from 'vs/base/common/uri';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
@@ -28,7 +28,7 @@ export class ElectronURLListener {
constructor(
initial: string | string[],
@IURLService private readonly urlService: IURLService,
@IWindowsMainService windowsService: IWindowsMainService
@IWindowsMainService windowsMainService: IWindowsMainService
) {
const globalBuffer = ((<any>global).getOpenUrls() || []) as string[];
const rawBuffer = [
@@ -58,18 +58,18 @@ export class ElectronURLListener {
const onOpenUrl = Event.filter(Event.map(onOpenElectronUrl, uriFromRawUrl), uri => !!uri);
onOpenUrl(this.urlService.open, this.urlService, this.disposables);
const isWindowReady = windowsService.getWindows()
const isWindowReady = windowsMainService.getWindows()
.filter(w => w.isReady)
.length > 0;
if (isWindowReady) {
flush();
} else {
Event.once(windowsService.onWindowReady)(flush);
Event.once(windowsMainService.onWindowReady)(flush);
}
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { URI, UriComponents } from 'vs/base/common/uri';
import product from 'vs/platform/product/node/product';
import product from 'vs/platform/product/common/product';
import { AbstractURLService } from 'vs/platform/url/common/urlService';
export class URLService extends AbstractURLService {

View File

@@ -0,0 +1,253 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent } from 'vs/platform/files/common/files';
import { IUserData, UserDataSyncStoreError, UserDataSyncStoreErrorCode, ISynchroniser, SyncStatus, ISettingsMergeService, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse, ParseError } from 'vs/base/common/json';
import { localize } from 'vs/nls';
import { Emitter, Event } from 'vs/base/common/event';
import { ILogService } from 'vs/platform/log/common/log';
import { CancelablePromise, createCancelablePromise, ThrottledDelayer } from 'vs/base/common/async';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
interface ISyncPreviewResult {
readonly fileContent: IFileContent | null;
readonly remoteUserData: IUserData;
readonly hasLocalChanged: boolean;
readonly hasRemoteChanged: boolean;
readonly hasConflicts: boolean;
}
export class SettingsSynchroniser extends Disposable implements ISynchroniser {
private static EXTERNAL_USER_DATA_SETTINGS_KEY: string = 'settings';
private syncPreviewResultPromise: CancelablePromise<ISyncPreviewResult> | null = null;
private _status: SyncStatus = SyncStatus.Idle;
get status(): SyncStatus { return this._status; }
private _onDidChangStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangStatus.event;
private readonly throttledDelayer: ThrottledDelayer<void>;
private _onDidChangeLocal: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeLocal: Event<void> = this._onDidChangeLocal.event;
private readonly lastSyncSettingsResource: URI;
constructor(
@IFileService private readonly fileService: IFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
@ISettingsMergeService private readonly settingsMergeService: ISettingsMergeService,
@ILogService private readonly logService: ILogService,
) {
super();
this.lastSyncSettingsResource = joinPath(this.environmentService.userRoamingDataHome, '.lastSyncSettings.json');
this.throttledDelayer = this._register(new ThrottledDelayer<void>(500));
this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.environmentService.settingsResource))(() => this.throttledDelayer.trigger(() => this.onDidChangeSettings())));
}
private async onDidChangeSettings(): Promise<void> {
const localFileContent = await this.getLocalFileContent();
const lastSyncData = await this.getLastSyncUserData();
if (localFileContent && lastSyncData) {
if (localFileContent.value.toString() !== lastSyncData.content) {
this._onDidChangeLocal.fire();
return;
}
}
if (!localFileContent || !lastSyncData) {
this._onDidChangeLocal.fire();
return;
}
}
private setStatus(status: SyncStatus): void {
if (this._status !== status) {
this._status = status;
this._onDidChangStatus.fire(status);
}
}
async sync(_continue?: boolean): Promise<boolean> {
if (_continue) {
return this.continueSync();
}
if (this.status !== SyncStatus.Idle) {
return false;
}
this.setStatus(SyncStatus.Syncing);
try {
const result = await this.getPreview();
if (result.hasConflicts) {
this.setStatus(SyncStatus.HasConflicts);
return false;
}
await this.apply();
return true;
} catch (e) {
this.syncPreviewResultPromise = null;
this.setStatus(SyncStatus.Idle);
if (e instanceof UserDataSyncStoreError && e.code === UserDataSyncStoreErrorCode.Rejected) {
// Rejected as there is a new remote version. Syncing again,
this.logService.info('Failed to Synchronise settings as there is a new remote version available. Synchronising again...');
return this.sync();
}
if (e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) {
// Rejected as there is a new local version. Syncing again.
this.logService.info('Failed to Synchronise settings as there is a new local version available. Synchronising again...');
return this.sync();
}
throw e;
}
}
private async continueSync(): Promise<boolean> {
if (this.status !== SyncStatus.HasConflicts) {
return false;
}
await this.apply();
return true;
}
private async apply(): Promise<void> {
if (!this.syncPreviewResultPromise) {
return;
}
if (await this.fileService.exists(this.environmentService.settingsSyncPreviewResource)) {
const settingsPreivew = await this.fileService.readFile(this.environmentService.settingsSyncPreviewResource);
const content = settingsPreivew.value.toString();
if (this.hasErrors(content)) {
return Promise.reject(localize('errorInvalidSettings', "Unable to sync settings. Please resolve conflicts without any errors/warnings and try again."));
}
let { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise;
if (hasRemoteChanged) {
const ref = await this.writeToRemote(content, remoteUserData.ref);
remoteUserData = { ref, content };
}
if (hasLocalChanged) {
await this.writeToLocal(content, fileContent);
}
if (remoteUserData.content) {
await this.updateLastSyncValue(remoteUserData);
}
// Delete the preview
await this.fileService.del(this.environmentService.settingsSyncPreviewResource);
}
this.syncPreviewResultPromise = null;
this.setStatus(SyncStatus.Idle);
}
private hasErrors(content: string): boolean {
const parseErrors: ParseError[] = [];
parse(content, parseErrors);
return parseErrors.length > 0;
}
private getPreview(): Promise<ISyncPreviewResult> {
if (!this.syncPreviewResultPromise) {
this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview());
}
return this.syncPreviewResultPromise;
}
private async generatePreview(): Promise<ISyncPreviewResult> {
const lastSyncData = await this.getLastSyncUserData();
const remoteUserData = await this.userDataSyncStoreService.read(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, lastSyncData);
const remoteContent: string | null = remoteUserData.content;
// Get file content last to get the latest
const fileContent = await this.getLocalFileContent();
let hasLocalChanged: boolean = false;
let hasRemoteChanged: boolean = false;
let hasConflicts: boolean = false;
// First time sync to remote
if (fileContent && !remoteContent) {
this.logService.trace('Settings Sync: Remote contents does not exist. So sync with settings file.');
hasRemoteChanged = true;
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(fileContent.value.toString()));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
// Settings file does not exist, so sync with remote contents.
if (remoteContent && !fileContent) {
this.logService.trace('Settings Sync: Settings file does not exist. So sync with remote contents');
hasLocalChanged = true;
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(remoteContent));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
if (fileContent && remoteContent) {
const localContent: string = fileContent.value.toString();
if (!lastSyncData // First time sync
|| lastSyncData.content !== localContent // Local has moved forwarded
|| lastSyncData.content !== remoteContent // Remote has moved forwarded
) {
this.logService.trace('Settings Sync: Merging remote contents with settings file.');
const result = await this.settingsMergeService.merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null);
// Sync only if there are changes
if (result.hasChanges) {
hasLocalChanged = result.mergeContent !== localContent;
hasRemoteChanged = result.mergeContent !== remoteContent;
hasConflicts = result.hasConflicts;
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(result.mergeContent));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
}
}
this.logService.trace('Settings Sync: No changes.');
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
private async getLastSyncUserData(): Promise<IUserData | null> {
try {
const content = await this.fileService.readFile(this.lastSyncSettingsResource);
return JSON.parse(content.value.toString());
} catch (error) {
return null;
}
}
private async getLocalFileContent(): Promise<IFileContent | null> {
try {
return await this.fileService.readFile(this.environmentService.settingsResource);
} catch (error) {
return null;
}
}
private async writeToRemote(content: string, ref: string | null): Promise<string> {
return this.userDataSyncStoreService.write(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, content, ref);
}
private async writeToLocal(newContent: string, oldContent: IFileContent | null): Promise<void> {
if (oldContent) {
// file exists already
await this.fileService.writeFile(this.environmentService.settingsResource, VSBuffer.fromString(newContent), oldContent);
} else {
// file does not exist
await this.fileService.createFile(this.environmentService.settingsResource, VSBuffer.fromString(newContent), { overwrite: false });
}
}
private async updateLastSyncValue(remoteUserData: IUserData): Promise<void> {
await this.fileService.writeFile(this.lastSyncSettingsResource, VSBuffer.fromString(JSON.stringify(remoteUserData)));
}
}

View File

@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { ISettingsMergeService } from 'vs/platform/userDataSync/common/userDataSync';
export class SettingsMergeChannel implements IServerChannel {
constructor(private readonly service: ISettingsMergeService) { }
listen(_: unknown, event: string): Event<any> {
throw new Error(`Event not found: ${event}`);
}
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'merge': return this.service.merge(args[0], args[1], args[2]);
}
throw new Error('Invalid call');
}
}
export class SettingsMergeChannelClient implements ISettingsMergeService {
_serviceBrand: undefined;
constructor(private readonly channel: IChannel) {
}
merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> {
return this.channel.call('merge', [localContent, remoteContent, baseContent]);
}
}

View File

@@ -0,0 +1,79 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
export interface IUserData {
ref: string;
content: string | null;
}
export enum UserDataSyncStoreErrorCode {
Rejected = 'Rejected',
Unknown = 'Unknown'
}
export class UserDataSyncStoreError extends Error {
constructor(message: string, public readonly code: UserDataSyncStoreErrorCode) {
super(message);
}
}
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');
export interface IUserDataSyncStoreService {
_serviceBrand: undefined;
readonly enabled: boolean;
readonly loggedIn: boolean;
readonly onDidChangeLoggedIn: Event<boolean>;
login(): Promise<void>;
logout(): Promise<void>;
read(key: string, oldValue: IUserData | null): Promise<IUserData>;
write(key: string, content: string, ref: string | null): Promise<string>;
}
export enum SyncSource {
Settings = 1,
Extensions
}
export enum SyncStatus {
Uninitialized = 'uninitialized',
Idle = 'idle',
Syncing = 'syncing',
HasConflicts = 'hasConflicts',
}
export interface ISynchroniser {
readonly status: SyncStatus;
readonly onDidChangeStatus: Event<SyncStatus>;
readonly onDidChangeLocal: Event<void>;
sync(_continue?: boolean): Promise<boolean>;
}
export const IUserDataSyncService = createDecorator<IUserDataSyncService>('IUserDataSyncService');
export interface IUserDataSyncService extends ISynchroniser {
_serviceBrand: any;
readonly conflictsSource: SyncSource | null;
}
export const ISettingsMergeService = createDecorator<ISettingsMergeService>('ISettingsMergeService');
export interface ISettingsMergeService {
_serviceBrand: undefined;
merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>;
}

View File

@@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync';
export class UserDataSyncChannel implements IServerChannel {
constructor(private readonly service: IUserDataSyncService) { }
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onDidChangeStatus': return this.service.onDidChangeStatus;
case 'onDidChangeLocal': return this.service.onDidChangeLocal;
}
throw new Error(`Event not found: ${event}`);
}
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'sync': return this.service.sync(args[0]);
case 'getConflictsSource': return Promise.resolve(this.service.conflictsSource);
}
throw new Error('Invalid call');
}
}

View File

@@ -0,0 +1,128 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource } from 'vs/platform/userDataSync/common/userDataSync';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
import { Emitter, Event } from 'vs/base/common/event';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { timeout } from 'vs/base/common/async';
export class UserDataSyncService extends Disposable implements IUserDataSyncService {
_serviceBrand: any;
private readonly synchronisers: ISynchroniser[];
private _status: SyncStatus = SyncStatus.Uninitialized;
get status(): SyncStatus { return this._status; }
private _onDidChangeStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangeStatus.event;
readonly onDidChangeLocal: Event<void>;
private _conflictsSource: SyncSource | null = null;
get conflictsSource(): SyncSource | null { return this._conflictsSource; }
constructor(
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
) {
super();
this.synchronisers = [
this.instantiationService.createInstance(SettingsSynchroniser)
];
this.updateStatus();
this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus()));
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal));
}
async sync(_continue?: boolean): Promise<boolean> {
if (!this.userDataSyncStoreService.enabled) {
throw new Error('Not enabled');
}
for (const synchroniser of this.synchronisers) {
if (!await synchroniser.sync(_continue)) {
return false;
}
}
return true;
}
private updateStatus(): void {
this._conflictsSource = this.computeConflictsSource();
this.setStatus(this.computeStatus());
}
private setStatus(status: SyncStatus): void {
if (this._status !== status) {
this._status = status;
this._onDidChangeStatus.fire(status);
}
}
private computeStatus(): SyncStatus {
if (!this.userDataSyncStoreService.enabled) {
return SyncStatus.Uninitialized;
}
if (this.synchronisers.some(s => s.status === SyncStatus.HasConflicts)) {
return SyncStatus.HasConflicts;
}
if (this.synchronisers.some(s => s.status === SyncStatus.Syncing)) {
return SyncStatus.Syncing;
}
return SyncStatus.Idle;
}
private computeConflictsSource(): SyncSource | null {
const source = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0];
if (source) {
if (source instanceof SettingsSynchroniser) {
return SyncSource.Settings;
}
}
return null;
}
}
export class UserDataAutoSync extends Disposable {
constructor(
@IConfigurationService private readonly configurationService: IConfigurationService,
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
) {
super();
if (userDataSyncStoreService.enabled) {
this.sync(true);
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('userConfiguration.enableSync'))(() => this.sync(true)));
// Sync immediately if there is a local change.
this._register(Event.debounce(this.userDataSyncService.onDidChangeLocal, () => undefined, 500)(() => this.sync(false)));
}
}
private async sync(loop: boolean): Promise<void> {
if (this.isSyncEnabled()) {
try {
await this.userDataSyncService.sync();
} catch (e) {
// Ignore errors
}
if (loop) {
await timeout(1000 * 5); // Loop sync for every 5s.
this.sync(loop);
}
}
}
private isSyncEnabled(): boolean {
const { user: userLocal } = this.configurationService.inspect<boolean>('userConfiguration.enableSync');
return userLocal === undefined || userLocal;
}
}

View File

@@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, } from 'vs/base/common/lifecycle';
import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync';
import { IProductService } from 'vs/platform/product/common/productService';
import { Emitter, Event } from 'vs/base/common/event';
import { IRequestService, asText } from 'vs/platform/request/common/request';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IHeaders } from 'vs/base/parts/request/common/request';
export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService {
_serviceBrand: any;
get enabled(): boolean { return !!this.productService.settingsSyncStoreUrl; }
private _loggedIn: boolean = false;
get loggedIn(): boolean { return this._loggedIn; }
private readonly _onDidChangeLoggedIn: Emitter<boolean> = this._register(new Emitter<boolean>());
readonly onDidChangeLoggedIn: Event<boolean> = this._onDidChangeLoggedIn.event;
constructor(
@IProductService private readonly productService: IProductService,
@IRequestService private readonly requestService: IRequestService,
) {
super();
}
async login(): Promise<void> {
}
async logout(): Promise<void> {
}
async read(key: string, oldValue: IUserData | null): Promise<IUserData> {
if (!this.enabled) {
return Promise.reject(new Error('No settings sync store url configured.'));
}
const url = joinPath(URI.parse(this.productService.settingsSyncStoreUrl!), key).toString();
const headers: IHeaders = {};
if (oldValue) {
headers['If-None-Match'] = oldValue.ref;
}
const context = await this.requestService.request({ type: 'GET', url, headers }, CancellationToken.None);
if (context.res.statusCode === 304) {
// There is no new value. Hence return the old value.
return oldValue!;
}
const ref = context.res.headers['etag'];
if (!ref) {
throw new Error('Server did not return the ref');
}
const content = await asText(context);
return { ref, content };
}
async write(key: string, data: string, ref: string | null): Promise<string> {
if (!this.enabled) {
return Promise.reject(new Error('No settings sync store url configured.'));
}
const url = joinPath(URI.parse(this.productService.settingsSyncStoreUrl!), key).toString();
const headers: IHeaders = { 'Content-Type': 'text/plain' };
if (ref) {
headers['If-Match'] = ref;
}
const context = await this.requestService.request({ type: 'POST', url, data, headers }, CancellationToken.None);
if (context.res.statusCode === 412) {
// There is a new value. Throw Rejected Error
throw new UserDataSyncStoreError('New data exists', UserDataSyncStoreErrorCode.Rejected);
}
const newRef = context.res.headers['etag'];
if (!newRef) {
throw new Error('Server did not return the ref');
}
return newRef;
}
}

View File

@@ -155,9 +155,6 @@ export interface IWindowsService {
openNewWindow(options?: INewWindowOptions): Promise<void>;
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void>;
getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>;
getWindowCount(): Promise<number>;
log(severity: string, args: string[]): Promise<void>;
showItemInFolder(path: URI): Promise<void>;
getActiveWindowId(): Promise<number | undefined>;
// This needs to be handled from browser process to prevent
@@ -167,7 +164,6 @@ export interface IWindowsService {
// TODO: this is a bit backwards
startCrashReporter(config: CrashReporterStartOptions): Promise<void>;
openAboutDialog(): Promise<void>;
resolveProxy(windowId: number, url: string): Promise<string | undefined>;
}

View File

@@ -104,17 +104,13 @@ export class WindowsChannel implements IServerChannel {
case 'openNewWindow': return this.service.openNewWindow(arg);
case 'openExtensionDevelopmentHostWindow': return this.service.openExtensionDevelopmentHostWindow(arg[0], arg[1]);
case 'getWindows': return this.service.getWindows();
case 'getWindowCount': return this.service.getWindowCount();
case 'relaunch': return this.service.relaunch(arg[0]);
case 'whenSharedProcessReady': return this.service.whenSharedProcessReady();
case 'toggleSharedProcess': return this.service.toggleSharedProcess();
case 'quit': return this.service.quit();
case 'log': return this.service.log(arg[0], arg[1]);
case 'showItemInFolder': return this.service.showItemInFolder(URI.revive(arg));
case 'getActiveWindowId': return this.service.getActiveWindowId();
case 'openExternal': return this.service.openExternal(arg);
case 'startCrashReporter': return this.service.startCrashReporter(arg);
case 'openAboutDialog': return this.service.openAboutDialog();
case 'resolveProxy': return this.service.resolveProxy(arg[0], arg[1]);
}

View File

@@ -222,18 +222,6 @@ export class WindowsService implements IWindowsService {
return result;
}
getWindowCount(): Promise<number> {
return this.channel.call('getWindowCount');
}
log(severity: string, args: string[]): Promise<void> {
return this.channel.call('log', [severity, args]);
}
showItemInFolder(path: URI): Promise<void> {
return this.channel.call('showItemInFolder', path);
}
getActiveWindowId(): Promise<number | undefined> {
return this.channel.call('getActiveWindowId');
}
@@ -250,10 +238,6 @@ export class WindowsService implements IWindowsService {
return this.channel.call('updateTouchBar', [windowId, items]);
}
openAboutDialog(): Promise<void> {
return this.channel.call('openAboutDialog');
}
resolveProxy(windowId: number, url: string): Promise<string | undefined> {
return Promise.resolve(this.channel.call('resolveProxy', [windowId, url]));
}

View File

@@ -3,25 +3,22 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as os from 'os';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { assign } from 'vs/base/common/objects';
import { URI } from 'vs/base/common/uri';
import product from 'vs/platform/product/node/product';
import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions, INewWindowOptions, IOpenSettings, IURIToOpen } from 'vs/platform/windows/common/windows';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { shell, crashReporter, app, Menu, clipboard } from 'electron';
import { shell, crashReporter, app, Menu } from 'electron';
import { Event } from 'vs/base/common/event';
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { IWindowsMainService, ISharedProcess, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { IHistoryMainService, IRecentlyOpened, IRecent } from 'vs/platform/history/common/history';
import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history';
import { IHistoryMainService } from 'vs/platform/history/electron-main/historyMainService';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { Schemas } from 'vs/base/common/network';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { isMacintosh, isLinux, IProcessEnvironment } from 'vs/base/common/platform';
import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
export class WindowsService extends Disposable implements IWindowsService, IURLHandler {
@@ -41,15 +38,15 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH
Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id))
);
readonly onRecentlyOpenedChange: Event<void> = this.historyService.onRecentlyOpenedChange;
readonly onRecentlyOpenedChange: Event<void> = this.historyMainService.onRecentlyOpenedChange;
constructor(
private sharedProcess: ISharedProcess,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IURLService urlService: IURLService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IHistoryMainService private readonly historyService: IHistoryMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IHistoryMainService private readonly historyMainService: IHistoryMainService,
@ILogService private readonly logService: ILogService
) {
super();
@@ -160,25 +157,25 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH
async addRecentlyOpened(recents: IRecent[]): Promise<void> {
this.logService.trace('windowsService#addRecentlyOpened');
this.historyService.addRecentlyOpened(recents);
this.historyMainService.addRecentlyOpened(recents);
}
async removeFromRecentlyOpened(paths: URI[]): Promise<void> {
this.logService.trace('windowsService#removeFromRecentlyOpened');
this.historyService.removeFromRecentlyOpened(paths);
this.historyMainService.removeFromRecentlyOpened(paths);
}
async clearRecentlyOpened(): Promise<void> {
this.logService.trace('windowsService#clearRecentlyOpened');
this.historyService.clearRecentlyOpened();
this.historyMainService.clearRecentlyOpened();
}
async getRecentlyOpened(windowId: number): Promise<IRecentlyOpened> {
this.logService.trace('windowsService#getRecentlyOpened', windowId);
return this.withWindow(windowId, codeWindow => this.historyService.getRecentlyOpened(codeWindow.config.workspace, codeWindow.config.folderUri, codeWindow.config.filesToOpenOrCreate), () => this.historyService.getRecentlyOpened())!;
return this.withWindow(windowId, codeWindow => this.historyMainService.getRecentlyOpened(codeWindow.config.workspace, codeWindow.config.folderUri, codeWindow.config.filesToOpenOrCreate), () => this.historyMainService.getRecentlyOpened())!;
}
async newWindowTab(): Promise<void> {
@@ -339,32 +336,6 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH
return this.windowsMainService.getWindows().length;
}
async log(severity: string, args: string[]): Promise<void> {
let consoleFn = console.log;
switch (severity) {
case 'error':
consoleFn = console.error;
break;
case 'warn':
consoleFn = console.warn;
break;
case 'info':
consoleFn = console.info;
break;
}
consoleFn.call(console, ...args);
}
async showItemInFolder(resource: URI): Promise<void> {
this.logService.trace('windowsService#showItemInFolder');
if (resource.scheme === Schemas.file) {
shell.showItemInFolder(resource.fsPath);
}
}
async getActiveWindowId(): Promise<number | undefined> {
return this._activeWindowId;
}
@@ -391,7 +362,7 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH
async relaunch(options: { addArgs?: string[], removeArgs?: string[] }): Promise<void> {
this.logService.trace('windowsService#relaunch');
this.lifecycleService.relaunch(options);
this.lifecycleMainService.relaunch(options);
}
async whenSharedProcessReady(): Promise<void> {
@@ -407,54 +378,6 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH
}
async openAboutDialog(): Promise<void> {
this.logService.trace('windowsService#openAboutDialog');
let version = app.getVersion();
if (product.target) {
version = `${version} (${product.target} setup)`;
}
const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION;
// {{SQL CARBON EDIT}}
const detail = nls.localize('aboutDetail',
"Version: {0}\nCommit: {1}\nDate: {2}\nVS Code {8}\nElectron: {3}\nChrome: {4}\nNode.js: {5}\nV8: {6}\nOS: {7}",
version,
product.commit || 'Unknown',
product.date || 'Unknown',
process.versions['electron'],
process.versions['chrome'],
process.versions['node'],
process.versions['v8'],
`${os.type()} ${os.arch()} ${os.release()}${isSnap ? ' snap' : ''}`,
product.vscodeVersion
);
const ok = nls.localize('okButton', "OK");
const copy = mnemonicButtonLabel(nls.localize({ key: 'copy', comment: ['&& denotes a mnemonic'] }, "&&Copy"));
let buttons: string[];
if (isLinux) {
buttons = [copy, ok];
} else {
buttons = [ok, copy];
}
const result = await this.windowsMainService.showMessageBox({
title: product.nameLong,
type: 'info',
message: product.nameLong,
detail: `\n${detail}`,
buttons,
noLink: true,
defaultId: buttons.indexOf(ok),
cancelId: buttons.indexOf(ok)
}, this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow());
if (buttons[result.button] === copy) {
clipboard.writeText(detail);
}
}
async handleURL(uri: URI): Promise<boolean> {
// Catch file URLs

View File

@@ -143,7 +143,7 @@ export interface IWorkspaceFolder extends IWorkspaceFolderData {
export class Workspace implements IWorkspace {
private _foldersMap: TernarySearchTree<WorkspaceFolder> = TernarySearchTree.forPaths<WorkspaceFolder>();
private _folders: WorkspaceFolder[];
private _folders!: WorkspaceFolder[];
constructor(
private _id: string,
@@ -265,4 +265,4 @@ export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[],
}
}
return result;
}
}

View File

@@ -5,7 +5,6 @@
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { localize } from 'vs/nls';
import { Event } from 'vs/base/common/event';
import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { URI, UriComponents } from 'vs/base/common/uri';
import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
@@ -19,9 +18,6 @@ import { toSlashes } from 'vs/base/common/extpath';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
export const IWorkspacesMainService = createDecorator<IWorkspacesMainService>('workspacesMainService');
export const IWorkspacesService = createDecorator<IWorkspacesService>('workspacesService');
export const WORKSPACE_EXTENSION = 'code-workspace';
export const WORKSPACE_FILTER = [{ name: localize('codeWorkspace', "Code Workspace"), extensions: [WORKSPACE_EXTENSION] }];
export const UNTITLED_WORKSPACE_NAME = 'workspace.json';
@@ -95,23 +91,10 @@ export interface IUntitledWorkspaceInfo {
remoteAuthority?: string;
}
export interface IWorkspacesMainService extends IWorkspacesService {
_serviceBrand: undefined;
onUntitledWorkspaceDeleted: Event<IWorkspaceIdentifier>;
createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier;
resolveLocalWorkspaceSync(path: URI): IResolvedWorkspace | null;
isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean;
deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void;
getUntitledWorkspacesSync(): IUntitledWorkspaceInfo[];
}
export const IWorkspacesService = createDecorator<IWorkspacesService>('workspacesService');
export interface IWorkspacesService {
_serviceBrand: undefined;
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
@@ -276,4 +259,4 @@ export function useSlashForPath(storedFolders: IStoredWorkspaceFolder[]): boolea
return false;
}
return true;
}
}

View File

@@ -1,32 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IWorkspacesService, IWorkspaceIdentifier, IWorkspaceFolderCreationData, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { URI } from 'vs/base/common/uri';
export class WorkspacesService implements IWorkspacesService {
_serviceBrand: undefined;
private channel: IChannel;
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
this.channel = mainProcessService.getChannel('workspaces');
}
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {
return this.channel.call('createUntitledWorkspace', [folders, remoteAuthority]).then(reviveWorkspaceIdentifier);
}
deleteUntitledWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise<void> {
return this.channel.call('deleteUntitledWorkspace', workspaceIdentifier);
}
getWorkspaceIdentifier(configPath: URI): Promise<IWorkspaceIdentifier> {
return this.channel.call('getWorkspaceIdentifier', configPath).then(reviveWorkspaceIdentifier);
}
}

View File

@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkspacesMainService, IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { join, dirname } from 'vs/base/common/path';
import { mkdirp, writeFile, rimrafSync, readdirSync, writeFileSync } from 'vs/base/node/pfs';
@@ -18,12 +18,38 @@ import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { Disposable } from 'vs/base/common/lifecycle';
import { originalFSPath, isEqualOrParent, joinPath } from 'vs/base/common/resources';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export interface IStoredWorkspace {
folders: IStoredWorkspaceFolder[];
remoteAuthority?: string;
}
export const IWorkspacesMainService = createDecorator<IWorkspacesMainService>('workspacesMainService');
export interface IWorkspacesMainService {
_serviceBrand: undefined;
onUntitledWorkspaceDeleted: Event<IWorkspaceIdentifier>;
createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier;
resolveLocalWorkspaceSync(path: URI): IResolvedWorkspace | null;
isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean;
deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void;
getUntitledWorkspacesSync(): IUntitledWorkspaceInfo[];
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void>;
getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier>;
}
export class WorkspacesMainService extends Disposable implements IWorkspacesMainService {
_serviceBrand: undefined;

View File

@@ -11,7 +11,7 @@ import * as pfs from 'vs/base/node/pfs';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { WorkspacesMainService, IStoredWorkspace } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { WORKSPACE_EXTENSION, IWorkspaceIdentifier, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder, rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/common/workspaces';
import { WORKSPACE_EXTENSION, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder, rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/common/workspaces';
import { NullLogService } from 'vs/platform/log/common/log';
import { URI } from 'vs/base/common/uri';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
@@ -29,16 +29,6 @@ suite('WorkspacesMainService', () => {
}
}
class TestWorkspacesMainService extends WorkspacesMainService {
public deleteWorkspaceCall: IWorkspaceIdentifier;
public deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void {
this.deleteWorkspaceCall = workspace;
super.deleteUntitledWorkspaceSync(workspace);
}
}
function createWorkspace(folders: string[], names?: string[]) {
return service.createUntitledWorkspace(folders.map((folder, index) => ({ uri: URI.file(folder), name: names ? names[index] : undefined } as IWorkspaceFolderCreationData)));
}
@@ -50,10 +40,10 @@ suite('WorkspacesMainService', () => {
const environmentService = new TestEnvironmentService(parseArgs(process.argv, OPTIONS), process.execPath);
const logService = new NullLogService();
let service: TestWorkspacesMainService;
let service: WorkspacesMainService;
setup(async () => {
service = new TestWorkspacesMainService(environmentService, logService);
service = new WorkspacesMainService(environmentService, logService);
// Delete any existing backups completely and then re-create it.
await pfs.rimraf(untitledWorkspacesHomePath, pfs.RimRafMode.MOVE);