提交 1800414c 编写于 作者: R Rob Lourens

#3355 - implement setting change telemetry

上级 61aa2e58
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom'; import * as DOM from 'vs/base/browser/dom';
import * as arrays from 'vs/base/common/arrays';
import * as objects from 'vs/base/common/objects';
import { Button } from 'vs/base/browser/ui/button/button';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { List } from 'vs/base/browser/ui/list/listWidget'; import { List } from 'vs/base/browser/ui/list/listWidget';
...@@ -11,6 +14,7 @@ import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; ...@@ -11,6 +14,7 @@ import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
import { IAction } from 'vs/base/common/actions'; import { IAction } from 'vs/base/common/actions';
import { Delayer, ThrottledDelayer } from 'vs/base/common/async'; import { Delayer, ThrottledDelayer } from 'vs/base/common/async';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
...@@ -19,23 +23,21 @@ import { localize } from 'vs/nls'; ...@@ -19,23 +23,21 @@ import { localize } from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IEditor } from 'vs/platform/editor/common/editor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { ILogService } from 'vs/platform/log/common/log';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { attachInputBoxStyler, attachSelectBoxStyler, attachButtonStyler } from 'vs/platform/theme/common/styler'; import { registerColor } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions } from 'vs/workbench/common/editor'; import { EditorOptions } from 'vs/workbench/common/editor';
import { SearchWidget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { SearchWidget, SettingsTargetsWidget, SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { IPreferencesService, ISetting, ISettingsEditorModel, ISearchResult } from 'vs/workbench/services/preferences/common/preferences'; import { IPreferencesService, ISearchResult, ISetting, ISettingsEditorModel } from 'vs/workbench/services/preferences/common/preferences';
import { PreferencesEditorInput2 } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { PreferencesEditorInput2 } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
import { DefaultSettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { DefaultSettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
import { Button } from 'vs/base/browser/ui/button/button';
import { IPreferencesSearchService, ISearchProvider } from '../common/preferences'; import { IPreferencesSearchService, ISearchProvider } from '../common/preferences';
import { isPromiseCanceledError, getErrorMessage } from 'vs/base/common/errors';
import { ILogService } from 'vs/platform/log/common/log';
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
import { IEditor } from 'vs/platform/editor/common/editor';
const SETTINGS_ENTRY_TEMPLATE_ID = 'settings.entry.template'; const SETTINGS_ENTRY_TEMPLATE_ID = 'settings.entry.template';
const SETTINGS_GROUP_ENTRY_TEMPLATE_ID = 'settings.group.template'; const SETTINGS_GROUP_ENTRY_TEMPLATE_ID = 'settings.group.template';
...@@ -106,6 +108,7 @@ export class SettingsEditor2 extends BaseEditor { ...@@ -106,6 +108,7 @@ export class SettingsEditor2 extends BaseEditor {
private dimension: DOM.Dimension; private dimension: DOM.Dimension;
private searchFocusContextKey: IContextKey<boolean>; private searchFocusContextKey: IContextKey<boolean>;
private delayedModifyLogging: Delayer<void>;
private delayedFilterLogging: Delayer<void>; private delayedFilterLogging: Delayer<void>;
private localSearchDelayer: Delayer<void>; private localSearchDelayer: Delayer<void>;
private remoteSearchThrottle: ThrottledDelayer<void>; private remoteSearchThrottle: ThrottledDelayer<void>;
...@@ -113,7 +116,8 @@ export class SettingsEditor2 extends BaseEditor { ...@@ -113,7 +116,8 @@ export class SettingsEditor2 extends BaseEditor {
private currentLocalSearchProvider: ISearchProvider; private currentLocalSearchProvider: ISearchProvider;
private currentRemoteSearchProvider: ISearchProvider; private currentRemoteSearchProvider: ISearchProvider;
private searchResults: ISearchResult[]; private searchResultModel: SearchResultModel;
private pendingSettingModifiedReport: { key: string, value: any };
constructor( constructor(
@ITelemetryService telemetryService: ITelemetryService, @ITelemetryService telemetryService: ITelemetryService,
...@@ -126,9 +130,11 @@ export class SettingsEditor2 extends BaseEditor { ...@@ -126,9 +130,11 @@ export class SettingsEditor2 extends BaseEditor {
@ILogService private logService: ILogService @ILogService private logService: ILogService
) { ) {
super(SettingsEditor2.ID, telemetryService, themeService); super(SettingsEditor2.ID, telemetryService, themeService);
this.delayedModifyLogging = new Delayer<void>(1000);
this.delayedFilterLogging = new Delayer<void>(1000); this.delayedFilterLogging = new Delayer<void>(1000);
this.localSearchDelayer = new Delayer(100); this.localSearchDelayer = new Delayer(100);
this.remoteSearchThrottle = new ThrottledDelayer(200); this.remoteSearchThrottle = new ThrottledDelayer(200);
this.searchResultModel = new SearchResultModel();
this._register(configurationService.onDidChangeConfiguration(() => this.render())); this._register(configurationService.onDidChangeConfiguration(() => this.render()));
} }
...@@ -275,6 +281,79 @@ export class SettingsEditor2 extends BaseEditor { ...@@ -275,6 +281,79 @@ export class SettingsEditor2 extends BaseEditor {
private onDidChangeSetting(key: string, value: any): void { private onDidChangeSetting(key: string, value: any): void {
// ConfigurationService displays the error // ConfigurationService displays the error
this.configurationService.updateValue(key, value, <ConfigurationTarget>this.settingsTargetsWidget.settingsTarget); this.configurationService.updateValue(key, value, <ConfigurationTarget>this.settingsTargetsWidget.settingsTarget);
const reportModifiedProps = {
key,
query: this.searchWidget.getValue(),
searchResults: this.searchResultModel.getUniqueResults(),
rawResults: this.searchResultModel.getRawResults(),
showConfiguredOnly: this.showConfiguredSettingsOnly,
isReset: typeof value === 'undefined',
settingsTarget: this.settingsTargetsWidget.settingsTarget as SettingsTarget
};
if (this.pendingSettingModifiedReport && key !== this.pendingSettingModifiedReport.key) {
this.reportModifiedSetting(reportModifiedProps);
}
this.pendingSettingModifiedReport = { key, value };
this.delayedModifyLogging.trigger(() => this.reportModifiedSetting(reportModifiedProps));
}
private reportModifiedSetting(props: { key: string, query: string, searchResults: ISearchResult[], rawResults: ISearchResult[], showConfiguredOnly: boolean, isReset: boolean, settingsTarget: SettingsTarget }): void {
this.pendingSettingModifiedReport = null;
const remoteResult = props.searchResults && props.searchResults[SearchResultIdx.Remote];
const localResult = props.searchResults && props.searchResults[SearchResultIdx.Local];
let groupId = undefined;
let nlpIndex = undefined;
let displayIndex = undefined;
if (props.searchResults) {
const localIndex = arrays.firstIndex(localResult.filterMatches, m => m.setting.key === props.key);
groupId = localIndex >= 0 ?
'local' :
'remote';
displayIndex = localIndex >= 0 ?
localIndex :
remoteResult && (arrays.firstIndex(remoteResult.filterMatches, m => m.setting.key === props.key) + localResult.filterMatches.length);
const rawResults = this.searchResultModel.getRawResults();
if (rawResults[SearchResultIdx.Remote]) {
const _nlpIndex = arrays.firstIndex(rawResults[SearchResultIdx.Remote].filterMatches, m => m.setting.key === props.key);
nlpIndex = _nlpIndex >= 0 ? _nlpIndex : undefined;
}
}
const reportedTarget = props.settingsTarget === ConfigurationTarget.USER ? 'user' :
props.settingsTarget === ConfigurationTarget.WORKSPACE ? 'workspace' :
'folder';
const data = {
key: props.key,
query: props.query,
groupId,
nlpIndex,
displayIndex,
showConfiguredOnly: props.showConfiguredOnly,
isReset: props.isReset,
target: reportedTarget
};
/* __GDPR__
"settingEditor.settingModified" : {
"key" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"query" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"groupId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"nlpIndex" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"displayIndex" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"showConfiguredOnly" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isReset" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"target" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('settingEditor.settingModified', data);
} }
private render(): TPromise<any> { private render(): TPromise<any> {
...@@ -290,8 +369,8 @@ export class SettingsEditor2 extends BaseEditor { ...@@ -290,8 +369,8 @@ export class SettingsEditor2 extends BaseEditor {
const query = this.searchWidget.getValue().trim(); const query = this.searchWidget.getValue().trim();
this.delayedFilterLogging.cancel(); this.delayedFilterLogging.cancel();
this.triggerSearch(query).then(() => { this.triggerSearch(query).then(() => {
if (query && this.searchResults) { if (query && this.searchResultModel.hasResults()) {
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(query, this.searchResults)); this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(query, this.searchResultModel.getUniqueResults()));
} }
}); });
} }
...@@ -307,7 +386,7 @@ export class SettingsEditor2 extends BaseEditor { ...@@ -307,7 +386,7 @@ export class SettingsEditor2 extends BaseEditor {
this.localSearchDelayer.cancel(); this.localSearchDelayer.cancel();
this.remoteSearchThrottle.cancel(); this.remoteSearchThrottle.cancel();
this.searchResults = null; this.searchResultModel.clear();
this.renderEntries(); this.renderEntries();
return TPromise.wrap(null); return TPromise.wrap(null);
} }
...@@ -324,14 +403,8 @@ export class SettingsEditor2 extends BaseEditor { ...@@ -324,14 +403,8 @@ export class SettingsEditor2 extends BaseEditor {
const counts = {}; const counts = {};
const filterResult = results[SearchResultIdx.Local]; const filterResult = results[SearchResultIdx.Local];
counts['filterResult'] = filterResult.filterMatches.length; counts['filterResult'] = filterResult.filterMatches.length;
if (nlpResult && nlpResult.filterMatches.length) { if (nlpResult) {
const localMatchKeys = new Set(); counts['nlpResult'] = nlpResult.filterMatches.length;
filterResult.filterMatches
.forEach(m => localMatchKeys.add(m.setting.key));
counts['nlpResult'] = nlpResult.filterMatches
.filter(m => !localMatchKeys.has(m.setting.key))
.length;
} }
const requestCount = nlpMetadata && nlpMetadata.requestCount; const requestCount = nlpMetadata && nlpMetadata.requestCount;
...@@ -370,8 +443,7 @@ export class SettingsEditor2 extends BaseEditor { ...@@ -370,8 +443,7 @@ export class SettingsEditor2 extends BaseEditor {
return TPromise.join(filterPs).then(results => { return TPromise.join(filterPs).then(results => {
const [result] = results; const [result] = results;
this.searchResults = this.searchResults || []; this.searchResultModel.setResult(type, result);
this.searchResults[type] = result;
return this.render(); return this.render();
}); });
} }
...@@ -431,8 +503,8 @@ export class SettingsEditor2 extends BaseEditor { ...@@ -431,8 +503,8 @@ export class SettingsEditor2 extends BaseEditor {
const focusedRowItem = DOM.findParentWithClass(<HTMLElement>document.activeElement, 'monaco-list-row'); const focusedRowItem = DOM.findParentWithClass(<HTMLElement>document.activeElement, 'monaco-list-row');
const focusedRowId = focusedRowItem && focusedRowItem.id; const focusedRowId = focusedRowItem && focusedRowItem.id;
const entries = this.searchResults ? const entries = this.searchResultModel.hasResults() ?
this.getEntriesFromSearch(this.searchResults) : this.getEntriesFromSearch(this.searchResultModel.getUniqueResults()) :
this.getEntriesFromModel(); this.getEntriesFromModel();
this.settingsList.splice(0, this.settingsList.length, entries); this.settingsList.splice(0, this.settingsList.length, entries);
...@@ -775,3 +847,51 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { ...@@ -775,3 +847,51 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
collector.addRule(`.settings-editor > .settings-body > .settings-list-container .monaco-list-row.is-configured .setting-value-checkbox::after { background-color: ${configuredItemBackgroundColor}; }`); collector.addRule(`.settings-editor > .settings-body > .settings-list-container .monaco-list-row.is-configured .setting-value-checkbox::after { background-color: ${configuredItemBackgroundColor}; }`);
} }
}); });
class SearchResultModel {
private rawSearchResults: ISearchResult[];
private cachedUniqueSearchResults: ISearchResult[];
getUniqueResults(): ISearchResult[] {
if (this.cachedUniqueSearchResults) {
return this.cachedUniqueSearchResults;
}
if (!this.rawSearchResults) {
return null;
}
const localMatchKeys = new Set();
const localResult = objects.deepClone(this.rawSearchResults[SearchResultIdx.Local]);
if (localResult) {
localResult.filterMatches.forEach(m => localMatchKeys.add(m.setting.key));
}
const remoteResult = objects.deepClone(this.rawSearchResults[SearchResultIdx.Remote]);
if (remoteResult) {
remoteResult.filterMatches = remoteResult.filterMatches.filter(m => !localMatchKeys.has(m.setting.key));
}
this.cachedUniqueSearchResults = [localResult, remoteResult];
return this.cachedUniqueSearchResults;
}
getRawResults(): ISearchResult[] {
return this.rawSearchResults;
}
hasResults(): boolean {
return !!this.rawSearchResults;
}
clear(): void {
this.cachedUniqueSearchResults = null;
this.rawSearchResults = null;
}
setResult(type: SearchResultIdx, result: ISearchResult): void {
this.cachedUniqueSearchResults = null;
this.rawSearchResults = this.rawSearchResults || [];
this.rawSearchResults[type] = result;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册