From 1db71731cd153291fbb9724d09c4182a4b1a8979 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 25 Mar 2026 16:42:18 +0300 Subject: [PATCH] vfmt: fix inconsistent wrapping of long lines (fixes #16543) --- vlib/v/fmt/fmt.v | 40 ++++++++++-- vlib/v/fmt/tests/long_assign_wrap_expected.vv | 63 +++++++++++++++++++ vlib/v/fmt/tests/long_assign_wrap_input.vv | 60 ++++++++++++++++++ 3 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 vlib/v/fmt/tests/long_assign_wrap_expected.vv create mode 100644 vlib/v/fmt/tests/long_assign_wrap_input.vv diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 5a78f084c..2b873e828 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -843,6 +843,15 @@ fn expr_is_single_line(expr ast.Expr) bool { return true } +fn (mut f Fmt) write_expr_list(exprs []ast.Expr) { + for i, expr in exprs { + f.expr(expr) + if i < exprs.len - 1 { + f.write(', ') + } + } +} + //=== Specific Stmt methods ===// pub fn (mut f Fmt) assert_stmt(node ast.AssertStmt) { @@ -866,10 +875,23 @@ pub fn (mut f Fmt) assign_stmt(node ast.AssignStmt) { } f.is_assign = true f.write(' ${node.op.str()} ') - for i, val in node.right { - f.expr(val) - if i < node.right.len - 1 { - f.write(', ') + right_start_pos := f.out.len + right_start_len := f.line_len + can_wrap_rhs := node.right.len == 1 && node.right[0] in [ast.CallExpr, ast.StructInit] + f.write_expr_list(node.right) + if can_wrap_rhs && !f.single_line_if && f.line_len > max_len { + right_str := f.out.after(right_start_pos) + if !right_str.contains('\n') { + f.out.go_back_to(right_start_pos) + f.line_len = right_start_len + if f.out.last() == ` ` { + f.out.go_back(1) + f.line_len-- + } + f.writeln('') + f.indent++ + f.write_expr_list(node.right) + f.indent-- } } if node.attr.name != '' { @@ -2157,8 +2179,14 @@ pub fn (mut f Fmt) call_args(args []ast.CallArg) { if arg.is_mut { f.write(arg.share.str() + ' ') } - if i > 0 && !f.single_line_if && !f.use_short_fn_args { - f.wrap_long_line(3, true) + if i > 0 && !f.single_line_if && !f.use_short_fn_args && arg.expr !is ast.StructInit { + arg_str := f.node_str(arg.expr) + tail_len := if i < args.len - 1 { 2 } else { 1 } + is_tiny_last_assign_arg := f.is_assign && i == args.len - 1 && arg_str.len <= 4 + if !is_tiny_last_assign_arg && !arg_str.contains('\n') + && f.line_len + arg_str.len + tail_len > max_len { + f.wrap_long_line(0, true) + } } f.expr(arg.expr) if post_comments.len > 0 { diff --git a/vlib/v/fmt/tests/long_assign_wrap_expected.vv b/vlib/v/fmt/tests/long_assign_wrap_expected.vv new file mode 100644 index 000000000..aab7c73a2 --- /dev/null +++ b/vlib/v/fmt/tests/long_assign_wrap_expected.vv @@ -0,0 +1,63 @@ +struct Tree { + child map[string]TreeNode +} + +struct TreeNode { + tree Tree +} + +struct EnumStmt { + name string +} + +struct BasicValueStmt { + value string +} + +enum NameCase { + snake_case +} + +enum NameKind { + other + field +} + +struct VAST {} + +fn (v VAST) extract_const_or_enum(tree Tree, enum_stmt EnumStmt, already_defined bool) EnumStmt { + return enum_stmt +} + +fn (v VAST) get_name(tree Tree, name_case NameCase, name_kind NameKind) string { + return '' +} + +fn (v VAST) get_type(tree Tree) string { + return '' +} + +fn main() { + tree := Tree{} + decl := TreeNode{} + field_name := TreeNode{} + field := TreeNode{} + mut v := VAST{} + enum_stmt := EnumStmt{} + mut values := map[string]BasicValueStmt{} + if true { + if true { + if true { + if true { + enum_stmt = v.extract_const_or_enum(decl.tree, enum_stmt, + enum_stmt.name.len > 0) + mut imp_name := + v.get_name(tree.child['Path'].tree, .snake_case, .other)#[1..-1].replace('/', '.') + values[v.get_name(field_name.tree, .snake_case, .field)] = + BasicValueStmt{v.get_type(field.tree)} + println(imp_name) + } + } + } + } +} diff --git a/vlib/v/fmt/tests/long_assign_wrap_input.vv b/vlib/v/fmt/tests/long_assign_wrap_input.vv new file mode 100644 index 000000000..db5d34e8c --- /dev/null +++ b/vlib/v/fmt/tests/long_assign_wrap_input.vv @@ -0,0 +1,60 @@ +struct Tree { + child map[string]TreeNode +} + +struct TreeNode { + tree Tree +} + +struct EnumStmt { + name string +} + +struct BasicValueStmt { + value string +} + +enum NameCase { + snake_case +} + +enum NameKind { + other + field +} + +struct VAST {} + +fn (v VAST) extract_const_or_enum(tree Tree, enum_stmt EnumStmt, already_defined bool) EnumStmt { + return enum_stmt +} + +fn (v VAST) get_name(tree Tree, name_case NameCase, name_kind NameKind) string { + return '' +} + +fn (v VAST) get_type(tree Tree) string { + return '' +} + +fn main() { + tree := Tree{} + decl := TreeNode{} + field_name := TreeNode{} + field := TreeNode{} + mut v := VAST{} + enum_stmt := EnumStmt{} + mut values := map[string]BasicValueStmt{} + if true { + if true { + if true { + if true { + enum_stmt = v.extract_const_or_enum(decl.tree, enum_stmt, enum_stmt.name.len > 0) + mut imp_name := v.get_name(tree.child['Path'].tree, .snake_case, .other)#[1..-1].replace('/', '.') + values[v.get_name(field_name.tree, .snake_case, .field)] = BasicValueStmt{v.get_type(field.tree)} + println(imp_name) + } + } + } + } +} -- 2.39.5