vxx2 / vlib / v / fmt / fmt.v
3697 lines · 3528 sloc · 89.68 KB · a41a13ac62cef64c319e42b075ad7305ed567cbc
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module fmt
5
6import os
7import strings
8import v.ast
9import v.util
10import v.pref
11
12const break_points = [0, 35, 60, 85, 93, 100]! // when to break a line depending on the penalty
13const max_len = break_points[break_points.len - 1]
14const bs = '\\'
15
16fn call_arg_spread_str(arg ast.CallArg) string {
17 return match arg.expr {
18 ast.ArrayDecompose {
19 decompose := arg.expr as ast.ArrayDecompose
20 '...${decompose.expr.str()}'
21 }
22 else {
23 arg.str()
24 }
25 }
26}
27
28@[minify]
29pub struct Fmt {
30pub:
31 pref &pref.Preferences = unsafe { nil }
32pub mut:
33 file ast.File
34 table &ast.Table = unsafe { nil }
35 is_debug bool
36 out strings.Builder
37 indent int
38 empty_line bool
39 line_len int // the current line length, Note: it counts \t as 4 spaces, and starts at 0 after f.writeln
40 buffering bool // disables line wrapping for exprs that will be analyzed later
41 par_level int // how many parentheses are put around the current expression
42 array_init_break []bool // line breaks after elements in hierarchy level of multi dimensional array
43 array_init_depth int // current level of hierarchy in array init
44 single_line_if bool
45 cur_mod string
46 import_pos int // position of the last import in the resulting string
47 mod2alias map[string]string // for `import time as t`, will contain: 'time'=>'t'
48 mod2syms map[string]string // import time { now } 'time.now'=>'now'
49 implied_import_str string // ​imports that the user's code uses but omitted to import explicitly
50 processed_imports []string
51 has_import_stmt bool
52 use_short_fn_args bool
53 single_line_fields bool // should struct fields be on a single line
54 in_lambda_depth int
55 inside_const bool
56 inside_unsafe bool
57 inside_comptime_if bool
58 is_assign bool
59 is_index_expr bool
60 is_mbranch_expr bool // match a { x...y { } }
61 is_struct_init bool
62 is_array_init bool
63 fn_scope &ast.Scope = unsafe { nil }
64 wsinfix_depth int
65 format_state FormatState
66 source_text string // can be set by `echo "println('hi')" | v fmt`, i.e. when processing source not from a file, but from stdin. In this case, it will contain the entire input text. You can use f.file.path otherwise, and read from that file.
67 global_processed_imports []string
68 branch_processed_imports []string
69 is_translated_module bool // @[translated]
70 is_c_function bool // C.func(...)
71}
72
73@[params]
74pub struct FmtOptions {
75pub:
76 source_text string
77}
78
79pub fn fmt(file ast.File, mut table ast.Table, pref_ &pref.Preferences, is_debug bool, options FmtOptions) string {
80 mut f := Fmt{
81 file: file
82 table: table
83 pref: pref_
84 is_debug: is_debug
85 out: strings.new_builder(1000)
86 }
87 f.source_text = options.source_text
88 f.process_file_imports(file)
89 // Compensate for indent increase of toplevel stmts done in `f.stmts()`.
90 f.indent--
91 f.stmts(file.stmts)
92 f.indent++
93 res := f.out.str().trim_space() + '\n'
94
95 // `implied_imports` should append to end of `import` block
96 if res.len == 1 {
97 return f.implied_import_str + '\n'
98 }
99 if res.len <= f.import_pos {
100 if f.implied_import_str.len > 0 {
101 return res + '\n' + f.implied_import_str + '\n'
102 }
103 return res
104 }
105 mut import_start_pos := f.import_pos
106 if f.import_pos == 0 && file.stmts.len > 1 {
107 // Check shebang.
108 stmt := file.stmts[1]
109 if stmt is ast.ExprStmt && stmt.expr is ast.Comment
110 && (stmt.expr as ast.Comment).text.starts_with('#!') {
111 import_start_pos = stmt.pos.len
112 }
113 }
114 if f.has_import_stmt || f.implied_import_str.len == 0 {
115 return res[..import_start_pos] + f.implied_import_str + res[import_start_pos..]
116 } else {
117 return res[..import_start_pos] + f.implied_import_str + '\n' + res[import_start_pos..]
118 }
119}
120
121/*
122// vfmt has a special type_to_str which calls Table.type_to_str, but does extra work.
123// Having it here and not in Table saves cpu cycles when not running the compiler in vfmt mode.
124pub fn (f &Fmt) type_to_str_using_aliases(typ ast.Type, import_aliases map[string]string) string {
125 mut s := f.type_to_str_using_aliases(typ, import_aliases)
126 if s.contains('Result') {
127 println('${s}')
128 }
129 return s
130}
131
132pub fn (f &Fmt) type_to_str(typ ast.Type) string {
133 return f.type_to_str(typ)
134}
135*/
136fn (f &Fmt) type_to_str_using_aliases(typ ast.Type, import_aliases map[string]string) string {
137 if f.table.new_int && typ == ast.int_type && (f.is_translated_module || f.is_c_function) {
138 return f.type_to_str_using_aliases(ast.i32_type, import_aliases)
139 }
140 return f.table.type_to_str_using_aliases(typ, import_aliases)
141}
142
143fn (f &Fmt) type_to_str(typ ast.Type) string {
144 if f.table.new_int && typ == ast.int_type && (f.is_translated_module || f.is_c_function) {
145 return 'i32'
146 }
147 return f.table.type_to_str(typ)
148}
149
150pub fn (mut f Fmt) process_file_imports(file &ast.File) {
151 mut sb := strings.new_builder(128)
152 for imp in file.implied_imports {
153 sb.writeln('import ${imp}')
154 }
155 f.implied_import_str = sb.str()
156
157 for imp in file.imports {
158 f.mod2alias[imp.mod] = imp.alias
159 f.mod2alias[imp.mod.all_after('${file.mod.name}.')] = imp.alias
160 for sym in imp.syms {
161 f.mod2alias['${imp.mod}.${sym.name}'] = sym.name
162 f.mod2alias['${imp.mod.all_after_last('.')}.${sym.name}'] = sym.name
163 f.mod2alias[sym.name] = sym.name
164 f.mod2syms['${imp.mod}.${sym.name}'] = sym.name
165 f.mod2syms['${imp.mod.all_after_last('.')}.${sym.name}'] = sym.name
166 f.mod2syms[sym.name] = sym.name
167 }
168 }
169}
170
171//=== Basic buffer write operations ===//
172
173pub fn (mut f Fmt) write(s string) {
174 if f.indent > 0 && f.empty_line {
175 f.write_indent()
176 }
177 f.out.write_string(s)
178 f.line_len += s.len
179 f.empty_line = false
180}
181
182pub fn (mut f Fmt) writeln(s string) {
183 if f.indent > 0 && f.empty_line && s != '' {
184 f.write_indent()
185 }
186 f.out.writeln(s)
187 f.empty_line = true
188 f.line_len = 0
189}
190
191fn (mut f Fmt) write_indent() {
192 f.out.write_string(util.tabs(f.indent))
193 f.line_len += f.indent * 4
194}
195
196pub fn (mut f Fmt) wrap_long_line(penalty_idx int, add_indent bool) bool {
197 if f.buffering {
198 return false
199 }
200 if penalty_idx > 0 && f.line_len <= break_points[penalty_idx] {
201 return false
202 }
203 if f.out.last() == ` ` {
204 f.out.go_back(1)
205 }
206 f.write('\n')
207 f.line_len = 0
208 if add_indent {
209 f.indent++
210 }
211 f.write_indent()
212 if add_indent {
213 f.indent--
214 }
215 return true
216}
217
218// When the removal action actually occurs, the string of the last line after the removal is returned
219pub fn (mut f Fmt) remove_new_line() string {
220 mut buffer := unsafe { &f.out }
221 mut i := 0
222 for i = buffer.len - 1; i >= 0; i-- {
223 if !buffer.byte_at(i).is_space() { // != `\n` {
224 break
225 }
226 }
227 if i == buffer.len - 1 {
228 return ''
229 }
230 buffer.go_back(buffer.len - i - 1)
231 f.empty_line = false
232 mut line_len := 0
233 mut last_line_str := []u8{}
234 for i = buffer.len - 1; i >= 0; i-- {
235 ch := buffer.byte_at(i)
236 if ch == `\n` {
237 break
238 }
239 line_len += if ch == `\t` { 4 } else { 1 }
240 last_line_str << ch
241 }
242 f.line_len = line_len
243 return last_line_str.reverse().bytestr()
244}
245
246//=== Specialized write methods ===//
247
248fn (mut f Fmt) write_language_prefix(lang ast.Language) {
249 match lang {
250 .c { f.write('C.') }
251 .js { f.write('JS.') }
252 .wasm { f.write('WASM.') }
253 else {}
254 }
255}
256
257fn (mut f Fmt) write_generic_types(gtypes []ast.Type) {
258 if gtypes.len > 0 {
259 f.write('[')
260 gtypes_string := gtypes.map(f.type_to_str(it)).join(', ')
261 f.write(gtypes_string)
262 f.write(']')
263 }
264}
265
266//=== Module handling helper methods ===//
267
268pub fn (mut f Fmt) set_current_module_name(cmodname string) {
269 f.cur_mod = cmodname
270 f.table.cmod_prefix = cmodname + '.'
271 // Drop type_to_str strings that were memoized before the module context was
272 // known (during parsing `cmod_prefix` is still empty), so that types of the
273 // current module are not left with a stale `mod.` prefix when a `fn` typed
274 // field signature is rebuilt later on (issue #27475).
275 f.table.invalidate_type_to_str_cache()
276}
277
278fn (f &Fmt) get_modname_prefix(mname string) (string, string) {
279 // ./tests/proto_module_importing_vproto_keep.vv to know, why here is checked for ']' and '&'
280 if !mname.contains(']') && !mname.contains('&') {
281 return mname, ''
282 }
283 after_rbc := mname.all_after_last(']')
284 after_ref := mname.all_after_last('&')
285 modname := if after_rbc.len < after_ref.len { after_rbc } else { after_ref }
286 return modname, mname.trim_string_right(modname)
287}
288
289fn (mut f Fmt) is_external_name(name string) bool {
290 if name.len > 2 && name[0] == `C` && name[1] == `.` {
291 return true
292 }
293 if name.len > 3 && name[0] == `J` && name[1] == `S` && name[2] == `.` {
294 return true
295 }
296 return false
297}
298
299pub fn (mut f Fmt) no_cur_mod(typename string) string {
300 return util.no_cur_mod(typename, f.cur_mod)
301}
302
303// foo.bar.fn() => bar.fn()
304pub fn (mut f Fmt) short_module(name string) string {
305 if !name.contains('.') || name.starts_with('JS.') {
306 return name
307 }
308 if name in f.mod2syms {
309 return f.mod2syms[name]
310 }
311 if name.ends_with(']') {
312 generic_levels := name.trim_string_right(']').split('[')
313 mut res := '${f.short_module(generic_levels[0])}'
314 for i in 1 .. generic_levels.len {
315 genshorts := generic_levels[i].split(', ').map(f.short_module(it)).join(', ')
316 res += '[${genshorts}'
317 }
318 res += ']'
319 return res
320 }
321 vals := name.split('.')
322 if vals.len < 2 {
323 return name
324 }
325 idx := vals.len - 1
326 mname, tprefix := f.get_modname_prefix(vals[..idx].join('.'))
327 symname := vals.last()
328 mut aname := f.mod2alias[mname]
329 if aname == '' {
330 for _, v in f.mod2alias {
331 if v == mname {
332 aname = mname
333 break
334 }
335 }
336 }
337 if aname == '' {
338 return '${tprefix}${symname}'
339 }
340 return '${tprefix}${aname}.${symname}'
341}
342
343//=== Import-related methods ===//
344
345pub fn (mut f Fmt) import_stmt(imp ast.Import) {
346 f.has_import_stmt = true
347 if imp.mod in f.file.auto_imports && imp.mod !in f.file.used_imports {
348 // Skip hidden imports like preludes.
349 return
350 }
351 imp_stmt := f.imp_stmt_str(imp)
352 if imp_stmt in f.global_processed_imports
353 || (f.inside_comptime_if && imp_stmt in f.branch_processed_imports) {
354 // Skip duplicates.
355 f.import_comments(imp.next_comments)
356 return
357 }
358 if f.inside_comptime_if {
359 f.branch_processed_imports << imp_stmt
360 } else {
361 f.global_processed_imports << imp_stmt
362 }
363 if !f.format_state.is_vfmt_on {
364 original_imp_line :=
365 f.get_source_lines()#[imp.pos.line_nr..imp.pos.last_line + 1].join('\n')
366 // Same line comments(`imp.comments`) are included in the `original_imp_line`.
367 f.writeln(original_imp_line)
368 f.import_comments(imp.next_comments)
369 } else {
370 f.writeln('import ${imp_stmt}')
371 f.import_comments(imp.comments, same_line: true)
372 f.import_comments(imp.next_comments)
373 }
374 f.import_pos = f.out.len
375}
376
377pub fn (f &Fmt) imp_stmt_str(imp ast.Import) string {
378 // Format / remove unused selective import symbols
379 // E.g.: `import foo { Foo }` || `import foo as f { Foo }`
380 has_alias := imp.alias != imp.source_name.all_after_last('.')
381 mut suffix := if has_alias { ' as ${imp.alias}' } else { '' }
382 mut syms := imp.syms.map(it.name).filter(f.file.imported_symbols_used[it])
383 syms.sort()
384 if syms.len > 0 {
385 suffix += if imp.syms[0].pos.line_nr == imp.pos.line_nr {
386 ' { ' + syms.join(', ') + ' }'
387 } else {
388 ' {\n\t' + syms.join(',\n\t') + ',\n}'
389 }
390 }
391 return '${imp.source_name}${suffix}'
392}
393
394//=== Node helpers ===//
395
396fn (f &Fmt) should_insert_newline_before_node(node ast.Node, prev_node ast.Node) bool {
397 // No need to insert a newline if there is already one
398 if f.out.last_n(2) == '\n\n' {
399 return false
400 }
401 prev_line_nr := prev_node.pos().last_line
402 // The nodes are Stmts
403 if node is ast.Stmt && prev_node is ast.Stmt {
404 match prev_node {
405 // Force a newline after a block of HashStmts
406 ast.HashStmt {
407 if node !in [ast.HashStmt, ast.ExprStmt] {
408 return true
409 }
410 }
411 // Force a newline after function declarations
412 // The only exception is inside a block of no_body functions
413 ast.FnDecl {
414 if node !is ast.FnDecl || !prev_node.no_body {
415 return true
416 }
417 }
418 ast.SemicolonStmt {
419 return false
420 }
421 // Force a newline after struct declarations
422 ast.StructDecl {
423 return true
424 }
425 // Empty line after a block of type declarations
426 ast.TypeDecl {
427 if node !is ast.TypeDecl {
428 return true
429 }
430 }
431 // Force a newline after imports
432 ast.Import {
433 return node !is ast.Import
434 }
435 ast.ConstDecl {
436 mut is_comment_expr_stmt := false
437 if node is ast.ExprStmt {
438 expr_stmt := node
439 is_comment_expr_stmt = expr_stmt.expr is ast.Comment
440 }
441 if node !is ast.ConstDecl && !is_comment_expr_stmt {
442 return true
443 }
444 }
445 else {}
446 }
447
448 match node {
449 // Attributes are not respected in the stmts position, so this requires manual checking
450 ast.StructDecl, ast.EnumDecl, ast.FnDecl {
451 if node.attrs.len > 0 && node.attrs[0].pos.line_nr - prev_line_nr <= 1 {
452 return false
453 }
454 }
455 ast.Import {
456 return false
457 }
458 else {}
459 }
460 }
461 // The node shouldn't have a newline before
462 if node.pos().line_nr - prev_line_nr <= 1 {
463 return false
464 }
465 return true
466}
467
468pub fn (mut f Fmt) node_str(node ast.Node) string {
469 was_empty_line := f.empty_line
470 prev_line_len := f.line_len
471 pos := f.out.len
472 match node {
473 ast.Stmt { f.stmt(node) }
474 ast.Expr { f.expr(node) }
475 else { panic('´f.node_str()´ is not implemented for ${node}.') }
476 }
477
478 str := f.out.after(pos)
479 f.out.go_back_to(pos)
480 f.empty_line = was_empty_line
481 f.line_len = prev_line_len
482 return str
483}
484
485//=== General Stmt-related methods and helpers ===//
486
487pub fn (mut f Fmt) stmts(stmts []ast.Stmt) {
488 mut prev_stmt := ast.empty_stmt
489 f.indent++
490 for i, stmt in stmts {
491 if i > 0 && f.should_insert_newline_before_node(stmt, prev_stmt) {
492 f.out.writeln('')
493 }
494 f.stmt(stmt)
495 prev_stmt = stmt
496 }
497 f.indent--
498}
499
500pub fn (mut f Fmt) stmt(node ast.Stmt) {
501 if f.is_debug {
502 eprintln('stmt ${node.type_name():-20} | pos: ${node.pos.line_str()}')
503 }
504 match node {
505 ast.EmptyStmt, ast.NodeError {}
506 ast.AsmStmt {
507 f.asm_stmt(node)
508 }
509 ast.AssertStmt {
510 f.assert_stmt(node)
511 }
512 ast.AssignStmt {
513 f.assign_stmt(node)
514 }
515 ast.Block {
516 if node.is_unsafe {
517 f.inside_unsafe = true
518 f.block(node)
519 f.inside_unsafe = false
520 } else {
521 f.block(node)
522 }
523 }
524 ast.BranchStmt {
525 f.branch_stmt(node)
526 }
527 ast.ComptimeFor {
528 f.comptime_for(node)
529 }
530 ast.ConstDecl {
531 f.const_decl(node)
532 }
533 ast.DebuggerStmt {
534 f.debugger_stmt(node)
535 }
536 ast.DeferStmt {
537 f.defer_stmt(node)
538 }
539 ast.EnumDecl {
540 f.enum_decl(node)
541 }
542 ast.ExprStmt {
543 f.expr_stmt(node)
544 }
545 ast.FnDecl {
546 f.fn_decl(node)
547 }
548 ast.ForCStmt {
549 f.for_c_stmt(node)
550 }
551 ast.ForInStmt {
552 f.for_in_stmt(node)
553 }
554 ast.ForStmt {
555 f.for_stmt(node)
556 }
557 ast.GlobalDecl {
558 f.global_decl(node)
559 }
560 ast.GotoLabel {
561 f.goto_label(node)
562 }
563 ast.GotoStmt {
564 f.goto_stmt(node)
565 }
566 ast.HashStmt {
567 f.hash_stmt(node)
568 }
569 ast.Import {
570 f.import_stmt(node)
571 }
572 ast.InterfaceDecl {
573 f.interface_decl(node)
574 }
575 ast.Module {
576 f.module_stmt(node)
577 }
578 ast.Return {
579 f.return_stmt(node)
580 }
581 ast.SemicolonStmt {}
582 ast.SqlStmt {
583 f.sql_stmt(node)
584 }
585 ast.StructDecl {
586 f.struct_decl(node, false)
587 }
588 ast.TypeDecl {
589 f.type_decl(node)
590 }
591 }
592}
593
594fn stmt_is_single_line(stmt ast.Stmt) bool {
595 return match stmt {
596 ast.ExprStmt, ast.AssertStmt { expr_is_single_line(stmt.expr) }
597 ast.Return, ast.AssignStmt, ast.BranchStmt { true }
598 ast.SemicolonStmt { true }
599 else { false }
600 }
601}
602
603//=== General Expr-related methods and helpers ===//
604
605pub fn (mut f Fmt) expr(node_ ast.Expr) {
606 mut node := unsafe { node_ }
607 if f.is_debug {
608 eprintln('expr ${node.type_name():-20} | pos: ${node.pos().line_str()} | ${node.str()}')
609 }
610 match mut node {
611 ast.NodeError {}
612 ast.EmptyExpr {}
613 ast.AnonFn {
614 f.anon_fn(node)
615 }
616 ast.ArrayDecompose {
617 f.array_decompose(node)
618 }
619 ast.ArrayInit {
620 f.array_init(node)
621 }
622 ast.AsCast {
623 f.as_cast(node)
624 }
625 ast.Assoc {
626 f.assoc(node)
627 }
628 ast.AtExpr {
629 f.at_expr(node)
630 }
631 ast.BoolLiteral {
632 f.write(node.val.str())
633 }
634 ast.CallExpr {
635 f.call_expr(node)
636 }
637 ast.CastExpr {
638 f.cast_expr(node)
639 }
640 ast.ChanInit {
641 f.chan_init(mut node)
642 }
643 ast.CharLiteral {
644 f.char_literal(node)
645 }
646 ast.Comment {
647 f.comment(node, same_line: true)
648 }
649 ast.ComptimeCall {
650 f.comptime_call(node)
651 }
652 ast.ComptimeSelector {
653 f.comptime_selector(node)
654 }
655 ast.ConcatExpr {
656 f.concat_expr(node)
657 }
658 ast.CTempVar {
659 eprintln('ast.CTempVar of ${node.orig.str()} should be generated/used only in cgen')
660 }
661 ast.DumpExpr {
662 f.dump_expr(node)
663 }
664 ast.EnumVal {
665 f.enum_val(node)
666 }
667 ast.FloatLiteral {
668 f.write(node.val)
669 if node.val.ends_with('.') {
670 f.write('0')
671 }
672 }
673 ast.GoExpr {
674 f.go_expr(node)
675 }
676 ast.SpawnExpr {
677 f.spawn_expr(node)
678 }
679 ast.Ident {
680 f.ident(node)
681 }
682 ast.IfExpr {
683 f.if_expr(node)
684 }
685 ast.IfGuardExpr {
686 f.if_guard_expr(node)
687 }
688 ast.IndexExpr {
689 f.index_expr(node)
690 }
691 ast.InfixExpr {
692 f.infix_expr(node)
693 }
694 ast.IntegerLiteral {
695 f.write(node.val)
696 }
697 ast.LambdaExpr {
698 f.write('|')
699 for i, x in node.params {
700 f.expr(x)
701 if i < node.params.len - 1 {
702 f.write(', ')
703 }
704 }
705 f.write('| ')
706 f.expr(node.expr)
707 }
708 ast.Likely {
709 f.likely(node)
710 }
711 ast.LockExpr {
712 f.lock_expr(node)
713 }
714 ast.MapInit {
715 f.map_init(node)
716 }
717 ast.MatchExpr {
718 f.match_expr(node)
719 }
720 ast.None {
721 f.write('none')
722 }
723 ast.Nil {
724 f.write('nil')
725 }
726 ast.OffsetOf {
727 f.offset_of(node)
728 }
729 ast.OrExpr {
730 // shouldn't happen, an or expression is always linked to a call expr or index expr
731 panic('fmt: OrExpr should be linked to ast.CallExpr or ast.IndexExpr')
732 }
733 ast.ParExpr {
734 f.par_expr(node)
735 }
736 ast.PostfixExpr {
737 f.postfix_expr(node)
738 }
739 ast.PrefixExpr {
740 f.prefix_expr(node)
741 }
742 ast.RangeExpr {
743 f.range_expr(node)
744 }
745 ast.SelectExpr {
746 f.select_expr(node)
747 }
748 ast.SelectorExpr {
749 f.selector_expr(node)
750 }
751 ast.SizeOf {
752 f.size_of(node)
753 }
754 ast.IsRefType {
755 f.is_ref_type(node)
756 }
757 ast.SqlExpr {
758 f.sql_expr(node)
759 }
760 ast.SqlQueryDataExpr {
761 f.sql_query_data_expr(node)
762 }
763 ast.StringLiteral {
764 f.string_literal(node)
765 }
766 ast.StringInterLiteral {
767 f.string_inter_literal(node)
768 }
769 ast.StructInit {
770 f.struct_init(node)
771 }
772 ast.TypeNode {
773 f.type_expr(node)
774 }
775 ast.TypeOf {
776 f.type_of(node)
777 }
778 ast.UnsafeExpr {
779 f.inside_unsafe = true
780 f.unsafe_expr(node)
781 f.inside_unsafe = false
782 }
783 ast.ComptimeType {
784 match node.kind {
785 .unknown { f.write('\$unknown') }
786 .array { f.write('\$array') }
787 .array_dynamic { f.write('\$array_dynamic') }
788 .array_fixed { f.write('\$array_fixed') }
789 .struct { f.write('\$struct') }
790 .iface { f.write('\$interface') }
791 .map { f.write('\$map') }
792 .int { f.write('\$int') }
793 .float { f.write('\$float') }
794 .sum_type { f.write('\$sumtype') }
795 .enum { f.write('\$enum') }
796 .alias { f.write('\$alias') }
797 .function { f.write('\$function') }
798 .option { f.write('\$option') }
799 .shared { f.write('\$shared') }
800 .string { f.write('\$string') }
801 .pointer { f.write('\$pointer') }
802 .voidptr { f.write('\$voidptr') }
803 }
804 }
805 }
806}
807
808fn expr_is_single_line(expr ast.Expr) bool {
809 match expr {
810 ast.Comment, ast.IfExpr, ast.MapInit, ast.MatchExpr, ast.SqlQueryDataExpr {
811 return false
812 }
813 ast.AnonFn {
814 if !expr.decl.no_body {
815 return false
816 }
817 }
818 ast.StructInit {
819 if !expr.no_keys && (expr.init_fields.len > 0 || expr.pre_comments.len > 0) {
820 return false
821 }
822 }
823 ast.CallExpr {
824 if expr.or_block.stmts.len > 1 || expr.args.any(it.expr is ast.CallExpr
825 && it.expr.or_block.stmts.len > 1) {
826 return false
827 }
828 }
829 ast.ArrayInit {
830 for e in expr.exprs {
831 if !expr_is_single_line(e) {
832 return false
833 }
834 }
835 }
836 ast.ConcatExpr {
837 for e in expr.vals {
838 if !expr_is_single_line(e) {
839 return false
840 }
841 }
842 }
843 ast.StringLiteral {
844 return expr.pos.line_nr == expr.pos.last_line
845 }
846 ast.OrExpr {
847 if expr.stmts.len == 1 && stmt_is_single_line(expr.stmts[0]) {
848 stmt := expr.stmts[0]
849 if stmt is ast.ExprStmt && stmt.expr is ast.CallExpr
850 && (stmt.expr as ast.CallExpr).comments.len > 0 {
851 if comment := (stmt.expr as ast.CallExpr).comments[0] {
852 if !comment.is_multi {
853 return false
854 }
855 }
856 }
857 return true
858 }
859 return false
860 }
861 else {}
862 }
863
864 return true
865}
866
867fn (mut f Fmt) write_expr_list(exprs []ast.Expr) {
868 for i, expr in exprs {
869 f.expr(expr)
870 if i < exprs.len - 1 {
871 f.write(', ')
872 }
873 }
874}
875
876//=== Specific Stmt methods ===//
877
878pub fn (mut f Fmt) assert_stmt(node ast.AssertStmt) {
879 f.write('assert ')
880 mut expr := node.expr
881 expr = expr.remove_par()
882 f.expr(expr)
883 if node.extra !is ast.EmptyExpr {
884 f.write(', ')
885 f.expr(node.extra)
886 }
887 f.writeln('')
888}
889
890pub fn (mut f Fmt) assign_stmt(node ast.AssignStmt) {
891 for i, left in node.left {
892 f.expr(left)
893 if i < node.left.len - 1 {
894 f.write(', ')
895 }
896 }
897 f.is_assign = true
898 f.write(' ${node.op.str()} ')
899 right_start_pos := f.out.len
900 right_start_len := f.line_len
901 can_wrap_rhs := node.right.len == 1 && node.right[0] in [ast.CallExpr, ast.StructInit]
902 f.write_expr_list(node.right)
903 if can_wrap_rhs && !f.single_line_if && f.line_len > max_len {
904 right_str := f.out.after(right_start_pos)
905 if !right_str.contains('\n') {
906 f.out.go_back_to(right_start_pos)
907 f.line_len = right_start_len
908 if f.out.last() == ` ` {
909 f.out.go_back(1)
910 f.line_len--
911 }
912 f.writeln('')
913 f.indent++
914 f.write_expr_list(node.right)
915 f.indent--
916 }
917 }
918 if node.attr.name != '' {
919 f.write(' @[${node.attr.name}]')
920 }
921 f.comments(node.end_comments, has_nl: false, same_line: true, level: .keep)
922 if !f.single_line_if {
923 f.writeln('')
924 }
925 f.is_assign = false
926}
927
928pub fn (mut f Fmt) block(node ast.Block) {
929 if node.is_unsafe {
930 f.write('unsafe ')
931 }
932 f.write('{')
933 if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line {
934 f.writeln('')
935 f.stmts(node.stmts)
936 }
937 f.writeln('}')
938}
939
940pub fn (mut f Fmt) debugger_stmt(node ast.DebuggerStmt) {
941 f.writeln('\$dbg;')
942}
943
944pub fn (mut f Fmt) branch_stmt(node ast.BranchStmt) {
945 f.writeln(node.str())
946}
947
948pub fn (mut f Fmt) comptime_for(node ast.ComptimeFor) {
949 f.write('\$for ${node.val_var} in ')
950 if node.typ != ast.void_type {
951 f.write(f.no_cur_mod(f.type_to_str_using_aliases(node.typ, f.mod2alias)))
952 } else {
953 f.expr(node.expr)
954 }
955 f.write('.${node.kind.str()} {')
956 if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line {
957 f.writeln('')
958 f.stmts(node.stmts)
959 }
960 f.writeln('}')
961}
962
963pub fn (mut f Fmt) const_decl(node ast.ConstDecl) {
964 if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line {
965 // remove "const()"
966 return
967 }
968
969 f.attrs(node.attrs)
970 if !node.is_block {
971 if node.is_pub {
972 f.write('pub ')
973 }
974 }
975 f.inside_const = true
976 defer { f.inside_const = false }
977 if !node.is_block {
978 f.write('const ')
979 }
980 mut prev_field := if node.fields.len > 0 {
981 ast.Node(node.fields[0])
982 } else {
983 ast.Node(ast.NodeError{})
984 }
985 for fidx, field in node.fields {
986 if field.comments.len > 0 {
987 if f.should_insert_newline_before_node(ast.Expr(field.comments[0]), prev_field) {
988 f.writeln('')
989 }
990 f.comments(field.comments, same_line: true)
991 prev_field = ast.Expr(field.comments.last())
992 }
993 if node.is_block && f.should_insert_newline_before_node(field, prev_field) {
994 f.writeln('')
995 }
996 name := field.name.after('.')
997 if node.is_block {
998 // const() blocks are deprecated, prepend "const" before each value
999 if node.is_pub {
1000 f.write('pub ')
1001 }
1002 f.write('const ')
1003 }
1004 if field.is_virtual_c {
1005 f.write('C.')
1006 }
1007 f.write('${name} ')
1008 if field.is_virtual_c {
1009 // f.typ(field.typ)
1010 f.write(f.type_to_str(field.typ))
1011 } else {
1012 f.write('= ')
1013 f.expr(field.expr)
1014 }
1015 f.comments(field.end_comments, same_line: true)
1016 if node.is_block && fidx < node.fields.len - 1 && node.fields.len > 1 {
1017 // old style grouped consts, converted to the new style ungrouped const
1018 f.writeln('')
1019 } else if node.end_comments.len > 0 {
1020 // Write out single line comments after const expr if present
1021 // E.g.: `const x = 1 // <comment>`
1022 if node.end_comments[0].text.contains('\n') {
1023 f.writeln('\n')
1024 }
1025 f.comments(node.end_comments, same_line: true, has_nl: false)
1026 }
1027 prev_field = field
1028 }
1029
1030 f.writeln('')
1031}
1032
1033fn (mut f Fmt) defer_stmt(node ast.DeferStmt) {
1034 f.write('defer')
1035 if node.mode == .function {
1036 f.write('(fn)')
1037 }
1038 if node.stmts.len == 0 {
1039 f.writeln(' {}')
1040 } else if node.stmts.len == 1 && node.pos.line_nr == node.pos.last_line
1041 && stmt_is_single_line(node.stmts[0]) {
1042 f.write(' { ')
1043 // the control stmts (return/break/continue...) print a newline inside them,
1044 // so, since this'll all be on one line, trim any possible whitespace
1045 str := f.node_str(node.stmts[0]).trim_space()
1046 // single_line := ' defer { ${str} }'
1047 // if single_line.len + f.line_len <= fmt.max_len {
1048 // f.write(single_line)
1049 // return
1050 //}
1051 f.write(str)
1052
1053 // f.stmt(node.stmts[0])
1054 f.writeln(' }')
1055 } else {
1056 f.writeln(' {')
1057 f.stmts(node.stmts)
1058 f.writeln('}')
1059 }
1060}
1061
1062pub fn (mut f Fmt) expr_stmt(node ast.ExprStmt) {
1063 f.comments(node.comments)
1064 f.expr(node.expr)
1065 if !f.single_line_if {
1066 f.writeln('')
1067 }
1068}
1069
1070pub fn (mut f Fmt) enum_decl(node ast.EnumDecl) {
1071 f.attrs(node.attrs)
1072 if node.is_pub {
1073 f.write('pub ')
1074 }
1075 mut name := node.name.after('.')
1076 if node.typ != ast.int_type && node.typ != ast.invalid_type {
1077 senum_type := f.type_to_str_using_aliases(node.typ, f.mod2alias)
1078 name += ' as ${senum_type}'
1079 }
1080 if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line {
1081 f.writeln('enum ${name} {}\n')
1082 return
1083 }
1084 f.writeln('enum ${name} {')
1085 f.comments(node.comments, same_line: true, level: .indent)
1086
1087 mut value_align := new_field_align(use_break_line: true)
1088 mut attr_align := new_field_align(use_threshold: true)
1089 mut comment_align := new_field_align(use_threshold: true)
1090 for field in node.fields {
1091 if field.has_expr {
1092 value_align.add_info(field.name.len, field.pos.line_nr, field.has_break_line)
1093 }
1094 attrs_len := inline_attrs_len(field.attrs)
1095 if field.attrs.len > 0 {
1096 if field.has_expr {
1097 attr_align.add_info(field.expr.str().len + 2, field.pos.line_nr,
1098 field.has_break_line)
1099 } else {
1100 attr_align.add_info(field.name.len, field.pos.line_nr, field.has_break_line)
1101 }
1102 }
1103 if field.comments.len > 0 {
1104 if field.attrs.len > 0 {
1105 comment_align.add_info(attrs_len, field.pos.line_nr, field.has_break_line)
1106 } else if field.has_expr {
1107 comment_align.add_info(field.expr.str().len + 2, field.pos.line_nr,
1108 field.has_break_line)
1109 } else {
1110 comment_align.add_info(field.name.len, field.pos.line_nr, field.has_break_line)
1111 }
1112 }
1113 }
1114
1115 for i, field in node.fields {
1116 if i > 0 && field.has_prev_newline {
1117 f.writeln('')
1118 }
1119 if field.pre_comments.len > 0 {
1120 f.comments(field.pre_comments, has_nl: true, level: .indent)
1121 }
1122 f.write('\t${field.name}')
1123 if field.has_expr {
1124 f.write(' '.repeat(value_align.max_len(field.pos.line_nr) - field.name.len))
1125 f.write(' = ')
1126 f.expr(field.expr)
1127 }
1128 attrs_len := inline_attrs_len(field.attrs)
1129 if field.attrs.len > 0 {
1130 if field.has_expr {
1131 f.write(' '.repeat(attr_align.max_len(field.pos.line_nr) - field.expr.str().len - 1))
1132 } else {
1133 f.write(' '.repeat(attr_align.max_len(field.pos.line_nr) - field.name.len + 1))
1134 }
1135 f.single_line_attrs(field.attrs, same_line: true)
1136 }
1137 // f.comments(field.comments, same_line: true, has_nl: false, level: .indent)
1138 if field.comments.len > 0 {
1139 if field.attrs.len > 0 {
1140 f.write(' '.repeat(comment_align.max_len(field.pos.line_nr) - attrs_len + 1))
1141 } else if field.has_expr {
1142 f.write(' '.repeat(comment_align.max_len(field.pos.line_nr) - field.expr.str().len -
1143 1))
1144 } else {
1145 f.write(' '.repeat(comment_align.max_len(field.pos.line_nr) - field.name.len + 1))
1146 }
1147 f.comments(field.comments, same_line: true, has_nl: false)
1148 }
1149 f.writeln('')
1150 f.comments(field.next_comments, has_nl: true, level: .indent)
1151 }
1152 f.writeln('}')
1153}
1154
1155pub fn (mut f Fmt) fn_decl(node ast.FnDecl) {
1156 f.attrs(node.attrs)
1157 if node.name.starts_with('C.') {
1158 f.is_c_function = true
1159 }
1160 f.table.new_int_fmt_fix = f.table.new_int && (f.is_translated_module || f.is_c_function)
1161 f.write(f.table.stringify_fn_decl(&node, f.cur_mod, f.mod2alias, true))
1162 f.table.new_int_fmt_fix = false
1163 f.is_c_function = false
1164 // Handle trailing comments after fn header declarations
1165 if node.no_body && node.end_comments.len > 0 {
1166 first_comment := node.end_comments[0]
1167 if first_comment.text.contains('\n') {
1168 f.writeln('\n')
1169 } else {
1170 f.write(' ')
1171 }
1172 f.comment(first_comment)
1173 if node.end_comments.len > 1 {
1174 f.writeln('\n')
1175 comments := node.end_comments[1..]
1176 for i, comment in comments {
1177 f.comment(comment)
1178 if i != comments.len - 1 {
1179 f.writeln('\n')
1180 }
1181 }
1182 }
1183 }
1184 f.fn_body(node)
1185}
1186
1187pub fn (mut f Fmt) anon_fn(node ast.AnonFn) {
1188 f.table.new_int_fmt_fix = f.table.new_int && (f.is_translated_module || f.is_c_function)
1189 f.write(f.table.stringify_anon_decl(&node, f.cur_mod, f.mod2alias)) // `Expr` instead of `ast.Expr` in mod ast
1190 f.table.new_int_fmt_fix = false
1191 f.fn_body(node.decl)
1192}
1193
1194fn (mut f Fmt) fn_body(node ast.FnDecl) {
1195 prev_fn_scope := f.fn_scope
1196 f.fn_scope = node.scope
1197 defer { f.fn_scope = prev_fn_scope }
1198 if node.language == .v || (node.is_method && node.language == .js) {
1199 if !node.no_body {
1200 f.write(' {')
1201 pre_comments := node.comments.filter(it.pos.pos < node.name_pos.pos)
1202 body_comments := node.comments[pre_comments.len..]
1203 f.comments(body_comments, same_line: true)
1204 if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line {
1205 if body_comments.len == 0 {
1206 f.writeln('')
1207 }
1208 f.stmts(node.stmts)
1209 }
1210 f.write('}')
1211 if node.end_comments.len > 0 {
1212 first_comment := node.end_comments[0]
1213 if first_comment.text.contains('\n') {
1214 f.writeln('\n')
1215 } else {
1216 f.write(' ')
1217 }
1218 f.comment(first_comment)
1219 if node.end_comments.len > 1 {
1220 f.writeln('\n')
1221 comments := node.end_comments[1..]
1222 for i, comment in comments {
1223 f.comment(comment)
1224 if i != comments.len - 1 {
1225 f.writeln('\n')
1226 }
1227 }
1228 }
1229 }
1230 }
1231 if !node.is_anon {
1232 f.writeln('')
1233 }
1234 } else {
1235 f.writeln('')
1236 }
1237}
1238
1239pub fn (mut f Fmt) for_c_stmt(node ast.ForCStmt) {
1240 if node.label.len > 0 {
1241 f.write('${node.label}: ')
1242 }
1243 init_comments := node.comments.filter(it.pos.pos < node.init.pos.pos)
1244 cond_comments := node.comments[init_comments.len..].filter(it.pos.pos < node.cond.pos().pos)
1245 inc_comments :=
1246 node.comments[(init_comments.len + cond_comments.len)..].filter(it.pos.pos < node.inc.pos.pos)
1247 after_inc_comments := node.comments[(init_comments.len + cond_comments.len + inc_comments.len)..]
1248 f.write('for ')
1249 if node.has_init {
1250 if init_comments.len > 0 {
1251 f.comments(init_comments)
1252 f.write(' ')
1253 }
1254 f.single_line_if = true // to keep all for ;; exprs on the same line
1255 f.stmt(node.init)
1256 f.single_line_if = false
1257 }
1258 f.write('; ')
1259 if cond_comments.len > 0 {
1260 f.comments(cond_comments)
1261 f.write(' ')
1262 }
1263 f.expr(node.cond)
1264 f.write('; ')
1265 if inc_comments.len > 0 {
1266 f.comments(inc_comments)
1267 f.write(' ')
1268 }
1269 f.stmt(node.inc)
1270 f.remove_new_line()
1271 if after_inc_comments.len > 0 {
1272 f.comments(after_inc_comments)
1273 }
1274 if f.out.len > 1 && !f.out.last_n(1)[0].is_space() {
1275 f.write(' ')
1276 }
1277 f.write('{')
1278 if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line {
1279 f.writeln('')
1280 f.stmts(node.stmts)
1281 }
1282 f.writeln('}')
1283}
1284
1285pub fn (mut f Fmt) for_in_stmt(node ast.ForInStmt) {
1286 if node.label.len > 0 {
1287 f.write('${node.label}: ')
1288 }
1289 kv_comments := node.comments.filter(it.pos.pos < node.kv_pos.pos)
1290 cond_comments := node.comments[kv_comments.len..].filter(it.pos.pos < node.cond.pos().pos)
1291 after_comments := node.comments[(kv_comments.len + cond_comments.len)..]
1292 f.write('for ')
1293 if kv_comments.len > 0 {
1294 f.comments(kv_comments)
1295 f.write(' ')
1296 }
1297 if node.key_var != '' {
1298 f.write(node.key_var)
1299 }
1300 if node.val_var != '' {
1301 if node.key_var != '' {
1302 f.write(', ')
1303 }
1304 if node.val_is_mut {
1305 f.write('mut ')
1306 }
1307 f.write(node.val_var)
1308 }
1309 f.write(' in ')
1310 if cond_comments.len > 0 {
1311 f.comments(cond_comments)
1312 f.write(' ')
1313 }
1314 f.expr(node.cond)
1315 if node.is_range {
1316 f.write(' .. ')
1317 f.expr(node.high)
1318 }
1319 if after_comments.len > 0 {
1320 f.comments(after_comments)
1321 }
1322 if f.out.len > 1 && !f.out.last_n(1)[0].is_space() {
1323 f.write(' ')
1324 }
1325 f.write('{')
1326 if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line {
1327 f.writeln('')
1328 f.stmts(node.stmts)
1329 }
1330 f.writeln('}')
1331}
1332
1333pub fn (mut f Fmt) for_stmt(node ast.ForStmt) {
1334 if node.label.len > 0 {
1335 f.write('${node.label}: ')
1336 }
1337 f.write('for ')
1338 if node.comments.len > 0 {
1339 f.comments(node.comments)
1340 }
1341 f.expr(node.cond)
1342 if f.out.len > 1 && !f.out.last_n(1)[0].is_space() {
1343 f.write(' ')
1344 }
1345 f.write('{')
1346 if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line {
1347 f.writeln('')
1348 f.stmts(node.stmts)
1349 }
1350 f.writeln('}')
1351}
1352
1353pub fn (mut f Fmt) global_decl(node ast.GlobalDecl) {
1354 f.attrs(node.attrs)
1355 if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line {
1356 // remove "__global()"
1357 return
1358 }
1359 f.write('__global ')
1360 mut max := 0
1361 if node.is_block {
1362 f.writeln('(')
1363 f.indent++
1364 for field in node.fields {
1365 if field.name.len > max {
1366 max = field.name.len
1367 }
1368 }
1369 }
1370 for field in node.fields {
1371 f.comments(field.comments, same_line: true)
1372 if field.is_const {
1373 f.write('const ')
1374 }
1375 if field.is_volatile {
1376 f.write('volatile ')
1377 }
1378 f.write('${field.name} ')
1379 f.write(' '.repeat(max - field.name.len))
1380 if field.has_expr {
1381 f.write('= ')
1382 f.expr(field.expr)
1383 } else {
1384 f.write('${f.type_to_str_using_aliases(field.typ, f.mod2alias)}')
1385 }
1386 if node.is_block {
1387 f.writeln('')
1388 }
1389 }
1390 f.comments_after_last_field(node.end_comments)
1391 if node.is_block {
1392 f.indent--
1393 f.writeln(')')
1394 } else {
1395 f.writeln('')
1396 }
1397}
1398
1399pub fn (mut f Fmt) spawn_expr(node ast.SpawnExpr) {
1400 f.write('spawn ')
1401 f.call_expr(node.call_expr)
1402}
1403
1404pub fn (mut f Fmt) go_expr(node ast.GoExpr) {
1405 f.write('go ')
1406 f.call_expr(node.call_expr)
1407}
1408
1409pub fn (mut f Fmt) goto_label(node ast.GotoLabel) {
1410 f.writeln('${node.name}:')
1411}
1412
1413pub fn (mut f Fmt) goto_stmt(node ast.GotoStmt) {
1414 f.writeln('goto ${node.name}')
1415}
1416
1417pub fn (mut f Fmt) hash_stmt(node ast.HashStmt) {
1418 f.attrs(node.attrs)
1419 f.writeln('#${node.val}')
1420}
1421
1422pub fn (mut f Fmt) interface_decl(node ast.InterfaceDecl) {
1423 f.attrs(node.attrs)
1424 if node.is_pub {
1425 f.write('pub ')
1426 }
1427 f.write('interface ')
1428 f.write_language_prefix(node.language)
1429 name := node.name.after('.') // strip prepended module
1430 f.write(name)
1431 f.write_generic_types(node.generic_types)
1432 f.write(' {')
1433 if node.fields.len > 0 || node.methods.len > 0 || node.pos.line_nr < node.pos.last_line {
1434 f.writeln('')
1435 }
1436 f.comments_before_field(node.pre_comments)
1437 for embed in node.embeds {
1438 f.write('\t${embed.name}')
1439 f.comments(embed.comments, same_line: true, has_nl: false, level: .indent)
1440 f.writeln('')
1441 }
1442 immut_fields := if node.mut_pos < 0 { node.fields } else { node.fields[..node.mut_pos] }
1443 mut_fields := if node.mut_pos < 0 { []ast.StructField{} } else { node.fields[node.mut_pos..] }
1444
1445 mut immut_methods := node.methods.clone()
1446 mut mut_methods := []ast.FnDecl{}
1447 for i, method in node.methods {
1448 if method.params[0].is_mut {
1449 immut_methods = node.methods[..i].clone()
1450 mut_methods = node.methods[i..].clone()
1451 break
1452 }
1453 }
1454
1455 mut type_align := new_field_align(use_break_line: true)
1456 mut comment_align := new_field_align(use_threshold: true)
1457 mut default_expr_align := new_field_align(use_threshold: true)
1458 mut attr_align := new_field_align(use_threshold: true)
1459 mut field_types := []string{cap: node.fields.len}
1460
1461 // Calculate the alignments first
1462 f.calculate_alignment(node.fields, mut type_align, mut comment_align, mut default_expr_align, mut
1463 attr_align, mut field_types)
1464
1465 mut method_comment_align := new_field_align(use_threshold: true)
1466 for method in node.methods {
1467 end_comments := method.comments.filter(it.pos.pos > method.pos.pos)
1468 if end_comments.len > 0 {
1469 f.table.new_int_fmt_fix = f.table.new_int && (f.is_translated_module || f.is_c_function)
1470 method_str :=
1471 f.table.stringify_fn_decl(&method, f.cur_mod, f.mod2alias, false).all_after_first('fn ')
1472 f.table.new_int_fmt_fix = false
1473 method_comment_align.add_info(method_str.len, method.pos.line_nr, method.has_break_line)
1474 }
1475 }
1476
1477 // TODO: alignment, comments, etc.
1478 for field in immut_fields {
1479 if field.has_prev_newline {
1480 f.writeln('')
1481 }
1482 f.interface_field(field, mut type_align, mut comment_align)
1483 }
1484 for method in immut_methods {
1485 if method.has_prev_newline {
1486 f.writeln('')
1487 }
1488 f.interface_method(method, mut method_comment_align)
1489 }
1490 if mut_fields.len + mut_methods.len > 0 {
1491 f.writeln('mut:')
1492 for field in mut_fields {
1493 if field.has_prev_newline {
1494 f.writeln('')
1495 }
1496 f.interface_field(field, mut type_align, mut comment_align)
1497 }
1498 for method in mut_methods {
1499 if method.has_prev_newline {
1500 f.writeln('')
1501 }
1502 f.interface_method(method, mut method_comment_align)
1503 }
1504 }
1505 f.writeln('}\n')
1506}
1507
1508enum AlignState {
1509 plain
1510 has_attributes
1511 has_default_expression
1512 has_everything
1513}
1514
1515pub fn (mut f Fmt) calculate_alignment(fields []ast.StructField, mut type_align FieldAlign, mut comment_align FieldAlign,
1516 mut default_expr_align FieldAlign, mut attr_align FieldAlign, mut field_types []string) {
1517 // Calculate the alignments first
1518 mut prev_state := AlignState.plain
1519 for field in fields {
1520 ft := f.no_cur_mod(f.type_to_str_using_aliases(field.typ, f.mod2alias))
1521 // Handle anon structs recursively
1522 field_types << ft
1523 attrs_len := inline_attrs_len(field.attrs)
1524 end_pos := field.pos.pos + field.pos.len
1525 type_align.add_info(field.name.len, field.pos.line_nr, field.has_break_line)
1526 if field.has_default_expr {
1527 default_expr_align.add_info(ft.len, field.pos.line_nr, field.has_break_line)
1528 }
1529 if field.attrs.len > 0 {
1530 attr_align.add_info(ft.len, field.pos.line_nr, field.has_break_line)
1531 }
1532 for comment in field.comments {
1533 if comment.pos.pos >= end_pos {
1534 if comment.pos.line_nr == field.pos.line_nr {
1535 if field.attrs.len > 0 {
1536 if prev_state != AlignState.has_attributes {
1537 comment_align.add_new_info(attrs_len, comment.pos.line_nr)
1538 } else {
1539 comment_align.add_info(attrs_len, comment.pos.line_nr,
1540 field.has_break_line)
1541 }
1542 prev_state = AlignState.has_attributes
1543 } else if field.has_default_expr {
1544 if prev_state != AlignState.has_default_expression {
1545 comment_align.add_new_info(field.default_expr.str().len + 2,
1546 comment.pos.line_nr)
1547 } else {
1548 comment_align.add_info(field.default_expr.str().len + 2,
1549 comment.pos.line_nr, field.has_break_line)
1550 }
1551 prev_state = AlignState.has_default_expression
1552 } else {
1553 if prev_state != AlignState.has_everything {
1554 comment_align.add_new_info(ft.len, comment.pos.line_nr)
1555 } else {
1556 comment_align.add_info(ft.len, comment.pos.line_nr,
1557 field.has_break_line)
1558 }
1559 prev_state = AlignState.has_everything
1560 }
1561 }
1562 continue
1563 }
1564 }
1565 }
1566}
1567
1568pub fn (mut f Fmt) interface_field(field ast.StructField, mut type_align FieldAlign, mut comment_align FieldAlign) {
1569 ft := f.no_cur_mod(f.type_to_str_using_aliases(field.typ, f.mod2alias))
1570 mut pre_cmts, mut end_cmts, mut next_line_cmts := []ast.Comment{}, []ast.Comment{}, []ast.Comment{}
1571 for cmt in field.comments {
1572 match true {
1573 cmt.pos.pos < field.pos.pos { pre_cmts << cmt }
1574 cmt.pos.line_nr > field.pos.last_line { next_line_cmts << cmt }
1575 else { end_cmts << cmt }
1576 }
1577 }
1578 if pre_cmts.len > 0 {
1579 f.comments(pre_cmts, level: .indent)
1580 }
1581
1582 sym := f.table.sym(field.typ)
1583 if sym.info is ast.Struct {
1584 if sym.info.is_anon {
1585 f.write('\t${field.name} ')
1586 f.write_anon_struct_field_decl(field.typ, ast.StructDecl{ fields: sym.info.fields })
1587 } else {
1588 f.write('\t${field.name} ')
1589 }
1590 } else {
1591 f.write('\t${field.name} ')
1592 }
1593 if !(sym.info is ast.Struct && sym.info.is_anon) {
1594 f.write(' '.repeat(type_align.max_len(field.pos.line_nr) - field.name.len))
1595 f.write(ft)
1596 }
1597 if end_cmts.len > 0 {
1598 f.write(' '.repeat(comment_align.max_len(field.pos.line_nr) - ft.len + 1))
1599 f.comments(end_cmts, level: .indent)
1600 } else {
1601 f.writeln('')
1602 }
1603 if next_line_cmts.len > 0 {
1604 f.comments(next_line_cmts, level: .indent)
1605 }
1606}
1607
1608pub fn (mut f Fmt) interface_method(method ast.FnDecl, mut comment_align FieldAlign) {
1609 before_comments := method.comments.filter(it.pos.pos < method.pos.pos)
1610 end_comments := method.comments.filter(it.pos.pos > method.pos.pos)
1611 if before_comments.len > 0 {
1612 f.comments(before_comments, level: .indent)
1613 }
1614 f.write('\t')
1615 f.table.new_int_fmt_fix = f.table.new_int && (f.is_translated_module || f.is_c_function)
1616 method_str :=
1617 f.table.stringify_fn_decl(&method, f.cur_mod, f.mod2alias, false).all_after_first('fn ')
1618 f.table.new_int_fmt_fix = false
1619 f.write(method_str)
1620 if end_comments.len > 0 {
1621 f.write(' '.repeat(comment_align.max_len(method.pos.line_nr) - method_str.len + 1))
1622 f.comments(end_comments, level: .indent)
1623 } else {
1624 f.writeln('')
1625 }
1626 f.comments(method.next_comments, level: .indent)
1627}
1628
1629pub fn (mut f Fmt) module_stmt(mod ast.Module) {
1630 f.set_current_module_name(mod.name)
1631 if mod.is_skipped {
1632 return
1633 }
1634 f.is_translated_module = mod.attrs.any(it.name == 'translated')
1635 f.attrs(mod.attrs)
1636 f.writeln('module ${mod.short_name}\n')
1637 if f.import_pos == 0 {
1638 f.import_pos = f.out.len
1639 }
1640}
1641
1642pub fn (mut f Fmt) return_stmt(node ast.Return) {
1643 f.write('return')
1644 if node.exprs.len > 0 {
1645 f.write(' ')
1646 mut sum_len := 0
1647 // Loop over all return values. In normal returns this will only run once.
1648 for i, expr in node.exprs {
1649 pre_comments := node.comments[sum_len..].filter(it.pos.pos < expr.pos().pos)
1650 sum_len += pre_comments.len
1651 if pre_comments.len > 0 {
1652 f.comments(pre_comments)
1653 f.write(' ')
1654 }
1655 if expr is ast.ParExpr && expr.comments.len == 0 {
1656 f.expr(expr.expr)
1657 } else {
1658 f.expr(expr)
1659 }
1660 if i < node.exprs.len - 1 {
1661 f.write(', ')
1662 }
1663 }
1664 }
1665 if !f.single_line_if {
1666 f.writeln('')
1667 }
1668}
1669
1670pub fn (mut f Fmt) sql_stmt(node ast.SqlStmt) {
1671 f.write('sql ')
1672 f.expr(node.db_expr)
1673 f.writeln(' {')
1674
1675 for line in node.lines {
1676 f.comments(line.pre_comments, level: .indent)
1677 f.sql_stmt_line(line)
1678 f.comments(line.end_comments, level: .indent)
1679 }
1680 f.write('}')
1681 f.or_expr(node.or_expr)
1682 f.writeln('')
1683}
1684
1685pub fn (mut f Fmt) sql_stmt_line(node ast.SqlStmtLine) {
1686 sym := f.table.sym(node.table_expr.typ)
1687 mut table_name := sym.name
1688 if !table_name.starts_with('C.') && !table_name.starts_with('JS.') {
1689 table_name = f.no_cur_mod(f.short_module(sym.name)) // TODO: f.type_to_str?
1690 }
1691
1692 f.write('\t')
1693 match node.kind {
1694 .insert {
1695 f.writeln('insert ${node.object_var} into ${table_name}')
1696 }
1697 .upsert {
1698 f.writeln('upsert ${node.object_var} into ${table_name}')
1699 }
1700 .update {
1701 if node.is_dynamic {
1702 f.write('dynamic update ${table_name} set ')
1703 f.expr(node.update_data_expr)
1704 f.write(' ')
1705 f.write('where ')
1706 f.expr(node.where_expr)
1707 f.writeln('')
1708 } else {
1709 mut has_multiline_update_expr := false
1710 for expr in node.update_exprs {
1711 if f.node_str(expr).contains('\n') {
1712 has_multiline_update_expr = true
1713 break
1714 }
1715 }
1716 if has_multiline_update_expr {
1717 f.writeln('update ${table_name} set')
1718 // SQL block lines use a manual extra tab, so nested update values need two
1719 // formatter indent levels to stay visually nested.
1720 f.indent += 2
1721 for i, col in node.updated_columns {
1722 f.write('${col} = ')
1723 f.expr(node.update_exprs[i])
1724 if i < node.updated_columns.len - 1 {
1725 f.write(',')
1726 }
1727 f.writeln('')
1728 }
1729 f.indent -= 2
1730 } else {
1731 f.write('update ${table_name} set ')
1732 for i, col in node.updated_columns {
1733 f.write('${col} = ')
1734 f.expr(node.update_exprs[i])
1735 if i < node.updated_columns.len - 1 {
1736 f.write(', ')
1737 } else {
1738 f.write(' ')
1739 }
1740 f.wrap_long_line(3, true)
1741 }
1742 }
1743 if has_multiline_update_expr {
1744 f.write('\twhere ')
1745 } else {
1746 f.write('where ')
1747 }
1748 f.expr(node.where_expr)
1749 f.writeln('')
1750 }
1751 }
1752 .delete {
1753 f.write('delete from ${table_name} where ')
1754 f.expr(node.where_expr)
1755 f.writeln('')
1756 }
1757 .create {
1758 f.writeln('create table ${table_name}')
1759 }
1760 .drop {
1761 f.writeln('drop table ${table_name}')
1762 }
1763 }
1764}
1765
1766pub fn (mut f Fmt) type_decl(node ast.TypeDecl) {
1767 match node {
1768 ast.AliasTypeDecl { f.alias_type_decl(node) }
1769 ast.FnTypeDecl { f.fn_type_decl(node) }
1770 ast.SumTypeDecl { f.sum_type_decl(node) }
1771 }
1772
1773 f.writeln('')
1774}
1775
1776pub fn (mut f Fmt) alias_type_decl(node ast.AliasTypeDecl) {
1777 f.attrs(node.attrs)
1778 if node.is_pub {
1779 f.write('pub ')
1780 }
1781 // aliases of anon struct: `type Foo = struct {}`
1782 sym := f.table.sym(node.parent_type)
1783 if sym.info is ast.Struct {
1784 if sym.info.is_anon {
1785 f.write('type ${node.name} = ')
1786 f.struct_decl(ast.StructDecl{ fields: sym.info.fields }, true)
1787 f.comments(node.comments, has_nl: false)
1788 return
1789 }
1790 }
1791 ptype := f.type_to_str_using_aliases(node.parent_type, f.mod2alias)
1792 f.write('type ${node.name} = ${ptype}')
1793
1794 f.comments(node.comments, has_nl: false)
1795}
1796
1797pub fn (mut f Fmt) fn_type_decl(node ast.FnTypeDecl) {
1798 f.attrs(node.attrs)
1799 if node.is_pub {
1800 f.write('pub ')
1801 }
1802 typ_sym := f.table.sym(node.typ)
1803 fn_typ_info := typ_sym.info as ast.FnType
1804 fn_info := fn_typ_info.func
1805 fn_name := f.no_cur_mod(node.name)
1806 mut generic_types_str := ''
1807 if node.generic_types.len > 0 {
1808 generic_names := node.generic_types.map(f.table.sym(it).name)
1809 generic_types_str = '[${generic_names.join(', ')}]'
1810 }
1811 f.write('type ${fn_name}${generic_types_str} = fn (')
1812 for i, arg in fn_info.params {
1813 if arg.is_mut {
1814 f.write(arg.typ.share().str() + ' ')
1815 }
1816 f.write(arg.name)
1817 mut s := f.no_cur_mod(f.type_to_str_using_aliases(arg.typ, f.mod2alias))
1818 if arg.is_mut {
1819 if s.starts_with('&') {
1820 s = s[1..]
1821 }
1822 s = s.trim_left('shared ')
1823 }
1824 is_last_arg := i == fn_info.params.len - 1
1825 should_add_type := true || is_last_arg
1826 || fn_info.params[i + 1].typ != arg.typ
1827 || (fn_info.is_variadic && i == fn_info.params.len - 2)
1828 if should_add_type {
1829 ns := if arg.name == '' { '' } else { ' ' }
1830 if fn_info.is_variadic && is_last_arg {
1831 f.write(ns + '...' + s)
1832 } else {
1833 f.write(ns + s)
1834 }
1835 }
1836 if !is_last_arg {
1837 f.write(', ')
1838 }
1839 }
1840 f.write(')')
1841 if fn_info.return_type.idx() != ast.void_type_idx {
1842 ret_str := f.no_cur_mod(f.type_to_str_using_aliases(fn_info.return_type, f.mod2alias))
1843 f.write(' ${ret_str}')
1844 } else if fn_info.return_type.has_flag(.option) {
1845 f.write(' ?')
1846 } else if fn_info.return_type.has_flag(.result) {
1847 f.write(' !')
1848 }
1849
1850 f.comments(node.comments, has_nl: false)
1851 f.writeln('')
1852}
1853
1854struct Variant {
1855 name string
1856 id int
1857}
1858
1859fn (mut f Fmt) sum_type_variant_comments(variant ast.TypeNode) {
1860 if variant.end_comments.len == 0 {
1861 return
1862 }
1863 mut same_line_comments := []ast.Comment{}
1864 mut follow_up_comments := []ast.Comment{}
1865 for comment in variant.end_comments {
1866 if comment.pos.line_nr == variant.pos.last_line {
1867 same_line_comments << comment
1868 } else {
1869 follow_up_comments << comment
1870 }
1871 }
1872 if same_line_comments.len > 0 {
1873 f.comments(same_line_comments, has_nl: false)
1874 }
1875 if follow_up_comments.len > 0 {
1876 f.writeln('')
1877 f.comments(follow_up_comments, has_nl: false, level: .indent)
1878 }
1879}
1880
1881pub fn (mut f Fmt) sum_type_decl(node ast.SumTypeDecl) {
1882 f.attrs(node.attrs)
1883 start_pos := f.out.len
1884 if node.is_pub {
1885 f.write('pub ')
1886 }
1887 f.write('type ${node.name}')
1888 f.write_generic_types(node.generic_types)
1889 f.write(' = ')
1890
1891 mut variants := []Variant{cap: node.variants.len}
1892 for i, variant in node.variants {
1893 variants << Variant{f.type_to_str_using_aliases(variant.typ, f.mod2alias), i}
1894 }
1895 // The first variant is now used as the default variant when doing `a:= Sumtype{}`, i.e. a change in semantics.
1896 // Sorting is disabled, because it is no longer a cosmetic change - it can change the default variant.
1897 // variants.sort(a.name < b.name)
1898
1899 mut separator := ' | '
1900 mut is_multiline := false
1901 // if line length is too long, put each type on its own line
1902 mut line_length := f.out.len - start_pos
1903 for variant in variants {
1904 // 3 = length of ' = ' or ' | '
1905 line_length += 3 + variant.name.len
1906 if line_length > max_len || (variant.id != node.variants.len - 1
1907 && node.variants[variant.id].end_comments.len > 0) {
1908 separator = '\n\t| '
1909 is_multiline = true
1910 break
1911 }
1912 }
1913
1914 for i, variant in variants {
1915 if i > 0 {
1916 f.write(separator)
1917 }
1918 f.write(variant.name)
1919 if node.variants[variant.id].end_comments.len > 0 && is_multiline {
1920 f.sum_type_variant_comments(node.variants[variant.id])
1921 }
1922 }
1923 if !is_multiline {
1924 f.comments(node.variants.last().end_comments,
1925 has_nl: false
1926 )
1927 }
1928}
1929
1930//=== Specific Expr methods ===//
1931
1932pub fn (mut f Fmt) array_decompose(node ast.ArrayDecompose) {
1933 f.write('...')
1934 f.expr(node.expr)
1935}
1936
1937pub fn (mut f Fmt) array_init(node ast.ArrayInit) {
1938 typed_fixed_literal := node.is_fixed && node.has_val && node.typ != 0
1939 && node.typ != ast.void_type
1940 if node.is_fixed && node.is_option {
1941 f.write('?')
1942 }
1943 if node.exprs.len == 0 && ((node.typ != 0 && node.typ != ast.void_type)
1944 || node.elem_type_expr !is ast.EmptyExpr) {
1945 // `x := []string{}`
1946 if node.alias_type != ast.void_type {
1947 f.write(f.type_to_str_using_aliases(node.alias_type, f.mod2alias))
1948 } else if node.elem_type_expr !is ast.EmptyExpr {
1949 f.write('[]')
1950 f.expr(node.elem_type_expr)
1951 } else {
1952 f.write(f.type_to_str_using_aliases(node.typ, f.mod2alias))
1953 }
1954 f.write('{')
1955 if node.has_len {
1956 f.write('len: ')
1957 f.expr(node.len_expr)
1958 if node.has_cap || node.has_init {
1959 f.write(', ')
1960 }
1961 }
1962 if node.has_cap {
1963 f.write('cap: ')
1964 f.expr(node.cap_expr)
1965 if node.has_init {
1966 f.write(', ')
1967 }
1968 }
1969 if node.has_init {
1970 f.write('init: ')
1971 old_is_array_init := f.is_array_init
1972 f.is_array_init = true
1973 f.expr(node.init_expr)
1974 f.is_array_init = old_is_array_init
1975 }
1976 f.write('}')
1977 return
1978 }
1979 if typed_fixed_literal && f.array_init_depth == 0 {
1980 fixed_literal_type := if node.literal_typ != ast.void_type {
1981 node.literal_typ
1982 } else {
1983 node.typ.clear_option_and_result()
1984 }
1985 f.write(f.type_to_str_using_aliases(fixed_literal_type, f.mod2alias))
1986 }
1987 // `[1,2,3]`
1988 f.write('[')
1989 mut inc_indent := false
1990 mut last_line_nr := node.pos.line_nr // to have the same newlines between array elements
1991 f.array_init_depth++
1992 if node.pre_cmnts.len > 0 {
1993 if node.pre_cmnts[0].pos.line_nr > last_line_nr {
1994 f.writeln('')
1995 }
1996 }
1997 if node.has_update_expr {
1998 f.write('...')
1999 f.expr(node.update_expr)
2000 if node.exprs.len > 0 {
2001 f.write(',')
2002 }
2003 if node.update_expr_comments.len > 0 {
2004 f.write(' ')
2005 f.comments(node.update_expr_comments,
2006 prev_line: node.update_expr_pos.last_line
2007 has_nl: false
2008 )
2009 last_line_nr = node.update_expr_comments.last().pos.last_line
2010 } else {
2011 last_line_nr = node.update_expr_pos.last_line
2012 }
2013 if node.exprs.len > 0 && node.update_expr_comments.len == 0 {
2014 f.write(' ')
2015 }
2016 }
2017 for i, c in node.pre_cmnts {
2018 if i < node.pre_cmnts.len - 1 {
2019 if c.pos.last_line < node.pre_cmnts[i + 1].pos.line_nr {
2020 f.comment(c, level: .indent)
2021 f.writeln('')
2022 } else {
2023 f.comment(c, level: .indent)
2024 f.write(' ')
2025 }
2026 } else {
2027 next_line := if node.exprs.len > 0 {
2028 node.exprs[0].pos().line_nr
2029 } else {
2030 node.pos.last_line
2031 }
2032 if c.pos.last_line < next_line {
2033 f.comment(c, level: .indent)
2034 if node.exprs.len == 0 {
2035 f.writeln('')
2036 }
2037 } else {
2038 f.comment(c, level: .indent)
2039 if node.exprs.len > 0 {
2040 f.write(' ')
2041 }
2042 }
2043 }
2044 last_line_nr = c.pos.last_line
2045 }
2046 mut set_comma := false
2047 for i, expr in node.exprs {
2048 pos := expr.pos()
2049 if i == 0 {
2050 if f.array_init_depth > f.array_init_break.len {
2051 f.array_init_break << pos.line_nr > last_line_nr
2052 || f.line_len + expr.pos().len > break_points[3]
2053 }
2054 }
2055 mut line_break := f.array_init_break[f.array_init_depth - 1]
2056 mut penalty := if line_break { 0 } else { 4 }
2057 if penalty > 0 {
2058 if i == 0
2059 || node.exprs[i - 1] in [ast.ArrayInit, ast.StructInit, ast.MapInit, ast.CallExpr] {
2060 penalty--
2061 }
2062 if expr in [ast.ArrayInit, ast.StructInit, ast.MapInit, ast.CallExpr] {
2063 penalty--
2064 }
2065 }
2066 mut is_new_line := f.wrap_long_line(penalty, !inc_indent)
2067 if is_new_line && !inc_indent {
2068 f.indent++
2069 inc_indent = true
2070 }
2071 single_line_expr := expr_is_single_line(expr)
2072 if single_line_expr {
2073 mut estr := ''
2074 if !is_new_line && !f.buffering && f.line_len + expr.pos().len > max_len {
2075 if inc_indent {
2076 estr = f.node_str(expr)
2077 }
2078 f.writeln('')
2079 is_new_line = true
2080 if !inc_indent {
2081 f.indent++
2082 inc_indent = true
2083 f.write_indent()
2084 f.empty_line = false
2085 estr = f.node_str(expr)
2086 }
2087 if i == 0 {
2088 f.array_init_break[f.array_init_depth - 1] = true
2089 line_break = true
2090 }
2091 } else {
2092 estr = f.node_str(expr)
2093 }
2094 if !is_new_line && i > 0 {
2095 f.write(' ')
2096 }
2097 f.write(estr)
2098 } else {
2099 if !is_new_line && i > 0 {
2100 f.write(' ')
2101 }
2102 // When the array stays on a single source line, keep nested
2103 // struct inits on a single line too (instead of expanding their
2104 // named fields onto multiple lines).
2105 keep_struct_inline := !line_break && expr is ast.StructInit
2106 && expr.pos.line_nr == expr.pos.last_line && expr.pre_comments.len == 0
2107 old_single_line_fields := f.single_line_fields
2108 if keep_struct_inline {
2109 f.single_line_fields = true
2110 }
2111 f.expr(expr)
2112 f.single_line_fields = old_single_line_fields
2113 }
2114 mut last_comment_was_inline := false
2115 mut has_comments := node.ecmnts[i].len > 0
2116 if i < node.ecmnts.len && has_comments {
2117 expr_pos := expr.pos()
2118 for icmt, cmt in node.ecmnts[i] {
2119 if !set_comma && cmt.pos.pos > expr_pos.pos + expr_pos.len + 2 {
2120 if icmt > 0 {
2121 if last_comment_was_inline {
2122 f.write(',')
2123 set_comma = true
2124 }
2125 } else {
2126 f.write(',') // first comment needs a comma
2127 set_comma = true
2128 }
2129 }
2130 if cmt.pos.line_nr > expr_pos.last_line {
2131 f.writeln('')
2132 f.comment(cmt)
2133 } else {
2134 if !set_comma {
2135 f.write(',')
2136 set_comma = true
2137 }
2138 f.write(' ')
2139 f.comment(cmt)
2140 if !line_break {
2141 f.writeln('')
2142 }
2143 }
2144 }
2145 } else if i == node.exprs.len - 1 && !line_break {
2146 is_new_line = false
2147 }
2148
2149 mut put_comma := !set_comma
2150 if has_comments && !last_comment_was_inline {
2151 put_comma = false
2152 }
2153 if i == node.exprs.len - 1 {
2154 if is_new_line {
2155 if put_comma {
2156 f.write(',')
2157 }
2158 f.writeln('')
2159 }
2160 } else if put_comma {
2161 f.write(',')
2162 }
2163 last_line_nr = pos.last_line
2164 set_comma = false
2165 }
2166 f.array_init_depth--
2167 if f.array_init_depth == 0 {
2168 f.array_init_break = []
2169 }
2170 if inc_indent {
2171 f.indent--
2172 }
2173 f.write(']')
2174 // `[100]u8`
2175 if node.is_fixed {
2176 if node.has_val {
2177 if typed_fixed_literal {
2178 return
2179 }
2180 if node.from_to_fixed_size {
2181 f.write('.to_fixed_size()')
2182 } else {
2183 f.write('!')
2184 }
2185 return
2186 }
2187 f.write(f.type_to_str_using_aliases(node.elem_type, f.mod2alias))
2188 if node.has_init {
2189 f.write('{init: ')
2190 f.expr(node.init_expr)
2191 f.write('}')
2192 } else {
2193 f.write('{}')
2194 }
2195 }
2196}
2197
2198pub fn (mut f Fmt) as_cast(node ast.AsCast) {
2199 type_str := f.type_to_str_using_aliases(node.typ, f.mod2alias)
2200 f.expr(node.expr)
2201 f.write(' as ${type_str}')
2202}
2203
2204pub fn (mut f Fmt) assoc(node ast.Assoc) {
2205 f.writeln('{')
2206 f.indent++
2207 f.writeln('...${node.var_name}')
2208 for i, field in node.fields {
2209 f.write('${field}: ')
2210 f.expr(node.exprs[i])
2211 f.writeln('')
2212 }
2213 f.indent--
2214 f.write('}')
2215}
2216
2217pub fn (mut f Fmt) at_expr(node ast.AtExpr) {
2218 f.write(node.name)
2219}
2220
2221fn (mut f Fmt) write_static_method(_name string, short_name string) {
2222 if short_name.contains('.') {
2223 indx := short_name.index_('.') + 1
2224 f.write(short_name[0..indx] + short_name[indx..].replace('__static__', '.').capitalize())
2225 } else {
2226 f.write(short_name.replace('__static__', '.').capitalize())
2227 }
2228}
2229
2230pub fn (mut f Fmt) call_expr(node ast.CallExpr) {
2231 mut is_method_newline := false
2232 if node.is_method {
2233 if ast.builtin_array_generic_methods_no_sort_matcher.matches(node.name) {
2234 f.in_lambda_depth++
2235 defer(fn) { f.in_lambda_depth-- }
2236 }
2237 f.expr(node.left)
2238 is_method_newline = node.left.pos().last_line != node.name_pos.line_nr
2239 if is_method_newline {
2240 f.indent++
2241 f.writeln('')
2242 }
2243 f.write('.' + node.name)
2244 } else {
2245 f.write_language_prefix(node.language)
2246 if node.left is ast.AnonFn {
2247 f.anon_fn(node.left)
2248 } else if node.language != .v {
2249 f.write('${node.name.after_char(`.`)}')
2250 } else {
2251 name := f.short_module(node.name)
2252 if node.is_static_method {
2253 f.write_static_method(node.name, name)
2254 } else if node.is_paren_wrapped_call {
2255 f.write('(${name})')
2256 } else {
2257 f.write(name)
2258 }
2259 }
2260 }
2261 if node.mod == '' && node.name == '' {
2262 if node.left is ast.CallExpr {
2263 f.expr(node.left)
2264 } else {
2265 f.write(node.left.str())
2266 }
2267 }
2268 f.write_generic_call_if_require(node)
2269 f.write('(')
2270 f.call_args(node.args)
2271 f.write(')')
2272 f.or_expr(node.or_block)
2273 f.comments(node.comments, has_nl: false)
2274 if is_method_newline {
2275 f.indent--
2276 }
2277}
2278
2279fn (mut f Fmt) write_generic_call_if_require(node ast.CallExpr) {
2280 if node.concrete_types.len > 0 {
2281 f.write('[')
2282 for i, concrete_type in node.concrete_types {
2283 tsym := f.table.sym(concrete_type)
2284 if !f.write_anon_struct_type(concrete_type) {
2285 mut name := f.type_to_str_using_aliases(concrete_type, f.mod2alias)
2286 if tsym.language != .js && !tsym.name.starts_with('JS.') {
2287 name = f.short_module(name)
2288 } else if tsym.language == .js && !tsym.name.starts_with('JS.') {
2289 name = 'JS.' + name
2290 }
2291 if tsym.language == .c {
2292 name = 'C.' + name
2293 }
2294 f.write(name)
2295 }
2296 if i != node.concrete_types.len - 1 {
2297 f.write(', ')
2298 }
2299 }
2300 f.write(']')
2301 }
2302}
2303
2304pub fn (mut f Fmt) call_args(args []ast.CallArg) {
2305 old_single_line_fields_state := f.single_line_fields
2306 old_short_arg_state := f.use_short_fn_args
2307 f.single_line_fields = true
2308 f.use_short_fn_args = false
2309 defer {
2310 f.single_line_fields = old_single_line_fields_state
2311 f.use_short_fn_args = old_short_arg_state
2312 }
2313 for i, arg in args {
2314 pre_comments := arg.comments.filter(it.pos.pos < arg.expr.pos().pos)
2315 post_comments := arg.comments[pre_comments.len..]
2316 if pre_comments.len > 0 {
2317 f.comments(pre_comments)
2318 f.write(' ')
2319 }
2320 if i == args.len - 1 && arg.expr is ast.StructInit {
2321 if arg.expr.typ == ast.void_type {
2322 f.use_short_fn_args = true
2323 }
2324 }
2325 if arg.is_mut {
2326 f.write(arg.share.str() + ' ')
2327 }
2328 if i > 0 && !f.single_line_if && !f.use_short_fn_args && arg.expr !is ast.StructInit {
2329 arg_str := f.node_str(arg.expr)
2330 tail_len := if i < args.len - 1 { 2 } else { 1 }
2331 is_tiny_last_assign_arg := f.is_assign && i == args.len - 1 && arg_str.len <= 4
2332 if !is_tiny_last_assign_arg && !arg_str.contains('\n')
2333 && f.line_len + arg_str.len + tail_len > max_len {
2334 f.wrap_long_line(0, true)
2335 }
2336 }
2337 f.expr(arg.expr)
2338 if post_comments.len > 0 {
2339 f.comments(post_comments)
2340 f.write(' ')
2341 }
2342 if i < args.len - 1 {
2343 f.write(', ')
2344 }
2345 }
2346}
2347
2348pub fn (mut f Fmt) cast_expr(node ast.CastExpr) {
2349 typ := f.type_to_str_using_aliases(node.typ, f.mod2alias)
2350 if typ == 'voidptr' {
2351 // `voidptr(0)` => `nil`
2352 if node.expr is ast.IntegerLiteral {
2353 if node.expr.val == '0' {
2354 if f.inside_unsafe {
2355 f.write('nil')
2356 } else {
2357 f.write('unsafe { nil }')
2358 }
2359 return
2360 }
2361 }
2362 }
2363 f.write('${typ}(')
2364 f.expr(node.expr)
2365 if node.has_arg {
2366 f.write(', ')
2367 f.expr(node.arg)
2368 }
2369 f.write(')')
2370}
2371
2372pub fn (mut f Fmt) chan_init(mut node ast.ChanInit) {
2373 info := f.table.sym(node.typ).chan_info()
2374 if node.elem_type == 0 && node.typ > 0 {
2375 node.elem_type = info.elem_type
2376 }
2377 is_mut := info.is_mut
2378 el_typ := if is_mut {
2379 node.elem_type.set_nr_muls(node.elem_type.nr_muls() - 1)
2380 } else {
2381 node.elem_type
2382 }
2383 f.write('chan ')
2384 if is_mut {
2385 f.write('mut ')
2386 }
2387 f.write(f.type_to_str_using_aliases(el_typ, f.mod2alias))
2388 f.write('{')
2389 if node.has_cap {
2390 f.write('cap: ')
2391 f.expr(node.cap_expr)
2392 }
2393 f.write('}')
2394}
2395
2396pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) {
2397 if node.is_template {
2398 if node.kind == .html {
2399 if node.args.len == 1 && node.args[0].expr is ast.StringLiteral {
2400 f.write('\$veb.html(')
2401 f.expr(node.args[0].expr)
2402 f.write(')')
2403 } else {
2404 f.write('\$veb.html()')
2405 }
2406 } else {
2407 f.write('\$tmpl(')
2408 f.expr(node.args[0].expr)
2409 f.write(')')
2410 }
2411 } else {
2412 match true {
2413 node.kind == .embed_file {
2414 f.write('\$embed_file(')
2415 f.expr(node.args[0].expr)
2416 if node.embed_file.compression_type != 'none' {
2417 f.write(', .${node.embed_file.compression_type}')
2418 }
2419 f.write(')')
2420 }
2421 node.kind == .env {
2422 f.write("\$env('${node.args_var}')")
2423 }
2424 node.kind == .pkgconfig {
2425 f.write("\$pkgconfig('${node.args_var}')")
2426 }
2427 node.kind in [.compile_error, .compile_warn] {
2428 if node.args.len == 0 {
2429 if node.args_var.contains("'") {
2430 f.write('\$${node.method_name}("${node.args_var}")')
2431 } else {
2432 f.write("\$${node.method_name}('${node.args_var}')")
2433 }
2434 } else {
2435 f.write('\$${node.method_name}(')
2436 f.expr(node.args[0].expr)
2437 f.write(')')
2438 }
2439 }
2440 node.kind == .d {
2441 f.write("\$d('${node.args_var}', ")
2442 f.expr(node.args[0].expr)
2443 f.write(')')
2444 }
2445 node.kind == .res {
2446 if node.args_var != '' {
2447 f.write('\$res(${node.args_var})')
2448 } else {
2449 f.write('\$res()')
2450 }
2451 }
2452 node.kind in [.zero, .new] {
2453 f.write('\$${node.method_name}(')
2454 f.expr(node.args[0].expr)
2455 f.write(')')
2456 }
2457 else {
2458 inner_args := if node.args_var != '' {
2459 node.args_var
2460 } else {
2461 node.args.map(call_arg_spread_str).join(', ')
2462 }
2463 method_expr := if node.has_parens {
2464 '(${node.method_name}(${inner_args}))'
2465 } else {
2466 '${node.method_name}(${inner_args})'
2467 }
2468 f.expr(node.left)
2469 f.write('.$${method_expr}')
2470 f.or_expr(node.or_block)
2471 }
2472 }
2473 }
2474}
2475
2476pub fn (mut f Fmt) comptime_selector(node ast.ComptimeSelector) {
2477 f.expr(node.left)
2478 f.write('.\$(${node.field_expr})')
2479}
2480
2481pub fn (mut f Fmt) concat_expr(node ast.ConcatExpr) {
2482 for i, val in node.vals {
2483 if i != 0 {
2484 f.write(', ')
2485 }
2486 f.expr(val)
2487 }
2488}
2489
2490pub fn (mut f Fmt) dump_expr(node ast.DumpExpr) {
2491 f.write('dump(')
2492 f.expr(node.expr)
2493 f.write(')')
2494}
2495
2496pub fn (mut f Fmt) enum_val(node ast.EnumVal) {
2497 name := f.short_module(node.enum_name)
2498 f.write(name + '.' + node.val)
2499}
2500
2501pub fn (mut f Fmt) ident(node ast.Ident) {
2502 if node.info is ast.IdentVar {
2503 if node.comptime && node.name in ast.valid_comptime_not_user_defined {
2504 f.write(node.name)
2505 return
2506 }
2507 if node.info.is_mut {
2508 f.write(node.info.share.str() + ' ')
2509 }
2510 var_info := node.var_info()
2511 if var_info.is_static {
2512 f.write('static ')
2513 }
2514 if var_info.is_volatile {
2515 f.write('volatile ')
2516 }
2517 }
2518 f.write_language_prefix(node.language)
2519 if node.kind == .blank_ident {
2520 f.write('_')
2521 } else {
2522 mut is_local := false
2523 if f.fn_scope != unsafe { nil } {
2524 if _ := f.fn_scope.find_var(node.name) {
2525 is_local = true
2526 }
2527 }
2528 if !is_local && !node.name.contains('.') && !f.inside_const {
2529 if _ := f.file.global_scope.find_const('${f.cur_mod}.${node.name}') {
2530 const_name := node.name.all_after_last('.')
2531 f.write(const_name)
2532 if node.or_expr.kind == .block {
2533 f.or_expr(node.or_expr)
2534 }
2535 return
2536 }
2537 }
2538 name := f.short_module(node.name)
2539 if node.name.contains('__static__') {
2540 f.write_static_method(node.name, name)
2541 } else if f.is_array_init && name == 'it' {
2542 f.write('index')
2543 } else {
2544 f.write(name)
2545 }
2546 if node.concrete_types.len > 0 {
2547 f.write('[')
2548 for i, concrete_type in node.concrete_types {
2549 if !f.write_anon_struct_type(concrete_type) {
2550 typ_name := f.type_to_str_using_aliases(concrete_type, f.mod2alias)
2551 f.write(typ_name)
2552 }
2553 if i != node.concrete_types.len - 1 {
2554 f.write(', ')
2555 }
2556 }
2557 f.write(']')
2558 }
2559 if node.or_expr.kind == .propagate_option {
2560 f.write('?')
2561 } else if node.or_expr.kind == .block {
2562 f.or_expr(node.or_expr)
2563 }
2564 }
2565}
2566
2567pub fn (mut f Fmt) if_expr(node ast.IfExpr) {
2568 dollar := if node.is_comptime { '$' } else { '' }
2569 f.inside_comptime_if = node.is_comptime
2570 mut keep_single_line := node.branches.len == 1 && branch_is_single_line(node.branches[0])
2571 is_ternary := node.branches.len == 2 && node.has_else && branch_is_single_line(node.branches[0])
2572 && branch_is_single_line(node.branches[1]) && (node.is_expr || f.is_assign
2573 || f.inside_const || f.is_struct_init || f.single_line_fields)
2574 keep_single_line = keep_single_line || is_ternary
2575 f.single_line_if = keep_single_line
2576 start_pos := f.out.len
2577 start_len := f.line_len
2578 for {
2579 for i, branch in node.branches {
2580 f.branch_processed_imports.clear()
2581 mut sum_len := 0
2582 if i > 0 {
2583 // `else`, close previous branch
2584 if branch.comments.len > 0 {
2585 f.writeln('}')
2586 pre_comments := branch.comments.filter(it.pos.pos < branch.pos.pos)
2587 sum_len += pre_comments.len
2588 if pre_comments.len > 0 {
2589 f.comments(pre_comments)
2590 }
2591 } else {
2592 f.write('} ')
2593 }
2594 f.write('${dollar}else ')
2595 }
2596 if i < node.branches.len - 1 || !node.has_else {
2597 f.write('${dollar}if ')
2598 cur_pos := f.out.len
2599 pre_comments :=
2600 branch.comments[sum_len..].filter(it.pos.pos < branch.cond.pos().pos)
2601 sum_len += pre_comments.len
2602 post_comments := branch.comments[sum_len..]
2603 if pre_comments.len > 0 {
2604 f.comments(pre_comments)
2605 f.write(' ')
2606 }
2607 f.expr(branch.cond)
2608 if post_comments.len > 0 {
2609 f.comments(post_comments)
2610 f.write(' ')
2611 }
2612 cond_len := f.out.len - cur_pos
2613 is_cond_wrapped := cond_len > 0 && branch.cond in [ast.IfGuardExpr, ast.CallExpr]
2614 && f.out.last_n(cond_len).contains('\n')
2615 if is_cond_wrapped {
2616 f.writeln('')
2617 } else {
2618 f.write(' ')
2619 }
2620 }
2621 f.write('{')
2622 if keep_single_line {
2623 f.write(' ')
2624 } else {
2625 f.writeln('')
2626 }
2627 f.stmts(branch.stmts)
2628 if keep_single_line {
2629 f.write(' ')
2630 }
2631 }
2632 if keep_single_line && f.line_len > max_len && !f.buffering {
2633 keep_single_line = false
2634 f.single_line_if = false
2635 f.out.go_back_to(start_pos)
2636 f.line_len = start_len
2637 f.empty_line = start_len == 0
2638 continue
2639 }
2640 break
2641 }
2642 f.write('}')
2643 f.single_line_if = false
2644 f.inside_comptime_if = false
2645 if node.post_comments.len > 0 {
2646 if keep_single_line {
2647 f.comments(node.post_comments,
2648 has_nl: false
2649 same_line: true
2650 prev_line: node.branches.last().body_pos.last_line
2651 )
2652 } else {
2653 f.writeln('')
2654 f.comments(node.post_comments,
2655 has_nl: false
2656 prev_line: node.branches.last().body_pos.last_line
2657 )
2658 }
2659 }
2660}
2661
2662fn branch_is_single_line(b ast.IfBranch) bool {
2663 if b.stmts.len == 1 && b.comments.len == 0 && stmt_is_single_line(b.stmts[0])
2664 && b.pos.line_nr == b.stmts[0].pos.line_nr {
2665 return true
2666 }
2667 return false
2668}
2669
2670fn sql_query_data_item_is_single_line(item ast.SqlQueryDataItem) bool {
2671 return match item {
2672 ast.SqlQueryDataLeaf {
2673 item.pre_comments.len == 0 && item.end_comments.len == 0
2674 && item.pos.line_nr == item.pos.last_line && expr_is_single_line(item.expr)
2675 }
2676 ast.SqlQueryDataIf {
2677 false
2678 }
2679 }
2680}
2681
2682fn sql_query_data_branch_is_single_line(branch ast.SqlQueryDataBranch) bool {
2683 return branch.end_comments.len == 0 && branch.pos.line_nr == branch.pos.last_line
2684 && branch.items.len == 1 && sql_query_data_item_is_single_line(branch.items[0])
2685}
2686
2687fn sql_query_data_item_pre_comments(item ast.SqlQueryDataItem) []ast.Comment {
2688 return match item {
2689 ast.SqlQueryDataLeaf { item.pre_comments }
2690 ast.SqlQueryDataIf { item.pre_comments }
2691 }
2692}
2693
2694fn sql_query_data_item_end_comments(item ast.SqlQueryDataItem) []ast.Comment {
2695 return match item {
2696 ast.SqlQueryDataLeaf { item.end_comments }
2697 ast.SqlQueryDataIf { item.end_comments }
2698 }
2699}
2700
2701fn sql_query_data_item_last_line(item ast.SqlQueryDataItem) int {
2702 return match item {
2703 ast.SqlQueryDataLeaf { item.pos.last_line }
2704 ast.SqlQueryDataIf { item.pos.last_line }
2705 }
2706}
2707
2708pub fn (mut f Fmt) if_guard_expr(node ast.IfGuardExpr) {
2709 for i, var in node.vars {
2710 if var.is_mut {
2711 f.write('mut ')
2712 }
2713 f.write(var.name)
2714 if i != node.vars.len - 1 {
2715 f.write(', ')
2716 }
2717 }
2718 f.write(' := ')
2719 f.expr(node.expr)
2720}
2721
2722pub fn (mut f Fmt) index_expr(node ast.IndexExpr) {
2723 f.expr(node.left)
2724 if node.is_gated {
2725 f.write('#')
2726 }
2727 last_index_expr_state := f.is_index_expr
2728 f.is_index_expr = true
2729 f.write('[')
2730 parts := if node.indices.len > 0 { node.indices } else { [node.index] }
2731 for i, part in parts {
2732 if i > 0 {
2733 f.write(', ')
2734 }
2735 f.expr(part)
2736 }
2737 f.write(']')
2738 f.is_index_expr = last_index_expr_state
2739 if node.or_expr.kind != .absent {
2740 f.or_expr(node.or_expr)
2741 }
2742}
2743
2744pub fn (mut f Fmt) infix_expr(node ast.InfixExpr) {
2745 buffering_save := f.buffering
2746 is_wrappable_additive_minus := node.op == .minus
2747 && (is_additive_infix(node.left) || is_additive_infix(node.right))
2748 if !f.buffering && (node.op in [.logical_or, .and, .plus] || is_wrappable_additive_minus) {
2749 f.buffering = true
2750 }
2751 is_assign_save := f.is_assign
2752 if node.op == .left_shift {
2753 f.is_assign = true // To write ternary if on a single line
2754 }
2755 start_pos := f.out.len
2756 start_len := f.line_len
2757 mut redundant_par := false
2758 if node.left is ast.ParExpr && node.op in [.and, .logical_or] {
2759 if node.left.expr is ast.InfixExpr {
2760 if node.left.expr.op !in [.and, .logical_or] {
2761 redundant_par = true
2762 f.expr(node.left.expr)
2763 }
2764 }
2765 }
2766 if !redundant_par {
2767 f.expr(node.left)
2768 }
2769 if node.before_op_comments.len > 0 {
2770 f.comments(node.before_op_comments)
2771 }
2772 is_one_val_array_init := node.op in [.key_in, .not_in] && node.right is ast.ArrayInit
2773 && node.right.exprs.len == 1
2774 is_and := node.op == .amp && f.node_str(node.right).starts_with('&')
2775 if is_one_val_array_init && !f.inside_comptime_if {
2776 // `var in [val]` => `var == val`
2777 op := if node.op == .key_in { ' == ' } else { ' != ' }
2778 f.write(op)
2779 } else if is_and {
2780 f.write(' && ')
2781 } else {
2782 f.write(' ${node.op.str()} ')
2783 }
2784 if node.after_op_comments.len > 0 {
2785 f.comments(node.after_op_comments)
2786 f.write(' ')
2787 }
2788 if is_one_val_array_init && !f.inside_comptime_if {
2789 // `var in [val]` => `var == val`
2790 f.expr((node.right as ast.ArrayInit).exprs[0])
2791 } else if is_and {
2792 f.write(f.node_str(node.right).trim_string_left('&'))
2793 } else {
2794 redundant_par = false
2795 if node.right is ast.ParExpr && node.op in [.and, .logical_or] {
2796 if node.right.expr is ast.InfixExpr {
2797 if node.right.expr.op !in [.and, .logical_or] {
2798 redundant_par = true
2799 f.expr(node.right.expr)
2800 }
2801 }
2802 }
2803 if !redundant_par {
2804 f.expr(node.right)
2805 }
2806 }
2807 if !buffering_save && f.buffering {
2808 f.buffering = false
2809 if !f.single_line_if && f.line_len > max_len {
2810 is_cond := node.op in [.and, .logical_or]
2811 f.wrap_infix(start_pos, start_len, is_cond)
2812 }
2813 }
2814 f.is_assign = is_assign_save
2815 f.or_expr(node.or_block)
2816}
2817
2818pub fn (mut f Fmt) wrap_infix(start_pos int, start_len int, is_cond bool) {
2819 cut_span := f.out.len - start_pos
2820 infix_str := f.out.cut_last(cut_span)
2821 if !infix_str.contains_any_substr(['&&', '||', '+', '-']) {
2822 f.write(infix_str)
2823 return
2824 }
2825 f.line_len = start_len
2826 if start_len == 0 {
2827 f.empty_line = true
2828 }
2829 conditions, penalties := split_up_infix(infix_str, false, is_cond)
2830 f.write_splitted_infix(conditions, penalties, false, is_cond)
2831}
2832
2833fn is_additive_infix(expr ast.Expr) bool {
2834 return match expr {
2835 ast.InfixExpr { expr.op in [.plus, .minus] }
2836 else { false }
2837 }
2838}
2839
2840fn split_up_infix(infix_str string, ignore_paren bool, is_cond_infix bool) ([]string, []int) {
2841 mut conditions := ['']
2842 mut penalties := [5]
2843 or_pen := if infix_str.contains('&&') { 3 } else { 5 }
2844 parts := infix_str.split(' ')
2845 mut inside_paren := false
2846 mut ind := 0
2847 for p in parts {
2848 if is_cond_infix && p in ['&&', '||'] {
2849 if inside_paren {
2850 conditions[ind] += '${p} '
2851 } else {
2852 pen := if p == '||' { or_pen } else { 5 }
2853 penalties << pen
2854 conditions << '${p} '
2855 ind++
2856 }
2857 } else if !is_cond_infix && p in ['+', '-'] {
2858 if inside_paren {
2859 conditions[ind] += '${p} '
2860 } else {
2861 penalties << 5
2862 conditions[ind] += '${p} '
2863 conditions << ''
2864 ind++
2865 }
2866 } else {
2867 conditions[ind] += '${p} '
2868 if ignore_paren {
2869 continue
2870 }
2871 if p.starts_with('(') {
2872 inside_paren = true
2873 } else if p.ends_with(')') {
2874 inside_paren = false
2875 }
2876 }
2877 }
2878 return conditions, penalties
2879}
2880
2881const wsinfix_depth_max = 10
2882
2883fn (mut f Fmt) write_splitted_infix(conditions []string, penalties []int, ignore_paren bool, is_cond bool) {
2884 f.wsinfix_depth++
2885 defer { f.wsinfix_depth-- }
2886 for i, cnd in conditions {
2887 c := cnd.trim_space()
2888 if f.line_len + c.len < break_points[penalties[i]] {
2889 if (i > 0 && i < conditions.len) || (ignore_paren && i == 0 && c.len > 5 && c[3] == `(`) {
2890 f.write(' ')
2891 }
2892 f.write(c)
2893 } else {
2894 is_paren_expr := (c[0] == `(` || (c.len > 5 && c[3] == `(`)) && c.ends_with(')')
2895 final_len := ((f.indent + 1) * 4) + c.len
2896 if f.wsinfix_depth > wsinfix_depth_max {
2897 // limit indefinite recursion, by just giving up splitting:
2898 f.write(c)
2899 continue
2900 }
2901 if final_len > max_len && is_paren_expr {
2902 conds, pens := split_up_infix(c, true, is_cond)
2903 f.write_splitted_infix(conds, pens, true, is_cond)
2904 continue
2905 }
2906 mut is_wrap_needed := true
2907 if i == 0 {
2908 last_line_str := f.remove_new_line().trim_space()
2909 if last_line_str in ['if', 'for'] {
2910 f.write(' ')
2911 is_wrap_needed = false
2912 }
2913 }
2914 if is_wrap_needed {
2915 f.writeln('')
2916 }
2917 f.indent++
2918 f.write(c)
2919 f.indent--
2920 }
2921 }
2922}
2923
2924pub fn (mut f Fmt) likely(node ast.Likely) {
2925 if node.is_likely {
2926 f.write('_likely_')
2927 } else {
2928 f.write('_unlikely_')
2929 }
2930 f.write('(')
2931 f.expr(node.expr)
2932 f.write(')')
2933}
2934
2935pub fn (mut f Fmt) lock_expr(node ast.LockExpr) {
2936 mut num_locked := 0
2937 mut num_rlocked := 0
2938 for is_rlock in node.is_rlock {
2939 if is_rlock {
2940 num_rlocked++
2941 } else {
2942 num_locked++
2943 }
2944 }
2945 if num_locked > 0 || num_rlocked == 0 {
2946 f.write('lock')
2947 if num_locked > 0 {
2948 f.write(' ')
2949 }
2950 mut n := 0
2951 for i, v in node.lockeds {
2952 if !node.is_rlock[i] {
2953 if n > 0 {
2954 f.write(', ')
2955 }
2956 f.expr(v)
2957 n++
2958 }
2959 }
2960 }
2961 if num_rlocked > 0 {
2962 if num_locked > 0 {
2963 f.write('; ')
2964 }
2965 f.write('rlock ')
2966 mut n := 0
2967 for i, v in node.lockeds {
2968 if node.is_rlock[i] {
2969 if n > 0 {
2970 f.write(', ')
2971 }
2972 f.expr(v)
2973 n++
2974 }
2975 }
2976 }
2977 f.writeln(' {')
2978 f.stmts(node.stmts)
2979 f.write('}')
2980}
2981
2982pub fn (mut f Fmt) map_init(node ast.MapInit) {
2983 if node.keys.len == 0 && !node.has_update_expr {
2984 if node.typ > ast.void_type {
2985 f.write(f.type_to_str_using_aliases(node.typ, f.mod2alias))
2986 }
2987 if node.pos.line_nr == node.pos.last_line {
2988 f.write('{}')
2989 } else {
2990 f.writeln('{')
2991 f.comments(node.pre_cmnts, level: .indent)
2992 f.write('}')
2993 }
2994 return
2995 }
2996 f.writeln('{')
2997 f.indent++
2998 f.comments(node.pre_cmnts)
2999 if node.has_update_expr {
3000 f.write('...')
3001 f.expr(node.update_expr)
3002 f.comments(node.update_expr_comments,
3003 prev_line: node.update_expr_pos.last_line
3004 has_nl: false
3005 )
3006 f.writeln('')
3007 }
3008 mut max_field_len := 0
3009 mut skeys := []string{}
3010 for key in node.keys {
3011 skey := f.node_str(key).trim_space()
3012 skeys << skey
3013 skey_len := utf8_str_visible_length(skey)
3014 if skey_len > max_field_len {
3015 max_field_len = skey_len
3016 }
3017 }
3018 for i, _ in node.keys {
3019 skey := skeys[i]
3020 f.write(skey)
3021 f.write(': ')
3022 skey_len := utf8_str_visible_length(skey)
3023 f.write(' '.repeat(max_field_len - skey_len))
3024 f.expr(node.vals[i])
3025 f.comments(node.comments[i], prev_line: node.vals[i].pos().last_line, has_nl: false)
3026 f.writeln('')
3027 }
3028 f.indent--
3029 f.write('}')
3030}
3031
3032fn (mut f Fmt) match_branch(branch ast.MatchBranch, single_line bool, is_comptime bool) {
3033 if !branch.is_else {
3034 // normal branch
3035 f.is_mbranch_expr = true
3036 for j, expr in branch.exprs {
3037 estr := f.node_str(expr).trim_space()
3038 if f.line_len + estr.len + 2 > max_len {
3039 f.remove_new_line()
3040 f.writeln('')
3041 }
3042 f.write(estr)
3043 if j < branch.exprs.len - 1 {
3044 f.write(', ')
3045 }
3046 if j < branch.ecmnts.len && branch.ecmnts[j].len > 0 {
3047 f.write(' ')
3048 f.comments(branch.ecmnts[j])
3049 }
3050 }
3051 f.is_mbranch_expr = false
3052 } else {
3053 // else branch
3054 if is_comptime {
3055 f.write('\$else')
3056 } else {
3057 f.write('else')
3058 }
3059 }
3060 if branch.stmts.len == 0 {
3061 f.writeln(' {}')
3062 } else {
3063 if single_line {
3064 f.write(' { ')
3065 } else if branch.ecmnts.len > 0 && branch.ecmnts.last().len > 0 {
3066 f.writeln('{')
3067 } else {
3068 f.writeln(' {')
3069 }
3070 f.stmts(branch.stmts)
3071 if single_line {
3072 f.remove_new_line()
3073 f.writeln(' }')
3074 } else {
3075 f.writeln('}')
3076 }
3077 }
3078 f.comments(branch.post_comments, same_line: true)
3079}
3080
3081pub fn (mut f Fmt) match_expr(node ast.MatchExpr) {
3082 dollar := if node.is_comptime { '$' } else { '' }
3083 cond, cond_or_expr := match_cond_with_trailing_or_expr(node.cond)
3084 f.write('${dollar}match ')
3085 f.expr(cond)
3086 f.writeln(' {')
3087 f.indent++
3088 f.comments(node.comments)
3089 mut single_line := true
3090 for branch in node.branches {
3091 if branch.stmts.len > 1 || branch.pos.line_nr < branch.pos.last_line {
3092 single_line = false
3093 break
3094 }
3095 if branch.stmts.len == 0 {
3096 continue
3097 }
3098 if !stmt_is_single_line(branch.stmts[0]) {
3099 single_line = false
3100 break
3101 }
3102 }
3103 mut else_idx := -1
3104 for i, branch in node.branches {
3105 if branch.is_else {
3106 else_idx = i
3107 continue
3108 }
3109 f.match_branch(branch, single_line, node.is_comptime)
3110 }
3111 if else_idx >= 0 {
3112 f.match_branch(node.branches[else_idx], single_line, node.is_comptime)
3113 }
3114 f.indent--
3115 f.write('}')
3116 f.or_expr(cond_or_expr)
3117}
3118
3119fn match_cond_with_trailing_or_expr(expr ast.Expr) (ast.Expr, ast.OrExpr) {
3120 match expr {
3121 ast.CallExpr {
3122 if expr.or_block.kind == .block {
3123 mut cond := expr
3124 or_expr := cond.or_block
3125 cond.or_block = ast.OrExpr{}
3126 return ast.Expr(cond), or_expr
3127 }
3128 }
3129 ast.Ident {
3130 if expr.or_expr.kind == .block {
3131 mut cond := expr
3132 or_expr := cond.or_expr
3133 cond.or_expr = ast.OrExpr{}
3134 return ast.Expr(cond), or_expr
3135 }
3136 }
3137 ast.IndexExpr {
3138 if expr.or_expr.kind == .block {
3139 mut cond := expr
3140 or_expr := cond.or_expr
3141 cond.or_expr = ast.OrExpr{}
3142 return ast.Expr(cond), or_expr
3143 }
3144 }
3145 ast.ParExpr {
3146 cond, or_expr := match_cond_with_trailing_or_expr(expr.expr)
3147 if or_expr.kind == .block {
3148 mut par_expr := expr
3149 par_expr.expr = cond
3150 return ast.Expr(par_expr), or_expr
3151 }
3152 }
3153 ast.PrefixExpr {
3154 if expr.op == .arrow && expr.or_block.kind == .block {
3155 mut cond := expr
3156 or_expr := cond.or_block
3157 cond.or_block = ast.OrExpr{}
3158 return ast.Expr(cond), or_expr
3159 }
3160 }
3161 ast.SelectorExpr {
3162 if expr.or_block.kind == .block {
3163 mut cond := expr
3164 or_expr := cond.or_block
3165 cond.or_block = ast.OrExpr{}
3166 return ast.Expr(cond), or_expr
3167 }
3168 }
3169 else {}
3170 }
3171
3172 return expr, ast.OrExpr{}
3173}
3174
3175pub fn (mut f Fmt) offset_of(node ast.OffsetOf) {
3176 f.write('__offsetof(${f.type_to_str_using_aliases(node.struct_type, f.mod2alias)}, ${node.field})')
3177}
3178
3179pub fn (mut f Fmt) or_expr(node ast.OrExpr) {
3180 match node.kind {
3181 .absent {}
3182 .block {
3183 if node.stmts.len == 0 {
3184 f.write(' or {')
3185 if node.pos.line_nr != node.pos.last_line {
3186 f.writeln('')
3187 }
3188 f.write('}')
3189 return
3190 } else if expr_is_single_line(node) {
3191 // the control stmts (return/break/continue...) print a newline inside them,
3192 // so, since this'll all be on one line, trim any possible whitespace
3193 str := f.node_str(node.stmts[0]).trim_space()
3194 single_line := ' or { ${str} }'
3195 if single_line.len + f.line_len <= max_len {
3196 f.write(single_line)
3197 return
3198 }
3199 }
3200 // Make it multiline if the blocks has at least two stmts
3201 // or a single line would be too long
3202 f.writeln(' or {')
3203 f.stmts(node.stmts)
3204 f.write('}')
3205 }
3206 .propagate_option {
3207 f.write('?')
3208 }
3209 .propagate_result {
3210 f.write('!')
3211 }
3212 }
3213}
3214
3215pub fn (mut f Fmt) par_expr(node ast.ParExpr) {
3216 mut expr := node.expr
3217 expr = expr.remove_par()
3218 requires_paren := expr !is ast.Ident || node.comments.len > 0
3219 if requires_paren {
3220 f.par_level++
3221 f.write('(')
3222 }
3223 pre_comments := node.comments.filter(it.pos.pos < expr.pos().pos)
3224 post_comments := node.comments[pre_comments.len..]
3225 if pre_comments.len > 0 {
3226 f.comments(pre_comments)
3227 f.write(' ')
3228 }
3229 f.expr(expr)
3230 if post_comments.len > 0 {
3231 f.comments(post_comments)
3232 f.write(' ')
3233 }
3234 if requires_paren {
3235 f.par_level--
3236 f.write(')')
3237 }
3238}
3239
3240pub fn (mut f Fmt) postfix_expr(node ast.PostfixExpr) {
3241 f.expr(node.expr)
3242 // `$if foo ?`
3243 if node.op == .question {
3244 f.write(' ?')
3245 } else {
3246 f.write('${node.op}')
3247 }
3248 if node.is_c2v_prefix {
3249 f.write('$')
3250 }
3251}
3252
3253pub fn (mut f Fmt) prefix_expr(node ast.PrefixExpr) {
3254 // !(a in b) => a !in b, !(a is b) => a !is b
3255 if node.op == .not && node.right is ast.ParExpr {
3256 if node.right.expr is ast.InfixExpr {
3257 if node.right.expr.op in [.key_in, .not_in, .key_is, .not_is]
3258 && node.right.expr.right !is ast.InfixExpr {
3259 f.expr(node.right.expr.left)
3260 match node.right.expr.op {
3261 .key_in { f.write(' !in ') }
3262 .not_in { f.write(' in ') }
3263 .key_is { f.write(' !is ') }
3264 .not_is { f.write(' is ') }
3265 else {}
3266 }
3267
3268 f.expr(node.right.expr.right)
3269 return
3270 }
3271 }
3272 }
3273 f.write(node.op.str())
3274 f.expr(node.right)
3275 f.or_expr(node.or_block)
3276}
3277
3278pub fn (mut f Fmt) range_expr(node ast.RangeExpr) {
3279 f.expr(node.low)
3280 if f.is_mbranch_expr && !f.is_index_expr {
3281 f.write('...')
3282 } else {
3283 f.write('..')
3284 }
3285 f.expr(node.high)
3286}
3287
3288pub fn (mut f Fmt) select_expr(node ast.SelectExpr) {
3289 f.writeln('select {')
3290 f.indent++
3291 for branch in node.branches {
3292 if branch.comment.text != '' {
3293 f.comment(branch.comment, same_line: true)
3294 f.writeln('')
3295 }
3296 if branch.is_else {
3297 f.write('else {')
3298 } else {
3299 f.single_line_if = true
3300 match branch.stmt {
3301 ast.ExprStmt { f.expr(branch.stmt.expr) }
3302 else { f.stmt(branch.stmt) }
3303 }
3304
3305 f.single_line_if = false
3306 f.write(' {')
3307 }
3308 if branch.stmts.len > 0 {
3309 f.writeln('')
3310 f.stmts(branch.stmts)
3311 }
3312 f.writeln('}')
3313 if branch.post_comments.len > 0 {
3314 f.comments(branch.post_comments, same_line: true)
3315 }
3316 }
3317 f.indent--
3318 f.write('}')
3319}
3320
3321pub fn (mut f Fmt) selector_expr(node ast.SelectorExpr) {
3322 // TODO(StunxFS): Even though we ignored the JS backend, the `v/gen/js/tests/js.v`
3323 // file was still formatted/transformed, so it is specifically ignored here. Fix this.
3324 if f.file.language != .js && node.expr is ast.StringLiteral && node.field_name == 'str'
3325 && !f.pref.backend.is_js()
3326 && !f.file.path.ends_with(os.join_path('v', 'gen', 'js', 'tests', 'js.v')) {
3327 f.write('c')
3328 f.expr(node.expr)
3329 return
3330 }
3331 f.expr(node.expr)
3332 f.write('.')
3333 f.write(node.field_name)
3334 f.or_expr(node.or_block)
3335}
3336
3337pub fn (mut f Fmt) size_of(node ast.SizeOf) {
3338 f.write('sizeof')
3339 if node.is_type && !node.guessed_type {
3340 // the new form was explicitly written in the source code; keep it:
3341 f.write('[')
3342 f.write(f.type_to_str_using_aliases(node.typ, f.mod2alias))
3343 f.write(']()')
3344 return
3345 }
3346 if node.is_type {
3347 f.write('(')
3348 f.write(f.type_to_str_using_aliases(node.typ, f.mod2alias))
3349 f.write(')')
3350 } else {
3351 f.write('(')
3352 f.expr(node.expr)
3353 f.write(')')
3354 }
3355}
3356
3357pub fn (mut f Fmt) is_ref_type(node ast.IsRefType) {
3358 f.write('isreftype')
3359 if node.is_type && !node.guessed_type {
3360 // the new form was explicitly written in the source code; keep it:
3361 f.write('[')
3362 f.write(f.type_to_str_using_aliases(node.typ, f.mod2alias))
3363 f.write(']()')
3364 return
3365 }
3366 if node.is_type {
3367 f.write('(')
3368 f.write(f.type_to_str_using_aliases(node.typ, f.mod2alias))
3369 f.write(')')
3370 } else {
3371 f.write('(')
3372 f.expr(node.expr)
3373 f.write(')')
3374 }
3375}
3376
3377pub fn (mut f Fmt) sql_expr(node ast.SqlExpr) {
3378 // sql app.db { select from Contributor where repo == id && user == 0 }
3379 f.write('sql ')
3380 f.expr(node.db_expr)
3381 f.writeln(' {')
3382 f.write('\t')
3383 if node.is_dynamic {
3384 f.write('dynamic ')
3385 }
3386 if node.is_insert {
3387 f.write('insert ')
3388 } else {
3389 f.write('select ')
3390 }
3391 if node.has_distinct {
3392 f.write('distinct ')
3393 }
3394 sym := f.table.sym(node.table_expr.typ)
3395 mut table_name := sym.name
3396 if !table_name.starts_with('C.') && !table_name.starts_with('JS.') {
3397 table_name = f.no_cur_mod(f.short_module(sym.name)) // TODO: f.type_to_str?
3398 }
3399 if node.aggregate_kind != .none {
3400 match node.aggregate_kind {
3401 .count {
3402 f.write('count ')
3403 }
3404 .sum, .avg, .min, .max {
3405 f.write('${node.aggregate_kind}(${node.aggregate_field}) ')
3406 }
3407 .none {}
3408 }
3409 } else if node.requested_fields.len > 0 {
3410 for i, requested_field in node.requested_fields {
3411 f.write(requested_field.name)
3412 if i < node.requested_fields.len - 1 {
3413 f.write(', ')
3414 }
3415 }
3416 } else {
3417 for i, fd in node.fields {
3418 f.write(fd.name)
3419 if i < node.fields.len - 1 {
3420 f.write(', ')
3421 }
3422 }
3423 }
3424 if node.aggregate_kind == .none && (node.requested_fields.len > 0 || node.fields.len > 0) {
3425 f.write(' ')
3426 }
3427 if node.is_insert {
3428 f.write('${node.inserted_var} into ${table_name}')
3429 } else {
3430 f.write('from ${table_name}')
3431 }
3432 // Format JOIN clauses
3433 for join in node.joins {
3434 f.writeln('')
3435 f.write('\t')
3436 match join.kind {
3437 .inner { f.write('join ') }
3438 .left { f.write('left join ') }
3439 .right { f.write('right join ') }
3440 .full_outer { f.write('full outer join ') }
3441 }
3442
3443 join_sym := f.table.sym(join.table_expr.typ)
3444 mut join_table_name := join_sym.name
3445 if !join_table_name.starts_with('C.') && !join_table_name.starts_with('JS.') {
3446 join_table_name = f.no_cur_mod(f.short_module(join_sym.name))
3447 }
3448 f.write('${join_table_name} on ')
3449 f.expr(join.on_expr)
3450 }
3451 if node.has_where {
3452 f.write(' where ')
3453 f.expr(node.where_expr)
3454 }
3455 if node.has_order {
3456 f.write(' order by ')
3457 f.expr(node.order_expr)
3458 if node.has_desc {
3459 f.write(' desc')
3460 }
3461 }
3462 if node.has_limit {
3463 f.write(' limit ')
3464 f.expr(node.limit_expr)
3465 }
3466 if node.has_offset {
3467 f.write(' offset ')
3468 f.expr(node.offset_expr)
3469 }
3470 f.writeln('')
3471 f.write('}')
3472 f.or_expr(node.or_expr)
3473}
3474
3475pub fn (mut f Fmt) sql_query_data_expr(node ast.SqlQueryDataExpr) {
3476 if node.items.len == 0 && node.end_comments.len == 0 {
3477 f.write('{}')
3478 return
3479 }
3480 f.writeln('{')
3481 f.indent++
3482 f.sql_query_data_items(node.items, node.end_comments)
3483 f.indent--
3484 f.write('}')
3485}
3486
3487fn (mut f Fmt) sql_query_data_items(items []ast.SqlQueryDataItem, end_comments []ast.Comment) {
3488 for idx, item in items {
3489 f.sql_query_data_comment_lines(sql_query_data_item_pre_comments(item))
3490 f.sql_query_data_item(item)
3491 if idx < items.len - 1 || end_comments.len > 0 {
3492 f.write(',')
3493 }
3494 item_end_comments := sql_query_data_item_end_comments(item)
3495 if item_end_comments.len > 0 {
3496 if item_end_comments[0].pos.line_nr == sql_query_data_item_last_line(item) {
3497 f.comments(item_end_comments, same_line: true, has_nl: true, level: .keep)
3498 } else {
3499 f.writeln('')
3500 f.sql_query_data_comment_lines(item_end_comments)
3501 }
3502 } else {
3503 f.writeln('')
3504 }
3505 }
3506 f.sql_query_data_comment_lines(end_comments)
3507}
3508
3509fn (mut f Fmt) sql_query_data_comment_lines(comments []ast.Comment) {
3510 for comment in comments {
3511 f.comment(comment)
3512 f.writeln('')
3513 }
3514}
3515
3516fn (mut f Fmt) sql_query_data_item(item ast.SqlQueryDataItem) {
3517 match item {
3518 ast.SqlQueryDataLeaf {
3519 f.expr(item.expr)
3520 }
3521 ast.SqlQueryDataIf {
3522 for idx, branch in item.branches {
3523 if idx == 0 {
3524 f.write('if ')
3525 f.expr(branch.cond)
3526 f.write(' ')
3527 } else if branch.cond is ast.EmptyExpr {
3528 f.write('else ')
3529 } else {
3530 f.write('else if ')
3531 f.expr(branch.cond)
3532 f.write(' ')
3533 }
3534 f.sql_query_data_branch_items(branch.items, branch.end_comments,
3535 sql_query_data_branch_is_single_line(branch))
3536 if idx < item.branches.len - 1 {
3537 f.write(' ')
3538 }
3539 }
3540 }
3541 }
3542}
3543
3544fn (mut f Fmt) sql_query_data_branch_items(items []ast.SqlQueryDataItem, end_comments []ast.Comment, keep_single_line bool) {
3545 if items.len == 0 && end_comments.len == 0 {
3546 f.write('{}')
3547 return
3548 }
3549 if keep_single_line {
3550 start_pos := f.out.len
3551 start_len := f.line_len
3552 f.write('{ ')
3553 f.sql_query_data_item(items[0])
3554 f.write(' }')
3555 if !f.out.after(start_pos).contains('\n') && f.line_len <= max_len {
3556 return
3557 }
3558 f.out.go_back_to(start_pos)
3559 f.line_len = start_len
3560 f.empty_line = start_len == 0
3561 }
3562 f.writeln('{')
3563 f.indent++
3564 f.sql_query_data_items(items, end_comments)
3565 f.indent--
3566 f.write('}')
3567}
3568
3569pub fn (mut f Fmt) char_literal(node ast.CharLiteral) {
3570 if node.val == r"\'" {
3571 f.write("`'`")
3572 return
3573 }
3574 if node.val.len == 1 {
3575 clit := node.val[0]
3576 if clit < 32 || clit > 127 || clit == 92 || clit == 96 {
3577 f.write('`\\x${clit.hex()}`')
3578 return
3579 }
3580 }
3581 f.write('`${node.val}`')
3582}
3583
3584pub fn (mut f Fmt) string_literal(node ast.StringLiteral) {
3585 quote := if node.val.contains("'") && !node.val.contains('"') { '"' } else { "'" }
3586 if node.is_raw {
3587 f.write('r')
3588 } else if node.language == ast.Language.c {
3589 f.write('c')
3590 } else if node.language == ast.Language.js {
3591 f.write('js')
3592 }
3593 if node.is_raw {
3594 f.write('${quote}${node.val}${quote}')
3595 } else {
3596 unescaped_val := node.val.replace('${bs}${bs}', '\x01').replace_each([
3597 "${bs}'",
3598 "'",
3599 '${bs}"',
3600 '"',
3601 ])
3602 s := unescaped_val.replace_each(['\x01', '${bs}${bs}', quote, '${bs}${quote}'])
3603 f.write('${quote}${s}${quote}')
3604 }
3605}
3606
3607pub fn (mut f Fmt) string_inter_literal(node ast.StringInterLiteral) {
3608 mut quote := "'"
3609 for val in node.vals {
3610 if val.contains('\\"') {
3611 quote = '"'
3612 break
3613 }
3614 if val.contains("\\'") {
3615 quote = "'"
3616 break
3617 }
3618 if val.contains('"') {
3619 quote = "'"
3620 }
3621 if val.contains("'") {
3622 quote = '"'
3623 }
3624 }
3625 // TODO: this code is very similar to ast.Expr.str()
3626 // serkonda7: it can not fully be replaced tho as ´f.expr()´ and `ast.Expr.str()`
3627 // work too different for the various exprs that are interpolated
3628 f.write(quote)
3629 for i, val in node.vals {
3630 unescaped_val := val.replace('${bs}${bs}', '\x01').replace_each([
3631 "${bs}'",
3632 "'",
3633 '${bs}"',
3634 '"',
3635 ])
3636 s := unescaped_val.replace_each(['\x01', '${bs}${bs}', quote, '${bs}${quote}'])
3637 f.write('${s}')
3638 if i >= node.exprs.len {
3639 break
3640 }
3641 f.write('$')
3642 fspec_str := node.get_fspec(i)
3643
3644 f.write('{')
3645 f.expr(node.exprs[i])
3646 f.write(fspec_str)
3647 f.write('}')
3648 }
3649 f.write(quote)
3650}
3651
3652pub fn (mut f Fmt) type_expr(node ast.TypeNode) {
3653 if node.stmt == ast.empty_stmt {
3654 f.write(f.type_to_str_using_aliases(node.typ, f.mod2alias))
3655 } else {
3656 f.struct_decl(ast.StructDecl{ fields: (node.stmt as ast.StructDecl).fields }, true)
3657 }
3658}
3659
3660pub fn (mut f Fmt) type_of(node ast.TypeOf) {
3661 f.write('typeof')
3662 if node.is_type {
3663 f.write('[')
3664 f.write(f.type_to_str_using_aliases(node.typ, f.mod2alias))
3665 f.write(']()')
3666 } else {
3667 f.write('(')
3668 f.expr(node.expr)
3669 f.write(')')
3670 }
3671}
3672
3673pub fn (mut f Fmt) unsafe_expr(node ast.UnsafeExpr) {
3674 single_line := node.pos.line_nr >= node.pos.last_line
3675 f.write('unsafe {')
3676 if single_line {
3677 f.write(' ')
3678 } else {
3679 f.writeln('')
3680 f.indent++
3681 f.empty_line = true
3682 }
3683 f.expr(node.expr)
3684 if single_line {
3685 f.write(' ')
3686 } else {
3687 f.writeln('')
3688 f.indent--
3689 }
3690 f.write('}')
3691}
3692
3693fn (mut f Fmt) trace[T](fbase string, x &T) {
3694 if f.file.path_base == fbase {
3695 println('> f.trace | ${fbase:-10s} | ${voidptr(x):16} | ${x}')
3696 }
3697}
3698