aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Gavin <danielgavin5@hotmail.com>2022-07-09 12:31:46 +0200
committerDaniel Gavin <danielgavin5@hotmail.com>2022-07-09 12:31:46 +0200
commit65cb2d90ff01e310381b0ad84b10e813bc47b08f (patch)
tree97e4e65883146726a57aff74ba6bfb855841731c
parent9b19888219305c3740f36d9490e08e04e148a413 (diff)
Add new snapshot system for odinfmt testing.
-rw-r--r--src/odin/printer/document.odin66
-rw-r--r--src/odin/printer/printer.odin7
-rw-r--r--src/odin/printer/visit.odin30
-rw-r--r--tests/builtin/builtin.odin27
-rw-r--r--tools/odinfmt/snapshot/snapshot.odin83
-rw-r--r--tools/odinfmt/tests.bat2
-rw-r--r--tools/odinfmt/tests.odin12
-rw-r--r--tools/odinfmt/tests/.snapshots/binary_expressions.odin47
-rw-r--r--tools/odinfmt/tests/.snapshots/calls.odin48
-rw-r--r--tools/odinfmt/tests/binary_expressions.odin25
-rw-r--r--tools/odinfmt/tests/calls.odin18
-rw-r--r--tools/odinfmt/tests/random/.snapshots/document.odin572
-rw-r--r--tools/odinfmt/tests/random/document.odin377
13 files changed, 1264 insertions, 50 deletions
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
+ }
+ }
+}
+