未验证 提交 540a998a 编写于 作者: R Rob Lourens 提交者: GitHub

Merge pull request #43274 from Microsoft/roblou/settingsSearchIssueReporter

Use issue reporter for settings search
......@@ -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;
}
......
......@@ -9,13 +9,13 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
export const ID = 'issueService';
export const IIssueService = createDecorator<IIssueService>(ID);
export const IIssueService = createDecorator<IIssueService>('issueService');
export enum IssueType {
Bug,
PerformanceIssue,
FeatureRequest
FeatureRequest,
SettingsSearchIssue
}
export interface IssueReporterStyles {
......@@ -42,7 +42,20 @@ 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>;
}
\ No newline at end of file
}
......@@ -20,7 +20,6 @@ import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IExtensionManagementService, LocalExtensionType, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import paths = require('vs/base/common/paths');
import { isMacintosh, isLinux } from 'vs/base/common/platform';
......@@ -44,17 +43,14 @@ import { FileKind } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionService, ActivationTimes } from 'vs/platform/extensions/common/extensions';
import { getEntries } from 'vs/base/common/performance';
import { IIssueService, IssueReporterData, IssueType, IssueReporterStyles } from 'vs/platform/issue/common/issue';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { textLinkForeground, inputBackground, inputBorder, inputForeground, buttonBackground, buttonHoverBackground, buttonForeground, inputValidationErrorBorder, foreground, inputActiveOptionBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IssueType } from 'vs/platform/issue/common/issue';
import { domEvent } from 'vs/base/browser/event';
import { once } from 'vs/base/common/event';
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
import { getDomNodePagePosition, createStyleSheet, createCSSRule } from 'vs/base/browser/dom';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Context } from 'vs/platform/contextkey/browser/contextKeyService';
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
// --- actions
......@@ -869,25 +865,6 @@ export class CloseMessagesAction extends Action {
}
}
export function getIssueReporterStyles(theme: ITheme): IssueReporterStyles {
return {
backgroundColor: theme.getColor(SIDE_BAR_BACKGROUND) && theme.getColor(SIDE_BAR_BACKGROUND).toString(),
color: theme.getColor(foreground).toString(),
textLinkColor: theme.getColor(textLinkForeground) && theme.getColor(textLinkForeground).toString(),
inputBackground: theme.getColor(inputBackground) && theme.getColor(inputBackground).toString(),
inputForeground: theme.getColor(inputForeground) && theme.getColor(inputForeground).toString(),
inputBorder: theme.getColor(inputBorder) && theme.getColor(inputBorder).toString(),
inputActiveBorder: theme.getColor(inputActiveOptionBorder) && theme.getColor(inputActiveOptionBorder).toString(),
inputErrorBorder: theme.getColor(inputValidationErrorBorder) && theme.getColor(inputValidationErrorBorder).toString(),
buttonBackground: theme.getColor(buttonBackground) && theme.getColor(buttonBackground).toString(),
buttonForeground: theme.getColor(buttonForeground) && theme.getColor(buttonForeground).toString(),
buttonHoverBackground: theme.getColor(buttonHoverBackground) && theme.getColor(buttonHoverBackground).toString(),
sliderActiveColor: theme.getColor(scrollbarSliderActiveBackground) && theme.getColor(scrollbarSliderActiveBackground).toString(),
sliderBackgroundColor: theme.getColor(scrollbarSliderBackground) && theme.getColor(scrollbarSliderBackground).toString(),
sliderHoverColor: theme.getColor(scrollbarSliderHoverBackground) && theme.getColor(scrollbarSliderHoverBackground).toString()
};
}
export class OpenIssueReporterAction extends Action {
public static readonly ID = 'workbench.action.openIssueReporter';
public static readonly LABEL = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue");
......@@ -895,28 +872,14 @@ export class OpenIssueReporterAction extends Action {
constructor(
id: string,
label: string,
@IIssueService private issueService: IIssueService,
@IThemeService private themeService: IThemeService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
@IWorkbenchIssueService private issueService: IWorkbenchIssueService
) {
super(id, label);
}
public run(): TPromise<boolean> {
return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => {
const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled({ id: getGalleryExtensionIdFromLocal(extension) }));
const theme = this.themeService.getTheme();
const issueReporterData: IssueReporterData = {
styles: getIssueReporterStyles(theme),
zoomLevel: webFrame.getZoomLevel(),
enabledExtensions
};
return this.issueService.openReporter(issueReporterData).then(() => {
return TPromise.as(true);
});
});
return this.issueService.openReporter()
.then(() => true);
}
}
......@@ -927,30 +890,15 @@ export class ReportPerformanceIssueUsingReporterAction extends Action {
constructor(
id: string,
label: string,
@IIssueService private issueService: IIssueService,
@IThemeService private themeService: IThemeService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
@IWorkbenchIssueService private issueService: IWorkbenchIssueService
) {
super(id, label);
}
public run(): TPromise<boolean> {
return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => {
const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension.identifier));
const theme = this.themeService.getTheme();
const issueReporterData: IssueReporterData = {
styles: getIssueReporterStyles(theme),
zoomLevel: webFrame.getZoomLevel(),
enabledExtensions,
issueType: IssueType.PerformanceIssue
};
// TODO: Reporter should send timings table as well
return this.issueService.openReporter(issueReporterData).then(() => {
return TPromise.as(true);
});
});
// TODO: Reporter should send timings table as well
return this.issueService.openReporter({ issueType: IssueType.PerformanceIssue })
.then(() => true);
}
}
......
......@@ -92,6 +92,8 @@ import { stat } from 'fs';
import { join } from 'path';
import { ILocalizationsChannel, LocalizationsChannelClient } from 'vs/platform/localizations/common/localizationsIpc';
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
import { WorkbenchIssueService } from 'vs/workbench/services/issue/electron-browser/workbenchIssueService';
/**
* Services that we require for the Shell
......@@ -445,6 +447,8 @@ export class WorkbenchShell {
serviceCollection.set(ISearchService, new SyncDescriptor(SearchService));
serviceCollection.set(IWorkbenchIssueService, new SyncDescriptor(WorkbenchIssueService));
serviceCollection.set(ICodeEditorService, new SyncDescriptor(CodeEditorServiceImpl));
serviceCollection.set(IIntegrityService, new SyncDescriptor(IntegrityServiceImpl));
......
......@@ -18,7 +18,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, IScoredResults } 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 +26,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, IMessageService } 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 +37,10 @@ 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 { IssueType, ISettingsSearchIssueReporterData, ISettingSearchResult } from 'vs/platform/issue/common/issue';
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
export interface IPreferencesRenderer<T> extends IDisposable {
readonly preferencesModel: IPreferencesEditorModel<T>;
......@@ -247,6 +250,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
private filteredMatchesRenderer: FilteredMatchesRenderer;
private hiddenAreasRenderer: HiddenAreasRenderer;
private editSettingActionRenderer: EditSettingRenderer;
private issueWidgetRenderer: IssueWidgetRenderer;
private feedbackWidgetRenderer: FeedbackWidgetRenderer;
private bracesHidingRenderer: BracesHidingRenderer;
private extensionCodelensRenderer: ExtensionCodelensRenderer;
......@@ -263,7 +267,8 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
constructor(protected editor: ICodeEditor, public readonly preferencesModel: DefaultSettingsEditorModel,
@IPreferencesService protected preferencesService: IPreferencesService,
@IInstantiationService protected instantiationService: IInstantiationService
@IInstantiationService protected instantiationService: IInstantiationService,
@IConfigurationService private configurationService: IConfigurationService
) {
super();
this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference));
......@@ -271,6 +276,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.settingsGroupTitleRenderer = this._register(instantiationService.createInstance(SettingsGroupTitleRenderer, editor));
this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor));
this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, preferencesModel, this.settingHighlighter));
this.issueWidgetRenderer = this._register(instantiationService.createInstance(IssueWidgetRenderer, editor));
this.feedbackWidgetRenderer = this._register(instantiationService.createInstance(FeedbackWidgetRenderer, editor));
this.bracesHidingRenderer = this._register(instantiationService.createInstance(BracesHidingRenderer, editor, preferencesModel));
this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, this.bracesHidingRenderer]));
......@@ -293,6 +299,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
public render() {
this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups);
this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel);
this.issueWidgetRenderer.render(null);
this.feedbackWidgetRenderer.render(null);
this.settingHighlighter.clear(true);
this.bracesHidingRenderer.render(null, this.preferencesModel.settingsGroups);
......@@ -306,7 +313,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
if (filterResult) {
this.filteredMatchesRenderer.render(filterResult, this.preferencesModel.settingsGroups);
this.settingsGroupTitleRenderer.render(null);
this.feedbackWidgetRenderer.render(filterResult);
this.renderIssueWidget(filterResult);
this.settingsHeaderRenderer.render(filterResult);
this.settingHighlighter.clear(true);
this.bracesHidingRenderer.render(filterResult, this.preferencesModel.settingsGroups);
......@@ -315,7 +322,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
} else {
this.settingHighlighter.clear(true);
this.filteredMatchesRenderer.render(null, this.preferencesModel.settingsGroups);
this.feedbackWidgetRenderer.render(null);
this.renderIssueWidget(null);
this.settingsHeaderRenderer.render(null);
this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups);
this.settingsGroupTitleRenderer.showGroup(0);
......@@ -327,6 +334,17 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.hiddenAreasRenderer.render();
}
private renderIssueWidget(filterResult: IFilterResult): void {
const workbenchSettings = this.configurationService.getValue<IWorkbenchSettingsConfiguration>().workbench.settings;
if (workbenchSettings.enableNaturalLanguageSearchFeedback) {
this.issueWidgetRenderer.render(null);
this.feedbackWidgetRenderer.render(filterResult);
} else {
this.feedbackWidgetRenderer.render(null);
this.issueWidgetRenderer.render(filterResult);
}
}
public focusPreference(s: ISetting): void {
const setting = this.getSetting(s);
if (setting) {
......@@ -594,9 +612,8 @@ export class FeedbackWidgetRenderer extends Disposable {
}
public render(result: IFilterResult): void {
const workbenchSettings = this.configurationService.getValue<IWorkbenchSettingsConfiguration>().workbench.settings;
this._currentResult = result;
if (result && result.metadata && workbenchSettings.enableNaturalLanguageSearchFeedback) {
if (result && result.metadata) {
this.showWidget();
} else if (this._feedbackWidget) {
this.disposeWidget();
......@@ -762,7 +779,88 @@ export class FeedbackWidgetRenderer extends Disposable {
public dispose() {
this.disposeWidget();
super.dispose();
}
}
export class IssueWidgetRenderer extends Disposable {
private _issueWidget: FloatingClickWidget;
private _currentResult: IFilterResult;
constructor(private editor: ICodeEditor,
@IInstantiationService private instantiationService: IInstantiationService,
@IWorkbenchIssueService private issueService: IWorkbenchIssueService,
@IEnvironmentService private environmentService: IEnvironmentService
) {
super();
}
public render(result: IFilterResult): void {
this._currentResult = result;
if (result && result.metadata && this.environmentService.appQuality !== 'stable') {
this.showWidget();
} else if (this._issueWidget) {
this.disposeWidget();
}
}
private showWidget(): void {
if (!this._issueWidget) {
this._issueWidget = this._register(this.instantiationService.createInstance(FloatingClickWidget, this.editor, nls.localize('reportSettingsSearchIssue', "Report Issue"), null));
this._register(this._issueWidget.onClick(() => this.showIssueReporter()));
this._issueWidget.render();
}
}
private showIssueReporter(): TPromise<void> {
const nlpMetadata = this._currentResult.metadata['nlpResult'];
const results = nlpMetadata.scoredResults;
const enabledExtensions = nlpMetadata.extensions;
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: Partial<ISettingsSearchIssueReporterData> = {
enabledExtensions,
issueType: IssueType.SettingsSearchIssue,
actualSearchResults: issueResults,
filterResultCount: this.getFilterResultCount(),
query: this._currentResult.query
};
return this.issueService.openReporter(issueReporterData);
}
private getFilterResultCount(): number {
const filterResultGroup = arrays.first(this._currentResult.filteredGroups, group => group.id === 'filterResult');
return filterResultGroup ?
filterResultGroup.sections[0].settings.length :
0;
}
private getExtensionIdByGuid(extensions: ILocalExtension[], guid: string): string {
const match = arrays.first(extensions, ext => ext.identifier.uuid === guid);
// identifier.id includes the version, not needed here
return match && `${match.manifest.publisher}.${match.manifest.name}`;
}
private disposeWidget(): void {
if (this._issueWidget) {
this._issueWidget.dispose();
this._issueWidget = null;
}
}
public dispose() {
this.disposeWidget();
super.dispose();
}
}
......
......@@ -17,6 +17,7 @@ import { join } from 'vs/base/common/paths';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import Event from 'vs/base/common/event';
import { IStringDictionary } from 'vs/base/common/collections';
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
export interface IWorkbenchSettingsConfiguration {
workbench: {
......@@ -110,6 +111,9 @@ export interface IFilterMetadata {
timestamp: number;
duration: number;
scoredResults: IScoredResults;
extensions?: ILocalExtension[];
/** The number of requests made, since requests are split by number of filters */
requestCount?: number;
/** The name of the server that actually served the request */
......
......@@ -42,8 +42,10 @@ export class PreferencesSearchService extends Disposable implements IPreferences
// This request goes to the shared process but results won't change during a window's lifetime, so cache the results.
this._installedExtensions = this.extensionManagementService.getInstalled(LocalExtensionType.User).then(exts => {
// Filter to enabled extensions
return exts.filter(ext => this.extensionEnablementService.isEnabled(ext.identifier));
// Filter to enabled extensions that have settings
return exts
.filter(ext => this.extensionEnablementService.isEnabled(ext.identifier))
.filter(ext => ext.manifest.contributes && ext.manifest.contributes.configuration);
});
}
......@@ -132,6 +134,7 @@ interface IBingRequestDetails {
url: string;
body?: string;
hasMoreFilters?: boolean;
extensions?: ILocalExtension[];
}
class RemoteSearchProvider implements ISearchProvider {
......@@ -280,7 +283,8 @@ class RemoteSearchProvider implements ISearchProvider {
duration,
timestamp,
scoredResults,
context: result['@odata.context']
context: result['@odata.context'],
extensions: details.extensions
};
});
}
......@@ -315,9 +319,10 @@ class RemoteSearchProvider implements ISearchProvider {
url += `${API_VERSION}&${QUERY_TYPE}`;
}
const extensions = await this.installedExtensions;
const filters = this.options.newExtensionsOnly ?
[`diminish eq 'latest'`] :
await this.getVersionFilters(this.environmentService.settingsSearchBuildId);
this.getVersionFilters(extensions, this.environmentService.settingsSearchBuildId);
const filterStr = filters
.slice(filterPage * RemoteSearchProvider.MAX_REQUEST_FILTERS, (filterPage + 1) * RemoteSearchProvider.MAX_REQUEST_FILTERS)
......@@ -333,23 +338,22 @@ class RemoteSearchProvider implements ISearchProvider {
return {
url,
body,
hasMoreFilters
hasMoreFilters,
extensions
};
}
private getVersionFilters(buildNumber?: number): TPromise<string[]> {
return this.installedExtensions.then(exts => {
// Only search extensions that contribute settings
const filters = exts
.filter(ext => ext.manifest.contributes && ext.manifest.contributes.configuration)
.map(ext => this.getExtensionFilter(ext));
private getVersionFilters(exts: ILocalExtension[], buildNumber?: number): string[] {
// Only search extensions that contribute settings
const filters = exts
.filter(ext => ext.manifest.contributes && ext.manifest.contributes.configuration)
.map(ext => this.getExtensionFilter(ext));
if (buildNumber) {
filters.push(`(packageid eq 'core' and startbuildno le '${buildNumber}' and endbuildno ge '${buildNumber}')`);
}
if (buildNumber) {
filters.push(`(packageid eq 'core' and startbuildno le '${buildNumber}' and endbuildno ge '${buildNumber}')`);
}
return filters;
});
return filters;
}
private getExtensionFilter(ext: ILocalExtension): string {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IssueReporterData } from 'vs/platform/issue/common/issue';
export const IWorkbenchIssueService = createDecorator<IWorkbenchIssueService>('workbenchIssueService');
export interface IWorkbenchIssueService {
_serviceBrand: any;
openReporter(dataOverrides?: Partial<IssueReporterData>): TPromise<void>;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IssueReporterStyles, IIssueService, IssueReporterData } from 'vs/platform/issue/common/issue';
import { TPromise } from 'vs/base/common/winjs.base';
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { textLinkForeground, inputBackground, inputBorder, inputForeground, buttonBackground, buttonHoverBackground, buttonForeground, inputValidationErrorBorder, foreground, inputActiveOptionBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { IExtensionManagementService, IExtensionEnablementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { webFrame } from 'electron';
import { assign } from 'vs/base/common/objects';
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
export class WorkbenchIssueService implements IWorkbenchIssueService {
_serviceBrand: any;
constructor(
@IIssueService private issueService: IIssueService,
@IThemeService private themeService: IThemeService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
) {
}
openReporter(dataOverrides: Partial<IssueReporterData> = {}): TPromise<void> {
return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => {
const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled({ id: getGalleryExtensionIdFromLocal(extension) }));
const theme = this.themeService.getTheme();
const issueReporterData: IssueReporterData = assign(
{
styles: getIssueReporterStyles(theme),
zoomLevel: webFrame.getZoomLevel(),
enabledExtensions
},
dataOverrides);
return this.issueService.openReporter(issueReporterData);
});
}
}
export function getIssueReporterStyles(theme: ITheme): IssueReporterStyles {
return {
backgroundColor: theme.getColor(SIDE_BAR_BACKGROUND) && theme.getColor(SIDE_BAR_BACKGROUND).toString(),
color: theme.getColor(foreground).toString(),
textLinkColor: theme.getColor(textLinkForeground) && theme.getColor(textLinkForeground).toString(),
inputBackground: theme.getColor(inputBackground) && theme.getColor(inputBackground).toString(),
inputForeground: theme.getColor(inputForeground) && theme.getColor(inputForeground).toString(),
inputBorder: theme.getColor(inputBorder) && theme.getColor(inputBorder).toString(),
inputActiveBorder: theme.getColor(inputActiveOptionBorder) && theme.getColor(inputActiveOptionBorder).toString(),
inputErrorBorder: theme.getColor(inputValidationErrorBorder) && theme.getColor(inputValidationErrorBorder).toString(),
buttonBackground: theme.getColor(buttonBackground) && theme.getColor(buttonBackground).toString(),
buttonForeground: theme.getColor(buttonForeground) && theme.getColor(buttonForeground).toString(),
buttonHoverBackground: theme.getColor(buttonHoverBackground) && theme.getColor(buttonHoverBackground).toString(),
sliderActiveColor: theme.getColor(scrollbarSliderActiveBackground) && theme.getColor(scrollbarSliderActiveBackground).toString(),
sliderBackgroundColor: theme.getColor(scrollbarSliderBackground) && theme.getColor(scrollbarSliderBackground).toString(),
sliderHoverColor: theme.getColor(scrollbarSliderHoverBackground) && theme.getColor(scrollbarSliderHoverBackground).toString()
};
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册