diff options
Diffstat (limited to 'src/server/methods.odin')
| -rw-r--r-- | src/server/methods.odin | 296 |
1 files changed, 218 insertions, 78 deletions
diff --git a/src/server/methods.odin b/src/server/methods.odin index 757b37d..19d9ff4 100644 --- a/src/server/methods.odin +++ b/src/server/methods.odin @@ -73,7 +73,7 @@ append_method_completion :: proc( for c in cases { method := Method { name = c, - pkg = selector_symbol.pkg, + pkg = "$builtin", // Untyped values are always builtin types } collect_methods( ast_context, @@ -86,9 +86,14 @@ append_method_completion :: proc( ) } } else { + // For typed values, check if it's a builtin type + method_pkg := selector_symbol.pkg + if is_builtin_type_name(selector_symbol.name) { + method_pkg = "$builtin" + } method := Method { name = selector_symbol.name, - pkg = selector_symbol.pkg, + pkg = method_pkg, } collect_methods( ast_context, @@ -114,83 +119,218 @@ collect_methods :: proc( results: ^[dynamic]CompletionResult, ) { for k, v in indexer.index.collection.packages { - if symbols, ok := &v.methods[method]; ok { - for &symbol in symbols { - if should_skip_private_symbol(symbol, ast_context.current_package, ast_context.fullpath) { - continue - } - resolve_unresolved_symbol(ast_context, &symbol) - - range, ok := get_range_from_selection_start_to_dot(position_context) - - if !ok { - return - } - - value: SymbolProcedureValue - value, ok = symbol.value.(SymbolProcedureValue) - - if !ok { - continue - } - - if len(value.arg_types) == 0 || value.arg_types[0].type == nil { - continue - } - - first_arg: Symbol - first_arg, ok = resolve_type_expression(ast_context, value.arg_types[0].type) - - if !ok { - continue - } - - pointers_to_add := first_arg.pointers - pointers - - references := "" - dereferences := "" - - if pointers_to_add > 0 { - for i in 0 ..< pointers_to_add { - references = fmt.tprintf("%v&", references) - } - } else if pointers_to_add < 0 { - for i in pointers_to_add ..< 0 { - dereferences = fmt.tprintf("%v^", dereferences) - } - } - - new_text := "" - - if symbol.pkg != ast_context.document_package { - new_text = fmt.tprintf( - "%v.%v", - path.base(get_symbol_pkg_name(ast_context, &symbol), false, ast_context.allocator), - symbol.name, - ) - } else { - new_text = fmt.tprintf("%v", symbol.name) - } - - if len(symbol.value.(SymbolProcedureValue).arg_types) > 1 { - new_text = fmt.tprintf("%v(%v%v%v$0)", new_text, references, receiver, dereferences) - } else { - new_text = fmt.tprintf("%v(%v%v%v)$0", new_text, references, receiver, dereferences) - } - - item := CompletionItem { - label = symbol.name, - kind = symbol_type_to_completion_kind(symbol.type), - detail = get_short_signature(ast_context, symbol), - additionalTextEdits = remove_edit, - textEdit = TextEdit{newText = new_text, range = {start = range.end, end = range.end}}, - insertTextFormat = .Snippet, - InsertTextMode = .adjustIndentation, - documentation = construct_symbol_docs(symbol), - } - - append(results, CompletionResult{completion_item = item}) + symbols, ok := &v.methods[method] + if !ok { + continue + } + + for &symbol in symbols { + if should_skip_private_symbol(symbol, ast_context.current_package, ast_context.fullpath) { + continue + } + resolve_unresolved_symbol(ast_context, &symbol) + + #partial switch &sym_value in symbol.value { + case SymbolProcedureValue: + add_proc_method_completion( + ast_context, + position_context, + &symbol, + sym_value, + pointers, + receiver, + remove_edit, + results, + ) + case SymbolProcedureGroupValue: + add_proc_group_method_completion( + ast_context, + position_context, + &symbol, + sym_value, + pointers, + receiver, + remove_edit, + results, + ) + } + } + } +} + +@(private = "file") +add_proc_method_completion :: proc( + ast_context: ^AstContext, + position_context: ^DocumentPositionContext, + symbol: ^Symbol, + value: SymbolProcedureValue, + pointers: int, + receiver: string, + remove_edit: []TextEdit, + results: ^[dynamic]CompletionResult, +) { + if len(value.arg_types) == 0 || value.arg_types[0].type == nil { + return + } + + range, ok := get_range_from_selection_start_to_dot(position_context) + if !ok { + return + } + + first_arg: Symbol + first_arg, ok = resolve_type_expression(ast_context, value.arg_types[0].type) + if !ok { + return + } + + references, dereferences := compute_pointer_adjustments(first_arg.pointers, pointers) + + new_text := build_method_call_text( + ast_context, + symbol, + receiver, + references, + dereferences, + len(value.arg_types) > 1, + ) + + item := CompletionItem { + label = symbol.name, + kind = symbol_type_to_completion_kind(symbol.type), + detail = get_short_signature(ast_context, symbol^), + additionalTextEdits = remove_edit, + textEdit = TextEdit{newText = new_text, range = {start = range.end, end = range.end}}, + insertTextFormat = .Snippet, + InsertTextMode = .adjustIndentation, + documentation = construct_symbol_docs(symbol^), + } + + append(results, CompletionResult{completion_item = item}) +} + +@(private = "file") +add_proc_group_method_completion :: proc( + ast_context: ^AstContext, + position_context: ^DocumentPositionContext, + symbol: ^Symbol, + value: SymbolProcedureGroupValue, + pointers: int, + receiver: string, + remove_edit: []TextEdit, + results: ^[dynamic]CompletionResult, +) { + proc_group, is_group := value.group.derived.(^ast.Proc_Group) + if !is_group || len(proc_group.args) == 0 { + return + } + + range, ok := get_range_from_selection_start_to_dot(position_context) + if !ok { + return + } + + // Get first member to determine pointer adjustments + first_member: Symbol + first_member, ok = resolve_type_expression(ast_context, proc_group.args[0]) + if !ok { + return + } + + member_proc, is_proc := first_member.value.(SymbolProcedureValue) + if !is_proc || len(member_proc.arg_types) == 0 || member_proc.arg_types[0].type == nil { + return + } + + first_arg: Symbol + first_arg, ok = resolve_type_expression(ast_context, member_proc.arg_types[0].type) + if !ok { + return + } + + references, dereferences := compute_pointer_adjustments(first_arg.pointers, pointers) + + // Check if any member of the proc group has additional arguments beyond the receiver + has_additional_args := false + for member_expr in proc_group.args { + member: Symbol + member, ok = resolve_type_expression(ast_context, member_expr) + if !ok { + continue + } + if proc_val, is_proc_val := member.value.(SymbolProcedureValue); is_proc_val { + if len(proc_val.arg_types) > 1 { + has_additional_args = true + break } } } + + new_text := build_method_call_text(ast_context, symbol, receiver, references, dereferences, has_additional_args) + + item := CompletionItem { + label = symbol.name, + kind = symbol_type_to_completion_kind(symbol.type), + detail = get_short_signature(ast_context, symbol^), + additionalTextEdits = remove_edit, + textEdit = TextEdit{newText = new_text, range = {start = range.end, end = range.end}}, + insertTextFormat = .Snippet, + InsertTextMode = .adjustIndentation, + documentation = construct_symbol_docs(symbol^), + } + + append(results, CompletionResult{completion_item = item}) +} + +@(private = "file") +compute_pointer_adjustments :: proc( + first_arg_pointers: int, + current_pointers: int, +) -> ( + references: string, + dereferences: string, +) { + pointers_to_add := first_arg_pointers - current_pointers + + if pointers_to_add > 0 { + for _ in 0 ..< pointers_to_add { + references = fmt.tprintf("%v&", references) + } + } else if pointers_to_add < 0 { + for _ in pointers_to_add ..< 0 { + dereferences = fmt.tprintf("%v^", dereferences) + } + } + + return references, dereferences +} + +@(private = "file") +build_method_call_text :: proc( + ast_context: ^AstContext, + symbol: ^Symbol, + receiver: string, + references: string, + dereferences: string, + has_additional_args: bool, +) -> string { + new_text: string + + if symbol.pkg != ast_context.document_package { + new_text = fmt.tprintf( + "%v.%v", + path.base(get_symbol_pkg_name(ast_context, symbol), false, ast_context.allocator), + symbol.name, + ) + } else { + new_text = fmt.tprintf("%v", symbol.name) + } + + if has_additional_args { + new_text = fmt.tprintf("%v(%v%v%v$0)", new_text, references, receiver, dereferences) + } else { + new_text = fmt.tprintf("%v(%v%v%v)$0", new_text, references, receiver, dereferences) + } + + return new_text } |