From 4eca8c2b02cb03f6de910b240439117e26192e28 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Sun, 17 Aug 2025 18:53:10 -0400 Subject: Add #soa to hover information --- src/server/ast.odin | 2 ++ src/server/documentation.odin | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'src/server') diff --git a/src/server/ast.odin b/src/server/ast.odin index f33ac6b..f7f3083 100644 --- a/src/server/ast.odin +++ b/src/server/ast.odin @@ -1221,11 +1221,13 @@ build_string_node :: proc(node: ^ast.Node, builder: ^strings.Builder, remove_poi } 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/documentation.odin b/src/server/documentation.odin index 2f73ed2..84d8c2e 100644 --- a/src/server/documentation.odin +++ b/src/server/documentation.odin @@ -372,15 +372,27 @@ 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) + 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) + 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) + 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) -- cgit v1.2.3 From d2af943d920f3ab63bae34189111ab5ee9b0a5cc Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Sun, 17 Aug 2025 19:35:30 -0400 Subject: Add field completions for soa arrays --- src/server/completion.odin | 161 ++++++++++++++++++++++++++------------------ tests/completions_test.odin | 18 +++++ 2 files changed, 115 insertions(+), 64 deletions(-) (limited to 'src/server') diff --git a/src/server/completion.odin b/src/server/completion.odin index 9bb2f3b..d126301 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_struct_field_completion :: proc( + ast_context: ^AstContext, + position_context: ^DocumentPositionContext, + results: ^[dynamic]CompletionResult, + selector: Symbol, + v: SymbolStructValue, +) { + for name, i in v.names { + if name == "_" { + continue + } + + if symbol, ok := resolve_type_expression(ast_context, v.types[i]); ok { + if expr, ok := position_context.selector.derived.(^ast.Selector_Expr); ok { + if expr.op.text == "->" && symbol.type != .Function { + continue + } + } + + if position_context.arrow { + if symbol.type != .Function && symbol.type != .Type_Function { + continue + } + if .ObjCIsClassMethod in symbol.flags { + assert(.ObjC in symbol.flags) + continue + } + } + if !position_context.arrow && .ObjC in selector.flags { + continue + } + + construct_struct_field_symbol(&symbol, selector.name, v, i) + append(results, CompletionResult{symbol = symbol}) + } else { + //just give some generic symbol with name. + item := CompletionItem { + label = symbol.name, + kind = .Field, + detail = fmt.tprintf("%v: %v", name, node_to_string(v.types[i])), + documentation = symbol.doc, + } + + append(results, CompletionResult{completion_item = item}) + } + } +} + get_selector_completion :: proc( ast_context: ^AstContext, position_context: ^DocumentPositionContext, @@ -652,6 +702,13 @@ get_selector_completion :: proc( append(results, CompletionResult{completion_item = item}) } } + if .Soa in selector.flags { + if symbol, ok := resolve_type_expression(ast_context, v.expr); ok { + if v, ok := symbol.value.(SymbolStructValue); ok { + add_struct_field_completion(ast_context, position_context, results, symbol, v) + } + } + } case SymbolUnionValue: is_incomplete = false @@ -728,60 +785,20 @@ 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 - } - - if symbol, ok := resolve_type_expression(ast_context, v.types[i]); ok { - if expr, ok := position_context.selector.derived.(^ast.Selector_Expr); ok { - if expr.op.text == "->" && symbol.type != .Function { - continue - } - } - - if position_context.arrow { - if symbol.type != .Function && symbol.type != .Type_Function { - continue - } - if .ObjCIsClassMethod in symbol.flags { - assert(.ObjC in symbol.flags) - continue - } - } - if !position_context.arrow && .ObjC in selector.flags { - continue - } - - construct_struct_field_symbol(&symbol, selector.name, v, i) - append(results, CompletionResult{symbol = symbol}) - } else { - //just give some generic symbol with name. - item := CompletionItem { - label = symbol.name, - kind = .Field, - detail = fmt.tprintf("%v: %v", name, node_to_string(v.types[i])), - documentation = symbol.doc, - } - - append(results, CompletionResult{completion_item = item}) - } - } - + add_struct_field_completion(ast_context, position_context, results, selector, v) case SymbolBitFieldValue: is_incomplete = false @@ -841,10 +858,23 @@ get_selector_completion :: proc( case SymbolDynamicArrayValue: is_incomplete = false append_magic_array_like_completion(position_context, selector, results) + if .Soa in selector.flags { + if symbol, ok := resolve_type_expression(ast_context, v.expr); ok { + if v, ok := symbol.value.(SymbolStructValue); ok { + add_struct_field_completion(ast_context, position_context, results, symbol, v) + } + } + } case SymbolSliceValue: is_incomplete = false append_magic_array_like_completion(position_context, selector, results) - + if .Soa in selector.flags { + if symbol, ok := resolve_type_expression(ast_context, v.expr); ok { + if v, ok := symbol.value.(SymbolStructValue); ok { + add_struct_field_completion(ast_context, position_context, results, symbol, v) + } + } + } case SymbolMapValue: is_incomplete = false append_magic_map_completion(position_context, selector, results) @@ -866,7 +896,7 @@ get_implicit_completion :: proc( ast_context: ^AstContext, position_context: ^DocumentPositionContext, results: ^[dynamic]CompletionResult, -) -> bool{ +) -> bool { is_incomplete := false selector: Symbol @@ -916,10 +946,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 +1087,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 +1272,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 +1435,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}) } } diff --git a/tests/completions_test.odin b/tests/completions_test.odin index 0b2bc5f..df03165 100644 --- a/tests/completions_test.odin +++ b/tests/completions_test.odin @@ -4415,3 +4415,21 @@ ast_completion_basic_type_other_pkg :: proc(t: ^testing.T) { test.expect_completion_docs(t, &source, "", {"my_package.foo: int"}) } + +@(test) +ast_completion_soa_slice_fields :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x, y: int, + } + + main :: proc() { + foos: #soa[]Foo + foos.{*} + } + `, + } + + test.expect_completion_docs(t, &source, "", {"Foo.x: int", "Foo.y: int"}) +} -- cgit v1.2.3 From 11be114305845bb0bc2674d8c1d8324e1011256a Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:12:19 -0400 Subject: Add hover information for soa fields and variables --- src/server/analysis.odin | 37 +++++++++++++++++++++++++++++++++++-- src/server/hover.odin | 9 +++++++++ tests/hover_test.odin | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 3 deletions(-) (limited to 'src/server') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 1524112..47a8c0a 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 @@ -1300,6 +1301,26 @@ resolve_type_assertion_expr :: proc(ast_context: ^AstContext, v: ^ast.Type_Asser return symbol, ok } +resolve_soa_selector_field :: proc(ast_context: ^AstContext, expr: ^ast.Expr, name: string) -> (Symbol, bool) { + 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 { + value := SymbolMultiPointerValue { + expr = v.types[i], + } + symbol.name = name + symbol.type = .Field + symbol.value = value + 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 +1331,9 @@ resolve_selector_expression :: proc(ast_context: ^AstContext, node: ^ast.Selecto symbol := Symbol{} #partial switch s in selector.value { case SymbolFixedArrayValue: + if .Soa in selector.flags { + return resolve_soa_selector_field(ast_context, s.expr, node.field.name) + } 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 +1406,14 @@ 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: + if .Soa in selector.flags { + return resolve_soa_selector_field(ast_context, s.expr, node.field.name) + } + case SymbolDynamicArrayValue: + if .Soa in selector.flags { + return resolve_soa_selector_field(ast_context, s.expr, node.field.name) + } } } @@ -1620,7 +1652,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 = diff --git a/src/server/hover.odin b/src/server/hover.odin index 2c68ad0..44fde02 100644 --- a/src/server/hover.odin +++ b/src/server/hover.odin @@ -347,6 +347,15 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> return hover, true, true } } + case SymbolSliceValue: + if .Soa in selector.flags { + if symbol, ok := resolve_soa_selector_field(&ast_context, v.expr, field); ok { + symbol.pkg = selector.name + build_documentation(&ast_context, &symbol, false) + hover.contents = write_hover_content(&ast_context, symbol) + return hover, true, true + } + } } } else if position_context.implicit_selector_expr != nil { implicit_selector := position_context.implicit_selector_expr diff --git a/tests/hover_test.odin b/tests/hover_test.odin index 6427c34..7201c8d 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -4148,7 +4148,7 @@ ast_hover_soa_slice :: proc(t: ^testing.T) { } @(test) -ast_hover_soa_struct_field :: proc(t: ^testing.T) { +ast_hover_struct_with_soa_field :: proc(t: ^testing.T) { source := test.Source { main = `package test Foo :: struct { @@ -4163,6 +4163,39 @@ ast_hover_soa_struct_field :: proc(t: ^testing.T) { test.expect_hover(t, &source, "test.Bar: struct {\n\tfoos: #soa[5]Foo,\n}") } +@(test) +ast_hover_soa_slice_field :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x, y: int, + } + + main :: proc() { + foos: #soa[]Foo + foos.x{*} + } + `, + } + test.expect_hover(t, &source, "foos.x: [^]int") +} + +@(test) +ast_hover_identifier_soa_slice_field :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x, y: int, + } + + main :: proc() { + foos: #soa[]Foo + x{*} := foos.x + } + `, + } + test.expect_hover(t, &source, "test.x: [^]int") +} /* Waiting for odin fix -- cgit v1.2.3 From 09f6cfa658b77504f1fd1f7c20eb3e15ef473030 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:30:44 -0400 Subject: Fix completions for soa fields --- src/server/analysis.odin | 1 + src/server/completion.odin | 110 ++++++++++++++++++++++---------------------- tests/completions_test.odin | 5 +- 3 files changed, 59 insertions(+), 57 deletions(-) (limited to 'src/server') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 47a8c0a..57ad029 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -1312,6 +1312,7 @@ resolve_soa_selector_field :: proc(ast_context: ^AstContext, expr: ^ast.Expr, na symbol.name = name symbol.type = .Field symbol.value = value + symbol.range = v.ranges[i] return symbol, true } } diff --git a/src/server/completion.odin b/src/server/completion.odin index d126301..ea55091 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -504,50 +504,24 @@ get_comp_lit_completion :: proc( return false } -add_struct_field_completion :: proc( - ast_context: ^AstContext, - position_context: ^DocumentPositionContext, - results: ^[dynamic]CompletionResult, - selector: Symbol, - v: SymbolStructValue, -) { - for name, i in v.names { - if name == "_" { - continue - } - - if symbol, ok := resolve_type_expression(ast_context, v.types[i]); ok { - if expr, ok := position_context.selector.derived.(^ast.Selector_Expr); ok { - if expr.op.text == "->" && symbol.type != .Function { +add_soa_field_completion :: proc(ast_context: ^AstContext, expr: ^ast.Expr, results: ^[dynamic]CompletionResult, parent_name: string) { + 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 } - } - if position_context.arrow { - if symbol.type != .Function && symbol.type != .Type_Function { - continue + resolved := Symbol { + name = name, + type = .Field, + range = v.ranges[i], + pkg = parent_name, + value = SymbolMultiPointerValue{expr = v.types[i]}, } - if .ObjCIsClassMethod in symbol.flags { - assert(.ObjC in symbol.flags) - continue - } - } - if !position_context.arrow && .ObjC in selector.flags { - continue + build_documentation(ast_context, &resolved) + append(results, CompletionResult{symbol = resolved}) } - - construct_struct_field_symbol(&symbol, selector.name, v, i) - append(results, CompletionResult{symbol = symbol}) - } else { - //just give some generic symbol with name. - item := CompletionItem { - label = symbol.name, - kind = .Field, - detail = fmt.tprintf("%v: %v", name, node_to_string(v.types[i])), - documentation = symbol.doc, - } - - append(results, CompletionResult{completion_item = item}) } } } @@ -703,11 +677,7 @@ get_selector_completion :: proc( } } if .Soa in selector.flags { - if symbol, ok := resolve_type_expression(ast_context, v.expr); ok { - if v, ok := symbol.value.(SymbolStructValue); ok { - add_struct_field_completion(ast_context, position_context, results, symbol, v) - } - } + add_soa_field_completion(ast_context, v.expr, results, selector.name) } case SymbolUnionValue: is_incomplete = false @@ -798,7 +768,45 @@ get_selector_completion :: proc( case SymbolStructValue: is_incomplete = false - add_struct_field_completion(ast_context, position_context, results, selector, v) + for name, i in v.names { + if name == "_" { + continue + } + + if symbol, ok := resolve_type_expression(ast_context, v.types[i]); ok { + if expr, ok := position_context.selector.derived.(^ast.Selector_Expr); ok { + if expr.op.text == "->" && symbol.type != .Function { + continue + } + } + + if position_context.arrow { + if symbol.type != .Function && symbol.type != .Type_Function { + continue + } + if .ObjCIsClassMethod in symbol.flags { + assert(.ObjC in symbol.flags) + continue + } + } + if !position_context.arrow && .ObjC in selector.flags { + continue + } + + construct_struct_field_symbol(&symbol, selector.name, v, i) + append(results, CompletionResult{symbol = symbol}) + } else { + //just give some generic symbol with name. + item := CompletionItem { + label = symbol.name, + kind = .Field, + detail = fmt.tprintf("%v: %v", name, node_to_string(v.types[i])), + documentation = symbol.doc, + } + + append(results, CompletionResult{completion_item = item}) + } + } case SymbolBitFieldValue: is_incomplete = false @@ -859,21 +867,13 @@ get_selector_completion :: proc( is_incomplete = false append_magic_array_like_completion(position_context, selector, results) if .Soa in selector.flags { - if symbol, ok := resolve_type_expression(ast_context, v.expr); ok { - if v, ok := symbol.value.(SymbolStructValue); ok { - add_struct_field_completion(ast_context, position_context, results, symbol, v) - } - } + add_soa_field_completion(ast_context, v.expr, results, selector.name) } case SymbolSliceValue: is_incomplete = false append_magic_array_like_completion(position_context, selector, results) if .Soa in selector.flags { - if symbol, ok := resolve_type_expression(ast_context, v.expr); ok { - if v, ok := symbol.value.(SymbolStructValue); ok { - add_struct_field_completion(ast_context, position_context, results, symbol, v) - } - } + add_soa_field_completion(ast_context, v.expr, results, selector.name) } case SymbolMapValue: is_incomplete = false diff --git a/tests/completions_test.odin b/tests/completions_test.odin index df03165..3c57a87 100644 --- a/tests/completions_test.odin +++ b/tests/completions_test.odin @@ -4421,7 +4421,8 @@ ast_completion_soa_slice_fields :: proc(t: ^testing.T) { source := test.Source { main = `package test Foo :: struct { - x, y: int, + x: int, + y: string, } main :: proc() { @@ -4431,5 +4432,5 @@ ast_completion_soa_slice_fields :: proc(t: ^testing.T) { `, } - test.expect_completion_docs(t, &source, "", {"Foo.x: int", "Foo.y: int"}) + test.expect_completion_docs(t, &source, "", {"foos.x: [^]int", "foos.y: [^]string"}) } -- cgit v1.2.3 From c96f264f8df3e83d82471f54b63e872ce5116f01 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:38:26 -0400 Subject: Handle references and go to definition for soa fields --- src/server/analysis.odin | 12 ++++++++++++ tests/definition_test.odin | 21 +++++++++++++++++++++ tests/hover_test.odin | 2 +- tests/references_test.odin | 22 ++++++++++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) (limited to 'src/server') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 57ad029..19c4e0a 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -2630,6 +2630,18 @@ 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: + if .Soa in symbol.flags { + return resolve_soa_selector_field(ast_context, v.expr, field) + } + case SymbolDynamicArrayValue: + if .Soa in symbol.flags { + return resolve_soa_selector_field(ast_context, v.expr, field) + } + case SymbolFixedArrayValue: + if .Soa in symbol.flags { + return resolve_soa_selector_field(ast_context, v.expr, field) + } } return symbol, true diff --git a/tests/definition_test.odin b/tests/definition_test.odin index e41cae4..261ef83 100644 --- a/tests/definition_test.odin +++ b/tests/definition_test.odin @@ -606,3 +606,24 @@ ast_goto_enum_struct_field_without_name :: proc(t: ^testing.T) { test.expect_definition_locations(t, &source, locations[:]) } + +@(test) +ast_goto_soa_field :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x, y: int, + } + + main :: proc() { + foos: #soa[]Foo + x := foos.x{*} + } + `, + } + locations := []common.Location { + {range = {start = {line = 2, character = 3}, end = {line = 2, character = 4}}}, + } + + test.expect_definition_locations(t, &source, locations[:]) +} diff --git a/tests/hover_test.odin b/tests/hover_test.odin index 7201c8d..b48c98f 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -4144,7 +4144,7 @@ ast_hover_soa_slice :: proc(t: ^testing.T) { } `, } - test.expect_hover(t, &source, "test.Foo: #soa[4]u8") + test.expect_hover(t, &source, "test.foos: #soa[]Foo") } @(test) diff --git a/tests/references_test.odin b/tests/references_test.odin index fb409e6..aa3c6b5 100644 --- a/tests/references_test.odin +++ b/tests/references_test.odin @@ -1148,3 +1148,25 @@ ast_references_poly_type :: proc(t: ^testing.T) { test.expect_reference_locations(t, &source, locations[:]) } + +@(test) +ast_references_soa_field :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x, y: int, + } + + main :: proc() { + foos: #soa[]Foo + x := foos.x{*} + } + `, + } + locations := []common.Location { + {range = {start = {line = 2, character = 3}, end = {line = 2, character = 4}}}, + {range = {start = {line = 7, character = 13}, end = {line = 7, character = 14}}}, + } + + test.expect_reference_locations(t, &source, locations[:]) +} -- cgit v1.2.3 From ad58adbd01d1f0481feceb5381f53adba541d021 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:54:10 -0400 Subject: Correctly handle fixed array #soa --- src/server/analysis.odin | 27 +++++++++++++++++---------- src/server/completion.odin | 31 +++++++++++++++++++++++-------- src/server/hover.odin | 20 +++++++++++++++++++- tests/completions_test.odin | 19 +++++++++++++++++++ tests/hover_test.odin | 17 +++++++++++++++++ 5 files changed, 95 insertions(+), 19 deletions(-) (limited to 'src/server') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 19c4e0a..d1282bb 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -1301,17 +1301,24 @@ resolve_type_assertion_expr :: proc(ast_context: ^AstContext, v: ^ast.Type_Asser return symbol, ok } -resolve_soa_selector_field :: proc(ast_context: ^AstContext, expr: ^ast.Expr, name: string) -> (Symbol, bool) { +resolve_soa_selector_field :: proc(ast_context: ^AstContext, expr: ^ast.Expr, size: ^ast.Expr, name: string) -> (Symbol, bool) { 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 { - value := SymbolMultiPointerValue { - expr = v.types[i], + 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.value = value symbol.range = v.ranges[i] return symbol, true } @@ -1333,7 +1340,7 @@ resolve_selector_expression :: proc(ast_context: ^AstContext, node: ^ast.Selecto #partial switch s in selector.value { case SymbolFixedArrayValue: if .Soa in selector.flags { - return resolve_soa_selector_field(ast_context, s.expr, node.field.name) + return resolve_soa_selector_field(ast_context, s.expr, s.len, node.field.name) } components_count := 0 for c in node.field.name { @@ -1409,11 +1416,11 @@ resolve_selector_expression :: proc(ast_context: ^AstContext, node: ^ast.Selecto return selector, true case SymbolSliceValue: if .Soa in selector.flags { - return resolve_soa_selector_field(ast_context, s.expr, node.field.name) + return resolve_soa_selector_field(ast_context, s.expr, nil, node.field.name) } case SymbolDynamicArrayValue: if .Soa in selector.flags { - return resolve_soa_selector_field(ast_context, s.expr, node.field.name) + return resolve_soa_selector_field(ast_context, s.expr, nil, node.field.name) } } } @@ -2632,15 +2639,15 @@ resolve_symbol_selector :: proc( } case SymbolSliceValue: if .Soa in symbol.flags { - return resolve_soa_selector_field(ast_context, v.expr, field) + return resolve_soa_selector_field(ast_context, v.expr, nil, field) } case SymbolDynamicArrayValue: if .Soa in symbol.flags { - return resolve_soa_selector_field(ast_context, v.expr, field) + return resolve_soa_selector_field(ast_context, v.expr, nil, field) } case SymbolFixedArrayValue: if .Soa in symbol.flags { - return resolve_soa_selector_field(ast_context, v.expr, field) + return resolve_soa_selector_field(ast_context, v.expr, v.len, field) } } diff --git a/src/server/completion.odin b/src/server/completion.odin index ea55091..9f795cc 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -504,7 +504,13 @@ get_comp_lit_completion :: proc( return false } -add_soa_field_completion :: proc(ast_context: ^AstContext, expr: ^ast.Expr, results: ^[dynamic]CompletionResult, parent_name: string) { +add_soa_field_completion :: proc( + ast_context: ^AstContext, + expr: ^ast.Expr, + size: ^ast.Expr, + results: ^[dynamic]CompletionResult, + parent_name: string, +) { if symbol, ok := resolve_type_expression(ast_context, expr); ok { if v, ok := symbol.value.(SymbolStructValue); ok { for name, i in v.names { @@ -513,11 +519,20 @@ add_soa_field_completion :: proc(ast_context: ^AstContext, expr: ^ast.Expr, resu } resolved := Symbol { - name = name, - type = .Field, + name = name, + type = .Field, range = v.ranges[i], - pkg = parent_name, - value = SymbolMultiPointerValue{expr = v.types[i]}, + pkg = parent_name, + } + if size != nil { + resolved.value = SymbolFixedArrayValue{ + expr = v.types[i], + len = size, + } + } else { + resolved.value = SymbolMultiPointerValue { + expr = v.types[i], + } } build_documentation(ast_context, &resolved) append(results, CompletionResult{symbol = resolved}) @@ -677,7 +692,7 @@ get_selector_completion :: proc( } } if .Soa in selector.flags { - add_soa_field_completion(ast_context, v.expr, results, selector.name) + add_soa_field_completion(ast_context, v.expr, v.len, results, selector.name) } case SymbolUnionValue: is_incomplete = false @@ -867,13 +882,13 @@ get_selector_completion :: proc( is_incomplete = false append_magic_array_like_completion(position_context, selector, results) if .Soa in selector.flags { - add_soa_field_completion(ast_context, v.expr, results, selector.name) + add_soa_field_completion(ast_context, v.expr, nil, results, selector.name) } case SymbolSliceValue: is_incomplete = false append_magic_array_like_completion(position_context, selector, results) if .Soa in selector.flags { - add_soa_field_completion(ast_context, v.expr, results, selector.name) + add_soa_field_completion(ast_context, v.expr, nil, results, selector.name) } case SymbolMapValue: is_incomplete = false diff --git a/src/server/hover.odin b/src/server/hover.odin index 44fde02..a36dc54 100644 --- a/src/server/hover.odin +++ b/src/server/hover.odin @@ -349,7 +349,25 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> } case SymbolSliceValue: if .Soa in selector.flags { - if symbol, ok := resolve_soa_selector_field(&ast_context, v.expr, field); ok { + if symbol, ok := resolve_soa_selector_field(&ast_context, v.expr, nil, field); ok { + symbol.pkg = selector.name + build_documentation(&ast_context, &symbol, false) + hover.contents = write_hover_content(&ast_context, symbol) + return hover, true, true + } + } + case SymbolDynamicArrayValue: + if .Soa in selector.flags { + if symbol, ok := resolve_soa_selector_field(&ast_context, v.expr, nil, field); ok { + symbol.pkg = selector.name + build_documentation(&ast_context, &symbol, false) + hover.contents = write_hover_content(&ast_context, symbol) + return hover, true, true + } + } + case SymbolFixedArrayValue: + if .Soa in selector.flags { + if symbol, ok := resolve_soa_selector_field(&ast_context, v.expr, v.len, field); ok { symbol.pkg = selector.name build_documentation(&ast_context, &symbol, false) hover.contents = write_hover_content(&ast_context, symbol) diff --git a/tests/completions_test.odin b/tests/completions_test.odin index 3c57a87..6a5e1a7 100644 --- a/tests/completions_test.odin +++ b/tests/completions_test.odin @@ -4434,3 +4434,22 @@ ast_completion_soa_slice_fields :: proc(t: ^testing.T) { test.expect_completion_docs(t, &source, "", {"foos.x: [^]int", "foos.y: [^]string"}) } + +@(test) +ast_completion_soa_fixed_array_fields :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x: int, + y: string, + } + + main :: proc() { + foos: #soa[3]Foo + foos.{*} + } + `, + } + + test.expect_completion_docs(t, &source, "", {"foos.x: [3]int", "foos.y: [3]string"}) +} diff --git a/tests/hover_test.odin b/tests/hover_test.odin index b48c98f..53414e5 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -4196,6 +4196,23 @@ ast_hover_identifier_soa_slice_field :: proc(t: ^testing.T) { } test.expect_hover(t, &source, "test.x: [^]int") } + +@(test) +ast_hover_soa_fixed_array_field :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x, y: int, + } + + main :: proc() { + foos: #soa[6]Foo + foos.x{*} + } + `, + } + test.expect_hover(t, &source, "foos.x: [6]int") +} /* Waiting for odin fix -- cgit v1.2.3 From cad56817a9b39819fc5a4f05bdc01dd0c5f50f13 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Sun, 17 Aug 2025 21:28:38 -0400 Subject: Add support for soa pointers --- src/server/analysis.odin | 55 +++++++++++++++++++++++-------------- src/server/ast.odin | 10 +++++++ src/server/completion.odin | 31 ++++++++++++--------- src/server/documentation.odin | 9 ++++++ src/server/hover.odin | 64 +++++++++++++++++++++++++------------------ src/server/symbol.odin | 1 + tests/completions_test.odin | 19 +++++++++++++ tests/hover_test.odin | 33 ++++++++++++++++++++++ tests/references_test.odin | 22 +++++++++++++++ 9 files changed, 184 insertions(+), 60 deletions(-) (limited to 'src/server') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index d1282bb..e2ce2a6 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -1145,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 { @@ -1301,15 +1304,35 @@ resolve_type_assertion_expr :: proc(ast_context: ^AstContext, v: ^ast.Type_Asser return symbol, ok } -resolve_soa_selector_field :: proc(ast_context: ^AstContext, expr: ^ast.Expr, size: ^ast.Expr, name: string) -> (Symbol, bool) { +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 size != nil { - symbol.value = SymbolFixedArrayValue{ + 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, + len = size, } } else { symbol.value = SymbolMultiPointerValue { @@ -1339,8 +1362,8 @@ resolve_selector_expression :: proc(ast_context: ^AstContext, node: ^ast.Selecto symbol := Symbol{} #partial switch s in selector.value { case SymbolFixedArrayValue: - if .Soa in selector.flags { - return resolve_soa_selector_field(ast_context, s.expr, s.len, node.field.name) + 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 { @@ -1415,13 +1438,9 @@ resolve_selector_expression :: proc(ast_context: ^AstContext, node: ^ast.Selecto selector.type = .EnumMember return selector, true case SymbolSliceValue: - if .Soa in selector.flags { - return resolve_soa_selector_field(ast_context, s.expr, nil, node.field.name) - } + return resolve_soa_selector_field(ast_context, selector, s.expr, nil, node.field.name) case SymbolDynamicArrayValue: - if .Soa in selector.flags { - return resolve_soa_selector_field(ast_context, s.expr, nil, node.field.name) - } + return resolve_soa_selector_field(ast_context, selector, s.expr, nil, node.field.name) } } @@ -2638,17 +2657,11 @@ resolve_symbol_selector :: proc( return resolve_symbol_selector(ast_context, selector, s) } case SymbolSliceValue: - if .Soa in symbol.flags { - return resolve_soa_selector_field(ast_context, v.expr, nil, field) - } + return resolve_soa_selector_field(ast_context, symbol, v.expr, nil, field) case SymbolDynamicArrayValue: - if .Soa in symbol.flags { - return resolve_soa_selector_field(ast_context, v.expr, nil, field) - } + return resolve_soa_selector_field(ast_context, symbol, v.expr, nil, field) case SymbolFixedArrayValue: - if .Soa in symbol.flags { - return resolve_soa_selector_field(ast_context, v.expr, v.len, field) - } + 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 f7f3083..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,6 +1225,7 @@ 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, "^") } diff --git a/src/server/completion.odin b/src/server/completion.odin index 9f795cc..c8f43f7 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -506,11 +506,15 @@ get_comp_lit_completion :: proc( 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 { @@ -519,21 +523,28 @@ add_soa_field_completion :: proc( } resolved := Symbol { - name = name, type = .Field, range = v.ranges[i], pkg = parent_name, } - if size != nil { - resolved.value = SymbolFixedArrayValue{ + 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, + len = size, } } else { resolved.value = SymbolMultiPointerValue { expr = v.types[i], } } + resolved.name = name build_documentation(ast_context, &resolved) append(results, CompletionResult{symbol = resolved}) } @@ -691,9 +702,7 @@ get_selector_completion :: proc( append(results, CompletionResult{completion_item = item}) } } - if .Soa in selector.flags { - add_soa_field_completion(ast_context, v.expr, v.len, results, selector.name) - } + add_soa_field_completion(ast_context, selector, v.expr, v.len, results, selector.name) case SymbolUnionValue: is_incomplete = false @@ -881,15 +890,11 @@ get_selector_completion :: proc( case SymbolDynamicArrayValue: is_incomplete = false append_magic_array_like_completion(position_context, selector, results) - if .Soa in selector.flags { - add_soa_field_completion(ast_context, v.expr, nil, results, selector.name) - } + 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) - if .Soa in selector.flags { - add_soa_field_completion(ast_context, v.expr, nil, results, selector.name) - } + 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) diff --git a/src/server/documentation.odin b/src/server/documentation.odin index 84d8c2e..dc0784d 100644 --- a/src/server/documentation.odin +++ b/src/server/documentation.odin @@ -372,6 +372,9 @@ write_short_signature :: proc(sb: ^strings.Builder, ast_context: ^AstContext, sy write_node(sb, ast_context, v.expr, "", short_signature = true) return case SymbolDynamicArrayValue: + 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") @@ -380,6 +383,9 @@ write_short_signature :: proc(sb: ^strings.Builder, ast_context: ^AstContext, sy write_node(sb, ast_context, v.expr, "", short_signature = true) return case SymbolSliceValue: + 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") @@ -388,6 +394,9 @@ write_short_signature :: proc(sb: ^strings.Builder, ast_context: ^AstContext, sy write_node(sb, ast_context, v.expr, "", short_signature = true) return case SymbolFixedArrayValue: + 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") diff --git a/src/server/hover.odin b/src/server/hover.odin index a36dc54..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 { @@ -348,32 +354,11 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> } } case SymbolSliceValue: - if .Soa in selector.flags { - if symbol, ok := resolve_soa_selector_field(&ast_context, v.expr, nil, field); ok { - symbol.pkg = selector.name - build_documentation(&ast_context, &symbol, false) - hover.contents = write_hover_content(&ast_context, symbol) - return hover, true, true - } - } + return get_soa_hover(&ast_context, selector, v.expr, nil, field) case SymbolDynamicArrayValue: - if .Soa in selector.flags { - if symbol, ok := resolve_soa_selector_field(&ast_context, v.expr, nil, field); ok { - symbol.pkg = selector.name - build_documentation(&ast_context, &symbol, false) - hover.contents = write_hover_content(&ast_context, symbol) - return hover, true, true - } - } + return get_soa_hover(&ast_context, selector, v.expr, nil, field) case SymbolFixedArrayValue: - if .Soa in selector.flags { - if symbol, ok := resolve_soa_selector_field(&ast_context, v.expr, v.len, field); ok { - symbol.pkg = selector.name - build_documentation(&ast_context, &symbol, false) - hover.contents = write_hover_content(&ast_context, symbol) - return hover, true, true - } - } + 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 @@ -452,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 } diff --git a/tests/completions_test.odin b/tests/completions_test.odin index 6a5e1a7..9fcc797 100644 --- a/tests/completions_test.odin +++ b/tests/completions_test.odin @@ -4453,3 +4453,22 @@ ast_completion_soa_fixed_array_fields :: proc(t: ^testing.T) { test.expect_completion_docs(t, &source, "", {"foos.x: [3]int", "foos.y: [3]string"}) } + +@(test) +ast_completion_soa_pointer :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x: int, + y: string, + } + + main :: proc() { + foos: #soa^#soa[3]Foo + foos.{*} + } + `, + } + + test.expect_completion_docs(t, &source, "", {"Foo.x: int", "Foo.y: string"}) +} diff --git a/tests/hover_test.odin b/tests/hover_test.odin index 53414e5..7301a7e 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -4213,6 +4213,39 @@ ast_hover_soa_fixed_array_field :: proc(t: ^testing.T) { } test.expect_hover(t, &source, "foos.x: [6]int") } + +@(test) +ast_hover_soa_pointer :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x, y: int, + } + + main :: proc() { + f{*}oo: #soa^#soa[6]Foo + } + `, + } + test.expect_hover(t, &source, "test.foo: #soa^#soa[6]Foo") +} + +@(test) +ast_hover_soa_pointer_field :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x, y: int, + } + + main :: proc() { + foo: #soa^#soa[6]Foo + foo.x{*} + } + `, + } + test.expect_hover(t, &source, "Foo.x: int") +} /* Waiting for odin fix diff --git a/tests/references_test.odin b/tests/references_test.odin index aa3c6b5..3510283 100644 --- a/tests/references_test.odin +++ b/tests/references_test.odin @@ -1170,3 +1170,25 @@ ast_references_soa_field :: proc(t: ^testing.T) { test.expect_reference_locations(t, &source, locations[:]) } + +@(test) +ast_references_soa_pointer_field :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + x, y: int, + } + + main :: proc() { + foos: #soa^#soa[]Foo + x := foos.x{*} + } + `, + } + locations := []common.Location { + {range = {start = {line = 2, character = 3}, end = {line = 2, character = 4}}}, + {range = {start = {line = 7, character = 13}, end = {line = 7, character = 14}}}, + } + + test.expect_reference_locations(t, &source, locations[:]) +} -- cgit v1.2.3 From 03a531cc687b4a97c5834037598a7ba74b3ef116 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Sun, 17 Aug 2025 21:31:29 -0400 Subject: Filter array magic methods from soa pointers --- src/server/completion.odin | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/server') diff --git a/src/server/completion.odin b/src/server/completion.odin index c8f43f7..4bd0272 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -1965,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 { -- cgit v1.2.3