ripgrepFileSearch.ts 5.7 KB
Newer Older
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.
 *--------------------------------------------------------------------------------------------*/

import * as cp from 'child_process';
7
import * as path from 'vs/base/common/path';
8
import * as glob from 'vs/base/common/glob';
9
import { normalizeNFD } from 'vs/base/common/normalization';
B
Benjamin Pasero 已提交
10
import * as extpath from 'vs/base/common/extpath';
R
Rob Lourens 已提交
11 12
import { isMacintosh as isMac } from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
13
import { IFileQuery, IFolderQuery } from 'vs/workbench/services/search/common/search';
R
Rob Lourens 已提交
14
import { anchorGlob } from 'vs/workbench/services/search/node/ripgrepSearchUtils';
15
import { rgPath } from 'vscode-ripgrep';
16

17 18 19
// If vscode-ripgrep is in an .asar file, then the binary is unpacked.
const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked');

20
export function spawnRipgrepCmd(config: IFileQuery, folderQuery: IFolderQuery, includePattern?: glob.IExpression, excludePattern?: glob.IExpression) {
21
	const rgArgs = getRgArgs(config, folderQuery, includePattern, excludePattern);
22
	const cwd = folderQuery.folder.fsPath;
23
	return {
24
		cmd: cp.spawn(rgDiskPath, rgArgs.args, { cwd }),
R
Rob Lourens 已提交
25
		rgDiskPath,
26 27 28
		siblingClauses: rgArgs.siblingClauses,
		rgArgs,
		cwd
29 30 31
	};
}

32
function getRgArgs(config: IFileQuery, folderQuery: IFolderQuery, includePattern?: glob.IExpression, excludePattern?: glob.IExpression) {
33 34 35 36
	const args = ['--files', '--hidden', '--case-sensitive'];

	// includePattern can't have siblingClauses
	foldersToIncludeGlobs([folderQuery], includePattern, false).forEach(globArg => {
R
Rob Lourens 已提交
37
		const inclusion = anchorGlob(globArg);
38 39 40 41 42 43 44
		args.push('-g', inclusion);
		if (isMac) {
			const normalized = normalizeNFD(inclusion);
			if (normalized !== inclusion) {
				args.push('-g', normalized);
			}
		}
45 46 47
	});

	const rgGlobs = foldersToRgExcludeGlobs([folderQuery], excludePattern, undefined, false);
48
	rgGlobs.globArgs.forEach(globArg => {
R
Rob Lourens 已提交
49
		const exclusion = `!${anchorGlob(globArg)}`;
50 51 52 53 54 55 56 57
		args.push('-g', exclusion);
		if (isMac) {
			const normalized = normalizeNFD(exclusion);
			if (normalized !== exclusion) {
				args.push('-g', normalized);
			}
		}
	});
58
	if (folderQuery.disregardIgnoreFiles !== false) {
59 60
		// Don't use .gitignore or .ignore
		args.push('--no-ignore');
R
Rob Lourens 已提交
61 62
	} else {
		args.push('--no-ignore-parent');
63
	}
64 65

	// Follow symlinks
66
	if (!folderQuery.ignoreSymlinks) {
67 68
		args.push('--follow');
	}
69

70 71 72 73
	if (config.exists) {
		args.push('--quiet');
	}

74
	args.push('--no-config');
75
	if (folderQuery.disregardGlobalIgnoreFiles) {
76 77
		args.push('--no-ignore-global');
	}
78

79 80 81 82
	return {
		args,
		siblingClauses: rgGlobs.siblingClauses
	};
83
}
C
Christof Marti 已提交
84

R
Rob Lourens 已提交
85 86
export interface IRgGlobResult {
	globArgs: string[];
87
	siblingClauses: glob.IExpression;
R
Rob Lourens 已提交
88 89
}

