chore(vscode): update to 1.56.0

This commit is contained in:
Akash Satheesan
2021-04-30 20:25:17 +05:30
1749 changed files with 88014 additions and 43316 deletions

View File

@@ -3,6 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-action-bar .action-item.menu-entry .action-label.icon {
width: 16px;
height: 16px;
background-repeat: no-repeat;
background-position: 50%;
}
.monaco-action-bar .action-item.menu-entry .action-label {
background-image: var(--menu-entry-icon-light);
}

View File

@@ -117,7 +117,7 @@ export class MenuEntryActionViewItem extends ActionViewItem {
private readonly _altKey: ModifierKeyEmitter;
constructor(
readonly _action: MenuItemAction,
_action: MenuItemAction,
@IKeybindingService protected readonly _keybindingService: IKeybindingService,
@INotificationService protected _notificationService: INotificationService
) {
@@ -125,11 +125,15 @@ export class MenuEntryActionViewItem extends ActionViewItem {
this._altKey = ModifierKeyEmitter.getInstance();
}
protected get _commandAction(): MenuItemAction {
return this._wantsAltCommand && (<MenuItemAction>this._action).alt || this._action;
protected get _menuItemAction(): MenuItemAction {
return <MenuItemAction>this._action;
}
onClick(event: MouseEvent): void {
protected get _commandAction(): MenuItemAction {
return this._wantsAltCommand && this._menuItemAction.alt || this._menuItemAction;
}
override onClick(event: MouseEvent): void {
event.preventDefault();
event.stopPropagation();
@@ -138,11 +142,11 @@ export class MenuEntryActionViewItem extends ActionViewItem {
.catch(err => this._notificationService.error(err));
}
render(container: HTMLElement): void {
override render(container: HTMLElement): void {
super.render(container);
container.classList.add('menu-entry');
this._updateItemClass(this._action.item);
this._updateItemClass(this._menuItemAction.item);
let mouseOver = false;
@@ -158,7 +162,7 @@ export class MenuEntryActionViewItem extends ActionViewItem {
}
};
if (this._action.alt) {
if (this._menuItemAction.alt) {
this._register(this._altKey.event(value => {
alternativeKeyDown = value.altKey || ((isWindows || isLinux) && value.shiftKey);
updateAltState();
@@ -176,13 +180,13 @@ export class MenuEntryActionViewItem extends ActionViewItem {
}));
}
updateLabel(): void {
override updateLabel(): void {
if (this.options.label && this.label) {
this.label.textContent = this._commandAction.label;
}
}
updateTooltip(): void {
override updateTooltip(): void {
if (this.label) {
const keybinding = this._keybindingService.lookupKeybinding(this._commandAction.id);
const keybindingLabel = keybinding && keybinding.getLabel();
@@ -191,9 +195,9 @@ export class MenuEntryActionViewItem extends ActionViewItem {
let title = keybindingLabel
? localize('titleAndKb', "{0} ({1})", tooltip, keybindingLabel)
: tooltip;
if (!this._wantsAltCommand && this._action.alt) {
const altTooltip = this._action.alt.tooltip || this._action.alt.label;
const altKeybinding = this._keybindingService.lookupKeybinding(this._action.alt.id);
if (!this._wantsAltCommand && this._menuItemAction.alt) {
const altTooltip = this._menuItemAction.alt.tooltip || this._menuItemAction.alt.label;
const altKeybinding = this._keybindingService.lookupKeybinding(this._menuItemAction.alt.id);
const altKeybindingLabel = altKeybinding && altKeybinding.getLabel();
const altTitleSection = altKeybindingLabel
? localize('titleAndKb', "{0} ({1})", altTooltip, altKeybindingLabel)
@@ -204,14 +208,14 @@ export class MenuEntryActionViewItem extends ActionViewItem {
}
}
updateClass(): void {
override updateClass(): void {
if (this.options.icon) {
if (this._commandAction !== this._action) {
if (this._action.alt) {
this._updateItemClass(this._action.alt.item);
if (this._commandAction !== this._menuItemAction) {
if (this._menuItemAction.alt) {
this._updateItemClass(this._menuItemAction.alt.item);
}
} else if ((<MenuItemAction>this._action).alt) {
this._updateItemClass(this._action.item);
} else if (this._menuItemAction.alt) {
this._updateItemClass(this._menuItemAction.item);
}
}
}
@@ -268,7 +272,7 @@ export class SubmenuEntryActionViewItem extends DropdownMenuActionViewItem {
});
}
render(container: HTMLElement): void {
override render(container: HTMLElement): void {
super.render(container);
if (this.element) {
container.classList.add('menu-entry');

View File

@@ -45,7 +45,7 @@ export interface ICommandAction {
tooltip?: string;
icon?: Icon;
precondition?: ContextKeyExpression;
toggled?: ContextKeyExpression | { condition: ContextKeyExpression, icon?: Icon, tooltip?: string };
toggled?: ContextKeyExpression | { condition: ContextKeyExpression, icon?: Icon, tooltip?: string, title?: string | ILocalizedString };
}
export type ISerializableCommandAction = UriDto<ICommandAction>;
@@ -123,6 +123,7 @@ export class MenuId {
static readonly SCMTitle = new MenuId('SCMTitle');
static readonly SearchContext = new MenuId('SearchContext');
static readonly StatusBarWindowIndicatorMenu = new MenuId('StatusBarWindowIndicatorMenu');
static readonly StatusBarRemoteIndicatorMenu = new MenuId('StatusBarRemoteIndicatorMenu');
static readonly TestItem = new MenuId('TestItem');
static readonly TouchBarContext = new MenuId('TouchBarContext');
static readonly TitleBarContext = new MenuId('TitleBarContext');
@@ -156,7 +157,11 @@ export class MenuId {
static readonly TimelineTitleContext = new MenuId('TimelineTitleContext');
static readonly AccountsContext = new MenuId('AccountsContext');
static readonly PanelTitle = new MenuId('PanelTitle');
static readonly TerminalContext = new MenuId('TerminalContext');
static readonly TerminalContainerContext = new MenuId('TerminalContainerContext');
static readonly TerminalToolbarContext = new MenuId('TerminalToolbarContext');
static readonly TerminalTabsWidgetContext = new MenuId('TerminalTabsWidgetContext');
static readonly TerminalTabsWidgetEmptyContext = new MenuId('TerminalTabsWidgetEmptyContext');
static readonly TerminalSingleTabContext = new MenuId('TerminalSingleTabContext');
readonly id: number;
readonly _debugName: string;
@@ -321,7 +326,7 @@ export class ExecuteCommandAction extends Action {
super(id, label);
}
run(...args: any[]): Promise<any> {
override run(...args: any[]): Promise<any> {
return this._commandService.executeCommand(this.id, ...args);
}
}
@@ -337,7 +342,7 @@ export class SubmenuItemAction extends SubmenuAction {
super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, [], 'submenu');
}
get actions(): readonly IAction[] {
override get actions(): readonly IAction[] {
const result: IAction[] = [];
const menu = this._menuService.createMenu(this.item.submenu, this._contextKeyService);
const groups = menu.getActions(this._options);
@@ -386,12 +391,16 @@ export class MenuItemAction implements IAction {
if (item.toggled) {
const toggled = ((item.toggled as { condition: ContextKeyExpression }).condition ? item.toggled : { condition: item.toggled }) as {
condition: ContextKeyExpression, icon?: Icon, tooltip?: string | ILocalizedString
condition: ContextKeyExpression, icon?: Icon, tooltip?: string | ILocalizedString, title?: string | ILocalizedString
};
this.checked = contextKeyService.contextMatchesRules(toggled.condition);
if (this.checked && toggled.tooltip) {
this.tooltip = typeof toggled.tooltip === 'string' ? toggled.tooltip : toggled.tooltip.value;
}
if (toggled.title) {
this.label = typeof toggled.title === 'string' ? toggled.title : toggled.title.value;
}
}
this.item = item;

View File

@@ -13,7 +13,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe
// --- service instances
const contextKeyService = new class extends MockContextKeyService {
contextMatchesRules() {
override contextMatchesRules() {
return true;
}
};
@@ -65,14 +65,14 @@ suite('MenuService', function () {
const groups = menuService.createMenu(testMenuId, contextKeyService).getActions();
assert.equal(groups.length, 5);
assert.strictEqual(groups.length, 5);
const [one, two, three, four, five] = groups;
assert.equal(one[0], 'navigation');
assert.equal(two[0], '0_hello');
assert.equal(three[0], 'hello');
assert.equal(four[0], 'Hello');
assert.equal(five[0], '');
assert.strictEqual(one[0], 'navigation');
assert.strictEqual(two[0], '0_hello');
assert.strictEqual(three[0], 'hello');
assert.strictEqual(four[0], 'Hello');
assert.strictEqual(five[0], '');
});
test('in group sorting, by title', function () {
@@ -94,14 +94,14 @@ suite('MenuService', function () {
const groups = menuService.createMenu(testMenuId, contextKeyService).getActions();
assert.equal(groups.length, 1);
assert.strictEqual(groups.length, 1);
const [, actions] = groups[0];
assert.equal(actions.length, 3);
assert.strictEqual(actions.length, 3);
const [one, two, three] = actions;
assert.equal(one.id, 'a');
assert.equal(two.id, 'b');
assert.equal(three.id, 'c');
assert.strictEqual(one.id, 'a');
assert.strictEqual(two.id, 'b');
assert.strictEqual(three.id, 'c');
});
test('in group sorting, by title and order', function () {
@@ -131,15 +131,15 @@ suite('MenuService', function () {
const groups = menuService.createMenu(testMenuId, contextKeyService).getActions();
assert.equal(groups.length, 1);
assert.strictEqual(groups.length, 1);
const [, actions] = groups[0];
assert.equal(actions.length, 4);
assert.strictEqual(actions.length, 4);
const [one, two, three, four] = actions;
assert.equal(one.id, 'd');
assert.equal(two.id, 'c');
assert.equal(three.id, 'b');
assert.equal(four.id, 'a');
assert.strictEqual(one.id, 'd');
assert.strictEqual(two.id, 'c');
assert.strictEqual(three.id, 'b');
assert.strictEqual(four.id, 'a');
});
@@ -165,14 +165,14 @@ suite('MenuService', function () {
const groups = menuService.createMenu(testMenuId, contextKeyService).getActions();
assert.equal(groups.length, 1);
assert.strictEqual(groups.length, 1);
const [[, actions]] = groups;
assert.equal(actions.length, 3);
assert.strictEqual(actions.length, 3);
const [one, two, three] = actions;
assert.equal(one.id, 'c');
assert.equal(two.id, 'b');
assert.equal(three.id, 'a');
assert.strictEqual(one.id, 'c');
assert.strictEqual(two.id, 'b');
assert.strictEqual(three.id, 'a');
});
test('special MenuId palette', function () {
@@ -188,16 +188,16 @@ suite('MenuService', function () {
for (const item of MenuRegistry.getMenuItems(MenuId.CommandPalette)) {
if (isIMenuItem(item)) {
if (item.command.id === 'a') {
assert.equal(item.command.title, 'Explicit');
assert.strictEqual(item.command.title, 'Explicit');
foundA = true;
}
if (item.command.id === 'b') {
assert.equal(item.command.title, 'Implicit');
assert.strictEqual(item.command.title, 'Implicit');
foundB = true;
}
}
}
assert.equal(foundA, true);
assert.equal(foundB, true);
assert.strictEqual(foundA, true);
assert.strictEqual(foundB, true);
});
});

View File

@@ -123,7 +123,7 @@ flakySuite('BackupMainService', () => {
return path.join(this.backupHome, id);
}
getFolderHash(folderUri: URI): string {
override getFolderHash(folderUri: URI): string {
return super.getFolderHash(folderUri);
}
};

View File

@@ -15,17 +15,19 @@ import { NullLogService } from 'vs/platform/log/common/log';
suite('Checksum Service', () => {
let diskFileSystemProvider: DiskFileSystemProvider;
let fileService: IFileService;
setup(() => {
const logService = new NullLogService();
fileService = new FileService(logService);
const diskFileSystemProvider = new DiskFileSystemProvider(logService);
diskFileSystemProvider = new DiskFileSystemProvider(logService);
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
});
teardown(() => {
diskFileSystemProvider.dispose();
fileService.dispose();
});

View File

@@ -148,3 +148,5 @@ export const NullCommandService: ICommandService = {
return Promise.resolve(undefined);
}
};
CommandsRegistry.registerCommand('noop', () => { });

View File

@@ -71,7 +71,7 @@ suite('Command Tests', function () {
CommandsRegistry.getCommands().get('test')!.handler.apply(undefined, [undefined!, 'string']);
CommandsRegistry.getCommands().get('test2')!.handler.apply(undefined, [undefined!, 'string']);
assert.throws(() => CommandsRegistry.getCommands().get('test3')!.handler.apply(undefined, [undefined!, 'string']));
assert.equal(CommandsRegistry.getCommands().get('test3')!.handler.apply(undefined, [undefined!, 1]), true);
assert.strictEqual(CommandsRegistry.getCommands().get('test3')!.handler.apply(undefined, [undefined!, 1]), true);
});
});

View File

@@ -214,42 +214,53 @@ export class DefaultConfigurationModel extends ConfigurationModel {
}
}
export interface ConfigurationParseOptions {
scopes: ConfigurationScope[] | undefined;
skipRestricted?: boolean;
}
export class ConfigurationModelParser {
private _raw: any = null;
private _configurationModel: ConfigurationModel | null = null;
private _restrictedConfigurations: string[] = [];
private _parseErrors: any[] = [];
constructor(protected readonly _name: string, private _scopes?: ConfigurationScope[]) { }
constructor(protected readonly _name: string) { }
get configurationModel(): ConfigurationModel {
return this._configurationModel || new ConfigurationModel();
}
get restrictedConfigurations(): string[] {
return this._restrictedConfigurations;
}
get errors(): any[] {
return this._parseErrors;
}
public parseContent(content: string | null | undefined): void {
public parse(content: string | null | undefined, options?: ConfigurationParseOptions): void {
if (!types.isUndefinedOrNull(content)) {
const raw = this.doParseContent(content);
this.parseRaw(raw);
this.parseRaw(raw, options);
}
}
public parseRaw(raw: any): void {
this._raw = raw;
const configurationModel = this.doParseRaw(raw);
this._configurationModel = new ConfigurationModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides);
}
public parse(): void {
public reparse(options: ConfigurationParseOptions): void {
if (this._raw) {
this.parseRaw(this._raw);
this.parseRaw(this._raw, options);
}
}
protected doParseContent(content: string): any {
public parseRaw(raw: any, options?: ConfigurationParseOptions): void {
this._raw = raw;
const { contents, keys, overrides, restricted } = this.doParseRaw(raw, options);
this._configurationModel = new ConfigurationModel(contents, keys, overrides);
this._restrictedConfigurations = restricted || [];
}
private doParseContent(content: string): any {
let raw: any = {};
let currentProperty: string | null = null;
let currentParent: any = [];
@@ -306,42 +317,50 @@ export class ConfigurationModelParser {
return raw;
}
protected doParseRaw(raw: any): IConfigurationModel {
if (this._scopes) {
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
raw = this.filterByScope(raw, configurationProperties, true, this._scopes);
}
protected doParseRaw(raw: any, options?: ConfigurationParseOptions): IConfigurationModel & { restricted?: string[] } {
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
const filtered = this.filter(raw, configurationProperties, true, options);
raw = filtered.raw;
const contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
const keys = Object.keys(raw);
const overrides: IOverrides[] = toOverrides(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
return { contents, keys, overrides };
return { contents, keys, overrides, restricted: filtered.restricted };
}
private filterByScope(properties: any, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }, filterOverriddenProperties: boolean, scopes: ConfigurationScope[]): {} {
const result: any = {};
private filter(properties: any, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema | undefined }, filterOverriddenProperties: boolean, options?: ConfigurationParseOptions): { raw: {}, restricted: string[] } {
if (!options?.scopes && !options?.skipRestricted) {
return { raw: properties, restricted: [] };
}
const raw: any = {};
const restricted: string[] = [];
for (let key in properties) {
if (OVERRIDE_PROPERTY_PATTERN.test(key) && filterOverriddenProperties) {
result[key] = this.filterByScope(properties[key], configurationProperties, false, scopes);
const result = this.filter(properties[key], configurationProperties, false, options);
raw[key] = result.raw;
restricted.push(...result.restricted);
} else {
const scope = this.getScope(key, configurationProperties);
const propertySchema = configurationProperties[key];
const scope = propertySchema ? typeof propertySchema.scope !== 'undefined' ? propertySchema.scope : ConfigurationScope.WINDOW : undefined;
if (propertySchema?.restricted) {
restricted.push(key);
}
// Load unregistered configurations always.
if (scope === undefined || scopes.indexOf(scope) !== -1) {
result[key] = properties[key];
if (scope === undefined || options.scopes === undefined || options.scopes.includes(scope)) {
if (!(options.skipRestricted && propertySchema?.restricted)) {
raw[key] = properties[key];
}
}
}
}
return result;
return { raw, restricted };
}
private getScope(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): ConfigurationScope | undefined {
const propertySchema = configurationProperties[key];
return propertySchema ? typeof propertySchema.scope !== 'undefined' ? propertySchema.scope : ConfigurationScope.WINDOW : undefined;
}
}
export class UserSettings extends Disposable {
private readonly parser: ConfigurationModelParser;
private readonly parseOptions: ConfigurationParseOptions;
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = this._onDidChange.event;
@@ -352,7 +371,8 @@ export class UserSettings extends Disposable {
private readonly fileService: IFileService
) {
super();
this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), this.scopes);
this.parser = new ConfigurationModelParser(this.userSettingsResource.toString());
this.parseOptions = { scopes: this.scopes };
this._register(this.fileService.watch(extUri.dirname(this.userSettingsResource)));
// Also listen to the resource incase the resource is a symlink - https://github.com/microsoft/vscode/issues/118134
this._register(this.fileService.watch(this.userSettingsResource));
@@ -362,17 +382,21 @@ export class UserSettings extends Disposable {
async loadConfiguration(): Promise<ConfigurationModel> {
try {
const content = await this.fileService.readFile(this.userSettingsResource);
this.parser.parseContent(content.value.toString() || '{}');
this.parser.parse(content.value.toString() || '{}', this.parseOptions);
return this.parser.configurationModel;
} catch (e) {
return new ConfigurationModel();
}
}
reprocess(): ConfigurationModel {
this.parser.parse();
reparse(): ConfigurationModel {
this.parser.reparse(this.parseOptions);
return this.parser.configurationModel;
}
getRestrictedSettings(): string[] {
return this.parser.restrictedConfigurations;
}
}
@@ -799,8 +823,9 @@ export class ConfigurationChangeEvent implements IConfigurationChangeEvent {
}
export class AllKeysConfigurationChangeEvent extends ConfigurationChangeEvent {
constructor(configuration: Configuration, workspace: Workspace, public source: ConfigurationTarget, public sourceConfig: any) {
constructor(configuration: Configuration, workspace: Workspace, source: ConfigurationTarget, sourceConfig: any) {
super({ keys: configuration.allKeys(), overrides: [] }, undefined, configuration, workspace);
this.source = source;
this.sourceConfig = sourceConfig;
}
}

View File

@@ -109,22 +109,35 @@ export const enum ConfigurationScope {
}
export interface IConfigurationPropertySchema extends IJSONSchema {
scope?: ConfigurationScope;
/**
* When restricted, value of this configuration will be read only from trusted sources.
* For eg., If the workspace is not trusted, then the value of this configuration is not read from workspace settings file.
*/
restricted?: boolean;
included?: boolean;
tags?: string[];
/**
* When enabled this setting is ignored during sync and user can override this.
*/
ignoreSync?: boolean;
/**
* When enabled this setting is ignored during sync and user cannot override this.
*/
disallowSyncIgnore?: boolean;
enumItemLabels?: string[];
}
export interface IConfigurationExtensionInfo {
id: string;
restrictedConfigurations?: string[];
}
export interface IConfigurationNode {
@@ -139,14 +152,12 @@ export interface IConfigurationNode {
extensionInfo?: IConfigurationExtensionInfo;
}
type SettingProperties = { [key: string]: any };
export const allSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
export const applicationSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
export const machineSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
export const machineOverridableSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
export const windowSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
export const resourceSettings: { properties: SettingProperties, patternProperties: SettingProperties } = { properties: {}, patternProperties: {} };
export const allSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const applicationSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const machineSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const machineOverridableSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const windowSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const resourceSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const resourceLanguageSettingsSchemaId = 'vscode://schemas/settings/resourceLanguage';
@@ -190,7 +201,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
public registerConfigurations(configurations: IConfigurationNode[], validate: boolean = true): void {
const properties: string[] = [];
configurations.forEach(configuration => {
properties.push(...this.validateAndRegisterProperties(configuration, validate)); // fills in defaults
properties.push(...this.validateAndRegisterProperties(configuration, validate, configuration.extensionInfo)); // fills in defaults
this.configurationContributors.push(configuration);
this.registerJSONConfiguration(configuration);
});
@@ -297,7 +308,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.updateOverridePropertyPatternKey();
}
private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, scope: ConfigurationScope = ConfigurationScope.WINDOW): string[] {
private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, extensionInfo?: IConfigurationExtensionInfo, scope: ConfigurationScope = ConfigurationScope.WINDOW): string[] {
scope = types.isUndefinedOrNull(configuration.scope) ? scope : configuration.scope;
let propertyKeys: string[] = [];
let properties = configuration.properties;
@@ -318,6 +329,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
property.scope = undefined; // No scope for overridable properties `[${identifier}]`
} else {
property.scope = types.isUndefinedOrNull(property.scope) ? scope : property.scope;
property.restricted = types.isUndefinedOrNull(property.restricted) ? !!extensionInfo?.restrictedConfigurations?.includes(key) : property.restricted;
}
// Add to properties maps
@@ -341,7 +353,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
let subNodes = configuration.allOf;
if (subNodes) {
for (let node of subNodes) {
propertyKeys.push(...this.validateAndRegisterProperties(node, validate, scope));
propertyKeys.push(...this.validateAndRegisterProperties(node, validate, extensionInfo, scope));
}
}
return propertyKeys;

View File

@@ -11,10 +11,10 @@ suite('Configuration', () => {
test('simple merge', () => {
let base = { 'a': 1, 'b': 2 };
merge(base, { 'a': 3, 'c': 4 }, true);
assert.deepEqual(base, { 'a': 3, 'b': 2, 'c': 4 });
assert.deepStrictEqual(base, { 'a': 3, 'b': 2, 'c': 4 });
base = { 'a': 1, 'b': 2 };
merge(base, { 'a': 3, 'c': 4 }, false);
assert.deepEqual(base, { 'a': 1, 'b': 2, 'c': 4 });
assert.deepStrictEqual(base, { 'a': 1, 'b': 2, 'c': 4 });
});
test('removeFromValueTree: remove a non existing key', () => {
@@ -22,7 +22,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'c');
assert.deepEqual(target, { 'a': { 'b': 2 } });
assert.deepStrictEqual(target, { 'a': { 'b': 2 } });
});
test('removeFromValueTree: remove a multi segmented key from an object that has only sub sections of the key', () => {
@@ -30,7 +30,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'a.b.c');
assert.deepEqual(target, { 'a': { 'b': 2 } });
assert.deepStrictEqual(target, { 'a': { 'b': 2 } });
});
test('removeFromValueTree: remove a single segmented key', () => {
@@ -38,7 +38,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'a');
assert.deepEqual(target, {});
assert.deepStrictEqual(target, {});
});
test('removeFromValueTree: remove a single segmented key when its value is undefined', () => {
@@ -46,7 +46,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'a');
assert.deepEqual(target, {});
assert.deepStrictEqual(target, {});
});
test('removeFromValueTree: remove a multi segmented key when its value is undefined', () => {
@@ -54,7 +54,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'a.b');
assert.deepEqual(target, {});
assert.deepStrictEqual(target, {});
});
test('removeFromValueTree: remove a multi segmented key when its value is array', () => {
@@ -62,7 +62,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'a.b');
assert.deepEqual(target, {});
assert.deepStrictEqual(target, {});
});
test('removeFromValueTree: remove a multi segmented key first segment value is array', () => {
@@ -70,7 +70,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'a.0');
assert.deepEqual(target, { 'a': [1] });
assert.deepStrictEqual(target, { 'a': [1] });
});
test('removeFromValueTree: remove when key is the first segmenet', () => {
@@ -78,7 +78,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'a');
assert.deepEqual(target, {});
assert.deepStrictEqual(target, {});
});
test('removeFromValueTree: remove a multi segmented key when the first node has more values', () => {
@@ -86,7 +86,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'a.b.c');
assert.deepEqual(target, { 'a': { 'd': 1 } });
assert.deepStrictEqual(target, { 'a': { 'd': 1 } });
});
test('removeFromValueTree: remove a multi segmented key when in between node has more values', () => {
@@ -94,7 +94,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'a.b.c.d');
assert.deepEqual(target, { 'a': { 'b': { 'd': 1 } } });
assert.deepStrictEqual(target, { 'a': { 'b': { 'd': 1 } } });
});
test('removeFromValueTree: remove a multi segmented key when the last but one node has more values', () => {
@@ -102,7 +102,7 @@ suite('Configuration', () => {
removeFromValueTree(target, 'a.b.c');
assert.deepEqual(target, { 'a': { 'b': { 'd': 1 } } });
assert.deepStrictEqual(target, { 'a': { 'b': { 'd': 1 } } });
});
});
@@ -111,37 +111,37 @@ suite('Configuration Changes: Merge', () => {
test('merge only keys', () => {
const actual = mergeChanges({ keys: ['a', 'b'], overrides: [] }, { keys: ['c', 'd'], overrides: [] });
assert.deepEqual(actual, { keys: ['a', 'b', 'c', 'd'], overrides: [] });
assert.deepStrictEqual(actual, { keys: ['a', 'b', 'c', 'd'], overrides: [] });
});
test('merge only keys with duplicates', () => {
const actual = mergeChanges({ keys: ['a', 'b'], overrides: [] }, { keys: ['c', 'd'], overrides: [] }, { keys: ['a', 'd', 'e'], overrides: [] });
assert.deepEqual(actual, { keys: ['a', 'b', 'c', 'd', 'e'], overrides: [] });
assert.deepStrictEqual(actual, { keys: ['a', 'b', 'c', 'd', 'e'], overrides: [] });
});
test('merge only overrides', () => {
const actual = mergeChanges({ keys: [], overrides: [['a', ['1', '2']]] }, { keys: [], overrides: [['b', ['3', '4']]] });
assert.deepEqual(actual, { keys: [], overrides: [['a', ['1', '2']], ['b', ['3', '4']]] });
assert.deepStrictEqual(actual, { keys: [], overrides: [['a', ['1', '2']], ['b', ['3', '4']]] });
});
test('merge only overrides with duplicates', () => {
const actual = mergeChanges({ keys: [], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] }, { keys: [], overrides: [['b', ['3', '4']]] }, { keys: [], overrides: [['c', ['1', '4']], ['a', ['2', '3']]] });
assert.deepEqual(actual, { keys: [], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] });
assert.deepStrictEqual(actual, { keys: [], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] });
});
test('merge', () => {
const actual = mergeChanges({ keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] }, { keys: ['b'], overrides: [['b', ['3', '4']]] }, { keys: ['c', 'a'], overrides: [['c', ['1', '4']], ['a', ['2', '3']]] });
assert.deepEqual(actual, { keys: ['b', 'c', 'a'], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] });
assert.deepStrictEqual(actual, { keys: ['b', 'c', 'a'], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] });
});
test('merge single change', () => {
const actual = mergeChanges({ keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] });
assert.deepEqual(actual, { keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] });
assert.deepStrictEqual(actual, { keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] });
});
test('merge no changes', () => {
const actual = mergeChanges();
assert.deepEqual(actual, { keys: [], overrides: [] });
assert.deepStrictEqual(actual, { keys: [], overrides: [] });
});
});

View File

@@ -19,8 +19,8 @@ suite('ConfigurationModel', () => {
testObject.setValue('f', 1);
assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'f': 1 });
assert.deepEqual(testObject.keys, ['a.b', 'f']);
assert.deepStrictEqual(testObject.contents, { 'a': { 'b': 1 }, 'f': 1 });
assert.deepStrictEqual(testObject.keys, ['a.b', 'f']);
});
test('setValue for a key that has no sections and defined', () => {
@@ -28,8 +28,8 @@ suite('ConfigurationModel', () => {
testObject.setValue('f', 3);
assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'f': 3 });
assert.deepEqual(testObject.keys, ['a.b', 'f']);
assert.deepStrictEqual(testObject.contents, { 'a': { 'b': 1 }, 'f': 3 });
assert.deepStrictEqual(testObject.keys, ['a.b', 'f']);
});
test('setValue for a key that has sections and not defined', () => {
@@ -37,8 +37,13 @@ suite('ConfigurationModel', () => {
testObject.setValue('b.c', 1);
assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'b': { 'c': 1 }, 'f': 1 });
assert.deepEqual(testObject.keys, ['a.b', 'f', 'b.c']);
const expected: any = {};
expected['a'] = { 'b': 1 };
expected['f'] = 1;
expected['b'] = Object.create(null);
expected['b']['c'] = 1;
assert.deepStrictEqual(testObject.contents, expected);
assert.deepStrictEqual(testObject.keys, ['a.b', 'f', 'b.c']);
});
test('setValue for a key that has sections and defined', () => {
@@ -46,8 +51,8 @@ suite('ConfigurationModel', () => {
testObject.setValue('b.c', 3);
assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'b': { 'c': 3 }, 'f': 1 });
assert.deepEqual(testObject.keys, ['a.b', 'b.c', 'f']);
assert.deepStrictEqual(testObject.contents, { 'a': { 'b': 1 }, 'b': { 'c': 3 }, 'f': 1 });
assert.deepStrictEqual(testObject.keys, ['a.b', 'b.c', 'f']);
});
test('setValue for a key that has sections and sub section not defined', () => {
@@ -55,8 +60,8 @@ suite('ConfigurationModel', () => {
testObject.setValue('a.c', 1);
assert.deepEqual(testObject.contents, { 'a': { 'b': 1, 'c': 1 }, 'f': 1 });
assert.deepEqual(testObject.keys, ['a.b', 'f', 'a.c']);
assert.deepStrictEqual(testObject.contents, { 'a': { 'b': 1, 'c': 1 }, 'f': 1 });
assert.deepStrictEqual(testObject.keys, ['a.b', 'f', 'a.c']);
});
test('setValue for a key that has sections and sub section defined', () => {
@@ -64,8 +69,8 @@ suite('ConfigurationModel', () => {
testObject.setValue('a.c', 3);
assert.deepEqual(testObject.contents, { 'a': { 'b': 1, 'c': 3 }, 'f': 1 });
assert.deepEqual(testObject.keys, ['a.b', 'a.c', 'f']);
assert.deepStrictEqual(testObject.contents, { 'a': { 'b': 1, 'c': 3 }, 'f': 1 });
assert.deepStrictEqual(testObject.keys, ['a.b', 'a.c', 'f']);
});
test('setValue for a key that has sections and last section is added', () => {
@@ -73,8 +78,8 @@ suite('ConfigurationModel', () => {
testObject.setValue('a.b.c', 1);
assert.deepEqual(testObject.contents, { 'a': { 'b': { 'c': 1 } }, 'f': 1 });
assert.deepEqual(testObject.keys, ['a.b.c', 'f']);
assert.deepStrictEqual(testObject.contents, { 'a': { 'b': { 'c': 1 } }, 'f': 1 });
assert.deepStrictEqual(testObject.keys, ['a.b.c', 'f']);
});
test('removeValue: remove a non existing key', () => {
@@ -82,8 +87,8 @@ suite('ConfigurationModel', () => {
testObject.removeValue('a.b.c');
assert.deepEqual(testObject.contents, { 'a': { 'b': 2 } });
assert.deepEqual(testObject.keys, ['a.b']);
assert.deepStrictEqual(testObject.contents, { 'a': { 'b': 2 } });
assert.deepStrictEqual(testObject.keys, ['a.b']);
});
test('removeValue: remove a single segmented key', () => {
@@ -91,8 +96,8 @@ suite('ConfigurationModel', () => {
testObject.removeValue('a');
assert.deepEqual(testObject.contents, {});
assert.deepEqual(testObject.keys, []);
assert.deepStrictEqual(testObject.contents, {});
assert.deepStrictEqual(testObject.keys, []);
});
test('removeValue: remove a multi segmented key', () => {
@@ -100,8 +105,8 @@ suite('ConfigurationModel', () => {
testObject.removeValue('a.b');
assert.deepEqual(testObject.contents, {});
assert.deepEqual(testObject.keys, []);
assert.deepStrictEqual(testObject.contents, {});
assert.deepStrictEqual(testObject.keys, []);
});
test('get overriding configuration model for an existing identifier', () => {
@@ -109,7 +114,7 @@ suite('ConfigurationModel', () => {
{ 'a': { 'b': 1 }, 'f': 1 }, [],
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 } }, keys: ['a'] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1 });
assert.deepStrictEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1 });
});
test('get overriding configuration model for an identifier that does not exist', () => {
@@ -117,7 +122,7 @@ suite('ConfigurationModel', () => {
{ 'a': { 'b': 1 }, 'f': 1 }, [],
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 } }, keys: ['a'] }]);
assert.deepEqual(testObject.override('xyz').contents, { 'a': { 'b': 1 }, 'f': 1 });
assert.deepStrictEqual(testObject.override('xyz').contents, { 'a': { 'b': 1 }, 'f': 1 });
});
test('get overriding configuration when one of the keys does not exist in base', () => {
@@ -125,7 +130,7 @@ suite('ConfigurationModel', () => {
{ 'a': { 'b': 1 }, 'f': 1 }, [],
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'g': 1 }, keys: ['a', 'g'] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1, 'g': 1 });
assert.deepStrictEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1, 'g': 1 });
});
test('get overriding configuration when one of the key in base is not of object type', () => {
@@ -133,7 +138,7 @@ suite('ConfigurationModel', () => {
{ 'a': { 'b': 1 }, 'f': 1 }, [],
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'f': { 'g': 1 } }, keys: ['a', 'f'] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': { 'g': 1 } });
assert.deepStrictEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': { 'g': 1 } });
});
test('get overriding configuration when one of the key in overriding contents is not of object type', () => {
@@ -141,7 +146,7 @@ suite('ConfigurationModel', () => {
{ 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [],
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'f': 1 }, keys: ['a', 'f'] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1 });
assert.deepStrictEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1 });
});
test('get overriding configuration if the value of overriding identifier is not object', () => {
@@ -149,7 +154,7 @@ suite('ConfigurationModel', () => {
{ 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [],
[{ identifiers: ['c'], contents: 'abc', keys: [] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1 }, 'f': { 'g': 1 } });
assert.deepStrictEqual(testObject.override('c').contents, { 'a': { 'b': 1 }, 'f': { 'g': 1 } });
});
test('get overriding configuration if the value of overriding identifier is an empty object', () => {
@@ -157,7 +162,7 @@ suite('ConfigurationModel', () => {
{ 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [],
[{ identifiers: ['c'], contents: {}, keys: [] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1 }, 'f': { 'g': 1 } });
assert.deepStrictEqual(testObject.override('c').contents, { 'a': { 'b': 1 }, 'f': { 'g': 1 } });
});
test('simple merge', () => {
@@ -165,8 +170,8 @@ suite('ConfigurationModel', () => {
let add = new ConfigurationModel({ 'a': 3, 'c': 4 }, ['a', 'c']);
let result = base.merge(add);
assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
assert.deepEqual(result.keys, ['a', 'b', 'c']);
assert.deepStrictEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
assert.deepStrictEqual(result.keys, ['a', 'b', 'c']);
});
test('recursive merge', () => {
@@ -174,9 +179,9 @@ suite('ConfigurationModel', () => {
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b']);
let result = base.merge(add);
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
assert.deepEqual(result.getValue('a'), { 'b': 2 });
assert.deepEqual(result.keys, ['a.b']);
assert.deepStrictEqual(result.contents, { 'a': { 'b': 2 } });
assert.deepStrictEqual(result.getValue('a'), { 'b': 2 });
assert.deepStrictEqual(result.keys, ['a.b']);
});
test('simple merge overrides', () => {
@@ -184,10 +189,10 @@ suite('ConfigurationModel', () => {
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'b': 2 }, keys: ['b'] }]);
let result = base.merge(add);
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 }, keys: ['a'] }]);
assert.deepEqual(result.override('c').contents, { 'a': 2, 'b': 2 });
assert.deepEqual(result.keys, ['a.b']);
assert.deepStrictEqual(result.contents, { 'a': { 'b': 2 } });
assert.deepStrictEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 }, keys: ['a'] }]);
assert.deepStrictEqual(result.override('c').contents, { 'a': 2, 'b': 2 });
assert.deepStrictEqual(result.keys, ['a.b']);
});
test('recursive merge overrides', () => {
@@ -195,10 +200,10 @@ suite('ConfigurationModel', () => {
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } }, keys: ['a'] }]);
let result = base.merge(add);
assert.deepEqual(result.contents, { 'a': { 'b': 2 }, 'f': 1 });
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } }, keys: ['a'] }]);
assert.deepEqual(result.override('c').contents, { 'a': { 'b': 2, 'd': 1, 'e': 2 }, 'f': 1 });
assert.deepEqual(result.keys, ['a.b', 'f']);
assert.deepStrictEqual(result.contents, { 'a': { 'b': 2 }, 'f': 1 });
assert.deepStrictEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } }, keys: ['a'] }]);
assert.deepStrictEqual(result.override('c').contents, { 'a': { 'b': 2, 'd': 1, 'e': 2 }, 'f': 1 });
assert.deepStrictEqual(result.keys, ['a.b', 'f']);
});
test('merge overrides when frozen', () => {
@@ -206,30 +211,30 @@ suite('ConfigurationModel', () => {
let model2 = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } }, keys: ['a'] }]).freeze();
let result = new ConfigurationModel().merge(model1, model2);
assert.deepEqual(result.contents, { 'a': { 'b': 2 }, 'f': 1 });
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } }, keys: ['a'] }]);
assert.deepEqual(result.override('c').contents, { 'a': { 'b': 2, 'd': 1, 'e': 2 }, 'f': 1 });
assert.deepEqual(result.keys, ['a.b', 'f']);
assert.deepStrictEqual(result.contents, { 'a': { 'b': 2 }, 'f': 1 });
assert.deepStrictEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } }, keys: ['a'] }]);
assert.deepStrictEqual(result.override('c').contents, { 'a': { 'b': 2, 'd': 1, 'e': 2 }, 'f': 1 });
assert.deepStrictEqual(result.keys, ['a.b', 'f']);
});
test('Test contents while getting an existing property', () => {
let testObject = new ConfigurationModel({ 'a': 1 });
assert.deepEqual(testObject.getValue('a'), 1);
assert.deepStrictEqual(testObject.getValue('a'), 1);
testObject = new ConfigurationModel({ 'a': { 'b': 1 } });
assert.deepEqual(testObject.getValue('a'), { 'b': 1 });
assert.deepStrictEqual(testObject.getValue('a'), { 'b': 1 });
});
test('Test contents are undefined for non existing properties', () => {
const testObject = new ConfigurationModel({ awesome: true });
assert.deepEqual(testObject.getValue('unknownproperty'), undefined);
assert.deepStrictEqual(testObject.getValue('unknownproperty'), undefined);
});
test('Test override gives all content merged with overrides', () => {
const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, [], [{ identifiers: ['b'], contents: { 'a': 2 }, keys: ['a'] }]);
assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1 });
assert.deepStrictEqual(testObject.override('b').contents, { 'a': 2, 'c': 1 });
});
});
@@ -237,96 +242,96 @@ suite('CustomConfigurationModel', () => {
test('simple merge using models', () => {
let base = new ConfigurationModelParser('base');
base.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
base.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
let add = new ConfigurationModelParser('add');
add.parseContent(JSON.stringify({ 'a': 3, 'c': 4 }));
add.parse(JSON.stringify({ 'a': 3, 'c': 4 }));
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
assert.deepStrictEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
});
test('simple merge with an undefined contents', () => {
let base = new ConfigurationModelParser('base');
base.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
base.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
let add = new ConfigurationModelParser('add');
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
assert.deepStrictEqual(result.contents, { 'a': 1, 'b': 2 });
base = new ConfigurationModelParser('base');
add = new ConfigurationModelParser('add');
add.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
add.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
assert.deepStrictEqual(result.contents, { 'a': 1, 'b': 2 });
base = new ConfigurationModelParser('base');
add = new ConfigurationModelParser('add');
result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, {});
assert.deepStrictEqual(result.contents, {});
});
test('Recursive merge using config models', () => {
let base = new ConfigurationModelParser('base');
base.parseContent(JSON.stringify({ 'a': { 'b': 1 } }));
base.parse(JSON.stringify({ 'a': { 'b': 1 } }));
let add = new ConfigurationModelParser('add');
add.parseContent(JSON.stringify({ 'a': { 'b': 2 } }));
add.parse(JSON.stringify({ 'a': { 'b': 2 } }));
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
assert.deepStrictEqual(result.contents, { 'a': { 'b': 2 } });
});
test('Test contents while getting an existing property', () => {
let testObject = new ConfigurationModelParser('test');
testObject.parseContent(JSON.stringify({ 'a': 1 }));
assert.deepEqual(testObject.configurationModel.getValue('a'), 1);
testObject.parse(JSON.stringify({ 'a': 1 }));
assert.deepStrictEqual(testObject.configurationModel.getValue('a'), 1);
testObject.parseContent(JSON.stringify({ 'a': { 'b': 1 } }));
assert.deepEqual(testObject.configurationModel.getValue('a'), { 'b': 1 });
testObject.parse(JSON.stringify({ 'a': { 'b': 1 } }));
assert.deepStrictEqual(testObject.configurationModel.getValue('a'), { 'b': 1 });
});
test('Test contents are undefined for non existing properties', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parseContent(JSON.stringify({
testObject.parse(JSON.stringify({
awesome: true
}));
assert.deepEqual(testObject.configurationModel.getValue('unknownproperty'), undefined);
assert.deepStrictEqual(testObject.configurationModel.getValue('unknownproperty'), undefined);
});
test('Test contents are undefined for undefined config', () => {
const testObject = new ConfigurationModelParser('test');
assert.deepEqual(testObject.configurationModel.getValue('unknownproperty'), undefined);
assert.deepStrictEqual(testObject.configurationModel.getValue('unknownproperty'), undefined);
});
test('Test configWithOverrides gives all content merged with overrides', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parseContent(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
testObject.parse(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
assert.deepEqual(testObject.configurationModel.override('b').contents, { 'a': 2, 'c': 1, '[b]': { 'a': 2 } });
assert.deepStrictEqual(testObject.configurationModel.override('b').contents, { 'a': 2, 'c': 1, '[b]': { 'a': 2 } });
});
test('Test configWithOverrides gives empty contents', () => {
const testObject = new ConfigurationModelParser('test');
assert.deepEqual(testObject.configurationModel.override('b').contents, {});
assert.deepStrictEqual(testObject.configurationModel.override('b').contents, {});
});
test('Test update with empty data', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parseContent('');
testObject.parse('');
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
assert.deepStrictEqual(testObject.configurationModel.contents, Object.create(null));
assert.deepStrictEqual(testObject.configurationModel.keys, []);
testObject.parseContent(null!);
testObject.parse(null!);
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
assert.deepStrictEqual(testObject.configurationModel.contents, Object.create(null));
assert.deepStrictEqual(testObject.configurationModel.keys, []);
testObject.parseContent(undefined!);
testObject.parse(undefined!);
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
assert.deepStrictEqual(testObject.configurationModel.contents, Object.create(null));
assert.deepStrictEqual(testObject.configurationModel.keys, []);
});
test('Test registering the same property again', () => {
@@ -356,7 +361,7 @@ suite('CustomConfigurationModel', () => {
}
}
});
assert.equal(true, new DefaultConfigurationModel().getValue('a'));
assert.strictEqual(true, new DefaultConfigurationModel().getValue('a'));
});
});
@@ -370,28 +375,28 @@ suite('Configuration', () => {
const { overrideIdentifiers } = testObject.inspect('a', {}, undefined);
assert.deepEqual(overrideIdentifiers, ['l1', 'l3', 'l4']);
assert.deepStrictEqual(overrideIdentifiers, ['l1', 'l3', 'l4']);
});
test('Test update value', () => {
const parser = new ConfigurationModelParser('test');
parser.parseContent(JSON.stringify({ 'a': 1 }));
parser.parse(JSON.stringify({ 'a': 1 }));
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
testObject.updateValue('a', 2);
assert.equal(testObject.getValue('a', {}, undefined), 2);
assert.strictEqual(testObject.getValue('a', {}, undefined), 2);
});
test('Test update value after inspect', () => {
const parser = new ConfigurationModelParser('test');
parser.parseContent(JSON.stringify({ 'a': 1 }));
parser.parse(JSON.stringify({ 'a': 1 }));
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
testObject.inspect('a', {}, undefined);
testObject.updateValue('a', 2);
assert.equal(testObject.getValue('a', {}, undefined), 2);
assert.strictEqual(testObject.getValue('a', {}, undefined), 2);
});
test('Test compare and update default configuration', () => {
@@ -407,7 +412,7 @@ suite('Configuration', () => {
}
}), ['editor.lineNumbers', '[markdown]']);
assert.deepEqual(actual, { keys: ['editor.lineNumbers', '[markdown]'], overrides: [['markdown', ['editor.wordWrap']]] });
assert.deepStrictEqual(actual, { keys: ['editor.lineNumbers', '[markdown]'], overrides: [['markdown', ['editor.wordWrap']]] });
});
@@ -430,7 +435,7 @@ suite('Configuration', () => {
}
}));
assert.deepEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
assert.deepStrictEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
});
@@ -453,7 +458,7 @@ suite('Configuration', () => {
}
}));
assert.deepEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
assert.deepStrictEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
});
@@ -476,7 +481,7 @@ suite('Configuration', () => {
}
}));
assert.deepEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
assert.deepStrictEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
});
@@ -492,13 +497,13 @@ suite('Configuration', () => {
const actual = testObject.compareAndDeleteFolderConfiguration(URI.file('file1'));
assert.deepEqual(actual, { keys: ['editor.lineNumbers', 'editor.fontSize', '[typescript]'], overrides: [['typescript', ['editor.wordWrap']]] });
assert.deepStrictEqual(actual, { keys: ['editor.lineNumbers', 'editor.fontSize', '[typescript]'], overrides: [['typescript', ['editor.wordWrap']]] });
});
function parseConfigurationModel(content: any): ConfigurationModel {
const parser = new ConfigurationModelParser('test');
parser.parseContent(JSON.stringify(content));
parser.parse(JSON.stringify(content));
return parser.configurationModel;
}
@@ -515,7 +520,7 @@ suite('ConfigurationChangeEvent', () => {
}));
let testObject = new ConfigurationChangeEvent(change, undefined, configuration);
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview', 'files.autoSave']);
assert.deepStrictEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview', 'files.autoSave']);
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window'));
@@ -547,7 +552,7 @@ suite('ConfigurationChangeEvent', () => {
}));
let testObject = new ConfigurationChangeEvent(change, { data }, configuration);
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview']);
assert.deepStrictEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview']);
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window'));
@@ -571,7 +576,7 @@ suite('ConfigurationChangeEvent', () => {
}));
let testObject = new ConfigurationChangeEvent(change, undefined, configuration);
assert.deepEqual(testObject.affectedKeys, ['files.autoSave', '[markdown]', 'editor.wordWrap']);
assert.deepStrictEqual(testObject.affectedKeys, ['files.autoSave', '[markdown]', 'editor.wordWrap']);
assert.ok(testObject.affectsConfiguration('files'));
assert.ok(testObject.affectsConfiguration('files.autoSave'));
@@ -613,7 +618,7 @@ suite('ConfigurationChangeEvent', () => {
}));
let testObject = new ConfigurationChangeEvent(change, { data }, configuration);
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', '[markdown]', 'workbench.editor.enablePreview', 'editor.fontSize']);
assert.deepStrictEqual(testObject.affectedKeys, ['window.zoomLevel', '[markdown]', 'workbench.editor.enablePreview', 'editor.fontSize']);
assert.ok(!testObject.affectsConfiguration('files'));
@@ -657,7 +662,7 @@ suite('ConfigurationChangeEvent', () => {
);
let testObject = new ConfigurationChangeEvent(change, { data, workspace }, configuration, workspace);
assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
assert.deepStrictEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('folder1') }));
@@ -755,7 +760,7 @@ suite('ConfigurationChangeEvent', () => {
const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('file1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('file2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]);
const testObject = new ConfigurationChangeEvent(change, { data, workspace }, configuration, workspace);
assert.deepEqual(testObject.affectedKeys, ['editor.lineNumbers', '[markdown]', '[json]', 'window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows', 'editor.wordWrap']);
assert.deepStrictEqual(testObject.affectedKeys, ['editor.lineNumbers', '[markdown]', '[json]', 'window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows', 'editor.wordWrap']);
assert.ok(testObject.affectsConfiguration('window.title'));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file1') }));
@@ -841,7 +846,7 @@ suite('ConfigurationChangeEvent', () => {
}));
let testObject = new ConfigurationChangeEvent(change, undefined, configuration);
assert.deepEqual(testObject.affectedKeys, ['launch', 'launch.version', 'tasks']);
assert.deepStrictEqual(testObject.affectedKeys, ['launch', 'launch.version', 'tasks']);
assert.ok(testObject.affectsConfiguration('launch'));
assert.ok(testObject.affectsConfiguration('launch.version'));
assert.ok(testObject.affectsConfiguration('tasks'));
@@ -870,7 +875,7 @@ suite('AllKeysConfigurationChangeEvent', () => {
const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('file1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('file2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]);
let testObject = new AllKeysConfigurationChangeEvent(configuration, workspace, ConfigurationTarget.USER, null);
assert.deepEqual(testObject.affectedKeys, ['editor.lineNumbers', '[markdown]', '[json]', 'window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
assert.deepStrictEqual(testObject.affectedKeys, ['editor.lineNumbers', '[markdown]', '[json]', 'window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
assert.ok(testObject.affectsConfiguration('window.title'));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file1') }));
@@ -946,6 +951,6 @@ suite('AllKeysConfigurationChangeEvent', () => {
function toConfigurationModel(obj: any): ConfigurationModel {
const parser = new ConfigurationModelParser('test');
parser.parseContent(JSON.stringify(obj));
parser.parse(JSON.stringify(obj));
return parser.configurationModel;
}

View File

@@ -24,15 +24,15 @@ suite('ConfigurationRegistry', () => {
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 1, b: 2 } }]);
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 2, c: 3 } }]);
assert.deepEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 1, b: 2 });
assert.deepEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, c: 3 });
assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 1, b: 2 });
assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, c: 3 });
});
test('configuration override defaults - merges defaults', async () => {
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 1, b: 2 } }]);
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 2, c: 3 } }]);
assert.deepEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, b: 2, c: 3 });
assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, b: 2, c: 3 });
});
test('configuration defaults - overrides defaults', async () => {
@@ -48,6 +48,6 @@ suite('ConfigurationRegistry', () => {
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 1, b: 2 } }]);
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 2, c: 3 } }]);
assert.deepEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 2, c: 3 });
assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 2, c: 3 });
});
});

