From 8b7e604593c24c21a5af2ae43c33036366c05f13 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 14 Nov 2022 11:55:09 -0800 Subject: [PATCH 1/6] Escape special glob chars when using "Find in Folder" Fixes #82415 --- .../services/search/common/queryBuilder.ts | 14 +++++++- .../search/test/common/queryBuilder.test.ts | 34 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/vs/workbench/services/search/test/common/queryBuilder.test.ts diff --git a/src/vs/workbench/services/search/common/queryBuilder.ts b/src/vs/workbench/services/search/common/queryBuilder.ts index 02d6c607ade7b..eb53c3f3efaad 100644 --- a/src/vs/workbench/services/search/common/queryBuilder.ts +++ b/src/vs/workbench/services/search/common/queryBuilder.ts @@ -580,6 +580,18 @@ function normalizeGlobPattern(pattern: string): string { .replace(/\/+$/g, ''); } +/** + * Escapes a path for use as a glob pattern that would match the input precisely. + * Characters '?', '*', '[', and ']' are escaped into character range glob syntax + * (for example, '?' becomes '[?]'). + * NOTE: This implementation has makes no special cases for UNC paths. For example, + * given the input "//?/C:A?.txt", this would produce output '//[?]/C:/A[?].txt', + * which may not be desirable in some cases. Use with caution if UNC paths could be expected. + */ +function escapeGlobPattern(path: string): string { + return path.replace(/([?*[\]])/g, '[$1]'); +} + /** * Construct an include pattern from a list of folders uris to search in. */ @@ -618,7 +630,7 @@ export function resolveResourcesForSearchIncludes(resources: URI[], contextServi } if (folderPath) { - folderPaths.push(folderPath); + folderPaths.push(escapeGlobPattern(folderPath)); } }); } diff --git a/src/vs/workbench/services/search/test/common/queryBuilder.test.ts b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts new file mode 100644 index 0000000000000..e6992e6fa270e --- /dev/null +++ b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as assert from 'assert'; +import { URI } from 'vs/base/common/uri'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; +import { resolveResourcesForSearchIncludes } from 'vs/workbench/services/search/common/queryBuilder'; +import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; + +suite('QueryBuilderCommon', () => { + let context: IWorkspaceContextService; + + setup(() => { + const workspace = testWorkspace(URI.file("C:\\testWorkspace")); + context = new TestContextService(workspace); + }); + + test('resolveResourcesForSearchIncludes passes through paths without special glob characters', () => { + const actual = resolveResourcesForSearchIncludes([URI.file("C:\\testWorkspace\\pages\\blog")], context); + assert.strictEqual(actual.length, 1); + assert.strictEqual(actual[0], "./pages/blog"); + }); + + test('resolveResourcesForSearchIncludes escapes paths with special characters', () => { + const workspace = testWorkspace(URI.file("C:\\testWorkspace")); + const context = new TestContextService(workspace); + + const actual = resolveResourcesForSearchIncludes([URI.file("C:\\testWorkspace\\pages\\blog\\[postId]")], context); + assert.strictEqual(actual.length, 1); + assert.strictEqual(actual[0], "./pages/blog/[[]postId[]]"); + }); +}); From 357f7fd844b60a7f836187549bcbfdcdf48fa2fe Mon Sep 17 00:00:00 2001 From: David Date: Mon, 14 Nov 2022 18:47:24 -0300 Subject: [PATCH 2/6] Cleaned up test --- .../workbench/services/search/test/common/queryBuilder.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/services/search/test/common/queryBuilder.test.ts b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts index e6992e6fa270e..b0258bc425091 100644 --- a/src/vs/workbench/services/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts @@ -24,9 +24,6 @@ suite('QueryBuilderCommon', () => { }); test('resolveResourcesForSearchIncludes escapes paths with special characters', () => { - const workspace = testWorkspace(URI.file("C:\\testWorkspace")); - const context = new TestContextService(workspace); - const actual = resolveResourcesForSearchIncludes([URI.file("C:\\testWorkspace\\pages\\blog\\[postId]")], context); assert.strictEqual(actual.length, 1); assert.strictEqual(actual[0], "./pages/blog/[[]postId[]]"); From cbfe73c37c34482c5207594db4da9c70fe2c7a1a Mon Sep 17 00:00:00 2001 From: David Date: Mon, 14 Nov 2022 18:50:21 -0300 Subject: [PATCH 3/6] More cleanups --- .../services/search/test/common/queryBuilder.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/search/test/common/queryBuilder.test.ts b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts index b0258bc425091..e0416a1c1b262 100644 --- a/src/vs/workbench/services/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts @@ -19,13 +19,11 @@ suite('QueryBuilderCommon', () => { test('resolveResourcesForSearchIncludes passes through paths without special glob characters', () => { const actual = resolveResourcesForSearchIncludes([URI.file("C:\\testWorkspace\\pages\\blog")], context); - assert.strictEqual(actual.length, 1); - assert.strictEqual(actual[0], "./pages/blog"); + assert.deepStrictEqual(actual, ["./pages/blog"]); }); test('resolveResourcesForSearchIncludes escapes paths with special characters', () => { const actual = resolveResourcesForSearchIncludes([URI.file("C:\\testWorkspace\\pages\\blog\\[postId]")], context); - assert.strictEqual(actual.length, 1); - assert.strictEqual(actual[0], "./pages/blog/[[]postId[]]"); + assert.deepStrictEqual(actual, ["./pages/blog/[[]postId[]]"]); }); }); From 526c2de71ca891582f999b15160cb8711dc963a8 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 20 Nov 2022 23:52:53 -0300 Subject: [PATCH 4/6] Fix tests on Linux --- .../services/search/test/common/queryBuilder.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/search/test/common/queryBuilder.test.ts b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts index e0416a1c1b262..a650ae9174e12 100644 --- a/src/vs/workbench/services/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; +import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { resolveResourcesForSearchIncludes } from 'vs/workbench/services/search/common/queryBuilder'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; @@ -13,8 +13,7 @@ suite('QueryBuilderCommon', () => { let context: IWorkspaceContextService; setup(() => { - const workspace = testWorkspace(URI.file("C:\\testWorkspace")); - context = new TestContextService(workspace); + context = new TestContextService(TestWorkspace); }); test('resolveResourcesForSearchIncludes passes through paths without special glob characters', () => { From 6bd570ca978f16bec810859cb6cba7bfb088a5a6 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 21 Nov 2022 00:00:23 -0300 Subject: [PATCH 5/6] Third time is the charm --- .../services/search/test/common/queryBuilder.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/search/test/common/queryBuilder.test.ts b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts index a650ae9174e12..f492eb080557d 100644 --- a/src/vs/workbench/services/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; +import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { resolveResourcesForSearchIncludes } from 'vs/workbench/services/search/common/queryBuilder'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; @@ -13,16 +14,17 @@ suite('QueryBuilderCommon', () => { let context: IWorkspaceContextService; setup(() => { - context = new TestContextService(TestWorkspace); + const workspace = testWorkspace(URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace')); + context = new TestContextService(workspace); }); test('resolveResourcesForSearchIncludes passes through paths without special glob characters', () => { - const actual = resolveResourcesForSearchIncludes([URI.file("C:\\testWorkspace\\pages\\blog")], context); + const actual = resolveResourcesForSearchIncludes([URI.file(isWindows ? "C:\\testWorkspace\\pages\\blog" : "/testWorkspace/pages/blog")], context); assert.deepStrictEqual(actual, ["./pages/blog"]); }); test('resolveResourcesForSearchIncludes escapes paths with special characters', () => { - const actual = resolveResourcesForSearchIncludes([URI.file("C:\\testWorkspace\\pages\\blog\\[postId]")], context); + const actual = resolveResourcesForSearchIncludes([URI.file(isWindows ? "C:\\testWorkspace\\pages\\blog\\[postId]" : "/testWorkspace/pages/blog/[postId]")], context); assert.deepStrictEqual(actual, ["./pages/blog/[[]postId[]]"]); }); }); From 3c275da9a161f38a545ed5b566d49d447e98cdfd Mon Sep 17 00:00:00 2001 From: David Date: Tue, 29 Nov 2022 02:18:21 -0800 Subject: [PATCH 6/6] Fix comment typos --- src/vs/workbench/services/search/common/queryBuilder.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/search/common/queryBuilder.ts b/src/vs/workbench/services/search/common/queryBuilder.ts index eb53c3f3efaad..0fdb121c0cc76 100644 --- a/src/vs/workbench/services/search/common/queryBuilder.ts +++ b/src/vs/workbench/services/search/common/queryBuilder.ts @@ -584,8 +584,8 @@ function normalizeGlobPattern(pattern: string): string { * Escapes a path for use as a glob pattern that would match the input precisely. * Characters '?', '*', '[', and ']' are escaped into character range glob syntax * (for example, '?' becomes '[?]'). - * NOTE: This implementation has makes no special cases for UNC paths. For example, - * given the input "//?/C:A?.txt", this would produce output '//[?]/C:/A[?].txt', + * NOTE: This implementation makes no special cases for UNC paths. For example, + * given the input "//?/C:/A?.txt", this would produce output '//[?]/C:/A[?].txt', * which may not be desirable in some cases. Use with caution if UNC paths could be expected. */ function escapeGlobPattern(path: string): string {