From b4ef1f01a66ee0b1fdd3d447226fa048ca789c6a Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Mon, 22 Sep 2025 08:02:51 -0400 Subject: Don't collect globals for ignored files and add more types for workspace symbol kinds --- src/server/ast.odin | 7 +++++-- src/server/collector.odin | 1 + src/server/symbol.odin | 6 +++++- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/server/ast.odin b/src/server/ast.odin index edb9007..879693b 100644 --- a/src/server/ast.odin +++ b/src/server/ast.odin @@ -527,11 +527,14 @@ collect_when_body :: proc( } collect_globals :: proc(file: ast.File) -> []GlobalExpr { + file_tags := parser.parse_file_tags(file, context.temp_allocator) + if file_tags.ignore { + return {} + } + exprs := make([dynamic]GlobalExpr, context.temp_allocator) defer shrink(&exprs) - file_tags := parser.parse_file_tags(file, context.temp_allocator) - for decl in file.decls { if value_decl, ok := decl.derived.(^ast.Value_Decl); ok { collect_value_decl(&exprs, file, file_tags, decl, {}) diff --git a/src/server/collector.odin b/src/server/collector.odin index 607521e..bd6d040 100644 --- a/src/server/collector.odin +++ b/src/server/collector.odin @@ -666,6 +666,7 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri case ^ast.Basic_Lit: token = v^ symbol.value = collect_generic(collection, col_expr, package_map, uri) + token_type = .Unresolved case ^ast.Ident: token = v^ symbol.value = collect_generic(collection, col_expr, package_map, uri) diff --git a/src/server/symbol.odin b/src/server/symbol.odin index 4e6b19f..42e5447 100644 --- a/src/server/symbol.odin +++ b/src/server/symbol.odin @@ -780,7 +780,7 @@ symbol_type_to_completion_kind :: proc(type: SymbolType) -> CompletionItemKind { symbol_kind_to_type :: proc(type: SymbolType) -> SymbolKind { #partial switch type { - case .Function: + case .Function, .Type_Function: return .Function case .Constant: return .Constant @@ -796,6 +796,10 @@ symbol_kind_to_type :: proc(type: SymbolType) -> SymbolKind { return .Key case .Field: return .Field + case .Unresolved: + return .Constant + case .Type: + return .Class case: return .Null } -- cgit v1.2.3 From c72350d844e15c530e0b3da1ff2faf6e91dfa7c2 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Mon, 22 Sep 2025 16:18:02 -0400 Subject: Substitute poly types for generic maps --- src/server/generics.odin | 11 +++++++++++ tests/hover_test.odin | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'src') diff --git a/src/server/generics.odin b/src/server/generics.odin index 374ffc9..6f0be73 100644 --- a/src/server/generics.odin +++ b/src/server/generics.odin @@ -452,6 +452,17 @@ find_and_replace_poly_type :: proc(expr: ^ast.Expr, poly_map: ^map[string]^ast.E v.pos.file = expr.pos.file v.end.file = expr.end.file } + case ^ast.Map_Type: + if expr, ok := get_poly_map(v.key, poly_map); ok { + v.key = expr + v.pos.file = expr.pos.file + v.end.file = expr.end.file + } + if expr, ok := get_poly_map(v.value, poly_map); ok { + v.value = expr + v.pos.file = expr.pos.file + v.end.file = expr.end.file + } } return visitor diff --git a/tests/hover_test.odin b/tests/hover_test.odin index 562a2d6..3063d13 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -4980,6 +4980,25 @@ ast_hover_proc_impl :: proc(t: ^testing.T) { } test.expect_hover(t, &source, "test.foo :: proc(a: int) -> int") } + +@(test) +ast_hover_proc_overload_generic_map :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {} + clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {} + clear :: proc{ + clear_dynamic_array, + clear_map, + } + main :: proc() { + foo: map[int]string + c{*}lear(&foo) + } + `, + } + test.expect_hover(t, &source, "test.clear :: proc(m: ^$T/map[$K]$V)") +} /* Waiting for odin fix -- cgit v1.2.3 From cceb115d109c6fc86d8f6aa9cbe19517437e1519 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Mon, 22 Sep 2025 21:09:41 -0400 Subject: Correct resolving basic type aliases of keywords by using the underlying ident name rather than the symbol name --- src/server/analysis.odin | 13 ++++++++----- tests/hover_test.odin | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 1e570b4..25f85c5 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -340,19 +340,22 @@ are_symbol_basic_same_keywords :: proc(a, b: Symbol) -> bool { if are_keyword_aliases(a.name, b.name) { return true } - if a.name != b.name { + a_value, a_ok := a.value.(SymbolBasicValue) + if !a_ok { return false } - if _, ok := a.value.(SymbolBasicValue); !ok { + + b_value, b_ok := b.value.(SymbolBasicValue) + if !b_ok { return false } - if _, ok := b.value.(SymbolBasicValue); !ok { + if a_value.ident.name != b_value.ident.name { return false } - if _, ok := keyword_map[a.name]; !ok { + if _, ok := keyword_map[a_value.ident.name]; !ok { return false } - if _, ok := keyword_map[b.name]; !ok { + if _, ok := keyword_map[b_value.ident.name]; !ok { return false } diff --git a/tests/hover_test.odin b/tests/hover_test.odin index 3063d13..9d65d8c 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -4999,6 +4999,35 @@ ast_hover_proc_overload_generic_map :: proc(t: ^testing.T) { } test.expect_hover(t, &source, "test.clear :: proc(m: ^$T/map[$K]$V)") } + +@(test) +ast_hover_proc_overload_basic_type_alias :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package, context.temp_allocator) + + append(&packages, test.Package{pkg = "my_package", source = `package my_package + Bar :: int + `}) + + source := test.Source { + main = `package test + import "my_package" + + foo_int :: proc(i: int) {} + foo_string :: proc(s: string) {} + foo :: proc { + foo_int, + foo_string, + } + + main :: proc() { + bar: my_package.Bar + f{*}oo(bar) + } + `, + packages = packages[:], + } + test.expect_hover(t, &source, "test.foo :: proc(i: int)") +} /* Waiting for odin fix -- cgit v1.2.3 From 767c312cb90235c3160e5179adc12b47c06eb5a0 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Mon, 22 Sep 2025 22:01:27 -0400 Subject: Handle resolving proc overloads with nil passed as a call parameter --- src/server/analysis.odin | 43 +++++++++++++++++++++++++++++++++++++++---- tests/hover_test.odin | 21 +++++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 25f85c5..8e1d7f0 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -362,6 +362,21 @@ are_symbol_basic_same_keywords :: proc(a, b: Symbol) -> bool { return true } +is_valid_nil_symbol :: proc(symbol: Symbol) -> bool { + if symbol.pointers > 0 { + return true + } + + #partial switch v in symbol.value { + case SymbolMapValue, SymbolSliceValue, SymbolProcedureValue, SymbolDynamicArrayValue: + return true + case SymbolUnionValue: + return v.kind != .no_nil + } + + return false +} + is_symbol_same_typed :: proc(ast_context: ^AstContext, a, b: Symbol, flags: ast.Field_Flags = {}) -> bool { // In order to correctly equate the symbols for overloaded functions, we need to check both directions if same, ok := are_symbol_untyped_basic_same_typed(a, b); ok { @@ -721,6 +736,7 @@ resolve_function_overload :: proc(ast_context: ^AstContext, group: ast.Proc_Grou call_symbol: Symbol arg_symbol: Symbol ok: bool + is_call_arg_nil: bool if _, ok = call_arg.derived.(^ast.Bad_Expr); ok { continue @@ -729,9 +745,14 @@ resolve_function_overload :: proc(ast_context: ^AstContext, group: ast.Proc_Grou //named parameter if field, is_field := call_arg.derived.(^ast.Field_Value); is_field { named = true - call_symbol, ok = resolve_call_arg_type_expression(ast_context, field.value) - if !ok { - break next_fn + if ident, is_ident := field.field.derived.(^ast.Ident); is_ident && ident.name == "nil" { + is_call_arg_nil = true + ok = true + } else { + call_symbol, ok = resolve_call_arg_type_expression(ast_context, field.value) + if !ok { + break next_fn + } } if ident, is_ident := field.field.derived.(^ast.Ident); is_ident { @@ -747,7 +768,12 @@ resolve_function_overload :: proc(ast_context: ^AstContext, group: ast.Proc_Grou log.error("Expected name parameter after starting named parmeter phase") return {}, false } - call_symbol, ok = resolve_call_arg_type_expression(ast_context, call_arg) + if ident, is_ident := call_arg.derived.(^ast.Ident); is_ident && ident.name == "nil" { + is_call_arg_nil = true + ok = true + } else { + call_symbol, ok = resolve_call_arg_type_expression(ast_context, call_arg) + } } if !ok { @@ -786,6 +812,15 @@ resolve_function_overload :: proc(ast_context: ^AstContext, group: ast.Proc_Grou break next_fn } + if is_call_arg_nil { + if is_valid_nil_symbol(arg_symbol) { + continue + } else { + break next_fn + } + + } + if !is_symbol_same_typed(ast_context, call_symbol, arg_symbol, proc_arg.flags) { found := false // Are we a union variant diff --git a/tests/hover_test.odin b/tests/hover_test.odin index 9d65d8c..b2af26a 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -5028,6 +5028,27 @@ ast_hover_proc_overload_basic_type_alias :: proc(t: ^testing.T) { } test.expect_hover(t, &source, "test.foo :: proc(i: int)") } + +@(test) +ast_hover_proc_overload_nil_pointer :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + import "my_package" + + foo_int :: proc(i: int) {} + foo_ptr :: proc(s: ^string) {} + foo :: proc { + foo_int, + foo_ptr, + } + + main :: proc() { + f{*}oo(nil) + } + `, + } + test.expect_hover(t, &source, "test.foo :: proc(s: ^string)") +} /* Waiting for odin fix -- cgit v1.2.3 From c0f609e92f9b641889beb323de404134556cda72 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Tue, 23 Sep 2025 09:37:18 -0400 Subject: Check file build tags before collecting symbols --- src/server/ast.odin | 3 +-- src/server/build.odin | 55 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/server/ast.odin b/src/server/ast.odin index 879693b..5548c23 100644 --- a/src/server/ast.odin +++ b/src/server/ast.odin @@ -528,10 +528,9 @@ collect_when_body :: proc( collect_globals :: proc(file: ast.File) -> []GlobalExpr { file_tags := parser.parse_file_tags(file, context.temp_allocator) - if file_tags.ignore { + if !should_collect_file(file_tags) { return {} } - exprs := make([dynamic]GlobalExpr, context.temp_allocator) defer shrink(&exprs) diff --git a/src/server/build.odin b/src/server/build.odin index 24e109d..a0ba276 100644 --- a/src/server/build.odin +++ b/src/server/build.odin @@ -34,7 +34,7 @@ platform_os: map[string]struct{} = { } -os_enum_to_string: map[runtime.Odin_OS_Type]string = { +os_enum_to_string: [runtime.Odin_OS_Type]string = { .Windows = "windows", .Darwin = "darwin", .Linux = "linux", @@ -43,11 +43,28 @@ os_enum_to_string: map[runtime.Odin_OS_Type]string = { .WASI = "wasi", .JS = "js", .Freestanding = "freestanding", - .JS = "wasm", .Haiku = "haiku", .OpenBSD = "openbsd", .NetBSD = "netbsd", - .FreeBSD = "freebsd", + .Orca = "orca", + .Unknown = "unknown", +} + +os_string_to_enum: map[string]runtime.Odin_OS_Type = { + "Windows" = .Windows, + "Darwin" = .Darwin, + "Linux" = .Linux, + "Essence" = .Essence, + "Freebsd" = .FreeBSD, + "Wasi" = .WASI, + "Js" = .JS, + "Freestanding" = .Freestanding, + "Wasm" = .JS, + "Haiku" = .Haiku, + "Openbsd" = .OpenBSD, + "Netbsd" = .NetBSD, + "Orca" = .Orca, + "Unknown" = .Unknown, } @(private = "file") @@ -90,6 +107,38 @@ skip_file :: proc(filename: string) -> bool { return false } +should_collect_file :: proc(file_tags: parser.File_Tags) -> bool { + if file_tags.ignore { + return false + } + + if len(file_tags.build) > 0 { + when_expr_map := make(map[string]When_Expr, context.temp_allocator) + + for key, value in common.config.profile.defines { + when_expr_map[key] = resolve_when_ident(when_expr_map, value) or_continue + } + + if when_expr, ok := resolve_when_ident(when_expr_map, "ODIN_OS"); ok { + if s, ok := when_expr.(string); ok { + if used_os, ok := os_string_to_enum[when_expr.(string)]; ok { + found := false + for tag in file_tags.build { + if used_os in tag.os { + found = true + break + } + } + if !found { + return false + } + } + } + } + } + return true +} + try_build_package :: proc(pkg_name: string) { if pkg, ok := build_cache.loaded_pkgs[pkg_name]; ok { return -- cgit v1.2.3 From 33e565981eec1e5f37370d7bd2279b11c04dcda5 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Tue, 23 Sep 2025 10:05:29 -0400 Subject: Correct array like method completions by resolving the selector before the field --- src/server/completion.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/server/completion.odin b/src/server/completion.odin index 38b5cbe..75bec86 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -2271,14 +2271,14 @@ get_expression_string_from_position_context :: proc(position_context: ^DocumentP } - if position_context.field != nil { - return src[position_context.field.pos.offset:position_context.field.end.offset] - } - if position_context.selector != nil { return src[position_context.selector.pos.offset:position_context.selector.end.offset] } + if position_context.field != nil { + return src[position_context.field.pos.offset:position_context.field.end.offset] + } + return "" } -- cgit v1.2.3 From 487158b00d62ea80bf1205ad59a6a67fe1804650 Mon Sep 17 00:00:00 2001 From: DanielGavin Date: Tue, 23 Sep 2025 21:55:03 +0200 Subject: Add proper support for ranged semantic tokens. --- src/server/file_resolve.odin | 39 +++++++++++++++++++++++++++++++++++++++ src/server/requests.odin | 10 ++++------ src/server/semantic_tokens.odin | 5 ++++- 3 files changed, 47 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/server/file_resolve.odin b/src/server/file_resolve.odin index 53fda8f..22d2d63 100644 --- a/src/server/file_resolve.odin +++ b/src/server/file_resolve.odin @@ -36,6 +36,45 @@ reset_position_context :: proc(position_context: ^DocumentPositionContext) { position_context.index = nil } +resolve_ranged_file :: proc( + document: ^Document, + range: common.Range, + allocator := context.allocator, +) -> map[uintptr]SymbolAndNode { + ast_context := make_ast_context( + document.ast, + document.imports, + document.package_name, + document.uri.uri, + document.fullpath, + allocator, + ) + + position_context: DocumentPositionContext + + get_globals(document.ast, &ast_context) + + ast_context.current_package = ast_context.document_package + + symbols := make(map[uintptr]SymbolAndNode, 10000, allocator) + + margin := 20 + + for decl in document.ast.decls { + if _, is_value := decl.derived.(^ast.Value_Decl); !is_value { + continue + } + + //Look for declarations that overlap with range + if range.start.line - margin <= decl.end.line && decl.pos.line <= range.end.line + margin { + resolve_decl(&position_context, &ast_context, document, decl, &symbols, .None, allocator) + clear(&ast_context.locals) + } + } + + return symbols +} + resolve_entire_file :: proc( document: ^Document, flag := ResolveReferenceFlag.None, diff --git a/src/server/requests.odin b/src/server/requests.odin index 0cbcd99..17597be 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -717,7 +717,7 @@ request_initialize :: proc( }, semanticTokensProvider = SemanticTokensOptions { range = config.enable_semantic_tokens, - full = config.enable_semantic_tokens, + full = false, legend = SemanticTokensLegend { tokenTypes = semantic_token_type_names, tokenModifiers = semantic_token_modifier_names, @@ -1205,12 +1205,10 @@ request_semantic_token_range :: proc( tokens_params: SemanticTokensResponseParams if config.enable_semantic_tokens { - resolve_entire_file_cached(document) + symbols := resolve_ranged_file(document, semantic_params.range, context.temp_allocator) - if file, ok := file_resolve_cache.files[document.uri.uri]; ok { - tokens := get_semantic_tokens(document, semantic_params.range, file.symbols) - tokens_params = semantic_tokens_to_response_params(tokens) - } + tokens := get_semantic_tokens(document, semantic_params.range, symbols) + tokens_params = semantic_tokens_to_response_params(tokens) } response := make_response_message(params = tokens_params, id = id) diff --git a/src/server/semantic_tokens.odin b/src/server/semantic_tokens.odin index 60042cd..669ff99 100644 --- a/src/server/semantic_tokens.odin +++ b/src/server/semantic_tokens.odin @@ -143,8 +143,11 @@ get_semantic_tokens :: proc( src = ast_context.file.src, } + margin := 20 + for decl in document.ast.decls { - if range.start.line <= decl.pos.line && decl.end.line <= range.end.line { + //Look for declarations that overlap with range + if range.start.line - margin <= decl.end.line && decl.pos.line <= range.end.line + margin { visit_node(decl, &builder) } } -- cgit v1.2.3 From 0101621c30dcc523a219cd32733abf6bdac991b2 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Wed, 24 Sep 2025 12:15:48 -0400 Subject: Correct bool docs --- src/server/documentation.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/server/documentation.odin b/src/server/documentation.odin index bc9b12f..65b9139 100644 --- a/src/server/documentation.odin +++ b/src/server/documentation.odin @@ -29,7 +29,7 @@ keywords_docs: map[string]string = { "f16" = "```odin\nf16 :: f16\n```\n`f16` is the set of all IEEE-754 16-bit floating-point numbers with native endianness.", "f32" = "```odin\nf32 :: f32\n```\n`f32` is the set of all IEEE-754 32-bit floating-point numbers with native endianness.", "f64" = "```odin\nf64 :: f64\n```\n`f64` is the set of all IEEE-754 64-bit floating-point numbers with native endianness.", - "bool" = "```odin\nbool :: bool :: bool\n`bool` is ```\n`bool` is the set of boolean values, `false` and `true`. This is distinct to `b8`. `bool` has a size of 1 byte (8 bits).", + "bool" = "```odin\nbool :: bool\n```\n`bool` is the set of boolean values, `false` and `true`. This is distinct to `b8`. `bool` has a size of 1 byte (8 bits).", "any" = "```odin\nany :: any\n```\n`any` is reference any data type at runtime. Internally it contains a pointer to the underlying data and its relevant `typeid`. This is a very useful construct in order to have a runtime type safe printing procedure.\n\nNote: The `any` value is only valid for as long as the underlying data is still valid. Passing a literal to an `any` will allocate the literal in the current stack frame.\n\nNote: It is highly recommend that you do not use this unless you know what you are doing. Its primary use is for printing procedures.", "b8" = "```odin\nb8 :: b8\n```\n`b8` is the set of boolean values, `false` and `true`. This is distinct to `bool`. `b8` has a size of 1 byte (8 bits).", "b16" = "```odin\nb16 :: b16\n```\n`b16` is the set of boolean values, `false` and `true`. `b16` has a size of 2 bytes (16 bits).", -- cgit v1.2.3 From e50bcf94367f594e76628cae8751b6f00911a4b8 Mon Sep 17 00:00:00 2001 From: DanielGavin Date: Wed, 24 Sep 2025 20:38:16 +0200 Subject: Use client capabilities to check if range semantic token is supported. --- src/server/requests.odin | 6 ++++-- src/server/semantic_tokens.odin | 8 +++++--- src/server/types.odin | 1 + src/server/unmarshal.odin | 13 +++++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/server/requests.odin b/src/server/requests.odin index 17597be..f03750c 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -697,6 +697,8 @@ request_initialize :: proc( signatureTriggerCharacters := []string{"(", ","} signatureRetriggerCharacters := []string{","} + semantic_range_support := initialize_params.capabilities.textDocument.semanticTokens.requests.range + response := make_response_message( params = ResponseInitializeParams { capabilities = ServerCapabilities { @@ -716,8 +718,8 @@ request_initialize :: proc( retriggerCharacters = signatureRetriggerCharacters, }, semanticTokensProvider = SemanticTokensOptions { - range = config.enable_semantic_tokens, - full = false, + range = config.enable_semantic_tokens && semantic_range_support, + full = config.enable_semantic_tokens && !semantic_range_support, legend = SemanticTokensLegend { tokenTypes = semantic_token_type_names, tokenModifiers = semantic_token_modifier_names, diff --git a/src/server/semantic_tokens.odin b/src/server/semantic_tokens.odin index 669ff99..d237109 100644 --- a/src/server/semantic_tokens.odin +++ b/src/server/semantic_tokens.odin @@ -65,10 +65,12 @@ SemanticTokenModifier :: enum u8 { semantic_token_modifier_names: []string = {"declaration", "definition", "deprecated", "readonly"} SemanticTokenModifiers :: bit_set[SemanticTokenModifier;u32] +SemanticTokensRequest :: struct { + range: bool, +} + SemanticTokensClientCapabilities :: struct { - requests: struct { - range: bool, - }, + requests: SemanticTokensRequest, tokenTypes: []string, tokenModifiers: []string, formats: []string, diff --git a/src/server/types.odin b/src/server/types.odin index 6442503..0f16b2e 100644 --- a/src/server/types.odin +++ b/src/server/types.odin @@ -190,6 +190,7 @@ TextDocumentClientCapabilities :: struct { signatureHelp: SignatureHelpClientCapabilities, documentSymbol: DocumentSymbolClientCapabilities, codeAction: CodeActionClientCapabilities, + semanticTokens: SemanticTokensClientCapabilities, } StaleRequestSupport :: struct { diff --git a/src/server/unmarshal.odin b/src/server/unmarshal.odin index d7efe63..d93663c 100644 --- a/src/server/unmarshal.odin +++ b/src/server/unmarshal.odin @@ -60,6 +60,19 @@ unmarshal :: proc(json_value: json.Value, v: any, allocator: mem.Allocator) -> j } case json.Array: #partial switch variant in type_info.variant { + case Type_Info_Slice: + array := (^mem.Raw_Slice)(v.data) + data := mem.alloc(variant.elem.size * int(len(j)), variant.elem.align, allocator) or_else panic("OOM") + array.data = data + array.len = len(j) + + for i in 0 ..< array.len { + a := any{rawptr(uintptr(array.data) + uintptr(variant.elem_size * i)), variant.elem.id} + + if ret := unmarshal(j[i], a, allocator); ret != nil { + return ret + } + } case Type_Info_Dynamic_Array: array := (^mem.Raw_Dynamic_Array)(v.data) if array.data == nil { -- cgit v1.2.3 From 50273daf85ec00f429dd61b1f9f196fb5fdc5bb2 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Wed, 24 Sep 2025 14:52:15 -0400 Subject: Correct resolving package instead of field when name collides --- src/server/hover.odin | 4 +++- tests/hover_test.odin | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/server/hover.odin b/src/server/hover.odin index fe69949..e3f641d 100644 --- a/src/server/hover.odin +++ b/src/server/hover.odin @@ -290,6 +290,7 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> ast_context.current_package = selector.pkg + // TODO: Use resolve_selector_expression for this? #partial switch v in selector.value { case SymbolStructValue: for name, i in v.names { @@ -323,7 +324,8 @@ get_hover_information :: proc(document: ^Document, position: common.Position) -> } } } - if resolved, ok := resolve_type_identifier(&ast_context, ident^); ok { + + if resolved, ok := resolve_symbol_return(&ast_context, lookup(ident.name, selector.pkg, ast_context.fullpath)); ok { build_documentation(&ast_context, &resolved, false) resolved.name = ident.name diff --git a/tests/hover_test.odin b/tests/hover_test.odin index b2af26a..345e9f9 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -5049,6 +5049,43 @@ ast_hover_proc_overload_nil_pointer :: proc(t: ^testing.T) { } test.expect_hover(t, &source, "test.foo :: proc(s: ^string)") } + +@(test) +ast_hover_package_proc_naming_conflicting_with_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 + foo :: proc() {} + `, + }, + ) + append( + &packages, + test.Package { + pkg = "foo", + source = `package foo + `, + }, + ) + + source := test.Source { + main = `package test + import "my_package" + import "foo" + + main :: proc() { + f := my_package.fo{*}o + } + `, + packages = packages[:], + } + + test.expect_hover(t, &source, "my_package.foo :: proc()") +} /* Waiting for odin fix -- cgit v1.2.3 From d13384a1e9a0758e4c608f4dfe3226e78fa4803e Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:12:46 -0400 Subject: Goto definition on package use goes to package declaration --- src/server/analysis.odin | 23 +++++++++++++++++++++- src/server/documents.odin | 28 ++++++++++++++++++++++++++- tests/definition_test.odin | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 8e1d7f0..9ee621a 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -2591,7 +2591,9 @@ resolve_location_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) - symbol.uri = uri.uri symbol.flags |= {.Local} return symbol, true - } else if global, ok := ast_context.globals[node.name]; ok { + } + + if global, ok := ast_context.globals[node.name]; ok { symbol.range = common.get_token_range(global.name_expr, ast_context.file.src) uri := common.create_uri(global.expr.pos.file, ast_context.allocator) symbol.pkg = ast_context.document_package @@ -2599,6 +2601,25 @@ resolve_location_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) - return symbol, true } + for imp in ast_context.imports { + if imp.name == ast_context.current_package { + continue + } + + if strings.compare(imp.base, node.name) == 0 { + symbol := Symbol { + type = .Package, + pkg = imp.name, + value = SymbolPackageValue{}, + range = imp.range, + } + + try_build_package(symbol.pkg) + + return symbol, true + } + } + pkg := get_package_from_node(node) if symbol, ok := lookup(node.name, pkg, node.pos.file); ok { return symbol, ok diff --git a/src/server/documents.odin b/src/server/documents.odin index dee90d5..b513da6 100644 --- a/src/server/documents.odin +++ b/src/server/documents.odin @@ -29,6 +29,7 @@ Package :: struct { base: string, base_original: string, original: string, + range: common.Range, } Document :: struct { @@ -75,7 +76,7 @@ document_get_allocator :: proc() -> ^virtual.Arena { return pop(&document_storage.free_allocators) } else { allocator := new(virtual.Arena) - _ = virtual.arena_init_growing(allocator) + _ = virtual.arena_init_growing(allocator) return allocator } } @@ -427,6 +428,8 @@ parse_imports :: proc(document: ^Document, config: ^common.Config) { if i := strings.index(imp.fullpath, "\""); i == -1 { continue } + // TODO: Breakdown this range like with semantic tokens + range := get_import_range(imp, string(document.text)) //collection specified if i := strings.index(imp.fullpath, ":"); i != -1 && i > 1 && i < len(imp.fullpath) - 1 { @@ -446,6 +449,7 @@ parse_imports :: proc(document: ^Document, config: ^common.Config) { import_: Package import_.original = imp.fullpath import_.name = strings.clone(path.join(elems = {dir, p}, allocator = context.temp_allocator)) + import_.range = range if imp.name.text != "" { import_.base = imp.name.text @@ -468,6 +472,7 @@ parse_imports :: proc(document: ^Document, config: ^common.Config) { allocator = context.temp_allocator, ) import_.name = path.clean(import_.name) + import_.range = range if imp.name.text != "" { import_.base = imp.name.text @@ -488,3 +493,24 @@ parse_imports :: proc(document: ^Document, config: ^common.Config) { document.imports = imports[:] } + +get_import_range :: proc(imp: ^ast.Import_Decl, src: string) -> common.Range { + if imp.name.text != "" { + start := common.token_pos_to_position(imp.name.pos, src) + end := start + end.character += len(imp.name.text) + return { + start = start, + end = end, + } + } + + start := common.token_pos_to_position(imp.relpath.pos, src) + end := start + text_len := len(imp.relpath.text) + end.character += text_len + return { + start = start, + end = end, + } +} diff --git a/tests/definition_test.odin b/tests/definition_test.odin index 6e7c7a4..4810361 100644 --- a/tests/definition_test.odin +++ b/tests/definition_test.odin @@ -675,3 +675,51 @@ ast_goto_nested_using_struct_field :: proc(t: ^testing.T) { test.expect_definition_locations(t, &source, locations[:]) } + +@(test) +ast_goto_package_declaration :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package, context.temp_allocator) + + append(&packages, test.Package{pkg = "my_package", source = `package my_package + Bar :: struct{} + `}) + source := test.Source { + main = `package test + import "my_package" + + main :: proc() { + bar: m{*}y_package.Bar + } + `, + packages = packages[:], + } + locations := []common.Location { + {range = {start = {line = 1, character = 9}, end = {line = 1, character = 21}}}, + } + + test.expect_definition_locations(t, &source, locations[:]) +} + +@(test) +ast_goto_package_declaration_with_alias :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package, context.temp_allocator) + + append(&packages, test.Package{pkg = "my_package", source = `package my_package + Bar :: struct{} + `}) + source := test.Source { + main = `package test + import mp "my_package" + + main :: proc() { + bar: m{*}p.Bar + } + `, + packages = packages[:], + } + locations := []common.Location { + {range = {start = {line = 1, character = 9}, end = {line = 1, character = 11}}}, + } + + test.expect_definition_locations(t, &source, locations[:]) +} -- cgit v1.2.3 From 8a014d117e86178e5b9ab5c91fd5c92d2ebf9422 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Thu, 25 Sep 2025 08:13:19 -0400 Subject: Correctly infer types when indexing matrices --- src/server/analysis.odin | 7 +++++++ tests/hover_test.odin | 28 +++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 9ee621a..2278e00 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -1297,6 +1297,13 @@ resolve_index_expr :: proc(ast_context: ^AstContext, v: ^ast.Index_Expr) -> (Sym return indexed, true } return {}, false + case SymbolMatrixValue: + value := SymbolFixedArrayValue{ + expr = v2.expr, + len = v2.x, + } + indexed.value = value + return indexed, true } diff --git a/tests/hover_test.odin b/tests/hover_test.odin index 345e9f9..d0b844e 100644 --- a/tests/hover_test.odin +++ b/tests/hover_test.odin @@ -5033,7 +5033,6 @@ ast_hover_proc_overload_basic_type_alias :: proc(t: ^testing.T) { ast_hover_proc_overload_nil_pointer :: proc(t: ^testing.T) { source := test.Source { main = `package test - import "my_package" foo_int :: proc(i: int) {} foo_ptr :: proc(s: ^string) {} @@ -5086,6 +5085,33 @@ ast_hover_package_proc_naming_conflicting_with_another_package :: proc(t: ^testi test.expect_hover(t, &source, "my_package.foo :: proc()") } + +@(test) +ast_hover_matrix_index :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + main :: proc() { + foo: matrix[3, 2]f32 + a{*} := foo[0] + } + `, + } + test.expect_hover(t, &source, "test.a: [3]f32") +} + +@(test) +ast_hover_matrix_index_twice :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + main :: proc() { + foo: matrix[2, 3]f32 + a := foo[0] + b{*} := a[0] + } + `, + } + test.expect_hover(t, &source, "test.b: f32") +} /* Waiting for odin fix -- cgit v1.2.3 From 297c9ac556008804c95f92de3381925787655b10 Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Fri, 26 Sep 2025 20:53:04 -0400 Subject: Ensure the lines and columns for diagnostics are non negative --- src/server/check.odin | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/server/check.odin b/src/server/check.odin index ea68066..b8f0c7c 100644 --- a/src/server/check.odin +++ b/src/server/check.odin @@ -159,8 +159,9 @@ check :: proc(paths: []string, uri: common.Uri, writer: ^Writer, config: ^common code = "checker", severity = .Error, range = { - start = {character = error.pos.column - 1, line = error.pos.line - 1}, - end = {character = error.pos.end_column - 1, line = error.pos.line - 1}, + // odin will sometimes report errors on column 0, so we ensure we don't provide a negative column/line to the client + start = {character = max(error.pos.column - 1, 0), line = max(error.pos.line - 1, 0)}, + end = {character = max(error.pos.end_column - 1, 0), line = max(error.pos.line - 1, 0)}, }, message = message, }, -- cgit v1.2.3