Skip to content

Commit

Permalink
fix search provider returns invalid URIs
Browse files Browse the repository at this point in the history
  • Loading branch information
aeschli committed Sep 2, 2024
1 parent 72d87cd commit 2133245
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 12 deletions.
3 changes: 2 additions & 1 deletion fs-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"onSearch:vscode-test-web"
],
"enabledApiProposals": [
"fileSearchProvider"
"fileSearchProvider",
"textSearchProvider"
],
"contributes": {
"resourceLabelFormatters": [
Expand Down
14 changes: 9 additions & 5 deletions fs-provider/src/fsProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,30 +141,34 @@ export class MemFileSystemProvider implements FileSystemProvider, FileSearchProv

async provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): Promise<Uri[]> {
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
if (!glob || glob.match(toMatch)) {
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

declare module 'vscode' {

// https://github.com/microsoft/vscode/issues/73524

/**
Expand All @@ -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;
}
Expand Down Expand Up @@ -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<Uri[]>;
provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult<Uri[]>;
}

export namespace workspace {
Expand Down
281 changes: 281 additions & 0 deletions fs-provider/vscode.proposed.textSearchProvider.d.ts
Original file line number Diff line number Diff line change
@@ -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<TextSearchResult>, token: CancellationToken): ProviderResult<TextSearchComplete>;
}

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;
}
}
2 changes: 1 addition & 1 deletion sample/src/web/test/suite/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down

0 comments on commit 2133245

Please sign in to comment.