use tower_lsp::lsp_types::{Position, Range};
use crate::navigation::references::SymbolKind;
pub(crate) fn is_after_arrow(source: &str, position: Position) -> bool {
let line = match source.lines().nth(position.line as usize) {
Some(l) => l,
None => return false,
};
let chars: Vec<char> = line.chars().collect();
let col = position.character as usize;
let mut utf16_col = 0usize;
let mut char_idx = 0usize;
for ch in &chars {
if utf16_col >= col {
break;
}
utf16_col += ch.len_utf16();
char_idx += 1;
}
let is_word = |c: char| c.is_alphanumeric() || c == '_';
while char_idx > 0 && is_word(chars[char_idx - 1]) {
char_idx -= 1;
}
char_idx >= 2 && chars[char_idx - 1] == '>' && chars[char_idx - 2] == '-'
}
pub(crate) fn symbol_kind_at(source: &str, position: Position, word: &str) -> Option<SymbolKind> {
if word.starts_with('$') {
return None; }
let line = source.lines().nth(position.line as usize)?;
let chars: Vec<char> = line.chars().collect();
let col = position.character as usize;
let mut utf16_col = 0usize;
let mut char_idx = 0usize;
for ch in &chars {
if utf16_col >= col {
break;
}
utf16_col += ch.len_utf16();
char_idx += 1;
}
let is_word_char = |c: char| c.is_alphanumeric() || c == '_';
while char_idx > 0 && is_word_char(chars[char_idx - 1]) {
char_idx -= 1;
}
let word_end = {
let mut i = char_idx;
while i < chars.len() && is_word_char(chars[i]) {
i += 1;
}
while i < chars.len() && chars[i] == ' ' {
i += 1;
}
i
};
let next_is_call = word_end < chars.len() && chars[word_end] == '(';
if char_idx >= 2 && chars[char_idx - 1] == '>' && chars[char_idx - 2] == '-' {
return if next_is_call {
Some(SymbolKind::Method)
} else {
Some(SymbolKind::Property)
};
}
if char_idx >= 3
&& chars[char_idx - 1] == '>'
&& chars[char_idx - 2] == '-'
&& chars[char_idx - 3] == '?'
{
return if next_is_call {
Some(SymbolKind::Method)
} else {
Some(SymbolKind::Property)
};
}
if char_idx >= 2 && chars[char_idx - 1] == ':' && chars[char_idx - 2] == ':' {
return if next_is_call {
Some(SymbolKind::Method)
} else {
Some(SymbolKind::Constant)
};
}
if word
.chars()
.next()
.map(|c| c.is_uppercase())
.unwrap_or(false)
{
return Some(SymbolKind::Class);
}
Some(SymbolKind::Function)
}
pub(crate) fn position_to_byte_offset_strict(source: &str, position: Position) -> Option<u32> {
let mut line_start = 0usize;
for _ in 0..position.line {
match source[line_start..].find('\n') {
Some(i) => line_start += i + 1,
None => return None,
}
}
let line_end = source[line_start..]
.find('\n')
.map_or(source.len(), |i| line_start + i);
let line_content = source[line_start..line_end].trim_end_matches('\r');
let byte =
line_start + crate::text::utf16_offset_to_byte(line_content, position.character as usize);
Some(byte as u32)
}
pub(crate) fn range_within(inner: Range, outer: Range) -> bool {
let start_ok =
(inner.start.line, inner.start.character) >= (outer.start.line, outer.start.character);
let end_ok = (inner.end.line, inner.end.character) <= (outer.end.line, outer.end.character);
start_ok && end_ok
}