Repository: microsoft/vscode (TypeScript) — Bug Fix

Issue:
"files to include" doesn't work with folder names containing glob characters

Issue Type: <b>Bug</b>

1. Create a workspace add the following folder, plus couple of other folders

```
//...
    {
      "path": "C:\\Users\\...\\folder1",
    },
    {
      "path": "C:\\Users\\...\\folderNamed",
      "name": "folderNamed (Special Folder)"
    },
    {
      "path": "C:\\Users\\...\\folder2",
    }
//...
```
2. go to explorer view tab
3. create same file with `lorem-ipsum` text in all the folders
4. right click on the `folderNamed (Special Folder)`
5. click on `File in Folder...`
6. ensure that **files to include** set as `./folderNamed (Special Folder)`
7. type `lorem` in the folder into the **Search** box
8. you will see that it retrieves from **folder1** and **folder2** too

where it should only retrieve from `folderNamed (Special Folder)`.
behaviour works fine on `folder1` and `folder2` since they are not named.

VS Code version: Code 1.39.1 (88f15d17dca836346e787762685a40bb5cce75a8, 2019-10-10T23:31:28.683Z)
OS version: Windows_NT x64 10.0.18362

<details>
<summary>System Info</summary>

|Item|Value|
|---|---|
|CPUs|Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz (16 x 3600)|
|GPU Status|2d_canvas: enabled<br>flash_3d: enabled<br>flash_stage3d: enabled<br>flash_stage3d_baseline: enabled<br>gpu_compositing: enabled<br>multiple_raster_threads: enabled_on<br>native_gpu_memory_buffers: disabled_software<br>oop_rasterization: disabled_off<br>protected_video_decode: unavailable_off<br>rasterization: enabled<br>skia_deferred_display_list: disabled_off<br>skia_renderer: disabled_off<br>surface_synchronization: enabled_on<br>video_decode: enabled<br>viz_display_compositor: disabled_off<br>webgl: enabled<br>webgl2: enabled|
|Load (avg)|undefined|
|Memory (System)|63.81GB (39.06GB free)|
|Process Argv||
|Screen Reader|no|
|VM|0%|
</details><details><summary>Extensions (86)</summary>

Extension|Author (truncated)|Version
---|---|---
better-comments|aar|2.0.5
markdown-imsize|amo|0.0.2
azds|azu|1.0.120191007
emojisense|bie|0.5.3
markdown-mermaid|bie|1.3.0
path-intellisense|chr|1.4.2
vscode-markdownlint|Dav|0.31.0
vscode-eslint|dba|1.9.1
githistory|don|0.4.6
xml|Dot|2.5.0
gitlens|eam|10.1.1
EditorConfig|Edi|0.14.1
prettier-vscode|esb|2.3.0
auto-close-tag|for|0.5.6
auto-rename-tag|for|0.1.0
dotnet-test-explorer|for|0.7.1
vscode-pull-request-github|Git|0.12.0
mdmath|goe|2.4.0
cloudcode|goo|0.0.12
asciidecorator|hel|0.2.0
rest-client|hum|0.22.2
ftp-simple|hum|0.7.4
reg|ion|1.0.1
markdown-extended|jeb|1.0.13
markdown-extension-pack|jeb|0.0.3
plantuml|jeb|2.12.1
super-replace|jeb|0.3.1
sharppad|jma|1.1.0
vscode-peacock|joh|3.1.5
vscodeilviewer|jos|0.0.1
jq-syntax-highlighting|jq-|0.0.2
docomment|k--|0.1.8
wordcounter|kir|1.9.3
rainbow-csv|mec|1.3.1
azure-pipelines|ms-|1.157.4
vscode-apimanagement|ms-|0.1.1
vscode-azureappservice|ms-|0.16.0
vscode-azureeventgrid|ms-|0.1.1
vscode-azurefunctions|ms-|0.18.1
vscode-azurestorage|ms-|0.7.2
vscode-cosmosdb|ms-|0.11.0
vscode-docker|ms-|0.8.1
vscode-logicapps|ms-|0.2.18
vscode-kubernetes-tools|ms-|1.0.4
mssql|ms-|1.6.0
sqlops-debug|ms-|1.3.0
vscode-postgresql|ms-|0.2.0
remote-containers|ms-|0.81.0
remote-ssh|ms-|0.47.1
remote-ssh-edit|ms-|0.47.1
remote-ssh-explorer|ms-|0.47.1
remote-wsl|ms-|0.39.9
vscode-remote-extensionpack|ms-|0.17.0
autorest|ms-|2.0.1
azure-account|ms-|0.8.6
azurecli|ms-|0.4.6
csharp|ms-|1.21.4
github-issues-prs|ms-|0.9.1
Go|ms-|0.11.7
mono-debug|ms-|0.15.8
powershell|ms-|2019.9.0
vscode-node-azure-pack|ms-|0.0.9
vscode-typescript-tslint-plugin|ms-|1.2.2
vsliveshare|ms-|1.0.950
vsliveshare-audio|ms-|0.1.66
team|ms-|1.149.2
azurerm-vscode-tools|msa|0.7.0
debugger-for-chrome|msj|4.12.0
color-highlight|nau|2.3.0
vscode-versionlens|pfl|0.24.0
material-icon-theme|PKi|3.9.1
quicktype|qui|12.0.46
vscode-yaml|red|0.5.3
elastic|ria|0.13.3
vscode-odata|sta|0.1.0
vscode-markdown-paste-image|tel|0.12.3
usql-vscode-ext|usq|0.2.15
vscodeintellicode|Vis|1.1.9
application-insights|Vis|0.4.2
vscode-redis|vit|1.2.0
azure-iot-edge|vsc|1.17.0
azure-iot-toolkit|vsc|2.10.0
vscode-ansible|vsc|0.5.2
WebTemplateStudio-dev-nightly|WAS|0.0.1925201
vscode-todo-highlight|way|1.0.4
t4-support|zbe|0.4.3


