textSearchManager.ts ×25

Frontier kind: Code frontier

unlabeled · c_3a2b9b263d54

11 tests · 10953 LOC · 48 files · introduces 0 tests · 120 LOC · 2 files

Introduces — evidence that enters the hierarchy at this concept

Code
28 ranges120 lines · 2 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1594 ranges10953 lines · 48 files · Browse complete extent
All tests (intent)
11 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: 120 introduced LOC across 28 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/services/search/common/textSearchManager.ts 105 introduced LOC · 25 ranges

Open complete file

56 let isCanceled = false;
57 const onResult = (result: TextSearchResult2, folderIdx: number) => {
58 > if (result instanceof AISearchKeyword) { textSearchManager.ts
59 // Already processed by the callback.
60 return;
61 }
62 > if (isCanceled) { textSearchManager.ts
63 return;
64 }
66 > if (!this.isLimitHit) {
67 > const resultSize = this.resultSize(result);
68 > if (result instanceof TextSearchMatch2 && typeof this.query.maxResults === 'number' && this.resultCount + resultSize > this.query.maxResults) {
69 this.isLimitHit = true;
70 isCanceled = true;
73 result = this.trimResultToSize(result, this.query.maxResults - this.resultCount);
74 }
76 > const newResultSize = this.resultSize(result);
77 > this.resultCount += newResultSize;
78 > const a = result instanceof TextSearchMatch2;
79 >
80 > if (newResultSize > 0 || !a) {
81 > this.collector!.add(result, folderIdx);
82 > }
83 > }
84 > };
85
86 // For each root folder
111
112 private resultSize(result: TextSearchResult2): number {
113 > if (result instanceof TextSearchMatch2) { textSearchManager.ts
114 > return Array.isArray(result.ranges) ?
115 > result.ranges.length :
116 1;
118 else {
119 // #104400 context lines shoudn't count towards result count
120 return 0;
121 }
123
124 private trimResultToSize(result: TextSearchMatch2, size: number): TextSearchMatch2 {
139 const progress = {
140 report: (result: TextSearchResult2 | AISearchResult) => {
141 > if (result instanceof AISearchKeyword) { textSearchManager.ts
142 onKeywordResult?.(result);
143 > } else { textSearchManager.ts
144 > if (result.uri === undefined) {
145 throw Error('Text search result URI is undefined. Please check provider implementation.');
146 }
147 > const folderQuery = folderMappings.findQueryFragmentAwareSubstr(result.uri); textSearchManager.ts
148 > if (folderQuery?.folder?.scheme) {
149 > const hasSibling = folderQuery.folder.scheme === Schemas.file ?
150 hasSiblingPromiseFn(() => {
151 return this.fileUtils.readdir(resources.dirname(result.uri));
152 }) :
153 undefined;
155 > const relativePath = resources.relativePath(folderQuery.folder, result.uri);
156 > if (relativePath) {
157 > // This method is only async when the exclude contains sibling clauses
158 > const included = folderQuery.queryTester.includedInQuery(relativePath, path.basename(relativePath), hasSibling);
159 > if (isThenable(included)) {
160 > testingPs.push(
161 > included.then(isIncluded => {
162 > if (isIncluded) {
163 > onResult(result, folderQuery.folderIdx);
164 > }
165 > }));
166 > } else if (included) {
167 onResult(result, folderQuery.folderIdx);
168 }
170 > }
171 > }
172 > }
173 };
174
189 }
190 if (testingPs.length) {
191 > await Promise.all(testingPs); textSearchManager.ts
192 > }
193
194 return result;
250
251 add(data: TextSearchResult2, folderIdx: number): void {
252 > // Collects TextSearchResults into IInternalFileMatches and collates using BatchedCollector. textSearchManager.ts
253 > // This is efficient for ripgrep which sends results back one file at a time. It wouldn't be efficient for other search
254 > // providers that send results in random order. We could do this step afterwards instead.
255 > if (this._currentFileMatch && (this._currentFolderIdx !== folderIdx || !resources.isEqual(this._currentUri, data.uri))) {
256 this.pushToCollector();
257 this._currentFileMatch = null;
258 }
260 > if (!this._currentFileMatch) {
261 > this._currentFolderIdx = folderIdx;
262 > this._currentUri = data.uri;
263 > this._currentFileMatch = {
264 > resource: data.uri,
265 > results: []
266 > };
267 > }
268 >
269 > this._currentFileMatch.results!.push(extensionResultToFrontendResult(data));
270 > }
271
272 private pushToCollector(): void {
273 const size = this._currentFileMatch && this._currentFileMatch.results ?
274 > this._currentFileMatch.results.length : textSearchManager.ts
275 0;
276 this._batchedCollector.addItem(this._currentFileMatch!, size);
283
284 private sendItems(items: IFileMatch[]): void {
285 > this._onResult(items); textSearchManager.ts
286 > }
287 }
288
289 > function extensionResultToFrontendResult(data: TextSearchResult2): ITextSearchResult { textSearchManager.ts
290 > // Warning: result from RipgrepTextSearchEH has fake Range. Don't depend on any other props beyond these...
291 > if (data instanceof TextSearchMatch2) {
292 > return {
293 > previewText: data.previewText,
294 > rangeLocations: data.ranges.map(r => ({
295 > preview: {
296 > startLineNumber: r.previewRange.start.line,
297 > startColumn: r.previewRange.start.character,
298 > endLineNumber: r.previewRange.end.line,
299 > endColumn: r.previewRange.end.character
300 > } satisfies ISearchRange,
301 > source: {
302 > startLineNumber: r.sourceRange.start.line,
303 > startColumn: r.sourceRange.start.character,
304 > endLineNumber: r.sourceRange.end.line,
305 > endColumn: r.sourceRange.end.character
306 > } satisfies ISearchRange,
307 > })),
308 > } satisfies ITextSearchMatch;
309 > } else {
310 return {
311 text: data.text,
353
354 private addItemToBatch(item: T, size: number): void {
355 > this.batch.push(item); textSearchManager.ts
356 > this.batchSize += size;
357 > this.onUpdate();
358 > }
359
360 private addItemsToBatch(item: T[], size: number): void {
365
366 private onUpdate(): void {
367 > if (this.totalNumberCompleted < BatchedCollector.START_BATCH_AFTER_COUNT) { textSearchManager.ts
368 > // Flush because we aren't batching yet
369 > this.flush();
370 > } else if (this.batchSize >= this.maxBatchSize) {
371 // Flush because the batch is full
372 this.flush();
377 }, BatchedCollector.TIMEOUT);
378 }
380
381 flush(): void {
382 if (this.batchSize) {
383 > this.totalNumberCompleted += this.batchSize; textSearchManager.ts
384 > this.cb(this.batch);
385 > this.batch = [];
386 > this.batchSize = 0;
387 >
388 > if (this.timeoutHandle) {
389 clearTimeout(this.timeoutHandle);
390 this.timeoutHandle = undefined;
391 }
393 }
394 }
src/vs/workbench/services/search/common/search.ts 15 introduced LOC · 3 ranges

Open complete file

786 */
787 includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | Promise<boolean>): Promise<boolean> | boolean {
788 > search.ts
789 > const isIncluded = () => {
790 > return this._parsedIncludeExpression ?
791 !!(this._parsedIncludeExpression(testPath, basename, hasSibling)) :
792 true;
793 > }; search.ts
794 >
795 > return Promise.all(this._parsedExcludeExpression.map(e => {
796 > const excluded = e(testPath, basename, hasSibling);
797 > if (isThenable(excluded)) {
798 return excluded.then(excluded => {
799 if (excluded) {
804 });
805 }
806 > search.ts
807 > return isIncluded();
808 >
809 > })).then(e => e.some(e => !!e));
810 >
811 >
812 > }
813
814 hasSiblingExcludeClauses(): boolean {