From 71b1281f1f81d64039f64d6cf4e730a47eabc75e Mon Sep 17 00:00:00 2001 From: DanielGavin Date: Thu, 16 Jun 2022 21:38:53 +0200 Subject: Update visit.odin --- src/odin/printer/visit.odin | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin index 4e07f02..08a4dca 100644 --- a/src/odin/printer/visit.odin +++ b/src/odin/printer/visit.odin @@ -1607,13 +1607,15 @@ repeat_space :: proc(amount: int) -> ^Document { get_node_length :: proc(node: ^ast.Node) -> int { #partial switch v in node.derived { case ^ast.Ident: - return len(v.name) + return strings.rune_count(v.name) case ^ast.Basic_Lit: - return len(v.tok.text) + return strings.rune_count(v.tok.text) case ^ast.Implicit_Selector_Expr: - return len(v.field.name) + 1 + return strings.rune_count(v.field.name) + 1 case ^ast.Binary_Expr: return 0 + case ^ast.Selector_Expr: + return get_node_length(v.expr) + strings.rune_count(v.op.text) + strings.rune_count(v.field.name) case: panic(fmt.aprintf("unhandled get_node_length case %v", node.derived)) } -- cgit v1.2.3 From 23feadba81e951e523dd37d14e068635408bc90b Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Mon, 20 Jun 2022 21:58:45 +0200 Subject: Print matrices with constant column and rows more nicely. --- src/odin/printer/visit.odin | 58 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin index 08a4dca..f0d718b 100644 --- a/src/odin/printer/visit.odin +++ b/src/odin/printer/visit.odin @@ -5,6 +5,7 @@ import "core:odin/tokenizer" import "core:strings" import "core:fmt" import "core:sort" +import "core:strconv" //right now the attribute order is not linearly parsed(bug?) @(private) @@ -1201,8 +1202,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = document = cons_with_nopl(document, visit_expr(p, v.type)) } - //If we call from the value declartion, we want it to be nicely newlined and aligned - if (should_align_comp_lit(p, v^) || contains_comments_in_range(p, v.pos, v.end)) && (called_from == .Value_Decl || called_from == .Assignment_Stmt) && len(v.elems) != 0 { + if matrix_type, ok := v.type.derived.(^ast.Matrix_Type); ok && is_matrix_type_constant(matrix_type) { + document = cons_with_opl(document, visit_begin_brace(p, v.pos, .Generic)) + + set_source_position(p, v.open) + document = cons(document, nest(p.indentation_count, cons(newline_position(p, 1, v.elems[0].pos), visit_matrix_comp_lit(p, v, matrix_type)))) + set_source_position(p, v.end) + + document = cons(document, cons(newline(1), text_position(p, "}", v.end))) + } + else if (should_align_comp_lit(p, v^) || contains_comments_in_range(p, v.pos, v.end)) && (called_from == .Value_Decl || called_from == .Assignment_Stmt) && len(v.elems) != 0 { document = cons_with_opl(document, visit_begin_brace(p, v.pos, .Generic)) set_source_position(p, v.open) @@ -1232,11 +1241,11 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = document = cons(document, text(")")) } case ^Pointer_Type: - return cons(text("^"), visit_expr(p, v.elem)) + document = cons(text("^"), visit_expr(p, v.elem)) case ^Multi_Pointer_Type: - return cons(text("[^]"), visit_expr(p, v.elem)) + document = cons(text("[^]"), visit_expr(p, v.elem)) case ^Implicit: - return text_token(p, v.tok) + document = text_token(p, v.tok) case ^Poly_Type: document = cons(text("$"), visit_expr(p, v.type)) @@ -1264,7 +1273,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = document = cons(document, text(",")) document = cons_with_opl(document, visit_expr(p, v.column_count)) document = cons(document, text("]")) - document = cons(document, visit_expr(p, v.elem)) + document = cons(group(document), visit_expr(p, v.elem)) case ^Matrix_Index_Expr: document = visit_expr(p, v.expr) document = cons(document, text("[")) @@ -1279,6 +1288,43 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = return cons(comments, document) } +@(private) +is_matrix_type_constant :: proc(matrix_type: ^ast.Matrix_Type) -> bool { + if row_count, is_lit := matrix_type.row_count.derived.(^ast.Basic_Lit); is_lit { + _, ok := strconv.parse_int(row_count.tok.text) + return ok + } + + if column_count, is_lit := matrix_type.column_count.derived.(^ast.Basic_Lit); is_lit { + _, ok := strconv.parse_int(column_count.tok.text) + return ok + } + + return false +} + +@(private) +visit_matrix_comp_lit :: proc(p: ^Printer, comp_lit: ^ast.Comp_Lit, matrix_type: ^ast.Matrix_Type) -> ^Document { + document := empty() + + //these values have already been validated + row_count, _ := strconv.parse_int(matrix_type.row_count.derived.(^ast.Basic_Lit).tok.text) + column_count, _ := strconv.parse_int(matrix_type.column_count.derived.(^ast.Basic_Lit).tok.text) + + for row := 0; row < row_count; row += 1 { + for column := 0; column < column_count; column += 1 { + document = cons(document, visit_expr(p, comp_lit.elems[column + row * row_count])) + document = cons(document, text(", ")) + } + if row != row_count - 1 { + document = cons(document, newline(1)) + } + } + + return document +} + + @(private) visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type, count := 0, same_line_spaces_before := 1) -> ^Document { set_source_position(p, begin) -- cgit v1.2.3 From f814d3e9791c9ef6dc3c002017f0d949224e6b44 Mon Sep 17 00:00:00 2001 From: DanielGavin Date: Tue, 21 Jun 2022 10:39:17 +0200 Subject: Fix matrix_type only be checked if type is not null --- src/odin/printer/visit.odin | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin index f0d718b..9a22c8b 100644 --- a/src/odin/printer/visit.odin +++ b/src/odin/printer/visit.odin @@ -1200,18 +1200,21 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = if v.type != nil { document = cons_with_nopl(document, visit_expr(p, v.type)) - } - - if matrix_type, ok := v.type.derived.(^ast.Matrix_Type); ok && is_matrix_type_constant(matrix_type) { - document = cons_with_opl(document, visit_begin_brace(p, v.pos, .Generic)) - set_source_position(p, v.open) - document = cons(document, nest(p.indentation_count, cons(newline_position(p, 1, v.elems[0].pos), visit_matrix_comp_lit(p, v, matrix_type)))) - set_source_position(p, v.end) + if matrix_type, ok := v.type.derived.(^ast.Matrix_Type); ok && is_matrix_type_constant(matrix_type) { + document = cons_with_opl(document, visit_begin_brace(p, v.pos, .Generic)) + + set_source_position(p, v.open) + document = cons(document, nest(p.indentation_count, cons(newline_position(p, 1, v.elems[0].pos), visit_matrix_comp_lit(p, v, matrix_type)))) + set_source_position(p, v.end) - document = cons(document, cons(newline(1), text_position(p, "}", v.end))) + document = cons(document, cons(newline(1), text_position(p, "}", v.end))) + + break + } } - else if (should_align_comp_lit(p, v^) || contains_comments_in_range(p, v.pos, v.end)) && (called_from == .Value_Decl || called_from == .Assignment_Stmt) && len(v.elems) != 0 { + + if (should_align_comp_lit(p, v^) || contains_comments_in_range(p, v.pos, v.end)) && (called_from == .Value_Decl || called_from == .Assignment_Stmt) && len(v.elems) != 0 { document = cons_with_opl(document, visit_begin_brace(p, v.pos, .Generic)) set_source_position(p, v.open) -- cgit v1.2.3 From 6070844d69cab5b4a5bb50473bd6ccc2343ff7dd Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Tue, 21 Jun 2022 23:22:40 +0200 Subject: Add new json variable for picking the space amount --- src/common/config.odin | 1 + src/server/format.odin | 1 + 2 files changed, 2 insertions(+) (limited to 'src') diff --git a/src/common/config.odin b/src/common/config.odin index 387e8b8..d90e5fa 100644 --- a/src/common/config.odin +++ b/src/common/config.odin @@ -28,6 +28,7 @@ Config :: struct { Format_Config :: struct { tabs: bool, characters: int, + spaces: int, } config: Config; \ No newline at end of file diff --git a/src/server/format.odin b/src/server/format.odin index 4c51943..3cf8de6 100644 --- a/src/server/format.odin +++ b/src/server/format.odin @@ -21,6 +21,7 @@ get_complete_format :: proc(document: ^common.Document, config: ^common.Config) style := printer.default_style style.max_characters = config.formatter.characters style.tabs = config.formatter.tabs + style.spaces = config.formatter.spaces prnt := printer.make_printer(style, context.temp_allocator) -- cgit v1.2.3 From b0e274b53936eac2c751d3d04a7b1bcd432cfe91 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 22 Jun 2022 00:08:21 +0200 Subject: Use the default values if not specified in json --- src/server/format.odin | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/server/format.odin b/src/server/format.odin index 3cf8de6..0035545 100644 --- a/src/server/format.odin +++ b/src/server/format.odin @@ -19,10 +19,16 @@ DocumentFormattingParams :: struct { get_complete_format :: proc(document: ^common.Document, config: ^common.Config) -> ([]TextEdit, bool) { style := printer.default_style - style.max_characters = config.formatter.characters style.tabs = config.formatter.tabs - style.spaces = config.formatter.spaces + if characters, ok := config.formatter.characters.(int); ok { + style.max_characters = characters + } + + if spaces, ok := config.formatter.spaces.(int); ok { + style.spaces = spaces + } + prnt := printer.make_printer(style, context.temp_allocator) if document.ast.syntax_error_count > 0 { -- cgit v1.2.3 From ff4801dfd8ee5113d1fba64aeede0f7412a91931 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 22 Jun 2022 00:09:15 +0200 Subject: Forgot file --- src/common/config.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/common/config.odin b/src/common/config.odin index d90e5fa..c510e57 100644 --- a/src/common/config.odin +++ b/src/common/config.odin @@ -27,8 +27,8 @@ Config :: struct { Format_Config :: struct { tabs: bool, - characters: int, - spaces: int, + characters: Maybe(int), + spaces: Maybe(int), } config: Config; \ No newline at end of file -- cgit v1.2.3 From 77e13fb4ecc4486fe2ea00b6a2f9c8b790150842 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sat, 25 Jun 2022 17:23:26 +0200 Subject: Removed the fill functionality for odinfmt --- src/odin/printer/document.odin | 24 ++------- src/odin/printer/visit.odin | 107 ++++++++++++++++++++++------------------- 2 files changed, 61 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/src/odin/printer/document.odin b/src/odin/printer/document.odin index 1f104a3..1e5ce3a 100644 --- a/src/odin/printer/document.odin +++ b/src/odin/printer/document.odin @@ -59,7 +59,6 @@ Document_Align :: struct { Document_Group_Mode :: enum { Flat, Break, - Fill, Fit, } @@ -162,15 +161,6 @@ group :: proc(grouped_document: ^Document, allocator := context.allocator) -> ^D return document } -fill_group :: proc(grouped_document: ^Document, allocator := context.allocator) -> ^Document { - document := new(Document, allocator) - document^ = Document_Group { - document = grouped_document, - mode = .Fill, - } - return document -} - cons :: proc(lhs: ^Document, rhs: ^Document, allocator := context.allocator) -> ^Document { document := new(Document, allocator) document^ = Document_Cons { @@ -251,8 +241,6 @@ fits :: proc(width: int, list: ^[dynamic]Tuple, consumed: ^int) -> bool { if data.mode == .Break && v.newline { consumed^ = start_width - width return true - } else if data.mode == .Fill && v.newline && width > 0 { - return true } else { width -= len(v.value) } @@ -266,7 +254,8 @@ fits :: proc(width: int, list: ^[dynamic]Tuple, consumed: ^int) -> bool { } consumed^ = start_width - width - return true + + return width <= 0 } format_newline :: proc(indentation: int, alignment: int, consumed: ^int, builder: ^strings.Builder, p: ^Printer) { @@ -318,11 +307,6 @@ format :: proc(width: int, list: ^[dynamic]Tuple, builder: ^strings.Builder, p: case Document_Break: if data.mode == .Break && v.newline { format_newline(data.indentation, data.alignment, &consumed, builder, p) - } else if data.mode == .Fill && consumed + len(v.value) < width { - strings.write_string(builder, v.value) - consumed += len(v.value) - } else if data.mode == .Fill && v.newline { - format_newline(data.indentation, data.alignment, &consumed, builder, p) } else { strings.write_string(builder, v.value) consumed += len(v.value) @@ -349,9 +333,7 @@ format :: proc(width: int, list: ^[dynamic]Tuple, builder: ^strings.Builder, p: else if fits(width-consumed, &l, &fits_consumed) && v.mode != .Break { append(list, Tuple {indentation = data.indentation, mode = .Flat, document = v.document, alignment = data.alignment}) } else { - if v.mode == .Fill { - append(list, Tuple {indentation = data.indentation, mode = .Fill, document = v.document, alignment = data.alignment}) - } else if v.mode == .Fit { + if v.mode == .Fit { append(list, Tuple {indentation = data.indentation, mode = .Fit, document = v.document, alignment = data.alignment}) } else { append(list, Tuple {indentation = data.indentation, mode = .Break, document = v.document, alignment = data.alignment}) diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin index 9a22c8b..4a279e2 100644 --- a/src/odin/printer/visit.odin +++ b/src/odin/printer/visit.odin @@ -307,11 +307,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) -> ^Do } if len(v.values) > 0 { - if is_values_binary(p, v.values) { - return cons(document, fill_group(cons_with_opl(group(lhs), align(fill_group(rhs))))) - } else { - return cons(document, group(cons_with_nopl(group(lhs), group(rhs)))) - } + return cons(document, group(cons_with_nopl(group(lhs), group(rhs)))) } else { return cons(document, group(lhs)) } @@ -322,16 +318,6 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) -> ^Do return empty() } -@(private) -is_values_binary :: proc(p: ^Printer, list: []^ast.Expr) -> bool { - for expr in list { - if _, bin := expr.derived.(^ast.Binary_Expr); bin { - return true - } - } - return false -} - @(private) visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, options := List_Options{}, called_from: Expr_Called_Type = .Generic) -> ^Document { if len(list) == 0 { @@ -622,7 +608,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener } if v.cond != nil { - if_document = cons_with_nopl(if_document, fill_group(visit_expr(p, v.cond))) + if_document = cons_with_nopl(if_document, group(visit_expr(p, v.cond))) } document = cons(document, group(hang(3, if_document))) @@ -702,11 +688,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener assign_document := group(cons_with_nopl(visit_exprs(p, v.lhs, {.Add_Comma, .Glue}), text(v.op.text))) if block_stmt { - if should_align_assignment_stmt(p, v^) { - assign_document = fill_group(cons(assign_document, align(cons(break_with_space(), visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))))) - } else { - assign_document = fill_group(cons(assign_document, cons(break_with_space(), visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt)))) - } + assign_document = group(cons(assign_document, nest(p.indentation_count, cons(break_with_space(), visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))))) } else { assign_document = cons_with_nopl(assign_document, visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt)) } @@ -731,7 +713,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if v.cond != nil { set_source_position(p, v.cond.pos) - for_document = cons_with_opl(for_document, fill_group(visit_expr(p, v.cond))) + for_document = cons_with_opl(for_document, group(visit_expr(p, v.cond))) } if v.post != nil { @@ -895,20 +877,6 @@ contains_do_in_expression :: proc(p: ^Printer, expr: ^ast.Expr) -> bool { return found_do } -@(private) -should_align_assignment_stmt :: proc(p: ^Printer, stmt: ast.Assign_Stmt) -> bool { - if len(stmt.rhs) == 0 { - return false - } - - for expr in stmt.rhs { - if _, ok := stmt.rhs[0].derived.(^ast.Binary_Expr); ok { - return true - } - } - return false -} - @(private) push_where_clauses :: proc(p: ^Printer, clauses: []^ast.Expr) -> ^Document { if len(clauses) == 0 { @@ -1174,11 +1142,12 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = case ^Selector_Expr: document = cons(visit_expr(p, v.expr), cons(text_token(p, v.op), visit_expr(p, v.field))) case ^Paren_Expr: - document = cons(text("("), cons(visit_expr(p, v.expr), text(")"))) + fmt.println(p.indentation_count) + document = group(cons(text("("), cons(nest(p.indentation_count, visit_expr(p, v.expr)), text(")")))) case ^Index_Expr: document = visit_expr(p, v.expr) document = cons(document, text("[")) - document = cons(document, fill_group(align(visit_expr(p, v.index)))) + document = cons(document, group(align(visit_expr(p, v.index)))) document = cons(document, text("]")) case ^Proc_Group: document = text_token(p, v.tok) @@ -1512,25 +1481,64 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) -> ^Document { } @(private) -visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) -> ^Document { - lhs: ^Document - rhs: ^Document - +extract_binary_expr :: proc(list: ^[dynamic]any, binary: ast.Binary_Expr) { if v, ok := binary.left.derived.(^ast.Binary_Expr); ok { - lhs = visit_binary_expr(p, v^) + extract_binary_expr(list, v^) } else { - lhs = visit_expr(p, binary.left) + append(list, binary.left) } + append(list, binary.op.text) + if v, ok := binary.right.derived.(^ast.Binary_Expr); ok { - rhs = visit_binary_expr(p, v^) + extract_binary_expr(list, v^) } else { - rhs = visit_expr(p, binary.right) + append(list, binary.right) } +} + + +@(private) +visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) -> ^Document { + list := make([dynamic]any) - op := text(binary.op.text) + extract_binary_expr(&list, binary) + + + first_order := empty() + second_order := empty() + + last_op := "" + + is_first_order :: proc(s: string) -> bool { + return s == "+" || s == "-" + } - return cons_with_nopl(lhs, cons_with_opl(op, rhs)) + for item, i in list { + switch v in item { + case ^ast.Expr: + if len(list) - 1 == i { + if is_first_order(last_op) { + first_order = cons(first_order, visit_expr(p, v)) + } else { + second_order = cons(second_order, visit_expr(p, v)) + first_order = cons(first_order, group(nest(p.indentation_count, second_order))) + } + } else { + last_op := list[i+1].(string) + + if is_first_order(last_op) { + first_order = cons(first_order, group(nest(p.indentation_count, second_order))) + second_order = empty() + first_order = cons(first_order, cons_with_nopl(visit_expr(p, v), cons(text(last_op), break_with_space()))) + } else { + second_order = cons(second_order, cons_with_nopl(visit_expr(p, v), cons(text(last_op), break_with_space()))) + } + } + } + } + + return group(nest(p.indentation_count, first_order)) } @(private) @@ -1543,8 +1551,9 @@ visit_call_exprs :: proc(p: ^Printer, call_expr: ^ast.Call_Expr) -> ^Document { if i == len(call_expr.args) - 1 && ellipsis { document = cons(document, text("..")) } + document = cons(document, group(visit_expr(p, expr))) - + if i != len(call_expr.args) - 1 { document = cons(document, text(",")) -- cgit v1.2.3 From 23386134013425a47fbfe43928d16f1420b55bfa Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Mon, 27 Jun 2022 00:25:16 +0200 Subject: Change the formatter handling of binary expressions --- src/odin/printer/visit.odin | 110 ++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin index 4a279e2..fccecf6 100644 --- a/src/odin/printer/visit.odin +++ b/src/odin/printer/visit.odin @@ -1,6 +1,7 @@ package odin_printer import "core:odin/ast" +import "core:odin/parser" import "core:odin/tokenizer" import "core:strings" import "core:fmt" @@ -307,7 +308,11 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) -> ^Do } if len(v.values) > 0 { - return cons(document, group(cons_with_nopl(group(lhs), group(rhs)))) + if is_values_binary(p, v.values) { + return cons(document, nest(p.indentation_count, group(cons_with_opl(group(lhs), group(rhs))))) + } else { + return cons(document, group(cons_with_nopl(group(lhs), group(rhs)))) + } } else { return cons(document, group(lhs)) } @@ -318,6 +323,16 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) -> ^Do return empty() } +@(private) +is_values_binary :: proc(p: ^Printer, list: []^ast.Expr) -> bool { + for expr in list { + if _, bin := expr.derived.(^ast.Binary_Expr); bin { + return true + } + } + return false +} + @(private) visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, options := List_Options{}, called_from: Expr_Called_Type = .Generic) -> ^Document { if len(list) == 0 { @@ -688,11 +703,11 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener assign_document := group(cons_with_nopl(visit_exprs(p, v.lhs, {.Add_Comma, .Glue}), text(v.op.text))) if block_stmt { - assign_document = group(cons(assign_document, nest(p.indentation_count, cons(break_with_space(), visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))))) + assign_document = cons_with_opl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))) } else { - assign_document = cons_with_nopl(assign_document, visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt)) + assign_document = cons_with_nopl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))) } - document = cons(document, group(assign_document)) + document = cons(document, group(nest(p.indentation_count, assign_document))) case ^Expr_Stmt: document = cons(document, visit_expr(p, v.expr)) case ^For_Stmt: @@ -1108,7 +1123,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = case ^Basic_Lit: document = text_token(p, v.tok) case ^Binary_Expr: - document = visit_binary_expr(p, v^) + document = visit_binary_expr(p, v^, true) case ^Implicit_Selector_Expr: document = cons(text("."), text_position(p, v.field.name, v.field.pos)) case ^Call_Expr: @@ -1142,7 +1157,6 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = case ^Selector_Expr: document = cons(visit_expr(p, v.expr), cons(text_token(p, v.op), visit_expr(p, v.field))) case ^Paren_Expr: - fmt.println(p.indentation_count) document = group(cons(text("("), cons(nest(p.indentation_count, visit_expr(p, v.expr)), text(")")))) case ^Index_Expr: document = visit_expr(p, v.expr) @@ -1170,7 +1184,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = if v.type != nil { document = cons_with_nopl(document, visit_expr(p, v.type)) - if matrix_type, ok := v.type.derived.(^ast.Matrix_Type); ok && is_matrix_type_constant(matrix_type) { + if matrix_type, ok := v.type.derived.(^ast.Matrix_Type); ok && len(v.elems) > 0 && is_matrix_type_constant(matrix_type) { document = cons_with_opl(document, visit_begin_brace(p, v.pos, .Generic)) set_source_position(p, v.open) @@ -1282,17 +1296,18 @@ visit_matrix_comp_lit :: proc(p: ^Printer, comp_lit: ^ast.Comp_Lit, matrix_type: //these values have already been validated row_count, _ := strconv.parse_int(matrix_type.row_count.derived.(^ast.Basic_Lit).tok.text) column_count, _ := strconv.parse_int(matrix_type.column_count.derived.(^ast.Basic_Lit).tok.text) - + for row := 0; row < row_count; row += 1 { for column := 0; column < column_count; column += 1 { - document = cons(document, visit_expr(p, comp_lit.elems[column + row * row_count])) + document = cons(document, visit_expr(p, comp_lit.elems[column + row * column_count])) document = cons(document, text(", ")) } - if row != row_count - 1 { + + if row_count - 1 != row { document = cons(document, newline(1)) } } - + return document } @@ -1480,65 +1495,40 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) -> ^Document { return document } -@(private) -extract_binary_expr :: proc(list: ^[dynamic]any, binary: ast.Binary_Expr) { - if v, ok := binary.left.derived.(^ast.Binary_Expr); ok { - extract_binary_expr(list, v^) - } else { - append(list, binary.left) - } - - append(list, binary.op.text) - - if v, ok := binary.right.derived.(^ast.Binary_Expr); ok { - extract_binary_expr(list, v^) - } else { - append(list, binary.right) - } -} - @(private) -visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) -> ^Document { - list := make([dynamic]any) - - extract_binary_expr(&list, binary) - +visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr, first := false) -> ^Document { + document := empty() - first_order := empty() - second_order := empty() + nest_first_expression := false - last_op := "" + if binary.left != nil { + if b, ok := binary.left.derived.(^ast.Binary_Expr); ok { + nest_first_expression = parser.token_precedence(nil, b.op.kind) != parser.token_precedence(nil, binary.op.kind) + document = cons(document, visit_binary_expr(p, b^)) + } else { + document = cons(document, visit_expr(p, binary.left)) + } + } - is_first_order :: proc(s: string) -> bool { - return s == "+" || s == "-" + if first { + if nest_first_expression { + document = nest(p.indentation_count, document) + } + document = group(document) } - for item, i in list { - switch v in item { - case ^ast.Expr: - if len(list) - 1 == i { - if is_first_order(last_op) { - first_order = cons(first_order, visit_expr(p, v)) - } else { - second_order = cons(second_order, visit_expr(p, v)) - first_order = cons(first_order, group(nest(p.indentation_count, second_order))) - } - } else { - last_op := list[i+1].(string) - - if is_first_order(last_op) { - first_order = cons(first_order, group(nest(p.indentation_count, second_order))) - second_order = empty() - first_order = cons(first_order, cons_with_nopl(visit_expr(p, v), cons(text(last_op), break_with_space()))) - } else { - second_order = cons(second_order, cons_with_nopl(visit_expr(p, v), cons(text(last_op), break_with_space()))) - } - } + document = cons_with_nopl(document, text(binary.op.text)) + + if binary.right != nil { + if b, ok := binary.right.derived.(^ast.Binary_Expr); ok { + document = cons_with_opl(document, group(nest(p.indentation_count, visit_binary_expr(p, b^)))) + } else { + document = cons_with_opl(document, group(nest(p.indentation_count, visit_expr(p, binary.right)))) } } - return group(nest(p.indentation_count, first_order)) + return document } @(private) -- cgit v1.2.3 From 0f780f6c0bc61294f735f2ccb7209226121e5918 Mon Sep 17 00:00:00 2001 From: DanielGavin Date: Tue, 28 Jun 2022 12:32:52 +0200 Subject: Remember to close the handle in `get_case_sensitive_path` --- src/common/util_windows.odin | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/common/util_windows.odin b/src/common/util_windows.odin index 194bd4d..716d4be 100644 --- a/src/common/util_windows.odin +++ b/src/common/util_windows.odin @@ -32,10 +32,10 @@ get_case_sensitive_path :: proc(path: string, allocator := context.temp_allocato file := win32.CreateFileW(&wide[0], 0, win32.FILE_SHARE_READ, nil, win32.OPEN_EXISTING, win32.FILE_FLAG_BACKUP_SEMANTICS, nil) if(file == win32.INVALID_HANDLE) - { + { log_last_error() - return ""; - } + return ""; + } buffer := make([]u16, 512, context.temp_allocator) @@ -43,6 +43,8 @@ get_case_sensitive_path :: proc(path: string, allocator := context.temp_allocato res, _ := win32.utf16_to_utf8(buffer[4:], allocator) + win32.CloseHandle(file) + return res } @@ -127,4 +129,4 @@ run_executable :: proc(command: string, stdout: ^[]byte) -> (u32, bool, []byte) win32.CloseHandle(stdout_read) return exit_code, true, stdout[0:index] -} \ No newline at end of file +} -- cgit v1.2.3 From 352221c8688e47364b0fa6c4e436f3e0a2dfc131 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Tue, 28 Jun 2022 22:43:40 +0200 Subject: odinfmt: fix proc tags being printed --- src/odin/printer/visit.odin | 53 +++++++++++++++++++++++++++++++++++++++------ src/server/analysis.odin | 12 +++++++++- src/server/format.odin | 7 ++++-- 3 files changed, 62 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin index fccecf6..98ab030 100644 --- a/src/odin/printer/visit.odin +++ b/src/odin/printer/visit.odin @@ -424,7 +424,7 @@ visit_union_exprs :: proc(p: ^Printer, union_type: ast.Union_Type, options := Li for expr, i in union_type.variants { if i == 0 && .Enforce_Newline in options { - comment, ok := visit_comments(p, union_type.variants[i].pos) + comment, ok := visit_comments(p, union_type.variants[i].pos) if _, is_nil := comment.(Document_Nil); !is_nil { comment = cons(comment, newline(1)) } @@ -533,6 +533,9 @@ visit_state_flags :: proc(p: ^Printer, flags: ast.Node_State_Flags) -> ^Document if .No_Bounds_Check in flags { return cons(text("#no_bounds_check"), break_with_no_newline()) } + if .Bounds_Check in flags { + return cons(text("#bounds_check"), break_with_no_newline()) + } return empty() } @@ -703,11 +706,18 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener assign_document := group(cons_with_nopl(visit_exprs(p, v.lhs, {.Add_Comma, .Glue}), text(v.op.text))) if block_stmt { - assign_document = cons_with_opl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))) + if is_values_binary(p, v.rhs) { + document = group(nest(p.indentation_count, cons_with_opl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))))) + } else { + document = cons_with_nopl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))) + } } else { - assign_document = cons_with_nopl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))) + if is_values_binary(p, v.rhs) { + document = group(nest(p.indentation_count, cons_with_opl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))))) + } else { + document = cons_with_nopl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))) + } } - document = cons(document, group(nest(p.indentation_count, assign_document))) case ^Expr_Stmt: document = cons(document, visit_expr(p, v.expr)) case ^For_Stmt: @@ -1251,7 +1261,10 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = document = cons(document, text("]")) document = cons(document, visit_expr(p, v.value)) case ^Helper_Type: - document = visit_expr(p, v.type) + if v.tok == .Hash { + document = cons(document, text("#type")) + } + document = cons_with_nopl(document, visit_expr(p, v.type)) case ^Matrix_Type: document = text_position(p, "matrix", v.pos) document = cons(document, text("[")) @@ -1441,6 +1454,29 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, options := List_Opt return document } +@(private) +visit_proc_tags :: proc(p: ^Printer, proc_tags: ast.Proc_Tags) -> ^Document { + document := empty() + + if .Bounds_Check in proc_tags { + document = cons_with_opl(document, text("#bounds_check")) + } + + if .No_Bounds_Check in proc_tags { + document = cons_with_opl(document, text("#no_bounds_check")) + } + + if .Optional_Ok in proc_tags { + document = cons_with_opl(document, text("#optional_ok")) + } + + if .Optional_Second in proc_tags { + document = cons_with_opl(document, text("#optional_second")) + } + + return document +} + @(private) visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) -> ^Document { document := text("proc") @@ -1492,6 +1528,8 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) -> ^Document { document = cons_with_nopl(document, text("!")) } + document = cons_with_opl(document, visit_proc_tags(p, proc_type.tags)) + return document } @@ -1511,11 +1549,12 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr, first := false) } } - if first { + if true { if nest_first_expression { document = nest(p.indentation_count, document) + document = group(document) } - document = group(document) + } document = cons_with_nopl(document, text(binary.op.text)) diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 5c40e7e..c22c1e8 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -1785,8 +1785,18 @@ make_symbol_union_from_ast :: proc(ast_context: ^AstContext, v: ast.Union_Type, symbol.name = "union" } + types := make([dynamic]^ast.Expr, ast_context.allocator) + + for variant in v.variants { + if v.poly_params != nil { + append(&types, clone_type(variant, ast_context.allocator, nil)) + } else { + append(&types, variant) + } + } + symbol.value = SymbolUnionValue { - types = v.variants, + types = types[:], } if v.poly_params != nil { diff --git a/src/server/format.odin b/src/server/format.odin index 0035545..219d7d9 100644 --- a/src/server/format.odin +++ b/src/server/format.odin @@ -1,9 +1,10 @@ package server import "shared:common" - import "shared:odin/printer" +import "core:log" + FormattingOptions :: struct { tabSize: uint, insertSpaces: bool, //tabs or spaces @@ -41,7 +42,9 @@ get_complete_format :: proc(document: ^common.Document, config: ^common.Config) src := printer.print(&prnt, &document.ast) - end_line := 0 + log.error(src) + + end_line := 0 end_charcter := 0 last := document.text[0] -- cgit v1.2.3 From 86e35a09c3c2e18e7cd4c73b7300f5236c9aa17e Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 29 Jun 2022 18:46:23 +0200 Subject: odinfmt: Add breaking support for lhs --- src/odin/printer/visit.odin | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin index 98ab030..411a6b4 100644 --- a/src/odin/printer/visit.odin +++ b/src/odin/printer/visit.odin @@ -308,7 +308,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) -> ^Do } if len(v.values) > 0 { - if is_values_binary(p, v.values) { + if is_values_nestable_assign(v.values) { return cons(document, nest(p.indentation_count, group(cons_with_opl(group(lhs), group(rhs))))) } else { return cons(document, group(cons_with_nopl(group(lhs), group(rhs)))) @@ -324,10 +324,11 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) -> ^Do } @(private) -is_values_binary :: proc(p: ^Printer, list: []^ast.Expr) -> bool { +is_values_nestable_assign :: proc(list: []^ast.Expr) -> bool { for expr in list { - if _, bin := expr.derived.(^ast.Binary_Expr); bin { - return true + #partial switch v in expr.derived { + case ^ast.Ident, ^ast.Binary_Expr, ^ast.Index_Expr, ^ast.Call_Expr, ^ast.Ternary_If_Expr, ^ast.Ternary_When_Expr, ^ast.Or_Else_Expr, ^ast.Or_Return_Expr: + return true } } return false @@ -705,19 +706,13 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener case ^Assign_Stmt: assign_document := group(cons_with_nopl(visit_exprs(p, v.lhs, {.Add_Comma, .Glue}), text(v.op.text))) - if block_stmt { - if is_values_binary(p, v.rhs) { - document = group(nest(p.indentation_count, cons_with_opl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))))) - } else { - document = cons_with_nopl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))) - } + rhs := visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt) + if is_values_nestable_assign(v.rhs) { + document = group(nest(p.indentation_count, cons_with_opl(assign_document, group(rhs)))) } else { - if is_values_binary(p, v.rhs) { - document = group(nest(p.indentation_count, cons_with_opl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))))) - } else { - document = cons_with_nopl(assign_document, group(visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt))) - } + document = group(cons_with_nopl(assign_document, group(rhs))) } + case ^Expr_Stmt: document = cons(document, visit_expr(p, v.expr)) case ^For_Stmt: @@ -1048,7 +1043,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = document = cons_with_nopl(document, text("{")) document = cons(document, text("}")) } else { - document = cons_with_opl(document, visit_begin_brace(p, v.pos, .Generic)) + document = cons_with_nopl(document, visit_begin_brace(p, v.pos, .Generic)) set_source_position(p, v.variants[0].pos) document = cons(document, nest(p.indentation_count, cons(newline_position(p, 1, v.pos), visit_union_exprs(p, v^, {.Add_Comma, .Trailing, .Enforce_Newline})))) set_source_position(p, v.end) @@ -1177,7 +1172,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = document = text_token(p, v.tok) if len(v.args) != 0 { - document = cons_with_opl(document, visit_begin_brace(p, v.pos, .Generic)) + document = cons_with_nopl(document, visit_begin_brace(p, v.pos, .Generic)) set_source_position(p, v.args[0].pos) document = cons(document, nest(p.indentation_count, cons(newline_position(p, 1, v.args[0].pos), visit_exprs(p, v.args, {.Add_Comma, .Trailing, .Enforce_Newline})))) document = cons(document, visit_end_brace(p, v.end, 1)) @@ -1195,7 +1190,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = document = cons_with_nopl(document, visit_expr(p, v.type)) if matrix_type, ok := v.type.derived.(^ast.Matrix_Type); ok && len(v.elems) > 0 && is_matrix_type_constant(matrix_type) { - document = cons_with_opl(document, visit_begin_brace(p, v.pos, .Generic)) + document = cons_with_nopl(document, visit_begin_brace(p, v.pos, .Generic)) set_source_position(p, v.open) document = cons(document, nest(p.indentation_count, cons(newline_position(p, 1, v.elems[0].pos), visit_matrix_comp_lit(p, v, matrix_type)))) @@ -1208,13 +1203,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, called_from: Expr_Called_Type = } if (should_align_comp_lit(p, v^) || contains_comments_in_range(p, v.pos, v.end)) && (called_from == .Value_Decl || called_from == .Assignment_Stmt) && len(v.elems) != 0 { - document = cons_with_opl(document, visit_begin_brace(p, v.pos, .Generic)) + document = cons_with_nopl(document, visit_begin_brace(p, v.pos, .Generic)) set_source_position(p, v.open) document = cons(document, nest(p.indentation_count, cons(newline_position(p, 1, v.elems[0].pos), visit_comp_lit_exprs(p, v^, {.Add_Comma, .Trailing, .Enforce_Newline})))) set_source_position(p, v.end) - document = cons(document, cons(newline(1), text_position(p, "}", v.end))) + document = cons(document, cons(newline(1), text_position(p, "}", v.end))) } else { document = cons(document, text("{")) document = cons(document, nest(p.indentation_count, cons(break_with(""), visit_exprs(p, v.elems, {.Add_Comma, .Group})))) -- cgit v1.2.3 From 9b19888219305c3740f36d9490e08e04e148a413 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 29 Jun 2022 23:09:14 +0200 Subject: Remove Maybe for now because json serialization messes it up. --- src/common/config.odin | 4 ++-- src/server/format.odin | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/common/config.odin b/src/common/config.odin index c510e57..d90e5fa 100644 --- a/src/common/config.odin +++ b/src/common/config.odin @@ -27,8 +27,8 @@ Config :: struct { Format_Config :: struct { tabs: bool, - characters: Maybe(int), - spaces: Maybe(int), + characters: int, + spaces: int, } config: Config; \ No newline at end of file diff --git a/src/server/format.odin b/src/server/format.odin index 219d7d9..041b204 100644 --- a/src/server/format.odin +++ b/src/server/format.odin @@ -22,12 +22,12 @@ get_complete_format :: proc(document: ^common.Document, config: ^common.Config) style := printer.default_style style.tabs = config.formatter.tabs - if characters, ok := config.formatter.characters.(int); ok { - style.max_characters = characters + if config.formatter.characters != 0 { + style.max_characters = config.formatter.characters } - if spaces, ok := config.formatter.spaces.(int); ok { - style.spaces = spaces + if config.formatter.spaces != 0 { + style.spaces = config.formatter.spaces } prnt := printer.make_printer(style, context.temp_allocator) -- cgit v1.2.3 From 65cb2d90ff01e310381b0ad84b10e813bc47b08f Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sat, 9 Jul 2022 12:31:46 +0200 Subject: Add new snapshot system for odinfmt testing. --- src/odin/printer/document.odin | 66 ++- src/odin/printer/printer.odin | 7 + src/odin/printer/visit.odin | 30 +- tests/builtin/builtin.odin | 27 - tools/odinfmt/snapshot/snapshot.odin | 83 +++ tools/odinfmt/tests.bat | 2 + tools/odinfmt/tests.odin | 12 + .../tests/.snapshots/binary_expressions.odin | 47 ++ tools/odinfmt/tests/.snapshots/calls.odin | 48 ++ tools/odinfmt/tests/binary_expressions.odin | 25 + tools/odinfmt/tests/calls.odin | 18 + .../odinfmt/tests/random/.snapshots/document.odin | 572 +++++++++++++++++++++ tools/odinfmt/tests/random/document.odin | 377 ++++++++++++++ 13 files changed, 1264 insertions(+), 50 deletions(-) delete mode 100644 tests/builtin/builtin.odin create mode 100644 tools/odinfmt/snapshot/snapshot.odin create mode 100644 tools/odinfmt/tests.bat create mode 100644 tools/odinfmt/tests.odin create mode 100644 tools/odinfmt/tests/.snapshots/binary_expressions.odin create mode 100644 tools/odinfmt/tests/.snapshots/calls.odin create mode 100644 tools/odinfmt/tests/binary_expressions.odin create mode 100644 tools/odinfmt/tests/calls.odin create mode 100644 tools/odinfmt/tests/random/.snapshots/document.odin create mode 100644 tools/odinfmt/tests/random/document.odin (limited to 'src') diff --git a/src/odin/printer/document.odin b/src/odin/printer/document.odin index 1e5ce3a..d77a490 100644 --- a/src/odin/printer/document.odin +++ b/src/odin/printer/document.odin @@ -13,6 +13,7 @@ Document :: union { Document_Cons, Document_If_Break, Document_Align, + Document_Nest_If_Break, } Document_Nil :: struct { @@ -33,6 +34,13 @@ Document_Nest :: struct { document: ^Document, } +Document_Nest_If_Break :: struct { + indentation: int, + alignment: int, + document: ^Document, + group_id: string, +} + Document_Break :: struct { value: string, newline: bool, @@ -45,6 +53,7 @@ Document_If_Break :: struct { Document_Group :: struct { document: ^Document, mode: Document_Group_Mode, + options: Document_Group_Options, } Document_Cons :: struct { @@ -62,6 +71,10 @@ Document_Group_Mode :: enum { Fit, } +Document_Group_Options :: struct { + id: string, +} + empty :: proc(allocator := context.allocator) -> ^Document { document := new(Document, allocator) document^ = Document_Nil {} @@ -93,6 +106,16 @@ nest :: proc(level: int, nested_document: ^Document, allocator := context.alloca return document } +nest_if_break :: proc(level: int, nested_document: ^Document, group_id := "", allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Nest_If_Break { + indentation = level, + document = nested_document, + group_id = group_id, + } + return document +} + hang :: proc(align: int, hanged_document: ^Document, allocator := context.allocator) -> ^Document { document := new(Document, allocator) document^ = Document_Nest { @@ -153,10 +176,11 @@ break_with_no_newline :: proc(allocator := context.allocator) -> ^Document { return break_with(" ", false, allocator) } -group :: proc(grouped_document: ^Document, allocator := context.allocator) -> ^Document { +group :: proc(grouped_document: ^Document, options := Document_Group_Options{}, allocator := context.allocator) -> ^Document { document := new(Document, allocator) document^ = Document_Group { document = grouped_document, + options = options, } return document } @@ -201,7 +225,7 @@ Tuple :: struct { document: ^Document, } -fits :: proc(width: int, list: ^[dynamic]Tuple, consumed: ^int) -> bool { +fits :: proc(width: int, list: ^[dynamic]Tuple) -> bool { assert(list != nil) start_width := width @@ -216,8 +240,7 @@ fits :: proc(width: int, list: ^[dynamic]Tuple, consumed: ^int) -> bool { for len(list) != 0 { data: Tuple = pop(list) - if width < 0 { - consumed^ = start_width + if width <= 0 { return false } @@ -225,7 +248,6 @@ fits :: proc(width: int, list: ^[dynamic]Tuple, consumed: ^int) -> bool { case Document_Nil: case Document_Newline: if v.amount > 0 { - consumed^ = start_width - width return true } case Document_Cons: @@ -239,7 +261,6 @@ fits :: proc(width: int, list: ^[dynamic]Tuple, consumed: ^int) -> bool { width -= len(v.value) case Document_Break: if data.mode == .Break && v.newline { - consumed^ = start_width - width return true } else { width -= len(v.value) @@ -248,14 +269,18 @@ fits :: proc(width: int, list: ^[dynamic]Tuple, consumed: ^int) -> bool { if data.mode == .Break { width -= len(v.value) } + case Document_Nest_If_Break: + if data.mode == .Break { + append(list, Tuple {indentation = data.indentation + v.indentation, mode = data.mode, document = v.document, alignment = data.alignment + v.alignment}) + } else { + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.document, alignment = data.alignment}) + } case Document_Group: - append(list, Tuple {indentation = data.indentation, mode = .Flat, document = v.document, alignment = data.alignment}) + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.document, alignment = data.alignment}) } } - consumed^ = start_width - width - - return width <= 0 + return width > 0 } format_newline :: proc(indentation: int, alignment: int, consumed: ^int, builder: ^strings.Builder, p: ^Printer) { @@ -267,7 +292,7 @@ format_newline :: proc(indentation: int, alignment: int, consumed: ^int, builder strings.write_string(builder, " ") } - consumed^ = indentation + alignment + consumed^ = indentation * p.indentation_width + alignment } format :: proc(width: int, list: ^[dynamic]Tuple, builder: ^strings.Builder, p: ^Printer) { @@ -292,7 +317,7 @@ format :: proc(width: int, list: ^[dynamic]Tuple, builder: ^strings.Builder, p: for i := 0; i < data.alignment; i += 1 { strings.write_string(builder, " ") } - consumed = data.indentation + data.alignment + consumed = data.indentation * p.indentation_width + data.alignment } case Document_Cons: append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.rhs, alignment = data.alignment}) @@ -316,21 +341,26 @@ format :: proc(width: int, list: ^[dynamic]Tuple, builder: ^strings.Builder, p: strings.write_string(builder, v.value) consumed += len(v.value) } + case Document_Nest_If_Break: + mode := v.group_id != "" ? p.group_modes[v.group_id] : data.mode + if mode == .Break { + append(list, Tuple {indentation = data.indentation + v.indentation, mode = data.mode, document = v.document, alignment = data.alignment + v.alignment}) + } else { + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.document, alignment = data.alignment}) + } case Document_Group: l := make([dynamic]Tuple, 0, len(list)) for element in list { append(&l, element) } - - append(&l, Tuple {indentation = data.indentation, mode = .Flat, document = v.document, alignment = data.alignment}) - - fits_consumed := 0 + append(&l, Tuple {indentation = data.indentation, mode = .Flat, document = v.document, alignment = data.alignment}) + if data.mode == .Fit { append(list, Tuple {indentation = data.indentation, mode = .Fit, document = v.document, alignment = data.alignment}) } - else if fits(width-consumed, &l, &fits_consumed) && v.mode != .Break { + else if fits(width-consumed, &l) && v.mode != .Break { append(list, Tuple {indentation = data.indentation, mode = .Flat, document = v.document, alignment = data.alignment}) } else { if v.mode == .Fit { @@ -339,6 +369,8 @@ format :: proc(width: int, list: ^[dynamic]Tuple, builder: ^strings.Builder, p: append(list, Tuple {indentation = data.indentation, mode = .Break, document = v.document, alignment = data.alignment}) } } + + p.group_modes[v.options.id] = list[len(list)-1].mode } } } diff --git a/src/odin/printer/printer.odin b/src/odin/printer/printer.odin index 5e1cb63..6f39c7c 100644 --- a/src/odin/printer/printer.odin +++ b/src/odin/printer/printer.odin @@ -22,8 +22,10 @@ Printer :: struct { indentation: string, newline: string, indentation_count: int, + indentation_width: int, disabled_lines: map[int]Disabled_Info, disabled_until_line: int, + group_modes: map[string]Document_Group_Mode, src: string, } @@ -37,6 +39,7 @@ Config :: struct { spaces: int, //Spaces per indentation newline_limit: int, //The limit of newlines between statements and declarations. tabs: bool, //Enable or disable tabs + tabs_width: int, convert_do: bool, //Convert all do statements to brace blocks brace_style: Brace_Style, indent_cases: bool, @@ -77,6 +80,7 @@ when ODIN_OS == .Windows { newline_limit = 2, convert_do = false, tabs = true, + tabs_width = 4, brace_style = ._1TBS, indent_cases = false, newline_style = .CRLF, @@ -88,6 +92,7 @@ when ODIN_OS == .Windows { newline_limit = 2, convert_do = false, tabs = true, + tabs_width = 4, brace_style = ._1TBS, indent_cases = false, newline_style = .LF, @@ -164,9 +169,11 @@ print_file :: proc(p: ^Printer, file: ^ast.File) -> string { if p.config.tabs { p.indentation = "\t" p.indentation_count = 1 + p.indentation_width = p.config.tabs_width } else { p.indentation_count = p.config.spaces p.indentation = " " + p.indentation_width = 1 } if p.config.newline_style == .CRLF { diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin index 411a6b4..bc5d57c 100644 --- a/src/odin/printer/visit.odin +++ b/src/odin/printer/visit.odin @@ -309,7 +309,11 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) -> ^Do if len(v.values) > 0 { if is_values_nestable_assign(v.values) { - return cons(document, nest(p.indentation_count, group(cons_with_opl(group(lhs), group(rhs))))) + return cons(document, group(nest(p.indentation_count, cons_with_opl(lhs, group(rhs))))) + } else if is_values_nestable_if_break_assign(v.values) { + assignments := cons(lhs, group(nest(p.indentation_count, break_with_space()), Document_Group_Options { id = "assignments"})) + assignments = cons(assignments, nest_if_break(p.indentation_count, group(rhs), "assignments")) + return cons(document, group(assignments)) } else { return cons(document, group(cons_with_nopl(group(lhs), group(rhs)))) } @@ -327,7 +331,19 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) -> ^Do is_values_nestable_assign :: proc(list: []^ast.Expr) -> bool { for expr in list { #partial switch v in expr.derived { - case ^ast.Ident, ^ast.Binary_Expr, ^ast.Index_Expr, ^ast.Call_Expr, ^ast.Ternary_If_Expr, ^ast.Ternary_When_Expr, ^ast.Or_Else_Expr, ^ast.Or_Return_Expr: + case ^ast.Ident, ^ast.Binary_Expr, ^ast.Index_Expr, ^ast.Ternary_If_Expr, ^ast.Ternary_When_Expr, ^ast.Or_Else_Expr, ^ast.Or_Return_Expr: + return true + } + } + return false +} + + +@(private) +is_values_nestable_if_break_assign :: proc(list: []^ast.Expr) -> bool { + for expr in list { + #partial switch v in expr.derived { + case ^ast.Call_Expr, ^ast.Comp_Lit: return true } } @@ -343,7 +359,6 @@ visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, options := List_Options{}, c document := empty() for expr, i in list { - p.source_position = expr.pos if .Enforce_Newline in options { @@ -704,15 +719,18 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener document = cons_with_nopl(document, visit_stmt(p, v.tag)) document = cons_with_nopl(document, visit_stmt(p, v.body, .Switch_Stmt)) case ^Assign_Stmt: - assign_document := group(cons_with_nopl(visit_exprs(p, v.lhs, {.Add_Comma, .Glue}), text(v.op.text))) + assign_document := group(cons(visit_exprs(p, v.lhs, {.Add_Comma, .Glue}), cons(text(" "), text(v.op.text)))) rhs := visit_exprs(p, v.rhs, {.Add_Comma}, .Assignment_Stmt) if is_values_nestable_assign(v.rhs) { document = group(nest(p.indentation_count, cons_with_opl(assign_document, group(rhs)))) + } else if is_values_nestable_if_break_assign(v.rhs) { + document = cons(assign_document, group(nest(p.indentation_count, break_with_space()), Document_Group_Options { id = "assignments"})) + document = cons(document, nest_if_break(p.indentation_count, group(rhs), "assignments")) + document = group(document) } else { - document = group(cons_with_nopl(assign_document, group(rhs))) + document = group(cons_with_opl(assign_document, group(rhs))) } - case ^Expr_Stmt: document = cons(document, visit_expr(p, v.expr)) case ^For_Stmt: diff --git a/tests/builtin/builtin.odin b/tests/builtin/builtin.odin deleted file mode 100644 index 009a7eb..0000000 --- a/tests/builtin/builtin.odin +++ /dev/null @@ -1,27 +0,0 @@ -package ols_builtin - -// Procedures -len :: proc(array: Array_Type) -> int --- -cap :: proc(array: Array_Type) -> int --- - -size_of :: proc($T: typeid) -> int --- -align_of :: proc($T: typeid) -> int --- -offset_of :: proc($T: typeid) -> uintptr --- -type_of :: proc(x: expr) -> type --- -type_info_of :: proc($T: typeid) -> ^runtime.Type_Info --- -typeid_of :: proc($T: typeid) -> typeid --- - -swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T --- - -complex :: proc(real, imag: Float) -> Complex_Type --- -quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type --- -real :: proc(value: Complex_Or_Quaternion) -> Float --- -imag :: proc(value: Complex_Or_Quaternion) -> Float --- -jmag :: proc(value: Quaternion) -> Float --- -kmag :: proc(value: Quaternion) -> Float --- -conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion --- - -min :: proc(values: ..T) -> T --- -max :: proc(values: ..T) -> T --- -abs :: proc(value: T) -> T --- -clamp :: proc(value, minimum, maximum: T) -> T --- \ No newline at end of file diff --git a/tools/odinfmt/snapshot/snapshot.odin b/tools/odinfmt/snapshot/snapshot.odin new file mode 100644 index 0000000..c4f860d --- /dev/null +++ b/tools/odinfmt/snapshot/snapshot.odin @@ -0,0 +1,83 @@ +package odinfmt_testing + +import "core:testing" +import "core:os" +import "core:path/filepath" +import "core:strings" +import "core:fmt" + +import "shared:odin/format" + +format_file :: proc(filepath: string, allocator := context.allocator) -> (string, bool) { + style := format.default_style + style.max_characters = 80 + style.newline_style = .LF //We want to make sure it works on linux and windows. + + if data, ok := os.read_entire_file(filepath, allocator); ok { + return format.format(filepath, string(data), style, {.Optional_Semicolons}, allocator); + } else { + return "", false; + } +} + +snapshot_directory :: proc(directory: string) -> bool { + matches, err := filepath.glob(fmt.tprintf("%v/*", directory)) + + if err != .None { + fmt.eprintf("Error in globbing directory: %v", directory) + } + + for match in matches { + if strings.contains(match, ".odin") { + snapshot_file(match) or_return + } + } + + for match in matches { + if !strings.contains(match, ".snapshots") { + if os.is_dir(match) { + snapshot_directory(match) + } + } + } + + return true +} + +snapshot_file :: proc(path: string) -> bool { + fmt.printf("Testing snapshot %v", path) + + + snapshot_path := filepath.join(elems = {filepath.dir(path, context.temp_allocator), "/.snapshots", filepath.base(path)}, allocator = context.temp_allocator); + + formatted, ok := format_file(path, context.temp_allocator) + + if !ok { + fmt.eprintf("Format failed on file %v", path) + return false + } + + if os.exists(snapshot_path) { + if snapshot_data, ok := os.read_entire_file(snapshot_path, context.temp_allocator); ok { + if cast(string)snapshot_data != formatted { + fmt.eprintf("Formatted file was different from snapshot file: %v", snapshot_path) + os.write_entire_file(fmt.tprintf("%v_failed", snapshot_path), transmute([]u8)formatted) + return false + } + } else { + fmt.eprintf("Failed to read snapshot file %v", snapshot_path) + return false + } + } else { + os.make_directory(filepath.dir(snapshot_path, context.temp_allocator)) + ok = os.write_entire_file(snapshot_path, transmute([]byte)formatted) + if !ok { + fmt.eprintf("Failed to write snapshot file %v", snapshot_path) + return false + } + } + + fmt.print(" - SUCCESS \n") + + return true +} \ No newline at end of file diff --git a/tools/odinfmt/tests.bat b/tools/odinfmt/tests.bat new file mode 100644 index 0000000..13172ef --- /dev/null +++ b/tools/odinfmt/tests.bat @@ -0,0 +1,2 @@ +odin run tests.odin -file -show-timings -collection:shared=../../src -out:tests.exe + diff --git a/tools/odinfmt/tests.odin b/tools/odinfmt/tests.odin new file mode 100644 index 0000000..025ea4b --- /dev/null +++ b/tools/odinfmt/tests.odin @@ -0,0 +1,12 @@ +package odinfmt_tests + +import "core:testing" +import "core:os" +import "core:fmt" + +import "snapshot" + + +main :: proc() { + snapshot.snapshot_directory("tests") +} diff --git a/tools/odinfmt/tests/.snapshots/binary_expressions.odin b/tools/odinfmt/tests/.snapshots/binary_expressions.odin new file mode 100644 index 0000000..7758a03 --- /dev/null +++ b/tools/odinfmt/tests/.snapshots/binary_expressions.odin @@ -0,0 +1,47 @@ +package odin_fmt_tests + + +binary :: proc() { + + addings := + 1111111111111111 + + 222222222222222222222 + + 3333333333333333333 + + 44444444444444444444 + + addings = + 1111111111111111 + + 222222222222222222222 + + 3333333333333333333 + + 44444444444444444444 + + + multiplication := + 1000 * 1111111111111111 + + 222222222222222222222 + + 3333333333333333333 * 2323 + + 44444444444444444444 + + multiplication = + 1000 * 1111111111111111 + + 222222222222222222222 + + 3333333333333333333 * 2323 + + 44444444444444444444 + + + logical_operator_1 := + 1111111111111111 == 222222222 && 111123411111 == 33333333434343433333 + + logical_operator_1 = + 1111111111111111 == 222222222 && 111123411111 == 33333333434343433333 + + logical_operator_2 := + 111111111111111111111111 == 22222222222222222222232323222 && + 111123432411123232311 == 3333332323232432333333333333333333 + + logical_operator_2 = + 111111111111111111111111 == 22222222222222222222232323222 && + 111123432411123232311 == 3333332323232432333333333333333333 + + +} diff --git a/tools/odinfmt/tests/.snapshots/calls.odin b/tools/odinfmt/tests/.snapshots/calls.odin new file mode 100644 index 0000000..3d6a3d3 --- /dev/null +++ b/tools/odinfmt/tests/.snapshots/calls.odin @@ -0,0 +1,48 @@ +package odin_fmt_tests + + +calls :: proc() { + + + aaaaaaaaaaaaa44444444777aaesult := + vk.CreateInsaaaaaadafaddddadwadawdwadawdawddgddaaaknce( + my_really_cool_call( + aaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccccccccc, + ddddddddddddddddddddddddddddddddddddd, + ), + ) + + + aaaaaaaaaaaaa44444444777aaesult = + vk.CreateInsaaaaaadafaddddadwadawdwadawdawddgddaaaknce( + my_really_cool_call( + aaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccccccccc, + ddddddddddddddddddddddddddddddddddddd, + ), + ) + + result := vk.CreateInsance( + my_really_cool_call( + aaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccccccccc, + ddddddddddddddddddddddddddddddddddddd, + ), + ) + + + result = vk.CreateInsance( + my_really_cool_call( + aaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccccccccc, + ddddddddddddddddddddddddddddddddddddd, + ), + ) + + +} diff --git a/tools/odinfmt/tests/binary_expressions.odin b/tools/odinfmt/tests/binary_expressions.odin new file mode 100644 index 0000000..85dc63c --- /dev/null +++ b/tools/odinfmt/tests/binary_expressions.odin @@ -0,0 +1,25 @@ +package odin_fmt_tests + + +binary :: proc() { + + addings := 1111111111111111 + 222222222222222222222 + 3333333333333333333 + 44444444444444444444 + + addings = 1111111111111111 + 222222222222222222222 + 3333333333333333333 + 44444444444444444444 + + + multiplication := 1000 * 1111111111111111 + 222222222222222222222 + 3333333333333333333 * 2323 + 44444444444444444444 + + multiplication = 1000 * 1111111111111111 + 222222222222222222222 + 3333333333333333333 * 2323 + 44444444444444444444 + + + logical_operator_1 := 1111111111111111 == 222222222 && 111123411111 == 33333333434343433333 + + logical_operator_1 = 1111111111111111 == 222222222 && 111123411111 == 33333333434343433333 + + logical_operator_2 := 111111111111111111111111 == 22222222222222222222232323222 && 111123432411123232311 == 3333332323232432333333333333333333 + + logical_operator_2 = 111111111111111111111111 == 22222222222222222222232323222 && 111123432411123232311 == 3333332323232432333333333333333333 + + +} \ No newline at end of file diff --git a/tools/odinfmt/tests/calls.odin b/tools/odinfmt/tests/calls.odin new file mode 100644 index 0000000..f450368 --- /dev/null +++ b/tools/odinfmt/tests/calls.odin @@ -0,0 +1,18 @@ +package odin_fmt_tests + + +calls :: proc() { + + + aaaaaaaaaaaaa44444444777aaesult := vk.CreateInsaaaaaadafaddddadwadawdwadawdawddgddaaaknce(my_really_cool_call(aaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccccccccccc, ddddddddddddddddddddddddddddddddddddd)) + + + aaaaaaaaaaaaa44444444777aaesult = vk.CreateInsaaaaaadafaddddadwadawdwadawdawddgddaaaknce(my_really_cool_call(aaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccccccccccc, ddddddddddddddddddddddddddddddddddddd)) + + result := vk.CreateInsance(my_really_cool_call(aaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccccccccccc, ddddddddddddddddddddddddddddddddddddd)) + + + result = vk.CreateInsance(my_really_cool_call(aaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccccccccccc, ddddddddddddddddddddddddddddddddddddd)) + + +} diff --git a/tools/odinfmt/tests/random/.snapshots/document.odin b/tools/odinfmt/tests/random/.snapshots/document.odin new file mode 100644 index 0000000..228d571 --- /dev/null +++ b/tools/odinfmt/tests/random/.snapshots/document.odin @@ -0,0 +1,572 @@ +package odin_printer + +import "core:strings" +import "core:fmt" + +Document :: union { + Document_Nil, + Document_Newline, + Document_Text, + Document_Nest, + Document_Break, + Document_Group, + Document_Cons, + Document_If_Break, + Document_Align, + Document_Nest_If_Break, +} + +Document_Nil :: struct {} + +Document_Newline :: struct { + amount: int, +} + +Document_Text :: struct { + value: string, +} + +Document_Nest :: struct { + indentation: int, + alignment: int, + document: ^Document, +} + +Document_Nest_If_Break :: struct { + indentation: int, + alignment: int, + document: ^Document, + group_id: string, +} + +Document_Break :: struct { + value: string, + newline: bool, +} + +Document_If_Break :: struct { + value: string, +} + +Document_Group :: struct { + document: ^Document, + mode: Document_Group_Mode, + options: Document_Group_Options, +} + +Document_Cons :: struct { + lhs: ^Document, + rhs: ^Document, +} + +Document_Align :: struct { + document: ^Document, +} + +Document_Group_Mode :: enum { + Flat, + Break, + Fit, +} + +Document_Group_Options :: struct { + id: string, +} + +empty :: proc(allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Nil{} + return document +} + +text :: proc(value: string, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Text { + value = value, + } + return document +} + +newline :: proc(amount: int, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Newline { + amount = amount, + } + return document +} + +nest :: proc( + level: int, + nested_document: ^Document, + allocator := context.allocator, +) -> ^Document { + document := new(Document, allocator) + document^ = Document_Nest { + indentation = level, + document = nested_document, + } + return document +} + +nest_if_break :: proc( + level: int, + nested_document: ^Document, + group_id := "", + allocator := context.allocator, +) -> ^Document { + document := new(Document, allocator) + document^ = Document_Nest_If_Break { + indentation = level, + document = nested_document, + group_id = group_id, + } + return document +} + +hang :: proc( + align: int, + hanged_document: ^Document, + allocator := context.allocator, +) -> ^Document { + document := new(Document, allocator) + document^ = Document_Nest { + alignment = align, + document = hanged_document, + } + return document +} + +enforce_fit :: proc( + fitted_document: ^Document, + allocator := context.allocator, +) -> ^Document { + document := new(Document, allocator) + document^ = Document_Group { + document = fitted_document, + mode = .Fit, + } + return document +} + +enforce_break :: proc( + fitted_document: ^Document, + allocator := context.allocator, +) -> ^Document { + document := new(Document, allocator) + document^ = Document_Group { + document = fitted_document, + mode = .Break, + } + return document +} + +align :: proc(aligned_document: ^Document, allocator := context.allocator) -> + ^Document { + document := new(Document, allocator) + document^ = Document_Align { + document = aligned_document, + } + return document +} + +if_break :: proc(value: string, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_If_Break { + value = value, + } + return document +} + +break_with :: proc( + value: string, + newline := true, + allocator := context.allocator, +) -> ^Document { + document := new(Document, allocator) + document^ = Document_Break { + value = value, + newline = newline, + } + return document +} + +break_with_space :: proc(allocator := context.allocator) -> ^Document { + return break_with(" ", true, allocator) +} + +break_with_no_newline :: proc(allocator := context.allocator) -> ^Document { + return break_with(" ", false, allocator) +} + +group :: proc( + grouped_document: ^Document, + options := Document_Group_Options{}, + allocator := context.allocator, +) -> ^Document { + document := new(Document, allocator) + document^ = Document_Group { + document = grouped_document, + options = options, + } + return document +} + +cons :: proc(lhs: ^Document, rhs: ^Document, allocator := context.allocator) -> + ^Document { + document := new(Document, allocator) + document^ = Document_Cons { + lhs = lhs, + rhs = rhs, + } + return document +} + +cons_with_opl :: proc( + lhs: ^Document, + rhs: ^Document, + allocator := context.allocator, +) -> ^Document { + if _, ok := lhs.(Document_Nil); ok { + return rhs + } + + if _, ok := rhs.(Document_Nil); ok { + return lhs + } + + return cons(lhs, cons(break_with_space(allocator), rhs), allocator) +} + +cons_with_nopl :: proc( + lhs: ^Document, + rhs: ^Document, + allocator := context.allocator, +) -> ^Document { + if _, ok := lhs.(Document_Nil); ok { + return rhs + } + + if _, ok := rhs.(Document_Nil); ok { + return lhs + } + + return cons(lhs, cons(break_with_no_newline(allocator), rhs), allocator) +} + +Tuple :: struct { + indentation: int, + alignment: int, + mode: Document_Group_Mode, + document: ^Document, +} + +fits :: proc(width: int, list: ^[dynamic]Tuple) -> bool { + assert(list != nil) + + start_width := width + width := width + + if len(list) == 0 { + return true + } else if width < 0 { + return false + } + + for len(list) != 0 { + data: Tuple = pop(list) + + if width <= 0 { + return false + } + + switch v in data.document { + case Document_Nil: + case Document_Newline: + if v.amount > 0 { + return true + } + case Document_Cons: + append( + list, + Tuple{ + indentation = data.indentation, + mode = data.mode, + document = v.rhs, + alignment = data.alignment, + }, + ) + append( + list, + Tuple{ + indentation = data.indentation, + mode = data.mode, + document = v.lhs, + alignment = data.alignment, + }, + ) + case Document_Align: + append( + list, + Tuple{ + indentation = 0, + mode = data.mode, + document = v.document, + alignment = start_width - width, + }, + ) + case Document_Nest: + append( + list, + Tuple{ + indentation = data.indentation + v.indentation, + mode = data.mode, + document = v.document, + alignment = data.alignment + v.alignment, + }, + ) + case Document_Text: + width -= len(v.value) + case Document_Break: + if data.mode == .Break && v.newline { + return true + } else { + width -= len(v.value) + } + case Document_If_Break: + if data.mode == .Break { + width -= len(v.value) + } + case Document_Nest_If_Break: + if data.mode == .Break { + append( + list, + Tuple{ + indentation = data.indentation + v.indentation, + mode = data.mode, + document = v.document, + alignment = data.alignment + v.alignment, + }, + ) + } else { + append( + list, + Tuple{ + indentation = data.indentation, + mode = data.mode, + document = v.document, + alignment = data.alignment, + }, + ) + } + case Document_Group: + append( + list, + Tuple{ + indentation = data.indentation, + mode = data.mode, + document = v.document, + alignment = data.alignment, + }, + ) + } + } + + return width > 0 +} + +format_newline :: proc( + indentation: int, + alignment: int, + consumed: ^int, + builder: ^strings.Builder, + p: ^Printer, +) { + strings.write_string(builder, p.newline) + for i := 0; i < indentation; i += 1 { + strings.write_string(builder, p.indentation) + } + for i := 0; i < alignment; i += 1 { + strings.write_string(builder, " ") + } + + consumed^ = indentation * p.indentation_width + alignment +} + +format :: proc( + width: int, + list: ^[dynamic]Tuple, + builder: ^strings.Builder, + p: ^Printer, +) { + assert(list != nil) + assert(builder != nil) + + consumed := 0 + + for len(list) != 0 { + data: Tuple = pop(list) + + switch v in data.document { + case Document_Nil: + case Document_Newline: + if v.amount > 0 { + for i := 0; i < v.amount; i += 1 { + strings.write_string(builder, p.newline) + } + for i := 0; i < data.indentation; i += 1 { + strings.write_string(builder, p.indentation) + } + for i := 0; i < data.alignment; i += 1 { + strings.write_string(builder, " ") + } + consumed = + data.indentation * p.indentation_width + data.alignment + } + case Document_Cons: + append( + list, + Tuple{ + indentation = data.indentation, + mode = data.mode, + document = v.rhs, + alignment = data.alignment, + }, + ) + append( + list, + Tuple{ + indentation = data.indentation, + mode = data.mode, + document = v.lhs, + alignment = data.alignment, + }, + ) + case Document_Nest: + append( + list, + Tuple{ + indentation = data.indentation + v.indentation, + mode = data.mode, + document = v.document, + alignment = data.alignment + v.alignment, + }, + ) + case Document_Align: + append( + list, + Tuple{ + indentation = 0, + mode = data.mode, + document = v.document, + alignment = consumed, + }, + ) + case Document_Text: + strings.write_string(builder, v.value) + consumed += len(v.value) + case Document_Break: + if data.mode == .Break && v.newline { + format_newline( + data.indentation, + data.alignment, + &consumed, + builder, + p, + ) + } else { + strings.write_string(builder, v.value) + consumed += len(v.value) + } + case Document_If_Break: + if data.mode == .Break { + strings.write_string(builder, v.value) + consumed += len(v.value) + } + case Document_Nest_If_Break: + mode := v.group_id != "" ? p.group_modes[v.group_id] : data.mode + if mode == .Break { + append( + list, + Tuple{ + indentation = data.indentation + v.indentation, + mode = data.mode, + document = v.document, + alignment = data.alignment + v.alignment, + }, + ) + } else { + append( + list, + Tuple{ + indentation = data.indentation, + mode = data.mode, + document = v.document, + alignment = data.alignment, + }, + ) + } + case Document_Group: + l := make([dynamic]Tuple, 0, len(list)) + + for element in list { + append(&l, element) + } + + append( + &l, + Tuple{ + indentation = data.indentation, + mode = .Flat, + document = v.document, + alignment = data.alignment, + }, + ) + + if data.mode == .Fit { + append( + list, + Tuple{ + indentation = data.indentation, + mode = .Fit, + document = v.document, + alignment = data.alignment, + }, + ) + } else if fits(width - consumed, &l) && v.mode != .Break { + append( + list, + Tuple{ + indentation = data.indentation, + mode = .Flat, + document = v.document, + alignment = data.alignment, + }, + ) + } else { + if v.mode == .Fit { + append( + list, + Tuple{ + indentation = data.indentation, + mode = .Fit, + document = v.document, + alignment = data.alignment, + }, + ) + } else { + append( + list, + Tuple{ + indentation = data.indentation, + mode = .Break, + document = v.document, + alignment = data.alignment, + }, + ) + } + } + + p.group_modes[v.options.id] = list[len(list) - 1].mode + } + } +} diff --git a/tools/odinfmt/tests/random/document.odin b/tools/odinfmt/tests/random/document.odin new file mode 100644 index 0000000..d77a490 --- /dev/null +++ b/tools/odinfmt/tests/random/document.odin @@ -0,0 +1,377 @@ +package odin_printer + +import "core:strings" +import "core:fmt" + +Document :: union { + Document_Nil, + Document_Newline, + Document_Text, + Document_Nest, + Document_Break, + Document_Group, + Document_Cons, + Document_If_Break, + Document_Align, + Document_Nest_If_Break, +} + +Document_Nil :: struct { + +} + +Document_Newline :: struct { + amount: int, +} + +Document_Text :: struct { + value: string, +} + +Document_Nest :: struct { + indentation: int, + alignment: int, + document: ^Document, +} + +Document_Nest_If_Break :: struct { + indentation: int, + alignment: int, + document: ^Document, + group_id: string, +} + +Document_Break :: struct { + value: string, + newline: bool, +} + +Document_If_Break :: struct { + value: string, +} + +Document_Group :: struct { + document: ^Document, + mode: Document_Group_Mode, + options: Document_Group_Options, +} + +Document_Cons :: struct { + lhs: ^Document, + rhs: ^Document, +} + +Document_Align :: struct { + document: ^Document, +} + +Document_Group_Mode :: enum { + Flat, + Break, + Fit, +} + +Document_Group_Options :: struct { + id: string, +} + +empty :: proc(allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Nil {} + return document +} + +text :: proc(value: string, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Text { + value = value, + } + return document +} + +newline :: proc(amount: int, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Newline { + amount = amount, + } + return document +} + +nest :: proc(level: int, nested_document: ^Document, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Nest { + indentation = level, + document = nested_document, + } + return document +} + +nest_if_break :: proc(level: int, nested_document: ^Document, group_id := "", allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Nest_If_Break { + indentation = level, + document = nested_document, + group_id = group_id, + } + return document +} + +hang :: proc(align: int, hanged_document: ^Document, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Nest { + alignment = align, + document = hanged_document, + } + return document +} + +enforce_fit :: proc(fitted_document: ^Document, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Group { + document = fitted_document, + mode = .Fit, + } + return document +} + +enforce_break :: proc(fitted_document: ^Document, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Group { + document = fitted_document, + mode = .Break, + } + return document +} + +align :: proc(aligned_document: ^Document, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Align { + document = aligned_document, + } + return document +} + +if_break :: proc(value: string, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_If_Break { + value = value, + } + return document +} + +break_with :: proc(value: string, newline := true, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Break { + value = value, + newline = newline, + } + return document +} + +break_with_space :: proc(allocator := context.allocator) -> ^Document { + return break_with(" ", true, allocator) +} + +break_with_no_newline :: proc(allocator := context.allocator) -> ^Document { + return break_with(" ", false, allocator) +} + +group :: proc(grouped_document: ^Document, options := Document_Group_Options{}, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Group { + document = grouped_document, + options = options, + } + return document +} + +cons :: proc(lhs: ^Document, rhs: ^Document, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Cons { + lhs = lhs, + rhs = rhs, + } + return document +} + +cons_with_opl :: proc(lhs: ^Document, rhs: ^Document, allocator := context.allocator) -> ^Document { + if _, ok := lhs.(Document_Nil); ok { + return rhs + } + + if _, ok := rhs.(Document_Nil); ok { + return lhs + } + + return cons(lhs, cons(break_with_space(allocator), rhs), allocator) +} + +cons_with_nopl:: proc(lhs: ^Document, rhs: ^Document, allocator := context.allocator) -> ^Document { + if _, ok := lhs.(Document_Nil); ok { + return rhs + } + + if _, ok := rhs.(Document_Nil); ok { + return lhs + } + + return cons(lhs, cons(break_with_no_newline(allocator), rhs), allocator) +} + +Tuple :: struct { + indentation: int, + alignment: int, + mode: Document_Group_Mode, + document: ^Document, +} + +fits :: proc(width: int, list: ^[dynamic]Tuple) -> bool { + assert(list != nil) + + start_width := width + width := width + + if len(list) == 0 { + return true + } else if width < 0 { + return false + } + + for len(list) != 0 { + data: Tuple = pop(list) + + if width <= 0 { + return false + } + + switch v in data.document { + case Document_Nil: + case Document_Newline: + if v.amount > 0 { + return true + } + case Document_Cons: + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.rhs, alignment = data.alignment}) + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.lhs, alignment = data.alignment}) + case Document_Align: + append(list, Tuple {indentation = 0, mode = data.mode, document = v.document, alignment = start_width - width}) + case Document_Nest: + append(list, Tuple {indentation = data.indentation + v.indentation, mode = data.mode, document = v.document, alignment = data.alignment + v.alignment}) + case Document_Text: + width -= len(v.value) + case Document_Break: + if data.mode == .Break && v.newline { + return true + } else { + width -= len(v.value) + } + case Document_If_Break: + if data.mode == .Break { + width -= len(v.value) + } + case Document_Nest_If_Break: + if data.mode == .Break { + append(list, Tuple {indentation = data.indentation + v.indentation, mode = data.mode, document = v.document, alignment = data.alignment + v.alignment}) + } else { + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.document, alignment = data.alignment}) + } + case Document_Group: + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.document, alignment = data.alignment}) + } + } + + return width > 0 +} + +format_newline :: proc(indentation: int, alignment: int, consumed: ^int, builder: ^strings.Builder, p: ^Printer) { + strings.write_string(builder, p.newline) + for i := 0; i < indentation; i += 1 { + strings.write_string(builder, p.indentation) + } + for i := 0; i < alignment; i += 1 { + strings.write_string(builder, " ") + } + + consumed^ = indentation * p.indentation_width + alignment +} + +format :: proc(width: int, list: ^[dynamic]Tuple, builder: ^strings.Builder, p: ^Printer) { + assert(list != nil) + assert(builder != nil) + + consumed := 0 + + for len(list) != 0 { + data: Tuple = pop(list) + + switch v in data.document { + case Document_Nil: + case Document_Newline: + if v.amount > 0 { + for i := 0; i < v.amount; i += 1 { + strings.write_string(builder, p.newline) + } + for i := 0; i < data.indentation; i += 1 { + strings.write_string(builder, p.indentation) + } + for i := 0; i < data.alignment; i += 1 { + strings.write_string(builder, " ") + } + consumed = data.indentation * p.indentation_width + data.alignment + } + case Document_Cons: + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.rhs, alignment = data.alignment}) + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.lhs, alignment = data.alignment}) + case Document_Nest: + append(list, Tuple {indentation = data.indentation + v.indentation, mode = data.mode, document = v.document, alignment = data.alignment + v.alignment}) + case Document_Align: + append(list, Tuple {indentation = 0, mode = data.mode, document = v.document, alignment = consumed}) + case Document_Text: + strings.write_string(builder, v.value) + consumed += len(v.value) + case Document_Break: + if data.mode == .Break && v.newline { + format_newline(data.indentation, data.alignment, &consumed, builder, p) + } else { + strings.write_string(builder, v.value) + consumed += len(v.value) + } + case Document_If_Break: + if data.mode == .Break { + strings.write_string(builder, v.value) + consumed += len(v.value) + } + case Document_Nest_If_Break: + mode := v.group_id != "" ? p.group_modes[v.group_id] : data.mode + if mode == .Break { + append(list, Tuple {indentation = data.indentation + v.indentation, mode = data.mode, document = v.document, alignment = data.alignment + v.alignment}) + } else { + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.document, alignment = data.alignment}) + } + case Document_Group: + l := make([dynamic]Tuple, 0, len(list)) + + for element in list { + append(&l, element) + } + + append(&l, Tuple {indentation = data.indentation, mode = .Flat, document = v.document, alignment = data.alignment}) + + if data.mode == .Fit { + append(list, Tuple {indentation = data.indentation, mode = .Fit, document = v.document, alignment = data.alignment}) + } + else if fits(width-consumed, &l) && v.mode != .Break { + append(list, Tuple {indentation = data.indentation, mode = .Flat, document = v.document, alignment = data.alignment}) + } else { + if v.mode == .Fit { + append(list, Tuple {indentation = data.indentation, mode = .Fit, document = v.document, alignment = data.alignment}) + } else { + append(list, Tuple {indentation = data.indentation, mode = .Break, document = v.document, alignment = data.alignment}) + } + } + + p.group_modes[v.options.id] = list[len(list)-1].mode + } + } +} + -- cgit v1.2.3 From b6c3746721a40948151485052164e756f24bb20e Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sat, 9 Jul 2022 12:38:04 +0200 Subject: odinfmt: Fix crash --- src/odin/printer/visit.odin | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin index bc5d57c..a8482cc 100644 --- a/src/odin/printer/visit.odin +++ b/src/odin/printer/visit.odin @@ -1555,7 +1555,10 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr, first := false) if binary.left != nil { if b, ok := binary.left.derived.(^ast.Binary_Expr); ok { - nest_first_expression = parser.token_precedence(nil, b.op.kind) != parser.token_precedence(nil, binary.op.kind) + pa := parser.Parser { + allow_in_expr = true, + } + nest_first_expression = parser.token_precedence(&pa, b.op.kind) != parser.token_precedence(nil, binary.op.kind) document = cons(document, visit_binary_expr(p, b^)) } else { document = cons(document, visit_expr(p, binary.left)) -- cgit v1.2.3 From fb5b19056827afbad2133f486d68d2ea162930e7 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sat, 9 Jul 2022 12:41:30 +0200 Subject: typo --- src/odin/printer/visit.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin index a8482cc..14d5d4e 100644 --- a/src/odin/printer/visit.odin +++ b/src/odin/printer/visit.odin @@ -1558,7 +1558,7 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr, first := false) pa := parser.Parser { allow_in_expr = true, } - nest_first_expression = parser.token_precedence(&pa, b.op.kind) != parser.token_precedence(nil, binary.op.kind) + nest_first_expression = parser.token_precedence(&pa, b.op.kind) != parser.token_precedence(&pa, binary.op.kind) document = cons(document, visit_binary_expr(p, b^)) } else { document = cons(document, visit_expr(p, binary.left)) -- cgit v1.2.3