From 617a4ab22a4e17472a5681fffb0b02206f111306 Mon Sep 17 00:00:00 2001 From: Lucas Santana Date: Mon, 22 Jun 2026 14:46:47 -0300 Subject: [PATCH 1/4] fix: wrap faultdefs and align documented declarations --- src/formatter.c3 | 89 ++++++++++++++++++++++++++++++-------- test/corpus/alignment.c3 | 8 ++++ test/corpus/alignment_f.c3 | 8 ++++ test/corpus/wrapping.c3 | 27 +++++++++++- test/corpus/wrapping_f.c3 | 37 ++++++++++++++++ 5 files changed, 150 insertions(+), 19 deletions(-) diff --git a/src/formatter.c3 b/src/formatter.c3 index 34dd18b..e554aa9 100644 --- a/src/formatter.c3 +++ b/src/formatter.c3 @@ -417,27 +417,55 @@ fn bool C3Fmt.has_trailing_comma(&self, TSNode node) return false; } -fn bool is_alignable_block_node(TSNode node) +fn bool is_declaration_wrapper(TSNode node) { String type = node.type(); - if (type == "declaration_stmt" || type == "global_declaration") + return type == "declaration_stmt" || type == "global_declaration"; +} + +fn TSNode declaration_target(TSNode node) +{ + if (!is_declaration_wrapper(node)) return node; + + for (uint i = 0; i < ts::node_named_child_count(node); ++i) { - if (ts::node_named_child_count(node) == 0) return false; - node = ts::node_named_child(node, 0); - type = node.type(); + TSNode child = ts::node_named_child(node, i); + if (child.type() == "doc_comment" || child.is_comment()) continue; + return child; } - return type == "const_declaration" || type == "var_declaration" || (type == "declaration" && node.start_point_row() == node.end_point_row()) || type == "enum_constant"; + return (TSNode){}; } -fn TSNode find_assignment_op(TSNode node) +fn void C3Fmt.process_declaration_prefix(&self, TSNode node, TSNode target) { - TSNode target = node; - String type = node.type(); - if (type == "declaration_stmt" || type == "global_declaration") + if (!is_declaration_wrapper(node)) return; + + for (uint i = 0; i < ts::node_child_count(node); ++i) { - if (ts::node_named_child_count(node) == 0) return (TSNode){}; - target = ts::node_named_child(node, 0); + TSNode child = ts::node_child(node, i); + if (ts::node_eq(child, target)) return; + if (ts::node_is_named(child)) self.process_node(child); } +} + +fn bool is_alignable_block_node(TSNode node) +{ + node = declaration_target(node); + if (ts::node_is_null(node)) return false; + String type = node.type(); + if (type == "const_declaration" || type == "var_declaration" || type == "enum_constant") return true; + if (type != "declaration") return false; + if (node.start_point_row() == node.end_point_row()) return true; + if (!node.has_child_with_field("right")) return false; + String right_type = node.get_child_with_field("right").type(); + return right_type == "binary_expr" || right_type == "ternary_expr" || right_type == "elvis_orelse_expr"; +} + +fn TSNode find_assignment_op(TSNode node) +{ + TSNode target = declaration_target(node); + if (ts::node_is_null(target)) return (TSNode){}; + String type = target.type(); for (uint i = 0; i < ts::node_child_count(target); ++i) { @@ -493,7 +521,7 @@ fn void C3Fmt.process_aligned_block(&self, TSNode parent, uint start_index, uint if (!ts::node_is_null(op)) { // Format up to the '=' - TSNode target = (child.type() == "declaration_stmt" || child.type() == "global_declaration") ? ts::node_named_child(child, 0) : child; + TSNode target = declaration_target(child); self.process_assignment_left(target, op); max_op_col = max(max_op_col, self.current_column()); } @@ -547,9 +575,11 @@ fn void C3Fmt.process_aligned_block(&self, TSNode parent, uint start_index, uint TSNode op = find_assignment_op(child); if (!ts::node_is_null(op) && max_op_col > 0) { - TSNode target = (child.type() == "declaration_stmt" || child.type() == "global_declaration") ? ts::node_named_child(child, 0) : child; + TSNode target = declaration_target(child); TSNode right = (target.type() == "macro_declaration" || !target.has_child_with_field("right")) ? (TSNode){} : target.get_child_with_field("right"); + self.process_declaration_prefix(child, target); + // Format until '=' self.process_assignment_left(target, op); @@ -572,7 +602,7 @@ fn void C3Fmt.process_aligned_block(&self, TSNode parent, uint start_index, uint TSNode decl = ts::node_named_child(parent, m); TSNode op2 = find_assignment_op(decl); if (!ts::node_is_null(op2)) { - TSNode t2 = (decl.type() == "declaration_stmt" || decl.type() == "global_declaration") ? ts::node_named_child(decl, 0) : decl; + TSNode t2 = declaration_target(decl); TSNode r2 = (t2.type() == "macro_declaration" || !t2.has_child_with_field("right")) ? (TSNode){} : t2.get_child_with_field("right"); // For macros, the 'right' side is the body, but it starts with => if (ts::node_is_null(r2) && t2.type() == "macro_declaration") r2 = op2; @@ -611,9 +641,14 @@ fn void C3Fmt.process_aligned_block(&self, TSNode parent, uint start_index, uint } // Semicolon of declaration_stmt if any - if (child.type() == "declaration_stmt" || child.type() == "global_declaration") { - for (uint s = 1; s < ts::node_child_count(child); ++s) { + if (is_declaration_wrapper(child)) { + bool found_target = false; + for (uint s = 0; s < ts::node_child_count(child); ++s) { TSNode sc = ts::node_child(child, s); + if (!found_target) { + if (ts::node_eq(sc, target)) found_target = true; + continue; + } if (ts::node_is_named(sc)) { if (sc.is_comment()) { self.buf.push(token::space()); @@ -1714,6 +1749,8 @@ fn void C3Fmt.process_node( } } bool is_multi = start_row != node.end_point().row; + usz id = 0; + bool has_const = false; for (uint i = 0; i < ts::node_child_count(node); ++i) { TSNode child = ts::node_child(node, i); @@ -1732,6 +1769,11 @@ fn void C3Fmt.process_node( { self.buf.newline(); self.indent(); + id = self.begin_wrap_group(2); + } + else + { + id = self.begin_wrap_group(2); } continue; } @@ -1740,7 +1782,6 @@ fn void C3Fmt.process_node( { if (is_multi) { - self.buf.newline(); self.dedent(); } self.buf.text(";"); @@ -1759,6 +1800,18 @@ fn void C3Fmt.process_node( } } + if (type == "const_ident" && self.wrapping_enabled) + { + if (!is_multi || has_const) + { + self.buf.wrap_point({ + .indent = is_multi ? 0 : 1, + .group_id = id + }); + } + has_const = true; + } + if (child.is_comment()) { String comment_str = self.extract_comment_string(child); diff --git a/test/corpus/alignment.c3 b/test/corpus/alignment.c3 index 038439c..fe855fe 100644 --- a/test/corpus/alignment.c3 +++ b/test/corpus/alignment.c3 @@ -8,3 +8,11 @@ const int[2][] SAUCER_LINES = { {6,0}, {6,1}, {6,2}, {6,3}, {6,4}, {6,5}, {7,0}, {7,1}, {7,2}, {7,3}, {7,4}, {7,5} }; + +<* + Constants in this group should align even when the first declaration has a + doc comment. +*> +const int FIRST = 1; +const int SECOND_LONGER = 2; +const int THIRD = 3; diff --git a/test/corpus/alignment_f.c3 b/test/corpus/alignment_f.c3 index 95a7e61..435acbb 100644 --- a/test/corpus/alignment_f.c3 +++ b/test/corpus/alignment_f.c3 @@ -4,3 +4,11 @@ const int[2][] SAUCER_LINES = { { 6, 0 }, { 6, 1 }, { 6, 2 }, { 6, 3 }, { 6, 4 }, { 6, 5 }, { 7, 0 }, { 7, 1 }, { 7, 2 }, { 7, 3 }, { 7, 4 }, { 7, 5 } }; + +<* + Constants in this group should align even when the first declaration has a + doc comment. +*> +const int FIRST = 1; +const int SECOND_LONGER = 2; +const int THIRD = 3; diff --git a/test/corpus/wrapping.c3 b/test/corpus/wrapping.c3 index 07e92be..bb423f2 100644 --- a/test/corpus/wrapping.c3 +++ b/test/corpus/wrapping.c3 @@ -26,4 +26,29 @@ Pipeline a = { .offset = "a very long string that will go beyond 80 words",// Todo: Add CameraData offset here .range = "a very long string that will go beyond 80 words" } -}; \ No newline at end of file +}; + +faultdef SAMPLE_ALPHA, SAMPLE_BETA, SAMPLE_GAMMA, SAMPLE_DELTA, SAMPLE_EPSILON, SAMPLE_ZETA, SAMPLE_ETA, SAMPLE_THETA, SAMPLE_IOTA; + +fn void wrap_assignment_expr() +{ + ParserState* state = (ParserState*)raw; + WorkItem* item = (WorkItem*)state.owner; + Handler handler = item.handler; + bool paused = (state.flags & SAMPLE_FLAG_DISABLED) != 0; + bool ready = !state.error_code; + ulong total_count = ready ? compute_total_count(context, state, item) : 0; + bool should_continue = ready && !paused && (long)item.interval > 0 && total_count <= (ulong)item.remaining_count; +} + +fn void wrap_assignment_expr_stable() +{ + ParserState* state = (ParserState*)raw; + WorkItem* item = (WorkItem*)state.owner; + Handler handler = item.handler; + bool paused = (state.flags & SAMPLE_FLAG_DISABLED) != 0; + bool ready = !state.error_code; + ulong total_count = ready ? compute_total_count(context, state, item) : 0; + bool should_continue = ready && !paused && (long)item.interval > 0 + && total_count <= (ulong)item.remaining_count; +} diff --git a/test/corpus/wrapping_f.c3 b/test/corpus/wrapping_f.c3 index 873c1f9..628e3e0 100644 --- a/test/corpus/wrapping_f.c3 +++ b/test/corpus/wrapping_f.c3 @@ -38,3 +38,40 @@ Pipeline a = { .range = "a very long string that will go beyond 80 words" } }; + +faultdef + SAMPLE_ALPHA, + SAMPLE_BETA, + SAMPLE_GAMMA, + SAMPLE_DELTA, + SAMPLE_EPSILON, + SAMPLE_ZETA, + SAMPLE_ETA, + SAMPLE_THETA, + SAMPLE_IOTA; + +fn void wrap_assignment_expr() +{ + ParserState* state = (ParserState*)raw; + WorkItem* item = (WorkItem*)state.owner; + Handler handler = item.handler; + bool paused = (state.flags & SAMPLE_FLAG_DISABLED) != 0; + bool ready = !state.error_code; + ulong total_count = ready ? compute_total_count(context, state, item) + : 0; + bool should_continue = ready && !paused && (long)item.interval > 0 + && total_count <= (ulong)item.remaining_count; +} + +fn void wrap_assignment_expr_stable() +{ + ParserState* state = (ParserState*)raw; + WorkItem* item = (WorkItem*)state.owner; + Handler handler = item.handler; + bool paused = (state.flags & SAMPLE_FLAG_DISABLED) != 0; + bool ready = !state.error_code; + ulong total_count = ready ? compute_total_count(context, state, item) + : 0; + bool should_continue = ready && !paused && (long)item.interval > 0 + && total_count <= (ulong)item.remaining_count; +} From 5f63790ca11fe2fb93723701df3ece9c281bee10 Mon Sep 17 00:00:00 2001 From: Lucas Santana Date: Mon, 22 Jun 2026 14:52:04 -0300 Subject: [PATCH 2/4] Add newline_at_eof formatter option --- .c3fmt | 3 ++- README.md | 2 +- src/config.c3 | 6 +++++- src/printer.c3 | 8 +++++++- test/corpus.c3 | 18 ++++++++++++++++++ 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/.c3fmt b/.c3fmt index cdaee1d..e587a19 100644 --- a/.c3fmt +++ b/.c3fmt @@ -7,6 +7,7 @@ max_line_length: 120 brace_style: ALLMAN else_on_newline: true +newline_at_eof: false align_assignments: true -align_comments: true \ No newline at end of file +align_comments: true diff --git a/README.md b/README.md index a227bd3..b8c3789 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ You can look at [.c3fmt](.c3fmt) for the default configuration. | `max_line_length` | Maximum line length before wrapping. | `120` | | `brace_style` | The brace style to use: `ALLMAN` or `K&R`. | `ALLMAN` | | `else_on_newline` | Whether to put `else` on a new line. | `true` | +| `newline_at_eof` | Ensure formatted output ends with a newline. | `false` | | `align_assignments` | Align `=` and `=>` in consecutive declarations/assignments. | `true` | | `align_comments` | Align trailing comments in consecutive lines. | `true` | @@ -148,4 +149,3 @@ The test suite includes: ## Known issues *(none yet)* - diff --git a/src/config.c3 b/src/config.c3 index 6275595..4db03cb 100644 --- a/src/config.c3 +++ b/src/config.c3 @@ -17,6 +17,7 @@ struct Config BraceStyle brace_style; bool else_on_newline; + bool newline_at_eof; bool align_assignments; bool align_comments; @@ -32,6 +33,7 @@ fn Config default_config() .max_line_length = 120, .brace_style = ALLMAN, .else_on_newline = true, + .newline_at_eof = false, .align_assignments = true, .align_comments = true, }; @@ -115,6 +117,8 @@ fn Config? parse_from_file(String file_path) => @pool() } case "else_on_newline": conf.else_on_newline = val.to_bool()!; + case "newline_at_eof": + conf.newline_at_eof = val.to_bool()!; case "align_assignments": conf.align_assignments = val.to_bool()!; case "align_comments": @@ -126,4 +130,4 @@ fn Config? parse_from_file(String file_path) => @pool() } return conf; -} \ No newline at end of file +} diff --git a/src/printer.c3 b/src/printer.c3 index 5ab0986..bbe611a 100644 --- a/src/printer.c3 +++ b/src/printer.c3 @@ -246,6 +246,12 @@ fn void C3Fmt.print(&self) default: } } + + String output = self.dstr_buf.str_view(); + if (self.config.newline_at_eof && output.len > 0 && output[^1] != '\n') + { + self.dstr_buf.append('\n'); + } } fn void C3Fmt.print_indent(&self) @@ -567,4 +573,4 @@ fn bool TokenBuffer.gobble_last_newline(&self) } return false; -} \ No newline at end of file +} diff --git a/test/corpus.c3 b/test/corpus.c3 index 3551b74..7697ce2 100644 --- a/test/corpus.c3 +++ b/test/corpus.c3 @@ -74,6 +74,24 @@ fn void alignment() @test { test("alignment", "test/corpus/alignment.c3", "test/corpus/alignment_f.c3", conf); } +fn void newline_at_eof() @test { + String input = "fn void main() {}"; + + Config conf = c3fmt::default_config(); + assert(!conf.newline_at_eof); + + String without_newline = c3fmt::format_string(mem, input, conf, check_semantic: true)!!; + defer without_newline.free(mem); + assert(without_newline.len > 0); + assert(without_newline[^1] != '\n'); + + conf.newline_at_eof = true; + String with_newline = c3fmt::format_string(mem, input, conf, check_semantic: true)!!; + defer with_newline.free(mem); + assert(with_newline.len > 0); + assert(with_newline[^1] == '\n'); +} + fn void indent_leak() @test { test("indent_leak", "test/corpus/indent_leak.c3", "test/corpus/indent_leak_f.c3"); } From e9dffa9db8f23b293b168945dc9b45ca20116d02 Mon Sep 17 00:00:00 2001 From: Lucas Santana Date: Mon, 22 Jun 2026 15:56:21 -0300 Subject: [PATCH 3/4] Stabilize alignment for long call declarations --- src/formatter.c3 | 56 +++++++++++++++++++---- test/corpus.c3 | 4 ++ test/corpus/alignment_wrap_stability.c3 | 6 +++ test/corpus/alignment_wrap_stability_f.c3 | 6 +++ 4 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 test/corpus/alignment_wrap_stability.c3 create mode 100644 test/corpus/alignment_wrap_stability_f.c3 diff --git a/src/formatter.c3 b/src/formatter.c3 index e554aa9..2ebe568 100644 --- a/src/formatter.c3 +++ b/src/formatter.c3 @@ -502,6 +502,17 @@ fn void C3Fmt.process_assignment_left(&self, TSNode target, TSNode op) } } +fn void C3Fmt.process_alignment_probe_node(&self, TSNode child) +{ + if (child.type() == "declaration_stmt" || child.type() == "global_declaration") + { + self.process_composite(child, append_newline: false); + return; + } + + self.process_node(child, false); +} + fn void C3Fmt.process_aligned_block(&self, TSNode parent, uint start_index, uint end_index) { usz max_op_col = 0; @@ -529,15 +540,7 @@ fn void C3Fmt.process_aligned_block(&self, TSNode parent, uint start_index, uint // Reset and format whole node without trailing newline to find comment col while (self.buf.len() > start_buf_len) (void)self.pop_token(); - if (child.type() == "declaration_stmt" || child.type() == "global_declaration") - { - // We need to NOT append newline here - self.process_composite(child, append_newline: false); - } - else - { - self.process_node(child, false); - } + self.process_alignment_probe_node(child); if (i + 1 < named_child_count) { @@ -553,6 +556,41 @@ fn void C3Fmt.process_aligned_block(&self, TSNode parent, uint start_index, uint } } + if (self.config.max_line_length > 0 && max_op_col > 0) + { + for (uint i = start_index; i < end_index; ++i) + { + TSNode child = ts::node_named_child(parent, i); + if (child.is_comment()) continue; + + sz start_buf_len = self.buf.len(); + TSNode op = find_assignment_op(child); + if (ts::node_is_null(op)) continue; + + TSNode target = declaration_target(child); + TSNode right = (target.type() == "macro_declaration" || !target.has_child_with_field("right")) + ? (TSNode){} + : target.get_child_with_field("right"); + if (ts::node_is_null(right) || right.type() != "call_expr") continue; + + self.process_assignment_left(target, op); + usz op_col = self.current_column(); + while (self.buf.len() > start_buf_len) (void)self.pop_token(); + + self.process_alignment_probe_node(child); + usz line_col = self.current_column(); + while (self.buf.len() > start_buf_len) (void)self.pop_token(); + + if (max_op_col > op_col + && line_col <= self.config.max_line_length + && line_col + (max_op_col - op_col) > self.config.max_line_length) + { + max_op_col = 0; + break; + } + } + } + if (max_op_col > 80) { max_op_col = 0; } diff --git a/test/corpus.c3 b/test/corpus.c3 index 7697ce2..e5a4102 100644 --- a/test/corpus.c3 +++ b/test/corpus.c3 @@ -74,6 +74,10 @@ fn void alignment() @test { test("alignment", "test/corpus/alignment.c3", "test/corpus/alignment_f.c3", conf); } +fn void alignment_wrap_stability() @test { + test("alignment_wrap_stability", "test/corpus/alignment_wrap_stability.c3", "test/corpus/alignment_wrap_stability_f.c3"); +} + fn void newline_at_eof() @test { String input = "fn void main() {}"; diff --git a/test/corpus/alignment_wrap_stability.c3 b/test/corpus/alignment_wrap_stability.c3 new file mode 100644 index 0000000..7a2bebf --- /dev/null +++ b/test/corpus/alignment_wrap_stability.c3 @@ -0,0 +1,6 @@ +fn void update_record(Source* source) +{ + ExtremelyLongComputationContext* context = (ExtremelyLongComputationContext*)source.context; + Result result = build_record(source.primary_value, context, source.secondary_value, source.tertiary_value); + (void)result; +} diff --git a/test/corpus/alignment_wrap_stability_f.c3 b/test/corpus/alignment_wrap_stability_f.c3 new file mode 100644 index 0000000..7a2bebf --- /dev/null +++ b/test/corpus/alignment_wrap_stability_f.c3 @@ -0,0 +1,6 @@ +fn void update_record(Source* source) +{ + ExtremelyLongComputationContext* context = (ExtremelyLongComputationContext*)source.context; + Result result = build_record(source.primary_value, context, source.secondary_value, source.tertiary_value); + (void)result; +} From c3a222e4d47a127d0b50d2bf65170ecb2e76523c Mon Sep 17 00:00:00 2001 From: Lucas Santana Date: Fri, 26 Jun 2026 09:16:30 -0300 Subject: [PATCH 4/4] Add align_string_concat formatter option --- README.md | 1 + src/config.c3 | 4 ++ src/formatter.c3 | 55 +++++++++++++++++++++++++++- test/corpus.c3 | 13 +++++++ test/corpus/string_concat.c3 | 10 +++++ test/corpus/string_concat_align_f.c3 | 15 ++++++++ test/corpus/string_concat_f.c3 | 15 ++++++++ 7 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 test/corpus/string_concat.c3 create mode 100644 test/corpus/string_concat_align_f.c3 create mode 100644 test/corpus/string_concat_f.c3 diff --git a/README.md b/README.md index b8c3789..106c486 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ You can look at [.c3fmt](.c3fmt) for the default configuration. | `newline_at_eof` | Ensure formatted output ends with a newline. | `false` | | `align_assignments` | Align `=` and `=>` in consecutive declarations/assignments. | `true` | | `align_comments` | Align trailing comments in consecutive lines. | `true` | +| `align_string_concat` | Align wrapped adjacent string literals under the first literal's column (otherwise indent one level). | `false` | ## Building diff --git a/src/config.c3 b/src/config.c3 index 4db03cb..41819c7 100644 --- a/src/config.c3 +++ b/src/config.c3 @@ -21,6 +21,7 @@ struct Config bool align_assignments; bool align_comments; + bool align_string_concat; } fn Config default_config() @@ -36,6 +37,7 @@ fn Config default_config() .newline_at_eof = false, .align_assignments = true, .align_comments = true, + .align_string_concat = false, }; } @@ -123,6 +125,8 @@ fn Config? parse_from_file(String file_path) => @pool() conf.align_assignments = val.to_bool()!; case "align_comments": conf.align_comments = val.to_bool()!; + case "align_string_concat": + conf.align_string_concat = val.to_bool()!; default: return INVALID_CONFIG~; diff --git a/src/formatter.c3 b/src/formatter.c3 index 2ebe568..b8c964e 100644 --- a/src/formatter.c3 +++ b/src/formatter.c3 @@ -913,6 +913,55 @@ fn void C3Fmt.process_composite( } } +<* + Format a sequence of adjacent string/bytes literals (implicit concatenation). + Each literal after the first becomes a wrap point, so an over-long concatenation + is broken with one literal per line, keeping the first on the opening line. + @param node : "The string_expr or bytes_expr node" +*> +fn void C3Fmt.process_concat_literals(&self, TSNode node) +{ + // Comments between literals are rare; fall back to the plain layout for them. + for (uint i = 0; i < ts::node_child_count(node); ++i) + { + if (ts::node_child(node, i).is_comment()) + { + self.process_composite(node); + return; + } + } + + if (!self.wrapping_enabled) + { + self.process_composite(node); + return; + } + + usz id = self.begin_wrap_group(2); + + // When aligning, continuation literals line up under the first literal's + // column; otherwise they hang one indent level below. + bool align = self.config.align_string_concat; + usz align_col = align ? self.current_column() : 0; + + for (uint i = 0; i < ts::node_child_count(node); ++i) + { + TSNode child = ts::node_child(node, i); + + if (i > 0) + { + self.buf.space(); + self.buf.wrap_point({ .indent = align ? 0 : 1, .group_id = id }); + if (align) + { + self.buf.push(token::align(align_col)); + } + } + + self.process_node(child); + } +} + <* Rawly format node sequentially, without appending any separator between them. Useful for nodes like field accessor ("my_struct.my_field"). @@ -2488,8 +2537,10 @@ fn void C3Fmt.process_node( case "doc_comment_contract_descriptor": self.buf.text(node.get_text(self.source_code)); - case "bytes_expr": // TODO idk what this is, a space separated list of bytes_literal ? - case "string_expr": // same... + case "bytes_expr": // space separated list of bytes_literal + case "string_expr": // adjacent string literals (implicit concatenation) + self.process_concat_literals(node); + case "lengthof": case "$eval": case "$stringify": diff --git a/test/corpus.c3 b/test/corpus.c3 index e5a4102..2a0a91d 100644 --- a/test/corpus.c3 +++ b/test/corpus.c3 @@ -68,6 +68,19 @@ fn void comments() @test { test("comments", "test/corpus/comments.c3", "test/corpus/comments_f.c3", conf); } +fn void string_concat() @test { + Config conf = c3fmt::default_config(); + conf.max_line_length = 80; + test("string_concat", "test/corpus/string_concat.c3", "test/corpus/string_concat_f.c3", conf); +} + +fn void string_concat_align() @test { + Config conf = c3fmt::default_config(); + conf.max_line_length = 80; + conf.align_string_concat = true; + test("string_concat_align", "test/corpus/string_concat.c3", "test/corpus/string_concat_align_f.c3", conf); +} + fn void alignment() @test { Config conf = c3fmt::default_config(); conf.max_line_length = 180; diff --git a/test/corpus/string_concat.c3 b/test/corpus/string_concat.c3 new file mode 100644 index 0000000..bef7418 --- /dev/null +++ b/test/corpus/string_concat.c3 @@ -0,0 +1,10 @@ +module x; + +const String SHORT = "ab" "cd"; + +const String LONG = "aaaaaaaaaaaaaaaaaaaaaaaa" "bbbbbbbbbbbbbbbbbbbbbbbb" "cccccccccccccccccccccccc" "dddddddddddddddddddddddd"; + +fn void f() +{ + String s = "one one one one one one one" "two two two two two two two" "three three three three three"; +} diff --git a/test/corpus/string_concat_align_f.c3 b/test/corpus/string_concat_align_f.c3 new file mode 100644 index 0000000..2292014 --- /dev/null +++ b/test/corpus/string_concat_align_f.c3 @@ -0,0 +1,15 @@ +module x; + +const String SHORT = "ab" "cd"; + +const String LONG = "aaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbb" + "cccccccccccccccccccccccc" + "dddddddddddddddddddddddd"; + +fn void f() +{ + String s = "one one one one one one one" + "two two two two two two two" + "three three three three three"; +} diff --git a/test/corpus/string_concat_f.c3 b/test/corpus/string_concat_f.c3 new file mode 100644 index 0000000..cd052f1 --- /dev/null +++ b/test/corpus/string_concat_f.c3 @@ -0,0 +1,15 @@ +module x; + +const String SHORT = "ab" "cd"; + +const String LONG = "aaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbb" + "cccccccccccccccccccccccc" + "dddddddddddddddddddddddd"; + +fn void f() +{ + String s = "one one one one one one one" + "two two two two two two two" + "three three three three three"; +}