aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2023-06-09 18:42:31 +0200
committerDanielGavin <danielgavin5@hotmail.com>2023-06-09 18:42:31 +0200
commit6e53e8873071bc43984aa1f22aa3c3874643a381 (patch)
treeaa557fc20ca3ffc76671a93b540c8858d88b4e44 /src
parentb13ba1f04b6442b15040b6f0dd1c0d7d3b5ce0f9 (diff)
Add custom marshal.
Diffstat (limited to 'src')
-rw-r--r--src/server/completion.odin2
-rw-r--r--src/server/inlay_hints.odin5
-rw-r--r--src/server/marshal.odin698
-rw-r--r--src/server/response.odin6
-rw-r--r--src/server/types.odin2
5 files changed, 705 insertions, 8 deletions
diff --git a/src/server/completion.odin b/src/server/completion.odin
index da0a8fe..77cc399 100644
--- a/src/server/completion.odin
+++ b/src/server/completion.odin
@@ -1322,7 +1322,7 @@ get_identifier_completion :: proc(
continue
}
- if result.snippet.insert != "" {
+ if result.snippet.insert != "" && false {
item := CompletionItem {
label = result.name,
insertText = result.snippet.insert,
diff --git a/src/server/inlay_hints.odin b/src/server/inlay_hints.odin
index b258b44..038c48d 100644
--- a/src/server/inlay_hints.odin
+++ b/src/server/inlay_hints.odin
@@ -2,6 +2,7 @@ package server
import "core:odin/ast"
import "core:fmt"
+import "core:log"
import "shared:common"
@@ -38,8 +39,6 @@ get_inlay_hints :: proc(
data := cast(^Visit_Data)visitor.data
if call, ok := node.derived.(^ast.Call_Expr); ok {
-
-
append(&data.calls, node)
}
@@ -62,7 +61,7 @@ get_inlay_hints :: proc(
call := node_call.derived.(^ast.Call_Expr)
for arg in call.args {
- if _, ok := arg.derived.(^ast.Field); ok {
+ if _, ok := arg.derived.(^ast.Field_Value); ok {
continue loop
}
}
diff --git a/src/server/marshal.odin b/src/server/marshal.odin
new file mode 100644
index 0000000..c2284fe
--- /dev/null
+++ b/src/server/marshal.odin
@@ -0,0 +1,698 @@
+package server
+
+import "core:mem"
+import "core:math/bits"
+import "core:runtime"
+import "core:strconv"
+import "core:strings"
+import "core:reflect"
+import "core:io"
+import "core:encoding/json"
+import "core:log"
+
+Marshal_Data_Error :: enum {
+ None,
+ Unsupported_Type,
+}
+
+Marshal_Error :: union #shared_nil {
+ Marshal_Data_Error,
+ io.Error,
+}
+
+// careful with MJSON maps & non quotes usage as keys without whitespace will lead to bad results
+Marshal_Options :: struct {
+ // output based on spec
+ spec: json.Specification,
+
+ // use line breaks & tab|spaces
+ pretty: bool,
+
+ // spacing
+ use_spaces: bool,
+ spaces: int,
+
+ // state
+ indentation: int,
+
+ // option to output uint in JSON5 & MJSON
+ write_uint_as_hex: bool,
+
+ // mjson output options
+ mjson_keys_use_quotes: bool,
+ mjson_keys_use_equal_sign: bool,
+
+ // mjson state
+ mjson_skipped_first_braces_start: bool,
+ mjson_skipped_first_braces_end: bool,
+}
+
+marshal :: proc(
+ v: any,
+ opt: Marshal_Options = {},
+ allocator := context.allocator,
+) -> (
+ data: []byte,
+ err: Marshal_Error,
+) {
+ b := strings.builder_make(allocator)
+ defer if err != nil {
+ strings.builder_destroy(&b)
+ }
+
+ opt := opt
+ marshal_to_builder(&b, v, &opt) or_return
+
+ if len(b.buf) != 0 {
+ data = b.buf[:]
+ }
+
+ return data, nil
+}
+
+marshal_to_builder :: proc(
+ b: ^strings.Builder,
+ v: any,
+ opt: ^Marshal_Options,
+) -> Marshal_Error {
+ return marshal_to_writer(strings.to_writer(b), v, opt)
+}
+
+marshal_to_writer :: proc(
+ w: io.Writer,
+ v: any,
+ opt: ^Marshal_Options,
+) -> (
+ err: Marshal_Error,
+) {
+ if v == nil {
+ io.write_string(w, "null") or_return
+ return
+ }
+
+ ti := runtime.type_info_base(type_info_of(v.id))
+ a := any{v.data, ti.id}
+
+ switch info in ti.variant {
+ case runtime.Type_Info_Named:
+ unreachable()
+
+ case runtime.Type_Info_Integer:
+ buf: [40]byte
+ u: u128
+ switch i in a {
+ case i8:
+ u = u128(i)
+ case i16:
+ u = u128(i)
+ case i32:
+ u = u128(i)
+ case i64:
+ u = u128(i)
+ case i128:
+ u = u128(i)
+ case int:
+ u = u128(i)
+ case u8:
+ u = u128(i)
+ case u16:
+ u = u128(i)
+ case u32:
+ u = u128(i)
+ case u64:
+ u = u128(i)
+ case u128:
+ u = u128(i)
+ case uint:
+ u = u128(i)
+ case uintptr:
+ u = u128(i)
+
+ case i16le:
+ u = u128(i)
+ case i32le:
+ u = u128(i)
+ case i64le:
+ u = u128(i)
+ case u16le:
+ u = u128(i)
+ case u32le:
+ u = u128(i)
+ case u64le:
+ u = u128(i)
+ case u128le:
+ u = u128(i)
+
+ case i16be:
+ u = u128(i)
+ case i32be:
+ u = u128(i)
+ case i64be:
+ u = u128(i)
+ case u16be:
+ u = u128(i)
+ case u32be:
+ u = u128(i)
+ case u64be:
+ u = u128(i)
+ case u128be:
+ u = u128(i)
+ }
+
+ s: string
+
+ // allow uints to be printed as hex
+ if opt.write_uint_as_hex &&
+ (opt.spec == .JSON5 || opt.spec == .MJSON) {
+ switch i in a {
+ case u8, u16, u32, u64, u128:
+ s = strconv.append_bits_128(
+ buf[:],
+ u,
+ 16,
+ info.signed,
+ 8 * ti.size,
+ "0123456789abcdef",
+ {.Prefix},
+ )
+
+ case:
+ s = strconv.append_bits_128(
+ buf[:],
+ u,
+ 10,
+ info.signed,
+ 8 * ti.size,
+ "0123456789",
+ nil,
+ )
+ }
+ } else {
+ s = strconv.append_bits_128(
+ buf[:],
+ u,
+ 10,
+ info.signed,
+ 8 * ti.size,
+ "0123456789",
+ nil,
+ )
+ }
+
+ io.write_string(w, s) or_return
+
+
+ case runtime.Type_Info_Rune:
+ r := a.(rune)
+ io.write_byte(w, '"') or_return
+ io.write_escaped_rune(w, r, '"', true) or_return
+ io.write_byte(w, '"') or_return
+
+ case runtime.Type_Info_Float:
+ switch f in a {
+ case f16:
+ io.write_f16(w, f) or_return
+ case f32:
+ io.write_f32(w, f) or_return
+ case f64:
+ io.write_f64(w, f) or_return
+ case:
+ return .Unsupported_Type
+ }
+
+ case runtime.Type_Info_Complex:
+ r, i: f64
+ switch z in a {
+ case complex32:
+ r, i = f64(real(z)), f64(imag(z))
+ case complex64:
+ r, i = f64(real(z)), f64(imag(z))
+ case complex128:
+ r, i = f64(real(z)), f64(imag(z))
+ case:
+ return .Unsupported_Type
+ }
+
+ io.write_byte(w, '[') or_return
+ io.write_f64(w, r) or_return
+ io.write_string(w, ", ") or_return
+ io.write_f64(w, i) or_return
+ io.write_byte(w, ']') or_return
+
+ case runtime.Type_Info_Quaternion:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_String:
+ switch s in a {
+ case string:
+ io.write_quoted_string(w, s, '"', nil, true) or_return
+ case cstring:
+ io.write_quoted_string(w, string(s), '"', nil, true) or_return
+ }
+
+ case runtime.Type_Info_Boolean:
+ val: bool
+ switch b in a {
+ case bool:
+ val = bool(b)
+ case b8:
+ val = bool(b)
+ case b16:
+ val = bool(b)
+ case b32:
+ val = bool(b)
+ case b64:
+ val = bool(b)
+ }
+ io.write_string(w, val ? "true" : "false") or_return
+
+ case runtime.Type_Info_Any:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Type_Id:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Pointer:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Multi_Pointer:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Soa_Pointer:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Procedure:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Parameters:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Simd_Vector:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Relative_Pointer:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Relative_Slice:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Matrix:
+ return .Unsupported_Type
+
+ case runtime.Type_Info_Array:
+ opt_write_start(w, opt, '[') or_return
+ for i in 0 ..< info.count {
+ opt_write_iteration(w, opt, i) or_return
+ data := uintptr(v.data) + uintptr(i * info.elem_size)
+ marshal_to_writer(
+ w,
+ any{rawptr(data), info.elem.id},
+ opt,
+ ) or_return
+ }
+ opt_write_end(w, opt, ']') or_return
+
+ case runtime.Type_Info_Enumerated_Array:
+ index := runtime.type_info_base(
+ info.index,
+ ).variant.(runtime.Type_Info_Enum)
+ opt_write_start(w, opt, '[') or_return
+ for i in 0 ..< info.count {
+ opt_write_iteration(w, opt, i) or_return
+ data := uintptr(v.data) + uintptr(i * info.elem_size)
+ marshal_to_writer(
+ w,
+ any{rawptr(data), info.elem.id},
+ opt,
+ ) or_return
+ }
+ opt_write_end(w, opt, ']') or_return
+
+ case runtime.Type_Info_Dynamic_Array:
+ opt_write_start(w, opt, '[') or_return
+ array := cast(^mem.Raw_Dynamic_Array)v.data
+ for i in 0 ..< array.len {
+ opt_write_iteration(w, opt, i) or_return
+ data := uintptr(array.data) + uintptr(i * info.elem_size)
+ marshal_to_writer(
+ w,
+ any{rawptr(data), info.elem.id},
+ opt,
+ ) or_return
+ }
+ opt_write_end(w, opt, ']') or_return
+
+ case runtime.Type_Info_Slice:
+ opt_write_start(w, opt, '[') or_return
+ slice := cast(^mem.Raw_Slice)v.data
+ for i in 0 ..< slice.len {
+ opt_write_iteration(w, opt, i) or_return
+ data := uintptr(slice.data) + uintptr(i * info.elem_size)
+ marshal_to_writer(
+ w,
+ any{rawptr(data), info.elem.id},
+ opt,
+ ) or_return
+ }
+ opt_write_end(w, opt, ']') or_return
+
+ case runtime.Type_Info_Map:
+ m := (^mem.Raw_Map)(v.data)
+ opt_write_start(w, opt, '{') or_return
+
+ if m != nil {
+ if info.map_info == nil {
+ return .Unsupported_Type
+ }
+ map_cap := uintptr(runtime.map_cap(m^))
+ ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
+
+ i := 0
+ bucket_for: for bucket_index in 0 ..< map_cap {
+ if !runtime.map_hash_is_valid(hs[bucket_index]) {
+ continue
+ }
+
+ key := rawptr(
+ runtime.map_cell_index_dynamic(
+ ks,
+ info.map_info.ks,
+ bucket_index,
+ ),
+ )
+ value := rawptr(
+ runtime.map_cell_index_dynamic(
+ vs,
+ info.map_info.vs,
+ bucket_index,
+ ),
+ )
+
+ opt_write_iteration(w, opt, i) or_return
+ i += 1
+
+
+ // check for string type
+ {
+ v := any{key, info.key.id}
+ ti := runtime.type_info_base(type_info_of(v.id))
+ a := any{v.data, ti.id}
+ name: string
+
+ #partial switch info in ti.variant {
+ case runtime.Type_Info_String:
+ switch s in a {
+ case string:
+ name = s
+ case cstring:
+ name = string(s)
+ }
+ opt_write_key(w, opt, name) or_return
+
+ case:
+ return .Unsupported_Type
+ }
+ }
+
+ marshal_to_writer(w, any{value, info.value.id}, opt) or_return
+ }
+ }
+
+ opt_write_end(w, opt, '}') or_return
+
+ case runtime.Type_Info_Struct:
+ opt_write_start(w, opt, '{') or_return
+
+ for name, i in info.names {
+ id := info.types[i].id
+ data := rawptr(uintptr(v.data) + info.offsets[i])
+
+ {
+ ti := runtime.type_info_base(type_info_of(id))
+ a := any{data, ti.id}
+
+ if reflect.is_union(ti) &&
+ reflect.is_nil(a) &&
+ name != "result" {
+ continue
+ }
+ }
+
+ opt_write_iteration(w, opt, i) or_return
+ if json_name := string(
+ reflect.struct_tag_get(auto_cast info.tags[i], "json"),
+ ); json_name != "" {
+ opt_write_key(w, opt, json_name) or_return
+ } else {
+ opt_write_key(w, opt, name) or_return
+ }
+
+ marshal_to_writer(w, any{data, id}, opt) or_return
+ }
+
+ opt_write_end(w, opt, '}') or_return
+
+ case runtime.Type_Info_Union:
+ tag_ptr := uintptr(v.data) + info.tag_offset
+ tag_any := any{rawptr(tag_ptr), info.tag_type.id}
+
+ tag: i64 = -1
+ switch i in tag_any {
+ case u8:
+ tag = i64(i)
+ case i8:
+ tag = i64(i)
+ case u16:
+ tag = i64(i)
+ case i16:
+ tag = i64(i)
+ case u32:
+ tag = i64(i)
+ case i32:
+ tag = i64(i)
+ case u64:
+ tag = i64(i)
+ case i64:
+ tag = i64(i)
+ case:
+ panic("Invalid union tag type")
+ }
+
+ if v.data == nil || tag == 0 {
+ io.write_string(w, "null") or_return
+ } else {
+ id := info.variants[tag - 1].id
+ return marshal_to_writer(w, any{v.data, id}, opt)
+ }
+
+ case runtime.Type_Info_Enum:
+ return marshal_to_writer(w, any{v.data, info.base.id}, opt)
+
+ case runtime.Type_Info_Bit_Set:
+ is_bit_set_different_endian_to_platform :: proc(
+ ti: ^runtime.Type_Info,
+ ) -> bool {
+ if ti == nil {
+ return false
+ }
+ t := runtime.type_info_base(ti)
+ #partial switch info in t.variant {
+ case runtime.Type_Info_Integer:
+ switch info.endianness {
+ case .Platform:
+ return false
+ case .Little:
+ return ODIN_ENDIAN != .Little
+ case .Big:
+ return ODIN_ENDIAN != .Big
+ }
+ }
+ return false
+ }
+
+ bit_data: u64
+ bit_size := u64(8 * ti.size)
+
+ do_byte_swap := is_bit_set_different_endian_to_platform(
+ info.underlying,
+ )
+
+ switch bit_size {
+ case 0:
+ bit_data = 0
+ case 8:
+ x := (^u8)(v.data)^
+ bit_data = u64(x)
+ case 16:
+ x := (^u16)(v.data)^
+ if do_byte_swap {
+ x = bits.byte_swap(x)
+ }
+ bit_data = u64(x)
+ case 32:
+ x := (^u32)(v.data)^
+ if do_byte_swap {
+ x = bits.byte_swap(x)
+ }
+ bit_data = u64(x)
+ case 64:
+ x := (^u64)(v.data)^
+ if do_byte_swap {
+ x = bits.byte_swap(x)
+ }
+ bit_data = u64(x)
+ case:
+ panic("unknown bit_size size")
+ }
+ io.write_u64(w, bit_data) or_return
+
+ return .Unsupported_Type
+ }
+
+ return
+}
+
+
+// write key as quoted string or with optional quotes in mjson
+opt_write_key :: proc(
+ w: io.Writer,
+ opt: ^Marshal_Options,
+ name: string,
+) -> (
+ err: io.Error,
+) {
+ switch opt.spec {
+ case .JSON, .JSON5:
+ io.write_quoted_string(w, name) or_return
+ io.write_string(w, ": ") or_return
+
+ case .MJSON:
+ if opt.mjson_keys_use_quotes {
+ io.write_quoted_string(w, name) or_return
+ } else {
+ io.write_string(w, name) or_return
+ }
+
+ if opt.mjson_keys_use_equal_sign {
+ io.write_string(w, " = ") or_return
+ } else {
+ io.write_string(w, ": ") or_return
+ }
+ }
+
+ return
+}
+
+// insert start byte and increase indentation on pretty
+opt_write_start :: proc(
+ w: io.Writer,
+ opt: ^Marshal_Options,
+ c: byte,
+) -> (
+ err: io.Error,
+) {
+ // skip mjson starting braces
+ if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start {
+ opt.mjson_skipped_first_braces_start = true
+ return
+ }
+
+ io.write_byte(w, c) or_return
+ opt.indentation += 1
+
+ if opt.pretty {
+ io.write_byte(w, '\n') or_return
+ }
+
+ return
+}
+
+// insert comma separation and write indentations
+opt_write_iteration :: proc(
+ w: io.Writer,
+ opt: ^Marshal_Options,
+ iteration: int,
+) -> (
+ err: io.Error,
+) {
+ switch opt.spec {
+ case .JSON, .JSON5:
+ if iteration > 0 {
+ io.write_string(w, ", ") or_return
+
+ if opt.pretty {
+ io.write_byte(w, '\n') or_return
+ }
+ }
+
+ opt_write_indentation(w, opt) or_return
+
+ case .MJSON:
+ if iteration > 0 {
+ // on pretty no commas necessary
+ if opt.pretty {
+ io.write_byte(w, '\n') or_return
+ } else {
+ // comma separation necessary for non pretty output!
+ io.write_string(w, ", ") or_return
+ }
+ }
+
+ opt_write_indentation(w, opt) or_return
+ }
+
+ return
+}
+
+// decrease indent, write spacing and insert end byte
+opt_write_end :: proc(
+ w: io.Writer,
+ opt: ^Marshal_Options,
+ c: byte,
+) -> (
+ err: io.Error,
+) {
+ if opt.spec == .MJSON &&
+ opt.mjson_skipped_first_braces_start &&
+ !opt.mjson_skipped_first_braces_end {
+ if opt.indentation == 0 {
+ opt.mjson_skipped_first_braces_end = true
+ return
+ }
+ }
+
+ opt.indentation -= 1
+
+ if opt.pretty {
+ io.write_byte(w, '\n') or_return
+ opt_write_indentation(w, opt) or_return
+ }
+
+ io.write_byte(w, c) or_return
+ return
+}
+
+// writes current indentation level based on options
+opt_write_indentation :: proc(
+ w: io.Writer,
+ opt: ^Marshal_Options,
+) -> (
+ err: io.Error,
+) {
+ if !opt.pretty {
+ return
+ }
+
+ if opt.use_spaces {
+ spaces := opt.spaces == 0 ? 4 : opt.spaces
+ for _ in 0 ..< opt.indentation * spaces {
+ io.write_byte(w, ' ') or_return
+ }
+ } else {
+ for _ in 0 ..< opt.indentation {
+ io.write_byte(w, '\t') or_return
+ }
+ }
+
+ return
+}
diff --git a/src/server/response.odin b/src/server/response.odin
index d31b3cd..916f1b1 100644
--- a/src/server/response.odin
+++ b/src/server/response.odin
@@ -7,7 +7,7 @@ send_notification :: proc(
notification: Notification,
writer: ^Writer,
) -> bool {
- data, error := json.marshal(notification, {}, context.temp_allocator)
+ data, error := marshal(notification, {}, context.temp_allocator)
header := fmt.tprintf("Content-Length: %v\r\n\r\n", len(data))
@@ -27,7 +27,7 @@ send_notification :: proc(
}
send_response :: proc(response: ResponseMessage, writer: ^Writer) -> bool {
- data, error := json.marshal(response, {}, context.temp_allocator)
+ data, error := marshal(response, {}, context.temp_allocator)
header := fmt.tprintf("Content-Length: %v\r\n\r\n", len(data))
@@ -47,7 +47,7 @@ send_response :: proc(response: ResponseMessage, writer: ^Writer) -> bool {
}
send_error :: proc(response: ResponseMessageError, writer: ^Writer) -> bool {
- data, error := json.marshal(response, {}, context.temp_allocator)
+ data, error := marshal(response, {}, context.temp_allocator)
header := fmt.tprintf("Content-Length: %v\r\n\r\n", len(data))
diff --git a/src/server/types.odin b/src/server/types.odin
index 2a54c27..e39bd7b 100644
--- a/src/server/types.odin
+++ b/src/server/types.odin
@@ -301,7 +301,7 @@ CompletionItem :: struct {
insertText: Maybe(string),
InsertTextMode: Maybe(InsertTextMode),
textEdit: Maybe(TextEdit),
- additionalTextEdits: []TextEdit,
+ additionalTextEdits: Maybe([]TextEdit),
tags: []CompletionItemTag,
deprecated: bool,
command: Maybe(Command),