diff options
| author | Bradley Lewis <22850972+BradLewis@users.noreply.github.com> | 2025-09-10 13:54:57 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-10 13:54:57 -0400 |
| commit | 2a67c8126594b40ece2bfced5aa934b40aa96596 (patch) | |
| tree | 8d888b9551b1b2a66886d1b09451fe8f79287d70 | |
| parent | 450cab982b4b5149059ac3f1b6c7675b0fc6dc91 (diff) | |
| parent | a60d0daad2ff3e57eb9942c58c532007794bbd63 (diff) | |
Merge pull request #990 from BradLewis/feat/where-clause-in-hover
Add where clauses to hover information
| -rw-r--r-- | src/server/analysis.odin | 43 | ||||
| -rw-r--r-- | src/server/collector.odin | 21 | ||||
| -rw-r--r-- | src/server/documentation.odin | 28 | ||||
| -rw-r--r-- | src/server/symbol.odin | 29 | ||||
| -rw-r--r-- | tests/hover_test.odin | 62 |
5 files changed, 156 insertions, 27 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 1b50468..d5ba9d1 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -1031,7 +1031,8 @@ internal_resolve_type_expression :: proc(ast_context: ^AstContext, node: ^ast.Ex return ok case ^Proc_Type: out^, ok = - make_symbol_procedure_from_ast(ast_context, node, v^, ast_context.field_name.name, {}, true, .None), true + make_symbol_procedure_from_ast(ast_context, node, v^, ast_context.field_name.name, {}, true, .None, nil), + 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 @@ -1662,12 +1663,31 @@ 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), + make_symbol_procedure_from_ast( + ast_context, + local.rhs, + v.type^, + node.name, + {}, + false, + v.inlining, + v.where_clauses, + ), true } } else { 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, + v.where_clauses, + ), + true } case ^ast.Proc_Group: return_symbol, ok = resolve_function_overload(ast_context, v^) @@ -1776,6 +1796,7 @@ resolve_global_identifier :: proc(ast_context: ^AstContext, node: ast.Ident, glo global.attributes, false, v.inlining, + v.where_clauses, ), true } @@ -1789,6 +1810,7 @@ resolve_global_identifier :: proc(ast_context: ^AstContext, node: ast.Ident, glo global.attributes, false, v.inlining, + v.where_clauses, ), true } @@ -3026,6 +3048,7 @@ make_symbol_procedure_from_ast :: proc( attributes: []^ast.Attribute, type: bool, inlining: ast.Proc_Inlining, + where_clauses: []^ast.Expr, ) -> Symbol { pkg := "" if n != nil { @@ -3071,6 +3094,7 @@ make_symbol_procedure_from_ast :: proc( tags = v.tags, attributes = attributes, inlining = inlining, + where_clauses = where_clauses, } if _, ok := get_attribute_objc_name(attributes); ok { @@ -3252,12 +3276,13 @@ make_symbol_union_from_ast :: proc( docs, comments := get_field_docs_and_comments(ast_context.file, v.variants, ast_context.allocator) symbol.value = SymbolUnionValue { - types = types[:], - poly = v.poly_params, - docs = docs[:], - comments = comments[:], - kind = v.kind, - align = v.align, + types = types[:], + poly = v.poly_params, + docs = docs[:], + comments = comments[:], + kind = v.kind, + align = v.align, + where_clauses = v.where_clauses, } if v.poly_params != nil { diff --git a/src/server/collector.odin b/src/server/collector.odin index a702546..ff1c791 100644 --- a/src/server/collector.odin +++ b/src/server/collector.odin @@ -85,6 +85,7 @@ collect_procedure_fields :: proc( package_map: map[string]string, attributes: []^ast.Attribute, inlining: ast.Proc_Inlining, + where_clauses: []^ast.Expr, ) -> SymbolProcedureValue { returns := make([dynamic]^ast.Field, 0, collection.allocator) args := make([dynamic]^ast.Field, 0, collection.allocator) @@ -111,7 +112,6 @@ collect_procedure_fields :: proc( append(&attrs, cloned) } - value := SymbolProcedureValue { return_types = returns[:], orig_return_types = returns[:], @@ -127,6 +127,7 @@ collect_procedure_fields :: proc( tags = proc_type.tags, attributes = attrs[:], inlining = inlining, + where_clauses = clone_array(where_clauses, collection.allocator, &collection.unique_strings), } return value @@ -180,6 +181,9 @@ collect_struct_fields :: proc( } b.poly = cast(^ast.Field_List)clone_type(struct_type.poly_params, collection.allocator, &collection.unique_strings) + for clause in struct_type.where_clauses { + append(&b.where_clauses, clone_expr(clause, collection.allocator, &collection.unique_strings)) + } value := to_symbol_struct_value(b) return value @@ -279,12 +283,13 @@ collect_union_fields :: proc( comments := clone_dynamic_array(temp_comments, collection.allocator, &collection.unique_strings) value := SymbolUnionValue { - types = types[:], - poly = cast(^ast.Field_List)clone_type(union_type.poly_params, collection.allocator, &collection.unique_strings), - comments = comments[:], - docs = docs[:], - kind = union_type.kind, - align = clone_type(union_type.align, collection.allocator, &collection.unique_strings), + types = types[:], + poly = cast(^ast.Field_List)clone_type(union_type.poly_params, collection.allocator, &collection.unique_strings), + comments = comments[:], + docs = docs[:], + kind = union_type.kind, + align = clone_type(union_type.align, collection.allocator, &collection.unique_strings), + where_clauses = clone_array(union_type.where_clauses, collection.allocator, &collection.unique_strings), } return value @@ -568,6 +573,7 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri package_map, expr.attributes, v.inlining, + v.where_clauses, ) } @@ -588,6 +594,7 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri package_map, expr.attributes, .None, + nil, ) case ^ast.Proc_Group: token = v^ diff --git a/src/server/documentation.odin b/src/server/documentation.odin index 285119d..6f591a4 100644 --- a/src/server/documentation.odin +++ b/src/server/documentation.odin @@ -216,6 +216,7 @@ write_signature :: proc(sb: ^strings.Builder, ast_context: ^AstContext, symbol: strings.write_string(sb, " #align") build_string_node(v.align, sb, false) } + write_where_clauses(sb, v.where_clauses) if len(v.types) == 0 { strings.write_string(sb, " {}") return @@ -485,7 +486,14 @@ write_proc_param_list_and_return :: proc(sb: ^strings.Builder, value: SymbolProc if len(value.orig_return_types) != 0 { strings.write_string(sb, " -> ") + add_parens := false if len(value.orig_return_types) > 1 { + add_parens = true + } else if field, ok := value.orig_return_types[0].derived.(^ast.Field); ok && len(field.names) > 0{ + add_parens = true + } + + if add_parens { strings.write_string(sb, "(") } @@ -496,7 +504,7 @@ write_proc_param_list_and_return :: proc(sb: ^strings.Builder, value: SymbolProc } } - if len(value.orig_return_types) > 1 { + if add_parens { strings.write_string(sb, ")") } } else if value.diverging { @@ -530,6 +538,7 @@ write_procedure_symbol_signature :: proc(sb: ^strings.Builder, value: SymbolProc } } write_proc_param_list_and_return(sb, value) + write_where_clauses(sb, value.where_clauses) if detailed_signature { for tag in value.tags { s := "" @@ -549,6 +558,18 @@ write_procedure_symbol_signature :: proc(sb: ^strings.Builder, value: SymbolProc } } +write_where_clauses :: proc(sb: ^strings.Builder, where_clauses: []^ast.Expr) { + if len(where_clauses) > 0 { + strings.write_string(sb, " where ") + for clause, i in where_clauses { + build_string_node(clause, sb, false) + if i != len(where_clauses) - 1 { + strings.write_string(sb, ", ") + } + } + } +} + write_struct_hover :: proc(sb: ^strings.Builder, ast_context: ^AstContext, v: SymbolStructValue, depth: int) { strings.write_string(sb, "struct") write_poly_list(sb, v.poly, v.poly_names) @@ -578,6 +599,9 @@ write_struct_hover :: proc(sb: ^strings.Builder, ast_context: ^AstContext, v: Sy strings.write_string(sb, " #no_copy") } } + + write_where_clauses(sb, v.where_clauses) + if len(v.names) == 0 { strings.write_string(sb, " {}") return @@ -718,7 +742,7 @@ write_node :: proc( symbol = make_symbol_bit_field_from_ast(ast_context, n, name, true) ok = true case ^ast.Proc_Type: - symbol = make_symbol_procedure_from_ast(ast_context, nil, n^, name, {}, true, .None) + symbol = make_symbol_procedure_from_ast(ast_context, nil, n^, name, {}, true, .None, nil) ok = true } if ok { diff --git a/src/server/symbol.odin b/src/server/symbol.odin index 9e220c5..3d176f1 100644 --- a/src/server/symbol.odin +++ b/src/server/symbol.odin @@ -39,6 +39,7 @@ SymbolStructValue :: struct { args: []^ast.Expr, //The arguments in the call expression for poly docs: []^ast.Comment_Group, comments: []^ast.Comment_Group, + where_clauses: []^ast.Expr, // Extra fields for embedded bit fields via usings backing_types: map[int]^ast.Expr, // the base type for the bit field @@ -74,6 +75,7 @@ SymbolProcedureValue :: struct { tags: ast.Proc_Tags, attributes: []^ast.Attribute, inlining: ast.Proc_Inlining, + where_clauses: []^ast.Expr, } SymbolProcedureGroupValue :: struct { @@ -96,13 +98,14 @@ SymbolEnumValue :: struct { } SymbolUnionValue :: struct { - types: []^ast.Expr, - poly: ^ast.Field_List, - poly_names: []string, - docs: []^ast.Comment_Group, - comments: []^ast.Comment_Group, - kind: ast.Union_Type_Kind, - align: ^ast.Expr, + types: []^ast.Expr, + poly: ^ast.Field_List, + poly_names: []string, + docs: []^ast.Comment_Group, + comments: []^ast.Comment_Group, + kind: ast.Union_Type_Kind, + align: ^ast.Expr, + where_clauses: []^ast.Expr, } SymbolDynamicArrayValue :: struct { @@ -161,9 +164,9 @@ SymbolPolyTypeValue :: struct { Generic symbol that is used by the indexer for any variable type(constants, defined global variables, etc), */ SymbolGenericValue :: struct { - expr: ^ast.Expr, + expr: ^ast.Expr, field_names: []string, - ranges: []common.Range, + ranges: []common.Range, } SymbolValue :: union { @@ -252,6 +255,7 @@ SymbolStructValueBuilder :: struct { unexpanded_usings: [dynamic]int, poly: ^ast.Field_List, poly_names: [dynamic]string, + where_clauses: [dynamic]^ast.Expr, // Extra fields for embedded bit fields via usings backing_types: map[int]^ast.Expr, @@ -280,6 +284,7 @@ symbol_struct_value_builder_make_none :: proc(allocator := context.allocator) -> poly_names = make([dynamic]string, allocator), backing_types = make(map[int]^ast.Expr, allocator), bit_sizes = make(map[int]^ast.Expr, allocator), + where_clauses = make([dynamic]^ast.Expr, allocator), } } @@ -301,6 +306,7 @@ symbol_struct_value_builder_make_symbol :: proc( poly_names = make([dynamic]string, allocator), backing_types = make(map[int]^ast.Expr, allocator), bit_sizes = make(map[int]^ast.Expr, allocator), + where_clauses = make([dynamic]^ast.Expr, allocator), } } @@ -327,6 +333,7 @@ symbol_struct_value_builder_make_symbol_symbol_struct_value :: proc( align = v.align, max_field_align = v.max_field_align, min_field_align = v.min_field_align, + where_clauses = slice.to_dynamic(v.where_clauses, allocator), } } @@ -361,6 +368,7 @@ to_symbol_struct_value :: proc(b: SymbolStructValueBuilder) -> SymbolStructValue max_field_align = b.max_field_align, min_field_align = b.min_field_align, tags = b.tags, + where_clauses = b.where_clauses[:], } } @@ -424,6 +432,9 @@ write_struct_type :: proc( if v.is_raw_union { b.tags |= {.Is_Raw_Union} } + for clause in v.where_clauses { + append(&b.where_clauses, clause) + } } expand_objc(ast_context, b) diff --git a/tests/hover_test.odin b/tests/hover_test.odin index 11ea8b3..5370804 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -4483,6 +4483,68 @@ ast_hover_float_binary_expr :: proc(t: ^testing.T) { } test.expect_hover(t, &source, "test.bar: float") } + +@(test) +ast_hover_parapoly_struct_with_where_clause :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + type_is_integer :: proc($T: typeid) -> bool { + return true + } + + F{*}oo :: struct($T: typeid, $N: int) #packed + where type_is_integer(T), + N > 2 { + x: [N]T, + y: [N-2]T, + } + `, + } + test.expect_hover(t, &source, "test.Foo: struct($T: typeid, $N: int) #packed where type_is_integer(T), N > 2 {\n\tx: [N]T,\n\ty: [N - 2]T,\n}") +} + +@(test) +ast_hover_parapoly_proc_with_where_clause :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + fo{*}o :: proc(x: [$N]int) -> bool + where N > 2 #optional_ok { + fmt.println(#procedure, "was called with the parameter", x) + return true + } + `, + } + test.expect_hover(t, &source, "test.foo: proc(x: [$N]int) -> bool where N > 2 #optional_ok") +} + +@(test) +ast_hover_parapoly_union_with_where_clause :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + type_is_integer :: proc($T: typeid) -> bool { + return true + } + + Fo{*}o :: union($T: typeid) #no_nil where type_is_integer(T){ + T, + string, + } + `, + } + test.expect_hover(t, &source, "test.Foo: union($T: typeid) #no_nil where type_is_integer(T) {\n\tT,\n\tstring,\n}") +} + +@(test) +ast_hover_proc_named_return_parens :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + f{*}oo :: proc() -> (a: int) { + return + } + `, + } + test.expect_hover(t, &source, "test.foo: proc() -> (a: int)") +} /* Waiting for odin fix |