search.ts ×34

Frontier kind: Code frontier

unlabeled · c_a873d47bb7dc

437 tests · 8373 LOC · 36 files · introduces 0 tests · 1119 LOC · 2 files

Introduces — evidence that enters the hierarchy at this concept

Code
38 ranges1119 lines · 2 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1079 ranges8373 lines · 36 files · Browse complete extent
All tests (intent)
437 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

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 native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

2 files ranked by introduced lines: 1119 introduced LOC across 38 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/services/search/common/searchExtTypes.ts 560 introduced LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- searchExtTypes.ts
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 { CancellationToken } from '../../../../base/common/cancellation.js';
7 > import { URI } from '../../../../base/common/uri.js';
8 > import { IProgress } from '../../../../platform/progress/common/progress.js';
9 >
10 > export class Position {
11 > constructor(readonly line: number, readonly character: number) { }
12 >
13 > isBefore(other: Position): boolean { return false; }
14 > isBeforeOrEqual(other: Position): boolean { return false; }
15 > isAfter(other: Position): boolean { return false; }
16 > isAfterOrEqual(other: Position): boolean { return false; }
17 > isEqual(other: Position): boolean { return false; }
18 > compareTo(other: Position): number { return 0; }
19 > translate(lineDelta?: number, characterDelta?: number): Position;
20 > translate(change: { lineDelta?: number; characterDelta?: number }): Position;
21 > translate(_?: any, _2?: any): Position { return new Position(0, 0); }
22 > with(line?: number, character?: number): Position;
23 > with(change: { line?: number; character?: number }): Position;
24 > with(_: any): Position { return new Position(0, 0); }
25 > }
26 >
27 > export class Range {
28 > readonly start: Position;
29 > readonly end: Position;
30 >
31 > constructor(startLine: number, startCol: number, endLine: number, endCol: number) {
32 this.start = new Position(startLine, startCol);
33 this.end = new Position(endLine, endCol);
34 }
36 > isEmpty = false;
37 > isSingleLine = false;
38 > contains(positionOrRange: Position | Range): boolean { return false; }
39 > isEqual(other: Range): boolean { return false; }
40 > intersection(range: Range): Range | undefined { return undefined; }
41 > union(other: Range): Range { return new Range(0, 0, 0, 0); }
42 >
43 > with(start?: Position, end?: Position): Range;
44 > with(change: { start?: Position; end?: Position }): Range;
45 > with(_: any): Range { return new Range(0, 0, 0, 0); }
46 > }
47 >
48 > export type ProviderResult<T> = T | undefined | null | Thenable<T | undefined | null>;
49 >
50 > /**
51 > * A relative pattern is a helper to construct glob patterns that are matched
52 > * relatively to a base path. The base path can either be an absolute file path
53 > * or a [workspace folder](#WorkspaceFolder).
54 > */
55 > export interface RelativePattern {
56 >
57 > /**
58 > * A base file path to which this pattern will be matched against relatively. The
59 > * file path must be absolute, should not have any trailing path separators and
60 > * not include any relative segments (`.` or `..`).
61 > */
62 > baseUri: URI;
63 >
64 > /**
65 > * A file glob pattern like `*.{ts,js}` that will be matched on file paths
66 > * relative to the base path.
67 > *
68 > * Example: Given a base of `/home/work/folder` and a file path of `/home/work/folder/index.js`,
69 > * the file glob pattern will match on `index.js`.
70 > */
71 > pattern: string;
72 > }
73 >
74 > /**
75 > * A file glob pattern to match file paths against. This can either be a glob pattern string
76 > * (like `** /*.{ts,js}` without space before / or `*.{ts,js}`) or a [relative pattern](#RelativePattern).
77 > *
78 > * Glob patterns can have the following syntax:
79 > * * `*` to match zero or more characters in a path segment
80 > * * `?` to match on one character in a path segment
81 > * * `**` to match any number of path segments, including none
82 > * * `{}` to group conditions (e.g. `** /*.{ts,js}` without space before / matches all TypeScript and JavaScript files)
83 > * * `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
84 > * * `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
85 > *
86 > * Note: a backslash (`\`) is not valid within a glob pattern. If you have an existing file
87 > * path to match against, consider to use the [relative pattern](#RelativePattern) support
88 > * that takes care of converting any backslash into slash. Otherwise, make sure to convert
89 > * any backslash to slash when creating the glob pattern.
90 > */
91 > export type GlobPattern = string | RelativePattern;
92 >
93 > /**
94 > * The parameters of a query for text search.
95 > */
96 > export interface TextSearchQuery2 {
97 > /**
98 > * The text pattern to search for.
99 > */
100 > pattern: string;
101 >
102 > /**
103 > * Whether or not `pattern` should match multiple lines of text.
104 > */
105 > isMultiline?: boolean;
106 >
107 > /**
108 > * Whether or not `pattern` should be interpreted as a regular expression.
109 > */
110 > isRegExp?: boolean;
111 >
112 > /**
113 > * Whether or not the search should be case-sensitive.
114 > */
115 > isCaseSensitive?: boolean;
116 >
117 > /**
118 > * Whether or not to search for whole word matches only.
119 > */
120 > isWordMatch?: boolean;
121 > }
122 >
123 >
124 > export interface TextSearchProviderFolderOptions {
125 > /**
126 > * The root folder to search within.
127 > */
128 > folder: URI;
129 >
130 > /**
131 > * Files that match an `includes` glob pattern should be included in the search.
132 > */
133 > includes: string[];
134 >
135 > /**
136 > * Files that match an `excludes` glob pattern should be excluded from the search.
137 > */
138 > excludes: GlobPattern[];
139 >
140 > /**
141 > * Whether to ignore case for glob patterns.
142 > */
143 > ignoreGlobCase?: boolean;
144 >
145 > /**
146 > * Whether symlinks should be followed while searching.
147 > * For more info, see the setting description for `search.followSymlinks`.
148 > */
149 > followSymlinks: boolean;
150 >
151 > /**
152 > * Which file locations we should look for ignore (.gitignore or .ignore) files to respect.
153 > */
154 > useIgnoreFiles: {
155 > /**
156 > * Use ignore files at the current workspace root.
157 > */
158 > local: boolean;
159 > /**
160 > * Use ignore files at the parent directory. If set, `local` in {@link TextSearchProviderFolderOptions.useIgnoreFiles} should also be `true`.
161 > */
162 > parent: boolean;
163 > /**
164 > * Use global ignore files. If set, `local` in {@link TextSearchProviderFolderOptions.useIgnoreFiles} should also be `true`.
165 > */
166 > global: boolean;
167 > };
168 >
169 > /**
170 > * Interpret files using this encoding.
171 > * See the vscode setting `"files.encoding"`
172 > */
173 > encoding: string;
174 > }
175 >
176 > /**
177 > * Options that apply to text search.
178 > */
179 > export interface TextSearchProviderOptions {
180 >
181 > folderOptions: TextSearchProviderFolderOptions[];
182 >
183 > /**
184 > * The maximum number of results to be returned.
185 > */
186 > maxResults: number;
187 >
188 > /**
189 > * Options to specify the size of the result text preview.
190 > */
191 > previewOptions: {
192 > /**
193 > * The maximum number of lines in the preview.
194 > * Only search providers that support multiline search will ever return more than one line in the match.
195 > * Defaults to 100.
196 > */
197 > matchLines: number;
198 >
199 > /**
200 > * The maximum number of characters included per line.
201 > * Defaults to 10000.
202 > */
203 > charsPerLine: number;
204 > };
205 >
206 > /**
207 > * Exclude files larger than `maxFileSize` in bytes.
208 > */
209 > maxFileSize: number | undefined;
210 >
211 >
212 > /**
213 > * Number of lines of context to include before and after each match.
214 > */
215 > surroundingContext: number;
216 > }
217 >
218 >
219 > /**
220 > * Information collected when text search is complete.
221 > */
222 > export interface TextSearchComplete2 {
223 > /**
224 > * Whether the search hit the limit on the maximum number of search results.
225 > * `maxResults` on [`TextSearchOptions`](#TextSearchOptions) specifies the max number of results.
226 > * - If exactly that number of matches exist, this should be false.
227 > * - If `maxResults` matches are returned and more exist, this should be true.
228 > * - If search hits an internal limit which is less than `maxResults`, this should be true.
229 > */
230 > limitHit?: boolean;
231 > }
232 >
233 > export interface FileSearchProviderFolderOptions {
234 > /**
235 > * The root folder to search within.
236 > */
237 > folder: URI;
238 >
239 > /**
240 > * Files that match an `includes` glob pattern should be included in the search.
241 > */
242 > includes: string[];
243 >
244 > /**
245 > * Files that match an `excludes` glob pattern should be excluded from the search.
246 > */
247 > excludes: GlobPattern[];
248 >
249 > /**
250 > * Whether symlinks should be followed while searching.
251 > * For more info, see the setting description for `search.followSymlinks`.
252 > */
253 > followSymlinks: boolean;
254 >
255 > /**
256 > * Which file locations we should look for ignore (.gitignore or .ignore) files to respect.
257 > */
258 > useIgnoreFiles: {
259 > /**
260 > * Use ignore files at the current workspace root.
261 > */
262 > local: boolean;
263 > /**
264 > * Use ignore files at the parent directory. If set, {@link FileSearchProviderOptions.useIgnoreFiles.local} should also be `true`.
265 > */
266 > parent: boolean;
267 > /**
268 > * Use global ignore files. If set, {@link FileSearchProviderOptions.useIgnoreFiles.local} should also be `true`.
269 > */
270 > global: boolean;
271 > };
272 > }
273 >
274 > /**
275 > * Options that apply to file search.
276 > */
277 > export interface FileSearchProviderOptions {
278 > folderOptions: FileSearchProviderFolderOptions[];
279 >
280 > /**
281 > * An object with a lifespan that matches the session's lifespan. If the provider chooses to, this object can be used as the key for a cache,
282 > * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared.
283 > */
284 > session: unknown;
285 >
286 > /**
287 > * The maximum number of results to be returned.
288 > */
289 > maxResults: number;
290 > }
291 >
292 > /**
293 > * The main match information for a {@link TextSearchResult2}.
294 > */
295 > export class TextSearchMatch2 {
296 > /**
297 > * @param uri The uri for the matching document.
298 > * @param ranges The ranges associated with this match.
299 > * @param previewText The text that is used to preview the match. The highlighted range in `previewText` is specified in `ranges`.
300 > */
301 > constructor(
302 public uri: URI,
303 public ranges: { sourceRange: Range; previewRange: Range }[],
304 public previewText: string) { }
306 > }
307 >
308 > /**
309 > * The potential context information for a {@link TextSearchResult2}.
310 > */
311 > export class TextSearchContext2 {
312 > /**
313 > * @param uri The uri for the matching document.
314 > * @param text The line of context text.
315 > * @param lineNumber The line number of this line of context.
316 > */
317 > constructor(
318 public uri: URI,
319 public text: string,
320 public lineNumber: number) { }
322 >
323 > /**
324 > /**
325 > * Keyword suggestion for AI search.
326 > */
327 > export class AISearchKeyword {
328 > /**
329 > * @param keyword The keyword associated with the search.
330 > */
331 > constructor(public keyword: string) { }
332 > }
333 >
334 > /**
335 > * A result payload for a text search, pertaining to matches within a single file.
336 > */
337 > export type TextSearchResult2 = TextSearchMatch2 | TextSearchContext2;
338 >
339 > /**
340 > * A result payload for an AI search.
341 > * This can be a {@link TextSearchMatch2 match} or a {@link AISearchKeyword keyword}.
342 > * The result can be a match or a keyword.
343 > */
344 > export type AISearchResult = TextSearchResult2 | AISearchKeyword;
345 >
346 > /**
347 > * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickaccess or other extensions.
348 > *
349 > * A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for
350 > * all files that match the user's query.
351 > *
352 > * The FileSearchProvider will be invoked on every keypress in quickaccess. When `workspace.findFiles` is called, it will be invoked with an empty query string,
353 > * and in that case, every file in the folder should be returned.
354 > */
355 > export interface FileSearchProvider2 {
356 > /**
357 > * Provide the set of files that match a certain file path pattern.
358 > * @param query The parameters for this query.
359 > * @param options A set of options to consider while searching files.
360 > * @param progress A progress callback that must be invoked for all results.
361 > * @param token A cancellation token.
362 > */
363 > provideFileSearchResults(pattern: string, options: FileSearchProviderOptions, token: CancellationToken): ProviderResult<URI[]>;
364 > }
365 >
366 > /**
367 > * A TextSearchProvider provides search results for text results inside files in the workspace.
368 > */
369 > export interface TextSearchProvider2 {
370 > /**
371 > * Provide results that match the given text pattern.
372 > * @param query The parameters for this query.
373 > * @param options A set of options to consider while searching.
374 > * @param progress A progress callback that must be invoked for all results.
375 > * @param token A cancellation token.
376 > */
377 > provideTextSearchResults(query: TextSearchQuery2, options: TextSearchProviderOptions, progress: IProgress<TextSearchResult2>, token: CancellationToken): ProviderResult<TextSearchComplete2>;
378 > }
379 >
380 > /**
381 > * Information collected when text search is complete.
382 > */
383 > export interface TextSearchComplete2 {
384 > /**
385 > * Whether the search hit the limit on the maximum number of search results.
386 > * `maxResults` on {@linkcode TextSearchOptions} specifies the max number of results.
387 > * - If exactly that number of matches exist, this should be false.
388 > * - If `maxResults` matches are returned and more exist, this should be true.
389 > * - If search hits an internal limit which is less than `maxResults`, this should be true.
390 > */
391 > limitHit?: boolean;
392 >
393 > /**
394 > * Additional information regarding the state of the completed search.
395 > *
396 > * Messages with "Information" style support links in markdown syntax:
397 > * - Click to [run a command](command:workbench.action.OpenQuickPick)
398 > * - Click to [open a website](https://aka.ms)
399 > *
400 > * Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again.
401 > */
402 > message?: TextSearchCompleteMessage2[];
403 > }
404 >
405 > /**
406 > * A message regarding a completed search.
407 > */
408 > export interface TextSearchCompleteMessage2 {
409 > /**
410 > * Markdown text of the message.
411 > */
412 > text: string;
413 > /**
414 > * Whether the source of the message is trusted, command links are disabled for untrusted message sources.
415 > * Messaged are untrusted by default.
416 > */
417 > trusted?: boolean;
418 > /**
419 > * The message type, this affects how the message will be rendered.
420 > */
421 > type: TextSearchCompleteMessageType;
422 > }
423 >
424 >
425 > /**
426 > * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickaccess or other extensions.
427 > *
428 > * A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for
429 > * all files that match the user's query.
430 > *
431 > * The FileSearchProvider will be invoked on every keypress in quickaccess. When `workspace.findFiles` is called, it will be invoked with an empty query string,
432 > * and in that case, every file in the folder should be returned.
433 > */
434 > export interface FileSearchProvider2 {
435 > /**
436 > * Provide the set of files that match a certain file path pattern.
437 > * @param query The parameters for this query.
438 > * @param options A set of options to consider while searching files.
439 > * @param progress A progress callback that must be invoked for all results.
440 > * @param token A cancellation token.
441 > */
442 > provideFileSearchResults(pattern: string, options: FileSearchProviderOptions, token: CancellationToken): ProviderResult<URI[]>;
443 > }
444 >
445 > /**
446 > * A TextSearchProvider provides search results for text results inside files in the workspace.
447 > */
448 > export interface TextSearchProvider2 {
449 > /**
450 > * Provide results that match the given text pattern.
451 > * @param query The parameters for this query.
452 > * @param options A set of options to consider while searching.
453 > * @param progress A progress callback that must be invoked for all results.
454 > * @param token A cancellation token.
455 > */
456 > provideTextSearchResults(query: TextSearchQuery2, options: TextSearchProviderOptions, progress: IProgress<TextSearchResult2>, token: CancellationToken): ProviderResult<TextSearchComplete2>;
457 > }
458 >
459 > /**
460 > * Information collected when text search is complete.
461 > */
462 > export interface TextSearchComplete2 {
463 > /**
464 > * Whether the search hit the limit on the maximum number of search results.
465 > * `maxResults` on {@link TextSearchOptions} specifies the max number of results.
466 > * - If exactly that number of matches exist, this should be false.
467 > * - If `maxResults` matches are returned and more exist, this should be true.
468 > * - If search hits an internal limit which is less than `maxResults`, this should be true.
469 > */
470 > limitHit?: boolean;
471 >
472 > /**
473 > * Additional information regarding the state of the completed search.
474 > *
475 > * Messages with "Information" style support links in markdown syntax:
476 > * - Click to [run a command](command:workbench.action.OpenQuickPick)
477 > * - Click to [open a website](https://aka.ms)
478 > *
479 > * Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again.
480 > */
481 > message?: TextSearchCompleteMessage2[];
482 > }
483 >
484 > /**
485 > * A message regarding a completed search.
486 > */
487 > export interface TextSearchCompleteMessage2 {
488 > /**
489 > * Markdown text of the message.
490 > */
491 > text: string;
492 > /**
493 > * Whether the source of the message is trusted, command links are disabled for untrusted message sources.
494 > * Messaged are untrusted by default.
495 > */
496 > trusted?: boolean;
497 > /**
498 > * The message type, this affects how the message will be rendered.
499 > */
500 > type: TextSearchCompleteMessageType;
501 > }
502 >
503 > /**
504 > * Options for following search.exclude and files.exclude settings.
505 > */
506 > export enum ExcludeSettingOptions {
507 > /*
508 > * Don't use any exclude settings.
509 > */
510 > None = 1,
511 > /*
512 > * Use:
513 > * - files.exclude setting
514 > */
515 > FilesExclude = 2,
516 > /*
517 > * Use:
518 > * - files.exclude setting
519 > * - search.exclude setting
520 > */
521 > SearchAndFilesExclude = 3
522 > }
523 >
524 > export enum TextSearchCompleteMessageType {
525 > Information = 1,
526 > Warning = 2,
527 > }
528 >
529 >
530 > /**
531 > * A message regarding a completed search.
532 > */
533 > export interface TextSearchCompleteMessage {
534 > /**
535 > * Markdown text of the message.
536 > */
537 > text: string;
538 > /**
539 > * Whether the source of the message is trusted, command links are disabled for untrusted message sources.
540 > */
541 > trusted?: boolean;
542 > /**
543 > * The message type, this affects how the message will be rendered.
544 > */
545 > type: TextSearchCompleteMessageType;
546 > }
547 >
548 >
549 > /**
550 > * An AITextSearchProvider provides additional AI text search results in the workspace.
551 > */
552 > export interface AITextSearchProvider {
553 >
554 > /**
555 > * The name of the AI searcher. Will be displayed as `{name} Results` in the Search View.
556 > */
557 > readonly name?: string;
558 >
559 > /**
560 > * WARNING: VERY EXPERIMENTAL.
561 > *
562 > * Provide results that match the given text pattern.
563 > * @param query The parameter for this query.
564 > * @param options A set of options to consider while searching.
565 > * @param progress A progress callback that must be invoked for all results.
566 > * @param token A cancellation token.
567 > */
568 > provideAITextSearchResults(query: string, options: TextSearchProviderOptions, progress: IProgress<TextSearchResult2>, token: CancellationToken): ProviderResult<TextSearchComplete2>;
569 > }
src/vs/workbench/services/search/common/search.ts 559 introduced LOC · 34 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- search.ts
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 { mapArrayOrNot } from '../../../../base/common/arrays.js';
7 > import { CancellationToken } from '../../../../base/common/cancellation.js';
8 > import * as glob from '../../../../base/common/glob.js';
9 > import { IDisposable } from '../../../../base/common/lifecycle.js';
10 > import * as objects from '../../../../base/common/objects.js';
11 > import * as extpath from '../../../../base/common/extpath.js';
12 > import { fuzzyContains, getNLines } from '../../../../base/common/strings.js';
13 > import { URI, UriComponents } from '../../../../base/common/uri.js';
14 > import { IFilesConfiguration } from '../../../../platform/files/common/files.js';
15 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
16 > import { ITelemetryData } from '../../../../platform/telemetry/common/telemetry.js';
17 > import { Event } from '../../../../base/common/event.js';
18 > import * as paths from '../../../../base/common/path.js';
19 > import { isCancellationError } from '../../../../base/common/errors.js';
20 > import { AISearchKeyword, GlobPattern, TextSearchCompleteMessageType } from './searchExtTypes.js';
21 > import { isThenable } from '../../../../base/common/async.js';
22 > import { ResourceSet } from '../../../../base/common/map.js';
23 >
24 > export { TextSearchCompleteMessageType };
25 >
26 > export const VIEWLET_ID = 'workbench.view.search';
27 > export const PANEL_ID = 'workbench.panel.search';
28 > export const VIEW_ID = 'workbench.view.search';
29 > export const SEARCH_RESULT_LANGUAGE_ID = 'search-result';
30 >
31 > export const SEARCH_EXCLUDE_CONFIG = 'search.exclude';
32 > export const DEFAULT_MAX_SEARCH_RESULTS = 20000;
33 >
34 > // Warning: this pattern is used in the search editor to detect offsets. If you
35 > // change this, also change the search-result built-in extension
36 > const SEARCH_ELIDED_PREFIX = '⟪ ';
37 > const SEARCH_ELIDED_SUFFIX = ' characters skipped ⟫';
38 > const SEARCH_ELIDED_MIN_LEN = (SEARCH_ELIDED_PREFIX.length + SEARCH_ELIDED_SUFFIX.length + 5) * 2;
39 >
40 > export const ISearchService = createDecorator<ISearchService>('searchService');
41 >
42 > /**
43 > * A service that enables to search for files or with in files.
44 > */
45 > export interface ISearchService {
46 > readonly _serviceBrand: undefined;
47 > textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise<ISearchComplete>;
48 > aiTextSearch(query: IAITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise<ISearchComplete>;
49 > getAIName(): Promise<string | undefined>;
50 > textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined, notebookFilesToIgnore?: ResourceSet, asyncNotebookFilesToIgnore?: Promise<ResourceSet>): { syncResults: ISearchComplete; asyncResults: Promise<ISearchComplete> };
51 > fileSearch(query: IFileQuery, token?: CancellationToken): Promise<ISearchComplete>;
52 > schemeHasFileSearchProvider(scheme: string): boolean;
53 > clearCache(cacheKey: string): Promise<void>;
54 > registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable;
55 > }
56 >
57 > /**
58 > * TODO@roblou - split text from file search entirely, or share code in a more natural way.
59 > */
60 > export const enum SearchProviderType {
61 > file,
62 > text,
63 > aiText
64 > }
65 >
66 > export interface ISearchResultProvider {
67 > getAIName(): Promise<string | undefined>;
68 > textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): Promise<ISearchComplete>;
69 > fileSearch(query: IFileQuery, token?: CancellationToken): Promise<ISearchComplete>;
70 > clearCache(cacheKey: string): Promise<void>;
71 > }
72 >
73 >
74 > export interface ExcludeGlobPattern<U extends UriComponents = URI> {
75 > folder?: U;
76 > pattern: glob.IExpression;
77 > }
78 >
79 > export interface IFolderQuery<U extends UriComponents = URI> {
80 > folder: U;
81 > folderName?: string;
82 > excludePattern?: ExcludeGlobPattern<U>[];
83 > includePattern?: glob.IExpression;
84 > ignoreGlobCase?: boolean;
85 > fileEncoding?: string;
86 > disregardIgnoreFiles?: boolean;
87 > disregardGlobalIgnoreFiles?: boolean;
88 > disregardParentIgnoreFiles?: boolean;
89 > ignoreSymlinks?: boolean;
90 > }
91 >
92 > export interface ICommonQueryProps<U extends UriComponents> {
93 > /** For telemetry - indicates what is triggering the source */
94 > _reason?: string;
95 >
96 > folderQueries: IFolderQuery<U>[];
97 > // The include pattern for files that gets passed into ripgrep.
98 > // Note that this will override any ignore files if applicable.
99 > includePattern?: glob.IExpression;
100 > excludePattern?: glob.IExpression;
101 > ignoreGlobCase?: boolean;
102 > extraFileResources?: U[];
103 >
104 > onlyOpenEditors?: boolean;
105 >
106 > maxResults?: number;
107 > usingSearchPaths?: boolean;
108 > onlyFileScheme?: boolean;
109 > }
110 >
111 > export interface IFileQueryProps<U extends UriComponents> extends ICommonQueryProps<U> {
112 > type: QueryType.File;
113 > filePattern?: string;
114 >
115 > // when walking through the tree to find the result, don't use the filePattern to fuzzy match.
116 > // Instead, should use glob matching.
117 > shouldGlobMatchFilePattern?: boolean;
118 >
119 > /**
120 > * If true no results will be returned. Instead `limitHit` will indicate if at least one result exists or not.
121 > * Currently does not work with queries including a 'siblings clause'.
122 > */
123 > exists?: boolean;
124 > sortByScore?: boolean;
125 > cacheKey?: string;
126 > }
127 >
128 > export interface ITextQueryProps<U extends UriComponents> extends ICommonQueryProps<U> {
129 > type: QueryType.Text;
130 > contentPattern: IPatternInfo;
131 >
132 > previewOptions?: ITextSearchPreviewOptions;
133 > maxFileSize?: number;
134 > surroundingContext?: number;
135 >
136 > userDisabledExcludesAndIgnoreFiles?: boolean;
137 > }
138 >
139 > export interface IAITextQueryProps<U extends UriComponents> extends ICommonQueryProps<U> {
140 > type: QueryType.aiText;
141 > contentPattern: string;
142 >
143 > previewOptions?: ITextSearchPreviewOptions;
144 > maxFileSize?: number;
145 > surroundingContext?: number;
146 >
147 > userDisabledExcludesAndIgnoreFiles?: boolean;
148 > }
149 >
150 > export type IFileQuery = IFileQueryProps<URI>;
151 > export type IRawFileQuery = IFileQueryProps<UriComponents>;
152 > export type ITextQuery = ITextQueryProps<URI>;
153 > export type IRawTextQuery = ITextQueryProps<UriComponents>;
154 > export type IAITextQuery = IAITextQueryProps<URI>;
155 > export type IRawAITextQuery = IAITextQueryProps<UriComponents>;
156 >
157 > export type IRawQuery = IRawTextQuery | IRawFileQuery | IRawAITextQuery;
158 > export type ISearchQuery = ITextQuery | IFileQuery | IAITextQuery;
159 > export type ITextSearchQuery = ITextQuery | IAITextQuery;
160 >
161 > export const enum QueryType {
162 > File = 1,
163 > Text = 2,
164 > aiText = 3
165 > }
166 >
167 > /* __GDPR__FRAGMENT__
168 > "IPatternInfo" : {
169 > "isRegExp": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
170 > "isWordMatch": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
171 > "wordSeparators": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
172 > "isMultiline": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
173 > "isCaseSensitive": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
174 > "isSmartCase": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
175 > }
176 > */
177 > export interface IPatternInfo {
178 > pattern: string;
179 > isRegExp?: boolean;
180 > isWordMatch?: boolean;
181 > wordSeparators?: string;
182 > isMultiline?: boolean;
183 > isUnicode?: boolean;
184 > isCaseSensitive?: boolean;
185 > notebookInfo?: INotebookPatternInfo;
186 > }
187 >
188 > export interface INotebookPatternInfo {
189 > isInNotebookMarkdownInput?: boolean;
190 > isInNotebookMarkdownPreview?: boolean;
191 > isInNotebookCellInput?: boolean;
192 > isInNotebookCellOutput?: boolean;
193 > }
194 >
195 > export interface IFileMatch<U extends UriComponents = URI> {
196 > resource: U;
197 > results?: ITextSearchResult<U>[];
198 > }
199 >
200 > export type IRawFileMatch2 = IFileMatch<UriComponents>;
201 >
202 > export interface ITextSearchPreviewOptions {
203 > matchLines: number;
204 > charsPerLine: number;
205 > }
206 >
207 > export interface ISearchRange {
208 > readonly startLineNumber: number;
209 > readonly startColumn: number;
210 > readonly endLineNumber: number;
211 > readonly endColumn: number;
212 > }
213 >
214 > export interface ITextSearchMatch<U extends UriComponents = URI> {
215 > uri?: U;
216 > rangeLocations: SearchRangeSetPairing[];
217 > previewText: string;
218 > webviewIndex?: number;
219 > cellFragment?: string;
220 > }
221 >
222 > export interface ITextSearchContext<U extends UriComponents = URI> {
223 > uri?: U;
224 > text: string;
225 > lineNumber: number;
226 > }
227 >
228 > export type ITextSearchResult<U extends UriComponents = URI> = ITextSearchMatch<U> | ITextSearchContext<U>;
229 >
230 > export function resultIsMatch(result: ITextSearchResult): result is ITextSearchMatch {
231 return !!(<ITextSearchMatch>result).rangeLocations && !!(<ITextSearchMatch>result).previewText;
232 }
233 > search.ts
234 > export interface IProgressMessage {
235 > message: string;
236 > }
237 >
238 > export type ISearchProgressItem = IFileMatch | IProgressMessage | AISearchKeyword;
239 >
240 > export function isFileMatch(p: ISearchProgressItem): p is IFileMatch {
241 return !!(<IFileMatch>p).resource;
242 }
243 > search.ts
244 > export function isAIKeyword(p: ISearchProgressItem): p is AISearchKeyword {
245 return !!(<AISearchKeyword>p).keyword;
246 }
247 > search.ts
248 > export function isProgressMessage(p: ISearchProgressItem | ISerializedSearchProgressItem): p is IProgressMessage {
249 return !!(p as IProgressMessage).message;
250 }
251 > search.ts
252 > export interface ITextSearchCompleteMessage {
253 > text: string;
254 > type: TextSearchCompleteMessageType;
255 > trusted?: boolean;
256 > }
257 >
258 > export interface ISearchCompleteStats {
259 > limitHit?: boolean;
260 > messages: ITextSearchCompleteMessage[];
261 > stats?: IFileSearchStats | ITextSearchStats;
262 > }
263 >
264 > export interface ISearchComplete extends ISearchCompleteStats {
265 > results: IFileMatch[];
266 > exit?: SearchCompletionExitCode;
267 > aiKeywords?: AISearchKeyword[];
268 > }
269 >
270 > export const enum SearchCompletionExitCode {
271 > Normal,
272 > NewSearchStarted
273 > }
274 >
275 > export interface ITextSearchStats {
276 > type: 'textSearchProvider' | 'searchProcess' | 'aiTextSearchProvider';
277 > }
278 >
279 > export interface IFileSearchStats {
280 > fromCache: boolean;
281 > detailStats: ISearchEngineStats | ICachedSearchStats | IFileSearchProviderStats;
282 >
283 > resultCount: number;
284 > type: 'fileSearchProvider' | 'searchProcess';
285 > sortingTime?: number;
286 > }
287 >
288 > export interface ICachedSearchStats {
289 > cacheWasResolved: boolean;
290 > cacheLookupTime: number;
291 > cacheFilterTime: number;
292 > cacheEntryCount: number;
293 > }
294 >
295 > export interface ISearchEngineStats {
296 > fileWalkTime: number;
297 > directoriesWalked: number;
298 > filesWalked: number;
299 > cmdTime: number;
300 > cmdResultCount?: number;
301 > }
302 >
303 > export interface IFileSearchProviderStats {
304 > providerTime: number;
305 > postProcessTime: number;
306 > }
307 >
308 > export class FileMatch implements IFileMatch {
309 > results: ITextSearchResult[] = [];
310 > constructor(public resource: URI) {
311 // empty
312 }
313 > } search.ts
314 >
315 > export interface SearchRangeSetPairing {
316 > source: ISearchRange;
317 > preview: ISearchRange;
318 > }
319 >
320 > export class TextSearchMatch implements ITextSearchMatch {
321 > rangeLocations: SearchRangeSetPairing[] = [];
322 > previewText: string;
323 > webviewIndex?: number;
324 >
325 > constructor(text: string, ranges: ISearchRange | ISearchRange[], previewOptions?: ITextSearchPreviewOptions, webviewIndex?: number) {
326 this.webviewIndex = webviewIndex;
327
371 }
372 }
373 > } search.ts
374 >
375 function isSingleLineRangeList(ranges: ISearchRange[]): boolean {
376 const line = ranges[0].startLineNumber;
383 return true;
384 }
385 > search.ts
386 > export class SearchRange implements ISearchRange {
387 > startLineNumber: number;
388 > startColumn: number;
389 > endLineNumber: number;
390 > endColumn: number;
391 >
392 > constructor(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number) {
393 this.startLineNumber = startLineNumber;
394 this.startColumn = startColumn;
396 this.endColumn = endColumn;
397 }
398 > } search.ts
399 >
400 > export class OneLineRange extends SearchRange {
401 > constructor(lineNumber: number, startColumn: number, endColumn: number) {
402 super(lineNumber, startColumn, lineNumber, endColumn);
403 }
404 > } search.ts
405 >
406 > export const enum ViewMode {
407 > List = 'list',
408 > Tree = 'tree'
409 > }
410 >
411 > export const enum SearchSortOrder {
412 > Default = 'default',
413 > FileNames = 'fileNames',
414 > Type = 'type',
415 > Modified = 'modified',
416 > CountDescending = 'countDescending',
417 > CountAscending = 'countAscending'
418 > }
419 >
420 > export const enum SemanticSearchBehavior {
421 > Auto = 'auto',
422 > Manual = 'manual',
423 > RunOnEmpty = 'runOnEmpty',
424 > }
425 >
426 > export interface ISearchConfigurationProperties {
427 > exclude: glob.IExpression;
428 > /**
429 > * Use ignore file for file search.
430 > */
431 > useIgnoreFiles: boolean;
432 > useGlobalIgnoreFiles: boolean;
433 > useParentIgnoreFiles: boolean;
434 > followSymlinks: boolean;
435 > smartCase: boolean;
436 > globalFindClipboard: boolean;
437 > useReplacePreview: boolean;
438 > showLineNumbers: boolean;
439 > actionsPosition: 'auto' | 'right';
440 > maxResults: number | null;
441 > collapseResults: 'auto' | 'alwaysCollapse' | 'alwaysExpand';
442 > searchOnType: boolean;
443 > seedOnFocus: boolean;
444 > seedWithNearestWord: boolean;
445 > searchOnTypeDebouncePeriod: number;
446 > mode: 'view' | 'reuseEditor' | 'newEditor';
447 > searchEditor: {
448 > doubleClickBehaviour: 'selectWord' | 'goToLocation' | 'openLocationToSide';
449 > singleClickBehaviour: 'default' | 'peekDefinition';
450 > reusePriorSearchConfiguration: boolean;
451 > defaultNumberOfContextLines: number | null;
452 > focusResultsOnSearch: boolean;
453 > experimental: {};
454 > };
455 > sortOrder: SearchSortOrder;
456 > decorations: {
457 > colors: boolean;
458 > badges: boolean;
459 > };
460 > quickAccess: {
461 > preserveInput: boolean;
462 > };
463 > defaultViewMode: ViewMode;
464 > experimental: {
465 > closedNotebookRichContentResults: boolean;
466 > };
467 > searchView: {
468 > semanticSearchBehavior: string;
469 > keywordSuggestions: boolean;
470 > };
471 > }
472 >
473 > export interface ISearchConfiguration extends IFilesConfiguration {
474 > search?: ISearchConfigurationProperties;
475 > editor: {
476 > wordSeparators: string;
477 > };
478 > }
479 >
480 > export function getExcludes(configuration: ISearchConfiguration, includeSearchExcludes = true): glob.IExpression | undefined {
481 const fileExcludes = configuration && configuration.files && configuration.files.exclude;
482 const searchExcludes = includeSearchExcludes && configuration && configuration.search && configuration.search.exclude;
497 return allExcludes;
498 }
499 > search.ts
500 > export function pathIncludedInQuery(queryProps: ICommonQueryProps<URI>, fsPath: string): boolean {
501 const globOptions = queryProps.ignoreGlobCase ? { ignoreCase: true } : undefined;
502 if (queryProps.excludePattern && glob.match(queryProps.excludePattern, fsPath, globOptions)) {
527 return true;
528 }
529 > search.ts
530 > export enum SearchErrorCode {
531 > unknownEncoding = 1,
532 > regexParseError,
533 > globParseError,
534 > invalidLiteral,
535 > rgProcessError,
536 > other,
537 > canceled
538 > }
539 >
540 > export class SearchError extends Error {
541 > constructor(message: string, readonly code?: SearchErrorCode) {
542 super(message);
543 }
544 > } search.ts
545 >
546 > export function deserializeSearchError(error: Error): SearchError {
547 const errorMsg = error.message;
548
558 }
559 }
560 > search.ts
561 > export function serializeSearchError(searchError: SearchError): Error {
562 const details = { message: searchError.message, code: searchError.code };
563 return new Error(JSON.stringify(details));
564 }
565 > export interface ITelemetryEvent { search.ts
566 > eventName: string;
567 > data: ITelemetryData;
568 > }
569 >
570 > export interface IRawSearchService {
571 > fileSearch(search: IRawFileQuery): Event<ISerializedSearchProgressItem | ISerializedSearchComplete>;
572 > textSearch(search: IRawTextQuery): Event<ISerializedSearchProgressItem | ISerializedSearchComplete>;
573 > clearCache(cacheKey: string): Promise<void>;
574 > }
575 >
576 > export interface IRawFileMatch {
577 > base?: string;
578 > /**
579 > * The path of the file relative to the containing `base` folder.
580 > * This path is exactly as it appears on the filesystem.
581 > */
582 > relativePath: string;
583 > /**
584 > * This path is transformed for search purposes. For example, this could be
585 > * the `relativePath` with the workspace folder name prepended. This way the
586 > * search algorithm would also match against the name of the containing folder.
587 > *
588 > * If not given, the search algorithm should use `relativePath`.
589 > */
590 > searchPath: string | undefined;
591 > }
592 >
593 > export interface ISearchEngine<T> {
594 > search: (onResult: (matches: T) => void, onProgress: (progress: IProgressMessage) => void, done: (error: Error | null, complete: ISearchEngineSuccess) => void) => void;
595 > cancel: () => void;
596 > }
597 >
598 > export interface ISerializedSearchSuccess {
599 > type: 'success';
600 > limitHit: boolean;
601 > messages: ITextSearchCompleteMessage[];
602 > stats?: IFileSearchStats | ITextSearchStats;
603 > }
604 >
605 > export interface ISearchEngineSuccess {
606 > limitHit: boolean;
607 > messages: ITextSearchCompleteMessage[];
608 > stats: ISearchEngineStats;
609 > }
610 >
611 > export interface ISerializedSearchError {
612 > type: 'error';
613 > error: {
614 > message: string;
615 > stack: string;
616 > };
617 > }
618 >
619 > export type ISerializedSearchComplete = ISerializedSearchSuccess | ISerializedSearchError;
620 >
621 > export function isSerializedSearchComplete(arg: ISerializedSearchProgressItem | ISerializedSearchComplete): arg is ISerializedSearchComplete {
622 // eslint-disable-next-line local/code-no-any-casts
623 if ((arg as any).type === 'error') {
630 }
631 }
632 > search.ts
633 > export function isSerializedSearchSuccess(arg: ISerializedSearchComplete): arg is ISerializedSearchSuccess {
634 return arg.type === 'success';
635 }
636 > search.ts
637 > export function isSerializedFileMatch(arg: ISerializedSearchProgressItem): arg is ISerializedFileMatch {
638 return !!(<ISerializedFileMatch>arg).path;
639 }
640 > search.ts
641 > const filePatternIgnoreCaseOptions = { ignoreCase: true };
642 >
643 > export function isFilePatternMatch(candidate: IRawFileMatch, filePatternToUse: string, fuzzy = true, ignoreCase?: boolean): boolean {
644 const pathToMatch = candidate.searchPath ? candidate.searchPath : candidate.relativePath;
645 return fuzzy ?
647 glob.match(filePatternToUse, pathToMatch, ignoreCase ? filePatternIgnoreCaseOptions : undefined);
648 }
649 > search.ts
650 > export interface ISerializedFileMatch {
651 > path: string;
652 > results?: ITextSearchResult[];
653 > numMatches?: number;
654 > }
655 >
656 > // Type of the possible values for progress calls from the engine
657 > export type ISerializedSearchProgressItem = ISerializedFileMatch | ISerializedFileMatch[] | IProgressMessage;
658 > export type IFileSearchProgressItem = IRawFileMatch | IRawFileMatch[] | IProgressMessage;
659 >
660 >
661 > export class SerializableFileMatch implements ISerializedFileMatch {
662 > path: string;
663 > results: ITextSearchMatch[];
664 >
665 > constructor(path: string) {
666 this.path = path;
667 this.results = [];
668 }
669 > search.ts
670 > addMatch(match: ITextSearchMatch): void {
671 this.results.push(match);
672 }
673 > search.ts
674 > serialize(): ISerializedFileMatch {
675 return {
676 path: this.path,
679 };
680 }
681 > } search.ts
682 >
683 > /**
684 > * Computes the patterns that the provider handles. Discards sibling clauses and 'false' patterns
685 > */
686 > export function resolvePatternsForProvider(globalPattern: glob.IExpression | undefined, folderPattern: glob.IExpression | undefined): string[] {
687 const merged = {
688 ...(globalPattern || {}),
696 });
697 }
698 > search.ts
699 > export class QueryGlobTester {
700 >
701 > private _excludeExpression: glob.IExpression[]; // TODO: evaluate globs based on baseURI of pattern
702 > private _parsedExcludeExpression: glob.ParsedExpression[];
703 >
704 > private _parsedIncludeExpression: glob.ParsedExpression | null = null;
705 >
706 > constructor(config: ISearchQuery, folderQuery: IFolderQuery) {
707 const globOptions = config.ignoreGlobCase || folderQuery.ignoreGlobCase ? { ignoreCase: true } : undefined;
708
739 }
740 }
741 > search.ts
742 > private _evalParsedExcludeExpression(testPath: string, basename: string | undefined, hasSibling?: (name: string) => boolean): string | null {
743 // todo: less hacky way of evaluating sync vs async sibling clauses
744 let result: string | null = null;
756 return result;
757 }
758 > search.ts
759 >
760 > matchesExcludesSync(testPath: string, basename?: string, hasSibling?: (name: string) => boolean): boolean {
761 if (this._parsedExcludeExpression && this._evalParsedExcludeExpression(testPath, basename, hasSibling)) {
762 return true;
765 return false;
766 }
767 > search.ts
768 > /**
769 > * Guaranteed sync - siblingsFn should not return a promise.
770 > */
771 > includedInQuerySync(testPath: string, basename?: string, hasSibling?: (name: string) => boolean): boolean {
772 if (this._parsedExcludeExpression && this._evalParsedExcludeExpression(testPath, basename, hasSibling)) {
773 return false;
780 return true;
781 }
782 > search.ts
783 > /**
784 > * Evaluating the exclude expression is only async if it includes sibling clauses. As an optimization, avoid doing anything with Promises
785 > * unless the expression is async.
786 > */
787 > includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | Promise<boolean>): Promise<boolean> | boolean {
788
789 const isIncluded = () => {
811
812 }
813 > search.ts
814 > hasSiblingExcludeClauses(): boolean {
815 return this._excludeExpression.reduce((prev, curr) => hasSiblingClauses(curr) || prev, false);
816 }
817 > } search.ts
818 >
819 function hasSiblingClauses(pattern: glob.IExpression): boolean {
820 for (const key in pattern) {
826 return false;
827 }
828 > search.ts
829 > export function hasSiblingPromiseFn(siblingsFn?: () => Promise<string[]>) {
830 if (!siblingsFn) {
831 return undefined;
841 };
842 }
843 > search.ts
844 > export function hasSiblingFn(siblingsFn?: () => string[]) {
845 if (!siblingsFn) {
846 return undefined;
856 };
857 }
858 > search.ts
859 function listToMap(list: string[]) {
860 const map: Record<string, true> = {};
864 return map;
865 }
866 > search.ts
867 > export function excludeToGlobPattern(excludesForFolder: { baseUri?: URI | undefined; patterns: string[] }[]): GlobPattern[] {
868 return excludesForFolder.flatMap(exclude => exclude.patterns.map(pattern => {
869 return exclude.baseUri ?
874 }));
875 }
876 > search.ts
877 > export const DEFAULT_TEXT_SEARCH_PREVIEW_OPTIONS = {
878 > matchLines: 100,
879 > charsPerLine: 10000
880 > };