Merge from vscode 2b0b9136329c181a9e381463a1f7dc3a2d105a34 (#4880)

This commit is contained in:
Karl Burtram
2019-04-05 10:09:18 -07:00
committed by GitHub
parent 9bd7e30d18
commit cb5bcf2248
433 changed files with 8915 additions and 8361 deletions

View File

@@ -19,8 +19,7 @@ import { StopWatch } from 'vs/base/common/stopwatch';
import * as strings from 'vs/base/common/strings';
import * as types from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import * as extfs from 'vs/base/node/extfs';
import * as flow from 'vs/base/node/flow';
import { readdir } from 'vs/base/node/pfs';
import { IFileQuery, IFolderQuery, IProgressMessage, ISearchEngineStats, IRawFileMatch, ISearchEngine, ISearchEngineSuccess } from 'vs/workbench/services/search/common/search';
import { spawnRipgrepCmd } from './ripgrepFileSearch';
@@ -128,7 +127,7 @@ export class FileWalker {
this.cmdSW = StopWatch.create(false);
// For each root folder
flow.parallel<IFolderQuery, void>(folderQueries, (folderQuery: IFolderQuery, rootFolderDone: (err: Error | null, result: void) => void) => {
this.parallel<IFolderQuery, void>(folderQueries, (folderQuery: IFolderQuery, rootFolderDone: (err: Error | null, result: void) => void) => {
this.call(this.cmdTraversal, this, folderQuery, onResult, onMessage, (err?: Error) => {
if (err) {
const errorMessage = toErrorMessage(err);
@@ -146,6 +145,34 @@ export class FileWalker {
});
}
private parallel<T, E>(list: T[], fn: (item: T, callback: (err: Error | null, result: E | null) => void) => void, callback: (err: Array<Error | null> | null, result: E[]) => void): void {
const results = new Array(list.length);
const errors = new Array<Error | null>(list.length);
let didErrorOccur = false;
let doneCount = 0;
if (list.length === 0) {
return callback(null, []);
}
list.forEach((item, index) => {
fn(item, (error, result) => {
if (error) {
didErrorOccur = true;
results[index] = null;
errors[index] = error;
} else {
results[index] = result;
errors[index] = null;
}
if (++doneCount === list.length) {
return callback(didErrorOccur ? errors : null, results);
}
});
});
}
private call<F extends Function>(fun: F, that: any, ...args: any[]): void {
try {
fun.apply(that, args);
@@ -440,7 +467,7 @@ export class FileWalker {
// Execute tasks on each file in parallel to optimize throughput
const hasSibling = glob.hasSiblingFn(() => files);
flow.parallel(files, (file: string, clb: (error: Error | null, _?: any) => void): void => {
this.parallel(files, (file: string, clb: (error: Error | null, _?: any) => void): void => {
// Check canceled
if (this.isCanceled || this.isLimitHit) {
@@ -489,12 +516,14 @@ export class FileWalker {
this.walkedPaths[realpath] = true; // remember as walked
// Continue walking
return extfs.readdir(currentAbsolutePath, (error: Error, children: string[]): void => {
if (error || this.isCanceled || this.isLimitHit) {
return readdir(currentAbsolutePath).then(children => {
if (this.isCanceled || this.isLimitHit) {
return clb(null);
}
this.doWalk(folderQuery, currentRelativePath, children, onResult, err => clb(err || null));
}, error => {
clb(null);
});
});
}

View File

@@ -11,7 +11,7 @@ import * as resources from 'vs/base/common/resources';
import { StopWatch } from 'vs/base/common/stopwatch';
import { URI } from 'vs/base/common/uri';
import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery, QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/common/search';
import * as vscode from 'vscode';
import { FileSearchProvider, FileSearchOptions } from 'vs/workbench/services/search/common/searchExtTypes';
export interface IInternalFileMatch {
base: URI;
@@ -45,7 +45,7 @@ class FileSearchEngine {
private globalExcludePattern?: glob.ParsedExpression;
constructor(private config: IFileQuery, private provider: vscode.FileSearchProvider, private sessionToken?: CancellationToken) {
constructor(private config: IFileQuery, private provider: FileSearchProvider, private sessionToken?: CancellationToken) {
this.filePattern = config.filePattern;
this.includePattern = config.includePattern && glob.parse(config.includePattern);
this.maxResults = config.maxResults || undefined;
@@ -172,7 +172,7 @@ class FileSearchEngine {
});
}
private getSearchOptionsForFolder(fq: IFolderQuery<URI>): vscode.FileSearchOptions {
private getSearchOptionsForFolder(fq: IFolderQuery<URI>): FileSearchOptions {
const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern);
const excludes = resolvePatternsForProvider(this.config.excludePattern, fq.excludePattern);
@@ -283,7 +283,7 @@ export class FileSearchManager {
private readonly sessions = new Map<string, CancellationTokenSource>();
fileSearch(config: IFileQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise<ISearchCompleteStats> {
fileSearch(config: IFileQuery, provider: FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise<ISearchCompleteStats> {
const sessionTokenSource = this.getSessionTokenSource(config.cacheKey);
const engine = new FileSearchEngine(config, provider, sessionTokenSource && sessionTokenSource.token);

View File

@@ -144,7 +144,8 @@ function globExprsToRgGlobs(patterns: glob.IExpression, folder?: string, exclude
}
globArgs.push(fixDriveC(key));
} else if (value && value.when) {
// {{SQL CARBON EDIT}} @todo anthonydresser cast value because we aren't using strict null checks
} else if (value && (<glob.SiblingClause>value).when) {
siblingClauses[key] = value;
}
});

View File

@@ -3,24 +3,25 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { OutputChannel } from 'vs/workbench/services/search/node/ripgrepSearchUtils';
import { RipgrepTextSearchEngine } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine';
import * as vscode from 'vscode';
import { TextSearchProvider, TextSearchComplete, TextSearchResult, TextSearchQuery, TextSearchOptions } from 'vs/workbench/services/search/common/searchExtTypes';
import { Progress } from 'vs/platform/progress/common/progress';
export class RipgrepSearchProvider implements vscode.TextSearchProvider {
private inProgress: Set<vscode.CancellationTokenSource> = new Set();
export class RipgrepSearchProvider implements TextSearchProvider {
private inProgress: Set<CancellationTokenSource> = new Set();
constructor(private outputChannel: OutputChannel) {
process.once('exit', () => this.dispose());
}
provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress<vscode.TextSearchResult>, token: vscode.CancellationToken): Promise<vscode.TextSearchComplete> {
provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress<TextSearchResult>, token: CancellationToken): Promise<TextSearchComplete> {
const engine = new RipgrepTextSearchEngine(this.outputChannel);
return this.withToken(token, token => engine.provideTextSearchResults(query, options, progress, token));
}
private async withToken<T>(token: vscode.CancellationToken, fn: (token: vscode.CancellationToken) => Promise<T>): Promise<T> {
private async withToken<T>(token: CancellationToken, fn: (token: CancellationToken) => Promise<T>): Promise<T> {
const merged = mergedTokenSource(token);
this.inProgress.add(merged);
const result = await fn(merged.token);
@@ -34,7 +35,7 @@ export class RipgrepSearchProvider implements vscode.TextSearchProvider {
}
}
function mergedTokenSource(token: vscode.CancellationToken): vscode.CancellationTokenSource {
function mergedTokenSource(token: CancellationToken): CancellationTokenSource {
const tokenSource = new CancellationTokenSource();
token.onCancellationRequested(() => tokenSource.cancel());

View File

@@ -6,8 +6,9 @@
import { startsWith } from 'vs/base/common/strings';
import { ILogService } from 'vs/platform/log/common/log';
import { SearchRange, TextSearchMatch } from 'vs/workbench/services/search/common/search';
import * as vscode from 'vscode';
import { mapArrayOrNot } from 'vs/base/common/arrays';
import { URI } from 'vs/base/common/uri';
import * as searchExtTypes from 'vs/workbench/services/search/common/searchExtTypes';
export type Maybe<T> = T | null | undefined;
@@ -16,9 +17,9 @@ export function anchorGlob(glob: string): string {
}
/**
* Create a vscode.TextSearchResult by using our internal TextSearchResult type for its previewOptions logic.
* Create a vscode.TextSearchMatch by using our internal TextSearchMatch type for its previewOptions logic.
*/
export function createTextSearchResult(uri: vscode.Uri, text: string, range: Range | Range[], previewOptions?: vscode.TextSearchPreviewOptions): vscode.TextSearchMatch {
export function createTextSearchResult(uri: URI, text: string, range: searchExtTypes.Range | searchExtTypes.Range[], previewOptions?: searchExtTypes.TextSearchPreviewOptions): searchExtTypes.TextSearchMatch {
const searchRange = mapArrayOrNot(range, rangeToSearchRange);
const internalResult = new TextSearchMatch(text, searchRange, previewOptions);
@@ -33,50 +34,12 @@ export function createTextSearchResult(uri: vscode.Uri, text: string, range: Ran
};
}
function rangeToSearchRange(range: Range): SearchRange {
function rangeToSearchRange(range: searchExtTypes.Range): SearchRange {
return new SearchRange(range.start.line, range.start.character, range.end.line, range.end.character);
}
function searchRangeToRange(range: SearchRange): Range {
return new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
}
export class Position {
constructor(readonly line: number, readonly character: number) { }
isBefore(other: Position): boolean { return false; }
isBeforeOrEqual(other: Position): boolean { return false; }
isAfter(other: Position): boolean { return false; }
isAfterOrEqual(other: Position): boolean { return false; }
isEqual(other: Position): boolean { return false; }
compareTo(other: Position): number { return 0; }
translate(lineDelta?: number, characterDelta?: number): Position;
translate(change: { lineDelta?: number; characterDelta?: number; }): Position;
translate(_?: any, _2?: any): Position { return new Position(0, 0); }
with(line?: number, character?: number): Position;
with(change: { line?: number; character?: number; }): Position;
with(_: any): Position { return new Position(0, 0); }
}
export class Range {
readonly start: Position;
readonly end: Position;
constructor(startLine: number, startCol: number, endLine: number, endCol: number) {
this.start = new Position(startLine, startCol);
this.end = new Position(endLine, endCol);
}
isEmpty: boolean;
isSingleLine: boolean;
contains(positionOrRange: Position | Range): boolean { return false; }
isEqual(other: Range): boolean { return false; }
intersection(range: Range): Range | undefined { return undefined; }
union(other: Range): Range { return new Range(0, 0, 0, 0); }
with(start?: Position, end?: Position): Range;
with(change: { start?: Position, end?: Position }): Range;
with(_: any): Range { return new Range(0, 0, 0, 0); }
function searchRangeToRange(range: SearchRange): searchExtTypes.Range {
return new searchExtTypes.Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
}
export interface IOutputChannel {

View File

@@ -10,12 +10,14 @@ import { NodeStringDecoder, StringDecoder } from 'string_decoder';
import { createRegExp, startsWith, startsWithUTF8BOM, stripUTF8BOM, escapeRegExpCharacters, endsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { IExtendedExtensionSearchOptions, SearchError, SearchErrorCode, serializeSearchError } from 'vs/workbench/services/search/common/search';
import * as vscode from 'vscode';
import { rgPath } from 'vscode-ripgrep';
import { anchorGlob, createTextSearchResult, IOutputChannel, Maybe, Range } from './ripgrepSearchUtils';
import { anchorGlob, createTextSearchResult, IOutputChannel, Maybe } from './ripgrepSearchUtils';
import { coalesce } from 'vs/base/common/arrays';
import { splitGlobAware } from 'vs/base/common/glob';
import { groupBy } from 'vs/base/common/collections';
import { TextSearchQuery, TextSearchOptions, TextSearchResult, TextSearchComplete, TextSearchPreviewOptions, TextSearchContext, TextSearchMatch, Range } from 'vs/workbench/services/search/common/searchExtTypes';
import { Progress } from 'vs/platform/progress/common/progress';
import { CancellationToken } from 'vs/base/common/cancellation';
// 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');
@@ -24,7 +26,7 @@ export class RipgrepTextSearchEngine {
constructor(private outputChannel: IOutputChannel) { }
provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress<vscode.TextSearchResult>, token: vscode.CancellationToken): Promise<vscode.TextSearchComplete> {
provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress<TextSearchResult>, token: CancellationToken): Promise<TextSearchComplete> {
this.outputChannel.appendLine(`provideTextSearchResults ${query.pattern}, ${JSON.stringify({
...options,
...{
@@ -53,7 +55,7 @@ export class RipgrepTextSearchEngine {
let gotResult = false;
const ripgrepParser = new RipgrepParser(options.maxResults, cwd, options.previewOptions);
ripgrepParser.on('result', (match: vscode.TextSearchResult) => {
ripgrepParser.on('result', (match: TextSearchResult) => {
gotResult = true;
progress.report(match);
});
@@ -155,7 +157,7 @@ export class RipgrepParser extends EventEmitter {
private numResults = 0;
constructor(private maxResults: number, private rootFolder: string, private previewOptions?: vscode.TextSearchPreviewOptions) {
constructor(private maxResults: number, private rootFolder: string, private previewOptions?: TextSearchPreviewOptions) {
super();
this.stringDecoder = new StringDecoder();
}
@@ -169,7 +171,7 @@ export class RipgrepParser extends EventEmitter {
}
on(event: 'result', listener: (result: vscode.TextSearchResult) => void): this;
on(event: 'result', listener: (result: TextSearchResult) => void): this;
on(event: 'hitLimit', listener: () => void): this;
on(event: string, listener: (...args: any[]) => void): this {
super.on(event, listener);
@@ -240,7 +242,7 @@ export class RipgrepParser extends EventEmitter {
}
}
private createTextSearchMatch(data: IRgMatch, uri: vscode.Uri): vscode.TextSearchMatch {
private createTextSearchMatch(data: IRgMatch, uri: URI): TextSearchMatch {
const lineNumber = data.line_number - 1;
let isBOMStripped = false;
let fullText = bytesOrTextToString(data.lines);
@@ -290,7 +292,7 @@ export class RipgrepParser extends EventEmitter {
return createTextSearchResult(uri, fullText, <Range[]>ranges, this.previewOptions);
}
private createTextSearchContext(data: IRgMatch, uri: URI): vscode.TextSearchContext[] {
private createTextSearchContext(data: IRgMatch, uri: URI): TextSearchContext[] {
const text = bytesOrTextToString(data.lines);
const startLine = data.line_number;
return text
@@ -305,7 +307,7 @@ export class RipgrepParser extends EventEmitter {
});
}
private onResult(match: vscode.TextSearchResult): void {
private onResult(match: TextSearchResult): void {
this.emit('result', match);
}
}
@@ -333,7 +335,7 @@ function getNumLinesAndLastNewlineLength(text: string): { numLines: number, last
return { numLines, lastLineLength };
}
function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions): string[] {
function getRgArgs(query: TextSearchQuery, options: TextSearchOptions): string[] {
const args = ['--hidden'];
args.push(query.isCaseSensitive ? '--case-sensitive' : '--ignore-case');

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import * as extfs from 'vs/base/node/extfs';
import * as pfs from 'vs/base/node/pfs';
import { IFileMatch, IProgressMessage, ITextQuery, ITextSearchStats, ITextSearchMatch, ISerializedFileMatch, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search';
import { RipgrepTextSearchEngine } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine';
import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager';
@@ -30,7 +30,7 @@ export class TextSearchEngineAdapter {
onMessage({ message: msg });
}
};
const textSearchManager = new TextSearchManager(this.query, new RipgrepTextSearchEngine(pretendOutputChannel), extfs);
const textSearchManager = new TextSearchManager(this.query, new RipgrepTextSearchEngine(pretendOutputChannel), pfs);
return new Promise((resolve, reject) => {
return textSearchManager
.search(

View File

@@ -11,9 +11,9 @@ import * as glob from 'vs/base/common/glob';
import * as resources from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { toCanonicalName } from 'vs/base/node/encoding';
import * as extfs from 'vs/base/node/extfs';
import * as pfs from 'vs/base/node/pfs';
import { IExtendedExtensionSearchOptions, IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchResult, QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/common/search';
import * as vscode from 'vscode';
import { TextSearchProvider, TextSearchResult, TextSearchMatch, TextSearchComplete, Range, TextSearchOptions, TextSearchQuery } from 'vs/workbench/services/search/common/searchExtTypes';
export class TextSearchManager {
@@ -22,7 +22,7 @@ export class TextSearchManager {
private isLimitHit: boolean;
private resultCount = 0;
constructor(private query: ITextQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs = extfs) {
constructor(private query: ITextQuery, private provider: TextSearchProvider, private _pfs: typeof pfs = pfs) {
}
search(onProgress: (matches: IFileMatch[]) => void, token: CancellationToken): Promise<ISearchCompleteStats> {
@@ -34,7 +34,7 @@ export class TextSearchManager {
this.collector = new TextSearchResultsCollector(onProgress);
let isCanceled = false;
const onResult = (result: vscode.TextSearchResult, folderIdx: number) => {
const onResult = (result: TextSearchResult, folderIdx: number) => {
if (isCanceled) {
return;
}
@@ -79,14 +79,14 @@ export class TextSearchManager {
});
}
private resultSize(result: vscode.TextSearchResult): number {
const match = <vscode.TextSearchMatch>result;
private resultSize(result: TextSearchResult): number {
const match = <TextSearchMatch>result;
return Array.isArray(match.ranges) ?
match.ranges.length :
1;
}
private trimResultToSize(result: vscode.TextSearchMatch, size: number): vscode.TextSearchMatch {
private trimResultToSize(result: TextSearchMatch, size: number): TextSearchMatch {
const rangesArr = Array.isArray(result.ranges) ? result.ranges : [result.ranges];
const matchesArr = Array.isArray(result.preview.matches) ? result.preview.matches : [result.preview.matches];
@@ -100,11 +100,11 @@ export class TextSearchManager {
};
}
private searchInFolder(folderQuery: IFolderQuery<URI>, onResult: (result: vscode.TextSearchResult) => void, token: CancellationToken): Promise<vscode.TextSearchComplete | null | undefined> {
private searchInFolder(folderQuery: IFolderQuery<URI>, onResult: (result: TextSearchResult) => void, token: CancellationToken): Promise<TextSearchComplete | null | undefined> {
const queryTester = new QueryGlobTester(this.query, folderQuery);
const testingPs: Promise<void>[] = [];
const progress = {
report: (result: vscode.TextSearchResult) => {
report: (result: TextSearchResult) => {
if (!this.validateProviderResult(result)) {
return;
}
@@ -135,7 +135,7 @@ export class TextSearchManager {
});
}
private validateProviderResult(result: vscode.TextSearchResult): boolean {
private validateProviderResult(result: TextSearchResult): boolean {
if (extensionResultIsMatch(result)) {
if (Array.isArray(result.ranges)) {
if (!Array.isArray(result.preview.matches)) {
@@ -143,7 +143,7 @@ export class TextSearchManager {
return false;
}
if ((<vscode.Range[]>result.preview.matches).length !== result.ranges.length) {
if ((<Range[]>result.preview.matches).length !== result.ranges.length) {
console.warn('INVALID - A text search provider match\'s`ranges` and`matches` properties must have the same length.');
return false;
}
@@ -159,22 +159,14 @@ export class TextSearchManager {
}
private readdir(dirname: string): Promise<string[]> {
return new Promise((resolve, reject) => {
this._extfs.readdir(dirname, (err, files) => {
if (err) {
return reject(err);
}
resolve(files);
});
});
return this._pfs.readdir(dirname);
}
private getSearchOptionsForFolder(fq: IFolderQuery<URI>): vscode.TextSearchOptions {
private getSearchOptionsForFolder(fq: IFolderQuery<URI>): TextSearchOptions {
const includes = resolvePatternsForProvider(this.query.includePattern, fq.includePattern);
const excludes = resolvePatternsForProvider(this.query.excludePattern, fq.excludePattern);
const options = <vscode.TextSearchOptions>{
const options = <TextSearchOptions>{
folder: URI.from(fq.folder),
excludes,
includes,
@@ -193,8 +185,8 @@ export class TextSearchManager {
}
}
function patternInfoToQuery(patternInfo: IPatternInfo): vscode.TextSearchQuery {
return <vscode.TextSearchQuery>{
function patternInfoToQuery(patternInfo: IPatternInfo): TextSearchQuery {
return <TextSearchQuery>{
isCaseSensitive: patternInfo.isCaseSensitive || false,
isRegExp: patternInfo.isRegExp || false,
isWordMatch: patternInfo.isWordMatch || false,
@@ -214,7 +206,7 @@ export class TextSearchResultsCollector {
this._batchedCollector = new BatchedCollector<IFileMatch>(512, items => this.sendItems(items));
}
add(data: vscode.TextSearchResult, folderIdx: number): void {
add(data: TextSearchResult, folderIdx: number): void {
// Collects TextSearchResults into IInternalFileMatches and collates using BatchedCollector.
// This is efficient for ripgrep which sends results back one file at a time. It wouldn't be efficient for other search
// providers that send results in random order. We could do this step afterwards instead.
@@ -251,8 +243,8 @@ export class TextSearchResultsCollector {
}
}
function extensionResultToFrontendResult(data: vscode.TextSearchResult): ITextSearchResult {
// Warning: result from RipgrepTextSearchEH has fake vscode.Range. Don't depend on any other props beyond these...
function extensionResultToFrontendResult(data: TextSearchResult): ITextSearchResult {
// Warning: result from RipgrepTextSearchEH has fake Range. Don't depend on any other props beyond these...
if (extensionResultIsMatch(data)) {
return <ITextSearchMatch>{
preview: {
@@ -279,8 +271,8 @@ function extensionResultToFrontendResult(data: vscode.TextSearchResult): ITextSe
}
}
export function extensionResultIsMatch(data: vscode.TextSearchResult): data is vscode.TextSearchMatch {
return !!(<vscode.TextSearchMatch>data).preview;
export function extensionResultIsMatch(data: TextSearchResult): data is TextSearchMatch {
return !!(<TextSearchMatch>data).preview;
}
/**