diff options
| author | DanielGavin <danielgavin5@hotmail.com> | 2025-06-10 20:22:27 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-10 20:22:27 +0200 |
| commit | e4ba6606fb75e964e360bf22fdfd0641f169497a (patch) | |
| tree | e1327c7bd089edab3c843b1578b1b2a679eb97d9 | |
| parent | a42400e0c9f1471ec27454476f6fe6c19dc95242 (diff) | |
| parent | cea7502466e9a413d1b8980b6447c9316f03a93a (diff) | |
Merge pull request #650 from BradLewis/feat/hover-struct-definitions
Enrich hover information
| -rw-r--r-- | src/server/analysis.odin | 94 | ||||
| -rw-r--r-- | src/server/completion.odin | 4 | ||||
| -rw-r--r-- | src/server/hover.odin | 29 | ||||
| -rw-r--r-- | tests/completions_test.odin | 12 | ||||
| -rw-r--r-- | tests/hover_test.odin | 177 |
5 files changed, 293 insertions, 23 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 668260b..e67b31e 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -3765,7 +3765,28 @@ unwrap_bitset :: proc(ast_context: ^AstContext, bitset_symbol: Symbol) -> (Symbo return {}, false } -get_signature :: proc(ast_context: ^AstContext, ident: ast.Ident, symbol: Symbol, was_variable := false) -> string { +append_variable_full_name :: proc( + sb: ^strings.Builder, + ast_context: ^AstContext, + symbol: Symbol, + pointer_prefix: string, +) { + pkg_name := get_symbol_pkg_name(ast_context, symbol) + if pkg_name == "" { + fmt.sbprintf(sb, "%s%s :: ", pointer_prefix, symbol.name) + return + } + fmt.sbprintf(sb, "%s%s.%s :: ", pointer_prefix, pkg_name, symbol.name) + return +} + +get_signature :: proc( + ast_context: ^AstContext, + ident: ast.Any_Node, + symbol: Symbol, + was_variable := false, + short_signature := false, +) -> string { if symbol.type == .Function { return symbol.signature } @@ -3789,11 +3810,26 @@ get_signature :: proc(ast_context: ^AstContext, ident: ast.Ident, symbol: Symbol allocator = ast_context.allocator, ) case SymbolEnumValue: + if short_signature { + builder := strings.builder_make(ast_context.allocator) + if is_variable { + append_variable_full_name(&builder, ast_context, symbol, pointer_prefix) + } + strings.write_string(&builder, "enum") + return strings.to_string(builder) + } + builder := strings.builder_make(ast_context.allocator) if is_variable { - return symbol.name - } else { - return "enum" + append_variable_full_name(&builder, ast_context, symbol, pointer_prefix) + } + strings.write_string(&builder, "enum {\n") + for i in 0 ..< len(v.names) { + strings.write_string(&builder, "\t") + strings.write_string(&builder, v.names[i]) + strings.write_string(&builder, ",\n") } + strings.write_string(&builder, "}") + return strings.to_string(builder) case SymbolMapValue: return strings.concatenate( a = {pointer_prefix, "map[", common.node_to_string(v.key), "]", common.node_to_string(v.value)}, @@ -3802,17 +3838,55 @@ get_signature :: proc(ast_context: ^AstContext, ident: ast.Ident, symbol: Symbol case SymbolProcedureValue: return "proc" case SymbolStructValue: + if short_signature { + builder := strings.builder_make(ast_context.allocator) + if is_variable { + append_variable_full_name(&builder, ast_context, symbol, pointer_prefix) + } + strings.write_string(&builder, "struct") + return strings.to_string(builder) + } + builder := strings.builder_make(ast_context.allocator) if is_variable { - return strings.concatenate({pointer_prefix, symbol.name}, ast_context.allocator) - } else { - return "struct" + append_variable_full_name(&builder, ast_context, symbol, pointer_prefix) + } + longestNameLen := 0 + for name in v.names { + if len(name) > longestNameLen { + longestNameLen = len(name) + } + } + 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, "") + common.build_string_node(v.types[i], &builder, false) + strings.write_string(&builder, ",\n") } + strings.write_string(&builder, "}") + return strings.to_string(builder) case SymbolUnionValue: + if short_signature { + builder := strings.builder_make(ast_context.allocator) + if is_variable { + append_variable_full_name(&builder, ast_context, symbol, pointer_prefix) + } + strings.write_string(&builder, "union") + return strings.to_string(builder) + } + builder := strings.builder_make(ast_context.allocator) if is_variable { - return strings.concatenate({pointer_prefix, symbol.name}, ast_context.allocator) - } else { - return "union" + append_variable_full_name(&builder, ast_context, symbol, pointer_prefix) + } + strings.write_string(&builder, "union {\n") + for i in 0 ..< len(v.types) { + strings.write_string(&builder, "\t") + common.build_string_node(v.types[i], &builder, false) + strings.write_string(&builder, ",\n") } + strings.write_string(&builder, "}") + return strings.to_string(builder) case SymbolBitFieldValue: if is_variable { return strings.concatenate({pointer_prefix, symbol.name}, ast_context.allocator) diff --git a/src/server/completion.odin b/src/server/completion.odin index 8d61e3e..57d289d 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -1289,7 +1289,7 @@ get_identifier_completion :: proc( ident.name = k if symbol, ok := resolve_type_identifier(ast_context, ident^); ok { - symbol.signature = get_signature(ast_context, ident^, symbol) + symbol.signature = get_signature(ast_context, ident, symbol, short_signature=true) build_procedure_symbol_signature(&symbol) @@ -1330,7 +1330,7 @@ get_identifier_completion :: proc( ident.name = k if symbol, ok := resolve_type_identifier(ast_context, ident^); ok { - symbol.signature = get_signature(ast_context, ident^, symbol) + symbol.signature = get_signature(ast_context, ident, symbol, short_signature=true) build_procedure_symbol_signature(&symbol) diff --git a/src/server/hover.odin b/src/server/hover.odin index cc3c5b2..b69385a 100644 --- a/src/server/hover.odin +++ b/src/server/hover.odin @@ -108,6 +108,28 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> } } + if position_context.struct_type != nil { + for field in position_context.struct_type.fields.list { + for name in field.names { + if position_in_node(name, position_context.position) { + if identifier, ok := name.derived.(^ast.Ident); ok && field.type != nil { + if position_context.value_decl != nil && len(position_context.value_decl.names) != 0 { + if symbol, ok := resolve_type_expression(&ast_context, field.type); ok { + if struct_symbol, ok := resolve_type_expression(&ast_context, position_context.value_decl.names[0]); ok { + symbol.pkg = struct_symbol.name + symbol.name = identifier.name + symbol.signature = get_signature(&ast_context, field.type.derived, symbol) + hover.contents = write_hover_content(&ast_context, symbol) + return hover, true, true + } + } + } + } + } + } + } + } + if position_context.field_value != nil && 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 { @@ -155,7 +177,7 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> if position_in_node(base, position_context.position) { if resolved, ok := resolve_type_identifier(&ast_context, ident); ok { - resolved.signature = get_signature(&ast_context, ident, resolved) + resolved.signature = get_signature(&ast_context, &ident, resolved) resolved.name = ident.name if resolved.type == .Variable { @@ -206,7 +228,7 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> if symbol, ok := resolve_type_expression(&ast_context, v.types[i]); ok { symbol.name = name symbol.pkg = selector.name - symbol.signature = common.node_to_string(v.types[i]) + symbol.signature = get_signature(&ast_context, v.types[i].derived, symbol) hover.contents = write_hover_content(&ast_context, symbol) return hover, true, true } @@ -228,6 +250,7 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> if position_context.field != nil { if ident, ok := position_context.field.derived.(^ast.Ident); ok { if symbol, ok := resolve_type_identifier(&ast_context, ident^); ok { + symbol.signature = get_signature(&ast_context, ident, symbol) hover.contents = write_hover_content(&ast_context, symbol) return hover, true, true } @@ -282,7 +305,7 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> } if resolved, ok := resolve_type_identifier(&ast_context, ident); ok { - resolved.signature = get_signature(&ast_context, ident, resolved) + resolved.signature = get_signature(&ast_context, &ident, resolved) resolved.name = ident.name if resolved.type == .Variable { diff --git a/tests/completions_test.odin b/tests/completions_test.odin index e5e0001..5e8c5d8 100644 --- a/tests/completions_test.odin +++ b/tests/completions_test.odin @@ -651,7 +651,7 @@ ast_for_in_identifier_completion :: proc(t: ^testing.T) { } - test.expect_completion_details(t, &source, "", {"test.my_element: My_Struct"}) + test.expect_completion_details(t, &source, "", {"test.my_element: test.My_Struct :: struct"}) } @(test) @@ -675,7 +675,7 @@ ast_for_in_call_expr_completion :: proc(t: ^testing.T) { } - test.expect_completion_details(t, &source, ".", {"test.zstep: Step"}) + test.expect_completion_details(t, &source, ".", {"test.zstep: test.Step :: struct"}) } @@ -1661,7 +1661,7 @@ ast_new_clone_completion :: proc(t: ^testing.T) { `, } - test.expect_completion_details(t, &source, "", {"test.adzz: ^Foo"}) + test.expect_completion_details(t, &source, "", {"test.adzz: ^test.Foo :: struct"}) } @(test) @@ -1735,7 +1735,7 @@ ast_index_proc_parameter_completion :: proc(t: ^testing.T) { packages = packages[:], } - test.expect_completion_details(t, &source, ".", {"my_package.param: My_Struct"}) + test.expect_completion_details(t, &source, ".", {"my_package.param: my_package.My_Struct :: struct"}) } @(test) @@ -2534,7 +2534,7 @@ ast_poly_struct_with_poly :: proc(t: ^testing.T) { `, } - test.expect_completion_details(t, &source, "", {"test.first: ^Animal"}) + test.expect_completion_details(t, &source, "", {"test.first: ^test.Animal :: struct"}) } @(test) @@ -3013,7 +3013,7 @@ ast_enumerated_array_range_completion :: proc(t: ^testing.T) { `, } - test.expect_completion_details(t, &source, "", {"test.indezx: Enum"}) + test.expect_completion_details(t, &source, "", {"test.indezx: test.Enum :: enum"}) } @(test) diff --git a/tests/hover_test.odin b/tests/hover_test.odin index bdd583a..fb64285 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -83,7 +83,37 @@ ast_hover_external_package_parameter :: proc(t: ^testing.T) { packages = packages[:], } - test.expect_hover(t, &source, "test.cool: My_Struct") + test.expect_hover(t, &source, "test.cool: my_package.My_Struct :: struct {\n\tone: int,\n\ttwo: int,\n\tthree: int,\n}") +} + +@(test) +ast_hover_external_package_parameter_pointer :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package, context.temp_allocator) + + append( + &packages, + test.Package { + pkg = "my_package", + source = `package my_package + My_Struct :: struct { + one: int, + two: int, + three: int, + } + `, + }, + ) + source := test.Source { + main = `package test + import "my_package" + main :: proc(cool: ^my_package.My_Struct) { + cool{*} + } + `, + packages = packages[:], + } + + test.expect_hover(t, &source, "test.cool: ^my_package.My_Struct :: struct {\n\tone: int,\n\ttwo: int,\n\tthree: int,\n}") } @(test) @@ -302,7 +332,7 @@ ast_hover_struct_field_selector_completion :: proc(t: ^testing.T) { packages = packages[:], } - test.expect_hover(t, &source, "my_package.My_Struct: struct") + test.expect_hover(t, &source, "my_package.My_Struct: struct {\n\tone: int,\n\ttwo: int,\n\tthree: int,\n}") } @(test) @@ -412,6 +442,149 @@ ast_hover_union_implicit_selector :: proc(t: ^testing.T) { test.expect_hover(t, &source, "test.Bar: .Foo1") } +@(test) +ast_hover_struct :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + bar: int, + f: proc(a: int) -> int, + } + + foo := F{*}oo{} + ` + } + + test.expect_hover(t, &source, "test.Foo: struct {\n\tbar: int,\n\tf: proc(a: int) -> int,\n}") +} + +@(test) +ast_hover_proc_param_with_struct_from_another_package :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package, context.temp_allocator) + + append( + &packages, + test.Package { + pkg = "my_package", + source = `package my_package + My_Struct :: struct { + one: int, + two: int, + three: int, + } + `, + }, + ) + source := test.Source { + main = `package test + import "my_package" + main :: proc(cool: my_package.My{*}_Struct) { + cool + } + `, + packages = packages[:], + } + + test.expect_hover(t, &source, "my_package.My_Struct: struct {\n\tone: int,\n\ttwo: int,\n\tthree: int,\n}") +} + +@(test) +ast_hover_struct_variable :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + bar: int, + f: proc(a: int) -> int, + } + + fo{*}o := Foo{} + ` + } + + test.expect_hover(t, &source, "test.foo: test.Foo :: struct {\n\tbar: int,\n\tf: proc(a: int) -> int,\n}") +} + +@(test) +ast_hover_enum :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: enum { + Foo1, + Foo2, + } + + foo: F{*}oo + ` + } + + test.expect_hover(t, &source, "test.Foo: enum {\n\tFoo1,\n\tFoo2,\n}") +} + +@(test) +ast_hover_enum_variable :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: enum { + Foo1, + Foo2, + } + + f{*}oo: Foo + ` + } + + test.expect_hover(t, &source, "test.foo: test.Foo :: enum {\n\tFoo1,\n\tFoo2,\n}") +} + +@(test) +ast_hover_union :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: union { + string, + int, + } + + foo: F{*}oo + ` + } + + test.expect_hover(t, &source, "test.Foo: union {\n\tstring,\n\tint,\n}") +} + +@(test) +ast_hover_union_variable :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: union { + string, + int, + } + + f{*}oo: Foo + ` + } + + test.expect_hover(t, &source, "test.foo: test.Foo :: union {\n\tstring,\n\tint,\n}") +} + +@(test) +ast_hover_struct_field_definition :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + b{*}ar: int, + f: proc(a: int) -> int, + } + + foo := Foo{ + bar = 1 + } + ` + } + + test.expect_hover(t, &source, "Foo.bar: int") +} /* Waiting for odin fix |