aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.vscode/c_cpp_properties.json21
-rw-r--r--.vscode/launch.json26
-rw-r--r--README.md4
-rwxr-xr-xbuild.sh4
-rw-r--r--editors/vscode/package-lock.json34
-rw-r--r--editors/vscode/package.json2
-rw-r--r--src/common/ast.odin32
-rw-r--r--src/common/util.odin13
-rw-r--r--src/server/analysis.odin679
-rw-r--r--src/server/build.odin12
-rw-r--r--src/server/caches.odin2
-rw-r--r--src/server/collector.odin8
-rw-r--r--src/server/completion.odin54
-rw-r--r--src/server/definition.odin10
-rw-r--r--src/server/file_resolve.odin540
-rw-r--r--src/server/generics.odin88
-rw-r--r--src/server/imports.odin4
-rw-r--r--src/server/references.odin459
-rw-r--r--src/server/rename.odin99
-rw-r--r--src/server/requests.odin56
-rw-r--r--src/server/semantic_tokens.odin103
-rw-r--r--src/server/symbol.odin7
-rw-r--r--src/server/types.odin17
-rw-r--r--src/testing/testing.odin67
-rw-r--r--tests/completions_test.odin140
-rw-r--r--tests/definition_test.odin123
-rw-r--r--tests/hover_test.odin38
-rw-r--r--tests/objc_test.odin2
-rw-r--r--tests/references_test.odin254
-rw-r--r--tests/semantic_tokens_test.odin35
31 files changed, 2143 insertions, 792 deletions
diff --git a/.gitignore b/.gitignore
index 7fad674..94b197c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,4 @@
/odinfmt
tests/tests
ols.json
-.vscode/settings.json
+.vscode/ \ No newline at end of file
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
deleted file mode 100644
index c78c930..0000000
--- a/.vscode/c_cpp_properties.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "configurations": [
- {
- "name": "Win32",
- "includePath": [
- "${workspaceFolder}/**"
- ],
- "defines": [
- "_DEBUG",
- "UNICODE",
- "_UNICODE"
- ],
- "windowsSdkVersion": "10.0.22000.0",
- "compilerPath": "cl.exe",
- "cStandard": "c17",
- "cppStandard": "c++17",
- "intelliSenseMode": "windows-msvc-x64"
- }
- ],
- "version": 4
-} \ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
deleted file mode 100644
index bf5a6b5..0000000
--- a/.vscode/launch.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [
- {
- "type": "cppvsdbg",
- "request": "attach",
- "name": "Attach OLS",
- "processId": "${command:pickProcess}"
- },
- {
- "type": "cppvsdbg",
- "request": "launch",
- "name": "Run unit",
- "program": "C:\\Users\\Daniel\\Desktop\\Computer_Science\\ols\\test_unit.exe",
- },
- {
- "type": "lldb",
- "request": "attach",
- "name": "Attach lldb",
- "pid": "${command:pickProcess}"
- },
- ]
-} \ No newline at end of file
diff --git a/README.md b/README.md
index fda3e28..b528c63 100644
--- a/README.md
+++ b/README.md
@@ -125,8 +125,10 @@ Support Language server features:
- Completion
- Go to definition
-- Semantic tokens(really unstable and unfinished)
+- Semantic tokens
- Document symbols
+- Rename
+- References
- Signature help
- Hover
diff --git a/build.sh b/build.sh
index d494fee..611b45d 100755
--- a/build.sh
+++ b/build.sh
@@ -74,9 +74,9 @@ if [[ $1 == "debug" ]]
then
shift
- odin build src/ -collection:src=src -out:ols -use-separate-modules -debug $@
+ odin build src/ -show-timings -collection:src=src -out:ols -microarch:native -no-bounds-check -use-separate-modules -debug $@
exit 0
fi
-odin build src/ -collection:src=src -out:ols -o:speed $@
+odin build src/ -show-timings -collection:src=src -out:ols -microarch:native -no-bounds-check -o:speed $@
diff --git a/editors/vscode/package-lock.json b/editors/vscode/package-lock.json
index 404d3e5..2384838 100644
--- a/editors/vscode/package-lock.json
+++ b/editors/vscode/package-lock.json
@@ -11,7 +11,7 @@
"adm-zip": "^0.5.9",
"https-proxy-agent": "^5.0.0",
"node-fetch": "^2.6.7",
- "vscode-languageclient": "8.1.0"
+ "vscode-languageclient": "9.0.1"
},
"devDependencies": {
"@types/glob": "^7.2.0",
@@ -3176,24 +3176,24 @@
}
},
"node_modules/vscode-jsonrpc": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz",
- "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
+ "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/vscode-languageclient": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0.tgz",
- "integrity": "sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==",
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz",
+ "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==",
"dependencies": {
"minimatch": "^5.1.0",
"semver": "^7.3.7",
- "vscode-languageserver-protocol": "3.17.3"
+ "vscode-languageserver-protocol": "3.17.5"
},
"engines": {
- "vscode": "^1.67.0"
+ "vscode": "^1.82.0"
}
},
"node_modules/vscode-languageclient/node_modules/brace-expansion": {
@@ -3216,18 +3216,18 @@
}
},
"node_modules/vscode-languageserver-protocol": {
- "version": "3.17.3",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz",
- "integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==",
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
+ "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
"dependencies": {
- "vscode-jsonrpc": "8.1.0",
- "vscode-languageserver-types": "3.17.3"
+ "vscode-jsonrpc": "8.2.0",
+ "vscode-languageserver-types": "3.17.5"
}
},
"node_modules/vscode-languageserver-types": {
- "version": "3.17.3",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
- "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
+ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="
},
"node_modules/vscode-test": {
"version": "1.6.1",
diff --git a/editors/vscode/package.json b/editors/vscode/package.json
index a017ed5..e7d0003 100644
--- a/editors/vscode/package.json
+++ b/editors/vscode/package.json
@@ -160,7 +160,7 @@
"adm-zip": "^0.5.9",
"https-proxy-agent": "^5.0.0",
"node-fetch": "^2.6.7",
- "vscode-languageclient": "8.1.0"
+ "vscode-languageclient": "9.0.1"
},
"configurationDefaults": {
"[odin]": {
diff --git a/src/common/ast.odin b/src/common/ast.odin
index 29d3e6c..4f1b25d 100644
--- a/src/common/ast.odin
+++ b/src/common/ast.odin
@@ -207,6 +207,35 @@ unwrap_pointer_expr :: proc(expr: ^ast.Expr) -> (^ast.Expr, int, bool) {
return expr, n, true
}
+expr_contains_poly :: proc(expr: ^ast.Expr) -> bool {
+ if expr == nil {
+ return false
+ }
+
+ visit :: proc(visitor: ^ast.Visitor, node: ^ast.Node) -> ^ast.Visitor {
+ if node == nil {
+ return nil
+ }
+ if _, ok := node.derived.(^ast.Poly_Type); ok {
+ b := cast(^bool)visitor.data
+ b^ = true
+ return nil
+ }
+ return visitor
+ }
+
+ found := false
+
+ visitor := ast.Visitor {
+ visit = visit,
+ data = &found,
+ }
+
+ ast.walk(&visitor, expr)
+
+ return found
+}
+
is_expr_basic_lit :: proc(expr: ^ast.Expr) -> bool {
_, ok := expr.derived.(^ast.Basic_Lit)
return ok
@@ -275,6 +304,9 @@ collect_value_decl :: proc(
is_private_file = true
}
}
+ } else if strings.has_prefix(txt, "//+build ignore") {
+ is_private_pkg = true
+ is_private_file = true
}
}
}
diff --git a/src/common/util.odin b/src/common/util.odin
index 9700829..f0195ca 100644
--- a/src/common/util.odin
+++ b/src/common/util.odin
@@ -6,6 +6,7 @@ import "core:log"
import "core:mem"
import "core:os"
import "core:path/filepath"
+import "core:path/slashpath"
import "core:strings"
foreign import libc "system:c"
@@ -120,3 +121,15 @@ when ODIN_OS == .Darwin || ODIN_OS == .Linux || ODIN_OS == .NetBSD {
fgetc :: proc "cdecl" (stream: ^FILE) -> i32 ---
}
}
+
+get_executable_path :: proc(allocator := context.temp_allocator) -> string {
+ exe_path, ok := filepath.abs(
+ slashpath.dir(os.args[0], context.temp_allocator),
+ context.temp_allocator,
+ )
+ if !ok {
+ log.error("Failed to resolve executable path")
+ return ""
+ }
+ return exe_path
+}
diff --git a/src/server/analysis.odin b/src/server/analysis.odin
index 93ada47..132a054 100644
--- a/src/server/analysis.odin
+++ b/src/server/analysis.odin
@@ -31,9 +31,12 @@ 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,
+ label: ^ast.Ident,
implicit_context: ^ast.Implicit,
+ index: ^ast.Index_Expr,
+ previous_index: ^ast.Index_Expr,
tag: ^ast.Node,
field: ^ast.Expr, //used for completion
call: ^ast.Expr, //used for signature help
@@ -76,7 +79,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
@@ -98,6 +101,7 @@ AstContext :: struct {
uri: string,
fullpath: string,
non_mutable_only: bool,
+ overloading: bool,
}
make_ast_context :: proc(
@@ -133,14 +137,19 @@ make_ast_context :: proc(
}
set_ast_package_deferred :: proc(ast_context: ^AstContext, pkg: string) {
+ if ast_context.deferred_count <= 0 {
+ return
+ }
ast_context.deferred_count -= 1
ast_context.current_package =
ast_context.deferred_package[ast_context.deferred_count]
- assert(ast_context.deferred_count >= 0)
}
@(deferred_in = set_ast_package_deferred)
set_ast_package_set_scoped :: proc(ast_context: ^AstContext, pkg: string) {
+ if ast_context.deferred_count >= DeferredDepth {
+ return
+ }
ast_context.deferred_package[ast_context.deferred_count] =
ast_context.current_package
ast_context.deferred_count += 1
@@ -148,14 +157,19 @@ set_ast_package_set_scoped :: proc(ast_context: ^AstContext, pkg: string) {
}
set_ast_package_none_deferred :: proc(ast_context: ^AstContext) {
+ if ast_context.deferred_count <= 0 {
+ return
+ }
ast_context.deferred_count -= 1
ast_context.current_package =
ast_context.deferred_package[ast_context.deferred_count]
- assert(ast_context.deferred_count >= 0)
}
@(deferred_in = set_ast_package_none_deferred)
set_ast_package_scoped :: proc(ast_context: ^AstContext) {
+ if ast_context.deferred_count >= DeferredDepth {
+ return
+ }
ast_context.deferred_package[ast_context.deferred_count] =
ast_context.current_package
ast_context.deferred_count += 1
@@ -165,10 +179,12 @@ set_ast_package_from_symbol_deferred :: proc(
ast_context: ^AstContext,
symbol: Symbol,
) {
+ if ast_context.deferred_count <= 0 {
+ return
+ }
ast_context.deferred_count -= 1
ast_context.current_package =
ast_context.deferred_package[ast_context.deferred_count]
- assert(ast_context.deferred_count >= 0)
}
@(deferred_in = set_ast_package_from_symbol_deferred)
@@ -176,6 +192,10 @@ set_ast_package_from_symbol_scoped :: proc(
ast_context: ^AstContext,
symbol: Symbol,
) {
+ if ast_context.deferred_count >= DeferredDepth {
+ return
+ }
+
ast_context.deferred_package[ast_context.deferred_count] =
ast_context.current_package
ast_context.deferred_count += 1
@@ -604,10 +624,22 @@ resolve_function_overload :: proc(
Symbol,
bool,
) {
+ old_overloading := ast_context.overloading
+ ast_context.overloading = true
+
+ defer {
+ ast_context.overloading = old_overloading
+ }
+
using ast
call_expr := ast_context.call
+ //If there is nothing to resolve from, we actually want to get the invalid overloaded results through setting overloading to false
+ if call_expr == nil || len(call_expr.args) == 0 {
+ ast_context.overloading = false
+ }
+
candidates := make([dynamic]Symbol, context.temp_allocator)
for arg_expr in group.args {
@@ -822,11 +854,6 @@ resolve_type_expression :: proc(
Symbol,
bool,
) {
- //Try to prevent stack overflows and prevent indexing out of bounds.
- if ast_context.deferred_count >= DeferredDepth {
- return {}, false
- }
-
clear(&ast_context.recursion_map)
return internal_resolve_type_expression(ast_context, node)
}
@@ -842,6 +869,11 @@ internal_resolve_type_expression :: proc(
return {}, false
}
+ //Try to prevent stack overflows and prevent indexing out of bounds.
+ if ast_context.deferred_count >= DeferredDepth {
+ return {}, false
+ }
+
set_ast_package_scoped(ast_context)
if check_node_recursion(ast_context, node) {
@@ -1317,11 +1349,6 @@ resolve_type_identifier :: proc(
Symbol,
bool,
) {
- //Try to prevent stack overflows and prevent indexing out of bounds.
- if ast_context.deferred_count > DeferredDepth {
- return {}, false
- }
-
return internal_resolve_type_identifier(ast_context, node)
}
@@ -1338,6 +1365,11 @@ internal_resolve_type_identifier :: proc(
return {}, false
}
+ //Try to prevent stack overflows and prevent indexing out of bounds.
+ if ast_context.deferred_count >= DeferredDepth {
+ return {}, false
+ }
+
set_ast_package_scoped(ast_context)
if v, ok := common.keyword_map[node.name]; ok {
@@ -1368,6 +1400,20 @@ internal_resolve_type_identifier :: proc(
}
}
+ for imp in ast_context.imports {
+ if strings.compare(imp.base, node.name) == 0 {
+ symbol := Symbol {
+ type = .Package,
+ pkg = imp.name,
+ value = SymbolPackageValue{},
+ }
+
+ try_build_package(symbol.pkg)
+
+ return symbol, true
+ }
+ }
+
if local, ok := get_local(ast_context^, node);
ok && ast_context.use_locals {
is_distinct := false
@@ -1432,22 +1478,10 @@ internal_resolve_type_identifier :: proc(
make_symbol_bit_field_from_ast(ast_context, v^, node), true
return_symbol.name = node.name
case ^Proc_Lit:
- if !is_procedure_generic(v.type) {
- return_symbol, ok =
- make_symbol_procedure_from_ast(
- ast_context,
- local.rhs,
- v.type^,
- node,
- {},
- false,
- ),
- true
- } else {
- if return_symbol, ok = resolve_generic_function(
- ast_context,
- v^,
- ); !ok {
+ if is_procedure_generic(v.type) {
+ return_symbol, ok = resolve_generic_function(ast_context, v^)
+
+ if !ok && !ast_context.overloading {
return_symbol, ok =
make_symbol_procedure_from_ast(
ast_context,
@@ -1459,6 +1493,18 @@ internal_resolve_type_identifier :: proc(
),
true
}
+ } else {
+
+ return_symbol, ok =
+ make_symbol_procedure_from_ast(
+ ast_context,
+ local.rhs,
+ v.type^,
+ node,
+ {},
+ false,
+ ),
+ true
}
case ^Proc_Group:
return_symbol, ok = resolve_function_overload(ast_context, v^)
@@ -1568,22 +1614,11 @@ internal_resolve_type_identifier :: proc(
make_symbol_bit_field_from_ast(ast_context, v^, node), true
return_symbol.name = node.name
case ^Proc_Lit:
- if !is_procedure_generic(v.type) {
- return_symbol, ok =
- make_symbol_procedure_from_ast(
- ast_context,
- global.expr,
- v.type^,
- node,
- global.attributes,
- false,
- ),
- true
- } else {
- if return_symbol, ok = resolve_generic_function(
- ast_context,
- v^,
- ); !ok {
+ if is_procedure_generic(v.type) {
+ return_symbol, ok = resolve_generic_function(ast_context, v^)
+
+ //If we are not overloading just show the unresolved generic function
+ if !ok && !ast_context.overloading {
return_symbol, ok =
make_symbol_procedure_from_ast(
ast_context,
@@ -1595,6 +1630,17 @@ internal_resolve_type_identifier :: proc(
),
true
}
+ } else {
+ return_symbol, ok =
+ make_symbol_procedure_from_ast(
+ ast_context,
+ global.expr,
+ v.type^,
+ node,
+ global.attributes,
+ false,
+ ),
+ true
}
case ^Proc_Group:
return_symbol, ok = resolve_function_overload(ast_context, v^)
@@ -1680,20 +1726,6 @@ internal_resolve_type_identifier :: proc(
}
}
- for imp in ast_context.imports {
- if strings.compare(imp.base, node.name) == 0 {
- symbol := Symbol {
- type = .Package,
- pkg = imp.name,
- value = SymbolPackageValue{},
- }
-
- try_build_package(symbol.pkg)
-
- return symbol, true
- }
- }
-
for built in indexer.builtin_packages {
if symbol, ok := lookup(node.name, built); ok {
return resolve_symbol_return(ast_context, symbol)
@@ -1853,6 +1885,34 @@ resolve_comp_literal :: proc(
value.arg_types[arg_index].type,
) or_return
}
+ } else if position_context.returns != nil {
+ return_index: int
+
+ if position_context.returns.results == nil {
+ return {}, false
+ }
+
+ for result, i in position_context.returns.results {
+ if position_in_node(result, position_context.position) {
+ return_index = i
+ break
+ }
+ }
+
+ if position_context.function.type == nil {
+ return {}, false
+ }
+
+ if position_context.function.type.results == nil {
+ return {}, false
+ }
+
+ if len(position_context.function.type.results.list) > return_index {
+ symbol = resolve_type_expression(
+ ast_context,
+ position_context.function.type.results.list[return_index].type,
+ ) or_return
+ }
}
set_ast_package_set_scoped(ast_context, symbol.pkg)
@@ -2212,6 +2272,7 @@ resolve_location_identifier :: proc(
uri := common.create_uri(local.lhs.pos.file, ast_context.allocator)
symbol.pkg = ast_context.document_package
symbol.uri = uri.uri
+ symbol.flags |= {.Local}
return symbol, true
} else if global, ok := ast_context.globals[node.name]; ok {
symbol.range = common.get_token_range(
@@ -2278,6 +2339,8 @@ resolve_location_implicit_selector :: proc(
symbol: Symbol,
ok: bool,
) {
+ ok = true
+
reset_ast_context(ast_context)
set_ast_package_set_scoped(ast_context, ast_context.document_package)
@@ -2295,6 +2358,15 @@ resolve_location_implicit_selector :: proc(
symbol.range = v.ranges[i]
}
}
+ case SymbolUnionValue:
+ enum_value := unwrap_super_enum(ast_context, v) or_return
+ for name, i in enum_value.names {
+ if strings.compare(name, implicit_selector.field.name) == 0 {
+ symbol.range = enum_value.ranges[i]
+ }
+ }
+ case:
+ ok = false
}
return symbol, ok
@@ -2302,7 +2374,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,
@@ -2311,40 +2383,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
}
@@ -2553,6 +2630,17 @@ make_int_ast :: proc(
return ident
}
+make_rune_ast :: proc(
+ ast_context: ^AstContext,
+ pos: tokenizer.Pos,
+ end: tokenizer.Pos,
+) -> ^ast.Ident {
+ ident := new_type(ast.Ident, pos, end, ast_context.allocator)
+ ident.name = "rune"
+ return ident
+}
+
+
make_ident_ast :: proc(
ast_context: ^AstContext,
pos: tokenizer.Pos,
@@ -3464,6 +3552,24 @@ get_locals_for_range_stmt :: proc(
if symbol, ok := resolve_type_expression(ast_context, stmt.expr); ok {
#partial switch v in symbol.value {
+ case SymbolBasicValue:
+ if ident, ok := unwrap_ident(stmt.vals[0]); ok {
+ if v.ident.name == "string" {
+ store_local(
+ ast_context,
+ ident,
+ make_rune_ast(ast_context, ident.pos, ident.end),
+ ident.pos.offset,
+ ident.name,
+ ast_context.local_id,
+ ast_context.non_mutable_only,
+ false,
+ true,
+ symbol.pkg,
+ false,
+ )
+ }
+ }
case SymbolMapValue:
if len(stmt.vals) >= 1 {
if ident, ok := unwrap_ident(stmt.vals[0]); ok {
@@ -3837,296 +3943,6 @@ clear_locals :: proc(ast_context: ^AstContext) {
clear(&ast_context.usings)
}
-ResolveReferenceFlag :: enum {
- None,
- Variable,
- Constant,
- StructElement,
- EnumElement,
-}
-
-resolve_entire_file :: proc(
- document: ^Document,
- reference := "",
- flag := ResolveReferenceFlag.None,
- save_unresolved := false,
- allocator := context.allocator,
-) -> map[uintptr]SymbolAndNode {
- ast_context := make_ast_context(
- document.ast,
- document.imports,
- document.package_name,
- document.uri.uri,
- document.fullpath,
- allocator,
- )
-
- get_globals(document.ast, &ast_context)
-
- set_ast_package_set_scoped(&ast_context, ast_context.document_package)
-
- symbols := make(map[uintptr]SymbolAndNode, 10000, allocator)
-
- for decl in document.ast.decls {
- if _, is_value := decl.derived.(^ast.Value_Decl); !is_value {
- continue
- }
-
- resolve_entire_decl(
- &ast_context,
- document,
- decl,
- &symbols,
- reference,
- flag,
- save_unresolved,
- allocator,
- )
- clear(&ast_context.locals)
- }
-
- return symbols
-}
-
-resolve_entire_decl :: proc(
- ast_context: ^AstContext,
- document: ^Document,
- decl: ^ast.Node,
- symbols: ^map[uintptr]SymbolAndNode,
- reference := "",
- flag := ResolveReferenceFlag.None,
- save_unresolved := false,
- allocator := context.allocator,
-) {
- Scope :: struct {
- offset: int,
- id: int,
- }
-
- Visit_Data :: struct {
- ast_context: ^AstContext,
- symbols: ^map[uintptr]SymbolAndNode,
- scopes: [dynamic]Scope,
- id_counter: int,
- last_visit: ^ast.Node,
- resolve_flag: ResolveReferenceFlag,
- reference: string,
- save_unresolved: bool,
- document: ^Document,
- }
-
- data := Visit_Data {
- ast_context = ast_context,
- symbols = symbols,
- scopes = make([dynamic]Scope, allocator),
- resolve_flag = flag,
- reference = reference,
- document = document,
- save_unresolved = save_unresolved,
- }
-
- visit :: proc(visitor: ^ast.Visitor, node: ^ast.Node) -> ^ast.Visitor {
- if node == nil || visitor == nil {
- return nil
- }
- data := cast(^Visit_Data)visitor.data
- ast_context := data.ast_context
-
- reset_ast_context(ast_context)
-
- data.last_visit = node
-
- //It's somewhat silly to check the scope everytime, but the alternative is to implement my own walker function.
- if len(data.scopes) > 0 {
- current_scope := data.scopes[len(data.scopes) - 1]
-
- if current_scope.offset < node.end.offset {
- clear_local_group(ast_context, current_scope.id)
-
- pop(&data.scopes)
-
- if len(data.scopes) > 0 {
- current_scope = data.scopes[len(data.scopes) - 1]
- ast_context.local_id = current_scope.id
- } else {
- ast_context.local_id = 0
- }
- }
- }
-
- #partial switch v in node.derived {
- case ^ast.Proc_Lit:
- if v.body == nil {
- break
- }
-
- scope: Scope
- scope.id = data.id_counter
- scope.offset = node.end.offset
- data.id_counter += 1
- ast_context.local_id = scope.id
-
- append(&data.scopes, scope)
- add_local_group(ast_context, scope.id)
-
- position_context: DocumentPositionContext
- position_context.position = node.end.offset
-
- get_locals_proc_param_and_results(
- ast_context.file,
- v^,
- ast_context,
- &position_context,
- )
- get_locals_stmt(
- ast_context.file,
- cast(^ast.Stmt)node,
- ast_context,
- &position_context,
- )
- case ^ast.If_Stmt,
- ^ast.For_Stmt,
- ^ast.Range_Stmt,
- ^ast.Inline_Range_Stmt:
- scope: Scope
- scope.id = data.id_counter
- scope.offset = node.end.offset
- data.id_counter += 1
- ast_context.local_id = scope.id
-
- append(&data.scopes, scope)
- add_local_group(ast_context, scope.id)
-
- position_context: DocumentPositionContext
- position_context.position = node.end.offset
- get_locals_stmt(
- ast_context.file,
- cast(^ast.Stmt)node,
- ast_context,
- &position_context,
- )
- }
-
- if data.resolve_flag == .None {
- #partial switch v in node.derived {
- case ^ast.Ident:
- if symbol, ok := resolve_type_identifier(ast_context, v^); ok {
- data.symbols[cast(uintptr)node] = SymbolAndNode {
- node = v,
- symbol = symbol,
- is_resolved = true,
- }
- } else if data.save_unresolved {
- data.symbols[cast(uintptr)node] = SymbolAndNode {
- node = v,
- }
- }
- case ^ast.Selector_Expr:
- if symbol, ok := resolve_type_expression(ast_context, &v.node);
- ok {
- data.symbols[cast(uintptr)node] = SymbolAndNode {
- node = v,
- symbol = symbol,
- is_resolved = true,
- }
- } else if data.save_unresolved {
- data.symbols[cast(uintptr)node] = SymbolAndNode {
- node = v,
- }
- }
- case ^ast.Call_Expr:
- if symbol, ok := resolve_type_expression(ast_context, &v.node);
- ok {
- data.symbols[cast(uintptr)node] = SymbolAndNode {
- node = v,
- symbol = symbol,
- is_resolved = true,
- }
- } else if data.save_unresolved {
- data.symbols[cast(uintptr)node] = SymbolAndNode {
- node = v,
- }
- }
- }
- } else {
- #partial done: switch v in node.derived {
- case ^ast.Selector_Expr:
- document: ^Document = data.document
-
- position_context := DocumentPositionContext {
- position = v.pos.offset,
- }
-
- get_document_position_decls(
- document.ast.decls[:],
- &position_context,
- )
-
- if symbol, ok := resolve_location_selector(ast_context, v);
- ok {
- data.symbols[cast(uintptr)node] = SymbolAndNode {
- node = v.field,
- symbol = symbol,
- }
- }
-
- if _, is_ident := v.field.derived.(^ast.Ident); is_ident {
- if data.resolve_flag == .Constant ||
- data.resolve_flag == .Variable {
- return nil
- }
- }
- case ^ast.Ident:
- if data.resolve_flag == .Variable && v.name != data.reference {
- break done
- }
-
- document: ^Document = data.document
-
- position_context := DocumentPositionContext {
- position = v.pos.offset,
- }
-
- get_document_position_decls(
- document.ast.decls[:],
- &position_context,
- )
-
- if position_context.field_value != nil &&
- position_in_node(
- position_context.field_value.field,
- v.pos.offset,
- ) {
- break done
- } else if position_context.struct_type != nil &&
- data.resolve_flag != .StructElement {
- break done
- } else if position_context.enum_type != nil &&
- data.resolve_flag != .EnumElement {
- break done
- }
-
- if symbol, ok := resolve_location_identifier(ast_context, v^);
- ok {
- data.symbols[cast(uintptr)node] = SymbolAndNode {
- node = v,
- symbol = symbol,
- }
- }
- }
- }
-
- return visitor
- }
-
- visitor := ast.Visitor {
- data = &data,
- visit = visit,
- }
-
- ast.walk(&visitor, decl)
-}
-
concatenate_symbol_information :: proc {
concatenate_raw_symbol_information,
concatenate_raw_string_information,
@@ -4246,12 +4062,37 @@ unwrap_enum :: proc(
if enum_symbol, ok := resolve_type_expression(ast_context, node); ok {
if enum_value, ok := enum_symbol.value.(SymbolEnumValue); ok {
return enum_value, true
+ } else if union_value, ok := enum_symbol.value.(SymbolUnionValue); ok {
+ return unwrap_super_enum(ast_context, union_value)
}
}
return {}, false
}
+unwrap_super_enum :: proc(
+ ast_context: ^AstContext,
+ symbol_union: SymbolUnionValue,
+) -> (
+ ret_value: SymbolEnumValue,
+ ret_ok: bool,
+) {
+ names := make([dynamic]string, 0, 20, ast_context.allocator)
+ ranges := make([dynamic]common.Range, 0, 20, ast_context.allocator)
+
+ for type in symbol_union.types {
+ symbol := resolve_type_expression(ast_context, type) or_return
+ value := symbol.value.(SymbolEnumValue) or_return
+ append(&names, ..value.names)
+ append(&ranges, ..value.ranges)
+ }
+
+ ret_value.names = names[:]
+ ret_value.ranges = ranges[:]
+
+ return ret_value, true
+}
+
unwrap_union :: proc(
ast_context: ^AstContext,
node: ^ast.Expr,
@@ -4284,6 +4125,9 @@ unwrap_bitset :: proc(
); ok {
if enum_value, ok := enum_symbol.value.(SymbolEnumValue); ok {
return enum_value, true
+ } else if union_value, ok := enum_symbol.value.(SymbolUnionValue);
+ ok {
+ return unwrap_super_enum(ast_context, union_value)
}
}
}
@@ -5045,6 +4889,19 @@ position_in_node :: proc(
)
}
+get_document_position_label :: proc(
+ label: ^ast.Expr,
+ position_context: ^DocumentPositionContext,
+) {
+ if label == nil {
+ return
+ }
+
+ if ident, ok := label.derived.(^ast.Ident); ok {
+ position_context.label = ident
+ }
+}
+
get_document_position_node :: proc(
node: ^ast.Node,
position_context: ^DocumentPositionContext,
@@ -5081,12 +4938,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.
@@ -5104,28 +4963,25 @@ get_document_position_node :: proc(
get_document_position(n.expr, position_context)
case ^Binary_Expr:
if position_context.parent_binary == nil {
- position_context.parent_binary = cast(^Binary_Expr)node
+ position_context.parent_binary = n
}
- position_context.binary = cast(^Binary_Expr)node
+ position_context.binary = n
get_document_position(n.left, position_context)
get_document_position(n.right, position_context)
case ^Paren_Expr:
get_document_position(n.expr, position_context)
case ^Call_Expr:
- if position_context.hint == .SignatureHelp ||
- position_context.hint == .Completion ||
- position_context.hint == .Definition {
- position_context.call = cast(^Expr)node
- }
+ position_context.call = n
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
@@ -5139,18 +4995,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 {
@@ -5158,6 +5007,8 @@ get_document_position_node :: proc(
get_document_position(n.field, position_context)
}
case ^Index_Expr:
+ position_context.previous_index = position_context.index
+ position_context.index = n
get_document_position(n.expr, position_context)
get_document_position(n.index, position_context)
case ^Deref_Expr:
@@ -5167,7 +5018,7 @@ get_document_position_node :: proc(
get_document_position(n.low, position_context)
get_document_position(n.high, position_context)
case ^Field_Value:
- position_context.field_value = cast(^Field_Value)node
+ position_context.field_value = n
get_document_position(n.field, position_context)
get_document_position(n.value, position_context)
case ^Ternary_If_Expr:
@@ -5191,17 +5042,17 @@ get_document_position_node :: proc(
case ^Expr_Stmt:
get_document_position(n.expr, position_context)
case ^Tag_Stmt:
- r := cast(^Tag_Stmt)node
+ r := n
get_document_position(r.stmt, position_context)
case ^Assign_Stmt:
- position_context.assign = cast(^Assign_Stmt)node
+ position_context.assign = n
get_document_position(n.lhs, position_context)
get_document_position(n.rhs, position_context)
case ^Block_Stmt:
- get_document_position(n.label, position_context)
+ get_document_position_label(n.label, position_context)
get_document_position(n.stmts, position_context)
case ^If_Stmt:
- get_document_position(n.label, position_context)
+ get_document_position_label(n.label, position_context)
get_document_position(n.init, position_context)
get_document_position(n.cond, position_context)
get_document_position(n.body, position_context)
@@ -5211,18 +5062,18 @@ get_document_position_node :: proc(
get_document_position(n.body, position_context)
get_document_position(n.else_stmt, position_context)
case ^Return_Stmt:
- position_context.returns = cast(^Return_Stmt)node
+ position_context.returns = n
get_document_position(n.results, position_context)
case ^Defer_Stmt:
get_document_position(n.stmt, position_context)
case ^For_Stmt:
- get_document_position(n.label, position_context)
+ get_document_position_label(n.label, position_context)
get_document_position(n.init, position_context)
get_document_position(n.cond, position_context)
get_document_position(n.post, position_context)
get_document_position(n.body, position_context)
case ^Range_Stmt:
- get_document_position(n.label, position_context)
+ get_document_position_label(n.label, position_context)
get_document_position(n.vals, position_context)
get_document_position(n.expr, position_context)
get_document_position(n.body, position_context)
@@ -5238,18 +5089,18 @@ get_document_position_node :: proc(
get_document_position(n.body, position_context)
case ^Switch_Stmt:
position_context.switch_stmt = cast(^Switch_Stmt)node
- get_document_position(n.label, position_context)
+ get_document_position_label(n.label, position_context)
get_document_position(n.init, position_context)
get_document_position(n.cond, position_context)
get_document_position(n.body, position_context)
case ^Type_Switch_Stmt:
position_context.switch_type_stmt = cast(^Type_Switch_Stmt)node
- get_document_position(n.label, position_context)
+ get_document_position_label(n.label, position_context)
get_document_position(n.tag, position_context)
get_document_position(n.expr, position_context)
get_document_position(n.body, position_context)
case ^Branch_Stmt:
- get_document_position(n.label, position_context)
+ get_document_position_label(n.label, position_context)
case ^Using_Stmt:
get_document_position(n.list, position_context)
case ^Bad_Decl:
@@ -5307,21 +5158,21 @@ get_document_position_node :: proc(
case ^Multi_Pointer_Type:
get_document_position(n.elem, position_context)
case ^Struct_Type:
- position_context.struct_type = cast(^Struct_Type)node
+ position_context.struct_type = n
get_document_position(n.poly_params, position_context)
get_document_position(n.align, position_context)
get_document_position(n.fields, position_context)
case ^Union_Type:
- position_context.union_type = cast(^Union_Type)node
+ position_context.union_type = n
get_document_position(n.poly_params, position_context)
get_document_position(n.align, position_context)
get_document_position(n.variants, position_context)
case ^Enum_Type:
- position_context.enum_type = cast(^Enum_Type)node
+ position_context.enum_type = n
get_document_position(n.base_type, position_context)
get_document_position(n.fields, position_context)
case ^Bit_Set_Type:
- position_context.bitset_type = cast(^Bit_Set_Type)node
+ position_context.bitset_type = n
get_document_position(n.elem, position_context)
get_document_position(n.underlying, position_context)
case ^Map_Type:
@@ -5337,7 +5188,7 @@ get_document_position_node :: proc(
case ^ast.Or_Return_Expr:
get_document_position(n.expr, position_context)
case ^ast.Bit_Field_Type:
- position_context.bit_field_type = cast(^Bit_Field_Type)node
+ position_context.bit_field_type = n
get_document_position(n.backing_type, position_context)
get_document_position(n.fields, position_context)
case ^ast.Bit_Field_Field:
@@ -5345,8 +5196,8 @@ get_document_position_node :: proc(
get_document_position(n.type, position_context)
get_document_position(n.bit_size, position_context)
case ^ast.Or_Branch_Expr:
+ get_document_position_label(n.label, position_context)
get_document_position(n.expr, position_context)
- get_document_position(n.label, position_context)
case:
}
}
diff --git a/src/server/build.odin b/src/server/build.odin
index 810c161..312cb00 100644
--- a/src/server/build.odin
+++ b/src/server/build.odin
@@ -189,17 +189,7 @@ setup_index :: proc() {
)
indexer.index = make_memory_index(symbol_collection)
- dir_exe, ok := filepath.abs(
- path.dir(os.args[0], context.temp_allocator),
- context.temp_allocator,
- )
-
- if !ok {
- log.error(
- "Failed to find ols executable path to build the builtin packages",
- )
- return
- }
+ dir_exe := common.get_executable_path()
try_build_package(path.join({dir_exe, "builtin"}, context.temp_allocator))
}
diff --git a/src/server/caches.odin b/src/server/caches.odin
index d286624..a861cc6 100644
--- a/src/server/caches.odin
+++ b/src/server/caches.odin
@@ -24,9 +24,7 @@ resolve_entire_file_cached :: proc(
file_resolve_cache.files[document.uri.uri] = FileResolve {
symbols = resolve_entire_file(
document,
- "",
.None,
- false,
common.scratch_allocator(document.allocator),
),
}
diff --git a/src/server/collector.odin b/src/server/collector.odin
index eecf2ed..bdc69a6 100644
--- a/src/server/collector.odin
+++ b/src/server/collector.odin
@@ -39,6 +39,7 @@ SymbolPackage :: struct {
symbols: map[string]Symbol,
objc_structs: map[string]ObjcStruct, //mapping from struct name to function
methods: map[Method][dynamic]Symbol,
+ imports: [dynamic]string, //Used for references to figure whether the package is even able to reference the symbol
}
get_index_unique_string :: proc {
@@ -100,6 +101,7 @@ delete_symbol_collection :: proc(collection: SymbolCollection) {
delete(v.methods)
delete(v.objc_structs)
delete(v.symbols)
+ delete(v.imports)
}
delete(collection.packages)
@@ -566,6 +568,10 @@ collect_objc :: proc(
}
}
+collect_imports :: proc(collection: ^SymbolCollection, file: ast.File) {
+
+}
+
collect_symbols :: proc(
collection: ^SymbolCollection,
file: ast.File,
@@ -577,6 +583,8 @@ collect_symbols :: proc(
exprs := common.collect_globals(file, true)
+ collect_imports(collection, file)
+
for expr in exprs {
symbol: Symbol
diff --git a/src/server/completion.odin b/src/server/completion.odin
index d3dfa08..42bec3e 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 {
@@ -1271,6 +1283,42 @@ get_implicit_completion :: proc(
}
}
}
+
+ if position_context.index != nil {
+ symbol: Symbol
+ ok := false
+ if position_context.previous_index != nil {
+ symbol, ok = resolve_type_expression(
+ ast_context,
+ position_context.previous_index,
+ )
+ if !ok {
+ return
+ }
+ } else {
+ symbol, ok = resolve_type_expression(
+ ast_context,
+ position_context.index.expr,
+ )
+ }
+
+ if array, ok := symbol.value.(SymbolFixedArrayValue); ok {
+ if enum_value, ok := unwrap_enum(ast_context, array.len); ok {
+ for name in enum_value.names {
+ item := CompletionItem {
+ label = name,
+ kind = .EnumMember,
+ detail = name,
+ }
+
+ append(&items, item)
+ }
+
+ list.items = items[:]
+ return
+ }
+ }
+ }
}
get_identifier_completion :: proc(
@@ -1398,6 +1446,10 @@ get_identifier_completion :: proc(
k,
)
+ if local_offset == -1 {
+ continue
+ }
+
reset_ast_context(ast_context)
ast_context.current_package = ast_context.document_package
diff --git a/src/server/definition.odin b/src/server/definition.odin
index 021a556..9d32faa 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 {
@@ -130,6 +130,8 @@ get_definition_location :: proc(
); ok {
location.range = resolved.range
uri = resolved.uri
+ } else {
+ return {}, false
}
} else if position_context.field_value != nil &&
position_context.comp_lit != nil &&
diff --git a/src/server/file_resolve.odin b/src/server/file_resolve.odin
new file mode 100644
index 0000000..09da787
--- /dev/null
+++ b/src/server/file_resolve.odin
@@ -0,0 +1,540 @@
+package server
+
+import "core:fmt"
+import "core:log"
+import "core:mem"
+import "core:odin/ast"
+import "core:odin/parser"
+import "core:odin/tokenizer"
+import "core:path/filepath"
+import path "core:path/slashpath"
+import "core:reflect"
+import "core:slice"
+import "core:sort"
+import "core:strconv"
+import "core:strings"
+import "core:unicode/utf8"
+
+import "src:common"
+
+ResolveReferenceFlag :: enum {
+ None,
+ Identifier,
+ Base,
+ Field,
+}
+
+@(private = "file")
+reset_position_context :: proc(position_context: ^DocumentPositionContext) {
+ position_context.comp_lit = nil
+ position_context.parent_comp_lit = nil
+ position_context.identifier = nil
+ position_context.call = nil
+ position_context.binary = nil
+ position_context.parent_binary = nil
+ position_context.previous_index = nil
+ position_context.index = nil
+}
+
+resolve_entire_file :: proc(
+ document: ^Document,
+ flag := ResolveReferenceFlag.None,
+ 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)
+
+ for decl in document.ast.decls {
+ if _, is_value := decl.derived.(^ast.Value_Decl); !is_value {
+ continue
+ }
+
+ resolve_decl(
+ &position_context,
+ &ast_context,
+ document,
+ decl,
+ &symbols,
+ flag,
+ allocator,
+ )
+ clear(&ast_context.locals)
+ }
+
+ return symbols
+}
+
+FileResolveData :: struct {
+ ast_context: ^AstContext,
+ symbols: ^map[uintptr]SymbolAndNode,
+ id_counter: int,
+ document: ^Document,
+ position_context: ^DocumentPositionContext,
+ flag: ResolveReferenceFlag,
+}
+
+@(private = "file")
+resolve_decl :: proc(
+ position_context: ^DocumentPositionContext,
+ ast_context: ^AstContext,
+ document: ^Document,
+ decl: ^ast.Node,
+ symbols: ^map[uintptr]SymbolAndNode,
+ flag: ResolveReferenceFlag,
+ allocator := context.allocator,
+) {
+ data := FileResolveData {
+ position_context = position_context,
+ ast_context = ast_context,
+ symbols = symbols,
+ document = document,
+ flag = flag,
+ }
+
+ resolve_node(decl, &data)
+}
+
+
+@(private = "file")
+local_scope_deferred :: proc(data: ^FileResolveData, stmt: ^ast.Stmt) {
+ clear_local_group(data.ast_context, data.ast_context.local_id)
+ data.ast_context.local_id -= 1
+}
+
+@(deferred_in = local_scope_deferred)
+@(private = "file")
+local_scope :: proc(data: ^FileResolveData, stmt: ^ast.Stmt) {
+ if stmt == nil {
+ return
+ }
+
+ data.ast_context.local_id += 1
+
+ add_local_group(data.ast_context, data.ast_context.local_id)
+
+ data.position_context.position = stmt.end.offset
+
+ get_locals_stmt(
+ data.ast_context.file,
+ stmt,
+ data.ast_context,
+ data.position_context,
+ )
+}
+
+@(private = "file")
+resolve_node :: proc(node: ^ast.Node, data: ^FileResolveData) {
+ using ast
+
+ if node == nil {
+ return
+ }
+
+ reset_ast_context(data.ast_context)
+
+ #partial switch n in node.derived {
+ 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 {
+ data.symbols[cast(uintptr)node] = SymbolAndNode {
+ node = n,
+ symbol = symbol,
+ }
+ }
+ } else {
+ if symbol, ok := resolve_type_identifier(data.ast_context, n^);
+ ok {
+ data.symbols[cast(uintptr)node] = SymbolAndNode {
+ node = n,
+ symbol = symbol,
+ }
+ }
+ }
+ case ^Selector_Call_Expr:
+ data.position_context.selector = n.expr
+ data.position_context.field = n.call
+ data.position_context.selector_expr = node
+
+ if _, ok := n.call.derived.(^ast.Call_Expr); ok {
+ data.position_context.call = n.call
+ }
+
+ resolve_node(n.expr, data)
+ resolve_node(n.call, data)
+ case ^Implicit_Selector_Expr:
+ data.position_context.implicit = true
+ data.position_context.implicit_selector_expr = n
+ if data.flag != .None {
+ data.position_context.position = n.pos.offset
+ if symbol, ok := resolve_location_implicit_selector(
+ data.ast_context,
+ data.position_context,
+ n,
+ ); ok {
+ data.symbols[cast(uintptr)node] = SymbolAndNode {
+ node = n,
+ symbol = symbol,
+ }
+ }
+ }
+ resolve_node(n.field, data)
+ case ^Selector_Expr:
+ data.position_context.selector = n.expr
+ data.position_context.field = n.field
+ data.position_context.selector_expr = node
+
+ if data.flag != .None {
+ if symbol, ok := resolve_location_selector(data.ast_context, n);
+ ok {
+ if data.flag != .Base {
+ 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(
+ data.ast_context,
+ &n.node,
+ ); ok {
+ data.symbols[cast(uintptr)node] = SymbolAndNode {
+ node = n,
+ symbol = symbol,
+ }
+ }
+ }
+
+ resolve_node(n.expr, data)
+ resolve_node(n.field, data)
+ case ^Field_Value:
+ data.position_context.field_value = n
+
+ if data.flag != .None && data.position_context.comp_lit != nil {
+ data.position_context.position = n.pos.offset
+
+ if symbol, ok := resolve_location_comp_lit_field(
+ data.ast_context,
+ data.position_context,
+ ); ok {
+ data.symbols[cast(uintptr)node] = SymbolAndNode {
+ node = n.field,
+ symbol = symbol,
+ }
+ }
+
+ resolve_node(n.value, data)
+ } else {
+ resolve_node(n.field, data)
+ resolve_node(n.value, data)
+ }
+ case ^Proc_Lit:
+ local_scope(data, n.body)
+
+ get_locals_proc_param_and_results(
+ data.ast_context.file,
+ n^,
+ data.ast_context,
+ data.position_context,
+ )
+
+ resolve_node(n.type, data)
+
+ data.position_context.function = cast(^Proc_Lit)node
+
+ append(
+ &data.position_context.functions,
+ data.position_context.function,
+ )
+
+ resolve_node(n.body, data)
+ case ^ast.Inline_Range_Stmt:
+ local_scope(data, n)
+ resolve_node(n.val0, data)
+ resolve_node(n.val1, data)
+ resolve_node(n.expr, data)
+ resolve_node(n.body, data)
+ case ^For_Stmt:
+ local_scope(data, n)
+ resolve_node(n.label, data)
+ resolve_node(n.init, data)
+ resolve_node(n.cond, data)
+ resolve_node(n.post, data)
+ resolve_node(n.body, data)
+ case ^Range_Stmt:
+ local_scope(data, n)
+ resolve_node(n.label, data)
+ resolve_nodes(n.vals, data)
+ resolve_node(n.expr, data)
+ resolve_node(n.body, data)
+ case ^Switch_Stmt:
+ local_scope(data, n)
+ data.position_context.switch_stmt = n
+ resolve_node(n.label, data)
+ resolve_node(n.init, data)
+ resolve_node(n.cond, data)
+ resolve_node(n.body, data)
+ case ^If_Stmt:
+ local_scope(data, n)
+ resolve_node(n.label, data)
+ resolve_node(n.init, data)
+ resolve_node(n.cond, data)
+ resolve_node(n.body, data)
+ resolve_node(n.else_stmt, data)
+ case ^When_Stmt:
+ resolve_node(n.cond, data)
+ resolve_node(n.body, data)
+ resolve_node(n.else_stmt, data)
+ case ^Implicit:
+ if n.tok.text == "context" {
+ data.position_context.implicit_context = n
+ }
+ case ^Undef:
+ case ^Basic_Lit:
+ data.position_context.basic_lit = cast(^Basic_Lit)node
+ case ^Matrix_Index_Expr:
+ resolve_node(n.expr, data)
+ resolve_node(n.row_index, data)
+ resolve_node(n.column_index, data)
+ case ^Matrix_Type:
+ resolve_node(n.row_count, data)
+ resolve_node(n.column_count, data)
+ resolve_node(n.elem, data)
+ case ^Ellipsis:
+ resolve_node(n.expr, data)
+ 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 = n
+ }
+
+ data.position_context.comp_lit = n
+
+ resolve_node(n.type, data)
+ resolve_nodes(n.elems, data)
+ case ^Tag_Expr:
+ resolve_node(n.expr, data)
+ case ^Unary_Expr:
+ resolve_node(n.expr, data)
+ case ^Binary_Expr:
+ if data.position_context.parent_binary == nil {
+ data.position_context.parent_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 = n
+ resolve_node(n.expr, data)
+
+ for arg in n.args {
+ data.position_context.position = arg.pos.offset
+ resolve_node(arg, data)
+ }
+ case ^Index_Expr:
+ resolve_node(n.expr, data)
+ resolve_node(n.index, data)
+ case ^Deref_Expr:
+ resolve_node(n.expr, data)
+ case ^Slice_Expr:
+ resolve_node(n.expr, data)
+ resolve_node(n.low, data)
+ resolve_node(n.high, data)
+ case ^Ternary_If_Expr:
+ resolve_node(n.x, data)
+ resolve_node(n.cond, data)
+ resolve_node(n.y, data)
+ case ^Ternary_When_Expr:
+ resolve_node(n.x, data)
+ resolve_node(n.cond, data)
+ resolve_node(n.y, data)
+ case ^Type_Assertion:
+ resolve_node(n.expr, data)
+ resolve_node(n.type, data)
+ case ^Type_Cast:
+ resolve_node(n.type, data)
+ resolve_node(n.expr, data)
+ case ^Auto_Cast:
+ resolve_node(n.expr, data)
+ case ^Bad_Stmt:
+ case ^Empty_Stmt:
+ case ^Expr_Stmt:
+ resolve_node(n.expr, data)
+ case ^Tag_Stmt:
+ r := cast(^Tag_Stmt)node
+ resolve_node(r.stmt, data)
+ case ^Block_Stmt:
+ resolve_node(n.label, data)
+ resolve_nodes(n.stmts, data)
+ case ^Return_Stmt:
+ data.position_context.returns = n
+ resolve_nodes(n.results, data)
+ case ^Defer_Stmt:
+ resolve_node(n.stmt, data)
+ case ^Case_Clause:
+ resolve_nodes(n.list, data)
+ resolve_nodes(n.body, data)
+ case ^Type_Switch_Stmt:
+ data.position_context.switch_type_stmt = n
+ resolve_node(n.label, data)
+ resolve_node(n.tag, data)
+ resolve_node(n.expr, data)
+ resolve_node(n.body, data)
+ case ^Branch_Stmt:
+ resolve_node(n.label, data)
+ case ^Using_Stmt:
+ resolve_nodes(n.list, data)
+ case ^Bad_Decl:
+ case ^Assign_Stmt:
+ data.position_context.assign = n
+ reset_position_context(data.position_context)
+ resolve_nodes(n.lhs, data)
+ resolve_nodes(n.rhs, data)
+ case ^Value_Decl:
+ data.position_context.value_decl = n
+ reset_position_context(data.position_context)
+ resolve_nodes(n.names, data)
+ resolve_node(n.type, data)
+ resolve_nodes(n.values, data)
+ case ^Package_Decl:
+ case ^Import_Decl:
+ case ^Foreign_Block_Decl:
+ resolve_node(n.foreign_library, data)
+ resolve_node(n.body, data)
+ case ^Foreign_Import_Decl:
+ resolve_node(n.name, data)
+ case ^Proc_Group:
+ resolve_nodes(n.args, data)
+ case ^Attribute:
+ resolve_nodes(n.elems, data)
+ case ^Field:
+ resolve_nodes(n.names, data)
+ resolve_node(n.type, data)
+ resolve_node(n.default_value, data)
+ case ^Field_List:
+ resolve_nodes(n.list, data)
+ case ^Typeid_Type:
+ resolve_node(n.specialization, data)
+ case ^Helper_Type:
+ resolve_node(n.type, data)
+ case ^Distinct_Type:
+ resolve_node(n.type, data)
+ case ^Poly_Type:
+ resolve_node(n.type, data)
+ resolve_node(n.specialization, data)
+ case ^Proc_Type:
+ resolve_node(n.params, data)
+ resolve_node(n.results, data)
+ case ^Pointer_Type:
+ resolve_node(n.elem, data)
+ case ^Array_Type:
+ resolve_node(n.len, data)
+ resolve_node(n.elem, data)
+ case ^Dynamic_Array_Type:
+ resolve_node(n.elem, data)
+ case ^Multi_Pointer_Type:
+ resolve_node(n.elem, data)
+ case ^Struct_Type:
+ data.position_context.struct_type = n
+ resolve_node(n.poly_params, data)
+ resolve_node(n.align, data)
+ resolve_node(n.fields, data)
+
+ if data.flag != .None {
+ for field in n.fields.list {
+ for name in field.names {
+ data.symbols[cast(uintptr)name] = SymbolAndNode {
+ node = name,
+ symbol = Symbol {
+ range = common.get_token_range(
+ name,
+ string(data.document.text),
+ ),
+ },
+ }
+ }
+ }
+ }
+ case ^Union_Type:
+ 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 = n
+ resolve_node(n.base_type, data)
+ resolve_nodes(n.fields, data)
+
+ if data.flag != .None {
+ for field in n.fields {
+ data.symbols[cast(uintptr)field] = SymbolAndNode {
+ node = field,
+ symbol = Symbol {
+ range = common.get_token_range(
+ field,
+ string(data.document.text),
+ ),
+ },
+ }
+ }
+ }
+ case ^Bit_Set_Type:
+ data.position_context.bitset_type = n
+ resolve_node(n.elem, data)
+ resolve_node(n.underlying, data)
+ case ^Map_Type:
+ resolve_node(n.key, data)
+ resolve_node(n.value, data)
+ case ^ast.Or_Else_Expr:
+ resolve_node(n.x, data)
+ resolve_node(n.y, data)
+ case ^ast.Or_Return_Expr:
+ resolve_node(n.expr, data)
+ case ^ast.Bit_Field_Type:
+ data.position_context.bit_field_type = n
+ resolve_node(n.backing_type, data)
+ resolve_nodes(n.fields, data)
+ case ^ast.Bit_Field_Field:
+ resolve_node(n.name, data)
+ resolve_node(n.type, data)
+ resolve_node(n.bit_size, data)
+ case ^ast.Or_Branch_Expr:
+ resolve_node(n.expr, data)
+ resolve_node(n.label, data)
+ case:
+ }
+
+
+}
+
+@(private = "file")
+resolve_nodes :: proc(array: []$T/^ast.Node, data: ^FileResolveData) {
+ for elem in array {
+ resolve_node(elem, data)
+ }
+}
diff --git a/src/server/generics.odin b/src/server/generics.odin
index 5ba22b8..237a271 100644
--- a/src/server/generics.odin
+++ b/src/server/generics.odin
@@ -40,6 +40,8 @@ resolve_poly :: proc(
case ^ast.Poly_Type:
specialization = v.specialization
type = v.type
+ case:
+ specialization = poly_node
}
if specialization == nil {
@@ -124,7 +126,7 @@ resolve_poly :: proc(
if call_struct, ok := call_node.derived.(^ast.Struct_Type); ok {
arg_index := 0
struct_value := call_symbol.value.(SymbolStructValue)
-
+ found := false
for arg in p.args {
if poly_type, ok := arg.derived.(^ast.Poly_Type); ok {
if poly_type.type == nil ||
@@ -140,8 +142,11 @@ resolve_poly :: proc(
)
arg_index += 1
+ found |= true
}
}
+
+ return found
}
case ^ast.Dynamic_Array_Type:
if call_array, ok := call_node.derived.(^ast.Dynamic_Array_Type); ok {
@@ -260,6 +265,25 @@ resolve_poly :: proc(
return true
}
}
+ case ^ast.Pointer_Type:
+ if call_pointer, ok := call_node.derived.(^ast.Pointer_Type); ok {
+ if poly_type, ok := p.elem.derived.(^ast.Poly_Type); ok {
+ if ident, ok := unwrap_ident(poly_type.type); ok {
+ save_poly_map(ident, call_pointer.elem, poly_map)
+ }
+
+ if poly_type.specialization != nil {
+ return resolve_poly(
+ ast_context,
+ call_pointer.elem,
+ call_symbol,
+ p.elem,
+ poly_map,
+ )
+ }
+ return true
+ }
+ }
case ^ast.Comp_Lit:
if comp_lit, ok := call_node.derived.(^ast.Comp_Lit); ok {
if poly_type, ok := p.type.derived.(^ast.Poly_Type); ok {
@@ -276,15 +300,14 @@ resolve_poly :: proc(
poly_map,
)
}
+ return true
}
}
- case ^ast.Ident:
- if n, ok := call_node.derived.(^ast.Ident); ok {
- return true
- }
case ^ast.Struct_Type, ^ast.Proc_Type:
+ case ^ast.Ident:
+ return true
case:
- log.panicf("Unhandled specialization %v", specialization.derived)
+ return false
}
return false
@@ -586,13 +609,18 @@ resolve_generic_function_symbol :: proc(
nil,
)
}
+ } else {
+ return {}, false
}
+ } else {
+ return {}, false
}
i += 1
}
}
+
for k, v in poly_map {
find_and_replace_poly_type(v, &poly_map)
}
@@ -644,28 +672,39 @@ resolve_generic_function_symbol :: proc(
append(&return_types, field)
}
+
for param in params {
- if len(param.names) == 0 {
- continue
+ field := cast(^ast.Field)clone_node(param, ast_context.allocator, nil)
+
+ if field.type != nil {
+ if poly_type, ok := field.type.derived.(^ast.Poly_Type); ok {
+ if expr, ok := poly_map[poly_type.type.name]; ok {
+ field.type = expr
+ }
+ } else {
+ if ident, ok := unwrap_ident(field.type); ok {
+ if expr, ok := poly_map[ident.name]; ok {
+ field.type = expr
+ }
+ }
+
+ find_and_replace_poly_type(field.type, &poly_map)
+ }
}
- //check the name for poly
- if poly_type, ok := param.names[0].derived.(^ast.Poly_Type);
- ok && param.type != nil {
- if m, ok := poly_map[poly_type.type.name]; ok {
- field := cast(^ast.Field)clone_node(
- param,
- ast_context.allocator,
- nil,
- )
- field.type = m
- append(&argument_types, field)
+ if len(param.names) > 0 {
+ if poly_type, ok := param.names[0].derived.(^ast.Poly_Type);
+ ok && param.type != nil {
+ if m, ok := poly_map[poly_type.type.name]; ok {
+ field.type = m
+ }
}
- } else {
- append(&argument_types, param)
}
+
+ append(&argument_types, field)
}
+
symbol.value = SymbolProcedureValue {
return_types = return_types[:],
arg_types = argument_types[:],
@@ -684,12 +723,9 @@ is_procedure_generic :: proc(proc_type: ^ast.Proc_Type) -> bool {
continue
}
- if expr, _, ok := common.unwrap_pointer_expr(param.type); ok {
- if _, ok := expr.derived.(^ast.Poly_Type); ok {
- return true
- }
+ if common.expr_contains_poly(param.type) {
+ return true
}
-
}
return false
diff --git a/src/server/imports.odin b/src/server/imports.odin
index 7bdf6a9..cd2271d 100644
--- a/src/server/imports.odin
+++ b/src/server/imports.odin
@@ -1,7 +1,7 @@
package server
-import "core:odin/ast"
import "core:mem"
+import "core:odin/ast"
fix_imports :: proc(document: ^Document) {
@@ -11,7 +11,7 @@ fix_imports :: proc(document: ^Document) {
context.allocator = mem.arena_allocator(&arena)
- symbols_and_nodes := resolve_entire_file(document, "", .None, true)
+ symbols_and_nodes := resolve_entire_file(document, .None)
}
diff --git a/src/server/references.odin b/src/server/references.odin
index 49d95bf..17add83 100644
--- a/src/server/references.odin
+++ b/src/server/references.odin
@@ -10,6 +10,7 @@ import "core:odin/parser"
import "core:os"
import "core:path/filepath"
import path "core:path/slashpath"
+import "core:slice"
import "core:strings"
import "src:common"
@@ -24,6 +25,8 @@ walk_directories :: proc(
err: os.Errno,
skip_dir: bool,
) {
+ document := cast(^Document)user_data
+
if info.is_dir {
return 0, false
}
@@ -33,230 +36,364 @@ walk_directories :: proc(
}
if strings.contains(info.name, ".odin") {
- append(&fullpaths, strings.clone(info.fullpath))
- }
-
- 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
- }
+ slash_path, _ := filepath.to_slash(
+ info.fullpath,
+ context.temp_allocator,
+ )
+ if slash_path != document.fullpath {
+ append(
+ &fullpaths,
+ strings.clone(info.fullpath, context.temp_allocator),
+ )
}
}
- return false
+ return 0, false
}
-
-resolve_references :: proc(
+prepare_references :: proc(
+ document: ^Document,
ast_context: ^AstContext,
position_context: ^DocumentPositionContext,
) -> (
- []common.Location,
- bool,
+ symbol: Symbol,
+ resolve_flag: ResolveReferenceFlag,
+ ok: bool,
) {
- locations := make([dynamic]common.Location, 0, ast_context.allocator)
- fullpaths = make([dynamic]string, 0, ast_context.allocator)
-
- resolve_flag: ResolveReferenceFlag
+ ok = false
reference := ""
- symbol: Symbol
- ok: bool
pkg := ""
- filepath.walk(
- filepath.dir(os.args[0], context.allocator),
- walk_directories,
- nil,
- )
-
- for workspace in common.config.workspace_folders {
- uri, _ := common.parse_uri(workspace.uri, context.temp_allocator)
- filepath.walk(uri.path, walk_directories, nil)
- }
-
- 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.label != nil {
+ return
+ } else if position_context.struct_type != nil {
+ found := false
+ done_struct: 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_struct
+ }
+ }
+ }
+ if !found {
+ return
+ }
} else if position_context.enum_type != nil {
- return {}, true
+ /*
+ found := false
+ done_enum: 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_enum
+ }
+ }
+ }
+ if !found {
+ return {}, false
+ }
+ */
} else if position_context.bitset_type != nil {
- return {}, true
+ return
} 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
+ found := false
+ for variant in position_context.union_type.variants {
+ if position_in_node(variant, position_context.position) {
+ if ident, ok := variant.derived.(^ast.Ident); ok {
+ symbol, ok = resolve_location_identifier(
+ ast_context,
+ ident^,
+ )
+ reference = ident.name
+ resolve_flag = .Identifier
+
+ if !ok {
+ return
+ }
+
+ found = true
+
+ break
+ } else {
+ return
+ }
}
- resolve_flag = .Constant
+ }
+ if !found {
+ return
}
- symbol, ok = resolve_location_selector(
+ } else if position_context.field_value != nil &&
+ position_context.comp_lit != nil &&
+ !common.is_expr_basic_lit(position_context.field_value.field) &&
+ position_in_node(
+ position_context.field_value.field,
+ position_context.position,
+ ) {
+ symbol, ok = resolve_location_comp_lit_field(
ast_context,
- position_context.selector_expr,
+ position_context,
)
if !ok {
- return {}, true
+ return
}
- if ident, ok := position_context.identifier.derived.(^ast.Ident); ok {
- reference = ident.name
- } else {
- return {}, true
+ //Only support structs for now
+ if _, ok := symbol.value.(SymbolStructValue); !ok {
+ return
}
- } 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
+ resolve_flag = .Field
+ } else if position_context.selector_expr != nil {
+ resolve_flag = .Field
+
+ base: ^ast.Ident
+ base, ok = position_context.selector.derived.(^ast.Ident)
+
+ if position_in_node(base, position_context.position) &&
+ position_context.identifier != nil &&
+ ok {
+
+ ident := position_context.identifier.derived.(^ast.Ident)
+
+ symbol, ok = resolve_location_identifier(ast_context, ident^)
+
+ if !ok {
+ return
}
+
+ 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 {
+ resolve_flag = .Field
+
+ symbol, ok = resolve_location_implicit_selector(
+ ast_context,
+ position_context,
+ position_context.implicit_selector_expr,
+ )
+
+ if !ok {
+ return
+ }
+ } 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
+ return
}
+ } else {
+ return
}
+ return symbol, resolve_flag, true
+}
+
+resolve_references :: proc(
+ document: ^Document,
+ ast_context: ^AstContext,
+ position_context: ^DocumentPositionContext,
+) -> (
+ []common.Location,
+ bool,
+) {
+ locations := make([dynamic]common.Location, 0, ast_context.allocator)
+ fullpaths = make([dynamic]string, 0, ast_context.allocator)
+
+ symbol, resolve_flag, ok := prepare_references(
+ document,
+ ast_context,
+ position_context,
+ )
+
if !ok {
return {}, true
}
- resolve_arena: mem.Arena
- mem.arena_init(&resolve_arena, make([]byte, mem.Megabyte * 25))
+ when !ODIN_TEST {
+ for workspace in common.config.workspace_folders {
+ uri, _ := common.parse_uri(workspace.uri, context.temp_allocator)
+ filepath.walk(uri.path, walk_directories, document)
+ }
+ }
- context.allocator = mem.arena_allocator(&resolve_arena)
+ reset_ast_context(ast_context)
- for fullpath in fullpaths {
- data, ok := os.read_entire_file(fullpath, context.allocator)
- if !ok {
- log.errorf("failed to read entire file for indexing %v", fullpath)
- continue
- }
+ arena: runtime.Arena
- p := parser.Parser {
- err = log_error_handler,
- warn = log_warning_handler,
- flags = {.Optional_Semicolons},
- }
+ _ = runtime.arena_init(
+ &arena,
+ mem.Megabyte * 40,
+ runtime.default_allocator(),
+ )
- dir := filepath.dir(fullpath)
- base := filepath.base(dir)
- forward_dir, _ := filepath.to_slash(dir)
+ defer runtime.arena_destroy(&arena)
- pkg := new(ast.Package)
- pkg.kind = .Normal
- pkg.fullpath = fullpath
- pkg.name = base
+ context.allocator = runtime.arena_allocator(&arena)
- if base == "runtime" {
- pkg.kind = .Runtime
- }
+ fullpaths := slice.unique(fullpaths[:])
- file := ast.File {
- fullpath = fullpath,
- src = string(data),
- pkg = pkg,
- }
+ if .Local not_in symbol.flags {
+ for fullpath in fullpaths {
+ dir := filepath.dir(fullpath)
+ base := filepath.base(dir)
+ forward_dir, _ := filepath.to_slash(dir)
- ok = parser.parse_file(&p, &file)
+ data, ok := os.read_entire_file(fullpath, context.allocator)
- if !ok {
- if !strings.contains(fullpath, "builtin.odin") &&
- !strings.contains(fullpath, "intrinsics.odin") {
- log.errorf("error in parse file for indexing %v", fullpath)
+ if !ok {
+ log.errorf(
+ "failed to read entire file for indexing %v",
+ fullpath,
+ )
+ continue
}
- continue
- }
- uri := common.create_uri(fullpath, context.allocator)
+ p := parser.Parser {
+ err = log_error_handler,
+ warn = log_warning_handler,
+ flags = {.Optional_Semicolons},
+ }
- document := Document {
- ast = file,
- }
- document.uri = uri
- document.text = transmute([]u8)file.src
- document.used_text = len(file.src)
+ pkg := new(ast.Package)
+ pkg.kind = .Normal
+ pkg.fullpath = fullpath
+ pkg.name = base
- document_setup(&document)
+ if base == "runtime" {
+ pkg.kind = .Runtime
+ }
- parse_imports(&document, &common.config)
+ file := ast.File {
+ fullpath = fullpath,
+ src = string(data),
+ pkg = pkg,
+ }
- in_pkg := false
+ ok = parser.parse_file(&p, &file)
- for pkg in document.imports {
- if pkg.name == symbol.pkg || forward_dir == symbol.pkg {
- in_pkg = true
+ if !ok {
+ if !strings.contains(fullpath, "builtin.odin") &&
+ !strings.contains(fullpath, "intrinsics.odin") {
+ log.errorf("error in parse file for indexing %v", fullpath)
+ }
continue
}
- }
- if in_pkg {
- symbols_and_nodes := resolve_entire_file(
- &document,
- reference,
- resolve_flag,
- false,
- context.allocator,
- )
+ uri := common.create_uri(fullpath, context.allocator)
- for k, v in symbols_and_nodes {
- if v.symbol.uri == symbol.uri &&
- v.symbol.range == symbol.range {
- node_uri := common.create_uri(
- v.node.pos.file,
- ast_context.allocator,
- )
+ document := Document {
+ ast = file,
+ }
- location := common.Location {
- range = common.get_token_range(
- v.node^,
- string(document.text),
- ),
- uri = strings.clone(
- node_uri.uri,
+ document.uri = uri
+ document.text = transmute([]u8)file.src
+ document.used_text = len(file.src)
+
+ document_setup(&document)
+
+ parse_imports(&document, &common.config)
+
+ in_pkg := false
+
+ for pkg in document.imports {
+ if pkg.name == symbol.pkg {
+ in_pkg = true
+ continue
+ }
+ }
+
+ if in_pkg || symbol.pkg == document.package_name {
+ symbols_and_nodes := resolve_entire_file(
+ &document,
+ resolve_flag,
+ context.allocator,
+ )
+
+ for k, v in symbols_and_nodes {
+ if v.symbol.uri == symbol.uri &&
+ v.symbol.range == symbol.range {
+ node_uri := common.create_uri(
+ v.node.pos.file,
ast_context.allocator,
- ),
+ )
+
+ location := common.Location {
+ range = common.get_token_range(
+ v.node^,
+ string(document.text),
+ ),
+ uri = strings.clone(
+ node_uri.uri,
+ ast_context.allocator,
+ ),
+ }
+ append(&locations, location)
}
- append(&locations, location)
}
}
+
+ free_all(context.allocator)
}
+ }
- free_all(context.allocator)
+ symbols_and_nodes := resolve_entire_file(
+ document,
+ resolve_flag,
+ context.allocator,
+ )
+
+ for k, v in symbols_and_nodes {
+ if v.symbol.uri == symbol.uri && v.symbol.range == symbol.range {
+ node_uri := common.create_uri(
+ v.node.pos.file,
+ ast_context.allocator,
+ )
+
+ range := common.get_token_range(v.node^, string(document.text))
+
+ //We don't have to have the `.` with, otherwise it renames the dot.
+ if _, ok := v.node.derived.(^ast.Implicit_Selector_Expr); ok {
+ range.start.character += 1
+ }
+
+ location := common.Location {
+ range = range,
+ uri = strings.clone(node_uri.uri, ast_context.allocator),
+ }
+
+ append(&locations, location)
+ }
}
return locations[:], true
@@ -269,21 +406,13 @@ get_references :: proc(
[]common.Location,
bool,
) {
- data := make([]byte, mem.Megabyte * 55, runtime.default_allocator())
- defer delete(data)
-
- arena: mem.Arena
- mem.arena_init(&arena, data)
-
- context.allocator = mem.arena_allocator(&arena)
-
ast_context := make_ast_context(
document.ast,
document.imports,
document.package_name,
document.uri.uri,
document.fullpath,
- context.allocator,
+ context.temp_allocator,
)
position_context, ok := get_document_position_context(
@@ -305,7 +434,11 @@ get_references :: proc(
)
}
- locations, ok2 := resolve_references(&ast_context, &position_context)
+ locations, ok2 := resolve_references(
+ document,
+ &ast_context,
+ &position_context,
+ )
temp_locations := make([dynamic]common.Location, 0, context.temp_allocator)
diff --git a/src/server/rename.odin b/src/server/rename.odin
index 252ebaa..6041641 100644
--- a/src/server/rename.odin
+++ b/src/server/rename.odin
@@ -17,21 +17,13 @@ get_rename :: proc(
WorkspaceEdit,
bool,
) {
- data := make([]byte, mem.Megabyte * 55, runtime.default_allocator())
- defer delete(data)
-
- arena: mem.Arena
- mem.arena_init(&arena, data)
-
- context.allocator = mem.arena_allocator(&arena)
-
ast_context := make_ast_context(
document.ast,
document.imports,
document.package_name,
document.uri.uri,
document.fullpath,
- context.allocator,
+ context.temp_allocator,
)
position_context, ok := get_document_position_context(
@@ -53,30 +45,21 @@ get_rename :: proc(
)
}
- locations, ok2 := resolve_references(&ast_context, &position_context)
-
- document_edits := make(
- map[string][dynamic]TextEdit,
- 0,
- context.temp_allocator,
+ locations, ok2 := resolve_references(
+ document,
+ &ast_context,
+ &position_context,
)
+ changes := make(map[string][dynamic]TextEdit, 0, context.temp_allocator)
+
for location in locations {
edits: ^[dynamic]TextEdit
- /*
- if location.range.start.line <= position.line &&
- position.line <= location.range.end.line &&
- location.range.start.character <= position.character &&
- position.character <= location.range.end.character {
- continue
- }
- */
-
- if edits = &document_edits[location.uri]; edits == nil {
- document_edits[strings.clone(location.uri, context.temp_allocator)] =
+ if edits = &changes[location.uri]; edits == nil {
+ changes[strings.clone(location.uri, context.temp_allocator)] =
make([dynamic]TextEdit, context.temp_allocator)
- edits = &document_edits[location.uri]
+ edits = &changes[location.uri]
}
append(edits, TextEdit{newText = new_text, range = location.range})
@@ -84,19 +67,61 @@ get_rename :: proc(
workspace: WorkspaceEdit
- document_changes := make([dynamic]TextDocumentEdit, context.temp_allocator)
+ workspace.changes = make(
+ map[string][]TextEdit,
+ len(changes),
+ context.temp_allocator,
+ )
+
+ for k, v in changes {
+ workspace.changes[k] = v[:]
+ }
+
+ return workspace, true
+}
+
+
+get_prepare_rename :: proc(
+ document: ^Document,
+ position: common.Position,
+) -> (
+ common.Range,
+ bool,
+) {
+ ast_context := make_ast_context(
+ document.ast,
+ document.imports,
+ document.package_name,
+ document.uri.uri,
+ document.fullpath,
+ context.temp_allocator,
+ )
+
+ position_context, ok := get_document_position_context(
+ document,
+ position,
+ .Hover,
+ )
+
+ get_globals(document.ast, &ast_context)
+
+ ast_context.current_package = ast_context.document_package
- for k, v in document_edits {
- append(
- &document_changes,
- TextDocumentEdit {
- edits = v[:],
- textDocument = {uri = k, version = document.version},
- },
+ if position_context.function != nil {
+ get_locals(
+ document.ast,
+ position_context.function,
+ &ast_context,
+ &position_context,
)
}
- workspace.documentChanges = document_changes[:]
+ symbol, _, ok2 := prepare_references(
+ document,
+ &ast_context,
+ &position_context,
+ )
+
- return workspace, true
+ return symbol.range, ok2
}
diff --git a/src/server/requests.odin b/src/server/requests.odin
index 778f1b2..dfab097 100644
--- a/src/server/requests.odin
+++ b/src/server/requests.odin
@@ -275,6 +275,7 @@ call_map: map[string]proc(
"textDocument/inlayHint" = request_inlay_hint,
"textDocument/documentLink" = request_document_links,
"textDocument/rename" = request_rename,
+ "textDocument/prepareRename" = request_prepare_rename,
"textDocument/references" = request_references,
"window/progress" = request_noop,
"workspace/symbol" = request_workspace_symbols,
@@ -705,10 +706,10 @@ request_initialize :: proc(
config.enable_semantic_tokens = false
config.enable_procedure_context = false
config.enable_snippets = false
- config.enable_references = false
+ config.enable_references = true
config.verbose = false
config.file_log = false
- config.enable_rename = false
+ config.enable_rename = true
config.odin_command = ""
config.checker_args = ""
config.enable_fake_method = false
@@ -809,7 +810,7 @@ request_initialize :: proc(
change = 2,
save = {includeText = true},
},
- renameProvider = config.enable_rename,
+ renameProvider = RenameOptions{prepareProvider = true},
workspaceSymbolProvider = true,
referencesProvider = config.enable_references,
definitionProvider = true,
@@ -1279,17 +1280,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)
@@ -1320,21 +1322,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)
@@ -1498,6 +1501,41 @@ request_document_links :: proc(
return .None
}
+request_prepare_rename :: proc(
+ params: json.Value,
+ id: RequestId,
+ config: ^common.Config,
+ writer: ^Writer,
+) -> common.Error {
+ params_object, ok := params.(json.Object)
+
+ if !ok {
+ return .ParseError
+ }
+
+ rename_param: PrepareRenameParams
+
+ if unmarshal(params, rename_param, context.temp_allocator) != nil {
+ return .ParseError
+ }
+
+ document := document_get(rename_param.textDocument.uri)
+
+ if document == nil {
+ return .InternalError
+ }
+
+ if range, ok := get_prepare_rename(document, rename_param.position); ok {
+ response := make_response_message(params = range, id = id)
+ send_response(response, writer)
+ } else {
+ response := make_response_message(params = nil, id = id)
+ send_response(response, writer)
+ }
+
+ return .None
+}
+
request_rename :: proc(
params: json.Value,
id: RequestId,
diff --git a/src/server/semantic_tokens.odin b/src/server/semantic_tokens.odin
index 35df4d0..4edb6ea 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,12 @@ get_semantic_tokens :: proc(
ast_context.current_package = ast_context.document_package
builder: SemanticTokenBuilder = {
- tokens = make([dynamic]u32, 0, 10000, context.temp_allocator),
+ tokens = make(
+ [dynamic]SemanticToken,
+ 0,
+ 2000,
+ context.temp_allocator,
+ ),
symbols = symbols,
src = ast_context.file.src,
}
@@ -139,7 +162,7 @@ get_semantic_tokens :: proc(
}
}
- return {data = builder.tokens[:]}
+ return builder.tokens[:]
}
write_semantic_at_pos :: proc(
@@ -156,11 +179,13 @@ write_semantic_at_pos :: proc(
)
append(
&builder.tokens,
- cast(u32)position.line,
- cast(u32)position.character,
- cast(u32)len,
- cast(u32)type,
- transmute(u32)modifiers,
+ 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
}
@@ -562,43 +587,45 @@ visit_ident :: proc(
modifiers += {.ReadOnly}
}
+ //log.errorf("%# \n", symbol)
+
/* variable idents */
#partial switch symbol.type {
case .Variable, .Constant, .Function:
#partial switch _ in symbol.value {
- case SymbolProcedureValue, SymbolProcedureGroupValue, SymbolAggregateValue:
+ case SymbolProcedureValue,
+ SymbolProcedureGroupValue,
+ SymbolAggregateValue:
write_semantic_node(builder, ident, .Function, modifiers)
case:
write_semantic_node(builder, ident, .Variable, modifiers)
}
case .EnumMember:
write_semantic_node(builder, ident, .EnumMember, modifiers)
- }
-
- /* type idents */
- switch v in symbol.value {
- case SymbolPackageValue:
- write_semantic_node(builder, ident, .Namespace, modifiers)
- case SymbolStructValue, SymbolBitFieldValue:
- write_semantic_node(builder, ident, .Struct, modifiers)
- case SymbolEnumValue, SymbolUnionValue:
- write_semantic_node(builder, ident, .Enum, modifiers)
- case SymbolProcedureValue,
- SymbolMatrixValue,
- SymbolBitSetValue,
- SymbolDynamicArrayValue,
- SymbolFixedArrayValue,
- SymbolSliceValue,
- SymbolMapValue,
- SymbolMultiPointer,
- SymbolBasicValue:
- write_semantic_node(builder, ident, .Type, modifiers)
- case SymbolUntypedValue:
- // handled by static syntax highlighting
- case SymbolGenericValue, SymbolProcedureGroupValue, SymbolAggregateValue:
- // unused
case:
- // log.errorf("Unexpected symbol value: %v", symbol.value);
- // panic(fmt.tprintf("Unexpected symbol value: %v", symbol.value));
+ /* type idents */
+ switch v in symbol.value {
+ case SymbolPackageValue:
+ write_semantic_node(builder, ident, .Namespace, modifiers)
+ case SymbolStructValue, SymbolBitFieldValue:
+ write_semantic_node(builder, ident, .Struct, modifiers)
+ case SymbolEnumValue, SymbolUnionValue:
+ write_semantic_node(builder, ident, .Enum, modifiers)
+ case SymbolProcedureValue,
+ SymbolMatrixValue,
+ SymbolBitSetValue,
+ SymbolDynamicArrayValue,
+ SymbolFixedArrayValue,
+ SymbolSliceValue,
+ SymbolMapValue,
+ SymbolMultiPointer,
+ SymbolBasicValue:
+ write_semantic_node(builder, ident, .Type, modifiers)
+ case SymbolUntypedValue:
+ // handled by static syntax highlighting
+ case SymbolGenericValue, SymbolProcedureGroupValue, SymbolAggregateValue:
+ // unused
+ case:
+ }
}
}
diff --git a/src/server/symbol.odin b/src/server/symbol.odin
index d46cf99..9e0f154 100644
--- a/src/server/symbol.odin
+++ b/src/server/symbol.odin
@@ -14,9 +14,8 @@ import "core:strings"
import "src:common"
SymbolAndNode :: struct {
- symbol: Symbol,
- node: ^ast.Node,
- is_resolved: bool,
+ symbol: Symbol,
+ node: ^ast.Node,
}
SymbolStructValue :: struct {
@@ -216,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)
@@ -249,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/server/types.odin b/src/server/types.odin
index 044a83c..7eb3a10 100644
--- a/src/server/types.odin
+++ b/src/server/types.odin
@@ -23,13 +23,14 @@ ResponseParams :: union {
CompletionList,
SignatureHelp,
[]DocumentSymbol,
- SemanticTokens,
+ SemanticTokensResponseParams,
Hover,
[]TextEdit,
[]InlayHint,
[]DocumentLink,
[]WorkspaceSymbol,
WorkspaceEdit,
+ common.Range,
}
ResponseMessage :: struct {
@@ -102,12 +103,17 @@ ServerCapabilities :: struct {
hoverProvider: bool,
documentFormattingProvider: bool,
inlayHintProvider: bool,
- renameProvider: bool,
+ renameProvider: RenameOptions,
referencesProvider: bool,
workspaceSymbolProvider: bool,
documentLinkProvider: DocumentLinkOptions,
}
+RenameOptions :: struct {
+ prepareProvider: bool,
+}
+
+
CompletionOptions :: struct {
resolveProvider: bool,
triggerCharacters: []string,
@@ -470,6 +476,11 @@ RenameParams :: struct {
position: common.Position,
}
+PrepareRenameParams :: struct {
+ textDocument: TextDocumentIdentifier,
+ position: common.Position,
+}
+
ReferenceParams :: struct {
textDocument: TextDocumentIdentifier,
position: common.Position,
@@ -486,7 +497,7 @@ TextDocumentEdit :: struct {
}
WorkspaceEdit :: struct {
- documentChanges: []TextDocumentEdit,
+ changes: map[string][]TextEdit,
}
WorkspaceSymbolParams :: struct {
diff --git a/src/testing/testing.odin b/src/testing/testing.odin
index fb8c034..0d9c028 100644
--- a/src/testing/testing.odin
+++ b/src/testing/testing.odin
@@ -62,7 +62,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)
@@ -382,3 +383,67 @@ expect_definition_locations :: proc(
}
}
}
+
+expect_reference_locations :: proc(
+ t: ^testing.T,
+ src: ^Source,
+ expect_locations: []common.Location,
+) {
+ setup(src)
+ defer teardown(src)
+
+ locations, ok := server.get_references(src.document, src.position)
+
+ for expect_location in expect_locations {
+ match := false
+ for location in locations {
+ if location.range == expect_location.range {
+ match = true
+ }
+ }
+ if !match {
+ ok = false
+ log.errorf("Failed to match with location: %v", expect_location)
+ }
+ }
+
+ if !ok {
+ log.error("Received:")
+ for location in locations {
+ log.errorf("%v \n", location)
+ }
+ }
+}
+
+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..<min(len(expected), len(tokens)) {
+ e, a := expected[i], tokens[i]
+ testing.expectf(t,
+ e == a,
+ "\n[%d]: Expected \n(%d, %d, %d, %v, %w)\nbut received\n(%d, %d, %d, %v, %w)",
+ i,
+ e.delta_line, e.delta_char, e.len, e.type, e.modifiers,
+ a.delta_line, a.delta_char, a.len, a.type, a.modifiers,
+ )
+ }
+}
diff --git a/tests/completions_test.odin b/tests/completions_test.odin
index d74de1b..04baade 100644
--- a/tests/completions_test.odin
+++ b/tests/completions_test.odin
@@ -266,6 +266,32 @@ ast_completion_identifier_proc_group :: proc(t: ^testing.T) {
}
@(test)
+ast_completion_identifier_proc_group_2 :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package test
+ raw_data_slice :: proc(v: $T/[]$E) -> [^]E {
+ }
+
+ zzcool :: proc {
+ raw_data_slice,
+ }
+
+ main :: proc() {
+ zzco{*}
+ }
+
+ `,
+ }
+
+ test.expect_completion_details(
+ t,
+ &source,
+ "",
+ {"test.zzcool: proc(v: $T/[]$E) -> [^]E"},
+ )
+}
+
+@(test)
ast_completion_in_comp_lit_type :: proc(t: ^testing.T) {
source := test.Source {
main = `package test
@@ -381,9 +407,6 @@ index_package_completion :: proc(t: ^testing.T) {
)
}
-import "core:odin/ast"
-import "core:odin/parser"
-
@(test)
ast_generic_make_slice :: proc(t: ^testing.T) {
@@ -687,11 +710,7 @@ ast_generic_make_completion :: proc(t: ^testing.T) {
main = `package test
make :: proc{
- make_dynamic_array,
make_dynamic_array_len,
- make_dynamic_array_len_cap,
- make_map,
- make_slice,
};
make_slice :: proc($T: typeid/[]$E, #any_int len: int, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
}
@@ -2560,7 +2579,7 @@ ast_poly_proc_matrix_whole :: proc(t: ^testing.T) {
matrix4_from_trs_f16 :: proc "contextless" () -> matrix[4, 4]f32 {
translation: matrix[4, 4]f32
rotation: matrix[4, 4]f32
- dsszz := matrix_mul(scale, translation)
+ dsszz := matrix_mul(rotation, translation)
dssz{*}
}
`,
@@ -2665,6 +2684,33 @@ ast_simple_bit_field_completion :: proc(t: ^testing.T) {
)
}
+@(test)
+ast_simple_union_of_enums_completion :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package test
+ Sub_Enum_1 :: enum {
+ ONE,
+ }
+ Sub_Enum_2 :: enum {
+ TWO,
+ }
+
+ Super_Enum :: union {
+ Sub_Enum_1,
+ Sub_Enum_2,
+ }
+
+ fn :: proc(mode: Super_Enum) {}
+
+ main :: proc() {
+ fn(.{*})
+ }
+ `,
+ }
+
+ test.expect_completion_labels(t, &source, ".", {"ONE", "TWO"})
+}
+
@(test)
ast_generics_function_with_struct_same_pkg :: proc(t: ^testing.T) {
@@ -2819,3 +2865,81 @@ ast_generics_function_with_comp_lit_struct :: proc(t: ^testing.T) {
},
)
}
+
+@(test)
+ast_enumerated_array_index_completion :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package main
+ Direction :: enum {
+ North,
+ East,
+ South,
+ West,
+ }
+
+ Direction_Vectors :: [Direction][2]int {
+ .North = {0, -1},
+ .East = {+1, 0},
+ .South = {0, +1},
+ .West = {-1, 0},
+ }
+
+ main :: proc() {
+ Direction_Vectors[.{*}]
+ }
+ `,
+ }
+
+ test.expect_completion_labels(
+ t,
+ &source,
+ ".",
+ {"North", "East", "South", "West"},
+ )
+}
+
+@(test)
+ast_raw_data_slice :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package main
+ _raw_data_slice :: proc(value: []$E) -> [^]E {}
+ _raw_data_dynamic :: proc(value: [dynamic]$E) -> [^]E {}
+ _raw_data_array :: proc(value: ^[$N]$E) -> [^]E {}
+ _raw_data_simd :: proc(value: ^#simd[$N]$E) -> [^]E {}
+ _raw_data_string :: proc(value: string) -> [^]byte {}
+
+ _raw_data :: proc{_raw_data_slice, _raw_data_dynamic, _raw_data_array, _raw_data_simd, _raw_data_string}
+
+ main :: proc() {
+ slice: []int
+ rezz := _raw_data(slice)
+ rez{*}
+ }
+ `,
+ }
+
+ test.expect_completion_details(t, &source, "", {"test.rezz: [^]int"})
+}
+
+@(test)
+ast_raw_data_slice_2 :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package main
+ raw_data_slice :: proc(v: $T/[]$E) -> [^]E {}
+
+
+ cool :: proc {
+ raw_data_slice,
+ }
+
+ main :: proc() {
+ my_slice: []int
+ rezz := cool(my_slice)
+ rez{*}
+ }
+
+ `,
+ }
+
+ test.expect_completion_details(t, &source, "", {"test.rezz: [^]int"})
+}
diff --git a/tests/definition_test.odin b/tests/definition_test.odin
index 98b055c..cd32628 100644
--- a/tests/definition_test.odin
+++ b/tests/definition_test.odin
@@ -230,3 +230,126 @@ ast_goto_shadowed_value_decls :: proc(t: ^testing.T) {
{{range = {{line = 2, character = 4}, {line = 2, character = 7}}}},
)
}
+
+@(test)
+ast_goto_implicit_super_enum_infer_from_assignment :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package test
+ Sub_Enum1 :: enum {
+ ONE,
+ }
+ Sub_Enum2 :: enum {
+ TWO,
+ }
+
+ Super_Enum :: union {
+ Sub_Enum1,
+ Sub_Enum2,
+ }
+
+ main :: proc() {
+ my_enum: Super_Enum
+ my_enum = .ON{*}E
+ }
+ `,
+ packages = {},
+ }
+
+ location := common.Location {
+ range = {
+ start = {line = 2, character = 3},
+ end = {line = 2, character = 6},
+ },
+ }
+
+ test.expect_definition_locations(t, &source, {location})
+}
+
+@(test)
+ast_goto_implicit_enum_infer_from_assignment :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package test
+ My_Enum :: enum {
+ One,
+ Two,
+ Three,
+ Four,
+ }
+
+ my_function :: proc() {
+ my_enum: My_Enum
+ my_enum = .Fo{*}ur
+ }
+ `,
+ packages = {},
+ }
+
+ location := common.Location {
+ range = {
+ start = {line = 5, character = 3},
+ end = {line = 5, character = 7},
+ },
+ }
+
+ test.expect_definition_locations(t, &source, {location})
+}
+
+@(test)
+ast_goto_implicit_enum_infer_from_return :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package test
+ My_Enum :: enum {
+ One,
+ Two,
+ Three,
+ Four,
+ }
+
+ my_function :: proc() -> My_Enum {
+ return .Fo{*}ur
+ }
+ `,
+ packages = {},
+ }
+
+ location := common.Location {
+ range = {
+ start = {line = 5, character = 3},
+ end = {line = 5, character = 7},
+ },
+ }
+
+ test.expect_definition_locations(t, &source, {location})
+}
+
+@(test)
+ast_goto_implicit_enum_infer_from_function :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package test
+ My_Enum :: enum {
+ One,
+ Two,
+ Three,
+ Four,
+ }
+
+ my_fn :: proc(my_enum: My_Enum) {
+
+ }
+
+ my_function :: proc() {
+ my_fn(.Fo{*}ur)
+ }
+ `,
+ packages = {},
+ }
+
+ location := common.Location {
+ range = {
+ start = {line = 5, character = 3},
+ end = {line = 5, character = 7},
+ },
+ }
+
+ test.expect_definition_locations(t, &source, {location})
+}
diff --git a/tests/hover_test.odin b/tests/hover_test.odin
index 22403a0..dcf3a57 100644
--- a/tests/hover_test.odin
+++ b/tests/hover_test.odin
@@ -185,3 +185,41 @@ ast_hover_on_bitset_variable :: proc(t: ^testing.T) {
test.expect_hover(t, &source, "test.derived_bit_set: bit_set[Foo]")
}
+
+@(test)
+ast_hover_struct_field_selector_completion :: proc(t: ^testing.T) {
+
+ packages := make([dynamic]test.Package, context.temp_allocator)
+
+ append(
+ &packages,
+ test.Package {
+ pkg = "my_package",
+ source = `package my_package
+ My_Struct :: struct {
+ one: int,
+ two: int,
+ three: int,
+ }
+ `,
+ },
+ )
+
+ source := test.Source {
+ main = `package test
+
+ import "my_package"
+ My_Foo :: struct {
+ bar: my_package.My_Stru{*}ct,
+ }
+
+ my_package :: proc() {
+
+ }
+
+ `,
+ packages = packages[:],
+ }
+
+ test.expect_hover(t, &source, "my_package.My_Struct: struct")
+}
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 25993ab..c07e8cf 100644
--- a/tests/references_test.odin
+++ b/tests/references_test.odin
@@ -2,3 +2,257 @@ package tests
import "core:fmt"
import "core:testing"
+
+import test "src:testing"
+
+@(test)
+reference_variables_in_function :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package test
+ my_function :: proc() {
+ a := 2
+ b := a
+ c := 2 + b{*}
+ }
+ `,
+ }
+
+ test.expect_reference_locations(
+ t,
+ &source,
+ {
+ {
+ range = {
+ start = {line = 3, character = 3},
+ end = {line = 3, character = 4},
+ },
+ },
+ {
+ range = {
+ 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{*}
+ }
+ `,
+ }
+
+ test.expect_reference_locations(
+ t,
+ &source,
+ {
+ {
+ range = {
+ start = {line = 1, character = 22},
+ 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
+ }
+ `,
+ }
+
+ test.expect_reference_locations(
+ t,
+ &source,
+ {
+ {
+ range = {
+ start = {line = 2, character = 3},
+ end = {line = 2, character = 4},
+ },
+ },
+ {
+ range = {
+ start = {line = 7, character = 6},
+ end = {line = 7, character = 7},
+ },
+ },
+ },
+ )
+}
+
+
+@(test)
+reference_field_comp_lit :: 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 := My_Struct {
+ foo = {soo_many_cases{*} = 2},
+ }
+ }
+ `,
+ }
+
+ test.expect_reference_locations(
+ t,
+ &source,
+ {
+ {
+ range = {
+ start = {line = 2, character = 3},
+ 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}})
+ }
+ `,
+ }
+
+ 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},
+ },
+ },
+ },
+ )
+}
+
+@(test)
+reference_field_comp_lit_infer_from_return :: 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 {
+ return {foo = {soo_many_cases{*} = 2}}
+ }
+ `,
+ }
+
+ test.expect_reference_locations(
+ t,
+ &source,
+ {
+ {
+ range = {
+ start = {line = 2, character = 3},
+ end = {line = 2, character = 17},
+ },
+ },
+ {
+ range = {
+ start = {line = 10, character = 18},
+ end = {line = 10, character = 32},
+ },
+ },
+ },
+ )
+}
+
+
+@(test)
+reference_enum_field_infer_from_assignment :: proc(t: ^testing.T) {
+ source := test.Source {
+ main = `package test
+ Sub_Enum1 :: enum {
+ ONE,
+ }
+ Sub_Enum2 :: enum {
+ TWO,
+ }
+
+ Super_Enum :: union {
+ Sub_Enum1,
+ Sub_Enum2,
+ }
+
+ main :: proc() {
+ my_enum: Super_Enum
+ my_enum = .ON{*}E
+ }
+ `,
+ }
+
+ test.expect_reference_locations(
+ t,
+ &source,
+ {
+ {
+ range = {
+ start = {line = 2, character = 3},
+ end = {line = 2, character = 6},
+ },
+ },
+ {
+ range = {
+ start = {line = 15, character = 14},
+ end = {line = 15, character = 17},
+ },
+ },
+ },
+ )
+}
diff --git a/tests/semantic_tokens_test.odin b/tests/semantic_tokens_test.odin
new file mode 100644
index 0000000..21115de
--- /dev/null
+++ b/tests/semantic_tokens_test.odin
@@ -0,0 +1,35 @@
+package tests
+
+import "core:fmt"
+import "core:testing"
+
+import "src:server"
+import test "src:testing"
+
+@(test)
+semantic_tokens :: proc(t: ^testing.T) {
+ src := test.Source {
+ main =
+`package test
+Proc_Type :: proc(a: string) -> 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
+ {1, 1, 1, .Variable, {}}, // [5] a
+ {1, 1, 1, .Variable, {}}, // [6] b
+ {0, 5, 1, .Variable, {}}, // [7] a
+ {1, 1, 1, .Variable, {}}, // [8] c
+ {0, 9, 1, .Variable, {}}, // [9] b
+ })
+}