diff --git a/src/vs/workbench/contrib/preferences/electron-browser/media/check-inverse.svg b/src/vs/workbench/contrib/preferences/browser/media/check-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/preferences/electron-browser/media/check-inverse.svg rename to src/vs/workbench/contrib/preferences/browser/media/check-inverse.svg diff --git a/src/vs/workbench/contrib/preferences/electron-browser/media/check.svg b/src/vs/workbench/contrib/preferences/browser/media/check.svg similarity index 100% rename from src/vs/workbench/contrib/preferences/electron-browser/media/check.svg rename to src/vs/workbench/contrib/preferences/browser/media/check.svg diff --git a/src/vs/workbench/contrib/preferences/electron-browser/media/configure-inverse.svg b/src/vs/workbench/contrib/preferences/browser/media/configure-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/preferences/electron-browser/media/configure-inverse.svg rename to src/vs/workbench/contrib/preferences/browser/media/configure-inverse.svg diff --git a/src/vs/workbench/contrib/preferences/electron-browser/media/configure.svg b/src/vs/workbench/contrib/preferences/browser/media/configure.svg similarity index 100% rename from src/vs/workbench/contrib/preferences/electron-browser/media/configure.svg rename to src/vs/workbench/contrib/preferences/browser/media/configure.svg diff --git a/src/vs/workbench/contrib/preferences/electron-browser/media/edit-json-inverse.svg b/src/vs/workbench/contrib/preferences/browser/media/edit-json-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/preferences/electron-browser/media/edit-json-inverse.svg rename to src/vs/workbench/contrib/preferences/browser/media/edit-json-inverse.svg diff --git a/src/vs/workbench/contrib/preferences/electron-browser/media/edit-json.svg b/src/vs/workbench/contrib/preferences/browser/media/edit-json.svg similarity index 100% rename from src/vs/workbench/contrib/preferences/electron-browser/media/edit-json.svg rename to src/vs/workbench/contrib/preferences/browser/media/edit-json.svg diff --git a/src/vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg b/src/vs/workbench/contrib/preferences/browser/media/preferences-editor-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg rename to src/vs/workbench/contrib/preferences/browser/media/preferences-editor-inverse.svg diff --git a/src/vs/workbench/contrib/preferences/electron-browser/media/preferences-editor.svg b/src/vs/workbench/contrib/preferences/browser/media/preferences-editor.svg similarity index 100% rename from src/vs/workbench/contrib/preferences/electron-browser/media/preferences-editor.svg rename to src/vs/workbench/contrib/preferences/browser/media/preferences-editor.svg diff --git a/src/vs/workbench/contrib/preferences/electron-browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css similarity index 100% rename from src/vs/workbench/contrib/preferences/electron-browser/media/settingsEditor2.css rename to src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css diff --git a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts similarity index 93% rename from src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts rename to src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 23d239b5adc31b3cfebe8bd2d8dda0f079fc40f5..98bc3f9a093478d07b5df9a13ca30143a34fb4b8 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -16,7 +16,6 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { WorkbenchStateContext, RemoteAuthorityContext } from 'vs/workbench/browser/contextkeys'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -30,10 +29,9 @@ import { ResourceContextKey } from 'vs/workbench/common/resources'; import { KeybindingsEditor } from 'vs/workbench/contrib/preferences/browser/keybindingsEditor'; import { ConfigureLanguageBasedSettingsAction, OpenDefaultKeybindingsFileAction, OpenFolderSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenGlobalSettingsAction, OpenRawDefaultSettingsAction, OpenSettings2Action, OpenSettingsJsonAction, OpenWorkspaceSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND, OPEN_FOLDER_SETTINGS_LABEL, OpenRemoteSettingsAction } from 'vs/workbench/contrib/preferences/browser/preferencesActions'; import { PreferencesEditor } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; -import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IKeybindingsEditor, IPreferencesSearchService, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, SETTINGS_COMMAND_OPEN_SETTINGS, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IKeybindingsEditor, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, SETTINGS_COMMAND_OPEN_SETTINGS, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN } from 'vs/workbench/contrib/preferences/common/preferences'; import { PreferencesContribution } from 'vs/workbench/contrib/preferences/common/preferencesContribution'; -import { PreferencesSearchService } from 'vs/workbench/contrib/preferences/electron-browser/preferencesSearch'; -import { SettingsEditor2 } from 'vs/workbench/contrib/preferences/electron-browser/settingsEditor2'; +import { SettingsEditor2 } from 'vs/workbench/contrib/preferences/browser/settingsEditor2'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; @@ -42,8 +40,6 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); - Registry.as(EditorExtensions.Editors).registerEditor( new EditorDescriptor( PreferencesEditor, @@ -385,8 +381,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: OpenGlobalKeybindingsAction.ID, title: OpenGlobalKeybindingsAction.LABEL, iconLocation: { - light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor.svg`)), - dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg`)) + light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor.svg`)), + dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-inverse.svg`)) } }, when: ResourceContextKey.Resource.isEqualTo(URI.file(environmentService.appKeybindingsPath).toString()), @@ -401,8 +397,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: commandId, title: OpenSettings2Action.LABEL, iconLocation: { - light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor.svg`)), - dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg`)) + light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor.svg`)), + dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-inverse.svg`)) } }, when: ResourceContextKey.Resource.isEqualTo(environmentService.settingsResource.toString()), @@ -442,8 +438,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: commandId, title: OpenSettings2Action.LABEL, iconLocation: { - light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor.svg`)), - dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg`)) + light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor.svg`)), + dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-inverse.svg`)) } }, when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource!.toString()), WorkbenchStateContext.isEqualTo('workspace')), @@ -470,8 +466,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: commandId, title: OpenSettings2Action.LABEL, iconLocation: { - light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor.svg`)), - dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg`)) + light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor.svg`)), + dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/preferences-editor-inverse.svg`)) } }, when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.getFolderSettingsResource(folder.uri)!.toString())), @@ -537,8 +533,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { id: OpenGlobalKeybindingsFileAction.ID, title: OpenGlobalKeybindingsFileAction.LABEL, iconLocation: { - light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/edit-json.svg`)), - dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/edit-json-inverse.svg`)) + light: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/edit-json.svg`)), + dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/browser/media/edit-json-inverse.svg`)) } }, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR), @@ -775,8 +771,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { id: SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, title: nls.localize('openSettingsJson', "Open Settings (JSON)"), iconLocation: { - dark: URI.parse(require.toUrl('vs/workbench/contrib/preferences/electron-browser/media/edit-json-inverse.svg')), - light: URI.parse(require.toUrl('vs/workbench/contrib/preferences/electron-browser/media/edit-json.svg')) + dark: URI.parse(require.toUrl('vs/workbench/contrib/preferences/browser/media/edit-json-inverse.svg')), + light: URI.parse(require.toUrl('vs/workbench/contrib/preferences/browser/media/edit-json.svg')) } }, group: 'navigation', diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts new file mode 100644 index 0000000000000000000000000000000000000000..98dc806587970294041447dc70f4383d7ac388e0 --- /dev/null +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -0,0 +1,219 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ISettingsEditorModel, ISetting, ISettingsGroup, ISearchResult, IGroupFilter } from 'vs/workbench/services/preferences/common/preferences'; +import { IRange } from 'vs/editor/common/core/range'; +import { distinct } from 'vs/base/common/arrays'; +import * as strings from 'vs/base/common/strings'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IMatch, or, matchesContiguousSubString, matchesPrefix, matchesCamelCase, matchesWords } from 'vs/base/common/filters'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IPreferencesSearchService, ISearchProvider } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CancellationToken } from 'vs/base/common/cancellation'; + +export interface IEndpointDetails { + urlBase?: string; + key?: string; +} + +export class PreferencesSearchService extends Disposable implements IPreferencesSearchService { + _serviceBrand: any; + + constructor( + @IInstantiationService protected readonly instantiationService: IInstantiationService, + ) { + super(); + } + + getRemoteSearchProvider(filter: string, newExtensionsOnly = false): ISearchProvider | undefined { + return undefined; + } + + getLocalSearchProvider(filter: string): LocalSearchProvider { + return this.instantiationService.createInstance(LocalSearchProvider, filter); + } +} + +export class LocalSearchProvider implements ISearchProvider { + static readonly EXACT_MATCH_SCORE = 10000; + static readonly START_SCORE = 1000; + + constructor(private _filter: string) { + // Remove " and : which are likely to be copypasted as part of a setting name. + // Leave other special characters which the user might want to search for. + this._filter = this._filter + .replace(/[":]/g, ' ') + .replace(/ /g, ' ') + .trim(); + } + + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise { + if (!this._filter) { + return Promise.resolve(null); + } + + let orderedScore = LocalSearchProvider.START_SCORE; // Sort is not stable + const settingMatcher = (setting: ISetting) => { + const matches = new SettingMatches(this._filter, setting, true, true, (filter, setting) => preferencesModel.findValueMatches(filter, setting)).matches; + const score = this._filter === setting.key ? + LocalSearchProvider.EXACT_MATCH_SCORE : + orderedScore--; + + return matches && matches.length ? + { + matches, + score + } : + null; + }; + + const filterMatches = preferencesModel.filterSettings(this._filter, this.getGroupFilter(this._filter), settingMatcher); + if (filterMatches[0] && filterMatches[0].score === LocalSearchProvider.EXACT_MATCH_SCORE) { + return Promise.resolve({ + filterMatches: filterMatches.slice(0, 1), + exactMatch: true + }); + } else { + return Promise.resolve({ + filterMatches + }); + } + } + + private getGroupFilter(filter: string): IGroupFilter { + const regex = strings.createRegExp(filter, false, { global: true }); + return (group: ISettingsGroup) => { + return regex.test(group.title); + }; + } +} + +export class SettingMatches { + + private readonly descriptionMatchingWords: Map = new Map(); + private readonly keyMatchingWords: Map = new Map(); + private readonly valueMatchingWords: Map = new Map(); + + readonly matches: IRange[]; + + constructor(searchString: string, setting: ISetting, private requireFullQueryMatch: boolean, private searchDescription: boolean, private valuesMatcher: (filter: string, setting: ISetting) => IRange[]) { + this.matches = distinct(this._findMatchesInSetting(searchString, setting), (match) => `${match.startLineNumber}_${match.startColumn}_${match.endLineNumber}_${match.endColumn}_`); + } + + private _findMatchesInSetting(searchString: string, setting: ISetting): IRange[] { + const result = this._doFindMatchesInSetting(searchString, setting); + if (setting.overrides && setting.overrides.length) { + for (const subSetting of setting.overrides) { + const subSettingMatches = new SettingMatches(searchString, subSetting, this.requireFullQueryMatch, this.searchDescription, this.valuesMatcher); + const words = searchString.split(' '); + 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 subSettingKeyRanges: IRange[] = this.getRangesForWords(words, subSettingMatches.keyMatchingWords, [this.descriptionMatchingWords, this.keyMatchingWords, subSettingMatches.valueMatchingWords]); + const subSettinValueRanges: IRange[] = this.getRangesForWords(words, subSettingMatches.valueMatchingWords, [this.descriptionMatchingWords, this.keyMatchingWords, subSettingMatches.keyMatchingWords]); + result.push(...descriptionRanges, ...keyRanges, ...subSettingKeyRanges, ...subSettinValueRanges); + result.push(...subSettingMatches.matches); + } + } + return result; + } + + private _doFindMatchesInSetting(searchString: string, setting: ISetting): IRange[] { + const registry: { [qualifiedKey: string]: IJSONSchema } = Registry.as(Extensions.Configuration).getConfigurationProperties(); + const schema: IJSONSchema = registry[setting.key]; + + const words = searchString.split(' '); + const settingKeyAsWords: string = setting.key.split('.').join(' '); + + for (const word of words) { + if (this.searchDescription) { + for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) { + const descriptionMatches = matchesWords(word, setting.description[lineIndex], true); + if (descriptionMatches) { + this.descriptionMatchingWords.set(word, descriptionMatches.map(match => this.toDescriptionRange(setting, match, lineIndex))); + } + } + } + + const keyMatches = or(matchesWords, matchesCamelCase)(word, settingKeyAsWords); + if (keyMatches) { + this.keyMatchingWords.set(word, keyMatches.map(match => this.toKeyRange(setting, match))); + } + + const valueMatches = typeof setting.value === 'string' ? matchesContiguousSubString(word, setting.value) : null; + if (valueMatches) { + this.valueMatchingWords.set(word, valueMatches.map(match => this.toValueRange(setting, match))); + } else if (schema && schema.enum && schema.enum.some(enumValue => typeof enumValue === 'string' && !!matchesContiguousSubString(word, enumValue))) { + this.valueMatchingWords.set(word, []); + } + } + + const descriptionRanges: IRange[] = []; + if (this.searchDescription) { + for (let lineIndex = 0; lineIndex < setting.description.length; 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])); + } + } + + const keyMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.key); + const keyRanges: IRange[] = keyMatches ? keyMatches.map(match => this.toKeyRange(setting, match)) : this.getRangesForWords(words, this.keyMatchingWords, [this.descriptionMatchingWords, this.valueMatchingWords]); + + let valueRanges: IRange[] = []; + if (setting.value && typeof setting.value === 'string') { + 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]); + } else { + valueRanges = this.valuesMatcher ? this.valuesMatcher(searchString, setting) : []; + } + + return [...descriptionRanges, ...keyRanges, ...valueRanges]; + } + + private getRangesForWords(words: string[], from: Map, others: Map[]): IRange[] { + const result: IRange[] = []; + for (const word of words) { + const ranges = from.get(word); + if (ranges) { + result.push(...ranges); + } else if (this.requireFullQueryMatch && others.every(o => !o.has(word))) { + return []; + } + } + return result; + } + + private toKeyRange(setting: ISetting, match: IMatch): IRange { + return { + startLineNumber: setting.keyRange.startLineNumber, + startColumn: setting.keyRange.startColumn + match.start, + endLineNumber: setting.keyRange.startLineNumber, + endColumn: setting.keyRange.startColumn + match.end + }; + } + + private toDescriptionRange(setting: ISetting, match: IMatch, lineIndex: number): IRange { + return { + startLineNumber: setting.descriptionRanges[lineIndex].startLineNumber, + startColumn: setting.descriptionRanges[lineIndex].startColumn + match.start, + endLineNumber: setting.descriptionRanges[lineIndex].endLineNumber, + endColumn: setting.descriptionRanges[lineIndex].startColumn + match.end + }; + } + + private toValueRange(setting: ISetting, match: IMatch): IRange { + return { + startLineNumber: setting.valueRange.startLineNumber, + startColumn: setting.valueRange.startColumn + match.start + 1, + endLineNumber: setting.valueRange.startLineNumber, + endColumn: setting.valueRange.startColumn + match.end + 1 + }; + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts similarity index 100% rename from src/vs/workbench/contrib/preferences/electron-browser/settingsEditor2.ts rename to src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts diff --git a/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts index 5b87f1adf51fabc0fb72ee6e8978f7322c7dfef2..a31aa422474a959565473701618721b6fa951d07 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/preferencesSearch.ts @@ -4,18 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { ISettingsEditorModel, ISetting, ISettingsGroup, IFilterMetadata, ISearchResult, IGroupFilter, ISettingMatcher, IScoredResults, ISettingMatch, IRemoteSetting, IExtensionSetting } from 'vs/workbench/services/preferences/common/preferences'; -import { IRange } from 'vs/editor/common/core/range'; -import { distinct, top } from 'vs/base/common/arrays'; +import { top } from 'vs/base/common/arrays'; import * as strings from 'vs/base/common/strings'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { IMatch, or, matchesContiguousSubString, matchesPrefix, matchesCamelCase, matchesWords } from 'vs/base/common/filters'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IRequestService } from 'vs/platform/request/node/request'; import { asJson } from 'vs/base/node/request'; -import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; import { IPreferencesSearchService, ISearchProvider, IWorkbenchSettingsConfiguration } from 'vs/workbench/contrib/preferences/common/preferences'; @@ -24,25 +18,26 @@ import { canceled } from 'vs/base/common/errors'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { nullRange } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { PreferencesSearchService as LocalPreferencesSearchService, SettingMatches } from 'vs/workbench/contrib/preferences/browser/preferencesSearch'; export interface IEndpointDetails { urlBase?: string; key?: string; } -export class PreferencesSearchService extends Disposable implements IPreferencesSearchService { +export class PreferencesSearchService extends LocalPreferencesSearchService implements IPreferencesSearchService { _serviceBrand: any; private _installedExtensions: Promise; constructor( + @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IInstantiationService private readonly instantiationService: IInstantiationService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService ) { - super(); + super(instantiationService); // 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(ExtensionType.User).then(exts => { @@ -86,10 +81,6 @@ export class PreferencesSearchService extends Disposable implements IPreferences return this.remoteSearchAllowed ? this.instantiationService.createInstance(RemoteSearchProvider, opts, this._installedExtensions) : undefined; } - - getLocalSearchProvider(filter: string): LocalSearchProvider { - return this.instantiationService.createInstance(LocalSearchProvider, filter); - } } export class LocalSearchProvider implements ISearchProvider { @@ -437,129 +428,4 @@ function remoteSettingToISetting(remoteSetting: IRemoteSetting): IExtensionSetti extensionName: remoteSetting.extensionName, extensionPublisher: remoteSetting.extensionPublisher }; -} - -class SettingMatches { - - private readonly descriptionMatchingWords: Map = new Map(); - private readonly keyMatchingWords: Map = new Map(); - private readonly valueMatchingWords: Map = new Map(); - - readonly matches: IRange[]; - - constructor(searchString: string, setting: ISetting, private requireFullQueryMatch: boolean, private searchDescription: boolean, private valuesMatcher: (filter: string, setting: ISetting) => IRange[]) { - this.matches = distinct(this._findMatchesInSetting(searchString, setting), (match) => `${match.startLineNumber}_${match.startColumn}_${match.endLineNumber}_${match.endColumn}_`); - } - - private _findMatchesInSetting(searchString: string, setting: ISetting): IRange[] { - const result = this._doFindMatchesInSetting(searchString, setting); - if (setting.overrides && setting.overrides.length) { - for (const subSetting of setting.overrides) { - const subSettingMatches = new SettingMatches(searchString, subSetting, this.requireFullQueryMatch, this.searchDescription, this.valuesMatcher); - const words = searchString.split(' '); - 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 subSettingKeyRanges: IRange[] = this.getRangesForWords(words, subSettingMatches.keyMatchingWords, [this.descriptionMatchingWords, this.keyMatchingWords, subSettingMatches.valueMatchingWords]); - const subSettinValueRanges: IRange[] = this.getRangesForWords(words, subSettingMatches.valueMatchingWords, [this.descriptionMatchingWords, this.keyMatchingWords, subSettingMatches.keyMatchingWords]); - result.push(...descriptionRanges, ...keyRanges, ...subSettingKeyRanges, ...subSettinValueRanges); - result.push(...subSettingMatches.matches); - } - } - return result; - } - - private _doFindMatchesInSetting(searchString: string, setting: ISetting): IRange[] { - const registry: { [qualifiedKey: string]: IJSONSchema } = Registry.as(Extensions.Configuration).getConfigurationProperties(); - const schema: IJSONSchema = registry[setting.key]; - - const words = searchString.split(' '); - const settingKeyAsWords: string = setting.key.split('.').join(' '); - - for (const word of words) { - if (this.searchDescription) { - for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) { - const descriptionMatches = matchesWords(word, setting.description[lineIndex], true); - if (descriptionMatches) { - this.descriptionMatchingWords.set(word, descriptionMatches.map(match => this.toDescriptionRange(setting, match, lineIndex))); - } - } - } - - const keyMatches = or(matchesWords, matchesCamelCase)(word, settingKeyAsWords); - if (keyMatches) { - this.keyMatchingWords.set(word, keyMatches.map(match => this.toKeyRange(setting, match))); - } - - const valueMatches = typeof setting.value === 'string' ? matchesContiguousSubString(word, setting.value) : null; - if (valueMatches) { - this.valueMatchingWords.set(word, valueMatches.map(match => this.toValueRange(setting, match))); - } else if (schema && schema.enum && schema.enum.some(enumValue => typeof enumValue === 'string' && !!matchesContiguousSubString(word, enumValue))) { - this.valueMatchingWords.set(word, []); - } - } - - const descriptionRanges: IRange[] = []; - if (this.searchDescription) { - for (let lineIndex = 0; lineIndex < setting.description.length; 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])); - } - } - - const keyMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.key); - const keyRanges: IRange[] = keyMatches ? keyMatches.map(match => this.toKeyRange(setting, match)) : this.getRangesForWords(words, this.keyMatchingWords, [this.descriptionMatchingWords, this.valueMatchingWords]); - - let valueRanges: IRange[] = []; - if (setting.value && typeof setting.value === 'string') { - 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]); - } else { - valueRanges = this.valuesMatcher ? this.valuesMatcher(searchString, setting) : []; - } - - return [...descriptionRanges, ...keyRanges, ...valueRanges]; - } - - private getRangesForWords(words: string[], from: Map, others: Map[]): IRange[] { - const result: IRange[] = []; - for (const word of words) { - const ranges = from.get(word); - if (ranges) { - result.push(...ranges); - } else if (this.requireFullQueryMatch && others.every(o => !o.has(word))) { - return []; - } - } - return result; - } - - private toKeyRange(setting: ISetting, match: IMatch): IRange { - return { - startLineNumber: setting.keyRange.startLineNumber, - startColumn: setting.keyRange.startColumn + match.start, - endLineNumber: setting.keyRange.startLineNumber, - endColumn: setting.keyRange.startColumn + match.end - }; - } - - private toDescriptionRange(setting: ISetting, match: IMatch, lineIndex: number): IRange { - return { - startLineNumber: setting.descriptionRanges[lineIndex].startLineNumber, - startColumn: setting.descriptionRanges[lineIndex].startColumn + match.start, - endLineNumber: setting.descriptionRanges[lineIndex].endLineNumber, - endColumn: setting.descriptionRanges[lineIndex].startColumn + match.end - }; - } - - private toValueRange(setting: ISetting, match: IMatch): IRange { - return { - startLineNumber: setting.valueRange.startLineNumber, - startColumn: setting.valueRange.startColumn + match.start + 1, - endLineNumber: setting.valueRange.startLineNumber, - endColumn: setting.valueRange.startColumn + match.end + 1 - }; - } -} +} \ No newline at end of file diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 2cfc6568cdd5f42de6abf2ff0449916e4bd0cafc..fcd9dbc62ab8073d15f7f207d3708cd40171b02c 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -191,8 +191,11 @@ import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; // Preferences -import 'vs/workbench/contrib/preferences/electron-browser/preferences.contribution'; +import 'vs/workbench/contrib/preferences/browser/preferences.contribution'; import 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'; +import { IPreferencesSearchService } from 'vs/workbench/contrib/preferences/common/preferences'; +import { PreferencesSearchService } from 'vs/workbench/contrib/preferences/electron-browser/preferencesSearch'; +registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); // Logs import 'vs/workbench/contrib/logs/common/logs.contribution'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 84153bcd2cae51688721b9740123ca6fdb38a29d..460b2419aa321cdb98dcfa0d030b7803a295a2d9 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -194,8 +194,11 @@ import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; // import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; // Preferences -// import 'vs/workbench/contrib/preferences/electron-browser/preferences.contribution'; +import 'vs/workbench/contrib/preferences/browser/preferences.contribution'; import 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'; +import { IPreferencesSearchService } from 'vs/workbench/contrib/preferences/common/preferences'; +import { PreferencesSearchService } from 'vs/workbench/contrib/preferences/browser/preferencesSearch'; +registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); // Logs import 'vs/workbench/contrib/logs/common/logs.contribution';