提交 7fbd3387 编写于 作者: R Rob Lourens

Merge branch 'roblou/newExtensionSettingSearch'

...@@ -1692,6 +1692,17 @@ CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForL ...@@ -1692,6 +1692,17 @@ CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForL
}); });
}); });
CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsWithId', function (accessor: ServicesAccessor, extensionId: string) {
const viewletService = accessor.get(IViewletService);
return viewletService.openViewlet(VIEWLET_ID, true)
.then(viewlet => viewlet as IExtensionsViewlet)
.then(viewlet => {
viewlet.search(`@id:${extensionId}`);
viewlet.focus();
});
});
export const extensionButtonProminentBackground = registerColor('extensionButton.prominentBackground', { export const extensionButtonProminentBackground = registerColor('extensionButton.prominentBackground', {
dark: '#327e36', dark: '#327e36',
light: '#327e36', light: '#327e36',
......
...@@ -111,7 +111,7 @@ export class PreferencesEditor extends BaseEditor { ...@@ -111,7 +111,7 @@ export class PreferencesEditor extends BaseEditor {
private preferencesRenderers: PreferencesRenderersController; private preferencesRenderers: PreferencesRenderersController;
private delayedFilterLogging: Delayer<void>; private delayedFilterLogging: Delayer<void>;
private remoteSearchThrottle: ThrottledDelayer<IFilterOrSearchResult>; private remoteSearchThrottle: ThrottledDelayer<void>;
private _lastReportedFilter: string; private _lastReportedFilter: string;
private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget = null; private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget = null;
...@@ -243,18 +243,18 @@ export class PreferencesEditor extends BaseEditor { ...@@ -243,18 +243,18 @@ export class PreferencesEditor extends BaseEditor {
TPromise.join([ TPromise.join([
this.preferencesRenderers.localFilterPreferences(query), this.preferencesRenderers.localFilterPreferences(query),
this.triggerThrottledSearch(query) this.triggerThrottledSearch(query)
]).then(results => { ]).then(() => {
if (results) { const result = this.preferencesRenderers.lastFilterResult;
const [localResult, remoteResult] = results; if (result) {
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed( this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(
query, query,
remoteResult ? remoteResult.defaultSettingsGroupCounts : localResult.defaultSettingsGroupCounts, result.defaultSettingsGroupCounts,
remoteResult && remoteResult.metadata)); result.metadata));
} }
}); });
} }
private triggerThrottledSearch(query: string): TPromise<IFilterOrSearchResult> { private triggerThrottledSearch(query: string): TPromise<void> {
if (query) { if (query) {
return this.remoteSearchThrottle.trigger(() => this.preferencesRenderers.remoteSearchPreferences(query)); return this.remoteSearchThrottle.trigger(() => this.preferencesRenderers.remoteSearchPreferences(query));
} else { } else {
...@@ -364,11 +364,13 @@ class PreferencesRenderersController extends Disposable { ...@@ -364,11 +364,13 @@ class PreferencesRenderersController extends Disposable {
private _editablePreferencesRendererDisposables: IDisposable[] = []; private _editablePreferencesRendererDisposables: IDisposable[] = [];
private _settingsNavigator: SettingsNavigator; private _settingsNavigator: SettingsNavigator;
private _filtersInProgress: TPromise<IFilterResult>[]; private _remoteFilterInProgress: TPromise<any>;
private _currentLocalSearchProvider: ISearchProvider; private _currentLocalSearchProvider: ISearchProvider;
private _currentRemoteSearchProvider: ISearchProvider; private _currentRemoteSearchProvider: ISearchProvider;
private _currentNewExtensionsSearchProvider: ISearchProvider;
private _lastQuery: string; private _lastQuery: string;
private _lastFilterResult: IFilterOrSearchResult;
private _onDidFilterResultsCountChange: Emitter<number> = this._register(new Emitter<number>()); private _onDidFilterResultsCountChange: Emitter<number> = this._register(new Emitter<number>());
public onDidFilterResultsCountChange: Event<number> = this._onDidFilterResultsCountChange.event; public onDidFilterResultsCountChange: Event<number> = this._onDidFilterResultsCountChange.event;
...@@ -380,6 +382,10 @@ class PreferencesRenderersController extends Disposable { ...@@ -380,6 +382,10 @@ class PreferencesRenderersController extends Disposable {
super(); super();
} }
get lastFilterResult(): IFilterOrSearchResult {
return this._lastFilterResult;
}
get defaultPreferencesRenderer(): IPreferencesRenderer<ISetting> { get defaultPreferencesRenderer(): IPreferencesRenderer<ISetting> {
return this._defaultPreferencesRenderer; return this._defaultPreferencesRenderer;
} }
...@@ -413,34 +419,47 @@ class PreferencesRenderersController extends Disposable { ...@@ -413,34 +419,47 @@ class PreferencesRenderersController extends Disposable {
} }
} }
async _onEditableContentDidChange(): TPromise<void> { private async _onEditableContentDidChange(): TPromise<void> {
await this.localFilterPreferences(this._lastQuery, true); await this.localFilterPreferences(this._lastQuery, true);
await this.remoteSearchPreferences(this._lastQuery, true); await this.remoteSearchPreferences(this._lastQuery, true);
} }
remoteSearchPreferences(query: string, updateCurrentResults?: boolean): TPromise<IFilterOrSearchResult> { remoteSearchPreferences(query: string, updateCurrentResults?: boolean): TPromise<void> {
if (this._remoteFilterInProgress && this._remoteFilterInProgress.cancel) {
// Resolved/rejected promises have no .cancel()
this._remoteFilterInProgress.cancel();
}
this._currentRemoteSearchProvider = (updateCurrentResults && this._currentRemoteSearchProvider) || this.preferencesSearchService.getRemoteSearchProvider(query); this._currentRemoteSearchProvider = (updateCurrentResults && this._currentRemoteSearchProvider) || this.preferencesSearchService.getRemoteSearchProvider(query);
return this.filterOrSearchPreferences(query, this._currentRemoteSearchProvider, 'nlpResult', nls.localize('nlpResult', "Natural Language Results")); this._currentNewExtensionsSearchProvider = (updateCurrentResults && this._currentNewExtensionsSearchProvider) || this.preferencesSearchService.getRemoteSearchProvider(query, true);
this._remoteFilterInProgress = this.filterOrSearchPreferences(query, this._currentRemoteSearchProvider, 'nlpResult', nls.localize('nlpResult', "Natural Language Results"), 1)
.then(result => this.filterOrSearchPreferences(query, this._currentNewExtensionsSearchProvider, 'newExtensionsResult', nls.localize('newExtensionsResult', "Other Extension Results"), 2));
return this._remoteFilterInProgress.then(() => {
this._remoteFilterInProgress = null;
}, err => {
if (isPromiseCanceledError(err)) {
return null;
} else {
onUnexpectedError(err);
}
});
} }
localFilterPreferences(query: string, updateCurrentResults?: boolean): TPromise<IFilterOrSearchResult> { localFilterPreferences(query: string, updateCurrentResults?: boolean): TPromise<void> {
this._currentLocalSearchProvider = (updateCurrentResults && this._currentLocalSearchProvider) || this.preferencesSearchService.getLocalSearchProvider(query); this._currentLocalSearchProvider = (updateCurrentResults && this._currentLocalSearchProvider) || this.preferencesSearchService.getLocalSearchProvider(query);
return this.filterOrSearchPreferences(query, this._currentLocalSearchProvider, 'filterResult', nls.localize('filterResult', "Filtered Results")); return this.filterOrSearchPreferences(query, this._currentLocalSearchProvider, 'filterResult', nls.localize('filterResult', "Filtered Results"), 0);
} }
filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string): TPromise<IFilterOrSearchResult> { private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise<void> {
this._lastQuery = query; this._lastQuery = query;
if (this._filtersInProgress) {
// Resolved/rejected promises have no .cancel()
this._filtersInProgress.forEach(p => p.cancel && p.cancel());
}
this._filtersInProgress = [ const filterPs = [
this._filterOrSearchPreferences(query, this.defaultPreferencesRenderer, searchProvider, groupId, groupLabel), this._filterOrSearchPreferences(query, this.defaultPreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder),
this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel)]; this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder)];
return TPromise.join(this._filtersInProgress).then(results => { return TPromise.join(filterPs).then(results => {
this._filtersInProgress = null;
const [defaultFilterResult, editableFilterResult] = results; const [defaultFilterResult, editableFilterResult] = results;
this.consolidateAndUpdate(defaultFilterResult, editableFilterResult); this.consolidateAndUpdate(defaultFilterResult, editableFilterResult);
...@@ -449,13 +468,7 @@ class PreferencesRenderersController extends Disposable { ...@@ -449,13 +468,7 @@ class PreferencesRenderersController extends Disposable {
defaultSettingsGroupCounts: defaultFilterResult && this._countById(defaultFilterResult.filteredGroups) defaultSettingsGroupCounts: defaultFilterResult && this._countById(defaultFilterResult.filteredGroups)
}; };
return result; this._lastFilterResult = result;
}, err => {
if (isPromiseCanceledError(err)) {
return null;
} else {
onUnexpectedError(err);
}
}); });
} }
...@@ -469,7 +482,7 @@ class PreferencesRenderersController extends Disposable { ...@@ -469,7 +482,7 @@ class PreferencesRenderersController extends Disposable {
this._focusPreference(setting, this._editablePreferencesRenderer); this._focusPreference(setting, this._editablePreferencesRenderer);
} }
private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer<ISetting>, provider: ISearchProvider, groupId: string, groupLabel: string): TPromise<IFilterResult> { private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer<ISetting>, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise<IFilterResult> {
if (preferencesRenderer) { if (preferencesRenderer) {
const model = <ISettingsEditorModel>preferencesRenderer.preferencesModel; const model = <ISettingsEditorModel>preferencesRenderer.preferencesModel;
const searchP = provider ? provider.searchModel(model) : TPromise.wrap(null); const searchP = provider ? provider.searchModel(model) : TPromise.wrap(null);
...@@ -493,7 +506,8 @@ class PreferencesRenderersController extends Disposable { ...@@ -493,7 +506,8 @@ class PreferencesRenderersController extends Disposable {
model.updateResultGroup(groupId, { model.updateResultGroup(groupId, {
id: groupId, id: groupId,
label: groupLabel, label: groupLabel,
result: searchResult result: searchResult,
order: groupOrder
}) : }) :
model.updateResultGroup(groupId, null); model.updateResultGroup(groupId, null);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { Delayer } from 'vs/base/common/async'; import { Delayer } from 'vs/base/common/async';
import * as arrays from 'vs/base/common/arrays';
import * as strings from 'vs/base/common/strings'; import * as strings from 'vs/base/common/strings';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IAction } from 'vs/base/common/actions'; import { IAction } from 'vs/base/common/actions';
...@@ -16,7 +17,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; ...@@ -16,7 +17,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { Range, IRange } from 'vs/editor/common/core/range'; import { Range, IRange } from 'vs/editor/common/core/range';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, ISettingsEditorModel, IScoredResults, IWorkbenchSettingsConfiguration } from 'vs/workbench/parts/preferences/common/preferences'; import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, ISettingsEditorModel, IScoredResults, IWorkbenchSettingsConfiguration, IExtensionSetting } from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; import { SettingsEditorModel, DefaultSettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView'; import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView';
...@@ -33,6 +34,8 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; ...@@ -33,6 +34,8 @@ import { MarkdownString } from 'vs/base/common/htmlContent';
import { overrideIdentifierFromKey, IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { overrideIdentifierFromKey, IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
import { CodeLensProviderRegistry, CodeLensProvider, ICodeLensSymbol } from 'vs/editor/common/modes';
import { CancellationToken } from 'vs/base/common/cancellation';
export interface IPreferencesRenderer<T> extends IDisposable { export interface IPreferencesRenderer<T> extends IDisposable {
readonly preferencesModel: IPreferencesEditorModel<T>; readonly preferencesModel: IPreferencesEditorModel<T>;
...@@ -254,6 +257,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR ...@@ -254,6 +257,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
private editSettingActionRenderer: EditSettingRenderer; private editSettingActionRenderer: EditSettingRenderer;
private feedbackWidgetRenderer: FeedbackWidgetRenderer; private feedbackWidgetRenderer: FeedbackWidgetRenderer;
private bracesHidingRenderer: BracesHidingRenderer; private bracesHidingRenderer: BracesHidingRenderer;
private extensionCodelensRenderer: ExtensionCodelensRenderer;
private filterResult: IFilterResult; private filterResult: IFilterResult;
private _onUpdatePreference: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>(); private _onUpdatePreference: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>();
...@@ -278,6 +282,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR ...@@ -278,6 +282,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.feedbackWidgetRenderer = this._register(instantiationService.createInstance(FeedbackWidgetRenderer, editor)); this.feedbackWidgetRenderer = this._register(instantiationService.createInstance(FeedbackWidgetRenderer, editor));
this.bracesHidingRenderer = this._register(instantiationService.createInstance(BracesHidingRenderer, editor, preferencesModel)); this.bracesHidingRenderer = this._register(instantiationService.createInstance(BracesHidingRenderer, editor, preferencesModel));
this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, this.bracesHidingRenderer])); this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, this.bracesHidingRenderer]));
this.extensionCodelensRenderer = this._register(instantiationService.createInstance(ExtensionCodelensRenderer, editor));
this._register(this.editSettingActionRenderer.onUpdateSetting(e => this._onUpdatePreference.fire(e))); this._register(this.editSettingActionRenderer.onUpdateSetting(e => this._onUpdatePreference.fire(e)));
this._register(this.settingsGroupTitleRenderer.onHiddenAreasChanged(() => this.hiddenAreasRenderer.render())); this._register(this.settingsGroupTitleRenderer.onHiddenAreasChanged(() => this.hiddenAreasRenderer.render()));
...@@ -305,6 +310,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR ...@@ -305,6 +310,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
public filterPreferences(filterResult: IFilterResult): void { public filterPreferences(filterResult: IFilterResult): void {
this.filterResult = filterResult; this.filterResult = filterResult;
if (filterResult) { if (filterResult) {
this.filteredMatchesRenderer.render(filterResult, this.preferencesModel.settingsGroups); this.filteredMatchesRenderer.render(filterResult, this.preferencesModel.settingsGroups);
this.settingsGroupTitleRenderer.render(filterResult.filteredGroups); this.settingsGroupTitleRenderer.render(filterResult.filteredGroups);
...@@ -313,6 +319,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR ...@@ -313,6 +319,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.settingHighlighter.clear(true); this.settingHighlighter.clear(true);
this.bracesHidingRenderer.render(filterResult, this.preferencesModel.settingsGroups); this.bracesHidingRenderer.render(filterResult, this.preferencesModel.settingsGroups);
this.editSettingActionRenderer.render(filterResult.filteredGroups, this._associatedPreferencesModel); this.editSettingActionRenderer.render(filterResult.filteredGroups, this._associatedPreferencesModel);
this.extensionCodelensRenderer.render(filterResult);
} else { } else {
this.settingHighlighter.clear(true); this.settingHighlighter.clear(true);
this.filteredMatchesRenderer.render(null, this.preferencesModel.settingsGroups); this.filteredMatchesRenderer.render(null, this.preferencesModel.settingsGroups);
...@@ -322,6 +329,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR ...@@ -322,6 +329,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.settingsGroupTitleRenderer.showGroup(0); this.settingsGroupTitleRenderer.showGroup(0);
this.bracesHidingRenderer.render(null, this.preferencesModel.settingsGroups); this.bracesHidingRenderer.render(null, this.preferencesModel.settingsGroups);
this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel); this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel);
this.extensionCodelensRenderer.render(null);
} }
this.hiddenAreasRenderer.render(); this.hiddenAreasRenderer.render();
...@@ -615,20 +623,23 @@ export class FeedbackWidgetRenderer extends Disposable { ...@@ -615,20 +623,23 @@ export class FeedbackWidgetRenderer extends Disposable {
const result = this._currentResult; const result = this._currentResult;
const actualResults = result.metadata.scoredResults; const actualResults = result.metadata.scoredResults;
const actualResultNames = Object.keys(actualResults); const actualResultIds = Object.keys(actualResults);
const feedbackQuery: any = {}; const feedbackQuery: any = {};
feedbackQuery['comment'] = FeedbackWidgetRenderer.DEFAULT_COMMENT_TEXT; feedbackQuery['comment'] = FeedbackWidgetRenderer.DEFAULT_COMMENT_TEXT;
feedbackQuery['queryString'] = result.query; feedbackQuery['queryString'] = result.query;
feedbackQuery['resultScores'] = {}; feedbackQuery['resultScores'] = {};
actualResultNames.forEach(settingKey => { actualResultIds.forEach(settingId => {
feedbackQuery['resultScores'][settingKey] = 10; const outputKey = actualResults[settingId].key;
feedbackQuery['resultScores'][outputKey] = 10;
}); });
feedbackQuery['alts'] = []; feedbackQuery['alts'] = [];
const contents = FeedbackWidgetRenderer.INSTRUCTION_TEXT + '\n' + const contents = FeedbackWidgetRenderer.INSTRUCTION_TEXT + '\n' +
JSON.stringify(feedbackQuery, undefined, ' ') + '\n\n' + JSON.stringify(feedbackQuery, undefined, ' ') + '\n\n' +
actualResultNames.map(name => `// ${name}: ${result.metadata.scoredResults[name]}`).join('\n'); actualResultIds.map(name => {
return `// ${actualResults[name].key}: ${actualResults[name].score}`;
}).join('\n');
this.editorService.openEditor({ contents, language: 'jsonc' }, /*sideBySide=*/true).then(feedbackEditor => { this.editorService.openEditor({ contents, language: 'jsonc' }, /*sideBySide=*/true).then(feedbackEditor => {
const sendFeedbackWidget = this._register(this.instantiationService.createInstance(FloatingClickWidget, feedbackEditor.getControl(), 'Send feedback', null)); const sendFeedbackWidget = this._register(this.instantiationService.createInstance(FloatingClickWidget, feedbackEditor.getControl(), 'Send feedback', null));
...@@ -835,6 +846,51 @@ export class HighlightMatchesRenderer extends Disposable { ...@@ -835,6 +846,51 @@ export class HighlightMatchesRenderer extends Disposable {
} }
} }
export class ExtensionCodelensRenderer extends Disposable implements CodeLensProvider {
private filterResult: IFilterResult;
constructor() {
super();
this._register(CodeLensProviderRegistry.register({ pattern: '**/settings.json' }, this));
}
public render(filterResult: IFilterResult): void {
this.filterResult = filterResult;
}
public provideCodeLenses(model: ITextModel, token: CancellationToken): ICodeLensSymbol[] {
if (!this.filterResult || !this.filterResult.filteredGroups) {
return [];
}
const newExtensionGroup = arrays.first(this.filterResult.filteredGroups, g => g.id === 'newExtensionsResult');
if (!newExtensionGroup) {
return [];
}
return newExtensionGroup.sections[0].settings
.filter((s: IExtensionSetting) => {
// Skip any non IExtensionSettings that somehow got in here
return s.extensionName && s.extensionPublisher;
})
.map((s: IExtensionSetting) => {
const extId = s.extensionPublisher + '.' + s.extensionName;
return <ICodeLensSymbol>{
command: {
title: nls.localize('newExtensionLabel', "View \"{0}\"", extId),
id: 'workbench.extensions.action.showExtensionsWithId',
arguments: [extId.toLowerCase()]
},
range: new Range(s.keyRange.startLineNumber, 1, s.keyRange.startLineNumber, 1)
};
});
}
public resolveCodeLens(model: ITextModel, codeLens: ICodeLensSymbol, token: CancellationToken): ICodeLensSymbol {
return codeLens;
}
}
export interface IIndexedSetting extends ISetting { export interface IIndexedSetting extends ISetting {
index: number; index: number;
groupId: string; groupId: string;
......
...@@ -24,6 +24,7 @@ export interface IWorkbenchSettingsConfiguration { ...@@ -24,6 +24,7 @@ export interface IWorkbenchSettingsConfiguration {
naturalLanguageSearchEndpoint: string; naturalLanguageSearchEndpoint: string;
naturalLanguageSearchKey: string; naturalLanguageSearchKey: string;
naturalLanguageSearchAutoIngestFeedback: boolean; naturalLanguageSearchAutoIngestFeedback: boolean;
useNaturalLanguageSearchPost: boolean;
enableNaturalLanguageSearch: boolean; enableNaturalLanguageSearch: boolean;
enableNaturalLanguageSearchFeedback: boolean; enableNaturalLanguageSearchFeedback: boolean;
} }
...@@ -56,6 +57,11 @@ export interface ISetting { ...@@ -56,6 +57,11 @@ export interface ISetting {
overrideOf?: ISetting; overrideOf?: ISetting;
} }
export interface IExtensionSetting extends ISetting {
extensionName: string;
extensionPublisher: string;
}
export interface ISearchResult { export interface ISearchResult {
filterMatches: ISettingMatch[]; filterMatches: ISettingMatch[];
metadata?: IFilterMetadata; metadata?: IFilterMetadata;
...@@ -65,6 +71,7 @@ export interface ISearchResultGroup { ...@@ -65,6 +71,7 @@ export interface ISearchResultGroup {
id: string; id: string;
label: string; label: string;
result: ISearchResult; result: ISearchResult;
order: number;
} }
export interface IFilterResult { export interface IFilterResult {
...@@ -82,7 +89,18 @@ export interface ISettingMatch { ...@@ -82,7 +89,18 @@ export interface ISettingMatch {
} }
export interface IScoredResults { export interface IScoredResults {
[key: string]: number; [key: string]: IRemoteSetting;
}
export interface IRemoteSetting {
score: number;
key: string;
id: string;
defaultValue: string;
description: string;
packageId: string;
extensionName?: string;
extensionPublisher?: string;
} }
export interface IFilterMetadata { export interface IFilterMetadata {
...@@ -102,7 +120,7 @@ export interface IPreferencesEditorModel<T> { ...@@ -102,7 +120,7 @@ export interface IPreferencesEditorModel<T> {
} }
export type IGroupFilter = (group: ISettingsGroup) => boolean; export type IGroupFilter = (group: ISettingsGroup) => boolean;
export type ISettingMatcher = (setting: ISetting) => { matches: IRange[], score: number }; export type ISettingMatcher = (setting: ISetting, group: ISettingsGroup) => { matches: IRange[], score: number };
export interface ISettingsEditorModel extends IPreferencesEditorModel<ISetting> { export interface ISettingsEditorModel extends IPreferencesEditorModel<ISetting> {
readonly onDidChangeGroups: Event<void>; readonly onDidChangeGroups: Event<void>;
...@@ -177,7 +195,7 @@ export interface IPreferencesSearchService { ...@@ -177,7 +195,7 @@ export interface IPreferencesSearchService {
_serviceBrand: any; _serviceBrand: any;
getLocalSearchProvider(filter: string): ISearchProvider; getLocalSearchProvider(filter: string): ISearchProvider;
getRemoteSearchProvider(filter: string): ISearchProvider; getRemoteSearchProvider(filter: string, newExtensionsOnly?: boolean): ISearchProvider;
} }
export interface ISearchProvider { export interface ISearchProvider {
......
...@@ -41,12 +41,14 @@ export abstract class AbstractSettingsModel extends EditorModel { ...@@ -41,12 +41,14 @@ export abstract class AbstractSettingsModel extends EditorModel {
* Remove duplicates between result groups, preferring results in earlier groups * Remove duplicates between result groups, preferring results in earlier groups
*/ */
private removeDuplicateResults(): void { private removeDuplicateResults(): void {
// Depends on order of map keys
const settingKeys = new Set<string>(); const settingKeys = new Set<string>();
this._currentResultGroups.forEach((group, id) => { map.keys(this._currentResultGroups)
group.result.filterMatches = group.result.filterMatches.filter(s => !settingKeys.has(s.setting.key)); .sort((a, b) => this._currentResultGroups.get(a).order - this._currentResultGroups.get(b).order)
group.result.filterMatches.forEach(s => settingKeys.add(s.setting.key)); .forEach(groupId => {
}); const group = this._currentResultGroups.get(groupId);
group.result.filterMatches = group.result.filterMatches.filter(s => !settingKeys.has(s.setting.key));
group.result.filterMatches.forEach(s => settingKeys.add(s.setting.key));
});
} }
public filterSettings(filter: string, groupFilter: IGroupFilter, settingMatcher: ISettingMatcher): ISettingMatch[] { public filterSettings(filter: string, groupFilter: IGroupFilter, settingMatcher: ISettingMatcher): ISettingMatch[] {
...@@ -57,11 +59,11 @@ export abstract class AbstractSettingsModel extends EditorModel { ...@@ -57,11 +59,11 @@ export abstract class AbstractSettingsModel extends EditorModel {
const groupMatched = groupFilter(group); const groupMatched = groupFilter(group);
for (const section of group.sections) { for (const section of group.sections) {
for (const setting of section.settings) { for (const setting of section.settings) {
const settingMatchResult = settingMatcher(setting); const settingMatchResult = settingMatcher(setting, group);
if (groupMatched || settingMatchResult) { if (groupMatched || settingMatchResult) {
filterMatches.push({ filterMatches.push({
setting, setting: this.copySetting(setting),
matches: settingMatchResult && settingMatchResult.matches, matches: settingMatchResult && settingMatchResult.matches,
score: settingMatchResult ? settingMatchResult.score : 0 score: settingMatchResult ? settingMatchResult.score : 0
}); });
...@@ -70,7 +72,22 @@ export abstract class AbstractSettingsModel extends EditorModel { ...@@ -70,7 +72,22 @@ export abstract class AbstractSettingsModel extends EditorModel {
} }
} }
return filterMatches.sort((a, b) => b.score - a.score); return filterMatches
.sort((a, b) => b.score - a.score)
.map(filteredMatch => {
// Fix match ranges to offset from setting start line
return <ISettingMatch>{
setting: filteredMatch.setting,
score: filteredMatch.score,
matches: filteredMatch.matches && filteredMatch.matches.map(match => {
return new Range(
match.startLineNumber - filteredMatch.setting.range.startLineNumber,
match.startColumn,
match.endLineNumber - filteredMatch.setting.range.startLineNumber,
match.endColumn);
})
};
});
} }
public getPreference(key: string): ISetting { public getPreference(key: string): ISetting {
...@@ -86,6 +103,18 @@ export abstract class AbstractSettingsModel extends EditorModel { ...@@ -86,6 +103,18 @@ export abstract class AbstractSettingsModel extends EditorModel {
return null; return null;
} }
private copySetting(setting: ISetting): ISetting {
return <ISetting>{
description: setting.description,
key: setting.key,
value: setting.value,
range: setting.range,
overrides: [],
overrideOf: setting.overrideOf
};
}
protected get filterGroups(): ISettingsGroup[] { protected get filterGroups(): ISettingsGroup[] {
return this.settingsGroups; return this.settingsGroups;
} }
...@@ -611,7 +640,9 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ...@@ -611,7 +640,9 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
protected update(): IFilterResult { protected update(): IFilterResult {
// Grab current result groups, only render non-empty groups // Grab current result groups, only render non-empty groups
const resultGroups = map.values(this._currentResultGroups); const resultGroups = map
.values(this._currentResultGroups)
.sort((a, b) => a.order - b.order);
const nonEmptyResultGroups = resultGroups.filter(group => group.result.filterMatches.length); const nonEmptyResultGroups = resultGroups.filter(group => group.result.filterMatches.length);
const startLine = tail(this.settingsGroups).range.endLineNumber + 2; const startLine = tail(this.settingsGroups).range.endLineNumber + 2;
...@@ -665,21 +696,6 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ...@@ -665,21 +696,6 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
} }
private writeSettingsGroupToBuilder(builder: SettingsContentBuilder, settingsGroup: ISettingsGroup, filterMatches: ISettingMatch[]): IRange[] { private writeSettingsGroupToBuilder(builder: SettingsContentBuilder, settingsGroup: ISettingsGroup, filterMatches: ISettingMatch[]): IRange[] {
// Fix match ranges to offset from setting start line
filterMatches = filterMatches.map(filteredMatch => {
return <ISettingMatch>{
setting: filteredMatch.setting,
score: filteredMatch.score,
matches: filteredMatch.matches && filteredMatch.matches.map(match => {
return new Range(
match.startLineNumber - filteredMatch.setting.range.startLineNumber,
match.startColumn,
match.endLineNumber - filteredMatch.setting.range.startLineNumber,
match.endColumn);
})
};
});
builder.pushGroup(settingsGroup); builder.pushGroup(settingsGroup);
builder.pushLine(','); builder.pushLine(',');
...@@ -718,19 +734,6 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ...@@ -718,19 +734,6 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
return null; return null;
} }
private copySettings(settings: ISetting[]): ISetting[] {
return settings.map(setting => {
return <ISetting>{
description: setting.description,
key: setting.key,
value: setting.value,
range: null,
valueRange: null,
overrides: []
};
});
}
private getGroup(resultGroup: ISearchResultGroup): ISettingsGroup { private getGroup(resultGroup: ISearchResultGroup): ISettingsGroup {
return <ISettingsGroup>{ return <ISettingsGroup>{
id: resultGroup.id, id: resultGroup.id,
...@@ -739,7 +742,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ...@@ -739,7 +742,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
titleRange: null, titleRange: null,
sections: [ sections: [
{ {
settings: this.copySettings(resultGroup.result.filterMatches.map(m => m.setting)) settings: resultGroup.result.filterMatches.map(m => m.setting)
} }
] ]
}; };
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { ISettingsEditorModel, ISetting, ISettingsGroup, IWorkbenchSettingsConfiguration, IFilterMetadata, IPreferencesSearchService, ISearchResult, ISearchProvider, IGroupFilter, ISettingMatcher, IScoredResults } from 'vs/workbench/parts/preferences/common/preferences'; import { ISettingsEditorModel, ISetting, ISettingsGroup, IWorkbenchSettingsConfiguration, IFilterMetadata, IPreferencesSearchService, ISearchResult, ISearchProvider, IGroupFilter, ISettingMatcher, IScoredResults, ISettingMatch, IRemoteSetting, IExtensionSetting } from 'vs/workbench/parts/preferences/common/preferences';
import { IRange } from 'vs/editor/common/core/range'; import { IRange } from 'vs/editor/common/core/range';
import { distinct, top } from 'vs/base/common/arrays'; import { distinct, top } from 'vs/base/common/arrays';
import * as strings from 'vs/base/common/strings'; import * as strings from 'vs/base/common/strings';
...@@ -19,6 +19,7 @@ import { IRequestService } from 'vs/platform/request/node/request'; ...@@ -19,6 +19,7 @@ import { IRequestService } from 'vs/platform/request/node/request';
import { asJson } from 'vs/base/node/request'; import { asJson } from 'vs/base/node/request';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { IExtensionManagementService, LocalExtensionType, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementService, LocalExtensionType, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILogService } from 'vs/platform/log/common/log';
export interface IEndpointDetails { export interface IEndpointDetails {
urlBase: string; urlBase: string;
...@@ -67,8 +68,17 @@ export class PreferencesSearchService extends Disposable implements IPreferences ...@@ -67,8 +68,17 @@ export class PreferencesSearchService extends Disposable implements IPreferences
} }
} }
getRemoteSearchProvider(filter: string): RemoteSearchProvider { getRemoteSearchProvider(filter: string, newExtensionsOnly = false): ISearchProvider {
return this.remoteSearchAllowed && this.instantiationService.createInstance(RemoteSearchProvider, filter, this._endpoint, this._installedExtensions); const workbenchSettings = this.configurationService.getValue<IWorkbenchSettingsConfiguration>().workbench.settings;
const opts: IRemoteSearchProviderOptions = {
filter,
newExtensionsOnly,
endpoint: this._endpoint,
usePost: workbenchSettings.useNaturalLanguageSearchPost
};
return this.remoteSearchAllowed && this.instantiationService.createInstance(RemoteSearchProvider, opts, this._installedExtensions);
} }
getLocalSearchProvider(filter: string): LocalSearchProvider { getLocalSearchProvider(filter: string): LocalSearchProvider {
...@@ -90,7 +100,7 @@ export class LocalSearchProvider implements ISearchProvider { ...@@ -90,7 +100,7 @@ export class LocalSearchProvider implements ISearchProvider {
let score = 1000; // Sort is not stable let score = 1000; // Sort is not stable
const settingMatcher = (setting: ISetting) => { const settingMatcher = (setting: ISetting) => {
const matches = new SettingMatches(this._filter, setting, true, (filter, setting) => preferencesModel.findValueMatches(filter, setting)).matches; const matches = new SettingMatches(this._filter, setting, true, false, (filter, setting) => preferencesModel.findValueMatches(filter, setting)).matches;
return matches && matches.length ? return matches && matches.length ?
{ {
matches, matches,
...@@ -113,77 +123,124 @@ export class LocalSearchProvider implements ISearchProvider { ...@@ -113,77 +123,124 @@ export class LocalSearchProvider implements ISearchProvider {
} }
} }
export class RemoteSearchProvider implements ISearchProvider { interface IRemoteSearchProviderOptions {
private _filter: string; filter: string;
endpoint: IEndpointDetails;
newExtensionsOnly: boolean;
usePost: boolean;
}
class RemoteSearchProvider implements ISearchProvider {
private _remoteSearchP: TPromise<IFilterMetadata>; private _remoteSearchP: TPromise<IFilterMetadata>;
constructor(filter: string, endpoint: IEndpointDetails, private installedExtensions: TPromise<ILocalExtension[]>, constructor(private options: IRemoteSearchProviderOptions, private installedExtensions: TPromise<ILocalExtension[]>,
@IEnvironmentService private environmentService: IEnvironmentService, @IEnvironmentService private environmentService: IEnvironmentService,
@IRequestService private requestService: IRequestService, @IRequestService private requestService: IRequestService,
@ILogService private logService: ILogService
) { ) {
this._filter = filter; this._remoteSearchP = (this.options.newExtensionsOnly && !this.options.usePost) ? TPromise.wrap(null) :
this.options.filter ?
// @queries are always handled by local filter this.getSettingsFromBing(this.options.filter) :
this._remoteSearchP = filter && !strings.startsWith(filter, '@') ? TPromise.wrap(null);
this.getSettingsFromBing(filter, endpoint) :
TPromise.wrap(null);
} }
searchModel(preferencesModel: ISettingsEditorModel): TPromise<ISearchResult> { searchModel(preferencesModel: ISettingsEditorModel): TPromise<ISearchResult> {
return this._remoteSearchP.then(remoteResult => { return this._remoteSearchP.then(remoteResult => {
if (remoteResult) { if (!remoteResult) {
const highScoreKey = top(Object.keys(remoteResult.scoredResults), (a, b) => remoteResult.scoredResults[b] - remoteResult.scoredResults[a], 1)[0]; return null;
const highScore = highScoreKey ? remoteResult.scoredResults[highScoreKey] : 0; }
const minScore = highScore / 5;
const resultKeys = Object.keys(remoteResult.scoredResults);
const highScoreKey = top(resultKeys, (a, b) => remoteResult.scoredResults[b].score - remoteResult.scoredResults[a].score, 1)[0];
const highScore = highScoreKey ? remoteResult.scoredResults[highScoreKey].score : 0;
const minScore = highScore / 5;
if (this.options.newExtensionsOnly) {
const passingScoreKeys = resultKeys.filter(k => remoteResult.scoredResults[k].score >= minScore);
const filterMatches: ISettingMatch[] = passingScoreKeys.map(k => {
const remoteSetting = remoteResult.scoredResults[k];
const setting = remoteSettingToISetting(remoteSetting);
return <ISettingMatch>{
setting,
score: remoteSetting.score,
matches: [] // TODO
};
});
const settingMatcher = this.getRemoteSettingMatcher(remoteResult.scoredResults, minScore, preferencesModel);
const filterMatches = preferencesModel.filterSettings(this._filter, group => null, settingMatcher);
return <ISearchResult>{ return <ISearchResult>{
filterMatches, filterMatches,
metadata: remoteResult metadata: remoteResult
}; };
} else { } else {
return null; const settingMatcher = this.getRemoteSettingMatcher(remoteResult.scoredResults, minScore, preferencesModel);
const filterMatches = preferencesModel.filterSettings(this.options.filter, group => null, settingMatcher);
return <ISearchResult>{
filterMatches,
metadata: remoteResult
};
} }
}); });
} }
private getSettingsFromBing(filter: string, endpoint: IEndpointDetails): TPromise<IFilterMetadata> { private getSettingsFromBing(filter: string): TPromise<IFilterMetadata> {
const start = Date.now(); const start = Date.now();
return this.prepareUrl(filter, endpoint, this.environmentService.settingsSearchBuildId).then(url => { return this.prepareRequest(filter).then(details => {
this.logService.debug(`Searching settings via ${details.url}`);
if (details.body) {
this.logService.debug(`Body: ${details.body}`);
}
const requestType = details.body ? 'post' : 'get';
return this.requestService.request({ return this.requestService.request({
url, type: requestType,
url: details.url,
data: details.body,
headers: { headers: {
'User-Agent': 'request', 'User-Agent': 'request',
'Content-Type': 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
'api-key': endpoint.key 'api-key': this.options.endpoint.key
}, },
timeout: 5000 timeout: 5000
}).then(context => { }).then(context => {
if (context.res.statusCode >= 300) { if (context.res.statusCode >= 300) {
throw new Error(`${url} returned status code: ${context.res.statusCode}`); throw new Error(`${details} returned status code: ${context.res.statusCode}`);
} }
return asJson(context); return asJson(context);
}).then((result: any) => { }).then((result: any) => {
const timestamp = Date.now(); const timestamp = Date.now();
const duration = timestamp - start; const duration = timestamp - start;
const suggestions = (result.value || []) const remoteSettings: IRemoteSetting[] = (result.value || [])
.map(r => ({ .map(r => {
name: r.setting || r.Setting, const key = JSON.parse(r.setting || r.Setting);
score: r['@search.score'] const packageId = r['packageid'];
})); const id = getSettingKey(key, packageId);
const packageName = r['packagename'];
let extensionName: string;
let extensionPublisher: string;
if (packageName && packageName.indexOf('##') >= 0) {
[extensionPublisher, extensionName] = packageName.split('##');
}
return <IRemoteSetting>{
key,
id,
defaultValue: r['value'],
score: r['@search.score'],
description: JSON.parse(r['details']),
packageId,
extensionName,
extensionPublisher
};
});
const scoredResults = Object.create(null); const scoredResults = Object.create(null);
suggestions.forEach(s => { remoteSettings.forEach(s => {
const name = s.name scoredResults[s.id] = s;
.replace(/^"/, '')
.replace(/"$/, '');
scoredResults[name] = s.score;
}); });
return <IFilterMetadata>{ return <IFilterMetadata>{
remoteUrl: url, remoteUrl: details.url, // telemetry for filter text?
duration, duration,
timestamp, timestamp,
scoredResults, scoredResults,
...@@ -194,18 +251,20 @@ export class RemoteSearchProvider implements ISearchProvider { ...@@ -194,18 +251,20 @@ export class RemoteSearchProvider implements ISearchProvider {
} }
private getRemoteSettingMatcher(scoredResults: IScoredResults, minScore: number, preferencesModel: ISettingsEditorModel): ISettingMatcher { private getRemoteSettingMatcher(scoredResults: IScoredResults, minScore: number, preferencesModel: ISettingsEditorModel): ISettingMatcher {
return (setting: ISetting) => { return (setting: ISetting, group: ISettingsGroup) => {
const score = scoredResults[setting.key]; const remoteSetting = scoredResults[getSettingKey(setting.key, group.id)] || // extension setting
if (typeof score === 'number' && score >= minScore) { scoredResults[getSettingKey(setting.key, 'core')] || // core setting
const settingMatches = new SettingMatches(this._filter, setting, false, (filter, setting) => preferencesModel.findValueMatches(filter, setting)).matches; scoredResults[getSettingKey(setting.key)]; // core setting from original prod endpoint
return { matches: settingMatches, score: scoredResults[setting.key] }; if (remoteSetting && remoteSetting.score >= minScore) {
const settingMatches = new SettingMatches(this.options.filter, setting, false, false, (filter, setting) => preferencesModel.findValueMatches(filter, setting)).matches;
return { matches: settingMatches, score: remoteSetting.score };
} }
return null; return null;
}; };
} }
private prepareUrl(query: string, endpoint: IEndpointDetails, buildNumber: number): TPromise<string> { private async prepareRequest(query: string): TPromise<{ url: string, body?: string }> {
query = escapeSpecialChars(query); query = escapeSpecialChars(query);
const boost = 10; const boost = 10;
const userQuery = `(${query})^${boost}`; const userQuery = `(${query})^${boost}`;
...@@ -214,41 +273,73 @@ export class RemoteSearchProvider implements ISearchProvider { ...@@ -214,41 +273,73 @@ export class RemoteSearchProvider implements ISearchProvider {
query = query.replace(/\ +/g, '~ ') + '~'; query = query.replace(/\ +/g, '~ ') + '~';
const encodedQuery = encodeURIComponent(userQuery + ' || ' + query); const encodedQuery = encodeURIComponent(userQuery + ' || ' + query);
let url = `${endpoint.urlBase}?`; let url = `${this.options.endpoint.urlBase}?`;
return this.installedExtensions.then(exts => { const buildNumber = this.environmentService.settingsSearchBuildId;
if (endpoint.key) { if (this.options.endpoint.key) {
url += `${API_VERSION}`; url += `${API_VERSION}&${QUERY_TYPE}`;
url += `&search=${encodedQuery}`; }
const filters = exts.map(ext => {
const uuid = ext.identifier.uuid;
const versionString = ext.manifest.version
.split('.')
.map(versionPart => strings.pad(<any>versionPart, 10))
.join('');
return `(packageid eq '${uuid}' and startbuildno le '${versionString}' and endbuildno ge '${versionString}')`;
});
if (buildNumber) { if (this.options.usePost) {
filters.push(`(packageid eq 'core' and startbuildno le '${buildNumber}' and endbuildno ge '${buildNumber}')`); const filters = this.options.newExtensionsOnly ?
url += `&$filter=${filters.join(' or ')}`; [`diminish eq 'latest'`] :
} await this.getVersionFilters(buildNumber);
} else {
url += `query=${encodedQuery}`;
if (buildNumber) { const filterStr = encodeURIComponent(filters.join(' or '));
url += `&build=${buildNumber}`; const body = JSON.stringify({
} query: encodedQuery,
filters: filterStr
});
return {
url,
body
};
} else {
url += `query=${encodedQuery}`;
if (buildNumber) {
url += `&build=${buildNumber}`;
} }
}
return TPromise.wrap({ url });
}
return url; 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));
if (buildNumber) {
filters.push(`(packageid eq 'core' and startbuildno le '${buildNumber}' and endbuildno ge '${buildNumber}')`);
}
return filters;
}); });
} }
private getExtensionFilter(ext: ILocalExtension): string {
const uuid = ext.identifier.uuid;
const versionString = ext.manifest.version
.split('.')
.map(versionPart => strings.pad(<any>versionPart, 10))
.join('');
return `(packageid eq '${uuid}' and startbuildno le '${versionString}' and endbuildno ge '${versionString}')`;
}
}
function getSettingKey(name: string, packageId?: string): string {
return packageId ?
packageId + '_' + name :
name;
} }
const API_VERSION = 'api-version=2016-09-01-Preview'; const API_VERSION = 'api-version=2016-09-01-Preview';
const QUERY_TYPE = 'querytype=full';
function escapeSpecialChars(query: string): string { function escapeSpecialChars(query: string): string {
return query.replace(/\./g, ' ') return query.replace(/\./g, ' ')
...@@ -257,6 +348,21 @@ function escapeSpecialChars(query: string): string { ...@@ -257,6 +348,21 @@ function escapeSpecialChars(query: string): string {
.trim(); .trim();
} }
function remoteSettingToISetting(remoteSetting: IRemoteSetting): IExtensionSetting {
return {
description: remoteSetting.description.split('\n'),
descriptionRanges: null,
key: remoteSetting.key,
keyRange: null,
value: remoteSetting.defaultValue,
range: null,
valueRange: null,
overrides: [],
extensionName: remoteSetting.extensionName,
extensionPublisher: remoteSetting.extensionPublisher
};
}
class SettingMatches { class SettingMatches {
private readonly descriptionMatchingWords: Map<string, IRange[]> = new Map<string, IRange[]>(); private readonly descriptionMatchingWords: Map<string, IRange[]> = new Map<string, IRange[]>();
...@@ -265,7 +371,7 @@ class SettingMatches { ...@@ -265,7 +371,7 @@ class SettingMatches {
public readonly matches: IRange[]; public readonly matches: IRange[];
constructor(searchString: string, setting: ISetting, private requireFullQueryMatch: boolean, private valuesMatcher: (filter: string, setting: ISetting) => IRange[]) { constructor(searchString: string, setting: ISetting, private requireFullQueryMatch: boolean, private searchDescription, private valuesMatcher: (filter: string, setting: ISetting) => IRange[]) {
this.matches = distinct(this._findMatchesInSetting(searchString, setting), (match) => `${match.startLineNumber}_${match.startColumn}_${match.endLineNumber}_${match.endColumn}_`); this.matches = distinct(this._findMatchesInSetting(searchString, setting), (match) => `${match.startLineNumber}_${match.startColumn}_${match.endLineNumber}_${match.endColumn}_`);
} }
...@@ -273,7 +379,7 @@ class SettingMatches { ...@@ -273,7 +379,7 @@ class SettingMatches {
const result = this._doFindMatchesInSetting(searchString, setting); const result = this._doFindMatchesInSetting(searchString, setting);
if (setting.overrides && setting.overrides.length) { if (setting.overrides && setting.overrides.length) {
for (const subSetting of setting.overrides) { for (const subSetting of setting.overrides) {
const subSettingMatches = new SettingMatches(searchString, subSetting, this.requireFullQueryMatch, this.valuesMatcher); const subSettingMatches = new SettingMatches(searchString, subSetting, this.requireFullQueryMatch, this.searchDescription, this.valuesMatcher);
let words = searchString.split(' '); let words = searchString.split(' ');
const descriptionRanges: IRange[] = this.getRangesForWords(words, this.descriptionMatchingWords, [subSettingMatches.descriptionMatchingWords, subSettingMatches.keyMatchingWords, subSettingMatches.valueMatchingWords]); const descriptionRanges: IRange[] = this.getRangesForWords(words, this.descriptionMatchingWords, [subSettingMatches.descriptionMatchingWords, subSettingMatches.keyMatchingWords, subSettingMatches.valueMatchingWords]);
const keyRanges: IRange[] = this.getRangesForWords(words, this.keyMatchingWords, [subSettingMatches.descriptionMatchingWords, subSettingMatches.keyMatchingWords, subSettingMatches.valueMatchingWords]); const keyRanges: IRange[] = this.getRangesForWords(words, this.keyMatchingWords, [subSettingMatches.descriptionMatchingWords, subSettingMatches.keyMatchingWords, subSettingMatches.valueMatchingWords]);
...@@ -294,10 +400,12 @@ class SettingMatches { ...@@ -294,10 +400,12 @@ class SettingMatches {
const settingKeyAsWords: string = setting.key.split('.').join(' '); const settingKeyAsWords: string = setting.key.split('.').join(' ');
for (const word of words) { for (const word of words) {
for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) { if (this.searchDescription) {
const descriptionMatches = matchesWords(word, setting.description[lineIndex], true); for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) {
if (descriptionMatches) { const descriptionMatches = matchesWords(word, setting.description[lineIndex], true);
this.descriptionMatchingWords.set(word, descriptionMatches.map(match => this.toDescriptionRange(setting, match, lineIndex))); if (descriptionMatches) {
this.descriptionMatchingWords.set(word, descriptionMatches.map(match => this.toDescriptionRange(setting, match, lineIndex)));
}
} }
} }
...@@ -315,12 +423,14 @@ class SettingMatches { ...@@ -315,12 +423,14 @@ class SettingMatches {
} }
const descriptionRanges: IRange[] = []; const descriptionRanges: IRange[] = [];
for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) { if (this.searchDescription) {
const matches = or(matchesContiguousSubString)(searchString, setting.description[lineIndex] || '') || []; for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) {
descriptionRanges.push(...matches.map(match => this.toDescriptionRange(setting, match, lineIndex))); const matches = or(matchesContiguousSubString)(searchString, setting.description[lineIndex] || '') || [];
} descriptionRanges.push(...matches.map(match => this.toDescriptionRange(setting, match, lineIndex)));
if (descriptionRanges.length === 0) { }
descriptionRanges.push(...this.getRangesForWords(words, this.descriptionMatchingWords, [this.keyMatchingWords, this.valueMatchingWords])); if (descriptionRanges.length === 0) {
descriptionRanges.push(...this.getRangesForWords(words, this.descriptionMatchingWords, [this.keyMatchingWords, this.valueMatchingWords]));
}
} }
const keyMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.key); const keyMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.key);
...@@ -331,7 +441,7 @@ class SettingMatches { ...@@ -331,7 +441,7 @@ class SettingMatches {
const valueMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.value); const valueMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.value);
valueRanges = valueMatches ? valueMatches.map(match => this.toValueRange(setting, match)) : this.getRangesForWords(words, this.valueMatchingWords, [this.keyMatchingWords, this.descriptionMatchingWords]); valueRanges = valueMatches ? valueMatches.map(match => this.toValueRange(setting, match)) : this.getRangesForWords(words, this.valueMatchingWords, [this.keyMatchingWords, this.descriptionMatchingWords]);
} else { } else {
valueRanges = this.valuesMatcher(searchString, setting); valueRanges = this.valuesMatcher ? this.valuesMatcher(searchString, setting) : [];
} }
return [...descriptionRanges, ...keyRanges, ...valueRanges]; return [...descriptionRanges, ...keyRanges, ...valueRanges];
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册