提交 db5143d5 编写于 作者: R Rob Lourens

Fix #43268 - implement a special issue reporter mode for settings search

上级 3b869657
......@@ -11,6 +11,7 @@ import { localize } from 'vs/nls';
import { $ } from 'vs/base/browser/dom';
import * as collections from 'vs/base/common/collections';
import * as browser from 'vs/base/browser/browser';
import { escape } from 'vs/base/common/strings';
import product from 'vs/platform/node/product';
import pkg from 'vs/platform/node/package';
import * as os from 'os';
......@@ -30,7 +31,7 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper
import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel';
import { IssueReporterData, IssueReporterStyles, IssueType } from 'vs/platform/issue/common/issue';
import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData } from 'vs/platform/issue/common/issue';
import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage';
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { debounce } from 'vs/base/common/decorators';
......@@ -67,7 +68,8 @@ export class IssueReporter extends Disposable {
includeSystemInfo: true,
includeWorkspaceInfo: true,
includeProcessInfo: true,
includeExtensions: true,
includeSearchedExtensions: true,
includeSettingsSearchDetails: true,
versionInfo: {
vscodeVersion: `${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`,
os: `${os.type()} ${os.arch()} ${os.release()}`
......@@ -101,14 +103,18 @@ export class IssueReporter extends Disposable {
show(document.getElementById('english'));
}
this.setUpTypes();
this.setEventHandlers();
this.applyZoom(configuration.data.zoomLevel);
this.applyStyles(configuration.data.styles);
this.handleExtensionData(configuration.data.enabledExtensions);
if (configuration.data.issueType === IssueType.SettingsSearchIssue) {
this.handleSettingsSearchData(<ISettingsSearchIssueReporterData>configuration.data);
}
}
render(): void {
(<HTMLSelectElement>document.getElementById('issue-type')).value = this.issueReporterModel.getData().issueType.toString();
this.renderBlocks();
}
......@@ -203,6 +209,46 @@ export class IssueReporter extends Disposable {
}
}
private handleSettingsSearchData(data: ISettingsSearchIssueReporterData): void {
this.issueReporterModel.update({
actualSearchResults: data.actualSearchResults,
query: data.query,
filterResultCount: data.filterResultCount
});
this.updateSearchedExtensionTable(data.enabledExtensions);
this.updateSettingsSearchDetails(data);
}
private updateSettingsSearchDetails(data: ISettingsSearchIssueReporterData): void {
const target = document.querySelector('.block-settingsSearchResults .block-info');
const details = `
<div class='block-settingsSearchResults-details'>
<div>Query: "${data.query}"</div>
<div>Literal match count: ${data.filterResultCount}</div>
</div>
`;
let table = `
<tr>
<th>Setting</th>
<th>Extension</th>
<th>Score</th>
</tr>`;
data.actualSearchResults
.forEach(setting => {
table += `
<tr>
<td>${setting.key}</td>
<td>${setting.extensionId}</td>
<td>${String(setting.score).slice(0, 5)}</td>
</tr>`;
});
target.innerHTML = `${details}<table>${table}</table>`;
}
private initServices(configuration: IWindowConfiguration): void {
const serviceCollection = new ServiceCollection();
const mainProcessClient = new ElectronIPCClient(String(`window${configuration.windowId}`));
......@@ -238,7 +284,7 @@ export class IssueReporter extends Disposable {
this.render();
});
['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions'].forEach(elementId => {
['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeSearchedExtensions', 'includeSettingsSearchDetails'].forEach(elementId => {
document.getElementById(elementId).addEventListener('click', (event: Event) => {
event.stopPropagation();
this.issueReporterModel.update({ [elementId]: !this.issueReporterModel.getData()[elementId] });
......@@ -351,6 +397,10 @@ export class IssueReporter extends Disposable {
return true;
}
if (issueType === IssueType.SettingsSearchIssue) {
return true;
}
return false;
}
......@@ -425,6 +475,25 @@ export class IssueReporter extends Disposable {
this.telemetryService.publicLog('issueReporterSearchError', { message: error.message });
}
private setUpTypes(): void {
const makeOption = (issueType: IssueType, description: string) => `<option value="${issueType.valueOf()}">${escape(description)}</option>`;
const typeSelect = (<HTMLSelectElement>document.getElementById('issue-type'));
const { issueType } = this.issueReporterModel.getData();
if (issueType === IssueType.SettingsSearchIssue) {
typeSelect.innerHTML = makeOption(IssueType.SettingsSearchIssue, localize('settingsSearchIssue', "Settings Search Issue"));
typeSelect.disabled = true;
} else {
typeSelect.innerHTML = [
makeOption(IssueType.Bug, localize('bugReporter', "Bug Report")),
makeOption(IssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue")),
makeOption(IssueType.FeatureRequest, localize('featureRequest', "Feature Request"))
].join('\n');
}
typeSelect.value = issueType.toString();
}
private renderBlocks(): void {
// Depending on Issue Type, we render different blocks and text
const { issueType } = this.issueReporterModel.getData();
......@@ -432,16 +501,24 @@ export class IssueReporter extends Disposable {
const processBlock = document.querySelector('.block-process');
const workspaceBlock = document.querySelector('.block-workspace');
const extensionsBlock = document.querySelector('.block-extensions');
const disabledExtensions = document.getElementById('disabledExtensions');
const searchedExtensionsBlock = document.querySelector('.block-searchedExtensions');
const settingsSearchResultsBlock = document.querySelector('.block-settingsSearchResults');
const disabledExtensions = document.getElementById('disabledExtensions');
const descriptionTitle = document.getElementById('issue-description-label');
const descriptionSubtitle = document.getElementById('issue-description-subtitle');
// Hide all by default
hide(systemBlock);
hide(processBlock);
hide(workspaceBlock);
hide(extensionsBlock);
hide(searchedExtensionsBlock);
hide(settingsSearchResultsBlock);
hide(disabledExtensions);
if (issueType === IssueType.Bug) {
show(systemBlock);
hide(processBlock);
hide(workspaceBlock);
show(extensionsBlock);
show(disabledExtensions);
......@@ -456,15 +533,15 @@ export class IssueReporter extends Disposable {
descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} <span class="required-input">*</span>`;
descriptionSubtitle.innerHTML = localize('performanceIssueDesciption', "When did this performance issue happen? Does it occur on startup or after a specific series of actions? We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.");
} else {
hide(systemBlock);
hide(processBlock);
hide(workspaceBlock);
hide(extensionsBlock);
hide(disabledExtensions);
} else if (issueType === IssueType.FeatureRequest) {
descriptionTitle.innerHTML = `${localize('description', "Description")} <span class="required-input">*</span>`;
descriptionSubtitle.innerHTML = localize('featureRequestDescription', "Please describe the feature you would like to see. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.");
} else if (issueType === IssueType.SettingsSearchIssue) {
show(searchedExtensionsBlock);
show(settingsSearchResultsBlock);
descriptionTitle.innerHTML = `${localize('expectedResults', "Expected Results")} <span class="required-input">*</span>`;
descriptionSubtitle.innerHTML = localize('settingsSearchResultsDescription', "Please list the results that you were expecting to see when you searched with this query. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.");
}
}
......@@ -591,6 +668,23 @@ export class IssueReporter extends Disposable {
return;
}
const table = this.getExtensionTableHtml(extensions);
target.innerHTML = `<table>${table}</table>${themeExclusionStr}`;
}
private updateSearchedExtensionTable(extensions: ILocalExtension[]): void {
const target = document.querySelector('.block-searchedExtensions .block-info');
if (!extensions.length) {
target.innerHTML = 'Extensions: none';
return;
}
const table = this.getExtensionTableHtml(extensions);
target.innerHTML = `<table>${table}</table>`;
}
private getExtensionTableHtml(extensions: ILocalExtension[]): string {
let table = `
<tr>
<th>Extension</th>
......@@ -598,16 +692,16 @@ export class IssueReporter extends Disposable {
<th>Version</th>
</tr>`;
extensions.forEach(extension => {
table += `
table += extensions.map(extension => {
return `
<tr>
<td>${extension.manifest.name}</td>
<td>${extension.manifest.publisher.substr(0, 3)}</td>
<td>${extension.manifest.version}</td>
</tr>`;
});
}).join('');
target.innerHTML = `<table>${table}</table>${themeExclusionStr}`;
return table;
}
}
......
......@@ -7,7 +7,7 @@
import { assign } from 'vs/base/common/objects';
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IssueType } from 'vs/platform/issue/common/issue';
import { IssueType, ISettingSearchResult } from 'vs/platform/issue/common/issue';
export interface IssueReporterData {
issueType?: IssueType;
......@@ -22,11 +22,16 @@ export interface IssueReporterData {
includeWorkspaceInfo?: boolean;
includeProcessInfo?: boolean;
includeExtensions?: boolean;
includeSearchedExtensions?: boolean;
includeSettingsSearchDetails?: boolean;
numberOfThemeExtesions?: number;
enabledNonThemeExtesions?: ILocalExtension[];
extensionsDisabled?: boolean;
reprosWithoutExtensions?: boolean;
actualSearchResults?: ISettingSearchResult[];
query?: string;
filterResultCount?: number;
}
export class IssueReporterModel {
......@@ -99,6 +104,17 @@ ${this.getInfos()}`;
info += this._data.reprosWithoutExtensions ? '\nReproduces without extensions' : '\nReproduces only with extensions';
}
if (this._data.issueType === IssueType.SettingsSearchIssue) {
if (this._data.includeSearchedExtensions) {
info += this.generateExtensionsMd();
}
if (this._data.includeSettingsSearchDetails) {
info += this.generateSettingSearchResultsMd();
info += '\n' + this.generateSettingsSearchResultDetailsMd();
}
}
return info;
}
......@@ -171,6 +187,35 @@ ${tableHeader}
${table}
${themeExclusionStr}
</details>`;
}
private generateSettingsSearchResultDetailsMd(): string {
return `
Query: ${this._data.query}
Literal matches: ${this._data.filterResultCount}`;
}
private generateSettingSearchResultsMd(): string {
if (!this._data.actualSearchResults) {
return '';
}
if (!this._data.actualSearchResults.length) {
return `No fuzzy results`;
}
let tableHeader = `Setting|Extension|Score
---|---|---`;
const table = this._data.actualSearchResults.map(setting => {
return `${setting.key}|${setting.extensionId}|${String(setting.score).slice(0, 5)}`;
}).join('\n');
return `<details><summary>Results</summary>
${tableHeader}
${table}
</details>`;
}
}
\ No newline at end of file
......@@ -15,9 +15,7 @@ export default (): string => `
<div class="input-group">
<label id="issue-type-label" class="inline-form-control" for="issue-type">${escape(localize('issueTypeLabel', "This is a"))}</label>
<select id="issue-type" class="inline-form-control">
<option value="0">${escape(localize('bugReporter', "Bug Report"))}</option>
<option value="1">${escape(localize('performanceIssue', "Performance Issue"))}</option>
<option value="2">${escape(localize('featureRequest', "Feature Request"))}</option>
<!-- To be dynamically filled -->
</select>
</div>
......@@ -86,6 +84,32 @@ export default (): string => `
</div>
</details>
</div>
<div class="block block-searchedExtensions">
<details>
<summary>${escape(localize('searchedExtensions', "Searched Extensions"))}
<div class="include-data">
<input class="sendData" type="checkbox" id="includeSearchedExtensions" checked/>
<label class="caption" for="includeSearchedExtensions">${escape(localize('sendData', "Send my data"))}</label>
</div>
</summary>
<div class="block-info">
<!-- To be dynamically filled -->
</div>
</details>
</div>
<div class="block block-settingsSearchResults">
<details>
<summary>${escape(localize('settingsSearchDetails', "Settings Search Details"))}
<div class="include-data">
<input class="sendData" type="checkbox" id="includeSettingsSearchDetails" checked/>
<label class="caption" for="includeSettingsSearchDetails">${escape(localize('sendData', "Send my data"))}</label>
</div>
</summary>
<div class="block-info">
<!-- To be dynamically filled -->
</div>
</details>
</div>
</div>
</div>
......
......@@ -30,6 +30,14 @@ td {
border-top: 1px solid #e9ecef;
}
.block-settingsSearchResults-details {
padding-bottom: .5rem;
}
.block-settingsSearchResults-details > div {
padding: .5rem .75rem;
}
.section {
margin-bottom: 1.5em;
}
......
......@@ -15,7 +15,8 @@ export const IIssueService = createDecorator<IIssueService>(ID);
export enum IssueType {
Bug,
PerformanceIssue,
FeatureRequest
FeatureRequest,
SettingsSearchIssue
}
export interface IssueReporterStyles {
......@@ -42,6 +43,19 @@ export interface IssueReporterData {
issueType?: IssueType;
}
export interface ISettingSearchResult {
extensionId: string;
key: string;
score: number;
}
export interface ISettingsSearchIssueReporterData extends IssueReporterData {
issueType: IssueType.SettingsSearchIssue;
actualSearchResults: ISettingSearchResult[];
query: string;
filterResultCount: number;
}
export interface IIssueService {
_serviceBrand: any;
openReporter(data: IssueReporterData): TPromise<void>;
......
......@@ -7,7 +7,6 @@ import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import { Delayer } from 'vs/base/common/async';
import * as arrays from 'vs/base/common/arrays';
import * as strings from 'vs/base/common/strings';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Position } from 'vs/editor/common/core/position';
import { IAction } from 'vs/base/common/actions';
......@@ -18,7 +17,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { Range, IRange } from 'vs/editor/common/core/range';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, ISettingsEditorModel, IScoredResults, IWorkbenchSettingsConfiguration, IExtensionSetting } from 'vs/workbench/parts/preferences/common/preferences';
import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, ISettingsEditorModel, IWorkbenchSettingsConfiguration, IExtensionSetting } from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView';
......@@ -26,8 +25,7 @@ import { SettingsGroupTitleWidget, EditPreferenceWidget, SettingsHeaderWidget, D
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations';
import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Severity } from 'vs/platform/message/common/message';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
......@@ -38,6 +36,11 @@ import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/ed
import { CodeLensProviderRegistry, CodeLensProvider, ICodeLensSymbol } from 'vs/editor/common/modes';
import { CancellationToken } from 'vs/base/common/cancellation';
import { getDomNodePagePosition } from 'vs/base/browser/dom';
import { IIssueService, IssueType, ISettingsSearchIssueReporterData, ISettingSearchResult } from 'vs/platform/issue/common/issue';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IExtensionManagementService, IExtensionEnablementService, LocalExtensionType, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getIssueReporterStyles } from 'vs/workbench/electron-browser/actions';
import { webFrame } from 'electron';
export interface IPreferencesRenderer<T> extends IDisposable {
readonly preferencesModel: IPreferencesEditorModel<T>;
......@@ -572,23 +575,16 @@ export class HiddenAreasRenderer extends Disposable {
}
export class FeedbackWidgetRenderer extends Disposable {
private static readonly DEFAULT_COMMENT_TEXT = 'Replace this comment with any text feedback.';
private static readonly INSTRUCTION_TEXT = [
'// Modify the "resultScores" section to contain only your expected results. Assign scores to indicate their relevance.',
'// Results present in "resultScores" will be automatically "boosted" for this query, if they are not already at the top of the result set.',
'// Add phrase pairs to the "alts" section to have them considered to be synonyms in queries.'
].join('\n');
private _feedbackWidget: FloatingClickWidget;
private _currentResult: IFilterResult;
constructor(private editor: ICodeEditor,
@IInstantiationService private instantiationService: IInstantiationService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@ITelemetryService private telemetryService: ITelemetryService,
@IMessageService private messageService: IMessageService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IConfigurationService private configurationService: IConfigurationService
@IIssueService private issueService: IIssueService,
@IConfigurationService private configurationService: IConfigurationService,
@IThemeService private themeService: IThemeService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
) {
super();
}
......@@ -606,151 +602,54 @@ export class FeedbackWidgetRenderer extends Disposable {
private showWidget(): void {
if (!this._feedbackWidget) {
this._feedbackWidget = this._register(this.instantiationService.createInstance(FloatingClickWidget, this.editor, 'Provide feedback', null));
this._register(this._feedbackWidget.onClick(() => this.getFeedback()));
this._register(this._feedbackWidget.onClick(() => this.showIssueReporter()));
this._feedbackWidget.render();
}
}
private getFeedback(): void {
if (!this.telemetryService.isOptedIn && this.environmentService.appQuality) {
this.messageService.show(Severity.Error, 'Can\'t send feedback, user is opted out of telemetry');
return;
}
const result = this._currentResult;
const metadata = result.metadata['nlpResult']; // Feedback only on nlpResult set for now
const actualResults = metadata ? metadata.scoredResults : {};
const actualResultIds = Object.keys(actualResults);
const feedbackQuery: any = {};
feedbackQuery['comment'] = FeedbackWidgetRenderer.DEFAULT_COMMENT_TEXT;
feedbackQuery['queryString'] = result.query;
feedbackQuery['duration'] = metadata ? metadata.duration : -1;
feedbackQuery['resultScores'] = [];
actualResultIds.forEach(settingId => {
feedbackQuery['resultScores'].push({
packageID: actualResults[settingId].packageId,
key: actualResults[settingId].key,
score: 10
});
});
feedbackQuery['alts'] = [];
const groupCountsText = result.filteredGroups
.map(group => `// ${group.id}: ${group.sections[0].settings.length}`)
.join('\n');
const contents = FeedbackWidgetRenderer.INSTRUCTION_TEXT + '\n' +
JSON.stringify(feedbackQuery, undefined, ' ') + '\n\n' +
this.getScoreText(actualResults) + '\n\n' +
groupCountsText + '\n';
this.editorService.openEditor({ contents, language: 'jsonc' }, /*sideBySide=*/true).then(feedbackEditor => {
const sendFeedbackWidget = this._register(this.instantiationService.createInstance(FloatingClickWidget, feedbackEditor.getControl(), 'Send feedback', null));
sendFeedbackWidget.render();
this._register(sendFeedbackWidget.onClick(() => {
this.sendFeedback(feedbackEditor.getControl() as ICodeEditor, result, actualResults).then(() => {
sendFeedbackWidget.dispose();
this.messageService.show(Severity.Info, 'Feedback sent successfully');
}, err => {
this.messageService.show(Severity.Error, 'Error sending feedback: ' + err.message);
});
}));
});
}
private getScoreText(results?: IScoredResults): string {
if (!results) {
return '';
}
return Object.keys(results)
.map(name => {
return `// ${results[name].key}: ${results[name].score}`;
}).join('\n');
}
private sendFeedback(feedbackEditor: ICodeEditor, result: IFilterResult, scoredResults: IScoredResults): TPromise<void> {
const model = feedbackEditor.getModel();
const expectedQueryLines = model.getLinesContent()
.filter(line => !strings.startsWith(line, '//'));
let expectedQuery: any;
try {
expectedQuery = JSON.parse(expectedQueryLines.join('\n'));
} catch (e) {
// invalid JSON
return TPromise.wrapError(new Error('Invalid JSON: ' + e.message));
}
const userComment = expectedQuery.comment === FeedbackWidgetRenderer.DEFAULT_COMMENT_TEXT ? undefined : expectedQuery.comment;
// validate alts
if (!this.validateAlts(expectedQuery.alts)) {
return TPromise.wrapError(new Error('alts must be an array of 2-element string arrays'));
}
const altsAdded = expectedQuery.alts && expectedQuery.alts.length;
const alts = altsAdded ? expectedQuery.alts : undefined;
const workbenchSettings = this.configurationService.getValue<IWorkbenchSettingsConfiguration>().workbench.settings;
const autoIngest = workbenchSettings.naturalLanguageSearchAutoIngestFeedback;
const nlpMetadata = result.metadata && result.metadata['nlpResult'];
const duration = nlpMetadata && nlpMetadata.duration;
const requestBody = nlpMetadata && nlpMetadata.requestBody;
const actualResultScores = {};
for (let key in scoredResults) {
actualResultScores[key] = {
score: scoredResults[key].score
private showIssueReporter(): void {
const filterResultGroup = arrays.first(this._currentResult.filteredGroups, group => group.id === 'filterResult');
const filterResultCount = filterResultGroup ?
filterResultGroup.sections[0].settings.length :
0;
const results = this._currentResult.metadata['nlpResult'].scoredResults;
this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => {
const enabledExtensions = extensions
.filter(extension => this.extensionEnablementService.isEnabled(extension.identifier))
.filter(ext => ext.manifest.contributes && ext.manifest.contributes.configuration);
const theme = this.themeService.getTheme();
const issueResults = Object.keys(results)
.map(key => (<ISettingSearchResult>{
key: key.split('##')[1],
extensionId: results[key].packageId === 'core' ?
'core' :
this.getExtensionIdByGuid(enabledExtensions, results[key].packageId),
score: results[key].score
}))
.slice(0, 20);
const issueReporterData: ISettingsSearchIssueReporterData = {
styles: getIssueReporterStyles(theme),
zoomLevel: webFrame.getZoomLevel(),
enabledExtensions,
issueType: IssueType.SettingsSearchIssue,
actualSearchResults: issueResults,
filterResultCount,
query: this._currentResult.query
};
}
/* __GDPR__
"settingsSearchResultFeedback" : {
"query" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"requestBody" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"userComment" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"actualResults" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"expectedResults" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"buildNumber" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"alts" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"autoIngest" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
return this.telemetryService.publicLog('settingsSearchResultFeedback', {
query: result.query,
requestBody,
userComment,
actualResults: actualResultScores,
expectedResults: expectedQuery.resultScores,
duration,
buildNumber: this.environmentService.settingsSearchBuildId,
alts,
autoIngest
return this.issueService.openReporter(issueReporterData);
});
}
private validateAlts(alts?: string[][]): boolean {
if (!alts) {
return true;
}
if (!Array.isArray(alts)) {
return false;
}
private getExtensionIdByGuid(extensions: ILocalExtension[], guid: string): string {
const match = arrays.first(extensions, ext => ext.identifier.uuid === guid);
if (!alts.length) {
return true;
}
if (!alts.every(altPair => Array.isArray(altPair) && altPair.length === 2 && typeof altPair[0] === 'string' && typeof altPair[1] === 'string')) {
return false;
}
return true;
// identifier.id includes the version, not needed here
return match && `${match.manifest.publisher}.${match.manifest.name}`;
}
private disposeWidget(): void {
......@@ -762,7 +661,6 @@ export class FeedbackWidgetRenderer extends Disposable {
public dispose() {
this.disposeWidget();
super.dispose();
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册