/*--------------------------------------------------------------------------------------------- * Copyright (c) Red Hat, Inc. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { isKubernetesAssociatedDocument } from '../../languageservice/parser/isKubernetes'; import { ResultLimitReachedNotification } from '../../requestTypes'; import * as path from 'path'; export class LanguageHandlers { constructor(connection, languageService, yamlSettings, validationHandler) { this.connection = connection; this.languageService = languageService; this.yamlSettings = yamlSettings; this.validationHandler = validationHandler; this.pendingLimitExceededWarnings = {}; } registerHandlers() { this.connection.onDocumentLinks((params) => this.documentLinkHandler(params)); this.connection.onDocumentSymbol((documentSymbolParams) => this.documentSymbolHandler(documentSymbolParams)); this.connection.onDocumentFormatting((formatParams) => this.formatterHandler(formatParams)); this.connection.onHover((textDocumentPositionParams) => this.hoverHandler(textDocumentPositionParams)); this.connection.onCompletion((textDocumentPosition) => this.completionHandler(textDocumentPosition)); this.connection.onDidChangeWatchedFiles((change) => this.watchedFilesHandler(change)); this.connection.onFoldingRanges((params) => this.foldingRangeHandler(params)); this.connection.onSelectionRanges((params) => this.selectionRangeHandler(params)); this.connection.onCodeAction((params) => this.codeActionHandler(params)); this.connection.onDocumentOnTypeFormatting((params) => this.formatOnTypeHandler(params)); this.connection.onCodeLens((params) => this.codeLensHandler(params)); this.connection.onCodeLensResolve((params) => this.codeLensResolveHandler(params)); this.connection.onDefinition((params) => this.definitionHandler(params)); this.yamlSettings.documents.onDidChangeContent((change) => this.cancelLimitExceededWarnings(change.document.uri)); this.yamlSettings.documents.onDidClose((event) => this.cancelLimitExceededWarnings(event.document.uri)); } documentLinkHandler(params) { const document = this.yamlSettings.documents.get(params.textDocument.uri); if (!document) { return Promise.resolve([]); } return this.languageService.findLinks(document); } /** * Called when the code outline in an editor needs to be populated * Returns a list of symbols that is then shown in the code outline */ documentSymbolHandler(documentSymbolParams) { const document = this.yamlSettings.documents.get(documentSymbolParams.textDocument.uri); if (!document) { return; } const onResultLimitExceeded = this.onResultLimitExceeded(document.uri, this.yamlSettings.maxItemsComputed, 'document symbols'); const context = { resultLimit: this.yamlSettings.maxItemsComputed, onResultLimitExceeded }; if (this.yamlSettings.hierarchicalDocumentSymbolSupport) { return this.languageService.findDocumentSymbols2(document, context); } else { return this.languageService.findDocumentSymbols(document, context); } } /** * Called when the formatter is invoked * Returns the formatted document content using prettier */ formatterHandler(formatParams) { const document = this.yamlSettings.documents.get(formatParams.textDocument.uri); if (!document) { return; } const customFormatterSettings = { tabWidth: formatParams.options.tabSize, ...this.yamlSettings.yamlFormatterSettings, }; return this.languageService.doFormat(document, customFormatterSettings); } formatOnTypeHandler(params) { const document = this.yamlSettings.documents.get(params.textDocument.uri); if (!document) { return; } return this.languageService.doDocumentOnTypeFormatting(document, params); } /** * Called when the user hovers with their mouse over a keyword * Returns an informational tooltip */ hoverHandler(textDocumentPositionParams) { const document = this.yamlSettings.documents.get(textDocumentPositionParams.textDocument.uri); if (!document) { return Promise.resolve(undefined); } return this.languageService.doHover(document, textDocumentPositionParams.position); } /** * Called when auto-complete is triggered in an editor * Returns a list of valid completion items */ completionHandler(textDocumentPosition) { const textDocument = this.yamlSettings.documents.get(textDocumentPosition.textDocument.uri); const result = { items: [], isIncomplete: false, }; if (!textDocument) { return Promise.resolve(result); } return this.languageService.doComplete(textDocument, textDocumentPosition.position, isKubernetesAssociatedDocument(textDocument, this.yamlSettings.specificValidatorPaths)); } /** * Called when a monitored file is changed in an editor * Re-validates the entire document */ watchedFilesHandler(change) { let hasChanges = false; change.changes.forEach((c) => { if (this.languageService.resetSchema(c.uri)) { hasChanges = true; } }); if (hasChanges) { this.yamlSettings.documents.all().forEach((document) => this.validationHandler.validate(document)); } } foldingRangeHandler(params) { const textDocument = this.yamlSettings.documents.get(params.textDocument.uri); if (!textDocument) { return; } const capabilities = this.yamlSettings.capabilities.textDocument.foldingRange; const rangeLimit = this.yamlSettings.maxItemsComputed || capabilities.rangeLimit; const onRangeLimitExceeded = this.onResultLimitExceeded(textDocument.uri, rangeLimit, 'folding ranges'); const context = { rangeLimit, onRangeLimitExceeded, lineFoldingOnly: capabilities.lineFoldingOnly, }; return this.languageService.getFoldingRanges(textDocument, context); } selectionRangeHandler(params) { const textDocument = this.yamlSettings.documents.get(params.textDocument.uri); if (!textDocument) { return; } return this.languageService.getSelectionRanges(textDocument, params.positions); } codeActionHandler(params) { const textDocument = this.yamlSettings.documents.get(params.textDocument.uri); if (!textDocument) { return; } return this.languageService.getCodeAction(textDocument, params); } codeLensHandler(params) { const textDocument = this.yamlSettings.documents.get(params.textDocument.uri); if (!textDocument) { return; } return this.languageService.getCodeLens(textDocument); } codeLensResolveHandler(param) { return this.languageService.resolveCodeLens(param); } definitionHandler(params) { const textDocument = this.yamlSettings.documents.get(params.textDocument.uri); if (!textDocument) { return; } return this.languageService.doDefinition(textDocument, params); } // Adapted from: // https://github.com/microsoft/vscode/blob/94c9ea46838a9a619aeafb7e8afd1170c967bb55/extensions/json-language-features/server/src/jsonServer.ts#L172 cancelLimitExceededWarnings(uri) { const warning = this.pendingLimitExceededWarnings[uri]; if (warning && warning.timeout) { clearTimeout(warning.timeout); delete this.pendingLimitExceededWarnings[uri]; } } onResultLimitExceeded(uri, resultLimit, name) { return () => { let warning = this.pendingLimitExceededWarnings[uri]; if (warning) { if (!warning.timeout) { // already shown return; } warning.features[name] = name; warning.timeout.refresh(); } else { warning = { features: { [name]: name } }; warning.timeout = setTimeout(() => { this.connection.sendNotification(ResultLimitReachedNotification.type, `${path.basename(uri)}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`); warning.timeout = undefined; }, 2000); this.pendingLimitExceededWarnings[uri] = warning; } }; } } //# sourceMappingURL=languageHandlers.js.map