Detect whether the cursor is in an array key completion context. Recognises patterns like: - `$var['` — empty partial, single-quote - `$var['na` — partial "na", single-quote - `$var["` — empty partial, double-quote - `$var["na` — partial "na", double-quote - `$var[` — no quote yet - `$var['key1']['key2'][`
(
content: &str,
position: Position,
)
| 128 | /// |
| 129 | /// Returns `None` if the cursor is not in such a context. |
| 130 | pub(crate) fn detect_array_key_context( |
| 131 | content: &str, |
| 132 | position: Position, |
| 133 | ) -> Option<ArrayKeyContext> { |
| 134 | let lines: Vec<&str> = content.lines().collect(); |
| 135 | let line_idx = position.line as usize; |
| 136 | if line_idx >= lines.len() { |
| 137 | return None; |
| 138 | } |
| 139 | |
| 140 | let line = lines[line_idx]; |
| 141 | let chars: Vec<char> = line.chars().collect(); |
| 142 | let col = (position.character as usize).min(chars.len()); |
| 143 | |
| 144 | if col == 0 { |
| 145 | return None; |
| 146 | } |
| 147 | |
| 148 | // Walk backward from the cursor to find the pattern. |
| 149 | let mut i = col; |
| 150 | |
| 151 | // 1. Collect partial key text (identifier characters the user has typed). |
| 152 | let partial_end = i; |
| 153 | while i > 0 && (chars[i - 1].is_alphanumeric() || chars[i - 1] == '_') { |
| 154 | i -= 1; |
| 155 | } |
| 156 | let partial_start = i; |
| 157 | |
| 158 | // 2. Check for a quote character. |
| 159 | let quote_char = if i > 0 && (chars[i - 1] == '\'' || chars[i - 1] == '"') { |
| 160 | let q = chars[i - 1]; |
| 161 | i -= 1; |
| 162 | Some(q) |
| 163 | } else { |
| 164 | None |
| 165 | }; |
| 166 | |
| 167 | // 3. Must have `[` immediately before the quote (or the partial if no quote). |
| 168 | if i == 0 || chars[i - 1] != '[' { |
| 169 | return None; |
| 170 | } |
| 171 | i -= 1; // skip `[` |
| 172 | |
| 173 | let key_start_col = partial_start as u32; |
| 174 | |
| 175 | // 4. Try to collect chained `['key']` access segments before the |
| 176 | // current `[`. Walk backward through zero or more `]['key']` |
| 177 | // or `]["key"]` patterns, collecting the keys. |
| 178 | let mut prefix_keys: Vec<String> = Vec::new(); |
| 179 | loop { |
| 180 | // We're now right before the `[` we just consumed. |
| 181 | // Check if there is a preceding `]` — that would indicate a |
| 182 | // chained access like `$var['k1']['k2'][`. |
| 183 | if i == 0 || chars[i - 1] != ']' { |
| 184 | break; |
| 185 | } |
| 186 | // Try to parse the preceding `['key']` segment. |
| 187 | let saved_i = i; |