mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-10 06:41:59 -06:00
Fixup line and offset of rename location of refactor (#19265)
* Fixup line and offset of rename location of refactor * Fixes * Handle "\r" only documents * Update api baselines * Fix error if an edit comes *after* the rename location * Add bounds check * Simpler implementation: get new text, then calculate line starts the usual way
This commit is contained in:
parent
8695f38d6f
commit
c1b4d59752
@ -351,7 +351,7 @@ namespace ts {
|
||||
/**
|
||||
* We assume the first line starts at position 0 and 'position' is non-negative.
|
||||
*/
|
||||
export function computeLineAndCharacterOfPosition(lineStarts: ReadonlyArray<number>, position: number) {
|
||||
export function computeLineAndCharacterOfPosition(lineStarts: ReadonlyArray<number>, position: number): LineAndCharacter {
|
||||
let lineNumber = binarySearch(lineStarts, position);
|
||||
if (lineNumber < 0) {
|
||||
// If the actual position was not found,
|
||||
|
||||
@ -3811,9 +3811,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface LineAndCharacter {
|
||||
/** 0-based. */
|
||||
line: number;
|
||||
/*
|
||||
* This value denotes the character position in line and is different from the 'column' because of tab characters.
|
||||
* 0-based. This value denotes the character position in line and is different from the 'column' because of tab characters.
|
||||
*/
|
||||
character: number;
|
||||
}
|
||||
|
||||
@ -661,4 +661,28 @@ namespace ts.server {
|
||||
session.consumeQueue();
|
||||
});
|
||||
});
|
||||
|
||||
describe("helpers", () => {
|
||||
it(getLocationInNewDocument.name, () => {
|
||||
const text = `// blank line\nconst x = 0;`;
|
||||
const renameLocationInOldText = text.indexOf("0");
|
||||
const fileName = "/a.ts";
|
||||
const edits: ts.FileTextChanges = {
|
||||
fileName,
|
||||
textChanges: [
|
||||
{
|
||||
span: { start: 0, length: 0 },
|
||||
newText: "const newLocal = 0;\n\n",
|
||||
},
|
||||
{
|
||||
span: { start: renameLocationInOldText, length: 1 },
|
||||
newText: "newLocal",
|
||||
},
|
||||
],
|
||||
};
|
||||
const renameLocationInNewText = renameLocationInOldText + edits.textChanges[0].newText.length;
|
||||
const res = getLocationInNewDocument(text, fileName, renameLocationInNewText, [edits]);
|
||||
assert.deepEqual(res, { line: 4, offset: 11 });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -1514,16 +1514,18 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
if (simplifiedResult) {
|
||||
const file = result.renameFilename;
|
||||
let location: protocol.Location | undefined;
|
||||
if (file !== undefined && result.renameLocation !== undefined) {
|
||||
const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(file));
|
||||
location = renameScriptInfo.positionToLineOffset(result.renameLocation);
|
||||
const { renameFilename, renameLocation, edits } = result;
|
||||
let mappedRenameLocation: protocol.Location | undefined;
|
||||
if (renameFilename !== undefined && renameLocation !== undefined) {
|
||||
const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(renameFilename));
|
||||
const snapshot = renameScriptInfo.getSnapshot();
|
||||
const oldText = snapshot.getText(0, snapshot.getLength());
|
||||
mappedRenameLocation = getLocationInNewDocument(oldText, renameFilename, renameLocation, edits);
|
||||
}
|
||||
return {
|
||||
renameLocation: location,
|
||||
renameFilename: file,
|
||||
edits: result.edits.map(change => this.mapTextChangesToCodeEdits(project, change))
|
||||
renameLocation: mappedRenameLocation,
|
||||
renameFilename,
|
||||
edits: edits.map(change => this.mapTextChangesToCodeEdits(project, change))
|
||||
};
|
||||
}
|
||||
else {
|
||||
@ -2040,4 +2042,26 @@ namespace ts.server {
|
||||
response?: {};
|
||||
responseRequired?: boolean;
|
||||
}
|
||||
|
||||
/* @internal */ // Exported only for tests
|
||||
export function getLocationInNewDocument(oldText: string, renameFilename: string, renameLocation: number, edits: ReadonlyArray<FileTextChanges>): protocol.Location {
|
||||
const newText = applyEdits(oldText, renameFilename, edits);
|
||||
const { line, character } = computeLineAndCharacterOfPosition(computeLineStarts(newText), renameLocation);
|
||||
return { line: line + 1, offset: character + 1 };
|
||||
}
|
||||
|
||||
function applyEdits(text: string, textFilename: string, edits: ReadonlyArray<FileTextChanges>): string {
|
||||
for (const { fileName, textChanges } of edits) {
|
||||
if (fileName !== textFilename) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let i = textChanges.length - 1; i >= 0; i--) {
|
||||
const { newText, span: { start, length } } = textChanges[i];
|
||||
text = text.slice(0, start) + newText + text.slice(start + length);
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2307,6 +2307,7 @@ declare namespace ts {
|
||||
LineFeed = 1,
|
||||
}
|
||||
interface LineAndCharacter {
|
||||
/** 0-based. */
|
||||
line: number;
|
||||
character: number;
|
||||
}
|
||||
|
||||
@ -2307,6 +2307,7 @@ declare namespace ts {
|
||||
LineFeed = 1,
|
||||
}
|
||||
interface LineAndCharacter {
|
||||
/** 0-based. */
|
||||
line: number;
|
||||
character: number;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user