Skip to content

Commit 98f04e6

Browse files
authored
Merge pull request #19135 from amcasey/GH18626
Introduce and consume suppressLeadingAndTrailingTrivia
2 parents 9af21eb + 6bfad52 commit 98f04e6

12 files changed

+149
-19
lines changed

src/compiler/factory.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2612,6 +2612,16 @@ namespace ts {
26122612
return node;
26132613
}
26142614

2615+
/**
2616+
* Sets flags that control emit behavior of a node.
2617+
*/
2618+
/* @internal */
2619+
export function addEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
2620+
const emitNode = getOrCreateEmitNode(node);
2621+
emitNode.flags = emitNode.flags | emitFlags;
2622+
return node;
2623+
}
2624+
26152625
/**
26162626
* Gets a custom text range to use when emitting source maps.
26172627
*/

src/harness/unittests/extractConstants.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,14 @@ const f = () => {
223223
testExtractConstant("extractConstant_ArrowFunction_Expression",
224224
`const f = () => [#|2 + 1|];`);
225225

226+
testExtractConstant("extractConstant_PreserveTrivia", `
227+
// a
228+
var q = /*b*/ //c
229+
/*d*/ [#|1 /*e*/ //f
230+
/*g*/ + /*h*/ //i
231+
/*j*/ 2|] /*k*/ //l
232+
/*m*/; /*n*/ //o`);
233+
226234
testExtractConstantFailed("extractConstant_Void", `
227235
function f(): void { }
228236
[#|f();|]`);

src/harness/unittests/extractFunctions.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,14 @@ function f() {
532532
[#|let x;|]
533533
return { x };
534534
}`);
535+
536+
testExtractFunction("extractFunction_PreserveTrivia", `
537+
// a
538+
var q = /*b*/ //c
539+
/*d*/ [#|1 /*e*/ //f
540+
/*g*/ + /*h*/ //i
541+
/*j*/ 2|] /*k*/ //l
542+
/*m*/; /*n*/ //o`);
535543
});
536544

