diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index 1a1e0f46fd3d40b2402a946db65ccc209db6888d..dccdd36adcd4ce140d5ee6bda5c0e649528eda62 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -183,14 +183,43 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.createSearchWidget(this.searchWidgetsContainer); 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'] || []; + let patternExclusions = this.viewletSettings['query.folderExclusions'] || ''; + const patternExclusionsHistory: string[] = this.viewletSettings['query.folderExclusionsHistory'] || []; + let patternIncludes = this.viewletSettings['query.folderIncludes'] || ''; + let patternIncludesHistory: string[] = this.viewletSettings['query.folderIncludesHistory'] || []; const queryDetailsExpanded = this.viewletSettings['query.queryDetailsExpanded'] || ''; const useExcludesAndIgnoreFiles = typeof this.viewletSettings['query.useExcludesAndIgnoreFiles'] === 'boolean' ? this.viewletSettings['query.useExcludesAndIgnoreFiles'] : true; + // Transition history from 1.22 combined include+exclude, to split include/exclude histories + const patternIncludesHistoryWithoutExcludes: string[] = []; + const patternExcludesHistoryFromIncludes: string[] = []; + patternIncludesHistory.forEach(historyEntry => { + const includeExclude = this.queryBuilder.parseIncludeExcludePattern(historyEntry); + if (includeExclude.includePattern) { + patternIncludesHistoryWithoutExcludes.push(includeExclude.includePattern); + } + + if (includeExclude.excludePattern) { + patternExcludesHistoryFromIncludes.push(includeExclude.excludePattern); + } + }); + + patternIncludesHistory = patternIncludesHistoryWithoutExcludes; + patternExclusionsHistory.push(...patternExcludesHistoryFromIncludes); + + // Split combined include/exclude to split include/exclude boxes + const includeExclude = this.queryBuilder.parseIncludeExcludePattern(patternIncludes); + patternIncludes = includeExclude.includePattern || ''; + + if (includeExclude.excludePattern) { + if (patternExclusions) { + patternExclusions += ', ' + includeExclude.excludePattern; + } else { + patternExclusions = includeExclude.excludePattern; + } + } + this.queryDetails = this.searchWidgetsContainer.div({ 'class': ['query-details'] }, (builder) => { builder.div({ 'class': 'more', 'tabindex': 0, 'role': 'button', 'title': nls.localize('moreSearch', "Toggle Search Details") }) .on(dom.EventType.CLICK, (e) => { diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index 417c6bdac2487cf1dde2f7ff8b5017f2621f2d98..d0a998f91172d06c9aa1353673670a7c4b5c71c6 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -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) { diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index bb9e17f68f05bb0b9e7de90735a1f04b6ab381cf..4ae1b1a4fafcc8117b7b275c6a9318d7460cd24f 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -697,6 +697,51 @@ suite('QueryBuilder', () => { assert(query.sortByScore); }); }); + + suite('parseIncludeExcludePattern', () => { + test('nothing', () => { + assert.deepEqual( + queryBuilder.parseIncludeExcludePattern(''), + {}); + }); + + test('includes', () => { + assert.deepEqual( + queryBuilder.parseIncludeExcludePattern('src'), + { + includePattern: 'src' + }); + + assert.deepEqual( + queryBuilder.parseIncludeExcludePattern('src, test'), + { + includePattern: 'src, test' + }); + }); + + test('excludes', () => { + assert.deepEqual( + queryBuilder.parseIncludeExcludePattern('!src'), + { + excludePattern: 'src' + }); + + assert.deepEqual( + queryBuilder.parseIncludeExcludePattern('!src, !test'), + { + excludePattern: 'src, test' + }); + }); + + test('includes and excludes', () => { + assert.deepEqual( + queryBuilder.parseIncludeExcludePattern('!src, test, !foo, bar'), + { + includePattern: 'test, bar', + excludePattern: 'src, foo' + }); + }); + }); }); function assertEqualQueries(actual: ISearchQuery, expected: ISearchQuery): void {