La velocidad de búsqueda global de vscode es muy rápida, ¿cuál es el secreto?
A menudo necesitamos buscar una cadena en una gran cantidad de archivos de texto, y podemos usar el integrado Linux
a continuación grep
, pero la línea de comando de grep todavía es un poco difícil de recordar. ripgrep
Es una herramienta de código abierto, Rust
escrita en uso y compatible con todas las plataformas. Parece mucho más poderoso que grep. Si lo usa VS Code
, entonces debe haber usado esta herramienta indirectamente, porque la función Buscar en archivos de VS Code se llama directamente ripgrep
.
La dirección de github de ripgrep: https://github.com/BurntSushi/ripgrep
ripgrep
es una herramienta de búsqueda orientada a líneas que busca recursivamente patrones de expresiones regulares en el directorio actual. De forma predeterminada, las reglas ripgrep
se respetan Gitignore
y los archivos/directorios ocultos y los archivos binarios se omiten automáticamente. ripgrep
Hay soporte de primera clase en Windows
, macOS
y Linux
, con descargas binarias disponibles para cada versión. ripgrep es similar a otras herramientas de búsqueda populares como Silver Searcher
, ACK
y Grep
.
Hay un gran dios que resolvió un documento chino: https://gitcode.gitcode.host/docs-cn/ripgrep-docs-cn/index.html
Para facilitar el uso en el proyecto, vscode está empaquetado en un módulo npmvscode-regexpp
Dirección de Npm: https://www.npmjs.com/package/@vscode/ripgrep
ejemplo:
const {
rgPath } = require('vscode-ripgrep');
// child_process.spawn(rgPath, ...)
repositorio de github: https://github.com/microsoft/vscode-ripgrep
Veamos como se llama, el siguiente es el paquete npm, de hecho también es un archivo ejecutable
que se ejecuta al final.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 });
}
}
});
});
}
}
Imprimir datos Los datos originales devuelven la matriz de búfer, que
se vuelve así después de ser procesada por vscode,
lo cual es muy claro y podemos entenderlo.
range es un objeto javascript que representa un fragmento de documento que contiene nodos y partes de nodos de texto. Se utiliza para el procesamiento de documentos.
https://developer.mozilla.org/en-US/docs/Web/API/Range
Vscode es un rango simulado que encapsula un rango adecuado para sus propios escenarios de uso.