mirror of
https://github.com/coder/code-server.git
synced 2026-05-05 20:15:19 +02:00
chore(vscode): update to 1.56.0
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
@@ -148,3 +148,5 @@ export const NullCommandService: ICommandService = {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
CommandsRegistry.registerCommand('noop', () => { });
|
||||
|
||||
@@ -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);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: [] });
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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() };
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
96
lib/vscode/src/vs/platform/driver/common/driverIpc.ts
Normal file
96
lib/vscode/src/vs/platform/driver/common/driverIpc.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
@@ -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');
|
||||
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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'); }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -730,7 +730,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
||||
|
||||
//#endregion
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
dispose(this.recursiveWatcher);
|
||||
|
||||
@@ -124,7 +124,7 @@ export class FileWatcher extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
this.isDisposed = true;
|
||||
|
||||
super.dispose();
|
||||
|
||||
@@ -91,7 +91,7 @@ export class FileWatcher extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
this.isDisposed = true;
|
||||
|
||||
super.dispose();
|
||||
|
||||
@@ -92,7 +92,7 @@ export class FileWatcher extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
this.isDisposed = true;
|
||||
|
||||
super.dispose();
|
||||
|
||||
@@ -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'); });
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export abstract class AbstractKeybindingService extends Disposable implements IK
|
||||
this._logging = false;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
public override dispose(): void {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
})];
|
||||
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface IRemoteAgentEnvironment {
|
||||
userHome: URI;
|
||||
os: OperatingSystem;
|
||||
marks: performance.PerformanceMark[];
|
||||
useHostProxy: boolean;
|
||||
}
|
||||
|
||||
export interface RemoteAgentConnectionContext {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)!;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,7 +26,7 @@ suite('StorageMainService', function () {
|
||||
|
||||
class TestStorageMainService extends StorageMainService {
|
||||
|
||||
protected getStorageOptions(): IStorageMainOptions {
|
||||
protected override getStorageOptions(): IStorageMainOptions {
|
||||
return {
|
||||
useInMemoryStorage: true
|
||||
};
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user