fileSearch.ts ×31

Frontier kind: Code frontier

unlabeled · c_a4e20ba014ae

32 tests · 78836 LOC · 282 files · introduces 0 tests · 624 LOC · 9 files

Introduces — evidence that enters the hierarchy at this concept

Code
108 ranges624 lines · 9 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
5398 ranges78836 lines · 282 files · Browse complete extent
All tests (intent)
32 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.

9 files ranked by introduced lines: 624 introduced LOC across 108 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/services/search/node/fileSearch.ts 156 introduced LOC · 31 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- fileSearch.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 * as childProcess from 'child_process';
7 > import * as fs from 'fs';
8 > import * as path from '../../../../base/common/path.js';
9 > import { Readable } from 'stream';
10 > import { StringDecoder } from 'string_decoder';
11 > import * as arrays from '../../../../base/common/arrays.js';
12 > import { toErrorMessage } from '../../../../base/common/errorMessage.js';
13 > import * as glob from '../../../../base/common/glob.js';
14 > import * as normalization from '../../../../base/common/normalization.js';
15 > import { isEqualOrParent } from '../../../../base/common/extpath.js';
16 > import * as platform from '../../../../base/common/platform.js';
17 > import { StopWatch } from '../../../../base/common/stopwatch.js';
18 > import * as strings from '../../../../base/common/strings.js';
19 > import * as types from '../../../../base/common/types.js';
20 > import { URI } from '../../../../base/common/uri.js';
21 > import { Promises } from '../../../../base/node/pfs.js';
22 > import { IFileQuery, IFolderQuery, IProgressMessage, ISearchEngineStats, IRawFileMatch, ISearchEngine, ISearchEngineSuccess, isFilePatternMatch, hasSiblingFn } from '../common/search.js';
23 > import { spawnRipgrepCmd } from './ripgrepFileSearch.js';
24 > import { prepareQuery } from '../../../../base/common/fuzzyScorer.js';
25 >
26 > interface IDirectoryEntry extends IRawFileMatch {
27 > base: string;
28 > basename: string;
29 > }
30 >
31 > interface IDirectoryTree {
32 > rootEntries: IDirectoryEntry[];
33 > pathToEntries: { [relativePath: string]: IDirectoryEntry[] };
34 > }
35 >
36 > const killCmds = new Set<() => void>();
37 > process.on('exit', () => {
38 > killCmds.forEach(cmd => cmd());
39 > });
40 >
41 > export class FileWalker {
42 > private config: IFileQuery;
43 > private filePattern: string;
44 > private normalizedFilePatternLowercase: string | null = null;
45 > private includePattern: glob.ParsedExpression | undefined;
46 > private maxResults: number | null;
47 > private exists: boolean;
48 > private maxFilesize: number | null = null;
49 > private isLimitHit: boolean;
50 > private resultCount: number;
51 > private isCanceled = false;
52 > private fileWalkSW: StopWatch | null = null;
53 > private directoriesWalked: number;
54 > private filesWalked: number;
55 > private errors: string[];
56 > private cmdSW: StopWatch | null = null;
57 > private cmdResultCount: number = 0;
58 >
59 > private folderExcludePatterns: Map<string, AbsoluteAndRelativeParsedExpression>;
60 > private globalExcludePattern: glob.ParsedExpression | undefined;
61 >
62 > private walkedPaths: { [path: string]: boolean };
63 >
64 > constructor(config: IFileQuery) {
65 this.config = config;
66 this.filePattern = config.filePattern || '';
109 });
110 }
112 > cancel(): void {
113 this.isCanceled = true;
114 killCmds.forEach(cmd => cmd());
115 }
117 > walk(folderQueries: IFolderQuery[], extraFiles: URI[], numThreads: number | undefined, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgressMessage) => void, done: (error: Error | null, isLimitHit: boolean) => void): void {
118 this.fileWalkSW = StopWatch.create(false);
119
154 });
155 }
157 > private parallel<T, E>(list: T[], fn: (item: T, callback: (err: Error | null, result: E | null) => void) => void, callback: (err: Array<Error | null> | null, result: E[]) => void): void {
158 const results = new Array(list.length);
159 const errors = new Array<Error | null>(list.length);
182 });
183 }
185 > private call<F extends Function>(fun: F, that: any, ...args: any[]): void {
186 try {
187 fun.apply(that, args);
190 }
191 }
193 > private async cmdTraversal(folderQuery: IFolderQuery, numThreads: number | undefined, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgressMessage) => void, cb: (err?: Error) => void): Promise<void> {
194 const rootFolder = folderQuery.folder.fsPath;
195 const isMac = platform.isMacintosh;
282 });
283 }
285 > /**
286 > * Public for testing.
287 > */
288 > spawnFindCmd(folderQuery: IFolderQuery) {
289 const excludePattern = this.folderExcludePatterns.get(folderQuery.folder.fsPath)!;
290 const basenames = excludePattern.getBasenameTerms();
307 return childProcess.spawn('find', args, { cwd: folderQuery.folder.fsPath });
308 }
310 > /**
311 > * Public for testing.
312 > */
313 > readStdout(cmd: childProcess.ChildProcess, encoding: BufferEncoding, cb: (err: Error | null, stdout?: string) => void): void {
314 let all = '';
315 this.collectStdout(cmd, encoding, () => { }, (err: Error | null, stdout?: string, last?: boolean) => {
325 });
326 }
328 > private collectStdout(cmd: childProcess.ChildProcess, encoding: BufferEncoding, onMessage: (message: IProgressMessage) => void, cb: (err: Error | null, stdout?: string, last?: boolean) => void): void {
329 let onData = (err: Error | null, stdout?: string, last?: boolean) => {
330 if (err || last) {
370 });
371 }
373 > private forwardData(stream: Readable, encoding: BufferEncoding, cb: (err: Error | null, stdout?: string) => void): StringDecoder {
374 const decoder = new StringDecoder(encoding);
375 stream.on('data', (data: Buffer) => {
378 return decoder;
379 }
381 > private collectData(stream: Readable): Buffer[] {
382 const buffers: Buffer[] = [];
383 stream.on('data', (data: Buffer) => {
386 return buffers;
387 }
389 > private decodeData(buffers: Buffer[], encoding: BufferEncoding): string {
390 const decoder = new StringDecoder(encoding);
391 return buffers.map(buffer => decoder.write(buffer)).join('');
392 }
394 > private initDirectoryTree(): IDirectoryTree {
395 const tree: IDirectoryTree = {
396 rootEntries: [],
400 return tree;
401 }
403 > private addDirectoryEntries(folderQuery: IFolderQuery, { pathToEntries }: IDirectoryTree, base: string, relativeFiles: string[], onResult: (result: IRawFileMatch) => void) {
404 // Support relative paths to files from a root resource (ignores excludes)
405 const filePatternMatch = this.filePattern && relativeFiles.find(f => strings.equals(f, this.filePattern, this.config.ignoreGlobCase));
429 relativeFiles.forEach(add);
430 }
432 > private matchDirectoryTree({ rootEntries, pathToEntries }: IDirectoryTree, rootFolder: string, onResult: (result: IRawFileMatch) => void) {
433 const self = this;
434 const excludePattern = this.folderExcludePatterns.get(rootFolder)!;
469 matchDirectory(rootEntries);
470 }
472 > getStats(): ISearchEngineStats {
473 return {
474 cmdTime: this.cmdSW!.elapsed(),
479 };
480 }
482 > private doWalk(folderQuery: IFolderQuery, relativeParentPath: string, files: string[], onResult: (result: IRawFileMatch) => void, done: (error?: Error) => void): void {
483 const rootFolder = folderQuery.folder;
484
573 });
574 }
576 > private matchFile(onResult: (result: IRawFileMatch) => void, candidate: IRawFileMatch): void {
577 if (this.isFileMatch(candidate) && (!this.includePattern || this.includePattern(candidate.relativePath, path.basename(candidate.relativePath)))) {
578 this.resultCount++;
587 }
588 }
590 > private isFileMatch(candidate: IRawFileMatch): boolean {
591 // Check for search pattern
592 if (this.filePattern) {
605 return true;
606 }
608 > private statLinkIfNeeded(path: string, lstat: fs.Stats, clb: (error: Error | null, stat: fs.Stats) => void): void {
609 if (lstat.isSymbolicLink()) {
610 return fs.stat(path, clb); // stat the target the link points to
613 return clb(null, lstat); // not a link, so the stat is already ok for us
614 }
616 > private realPathIfNeeded(path: string, lstat: fs.Stats, clb: (error: Error | null, realpath?: string) => void): void {
617 if (lstat.isSymbolicLink()) {
618 return fs.realpath(path, (error, realpath) => {
627 return clb(null, path);
628 }
630 > /**
631 > * If we're searching for files in multiple workspace folders, then better prepend the
632 > * name of the workspace folder to the path of the file. This way we'll be able to
633 > * better filter files that are all on the top of a workspace folder and have all the
634 > * same name. A typical example are `package.json` or `README.md` files.
635 > */
636 > private getSearchPath(folderQuery: IFolderQuery, relativePath: string): string {
637 if (folderQuery.folderName) {
638 return path.join(folderQuery.folderName, relativePath);
640 return relativePath;
641 }
642 > } fileSearch.ts
643 >
644 > export class Engine implements ISearchEngine<IRawFileMatch> {
645 > private folderQueries: IFolderQuery[];
646 > private extraFiles: URI[];
647 > private walker: FileWalker;
648 > private numThreads?: number;
649 >
650 > constructor(config: IFileQuery, numThreads?: number) {
651 this.folderQueries = config.folderQueries;
652 this.extraFiles = config.extraFileResources || [];
655 this.walker = new FileWalker(config);
656 }
658 > search(onResult: (result: IRawFileMatch) => void, onProgress: (progress: IProgressMessage) => void, done: (error: Error | null, complete: ISearchEngineSuccess) => void): void {
659 this.walker.walk(this.folderQueries, this.extraFiles, this.numThreads, onResult, onProgress, (err: Error | null, isLimitHit: boolean) => {
660 done(err, {
665 });
666 }
668 > cancel(): void {
669 this.walker.cancel();
670 }
671 > } fileSearch.ts
672 >
673 > /**
674 > * This class exists to provide one interface on top of two ParsedExpressions, one for absolute expressions and one for relative expressions.
675 > * The absolute and relative expressions don't "have" to be kept separate, but this keeps us from having to path.join every single
676 > * file searched, it's only used for a text search with a searchPath
677 > */
678 > class AbsoluteAndRelativeParsedExpression {
679 > private absoluteParsedExpr: glob.ParsedExpression | undefined;
680 > private relativeParsedExpr: glob.ParsedExpression | undefined;
681 >
682 > constructor(public expression: glob.IExpression, private root: string, private ignoreCase?: boolean) {
683 this.init(expression);
684 }
686 > /**
687 > * Split the IExpression into its absolute and relative components, and glob.parse them separately.
688 > */
689 > private init(expr: glob.IExpression): void {
690 let absoluteGlobExpr: glob.IExpression | undefined;
691 let relativeGlobExpr: glob.IExpression | undefined;
706 this.relativeParsedExpr = relativeGlobExpr && glob.parse(relativeGlobExpr, globOptions);
707 }
709 > test(_path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise<boolean>): string | Promise<string | null> | undefined | null {
710 return (this.relativeParsedExpr && this.relativeParsedExpr(_path, basename, hasSibling)) ||
711 (this.absoluteParsedExpr && this.absoluteParsedExpr(path.join(this.root, _path), basename, hasSibling));
712 }
714 > getBasenameTerms(): string[] {
715 const basenameTerms: string[] = [];
716 if (this.absoluteParsedExpr) {
724 return basenameTerms;
725 }
727 > getPathTerms(): string[] {
728 const pathTerms: string[] = [];
729 if (this.absoluteParsedExpr) {
737 return pathTerms;
738 }
739 > } fileSearch.ts
740 >
741 function rgErrorMsgForDisplay(msg: string): string | undefined {
742 const lines = msg.trim().split('\n');
src/vs/workbench/api/common/extHostSearch.ts 119 introduced LOC · 14 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostSearch.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 { IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
7 > import type * as vscode from 'vscode';
8 > import { ExtHostSearchShape, MainThreadSearchShape, MainContext } from './extHost.protocol.js';
9 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
10 > import { FileSearchManager } from '../../services/search/common/fileSearchManager.js';
11 > import { IExtHostRpcService } from './extHostRpcService.js';
12 > import { IURITransformerService } from './extHostUriTransformerService.js';
13 > import { ILogService } from '../../../platform/log/common/log.js';
14 > import { IRawFileQuery, ISearchCompleteStats, IFileQuery, IRawTextQuery, IRawQuery, ITextQuery, IFolderQuery, IRawAITextQuery, IAITextQuery } from '../../services/search/common/search.js';
15 > import { URI, UriComponents } from '../../../base/common/uri.js';
16 > import { TextSearchManager } from '../../services/search/common/textSearchManager.js';
17 > import { CancellationToken } from '../../../base/common/cancellation.js';
18 > import { revive } from '../../../base/common/marshalling.js';
19 > import { OldFileSearchProviderConverter, OldTextSearchProviderConverter } from '../../services/search/common/searchExtConversionTypes.js';
20 >
21 > export interface IExtHostSearch extends ExtHostSearchShape {
22 > registerTextSearchProviderOld(scheme: string, provider: vscode.TextSearchProvider): IDisposable;
23 > registerFileSearchProviderOld(scheme: string, provider: vscode.FileSearchProvider): IDisposable;
24 > registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider2): IDisposable;
25 > registerAITextSearchProvider(scheme: string, provider: vscode.AITextSearchProvider): IDisposable;
26 > registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider2): IDisposable;
27 > doInternalFileSearchWithCustomCallback(query: IFileQuery, token: CancellationToken, handleFileMatch: (data: URI[]) => void): Promise<ISearchCompleteStats>;
28 > }
29 >
30 > export const IExtHostSearch = createDecorator<IExtHostSearch>('IExtHostSearch');
31 >
32 > export class ExtHostSearch implements IExtHostSearch {
33 >
34 > protected readonly _proxy: MainThreadSearchShape;
35 > protected _handlePool: number;
36 >
37 > private readonly _textSearchProvider: Map<number, vscode.TextSearchProvider2>;
38 > private readonly _textSearchUsedSchemes: Set<string>;
39 >
40 > private readonly _aiTextSearchProvider: Map<number, vscode.AITextSearchProvider>;
41 > private readonly _aiTextSearchUsedSchemes: Set<string>;
42 >
43 > private readonly _fileSearchProvider: Map<number, vscode.FileSearchProvider2>;
44 > private readonly _fileSearchUsedSchemes: Set<string>;
45 >
46 > private readonly _fileSearchManager: FileSearchManager;
47 >
48 > constructor(
49 > @IExtHostRpcService private extHostRpc: IExtHostRpcService,
50 > @IURITransformerService protected _uriTransformer: IURITransformerService,
51 > @ILogService protected _logService: ILogService,
52 > ) {
53 > this._proxy = this.extHostRpc.getProxy(MainContext.MainThreadSearch);
54 > this._handlePool = 0;
55 > this._textSearchProvider = new Map<number, vscode.TextSearchProvider2>();
56 > this._textSearchUsedSchemes = new Set<string>();
57 > this._aiTextSearchProvider = new Map<number, vscode.AITextSearchProvider>();
58 > this._aiTextSearchUsedSchemes = new Set<string>();
59 > this._fileSearchProvider = new Map<number, vscode.FileSearchProvider2>();
60 > this._fileSearchUsedSchemes = new Set<string>();
61 > this._fileSearchManager = new FileSearchManager();
62 > }
63 >
64 > protected _transformScheme(scheme: string): string {
65 > return this._uriTransformer.transformOutgoingScheme(scheme);
66 > }
67 >
68 > registerTextSearchProviderOld(scheme: string, provider: vscode.TextSearchProvider): IDisposable {
69 if (this._textSearchUsedSchemes.has(scheme)) {
70 throw new Error(`a text search provider for the scheme '${scheme}' is already registered`);
81 });
82 }
84 > registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider2): IDisposable {
85 > if (this._textSearchUsedSchemes.has(scheme)) {
86 throw new Error(`a text search provider for the scheme '${scheme}' is already registered`);
87 }
89 > this._textSearchUsedSchemes.add(scheme);
90 > const handle = this._handlePool++;
91 > this._textSearchProvider.set(handle, provider);
92 > this._proxy.$registerTextSearchProvider(handle, this._transformScheme(scheme));
93 > return toDisposable(() => {
94 > this._textSearchUsedSchemes.delete(scheme);
95 > this._textSearchProvider.delete(handle);
96 > this._proxy.$unregisterProvider(handle);
97 > });
98 > }
99 >
100 > registerAITextSearchProvider(scheme: string, provider: vscode.AITextSearchProvider): IDisposable {
101 if (this._aiTextSearchUsedSchemes.has(scheme)) {
102 throw new Error(`an AI text search provider for the scheme '${scheme}'is already registered`);
113 });
114 }
116 > registerFileSearchProviderOld(scheme: string, provider: vscode.FileSearchProvider): IDisposable {
117 if (this._fileSearchUsedSchemes.has(scheme)) {
118 throw new Error(`a file search provider for the scheme '${scheme}' is already registered`);
129 });
130 }
132 > registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider2): IDisposable {
133 if (this._fileSearchUsedSchemes.has(scheme)) {
134 throw new Error(`a file search provider for the scheme '${scheme}' is already registered`);
145 });
146 }
148 > $provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
149 const query = reviveQuery(rawQuery);
150 const provider = this._fileSearchProvider.get(handle);
157 }
158 }
160 > async doInternalFileSearchWithCustomCallback(query: IFileQuery, token: CancellationToken, handleFileMatch: (data: URI[]) => void): Promise<ISearchCompleteStats> {
161 return { messages: [] };
162 }
164 > $clearCache(cacheKey: string): Promise<void> {
165 this._fileSearchManager.clearCache(cacheKey);
166
167 return Promise.resolve(undefined);
168 }
170 > $provideTextSearchResults(handle: number, session: number, rawQuery: IRawTextQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
171 const provider = this._textSearchProvider.get(handle);
172 if (!provider || !provider.provideTextSearchResults) {
178 return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token);
179 }
181 > $provideAITextSearchResults(handle: number, session: number, rawQuery: IRawAITextQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
182 const provider = this._aiTextSearchProvider.get(handle);
183 if (!provider || !provider.provideAITextSearchResults) {
189 return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token, result => this._proxy.$handleKeywordResult(handle, session, result));
190 }
192 > $enableExtensionHostSearch(): void { }
193 >
194 > async $getAIName(handle: number): Promise<string | undefined> {
195 const provider = this._aiTextSearchProvider.get(handle);
196 if (!provider || !provider.provideAITextSearchResults) {
201 return provider.name ?? 'AI';
202 }
204 > protected createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider2): TextSearchManager {
205 return new TextSearchManager({ query, provider }, {
206 readdir: resource => Promise.resolve([]),
208 }, 'textSearchProvider');
209 }
211 > protected createAITextSearchManager(query: IAITextQuery, provider: vscode.AITextSearchProvider): TextSearchManager {
212 return new TextSearchManager({ query, provider }, {
213 readdir: resource => Promise.resolve([]),
215 }, 'aiTextSearchProvider');
216 }
218 >
219 > export function reviveQuery<U extends IRawQuery>(rawQuery: U): U extends IRawTextQuery ? ITextQuery : U extends IRawAITextQuery ? IAITextQuery : IFileQuery {
220 > return {
221 > // eslint-disable-next-line local/code-no-any-casts
222 > ...<any>rawQuery, // TODO@rob ???
223 > ...{
224 > folderQueries: rawQuery.folderQueries && rawQuery.folderQueries.map(reviveFolderQuery),
225 > extraFileResources: rawQuery.extraFileResources && rawQuery.extraFileResources.map(components => URI.revive(components))
226 > }
227 > };
228 > }
229 >
230 function reviveFolderQuery(rawFolderQuery: IFolderQuery<UriComponents>): IFolderQuery<URI> {
231 return revive(rawFolderQuery);
src/vs/workbench/services/search/common/fileSearchManager.ts 112 introduced LOC · 18 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- fileSearchManager.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 * as path from '../../../../base/common/path.js';
7 > import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
8 > import { toErrorMessage } from '../../../../base/common/errorMessage.js';
9 > import * as strings from '../../../../base/common/strings.js';
10 > import * as glob from '../../../../base/common/glob.js';
11 > import * as resources from '../../../../base/common/resources.js';
12 > import { StopWatch } from '../../../../base/common/stopwatch.js';
13 > import { URI } from '../../../../base/common/uri.js';
14 > import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery, QueryGlobTester, resolvePatternsForProvider, hasSiblingFn, excludeToGlobPattern, DEFAULT_MAX_SEARCH_RESULTS } from './search.js';
15 > import { FileSearchProviderFolderOptions, FileSearchProvider2, FileSearchProviderOptions } from './searchExtTypes.js';
16 > import { OldFileSearchProviderConverter } from './searchExtConversionTypes.js';
17 > import { FolderQuerySearchTree } from './folderQuerySearchTree.js';
18 >
19 > interface IInternalFileMatch {
20 > base: URI;
21 > original?: URI;
22 > relativePath?: string; // Not present for extraFiles or absolute path matches
23 > basename: string;
24 > size?: number;
25 > }
26 >
27 > interface IDirectoryEntry {
28 > base: URI;
29 > relativePath: string;
30 > basename: string;
31 > }
32 >
33 > interface FolderQueryInfo {
34 > queryTester: QueryGlobTester;
35 > noSiblingsClauses: boolean;
36 > folder: URI;
37 > tree: IDirectoryTree;
38 > }
39 >
40 > interface IDirectoryTree {
41 > rootEntries: IDirectoryEntry[];
42 > pathToEntries: { [relativePath: string]: IDirectoryEntry[] };
43 > }
44 >
45 > class FileSearchEngine {
46 > private filePattern?: string;
47 > private includePattern?: glob.ParsedExpression;
48 > private maxResults?: number;
49 > private exists?: boolean;
50 > private isLimitHit = false;
51 > private resultCount = 0;
52 > private isCanceled = false;
53 >
54 > private activeCancellationTokens: Set<CancellationTokenSource>;
55 >
56 > private globalExcludePattern?: glob.ParsedExpression;
57 >
58 > constructor(private config: IFileQuery, private provider: FileSearchProvider2, private sessionLifecycle?: SessionLifecycle) {
59 this.filePattern = config.filePattern;
60 const globOptions = config.ignoreGlobCase ? { ignoreCase: true } : undefined;
66 this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern, globOptions);
67 }
69 > cancel(): void {
70 this.isCanceled = true;
71 this.activeCancellationTokens.forEach(t => t.cancel());
72 this.activeCancellationTokens = new Set();
73 }
75 > search(_onResult: (match: IInternalFileMatch) => void): Promise<IInternalSearchComplete> {
76 const folderQueries = this.config.folderQueries || [];
77
115 });
116 }
118 >
119 > private async doSearch(fqs: IFolderQuery<URI>[], onResult: (match: IInternalFileMatch) => void): Promise<IFileSearchProviderStats | null> {
120 const cancellation = new CancellationTokenSource();
121 const folderOptions = fqs.map(fq => this.getSearchOptionsForFolder(fq));
188 }
189 }
191 > private getSearchOptionsForFolder(fq: IFolderQuery<URI>): FileSearchProviderFolderOptions {
192 const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern);
193 let excludePattern = fq.excludePattern?.map(e => ({
215 };
216 }
218 > private initDirectoryTree(): IDirectoryTree {
219 const tree: IDirectoryTree = {
220 rootEntries: [],
224 return tree;
225 }
227 > private addDirectoryEntries({ pathToEntries }: IDirectoryTree, base: URI, relativeFile: string, onResult: (result: IInternalFileMatch) => void) {
228 // Support relative paths to files from a root resource (ignores excludes)
229 if (this.filePattern && strings.equals(relativeFile, this.filePattern, this.config.ignoreGlobCase)) {
249 add(relativeFile);
250 }
252 > private matchDirectoryTree({ rootEntries, pathToEntries }: IDirectoryTree, queryTester: QueryGlobTester, onResult: (result: IInternalFileMatch) => void) {
253 const self = this;
254 const filePattern = this.filePattern;
286 matchDirectory(rootEntries);
287 }
289 > private matchFile(onResult: (result: IInternalFileMatch) => void, candidate: IInternalFileMatch): void {
290 if (!this.includePattern || (candidate.relativePath && this.includePattern(candidate.relativePath, candidate.basename))) {
291 if (this.exists || (this.maxResults && this.resultCount >= this.maxResults)) {
299 }
300 }
302 >
303 > interface IInternalSearchComplete {
304 > limitHit: boolean;
305 > stats?: IFileSearchProviderStats;
306 > }
307 >
308 > /**
309 > * For backwards compatibility, store both a cancellation token and a session object. The session object is the new implementation, where
310 > */
311 > class SessionLifecycle {
312 > private _obj: object | undefined;
313 > public readonly tokenSource: CancellationTokenSource;
314 >
315 > constructor() {
316 this._obj = new Object();
317 this.tokenSource = new CancellationTokenSource();
318 }
320 > public get obj() {
321 if (this._obj) {
322 return this._obj;
325 throw new Error('Session object has been dereferenced.');
326 }
328 > cancel() {
329 this.tokenSource.cancel();
330 this._obj = undefined; // dereference
331 }
333 >
334 > export class FileSearchManager {
335 >
336 > private static readonly BATCH_SIZE = 512;
337 >
338 > private readonly sessions = new Map<string, SessionLifecycle>();
339 >
340 > fileSearch(config: IFileQuery, provider: FileSearchProvider2, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise<ISearchCompleteStats> {
341 const sessionTokenSource = this.getSessionTokenSource(config.cacheKey);
342 const engine = new FileSearchEngine(config, provider, sessionTokenSource);
362 });
363 }
365 > clearCache(cacheKey: string): void {
366 // cancel the token
367 this.sessions.get(cacheKey)?.cancel();
369 this.sessions.delete(cacheKey);
370 }
372 > private getSessionTokenSource(cacheKey: string | undefined): SessionLifecycle | undefined {
373 if (!cacheKey) {
374 return undefined;
381 return this.sessions.get(cacheKey);
382 }
384 > private rawMatchToSearchItem(match: IInternalFileMatch): IFileMatch {
385 if (match.relativePath) {
386 return {
394 }
395 }
397 > private doSearch(engine: FileSearchEngine, batchSize: number, onResultBatch: (matches: IInternalFileMatch[]) => void, token: CancellationToken): Promise<IInternalSearchComplete> {
398 const listener = token.onCancellationRequested(() => {
399 engine.cancel();
src/vs/workbench/services/search/node/rawSearchService.ts 89 introduced LOC · 22 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- rawSearchService.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 * as arrays from '../../../../base/common/arrays.js';
7 > import { CancelablePromise, createCancelablePromise } from '../../../../base/common/async.js';
8 > import { CancellationToken } from '../../../../base/common/cancellation.js';
9 > import { canceled } from '../../../../base/common/errors.js';
10 > import { Emitter, Event } from '../../../../base/common/event.js';
11 > import { compareItemsByFuzzyScore, FuzzyScorerCache, IItemAccessor, prepareQuery } from '../../../../base/common/fuzzyScorer.js';
12 > import { revive } from '../../../../base/common/marshalling.js';
13 > import { basename, dirname, join, sep } from '../../../../base/common/path.js';
14 > import { StopWatch } from '../../../../base/common/stopwatch.js';
15 > import { URI, UriComponents } from '../../../../base/common/uri.js';
16 > import { ByteSize } from '../../../../platform/files/common/files.js';
17 > import { DEFAULT_MAX_SEARCH_RESULTS, ICachedSearchStats, IFileQuery, IFileSearchProgressItem, IFileSearchStats, IFolderQuery, IProgressMessage, IRawFileMatch, IRawFileQuery, IRawQuery, IRawSearchService, IRawTextQuery, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess, isFilePatternMatch, ITextQuery } from '../common/search.js';
18 > import { Engine as FileSearchEngine } from './fileSearch.js';
19 > import { TextSearchEngineAdapter } from './textSearchAdapter.js';
20 >
21 > export type IProgressCallback = (p: ISerializedSearchProgressItem) => void;
22 > type IFileProgressCallback = (p: IFileSearchProgressItem) => void;
23 >
24 > export class SearchService implements IRawSearchService {
25 >
26 > private static readonly BATCH_SIZE = 512;
27 >
28 > private caches: { [cacheKey: string]: Cache } = Object.create(null);
29 >
30 > constructor(private readonly processType: IFileSearchStats['type'] = 'searchProcess', private readonly getNumThreads?: () => Promise<number | undefined>) { }
31 >
32 > fileSearch(config: IRawFileQuery): Event<ISerializedSearchProgressItem | ISerializedSearchComplete> {
33 let promise: CancelablePromise<ISerializedSearchSuccess>;
34
52 return emitter.event;
53 }
55 > textSearch(rawQuery: IRawTextQuery): Event<ISerializedSearchProgressItem | ISerializedSearchComplete> {
56 let promise: CancelablePromise<ISerializedSearchComplete>;
57
74 return emitter.event;
75 }
77 > private async ripgrepTextSearch(config: ITextQuery, progressCallback: IProgressCallback, token: CancellationToken): Promise<ISerializedSearchSuccess> {
78 config.maxFileSize = this.getPlatformFileLimits().maxFileSize;
79 const numThreads = await this.getNumThreads?.();
82 return engine.search(token, progressCallback, progressCallback);
83 }
85 > private getPlatformFileLimits(): { readonly maxFileSize: number } {
86 return {
87 maxFileSize: 16 * ByteSize.GB
88 };
89 }
91 > doFileSearch(config: IFileQuery, numThreads: number | undefined, progressCallback: IProgressCallback, token?: CancellationToken): Promise<ISerializedSearchSuccess> {
92 return this.doFileSearchWithEngine(FileSearchEngine, config, progressCallback, token, SearchService.BATCH_SIZE, numThreads);
93 }
95 > doFileSearchWithEngine(EngineClass: { new(config: IFileQuery, numThreads?: number | undefined): ISearchEngine<IRawFileMatch> }, config: IFileQuery, progressCallback: IProgressCallback, token?: CancellationToken, batchSize = SearchService.BATCH_SIZE, threads?: number): Promise<ISerializedSearchSuccess> {
96 let resultCount = 0;
97 const fileProgressCallback: IFileProgressCallback = progress => {
141 });
142 }
144 > private rawMatchToSearchItem(match: IRawFileMatch): ISerializedFileMatch {
145 return { path: match.base ? join(match.base, match.relativePath) : match.relativePath };
146 }
148 > private doSortedSearch(engine: ISearchEngine<IRawFileMatch>, config: IFileQuery, progressCallback: IProgressCallback, fileProgressCallback: IFileProgressCallback, token?: CancellationToken): Promise<[ISerializedSearchSuccess, IRawFileMatch[]]> {
149 const emitter = new Emitter<IFileSearchProgressItem>();
150
209 });
210 }
212 > private getOrCreateCache(cacheKey: string): Cache {
213 const existing = this.caches[cacheKey];
214 if (existing) {
217 return this.caches[cacheKey] = new Cache();
218 }
220 > private trySortedSearchFromCache(config: IFileQuery, progressCallback: IFileProgressCallback, token?: CancellationToken): Promise<[ISerializedSearchSuccess, IRawFileMatch[]]> | undefined {
221 const cache = config.cacheKey && this.caches[config.cacheKey];
222 if (!cache) {
253 return undefined;
254 }
256 > private sortResults(config: IFileQuery, results: IRawFileMatch[], scorerCache: FuzzyScorerCache, token?: CancellationToken): Promise<IRawFileMatch[]> {
257 // we use the same compare function that is used later when showing the results using fuzzy scoring
258 // this is very important because we are also limiting the number of results by config.maxResults
265 return arrays.topAsync(results, compare, maxResults, 10000, token);
266 }
268 > private sendProgress(results: ISerializedFileMatch[], progressCb: IProgressCallback, batchSize: number) {
269 if (batchSize && batchSize > 0) {
270 for (let i = 0; i < results.length; i += batchSize) {
275 }
276 }
278 > private getResultsFromCache(cache: Cache, searchValue: string, progressCallback: IFileProgressCallback, token?: CancellationToken): Promise<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]> | null {
279 const cacheLookupSW = StopWatch.create(false);
280
339 });
340 }
342 >
343 >
344 > private doSearch(engine: ISearchEngine<IRawFileMatch>, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): Promise<ISearchEngineSuccess> {
345 return new Promise<ISearchEngineSuccess>((c, e) => {
346 let batch: IRawFileMatch[] = [];
376 });
377 }
379 > clearCache(cacheKey: string): Promise<void> {
380 delete this.caches[cacheKey];
381 return Promise.resolve(undefined);
382 }
384 > /**
385 > * Return a CancelablePromise which is not actually cancelable
386 > * TODO@rob - Is this really needed?
387 > */
388 > private preventCancellation<C>(promise: CancelablePromise<C>): CancelablePromise<C> {
389 return new class implements CancelablePromise<C> {
390 get [Symbol.toStringTag]() { return this.toString(); }
403 };
404 }
406 >
407 > interface ICacheRow {
408 > // TODO@roblou - never actually canceled
409 > promise: CancelablePromise<[ISearchEngineSuccess, IRawFileMatch[]]>;
410 > resolved: boolean;
411 > readonly event: Event<IFileSearchProgressItem>;
412 > }
413 >
414 class Cache {
415
417
418 scorerCache: FuzzyScorerCache = Object.create(null);
420 >
421 > const FileMatchItemAccessor = new class implements IItemAccessor<IRawFileMatch> {
422 >
423 > getItemLabel(match: IRawFileMatch): string {
424 return basename(match.relativePath); // e.g. myFile.txt
425 }
427 > getItemDescription(match: IRawFileMatch): string {
428 return dirname(match.relativePath); // e.g. some/path/to/file
429 }
431 > getItemPath(match: IRawFileMatch): string {
432 return match.relativePath; // e.g. some/path/to/file/myFile.txt
433 }
435 >
436 function reviveQuery<U extends IRawQuery>(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery {
437 return {
444 };
445 }
447 function reviveFolderQuery(rawFolderQuery: IFolderQuery<UriComponents>): IFolderQuery<URI> {
448 return revive(rawFolderQuery);
src/vs/workbench/api/node/extHostSearch.ts 86 introduced LOC · 14 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extHostSearch.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 { DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
7 > import { Schemas } from '../../../base/common/network.js';
8 > import { URI } from '../../../base/common/uri.js';
9 > import * as pfs from '../../../base/node/pfs.js';
10 > import { ILogService } from '../../../platform/log/common/log.js';
11 > import { IExtHostConfiguration } from '../common/extHostConfiguration.js';
12 > import { IExtHostInitDataService } from '../common/extHostInitDataService.js';
13 > import { IExtHostRpcService } from '../common/extHostRpcService.js';
14 > import { ExtHostSearch, reviveQuery } from '../common/extHostSearch.js';
15 > import { IURITransformerService } from '../common/extHostUriTransformerService.js';
16 > import { IFileQuery, IRawFileQuery, ISearchCompleteStats, ISerializedSearchProgressItem, isSerializedFileMatch, ITextQuery } from '../../services/search/common/search.js';
17 > import { TextSearchManager } from '../../services/search/common/textSearchManager.js';
18 > import { SearchService } from '../../services/search/node/rawSearchService.js';
19 > import { RipgrepSearchProvider } from '../../services/search/node/ripgrepSearchProvider.js';
20 > import { OutputChannel } from '../../services/search/node/ripgrepSearchUtils.js';
21 > import { NativeTextSearchManager } from '../../services/search/node/textSearchManager.js';
22 > import type * as vscode from 'vscode';
23 >
24 > export class NativeExtHostSearch extends ExtHostSearch implements IDisposable {
25 >
26 > protected _pfs: typeof pfs = pfs; // allow extending for tests
27 >
28 > private _internalFileSearchHandle: number = -1;
29 > private _internalFileSearchProvider: SearchService | null = null;
30 >
31 > private _registeredEHSearchProvider = false;
32 >
33 > private _numThreadsPromise: Promise<number | undefined> | undefined;
34 >
35 > private readonly _disposables = new DisposableStore();
36 >
37 > private isDisposed = false;
38 >
39 > constructor(
40 > @IExtHostRpcService extHostRpc: IExtHostRpcService,
41 > @IExtHostInitDataService initData: IExtHostInitDataService,
42 > @IURITransformerService _uriTransformer: IURITransformerService,
43 > @IExtHostConfiguration private readonly configurationService: IExtHostConfiguration,
44 > @ILogService _logService: ILogService,
45 > ) {
46 > super(extHostRpc, _uriTransformer, _logService);
47 > this.getNumThreads = this.getNumThreads.bind(this);
48 > this.getNumThreadsCached = this.getNumThreadsCached.bind(this);
49 > this.handleConfigurationChanged = this.handleConfigurationChanged.bind(this);
50 > const outputChannel = new OutputChannel('RipgrepSearchUD', this._logService);
51 > this._disposables.add(this.registerTextSearchProvider(Schemas.vscodeUserData, new RipgrepSearchProvider(outputChannel, this.getNumThreadsCached)));
52 > if (initData.remote.isRemote && initData.remote.authority) {
53 this._registerEHSearchProviders();
54 }
56 > configurationService.getConfigProvider().then(provider => {
57 > if (this.isDisposed) {
58 return;
59 }
60 > this._disposables.add(provider.onDidChangeConfiguration(this.handleConfigurationChanged)); extHostSearch.ts
61 > });
62 > }
63 >
64 > private handleConfigurationChanged(event: vscode.ConfigurationChangeEvent) {
65 if (!event.affectsConfiguration('search')) {
66 return;
68 this._numThreadsPromise = undefined;
69 }
71 > async getNumThreads(): Promise<number | undefined> {
72 const configProvider = await this.configurationService.getConfigProvider();
73 const numThreads = configProvider.getConfiguration('search').get<number>('ripgrep.maxThreads');
74 return numThreads;
75 }
77 > async getNumThreadsCached(): Promise<number | undefined> {
78 if (!this._numThreadsPromise) {
79 this._numThreadsPromise = this.getNumThreads();
81 return this._numThreadsPromise;
82 }
84 > dispose(): void {
85 > this.isDisposed = true;
86 > this._disposables.dispose();
87 > }
88 >
89 > override $enableExtensionHostSearch(): void {
90 this._registerEHSearchProviders();
91 }
93 > private _registerEHSearchProviders(): void {
94 if (this._registeredEHSearchProvider) {
95 return;
101 this._disposables.add(this.registerInternalFileSearchProvider(Schemas.file, new SearchService('fileSearchProvider', this.getNumThreadsCached)));
102 }
104 > private registerInternalFileSearchProvider(scheme: string, provider: SearchService): IDisposable {
105 const handle = this._handlePool++;
106 this._internalFileSearchProvider = provider;
112 });
113 }
115 > override $provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
116 const query = reviveQuery(rawQuery);
117 if (handle === this._internalFileSearchHandle) {
126 return super.$provideFileSearchResults(handle, session, rawQuery, token);
127 }
129 > override async doInternalFileSearchWithCustomCallback(rawQuery: IFileQuery, token: vscode.CancellationToken, handleFileMatch: (data: URI[]) => void): Promise<ISearchCompleteStats> {
130 const onResult = (ev: ISerializedSearchProgressItem) => {
131 if (isSerializedFileMatch(ev)) {
149 return <Promise<ISearchCompleteStats>>this._internalFileSearchProvider.doFileSearch(rawQuery, numThreads, onResult, token);
150 }
152 > private async doInternalFileSearch(handle: number, session: number, rawQuery: IFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
153 return this.doInternalFileSearchWithCustomCallback(rawQuery, token, (data) => {
154 this._proxy.$handleFileMatch(handle, session, data);
155 });
156 }
158 > override $clearCache(cacheKey: string): Promise<void> {
159 this._internalFileSearchProvider?.clearCache(cacheKey);
160
161 return super.$clearCache(cacheKey);
162 }
164 > protected override createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider2): TextSearchManager {
165 return new NativeTextSearchManager(query, provider, undefined, 'textSearchProvider');
166 }
src/vs/workbench/services/search/node/ripgrepSearchProvider.ts 29 introduced LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- ripgrepSearchProvider.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 { CancellationTokenSource, CancellationToken } from '../../../../base/common/cancellation.js';
7 > import { OutputChannel } from './ripgrepSearchUtils.js';
8 > import { RipgrepTextSearchEngine } from './ripgrepTextSearchEngine.js';
9 > import { TextSearchProvider2, TextSearchComplete2, TextSearchResult2, TextSearchQuery2, TextSearchProviderOptions, } from '../common/searchExtTypes.js';
10 > import { Progress } from '../../../../platform/progress/common/progress.js';
11 > import { Schemas } from '../../../../base/common/network.js';
12 > import type { RipgrepTextSearchOptions } from '../common/searchExtTypesInternal.js';
13 >
14 > export class RipgrepSearchProvider implements TextSearchProvider2 {
15 > private inProgress: Set<CancellationTokenSource> = new Set();
16 >
17 > constructor(private outputChannel: OutputChannel, private getNumThreads: () => Promise<number | undefined>) {
18 > process.once('exit', () => this.dispose());
19 > }
20 >
21 > async provideTextSearchResults(query: TextSearchQuery2, options: TextSearchProviderOptions, progress: Progress<TextSearchResult2>, token: CancellationToken): Promise<TextSearchComplete2> {
22 const numThreads = await this.getNumThreads();
23 const engine = new RipgrepTextSearchEngine(this.outputChannel, numThreads);
51
52 }
54 > private async withToken<T>(token: CancellationToken, fn: (token: CancellationToken) => Promise<T>): Promise<T> {
55 const merged = mergedTokenSource(token);
56 this.inProgress.add(merged);
60 return result;
61 }
63 > private dispose() {
64 > this.inProgress.forEach(engine => engine.cancel());
65 > }
66 > }
67 >
68 function mergedTokenSource(token: CancellationToken): CancellationTokenSource {
69 const tokenSource = new CancellationTokenSource();
src/vs/workbench/services/search/node/textSearchAdapter.ts 18 introduced LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- textSearchAdapter.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 * as pfs from '../../../../base/node/pfs.js';
8 > import { IFileMatch, IProgressMessage, ITextQuery, ITextSearchMatch, ISerializedFileMatch, ISerializedSearchSuccess, resultIsMatch } from '../common/search.js';
9 > import { RipgrepTextSearchEngine } from './ripgrepTextSearchEngine.js';
10 > import { NativeTextSearchManager } from './textSearchManager.js';
11 >
12 > export class TextSearchEngineAdapter {
13 >
14 > constructor(private query: ITextQuery, private numThreads?: number) { }
15 >
16 > search(token: CancellationToken, onResult: (matches: ISerializedFileMatch[]) => void, onMessage: (message: IProgressMessage) => void): Promise<ISerializedSearchSuccess> {
17 if ((!this.query.folderQueries || !this.query.folderQueries.length) && (!this.query.extraFileResources || !this.query.extraFileResources.length)) {
18 return Promise.resolve({
44 });
45 }
47 >
48 function fileMatchToSerialized(match: IFileMatch): ISerializedFileMatch {
49 return {
src/vs/workbench/api/test/common/testRPCProtocol.ts 8 introduced LOC · 2 ranges

Open complete file

77
78 sync(): Promise<any> {
79 > return new Promise<any>((c) => { testRPCProtocol.ts
80 > setTimeout(c, 0);
81 > }).then(() => {
82 > if (this._callCount === 0) {
83 > return undefined;
84 > }
85 if (!this._idle) {
86 this._idle = new Promise<any>((c, e) => {
89 }
90 return this._idle;
92 > }
93
94 public getProxy<T>(identifier: ProxyIdentifier<T>): Proxied<T> {
src/vs/workbench/api/common/extHostUriTransformerService.ts 7 introduced LOC · 2 ranges

Open complete file

23
24 constructor(delegate: IURITransformer | null) {
25 > if (!delegate) { extHostUriTransformerService.ts
26 > this.transformIncoming = arg => arg;
27 > this.transformOutgoing = arg => arg;
28 > this.transformOutgoingURI = arg => arg;
29 > this.transformOutgoingScheme = arg => arg;
30 > } else {
31 this.transformIncoming = delegate.transformIncoming.bind(delegate);
32 this.transformOutgoing = delegate.transformOutgoing.bind(delegate);