diff options
| author | Damian Tarnawski <gthetarnav@gmail.com> | 2025-07-05 00:09:54 +0200 |
|---|---|---|
| committer | Damian Tarnawski <gthetarnav@gmail.com> | 2025-07-05 00:09:54 +0200 |
| commit | 2a37eeda1f681c7b0d0eb29b32e43cf3d7f5d374 (patch) | |
| tree | 7b706b4526a3dae206f87a66a9b2f671cf2f588b /src | |
| parent | 384aaa75affae6fdceca2811ddd9a0b57d842e2d (diff) | |
Better handle inlay hints with named params passed not in order
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/position.odin | 39 | ||||
| -rw-r--r-- | src/server/inlay_hints.odin | 189 |
2 files changed, 114 insertions, 114 deletions
diff --git a/src/common/position.odin b/src/common/position.odin index 1eecfdc..ca3976a 100644 --- a/src/common/position.odin +++ b/src/common/position.odin @@ -3,6 +3,7 @@ package common import "core:fmt" import "core:log" import "core:odin/ast" +import "core:odin/tokenizer" import "core:strings" import "core:unicode/utf8" @@ -101,31 +102,31 @@ go_backwards_to_endline :: proc(offset: int, document_text: []u8) -> int { return index + 1 } -/* - Get the range of a token in utf16 space -*/ -get_token_range :: proc(node: ast.Node, document_text: string) -> Range { - range: Range - - pos_offset := min(len(document_text) - 1, node.pos.offset) - end_offset := min(len(document_text) - 1, node.end.offset) +token_pos_to_position :: proc(pos: tokenizer.Pos, document_text: string) -> (position: Position) { + pos_offset := min(len(document_text) - 1, pos.offset) offset := go_backwards_to_endline(pos_offset, transmute([]u8)document_text) - + if offset < 0 { - offset := 0 - log.errorf("Failed to find offset in get_token_range: %v", node) + offset = 0 + log.errorf("Failed to find offset in token_pos_to_position: %v", pos) } - range.start.line = node.pos.line - 1 - range.start.character = get_character_offset_u8_to_u16(node.pos.column - 1, transmute([]u8)document_text[offset:]) - - offset = go_backwards_to_endline(end_offset - 1, transmute([]u8)document_text) - - range.end.line = node.end.line - 1 - range.end.character = get_character_offset_u8_to_u16(node.end.column - 1, transmute([]u8)document_text[offset:]) + return { + line = pos.line-1, + character = get_character_offset_u8_to_u16(pos.column-1, transmute([]u8)document_text[offset:]), + } +} - return range +/* + Get the range of a token in utf16 space +*/ +get_token_range :: proc(node: ast.Node, document_text: string) -> (range: Range) { + range.start = token_pos_to_position(node.pos, document_text) + end_pos := node.end + end_pos.offset -= 1 + range.end = token_pos_to_position(end_pos, document_text) + return } get_absolute_range :: proc(range: Range, document_text: []u8) -> (AbsoluteRange, bool) { diff --git a/src/server/inlay_hints.odin b/src/server/inlay_hints.odin index e4cea54..6dc155d 100644 --- a/src/server/inlay_hints.odin +++ b/src/server/inlay_hints.odin @@ -57,124 +57,123 @@ get_inlay_hints :: proc( loop: for node_call in &data.calls { - symbol_arg_count := 0 - is_selector_call := false + is_ellipsis := false has_added_default := false call := node_call.derived.(^ast.Call_Expr) - if selector, ok := call.expr.derived.(^ast.Selector_Expr); ok && selector.op.kind == .Arrow_Right { - is_selector_call = true + selector, is_selector_call := call.expr.derived.(^ast.Selector_Expr) + is_selector_call &&= selector.op.kind == .Arrow_Right + + end_pos := common.token_pos_to_position(call.close, string(document.text)) + + symbol_and_node := symbols[cast(uintptr)call.expr] or_continue + symbol_call := symbol_and_node.symbol.value.(SymbolProcedureValue) or_continue + + positional_arg_idx := 0 + + expr_name :: proc (node: ^ast.Node) -> (name: string, ok: bool) { + #partial switch v in node.derived { + case ^ast.Ident: + return v.name, true + case ^ast.Poly_Type: + ident := v.type.derived.(^ast.Ident) or_return + return ident.name, true + case: + return + } } + + for arg, arg_type_idx in symbol_call.arg_types { + if arg_type_idx == 0 && is_selector_call { + continue + } - if symbol_and_node, ok := symbols[cast(uintptr)call.expr]; ok { - if symbol_call, ok := symbol_and_node.symbol.value.(SymbolProcedureValue); ok { - for arg, i in symbol_call.arg_types { - if i == 0 && is_selector_call { - continue - } - - for name in arg.names { - label := "" - is_current_ellipsis := false - - if arg.type != nil { - if ellipsis, ok := arg.type.derived.(^ast.Ellipsis); ok { - is_current_ellipsis = true - } - } - - #partial switch v in name.derived { - case ^ast.Ident: - label = v.name - case ^ast.Poly_Type: - if ident, ok := v.type.derived.(^ast.Ident); ok { - label = ident.name - } else { - continue loop - } - case: - continue loop - } + for name, name_idx in arg.names { - if is_ellipsis || symbol_arg_count >= len(call.args) { - if arg.default_value == nil { - continue loop - } + arg_call_idx := arg_type_idx + name_idx + if is_selector_call do arg_call_idx -= 1 - if !config.enable_inlay_hints_default_params { - continue loop - } - - value := node_to_string(arg.default_value) + label := expr_name(name) or_continue loop - call_range := common.get_token_range(call, string(document.text)) + is_provided_named, is_provided_positional: bool + call_arg: ^ast.Expr - position: common.Position - position = call_range.end - position.character -= 1 + for a, a_i in call.args[positional_arg_idx:] { + call_arg_idx := a_i + positional_arg_idx + // provided as named + if field_value, ok := a.derived.(^ast.Field_Value); ok { + ident := field_value.field.derived.(^ast.Ident) or_break + if ident.name == label { + is_provided_named = true + call_arg = a + } + break + } // provided as positional + else if arg_call_idx == call_arg_idx { + is_provided_positional = true + positional_arg_idx += 1 + call_arg = a + break + } + } - needs_leading_comma := i > 0 + if is_ellipsis || (!is_provided_named && !is_provided_positional) { + // This parameter is not provided, so it should use default value + if arg.default_value == nil { + continue loop + } - if !has_added_default && needs_leading_comma { - till_end := string(document.text[:call.close.offset]) - #reverse for ch in till_end { - switch ch { - case ' ', '\t', '\n': - continue - case ',': - needs_leading_comma = false - } - break - } - } + if !config.enable_inlay_hints_default_params { + continue loop + } - hint := InlayHint { - kind = .Parameter, - label = fmt.tprintf("%s%v = %v", needs_leading_comma ? ", " : "", label, value), - position = position, - } - append(&hints, hint) + value := node_to_string(arg.default_value) - has_added_default = true - } else if config.enable_inlay_hints_params { + needs_leading_comma := arg_call_idx > 0 - // if is already provided as a named argument - if _, ok := call.args[symbol_arg_count].derived.(^ast.Field_Value); ok { - symbol_arg_count += 1 + if !has_added_default && needs_leading_comma { + till_end := string(document.text[:call.close.offset]) + #reverse for ch in till_end { + switch ch { + case ' ', '\t', '\n': continue + case ',': + needs_leading_comma = false } - - // if the arg name and param name are the same, don't add it. - same_name: bool - #partial switch v in call.args[symbol_arg_count].derived_expr { - case ^ast.Ident: - same_name = label == v.name - case ^ast.Poly_Type: - if ident, ok := v.type.derived.(^ast.Ident); ok { - same_name = label == ident.name - } - } - - if !same_name { - range := common.get_token_range(call.args[symbol_arg_count], string(document.text)) - hint := InlayHint { - kind = .Parameter, - label = fmt.tprintf("%v = ", label), - position = range.start, - } - append(&hints, hint) - } + break } + } - if is_current_ellipsis { - is_ellipsis = true + hint := InlayHint { + kind = .Parameter, + label = fmt.tprintf("%s%v = %v", needs_leading_comma ? ", " : "", label, value), + position = end_pos, + } + append(&hints, hint) + + has_added_default = true + } else if config.enable_inlay_hints_params && is_provided_positional && !is_provided_named { + // This parameter is provided via positional argument, show parameter hint + + // if the arg name and param name are the same, don't add it. + call_arg_name, _ := expr_name(call_arg) + if call_arg_name != label { + range := common.get_token_range(call_arg, string(document.text)) + hint := InlayHint { + kind = .Parameter, + label = fmt.tprintf("%v = ", label), + position = range.start, } - - symbol_arg_count += 1 + append(&hints, hint) } } + + if arg.type != nil { + _, is_current_ellipsis := arg.type.derived.(^ast.Ellipsis) + is_ellipsis ||= is_current_ellipsis + } } } } |