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