mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-10 18:22:34 -05:00
Merge from vscode 731f9c25632dbbf01ee3a7892ad9d2791fe0260c
This commit is contained in:
@@ -527,12 +527,13 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
private registerConfigurationSchemas(): void {
|
||||
if (this.workspace) {
|
||||
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
const defaultSettingsSchema: IJSONSchema = { additionalProperties: true, allowTrailingCommas: true, allowComments: true };
|
||||
const allSettingsSchema: IJSONSchema = { properties: allSettings.properties, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true };
|
||||
const userSettingsSchema: IJSONSchema = this.remoteUserConfiguration ? { properties: { ...applicationSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true } : allSettingsSchema;
|
||||
const machineSettingsSchema: IJSONSchema = { properties: { ...machineSettings.properties, ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true };
|
||||
const workspaceSettingsSchema: IJSONSchema = { properties: { ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true };
|
||||
|
||||
jsonRegistry.registerSchema(defaultSettingsSchemaId, allSettingsSchema);
|
||||
jsonRegistry.registerSchema(defaultSettingsSchemaId, defaultSettingsSchema);
|
||||
jsonRegistry.registerSchema(userSettingsSchemaId, userSettingsSchema);
|
||||
jsonRegistry.registerSchema(machineSettingsSchemaId, machineSettingsSchema);
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAction, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IAction, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, Separator, SubmenuAction } from 'vs/base/common/actions';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
@@ -13,7 +12,7 @@ import { getZoomFactor } from 'vs/base/browser/browser';
|
||||
import { unmnemonicLabel } from 'vs/base/common/labels';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IContextMenuDelegate, ContextSubMenu, IContextMenuEvent } from 'vs/base/browser/contextmenu';
|
||||
import { IContextMenuDelegate, IContextMenuEvent } from 'vs/base/browser/contextmenu';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu';
|
||||
@@ -26,6 +25,7 @@ import { ContextMenuService as HTMLContextMenuService } from 'vs/platform/contex
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { stripCodicons } from 'vs/base/common/codicons';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
|
||||
export class ContextMenuService extends Disposable implements IContextMenuService {
|
||||
|
||||
@@ -124,24 +124,28 @@ class NativeContextMenuService extends Disposable implements IContextMenuService
|
||||
}
|
||||
}
|
||||
|
||||
private createMenu(delegate: IContextMenuDelegate, entries: ReadonlyArray<IAction | ContextSubMenu>, onHide: () => void): IContextMenuItem[] {
|
||||
private createMenu(delegate: IContextMenuDelegate, entries: IAction[], onHide: () => void, submenuIds = new Set<string>()): IContextMenuItem[] {
|
||||
const actionRunner = delegate.actionRunner || new ActionRunner();
|
||||
|
||||
return entries.map(entry => this.createMenuItem(delegate, entry, actionRunner, onHide));
|
||||
return coalesce(entries.map(entry => this.createMenuItem(delegate, entry, actionRunner, onHide, submenuIds)));
|
||||
}
|
||||
|
||||
private createMenuItem(delegate: IContextMenuDelegate, entry: IAction | ContextSubMenu, actionRunner: IActionRunner, onHide: () => void): IContextMenuItem {
|
||||
|
||||
private createMenuItem(delegate: IContextMenuDelegate, entry: IAction, actionRunner: IActionRunner, onHide: () => void, submenuIds: Set<string>): IContextMenuItem | undefined {
|
||||
// Separator
|
||||
if (entry instanceof Separator) {
|
||||
return { type: 'separator' };
|
||||
}
|
||||
|
||||
// Submenu
|
||||
if (entry instanceof ContextSubMenu) {
|
||||
if (entry instanceof SubmenuAction) {
|
||||
if (submenuIds.has(entry.id)) {
|
||||
console.warn(`Found submenu cycle: ${entry.id}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const actions = Array.isArray(entry.actions) ? entry.actions : entry.actions();
|
||||
return {
|
||||
label: unmnemonicLabel(stripCodicons(entry.label)).trim(),
|
||||
submenu: this.createMenu(delegate, entry.entries, onHide)
|
||||
submenu: this.createMenu(delegate, actions, onHide, new Set([...submenuIds, entry.id]))
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const ITASExperimentService = createDecorator<ITASExperimentService>('TASExperimentService');
|
||||
|
||||
export interface ITASExperimentService {
|
||||
readonly _serviceBrand: undefined;
|
||||
getTreatment<T extends string | number | boolean>(name: string): Promise<T | undefined>;
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IKeyValueStorage, IExperimentationTelemetry, IExperimentationFilterProvider, ExperimentationService as TASClient } from 'tas-client';
|
||||
import { MementoObject, Memento } from 'vs/workbench/common/memento';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryData } from 'vs/base/common/actions';
|
||||
import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
const storageKey = 'VSCode.ABExp.FeatureData';
|
||||
const refetchInterval = 1000 * 60 * 30; // By default it's set up to 30 minutes.
|
||||
|
||||
class MementoKeyValueStorage implements IKeyValueStorage {
|
||||
constructor(private mementoObj: MementoObject) { }
|
||||
|
||||
async getValue<T>(key: string, defaultValue?: T | undefined): Promise<T | undefined> {
|
||||
const value = await this.mementoObj[key];
|
||||
return value || defaultValue;
|
||||
}
|
||||
|
||||
setValue<T>(key: string, value: T): void {
|
||||
this.mementoObj[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
class ExperimentServiceTelemetry implements IExperimentationTelemetry {
|
||||
constructor(private telemetryService: ITelemetryService) { }
|
||||
|
||||
// __GDPR__COMMON__ "VSCode.ABExp.Features" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
// __GDPR__COMMON__ "abexp.assignmentcontext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
setSharedProperty(name: string, value: string): void {
|
||||
this.telemetryService.setExperimentProperty(name, value);
|
||||
}
|
||||
|
||||
postEvent(eventName: string, props: Map<string, string>): void {
|
||||
const data: ITelemetryData = {};
|
||||
for (const [key, value] of props.entries()) {
|
||||
data[key] = value;
|
||||
}
|
||||
|
||||
/* __GDPR__
|
||||
"query-expfeature" : {
|
||||
"ABExp.queriedFeature": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog(eventName, data);
|
||||
}
|
||||
}
|
||||
|
||||
class ExperimentServiceFilterProvider implements IExperimentationFilterProvider {
|
||||
constructor(
|
||||
private version: string,
|
||||
private appName: string,
|
||||
private machineId: string,
|
||||
private targetPopulation: TargetPopulation
|
||||
) { }
|
||||
|
||||
getFilterValue(filter: string): string | null {
|
||||
switch (filter) {
|
||||
case Filters.ApplicationVersion:
|
||||
return this.version; // productService.version
|
||||
case Filters.Build:
|
||||
return this.appName; // productService.nameLong
|
||||
case Filters.ClientId:
|
||||
return this.machineId;
|
||||
case Filters.Language:
|
||||
return platform.language;
|
||||
case Filters.ExtensionName:
|
||||
return 'vscode-core'; // always return vscode-core for exp service
|
||||
case Filters.TargetPopulation:
|
||||
return this.targetPopulation;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
getFilters(): Map<string, any> {
|
||||
let filters: Map<string, any> = new Map<string, any>();
|
||||
let filterValues = Object.values(Filters);
|
||||
for (let value of filterValues) {
|
||||
filters.set(value, this.getFilterValue(value));
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Based upon the official VSCode currently existing filters in the
|
||||
ExP backend for the VSCode cluster.
|
||||
https://experimentation.visualstudio.com/Analysis%20and%20Experimentation/_git/AnE.ExP.TAS.TachyonHost.Configuration?path=%2FConfigurations%2Fvscode%2Fvscode.json&version=GBmaster
|
||||
"X-MSEdge-Market": "detection.market",
|
||||
"X-FD-Corpnet": "detection.corpnet",
|
||||
"X-VSCode–AppVersion": "appversion",
|
||||
"X-VSCode-Build": "build",
|
||||
"X-MSEdge-ClientId": "clientid",
|
||||
"X-VSCode-ExtensionName": "extensionname",
|
||||
"X-VSCode-TargetPopulation": "targetpopulation",
|
||||
"X-VSCode-Language": "language"
|
||||
*/
|
||||
|
||||
enum Filters {
|
||||
/**
|
||||
* The market in which the extension is distributed.
|
||||
*/
|
||||
Market = 'X-MSEdge-Market',
|
||||
|
||||
/**
|
||||
* The corporation network.
|
||||
*/
|
||||
CorpNet = 'X-FD-Corpnet',
|
||||
|
||||
/**
|
||||
* Version of the application which uses experimentation service.
|
||||
*/
|
||||
ApplicationVersion = 'X-VSCode-AppVersion',
|
||||
|
||||
/**
|
||||
* Insiders vs Stable.
|
||||
*/
|
||||
Build = 'X-VSCode-Build',
|
||||
|
||||
/**
|
||||
* Client Id which is used as primary unit for the experimentation.
|
||||
*/
|
||||
ClientId = 'X-MSEdge-ClientId',
|
||||
|
||||
/**
|
||||
* Extension header.
|
||||
*/
|
||||
ExtensionName = 'X-VSCode-ExtensionName',
|
||||
|
||||
/**
|
||||
* The language in use by VS Code
|
||||
*/
|
||||
Language = 'X-VSCode-Language',
|
||||
|
||||
/**
|
||||
* The target population.
|
||||
* This is used to separate internal, early preview, GA, etc.
|
||||
*/
|
||||
TargetPopulation = 'X-VSCode-TargetPopulation',
|
||||
}
|
||||
|
||||
enum TargetPopulation {
|
||||
Team = 'team',
|
||||
Internal = 'internal',
|
||||
Insiders = 'insider',
|
||||
Public = 'public',
|
||||
}
|
||||
|
||||
export class ExperimentService implements ITASExperimentService {
|
||||
_serviceBrand: undefined;
|
||||
private tasClient: Promise<TASClient> | undefined;
|
||||
private static MEMENTO_ID = 'experiment.service.memento';
|
||||
|
||||
constructor(
|
||||
@IProductService private productService: IProductService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IStorageService private storageService: IStorageService
|
||||
) {
|
||||
|
||||
if (this.productService.tasConfig) {
|
||||
this.tasClient = this.setupTASClient();
|
||||
}
|
||||
}
|
||||
|
||||
async getTreatment<T extends string | number | boolean>(name: string): Promise<T | undefined> {
|
||||
if (!this.tasClient) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (await this.tasClient).getTreatmentVariable<T>('vscode', name);
|
||||
}
|
||||
|
||||
private async setupTASClient(): Promise<TASClient> {
|
||||
const telemetryInfo = await this.telemetryService.getTelemetryInfo();
|
||||
const targetPopulation = telemetryInfo.msftInternal ? TargetPopulation.Internal : (this.productService.quality === 'stable' ? TargetPopulation.Public : TargetPopulation.Insiders);
|
||||
const machineId = telemetryInfo.machineId;
|
||||
const filterProvider = new ExperimentServiceFilterProvider(
|
||||
this.productService.version,
|
||||
this.productService.nameLong,
|
||||
machineId,
|
||||
targetPopulation
|
||||
);
|
||||
|
||||
const memento = new Memento(ExperimentService.MEMENTO_ID, this.storageService);
|
||||
const keyValueStorage = new MementoKeyValueStorage(memento.getMemento(StorageScope.GLOBAL));
|
||||
|
||||
const telemetry = new ExperimentServiceTelemetry(this.telemetryService);
|
||||
|
||||
const tasConfig = this.productService.tasConfig!;
|
||||
const tasClient = new TASClient({
|
||||
filterProviders: [filterProvider],
|
||||
telemetry: telemetry,
|
||||
storageKey: storageKey,
|
||||
keyValueStorage: keyValueStorage,
|
||||
featuresTelemetryPropertyName: tasConfig.featuresTelemetryPropertyName,
|
||||
assignmentContextTelemetryPropertyName: tasConfig.assignmentContextTelemetryPropertyName,
|
||||
telemetryEventName: tasConfig.telemetryEventName,
|
||||
endpoint: tasConfig.endpoint,
|
||||
refetchInterval: refetchInterval,
|
||||
});
|
||||
|
||||
await tasClient.initializePromise;
|
||||
return tasClient;
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(ITASExperimentService, ExperimentService, false);
|
||||
|
||||
@@ -65,6 +65,10 @@ export class TelemetryService extends Disposable implements ITelemetryService {
|
||||
return this.impl.setEnabled(value);
|
||||
}
|
||||
|
||||
setExperimentProperty(name: string, value: string): void {
|
||||
return this.impl.setExperimentProperty(name, value);
|
||||
}
|
||||
|
||||
get isOptedIn(): boolean {
|
||||
return this.impl.isOptedIn;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ export class TelemetryService extends Disposable implements ITelemetryService {
|
||||
return this.impl.setEnabled(value);
|
||||
}
|
||||
|
||||
setExperimentProperty(name: string, value: string): void {
|
||||
return this.impl.setExperimentProperty(name, value);
|
||||
}
|
||||
|
||||
get isOptedIn(): boolean {
|
||||
return this.impl.isOptedIn;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user