Skip to content

Commit b8a6d57

Browse files
authored
fix(graindoc): Preserve indentation in Doc comments during trim (#1119)
chore(stdlib): Regenerate markdown docs for Regex module
1 parent 0f681ea commit b8a6d57

File tree

4 files changed

+98
-24
lines changed

4 files changed

+98
-24
lines changed

compiler/src/parsing/parser_header.re

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ let make_doc_comment = (source, loc) => {
3030
let content =
3131
String_utils.slice(~first=3, ~last=-2, source)
3232
|> String_utils.deasterisk_each_line
33-
|> String_utils.trim_each_line;
33+
|> String_utils.trim_each_line(~style=String_utils.KeepIndent);
3434
Doc({cmt_content: content, cmt_source: source, cmt_loc: loc});
3535
};
3636

compiler/src/utils/string_utils.re

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,6 @@ let starts_with = (string, prefix) => {
1313
};
1414
};
1515

16-
let trim_each_line = str => {
17-
str
18-
|> Str.split(Str.regexp("\\(\r\n\\|\n\\)"))
19-
|> List.map(String.trim)
20-
|> String.concat("\n");
21-
};
22-
2316
let deasterisk_each_line = str => {
2417
Str.global_replace(Str.regexp("^[ \t]*\\*"), "", str);
2518
};
@@ -51,6 +44,56 @@ let slice = (~first=?, ~last=?, string) => {
5144
};
5245
};
5346

