提交 1d89f95b 编写于 作者: R Rob Lourens

Fix #22984 and add unit tests

上级 00187b00
......@@ -66,6 +66,7 @@
{
"type": "node",
"request": "attach",
"protocol": "legacy",
"name": "Attach to Search process",
"port": 7890,
"sourceMaps": true,
......
......@@ -88,6 +88,7 @@ export class RipgrepEngine implements ISearchEngine<ISerializedFileMatch> {
});
this.rgProc.on('close', code => {
this.ripgrepParser.flush(); // Get last result
this.rgProc = null;
// console.log(`closed with ${code}`);
......@@ -106,8 +107,8 @@ export class RipgrepParser extends EventEmitter {
private static RESULT_REGEX = /^\u001b\[m(\d+)\u001b\[m:(.*)$/;
private static FILE_REGEX = /^\u001b\[m(.+)\u001b\[m$/;
private static MATCH_START_MARKER = '\u001b[m\u001b[31m';
private static MATCH_END_MARKER = '\u001b[m';
public static MATCH_START_MARKER = '\u001b[m\u001b[31m';
public static MATCH_END_MARKER = '\u001b[m';
private fileMatch: FileMatch;
private remainder: string;
......@@ -123,8 +124,14 @@ export class RipgrepParser extends EventEmitter {
this.isDone = true;
}
public flush(): void {
if (this.fileMatch) {
this.onResult();
}
}
public handleData(data: string | Buffer): void {
// If the previous data chunk didn't end in a newline, append it to this chunk
// If the previous data chunk didn't end in a newline, prepend it to this chunk
const dataStr = this.remainder ?
this.remainder + data.toString() :
data.toString();
......@@ -139,23 +146,18 @@ export class RipgrepParser extends EventEmitter {
}
let r: RegExpMatchArray;
if (!outputLine) {
if (this.fileMatch) {
this.onResult();
}
} else if (r = outputLine.match(RipgrepParser.RESULT_REGEX)) {
if (r = outputLine.match(RipgrepParser.RESULT_REGEX)) {
// Line is a result - add to collected results for the current file path
this.handleMatchLine(outputLine, parseInt(r[1]) - 1, r[2]);
} else if (r = outputLine.match(RipgrepParser.FILE_REGEX)) {
// Line is a file path - send all collected results for the previous file path
if (this.fileMatch) {
// TODO@Rob Check fileMatch against other exclude globs
this.onResult();
}
this.fileMatch = new FileMatch(path.join(this.rootFolder, r[1]));
} else {
// Line is malformed
// Line is empty (or malformed)
}
}
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import path = require('path');
import assert = require('assert');
import * as arrays from 'vs/base/common/arrays';
import { RipgrepParser } from 'vs/workbench/services/search/node/ripgrepTextSearch';
import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search';
suite('RipgrepParser', () => {
const rootFolder = '/workspace';
const fileSectionEnd = '\n';
function getFileLine(relativePath: string): string {
return `\u001b\[m${relativePath}\u001b\[m`;
}
function getMatchLine(lineNum: number, matchParts: string[]): string {
let matchLine = `\u001b\[m${lineNum}\u001b\[m:` +
`${matchParts.shift()}${RipgrepParser.MATCH_START_MARKER}${matchParts.shift()}${RipgrepParser.MATCH_END_MARKER}${matchParts.shift()}`;
while (matchParts.length) {
matchLine += `${RipgrepParser.MATCH_START_MARKER}${matchParts.shift()}${RipgrepParser.MATCH_END_MARKER}${matchParts.shift() || ''}`;
}
return matchLine;
}
function parseInput(inputChunks: string[]): ISerializedFileMatch[] {
const matches: ISerializedFileMatch[] = [];
const rgp = new RipgrepParser(1e6, rootFolder);
rgp.on('result', (match: ISerializedFileMatch) => {
matches.push(match);
});
inputChunks.forEach(chunk => rgp.handleData(chunk));
rgp.flush();
return matches;
}
function halve(str: string) {
const halfIdx = Math.floor(str.length / 2);
return [str.substr(0, halfIdx), str.substr(halfIdx)];
}
function arrayOfChars(str: string) {
const chars = [];
for (let char of str) {
chars.push(char);
}
return chars;
}
test('Parses one chunk', () => {
const input = [
[getFileLine('a.txt'), getMatchLine(1, ['before', 'match', 'after']), getMatchLine(2, ['before', 'match', 'after']), fileSectionEnd].join('\n')
];
const results = parseInput(input);
assert.equal(results.length, 1);
assert.deepEqual(results[0],
<ISerializedFileMatch>{
numMatches: 2,
path: path.join(rootFolder, 'a.txt'),
lineMatches: [
{
lineNumber: 0,
preview: 'beforematchafter',
offsetAndLengths: [[6, 5]]
},
{
lineNumber: 1,
preview: 'beforematchafter',
offsetAndLengths: [[6, 5]]
}
]
});
});
test('Parses multiple chunks broken at file sections', () => {
const input = [
[getFileLine('a.txt'), getMatchLine(1, ['before', 'match', 'after']), getMatchLine(2, ['before', 'match', 'after']), fileSectionEnd].join('\n'),
[getFileLine('b.txt'), getMatchLine(1, ['before', 'match', 'after']), getMatchLine(2, ['before', 'match', 'after']), fileSectionEnd].join('\n'),
[getFileLine('c.txt'), getMatchLine(1, ['before', 'match', 'after']), getMatchLine(2, ['before', 'match', 'after']), fileSectionEnd].join('\n')
];
const results = parseInput(input);
assert.equal(results.length, 3);
results.forEach(fileResult => assert.equal(fileResult.numMatches, 2));
});
const singleLineChunks = [
getFileLine('a.txt'),
getMatchLine(1, ['before', 'match', 'after']),
getMatchLine(2, ['before', 'match', 'after']),
fileSectionEnd,
getFileLine('b.txt'),
getMatchLine(1, ['before', 'match', 'after']),
getMatchLine(2, ['before', 'match', 'after']),
fileSectionEnd,
getFileLine('c.txt'),
getMatchLine(1, ['before', 'match', 'after']),
getMatchLine(2, ['before', 'match', 'after']),
fileSectionEnd
];
test('Parses multiple chunks broken at each line', () => {
const input = singleLineChunks.map(chunk => chunk + '\n');
const results = parseInput(input);
assert.equal(results.length, 3);
results.forEach(fileResult => assert.equal(fileResult.numMatches, 2));
});
test('Parses multiple chunks broken in the middle of each line', () => {
const input = arrays.flatten(singleLineChunks
.map(chunk => chunk + '\n')
.map(halve));
const results = parseInput(input);
assert.equal(results.length, 3);
results.forEach(fileResult => assert.equal(fileResult.numMatches, 2));
});
test('Parses multiple chunks broken at each character', () => {
const input = arrays.flatten(singleLineChunks
.map(chunk => chunk + '\n')
.map(arrayOfChars));
const results = parseInput(input);
assert.equal(results.length, 3);
results.forEach(fileResult => assert.equal(fileResult.numMatches, 2));
});
test('Parses chunks broken before newline', () => {
const input = arrays.flatten(singleLineChunks
.map(chunk => '\n' + chunk)
.map(arrayOfChars));
const results = parseInput(input);
assert.equal(results.length, 3);
results.forEach(fileResult => assert.equal(fileResult.numMatches, 2));
});
});
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册