From 9cf3c6313e77b0c3d80b8ffc298b20ab2d5b633d Mon Sep 17 00:00:00 2001 From: DanielGavin Date: Wed, 29 May 2024 16:41:24 +0200 Subject: Work on file resolve --- src/testing/testing.odin | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'src/testing/testing.odin') diff --git a/src/testing/testing.odin b/src/testing/testing.odin index 9f67ff0..225775c 100644 --- a/src/testing/testing.odin +++ b/src/testing/testing.odin @@ -58,7 +58,8 @@ setup :: proc(src: ^Source) { } else if current == '\n' { current_line += 1 current_character = 0 - } else if src.main[current_index:current_index + 3] == "{*}" { + } else if len(src.main) > current_index + 3 && + src.main[current_index:current_index + 3] == "{*}" { dst_slice := transmute([]u8)src.main[current_index:] src_slice := transmute([]u8)src.main[current_index + 3:] copy(dst_slice, src_slice) @@ -383,3 +384,38 @@ expect_definition_locations :: proc( } } } + +expect_symbol_location :: proc( + t: ^testing.T, + src: ^Source, + expect_locations: []common.Location, +) { + setup(src) + defer teardown(src) + + symbol_and_nodes := server.resolve_entire_file(src.document, .None) + + ok := true + + for location in expect_locations { + match := false + for k, v in symbol_and_nodes { + if v.symbol.range == location.range { + match = true + } + } + if !match { + ok = false + testing.errorf(t, "Failed to match with location: %v", location) + } + } + + if !ok { + testing.error(t, "Received:") + for k, v in symbol_and_nodes { + testing.errorf(t, "%v \n", v.symbol) + } + } + + +} -- cgit v1.2.3 From 42fefebdd512958b41948abefb2d52c1b4682aff Mon Sep 17 00:00:00 2001 From: DanielGavin Date: Sat, 8 Jun 2024 18:26:34 +0200 Subject: Fix issues with selector call expression confusing whether to selector complete or identifier --- src/server/analysis.odin | 76 +++++++++++++++++++++++----------------------- src/server/completion.odin | 14 ++++++++- src/server/definition.odin | 8 ++--- src/testing/testing.odin | 9 +++--- tests/objc_test.odin | 2 +- tests/references_test.odin | 1 + 6 files changed, 62 insertions(+), 48 deletions(-) (limited to 'src/testing/testing.odin') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index e84d983..4fef18c 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -31,7 +31,7 @@ DocumentPositionContext :: struct { function: ^ast.Proc_Lit, //used to help with type resolving in function scope functions: [dynamic]^ast.Proc_Lit, //stores all the functions that have been iterated through to find the position selector: ^ast.Expr, //used for completion - selector_expr: ^ast.Selector_Expr, + selector_expr: ^ast.Node, identifier: ^ast.Node, implicit_context: ^ast.Implicit, tag: ^ast.Node, @@ -2303,7 +2303,7 @@ resolve_location_implicit_selector :: proc( resolve_location_selector :: proc( ast_context: ^AstContext, - selector: ^ast.Selector_Expr, + selector_expr: ^ast.Node, ) -> ( symbol: Symbol, ok: bool, @@ -2312,40 +2312,45 @@ resolve_location_selector :: proc( set_ast_package_set_scoped(ast_context, ast_context.document_package) - symbol = resolve_type_expression(ast_context, selector.expr) or_return + if selector, ok := selector_expr.derived.(^ast.Selector_Expr); ok { - field: string + symbol = resolve_type_expression(ast_context, selector.expr) or_return - if selector.field != nil { - #partial switch v in selector.field.derived { - case ^ast.Ident: - field = v.name - } - } + field: string - #partial switch v in symbol.value { - case SymbolStructValue: - for name, i in v.names { - if strings.compare(name, field) == 0 { - symbol.range = v.ranges[i] + if selector.field != nil { + #partial switch v in selector.field.derived { + case ^ast.Ident: + field = v.name } } - case SymbolBitFieldValue: - for name, i in v.names { - if strings.compare(name, field) == 0 { - symbol.range = v.ranges[i] + + #partial switch v in symbol.value { + case SymbolStructValue: + for name, i in v.names { + if strings.compare(name, field) == 0 { + symbol.range = v.ranges[i] + } + } + case SymbolBitFieldValue: + for name, i in v.names { + if strings.compare(name, field) == 0 { + symbol.range = v.ranges[i] + } + } + case SymbolPackageValue: + if pkg, ok := lookup(field, symbol.pkg); ok { + symbol.range = pkg.range + symbol.uri = pkg.uri + } else { + return {}, false } } - case SymbolPackageValue: - if pkg, ok := lookup(field, symbol.pkg); ok { - symbol.range = pkg.range - symbol.uri = pkg.uri - } else { - return {}, false - } + + return symbol, true } - return symbol, true + return {}, false } @@ -4828,15 +4833,17 @@ get_document_position_node :: proc( position_context.hint == .Definition { position_context.call = cast(^Expr)node } + get_document_position(n.expr, position_context) get_document_position(n.args, position_context) case ^Selector_Call_Expr: if position_context.hint == .Definition || position_context.hint == .Hover || - position_context.hint == .SignatureHelp { + position_context.hint == .SignatureHelp || + position_context.hint == .Completion { position_context.selector = n.expr position_context.field = n.call - position_context.selector_expr = cast(^Selector_Expr)node + position_context.selector_expr = node if _, ok := n.call.derived.(^ast.Call_Expr); ok { position_context.call = n.call @@ -4850,18 +4857,11 @@ get_document_position_node :: proc( } } case ^Selector_Expr: - if position_context.hint == .Completion { - if n.field != nil && - n.field.pos.line - 1 == position_context.line { - //The parser is not fault tolerant enough, relying on the fallback as the main completion parsing for now - //position_context.selector = n.expr; - //position_context.field = n.field; - } - } else if position_context.hint == .Definition || + if position_context.hint == .Definition || position_context.hint == .Hover && n.field != nil { position_context.selector = n.expr position_context.field = n.field - position_context.selector_expr = cast(^Selector_Expr)node + position_context.selector_expr = node get_document_position(n.expr, position_context) get_document_position(n.field, position_context) } else { diff --git a/src/server/completion.odin b/src/server/completion.odin index e404b78..efd18f9 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -99,7 +99,19 @@ get_completion_list :: proc( } if position_context.selector != nil { - completion_type = .Selector + if position_context.selector_expr != nil { + if selector_call, ok := position_context.selector_expr.derived.(^ast.Selector_Call_Expr); + ok { + if !position_in_node( + selector_call.call, + position_context.position, + ) { + completion_type = .Selector + } + } + } else { + completion_type = .Selector + } } if position_context.tag != nil { diff --git a/src/server/definition.odin b/src/server/definition.odin index 021a556..017cf0d 100644 --- a/src/server/definition.odin +++ b/src/server/definition.odin @@ -90,10 +90,10 @@ get_definition_location :: proc( if position_context.import_stmt != nil { if get_all_package_file_locations( - document, - position_context.import_stmt, - &locations, - ) { + document, + position_context.import_stmt, + &locations, + ) { return locations[:], true } } else if position_context.selector_expr != nil { diff --git a/src/testing/testing.odin b/src/testing/testing.odin index 22440cd..e499db5 100644 --- a/src/testing/testing.odin +++ b/src/testing/testing.odin @@ -387,12 +387,13 @@ expect_definition_locations :: proc( expect_symbol_location :: proc( t: ^testing.T, src: ^Source, + flag: server.ResolveReferenceFlag, expect_locations: []common.Location, ) { setup(src) defer teardown(src) - symbol_and_nodes := server.resolve_entire_file(src.document, .None) + symbol_and_nodes := server.resolve_entire_file(src.document, flag) ok := true @@ -405,14 +406,14 @@ expect_symbol_location :: proc( } if !match { ok = false - testing.errorf(t, "Failed to match with location: %v", location) + log.errorf("Failed to match with location: %v", location) } } if !ok { - testing.error(t, "Received:") + log.error("Received:") for k, v in symbol_and_nodes { - testing.errorf(t, "%v \n", v.symbol) + log.errorf("%v \n", v.symbol) } } diff --git a/tests/objc_test.odin b/tests/objc_test.odin index 4857861..9c69afb 100644 --- a/tests/objc_test.odin +++ b/tests/objc_test.odin @@ -145,7 +145,7 @@ objc_hover_chained_selector :: proc(t: ^testing.T) { ) } -//@(test) TODO: Disabled for now until refractor +@(test) objc_implicit_enum_completion :: proc(t: ^testing.T) { packages := make([dynamic]test.Package, context.temp_allocator) diff --git a/tests/references_test.odin b/tests/references_test.odin index 0989230..3f8a2cc 100644 --- a/tests/references_test.odin +++ b/tests/references_test.odin @@ -21,6 +21,7 @@ reference_variables_in_function :: proc(t: ^testing.T) { test.expect_symbol_location( t, &source, + .Variable, { { range = { -- cgit v1.2.3 From 53f42476fc2fe3455f1e899189cb904462502e8c Mon Sep 17 00:00:00 2001 From: DanielGavin Date: Sat, 8 Jun 2024 21:18:13 +0200 Subject: First version of selector rename. --- src/server/analysis.odin | 4 +- src/server/file_resolve.odin | 84 ++++++++++++++++++++++++-------------- src/server/references.odin | 96 +++++++++++++++++++------------------------- src/testing/testing.odin | 6 ++- tests/references_test.odin | 73 ++++++++++++++++++++++++++++++++- 5 files changed, 176 insertions(+), 87 deletions(-) (limited to 'src/testing/testing.odin') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 4fef18c..4c32c46 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -76,7 +76,7 @@ DocumentLocal :: struct { parameter: bool, } -DeferredDepth :: 100 +DeferredDepth :: 35 AstContext :: struct { locals: map[int]map[string][dynamic]DocumentLocal, //locals all the way to the document position @@ -4803,6 +4803,8 @@ get_document_position_node :: proc( position_context.function = cast(^Proc_Lit)node append(&position_context.functions, position_context.function) get_document_position(n.body, position_context) + } else if position_in_node(n.type, position_context.position) { + position_context.function = cast(^Proc_Lit)node } case ^Comp_Lit: //only set this for the parent comp literal, since we will need to walk through it to infer types. diff --git a/src/server/file_resolve.odin b/src/server/file_resolve.odin index 1aa5163..3d6d7c0 100644 --- a/src/server/file_resolve.odin +++ b/src/server/file_resolve.odin @@ -19,10 +19,9 @@ import "src:common" ResolveReferenceFlag :: enum { None, - Variable, - Constant, - StructElement, - EnumElement, + Identifier, + Base, + Field, } resolve_entire_file :: proc( @@ -156,7 +155,7 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { case ^Selector_Call_Expr: data.position_context.selector = n.expr data.position_context.field = n.call - data.position_context.selector_expr = cast(^Selector_Expr)node + data.position_context.selector_expr = node if _, ok := n.call.derived.(^ast.Call_Expr); ok { data.position_context.call = n.call @@ -167,15 +166,23 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { case ^Selector_Expr: data.position_context.selector = n.expr data.position_context.field = n.field - data.position_context.selector_expr = cast(^Selector_Expr)node + data.position_context.selector_expr = node if data.flag != .None { if symbol, ok := resolve_location_selector(data.ast_context, n); ok { - data.symbols[cast(uintptr)node] = SymbolAndNode { - node = n, - symbol = symbol, + if data.flag == .Field { + data.symbols[cast(uintptr)node] = SymbolAndNode { + node = n.field, + symbol = symbol, + } + } else { + data.symbols[cast(uintptr)node] = SymbolAndNode { + node = n, + symbol = symbol, + } } + } } else { if symbol, ok := resolve_type_expression( @@ -201,8 +208,6 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { data.position_context, ) - //log.error(data.ast_context.locals) - resolve_node(n.type, data) data.position_context.function = cast(^Proc_Lit)node @@ -234,7 +239,7 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { resolve_node(n.body, data) case ^Switch_Stmt: local_scope(data, n) - data.position_context.switch_stmt = cast(^Switch_Stmt)node + data.position_context.switch_stmt = n resolve_node(n.label, data) resolve_node(n.init, data) resolve_node(n.cond, data) @@ -271,10 +276,10 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { case ^Comp_Lit: //only set this for the parent comp literal, since we will need to walk through it to infer types. if data.position_context.parent_comp_lit == nil { - data.position_context.parent_comp_lit = cast(^Comp_Lit)node + data.position_context.parent_comp_lit = n } - data.position_context.comp_lit = cast(^Comp_Lit)node + data.position_context.comp_lit = n resolve_node(n.type, data) resolve_nodes(n.elems, data) @@ -286,13 +291,13 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { if data.position_context.parent_binary == nil { data.position_context.parent_binary = cast(^Binary_Expr)node } - data.position_context.binary = cast(^Binary_Expr)node + data.position_context.binary = n resolve_node(n.left, data) resolve_node(n.right, data) case ^Paren_Expr: resolve_node(n.expr, data) case ^Call_Expr: - data.position_context.call = cast(^Expr)node + data.position_context.call = n resolve_node(n.expr, data) resolve_nodes(n.args, data) case ^Index_Expr: @@ -305,7 +310,7 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { resolve_node(n.low, data) resolve_node(n.high, data) case ^Field_Value: - data.position_context.field_value = cast(^Field_Value)node + data.position_context.field_value = n resolve_node(n.field, data) resolve_node(n.value, data) case ^Ternary_If_Expr: @@ -331,15 +336,11 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { case ^Tag_Stmt: r := cast(^Tag_Stmt)node resolve_node(r.stmt, data) - case ^Assign_Stmt: - data.position_context.assign = cast(^Assign_Stmt)node - resolve_nodes(n.lhs, data) - resolve_nodes(n.rhs, data) case ^Block_Stmt: resolve_node(n.label, data) resolve_nodes(n.stmts, data) case ^Return_Stmt: - data.position_context.returns = cast(^Return_Stmt)node + data.position_context.returns = n resolve_nodes(n.results, data) case ^Defer_Stmt: resolve_node(n.stmt, data) @@ -347,7 +348,7 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { resolve_nodes(n.list, data) resolve_nodes(n.body, data) case ^Type_Switch_Stmt: - data.position_context.switch_type_stmt = cast(^Type_Switch_Stmt)node + data.position_context.switch_type_stmt = n resolve_node(n.label, data) resolve_node(n.tag, data) resolve_node(n.expr, data) @@ -357,8 +358,16 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { case ^Using_Stmt: resolve_nodes(n.list, data) case ^Bad_Decl: + case ^Assign_Stmt: + data.position_context.assign = n + data.position_context.comp_lit = nil + data.position_context.parent_comp_lit = nil + resolve_nodes(n.lhs, data) + resolve_nodes(n.rhs, data) case ^Value_Decl: - data.position_context.value_decl = cast(^Value_Decl)node + data.position_context.value_decl = n + data.position_context.comp_lit = nil + data.position_context.parent_comp_lit = nil resolve_nodes(n.names, data) resolve_node(n.type, data) resolve_nodes(n.values, data) @@ -374,7 +383,22 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { case ^Attribute: resolve_nodes(n.elems, data) case ^Field: - resolve_nodes(n.names, data) + if data.flag != .None { + for name in n.names { + data.symbols[cast(uintptr)node] = SymbolAndNode { + node = name, + symbol = Symbol { + range = common.get_token_range( + name, + string(data.document.text), + ), + }, + } + } + } else { + resolve_nodes(n.names, data) + } + resolve_node(n.type, data) resolve_node(n.default_value, data) case ^Field_List: @@ -401,21 +425,21 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { case ^Multi_Pointer_Type: resolve_node(n.elem, data) case ^Struct_Type: - data.position_context.struct_type = cast(^Struct_Type)node + data.position_context.struct_type = n resolve_node(n.poly_params, data) resolve_node(n.align, data) resolve_node(n.fields, data) case ^Union_Type: - data.position_context.union_type = cast(^Union_Type)node + data.position_context.union_type = n resolve_node(n.poly_params, data) resolve_node(n.align, data) resolve_nodes(n.variants, data) case ^Enum_Type: - data.position_context.enum_type = cast(^Enum_Type)node + data.position_context.enum_type = n resolve_node(n.base_type, data) resolve_nodes(n.fields, data) case ^Bit_Set_Type: - data.position_context.bitset_type = cast(^Bit_Set_Type)node + data.position_context.bitset_type = n resolve_node(n.elem, data) resolve_node(n.underlying, data) case ^Map_Type: @@ -431,7 +455,7 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { case ^ast.Or_Return_Expr: resolve_node(n.expr, data) case ^ast.Bit_Field_Type: - data.position_context.bit_field_type = cast(^Bit_Field_Type)node + data.position_context.bit_field_type = n resolve_node(n.backing_type, data) resolve_nodes(n.fields, data) case ^ast.Bit_Field_Field: diff --git a/src/server/references.odin b/src/server/references.odin index f1939c1..2ab571e 100644 --- a/src/server/references.odin +++ b/src/server/references.odin @@ -45,22 +45,6 @@ walk_directories :: proc( return 0, false } -position_in_struct_names :: proc( - position_context: ^DocumentPositionContext, - type: ^ast.Struct_Type, -) -> bool { - for field in type.fields.list { - for name in field.names { - if position_in_node(name, position_context.position) { - return true - } - } - } - - return false -} - - resolve_references :: proc( document: ^Document, ast_context: ^AstContext, @@ -87,12 +71,26 @@ resolve_references :: proc( reset_ast_context(ast_context) - if position_context.struct_type != nil && - position_in_struct_names( - position_context, - position_context.struct_type, - ) { - return {}, true + if position_context.struct_type != nil { + found := false + done: for field in position_context.struct_type.fields.list { + for name in field.names { + if position_in_node(name, position_context.position) { + symbol = Symbol { + range = common.get_token_range( + name, + string(document.text), + ), + } + found = true + resolve_flag = .Field + break done + } + } + } + if !found { + return {}, false + } } else if position_context.enum_type != nil { return {}, true } else if position_context.bitset_type != nil { @@ -100,53 +98,43 @@ resolve_references :: proc( } else if position_context.union_type != nil { return {}, true } else if position_context.selector_expr != nil { - if resolved, ok := resolve_type_expression( - ast_context, - position_context.selector, - ); ok { - if _, is_package := resolved.value.(SymbolPackageValue); - !is_package { - return {}, true - } - resolve_flag = .Constant - } + resolve_flag = .Field - symbol, ok = resolve_location_selector( - ast_context, - position_context.selector_expr, - ) + base: ^ast.Ident + base, ok = position_context.selector.derived.(^ast.Ident) - if !ok { + if !ok || position_context.identifier == nil { return {}, true } - if ident, ok := position_context.identifier.derived.(^ast.Ident); ok { - reference = ident.name - } else { - return {}, true - } - } else if position_context.implicit { - return {}, true - } else if position_context.identifier != nil { ident := position_context.identifier.derived.(^ast.Ident) - if resolved, ok := resolve_type_identifier(ast_context, ident^); ok { - if resolved.type == .Variable { - resolve_flag = .Variable - } else { - resolve_flag = .Constant + if position_in_node(base, position_context.position) { + symbol, ok = resolve_location_identifier(ast_context, ident^) + + if !ok { + return {}, true } + + resolve_flag = .Base } else { - log.errorf( - "Failed to resolve identifier for indexing: %v", - ident.name, + symbol, ok = resolve_location_selector( + ast_context, + position_context.selector_expr, ) - return {}, true + + resolve_flag = .Field } + } else if position_context.implicit { + return {}, true + } else if position_context.identifier != nil { + ident := position_context.identifier.derived.(^ast.Ident) reference = ident.name symbol, ok = resolve_location_identifier(ast_context, ident^) + resolve_flag = .Identifier + if !ok { return {}, true } diff --git a/src/testing/testing.odin b/src/testing/testing.odin index e499db5..2a08b23 100644 --- a/src/testing/testing.odin +++ b/src/testing/testing.odin @@ -393,7 +393,11 @@ expect_symbol_location :: proc( setup(src) defer teardown(src) - symbol_and_nodes := server.resolve_entire_file(src.document, flag) + symbol_and_nodes := server.resolve_entire_file( + src.document, + flag, + context.temp_allocator, + ) ok := true diff --git a/tests/references_test.odin b/tests/references_test.odin index 3f8a2cc..aa2579b 100644 --- a/tests/references_test.odin +++ b/tests/references_test.odin @@ -21,7 +21,7 @@ reference_variables_in_function :: proc(t: ^testing.T) { test.expect_symbol_location( t, &source, - .Variable, + .Identifier, { { range = { @@ -44,3 +44,74 @@ reference_variables_in_function :: proc(t: ^testing.T) { }, ) } + + +@(test) +reference_variables_in_function_parameters :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + my_function :: proc(a: int) { + b := a + c := 2 + b + } + `, + packages = {}, + } + + test.expect_symbol_location( + t, + &source, + .Identifier, + { + { + range = { + start = {line = 1, character = 22}, + end = {line = 1, character = 23}, + }, + }, + { + range = { + start = {line = 2, character = 3}, + end = {line = 2, character = 4}, + }, + }, + { + range = { + start = {line = 3, character = 3}, + end = {line = 3, character = 4}, + }, + }, + }, + ) +} + +@(test) +reference_selectors_in_function :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + My_Struct :: struct { + a: int, + } + + my_function :: proc() { + my: My_Struct + my.a = 2 + } + `, + packages = {}, + } + + test.expect_symbol_location( + t, + &source, + .Field, + { + { + range = { + start = {line = 2, character = 3}, + end = {line = 2, character = 4}, + }, + }, + }, + ) +} -- cgit v1.2.3 From 33b5b66a53fef0ea3fd9ccec4841290050cd95f4 Mon Sep 17 00:00:00 2001 From: DanielGavin Date: Sun, 9 Jun 2024 12:55:24 +0200 Subject: Improve reference testing --- src/server/analysis.odin | 4 +- src/server/file_resolve.odin | 4 +- src/server/references.odin | 10 +++- src/server/symbol.odin | 2 + src/testing/testing.odin | 25 +++------- tests/references_test.odin | 116 +++++++++++++++++++++++++++++++++---------- 6 files changed, 111 insertions(+), 50 deletions(-) (limited to 'src/testing/testing.odin') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index acd5733..7b4d125 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -4797,14 +4797,14 @@ get_document_position_node :: proc( case ^Ellipsis: get_document_position(n.expr, position_context) case ^Proc_Lit: - get_document_position(n.type, position_context) - if position_in_node(n.body, position_context.position) { + get_document_position(n.type, position_context) position_context.function = cast(^Proc_Lit)node append(&position_context.functions, position_context.function) get_document_position(n.body, position_context) } else if position_in_node(n.type, position_context.position) { position_context.function = cast(^Proc_Lit)node + get_document_position(n.type, position_context) } case ^Comp_Lit: //only set this for the parent comp literal, since we will need to walk through it to infer types. diff --git a/src/server/file_resolve.odin b/src/server/file_resolve.odin index d6119c5..3c1ed17 100644 --- a/src/server/file_resolve.odin +++ b/src/server/file_resolve.odin @@ -142,7 +142,6 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { case ^Bad_Expr: case ^Ident: data.position_context.identifier = node - if data.flag != .None { if symbol, ok := resolve_location_identifier(data.ast_context, n^); ok { @@ -421,10 +420,9 @@ resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) { }, } } - } else { - resolve_nodes(n.names, data) } + resolve_nodes(n.names, data) resolve_node(n.type, data) resolve_node(n.default_value, data) case ^Field_List: diff --git a/src/server/references.odin b/src/server/references.odin index a23d968..f82a80e 100644 --- a/src/server/references.odin +++ b/src/server/references.odin @@ -36,9 +36,15 @@ walk_directories :: proc( } if strings.contains(info.name, ".odin") { - slash_path, _ := filepath.to_slash(info.fullpath) + slash_path, _ := filepath.to_slash( + info.fullpath, + context.temp_allocator, + ) if slash_path != document.fullpath { - append(&fullpaths, strings.clone(info.fullpath)) + append( + &fullpaths, + strings.clone(info.fullpath, context.temp_allocator), + ) } } diff --git a/src/server/symbol.odin b/src/server/symbol.odin index 2e9aa27..9e0f154 100644 --- a/src/server/symbol.odin +++ b/src/server/symbol.odin @@ -215,6 +215,7 @@ free_symbol :: proc(symbol: Symbol, allocator: mem.Allocator) { common.free_ast(v.arg_types, allocator) case SymbolStructValue: delete(v.names, allocator) + delete(v.ranges, allocator) common.free_ast(v.types, allocator) case SymbolGenericValue: common.free_ast(v.expr, allocator) @@ -248,6 +249,7 @@ free_symbol :: proc(symbol: Symbol, allocator: mem.Allocator) { case SymbolPackageValue: case SymbolBitFieldValue: delete(v.names, allocator) + delete(v.ranges, allocator) common.free_ast(v.types, allocator) } } diff --git a/src/testing/testing.odin b/src/testing/testing.odin index 2a08b23..210db59 100644 --- a/src/testing/testing.odin +++ b/src/testing/testing.odin @@ -384,42 +384,33 @@ expect_definition_locations :: proc( } } -expect_symbol_location :: proc( +expect_reference_locations :: proc( t: ^testing.T, src: ^Source, - flag: server.ResolveReferenceFlag, expect_locations: []common.Location, ) { setup(src) defer teardown(src) - symbol_and_nodes := server.resolve_entire_file( - src.document, - flag, - context.temp_allocator, - ) - - ok := true + locations, ok := server.get_references(src.document, src.position) - for location in expect_locations { + for expect_location in expect_locations { match := false - for k, v in symbol_and_nodes { - if v.symbol.range == location.range { + for location in locations { + if location.range == expect_location.range { match = true } } if !match { ok = false - log.errorf("Failed to match with location: %v", location) + log.errorf("Failed to match with location: %v", expect_location) } } if !ok { log.error("Received:") - for k, v in symbol_and_nodes { - log.errorf("%v \n", v.symbol) + for location in locations { + log.errorf("%v \n", location) } } - - } diff --git a/tests/references_test.odin b/tests/references_test.odin index aa2579b..cadaff4 100644 --- a/tests/references_test.odin +++ b/tests/references_test.odin @@ -12,23 +12,16 @@ reference_variables_in_function :: proc(t: ^testing.T) { my_function :: proc() { a := 2 b := a - c := 2 + b + c := 2 + b{*} } `, packages = {}, } - test.expect_symbol_location( + test.expect_reference_locations( t, &source, - .Identifier, { - { - range = { - start = {line = 2, character = 3}, - end = {line = 2, character = 4}, - }, - }, { range = { start = {line = 3, character = 3}, @@ -37,31 +30,28 @@ reference_variables_in_function :: proc(t: ^testing.T) { }, { range = { - start = {line = 4, character = 3}, - end = {line = 4, character = 4}, + start = {line = 4, character = 12}, + end = {line = 4, character = 13}, }, }, }, ) } - @(test) reference_variables_in_function_parameters :: proc(t: ^testing.T) { source := test.Source { main = `package test my_function :: proc(a: int) { - b := a - c := 2 + b + b := a{*} } `, packages = {}, } - test.expect_symbol_location( + test.expect_reference_locations( t, &source, - .Identifier, { { range = { @@ -69,6 +59,30 @@ reference_variables_in_function_parameters :: proc(t: ^testing.T) { end = {line = 1, character = 23}, }, }, + }, + ) +} + +@(test) +reference_selectors_in_function :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + My_Struct :: struct { + a: int, + } + + my_function :: proc() { + my: My_Struct + my.a{*} = 2 + } + `, + packages = {}, + } + + test.expect_reference_locations( + t, + &source, + { { range = { start = {line = 2, character = 3}, @@ -77,39 +91,89 @@ reference_variables_in_function_parameters :: proc(t: ^testing.T) { }, { range = { - start = {line = 3, character = 3}, - end = {line = 3, character = 4}, + start = {line = 7, character = 6}, + end = {line = 7, character = 7}, }, }, }, ) } + @(test) -reference_selectors_in_function :: proc(t: ^testing.T) { +reference_field_comp_lit :: proc(t: ^testing.T) { source := test.Source { main = `package test + Foo :: struct { + soo_many_cases: int, + } + My_Struct :: struct { - a: int, + foo: Foo, } - my_function :: proc() { - my: My_Struct - my.a = 2 + my_function :: proc(my_struct: My_Struct) { + my := My_Struct { + foo = {soo_many_cases{*} = 2}, + } } `, packages = {}, } - test.expect_symbol_location( + test.expect_reference_locations( t, &source, - .Field, { { range = { start = {line = 2, character = 3}, - end = {line = 2, character = 4}, + end = {line = 2, character = 17}, + }, + }, + { + range = { + start = {line = 11, character = 11}, + end = {line = 11, character = 25}, + }, + }, + }, + ) +} + +@(test) +reference_field_comp_lit_infer_from_function :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + Foo :: struct { + soo_many_cases: int, + } + + My_Struct :: struct { + foo: Foo, + } + + my_function :: proc(my_struct: My_Struct) { + my_function({foo = {soo_many_cases{*} = 2}}) + } + `, + packages = {}, + } + + test.expect_reference_locations( + t, + &source, + { + { + range = { + start = {line = 2, character = 3}, + end = {line = 2, character = 17}, + }, + }, + { + range = { + start = {line = 10, character = 23}, + end = {line = 10, character = 37}, }, }, }, -- cgit v1.2.3 From 20ebad86257391f3cd6dc8104fca4e169cee4695 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 13 Jun 2024 11:09:41 +0200 Subject: Setup tests for semantic tokens and add one simple test --- src/server/requests.odin | 14 +++++++------ src/server/semantic_tokens.odin | 45 ++++++++++++++++++++++++++++------------- src/server/types.odin | 2 +- src/testing/testing.odin | 33 ++++++++++++++++++++++++++++++ tests/semantic_tokens_test.odin | 36 +++++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 tests/semantic_tokens_test.odin (limited to 'src/testing/testing.odin') diff --git a/src/server/requests.odin b/src/server/requests.odin index 3ca38d6..6216ecb 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -1272,17 +1272,18 @@ request_semantic_token_full :: proc( end = common.Position{line = 9000000}, //should be enough } - symbols: SemanticTokens + tokens_params: SemanticTokensResponseParams if config.enable_semantic_tokens { resolve_entire_file_cached(document) if file, ok := file_resolve_cache.files[document.uri.uri]; ok { - symbols = get_semantic_tokens(document, range, file.symbols) + tokens := get_semantic_tokens(document, range, file.symbols) + tokens_params = semantic_tokens_to_response_params(tokens) } } - response := make_response_message(params = symbols, id = id) + response := make_response_message(params = tokens_params, id = id) send_response(response, writer) @@ -1313,21 +1314,22 @@ request_semantic_token_range :: proc( return .InternalError } - symbols: SemanticTokens + tokens_params: SemanticTokensResponseParams if config.enable_semantic_tokens { resolve_entire_file_cached(document) if file, ok := file_resolve_cache.files[document.uri.uri]; ok { - symbols = get_semantic_tokens( + tokens := get_semantic_tokens( document, semantic_params.range, file.symbols, ) + tokens_params = semantic_tokens_to_response_params(tokens) } } - response := make_response_message(params = symbols, id = id) + response := make_response_message(params = tokens_params, id = id) send_response(response, writer) diff --git a/src/server/semantic_tokens.odin b/src/server/semantic_tokens.odin index 507cd18..ae8918a 100644 --- a/src/server/semantic_tokens.odin +++ b/src/server/semantic_tokens.odin @@ -15,7 +15,7 @@ import "core:unicode/utf8" import "src:common" -SemanticTokenTypes :: enum u8 { +SemanticTokenTypes :: enum u32 { Namespace, Type, Enum, @@ -101,22 +101,40 @@ SemanticTokensRangeParams :: struct { range: common.Range, } -SemanticTokens :: struct { +SemanticToken :: struct { + // token line number, relative to the previous token + delta_line: u32, + // token start character, relative to the previous token + // (relative to 0 or the previous token’s start if they are on the same line) + delta_char: u32, + len: u32, + type: SemanticTokenTypes, + modifiers: SemanticTokenModifiers, +} +#assert(size_of(SemanticToken) == 5 * size_of(u32)) + +SemanticTokensResponseParams :: struct { data: []u32, } SemanticTokenBuilder :: struct { current_start: int, - tokens: [dynamic]u32, + tokens: [dynamic]SemanticToken, symbols: map[uintptr]SymbolAndNode, src: string, } +semantic_tokens_to_response_params :: proc ( + tokens: []SemanticToken, +) -> SemanticTokensResponseParams { + return {data = (cast([^]u32)raw_data(tokens))[:len(tokens) * 5]} +} + get_semantic_tokens :: proc( document: ^Document, range: common.Range, symbols: map[uintptr]SymbolAndNode, -) -> SemanticTokens { +) -> []SemanticToken { ast_context := make_ast_context( document.ast, document.imports, @@ -127,7 +145,7 @@ get_semantic_tokens :: proc( ast_context.current_package = ast_context.document_package builder: SemanticTokenBuilder = { - tokens = make([dynamic]u32, 10000, context.temp_allocator), + tokens = make([dynamic]SemanticToken, 0, 2000, context.temp_allocator), symbols = symbols, src = ast_context.file.src, } @@ -139,7 +157,7 @@ get_semantic_tokens :: proc( } } - return {data = builder.tokens[:]} + return builder.tokens[:] } write_semantic_at_pos :: proc( @@ -154,14 +172,13 @@ write_semantic_at_pos :: proc( transmute([]u8)builder.src, builder.current_start, ) - append( - &builder.tokens, - cast(u32)position.line, - cast(u32)position.character, - cast(u32)len, - cast(u32)type, - transmute(u32)modifiers, - ) + append(&builder.tokens, SemanticToken{ + delta_line = cast(u32)position.line, + delta_char = cast(u32)position.character, + len = cast(u32)len, + type = type, + modifiers = modifiers, + }) builder.current_start = pos } diff --git a/src/server/types.odin b/src/server/types.odin index bb6d00f..d082309 100644 --- a/src/server/types.odin +++ b/src/server/types.odin @@ -23,7 +23,7 @@ ResponseParams :: union { CompletionList, SignatureHelp, []DocumentSymbol, - SemanticTokens, + SemanticTokensResponseParams, Hover, []TextEdit, []InlayHint, diff --git a/src/testing/testing.odin b/src/testing/testing.odin index 210db59..0d9c028 100644 --- a/src/testing/testing.odin +++ b/src/testing/testing.odin @@ -414,3 +414,36 @@ expect_reference_locations :: proc( } } } + +expect_semantic_tokens :: proc( + t: ^testing.T, + src: ^Source, + expected: []server.SemanticToken, +) { + setup(src) + defer teardown(src) + + + resolve_flag: server.ResolveReferenceFlag + symbols_and_nodes := server.resolve_entire_file( + src.document, + resolve_flag, + context.temp_allocator, + ) + + range := common.Range{end = {line = 9000000}} //should be enough + tokens := server.get_semantic_tokens(src.document, range, symbols_and_nodes) + + testing.expectf(t, len(expected) == len(tokens), "\nExpected %d tokens, but received %d", len(expected), len(tokens)) + + for i in 0.. int +my_function :: proc() { + a := 2 + b := a + c := 2 + b +} +`, + } + + test.expect_semantic_tokens(t, &src, { + {1, 0, 9, .Type, {.ReadOnly}}, // [0] Proc_Type + {0, 18, 1, .Parameter, {}}, // [1] a + {0, 3, 6, .Type, {.ReadOnly}}, // [2] string + {0, 11, 3, .Type, {.ReadOnly}}, // [3] int + {1, 0, 11, .Function, {.ReadOnly}}, // [4] my_function + {0, 0, 11, .Type, {.ReadOnly}}, // [5] !!! WRONG !!! + {1, 1, 1, .Variable, {}}, // [6] a + {1, 1, 1, .Variable, {}}, // [7] b + {0, 5, 1, .Variable, {}}, // [8] a + {1, 1, 1, .Variable, {}}, // [9] c + {0, 9, 1, .Variable, {}}, // [10] b + }) +} -- cgit v1.2.3