460 lines
20 KiB
JavaScript
460 lines
20 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.transformDocumentLinkTarget = transformDocumentLinkTarget;
|
|
exports.transformMarkdown = transformMarkdown;
|
|
exports.transformCompletionItem = transformCompletionItem;
|
|
exports.transformCompletionList = transformCompletionList;
|
|
exports.transformDocumentSymbol = transformDocumentSymbol;
|
|
exports.transformFoldingRanges = transformFoldingRanges;
|
|
exports.transformHover = transformHover;
|
|
exports.transformLocation = transformLocation;
|
|
exports.transformLocations = transformLocations;
|
|
exports.transformSelectionRange = transformSelectionRange;
|
|
exports.transformSelectionRanges = transformSelectionRanges;
|
|
exports.transformTextEdit = transformTextEdit;
|
|
exports.transformWorkspaceSymbol = transformWorkspaceSymbol;
|
|
exports.transformWorkspaceEdit = transformWorkspaceEdit;
|
|
exports.pushEditToDocumentChanges = pushEditToDocumentChanges;
|
|
const language_core_1 = require("@volar/language-core");
|
|
const vscode_uri_1 = require("vscode-uri");
|
|
const featureWorkers_1 = require("./featureWorkers");
|
|
function transformDocumentLinkTarget(_target, context) {
|
|
let target = vscode_uri_1.URI.parse(_target);
|
|
const decoded = context.decodeEmbeddedDocumentUri(target);
|
|
if (!decoded) {
|
|
return target;
|
|
}
|
|
const embeddedRange = target.fragment.match(/^L(\d+)(,(\d+))?(-L(\d+)(,(\d+))?)?$/);
|
|
const sourceScript = context.language.scripts.get(decoded[0]);
|
|
const virtualCode = sourceScript?.generated?.embeddedCodes.get(decoded[1]);
|
|
target = decoded[0];
|
|
if (embeddedRange && 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)) {
|
|
if (!map.mappings.some(mapping => (0, language_core_1.isDocumentLinkEnabled)(mapping.data))) {
|
|
continue;
|
|
}
|
|
const sourceDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot);
|
|
const docs = [sourceDocument, embeddedDocument, map];
|
|
const startLine = Number(embeddedRange[1]) - 1;
|
|
const startCharacter = Number(embeddedRange[3] ?? 1) - 1;
|
|
if (embeddedRange[5] !== undefined) {
|
|
const endLine = Number(embeddedRange[5]) - 1;
|
|
const endCharacter = Number(embeddedRange[7] ?? 1) - 1;
|
|
const sourceRange = (0, featureWorkers_1.getSourceRange)(docs, {
|
|
start: { line: startLine, character: startCharacter },
|
|
end: { line: endLine, character: endCharacter },
|
|
});
|
|
if (sourceRange) {
|
|
target = target.with({
|
|
fragment: 'L' + (sourceRange.start.line + 1) + ',' + (sourceRange.start.character + 1)
|
|
+ '-L' + (sourceRange.end.line + 1) + ',' + (sourceRange.end.character + 1),
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
let mapped = false;
|
|
for (const sourcePos of (0, featureWorkers_1.getSourcePositions)(docs, { line: startLine, character: startCharacter })) {
|
|
mapped = true;
|
|
target = target.with({
|
|
fragment: 'L' + (sourcePos.line + 1) + ',' + (sourcePos.character + 1),
|
|
});
|
|
break;
|
|
}
|
|
if (mapped) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return target;
|
|
}
|
|
function transformMarkdown(content, context) {
|
|
return content.replace(/(?!\()volar-embedded-content:\/\/\w+\/[^)]+/g, match => {
|
|
const segments = match.split('|');
|
|
segments[0] = transformDocumentLinkTarget(segments[0], context).toString();
|
|
return segments.join('|');
|
|
});
|
|
}
|
|
function transformCompletionItem(item, getOtherRange, document, context) {
|
|
return {
|
|
...item,
|
|
additionalTextEdits: item.additionalTextEdits
|
|
?.map(edit => transformTextEdit(edit, getOtherRange, document))
|
|
.filter(edit => !!edit),
|
|
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
|
|
};
|
|
}
|
|
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)),
|
|
};
|
|
}
|
|
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(child => !!child),
|
|
};
|
|
}
|
|
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;
|
|
}
|
|
function transformHover(hover, getOtherRange) {
|
|
if (!hover?.range) {
|
|
return hover;
|
|
}
|
|
const range = getOtherRange(hover.range);
|
|
if (!range) {
|
|
return;
|
|
}
|
|
return {
|
|
...hover,
|
|
range,
|
|
};
|
|
}
|
|
function transformLocation(location, getOtherRange) {
|
|
const range = getOtherRange(location.range);
|
|
if (!range) {
|
|
return;
|
|
}
|
|
return {
|
|
...location,
|
|
range,
|
|
};
|
|
}
|
|
function transformLocations(locations, getOtherRange) {
|
|
return locations
|
|
.map(location => transformLocation(location, getOtherRange))
|
|
.filter(location => !!location);
|
|
}
|
|
function transformSelectionRange(location, getOtherRange) {
|
|
const range = getOtherRange(location.range);
|
|
if (!range) {
|
|
return;
|
|
}
|
|
const parent = location.parent ? transformSelectionRange(location.parent, getOtherRange) : undefined;
|
|
return {
|
|
range,
|
|
parent,
|
|
};
|
|
}
|
|
function transformSelectionRanges(locations, getOtherRange) {
|
|
return locations
|
|
.map(location => transformSelectionRange(location, getOtherRange))
|
|
.filter(location => !!location);
|
|
}
|
|
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,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* 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,
|
|
};
|
|
}
|
|
function transformWorkspaceEdit(edit, context, mode, versions = {}) {
|
|
const sourceResult = {};
|
|
let hasResult = false;
|
|
for (const tsUri in edit.changeAnnotations) {
|
|
sourceResult.changeAnnotations ??= {};
|
|
const tsAnno = edit.changeAnnotations[tsUri];
|
|
const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsUri));
|
|
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
|
|
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
|
|
if (sourceScript && virtualCode) {
|
|
for (const [sourceScript] of context.language.maps.forEach(virtualCode)) {
|
|
// TODO: check capability?
|
|
const uri = sourceScript.id.toString();
|
|
sourceResult.changeAnnotations[uri] = tsAnno;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
sourceResult.changeAnnotations[tsUri] = tsAnno;
|
|
}
|
|
}
|
|
for (const tsUri in edit.changes) {
|
|
sourceResult.changes ??= {};
|
|
const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsUri));
|
|
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 tsEdits = edit.changes[tsUri];
|
|
for (const tsEdit of tsEdits) {
|
|
if (mode === 'rename' || mode === 'fileName' || mode === 'codeAction') {
|
|
let _data;
|
|
const range = (0, featureWorkers_1.getSourceRange)(docs, tsEdit.range, data => {
|
|
_data = data;
|
|
return (0, language_core_1.isRenameEnabled)(data);
|
|
});
|
|
if (range) {
|
|
sourceResult.changes[sourceDocument.uri] ??= [];
|
|
sourceResult.changes[sourceDocument.uri].push({
|
|
newText: (0, language_core_1.resolveRenameEditText)(tsEdit.newText, _data),
|
|
range,
|
|
});
|
|
hasResult = true;
|
|
}
|
|
}
|
|
else {
|
|
const range = (0, featureWorkers_1.getSourceRange)(docs, tsEdit.range);
|
|
if (range) {
|
|
sourceResult.changes[sourceDocument.uri] ??= [];
|
|
sourceResult.changes[sourceDocument.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 decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsDocEdit.textDocument.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];
|
|
sourceEdit = {
|
|
textDocument: {
|
|
uri: sourceDocument.uri,
|
|
version: versions[sourceDocument.uri] ?? null,
|
|
},
|
|
edits: [],
|
|
};
|
|
for (const tsEdit of tsDocEdit.edits) {
|
|
if (mode === 'rename' || mode === 'fileName' || mode === 'codeAction') {
|
|
let _data;
|
|
const range = (0, featureWorkers_1.getSourceRange)(docs, 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 = (0, featureWorkers_1.getSourceRange)(docs, 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 decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsDocEdit.oldUri));
|
|
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
|
|
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
|
|
if (virtualCode) {
|
|
for (const [sourceScript] of context.language.maps.forEach(virtualCode)) {
|
|
// TODO: check capability?
|
|
sourceEdit = {
|
|
kind: 'rename',
|
|
oldUri: sourceScript.id.toString(),
|
|
newUri: tsDocEdit.newUri /* TODO: remove .ts? */,
|
|
options: tsDocEdit.options,
|
|
annotationId: tsDocEdit.annotationId,
|
|
};
|
|
}
|
|
}
|
|
else {
|
|
sourceEdit = tsDocEdit;
|
|
}
|
|
}
|
|
else if (tsDocEdit.kind === 'delete') {
|
|
const decoded = context.decodeEmbeddedDocumentUri(vscode_uri_1.URI.parse(tsDocEdit.uri));
|
|
const sourceScript = decoded && context.language.scripts.get(decoded[0]);
|
|
const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
|
|
if (virtualCode) {
|
|
for (const [sourceScript] of context.language.maps.forEach(virtualCode)) {
|
|
// TODO: check capability?
|
|
sourceEdit = {
|
|
kind: 'delete',
|
|
uri: sourceScript.id.toString(),
|
|
options: tsDocEdit.options,
|
|
annotationId: tsDocEdit.annotationId,
|
|
};
|
|
}
|
|
}
|
|
else {
|
|
sourceEdit = tsDocEdit;
|
|
}
|
|
}
|
|
if (sourceEdit) {
|
|
pushEditToDocumentChanges(sourceResult.documentChanges, sourceEdit);
|
|
hasResult = true;
|
|
}
|
|
}
|
|
}
|
|
if (hasResult) {
|
|
return sourceResult;
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
//# sourceMappingURL=transform.js.map
|