Detect whether the cursor is inside a function/method call and extract the context needed for named-argument completion. Returns `None` if the cursor is not at an eligible position (e.g. after `$`, `->`, `::`, or inside a string/comment).
(content: &str, position: Position)
| 101 | /// Returns `None` if the cursor is not at an eligible position (e.g. after |
| 102 | /// `$`, `->`, `::`, or inside a string/comment). |
| 103 | pub fn detect_named_arg_context(content: &str, position: Position) -> Option<NamedArgContext> { |
| 104 | let chars: Vec<char> = content.chars().collect(); |
| 105 | let cursor = position_to_char_offset(&chars, position)?; |
| 106 | |
| 107 | // ── Check eligibility at cursor ───────────────────────────────── |
| 108 | // Walk backward from cursor through identifier chars to find the |
| 109 | // start of the current "word". |
| 110 | let mut word_start = cursor; |
| 111 | while word_start > 0 |
| 112 | && (chars[word_start - 1].is_alphanumeric() || chars[word_start - 1] == '_') |
| 113 | { |
| 114 | word_start -= 1; |
| 115 | } |
| 116 | |
| 117 | // If preceded by `$`, this is a variable — not a named arg. |
| 118 | if word_start > 0 && chars[word_start - 1] == '$' { |
| 119 | return None; |
| 120 | } |
| 121 | |
| 122 | // If preceded by `->` or `::`, member completion handles this. |
| 123 | if word_start >= 2 && chars[word_start - 2] == '-' && chars[word_start - 1] == '>' { |
| 124 | return None; |
| 125 | } |
| 126 | if word_start >= 2 && chars[word_start - 2] == ':' && chars[word_start - 1] == ':' { |
| 127 | return None; |
| 128 | } |
| 129 | |
| 130 | let prefix: String = chars[word_start..cursor].iter().collect(); |
| 131 | |
| 132 | // ── Find enclosing open paren ─────────────────────────────────── |
| 133 | let open_paren = find_enclosing_open_paren(&chars, word_start)?; |
| 134 | |
| 135 | // ── Extract call expression before `(` ────────────────────────── |
| 136 | let call_expr = extract_call_expression(&chars, open_paren)?; |
| 137 | if call_expr.is_empty() { |
| 138 | return None; |
| 139 | } |
| 140 | |
| 141 | // ── Parse arguments between `(` and cursor ────────────────────── |
| 142 | let args_text: String = chars[open_paren + 1..word_start].iter().collect(); |
| 143 | let (existing_named, positional_count) = parse_existing_args(&args_text); |
| 144 | |
| 145 | Some(NamedArgContext { |
| 146 | call_expression: call_expr, |
| 147 | existing_named_args: existing_named, |
| 148 | positional_count, |
| 149 | prefix, |
| 150 | }) |
| 151 | } |
| 152 | |
| 153 | // Re-exported from `crate::util` for backward compatibility with |
| 154 | // existing import paths. |