UNPKG

@typespec/compiler

Version:

TypeSpec compiler and standard library

121 lines 5.06 kB
import { DiagnosticSeverity } from "vscode-languageserver"; import { getDiagnosticTemplateInstantitationTrace, getSourceLocation, } from "../core/diagnostics.js"; import { getTypeName } from "../core/helpers/type-name-utils.js"; import { isDefined } from "../utils/misc.js"; /** Convert TypeSpec Diagnostic to Lsp diagnostic. Each TypeSpec diagnostic could produce multiple lsp ones when it involve multiple locations. */ export function convertDiagnosticToLsp(fileService, program, document, diagnostic) { const root = getVSLocation(fileService, getSourceLocation(diagnostic.target, { locateId: true }), document); if (root === undefined || !fileService.upToDate(root.document)) return []; const instantiationNodes = getDiagnosticTemplateInstantitationTrace(diagnostic.target); const relatedInformation = []; const relatedDiagnostics = []; if (instantiationNodes.length > 0) { const items = instantiationNodes .map((node) => getVSLocationWithTypeInfo(program, fileService, node, document)) .filter(isDefined); for (const location of items) { relatedInformation.push({ location: { uri: location.document.uri, range: location.range }, message: `in instantiation of template \`${location.typeName}\``, }); } const [last, ...rest] = [...items].reverse(); if (last) { relatedDiagnostics.push([ createLspDiagnostic({ range: last.range, message: `In instantiation of this template:`, severity: DiagnosticSeverity.Warning, code: diagnostic.code, relatedInformation: [ ...rest.map((location) => ({ location: { uri: location.document.uri, range: location.range }, message: `in instantiation of template \`${location.typeName}\``, })), { location: { uri: root.document.uri, range: root.range }, message: diagnostic.message, }, ], }), last.document, ]); } } const rootDiagnostic = [ createLspDiagnostic({ range: root.range, message: diagnostic.message, severity: convertSeverity(diagnostic.severity), code: diagnostic.code, relatedInformation, }), root.document, ]; return [rootDiagnostic, ...relatedDiagnostics]; } function createLspDiagnostic(diag) { return { source: "TypeSpec", ...diag, }; } function getDocumentForLocation(fileService, location, currentDocument) { if (location?.file) { const doc = location.file.document; if (doc) { return doc; } // Since we are using cache between compilations, it's possible that when the result is used as cache, the file is actually opened or closed, // so need to do some handling for the open case here. No special handling for the closed case which will just be ignored by client const opened = fileService.getOpenDocument(location.file.path); if (opened) { // the doc is opened now and return it if it's not out-of-date // compared to when it was first opened. const initVersion = fileService.getOpenDocumentInitVersion(opened.uri); if (opened.version === initVersion) { return opened; } } return undefined; } else { // https://github.com/microsoft/language-server-protocol/issues/256 // // LSP does not currently allow sending a diagnostic with no location so // we report diagnostics with no location on the document that changed to // trigger. return currentDocument; } } function getVSLocation(fileService, location, currentDocument) { if (location === undefined) return undefined; const document = getDocumentForLocation(fileService, location, currentDocument); if (!document) { return undefined; } const start = document.positionAt(location?.pos ?? 0); const end = document.positionAt(location?.end ?? 0); return { range: { start, end }, document }; } function getVSLocationWithTypeInfo(program, fileService, node, document) { const location = getVSLocation(fileService, getSourceLocation(node, { locateId: true }), document); if (location === undefined) return undefined; return { ...location, node, typeName: getTypeName(program.checker.getTypeForNode(node)), }; } function convertSeverity(severity) { switch (severity) { case "warning": return DiagnosticSeverity.Warning; case "error": return DiagnosticSeverity.Error; } } //# sourceMappingURL=diagnostics.js.map