提交 bae21689 编写于 作者: B Benjamin Pasero

Improper progress during search (fixes #4508)

上级 c8d6ee0c
...@@ -54,17 +54,17 @@ export class FileWalker { ...@@ -54,17 +54,17 @@ export class FileWalker {
this.isCanceled = true; this.isCanceled = true;
} }
public walk(rootFolders: string[], extraFiles: string[], onResult: (result: ISerializedFileMatch) => void, done: (error: Error, isLimitHit: boolean) => void): void { public walk(rootFolders: string[], extraFiles: string[], onResult: (result: ISerializedFileMatch, size: number) => void, done: (error: Error, isLimitHit: boolean) => void): void {
// Support that the file pattern is a full path to a file that exists // Support that the file pattern is a full path to a file that exists
this.checkFilePatternAbsoluteMatch((exists) => { this.checkFilePatternAbsoluteMatch((exists, size) => {
if (this.isCanceled) { if (this.isCanceled) {
return done(null, this.isLimitHit); return done(null, this.isLimitHit);
} }
// Report result from file pattern if matching // Report result from file pattern if matching
if (exists) { if (exists) {
onResult({ path: this.filePattern }); onResult({ path: this.filePattern }, size);
// Optimization: a match on an absolute path is a good result and we do not // Optimization: a match on an absolute path is a good result and we do not
// continue walking the entire root paths array for other matches because // continue walking the entire root paths array for other matches because
...@@ -92,14 +92,14 @@ export class FileWalker { ...@@ -92,14 +92,14 @@ export class FileWalker {
} }
// Support relative paths to files from a root resource // Support relative paths to files from a root resource
return this.checkFilePatternRelativeMatch(absolutePath, (match) => { return this.checkFilePatternRelativeMatch(absolutePath, (match, size) => {
if (this.isCanceled || this.isLimitHit) { if (this.isCanceled || this.isLimitHit) {
return perEntryCallback(null, null); return perEntryCallback(null, null);
} }
// Report result from file pattern if matching // Report result from file pattern if matching
if (match) { if (match) {
onResult({ path: match }); onResult({ path: match }, size);
} }
return this.doWalk(paths.normalize(absolutePath), '', files, onResult, perEntryCallback); return this.doWalk(paths.normalize(absolutePath), '', files, onResult, perEntryCallback);
...@@ -111,17 +111,17 @@ export class FileWalker { ...@@ -111,17 +111,17 @@ export class FileWalker {
}); });
} }
private checkFilePatternAbsoluteMatch(clb: (exists: boolean) => void): void { private checkFilePatternAbsoluteMatch(clb: (exists: boolean, size?: number) => void): void {
if (!this.filePattern || !paths.isAbsolute(this.filePattern)) { if (!this.filePattern || !paths.isAbsolute(this.filePattern)) {
return clb(false); return clb(false);
} }
return fs.stat(this.filePattern, (error, stat) => { return fs.stat(this.filePattern, (error, stat) => {
return clb(!error && !stat.isDirectory()); // only existing files return clb(!error && !stat.isDirectory(), stat && stat.size); // only existing files
}); });
} }
private checkFilePatternRelativeMatch(basePath: string, clb: (matchPath: string) => void): void { private checkFilePatternRelativeMatch(basePath: string, clb: (matchPath: string, size?: number) => void): void {
if (!this.filePattern || paths.isAbsolute(this.filePattern)) { if (!this.filePattern || paths.isAbsolute(this.filePattern)) {
return clb(null); return clb(null);
} }
...@@ -129,11 +129,11 @@ export class FileWalker { ...@@ -129,11 +129,11 @@ export class FileWalker {
const absolutePath = paths.join(basePath, this.filePattern); const absolutePath = paths.join(basePath, this.filePattern);
return fs.stat(absolutePath, (error, stat) => { return fs.stat(absolutePath, (error, stat) => {
return clb(!error && !stat.isDirectory() ? absolutePath : null); // only existing files return clb(!error && !stat.isDirectory() ? absolutePath : null, stat && stat.size); // only existing files
}); });
} }
private doWalk(absolutePath: string, relativeParentPathWithSlashes: string, files: string[], onResult: (result: ISerializedFileMatch) => void, done: (error: Error, result: any) => void): void { private doWalk(absolutePath: string, relativeParentPathWithSlashes: string, files: string[], onResult: (result: ISerializedFileMatch, size: number) => void, done: (error: Error, result: any) => void): void {
// Execute tasks on each file in parallel to optimize throughput // Execute tasks on each file in parallel to optimize throughput
flow.parallel(files, (file: string, clb: (error: Error) => void): void => { flow.parallel(files, (file: string, clb: (error: Error) => void): void => {
...@@ -208,7 +208,7 @@ export class FileWalker { ...@@ -208,7 +208,7 @@ export class FileWalker {
return clb(null); // ignore file if max file size is hit return clb(null); // ignore file if max file size is hit
} }
this.matchFile(onResult, currentAbsolutePath, currentRelativePathWithSlashes); this.matchFile(onResult, currentAbsolutePath, currentRelativePathWithSlashes, stat.size);
} }
// Unwind // Unwind
...@@ -224,7 +224,7 @@ export class FileWalker { ...@@ -224,7 +224,7 @@ export class FileWalker {
}); });
} }
private matchFile(onResult: (result: ISerializedFileMatch) => void, absolutePath: string, relativePathWithSlashes: string): void { private matchFile(onResult: (result: ISerializedFileMatch, size: number) => void, absolutePath: string, relativePathWithSlashes: string, size?: number): void {
if (this.isFilePatternMatch(relativePathWithSlashes) && (!this.includePattern || glob.match(this.includePattern, relativePathWithSlashes))) { if (this.isFilePatternMatch(relativePathWithSlashes) && (!this.includePattern || glob.match(this.includePattern, relativePathWithSlashes))) {
this.resultCount++; this.resultCount++;
...@@ -235,7 +235,7 @@ export class FileWalker { ...@@ -235,7 +235,7 @@ export class FileWalker {
if (!this.isLimitHit) { if (!this.isLimitHit) {
onResult({ onResult({
path: absolutePath path: absolutePath
}); }, size);
} }
} }
} }
......
...@@ -34,6 +34,7 @@ export class Engine implements ISearchEngine { ...@@ -34,6 +34,7 @@ export class Engine implements ISearchEngine {
private isDone: boolean; private isDone: boolean;
private total: number; private total: number;
private worked: number; private worked: number;
private progressed: number;
private walkerError: Error; private walkerError: Error;
private walkerIsDone: boolean; private walkerIsDone: boolean;
private fileEncoding: string; private fileEncoding: string;
...@@ -48,6 +49,7 @@ export class Engine implements ISearchEngine { ...@@ -48,6 +49,7 @@ export class Engine implements ISearchEngine {
this.limitReached = false; this.limitReached = false;
this.maxResults = config.maxResults; this.maxResults = config.maxResults;
this.worked = 0; this.worked = 0;
this.progressed = 0;
this.total = 0; this.total = 0;
this.fileEncoding = encodingExists(config.fileEncoding) ? config.fileEncoding : UTF8; this.fileEncoding = encodingExists(config.fileEncoding) ? config.fileEncoding : UTF8;
} }
...@@ -60,14 +62,19 @@ export class Engine implements ISearchEngine { ...@@ -60,14 +62,19 @@ export class Engine implements ISearchEngine {
public search(onResult: (match: ISerializedFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, isLimitHit: boolean) => void): void { public search(onResult: (match: ISerializedFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, isLimitHit: boolean) => void): void {
let resultCounter = 0; let resultCounter = 0;
let progress = () => {
this.progressed++;
if (this.progressed % Engine.PROGRESS_FLUSH_CHUNK_SIZE === 0) {
onProgress({ total: this.total, worked: this.worked }); // buffer progress in chunks to reduce pressure
}
};
let unwind = (processed: number) => { let unwind = (processed: number) => {
this.worked += processed; this.worked += processed;
// Emit progress() unless we got canceled or hit the limit // Emit progress() unless we got canceled or hit the limit
if (processed && !this.isDone && !this.isCanceled && !this.limitReached) { if (processed && !this.isDone && !this.isCanceled && !this.limitReached) {
if (this.worked % Engine.PROGRESS_FLUSH_CHUNK_SIZE === 0) { progress();
onProgress({ total: this.total, worked: this.worked });
}
} }
// Emit done() // Emit done()
...@@ -78,18 +85,17 @@ export class Engine implements ISearchEngine { ...@@ -78,18 +85,17 @@ export class Engine implements ISearchEngine {
}; };
// Walk over the file system // Walk over the file system
this.walker.walk(this.rootFolders, this.extraFiles, (result) => { this.walker.walk(this.rootFolders, this.extraFiles, (result, size) => {
this.total++; size = size ||  1;
this.total += size;
// If the result is empty or we have reached the limit or we are canceled, ignore it // If the result is empty or we have reached the limit or we are canceled, ignore it
if (this.limitReached || this.isCanceled) { if (this.limitReached || this.isCanceled) {
return unwind(1); return unwind(size);
} }
// Indicate progress to the outside // Indicate progress to the outside
if (this.worked % Engine.PROGRESS_FLUSH_CHUNK_SIZE === 0) { progress();
onProgress({ total: this.total, worked: this.worked });
}
let fileMatch: FileMatch = null; let fileMatch: FileMatch = null;
...@@ -98,7 +104,7 @@ export class Engine implements ISearchEngine { ...@@ -98,7 +104,7 @@ export class Engine implements ISearchEngine {
onResult(fileMatch.serialize()); onResult(fileMatch.serialize());
} }
return unwind(1); return unwind(size);
}; };
let perLineCallback = (line: string, lineNumber: number) => { let perLineCallback = (line: string, lineNumber: number) => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册