diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index 65ed75f23fafebee52650082f611232a3c17c21b..5fcccab60d4e5791e05b2945b0c07cdcf513716f 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -46,6 +46,7 @@ interface Settings { json?: { schemas?: JSONSchemaSettings[]; format?: { enable: boolean; }; + resultLimit?: number; }; http?: { proxy?: string; @@ -320,6 +321,7 @@ function getSettings(): Settings { }, json: { schemas: [], + resultLimit: 5000 } }; let schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null); diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 3debd44e4dfe0214b725fc348490d3f52fc948d8..e6373240c70747aba20d6f627edb16c281fea4f4 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,11 +12,10 @@ }, "main": "./out/jsonServerMain", "dependencies": { - "jsonc-parser": "^2.1.1", + "jsonc-parser": "^2.2.0", "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.3.5", + "vscode-json-languageservice": "^3.4.1", "vscode-languageserver": "^6.0.0-next.1", - "vscode-nls": "^4.1.1", "vscode-uri": "^2.0.3" }, "devDependencies": { diff --git a/extensions/json-language-features/server/src/jsonServerMain.ts b/extensions/json-language-features/server/src/jsonServerMain.ts index 2439da72e0db94c0de7689c3c13a3339946b7ed7..0fa1de87d321c879b635e37bc60482422a44437a 100644 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/jsonServerMain.ts @@ -13,6 +13,8 @@ import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDes import * as fs from 'fs'; import { URI } from 'vscode-uri'; import * as URL from 'url'; +import { posix } from 'path'; +import { setTimeout, clearTimeout } from 'timers'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; import { JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; @@ -115,9 +117,12 @@ documents.listen(connection); let clientSnippetSupport = false; let dynamicFormatterRegistration = false; -let foldingRangeLimit = Number.MAX_VALUE; let hierarchicalDocumentSymbolSupport = false; +let foldingRangeLimitDefault = Number.MAX_VALUE; +let foldingRangeLimit = Number.MAX_VALUE; +let resultLimit = Number.MAX_VALUE; + // After the server has started the client sends an initialize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { @@ -145,7 +150,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions.provideFormatter !== 'boolean'); - foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); + foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); const capabilities: ServerCapabilities = { // Tell the client that the server works in FULL text document sync mode @@ -169,6 +174,7 @@ interface Settings { json: { schemas: JSONSchemaSettings[]; format: { enable: boolean; }; + resultLimit?: number; }; http: { proxy: string; @@ -182,6 +188,39 @@ interface JSONSchemaSettings { schema?: JSONSchema; } +namespace LimitExceededWarnings { + const pendingWarnings: { [uri: string]: { features: { [name: string]: string }; timeout?: NodeJS.Timeout; } } = {}; + + export function cancel(uri: string) { + const warning = pendingWarnings[uri]; + if (warning && warning.timeout) { + clearTimeout(warning.timeout); + delete pendingWarnings[uri]; + } + } + + export function onResultLimitExceeded(uri: string, resultLimit: number, name: string) { + return () => { + let warning = pendingWarnings[uri]; + if (warning) { + if (!warning.timeout) { + // already shown + return; + } + warning.features[name] = name; + warning.timeout.refresh(); + } else { + warning = { features: { [name]: name } }; + warning.timeout = setTimeout(() => { + connection.window.showInformationMessage(`${posix.basename(uri)}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`); + warning.timeout = undefined; + }, 2000); + pendingWarnings[uri] = warning; + } + }; + } +} + let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined; let schemaAssociations: ISchemaAssociations | undefined = undefined; let formatterRegistration: Thenable | null = null; @@ -194,6 +233,9 @@ connection.onDidChangeConfiguration((change) => { jsonConfigurationSettings = settings.json && settings.json.schemas; updateConfiguration(); + foldingRangeLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || foldingRangeLimitDefault, 0)); + resultLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || Number.MAX_VALUE, 0)); + // dynamically enable & disable the formatter if (dynamicFormatterRegistration) { const enableFormatter = settings && settings.json && settings.json.format && settings.json.format.enable; @@ -270,11 +312,13 @@ function updateConfiguration() { // The content of a text document has changed. This event is emitted // when the text document first opened or when its content has changed. documents.onDidChangeContent((change) => { + LimitExceededWarnings.cancel(change.document.uri); triggerValidation(change.document); }); // a document has closed: clear all diagnostics documents.onDidClose(event => { + LimitExceededWarnings.cancel(event.document.uri); cleanPendingValidation(event.document); connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); }); @@ -383,10 +427,11 @@ connection.onDocumentSymbol((documentSymbolParams, token) => { const document = documents.get(documentSymbolParams.textDocument.uri); if (document) { const jsonDocument = getJSONDocument(document); + const onResultLimitExceeded = LimitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document symbols'); if (hierarchicalDocumentSymbolSupport) { - return languageService.findDocumentSymbols2(document, jsonDocument); + return languageService.findDocumentSymbols2(document, jsonDocument, { resultLimit, onResultLimitExceeded }); } else { - return languageService.findDocumentSymbols(document, jsonDocument); + return languageService.findDocumentSymbols(document, jsonDocument, { resultLimit, onResultLimitExceeded }); } } return []; @@ -407,8 +452,9 @@ connection.onDocumentColor((params, token) => { return runSafeAsync(async () => { const document = documents.get(params.textDocument.uri); if (document) { + const onResultLimitExceeded = LimitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document colors'); const jsonDocument = getJSONDocument(document); - return languageService.findDocumentColors(document, jsonDocument); + return languageService.findDocumentColors(document, jsonDocument, { resultLimit, onResultLimitExceeded }); } return []; }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); @@ -429,7 +475,8 @@ connection.onFoldingRanges((params, token) => { return runSafe(() => { const document = documents.get(params.textDocument.uri); if (document) { - return languageService.getFoldingRanges(document, { rangeLimit: foldingRangeLimit }); + const onRangeLimitExceeded = LimitExceededWarnings.onResultLimitExceeded(document.uri, foldingRangeLimit, 'folding ranges'); + return languageService.getFoldingRanges(document, { rangeLimit: foldingRangeLimit, onRangeLimitExceeded }); } return null; }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index a1c7e761720f46ca430aa60a1cac506879717b10..2c788b621d549a415ced2c602c6f9cc73776bc9c 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -54,10 +54,10 @@ https-proxy-agent@^2.2.1: agent-base "^4.1.0" debug "^3.1.0" -jsonc-parser@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" - integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== +jsonc-parser@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef" + integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA== ms@2.0.0: version "2.0.0" @@ -73,15 +73,15 @@ request-light@^0.2.4: https-proxy-agent "^2.2.1" vscode-nls "^4.0.0" -vscode-json-languageservice@^3.3.5: - version "3.3.5" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.3.5.tgz#e222e8391beeb23cfa40cf17fd57d1594d295fc7" - integrity sha512-Le6SG5aRdrRc5jVeVMRkYbGH9rrVaZHCW0Oa8zCFQ0T8viUud9qdZ29lSv5NPNLwTB8mn4pYucFyyEPM2YWvLA== +vscode-json-languageservice@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.4.1.tgz#e051f3bb2de5d23995763deac108622a5e93604a" + integrity sha512-IWJreQ9HtBSyveqaC3UUEArUqCnt5zYLgHewSJ0CvxlIJfvY7yD8GDbLuLxGeHMWwSudYlODit1IfwNzvjZjEg== dependencies: - jsonc-parser "^2.1.1" + jsonc-parser "^2.2.0" vscode-languageserver-types "^3.15.0-next.5" vscode-nls "^4.1.1" - vscode-uri "^2.0.3" + vscode-uri "^2.1.0" vscode-jsonrpc@^5.0.0-next.2: version "5.0.0-next.2" @@ -128,3 +128,8 @@ vscode-uri@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.3.tgz#25e5f37f552fbee3cec7e5f80cef8469cefc6543" integrity sha512-4D3DI3F4uRy09WNtDGD93H9q034OHImxiIcSq664Hq1Y1AScehlP3qqZyTkX/RWxeu0MRMHGkrxYqm2qlDF/aw== + +vscode-uri@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.0.tgz#475a4269e63edbc13914b40c84bc1416e3398156" + integrity sha512-3voe44nOhb6OdKlpZShVsmVvY2vFQHMe6REP3Ky9RVJuPyM/XidsjH6HncCIDdSmbcF5YQHrTC/Q+Q2loJGkOw==