View File

@@ -43,7 +43,7 @@ suite('ConfigurationService', () => {
}>();
assert.ok(config);
assert.equal(config.foo, 'bar');
assert.strictEqual(config.foo, 'bar');
});
test('config gets flattened', async () => {
@@ -62,7 +62,7 @@ suite('ConfigurationService', () => {
assert.ok(config);
assert.ok(config.testworkbench);
assert.ok(config.testworkbench.editor);
assert.equal(config.testworkbench.editor.tabs, true);
assert.strictEqual(config.testworkbench.editor.tabs, true);
});
test('error case does not explode', async () => {
@@ -91,7 +91,7 @@ suite('ConfigurationService', () => {
await testObject.initialize();
return new Promise<void>(async (c) => {
disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(() => {
assert.equal(testObject.getValue('foo'), 'bar');
assert.strictEqual(testObject.getValue('foo'), 'bar');
c();
}));
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }'));
@@ -106,7 +106,7 @@ suite('ConfigurationService', () => {
return new Promise<void>((c) => {
disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
assert.equal(testObject.getValue('foo'), 'barz');
assert.strictEqual(testObject.getValue('foo'), 'barz');
c();
}));
fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "barz" }'));
@@ -122,7 +122,7 @@ suite('ConfigurationService', () => {
foo: string;
}>();
assert.ok(config);
assert.equal(config.foo, 'bar');
assert.strictEqual(config.foo, 'bar');
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "changed" }'));
// force a reload to get latest
@@ -131,7 +131,7 @@ suite('ConfigurationService', () => {
foo: string;
}>();
assert.ok(config);
assert.equal(config.foo, 'changed');
assert.strictEqual(config.foo, 'changed');
});
test('model defaults', async () => {
@@ -160,7 +160,7 @@ suite('ConfigurationService', () => {
let setting = testObject.getValue<ITestSetting>();
assert.ok(setting);
assert.equal(setting.configuration.service.testSetting, 'isSet');
assert.strictEqual(setting.configuration.service.testSetting, 'isSet');
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }'));
testObject = disposables.add(new ConfigurationService(settingsResource, fileService));
@@ -168,14 +168,14 @@ suite('ConfigurationService', () => {
setting = testObject.getValue<ITestSetting>();
assert.ok(setting);
assert.equal(setting.configuration.service.testSetting, 'isSet');
assert.strictEqual(setting.configuration.service.testSetting, 'isSet');
await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "configuration.service.testSetting": "isChanged" }'));
await testObject.reloadConfiguration();
setting = testObject.getValue<ITestSetting>();
assert.ok(setting);
assert.equal(setting.configuration.service.testSetting, 'isChanged');
assert.strictEqual(setting.configuration.service.testSetting, 'isChanged');
});
test('lookup', async () => {

View File

@@ -75,19 +75,19 @@ class NullContext extends Context {
super(-1, null);
}
public setValue(key: string, value: any): boolean {
public override setValue(key: string, value: any): boolean {
return false;
}
public removeValue(key: string): boolean {
public override removeValue(key: string): boolean {
return false;
}
public getValue<T>(key: string): T | undefined {
public override getValue<T>(key: string): T | undefined {
return undefined;
}
collectAllValues(): { [key: string]: any; } {
override collectAllValues(): { [key: string]: any; } {
return Object.create(null);
}
}
@@ -137,7 +137,7 @@ class ConfigAwareContextValuesContainer extends Context {
this._listener.dispose();
}
getValue(key: string): any {
override getValue(key: string): any {
if (key.indexOf(ConfigAwareContextValuesContainer._keyPrefix) !== 0) {
return super.getValue(key);
@@ -168,15 +168,15 @@ class ConfigAwareContextValuesContainer extends Context {
return value;
}
setValue(key: string, value: any): boolean {
override setValue(key: string, value: any): boolean {
return super.setValue(key, value);
}
removeValue(key: string): boolean {
override removeValue(key: string): boolean {
return super.removeValue(key);
}
collectAllValues(): { [key: string]: any; } {
override collectAllValues(): { [key: string]: any; } {
const result: { [key: string]: any } = Object.create(null);
this._values.forEach((value, index) => result[index] = value);
return { ...result, ...super.collectAllValues() };

View File

@@ -339,7 +339,7 @@ export class ContextKeyDefinedExpr implements IContextKeyExpression {
public readonly type = ContextKeyExprType.Defined;
protected constructor(protected readonly key: string) {
protected constructor(readonly key: string) {
}
public cmp(other: ContextKeyExpression): number {
@@ -1273,7 +1273,7 @@ export class RawContextKey<T> extends ContextKeyDefinedExpr {
private readonly _defaultValue: T | undefined;
constructor(readonly key: string, defaultValue: T | undefined, metaOrHide?: string | true | { type: string, description: string }) {
constructor(key: string, defaultValue: T | undefined, metaOrHide?: string | true | { type: string, description: string }) {
super(key);
this._defaultValue = defaultValue;

View File

@@ -18,6 +18,7 @@ import { EventType, $, isHTMLElement } from 'vs/base/browser/dom';
import { attachMenuStyler } from 'vs/platform/theme/common/styler';
import { domEvent } from 'vs/base/browser/event';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { isPromiseCanceledError } from 'vs/base/common/errors';
export interface IContextMenuHandlerOptions {
blockMouse: boolean;
@@ -145,9 +146,7 @@ export class ContextMenuHandler {
}
private onActionRun(e: IRunEvent): void {
if (this.telemetryService) {
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: e.action.id, from: 'contextMenu' });
}
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: e.action.id, from: 'contextMenu' });
this.contextViewService.hideContextView(false);
@@ -158,7 +157,7 @@ export class ContextMenuHandler {
}
private onDidActionRun(e: IRunEvent): void {
if (e.error) {
if (e.error && !isPromiseCanceledError(e.error)) {
this.notificationService.error(e.error);
}
}

View File

@@ -5,7 +5,6 @@
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { IProcessEnvironment } from 'vs/base/common/platform';
export const IExtensionHostDebugService = createDecorator<IExtensionHostDebugService>('extensionHostDebugService');
@@ -30,6 +29,14 @@ export interface ICloseSessionEvent {
export interface IOpenExtensionWindowResult {
rendererDebugPort?: number;
success: boolean;
}
/**
* Like a IProcessEnvironment, but the value "null" deletes an environment variable
*/
export interface INullableProcessEnvironment {
[key: string]: string | null;
}
export interface IExtensionHostDebugService {
@@ -47,5 +54,5 @@ export interface IExtensionHostDebugService {
terminateSession(sessionId: string, subId?: string): void;
readonly onTerminateSession: Event<ITerminateSessionEvent>;
openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment, debugRenderer: boolean): Promise<IOpenExtensionWindowResult>;
openExtensionDevelopmentHostWindow(args: string[], env: INullableProcessEnvironment | undefined, debugRenderer: boolean): Promise<IOpenExtensionWindowResult>;
}

View File

@@ -4,10 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { IReloadSessionEvent, ICloseSessionEvent, IAttachSessionEvent, ITerminateSessionEvent, IExtensionHostDebugService, IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug';
import { IReloadSessionEvent, ICloseSessionEvent, IAttachSessionEvent, ITerminateSessionEvent, IExtensionHostDebugService, IOpenExtensionWindowResult, INullableProcessEnvironment } from 'vs/platform/debug/common/extensionHostDebug';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IProcessEnvironment } from 'vs/base/common/platform';
export class ExtensionHostDebugBroadcastChannel<TContext> implements IServerChannel<TContext> {
@@ -87,7 +86,7 @@ export class ExtensionHostDebugChannelClient extends Disposable implements IExte
return this.channel.listen('terminate');
}
openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment, debugRenderer: boolean): Promise<IOpenExtensionWindowResult> {
return this.channel.call('openExtensionDevelopmentHostWindow', [args, env, debugRenderer]);
openExtensionDevelopmentHostWindow(args: string[], env: INullableProcessEnvironment | undefined, debugRenderer: boolean): Promise<IOpenExtensionWindowResult> {
return this.channel.call('openExtensionDevelopmentHostWindow', [args, env || {}, debugRenderer]);
}
}

View File

@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug';
import { INullableProcessEnvironment, IOpenExtensionWindowResult } from 'vs/platform/debug/common/extensionHostDebug';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { createServer, AddressInfo } from 'net';
@@ -16,7 +16,7 @@ export class ElectronExtensionHostDebugBroadcastChannel<TContext> extends Extens
super();
}
call(ctx: TContext, command: string, arg?: any): Promise<any> {
override call(ctx: TContext, command: string, arg?: any): Promise<any> {
if (command === 'openExtensionDevelopmentHostWindow') {
return this.openExtensionDevelopmentHostWindow(arg[0], arg[1], arg[2]);
} else {
@@ -24,28 +24,45 @@ export class ElectronExtensionHostDebugBroadcastChannel<TContext> extends Extens
}
}
private async openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment, debugRenderer: boolean): Promise<IOpenExtensionWindowResult> {
private async openExtensionDevelopmentHostWindow(args: string[], env: INullableProcessEnvironment, debugRenderer: boolean): Promise<IOpenExtensionWindowResult> {
const pargs = parseArgs(args, OPTIONS);
pargs.debugRenderer = debugRenderer;
const extDevPaths = pargs.extensionDevelopmentPath;
if (!extDevPaths) {
return {};
return { success: false };
}
// split INullableProcessEnvironment into a IProcessEnvironment and an array of keys to be deleted
// TODO: support to delete env vars; currently the "deletes" are ignored
let userEnv: IProcessEnvironment | undefined;
//let userEnvDeletes: string[] = [];
const keys = Object.keys(env);
for (let k of keys) {
let value = env[k];
if (value === null) {
//userEnvDeletes.push(k);
} else {
if (!userEnv) {
userEnv = Object.create(null) as IProcessEnvironment;
}
userEnv[k] = value;
}
}
const [codeWindow] = this.windowsMainService.openExtensionDevelopmentHostWindow(extDevPaths, {
context: OpenContext.API,
cli: pargs,
userEnv: Object.keys(env).length > 0 ? env : undefined
userEnv: userEnv
});
if (!debugRenderer) {
return {};
return { success: true };
}
const win = codeWindow.win;
if (!win) {
return {};
return { success: true };
}
const debug = win.webContents.debugger;
@@ -110,6 +127,6 @@ export class ElectronExtensionHostDebugBroadcastChannel<TContext> extends Extens
await new Promise<void>(r => server.listen(0, r));
win.on('close', () => server.close());
return { rendererDebugPort: (server.address() as AddressInfo).port };
return { rendererDebugPort: (server.address() as AddressInfo).port, success: true };
}
}

View File

@@ -9,6 +9,8 @@ import { URI } from 'vs/base/common/uri';
import { basename } from 'vs/base/common/resources';
import { localize } from 'vs/nls';
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { Codicon } from 'vs/base/common/codicons';
import { IMarkdownString } from 'vs/base/common/htmlContent';
export interface FileFilter {
extensions: string[];
@@ -100,6 +102,7 @@ export interface IPickAndOpenOptions {
defaultUri?: URI;
telemetryExtraData?: ITelemetryData;
availableFileSystems?: string[];
remoteAuthority?: string | null;
}
export interface ISaveDialogOptions {
@@ -177,11 +180,24 @@ export interface IOpenDialogOptions {
export const IDialogService = createDecorator<IDialogService>('dialogService');
export interface ICustomDialogOptions {
buttonDetails?: string[];
markdownDetails?: ICustomDialogMarkdown[];
classes?: string[];
icon?: Codicon;
disableCloseAction?: boolean;
}
export interface ICustomDialogMarkdown {
markdown: IMarkdownString,
classes?: string[]
}
export interface IDialogOptions {
cancelId?: number;
detail?: string;
checkbox?: ICheckbox;
useCustom?: boolean;
custom?: boolean | ICustomDialogOptions;
}
export interface IInput {

View File

@@ -54,3 +54,12 @@ export interface IWindowDriver {
getTerminalBuffer(selector: string): Promise<string[]>;
writeInTerminal(selector: string, text: string): Promise<void>;
}
export interface IDriverOptions {
verbose: boolean;
}
export interface IWindowDriverRegistry {
registerWindowDriver(windowId: number): Promise<IDriverOptions>;
reloadWindowDriver(windowId: number): Promise<void>;
}

View File

@@ -0,0 +1,96 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. 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';
import { IDriverOptions, IElement, IWindowDriver, IWindowDriverRegistry } from 'vs/platform/driver/common/driver';
export class WindowDriverChannel implements IServerChannel {
constructor(private driver: IWindowDriver) { }
listen<T>(_: unknown, event: string): Event<T> {
throw new Error(`No event found: ${event}`);
}
call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'click': return this.driver.click(arg[0], arg[1], arg[2]);
case 'doubleClick': return this.driver.doubleClick(arg);
case 'setValue': return this.driver.setValue(arg[0], arg[1]);
case 'getTitle': return this.driver.getTitle();
case 'isActiveElement': return this.driver.isActiveElement(arg);
case 'getElements': return this.driver.getElements(arg[0], arg[1]);
case 'getElementXY': return this.driver.getElementXY(arg[0], arg[1], arg[2]);
case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1]);
case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg);
case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1]);
}
throw new Error(`Call not found: ${command}`);
}
}
export class WindowDriverChannelClient implements IWindowDriver {
declare readonly _serviceBrand: undefined;
constructor(private channel: IChannel) { }
click(selector: string, xoffset?: number, yoffset?: number): Promise<void> {
return this.channel.call('click', [selector, xoffset, yoffset]);
}
doubleClick(selector: string): Promise<void> {
return this.channel.call('doubleClick', selector);
}
setValue(selector: string, text: string): Promise<void> {
return this.channel.call('setValue', [selector, text]);
}
getTitle(): Promise<string> {
return this.channel.call('getTitle');
}
isActiveElement(selector: string): Promise<boolean> {
return this.channel.call('isActiveElement', selector);
}
getElements(selector: string, recursive: boolean): Promise<IElement[]> {
return this.channel.call('getElements', [selector, recursive]);
}
getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number, y: number }> {
return this.channel.call('getElementXY', [selector, xoffset, yoffset]);
}
typeInEditor(selector: string, text: string): Promise<void> {
return this.channel.call('typeInEditor', [selector, text]);
}
getTerminalBuffer(selector: string): Promise<string[]> {
return this.channel.call('getTerminalBuffer', selector);
}
writeInTerminal(selector: string, text: string): Promise<void> {
return this.channel.call('writeInTerminal', [selector, text]);
}
}
export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry {
declare readonly _serviceBrand: undefined;
constructor(private channel: IChannel) { }
registerWindowDriver(windowId: number): Promise<IDriverOptions> {
return this.channel.call('registerWindowDriver', windowId);
}
reloadWindowDriver(windowId: number): Promise<void> {
return this.channel.call('reloadWindowDriver', windowId);
}
}

View File

@@ -3,7 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DriverChannel, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IDriverOptions } from 'vs/platform/driver/node/driver';
import { DriverChannel, WindowDriverRegistryChannel } from 'vs/platform/driver/node/driver';
import { WindowDriverChannelClient } from 'vs/platform/driver/common/driverIpc';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { serve as serveNet } from 'vs/base/parts/ipc/node/ipc.net';
import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle';
@@ -17,7 +18,7 @@ import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/e
import { ScanCodeBinding } from 'vs/base/common/scanCode';
import { KeybindingParser } from 'vs/base/common/keybindingParser';
import { timeout } from 'vs/base/common/async';
import { IDriver, IElement, IWindowDriver } from 'vs/platform/driver/common/driver';
import { IDriver, IDriverOptions, IElement, IWindowDriver, IWindowDriverRegistry } from 'vs/platform/driver/common/driver';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService';

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/node/driver';
import { WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/common/driverIpc';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { timeout } from 'vs/base/common/async';

View File

@@ -7,7 +7,7 @@ import { Client } from 'vs/base/parts/ipc/common/ipc.net';
import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net';
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { IDriver, IElement, IWindowDriver } from 'vs/platform/driver/common/driver';
import { IDriver, IElement, IWindowDriverRegistry } from 'vs/platform/driver/common/driver';
export class DriverChannel implements IServerChannel {
@@ -107,15 +107,6 @@ export class DriverChannelClient implements IDriver {
}
}
export interface IDriverOptions {
verbose: boolean;
}
export interface IWindowDriverRegistry {
registerWindowDriver(windowId: number): Promise<IDriverOptions>;
reloadWindowDriver(windowId: number): Promise<void>;
}
export class WindowDriverRegistryChannel implements IServerChannel {
constructor(private registry: IWindowDriverRegistry) { }
@@ -134,94 +125,6 @@ export class WindowDriverRegistryChannel implements IServerChannel {
}
}
export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry {
declare readonly _serviceBrand: undefined;
constructor(private channel: IChannel) { }
registerWindowDriver(windowId: number): Promise<IDriverOptions> {
return this.channel.call('registerWindowDriver', windowId);
}
reloadWindowDriver(windowId: number): Promise<void> {
return this.channel.call('reloadWindowDriver', windowId);
}
}
export class WindowDriverChannel implements IServerChannel {
constructor(private driver: IWindowDriver) { }
listen<T>(_: unknown, event: string): Event<T> {
throw new Error(`No event found: ${event}`);
}
call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'click': return this.driver.click(arg[0], arg[1], arg[2]);
case 'doubleClick': return this.driver.doubleClick(arg);
case 'setValue': return this.driver.setValue(arg[0], arg[1]);
case 'getTitle': return this.driver.getTitle();
case 'isActiveElement': return this.driver.isActiveElement(arg);
case 'getElements': return this.driver.getElements(arg[0], arg[1]);
case 'getElementXY': return this.driver.getElementXY(arg[0], arg[1], arg[2]);
case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1]);
case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg);
case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1]);
}
throw new Error(`Call not found: ${command}`);
}
}
export class WindowDriverChannelClient implements IWindowDriver {
declare readonly _serviceBrand: undefined;
constructor(private channel: IChannel) { }
click(selector: string, xoffset?: number, yoffset?: number): Promise<void> {
return this.channel.call('click', [selector, xoffset, yoffset]);
}
doubleClick(selector: string): Promise<void> {
return this.channel.call('doubleClick', selector);
}
setValue(selector: string, text: string): Promise<void> {
return this.channel.call('setValue', [selector, text]);
}
getTitle(): Promise<string> {
return this.channel.call('getTitle');
}
isActiveElement(selector: string): Promise<boolean> {
return this.channel.call('isActiveElement', selector);
}
getElements(selector: string, recursive: boolean): Promise<IElement[]> {
return this.channel.call('getElements', [selector, recursive]);
}
getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number, y: number }> {
return this.channel.call('getElementXY', [selector, xoffset, yoffset]);
}
typeInEditor(selector: string, text: string): Promise<void> {
return this.channel.call('typeInEditor', [selector, text]);
}
getTerminalBuffer(selector: string): Promise<string[]> {
return this.channel.call('getTerminalBuffer', selector);
}
writeInTerminal(selector: string, text: string): Promise<void> {
return this.channel.call('writeInTerminal', [selector, text]);
}
}
export async function connect(handle: string): Promise<{ client: Client, driver: IDriver }> {
const client = await connectNet(handle, 'driverClient');
const channel = client.getChannel('driver');

View File

@@ -9,14 +9,19 @@ import { Event } from 'vs/base/common/event';
export interface IEditorModel {
/**
* Emitted when the model is disposed.
* Emitted when the model is about to be disposed.
*/
readonly onDispose: Event<void>;
readonly onWillDispose: Event<void>;
/**
* Loads the model.
* Resolves the model.
*/
load(): Promise<IEditorModel>;
resolve(): Promise<void>;
/**
* Find out if the editor model was resolved or not.
*/
isResolved(): boolean;
/**
* Find out if this model has been disposed.
@@ -65,6 +70,23 @@ export interface IBaseResourceEditorInput {
readonly forceUntitled?: boolean;
}
/**
* This identifier allows to uniquely identify an editor with a
* resource and type identifier.
*/
export interface IResourceEditorInputIdentifier {
/**
* The resource URI of the editor.
*/
readonly resource: URI;
/**
* The type of the editor.
*/
readonly typeId: string;
}
export interface IResourceEditorInput extends IBaseResourceEditorInput {
/**

View File

@@ -41,7 +41,7 @@ export interface NativeParsedArgs {
'extensions-dir'?: string;
'extensions-download-dir'?: string;
'builtin-extensions-dir'?: string;
extensionDevelopmentPath?: string[]; // // undefined or array of 1 or more local paths or URIs
extensionDevelopmentPath?: string[]; // undefined or array of 1 or more local paths or URIs
extensionTestsPath?: string; // either a local path or a URI
extensionDevelopmentKind?: string[];
'inspect-extensions'?: string;
@@ -100,4 +100,5 @@ export interface NativeParsedArgs {
'ignore-certificate-errors'?: boolean;
'allow-insecure-localhost'?: boolean;
'log-net-log'?: string;
'vmodule'?: string;
}

View File

@@ -25,9 +25,12 @@ export interface IEnvironmentMainService extends INativeEnvironmentService {
backupHome: string;
backupWorkspacesPath: string;
// --- V8 script cache path
// --- V8 script cache path (ours)
nodeCachedDataDir?: string;
// --- V8 script cache path (chrome)
chromeCachedDataDir: string;
// --- IPC
mainIPCHandle: string;
@@ -66,4 +69,7 @@ export class EnvironmentMainService extends NativeEnvironmentService implements
@memoize
get nodeCachedDataDir(): string | undefined { return process.env['VSCODE_NODE_CACHED_DATA_DIR'] || undefined; }
@memoize
get chromeCachedDataDir(): string { return join(this.userDataPath, 'Code Cache'); }
}

View File

@@ -134,6 +134,7 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
'ignore-certificate-errors': { type: 'boolean' },
'allow-insecure-localhost': { type: 'boolean' },
'log-net-log': { type: 'string' },
'vmodule': { type: 'string' },
'_urls': { type: 'string[]' },
_: { type: 'string[]' } // main arguments

View File

@@ -8,6 +8,7 @@ import { localize } from 'vs/nls';
import { MIN_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/common/files';
import { parseArgs, ErrorReporter, OPTIONS } from 'vs/platform/environment/node/argv';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { IProcessEnvironment } from 'vs/base/common/platform';
function parseAndValidate(cmdLineArgs: string[], reportWarnings: boolean): NativeParsedArgs {
const errorReporter: ErrorReporter = {
@@ -79,6 +80,6 @@ export function addArg(argv: string[], ...args: string[]): string[] {
return argv;
}
export function isLaunchedFromCli(env: NodeJS.ProcessEnv): boolean {
export function isLaunchedFromCli(env: IProcessEnvironment): boolean {
return env['VSCODE_CLI'] === '1';
}

View File

@@ -3,9 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { spawn } from 'child_process';
import { generateUuid } from 'vs/base/common/uuid';
import { isWindows, platform } from 'vs/base/common/platform';
import { IProcessEnvironment, isWindows, OS } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper';
@@ -17,7 +18,7 @@ import { getSystemShell } from 'vs/base/node/shell';
* This should only be done when Code itself is not launched
* from within a shell.
*/
export async function resolveShellEnv(logService: ILogService, args: NativeParsedArgs, env: NodeJS.ProcessEnv): Promise<typeof process.env> {
export async function resolveShellEnv(logService: ILogService, args: NativeParsedArgs, env: IProcessEnvironment): Promise<typeof process.env> {
// Skip if --force-disable-user-env
if (args['force-disable-user-env']) {
@@ -75,31 +76,55 @@ async function doResolveUnixShellEnv(logService: ILogService): Promise<typeof pr
ELECTRON_NO_ATTACH_CONSOLE: '1'
};
const command = `'${process.execPath}' -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`;
logService.trace('getUnixShellEnvironment#env', env);
logService.trace('getUnixShellEnvironment#spawn', command);
const systemShellUnix = await getSystemShell(platform, env);
const systemShellUnix = await getSystemShell(OS, env);
logService.trace('getUnixShellEnvironment#shell', systemShellUnix);
const child = spawn(systemShellUnix, ['-ilc', command], {
// handle popular non-POSIX shells
const name = path.basename(systemShellUnix);
let command: string, shellArgs: Array<string>;
if (/^pwsh(-preview)?$/.test(name)) {
// Older versions of PowerShell removes double quotes sometimes so we use "double single quotes" which is how
// you escape single quotes inside of a single quoted string.
command = `& '${process.execPath}' -p '''${mark}'' + JSON.stringify(process.env) + ''${mark}'''`;
shellArgs = ['-Login', '-Command'];
} else {
command = `'${process.execPath}' -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`;
shellArgs = ['-ilc'];
}
logService.trace('getUnixShellEnvironment#spawn', JSON.stringify(shellArgs), command);
const child = spawn(systemShellUnix, [...shellArgs, command], {
detached: true,
stdio: ['ignore', 'pipe', process.stderr],
stdio: ['ignore', 'pipe', 'pipe'],
env
});
child.on('error', err => {
logService.error('getUnixShellEnvironment#errorChildProcess', toErrorMessage(err));
resolve({});
});
const buffers: Buffer[] = [];
child.on('error', () => resolve({}));
child.stdout.on('data', b => buffers.push(b));
child.on('close', code => {
if (code !== 0) {
return reject(new Error('Failed to get environment'));
}
const stderr: Buffer[] = [];
child.stderr.on('data', b => stderr.push(b));
child.on('close', (code, signal) => {
const raw = Buffer.concat(buffers).toString('utf8');
logService.trace('getUnixShellEnvironment#raw', raw);
const stderrStr = Buffer.concat(stderr).toString('utf8');
if (stderrStr.trim()) {
logService.trace('getUnixShellEnvironment#stderr', stderrStr);
}
if (code || signal) {
return reject(new Error(`Failed to get environment (code ${code}, signal ${signal})`));
}
const match = regex.exec(raw);
const rawStripped = match ? match[1] : '{}';
@@ -124,7 +149,7 @@ async function doResolveUnixShellEnv(logService: ILogService): Promise<typeof pr
logService.trace('getUnixShellEnvironment#result', env);
resolve(env);
} catch (err) {
logService.error('getUnixShellEnvironment#error', err);
logService.error('getUnixShellEnvironment#errorCaught', toErrorMessage(err));
reject(err);
}
});

View File

@@ -10,23 +10,39 @@
'use strict';
/**
* @typedef {import('../../environment/common/argv').NativeParsedArgs} NativeParsedArgs
*
* @param {typeof import('path')} path
* @param {typeof import('os')} os
* @param {string} productName
* @param {string} cwd
*/
function factory(path, os, productName) {
function factory(path, os, productName, cwd) {
/**
* @param {import('../../environment/common/argv').NativeParsedArgs} cliArgs
* @param {NativeParsedArgs} cliArgs
*
* @returns {string}
*/
function getUserDataPath(cliArgs) {
return path.resolve(doGetUserDataPath(cliArgs));
const userDataPath = doGetUserDataPath(cliArgs);
const pathsToResolve = [userDataPath];
// If the user-data-path is not absolute, make
// sure to resolve it against the passed in
// current working directory. We cannot use the
// node.js `path.resolve()` logic because it will
// not pick up our `VSCODE_CWD` environment variable
// (https://github.com/microsoft/vscode/issues/120269)
if (!path.isAbsolute(userDataPath)) {
pathsToResolve.unshift(cwd);
}
return path.resolve(...pathsToResolve);
}
/**
* @param {import('../../environment/common/argv').NativeParsedArgs} cliArgs
* @param {NativeParsedArgs} cliArgs
*
* @returns {string}
*/
@@ -81,18 +97,25 @@
}
if (typeof define === 'function') {
define(['require', 'path', 'os', 'vs/base/common/network', 'vs/base/common/resources'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('os')} */ os, /** @type {typeof import('../../../base/common/network')} */ network, /** @type {typeof import("../../../base/common/resources")} */ resources) {
define(['require', 'path', 'os', 'vs/base/common/network', 'vs/base/common/resources', 'vs/base/common/process'], function (
require,
/** @type {typeof import('path')} */ path,
/** @type {typeof import('os')} */ os,
/** @type {typeof import('../../../base/common/network')} */ network,
/** @type {typeof import("../../../base/common/resources")} */ resources,
/** @type {typeof import("../../../base/common/process")} */ process
) {
const rootPath = resources.dirname(network.FileAccess.asFileUri('', require));
const pkg = require.__$__nodeRequire(resources.joinPath(rootPath, 'package.json').fsPath);
return factory(path, os, pkg.name);
return factory(path, os, pkg.name, process.cwd());
}); // amd
} else if (typeof module === 'object' && typeof module.exports === 'object') {
const pkg = require('../../../../../package.json');
const path = require('path');
const os = require('os');
module.exports = factory(path, os, pkg.name); // commonjs
module.exports = factory(path, os, pkg.name, process.env['VSCODE_CWD'] || process.cwd()); // commonjs
} else {
throw new Error('Unknown context');
}

View File

@@ -16,13 +16,13 @@ suite('formatOptions', () => {
}
test('Text should display small columns correctly', () => {
assert.deepEqual(
assert.deepStrictEqual(
formatOptions({
'add': o('bar')
}, 80),
[' --add bar']
);
assert.deepEqual(
assert.deepStrictEqual(
formatOptions({
'add': o('bar'),
'wait': o('ba'),
@@ -36,7 +36,7 @@ suite('formatOptions', () => {
});
test('Text should wrap', () => {
assert.deepEqual(
assert.deepStrictEqual(
formatOptions({
'add': o((<any>'bar ').repeat(9))
}, 40),
@@ -47,7 +47,7 @@ suite('formatOptions', () => {
});
test('Text should revert to the condensed view when the terminal is too narrow', () => {
assert.deepEqual(
assert.deepStrictEqual(
formatOptions({
'add': o((<any>'bar ').repeat(9))
}, 30),
@@ -58,11 +58,11 @@ suite('formatOptions', () => {
});
test('addArg', () => {
assert.deepEqual(addArg([], 'foo'), ['foo']);
assert.deepEqual(addArg([], 'foo', 'bar'), ['foo', 'bar']);
assert.deepEqual(addArg(['foo'], 'bar'), ['foo', 'bar']);
assert.deepEqual(addArg(['--wait'], 'bar'), ['--wait', 'bar']);
assert.deepEqual(addArg(['--wait', '--', '--foo'], 'bar'), ['--wait', 'bar', '--', '--foo']);
assert.deepEqual(addArg(['--', '--foo'], 'bar'), ['bar', '--', '--foo']);
assert.deepStrictEqual(addArg([], 'foo'), ['foo']);
assert.deepStrictEqual(addArg([], 'foo', 'bar'), ['foo', 'bar']);
assert.deepStrictEqual(addArg(['foo'], 'bar'), ['foo', 'bar']);
assert.deepStrictEqual(addArg(['--wait'], 'bar'), ['--wait', 'bar']);
assert.deepStrictEqual(addArg(['--wait', '--', '--foo'], 'bar'), ['--wait', 'bar', '--', '--foo']);
assert.deepStrictEqual(addArg(['--', '--foo'], 'bar'), ['bar', '--', '--foo']);
});
});

View File

@@ -133,7 +133,7 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
if (vsixs.length) {
await Promise.all(vsixs.map(async vsix => {
try {
const manifest = await this.installVSIX(vsix, force, output);
const manifest = await this.installVSIX(vsix, { isBuiltin: false, isMachineScoped }, force, output);
if (manifest) {
installedExtensionsManifests.push(manifest);
}
@@ -173,7 +173,7 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
}
}
private async installVSIX(vsix: URI, force: boolean, output: CLIOutput): Promise<IExtensionManifest | null> {
private async installVSIX(vsix: URI, installOptions: InstallOptions, force: boolean, output: CLIOutput): Promise<IExtensionManifest | null> {
const manifest = await this.extensionManagementService.getManifest(vsix);
if (!manifest) {
@@ -183,7 +183,7 @@ export class ExtensionManagementCLIService implements IExtensionManagementCLISer
const valid = await this.validateVSIX(manifest, force, output);
if (valid) {
try {
await this.extensionManagementService.install(vsix);
await this.extensionManagementService.install(vsix, installOptions);
output.log(localize('successVsixInstall', "Extension '{0}' was successfully installed.", getBaseLabel(vsix)));
return manifest;
} catch (error) {

View File

@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { IProductService, IConfigBasedExtensionTip as IRawConfigBasedExtensionTip } from 'vs/platform/product/common/productService';
import { IProductService } from 'vs/platform/product/common/productService';
import { IConfigBasedExtensionTip as IRawConfigBasedExtensionTip } from 'vs/base/common/product';
import { IFileService } from 'vs/platform/files/common/files';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { IExtensionTipsService, IExecutableBasedExtensionTip, IWorkspaceTips, IConfigBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement';

View File

@@ -40,7 +40,7 @@ const lastPromptedMediumImpExeTimeStorageKey = 'extensionTips/lastPromptedMedium
export class ExtensionTipsService extends BaseExtensionTipsService {
_serviceBrand: any;
override _serviceBrand: any;
private readonly highImportanceExecutableTips: Map<string, IExeBasedExtensionTips> = new Map<string, IExeBasedExtensionTips>();
private readonly mediumImportanceExecutableTips: Map<string, IExeBasedExtensionTips> = new Map<string, IExeBasedExtensionTips>();
@@ -101,13 +101,13 @@ export class ExtensionTipsService extends BaseExtensionTipsService {
});
}
async getImportantExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]> {
override async getImportantExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]> {
const highImportanceExeTips = await this.getValidExecutableBasedExtensionTips(this.highImportanceExecutableTips);
const mediumImportanceExeTips = await this.getValidExecutableBasedExtensionTips(this.mediumImportanceExecutableTips);
return [...highImportanceExeTips, ...mediumImportanceExeTips];
}
getOtherExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]> {
override getOtherExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]> {
return this.getValidExecutableBasedExtensionTips(this.allOtherExecutableTips);
}

View File

@@ -388,12 +388,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
publisherDisplayName: extension.publisherDisplayName,
};
let zipPath;
let zipPath: string | undefined;
try {
this.logService.trace('Started downloading extension:', extension.identifier.id);
const zip = await this.extensionsDownloader.downloadExtension(extension, operation);
zipPath = (await this.extensionsDownloader.downloadExtension(extension, operation)).fsPath;
this.logService.info('Downloaded extension:', extension.identifier.id, zipPath);
zipPath = zip.fsPath;
} catch (error) {
throw new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING);
}

View File

@@ -19,8 +19,10 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { mock } from 'vs/base/test/common/mock';
class EnvironmentServiceMock extends mock<IEnvironmentService>() {
constructor(readonly serviceMachineIdResource: URI) {
override readonly serviceMachineIdResource: URI;
constructor(serviceMachineIdResource: URI) {
super();
this.serviceMachineIdResource = serviceMachineIdResource;
}
}
@@ -43,6 +45,6 @@ suite('Extension Gallery Service', () => {
const headers = await resolveMarketplaceHeaders(product.version, environmentService, fileService, storageService);
assert.ok(isUUID(headers['X-Market-User-Id']));
const headers2 = await resolveMarketplaceHeaders(product.version, environmentService, fileService, storageService);
assert.equal(headers['X-Market-User-Id'], headers2['X-Market-User-Id']);
assert.strictEqual(headers['X-Market-User-Id'], headers2['X-Market-User-Id']);
});
});

View File

@@ -9,21 +9,21 @@ suite('Extension Identifier Pattern', () => {
test('extension identifier pattern', () => {
const regEx = new RegExp(EXTENSION_IDENTIFIER_PATTERN);
assert.equal(true, regEx.test('publisher.name'));
assert.equal(true, regEx.test('publiSher.name'));
assert.equal(true, regEx.test('publisher.Name'));
assert.equal(true, regEx.test('PUBLISHER.NAME'));
assert.equal(true, regEx.test('PUBLISHEr.NAMe'));
assert.equal(true, regEx.test('PUBLISHEr.N-AMe'));
assert.equal(true, regEx.test('PUB-LISHEr.NAMe'));
assert.equal(true, regEx.test('PUB-LISHEr.N-AMe'));
assert.equal(true, regEx.test('PUBLISH12Er90.N-A54Me123'));
assert.equal(true, regEx.test('111PUBLISH12Er90.N-1111A54Me123'));
assert.equal(false, regEx.test('publishername'));
assert.equal(false, regEx.test('-publisher.name'));
assert.equal(false, regEx.test('publisher.-name'));
assert.equal(false, regEx.test('-publisher.-name'));
assert.equal(false, regEx.test('publ_isher.name'));
assert.equal(false, regEx.test('publisher._name'));
assert.strictEqual(true, regEx.test('publisher.name'));
assert.strictEqual(true, regEx.test('publiSher.name'));
assert.strictEqual(true, regEx.test('publisher.Name'));
assert.strictEqual(true, regEx.test('PUBLISHER.NAME'));
assert.strictEqual(true, regEx.test('PUBLISHEr.NAMe'));
assert.strictEqual(true, regEx.test('PUBLISHEr.N-AMe'));
assert.strictEqual(true, regEx.test('PUB-LISHEr.NAMe'));
assert.strictEqual(true, regEx.test('PUB-LISHEr.N-AMe'));
assert.strictEqual(true, regEx.test('PUBLISH12Er90.N-A54Me123'));
assert.strictEqual(true, regEx.test('111PUBLISH12Er90.N-1111A54Me123'));
assert.strictEqual(false, regEx.test('publishername'));
assert.strictEqual(false, regEx.test('-publisher.name'));
assert.strictEqual(false, regEx.test('publisher.-name'));
assert.strictEqual(false, regEx.test('-publisher.-name'));
assert.strictEqual(false, regEx.test('publ_isher.name'));
assert.strictEqual(false, regEx.test('publisher._name'));
});
});

View File

@@ -113,14 +113,13 @@ export interface IAuthenticationContribution {
readonly label: string;
}
export interface IWalkthroughTask {
export interface IWalkthroughStep {
readonly id: string;
readonly title: string;
readonly description: string;
readonly button:
| { title: string, link: string, command?: never }
| { title: string, command: string, link?: never },
readonly media: { path: string, altText: string },
readonly media:
| { path: string | { dark: string, light: string, hc: string }, altText: string }
| { path: string, },
readonly doneOn?: { command: string };
readonly when?: string;
}
@@ -129,11 +128,19 @@ export interface IWalkthrough {
readonly id: string,
readonly title: string;
readonly description: string;
readonly tasks: IWalkthroughTask[];
readonly steps: IWalkthroughStep[];
readonly primary?: boolean;
readonly when?: string;
}
export interface IStartEntry {
readonly title: string;
readonly description: string;
readonly command: string;
readonly type?: 'sample-folder' | 'sample-notebook' | string;
readonly when?: string;
}
export interface IExtensionContributions {
commands?: ICommand[];
configuration?: IConfiguration | IConfiguration[];
@@ -155,11 +162,17 @@ export interface IExtensionContributions {
readonly codeActions?: readonly ICodeActionContribution[];
authentication?: IAuthenticationContribution[];
walkthroughs?: IWalkthrough[];
startEntries?: IStartEntry[];
}
export interface IExtensionCapabilities {
readonly virtualWorkspaces?: boolean;
readonly untrustedWorkspaces?: ExtensionUntrustedWorkspaceSupport;
}
export type ExtensionKind = 'ui' | 'workspace' | 'web';
export type ExtensionWorkspaceTrustRequirement = false | 'onStart' | 'onDemand';
export type ExtensionWorkspaceTrust = { required: ExtensionWorkspaceTrustRequirement, description?: string };
export type ExtensionUntrustedWorkpaceSupportType = boolean | 'limited';
export type ExtensionUntrustedWorkspaceSupport = { supported: true; } | { supported: false, description: string } | { supported: 'limited', description: string, restrictedConfigurations?: string[] };
export function isIExtensionIdentifier(thing: any): thing is IExtensionIdentifier {
return thing
@@ -216,7 +229,7 @@ export interface IExtensionManifest {
readonly enableProposedApi?: boolean;
readonly api?: string;
readonly scripts?: { [key: string]: string; };
readonly workspaceTrust?: ExtensionWorkspaceTrust;
readonly capabilities?: IExtensionCapabilities;
}
export const enum ExtensionType {
@@ -316,6 +329,7 @@ export interface IScannedExtension {
readonly packageNLSUrl?: URI;
readonly readmeUrl?: URI;
readonly changelogUrl?: URI;
readonly isUnderDevelopment: boolean;
}
export interface ITranslatedScannedExtension {
@@ -325,6 +339,7 @@ export interface ITranslatedScannedExtension {
readonly packageJSON: IExtensionManifest;
readonly readmeUrl?: URI;
readonly changelogUrl?: URI;
readonly isUnderDevelopment: boolean;
}
export const IBuiltinExtensionsScannerService = createDecorator<IBuiltinExtensionsScannerService>('IBuiltinExtensionsScannerService');

View File

@@ -0,0 +1,210 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions } from 'vs/platform/files/common/files';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { extUri } from 'vs/base/common/resources';
function split(path: string): [string, string] | undefined {
const match = /^(.*)\/([^/]+)$/.exec(path);
if (!match) {
return undefined;
}
const [, parentPath, name] = match;
return [parentPath, name];
}
function getRootUUID(uri: URI): string | undefined {
const match = /^\/([^/]+)\/[^/]+\/?$/.exec(uri.path);
if (!match) {
return undefined;
}
return match[1];
}
export class HTMLFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability {
private readonly files = new Map<string, FileSystemFileHandle>();
private readonly directories = new Map<string, FileSystemDirectoryHandle>();
readonly capabilities: FileSystemProviderCapabilities =
FileSystemProviderCapabilities.FileReadWrite
| FileSystemProviderCapabilities.PathCaseSensitive;
readonly onDidChangeCapabilities = Event.None;
private readonly _onDidChangeFile = new Emitter<readonly IFileChange[]>();
readonly onDidChangeFile = this._onDidChangeFile.event;
private readonly _onDidErrorOccur = new Emitter<string>();
readonly onDidErrorOccur = this._onDidErrorOccur.event;
async readFile(resource: URI): Promise<Uint8Array> {
const handle = await this.getFileHandle(resource);
if (!handle) {
throw new Error('File not found.');
}
const file = await handle.getFile();
return new Uint8Array(await file.arrayBuffer());
}
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
const handle = await this.getFileHandle(resource);
if (!handle) {
throw new Error('File not found.');
}
const writable = await handle.createWritable();
await writable.write(content);
await writable.close();
}
watch(resource: URI, opts: IWatchOptions): IDisposable {
return Disposable.None;
}
async stat(resource: URI): Promise<IStat> {
const rootUUID = getRootUUID(resource);
if (rootUUID) {
const fileHandle = this.files.get(rootUUID);
if (fileHandle) {
const file = await fileHandle.getFile();
return {
type: FileType.File,
mtime: file.lastModified,
ctime: 0,
size: file.size
};
}
const directoryHandle = this.directories.get(rootUUID);
if (directoryHandle) {
return {
type: FileType.Directory,
mtime: 0,
ctime: 0,
size: 0
};
}
}
const parent = await this.getParentDirectoryHandle(resource);
if (!parent) {
throw new Error('Stat error: no parent found');
}
const name = extUri.basename(resource);
for await (const [childName, child] of parent) {
if (childName === name) {
if (child.kind === 'file') {
const file = await child.getFile();
return {
type: FileType.File,
mtime: file.lastModified,
ctime: 0,
size: file.size
};
} else {
return {
type: FileType.Directory,
mtime: 0,
ctime: 0,
size: 0
};
}
}
}
throw new Error('Stat error: entry not found');
}
mkdir(resource: URI): Promise<void> {
throw new Error('Method not implemented.');
}
async readdir(resource: URI): Promise<[string, FileType][]> {
const parent = await this.getDirectoryHandle(resource);
if (!parent) {
throw new Error('Stat error: no parent found');
}
const result: [string, FileType][] = [];
for await (const [name, child] of parent) {
result.push([name, child.kind === 'file' ? FileType.File : FileType.Directory]);
}
return result;
}
delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
throw new Error('Method not implemented: delete');
}
rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
throw new Error('Method not implemented: rename');
}
private async getDirectoryHandle(uri: URI): Promise<FileSystemDirectoryHandle | undefined> {
const rootUUID = getRootUUID(uri);
if (rootUUID) {
return this.directories.get(rootUUID);
}
const splitResult = split(uri.path);
if (!splitResult) {
return undefined;
}
const parent = await this.getDirectoryHandle(URI.from({ ...uri, path: splitResult[0] }));
return await parent?.getDirectoryHandle(extUri.basename(uri));
}
private async getParentDirectoryHandle(uri: URI): Promise<FileSystemDirectoryHandle | undefined> {
return this.getDirectoryHandle(URI.from({ ...uri, path: extUri.dirname(uri).path }));
}
private async getFileHandle(uri: URI): Promise<FileSystemFileHandle | undefined> {
const rootUUID = getRootUUID(uri);
if (rootUUID) {
return this.files.get(rootUUID);
}
const parent = await this.getParentDirectoryHandle(uri);
const name = extUri.basename(uri);
return await parent?.getFileHandle(name);
}
registerFileHandle(uuid: string, handle: FileSystemFileHandle): void {
this.files.set(uuid, handle);
}
registerDirectoryHandle(uuid: string, handle: FileSystemDirectoryHandle): void {
this.directories.set(uuid, handle);
}
dispose(): void {
this._onDidChangeFile.dispose();
}
}

View File

@@ -71,6 +71,10 @@ export class FileService extends Disposable implements IFileService {
});
}
getProvider(scheme: string): IFileSystemProvider | undefined {
return this.provider.get(scheme);
}
async activateProvider(scheme: string): Promise<void> {
// Emit an event that we are about to activate a provider with the given scheme.
@@ -1000,7 +1004,7 @@ export class FileService extends Disposable implements IFileService {
].join();
}
dispose(): void {
override dispose(): void {
super.dispose();
this.activeWatchers.forEach(watcher => dispose(watcher.disposable));

View File

@@ -46,6 +46,11 @@ export interface IFileService {
*/
registerProvider(scheme: string, provider: IFileSystemProvider): IDisposable;
/**
* Returns a file system provider for a certain scheme.
*/
getProvider(scheme: string): IFileSystemProvider | undefined;
/**
* Tries to activate a provider with the given scheme.
*/
@@ -199,7 +204,7 @@ export interface FileOverwriteOptions {
* Set to `true` to overwrite a file if it exists. Will
* throw an error otherwise if the file does exist.
*/
overwrite: boolean;
readonly overwrite: boolean;
}
export interface FileUnlockOptions {
@@ -209,7 +214,7 @@ export interface FileUnlockOptions {
* have. A file that is write locked will throw an error for any
* attempt to write to unless `unlock: true` is provided.
*/
unlock: boolean;
readonly unlock: boolean;
}
export interface FileReadStreamOptions {
@@ -241,7 +246,7 @@ export interface FileWriteOptions extends FileOverwriteOptions, FileUnlockOption
* Set to `true` to create a file when it does not exist. Will
* throw an error otherwise if the file does not exist.
*/
create: boolean;
readonly create: boolean;
}
export type FileOpenOptions = FileOpenForReadOptions | FileOpenForWriteOptions;
@@ -255,7 +260,7 @@ export interface FileOpenForReadOptions {
/**
* A hint that the file should be opened for reading only.
*/
create: false;
readonly create: false;
}
export interface FileOpenForWriteOptions extends FileUnlockOptions {
@@ -263,7 +268,7 @@ export interface FileOpenForWriteOptions extends FileUnlockOptions {
/**
* A hint that the file should be opened for reading and writing.
*/
create: true;
readonly create: true;
}
export interface FileDeleteOptions {
@@ -273,14 +278,14 @@ export interface FileDeleteOptions {
* only applies to folders and can lead to an error unless provided
* if the folder is not empty.
*/
recursive: boolean;
readonly recursive: boolean;
/**
* Set to `true` to attempt to move the file to trash
* instead of deleting it permanently from disk. This
* option maybe not be supported on all providers.
*/
useTrash: boolean;
readonly useTrash: boolean;
}
export enum FileType {
@@ -315,17 +320,17 @@ export interface IStat {
/**
* The file type.
*/
type: FileType;
readonly type: FileType;
/**
* The last modification date represented as millis from unix epoch.
*/
mtime: number;
readonly mtime: number;
/**
* The creation date represented as millis from unix epoch.
*/
ctime: number;
readonly ctime: number;
/**
* The size of the file in bytes.
@@ -339,7 +344,7 @@ export interface IWatchOptions {
* Set to `true` to watch for changes recursively in a folder
* and all of its children.
*/
recursive: boolean;
readonly recursive: boolean;
/**
* A set of paths to exclude from watching.
@@ -561,18 +566,18 @@ export function toFileOperationResult(error: Error): FileOperationResult {
}
export interface IFileSystemProviderRegistrationEvent {
added: boolean;
scheme: string;
provider?: IFileSystemProvider;
readonly added: boolean;
readonly scheme: string;
readonly provider?: IFileSystemProvider;
}
export interface IFileSystemProviderCapabilitiesChangeEvent {
provider: IFileSystemProvider;
scheme: string;
readonly provider: IFileSystemProvider;
readonly scheme: string;
}
export interface IFileSystemProviderActivationEvent {
scheme: string;
readonly scheme: string;
join(promise: Promise<void>): void;
}
@@ -823,13 +828,13 @@ interface IBaseStat {
/**
* The unified resource identifier of this file or folder.
*/
resource: URI;
readonly resource: URI;
/**
* The name which is the last segment
* of the {{path}}.
*/
name: string;
readonly name: string;
/**
* The size of the file.
@@ -837,7 +842,7 @@ interface IBaseStat {
* The value may or may not be resolved as
* it is optional.
*/
size?: number;
readonly size?: number;
/**
* The last modification date represented as millis from unix epoch.
@@ -845,7 +850,7 @@ interface IBaseStat {
* The value may or may not be resolved as
* it is optional.
*/
mtime?: number;
readonly mtime?: number;
/**
* The creation date represented as millis from unix epoch.
@@ -853,7 +858,7 @@ interface IBaseStat {
* The value may or may not be resolved as
* it is optional.
*/
ctime?: number;
readonly ctime?: number;
/**
* A unique identifier thet represents the
@@ -862,7 +867,7 @@ interface IBaseStat {
* The value may or may not be resolved as
* it is optional.
*/
etag?: string;
readonly etag?: string;
}
export interface IBaseStatWithMetadata extends Required<IBaseStat> { }
@@ -875,12 +880,12 @@ export interface IFileStat extends IBaseStat {
/**
* The resource is a file.
*/
isFile: boolean;
readonly isFile: boolean;
/**
* The resource is a directory.
*/
isDirectory: boolean;
readonly isDirectory: boolean;
/**
* The resource is a symbolic link. Note: even when the
@@ -888,7 +893,7 @@ export interface IFileStat extends IBaseStat {
* and `FileType.Directory` to know the type of the target
* the link points to.
*/
isSymbolicLink: boolean;
readonly isSymbolicLink: boolean;
/**
* The children of the file stat or undefined if none.
@@ -897,20 +902,20 @@ export interface IFileStat extends IBaseStat {
}
export interface IFileStatWithMetadata extends IFileStat, IBaseStatWithMetadata {
mtime: number;
ctime: number;
etag: string;
size: number;
children?: IFileStatWithMetadata[];
readonly mtime: number;
readonly ctime: number;
readonly etag: string;
readonly size: number;
readonly children?: IFileStatWithMetadata[];
}
export interface IResolveFileResult {
stat?: IFileStat;
success: boolean;
readonly stat?: IFileStat;
readonly success: boolean;
}
export interface IResolveFileResultWithMetadata extends IResolveFileResult {
stat?: IFileStatWithMetadata;
readonly stat?: IFileStatWithMetadata;
}
export interface IFileContent extends IBaseStatWithMetadata {
@@ -918,7 +923,7 @@ export interface IFileContent extends IBaseStatWithMetadata {
/**
* The content of a file as buffer.
*/
value: VSBuffer;
readonly value: VSBuffer;
}
export interface IFileStreamContent extends IBaseStatWithMetadata {
@@ -926,7 +931,7 @@ export interface IFileStreamContent extends IBaseStatWithMetadata {
/**
* The content of a file as stream.
*/
value: VSBufferReadableStream;
readonly value: VSBufferReadableStream;
}
export interface IBaseReadFileOptions extends FileReadStreamOptions {
@@ -1126,6 +1131,7 @@ export const FALLBACK_MAX_MEMORY_SIZE_MB = 4096;
* Helper to format a raw byte size into a human readable label.
*/
export class ByteSize {
static readonly KB = 1024;
static readonly MB = ByteSize.KB * ByteSize.KB;
static readonly GB = ByteSize.MB * ByteSize.KB;
@@ -1159,8 +1165,8 @@ export class ByteSize {
// Native only: Arch limits
export interface IArchLimits {
maxFileSize: number;
maxHeapSize: number;
readonly maxFileSize: number;
readonly maxHeapSize: number;
}
export const enum Arch {

View File

@@ -10,6 +10,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { IFileSystemProviderWithOpenReadWriteCloseCapability, FileReadStreamOptions, createFileSystemProviderError, FileSystemProviderErrorCode, ensureFileSystemProviderError } from 'vs/platform/files/common/files';
import { canceled } from 'vs/base/common/errors';
import { IErrorTransformer, IDataTransformer, WriteableStream } from 'vs/base/common/stream';
import product from 'vs/platform/product/common/product';
export interface ICreateReadStreamOptions extends FileReadStreamOptions {
@@ -127,7 +128,7 @@ function throwIfTooLarge(totalBytesRead: number, options: ICreateReadStreamOptio
// Return early if file is too large to load and we have configured limits
if (options?.limits) {
if (typeof options.limits.memory === 'number' && totalBytesRead > options.limits.memory) {
throw createFileSystemProviderError(localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart and allow it to use more memory"), FileSystemProviderErrorCode.FileExceedsMemoryLimit);
throw createFileSystemProviderError(localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart and allow {0} to use more memory", product.nameShort), FileSystemProviderErrorCode.FileExceedsMemoryLimit);
}
if (typeof options.limits.size === 'number' && totalBytesRead > options.limits.size) {

View File

@@ -21,7 +21,7 @@ export class DiskFileSystemProvider extends NodeDiskFileSystemProvider {
super(logService, options);
}
get capabilities(): FileSystemProviderCapabilities {
override get capabilities(): FileSystemProviderCapabilities {
if (!this._capabilities) {
this._capabilities = super.capabilities | FileSystemProviderCapabilities.Trash;
}
@@ -29,7 +29,7 @@ export class DiskFileSystemProvider extends NodeDiskFileSystemProvider {
return this._capabilities;
}
protected async doDelete(filePath: string, opts: FileDeleteOptions): Promise<void> {
protected override async doDelete(filePath: string, opts: FileDeleteOptions): Promise<void> {
if (!opts.useTrash) {
return super.doDelete(filePath, opts);
}

View File

@@ -730,7 +730,7 @@ export class DiskFileSystemProvider extends Disposable implements
//#endregion
dispose(): void {
override dispose(): void {
super.dispose();
dispose(this.recursiveWatcher);

View File

@@ -124,7 +124,7 @@ export class FileWatcher extends Disposable {
}
}
dispose(): void {
override dispose(): void {
this.isDisposed = true;
super.dispose();

View File

@@ -91,7 +91,7 @@ export class FileWatcher extends Disposable {
}
}
dispose(): void {
override dispose(): void {
this.isDisposed = true;
super.dispose();

View File

@@ -92,7 +92,7 @@ export class FileWatcher extends Disposable {
}
}
dispose(): void {
override dispose(): void {
this.isDisposed = true;
super.dispose();

View File

@@ -22,6 +22,7 @@ suite('File Service', () => {
const provider = new NullFileSystemProvider();
assert.strictEqual(service.canHandleResource(resource), false);
assert.strictEqual(service.getProvider(resource.scheme), undefined);
const registrations: IFileSystemProviderRegistrationEvent[] = [];
service.onDidChangeFileSystemProviderRegistrations(e => {
@@ -33,7 +34,7 @@ suite('File Service', () => {
capabilityChanges.push(e);
});
let registrationDisposable: IDisposable | undefined = undefined;
let registrationDisposable: IDisposable | undefined;
let callCount = 0;
service.onWillActivateFileSystemProvider(e => {
callCount++;
@@ -50,6 +51,7 @@ suite('File Service', () => {
await service.activateProvider('test');
assert.strictEqual(service.canHandleResource(resource), true);
assert.strictEqual(service.getProvider(resource.scheme), provider);
assert.strictEqual(registrations.length, 1);
assert.strictEqual(registrations[0].scheme, 'test');
@@ -141,7 +143,7 @@ suite('File Service', () => {
const service = new FileService(new NullLogService());
const provider = new class extends NullFileSystemProvider {
async stat(resource: URI): Promise<IStat> {
override async stat(resource: URI): Promise<IStat> {
return {
mtime: Date.now(),
ctime: Date.now(),
@@ -150,7 +152,7 @@ suite('File Service', () => {
};
}
readFile(resource: URI): Promise<Uint8Array> {
override readFile(resource: URI): Promise<Uint8Array> {
if (async) {
return timeout(5).then(() => { throw new Error('failed'); });
}
@@ -158,7 +160,7 @@ suite('File Service', () => {
throw new Error('failed');
}
open(resource: URI, opts: FileOpenOptions): Promise<number> {
override open(resource: URI, opts: FileOpenOptions): Promise<number> {
if (async) {
return timeout(5).then(() => { throw new Error('failed'); });
}

View File

@@ -58,7 +58,7 @@ export class TestDiskFileSystemProvider extends DiskFileSystemProvider {
private smallStatSize: boolean = false;
private _testCapabilities!: FileSystemProviderCapabilities;
get capabilities(): FileSystemProviderCapabilities {
override get capabilities(): FileSystemProviderCapabilities {
if (!this._testCapabilities) {
this._testCapabilities =
FileSystemProviderCapabilities.FileReadWrite |
@@ -76,7 +76,7 @@ export class TestDiskFileSystemProvider extends DiskFileSystemProvider {
return this._testCapabilities;
}
set capabilities(capabilities: FileSystemProviderCapabilities) {
override set capabilities(capabilities: FileSystemProviderCapabilities) {
this._testCapabilities = capabilities;
}
@@ -88,7 +88,7 @@ export class TestDiskFileSystemProvider extends DiskFileSystemProvider {
this.smallStatSize = enabled;
}
async stat(resource: URI): Promise<IStat> {
override async stat(resource: URI): Promise<IStat> {
const res = await super.stat(resource);
if (this.invalidStatSize) {
@@ -100,7 +100,7 @@ export class TestDiskFileSystemProvider extends DiskFileSystemProvider {
return res;
}
async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
override async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
const bytesRead = await super.read(fd, pos, data, offset, length);
this.totalBytesRead += bytesRead;
@@ -108,7 +108,7 @@ export class TestDiskFileSystemProvider extends DiskFileSystemProvider {
return bytesRead;
}
async readFile(resource: URI): Promise<Uint8Array> {
override async readFile(resource: URI): Promise<Uint8Array> {
const res = await super.readFile(resource);
this.totalBytesRead += res.byteLength;

View File

@@ -77,4 +77,34 @@ export class Graph<T> {
}
return data.join('\n');
}
/**
* This is brute force and slow and **only** be used
* to trouble shoot.
*/
findCycleSlow() {
for (let [id, node] of this._nodes) {
const seen = new Set<string>([id]);
const res = this._findCycle(node, seen);
if (res) {
return res;
}
}
return undefined;
}
private _findCycle(node: Node<T>, seen: Set<string>): string | undefined {
for (let [id, outgoing] of node.outgoing) {
if (seen.has(id)) {
return [...seen, id].join(' -> ');
}
seen.add(id);
const value = this._findCycle(outgoing, seen);
if (value) {
return value;
}
seen.delete(id);
}
return undefined;
}
}

View File

@@ -16,7 +16,7 @@ const _enableTracing = false;
class CyclicDependencyError extends Error {
constructor(graph: Graph<any>) {
super('cyclic dependency between services');
this.message = graph.toString();
this.message = graph.findCycleSlow() ?? `UNABLE to detect cycle, dumping graph: \n${graph.toString()}`;
}
}
@@ -267,8 +267,8 @@ class Trace {
private static readonly _None = new class extends Trace {
constructor() { super(-1, null); }
stop() { }
branch() { return this; }
override stop() { }
override branch() { return this; }
};
static traceInvocation(ctor: any): Trace {

View File

@@ -13,34 +13,34 @@ suite('Graph', () => {
});
test('is possible to lookup nodes that don\'t exist', function () {
assert.deepEqual(graph.lookup('ddd'), null);
assert.strictEqual(graph.lookup('ddd'), undefined);
});
test('inserts nodes when not there yet', function () {
assert.deepEqual(graph.lookup('ddd'), null);
assert.deepEqual(graph.lookupOrInsertNode('ddd').data, 'ddd');
assert.deepEqual(graph.lookup('ddd')!.data, 'ddd');
assert.strictEqual(graph.lookup('ddd'), undefined);
assert.strictEqual(graph.lookupOrInsertNode('ddd').data, 'ddd');
assert.strictEqual(graph.lookup('ddd')!.data, 'ddd');
});
test('can remove nodes and get length', function () {
assert.ok(graph.isEmpty());
assert.deepEqual(graph.lookup('ddd'), null);
assert.deepEqual(graph.lookupOrInsertNode('ddd').data, 'ddd');
assert.strictEqual(graph.lookup('ddd'), undefined);
assert.strictEqual(graph.lookupOrInsertNode('ddd').data, 'ddd');
assert.ok(!graph.isEmpty());
graph.removeNode('ddd');
assert.deepEqual(graph.lookup('ddd'), null);
assert.strictEqual(graph.lookup('ddd'), undefined);
assert.ok(graph.isEmpty());
});
test('root', () => {
graph.insertEdge('1', '2');
let roots = graph.roots();
assert.equal(roots.length, 1);
assert.equal(roots[0].data, '2');
assert.strictEqual(roots.length, 1);
assert.strictEqual(roots[0].data, '2');
graph.insertEdge('2', '1');
roots = graph.roots();
assert.equal(roots.length, 0);
assert.strictEqual(roots.length, 0);
});
test('root complex', function () {
@@ -49,7 +49,7 @@ suite('Graph', () => {
graph.insertEdge('3', '4');
let roots = graph.roots();
assert.equal(roots.length, 2);
assert.strictEqual(roots.length, 2);
assert(['2', '4'].every(n => roots.some(node => node.data === n)));
});
});

View File

@@ -55,7 +55,7 @@ interface IDependentService {
class DependentService implements IDependentService {
declare readonly _serviceBrand: undefined;
constructor(@IService1 service: IService1) {
assert.equal(service.c, 1);
assert.strictEqual(service.c, 1);
}
name = 'farboo';
@@ -65,7 +65,7 @@ class Service1Consumer {
constructor(@IService1 service1: IService1) {
assert.ok(service1);
assert.equal(service1.c, 1);
assert.strictEqual(service1.c, 1);
}
}
@@ -81,7 +81,7 @@ class TargetWithStaticParam {
constructor(v: boolean, @IService1 service1: IService1) {
assert.ok(v);
assert.ok(service1);
assert.equal(service1.c, 1);
assert.strictEqual(service1.c, 1);
}
}
@@ -93,7 +93,7 @@ class TargetNotOptional {
class TargetOptional {
constructor(@IService1 service1: IService1, @optional(IService2) service2: IService2) {
assert.ok(service1);
assert.equal(service1.c, 1);
assert.strictEqual(service1.c, 1);
assert.ok(service2 === undefined);
}
}
@@ -101,16 +101,16 @@ class TargetOptional {
class DependentServiceTarget {
constructor(@IDependentService d: IDependentService) {
assert.ok(d);
assert.equal(d.name, 'farboo');
assert.strictEqual(d.name, 'farboo');
}
}
class DependentServiceTarget2 {
constructor(@IDependentService d: IDependentService, @IService1 s: IService1) {
assert.ok(d);
assert.equal(d.name, 'farboo');
assert.strictEqual(d.name, 'farboo');
assert.ok(s);
assert.equal(s.c, 1);
assert.strictEqual(s.c, 1);
}
}
@@ -138,9 +138,9 @@ suite('Instantiation Service', () => {
test('service collection, cannot overwrite', function () {
let collection = new ServiceCollection();
let result = collection.set(IService1, null!);
assert.equal(result, undefined);
assert.strictEqual(result, undefined);
result = collection.set(IService1, new Service1());
assert.equal(result, null);
assert.strictEqual(result, null);
});
test('service collection, add/has', function () {
@@ -237,7 +237,7 @@ suite('Instantiation Service', () => {
let service1 = accessor.get(IService1);
assert.ok(service1);
assert.equal(service1.c, 1);
assert.strictEqual(service1.c, 1);
let service2 = accessor.get(IService1);
assert.ok(service1 === service2);
@@ -253,7 +253,7 @@ suite('Instantiation Service', () => {
service.invokeFunction(accessor => {
let d = accessor.get(IDependentService);
assert.ok(d);
assert.equal(d.name, 'farboo');
assert.strictEqual(d.name, 'farboo');
});
});
@@ -305,12 +305,12 @@ suite('Instantiation Service', () => {
function test(accessor: ServicesAccessor) {
assert.ok(accessor.get(IService1) instanceof Service1);
assert.equal(accessor.get(IService1).c, 1);
assert.strictEqual(accessor.get(IService1).c, 1);
return true;
}
assert.equal(service.invokeFunction(test), true);
assert.strictEqual(service.invokeFunction(test), true);
});
test('Invoke - get service, optional', function () {
@@ -320,10 +320,10 @@ suite('Instantiation Service', () => {
function test(accessor: ServicesAccessor) {
assert.ok(accessor.get(IService1) instanceof Service1);
assert.throws(() => accessor.get(IService2));
assert.equal(accessor.get(IService2, optional), undefined);
assert.strictEqual(accessor.get(IService2, optional), undefined);
return true;
}
assert.equal(service.invokeFunction(test), true);
assert.strictEqual(service.invokeFunction(test), true);
});
test('Invoke - keeping accessor NOT allowed', function () {
@@ -336,12 +336,12 @@ suite('Instantiation Service', () => {
function test(accessor: ServicesAccessor) {
assert.ok(accessor.get(IService1) instanceof Service1);
assert.equal(accessor.get(IService1).c, 1);
assert.strictEqual(accessor.get(IService1).c, 1);
cached = accessor;
return true;
}
assert.equal(service.invokeFunction(test), true);
assert.strictEqual(service.invokeFunction(test), true);
assert.throws(() => cached.get(IService2));
});
@@ -379,7 +379,7 @@ suite('Instantiation Service', () => {
let child = service.createChild(new ServiceCollection([IService2, new Service2()]));
child.createInstance(Service1Consumer);
assert.equal(serviceInstanceCount, 1);
assert.strictEqual(serviceInstanceCount, 1);
// creating the service instance AFTER the child service
serviceInstanceCount = 0;
@@ -390,7 +390,7 @@ suite('Instantiation Service', () => {
service.createInstance(Service1Consumer);
child.createInstance(Service1Consumer);
assert.equal(serviceInstanceCount, 1);
assert.strictEqual(serviceInstanceCount, 1);
});
test('Remote window / integration tests is broken #105562', function () {

View File

@@ -3,6 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes';
// Since data sent through the service is serialized to JSON, functions will be lost, so Color objects
// should not be sent as their 'toString' method will be stripped. Instead convert to strings before sending.
export interface WindowStyles {
@@ -67,9 +69,6 @@ export interface ISettingSearchResult {
score: number;
}
export interface IssueReporterFeatures {
}
export interface ProcessExplorerStyles extends WindowStyles {
hoverBackground?: string;
hoverForeground?: string;
@@ -88,3 +87,17 @@ export interface ICommonIssueService {
openProcessExplorer(data: ProcessExplorerData): Promise<void>;
getSystemStatus(): Promise<string>;
}
export interface IssueReporterWindowConfiguration extends ISandboxConfiguration {
disableExtensions: boolean;
data: IssueReporterData;
os: {
type: string;
arch: string;
release: string;
}
}
export interface ProcessExplorerWindowConfiguration extends ISandboxConfiguration {
data: ProcessExplorerData;
}

View File

@@ -4,15 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import * as os from 'os';
import { IProductService } from 'vs/platform/product/common/productService';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { ICommonIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/common/issue';
import { arch, release, type } from 'os';
import product from 'vs/platform/product/common/product';
import { ICommonIssueService, IssueReporterWindowConfiguration, IssueReporterData, ProcessExplorerData, ProcessExplorerWindowConfiguration } from 'vs/platform/issue/common/issue';
import { BrowserWindow, ipcMain, screen, IpcMainEvent, Display } from 'electron';
import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService';
import { IDiagnosticsService, PerformanceInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform';
import { isMacintosh, IProcessEnvironment, browserCodeLoadingCacheStrategy } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
import { IWindowState } from 'vs/platform/windows/electron-main/windows';
import { listProcesses } from 'vs/base/node/ps';
@@ -21,22 +20,26 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { zoomLevelToZoomFactor } from 'vs/platform/windows/common/windows';
import { FileAccess } from 'vs/base/common/network';
import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService';
const DEFAULT_BACKGROUND_COLOR = '#1E1E1E';
import { IIPCObjectUrl, IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol';
import { DisposableStore } from 'vs/base/common/lifecycle';
export const IIssueMainService = createDecorator<IIssueMainService>('issueMainService');
export interface IIssueMainService extends ICommonIssueService { }
export class IssueMainService implements ICommonIssueService {
declare readonly _serviceBrand: undefined;
_issueWindow: BrowserWindow | null = null;
_issueParentWindow: BrowserWindow | null = null;
_processExplorerWindow: BrowserWindow | null = null;
_processExplorerParentWindow: BrowserWindow | null = null;
private static readonly DEFAULT_BACKGROUND_COLOR = '#1E1E1E';
private issueReporterWindow: BrowserWindow | null = null;
private issueReporterParentWindow: BrowserWindow | null = null;
private processExplorerWindow: BrowserWindow | null = null;
private processExplorerParentWindow: BrowserWindow | null = null;
constructor(
private machineId: string,
private userEnv: IProcessEnvironment,
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
@ILaunchMainService private readonly launchMainService: ILaunchMainService,
@@ -44,44 +47,42 @@ export class IssueMainService implements ICommonIssueService {
@IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService,
@IDialogMainService private readonly dialogMainService: IDialogMainService,
@INativeHostMainService private readonly nativeHostMainService: INativeHostMainService,
@IProductService private readonly productService: IProductService
@IProtocolMainService private readonly protocolMainService: IProtocolMainService
) {
this.registerListeners();
}
private registerListeners(): void {
ipcMain.on('vscode:issueSystemInfoRequest', async (event: 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 => {
this.safeSend(event, 'vscode:issueSystemInfoResponse', msg);
});
});
ipcMain.on('vscode:issueSystemInfoRequest', async event => {
const [info, remoteData] = await Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]);
const msg = await this.diagnosticsService.getSystemInfo(info, remoteData);
this.safeSend(event, 'vscode:issueSystemInfoResponse', msg);
});
ipcMain.on('vscode:listProcesses', async (event: IpcMainEvent) => {
ipcMain.on('vscode:listProcesses', async event => {
const processes = [];
try {
const mainPid = await this.launchMainService.getMainProcessId();
processes.push({ name: localize('local', "Local"), rootProcess: await listProcesses(mainPid) });
(await this.launchMainService.getRemoteDiagnostics({ includeProcesses: true }))
.forEach(data => {
if (isRemoteDiagnosticError(data)) {
const remoteDiagnostics = await this.launchMainService.getRemoteDiagnostics({ includeProcesses: true });
remoteDiagnostics.forEach(data => {
if (isRemoteDiagnosticError(data)) {
processes.push({
name: data.hostName,
rootProcess: data
});
} else {
if (data.processes) {
processes.push({
name: data.hostName,
rootProcess: data
rootProcess: data.processes
});
} else {
if (data.processes) {
processes.push({
name: data.hostName,
rootProcess: data.processes
});
}
}
});
}
});
} catch (e) {
this.logService.error(`Listing processes failed: ${e}`);
}
@@ -89,7 +90,7 @@ export class IssueMainService implements ICommonIssueService {
this.safeSend(event, 'vscode:listProcessesResponse', processes);
});
ipcMain.on('vscode:issueReporterClipboard', (event: IpcMainEvent) => {
ipcMain.on('vscode:issueReporterClipboard', async event => {
const messageOptions = {
message: localize('issueReporterWriteToClipboard', "There is too much data to send to GitHub directly. The data will be copied to the clipboard, please paste it into the GitHub issue page that is opened."),
type: 'warning',
@@ -99,21 +100,18 @@ export class IssueMainService implements ICommonIssueService {
]
};
if (this._issueWindow) {
this.dialogMainService.showMessageBox(messageOptions, this._issueWindow)
.then(result => {
this.safeSend(event, 'vscode:issueReporterClipboardResponse', result.response === 0);
});
if (this.issueReporterWindow) {
const result = await this.dialogMainService.showMessageBox(messageOptions, this.issueReporterWindow);
this.safeSend(event, 'vscode:issueReporterClipboardResponse', result.response === 0);
}
});
ipcMain.on('vscode:issuePerformanceInfoRequest', (event: IpcMainEvent) => {
this.getPerformanceInfo().then(msg => {
this.safeSend(event, 'vscode:issuePerformanceInfoResponse', msg);
});
ipcMain.on('vscode:issuePerformanceInfoRequest', async event => {
const performanceInfo = await this.getPerformanceInfo();
this.safeSend(event, 'vscode:issuePerformanceInfoResponse', performanceInfo);
});
ipcMain.on('vscode:issueReporterConfirmClose', () => {
ipcMain.on('vscode:issueReporterConfirmClose', async () => {
const messageOptions = {
message: localize('confirmCloseIssueReporter', "Your input will not be saved. Are you sure you want to close this window?"),
type: 'warning',
@@ -123,16 +121,14 @@ export class IssueMainService implements ICommonIssueService {
]
};
if (this._issueWindow) {
this.dialogMainService.showMessageBox(messageOptions, this._issueWindow)
.then(result => {
if (result.response === 0) {
if (this._issueWindow) {
this._issueWindow.destroy();
this._issueWindow = null;
}
}
});
if (this.issueReporterWindow) {
const result = await this.dialogMainService.showMessageBox(messageOptions, this.issueReporterWindow);
if (result.response === 0) {
if (this.issueReporterWindow) {
this.issueReporterWindow.destroy();
this.issueReporterWindow = null;
}
}
}
});
@@ -142,10 +138,10 @@ export class IssueMainService implements ICommonIssueService {
let parentWindow: BrowserWindow | null;
switch (from) {
case 'issueReporter':
parentWindow = this._issueParentWindow;
parentWindow = this.issueReporterParentWindow;
break;
case 'processExplorer':
parentWindow = this._processExplorerParentWindow;
parentWindow = this.processExplorerParentWindow;
break;
default:
throw new Error(`Unexpected command source: ${from}`);
@@ -160,22 +156,21 @@ export class IssueMainService implements ICommonIssueService {
this.nativeHostMainService.openExternal(undefined, arg);
});
ipcMain.on('vscode:closeIssueReporter', (event: IpcMainEvent) => {
if (this._issueWindow) {
this._issueWindow.close();
ipcMain.on('vscode:closeIssueReporter', event => {
if (this.issueReporterWindow) {
this.issueReporterWindow.close();
}
});
ipcMain.on('vscode:closeProcessExplorer', (event: IpcMainEvent) => {
if (this._processExplorerWindow) {
this._processExplorerWindow.close();
ipcMain.on('vscode:closeProcessExplorer', event => {
if (this.processExplorerWindow) {
this.processExplorerWindow.close();
}
});
ipcMain.on('vscode:windowsInfoRequest', (event: IpcMainEvent) => {
this.launchMainService.getMainProcessInfo().then(info => {
this.safeSend(event, 'vscode:windowsInfoResponse', info.windows);
});
ipcMain.on('vscode:windowsInfoRequest', async event => {
const mainProcessInfo = await this.launchMainService.getMainProcessInfo();
this.safeSend(event, 'vscode:windowsInfoResponse', mainProcessInfo.windows);
});
}
@@ -186,128 +181,138 @@ export class IssueMainService implements ICommonIssueService {
}
async openReporter(data: IssueReporterData): Promise<void> {
if (!this._issueWindow) {
this._issueParentWindow = BrowserWindow.getFocusedWindow();
if (this._issueParentWindow) {
const position = this.getWindowPosition(this._issueParentWindow, 700, 800);
if (!this.issueReporterWindow) {
this.issueReporterParentWindow = BrowserWindow.getFocusedWindow();
if (this.issueReporterParentWindow) {
const issueReporterDisposables = new DisposableStore();
this._issueWindow = new BrowserWindow({
fullscreen: false,
width: position.width,
height: position.height,
minWidth: 300,
minHeight: 200,
x: position.x,
y: position.y,
title: localize('issueReporter', "Issue Reporter"),
backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR,
webPreferences: {
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath,
v8CacheOptions: 'bypassHeatCheck',
enableWebSQL: false,
enableRemoteModule: false,
spellcheck: false,
nativeWindowOpen: true,
zoomFactor: zoomLevelToZoomFactor(data.zoomLevel),
sandbox: true,
contextIsolation: true
}
const issueReporterWindowConfigUrl = issueReporterDisposables.add(this.protocolMainService.createIPCObjectUrl<IssueReporterWindowConfiguration>());
const position = this.getWindowPosition(this.issueReporterParentWindow, 700, 800);
this.issueReporterWindow = this.createBrowserWindow(position, issueReporterWindowConfigUrl, data.styles.backgroundColor, localize('issueReporter', "Issue Reporter"), data.zoomLevel);
// Store into config object URL
issueReporterWindowConfigUrl.update({
appRoot: this.environmentMainService.appRoot,
windowId: this.issueReporterWindow.id,
userEnv: this.userEnv,
data,
disableExtensions: !!this.environmentMainService.disableExtensions,
os: {
type: type(),
arch: arch(),
release: release(),
},
product
});
this._issueWindow.setMenuBarVisibility(false); // workaround for now, until a menu is implemented
this.issueReporterWindow.loadURL(
FileAccess.asBrowserUri('vs/code/electron-sandbox/issue/issueReporter.html', require, true).toString(true)
);
// Modified when testing UI
const features: IssueReporterFeatures = {};
this.issueReporterWindow.on('close', () => {
this.issueReporterWindow = null;
this.logService.trace('issueService#openReporter: opening issue reporter');
this._issueWindow.loadURL(this.getIssueReporterPath(data, features));
issueReporterDisposables.dispose();
});
this._issueWindow.on('close', () => this._issueWindow = null);
this.issueReporterParentWindow.on('closed', () => {
if (this.issueReporterWindow) {
this.issueReporterWindow.close();
this.issueReporterWindow = null;
this._issueParentWindow.on('closed', () => {
if (this._issueWindow) {
this._issueWindow.close();
this._issueWindow = null;
issueReporterDisposables.dispose();
}
});
}
}
if (this._issueWindow) {
this._issueWindow.focus();
}
this.issueReporterWindow?.focus();
}
async openProcessExplorer(data: ProcessExplorerData): Promise<void> {
// Create as singleton
if (!this._processExplorerWindow) {
this._processExplorerParentWindow = BrowserWindow.getFocusedWindow();
if (this._processExplorerParentWindow) {
const position = this.getWindowPosition(this._processExplorerParentWindow, 800, 500);
this._processExplorerWindow = new BrowserWindow({
skipTaskbar: true,
resizable: true,
fullscreen: false,
width: position.width,
height: position.height,
minWidth: 300,
minHeight: 200,
x: position.x,
y: position.y,
backgroundColor: data.styles.backgroundColor,
title: localize('processExplorer', "Process Explorer"),
webPreferences: {
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath,
v8CacheOptions: 'bypassHeatCheck',
enableWebSQL: false,
enableRemoteModule: false,
spellcheck: false,
nativeWindowOpen: true,
zoomFactor: zoomLevelToZoomFactor(data.zoomLevel),
sandbox: true,
contextIsolation: true
}
if (!this.processExplorerWindow) {
this.processExplorerParentWindow = BrowserWindow.getFocusedWindow();
if (this.processExplorerParentWindow) {
const processExplorerDisposables = new DisposableStore();
const processExplorerWindowConfigUrl = processExplorerDisposables.add(this.protocolMainService.createIPCObjectUrl<ProcessExplorerWindowConfiguration>());
const position = this.getWindowPosition(this.processExplorerParentWindow, 800, 500);
this.processExplorerWindow = this.createBrowserWindow(position, processExplorerWindowConfigUrl, data.styles.backgroundColor, localize('processExplorer', "Process Explorer"), data.zoomLevel);
// Store into config object URL
processExplorerWindowConfigUrl.update({
appRoot: this.environmentMainService.appRoot,
windowId: this.processExplorerWindow.id,
userEnv: this.userEnv,
data,
product
});
this._processExplorerWindow.setMenuBarVisibility(false);
this.processExplorerWindow.loadURL(
FileAccess.asBrowserUri('vs/code/electron-sandbox/processExplorer/processExplorer.html', require, true).toString(true)
);
const windowConfiguration = {
appRoot: this.environmentMainService.appRoot,
windowId: this._processExplorerWindow.id,
userEnv: this.userEnv,
machineId: this.machineId,
data
};
this.processExplorerWindow.on('close', () => {
this.processExplorerWindow = null;
processExplorerDisposables.dispose();
});
this._processExplorerWindow.loadURL(
toWindowUrl('vs/code/electron-sandbox/processExplorer/processExplorer.html', windowConfiguration));
this.processExplorerParentWindow.on('close', () => {
if (this.processExplorerWindow) {
this.processExplorerWindow.close();
this.processExplorerWindow = null;
this._processExplorerWindow.on('close', () => this._processExplorerWindow = null);
this._processExplorerParentWindow.on('close', () => {
if (this._processExplorerWindow) {
this._processExplorerWindow.close();
this._processExplorerWindow = null;
processExplorerDisposables.dispose();
}
});
}
}
// Focus
if (this._processExplorerWindow) {
this._processExplorerWindow.focus();
}
this.processExplorerWindow?.focus();
}
public async getSystemStatus(): Promise<string> {
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);
});
private createBrowserWindow<T>(position: IWindowState, ipcObjectUrl: IIPCObjectUrl<T>, backgroundColor: string | undefined, title: string, zoomLevel: number): BrowserWindow {
const window = new BrowserWindow({
fullscreen: false,
skipTaskbar: true,
resizable: true,
width: position.width,
height: position.height,
minWidth: 300,
minHeight: 200,
x: position.x,
y: position.y,
title,
backgroundColor: backgroundColor || IssueMainService.DEFAULT_BACKGROUND_COLOR,
webPreferences: {
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath,
additionalArguments: [`--vscode-window-config=${ipcObjectUrl.resource.toString()}`, '--context-isolation' /* TODO@bpasero: Use process.contextIsolateed when 13-x-y is adopted (https://github.com/electron/electron/pull/28030) */],
v8CacheOptions: browserCodeLoadingCacheStrategy,
enableWebSQL: false,
enableRemoteModule: false,
spellcheck: false,
nativeWindowOpen: true,
zoomFactor: zoomLevelToZoomFactor(zoomLevel),
sandbox: true,
contextIsolation: true
}
});
window.setMenuBarVisibility(false);
return window;
}
async getSystemStatus(): Promise<string> {
const [info, remoteData] = await Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]);
return this.diagnosticsService.getDiagnostics(info, remoteData);
}
private getWindowPosition(parentWindow: BrowserWindow, defaultWidth: number, defaultHeight: number): IWindowState {
// We want the new window to open on the same display that the parent is in
let displayToUse: Display | undefined;
const displays = screen.getAllDisplays();
@@ -375,67 +380,14 @@ export class IssueMainService implements ICommonIssueService {
return state;
}
private getPerformanceInfo(): Promise<PerformanceInfo> {
return new Promise(async (resolve, reject) => {
Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true })])
.then(result => {
const [info, remoteData] = result;
this.diagnosticsService.getPerformanceInfo(info, remoteData)
.then(diagnosticInfo => {
resolve(diagnosticInfo);
})
.catch(err => {
this.logService.warn('issueService#getPerformanceInfo ', err.message);
reject(err);
});
});
});
}
private async getPerformanceInfo(): Promise<PerformanceInfo> {
try {
const [info, remoteData] = await Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true })]);
return await this.diagnosticsService.getPerformanceInfo(info, remoteData);
} catch (error) {
this.logService.warn('issueService#getPerformanceInfo ', error.message);
private getIssueReporterPath(data: IssueReporterData, features: IssueReporterFeatures): string {
if (!this._issueWindow) {
throw new Error('Issue window has been disposed');
}
const windowConfiguration = {
appRoot: this.environmentMainService.appRoot,
windowId: this._issueWindow.id,
machineId: this.machineId,
userEnv: this.userEnv,
data,
features,
disableExtensions: this.environmentMainService.disableExtensions,
os: {
type: os.type(),
arch: os.arch(),
release: os.release(),
},
product: {
nameShort: this.productService.nameShort,
version: !!this.productService.darwinUniversalAssetId ? `${this.productService.version} (Universal)` : this.productService.version,
commit: this.productService.commit,
date: this.productService.date,
reportIssueUrl: this.productService.reportIssueUrl,
reportMarketplaceIssueUrl: this.productService.reportMarketplaceIssueUrl
}
};
return toWindowUrl('vs/code/electron-sandbox/issue/issueReporter.html', windowConfiguration);
}
}
function toWindowUrl<T>(modulePathToHtml: string, windowConfiguration: T): string {
const environment = parseArgs(process.argv, OPTIONS);
const config = Object.assign(environment, windowConfiguration);
for (const keyValue of Object.keys(config)) {
const key = keyValue as keyof typeof config;
if (config[key] === undefined || config[key] === null || config[key] === '') {
delete config[key]; // only send over properties that have a true value
throw error;
}
}
return FileAccess
.asBrowserUri(modulePathToHtml, require, true)
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` })
.toString(true);
}

View File

@@ -61,7 +61,7 @@ export abstract class AbstractKeybindingService extends Disposable implements IK
this._logging = false;
}
public dispose(): void {
public override dispose(): void {
super.dispose();
}

View File

@@ -78,7 +78,7 @@ export class MockScopableContextKeyService extends MockContextKeyService {
/**
* Don't implement this for all tests since we rarely depend on this behavior and it isn't implemented fully
*/
public createScoped(domNote: HTMLElement): IContextKeyService {
public override createScoped(domNote: HTMLElement): IContextKeyService {
return new MockContextKeyService();
}
}

View File

@@ -298,7 +298,7 @@ export class WorkbenchList<T> extends List<T> {
this.disposables.add(this.navigator);
}
updateOptions(options: IWorkbenchListOptionsUpdate): void {
override updateOptions(options: IWorkbenchListOptionsUpdate): void {
super.updateOptions(options);
if (options.overrideStyles) {
@@ -315,7 +315,7 @@ export class WorkbenchList<T> extends List<T> {
return this._useAltAsMultipleSelectionModifier;
}
dispose(): void {
override dispose(): void {
this._styler?.dispose();
super.dispose();
}
@@ -410,7 +410,7 @@ export class WorkbenchPagedList<T> extends PagedList<T> {
this.disposables.add(this.navigator);
}
updateOptions(options: IWorkbenchListOptionsUpdate): void {
override updateOptions(options: IWorkbenchListOptionsUpdate): void {
super.updateOptions(options);
if (options.overrideStyles) {
@@ -427,7 +427,7 @@ export class WorkbenchPagedList<T> extends PagedList<T> {
return this._useAltAsMultipleSelectionModifier;
}
dispose(): void {
override dispose(): void {
this._styler?.dispose();
this.disposables.dispose();
super.dispose();
@@ -547,7 +547,7 @@ export class WorkbenchTable<TRow> extends Table<TRow> {
this.disposables.add(this.navigator);
}
updateOptions(options: IWorkbenchTableOptionsUpdate): void {
override updateOptions(options: IWorkbenchTableOptionsUpdate): void {
super.updateOptions(options);
if (options.overrideStyles) {
@@ -564,7 +564,7 @@ export class WorkbenchTable<TRow> extends Table<TRow> {
return this._useAltAsMultipleSelectionModifier;
}
dispose(): void {
override dispose(): void {
this._styler?.dispose();
this.disposables.dispose();
super.dispose();
@@ -669,6 +669,15 @@ abstract class ResourceNavigator<T> extends Disposable {
return;
}
// copied from AbstractTree
const target = browserEvent.target as HTMLElement;
const onTwistie = target.classList.contains('monaco-tl-twistie')
|| (target.classList.contains('monaco-icon-label') && target.classList.contains('folder-icon') && browserEvent.offsetX < 16);
if (onTwistie) {
return;
}
const preserveFocus = false;
const pinned = true;
const sideBySide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey);
@@ -698,11 +707,14 @@ abstract class ResourceNavigator<T> extends Disposable {
class ListResourceNavigator<T> extends ResourceNavigator<T> {
protected override readonly widget: List<T> | PagedList<T>;
constructor(
protected readonly widget: List<T> | PagedList<T>,
widget: List<T> | PagedList<T>,
options: IResourceNavigatorOptions
) {
super(widget, options);
this.widget = widget;
}
getSelectedElement(): T | undefined {
@@ -712,8 +724,10 @@ class ListResourceNavigator<T> extends ResourceNavigator<T> {
class TableResourceNavigator<TRow> extends ResourceNavigator<TRow> {
protected override readonly widget!: Table<TRow>;
constructor(
protected readonly widget: Table<TRow>,
widget: Table<TRow>,
options: IResourceNavigatorOptions
) {
super(widget, options);
@@ -726,8 +740,10 @@ class TableResourceNavigator<TRow> extends ResourceNavigator<TRow> {
class TreeResourceNavigator<T, TFilterData> extends ResourceNavigator<T> {
protected override readonly widget!: ObjectTree<T, TFilterData> | CompressibleObjectTree<T, TFilterData> | DataTree<any, T, TFilterData> | AsyncDataTree<any, T, TFilterData> | CompressibleAsyncDataTree<any, T, TFilterData>;
constructor(
protected readonly widget: ObjectTree<T, TFilterData> | CompressibleObjectTree<T, TFilterData> | DataTree<any, T, TFilterData> | AsyncDataTree<any, T, TFilterData> | CompressibleAsyncDataTree<any, T, TFilterData>,
widget: ObjectTree<T, TFilterData> | CompressibleObjectTree<T, TFilterData> | DataTree<any, T, TFilterData> | AsyncDataTree<any, T, TFilterData> | CompressibleAsyncDataTree<any, T, TFilterData>,
options: IResourceNavigatorOptions
) {
super(widget, options);
@@ -829,7 +845,7 @@ export class WorkbenchCompressibleObjectTree<T extends NonNullable<any>, TFilter
this.disposables.add(this.internals);
}
updateOptions(options: IWorkbenchCompressibleObjectTreeOptionsUpdate = {}): void {
override updateOptions(options: IWorkbenchCompressibleObjectTreeOptionsUpdate = {}): void {
super.updateOptions(options);
if (options.overrideStyles) {
@@ -875,7 +891,7 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
this.disposables.add(this.internals);
}
updateOptions(options: IWorkbenchDataTreeOptionsUpdate = {}): void {
override updateOptions(options: IWorkbenchDataTreeOptionsUpdate = {}): void {
super.updateOptions(options);
if (options.overrideStyles) {
@@ -921,7 +937,7 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
this.disposables.add(this.internals);
}
updateOptions(options: IWorkbenchAsyncDataTreeOptionsUpdate = {}): void {
override updateOptions(options: IWorkbenchAsyncDataTreeOptionsUpdate = {}): void {
super.updateOptions(options);
if (options.overrideStyles) {

View File

@@ -82,7 +82,7 @@ export class BufferLogService extends AbstractLogger implements ILogService {
this._log(LogLevel.Critical, message, ...args);
}
dispose(): void {
override dispose(): void {
if (this._logger) {
this._logger.dispose();
}

View File

@@ -3,13 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, LogLevel, AbstractLogger, ILoggerService, ILogger } from 'vs/platform/log/common/log';
import { ILogService, LogLevel, AbstractLogger, ILoggerService, ILogger, AbstractLoggerService } from 'vs/platform/log/common/log';
import { URI } from 'vs/base/common/uri';
import { ByteSize, FileOperationError, FileOperationResult, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files';
import { Queue } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { dirname, joinPath, basename } from 'vs/base/common/resources';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
@@ -159,34 +158,19 @@ export class FileLogger extends AbstractLogger implements ILogger {
}
}
export class FileLoggerService extends Disposable implements ILoggerService {
declare readonly _serviceBrand: undefined;
private readonly loggers = new Map<string, ILogger>();
export class FileLoggerService extends AbstractLoggerService implements ILoggerService {
constructor(
@ILogService private logService: ILogService,
@IInstantiationService private instantiationService: IInstantiationService,
@IFileService private fileService: IFileService,
@ILogService logService: ILogService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IFileService private readonly fileService: IFileService,
) {
super();
this._register(logService.onDidChangeLogLevel(level => this.loggers.forEach(logger => logger.setLevel(level))));
super(logService.getLevel(), logService.onDidChangeLogLevel);
}
createLogger(resource: URI): ILogger {
let logger = this.loggers.get(resource.toString());
if (!logger) {
logger = new BufferLogService(this.logService.getLevel());
this.loggers.set(resource.toString(), logger);
whenProviderRegistered(resource, this.fileService).then(() => (<BufferLogService>logger).logger = this.instantiationService.createInstance(FileLogger, basename(resource), resource, this.logService.getLevel()));
}
protected doCreateLogger(resource: URI, logLevel: LogLevel): ILogger {
const logger = new BufferLogService(logLevel);
whenProviderRegistered(resource, this.fileService).then(() => (<BufferLogService>logger).logger = this.instantiationService.createInstance(FileLogger, basename(resource), resource, logger.getLevel()));
return logger;
}
dispose(): void {
this.loggers.forEach(logger => logger.dispose());
this.loggers.clear();
super.dispose();
}
}

View File

@@ -250,7 +250,7 @@ export class ConsoleMainLogger extends AbstractLogger implements ILogger {
}
}
dispose(): void {
override dispose(): void {
// noop
}
@@ -303,7 +303,7 @@ export class ConsoleLogger extends AbstractLogger implements ILogger {
}
}
dispose(): void {
override dispose(): void {
// noop
}
@@ -363,7 +363,7 @@ export class AdapterLogger extends AbstractLogger implements ILogger {
return toErrorMessage(msg, this.getLevel() <= LogLevel.Trace);
}
dispose(): void {
override dispose(): void {
// noop
}
@@ -382,7 +382,7 @@ export class MultiplexLogService extends AbstractLogger implements ILogService {
}
}
setLevel(level: LogLevel): void {
override setLevel(level: LogLevel): void {
for (const logService of this.logServices) {
logService.setLevel(level);
}
@@ -431,7 +431,7 @@ export class MultiplexLogService extends AbstractLogger implements ILogService {
}
}
dispose(): void {
override dispose(): void {
for (const logService of this.logServices) {
logService.dispose();
}
@@ -487,6 +487,46 @@ export class LogService extends Disposable implements ILogService {
}
}
export abstract class AbstractLoggerService extends Disposable implements ILoggerService {
declare readonly _serviceBrand: undefined;
private readonly loggers = new Map<string, ILogger>();
private readonly logLevelChangeableLoggers: ILogger[] = [];
constructor(
private logLevel: LogLevel,
onDidChangeLogLevel: Event<LogLevel>,
) {
super();
this._register(onDidChangeLogLevel(logLevel => {
this.logLevel = logLevel;
this.logLevelChangeableLoggers.forEach(logger => logger.setLevel(logLevel));
}));
}
createLogger(resource: URI, options?: ILoggerOptions): ILogger {
let logger = this.loggers.get(resource.toString());
if (!logger) {
logger = this.doCreateLogger(resource, options?.always ? LogLevel.Trace : this.logLevel, options);
this.loggers.set(resource.toString(), logger);
if (!options?.always) {
this.logLevelChangeableLoggers.push(logger);
}
}
return logger;
}
override dispose(): void {
this.logLevelChangeableLoggers.splice(0, this.logLevelChangeableLoggers.length);
this.loggers.forEach(logger => logger.dispose());
this.loggers.clear();
super.dispose();
}
protected abstract doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger;
}
export class NullLogService implements ILogService {
declare readonly _serviceBrand: undefined;
readonly onDidChangeLogLevel: Event<LogLevel> = new Emitter<LogLevel>().event;

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { LogLevel, ILogService, LogService, ILoggerService, ILogger, AbstractMessageLogger, ILoggerOptions, AdapterLogger } from 'vs/platform/log/common/log';
import { LogLevel, ILogService, LogService, ILoggerService, ILogger, AbstractMessageLogger, ILoggerOptions, AdapterLogger, AbstractLoggerService } from 'vs/platform/log/common/log';
import { Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
@@ -113,11 +113,11 @@ export class LoggerChannel implements IServerChannel {
}
}
export class LoggerChannelClient implements ILoggerService {
export class LoggerChannelClient extends AbstractLoggerService implements ILoggerService {
declare readonly _serviceBrand: undefined;
constructor(private readonly channel: IChannel) { }
constructor(logLevel: LogLevel, onDidChangeLogLevel: Event<LogLevel>, private readonly channel: IChannel) {
super(logLevel, onDidChangeLogLevel);
}
createConsoleMainLogger(): ILogger {
return new AdapterLogger({
@@ -127,8 +127,8 @@ export class LoggerChannelClient implements ILoggerService {
});
}
createLogger(file: URI, options?: ILoggerOptions): ILogger {
return new Logger(this.channel, file, options);
protected doCreateLogger(file: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger {
return new Logger(this.channel, file, logLevel, options);
}
}
@@ -141,38 +141,40 @@ class Logger extends AbstractMessageLogger {
constructor(
private readonly channel: IChannel,
private readonly file: URI,
logLevel: LogLevel,
loggerOptions?: ILoggerOptions,
) {
super(loggerOptions?.always);
this.setLevel(logLevel);
this.channel.call('createLogger', [file, loggerOptions])
.then(() => {
this._log(this.buffer);
this.doLog(this.buffer);
this.isLoggerCreated = true;
});
}
protected log(level: LogLevel, message: string) {
this._log([[level, message]]);
}
private _log(messages: [LogLevel, string][]) {
const messages: [LogLevel, string][] = [[level, message]];
if (this.isLoggerCreated) {
this.channel.call('log', [this.file, messages]);
this.doLog(messages);
} else {
this.buffer.push(...messages);
}
}
private doLog(messages: [LogLevel, string][]) {
this.channel.call('log', [this.file, messages]);
}
}
export class FollowerLogService extends LogService implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(private parent: LogLevelChannelClient, logService: ILogService) {
super(logService);
this._register(parent.onDidChangeLogLevel(level => logService.setLevel(level)));
}
setLevel(level: LogLevel): void {
override setLevel(level: LogLevel): void {
super.setLevel(level);
this.parent.setLevel(level);

View File

@@ -3,8 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, ILoggerService, ILogger, ILoggerOptions, LogLevel } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService, ILoggerService, ILogger, ILoggerOptions, AbstractLoggerService, LogLevel } from 'vs/platform/log/common/log';
import { URI } from 'vs/base/common/uri';
import { basename } from 'vs/base/common/resources';
import { Schemas } from 'vs/base/common/network';
@@ -13,47 +12,25 @@ import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
import { IFileService } from 'vs/platform/files/common/files';
import { generateUuid } from 'vs/base/common/uuid';
export class LoggerService extends Disposable implements ILoggerService {
declare readonly _serviceBrand: undefined;
private readonly loggers = new Map<string, ILogger>();
private readonly logLevelChangeableLoggers: ILogger[] = [];
export class LoggerService extends AbstractLoggerService implements ILoggerService {
constructor(
@ILogService private logService: ILogService,
@IFileService private fileService: IFileService
@ILogService logService: ILogService,
@IFileService private readonly fileService: IFileService
) {
super();
this._register(logService.onDidChangeLogLevel(level => this.logLevelChangeableLoggers.forEach(logger => logger.setLevel(level))));
super(logService.getLevel(), logService.onDidChangeLogLevel);
}
createLogger(resource: URI, options?: ILoggerOptions): ILogger {
let logger = this.loggers.get(resource.toString());
if (!logger) {
if (resource.scheme === Schemas.file) {
logger = new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, this.logService.getLevel());
if (options?.donotUseFormatters) {
(<SpdLogLogger>logger).clearFormatters();
}
} else {
logger = new FileLogger(options?.name ?? basename(resource), resource, this.logService.getLevel(), this.fileService);
}
this.loggers.set(resource.toString(), logger);
if (options?.always) {
logger.setLevel(LogLevel.Trace);
} else {
this.logLevelChangeableLoggers.push(logger);
protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger {
if (resource.scheme === Schemas.file) {
const logger = new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, logLevel);
if (options?.donotUseFormatters) {
(<SpdLogLogger>logger).clearFormatters();
}
return logger;
} else {
return new FileLogger(options?.name ?? basename(resource), resource, logLevel, this.fileService);
}
return logger;
}
dispose(): void {
this.logLevelChangeableLoggers.splice(0, this.logLevelChangeableLoggers.length);
this.loggers.forEach(logger => logger.dispose());
this.loggers.clear();
super.dispose();
}
}

View File

@@ -95,7 +95,7 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger {
}
}
flush(): void {
override flush(): void {
if (this._logger) {
this._logger.flush();
} else {
@@ -103,7 +103,7 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger {
}
}
dispose(): void {
override dispose(): void {
if (this._logger) {
this.disposeLogger();
} else {

View File

@@ -30,10 +30,10 @@ suite('Marker Service', () => {
marker: randomMarkerData(MarkerSeverity.Error)
}]);
assert.equal(service.read().length, 1);
assert.equal(service.read({ owner: 'far' }).length, 1);
assert.equal(service.read({ resource: URI.parse('file:///c/test/file.cs') }).length, 1);
assert.equal(service.read({ owner: 'far', resource: URI.parse('file:///c/test/file.cs') }).length, 1);
assert.strictEqual(service.read().length, 1);
assert.strictEqual(service.read({ owner: 'far' }).length, 1);
assert.strictEqual(service.read({ resource: URI.parse('file:///c/test/file.cs') }).length, 1);
assert.strictEqual(service.read({ owner: 'far', resource: URI.parse('file:///c/test/file.cs') }).length, 1);
service.changeAll('boo', [{
@@ -41,14 +41,14 @@ suite('Marker Service', () => {
marker: randomMarkerData(MarkerSeverity.Warning)
}]);
assert.equal(service.read().length, 2);
assert.equal(service.read({ owner: 'far' }).length, 1);
assert.equal(service.read({ owner: 'boo' }).length, 1);
assert.strictEqual(service.read().length, 2);
assert.strictEqual(service.read({ owner: 'far' }).length, 1);
assert.strictEqual(service.read({ owner: 'boo' }).length, 1);
assert.equal(service.read({ severities: MarkerSeverity.Error }).length, 1);
assert.equal(service.read({ severities: MarkerSeverity.Warning }).length, 1);
assert.equal(service.read({ severities: MarkerSeverity.Hint }).length, 0);
assert.equal(service.read({ severities: MarkerSeverity.Error | MarkerSeverity.Warning }).length, 2);
assert.strictEqual(service.read({ severities: MarkerSeverity.Error }).length, 1);
assert.strictEqual(service.read({ severities: MarkerSeverity.Warning }).length, 1);
assert.strictEqual(service.read({ severities: MarkerSeverity.Hint }).length, 0);
assert.strictEqual(service.read({ severities: MarkerSeverity.Error | MarkerSeverity.Warning }).length, 2);
});
@@ -57,17 +57,17 @@ suite('Marker Service', () => {
let service = new markerService.MarkerService();
service.changeOne('far', URI.parse('file:///path/only.cs'), [randomMarkerData()]);
assert.equal(service.read().length, 1);
assert.equal(service.read({ owner: 'far' }).length, 1);
assert.strictEqual(service.read().length, 1);
assert.strictEqual(service.read({ owner: 'far' }).length, 1);
service.changeOne('boo', URI.parse('file:///path/only.cs'), [randomMarkerData()]);
assert.equal(service.read().length, 2);
assert.equal(service.read({ owner: 'far' }).length, 1);
assert.equal(service.read({ owner: 'boo' }).length, 1);
assert.strictEqual(service.read().length, 2);
assert.strictEqual(service.read({ owner: 'far' }).length, 1);
assert.strictEqual(service.read({ owner: 'boo' }).length, 1);
service.changeOne('far', URI.parse('file:///path/only.cs'), [randomMarkerData(), randomMarkerData()]);
assert.equal(service.read({ owner: 'far' }).length, 2);
assert.equal(service.read({ owner: 'boo' }).length, 1);
assert.strictEqual(service.read({ owner: 'far' }).length, 2);
assert.strictEqual(service.read({ owner: 'boo' }).length, 1);
});
@@ -76,19 +76,19 @@ suite('Marker Service', () => {
let service = new markerService.MarkerService();
service.changeOne('far', URI.parse('file:///path/only.cs'), [randomMarkerData()]);
service.changeOne('boo', URI.parse('file:///path/only.cs'), [randomMarkerData()]);
assert.equal(service.read({ owner: 'far' }).length, 1);
assert.equal(service.read({ owner: 'boo' }).length, 1);
assert.equal(service.read().length, 2);
assert.strictEqual(service.read({ owner: 'far' }).length, 1);
assert.strictEqual(service.read({ owner: 'boo' }).length, 1);
assert.strictEqual(service.read().length, 2);
service.changeOne('far', URI.parse('file:///path/only.cs'), []);
assert.equal(service.read({ owner: 'far' }).length, 0);
assert.equal(service.read({ owner: 'boo' }).length, 1);
assert.equal(service.read().length, 1);
assert.strictEqual(service.read({ owner: 'far' }).length, 0);
assert.strictEqual(service.read({ owner: 'boo' }).length, 1);
assert.strictEqual(service.read().length, 1);
service.changeAll('boo', []);
assert.equal(service.read({ owner: 'far' }).length, 0);
assert.equal(service.read({ owner: 'boo' }).length, 0);
assert.equal(service.read().length, 0);
assert.strictEqual(service.read({ owner: 'far' }).length, 0);
assert.strictEqual(service.read({ owner: 'boo' }).length, 0);
assert.strictEqual(service.read().length, 0);
});
test('changeAll sends event for cleared', () => {
@@ -102,12 +102,12 @@ suite('Marker Service', () => {
marker: randomMarkerData()
}]);
assert.equal(service.read({ owner: 'far' }).length, 2);
assert.strictEqual(service.read({ owner: 'far' }).length, 2);
service.onMarkerChanged(changedResources => {
assert.equal(changedResources.length, 1);
changedResources.forEach(u => assert.equal(u.toString(), 'file:///d/path'));
assert.equal(service.read({ owner: 'far' }).length, 0);
assert.strictEqual(changedResources.length, 1);
changedResources.forEach(u => assert.strictEqual(u.toString(), 'file:///d/path'));
assert.strictEqual(service.read({ owner: 'far' }).length, 0);
});
service.changeAll('far', []);
@@ -124,7 +124,7 @@ suite('Marker Service', () => {
marker: randomMarkerData()
}]);
assert.equal(service.read({ owner: 'far' }).length, 2);
assert.strictEqual(service.read({ owner: 'far' }).length, 2);
});
test('changeAll must not break integrety, issue #12635', () => {
@@ -151,8 +151,8 @@ suite('Marker Service', () => {
marker: randomMarkerData()
}]);
assert.equal(service.read({ owner: 'far' }).length, 2);
assert.equal(service.read({ resource: URI.parse('scheme:path1') }).length, 2);
assert.strictEqual(service.read({ owner: 'far' }).length, 2);
assert.strictEqual(service.read({ resource: URI.parse('scheme:path1') }).length, 2);
});
test('invalid marker data', () => {
@@ -162,15 +162,15 @@ suite('Marker Service', () => {
data.message = undefined!;
service.changeOne('far', URI.parse('some:uri/path'), [data]);
assert.equal(service.read({ owner: 'far' }).length, 0);
assert.strictEqual(service.read({ owner: 'far' }).length, 0);
data.message = null!;
service.changeOne('far', URI.parse('some:uri/path'), [data]);
assert.equal(service.read({ owner: 'far' }).length, 0);
assert.strictEqual(service.read({ owner: 'far' }).length, 0);
data.message = 'null';
service.changeOne('far', URI.parse('some:uri/path'), [data]);
assert.equal(service.read({ owner: 'far' }).length, 1);
assert.strictEqual(service.read({ owner: 'far' }).length, 1);
});
test('MapMap#remove returns bad values, https://github.com/microsoft/vscode/issues/13548', () => {
@@ -189,7 +189,7 @@ suite('Marker Service', () => {
endLineNumber: 1,
endColumn: 5,
message: 'test',
severity: 0,
severity: 0 as MarkerSeverity,
source: 'me'
};
let service = new markerService.MarkerService();
@@ -197,7 +197,7 @@ suite('Marker Service', () => {
service.changeOne('far', URI.parse('some:thing'), [data]);
let marker = service.read({ resource: URI.parse('some:thing') });
assert.equal(marker.length, 1);
assert.equal(marker[0].code, '0');
assert.strictEqual(marker.length, 1);
assert.strictEqual(marker[0].code, '0');
});
});

View File

@@ -168,7 +168,7 @@ export class Menubar {
// Keep flag when app quits
this.lifecycleMainService.onWillShutdown(() => this.willShutdown = true);
// // Listen to some events from window service to update menu
// Listen to some events from window service to update menu
this.windowsMainService.onDidChangeWindowsCount(e => this.onDidChangeWindowsCount(e));
this.nativeHostMainService.onDidBlurWindow(() => this.onDidChangeWindowFocus());
this.nativeHostMainService.onDidFocusWindow(() => this.onDidChangeWindowFocus());
@@ -569,10 +569,7 @@ export class Menubar {
return [new MenuItem({
label: this.mnemonicLabel(nls.localize('miCheckForUpdates', "Check for &&Updates...")), click: () => setTimeout(() => {
this.reportMenuActionTelemetry('CheckForUpdate');
const window = this.windowsMainService.getLastActiveWindow();
const context = window && `window:${window.id}`; // sessionId
this.updateService.checkForUpdates(context);
this.updateService.checkForUpdates(true);
}, 0)
})];

View File

@@ -407,7 +407,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
icns: (isMacintosh && this.environmentMainService.isBuilt) ? join(dirname(this.environmentMainService.appRoot), `${this.productService.nameShort}.icns`) : undefined
};
sudoPrompt.exec(sudoCommand.join(' '), promptOptions, (error: string, stdout: string, stderr: string) => {
sudoPrompt.exec(sudoCommand.join(' '), promptOptions, (error?, stdout?, stderr?) => {
if (stdout) {
this.logService.trace(`[sudo-prompt] received stdout: ${stdout}`);
}

View File

@@ -72,6 +72,13 @@ export interface INeverShowAgainOptions {
export interface INotification extends INotificationProperties {
/**
* The id of the notification. If provided, will be used to compare
* notifications with others to decide whether a notification is
* duplicate or not.
*/
readonly id?: string;
/**
* The severity of the notification. Either `Info`, `Warning` or `Error`.
*/
@@ -117,14 +124,14 @@ export interface INotificationActions {
*
* Pass `ActionWithMenuAction` for an action that has additional menu actions.
*/
readonly primary?: ReadonlyArray<IAction>;
readonly primary?: readonly IAction[];
/**
* Secondary actions are meant to provide additional configuration or context
* for the notification and will show up less prominent. A notification does not
* close automatically when invoking a secondary action.
*/
readonly secondary?: ReadonlyArray<IAction>;
readonly secondary?: readonly IAction[];
}
export interface INotificationProgressProperties {

View File

@@ -53,7 +53,7 @@ export class Link extends Disposable {
this._register(onOpen(e => {
EventHelper.stop(e, true);
if (!this.disabled) {
openerService.open(link.href);
openerService.open(link.href, { allowCommands: true });
}
}));

View File

@@ -30,6 +30,11 @@ export type OpenInternalOptions = {
* action, such as keyboard or mouse usage.
*/
readonly fromUserGesture?: boolean;
/**
* Allow command links to be handled.
*/
readonly allowCommands?: boolean;
};
export type OpenExternalOptions = {

View File

@@ -4,50 +4,26 @@
*--------------------------------------------------------------------------------------------*/
import { FileAccess } from 'vs/base/common/network';
import { isWeb } from 'vs/base/common/platform';
import { isWeb, globals } from 'vs/base/common/platform';
import { env } from 'vs/base/common/process';
import { dirname, joinPath } from 'vs/base/common/resources';
import { IProductConfiguration } from 'vs/platform/product/common/productService';
import { IProductConfiguration } from 'vs/base/common/product';
import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes';
let product: IProductConfiguration;
// Web or Native (sandbox TODO@sandbox need to add all properties of product.json)
if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire !== 'function') {
// Built time configuration (do NOT modify)
product = { /*BUILD->INSERT_PRODUCT_CONFIGURATION*/ } as IProductConfiguration;
// Running out of sources
if (Object.keys(product).length === 0) {
Object.assign(product, {
version: '1.55.0-dev',
nameShort: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev',
nameLong: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev',
applicationName: 'code-oss',
dataFolderName: '.vscode-oss',
urlProtocol: 'code-oss',
reportIssueUrl: 'https://github.com/microsoft/vscode/issues/new',
licenseName: 'MIT',
licenseUrl: 'https://github.com/microsoft/vscode/blob/main/LICENSE.txt',
extensionAllowedProposedApi: [
'ms-vscode.vscode-js-profile-flame',
'ms-vscode.vscode-js-profile-table',
'ms-vscode.github-browser',
'ms-vscode.remotehub',
'ms-vscode.remotehub-insiders'
],
});
}
// NOTE@coder: Add the ability to inject settings from the server.
const el = document.getElementById('vscode-remote-product-configuration');
const rawProductConfiguration = el && el.getAttribute('data-settings');
if (rawProductConfiguration) {
Object.assign(product, JSON.parse(rawProductConfiguration));
// Native sandbox environment
if (typeof globals.vscode !== 'undefined') {
const configuration: ISandboxConfiguration | undefined = globals.vscode.context.configuration();
if (configuration) {
product = configuration.product;
} else {
throw new Error('Sandbox: unable to resolve product configuration from preload script.');
}
}
// Native (non-sandboxed)
else {
// Native node.js environment
else if (typeof require?.__$__nodeRequire === 'function') {
// Obtain values from product.json and package.json
const rootPath = dirname(FileAccess.asFileUri('', require));
@@ -69,4 +45,40 @@ else {
});
}
// Web environment or unknown
else {
// Built time configuration (do NOT modify)
product = { /*BUILD->INSERT_PRODUCT_CONFIGURATION*/ } as IProductConfiguration;
// Running out of sources
if (Object.keys(product).length === 0) {
Object.assign(product, {
version: '1.56.0-dev',
nameShort: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev',
nameLong: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev',
applicationName: 'code-oss',
dataFolderName: '.vscode-oss',
urlProtocol: 'code-oss',
reportIssueUrl: 'https://github.com/microsoft/vscode/issues/new',
licenseName: 'MIT',
licenseUrl: 'https://github.com/microsoft/vscode/blob/main/LICENSE.txt',
extensionAllowedProposedApi: [
'ms-vscode.vscode-js-profile-flame',
'ms-vscode.vscode-js-profile-table',
'ms-vscode.github-browser',
'ms-vscode.github-richnav',
'ms-vscode.remotehub',
'ms-vscode.remotehub-insiders'
],
});
}
// NOTE@coder: Add the ability to inject settings from the server.
const el = document.getElementById('vscode-remote-product-configuration');
const rawProductConfiguration = el && el.getAttribute('data-settings');
if (rawProductConfiguration) {
Object.assign(product, JSON.parse(rawProductConfiguration));
}
}
export default product;

View File

@@ -4,8 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ExtensionKind } from 'vs/platform/extensions/common/extensions';
import { IStringDictionary } from 'vs/base/common/collections';
import { IProductConfiguration } from 'vs/base/common/product';
export const IProductService = createDecorator<IProductService>('productService');
@@ -14,6 +13,7 @@ export interface IProductService extends Readonly<IProductConfiguration> {
readonly _serviceBrand: undefined;
}
<<<<<<< HEAD
export interface IBuiltInExtension {
readonly name: string;
@@ -171,3 +171,5 @@ export interface ISurveyData {
editCount: number;
userProbability: number;
}
=======
>>>>>>> 58ce849223667f77dc0d6d7658870ca3f815e17f

View File

@@ -60,8 +60,8 @@ export interface IProgressOptions {
export interface IProgressNotificationOptions extends IProgressOptions {
readonly location: ProgressLocation.Notification;
readonly primaryActions?: ReadonlyArray<IAction>;
readonly secondaryActions?: ReadonlyArray<IAction>;
readonly primaryActions?: readonly IAction[];
readonly secondaryActions?: readonly IAction[];
readonly delay?: number;
readonly silent?: boolean;
}

View File

@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IProtocolMainService = createDecorator<IProtocolMainService>('protocolMainService');
export interface IIPCObjectUrl<T> extends IDisposable {
/**
* A `URI` that a renderer can use to retrieve the
* object via `ipcRenderer.invoke(resource.toString())`
*/
resource: URI;
/**
* Allows to update the value of the object after it
* has been created.
*
* @param obj the object to make accessible to the
* renderer.
*/
update(obj: T): void;
}
export interface IProtocolMainService {
readonly _serviceBrand: undefined;
/**
* Allows to make an object accessible to a renderer
* via `ipcRenderer.invoke(resource.toString())`.
*
* @param obj the (optional) object to make accessible to the
* renderer. Can be updated later via the `IObjectUrl#update`
* method too.
*/
createIPCObjectUrl<T>(obj?: T): IIPCObjectUrl<T>;
/**
* Adds a `URI` as root to the list of allowed
* resources for file access.
*
* @param root the URI to allow for file access
*/
addValidFileRoot(root: URI): IDisposable;
}

View File

@@ -0,0 +1,172 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { FileAccess, Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { ipcMain, session } from 'electron';
import { ILogService } from 'vs/platform/log/common/log';
import { TernarySearchTree } from 'vs/base/common/map';
import { isLinux, isPreferringBrowserCodeLoad } from 'vs/base/common/platform';
import { extname } from 'vs/base/common/resources';
import { IIPCObjectUrl, IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol';
import { generateUuid } from 'vs/base/common/uuid';
type ProtocolCallback = { (result: string | Electron.FilePathWithHeaders | { error: number }): void };
export class ProtocolMainService extends Disposable implements IProtocolMainService {
declare readonly _serviceBrand: undefined;
private readonly validRoots = TernarySearchTree.forUris<boolean>(() => !isLinux);
private readonly validExtensions = new Set(['.png', '.jpg', '.jpeg', '.gif', '.bmp']); // https://github.com/microsoft/vscode/issues/119384
constructor(
@INativeEnvironmentService environmentService: INativeEnvironmentService,
@ILogService private readonly logService: ILogService
) {
super();
// Define an initial set of roots we allow loading from
// - appRoot : all files installed as part of the app
// - extensions : all files shipped from extensions
// - storage : all files in global and workspace storage (https://github.com/microsoft/vscode/issues/116735)
this.addValidFileRoot(URI.file(environmentService.appRoot));
this.addValidFileRoot(URI.file(environmentService.extensionsPath));
this.addValidFileRoot(environmentService.globalStorageHome);
this.addValidFileRoot(environmentService.workspaceStorageHome);
// Handle protocols
this.handleProtocols();
}
private handleProtocols(): void {
const { defaultSession } = session;
// Register vscode-file:// handler
defaultSession.protocol.registerFileProtocol(Schemas.vscodeFileResource, (request, callback) => this.handleResourceRequest(request, callback as unknown as ProtocolCallback));
// Intercept any file:// access
defaultSession.protocol.interceptFileProtocol(Schemas.file, (request, callback) => this.handleFileRequest(request, callback as unknown as ProtocolCallback));
// Cleanup
this._register(toDisposable(() => {
defaultSession.protocol.unregisterProtocol(Schemas.vscodeFileResource);
defaultSession.protocol.uninterceptProtocol(Schemas.file);
}));
}
addValidFileRoot(root: URI): IDisposable {
if (!this.validRoots.get(root)) {
this.validRoots.set(root, true);
return toDisposable(() => this.validRoots.delete(root));
}
return Disposable.None;
}
//#region file://
private handleFileRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback): void {
const fileUri = URI.parse(request.url);
// isPreferringBrowserCodeLoad: false
if (!isPreferringBrowserCodeLoad) {
// first check by validRoots
if (this.validRoots.findSubstr(fileUri)) {
return callback({
path: fileUri.fsPath
});
}
// then check by validExtensions
if (this.validExtensions.has(extname(fileUri))) {
return callback({
path: fileUri.fsPath
});
}
// finally block to load the resource
this.logService.error(`${Schemas.file}: Refused to load resource ${fileUri.fsPath} from ${Schemas.file}: protocol (original URL: ${request.url})`);
return callback({ error: -3 /* ABORTED */ });
}
// isPreferringBrowserCodeLoad: true
// => block any file request
else {
this.logService.error(`Refused to load resource ${fileUri.fsPath} from ${Schemas.file}: protocol (original URL: ${request.url})`);
return callback({ error: -3 /* ABORTED */ });
}
}
//#endregion
//#region vscode-file://
private handleResourceRequest(request: Electron.ProtocolRequest, callback: ProtocolCallback): void {
const uri = URI.parse(request.url);
// Restore the `vscode-file` URI to a `file` URI so that we can
// ensure the root is valid and properly tell Chrome where the
// resource is at.
const fileUri = FileAccess.asFileUri(uri);
// first check by validRoots
if (this.validRoots.findSubstr(fileUri)) {
return callback({
path: fileUri.fsPath
});
}
// then check by validExtensions
if (this.validExtensions.has(extname(fileUri))) {
return callback({
path: fileUri.fsPath
});
}
// finally block to load the resource
this.logService.error(`${Schemas.vscodeFileResource}: Refused to load resource ${fileUri.fsPath} from ${Schemas.vscodeFileResource}: protocol (original URL: ${request.url})`);
return callback({ error: -3 /* ABORTED */ });
}
//#endregion
//#region IPC Object URLs
createIPCObjectUrl<T>(obj: T): IIPCObjectUrl<T> {
// Create unique URI
const resource = URI.from({
scheme: 'vscode', // used for all our IPC communication (vscode:<channel>)
path: generateUuid()
});
// Install IPC handler
const channel = resource.toString();
const handler = async (): Promise<T> => obj;
ipcMain.handle(channel, handler);
this.logService.trace(`IPC Object URL: Registered new channel ${channel}.`);
return {
resource,
update: updatedObj => obj = updatedObj,
dispose: () => {
this.logService.trace(`IPC Object URL: Removed channel ${channel}.`);
ipcMain.removeHandler(channel);
}
};
}
//#endregion
}

View File

@@ -39,8 +39,10 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
private readonly commandsHistory = this._register(this.instantiationService.createInstance(CommandsHistory));
protected override readonly options: ICommandsQuickAccessOptions;
constructor(
protected options: ICommandsQuickAccessOptions,
options: ICommandsQuickAccessOptions,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
@ICommandService private readonly commandService: ICommandService,
@@ -48,6 +50,8 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
@INotificationService private readonly notificationService: INotificationService
) {
super(AbstractCommandsQuickAccessProvider.PREFIX, options);
this.options = options;
}
protected async getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Promise<Array<ICommandQuickPick | IQuickPickSeparator>> {

View File

@@ -73,8 +73,8 @@ export interface IPickerQuickAccessProviderOptions<T extends IPickerQuickAccessI
}
export type Pick<T> = T | IQuickPickSeparator;
export type PicksWithActive<T> = { items: ReadonlyArray<Pick<T>>, active?: T };
export type Picks<T> = ReadonlyArray<Pick<T>> | PicksWithActive<T>;
export type PicksWithActive<T> = { items: readonly Pick<T>[], active?: T };
export type Picks<T> = readonly Pick<T>[] | PicksWithActive<T>;
export type FastAndSlowPicks<T> = { picks: Picks<T>, additionalPicks: Promise<Picks<T>> };
function isPicksWithActive<T>(obj: unknown): obj is PicksWithActive<T> {
@@ -125,7 +125,7 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
const providedPicks = this.getPicks(picksFilter, picksDisposables, picksToken);
const applyPicks = (picks: Picks<T>, skipEmpty?: boolean): boolean => {
let items: ReadonlyArray<Pick<T>>;
let items: readonly Pick<T>[];
let activeItem: T | undefined = undefined;
if (isPicksWithActive(picks)) {
@@ -191,7 +191,7 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
return;
}
let picks: ReadonlyArray<Pick<T>>;
let picks: readonly Pick<T>[];
let activePick: Pick<T> | undefined = undefined;
if (isPicksWithActive(providedPicks.picks)) {
picks = providedPicks.picks.items;
@@ -200,7 +200,7 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
picks = providedPicks.picks;
}
let additionalPicks: ReadonlyArray<Pick<T>>;
let additionalPicks: readonly Pick<T>[];
let additionalActivePick: Pick<T> | undefined = undefined;
if (isPicksWithActive(awaitedAdditionalPicks)) {
additionalPicks = awaitedAdditionalPicks.items;

View File

@@ -7,7 +7,7 @@ import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuick
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService, Themable } from 'vs/platform/theme/common/themeService';
import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, badgeBackground, badgeForeground, contrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, progressBarBackground, widgetShadow, listFocusForeground, activeContrastBorder, pickerGroupBorder, pickerGroupForeground, quickInputForeground, quickInputBackground, quickInputTitleBackground, quickInputListFocusBackground } from 'vs/platform/theme/common/colorRegistry';
import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, badgeBackground, badgeForeground, contrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, progressBarBackground, widgetShadow, listFocusForeground, activeContrastBorder, pickerGroupBorder, pickerGroupForeground, quickInputForeground, quickInputBackground, quickInputTitleBackground, quickInputListFocusBackground, keybindingLabelBackground, keybindingLabelForeground, keybindingLabelBorder, keybindingLabelBottomBorder } from 'vs/platform/theme/common/colorRegistry';
import { CancellationToken } from 'vs/base/common/cancellation';
import { computeStyles } from 'vs/platform/theme/common/styler';
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
@@ -166,7 +166,7 @@ export class QuickInputService extends Themable implements IQuickInputService {
return this.controller.cancel();
}
protected updateStyles() {
protected override updateStyles() {
this.controller.applyStyles(this.computeStyles());
}
@@ -209,6 +209,13 @@ export class QuickInputService extends Themable implements IQuickInputService {
progressBar: computeStyles(this.theme, {
progressBarBackground
}),
keybindingLabel: computeStyles(this.theme, {
keybindingLabelBackground,
keybindingLabelForeground,
keybindingLabelBorder,
keybindingLabelBottomBorder,
keybindingLabelShadow: widgetShadow
}),
list: computeStyles(this.theme, {
listBackground: quickInputBackground,
// Look like focused when inactive.

View File

@@ -21,7 +21,7 @@ suite('Platform / Registry', () => {
assert.ok(Registry.knows('foo'));
assert.ok(Registry.as<any>('foo').bar);
assert.equal(Registry.as<any>('foo').bar, true);
assert.strictEqual(Registry.as<any>('foo').bar, true);
});
test('registry - knows, as', function () {

View File

@@ -20,6 +20,7 @@ export interface IRemoteAgentEnvironment {
userHome: URI;
os: OperatingSystem;
marks: performance.PerformanceMark[];
useHostProxy: boolean;
}
export interface RemoteAgentConnectionContext {

View File

@@ -15,8 +15,15 @@ export interface ResolvedAuthority {
readonly connectionToken: string | undefined;
}
export enum RemoteTrustOption {
Unknown = 0,
DisableTrust = 1,
MachineTrusted = 2
}
export interface ResolvedOptions {
readonly extensionHostEnv?: { [key: string]: string | null };
readonly trust?: RemoteTrustOption;
}
export interface TunnelDescription {

View File

@@ -5,6 +5,7 @@
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
export function getRemoteAuthority(uri: URI): string | undefined {
return uri.scheme === Schemas.vscodeRemote ? uri.authority : undefined;
@@ -24,3 +25,20 @@ export function getRemoteName(authority: string | undefined): string | undefined
}
return authority.substr(0, pos);
}
function isVirtualResource(resource: URI) {
return resource.scheme !== Schemas.file && resource.scheme !== Schemas.vscodeRemote;
}
export function getVirtualWorkspaceLocation(workspace: IWorkspace): { scheme: string, authority: string } | undefined {
if (workspace.folders.length) {
return workspace.folders.every(f => isVirtualResource(f.uri)) ? workspace.folders[0].uri : undefined;
} else if (workspace.configuration && isVirtualResource(workspace.configuration)) {
return workspace.configuration;
}
return undefined;
}
export function getVirtualWorkspaceScheme(workspace: IWorkspace): string | undefined {
return getVirtualWorkspaceLocation(workspace)?.scheme;
}

View File

@@ -237,7 +237,7 @@ export abstract class AbstractTunnelService implements ITunnelService {
localAddress: tunnel.localAddress,
public: tunnel.public,
dispose: async () => {
this.logService.trace(`ForwardedPorts: (TunnelService) dispose request for ${tunnel.tunnelRemotePort} `);
this.logService.trace(`ForwardedPorts: (TunnelService) dispose request for ${tunnel.tunnelRemoteHost}:${tunnel.tunnelRemotePort} `);
const existingHost = this._tunnels.get(tunnel.tunnelRemoteHost);
if (existingHost) {
const existing = existingHost.get(tunnel.tunnelRemotePort);
@@ -267,7 +267,7 @@ export abstract class AbstractTunnelService implements ITunnelService {
}
async closeTunnel(remoteHost: string, remotePort: number): Promise<void> {
this.logService.trace(`ForwardedPorts: (TunnelService) close request for ${remotePort} `);
this.logService.trace(`ForwardedPorts: (TunnelService) close request for ${remoteHost}:${remotePort} `);
const portMap = this._tunnels.get(remoteHost);
if (portMap && portMap.has(remotePort)) {
const value = portMap.get(remotePort)!;

View File

@@ -58,7 +58,7 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
this.tunnelRemoteHost = tunnelRemoteHost;
}
public async dispose(): Promise<void> {
public override async dispose(): Promise<void> {
super.dispose();
this._server.removeListener('listening', this._listeningListener);
this._server.removeListener('connection', this._connectionListener);

View File

@@ -6,7 +6,7 @@
import { localize } from 'vs/nls';
import { CancellationToken } from 'vs/base/common/cancellation';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IConfigurationRegistry, Extensions, ConfigurationScope, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import { streamToBuffer } from 'vs/base/common/buffer';
import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request';
@@ -66,43 +66,63 @@ export interface IHTTPConfiguration {
};
}
Registry.as<IConfigurationRegistry>(Extensions.Configuration)
.registerConfiguration({
export function updateProxyConfigurationsScope(scope: ConfigurationScope): void {
registerProxyConfigurations(scope);
}
let proxyConfiguration: IConfigurationNode | undefined;
function registerProxyConfigurations(scope: ConfigurationScope): void {
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
if (proxyConfiguration) {
configurationRegistry.deregisterConfigurations([proxyConfiguration]);
}
proxyConfiguration = {
id: 'http',
order: 15,
title: localize('httpConfigurationTitle', "HTTP"),
type: 'object',
scope,
properties: {
'http.proxy': {
type: 'string',
pattern: '^https?://([^:]*(:[^@]*)?@)?([^:]+|\\[[:0-9a-fA-F]+\\])(:\\d+)?/?$|^$',
markdownDescription: localize('proxy', "The proxy setting to use. If not set, will be inherited from the `http_proxy` and `https_proxy` environment variables.")
markdownDescription: localize('proxy', "The proxy setting to use. If not set, will be inherited from the `http_proxy` and `https_proxy` environment variables."),
restricted: true
},
'http.proxyStrictSSL': {
type: 'boolean',
default: true,
description: localize('strictSSL', "Controls whether the proxy server certificate should be verified against the list of supplied CAs.")
description: localize('strictSSL', "Controls whether the proxy server certificate should be verified against the list of supplied CAs."),
restricted: true
},
'http.proxyAuthorization': {
type: ['null', 'string'],
default: null,
markdownDescription: localize('proxyAuthorization', "The value to send as the `Proxy-Authorization` header for every network request.")
markdownDescription: localize('proxyAuthorization', "The value to send as the `Proxy-Authorization` header for every network request."),
restricted: true
},
'http.proxySupport': {
type: 'string',
enum: ['off', 'on', 'override'],
enum: ['off', 'on', 'fallback', 'override'],
enumDescriptions: [
localize('proxySupportOff', "Disable proxy support for extensions."),
localize('proxySupportOn', "Enable proxy support for extensions."),
localize('proxySupportFallback', "Enable proxy support for extensions, fall back to request options, when no proxy found."),
localize('proxySupportOverride', "Enable proxy support for extensions, override request options."),
],
default: 'override',
description: localize('proxySupport', "Use the proxy support for extensions.")
description: localize('proxySupport', "Use the proxy support for extensions."),
restricted: true
},
'http.systemCertificates': {
type: 'boolean',
default: true,
description: localize('systemCertificates', "Controls whether CA certificates should be loaded from the OS. (On Windows and macOS, a reload of the window is required after turning this off.)")
description: localize('systemCertificates', "Controls whether CA certificates should be loaded from the OS. (On Windows and macOS, a reload of the window is required after turning this off.)"),
restricted: true
}
}
});
};
configurationRegistry.registerConfiguration(proxyConfiguration);
}
registerProxyConfigurations(ConfigurationScope.MACHINE);

View File

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

View File

@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import product from 'vs/platform/product/common/product';
import { BrowserWindow, ipcMain, Event as ElectronEvent, MessagePortMain, IpcMainEvent, RenderProcessGoneDetails } from 'electron';
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
import { Barrier } from 'vs/base/common/async';
@@ -10,7 +11,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
import { FileAccess } from 'vs/base/common/network';
import { browserCodeLoadingCacheStrategy } from 'vs/base/common/platform';
import { browserCodeLoadingCacheStrategy, IProcessEnvironment } from 'vs/base/common/platform';
import { ISharedProcess, ISharedProcessConfiguration } from 'vs/platform/sharedProcess/node/sharedProcess';
import { Disposable } from 'vs/base/common/lifecycle';
import { connect as connectMessagePort } from 'vs/base/parts/ipc/electron-main/ipc.mp';
@@ -18,6 +19,7 @@ import { assertIsDefined } from 'vs/base/common/types';
import { Emitter, Event } from 'vs/base/common/event';
import { WindowError } from 'vs/platform/windows/electron-main/windows';
import { resolveShellEnv } from 'vs/platform/environment/node/shellEnv';
import { IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol';
export class SharedProcess extends Disposable implements ISharedProcess {
@@ -31,11 +33,12 @@ export class SharedProcess extends Disposable implements ISharedProcess {
constructor(
private readonly machineId: string,
private userEnv: NodeJS.ProcessEnv,
private userEnv: IProcessEnvironment,
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@ILogService private readonly logService: ILogService,
@IThemeMainService private readonly themeMainService: IThemeMainService
@IThemeMainService private readonly themeMainService: IThemeMainService,
@IProtocolMainService private readonly protocolMainService: IProtocolMainService
) {
super();
@@ -158,6 +161,7 @@ export class SharedProcess extends Disposable implements ISharedProcess {
}
private createWindow(): void {
const configObjectUrl = this._register(this.protocolMainService.createIPCObjectUrl<ISharedProcessConfiguration>());
// shared process is a hidden window by default
this.window = new BrowserWindow({
@@ -165,8 +169,10 @@ export class SharedProcess extends Disposable implements ISharedProcess {
backgroundColor: this.themeMainService.getBackgroundColor(),
webPreferences: {
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath,
additionalArguments: [`--vscode-window-config=${configObjectUrl.resource.toString()}`],
v8CacheOptions: browserCodeLoadingCacheStrategy,
nodeIntegration: true,
contextIsolation: false,
enableWebSQL: false,
enableRemoteModule: false,
spellcheck: false,
@@ -177,7 +183,8 @@ export class SharedProcess extends Disposable implements ISharedProcess {
}
});
const config: ISharedProcessConfiguration = {
// Store into config object URL
configObjectUrl.update({
machineId: this.machineId,
windowId: this.window.id,
appRoot: this.environmentMainService.appRoot,
@@ -185,15 +192,12 @@ export class SharedProcess extends Disposable implements ISharedProcess {
backupWorkspacesPath: this.environmentMainService.backupWorkspacesPath,
userEnv: this.userEnv,
args: this.environmentMainService.args,
logLevel: this.logService.getLevel()
};
logLevel: this.logService.getLevel(),
product
});
// Load with config
this.window.loadURL(FileAccess
.asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require)
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` })
.toString(true)
);
this.window.loadURL(FileAccess.asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require).toString(true));
}
private registerWindowListeners(): void {
@@ -216,7 +220,7 @@ export class SharedProcess extends Disposable implements ISharedProcess {
this.window.on('close', this.windowCloseListener);
// Crashes & Unrsponsive & Failed to load
// Crashes & Unresponsive & Failed to load
// We use `onUnexpectedError` explicitly because the error handler
// will send the error to the active window to log in devtools too
this.window.webContents.on('render-process-gone', (event, details) => this._onDidError.fire({ type: WindowError.CRASHED, details }));

View File

@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { LogLevel } from 'vs/platform/log/common/log';
@@ -15,18 +16,12 @@ export interface ISharedProcess {
toggle(): Promise<void>;
}
export interface ISharedProcessConfiguration {
export interface ISharedProcessConfiguration extends ISandboxConfiguration {
readonly machineId: string;
readonly windowId: number;
readonly appRoot: string;
readonly userEnv: NodeJS.ProcessEnv;
readonly args: NativeParsedArgs;
readonly logLevel: LogLevel;
readonly nodeCachedDataDir?: string;
readonly backupWorkspacesPath: string;
}

View File

@@ -94,7 +94,7 @@ export class BrowserStorageService extends AbstractStorageService {
throw new Error('Migrating storage is currently unsupported in Web');
}
protected shouldFlushWhenIdle(): boolean {
protected override shouldFlushWhenIdle(): boolean {
// this flush() will potentially cause new state to be stored
// since new state will only be created while the document
// has focus, one optimization is to not run this when the

View File

@@ -206,7 +206,7 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain {
}));
}
protected async doInit(storage: IStorage): Promise<void> {
protected override async doInit(storage: IStorage): Promise<void> {
await super.doInit(storage);
// Apply global telemetry values as part of the initialization

View File

@@ -26,7 +26,7 @@ suite('StorageMainService', function () {
class TestStorageMainService extends StorageMainService {
protected getStorageOptions(): IStorageMainOptions {
protected override getStorageOptions(): IStorageMainOptions {
return {
useInMemoryStorage: true
};

View File

@@ -8,7 +8,7 @@ import { globals } from 'vs/base/common/platform';
import BaseErrorTelemetry, { ErrorEvent } from 'vs/platform/telemetry/common/errorTelemetry';
export default class ErrorTelemetry extends BaseErrorTelemetry {
protected installErrorListeners(): void {
protected override installErrorListeners(): void {
let oldOnError: Function;
let that = this;
if (typeof globals.onerror === 'function') {

View File

@@ -4,14 +4,23 @@
*--------------------------------------------------------------------------------------------*/
import { IFileService } from 'vs/platform/files/common/files';
import { isLinuxSnap, PlatformToString, platform } from 'vs/base/common/platform';
import { isLinuxSnap, PlatformToString, platform, Platform } from 'vs/base/common/platform';
import { platform as nodePlatform, env } from 'vs/base/common/process';
import { generateUuid } from 'vs/base/common/uuid';
import { URI } from 'vs/base/common/uri';
function getPlatformDetail(hostname: string): string | undefined {
if (platform === Platform.Linux && /^penguin(\.|$)/i.test(hostname)) {
return 'chromebook';
}
return undefined;
}
export async function resolveCommonProperties(
fileService: IFileService,
release: string,
hostname: string,
arch: string,
commit: string | undefined,
version: string | undefined,
@@ -73,6 +82,13 @@ export async function resolveCommonProperties(
result['common.snap'] = 'true';
}
const platformDetail = getPlatformDetail(hostname);
if (platformDetail) {
// __GDPR__COMMON__ "common.platformDetail" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
result['common.platformDetail'] = platformDetail;
}
try {
const contents = await fileService.readFile(URI.file(installSourcePath));

View File

@@ -10,7 +10,7 @@ import { ITelemetryService, ITelemetryInfo, ITelemetryData } from 'vs/platform/t
import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IConfigurationRegistry, Extensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { cloneAndChange, mixin } from 'vs/base/common/objects';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -223,6 +223,8 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
localize('telemetry.enableTelemetry', "Enable usage data and errors to be sent to a Microsoft online service.") :
localize('telemetry.enableTelemetryMd', "Enable usage data and errors to be sent to a Microsoft online service. Read our privacy statement [here]({0}).", product.privacyStatementUrl),
'default': true,
'restricted': true,
'scope': ConfigurationScope.APPLICATION,
'tags': ['usesOnlineServices']
}
}

View File

@@ -7,7 +7,7 @@ import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/err
import BaseErrorTelemetry from 'vs/platform/telemetry/common/errorTelemetry';
export default class ErrorTelemetry extends BaseErrorTelemetry {
protected installErrorListeners(): void {
protected override installErrorListeners(): void {
setUnexpectedErrorHandler(err => console.error(err));
// Print a console message when rejection isn't handled within N seconds. For details:

View File

@@ -90,10 +90,10 @@ suite('TelemetryService', () => {
let service = new TelemetryService({ appender: testAppender }, undefined!);
return service.publicLog('testPrivateEvent').then(() => {
assert.equal(testAppender.getEventsCount(), 1);
assert.strictEqual(testAppender.getEventsCount(), 1);
service.dispose();
assert.equal(!testAppender.isDisposed, true);
assert.strictEqual(!testAppender.isDisposed, true);
});
}));
@@ -103,9 +103,9 @@ suite('TelemetryService', () => {
let service = new TelemetryService({ appender: testAppender }, undefined!);
return service.publicLog('testEvent').then(_ => {
assert.equal(testAppender.getEventsCount(), 1);
assert.equal(testAppender.events[0].eventName, 'testEvent');
assert.notEqual(testAppender.events[0].data, null);
assert.strictEqual(testAppender.getEventsCount(), 1);
assert.strictEqual(testAppender.events[0].eventName, 'testEvent');
assert.notStrictEqual(testAppender.events[0].data, null);
service.dispose();
});
@@ -123,13 +123,13 @@ suite('TelemetryService', () => {
'value': 0
}
}).then(() => {
assert.equal(testAppender.getEventsCount(), 1);
assert.equal(testAppender.events[0].eventName, 'testEvent');
assert.notEqual(testAppender.events[0].data, null);
assert.equal(testAppender.events[0].data['stringProp'], 'property');
assert.equal(testAppender.events[0].data['numberProp'], 1);
assert.equal(testAppender.events[0].data['booleanProp'], true);
assert.equal(testAppender.events[0].data['complexProp'].value, 0);
assert.strictEqual(testAppender.getEventsCount(), 1);
assert.strictEqual(testAppender.events[0].eventName, 'testEvent');
assert.notStrictEqual(testAppender.events[0].data, null);
assert.strictEqual(testAppender.events[0].data['stringProp'], 'property');
assert.strictEqual(testAppender.events[0].data['numberProp'], 1);
assert.strictEqual(testAppender.events[0].data['booleanProp'], true);
assert.strictEqual(testAppender.events[0].data['complexProp'].value, 0);
service.dispose();
});
@@ -146,9 +146,9 @@ suite('TelemetryService', () => {
return service.publicLog('testEvent').then(_ => {
let [first] = testAppender.events;
assert.equal(Object.keys(first.data).length, 2);
assert.equal(typeof first.data['foo'], 'string');
assert.equal(typeof first.data['bar'], 'number');
assert.strictEqual(Object.keys(first.data).length, 2);
assert.strictEqual(typeof first.data['foo'], 'string');
assert.strictEqual(typeof first.data['bar'], 'number');
service.dispose();
});
@@ -164,11 +164,11 @@ suite('TelemetryService', () => {
return service.publicLog('testEvent', { hightower: 'xl', price: 8000 }).then(_ => {
let [first] = testAppender.events;
assert.equal(Object.keys(first.data).length, 4);
assert.equal(typeof first.data['foo'], 'string');
assert.equal(typeof first.data['bar'], 'number');
assert.equal(typeof first.data['hightower'], 'string');
assert.equal(typeof first.data['price'], 'number');
assert.strictEqual(Object.keys(first.data).length, 4);
assert.strictEqual(typeof first.data['foo'], 'string');
assert.strictEqual(typeof first.data['bar'], 'number');
assert.strictEqual(typeof first.data['hightower'], 'string');
assert.strictEqual(typeof first.data['price'], 'number');
service.dispose();
});
@@ -185,9 +185,9 @@ suite('TelemetryService', () => {
}, undefined!);
return service.getTelemetryInfo().then(info => {
assert.equal(info.sessionId, 'one');
assert.equal(info.instanceId, 'two');
assert.equal(info.machineId, 'three');
assert.strictEqual(info.sessionId, 'one');
assert.strictEqual(info.instanceId, 'two');
assert.strictEqual(info.machineId, 'three');
service.dispose();
});
@@ -198,8 +198,8 @@ suite('TelemetryService', () => {
let service = new TelemetryService({ appender: testAppender }, undefined!);
return service.publicLog('testEvent').then(() => {
assert.equal(testAppender.getEventsCount(), 1);
assert.equal(testAppender.events[0].eventName, 'testEvent');
assert.strictEqual(testAppender.getEventsCount(), 1);
assert.strictEqual(testAppender.events[0].eventName, 'testEvent');
service.dispose();
});
@@ -217,7 +217,7 @@ suite('TelemetryService', () => {
return Promise.all(this.promises);
}
publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<void> {
override publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<void> {
let p = super.publicLog(eventName, data, anonymizeFilePaths);
this.promises.push(p);
return p;
@@ -245,9 +245,9 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.equal(testAppender.getEventsCount(), 1);
assert.equal(testAppender.events[0].eventName, 'UnhandledError');
assert.equal(testAppender.events[0].data.msg, 'This is a test.');
assert.strictEqual(testAppender.getEventsCount(), 1);
assert.strictEqual(testAppender.events[0].eventName, 'UnhandledError');
assert.strictEqual(testAppender.events[0].data.msg, 'This is a test.');
errorTelemetry.dispose();
service.dispose();
@@ -275,9 +275,9 @@ suite('TelemetryService', () => {
// // allow for the promise to finish
// this.clock.tick(MainErrorTelemetry.ERROR_FLUSH_TIMEOUT);
//
// assert.equal(testAppender.getEventsCount(), 1);
// assert.equal(testAppender.events[0].eventName, 'UnhandledError');
// assert.equal(testAppender.events[0].data.msg, 'This should get logged');
// assert.strictEqual(testAppender.getEventsCount(), 1);
// assert.strictEqual(testAppender.events[0].eventName, 'UnhandledError');
// assert.strictEqual(testAppender.events[0].data.msg, 'This should get logged');
//
// service.dispose();
// } finally {
@@ -298,16 +298,16 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.equal(errorStub.alwaysCalledWithExactly('Error Message', 'file.js', 2, 42, testError), true);
assert.equal(errorStub.callCount, 1);
assert.strictEqual(errorStub.alwaysCalledWithExactly('Error Message', 'file.js', 2, 42, testError), true);
assert.strictEqual(errorStub.callCount, 1);
assert.equal(testAppender.getEventsCount(), 1);
assert.equal(testAppender.events[0].eventName, 'UnhandledError');
assert.equal(testAppender.events[0].data.msg, 'Error Message');
assert.equal(testAppender.events[0].data.file, 'file.js');
assert.equal(testAppender.events[0].data.line, 2);
assert.equal(testAppender.events[0].data.column, 42);
assert.equal(testAppender.events[0].data.uncaught_error_msg, 'test');
assert.strictEqual(testAppender.getEventsCount(), 1);
assert.strictEqual(testAppender.events[0].eventName, 'UnhandledError');
assert.strictEqual(testAppender.events[0].data.msg, 'Error Message');
assert.strictEqual(testAppender.events[0].data.file, 'file.js');
assert.strictEqual(testAppender.events[0].data.line, 2);
assert.strictEqual(testAppender.events[0].data.column, 42);
assert.strictEqual(testAppender.events[0].data.uncaught_error_msg, 'test');
errorTelemetry.dispose();
service.dispose();
@@ -328,9 +328,9 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.equal(errorStub.callCount, 1);
assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo.replace(settings.personalInfo, personInfoWithSpaces)), -1);
assert.equal(testAppender.events[0].data.file, settings.importantInfo + '/test.js');
assert.strictEqual(errorStub.callCount, 1);
assert.strictEqual(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo.replace(settings.personalInfo, personInfoWithSpaces)), -1);
assert.strictEqual(testAppender.events[0].data.file, settings.importantInfo + '/test.js');
errorTelemetry.dispose();
service.dispose();
@@ -350,8 +350,8 @@ suite('TelemetryService', () => {
(<any>window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo + '/test.js', 2, 42, dangerousFilenameError);
clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
return service.join().then(() => {
assert.equal(errorStub.callCount, 1);
assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1);
assert.strictEqual(errorStub.callCount, 1);
assert.strictEqual(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1);
dangerousFilenameError = new Error('dangerousFilename');
dangerousFilenameError.stack = settings.stack;
@@ -359,9 +359,9 @@ suite('TelemetryService', () => {
clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
return service.join();
}).then(() => {
assert.equal(errorStub.callCount, 2);
assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1);
assert.equal(testAppender.events[0].data.file, settings.importantInfo + '/test.js');
assert.strictEqual(errorStub.callCount, 2);
assert.strictEqual(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1);
assert.strictEqual(testAppender.events[0].data.file, settings.importantInfo + '/test.js');
errorTelemetry.dispose();
service.dispose();
@@ -383,13 +383,13 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.strictEqual(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
errorTelemetry.dispose();
service.dispose();
@@ -413,14 +413,14 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.equal(errorStub.callCount, 1);
assert.strictEqual(errorStub.callCount, 1);
// Test that no file information remains, esp. personal info
assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.strictEqual(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
errorTelemetry.dispose();
service.dispose();
@@ -445,14 +445,14 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
assert.notStrictEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.strictEqual(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
errorTelemetry.dispose();
service.dispose();
@@ -476,16 +476,16 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.equal(errorStub.callCount, 1);
assert.strictEqual(errorStub.callCount, 1);
// Test that important information remains but personal info does not
assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
assert.notStrictEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.strictEqual(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
errorTelemetry.dispose();
service.dispose();
@@ -510,10 +510,10 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModuleAsarPathToRetain), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModulePathToRetain), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModuleAsarPathToRetain), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModulePathToRetain), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModuleAsarPathToRetain), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModulePathToRetain), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModuleAsarPathToRetain), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModulePathToRetain), -1);
errorTelemetry.dispose();
service.dispose();
@@ -537,12 +537,12 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.equal(errorStub.callCount, 1);
assert.strictEqual(errorStub.callCount, 1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModuleAsarPathToRetain), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModulePathToRetain), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModuleAsarPathToRetain), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModulePathToRetain), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModuleAsarPathToRetain), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModulePathToRetain), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModuleAsarPathToRetain), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf('(/' + settings.nodeModulePathToRetain), -1);
errorTelemetry.dispose();
service.dispose();
@@ -568,14 +568,14 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
assert.notStrictEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.strictEqual(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
errorTelemetry.dispose();
service.dispose();
@@ -599,16 +599,16 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.equal(errorStub.callCount, 1);
assert.strictEqual(errorStub.callCount, 1);
// Test that important information remains but personal info does not
assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
assert.notStrictEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.importantInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.strictEqual(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
errorTelemetry.dispose();
service.dispose();
@@ -634,14 +634,14 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.missingModelPrefix), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.missingModelPrefix), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
assert.notStrictEqual(testAppender.events[0].data.msg.indexOf(settings.missingModelPrefix), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.missingModelPrefix), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.strictEqual(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
errorTelemetry.dispose();
service.dispose();
@@ -664,17 +664,17 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.equal(errorStub.callCount, 1);
assert.strictEqual(errorStub.callCount, 1);
// Test that no file information remains, but this particular
// error message does (Received model events for missing model)
assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.missingModelPrefix), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.missingModelPrefix), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
assert.notStrictEqual(testAppender.events[0].data.msg.indexOf(settings.missingModelPrefix), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.missingModelPrefix), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.strictEqual(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
errorTelemetry.dispose();
service.dispose();
@@ -700,14 +700,14 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.noSuchFilePrefix), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.noSuchFilePrefix), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
assert.notStrictEqual(testAppender.events[0].data.msg.indexOf(settings.noSuchFilePrefix), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.noSuchFilePrefix), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.strictEqual(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
errorTelemetry.dispose();
service.dispose();
@@ -734,18 +734,18 @@ suite('TelemetryService', () => {
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
await service.join();
assert.equal(errorStub.callCount, 1);
assert.strictEqual(errorStub.callCount, 1);
// Test that no file information remains, but this particular
// error message does (ENOENT: no such file or directory)
Errors.onUnexpectedError(noSuchFileError);
assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.noSuchFilePrefix), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.noSuchFilePrefix), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.equal(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.equal(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
assert.notStrictEqual(testAppender.events[0].data.msg.indexOf(settings.noSuchFilePrefix), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.noSuchFilePrefix), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.personalInfo), -1);
assert.strictEqual(testAppender.events[0].data.callstack.indexOf(settings.filePrefix), -1);
assert.notStrictEqual(testAppender.events[0].data.callstack.indexOf(settings.stack[4].replace(settings.randomUserFile, settings.anonymizedRandomUserFile)), -1);
assert.strictEqual(testAppender.events[0].data.callstack.split('\n').length, settings.stack.length);
errorTelemetry.dispose();
service.dispose();
@@ -759,7 +759,7 @@ suite('TelemetryService', () => {
let service = new TelemetryService({ appender: testAppender }, undefined!);
return service.publicLog('testEvent').then(() => {
assert.equal(testAppender.getEventsCount(), 1);
assert.strictEqual(testAppender.getEventsCount(), 1);
service.dispose();
});
}));
@@ -773,23 +773,23 @@ suite('TelemetryService', () => {
let service = new TelemetryService({
appender: testAppender
}, new class extends TestConfigurationService {
onDidChangeConfiguration = emitter.event;
getValue() {
override onDidChangeConfiguration = emitter.event;
override getValue() {
return {
enableTelemetry: enableTelemetry
} as any;
}
}());
assert.equal(service.isOptedIn, false);
assert.strictEqual(service.isOptedIn, false);
enableTelemetry = true;
emitter.fire({});
assert.equal(service.isOptedIn, true);
assert.strictEqual(service.isOptedIn, true);
enableTelemetry = false;
emitter.fire({});
assert.equal(service.isOptedIn, false);
assert.strictEqual(service.isOptedIn, false);
service.dispose();
});

View File

@@ -53,7 +53,7 @@ class TestTelemetryLogger extends AbstractLogger implements ILogger {
}
}
dispose(): void { }
override dispose(): void { }
flush(): void { }
}
@@ -77,14 +77,14 @@ suite('TelemetryLogAdapter', () => {
const testLoggerService = new TestTelemetryLoggerService(DEFAULT_LOG_LEVEL);
const testObject = new TelemetryLogAppender(testLoggerService, new TestInstantiationService().stub(IEnvironmentService, {}));
testObject.log('testEvent', { hello: 'world', isTrue: true, numberBetween1And3: 2 });
assert.equal(testLoggerService.logger.logs.length, 2);
assert.strictEqual(testLoggerService.logger.logs.length, 2);
});
test('Log Telemetry if log level is trace', async () => {
const testLoggerService = new TestTelemetryLoggerService(LogLevel.Trace);
const testObject = new TelemetryLogAppender(testLoggerService, new TestInstantiationService().stub(IEnvironmentService, {}));
testObject.log('testEvent', { hello: 'world', isTrue: true, numberBetween1And3: 2 });
assert.equal(testLoggerService.logger.logs[2], 'telemetry/testEvent' + JSON.stringify([{
assert.strictEqual(testLoggerService.logger.logs[2], 'telemetry/testEvent' + JSON.stringify([{
properties: {
hello: 'world',
},

Some files were not shown because too many files have changed in this diff Show More