@@ -821,6 +821,8 @@ namespace ts {
821
821
let detachedCommentsInfo : { nodePos : number , detachedCommentEndPos : number } [ ] | undefined ;
822
822
let hasWrittenComment = false ;
823
823
let commentsDisabled = ! ! printerOptions . removeComments ;
824
+ let lastNode : Node | undefined ;
825
+ let lastSubstitution : Node | undefined ;
824
826
const { enter : enterComment , exit : exitComment } = performance . createTimerIf ( extendedDiagnostics , "commentTime" , "beforeComment" , "afterComment" ) ;
825
827
826
828
reset ( ) ;
@@ -1043,8 +1045,7 @@ namespace ts {
1043
1045
setSourceFile ( sourceFile ) ;
1044
1046
}
1045
1047
1046
- const pipelinePhase = getPipelinePhase ( PipelinePhase . Notification , node ) ;
1047
- pipelinePhase ( hint , node ) ;
1048
+ pipelineEmit ( hint , node ) ;
1048
1049
}
1049
1050
1050
1051
function setSourceFile ( sourceFile : SourceFile | undefined ) {
@@ -1076,31 +1077,56 @@ namespace ts {
1076
1077
currentSourceFile = undefined ! ;
1077
1078
currentLineMap = undefined ! ;
1078
1079
detachedCommentsInfo = undefined ;
1080
+ lastNode = undefined ;
1081
+ lastSubstitution = undefined ;
1079
1082
setWriter ( /*output*/ undefined , /*_sourceMapGenerator*/ undefined ) ;
1080
1083
}
1081
1084
1082
1085
function getCurrentLineMap ( ) {
1083
1086
return currentLineMap || ( currentLineMap = getLineStarts ( currentSourceFile ! ) ) ;
1084
1087
}
1085
1088
1089
+ function emit ( node : Node ) : Node ;
1090
+ function emit ( node : Node | undefined ) : Node | undefined ;
1086
1091
function emit ( node : Node | undefined ) {
1087
1092
if ( node === undefined ) return ;
1093
+
1088
1094
const prevSourceFileTextKind = recordBundleFileInternalSectionStart ( node ) ;
1089
- const pipelinePhase = getPipelinePhase ( PipelinePhase . Notification , node ) ;
1090
- pipelinePhase ( EmitHint . Unspecified , node ) ;
1095
+ const substitute = pipelineEmit ( EmitHint . Unspecified , node ) ;
1091
1096
recordBundleFileInternalSectionEnd ( prevSourceFileTextKind ) ;
1097
+ return substitute ;
1092
1098
}
1093
1099
1094
- function emitIdentifierName ( node : Identifier | undefined ) {
1100
+ function emitIdentifierName ( node : Identifier ) : Node ;
1101
+ function emitIdentifierName ( node : Identifier | undefined ) : Node | undefined ;
1102
+ function emitIdentifierName ( node : Identifier | undefined ) : Node | undefined {
1095
1103
if ( node === undefined ) return ;
1096
- const pipelinePhase = getPipelinePhase ( PipelinePhase . Notification , node ) ;
1097
- pipelinePhase ( EmitHint . IdentifierName , node ) ;
1104
+ return pipelineEmit ( EmitHint . IdentifierName , node ) ;
1098
1105
}
1099
1106
1100
- function emitExpression ( node : Expression | undefined ) {
1107
+ function emitExpression ( node : Expression ) : Node ;
1108
+ function emitExpression ( node : Expression | undefined ) : Node | undefined ;
1109
+ function emitExpression ( node : Expression | undefined ) : Node | undefined {
1101
1110
if ( node === undefined ) return ;
1111
+ return pipelineEmit ( EmitHint . Expression , node ) ;
1112
+ }
1113
+
1114
+ function pipelineEmit ( emitHint : EmitHint , node : Node ) {
1115
+ const savedLastNode = lastNode ;
1116
+ const savedLastSubstitution = lastSubstitution ;
1117
+ lastNode = node ;
1118
+ lastSubstitution = undefined ;
1119
+
1102
1120
const pipelinePhase = getPipelinePhase ( PipelinePhase . Notification , node ) ;
1103
- pipelinePhase ( EmitHint . Expression , node ) ;
1121
+ pipelinePhase ( emitHint , node ) ;
1122
+
1123
+ Debug . assert ( lastNode === node ) ;
1124
+
1125
+ const substitute = lastSubstitution ;
1126
+ lastNode = savedLastNode ;
1127
+ lastSubstitution = savedLastSubstitution ;
1128
+
1129
+ return substitute || node ;
1104
1130
}
1105
1131
1106
1132
function getPipelinePhase ( phase : PipelinePhase , node : Node ) {
@@ -1142,11 +1168,14 @@ namespace ts {
1142
1168
}
1143
1169
1144
1170
function pipelineEmitWithNotification ( hint : EmitHint , node : Node ) {
1171
+ Debug . assert ( lastNode === node ) ;
1145
1172
const pipelinePhase = getNextPipelinePhase ( PipelinePhase . Notification , node ) ;
1146
1173
onEmitNode ( hint , node , pipelinePhase ) ;
1174
+ Debug . assert ( lastNode === node ) ;
1147
1175
}
1148
1176
1149
1177
function pipelineEmitWithHint ( hint : EmitHint , node : Node ) : void {
1178
+ Debug . assert ( lastNode === node || lastSubstitution === node ) ;
1150
1179
if ( hint === EmitHint . SourceFile ) return emitSourceFile ( cast ( node , isSourceFile ) ) ;
1151
1180
if ( hint === EmitHint . IdentifierName ) return emitIdentifier ( cast ( node , isIdentifier ) ) ;
1152
1181
if ( hint === EmitHint . MappedTypeParameter ) return emitMappedTypeParameter ( cast ( node , isTypeParameterDeclaration ) ) ;
@@ -1459,7 +1488,7 @@ namespace ts {
1459
1488
if ( isExpression ( node ) ) {
1460
1489
hint = EmitHint . Expression ;
1461
1490
if ( substituteNode !== noEmitSubstitution ) {
1462
- node = substituteNode ( hint , node ) ;
1491
+ lastSubstitution = node = substituteNode ( hint , node ) ;
1463
1492
}
1464
1493
}
1465
1494
else if ( isToken ( node ) ) {
@@ -1575,8 +1604,11 @@ namespace ts {
1575
1604
}
1576
1605
1577
1606
function pipelineEmitWithSubstitution ( hint : EmitHint , node : Node ) {
1607
+ Debug . assert ( lastNode === node || lastSubstitution === node ) ;
1578
1608
const pipelinePhase = getNextPipelinePhase ( PipelinePhase . Substitution , node ) ;
1579
- pipelinePhase ( hint , substituteNode ( hint , node ) ) ;
1609
+ lastSubstitution = substituteNode ( hint , node ) ;
1610
+ pipelinePhase ( hint , lastSubstitution ) ;
1611
+ Debug . assert ( lastNode === node || lastSubstitution === node ) ;
1580
1612
}
1581
1613
1582
1614
function getHelpersFromBundledSourceFiles ( bundle : Bundle ) : string [ ] | undefined {
@@ -2074,8 +2106,7 @@ namespace ts {
2074
2106
}
2075
2107
writePunctuation ( "[" ) ;
2076
2108
2077
- const pipelinePhase = getPipelinePhase ( PipelinePhase . Notification , node . typeParameter ) ;
2078
- pipelinePhase ( EmitHint . MappedTypeParameter , node . typeParameter ) ;
2109
+ pipelineEmit ( EmitHint . MappedTypeParameter , node . typeParameter ) ;
2079
2110
2080
2111
writePunctuation ( "]" ) ;
2081
2112
if ( node . questionToken ) {
@@ -2173,70 +2204,60 @@ namespace ts {
2173
2204
}
2174
2205
2175
2206
function emitPropertyAccessExpression ( node : PropertyAccessExpression ) {
2176
- let indentBeforeDot = false ;
2177
- let indentAfterDot = false ;
2178
- const dotRangeFirstCommentStart = skipTrivia (
2179
- currentSourceFile ! . text ,
2180
- node . expression . end ,
2181
- /*stopAfterLineBreak*/ false ,
2182
- /*stopAtComments*/ true
2183
- ) ;
2184
- const dotRangeStart = skipTrivia ( currentSourceFile ! . text , dotRangeFirstCommentStart ) ;
2185
- const dotRangeEnd = dotRangeStart + 1 ;
2186
- if ( ! ( getEmitFlags ( node ) & EmitFlags . NoIndentation ) ) {
2187
- const dotToken = createToken ( SyntaxKind . DotToken ) ;
2188
- dotToken . pos = node . expression . end ;
2189
- dotToken . end = dotRangeEnd ;
2190
- indentBeforeDot = needsIndentation ( node , node . expression , dotToken ) ;
2191
- indentAfterDot = needsIndentation ( node , dotToken , node . name ) ;
2192
- }
2207
+ const expression = cast ( emitExpression ( node . expression ) , isExpression ) ;
2208
+ const token = getDotOrQuestionDotToken ( node ) ;
2209
+ const indentBeforeDot = needsIndentation ( node , node . expression , token ) ;
2210
+ const indentAfterDot = needsIndentation ( node , token , node . name ) ;
2193
2211
2194
- emitExpression ( node . expression ) ;
2195
2212
increaseIndentIf ( indentBeforeDot , /*writeSpaceIfNotIndenting*/ false ) ;
2196
2213
2197
- const dotHasCommentTrivia = dotRangeFirstCommentStart !== dotRangeStart ;
2198
- const shouldEmitDotDot = ! indentBeforeDot && needsDotDotForPropertyAccess ( node . expression , dotHasCommentTrivia ) ;
2214
+ const shouldEmitDotDot =
2215
+ token . kind !== SyntaxKind . QuestionDotToken &&
2216
+ mayNeedDotDotForPropertyAccess ( expression ) &&
2217
+ ! writer . hasPrecedingComment ( ) &&
2218
+ ! writer . hasPrecedingWhitespace ( ) ;
2219
+
2199
2220
if ( shouldEmitDotDot ) {
2200
2221
writePunctuation ( "." ) ;
2201
2222
}
2202
- emitTokenWithComment ( SyntaxKind . DotToken , node . expression . end , writePunctuation , node ) ;
2203
2223
2224
+ emitTokenWithComment ( token . kind , node . expression . end , writePunctuation , node ) ;
2204
2225
increaseIndentIf ( indentAfterDot , /*writeSpaceIfNotIndenting*/ false ) ;
2205
2226
emit ( node . name ) ;
2206
2227
decreaseIndentIf ( indentBeforeDot , indentAfterDot ) ;
2207
2228
}
2208
2229
2209
2230
// 1..toString is a valid property access, emit a dot after the literal
2210
2231
// Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal
2211
- function needsDotDotForPropertyAccess ( expression : Expression , dotHasTrivia : boolean ) {
2232
+ function mayNeedDotDotForPropertyAccess ( expression : Expression ) {
2212
2233
expression = skipPartiallyEmittedExpressions ( expression ) ;
2213
2234
if ( isNumericLiteral ( expression ) ) {
2214
2235
// check if numeric literal is a decimal literal that was originally written with a dot
2215
2236
const text = getLiteralTextOfNode ( < LiteralExpression > expression , /*neverAsciiEscape*/ true ) ;
2216
2237
// If he number will be printed verbatim and it doesn't already contain a dot, add one
2217
2238
// if the expression doesn't have any comments that will be emitted.
2218
- return ! expression . numericLiteralFlags && ! stringContains ( text , tokenToString ( SyntaxKind . DotToken ) ! ) &&
2219
- ( ! dotHasTrivia || printerOptions . removeComments ) ;
2239
+ return ! expression . numericLiteralFlags && ! stringContains ( text , tokenToString ( SyntaxKind . DotToken ) ! ) ;
2220
2240
}
2221
2241
else if ( isPropertyAccessExpression ( expression ) || isElementAccessExpression ( expression ) ) {
2222
2242
// check if constant enum value is integer
2223
2243
const constantValue = getConstantValue ( expression ) ;
2224
2244
// isFinite handles cases when constantValue is undefined
2225
2245
return typeof constantValue === "number" && isFinite ( constantValue )
2226
- && Math . floor ( constantValue ) === constantValue
2227
- && printerOptions . removeComments ;
2246
+ && Math . floor ( constantValue ) === constantValue ;
2228
2247
}
2229
2248
}
2230
2249
2231
2250
function emitElementAccessExpression ( node : ElementAccessExpression ) {
2232
2251
emitExpression ( node . expression ) ;
2252
+ emit ( node . questionDotToken ) ;
2233
2253
emitTokenWithComment ( SyntaxKind . OpenBracketToken , node . expression . end , writePunctuation , node ) ;
2234
2254
emitExpression ( node . argumentExpression ) ;
2235
2255
emitTokenWithComment ( SyntaxKind . CloseBracketToken , node . argumentExpression . end , writePunctuation , node ) ;
2236
2256
}
2237
2257
2238
2258
function emitCallExpression ( node : CallExpression ) {
2239
2259
emitExpression ( node . expression ) ;
2260
+ emit ( node . questionDotToken ) ;
2240
2261
emitTypeArguments ( node , node . typeArguments ) ;
2241
2262
emitExpressionList ( node , node . arguments , ListFormat . CallExpressionArguments ) ;
2242
2263
}
@@ -3700,8 +3721,7 @@ namespace ts {
3700
3721
writeLine ( ) ;
3701
3722
increaseIndent ( ) ;
3702
3723
if ( isEmptyStatement ( node ) ) {
3703
- const pipelinePhase = getPipelinePhase ( PipelinePhase . Notification , node ) ;
3704
- pipelinePhase ( EmitHint . EmbeddedStatement , node ) ;
3724
+ pipelineEmit ( EmitHint . EmbeddedStatement , node ) ;
3705
3725
}
3706
3726
else {
3707
3727
emit ( node ) ;
@@ -4172,6 +4192,10 @@ namespace ts {
4172
4192
}
4173
4193
4174
4194
function needsIndentation ( parent : Node , node1 : Node , node2 : Node ) : boolean {
4195
+ if ( getEmitFlags ( parent ) & EmitFlags . NoIndentation ) {
4196
+ return false ;
4197
+ }
4198
+
4175
4199
parent = skipSynthesizedParentheses ( parent ) ;
4176
4200
node1 = skipSynthesizedParentheses ( node1 ) ;
4177
4201
node2 = skipSynthesizedParentheses ( node2 ) ;
@@ -4627,6 +4651,7 @@ namespace ts {
4627
4651
// Comments
4628
4652
4629
4653
function pipelineEmitWithComments ( hint : EmitHint , node : Node ) {
4654
+ Debug . assert ( lastNode === node || lastSubstitution === node ) ;
4630
4655
enterComment ( ) ;
4631
4656
hasWrittenComment = false ;
4632
4657
const emitFlags = getEmitFlags ( node ) ;
@@ -4693,6 +4718,7 @@ namespace ts {
4693
4718
}
4694
4719
}
4695
4720
exitComment ( ) ;
4721
+ Debug . assert ( lastNode === node || lastSubstitution === node ) ;
4696
4722
}
4697
4723
4698
4724
function emitLeadingSynthesizedComment ( comment : SynthesizedComment ) {
@@ -4939,6 +4965,7 @@ namespace ts {
4939
4965
}
4940
4966
4941
4967
function pipelineEmitWithSourceMap ( hint : EmitHint , node : Node ) {
4968
+ Debug . assert ( lastNode === node || lastSubstitution === node ) ;
4942
4969
const pipelinePhase = getNextPipelinePhase ( PipelinePhase . SourceMaps , node ) ;
4943
4970
if ( isUnparsedSource ( node ) || isUnparsedPrepend ( node ) ) {
4944
4971
pipelinePhase ( hint , node ) ;
@@ -4981,6 +5008,7 @@ namespace ts {
4981
5008
emitSourcePos ( source , end ) ;
4982
5009
}
4983
5010
}
5011
+ Debug . assert ( lastNode === node || lastSubstitution === node ) ;
4984
5012
}
4985
5013
4986
5014
/**
0 commit comments