Skip to content

Commit 2a4ab08

Browse files
committed
Refine extraction of expression statements
1) Replace range, rather than node, to leave trivia intact. 2) Only replace node in the innermost scope - otherwise insert as usual and delete the original statement.
1 parent b21d8a4 commit 2a4ab08

File tree

4 files changed

+65
-5
lines changed

4 files changed

+65
-5
lines changed

src/harness/unittests/extractConstants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ namespace ts {
3333
testExtractConstant("extractConstant_ExpressionStatementExpression",
3434
`[#|"hello"|];`);
3535

36+
testExtractConstant("extractConstant_ExpressionStatementInNestedScope", `
37+
let i = 0;
38+
function F() {
39+
[#|i++|];
40+
}
41+
`);
42+
3643
testExtractConstant("extractConstant_BlockScopes_NoDependencies",
3744
`for (let i = 0; i < 10; i++) {
3845
for (let j = 0; j < 10; j++) {

src/services/refactors/extractSymbol.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -911,12 +911,13 @@ namespace ts.refactor.extractSymbol {
911911
const localReference = createIdentifier(localNameText);
912912
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
913913
}
914-
else if (node.parent.kind === SyntaxKind.ExpressionStatement) {
915-
// If the parent is an expression statement, replace the statement with the declaration.
914+
else if (node.parent.kind === SyntaxKind.ExpressionStatement && scope === findAncestor(node, isScope)) {
915+
// If the parent is an expression statement and the target scope is the immediately enclosing one,
916+
// replace the statement with the declaration.
916917
const newVariableStatement = createVariableStatement(
917918
/*modifiers*/ undefined,
918919
createVariableDeclarationList([newVariableDeclaration], NodeFlags.Const));
919-
changeTracker.replaceNodeWithNodes(context.file, node.parent, [newVariableStatement], { nodeSeparator: context.newLineCharacter });
920+
changeTracker.replaceRange(context.file, { pos: node.parent.getStart(), end: node.parent.end }, newVariableStatement);
920921
}
921922
else {
922923
const newVariableStatement = createVariableStatement(
@@ -940,8 +941,14 @@ namespace ts.refactor.extractSymbol {
940941
}
941942

942943
// Consume
943-
const localReference = createIdentifier(localNameText);
944-
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
944+
if (node.parent.kind === SyntaxKind.ExpressionStatement) {
945+
// If the parent is an expression statement, delete it.
946+
changeTracker.deleteRange(context.file, { pos: node.parent.getStart(), end: node.parent.end });
947+
}
948+
else {
949+
const localReference = createIdentifier(localNameText);
950+
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
951+
}
945952
}
946953
}
947954

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// ==ORIGINAL==
2+
3+
let i = 0;
4+
function F() {
5+
i++;
6+
}
7+
8+
// ==SCOPE::Extract to constant in enclosing scope==
9+
10+
let i = 0;
11+
function F() {
12+
const /*RENAME*/newLocal = i++;
13+
}
14+
15+
// ==SCOPE::Extract to constant in global scope==
16+
17+
let i = 0;
18+
const /*RENAME*/newLocal = i++;
19+
20+
function F() {
21+
22+
}
23+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// ==ORIGINAL==
2+
3+
let i = 0;
4+
function F() {
5+
i++;
6+
}
7+
8+
// ==SCOPE::Extract to constant in enclosing scope==
9+
10+
let i = 0;
11+
function F() {
12+
const /*RENAME*/newLocal = i++;
13+
}
14+
15+
// ==SCOPE::Extract to constant in global scope==
16+
17+
let i = 0;
18+
const /*RENAME*/newLocal = i++;
19+
20+
function F() {
21+
22+
}
23+

0 commit comments

Comments
 (0)