difftreelog
feat(fmt) preserve newline above last comment
in: master
3 files changed
cmds/jrsonnet-fmt/src/children.rsdiffbeforeafterboth1// TODO: Return errors as trivia1// TODO: Return errors as trivia223use std::{fmt::Debug, marker::PhantomData, mem};3use std::{fmt::Debug, mem};445use jrsonnet_rowan_parser::{5use jrsonnet_rowan_parser::{6 nodes::{Trivia, TriviaKind},6 nodes::{Trivia, TriviaKind},62 node: SyntaxNode,62 node: SyntaxNode,63 start: Option<&SyntaxElement>,63 start: Option<&SyntaxElement>,64 end: Option<&SyntaxElement>,64 end: Option<&SyntaxElement>,65) -> ChildTrivia {65) -> EndingComments {66 let mut iter = node.children_with_tokens().peekable();66 let mut iter = node.children_with_tokens().peekable();67 while iter.peek() != start {67 while iter.peek() != start {68 iter.next();68 iter.next();85 )85 )86 }86 }87 }87 }88 EndingComments {89 should_start_with_newline: should_start_with_newline(None, &out),88 out90 trivia: out,91 }89}92}909391pub fn children_between<T: AstNode + Debug>(94pub fn children_between<T: AstNode + Debug>(92 node: SyntaxNode,95 node: SyntaxNode,93 start: Option<&SyntaxElement>,96 start: Option<&SyntaxElement>,94 end: Option<&SyntaxElement>,97 end: Option<&SyntaxElement>,95) -> (Vec<Child<T>>, ChildTrivia) {98) -> (Vec<Child<T>>, EndingComments) {96 let mut iter = node.children_with_tokens().peekable();99 let mut iter = node.children_with_tokens().peekable();97 while iter.peek() != start {100 while iter.peek() != start {98 iter.next();101 iter.next();104 )107 )105}108}106109107pub fn should_start_with_newline(tt: &ChildTrivia) -> bool {110pub fn should_start_with_newline(prev_inline: Option<&ChildTrivia>, tt: &ChildTrivia) -> bool {108 // First for previous item end109 count_newlines_before(tt) >= 2111 count_newlines_before(tt)112 + prev_inline113 .map(count_newlines_after)114 .unwrap_or_default()115116 // First for previous item end, second for current item117 >= 2110}118}111119142 nl_count150 nl_count143}151}144152145pub fn children<'a, T: AstNode + Debug>(153pub fn children<T: AstNode + Debug>(146 items: impl Iterator<Item = SyntaxElement>,154 items: impl Iterator<Item = SyntaxElement>,147 loose: bool,155 loose: bool,148) -> (Vec<Child<T>>, ChildTrivia) {156) -> (Vec<Child<T>>, EndingComments) {149 let mut out = Vec::new();157 let mut out = Vec::new();150 let mut current_child = None::<Child<T>>;158 let mut current_child = None::<Child<T>>;151 let mut next = ChildTrivia::new();159 let mut next = ChildTrivia::new();157 if let Some(value) = item.as_node().cloned().and_then(T::cast) {165 if let Some(value) = item.as_node().cloned().and_then(T::cast) {158 let before_trivia = mem::take(&mut next);166 let before_trivia = mem::take(&mut next);159 let last_child = current_child.replace(Child {167 let last_child = current_child.replace(Child {168 // First item should not start with newline160 newlines_above: if had_some {169 should_start_with_newline: had_some161 count_newlines_before(&before_trivia)170 && should_start_with_newline(162 + current_child171 current_child.as_ref().map(|c| &c.inline_trivia),163 .as_ref()172 &before_trivia,164 .map(|c| count_newlines_after(&c.inline_trivia))165 .unwrap_or_default()166 } else {167 0168 },173 ),169 before_trivia,174 before_trivia,170 value,175 value,171 inline_trivia: Vec::new(),176 inline_trivia: Vec::new(),206 }211 }207 }212 }213214 let ending_comments = EndingComments {215 should_start_with_newline: should_start_with_newline(216 current_child.as_ref().map(|c| &c.inline_trivia),217 &next,218 ),219 trivia: next,220 };208221209 if let Some(current_child) = current_child {222 if let Some(current_child) = current_child {210 out.push(current_child);223 out.push(current_child);211 }224 }212225213 (out, next)226 (out, ending_comments)214}227}215228216#[derive(Debug)]229#[derive(Debug)]217pub struct Child<T> {230pub struct Child<T> {231 /// If this child has two newlines above in source code, so it needs to have it in the output218 newlines_above: usize,232 pub should_start_with_newline: bool,219 /// Comment before item, i.e233 /// Comment before item, i.e220 ///234 ///221 /// ```ignore235 /// ```ignore234 pub inline_trivia: ChildTrivia,248 pub inline_trivia: ChildTrivia,235}249}236250237impl<T> Child<T> {251pub struct EndingComments {238 /// If this child has two newlines above in source code, so it needs to have it in output252 /// If this child has two newlines above in source code, so it needs to have it in the output239 pub fn needs_newline_above(&self) -> bool {253 pub should_start_with_newline: bool,240 // First line for end of previous item254 pub trivia: ChildTrivia,241 self.newlines_above >= 2242 }243}255}244256cmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth--- 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 str("}"));
pi
}
@@ -450,15 +449,17 @@
} else {
p!(pi: str("local") >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: <i);
}
p!(pi: str(";") nl);
@@ -471,9 +472,11 @@
.map(Into::into)
.as_ref(),
);
- p!(pi: items(format_comments(&expr_comments, CommentLocation::AboveItem)));
- // TODO: needs_newline_above expr
+ if expr_comments.should_start_with_newline {
+ p!(pi: nl);
+ }
+ p!(pi: items(format_comments(&expr_comments.trivia, CommentLocation::AboveItem)));
p!(pi: {l.expr()});
pi
}
cmds/jrsonnet-fmt/src/tests.rsdiffbeforeafterboth--- a/cmds/jrsonnet-fmt/src/tests.rs
+++ b/cmds/jrsonnet-fmt/src/tests.rs
@@ -20,7 +20,8 @@
macro_rules! assert_formatted {
($input:literal, $output:literal) => {
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!(