From 81494ebb876c3c1f2996b944d106cae8e8ee7f89 Mon Sep 17 00:00:00 2001 From: Jt Whissel Date: Wed, 13 Aug 2025 12:17:49 -0400 Subject: [PATCH 1/3] Added padding line between statements --- deployment/schema.json | 15 +++++ src/configuration/builder.rs | 8 +++ src/configuration/resolve_config.rs | 2 + src/configuration/types.rs | 3 + src/generation/generate.rs | 60 +++++++++++++++++-- .../Padding_Line_Between_Statements_False.txt | 40 +++++++++++++ .../Padding_Line_Between_Statements_True.txt | 44 ++++++++++++++ 7 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 tests/specs/general/Padding_Line_Between_Statements_False.txt create mode 100644 tests/specs/general/Padding_Line_Between_Statements_True.txt diff --git a/deployment/schema.json b/deployment/schema.json index 50c4f059..883d26ff 100644 --- a/deployment/schema.json +++ b/deployment/schema.json @@ -231,6 +231,21 @@ "description": "Forces the operator to be on the next line." }] }, + "paddingLineBetweenStatements": { + "description": "If true, adds blank lines between statements. If false, maintains existing spacing.", + "type": "boolean", + "default": false, + "oneOf": [ + { + "const": true, + "description": "Adds blank lines between statements for better readability." + }, + { + "const": false, + "description": "Maintains existing spacing between statements." + } + ] + }, "preferHanging": { "description": "Set to prefer hanging indentation when exceeding the line width instead of making code split up on multiple lines.", "type": "boolean", diff --git a/src/configuration/builder.rs b/src/configuration/builder.rs index aeaaa4d3..605dfe78 100644 --- a/src/configuration/builder.rs +++ b/src/configuration/builder.rs @@ -87,6 +87,14 @@ impl ConfigurationBuilder { self.insert("useTabs", value.into()) } + /// Set the padding line between statements. + /// + /// When true, adds blank lines between statements. When false, maintains existing spacing. + /// Default: `false` + pub fn padding_line_between_statements(&mut self, value: bool) -> &mut Self { + self.insert("paddingLineBetweenStatements", value.into()) + } + /// The number of columns for an indent. /// /// Default: `4` diff --git a/src/configuration/resolve_config.rs b/src/configuration/resolve_config.rs index 486b2365..b764422f 100644 --- a/src/configuration/resolve_config.rs +++ b/src/configuration/resolve_config.rs @@ -185,6 +185,8 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration) while_statement_prefer_hanging: get_value(&mut config, "whileStatement.preferHanging", prefer_hanging, &mut diagnostics), /* member spacing */ enum_declaration_member_spacing: get_value(&mut config, "enumDeclaration.memberSpacing", MemberSpacing::Maintain, &mut diagnostics), + /* padding line between statements */ + padding_line_between_statements: get_value(&mut config, "paddingLineBetweenStatements", false, &mut diagnostics), /* next control flow position */ if_statement_next_control_flow_position: get_value(&mut config, "ifStatement.nextControlFlowPosition", next_control_flow_position, &mut diagnostics), try_statement_next_control_flow_position: get_value( diff --git a/src/configuration/types.rs b/src/configuration/types.rs index 1e67d7b7..1e1b46a0 100644 --- a/src/configuration/types.rs +++ b/src/configuration/types.rs @@ -454,6 +454,9 @@ pub struct Configuration { /* member spacing */ #[serde(rename = "enumDeclaration.memberSpacing")] pub enum_declaration_member_spacing: MemberSpacing, + /* padding line between statements */ + #[serde(rename = "paddingLineBetweenStatements")] + pub padding_line_between_statements: bool, /* next control flow position */ #[serde(rename = "ifStatement.nextControlFlowPosition")] pub if_statement_next_control_flow_position: NextControlFlowPosition, diff --git a/src/generation/generate.rs b/src/generation/generate.rs index f978a04b..1e098cf2 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -4382,7 +4382,7 @@ fn gen_program<'a, 'b>(node: ProgramInfo<'a, 'b>, context: &mut Context<'a>) -> } } - items.extend(gen_statements(node.range, node.statements, context)); + items.extend(gen_statements(node.range, node.statements, context, true)); items } @@ -4653,7 +4653,7 @@ fn accessibility_to_str(accessibility: Accessibility) -> &'static str { fn gen_block_stmt<'a>(node: &BlockStmt<'a>, context: &mut Context<'a>) -> PrintItems { gen_block( - |stmts, context| gen_statements(node.get_inner_range(context), stmts, context), + |stmts, context| gen_statements(node.get_inner_range(context), stmts, context, false), GenBlockOptions { range: Some(node.range()), children: node.stmts.iter().map(|x| x.into()).collect(), @@ -5360,6 +5360,7 @@ fn gen_switch_case<'a>(node: &SwitchCase<'a>, context: &mut Context<'a>) -> Prin SourceRange::new(colon_token.end(), node.end()), node.cons.iter().map(|node| node.into()).collect(), context, + false, ))); } } @@ -7237,7 +7238,7 @@ where items } -fn gen_statements<'a>(inner_range: SourceRange, stmts: Vec>, context: &mut Context<'a>) -> PrintItems { +fn gen_statements<'a>(inner_range: SourceRange, stmts: Vec>, context: &mut Context<'a>, _is_top_level: bool) -> PrintItems { let stmt_groups = get_stmt_groups(stmts, context); let mut items = PrintItems::new(); let mut last_node: Option = None; @@ -7264,13 +7265,62 @@ fn gen_statements<'a>(inner_range: SourceRange, stmts: Vec>, context: & Some(sorter) => Some(get_sorted_indexes(stmt_group.nodes.iter().map(|n| Some(*n)), sorter, context)), None => None, }; + + // Clone nodes to avoid borrow checker issues + let nodes_clone = stmt_group.nodes.clone(); + for (i, node) in stmt_group.nodes.into_iter().enumerate() { let is_empty_stmt = node.is::(); if !is_empty_stmt { let mut separator_items = PrintItems::new(); if let Some(last_node) = &last_node { separator_items.push_signal(Signal::NewLine); - if node_helpers::has_separating_blank_line(&last_node, &node, context.program) { + // Check if we should add padding lines between statements based on ESLint rule + let should_add_padding = context.config.padding_line_between_statements; + if should_add_padding { + // Check if we should add blank line based on ESLint padding-line-between-statements rule + let prev_node = &nodes_clone[i - 1]; + let current_node = &nodes_clone[i]; + + // Check if previous is a variable declaration + let is_prev_var = prev_node.is::(); + + // Check if current is a variable declaration + let is_current_var = current_node.is::(); + + // Check if previous is a control flow statement + let is_prev_control_flow = prev_node.is::() + || prev_node.is::() + || prev_node.is::() + || prev_node.is::() + || prev_node.is::() + || prev_node.is::() + || prev_node.is::() + || prev_node.is::(); + + // Check if previous is a function declaration + let is_prev_function = prev_node.is::(); + + // Check if previous is a class declaration + let is_prev_class = prev_node.is::(); + + // Check if previous is a block statement + let is_prev_block = prev_node.is::(); + + // Always add blank line when previous is one of the specified types + // EXCEPT when both are variable declarations (no blank line between consecutive vars) + let should_add_blank_line = if is_prev_var && is_current_var { + false // No blank line between consecutive variable declarations + } else if is_prev_var || is_prev_control_flow || is_prev_function || is_prev_class || is_prev_block { + true // Always add blank line after var/control-flow/function/class/block + } else { + false // No blank line for other cases + }; + + if should_add_blank_line { + separator_items.push_signal(Signal::NewLine); + } + } else if node_helpers::has_separating_blank_line(&last_node, &node, context.program) { separator_items.push_signal(Signal::NewLine); } generated_line_separators.insert(i, separator_items); @@ -8810,7 +8860,7 @@ fn gen_conditional_brace_body<'a>(opts: GenConditionalBraceBodyOptions<'a>, cont context, )); } else { - items.extend(gen_statements(inner_range, body_node.stmts.iter().map(|x| x.into()).collect(), context)); + items.extend(gen_statements(inner_range, body_node.stmts.iter().map(|x| x.into()).collect(), context, false)); } items })); diff --git a/tests/specs/general/Padding_Line_Between_Statements_False.txt b/tests/specs/general/Padding_Line_Between_Statements_False.txt new file mode 100644 index 00000000..9daa2b96 --- /dev/null +++ b/tests/specs/general/Padding_Line_Between_Statements_False.txt @@ -0,0 +1,40 @@ +~~ paddingLineBetweenStatements: false ~~ +== should maintain existing spacing between statements == +const a = 1; +const b = 2; + +function test() { + const too = 0; + if (a > 0) { + const foo = 0; + const bar = 1; + console.log(a); + } + if (b > 0) { + const foo = 0; + const bar = 1; + console.log(b); + } else { + console.log(too); + } +} + +[expect] +const a = 1; +const b = 2; + +function test() { + const too = 0; + if (a > 0) { + const foo = 0; + const bar = 1; + console.log(a); + } + if (b > 0) { + const foo = 0; + const bar = 1; + console.log(b); + } else { + console.log(too); + } +} diff --git a/tests/specs/general/Padding_Line_Between_Statements_True.txt b/tests/specs/general/Padding_Line_Between_Statements_True.txt new file mode 100644 index 00000000..ba4e9b69 --- /dev/null +++ b/tests/specs/general/Padding_Line_Between_Statements_True.txt @@ -0,0 +1,44 @@ +~~ paddingLineBetweenStatements: true ~~ +== should add blank lines between statements == +const a = 1; +const b = 2; + +function test() { + const too = 0; + if (a > 0) { + const foo = 0; + const bar = 1; + console.log(a); + } + if (b > 0) { + const foo = 0; + const bar = 1; + console.log(b); + } else { + console.log(too); + } +} + +[expect] +const a = 1; +const b = 2; + +function test() { + const too = 0; + + if (a > 0) { + const foo = 0; + const bar = 1; + + console.log(a); + } + + if (b > 0) { + const foo = 0; + const bar = 1; + + console.log(b); + } else { + console.log(too); + } +} From d7624be494eebccf4b42e5a0bb4e8344b7a828d4 Mon Sep 17 00:00:00 2001 From: Jt Whissel Date: Wed, 13 Aug 2025 13:40:02 -0400 Subject: [PATCH 2/3] Fixed bug not respecting new lines --- src/generation/generate.rs | 6 ++++-- .../Padding_Line_Between_Statements_True.txt | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 1e098cf2..d4bd8ced 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -7309,7 +7309,7 @@ fn gen_statements<'a>(inner_range: SourceRange, stmts: Vec>, context: & // Always add blank line when previous is one of the specified types // EXCEPT when both are variable declarations (no blank line between consecutive vars) - let should_add_blank_line = if is_prev_var && is_current_var { + let rule_wants_blank_line = if is_prev_var && is_current_var { false // No blank line between consecutive variable declarations } else if is_prev_var || is_prev_control_flow || is_prev_function || is_prev_class || is_prev_block { true // Always add blank line after var/control-flow/function/class/block @@ -7317,7 +7317,9 @@ fn gen_statements<'a>(inner_range: SourceRange, stmts: Vec>, context: & false // No blank line for other cases }; - if should_add_blank_line { + // Never remove existing blank lines—preserve what's in the source + let had_blank_line_in_source = node_helpers::has_separating_blank_line(&last_node, &node, context.program); + if rule_wants_blank_line || had_blank_line_in_source { separator_items.push_signal(Signal::NewLine); } } else if node_helpers::has_separating_blank_line(&last_node, &node, context.program) { diff --git a/tests/specs/general/Padding_Line_Between_Statements_True.txt b/tests/specs/general/Padding_Line_Between_Statements_True.txt index ba4e9b69..725bcbf5 100644 --- a/tests/specs/general/Padding_Line_Between_Statements_True.txt +++ b/tests/specs/general/Padding_Line_Between_Statements_True.txt @@ -17,6 +17,14 @@ function test() { } else { console.log(too); } + test2(); + test2(); + + console.log("hello world"); + // Get the header from the first row of the first file +} + +function test2() { } [expect] @@ -41,4 +49,13 @@ function test() { } else { console.log(too); } + + test2(); + test2(); + + console.log("hello world"); + // Get the header from the first row of the first file +} + +function test2() { } From 6d3799b63a07c40bfacddeddf376f408cadb7613 Mon Sep 17 00:00:00 2001 From: Jt Whissel Date: Thu, 14 Aug 2025 13:06:02 -0400 Subject: [PATCH 3/3] Fixed a panic --- src/generation/generate.rs | 91 ++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/src/generation/generate.rs b/src/generation/generate.rs index d4bd8ced..bf50b072 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -7278,49 +7278,56 @@ fn gen_statements<'a>(inner_range: SourceRange, stmts: Vec>, context: & // Check if we should add padding lines between statements based on ESLint rule let should_add_padding = context.config.padding_line_between_statements; if should_add_padding { - // Check if we should add blank line based on ESLint padding-line-between-statements rule - let prev_node = &nodes_clone[i - 1]; - let current_node = &nodes_clone[i]; - - // Check if previous is a variable declaration - let is_prev_var = prev_node.is::(); - - // Check if current is a variable declaration - let is_current_var = current_node.is::(); - - // Check if previous is a control flow statement - let is_prev_control_flow = prev_node.is::() - || prev_node.is::() - || prev_node.is::() - || prev_node.is::() - || prev_node.is::() - || prev_node.is::() - || prev_node.is::() - || prev_node.is::(); - - // Check if previous is a function declaration - let is_prev_function = prev_node.is::(); - - // Check if previous is a class declaration - let is_prev_class = prev_node.is::(); - - // Check if previous is a block statement - let is_prev_block = prev_node.is::(); - - // Always add blank line when previous is one of the specified types - // EXCEPT when both are variable declarations (no blank line between consecutive vars) - let rule_wants_blank_line = if is_prev_var && is_current_var { - false // No blank line between consecutive variable declarations - } else if is_prev_var || is_prev_control_flow || is_prev_function || is_prev_class || is_prev_block { - true // Always add blank line after var/control-flow/function/class/block - } else { - false // No blank line for other cases - }; + if i > 0 { + // Check if we should add blank line based on the previous statement type + let prev_node = &nodes_clone[i - 1]; + let current_node = &nodes_clone[i]; + + // Check if previous is a variable declaration + let is_prev_var = prev_node.is::(); + + // Check if current is a variable declaration + let is_current_var = current_node.is::(); + + // Check if previous is a control flow statement + let is_prev_control_flow = prev_node.is::() + || prev_node.is::() + || prev_node.is::() + || prev_node.is::() + || prev_node.is::() + || prev_node.is::() + || prev_node.is::() + || prev_node.is::(); + + // Check if previous is a function declaration + let is_prev_function = prev_node.is::(); + + // Check if previous is a class declaration + let is_prev_class = prev_node.is::(); + + // Check if previous is a block statement + let is_prev_block = prev_node.is::(); + + // Always add blank line when previous is one of the specified types + // EXCEPT when both are variable declarations (no blank line between consecutive vars) + let rule_wants_blank_line = if is_prev_var && is_current_var { + false // No blank line between consecutive variable declarations + } else if is_prev_var || is_prev_control_flow || is_prev_function || is_prev_class || is_prev_block { + true // Always add blank line after var/control-flow/function/class/block + } else { + false // No blank line for other cases + }; - // Never remove existing blank lines—preserve what's in the source - let had_blank_line_in_source = node_helpers::has_separating_blank_line(&last_node, &node, context.program); - if rule_wants_blank_line || had_blank_line_in_source { - separator_items.push_signal(Signal::NewLine); + // Never remove existing blank lines—preserve what's in the source + let had_blank_line_in_source = node_helpers::has_separating_blank_line(&last_node, &node, context.program); + if rule_wants_blank_line || had_blank_line_in_source { + separator_items.push_signal(Signal::NewLine); + } + } else { + // First statement in group—only preserve existing blank lines + if node_helpers::has_separating_blank_line(&last_node, &node, context.program) { + separator_items.push_signal(Signal::NewLine); + } } } else if node_helpers::has_separating_blank_line(&last_node, &node, context.program) { separator_items.push_signal(Signal::NewLine);