vscode/src/vs/workbench/services/search/common/getFileResults.ts

130 lines
4.4 KiB
TypeScript
Raw Blame History

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITextSearchResult } from 'vs/workbench/services/search/common/search';
import { TextSearchPreviewOptions } from 'vs/workbench/services/search/common/searchExtTypes';
import { Range } from 'vs/editor/common/core/range';
export const getFileResults = (
bytes: Uint8Array,
pattern: RegExp,
options: {
beforeContext: number;
afterContext: number;
previewOptions: TextSearchPreviewOptions | undefined;
remainingResultQuota: number;
}
): ITextSearchResult[] => {
let text: string;
if (bytes[0] === 0xff && bytes[1] === 0xfe) {
text = new TextDecoder('utf-16le').decode(bytes);
} else if (bytes[0] === 0xfe && bytes[1] === 0xff) {
text = new TextDecoder('utf-16be').decode(bytes);
} else {
text = new TextDecoder('utf8').decode(bytes);
// allow-any-unicode-next-line
if (text.slice(0, 1000).includes('<27>') && bytes.includes(0)) {
return [];
}
}
const results: ITextSearchResult[] = [];
const patternIndecies: { matchStartIndex: number; matchedText: string; }[] = [];
let patternMatch: RegExpExecArray | null = null;
let remainingResultQuota = options.remainingResultQuota;
while (remainingResultQuota >= 0 && (patternMatch = pattern.exec(text))) {
patternIndecies.push({ matchStartIndex: patternMatch.index, matchedText: patternMatch[0] });
remainingResultQuota--;
}
if (patternIndecies.length) {
const contextLinesNeeded = new Set<number>();
const resultLines = new Set<number>();
const lineRanges: { start: number; end: number; }[] = [];
const readLine = (lineNumber: number) => text.slice(lineRanges[lineNumber].start, lineRanges[lineNumber].end);
let prevLineEnd = 0;
let lineEndingMatch: RegExpExecArray | null = null;
const lineEndRegex = /\r?\n/g;
while ((lineEndingMatch = lineEndRegex.exec(text))) {
lineRanges.push({ start: prevLineEnd, end: lineEndingMatch.index });
prevLineEnd = lineEndingMatch.index + lineEndingMatch[0].length;
}
if (prevLineEnd < text.length) { lineRanges.push({ start: prevLineEnd, end: text.length }); }
let startLine = 0;
for (const { matchStartIndex, matchedText } of patternIndecies) {
if (remainingResultQuota < 0) {
break;
}
while (Boolean(lineRanges[startLine + 1]) && matchStartIndex > lineRanges[startLine].end) {
startLine++;
}
let endLine = startLine;
while (Boolean(lineRanges[endLine + 1]) && matchStartIndex + matchedText.length > lineRanges[endLine].end) {
endLine++;
}
if (options.beforeContext) {
for (let contextLine = Math.max(0, startLine - options.beforeContext); contextLine < startLine; contextLine++) {
contextLinesNeeded.add(contextLine);
}
}
let previewText = '';
let offset = 0;
for (let matchLine = startLine; matchLine <= endLine; matchLine++) {
let previewLine = readLine(matchLine);
if (options.previewOptions?.charsPerLine && previewLine.length > options.previewOptions.charsPerLine) {
offset = Math.max(matchStartIndex - lineRanges[startLine].start - 20, 0);
previewLine = previewLine.substr(offset, options.previewOptions.charsPerLine);
}
previewText += `${previewLine}\n`;
resultLines.add(matchLine);
}
const fileRange = new Range(
startLine,
matchStartIndex - lineRanges[startLine].start,
endLine,
matchStartIndex + matchedText.length - lineRanges[endLine].start
);
const previewRange = new Range(
0,
matchStartIndex - lineRanges[startLine].start - offset,
endLine - startLine,
matchStartIndex + matchedText.length - lineRanges[endLine].start - (endLine === startLine ? offset : 0)
);
const match: ITextSearchResult = {
ranges: fileRange,
preview: { text: previewText, matches: previewRange },
};
results.push(match);
if (options.afterContext) {
for (let contextLine = endLine + 1; contextLine <= Math.min(endLine + options.afterContext, lineRanges.length - 1); contextLine++) {
contextLinesNeeded.add(contextLine);
}
}
}
for (const contextLine of contextLinesNeeded) {
if (!resultLines.has(contextLine)) {
results.push({
text: readLine(contextLine),
lineNumber: contextLine + 1,
});
}
}
}
return results;
};