"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.errorMarkups = void 0; exports.register = register; exports.transformDiagnostic = transformDiagnostic; exports.updateRange = updateRange; const language_core_1 = require("@volar/language-core"); const vscode_uri_1 = require("vscode-uri"); const cancellation_1 = require("../utils/cancellation"); const common_1 = require("../utils/common"); const dedupe = require("../utils/dedupe"); const featureWorkers_1 = require("../utils/featureWorkers"); const uriMap_1 = require("../utils/uriMap"); exports.errorMarkups = (0, uriMap_1.createUriMap)(); function register(context) { const lastResponses = (0, uriMap_1.createUriMap)(); const cacheMaps = { semantic: new Map(), syntactic: new Map(), }; context.env.onDidChangeConfiguration?.(() => { lastResponses.clear(); cacheMaps.semantic.clear(); cacheMaps.syntactic.clear(); }); return async (uri, response, token = cancellation_1.NoneCancellationToken) => { let langaugeIdAndSnapshot; const decoded = context.decodeEmbeddedDocumentUri(uri); if (decoded) { langaugeIdAndSnapshot = context.language.scripts.get(decoded[0])?.generated?.embeddedCodes.get(decoded[1]); } else { langaugeIdAndSnapshot = context.language.scripts.get(uri); } if (!langaugeIdAndSnapshot) { return []; } const document = context.documents.get(uri, langaugeIdAndSnapshot.languageId, langaugeIdAndSnapshot.snapshot); const lastResponse = lastResponses.get(uri) ?? lastResponses.set(uri, { semantic: { errors: [] }, syntactic: { errors: [] }, }).get(uri); let updateCacheRangeFailed = false; let errorsUpdated = false; let lastCheckCancelAt = 0; for (const cache of Object.values(lastResponse)) { const oldSnapshot = cache.snapshot; const oldDocument = cache.document; const change = oldSnapshot ? langaugeIdAndSnapshot.snapshot.getChangeRange(oldSnapshot) : undefined; cache.snapshot = langaugeIdAndSnapshot.snapshot; cache.document = document; if (!updateCacheRangeFailed && oldDocument && change) { const changeRange = { range: { start: oldDocument.positionAt(change.span.start), end: oldDocument.positionAt(change.span.start + change.span.length), }, newEnd: document.positionAt(change.span.start + change.newLength), }; for (const error of cache.errors) { if (!updateRange(error.range, changeRange)) { updateCacheRangeFailed = true; break; } } } } await worker('syntactic', cacheMaps.syntactic, lastResponse.syntactic); processResponse(); await worker('semantic', cacheMaps.semantic, lastResponse.semantic); return collectErrors(); function processResponse() { if (errorsUpdated && !updateCacheRangeFailed) { response?.(collectErrors()); errorsUpdated = false; } } function collectErrors() { return Object.values(lastResponse).flatMap(({ errors }) => errors); } async function worker(kind, cacheMap, cache) { const result = await (0, featureWorkers_1.documentFeatureWorker)(context, uri, docs => docs[2].mappings.some(mapping => (0, language_core_1.isDiagnosticsEnabled)(mapping.data)), async (plugin, document) => { const interFileDependencies = plugin[0].capabilities.diagnosticProvider?.interFileDependencies; if (kind === 'semantic' !== interFileDependencies) { return; } if (Date.now() - lastCheckCancelAt >= 10) { await (0, common_1.sleep)(10); // waiting LSP event polling lastCheckCancelAt = Date.now(); } if (token.isCancellationRequested) { return; } const pluginIndex = context.plugins.indexOf(plugin); const pluginCache = cacheMap.get(pluginIndex) ?? cacheMap.set(pluginIndex, new Map()).get(pluginIndex); const cache = pluginCache.get(document.uri); if (!interFileDependencies && cache && cache.documentVersion === document.version) { return cache.errors; } const errors = await plugin[1].provideDiagnostics?.(document, token) || []; errors.forEach(error => { error.data = { uri: uri.toString(), version: document.version, pluginIndex: pluginIndex, isFormat: false, original: { data: error.data, }, documentUri: document.uri, }; }); errorsUpdated = true; pluginCache.set(document.uri, { documentVersion: document.version, errors, }); return errors; }, (errors, map) => { return errors .map(error => transformDiagnostic(context, error, map)) .filter(error => !!error); }, arr => dedupe.withDiagnostics(arr.flat())); if (result) { cache.errors = result; cache.snapshot = langaugeIdAndSnapshot?.snapshot; } } }; } function transformDiagnostic(context, error, docs) { // clone it to avoid modify cache let _error = { ...error }; if (docs) { const range = (0, featureWorkers_1.getSourceRange)(docs, error.range, data => (0, language_core_1.shouldReportDiagnostics)(data, error.source, error.code)); if (!range) { return; } _error.range = range; } if (_error.relatedInformation) { const relatedInfos = []; for (const info of _error.relatedInformation) { const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(info.location.uri)); const sourceScript = decoded && context.language.scripts.get(decoded[0]); const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]); if (sourceScript && virtualCode) { const embeddedDocument = context.documents.get(context.encodeEmbeddedDocumentUri(sourceScript.id, virtualCode.id), virtualCode.languageId, virtualCode.snapshot); for (const [sourceScript, map] of context.language.maps.forEach(virtualCode)) { const sourceDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot); const docs = [sourceDocument, embeddedDocument, map]; const range = (0, featureWorkers_1.getSourceRange)(docs, info.location.range, data => (0, language_core_1.shouldReportDiagnostics)(data, undefined, undefined)); if (range) { relatedInfos.push({ location: { uri: sourceDocument.uri, range, }, message: info.message, }); } } } else { relatedInfos.push(info); } } _error.relatedInformation = relatedInfos; } return _error; } function updateRange(range, change) { if (!updatePosition(range.start, change, false)) { return; } if (!updatePosition(range.end, change, true)) { return; } if (range.end.line === range.start.line && range.end.character <= range.start.character) { range.end.character++; } return range; } function updatePosition(position, change, isEnd) { if (change.range.end.line > position.line) { if (change.newEnd.line > position.line) { // No change return true; } else if (change.newEnd.line === position.line) { position.character = Math.min(position.character, change.newEnd.character); return true; } else if (change.newEnd.line < position.line) { position.line = change.newEnd.line; position.character = change.newEnd.character; return true; } } else if (change.range.end.line === position.line) { const characterDiff = change.newEnd.character - change.range.end.character; if (position.character >= change.range.end.character) { if (change.newEnd.line !== change.range.end.line) { position.line = change.newEnd.line; position.character = change.newEnd.character + position.character - change.range.end.character; } else { if (isEnd ? change.range.end.character < position.character : change.range.end.character <= position.character) { position.character += characterDiff; } else { const offset = change.range.end.character - position.character; if (-characterDiff > offset) { position.character += characterDiff + offset; } } } return true; } else { if (change.newEnd.line === change.range.end.line) { const offset = change.range.end.character - position.character; if (-characterDiff > offset) { position.character += characterDiff + offset; } } else if (change.newEnd.line < change.range.end.line) { position.line = change.newEnd.line; position.character = change.newEnd.character; } else { // No change } return true; } } else if (change.range.end.line < position.line) { position.line += change.newEnd.line - change.range.end.line; return true; } return false; } //# sourceMappingURL=provideDiagnostics.js.map