vscode文档搜索机制

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 规则,并自动跳过隐藏文件 / 目录和二进制文件。ripgrepWindowsmacOSLinux 上有一流的支持,二进制下载可用于 every release。 ripgrep类似于其他流行的搜索工具,如 Silver SearcherACK 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。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值