"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.pushEditToDocumentChanges = exports.transformWorkspaceEdit = exports.transformWorkspaceSymbol = exports.transformTextEdit = exports.transformSelectionRanges = exports.transformSelectionRange = exports.transformLocations = exports.transformLocation = exports.transformHover = exports.transformFoldingRanges = exports.transformDocumentSymbol = exports.transformCompletionList = exports.transformCompletionItem = exports.transformMarkdown = exports.transformDocumentLinkTarget = void 0; const language_core_1 = require("@volar/language-core"); const vscode_uri_1 = require("vscode-uri"); const common_1 = require("./common"); function transformDocumentLinkTarget(target, context) { const targetUri = vscode_uri_1.URI.parse(target); const clearUri = targetUri.with({ fragment: '' }).toString(true); const [virtualCode] = context.documents.getVirtualCodeByUri(clearUri); if (virtualCode) { for (const map of context.documents.getMaps(virtualCode)) { if (!map.map.mappings.some(mapping => (0, language_core_1.isDocumentLinkEnabled)(mapping.data))) { continue; } target = map.sourceFileDocument.uri; const hash = targetUri.fragment; const range = hash.match(/^L(\d+)(,(\d+))?(-L(\d+)(,(\d+))?)?$/); if (range) { const startLine = Number(range[1]) - 1; const startCharacter = Number(range[3] ?? 1) - 1; if (range[5] !== undefined) { const endLine = Number(range[5]) - 1; const endCharacter = Number(range[7] ?? 1) - 1; const sourceRange = map.getSourceRange({ start: { line: startLine, character: startCharacter }, end: { line: endLine, character: endCharacter }, }); if (sourceRange) { target += '#L' + (sourceRange.start.line + 1) + ',' + (sourceRange.start.character + 1); target += '-L' + (sourceRange.end.line + 1) + ',' + (sourceRange.end.character + 1); break; } } else { const sourcePos = map.getSourcePosition({ line: startLine, character: startCharacter }); if (sourcePos) { target += '#L' + (sourcePos.line + 1) + ',' + (sourcePos.character + 1); break; } } } } } return target; } exports.transformDocumentLinkTarget = transformDocumentLinkTarget; function transformMarkdown(content, context) { return content.replace(/(\[[^\]]+\])(\([^)]+\))/g, s => { const match = s.match(/(\[[^\]]+\])(\([^)]+\))/); return `${match[1]}(${transformDocumentLinkTarget(match[2].slice(1, -1), context)})`; }); } exports.transformMarkdown = transformMarkdown; function transformCompletionItem(item, getOtherRange, document, context) { return { ...item, additionalTextEdits: item.additionalTextEdits ?.map(edit => transformTextEdit(edit, getOtherRange, document)) .filter(common_1.notEmpty), textEdit: item.textEdit ? transformTextEdit(item.textEdit, getOtherRange, document) : undefined, documentation: item.documentation ? typeof item.documentation === 'string' ? transformMarkdown(item.documentation, context) : item.documentation.kind === 'markdown' ? { kind: 'markdown', value: transformMarkdown(item.documentation.value, context) } : item.documentation : undefined }; } exports.transformCompletionItem = transformCompletionItem; function transformCompletionList(completionList, getOtherRange, document, context) { return { isIncomplete: completionList.isIncomplete, itemDefaults: completionList.itemDefaults ? { ...completionList.itemDefaults, editRange: completionList.itemDefaults.editRange ? 'replace' in completionList.itemDefaults.editRange ? { insert: getOtherRange(completionList.itemDefaults.editRange.insert), replace: getOtherRange(completionList.itemDefaults.editRange.replace), } : getOtherRange(completionList.itemDefaults.editRange) : undefined, } : undefined, items: completionList.items.map(item => transformCompletionItem(item, getOtherRange, document, context)), }; } exports.transformCompletionList = transformCompletionList; function transformDocumentSymbol(symbol, getOtherRange) { const range = getOtherRange(symbol.range); if (!range) { return; } const selectionRange = getOtherRange(symbol.selectionRange); if (!selectionRange) { return; } return { ...symbol, range, selectionRange, children: symbol.children ?.map(child => transformDocumentSymbol(child, getOtherRange)) .filter(common_1.notEmpty), }; } exports.transformDocumentSymbol = transformDocumentSymbol; function transformFoldingRanges(ranges, getOtherRange) { const result = []; for (const range of ranges) { const otherRange = getOtherRange({ start: { line: range.startLine, character: range.startCharacter ?? 0 }, end: { line: range.endLine, character: range.endCharacter ?? 0 }, }); if (otherRange) { range.startLine = otherRange.start.line; range.endLine = otherRange.end.line; if (range.startCharacter !== undefined) { range.startCharacter = otherRange.start.character; } if (range.endCharacter !== undefined) { range.endCharacter = otherRange.end.character; } result.push(range); } } return result; } exports.transformFoldingRanges = transformFoldingRanges; function transformHover(hover, getOtherRange) { if (!hover?.range) { return hover; } const range = getOtherRange(hover.range); if (!range) { return; } return { ...hover, range, }; } exports.transformHover = transformHover; function transformLocation(location, getOtherRange) { const range = getOtherRange(location.range); if (!range) { return; } return { ...location, range, }; } exports.transformLocation = transformLocation; function transformLocations(locations, getOtherRange) { return locations .map(location => transformLocation(location, getOtherRange)) .filter(common_1.notEmpty); } exports.transformLocations = transformLocations; function transformSelectionRange(location, getOtherRange) { const range = getOtherRange(location.range); if (!range) { return; } const parent = location.parent ? transformSelectionRange(location.parent, getOtherRange) : undefined; return { range, parent, }; } exports.transformSelectionRange = transformSelectionRange; function transformSelectionRanges(locations, getOtherRange) { return locations .map(location => transformSelectionRange(location, getOtherRange)) .filter(common_1.notEmpty); } exports.transformSelectionRanges = transformSelectionRanges; function transformTextEdit(textEdit, getOtherRange, document) { if ('range' in textEdit) { let range = getOtherRange(textEdit.range); if (range) { return { ...textEdit, range, }; } ; const cover = tryRecoverTextEdit(getOtherRange, textEdit.range, textEdit.newText, document); if (cover) { return { ...textEdit, range: cover.range, newText: cover.newText, }; } } else if ('replace' in textEdit && 'insert' in textEdit) { const insert = getOtherRange(textEdit.insert); const replace = insert ? getOtherRange(textEdit.replace) : undefined; if (insert && replace) { return { ...textEdit, insert, replace, }; } const recoverInsert = tryRecoverTextEdit(getOtherRange, textEdit.insert, textEdit.newText, document); const recoverReplace = recoverInsert ? tryRecoverTextEdit(getOtherRange, textEdit.replace, textEdit.newText, document) : undefined; if (recoverInsert && recoverReplace && recoverInsert.newText === recoverReplace.newText) { return { ...textEdit, insert: recoverInsert.range, replace: recoverReplace.range, newText: recoverInsert.newText, }; } } } exports.transformTextEdit = transformTextEdit; /** * update edit text from ". foo" to " foo" * fix https://github.com/johnsoncodehk/volar/issues/2155 */ function tryRecoverTextEdit(getOtherRange, replaceRange, newText, document) { if (replaceRange.start.line === replaceRange.end.line && replaceRange.end.character > replaceRange.start.character) { let character = replaceRange.start.character; while (newText.length && replaceRange.end.character > character) { const newStart = { line: replaceRange.start.line, character: replaceRange.start.character + 1 }; if (document.getText({ start: replaceRange.start, end: newStart }) === newText[0]) { newText = newText.slice(1); character++; const otherRange = getOtherRange({ start: newStart, end: replaceRange.end }); if (otherRange) { return { newText, range: otherRange, }; } } else { break; } } } } function transformWorkspaceSymbol(symbol, getOtherLocation) { if (!('range' in symbol.location)) { return symbol; } const loc = getOtherLocation(symbol.location); if (!loc) { return; } return { ...symbol, location: loc, }; } exports.transformWorkspaceSymbol = transformWorkspaceSymbol; function transformWorkspaceEdit(edit, { documents }, mode, versions = {}) { const sourceResult = {}; let hasResult = false; for (const tsUri in edit.changeAnnotations) { sourceResult.changeAnnotations ??= {}; const tsAnno = edit.changeAnnotations[tsUri]; const [virtualCode] = documents.getVirtualCodeByUri(tsUri); if (virtualCode) { for (const map of documents.getMaps(virtualCode)) { // TODO: check capability? const uri = map.sourceFileDocument.uri; sourceResult.changeAnnotations[uri] = tsAnno; } } else { sourceResult.changeAnnotations[tsUri] = tsAnno; } } for (const tsUri in edit.changes) { sourceResult.changes ??= {}; const [virtualCode] = documents.getVirtualCodeByUri(tsUri); if (virtualCode) { for (const map of documents.getMaps(virtualCode)) { const tsEdits = edit.changes[tsUri]; for (const tsEdit of tsEdits) { if (mode === 'rename' || mode === 'fileName' || mode === 'codeAction') { let _data; const range = map.getSourceRange(tsEdit.range, data => { _data = data; return (0, language_core_1.isRenameEnabled)(data); }); if (range) { sourceResult.changes[map.sourceFileDocument.uri] ??= []; sourceResult.changes[map.sourceFileDocument.uri].push({ newText: (0, language_core_1.resolveRenameEditText)(tsEdit.newText, _data), range, }); hasResult = true; } } else { const range = map.getSourceRange(tsEdit.range); if (range) { sourceResult.changes[map.sourceFileDocument.uri] ??= []; sourceResult.changes[map.sourceFileDocument.uri].push({ newText: tsEdit.newText, range }); hasResult = true; } } } } } else { sourceResult.changes[tsUri] = edit.changes[tsUri]; hasResult = true; } } if (edit.documentChanges) { for (const tsDocEdit of edit.documentChanges) { sourceResult.documentChanges ??= []; let sourceEdit; if ('textDocument' in tsDocEdit) { const [virtualCode] = documents.getVirtualCodeByUri(tsDocEdit.textDocument.uri); if (virtualCode) { for (const map of documents.getMaps(virtualCode)) { sourceEdit = { textDocument: { uri: map.sourceFileDocument.uri, version: versions[map.sourceFileDocument.uri] ?? null, }, edits: [], }; for (const tsEdit of tsDocEdit.edits) { if (mode === 'rename' || mode === 'fileName' || mode === 'codeAction') { let _data; const range = map.getSourceRange(tsEdit.range, data => { _data = data; // fix https://github.com/johnsoncodehk/volar/issues/1091 return (0, language_core_1.isRenameEnabled)(data); }); if (range) { sourceEdit.edits.push({ annotationId: 'annotationId' in tsEdit ? tsEdit.annotationId : undefined, newText: (0, language_core_1.resolveRenameEditText)(tsEdit.newText, _data), range, }); } } else { const range = map.getSourceRange(tsEdit.range); if (range) { sourceEdit.edits.push({ annotationId: 'annotationId' in tsEdit ? tsEdit.annotationId : undefined, newText: tsEdit.newText, range, }); } } } if (!sourceEdit.edits.length) { sourceEdit = undefined; } } } else { sourceEdit = tsDocEdit; } } else if (tsDocEdit.kind === 'create') { sourceEdit = tsDocEdit; // TODO: remove .ts? } else if (tsDocEdit.kind === 'rename') { const [virtualCode] = documents.getVirtualCodeByUri(tsDocEdit.oldUri); if (virtualCode) { for (const map of documents.getMaps(virtualCode)) { // TODO: check capability? sourceEdit = { kind: 'rename', oldUri: map.sourceFileDocument.uri, newUri: tsDocEdit.newUri /* TODO: remove .ts? */, options: tsDocEdit.options, annotationId: tsDocEdit.annotationId, }; } } else { sourceEdit = tsDocEdit; } } else if (tsDocEdit.kind === 'delete') { const [virtualCode] = documents.getVirtualCodeByUri(tsDocEdit.uri); if (virtualCode) { for (const map of documents.getMaps(virtualCode)) { // TODO: check capability? sourceEdit = { kind: 'delete', uri: map.sourceFileDocument.uri, options: tsDocEdit.options, annotationId: tsDocEdit.annotationId, }; } } else { sourceEdit = tsDocEdit; } } if (sourceEdit) { pushEditToDocumentChanges(sourceResult.documentChanges, sourceEdit); hasResult = true; } } } if (hasResult) { return sourceResult; } } exports.transformWorkspaceEdit = transformWorkspaceEdit; function pushEditToDocumentChanges(arr, item) { const current = arr.find(edit => 'textDocument' in edit && 'textDocument' in item && edit.textDocument.uri === item.textDocument.uri); if (current) { current.edits.push(...item.edits); } else { arr.push(item); } } exports.pushEditToDocumentChanges = pushEditToDocumentChanges; //# sourceMappingURL=transform.js.map