提交 2196c4a7 编写于 作者: J Jackson Kearl

Initial work on "search in open editors"

上级 7d57a8f6
......@@ -18,7 +18,8 @@ import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedH
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import type { IThemable } from 'vs/base/common/styler';
import { Codicon } from 'vs/base/common/codicons';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ISearchConfiguration } from 'vs/workbench/services/search/common/search';
export interface IOptions {
placeholder?: string;
width?: number;
......@@ -50,7 +51,8 @@ export class PatternInputWidget extends Widget implements IThemable {
constructor(parent: HTMLElement, private contextViewProvider: IContextViewProvider, options: IOptions = Object.create(null),
@IThemeService protected themeService: IThemeService,
@IContextKeyService private readonly contextKeyService: IContextKeyService
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IConfigurationService protected readonly configurationService: IConfigurationService
) {
super();
this.width = options.width || 100;
......@@ -178,6 +180,62 @@ export class PatternInputWidget extends Widget implements IThemable {
}
}
export class IncludePatternInputWidget extends PatternInputWidget {
private _onChangeSearchInEditorsBoxEmitter = this._register(new Emitter<void>());
onChangeSearchInEditorsBox = this._onChangeSearchInEditorsBoxEmitter.event;
constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, options: IOptions = Object.create(null),
@IThemeService themeService: IThemeService,
@IContextKeyService contextKeyService: IContextKeyService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(parent, contextViewProvider, options, themeService, contextKeyService, configurationService);
}
private useSearchInEditorsBox!: Checkbox;
dispose(): void {
super.dispose();
this.useSearchInEditorsBox.dispose();
}
onlySearchInOpenEditors(): boolean {
return this.useSearchInEditorsBox.checked;
}
setOnlySearchInOpenEditors(value: boolean) {
this.useSearchInEditorsBox.checked = value;
}
protected getSubcontrolsWidth(): number {
if (this.configurationService.getValue<ISearchConfiguration>().search?.experimental?.searchInOpenEditors) {
return super.getSubcontrolsWidth() + this.useSearchInEditorsBox.width();
}
return super.getSubcontrolsWidth();
}
protected renderSubcontrols(controlsDiv: HTMLDivElement): void {
this.useSearchInEditorsBox = this._register(new Checkbox({
icon: Codicon.book,
title: nls.localize('onlySearchInOpenEditors', "Search Only in Open Editors"),
isChecked: false,
}));
if (!this.configurationService.getValue<ISearchConfiguration>().search?.experimental?.searchInOpenEditors) {
return;
}
this._register(this.useSearchInEditorsBox.onChange(viaKeyboard => {
this._onChangeSearchInEditorsBoxEmitter.fire();
if (!viaKeyboard) {
this.inputBox.focus();
}
}));
this._register(attachCheckboxStyler(this.useSearchInEditorsBox, this.themeService));
controlsDiv.appendChild(this.useSearchInEditorsBox.domNode);
super.renderSubcontrols(controlsDiv);
}
}
export class ExcludePatternInputWidget extends PatternInputWidget {
private _onChangeIgnoreBoxEmitter = this._register(new Emitter<void>());
......@@ -185,9 +243,10 @@ export class ExcludePatternInputWidget extends PatternInputWidget {
constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, options: IOptions = Object.create(null),
@IThemeService themeService: IThemeService,
@IContextKeyService contextKeyService: IContextKeyService
@IContextKeyService contextKeyService: IContextKeyService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(parent, contextViewProvider, options, themeService, contextKeyService);
super(parent, contextViewProvider, options, themeService, contextKeyService, configurationService);
}
private useExcludesAndIgnoreFilesBox!: Checkbox;
......
......@@ -873,6 +873,11 @@ configurationRegistry.registerConfiguration({
],
'description': nls.localize('search.sortOrder', "Controls sorting order of search results.")
},
'search.experimental.searchInOpenEditors': {
type: 'boolean',
default: false,
markdownDescription: nls.localize('search.experimental.searchInOpenEditors', "Experimental. When enabled, an option is provided to make workspace search only search files that have been opened. **Requires restart to take effect.**")
}
}
});
......
......@@ -42,7 +42,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/
import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
import { ResourceLabels } from 'vs/workbench/browser/labels';
import { IEditorPane } from 'vs/workbench/common/editor';
import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget';
import { ExcludePatternInputWidget, IncludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget';
import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, IFindInFilesArgs, appendKeyBindingLabel, ExpandAllAction, ToggleCollapseAndExpandAction } from 'vs/workbench/contrib/search/browser/searchActions';
import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchAccessibilityProvider, SearchDelegate, SearchDND } from 'vs/workbench/contrib/search/browser/searchResultsView';
import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget';
......@@ -131,7 +131,7 @@ export class SearchView extends ViewPane {
private queryDetails!: HTMLElement;
private toggleQueryDetailsButton!: HTMLElement;
private inputPatternExcludes!: ExcludePatternInputWidget;
private inputPatternIncludes!: PatternInputWidget;
private inputPatternIncludes!: IncludePatternInputWidget;
private resultsElement!: HTMLElement;
private currentSelectedFileMatch: FileMatch | undefined;
......@@ -315,7 +315,7 @@ export class SearchView extends ViewPane {
const filesToIncludeTitle = nls.localize('searchScope.includes', "files to include");
dom.append(folderIncludesList, $('h4', undefined, filesToIncludeTitle));
this.inputPatternIncludes = this._register(this.instantiationService.createInstance(PatternInputWidget, folderIncludesList, this.contextViewService, {
this.inputPatternIncludes = this._register(this.instantiationService.createInstance(IncludePatternInputWidget, folderIncludesList, this.contextViewService, {
ariaLabel: nls.localize('label.includes', 'Search Include Patterns'),
history: patternIncludesHistory,
}));
......@@ -324,6 +324,7 @@ export class SearchView extends ViewPane {
this.inputPatternIncludes.onSubmit(triggeredOnType => this.triggerQueryChange({ triggeredOnType, delay: this.searchConfig.searchOnTypeDebouncePeriod }));
this.inputPatternIncludes.onCancel(() => this.cancelSearch(false));
this.inputPatternIncludes.onChangeSearchInEditorsBox(() => this.triggerQueryChange());
this.trackInputBox(this.inputPatternIncludes.inputFocusTracker, this.inputPatternIncludesFocused);
// excludes list
......@@ -1298,6 +1299,7 @@ export class SearchView extends ViewPane {
const excludePatternText = this.inputPatternExcludes.getValue().trim();
const includePatternText = this.inputPatternIncludes.getValue().trim();
const useExcludesAndIgnoreFiles = this.inputPatternExcludes.useExcludesAndIgnoreFiles();
const onlySearchInOpenEditors = this.inputPatternIncludes.onlySearchInOpenEditors();
if (contentPattern.length === 0) {
this.clearSearchResults(false);
......@@ -1326,6 +1328,7 @@ export class SearchView extends ViewPane {
maxResults: SearchView.MAX_TEXT_RESULTS,
disregardIgnoreFiles: !useExcludesAndIgnoreFiles || undefined,
disregardExcludeSettings: !useExcludesAndIgnoreFiles || undefined,
onlyOpenEditors: onlySearchInOpenEditors,
excludePattern,
includePattern,
previewOptions: {
......
......@@ -16,6 +16,7 @@ import { isMultilineRegexSource } from 'vs/editor/common/model/textModelSearch';
import * as nls from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceContextService, IWorkspaceFolderData, toWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { getExcludes, ICommonQueryProps, IFileQuery, IFolderQuery, IPatternInfo, ISearchConfiguration, ITextQuery, ITextSearchPreviewOptions, pathIncludedInQuery, QueryType } from 'vs/workbench/services/search/common/search';
......@@ -59,6 +60,7 @@ export interface ICommonQueryBuilderOptions {
disregardExcludeSettings?: boolean;
disregardSearchExcludeSettings?: boolean;
ignoreSymlinks?: boolean;
onlyOpenEditors?: boolean;
}
export interface IFileQueryBuilderOptions extends ICommonQueryBuilderOptions {
......@@ -81,6 +83,7 @@ export class QueryBuilder {
constructor(
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService,
@IPathService private readonly pathService: IPathService
) {
}
......@@ -180,9 +183,26 @@ export class QueryBuilder {
excludePattern: excludeSearchPathsInfo.pattern,
includePattern: includeSearchPathsInfo.pattern,
onlyOpenEditors: options.onlyOpenEditors,
maxResults: options.maxResults
};
if (options.onlyOpenEditors) {
const openEditors = arrays.coalesce(arrays.flatten(this.editorGroupsService.groups.map(group => group.editors.map(editor => editor.resource))));
const openEditorsInQuery = openEditors.filter(editor => pathIncludedInQuery(queryProps, editor.fsPath));
const openEditorIncludes = openEditorsInQuery.map(editor => {
const workspace = this.workspaceContextService.getWorkspaceFolder(editor);
if (workspace) {
const relPath = path.relative(workspace?.uri.fsPath, editor.fsPath);
return includeFolderName ? `./${workspace.name}/${relPath}` : `./${relPath}`;
}
else {
return editor.fsPath.replace(/^\//, '');
}
});
// return this.commonQuery(folderResources, { ...options, expandPatterns: false, onlyOpenEditors: false, includePattern: openEditorIncludes.join(', ') });
}
// Filter extraFileResources against global include/exclude patterns - they are already expected to not belong to a workspace
const extraFileResources = options.extraFileResources && options.extraFileResources.filter(extraFile => pathIncludedInQuery(queryProps, extraFile.fsPath));
queryProps.extraFileResources = extraFileResources && extraFileResources.length ? extraFileResources : undefined;
......
......@@ -38,7 +38,7 @@ import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/com
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget';
import { ExcludePatternInputWidget, IncludePatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget';
import { SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget';
import { InputBoxFocusedKey } from 'vs/workbench/contrib/search/common/constants';
import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
......@@ -66,7 +66,7 @@ export class SearchEditor extends BaseTextEditor {
private searchResultEditor!: CodeEditorWidget;
private queryEditorContainer!: HTMLElement;
private dimension?: DOM.Dimension;
private inputPatternIncludes!: PatternInputWidget;
private inputPatternIncludes!: IncludePatternInputWidget;
private inputPatternExcludes!: ExcludePatternInputWidget;
private includesExcludesContainer!: HTMLElement;
private toggleQueryDetailsButton!: HTMLElement;
......@@ -166,10 +166,11 @@ export class SearchEditor extends BaseTextEditor {
const folderIncludesList = DOM.append(this.includesExcludesContainer, DOM.$('.file-types.includes'));
const filesToIncludeTitle = localize('searchScope.includes', "files to include");
DOM.append(folderIncludesList, DOM.$('h4', undefined, filesToIncludeTitle));
this.inputPatternIncludes = this._register(this.instantiationService.createInstance(PatternInputWidget, folderIncludesList, this.contextViewService, {
this.inputPatternIncludes = this._register(this.instantiationService.createInstance(IncludePatternInputWidget, folderIncludesList, this.contextViewService, {
ariaLabel: localize('label.includes', 'Search Include Patterns'),
}));
this.inputPatternIncludes.onSubmit(triggeredOnType => this.triggerSearch({ resetCursor: false, delay: triggeredOnType ? this.searchConfig.searchOnTypeDebouncePeriod : 0 }));
this._register(this.inputPatternIncludes.onChangeSearchInEditorsBox(() => this.triggerSearch()));
// // Excludes
const excludesList = DOM.append(this.includesExcludesContainer, DOM.$('.file-types.excludes'));
......@@ -179,7 +180,7 @@ export class SearchEditor extends BaseTextEditor {
ariaLabel: localize('label.excludes', 'Search Exclude Patterns'),
}));
this.inputPatternExcludes.onSubmit(triggeredOnType => this.triggerSearch({ resetCursor: false, delay: triggeredOnType ? this.searchConfig.searchOnTypeDebouncePeriod : 0 }));
this.inputPatternExcludes.onChangeIgnoreBox(() => this.triggerSearch());
this._register(this.inputPatternExcludes.onChangeIgnoreBox(() => this.triggerSearch()));
[this.queryEditorWidget.searchInput, this.inputPatternIncludes, this.inputPatternExcludes].map(input =>
this._register(attachInputBoxStyler(input, this.themeService, { inputBorder: searchEditorTextInputBorder })));
......@@ -447,6 +448,7 @@ export class SearchEditor extends BaseTextEditor {
regexp: this.queryEditorWidget.searchInput.getRegex(),
wholeWord: this.queryEditorWidget.searchInput.getWholeWords(),
useIgnores: this.inputPatternExcludes.useExcludesAndIgnoreFiles(),
onlyOpenEditors: this.inputPatternIncludes.onlySearchInOpenEditors(),
showIncludesExcludes: this.showingIncludesExcludes
};
}
......@@ -479,6 +481,7 @@ export class SearchEditor extends BaseTextEditor {
maxResults: 10000,
disregardIgnoreFiles: !config.useIgnores || undefined,
disregardExcludeSettings: !config.useIgnores || undefined,
onlyOpenEditors: config.onlyOpenEditors,
excludePattern: config.excludes,
includePattern: config.includes,
previewOptions: {
......
......@@ -39,6 +39,7 @@ export type SearchConfiguration = {
regexp: boolean,
useIgnores: boolean,
showIncludesExcludes: boolean,
onlyOpenEditors: boolean,
};
export const SEARCH_EDITOR_EXT = '.code-search';
......
......@@ -115,6 +115,7 @@ const contentPatternToSearchConfiguration = (pattern: ITextQuery, includes: stri
showIncludesExcludes: !!(includes || excludes || pattern?.userDisabledExcludesAndIgnoreFiles),
useIgnores: (pattern?.userDisabledExcludesAndIgnoreFiles === undefined ? true : !pattern.userDisabledExcludesAndIgnoreFiles),
contextLines,
onlyOpenEditors: !!pattern.onlyOpenEditors,
};
};
......@@ -131,6 +132,7 @@ export const serializeSearchConfiguration = (config: Partial<SearchConfiguration
config.caseSensitive && 'CaseSensitive',
config.wholeWord && 'WordMatch',
config.regexp && 'RegExp',
config.onlyOpenEditors && 'OpenEditors',
(config.useIgnores === false) && 'IgnoreExcludeSettings'
]).join(' ')}`,
config.includes ? `# Including: ${config.includes}` : undefined,
......@@ -153,6 +155,7 @@ export const defaultSearchConfig = (): SearchConfiguration => ({
wholeWord: false,
contextLines: 0,
showIncludesExcludes: false,
onlyOpenEditors: false,
});
export const extractSearchQueryFromLines = (lines: string[]): SearchConfiguration => {
......@@ -197,6 +200,7 @@ export const extractSearchQueryFromLines = (lines: string[]): SearchConfiguratio
query.caseSensitive = value.indexOf('CaseSensitive') !== -1;
query.useIgnores = value.indexOf('IgnoreExcludeSettings') === -1;
query.wholeWord = value.indexOf('WordMatch') !== -1;
query.onlyOpenEditors = value.indexOf('OpenEditors') !== -1;
}
}
}
......
......@@ -71,6 +71,8 @@ export interface ICommonQueryProps<U extends UriComponents> {
excludePattern?: glob.IExpression;
extraFileResources?: U[];
onlyOpenEditors?: boolean;
maxResults?: number;
usingSearchPaths?: boolean;
}
......@@ -352,6 +354,9 @@ export interface ISearchConfigurationProperties {
defaultNumberOfContextLines: number | null,
experimental: {}
};
experimental: {
searchInOpenEditors: boolean
}
sortOrder: SearchSortOrder;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册