--- a/cmds/jrsonnet-fmt/src/children.rs +++ b/cmds/jrsonnet-fmt/src/children.rs @@ -1,6 +1,6 @@ // TODO: Return errors as trivia -use std::{fmt::Debug, marker::PhantomData, mem}; +use std::{fmt::Debug, mem}; use jrsonnet_rowan_parser::{ nodes::{Trivia, TriviaKind}, @@ -62,7 +62,7 @@ node: SyntaxNode, start: Option<&SyntaxElement>, end: Option<&SyntaxElement>, -) -> ChildTrivia { +) -> EndingComments { let mut iter = node.children_with_tokens().peekable(); while iter.peek() != start { iter.next(); @@ -85,14 +85,17 @@ ) } } - out + EndingComments { + should_start_with_newline: should_start_with_newline(None, &out), + trivia: out, + } } pub fn children_between( node: SyntaxNode, start: Option<&SyntaxElement>, end: Option<&SyntaxElement>, -) -> (Vec>, ChildTrivia) { +) -> (Vec>, EndingComments) { let mut iter = node.children_with_tokens().peekable(); while iter.peek() != start { iter.next(); @@ -104,9 +107,14 @@ ) } -pub fn should_start_with_newline(tt: &ChildTrivia) -> bool { - // First for previous item end - count_newlines_before(tt) >= 2 +pub fn should_start_with_newline(prev_inline: Option<&ChildTrivia>, tt: &ChildTrivia) -> bool { + count_newlines_before(tt) + + prev_inline + .map(count_newlines_after) + .unwrap_or_default() + + // First for previous item end, second for current item + >= 2 } fn count_newlines_before(tt: &ChildTrivia) -> usize { @@ -142,10 +150,10 @@ nl_count } -pub fn children<'a, T: AstNode + Debug>( +pub fn children( items: impl Iterator, loose: bool, -) -> (Vec>, ChildTrivia) { +) -> (Vec>, EndingComments) { let mut out = Vec::new(); let mut current_child = None::>; let mut next = ChildTrivia::new(); @@ -157,15 +165,12 @@ if let Some(value) = item.as_node().cloned().and_then(T::cast) { let before_trivia = mem::take(&mut next); let last_child = current_child.replace(Child { - newlines_above: if had_some { - count_newlines_before(&before_trivia) - + current_child - .as_ref() - .map(|c| count_newlines_after(&c.inline_trivia)) - .unwrap_or_default() - } else { - 0 - }, + // First item should not start with newline + should_start_with_newline: had_some + && should_start_with_newline( + current_child.as_ref().map(|c| &c.inline_trivia), + &before_trivia, + ), before_trivia, value, inline_trivia: Vec::new(), @@ -206,16 +211,25 @@ } } + let ending_comments = EndingComments { + should_start_with_newline: should_start_with_newline( + current_child.as_ref().map(|c| &c.inline_trivia), + &next, + ), + trivia: next, + }; + if let Some(current_child) = current_child { out.push(current_child); } - (out, next) + (out, ending_comments) } #[derive(Debug)] pub struct Child { - newlines_above: usize, + /// If this child has two newlines above in source code, so it needs to have it in the output + pub should_start_with_newline: bool, /// Comment before item, i.e /// /// ```ignore @@ -234,10 +248,8 @@ pub inline_trivia: ChildTrivia, } -impl Child { - /// If this child has two newlines above in source code, so it needs to have it in output - pub fn needs_newline_above(&self) -> bool { - // First line for end of previous item - self.newlines_above >= 2 - } +pub struct EndingComments { + /// If this child has two newlines above in source code, so it needs to have it in the output + pub should_start_with_newline: bool, + pub trivia: ChildTrivia, } --- a/cmds/jrsonnet-fmt/src/main.rs +++ b/cmds/jrsonnet-fmt/src/main.rs @@ -13,7 +13,7 @@ }; use crate::{ - children::{should_start_with_newline, trivia_after, trivia_between}, + children::{trivia_after, trivia_between}, comments::{format_comments, CommentLocation}, }; @@ -294,7 +294,7 @@ l.r_brace_token().map(Into::into).as_ref(), ); for mem in children.into_iter() { - if mem.needs_newline_above() { + if mem.should_start_with_newline { p!(pi: nl); } p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem))); @@ -314,11 +314,10 @@ p!(pi: nl) } - // TODO: implement same thing as needs_newline_above, but for end comments - if should_start_with_newline(&end_comments) { + if end_comments.should_start_with_newline { p!(pi: nl); } - p!(pi: items(format_comments(&end_comments, CommentLocation::EndOfItems))); + p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems))); p!(pi: i nl); for bind in binds { - if bind.needs_newline_above() { + if bind.should_start_with_newline { p!(pi: nl); } p!(pi: items(format_comments(&bind.before_trivia, CommentLocation::AboveItem))); p!(pi: {bind.value} str(";")); p!(pi: items(format_comments(&bind.inline_trivia, CommentLocation::ItemInline)) nl); } - // TODO: needs_newline_above end_comments - p!(pi: items(format_comments(&end_comments, CommentLocation::EndOfItems))); + if end_comments.should_start_with_newline { + p!(pi: nl) + } + p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems))); p!(pi: { let formatted = reformat(indoc!($input)); - let expected = indoc!($output); + let mut expected = indoc!($output).to_owned(); + expected.push('\n'); if formatted != expected { panic!( "bad formatting, expected\n```\n{formatted}\n```\nto be equal to\n```\n{expected}\n```", @@ -49,7 +50,6 @@ ); } -// Fails #[test] fn last_comment_respects_spacing_with_inline_comment_above() { assert_formatted!(