aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2025-09-27 21:29:53 +0200
committerDanielGavin <danielgavin5@hotmail.com>2025-09-27 21:29:53 +0200
commit2be257718c637e2f42b760fe9acca3eb7674530a (patch)
tree81fa0659c7f00a797b4e770df9b78eca93fdd97a /src/server
parent0cc6300920f1a8b8f5191f30f9f5eae1b301959d (diff)
parentd9e6c6aa6988e880448c1418c4a904c66f157fc3 (diff)
Merge branch 'master' into remove-unused-imports
Diffstat (limited to 'src/server')
-rw-r--r--src/server/analysis.odin86
-rw-r--r--src/server/ast.odin6
-rw-r--r--src/server/build.odin55
-rw-r--r--src/server/check.odin5
-rw-r--r--src/server/collector.odin1
-rw-r--r--src/server/completion.odin8
-rw-r--r--src/server/documentation.odin2
-rw-r--r--src/server/documents.odin26
-rw-r--r--src/server/file_resolve.odin39
-rw-r--r--src/server/generics.odin11
-rw-r--r--src/server/hover.odin4
-rw-r--r--src/server/requests.odin14
-rw-r--r--src/server/semantic_tokens.odin13
-rw-r--r--src/server/symbol.odin6
-rw-r--r--src/server/types.odin1
-rw-r--r--src/server/unmarshal.odin13
16 files changed, 255 insertions, 35 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin
index 1e570b4..2278e00 100644
--- a/src/server/analysis.odin
+++ b/src/server/analysis.odin
@@ -340,25 +340,43 @@ 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
}
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 {
@@ -718,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
@@ -726,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 {
@@ -744,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 {
@@ -783,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
@@ -1259,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
}
@@ -2553,7 +2598,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
@@ -2561,6 +2608,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/ast.odin b/src/server/ast.odin
index edb9007..5548c23 100644
--- a/src/server/ast.odin
+++ b/src/server/ast.odin
@@ -527,11 +527,13 @@ collect_when_body :: proc(
}
collect_globals :: proc(file: ast.File) -> []GlobalExpr {
+ file_tags := parser.parse_file_tags(file, context.temp_allocator)
+ if !should_collect_file(file_tags) {
+ 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/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
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,
},
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/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 ""
}
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).",
diff --git a/src/server/documents.odin b/src/server/documents.odin
index 591c56c..54d995d 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,
import_decl: ^ast.Import_Decl,
}
@@ -433,6 +434,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 {
@@ -452,6 +455,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
import_.import_decl = imp
if imp.name.text != "" {
@@ -475,6 +479,7 @@ parse_imports :: proc(document: ^Document, config: ^common.Config) {
allocator = context.temp_allocator,
)
import_.name = path.clean(import_.name)
+ import_.range = range
import_.import_decl = imp
if imp.name.text != "" {
@@ -496,3 +501,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/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/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/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/src/server/requests.odin b/src/server/requests.odin
index 355cf1b..c013ffc 100644
--- a/src/server/requests.odin
+++ b/src/server/requests.odin
@@ -699,6 +699,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 {
@@ -718,8 +720,8 @@ request_initialize :: proc(
retriggerCharacters = signatureRetriggerCharacters,
},
semanticTokensProvider = SemanticTokensOptions {
- range = config.enable_semantic_tokens,
- full = config.enable_semantic_tokens,
+ 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,
@@ -1207,12 +1209,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..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,
@@ -143,8 +145,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)
}
}
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
}
diff --git a/src/server/types.odin b/src/server/types.odin
index 75161c5..6487d1f 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 {