Skip to content

Commit c1b4d59

Browse files
author
Andy
authored
Fixup line and offset of rename location of refactor (microsoft#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
1 parent 8695f38 commit c1b4d59

6 files changed

Lines changed: 61 additions & 10 deletions

File tree

src/compiler/scanner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ namespace ts {
351351
/**
352352
* We assume the first line starts at position 0 and 'position' is non-negative.
353353
*/
354-
export function computeLineAndCharacterOfPosition(lineStarts: ReadonlyArray<number>, position: number) {
354+
export function computeLineAndCharacterOfPosition(lineStarts: ReadonlyArray<number>, position: number): LineAndCharacter {
355355
let lineNumber = binarySearch(lineStarts, position);
356356
if (lineNumber < 0) {
357357
// If the actual position was not found,

src/compiler/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3811,9 +3811,10 @@ namespace ts {
38113811
}
38123812

38133813
export interface LineAndCharacter {
3814+
/** 0-based. */
38143815
line: number;
38153816
/*
3816-
* This value denotes the character position in line and is different from the 'column' because of tab characters.
3817+
* 0-based. This value denotes the character position in line and is different from the 'column' because of tab characters.
38173818
*/
38183819
character: number;
38193820
}

src/harness/unittests/session.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,4 +661,28 @@ namespace ts.server {
661661
session.consumeQueue();
662662
});
663663
});
664+
665+
describe("helpers", () => {
666+
it(getLocationInNewDocument.name, () => {
667+
const text = `// blank line\nconst x = 0;`;
668+
const renameLocationInOldText = text.indexOf("0");
669+
const fileName = "/a.ts";
670+
const edits: ts.FileTextChanges = {
671+
fileName,
672+
textChanges: [
673+
{
674+
span: { start: 0, length: 0 },
675+
newText: "const newLocal = 0;\n\n",
676+
},
677+
{
678+
span: { start: renameLocationInOldText, length: 1 },
679+
newText: "newLocal",
680+
},
681+
],
682+
};
683+
const renameLocationInNewText = renameLocationInOldText + edits.textChanges[0].newText.length;
684+
const res = getLocationInNewDocument(text, fileName, renameLocationInNewText, [edits]);
685+
assert.deepEqual(res, { line: 4, offset: 11 });
686+
});
687+
});
664688
}

src/server/session.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,16 +1514,18 @@ namespace ts.server {
15141514
}
15151515

15161516
if (simplifiedResult) {
1517-
const file = result.renameFilename;
1518-
let location: protocol.Location | undefined;
1519-
if (file !== undefined && result.renameLocation !== undefined) {
1520-
const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(file));
1521-
location = renameScriptInfo.positionToLineOffset(result.renameLocation);
1517+
const { renameFilename, renameLocation, edits } = result;
1518+
let mappedRenameLocation: protocol.Location | undefined;
1519+
if (renameFilename !== undefined && renameLocation !== undefined) {
1520+
const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(renameFilename));
1521+
const snapshot = renameScriptInfo.getSnapshot();
1522+
const oldText = snapshot.getText(0, snapshot.getLength());
1523+
mappedRenameLocation = getLocationInNewDocument(oldText, renameFilename, renameLocation, edits);
15221524
}
15231525
return {
1524-
renameLocation: location,
1525-
renameFilename: file,
1526-
edits: result.edits.map(change => this.mapTextChangesToCodeEdits(project, change))
1526+
renameLocation: mappedRenameLocation,
1527+
renameFilename,
1528+
edits: edits.map(change => this.mapTextChangesToCodeEdits(project, change))
15271529
};
15281530
}
15291531
else {
@@ -2040,4 +2042,26 @@ namespace ts.server {
20402042
response?: {};
20412043
responseRequired?: boolean;
20422044
}
2045+
2046+
/* @internal */ // Exported only for tests
2047+
export function getLocationInNewDocument(oldText: string, renameFilename: string, renameLocation: number, edits: ReadonlyArray<FileTextChanges>): protocol.Location {
2048+
const newText = applyEdits(oldText, renameFilename, edits);
2049+
const { line, character } = computeLineAndCharacterOfPosition(computeLineStarts(newText), renameLocation);
2050+
return { line: line + 1, offset: character + 1 };
2051+
}
2052+
2053+
function applyEdits(text: string, textFilename: string, edits: ReadonlyArray<FileTextChanges>): string {
2054+
for (const { fileName, textChanges } of edits) {
2055+
if (fileName !== textFilename) {
2056+
continue;
2057+
}
2058+
2059+
for (let i = textChanges.length - 1; i >= 0; i--) {
2060+
const { newText, span: { start, length } } = textChanges[i];
2061+
text = text.slice(0, start) + newText + text.slice(start + length);
2062+
}
2063+
}
2064+
2065+
return text;
2066+
}
20432067
}

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2307,6 +2307,7 @@ declare namespace ts {
23072307
LineFeed = 1,
23082308
}
23092309
interface LineAndCharacter {
2310+
/** 0-based. */
23102311
line: number;
23112312
character: number;
23122313
}

tests/baselines/reference/api/typescript.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2307,6 +2307,7 @@ declare namespace ts {
23072307
LineFeed = 1,
23082308
}
23092309
interface LineAndCharacter {
2310+
/** 0-based. */
23102311
line: number;
23112312
character: number;
23122313
}

0 commit comments

Comments
 (0)