"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.register = register; const language_core_1 = require("@volar/language-core"); const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument"); const vscode_uri_1 = require("vscode-uri"); const cancellation_1 = require("../utils/cancellation"); const common_1 = require("../utils/common"); const featureWorkers_1 = require("../utils/featureWorkers"); function register(context) { return async (uri, options, range, onTypeParams, token = cancellation_1.NoneCancellationToken) => { const sourceScript = context.language.scripts.get(uri); if (!sourceScript) { return; } let document = context.documents.get(uri, sourceScript.languageId, sourceScript.snapshot); range ??= { start: document.positionAt(0), end: document.positionAt(document.getText().length), }; if (!sourceScript.generated) { return onTypeParams ? (await tryFormat(document, document, sourceScript, undefined, 0, onTypeParams.position, onTypeParams.ch))?.edits : (await tryFormat(document, document, sourceScript, undefined, 0, range, undefined))?.edits; } const embeddedRanges = new Map(); // TODO: Formatting of upper-level virtual code may cause offset of lower-level selection range const startOffset = document.offsetAt(range.start); const endOffset = document.offsetAt(range.end); for (const code of (0, language_core_1.forEachEmbeddedCode)(sourceScript.generated.root)) { const map = context.language.maps.get(code, sourceScript); if (map) { const embeddedRange = (0, common_1.findOverlapCodeRange)(startOffset, endOffset, map, language_core_1.isFormattingEnabled); if (embeddedRange) { if (embeddedRange.start === map.mappings[0].generatedOffsets[0]) { embeddedRange.start = 0; } const lastMapping = map.mappings[map.mappings.length - 1]; if (embeddedRange.end === lastMapping.generatedOffsets[lastMapping.generatedOffsets.length - 1] + (lastMapping.generatedLengths ?? lastMapping.lengths)[lastMapping.lengths.length - 1]) { embeddedRange.end = code.snapshot.getLength(); } embeddedRanges.set(code.id, embeddedRange); } } } try { const originalDocument = document; let tempSourceSnapshot = sourceScript.snapshot; let tempVirtualFile = context.language.scripts.set(vscode_uri_1.URI.parse(sourceScript.id.toString() + '.tmp'), sourceScript.snapshot, sourceScript.languageId, [sourceScript.generated.languagePlugin])?.generated?.root; if (!tempVirtualFile) { return; } let currentCodes = []; for (let depth = 0; (currentCodes = getNestedEmbeddedFiles(context, sourceScript.id, tempVirtualFile, depth)).length > 0; depth++) { let edits = []; for (const code of currentCodes) { if (!code.mappings.some(mapping => (0, language_core_1.isFormattingEnabled)(mapping.data))) { continue; } const currentRange = embeddedRanges.get(code.id); if (!currentRange) { continue; } const isChildRange = [...(0, language_core_1.forEachEmbeddedCode)(code)].some(child => { if (child === code) { return false; } const childRange = embeddedRanges.get(child.id); return childRange && childRange.end - childRange.start >= currentRange.end - currentRange.start; }); if (isChildRange) { continue; } const docs = [ context.documents.get(uri, sourceScript.languageId, tempSourceSnapshot), context.documents.get(context.encodeEmbeddedDocumentUri(uri, code.id), code.languageId, code.snapshot), context.language.mapperFactory(code.mappings), ]; let embeddedResult; if (onTypeParams) { for (const embeddedPosition of (0, featureWorkers_1.getGeneratedPositions)(docs, onTypeParams.position)) { embeddedResult = await tryFormat(docs[0], docs[1], sourceScript, code, depth, embeddedPosition, onTypeParams.ch); break; } } else if (currentRange) { embeddedResult = await tryFormat(docs[0], docs[1], sourceScript, code, depth, { start: docs[1].positionAt(currentRange.start), end: docs[1].positionAt(currentRange.end), }); } if (!embeddedResult) { continue; } for (const textEdit of embeddedResult.edits) { const range = (0, featureWorkers_1.getSourceRange)(docs, textEdit.range); if (range) { edits.push({ newText: textEdit.newText, range, }); } } } if (edits.length > 0) { const newText = vscode_languageserver_textdocument_1.TextDocument.applyEdits(document, edits); document = vscode_languageserver_textdocument_1.TextDocument.create(document.uri, document.languageId, document.version + 1, newText); tempSourceSnapshot = (0, common_1.stringToSnapshot)(newText); tempVirtualFile = context.language.scripts.set(vscode_uri_1.URI.parse(sourceScript.id.toString() + '.tmp'), tempSourceSnapshot, sourceScript.languageId, [sourceScript.generated.languagePlugin])?.generated?.root; if (!tempVirtualFile) { break; } } } if (document.getText() === originalDocument.getText()) { return; } const editRange = { start: originalDocument.positionAt(0), end: originalDocument.positionAt(originalDocument.getText().length), }; const textEdit = { range: editRange, newText: document.getText(), }; return [textEdit]; } finally { context.language.scripts.delete(vscode_uri_1.URI.parse(sourceScript.id.toString() + '.tmp')); } async function tryFormat(sourceDocument, document, sourceScript, virtualCode, embeddedLevel, rangeOrPosition, ch) { if (context.disabledEmbeddedDocumentUris.get(vscode_uri_1.URI.parse(document.uri))) { return; } let codeOptions; rangeOrPosition ??= { start: document.positionAt(0), end: document.positionAt(document.getText().length), }; if (virtualCode) { codeOptions = { level: embeddedLevel, initialIndentLevel: 0, }; if (virtualCode.mappings.length) { const firstMapping = virtualCode.mappings[0]; const startOffset = firstMapping.sourceOffsets[0]; const startPosition = sourceDocument.positionAt(startOffset); codeOptions.initialIndentLevel = computeInitialIndent(sourceDocument.getText(), sourceDocument.offsetAt({ line: startPosition.line, character: 0 }), options); } for (const plugin of context.plugins) { if (context.disabledServicePlugins.has(plugin[1])) { continue; } codeOptions = await plugin[1].resolveEmbeddedCodeFormattingOptions?.(sourceScript, virtualCode, codeOptions, token) ?? codeOptions; } } for (const plugin of context.plugins) { if (context.disabledServicePlugins.has(plugin[1])) { continue; } if (token.isCancellationRequested) { break; } let edits; try { if (ch !== undefined && rangeOrPosition && 'line' in rangeOrPosition && 'character' in rangeOrPosition) { if (plugin[0].capabilities.documentOnTypeFormattingProvider?.triggerCharacters?.includes(ch)) { edits = await plugin[1].provideOnTypeFormattingEdits?.(document, rangeOrPosition, ch, options, codeOptions, token); } } else if (ch === undefined && rangeOrPosition && 'start' in rangeOrPosition && 'end' in rangeOrPosition) { edits = await plugin[1].provideDocumentFormattingEdits?.(document, rangeOrPosition, options, codeOptions, token); } } catch (err) { console.warn(err); } if (!edits) { continue; } return { plugin, edits, }; } } }; } function getNestedEmbeddedFiles(context, uri, rootCode, depth) { const nestedCodesByLevel = [[rootCode]]; while (true) { if (nestedCodesByLevel.length > depth) { return nestedCodesByLevel[depth]; } const nestedCodes = []; for (const code of nestedCodesByLevel[nestedCodesByLevel.length - 1]) { if (code.embeddedCodes) { for (const embedded of code.embeddedCodes) { if (!context.disabledEmbeddedDocumentUris.get(context.encodeEmbeddedDocumentUri(uri, embedded.id))) { nestedCodes.push(embedded); } } } } nestedCodesByLevel.push(nestedCodes); } } function computeInitialIndent(content, i, options) { let nChars = 0; const tabSize = options.tabSize || 4; while (i < content.length) { const ch = content.charAt(i); if (ch === ' ') { nChars++; } else if (ch === '\t') { nChars += tabSize; } else { break; } i++; } return Math.floor(nChars / tabSize); } //# sourceMappingURL=provideDocumentFormattingEdits.js.map