[json] support result limits

This commit is contained in:
Martin Aeschlimann
2019-10-28 20:23:06 +01:00
parent e42b3d1781
commit 223076ac54
4 changed files with 72 additions and 19 deletions

View File

@@ -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<Disposable> | 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);