Detect an access operator (`->`, `?->`, or `::`) before a cursor position in an already-collapsed character slice, and extract the subject expression to the operator's left. The cursor is expected to sit *after* the operator, possibly with a partial identifier already typed (used by completion). # Parameters `chars` — the collapsed line as a char slice. `col` — the cursor's character offset wit
(chars: &[char], col: usize)
| 709 | /// `Some((subject, AccessKind))` when an operator is found, `None` |
| 710 | /// otherwise. |
| 711 | pub(crate) fn detect_access_operator(chars: &[char], col: usize) -> Option<(String, AccessKind)> { |
| 712 | let col = col.min(chars.len()); |
| 713 | |
| 714 | if chars.is_empty() { |
| 715 | return None; |
| 716 | } |
| 717 | |
| 718 | // Walk backwards past any partial identifier the user has typed, |
| 719 | // then skip whitespace so that `-> ` (operator followed by |
| 720 | // spaces but no identifier yet) is still detected. |
| 721 | let operator_end = { |
| 722 | let mut i = col; |
| 723 | while i > 0 && (chars[i - 1].is_alphanumeric() || chars[i - 1] == '_') { |
| 724 | i -= 1; |
| 725 | } |
| 726 | // Skip `$` prefix for partially typed static properties |
| 727 | // (e.g. `Foo::$f` — the `$` is the property sigil, not part |
| 728 | // of the operator). |
| 729 | if i > 0 && chars[i - 1] == '$' { |
| 730 | i -= 1; |
| 731 | } |
| 732 | while i > 0 && chars[i - 1] == ' ' { |
| 733 | i -= 1; |
| 734 | } |
| 735 | i |
| 736 | }; |
| 737 | |
| 738 | // Try `::`. |
| 739 | if operator_end >= 2 && chars[operator_end - 2] == ':' && chars[operator_end - 1] == ':' { |
| 740 | let subject = extract_double_colon_subject(chars, operator_end - 2); |
| 741 | if !subject.is_empty() { |
| 742 | return Some((subject, AccessKind::DoubleColon)); |
| 743 | } |
| 744 | } |
| 745 | |
| 746 | // Try `->`. |
| 747 | if operator_end >= 2 && chars[operator_end - 2] == '-' && chars[operator_end - 1] == '>' { |
| 748 | let subject = extract_arrow_subject(chars, operator_end - 2); |
| 749 | if !subject.is_empty() { |
| 750 | return Some((subject, AccessKind::Arrow)); |
| 751 | } |
| 752 | } |
| 753 | |
| 754 | // Try `?->` (null-safe operator). |
| 755 | if operator_end >= 3 |
| 756 | && chars[operator_end - 3] == '?' |
| 757 | && chars[operator_end - 2] == '-' |
| 758 | && chars[operator_end - 1] == '>' |
| 759 | { |
| 760 | let subject = extract_arrow_subject(chars, operator_end - 3); |
| 761 | if !subject.is_empty() { |
| 762 | return Some((subject, AccessKind::Arrow)); |
| 763 | } |
| 764 | } |
| 765 | |
| 766 | None |
| 767 | } |
| 768 |