diff options
| author | DanielGavin <danielgavin5@hotmail.com> | 2021-04-25 22:33:42 +0200 |
|---|---|---|
| committer | DanielGavin <danielgavin5@hotmail.com> | 2021-04-25 22:33:42 +0200 |
| commit | e96b24da93e22549c6f32cd9d46c28bd0b608b5a (patch) | |
| tree | fc082f5bcc83ed4ed26eaa42b20edbade783bc76 /src | |
| parent | 28d227ec89a4cbe15021eddeb16e356ec13bf51f (diff) | |
| parent | b43fc8881fa1e48c7e0c1af582ce499292daf436 (diff) | |
Merge branch 'master' of https://github.com/DanielGavin/ols
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/allocator.odin | 251 | ||||
| -rw-r--r-- | src/common/ast.odin | 26 | ||||
| -rw-r--r-- | src/common/config.odin | 2 | ||||
| -rw-r--r-- | src/common/position.odin | 30 | ||||
| -rw-r--r-- | src/common/track_allocator.odin | 192 | ||||
| -rw-r--r-- | src/index/build.odin | 2 | ||||
| -rw-r--r-- | src/index/clone.odin | 9 | ||||
| -rw-r--r-- | src/index/collector.odin | 48 | ||||
| -rw-r--r-- | src/index/symbol.odin | 4 | ||||
| -rw-r--r-- | src/server/analysis.odin | 294 | ||||
| -rw-r--r-- | src/server/completion.odin | 123 | ||||
| -rw-r--r-- | src/server/hover.odin | 5 | ||||
| -rw-r--r-- | src/server/requests.odin | 24 | ||||
| -rw-r--r-- | src/server/semantic_tokens.odin | 35 | ||||
| -rw-r--r-- | src/server/types.odin | 5 | ||||
| -rw-r--r-- | src/server/unmarshal.odin | 14 | ||||
| -rw-r--r-- | src/testing/testing.odin | 25 |
17 files changed, 561 insertions, 528 deletions
diff --git a/src/common/allocator.odin b/src/common/allocator.odin index 4f72f81..ca0abe6 100644 --- a/src/common/allocator.odin +++ b/src/common/allocator.odin @@ -1,121 +1,130 @@ -package common - -import "core:mem" - -Scratch_Allocator :: struct { - data: []byte, - curr_offset: int, - prev_allocation: rawptr, - backup_allocator: mem.Allocator, - leaked_allocations: [dynamic]rawptr, -} - -scratch_allocator_init :: proc (s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) { - s.data = mem.make_aligned([]byte, size, 2 * align_of(rawptr), backup_allocator); - s.curr_offset = 0; - s.prev_allocation = nil; - s.backup_allocator = backup_allocator; - s.leaked_allocations.allocator = backup_allocator; -} - -scratch_allocator_destroy :: proc (s: ^Scratch_Allocator) { - if s == nil { - return; - } - for ptr in s.leaked_allocations { - free(ptr, s.backup_allocator); - } - delete(s.leaked_allocations); - delete(s.data, s.backup_allocator); - s^ = {}; -} - -scratch_allocator_proc :: proc (allocator_data: rawptr, mode: mem.Allocator_Mode, -size, alignment: int, -old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr { - - s := (^Scratch_Allocator)(allocator_data); - - if s.data == nil { - DEFAULT_BACKING_SIZE :: 1 << 22; - if !(context.allocator.procedure != scratch_allocator_proc && - context.allocator.data != allocator_data) { - panic("cyclic initialization of the scratch allocator with itself"); - } - scratch_allocator_init(s, DEFAULT_BACKING_SIZE); - } - - size := size; - - switch mode { - case .Alloc: - size = mem.align_forward_int(size, alignment); - - switch { - case s.curr_offset + size <= len(s.data): - start := uintptr(raw_data(s.data)); - ptr := start + uintptr(s.curr_offset); - ptr = mem.align_forward_uintptr(ptr, uintptr(alignment)); - mem.zero(rawptr(ptr), size); - - s.prev_allocation = rawptr(ptr); - offset := int(ptr - start); - s.curr_offset = offset + size; - return rawptr(ptr); - } - a := s.backup_allocator; - if a.procedure == nil { - a = context.allocator; - s.backup_allocator = a; - } - - ptr := mem.alloc(size, alignment, a, loc); - if s.leaked_allocations == nil { - s.leaked_allocations = make([dynamic]rawptr, a); - } - append(&s.leaked_allocations, ptr); - - return ptr; - - case .Free: - case .Free_All: - s.curr_offset = 0; - s.prev_allocation = nil; - for ptr in s.leaked_allocations { - free(ptr, s.backup_allocator); - } - clear(&s.leaked_allocations); - - case .Resize: - begin := uintptr(raw_data(s.data)); - end := begin + uintptr(len(s.data)); - old_ptr := uintptr(old_memory); - //if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end { - // s.curr_offset = int(old_ptr-begin)+size; - // return old_memory; - //} - ptr := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, flags, loc); - mem.copy(ptr, old_memory, old_size); - scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, flags, loc); - return ptr; - - case .Query_Features: - set := (^mem.Allocator_Mode_Set)(old_memory); - if set != nil { - set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}; - } - return set; - - case .Query_Info: - return nil; - } - - return nil; -} - -scratch_allocator :: proc (allocator: ^Scratch_Allocator) -> mem.Allocator { - return mem.Allocator { - procedure = scratch_allocator_proc, - data = allocator, - }; -} +package common
+
+import "core:mem"
+import "core:runtime"
+
+Scratch_Allocator :: struct {
+ data: []byte,
+ curr_offset: int,
+ prev_allocation: rawptr,
+ backup_allocator: mem.Allocator,
+ leaked_allocations: [dynamic][]byte,
+}
+
+scratch_allocator_init :: proc (s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) {
+ s.data = mem.make_aligned([]byte, size, 2 * align_of(rawptr), backup_allocator);
+ s.curr_offset = 0;
+ s.prev_allocation = nil;
+ s.backup_allocator = backup_allocator;
+ s.leaked_allocations.allocator = backup_allocator;
+}
+
+scratch_allocator_destroy :: proc (s: ^Scratch_Allocator) {
+ if s == nil {
+ return;
+ }
+ for ptr in s.leaked_allocations {
+ mem.free_bytes(ptr, s.backup_allocator);
+ }
+ delete(s.leaked_allocations);
+ delete(s.data, s.backup_allocator);
+ s^ = {};
+}
+
+scratch_allocator_proc :: proc (allocator_data: rawptr, mode: mem.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
+
+ s := (^Scratch_Allocator)(allocator_data);
+
+ if s.data == nil {
+ DEFAULT_BACKING_SIZE :: 1 << 22;
+ if !(context.allocator.procedure != scratch_allocator_proc &&
+ context.allocator.data != allocator_data) {
+ panic("cyclic initialization of the scratch allocator with itself");
+ }
+ scratch_allocator_init(s, DEFAULT_BACKING_SIZE);
+ }
+
+ size := size;
+
+ switch mode {
+ case .Alloc:
+ size = mem.align_forward_int(size, alignment);
+
+ switch {
+ case s.curr_offset + size <= len(s.data):
+ start := uintptr(raw_data(s.data));
+ ptr := start + uintptr(s.curr_offset);
+ ptr = mem.align_forward_uintptr(ptr, uintptr(alignment));
+ mem.zero(rawptr(ptr), size);
+
+ s.prev_allocation = rawptr(ptr);
+ offset := int(ptr - start);
+ s.curr_offset = offset + size;
+ return mem.byte_slice(rawptr(ptr), size), nil;
+ }
+
+ a := s.backup_allocator;
+ if a.procedure == nil {
+ a = context.allocator;
+ s.backup_allocator = a;
+ }
+
+ ptr, err := mem.alloc_bytes(size, alignment, a, loc);
+ if err != nil {
+ return ptr, err;
+ }
+ if s.leaked_allocations == nil {
+ s.leaked_allocations = make([dynamic][]byte, a);
+ }
+ append(&s.leaked_allocations, ptr);
+
+ if logger := context.logger; logger.lowest_level <= .Warning {
+ if logger.procedure != nil {
+ logger.procedure(logger.data, .Warning, "mem.Scratch_Allocator resorted to backup_allocator" , logger.options, loc);
+ }
+ }
+
+ return ptr, err;
+
+ case .Free:
+ case .Free_All:
+ s.curr_offset = 0;
+ s.prev_allocation = nil;
+ for ptr in s.leaked_allocations {
+ mem.free_bytes(ptr, s.backup_allocator);
+ }
+ clear(&s.leaked_allocations);
+
+ case .Resize:
+ begin := uintptr(raw_data(s.data));
+ end := begin + uintptr(len(s.data));
+ old_ptr := uintptr(old_memory);
+
+ data, err := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
+ if err != nil {
+ return data, err;
+ }
+
+ runtime.copy(data, mem.byte_slice(old_memory, old_size));
+ _, err = scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc);
+ return data, err;
+
+ case .Query_Features:
+ set := (^mem.Allocator_Mode_Set)(old_memory);
+ if set != nil {
+ set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
+ }
+ return nil, nil;
+ case .Query_Info:
+ return nil, nil;
+ }
+
+ return nil, nil;
+}
+
+scratch_allocator :: proc (allocator: ^Scratch_Allocator) -> mem.Allocator {
+ return mem.Allocator {
+ procedure = scratch_allocator_proc,
+ data = allocator,
+ };
+}
diff --git a/src/common/ast.odin b/src/common/ast.odin index 9ca58ed..5f8ffd0 100644 --- a/src/common/ast.odin +++ b/src/common/ast.odin @@ -34,8 +34,17 @@ GlobalExpr :: struct { docs: ^ast.Comment_Group, } -collect_value_decl :: proc(exprs: ^[dynamic]GlobalExpr, file: ast.File, stmt: ^ast.Node) { +collect_value_decl :: proc(exprs: ^[dynamic]GlobalExpr, file: ast.File, stmt: ^ast.Node, skip_private: bool) { if value_decl, ok := stmt.derived.(ast.Value_Decl); ok { + + for attribute in value_decl.attributes { + for elem in attribute.elems { + if ident, ok := elem.derived.(ast.Ident); ok && ident.name == "private" && skip_private { + return; + } + } + } + for name, i in value_decl.names { str := get_ast_node_string(name, file.src); @@ -50,15 +59,14 @@ collect_value_decl :: proc(exprs: ^[dynamic]GlobalExpr, file: ast.File, stmt: ^a } } -//TODO(add a sub procedure to avoid repeating the value decl work) -collect_globals :: proc(file: ast.File) -> []GlobalExpr { +collect_globals :: proc(file: ast.File, skip_private := false) -> []GlobalExpr { exprs := make([dynamic]GlobalExpr, context.temp_allocator); for decl in file.decls { if value_decl, ok := decl.derived.(ast.Value_Decl); ok { - collect_value_decl(&exprs, file, decl); + collect_value_decl(&exprs, file, decl, skip_private); } else if when_decl, ok := decl.derived.(ast.When_Stmt); ok { if when_decl.cond == nil { @@ -96,13 +104,13 @@ collect_globals :: proc(file: ast.File) -> []GlobalExpr { if block, ok := when_decl.body.derived.(ast.Block_Stmt); ok { for stmt in block.stmts { - collect_value_decl(&exprs, file, stmt); + collect_value_decl(&exprs, file, stmt, skip_private); } } } else if ident.name != "ODIN_OS" { if block, ok := when_decl.body.derived.(ast.Block_Stmt); ok { for stmt in block.stmts { - collect_value_decl(&exprs, file, stmt); + collect_value_decl(&exprs, file, stmt, skip_private); } } } @@ -112,7 +120,7 @@ collect_globals :: proc(file: ast.File) -> []GlobalExpr { else { if block, ok := when_decl.body.derived.(ast.Block_Stmt); ok { for stmt in block.stmts { - collect_value_decl(&exprs, file, stmt); + collect_value_decl(&exprs, file, stmt, skip_private); } } } @@ -124,7 +132,7 @@ collect_globals :: proc(file: ast.File) -> []GlobalExpr { if block, ok := foreign_decl.body.derived.(ast.Block_Stmt); ok { for stmt in block.stmts { - collect_value_decl(&exprs, file, stmt); + collect_value_decl(&exprs, file, stmt, skip_private); } } } @@ -373,7 +381,7 @@ free_ast_node :: proc(node: ^ast.Node, allocator: mem.Allocator) { free_ast(n.key, allocator); free_ast(n.value, allocator); case: - log.warnf("free Unhandled node kind: %T", n); + panic(fmt.aprintf("free Unhandled node kind: %T", n)); } mem.free(node, allocator); diff --git a/src/common/config.odin b/src/common/config.odin index df6ddb1..432794f 100644 --- a/src/common/config.odin +++ b/src/common/config.odin @@ -11,3 +11,5 @@ Config :: struct { debug_single_thread: bool, enable_semantic_tokens: bool, //This will be removed when vscode client stops sending me semantic tokens after disabling it in requests initialize. } + +config: Config;
\ No newline at end of file diff --git a/src/common/position.odin b/src/common/position.odin index 865d0c8..d410ce6 100644 --- a/src/common/position.odin +++ b/src/common/position.odin @@ -208,7 +208,7 @@ get_character_offset_u16_to_u8 :: proc(character_offset: int, document_text: []u r, w := utf8.decode_rune(document_text[utf8_idx:]); - if r == '\n' { + if r == '\n' || r == '\r' { return utf8_idx; } else if w == 0 { return utf8_idx; @@ -233,7 +233,7 @@ get_character_offset_u8_to_u16 :: proc(character_offset: int, document_text: []u r, w := utf8.decode_rune(document_text[utf8_idx:]); - if r == '\n' { + if r == '\n' || r == '\r' { return utf16_idx; } else if w == 0 { return utf16_idx; @@ -247,28 +247,4 @@ get_character_offset_u8_to_u16 :: proc(character_offset: int, document_text: []u } return utf16_idx; -} - -get_end_line_u16 :: proc(document_text: []u8) -> int { - - utf8_idx := 0; - utf16_idx := 0; - - for utf8_idx < len(document_text) { - r, w := utf8.decode_rune(document_text[utf8_idx:]); - - if r == '\n' { - return utf16_idx; - } else if w == 0 { - return utf16_idx; - } else if r < 0x10000 { - utf16_idx += 1; - } else { - utf16_idx += 2; - } - - utf8_idx += w; - } - - return utf16_idx; -} +}
\ No newline at end of file diff --git a/src/common/track_allocator.odin b/src/common/track_allocator.odin deleted file mode 100644 index 978a74e..0000000 --- a/src/common/track_allocator.odin +++ /dev/null @@ -1,192 +0,0 @@ -package common - -/* - https://gist.github.com/jharler/7ee9a4d5b46e31f7f9399da49cfabe72 -*/ - -import "core:mem" -import "core:fmt" -import "core:runtime" -import "core:sync" -import "core:log" - -// ---------------------------------------------------------------------------------------------------- - -ThreadSafe_Allocator_Data :: struct { - actual_allocator: mem.Allocator, - mutex: sync.Mutex, -} - -// ---------------------------------------------------------------------------------------------------- - -threadsafe_allocator :: proc(allocator: mem.Allocator) -> mem.Allocator { - data := new(ThreadSafe_Allocator_Data); - data.actual_allocator = allocator; - sync.mutex_init(&data.mutex); - - return mem.Allocator {procedure = threadsafe_allocator_proc, data = data}; -} - -// ---------------------------------------------------------------------------------------------------- - -threadsafe_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, size, alignment: int, -old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr { - - data := cast(^ThreadSafe_Allocator_Data)allocator_data; - - sync.mutex_lock(&data.mutex); - defer sync.mutex_unlock(&data.mutex); - - return data.actual_allocator.procedure(data.actual_allocator.data, mode, size, alignment, old_memory, old_size, flags, loc); -} - -// ---------------------------------------------------------------------------------------------------- - -Memleak_Allocator_Data :: struct { - actual_allocator: mem.Allocator, - allocations: map[rawptr]Memleak_Entry, - frees: map[rawptr]Memleak_Entry, - allocation_count: u32, - unexpected_frees: u32, - mutex: sync.Mutex, - track_frees: bool, -} - -// ---------------------------------------------------------------------------------------------------- - -Memleak_Entry :: struct { - location: runtime.Source_Code_Location, - size: int, - index: u32, -} - -// ---------------------------------------------------------------------------------------------------- - -memleak_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, size, alignment: int, -old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr { - - memleak := cast(^Memleak_Allocator_Data)allocator_data; - - sync.mutex_lock(&memleak.mutex); - defer sync.mutex_unlock(&memleak.mutex); - - if mode == .Free { - if old_memory not_in memleak.allocations { - if memleak.track_frees { - if old_memory in memleak.frees { - fmt.println(fmt.tprintf("{0}({1}:{2}) {3} freed memory already freed by this memleak allocator", loc.file_path, loc.line, loc.column, loc.procedure)); - free_loc := memleak.frees[old_memory].location; - fmt.println(fmt.tprintf("{0}({1}:{2}) {3} <<< freed here", loc.file_path, loc.line, loc.column, loc.procedure)); - } else { - fmt.println(fmt.tprintf("{0}({1}:{2}) {3} freed memory not allocated or previously freed by this memleak allocator", loc.file_path, loc.line, loc.column, loc.procedure)); - } - } else { - fmt.println(fmt.tprintf("{0}({1}:{2}) {3} freed memory not allocated by this memleak allocator", loc.file_path, loc.line, loc.column, loc.procedure)); - } - memleak.unexpected_frees += 1; - return nil; - } else { - //entry := &memleak.allocations[old_memory]; - delete_key(&memleak.allocations, old_memory); - - if memleak.track_frees { - memleak.frees[old_memory] = Memleak_Entry { - location = loc, - size = size, - index = 0, - }; - } - } - } - - result := memleak.actual_allocator.procedure(memleak.actual_allocator.data, mode, size, alignment, old_memory, old_size, flags, loc); - - if mode == .Resize && result != old_memory { - delete_key(&memleak.allocations, old_memory); - } - - if mode != .Free { - // using a conditional breakpoint with memleak.allocation_count in the condition - // can be very useful for inspecting the stack trace of a particular allocation - - memleak.allocations[result] = Memleak_Entry { - location = loc, - size = size, - index = memleak.allocation_count, - }; - - memleak.allocation_count += 1; - - if memleak.track_frees { - if result in memleak.frees { - delete_key(&memleak.frees, result); - } - } - } - - return result; -} - -// ---------------------------------------------------------------------------------------------------- - -memleak_allocator :: proc(track_frees: bool) -> mem.Allocator { - - make([]byte, 1, context.temp_allocator); // so the temp allocation doesn't clutter our results - - data := new(Memleak_Allocator_Data); - data.actual_allocator = context.allocator; - data.allocations = make(map[rawptr]Memleak_Entry); - - if track_frees { - data.track_frees = true; - data.frees = make(map[rawptr]Memleak_Entry); - } - - sync.mutex_init(&data.mutex); - - return mem.Allocator {procedure = memleak_allocator_proc, data = data}; -} - -// ---------------------------------------------------------------------------------------------------- - -memleak_detected_leaks :: proc() -> bool { - if context.allocator.procedure == memleak_allocator_proc { - memleak := cast(^Memleak_Allocator_Data)context.allocator.data; - return len(memleak.allocations) > 0; - } - - return false; -} - -// ---------------------------------------------------------------------------------------------------- - -memleak_dump :: proc(memleak_alloc: mem.Allocator, dump_proc: proc(message: string, user_data: rawptr), user_data: rawptr) { - memleak := cast(^Memleak_Allocator_Data)memleak_alloc.data; - - context.allocator = memleak.actual_allocator; - - // check for an ignore default_temp_allocator_proc allocations - tmp_check := 0; - for _, leak in &memleak.allocations { - if leak.location.procedure == "default_temp_allocator_proc" { - tmp_check += 1; - } - } - - dump_proc(fmt.tprintf("{0} memory leaks detected!", len(memleak.allocations) - tmp_check), user_data); - dump_proc(fmt.tprintf("{0} unexpected frees", memleak.unexpected_frees), user_data); - - for _, leak in &memleak.allocations { - if leak.location.procedure != "default_temp_allocator_proc" { - dump_proc(fmt.tprintf("{0}({1}:{2}) {3} allocated {4} bytes [{5}]", leak.location.file_path, leak.location.line, leak.location.column, leak.location.procedure, leak.size, leak.index), user_data); - } - } - - context.allocator = mem.Allocator {procedure = memleak_allocator_proc, data = memleak}; -} - -// ---------------------------------------------------------------------------------------------------- - -log_dump :: proc(message: string, user_data: rawptr) { - log.info(message); -} diff --git a/src/index/build.odin b/src/index/build.odin index ad6ba2a..b464ce9 100644 --- a/src/index/build.odin +++ b/src/index/build.odin @@ -19,7 +19,7 @@ files: [dynamic]string; platform_os: map[string]bool = { "windows" = true, - "linux" = true, + "linux" = true, "essence" = true, "js" = true, "freebsd" = true, diff --git a/src/index/clone.odin b/src/index/clone.odin index f9a7604..0380c78 100644 --- a/src/index/clone.odin +++ b/src/index/clone.odin @@ -30,7 +30,7 @@ clone_array :: proc(array: $A/[]^$T, allocator: mem.Allocator, unique_strings: ^ } res := make(A, len(array), allocator); for elem, i in array { - res[i] = auto_cast clone_type(elem, allocator, unique_strings); + res[i] = cast(^T)clone_type(elem, allocator, unique_strings); } return res; } @@ -43,7 +43,7 @@ clone_dynamic_array :: proc(array: $A/[dynamic]^$T, allocator: mem.Allocator, un for elem, i in array { res[i] = auto_cast clone_type(elem, allocator, unique_strings); } - return res; + return res; } clone_expr :: proc(node: ^ast.Expr, allocator: mem.Allocator, unique_strings: ^map[string]string) -> ^ast.Expr { @@ -211,6 +211,8 @@ clone_node :: proc(node: ^ast.Node, allocator: mem.Allocator, unique_strings: ^m case Proc_Lit: r := cast(^Proc_Lit)res; r.type = cast(^Proc_Type)clone_type(cast(^Node)r.type, allocator, unique_strings); + r.body = nil; + r.where_clauses = nil; case Helper_Type: r := cast(^Helper_Type)res; r.type = clone_type(r.type, allocator, unique_strings); @@ -218,6 +220,9 @@ clone_node :: proc(node: ^ast.Node, allocator: mem.Allocator, unique_strings: ^m r := cast(^Type_Cast)res; r.type = clone_type(r.type, allocator, unique_strings); r.expr = clone_type(r.expr, allocator, unique_strings); + case Deref_Expr: + r := cast(^Deref_Expr)res; + r.expr = clone_type(r.expr, allocator, unique_strings); case: panic(fmt.aprintf("Clone type Unhandled node kind: %T", node.derived)); } diff --git a/src/index/collector.odin b/src/index/collector.odin index 14fc772..0125762 100644 --- a/src/index/collector.odin +++ b/src/index/collector.odin @@ -12,7 +12,7 @@ import "core:strconv" import "shared:common" -SymbolCollection :: struct { +SymbolCollection :: struct { allocator: mem.Allocator, config: ^common.Config, symbols: map[uint]Symbol, @@ -161,7 +161,10 @@ collect_union_fields :: proc(collection: ^SymbolCollection, union_type: ast.Unio } } - append(&types, clone_type(variant, collection.allocator, &collection.unique_strings)); + cloned := clone_type(variant, collection.allocator, &collection.unique_strings); + replace_package_alias(cloned, package_map, collection); + + append(&types, cloned); } value := SymbolUnionValue { @@ -174,8 +177,11 @@ collect_union_fields :: proc(collection: ^SymbolCollection, union_type: ast.Unio collect_bitset_field :: proc(collection: ^SymbolCollection, bitset_type: ast.Bit_Set_Type, package_map: map[string]string) -> SymbolBitSetValue { + cloned := clone_type(bitset_type.elem, collection.allocator, &collection.unique_strings); + replace_package_alias(cloned, package_map, collection); + value := SymbolBitSetValue { - expr = clone_type(bitset_type.elem, collection.allocator, &collection.unique_strings), + expr = cloned, }; return value; @@ -195,8 +201,7 @@ collect_generic :: proc(collection: ^SymbolCollection, expr: ^ast.Expr, package_ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: string) -> common.Error { - forward, _ := filepath.to_slash(file.fullpath, context.temp_allocator); - package_map := get_package_mapping(file, collection.config, uri); + forward, _ := filepath.to_slash(file.fullpath, context.temp_allocator); when ODIN_OS == "windows" { directory := strings.to_lower(path.dir(forward, context.temp_allocator), context.temp_allocator); @@ -204,9 +209,9 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri directory := path.dir(forward, context.temp_allocator); } - + package_map := get_package_mapping(file, collection.config, directory); - exprs := common.collect_globals(file); + exprs := common.collect_globals(file, true); for expr in exprs { @@ -295,13 +300,20 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri symbol.value = collect_generic(collection, col_expr, package_map); case ast.Ident: token = v; - token_type = .Variable; symbol.value = collect_generic(collection, col_expr, package_map); + if expr.mutable { + token_type = .Variable; + } else { + token_type = .Unresolved; + } case: // default symbol.value = collect_generic(collection, col_expr, package_map); - token_type = .Variable; - token = expr.expr; - break; + if expr.mutable { + token_type = .Variable; + } else { + token_type = .Unresolved; + } + token = expr.expr; } symbol.range = common.get_token_range(token, file.src); @@ -314,8 +326,8 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri } else { symbol.uri = get_index_unique_string(collection, uri); } - - + + if expr.docs != nil { tmp: string; @@ -347,7 +359,7 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri /* Gets the map from import alias to absolute package directory */ -get_package_mapping :: proc(file: ast.File, config: ^common.Config, uri: string) -> map[string]string { +get_package_mapping :: proc(file: ast.File, config: ^common.Config, directory: string) -> map[string]string { package_map := make(map[string]string, 0, context.temp_allocator); @@ -389,15 +401,12 @@ get_package_mapping :: proc(file: ast.File, config: ^common.Config, uri: string) name: string; - base := path.base(uri, false, context.temp_allocator); - - full := path.join(elems = {base, imp.fullpath[1:len(imp.fullpath) - 1]}, allocator = context.temp_allocator); + full := path.join(elems = {directory, imp.fullpath[1:len(imp.fullpath) - 1]}, allocator = context.temp_allocator); full = path.clean(full, context.temp_allocator); if imp.name.text != "" { name = imp.name.text; - //ERROR hover is wrong on name } else { name = path.base(full, false, context.temp_allocator); } @@ -541,6 +550,9 @@ replace_package_alias_node :: proc(node: ^ast.Node, package_map: map[string]stri case Comp_Lit: replace_package_alias(n.type, package_map, collection); replace_package_alias(n.elems, package_map, collection); + case Helper_Type: + replace_package_alias(n.type, package_map, collection); + case Proc_Lit: case: log.warnf("Replace Unhandled node kind: %T", n); } diff --git a/src/index/symbol.odin b/src/index/symbol.odin index 9fd656d..0f9069a 100644 --- a/src/index/symbol.odin +++ b/src/index/symbol.odin @@ -4,7 +4,6 @@ import "core:odin/ast" import "core:hash" import "core:strings" import "core:mem" -import "core:fmt" import "core:path/filepath" import "core:path" import "core:slice" @@ -88,13 +87,14 @@ SymbolType :: enum { Keyword = 14, EnumMember = 20, Struct = 22, + Unresolved = 9999, } free_symbol :: proc(symbol: Symbol, allocator: mem.Allocator) { if symbol.signature != "" && symbol.signature != "struct" && symbol.signature != "union" && symbol.signature != "enum" && - symbol.signature != "bitset" && symbol.signature != "bitfield" { + symbol.signature != "bitset" { delete(symbol.signature, allocator); } diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 87fb54e..7f108e2 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -39,26 +39,27 @@ DocumentPositionContext :: struct { position: common.AbsolutePosition, line: int, function: ^ast.Proc_Lit, //used to help with type resolving in function scope - selector: ^ast.Expr, //used for completion + selector: ^ast.Expr, //used for completion identifier: ^ast.Node, tag: ^ast.Node, - field: ^ast.Expr, //used for completion - call: ^ast.Expr, //used for signature help + field: ^ast.Expr, //used for completion + call: ^ast.Expr, //used for signature help returns: ^ast.Return_Stmt, //used for completion - comp_lit: ^ast.Comp_Lit, //used for completion - parent_comp_lit: ^ast.Comp_Lit, //used for completion - implicit: bool, //used for completion + comp_lit: ^ast.Comp_Lit, //used for completion + parent_comp_lit: ^ast.Comp_Lit, //used for completion + implicit: bool, //used for completion arrow: bool, - binary: ^ast.Binary_Expr, //used for completion - parent_binary: ^ast.Binary_Expr, //used for completion - assign: ^ast.Assign_Stmt, //used for completion - switch_stmt: ^ast.Switch_Stmt, //used for completion + binary: ^ast.Binary_Expr, //used for completion + parent_binary: ^ast.Binary_Expr, //used for completion + assign: ^ast.Assign_Stmt, //used for completion + switch_stmt: ^ast.Switch_Stmt, //used for completion switch_type_stmt: ^ast.Type_Switch_Stmt, //used for completion - case_clause: ^ast.Case_Clause, //used for completion - value_decl: ^ast.Value_Decl, //used for completion + case_clause: ^ast.Case_Clause, //used for completion + value_decl: ^ast.Value_Decl, //used for completion abort_completion: bool, hint: DocumentPositionContextHint, global_lhs_stmt: bool, + import_stmt: ^ast.Import_Decl, } DocumentLocal :: struct { @@ -67,26 +68,28 @@ DocumentLocal :: struct { } AstContext :: struct { - locals: map[string][dynamic]DocumentLocal, //locals all the way to the document position - globals: map[string]^ast.Expr, - variables: map[string]bool, - parameters: map[string]bool, - in_package: map[string]string, //sometimes you have to extract types from arrays/maps and you lose package information - usings: [dynamic]string, - file: ast.File, - allocator: mem.Allocator, - imports: []Package, //imports for the current document - current_package: string, - document_package: string, - use_globals: bool, - use_locals: bool, - call: ^ast.Call_Expr, //used to determene the types for generics and the correct function for overloaded functions - position: common.AbsolutePosition, - value_decl: ^ast.Value_Decl, - field_name: string, -} - -make_ast_context :: proc(file: ast.File, imports: []Package, package_name: string, allocator := context.temp_allocator) -> AstContext { + locals: map[string][dynamic]DocumentLocal, //locals all the way to the document position + globals: map[string]^ast.Expr, + variables: map[string]bool, + parameters: map[string]bool, + in_package: map[string]string, //sometimes you have to extract types from arrays/maps and you lose package information + usings: [dynamic]string, + file: ast.File, + allocator: mem.Allocator, + imports: []Package, //imports for the current document + current_package: string, + document_package: string, + use_globals: bool, + use_locals: bool, + call: ^ast.Call_Expr, //used to determene the types for generics and the correct function for overloaded functions + position: common.AbsolutePosition, + value_decl: ^ast.Value_Decl, + field_name: string, + uri: string, + +} + +make_ast_context :: proc(file: ast.File, imports: []Package, package_name: string, uri: string, allocator := context.temp_allocator) -> AstContext { ast_context := AstContext { locals = make(map[string][dynamic]DocumentLocal, 0, allocator), @@ -101,7 +104,13 @@ make_ast_context :: proc(file: ast.File, imports: []Package, package_name: strin use_globals = true, document_package = package_name, current_package = package_name, + uri = uri, }; + + when ODIN_OS == "windows" { + ast_context.uri = strings.to_lower(ast_context.uri, allocator); + } + return ast_context; } @@ -379,10 +388,10 @@ resolve_generic_function_symbol :: proc(ast_context: ^AstContext, params: []^ast function_range: common.Range; if ident, ok := call_expr.expr.derived.(Ident); ok { - function_name = ident.name; + function_name = ident.name; function_range = common.get_token_range(ident, ast_context.file.src); } else if selector, ok := call_expr.expr.derived.(Selector_Expr); ok { - function_name = selector.field.name; + function_name = selector.field.name; function_range = common.get_token_range(selector, ast_context.file.src); } else { log.debug("call expr expr could not be derived correctly"); @@ -420,8 +429,6 @@ resolve_generic_function_symbol :: proc(ast_context: ^AstContext, params: []^ast arg_types = params, }; - //log.infof("return %v", poly_map); - return symbol, true; } @@ -451,10 +458,7 @@ resolve_function_overload :: proc(ast_context: ^AstContext, group: ast.Proc_Grou using ast; - //log.info("overload"); - if ast_context.call == nil { - //log.info("no call"); return index.Symbol {}, false; } @@ -623,7 +627,7 @@ resolve_type_expression :: proc(ast_context: ^AstContext, node: ^ast.Expr) -> (i if len(s.return_types) == 1 { selector_expr := index.new_type(ast.Selector_Expr, s.return_types[0].node.pos, s.return_types[0].node.end, context.temp_allocator); - selector_expr.expr = s.return_types[0].type; + selector_expr.expr = s.return_types[0].type; selector_expr.field = v.field; return resolve_type_expression(ast_context, selector_expr); } @@ -672,7 +676,7 @@ store_local :: proc(ast_context: ^AstContext, expr: ^ast.Expr, offset: int, name if local_stack == nil { ast_context.locals[name] = make([dynamic]DocumentLocal, context.temp_allocator); - local_stack = &ast_context.locals[name]; + local_stack = &ast_context.locals[name]; } append(local_stack, DocumentLocal {expr = expr, offset = offset}); @@ -684,9 +688,7 @@ get_local :: proc(ast_context: ^AstContext, offset: int, name: string) -> ^ast.E //is the local we are getting being declared? if ast_context.value_decl != nil { - for value_decl_name in ast_context.value_decl.names { - if ident, ok := value_decl_name.derived.(ast.Ident); ok { if ident.name == name { @@ -698,11 +700,13 @@ get_local :: proc(ast_context: ^AstContext, offset: int, name: string) -> ^ast.E } if local_stack, ok := ast_context.locals[name]; ok { - for i := len(local_stack) - 1; i >= 0; i -= 1 { - if local_stack[i].offset <= offset { - return local_stack[max(0, i - previous)].expr; + if i - previous < 0 { + return nil; + } else { + return local_stack[i - previous].expr; + } } } } @@ -724,9 +728,7 @@ resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (i if _, ok := ast_context.parameters[node.name]; ok { for imp in ast_context.imports { - if strings.compare(imp.base, node.name) == 0 { - symbol := index.Symbol { type = .Package, pkg = imp.name, @@ -741,13 +743,14 @@ resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (i //note(Daniel, if global and local ends up being 100% same just make a function that takes the map) if local := get_local(ast_context, node.pos.offset, node.name); local != nil && ast_context.use_locals { - switch v in local.derived { - case Ident: - - if node.name == v.name { - break; + if dist, ok := local.derived.(ast.Distinct_Type); ok { + if dist.type != nil { + local = dist.type; //save information for overloading } + } + switch v in local.derived { + case Ident: return resolve_type_identifier(ast_context, v); case Union_Type: return make_symbol_union_from_ast(ast_context, v, node), true; @@ -774,17 +777,17 @@ resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (i case: log.warnf("default type node kind: %T", v); return resolve_type_expression(ast_context, local); - //return make_symbol_generic_from_ast(ast_context, local), true; } } else if global, ok := ast_context.globals[node.name]; ast_context.use_globals && ok { - switch v in global.derived { - case Ident: - - if node.name == v.name { - break; + if dist, ok := global.derived.(ast.Distinct_Type); ok { + if dist.type != nil { + global = dist.type; //save information for overloading } + } + switch v in global.derived { + case Ident: return resolve_type_identifier(ast_context, v); case Struct_Type: return make_symbol_struct_from_ast(ast_context, v, node), true; @@ -879,7 +882,6 @@ resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (i } } - //TODO(daniel, index can be used on identifiers if using is in the function scope) } return index.Symbol {}, false; @@ -920,7 +922,7 @@ expand_struct_usings :: proc(ast_context: ^AstContext, symbol: index.Symbol, val //ERROR no completion or over on names and types - generic resolve error names := slice.to_dynamic(value.names, context.temp_allocator); types := slice.to_dynamic(value.types, context.temp_allocator); - + //ERROR no hover on k and v(completion works) for k, v in value.usings { @@ -966,6 +968,12 @@ resolve_symbol_return :: proc(ast_context: ^AstContext, symbol: index.Symbol, ok return symbol, ok; } + symbol := symbol; + + if symbol.type == .Unresolved { + fix_symbol_unresolved_type(&symbol); + } + #partial switch v in symbol.value { case index.SymbolProcedureGroupValue: if symbol, ok := resolve_function_overload(ast_context, v.group.derived.(ast.Proc_Group)); ok { @@ -997,6 +1005,29 @@ resolve_symbol_return :: proc(ast_context: ^AstContext, symbol: index.Symbol, ok return symbol, true; } +fix_symbol_unresolved_type :: proc(symbol: ^index.Symbol) { + + using index; + + switch v in symbol.value { + case SymbolStructValue: + symbol.type = .Struct; + case SymbolPackageValue: + symbol.type = .Package; + case SymbolProcedureValue, SymbolProcedureGroupValue: + symbol.type = .Function; + case SymbolGenericValue: + symbol.type = .Variable; + case SymbolUnionValue: + symbol.type = .Enum; + case SymbolEnumValue: + symbol.type = .Enum; + case SymbolBitSetValue: + symbol.type = .Enum; + } + +} + resolve_location_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (index.Symbol, bool) { symbol: index.Symbol; @@ -1359,7 +1390,7 @@ get_globals :: proc(file: ast.File, ast_context: ^AstContext) { exprs := common.collect_globals(file); for expr in exprs { - ast_context.globals[expr.name] = expr.expr; + ast_context.globals[expr.name] = expr.expr; ast_context.variables[expr.name] = expr.mutable; } } @@ -1368,7 +1399,7 @@ get_generic_assignment :: proc(file: ast.File, value: ^ast.Expr, ast_context: ^A using ast; - ast_context.use_locals = true; + ast_context.use_locals = true; ast_context.use_globals = true; switch v in value.derived { @@ -1424,7 +1455,7 @@ get_locals_value_decl :: proc(file: ast.File, value_decl: ast.Value_Decl, ast_co if value_decl.type != nil { str := common.get_ast_node_string(value_decl.names[0], file.src); ast_context.variables[str] = value_decl.is_mutable; - store_local(ast_context, value_decl.type, value_decl.pos.offset, str); + store_local(ast_context, value_decl.type, value_decl.end.offset, str); return; } @@ -1438,7 +1469,7 @@ get_locals_value_decl :: proc(file: ast.File, value_decl: ast.Value_Decl, ast_co if i < len(results) { str := common.get_ast_node_string(name, file.src); ast_context.in_package[str] = get_package_from_node(results[i]); - store_local(ast_context, results[i], name.pos.offset, str); + store_local(ast_context, results[i], value_decl.end.offset, str); ast_context.variables[str] = value_decl.is_mutable; } } @@ -1446,8 +1477,8 @@ get_locals_value_decl :: proc(file: ast.File, value_decl: ast.Value_Decl, ast_co get_locals_stmt :: proc(file: ast.File, stmt: ^ast.Stmt, ast_context: ^AstContext, document_position: ^DocumentPositionContext, save_assign := false) { - ast_context.use_locals = true; - ast_context.use_globals = true; + ast_context.use_locals = true; + ast_context.use_globals = true; ast_context.current_package = ast_context.document_package; using ast; @@ -1509,8 +1540,8 @@ get_locals_using_stmt :: proc(stmt: ast.Using_Stmt, ast_context: ^AstContext) { case index.SymbolStructValue: for name, i in v.names { selector := index.new_type(ast.Selector_Expr, v.types[i].pos, v.types[i].end, context.temp_allocator); - selector.expr = u; - selector.field = index.new_type(ast.Ident, v.types[i].pos, v.types[i].end, context.temp_allocator); + selector.expr = u; + selector.field = index.new_type(ast.Ident, v.types[i].pos, v.types[i].end, context.temp_allocator); selector.field.name = name; store_local(ast_context, selector, 0, name); ast_context.variables[name] = true; @@ -1577,10 +1608,18 @@ get_locals_for_range_stmt :: proc(file: ast.File, stmt: ast.Range_Stmt, ast_cont switch v in generic.expr.derived { case Map_Type: - for val in stmt.vals { - if ident, ok := val.derived.(Ident); ok { + if len(stmt.vals) >= 1 { + if ident, ok := stmt.vals[0].derived.(Ident); ok { store_local(ast_context, v.key, ident.pos.offset, ident.name); - ast_context.variables[ident.name] = true; + ast_context.variables[ident.name] = true; + ast_context.in_package[ident.name] = symbol.pkg; + } + } + + if len(stmt.vals) >= 2 { + if ident, ok := stmt.vals[1].derived.(Ident); ok { + store_local(ast_context, v.value, ident.pos.offset, ident.name); + ast_context.variables[ident.name] = true; ast_context.in_package[ident.name] = symbol.pkg; } } @@ -1588,7 +1627,7 @@ get_locals_for_range_stmt :: proc(file: ast.File, stmt: ast.Range_Stmt, ast_cont if len(stmt.vals) >= 1 { if ident, ok := stmt.vals[0].derived.(Ident); ok { store_local(ast_context, v.elem, ident.pos.offset, ident.name); - ast_context.variables[ident.name] = true; + ast_context.variables[ident.name] = true; ast_context.in_package[ident.name] = symbol.pkg; } } @@ -1596,7 +1635,7 @@ get_locals_for_range_stmt :: proc(file: ast.File, stmt: ast.Range_Stmt, ast_cont if len(stmt.vals) >= 2 { if ident, ok := stmt.vals[1].derived.(Ident); ok { store_local(ast_context, make_int_ast(), ident.pos.offset, ident.name); - ast_context.variables[ident.name] = true; + ast_context.variables[ident.name] = true; ast_context.in_package[ident.name] = symbol.pkg; } } @@ -1605,7 +1644,7 @@ get_locals_for_range_stmt :: proc(file: ast.File, stmt: ast.Range_Stmt, ast_cont if ident, ok := stmt.vals[0].derived.(Ident); ok { store_local(ast_context, v.elem, ident.pos.offset, ident.name); - ast_context.variables[ident.name] = true; + ast_context.variables[ident.name] = true; ast_context.in_package[ident.name] = symbol.pkg; } } @@ -1614,7 +1653,7 @@ get_locals_for_range_stmt :: proc(file: ast.File, stmt: ast.Range_Stmt, ast_cont if ident, ok := stmt.vals[1].derived.(Ident); ok { store_local(ast_context, make_int_ast(), ident.pos.offset, ident.name); - ast_context.variables[ident.name] = true; + ast_context.variables[ident.name] = true; ast_context.in_package[ident.name] = symbol.pkg; } } @@ -1694,12 +1733,12 @@ get_locals :: proc(file: ast.File, function: ^ast.Node, ast_context: ^AstContext if arg.type != nil { str := common.get_ast_node_string(name, file.src); store_local(ast_context, arg.type, name.pos.offset, str); - ast_context.variables[str] = true; + ast_context.variables[str] = true; ast_context.parameters[str] = true; if .Using in arg.flags { using_stmt: ast.Using_Stmt; - using_stmt.list = make([]^ast.Expr, 1, context.temp_allocator); + using_stmt.list = make([]^ast.Expr, 1, context.temp_allocator); using_stmt.list[0] = arg.type; get_locals_using_stmt(using_stmt, ast_context); } @@ -1716,7 +1755,7 @@ get_locals :: proc(file: ast.File, function: ^ast.Node, ast_context: ^AstContext if result.type != nil { str := common.get_ast_node_string(name, file.src); store_local(ast_context, result.type, name.pos.offset, str); - ast_context.variables[str] = true; + ast_context.variables[str] = true; ast_context.parameters[str] = true; } } @@ -1771,7 +1810,7 @@ get_definition_location :: proc(document: ^Document, position: common.Position) location: common.Location; - ast_context := make_ast_context(document.ast, document.imports, document.package_name); + ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri); uri: string; @@ -1817,8 +1856,8 @@ get_definition_location :: proc(document: ^Document, position: common.Position) selector: index.Symbol; - ast_context.use_locals = true; - ast_context.use_globals = true; + ast_context.use_locals = true; + ast_context.use_globals = true; ast_context.current_package = ast_context.document_package; selector, ok = resolve_type_expression(&ast_context, position_context.selector); @@ -1851,7 +1890,7 @@ get_definition_location :: proc(document: ^Document, position: common.Position) case index.SymbolPackageValue: if symbol, ok := index.lookup(field, selector.pkg); ok { location.range = symbol.range; - uri = symbol.uri; + uri = symbol.uri; } else { return location, false; } @@ -1864,7 +1903,7 @@ get_definition_location :: proc(document: ^Document, position: common.Position) if resolved, ok := resolve_location_identifier(&ast_context, position_context.identifier.derived.(ast.Ident)); ok { location.range = resolved.range; - uri = resolved.uri; + uri = resolved.uri; } else { return location, false; } @@ -1888,7 +1927,7 @@ write_hover_content :: proc(ast_context: ^AstContext, symbol: index.Symbol) -> M cat := concatenate_symbols_information(ast_context, symbol, false); if cat != "" { - content.kind = "markdown"; + content.kind = "markdown"; content.value = fmt.tprintf("```odin\n %v\n```\n%v", cat, symbol.doc); } else { content.kind = "plaintext"; @@ -1941,7 +1980,7 @@ get_signature_information :: proc(document: ^Document, position: common.Position signature_help: SignatureHelp; - ast_context := make_ast_context(document.ast, document.imports, document.package_name); + ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri); position_context, ok := get_document_position_context(document, position, .SignatureHelp); @@ -1968,10 +2007,10 @@ get_signature_information :: proc(document: ^Document, position: common.Position signature_information := make([]SignatureInformation, 1, context.temp_allocator); - signature_information[0].label = concatenate_symbols_information(&ast_context, call, false); + signature_information[0].label = concatenate_symbols_information(&ast_context, call, false); signature_information[0].documentation = call.doc; - signature_help.signatures = signature_information; + signature_help.signatures = signature_information; signature_help.activeSignature = 0; signature_help.activeParameter = 0; @@ -1980,7 +2019,7 @@ get_signature_information :: proc(document: ^Document, position: common.Position get_document_symbols :: proc(document: ^Document) -> []DocumentSymbol { - ast_context := make_ast_context(document.ast, document.imports, document.package_name); + ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri); get_globals(document.ast, &ast_context); @@ -1992,8 +2031,8 @@ get_document_symbols :: proc(document: ^Document) -> []DocumentSymbol { return {}; } - package_symbol.kind = .Package; - package_symbol.name = path.base(document.package_name, false, context.temp_allocator); + package_symbol.kind = .Package; + package_symbol.name = path.base(document.package_name, false, context.temp_allocator); package_symbol.range = { start = { line = document.ast.decls[0].pos.line, @@ -2010,16 +2049,16 @@ get_document_symbols :: proc(document: ^Document) -> []DocumentSymbol { symbol: DocumentSymbol; - symbol.range = common.get_token_range(expr, ast_context.file.src); + symbol.range = common.get_token_range(expr, ast_context.file.src); symbol.selectionRange = symbol.range; - symbol.name = k; + symbol.name = k; switch v in expr.derived { case ast.Struct_Type: symbol.kind = .Struct; - case ast.Proc_Lit,ast.Proc_Group: + case ast.Proc_Lit, ast.Proc_Group: symbol.kind = .Function; - case ast.Enum_Type,ast.Union_Type: + case ast.Enum_Type, ast.Union_Type: symbol.kind = .Enum; case: symbol.kind = .Variable; @@ -2055,17 +2094,31 @@ get_document_position_context :: proc(document: ^Document, position: common.Posi position_context.position = absolute_position; + exists_in_decl := false; + for decl in document.ast.decls { if position_in_node(decl, position_context.position) { get_document_position(decl, &position_context); - + exists_in_decl = true; switch v in decl.derived { case ast.Expr_Stmt: position_context.global_lhs_stmt = true; } + break; + } + } + + for import_stmt in document.ast.imports { + if position_in_node(import_stmt, position_context.position) { + position_context.import_stmt = import_stmt; + break; } } + if !exists_in_decl && position_context.import_stmt == nil { + position_context.abort_completion = true; + } + if !position_in_node(position_context.comp_lit, position_context.position) { position_context.comp_lit = nil; } @@ -2154,15 +2207,15 @@ fallback_position_context_completion :: proc(document: ^Document, position: comm if c == ' ' || c == '{' || c == ',' || c == '}' || c == '^' || c == ':' || c == '\n' || c == '\r' || c == '=' || - c == '<' || c == '-' || - c == '+' || c == '&' { + c == '<' || c == '-' || c == '!' || + c == '+' || c == '&'|| c == '|' { start = i + 1; break; } else if c == '>' { partial_arrow = true; } - last_dot = false; + last_dot = false; last_arrow = false; i -= 1; @@ -2189,12 +2242,12 @@ fallback_position_context_completion :: proc(document: ^Document, position: comm return; } + s := string(position_context.file.src[begin_offset:end_offset]); + if !partial_arrow { only_whitespaces := true; - s := string(position_context.file.src[begin_offset:end_offset]); - for r in s { if !strings.is_space(r) { only_whitespaces = false; @@ -2207,16 +2260,16 @@ fallback_position_context_completion :: proc(document: ^Document, position: comm } p := parser.Parser { - err = parser_warning_handler, //empty + err = parser_warning_handler, //empty warn = parser_warning_handler, //empty file = &position_context.file, }; tokenizer.init(&p.tok, str, position_context.file.fullpath, parser_warning_handler); - p.tok.ch = ' '; - p.tok.line_count = position.line; - p.tok.offset = begin_offset; + p.tok.ch = ' '; + p.tok.line_count = position.line; + p.tok.offset = begin_offset; p.tok.read_offset = begin_offset; tokenizer.advance_rune(&p.tok); @@ -2235,7 +2288,7 @@ fallback_position_context_completion :: proc(document: ^Document, position: comm position_context.selector = e; } else if s, ok := e.derived.(ast.Selector_Expr); ok { position_context.selector = s.expr; - position_context.field = s.field; + position_context.field = s.field; } else if s, ok := e.derived.(ast.Implicit_Selector_Expr); ok { position_context.implicit = true; } else if s, ok := e.derived.(ast.Tag_Expr); ok { @@ -2253,9 +2306,9 @@ fallback_position_context_completion :: proc(document: ^Document, position: comm tokenizer.init(&p.tok, position_context.file.src[0:last_dot], position_context.file.fullpath, parser_warning_handler); - p.tok.ch = ' '; - p.tok.line_count = position.line; - p.tok.offset = begin_offset; + p.tok.ch = ' '; + p.tok.line_count = position.line; + p.tok.offset = begin_offset; p.tok.read_offset = begin_offset; tokenizer.advance_rune(&p.tok); @@ -2320,16 +2373,16 @@ fallback_position_context_signature :: proc(document: ^Document, position: commo str := position_context.file.src[0:end_offset]; p := parser.Parser { - err = parser_warning_handler, //empty + err = parser_warning_handler, //empty warn = parser_warning_handler, //empty file = &position_context.file, }; tokenizer.init(&p.tok, str, position_context.file.fullpath, parser_warning_handler); - p.tok.ch = ' '; - p.tok.line_count = position.line; - p.tok.offset = begin_offset; + p.tok.ch = ' '; + p.tok.line_count = position.line; + p.tok.offset = begin_offset; p.tok.read_offset = begin_offset; tokenizer.advance_rune(&p.tok); @@ -2342,11 +2395,7 @@ fallback_position_context_signature :: proc(document: ^Document, position: commo context.allocator = context.temp_allocator; - e := parser.parse_expr(&p, true); - - if call, ok := e.derived.(ast.Call_Expr); ok { - position_context.call = e; - } + position_context.call = parser.parse_expr(&p, true); //log.error(string(position_context.file.src[begin_offset:end_offset])); } @@ -2355,7 +2404,7 @@ fallback_position_context_signature :: proc(document: ^Document, position: commo All these fallback functions are not perfect and should be fixed. A lot of weird use of the odin tokenizer and parser. */ -get_document_position :: proc { +get_document_position ::proc { get_document_position_array, get_document_position_dynamic_array, get_document_position_node, @@ -2439,12 +2488,13 @@ get_document_position_node :: proc(node: ^ast.Node, position_context: ^DocumentP case Selector_Expr: if position_context.hint == .Completion { if n.field != nil && n.field.pos.line - 1 == position_context.line { - position_context.selector = n.expr; - position_context.field = n.field; + //The parser is not fault tolerant enough, relying on the fallback as the main completion parsing for now + //position_context.selector = n.expr; + //position_context.field = n.field; } } else if (position_context.hint == .Definition || position_context.hint == .Hover) && n.field != nil { position_context.selector = n.expr; - position_context.field = n.field; + position_context.field = n.field; get_document_position(n.expr, position_context); get_document_position(n.field, position_context); } else { diff --git a/src/server/completion.odin b/src/server/completion.odin index 7dac1b9..cf26b9c 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -12,6 +12,8 @@ import "core:strconv" import "core:path/filepath" import "core:sort" import "core:slice" +import "core:os" + import "shared:common" import "shared:index" @@ -23,9 +25,10 @@ Completion_Type :: enum { Identifier, Comp_Lit, Directive, + Package, } -get_completion_list :: proc(document: ^Document, position: common.Position) -> (CompletionList, bool) { +get_completion_list :: proc(document: ^Document, position: common.Position, completion_context: CompletionContext) -> (CompletionList, bool) { list: CompletionList; @@ -35,7 +38,11 @@ get_completion_list :: proc(document: ^Document, position: common.Position) -> ( return list, true; } - ast_context := make_ast_context(document.ast, document.imports, document.package_name); + if position_context.import_stmt == nil && strings.contains_any(completion_context.triggerCharacter, "/:\"") { + return list, true; + } + + ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri); get_globals(document.ast, &ast_context); @@ -64,10 +71,17 @@ get_completion_list :: proc(document: ^Document, position: common.Position) -> ( completion_type = .Implicit; } + if position_context.import_stmt != nil { + completion_type = .Package; + } + if position_context.switch_type_stmt != nil && position_context.case_clause != nil { if assign, ok := position_context.switch_type_stmt.tag.derived.(ast.Assign_Stmt); ok && assign.rhs != nil && len(assign.rhs) == 1 { + ast_context.use_globals = true; + ast_context.use_locals = true; + if symbol, ok := resolve_type_expression(&ast_context, assign.rhs[0]); ok { if union_value, ok := symbol.value.(index.SymbolUnionValue); ok { @@ -87,9 +101,11 @@ get_completion_list :: proc(document: ^Document, position: common.Position) -> ( case .Selector: get_selector_completion(&ast_context, &position_context, &list); case .Switch_Type: - get_type_switch_Completion(&ast_context, &position_context, &list); + get_type_switch_completion(&ast_context, &position_context, &list); case .Directive: get_directive_completion(&ast_context, &position_context, &list); + case .Package: + get_package_completion(&ast_context, &position_context, &list); } return list, true; @@ -203,7 +219,9 @@ get_comp_lit_completion :: proc(ast_context: ^AstContext, position_context: ^Doc #partial switch v in comp_symbol.value { case index.SymbolStructValue: for name, i in v.names { - //ERROR no completion on name and hover + + ast_context.current_package = comp_symbol.pkg; + if resolved, ok := resolve_type_expression(ast_context, v.types[i]); ok { if field_exists_in_comp_lit(position_context.comp_lit, name) { @@ -724,12 +742,8 @@ get_implicit_completion :: proc(ast_context: ^AstContext, position_context: ^Doc if proc_value, ok := symbol.value.(index.SymbolProcedureValue); ok { - log.error("procedure symbol"); - if enum_value, ok := unwrap_enum(ast_context, proc_value.arg_types[parameter_index].type); ok { - log.error("unwrap"); - for name in enum_value.names { item := CompletionItem { label = name, @@ -800,9 +814,10 @@ get_identifier_completion :: proc(ast_context: ^AstContext, position_context: ^D append(&pkgs, ast_context.document_package); if results, ok := index.fuzzy_search(lookup, pkgs[:]); ok { - for r in results { - append(&combined, CombinedResult {score = r.score, symbol = r.symbol}); + if r.symbol.uri != ast_context.uri { + append(&combined, CombinedResult {score = r.score, symbol = r.symbol}); + } } } @@ -938,9 +953,92 @@ get_identifier_completion :: proc(ast_context: ^AstContext, position_context: ^D } get_package_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) { + + items := make([dynamic]CompletionItem, context.temp_allocator); + + list.isIncomplete = false; + + fullpath_length := len(position_context.import_stmt.fullpath); + + if fullpath_length <= 1 { + return; + } + + without_quotes := position_context.import_stmt.fullpath[1:fullpath_length-1]; + absolute_path := without_quotes; + colon_index := strings.index(without_quotes, ":"); + + if colon_index >= 0 { + c := without_quotes[0:colon_index]; + + if colon_index+1 < len(without_quotes) { + absolute_path = filepath.join(elems = {common.config.collections[c], filepath.dir(without_quotes[colon_index+1:], context.temp_allocator)}, allocator = context.temp_allocator); + } else { + absolute_path = common.config.collections[c]; + } + } else { + import_file_dir := filepath.dir(position_context.import_stmt.pos.file, context.temp_allocator); + import_dir := filepath.dir(without_quotes, context.temp_allocator); + absolute_path = filepath.join(elems = {import_file_dir, import_dir}, allocator = context.temp_allocator); + } + + if !strings.contains(position_context.import_stmt.fullpath, "/") && !strings.contains(position_context.import_stmt.fullpath, ":") { + + for key, _ in common.config.collections { + + item := CompletionItem { + detail = "collection", + label = key, + kind = .Module, + }; + + append(&items, item); + } + + } + + for pkg in search_for_packages(absolute_path) { + + item := CompletionItem { + detail = pkg, + label = filepath.base(pkg), + kind = .Folder, + }; + + if item.label[0] == '.' { + continue; + } + + append(&items, item); + } + + list.items = items[:]; } -get_type_switch_Completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) { +search_for_packages :: proc(fullpath: string) -> [] string { + + packages := make([dynamic]string, context.temp_allocator); + + fh, err := os.open(fullpath); + + if err != 0 { + return {}; + } + + if files, err := os.read_dir(fh, 0, context.temp_allocator); err == 0 { + + for file in files { + if file.is_dir { + append(&packages, file.fullpath); + } + } + + } + + return packages[:]; +} + +get_type_switch_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) { items := make([dynamic]CompletionItem, context.temp_allocator); list.isIncomplete = false; @@ -963,6 +1061,9 @@ get_type_switch_Completion :: proc(ast_context: ^AstContext, position_context: ^ } } + ast_context.use_locals = true; + ast_context.use_globals = true; + if assign, ok := position_context.switch_type_stmt.tag.derived.(ast.Assign_Stmt); ok && assign.rhs != nil && len(assign.rhs) == 1 { if union_value, ok := unwrap_union(ast_context, assign.rhs[0]); ok { diff --git a/src/server/hover.odin b/src/server/hover.odin index 109fffe..e22e539 100644 --- a/src/server/hover.odin +++ b/src/server/hover.odin @@ -24,7 +24,7 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> }, }; - ast_context := make_ast_context(document.ast, document.imports, document.package_name); + ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri); position_context, ok := get_document_position_context(document, position, .Hover); @@ -114,9 +114,6 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> return hover, true; } } - } else if symbol, ok := index.lookup(field, selector.pkg); ok { - hover.contents = write_hover_content(&ast_context, symbol); - return hover, true; } } } else if position_context.identifier != nil { diff --git a/src/server/requests.odin b/src/server/requests.odin index 91cd073..0b4f7a1 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -208,7 +208,7 @@ handle_request :: proc (request: json.Value, config: ^common.Config, writer: ^Wr log.error("No root object"); return false; } - + id: RequestId; id_value: json.Value; id_value, ok = root["id"]; @@ -399,7 +399,7 @@ request_initialize :: proc (task: ^common.Task) { enable_document_symbols: bool; enable_hover: bool; enable_format: bool; - + if len(config.workspace_folders) > 0 { //right now just look at the first workspace - TODO(daniel, add multiple workspace support) @@ -432,6 +432,10 @@ request_initialize :: proc (task: ^common.Task) { config.collections[strings.clone(p.name)] = path.join(elems = {uri.path, forward_path}, allocator = context.allocator); } } + + if ok := "" in config.collections; !ok { + config.collections[""] = uri.path; + } } else { log.errorf("Failed to unmarshal %v", ols_config_path); } @@ -461,8 +465,9 @@ request_initialize :: proc (task: ^common.Task) { config.signature_offset_support = initialize_params.capabilities.textDocument.signatureHelp.signatureInformation.parameterInformation.labelOffsetSupport; - completionTriggerCharacters := []string {".", ">", "#"}; - signatureTriggerCharacters := []string {"("}; + completionTriggerCharacters := []string {".", ">", "#", "\"", "/", ":"}; + signatureTriggerCharacters := []string {"("}; + signatureRetriggerCharacters := []string {","}; token_type := type_info_of(SemanticTokenTypes).variant.(runtime.Type_Info_Named).base.variant.(runtime.Type_Info_Enum); token_modifier := type_info_of(SemanticTokenModifiers).variant.(runtime.Type_Info_Named).base.variant.(runtime.Type_Info_Enum); @@ -494,7 +499,8 @@ request_initialize :: proc (task: ^common.Task) { triggerCharacters = completionTriggerCharacters, }, signatureHelpProvider = SignatureHelpOptions { - triggerCharacters = signatureTriggerCharacters + triggerCharacters = signatureTriggerCharacters, + retriggerCharacters = signatureRetriggerCharacters, }, semanticTokensProvider = SemanticTokensOptions { range = false, @@ -526,13 +532,13 @@ request_initialize :: proc (task: ^common.Task) { */ if core, ok := config.collections["core"]; ok { - when ODIN_OS == "windows" { + when ODIN_OS == "windows" { append(&index.indexer.built_in_packages, path.join(strings.to_lower(core, context.temp_allocator), "runtime")); } else { append(&index.indexer.built_in_packages, path.join(core, "runtime")); } } - + log.info("Finished indexing"); } @@ -624,7 +630,7 @@ request_completion :: proc (task: ^common.Task) { } list: CompletionList; - list, ok = get_completion_list(document, completition_params.position); + list, ok = get_completion_list(document, completition_params.position, completition_params.context_); if !ok { handle_error(.InternalError, id, writer); @@ -878,7 +884,7 @@ notification_did_save :: proc (task: ^common.Task) { index.indexer.dynamic_index.collection.symbols[key] = {}; } } - + 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); } diff --git a/src/server/semantic_tokens.odin b/src/server/semantic_tokens.odin index b60d7ef..4feced5 100644 --- a/src/server/semantic_tokens.odin +++ b/src/server/semantic_tokens.odin @@ -96,11 +96,15 @@ get_tokens :: proc(builder: SemanticTokenBuilder) -> SemanticTokens { get_semantic_tokens :: proc(document: ^Document, range: common.Range) -> SemanticTokens { - ast_context := make_ast_context(document.ast, document.imports, document.package_name, context.temp_allocator); + ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri, context.temp_allocator); builder := make_token_builder(); get_globals(document.ast, &ast_context); + if document.ast.pkg_decl != nil { + write_semantic_token(&builder, document.ast.pkg_token, document.ast.src, .Keyword, .None); + } + for decl in document.ast.decls { if range.start.line <= decl.pos.line && decl.end.line <= range.end.line { write_semantic_tokens(decl, &builder, &ast_context); @@ -178,7 +182,7 @@ resolve_and_write_ident :: proc(node: ^ast.Node, builder: ^SemanticTokenBuilder, return; } -write_semantic_tokens :: proc{ +write_semantic_tokens :: proc { write_semantic_tokens_node, write_semantic_tokens_dynamic_array, write_semantic_tokens_array, @@ -204,7 +208,6 @@ write_semantic_tokens_stmt :: proc(node: ^ast.Stmt, builder: ^SemanticTokenBuild ast_context.use_globals = true; ast_context.use_locals = true; builder.selector_member = false; - builder.selector_package = false; write_semantic_tokens_node(node, builder, ast_context); } @@ -354,8 +357,19 @@ write_semantic_tokens_node :: proc(node: ^ast.Node, builder: ^SemanticTokenBuild write_semantic_token_pos(builder, n.tok_pos, "map", ast_context.file.src, .Keyword, .None); write_semantic_tokens(n.key, builder, ast_context); write_semantic_tokens(n.value, builder, ast_context); + case Defer_Stmt: + write_semantic_token_pos(builder, n.pos, "defer", ast_context.file.src, .Keyword, .None); + write_semantic_tokens(n.stmt, builder, ast_context); + case Import_Decl: + write_semantic_token(builder, n.import_tok, ast_context.file.src, .Keyword, .None); + + if n.name.text != "" { + write_semantic_token(builder, n.name, ast_context.file.src, .Namespace, .None); + } + + write_semantic_token(builder, n.relpath, ast_context.file.src, .String, .None); case: - log.infof("unhandled write node %v", n); + log.warnf("unhandled write node %v", n); } } @@ -527,13 +541,18 @@ write_semantic_selector :: proc(selector: ^ast.Selector_Expr, builder: ^Semantic using ast; - if ident, ok := selector.expr.derived.(Ident); ok { + if _, ok := selector.expr.derived.(Selector_Expr); !ok { get_locals_at(builder.current_function, selector.expr, ast_context); - builder.selector_member, builder.selector_package, ast_context.current_package = resolve_and_write_ident(selector.expr, builder, ast_context); //base - if builder.selector_package && selector.field != nil && resolve_ident_is_variable(ast_context, selector.field^) { - builder.selector_member = true; + if symbol, ok := resolve_type_expression(ast_context, selector.expr); ok { + + #partial switch v in symbol.value { + case index.SymbolStructValue: + builder.selector_member = true; + } + } + } else { write_semantic_tokens(selector.expr, builder, ast_context); } diff --git a/src/server/types.odin b/src/server/types.odin index 0efbd39..e2740f7 100644 --- a/src/server/types.odin +++ b/src/server/types.odin @@ -96,6 +96,10 @@ CompletionOptions :: struct { triggerCharacters: []string, } +CompletionContext :: struct { + triggerCharacter: string, +} + SaveOptions :: struct { includeText: bool, } @@ -222,6 +226,7 @@ SignatureHelpParams :: struct { CompletionParams :: struct { textDocument: TextDocumentIdentifier, position: common.Position, + context_: CompletionContext, } CompletionItemKind :: enum { diff --git a/src/server/unmarshal.odin b/src/server/unmarshal.odin index ba37497..1776b07 100644 --- a/src/server/unmarshal.odin +++ b/src/server/unmarshal.odin @@ -32,9 +32,19 @@ unmarshal :: proc(json_value: json.Value, v: any, allocator: mem.Allocator) -> j case Type_Info_Struct: for field, i in variant.names { a := any {rawptr(uintptr(v.data) + uintptr(variant.offsets[i])), variant.types[i].id}; - if ret := unmarshal(j[field], a, allocator); ret != .None { - return ret; + + //TEMP most likely have to rewrite the entire unmarshal using tags instead, because i sometimes have to support names like 'context', which can't be written like that + if field[len(field)-1] == '_' { + if ret := unmarshal(j[field[:len(field)-1]], a, allocator); ret != .None { + return ret; + } + } else { + if ret := unmarshal(j[field], a, allocator); ret != .None { + return ret; + } } + + } case Type_Info_Union: diff --git a/src/testing/testing.odin b/src/testing/testing.odin new file mode 100644 index 0000000..7b47f90 --- /dev/null +++ b/src/testing/testing.odin @@ -0,0 +1,25 @@ +package ols_testing
+
+import "core:testing"
+
+Package_Source :: struct {
+ pkg_name: string,
+ source: string,
+}
+
+Source :: struct {
+ main: string,
+ source_packages: Package_Source,
+}
+
+expect_signature :: proc(t: ^testing.T, src: Source, expect_arg: []string) {
+
+}
+
+expect_completion :: proc(t: ^testing.T, src: Source, completions: []string) {
+
+}
+
+expect_hover :: proc(t: ^testing.T, src: Source, hover_info: string) {
+
+}
\ No newline at end of file |