From 68a205b4a0b3204d20656ae7d46d5edcf52e7574 Mon Sep 17 00:00:00 2001 From: Todor Andonov Date: Fri, 29 May 2026 08:41:57 +0300 Subject: [PATCH 1/2] fix: preserve blank line before trailing comments at end of object/array (#34) Comments at the end of an object or array (before the closing token) are emitted via the close-token leading-comments path, which called gen_comments_as_statements with no "last node". The blank-line gap between the previous member and the comment was therefore never computed, so an intentional blank line before a trailing comment block was dropped. Emit one extra newline before those comments when the source had a blank line between the preceding token and the first comment. The surrounding structure already emits the single separating newline, so only the blank line itself is added (and multiple blank lines still collapse to one). --- src/generation/generate.rs | 16 ++++- .../Comments_BlankLineBeforeTrailing.txt | 64 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/specs/comments/Comments_BlankLineBeforeTrailing.txt diff --git a/src/generation/generate.rs b/src/generation/generate.rs index b3d2997..b4fb945 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -402,7 +402,21 @@ fn gen_surrounded_by_tokens<'a, 'b>( &Range::from_byte_index(open_token_end), context, ))); - if let Some(leading_comments) = context.comments.get(&close_token_start) { + if context.comments.contains_key(&close_token_start) { + // the separating newline is emitted elsewhere, so only add a newline for a blank line in the source + let first_comment_start = context + .comments + .get(&close_token_start) + .and_then(|c| c.first().map(|c| c.start())); + if let Some(start) = first_comment_start { + let comment_start_line = context.text_info.line_index(start); + if let Some(prev_token) = context.token_finder.get_previous_token(&Range::from_byte_index(start)) + && comment_start_line > context.text_info.line_index(prev_token.end()) + 1 + { + items.push_signal(Signal::NewLine); + } + } + let leading_comments = context.comments.get(&close_token_start).unwrap(); items.extend(ir_helpers::with_indent(gen_comments_as_statements( leading_comments.iter(), None, diff --git a/tests/specs/comments/Comments_BlankLineBeforeTrailing.txt b/tests/specs/comments/Comments_BlankLineBeforeTrailing.txt new file mode 100644 index 0000000..6e13b53 --- /dev/null +++ b/tests/specs/comments/Comments_BlankLineBeforeTrailing.txt @@ -0,0 +1,64 @@ +-- /file.jsonc -- +== should preserve a blank line before trailing comments at the end of an object (#34) == +{ + // This is a setting. + "foo": "", + + // These settings are temporarily commented out. + // "baz": "", + // "qux": "" +} + +[expect] +{ + // This is a setting. + "foo": "", + + // These settings are temporarily commented out. + // "baz": "", + // "qux": "" +} + +== should preserve a blank line before trailing comments at the end of an array == +[ + "foo", + + // trailing comment + // another +] + +[expect] +[ + "foo", + + // trailing comment + // another +] + +== should not add a blank line before trailing comments when the source had none == +{ + "foo": "" + // trailing comment +} + +[expect] +{ + "foo": "", + // trailing comment +} + +== should collapse multiple blank lines before trailing comments to one == +{ + "foo": "" + + + + // trailing comment +} + +[expect] +{ + "foo": "", + + // trailing comment +} From 38bec18096b52afa362d47648588b14c6b6528a4 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 17 Jun 2026 12:49:56 -0400 Subject: [PATCH 2/2] refactor: simplify close-token leading comment lookup --- src/generation/generate.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/generation/generate.rs b/src/generation/generate.rs index b4fb945..31f0a61 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -402,13 +402,9 @@ fn gen_surrounded_by_tokens<'a, 'b>( &Range::from_byte_index(open_token_end), context, ))); - if context.comments.contains_key(&close_token_start) { + if let Some(leading_comments) = context.comments.get(&close_token_start) { // the separating newline is emitted elsewhere, so only add a newline for a blank line in the source - let first_comment_start = context - .comments - .get(&close_token_start) - .and_then(|c| c.first().map(|c| c.start())); - if let Some(start) = first_comment_start { + if let Some(start) = leading_comments.first().map(|c| c.start()) { let comment_start_line = context.text_info.line_index(start); if let Some(prev_token) = context.token_finder.get_previous_token(&Range::from_byte_index(start)) && comment_start_line > context.text_info.line_index(prev_token.end()) + 1 @@ -416,7 +412,6 @@ fn gen_surrounded_by_tokens<'a, 'b>( items.push_signal(Signal::NewLine); } } - let leading_comments = context.comments.get(&close_token_start).unwrap(); items.extend(ir_helpers::with_indent(gen_comments_as_statements( leading_comments.iter(), None,