diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts
index 8ce00331d83..85383529fd4 100644
--- a/extensions/html/server/src/htmlServerMain.ts
+++ b/extensions/html/server/src/htmlServerMain.ts
@@ -16,6 +16,7 @@ import { format } from './modes/formatting';
import { pushAll } from './utils/arrays';
import { getDocumentContext } from './utils/documentContext';
import uri from 'vscode-uri';
+import { formatError, runSafe } from './utils/errors';
import * as nls from 'vscode-nls';
@@ -31,6 +32,10 @@ let connection: IConnection = createConnection();
console.log = connection.console.log.bind(connection.console);
console.error = connection.console.error.bind(connection.console);
+process.on('unhandledRejection', (e: any) => {
+ connection.console.error(formatError(`Unhandled exception`, e));
+});
+
// Create a simple text document manager. The text document manager
// supports full document sync only
let documents: TextDocuments = new TextDocuments();
@@ -208,165 +213,195 @@ function isValidationEnabled(languageId: string, settings: Settings = globalSett
}
async function validateTextDocument(textDocument: TextDocument) {
- let diagnostics: Diagnostic[] = [];
- if (textDocument.languageId === 'html') {
- let modes = languageModes.getAllModesInDocument(textDocument);
- let settings = await getDocumentSettings(textDocument, () => modes.some(m => !!m.doValidation));
- modes.forEach(mode => {
- if (mode.doValidation && isValidationEnabled(mode.getId(), settings)) {
- pushAll(diagnostics, mode.doValidation(textDocument, settings));
- }
- });
+ try {
+ let diagnostics: Diagnostic[] = [];
+ if (textDocument.languageId === 'html') {
+ let modes = languageModes.getAllModesInDocument(textDocument);
+ let settings = await getDocumentSettings(textDocument, () => modes.some(m => !!m.doValidation));
+ modes.forEach(mode => {
+ if (mode.doValidation && isValidationEnabled(mode.getId(), settings)) {
+ pushAll(diagnostics, mode.doValidation(textDocument, settings));
+ }
+ });
+ }
+ connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
+ } catch (e) {
+ connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e));
}
- connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}
connection.onCompletion(async textDocumentPosition => {
- let document = documents.get(textDocumentPosition.textDocument.uri);
- let mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);
- if (mode && mode.doComplete) {
- let doComplete = mode.doComplete;
- if (mode.getId() !== 'html') {
- connection.telemetry.logEvent({ key: 'html.embbedded.complete', value: { languageId: mode.getId() } });
+ return runSafe(async () => {
+ let document = documents.get(textDocumentPosition.textDocument.uri);
+ let mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);
+ if (mode && mode.doComplete) {
+ let doComplete = mode.doComplete;
+ if (mode.getId() !== 'html') {
+ connection.telemetry.logEvent({ key: 'html.embbedded.complete', value: { languageId: mode.getId() } });
+ }
+ let settings = await getDocumentSettings(document, () => doComplete.length > 2);
+ return doComplete(document, textDocumentPosition.position, settings);
}
- let settings = await getDocumentSettings(document, () => doComplete.length > 2);
- return doComplete(document, textDocumentPosition.position, settings);
- }
- return { isIncomplete: true, items: [] };
+ return { isIncomplete: true, items: [] };
+ }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`);
});
connection.onCompletionResolve(item => {
- let data = item.data;
- if (data && data.languageId && data.uri) {
- let mode = languageModes.getMode(data.languageId);
- let document = documents.get(data.uri);
- if (mode && mode.doResolve && document) {
- return mode.doResolve(document, item);
+ return runSafe(() => {
+ let data = item.data;
+ if (data && data.languageId && data.uri) {
+ let mode = languageModes.getMode(data.languageId);
+ let document = documents.get(data.uri);
+ if (mode && mode.doResolve && document) {
+ return mode.doResolve(document, item);
+ }
}
- }
- return item;
+ return item;
+ }, null, `Error while resolving completion proposal`);
});
connection.onHover(textDocumentPosition => {
- let document = documents.get(textDocumentPosition.textDocument.uri);
- let mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);
- if (mode && mode.doHover) {
- return mode.doHover(document, textDocumentPosition.position);
- }
- return null;
+ return runSafe(() => {
+ let document = documents.get(textDocumentPosition.textDocument.uri);
+ let mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);
+ if (mode && mode.doHover) {
+ return mode.doHover(document, textDocumentPosition.position);
+ }
+ return null;
+ }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`);
});
connection.onDocumentHighlight(documentHighlightParams => {
- let document = documents.get(documentHighlightParams.textDocument.uri);
- let mode = languageModes.getModeAtPosition(document, documentHighlightParams.position);
- if (mode && mode.findDocumentHighlight) {
- return mode.findDocumentHighlight(document, documentHighlightParams.position);
- }
- return [];
+ return runSafe(() => {
+ let document = documents.get(documentHighlightParams.textDocument.uri);
+ let mode = languageModes.getModeAtPosition(document, documentHighlightParams.position);
+ if (mode && mode.findDocumentHighlight) {
+ return mode.findDocumentHighlight(document, documentHighlightParams.position);
+ }
+ return [];
+ }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`);
});
connection.onDefinition(definitionParams => {
- let document = documents.get(definitionParams.textDocument.uri);
- let mode = languageModes.getModeAtPosition(document, definitionParams.position);
- if (mode && mode.findDefinition) {
- return mode.findDefinition(document, definitionParams.position);
- }
- return [];
+ return runSafe(() => {
+ let document = documents.get(definitionParams.textDocument.uri);
+ let mode = languageModes.getModeAtPosition(document, definitionParams.position);
+ if (mode && mode.findDefinition) {
+ return mode.findDefinition(document, definitionParams.position);
+ }
+ return [];
+ }, null, `Error while computing definitions for ${definitionParams.textDocument.uri}`);
});
connection.onReferences(referenceParams => {
- let document = documents.get(referenceParams.textDocument.uri);
- let mode = languageModes.getModeAtPosition(document, referenceParams.position);
- if (mode && mode.findReferences) {
- return mode.findReferences(document, referenceParams.position);
- }
- return [];
+ return runSafe(() => {
+ let document = documents.get(referenceParams.textDocument.uri);
+ let mode = languageModes.getModeAtPosition(document, referenceParams.position);
+ if (mode && mode.findReferences) {
+ return mode.findReferences(document, referenceParams.position);
+ }
+ return [];
+ }, [], `Error while computing references for ${referenceParams.textDocument.uri}`);
});
connection.onSignatureHelp(signatureHelpParms => {
- let document = documents.get(signatureHelpParms.textDocument.uri);
- let mode = languageModes.getModeAtPosition(document, signatureHelpParms.position);
- if (mode && mode.doSignatureHelp) {
- return mode.doSignatureHelp(document, signatureHelpParms.position);
- }
- return null;
+ return runSafe(() => {
+ let document = documents.get(signatureHelpParms.textDocument.uri);
+ let mode = languageModes.getModeAtPosition(document, signatureHelpParms.position);
+ if (mode && mode.doSignatureHelp) {
+ return mode.doSignatureHelp(document, signatureHelpParms.position);
+ }
+ return null;
+ }, null, `Error while computing signature help for ${signatureHelpParms.textDocument.uri}`);
});
connection.onDocumentRangeFormatting(async formatParams => {
- let document = documents.get(formatParams.textDocument.uri);
- let settings = await getDocumentSettings(document, () => true);
- if (!settings) {
- settings = globalSettings;
- }
- let unformattedTags: string = settings && settings.html && settings.html.format && settings.html.format.unformatted || '';
- let enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) };
+ return runSafe(async () => {
+ let document = documents.get(formatParams.textDocument.uri);
+ let settings = await getDocumentSettings(document, () => true);
+ if (!settings) {
+ settings = globalSettings;
+ }
+ let unformattedTags: string = settings && settings.html && settings.html.format && settings.html.format.unformatted || '';
+ let enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) };
- return format(languageModes, document, formatParams.range, formatParams.options, settings, enabledModes);
+ return format(languageModes, document, formatParams.range, formatParams.options, settings, enabledModes);
+ }, [], `Error while formatting range for ${formatParams.textDocument.uri}`);
});
connection.onDocumentLinks(documentLinkParam => {
- let document = documents.get(documentLinkParam.textDocument.uri);
- let links: DocumentLink[] = [];
- if (document) {
- let documentContext = getDocumentContext(document.uri, workspaceFolders);
- languageModes.getAllModesInDocument(document).forEach(m => {
- if (m.findDocumentLinks) {
- pushAll(links, m.findDocumentLinks(document, documentContext));
- }
- });
- }
- return links;
+ return runSafe(() => {
+ let document = documents.get(documentLinkParam.textDocument.uri);
+ let links: DocumentLink[] = [];
+ if (document) {
+ let documentContext = getDocumentContext(document.uri, workspaceFolders);
+ languageModes.getAllModesInDocument(document).forEach(m => {
+ if (m.findDocumentLinks) {
+ pushAll(links, m.findDocumentLinks(document, documentContext));
+ }
+ });
+ }
+ return links;
+ }, [], `Error while document links for ${documentLinkParam.textDocument.uri}`);
});
connection.onDocumentSymbol(documentSymbolParms => {
- let document = documents.get(documentSymbolParms.textDocument.uri);
- let symbols: SymbolInformation[] = [];
- languageModes.getAllModesInDocument(document).forEach(m => {
- if (m.findDocumentSymbols) {
- pushAll(symbols, m.findDocumentSymbols(document));
- }
- });
- return symbols;
+ return runSafe(() => {
+ let document = documents.get(documentSymbolParms.textDocument.uri);
+ let symbols: SymbolInformation[] = [];
+ languageModes.getAllModesInDocument(document).forEach(m => {
+ if (m.findDocumentSymbols) {
+ pushAll(symbols, m.findDocumentSymbols(document));
+ }
+ });
+ return symbols;
+ }, [], `Error while computing document symbols for ${documentSymbolParms.textDocument.uri}`);
});
connection.onRequest(DocumentColorRequest.type, params => {
- let infos: ColorInformation[] = [];
- let document = documents.get(params.textDocument.uri);
- if (document) {
- languageModes.getAllModesInDocument(document).forEach(m => {
- if (m.findDocumentColors) {
- pushAll(infos, m.findDocumentColors(document));
- }
- });
- }
- return infos;
+ return runSafe(() => {
+ let infos: ColorInformation[] = [];
+ let document = documents.get(params.textDocument.uri);
+ if (document) {
+ languageModes.getAllModesInDocument(document).forEach(m => {
+ if (m.findDocumentColors) {
+ pushAll(infos, m.findDocumentColors(document));
+ }
+ });
+ }
+ return infos;
+ }, [], `Error while computing document colors for ${params.textDocument.uri}`);
});
connection.onRequest(ColorPresentationRequest.type, params => {
- let document = documents.get(params.textDocument.uri);
- if (document) {
- let mode = languageModes.getModeAtPosition(document, params.range.start);
- if (mode && mode.getColorPresentations) {
- return mode.getColorPresentations(document, params.color, params.range);
+ return runSafe(() => {
+ let document = documents.get(params.textDocument.uri);
+ if (document) {
+ let mode = languageModes.getModeAtPosition(document, params.range.start);
+ if (mode && mode.getColorPresentations) {
+ return mode.getColorPresentations(document, params.color, params.range);
+ }
}
- }
- return [];
+ return [];
+ }, [], `Error while computing color presentations for ${params.textDocument.uri}`);
});
connection.onRequest(TagCloseRequest.type, params => {
- let document = documents.get(params.textDocument.uri);
- if (document) {
- let pos = params.position;
- if (pos.character > 0) {
- let mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1));
- if (mode && mode.doAutoClose) {
- return mode.doAutoClose(document, pos);
+ return runSafe(() => {
+ let document = documents.get(params.textDocument.uri);
+ if (document) {
+ let pos = params.position;
+ if (pos.character > 0) {
+ let mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1));
+ if (mode && mode.doAutoClose) {
+ return mode.doAutoClose(document, pos);
+ }
}
}
- }
- return null;
+ return null;
+ }, null, `Error while computing tag close actions for ${params.textDocument.uri}`);
});
diff --git a/extensions/html/server/src/utils/errors.ts b/extensions/html/server/src/utils/errors.ts
new file mode 100644
index 00000000000..d5a0c8e7d05
--- /dev/null
+++ b/extensions/html/server/src/utils/errors.ts
@@ -0,0 +1,33 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+'use strict';
+
+export function formatError(message: string, err: any): string {
+ if (err instanceof Error) {
+ let error = err;
+ return `${message}: ${error.message}\n${error.stack}`;
+ } else if (typeof err === 'string') {
+ return `${message}: ${err}`;
+ } else if (err) {
+ return `${message}: ${err.toString()}`;
+ }
+ return message;
+}
+
+export function runSafe(func: () => Thenable | T, errorVal: T, errorMessage: string): Thenable | T {
+ try {
+ let t = func();
+ if (t instanceof Promise) {
+ return t.then(void 0, e => {
+ console.error(formatError(errorMessage, e));
+ return errorVal;
+ });
+ }
+ return t;
+ } catch (e) {
+ console.error(formatError(errorMessage, e));
+ return errorVal;
+ }
+}
\ No newline at end of file