From 21332451859f1cfcf6662346f56f062ffd1d4058 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 2 Sep 2024 11:38:28 +0200 Subject: [PATCH] fix search provider returns invalid URIs --- fs-provider/package.json | 3 +- fs-provider/src/fsProvider.ts | 14 +- .../vscode.proposed.fileSearchProvider.d.ts | 13 +- .../vscode.proposed.textSearchProvider.d.ts | 281 ++++++++++++++++++ sample/src/web/test/suite/search.test.ts | 2 +- 5 files changed, 301 insertions(+), 12 deletions(-) rename fs-provider/{types => }/vscode.proposed.fileSearchProvider.d.ts (77%) create mode 100644 fs-provider/vscode.proposed.textSearchProvider.d.ts diff --git a/fs-provider/package.json b/fs-provider/package.json index e037553..f8ef616 100644 --- a/fs-provider/package.json +++ b/fs-provider/package.json @@ -17,7 +17,8 @@ "onSearch:vscode-test-web" ], "enabledApiProposals": [ - "fileSearchProvider" + "fileSearchProvider", + "textSearchProvider" ], "contributes": { "resourceLabelFormatters": [ diff --git a/fs-provider/src/fsProvider.ts b/fs-provider/src/fsProvider.ts index 3f74245..6854a83 100644 --- a/fs-provider/src/fsProvider.ts +++ b/fs-provider/src/fsProvider.ts @@ -141,17 +141,21 @@ export class MemFileSystemProvider implements FileSystemProvider, FileSearchProv async provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): Promise { const pattern = query.pattern; + // Pattern is always blank: https://github.com/microsoft/vscode/issues/200892 const glob = pattern ? new Minimatch(pattern) : undefined; const result: Uri[] = []; - const dive = async (currentDirectory: Directory, pathSegments: string[] = []) => { - for (const [name, entry] of await currentDirectory.entries) { + const dive = async (folderUri: Uri) => { + const directory = await this._lookupAsDirectory(folderUri, false); + for (const [name, entry] of await directory.entries) { + /* support options.includes && options.excludes */ + if (typeof options.maxResults !== 'undefined' && result.length >= options.maxResults) { break; } - const uri = Uri.joinPath(this.extensionUri, ...pathSegments, entry.name); + const uri = Uri.joinPath(folderUri, entry.name); if (entry.type === FileType.File) { const toMatch = uri.toString(); // Pattern is always blank: https://github.com/microsoft/vscode/issues/200892 @@ -159,12 +163,12 @@ export class MemFileSystemProvider implements FileSystemProvider, FileSearchProv result.push(uri); } } else if (entry.type === FileType.Directory) { - await dive(entry, [...pathSegments, name]); + await dive(uri); } } }; - await dive(this.root); + await dive(options.folder); return result; } diff --git a/fs-provider/types/vscode.proposed.fileSearchProvider.d.ts b/fs-provider/vscode.proposed.fileSearchProvider.d.ts similarity index 77% rename from fs-provider/types/vscode.proposed.fileSearchProvider.d.ts rename to fs-provider/vscode.proposed.fileSearchProvider.d.ts index 7c62077..8dcfd99 100644 --- a/fs-provider/types/vscode.proposed.fileSearchProvider.d.ts +++ b/fs-provider/vscode.proposed.fileSearchProvider.d.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ declare module 'vscode' { + // https://github.com/microsoft/vscode/issues/73524 /** @@ -12,6 +13,12 @@ declare module 'vscode' { export interface FileSearchQuery { /** * The search pattern to match against file paths. + * To be correctly interpreted by Quick Open, this is interpreted in a relaxed way. The picker will apply its own highlighting and scoring on the results. + * + * Tips for matching in Quick Open: + * With the pattern, the picker will use the file name and file paths to score each entry. The score will determine the ordering and filtering. + * The scoring prioritizes prefix and substring matching. Then, it checks and it checks whether the pattern's letters appear in the same order as in the target (file name and path). + * If a file does not match at all using our criteria, it will be omitted from Quick Open. */ pattern: string; } @@ -48,11 +55,7 @@ declare module 'vscode' { * @param options A set of options to consider while searching files. * @param token A cancellation token. */ - provideFileSearchResults( - query: FileSearchQuery, - options: FileSearchOptions, - token: CancellationToken - ): ProviderResult; + provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; } export namespace workspace { diff --git a/fs-provider/vscode.proposed.textSearchProvider.d.ts b/fs-provider/vscode.proposed.textSearchProvider.d.ts new file mode 100644 index 0000000..5d4b2b5 --- /dev/null +++ b/fs-provider/vscode.proposed.textSearchProvider.d.ts @@ -0,0 +1,281 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/59921 + + /** + * The parameters of a query for text search. + */ + export interface TextSearchQuery { + /** + * The text pattern to search for. + */ + pattern: string; + + /** + * Whether or not `pattern` should match multiple lines of text. + */ + isMultiline?: boolean; + + /** + * Whether or not `pattern` should be interpreted as a regular expression. + */ + isRegExp?: boolean; + + /** + * Whether or not the search should be case-sensitive. + */ + isCaseSensitive?: boolean; + + /** + * Whether or not to search for whole word matches only. + */ + isWordMatch?: boolean; + } + + /** + * A file glob pattern to match file paths against. + * TODO@roblourens merge this with the GlobPattern docs/definition in vscode.d.ts. + * @see {@link GlobPattern} + */ + export type GlobString = string; + + /** + * Options common to file and text search + */ + export interface SearchOptions { + /** + * The root folder to search within. + */ + folder: Uri; + + /** + * Files that match an `includes` glob pattern should be included in the search. + */ + includes: GlobString[]; + + /** + * Files that match an `excludes` glob pattern should be excluded from the search. + */ + excludes: GlobString[]; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles: boolean; + + /** + * Whether files in parent directories that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useParentIgnoreFiles"`. + */ + useParentIgnoreFiles: boolean; + } + + /** + * Options to specify the size of the result text preview. + * These options don't affect the size of the match itself, just the amount of preview text. + */ + export interface TextSearchPreviewOptions { + /** + * The maximum number of lines in the preview. + * Only search providers that support multiline search will ever return more than one line in the match. + */ + matchLines: number; + + /** + * The maximum number of characters included per line. + */ + charsPerLine: number; + } + + /** + * Options that apply to text search. + */ + export interface TextSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults: number; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Exclude files larger than `maxFileSize` in bytes. + */ + maxFileSize?: number; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + /** + * Represents the severity of a TextSearchComplete message. + */ + export enum TextSearchCompleteMessageType { + Information = 1, + Warning = 2, + } + + /** + * A message regarding a completed search. + */ + export interface TextSearchCompleteMessage { + /** + * Markdown text of the message. + */ + text: string; + /** + * Whether the source of the message is trusted, command links are disabled for untrusted message sources. + * Messaged are untrusted by default. + */ + trusted?: boolean; + /** + * The message type, this affects how the message will be rendered. + */ + type: TextSearchCompleteMessageType; + } + + /** + * Information collected when text search is complete. + */ + export interface TextSearchComplete { + /** + * Whether the search hit the limit on the maximum number of search results. + * `maxResults` on {@linkcode TextSearchOptions} specifies the max number of results. + * - If exactly that number of matches exist, this should be false. + * - If `maxResults` matches are returned and more exist, this should be true. + * - If search hits an internal limit which is less than `maxResults`, this should be true. + */ + limitHit?: boolean; + + /** + * Additional information regarding the state of the completed search. + * + * Messages with "Information" style support links in markdown syntax: + * - Click to [run a command](command:workbench.action.OpenQuickPick) + * - Click to [open a website](https://aka.ms) + * + * Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again. + */ + message?: TextSearchCompleteMessage | TextSearchCompleteMessage[]; + } + + /** + * A preview of the text result. + */ + export interface TextSearchMatchPreview { + /** + * The matching lines of text, or a portion of the matching line that contains the match. + */ + text: string; + + /** + * The Range within `text` corresponding to the text of the match. + * The number of matches must match the TextSearchMatch's range property. + */ + matches: Range | Range[]; + } + + /** + * A match from a text search + */ + export interface TextSearchMatch { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * The range of the match within the document, or multiple ranges for multiple matches. + */ + ranges: Range | Range[]; + + /** + * A preview of the text match. + */ + preview: TextSearchMatchPreview; + } + + /** + * A line of context surrounding a TextSearchMatch. + */ + export interface TextSearchContext { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * One line of text. + * previewOptions.charsPerLine applies to this + */ + text: string; + + /** + * The line number of this line of context. + */ + lineNumber: number; + } + + export type TextSearchResult = TextSearchMatch | TextSearchContext; + + /** + * A TextSearchProvider provides search results for text results inside files in the workspace. + */ + export interface TextSearchProvider { + /** + * Provide results that match the given text pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * Register a text search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; + } +} diff --git a/sample/src/web/test/suite/search.test.ts b/sample/src/web/test/suite/search.test.ts index b30238f..f5fc6de 100644 --- a/sample/src/web/test/suite/search.test.ts +++ b/sample/src/web/test/suite/search.test.ts @@ -43,7 +43,7 @@ suite('Workspace search', () => { } // commented out because of https://github.com/microsoft/vscode/issues/227248 - test.skip('Find files', async () => { + test('Find files', async () => { debugger; await assertEntries('/folder', ['x.txt'], ['.bar']); await assertEntries('/folder/', ['x.txt'], ['.bar']);