90
export function foldersToRgExcludeGlobs(folderQueries: IFolderQuery[], globalExclude?: glob.IExpression, excludesToSkip?: Set<string>, absoluteGlobs = true): IRgGlobResult {
R
Rob Lourens 已提交
91 92 93
	const globArgs: string[] = [];
	let siblingClauses: glob.IExpression = {};
	folderQueries.forEach(folderQuery => {
94
		const totalExcludePattern = Object.assign({}, folderQuery.excludePattern || {}, globalExclude || {});
M
Matt Bierner 已提交
95
		const result = globExprsToRgGlobs(totalExcludePattern, absoluteGlobs ? folderQuery.folder.fsPath : undefined, excludesToSkip);
R
Rob Lourens 已提交
96 97
		globArgs.push(...result.globArgs);
		if (result.siblingClauses) {
98
			siblingClauses = Object.assign(siblingClauses, result.siblingClauses);
R
Rob Lourens 已提交
99 100 101 102 103 104
		}
	});

	return { globArgs, siblingClauses };
}

105
export function foldersToIncludeGlobs(folderQueries: IFolderQuery[], globalInclude?: glob.IExpression, absoluteGlobs = true): string[] {
R
Rob Lourens 已提交
106 107
	const globArgs: string[] = [];
	folderQueries.forEach(folderQuery => {
108
		const totalIncludePattern = Object.assign({}, globalInclude || {}, folderQuery.includePattern || {});
M
Matt Bierner 已提交
109
		const result = globExprsToRgGlobs(totalIncludePattern, absoluteGlobs ? folderQuery.folder.fsPath : undefined);
R
Rob Lourens 已提交
110 111 112 113 114 115 116 117
		globArgs.push(...result.globArgs);
	});

	return globArgs;
}

function globExprsToRgGlobs(patterns: glob.IExpression, folder?: string, excludesToSkip?: Set<string>): IRgGlobResult {
	const globArgs: string[] = [];
118
	const siblingClauses: glob.IExpression = {};
R
Rob Lourens 已提交
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
	Object.keys(patterns)
		.forEach(key => {
			if (excludesToSkip && excludesToSkip.has(key)) {
				return;
			}

			if (!key) {
				return;
			}

			const value = patterns[key];
			key = trimTrailingSlash(folder ? getAbsoluteGlob(folder, key) : key);

			// glob.ts requires forward slashes, but a UNC path still must start with \\
			// #38165 and #38151
134
			if (key.startsWith('\\\\')) {
R
Rob Lourens 已提交
135 136 137 138 139 140
				key = '\\\\' + key.substr(2).replace(/\\/g, '/');
			} else {
				key = key.replace(/\\/g, '/');
			}

			if (typeof value === 'boolean' && value) {
141
				if (key.startsWith('\\\\')) {
R
Rob Lourens 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
					// Absolute globs UNC paths don't work properly, see #58758
					key += '**';
				}

				globArgs.push(fixDriveC(key));
			} else if (value && value.when) {
				siblingClauses[key] = value;
			}
		});

	return { globArgs, siblingClauses };
}

/**
 * Resolves a glob like "node_modules/**" in "/foo/bar" to "/foo/bar/node_modules/**".
 * Special cases C:/foo paths to write the glob like /foo instead - see https://github.com/BurntSushi/ripgrep/issues/530.
 *
 * Exported for testing
 */
export function getAbsoluteGlob(folder: string, key: string): string {
162
	return path.isAbsolute(key) ?
R
Rob Lourens 已提交
163 164 165 166 167 168 169 170 171 172
		key :
		path.join(folder, key);
}

function trimTrailingSlash(str: string): string {
	str = strings.rtrim(str, '\\');
	return strings.rtrim(str, '/');
}

export function fixDriveC(path: string): string {
B
Benjamin Pasero 已提交
173
	const root = extpath.getRoot(path);
R
Rob Lourens 已提交
174 175 176
	return root.toLowerCase() === 'c:/' ?
		path.replace(/^c:[/\\]/i, '/') :
		path;
177
}