From bd9ac7a08942c7c097f739f8c0c5d0c5becd5d60 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 2 Feb 2023 22:36:41 +0100 Subject: [PATCH] Fix JSON schema configuration in multi root workspaces (#173169) [json] add folderUri to SchemaAssociation and schema setting --- .../client/src/jsonClient.ts | 76 ++++--------------- .../json-language-features/server/README.md | 7 +- .../server/package.json | 6 +- .../server/src/jsonServer.ts | 3 +- .../json-language-features/server/yarn.lock | 8 +- 5 files changed, 29 insertions(+), 71 deletions(-) diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index 25968290446..650f20891a2 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -70,6 +70,7 @@ export type JSONSchemaSettings = { fileMatch?: string[]; url?: string; schema?: any; + folderUri?: string; }; export namespace SettingIds { @@ -472,6 +473,8 @@ function getSettings(): Settings { jsonFoldingLimit = normalizeLimit(workspace.getConfiguration(SettingIds.editorSection, { languageId: 'json' }).get(SettingIds.foldingMaximumRegions)); jsoncFoldingLimit = normalizeLimit(workspace.getConfiguration(SettingIds.editorSection, { languageId: 'jsonc' }).get(SettingIds.foldingMaximumRegions)); + const schemas: JSONSchemaSettings[] = []; + const settings: Settings = { http: { proxy: httpSettings.get('proxy'), @@ -481,85 +484,34 @@ function getSettings(): Settings { validate: { enable: configuration.get(SettingIds.enableValidation) }, format: { enable: configuration.get(SettingIds.enableFormatter) }, keepLines: { enable: configuration.get(SettingIds.enableKeepLines) }, - schemas: [], + schemas, resultLimit: resultLimit + 1, // ask for one more so we can detect if the limit has been exceeded jsonFoldingLimit: jsonFoldingLimit + 1, jsoncFoldingLimit: jsoncFoldingLimit + 1 } }; - const schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null); - const collectSchemaSettings = (schemaSettings: JSONSchemaSettings[], folderUri?: Uri, isMultiRoot?: boolean) => { - let fileMatchPrefix = undefined; - if (folderUri && isMultiRoot) { - fileMatchPrefix = folderUri.toString(); - if (fileMatchPrefix[fileMatchPrefix.length - 1] === '/') { - fileMatchPrefix = fileMatchPrefix.substr(0, fileMatchPrefix.length - 1); - } - } + const collectSchemaSettings = (schemaSettings: JSONSchemaSettings[], folderUri?: Uri) => { for (const setting of schemaSettings) { const url = getSchemaId(setting, folderUri); - if (!url) { - continue; - } - let schemaSetting = schemaSettingsById[url]; - if (!schemaSetting) { - schemaSetting = schemaSettingsById[url] = { url, fileMatch: [] }; - settings.json!.schemas!.push(schemaSetting); - } - const fileMatches = setting.fileMatch; - if (Array.isArray(fileMatches)) { - const resultingFileMatches = schemaSetting.fileMatch || []; - schemaSetting.fileMatch = resultingFileMatches; - const addMatch = (pattern: string) => { // filter duplicates - if (resultingFileMatches.indexOf(pattern) === -1) { - resultingFileMatches.push(pattern); - } - }; - for (const fileMatch of fileMatches) { - if (fileMatchPrefix) { - if (fileMatch[0] === '/') { - addMatch(fileMatchPrefix + fileMatch); - addMatch(fileMatchPrefix + '/*' + fileMatch); - } else { - addMatch(fileMatchPrefix + '/' + fileMatch); - addMatch(fileMatchPrefix + '/*/' + fileMatch); - } - } else { - addMatch(fileMatch); - } - } - } - if (setting.schema && !schemaSetting.schema) { - schemaSetting.schema = setting.schema; + if (url) { + const schemaSetting: JSONSchemaSettings = { url, fileMatch: setting.fileMatch, folderUri: folderUri?.toString(false), schema: setting.schema }; + schemas.push(schemaSetting); } } }; - const folders = workspace.workspaceFolders; - - // merge global and folder settings. Qualify all file matches with the folder path. const globalSettings = workspace.getConfiguration('json', null).get('schemas'); if (Array.isArray(globalSettings)) { - if (!folders) { - collectSchemaSettings(globalSettings); - } + collectSchemaSettings(globalSettings); } + const folders = workspace.workspaceFolders; if (folders) { - const isMultiRoot = folders.length > 1; for (const folder of folders) { - const folderUri = folder.uri; - - const schemaConfigInfo = workspace.getConfiguration('json', folderUri).inspect('schemas'); - - const folderSchemas = schemaConfigInfo!.workspaceFolderValue; - if (Array.isArray(folderSchemas)) { - collectSchemaSettings(folderSchemas, folderUri, isMultiRoot); + const schemaConfigInfo = workspace.getConfiguration('json', folder.uri).inspect('schemas'); + if (schemaConfigInfo && Array.isArray(schemaConfigInfo.workspaceFolderValue)) { + collectSchemaSettings(schemaConfigInfo.workspaceFolderValue, folder.uri); } - if (Array.isArray(globalSettings)) { - collectSchemaSettings(globalSettings, folderUri, isMultiRoot); - } - } } return settings; @@ -572,7 +524,7 @@ function getSchemaId(schema: JSONSchemaSettings, folderUri?: Uri): string | unde url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`; } } else if (folderUri && (url[0] === '.' || url[0] === '/')) { - url = Uri.joinPath(folderUri, url).toString(); + url = Uri.joinPath(folderUri, url).toString(false); } return url; } diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index 1285255a607..d84175a1f59 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -67,7 +67,8 @@ The server supports the following settings: - `schemas`: Configures association of file names to schema URL or schemas and/or associations of schema URL to schema content. - `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern. - `url`: The URL of the schema, optional when also a schema is provided. - - `schema`: The schema content. + - `schema`: The schema content, optional + - `folderUri`: If provided, the association is only used if the document is located in the given folder (directly or indirectly) - `resultLimit`: The max number of color decorators and outline symbols to be computed (for performance reasons) - `jsonFoldingLimit`: The max number of folding ranges to be computed for json documents (for performance reasons) - `jsoncFoldingLimit`: The max number of folding ranges to be computed for jsonc documents (for performance reasons) @@ -170,6 +171,10 @@ interface ISchemaAssociation { * A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'. */ fileMatch: string[]; + /** + * If provided, the association is only used if the validated document is located in the given folder (directly or indirectly) + */ + folderUri?: string; /* * The schema for the given URI. * If no schema is provided, the schema will be fetched with the schema request service (if available). diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 800db168e2c..5abe1a2d35c 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,12 +12,12 @@ }, "main": "./out/node/jsonServerMain", "dependencies": { + "@vscode/l10n": "^0.0.11", "jsonc-parser": "^3.2.0", "request-light": "^0.7.0", - "vscode-json-languageservice": "^5.1.4", + "vscode-json-languageservice": "^5.2.0", "vscode-languageserver": "^8.1.0-next.6", - "vscode-uri": "^3.0.7", - "@vscode/l10n": "^0.0.11" + "vscode-uri": "^3.0.7" }, "devDependencies": { "@types/mocha": "^9.1.1", diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 39055e0965b..edbdf2c3af8 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -201,6 +201,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) fileMatch?: string[]; url?: string; schema?: JSONSchema; + folderUri?: string; } @@ -313,7 +314,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) uri = schema.schema.id || `vscode://schemas/custom/${index}`; } if (uri) { - languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema }); + languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema, folderUri: schema.folderUri }); } }); } diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 517c007e91b..5649955bbd4 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -27,10 +27,10 @@ request-light@^0.7.0: resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.7.0.tgz#885628bb2f8040c26401ebf258ec51c4ae98ac2a" integrity sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q== -vscode-json-languageservice@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.1.4.tgz#cbdb447f281dcd107705e7fe8fbfc0b71db7610d" - integrity sha512-ROZ1ezYQUbq9b/07xYpHtZSyyhoUk3oTTGVAEr6bU1DKr8ELaz9fsDoHno34tKtHj/Tf3deQqfjQNGKdbRuvTw== +vscode-json-languageservice@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.2.0.tgz#884b7f108be4310e3332167c3ea60ab17f03418c" + integrity sha512-q8Rdhu2HEddRxvlhVqwh0cWmKK+OtyMB2xRhtqXEQ7cjb0iZ14madb90iJe9fCHPjoj9CGBrq6QzuOp8OE6XWg== dependencies: "@vscode/l10n" "^0.0.11" jsonc-parser "^3.2.0"