diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 17633097592..8c6a04f03a5 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -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(configuration.data); + } } render(): void { - (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 = ` +
+
Query: "${data.query}"
+
Literal match count: ${data.filterResultCount}
+
+ `; + + let table = ` + + Setting + Extension + Score + `; + + data.actualSearchResults + .forEach(setting => { + table += ` + + ${setting.key} + ${setting.extensionId} + ${String(setting.score).slice(0, 5)} + `; + }); + + target.innerHTML = `${details}${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) => ``; + + const typeSelect = (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")} *`; 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")} *`; 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")} *`; + 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}
${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}
`; + } + + private getExtensionTableHtml(extensions: ILocalExtension[]): string { let table = ` Extension @@ -598,16 +692,16 @@ export class IssueReporter extends Disposable { Version `; - extensions.forEach(extension => { - table += ` + table += extensions.map(extension => { + return ` ${extension.manifest.name} ${extension.manifest.publisher.substr(0, 3)} ${extension.manifest.version} `; - }); + }).join(''); - target.innerHTML = `${table}
${themeExclusionStr}`; + return table; } } diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-browser/issue/issueReporterModel.ts index e9590c15068..f6656f20760 100644 --- a/src/vs/code/electron-browser/issue/issueReporterModel.ts +++ b/src/vs/code/electron-browser/issue/issueReporterModel.ts @@ -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} +`; + } + + 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 `
Results + +${tableHeader} +${table} +
`; } } \ No newline at end of file diff --git a/src/vs/code/electron-browser/issue/issueReporterPage.ts b/src/vs/code/electron-browser/issue/issueReporterPage.ts index 638a365632d..1b889915991 100644 --- a/src/vs/code/electron-browser/issue/issueReporterPage.ts +++ b/src/vs/code/electron-browser/issue/issueReporterPage.ts @@ -15,9 +15,7 @@ export default (): string => `
@@ -86,6 +84,32 @@ export default (): string => ` +
+
+ ${escape(localize('searchedExtensions', "Searched Extensions"))} +
+ + +
+
+
+ +
+
+
+
+
+ ${escape(localize('settingsSearchDetails', "Settings Search Details"))} +
+ + +
+
+
+ +
+
+
diff --git a/src/vs/code/electron-browser/issue/media/issueReporter.css b/src/vs/code/electron-browser/issue/media/issueReporter.css index 3cc8d876041..74c25832695 100644 --- a/src/vs/code/electron-browser/issue/media/issueReporter.css +++ b/src/vs/code/electron-browser/issue/media/issueReporter.css @@ -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; } diff --git a/src/vs/platform/issue/common/issue.ts b/src/vs/platform/issue/common/issue.ts index 97ac40453ce..4d171950708 100644 --- a/src/vs/platform/issue/common/issue.ts +++ b/src/vs/platform/issue/common/issue.ts @@ -15,7 +15,8 @@ export const IIssueService = createDecorator(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; diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 85cb33132aa..648b0367d6d 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -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 extends IDisposable { readonly preferencesModel: IPreferencesEditorModel; @@ -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; - } + private showIssueReporter(): void { + const filterResultGroup = arrays.first(this._currentResult.filteredGroups, group => group.id === 'filterResult'); + const filterResultCount = filterResultGroup ? + filterResultGroup.sections[0].settings.length : + 0; - 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 results = this._currentResult.metadata['nlpResult'].scoredResults; - 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'] = []; + 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 groupCountsText = result.filteredGroups - .map(group => `// ${group.id}: ${group.sections[0].settings.length}`) - .join('\n'); + const issueResults = Object.keys(results) + .map(key => ({ + key: key.split('##')[1], + extensionId: results[key].packageId === 'core' ? + 'core' : + this.getExtensionIdByGuid(enabledExtensions, results[key].packageId), + score: results[key].score + })) + .slice(0, 20); - 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 { - 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().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 + 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; - } + private getExtensionIdByGuid(extensions: ILocalExtension[], guid: string): string { + const match = arrays.first(extensions, ext => ext.identifier.uuid === guid); - if (!Array.isArray(alts)) { - return false; - } - - 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(); } }