src/vs/workbench/services/search/common/queryBuilder.ts

745 LOC · 212 covered · 533 uncovered · 30 ranges · 43 concepts · 3 introducers · 36 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filesrc/vs/workbench/test/common/workbenchTestServices.ts · 908 LOCcommon/workbenchTestServ…extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1, extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 3 introduced LOCextHostMcp.ts ×1extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 1 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 6 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 4 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×2 · 4 introduced LOCextHostMcp.ts ×2extHostMcp.ts ×1 · 1 introduced LOCextHostMcp.ts ×1extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×4 · 6 introduced LOCextHostMcp.ts ×4extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1, extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 6 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×4 · 13 introduced LOCextHostMcp.ts ×4extHostMcpNode.ts ×8 · 45 introduced LOCextHostMcpNode.ts ×8extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×5 · 25 introduced LOCextHostMcp.ts ×5extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×3 · 23 introduced LOCextHostMcp.ts ×3extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×8 · 60 introduced LOCextHostMcp.ts ×8extHostMcp.ts ×43 · 874 introduced LOCextHostMcp.ts ×43extensionHostMain.ts ×1 · 2 introduced LOCextensionHostMain.ts ×1reset prepareStackTrace-callback|occurrence=1, extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1 · 0 introduced LOCreset prepareStackTrace-…extensionHostMain.ts ×5 · 26 introduced LOCextensionHostMain.ts ×5f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1 · 0 introduced LOCf0f2e182082072efdaf0f8e1…extensionHostMain.ts ×1 · 3 introduced LOCextensionHostMain.ts ×1extensionHostMain.ts ×1 · 3 introduced LOCextensionHostMain.ts ×1extensionHostMain.ts ×11 · 164 introduced LOCextensionHostMain.ts ×11extHostExtensionService.ts ×64 · 1246 introduced LOCextHostExtensionService.…queryBuilder.test|title=QueryBuilderCommon escapeGlobPattern properly escapes square brackets for literal matching|occurrence=1 · 0 introduced LOCqueryBuilder.test|title=…queryBuilder.test|title=QueryBuilderCommon resolveResourcesForSearchIncludes escapes paths with special characters|occurrence=1 · 0 introduced LOCqueryBuilder.test|title=…queryBuilder.ts ×2 · 25 introduced LOCqueryBuilder.ts ×2queryBuilder.ts ×1 · 2 introduced LOCqueryBuilder.ts ×1queryBuilder.ts ×27 · 187 introduced LOCqueryBuilder.ts ×27extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should allow undefined scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should allow undefined scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when no WWW-Authenticate header and scopes are already undefined|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when no WWW-Authenticate header and scopes are already undefined|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle empty scope string in WWW-Authenticate header|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle empty scope string in WWW-Authenticate header|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle malformed WWW-Authenticate header gracefully|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle malformed WWW-Authenticate header gracefully|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use resource_metadata challenge URL from WWW-Authenticate header|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use resource_metadata challenge URL from WWW-Authenticate header|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1extHostMcp.test|title=Ex…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 basics|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 basics|occurrence=1extensionHostMain.test|t…f0f2e182082072efdaf0f8e1537d2cce Never restored, separate operations|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Never restored, separate operations|occurrence=1f0f2e182082072efdaf0f8e1…f0f2e182082072efdaf0f8e1537d2cce Restored, separate operations|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Restored, separate operations|occurrence=1f0f2e182082072efdaf0f8e1…f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1f0f2e182082072efdaf0f8e1…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1extensionHostMain.test|t…reset prepareStackTrace-callback|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 set/reset prepareStackTrace-callback|occurrence=1reset prepareStackTrace-…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 wrap prepareStackTrace-callback|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 wrap prepareStackTrace-callback|occurrence=1extensionHostMain.test|t…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg does not add stray ^ to argument values|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg does not add stray ^ to argument values|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg doubles embedded double quotes (cmd.exe convention)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg doubles embedded double quotes (cmd.exe convention)|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg neutralizes cmd.exe metacharacters inside quotes (CVE-2024-27980)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg neutralizes cmd.exe metacharacters inside quotes (CVE-2024-27980)|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with parentheses without injecting ^|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with parentheses without injecting ^|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with spaces|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with spaces|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg wraps simple values in double quotes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg wraps simple values in double quotes|occurrence=1extHostMcpNode.test|titl…queryBuilder.test|title=QueryBuilderCommon escapeGlobPattern properly escapes square brackets for literal matching|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/search/test/common/queryBuilder.test|title=QueryBuilderCommon escapeGlobPattern properly escapes square brackets for literal matching|occurrence=1queryBuilder.test|title=…queryBuilder.test|title=QueryBuilderCommon resolveResourcesForSearchIncludes escapes paths with special characters|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/search/test/common/queryBuilder.test|title=QueryBuilderCommon resolveResourcesForSearchIncludes escapes paths with special characters|occurrence=1queryBuilder.test|title=…queryBuilder.test|title=QueryBuilderCommon resolveResourcesForSearchIncludes passes through paths without special glob characters|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/search/test/common/queryBuilder.test|title=QueryBuilderCommon resolveResourcesForSearchIncludes passes through paths without special glob characters|occurrence=1queryBuilder.test|title=…Focused file · src/vs/workbench/services/search/common/queryBuilder.ts · 745 LOCcommon/queryBuilder.ts

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- queryBuilder.ts ×27
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import * as arrays from '../../../../base/common/arrays.js';
7 > import * as collections from '../../../../base/common/collections.js';
8 > import * as glob from '../../../../base/common/glob.js';
9 > import { untildify } from '../../../../base/common/labels.js';
10 > import { ResourceMap } from '../../../../base/common/map.js';
11 > import { Schemas } from '../../../../base/common/network.js';
12 > import * as path from '../../../../base/common/path.js';
13 > import { isEqual, basename, relativePath, isAbsolutePath } from '../../../../base/common/resources.js';
14 > import * as strings from '../../../../base/common/strings.js';
15 > import { assertReturnsDefined, isDefined } from '../../../../base/common/types.js';
16 > import { URI, URI as uri, UriComponents } from '../../../../base/common/uri.js';
17 > import { isMultilineRegexSource } from '../../../../editor/common/model/textModelSearch.js';
18 > import * as nls from '../../../../nls.js';
19 > import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
20 > import { ILogService } from '../../../../platform/log/common/log.js';
21 > import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
22 > import { IWorkspaceContextService, IWorkspaceFolderData, toWorkspaceFolder, WorkbenchState } from '../../../../platform/workspace/common/workspace.js';
23 > import { IEditorGroupsService } from '../../editor/common/editorGroupsService.js';
24 > import { IPathService } from '../../path/common/pathService.js';
25 > import { ExcludeGlobPattern, getExcludes, IAITextQuery, ICommonQueryProps, IFileQuery, IFolderQuery, IPatternInfo, ISearchConfiguration, ITextQuery, ITextSearchPreviewOptions, pathIncludedInQuery, QueryType } from './search.js';
26 > import { GlobPattern } from './searchExtTypes.js';
27 >
28 > /**
29 > * One folder to search and a glob expression that should be applied.
30 > */
31 > interface IOneSearchPathPattern {
32 > searchPath: uri;
33 > pattern?: string;
34 > }
35 >
36 > /**
37 > * One folder to search and a set of glob expressions that should be applied.
38 > */
39 > export interface ISearchPathPattern {
40 > searchPath: uri;
41 > pattern?: glob.IExpression;
42 > }
43 >
44 > type ISearchPathPatternBuilder = string | string[];
45 >
46 > export interface ISearchPatternBuilder<U extends UriComponents> {
47 > uri?: U;
48 > pattern: ISearchPathPatternBuilder;
49 > }
50 >
51 > export function isISearchPatternBuilder<U extends UriComponents>(object: ISearchPatternBuilder<U> | ISearchPathPatternBuilder): object is ISearchPatternBuilder<U> {
52 return (typeof object === 'object' && 'uri' in object && 'pattern' in object);
53 }
55 > export function globPatternToISearchPatternBuilder(globPattern: GlobPattern): ISearchPatternBuilder<URI> {
56
57 if (typeof globPattern === 'string') {
58 return {
59 pattern: globPattern
60 };
61 }
62
63 return {
64 pattern: globPattern.pattern,
65 uri: globPattern.baseUri
66 };
67 }
69 > /**
70 > * A set of search paths and a set of glob expressions that should be applied.
71 > */
72 > export interface ISearchPathsInfo {
73 > searchPaths?: ISearchPathPattern[];
74 > pattern?: glob.IExpression;
75 > }
76 >
77 > interface ICommonQueryBuilderOptions<U extends UriComponents = URI> {
78 > _reason?: string;
79 > excludePattern?: ISearchPatternBuilder<U>[];
80 > includePattern?: ISearchPathPatternBuilder;
81 > extraFileResources?: U[];
82 >
83 > /** Parse the special ./ syntax supported by the searchview, and expand foo to ** /foo */
84 > expandPatterns?: boolean;
85 >
86 > maxResults?: number;
87 > maxFileSize?: number;
88 > disregardIgnoreFiles?: boolean;
89 > disregardGlobalIgnoreFiles?: boolean;
90 > disregardParentIgnoreFiles?: boolean;
91 > disregardExcludeSettings?: boolean;
92 > disregardSearchExcludeSettings?: boolean;
93 > ignoreSymlinks?: boolean;
94 > ignoreGlobCase?: boolean;
95 > onlyOpenEditors?: boolean;
96 > changedFileUris?: URI[];
97 > onlyFileScheme?: boolean;
98 > }
99 >
100 > export interface IFileQueryBuilderOptions<U extends UriComponents = URI> extends ICommonQueryBuilderOptions<U> {
101 > filePattern?: string;
102 > exists?: boolean;
103 > sortByScore?: boolean;
104 > cacheKey?: string;
105 > shouldGlobSearch?: boolean;
106 > }
107 >
108 > export interface ITextQueryBuilderOptions<U extends UriComponents = URI> extends ICommonQueryBuilderOptions<U> {
109 > previewOptions?: ITextSearchPreviewOptions;
110 > fileEncoding?: string;
111 > surroundingContext?: number;
112 > isSmartCase?: boolean;
113 > notebookSearchConfig?: {
114 > includeMarkupInput: boolean;
115 > includeMarkupPreview: boolean;
116 > includeCodeInput: boolean;
117 > includeOutput: boolean;
118 > };
119 > }
120 >
121 > export class QueryBuilder {
122 >
123 > constructor(
124 @IConfigurationService private readonly configurationService: IConfigurationService,
125 @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
126 @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService,
127 @ILogService private readonly logService: ILogService,
128 @IPathService private readonly pathService: IPathService,
129 @IUriIdentityService private readonly uriIdentityService: IUriIdentityService
130 ) {
131 }
133 > aiText(contentPattern: string, folderResources?: uri[], options: ITextQueryBuilderOptions = {}): IAITextQuery {
134 const commonQuery = this.commonQuery(folderResources?.map(toWorkspaceFolder), options);
135 return {
136 ...commonQuery,
137 type: QueryType.aiText,
138 contentPattern,
139 };
140 }
142 > text(contentPattern: IPatternInfo, folderResources?: uri[], options: ITextQueryBuilderOptions = {}): ITextQuery {
143 contentPattern = this.getContentPattern(contentPattern, options);
144
145 const commonQuery = this.commonQuery(folderResources?.map(toWorkspaceFolder), options);
146 return {
147 ...commonQuery,
148 type: QueryType.Text,
149 contentPattern,
150 previewOptions: options.previewOptions,
151 maxFileSize: options.maxFileSize,
152 surroundingContext: options.surroundingContext,
153 userDisabledExcludesAndIgnoreFiles: options.disregardExcludeSettings && options.disregardIgnoreFiles,
154
155 };
156 }
158 > /**
159 > * Adjusts input pattern for config
160 > */
161 > private getContentPattern(inputPattern: IPatternInfo, options: ITextQueryBuilderOptions): IPatternInfo {
162 const searchConfig = this.configurationService.getValue<ISearchConfiguration>();
163
164 if (inputPattern.isRegExp) {
165 inputPattern.pattern = inputPattern.pattern.replace(/\r?\n/g, '\\n');
166 }
167
168 const newPattern = {
169 ...inputPattern,
170 wordSeparators: searchConfig.editor.wordSeparators
171 };
172
173 if (this.isCaseSensitive(inputPattern, options)) {
174 newPattern.isCaseSensitive = true;
175 }
176
177 if (this.isMultiline(inputPattern)) {
178 newPattern.isMultiline = true;
179 }
180
181 if (options.notebookSearchConfig?.includeMarkupInput) {
182 if (!newPattern.notebookInfo) {
183 newPattern.notebookInfo = {};
184 }
185 newPattern.notebookInfo.isInNotebookMarkdownInput = options.notebookSearchConfig.includeMarkupInput;
186 }
187
188 if (options.notebookSearchConfig?.includeMarkupPreview) {
189 if (!newPattern.notebookInfo) {
190 newPattern.notebookInfo = {};
191 }
192 newPattern.notebookInfo.isInNotebookMarkdownPreview = options.notebookSearchConfig.includeMarkupPreview;
193 }
194
195 if (options.notebookSearchConfig?.includeCodeInput) {
196 if (!newPattern.notebookInfo) {
197 newPattern.notebookInfo = {};
198 }
199 newPattern.notebookInfo.isInNotebookCellInput = options.notebookSearchConfig.includeCodeInput;
200 }
201
202 if (options.notebookSearchConfig?.includeOutput) {
203 if (!newPattern.notebookInfo) {
204 newPattern.notebookInfo = {};
205 }
206 newPattern.notebookInfo.isInNotebookCellOutput = options.notebookSearchConfig.includeOutput;
207 }
208
209 return newPattern;
210 }
212 > file(folders: (IWorkspaceFolderData | URI)[], options: IFileQueryBuilderOptions = {}): IFileQuery {
213 const commonQuery = this.commonQuery(folders, options);
214 return {
215 ...commonQuery,
216 type: QueryType.File,
217 filePattern: options.filePattern
218 ? options.filePattern.trim()
219 : options.filePattern,
220 exists: options.exists,
221 sortByScore: options.sortByScore,
222 cacheKey: options.cacheKey,
223 shouldGlobMatchFilePattern: options.shouldGlobSearch
224 };
225 }
227 > private handleIncludeExclude(pattern: string | string[] | undefined, expandPatterns: boolean | undefined): ISearchPathsInfo {
228 if (!pattern) {
229 return {};
230 }
231
232 if (Array.isArray(pattern)) {
233 pattern = pattern.filter(p => p.length > 0).map(normalizeSlashes);
234 if (!pattern.length) {
235 return {};
236 }
237 } else {
238 pattern = normalizeSlashes(pattern);
239 }
240 return expandPatterns
241 ? this.parseSearchPaths(pattern)
242 : { pattern: patternListToIExpression(...(Array.isArray(pattern) ? pattern : [pattern])) };
243 }
245 > private commonQuery(folderResources: (IWorkspaceFolderData | URI)[] = [], options: ICommonQueryBuilderOptions = {}): ICommonQueryProps<uri> {
246
247 let excludePatterns: string | string[] | undefined = Array.isArray(options.excludePattern) ? options.excludePattern.map(p => p.pattern).flat() : options.excludePattern;
248 excludePatterns = excludePatterns?.length === 1 ? excludePatterns[0] : excludePatterns;
249 const includeSearchPathsInfo: ISearchPathsInfo = this.handleIncludeExclude(options.includePattern, options.expandPatterns);
250 const excludeSearchPathsInfo: ISearchPathsInfo = this.handleIncludeExclude(excludePatterns, options.expandPatterns);
251
252 // Build folderQueries from searchPaths, if given, otherwise folderResources
253 const includeFolderName = folderResources.length > 1;
254 const folderQueries = (includeSearchPathsInfo.searchPaths && includeSearchPathsInfo.searchPaths.length ?
255 includeSearchPathsInfo.searchPaths.map(searchPath => this.getFolderQueryForSearchPath(searchPath, options, excludeSearchPathsInfo)) :
256 folderResources.map(folder => this.getFolderQueryForRoot(folder, options, excludeSearchPathsInfo, includeFolderName)))
257 .filter(query => !!query) as IFolderQuery[];
258
259 const queryProps: ICommonQueryProps<uri> = {
260 _reason: options._reason,
261 folderQueries,
262 usingSearchPaths: !!(includeSearchPathsInfo.searchPaths && includeSearchPathsInfo.searchPaths.length),
263 extraFileResources: options.extraFileResources,
264
265 excludePattern: excludeSearchPathsInfo.pattern,
266 includePattern: includeSearchPathsInfo.pattern,
267 ignoreGlobCase: options.ignoreGlobCase,
268 onlyOpenEditors: options.onlyOpenEditors,
269 maxResults: options.maxResults,
270 onlyFileScheme: options.onlyFileScheme
271 };
272
273 if (options.onlyOpenEditors) {
274 const openEditors = arrays.coalesce(this.editorGroupsService.groups.flatMap(group => group.editors.map(editor => editor.resource)));
275 this.logService.trace('QueryBuilder#commonQuery - openEditor URIs', JSON.stringify(openEditors));
276 const openEditorsInQuery = openEditors.filter(editor => pathIncludedInQuery(queryProps, editor.fsPath));
277 const openEditorsQueryProps = this.commonQueryFromFileList(openEditorsInQuery);
278 this.logService.trace('QueryBuilder#commonQuery - openEditor Query', JSON.stringify(openEditorsQueryProps));
279 return { ...queryProps, ...openEditorsQueryProps };
280 }
281
282 if (options.changedFileUris !== undefined) {
283 const changedFilesInQuery = options.changedFileUris.filter(uri => pathIncludedInQuery(queryProps, uri.fsPath));
284 const changedFilesQueryProps = this.commonQueryFromFileList(changedFilesInQuery);
285 this.logService.trace('QueryBuilder#commonQuery - changedFile Query', JSON.stringify(changedFilesQueryProps));
286 return { ...queryProps, ...changedFilesQueryProps };
287 }
288
289 // Filter extraFileResources against global include/exclude patterns - they are already expected to not belong to a workspace
290 const extraFileResources = options.extraFileResources && options.extraFileResources.filter(extraFile => pathIncludedInQuery(queryProps, extraFile.fsPath));
291 queryProps.extraFileResources = extraFileResources && extraFileResources.length ? extraFileResources : undefined;
292
293 return queryProps;
294 }
296 > private commonQueryFromFileList(files: URI[]): ICommonQueryProps<URI> {
297 const folderQueries: IFolderQuery[] = [];
298 const foldersToSearch: ResourceMap<IFolderQuery> = new ResourceMap();
299 const includePattern: glob.IExpression = {};
300 let hasIncludedFile = false;
301 files.forEach(file => {
302 if (file.scheme === Schemas.walkThrough) { return; }
303
304 const providerExists = isAbsolutePath(file);
305 // Special case userdata as we don't have a search provider for it, but it can be searched.
306 if (providerExists) {
307
308 const searchRoot = this.workspaceContextService.getWorkspaceFolder(file)?.uri ?? this.uriIdentityService.extUri.dirname(file);
309
310 let folderQuery = foldersToSearch.get(searchRoot);
311 if (!folderQuery) {
312 hasIncludedFile = true;
313 folderQuery = { folder: searchRoot, includePattern: {} };
314 folderQueries.push(folderQuery);
315 foldersToSearch.set(searchRoot, folderQuery);
316 }
317
318 const relPath = path.relative(searchRoot.fsPath, file.fsPath);
319 assertReturnsDefined(folderQuery.includePattern)[escapeGlobPattern(relPath.replace(/\\/g, '/'))] = true;
320 } else {
321 if (file.fsPath) {
322 hasIncludedFile = true;
323 includePattern[escapeGlobPattern(file.fsPath)] = true;
324 }
325 }
326 });
327
328 return {
329 folderQueries,
330 includePattern,
331 usingSearchPaths: true,
332 excludePattern: hasIncludedFile ? undefined : { '**/*': true }
333 };
334 }
336 > /**
337 > * Resolve isCaseSensitive flag based on the query and the isSmartCase flag, for search providers that don't support smart case natively.
338 > */
339 > private isCaseSensitive(contentPattern: IPatternInfo, options: ITextQueryBuilderOptions): boolean {
340 if (options.isSmartCase) {
341 if (contentPattern.isRegExp) {
342 // Consider it case sensitive if it contains an unescaped capital letter
343 if (strings.containsUppercaseCharacter(contentPattern.pattern, true)) {
344 return true;
345 }
346 } else if (strings.containsUppercaseCharacter(contentPattern.pattern)) {
347 return true;
348 }
349 }
350
351 return !!contentPattern.isCaseSensitive;
352 }
354 > private isMultiline(contentPattern: IPatternInfo): boolean {
355 if (contentPattern.isMultiline) {
356 return true;
357 }
358
359 if (contentPattern.isRegExp && isMultilineRegexSource(contentPattern.pattern)) {
360 return true;
361 }
362
363 if (contentPattern.pattern.indexOf('\n') >= 0) {
364 return true;
365 }
366
367 return !!contentPattern.isMultiline;
368 }
370 > /**
371 > * Take the includePattern as seen in the search viewlet, and split into components that look like searchPaths, and
372 > * glob patterns. Glob patterns are expanded from 'foo/bar' to '{foo/bar/**, **\/foo/bar}.
373 > *
374 > * Public for test.
375 > */
376 > parseSearchPaths(pattern: string | string[]): ISearchPathsInfo {
377 const isSearchPath = (segment: string) => {
378 // A segment is a search path if it is an absolute path or starts with ./, ../, .\, or ..\
379 return path.isAbsolute(segment) || /^\.\.?([\/\\]|$)/.test(segment);
380 };
381
382 const patterns = Array.isArray(pattern) ? pattern : splitGlobPattern(pattern);
383 const segments = patterns
384 .map(segment => {
385 const userHome = this.pathService.resolvedUserHome;
386 if (userHome) {
387 return untildify(segment, userHome.scheme === Schemas.file ? userHome.fsPath : userHome.path);
388 }
389
390 return segment;
391 });
392 const groups = collections.groupBy(segments,
393 segment => isSearchPath(segment) ? 'searchPaths' : 'exprSegments');
394
395 const expandedExprSegments = (groups.exprSegments || [])
396 .map(s => strings.rtrim(s, '/'))
397 .map(s => strings.rtrim(s, '\\'))
398 .map(p => {
399 if (p[0] === '.') {
400 p = '*' + p; // convert ".js" to "*.js"
401 }
402
403 return expandGlobalGlob(p);
404 });
405
406 const result: ISearchPathsInfo = {};
407 const searchPaths = this.expandSearchPathPatterns(groups.searchPaths || []);
408 if (searchPaths && searchPaths.length) {
409 result.searchPaths = searchPaths;
410 }
411
412 const exprSegments = expandedExprSegments.flat();
413 const includePattern = patternListToIExpression(...exprSegments);
414 if (includePattern) {
415 result.pattern = includePattern;
416 }
417
418 return result;
419 }
421 > private getExcludesForFolder(folderConfig: ISearchConfiguration, options: ICommonQueryBuilderOptions): glob.IExpression | undefined {
422 return options.disregardExcludeSettings ?
423 undefined :
424 getExcludes(folderConfig, !options.disregardSearchExcludeSettings);
425 }
427 > /**
428 > * Split search paths (./ or ../ or absolute paths in the includePatterns) into absolute paths and globs applied to those paths
429 > */
430 > private expandSearchPathPatterns(searchPaths: string[]): ISearchPathPattern[] {
431 if (!searchPaths || !searchPaths.length) {
432 // No workspace => ignore search paths
433 return [];
434 }
435
436 const expandedSearchPaths = searchPaths.flatMap(searchPath => {
437 // 1 open folder => just resolve the search paths to absolute paths
438 let { pathPortion, globPortion } = splitGlobFromPath(searchPath);
439
440 if (globPortion) {
441 globPortion = normalizeGlobPattern(globPortion);
442 }
443
444 // One pathPortion to multiple expanded search paths (e.g. duplicate matching workspace folders)
445 const oneExpanded = this.expandOneSearchPath(pathPortion);
446
447 // Expanded search paths to multiple resolved patterns (with ** and without)
448 return oneExpanded.flatMap(oneExpandedResult => this.resolveOneSearchPathPattern(oneExpandedResult, globPortion));
449 });
450
451 const searchPathPatternMap = new Map<string, ISearchPathPattern>();
452 expandedSearchPaths.forEach(oneSearchPathPattern => {
453 const key = oneSearchPathPattern.searchPath.toString();
454 const existing = searchPathPatternMap.get(key);
455 if (existing) {
456 if (oneSearchPathPattern.pattern) {
457 existing.pattern = existing.pattern || {};
458 existing.pattern[oneSearchPathPattern.pattern] = true;
459 }
460 } else {
461 searchPathPatternMap.set(key, {
462 searchPath: oneSearchPathPattern.searchPath,
463 pattern: oneSearchPathPattern.pattern ? patternListToIExpression(oneSearchPathPattern.pattern) : undefined
464 });
465 }
466 });
467
468 return Array.from(searchPathPatternMap.values());
469 }
471 > /**
472 > * Takes a searchPath like `./a/foo` or `../a/foo` and expands it to absolute paths for all the workspaces it matches.
473 > */
474 > private expandOneSearchPath(searchPath: string): IOneSearchPathPattern[] {
475 if (path.isAbsolute(searchPath)) {
476 const workspaceFolders = this.workspaceContextService.getWorkspace().folders;
477 if (workspaceFolders[0] && workspaceFolders[0].uri.scheme !== Schemas.file) {
478 return [{
479 searchPath: workspaceFolders[0].uri.with({ path: searchPath })
480 }];
481 }
482
483 // Currently only local resources can be searched for with absolute search paths.
484 // TODO convert this to a workspace folder + pattern, so excludes will be resolved properly for an absolute path inside a workspace folder
485 return [{
486 searchPath: uri.file(path.normalize(searchPath))
487 }];
488 }
489
490 if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.FOLDER) {
491 const workspaceUri = this.workspaceContextService.getWorkspace().folders[0].uri;
492
493 searchPath = normalizeSlashes(searchPath);
494 if (searchPath.startsWith('../') || searchPath === '..') {
495 const resolvedPath = path.posix.resolve(workspaceUri.path, searchPath);
496 return [{
497 searchPath: workspaceUri.with({ path: resolvedPath })
498 }];
499 }
500
501 const cleanedPattern = normalizeGlobPattern(searchPath);
502 return [{
503 searchPath: workspaceUri,
504 pattern: cleanedPattern
505 }];
506 } else if (searchPath === './' || searchPath === '.\\') {
507 return []; // ./ or ./**/foo makes sense for single-folder but not multi-folder workspaces
508 } else {
509 const searchPathWithoutDotSlash = searchPath.replace(/^\.[\/\\]/, '');
510 const folders = this.workspaceContextService.getWorkspace().folders;
511 const folderMatches = folders.map(folder => {
512 const match = searchPathWithoutDotSlash.match(new RegExp(`^${strings.escapeRegExpCharacters(folder.name)}(?:/(.*)|$)`));
513 return match ? {
514 match,
515 folder
516 } : null;
517 }).filter(isDefined);
518
519 if (folderMatches.length) {
520 return folderMatches.map(match => {
521 const patternMatch = match.match[1];
522 return {
523 searchPath: match.folder.uri,
524 pattern: patternMatch && normalizeGlobPattern(patternMatch)
525 };
526 });
527 } else {
528 const probableWorkspaceFolderNameMatch = searchPath.match(/\.[\/\\](.+)[\/\\]?/);
529 const probableWorkspaceFolderName = probableWorkspaceFolderNameMatch ? probableWorkspaceFolderNameMatch[1] : searchPath;
530
531 // No root folder with name
532 const searchPathNotFoundError = nls.localize('search.noWorkspaceWithName', "Workspace folder does not exist: {0}", probableWorkspaceFolderName);
533 throw new Error(searchPathNotFoundError);
534 }
535 }
536 }
538 > private resolveOneSearchPathPattern(oneExpandedResult: IOneSearchPathPattern, globPortion?: string): IOneSearchPathPattern[] {
539 const pattern = oneExpandedResult.pattern && globPortion ?
540 `${oneExpandedResult.pattern}/${globPortion}` :
541 oneExpandedResult.pattern || globPortion;
542
543 const results = [
544 {
545 searchPath: oneExpandedResult.searchPath,
546 pattern
547 }];
548
549 if (pattern && !pattern.endsWith('**')) {
550 results.push({
551 searchPath: oneExpandedResult.searchPath,
552 pattern: pattern + '/**'
553 });
554 }
555
556 return results;
557 }
559 > private getFolderQueryForSearchPath(searchPath: ISearchPathPattern, options: ICommonQueryBuilderOptions, searchPathExcludes: ISearchPathsInfo): IFolderQuery | null {
560 const rootConfig = this.getFolderQueryForRoot(toWorkspaceFolder(searchPath.searchPath), options, searchPathExcludes, false);
561 if (!rootConfig) {
562 return null;
563 }
564
565 return {
566 ...rootConfig,
567 ...{
568 includePattern: searchPath.pattern
569 }
570 };
571 }
573 > private getFolderQueryForRoot(folder: (IWorkspaceFolderData | URI), options: ICommonQueryBuilderOptions, searchPathExcludes: ISearchPathsInfo, includeFolderName: boolean): IFolderQuery | null {
574 let thisFolderExcludeSearchPathPattern: glob.IExpression | undefined;
575 const folderUri = URI.isUri(folder) ? folder : folder.uri;
576
577 // only use exclude root if it is different from the folder root
578 let excludeFolderRoots = options.excludePattern?.map(excludePattern => {
579 const excludeRoot = options.excludePattern && isISearchPatternBuilder(excludePattern) ? excludePattern.uri : undefined;
580 const shouldUseExcludeRoot = (!excludeRoot || !(URI.isUri(folder) && this.uriIdentityService.extUri.isEqual(folder, excludeRoot)));
581 return shouldUseExcludeRoot ? excludeRoot : undefined;
582 });
583
584 if (!excludeFolderRoots?.length) {
585 excludeFolderRoots = [undefined];
586 }
587
588 if (searchPathExcludes.searchPaths) {
589 const thisFolderExcludeSearchPath = searchPathExcludes.searchPaths.filter(sp => isEqual(sp.searchPath, folderUri))[0];
590 if (thisFolderExcludeSearchPath && !thisFolderExcludeSearchPath.pattern) {
591 // entire folder is excluded
592 return null;
593 } else if (thisFolderExcludeSearchPath) {
594 thisFolderExcludeSearchPathPattern = thisFolderExcludeSearchPath.pattern;
595 }
596 }
597
598 const folderConfig = this.configurationService.getValue<ISearchConfiguration>({ resource: folderUri });
599 const settingExcludes = this.getExcludesForFolder(folderConfig, options);
600 const excludePattern: glob.IExpression = {
601 ...(settingExcludes || {}),
602 ...(thisFolderExcludeSearchPathPattern || {})
603 };
604
605 const folderName = URI.isUri(folder) ? basename(folder) : folder.name;
606
607 const excludePatternRet: ExcludeGlobPattern[] = excludeFolderRoots.map(excludeFolderRoot => {
608 return Object.keys(excludePattern).length > 0 ? {
609 folder: excludeFolderRoot,
610 pattern: excludePattern
611 } satisfies ExcludeGlobPattern : undefined;
612 }).filter((e) => e) as ExcludeGlobPattern[];
613
614 return {
615 folder: folderUri,
616 folderName: includeFolderName ? folderName : undefined,
617 excludePattern: excludePatternRet,
618 fileEncoding: folderConfig.files && folderConfig.files.encoding,
619 disregardIgnoreFiles: typeof options.disregardIgnoreFiles === 'boolean' ? options.disregardIgnoreFiles : !folderConfig.search?.useIgnoreFiles,
620 disregardGlobalIgnoreFiles: typeof options.disregardGlobalIgnoreFiles === 'boolean' ? options.disregardGlobalIgnoreFiles : !folderConfig.search?.useGlobalIgnoreFiles,
621 disregardParentIgnoreFiles: typeof options.disregardParentIgnoreFiles === 'boolean' ? options.disregardParentIgnoreFiles : !folderConfig.search?.useParentIgnoreFiles,
622 ignoreSymlinks: typeof options.ignoreSymlinks === 'boolean' ? options.ignoreSymlinks : !folderConfig.search?.followSymlinks,
623 ignoreGlobCase: options.ignoreGlobCase,
624 };
625 }
627 >
628 function splitGlobFromPath(searchPath: string): { pathPortion: string; globPortion?: string } {
629 const globCharMatch = searchPath.match(/[\*\{\}\(\)\[\]\?]/);
630 if (globCharMatch) {
631 const globCharIdx = globCharMatch.index;
632 const lastSlashMatch = searchPath.substr(0, globCharIdx).match(/[/|\\][^/\\]*$/);
633 if (lastSlashMatch) {
634 let pathPortion = searchPath.substr(0, lastSlashMatch.index);
635 if (!pathPortion.match(/[/\\]/)) {
636 // If the last slash was the only slash, then we now have '' or 'C:' or '.'. Append a slash.
637 pathPortion += '/';
638 }
639
640 return {
641 pathPortion,
642 globPortion: searchPath.substr((lastSlashMatch.index || 0) + 1)
643 };
644 }
645 }
646
647 // No glob char, or malformed
648 return {
649 pathPortion: searchPath
650 };
651 }
653 function patternListToIExpression(...patterns: string[]): glob.IExpression | undefined {
654 return patterns.length ?
655 patterns.reduce((glob, cur) => { glob[cur] = true; return glob; }, Object.create(null)) :
656 undefined;
657 }
659 function splitGlobPattern(pattern: string): string[] {
660 return glob.splitGlobAware(pattern, ',')
661 .map(s => s.trim())
662 .filter(s => !!s.length);
663 }
665 > /**
666 > * Note - we used {} here previously but ripgrep can't handle nested {} patterns. See https://github.com/microsoft/vscode/issues/32761
667 > */
668 function expandGlobalGlob(pattern: string): string[] {
669 const patterns = [
670 `**/${pattern}/**`,
671 `**/${pattern}`
672 ];
673
674 return patterns.map(p => p.replace(/\*\*\/\*\*/g, '**'));
675 }
677 function normalizeSlashes(pattern: string): string {
678 return pattern.replace(/\\/g, '/');
679 }
681 > /**
682 > * Normalize slashes, remove `./` and trailing slashes
683 > */
684 function normalizeGlobPattern(pattern: string): string {
685 return normalizeSlashes(pattern)
686 .replace(/^\.\//, '')
687 .replace(/\/+$/g, '');
688 }
690 > /**
691 > * Escapes a path for use as a glob pattern that would match the input precisely.
692 > * Characters '?', '*', '[', and ']' are escaped into character range glob syntax
693 > * (for example, '?' becomes '[?]').
694 > * NOTE: This implementation makes no special cases for UNC paths. For example,
695 > * given the input "//?/C:/A?.txt", this would produce output '//[?]/C:/A[?].txt',
696 > * which may not be desirable in some cases. Use with caution if UNC paths could be expected.
697 > */
698 > export function escapeGlobPattern(path: string): string {
699 > return path.replace(/([?*[\]])/g, '[$1]'); queryBuilder.ts ×1
700 > }
702 > /**
703 > * Construct an include pattern from a list of folders uris to search in.
704 > */
705 > export function resolveResourcesForSearchIncludes(resources: URI[], contextService: IWorkspaceContextService): string[] {
706 > resources = arrays.distinct(resources, resource => resource.toString()); queryBuilder.ts ×2
707 >
708 > const folderPaths: string[] = [];
709 > const workspace = contextService.getWorkspace();
710 >
711 > if (resources) {
712 > resources.forEach(resource => {
713 > let folderPath: string | undefined;
714 > if (contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
715 > // Show relative path from the root for single-root mode
716 > folderPath = relativePath(workspace.folders[0].uri, resource); // always uses forward slashes
717 > if (folderPath && folderPath !== '.') {
718 > folderPath = './' + folderPath;
719 > }
720 > } else {
721 const owningFolder = contextService.getWorkspaceFolder(resource);
722 if (owningFolder) {
723 const owningRootName = owningFolder.name;
724 // If this root is the only one with its basename, use a relative ./ path. If there is another, use an absolute path
725 const isUniqueFolder = workspace.folders.filter(folder => folder.name === owningRootName).length === 1;
726 if (isUniqueFolder) {
727 const relPath = relativePath(owningFolder.uri, resource); // always uses forward slashes
728 if (relPath === '') {
729 folderPath = `./${owningFolder.name}`;
730 } else {
731 folderPath = `./${owningFolder.name}/${relPath}`;
732 }
733 } else {
734 folderPath = resource.fsPath; // TODO rob: handle non-file URIs
735 }
736 }
737 }
739 > if (folderPath) {
740 > folderPaths.push(escapeGlobPattern(folderPath));
741 > }
742 > });
743 > }
744 > return folderPaths;
745 > }