diff --git a/deployment/schema.json b/deployment/schema.json index 50c4f059..a2b32661 100644 --- a/deployment/schema.json +++ b/deployment/schema.json @@ -199,6 +199,9 @@ }, { "const": "nextLine", "description": "Forces the next control flow to be on the next line." + }, { + "const": "nextLineExceptAfterBrace", + "description": "Forces the next control flow to be on the next line, except when preceded by a brace." }] }, "trailingCommas": { diff --git a/src/configuration/types.rs b/src/configuration/types.rs index 1e67d7b7..970376b2 100644 --- a/src/configuration/types.rs +++ b/src/configuration/types.rs @@ -119,9 +119,17 @@ pub enum NextControlFlowPosition { SameLine, /// Forces the next control flow to be on the next line. NextLine, + /// Forces the next control flow to be on the next line except when it appears after a closing brace. + NextLineExceptAfterBrace, } -generate_str_to_from![NextControlFlowPosition, [Maintain, "maintain"], [SameLine, "sameLine"], [NextLine, "nextLine"]]; +generate_str_to_from![ + NextControlFlowPosition, + [Maintain, "maintain"], + [SameLine, "sameLine"], + [NextLine, "nextLine"], + [NextLineExceptAfterBrace, "nextLineExceptAfterBrace"] +]; /// Where to place the operator for expressions that span multiple lines. #[derive(Debug, Clone, PartialEq, Copy, Serialize, Deserialize)] diff --git a/src/generation/generate.rs b/src/generation/generate.rs index d5090dc9..de34a282 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -4721,12 +4721,19 @@ fn gen_do_while_stmt<'a>(node: &DoWhileStmt<'a>, context: &mut Context<'a>) -> P )); items.extend(gen_node(node.body.into(), context)); if context.config.semi_colons.is_true() || matches!(node.body, Stmt::Block(_)) { + let body_has_braces_ref = if matches!(node.body, Stmt::Block(_)) + && context.config.do_while_statement_next_control_flow_position == NextControlFlowPosition::NextLineExceptAfterBrace + { + Some(push_always_true_condition(&mut items, "doWhileBodyHasBraces")) + } else { + None + }; items.extend(gen_control_flow_separator( context.config.do_while_statement_next_control_flow_position, &node.body.range(), "while", None, - None, + body_has_braces_ref, context, )); } else { @@ -5430,6 +5437,12 @@ fn gen_try_stmt<'a>(node: &TryStmt<'a>, context: &mut Context<'a>) -> PrintItems let mut last_block_range = node.block.range(); let mut last_block_start_ln = LineNumber::new("tryStart"); + let has_braces_ref = if next_control_flow_position == NextControlFlowPosition::NextLineExceptAfterBrace { + Some(push_always_true_condition(&mut items, "tryStatementHasBraces")) + } else { + None + }; + items.push_info(last_block_start_ln); items.push_sc(sc!("try")); @@ -5457,7 +5470,7 @@ fn gen_try_stmt<'a>(node: &TryStmt<'a>, context: &mut Context<'a>) -> PrintItems &last_block_range, "catch", Some(last_block_start_ln), - None, + has_braces_ref, context, )); last_block_range = handler.range(); @@ -5473,7 +5486,7 @@ fn gen_try_stmt<'a>(node: &TryStmt<'a>, context: &mut Context<'a>) -> PrintItems &last_block_range, "finally", Some(last_block_start_ln), - None, + has_braces_ref, context, )); items.push_sc(sc!("finally")); @@ -8507,12 +8520,76 @@ fn gen_control_flow_separator( previous_close_brace_condition_ref: Option, context: &mut Context, ) -> PrintItems { + fn should_force_newline_for_keyword(keyword_token: Option<&TokenAndSpan>, previous_node_block: &SourceRange, context: &mut Context) -> bool { + let Some(token_ref) = keyword_token else { + return false; + }; + let range = token_ref.range(); + if !get_leading_comments_on_previous_lines(&range, context).is_empty() { + return true; + } + let mut remaining = SourceRange::new(previous_node_block.end, range.start).text_fast(context.program).trim_start(); + while let Some(after_block_start) = remaining.strip_prefix("/*") { + if let Some(end_index) = after_block_start.find("*/") { + remaining = after_block_start[end_index + 2..].trim_start(); + } else { + return true; + } + } + if !remaining.is_empty() { + return true; + } + + for comment in previous_node_block.trailing_comments_fast(context.program) { + if comment.start() >= range.start { + break; + } + if comment.start_line_fast(context.program) > previous_node_block.end_line_fast(context.program) { + return true; + } + } + + false + } + let mut items = PrintItems::new(); match next_control_flow_position { NextControlFlowPosition::SameLine => { + // Check if previous block is an empty block statement by checking if it's on one line + // and contains only braces with whitespace + let is_previous_empty_block_stmt = { + let text = previous_node_block.text_fast(context.program); + let is_single_line = previous_node_block.start_line_fast(context.program) == previous_node_block.end_line_fast(context.program); + + is_single_line + && text + .rfind('{') + .and_then(|open_pos| { + text.rfind('}').and_then(|close_pos| { + if open_pos < close_pos { + let between = &text[open_pos + 1..close_pos]; + // Empty if only whitespace between braces + Some(between.trim().is_empty()) + } else { + Some(false) + } + }) + }) + .unwrap_or(false) + }; + let keyword_token = context.token_finder.get_first_keyword_after(previous_node_block, token_text); + let force_newline_due_to_comment = should_force_newline_for_keyword(keyword_token, previous_node_block, context); items.push_condition(if_true_or( "newLineOrSpace", Rc::new(move |condition_context| { + if force_newline_due_to_comment { + return Some(true); + } + // Don't force newline if previous block is empty and compact (like try {} catch {}) + if is_previous_empty_block_stmt { + return Some(false); + } + // newline if on the same line as the previous if let Some(previous_start_ln) = previous_start_ln { if condition_helpers::is_on_same_line(condition_context, previous_start_ln)? { @@ -8534,11 +8611,55 @@ fn gen_control_flow_separator( )); } NextControlFlowPosition::NextLine => items.push_signal(Signal::NewLine), + NextControlFlowPosition::NextLineExceptAfterBrace => { + // Check if there's a trailing comment on the same line as the closing brace + let keyword_token = context.token_finder.get_first_keyword_after(previous_node_block, token_text); + let keyword_start = keyword_token.map(|token| token.start()).unwrap_or(previous_node_block.end()); + let has_trailing_comment = previous_node_block + .trailing_comments_fast(context.program) + .take_while(|c| c.start() < keyword_start) + .any(|c| c.start_line_fast(context.program) == previous_node_block.end_line_fast(context.program)); + let force_newline_due_to_comment = should_force_newline_for_keyword(keyword_token, previous_node_block, context); + + if has_trailing_comment || force_newline_due_to_comment { + // If there's a trailing comment, always use newline before the next keyword + items.push_signal(Signal::NewLine); + } else { + // Use space if previous had braces, otherwise newline + items.push_condition(if_true_or( + "nextLineExceptAfterBrace", + Rc::new(move |condition_context| { + // use space if previous had a close brace + if let Some(previous_close_brace_condition_ref) = previous_close_brace_condition_ref { + if condition_context.resolved_condition(&previous_close_brace_condition_ref)? { + return Some(false); + } + } + // otherwise force new line + Some(true) + }), + Signal::NewLine.into(), + " ".into(), + )); + } + } NextControlFlowPosition::Maintain => { - let token = context.token_finder.get_first_keyword_after(previous_node_block, token_text); + let keyword_token = context.token_finder.get_first_keyword_after(previous_node_block, token_text); + let force_newline_due_to_comment = should_force_newline_for_keyword(keyword_token, previous_node_block, context); + let previous_end_line = previous_node_block.end_line_fast(context.program); + let token_past_previous_line = keyword_token + .map(|token_ref| { + let range = token_ref.range(); + node_helpers::is_first_node_on_line(&range, context.program) || range.start_line_fast(context.program) > previous_end_line + }) + .unwrap_or(false); - if token.is_some() && node_helpers::is_first_node_on_line(&token.unwrap().range(), context.program) { + if force_newline_due_to_comment || token_past_previous_line { items.push_signal(Signal::NewLine); + } else if let Some(else_separator) = + handle_else_after_brace_removal(keyword_token, token_text, previous_node_block, previous_close_brace_condition_ref, context) + { + items.extend(else_separator); } else { items.push_space(); } @@ -8547,6 +8668,50 @@ fn gen_control_flow_separator( items } +fn handle_else_after_brace_removal( + token: Option<&TokenAndSpan>, + token_text: &str, + previous_node_block: &SourceRange, + previous_close_brace_condition_ref: Option, + context: &mut Context, +) -> Option { + if token_text != "else" { + return None; + } + let token_range = token?.range(); + + // Check that only whitespace exists between the closing brace and "else" + // If there are comments or other code, don't apply special formatting + let full_text = SourceRange::new(previous_node_block.start, token_range.start).text_fast(context.program); + if !full_text[full_text.rfind('}')? + 1..].trim().is_empty() { + return None; + } + + if previous_node_block.start_line_fast(context.program) == token_range.start_line_fast(context.program) { + return None; + } + + let mut items = PrintItems::new(); + if let Some(close_brace_ref) = previous_close_brace_condition_ref { + items.push_condition(if_true_or( + "elseSeparator", + Rc::new(move |condition_context| Some(condition_context.resolved_condition(&close_brace_ref)?)), + " ".into(), + Signal::NewLine.into(), + )); + } else { + items.push_signal(Signal::NewLine); + } + Some(items) +} + +fn push_always_true_condition(items: &mut PrintItems, name: &'static str) -> ConditionReference { + let mut condition = if_true(name, Rc::new(|_| Some(true)), PrintItems::new()); + let condition_ref = condition.create_reference(); + items.push_condition(condition); + condition_ref +} + struct GenHeaderWithConditionalBraceBodyOptions<'a> { body_node: Stmt<'a>, generated_header: PrintItems, @@ -8855,11 +9020,30 @@ fn gen_conditional_brace_body<'a>(opts: GenConditionalBraceBodyOptions<'a>, cont .into(), )); items.push_sc(sc!("}")); + // Generate trailing comments for BlockStmt nodes when braces are always present + if use_braces == UseBraces::Always { + if let Node::BlockStmt(block_node) = opts.body_node { + items.extend(gen_trailing_comments(&block_node.range(), context)); + } + } items }, ); let close_brace_condition_ref = close_brace_condition.create_reference(); items.push_condition(close_brace_condition); + + // Generate trailing comments for BlockStmt nodes when braces can be removed + // (so comments appear even when braces are not present) + // These should be generated as statement comments (on separate lines) not trailing comments + if use_braces == UseBraces::PreferNone { + if let Node::BlockStmt(_) = opts.body_node { + let trailing_comments = opts.body_node.range().trailing_comments_fast(context.program); + if !trailing_comments.is_empty() { + items.extend(gen_trailing_comments_as_statements(&opts.body_node.range(), context)); + } + } + } + items.push_info(end_ln); items.push_reevaluation(open_brace_condition_reevaluation); @@ -8884,7 +9068,15 @@ fn gen_conditional_brace_body<'a>(opts: GenConditionalBraceBodyOptions<'a>, cont SameOrNextLinePosition::Maintain => { get_body_stmt_start_line(body_node, context) > body_node.previous_token_fast(context.program).start_line_fast(context.program) } - SameOrNextLinePosition::NextLine => true, + SameOrNextLinePosition::NextLine => { + // Keep empty single-line blocks on the same line + if let Node::BlockStmt(block_stmt) = body_node { + if block_stmt.stmts.is_empty() { + return block_stmt.start_line_fast(context.program) < block_stmt.end_line_fast(context.program); + } + } + true + } SameOrNextLinePosition::SameLine => { if let Node::BlockStmt(block_stmt) = body_node { if block_stmt.stmts.len() != 1 { diff --git a/tests/specs/statements/doWhileStatement/DoWhileStatement_NextControlFlowPosition_NextLineExceptAfterBrace.txt b/tests/specs/statements/doWhileStatement/DoWhileStatement_NextControlFlowPosition_NextLineExceptAfterBrace.txt new file mode 100644 index 00000000..148e1d6b --- /dev/null +++ b/tests/specs/statements/doWhileStatement/DoWhileStatement_NextControlFlowPosition_NextLineExceptAfterBrace.txt @@ -0,0 +1,141 @@ +~~ doWhileStatement.nextControlFlowPosition: nextLineExceptAfterBrace ~~ +== should keep while on same line after brace == +do { + doSomething(); +} while (condition); + +[expect] +do { + doSomething(); +} while (condition); + +== should use newline before while for non-block body == +do doSomething(); while (condition); + +[expect] +do doSomething(); +while (condition); + +== should keep while on same line after brace even with trailing comment == +do { + doSomething(); +} // trailing comment +while (condition); + +[expect] +do { + doSomething(); +} // trailing comment +while (condition); + +== should use newline before while when body lacks braces despite trailing comment == +do doSomething(); // trailing comment +while (condition); + +[expect] +do doSomething(); // trailing comment +while (condition); + +== should handle block comment after closing brace == +do { + code(); +} /* block comment */ while (condition); + +[expect] +do { + code(); +} /* block comment */ +while (condition); + +== should handle empty block with trailing comment == +do {} // comment +while (condition); + +[expect] +do {} // comment +while (condition); + +== should handle multi-line block comment == +do { + code(); +} /* + * multi-line + * comment + */ +while (condition); + +[expect] +do { + code(); +} /* + * multi-line + * comment + */ +while (condition); + +== should handle nested structures with trailing comments == +do { + if (x) { + code(); + } // inner comment +} // outer comment +while (condition); + +[expect] +do { + if (x) { + code(); + } // inner comment +} // outer comment +while (condition); + +== should handle empty comment with no text == +do { + code(); +} // +while (condition); + +[expect] +do { + code(); +} // +while (condition); + +== should handle comment with only whitespace == +do { + code(); +} // +while (condition); + +[expect] +do { + code(); +} // +while (condition); + +== should handle pathological all-on-one-line case == +do {} /* comment */ while (condition); + +[expect] +do {} /* comment */ +while (condition); + +== should handle very deeply nested structures == +do { + if (a) { + while (b) { + code(); + } // inner + } // middle +} // outer +while (condition); + +[expect] +do { + if (a) { + while (b) { + code(); + } // inner + } // middle +} // outer +while (condition); diff --git a/tests/specs/statements/doWhileStatement/DoWhileStatement_NextControlFlowPosition_NextLineExceptAfterBrace_Asi.txt b/tests/specs/statements/doWhileStatement/DoWhileStatement_NextControlFlowPosition_NextLineExceptAfterBrace_Asi.txt new file mode 100644 index 00000000..aabd5eff --- /dev/null +++ b/tests/specs/statements/doWhileStatement/DoWhileStatement_NextControlFlowPosition_NextLineExceptAfterBrace_Asi.txt @@ -0,0 +1,49 @@ +~~ semiColons: asi, doWhileStatement.nextControlFlowPosition: nextLineExceptAfterBrace ~~ +== should keep while on same line after brace with ASI == +do { + doSomething(); +} while (condition); + +[expect] +do { + doSomething() +} while (condition) + +== should keep while on same line after brace with trailing comment under ASI == +do { + doSomething() +} // trailing comment +while (condition) + +[expect] +do { + doSomething() +} // trailing comment +while (condition) + +== should use newline before while when body lacks braces under ASI with trailing comment == +do doSomething() // trailing comment +while (condition) + +[expect] +do doSomething() // trailing comment +while (condition) + +== should handle empty comment with no text under ASI == +do { + code() +} // +while (condition) + +[expect] +do { + code() +} // +while (condition) + +== should handle pathological all-on-one-line case with ASI == +do {} /* comment */ while (condition) + +[expect] +do {} /* comment */ +while (condition) diff --git a/tests/specs/statements/ifStatement/IfStatement_NextControlFlowPosition_Maintain_PreferNone.txt b/tests/specs/statements/ifStatement/IfStatement_NextControlFlowPosition_Maintain_PreferNone.txt new file mode 100644 index 00000000..9b1f31ba --- /dev/null +++ b/tests/specs/statements/ifStatement/IfStatement_NextControlFlowPosition_Maintain_PreferNone.txt @@ -0,0 +1,30 @@ +~~ ifStatement.useBraces: preferNone, ifStatement.nextControlFlowPosition: maintain ~~ +== should move else to next line when braces are removed == +if (condition) { + doSomething(); +} else { + handleElse(); +} + +[expect] +if (condition) + doSomething(); +else + handleElse(); + +== should keep else-if chain readable after brace removal == +if (first) { + handleFirst(); +} else if (second) { + handleSecond(); +} else { + handleFallback(); +} + +[expect] +if (first) + handleFirst(); +else if (second) + handleSecond(); +else + handleFallback(); diff --git a/tests/specs/statements/ifStatement/IfStatement_NextControlFlowPosition_NextLineExceptAfterBrace.txt b/tests/specs/statements/ifStatement/IfStatement_NextControlFlowPosition_NextLineExceptAfterBrace.txt new file mode 100644 index 00000000..26eebf59 --- /dev/null +++ b/tests/specs/statements/ifStatement/IfStatement_NextControlFlowPosition_NextLineExceptAfterBrace.txt @@ -0,0 +1,349 @@ +~~ ifStatement.useBraces: preferNone, ifStatement.nextControlFlowPosition: nextLineExceptAfterBrace ~~ +== should move else to next line when braces removed == +if (value) { + handle(); +} else { + fallback(); +} + +[expect] +if (value) + handle(); +else + fallback(); + +== should keep else on same line when braces remain == +if (value) { + handle(); +} else { + fallback(); + more(); +} + +[expect] +if (value) + handle(); +else { + fallback(); + more(); +} + +== should handle trailing comment before else == +if (x) { + f(); +} // test +else { + g(); +} + +[expect] +if (x) + f(); +// test +else + g(); + +== should handle else-if chain == +if (first) { + handleFirst(); +} else if (second) { + handleSecond(); +} else { + handleFallback(); +} + +[expect] +if (first) + handleFirst(); +else if (second) + handleSecond(); +else + handleFallback(); + +== should handle else-if chain with trailing comments == +if (x) { + f(); +} // comment1 +else if (y) { + g(); +} // comment2 +else { + h(); +} + +[expect] +if (x) + f(); +// comment1 +else if (y) + g(); +// comment2 +else + h(); + +== should handle block comment before else == +if (x) { + f(); +} /* block comment */ else { + g(); +} + +[expect] +if (x) + f(); +/* block comment */ else + g(); + +== should handle empty if block with trailing comment == +if (x) {} // comment +else { + g(); +} + +[expect] +if (x) {} // comment +else { + g(); +} + +== should handle comment after else block == +if (x) { + f(); +} else { + g(); +} // final comment + +[expect] +if (x) + f(); +else + g(); // final comment + +== should handle nested structures with multiple trailing comments == +if (outer) { + if (inner) { + code(); + } // inner comment +} // outer comment +else { + fallback(); +} + +[expect] +if (outer) { + if (inner) + code(); // inner comment +} // outer comment +else { + fallback(); +} + +== should handle multi-line block comment before else == +if (x) { + f(); +} /* + * multi-line + * comment + */ +else { + g(); +} + +[expect] +if (x) + f(); +/* + * multi-line + * comment + */ +else + g(); + +== should handle comments with else-if without braces == +if (x) + single(); // comment +else if (y) + other(); + +[expect] +if (x) + single(); // comment +else if (y) + other(); + +== should handle block comment with braces kept == +if (x) { + f(); + g(); +} /* block */ +else { + h(); +} + +[expect] +if (x) { + f(); + g(); +} /* block */ +else { + h(); +} + +== should handle empty comment with no text == +if (x) { + f(); +} // +else { + g(); +} + +[expect] +if (x) + f(); +// +else + g(); + +== should handle comment with only whitespace == +if (x) { + f(); +} // +else { + g(); +} + +[expect] +if (x) + f(); +// +else + g(); + +== should handle pathological all-on-one-line case == +if (a) {} /* c1 */ else if (b) {} /* c2 */ else {} + +[expect] +if (a) {} /* c1 */ +else if (b) {} /* c2 */ +else {} + +== should handle very deeply nested structures with comments == +if (outer) { + if (mid) { + if (inner) { + code(); + } // level3 + } // level2 +} // level1 +else { + fallback(); +} + +[expect] +if (outer) { + if (mid) { + if (inner) + code(); // level3 + } // level2 +} // level1 +else { + fallback(); +} + +== should keep empty if block compact with trailing comment == +if (x) {} // empty +else { + g(); +} + +[expect] +if (x) {} // empty +else { + g(); +} + +== should keep non-empty if block compact without trailing comment == +if (x) { + f(); +} +else { + g(); +} + +[expect] +if (x) + f(); +else + g(); + +== should keep non-empty if block compact with trailing comment == +if (x) { + f(); +} // done +else { + g(); +} + +[expect] +if (x) + f(); +// done +else + g(); + +== should keep empty else block compact with trailing comment == +if (x) { + f(); +} else {} // empty else + +[expect] +if (x) + f(); +else {} // empty else + +== should keep non-empty else block compact == +if (x) { + f(); +} else { + g(); +} + +[expect] +if (x) + f(); +else + g(); + +== should handle mixed empty and non-empty blocks with comments == +if (x) {} // empty if +else { + g(); +} + +[expect] +if (x) {} // empty if +else { + g(); +} + +== should handle all empty if-else with comments == +if (x) {} // x +else {} // y + +[expect] +if (x) {} // x +else {} // y + +== should handle empty if with braces kept and trailing comment == +if (x) { + f(); + g(); +} // multi +else {} // empty + +[expect] +if (x) { + f(); + g(); +} // multi +else {} // empty diff --git a/tests/specs/statements/ifStatement/IfStatement_NextControlFlowPosition_NextLineExceptAfterBrace_Asi.txt b/tests/specs/statements/ifStatement/IfStatement_NextControlFlowPosition_NextLineExceptAfterBrace_Asi.txt new file mode 100644 index 00000000..e4f2b5ee --- /dev/null +++ b/tests/specs/statements/ifStatement/IfStatement_NextControlFlowPosition_NextLineExceptAfterBrace_Asi.txt @@ -0,0 +1,51 @@ +~~ semiColons: asi, ifStatement.useBraces: preferNone, ifStatement.nextControlFlowPosition: nextLineExceptAfterBrace ~~ +== should handle trailing comment before else with ASI == +if (x) { + f() +} // test +else { + g() +} + +[expect] +if (x) + f() +// test +else + g() + +== should handle else-if chain with trailing comments under ASI == +if (x) { + f() +} // comment1 +else if (y) { + g() +} // comment2 +else { + h() +} + +[expect] +if (x) + f() +// comment1 +else if (y) + g() +// comment2 +else + h() + +== should handle block comment before else with ASI == +if (x) { + f() +} /* block comment */ +else { + g() +} + +[expect] +if (x) + f() +/* block comment */ +else + g() diff --git a/tests/specs/statements/tryStatement/TryStatement_All.txt b/tests/specs/statements/tryStatement/TryStatement_All.txt index 0dc4db9f..32e4abdc 100644 --- a/tests/specs/statements/tryStatement/TryStatement_All.txt +++ b/tests/specs/statements/tryStatement/TryStatement_All.txt @@ -56,18 +56,14 @@ try { test; } catch {} -== should expand to try block being on one line when all are on one line == +== should keep empty try block compact when all are on one line == try {} catch {} [expect] -try { -} catch {} +try {} catch {} -== should expand to try block being on one line when all are on one line == +== should keep all empty blocks compact when all are on one line == try {} catch {} finally {} [expect] -try { -} catch { -} finally { -} +try {} catch {} finally {} diff --git a/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_Maintain.txt b/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_Maintain.txt index 71bd698e..6c0a4789 100644 --- a/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_Maintain.txt +++ b/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_Maintain.txt @@ -54,3 +54,51 @@ catch { } finally { } + +== should maintain same line after brace with trailing comment == +try { + code(); +} // comment +catch (e) { + handle(); +} + +[expect] +try { + code(); +} // comment +catch (e) { + handle(); +} + +== should maintain new line when comment before catch == +try { + code(); +} +// comment before catch +catch (e) { + handle(); +} + +[expect] +try { + code(); +} +// comment before catch +catch (e) { + handle(); +} + +== should maintain position with block comment == +try { + code(); +} /* block */ catch (e) { + handle(); +} + +[expect] +try { + code(); +} /* block */ catch (e) { + handle(); +} diff --git a/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_NextLine.txt b/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_NextLine.txt index fed4ea4b..0cd4b6d3 100644 --- a/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_NextLine.txt +++ b/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_NextLine.txt @@ -15,3 +15,35 @@ catch { } finally { } + +== should always use newline even with trailing comment == +try { + code(); +} // comment +catch (e) { + handle(); +} + +[expect] +try { + code(); +} // comment +catch (e) { + handle(); +} + +== should always use newline with block comment == +try { + code(); +} /* block */ +catch (e) { + handle(); +} + +[expect] +try { + code(); +} /* block */ +catch (e) { + handle(); +} diff --git a/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_NextLineExceptAfterBrace.txt b/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_NextLineExceptAfterBrace.txt new file mode 100644 index 00000000..97bc2f7c --- /dev/null +++ b/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_NextLineExceptAfterBrace.txt @@ -0,0 +1,407 @@ +~~ tryStatement.nextControlFlowPosition: nextLineExceptAfterBrace ~~ +== should keep catch on same line after brace == +try { + doSomething(); +} catch (err) { + handleError(); +} + +[expect] +try { + doSomething(); +} catch (err) { + handleError(); +} + +== should keep catch on new line when trailing comment present == +try { + run(); +} // trailing comment +catch (err) { + handle(err); +} + +[expect] +try { + run(); +} // trailing comment +catch (err) { + handle(err); +} + +== should keep finally on new line when trailing comment present == +try { + run(); +} // trailing comment +finally { + cleanup(); +} + +[expect] +try { + run(); +} // trailing comment +finally { + cleanup(); +} + +== should keep finally on same line in try-finally == +try { + doSomething(); +} finally { + cleanup(); +} + +[expect] +try { + doSomething(); +} finally { + cleanup(); +} + +== should handle try-catch-finally == +try { + doSomething(); +} catch (err) { + handleError(); +} finally { + cleanup(); +} + +[expect] +try { + doSomething(); +} catch (err) { + handleError(); +} finally { + cleanup(); +} + +== should handle try-catch-finally with trailing comments == +try { + doSomething(); +} // comment1 +catch (err) { + handleError(); +} // comment2 +finally { + cleanup(); +} + +[expect] +try { + doSomething(); +} // comment1 +catch (err) { + handleError(); +} // comment2 +finally { + cleanup(); +} + +== should handle block comment after closing brace == +try { + code(); +} /* block comment */ +catch (e) { + handle(); +} + +[expect] +try { + code(); +} /* block comment */ +catch (e) { + handle(); +} + +== should handle empty blocks with trailing comments == +try {} // comment +catch (e) {} + +[expect] +try {} // comment +catch (e) {} + +== should handle comment after finally block == +try { + code(); +} catch (e) { + handle(); +} finally { + cleanup(); +} // final comment + +[expect] +try { + code(); +} catch (e) { + handle(); +} finally { + cleanup(); +} // final comment + +== should handle nested structures with multiple trailing comments == +try { + if (x) { + code(); + } // inner comment +} // outer comment +catch (e) { + handle(); +} + +[expect] +try { + if (x) { + code(); + } // inner comment +} // outer comment +catch (e) { + handle(); +} + +== should handle multi-line block comments == +try { + code(); +} /* + * multi-line + * comment + */ +catch (e) { + handle(); +} + +[expect] +try { + code(); +} /* + * multi-line + * comment + */ +catch (e) { + handle(); +} + +== should keep catch on same line when no trailing comment == +try { + run(); +} +catch (err) { + handleWithoutBraces(); +} + +[expect] +try { + run(); +} catch (err) { + handleWithoutBraces(); +} + +== should handle empty comment with no text == +try { + code(); +} // +catch (e) { + handle(); +} + +[expect] +try { + code(); +} // +catch (e) { + handle(); +} + +== should handle comment with only whitespace == +try { + code(); +} // +catch (e) { + handle(); +} + +[expect] +try { + code(); +} // +catch (e) { + handle(); +} + +== should handle very deeply nested structures == +try { + if (a) { + while (b) { + do { + code(); + } // inner1 + while (c); // inner2 + } // inner3 + } // inner4 +} // outer +catch (e) { + handle(); +} + +[expect] +try { + if (a) { + while (b) { + do { + code(); + } // inner1 + while (c); // inner2 + } // inner3 + } // inner4 +} // outer +catch (e) { + handle(); +} + +== should keep empty try block compact without trailing comment == +try {} +catch (e) { + handle(); +} + +[expect] +try {} catch (e) { + handle(); +} + +== should keep empty try block compact with trailing comment == +try {} // empty +catch (e) { + handle(); +} + +[expect] +try {} // empty +catch (e) { + handle(); +} + +== should keep non-empty try block compact without trailing comment == +try { + code(); +} +catch (e) { + handle(); +} + +[expect] +try { + code(); +} catch (e) { + handle(); +} + +== should keep non-empty try block compact with trailing comment == +try { + code(); +} // done +catch (e) { + handle(); +} + +[expect] +try { + code(); +} // done +catch (e) { + handle(); +} + +== should keep empty catch block on same line without trailing comment == +try { + code(); +} catch (e) {} + +[expect] +try { + code(); +} catch (e) {} + +== should keep empty catch block compact with trailing comment == +try { + code(); +} catch (e) {} // empty catch + +[expect] +try { + code(); +} catch (e) {} // empty catch + +== should keep non-empty catch block compact == +try { + code(); +} catch (e) { + handle(); +} + +[expect] +try { + code(); +} catch (e) { + handle(); +} + +== should keep empty finally block on same line without trailing comment == +try { + code(); +} catch (e) { + handle(); +} finally {} + +[expect] +try { + code(); +} catch (e) { + handle(); +} finally {} + +== should keep empty finally block compact with trailing comment == +try { + code(); +} catch (e) { + handle(); +} finally {} // cleanup + +[expect] +try { + code(); +} catch (e) { + handle(); +} finally {} // cleanup + +== should handle mixed empty and non-empty blocks == +try {} // empty try +catch (e) { + handle(); +} finally {} // empty finally + +[expect] +try {} // empty try +catch (e) { + handle(); +} finally {} // empty finally + +== should keep all empty blocks compact without comments == +try {} +catch (e) {} +finally {} + +[expect] +try {} catch (e) {} finally {} + +== should keep all empty blocks compact with comments == +try {} // t +catch (e) {} // c +finally {} // f + +[expect] +try {} // t +catch (e) {} // c +finally {} // f diff --git a/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_NextLineExceptAfterBrace_Asi.txt b/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_NextLineExceptAfterBrace_Asi.txt new file mode 100644 index 00000000..41a3bcbc --- /dev/null +++ b/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_NextLineExceptAfterBrace_Asi.txt @@ -0,0 +1,68 @@ +~~ semiColons: asi, tryStatement.nextControlFlowPosition: nextLineExceptAfterBrace ~~ +== should keep catch on same line with ASI == +try { + doSomething() +} catch (err) { + handleError() +} + +[expect] +try { + doSomething() +} catch (err) { + handleError() +} + +== should handle trailing comment with ASI == +try { + run() +} // trailing comment +catch (err) { + handle(err) +} + +[expect] +try { + run() +} // trailing comment +catch (err) { + handle(err) +} + +== should handle block comment with ASI == +try { + code() +} /* block comment */ +catch (e) { + handle() +} + +[expect] +try { + code() +} /* block comment */ +catch (e) { + handle() +} + +== should handle try-catch-finally with trailing comments under ASI == +try { + doSomething() +} // comment1 +catch (err) { + handleError() +} // comment2 +finally { + cleanup() +} + +[expect] +try { + doSomething() +} // comment1 +catch (err) { + handleError() +} // comment2 +finally { + cleanup() +} diff --git a/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_SameLine.txt b/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_SameLine.txt index be3186de..03d4f2c8 100644 --- a/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_SameLine.txt +++ b/tests/specs/statements/tryStatement/TryStatement_NextControlFlowPosition_SameLine.txt @@ -15,3 +15,49 @@ try { } catch { } finally { } + +== should use newline when trailing comment present == +try { + code(); +} // comment +catch (e) { + handle(); +} + +[expect] +try { + code(); +} // comment +catch (e) { + handle(); +} + +== should keep on same line with block comment == +try { + code(); +} /* block */ catch (e) { + handle(); +} + +[expect] +try { + code(); +} /* block */ catch (e) { + handle(); +} + +== should use newline when block and line comments present == +try { + work(); +} /* block */ // trailing +catch (err) { + recover(); +} + +[expect] +try { + work(); +} /* block */ // trailing +catch (err) { + recover(); +}