diff options
| author | Bradley Lewis <22850972+BradLewis@users.noreply.github.com> | 2025-08-17 21:42:44 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-17 21:42:44 -0400 |
| commit | 7f8c9bdf7d1be2f4e4f2301de163a8bac8bb025b (patch) | |
| tree | bd06c4da9dbfe0f56cc2817f5e3287b7d4dd657d /src/server | |
| parent | 6282669ff2438cdeb1b90b05af0f573602ad3144 (diff) | |
| parent | 03a531cc687b4a97c5834037598a7ba74b3ef116 (diff) | |
Merge pull request #900 from BradLewis/feat/soa
Support #soa fields
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/analysis.odin | 70 | ||||
| -rw-r--r-- | src/server/ast.odin | 12 | ||||
| -rw-r--r-- | src/server/completion.odin | 109 | ||||
| -rw-r--r-- | src/server/documentation.odin | 27 | ||||
| -rw-r--r-- | src/server/hover.odin | 43 | ||||
| -rw-r--r-- | src/server/symbol.odin | 1 |
6 files changed, 230 insertions, 32 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 1524112..e2ce2a6 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -1080,7 +1080,8 @@ internal_resolve_type_expression :: proc(ast_context: ^AstContext, node: ^ast.Ex out^, ok = make_symbol_map_from_ast(ast_context, v^, ast_context.field_name), true return ok case ^Proc_Type: - out^, ok = make_symbol_procedure_from_ast(ast_context, node, v^, ast_context.field_name.name, {}, true, .None), true + out^, ok = + make_symbol_procedure_from_ast(ast_context, node, v^, ast_context.field_name.name, {}, true, .None), true return ok case ^Bit_Field_Type: out^, ok = make_symbol_bit_field_from_ast(ast_context, v, ast_context.field_name.name, true), true @@ -1144,6 +1145,9 @@ internal_resolve_type_expression :: proc(ast_context: ^AstContext, node: ^ast.Ex case ^Pointer_Type: ok := internal_resolve_type_expression(ast_context, v.elem, out) out.pointers += 1 + if pointer_is_soa(v^) { + out.flags += {.SoaPointer} + } return ok case ^Matrix_Index_Expr: if ok := internal_resolve_type_expression(ast_context, v.expr, out); ok { @@ -1300,6 +1304,54 @@ resolve_type_assertion_expr :: proc(ast_context: ^AstContext, v: ^ast.Type_Asser return symbol, ok } +resolve_soa_selector_field :: proc( + ast_context: ^AstContext, + selector: Symbol, + expr: ^ast.Expr, + size: ^ast.Expr, + name: string, +) -> ( + Symbol, + bool, +) { + if .Soa not_in selector.flags && .SoaPointer not_in selector.flags { + return {}, false + } + + if symbol, ok := resolve_type_expression(ast_context, expr); ok { + if v, ok := symbol.value.(SymbolStructValue); ok { + for n, i in v.names { + if n == name { + if .SoaPointer in selector.flags { + if resolved, ok := resolve_type_expression(ast_context, v.types[i]); ok { + symbol.value = resolved.value + symbol.pkg = symbol.name + } else { + return {}, false + } + } else if size != nil { + symbol.value = SymbolFixedArrayValue { + expr = v.types[i], + len = size, + } + } else { + symbol.value = SymbolMultiPointerValue { + expr = v.types[i], + } + } + + symbol.name = name + symbol.type = .Field + symbol.range = v.ranges[i] + return symbol, true + } + } + } + } + + return {}, false +} + resolve_selector_expression :: proc(ast_context: ^AstContext, node: ^ast.Selector_Expr) -> (Symbol, bool) { selector := Symbol{} if ok := internal_resolve_type_expression(ast_context, node.expr, &selector); ok { @@ -1310,6 +1362,9 @@ resolve_selector_expression :: proc(ast_context: ^AstContext, node: ^ast.Selecto symbol := Symbol{} #partial switch s in selector.value { case SymbolFixedArrayValue: + if symbol, ok := resolve_soa_selector_field(ast_context, selector, s.expr, s.len, node.field.name); ok { + return symbol, ok + } components_count := 0 for c in node.field.name { if c == 'x' || c == 'y' || c == 'z' || c == 'w' || c == 'r' || c == 'g' || c == 'b' || c == 'a' { @@ -1382,6 +1437,10 @@ resolve_selector_expression :: proc(ast_context: ^AstContext, node: ^ast.Selecto // enum members probably require own symbol value selector.type = .EnumMember return selector, true + case SymbolSliceValue: + return resolve_soa_selector_field(ast_context, selector, s.expr, nil, node.field.name) + case SymbolDynamicArrayValue: + return resolve_soa_selector_field(ast_context, selector, s.expr, nil, node.field.name) } } @@ -1620,7 +1679,8 @@ resolve_local_identifier :: proc(ast_context: ^AstContext, node: ast.Ident, loca if !ok && !ast_context.overloading { return_symbol, ok = - make_symbol_procedure_from_ast(ast_context, local.rhs, v.type^, node.name, {}, false, v.inlining), true + make_symbol_procedure_from_ast(ast_context, local.rhs, v.type^, node.name, {}, false, v.inlining), + true } } else { return_symbol, ok = @@ -2596,6 +2656,12 @@ resolve_symbol_selector :: proc( if s, ok := resolve_type_expression(ast_context, v.return_types[0].type); ok { return resolve_symbol_selector(ast_context, selector, s) } + case SymbolSliceValue: + return resolve_soa_selector_field(ast_context, symbol, v.expr, nil, field) + case SymbolDynamicArrayValue: + return resolve_soa_selector_field(ast_context, symbol, v.expr, nil, field) + case SymbolFixedArrayValue: + return resolve_soa_selector_field(ast_context, symbol, v.expr, v.len, field) } return symbol, true diff --git a/src/server/ast.odin b/src/server/ast.odin index f33ac6b..36f6a88 100644 --- a/src/server/ast.odin +++ b/src/server/ast.odin @@ -243,6 +243,15 @@ unwrap_pointer_expr :: proc(expr: ^ast.Expr) -> (^ast.Expr, int, bool) { return expr, n, true } +pointer_is_soa :: proc(pointer: ast.Pointer_Type) -> bool { + if pointer.tag != nil { + if basic, ok := pointer.tag.derived.(^ast.Basic_Directive); ok && basic.name == "soa" { + return true + } + } + return false +} + array_is_soa :: proc(array: ast.Array_Type) -> bool { if array.tag != nil { if basic, ok := array.tag.derived.(^ast.Basic_Directive); ok && basic.name == "soa" { @@ -1216,16 +1225,19 @@ build_string_node :: proc(node: ^ast.Node, builder: ^strings.Builder, remove_poi build_string(n.results, builder, remove_pointers) } case ^Pointer_Type: + build_string(n.tag, builder, remove_pointers) if !remove_pointers { strings.write_string(builder, "^") } build_string(n.elem, builder, remove_pointers) case ^Array_Type: + build_string(n.tag, builder, remove_pointers) strings.write_string(builder, "[") build_string(n.len, builder, remove_pointers) strings.write_string(builder, "]") build_string(n.elem, builder, remove_pointers) case ^Dynamic_Array_Type: + build_string(n.tag, builder, remove_pointers) strings.write_string(builder, "[dynamic]") build_string(n.elem, builder, remove_pointers) case ^Struct_Type: diff --git a/src/server/completion.odin b/src/server/completion.odin index 9bb2f3b..4bd0272 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -1,6 +1,7 @@ #+feature dynamic-literals package server +import "base:runtime" import "core:fmt" import "core:log" import "core:mem" @@ -14,7 +15,6 @@ import "core:slice" import "core:sort" import "core:strconv" import "core:strings" -import "base:runtime" import "src:common" @@ -214,14 +214,14 @@ convert_completion_results :: proc( // temporary as we move things to use the symbols directly if item.documentation == nil { item.documentation = MarkupContent { - kind = "markdown", - value = fmt.tprintf("```odin\n%v\n```", item.detail) + kind = "markdown", + value = fmt.tprintf("```odin\n%v\n```", item.detail), } item.detail = "" } else if s, ok := item.documentation.(string); ok && s == "" { item.documentation = MarkupContent { - kind = "markdown", - value = fmt.tprintf("```odin\n%v\n```", item.detail) + kind = "markdown", + value = fmt.tprintf("```odin\n%v\n```", item.detail), } item.detail = "" } @@ -316,7 +316,7 @@ convert_completion_results :: proc( } if completion_type == .Identifier { - append_non_imported_packages(ast_context, position_context, &items) + append_non_imported_packages(ast_context, position_context, &items) } return items[:] @@ -419,13 +419,15 @@ DIRECTIVE_NAME_LIST :: []string { completion_items_directives: []CompletionResult @(init) -_init_completion_items_directives :: proc "contextless" () { +_init_completion_items_directives :: proc "contextless" () { context = runtime.default_context() completion_items_directives = slice.mapper(DIRECTIVE_NAME_LIST, proc(name: string) -> CompletionResult { - return CompletionResult{ - completion_item = CompletionItem{ - detail = strings.concatenate({"#", name}) or_else name, label = name, kind = .Constant, - } + return CompletionResult { + completion_item = CompletionItem { + detail = strings.concatenate({"#", name}) or_else name, + label = name, + kind = .Constant, + }, } }) } @@ -502,6 +504,54 @@ get_comp_lit_completion :: proc( return false } +add_soa_field_completion :: proc( + ast_context: ^AstContext, + selector: Symbol, + expr: ^ast.Expr, + size: ^ast.Expr, + results: ^[dynamic]CompletionResult, + parent_name: string, +) { + if .Soa not_in selector.flags && .SoaPointer not_in selector.flags { + return + } + if symbol, ok := resolve_type_expression(ast_context, expr); ok { + if v, ok := symbol.value.(SymbolStructValue); ok { + for name, i in v.names { + if name == "_" { + continue + } + + resolved := Symbol { + type = .Field, + range = v.ranges[i], + pkg = parent_name, + } + if .SoaPointer in selector.flags { + if s, ok := resolve_type_expression(ast_context, v.types[i]); ok { + resolved.value = s.value + resolved.pkg = symbol.name + } else { + continue + } + } else if size != nil { + resolved.value = SymbolFixedArrayValue { + expr = v.types[i], + len = size, + } + } else { + resolved.value = SymbolMultiPointerValue { + expr = v.types[i], + } + } + resolved.name = name + build_documentation(ast_context, &resolved) + append(results, CompletionResult{symbol = resolved}) + } + } + } +} + get_selector_completion :: proc( ast_context: ^AstContext, position_context: ^DocumentPositionContext, @@ -652,6 +702,7 @@ get_selector_completion :: proc( append(results, CompletionResult{completion_item = item}) } } + add_soa_field_completion(ast_context, selector, v.expr, v.len, results, selector.name) case SymbolUnionValue: is_incomplete = false @@ -728,20 +779,19 @@ get_selector_completion :: proc( for name in enumv.names { append( results, - CompletionResult{ - completion_item = CompletionItem{ + CompletionResult { + completion_item = CompletionItem { label = fmt.tprintf(".%s", name), kind = .EnumMember, detail = fmt.tprintf("%s.%s", selector.name, name), additionalTextEdits = additionalTextEdits, }, - } + }, ) } case SymbolStructValue: is_incomplete = false - for name, i in v.names { if name == "_" { continue @@ -781,7 +831,6 @@ get_selector_completion :: proc( append(results, CompletionResult{completion_item = item}) } } - case SymbolBitFieldValue: is_incomplete = false @@ -841,10 +890,11 @@ get_selector_completion :: proc( case SymbolDynamicArrayValue: is_incomplete = false append_magic_array_like_completion(position_context, selector, results) + add_soa_field_completion(ast_context, selector, v.expr, nil, results, selector.name) case SymbolSliceValue: is_incomplete = false append_magic_array_like_completion(position_context, selector, results) - + add_soa_field_completion(ast_context, selector, v.expr, nil, results, selector.name) case SymbolMapValue: is_incomplete = false append_magic_map_completion(position_context, selector, results) @@ -866,7 +916,7 @@ get_implicit_completion :: proc( ast_context: ^AstContext, position_context: ^DocumentPositionContext, results: ^[dynamic]CompletionResult, -) -> bool{ +) -> bool { is_incomplete := false selector: Symbol @@ -916,10 +966,12 @@ get_implicit_completion :: proc( } } else if v, ok := symbol.value.(SymbolStructValue); ok { if position_context.field_value != nil { - if symbol, ok := resolve_implicit_selector_comp_literal(ast_context, position_context, symbol); ok { + if symbol, ok := resolve_implicit_selector_comp_literal(ast_context, position_context, symbol); + ok { if enum_value, ok := symbol.value.(SymbolEnumValue); ok { for name in enum_value.names { - if position_context.comp_lit != nil && field_exists_in_comp_lit(position_context.comp_lit, name) { + if position_context.comp_lit != nil && + field_exists_in_comp_lit(position_context.comp_lit, name) { continue } item := CompletionItem { @@ -1055,7 +1107,7 @@ get_implicit_completion :: proc( } } } - + //infer bitset and enums based on the identifier comp_lit, i.e. a := My_Struct { my_ident = . } if position_context.comp_lit != nil && position_context.parent_comp_lit != nil { if symbol, ok := resolve_comp_literal(ast_context, position_context); ok { @@ -1240,7 +1292,8 @@ get_implicit_completion :: proc( if enum_value, ok := unwrap_enum(ast_context, arg_type.type); ok { for name in enum_value.names { - if position_context.comp_lit != nil && field_exists_in_comp_lit(position_context.comp_lit, name) { + if position_context.comp_lit != nil && + field_exists_in_comp_lit(position_context.comp_lit, name) { continue } item := CompletionItem { @@ -1402,9 +1455,9 @@ get_identifier_completion :: proc( if symbol, ok := resolve_type_identifier(ast_context, ident^); ok { if score, ok := common.fuzzy_match(matcher, ident.name); ok == 1 { - symbol.type_name = symbol.name - symbol.type_pkg = symbol.pkg - symbol.name = clean_ident(ident.name) + symbol.type_name = symbol.name + symbol.type_pkg = symbol.pkg + symbol.name = clean_ident(ident.name) append(results, CompletionResult{score = score * 1.1, symbol = symbol}) } } @@ -1912,6 +1965,12 @@ append_magic_array_like_completion :: proc( symbol: Symbol, results: ^[dynamic]CompletionResult, ) { + // Can't iterate over an soa pointer + // eg foos: #soa^#soa[]struct{} + if .SoaPointer in symbol.flags { + return + } + range, ok := get_range_from_selection_start_to_dot(position_context) if !ok { diff --git a/src/server/documentation.odin b/src/server/documentation.odin index 2f73ed2..dc0784d 100644 --- a/src/server/documentation.odin +++ b/src/server/documentation.odin @@ -372,15 +372,36 @@ write_short_signature :: proc(sb: ^strings.Builder, ast_context: ^AstContext, sy write_node(sb, ast_context, v.expr, "", short_signature = true) return case SymbolDynamicArrayValue: - fmt.sbprintf(sb, "%s[dynamic]", pointer_prefix) + if .SoaPointer in symbol.flags { + strings.write_string(sb, "#soa") + } + strings.write_string(sb, pointer_prefix) + if .Soa in symbol.flags { + strings.write_string(sb, "#soa") + } + strings.write_string(sb, "[dynamic]") write_node(sb, ast_context, v.expr, "", short_signature = true) return case SymbolSliceValue: - fmt.sbprintf(sb, "%s[]", pointer_prefix) + if .SoaPointer in symbol.flags { + strings.write_string(sb, "#soa") + } + strings.write_string(sb, pointer_prefix) + if .Soa in symbol.flags { + strings.write_string(sb, "#soa") + } + strings.write_string(sb, "[]") write_node(sb, ast_context, v.expr, "", short_signature = true) return case SymbolFixedArrayValue: - fmt.sbprintf(sb, "%s[", pointer_prefix) + if .SoaPointer in symbol.flags { + strings.write_string(sb, "#soa") + } + strings.write_string(sb, pointer_prefix) + if .Soa in symbol.flags { + strings.write_string(sb, "#soa") + } + strings.write_string(sb, "[") build_string_node(v.len, sb, false) strings.write_string(sb, "]") write_node(sb, ast_context, v.expr, "", short_signature = true) diff --git a/src/server/hover.odin b/src/server/hover.odin index 2c68ad0..03cdedf 100644 --- a/src/server/hover.odin +++ b/src/server/hover.odin @@ -153,7 +153,12 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> position_context.value_decl.names[0], ); ok { if value, ok := struct_symbol.value.(SymbolStructValue); ok { - construct_struct_field_symbol(&symbol, struct_symbol.name, value, field_index+name_index) + construct_struct_field_symbol( + &symbol, + struct_symbol.name, + value, + field_index + name_index, + ) build_documentation(&ast_context, &symbol, true) hover.contents = write_hover_content(&ast_context, symbol) return hover, true, true @@ -188,7 +193,8 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> } } - if position_context.field_value != nil && position_in_node(position_context.field_value.field, position_context.position) { + if position_context.field_value != nil && + position_in_node(position_context.field_value.field, position_context.position) { if position_context.comp_lit != nil { if comp_symbol, ok := resolve_comp_literal(&ast_context, &position_context); ok { if field, ok := position_context.field_value.field.derived.(^ast.Ident); ok { @@ -347,6 +353,12 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> return hover, true, true } } + case SymbolSliceValue: + return get_soa_hover(&ast_context, selector, v.expr, nil, field) + case SymbolDynamicArrayValue: + return get_soa_hover(&ast_context, selector, v.expr, nil, field) + case SymbolFixedArrayValue: + return get_soa_hover(&ast_context, selector, v.expr, v.len, field) } } else if position_context.implicit_selector_expr != nil { implicit_selector := position_context.implicit_selector_expr @@ -425,3 +437,30 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> return hover, false, true } + +@(private = "file") +get_soa_hover :: proc( + ast_context: ^AstContext, + selector: Symbol, + expr: ^ast.Expr, + size: ^ast.Expr, + field: string, +) -> ( + Hover, + bool, + bool, +) { + if .SoaPointer not_in selector.flags && .Soa not_in selector.flags { + return {}, false, true + } + if symbol, ok := resolve_soa_selector_field(ast_context, selector, expr, size, field); ok { + if selector.name != "" { + symbol.pkg = selector.name + } + build_documentation(ast_context, &symbol, false) + hover: Hover + hover.contents = write_hover_content(ast_context, symbol) + return hover, true, true + } + return {}, false, true +} diff --git a/src/server/symbol.odin b/src/server/symbol.odin index 2e2f26b..d163e45 100644 --- a/src/server/symbol.odin +++ b/src/server/symbol.odin @@ -197,6 +197,7 @@ SymbolFlag :: enum { ObjC, ObjCIsClassMethod, // should be set true only when ObjC is enabled Soa, + SoaPointer, Parameter, //If the symbol is a procedure argument } |