提交 6eaed50e 编写于 作者: R Rob Lourens

Support sending feedback on settings search results

上级 626ef93c
......@@ -449,7 +449,7 @@ class PreferencesRenderers extends Disposable {
const editablePreferencesFilteredGroups = editablePreferencesFilterResult ? editablePreferencesFilterResult.filteredGroups : this._getAllPreferences(this._editablePreferencesRenderer);
const consolidatedSettings = this._consolidateSettings(editablePreferencesFilteredGroups, defaultPreferencesFilteredGroups);
if (defaultPreferencesFilterResult && defaultPreferencesFilterResult.scores) {
if (defaultPreferencesFilterResult && defaultPreferencesFilterResult.remoteResult) {
// Disable navigation for remote settings search
this._settingsNavigator = null;
} else {
......
......@@ -308,7 +308,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
public filterPreferences(filterResult: IFilterResult): void {
this.filterResult = filterResult;
if (filterResult) {
if (filterResult.scores) {
if (filterResult.remoteResult) {
this.filteredMatchesRenderer.render(null);
this.settingsGroupTitleRenderer.render(null);
} else {
......@@ -582,7 +582,7 @@ export class MostRelevantMatchesRenderer extends Disposable implements HiddenAre
public render(result: IFilterResult): void {
this.hiddenAreas = [];
if (result && result.matches.length && result.scores) {
if (result && result.matches.length && result.remoteResult) {
const settingsTextEndLine = this.renderResults(result);
this.hiddenAreas = [{
......@@ -606,7 +606,7 @@ export class MostRelevantMatchesRenderer extends Disposable implements HiddenAre
this.hiddenAreas = [];
this.editor.updateOptions({ readOnly: false });
const relevantRanges = this.getOrderedSettingRanges(result.filteredGroups, result.allGroups, result.scores, this.editor.getModel());
const relevantRanges = this.getOrderedSettingRanges(result.filteredGroups, result.allGroups, result.remoteResult.scores, this.editor.getModel());
let totalLines = 0;
const settingsValue = relevantRanges.map(visibleRange => {
const settingLines = (visibleRange.endLineNumber - visibleRange.startLineNumber) + 1;
......@@ -702,17 +702,22 @@ export class MostRelevantMatchesRenderer extends Disposable implements HiddenAre
}
export class FeedbackWidgetRenderer extends Disposable {
private static COMMENT_TEXT = '// Reorder and modify the below results to match your expectations. Replace this comment with any text feedback.';
private _feedbackWidget: FloatingClickWidget;
private _currentResult: IFilterResult;
constructor(private editor: ICodeEditor,
@IInstantiationService private instantiationService: IInstantiationService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@ITelemetryService private telemetryService: ITelemetryService
) {
super();
}
public render(result: IFilterResult): void {
if (result && result.scores) {
this._currentResult = result;
if (result && result.remoteResult) {
this.showWidget();
} else if (this._feedbackWidget) {
this.disposeWidget();
......@@ -728,15 +733,54 @@ export class FeedbackWidgetRenderer extends Disposable {
}
private getFeedback(): void {
this.editorService.openEditor({ contents: 'test' }, true).then(feedbackEditor => {
const result = this._currentResult;
const actualResults = Object.keys(result.remoteResult.scores)
.sort((a, b) => result.remoteResult.scores[b] - result.remoteResult.scores[a]);
const actualResultText = actualResults.join('\n');
const contents = FeedbackWidgetRenderer.COMMENT_TEXT + '\n' + actualResultText;
this.editorService.openEditor({ contents }, /*sideBySide=*/true).then(feedbackEditor => {
const sendFeedbackWidget = this._register(this.instantiationService.createInstance(FloatingClickWidget, feedbackEditor.getControl(), 'Send feedback', null));
this._register(sendFeedbackWidget.onClick(() => this.sendFeedback()));
sendFeedbackWidget.render();
this._register(sendFeedbackWidget.onClick(() => {
this.sendFeedback(feedbackEditor.getControl() as ICodeEditor, result, actualResults);
sendFeedbackWidget.dispose();
}));
});
}
private sendFeedback(): void {
private sendFeedback(feedbackEditor: ICodeEditor, result: IFilterResult, actualResults: string[]): void {
const model = feedbackEditor.getModel();
const expectedResults = model.getLinesContent().slice();
const commentLines = [];
while (strings.startsWith(expectedResults[0], '//')) {
commentLines.push(expectedResults.shift());
}
const commentText = commentLines.join('\n');
/* __GDPR__
"settingsSearchResultFeedback" : {
"query" : { "classification": "CustomContent", "purpose": "FeatureInsight" },
"userComment" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"actualResults" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"expectedResults" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"url" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"timestamp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('settingsSearchResultFeedback', {
query: result.query,
userComment: commentText === FeedbackWidgetRenderer.COMMENT_TEXT ? undefined : commentText,
actualResults,
expectedResults,
url: result.remoteResult.url,
duration: result.remoteResult.duration,
timestamp: result.remoteResult.timestamp
});
console.log('Feedback sent successfully');
}
private disposeWidget(): void {
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import { ISettingsEditorModel, IFilterResult, ISetting, ISettingsGroup, IWorkbenchSettingsConfiguration } from 'vs/workbench/parts/preferences/common/preferences';
import { ISettingsEditorModel, IFilterResult, ISetting, ISettingsGroup, IWorkbenchSettingsConfiguration, IRemoteFilterResult } from 'vs/workbench/parts/preferences/common/preferences';
import { IRange, Range } from 'vs/editor/common/core/range';
import { distinct } from 'vs/base/common/arrays';
import * as strings from 'vs/base/common/strings';
......@@ -75,17 +75,17 @@ class LocalSearchProvider {
class RemoteSearchProvider {
private _filter: string;
private _remoteSearchP: TPromise<{ [key: string]: number }>;
private _remoteSearchP: TPromise<IRemoteFilterResult>;
constructor(filter: string) {
this._filter = filter;
this._remoteSearchP = filter ? getSettingsFromBing(filter) : TPromise.wrap({});
this._remoteSearchP = filter ? getSettingsFromBing(filter) : TPromise.wrap(null);
}
filterPreferences(preferencesModel: ISettingsEditorModel): TPromise<IFilterResult> {
return this._remoteSearchP.then(settingsSet => {
return this._remoteSearchP.then(remoteResult => {
const settingFilter = (setting: ISetting) => {
if (!!settingsSet[setting.key]) {
if (!!remoteResult.scores[setting.key]) {
const settingMatches = new SettingMatches(this._filter, setting, (filter, setting) => preferencesModel.findValueMatches(filter, setting)).matches;
if (settingMatches.length) {
return settingMatches;
......@@ -97,14 +97,18 @@ class RemoteSearchProvider {
}
};
const result = preferencesModel.filterSettings(this._filter, group => null, settingFilter);
result.scores = settingsSet;
return result;
if (remoteResult) {
const result = preferencesModel.filterSettings(this._filter, group => null, settingFilter);
result.remoteResult = remoteResult;
return result;
} else {
return null;
}
});
}
}
function getSettingsFromBing(filter: string): TPromise<{ [key: string]: number }> {
function getSettingsFromBing(filter: string): TPromise<IRemoteFilterResult> {
const url = prepareUrl(filter);
console.log('fetching: ' + url);
const start = Date.now();
......@@ -117,22 +121,29 @@ function getSettingsFromBing(filter: string): TPromise<{ [key: string]: number }
})
.then(r => r.json())
.then(result => {
console.log('time: ' + (Date.now() - start) / 1000);
const timestamp = Date.now();
const duration = timestamp - start;
console.log('time: ' + duration / 1000);
const suggestions = (result.value || [])
.map(r => ({
name: r.Setting,
score: r['@search.score']
}));
const suggSet = Object.create(null);
const scores = Object.create(null);
suggestions.forEach(s => {
const name = s.name
.replace(/^"/, '')
.replace(/"$/, '');
suggSet[name] = s.score;
scores[name] = s.score;
});
return suggSet;
return <IRemoteFilterResult>{
url,
scores,
duration,
timestamp
};
});
return TPromise.as(p as any);
......
......@@ -50,10 +50,18 @@ export interface ISetting {
}
export interface IFilterResult {
query: string;
filteredGroups: ISettingsGroup[];
allGroups: ISettingsGroup[];
matches: IRange[];
scores?: { [key: string]: number };
remoteResult?: IRemoteFilterResult;
}
export interface IRemoteFilterResult {
url: string;
timestamp: number;
duration: number;
scores: { [key: string]: number };
}
export interface IPreferencesEditorModel<T> {
......
......@@ -37,7 +37,8 @@ export abstract class AbstractSettingsModel extends EditorModel {
return {
filteredGroups: allGroups,
allGroups,
matches: []
matches: [],
query: filter
};
}
......@@ -46,7 +47,8 @@ export abstract class AbstractSettingsModel extends EditorModel {
return {
filteredGroups: [group],
allGroups,
matches: []
matches: [],
query: filter
};
}
......@@ -85,7 +87,7 @@ export abstract class AbstractSettingsModel extends EditorModel {
});
}
}
return { filteredGroups, matches, allGroups };
return { filteredGroups, matches, allGroups, query: filter };
}
private filterByGroupTerm(filter: string): ISettingsGroup {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册