Skip to content

Commit

Permalink
Fix #41119 - implement "search.smartCase" setting
Browse files Browse the repository at this point in the history
  • Loading branch information
roblourens committed Jan 4, 2018
1 parent 3f77b46 commit 6c6364d
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/vs/platform/search/common/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export interface IPatternInfo {
wordSeparators?: string;
isMultiline?: boolean;
isCaseSensitive?: boolean;
isSmartCase?: boolean;
}

export interface IFileMatch {
Expand Down Expand Up @@ -179,6 +180,7 @@ export interface ISearchConfiguration extends IFilesConfiguration {
*/
useIgnoreFiles: boolean;
followSymlinks: boolean;
smartCase: boolean;
};
editor: {
wordSeparators: string;
Expand Down
7 changes: 4 additions & 3 deletions src/vs/workbench/parts/search/browser/searchViewlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { Viewlet } from 'vs/workbench/browser/viewlet';
import { Match, FileMatch, SearchModel, FileMatchOrMatch, IChangeEvent, ISearchWorkbenchService, FolderMatch } from 'vs/workbench/parts/search/common/searchModel';
import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder';
import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { ISearchProgressItem, ISearchComplete, ISearchQuery, IQueryOptions, ISearchConfiguration } from 'vs/platform/search/common/search';
import { ISearchProgressItem, ISearchComplete, ISearchQuery, IQueryOptions, ISearchConfiguration, IPatternInfo } from 'vs/platform/search/common/search';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
Expand Down Expand Up @@ -980,12 +980,13 @@ export class SearchViewlet extends Viewlet {
}
}

const content = {
const content: IPatternInfo = {
pattern: contentPattern,
isRegExp: isRegex,
isCaseSensitive: isCaseSensitive,
isWordMatch: isWholeWords,
wordSeparators: this.configurationService.getValue<ISearchConfiguration>().editor.wordSeparators
wordSeparators: this.configurationService.getValue<ISearchConfiguration>().editor.wordSeparators,
isSmartCase: this.configurationService.getValue<ISearchConfiguration>().search.smartCase
};

const excludePattern = this.inputPatternExcludes.getValue();
Expand Down
18 changes: 18 additions & 0 deletions src/vs/workbench/parts/search/common/queryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export class QueryBuilder {

const ignoreSymlinks = !this.configurationService.getValue<ISearchConfiguration>().search.followSymlinks;

this.resolveSmartCaseToCaseSensitive(contentPattern);

const query = <ISearchQuery>{
type,
folderQueries,
Expand All @@ -95,6 +97,22 @@ export class QueryBuilder {
return query;
}

/**
* Fix the isCaseSensitive flag based on the query and the isSmartCase flag, for search providers that don't support smart case natively.
*/
private resolveSmartCaseToCaseSensitive(contentPattern: IPatternInfo): void {
if (contentPattern.isSmartCase) {
if (contentPattern.isRegExp) {
// Consider it case sensitive if it contains an unescaped capital letter
if (contentPattern.pattern.match(/([^\\]|^)[A-Z]/)) {
contentPattern.isCaseSensitive = true;
}
} else if (contentPattern.pattern.match(/[A-Z]/)) {
contentPattern.isCaseSensitive = true;
}
}
}

/**
* Take the includePattern as seen in the search viewlet, and split into components that look like searchPaths, and
* glob patterns. Glob patterns are expanded from 'foo/bar' to '{foo/bar/**, **\/foo/bar}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,11 @@ configurationRegistry.registerConfiguration({
'type': 'boolean',
'description': nls.localize('search.followSymlinks', "Controls whether to follow symlinks while searching."),
'default': true
},
'search.smartCase': {
'type': 'boolean',
'description': nls.localize('search.smartCase', "Searches case-insensitively if the pattern is all lowercase, otherwise, searches case-sensitively"),
'default': false
}
}
});
Expand Down
81 changes: 81 additions & 0 deletions src/vs/workbench/parts/search/test/common/queryBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,87 @@ suite('QueryBuilder', () => {
cases.forEach(testIncludesDataItem);
});
});

suite('smartCase', () => {
test('no flags -> no change', () => {
const query = queryBuilder.text(
{
pattern: 'a'
},
[]);

assert(!query.contentPattern.isCaseSensitive);
});

test('maintains isCaseSensitive when smartCase not set', () => {
const query = queryBuilder.text(
{
pattern: 'a',
isCaseSensitive: true
},
[]);

assert(query.contentPattern.isCaseSensitive);
});

test('maintains isCaseSensitive when smartCase set', () => {
const query = queryBuilder.text(
{
pattern: 'a',
isCaseSensitive: true,
isSmartCase: true
},
[]);

assert(query.contentPattern.isCaseSensitive);
});

test('smartCase determines not case sensitive', () => {
const query = queryBuilder.text(
{
pattern: 'abcd',
isSmartCase: true
},
[]);

assert(!query.contentPattern.isCaseSensitive);
});

test('smartCase determines case sensitive', () => {
const query = queryBuilder.text(
{
pattern: 'abCd',
isSmartCase: true
},
[]);

assert(query.contentPattern.isCaseSensitive);
});

test('smartCase determines not case sensitive (regex)', () => {
const query = queryBuilder.text(
{
pattern: 'ab\\Sd',
isRegExp: true,
isSmartCase: true
},
[]);

assert(!query.contentPattern.isCaseSensitive);
});

test('smartCase determines case sensitive (regex)', () => {
const query = queryBuilder.text(
{
pattern: 'ab[A-Z]d',
isRegExp: true,
isSmartCase: true
},
[]);

assert(query.contentPattern.isCaseSensitive);
});
});
});

function assertEqualQueries(actual: ISearchQuery, expected: ISearchQuery): void {
Expand Down
6 changes: 5 additions & 1 deletion src/vs/workbench/services/search/node/ripgrepTextSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,11 @@ export function fixDriveC(path: string): string {

function getRgArgs(config: IRawSearch): IRgGlobResult {
const args = ['--hidden', '--heading', '--line-number', '--color', 'ansi', '--colors', 'path:none', '--colors', 'line:none', '--colors', 'match:fg:red', '--colors', 'match:style:nobold'];
args.push(config.contentPattern.isCaseSensitive ? '--case-sensitive' : '--ignore-case');
if (config.contentPattern.isSmartCase) {
args.push('--smart-case');
} else {
args.push(config.contentPattern.isCaseSensitive ? '--case-sensitive' : '--ignore-case');
}

// includePattern can't have siblingClauses
foldersToIncludeGlobs(config.folderQueries, config.includePattern).forEach(globArg => {
Expand Down

0 comments on commit 6c6364d

Please sign in to comment.