aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2021-05-09 19:37:24 +0200
committerGitHub <noreply@github.com>2021-05-09 19:37:24 +0200
commit84d844bd4ad9320d00ea5f025a9ca44bf74c6ee7 (patch)
tree87c7e9d164e1da62c5cf4a3dff0cc9dea3f6e1d1 /src
parent87d4464e91f8b5784ba20e6ef78c818092afb9a9 (diff)
parent9c2090e0395d68bb9a89cfd3f69f863375a27f54 (diff)
Merge pull request #39 from DanielGavin/procedure-arguments
begun argument underlining
Diffstat (limited to 'src')
-rw-r--r--src/common/ast.odin200
-rw-r--r--src/index/clone.odin18
-rw-r--r--src/index/collector.odin21
-rw-r--r--src/index/indexer.odin1
-rw-r--r--src/index/util.odin169
-rw-r--r--src/server/analysis.odin158
-rw-r--r--src/server/completion.odin39
-rw-r--r--src/server/hover.odin5
-rw-r--r--src/server/signature.odin138
-rw-r--r--src/server/types.odin31
-rw-r--r--src/testing/testing.odin13
11 files changed, 504 insertions, 289 deletions
diff --git a/src/common/ast.odin b/src/common/ast.odin
index 0c9bdcf..839c7f5 100644
--- a/src/common/ast.odin
+++ b/src/common/ast.odin
@@ -5,6 +5,7 @@ import "core:log"
import "core:mem"
import "core:fmt"
import "core:strings"
+import "core:path"
keyword_map: map[string]bool = {
"int" = true,
@@ -491,12 +492,6 @@ node_equal_node :: proc(a, b: ^ast.Node) -> bool {
}
case Poly_Type:
return true;
- //return node_equal(n.sp)
- //if n, ok := a.derived.(Poly_Type); ok {
- // ret := node_equal(n.type, m.type);
- // ret &= node_equal(n.specialization, m.specialization);
- // return ret;
- //}
case Ellipsis:
if n, ok := a.derived.(Ellipsis); ok {
return node_equal(n.expr, m.expr);
@@ -548,8 +543,10 @@ node_equal_node :: proc(a, b: ^ast.Node) -> bool {
}
case Array_Type:
if n, ok := a.derived.(Array_Type); ok {
- ret := node_equal(n.len, m.len);
- ret &= node_equal(n.elem, m.elem);
+ ret := node_equal(n.elem, m.elem);
+ if n.len != nil && m.len != nil {
+ ret &= node_equal(n.len, m.len);
+ }
return ret;
}
case Dynamic_Array_Type:
@@ -613,12 +610,193 @@ node_equal_node :: proc(a, b: ^ast.Node) -> bool {
}
case Typeid_Type:
return true;
- //if n, ok := a.derived.(Typeid_Type); ok {
- // return node_equal(n.specialization, m.specialization);
- //}
case:
log.warn("Unhandled poly node kind: %T", m);
}
return false;
}
+
+/*
+ Returns the string representation of a type. This allows us to print the signature without storing it in the indexer as a string(saving memory).
+*/
+node_to_string :: proc(node: ^ast.Node) -> string {
+
+ builder := strings.make_builder(context.temp_allocator);
+
+ build_string(node, &builder);
+
+ return strings.to_string(builder);
+}
+
+build_string :: proc{
+ build_string_ast_array,
+ build_string_dynamic_array,
+ build_string_node,
+};
+
+build_string_dynamic_array :: proc(array: $A/[]^$T, builder: ^strings.Builder) {
+
+ for elem, i in array {
+ build_string(elem, builder);
+ }
+}
+
+build_string_ast_array :: proc(array: $A/[dynamic]^$T, builder: ^strings.Builder) {
+
+ for elem, i in array {
+ build_string(elem, builder);
+ }
+}
+
+build_string_node :: proc(node: ^ast.Node, builder: ^strings.Builder) {
+
+ using ast;
+
+ if node == nil {
+ return;
+ }
+
+ switch n in node.derived {
+ case Bad_Expr:
+ case Ident:
+ if strings.contains(n.name, "/") {
+ strings.write_string(builder, path.base(n.name, false, context.temp_allocator));
+ } else {
+ strings.write_string(builder, n.name);
+ }
+ case Implicit:
+ strings.write_string(builder, n.tok.text);
+ case Undef:
+ case Basic_Lit:
+ strings.write_string(builder, n.tok.text);
+ case Basic_Directive:
+ strings.write_string(builder, n.name);
+ case Ellipsis:
+ build_string(n.expr, builder);
+ case Proc_Lit:
+ build_string(n.type, builder);
+ build_string(n.body, builder);
+ case Comp_Lit:
+ build_string(n.type, builder);
+ build_string(n.elems, builder);
+ case Tag_Expr:
+ build_string(n.expr, builder);
+ case Unary_Expr:
+ build_string(n.expr, builder);
+ case Binary_Expr:
+ build_string(n.left, builder);
+ build_string(n.right, builder);
+ case Paren_Expr:
+ strings.write_string(builder, "(");
+ build_string(n.expr, builder);
+ strings.write_string(builder, ")");
+ case Call_Expr:
+ build_string(n.expr, builder);
+ strings.write_string(builder, "(");
+ build_string(n.args, builder);
+ strings.write_string(builder, ")");
+ case Selector_Expr:
+ build_string(n.expr, builder);
+ strings.write_string(builder, ".");
+ build_string(n.field, builder);
+ case Index_Expr:
+ build_string(n.expr, builder);
+ strings.write_string(builder, "[");
+ build_string(n.index, builder);
+ strings.write_string(builder, "]");
+ case Deref_Expr:
+ build_string(n.expr, builder);
+ case Slice_Expr:
+ build_string(n.expr, builder);
+ build_string(n.low, builder);
+ build_string(n.high, builder);
+ case Field_Value:
+ build_string(n.field, builder);
+ strings.write_string(builder, ": ");
+ build_string(n.value, builder);
+ case Type_Cast:
+ build_string(n.type, builder);
+ build_string(n.expr, builder);
+ case Bad_Stmt:
+ case Bad_Decl:
+ case Attribute:
+ build_string(n.elems, builder);
+ case Field:
+ build_string(n.names, builder);
+ if len(n.names) > 0 && n.type != nil {
+ strings.write_string(builder, ": ");
+ build_string(n.type, builder);
+
+ if n.default_value != nil && n.type != nil {
+ strings.write_string(builder, "=");
+ }
+
+ } else if len(n.names) > 0 && n.default_value != nil {
+ strings.write_string(builder, " := ");
+ } else {
+ build_string(n.type, builder);
+ }
+
+ build_string(n.default_value, builder);
+ case Field_List:
+ for field, i in n.list {
+ build_string(field, builder);
+ if len(n.list) - 1 != i {
+ strings.write_string(builder, ",");
+ }
+ }
+ case Typeid_Type:
+ strings.write_string(builder, "$");
+ build_string(n.specialization, builder);
+ case Helper_Type:
+ build_string(n.type, builder);
+ case Distinct_Type:
+ build_string(n.type, builder);
+ case Poly_Type:
+ strings.write_string(builder, "$");
+
+ build_string(n.type, builder);
+
+ if n.specialization != nil {
+ strings.write_string(builder, "/");
+ build_string(n.specialization, builder);
+ }
+ case Proc_Type:
+ strings.write_string(builder, "proc(");
+ build_string(n.params, builder);
+ strings.write_string(builder, ") -> ");
+ build_string(n.results, builder);
+ case Pointer_Type:
+ strings.write_string(builder, "^");
+ build_string(n.elem, builder);
+ case Array_Type:
+ strings.write_string(builder, "[");
+ build_string(n.len, builder);
+ strings.write_string(builder, "]");
+ build_string(n.elem, builder);
+ case Dynamic_Array_Type:
+ strings.write_string(builder, "[dynamic]");
+ build_string(n.elem, builder);
+ case Struct_Type:
+ build_string(n.poly_params, builder);
+ build_string(n.align, builder);
+ build_string(n.fields, builder);
+ case Union_Type:
+ build_string(n.poly_params, builder);
+ build_string(n.align, builder);
+ build_string(n.variants, builder);
+ case Enum_Type:
+ build_string(n.base_type, builder);
+ build_string(n.fields, builder);
+ case Bit_Set_Type:
+ build_string(n.elem, builder);
+ build_string(n.underlying, builder);
+ case Map_Type:
+ strings.write_string(builder, "map");
+ strings.write_string(builder, "[");
+ build_string(n.key, builder);
+ strings.write_string(builder, "]");
+ build_string(n.value, builder);
+ }
+}
diff --git a/src/index/clone.odin b/src/index/clone.odin
index fb2b687..17a1002 100644
--- a/src/index/clone.odin
+++ b/src/index/clone.odin
@@ -96,9 +96,27 @@ clone_node :: proc(node: ^ast.Node, allocator: mem.Allocator, unique_strings: ^m
r.name = get_index_unique_string(unique_strings, allocator, n.name);
}
case Implicit:
+ r := cast(^Implicit)res;
+ if unique_strings == nil {
+ r.tok.text = strings.clone(n.tok.text, allocator);
+ } else {
+ r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text);
+ }
case Undef:
case Basic_Lit:
+ r := cast(^Basic_Lit)res;
+ if unique_strings == nil {
+ r.tok.text = strings.clone(n.tok.text, allocator);
+ } else {
+ r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text);
+ }
case Basic_Directive:
+ r := cast(^Basic_Directive)res;
+ if unique_strings == nil {
+ r.name = strings.clone(n.name, allocator);
+ } else {
+ r.name = get_index_unique_string(unique_strings, allocator, n.name);
+ }
case Ellipsis:
r := cast(^Ellipsis)res;
r.expr = clone_type(r.expr, allocator, unique_strings);
diff --git a/src/index/collector.odin b/src/index/collector.odin
index 053c27e..f25add8 100644
--- a/src/index/collector.odin
+++ b/src/index/collector.odin
@@ -288,33 +288,12 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri
token = v;
token_type = .Function;
- if v.type.params != nil {
- symbol.signature = strings.concatenate({"(", string(file.src[v.type.params.pos.offset:v.type.params.end.offset]), ")"},
- collection.allocator);
- }
-
- if v.type.results != nil {
- symbol.returns = strings.concatenate({"(", string(file.src[v.type.results.pos.offset:v.type.results.end.offset]), ")"},
- collection.allocator);
- }
-
if v.type != nil {
symbol.value = collect_procedure_fields(collection, v.type, v.type.params, v.type.results, package_map);
}
case ast.Proc_Type:
token = v;
token_type = .Function;
-
- if v.params != nil {
- symbol.signature = strings.concatenate({"(", string(file.src[v.params.pos.offset:v.params.end.offset]), ")"},
- collection.allocator);
- }
-
- if v.results != nil {
- symbol.returns = strings.concatenate({"(", string(file.src[v.results.pos.offset:v.results.end.offset]), ")"},
- collection.allocator);
- }
-
symbol.value = collect_procedure_fields(collection, cast(^ast.Proc_Type)col_expr, v.params, v.results, package_map);
case ast.Proc_Group:
token = v;
diff --git a/src/index/indexer.odin b/src/index/indexer.odin
index 2020992..ce031f1 100644
--- a/src/index/indexer.odin
+++ b/src/index/indexer.odin
@@ -59,7 +59,6 @@ lookup :: proc(name: string, pkg: string, loc := #caller_location) -> (Symbol, b
}
for built in indexer.built_in_packages {
-
if symbol, ok := memory_index_lookup(&indexer.static_index, name, built); ok {
log.infof("lookup name: %v pkg: %v, symbol %v location %v", name, pkg, symbol, loc);
return symbol, true;
diff --git a/src/index/util.odin b/src/index/util.odin
deleted file mode 100644
index e87af2b..0000000
--- a/src/index/util.odin
+++ /dev/null
@@ -1,169 +0,0 @@
-package index
-
-import "core:odin/ast"
-import "core:strings"
-import "core:path"
-
-/*
- Returns the string representation of a type. This allows us to print the signature without storing it in the indexer as a string(saving memory).
-*/
-node_to_string :: proc(node: ^ast.Node) -> string {
-
- builder := strings.make_builder(context.temp_allocator);
-
- build_string(node, &builder);
-
- return strings.to_string(builder);
-}
-
-build_string :: proc{
- build_string_ast_array,
- build_string_dynamic_array,
- build_string_node,
-};
-
-build_string_dynamic_array :: proc(array: $A/[]^$T, builder: ^strings.Builder) {
-
- for elem, i in array {
- build_string(elem, builder);
- }
-}
-
-build_string_ast_array :: proc(array: $A/[dynamic]^$T, builder: ^strings.Builder) {
-
- for elem, i in array {
- build_string(elem, builder);
- }
-}
-
-build_string_node :: proc(node: ^ast.Node, builder: ^strings.Builder) {
-
- using ast;
-
- if node == nil {
- return;
- }
-
- switch n in node.derived {
- case Bad_Expr:
- case Ident:
- if strings.contains(n.name, "/") {
- strings.write_string(builder, path.base(n.name, false, context.temp_allocator));
- } else {
- strings.write_string(builder, n.name);
- }
- case Implicit:
- case Undef:
- case Basic_Lit:
- //strings.write_string(builder, n.tok.text);
- case Ellipsis:
- build_string(n.expr, builder);
- case Proc_Lit:
- build_string(n.type, builder);
- build_string(n.body, builder);
- case Comp_Lit:
- build_string(n.type, builder);
- build_string(n.elems, builder);
- case Tag_Expr:
- build_string(n.expr, builder);
- case Unary_Expr:
- build_string(n.expr, builder);
- case Binary_Expr:
- build_string(n.left, builder);
- build_string(n.right, builder);
- case Paren_Expr:
- strings.write_string(builder, "(");
- build_string(n.expr, builder);
- strings.write_string(builder, ")");
- case Call_Expr:
- build_string(n.expr, builder);
- strings.write_string(builder, "(");
- build_string(n.args, builder);
- strings.write_string(builder, ")");
- case Selector_Expr:
- build_string(n.expr, builder);
- strings.write_string(builder, ".");
- build_string(n.field, builder);
- case Index_Expr:
- build_string(n.expr, builder);
- strings.write_string(builder, "[");
- build_string(n.index, builder);
- strings.write_string(builder, "]");
- case Deref_Expr:
- build_string(n.expr, builder);
- case Slice_Expr:
- build_string(n.expr, builder);
- build_string(n.low, builder);
- build_string(n.high, builder);
- case Field_Value:
- build_string(n.field, builder);
- strings.write_string(builder, ": ");
- build_string(n.value, builder);
- case Type_Cast:
- build_string(n.type, builder);
- build_string(n.expr, builder);
- case Bad_Stmt:
- case Bad_Decl:
- case Attribute:
- build_string(n.elems, builder);
- case Field:
- build_string(n.names, builder);
- if len(n.names) > 0 {
- strings.write_string(builder, ": ");
- }
- build_string(n.type, builder);
- build_string(n.default_value, builder);
- case Field_List:
- for field, i in n.list {
- build_string(field, builder);
- if len(n.list) - 1 != i {
- strings.write_string(builder, ",");
- }
- }
- case Typeid_Type:
- build_string(n.specialization, builder);
- case Helper_Type:
- build_string(n.type, builder);
- case Distinct_Type:
- build_string(n.type, builder);
- case Poly_Type:
- build_string(n.type, builder);
- build_string(n.specialization, builder);
- case Proc_Type:
- strings.write_string(builder, "proc(");
- build_string(n.params, builder);
- strings.write_string(builder, ") -> ");
- build_string(n.results, builder);
- case Pointer_Type:
- strings.write_string(builder, "^");
- build_string(n.elem, builder);
- case Array_Type:
- strings.write_string(builder, "[");
- build_string(n.len, builder);
- strings.write_string(builder, "]");
- build_string(n.elem, builder);
- case Dynamic_Array_Type:
- strings.write_string(builder, "[dynamic]");
- build_string(n.elem, builder);
- case Struct_Type:
- build_string(n.poly_params, builder);
- build_string(n.align, builder);
- build_string(n.fields, builder);
- case Union_Type:
- build_string(n.poly_params, builder);
- build_string(n.align, builder);
- build_string(n.variants, builder);
- case Enum_Type:
- build_string(n.base_type, builder);
- build_string(n.fields, builder);
- case Bit_Set_Type:
- build_string(n.elem, builder);
- build_string(n.underlying, builder);
- case Map_Type:
- strings.write_string(builder, "map");
- strings.write_string(builder, "[");
- build_string(n.key, builder);
- strings.write_string(builder, "]");
- build_string(n.value, builder);
- }
-}
diff --git a/src/server/analysis.odin b/src/server/analysis.odin
index cd505ce..c90e2fe 100644
--- a/src/server/analysis.odin
+++ b/src/server/analysis.odin
@@ -57,6 +57,7 @@ DocumentPositionContext :: struct {
hint: DocumentPositionContextHint,
global_lhs_stmt: bool,
import_stmt: ^ast.Import_Decl,
+ call_commas: []int,
}
DocumentLocal :: struct {
@@ -177,7 +178,7 @@ resolve_poly_spec_node :: proc(ast_context: ^AstContext, call_node: ^ast.Node, s
case Implicit:
case Undef:
case Basic_Lit:
- case Poly_Type:
+ case Poly_Type:
if expr := get_poly_node_to_expr(call_node); expr != nil {
poly_map[m.type.name] = expr;
}
@@ -335,23 +336,28 @@ resolve_generic_function_symbol :: proc(ast_context: ^AstContext, params: []^ast
using ast;
if params == nil {
- return index.Symbol {}, false;
+ return {}, false;
}
if results == nil {
- return index.Symbol {}, false;
+ return {}, false;
}
if ast_context.call == nil {
- return index.Symbol {}, false;
+ return {}, false;
}
call_expr := ast_context.call;
- poly_map := make(map[string]^Expr, 0, context.temp_allocator);
- i := 0;
+ poly_map := make(map[string]^Expr, 0, context.temp_allocator);
+ i := 0;
+ count_required_params := 0;
for param in params {
+ if param.default_value == nil {
+ count_required_params += 1;
+ }
+
for name in param.names {
if len(call_expr.args) <= i {
@@ -366,12 +372,22 @@ resolve_generic_function_symbol :: proc(ast_context: ^AstContext, params: []^ast
continue;
}
+ if type_id, ok := param.type.derived.(Typeid_Type); ok {
+ if !common.node_equal(call_expr.args[i], type_id.specialization) {
+ return {}, false;
+ }
+ }
+
resolve_poly_spec_node(ast_context, call_expr.args[i], param.type, &poly_map);
i += 1;
}
}
+ if count_required_params > len(call_expr.args) || count_required_params == 0 || len(call_expr.args) == 0 {
+ return {}, false;
+ }
+
function_name := "";
function_range: common.Range;
@@ -382,7 +398,7 @@ resolve_generic_function_symbol :: proc(ast_context: ^AstContext, params: []^ast
function_name = selector.field.name;
function_range = common.get_token_range(selector, ast_context.file.src);
} else {
- return index.Symbol {}, false;
+ return {}, false;
}
symbol := index.Symbol {
@@ -392,6 +408,7 @@ resolve_generic_function_symbol :: proc(ast_context: ^AstContext, params: []^ast
};
return_types := make([dynamic]^ast.Field, context.temp_allocator);
+ argument_types := make([dynamic]^ast.Field, context.temp_allocator);
for result in results {
@@ -400,20 +417,40 @@ resolve_generic_function_symbol :: proc(ast_context: ^AstContext, params: []^ast
}
if ident, ok := result.type.derived.(Ident); ok {
- field := cast(^Field)index.clone_node(result, context.temp_allocator, nil);
-
- if m := &poly_map[ident.name]; m != nil {
- field.type = poly_map[ident.name];
+ if m, ok := poly_map[ident.name]; ok {
+ field := cast(^Field)index.clone_node(result, context.temp_allocator, nil);
+ field.type = m;
append(&return_types, field);
} else {
- return index.Symbol {}, false;
+ append(&return_types, result);
}
+ } else {
+ append(&return_types, result);
}
}
+ for param in params {
+
+ if len(param.names) == 0 {
+ continue;
+ }
+
+ //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(^Field)index.clone_node(param, context.temp_allocator, nil);
+ field.type = m;
+ append(&argument_types, field);
+ }
+ } else {
+ append(&argument_types, param);
+ }
+
+ }
+
symbol.value = index.SymbolProcedureValue {
return_types = return_types[:],
- arg_types = params,
+ arg_types = argument_types[:],
};
return symbol, true;
@@ -438,8 +475,7 @@ resolve_generic_function_ast :: proc(ast_context: ^AstContext, proc_lit: ast.Pro
return resolve_generic_function_symbol(ast_context, proc_lit.type.params.list, proc_lit.type.results.list);
}
-is_symbol_same_typed :: proc(ast_context: ^AstContext, a, b: index.Symbol) -> bool
-{
+is_symbol_same_typed :: proc(ast_context: ^AstContext, a, b: index.Symbol) -> bool {
//relying on the fact that a is the call argument to avoid checking both sides for untyped.
if untyped, ok := a.value.(index.SymbolUntypedValue); ok {
if basic, ok := b.value.(index.SymbolBasicValue); ok {
@@ -616,6 +652,18 @@ resolve_function_overload :: proc(ast_context: ^AstContext, group: ast.Proc_Grou
if procedure, ok := f.value.(index.SymbolProcedureValue); ok {
+ count_required_params := 0;
+
+ for arg in procedure.arg_types {
+ if arg.default_value == nil {
+ count_required_params += 1;
+ }
+ }
+
+ if count_required_params > len(call_expr.args) {
+ break next_fn;
+ }
+
if len(procedure.arg_types) < len(call_expr.args) {
continue;
}
@@ -632,17 +680,21 @@ resolve_function_overload :: proc(ast_context: ^AstContext, group: ast.Proc_Grou
call_symbol, ok = resolve_type_expression(ast_context, arg);
- if !ok {
+ if !ok {
break next_fn;
}
- arg_symbol, ok = resolve_type_expression(ast_context, procedure.arg_types[i].type);
+ if procedure.arg_types[i].type != nil {
+ arg_symbol, ok = resolve_type_expression(ast_context, procedure.arg_types[i].type);
+ } else {
+ arg_symbol, ok = resolve_type_expression(ast_context, procedure.arg_types[i].default_value);
+ }
- if !ok {
+ if !ok {
break next_fn;
}
-
- if !is_symbol_same_typed(ast_context, call_symbol, arg_symbol) {
+
+ if !is_symbol_same_typed(ast_context, call_symbol, arg_symbol) {
break next_fn;
}
@@ -943,8 +995,6 @@ resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (i
return_symbol, ok = make_symbol_dynamic_array_from_ast(ast_context, v), true;
case Map_Type:
return_symbol, ok = make_symbol_map_from_ast(ast_context, v), true;
- case Call_Expr:
- return_symbol, ok = resolve_type_expression(ast_context, local);
case:
return_symbol, ok = resolve_type_expression(ast_context, local);
}
@@ -999,8 +1049,6 @@ resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (i
return_symbol, ok = make_symbol_array_from_ast(ast_context, v), true;
case Dynamic_Array_Type:
return_symbol, ok = make_symbol_dynamic_array_from_ast(ast_context, v), true;
- case Call_Expr:
- return_symbol, ok = resolve_type_expression(ast_context, global.expr);
case:
return_symbol, ok = resolve_type_expression(ast_context, global.expr);
}
@@ -1012,7 +1060,6 @@ resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (i
return return_symbol, ok;
} else if node.name == "context" {
- //if there are more of these variables that hard builtin, move them to the indexer
return index.lookup("Context", ast_context.current_package);
} else if v, ok := common.keyword_map[node.name]; ok {
//keywords
@@ -1364,21 +1411,15 @@ make_symbol_procedure_from_ast :: proc(ast_context: ^AstContext, n: ^ast.Node, v
arg_types := make([dynamic]^ast.Field, context.temp_allocator);
if v.results != nil {
-
for ret in v.results.list {
append(&return_types, ret);
}
-
- symbol.returns = strings.concatenate({"(", string(ast_context.file.src[v.results.pos.offset:v.results.end.offset]), ")"}, context.temp_allocator);
}
if v.params != nil {
-
for param in v.params.list {
append(&arg_types, param);
}
-
- symbol.signature = strings.concatenate({"(", string(ast_context.file.src[v.params.pos.offset:v.params.end.offset]), ")"}, context.temp_allocator);
}
symbol.value = index.SymbolProcedureValue {
@@ -2048,8 +2089,6 @@ clear_locals :: proc(ast_context: ^AstContext) {
clear(&ast_context.usings);
}
-
-
concatenate_symbols_information :: proc(ast_context: ^AstContext, symbol: index.Symbol, is_completion: bool) -> string {
pkg := path.base(symbol.pkg, false, context.temp_allocator);
@@ -2057,7 +2096,7 @@ concatenate_symbols_information :: proc(ast_context: ^AstContext, symbol: index.
if symbol.type == .Function {
if symbol.returns != "" {
- return fmt.tprintf("%v.%v: proc %v -> %v", pkg, symbol.name, symbol.signature, symbol.returns);
+ return fmt.tprintf("%v.%v: proc%v -> %v", pkg, symbol.name, symbol.signature, symbol.returns);
} else {
return fmt.tprintf("%v.%v: proc%v", pkg, symbol.name, symbol.signature);
}
@@ -2201,7 +2240,7 @@ get_signature :: proc(ast_context: ^AstContext, ident: ast.Ident, symbol: index.
if i, ok := local.derived.(ast.Ident); ok {
return get_signature(ast_context, i, symbol, true);
} else {
- return index.node_to_string(local);
+ return common.node_to_string(local);
}
}
@@ -2209,7 +2248,7 @@ get_signature :: proc(ast_context: ^AstContext, ident: ast.Ident, symbol: index.
if i, ok := global.expr.derived.(ast.Ident); ok {
return get_signature(ast_context, i, symbol, true);
} else {
- return index.node_to_string(global.expr);
+ return common.node_to_string(global.expr);
}
}
}
@@ -2285,6 +2324,45 @@ get_document_symbols :: proc(document: ^Document) -> []DocumentSymbol {
}
/*
+ Parser gives ranges of expression, but not actually where the commas are placed.
+*/
+get_call_commas :: proc(position_context: ^DocumentPositionContext, document: ^Document) {
+
+ if position_context.call == nil {
+ return;
+ }
+
+ commas := make([dynamic]int, 0, 10, context.temp_allocator);
+
+ paren_count := 0;
+ bracket_count := 0;
+ brace_count := 0;
+
+ if call, ok := position_context.call.derived.(ast.Call_Expr); ok {
+ if document.text[call.open.offset] == '(' {
+ paren_count -= 1;
+ }
+ for i := call.open.offset; i < call.close.offset; i += 1 {
+
+ switch document.text[i] {
+ case '[': paren_count += 1;
+ case ']': paren_count -= 1;
+ case '{': brace_count += 1;
+ case '}': brace_count -= 1;
+ case '(': paren_count += 1;
+ case ')': paren_count -= 1;
+ case ',':
+ if paren_count == 0 && brace_count == 0 && bracket_count == 0 {
+ append(&commas, i);
+ }
+ }
+ }
+ }
+
+ position_context.call_commas = commas[:];
+}
+
+/*
Figure out what exactly is at the given position and whether it is in a function, struct, etc.
*/
get_document_position_context :: proc(document: ^Document, position: common.Position, hint: DocumentPositionContextHint) -> (DocumentPositionContext, bool) {
@@ -2357,6 +2435,10 @@ get_document_position_context :: proc(document: ^Document, position: common.Posi
fallback_position_context_signature(document, position, &position_context);
}
+ if hint == .SignatureHelp {
+ get_call_commas(&position_context, document);
+ }
+
return position_context, true;
}
@@ -2584,6 +2666,10 @@ fallback_position_context_signature :: proc(document: ^Document, position: commo
begin_offset := max(0, start);
end_offset := max(start, end + 1);
+ if end_offset - begin_offset <= 1 {
+ return;
+ }
+
str := position_context.file.src[0:end_offset];
p := parser.Parser {
diff --git a/src/server/completion.odin b/src/server/completion.odin
index 66893e0..3fc4917 100644
--- a/src/server/completion.odin
+++ b/src/server/completion.odin
@@ -18,6 +18,11 @@ import "core:os"
import "shared:common"
import "shared:index"
+/*
+ TODOS: Making the signature details is really annoying and not that nice - try to see if this can be refractored.
+
+*/
+
Completion_Type :: enum {
Implicit,
Selector,
@@ -156,10 +161,10 @@ field_exists_in_comp_lit :: proc(comp_lit: ^ast.Comp_Lit, name: string) -> bool
return false;
}
-get_attribute_completion :: proc(ast_context: ^AstContext, postition_context: ^DocumentPositionContext, list: ^CompletionList) {
+get_attribute_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) {
}
-get_directive_completion :: proc(ast_context: ^AstContext, postition_context: ^DocumentPositionContext, list: ^CompletionList) {
+get_directive_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) {
list.isIncomplete = false;
@@ -229,9 +234,9 @@ get_comp_lit_completion :: proc(ast_context: ^AstContext, position_context: ^Doc
}
item := CompletionItem {
- label = resolved.name,
+ label = name,
kind = .Field,
- detail = fmt.tprintf("%v.%v: %v", comp_symbol.name, resolved.name, index.node_to_string(v.types[i])),
+ detail = fmt.tprintf("%v.%v: %v", comp_symbol.name, name, common.node_to_string(v.types[i])),
documentation = resolved.doc,
};
@@ -364,7 +369,7 @@ get_selector_completion :: proc(ast_context: ^AstContext, position_context: ^Doc
item := CompletionItem {
label = name,
kind = .Field,
- detail = fmt.tprintf("%v.%v: %v", selector.name, name, index.node_to_string(v.types[i])),
+ detail = fmt.tprintf("%v.%v: %v", selector.name, name, common.node_to_string(v.types[i])),
documentation = symbol.doc,
};
@@ -374,7 +379,7 @@ get_selector_completion :: proc(ast_context: ^AstContext, position_context: ^Doc
item := CompletionItem {
label = symbol.name,
kind = .Field,
- detail = fmt.tprintf("%v: %v", name, index.node_to_string(v.types[i])),
+ detail = fmt.tprintf("%v: %v", name, common.node_to_string(v.types[i])),
documentation = symbol.doc,
};
@@ -390,11 +395,16 @@ get_selector_completion :: proc(ast_context: ^AstContext, position_context: ^Doc
for search in searched {
+ symbol := search.symbol;
+
+ build_symbol_signature(&symbol);
+ build_symbol_return(&symbol);
+
item := CompletionItem {
- label = search.symbol.name,
- kind = cast(CompletionItemKind)search.symbol.type,
- detail = fmt.tprintf("%v.%v: %v", path.base(search.symbol.pkg, false, context.temp_allocator), search.symbol.name, search.symbol.signature),
- documentation = search.symbol.doc,
+ label = symbol.name,
+ kind = cast(CompletionItemKind)symbol.type,
+ detail = fmt.tprintf("%v.%v: %v", path.base(symbol.pkg, false, context.temp_allocator), symbol.name, symbol.signature),
+ documentation = symbol.doc,
};
append(&items, item);
@@ -819,6 +829,9 @@ get_identifier_completion :: proc(ast_context: ^AstContext, position_context: ^D
if results, ok := index.fuzzy_search(lookup, pkgs[:]); ok {
for r in results {
+ r := r;
+ build_symbol_return(&r.symbol);
+ build_symbol_signature(&r.symbol);
if r.symbol.uri != ast_context.uri {
append(&combined, CombinedResult {score = r.score, symbol = r.symbol});
}
@@ -851,6 +864,9 @@ get_identifier_completion :: proc(ast_context: ^AstContext, position_context: ^D
symbol.name = ident.name;
symbol.signature = get_signature(ast_context, ident^, symbol);
+ build_symbol_return(&symbol);
+ build_symbol_signature(&symbol);
+
if score, ok := common.fuzzy_match(matcher, symbol.name); ok {
append(&combined, CombinedResult {score = score * 1.1, symbol = symbol, variable = ident});
}
@@ -874,6 +890,9 @@ get_identifier_completion :: proc(ast_context: ^AstContext, position_context: ^D
symbol.name = ident.name;
symbol.signature = get_signature(ast_context, ident^, symbol);
+ build_symbol_return(&symbol);
+ build_symbol_signature(&symbol);
+
if score, ok := common.fuzzy_match(matcher, symbol.name); ok {
append(&combined, CombinedResult {score = score * 1.1, symbol = symbol, variable = ident});
}
diff --git a/src/server/hover.odin b/src/server/hover.odin
index d14cd34..adb141d 100644
--- a/src/server/hover.odin
+++ b/src/server/hover.odin
@@ -30,6 +30,9 @@ write_hover_content :: proc(ast_context: ^AstContext, symbol: index.Symbol) -> M
}
}
+ build_symbol_return(&symbol);
+ build_symbol_signature(&symbol);
+
cat := concatenate_symbols_information(ast_context, symbol, false);
if cat != "" {
@@ -126,7 +129,7 @@ get_hover_information :: proc(document: ^Document, position: common.Position) ->
if symbol, ok := resolve_type_expression(&ast_context, v.types[i]); ok {
symbol.name = name;
symbol.pkg = selector.name;
- symbol.signature = index.node_to_string(v.types[i]);
+ symbol.signature = common.node_to_string(v.types[i]);
hover.contents = write_hover_content(&ast_context, symbol);
return hover, true;
}
diff --git a/src/server/signature.odin b/src/server/signature.odin
index 883bd2e..c1cdbe3 100644
--- a/src/server/signature.odin
+++ b/src/server/signature.odin
@@ -16,6 +16,77 @@ import "core:slice"
import "shared:common"
import "shared:index"
+SignatureInformationCapabilities :: struct {
+ parameterInformation: ParameterInformationCapabilities,
+}
+
+SignatureHelpClientCapabilities :: struct {
+ dynamicRegistration: bool,
+ signatureInformation: SignatureInformationCapabilities,
+ contextSupport: bool,
+}
+
+SignatureHelpOptions :: struct {
+ triggerCharacters: []string,
+ retriggerCharacters: []string,
+}
+
+SignatureHelp :: struct {
+ signatures: []SignatureInformation,
+ activeSignature: int,
+ activeParameter: int,
+}
+
+SignatureInformation :: struct {
+ label: string,
+ documentation: string,
+ parameters: []ParameterInformation,
+}
+
+ParameterInformation :: struct {
+ label: string,
+ activeParameter: int,
+}
+
+/*
+ Lazily build the signature and returns from ast.Nodes
+*/
+build_symbol_signature :: proc(symbol: ^index.Symbol) {
+ if value, ok := symbol.value.(index.SymbolProcedureValue); ok {
+ builder := strings.make_builder(context.temp_allocator);
+
+ strings.write_string(&builder, "(");
+ for arg, i in value.arg_types {
+ strings.write_string(&builder, common.node_to_string(arg));
+ if i != len(value.arg_types) - 1 {
+ strings.write_string(&builder, ", ");
+ }
+ }
+ strings.write_string(&builder, ")");
+
+ symbol.signature = strings.to_string(builder);
+ }
+}
+
+build_symbol_return :: proc(symbol: ^index.Symbol) {
+ if value, ok := symbol.value.(index.SymbolProcedureValue); ok {
+ builder := strings.make_builder(context.temp_allocator);
+
+ if len(value.return_types) == 0 {
+ return;
+ }
+
+ strings.write_string(&builder, "(");
+ for arg, i in value.return_types {
+ strings.write_string(&builder, common.node_to_string(arg));
+ if i != len(value.return_types) - 1 {
+ strings.write_string(&builder, ", ");
+ }
+ }
+ strings.write_string(&builder, ")");
+ symbol.returns = strings.to_string(builder);
+ }
+}
get_signature_information :: proc(document: ^Document, position: common.Position) -> (SignatureHelp, bool) {
@@ -30,6 +101,7 @@ get_signature_information :: proc(document: ^Document, position: common.Position
return signature_help, true;
}
+ //TODO(should probably not be an ast.Expr, but ast.Call_Expr)
if position_context.call == nil {
return signature_help, true;
}
@@ -40,30 +112,80 @@ get_signature_information :: proc(document: ^Document, position: common.Position
get_locals(document.ast, position_context.function, &ast_context, &position_context);
}
+ for comma, i in position_context.call_commas {
+ if position_context.position > comma {
+ signature_help.activeParameter = i+1;
+ } else if position_context.position == comma {
+ signature_help.activeParameter = i;
+ }
+ }
+
call: index.Symbol;
call, ok = resolve_type_expression(&ast_context, position_context.call);
signature_information := make([dynamic]SignatureInformation, context.temp_allocator);
- if _, ok := call.value.(index.SymbolProcedureValue); ok {
+ if value, ok := call.value.(index.SymbolProcedureValue); ok {
+
+ parameters := make([]ParameterInformation, len(value.arg_types), context.temp_allocator);
+
+ for arg, i in value.arg_types {
+
+ if arg.type != nil {
+ if _, is_ellipsis := arg.type.derived.(ast.Ellipsis); is_ellipsis {
+ signature_help.activeParameter = min(i, signature_help.activeParameter);
+ }
+ }
+
+ parameters[i].label = common.node_to_string(arg);
+ }
+
+ build_symbol_signature(&call);
+ build_symbol_return(&call);
+
info := SignatureInformation {
label = concatenate_symbols_information(&ast_context, call, false),
documentation = call.doc,
+ parameters = parameters,
};
append(&signature_information, info);
} else if value, ok := call.value.(index.SymbolAggregateValue); ok {
+ //function overloaded procedures
for symbol in value.symbols {
- info := SignatureInformation {
- label = concatenate_symbols_information(&ast_context, symbol, false),
- documentation = symbol.doc,
- };
- append(&signature_information, info);
+
+ symbol := symbol;
+
+ if value, ok := symbol.value.(index.SymbolProcedureValue); ok {
+
+ parameters := make([]ParameterInformation, len(value.arg_types), context.temp_allocator);
+
+ for arg, i in value.arg_types {
+
+ if arg.type != nil {
+ if _, is_ellipsis := arg.type.derived.(ast.Ellipsis); is_ellipsis {
+ signature_help.activeParameter = min(i, signature_help.activeParameter);
+ }
+ }
+
+ parameters[i].label = common.node_to_string(arg);
+ parameters[i].activeParameter = i;
+ }
+
+ build_symbol_signature(&symbol);
+ build_symbol_return(&symbol);
+
+ info := SignatureInformation {
+ label = concatenate_symbols_information(&ast_context, symbol, false),
+ documentation = symbol.doc,
+ parameters = parameters,
+ };
+
+ append(&signature_information, info);
+ }
}
}
signature_help.signatures = signature_information[:];
- signature_help.activeSignature = 0;
- signature_help.activeParameter = 0;
return signature_help, true;
} \ No newline at end of file
diff --git a/src/server/types.odin b/src/server/types.odin
index e6ee85d..df78906 100644
--- a/src/server/types.odin
+++ b/src/server/types.odin
@@ -131,21 +131,6 @@ ParameterInformationCapabilities :: struct {
labelOffsetSupport: bool,
}
-SignatureInformationCapabilities :: struct {
- parameterInformation: ParameterInformationCapabilities,
-}
-
-SignatureHelpClientCapabilities :: struct {
- dynamicRegistration: bool,
- signatureInformation: SignatureInformationCapabilities,
- contextSupport: bool,
-}
-
-SignatureHelpOptions :: struct {
- triggerCharacters: []string,
- retriggerCharacters: []string,
-}
-
ClientCapabilities :: struct {
textDocument: TextDocumentClientCapabilities,
}
@@ -275,22 +260,6 @@ TextDocumentSyncOptions :: struct {
save: SaveOptions,
}
-SignatureHelp :: struct {
- signatures: []SignatureInformation,
- activeSignature: int,
- activeParameter: int,
-}
-
-SignatureInformation :: struct {
- label: string,
- documentation: string,
- parameters: []ParameterInformation,
-}
-
-ParameterInformation :: struct {
- label: [2]int,
-}
-
OlsConfig :: struct {
collections: [dynamic]OlsConfigCollection,
thread_pool_count: int,
diff --git a/src/testing/testing.odin b/src/testing/testing.odin
index e74fb0e..ebaf4fc 100644
--- a/src/testing/testing.odin
+++ b/src/testing/testing.odin
@@ -73,6 +73,7 @@ setup :: proc(src: ^Source) {
There is a lot code here that is used in the real code, then i'd like to see.
*/
+ index.indexer.static_index = index.make_memory_index(index.make_symbol_collection(context.allocator, &common.config));
index.indexer.dynamic_index = index.make_memory_index(index.make_symbol_collection(context.allocator, &common.config));
for src_pkg in src.packages {
@@ -108,7 +109,7 @@ setup :: proc(src: ^Source) {
return;
}
- if ret := index.collect_symbols(&index.indexer.dynamic_index.collection, file, uri.uri); ret != .None {
+ if ret := index.collect_symbols(&index.indexer.static_index.collection, file, uri.uri); ret != .None {
return;
}
}
@@ -145,6 +146,16 @@ expect_signature_labels :: proc(t: ^testing.T, src: ^Source, expect_labels: []st
}
+expect_signature_parameter_position :: proc(t: ^testing.T, src: ^Source, position: int) {
+ setup(src);
+
+ help, ok := server.get_signature_information(src.document, src.position);
+
+ if help.activeParameter != position {
+ testing.errorf(t, "expected parameter position %v, but received %v", position, help.activeParameter);
+ }
+}
+
expect_completion_details :: proc(t: ^testing.T, src: ^Source, trigger_character: string, expect_details: []string) {
setup(src);