Skip to content

Commit c031d6b

Browse files
authored
[fix] Prevent additional blank lines in multiline sends (#868)
1 parent 6b748fc commit c031d6b

5 files changed

Lines changed: 60 additions & 12 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
foo
2+
.bar.
3+
4+
baz
5+
6+
7+
foo
8+
.bar.
9+
10+
# Some more info about baz
11+
baz
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
foo
2+
.bar
3+
.baz
4+
5+
foo
6+
.bar
7+
# Some more info about baz
8+
.baz

librubyfmt/src/file_comments.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ impl FileComments {
211211
&mut self,
212212
starting_line_number: LineNumber,
213213
line_number: LineNumber,
214+
suppress_leading_blank: bool,
214215
) -> Option<(CommentBlock, LineNumber)> {
215216
let lowest_line = self.other_comments.first().map(|(ln, _)| *ln)?;
216217
if lowest_line > line_number {
@@ -224,10 +225,12 @@ impl FileComments {
224225
let mut comment_block_with_spaces = Vec::new();
225226
let mut last_line = None;
226227

227-
if line_difference_requires_newline(
228-
self.other_comments.first().unwrap().0,
229-
starting_line_number,
230-
) {
228+
if !suppress_leading_blank
229+
&& line_difference_requires_newline(
230+
self.other_comments.first().unwrap().0,
231+
starting_line_number,
232+
)
233+
{
231234
comment_block_with_spaces.push(b"".into());
232235
}
233236

librubyfmt/src/format_prism.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2396,7 +2396,10 @@ fn format_call_body<'src>(
23962396
}
23972397
}
23982398

2399-
ps.at_offset(start_loc_for_call_node_in_chain(&element));
2399+
// Blank lines between leading-dot chain elements produce invalid Ruby.
2400+
ps.with_user_newlines_disabled(|ps| {
2401+
ps.at_offset(start_loc_for_call_node_in_chain(&element));
2402+
});
24002403
ps.shift_comments();
24012404

24022405
let skip_value =

librubyfmt/src/parser_state.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ pub struct ParserState<'src> {
7070
/// Whether we're currently rendering inside a squiggly heredoc's content.
7171
/// Used to mark nested non-squiggly heredocs so they don't get incorrect indentation.
7272
inside_squiggly_heredoc: bool,
73+
/// When true, strip blank lines that would otherwise be prepended to an extracted comment
74+
/// block. Used during call-chain element offset jumps, where a trailing-dot blank line in
75+
/// the source must not produce an invalid blank line before a leading-dot comment.
76+
suppress_blank_before_comment: bool,
7377
}
7478

7579
impl<'src> ParserState<'src> {
@@ -106,9 +110,11 @@ impl<'src> ParserState<'src> {
106110
// Update line number and clear out any comments we might have rendered in e.g. an embexpr
107111
//
108112
// (Ignore this comment extraction, we've already rendered them elsewhere)
109-
let _ = self
110-
.comments_hash
111-
.extract_comments_to_line(self.current_orig_line_number, end_line);
113+
let _ = self.comments_hash.extract_comments_to_line(
114+
self.current_orig_line_number,
115+
end_line,
116+
false,
117+
);
112118
self.current_orig_line_number = end_line;
113119

114120
let segments = next_ps.render_to_segments();
@@ -329,10 +335,11 @@ impl<'src> ParserState<'src> {
329335
be.push_line_number(line_number);
330336
}
331337

332-
if let Some((comments, last_comment_line)) = self
333-
.comments_hash
334-
.extract_comments_to_line(self.current_orig_line_number, line_number)
335-
{
338+
if let Some((comments, last_comment_line)) = self.comments_hash.extract_comments_to_line(
339+
self.current_orig_line_number,
340+
line_number,
341+
self.suppress_blank_before_comment,
342+
) {
336343
self.push_comments(comments);
337344
self.current_orig_line_number =
338345
std::cmp::max(self.current_orig_line_number, last_comment_line);
@@ -718,6 +725,7 @@ impl<'src> ParserState<'src> {
718725
spaces_after_last_newline: 0,
719726
scopes: vec![vec![]],
720727
inside_squiggly_heredoc: false,
728+
suppress_blank_before_comment: false,
721729
}
722730
}
723731

@@ -775,6 +783,21 @@ impl<'src> ParserState<'src> {
775783
self.insert_user_newlines = false;
776784
}
777785

786+
pub(crate) fn with_user_newlines_disabled<F>(&mut self, f: F)
787+
where
788+
F: FnOnce(&mut ParserState<'src>),
789+
{
790+
let saved_newlines = self.insert_user_newlines;
791+
let saved_blank = self.suppress_blank_before_comment;
792+
self.insert_user_newlines = false;
793+
self.suppress_blank_before_comment = true;
794+
795+
f(self);
796+
797+
self.insert_user_newlines = saved_newlines;
798+
self.suppress_blank_before_comment = saved_blank;
799+
}
800+
778801
pub(crate) fn last_token_is_a_newline(&self) -> bool {
779802
match self.breakable_entry_stack.last() {
780803
Some(be) => be.last_token_is_a_newline(),

0 commit comments

Comments
 (0)