diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts index b4ce51c162a..af55ec28f06 100644 --- a/extensions/css-language-features/server/src/cssServer.ts +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -57,7 +57,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let dataProvidersReady: Promise = Promise.resolve(); - let diagnosticPushSupport: DiagnosticsSupport | undefined; + let diagnosticsSupport: DiagnosticsSupport | undefined; const languageServices: { [id: string]: LanguageService } = {}; @@ -103,9 +103,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); if (pullDiagnosticSupport === undefined) { - diagnosticPushSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); + diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); } else { - diagnosticPushSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); + diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); } const capabilities: ServerCapabilities = { @@ -173,7 +173,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } // reset all document settings documentSettings = {}; - diagnosticPushSupport?.requestRefresh(); + diagnosticsSupport?.requestRefresh(); } async function validateTextDocument(textDocument: TextDocument): Promise { diff --git a/extensions/css-language-features/server/src/utils/validation.ts b/extensions/css-language-features/server/src/utils/validation.ts index 2658f74016a..edd5f5618c7 100644 --- a/extensions/css-language-features/server/src/utils/validation.ts +++ b/extensions/css-language-features/server/src/utils/validation.ts @@ -58,8 +58,6 @@ export function registerDiagnosticsPushSupport(documents: TextDocuments { documents.all().forEach(triggerValidation); @@ -85,7 +83,7 @@ export function registerDiagnosticsPullSupport(documents: TextDocuments { + const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { return runSafeAsync(runtime, async () => { const document = documents.get(params.textDocument.uri); if (document) { @@ -103,6 +101,7 @@ export function registerDiagnosticsPullSupport(documents: TextDocuments { + registration.dispose(); } }; diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index 319399a038c..3198ad16d96 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -19,6 +19,7 @@ import { pushAll } from './utils/arrays'; import { getDocumentContext } from './utils/documentContext'; import { URI } from 'vscode-uri'; import { formatError, runSafe } from './utils/runner'; +import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; import { getFoldingRanges } from './modes/htmlFolding'; import { fetchHTMLDataProviders } from './customData'; @@ -92,6 +93,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let languageModes: LanguageModes; + let diagnosticsSupport: DiagnosticsSupport | undefined; + let clientSnippetSupport = false; let dynamicFormatterRegistration = false; let scopedSettingsSupport = false; @@ -180,6 +183,14 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) workspaceFoldersSupport = getClientCapability('workspace.workspaceFolders', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); formatterMaxNumberOfEdits = initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; + + const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); + if (pullDiagnosticSupport === undefined) { + diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); + } else { + diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); + } + const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined, @@ -196,7 +207,12 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) foldingRangeProvider: true, selectionRangeProvider: true, renameProvider: true, - linkedEditingRangeProvider: true + linkedEditingRangeProvider: true, + diagnosticProvider: { + documentSelector: null, + interFileDependencies: false, + workspaceDiagnostics: false + } }; return { capabilities }; }); @@ -217,7 +233,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } } workspaceFolders = updatedFolders.concat(toAdd); - documents.all().forEach(triggerValidation); + diagnosticsSupport?.requestRefresh(); }); } }); @@ -228,7 +244,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) connection.onDidChangeConfiguration((change) => { globalSettings = change.settings as Settings; documentSettings = {}; // reset all document settings - documents.all().forEach(triggerValidation); + diagnosticsSupport?.requestRefresh(); // dynamically enable & disable the formatter if (dynamicFormatterRegistration) { @@ -248,37 +264,6 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } }); - const pendingValidationRequests: { [uri: string]: Disposable } = {}; - const validationDelayMs = 500; - - // 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 => { - triggerValidation(change.document); - }); - - // a document has closed: clear all diagnostics - documents.onDidClose(event => { - cleanPendingValidation(event.document); - connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); - }); - - function cleanPendingValidation(textDocument: TextDocument): void { - const request = pendingValidationRequests[textDocument.uri]; - if (request) { - request.dispose(); - delete pendingValidationRequests[textDocument.uri]; - } - } - - function triggerValidation(textDocument: TextDocument): void { - cleanPendingValidation(textDocument); - pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(() => { - delete pendingValidationRequests[textDocument.uri]; - validateTextDocument(textDocument); - }, validationDelayMs); - } - function isValidationEnabled(languageId: string, settings: Settings = globalSettings) { const validationSettings = settings && settings.html && settings.html.validate; if (validationSettings) { @@ -287,7 +272,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) return true; } - async function validateTextDocument(textDocument: TextDocument) { + async function validateTextDocument(textDocument: TextDocument): Promise { try { const version = textDocument.version; const diagnostics: Diagnostic[] = []; @@ -301,12 +286,13 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) pushAll(diagnostics, await mode.doValidation(latestTextDocument, settings)); } } - connection.sendDiagnostics({ uri: latestTextDocument.uri, diagnostics }); + return diagnostics; } } } catch (e) { connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); } + return []; } connection.onCompletion(async (textDocumentPosition, token) => { diff --git a/extensions/html-language-features/server/src/utils/validation.ts b/extensions/html-language-features/server/src/utils/validation.ts index 5c5261cf9e5..adb13086391 100644 --- a/extensions/html-language-features/server/src/utils/validation.ts +++ b/extensions/html-language-features/server/src/utils/validation.ts @@ -3,15 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Connection, Diagnostic, Disposable, TextDocuments } from 'vscode-languageserver'; +import { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver'; import { TextDocument } from 'vscode-html-languageservice'; -import { formatError } from './runner'; +import { formatError, runSafe } from './runner'; import { RuntimeEnvironment } from '../htmlServer'; export type Validator = (textDocument: TextDocument) => Promise; -export type DiagnosticPushSupport = { dispose(): void; triggerValidation(textDocument: TextDocument): void }; +export type DiagnosticsSupport = { + dispose(): void; + requestRefresh(): void; +}; -export function registerDiagnosticPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticPushSupport { +export function registerDiagnosticsPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { const pendingValidationRequests: { [uri: string]: Disposable } = {}; const validationDelayMs = 500; @@ -55,10 +58,10 @@ export function registerDiagnosticPushSupport(documents: TextDocuments { + documents.all().forEach(triggerValidation); + }, dispose: () => { disposables.forEach(d => d.dispose()); disposables.length = 0; @@ -70,3 +73,36 @@ export function registerDiagnosticPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport { + return { + kind: DocumentDiagnosticReportKind.Full, + items: diagnostics + }; + } + + const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { + return runSafe(runtime, async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + return newDocumentDiagnosticReport(await validate(document)); + } + return newDocumentDiagnosticReport([]); + + }, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token); + }); + + function requestRefresh(): void { + connection.languages.diagnostics.refresh(); + } + + return { + requestRefresh, + dispose: () => { + registration.dispose(); + } + }; + +} diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index df8bb6faa35..5df112220d1 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -9,7 +9,7 @@ import { DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions, Diagnostic } from 'vscode-languageserver'; -import { formatError, runSafe, runSafeAsync } from './utils/runner'; +import { runSafe, runSafeAsync } from './utils/runner'; import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; @@ -114,7 +114,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let resultLimit = Number.MAX_VALUE; let formatterMaxNumberOfEdits = Number.MAX_VALUE; - let diagnosticSupport: DiagnosticsSupport | undefined; + let diagnosticsSupport: DiagnosticsSupport | undefined; // After the server has started the client sends an initialize request. The server receives @@ -154,9 +154,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); if (pullDiagnosticSupport === undefined) { - diagnosticSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); + diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); } else { - diagnosticSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); + diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); } @@ -301,25 +301,18 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) needsRevalidation = languageService.resetSchema(uriOrUris); } if (needsRevalidation) { - for (const doc of documents.all()) { - triggerValidation(doc); - } + diagnosticsSupport?.requestRefresh(); } }); // Retry schema validation on all open documents - connection.onRequest(ForceValidateRequest.type, uri => { - return new Promise(resolve => { - const document = documents.get(uri); - if (document) { - updateConfiguration(); - validateTextDocument(document, diagnostics => { - resolve(diagnostics); - }); - } else { - resolve([]); - } - }); + connection.onRequest(ForceValidateRequest.type, async uri => { + const document = documents.get(uri); + if (document) { + updateConfiguration(); + return await validateTextDocument(document); + } + return []; }); connection.onRequest(LanguageStatusRequest.type, async uri => { @@ -365,8 +358,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } languageService.configure(languageSettings); - // Revalidate any open text documents - documents.all().forEach(triggerValidation); + diagnosticsSupport?.requestRefresh(); } async function validateTextDocument(textDocument: TextDocument): Promise { @@ -387,7 +379,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } }); if (hasChanges) { - documents.all().forEach(triggerValidation); + diagnosticsSupport?.requestRefresh(); } }); diff --git a/extensions/json-language-features/server/src/utils/validation.ts b/extensions/json-language-features/server/src/utils/validation.ts index 10130d2684f..a4d06d90cd6 100644 --- a/extensions/json-language-features/server/src/utils/validation.ts +++ b/extensions/json-language-features/server/src/utils/validation.ts @@ -58,8 +58,6 @@ export function registerDiagnosticsPushSupport(documents: TextDocuments { documents.all().forEach(triggerValidation); @@ -85,7 +83,7 @@ export function registerDiagnosticsPullSupport(documents: TextDocuments { + const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { return runSafeAsync(runtime, async () => { const document = documents.get(params.textDocument.uri); if (document) { @@ -103,6 +101,7 @@ export function registerDiagnosticsPullSupport(documents: TextDocuments { + registration.dispose(); } };