From bc55f5beea4ddd64ce4a2996d64095e145f26cfc Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sun, 28 Nov 2021 16:43:50 +0100 Subject: Add formatter to ols --- src/common/config.odin | 7 + src/odin/printer/document.odin | 295 ++++++++++ src/odin/printer/printer.odin | 150 +++++ src/odin/printer/visit.odin | 1239 ++++++++++++++++++++++++++++++++++++++++ src/server/format.odin | 11 +- src/server/requests.odin | 15 +- src/server/types.odin | 4 +- 7 files changed, 1714 insertions(+), 7 deletions(-) create mode 100644 src/odin/printer/document.odin create mode 100644 src/odin/printer/printer.odin create mode 100644 src/odin/printer/visit.odin (limited to 'src') diff --git a/src/common/config.odin b/src/common/config.odin index df12845..89edb06 100644 --- a/src/common/config.odin +++ b/src/common/config.odin @@ -15,6 +15,13 @@ Config :: struct { enable_semantic_tokens: bool, enable_procedure_context: bool, thread_count: int, + file_log: bool, + formatter: Format_Config, +} + +Format_Config :: struct { + tabs: bool, + characters: int, } config: Config; \ No newline at end of file diff --git a/src/odin/printer/document.odin b/src/odin/printer/document.odin new file mode 100644 index 0000000..8a563d8 --- /dev/null +++ b/src/odin/printer/document.odin @@ -0,0 +1,295 @@ +package odin_printer + +import "core:strings" + +Document :: union { + Document_Nil, + Document_Newline, + Document_Text, + Document_Nest, + Document_Break, + Document_Group, + Document_Cons, + Document_If_Break, +} + +Document_Nil :: struct { + +} + +Document_Newline :: struct { + amount: int, +} + +Document_Text :: struct { + value: string, +} + +Document_Nest :: struct { + level: int, + document: ^Document, +} + +Document_Break :: struct { + value: string, + newline: bool, +} + +Document_If_Break :: struct { + value: string, +} + +Document_Group :: struct { + document: ^Document, + fill: bool, +} + +Document_Cons :: struct { + lhs: ^Document, + rhs: ^Document, +} + +Document_Group_Mode :: enum { + Flat, + Break, + Fill, +} + +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 { + level = level, + document = nested_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, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Group { + document = grouped_document, + } + return document +} + +fill_group :: proc(grouped_document: ^Document, allocator := context.allocator) -> ^Document { + document := new(Document, allocator) + document^ = Document_Group { + document = grouped_document, + fill = true, + } + 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, + mode: Document_Group_Mode, + document: ^Document, +} + +fits :: proc(width: int, list: ^[dynamic]Tuple) -> bool { + assert(list != nil) + + 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: + return true + case Document_Cons: + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.rhs}) + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.lhs}) + case Document_Nest: + append(list, Tuple {indentation = data.indentation + v.level, mode = data.mode, document = v.document}) + 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_Group: + append(list, Tuple {indentation = data.indentation, mode = .Flat, document = v.document}) + } + } + + return true +} + +format_newline :: proc(indentation: 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) + } + consumed^ = indentation +} + +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) + } + consumed = data.indentation + } + case Document_Cons: + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.rhs}) + append(list, Tuple {indentation = data.indentation, mode = data.mode, document = v.lhs}) + case Document_Nest: + append(list, Tuple {indentation = data.indentation + v.level, mode = data.mode, document = v.document}) + 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, &consumed, builder, p) + } else if data.mode == .Fill && consumed < width { + strings.write_string(builder, v.value) + consumed += len(v.value) + } else if data.mode == .Fill && v.newline { + format_newline(data.indentation, &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_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}) + + if fits(width-consumed, &l) { + append(list, Tuple {indentation = data.indentation, mode = .Flat, document = v.document}) + } else { + if v.fill || data.mode == .Fill { + append(list, Tuple {indentation = data.indentation, mode = .Fill, document = v.document}) + } else { + append(list, Tuple {indentation = data.indentation, mode = .Break, document = v.document}) + } + } + } + } +} + diff --git a/src/odin/printer/printer.odin b/src/odin/printer/printer.odin new file mode 100644 index 0000000..e0776fc --- /dev/null +++ b/src/odin/printer/printer.odin @@ -0,0 +1,150 @@ +package odin_printer + +import "core:odin/ast" +import "core:odin/tokenizer" +import "core:strings" +import "core:fmt" +import "core:mem" + +Printer :: struct { + string_builder: strings.Builder, + config: Config, + comments: [dynamic]^ast.Comment_Group, + latest_comment_index: int, + allocator: mem.Allocator, + file: ^ast.File, + source_position: tokenizer.Pos, + last_source_position: tokenizer.Pos, + skip_semicolon: bool, + current_line_index: int, + last_line_index: int, + document: ^Document, + indentation: string, + newline: string, + indentation_count: int, + disabled_lines: map[int]string, + src: string, +} + +Config :: struct { + max_characters: int, + spaces: int, //Spaces per indentation + newline_limit: int, //The limit of newlines between statements and declarations. + tabs: bool, //Enable or disable tabs + convert_do: bool, //Convert all do statements to brace blocks + brace_style: Brace_Style, + indent_cases: bool, + newline_style: Newline_Style, +} + +Brace_Style :: enum { + _1TBS, + Allman, + Stroustrup, + K_And_R, +} + +Block_Type :: enum { + None, + If_Stmt, + Proc, + Generic, + Comp_Lit, + Switch_Stmt, +} + +Newline_Style :: enum { + CRLF, + LF, +} + +default_style := Config { + spaces = 4, + newline_limit = 2, + convert_do = false, + tabs = false, + brace_style = ._1TBS, + indent_cases = false, + newline_style = .CRLF, + max_characters = 120, +} + +make_printer :: proc(config: Config, allocator := context.allocator) -> Printer { + return { + config = config, + allocator = allocator, + } +} + + +build_disabled_lines_info :: proc(p: ^Printer) { + found_disable := false + disable_position: tokenizer.Pos + + for group in p.comments { + for comment in group.list { + if strings.contains(comment.text[:], "//odinfmt: disable") { + found_disable = true + disable_position = comment.pos + } else if strings.contains(comment.text[:], "//odinfmt: enable") && found_disable { + for line := disable_position.line; line <= comment.pos.line; line += 1 { + p.disabled_lines[line] = p.src[disable_position.offset:comment.pos.offset+len(comment.text)] + } + found_disable = false + } + } + } + +} + +print :: proc(p: ^Printer, file: ^ast.File) -> string { + p.comments = file.comments + p.string_builder = strings.make_builder(p.allocator) + p.src = file.src + context.allocator = p.allocator + + if p.config.tabs { + p.indentation = "\t" + p.indentation_count = 1 + } else { + p.indentation_count = p.config.spaces + p.indentation = " " + } + + if p.config.newline_style == .CRLF { + p.newline = "\r\n" + } else { + p.newline = "\n" + } + + set_source_position(p, file.pkg_token.pos) + + p.last_source_position.line = 1 + + build_disabled_lines_info(p) + + p.document = cons_with_nopl(text("package"), text(file.pkg_name)) + + for decl in file.decls { + p.document = cons(p.document, visit_decl(p, cast(^ast.Decl)decl)) + } + + if len(p.comments) > 0 { + infinite := p.comments[len(p.comments) - 1].end + infinite.offset = 9999999 + document, _ := visit_comments(p, infinite) + p.document = cons(p.document, document) + } + + list := make([dynamic]Tuple, p.allocator) + + append(&list, Tuple { + document = p.document, + indentation = 0, + }) + + format(p.config.max_characters, &list, &p.string_builder, p) + + return strings.to_string(p.string_builder) +} + diff --git a/src/odin/printer/visit.odin b/src/odin/printer/visit.odin new file mode 100644 index 0000000..44aea2b --- /dev/null +++ b/src/odin/printer/visit.odin @@ -0,0 +1,1239 @@ +package odin_printer + +import "core:odin/ast" +import "core:odin/tokenizer" +import "core:strings" +import "core:fmt" +import "core:sort" + +//right now the attribute order is not linearly parsed(bug?) +@(private) +sort_attribute :: proc(s: ^[dynamic]^ast.Attribute) -> sort.Interface { + return sort.Interface { + collection = rawptr(s), + len = proc(it: sort.Interface) -> int { + s := (^[dynamic]^ast.Attribute)(it.collection) + return len(s^) + }, + less = proc(it: sort.Interface, i, j: int) -> bool { + s := (^[dynamic]^ast.Attribute)(it.collection) + return s[i].pos.offset < s[j].pos.offset + }, + swap = proc(it: sort.Interface, i, j: int) { + s := (^[dynamic]^ast.Attribute)(it.collection) + s[i], s[j] = s[j], s[i] + }, + } +} + +@(private) +comment_before_position :: proc(p: ^Printer, pos: tokenizer.Pos) -> bool { + if len(p.comments) <= p.latest_comment_index { + return false + } + + comment := p.comments[p.latest_comment_index] + + return comment.pos.offset < pos.offset +} + +@(private) +comment_before_or_in_line :: proc(p: ^Printer, line: int) -> bool { + if len(p.comments) <= p.latest_comment_index { + return false + } + + comment := p.comments[p.latest_comment_index] + + return comment.pos.line < line +} + +@(private) +next_comment_group :: proc(p: ^Printer) { + p.latest_comment_index += 1 +} + +@(private) +text_token :: proc(p: ^Printer, token: tokenizer.Token) -> ^Document { + document, _ := visit_comments(p, token.pos) + return cons(document, text(token.text)) +} + +text_position :: proc(p: ^Printer, value: string, pos: tokenizer.Pos) -> ^Document { + document, _ := visit_comments(p, pos) + return cons(document, text(value)) +} + +newline_position :: proc(p: ^Printer, amount: int, pos: tokenizer.Pos) -> ^Document { + document, _ := visit_comments(p, pos) + return cons(document, newline(amount)) +} + +@(private) +set_source_position :: proc(p: ^Printer, pos: tokenizer.Pos) { + p.source_position = pos +} + +@(private) +move_line :: proc(p: ^Printer, pos: tokenizer.Pos) -> ^Document { + l, _ := move_line_limit(p, pos, p.config.newline_limit+1) + return l +} + +@(private) +move_line_limit :: proc(p: ^Printer, pos: tokenizer.Pos, limit: int) -> (^Document, bool) { + lines := pos.line - p.source_position.line + + if lines < 0 { + return empty(), false + } + + document, comments_newlined := visit_comments(p, pos) + + p.source_position = pos + + return cons(document, newline(max(min(lines-comments_newlined, limit), 0))), lines > 0 +} + +@(private) +visit_comment :: proc(p: ^Printer, comment: tokenizer.Token, end_newline := true) -> (int, ^Document) { + document := empty() + if len(comment.text) == 0 { + return 0, document + } + + newlines_before_comment := min(comment.pos.line - p.source_position.line, p.config.newline_limit + 1) + + document = cons(document, newline(newlines_before_comment)) + + if comment.text[:2] != "/*" { + if comment.pos.line in p.disabled_lines { + p.source_position = comment.pos + return 1, empty() + } else if comment.pos.line == p.source_position.line { + p.source_position = comment.pos + return newlines_before_comment, cons_with_opl(document, text(comment.text)) + } else { + p.source_position = comment.pos + return newlines_before_comment, cons(document, text(comment.text)) + } + /* + if comment.pos.line in p.disabled_lines { + p.source_position = comment.pos + return 1, empty() + } else if !end_newline || comment.pos.line == p.source_position.line { + p.source_position = comment.pos + return newlines_before_comment, cons_with_opl(document, text(comment.text)) + } else { + p.source_position = comment.pos + p.source_position.line += 1 + return newlines_before_comment+1, cons(document, cons(text(comment.text), newline(1))) + } + */ + } else { + newlines := strings.count(comment.text, "\n") + + if comment.pos.line in p.disabled_lines { + p.source_position = comment.pos + p.source_position.line += newlines + return 1, empty() + } else if comment.pos.line == p.source_position.line { + p.source_position = comment.pos + p.source_position.line += newlines + return newlines_before_comment+newlines, cons_with_opl(document, text(comment.text)) + } else { + p.source_position = comment.pos + p.source_position.line += newlines + return newlines_before_comment+newlines, cons(document, text(comment.text)) + } + + /* + if comment.pos.line in p.disabled_lines { + p.source_position = comment.pos + p.source_position.line += newlines + return 1, empty() + } else if comment.pos.line == p.source_position.line { + p.source_position = comment.pos + p.source_position.line += newlines + return newlines_before_comment+newlines, cons_with_opl(document, text(comment.text)) + } else { + p.source_position = comment.pos + p.source_position.line += newlines + p.source_position.line += 1 + return newlines_before_comment+newlines+1, cons(document, cons(text(comment.text), newline(1))) + } + */ + + return 0, document + } + + +} + +@(private) +visit_comments :: proc(p: ^Printer, pos: tokenizer.Pos, end_newline := true) -> (^Document, int) { + document := empty() + lines := 0 + + for comment_before_position(p, pos) { + comment_group := p.comments[p.latest_comment_index] + + for comment, i in comment_group.list { + newlined, tmp_document := visit_comment(p, comment, end_newline) + lines += newlined + document = cons(document, tmp_document) + } + + next_comment_group(p) + } + + return document, lines +} + +visit_disabled_decl :: proc(p: ^Printer, decl: ^ast.Decl) -> ^Document { + disabled_text := p.disabled_lines[decl.pos.line] + move := move_line(p, decl.pos) + + for comment_before_or_in_line(p, decl.end.line + 1) { + next_comment_group(p) + } + + p.source_position = decl.end + + return cons(move, text(disabled_text)) +} + +@(private) +visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) -> ^Document { + using ast + + if decl == nil { + return empty() + } + + if decl.pos.line in p.disabled_lines { + return visit_disabled_decl(p, decl) + } + + switch v in &decl.derived { + case Expr_Stmt: + document := move_line(p, decl.pos) + return cons(document, visit_expr(p, v.expr)) + case When_Stmt: + return visit_stmt(p, cast(^Stmt)decl) + case Foreign_Import_Decl: + document := empty() + if len(v.attributes) > 0 { + document = cons(document, visit_attributes(p, &v.attributes, v.pos)) + } + + document = cons(document, move_line(p, decl.pos)) + document = cons(document, cons_with_opl(text(v.foreign_tok.text), text(v.import_tok.text))) + + if v.name != nil { + document = cons_with_opl(document, text_position(p, v.name.name, v.pos)) + } + + for path in v.fullpaths { + document = cons_with_opl(document, text(path)) + } + return document + case Foreign_Block_Decl: + document := empty() + if len(v.attributes) > 0 { + document = cons(document, visit_attributes(p, &v.attributes, v.pos)) + } + + document = cons(document, move_line(p, decl.pos)) + document = cons(document, cons_with_opl(text("foreign"), visit_expr(p, v.foreign_library))) + document = cons_with_nopl(document, visit_stmt(p, v.body)) + return document + case Import_Decl: + document := move_line(p, decl.pos) + + if v.name.text != "" { + document = cons(document, text_token(p, v.import_tok)) + document = cons(document, break_with_space()) + document = cons(document, text_token(p, v.name)) + document = cons(document, break_with_space()) + document = cons(document, text(v.fullpath)) + } else { + document = cons(document, text_token(p, v.import_tok)) + document = cons(document, break_with_space()) + document = cons(document, text(v.fullpath)) + } + return document + case Value_Decl: + document := empty() + if len(v.attributes) > 0 { + document = cons(document, visit_attributes(p, &v.attributes, v.pos)) + } + + document = cons(document, move_line(p, decl.pos)) + + if v.is_using { + document = cons(document, cons(text("using"), break_with_no_newline())) + } + + document = cons(document, visit_exprs(p, v.names, {.Add_Comma})) + + if v.type != nil { + document = group(cons(document, text(":"))) + document = cons_with_nopl(document, visit_expr(p, v.type)) + } else { + if !v.is_mutable { + document = cons_with_nopl(document, cons(text(":"), text(":"))) + } else { + document = cons_with_nopl(document, text(":")) + } + } + + if len(v.values) > 0 && v.is_mutable { + if v.type != nil { + document = cons_with_nopl(document, text("=")) + } else { + document = cons(document, text("=")) + } + document = cons_with_nopl(document, visit_exprs(p, v.values, {.Add_Comma})) + } else if len(v.values) > 0 && v.type != nil { + document = cons_with_nopl(document, cons_with_nopl(text(":"), visit_exprs(p, v.values, {.Add_Comma}))) + } else { + document = cons_with_nopl(document, visit_exprs(p, v.values, {.Add_Comma})) + } + + return document + case: + panic(fmt.aprint(decl.derived)) + } + + return empty() +} + +@(private) +visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, options := List_Options{}) -> ^Document { + if len(list) == 0 { + return empty() + } + + document := empty() + + for expr, i in list { + + if (.Enforce_Newline in options) { + document = group(cons(document, visit_expr(p, expr, options))) + } else { + document = group(cons_with_opl(document, visit_expr(p, expr, options))) + } + + if (i != len(list) - 1 || .Trailing in options) && .Add_Comma in options { + document = cons(document, text(",")) + } + + if (i != len(list) - 1 && .Enforce_Newline in options) { + comment, ok := visit_comments(p, list[i+1].pos, false) + document = cons(document, cons(comment, newline(1))) + } + } + + return document +} + +@(private) +visit_attributes :: proc(p: ^Printer, attributes: ^[dynamic]^ast.Attribute, pos: tokenizer.Pos) -> ^Document { + document := empty() + if len(attributes) == 0 { + return document + } + + sort.sort(sort_attribute(attributes)) + document = cons(document, move_line(p, attributes[0].pos)) + + for attribute, i in attributes { + document = cons(document, cons(text("@"), text("("))) + document = cons(document, visit_exprs(p, attribute.elems, {.Add_Comma})) + document = cons(document, text(")")) + + if i != len(attributes) - 1 { + document = cons(document, newline(1)) + } else if pos.line == attributes[0].pos.line { + document = cons(document, newline(1)) + } + } + + return document +} + +@(private) +visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) -> ^Document { + using ast + + if stmt == nil { + return empty() + } + + switch v in stmt.derived { + case Import_Decl: + return visit_decl(p, cast(^Decl)stmt, true) + case Value_Decl: + return visit_decl(p, cast(^Decl)stmt, true) + case Foreign_Import_Decl: + return visit_decl(p, cast(^Decl)stmt, true) + case Foreign_Block_Decl: + return visit_decl(p, cast(^Decl)stmt, true) + } + + switch v in stmt.derived { + case Using_Stmt: + document := move_line(p, v.pos) + document = cons(document, cons_with_nopl(text("using"), visit_exprs(p, v.list, {.Add_Comma}))) + return document + case Block_Stmt: + document := move_line(p, v.pos) + + if !empty_block { + document = cons(document, visit_begin_brace(p, v.pos, block_type, len(v.stmts))) + } + + set_source_position(p, v.pos) + + block := group(visit_block_stmts(p, v.stmts, len(v.stmts) > 1)) + comment_end, _ := visit_comments(p, tokenizer.Pos {line = v.end.line, offset = v.end.offset}, false) + + if block_type == .Switch_Stmt && !p.config.indent_cases { + document = cons(document, cons(block, comment_end)) + } else { + document = cons(document, nest(p.indentation_count, cons(block, comment_end))) + } + + if !empty_block { + document = cons(document, visit_end_brace(p, v.end)) + } + return document + case If_Stmt: + document := move_line(p, v.pos) + + if v.label != nil { + document = cons(document, cons(visit_expr(p, v.label), cons(text(":"), break_with_space()))) + } + + document = cons(document, text("if")) + + if v.init != nil { + document = cons_with_nopl(document, cons(visit_stmt(p, v.init), text(";"))) + } + + document = cons_with_opl(document, visit_expr(p, v.cond)) + + uses_do := false + + if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do { + uses_do = true + } + + if uses_do && !p.config.convert_do { + document = cons(document, cons_with_opl(text("do"), visit_stmt(p, v.body, .If_Stmt, true))) + } else { + if uses_do { + document = cons(document, newline(1)) + } + + set_source_position(p, v.body.pos) + + document = cons_with_nopl(document, visit_stmt(p, v.body, .If_Stmt)) + + set_source_position(p, v.body.end) + } + + if v.else_stmt != nil { + + if p.config.brace_style == .Allman || p.config.brace_style == .Stroustrup { + document = cons(document, newline(1)) + } + + set_source_position(p, v.else_stmt.pos) + + if _, ok := v.else_stmt.derived.(ast.If_Stmt); ok { + document = cons_with_opl(document, cons_with_opl(text("else"), visit_stmt(p, v.else_stmt))) + } else { + document = cons_with_opl(document, cons(text("else"), visit_stmt(p, v.else_stmt))) + } + } + return document + case Switch_Stmt: + document := move_line(p, v.pos) + + if v.label != nil { + document = cons(document, cons(visit_expr(p, v.label), cons(text(":"), break_with_space()))) + } + + if v.partial { + document = cons(document, cons(text("#partial"), break_with_space())) + } + + document = cons(document, text("switch")) + + if v.init != nil { + document = cons_with_opl(document, visit_stmt(p, v.init)) + } + + if v.init != nil && v.cond != nil { + document = cons(document, text(";")) + } + + document = cons_with_opl(document, visit_expr(p, v.cond)) + document = cons_with_nopl(document, visit_stmt(p, v.body, .Switch_Stmt)) + return document + case Case_Clause: + document := move_line(p, v.pos) + document = cons(document, text("case")) + + if v.list != nil { + document = cons_with_nopl(document, visit_exprs(p, v.list, {.Add_Comma})) + } + + document = cons(document, text(v.terminator.text)) + + if len(v.body) != 0 { + set_source_position(p, v.body[0].pos) + document = cons(document, nest(p.indentation_count, cons(newline(1), visit_block_stmts(p, v.body)))) + } + + return document + case Type_Switch_Stmt: + document := move_line(p, v.pos) + + if v.label != nil { + document = cons(document, cons(visit_expr(p, v.label), cons(text(":"), break_with_space()))) + } + + if v.partial { + document = cons(document, cons(text("#partial"), break_with_space())) + } + + document = cons(document, text("switch")) + + document = cons_with_nopl(document, visit_stmt(p, v.tag)) + document = cons_with_nopl(document, visit_stmt(p, v.body, .Switch_Stmt)) + return document + case Assign_Stmt: + document := move_line(p, v.pos) + + document = cons(document, visit_exprs(p, v.lhs, {.Add_Comma})) + document = cons_with_opl(document, cons_with_opl(text(v.op.text), visit_exprs(p, v.rhs, {.Add_Comma}))) + + return document + case Expr_Stmt: + document := move_line(p, v.pos) + document = cons(document, visit_expr(p, v.expr)) + return document + case For_Stmt: + document := move_line(p, v.pos) + + if v.label != nil { + document = cons(document, cons(visit_expr(p, v.label), cons(text(":"), break_with_space()))) + } + + document = cons(document, text("for")) + + if v.init != nil { + document = cons_with_nopl(document, cons(visit_stmt(p, v.init), text(";"))) + } else if v.post != nil { + document = cons_with_nopl(document, text(";")) + } + + if v.cond != nil { + document = cons_with_nopl(document, visit_expr(p, v.cond)) + } + + if v.post != nil { + document = cons(document, text(";")) + document = cons_with_nopl(document, visit_stmt(p, v.post)) + } else if v.post == nil && v.cond != nil && v.init != nil { + document = cons(document, text(";")) + } + + document = cons_with_nopl(document, visit_stmt(p, v.body)) + return document + case Inline_Range_Stmt: + document := move_line(p, v.pos) + + if v.label != nil { + document = cons(document, cons(visit_expr(p, v.label), cons(text(":"), break_with_space()))) + } + + document = cons(document, text("#unroll")) + document = cons_with_nopl(document, text("for")) + + document = cons_with_nopl(document, visit_expr(p, v.val0)) + + if v.val1 != nil { + document = cons_with_nopl(document, cons_with_opl(text(","), visit_expr(p, v.val1))) + } + + document = cons_with_nopl(document, text("in")) + + document = cons_with_nopl(document, visit_expr(p, v.expr)) + document = cons_with_nopl(document, visit_stmt(p, v.body)) + return document + case Range_Stmt: + document := move_line(p, v.pos) + + if v.label != nil { + document = cons(document, cons(visit_expr(p, v.label), cons(text(":"), break_with_space()))) + } + + document = cons(document, text("for")) + + if len(v.vals) >= 1 { + document = cons_with_opl(document, visit_expr(p, v.vals[0])) + } + + if len(v.vals) >= 2 { + document = cons(document, cons_with_opl(text(","), visit_expr(p, v.vals[1]))) + } + + document = cons_with_opl(document, text("in")) + + document = cons_with_opl(document, visit_expr(p, v.expr)) + document = cons_with_nopl(document, visit_stmt(p, v.body)) + return document + case Return_Stmt: + document := move_line(p, v.pos) + + document = cons(document, text("return")) + + if v.results != nil { + document = cons_with_opl(document, visit_exprs(p, v.results, {.Add_Comma})) + } + + return document + case Defer_Stmt: + document := move_line(p, v.pos) + document = cons(document, text("defer")) + document = cons_with_opl(document, visit_stmt(p, v.stmt)) + return document + case When_Stmt: + document := move_line(p, v.pos) + + document = cons(document, cons_with_opl(text("when"), visit_expr(p, v.cond))) + + document = cons_with_nopl(document, visit_stmt(p, v.body)) + + if v.else_stmt != nil { + if p.config.brace_style == .Allman { + document = cons(document, newline(1)) + } + + set_source_position(p, v.else_stmt.pos) + + document = cons(document, cons_with_opl(text("else"), visit_stmt(p, v.else_stmt))) + } + return document + case Branch_Stmt: + document := move_line(p, v.pos) + + document = cons(document, text(v.tok.text)) + + if v.label != nil { + document = cons_with_nopl(document, visit_expr(p, v.label)) + } + return document + case: + panic(fmt.aprint(stmt.derived)) + } + + set_source_position(p, stmt.end) + + return empty() +} + +@(private) +push_where_clauses :: proc(p: ^Printer, clauses: []^ast.Expr) -> ^Document { + if len(clauses) == 0 { + return empty() + } + + return nest(p.indentation_count, cons(newline(1), cons_with_opl(text("where"), visit_exprs(p, clauses, {.Add_Comma, .Enforce_Newline})))) +} + +@(private) +visit_poly_params :: proc(p: ^Printer, poly_params: ^ast.Field_List) -> ^Document { + if poly_params != nil { + return cons(text("("), cons(visit_field_list(p, poly_params, {.Add_Comma, .Enforce_Poly_Names}), text(")"))) + } else { + return empty() + } +} + + +@(private) +visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) -> ^Document { + using ast + + if expr == nil { + return empty() + } + + set_source_position(p, expr.pos) + + switch v in expr.derived { + case Inline_Asm_Expr: + document := cons(text_token(p, v.tok), text("(")) + document = cons(document, visit_exprs(p, v.param_types, {.Add_Comma})) + document = cons(document, text(")")) + document = cons_with_opl(document, cons(text("-"), text(">"))) + document = cons_with_opl(document, visit_expr(p, v.return_type)) + + document = cons(document, text("{")) + document = cons(document, visit_expr(p, v.asm_string)) + document = cons(document, text(",")) + document = cons(document, visit_expr(p, v.constraints_string)) + document = cons(document, text("}")) + case Undef: + return text("---") + case Auto_Cast: + return cons(text_token(p, v.op), visit_expr(p, v.expr)) + case Ternary_If_Expr: + document := visit_expr(p, v.x) + document = cons_with_opl(document, text_token(p, v.op1)) + document = cons_with_opl(document, visit_expr(p, v.cond)) + document = cons_with_opl(document, text_token(p, v.op2)) + document = cons_with_opl(document, visit_expr(p, v.y)) + return document + case Ternary_When_Expr: + document := visit_expr(p, v.x) + document = cons_with_opl(document, text_token(p, v.op1)) + document = cons_with_opl(document, visit_expr(p, v.cond)) + document = cons_with_opl(document, text_token(p, v.op2)) + document = cons_with_opl(document, visit_expr(p, v.y)) + return document + case Or_Else_Expr: + document := visit_expr(p, v.x) + document = cons_with_opl(document, text_token(p, v.token)) + document = cons_with_opl(document, visit_expr(p, v.y)) + return document + case Or_Return_Expr: + return cons_with_opl(visit_expr(p, v.expr), text_token(p, v.token)) + case Selector_Call_Expr: + document := visit_expr(p, v.call.expr) + document = cons(document, text("(")) + document = cons(document, visit_exprs(p, v.call.args, {.Add_Comma})) + document = cons(document, text(")")) + return document + case Ellipsis: + return cons_with_opl(text("..."), visit_expr(p, v.expr)) + case Relative_Type: + return cons_with_opl(visit_expr(p, v.tag), visit_expr(p, v.type)) + case Slice_Expr: + document := visit_expr(p, v.expr) + document = cons(document, text("[")) + document = cons(document, visit_expr(p, v.low)) + document = cons(document, text(v.interval.text)) + + if v.high != nil { + document = cons(document, visit_expr(p, v.high)) + } + document = cons(document, text("]")) + return document + case Ident: + if .Enforce_Poly_Names in options { + return cons(text("$"), text(v.name)) + } else { + return text_position(p, v.name, v.pos) + } + case Deref_Expr: + return cons(visit_expr(p, v.expr), text_token(p, v.op)) + case Type_Cast: + document := cons(text_token(p, v.tok), text("(")) + document = cons(document, visit_expr(p, v.type)) + document = cons(document, text(")")) + document = cons(document, visit_expr(p, v.expr)) + return document + case Basic_Directive: + return cons(text_token(p, v.tok), text_position(p, v.name, v.pos)) + case Distinct_Type: + return cons_with_opl(text_position(p, "distinct", v.pos), visit_expr(p, v.type)) + case Dynamic_Array_Type: + document := visit_expr(p, v.tag) + document = cons(document, text("[")) + document = cons(document, text("dynamic")) + document = cons(document, text("]")) + document = cons(document, visit_expr(p, v.elem)) + return document + case Bit_Set_Type: + document := text_position(p, "bit_set", v.pos) + document = cons(document, text("[")) + document = cons(document, visit_expr(p, v.elem)) + + if v.underlying != nil { + document = cons(document, cons(text(";"), visit_expr(p, v.underlying))) + } + + document = cons(document, text("]")) + return document + case Union_Type: + document := text_position(p, "union", v.pos) + + document = cons(document, visit_poly_params(p, v.poly_params)) + + if v.is_maybe { + document = cons_with_opl(document, text("#maybe")) + } + + document = cons_with_opl(document, push_where_clauses(p, v.where_clauses)) + + if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { + document = cons_with_nopl(document, text("{")) + document = cons(document, visit_exprs(p, v.variants, {.Add_Comma})) + document = cons(document, text("}")) + } else if v.variants != nil { + document = cons_with_opl(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.variants[0].pos), visit_exprs(p, v.variants, {.Add_Comma, .Trailing, .Enforce_Newline})))) + document = cons(document, visit_end_brace(p, v.end)) + } + return document + case Enum_Type: + document := text_position(p, "enum", v.pos) + + if v.base_type != nil { + document = cons(document, visit_expr(p, v.base_type)) + } + + if v.fields != nil && (len(v.fields) == 0 || v.pos.line == v.end.line) { + document = cons_with_nopl(document, text("{")) + document = cons(document, visit_exprs(p, v.fields, {.Add_Comma})) + document = cons(document, text("}")) + } else { + document = cons(document, cons(break_with_space(), visit_begin_brace(p, v.pos, .Generic))) + + set_source_position(p, v.fields[0].pos) + + document = cons(document, nest(p.indentation_count, cons(newline_position(p, 1, v.fields[0].pos), visit_exprs(p, v.fields, {.Add_Comma, .Trailing, .Enforce_Newline})))) + document = cons(document, visit_end_brace(p, v.end)) + } + + set_source_position(p, v.end) + return document + case Struct_Type: + document := text_position(p, "struct", v.pos) + + document = cons(document, visit_poly_params(p, v.poly_params)) + + if v.is_packed { + document = cons_with_nopl(document, text("#packed")) + } + + if v.is_raw_union { + document = cons_with_nopl(document, text("#raw_union")) + } + + if v.align != nil { + document = cons_with_nopl(document, text("#align")) + document = cons_with_nopl(document, visit_expr(p, v.align)) + } + + document = cons_with_opl(document, push_where_clauses(p, v.where_clauses)) + + if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { + document = cons_with_nopl(document, text("{")) + document = cons(document, visit_field_list(p, v.fields, {.Add_Comma})) + document = cons(document, text("}")) + } else if v.fields != nil { + document = cons(document, cons(break_with_space(), visit_begin_brace(p, v.pos, .Generic))) + + set_source_position(p, v.fields.pos) + + document = cons(document, nest(p.indentation_count, cons(newline_position(p, 1, v.fields.pos), visit_field_list(p, v.fields, {.Add_Comma, .Trailing, .Enforce_Newline})))) + document = cons(document, visit_end_brace(p, v.end)) + } + + set_source_position(p, v.end) + return document + case Proc_Lit: + document := empty() + + switch v.inlining { + case .None: + case .Inline: + document = cons(document, text("#force_inline")) + case .No_Inline: + document = cons(document, text("#force_no_inline")) + } + + document = cons(document, visit_proc_type(p, v.type^)) + document = cons_with_opl(document, push_where_clauses(p, v.where_clauses)) + + if v.body != nil { + set_source_position(p, v.body.pos) + document = cons_with_nopl(document, group(visit_stmt(p, v.body, .Proc))) + } else { + document = cons_with_opl(document, text("---")) + } + + return document + case Proc_Type: + return group(visit_proc_type(p, v)) + case Basic_Lit: + return text_token(p, v.tok) + case Binary_Expr: + return nest(p.indentation_count, fill_group(visit_binary_expr(p, v))) + case Implicit_Selector_Expr: + return cons(text("."), text_position(p, v.field.name, v.field.pos)) + case Call_Expr: + document := group(visit_expr(p, v.expr)) + document = cons(document, text("(")) + document = cons(document, nest(p.indentation_count, fill_group(visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis)))) + document = cons(document, text(")")) + return document + case Typeid_Type: + document := text("typeid") + + if v.specialization != nil { + document = cons(document, cons(text("/"), visit_expr(p, v.specialization))) + } + return document + case Selector_Expr: + return cons(visit_expr(p, v.expr), cons(text_token(p, v.op), visit_expr(p, v.field))) + case Paren_Expr: + return cons(text("("), cons(visit_expr(p, v.expr), text(")"))) + case Index_Expr: + document := visit_expr(p, v.expr) + document = cons(document, text("[")) + document = cons(document, visit_expr(p, v.index)) + document = cons(document, text("]")) + return document + case Proc_Group: + document := text_token(p, v.tok) + + if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { + document = cons(document, cons(break_with_space(), 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)) + } else { + document = cons(document, text("{")) + document = cons(document, visit_exprs(p, v.args, {.Add_Comma})) + document = cons(document, text("}")) + } + return document + case Comp_Lit: + document := empty() + if v.type != nil { + document = cons(document, visit_expr(p, v.type)) + } + + if len(v.elems) != 0 && v.pos.line != v.elems[len(v.elems) - 1].pos.line { + document = cons(document, cons(break_with_space(), visit_begin_brace(p, v.pos, .Generic))) + set_source_position(p, v.elems[0].pos) + document = cons(document, nest(p.indentation_count, cons(newline_position(p, 1, v.elems[0].pos), visit_exprs(p, v.elems, {.Add_Comma, .Trailing, .Enforce_Newline})))) + document = cons(document, visit_end_brace(p, v.end)) + } else { + document = cons(document, text("{")) + document = cons(document, visit_exprs(p, v.elems, {.Add_Comma})) + document = cons(document, text("}")) + } + return document + case Unary_Expr: + return cons(text_token(p, v.op), visit_expr(p, v.expr)) + case Field_Value: + document := cons_with_opl(visit_expr(p, v.field), cons_with_nopl(text_position(p, "=", v.sep), visit_expr(p, v.value))) + return document + case Type_Assertion: + document := visit_expr(p, v.expr) + + if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" { + document = cons(document, cons(text("."), visit_expr(p, v.type))) + } else { + document = cons(document, text(".")) + document = cons(document, text("(")) + document = cons(document, visit_expr(p, v.type)) + document = cons(document, text(")")) + } + return document + case Pointer_Type: + return cons(text("^"), visit_expr(p, v.elem)) + case Implicit: + return text_token(p, v.tok) + case Poly_Type: + document := cons(text("$"), visit_expr(p, v.type)) + + if v.specialization != nil { + document = cons(document, text("/")) + document = cons(document, visit_expr(p, v.specialization)) + } + return document + case Array_Type: + document := visit_expr(p, v.tag) + document = cons(document, text("[")) + document = cons(document, visit_expr(p, v.len)) + document = cons(document, text("]")) + document = cons(document, visit_expr(p, v.elem)) + return document + case Map_Type: + document := cons(text("map"), text("[")) + document = cons(document, visit_expr(p, v.key)) + document = cons(document, text("]")) + document = cons(document, visit_expr(p, v.value)) + return document + case Helper_Type: + return visit_expr(p, v.type) + case Matrix_Type: + document := text_position(p, "matrix", v.pos) + document = cons(document, text("[")) + document = cons(document, visit_expr(p, v.row_count)) + 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)) + return document + case Matrix_Index_Expr: + document := visit_expr(p, v.expr) + document = cons(document, text("[")) + document = cons(document, visit_expr(p, v.row_index)) + document = cons(document, text(",")) + document = cons_with_opl(document, visit_expr(p, v.column_index)) + document = cons(document, text("]")) + return document + case: + panic(fmt.aprint(expr.derived)) + } + + return empty() +} + +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) + + newline_braced := p.config.brace_style == .Allman + newline_braced |= p.config.brace_style == .K_And_R && type == .Proc + newline_braced &= p.config.brace_style != ._1TBS + + if newline_braced { + document := newline(1) + document = cons(document, text("{")) + return document + } else { + return text("{") + } +} + +visit_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) -> ^Document { + return cons(move_line(p, end), text("}")) +} + +visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, split := false) -> ^Document { + document := empty() + + for stmt, i in stmts { + document = cons(document, visit_stmt(p, stmt, .Generic, false, true)) + } + + return document +} + +List_Option :: enum u8 { + Add_Comma, + Trailing, + Enforce_Newline, + Enforce_Poly_Names, +} + +List_Options :: distinct bit_set[List_Option] + +visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, options := List_Options{}) -> ^Document { + document := empty() + if list.list == nil { + return document + } + + for field, i in list.list { + + p.source_position = field.pos + + if .Using in field.flags { + document = cons(document, cons(text("using"), break_with_no_newline())) + } + + name_options := List_Options{.Add_Comma} + if .Enforce_Poly_Names in options { + name_options += {.Enforce_Poly_Names} + } + + if (.Enforce_Newline in options) { + document = cons(document, visit_exprs(p, field.names, name_options)) + } else { + document = cons_with_opl(document, visit_exprs(p, field.names, name_options)) + } + + if field.type != nil { + if len(field.names) != 0 { + document = cons(document, text(":")) + } + document = cons_with_opl(document, visit_expr(p, field.type)) + } else { + document = cons(document, cons(text(":"), text("="))) + document = cons_with_opl(document, visit_expr(p, field.default_value)) + } + + if field.tag.text != "" { + document = cons_with_nopl(document, text_token(p, field.tag)) + } + + if (i != len(list.list) - 1 || .Trailing in options) && .Add_Comma in options { + document = cons(document, text(",")) + } + + if i != len(list.list) - 1 && .Enforce_Newline in options { + comment, ok := visit_comments(p, list.list[i+1].pos, false) + document = cons(document, cons(comment, newline(1))) + } + } + return document +} + +visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) -> ^Document { + + document := text("proc") + + explicit_calling := false + + if v, ok := proc_type.calling_convention.(string); ok { + explicit_calling = true + document = cons_with_opl(document, text(v)) + } + + if explicit_calling { + document = cons_with_opl(document, text("(")) + } else { + document = cons(document, text("(")) + } + + document = cons(document, nest(p.indentation_count, cons(break_with(""), visit_signature_list(p, proc_type.params, false)))) + document = group(cons(document, cons(break_with(""), text(")")))) + + if proc_type.results != nil { + document = cons_with_nopl(document, text("-")) + document = cons(document, text(">")) + + use_parens := false + + if len(proc_type.results.list) > 1 { + use_parens = true + } else if len(proc_type.results.list) == 1 { + + for name in proc_type.results.list[0].names { + if ident, ok := name.derived.(ast.Ident); ok { + if ident.name != "_" { + use_parens = true + } + } + } + } + + if use_parens { + document = cons_with_nopl(document, text("(")) + document = cons(document, nest(p.indentation_count, cons(break_with(""), visit_signature_list(p, proc_type.results)))) + document = group(cons(document, cons(break_with(""), text(")")))) + } else { + document = cons_with_opl(document, nest(p.indentation_count, cons(break_with(""), visit_signature_list(p, proc_type.results)))) + } + } + + return document +} + +visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) -> ^Document { + lhs: ^Document + rhs: ^Document + + if v, ok := binary.left.derived.(ast.Binary_Expr); ok { + lhs = visit_binary_expr(p, v) + } else { + lhs = visit_expr(p, binary.left) + } + + if v, ok := binary.right.derived.(ast.Binary_Expr); ok { + rhs = visit_binary_expr(p, v) + } else { + rhs = visit_expr(p, binary.right) + } + + op := text(binary.op.text) + + return nest(0, cons_with_nopl(lhs, cons_with_opl(op, rhs))) +} + +visit_call_exprs :: proc(p: ^Printer, list: []^ast.Expr, ellipsis := false) -> ^Document { + document := empty() + + for expr, i in list { + if i == len(list) - 1 && ellipsis { + document = cons(document, text("...")) + } + document = cons(document, visit_expr(p, expr)) + + if i != len(list) - 1 { + document = cons(document, text(",")) + document = cons(document, break_with_space()) + } + + } + return document +} + +visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank := true) -> ^Document { + document := empty() + + for field, i in list.list { + + if .Using in field.flags { + document = cons(document, cons(text("using"), break_with_no_newline())) + } + + named := false + + for name in field.names { + if ident, ok := name.derived.(ast.Ident); ok { + //for some reason the parser uses _ to mean empty + if ident.name != "_" || !remove_blank { + named = true + } + } else { + //alternative is poly names + named = true + } + } + + if named { + document = cons(document, visit_exprs(p, field.names, {.Add_Comma})) + + if len(field.names) != 0 && field.type != nil { + document = cons(document, cons(text(":"), break_with_no_newline())) + } + } + + if field.type != nil && field.default_value != nil { + document = cons(document, visit_expr(p, field.type)) + document = cons_with_nopl(document, text("=")) + document = cons_with_nopl(document, visit_expr(p, field.default_value)) + } else if field.type != nil { + document = cons(document, visit_expr(p, field.type)) + } else { + document = cons_with_nopl(document, text(":")) + document = cons(document, text("=")) + document = cons_with_nopl(document, visit_expr(p, field.default_value)) + } + + if i != len(list.list) - 1 { + document = cons(document, cons(text(","), break_with_space())) + } else { + document = cons(document, if_break(",")) + } + } + + + return document +} diff --git a/src/server/format.odin b/src/server/format.odin index 82857dc..6eb83de 100644 --- a/src/server/format.odin +++ b/src/server/format.odin @@ -2,7 +2,7 @@ package server import "shared:common" -import "core:odin/printer" +import "shared:odin/printer" FormattingOptions :: struct { tabSize: uint, @@ -22,8 +22,13 @@ TextEdit :: struct { newText: string, } -get_complete_format :: proc(document: ^common.Document) -> ([]TextEdit, bool) { - prnt := printer.make_printer(printer.default_style, context.temp_allocator); +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; + + prnt := printer.make_printer(style, context.temp_allocator); if document.ast.syntax_error_count > 0 { return {}, true; diff --git a/src/server/requests.odin b/src/server/requests.odin index 1d48887..f78053e 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -399,17 +399,24 @@ request_initialize :: proc (task: ^common.Task) { if value, err := json.parse(data = data, allocator = context.temp_allocator, parse_integers = true); err == .None { - ols_config: OlsConfig; + ols_config := OlsConfig { + formatter = { + characters = 90, + tabs = true, + }, + } if unmarshal(value, ols_config, context.temp_allocator) == .None { config.thread_count = ols_config.thread_pool_count; config.enable_document_symbols = ols_config.enable_document_symbols; config.enable_hover = ols_config.enable_hover; - config.enable_format = ols_config.enable_format; + config.enable_format = true // ols_config.enable_format; config.enable_semantic_tokens = ols_config.enable_semantic_tokens; config.enable_procedure_context = ols_config.enable_procedure_context; config.verbose = ols_config.verbose; + config.file_log = ols_config.file_log; + config.formatter = ols_config.formatter; for p in ols_config.collections { @@ -723,7 +730,7 @@ request_format_document :: proc (task: ^common.Task) { } edit: []TextEdit; - edit, ok = get_complete_format(document); + edit, ok = get_complete_format(document, config); if !ok { handle_error(.InternalError, id, writer); @@ -913,6 +920,8 @@ notification_did_save :: proc (task: ^common.Task) { if ret := index.collect_symbols(&index.indexer.dynamic_index.collection, file, uri.uri); ret != .None { log.errorf("failed to collect symbols on save %v", ret); } + + check(uri, writer); } request_semantic_token_full :: proc (task: ^common.Task) { diff --git a/src/server/types.odin b/src/server/types.odin index 03da6f4..53a54b7 100644 --- a/src/server/types.odin +++ b/src/server/types.odin @@ -269,6 +269,8 @@ OlsConfig :: struct { enable_format: bool, enable_procedure_context: bool, verbose: bool, + file_log: bool, + formatter: common.Format_Config, } OlsConfigCollection :: struct { @@ -327,4 +329,4 @@ Command :: struct { title: string, command: string, arguments: []string, -} +} \ No newline at end of file -- cgit v1.2.3