431 lines
18 KiB
Plaintext
431 lines
18 KiB
Plaintext
|
"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
|