git.delta.rocks / jrsonnet / refs/commits / 443991edefaa

difftreelog

feat(fmt) preserve newline above last comment

Yaroslav Bolyukin2023-09-04parent: #dacee20.patch.diff
in: master

3 files changed

modifiedcmds/jrsonnet-fmt/src/children.rsdiffbeforeafterboth
1// TODO: Return errors as trivia1// TODO: Return errors as trivia
22
3use std::{fmt::Debug, marker::PhantomData, mem};3use std::{fmt::Debug, mem};
44
5use 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}
9093
91pub 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}
106109
107pub 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 end
109 count_newlines_before(tt) >= 2111 count_newlines_before(tt)
112 + prev_inline
113 .map(count_newlines_after)
114 .unwrap_or_default()
115
116 // First for previous item end, second for current item
117 >= 2
110}118}
111119
142 nl_count150 nl_count
143}151}
144152
145pub 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 newline
160 newlines_above: if had_some {169 should_start_with_newline: had_some
161 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 0
168 },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 }
213
214 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 };
208221
209 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 }
212225
213 (out, next)226 (out, ending_comments)
214}227}
215228
216#[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 output
218 newlines_above: usize,232 pub should_start_with_newline: bool,
219 /// Comment before item, i.e233 /// Comment before item, i.e
220 ///234 ///
221 /// ```ignore235 /// ```ignore
234 pub inline_trivia: ChildTrivia,248 pub inline_trivia: ChildTrivia,
235}249}
236250
237impl<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 output
239 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 >= 2
242 }
243}255}
244256
modifiedcmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth
13};13};
1414
15use crate::{15use crate::{
16 children::{should_start_with_newline, trivia_after, trivia_between},16 children::{trivia_after, trivia_between},
17 comments::{format_comments, CommentLocation},17 comments::{format_comments, CommentLocation},
18};18};
1919
294 l.r_brace_token().map(Into::into).as_ref(),294 l.r_brace_token().map(Into::into).as_ref(),
295 );295 );
296 for mem in children.into_iter() {296 for mem in children.into_iter() {
297 if mem.needs_newline_above() {297 if mem.should_start_with_newline {
298 p!(pi: nl);298 p!(pi: nl);
299 }299 }
300 p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));300 p!(pi: items(format_comments(&mem.before_trivia, CommentLocation::AboveItem)));
314 p!(pi: nl)314 p!(pi: nl)
315 }315 }
316316
317 // TODO: implement same thing as needs_newline_above, but for end comments
318 if should_start_with_newline(&end_comments) {317 if end_comments.should_start_with_newline {
319 p!(pi: nl);318 p!(pi: nl);
320 }319 }
321 p!(pi: items(format_comments(&end_comments, CommentLocation::EndOfItems)));320 p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));
322 p!(pi: <i str("}"));321 p!(pi: <i str("}"));
323 pi322 pi
324 }323 }
450 } else {449 } else {
451 p!(pi: str("local") >i nl);450 p!(pi: str("local") >i nl);
452 for bind in binds {451 for bind in binds {
453 if bind.needs_newline_above() {452 if bind.should_start_with_newline {
454 p!(pi: nl);453 p!(pi: nl);
455 }454 }
456 p!(pi: items(format_comments(&bind.before_trivia, CommentLocation::AboveItem)));455 p!(pi: items(format_comments(&bind.before_trivia, CommentLocation::AboveItem)));
457 p!(pi: {bind.value} str(";"));456 p!(pi: {bind.value} str(";"));
458 p!(pi: items(format_comments(&bind.inline_trivia, CommentLocation::ItemInline)) nl);457 p!(pi: items(format_comments(&bind.inline_trivia, CommentLocation::ItemInline)) nl);
459 }458 }
460 // TODO: needs_newline_above end_comments459 if end_comments.should_start_with_newline {
460 p!(pi: nl)
461 }
461 p!(pi: items(format_comments(&end_comments, CommentLocation::EndOfItems)));462 p!(pi: items(format_comments(&end_comments.trivia, CommentLocation::EndOfItems)));
462 p!(pi: <i);463 p!(pi: <i);
463 }464 }
464 p!(pi: str(";") nl);465 p!(pi: str(";") nl);
472 .as_ref(),473 .as_ref(),
473 );474 );
475
476 if expr_comments.should_start_with_newline {
477 p!(pi: nl);
478 }
474 p!(pi: items(format_comments(&expr_comments, CommentLocation::AboveItem)));479 p!(pi: items(format_comments(&expr_comments.trivia, CommentLocation::AboveItem)));
475
476 // TODO: needs_newline_above expr
477 p!(pi: {l.expr()});480 p!(pi: {l.expr()});
478 pi481 pi
479 }482 }
modifiedcmds/jrsonnet-fmt/src/tests.rsdiffbeforeafterboth
20macro_rules! assert_formatted {20macro_rules! assert_formatted {
21 ($input:literal, $output:literal) => {21 ($input:literal, $output:literal) => {
22 let formatted = reformat(indoc!($input));22 let formatted = reformat(indoc!($input));
23 let expected = indoc!($output);23 let mut expected = indoc!($output).to_owned();
24 expected.push('\n');
24 if formatted != expected {25 if formatted != expected {
25 panic!(26 panic!(
26 "bad formatting, expected\n```\n{formatted}\n```\nto be equal to\n```\n{expected}\n```",27 "bad formatting, expected\n```\n{formatted}\n```\nto be equal to\n```\n{expected}\n```",
49 );50 );
50}51}
5152
52// Fails
53#[test]53#[test]
54fn last_comment_respects_spacing_with_inline_comment_above() {54fn last_comment_respects_spacing_with_inline_comment_above() {
55 assert_formatted!(55 assert_formatted!(