Skip to content

Commit 05b5f52

Browse files
fix(grainfmt): Handle comments within lists (#1429)
1 parent be4b38d commit 05b5f52

File tree

3 files changed

+169
-9
lines changed

3 files changed

+169
-9
lines changed

compiler/src/formatting/format.re

Lines changed: 114 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,41 +1066,146 @@ and resugar_list =
10661066

10671067
let last_item_was_spread = ref(false);
10681068

1069+
let list_length = List.length(processed_list);
1070+
10691071
let items =
1070-
List.map(
1071-
i =>
1072-
switch (i) {
1072+
List.mapi(
1073+
(index, item) =>
1074+
switch (item) {
10731075
| Regular(e) =>
10741076
last_item_was_spread := false;
10751077

1076-
Doc.group(print_expression(~original_source, ~comments, e));
1078+
// Do we have any comments on this line?
1079+
// If so, we break the whole list
1080+
1081+
// we might have a list list [1, 2 // comment
1082+
// 3]
1083+
// so need to use the comment after the last item
1084+
// [1,
1085+
// 2, //comment
1086+
// 3]
1087+
1088+
let end_line_comments =
1089+
if (index < list_length - 2) {
1090+
let next_item = List.nth(processed_list, index + 1);
1091+
Comment_utils.get_comments_between_locations(
1092+
~loc1=e.pexp_loc,
1093+
~loc2=
1094+
switch (next_item) {
1095+
| Regular(e)
1096+
| Spread(e) => e.pexp_loc
1097+
},
1098+
comments,
1099+
);
1100+
} else {
1101+
let (_, item_line, item_char, _) =
1102+
Locations.get_raw_pos_info(e.pexp_loc.loc_end);
1103+
Comment_utils.get_comments_on_line_end(
1104+
~line=item_line,
1105+
~char=item_char,
1106+
comments,
1107+
);
1108+
};
1109+
1110+
(
1111+
print_expression(
1112+
~original_source,
1113+
~comments=
1114+
Comment_utils.get_comments_inside_location(
1115+
~location=e.pexp_loc,
1116+
comments,
1117+
),
1118+
e,
1119+
),
1120+
end_line_comments,
1121+
);
1122+
10771123
| Spread(e) =>
10781124
last_item_was_spread := true;
1079-
Doc.group(
1125+
1126+
(
10801127
Doc.concat([
10811128
Doc.text("..."),
1082-
print_expression(~original_source, ~comments, e),
1129+
print_expression(
1130+
~original_source,
1131+
~comments=
1132+
Comment_utils.get_comments_inside_location(
1133+
~location=e.pexp_loc,
1134+
comments,
1135+
),
1136+
e,
1137+
),
10831138
]),
1139+
[],
10841140
);
10851141
},
10861142
processed_list,
10871143
);
10881144

1145+
// We have to compose this list by hand because of the complexity of if a list item
1146+
// is followed by a comment, the comma must come before the comment.
1147+
// It also impacts how we force a new line for a line ending comment at the end of a list
1148+
// without introducing an extra blank line when bringing the indentation back in again
1149+
1150+
let last_line_breaks_for_comments = ref(false);
1151+
let items_length = List.length(items);
1152+
let list_items =
1153+
List.mapi(
1154+
(i, (item, item_comments)) => {
1155+
let final_item = items_length - 1 == i;
1156+
1157+
let comment_doc =
1158+
switch (item_comments) {
1159+
| [] =>
1160+
last_line_breaks_for_comments := false;
1161+
if (final_item) {
1162+
Doc.nil;
1163+
} else {
1164+
Doc.concat([Doc.comma, Doc.line]);
1165+
};
1166+
| _ =>
1167+
let trailing_comments =
1168+
List.map(
1169+
(cmt: Parsetree.comment) =>
1170+
Doc.concat([
1171+
Doc.space,
1172+
Comment_utils.nobreak_comment_to_doc(cmt),
1173+
]),
1174+
item_comments,
1175+
);
1176+
1177+
last_line_breaks_for_comments := true;
1178+
Doc.concat([
1179+
Doc.comma,
1180+
Doc.concat(trailing_comments),
1181+
if (final_item) {Doc.nil} else {Doc.hardLine},
1182+
]);
1183+
};
1184+
1185+
Doc.concat([Doc.group(item), comment_doc]);
1186+
},
1187+
items,
1188+
);
1189+
10891190
Doc.group(
10901191
Doc.concat([
10911192
Doc.lbracket,
10921193
Doc.indent(
10931194
Doc.concat([
10941195
Doc.softLine,
1095-
Doc.join(~sep=Doc.concat([Doc.comma, Doc.line]), items),
1096-
if (last_item_was_spread^) {
1196+
Doc.concat(list_items),
1197+
if (last_item_was_spread^ || last_line_breaks_for_comments^) {
10971198
Doc.nil;
10981199
} else {
10991200
Doc.ifBreaks(Doc.comma, Doc.nil);
11001201
},
11011202
]),
11021203
),
1103-
Doc.softLine,
1204+
if (last_line_breaks_for_comments^) {
1205+
Doc.hardLine;
1206+
} else {
1207+
Doc.softLine;
1208+
},
11041209
Doc.rbracket,
11051210
]),
11061211
);

compiler/test/formatter_inputs/list_sugar.gr

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,31 @@ let list4 = [[1,2],[3,4],[5,6]]
3030
let list5 = [[1,2,3],[3,4,5],[5,6,7]]
3131
}
3232

33+
let test = [ 1, /* a */
34+
2, // test
35+
3,
36+
]
37+
38+
let test2 = [ 1, /* a */
39+
2, // test
40+
3, // final comment
41+
]
42+
43+
let test3 = [ 1, // comm
44+
45+
]
46+
47+
48+
49+
let recs = [
50+
{ num: 1, var: A, str: "" },
51+
52+
53+
{ num: 1, var: A, str: "foo" }, // A comment
54+
{ num: 1, var: A, str: "bar" },
55+
]
56+
57+
let t = [ 1, /*inline*/ 2, // comment
58+
3]
59+
3360
let cons = (a, b) => [a, ...b] // <- some long comment some long comment some long comment some long comment

compiler/test/formatter_outputs/list_sugar.gr

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,34 @@ let inaBlock = {
3434
let list5 = [[1, 2, 3], [3, 4, 5], [5, 6, 7]]
3535
}
3636

37+
let test = [
38+
1, /* a */
39+
2, // test
40+
3,
41+
]
42+
43+
let test2 = [
44+
1, /* a */
45+
2, // test
46+
3, // final comment
47+
]
48+
49+
let test3 = [
50+
1, // comm
51+
]
52+
53+
let recs = [
54+
{ num: 1, var: A, str: "" },
55+
{ num: 1, var: A, str: "foo" }, // A comment
56+
{ num: 1, var: A, str: "bar" },
57+
]
58+
59+
let t = [
60+
1, /*inline*/
61+
2, // comment
62+
3,
63+
]
64+
3765
let cons = (a, b) =>
3866
[
3967
a,

0 commit comments

Comments
 (0)