diff options
| author | DanielGavin <danielgavin5@hotmail.com> | 2021-05-09 19:37:24 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-05-09 19:37:24 +0200 |
| commit | 84d844bd4ad9320d00ea5f025a9ca44bf74c6ee7 (patch) | |
| tree | 87c7e9d164e1da62c5cf4a3dff0cc9dea3f6e1d1 /src | |
| parent | 87d4464e91f8b5784ba20e6ef78c818092afb9a9 (diff) | |
| parent | 9c2090e0395d68bb9a89cfd3f69f863375a27f54 (diff) | |
Merge pull request #39 from DanielGavin/procedure-arguments
begun argument underlining
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/ast.odin | 200 | ||||
| -rw-r--r-- | src/index/clone.odin | 18 | ||||
| -rw-r--r-- | src/index/collector.odin | 21 | ||||
| -rw-r--r-- | src/index/indexer.odin | 1 | ||||
| -rw-r--r-- | src/index/util.odin | 169 | ||||
| -rw-r--r-- | src/server/analysis.odin | 158 | ||||
| -rw-r--r-- | src/server/completion.odin | 39 | ||||
| -rw-r--r-- | src/server/hover.odin | 5 | ||||
| -rw-r--r-- | src/server/signature.odin | 138 | ||||
| -rw-r--r-- | src/server/types.odin | 31 | ||||
| -rw-r--r-- | src/testing/testing.odin | 13 |
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); |