Skip to content

Commit

Permalink
Fix #45702 - merge include/exclude inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
roblourens committed Mar 13, 2018
1 parent 5539eeb commit 7fdf1e3
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 123 deletions.
42 changes: 0 additions & 42 deletions src/vs/workbench/parts/search/browser/searchActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,48 +121,6 @@ export class ShowPreviousSearchIncludeAction extends Action {
}
}

export class ShowNextSearchExcludeAction extends Action {

public static readonly ID = 'search.history.showNextExcludePattern';
public static readonly LABEL = nls.localize('nextSearchExcludePattern', "Show Next Search Exclude Pattern");

constructor(id: string, label: string,
@IViewletService private viewletService: IViewletService,
@IPanelService private panelService: IPanelService,
@IContextKeyService private contextKeyService: IContextKeyService
) {
super(id, label);
this.enabled = this.contextKeyService.contextMatchesRules(Constants.SearchViewVisibleKey);
}

public run(): TPromise<any> {
const searchView = getSearchView(this.viewletService, this.panelService);
searchView.searchExcludePattern.showNextTerm();
return TPromise.as(null);
}
}

export class ShowPreviousSearchExcludeAction extends Action {

public static readonly ID = 'search.history.showPreviousExcludePattern';
public static readonly LABEL = nls.localize('previousSearchExcludePattern', "Show Previous Search Exclude Pattern");

constructor(id: string, label: string,
@IViewletService private viewletService: IViewletService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IPanelService private panelService: IPanelService
) {
super(id, label);
this.enabled = this.contextKeyService.contextMatchesRules(Constants.SearchViewVisibleKey);
}

public run(): TPromise<any> {
const searchView = getSearchView(this.viewletService, this.panelService);
searchView.searchExcludePattern.showPreviousTerm();
return TPromise.as(null);
}
}

