mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-13 23:44:09 +01:00
add tests and bugfixes - search API (#227652)
* add tests and bugfixes * restore non-disposable file manager * remove old test
This commit is contained in:
@@ -141,7 +141,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
|
||||
// --- search ---
|
||||
|
||||
$startFileSearch(_includeFolder: UriComponents | null, options: IFileQueryBuilderOptions, token: CancellationToken): Promise<UriComponents[] | null> {
|
||||
$startFileSearch(_includeFolder: UriComponents | null, options: IFileQueryBuilderOptions<UriComponents>, token: CancellationToken): Promise<UriComponents[] | null> {
|
||||
const includeFolder = URI.revive(_includeFolder);
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
|
||||
@@ -160,7 +160,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
});
|
||||
}
|
||||
|
||||
$startTextSearch(pattern: IPatternInfo, _folder: UriComponents | null, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete | null> {
|
||||
$startTextSearch(pattern: IPatternInfo, _folder: UriComponents | null, options: ITextQueryBuilderOptions<UriComponents>, requestId: number, token: CancellationToken): Promise<ITextSearchComplete | null> {
|
||||
const folder = URI.revive(_folder);
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
const folders = folder ? [folder] : workspace.folders.map(folder => folder.uri);
|
||||
|
||||
@@ -987,9 +987,9 @@ interface IExtensionListener<E> {
|
||||
(e: E): any;
|
||||
}
|
||||
|
||||
function globsToISearchPatternBuilder(excludes: vscode.GlobPattern[] | undefined): ISearchPatternBuilder[] {
|
||||
function globsToISearchPatternBuilder(excludes: vscode.GlobPattern[] | undefined): ISearchPatternBuilder<URI>[] {
|
||||
return (
|
||||
excludes?.map((exclude): ISearchPatternBuilder | undefined => {
|
||||
excludes?.map((exclude): ISearchPatternBuilder<URI> | undefined => {
|
||||
if (typeof exclude === 'string') {
|
||||
if (exclude === '') {
|
||||
return undefined;
|
||||
@@ -997,7 +997,7 @@ function globsToISearchPatternBuilder(excludes: vscode.GlobPattern[] | undefined
|
||||
return {
|
||||
pattern: exclude,
|
||||
uri: undefined
|
||||
} satisfies ISearchPatternBuilder;
|
||||
} satisfies ISearchPatternBuilder<URI>;
|
||||
} else {
|
||||
const parsedExclude = parseSearchExcludeInclude(exclude);
|
||||
if (!parsedExclude) {
|
||||
@@ -1006,8 +1006,8 @@ function globsToISearchPatternBuilder(excludes: vscode.GlobPattern[] | undefined
|
||||
return {
|
||||
pattern: parsedExclude.pattern,
|
||||
uri: parsedExclude.folder
|
||||
} satisfies ISearchPatternBuilder;
|
||||
} satisfies ISearchPatternBuilder<URI>;
|
||||
}
|
||||
}) ?? []
|
||||
).filter((e): e is ISearchPatternBuilder => !!e);
|
||||
).filter((e): e is ISearchPatternBuilder<URI> => !!e);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { MainThreadWorkspace } from '../../browser/mainThreadWorkspace.js';
|
||||
import { SingleProxyRPCProtocol } from '../common/testRPCProtocol.js';
|
||||
import { IFileQuery, ISearchService } from '../../../services/search/common/search.js';
|
||||
import { workbenchInstantiationService } from '../../../test/browser/workbenchTestServices.js';
|
||||
import { URI, UriComponents } from '../../../../base/common/uri.js';
|
||||
|
||||
suite('MainThreadWorkspace', () => {
|
||||
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
|
||||
@@ -123,4 +124,23 @@ suite('MainThreadWorkspace', () => {
|
||||
const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } })));
|
||||
return mtw.$startFileSearch(null, { maxResults: 10, includePattern: '', excludePattern: [{ pattern: 'exclude/**' }], disregardSearchExcludeSettings: true }, CancellationToken.None);
|
||||
});
|
||||
test('Valid revived URI after moving to EH', () => {
|
||||
const uriComponents: UriComponents = {
|
||||
scheme: 'test',
|
||||
path: '/Users/username/Downloads',
|
||||
};
|
||||
instantiationService.stub(ISearchService, {
|
||||
fileSearch(query: IFileQuery) {
|
||||
assert.strictEqual(query.folderQueries?.length, 1);
|
||||
assert.ok(URI.isUri(query.folderQueries[0].folder));
|
||||
assert.strictEqual(query.folderQueries[0].folder.path, '/Users/username/Downloads');
|
||||
assert.strictEqual(query.folderQueries[0].folder.scheme, 'test');
|
||||
|
||||
return Promise.resolve({ results: [], messages: [] });
|
||||
}
|
||||
});
|
||||
|
||||
const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } })));
|
||||
return mtw.$startFileSearch(uriComponents, { filePattern: '*.md' }, CancellationToken.None);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -263,6 +263,28 @@ suite('ExtHostSearch', () => {
|
||||
assert(!results.length);
|
||||
});
|
||||
|
||||
test('session cancellation should work', async () => {
|
||||
let numSessionCancelled = 0;
|
||||
const disposables: (vscode.Disposable | undefined)[] = [];
|
||||
await registerTestFileSearchProvider({
|
||||
provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Promise<URI[]> {
|
||||
|
||||
disposables.push(options.session?.onCancellationRequested(() => {
|
||||
numSessionCancelled++;
|
||||
}));
|
||||
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
await runFileSearch({ ...getSimpleQuery(), cacheKey: '1' }, true);
|
||||
await runFileSearch({ ...getSimpleQuery(), cacheKey: '2' }, true);
|
||||
extHostSearch.$clearCache('1');
|
||||
assert.strictEqual(numSessionCancelled, 1);
|
||||
disposables.forEach(d => d?.dispose());
|
||||
});
|
||||
|
||||
test('provider returns null', async () => {
|
||||
await registerTestFileSearchProvider({
|
||||
provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Promise<URI[]> {
|
||||
@@ -702,6 +724,24 @@ suite('ExtHostSearch', () => {
|
||||
const { results } = await runFileSearch(query);
|
||||
compareURIs(results, reportedResults);
|
||||
});
|
||||
test('if onlyFileScheme is set, do not call custom schemes', async () => {
|
||||
let fancySchemeCalled = false;
|
||||
await registerTestFileSearchProvider({
|
||||
provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Promise<URI[]> {
|
||||
fancySchemeCalled = true;
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}, fancyScheme);
|
||||
|
||||
const query: ISearchQuery = {
|
||||
type: QueryType.File,
|
||||
filePattern: '',
|
||||
folderQueries: []
|
||||
};
|
||||
|
||||
await runFileSearch(query);
|
||||
assert(!fancySchemeCalled);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Text:', () => {
|
||||
|
||||
@@ -13,7 +13,6 @@ import { URI } from '../../../../base/common/uri.js';
|
||||
import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery, QueryGlobTester, resolvePatternsForProvider, hasSiblingFn, excludeToGlobPattern, DEFAULT_MAX_SEARCH_RESULTS } from './search.js';
|
||||
import { FileSearchProviderFolderOptions, FileSearchProviderNew, FileSearchProviderOptions } from './searchExtTypes.js';
|
||||
import { TernarySearchTree } from '../../../../base/common/ternarySearchTree.js';
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { OldFileSearchProviderConverter } from './searchExtConversionTypes.js';
|
||||
|
||||
interface IInternalFileMatch {
|
||||
@@ -307,19 +306,26 @@ interface IInternalSearchComplete {
|
||||
/**
|
||||
* For backwards compatibility, store both a cancellation token and a session object. The session object is the new implementation, where
|
||||
*/
|
||||
class SessionLifecycle extends Disposable {
|
||||
public readonly obj: object;
|
||||
class SessionLifecycle {
|
||||
private _obj: object | undefined;
|
||||
public readonly tokenSource: CancellationTokenSource;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.obj = new Object();
|
||||
this._obj = new Object();
|
||||
this.tokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
public override dispose(): void {
|
||||
public get obj() {
|
||||
if (this._obj) {
|
||||
return this._obj;
|
||||
}
|
||||
|
||||
throw new Error('Session object has been dereferenced.');
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.tokenSource.cancel();
|
||||
super.dispose();
|
||||
this._obj = undefined; // dereference
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +362,7 @@ export class FileSearchManager {
|
||||
|
||||
clearCache(cacheKey: string): void {
|
||||
// cancel the token
|
||||
this.sessions.get(cacheKey)?.dispose();
|
||||
this.sessions.get(cacheKey)?.cancel();
|
||||
// with no reference to this, it will be removed from WeakMaps
|
||||
this.sessions.delete(cacheKey);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import * as path from '../../../../base/common/path.js';
|
||||
import { isEqual, basename, relativePath, isAbsolutePath } from '../../../../base/common/resources.js';
|
||||
import * as strings from '../../../../base/common/strings.js';
|
||||
import { assertIsDefined, isDefined } from '../../../../base/common/types.js';
|
||||
import { URI, URI as uri } from '../../../../base/common/uri.js';
|
||||
import { URI, URI as uri, UriComponents } from '../../../../base/common/uri.js';
|
||||
import { isMultilineRegexSource } from '../../../../editor/common/model/textModelSearch.js';
|
||||
import * as nls from '../../../../nls.js';
|
||||
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
|
||||
@@ -43,16 +43,16 @@ export interface ISearchPathPattern {
|
||||
|
||||
type ISearchPathPatternBuilder = string | string[];
|
||||
|
||||
export interface ISearchPatternBuilder {
|
||||
uri?: uri;
|
||||
export interface ISearchPatternBuilder<U extends UriComponents> {
|
||||
uri?: U;
|
||||
pattern: ISearchPathPatternBuilder;
|
||||
}
|
||||
|
||||
export function isISearchPatternBuilder(object: ISearchPatternBuilder | ISearchPathPatternBuilder): object is ISearchPatternBuilder {
|
||||
export function isISearchPatternBuilder<U extends UriComponents>(object: ISearchPatternBuilder<U> | ISearchPathPatternBuilder): object is ISearchPatternBuilder<U> {
|
||||
return (typeof object === 'object' && 'uri' in object && 'pattern' in object);
|
||||
}
|
||||
|
||||
export function globPatternToISearchPatternBuilder(globPattern: GlobPattern): ISearchPatternBuilder {
|
||||
export function globPatternToISearchPatternBuilder(globPattern: GlobPattern): ISearchPatternBuilder<URI> {
|
||||
|
||||
if (typeof globPattern === 'string') {
|
||||
return {
|
||||
@@ -74,11 +74,11 @@ export interface ISearchPathsInfo {
|
||||
pattern?: glob.IExpression;
|
||||
}
|
||||
|
||||
interface ICommonQueryBuilderOptions {
|
||||
interface ICommonQueryBuilderOptions<U extends UriComponents = URI> {
|
||||
_reason?: string;
|
||||
excludePattern?: ISearchPatternBuilder[];
|
||||
excludePattern?: ISearchPatternBuilder<U>[];
|
||||
includePattern?: ISearchPathPatternBuilder;
|
||||
extraFileResources?: uri[];
|
||||
extraFileResources?: U[];
|
||||
|
||||
/** Parse the special ./ syntax supported by the searchview, and expand foo to ** /foo */
|
||||
expandPatterns?: boolean;
|
||||
@@ -95,7 +95,7 @@ interface ICommonQueryBuilderOptions {
|
||||
onlyFileScheme?: boolean;
|
||||
}
|
||||
|
||||
export interface IFileQueryBuilderOptions extends ICommonQueryBuilderOptions {
|
||||
export interface IFileQueryBuilderOptions<U extends UriComponents = URI> extends ICommonQueryBuilderOptions<U> {
|
||||
filePattern?: string;
|
||||
exists?: boolean;
|
||||
sortByScore?: boolean;
|
||||
@@ -103,7 +103,7 @@ export interface IFileQueryBuilderOptions extends ICommonQueryBuilderOptions {
|
||||
shouldGlobSearch?: boolean;
|
||||
}
|
||||
|
||||
export interface ITextQueryBuilderOptions extends ICommonQueryBuilderOptions {
|
||||
export interface ITextQueryBuilderOptions<U extends UriComponents = URI> extends ICommonQueryBuilderOptions<U> {
|
||||
previewOptions?: ITextSearchPreviewOptions;
|
||||
fileEncoding?: string;
|
||||
surroundingContext?: number;
|
||||
|
||||
Reference in New Issue
Block a user