</details>
<!-- generated by issue reporter -->


Discussion:
Currently this doesn't work for folders with glob characters in their name...
Just to clarify.. if this may help for something to be done.. It is not that "it doesn't work with glob characters".. ..since glob characters have a meaning they just need to be escaped in the search pattern. (see #111677 )
So the bug is in the pre-filled value when using "Find in Folder" not being escaped and not in the "files to include".
Maybe in the "files to include" side we may ask a feature request to enable/disable glob.. ..but is not a bug there.

> Just to clarify.. if this may help for something to be done.. It is not that "it doesn't work with glob characters".. ..since glob characters have a meaning they just need to be escaped in the search pattern. (see #111677 ) So the bug is in the pre-filled value when using "Find in Folder" not being escaped and not in the "files to include". Maybe in the "files to include" side we may ask a feature request to enable/disable glob.. ..but is not a bug there.

I agree there. When you input "by hand" a folder or pattern, the better idea would be to enter the path and not the folder's name... or escape its name.
But on the side of the "Find in folder" contextual menu option, it's definitively a bug: the pre-filled "files to include" field should contain either the escaped folder's name or its path...
This is very important feature to search the files in NextJS Projects, which is using [ ] in the folder name. 

currently I am unable to search in any folder in nextjs contains the character **[** or **]**


This BUG prevents searching in properly named next.js projects, where square brackets in folder names are used for REST API pathing such as api/collection/[id] by putting an actual folder named '[id]', see official documentation https://nextjs.org/docs/routing/dynamic-routes . Next.js is one of the most popular react frameworks, maintained by Vercel.  Please fix this !  It works fine in Intellij, and I'll have to switch back if you don't.

Please consider re-categorizing this as a bug ticket (and a critical one at that), not a feature request.
Disappointing to see maintainers keep resolving bugs to this one bug no progress has been made. It has been making trouble to view logs for a long time.
@roblourens Why is this a feature request? When it is clearly a bug, let me explain: The search is broken, it pretends to search in all folders, when it clearly doesn't. For a "search" feature not searching in all elements because of special characters in a folder name, is a clear "false-negative" (it does find something, that it should find when it exists) and as such a bug. Those special characters are allowed characters in any operating system for folder names that VSCode supports (e.g., Microsoft Windows). Aka VSCode does currently not support them same file path character sets that the underlying OS supports. For a file editor/IDE a pretty bad starting point, not considering this as a bug.
@m1no Theoretically this is the same kind of problem compared to SQL injection, but these "new programmers" just don't want to admit it.

Resolving patch:
diff --git a/src/vs/workbench/services/search/common/queryBuilder.ts b/src/vs/workbench/services/search/common/queryBuilder.ts
--- a/src/vs/workbench/services/search/common/queryBuilder.ts
+++ b/src/vs/workbench/services/search/common/queryBuilder.ts
@@ -578,6 +578,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 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.
  */
@@ -616,7 +628,7 @@ export function resolveResourcesForSearchIncludes(resources: URI[], contextServi
 			}
 
 			if (folderPath) {
-				folderPaths.push(folderPath);
+				folderPaths.push(escapeGlobPattern(folderPath));
 			}
 		});
 	}


Test patch:
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
--- /dev/null
+++ b/src/vs/workbench/services/search/test/common/queryBuilder.test.ts
@@ -0,0 +1,30 @@
+/*---------------------------------------------------------------------------------------------
+ *  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 { 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 { 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(isWindows ? 'C:\\testWorkspace' : '/testWorkspace'));
+		context = new TestContextService(workspace);
+	});
+
+	test('resolveResourcesForSearchIncludes passes through paths without special glob characters', () => {
+		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(isWindows ? "C:\\testWorkspace\\pages\\blog\\[postId]" : "/testWorkspace/pages/blog/[postId]")], context);
+		assert.deepStrictEqual(actual, ["./pages/blog/[[]postId[]]"]);
+	});
+});