Skip to content

Commit 13c3b75

Browse files
authored
Extractor: Fix multi-line ERB comment extraction in extract_ruby (#1297)
This pull request updates the ERB comment `<%# ... %>` extraction in the `extract_ruby` method. When `comments: true` is set in `herb_extract_ruby`, single-line ERB comments (`<%# comment %>`) correctly convert to ` # comment `. However, multi-line ERB comments produced invalid Ruby because only the first line got the `#` prefix. Subsequent lines were output as raw text. Additionally, when `comments: false` (default) was set, multi-line ERB comment newlines were collapsed into spaces. Resolves #102
1 parent 7ecc385 commit 13c3b75

File tree

3 files changed

+119
-10
lines changed

3 files changed

+119
-10
lines changed

rust/tests/extract_ruby_options_test.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@ fn test_extract_ruby_with_comments() {
2929
assert_eq!(result, " # comment \n code ;");
3030
}
3131

32+
#[test]
33+
fn test_extract_ruby_multiline_erb_comment_with_comments() {
34+
let source = "<%#\n end\n end\n%>\n";
35+
let options = ExtractRubyOptions {
36+
comments: true,
37+
..Default::default()
38+
};
39+
let result = extract_ruby_with_options(source, &options).unwrap();
40+
assert_eq!(result, "# \n# end\n# end\n# \n");
41+
}
42+
43+
#[test]
44+
fn test_extract_ruby_multiline_erb_comment_default() {
45+
let source = "<%#\n end\n end\n%>\n";
46+
let result = extract_ruby(source).unwrap();
47+
assert_eq!(result, " \n \n \n \n");
48+
}
49+
3250
#[test]
3351
fn test_extract_ruby_without_preserve_positions() {
3452
let source = "<% x = 1 %> <% y = 2 %>";

src/extract.c

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,23 @@ void herb_extract_ruby_to_buffer_with_options(
4545
is_comment_tag = false;
4646

4747
if (extract_options.preserve_positions) {
48-
hb_buffer_append_whitespace(output, 2);
49-
hb_buffer_append_char(output, '#');
48+
bool is_multiline = false;
49+
50+
if (i + 1 < hb_array_size(tokens)) {
51+
const token_T* next = hb_array_get(tokens, i + 1);
52+
53+
if (next->type == TOKEN_ERB_CONTENT && next->value != NULL && strchr(next->value, '\n') != NULL) {
54+
is_multiline = true;
55+
}
56+
}
57+
58+
if (is_multiline) {
59+
hb_buffer_append_char(output, '#');
60+
hb_buffer_append_whitespace(output, 2);
61+
} else {
62+
hb_buffer_append_whitespace(output, 2);
63+
hb_buffer_append_char(output, '#');
64+
}
5065
} else {
5166
if (need_newline) { hb_buffer_append_char(output, '\n'); }
5267
hb_buffer_append_char(output, '#');
@@ -96,12 +111,45 @@ void herb_extract_ruby_to_buffer_with_options(
96111

97112
if (is_inline_comment) {
98113
if (extract_options.preserve_positions) { hb_buffer_append_whitespace(output, range_length(token->range)); }
114+
} else if (is_erb_comment_tag && token->value != NULL) {
115+
const char* content = token->value;
116+
117+
while (*content != '\0') {
118+
if (*content == '\n') {
119+
hb_buffer_append_char(output, '\n');
120+
content++;
121+
122+
if (extract_options.preserve_positions && *content == ' ') { content++; }
123+
124+
hb_buffer_append_char(output, '#');
125+
} else {
126+
hb_buffer_append_char(output, *content);
127+
content++;
128+
}
129+
}
130+
131+
if (!extract_options.preserve_positions) { need_newline = true; }
99132
} else {
100133
hb_buffer_append(output, token->value);
134+
101135
if (!extract_options.preserve_positions) { need_newline = true; }
102136
}
103137
} else {
104-
if (extract_options.preserve_positions) { hb_buffer_append_whitespace(output, range_length(token->range)); }
138+
if (is_erb_comment_tag && extract_options.preserve_positions && token->value != NULL) {
139+
const char* content = token->value;
140+
141+
while (*content != '\0') {
142+
if (*content == '\n') {
143+
hb_buffer_append_char(output, '\n');
144+
} else {
145+
hb_buffer_append_char(output, ' ');
146+
}
147+
148+
content++;
149+
}
150+
} else if (extract_options.preserve_positions) {
151+
hb_buffer_append_whitespace(output, range_length(token->range));
152+
}
105153
}
106154

107155
break;

test/extractor/extract_ruby_test.rb

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,11 @@ class ExtractRubyTest < Minitest::Spec
6969
%>
7070
HTML
7171

72-
expected = " \n"
73-
74-
# TODO: it should also preserve the newlines in the ERB content
75-
# expected = "\n #\n end\n "
72+
expected = <<~EXPECTED
73+
\s\s\s
74+
\s\s\s\s\s
75+
\s\s
76+
EXPECTED
7677

7778
assert_equal expected, actual
7879
end
@@ -87,10 +88,52 @@ class ExtractRubyTest < Minitest::Spec
8788
%>
8889
HTML
8990

90-
expected = " \n"
91+
expected = <<~EXPECTED
92+
\s\s\s
93+
\s\s\s\s\s
94+
\s\s\s\s\s
95+
\s\s\s\s\s
96+
\s\s\s\s\s
97+
\s\s
98+
EXPECTED
99+
100+
assert_equal expected, actual
101+
end
102+
103+
test "multi-line erb comment with comments: true" do
104+
actual = Herb.extract_ruby(<<~HTML, comments: true)
105+
<%#
106+
end
107+
end
108+
end
109+
end
110+
%>
111+
HTML
112+
113+
expected = <<~EXPECTED
114+
#\s\s
115+
# end
116+
# end
117+
# end
118+
# end
119+
#\s\s
120+
EXPECTED
121+
122+
assert_equal expected, actual
123+
end
124+
125+
test "erb comment broken up over multiple lines with comments: true" do
126+
actual = Herb.extract_ruby(<<~HTML, comments: true)
127+
<%#
128+
end
129+
%>
130+
HTML
91131

92-
# TODO: it should also preserve the newlines in the ERB content
93-
# expected = " \n \n \n \n \n \n"
132+
expected = <<~EXPECTED
133+
#\s\s
134+
# end
135+
#\s\s
136+
EXPECTED
94137

95138
assert_equal expected, actual
96139
end

0 commit comments

Comments
 (0)