aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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/util.odin13
-rw-r--r--src/server/analysis.odin539
-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.odin21
-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.odin55
-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.odin59
-rw-r--r--tests/definition_test.odin123
-rw-r--r--tests/objc_test.odin2
-rw-r--r--tests/references_test.odin254
-rw-r--r--tests/semantic_tokens_test.odin36
26 files changed, 1840 insertions, 641 deletions
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/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..d6592ae 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
@@ -133,14 +136,17 @@ make_ast_context :: proc(
}
set_ast_package_deferred :: proc(ast_context: ^AstContext, pkg: string) {
+ assert(ast_context.deferred_count > 0)
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 +154,17 @@ set_ast_package_set_scoped :: proc(ast_context: ^AstContext, pkg: string) {
}
set_ast_package_none_deferred :: proc(ast_context: ^AstContext) {
+ assert(ast_context.deferred_count > 0)
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 +174,10 @@ set_ast_package_from_symbol_deferred :: proc(
ast_context: ^AstContext,
symbol: Symbol,
) {
+ assert(ast_context.deferred_count > 0)
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 +185,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
@@ -822,11 +835,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 +850,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 +1330,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 +1346,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 {
@@ -1853,6 +1866,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 +2253,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 +2320,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 +2339,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 +2355,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 +2364,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
}
@@ -3837,296 +3895,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 +4014,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 +4077,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 +4841,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 +4890,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 +4915,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 +4947,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 +4959,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 +4970,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 +4994,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 +5014,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 +5041,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 +5110,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 +5140,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 +5148,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 65271b2..9e915c3 100644
--- a/src/server/generics.odin
+++ b/src/server/generics.odin
@@ -261,6 +261,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 {
@@ -284,7 +303,7 @@ resolve_poly :: proc(
return true
}
case:
- log.panicf("Unhandled specialization %v", specialization.derived)
+ log.error("Unhandled specialization %v", specialization.derived)
}
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 0fd73ba..b47c4a0 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,
@@ -696,10 +697,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_inlay_hints = false
@@ -801,7 +802,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,
@@ -1271,17 +1272,18 @@ request_semantic_token_full :: proc(
end = common.Position{line = 9000000}, //should be enough
}
- symbols: SemanticTokens
+ tokens_params: SemanticTokensResponseParams
if config.enable_semantic_tokens {
resolve_entire_file_cached(document)
if file, ok := file_resolve_cache.files[document.uri.uri]; ok {
- symbols = get_semantic_tokens(document, range, file.symbols)
+ tokens := get_semantic_tokens(document, range, file.symbols)
+ tokens_params = semantic_tokens_to_response_params(tokens)
}
}
- response := make_response_message(params = symbols, id = id)
+ response := make_response_message(params = tokens_params, id = id)
send_response(response, writer)
@@ -1312,21 +1314,22 @@ request_semantic_token_range :: proc(
return .InternalError
}
- symbols: SemanticTokens
+ tokens_params: SemanticTokensResponseParams
if config.enable_semantic_tokens {
resolve_entire_file_cached(document)
if file, ok := file_resolve_cache.files[document.uri.uri]; ok {
- symbols = get_semantic_tokens(
+ tokens := get_semantic_tokens(
document,
semantic_params.range,
file.symbols,
)
+ tokens_params = semantic_tokens_to_response_params(tokens)
}
}
- response := make_response_message(params = symbols, id = id)
+ response := make_response_message(params = tokens_params, id = id)
send_response(response, writer)
@@ -1490,6 +1493,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..8ace068 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,11 +587,15 @@ 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)
@@ -598,7 +627,5 @@ visit_ident :: proc(
case SymbolGenericValue, SymbolProcedureGroupValue, SymbolAggregateValue:
// unused
case:
- // log.errorf("Unexpected symbol value: %v", symbol.value);
- // panic(fmt.tprintf("Unexpected symbol value: %v", symbol.value));
}
}
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 9cc1e01..5079bf6 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,
@@ -468,6 +474,11 @@ RenameParams :: struct {
position: common.Position,
}
+PrepareRenameParams :: struct {
+ textDocument: TextDocumentIdentifier,
+ position: common.Position,
+}
+
ReferenceParams :: struct {
textDocument: TextDocumentIdentifier,
position: common.Position,
@@ -484,7 +495,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..60e7070 100644
--- a/tests/completions_test.odin
+++ b/tests/completions_test.odin
@@ -2665,6 +2665,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 +2846,35 @@ 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"},
+ )
+}
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/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..aff7788
--- /dev/null
+++ b/tests/semantic_tokens_test.odin
@@ -0,0 +1,36 @@
+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
+ {0, 0, 11, .Type, {.ReadOnly}}, // [5] !!! WRONG !!!
+ {1, 1, 1, .Variable, {}}, // [6] a
+ {1, 1, 1, .Variable, {}}, // [7] b
+ {0, 5, 1, .Variable, {}}, // [8] a
+ {1, 1, 1, .Variable, {}}, // [9] c
+ {0, 9, 1, .Variable, {}}, // [10] b
+ })
+}