Allow searching with a ./ path in a workspace folder that has a slash in its name

Fix #99512
This commit is contained in:
Rob Lourens 2021-05-03 18:59:19 -07:00
parent bc04b1c04b
commit f19a2c969b
2 changed files with 69 additions and 22 deletions

View file

@ -12,7 +12,7 @@ import { Schemas } from 'vs/base/common/network';
import * as path from 'vs/base/common/path';
import { isEqual, basename, relativePath, isAbsolutePath } from 'vs/base/common/resources';
import * as strings from 'vs/base/common/strings';
import { assertIsDefined } from 'vs/base/common/types';
import { assertIsDefined, isDefined } from 'vs/base/common/types';
import { URI, URI as uri } from 'vs/base/common/uri';
import { isMultilineRegexSource } from 'vs/editor/common/model/textModelSearch';
import * as nls from 'vs/nls';
@ -415,29 +415,33 @@ export class QueryBuilder {
} else if (searchPath === './' || searchPath === '.\\') {
return []; // ./ or ./**/foo makes sense for single-folder but not multi-folder workspaces
} else {
const relativeSearchPathMatch = searchPath.match(/\.[\/\\]([^\/\\]+)(?:[\/\\](.+))?/);
if (relativeSearchPathMatch) {
const searchPathRoot = relativeSearchPathMatch[1];
const matchingRoots = this.workspaceContextService.getWorkspace().folders.filter(folder => folder.name === searchPathRoot);
if (matchingRoots.length) {
return matchingRoots.map(root => {
const patternMatch = relativeSearchPathMatch[2];
return {
searchPath: root.uri,
pattern: patternMatch && normalizeGlobPattern(patternMatch)
};
});
} else {
// No root folder with name
const searchPathNotFoundError = nls.localize('search.noWorkspaceWithName', "Workspace folder does not exist: {0}", searchPathRoot);
throw new Error(searchPathNotFoundError);
}
const searchPathWithoutDotSlash = searchPath.replace(/^\.[\/\\]/, '');
const folders = this.workspaceContextService.getWorkspace().folders;
const folderMatches = folders.map(folder => {
const match = searchPathWithoutDotSlash.match(new RegExp(`^${folder.name}(?:/(.*))?`));
return match ? {
match,
folder
} : null;
}).filter(isDefined);
if (folderMatches.length) {
return folderMatches.map(match => {
const patternMatch = match.match[1];
return {
searchPath: match.folder.uri,
pattern: patternMatch && normalizeGlobPattern(patternMatch)
};
});
} else {
// Malformed ./ search path, ignore
const probableWorkspaceFolderNameMatch = searchPath.match(/\.[\/\\](.+)[\/\\]?/);
const probableWorkspaceFolderName = probableWorkspaceFolderNameMatch ? probableWorkspaceFolderNameMatch[1] : searchPath;
// No root folder with name
const searchPathNotFoundError = nls.localize('search.noWorkspaceWithName', "Workspace folder does not exist: {0}", probableWorkspaceFolderName);
throw new Error(searchPathNotFoundError);
}
}
return [];
}
private resolveOneSearchPathPattern(oneExpandedResult: IOneSearchPathPattern, globPortion?: string): IOneSearchPathPattern[] {

View file

@ -573,8 +573,15 @@ suite('QueryBuilder', () => {
});
function testIncludes(includePattern: string, expectedResult: ISearchPathsInfo): void {
let actual: ISearchPathsInfo;
try {
actual = queryBuilder.parseSearchPaths(includePattern);
} catch (_) {
actual = { searchPaths: [] };
}
assertEqualSearchPathResults(
queryBuilder.parseSearchPaths(includePattern),
actual,
expectedResult,
includePattern);
}
@ -799,6 +806,42 @@ suite('QueryBuilder', () => {
cases.forEach(testIncludesDataItem);
});
test('folder with slash in the name', () => {
const ROOT_2 = '/project/root2';
const ROOT_2_URI = getUri(ROOT_2);
const ROOT_1_FOLDERNAME = 'folder/one';
const ROOT_2_FOLDERNAME = 'folder/two';
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath, name: ROOT_1_FOLDERNAME }, { path: ROOT_2_URI.fsPath, name: ROOT_2_FOLDERNAME }], WS_CONFIG_PATH, extUriBiasedIgnorePathCase);
mockWorkspace.configuration = uri.file(fixPath('config'));
const cases: [string, ISearchPathsInfo][] = [
[
'./folder/one',
{
searchPaths: [{
searchPath: ROOT_1_URI
}]
}
],
[
'./folder/two/foo/',
{
searchPaths: [{
searchPath: ROOT_2_URI,
pattern: patternsToIExpression('foo', 'foo/**')
}]
}
],
[
'./folder',
{
searchPaths: []
}
]
];
cases.forEach(testIncludesDataItem);
});
test('relative includes w/multiple ambiguous root folders', () => {
const ROOT_2 = '/project/rootB';
const ROOT_3 = '/otherproject/rootB';