@@ -1209,6 +1209,12 @@ defmodule Code do
1209
1209
* `:emit_warnings` (since v1.16.0) - when `false`, does not emit
1210
1210
tokenizing/parsing related warnings. Defaults to `true`.
1211
1211
1212
+ * `:include_comments` (since v1.19.0) - when `true`, includes comments
1213
+ in the quoted form. Defaults to `false`. If this option is set to
1214
+ `true`, the `:literal_encoder` option must be set to a function
1215
+ that ensures all literals are annotated, for example
1216
+ `&{:ok, {:__block__, &2, [&1]}}`.
1217
+
1212
1218
## `Macro.to_string/2`
1213
1219
1214
1220
The opposite of converting a string to its quoted form is
@@ -1248,21 +1254,87 @@ defmodule Code do
1248
1254
* atoms used to represent single-letter sigils like `:sigil_X`
1249
1255
(but multi-letter sigils like `:sigil_XYZ` are encoded).
1250
1256
1257
+ ## Comments
1258
+
1259
+ When `include_comments: true` is passed, comments are included in the
1260
+ quoted form.
1261
+
1262
+ There are three types of comments:
1263
+ - `:leading_comments`: Comments that are located before a node,
1264
+ or in the same line.
1265
+
1266
+ Examples:
1267
+
1268
+ # This is a leading comment
1269
+ foo # This one too
1270
+
1271
+ - `:trailing_comments`: Comments that are located after a node, and
1272
+ before the end of the parent enclosing the node(or the root document).
1273
+
1274
+ Examples:
1275
+
1276
+ foo
1277
+ # This is a trailing comment
1278
+ # This one too
1279
+
1280
+ - `:inner_comments`: Comments that are located inside an empty node.
1281
+
1282
+ Examples:
1283
+
1284
+ foo do
1285
+ # This is an inner comment
1286
+ end
1287
+
1288
+ [
1289
+ # This is an inner comment
1290
+ ]
1291
+
1292
+ %{
1293
+ # This is an inner comment
1294
+ }
1295
+
1296
+ A comment may be considered inner or trailing depending on wether the enclosing
1297
+ node is empty or not. For example, in the following code:
1298
+
1299
+ foo do
1300
+ # This is an inner comment
1301
+ end
1302
+
1303
+ The comment is considered inner because the `do` block is empty. However, in the
1304
+ following code:
1305
+
1306
+ foo do
1307
+ bar
1308
+ # This is a trailing comment
1309
+ end
1310
+
1311
+ The comment is considered trailing to `bar` because the `do` block is not empty.
1312
+
1313
+ In the case no nodes are present in the AST but there are comments, they are
1314
+ inserted into a placeholder `:__block__` node as `:inner_comments`.
1251
1315
"""
1252
1316
@ spec string_to_quoted ( List.Chars . t ( ) , keyword ) ::
1253
1317
{ :ok , Macro . t ( ) } | { :error , { location :: keyword , binary | { binary , binary } , binary } }
1254
1318
def string_to_quoted ( string , opts \\ [ ] ) when is_list ( opts ) do
1255
1319
file = Keyword . get ( opts , :file , "nofile" )
1256
1320
line = Keyword . get ( opts , :line , 1 )
1257
1321
column = Keyword . get ( opts , :column , 1 )
1258
- include_comments = Keyword . get ( opts , :include_comments , false )
1322
+ include_comments? = Keyword . get ( opts , :include_comments , false )
1259
1323
1260
1324
Process . put ( :code_formatter_comments , [ ] )
1261
- opts = [ preserve_comments: & preserve_comments / 5 ] ++ opts
1325
+ opts =
1326
+ if include_comments? do
1327
+ [
1328
+ preserve_comments: & preserve_comments / 5 ,
1329
+ token_metadata: true ,
1330
+ ] ++ opts
1331
+ else
1332
+ opts
1333
+ end
1262
1334
1263
1335
with { :ok , tokens } <- :elixir . string_to_tokens ( to_charlist ( string ) , line , column , file , opts ) ,
1264
1336
{ :ok , quoted } <- :elixir . tokens_to_quoted ( tokens , file , opts ) do
1265
- if include_comments do
1337
+ if include_comments? do
1266
1338
quoted = Code.Normalizer . normalize ( quoted )
1267
1339
quoted = Code.Comments . merge_comments ( quoted , Process . get ( :code_formatter_comments ) )
1268
1340
@@ -1292,26 +1364,23 @@ defmodule Code do
1292
1364
file = Keyword . get ( opts , :file , "nofile" )
1293
1365
line = Keyword . get ( opts , :line , 1 )
1294
1366
column = Keyword . get ( opts , :column , 1 )
1295
- include_comments = Keyword . get ( opts , :include_comments , false )
1367
+ include_comments? = Keyword . get ( opts , :include_comments , false )
1296
1368
1297
1369
Process . put ( :code_formatter_comments , [ ] )
1298
1370
1299
- opts =
1300
- if include_comments do
1301
- [ preserve_comments: & preserve_comments / 5 ,
1302
- literal_encoder: & { :ok , { :__block__ , & 2 , [ & 1 ] } } ,
1303
- token_metadata: true ,
1304
- unescape: false ,
1305
- columns: true ,
1306
- ] ++ opts
1371
+ opts =
1372
+ if include_comments? do
1373
+ [
1374
+ preserve_comments: & preserve_comments / 5 ,
1375
+ token_metadata: true ,
1376
+ ] ++ opts
1307
1377
else
1308
1378
opts
1309
1379
end
1310
1380
1311
1381
quoted = :elixir . string_to_quoted! ( to_charlist ( string ) , line , column , file , opts )
1312
1382
1313
- if include_comments do
1314
- # quoted = Code.Normalizer.normalize(quoted)
1383
+ if include_comments? do
1315
1384
Code.Comments . merge_comments ( quoted , Process . get ( :code_formatter_comments ) )
1316
1385
else
1317
1386
quoted
0 commit comments