diffjam
Version:
cli for diffjam.com
96 lines (78 loc) • 3.61 kB
text/typescript
/*
Finds matches for a given set of `needles` to search for, calling `onMatch`
for each match found.
The `needles` has a primary regex with the global and multiline flags, so
we cycle through each of its matches using `.exec`. Any other regexes in
the `needles` are used to further filter whether the first line matched in
meets other terms.
If this is indeed a match, we compute the line number and column number as
well as format the match for display.
*/
import chalk from "chalk";
import { findLastIndex, last } from "lodash";
import { Match } from "./match";
import { Needles } from "./Policy";
const newLineRegExp = new RegExp("\n", "gm");
export class FileMatcher {
private newLineIndexes: number[] = [];
constructor(public path: string, public contents: string) {
let newLineRegExpMatch: RegExpExecArray | null;
while ((newLineRegExpMatch = newLineRegExp.exec(contents))) {
this.newLineIndexes.push(newLineRegExpMatch.index);
}
}
findMatches(needles: Needles, onMatch: (match: Match) => void) {
let regexMatch: RegExpExecArray | null;
while (regexMatch = needles.regex.exec(this.contents)) {
const found = regexMatch[0];
const foundStartIndex = regexMatch.index;
const foundLength = found.length;
const foundEndIndex = foundStartIndex + foundLength;
const startLineNumber = this.newLineIndexes.length && (
foundStartIndex > last(this.newLineIndexes)!
? this.newLineIndexes.length
: this.newLineIndexes.findIndex((index) => index > foundStartIndex)
)
const startLineIndex = startLineNumber === 0 ? 0 : this.newLineIndexes[startLineNumber - 1] + 1;
const startLineEndsIndex = this.newLineIndexes[startLineNumber];
const foundOnStartLine = foundEndIndex > startLineEndsIndex ? this.contents.substring(foundStartIndex, startLineEndsIndex) : found;
const beforeInSameLine = this.contents.substring(startLineIndex, foundStartIndex);
const afterInSameLine = foundEndIndex > startLineEndsIndex ? "" : this.contents.substring(foundStartIndex + foundLength, this.newLineIndexes[startLineNumber] || this.contents.length);
const startWholeLine = beforeInSameLine + foundOnStartLine + afterInSameLine;
if (needles.negative.some(term => startWholeLine.includes(term))) {
continue;
}
if (!needles.positive.every(term => startWholeLine.includes(term))) {
continue;
}
if (!needles.otherRegexes.every(term => term.test(startWholeLine))) {
continue;
}
const endLineNumber = this.newLineIndexes.length && (
1 + findLastIndex(this.newLineIndexes, (index) => index < foundEndIndex)
);
const endLineIndex = endLineNumber === 0 ? 0 : this.newLineIndexes[endLineNumber - 1] + 1;
const startColumn = foundStartIndex - startLineIndex;
const endColumn = foundEndIndex - endLineIndex;
const startWholeLineFormatted = beforeInSameLine + chalk.bold(foundOnStartLine) + afterInSameLine;
const breachPath = `${this.path}(${startLineNumber + 1}:${startColumn + 1})`;
onMatch({
startLineNumber,
endLineNumber,
startColumn,
endColumn,
found,
startWholeLine,
startWholeLineFormatted,
breachPath,
path: this.path,
});
}
}
}
export function findMatches(fileContents: string, needles: Needles) {
const fileMatcher = new FileMatcher("", fileContents);
const matches: Match[] = [];
fileMatcher.findMatches(needles, (match) => matches.push(match));
return matches;
}