diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx b/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx index b2e414c094765..837363f702e11 100644 --- a/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx +++ b/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx @@ -290,7 +290,8 @@ export class SearchInWorkspaceResultTreeWidget extends TreeWidget { } return workspaceRootUri.toString().concat(pattern.replace('./', '/')); } - return globalPrefix.concat(pattern); + + return pattern.startsWith('/') ? '**'.concat(pattern) : globalPrefix.concat(pattern); } /** @@ -515,16 +516,16 @@ export class SearchInWorkspaceResultTreeWidget extends TreeWidget { matches.forEach(m => this.appendToResultTree(m)); - // Exclude files already covered by searching open editors. - this.editorManager.all.forEach(e => { - const rootUri = this.workspaceService.getWorkspaceRootUri(e.editor.uri); - if (rootUri) { - // Exclude pattern beginning with './' works after the fix of #8469. - const { name, path } = this.filenameAndPath(e.editor.uri.toString(), rootUri.toString()); - const excludePath: string = path === '' ? './' + name : path + '/' + name; - searchOptions.exclude = (searchOptions.exclude) ? searchOptions.exclude.concat(excludePath) : [excludePath]; - } - }); + // TODO: The following optimization is disabled until issue #9319 is resolved + // // Exclude files already covered by searching open editors. + // this.editorManager.all.forEach(e => { + // const rootUri = this.workspaceService.getWorkspaceRootUri(e.editor.uri); + // if (rootUri) { + // const { name, path } = this.filenameAndPath(rootUri.toString(), e.editor.uri.toString()); + // const excludePath: string = path === '' ? './' + name : path + '/' + name; + // searchOptions.exclude = (searchOptions.exclude) ? searchOptions.exclude.concat(excludePath) : [excludePath]; + // } + // }); // Reduce `maxResults` due to editor results. if (searchOptions.maxResults) { diff --git a/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.ts b/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.ts index e15f1ab0823ae..fdc78e3216140 100644 --- a/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.ts +++ b/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.ts @@ -89,9 +89,6 @@ export class RipgrepSearchInWorkspaceServer implements SearchInWorkspaceServer { @inject(RgPath) protected readonly rgPath: string; - private static readonly GLOB_SUB_DIRECTORY_PATTERN = '**/'; - private static readonly GLOB_COMMAND_ARGUMENT = '--glob='; - constructor( @inject(ILogger) protected readonly logger: ILogger, @inject(RawProcessFactory) protected readonly rawProcessFactory: RawProcessFactory, @@ -148,11 +145,15 @@ export class RipgrepSearchInWorkspaceServer implements SearchInWorkspaceServer { protected pathToGlobs(inputPath: string, rootFolder: string, exclude: boolean): string[] { const prefixPattern = (inputPattern: string): string => { + const globalCommandArgument = '--glob='; const excludeChar = exclude ? '!' : ''; - const updatedPattern = inputPattern.startsWith(RipgrepSearchInWorkspaceServer.GLOB_SUB_DIRECTORY_PATTERN) ? - inputPattern : `${RipgrepSearchInWorkspaceServer.GLOB_SUB_DIRECTORY_PATTERN}${inputPattern}`; + const subDirGlobPattern = '**/'; + + const subDirGlobPrefix = inputPattern.startsWith('/') ? '**' : subDirGlobPattern; + const updatedPattern = inputPattern.startsWith(subDirGlobPattern) ? + inputPattern : `${subDirGlobPrefix}${inputPattern}`; - return `${RipgrepSearchInWorkspaceServer.GLOB_COMMAND_ARGUMENT}${excludeChar}${updatedPattern}`; + return `${globalCommandArgument}${excludeChar}${updatedPattern}`; }; const toGlobPattern = pathToGlobPattern({ @@ -180,8 +181,8 @@ export class RipgrepSearchInWorkspaceServer implements SearchInWorkspaceServer { // we'll use to parse the lines. const searchId = this.nextSearchId++; const rootPaths = rootUris.map(root => FileUri.fsPath(root)); - const rgArgs = this.getArgs(rootPaths, opts); const searchPaths: string[] = this.resolveSearchPaths(rootPaths, opts); + const rgArgs = this.getArgs(searchPaths, opts); // if we use matchWholeWord we use regExp internally, // so, we need to escape regexp characters if we actually not set regexp true in UI. if (opts && opts.matchWholeWord && !opts.useRegExp) { @@ -344,18 +345,50 @@ export class RipgrepSearchInWorkspaceServer implements SearchInWorkspaceServer { } const searchPaths: string[] = []; + const patternPaths: string[] = []; opts.include.forEach(pattern => { rootPaths.forEach(root => { - const searchPath = path.join(root, pattern); + let searchPath = path.join(root, pattern); + let isPatternAValidPath = false; if (fs.existsSync(searchPath)) { + isPatternAValidPath = true; + } else if (fs.existsSync(pattern)) { + searchPath = pattern; + isPatternAValidPath = true; + } else if (pattern.endsWith('**')) { + const patternPath = this.includeSubDirectoriesToPath(pattern); + if (fs.existsSync(patternPath)) { + searchPath = patternPath; + isPatternAValidPath = true; + } + } + + if (isPatternAValidPath) { searchPaths.push(searchPath); + patternPaths.push(pattern); } }); }); + // Exclude include file patters that were successfully translated to search paths + opts.include = opts.include.filter(item => !patternPaths.includes(item)); return searchPaths.length > 0 ? searchPaths : rootPaths; } + protected includeSubDirectoriesToPath(pattern: string): string { + const parsedObj = path.parse(pattern); + + let resultingPath = pattern; + if (parsedObj.base === '**') { + parsedObj.base = ''; + parsedObj.name = ''; + const transformed = resultingPath = path.format(parsedObj); + resultingPath = transformed.endsWith(path.sep) ? transformed.slice(0, -1) : transformed; + } + + return resultingPath; + } + /** * Returns the root folder uri that a file belongs to. * In case that a file belongs to more than one root folders, returns the root folder that is closest to the file.