mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-04 01:25:38 -05:00
Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f (#7282)
* Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f * fix various icon issues * fix preview features
This commit is contained in:
@@ -93,6 +93,7 @@ export const enum MenuId {
|
||||
SearchContext,
|
||||
StatusBarWindowIndicatorMenu,
|
||||
TouchBarContext,
|
||||
TitleBarContext,
|
||||
ViewItemContext,
|
||||
ViewTitle,
|
||||
ObjectExplorerItemContext, // {{SQL CARBON EDIT}}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
22
src/vs/platform/backup/node/backup.ts
Normal file
22
src/vs/platform/backup/node/backup.ts
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
19
src/vs/platform/credentials/common/credentials.ts
Normal file
19
src/vs/platform/credentials/common/credentials.ts
Normal 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 }>>;
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
17
src/vs/platform/electron/electron-browser/electronService.ts
Normal file
17
src/vs/platform/electron/electron-browser/electronService.ts
Normal 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'));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
23
src/vs/platform/electron/node/electron.ts
Normal file
23
src/vs/platform/electron/node/electron.ts
Normal 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>;
|
||||
}
|
||||
@@ -123,6 +123,7 @@ export interface IEnvironmentService {
|
||||
// user roaming data
|
||||
userRoamingDataHome: URI;
|
||||
settingsResource: URI;
|
||||
settingsSyncPreviewResource: URI;
|
||||
keybindingsResource: URI;
|
||||
keyboardLayoutResource: URI;
|
||||
localeResource: URI;
|
||||
|
||||
@@ -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')); }
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
48
src/vs/platform/ipc/node/simpleIpcProxy.ts
Normal file
48
src/vs/platform/ipc/node/simpleIpcProxy.ts
Normal 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;
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export class LifecycleService extends AbstractLifecycleService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private shutdownReason: ShutdownReason;
|
||||
private shutdownReason: ShutdownReason | undefined;
|
||||
|
||||
constructor(
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
|
||||
@@ -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
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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); },
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
128
src/vs/platform/product/common/productService.ts
Normal file
128
src/vs/platform/product/common/productService.ts
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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."));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
253
src/vs/platform/userDataSync/common/settingsSync.ts
Normal file
253
src/vs/platform/userDataSync/common/settingsSync.ts
Normal 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)));
|
||||
}
|
||||
|
||||
}
|
||||
37
src/vs/platform/userDataSync/common/settingsSyncIpc.ts
Normal file
37
src/vs/platform/userDataSync/common/settingsSyncIpc.ts
Normal 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]);
|
||||
}
|
||||
|
||||
}
|
||||
79
src/vs/platform/userDataSync/common/userDataSync.ts
Normal file
79
src/vs/platform/userDataSync/common/userDataSync.ts
Normal 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 }>;
|
||||
|
||||
}
|
||||
29
src/vs/platform/userDataSync/common/userDataSyncIpc.ts
Normal file
29
src/vs/platform/userDataSync/common/userDataSyncIpc.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
128
src/vs/platform/userDataSync/common/userDataSyncService.ts
Normal file
128
src/vs/platform/userDataSync/common/userDataSyncService.ts
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
|
||||
@@ -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]));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user