vscode的全局搜索速度非常快,其中的奥妙是什么?
我们常常需要在一大堆文本文件里搜索一个字符串,在Linux下可以用自带的grep,不过grep的命令行还是有点难记。ripgrep是开源工具,使用Rust编写,全平台支持。看上去比grep要强大不少。如果你使用VS Code,那么你必定间接的用过这个工具了,因为VS Code的Find In Files功能就是直接调用的ripgrep。
ripgrep的github地址:https://github.com/BurntSushi/ripgrep

 ripgrep 是一种面向行的搜索工具,它递归地在当前目录中搜索正则表达式模式。默认情况下,ripgrep将尊重 Gitignore 规则,并自动跳过隐藏文件 / 目录和二进制文件。ripgrep在 Windows、macOS 和 Linux 上有一流的支持,二进制下载可用于 every release。 ripgrep类似于其他流行的搜索工具,如 Silver Searcher、ACK 和 Grep。
有大神整理出黎一份中文文档:https://gitcode.gitcode.host/docs-cn/ripgrep-docs-cn/index.html
vscode为了方便在项目中使用,封装成了一个npm 模块 vscode-regexpp
npm地址:https://www.npmjs.com/package/@vscode/ripgrep
example:
const { rgPath } = require('vscode-ripgrep');
// child_process.spawn(rgPath, ...)
github仓库:https://github.com/microsoft/vscode-ripgrep
我们看下是怎么调用的,下面是npm包
 
 
 其实最后也是执行的一个rg的可执行文件
src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts
import * as cp from 'child_process';
import { rgPath } from '@vscode/ripgrep';
// 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');
export class RipgrepTextSearchEngine {
	constructor(private outputChannel: IOutputChannel) { }
	provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress<TextSearchResult>, token: CancellationToken): Promise<TextSearchComplete> {
		this.outputChannel.appendLine(`provideTextSearchResults ${query.pattern}, ${JSON.stringify({
			...options,
			...{
				folder: options.folder.toString()
			}
		})}`);
		return new Promise((resolve, reject) => {
			token.onCancellationRequested(() => cancel());
			const rgArgs = getRgArgs(query, options);
			const cwd = options.folder.fsPath;
			const escapedArgs = rgArgs
				.map(arg => arg.match(/^-/) ? arg : `'${arg}'`)
				.join(' ');
			this.outputChannel.appendLine(`${rgDiskPath} ${escapedArgs}\n - cwd: ${cwd}`);
			// console.log('999999999999', rgDiskPath); // /Users/liuchongyang/site/vscode/node_modules/@vscode/ripgrep/bin/rg
			let rgProc: Maybe<cp.ChildProcess> = cp.spawn(rgDiskPath, rgArgs, { cwd });
			rgProc.on('error', e => {
				console.error(e);
				this.outputChannel.appendLine('Error: ' + (e && e.message));
				reject(serializeSearchError(new SearchError(e && e.message, SearchErrorCode.rgProcessError)));
			});
			let gotResult = false;
			const ripgrepParser = new RipgrepParser(options.maxResults, cwd, options.previewOptions);
			ripgrepParser.on('result', (match: TextSearchResult) => {
				console.log('match-->', match); //匹配的结果
				gotResult = true;
				dataWithoutResult = '';
				progress.report(match);
			});
			let isDone = false;
			const cancel = () => {
				isDone = true;
				if (rgProc) {
					rgProc.kill();
				}
				if (ripgrepParser) {
					ripgrepParser.cancel();
				}
			};
			let limitHit = false;
			ripgrepParser.on('hitLimit', () => {
				limitHit = true;
				cancel();
			});
			let dataWithoutResult = '';
			rgProc.stdout!.on('data', data => {
				console.log('data->', data);
				ripgrepParser.handleData(data);
				if (!gotResult) {
					dataWithoutResult += data;
				}
			});
			let gotData = false;
			rgProc.stdout!.once('data', () => gotData = true);
			let stderr = '';
			rgProc.stderr!.on('data', data => {
				const message = data.toString();
				this.outputChannel.appendLine(message);
				stderr += message;
			});
			rgProc.on('close', () => {
				this.outputChannel.appendLine(gotData ? 'Got data from stdout' : 'No data from stdout');
				this.outputChannel.appendLine(gotResult ? 'Got result from parser' : 'No result from parser');
				if (dataWithoutResult) {
					this.outputChannel.appendLine(`Got data without result: ${dataWithoutResult}`);
				}
				this.outputChannel.appendLine('');
				if (isDone) {
					resolve({ limitHit });
				} else {
					// Trigger last result
					ripgrepParser.flush();
					rgProc = null;
					let searchError: Maybe<SearchError>;
					if (stderr && !gotData && (searchError = rgErrorMsgForDisplay(stderr))) {
						reject(serializeSearchError(new SearchError(searchError.message, searchError.code)));
					} else {
						resolve({ limitHit });
					}
				}
			});
		});
	}
}
打印data 原数据返回的是buffer数组
 
 经过vscode处理后变成了这样的
 
 这样很清晰 我们就可以看懂了。
range 是javascript对象,表示一个包含节点与文本节点的一部分的文档片段。用于文档处理。
 https://developer.mozilla.org/zh-CN/docs/Web/API/Range
vscode是模拟range 封装了一个 适合自己使用场景的range。



