537545
function testExtractFunction(caption: string, text: string) {

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4519,9 +4519,9 @@ namespace ts.projectSystem {
45194519
fileName: "/a.ts",
45204520
textChanges: [
45214521
{
4522-
start: { line: 2, offset: 1 },
4523-
end: { line: 3, offset: 1 },
4524-
newText: " newFunction();\n",
4522+
start: { line: 2, offset: 3 },
4523+
end: { line: 2, offset: 5 },
4524+
newText: "newFunction();",
45254525
},
45264526
{
45274527
start: { line: 3, offset: 2 },

src/services/refactors/extractSymbol.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,8 @@ namespace ts.refactor.extractSymbol {
740740
}
741741

742742
const { body, returnValueProperty } = transformFunctionBody(node, exposedVariableDeclarations, writes, substitutions, !!(range.facts & RangeFacts.HasReturn));
743+
suppressLeadingAndTrailingTrivia(body);
744+
743745
let newFunction: MethodDeclaration | FunctionDeclaration;
744746

745747
if (isClassLike(scope)) {
@@ -926,15 +928,10 @@ namespace ts.refactor.extractSymbol {
926928
}
927929
}
928930

929-
if (isReadonlyArray(range.range)) {
930-
changeTracker.replaceNodesWithNodes(context.file, range.range, newNodes, {
931-
nodeSeparator: context.newLineCharacter,
932-
suffix: context.newLineCharacter // insert newline only when replacing statements
933-
});
934-
}
935-
else {
936-
changeTracker.replaceNodeWithNodes(context.file, range.range, newNodes, { nodeSeparator: context.newLineCharacter });
937-
}
931+
const replacementRange = isReadonlyArray(range.range)
932+
? { pos: first(range.range).getStart(), end: last(range.range).end }
933+
: { pos: range.range.getStart(), end: range.range.end };
934+
changeTracker.replaceRangeWithNodes(context.file, replacementRange, newNodes, { nodeSeparator: context.newLineCharacter });
938935

939936
const edits = changeTracker.getChanges();
940937
const renameRange = isReadonlyArray(range.range) ? first(range.range) : range.range;
@@ -982,6 +979,7 @@ namespace ts.refactor.extractSymbol {
982979
: checker.typeToTypeNode(checker.getContextualType(node), scope, NodeBuilderFlags.NoTruncation);
983980

984981
const initializer = transformConstantInitializer(node, substitutions);
982+
suppressLeadingAndTrailingTrivia(initializer);
985983

986984
const changeTracker = textChanges.ChangeTracker.fromContext(context);
987985

@@ -1014,7 +1012,7 @@ namespace ts.refactor.extractSymbol {
10141012
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, { suffix: context.newLineCharacter + context.newLineCharacter });
10151013

10161014
// Consume
1017-
changeTracker.replaceNodeWithNodes(context.file, node, [localReference], { nodeSeparator: context.newLineCharacter });
1015+
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
10181016
}
10191017
else {
10201018
const newVariableDeclaration = createVariableDeclaration(localNameText, variableType, initializer);

src/services/utilities.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,4 +1369,39 @@ namespace ts {
13691369

13701370
return visited;
13711371
}
1372+
1373+
/**
1374+
* Sets EmitFlags to suppress leading and trailing trivia on the node.
1375+
*/
1376+
/* @internal */
1377+
export function suppressLeadingAndTrailingTrivia(node: Node) {
1378+
Debug.assert(node !== undefined);
1379+
1380+
suppressLeading(node);
1381+
suppressTrailing(node);
1382+
1383+
function suppressLeading(node: Node) {
1384+
addEmitFlags(node, EmitFlags.NoLeadingComments);
1385+
1386+
const firstChild = forEachChild(node, child => child);
1387+
firstChild && suppressLeading(firstChild);
1388+
}
1389+
1390+
function suppressTrailing(node: Node) {
1391+
addEmitFlags(node, EmitFlags.NoTrailingComments);
1392+
1393+
let lastChild: Node = undefined;
1394+
forEachChild(
1395+
node,
1396+
child => (lastChild = child, undefined),
1397+
children => {
1398+
// As an optimization, jump straight to the end of the list.
1399+
if (children.length) {
1400+
lastChild = last(children);
1401+
}
1402+
return undefined;
1403+
});
1404+
lastChild && suppressTrailing(lastChild);
1405+
}
1406+
}
13721407
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// ==ORIGINAL==
2+
3+
// a
4+
var q = /*b*/ //c
5+
/*d*/ /*[#|*/1 /*e*/ //f
6+
/*g*/ + /*h*/ //i
7+
/*j*/ 2/*|]*/ /*k*/ //l
8+
/*m*/; /*n*/ //o
9+
// ==SCOPE::Extract to constant in enclosing scope==
10+
const newLocal = 1 /*e*/ //f
11+
/*g*/ + /*h*/ //i
12+
/*j*/ 2;
13+
14+
// a
15+
var q = /*b*/ //c
16+
/*d*/ /*RENAME*/newLocal /*k*/ //l
17+
/*m*/; /*n*/ //o
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// ==ORIGINAL==
2+
3+
// a
4+
var q = /*b*/ //c
5+
/*d*/ /*[#|*/1 /*e*/ //f
6+
/*g*/ + /*h*/ //i
7+
/*j*/ 2/*|]*/ /*k*/ //l
8+
/*m*/; /*n*/ //o
9+
// ==SCOPE::Extract to constant in enclosing scope==
10+
const newLocal = 1 /*e*/ //f
11+
/*g*/ + /*h*/ //i
12+
/*j*/ 2;
13+
14+
// a
15+
var q = /*b*/ //c
16+
/*d*/ /*RENAME*/newLocal /*k*/ //l
17+
/*m*/; /*n*/ //o

tests/baselines/reference/extractFunction/extractFunction13.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
2121
function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
2222
<U3a, U3b>(u3a: U3a, u3b: U3b) => {
23-
/*RENAME*/newFunction<U3a>(u3a);
23+
/*RENAME*/newFunction<U3a>(u3a);
2424
}
2525

2626
function newFunction<U3a>(u3a: U3a) {
@@ -40,7 +40,7 @@
4040
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
4141
function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
4242
<U3a, U3b>(u3a: U3a, u3b: U3b) => {
43-
/*RENAME*/newFunction<U2a, T2a, U3a>(t2a, u2a, u3a);
43+
/*RENAME*/newFunction<U2a, T2a, U3a>(t2a, u2a, u3a);
4444
}
4545
}
4646
}
@@ -60,7 +60,7 @@
6060
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
6161
function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
6262
<U3a, U3b>(u3a: U3a, u3b: U3b) => {
63-
/*RENAME*/newFunction<U1a, T1a, U2a, T2a, U3a>(t1a, t2a, u1a, u2a, u3a);
63+
/*RENAME*/newFunction<U1a, T1a, U2a, T2a, U3a>(t1a, t2a, u1a, u2a, u3a);
6464
}
6565
}
6666
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// ==ORIGINAL==
2+
3+
// a
4+
var q = /*b*/ //c
5+
/*d*/ /*[#|*/1 /*e*/ //f
6+
/*g*/ + /*h*/ //i
7+
/*j*/ 2/*|]*/ /*k*/ //l
8+
/*m*/; /*n*/ //o
9+
// ==SCOPE::Extract to function in global scope==
10+
11+
// a
12+
var q = /*b*/ //c
13+
/*d*/ /*RENAME*/newFunction() /*k*/ //l
14+
/*m*/; /*n*/ //o
15+
function newFunction() {
16+
return 1 /*e*/ //f
17+
/*g*/ + /*h*/ //i
18+
/*j*/ 2;
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// ==ORIGINAL==
2+
3+
// a
4+
var q = /*b*/ //c
5+
/*d*/ /*[#|*/1 /*e*/ //f
6+
/*g*/ + /*h*/ //i
7+
/*j*/ 2/*|]*/ /*k*/ //l
8+
/*m*/; /*n*/ //o
9+
// ==SCOPE::Extract to function in global scope==
10+
11+
// a
12+
var q = /*b*/ //c
13+
/*d*/ /*RENAME*/newFunction() /*k*/ //l
14+
/*m*/; /*n*/ //o
15+
function newFunction() {
16+
return 1 /*e*/ //f
17+
/*g*/ + /*h*/ //i
18+
/*j*/ 2;
19+
}

tests/cases/fourslash/extract-method-uniqueName.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ edit.applyRefactor({
1111
actionName: "function_scope_0",
1212
actionDescription: "Extract to function in global scope",
1313
newContent:
14-
`/*RENAME*/newFunction_1();
15-
14+
`// newFunction
15+
/*RENAME*/newFunction_1();
1616
function newFunction_1() {
17-
// newFunction
1817
1 + 1;
1918
}
2019
`

0 commit comments

Comments
 (0)