@@ -73,6 +73,8 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
73
73
const lastComment = trailingCommentRanges [ trailingCommentRanges . length - 1 ] ;
74
74
const trailingRange = { pos : trailingCommentRanges [ 0 ] . pos , end : lastComment . end } ;
75
75
76
+ // since suppressTrailingTrivia(maybeBinary) does not work, the trailing comment is removed manually
77
+ // otherwise it would have the trailing comment twice
76
78
return textChanges . ChangeTracker . with ( context , t => {
77
79
t . deleteRange ( file , trailingRange ) ;
78
80
t . replaceNode ( file , maybeBinary , templateLiteral ) ;
@@ -81,7 +83,6 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
81
83
else {
82
84
return textChanges . ChangeTracker . with ( context , t => t . replaceNode ( file , maybeBinary , templateLiteral ) ) ;
83
85
}
84
-
85
86
}
86
87
87
88
const templateSpanToExpressions = ( file : SourceFile ) => ( templateSpan : TemplateSpan ) : Expression [ ] => {
@@ -147,31 +148,36 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
147
148
return containsString && areOperatorsValid ;
148
149
}
149
150
150
- function treeToArray ( node : Node ) : { nodes : ReadonlyArray < Expression > , operators : Token < BinaryOperator > [ ] , containsString : boolean , areOperatorsValid : boolean } {
151
- if ( isBinaryExpression ( node ) ) {
152
- const { nodes : leftNodes , operators : leftOperator , containsString : leftHasString , areOperatorsValid : leftOperatorValid } = treeToArray ( node . left ) ;
153
- const { nodes : rightNodes , containsString : rightHasString , areOperatorsValid : rightOperatorValid } = treeToArray ( node . right ) ;
151
+ function treeToArray ( current : Node ) : { nodes : Expression [ ] , operators : Token < BinaryOperator > [ ] , containsString : boolean , areOperatorsValid : boolean } {
152
+ if ( isBinaryExpression ( current ) ) {
153
+ const { nodes, operators, containsString : leftHasString , areOperatorsValid : leftOperatorValid } = treeToArray ( current . left ) ;
154
154
155
- if ( ! leftHasString && ! rightHasString ) {
156
- return { nodes : [ node ] , operators : [ ] , containsString : false , areOperatorsValid : true } ;
155
+ if ( ! leftHasString && ! isStringLiteral ( current . right ) ) {
156
+ return { nodes : [ current ] , operators : [ ] , containsString : false , areOperatorsValid : true } ;
157
157
}
158
158
159
- const currentOperatorValid = node . operatorToken . kind === SyntaxKind . PlusToken ;
160
- const areOperatorsValid = leftOperatorValid && currentOperatorValid && rightOperatorValid ;
161
- leftOperator . push ( node . operatorToken ) ;
159
+ const currentOperatorValid = current . operatorToken . kind === SyntaxKind . PlusToken ;
160
+ const areOperatorsValid = leftOperatorValid && currentOperatorValid ;
161
+
162
+ nodes . push ( current . right ) ;
163
+ operators . push ( current . operatorToken ) ;
162
164
163
- return { nodes : leftNodes . concat ( rightNodes ) , operators : leftOperator , containsString : true , areOperatorsValid } ;
165
+ return { nodes, operators, containsString : true , areOperatorsValid } ;
164
166
}
165
167
166
- return { nodes : [ node as Expression ] , operators : [ ] , containsString : isStringLiteral ( node ) , areOperatorsValid : true } ;
168
+ return { nodes : [ current as Expression ] , operators : [ ] , containsString : isStringLiteral ( current ) , areOperatorsValid : true } ;
167
169
}
168
170
171
+ // to copy comments following the operator
172
+ // "foo" + /* comment */ "bar"
169
173
const copyTrailingOperatorComments = ( operators : Token < BinaryOperator > [ ] , file : SourceFile ) => ( index : number , targetNode : Node ) => {
170
174
if ( index < operators . length ) {
171
175
copyTrailingComments ( operators [ index ] , targetNode , file , SyntaxKind . MultiLineCommentTrivia , /* hasTrailingNewLine */ false ) ;
172
176
}
173
177
} ;
174
178
179
+ // to copy comments following the string
180
+ // "foo" /* comment */ + "bar" /* comment */ + "bar2"
175
181
const copyCommentFromMultiNode = ( nodes : ReadonlyArray < Expression > , file : SourceFile , copyOperatorComments : ( index : number , targetNode : Node ) => void ) =>
176
182
( indexes : number [ ] , targetNode : Node ) => {
177
183
while ( indexes . length > 0 ) {
@@ -198,8 +204,6 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
198
204
function nodesToTemplate ( { nodes, operators} : { nodes : ReadonlyArray < Expression > , operators : Token < BinaryOperator > [ ] } , file : SourceFile ) {
199
205
const copyOperatorComments = copyTrailingOperatorComments ( operators , file ) ;
200
206
const copyCommentFromStringLiterals = copyCommentFromMultiNode ( nodes , file , copyOperatorComments ) ;
201
-
202
- const templateSpans : TemplateSpan [ ] = [ ] ;
203
207
const [ begin , headText , headIndexes ] = concatConsecutiveString ( 0 , nodes ) ;
204
208
205
209
if ( begin === nodes . length ) {
@@ -208,36 +212,41 @@ namespace ts.refactor.convertStringOrTemplateLiteral {
208
212
return noSubstitutionTemplateLiteral ;
209
213
}
210
214
215
+ const templateSpans : TemplateSpan [ ] = [ ] ;
211
216
const templateHead = createTemplateHead ( headText ) ;
212
217
copyCommentFromStringLiterals ( headIndexes , templateHead ) ;
213
218
214
219
for ( let i = begin ; i < nodes . length ; i ++ ) {
215
- let currentNode = nodes [ i ] ;
216
-
217
- if ( isParenthesizedExpression ( currentNode ) ) {
218
- copyCommentsWhenParenthesized ( currentNode ) ;
219
- currentNode = currentNode . expression ;
220
- }
221
-
220
+ const currentNode = getExpressionFromParenthesesOrExpression ( nodes [ i ] ) ;
222
221
copyOperatorComments ( i , currentNode ) ;
222
+
223
223
const [ newIndex , subsequentText , stringIndexes ] = concatConsecutiveString ( i + 1 , nodes ) ;
224
224
i = newIndex - 1 ;
225
225
226
226
const templatePart = i === nodes . length - 1 ? createTemplateTail ( subsequentText ) : createTemplateMiddle ( subsequentText ) ;
227
227
copyCommentFromStringLiterals ( stringIndexes , templatePart ) ;
228
-
229
228
templateSpans . push ( createTemplateSpan ( currentNode , templatePart ) ) ;
230
229
}
231
230
232
231
return createTemplateExpression ( templateHead , templateSpans ) ;
233
232
}
234
233
234
+ // to copy comments following the opening & closing parentheses
235
+ // "foo" + ( /* comment */ 5 + 5 ) /* comment */ + "bar"
235
236
function copyCommentsWhenParenthesized ( node : ParenthesizedExpression ) {
236
237
const file = node . getSourceFile ( ) ;
237
238
copyTrailingComments ( node , node . expression , file , SyntaxKind . MultiLineCommentTrivia , /* hasTrailingNewLine */ false ) ;
238
239
copyTrailingAsLeadingComments ( node . expression , node . expression , file , SyntaxKind . MultiLineCommentTrivia , /* hasTrailingNewLine */ false ) ;
239
240
}
240
241
242
+ function getExpressionFromParenthesesOrExpression ( node : Expression ) {
243
+ if ( isParenthesizedExpression ( node ) ) {
244
+ copyCommentsWhenParenthesized ( node ) ;
245
+ node = node . expression ;
246
+ }
247
+ return node ;
248
+ }
249
+
241
250
const hexToUnicode = ( _match : string , grp : string ) => String . fromCharCode ( parseInt ( grp , 16 ) ) ;
242
251
const octalToUnicode = ( _match : string , grp : string ) => String . fromCharCode ( parseInt ( grp , 8 ) ) ;
243
252
0 commit comments