aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorDaniel Gavin <danielgavin5@hotmail.com>2022-04-09 22:19:08 +0200
committerDaniel Gavin <danielgavin5@hotmail.com>2022-04-09 22:19:08 +0200
commit144d2b3e36d3dca77b8531dc0136ba084b530d54 (patch)
treea06cfd4d76baa4cd64f77379e7ee9920e93371ae /src/server
parent4d0d079b4b79ce5730d8c2ee8694652a3f73049f (diff)
Merge packages
Diffstat (limited to 'src/server')
-rw-r--r--src/server/analysis.odin3397
-rw-r--r--src/server/build.odin215
-rw-r--r--src/server/caches.odin8
-rw-r--r--src/server/clone.odin253
-rw-r--r--src/server/collector.odin602
-rw-r--r--src/server/completion.odin89
-rw-r--r--src/server/definition.odin15
-rw-r--r--src/server/document_links.odin4
-rw-r--r--src/server/document_symbols.odin5
-rw-r--r--src/server/hover.odin16
-rw-r--r--src/server/indexer.odin109
-rw-r--r--src/server/inlay_hints.odin8
-rw-r--r--src/server/lens.odin5
-rw-r--r--src/server/memory_index.odin65
-rw-r--r--src/server/references.odin41
-rw-r--r--src/server/rename.odin3
-rw-r--r--src/server/requests.odin27
-rw-r--r--src/server/semantic_tokens.odin61
-rw-r--r--src/server/signature.odin24
-rw-r--r--src/server/symbol.odin197
20 files changed, 4986 insertions, 158 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin
new file mode 100644
index 0000000..3ef9733
--- /dev/null
+++ b/src/server/analysis.odin
@@ -0,0 +1,3397 @@
+package server
+
+import "core:odin/parser"
+import "core:odin/ast"
+import "core:odin/tokenizer"
+import "core:fmt"
+import "core:log"
+import "core:strings"
+import path "core:path/slashpath"
+import "core:mem"
+import "core:strconv"
+import "core:path/filepath"
+import "core:sort"
+import "core:slice"
+import "core:unicode/utf8"
+import "core:reflect"
+
+import "shared:common"
+
+DocumentPositionContextHint :: enum {
+ Completion,
+ SignatureHelp,
+ Definition,
+ Hover,
+}
+
+DocumentPositionContext :: struct {
+ file: ast.File,
+ position: common.AbsolutePosition,
+ line: int,
+ function: ^ast.Proc_Lit, //used to help with type resolving in function scope
+ 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
+ returns: ^ast.Return_Stmt, //used for completion
+ comp_lit: ^ast.Comp_Lit, //used for completion
+ parent_comp_lit: ^ast.Comp_Lit, //used for completion
+ field_value: ^ast.Field_Value,
+ 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
+ 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
+ abort_completion: bool,
+ hint: DocumentPositionContextHint,
+ global_lhs_stmt: bool,
+ import_stmt: ^ast.Import_Decl,
+ call_commas: []int,
+}
+
+DocumentLocal :: struct {
+ expr: ^ast.Expr,
+ offset: int,
+ id: int, //Id that can used to connect the local to something, i.e. for stmt begin offset
+}
+
+AstContext :: struct {
+ locals: map[int]map[string][dynamic]DocumentLocal, //locals all the way to the document position
+ globals: map[string]common.GlobalExpr,
+ 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: []common.Package, //imports for the current document
+ current_package: string,
+ document_package: string,
+ use_globals: bool,
+ use_locals: bool,
+ local_id: int,
+ 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,
+ recursion_counter: int, //Sometimes the ast is so malformed that it causes infinite recursion.
+}
+
+make_ast_context :: proc(file: ast.File, imports: []common.Package, package_name: string, uri: string, allocator := context.temp_allocator) -> AstContext {
+ ast_context := AstContext {
+ locals = make(map[int]map[string][dynamic]DocumentLocal, 0, allocator),
+ globals = make(map[string]common.GlobalExpr, 0, allocator),
+ variables = make(map[string]bool, 0, allocator),
+ usings = make([dynamic]string, allocator),
+ parameters = make(map[string]bool, 0, allocator),
+ in_package = make(map[string]string, 0, allocator),
+ file = file,
+ imports = imports,
+ use_locals = true,
+ use_globals = true,
+ document_package = package_name,
+ current_package = package_name,
+ uri = uri,
+ allocator = allocator,
+ }
+
+ add_local_group(&ast_context, 0)
+
+ when ODIN_OS == .Windows {
+ ast_context.uri = strings.to_lower(ast_context.uri, allocator)
+ }
+
+ return ast_context
+}
+
+tokenizer_error_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) {
+}
+
+/*
+ Walk through the type expression while both the call expression and specialization type are the same
+*/
+
+resolve_poly_spec :: proc {
+ resolve_poly_spec_node,
+ resolve_poly_spec_array,
+ resolve_poly_spec_dynamic_array,
+}
+
+resolve_poly_spec_array :: proc(ast_context: ^AstContext, call_array: $A/[]^$T, spec_array: $D/[]^$K, poly_map: ^map[string]^ast.Expr) {
+ if len(call_array) != len(spec_array) {
+ return
+ }
+
+ for elem, i in call_array {
+ resolve_poly_spec(ast_context, elem, spec_array[i], poly_map)
+ }
+}
+
+resolve_poly_spec_dynamic_array :: proc(ast_context: ^AstContext, call_array: $A/[dynamic]^$T, spec_array: $D/[dynamic]^$K, poly_map: ^map[string]^ast.Expr) {
+ if len(call_array) != len(spec_array) {
+ return
+ }
+
+ for elem, i in call_array {
+ resolve_poly_spec(ast_context, elem, spec_array[i], poly_map)
+ }
+}
+
+get_poly_node_to_expr :: proc(node: ^ast.Node) -> ^ast.Expr {
+ using ast
+
+ #partial switch v in node.derived {
+ case ^Ident:
+ return cast(^Expr)node
+ case:
+ log.warnf("Unhandled poly to node kind %v", v)
+ }
+
+ return nil
+}
+
+resolve_poly_spec_node :: proc(ast_context: ^AstContext, call_node: ^ast.Node, spec_node: ^ast.Node, poly_map: ^map[string]^ast.Expr) {
+ using ast
+
+ if call_node == nil || spec_node == nil {
+ return
+ }
+
+ #partial switch m in spec_node.derived {
+ case ^Bad_Expr:
+ case ^Ident:
+ case ^Implicit:
+ case ^Undef:
+ case ^Basic_Lit:
+ case ^Poly_Type:
+ if expr := get_poly_node_to_expr(call_node); expr != nil {
+ poly_map[m.type.name] = expr
+ }
+ case ^Ellipsis:
+ if n, ok := call_node.derived.(^Ellipsis); ok {
+ resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
+ }
+ case ^Tag_Expr:
+ if n, ok := call_node.derived.(^Tag_Expr); ok {
+ resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
+ }
+ case ^Unary_Expr:
+ if n, ok := call_node.derived.(^Unary_Expr); ok {
+ resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
+ }
+ case ^Binary_Expr:
+ if n, ok := call_node.derived.(^Binary_Expr); ok {
+ resolve_poly_spec(ast_context, n.left, m.left, poly_map)
+ resolve_poly_spec(ast_context, n.right, m.right, poly_map)
+ }
+ case ^Paren_Expr:
+ if n, ok := call_node.derived.(^Paren_Expr); ok {
+ resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
+ }
+ case ^Selector_Expr:
+ if n, ok := call_node.derived.(^Selector_Expr); ok {
+ resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
+ resolve_poly_spec(ast_context, n.field, m.field, poly_map)
+ }
+ case ^Slice_Expr:
+ if n, ok := call_node.derived.(^Slice_Expr); ok {
+ resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
+ resolve_poly_spec(ast_context, n.low, m.low, poly_map)
+ resolve_poly_spec(ast_context, n.high, m.high, poly_map)
+ }
+ case ^Distinct_Type:
+ if n, ok := call_node.derived.(^Distinct_Type); ok {
+ resolve_poly_spec(ast_context, n.type, m.type, poly_map)
+ }
+ case ^Proc_Type:
+ if n, ok := call_node.derived.(^Proc_Type); ok {
+ resolve_poly_spec(ast_context, n.params, m.params, poly_map)
+ resolve_poly_spec(ast_context, n.results, m.results, poly_map)
+ }
+ case ^Pointer_Type:
+ if n, ok := call_node.derived.(^Pointer_Type); ok {
+ resolve_poly_spec(ast_context, n.elem, m.elem, poly_map)
+ }
+ case ^Array_Type:
+ if n, ok := call_node.derived.(^Array_Type); ok {
+ resolve_poly_spec(ast_context, n.len, m.len, poly_map)
+ resolve_poly_spec(ast_context, n.elem, m.elem, poly_map)
+ }
+ case ^Dynamic_Array_Type:
+ if n, ok := call_node.derived.(^Dynamic_Array_Type); ok {
+ resolve_poly_spec(ast_context, n.elem, m.elem, poly_map)
+ }
+ case ^Struct_Type:
+ if n, ok := call_node.derived.(^Struct_Type); ok {
+ resolve_poly_spec(ast_context, n.poly_params, m.poly_params, poly_map)
+ resolve_poly_spec(ast_context, n.align, m.align, poly_map)
+ resolve_poly_spec(ast_context, n.fields, m.fields, poly_map)
+ }
+ case ^Field:
+ if n, ok := call_node.derived.(^Field); ok {
+ resolve_poly_spec(ast_context, n.names, m.names, poly_map)
+ resolve_poly_spec(ast_context, n.type, m.type, poly_map)
+ resolve_poly_spec(ast_context, n.default_value, m.default_value, poly_map)
+ }
+ case ^Field_List:
+ if n, ok := call_node.derived.(^Field_List); ok {
+ resolve_poly_spec(ast_context, n.list, m.list, poly_map)
+ }
+ case ^Field_Value:
+ if n, ok := call_node.derived.(^Field_Value); ok {
+ resolve_poly_spec(ast_context, n.field, m.field, poly_map)
+ resolve_poly_spec(ast_context, n.value, m.value, poly_map)
+ }
+ case ^Union_Type:
+ if n, ok := call_node.derived.(^Union_Type); ok {
+ resolve_poly_spec(ast_context, n.poly_params, m.poly_params, poly_map)
+ resolve_poly_spec(ast_context, n.align, m.align, poly_map)
+ resolve_poly_spec(ast_context, n.variants, m.variants, poly_map)
+ }
+ case ^Enum_Type:
+ if n, ok := call_node.derived.(^Enum_Type); ok {
+ resolve_poly_spec(ast_context, n.base_type, m.base_type, poly_map)
+ resolve_poly_spec(ast_context, n.fields, m.fields, poly_map)
+ }
+ case ^Bit_Set_Type:
+ if n, ok := call_node.derived.(^Bit_Set_Type); ok {
+ resolve_poly_spec(ast_context, n.elem, m.elem, poly_map)
+ resolve_poly_spec(ast_context, n.underlying, m.underlying, poly_map)
+ }
+ case ^Map_Type:
+ if n, ok := call_node.derived.(^Map_Type); ok {
+ resolve_poly_spec(ast_context, n.key, m.key, poly_map)
+ resolve_poly_spec(ast_context, n.value, m.value, poly_map)
+ }
+ case ^Call_Expr:
+ if n, ok := call_node.derived.(^Call_Expr); ok {
+ resolve_poly_spec(ast_context, n.expr, m.expr, poly_map)
+ resolve_poly_spec(ast_context, n.args, m.args, poly_map)
+ }
+ case ^Typeid_Type:
+ if n, ok := call_node.derived.(^Typeid_Type); ok {
+ resolve_poly_spec(ast_context, n.specialization, m.specialization, poly_map)
+ }
+ case:
+ log.error("Unhandled poly node kind: %T", m)
+ }
+}
+
+resolve_type_comp_literal :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, current_symbol: Symbol, current_comp_lit: ^ast.Comp_Lit) -> (Symbol, ^ast.Comp_Lit, bool) {
+ if position_context.comp_lit == current_comp_lit {
+ return current_symbol, current_comp_lit, true
+ } else if current_comp_lit == nil {
+ return {}, nil, false
+ }
+
+ element_index := 0
+
+ prev_package := ast_context.current_package
+ ast_context.current_package = current_symbol.pkg
+
+ defer ast_context.current_package = prev_package
+
+ for elem, i in current_comp_lit.elems {
+ if position_in_node(elem, position_context.position) {
+ element_index = i
+ }
+ }
+
+ for elem in current_comp_lit.elems {
+ if !position_in_node(elem, position_context.position) {
+ continue
+ }
+
+ if field_value, ok := elem.derived.(^ast.Field_Value); ok { //named
+ if comp_lit, ok := field_value.value.derived.(^ast.Comp_Lit); ok {
+ if s, ok := current_symbol.value.(SymbolStructValue); ok {
+ for name, i in s.names {
+ if name == field_value.field.derived.(^ast.Ident).name {
+ if symbol, ok := resolve_type_expression(ast_context, s.types[i]); ok {
+ //Stop at bitset, because we don't want to enter a comp_lit of a bitset
+ if _, ok := symbol.value.(SymbolBitSetValue); ok {
+ return current_symbol, current_comp_lit, true
+ }
+ return resolve_type_comp_literal(ast_context, position_context, symbol, cast(^ast.Comp_Lit)field_value.value)
+ }
+ }
+ }
+ }
+ }
+ } else { //indexed
+ if s, ok := current_symbol.value.(SymbolStructValue); ok {
+
+ if len(s.types) <= element_index {
+ return {}, {}, false
+ }
+
+ if symbol, ok := resolve_type_expression(ast_context, s.types[element_index]); ok {
+ //Stop at bitset, because we don't want to enter a comp_lit of a bitset
+ if _, ok := symbol.value.(SymbolBitSetValue); ok {
+ return current_symbol, current_comp_lit, true
+ }
+ return resolve_type_comp_literal(ast_context, position_context, symbol, cast(^ast.Comp_Lit)field_value.value)
+ }
+ }
+ }
+ }
+
+ return current_symbol, current_comp_lit, true
+}
+
+resolve_generic_function :: proc {
+ resolve_generic_function_ast,
+ resolve_generic_function_symbol,
+}
+
+resolve_generic_function_symbol :: proc(ast_context: ^AstContext, params: []^ast.Field, results: []^ast.Field) -> (Symbol, bool) {
+ if params == nil {
+ return {}, false
+ }
+
+ if results == nil {
+ return {}, false
+ }
+
+ if ast_context.call == nil {
+ return {}, false
+ }
+
+ call_expr := ast_context.call
+ poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator)
+ i := 0
+ count_required_params := 0
+
+ for param in params {
+ if param.default_value == nil {
+ count_required_params += 1
+ }
+
+ for name in param.names {
+ if len(call_expr.args) <= i {
+ break
+ }
+
+ if poly, ok := name.derived.(^ast.Poly_Type); ok {
+ poly_map[poly.type.name] = call_expr.args[i]
+ }
+
+ if param.type == nil {
+ continue
+ }
+
+ if type_id, ok := param.type.derived.(^ast.Typeid_Type); ok {
+ if type_id.specialization != nil && !common.node_equal(call_expr.args[i], type_id.specialization) {
+ return {}, false
+ }
+ }
+
+ resolve_poly_spec_node(ast_context, call_expr.args[i], param.type, &poly_map)
+
+ i += 1
+ }
+ }
+
+ if count_required_params > len(call_expr.args) || count_required_params == 0 || len(call_expr.args) == 0 {
+ return {}, false
+ }
+
+ function_name := ""
+ function_range: common.Range
+
+ if ident, ok := call_expr.expr.derived.(^ast.Ident); ok {
+ function_name = ident.name
+ function_range = common.get_token_range(ident, ast_context.file.src)
+ } else if selector, ok := call_expr.expr.derived.(^ast.Selector_Expr); ok {
+ function_name = selector.field.name
+ function_range = common.get_token_range(selector, ast_context.file.src)
+ } else {
+ return {}, false
+ }
+
+ symbol := Symbol {
+ range = function_range,
+ type = .Function,
+ name = function_name,
+ }
+
+ return_types := make([dynamic]^ast.Field, ast_context.allocator)
+ argument_types := make([dynamic]^ast.Field, ast_context.allocator)
+
+ for result in results {
+ if result.type == nil {
+ continue
+ }
+
+ ident, ok := common.unwrap_pointer(result.type)
+
+ if ok {
+ if m, ok := poly_map[ident.name]; ok {
+ field := cast(^ast.Field)clone_node(result, ast_context.allocator, nil)
+ field.type = m
+ append(&return_types, field)
+ } else {
+ append(&return_types, result)
+ }
+ } else {
+ append(&return_types, result)
+ }
+ }
+
+ for param in params {
+ if len(param.names) == 0 {
+ continue
+ }
+
+ //check the name for poly
+ if poly_type, ok := param.names[0].derived.(^ast.Poly_Type); ok && param.type != nil {
+ if m, ok := poly_map[poly_type.type.name]; ok {
+ field := cast(^ast.Field)clone_node(param, ast_context.allocator, nil)
+ field.type = m
+ append(&argument_types, field)
+ }
+ } else {
+ append(&argument_types, param)
+ }
+ }
+
+ symbol.value = SymbolProcedureValue {
+ return_types = return_types[:],
+ arg_types = argument_types[:],
+ }
+
+ return symbol, true
+}
+
+resolve_generic_function_ast :: proc(ast_context: ^AstContext, proc_lit: ast.Proc_Lit) -> (Symbol, bool) {
+
+ using ast
+
+ if proc_lit.type.params == nil {
+ return Symbol {}, false
+ }
+
+ if proc_lit.type.results == nil {
+ return Symbol {}, false
+ }
+
+ if ast_context.call == nil {
+ return Symbol {}, false
+ }
+
+ return resolve_generic_function_symbol(ast_context, proc_lit.type.params.list, proc_lit.type.results.list)
+}
+
+is_symbol_same_typed :: proc(ast_context: ^AstContext, a, b: Symbol, flags: ast.Field_Flags = {}) -> bool {
+ //relying on the fact that a is the call argument to avoid checking both sides for untyped.
+ if untyped, ok := a.value.(SymbolUntypedValue); ok {
+ if basic, ok := b.value.(SymbolBasicValue); ok {
+ switch untyped.type {
+ case .Integer:
+ switch basic.ident.name {
+ case "int", "uint", "u32", "i32", "u8", "i8", "u64", "u16", "i16": return true
+ case: return false
+ }
+ case .Bool:
+ switch basic.ident.name {
+ case "bool", "b32", "b64": return true
+ case: return false
+ }
+ case .String:
+ switch basic.ident.name {
+ case "string", "cstring": return true
+ case: return false
+ }
+ case .Float:
+ switch basic.ident.name {
+ case "f32", "f64": return true
+ case: return false
+ }
+ }
+ }
+ }
+
+ a_id := reflect.union_variant_typeid(a.value)
+ b_id := reflect.union_variant_typeid(b.value)
+
+ if a_id != b_id {
+ return false
+ }
+
+ if a.pointers != b.pointers {
+ return false
+ }
+
+ if .Distinct in a.flags != .Distinct in b.flags {
+ return false
+ }
+
+ if .Distinct in a.flags == .Distinct in b.flags &&
+ .Distinct in a.flags &&
+ a.name == b.name &&
+ a.pkg == b.pkg {
+ return true
+ }
+
+ #partial switch b_value in b.value {
+ case SymbolBasicValue:
+ if .Auto_Cast in flags {
+ return true
+ } else if .Any_Int in flags {
+ //Temporary - make a function that finds the base type of basic values
+ //This code only works with non distinct ints
+ switch a.name {
+ case "int", "uint", "u32", "i32", "u8", "i8", "u64", "u16", "i16": return true
+ }
+ }
+ }
+
+ #partial switch a_value in a.value {
+ case SymbolBasicValue:
+ return a.name == b.name && a.pkg == b.pkg
+ case SymbolStructValue, SymbolEnumValue, SymbolUnionValue, SymbolBitSetValue:
+ return a.name == b.name && a.pkg == b.pkg
+ case SymbolSliceValue:
+ b_value := b.value.(SymbolSliceValue)
+
+ a_symbol: Symbol
+ b_symbol: Symbol
+ ok: bool
+
+ a_symbol, ok = resolve_type_expression(ast_context, a_value.expr)
+
+ if !ok {
+ return false
+ }
+
+ b_symbol, ok = resolve_type_expression(ast_context, b_value.expr)
+
+ if !ok {
+ return false
+ }
+
+ return is_symbol_same_typed(ast_context, a_symbol, b_symbol)
+ case SymbolFixedArrayValue:
+ b_value := b.value.(SymbolFixedArrayValue)
+
+ a_symbol: Symbol
+ b_symbol: Symbol
+ ok: bool
+
+ a_symbol, ok = resolve_type_expression(ast_context, a_value.expr)
+
+ if !ok {
+ return false
+ }
+
+ b_symbol, ok = resolve_type_expression(ast_context, b_value.expr)
+
+ if !ok {
+ return false
+ }
+
+ return is_symbol_same_typed(ast_context, a_symbol, b_symbol)
+ case SymbolDynamicArrayValue:
+ b_value := b.value.(SymbolDynamicArrayValue)
+
+ a_symbol: Symbol
+ b_symbol: Symbol
+ ok: bool
+
+ a_symbol, ok = resolve_type_expression(ast_context, a_value.expr)
+
+ if !ok {
+ return false
+ }
+
+ b_symbol, ok = resolve_type_expression(ast_context, b_value.expr)
+
+ if !ok {
+ return false
+ }
+
+ return is_symbol_same_typed(ast_context, a_symbol, b_symbol)
+ case SymbolMapValue:
+ b_value := b.value.(SymbolMapValue)
+
+ a_key_symbol: Symbol
+ b_key_symbol: Symbol
+ a_value_symbol: Symbol
+ b_value_symbol: Symbol
+ ok: bool
+
+ a_key_symbol, ok = resolve_type_expression(ast_context, a_value.key)
+
+ if !ok {
+ return false
+ }
+
+ b_key_symbol, ok = resolve_type_expression(ast_context, b_value.key)
+
+ if !ok {
+ return false
+ }
+
+ a_value_symbol, ok = resolve_type_expression(ast_context, a_value.value)
+
+ if !ok {
+ return false
+ }
+
+ b_value_symbol, ok = resolve_type_expression(ast_context, b_value.value)
+
+ if !ok {
+ return false
+ }
+
+ return is_symbol_same_typed(ast_context, a_key_symbol, b_key_symbol) && is_symbol_same_typed(ast_context, a_value_symbol, b_value_symbol)
+ }
+
+ return false
+}
+
+get_field_list_name_index :: proc(name: string, field_list: []^ast.Field) -> (int, bool) {
+ for field, i in field_list {
+ for field_name in field.names {
+ if ident, ok := field_name.derived.(^ast.Ident); ok {
+ if name == ident.name {
+ return i, true
+ }
+ }
+ }
+ }
+
+ return 0, false
+}
+
+/*
+ Figure out which function the call expression is using out of the list from proc group
+*/
+resolve_function_overload :: proc(ast_context: ^AstContext, group: ast.Proc_Group) -> (Symbol, bool) {
+ using ast
+
+ call_expr := ast_context.call
+
+ candidates := make([dynamic]Symbol, context.temp_allocator)
+
+ for arg_expr in group.args {
+
+ next_fn: if f, ok := resolve_type_expression(ast_context, arg_expr); ok {
+
+ if call_expr == nil || len(call_expr.args) == 0 {
+ append(&candidates, f)
+ break next_fn
+ }
+
+ if procedure, ok := f.value.(SymbolProcedureValue); ok {
+
+ count_required_params := 0
+
+ for arg in procedure.arg_types {
+ if arg.default_value == nil {
+ count_required_params += 1
+ }
+ }
+
+ if len(procedure.arg_types) < len(call_expr.args) {
+ continue
+ }
+
+ for arg, i in call_expr.args {
+
+ ast_context.use_locals = true
+
+ call_symbol: Symbol
+ arg_symbol: Symbol
+ ok: bool
+ i := i
+
+ if _, ok = arg.derived.(^ast.Bad_Expr); ok {
+ continue
+ }
+
+ //named parameter
+ if field, is_field := arg.derived.(^ast.Field_Value); is_field {
+ call_symbol, ok = resolve_type_expression(ast_context, field.value)
+ if !ok {
+ break next_fn
+ }
+
+ if ident, is_ident := field.field.derived.(^ast.Ident); is_ident {
+ i, ok = get_field_list_name_index(field.field.derived.(^ast.Ident).name, procedure.arg_types)
+ } else {
+ break next_fn
+ }
+ } else {
+ call_symbol, ok = resolve_type_expression(ast_context, arg)
+ }
+
+ if !ok {
+ break next_fn
+ }
+
+ if p, ok := call_symbol.value.(SymbolProcedureValue); ok {
+ if len(p.return_types) != 1 {
+ break next_fn
+ }
+ if s, ok := resolve_type_expression(ast_context, p.return_types[0].type); ok {
+ call_symbol = s
+ }
+ }
+
+ if procedure.arg_types[i].type != nil {
+ arg_symbol, ok = resolve_type_expression(ast_context, procedure.arg_types[i].type)
+ } else {
+ arg_symbol, ok = resolve_type_expression(ast_context, procedure.arg_types[i].default_value)
+ }
+
+ if !ok {
+ break next_fn
+ }
+
+ if !is_symbol_same_typed(ast_context, call_symbol, arg_symbol, procedure.arg_types[i].flags) {
+ break next_fn
+ }
+ }
+
+ append(&candidates, f)
+ }
+ }
+ }
+
+ if len(candidates) > 1 {
+ return Symbol {
+ type = candidates[0].type,
+ name = candidates[0].name,
+ pkg = candidates[0].pkg,
+ value = SymbolAggregateValue {
+ symbols = candidates[:],
+ },
+ }, true
+ } else if len(candidates) == 1 {
+ return candidates[0], true
+ }
+
+ return Symbol {}, false
+}
+
+resolve_basic_lit :: proc(ast_context: ^AstContext, basic_lit: ast.Basic_Lit) -> (Symbol, bool) {
+ symbol := Symbol {
+ type = .Constant,
+ }
+
+ value: SymbolUntypedValue
+
+ if v, ok := strconv.parse_int(basic_lit.tok.text); ok {
+ value.type = .Integer
+ } else if v, ok := strconv.parse_bool(basic_lit.tok.text); ok {
+ value.type = .Bool
+ } else if v, ok := strconv.parse_f64(basic_lit.tok.text); ok {
+ value.type = .Float
+ } else {
+ value.type = .String
+ }
+
+ symbol.pkg = ast_context.current_package
+ symbol.value = value
+
+ return symbol, true
+}
+
+resolve_basic_directive :: proc(ast_context: ^AstContext, directive: ast.Basic_Directive, a := #caller_location) -> (Symbol, bool) {
+ switch directive.name {
+ case "caller_location":
+ ident := new_type(ast.Ident, directive.pos, directive.end, ast_context.allocator)
+ ident.name = "Source_Code_Location"
+ ast_context.current_package = ast_context.document_package
+ return resolve_type_identifier(ast_context, ident^)
+ }
+
+ return {}, false
+}
+
+
+resolve_type_expression :: proc(ast_context: ^AstContext, node: ^ast.Expr) -> (Symbol, bool) {
+ if node == nil {
+ return {}, false
+ }
+
+ saved_package := ast_context.current_package
+
+ defer {
+ ast_context.current_package = saved_package
+ }
+
+ if ast_context.recursion_counter > 15 {
+ log.error("Recursion passed 15 attempts - giving up")
+ return {}, false
+ }
+
+ ast_context.recursion_counter += 1
+
+ defer {
+ ast_context.recursion_counter -= 1
+ }
+
+ using ast
+
+ #partial switch v in node.derived {
+ case ^Union_Type:
+ return make_symbol_union_from_ast(ast_context, v^, ast_context.field_name, true), true
+ case ^Enum_Type:
+ return make_symbol_enum_from_ast(ast_context, v^, ast_context.field_name, true), true
+ case ^Struct_Type:
+ return make_symbol_struct_from_ast(ast_context, v^, ast_context.field_name, true), true
+ case ^Bit_Set_Type:
+ return make_symbol_bitset_from_ast(ast_context, v^, ast_context.field_name, true), true
+ case ^Array_Type:
+ return make_symbol_array_from_ast(ast_context, v^, ast_context.field_name), true
+ case ^Dynamic_Array_Type:
+ return make_symbol_dynamic_array_from_ast(ast_context, v^, ast_context.field_name), true
+ case ^Map_Type:
+ return make_symbol_map_from_ast(ast_context, v^, ast_context.field_name), true
+ case ^Proc_Type:
+ return make_symbol_procedure_from_ast(ast_context, node, v^, ast_context.field_name), true
+ case ^Basic_Directive:
+ return resolve_basic_directive(ast_context, v^)
+ case ^Binary_Expr:
+ return resolve_first_symbol_from_binary_expression(ast_context, v)
+ case ^Ident:
+ return resolve_type_identifier(ast_context, v^)
+ case ^Basic_Lit:
+ return resolve_basic_lit(ast_context, v^)
+ case ^Type_Cast:
+ return resolve_type_expression(ast_context, v.type)
+ case ^Auto_Cast:
+ return resolve_type_expression(ast_context, v.expr)
+ case ^Comp_Lit:
+ return resolve_type_expression(ast_context, v.type)
+ case ^Unary_Expr:
+ if v.op.kind == .And {
+ symbol, ok := resolve_type_expression(ast_context, v.expr)
+ symbol.pointers += 1
+ return symbol, ok
+ } else {
+ return resolve_type_expression(ast_context, v.expr)
+ }
+ case ^Deref_Expr:
+ symbol, ok := resolve_type_expression(ast_context, v.expr)
+ symbol.pointers -= 1
+ return symbol, ok
+ case ^Paren_Expr:
+ return resolve_type_expression(ast_context, v.expr)
+ case ^Slice_Expr:
+ return resolve_type_expression(ast_context, v.expr)
+ case ^Tag_Expr:
+ return resolve_type_expression(ast_context, v.expr)
+ case ^Helper_Type:
+ return resolve_type_expression(ast_context, v.type)
+ case ^Ellipsis:
+ return resolve_type_expression(ast_context, v.expr)
+ case ^Implicit:
+ ident := new_type(Ident, v.node.pos, v.node.end, context.temp_allocator)
+ ident.name = v.tok.text
+ return resolve_type_identifier(ast_context, ident^)
+ case ^Type_Assertion:
+ if unary, ok := v.type.derived.(^ast.Unary_Expr); ok {
+ if unary.op.kind == .Question {
+ if symbol, ok := resolve_type_expression(ast_context, v.expr); ok {
+ if union_value, ok := symbol.value.(SymbolUnionValue); ok {
+ if len(union_value.types) != 1 {
+ return {}, false
+ }
+ return resolve_type_expression(ast_context, union_value.types[0])
+ }
+ }
+ }
+ } else {
+ return resolve_type_expression(ast_context, v.type)
+ }
+ case ^Proc_Lit:
+ if v.type.results != nil {
+ if len(v.type.results.list) == 1 {
+ return resolve_type_expression(ast_context, v.type.results.list[0].type)
+ }
+ }
+ case ^Pointer_Type:
+ symbol, ok := resolve_type_expression(ast_context, v.elem)
+ symbol.pointers += 1
+ return symbol, ok
+ case ^Multi_Pointer_Type:
+ symbol, ok := resolve_type_expression(ast_context, v.elem)
+ symbol.pointers += 1
+ return symbol, ok
+ case ^Index_Expr:
+ indexed, ok := resolve_type_expression(ast_context, v.expr)
+
+ if !ok {
+ return {}, false
+ }
+
+ symbol: Symbol
+
+ #partial switch v2 in indexed.value {
+ case SymbolDynamicArrayValue:
+ symbol, ok = resolve_type_expression(ast_context, v2.expr)
+ case SymbolSliceValue:
+ symbol, ok = resolve_type_expression(ast_context, v2.expr)
+ case SymbolFixedArrayValue:
+ symbol, ok = resolve_type_expression(ast_context, v2.expr)
+ case SymbolMapValue:
+ symbol, ok = resolve_type_expression(ast_context, v2.value)
+ }
+
+ symbol.type = indexed.type
+
+ return symbol, ok
+ case ^Call_Expr:
+ ast_context.call = cast(^Call_Expr)node
+ return resolve_type_expression(ast_context, v.expr)
+ case ^Implicit_Selector_Expr:
+ return Symbol {}, false
+ case ^Selector_Call_Expr:
+ return resolve_type_expression(ast_context, v.expr)
+ case ^Selector_Expr:
+ if selector, ok := resolve_type_expression(ast_context, v.expr); ok {
+ ast_context.use_locals = false
+
+ #partial switch s in selector.value {
+ case SymbolFixedArrayValue:
+ components_count := 0
+ for c in v.field.name {
+ if c == 'x' || c == 'y' || c == 'z' || c == 'w' ||
+ c == 'r' || c == 'g' || c == 'b' || c == 'a' {
+ components_count += 1
+ }
+ }
+
+ if components_count == 0 {
+ return {}, false
+ }
+
+ if components_count == 1 {
+ if selector.pkg != "" {
+ ast_context.current_package = selector.pkg
+ } else {
+ ast_context.current_package = ast_context.document_package
+ }
+ symbol, ok := resolve_type_expression(ast_context, s.expr)
+ symbol.type = .Variable
+ return symbol, ok
+ } else {
+ value := SymbolFixedArrayValue {
+ expr = s.expr,
+ len = make_int_basic_value(ast_context, components_count),
+ }
+ selector.value = value
+ selector.type = .Variable
+ return selector, true
+ }
+ case SymbolProcedureValue:
+ if len(s.return_types) == 1 {
+ selector_expr := 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.field = v.field
+ return resolve_type_expression(ast_context, selector_expr)
+ }
+ case SymbolStructValue:
+ if selector.pkg != "" {
+ ast_context.current_package = selector.pkg
+ } else {
+ ast_context.current_package = ast_context.document_package
+ }
+
+ for name, i in s.names {
+ if v.field != nil && name == v.field.name {
+ ast_context.field_name = v.field.name
+ symbol, ok := resolve_type_expression(ast_context, s.types[i])
+ symbol.type = .Variable
+ return symbol, ok
+ }
+ }
+ case SymbolPackageValue:
+ ast_context.current_package = selector.pkg
+
+ if v.field != nil {
+ return resolve_symbol_return(ast_context, lookup(v.field.name, selector.pkg))
+ } else {
+ return Symbol {}, false
+ }
+ }
+ } else {
+ return Symbol {}, false
+ }
+ case:
+ log.warnf("default node kind, resolve_type_expression: %T", v)
+ if v == nil {
+ return {}, false
+ }
+ }
+
+ return Symbol {}, false
+}
+
+store_local :: proc(ast_context: ^AstContext, expr: ^ast.Expr, offset: int, name: string, id := 0) {
+ local_stack := &ast_context.locals[id][name]
+
+ if local_stack == nil {
+ locals := &ast_context.locals[id]
+ locals[name] = make([dynamic]DocumentLocal, ast_context.allocator)
+ local_stack = &locals[name]
+ }
+
+ append(local_stack, DocumentLocal {expr = expr, offset = offset, id = id})
+}
+
+add_local_group :: proc(ast_context: ^AstContext, id: int) {
+ ast_context.locals[id] = make(map[string][dynamic]DocumentLocal, 100, ast_context.allocator)
+}
+
+clear_local_group :: proc(ast_context: ^AstContext, id: int) {
+ ast_context.locals[id] = {}
+}
+
+get_local :: proc(ast_context: ^AstContext, offset: int, name: string) -> ^ast.Expr {
+ previous := 0
+
+ //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 {
+ previous = 1
+ break
+ }
+ }
+ }
+ }
+
+ for _, locals in &ast_context.locals {
+ if local_stack, ok := locals[name]; ok {
+ for i := len(local_stack) - 1; i >= 0; i -= 1 {
+ if local_stack[i].offset <= offset {
+ if i - previous < 0 {
+ return nil
+ } else {
+ return local_stack[i - previous].expr
+ }
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+get_local_offset :: proc(ast_context: ^AstContext, offset: int, name: string) -> int {
+ for _, locals in &ast_context.locals {
+ if local_stack, ok := locals[name]; ok {
+ for i := len(local_stack) - 1; i >= 0; i -= 1 {
+ if local_stack[i].offset <= offset {
+ if i < 0 {
+ return -1
+ } else {
+ return local_stack[i].offset
+ }
+ }
+ }
+ }
+ }
+
+ return -1
+}
+
+resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (Symbol, bool) {
+ using ast
+
+ if ast_context.recursion_counter > 15 {
+ log.error("Recursion passed 15 attempts - giving up")
+ return {}, false
+ }
+
+ saved_package := ast_context.current_package
+
+ defer {
+ ast_context.current_package = saved_package
+ }
+
+ ast_context.recursion_counter += 1
+
+ defer {
+ ast_context.recursion_counter -= 1
+ }
+
+ if pkg, ok := ast_context.in_package[node.name]; ok {
+ ast_context.current_package = pkg
+ }
+
+ if _, ok := ast_context.parameters[node.name]; ok {
+ for imp in ast_context.imports {
+ if strings.compare(imp.base, node.name) == 0 {
+ symbol := Symbol {
+ type = .Package,
+ pkg = imp.name,
+ value = SymbolPackageValue {},
+ }
+
+ return symbol, true
+ }
+ }
+ }
+
+ //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 {
+ is_distinct := false
+
+ if dist, ok := local.derived.(^ast.Distinct_Type); ok {
+ if dist.type != nil {
+ local = dist.type
+ is_distinct = true
+ }
+ }
+
+ return_symbol: Symbol
+ ok: bool
+
+ #partial switch v in local.derived {
+ case ^Ident:
+ return_symbol, ok = resolve_type_identifier(ast_context, v^)
+ case ^Union_Type:
+ return_symbol, ok = make_symbol_union_from_ast(ast_context, v^, node.name), true
+ return_symbol.name = node.name
+ case ^Enum_Type:
+ return_symbol, ok = make_symbol_enum_from_ast(ast_context, v^, node.name), true
+ return_symbol.name = node.name
+ case ^Struct_Type:
+ return_symbol, ok = make_symbol_struct_from_ast(ast_context, v^, node.name), true
+ return_symbol.name = node.name
+ case ^Bit_Set_Type:
+ return_symbol, ok = make_symbol_bitset_from_ast(ast_context, v^, node.name), true
+ return_symbol.name = node.name
+ case ^Proc_Lit:
+ if !v.type.generic {
+ return_symbol, ok = make_symbol_procedure_from_ast(ast_context, local, v.type^, node.name), true
+ } else {
+ if return_symbol, ok = resolve_generic_function(ast_context, v^); !ok {
+ return_symbol, ok = make_symbol_procedure_from_ast(ast_context, local, v.type^, node.name), true
+ }
+ }
+ case ^Proc_Group:
+ return_symbol, ok = resolve_function_overload(ast_context, v^)
+ case ^Array_Type:
+ return_symbol, ok = make_symbol_array_from_ast(ast_context, v^, node.name), true
+ case ^Dynamic_Array_Type:
+ return_symbol, ok = make_symbol_dynamic_array_from_ast(ast_context, v^, node.name), true
+ case ^Map_Type:
+ return_symbol, ok = make_symbol_map_from_ast(ast_context, v^, node.name), true
+ case ^Basic_Lit:
+ return_symbol, ok = resolve_basic_lit(ast_context, v^)
+ return_symbol.name = node.name
+ return_symbol.type = ast_context.variables[node.name] ? .Variable : .Constant
+ case:
+ return_symbol, ok = resolve_type_expression(ast_context, local)
+ }
+
+ if is_distinct {
+ return_symbol.name = node.name
+ return_symbol.flags |= {.Distinct}
+ }
+
+ if is_variable, ok := ast_context.variables[node.name]; ok && is_variable {
+ //return_symbol.name = node.name
+ return_symbol.type = .Variable
+ }
+
+ return return_symbol, ok
+
+ } else if global, ok := ast_context.globals[node.name]; ast_context.use_globals && ok {
+ is_distinct := false
+
+ if dist, ok := global.expr.derived.(^ast.Distinct_Type); ok {
+ if dist.type != nil {
+ global.expr = dist.type
+ is_distinct = true
+ }
+ }
+
+ return_symbol: Symbol
+ ok: bool
+
+ #partial switch v in global.expr.derived {
+ case ^Ident:
+ return_symbol, ok = resolve_type_identifier(ast_context, v^)
+ case ^Struct_Type:
+ return_symbol, ok = make_symbol_struct_from_ast(ast_context, v^, node.name), true
+ return_symbol.name = node.name
+ case ^Bit_Set_Type:
+ return_symbol, ok = make_symbol_bitset_from_ast(ast_context, v^, node.name), true
+ return_symbol.name = node.name
+ case ^Union_Type:
+ return_symbol, ok = make_symbol_union_from_ast(ast_context, v^, node.name), true
+ return_symbol.name = node.name
+ case ^Enum_Type:
+ return_symbol, ok = make_symbol_enum_from_ast(ast_context, v^, node.name), true
+ return_symbol.name = node.name
+ case ^Proc_Lit:
+ if !v.type.generic {
+ return_symbol, ok = make_symbol_procedure_from_ast(ast_context, global.expr, v.type^, node.name), true
+ } else {
+ if return_symbol, ok = resolve_generic_function(ast_context, v^); !ok {
+ return_symbol, ok = make_symbol_procedure_from_ast(ast_context, global.expr, v.type^, node.name), true
+ }
+ }
+ case ^Proc_Group:
+ return_symbol, ok = resolve_function_overload(ast_context, v^)
+ case ^Array_Type:
+ return_symbol, ok = make_symbol_array_from_ast(ast_context, v^, node.name), true
+ case ^Dynamic_Array_Type:
+ return_symbol, ok = make_symbol_dynamic_array_from_ast(ast_context, v^, node.name), true
+ case ^Map_Type:
+ return_symbol, ok = make_symbol_map_from_ast(ast_context, v^, node.name), true
+ case ^Basic_Lit:
+ return_symbol, ok = resolve_basic_lit(ast_context, v^)
+ return_symbol.name = node.name
+ return_symbol.type = global.mutable ? .Variable : .Constant
+ case:
+ return_symbol, ok = resolve_type_expression(ast_context, global.expr)
+ }
+
+ if is_distinct {
+ return_symbol.name = node.name
+ return_symbol.flags |= {.Distinct}
+ }
+
+ if is_variable, ok := ast_context.variables[node.name]; ok && is_variable {
+ return_symbol.type = .Variable
+ }
+
+ return_symbol.doc = common.get_doc(global.docs, ast_context.allocator)
+
+ return return_symbol, ok
+ } else if node.name == "context" {
+ for built in indexer.builtin_packages {
+ if symbol, ok := lookup("Context", built); ok {
+ symbol.type = .Variable
+ return symbol, ok
+ }
+ }
+ } else if v, ok := common.keyword_map[node.name]; ok {
+ //keywords
+ ident := new_type(Ident, node.pos, node.end, ast_context.allocator)
+ ident.name = node.name
+
+ symbol: Symbol
+
+ switch ident.name {
+ case "true", "false":
+ symbol = Symbol {
+ type = .Keyword,
+ signature = node.name,
+ pkg = ast_context.current_package,
+ value = SymbolUntypedValue {
+ type = .Bool,
+ },
+ }
+ case:
+ symbol = Symbol {
+ type = .Keyword,
+ signature = node.name,
+ name = ident.name,
+ pkg = ast_context.current_package,
+ value = SymbolBasicValue {
+ ident = ident,
+ },
+ }
+ }
+
+ return symbol, true
+ } else {
+ //right now we replace the package ident with the absolute directory name, so it should have '/' which is not a valid ident character
+ if strings.contains(node.name, "/") {
+ symbol := Symbol {
+ type = .Package,
+ pkg = node.name,
+ value = SymbolPackageValue {},
+ }
+
+ return symbol, true
+ } else {
+ //part of the ast so we check the imports of the document
+ for imp in ast_context.imports {
+ if strings.compare(imp.base, node.name) == 0 {
+ symbol := Symbol {
+ type = .Package,
+ pkg = imp.name,
+ value = SymbolPackageValue {},
+ }
+
+ return symbol, true
+ }
+ }
+ }
+
+ //last option is to check the index
+ if symbol, ok := lookup(node.name, ast_context.current_package); ok {
+ return resolve_symbol_return(ast_context, symbol)
+ }
+
+ //If we are resolving a symbol that is in the document package, then we'll check the builtin packages.
+ if ast_context.current_package == ast_context.document_package {
+ if symbol, ok := lookup(node.name, "$builtin"); ok {
+ return resolve_symbol_return(ast_context, symbol)
+ }
+ for built in indexer.builtin_packages {
+ if symbol, ok := lookup(node.name, built); ok {
+ return resolve_symbol_return(ast_context, symbol)
+ }
+ }
+ }
+
+ for u in ast_context.usings {
+ //TODO(Daniel, make into a map, not really required for performance but looks nicer)
+ for imp in ast_context.imports {
+ if strings.compare(imp.base, u) == 0 {
+ if symbol, ok := lookup(node.name, imp.name); ok {
+ return resolve_symbol_return(ast_context, symbol)
+ }
+ }
+ }
+ }
+ }
+
+ return Symbol {}, false
+}
+
+resolve_ident_is_package :: proc(ast_context: ^AstContext, node: ast.Ident) -> bool {
+ if strings.contains(node.name, "/") {
+ return true
+ } else {
+ for imp in ast_context.imports {
+
+ if imp.base == node.name {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+expand_struct_usings :: proc(ast_context: ^AstContext, symbol: Symbol, value: SymbolStructValue) -> SymbolStructValue {
+ names := slice.to_dynamic(value.names, ast_context.allocator)
+ types := slice.to_dynamic(value.types, ast_context.allocator)
+
+ for k, v in value.usings {
+ ast_context.current_package = symbol.pkg
+
+ field_expr: ^ast.Expr
+
+ for name, i in value.names {
+
+ if name == k && v {
+ field_expr = value.types[i]
+ }
+ }
+
+ if field_expr == nil {
+ continue
+ }
+
+ if s, ok := resolve_type_expression(ast_context, field_expr); ok {
+ if struct_value, ok := s.value.(SymbolStructValue); ok {
+ for name in struct_value.names {
+ append(&names, name)
+ }
+
+ for type in struct_value.types {
+ append(&types, type)
+ }
+ }
+ }
+ }
+
+ return {
+ names = names[:],
+ types = types[:],
+ }
+}
+
+resolve_symbol_return :: proc(ast_context: ^AstContext, symbol: Symbol, ok := true) -> (Symbol, bool) {
+ if !ok {
+ return symbol, ok
+ }
+
+ symbol := symbol
+
+ if symbol.type == .Unresolved {
+ resolve_unresolved_symbol(ast_context, &symbol)
+ }
+
+ #partial switch v in &symbol.value {
+ case SymbolProcedureGroupValue:
+ if symbol, ok := resolve_function_overload(ast_context, v.group.derived.(^ast.Proc_Group)^); ok {
+ return symbol, true
+ } else {
+ return symbol, false
+ }
+ case SymbolProcedureValue:
+ if v.generic {
+ if resolved_symbol, ok := resolve_generic_function(ast_context, v.arg_types, v.return_types); ok {
+ return resolved_symbol, ok
+ } else {
+ return symbol, true
+ }
+ } else {
+ return symbol, true
+ }
+ case SymbolUnionValue:
+ if v.poly != nil {
+ //Todo(daniel): Maybe change the function to return a new symbol instead of referencing it.
+ //resolving the poly union means changing the type, so we do a copy of it.
+ types := make([dynamic]^ast.Expr, ast_context.allocator)
+ append_elems(&types, ..v.types)
+ v.types = types[:]
+ resolve_poly_union(ast_context, v.poly, &symbol)
+ }
+ return symbol, ok
+ case SymbolStructValue:
+ if v.poly != nil {
+ //Todo(daniel): Maybe change the function to return a new symbol instead of referencing it.
+ //resolving the struct union means changing the type, so we do a copy of it.
+ types := make([dynamic]^ast.Expr, ast_context.allocator)
+ append_elems(&types, ..v.types)
+ v.types = types[:]
+ resolve_poly_struct(ast_context, v.poly, &symbol)
+ }
+
+ //expand the types and names from the using - can't be done while indexing without complicating everything(this also saves memory)
+ if len(v.usings) > 0 {
+ expanded := symbol
+ expanded.value = expand_struct_usings(ast_context, symbol, v)
+ return expanded, true
+ } else {
+ return symbol, true
+ }
+ case SymbolGenericValue:
+ ret, ok := resolve_type_expression(ast_context, v.expr)
+ return ret, ok
+ }
+
+ return symbol, true
+}
+
+resolve_unresolved_symbol :: proc(ast_context: ^AstContext, symbol: ^Symbol) {
+ if symbol.type != .Unresolved {
+ return
+ }
+
+ #partial switch v in symbol.value {
+ case SymbolStructValue:
+ symbol.type = .Struct
+ case SymbolPackageValue:
+ symbol.type = .Package
+ case SymbolProcedureValue, SymbolProcedureGroupValue:
+ symbol.type = .Function
+ case SymbolUnionValue:
+ symbol.type = .Enum
+ case SymbolEnumValue:
+ symbol.type = .Enum
+ case SymbolBitSetValue:
+ symbol.type = .Enum
+ case SymbolGenericValue:
+ ast_context.current_package = symbol.pkg
+ if ret, ok := resolve_type_expression(ast_context, v.expr); ok {
+ symbol.type = ret.type
+ symbol.signature = ret.signature
+ }
+ }
+}
+
+resolve_location_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (Symbol, bool) {
+ symbol: Symbol
+
+ if local := get_local(ast_context, node.pos.offset, node.name); local != nil {
+ symbol.range = common.get_token_range(get_local(ast_context, node.pos.offset, node.name), ast_context.file.src)
+ return symbol, true
+ } else if global, ok := ast_context.globals[node.name]; ok {
+ symbol.range = common.get_token_range(global.expr, ast_context.file.src)
+ return symbol, true
+ }
+
+ if symbol, ok := lookup(node.name, ast_context.document_package); ok {
+ return symbol, ok
+ }
+
+ usings := get_using_packages(ast_context)
+
+ for pkg in usings {
+ if symbol, ok := lookup(node.name, pkg); ok {
+ return symbol, ok
+ }
+ }
+
+ return {}, false
+}
+
+resolve_first_symbol_from_binary_expression :: proc(ast_context: ^AstContext, binary: ^ast.Binary_Expr) -> (Symbol, bool) {
+ //Fairly simple function to find the earliest identifier symbol in binary expression.
+
+ if binary.left != nil {
+
+ if ident, ok := binary.left.derived.(^ast.Ident); ok {
+ if s, ok := resolve_type_identifier(ast_context, ident^); ok {
+ return s, ok
+ }
+ } else if _, ok := binary.left.derived.(^ast.Binary_Expr); ok {
+ if s, ok := resolve_first_symbol_from_binary_expression(ast_context, cast(^ast.Binary_Expr)binary.left); ok {
+ return s, ok
+ }
+ }
+ }
+
+ if binary.right != nil {
+ if ident, ok := binary.right.derived.(^ast.Ident); ok {
+ if s, ok := resolve_type_identifier(ast_context, ident^); ok {
+ return s, ok
+ }
+ } else if _, ok := binary.right.derived.(^ast.Binary_Expr); ok {
+ if s, ok := resolve_first_symbol_from_binary_expression(ast_context, cast(^ast.Binary_Expr)binary.right); ok {
+ return s, ok
+ }
+ }
+ }
+
+ return {}, false
+}
+
+find_position_in_call_param :: proc(ast_context: ^AstContext, call: ast.Call_Expr) -> (int, bool) {
+ if call.args == nil {
+ return 0, false
+ }
+
+ for arg, i in call.args {
+ if position_in_node(arg, ast_context.position) {
+ return i, true
+ }
+ }
+
+ return len(call.args) - 1, true
+}
+
+make_pointer_ast :: proc(ast_context: ^AstContext, elem: ^ast.Expr) -> ^ast.Pointer_Type {
+ pointer := new_type(ast.Pointer_Type, elem.pos, elem.end, ast_context.allocator)
+ pointer.elem = elem
+ return pointer
+}
+
+make_bool_ast :: proc(ast_context: ^AstContext) -> ^ast.Ident {
+ ident := new_type(ast.Ident, {}, {}, ast_context.allocator)
+ ident.name = "bool"
+ return ident
+}
+
+make_int_ast :: proc(ast_context: ^AstContext) -> ^ast.Ident {
+ ident := new_type(ast.Ident, {}, {}, ast_context.allocator)
+ ident.name = "int"
+ return ident
+}
+
+make_int_basic_value :: proc(ast_context: ^AstContext, n: int) -> ^ast.Basic_Lit {
+ basic := new_type(ast.Basic_Lit, {}, {}, ast_context.allocator)
+ basic.tok.text = fmt.tprintf("%v", n)
+ return basic
+}
+
+get_package_from_node :: proc(node: ast.Node) -> string {
+ slashed, _ := filepath.to_slash(node.pos.file, context.temp_allocator)
+
+ when ODIN_OS == .Windows {
+ ret := strings.to_lower(path.dir(slashed, context.temp_allocator), context.temp_allocator)
+ } else {
+ ret := path.dir(slashed, context.temp_allocator)
+ }
+
+ return ret
+}
+
+get_using_packages :: proc(ast_context: ^AstContext) -> []string {
+ usings := make([]string, len(ast_context.usings), context.temp_allocator)
+
+ if len(ast_context.usings) == 0 {
+ return usings
+ }
+
+ //probably map instead
+ for u, i in ast_context.usings {
+
+ for imp in ast_context.imports {
+
+ if strings.compare(imp.base, u) == 0 {
+ usings[i] = imp.name
+ }
+ }
+ }
+
+ return usings
+}
+
+make_symbol_procedure_from_ast :: proc(ast_context: ^AstContext, n: ^ast.Node, v: ast.Proc_Type, name: string) -> Symbol {
+ symbol := Symbol {
+ range = common.get_token_range(n^, ast_context.file.src),
+ type = .Function,
+ pkg = get_package_from_node(n^),
+ name = name,
+ }
+
+ return_types := make([dynamic]^ast.Field, ast_context.allocator)
+ arg_types := make([dynamic]^ast.Field, ast_context.allocator)
+
+ if v.results != nil {
+ for ret in v.results.list {
+ append(&return_types, ret)
+ }
+ }
+
+ if v.params != nil {
+ for param in v.params.list {
+ append(&arg_types, param)
+ }
+ }
+
+ if expr, ok := ast_context.globals[name]; ok {
+ if expr.deprecated {
+ symbol.flags |= {.Distinct}
+ }
+ }
+
+ symbol.value = SymbolProcedureValue {
+ return_types = return_types[:],
+ arg_types = arg_types[:],
+ }
+
+ return symbol
+}
+
+make_symbol_array_from_ast :: proc(ast_context: ^AstContext, v: ast.Array_Type, name: string) -> Symbol {
+ symbol := Symbol {
+ range = common.get_token_range(v.node, ast_context.file.src),
+ type = .Variable,
+ pkg = get_package_from_node(v.node),
+ name = name,
+ }
+
+ if v.len != nil {
+ symbol.value = SymbolFixedArrayValue {
+ expr = v.elem,
+ len = v.len,
+ }
+ } else {
+ symbol.value = SymbolSliceValue {
+ expr = v.elem,
+ }
+ }
+
+ return symbol
+}
+
+make_symbol_dynamic_array_from_ast :: proc(ast_context: ^AstContext, v: ast.Dynamic_Array_Type, name: string) -> Symbol {
+ symbol := Symbol {
+ range = common.get_token_range(v.node, ast_context.file.src),
+ type = .Variable,
+ pkg = get_package_from_node(v.node),
+ name = name,
+ }
+
+ symbol.value = SymbolDynamicArrayValue {
+ expr = v.elem,
+ }
+
+ return symbol
+}
+
+make_symbol_map_from_ast :: proc(ast_context: ^AstContext, v: ast.Map_Type, name: string) -> Symbol {
+ symbol := Symbol {
+ range = common.get_token_range(v.node, ast_context.file.src),
+ type = .Variable,
+ pkg = get_package_from_node(v.node),
+ name = name,
+ }
+
+ symbol.value = SymbolMapValue {
+ key = v.key,
+ value = v.value,
+ }
+
+ return symbol
+}
+
+make_symbol_basic_type_from_ast :: proc(ast_context: ^AstContext, n: ^ast.Node, v: ^ast.Ident) -> Symbol {
+ symbol := Symbol {
+ range = common.get_token_range(n^, ast_context.file.src),
+ type = .Variable,
+ pkg = get_package_from_node(n^),
+ }
+
+ symbol.value = SymbolBasicValue {
+ ident = v,
+ }
+
+ return symbol
+}
+
+make_symbol_union_from_ast :: proc(ast_context: ^AstContext, v: ast.Union_Type, ident: string, inlined := false) -> Symbol {
+ symbol := Symbol {
+ range = common.get_token_range(v, ast_context.file.src),
+ type = .Union,
+ pkg = get_package_from_node(v.node),
+ name = ident,
+ }
+
+ if inlined {
+ symbol.flags |= {.Anonymous}
+ symbol.name = "union"
+ }
+
+ symbol.value = SymbolUnionValue {
+ types = v.variants,
+ }
+
+ if v.poly_params != nil {
+ resolve_poly_union(ast_context, v.poly_params, &symbol)
+ }
+
+ return symbol
+}
+
+make_symbol_enum_from_ast :: proc(ast_context: ^AstContext, v: ast.Enum_Type, ident: string, inlined := false) -> Symbol {
+ symbol := Symbol {
+ range = common.get_token_range(v, ast_context.file.src),
+ type = .Enum,
+ name = ident,
+ pkg = get_package_from_node(v.node),
+ }
+
+ if inlined {
+ symbol.flags |= {.Anonymous}
+ symbol.name = "enum"
+ }
+
+
+ names := make([dynamic]string, ast_context.allocator)
+
+ for n in v.fields {
+ if ident, ok := n.derived.(^ast.Ident); ok {
+ append(&names, ident.name)
+ } else if field, ok := n.derived.(^ast.Field_Value); ok {
+ if ident, ok := field.field.derived.(^ast.Ident); ok {
+ append(&names, ident.name)
+ } else if binary, ok := field.field.derived.(^ast.Binary_Expr); ok {
+ append(&names, binary.left.derived.(^ast.Ident).name)
+ }
+ }
+ }
+
+ symbol.value = SymbolEnumValue {
+ names = names[:],
+ }
+
+ return symbol
+}
+
+make_symbol_bitset_from_ast :: proc(ast_context: ^AstContext, v: ast.Bit_Set_Type, ident: string, inlined := false) -> Symbol {
+ symbol := Symbol {
+ range = common.get_token_range(v, ast_context.file.src),
+ type = .Enum,
+ name = ident,
+ pkg = get_package_from_node(v.node),
+ }
+
+ if inlined {
+ symbol.flags |= {.Anonymous}
+ symbol.name = "bitset"
+ }
+
+ symbol.value = SymbolBitSetValue {
+ expr = v.elem,
+ }
+
+ return symbol
+}
+
+make_symbol_struct_from_ast :: proc(ast_context: ^AstContext, v: ast.Struct_Type, ident: string, inlined := false) -> Symbol {
+ symbol := Symbol {
+ range = common.get_token_range(v, ast_context.file.src),
+ type = .Struct,
+ pkg = get_package_from_node(v.node),
+ name = ident,
+ }
+
+ if inlined {
+ symbol.flags |= {.Anonymous}
+ symbol.name = "struct"
+ }
+
+ names := make([dynamic]string, ast_context.allocator)
+ types := make([dynamic]^ast.Expr, ast_context.allocator)
+ usings := make(map[string]bool, 0, ast_context.allocator)
+
+ for field in v.fields.list {
+ for n in field.names {
+ if identifier, ok := n.derived.(^ast.Ident); ok {
+ append(&names, identifier.name)
+ append(&types, clone_type(field.type, ast_context.allocator, nil))
+
+ if .Using in field.flags {
+ usings[identifier.name] = true
+ }
+ }
+ }
+ }
+
+ symbol.value = SymbolStructValue {
+ names = names[:],
+ types = types[:],
+ usings = usings,
+ }
+
+ if v.poly_params != nil {
+ resolve_poly_struct(ast_context, v.poly_params, &symbol)
+ }
+
+ //TODO change the expand to not double copy the array, but just pass the dynamic arrays
+ if len(usings) > 0 {
+ symbol.value = expand_struct_usings(ast_context, symbol, symbol.value.(SymbolStructValue))
+ }
+
+ return symbol
+}
+
+resolve_poly_union :: proc(ast_context: ^AstContext, poly_params: ^ast.Field_List, symbol: ^Symbol) {
+ if ast_context.call == nil {
+ return
+ }
+
+ symbol_value := &symbol.value.(SymbolUnionValue)
+
+ if symbol_value == nil {
+ return
+ }
+
+ i := 0
+
+ poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator)
+
+ for param in poly_params.list {
+ for name in param.names {
+ if len(ast_context.call.args) <= i {
+ break
+ }
+
+ if param.type == nil {
+ continue
+ }
+
+ if poly, ok := param.type.derived.(^ast.Typeid_Type); ok {
+ if ident, ok := name.derived.(^ast.Ident); ok {
+ poly_map[ident.name] = ast_context.call.args[i]
+ } else if poly, ok := name.derived.(^ast.Poly_Type); ok {
+ if poly.type != nil {
+ poly_map[poly.type.name] = ast_context.call.args[i]
+ }
+ }
+ }
+
+ i += 1
+ }
+ }
+
+ for type, i in symbol_value.types {
+ if ident, ok := type.derived.(^ast.Ident); ok {
+ if expr, ok := poly_map[ident.name]; ok {
+ symbol_value.types[i] = expr
+ }
+ } else if call_expr, ok := type.derived.(^ast.Call_Expr); ok {
+ if call_expr.args == nil {
+ continue
+ }
+
+ for arg, i in call_expr.args {
+ if ident, ok := arg.derived.(^ast.Ident); ok {
+ if expr, ok := poly_map[ident.name]; ok {
+ symbol_value.types[i] = expr
+ }
+ }
+ }
+ }
+ }
+}
+
+resolve_poly_struct :: proc(ast_context: ^AstContext, poly_params: ^ast.Field_List, symbol: ^Symbol) {
+ if ast_context.call == nil {
+ return
+ }
+
+ symbol_value := &symbol.value.(SymbolStructValue)
+
+ if symbol_value == nil {
+ return
+ }
+
+ i := 0
+
+ poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator)
+
+ for param in poly_params.list {
+ for name in param.names {
+ if len(ast_context.call.args) <= i {
+ break
+ }
+
+ if param.type == nil {
+ continue
+ }
+
+ if poly, ok := param.type.derived.(^ast.Typeid_Type); ok {
+ if ident, ok := name.derived.(^ast.Ident); ok {
+ poly_map[ident.name] = ast_context.call.args[i]
+ } else if poly, ok := name.derived.(^ast.Poly_Type); ok {
+ if poly.type != nil {
+ poly_map[poly.type.name] = ast_context.call.args[i]
+ }
+ }
+ }
+
+ i += 1
+ }
+ }
+
+ for type, i in symbol_value.types {
+ if ident, ok := type.derived.(^ast.Ident); ok {
+ if expr, ok := poly_map[ident.name]; ok {
+ symbol_value.types[i] = expr
+ }
+ } else if call_expr, ok := type.derived.(^ast.Call_Expr); ok {
+ if call_expr.args == nil {
+ continue
+ }
+
+ for arg, i in call_expr.args {
+ if ident, ok := arg.derived.(^ast.Ident); ok {
+ if expr, ok := poly_map[ident.name]; ok {
+ symbol_value.types[i] = expr
+ }
+ }
+ }
+ }
+ }
+}
+
+get_globals :: proc(file: ast.File, ast_context: ^AstContext) {
+ ast_context.variables["context"] = true
+
+ exprs := common.collect_globals(file)
+
+ for expr in exprs {
+ ast_context.variables[expr.name] = expr.mutable
+ ast_context.globals[expr.name] = expr
+ }
+}
+
+get_generic_assignment :: proc(file: ast.File, value: ^ast.Expr, ast_context: ^AstContext, results: ^[dynamic]^ast.Expr) {
+ using ast
+
+ ast_context.use_locals = true
+ ast_context.use_globals = true
+
+ #partial switch v in value.derived {
+ case ^Call_Expr:
+ ast_context.call = cast(^ast.Call_Expr)value
+
+ if symbol, ok := resolve_type_expression(ast_context, v.expr); ok {
+ if procedure, ok := symbol.value.(SymbolProcedureValue); ok {
+ for ret in procedure.return_types {
+ append(results, ret.type)
+ }
+ }
+ }
+ case ^Comp_Lit:
+ if v.type != nil {
+ append(results, v.type)
+ }
+ case ^Array_Type:
+ if v.elem != nil {
+ append(results, v.elem)
+ }
+ case ^Dynamic_Array_Type:
+ if v.elem != nil {
+ append(results, v.elem)
+ }
+ case ^Selector_Expr:
+ if v.expr != nil {
+ append(results, value)
+ }
+ case ^Type_Assertion:
+ if v.type != nil {
+ //This is the unique .? that can only be used with maybe
+ if unary, ok := v.type.derived.(^ast.Unary_Expr); ok && unary.op.kind == .Question {
+ append(results, cast(^ast.Expr)&v.node)
+ } else {
+ append(results, v.type)
+ }
+
+ b := make_bool_ast(ast_context)
+ b.pos.file = v.type.pos.file
+ append(results, b)
+ }
+ case:
+ //log.debugf("default node get_generic_assignment %v", v);
+ append(results, value)
+ }
+}
+
+get_locals_value_decl :: proc(file: ast.File, value_decl: ast.Value_Decl, ast_context: ^AstContext) {
+ using ast
+
+ if len(value_decl.names) <= 0 {
+ return
+ }
+
+ if value_decl.type != nil {
+ for name, i in value_decl.names {
+ str := common.get_ast_node_string(value_decl.names[i], file.src)
+ ast_context.variables[str] = value_decl.is_mutable
+ store_local(ast_context, value_decl.type, value_decl.end.offset, str, ast_context.local_id)
+ }
+ return
+ }
+
+ results := make([dynamic]^Expr, context.temp_allocator)
+
+ for value in value_decl.values {
+ get_generic_assignment(file, value, ast_context, &results)
+ }
+
+ if len(results) == 0 {
+ return
+ }
+
+ for name, i in value_decl.names {
+ result_i := min(len(results)-1, i)
+ str := common.get_ast_node_string(name, file.src)
+ ast_context.in_package[str] = get_package_from_node(results[result_i]^)
+ store_local(ast_context, results[result_i], value_decl.end.offset, str, ast_context.local_id)
+ ast_context.variables[str] = value_decl.is_mutable
+ }
+}
+
+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.current_package = ast_context.document_package
+
+ using ast
+
+ if stmt == nil {
+ return
+ }
+
+ if stmt.pos.offset > document_position.position {
+ return
+ }
+
+ #partial switch v in stmt.derived {
+ case ^Value_Decl:
+ get_locals_value_decl(file, v^, ast_context)
+ case ^Type_Switch_Stmt:
+ get_locals_type_switch_stmt(file, v^, ast_context, document_position)
+ case ^Switch_Stmt:
+ get_locals_switch_stmt(file, v^, ast_context, document_position)
+ case ^For_Stmt:
+ get_locals_for_stmt(file, v^, ast_context, document_position)
+ case ^Inline_Range_Stmt:
+ get_locals_stmt(file, v.body, ast_context, document_position)
+ case ^Range_Stmt:
+ get_locals_for_range_stmt(file, v^, ast_context, document_position)
+ case ^If_Stmt:
+ get_locals_if_stmt(file, v^, ast_context, document_position)
+ case ^Block_Stmt:
+ get_locals_block_stmt(file, v^, ast_context, document_position)
+ case ^Proc_Lit:
+ get_locals_stmt(file, v.body, ast_context, document_position)
+ case ^Assign_Stmt:
+ if save_assign {
+ get_locals_assign_stmt(file, v^, ast_context)
+ }
+ case ^Using_Stmt:
+ get_locals_using_stmt(v^, ast_context)
+ case ^When_Stmt:
+ get_locals_stmt(file, v.else_stmt, ast_context, document_position)
+ get_locals_stmt(file, v.body, ast_context, document_position)
+ case ^Case_Clause:
+ for stmt in v.body {
+ get_locals_stmt(file, stmt, ast_context, document_position)
+ }
+ case:
+ //log.debugf("default node local stmt %v", v);
+ }
+}
+
+get_locals_block_stmt :: proc(file: ast.File, block: ast.Block_Stmt, ast_context: ^AstContext, document_position: ^DocumentPositionContext) {
+ if !(block.pos.offset <= document_position.position && document_position.position <= block.end.offset) {
+ return
+ }
+
+ for stmt in block.stmts {
+ get_locals_stmt(file, stmt, ast_context, document_position)
+ }
+}
+
+get_locals_using_stmt :: proc(stmt: ast.Using_Stmt, ast_context: ^AstContext) {
+ for u in stmt.list {
+ if symbol, ok := resolve_type_expression(ast_context, u); ok {
+ #partial switch v in symbol.value {
+ case SymbolPackageValue:
+ if ident, ok := u.derived.(^ast.Ident); ok {
+ append(&ast_context.usings, ident.name)
+ }
+ case SymbolStructValue:
+ for name, i in v.names {
+ selector := new_type(ast.Selector_Expr, v.types[i].pos, v.types[i].end, context.temp_allocator)
+ selector.expr = u
+ selector.field = 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.local_id)
+ ast_context.variables[name] = true
+ }
+ }
+ }
+ }
+}
+
+get_locals_assign_stmt :: proc(file: ast.File, stmt: ast.Assign_Stmt, ast_context: ^AstContext) {
+ using ast
+
+ if stmt.lhs == nil || stmt.rhs == nil {
+ return
+ }
+
+ results := make([dynamic]^Expr, context.temp_allocator)
+
+ for rhs in stmt.rhs {
+ get_generic_assignment(file, rhs, ast_context, &results)
+ }
+
+ if len(stmt.lhs) != len(results) {
+ return
+ }
+
+ for lhs, i in stmt.lhs {
+ if ident, ok := lhs.derived.(^ast.Ident); ok {
+ store_local(ast_context, results[i], ident.pos.offset, ident.name, ast_context.local_id)
+ ast_context.variables[ident.name] = true
+ }
+ }
+}
+
+get_locals_if_stmt :: proc(file: ast.File, stmt: ast.If_Stmt, ast_context: ^AstContext, document_position: ^DocumentPositionContext) {
+ if !(stmt.pos.offset <= document_position.position && document_position.position <= stmt.end.offset) {
+ return
+ }
+
+ get_locals_stmt(file, stmt.init, ast_context, document_position, true)
+ get_locals_stmt(file, stmt.body, ast_context, document_position)
+ get_locals_stmt(file, stmt.else_stmt, ast_context, document_position)
+}
+
+get_locals_for_range_stmt :: proc(file: ast.File, stmt: ast.Range_Stmt, ast_context: ^AstContext, document_position: ^DocumentPositionContext) {
+ using ast
+
+ if !(stmt.body.pos.offset <= document_position.position && document_position.position <= stmt.body.end.offset) {
+ return
+ }
+
+ results := make([dynamic]^Expr, context.temp_allocator)
+
+ if stmt.expr == nil {
+ return
+ }
+
+ if symbol, ok := resolve_type_expression(ast_context, stmt.expr); ok {
+ #partial switch v in symbol.value {
+ case SymbolMapValue:
+ 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.local_id)
+ 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.local_id)
+ ast_context.variables[ident.name] = true
+ ast_context.in_package[ident.name] = symbol.pkg
+ }
+ }
+ case SymbolDynamicArrayValue:
+ if len(stmt.vals) >= 1 {
+ if ident, ok := stmt.vals[0].derived.(^Ident); ok {
+ store_local(ast_context, v.expr, ident.pos.offset, ident.name, ast_context.local_id)
+ 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, make_int_ast(ast_context), ident.pos.offset, ident.name, ast_context.local_id)
+ ast_context.variables[ident.name] = true
+ ast_context.in_package[ident.name] = symbol.pkg
+ }
+ }
+ case SymbolFixedArrayValue:
+ if len(stmt.vals) >= 1 {
+ if ident, ok := stmt.vals[0].derived.(^Ident); ok {
+ store_local(ast_context, v.expr, ident.pos.offset, ident.name, ast_context.local_id)
+ 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, make_int_ast(ast_context), ident.pos.offset, ident.name, ast_context.local_id)
+ ast_context.variables[ident.name] = true
+ ast_context.in_package[ident.name] = symbol.pkg
+ }
+ }
+ case SymbolSliceValue:
+ if len(stmt.vals) >= 1 {
+ if ident, ok := stmt.vals[0].derived.(^Ident); ok {
+ store_local(ast_context, v.expr, ident.pos.offset, ident.name, ast_context.local_id)
+ 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, make_int_ast(ast_context), ident.pos.offset, ident.name, ast_context.local_id)
+ ast_context.variables[ident.name] = true
+ ast_context.in_package[ident.name] = symbol.pkg
+ }
+ }
+ }
+ }
+
+ get_locals_stmt(file, stmt.body, ast_context, document_position)
+}
+
+get_locals_for_stmt :: proc(file: ast.File, stmt: ast.For_Stmt, ast_context: ^AstContext, document_position: ^DocumentPositionContext) {
+ if !(stmt.pos.offset <= document_position.position && document_position.position <= stmt.end.offset) {
+ return
+ }
+
+ get_locals_stmt(file, stmt.init, ast_context, document_position, true)
+ get_locals_stmt(file, stmt.body, ast_context, document_position)
+}
+
+get_locals_switch_stmt :: proc(file: ast.File, stmt: ast.Switch_Stmt, ast_context: ^AstContext, document_position: ^DocumentPositionContext) {
+ if !(stmt.pos.offset <= document_position.position && document_position.position <= stmt.end.offset) {
+ return
+ }
+
+ get_locals_stmt(file, stmt.body, ast_context, document_position)
+}
+
+get_locals_type_switch_stmt :: proc(file: ast.File, stmt: ast.Type_Switch_Stmt, ast_context: ^AstContext, document_position: ^DocumentPositionContext) {
+ using ast
+
+ if !(stmt.pos.offset <= document_position.position && document_position.position <= stmt.end.offset) {
+ return
+ }
+
+ if stmt.body == nil {
+ return
+ }
+
+ if block, ok := stmt.body.derived.(^Block_Stmt); ok {
+ for block_stmt in block.stmts {
+ if cause, ok := block_stmt.derived.(^Case_Clause); ok && cause.pos.offset <= document_position.position && document_position.position <= cause.end.offset {
+ for b in cause.body {
+ get_locals_stmt(file, b, ast_context, document_position)
+ }
+
+ tag := stmt.tag.derived.(^Assign_Stmt)
+
+ if len(tag.lhs) == 1 && len(cause.list) == 1 {
+ ident := tag.lhs[0].derived.(^Ident)
+ store_local(ast_context, cause.list[0], ident.pos.offset, ident.name, ast_context.local_id)
+ ast_context.variables[ident.name] = true
+ }
+ }
+ }
+ }
+}
+
+get_locals_proc_param_and_results :: proc(file: ast.File, function: ast.Proc_Lit, ast_context: ^AstContext, document_position: ^DocumentPositionContext) {
+ proc_lit, ok := function.derived.(^ast.Proc_Lit)
+
+ if !ok || proc_lit.body == nil {
+ return
+ }
+
+ if proc_lit.type != nil && proc_lit.type.params != nil {
+ for arg in proc_lit.type.params.list {
+ for name in arg.names {
+ 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.local_id)
+ 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[0] = arg.type
+ get_locals_using_stmt(using_stmt, ast_context)
+ }
+ } else {
+ str := common.get_ast_node_string(name, file.src)
+ store_local(ast_context, arg.default_value, name.pos.offset, str, ast_context.local_id)
+ ast_context.variables[str] = true
+ ast_context.parameters[str] = true
+ }
+ }
+ }
+ }
+
+ if proc_lit.type != nil && proc_lit.type.results != nil {
+ for result in proc_lit.type.results.list {
+ for name in result.names {
+ 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.local_id)
+ ast_context.variables[str] = true
+ ast_context.parameters[str] = true
+ }
+ }
+ }
+ }
+}
+
+get_locals :: proc(file: ast.File, function: ^ast.Node, ast_context: ^AstContext, document_position: ^DocumentPositionContext) {
+ proc_lit, ok := function.derived.(^ast.Proc_Lit)
+
+ if !ok || proc_lit.body == nil {
+ return
+ }
+
+ get_locals_proc_param_and_results(file, proc_lit^, ast_context, document_position)
+
+ block: ^ast.Block_Stmt
+ block, ok = proc_lit.body.derived.(^ast.Block_Stmt)
+
+ if !ok {
+ log.error("Proc_List body not block")
+ return
+ }
+
+ for stmt in block.stmts {
+ get_locals_stmt(file, stmt, ast_context, document_position)
+ }
+}
+
+clear_locals :: proc(ast_context: ^AstContext) {
+ clear(&ast_context.locals)
+ clear(&ast_context.parameters)
+ clear(&ast_context.variables)
+ clear(&ast_context.usings)
+}
+
+resolve_entire_file :: proc(document: ^common.Document, allocator := context.allocator) -> map[uintptr]SymbolAndNode {
+ ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri, allocator)
+
+ get_globals(document.ast, &ast_context)
+
+ ast_context.current_package = ast_context.document_package
+
+ symbols := make(map[uintptr]SymbolAndNode, 10000, allocator)
+
+ for decl in document.ast.decls {
+ resolve_entire_decl(&ast_context, decl, &symbols, allocator)
+ clear_local_group(&ast_context, 0)
+ add_local_group(&ast_context, 0)
+ }
+
+ return symbols
+}
+
+resolve_entire_decl :: proc(ast_context: ^AstContext, decl: ^ast.Node, symbols: ^map[uintptr]SymbolAndNode, allocator := context.allocator) {
+ Scope :: struct {
+ offset: int,
+ id: int,
+ }
+
+ Visit_Data :: struct {
+ ast_context: ^AstContext,
+ symbols: ^map[uintptr]SymbolAndNode,
+ scopes: [dynamic]Scope,
+ id_counter: int,
+ }
+
+ data := Visit_Data {
+ ast_context = ast_context,
+ symbols = symbols,
+ scopes = make([dynamic]Scope, allocator),
+ }
+
+ visit :: proc(visitor: ^ast.Visitor, node: ^ast.Node) -> ^ast.Visitor {
+ if node == nil || visitor == nil {
+ return nil
+ }
+ data := cast(^Visit_Data)visitor.data
+ ast_context := data.ast_context
+ ast_context.use_locals = true
+ ast_context.use_globals = true
+
+ //It's somewhat silly to check the scope everytime, but the alternative is to implement my own walker function.
+ if len(data.scopes) > 0 {
+ current_scope := data.scopes[len(data.scopes)-1]
+
+ if current_scope.offset < node.end.offset {
+ clear_local_group(ast_context, current_scope.id)
+
+ pop(&data.scopes)
+
+ if len(data.scopes) > 0 {
+ current_scope = data.scopes[len(data.scopes)-1]
+ ast_context.local_id = current_scope.id
+ } else {
+ ast_context.local_id = 0
+ }
+ }
+ }
+
+ #partial switch v in node.derived {
+ case ^ast.If_Stmt, ^ast.For_Stmt, ^ast.Range_Stmt, ^ast.Inline_Range_Stmt, ^ast.Proc_Lit:
+ scope: Scope
+ scope.id = data.id_counter
+ scope.offset = node.end.offset
+ data.id_counter += 1
+ ast_context.local_id = scope.id
+
+ append(&data.scopes, scope)
+ add_local_group(ast_context, scope.id)
+
+ position_context: DocumentPositionContext
+ position_context.position = node.end.offset
+ get_locals_stmt(ast_context.file, cast(^ast.Stmt)node, ast_context, &position_context)
+ case ^ast.Ident:
+ if symbol, ok := resolve_type_identifier(ast_context, v^); ok {
+ data.symbols[cast(uintptr)node] = SymbolAndNode {
+ node = v,
+ symbol = symbol,
+ }
+ }
+ case ^ast.Selector_Expr:
+ if symbol, ok := resolve_type_expression(ast_context, &v.node); ok {
+ data.symbols[cast(uintptr)node] = SymbolAndNode {
+ node = v,
+ symbol = symbol,
+ }
+ }
+ case ^ast.Call_Expr:
+ if symbol, ok := resolve_type_expression(ast_context, &v.node); ok {
+ data.symbols[cast(uintptr)node] = SymbolAndNode {
+ node = v,
+ symbol = symbol,
+ }
+ }
+ }
+
+ #partial switch v in node.derived {
+ case ^ast.Proc_Lit:
+ if v.body == nil {
+ break
+ }
+
+ type_position_context: DocumentPositionContext
+ type_position_context.position = v.end.offset
+ get_locals_proc_param_and_results(ast_context.file, v^, ast_context, &type_position_context)
+ }
+
+ return visitor
+ }
+
+ visitor := ast.Visitor {
+ data = &data,
+ visit = visit,
+ }
+
+ ast.walk(&visitor, decl)
+}
+
+concatenate_symbol_information :: proc {
+ concatenate_raw_symbol_information,
+ concatenate_raw_string_information,
+}
+
+concatenate_raw_symbol_information :: proc(ast_context: ^AstContext, symbol: Symbol, is_completion: bool) -> string {
+ return concatenate_raw_string_information(ast_context, symbol.pkg, symbol.name, symbol.signature, symbol.type, is_completion)
+}
+
+concatenate_raw_string_information :: proc(ast_context: ^AstContext, pkg: string, name: string, signature: string, type: SymbolType, is_completion: bool) -> string {
+ pkg := path.base(pkg, false, context.temp_allocator)
+
+ if type == .Package {
+ return fmt.tprintf("%v: package", name)
+ } else if type == .Keyword && is_completion {
+ return name
+ } else {
+ if signature != "" {
+ return fmt.tprintf("%v.%v: %v", pkg, name, signature)
+ } else {
+ return fmt.tprintf("%v.%v", pkg, name)
+ }
+ }
+}
+
+unwrap_enum :: proc(ast_context: ^AstContext, node: ^ast.Expr) -> (SymbolEnumValue, bool) {
+ if node == nil {
+ return {}, false
+ }
+
+ if enum_symbol, ok := resolve_type_expression(ast_context, node); ok {
+
+ if enum_value, ok := enum_symbol.value.(SymbolEnumValue); ok {
+ return enum_value, true
+ }
+ }
+
+ return {}, false
+}
+
+unwrap_union :: proc(ast_context: ^AstContext, node: ^ast.Expr) -> (SymbolUnionValue, bool) {
+ if union_symbol, ok := resolve_type_expression(ast_context, node); ok {
+ if union_value, ok := union_symbol.value.(SymbolUnionValue); ok {
+ return union_value, true
+ }
+ }
+
+ return {}, false
+}
+
+unwrap_bitset :: proc(ast_context: ^AstContext, bitset_symbol: Symbol) -> (SymbolEnumValue, bool) {
+ if bitset_value, ok := bitset_symbol.value.(SymbolBitSetValue); ok {
+ if enum_symbol, ok := resolve_type_expression(ast_context, bitset_value.expr); ok {
+ if enum_value, ok := enum_symbol.value.(SymbolEnumValue); ok {
+ return enum_value, true
+ }
+ }
+ }
+
+ return {}, false
+}
+
+get_signature :: proc(ast_context: ^AstContext, ident: ast.Ident, symbol: Symbol, was_variable := false) -> string {
+ if symbol.type == .Function {
+ return symbol.signature
+ }
+
+ if .Distinct in symbol.flags {
+ return symbol.name
+ }
+
+ is_variable := symbol.type == .Variable
+
+ #partial switch v in symbol.value {
+ case SymbolBasicValue:
+ return common.node_to_string(v.ident)
+ case SymbolBitSetValue:
+ return common.node_to_string(v.expr)
+ case SymbolEnumValue:
+ if is_variable {
+ return symbol.name
+ }
+ else {
+ return "enum"
+ }
+ case SymbolMapValue:
+ return strings.concatenate(a = {"map[", common.node_to_string(v.key), "]", common.node_to_string(v.value)}, allocator = ast_context.allocator)
+ case SymbolProcedureValue:
+ return "proc"
+ case SymbolStructValue:
+ if is_variable {
+ return symbol.name
+ }
+ else {
+ return "struct"
+ }
+ case SymbolUnionValue:
+ if is_variable {
+ return symbol.name
+ }
+ else {
+ return "union"
+ }
+ case SymbolDynamicArrayValue:
+ return strings.concatenate(a = {"[dynamic]", common.node_to_string(v.expr)}, allocator = ast_context.allocator)
+ case SymbolSliceValue:
+ return strings.concatenate(a = {"[]", common.node_to_string(v.expr)}, allocator = ast_context.allocator)
+ case SymbolFixedArrayValue:
+ return strings.concatenate(a = {"[", common.node_to_string(v.len), "]", common.node_to_string(v.expr)}, allocator = ast_context.allocator)
+ case SymbolPackageValue:
+ return "package"
+ case SymbolUntypedValue:
+ switch v.type {
+ case .Float: return "float"
+ case .String: return "string"
+ case .Bool: return "bool"
+ case .Integer: return "int"
+ }
+ }
+
+ return ""
+}
+
+position_in_proc_decl :: proc(position_context: ^DocumentPositionContext) -> bool {
+ if position_context.value_decl == nil {
+ return false
+ }
+
+ if len(position_context.value_decl.values) != 1 {
+ return false
+ }
+
+ if _, ok := position_context.value_decl.values[0].derived.(^ast.Proc_Type); ok {
+ return true
+ }
+
+ if proc_lit, ok := position_context.value_decl.values[0].derived.(^ast.Proc_Lit); ok {
+ if proc_lit.type != nil && position_in_node(proc_lit.type, position_context.position) {
+ return true
+ }
+ }
+
+ return false
+}
+
+
+is_lhs_comp_lit :: proc(position_context: ^DocumentPositionContext) -> bool {
+ if position_context.position <= position_context.comp_lit.open.offset {
+ return false
+ }
+
+ if len(position_context.comp_lit.elems) == 0 {
+ return true
+ }
+
+ for elem in position_context.comp_lit.elems {
+ if position_in_node(elem, position_context.position) {
+ if ident, ok := elem.derived.(^ast.Ident); ok {
+ return true
+ } else if field, ok := elem.derived.(^ast.Field_Value); ok {
+
+ if position_in_node(field.value, position_context.position) {
+ return false
+ }
+ }
+ }
+ }
+
+ return true
+}
+
+field_exists_in_comp_lit :: proc(comp_lit: ^ast.Comp_Lit, name: string) -> bool {
+ for elem in comp_lit.elems {
+ if field, ok := elem.derived.(^ast.Field_Value); ok {
+ if field.field != nil {
+ if ident, ok := field.field.derived.(^ast.Ident); ok {
+ if ident.name == name {
+ return true
+ }
+ }
+ }
+ }
+ }
+
+ return false
+}
+
+/*
+ Parser gives ranges of expression, but not actually where the commas are placed.
+*/
+get_call_commas :: proc(position_context: ^DocumentPositionContext, document: ^common.Document) {
+ if position_context.call == nil {
+ return
+ }
+
+ commas := make([dynamic]int, 0, 10, context.temp_allocator)
+
+ paren_count := 0
+ bracket_count := 0
+ brace_count := 0
+
+ if call, ok := position_context.call.derived.(^ast.Call_Expr); ok {
+ if document.text[call.open.offset] == '(' {
+ paren_count -= 1
+ }
+ for i := call.open.offset; i < call.close.offset; i += 1 {
+ switch document.text[i] {
+ case '[': paren_count += 1
+ case ']': paren_count -= 1
+ case '{': brace_count += 1
+ case '}': brace_count -= 1
+ case '(': paren_count += 1
+ case ')': paren_count -= 1
+ case ',':
+ if paren_count == 0 && brace_count == 0 && bracket_count == 0 {
+ append(&commas, i)
+ }
+ }
+ }
+ }
+
+ position_context.call_commas = commas[:]
+}
+
+type_to_string :: proc(ast_context: ^AstContext, expr: ^ast.Expr) -> string {
+ if symbol, ok := resolve_type_expression(ast_context, expr); ok {
+ if .Anonymous in symbol.flags {
+ return symbol.name
+ }
+ }
+
+ return common.node_to_string(expr)
+}
+
+/*
+ Figure out what exactly is at the given position and whether it is in a function, struct, etc.
+*/
+get_document_position_context :: proc(document: ^common.Document, position: common.Position, hint: DocumentPositionContextHint) -> (DocumentPositionContext, bool) {
+ position_context: DocumentPositionContext
+
+ position_context.hint = hint
+ position_context.file = document.ast
+ position_context.line = position.line
+
+ absolute_position, ok := common.get_absolute_position(position, document.text)
+
+ if !ok {
+ log.error("failed to get absolute position")
+ return position_context, false
+ }
+
+ 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
+ #partial 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
+ }
+
+ if !position_in_node(position_context.parent_comp_lit, position_context.position) {
+ position_context.parent_comp_lit = nil
+ }
+
+ if !position_in_node(position_context.assign, position_context.position) {
+ position_context.assign = nil
+ }
+
+ if !position_in_node(position_context.binary, position_context.position) {
+ position_context.binary = nil
+ }
+
+ if !position_in_node(position_context.parent_binary, position_context.position) {
+ position_context.parent_binary = nil
+ }
+
+ if hint == .Completion && position_context.selector == nil && position_context.field == nil {
+ fallback_position_context_completion(document, position, &position_context)
+ }
+
+ if (hint == .SignatureHelp || hint == .Completion) && position_context.call == nil {
+ fallback_position_context_signature(document, position, &position_context)
+ }
+
+ if hint == .SignatureHelp {
+ get_call_commas(&position_context, document)
+ }
+
+ return position_context, true
+}
+
+//terrible fallback code
+fallback_position_context_completion :: proc(document: ^common.Document, position: common.Position, position_context: ^DocumentPositionContext) {
+ paren_count: int
+ bracket_count: int
+ end: int
+ start: int
+ empty_dot: bool
+ empty_arrow: bool
+ last_dot: bool
+ last_arrow: bool
+ dots_seen: int
+ partial_arrow: bool
+
+ i := position_context.position - 1
+
+ end = i
+
+ for i > 0 {
+ c := position_context.file.src[i]
+
+ if c == '(' && paren_count == 0 {
+ start = i + 1
+ break
+ } else if c == '[' && bracket_count == 0 {
+ start = i + 1
+ break
+ } else if c == ']' && !last_dot {
+ start = i + 1
+ break
+ } else if c == ')' && !last_dot {
+ start = i + 1
+ break
+ } else if c == ')' {
+ paren_count -= 1
+ } else if c == '(' {
+ paren_count += 1
+ } else if c == '[' {
+ bracket_count += 1
+ } else if c == ']' {
+ bracket_count -= 1
+ } else if c == '.' {
+ dots_seen += 1
+ last_dot = true
+ i -= 1
+ continue
+ } else if position_context.file.src[max(0, i - 1)] == '-' && c == '>' {
+ last_arrow = true
+ i -= 2
+ continue
+ }
+
+ //ignore everything in the bracket
+ if bracket_count != 0 || paren_count != 0 {
+ i -= 1
+ continue
+ }
+
+ //yeah..
+ if c == ' ' || c == '{' || c == ',' ||
+ c == '}' || c == '^' || c == ':' ||
+ c == '\n' || c == '\r' || c == '=' ||
+ c == '<' || c == '-' || c == '!' ||
+ c == '+' || c == '&'|| c == '|' {
+ start = i + 1
+ break
+ } else if c == '>' {
+ partial_arrow = true
+ }
+
+ last_dot = false
+ last_arrow = false
+
+ i -= 1
+ }
+
+ if i >= 0 && position_context.file.src[end] == '.' {
+ empty_dot = true
+ end -= 1
+ } else if i >= 0 && position_context.file.src[max(0, end - 1)] == '-' && position_context.file.src[end] == '>' {
+ empty_arrow = true
+ end -= 2
+ position_context.arrow = true
+ }
+
+ begin_offset := max(0, start)
+ end_offset := max(start, end + 1)
+ line_offset := begin_offset
+
+ if line_offset < len(position_context.file.src) {
+ for line_offset > 0 {
+ c := position_context.file.src[line_offset]
+ if c == '\n' || c == '\r' {
+ line_offset += 1
+ break
+ }
+ line_offset -= 1
+ }
+ }
+
+ str := position_context.file.src[0:end_offset]
+
+ if empty_dot && end_offset - begin_offset == 0 {
+ position_context.implicit = true
+ return
+ }
+
+ s := string(position_context.file.src[begin_offset:end_offset])
+
+ if !partial_arrow {
+
+ only_whitespaces := true
+
+ for r in s {
+ if !strings.is_space(r) {
+ only_whitespaces = false
+ }
+ }
+
+ if only_whitespaces {
+ return
+ }
+ }
+
+ p := parser.Parser {
+ err = common.parser_warning_handler, //empty
+ warn = common.parser_warning_handler, //empty
+ flags = {.Optional_Semicolons},
+ file = &position_context.file,
+ }
+
+ tokenizer.init(&p.tok, str, position_context.file.fullpath, common.parser_warning_handler)
+
+ p.tok.ch = ' '
+ p.tok.line_count = position.line + 1
+ p.tok.line_offset = line_offset
+ p.tok.offset = begin_offset
+ p.tok.read_offset = begin_offset
+
+ tokenizer.advance_rune(&p.tok)
+
+ if p.tok.ch == utf8.RUNE_BOM {
+ tokenizer.advance_rune(&p.tok)
+ }
+
+ parser.advance_token(&p)
+
+ context.allocator = context.temp_allocator
+
+ e := parser.parse_expr(&p, true)
+
+ if empty_dot || empty_arrow {
+ position_context.selector = e
+ } else if s, ok := e.derived.(^ast.Selector_Expr); ok {
+ position_context.selector = s.expr
+ 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 {
+ position_context.tag = s.expr
+ } else if bad_expr, ok := e.derived.(^ast.Bad_Expr); ok {
+ //this is most likely because of use of 'in', 'context', etc.
+ //try to go back one dot.
+
+ src_with_dot := string(position_context.file.src[0:min(len(position_context.file.src), end_offset + 1)])
+ last_dot := strings.last_index(src_with_dot, ".")
+
+ if last_dot == -1 {
+ return
+ }
+
+ tokenizer.init(&p.tok, position_context.file.src[0:last_dot], position_context.file.fullpath, common.parser_warning_handler)
+
+ p.tok.ch = ' '
+ p.tok.line_count = position.line + 1
+ p.tok.line_offset = line_offset
+ p.tok.offset = begin_offset
+ p.tok.read_offset = begin_offset
+
+ tokenizer.advance_rune(&p.tok)
+
+ if p.tok.ch == utf8.RUNE_BOM {
+ tokenizer.advance_rune(&p.tok)
+ }
+
+ parser.advance_token(&p)
+
+ e := parser.parse_expr(&p, true)
+
+ if e == nil {
+ position_context.abort_completion = true
+ return
+ } else if e, ok := e.derived.(^ast.Bad_Expr); ok {
+ position_context.abort_completion = true
+ return
+ }
+
+ position_context.selector = e
+
+ ident := new_type(ast.Ident, e.pos, e.end, context.temp_allocator)
+ ident.name = string(position_context.file.src[last_dot + 1:end_offset])
+
+ if ident.name != "" {
+ position_context.field = ident
+ }
+ } else {
+ position_context.identifier = e
+ }
+}
+
+fallback_position_context_signature :: proc(document: ^common.Document, position: common.Position, position_context: ^DocumentPositionContext) {
+ end: int
+ start: int
+ i := position_context.position - 1
+ end = i
+
+ for i > 0 {
+
+ c := position_context.file.src[i]
+
+ if c == ' ' || c == '\n' || c == '\r' {
+ start = i + 1
+ break
+ }
+
+ i -= 1
+ }
+
+ if end < 0 {
+ return
+ }
+
+ if position_context.file.src[end] != '(' {
+ return
+ }
+
+ end -= 1
+
+ begin_offset := max(0, start)
+ end_offset := max(start, end + 1)
+
+ if end_offset - begin_offset <= 1 {
+ return
+ }
+
+ str := position_context.file.src[0:end_offset]
+
+ p := parser.Parser {
+ err = common.parser_warning_handler, //empty
+ warn = common.parser_warning_handler, //empty
+ file = &position_context.file,
+ }
+
+ tokenizer.init(&p.tok, str, position_context.file.fullpath, common.parser_warning_handler)
+
+ 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)
+
+ if p.tok.ch == utf8.RUNE_BOM {
+ tokenizer.advance_rune(&p.tok)
+ }
+
+ parser.advance_token(&p)
+
+ context.allocator = context.temp_allocator
+
+ position_context.call = parser.parse_expr(&p, true)
+
+ if _, ok := position_context.call.derived.(^ast.Proc_Type); ok {
+ position_context.call = nil
+ }
+
+ //log.error(string(position_context.file.src[begin_offset:end_offset]));
+}
+
+/*
+ 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_array,
+ get_document_position_dynamic_array,
+ get_document_position_node,
+}
+
+get_document_position_array :: proc(array: $A/[]^$T, position_context: ^DocumentPositionContext) {
+ for elem, i in array {
+ get_document_position(elem, position_context)
+ }
+}
+
+get_document_position_dynamic_array :: proc(array: $A/[dynamic]^$T, position_context: ^DocumentPositionContext) {
+ for elem, i in array {
+ get_document_position(elem, position_context)
+ }
+}
+
+position_in_node :: proc(node: ^ast.Node, position: common.AbsolutePosition) -> bool {
+ return node != nil && node.pos.offset <= position && position <= node.end.offset
+}
+
+get_document_position_node :: proc(node: ^ast.Node, position_context: ^DocumentPositionContext) {
+ using ast
+
+ if node == nil {
+ return
+ }
+
+ if !position_in_node(node, position_context.position) {
+ return
+ }
+
+ #partial switch n in node.derived {
+ case ^Bad_Expr:
+ case ^Ident:
+ position_context.identifier = node
+ case ^Implicit:
+ case ^Undef:
+ case ^Basic_Lit:
+ case ^Ellipsis:
+ get_document_position(n.expr, position_context)
+ case ^Proc_Lit:
+ get_document_position(n.type, position_context)
+
+ if position_in_node(n.body, position_context.position) {
+ position_context.function = cast(^Proc_Lit)node
+ get_document_position(n.body, position_context)
+ }
+ case ^Comp_Lit:
+ //only set this for the parent comp literal, since we will need to walk through it to infer types.
+ if position_context.parent_comp_lit == nil {
+ position_context.parent_comp_lit = cast(^Comp_Lit)node
+ }
+
+ position_context.comp_lit = cast(^Comp_Lit)node
+
+ get_document_position(n.type, position_context)
+ get_document_position(n.elems, position_context)
+ case ^Tag_Expr:
+ get_document_position(n.expr, position_context)
+ case ^Unary_Expr:
+ get_document_position(n.expr, position_context)
+ case ^Binary_Expr:
+ if position_context.parent_binary == nil {
+ position_context.parent_binary = cast(^Binary_Expr)node
+ }
+ position_context.binary = cast(^Binary_Expr)node
+ get_document_position(n.left, position_context)
+ get_document_position(n.right, position_context)
+ case ^Paren_Expr:
+ get_document_position(n.expr, position_context)
+ case ^Call_Expr:
+ if position_context.hint == .SignatureHelp || position_context.hint == .Completion {
+ position_context.call = cast(^Expr)node
+ }
+ get_document_position(n.expr, position_context)
+ get_document_position(n.args, position_context)
+ case ^Selector_Expr:
+ if position_context.hint == .Completion {
+ if n.field != nil && n.field.pos.line - 1 == position_context.line {
+ //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
+ get_document_position(n.expr, position_context)
+ get_document_position(n.field, position_context)
+ } else {
+ get_document_position(n.expr, position_context)
+ get_document_position(n.field, position_context)
+ }
+ case ^Index_Expr:
+ get_document_position(n.expr, position_context)
+ get_document_position(n.index, position_context)
+ case ^Deref_Expr:
+ get_document_position(n.expr, position_context)
+ case ^Slice_Expr:
+ get_document_position(n.expr, position_context)
+ get_document_position(n.low, position_context)
+ get_document_position(n.high, position_context)
+ case ^Field_Value:
+ position_context.field_value = cast(^Field_Value)node
+ get_document_position(n.field, position_context)
+ get_document_position(n.value, position_context)
+ case ^Ternary_If_Expr:
+ get_document_position(n.x, position_context)
+ get_document_position(n.cond, position_context)
+ get_document_position(n.y, position_context)
+ case ^Ternary_When_Expr:
+ get_document_position(n.x, position_context)
+ get_document_position(n.cond, position_context)
+ get_document_position(n.y, position_context)
+ case ^Type_Assertion:
+ get_document_position(n.expr, position_context)
+ get_document_position(n.type, position_context)
+ case ^Type_Cast:
+ get_document_position(n.type, position_context)
+ get_document_position(n.expr, position_context)
+ case ^Auto_Cast:
+ get_document_position(n.expr, position_context)
+ case ^Bad_Stmt:
+ case ^Empty_Stmt:
+ case ^Expr_Stmt:
+ get_document_position(n.expr, position_context)
+ case ^Tag_Stmt:
+ r := cast(^Tag_Stmt)node
+ get_document_position(r.stmt, position_context)
+ case ^Assign_Stmt:
+ position_context.assign = cast(^Assign_Stmt)node
+ get_document_position(n.lhs, position_context)
+ get_document_position(n.rhs, position_context)
+ case ^Block_Stmt:
+ get_document_position(n.label, position_context)
+ get_document_position(n.stmts, position_context)
+ case ^If_Stmt:
+ get_document_position(n.label, position_context)
+ get_document_position(n.init, position_context)
+ get_document_position(n.cond, position_context)
+ get_document_position(n.body, position_context)
+ get_document_position(n.else_stmt, position_context)
+ case ^When_Stmt:
+ get_document_position(n.cond, position_context)
+ get_document_position(n.body, position_context)
+ get_document_position(n.else_stmt, position_context)
+ case ^Return_Stmt:
+ position_context.returns = cast(^Return_Stmt)node
+ get_document_position(n.results, position_context)
+ case ^Defer_Stmt:
+ get_document_position(n.stmt, position_context)
+ case ^For_Stmt:
+ get_document_position(n.label, position_context)
+ get_document_position(n.init, position_context)
+ get_document_position(n.cond, position_context)
+ get_document_position(n.post, position_context)
+ get_document_position(n.body, position_context)
+ case ^Range_Stmt:
+ get_document_position(n.label, position_context)
+ get_document_position(n.vals, position_context)
+ get_document_position(n.expr, position_context)
+ get_document_position(n.body, position_context)
+ case ^Case_Clause:
+ for elem in n.list {
+ if position_in_node(elem, position_context.position) {
+ position_context.case_clause = cast(^Case_Clause)node
+ break
+ }
+ }
+
+ get_document_position(n.list, position_context)
+ get_document_position(n.body, position_context)
+ case ^Switch_Stmt:
+ position_context.switch_stmt = cast(^Switch_Stmt)node
+ get_document_position(n.label, position_context)
+ get_document_position(n.init, position_context)
+ get_document_position(n.cond, position_context)
+ get_document_position(n.body, position_context)
+ case ^Type_Switch_Stmt:
+ position_context.switch_type_stmt = cast(^Type_Switch_Stmt)node
+ get_document_position(n.label, position_context)
+ get_document_position(n.tag, position_context)
+ get_document_position(n.expr, position_context)
+ get_document_position(n.body, position_context)
+ case ^Branch_Stmt:
+ get_document_position(n.label, position_context)
+ case ^Using_Stmt:
+ get_document_position(n.list, position_context)
+ case ^Bad_Decl:
+ case ^Value_Decl:
+ position_context.value_decl = cast(^Value_Decl)node
+ get_document_position(n.attributes, position_context)
+
+ for name in n.names {
+ if position_in_node(name, position_context.position) && n.end.line - 1 == position_context.line {
+ position_context.abort_completion = true
+ break
+ }
+ }
+ get_document_position(n.names, position_context)
+ get_document_position(n.type, position_context)
+ get_document_position(n.values, position_context)
+ case ^Package_Decl:
+ case ^Import_Decl:
+ case ^Foreign_Block_Decl:
+ get_document_position(n.attributes, position_context)
+ get_document_position(n.foreign_library, position_context)
+ get_document_position(n.body, position_context)
+ case ^Foreign_Import_Decl:
+ get_document_position(n.name, position_context)
+ case ^Proc_Group:
+ get_document_position(n.args, position_context)
+ case ^Attribute:
+ get_document_position(n.elems, position_context)
+ case ^Field:
+ get_document_position(n.names, position_context)
+ get_document_position(n.type, position_context)
+ get_document_position(n.default_value, position_context)
+ case ^Field_List:
+ get_document_position(n.list, position_context)
+ case ^Typeid_Type:
+ get_document_position(n.specialization, position_context)
+ case ^Helper_Type:
+ get_document_position(n.type, position_context)
+ case ^Distinct_Type:
+ get_document_position(n.type, position_context)
+ case ^Poly_Type:
+ get_document_position(n.type, position_context)
+ get_document_position(n.specialization, position_context)
+ case ^Proc_Type:
+ get_document_position(n.params, position_context)
+ get_document_position(n.results, position_context)
+ case ^Pointer_Type:
+ get_document_position(n.elem, position_context)
+ case ^Array_Type:
+ get_document_position(n.len, position_context)
+ get_document_position(n.elem, position_context)
+ case ^Dynamic_Array_Type:
+ get_document_position(n.elem, position_context)
+ case ^Struct_Type:
+ get_document_position(n.poly_params, position_context)
+ get_document_position(n.align, position_context)
+ get_document_position(n.fields, position_context)
+ case ^Union_Type:
+ get_document_position(n.poly_params, position_context)
+ get_document_position(n.align, position_context)
+ get_document_position(n.variants, position_context)
+ case ^Enum_Type:
+ get_document_position(n.base_type, position_context)
+ get_document_position(n.fields, position_context)
+ case ^Bit_Set_Type:
+ get_document_position(n.elem, position_context)
+ get_document_position(n.underlying, position_context)
+ case ^Map_Type:
+ get_document_position(n.key, position_context)
+ get_document_position(n.value, position_context)
+ case ^Implicit_Selector_Expr:
+ position_context.implicit = true
+ get_document_position(n.field, position_context)
+ case:
+ log.errorf("Unhandled node kind: %T", n)
+ }
+}
diff --git a/src/server/build.odin b/src/server/build.odin
new file mode 100644
index 0000000..c9edf93
--- /dev/null
+++ b/src/server/build.odin
@@ -0,0 +1,215 @@
+package server
+
+import "core:path/filepath"
+import path "core:path/slashpath"
+import "core:os"
+import "core:fmt"
+import "core:odin/parser"
+import "core:odin/ast"
+import "core:log"
+import "core:odin/tokenizer"
+import "core:strings"
+import "core:mem"
+import "core:runtime"
+
+import "shared:common"
+
+
+symbol_collection: SymbolCollection
+
+files: [dynamic]string
+
+platform_os: map[string]bool = {
+ "windows" = true,
+ "linux" = true,
+ "essence" = true,
+ "js" = true,
+ "freebsd" = true,
+ "darwin" = true,
+ "wasm32" = true,
+}
+
+
+os_enum_to_string: map[runtime.Odin_OS_Type]string = {
+ .Windows = "windows",
+ .Darwin = "darwin",
+ .Linux = "linux",
+ .Essence = "essence",
+ .FreeBSD = "freebsd",
+ .WASI = "wasi",
+ .JS = "js",
+ .Freestanding = "freestanding",
+}
+
+
+walk_static_index_build :: proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip_dir: bool) {
+ if info.is_dir {
+ return 0, false
+ }
+
+ if filepath.ext(info.name) != ".odin" {
+ return 0, false
+ }
+
+ last_underscore_index := strings.last_index(info.name, "_")
+ last_dot_index := strings.last_index(info.name, ".")
+
+ if last_underscore_index + 1 < last_dot_index {
+ name_between := info.name[last_underscore_index + 1:last_dot_index]
+
+ if _, ok := platform_os[name_between]; ok {
+ if name_between != os_enum_to_string[ODIN_OS] {
+ return 0, false
+ }
+ }
+ }
+
+ forward, _ := filepath.to_slash(info.fullpath, context.temp_allocator)
+
+ append(&files, strings.clone(forward, context.allocator))
+
+ return 0, false
+}
+
+build_static_index :: proc(allocator := context.allocator, config: ^common.Config) {
+ symbol_collection = make_symbol_collection(allocator, config)
+
+ files = make([dynamic]string, context.allocator)
+
+ for k, v in config.collections {
+ filepath.walk(v, walk_static_index_build)
+ }
+
+ when ODIN_OS == .Windows {
+ slashed, _ := filepath.to_slash(os.get_current_directory(context.temp_allocator), context.temp_allocator)
+ } else {
+ slashed, _ := filepath.to_slash(os.get_current_directory(), context.temp_allocator)
+ }
+
+ when ODIN_OS == .Windows {
+ builtin_package := path.join(elems = {slashed, "builtin"}, allocator = context.temp_allocator)
+ append(&indexer.builtin_packages, strings.to_lower(builtin_package))
+ } else {
+ builtin_package := path.join(elems = {slashed, "builtin"}, allocator = context.allocator)
+ append(&indexer.builtin_packages, builtin_package)
+ }
+
+ filepath.walk(builtin_package, walk_static_index_build)
+
+ temp_arena: mem.Arena
+
+ mem.init_arena(&temp_arena, make([]byte, mem.megabytes(100)))
+
+ context.allocator = mem.arena_allocator(&temp_arena)
+
+ for fullpath in files {
+ data, ok := os.read_entire_file(fullpath, context.allocator)
+
+ if !ok {
+ log.errorf("failed to read entire file for indexing %v", fullpath)
+ continue
+ }
+
+ p := parser.Parser {
+ err = log_error_handler,
+ warn = log_warning_handler,
+ flags = {.Optional_Semicolons},
+ }
+
+ dir := filepath.base(filepath.dir(fullpath, context.allocator))
+
+ pkg := new(ast.Package)
+ pkg.kind = .Normal
+ pkg.fullpath = fullpath
+ pkg.name = dir
+
+ if dir == "runtime" {
+ pkg.kind = .Runtime
+ }
+
+ file := ast.File {
+ fullpath = fullpath,
+ src = string(data),
+ pkg = pkg,
+ }
+
+ ok = parser.parse_file(&p, &file)
+
+ if !ok {
+ log.info(pkg)
+ log.errorf("error in parse file for indexing %v", fullpath)
+ }
+
+ uri := common.create_uri(fullpath, context.allocator)
+
+ collect_symbols(&symbol_collection, file, uri.uri)
+
+ free_all(context.allocator)
+ }
+
+ //afterwards, I have to go through the files again to find all the references. Better to reload individually the files again to ensure we don't use too much memory.
+
+ for fullpath in files {
+ data, ok := os.read_entire_file(fullpath, context.allocator)
+
+ if !ok {
+ log.errorf("failed to read entire file for indexing %v", fullpath)
+ continue
+ }
+
+ p := parser.Parser {
+ err = log_error_handler,
+ warn = log_warning_handler,
+ flags = {.Optional_Semicolons},
+ }
+
+ dir := filepath.base(filepath.dir(fullpath, context.allocator))
+
+ pkg := new(ast.Package)
+ pkg.kind = .Normal
+ pkg.fullpath = fullpath
+ pkg.name = dir
+
+ if dir == "runtime" {
+ pkg.kind = .Runtime
+ }
+
+ file := ast.File {
+ fullpath = fullpath,
+ src = string(data),
+ pkg = pkg,
+ }
+
+ ok = parser.parse_file(&p, &file)
+
+ if !ok {
+ log.info(pkg)
+ log.errorf("error in parse file for indexing %v", fullpath)
+ }
+
+ uri := common.create_uri(fullpath, context.allocator)
+
+ //collect_references(&symbol_collection, file, uri.uri)
+
+ free_all(context.allocator)
+
+ delete(fullpath, allocator)
+ }
+
+ delete(files)
+ delete(temp_arena.data)
+
+ indexer.static_index = make_memory_index(symbol_collection)
+}
+
+free_static_index :: proc() {
+ delete_symbol_collection(symbol_collection)
+}
+
+log_error_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) {
+ log.warnf("%v %v %v", pos, msg, args)
+}
+
+log_warning_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) {
+ log.warnf("%v %v %v", pos, msg, args)
+}
diff --git a/src/server/caches.odin b/src/server/caches.odin
index cc993c3..42dbc27 100644
--- a/src/server/caches.odin
+++ b/src/server/caches.odin
@@ -1,19 +1,17 @@
package server
-import "shared:index"
-import "shared:analysis"
import "shared:common"
//Used in semantic tokens and inlay hints to handle the entire file being resolved.
FileResolveCache :: struct {
- files: map[string]map[uintptr]index.SymbolAndNode,
+ files: map[string]map[uintptr]SymbolAndNode,
}
file_resolve_cache: FileResolveCache
-resolve_entire_file :: proc(document: ^common.Document) -> map[uintptr]index.SymbolAndNode{
+resolve_entire_file_cached :: proc(document: ^common.Document) -> map[uintptr]SymbolAndNode{
if document.uri.uri not_in file_resolve_cache.files {
- file_resolve_cache.files[document.uri.uri] = analysis.resolve_entire_file(
+ file_resolve_cache.files[document.uri.uri] = resolve_entire_file(
document,
common.scratch_allocator(document.allocator),
)
diff --git a/src/server/clone.odin b/src/server/clone.odin
new file mode 100644
index 0000000..9a4af46
--- /dev/null
+++ b/src/server/clone.odin
@@ -0,0 +1,253 @@
+package server
+
+import "core:mem"
+import "core:fmt"
+import "core:odin/tokenizer"
+import "core:odin/ast"
+import "core:strings"
+import "core:log"
+import "core:intrinsics"
+import "core:reflect"
+_ :: intrinsics
+
+new_type :: proc($T: typeid, pos, end: tokenizer.Pos, allocator: mem.Allocator) -> ^T {
+ n, _ := mem.new(T, allocator)
+ n.pos = pos
+ n.end = end
+ n.derived = n
+ base: ^ast.Node = n // dummy check
+ _ = base // "Use" type to make -vet happy
+ when intrinsics.type_has_field(T, "derived_expr") {
+ n.derived_expr = n
+ }
+ when intrinsics.type_has_field(T, "derived_stmt") {
+ n.derived_stmt = n
+ }
+ return n
+}
+
+clone_type :: proc {
+ clone_node,
+ clone_expr,
+ clone_array,
+ clone_dynamic_array,
+}
+
+clone_array :: proc(array: $A/[]^$T, allocator: mem.Allocator, unique_strings: ^map[string]string) -> A {
+ if len(array) == 0 {
+ return nil
+ }
+ res := make(A, len(array), allocator)
+ for elem, i in array {
+ res[i] = cast(^T)clone_type(elem, allocator, unique_strings)
+ }
+ return res
+}
+
+clone_dynamic_array :: proc(array: $A/[dynamic]^$T, allocator: mem.Allocator, unique_strings: ^map[string]string) -> A {
+ if len(array) == 0 {
+ return nil
+ }
+ res := make(A, len(array), allocator)
+ for elem, i in array {
+ res[i] = auto_cast clone_type(elem, allocator, unique_strings)
+ }
+ return res
+}
+
+clone_expr :: proc(node: ^ast.Expr, allocator: mem.Allocator, unique_strings: ^map[string]string) -> ^ast.Expr {
+ return cast(^ast.Expr)clone_node(node, allocator, unique_strings)
+}
+
+clone_node :: proc(node: ^ast.Node, allocator: mem.Allocator, unique_strings: ^map[string]string) -> ^ast.Node {
+ using ast
+ if node == nil {
+ return nil
+ }
+
+ size := size_of(Node)
+ align := align_of(Node)
+ ti := reflect.union_variant_type_info(node.derived)
+ if ti != nil {
+ elem := ti.variant.(reflect.Type_Info_Pointer).elem
+ size = elem.size
+ align = elem.align
+ }
+
+ #partial switch in node.derived {
+ case ^Package, ^File:
+ panic("Cannot clone this node type")
+ }
+
+ res := cast(^Node)mem.alloc(size, align, allocator)
+ src: rawptr = node
+ if node.derived != nil {
+ src = (^rawptr)(&node.derived)^
+ }
+ mem.copy(res, src, size)
+ res_ptr_any: any
+ res_ptr_any.data = &res
+ res_ptr_any.id = ti.id
+
+ if unique_strings != nil && node.pos.file != "" {
+ res.pos.file = get_index_unique_string(unique_strings, allocator, node.pos.file)
+ } else {
+ res.pos.file = node.pos.file
+ }
+
+ if unique_strings != nil && node.end.file != "" {
+ res.end.file = get_index_unique_string(unique_strings, allocator, node.end.file)
+ } else {
+ res.end.file = node.end.file
+ }
+
+ reflect.set_union_value(res.derived, res_ptr_any)
+
+ res_ptr := reflect.deref(res_ptr_any)
+
+ if de := reflect.struct_field_value_by_name(res_ptr, "derived_expr", true); de != nil {
+ reflect.set_union_value(de, res_ptr_any)
+ }
+ if ds := reflect.struct_field_value_by_name(res_ptr, "derived_stmt", true); ds != nil {
+ reflect.set_union_value(ds, res_ptr_any)
+ }
+
+ if res.derived != nil do #partial switch r in res.derived {
+ case ^Ident:
+ n := node.derived.(^Ident)
+
+ if unique_strings == nil {
+ r.name = strings.clone(n.name, allocator)
+ } else {
+ r.name = get_index_unique_string(unique_strings, allocator, n.name)
+ }
+ case ^Implicit:
+ n := node.derived.(^Implicit)
+ if unique_strings == nil {
+ r.tok.text = strings.clone(n.tok.text, allocator)
+ } else {
+ r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text)
+ }
+ case ^Undef:
+ case ^Basic_Lit:
+ n := node.derived.(^Basic_Lit)
+ if unique_strings == nil {
+ r.tok.text = strings.clone(n.tok.text, allocator)
+ } else {
+ r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text)
+ }
+ case ^Basic_Directive:
+ n := node.derived.(^Basic_Directive)
+ if unique_strings == nil {
+ r.name = strings.clone(n.name, allocator)
+ } else {
+ r.name = get_index_unique_string(unique_strings, allocator, n.name)
+ }
+ case ^Ellipsis:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Tag_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Unary_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Binary_Expr:
+ r.left = clone_type(r.left, allocator, unique_strings)
+ r.right = clone_type(r.right, allocator, unique_strings)
+ case ^Paren_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Selector_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ r.field = auto_cast clone_type(r.field, allocator, unique_strings)
+ case ^Implicit_Selector_Expr:
+ r.field = auto_cast clone_type(r.field, allocator, unique_strings)
+ case ^Slice_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ r.low = clone_type(r.low, allocator, unique_strings)
+ r.high = clone_type(r.high, allocator, unique_strings)
+ case ^Attribute:
+ r.elems = clone_type(r.elems, allocator, unique_strings)
+ case ^Distinct_Type:
+ r.type = clone_type(r.type, allocator, unique_strings)
+ case ^Proc_Type:
+ r.params = auto_cast clone_type(r.params, allocator, unique_strings)
+ r.results = auto_cast clone_type(r.results, allocator, unique_strings)
+ case ^Pointer_Type:
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ case ^Array_Type:
+ r.len = clone_type(r.len, allocator, unique_strings)
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ r.tag = clone_type(r.tag, allocator, unique_strings)
+ case ^Dynamic_Array_Type:
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ r.tag = clone_type(r.tag, allocator, unique_strings)
+ case ^Struct_Type:
+ r.poly_params = auto_cast clone_type(r.poly_params, allocator, unique_strings)
+ r.align = clone_type(r.align, allocator, unique_strings)
+ r.fields = auto_cast clone_type(r.fields, allocator, unique_strings)
+ r.where_clauses = clone_type(r.where_clauses, allocator, unique_strings)
+ case ^Field:
+ r.names = clone_type(r.names, allocator, unique_strings)
+ r.type = clone_type(r.type, allocator, unique_strings)
+ r.default_value = clone_type(r.default_value, allocator, unique_strings)
+ case ^Field_List:
+ r.list = clone_type(r.list, allocator, unique_strings)
+ case ^Field_Value:
+ r.field = clone_type(r.field, allocator, unique_strings)
+ r.value = clone_type(r.value, allocator, unique_strings)
+ case ^Union_Type:
+ r.poly_params = auto_cast clone_type(r.poly_params, allocator, unique_strings)
+ r.align = clone_type(r.align, allocator, unique_strings)
+ r.variants = clone_type(r.variants, allocator, unique_strings)
+ r.where_clauses = clone_type(r.where_clauses, allocator, unique_strings)
+ case ^Enum_Type:
+ r.base_type = clone_type(r.base_type, allocator, unique_strings)
+ r.fields = clone_type(r.fields, allocator, unique_strings)
+ case ^Bit_Set_Type:
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ r.underlying = clone_type(r.underlying, allocator, unique_strings)
+ case ^Map_Type:
+ r.key = clone_type(r.key, allocator, unique_strings)
+ r.value = clone_type(r.value, allocator, unique_strings)
+ case ^Call_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ r.args = clone_type(r.args, allocator, unique_strings)
+ case ^Typeid_Type:
+ r.specialization = clone_type(r.specialization, allocator, unique_strings)
+ case ^Ternary_When_Expr:
+ r.x = clone_type(r.x, allocator, unique_strings)
+ r.cond = clone_type(r.cond, allocator, unique_strings)
+ r.y = clone_type(r.y, allocator, unique_strings)
+ case ^Poly_Type:
+ r.type = auto_cast clone_type(r.type, allocator, unique_strings)
+ r.specialization = clone_type(r.specialization, allocator, unique_strings)
+ case ^Proc_Group:
+ r.args = clone_type(r.args, allocator, unique_strings)
+ case ^Comp_Lit:
+ r.type = clone_type(r.type, allocator, unique_strings)
+ r.elems = clone_type(r.elems, allocator, unique_strings)
+ case ^Proc_Lit:
+ 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.type = clone_type(r.type, allocator, unique_strings)
+ case ^Type_Cast:
+ r.type = clone_type(r.type, allocator, unique_strings)
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Deref_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ case ^Index_Expr:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ r.index = clone_type(r.index, allocator, unique_strings)
+ case ^Multi_Pointer_Type:
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ case ^Matrix_Type:
+ r.elem = clone_type(r.elem, allocator, unique_strings)
+ case ^Type_Assertion:
+ r.expr = clone_type(r.expr, allocator, unique_strings)
+ r.type = clone_type(r.type, allocator, unique_strings)
+ case:
+ //fmt.logf("Unhandled node kind: %T", r)
+ }
+
+ return res
+}
diff --git a/src/server/collector.odin b/src/server/collector.odin
new file mode 100644
index 0000000..e049c66
--- /dev/null
+++ b/src/server/collector.odin
@@ -0,0 +1,602 @@
+package server
+
+import "core:odin/ast"
+import "core:hash"
+import "core:strings"
+import "core:mem"
+import "core:fmt"
+import "core:path/filepath"
+import path "core:path/slashpath"
+import "core:log"
+import "core:strconv"
+
+import "shared:common"
+
+SymbolCollection :: struct {
+ allocator: mem.Allocator,
+ config: ^common.Config,
+ packages: map[string]map[string]Symbol,
+ references: map[string]map[string]Reference,
+ unique_strings: map[string]string, //store all our strings as unique strings and reference them to save memory.
+}
+
+get_index_unique_string :: proc {
+ get_index_unique_string_collection,
+ get_index_unique_string_collection_raw,
+}
+
+get_index_unique_string_collection :: proc(collection: ^SymbolCollection, s: string) -> string {
+ return get_index_unique_string_collection_raw(&collection.unique_strings, collection.allocator, s)
+}
+
+get_index_unique_string_collection_raw :: proc(unique_strings: ^map[string]string, allocator: mem.Allocator, s: string) -> string {
+ if _, ok := unique_strings[s]; !ok {
+ str := strings.clone(s, allocator)
+ unique_strings[str] = str
+ }
+
+ return unique_strings[s]
+}
+
+make_symbol_collection :: proc(allocator := context.allocator, config: ^common.Config) -> SymbolCollection {
+ return SymbolCollection {
+ allocator = allocator,
+ config = config,
+ packages = make(map[string]map[string]Symbol, 16, allocator),
+ unique_strings = make(map[string]string, 16, allocator),
+ }
+}
+
+delete_symbol_collection :: proc(collection: SymbolCollection) {
+ for k, v in collection.packages {
+ for k2, v2 in v {
+ free_symbol(v2, collection.allocator)
+ }
+ }
+
+ for k, v in collection.unique_strings {
+ delete(v, collection.allocator)
+ }
+
+ for k, v in collection.packages {
+ delete(v)
+ }
+
+ delete(collection.packages)
+ delete(collection.unique_strings)
+}
+
+collect_procedure_fields :: proc(collection: ^SymbolCollection, proc_type: ^ast.Proc_Type, arg_list: ^ast.Field_List, return_list: ^ast.Field_List, package_map: map[string]string) -> SymbolProcedureValue {
+ returns := make([dynamic]^ast.Field, 0, collection.allocator)
+ args := make([dynamic]^ast.Field, 0, collection.allocator)
+
+ if return_list != nil {
+ for ret in return_list.list {
+ cloned := cast(^ast.Field)clone_type(ret, collection.allocator, &collection.unique_strings)
+ replace_package_alias(cloned, package_map, collection)
+ append(&returns, cloned)
+ }
+ }
+
+ if arg_list != nil {
+ for arg in arg_list.list {
+ cloned := cast(^ast.Field)clone_type(arg, collection.allocator, &collection.unique_strings)
+ replace_package_alias(cloned, package_map, collection)
+ append(&args, cloned)
+ }
+ }
+
+ value := SymbolProcedureValue {
+ return_types = returns[:],
+ arg_types = args[:],
+ generic = proc_type.generic,
+ }
+ return value
+}
+
+collect_struct_fields :: proc(collection: ^SymbolCollection, struct_type: ast.Struct_Type, package_map: map[string]string) -> SymbolStructValue {
+ names := make([dynamic]string, 0, collection.allocator)
+ types := make([dynamic]^ast.Expr, 0, collection.allocator)
+ usings := make(map[string]bool, 0, collection.allocator)
+
+ for field in struct_type.fields.list {
+ for n in field.names {
+ ident := n.derived.(^ast.Ident)
+ append(&names, get_index_unique_string(collection, ident.name))
+
+ cloned := clone_type(field.type, collection.allocator, &collection.unique_strings)
+ replace_package_alias(cloned, package_map, collection)
+ append(&types, cloned)
+
+ if .Using in field.flags {
+ usings[names[len(names) - 1]] = true
+ }
+ }
+ }
+
+ value := SymbolStructValue {
+ names = names[:],
+ types = types[:],
+ usings = usings,
+ poly = cast(^ast.Field_List)clone_type(struct_type.poly_params, collection.allocator, &collection.unique_strings),
+ }
+
+ return value
+}
+
+collect_enum_fields :: proc(collection: ^SymbolCollection, fields: []^ast.Expr, package_map: map[string]string) -> SymbolEnumValue {
+ names := make([dynamic]string, 0, collection.allocator)
+
+ //ERROR no hover on n in the for, but elsewhere is fine
+ for n in fields {
+ if ident, ok := n.derived.(^ast.Ident); ok {
+ append(&names, get_index_unique_string(collection, ident.name))
+ } else if field, ok := n.derived.(^ast.Field_Value); ok {
+ if ident, ok := field.field.derived.(^ast.Ident); ok {
+ append(&names, get_index_unique_string(collection, ident.name))
+ } else if binary, ok := field.field.derived.(^ast.Binary_Expr); ok {
+ append(&names, get_index_unique_string(collection, binary.left.derived.(^ast.Ident).name))
+ }
+ }
+ }
+
+ value := SymbolEnumValue {
+ names = names[:],
+ }
+
+ return value
+}
+
+collect_union_fields :: proc(collection: ^SymbolCollection, union_type: ast.Union_Type, package_map: map[string]string) -> SymbolUnionValue {
+ types := make([dynamic]^ast.Expr, 0, collection.allocator)
+
+ for variant in union_type.variants {
+ cloned := clone_type(variant, collection.allocator, &collection.unique_strings)
+ replace_package_alias(cloned, package_map, collection)
+ append(&types, cloned)
+ }
+
+ value := SymbolUnionValue {
+ types = types[:],
+ poly = cast(^ast.Field_List)clone_type(union_type.poly_params, collection.allocator, &collection.unique_strings),
+ }
+
+ return value
+}
+
+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)
+
+ return SymbolBitSetValue {
+ expr = cloned,
+ }
+}
+
+collect_slice :: proc(collection: ^SymbolCollection, array: ast.Array_Type, package_map: map[string]string) -> SymbolFixedArrayValue {
+ elem := clone_type(array.elem, collection.allocator, &collection.unique_strings)
+ len := clone_type(array.len, collection.allocator, &collection.unique_strings)
+
+ replace_package_alias(elem, package_map, collection)
+ replace_package_alias(len, package_map, collection)
+
+ return SymbolFixedArrayValue {
+ expr = elem,
+ len = len,
+ }
+}
+
+collect_array :: proc(collection: ^SymbolCollection, array: ast.Array_Type, package_map: map[string]string) -> SymbolSliceValue {
+ elem := clone_type(array.elem, collection.allocator, &collection.unique_strings)
+
+ replace_package_alias(elem, package_map, collection)
+
+ return SymbolSliceValue {
+ expr = elem,
+ }
+}
+
+collect_map :: proc(collection: ^SymbolCollection, m: ast.Map_Type, package_map: map[string]string) -> SymbolMapValue {
+ key := clone_type(m.key, collection.allocator, &collection.unique_strings)
+ value := clone_type(m.value, collection.allocator, &collection.unique_strings)
+
+ replace_package_alias(key, package_map, collection)
+ replace_package_alias(value, package_map, collection)
+
+ return SymbolMapValue {
+ key = key,
+ value = value,
+ }
+}
+
+collect_dynamic_array :: proc(collection: ^SymbolCollection, array: ast.Dynamic_Array_Type, package_map: map[string]string) -> SymbolDynamicArrayValue {
+ elem := clone_type(array.elem, collection.allocator, &collection.unique_strings)
+
+ replace_package_alias(elem, package_map, collection)
+
+ return SymbolDynamicArrayValue {
+ expr = elem,
+ }
+}
+
+collect_generic :: proc(collection: ^SymbolCollection, expr: ^ast.Expr, package_map: map[string]string, uri: string) -> SymbolGenericValue {
+ //Bit hacky right now, but it's hopefully a temporary solution.
+ //In the c package code it uses a documentation package(builtin).
+ if selector, ok := expr.derived.(^ast.Selector_Expr); ok {
+ if ident, ok := selector.expr.derived.(^ast.Ident); ok {
+ if ident.name == "builtin" && strings.contains(uri, "Odin/core/c/c.odin") {
+ cloned := clone_type(selector.field, collection.allocator, &collection.unique_strings)
+ replace_package_alias(cloned, package_map, collection)
+ value := SymbolGenericValue {
+ expr = cloned,
+ }
+ return value
+ }
+ }
+ }
+
+ cloned := clone_type(expr, collection.allocator, &collection.unique_strings)
+ replace_package_alias(cloned, package_map, collection)
+
+ value := SymbolGenericValue {
+ expr = cloned,
+ }
+
+ return value
+}
+
+collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: string) -> common.Error {
+ 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)
+ } else {
+ directory := path.dir(forward, context.temp_allocator)
+ }
+
+ package_map := get_package_mapping(file, collection.config, directory)
+
+ exprs := common.collect_globals(file, true)
+
+ for expr in exprs {
+ symbol: Symbol
+
+ token: ast.Node
+ token_type: SymbolType
+
+ name := expr.name
+
+ col_expr := expr.expr
+
+ if helper, ok := col_expr.derived.(^ast.Helper_Type); ok {
+ if helper.type != nil {
+ col_expr = helper.type
+ }
+ }
+
+ if dist, ok := col_expr.derived.(^ast.Distinct_Type); ok {
+ if dist.type != nil {
+ col_expr = dist.type
+ }
+ }
+
+ #partial switch v in col_expr.derived {
+ case ^ast.Proc_Lit:
+ token = v^
+ token_type = .Function
+
+ if v.type != nil {
+ symbol.value = collect_procedure_fields(collection, v.type, v.type.params, v.type.results, package_map)
+ }
+ case ^ast.Proc_Type:
+ token = v^
+ token_type = .Function
+ symbol.value = collect_procedure_fields(collection, cast(^ast.Proc_Type)col_expr, v.params, v.results, package_map)
+ case ^ast.Proc_Group:
+ token = v^
+ token_type = .Function
+ symbol.value = SymbolProcedureGroupValue {
+ group = clone_type(col_expr, collection.allocator, &collection.unique_strings),
+ }
+ case ^ast.Struct_Type:
+ token = v^
+ token_type = .Struct
+ symbol.value = collect_struct_fields(collection, v^, package_map)
+ symbol.signature = "struct"
+ case ^ast.Enum_Type:
+ token = v^
+ token_type = .Enum
+ symbol.value = collect_enum_fields(collection, v.fields, package_map)
+ symbol.signature = "enum"
+ case ^ast.Union_Type:
+ token = v^
+ token_type = .Union
+ symbol.value = collect_union_fields(collection, v^, package_map)
+ symbol.signature = "union"
+ case ^ast.Bit_Set_Type:
+ token = v^
+ token_type = .Enum
+ symbol.value = collect_bitset_field(collection, v^, package_map)
+ symbol.signature = "bitset"
+ case ^ast.Map_Type:
+ token = v^
+ token_type = .Variable
+ symbol.value = collect_map(collection, v^, package_map)
+ case ^ast.Array_Type:
+ token = v^
+ token_type = .Variable
+ if v.len == nil {
+ symbol.value = collect_slice(collection, v^, package_map)
+ } else {
+ symbol.value = collect_array(collection, v^, package_map)
+ }
+ case ^ast.Dynamic_Array_Type:
+ token = v^
+ token_type = .Variable
+ symbol.value = collect_dynamic_array(collection, v^, package_map)
+ case ^ast.Basic_Lit:
+ token = v^
+ symbol.value = collect_generic(collection, col_expr, package_map, uri)
+ if expr.mutable {
+ token_type = .Variable
+ } else {
+ token_type = .Constant
+ }
+ case ^ast.Ident:
+ token = v^
+ symbol.value = collect_generic(collection, col_expr, package_map, uri)
+ if expr.mutable {
+ token_type = .Variable
+ } else {
+ token_type = .Unresolved
+ }
+ case: // default
+ symbol.value = collect_generic(collection, col_expr, package_map, uri)
+ if expr.mutable {
+ token_type = .Variable
+ } else {
+ token_type = .Unresolved
+ }
+ token = expr.expr
+ }
+
+ symbol.range = common.get_token_range(token, file.src)
+ symbol.name = get_index_unique_string(collection, name)
+ symbol.pkg = get_index_unique_string(collection, directory)
+ symbol.type = token_type
+ symbol.doc = common.get_doc(expr.docs, collection.allocator)
+
+ if expr.builtin {
+ symbol.pkg = "$builtin"
+ } else {
+ symbol.pkg = get_index_unique_string(collection, directory)
+ }
+
+ if expr.deprecated {
+ symbol.flags |= {.Deprecated}
+ }
+
+ if expr.file_private {
+ symbol.flags |= {.PrivateFile}
+ }
+
+ if expr.package_private {
+ symbol.flags |= {.PrivatePackage}
+ }
+
+ when ODIN_OS == .Windows {
+ symbol.uri = get_index_unique_string(collection, strings.to_lower(uri, context.temp_allocator))
+ } else {
+ symbol.uri = get_index_unique_string(collection, uri)
+ }
+
+ pkg: ^map[string]Symbol
+ ok: bool
+
+ if pkg, ok = &collection.packages[symbol.pkg]; !ok {
+ collection.packages[symbol.pkg] = make(map[string]Symbol, 100, collection.allocator)
+ pkg = &collection.packages[symbol.pkg]
+ }
+
+ if v, ok := pkg[symbol.name]; !ok || v.name == "" {
+ pkg[symbol.name] = symbol
+ } else {
+ free_symbol(symbol, collection.allocator)
+ }
+ }
+
+ return .None
+}
+
+/*
+ Gets the map from import alias to absolute package directory
+*/
+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)
+
+ for imp, index in file.imports {
+ //collection specified
+ if i := strings.index(imp.fullpath, ":"); i != -1 {
+ collection := imp.fullpath[1:i]
+ p := imp.fullpath[i + 1:len(imp.fullpath) - 1]
+
+ dir, ok := config.collections[collection]
+
+ if !ok {
+ continue
+ }
+
+ name: string
+
+ when ODIN_OS == .Windows {
+ full := path.join(elems = {strings.to_lower(dir, context.temp_allocator), p}, allocator = context.temp_allocator)
+ } else {
+ full := path.join(elems = {dir, p}, allocator = context.temp_allocator)
+ }
+
+ if imp.name.text != "" {
+ name = imp.name.text
+ } else {
+ name = path.base(full, false, context.temp_allocator)
+ }
+
+ when ODIN_OS == .Windows {
+ package_map[name] = strings.to_lower(full, context.temp_allocator)
+ } else {
+ package_map[name] = full
+ }
+ } else {
+ name: string
+
+ 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
+ } else {
+ name = path.base(full, false, context.temp_allocator)
+ }
+
+ when ODIN_OS == .Windows {
+ package_map[name] = strings.to_lower(full, context.temp_allocator)
+ } else {
+ package_map[name] = full
+ }
+ }
+ }
+
+ return package_map
+}
+
+/*
+ We can't have the alias names for packages with selector expression since that is specific to every files import, instead just replace it with the absolute
+ package name(absolute directory path)
+*/
+
+replace_package_alias :: proc {
+ replace_package_alias_node,
+ replace_package_alias_expr,
+ replace_package_alias_array,
+ replace_package_alias_dynamic_array,
+}
+
+replace_package_alias_array :: proc(array: $A/[]^$T, package_map: map[string]string, collection: ^SymbolCollection) {
+ for elem, i in array {
+ replace_package_alias(elem, package_map, collection)
+ }
+}
+
+replace_package_alias_dynamic_array :: proc(array: $A/[dynamic]^$T, package_map: map[string]string, collection: ^SymbolCollection) {
+ for elem, i in array {
+ replace_package_alias(elem, package_map, collection)
+ }
+}
+
+replace_package_alias_expr :: proc(node: ^ast.Expr, package_map: map[string]string, collection: ^SymbolCollection) {
+ replace_package_alias_node(node, package_map, collection)
+}
+
+replace_package_alias_node :: proc(node: ^ast.Node, package_map: map[string]string, collection: ^SymbolCollection) {
+ using ast
+
+ if node == nil {
+ return
+ }
+
+ #partial switch n in node.derived {
+ case ^Bad_Expr:
+ case ^Ident:
+ case ^Implicit:
+ case ^Undef:
+ case ^Basic_Lit:
+ case ^Basic_Directive:
+ case ^Ellipsis:
+ replace_package_alias(n.expr, package_map, collection)
+ case ^Tag_Expr:
+ replace_package_alias(n.expr, package_map, collection)
+ case ^Unary_Expr:
+ replace_package_alias(n.expr, package_map, collection)
+ case ^Binary_Expr:
+ replace_package_alias(n.left, package_map, collection)
+ replace_package_alias(n.right, package_map, collection)
+ case ^Paren_Expr:
+ replace_package_alias(n.expr, package_map, collection)
+ case ^Selector_Expr:
+ if _, ok := n.expr.derived.(^Ident); ok {
+ ident := n.expr.derived.(^Ident)
+
+ if package_name, ok := package_map[ident.name]; ok {
+ ident.name = get_index_unique_string(collection, package_name)
+ }
+ } else {
+ replace_package_alias(n.expr, package_map, collection)
+ replace_package_alias(n.field, package_map, collection)
+ }
+ case ^Implicit_Selector_Expr:
+ replace_package_alias(n.field, package_map, collection)
+ case ^Slice_Expr:
+ replace_package_alias(n.expr, package_map, collection)
+ replace_package_alias(n.low, package_map, collection)
+ replace_package_alias(n.high, package_map, collection)
+ case ^Attribute:
+ replace_package_alias(n.elems, package_map, collection)
+ case ^Distinct_Type:
+ replace_package_alias(n.type, package_map, collection)
+ case ^Proc_Type:
+ replace_package_alias(n.params, package_map, collection)
+ replace_package_alias(n.results, package_map, collection)
+ case ^Pointer_Type:
+ replace_package_alias(n.elem, package_map, collection)
+ case ^Array_Type:
+ replace_package_alias(n.len, package_map, collection)
+ replace_package_alias(n.elem, package_map, collection)
+ case ^Dynamic_Array_Type:
+ replace_package_alias(n.elem, package_map, collection)
+ case ^Struct_Type:
+ replace_package_alias(n.poly_params, package_map, collection)
+ replace_package_alias(n.align, package_map, collection)
+ replace_package_alias(n.fields, package_map, collection)
+ case ^Field:
+ replace_package_alias(n.names, package_map, collection)
+ replace_package_alias(n.type, package_map, collection)
+ replace_package_alias(n.default_value, package_map, collection)
+ case ^Field_List:
+ replace_package_alias(n.list, package_map, collection)
+ case ^Field_Value:
+ replace_package_alias(n.field, package_map, collection)
+ replace_package_alias(n.value, package_map, collection)
+ case ^Union_Type:
+ replace_package_alias(n.poly_params, package_map, collection)
+ replace_package_alias(n.align, package_map, collection)
+ replace_package_alias(n.variants, package_map, collection)
+ case ^Enum_Type:
+ replace_package_alias(n.base_type, package_map, collection)
+ replace_package_alias(n.fields, package_map, collection)
+ case ^Bit_Set_Type:
+ replace_package_alias(n.elem, package_map, collection)
+ replace_package_alias(n.underlying, package_map, collection)
+ case ^Map_Type:
+ replace_package_alias(n.key, package_map, collection)
+ replace_package_alias(n.value, package_map, collection)
+ case ^Call_Expr:
+ replace_package_alias(n.expr, package_map, collection)
+ replace_package_alias(n.args, package_map, collection)
+ case ^Typeid_Type:
+ replace_package_alias(n.specialization, package_map, collection)
+ case ^Poly_Type:
+ replace_package_alias(n.type, package_map, collection)
+ replace_package_alias(n.specialization, package_map, collection)
+ case ^Proc_Group:
+ replace_package_alias(n.args, package_map, collection)
+ 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 ^Multi_Pointer_Type:
+ replace_package_alias(n.elem, package_map, collection)
+ case:
+ log.warnf("Replace Unhandled node kind: %T", n)
+ }
+}
diff --git a/src/server/completion.odin b/src/server/completion.odin
index 02ed78f..40018b1 100644
--- a/src/server/completion.odin
+++ b/src/server/completion.odin
@@ -16,8 +16,6 @@ import "core:os"
import "shared:common"
-import "shared:index"
-import "shared:analysis"
/*
TODOS: Making the signature details is really annoying and not that nice - try to see if this can be refractored.
@@ -35,7 +33,6 @@ Completion_Type :: enum {
}
get_completion_list :: proc(document: ^common.Document, position: common.Position, completion_context: CompletionContext) -> (CompletionList, bool) {
- using analysis
list: CompletionList
@@ -90,8 +87,7 @@ get_completion_list :: proc(document: ^common.Document, position: common.Positio
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 {
+ if union_value, ok := symbol.value.(SymbolUnionValue); ok {
completion_type = .Switch_Type
}
}
@@ -118,11 +114,11 @@ get_completion_list :: proc(document: ^common.Document, position: common.Positio
return list, true
}
-get_attribute_completion :: proc(ast_context: ^analysis.AstContext, position_context: ^analysis.DocumentPositionContext, list: ^CompletionList) {
+get_attribute_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) {
}
-get_directive_completion :: proc(ast_context: ^analysis.AstContext, position_context: ^analysis.DocumentPositionContext, list: ^CompletionList) {
+get_directive_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) {
list.isIncomplete = false
@@ -167,8 +163,7 @@ get_directive_completion :: proc(ast_context: ^analysis.AstContext, position_con
list.items = items[:]
}
-get_comp_lit_completion :: proc(ast_context: ^analysis.AstContext, position_context: ^analysis.DocumentPositionContext, list: ^CompletionList) {
- using analysis
+get_comp_lit_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) {
items := make([dynamic]CompletionItem, context.temp_allocator)
@@ -180,7 +175,7 @@ get_comp_lit_completion :: proc(ast_context: ^analysis.AstContext, position_cont
if comp_symbol, _, ok := resolve_type_comp_literal(ast_context, position_context, symbol, position_context.parent_comp_lit); ok {
ast_context.current_package = comp_symbol.pkg;
#partial switch v in comp_symbol.value {
- case index.SymbolStructValue:
+ case SymbolStructValue:
for name, i in v.names {
ast_context.current_package = comp_symbol.pkg
@@ -206,14 +201,13 @@ get_comp_lit_completion :: proc(ast_context: ^analysis.AstContext, position_cont
list.items = items[:]
}
-get_selector_completion :: proc(ast_context: ^analysis.AstContext, position_context: ^analysis.DocumentPositionContext, list: ^CompletionList) {
- using analysis
+get_selector_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) {
items := make([dynamic]CompletionItem, context.temp_allocator)
ast_context.current_package = ast_context.document_package
- selector: index.Symbol
+ selector: Symbol
ok: bool
ast_context.use_locals = true
@@ -244,7 +238,7 @@ get_selector_completion :: proc(ast_context: ^analysis.AstContext, position_cont
}
}
- if s, ok := selector.value.(index.SymbolProcedureValue); ok {
+ if s, ok := selector.value.(SymbolProcedureValue); ok {
if len(s.return_types) == 1 {
if selector, ok = resolve_type_expression(ast_context, s.return_types[0].type); !ok {
return
@@ -253,7 +247,7 @@ get_selector_completion :: proc(ast_context: ^analysis.AstContext, position_cont
}
#partial switch v in selector.value {
- case index.SymbolFixedArrayValue:
+ case SymbolFixedArrayValue:
list.isIncomplete = true
containsColor := 1
@@ -348,7 +342,7 @@ get_selector_completion :: proc(ast_context: ^analysis.AstContext, position_cont
append(&items, item)
}
}
- case index.SymbolUnionValue:
+ case SymbolUnionValue:
list.isIncomplete = false
append_magic_union_completion(position_context, selector, &items)
@@ -373,7 +367,7 @@ get_selector_completion :: proc(ast_context: ^analysis.AstContext, position_cont
}
}
- case index.SymbolEnumValue:
+ case SymbolEnumValue:
list.isIncomplete = false
for name in v.names {
@@ -386,7 +380,7 @@ get_selector_completion :: proc(ast_context: ^analysis.AstContext, position_cont
append(&items, item)
}
- case index.SymbolStructValue:
+ case SymbolStructValue:
list.isIncomplete = false
for name, i in v.names {
@@ -428,10 +422,10 @@ get_selector_completion :: proc(ast_context: ^analysis.AstContext, position_cont
}
}
- case index.SymbolPackageValue:
+ case SymbolPackageValue:
list.isIncomplete = true
- if searched, ok := index.fuzzy_search(field, {selector.pkg}); ok {
+ if searched, ok := fuzzy_search(field, {selector.pkg}); ok {
for search in searched {
symbol := search.symbol
@@ -458,10 +452,10 @@ get_selector_completion :: proc(ast_context: ^analysis.AstContext, position_cont
log.errorf("Failed to fuzzy search, field: %v, package: %v", field, selector.pkg)
return
}
- case index.SymbolDynamicArrayValue:
+ case SymbolDynamicArrayValue:
list.isIncomplete = false
append_magic_dynamic_array_completion(position_context, selector, &items)
- case index.SymbolMapValue:
+ case SymbolMapValue:
list.isIncomplete = false
append_magic_map_completion(position_context, selector, &items)
}
@@ -469,14 +463,13 @@ get_selector_completion :: proc(ast_context: ^analysis.AstContext, position_cont
list.items = items[:]
}
-get_implicit_completion :: proc(ast_context: ^analysis.AstContext, position_context: ^analysis.DocumentPositionContext, list: ^CompletionList) {
- using analysis
+get_implicit_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) {
items := make([dynamic]CompletionItem, context.temp_allocator)
list.isIncomplete = false
- selector: index.Symbol
+ selector: Symbol
ast_context.use_locals = true
ast_context.use_globals = true
@@ -586,7 +579,7 @@ get_implicit_completion :: proc(ast_context: ^analysis.AstContext, position_cont
if symbol, ok := resolve_type_expression(ast_context, position_context.parent_comp_lit.type); ok {
if comp_symbol, comp_lit, ok := resolve_type_comp_literal(ast_context, position_context, symbol, position_context.parent_comp_lit); ok {
- if s, ok := comp_symbol.value.(index.SymbolStructValue); ok {
+ if s, ok := comp_symbol.value.(SymbolStructValue); ok {
ast_context.current_package = comp_symbol.pkg;
//We can either have the final
@@ -686,7 +679,7 @@ get_implicit_completion :: proc(ast_context: ^analysis.AstContext, position_cont
} else {
//procedures are the only types that can return more than one value
if symbol, ok := resolve_type_expression(ast_context, elem); ok {
- if procedure, ok := symbol.value.(index.SymbolProcedureValue); ok {
+ if procedure, ok := symbol.value.(SymbolProcedureValue); ok {
if procedure.return_types == nil {
return
}
@@ -761,7 +754,7 @@ get_implicit_completion :: proc(ast_context: ^analysis.AstContext, position_cont
if call, ok := position_context.call.derived.(^ast.Call_Expr); ok {
parameter_index, parameter_ok := find_position_in_call_param(ast_context, call^)
if symbol, ok := resolve_type_expression(ast_context, call.expr); ok && parameter_ok {
- if proc_value, ok := symbol.value.(index.SymbolProcedureValue); ok {
+ if proc_value, ok := symbol.value.(SymbolProcedureValue); ok {
if len(proc_value.arg_types) <= parameter_index {
return
}
@@ -786,8 +779,7 @@ get_implicit_completion :: proc(ast_context: ^analysis.AstContext, position_cont
}
}
-get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_context: ^analysis.DocumentPositionContext, list: ^CompletionList) {
- using analysis
+get_identifier_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) {
items := make([dynamic]CompletionItem, context.temp_allocator)
@@ -797,11 +789,11 @@ get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_co
score: f32,
snippet: Snippet_Info,
name: string,
- type: index.SymbolType,
+ type: SymbolType,
doc: string,
pkg: string,
signature: string,
- flags: index.SymbolFlags,
+ flags: SymbolFlags,
}
combined_sort_interface :: proc(s: ^[dynamic]CombinedResult) -> sort.Interface {
@@ -824,11 +816,11 @@ get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_co
combined := make([dynamic]CombinedResult)
- lookup := ""
+ lookup_name := ""
if position_context.identifier != nil {
if ident, ok := position_context.identifier.derived.(^ast.Ident); ok {
- lookup = ident.name
+ lookup_name = ident.name
}
}
@@ -843,7 +835,7 @@ get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_co
append(&pkgs, ast_context.document_package)
append(&pkgs, "$builtin")
- if results, ok := index.fuzzy_search(lookup, pkgs[:]); ok {
+ if results, ok := fuzzy_search(lookup_name, pkgs[:]); ok {
for r in results {
r := r
resolve_unresolved_symbol(ast_context, &r.symbol)
@@ -862,7 +854,7 @@ get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_co
}
}
- matcher := common.make_fuzzy_matcher(lookup)
+ matcher := common.make_fuzzy_matcher(lookup_name)
global: for k, v in ast_context.globals {
if position_context.global_lhs_stmt {
@@ -880,7 +872,7 @@ get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_co
ast_context.use_globals = true
ast_context.current_package = ast_context.document_package
- ident := index.new_type(ast.Ident, v.expr.pos, v.expr.end, context.temp_allocator)
+ ident := new_type(ast.Ident, v.expr.pos, v.expr.end, context.temp_allocator)
ident.name = k
if symbol, ok := resolve_type_identifier(ast_context, ident^); ok {
@@ -914,7 +906,7 @@ get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_co
ast_context.use_globals = true
ast_context.current_package = ast_context.document_package
- ident := index.new_type(ast.Ident, {offset = local_offset}, {offset = local_offset}, context.temp_allocator)
+ ident := new_type(ast.Ident, {offset = local_offset}, {offset = local_offset}, context.temp_allocator)
ident.name = k
if symbol, ok := resolve_type_identifier(ast_context, ident^); ok {
@@ -942,7 +934,7 @@ get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_co
break
}
- symbol := index.Symbol {
+ symbol := Symbol {
name = pkg.base,
type = .Package,
}
@@ -961,7 +953,7 @@ get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_co
}
for keyword, _ in common.keyword_map {
- symbol := index.Symbol {
+ symbol := Symbol {
name = keyword,
type = .Keyword,
}
@@ -980,7 +972,7 @@ get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_co
}
for keyword, _ in language_keywords {
- symbol := index.Symbol {
+ symbol := Symbol {
name = keyword,
type = .Keyword,
}
@@ -1064,7 +1056,7 @@ get_identifier_completion :: proc(ast_context: ^analysis.AstContext, position_co
list.items = items[:]
}
-get_package_completion :: proc(ast_context: ^analysis.AstContext, position_context: ^analysis.DocumentPositionContext, list: ^CompletionList) {
+get_package_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) {
items := make([dynamic]CompletionItem, context.temp_allocator)
@@ -1146,8 +1138,7 @@ search_for_packages :: proc(fullpath: string) -> [] string {
return packages[:]
}
-get_type_switch_completion :: proc(ast_context: ^analysis.AstContext, position_context: ^analysis.DocumentPositionContext, list: ^CompletionList) {
- using analysis
+get_type_switch_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) {
items := make([dynamic]CompletionItem, context.temp_allocator)
list.isIncomplete = false
@@ -1197,7 +1188,7 @@ get_type_switch_completion :: proc(ast_context: ^analysis.AstContext, position_c
list.items = items[:]
}
-get_core_insert_package_if_non_existent :: proc(ast_context: ^analysis.AstContext, pkg: string) -> (TextEdit, bool) {
+get_core_insert_package_if_non_existent :: proc(ast_context: ^AstContext, pkg: string) -> (TextEdit, bool) {
builder := strings.make_builder(context.temp_allocator)
for imp in ast_context.imports {
@@ -1223,7 +1214,7 @@ get_core_insert_package_if_non_existent :: proc(ast_context: ^analysis.AstContex
}, true
}
-get_range_from_selection_start_to_dot :: proc(position_context: ^analysis.DocumentPositionContext) -> (common.Range, bool) {
+get_range_from_selection_start_to_dot :: proc(position_context: ^DocumentPositionContext) -> (common.Range, bool) {
if position_context.selector != nil {
range := common.get_token_range(position_context.selector, position_context.file.src)
range.end.character += 1
@@ -1233,7 +1224,7 @@ get_range_from_selection_start_to_dot :: proc(position_context: ^analysis.Docume
return {}, false
}
-append_magic_map_completion :: proc(position_context: ^analysis.DocumentPositionContext, symbol: index.Symbol, items: ^[dynamic]CompletionItem) {
+append_magic_map_completion :: proc(position_context: ^DocumentPositionContext, symbol: Symbol, items: ^[dynamic]CompletionItem) {
range, ok := get_range_from_selection_start_to_dot(position_context)
if !ok {
@@ -1277,7 +1268,7 @@ append_magic_map_completion :: proc(position_context: ^analysis.DocumentPosition
}
-append_magic_dynamic_array_completion :: proc(position_context: ^analysis.DocumentPositionContext, symbol: index.Symbol, items: ^[dynamic]CompletionItem) {
+append_magic_dynamic_array_completion :: proc(position_context: ^DocumentPositionContext, symbol: Symbol, items: ^[dynamic]CompletionItem) {
range, ok := get_range_from_selection_start_to_dot(position_context)
if !ok {
@@ -1341,7 +1332,7 @@ append_magic_dynamic_array_completion :: proc(position_context: ^analysis.Docume
}
-append_magic_union_completion :: proc(position_context: ^analysis.DocumentPositionContext, symbol: index.Symbol, items: ^[dynamic]CompletionItem) {
+append_magic_union_completion :: proc(position_context: ^DocumentPositionContext, symbol: Symbol, items: ^[dynamic]CompletionItem) {
range, ok := get_range_from_selection_start_to_dot(position_context)
if !ok {
diff --git a/src/server/definition.odin b/src/server/definition.odin
index cfa2c49..4447bb2 100644
--- a/src/server/definition.odin
+++ b/src/server/definition.odin
@@ -14,14 +14,9 @@ import "core:sort"
import "core:slice"
import "core:os"
-
import "shared:common"
-import "shared:index"
-import "shared:analysis"
get_definition_location :: proc(document: ^common.Document, position: common.Position) -> ([]common.Location, bool) {
- using analysis
-
locations := make([dynamic]common.Location, context.temp_allocator)
location: common.Location
@@ -69,7 +64,7 @@ get_definition_location :: proc(document: ^common.Document, position: common.Pos
//otherwise it's the field the client wants to go to.
- selector: index.Symbol
+ selector: Symbol
ast_context.use_locals = true
ast_context.use_globals = true
@@ -94,16 +89,16 @@ get_definition_location :: proc(document: ^common.Document, position: common.Pos
uri = selector.uri
#partial switch v in selector.value {
- case index.SymbolEnumValue:
+ case SymbolEnumValue:
location.range = selector.range
- case index.SymbolStructValue:
+ case SymbolStructValue:
for name, i in v.names {
if strings.compare(name, field) == 0 {
location.range = common.get_token_range(v.types[i]^, document.ast.src)
}
}
- case index.SymbolPackageValue:
- if symbol, ok := index.lookup(field, selector.pkg); ok {
+ case SymbolPackageValue:
+ if symbol, ok := lookup(field, selector.pkg); ok {
location.range = symbol.range
uri = symbol.uri
} else {
diff --git a/src/server/document_links.odin b/src/server/document_links.odin
index a1514b0..08330ab 100644
--- a/src/server/document_links.odin
+++ b/src/server/document_links.odin
@@ -16,12 +16,8 @@ import "core:os"
import "shared:common"
-import "shared:index"
-import "shared:analysis"
get_document_links :: proc(document: ^common.Document) -> ([]DocumentLink, bool) {
- using analysis
-
links := make([dynamic]DocumentLink, 0, context.temp_allocator)
for imp in document.ast.imports {
diff --git a/src/server/document_symbols.odin b/src/server/document_symbols.odin
index 667b94a..b442bfe 100644
--- a/src/server/document_symbols.odin
+++ b/src/server/document_symbols.odin
@@ -16,13 +16,8 @@ import "core:os"
import "shared:common"
-import "shared:index"
-import "shared:analysis"
-
get_document_symbols :: proc(document: ^common.Document) -> []DocumentSymbol {
- using analysis
-
ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri)
get_globals(document.ast, &ast_context)
diff --git a/src/server/hover.odin b/src/server/hover.odin
index 4286ee8..1fdecd5 100644
--- a/src/server/hover.odin
+++ b/src/server/hover.odin
@@ -14,17 +14,13 @@ import "core:sort"
import "core:slice"
import "shared:common"
-import "shared:index"
-import "shared:analysis"
-
-write_hover_content :: proc(ast_context: ^analysis.AstContext, symbol: index.Symbol) -> MarkupContent {
- using analysis
+write_hover_content :: proc(ast_context: ^AstContext, symbol: Symbol) -> MarkupContent {
content: MarkupContent
symbol := symbol
- if untyped, ok := symbol.value.(index.SymbolUntypedValue); ok {
+ if untyped, ok := symbol.value.(SymbolUntypedValue); ok {
switch untyped.type {
case .String: symbol.signature = "string"
case .Bool: symbol.signature = "bool"
@@ -49,8 +45,6 @@ write_hover_content :: proc(ast_context: ^analysis.AstContext, symbol: index.Sym
get_hover_information :: proc(document: ^common.Document, position: common.Position) -> (Hover, bool) {
- using analysis
-
hover := Hover {
contents = {
kind = "plaintext",
@@ -104,7 +98,7 @@ get_hover_information :: proc(document: ^common.Document, position: common.Posit
}
}
- selector: index.Symbol
+ selector: Symbol
selector, ok = resolve_type_expression(&ast_context, position_context.selector)
if !ok {
@@ -123,7 +117,7 @@ get_hover_information :: proc(document: ^common.Document, position: common.Posit
hover.range = common.get_token_range(position_context.identifier^, document.ast.src)
#partial switch v in selector.value {
- case index.SymbolStructValue:
+ case SymbolStructValue:
for name, i in v.names {
if strings.compare(name, field) == 0 {
if symbol, ok := resolve_type_expression(&ast_context, v.types[i]); ok {
@@ -135,7 +129,7 @@ get_hover_information :: proc(document: ^common.Document, position: common.Posit
}
}
}
- case index.SymbolPackageValue:
+ case SymbolPackageValue:
if position_context.field != nil {
if ident, ok := position_context.field.derived.(^ast.Ident); ok {
ast_context.current_package = selector.pkg
diff --git a/src/server/indexer.odin b/src/server/indexer.odin
new file mode 100644
index 0000000..03b3e75
--- /dev/null
+++ b/src/server/indexer.odin
@@ -0,0 +1,109 @@
+package server
+
+import "core:odin/ast"
+import "core:fmt"
+import "core:strings"
+import "core:log"
+import "core:sort"
+
+/*
+ Concept ideas:
+
+ static indexing:
+
+ is responsible for implementing the indexing of symbols for static files.
+
+ This is to solve the scaling problem of large projects with many files and symbols, as most of these files will be static.
+
+ Possible scopes for static files:
+ global scope (we don't have hiarachy of namespaces and therefore only need to look at the global scope)
+
+ Scopes not part of the indexer:
+ function scope, file scope, package scope(these are only relevant for dynamic active files in your project, that use the ast instead of indexing)
+
+ Potential features:
+ Allow for saving the indexer, instead of recreating it everytime the lsp starts(but you would have to account for stale data).
+
+
+ dynamic indexing:
+
+ When the user modifies files we need some smaller index to handle everything the user is using right now. This will allow
+ us to rebuild parts of the index without too much of a performance hit.
+
+ This index is first searched and if nothing is found look in the static index.
+*/
+
+
+
+Indexer :: struct {
+ builtin_packages: [dynamic]string,
+ static_index: MemoryIndex,
+ dynamic_index: MemoryIndex,
+ dynamic_uri_owned: map[string]bool,
+}
+
+indexer: Indexer
+
+FuzzyResult :: struct {
+ symbol: Symbol,
+ score: f32,
+}
+
+lookup :: proc(name: string, pkg: string, loc := #caller_location) -> (Symbol, bool) {
+ if symbol, ok := memory_index_lookup(&indexer.dynamic_index, name, pkg); ok {
+ log.infof("lookup dynamic name: %v pkg: %v, symbol %v location %v", name, pkg, symbol, loc)
+ return symbol, true
+ }
+
+ if symbol, ok := memory_index_lookup(&indexer.static_index, name, pkg); ok && symbol.uri not_in indexer.dynamic_uri_owned {
+ log.infof("lookup name: %v pkg: %v, symbol %v location %v", name, pkg, symbol, loc)
+ return symbol, true
+ }
+
+ log.infof("lookup failed name: %v pkg: %v location %v", name, pkg, loc)
+ return {}, false
+}
+
+fuzzy_search :: proc(name: string, pkgs: []string) -> ([]FuzzyResult, bool) {
+ dynamic_results, dynamic_ok := memory_index_fuzzy_search(&indexer.dynamic_index, name, pkgs)
+ static_results, static_ok := memory_index_fuzzy_search(&indexer.static_index, name, pkgs)
+ result := make([dynamic]FuzzyResult, context.temp_allocator)
+
+ if !dynamic_ok || !static_ok {
+ return {}, false
+ }
+
+ for r in dynamic_results {
+ append(&result, r)
+ }
+
+ for r in static_results {
+ if r.symbol.uri in indexer.dynamic_uri_owned {
+ continue
+ }
+
+ append(&result, r)
+ }
+
+ sort.sort(fuzzy_sort_interface(&result))
+
+ return result[:], true
+}
+
+fuzzy_sort_interface :: proc(s: ^[dynamic]FuzzyResult) -> sort.Interface {
+ return sort.Interface {
+ collection = rawptr(s),
+ len = proc(it: sort.Interface) -> int {
+ s := (^[dynamic]FuzzyResult)(it.collection)
+ return len(s^)
+ },
+ less = proc(it: sort.Interface, i, j: int) -> bool {
+ s := (^[dynamic]FuzzyResult)(it.collection)
+ return s[i].score > s[j].score
+ },
+ swap = proc(it: sort.Interface, i, j: int) {
+ s := (^[dynamic]FuzzyResult)(it.collection)
+ s[i], s[j] = s[j], s[i]
+ },
+ }
+}
diff --git a/src/server/inlay_hints.odin b/src/server/inlay_hints.odin
index a5be9c2..b8f4024 100644
--- a/src/server/inlay_hints.odin
+++ b/src/server/inlay_hints.odin
@@ -4,13 +4,9 @@ import "core:odin/ast"
import "core:fmt"
import "shared:common"
-import "shared:analysis"
-import "shared:index"
//document
-get_inlay_hints :: proc(document: ^common.Document, symbols: map[uintptr]index.SymbolAndNode) -> ([]InlayHint, bool) {
- using analysis
-
+get_inlay_hints :: proc(document: ^common.Document, symbols: map[uintptr]SymbolAndNode) -> ([]InlayHint, bool) {
hints := make([dynamic]InlayHint, context.temp_allocator)
ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri)
@@ -58,7 +54,7 @@ get_inlay_hints :: proc(document: ^common.Document, symbols: map[uintptr]index.S
}
if symbol_and_node, ok := symbols[cast(uintptr)node_call]; ok {
- if symbol_call, ok := symbol_and_node.symbol.value.(index.SymbolProcedureValue); ok {
+ if symbol_call, ok := symbol_and_node.symbol.value.(SymbolProcedureValue); ok {
for arg in symbol_call.arg_types {
for name in arg.names {
if symbol_arg_count >= len(call.args) {
diff --git a/src/server/lens.odin b/src/server/lens.odin
index 93827f5..abde537 100644
--- a/src/server/lens.odin
+++ b/src/server/lens.odin
@@ -2,7 +2,7 @@ package server
import "core:odin/ast"
-import "shared:analysis"
+
import "shared:common"
@@ -21,9 +21,6 @@ CodeLens :: struct {
}
get_code_lenses :: proc(document: ^common.Document, position: common.Position) -> ([]CodeLens, bool) {
-
- using analysis
-
ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri)
get_globals(document.ast, &ast_context)
diff --git a/src/server/memory_index.odin b/src/server/memory_index.odin
new file mode 100644
index 0000000..03504c1
--- /dev/null
+++ b/src/server/memory_index.odin
@@ -0,0 +1,65 @@
+package server
+
+import "core:hash"
+import "core:strings"
+import "core:fmt"
+import "core:log"
+import "core:sort"
+
+import "shared:common"
+
+/*
+ This is a in memory index designed for the dynamic indexing of symbols and files.
+ Designed for few files and should be fast at rebuilding.
+
+ Right now the implementation is extremely naive.
+*/
+MemoryIndex :: struct {
+ collection: SymbolCollection,
+}
+
+make_memory_index :: proc(collection: SymbolCollection) -> MemoryIndex {
+ return MemoryIndex {
+ collection = collection,
+ }
+}
+
+memory_index_lookup :: proc(index: ^MemoryIndex, name: string, pkg: string) -> (Symbol, bool) {
+ if pkg, ok := &index.collection.packages[pkg]; ok {
+ return pkg[name]
+ }
+
+ return {}, false
+}
+
+memory_index_fuzzy_search :: proc(index: ^MemoryIndex, name: string, pkgs: []string) -> ([]FuzzyResult, bool) {
+ symbols := make([dynamic]FuzzyResult, 0, context.temp_allocator)
+
+ fuzzy_matcher := common.make_fuzzy_matcher(name)
+
+ top := 20
+
+ for pkg in pkgs {
+ if pkg, ok := index.collection.packages[pkg]; ok {
+ for _, symbol in pkg {
+ if score, ok := common.fuzzy_match(fuzzy_matcher, symbol.name); ok == 1 {
+ result := FuzzyResult {
+ symbol = symbol,
+ score = score,
+ }
+
+ append(&symbols, result)
+ }
+ }
+ }
+ }
+
+ sort.sort(fuzzy_sort_interface(&symbols))
+
+ if name == "" {
+ return symbols[:], true
+ } else {
+ return symbols[:min(top, len(symbols))], true
+ }
+}
+
diff --git a/src/server/references.odin b/src/server/references.odin
new file mode 100644
index 0000000..86645b1
--- /dev/null
+++ b/src/server/references.odin
@@ -0,0 +1,41 @@
+package server
+
+
+import "shared:common"
+
+import "core:strings"
+import "core:odin/ast"
+import path "core:path/slashpath"
+
+
+Reference :: struct {
+ identifiers: [dynamic]^ast.Ident,
+ selectors: [dynamic]^ast.Selector_Expr,
+}
+
+collect_references :: proc(collection: ^SymbolCollection, file: ast.File, uri: string) -> common.Error {
+ document := common.Document {
+ ast = file,
+ }
+
+ uri, ok := common.parse_uri(uri, context.temp_allocator)
+
+ if !ok {
+ return .ParseError
+ }
+
+ when ODIN_OS == .Windows {
+ document.package_name = strings.to_lower(path.dir(document.uri.path, context.temp_allocator))
+ } else {
+ document.package_name = path.dir(document.uri.path)
+ }
+
+ document.uri = uri
+
+
+
+ return {}
+
+}
+
+
diff --git a/src/server/rename.odin b/src/server/rename.odin
index b7d39ef..7c4629e 100644
--- a/src/server/rename.odin
+++ b/src/server/rename.odin
@@ -1,14 +1,11 @@
package server
import "shared:common"
-import "shared:analysis"
import "core:log"
import "core:odin/ast"
get_rename :: proc(document: ^common.Document, new_text: string, position: common.Position) -> (WorkspaceEdit, bool) {
- using analysis
-
workspace: WorkspaceEdit
document_changes := make([dynamic]TextDocumentEdit, context.temp_allocator)
diff --git a/src/server/requests.odin b/src/server/requests.odin
index 8ffaa97..fdc5d31 100644
--- a/src/server/requests.odin
+++ b/src/server/requests.odin
@@ -18,7 +18,6 @@ import "core:odin/ast"
import "core:odin/parser"
import "shared:common"
-import "shared:index"
Header :: struct {
content_length: int,
@@ -537,10 +536,10 @@ request_initialize :: proc (params: json.Value, id: RequestId, config: ^common.C
Temp index here, but should be some background thread that starts the indexing
*/
- index.indexer.dynamic_index = index.make_memory_index(index.make_symbol_collection(context.allocator, config))
- index.indexer.dynamic_uri_owned = make(map[string]bool, 200, context.allocator)
+ indexer.dynamic_index = make_memory_index(make_symbol_collection(context.allocator, config))
+ indexer.dynamic_uri_owned = make(map[string]bool, 200, context.allocator)
- index.build_static_index(context.allocator, config)
+ build_static_index(context.allocator, config)
/*
Add runtime package
@@ -548,9 +547,9 @@ request_initialize :: proc (params: json.Value, id: RequestId, config: ^common.C
if core, ok := config.collections["core"]; ok {
when ODIN_OS == .Windows {
- append(&index.indexer.builtin_packages, path.join(strings.to_lower(core, context.temp_allocator), "runtime"))
+ append(&indexer.builtin_packages, path.join(strings.to_lower(core, context.temp_allocator), "runtime"))
} else {
- append(&index.indexer.builtin_packages, path.join(core, "runtime"))
+ append(&indexer.builtin_packages, path.join(core, "runtime"))
}
}
@@ -799,8 +798,8 @@ notification_did_save :: proc (params: json.Value, id: RequestId, config: ^commo
fullpath := uri.path
p := parser.Parser {
- err = index.log_error_handler,
- warn = index.log_warning_handler,
+ err = log_error_handler,
+ warn = log_warning_handler,
flags = {.Optional_Semicolons},
}
@@ -827,20 +826,20 @@ notification_did_save :: proc (params: json.Value, id: RequestId, config: ^commo
log.errorf("error in parse file for indexing %v", fullpath)
}
- for k, v in &index.indexer.dynamic_index.collection.packages {
+ for k, v in &indexer.dynamic_index.collection.packages {
for k2, v2 in &v {
if v2.uri == uri.uri {
- index.free_symbol(v2, context.allocator)
+ free_symbol(v2, context.allocator)
v[k2] = {}
}
}
}
- if ret := index.collect_symbols(&index.indexer.dynamic_index.collection, file, uri.uri); ret != .None {
+ if ret := collect_symbols(&indexer.dynamic_index.collection, file, uri.uri); ret != .None {
log.errorf("failed to collect symbols on save %v", ret)
}
- index.indexer.dynamic_uri_owned[uri.uri] = true
+ indexer.dynamic_uri_owned[uri.uri] = true
check(uri, writer, config)
@@ -878,7 +877,7 @@ request_semantic_token_full :: proc (params: json.Value, id: RequestId, config:
symbols: SemanticTokens
if config.enable_semantic_tokens {
- resolve_entire_file(document)
+ resolve_entire_file_cached(document)
if cache_symbols, ok := file_resolve_cache.files[document.uri.uri]; ok {
symbols = get_semantic_tokens(document, range, cache_symbols)
@@ -1008,7 +1007,7 @@ request_inlay_hint :: proc (params: json.Value, id: RequestId, config: ^common.C
hints: []InlayHint
- resolve_entire_file(document)
+ resolve_entire_file_cached(document)
if cache_symbols, ok := file_resolve_cache.files[document.uri.uri]; ok {
hints, ok = get_inlay_hints(document, cache_symbols)
diff --git a/src/server/semantic_tokens.odin b/src/server/semantic_tokens.odin
index 5ae011d..acbe1a5 100644
--- a/src/server/semantic_tokens.odin
+++ b/src/server/semantic_tokens.odin
@@ -6,8 +6,7 @@ import "core:log"
import "core:fmt"
import "shared:common"
-import "shared:index"
-import "shared:analysis"
+
/*
Right now I might be setting the wrong types, since there is no documentation as to what should be what, and looking at other LSP there is no consistancy.
@@ -78,7 +77,7 @@ SemanticTokens :: struct {
SemanticTokenBuilder :: struct {
current_start: int,
tokens: [dynamic]u32,
- symbols: map[uintptr]index.SymbolAndNode,
+ symbols: map[uintptr]SymbolAndNode,
selector: bool,
}
@@ -94,9 +93,7 @@ get_tokens :: proc(builder: SemanticTokenBuilder) -> SemanticTokens {
}
}
-get_semantic_tokens :: proc(document: ^common.Document, range: common.Range, symbols: map[uintptr]index.SymbolAndNode) -> SemanticTokens {
- using analysis
-
+get_semantic_tokens :: proc(document: ^common.Document, range: common.Range, symbols: map[uintptr]SymbolAndNode) -> SemanticTokens {
builder := make_token_builder()
if document.ast.pkg_decl != nil {
@@ -144,23 +141,23 @@ visit :: proc {
visit_stmt,
}
-visit_array :: proc(array: $A/[]^$T, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) {
+visit_array :: proc(array: $A/[]^$T, builder: ^SemanticTokenBuilder, ast_context: ^AstContext) {
for elem, i in array {
visit(elem, builder, ast_context)
}
}
-visit_dynamic_array :: proc(array: $A/[dynamic]^$T, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) {
+visit_dynamic_array :: proc(array: $A/[dynamic]^$T, builder: ^SemanticTokenBuilder, ast_context: ^AstContext) {
for elem, i in array {
visit(elem, builder, ast_context)
}
}
-visit_stmt :: proc(node: ^ast.Stmt, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) {
+visit_stmt :: proc(node: ^ast.Stmt, builder: ^SemanticTokenBuilder, ast_context: ^AstContext) {
visit_node(node, builder, ast_context)
}
-visit_node :: proc(node: ^ast.Node, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) {
+visit_node :: proc(node: ^ast.Node, builder: ^SemanticTokenBuilder, ast_context: ^AstContext) {
using ast
if node == nil {
@@ -179,21 +176,21 @@ visit_node :: proc(node: ^ast.Node, builder: ^SemanticTokenBuilder, ast_context:
}
#partial switch v in symbol_and_node.symbol.value {
- case index.SymbolPackageValue:
+ case SymbolPackageValue:
write_semantic_node(builder, node, ast_context.file.src, .Namespace, .None)
- case index.SymbolStructValue:
+ case SymbolStructValue:
write_semantic_node(builder, node, ast_context.file.src, .Struct, .None)
- case index.SymbolEnumValue:
+ case SymbolEnumValue:
write_semantic_node(builder, node, ast_context.file.src, .Enum, .None)
- case index.SymbolUnionValue:
+ case SymbolUnionValue:
write_semantic_node(builder, node, ast_context.file.src, .Enum, .None)
- case index.SymbolProcedureValue:
+ case SymbolProcedureValue:
write_semantic_node(builder, node, ast_context.file.src, .Function, .None)
- case index.SymbolProcedureGroupValue:
+ case SymbolProcedureGroupValue:
write_semantic_node(builder, node, ast_context.file.src, .Function, .None)
- case index.SymbolUntypedValue:
+ case SymbolUntypedValue:
write_semantic_node(builder, node, ast_context.file.src, .Type, .None)
- case index.SymbolBasicValue:
+ case SymbolBasicValue:
write_semantic_node(builder, node, ast_context.file.src, .Type, .None)
case:
//log.errorf("Unexpected symbol value: %v", symbol.value);
@@ -349,11 +346,9 @@ visit_node :: proc(node: ^ast.Node, builder: ^SemanticTokenBuilder, ast_context:
}
}
-visit_basic_lit :: proc(basic_lit: ast.Basic_Lit, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) {
- using analysis
-
+visit_basic_lit :: proc(basic_lit: ast.Basic_Lit, builder: ^SemanticTokenBuilder, ast_context: ^AstContext) {
if symbol, ok := resolve_basic_lit(ast_context, basic_lit); ok {
- if untyped, ok := symbol.value.(index.SymbolUntypedValue); ok {
+ if untyped, ok := symbol.value.(SymbolUntypedValue); ok {
switch untyped.type {
case .Bool:
write_semantic_token(builder, basic_lit.tok, ast_context.file.src, .Keyword, .None)
@@ -366,7 +361,7 @@ visit_basic_lit :: proc(basic_lit: ast.Basic_Lit, builder: ^SemanticTokenBuilder
}
}
-visit_value_decl :: proc(value_decl: ast.Value_Decl, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) {
+visit_value_decl :: proc(value_decl: ast.Value_Decl, builder: ^SemanticTokenBuilder, ast_context: ^AstContext) {
using ast
if value_decl.type != nil {
@@ -433,7 +428,7 @@ visit_token_op :: proc(builder: ^SemanticTokenBuilder, token: tokenizer.Token, s
}
}
-visit_proc_type :: proc(node: ^ast.Proc_Type, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) {
+visit_proc_type :: proc(node: ^ast.Proc_Type, builder: ^SemanticTokenBuilder, ast_context: ^AstContext) {
using ast
if node == nil {
@@ -460,7 +455,7 @@ visit_proc_type :: proc(node: ^ast.Proc_Type, builder: ^SemanticTokenBuilder, as
}
}
-visit_enum_fields :: proc(node: ast.Enum_Type, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) {
+visit_enum_fields :: proc(node: ast.Enum_Type, builder: ^SemanticTokenBuilder, ast_context: ^AstContext) {
using ast
if node.fields == nil {
@@ -480,7 +475,7 @@ visit_enum_fields :: proc(node: ast.Enum_Type, builder: ^SemanticTokenBuilder, a
}
}
-visit_struct_fields :: proc(node: ast.Struct_Type, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) {
+visit_struct_fields :: proc(node: ast.Struct_Type, builder: ^SemanticTokenBuilder, ast_context: ^AstContext) {
using ast
if node.fields == nil {
@@ -498,7 +493,7 @@ visit_struct_fields :: proc(node: ast.Struct_Type, builder: ^SemanticTokenBuilde
}
}
-visit_selector :: proc(selector: ^ast.Selector_Expr, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) {
+visit_selector :: proc(selector: ^ast.Selector_Expr, builder: ^SemanticTokenBuilder, ast_context: ^AstContext) {
if _, ok := selector.expr.derived.(^ast.Selector_Expr); ok {
visit_selector(cast(^ast.Selector_Expr)selector.expr, builder, ast_context)
} else {
@@ -511,17 +506,17 @@ visit_selector :: proc(selector: ^ast.Selector_Expr, builder: ^SemanticTokenBuil
write_semantic_node(builder, selector.field, ast_context.file.src, .Method, .None)
}
#partial switch v in symbol_and_node.symbol.value {
- case index.SymbolPackageValue:
+ case SymbolPackageValue:
write_semantic_node(builder, selector.field, ast_context.file.src, .Namespace, .None)
- case index.SymbolStructValue:
+ case SymbolStructValue:
write_semantic_node(builder, selector.field, ast_context.file.src, .Struct, .None)
- case index.SymbolEnumValue:
+ case SymbolEnumValue:
write_semantic_node(builder, selector.field, ast_context.file.src, .Enum, .None)
- case index.SymbolUnionValue:
+ case SymbolUnionValue:
write_semantic_node(builder, selector.field, ast_context.file.src, .Enum, .None)
- case index.SymbolProcedureValue:
+ case SymbolProcedureValue:
write_semantic_node(builder, selector.field, ast_context.file.src, .Function, .None)
- case index.SymbolProcedureGroupValue:
+ case SymbolProcedureGroupValue:
write_semantic_node(builder, selector.field, ast_context.file.src, .Function, .None)
}
}
diff --git a/src/server/signature.odin b/src/server/signature.odin
index e8b3ae1..e866953 100644
--- a/src/server/signature.odin
+++ b/src/server/signature.odin
@@ -14,8 +14,6 @@ import "core:sort"
import "core:slice"
import "shared:common"
-import "shared:index"
-import "shared:analysis"
SignatureInformationCapabilities :: struct {
parameterInformation: ParameterInformationCapabilities,
@@ -52,8 +50,8 @@ ParameterInformation :: struct {
/*
Lazily build the signature and returns from ast.Nodes
*/
-build_procedure_symbol_signature :: proc(symbol: ^index.Symbol) {
- if value, ok := symbol.value.(index.SymbolProcedureValue); ok {
+build_procedure_symbol_signature :: proc(symbol: ^Symbol) {
+ if value, ok := symbol.value.(SymbolProcedureValue); ok {
builder := strings.make_builder(context.temp_allocator)
strings.write_string(&builder, "proc")
@@ -85,13 +83,13 @@ build_procedure_symbol_signature :: proc(symbol: ^index.Symbol) {
}
}
symbol.signature = strings.to_string(builder)
- } else if value, ok := symbol.value.(index.SymbolAggregateValue); ok {
+ } else if value, ok := symbol.value.(SymbolAggregateValue); ok {
symbol.signature = "proc"
}
}
-seperate_proc_field_arguments :: proc(procedure: ^index.Symbol) {
- if value, ok := &procedure.value.(index.SymbolProcedureValue); ok {
+seperate_proc_field_arguments :: proc(procedure: ^Symbol) {
+ if value, ok := &procedure.value.(SymbolProcedureValue); ok {
types := make([dynamic]^ast.Field, context.temp_allocator)
for arg, i in value.arg_types {
@@ -101,7 +99,7 @@ seperate_proc_field_arguments :: proc(procedure: ^index.Symbol) {
}
for name in arg.names {
- field : ^ast.Field = index.new_type(ast.Field, {}, {}, context.temp_allocator)
+ field : ^ast.Field = new_type(ast.Field, {}, {}, context.temp_allocator)
field.names = make([]^ast.Expr, 1, context.temp_allocator)
field.names[0] = name
field.type = arg.type
@@ -114,8 +112,6 @@ seperate_proc_field_arguments :: proc(procedure: ^index.Symbol) {
}
get_signature_information :: proc(document: ^common.Document, position: common.Position) -> (SignatureHelp, bool) {
- using analysis
-
signature_help: SignatureHelp
ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri)
@@ -145,7 +141,7 @@ get_signature_information :: proc(document: ^common.Document, position: common.P
}
}
- call: index.Symbol
+ call: Symbol
call, ok = resolve_type_expression(&ast_context, position_context.call)
if !ok {
@@ -156,7 +152,7 @@ get_signature_information :: proc(document: ^common.Document, position: common.P
signature_information := make([dynamic]SignatureInformation, context.temp_allocator)
- if value, ok := call.value.(index.SymbolProcedureValue); ok {
+ if value, ok := call.value.(SymbolProcedureValue); ok {
parameters := make([]ParameterInformation, len(value.arg_types), context.temp_allocator)
for arg, i in value.arg_types {
@@ -177,13 +173,13 @@ get_signature_information :: proc(document: ^common.Document, position: common.P
parameters = parameters,
}
append(&signature_information, info)
- } else if value, ok := call.value.(index.SymbolAggregateValue); ok {
+ } else if value, ok := call.value.(SymbolAggregateValue); ok {
//function overloaded procedures
for symbol in value.symbols {
symbol := symbol
- if value, ok := symbol.value.(index.SymbolProcedureValue); ok {
+ if value, ok := symbol.value.(SymbolProcedureValue); ok {
parameters := make([]ParameterInformation, len(value.arg_types), context.temp_allocator)
diff --git a/src/server/symbol.odin b/src/server/symbol.odin
new file mode 100644
index 0000000..b6a3a3c
--- /dev/null
+++ b/src/server/symbol.odin
@@ -0,0 +1,197 @@
+package server
+
+import "core:odin/ast"
+import "core:hash"
+import "core:strings"
+import "core:mem"
+import "core:path/filepath"
+import path "core:path/slashpath"
+import "core:slice"
+
+import "shared:common"
+
+SymbolAndNode :: struct {
+ symbol: Symbol,
+ node: ^ast.Node,
+}
+
+SymbolStructValue :: struct {
+ names: []string,
+ types: []^ast.Expr,
+ usings: map[string]bool,
+ poly: ^ast.Field_List,
+}
+
+SymbolPackageValue :: struct {}
+
+SymbolProcedureValue :: struct {
+ return_types: []^ast.Field,
+ arg_types: []^ast.Field,
+ generic: bool,
+}
+
+SymbolProcedureGroupValue :: struct {
+ group: ^ast.Expr,
+}
+
+//runtime temp symbol value
+SymbolAggregateValue :: struct {
+ symbols: []Symbol,
+}
+
+SymbolEnumValue :: struct {
+ names: []string,
+}
+
+SymbolUnionValue :: struct {
+ types: []^ast.Expr,
+ poly: ^ast.Field_List,
+}
+
+SymbolDynamicArrayValue :: struct {
+ expr: ^ast.Expr,
+}
+
+SymbolFixedArrayValue :: struct {
+ len: ^ast.Expr,
+ expr: ^ast.Expr,
+}
+
+SymbolSliceValue :: struct {
+ expr: ^ast.Expr,
+}
+
+SymbolBasicValue :: struct {
+ ident: ^ast.Ident,
+}
+
+SymbolBitSetValue :: struct {
+ expr: ^ast.Expr,
+}
+
+SymbolUntypedValue :: struct {
+ type: enum {Integer, Float, String, Bool},
+}
+
+SymbolMapValue :: struct {
+ key: ^ast.Expr,
+ value: ^ast.Expr,
+}
+
+/*
+ Generic symbol that is used by the indexer for any variable type(constants, defined global variables, etc),
+*/
+SymbolGenericValue :: struct {
+ expr: ^ast.Expr,
+}
+
+SymbolValue :: union {
+ SymbolStructValue,
+ SymbolPackageValue,
+ SymbolProcedureValue,
+ SymbolGenericValue,
+ SymbolProcedureGroupValue,
+ SymbolUnionValue,
+ SymbolEnumValue,
+ SymbolBitSetValue,
+ SymbolAggregateValue,
+ SymbolDynamicArrayValue,
+ SymbolFixedArrayValue,
+ SymbolMapValue,
+ SymbolSliceValue,
+ SymbolBasicValue,
+ SymbolUntypedValue,
+}
+
+SymbolFlag :: enum {
+ Distinct,
+ Deprecated,
+ PrivateFile,
+ PrivatePackage,
+ Anonymous, //Usually applied to structs that are defined inline inside another struct
+ Variable, //Symbols that are variable, this means their value decl was mutable
+}
+
+SymbolFlags :: bit_set[SymbolFlag]
+
+Symbol :: struct {
+ range: common.Range, //the range of the symbol in the file
+ uri: string, //uri of the file the symbol resides
+ pkg: string, //absolute directory path where the symbol resides
+ name: string, //name of the symbol
+ doc: string,
+ signature: string, //type signature
+ type: SymbolType,
+ value: SymbolValue,
+ pointers: int, //how many `^` are applied to the symbol
+ flags: SymbolFlags,
+}
+
+SymbolType :: enum {
+ Function = 3,
+ Field = 5,
+ Variable = 6,
+ Package = 9,
+ Enum = 13,
+ Keyword = 14,
+ EnumMember = 20,
+ Constant = 21,
+ Struct = 22,
+ Union = 7,
+ Unresolved = 9999,
+}
+
+new_clone_symbol :: proc(data: Symbol, allocator := context.allocator) -> (^Symbol) {
+ new_symbol := new(Symbol, allocator)
+ new_symbol^ = data
+ new_symbol.value = data.value
+ return new_symbol
+}
+
+free_symbol :: proc(symbol: Symbol, allocator: mem.Allocator) {
+ if symbol.signature != "" && symbol.signature != "struct" &&
+ symbol.signature != "union" && symbol.signature != "enum" &&
+ symbol.signature != "bitset" {
+ delete(symbol.signature, allocator)
+ }
+
+ if symbol.doc != "" {
+ delete(symbol.doc, allocator)
+ }
+
+ switch v in symbol.value {
+ case SymbolProcedureValue:
+ common.free_ast(v.return_types, allocator)
+ common.free_ast(v.arg_types, allocator)
+ case SymbolStructValue:
+ delete(v.names, allocator)
+ common.free_ast(v.types, allocator)
+ case SymbolGenericValue:
+ common.free_ast(v.expr, allocator)
+ case SymbolProcedureGroupValue:
+ common.free_ast(v.group, allocator)
+ case SymbolEnumValue:
+ delete(v.names, allocator)
+ case SymbolUnionValue:
+ common.free_ast(v.types, allocator)
+ case SymbolBitSetValue:
+ common.free_ast(v.expr, allocator)
+ case SymbolDynamicArrayValue:
+ common.free_ast(v.expr, allocator)
+ case SymbolFixedArrayValue:
+ common.free_ast(v.expr, allocator)
+ common.free_ast(v.len, allocator)
+ case SymbolSliceValue:
+ common.free_ast(v.expr, allocator)
+ case SymbolBasicValue:
+ common.free_ast(v.ident, allocator)
+ case SymbolAggregateValue:
+ for symbol in v.symbols {
+ free_symbol(symbol, allocator)
+ }
+ case SymbolMapValue:
+ common.free_ast(v.key, allocator)
+ common.free_ast(v.value, allocator)
+ case SymbolUntypedValue, SymbolPackageValue:
+ }
+} \ No newline at end of file