searchQuery.ts 5.8 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

7
// import nls = require('vs/nls');
R
Rob Lourens 已提交
8
import { IExpression } from 'vs/base/common/glob';
9 10
import * as objects from 'vs/base/common/objects';
import * as paths from 'vs/base/common/paths';
R
Rob Lourens 已提交
11
import uri from 'vs/base/common/uri';
12
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
R
Rob Lourens 已提交
13
import { IPatternInfo, IQueryOptions, IFolderQueryOptions, ISearchQuery, QueryType, ISearchConfiguration, getExcludes } from 'vs/platform/search/common/search';
J
Johannes Rieken 已提交
14
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
E
Erich Gamma 已提交
15 16 17

export class QueryBuilder {

18 19 20
	constructor(
		@IConfigurationService private configurationService: IConfigurationService,
		@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService) {
E
Erich Gamma 已提交
21 22
	}

R
Rob Lourens 已提交
23 24
	public text(contentPattern: IPatternInfo, folderResources?: uri[], options?: IQueryOptions): ISearchQuery {
		return this.query(QueryType.Text, contentPattern, folderResources, options);
E
Erich Gamma 已提交
25 26
	}

R
Rob Lourens 已提交
27 28
	public file(folderResources?: uri[], options?: IQueryOptions): ISearchQuery {
		return this.query(QueryType.File, null, folderResources, options);
E
Erich Gamma 已提交
29 30
	}

R
Rob Lourens 已提交
31 32 33 34 35 36 37 38 39
	private query(type: QueryType, contentPattern: IPatternInfo, folderResources?: uri[], options: IQueryOptions = {}): ISearchQuery {
		const folderQueries = folderResources && folderResources.map(folder => {
			const folderConfig = this.configurationService.getConfiguration<ISearchConfiguration>(undefined, { resource: folder });
			return <IFolderQueryOptions>{
				folder,
				excludePattern: this.getExcludesForFolder(folderConfig, options),
				fileEncoding: folderConfig.files.encoding
			};
		});
E
Erich Gamma 已提交
40

R
Rob Lourens 已提交
41 42 43 44
		const useRipgrep = !folderResources || folderResources.every(folder => {
			const folderConfig = this.configurationService.getConfiguration<ISearchConfiguration>(undefined, { resource: folder });
			return folderConfig.search.useRipgrep;
		});
45

46 47 48 49 50
		const { searchPaths, additionalIncludePatterns } = this.getSearchPaths(options);
		const includePattern = objects.clone(options.includePattern);
		for (const additionalInclude of additionalIncludePatterns) {
			includePattern[additionalInclude] = true;
		}
51

52
		return {
R
Rob Lourens 已提交
53 54
			type,
			folderQueries,
55 56 57
			extraFileResources: options.extraFileResources,
			filePattern: options.filePattern,
			excludePattern: options.excludePattern,
58
			includePattern,
59
			maxResults: options.maxResults,
60 61
			sortByScore: options.sortByScore,
			cacheKey: options.cacheKey,
62
			contentPattern: contentPattern,
R
Rob Lourens 已提交
63
			useRipgrep,
64
			disregardIgnoreFiles: options.disregardIgnoreFiles,
65
			disregardExcludeSettings: options.disregardExcludeSettings,
66
			searchPaths
67
		};
E
Erich Gamma 已提交
68
	}
R
Rob Lourens 已提交
69 70 71 72 73 74 75

	private getExcludesForFolder(folderConfig: ISearchConfiguration, options: IQueryOptions): IExpression | null {
		const settingsExcludePattern = getExcludes(folderConfig);

		if (options.disregardExcludeSettings) {
			return null;
		} else if (options.excludePattern) {
76
			return objects.mixin(options.excludePattern, settingsExcludePattern, false /* no overwrite */);
R
Rob Lourens 已提交
77 78 79 80
		} else {
			return settingsExcludePattern;
		}
	}
81 82 83 84 85 86 87 88 89 90

	private getSearchPaths(options: IQueryOptions): { searchPaths: string[], additionalIncludePatterns: string[] } {
		if (!this.workspaceContextService.hasWorkspace() || !options.searchPaths) {
			// No workspace => ignore search paths
			return {
				searchPaths: [],
				additionalIncludePatterns: []
			};
		}

91 92 93
		const searchPaths: string[] = [];
		const additionalIncludePatterns: string[] = [];

94 95 96 97 98
		for (const searchPath of options.searchPaths) {
			// 1 open folder => just resolve the search paths to absolute paths
			const { pathPortion, globPortion } = splitGlobFromPath(searchPath);
			const absolutePathPortions = this.expandAbsoluteSearchPaths(pathPortion);
			searchPaths.push(...absolutePathPortions);
99

100 101 102
			if (globPortion) {
				additionalIncludePatterns.push(...absolutePathPortions.map(abs => paths.join(abs, globPortion)));
			}
103 104
		}

105 106 107 108 109 110 111 112 113 114
		return {
			searchPaths,
			additionalIncludePatterns
		};
	}

	private expandAbsoluteSearchPaths(searchPath: string): string[] {
		if (paths.isAbsolute(searchPath)) {
			return [searchPath];
		}
115

116 117 118 119 120 121 122 123 124 125
		const workspace = this.workspaceContextService.getWorkspace();
		if (workspace.roots.length === 1) {
			return [paths.join(workspace.roots[0].fsPath, searchPath)];
		} else {
			const relativeSearchPathMatch = searchPath.match(/\.\/([^\/]+)(\/.+)?/);
			if (relativeSearchPathMatch) {
				const searchPathRoot = relativeSearchPathMatch[1];
				const matchingRoots = workspace.roots.filter(root => paths.basename(root.fsPath) === searchPathRoot);
				if (matchingRoots.length) {
					return matchingRoots.map(root => paths.join(root.fsPath, relativeSearchPathMatch[2] || ''));
126
				} else {
127
					// throw new Error(nls.localize('search.invalidRootFolder', 'No root folder named {}', searchPathRoot));
128
				}
129 130 131
			} else {
				// Malformed ./ search path
				// throw new Error(nls.localize('search.invalidRelativeInclude', 'Invalid folder include pattern: {}', searchPath));
132 133 134
			}
		}

135
		return [];
136
	}
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
}

function splitGlobFromPath(searchPath: string): { pathPortion: string, globPortion?: string } {
	const globCharMatch = searchPath.match(/[\*\{\}\(\)\[\]\?]/);
	if (globCharMatch) {
		const globCharIdx = globCharMatch.index;
		const lastSlashMatch = searchPath.substr(0, globCharIdx).match(/[/|\\][^/\\]*$/);
		if (lastSlashMatch) {
			return {
				pathPortion: searchPath.substr(0, lastSlashMatch.index),
				globPortion: searchPath.substr(lastSlashMatch.index + 1)
			};
		}
	}

	// No glob char, or malformed
	return {
		pathPortion: searchPath
	};
156
}