47+
type trim =
48+
| KeepIndent
49+
| FullTrim;
50+
51+
let get_common_indentation = lines => {
52+
let min_whitespace_length =
53+
List.fold_left(
54+
(min_whitespace_length, line) => {
55+
let non_empty_line =
56+
Str.string_match(Str.regexp("^\\([ \t]*\\)[^ \t]"), line, 0);
57+
if (non_empty_line) {
58+
let whitespace = Str.matched_group(1, line);
59+
let whitespace_length = String.length(whitespace);
60+
switch (min_whitespace_length) {
61+
| None => Some(whitespace_length)
62+
| Some(min_whitespace_length) =>
63+
Some(min(min_whitespace_length, whitespace_length))
64+
};
65+
} else {
66+
min_whitespace_length;
67+
};
68+
},
69+
None,
70+
lines,
71+
);
72+
73+
Option.value(~default=0, min_whitespace_length);
74+
};
75+
76+
let trim_each_line = (~style=FullTrim, str) => {
77+
let lines = str |> Str.split(Str.regexp("\\(\r\n\\|\n\\)"));
78+
79+
let min_whitespace_length =
80+
switch (style) {
81+
| KeepIndent => get_common_indentation(lines)
82+
| FullTrim => 0
83+
};
84+
85+
let trim_style = line => {
86+
switch (style) {
87+
| KeepIndent =>
88+
let line = slice(~first=min_whitespace_length, line);
89+
Str.global_replace(Str.regexp("[ \t]+$"), "", line);
90+
| FullTrim => String.trim(line)
91+
};
92+
};
93+
94+
lines |> List.map(trim_style) |> String.concat("\n");
95+
};
96+
5497
/** TODO(#436): Re-enable these when we can include in the Windows build
5598
let%expect_test "empty string" = {
5699
print_endline(slice(""));

compiler/test/suites/comments.re

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,28 @@ describe("comments", ({test}) => {
8686
},
8787
);
8888
assertParse(
89-
"comment_parse_doc_multiline_trim",
90-
"/** Test\n Weird indent\n Normal indent */\"foo\"",
89+
"comment_parse_doc_multiline_trim_all_same_indent",
90+
"/**\n Test\n Weird indent\n Normal indent */\"foo\"",
9191
{
9292
statements: [str("foo")],
9393
comments: [
9494
Parsetree.Doc({
9595
cmt_content: "Test\nWeird indent\nNormal indent",
96+
cmt_source: "/**\n Test\n Weird indent\n Normal indent */",
97+
cmt_loc: Location.dummy_loc,
98+
}),
99+
],
100+
prog_loc: Location.dummy_loc,
101+
},
102+
);
103+
assertParse(
104+
"comment_parse_doc_multiline_trim_keeps_differnt_indent",
105+
"/** Test\n Weird indent\n Normal indent */\"foo\"",
106+
{
107+
statements: [str("foo")],
108+
comments: [
109+
Parsetree.Doc({
110+
cmt_content: "Test\n Weird indent\n Normal indent",
96111
cmt_source: "/** Test\n Weird indent\n Normal indent */",
97112
cmt_loc: Location.dummy_loc,
98113
}),
@@ -101,14 +116,15 @@ describe("comments", ({test}) => {
101116
},
102117
);
103118
assertParse(
104-
"comment_parse_doc_multiline_trim2",
105-
"/** Test\r\n Weird indent\r\n Normal indent */\"foo\"",
119+
"comment_parse_doc_multiline_trim_normalizes_tabs",
120+
// Note: There are explicit tab characters in this string to test them
121+
"/**\n Test\r\n Weird indent\r\n Normal indent */\"foo\"",
106122
{
107123
statements: [str("foo")],
108124
comments: [
109125
Parsetree.Doc({
110126
cmt_content: "Test\nWeird indent\nNormal indent",
111-
cmt_source: "/** Test\r\n Weird indent\r\n Normal indent */",
127+
cmt_source: "/**\n Test\r\n Weird indent\r\n Normal indent */",
112128
cmt_loc: Location.dummy_loc,
113129
}),
114130
],
@@ -152,14 +168,29 @@ describe("comments", ({test}) => {
152168
statements: [str("foo")],
153169
comments: [
154170
Parsetree.Doc({
155-
cmt_content: "Test\nno space before\nspace before\ntab before\nno space after",
171+
cmt_content: " Test\n no space before\n space before\n tab before\nno space after",
156172
cmt_source: "/** Test\n* no space before\n * space before\n * tab before\n *no space after */",
157173
cmt_loc: Location.dummy_loc,
158174
}),
159175
],
160176
prog_loc: Location.dummy_loc,
161177
},
162178
);
179+
assertParse(
180+
"comment_parse_doc_deasterisk2",
181+
"/** Test\n* no space before\n * space before\n * tab before\n * trailing space after */\"foo\"",
182+
{
183+
statements: [str("foo")],
184+
comments: [
185+
Parsetree.Doc({
186+
cmt_content: "Test\nno space before\nspace before\ntab before\ntrailing space after",
187+
cmt_source: "/** Test\n* no space before\n * space before\n * tab before\n * trailing space after */",
188+
cmt_loc: Location.dummy_loc,
189+
}),
190+
],
191+
prog_loc: Location.dummy_loc,
192+
},
193+
);
163194
assertCompileError(
164195
"comment_line_numbers_1",
165196
"//comment\n//comment\n5 + 5L",

stdlib/regex.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,17 @@ The special character sequences are as follows:
6262
- `(«re»)` - Matches `«re»`, storing the result in a group
6363
- `(?:«re»)` - Matches `«re»` without storing the result in a group
6464
- `(?«mode»:«re») - Matches `«re»` with the mode settings specified by `«mode»` using the following syntax:
65-
- `«mode»i` - The same as `«mode»`, but with case-insensitivity enabled (temporarily not supported until grain-lang/grain#661 is resolved)
66-
- `«mode»-i` - The same as `«mode»`, but with case-insensitivity disabled (the default)
67-
- `«mode»m` / `«mode»-s` - The same as `«mode»`, but with multi-line mode enabled
68-
- `«mode»-m` / `«mode»s` - The same as `«mode»`, but with multi-line mode disabled
69-
- An empty string, which will not change any mode settings
65+
- `«mode»i` - The same as `«mode»`, but with case-insensitivity enabled (temporarily not supported until grain-lang/grain#661 is resolved)
66+
- `«mode»-i` - The same as `«mode»`, but with case-insensitivity disabled (the default)
67+
- `«mode»m` / `«mode»-s` - The same as `«mode»`, but with multi-line mode enabled
68+
- `«mode»-m` / `«mode»s` - The same as `«mode»`, but with multi-line mode disabled
69+
- An empty string, which will not change any mode settings
7070
- `(?«tst»«re1»|«re2»)` - Will match `«re1»` if `«tst»`, otherwise will match `«re2»`. The following options are available for `«tst»`
71-
- `(«n»)` - Will be true if group `«n»` has a match
72-
- `(?=«re»)` - Will be true if `«re»` matches the next sequence
73-
- `(?!«re»)` - Will be true if `«re»` does not match the next sequence
74-
- `(?<=«re»)` - Will be true if `«re»` matches the preceding sequence
75-
- `(?<!«re»)` - Will be true if `«re»` does not match the preceding sequence
71+
- `(«n»)` - Will be true if group `«n»` has a match
72+
- `(?=«re»)` - Will be true if `«re»` matches the next sequence
73+
- `(?!«re»)` - Will be true if `«re»` does not match the next sequence
74+
- `(?<=«re»)` - Will be true if `«re»` matches the preceding sequence
75+
- `(?<!«re»)` - Will be true if `«re»` does not match the preceding sequence
7676
- `(?«tst»«re»)` - Equivalent to `(?«tst»«re»|)`
7777
- Finally, basic classes (defined below) can also appear outside of character ranges.
7878

0 commit comments

Comments
 (0)