aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorBrad Lewis <22850972+BradLewis@users.noreply.github.com>2025-06-15 08:56:52 -0400
committerBrad Lewis <22850972+BradLewis@users.noreply.github.com>2025-06-24 20:35:20 -0400
commite4804807bb7c7a26b2bff10919d719a1a430870f (patch)
tree116985fc1454378d4a5f01fc7859043a69336ab5 /src/server
parentbe08855c0a96ffff4f577fa5ac703853422ca153 (diff)
Introduce a builder for `SymbolStructValue` and add docs and comments to
struct hover
Diffstat (limited to 'src/server')
-rw-r--r--src/server/analysis.odin266
-rw-r--r--src/server/ast.odin48
-rw-r--r--src/server/collector.odin31
-rw-r--r--src/server/generics.odin22
-rw-r--r--src/server/hover.odin17
-rw-r--r--src/server/symbol.odin278
6 files changed, 471 insertions, 191 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin
index e779547..4f5b6d6 100644
--- a/src/server/analysis.odin
+++ b/src/server/analysis.odin
@@ -900,7 +900,7 @@ internal_resolve_type_expression :: proc(ast_context: ^AstContext, node: ^ast.Ex
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
+ 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:
@@ -1373,7 +1373,7 @@ internal_resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ide
return_symbol, ok = make_symbol_enum_from_ast(ast_context, v^, node), true
return_symbol.name = node.name
case ^Struct_Type:
- return_symbol, ok = make_symbol_struct_from_ast(ast_context, v^, node, {}), true
+ return_symbol, ok = make_symbol_struct_from_ast(ast_context, v, node, {}), true
return_symbol.name = node.name
case ^Bit_Set_Type:
return_symbol, ok = make_symbol_bitset_from_ast(ast_context, v^, node), true
@@ -1483,7 +1483,7 @@ internal_resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ide
}
case ^Struct_Type:
- return_symbol, ok = make_symbol_struct_from_ast(ast_context, v^, node, global.attributes), true
+ return_symbol, ok = make_symbol_struct_from_ast(ast_context, v, node, global.attributes), true
return_symbol.name = node.name
case ^Bit_Set_Type:
return_symbol, ok = make_symbol_bitset_from_ast(ast_context, v^, node), true
@@ -1614,79 +1614,33 @@ internal_resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ide
return Symbol{}, 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)
- ranges := slice.to_dynamic(value.ranges, ast_context.allocator)
-
- for k, v in value.usings {
- set_ast_package_set_scoped(ast_context, symbol.pkg)
-
- field_expr: ^ast.Expr
-
- field_expr = value.types[k]
-
- 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)
- }
-
- for range in struct_value.ranges {
- append(&ranges, range)
- }
- }
- }
-
- //We have to resolve the expressions two times, so clear it to prevent it from being picked up as recursion.
- delete_key(&ast_context.recursion_map, value.types[k])
+struct_type_from_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (^ast.Struct_Type, bool) {
+ if check_node_recursion(ast_context, node.derived.(^ast.Ident)) {
+ return {}, false
}
- if .ObjC in symbol.flags {
- pkg := indexer.index.collection.packages[symbol.pkg]
-
- if obj_struct, ok := pkg.objc_structs[symbol.name]; ok {
- _objc_function: for function, i in obj_struct.functions {
- base := new_type(ast.Ident, {}, {}, context.temp_allocator)
- base.name = obj_struct.pkg
-
- field := new_type(ast.Ident, {}, {}, context.temp_allocator)
- field.name = function.physical_name
-
- selector := new_type(ast.Selector_Expr, {}, {}, context.temp_allocator)
-
- selector.field = field
- selector.expr = base
+ //Try to prevent stack overflows and prevent indexing out of bounds.
+ if ast_context.deferred_count >= DeferredDepth {
+ return {}, false
+ }
- //Check if the base functions need to be overridden. Potentially look at some faster approach than a linear loop.
- for name, j in names {
- if name == function.logical_name {
- names[j] = function.logical_name
- types[j] = selector
- ranges[j] = obj_struct.ranges[i]
- continue _objc_function
- }
- }
+ set_ast_package_scoped(ast_context)
- append(&names, function.logical_name)
- append(&types, selector)
- append(&ranges, obj_struct.ranges[i])
- }
+ if local, ok := get_local(ast_context^, node); ok && (ast_context.use_locals || local.local_global) {
+ v, ok := local.rhs.derived.(^ast.Struct_Type)
+ return v, ok
+ }
- }
+ if global, ok := ast_context.globals[node.name];
+ ast_context.current_package == ast_context.document_package && ok {
+ v, ok := global.expr.derived.(^ast.Struct_Type)
+ return v, ok
}
- return {names = names[:], types = types[:], ranges = ranges[:]}
+ return nil, false
}
+
resolve_slice_expression :: proc(ast_context: ^AstContext, slice_expr: ^ast.Slice_Expr) -> (symbol: Symbol, ok: bool) {
symbol = resolve_type_expression(ast_context, slice_expr.expr) or_return
@@ -1995,26 +1949,15 @@ resolve_symbol_return :: proc(ast_context: ^AstContext, symbol: Symbol, ok := tr
}
return symbol, ok
case SymbolStructValue:
+ b := symbol_struct_value_builder_make(symbol, v, ast_context.allocator)
if v.poly != nil {
- types := make([dynamic]^ast.Expr, ast_context.allocator)
-
- for type in v.types {
- append(&types, clone_expr(type, context.temp_allocator, nil))
- }
-
- v.types = types[:]
-
- resolve_poly_struct(ast_context, v.poly, &symbol)
+ resolve_poly_struct(ast_context, &b, v.poly)
}
//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 || .ObjC in symbol.flags {
- expanded := symbol
- expanded.value = expand_struct_usings(ast_context, symbol, v)
- return expanded, true
- } else {
- return symbol, true
- }
+ expand_usings(ast_context, &b)
+ expand_objc(ast_context, &b)
+ return to_symbol(b), ok
case SymbolGenericValue:
ret, ok := resolve_type_expression(ast_context, v.expr)
if symbol.type == .Variable {
@@ -2466,8 +2409,11 @@ get_using_packages :: proc(ast_context: ^AstContext) -> []string {
}
get_symbol_pkg_name :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
- name := path.base(symbol.pkg, false, context.temp_allocator)
+ return get_pkg_name(ast_context, symbol.pkg)
+}
+get_pkg_name :: proc(ast_context: ^AstContext, pkg: string) -> string {
+ name := path.base(pkg, false, context.temp_allocator)
for imp in ast_context.imports {
if imp.base_original == name {
return imp.base
@@ -2761,11 +2707,12 @@ make_symbol_bitset_from_ast :: proc(
make_symbol_struct_from_ast :: proc(
ast_context: ^AstContext,
- v: ast.Struct_Type,
+ v: ^ast.Struct_Type,
ident: ast.Ident,
attributes: []^ast.Attribute,
inlined := false,
) -> Symbol {
+ node := v.node
symbol := Symbol {
range = common.get_token_range(v, ast_context.file.src),
type = .Struct,
@@ -2778,54 +2725,9 @@ make_symbol_struct_from_ast :: proc(
symbol.name = "struct"
}
- names := make([dynamic]string, ast_context.allocator)
- types := make([dynamic]^ast.Expr, ast_context.allocator)
- usings := make(map[int]bool, 0, ast_context.allocator)
- ranges := make([dynamic]common.Range, 0, ast_context.allocator)
-
- for field in v.fields.list {
- for n in field.names {
- if identifier, ok := n.derived.(^ast.Ident); ok && field.type != nil {
- if .Using in field.flags {
- usings[len(types)] = true
- }
-
- append(&names, identifier.name)
- if v.poly_params != nil {
- append(&types, clone_type(field.type, ast_context.allocator, nil))
- } else {
- append(&types, field.type)
- }
-
- append(&ranges, common.get_token_range(n, ast_context.file.src))
- }
- }
- }
-
- symbol.value = SymbolStructValue {
- names = names[:],
- types = types[:],
- ranges = ranges[:],
- usings = usings,
- poly = v.poly_params,
- }
-
- if _, ok := get_attribute_objc_class_name(attributes); ok {
- symbol.flags |= {.ObjC}
- if get_attribute_objc_is_class_method(attributes) {
- symbol.flags |= {.ObjCIsClassMethod}
- }
- }
-
- 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 || .ObjC in symbol.flags {
- symbol.value = expand_struct_usings(ast_context, symbol, symbol.value.(SymbolStructValue))
- }
-
+ b := symbol_struct_value_builder_make(symbol, ast_context.allocator)
+ write_struct_type(ast_context, &b, v^, ident, attributes, -1, inlined)
+ symbol = to_symbol(b)
return symbol
}
@@ -3956,6 +3858,16 @@ append_variable_full_name :: proc(
return
}
+make_comment_map :: proc(groups: []^ast.Comment_Group, allocator: mem.Allocator) -> map[int]^ast.Comment_Group {
+ comment_map := make(map[int]^ast.Comment_Group, allocator = allocator)
+
+ for cg in groups {
+ comment_map[cg.pos.line] = cg
+ }
+
+ return comment_map
+}
+
get_signature :: proc(
ast_context: ^AstContext,
symbol: Symbol,
@@ -3971,7 +3883,7 @@ get_signature :: proc(
}
is_variable := symbol.type == .Variable
-
+ is_field := symbol.type == .Field
pointer_prefix := repeat("^", symbol.pointers, context.temp_allocator)
@@ -4024,36 +3936,30 @@ get_signature :: proc(
builder := strings.builder_make(ast_context.allocator)
if is_variable {
append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
+ } else if is_field {
+ pkg_name := get_pkg_name(ast_context, symbol.type_pkg)
+ if pkg_name == "" {
+ fmt.sbprintf(&builder, "%s%s", pointer_prefix, symbol.type_name)
+ } else {
+ fmt.sbprintf(&builder, "%s%s.%s", pointer_prefix, pkg_name, symbol.type_name)
+ }
+ if symbol.comment != "" {
+ fmt.sbprintf(&builder, " %s", symbol.comment)
+ }
+ return strings.to_string(builder)
} else if symbol.type_name != "" {
if symbol.type_pkg == "" {
fmt.sbprintf(&builder, "%s%s :: ", pointer_prefix, symbol.type_name)
} else {
- // TODO: make this function just take a string
- symbol := symbol
- symbol.pkg = symbol.type_pkg
- pkg_name := get_symbol_pkg_name(ast_context, symbol)
+ pkg_name := get_pkg_name(ast_context, symbol.type_pkg)
fmt.sbprintf(&builder, "%s%s.%s :: ", pointer_prefix, pkg_name, symbol.type_name)
}
}
- longestNameLen := 0
- for name in v.names {
- if len(name) > longestNameLen {
- longestNameLen = len(name)
- }
- }
if len(v.names) == 0 {
strings.write_string(&builder, "struct {}")
return strings.to_string(builder)
}
- strings.write_string(&builder, "struct {\n")
- for i in 0 ..< len(v.names) {
- strings.write_string(&builder, "\t")
- strings.write_string(&builder, v.names[i])
- fmt.sbprintf(&builder, ":%*s", longestNameLen - len(v.names[i]) + 1, "")
- build_string_node(v.types[i], &builder, false)
- strings.write_string(&builder, ",\n")
- }
- strings.write_string(&builder, "}")
+ write_struct_hover(ast_context, &builder, v)
return strings.to_string(builder)
case SymbolUnionValue:
if short_signature {
@@ -4134,6 +4040,60 @@ get_signature :: proc(
return ""
}
+write_struct_hover :: proc(ast_context: ^AstContext, sb: ^strings.Builder, v: SymbolStructValue) {
+ using_prefix := "using "
+ longestNameLen := 0
+ for name, i in v.names {
+ l := len(name)
+ if _, ok := v.usings[i]; ok {
+ l += len(using_prefix)
+ }
+ if l > longestNameLen {
+ longestNameLen = len(name)
+ }
+ }
+
+ using_index := -1
+
+ strings.write_string(sb, "struct {\n")
+ for i in 0 ..< len(v.names) {
+ if i < len(v.from_usings) {
+ if index := v.from_usings[i]; index != using_index {
+ fmt.sbprintf(sb, "\n\t// from `using %s: ", v.names[index])
+ build_string_node(v.types[index], sb, false)
+ strings.write_string(sb, "`\n")
+ using_index = index
+ }
+ }
+ if i < len(v.docs) && v.docs[i] != nil {
+ for c in v.docs[i].list {
+ fmt.sbprintf(sb, "\t%s\n", c.text)
+ }
+ }
+
+ strings.write_string(sb, "\t")
+
+ name_len := len(v.names[i])
+ if _, ok := v.usings[i]; ok {
+ strings.write_string(sb, using_prefix)
+ name_len += len(using_prefix)
+ }
+ strings.write_string(sb, v.names[i])
+ fmt.sbprintf(sb, ":%*s", longestNameLen - name_len + 1, "")
+ build_string_node(v.types[i], sb, false)
+ strings.write_string(sb, ",")
+
+ if i < len(v.comments) && v.comments[i] != nil {
+ for c in v.comments[i].list {
+ fmt.sbprintf(sb, " %s\n", c.text)
+ }
+ } else {
+ strings.write_string(sb, "\n")
+ }
+ }
+ strings.write_string(sb, "}")
+}
+
position_in_proc_decl :: proc(position_context: ^DocumentPositionContext) -> bool {
if position_context.value_decl == nil {
return false
diff --git a/src/server/ast.odin b/src/server/ast.odin
index 78e037c..73c84cc 100644
--- a/src/server/ast.odin
+++ b/src/server/ast.odin
@@ -1174,3 +1174,51 @@ repeat :: proc(value: string, count: int, allocator := context.allocator) -> str
}
return strings.repeat(value, count, allocator)
}
+
+construct_struct_field_docs :: proc(file: ast.File, v: ^ast.Struct_Type) {
+ for field, i in v.fields.list {
+ // There is currently a bug in the odin parser where it adds line comments for a field to the
+ // docs of the following field, we address this problem here.
+ // see https://github.com/odin-lang/Odin/issues/5353
+ if field.comment == nil {
+ // We check if the comment is at the start of the next field
+ if i != len(v.fields.list) - 1 {
+ next_field := v.fields.list[i + 1]
+ if next_field.docs != nil && len(next_field.docs.list) > 0 {
+ list := next_field.docs.list
+ if list[0].pos.line == field.pos.line {
+ field.comment = ast.new(ast.Comment_Group, list[0].pos, parser.end_pos(list[0]))
+ field.comment.list = list[:1]
+ if len(list) > 1 {
+ next_field.docs = ast.new(
+ ast.Comment_Group,
+ list[1].pos,
+ parser.end_pos(list[len(list) - 2]),
+ )
+ next_field.docs.list = list[1:]
+ } else {
+ next_field.docs = nil
+ }
+ }
+ }
+ } else {
+ // We need to check the file to see if it contains a line comment as there is no next field
+ // TODO: linear scan might be a bit slow for files with lots of comments?
+ for c in file.comments {
+ if c.pos.line == field.pos.line {
+ for item, j in c.list {
+ field.comment = ast.new(ast.Comment_Group, item.pos, parser.end_pos(item))
+ if j == len(c.list) - 1 {
+ field.comment.list = c.list[j:]
+ } else {
+ field.comment.list = c.list[j:j + 1]
+ }
+ break
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/server/collector.odin b/src/server/collector.odin
index fb2588a..1bbe569 100644
--- a/src/server/collector.odin
+++ b/src/server/collector.odin
@@ -118,40 +118,37 @@ collect_procedure_fields :: proc(
collect_struct_fields :: proc(
collection: ^SymbolCollection,
- struct_type: ast.Struct_Type,
+ struct_type: ^ast.Struct_Type,
package_map: map[string]string,
file: ast.File,
) -> SymbolStructValue {
- names := make([dynamic]string, 0, collection.allocator)
- types := make([dynamic]^ast.Expr, 0, collection.allocator)
- usings := make(map[int]bool, 0, collection.allocator)
- ranges := make([dynamic]common.Range, 0, collection.allocator)
+ b := symbol_struct_value_builder_make(collection.allocator)
+ construct_struct_field_docs(file, struct_type)
for field in struct_type.fields.list {
for n in field.names {
if ident, ok := n.derived.(^ast.Ident); ok {
- append(&names, get_index_unique_string(collection, ident.name))
+ append(&b.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)
+ append(&b.types, cloned)
if .Using in field.flags {
- usings[len(names) - 1] = true
+ append(&b.unexpanded_usings, len(b.names) - 1)
}
- append(&ranges, common.get_token_range(n, file.src))
+ append(&b.ranges, common.get_token_range(n, file.src))
+
+ append(&b.docs, field.docs)
+ append(&b.comments, field.comment)
+ append(&b.from_usings, -1)
}
}
}
- value := SymbolStructValue {
- names = names[:],
- types = types[:],
- ranges = ranges[:],
- usings = usings,
- poly = cast(^ast.Field_List)clone_type(struct_type.poly_params, collection.allocator, &collection.unique_strings),
- }
+ value := to_symbol_struct_value(b)
+ value.poly = cast(^ast.Field_List)clone_type(struct_type.poly_params, collection.allocator, &collection.unique_strings)
return value
}
@@ -522,7 +519,7 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri
case ^ast.Struct_Type:
token = v^
token_type = .Struct
- symbol.value = collect_struct_fields(collection, v^, package_map, file)
+ symbol.value = collect_struct_fields(collection, v, package_map, file)
symbol.signature = "struct"
if _, is_objc := get_attribute_objc_class_name(expr.attributes); is_objc {
diff --git a/src/server/generics.odin b/src/server/generics.odin
index db7ad88..d31dfcd 100644
--- a/src/server/generics.odin
+++ b/src/server/generics.odin
@@ -643,21 +643,15 @@ is_procedure_generic :: proc(proc_type: ^ast.Proc_Type) -> bool {
}
-resolve_poly_struct :: proc(ast_context: ^AstContext, poly_params: ^ast.Field_List, symbol: ^Symbol) {
+// TODO: update to use builder
+resolve_poly_struct :: proc(ast_context: ^AstContext, b: ^SymbolStructValueBuilder, poly_params: ^ast.Field_List) {
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)
- args := make([dynamic]^ast.Expr, 0, context.temp_allocator)
for param in poly_params.list {
for name in param.names {
@@ -679,7 +673,7 @@ resolve_poly_struct :: proc(ast_context: ^AstContext, poly_params: ^ast.Field_Li
}
}
- append(&args, ast_context.call.args[i])
+ append(&b.args, ast_context.call.args[i])
i += 1
}
@@ -687,7 +681,7 @@ resolve_poly_struct :: proc(ast_context: ^AstContext, poly_params: ^ast.Field_Li
Visit_Data :: struct {
poly_map: map[string]^ast.Expr,
- symbol_value: ^SymbolStructValue,
+ symbol_value_builder: ^SymbolStructValueBuilder,
parent: ^ast.Node,
i: int,
poly_index: int,
@@ -712,7 +706,7 @@ resolve_poly_struct :: proc(ast_context: ^AstContext, poly_params: ^ast.Field_Li
v.elem = expr
}
} else {
- data.symbol_value.types[data.i] = expr
+ data.symbol_value_builder.types[data.i] = expr
data.poly_index += 1
}
}
@@ -726,10 +720,10 @@ resolve_poly_struct :: proc(ast_context: ^AstContext, poly_params: ^ast.Field_Li
return visitor
}
- for type, i in symbol_value.types {
+ for type, i in b.types {
data := Visit_Data {
poly_map = poly_map,
- symbol_value = symbol_value,
+ symbol_value_builder = b,
i = i,
}
@@ -740,8 +734,6 @@ resolve_poly_struct :: proc(ast_context: ^AstContext, poly_params: ^ast.Field_Li
ast.walk(&visitor, type)
}
-
- symbol_value.args = args[:]
}
diff --git a/src/server/hover.odin b/src/server/hover.odin
index afeab99..dd4af97 100644
--- a/src/server/hover.odin
+++ b/src/server/hover.odin
@@ -125,10 +125,27 @@ get_hover_information :: proc(document: ^Document, position: common.Position) ->
&ast_context,
position_context.value_decl.names[0],
); ok {
+ symbol.type = .Field
+ symbol.range = common.get_token_range(field.node, ast_context.file.src)
symbol.type_name = symbol.name
symbol.type_pkg = symbol.pkg
symbol.pkg = struct_symbol.name
symbol.name = identifier.name
+
+ // Get the inline comment from the field definition if it exists
+ if value, ok := struct_symbol.value.(SymbolStructValue); ok {
+ index := -1
+ for n, i in value.names {
+ if n == symbol.name {
+ index = i
+ break
+ }
+ }
+ if index != -1 && value.comments[index] != nil && len(value.comments[index].list) > 0 {
+ symbol.comment = value.comments[index].list[0].text
+ }
+ }
+
symbol.signature = get_signature(&ast_context, symbol)
hover.contents = write_hover_content(&ast_context, symbol)
return hover, true, true
diff --git a/src/server/symbol.odin b/src/server/symbol.odin
index 49ada35..7bd6cd2 100644
--- a/src/server/symbol.odin
+++ b/src/server/symbol.odin
@@ -5,6 +5,7 @@ import "core:hash"
import "core:log"
import "core:mem"
import "core:odin/ast"
+import "core:odin/parser"
import "core:odin/tokenizer"
import "core:path/filepath"
import path "core:path/slashpath"
@@ -18,13 +19,22 @@ SymbolAndNode :: struct {
node: ^ast.Node,
}
+UsingInfo :: struct {
+ from_index: int,
+ is_using: bool,
+}
+
SymbolStructValue :: struct {
- names: []string,
- ranges: []common.Range,
- types: []^ast.Expr,
- usings: map[int]bool,
- poly: ^ast.Field_List,
- args: []^ast.Expr, //The arguments in the call expression for poly
+ names: []string,
+ ranges: []common.Range,
+ types: []^ast.Expr,
+ usings: map[int]struct{},
+ from_usings: []int,
+ unexpanded_usings: []int,
+ poly: ^ast.Field_List,
+ args: []^ast.Expr, //The arguments in the call expression for poly
+ docs: []^ast.Comment_Group,
+ comments: []^ast.Comment_Group,
}
SymbolBitFieldValue :: struct {
@@ -160,6 +170,7 @@ Symbol :: struct {
pkg: string, //absolute directory path where the symbol resides
name: string, //name of the symbol
doc: string,
+ comment: string,
signature: string, //type signature
type: SymbolType,
type_pkg: string,
@@ -185,6 +196,261 @@ SymbolType :: enum {
Unresolved = 1, //Use text if not being able to resolve it.
}
+SymbolStructValueBuilder :: struct {
+ symbol: Symbol,
+ names: [dynamic]string,
+ types: [dynamic]^ast.Expr,
+ args: [dynamic]^ast.Expr, //The arguments in the call expression for poly
+ ranges: [dynamic]common.Range,
+ docs: [dynamic]^ast.Comment_Group,
+ comments: [dynamic]^ast.Comment_Group,
+ usings: map[int]struct{},
+ from_usings: [dynamic]int,
+ unexpanded_usings: [dynamic]int,
+ poly: ^ast.Field_List,
+}
+
+symbol_struct_value_builder_make_none :: proc(allocator := context.allocator) -> SymbolStructValueBuilder {
+ return SymbolStructValueBuilder {
+ names = make([dynamic]string, allocator),
+ types = make([dynamic]^ast.Expr, allocator),
+ args = make([dynamic]^ast.Expr, allocator),
+ ranges = make([dynamic]common.Range, allocator),
+ docs = make([dynamic]^ast.Comment_Group, allocator),
+ comments = make([dynamic]^ast.Comment_Group, allocator),
+ usings = make(map[int]struct{}, allocator),
+ from_usings = make([dynamic]int, allocator),
+ unexpanded_usings = make([dynamic]int, allocator),
+ }
+}
+
+symbol_struct_value_builder_make_symbol :: proc(
+ symbol: Symbol,
+ allocator := context.allocator,
+) -> SymbolStructValueBuilder {
+ return SymbolStructValueBuilder {
+ symbol = symbol,
+ names = make([dynamic]string, allocator),
+ types = make([dynamic]^ast.Expr, allocator),
+ args = make([dynamic]^ast.Expr, allocator),
+ ranges = make([dynamic]common.Range, allocator),
+ docs = make([dynamic]^ast.Comment_Group, allocator),
+ comments = make([dynamic]^ast.Comment_Group, allocator),
+ usings = make(map[int]struct{}, allocator),
+ from_usings = make([dynamic]int, allocator),
+ unexpanded_usings = make([dynamic]int, allocator),
+ }
+}
+
+symbol_struct_value_builder_make_symbol_symbol_struct_value :: proc(
+ symbol: Symbol,
+ v: SymbolStructValue,
+ allocator := context.allocator,
+) -> SymbolStructValueBuilder {
+ return SymbolStructValueBuilder {
+ symbol = symbol,
+ names = slice.to_dynamic(v.names, allocator),
+ types = slice.to_dynamic(v.types, allocator),
+ args = slice.to_dynamic(v.args, allocator),
+ ranges = slice.to_dynamic(v.ranges, allocator),
+ docs = slice.to_dynamic(v.docs, allocator),
+ comments = slice.to_dynamic(v.comments, allocator),
+ from_usings = slice.to_dynamic(v.from_usings, allocator),
+ unexpanded_usings = slice.to_dynamic(v.unexpanded_usings, allocator),
+ }
+}
+
+symbol_struct_value_builder_make :: proc {
+ symbol_struct_value_builder_make_none,
+ symbol_struct_value_builder_make_symbol,
+ symbol_struct_value_builder_make_symbol_symbol_struct_value,
+}
+
+to_symbol :: proc(b: SymbolStructValueBuilder) -> Symbol {
+ symbol := b.symbol
+ symbol.value = to_symbol_struct_value(b)
+ return symbol
+}
+
+to_symbol_struct_value :: proc(b: SymbolStructValueBuilder) -> SymbolStructValue {
+ return SymbolStructValue {
+ names = b.names[:],
+ types = b.types[:],
+ ranges = b.ranges[:],
+ args = b.args[:],
+ docs = b.docs[:],
+ comments = b.comments[:],
+ usings = b.usings,
+ from_usings = b.from_usings[:],
+ unexpanded_usings = b.unexpanded_usings[:],
+ poly = b.poly,
+ }
+}
+
+write_struct_type :: proc(
+ ast_context: ^AstContext,
+ b: ^SymbolStructValueBuilder,
+ v: ast.Struct_Type,
+ ident: ast.Ident,
+ attributes: []^ast.Attribute,
+ base_using_index: int,
+ inlined := false,
+) {
+ b.poly = v.poly_params
+ v := v
+ construct_struct_field_docs(ast_context.file, &v)
+ for field, i in v.fields.list {
+ for n in field.names {
+ if identifier, ok := n.derived.(^ast.Ident); ok && field.type != nil {
+ if .Using in field.flags {
+ append(&b.unexpanded_usings, len(b.types))
+ }
+
+ append(&b.names, identifier.name)
+ if v.poly_params != nil {
+ append(&b.types, clone_type(field.type, ast_context.allocator, nil))
+ } else {
+ append(&b.types, field.type)
+ }
+
+ append(&b.ranges, common.get_token_range(n, ast_context.file.src))
+ append(&b.docs, field.docs)
+ append(&b.comments, field.comment)
+ append(&b.from_usings, base_using_index)
+ }
+ }
+ }
+
+ if _, ok := get_attribute_objc_class_name(attributes); ok {
+ b.symbol.flags |= {.ObjC}
+ if get_attribute_objc_is_class_method(attributes) {
+ b.symbol.flags |= {.ObjCIsClassMethod}
+ }
+ }
+
+ if v.poly_params != nil {
+ resolve_poly_struct(ast_context, b, v.poly_params)
+ }
+
+ expand_usings(ast_context, b)
+ expand_objc(ast_context, b)
+}
+
+write_symbol_struct_value :: proc(
+ ast_context: ^AstContext, b: ^SymbolStructValueBuilder, v: SymbolStructValue, base_using_index: int
+) {
+ base_index := len(b.names)
+ for name in v.names {
+ append(&b.names, name)
+ }
+ for type in v.types {
+ append(&b.types, type)
+ }
+ for arg in v.args {
+ append(&b.args, arg)
+ }
+ for range in v.ranges {
+ append(&b.ranges, range)
+ }
+ for doc in v.docs {
+ append(&b.docs, doc)
+ }
+ for comment in v.comments {
+ append(&b.comments, comment)
+ }
+ for u in v.from_usings {
+ if u == -1 {
+ append(&b.from_usings, base_using_index)
+ } else {
+ append(&b.from_usings, u + base_index)
+ }
+ }
+ for u in v.unexpanded_usings {
+ append(&b.unexpanded_usings, u+base_index)
+ }
+ for k in v.usings {
+ b.usings[k+base_index] = struct{}{}
+ }
+ expand_usings(ast_context, b)
+}
+
+expand_usings :: proc(ast_context: ^AstContext, b: ^SymbolStructValueBuilder) {
+ base := len(b.names) - 1
+ for len(b.unexpanded_usings) > 0 {
+ u := pop_front(&b.unexpanded_usings)
+
+ field_expr := b.types[u]
+ pkg := get_package_from_node(field_expr.expr_base)
+ set_ast_package_set_scoped(ast_context, pkg)
+
+
+ if field_expr == nil {
+ continue
+ }
+
+ b.usings[u] = struct{}{}
+
+ if ident, ok := field_expr.derived.(^ast.Ident); ok {
+ if v, ok := struct_type_from_identifier(ast_context, ident^); ok {
+ write_struct_type(ast_context, b, v^, ident^, {}, u, true)
+ } else {
+ clear(&ast_context.recursion_map)
+ if symbol, ok := resolve_type_identifier(ast_context, ident^); ok {
+ if v, ok := symbol.value.(SymbolStructValue); ok {
+ write_symbol_struct_value(ast_context, b, v, u)
+ }
+ }
+ }
+ } else if selector, ok := field_expr.derived.(^ast.Selector_Expr); ok {
+ if s, ok := resolve_selector_expression(ast_context, selector); ok {
+ if v, ok := s.value.(SymbolStructValue); ok {
+ write_symbol_struct_value(ast_context, b, v, u)
+ }
+ }
+ }
+ delete_key(&ast_context.recursion_map, b.types[u])
+ }
+}
+
+expand_objc :: proc(ast_context: ^AstContext, b: ^SymbolStructValueBuilder) {
+ symbol := b.symbol
+ if .ObjC in symbol.flags {
+ pkg := indexer.index.collection.packages[symbol.pkg]
+
+ if obj_struct, ok := pkg.objc_structs[symbol.name]; ok {
+ _objc_function: for function, i in obj_struct.functions {
+ base := new_type(ast.Ident, {}, {}, context.temp_allocator)
+ base.name = obj_struct.pkg
+
+ field := new_type(ast.Ident, {}, {}, context.temp_allocator)
+ field.name = function.physical_name
+
+ selector := new_type(ast.Selector_Expr, {}, {}, context.temp_allocator)
+
+ selector.field = field
+ selector.expr = base
+
+ //Check if the base functions need to be overridden. Potentially look at some faster approach than a linear loop.
+ for name, j in b.names {
+ if name == function.logical_name {
+ b.names[j] = function.logical_name
+ b.types[j] = selector
+ b.ranges[j] = obj_struct.ranges[i]
+ continue _objc_function
+ }
+ }
+
+ append(&b.names, function.logical_name)
+ append(&b.types, selector)
+ append(&b.ranges, obj_struct.ranges[i])
+ append(&b.docs, nil)
+ append(&b.comments, nil)
+ append(&b.from_usings, -1)
+ }
+ }
+ }
+}
+
new_clone_symbol :: proc(data: Symbol, allocator := context.allocator) -> ^Symbol {
new_symbol := new(Symbol, allocator)
new_symbol^ = data