export class ShowNextSearchTermAction extends Action {

public static readonly ID = 'search.history.showNext';
Expand Down
106 changes: 29 additions & 77 deletions src/vs/workbench/parts/search/browser/searchView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
private viewletVisible: IContextKey<boolean>;
private inputBoxFocused: IContextKey<boolean>;
private inputPatternIncludesFocused: IContextKey<boolean>;
private inputPatternExclusionsFocused: IContextKey<boolean>;
private firstMatchFocused: IContextKey<boolean>;
private fileMatchOrMatchFocused: IContextKey<boolean>;
private fileMatchFocused: IContextKey<boolean>;
Expand All @@ -94,8 +93,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
private searchWidget: SearchWidget;
private size: Dimension;
private queryDetails: HTMLElement;
private inputPatternExcludes: ExcludePatternInputWidget;
private inputPatternIncludes: PatternInputWidget;
private inputPatternIncludes: ExcludePatternInputWidget;
private results: Builder;

private currentSelectedFileMatch: FileMatch;
Expand Down Expand Up @@ -132,7 +130,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
this.viewletVisible = Constants.SearchViewVisibleKey.bindTo(contextKeyService);
this.inputBoxFocused = Constants.InputBoxFocusedKey.bindTo(this.contextKeyService);
this.inputPatternIncludesFocused = Constants.PatternIncludesFocusedKey.bindTo(this.contextKeyService);
this.inputPatternExclusionsFocused = Constants.PatternExcludesFocusedKey.bindTo(this.contextKeyService);
this.firstMatchFocused = Constants.FirstMatchFocusKey.bindTo(contextKeyService);
this.fileMatchOrMatchFocused = Constants.FileMatchOrMatchFocusKey.bindTo(contextKeyService);
this.fileMatchFocused = Constants.FileFocusKey.bindTo(contextKeyService);
Expand Down Expand Up @@ -178,7 +175,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {

const filePatterns = this.viewletSettings['query.filePatterns'] || '';
const patternExclusions = this.viewletSettings['query.folderExclusions'] || '';
const patternExclusionsHistory = this.viewletSettings['query.folderExclusionsHistory'] || [];
const patternIncludes = this.viewletSettings['query.folderIncludes'] || '';
const patternIncludesHistory = this.viewletSettings['query.folderIncludesHistory'] || [];
const queryDetailsExpanded = this.viewletSettings['query.queryDetailsExpanded'] || '';
Expand All @@ -201,15 +197,27 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {

//folder includes list
builder.div({ 'class': 'file-types' }, (builder) => {
let title = nls.localize('searchScope.includes', "files to include");
let title = nls.localize('searchIncludeExclude.label', "files to include/exclude");
builder.element('h4', { text: title });

this.inputPatternIncludes = new PatternInputWidget(builder.getContainer(), this.contextViewService, this.themeService, {
ariaLabel: nls.localize('label.includes', 'Search Include Patterns')
this.inputPatternIncludes = new ExcludePatternInputWidget(builder.getContainer(), this.contextViewService, this.themeService, {
ariaLabel: nls.localize('searchIncludeExclude.ariaLabel', 'Search Include/Exclude Patterns'),
placeholder: nls.localize('searchIncludeExclude.placeholder', "Examples: src, !*.ts, test/**/*.log")
});

this.inputPatternIncludes.setValue(patternIncludes);

let mergedIncludeExcludes = patternIncludes;
if (patternExclusions) {
if (mergedIncludeExcludes) {
mergedIncludeExcludes += ', ';
}

mergedIncludeExcludes += patternExclusions;
}

this.inputPatternIncludes.setValue(mergedIncludeExcludes);
this.inputPatternIncludes.setHistory(patternIncludesHistory);
this.inputPatternIncludes.setUseExcludesAndIgnoreFiles(useExcludesAndIgnoreFiles);

this.inputPatternIncludes
.on(FindInput.OPTION_CHANGE, (e) => {
Expand All @@ -220,30 +228,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
this.inputPatternIncludes.onCancel(() => this.viewModel.cancelSearch()); // Cancel search without focusing the search widget
this.trackInputBox(this.inputPatternIncludes.inputFocusTracker, this.inputPatternIncludesFocused);
});

//pattern exclusion list
builder.div({ 'class': 'file-types' }, (builder) => {
let title = nls.localize('searchScope.excludes', "files to exclude");
builder.element('h4', { text: title });

this.inputPatternExcludes = new ExcludePatternInputWidget(builder.getContainer(), this.contextViewService, this.themeService, {
ariaLabel: nls.localize('label.excludes', 'Search Exclude Patterns')
});

this.inputPatternExcludes.setValue(patternExclusions);
this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(useExcludesAndIgnoreFiles);
this.inputPatternExcludes.setHistory(patternExclusionsHistory);

this.inputPatternExcludes
.on(FindInput.OPTION_CHANGE, (e) => {
this.onQueryChanged(false);
});

this.inputPatternExcludes.onSubmit(() => this.onQueryChanged(true, true));
this.inputPatternExcludes.onSubmit(() => this.onQueryChanged(true, true));
this.inputPatternExcludes.onCancel(() => this.viewModel.cancelSearch()); // Cancel search without focusing the search widget
this.trackInputBox(this.inputPatternExcludes.inputFocusTracker, this.inputPatternExclusionsFocused);
});
}).getHTMLElement();

this.messages = builder.div({ 'class': 'messages' }).hide().clone();
Expand Down Expand Up @@ -276,10 +260,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
return this.inputPatternIncludes;
}

public get searchExcludePattern(): PatternInputWidget {
return this.inputPatternExcludes;
}

private updateActions(): void {
for (const action of this.actions) {
action.update();
Expand Down Expand Up @@ -336,8 +316,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
this.toUnbind.push(inputFocusTracker.onDidBlur(() => {
this.inputBoxFocused.set(this.searchWidget.searchInputHasFocus()
|| this.searchWidget.replaceInputHasFocus()
|| this.inputPatternIncludes.inputHasFocus()
|| this.inputPatternExcludes.inputHasFocus());
|| this.inputPatternIncludes.inputHasFocus());
if (contextKey) {
contextKey.set(false);
}
Expand Down Expand Up @@ -714,12 +693,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
}

if (this.inputPatternIncludes.inputHasFocus()) {
this.inputPatternExcludes.focus();
this.inputPatternExcludes.select();
return;
}

if (this.inputPatternExcludes.inputHasFocus()) {
this.selectTreeIfNotSelected();
return;
}
Expand Down Expand Up @@ -748,12 +721,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
return;
}

if (this.inputPatternExcludes.inputHasFocus()) {
this.inputPatternIncludes.focus();
this.inputPatternIncludes.select();
return;
}

if (this.tree.isDOMFocused()) {
this.moveFocusFromResults();
return;
Expand All @@ -762,7 +729,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {

private moveFocusFromResults(): void {
if (this.showsFileTypes()) {
this.toggleQueryDetails(true, true, false, true);
this.toggleQueryDetails(true, true, false);
} else {
this.searchWidget.focus(true, true);
}
Expand All @@ -775,7 +742,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {

this.searchWidget.setWidth(this.size.width - 28 /* container margin */);

this.inputPatternExcludes.setWidth(this.size.width - 28 /* container margin */);
this.inputPatternIncludes.setWidth(this.size.width - 28 /* container margin */);

const messagesSize = this.messages.isHidden() ? 0 : dom.getTotalHeight(this.messages.getHTMLElement());
Expand Down Expand Up @@ -896,7 +862,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
this.onQueryChanged(true, true);
}

public toggleQueryDetails(moveFocus?: boolean, show?: boolean, skipLayout?: boolean, reverse?: boolean): void {
public toggleQueryDetails(moveFocus?: boolean, show?: boolean, skipLayout?: boolean): void {
let cls = 'more';
show = typeof show === 'undefined' ? !dom.hasClass(this.queryDetails, cls) : Boolean(show);
this.viewletSettings['query.queryDetailsExpanded'] = show;
Expand All @@ -905,13 +871,8 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
if (show) {
dom.addClass(this.queryDetails, cls);
if (moveFocus) {
if (reverse) {
this.inputPatternExcludes.focus();
this.inputPatternExcludes.select();
} else {
this.inputPatternIncludes.focus();
this.inputPatternIncludes.select();
}
this.inputPatternIncludes.focus();
this.inputPatternIncludes.select();
}
} else {
dom.removeClass(this.queryDetails, cls);
Expand Down Expand Up @@ -979,9 +940,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
const isWholeWords = this.searchWidget.searchInput.getWholeWords();
const isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive();
const contentPattern = this.searchWidget.searchInput.getValue();
const excludePatternText = this.inputPatternExcludes.getValue().trim();
const includePatternText = this.inputPatternIncludes.getValue().trim();
const useExcludesAndIgnoreFiles = this.inputPatternExcludes.useExcludesAndIgnoreFiles();
const useExcludesAndIgnoreFiles = this.inputPatternIncludes.useExcludesAndIgnoreFiles();

if (!rerunQuery) {
return;
Expand Down Expand Up @@ -1014,8 +973,8 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
isSmartCase: this.configurationService.getValue<ISearchConfiguration>().search.smartCase
};

const excludePattern = this.inputPatternExcludes.getValue();
const includePattern = this.inputPatternIncludes.getValue();
const includeExcludePattern = this.inputPatternIncludes.getValue().trim();
const { includePattern, excludePattern } = this.queryBuilder.parseIncludeExcludePattern(includeExcludePattern);

const options: IQueryOptions = {
extraFileResources: getOutOfWorkspaceEditorResources(this.editorGroupService, this.contextService),
Expand All @@ -1041,7 +1000,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
}

this.validateQuery(query).then(() => {
this.onQueryTriggered(query, excludePatternText, includePatternText);
this.onQueryTriggered(query, excludePattern, includePattern);

if (!preserveFocus) {
this.searchWidget.focus(false); // focus back to input field
Expand Down Expand Up @@ -1072,7 +1031,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
}

private onQueryTriggered(query: ISearchQuery, excludePatternText: string, includePatternText: string): void {
this.inputPatternExcludes.onSearchSubmit();
this.inputPatternIncludes.onSearchSubmit();

this.viewModel.cancelSearch();
Expand Down Expand Up @@ -1133,8 +1091,8 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
}

if (!hasResults) {
let hasExcludes = !!excludePatternText;
let hasIncludes = !!includePatternText;
let hasExcludes = !!query.excludePattern;
let hasIncludes = !!query.includePattern;
let message: string;

if (!completed) {
Expand Down Expand Up @@ -1174,7 +1132,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
}).on(dom.EventType.CLICK, (e: MouseEvent) => {
dom.EventHelper.stop(e, false);

this.inputPatternExcludes.setValue('');
this.inputPatternIncludes.setValue('');

this.onQueryChanged(true);
Expand Down Expand Up @@ -1463,11 +1420,9 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
const isWholeWords = this.searchWidget.searchInput.getWholeWords();
const isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive();
const contentPattern = this.searchWidget.searchInput.getValue();
const patternExcludes = this.inputPatternExcludes.getValue().trim();
const patternIncludes = this.inputPatternIncludes.getValue().trim();
const useExcludesAndIgnoreFiles = this.inputPatternExcludes.useExcludesAndIgnoreFiles();
const useExcludesAndIgnoreFiles = this.inputPatternIncludes.useExcludesAndIgnoreFiles();
const searchHistory = this.searchWidget.getHistory();
const patternExcludesHistory = this.inputPatternExcludes.getHistory();
const patternIncludesHistory = this.inputPatternIncludes.getHistory();

// store memento
Expand All @@ -1476,9 +1431,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
this.viewletSettings['query.regex'] = isRegex;
this.viewletSettings['query.wholeWords'] = isWholeWords;
this.viewletSettings['query.caseSensitive'] = isCaseSensitive;
this.viewletSettings['query.folderExclusions'] = patternExcludes;
this.viewletSettings['query.folderIncludes'] = patternIncludes;
this.viewletSettings['query.folderExclusionsHistory'] = patternExcludesHistory;
this.viewletSettings['query.folderIncludesHistory'] = patternIncludesHistory;
this.viewletSettings['query.useExcludesAndIgnoreFiles'] = useExcludesAndIgnoreFiles;

Expand All @@ -1494,7 +1447,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {

this.searchWidget.dispose();
this.inputPatternIncludes.dispose();
this.inputPatternExcludes.dispose();

this.viewModel.dispose();

Expand Down
22 changes: 22 additions & 0 deletions src/vs/workbench/parts/search/common/queryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,28 @@ export class QueryBuilder {
return Object.keys(excludeExpression).length ? excludeExpression : undefined;
}

/**
* A helper that splits positive and negative patterns from a string that combines both.
*/
public parseIncludeExcludePattern(pattern: string): { includePattern?: string, excludePattern?: string } {
const grouped = collections.groupBy(
splitGlobPattern(pattern),
s => strings.startsWith(s, '!') ? 'excludePattern' : 'includePattern');

const result = {};
if (grouped.includePattern) {
result['includePattern'] = grouped.includePattern.join(', ');
}

if (grouped.excludePattern) {
result['excludePattern'] = grouped.excludePattern
.map(s => strings.ltrim(s, '!'))
.join(', ');
}

return result;
}

private mergeExcludesFromFolderQueries(folderQueries: IFolderQuery[]): glob.IExpression | undefined {
const mergedExcludes = folderQueries.reduce((merged: glob.IExpression, fq: IFolderQuery) => {
if (fq.excludePattern) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import { getMultiSelectedResources } from 'vs/workbench/parts/files/browser/file
import { Schemas } from 'vs/base/common/network';
import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { openSearchView, getSearchView, ReplaceAllInFolderAction, ReplaceAllAction, CloseReplaceAction, FocusNextInputAction, FocusPreviousInputAction, FocusNextSearchResultAction, FocusPreviousSearchResultAction, ReplaceInFilesAction, FindInFilesAction, FocusActiveEditorCommand, toggleCaseSensitiveCommand, ShowNextSearchTermAction, ShowPreviousSearchTermAction, toggleRegexCommand, ShowNextSearchExcludeAction, ShowPreviousSearchIncludeAction, ShowNextSearchIncludeAction, ShowPreviousSearchExcludeAction, CollapseDeepestExpandedLevelAction, toggleWholeWordCommand, RemoveAction, ReplaceAction } from 'vs/workbench/parts/search/browser/searchActions';
import { openSearchView, getSearchView, ReplaceAllInFolderAction, ReplaceAllAction, CloseReplaceAction, FocusNextInputAction, FocusPreviousInputAction, FocusNextSearchResultAction, FocusPreviousSearchResultAction, ReplaceInFilesAction, FindInFilesAction, FocusActiveEditorCommand, toggleCaseSensitiveCommand, ShowNextSearchTermAction, ShowPreviousSearchTermAction, toggleRegexCommand, ShowPreviousSearchIncludeAction, ShowNextSearchIncludeAction, CollapseDeepestExpandedLevelAction, toggleWholeWordCommand, RemoveAction, ReplaceAction } from 'vs/workbench/parts/search/browser/searchActions';
import { VIEW_ID } from 'vs/platform/search/common/search';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
Expand Down Expand Up @@ -353,9 +353,6 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousSearchTerm
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextSearchIncludeAction, ShowNextSearchIncludeAction.ID, ShowNextSearchIncludeAction.LABEL, ShowNextFindTermKeybinding, ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.PatternIncludesFocusedKey)), 'Search: Show Next Search Include Pattern', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousSearchIncludeAction, ShowPreviousSearchIncludeAction.ID, ShowPreviousSearchIncludeAction.LABEL, ShowPreviousFindTermKeybinding, ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.PatternIncludesFocusedKey)), 'Search: Show Previous Search Include Pattern', category);

registry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextSearchExcludeAction, ShowNextSearchExcludeAction.ID, ShowNextSearchExcludeAction.LABEL, ShowNextFindTermKeybinding, ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.PatternExcludesFocusedKey)), 'Search: Show Next Search Exclude Pattern', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousSearchExcludeAction, ShowPreviousSearchExcludeAction.ID, ShowPreviousSearchExcludeAction.LABEL, ShowPreviousFindTermKeybinding, ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.PatternExcludesFocusedKey)), 'Search: Show Previous Search Exclude Pattern', category);

registry.registerWorkbenchAction(new SyncActionDescriptor(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL), 'Search: Collapse All', category);

registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllSymbolsAction, ShowAllSymbolsAction.ID, ShowAllSymbolsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), 'Go to Symbol in Workspace...');
Expand Down
Loading

0 comments on commit 7fdf1e3

Please sign in to comment.