Visitar URL original
JSON - support ${workspaceFolder} in json.schemas fileMatch patterns by soreavis · Pull Request #326706 · microsoft/vscode · GitHub
Skip to content

JSON - support ${workspaceFolder} in json.schemas fileMatch patterns - #326706

Merged
Martin Aeschlimann (aeschli) merged 6 commits into
microsoft:mainfrom
soreavis:fix/44135-json-filematch-workspacefolder
Sep 30, 2026
Merged

Martin Aeschlimann (aeschli) merged 6 commits into
microsoft:mainfrom
soreavis:fix/44135-json-filematch-workspacefolder

Conversation

@soreavis

@soreavis soreavis commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #44135

Implements the approach from #44135 (comment): ${workspaceFolder} is expanded on the client, and the server API stays unchanged.

Change

  • jsonClient.ts: when collecting json.schemas settings, a leading ${workspaceFolder} in a fileMatch pattern (also after a ! exclusion prefix) expands to the workspace folder path. This happens in the two scopes with an unambiguous folder: workspace folder settings in a multi-root workspace, and workspace settings of a single-folder workspace. User settings and multi-root workspace-file settings are left untouched (no single folder to expand to). Non-array/non-string values pass through unchanged.
  • utils/strings.ts: escapeGlobCharacters, used to insert the folder path into the pattern.
  • package.nls.json: documents the new pattern form.

Since the expanded pattern carries the folder path, the server's implicit **/ prefix no longer widens it below the folder — ${workspaceFolder}/*.json matches only in the folder root, ${workspaceFolder}/sub/*.json only in that subfolder, and existing patterns behave exactly as before.

Validation

  • A parity harness against the service's real createRegex + vscode-uri (66 cases): root-only and subfolder anchoring, recursive ** patterns, exclusions, the multi-root folderUri gate, folder names with spaces/unicode/parens/$/glob metacharacters/#/?, uppercase drive letters, filesystem-root and drive-root folders, and malformed fileMatch values (bare string, non-string entries — no throw, pass through).
  • End-to-end in a dev build, re-run on this revision: with a ${workspaceFolder}/*.json schema and a plain *.json schema both requiring a property, the folder-root file reports two diagnostics and a file in a subfolder reports one — the anchored schema applies at the root only.

Notes

  • The folder path is spelled the way the server spells the document uris it matches against: a leading drive letter is lowercased and # and ? are percent-encoded — Uri.path lowercases neither and decodes both. Glob metacharacters are encoded as \xAB escapes, which the glob parser copies through to the generated regular expression, so folder names like my[proj] or w*s match literally. A backslash escape can't be used for this — the parser has no escape syntax, so it copies the backslash through and still reads the next character as glob syntax.
  • Being a path, the pattern is not tied to a scheme or authority, and the **/ prefix still allows leading segments before it — the same as any absolute pattern written by hand today. In a multi-root workspace the existing folderUri gate still scopes each pattern to its folder.
  • While validating the multi-root case I noticed folder-scoped json.schemas (in a folder's .vscode/settings.json inside a .code-workspace) don't reach the server even for plain patterns, also on stable 1.129 — that looks like a separate pre-existing issue, independent of this change. I can file it with the minimal fixture if useful.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds ${workspaceFolder} expansion for JSON schema fileMatch patterns.

Changes:

  • Expands leading workspace-folder variables, including exclusions.
  • Documents supported scopes and usage.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
extensions/json-language-features/client/src/jsonClient.ts Implements workspace-folder expansion.
extensions/json-language-features/package.nls.json Documents the new pattern form.

Comment thread extensions/json-language-features/client/src/jsonClient.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

// Drop the query and fragment like the server does for document uris, and turn glob
// metacharacters in the folder string into regex hex escapes, which the glob passes
// through, so folder names can not alter the meaning of the pattern.
const folderString = workspaceFolder.with({ query: '', fragment: '' }).toString(true)

@aeschli Martin Aeschlimann (aeschli) Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For readability, can we just use fm.startsWith('${workspaceFolder}/')
To concatenate use the URI utils.
vscode.Uri.joinPath(workspaceFolder, fm.substring(...)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched the detection as requested. For the join I kept string concatenation — the rest of the pattern is a glob, not a path: Uri.joinPath(folder, 'file?.json').toString(true) comes back as …/file%3F.json, and the folder part carries the metachar escaping from the earlier round.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the server does't want a URI but a posix path.
folder.path will give you the path segment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, using folder.path now.

@aeschli

Martin Aeschlimann (aeschli) commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

For

workspaceFolder.path
			.replace(/^\/[A-Z]:/, s => s.toLowerCase())
			.replace(/\/$/, '')
			.replace(/[*?{}[\],\\]/g, c => `\\x${c.charCodeAt(0).toString(16)}`);`

we normally use
escapeRegExpCharacters (

export function escapeRegExpCharacters(value: string): string {
)

That code is not reachable in jsonClient, but I would suggest to create a extensions/json-language-features/client/src/utils/strings.ts with a copy of that function.

@soreavis

Copy link
Copy Markdown
Contributor Author

escapeRegExpCharacters doesn't hold up here — the pattern hits the language service's glob parser before it's a regexp, and that parser has no escape syntax, so the backslash is copied through and the next character is still read as glob:

  • \. reaches the regexp as \\., so a folder like .config stops matching
  • \* becomes an unbalanced group that throws, and the catch drops the whole json.schemas entry

New file has escapeGlobCharacters instead. A second commit percent-encodes # and ? — folder.path decodes them, the document uris don't. That also retires the ?-folder gap in the description: nothing is stripped, doc uris carry %3F.

A fileMatch pattern has no way to target files in the workspace folder
root only: patterns are matched against the full document uri with an
implicit '**/' prefix, so `*.json` matches at every depth and a leading
`/` is redundant.

Expand a leading `${workspaceFolder}` (also after a `!` exclusion
prefix) to the workspace folder uri when collecting json.schemas
settings, in the two scopes with an unambiguous folder: workspace
folder settings in a multi-root workspace and workspace settings of a
single folder workspace. The expanded pattern is absolute, so the
implicit '**/' prefix no longer widens it and
`${workspaceFolder}/*.json` matches in the folder root only. The
server API stays unchanged.

Fixes microsoft#44135
The service's glob has no escape syntax, so a folder name containing a
glob metacharacter altered the meaning of the expanded pattern: names
with brackets or braces made it match nothing and a '*' made it match
too much. Since the glob passes a backslash and alphanumerics through
to the resulting regular expression verbatim, encoding the folder
string's metacharacters as regex hex escapes makes them match
literally.

Also drop the query and fragment from the folder uri, mirroring what
the server does to document uris before matching.
@soreavis
soreavis force-pushed the fix/44135-json-filematch-workspacefolder branch from 31d3de9 to f1db298 Compare September 26, 2026 11:08
@aeschli
Martin Aeschlimann (aeschli) merged commit d4b0f3a into microsoft:main Sep 30, 2026
35 checks passed
@vs-code-engineering vs-code-engineering Bot added this to the 1.141.0 milestone Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[json] schema fileMatch for all *.json files in root folder

6 participants