#[derive(Debug, PartialEq, Eq)]
pub(super) struct CompletionContext {
pub(super) token: String,
pub(super) prefix: String,
pub(super) suffix: String,
}
pub(super) fn completion_context(
line: &str,
cursor_pos: usize,
token: &str,
) -> Option<CompletionContext> {
let cursor_pos = cursor_pos.min(line.len());
if !line.is_char_boundary(cursor_pos) {
return None;
}
let token_start = cursor_pos.checked_sub(token.len())?;
if !line.is_char_boundary(token_start) || &line[token_start..cursor_pos] != token {
return None;
}
Some(CompletionContext {
token: token.to_owned(),
prefix: line[..token_start].to_owned(),
suffix: line[cursor_pos..].to_owned(),
})
}
#[derive(Debug, PartialEq)]
pub(super) struct LibraryContext {
pub(super) partial: String,
pub(super) start_pos: usize,
}
pub(super) fn detect_library_context(
line: &str,
cursor_pos: usize,
func_names: &[String],
) -> Option<LibraryContext> {
let pos = cursor_pos.min(line.len());
if !line.is_char_boundary(pos) {
return None;
}
let before_cursor = &line[..pos];
let mut paren_depth = 0;
let mut last_open_paren_pos = None;
for (i, c) in before_cursor.char_indices().rev() {
match c {
')' => paren_depth += 1,
'(' => {
if paren_depth == 0 {
last_open_paren_pos = Some(i);
break;
}
paren_depth -= 1;
}
_ => {}
}
}
let open_pos = last_open_paren_pos?;
let before_paren = before_cursor[..open_pos].trim_end();
let func_name = before_paren
.rsplit(|c: char| !c.is_alphanumeric() && c != '_' && c != '.' && c != ':')
.next()?;
if func_name.is_empty() {
return None;
}
let func_start = before_paren.len() - func_name.len();
if func_start > 0 {
let preceding_char = before_paren[..func_start].chars().next_back();
if matches!(preceding_char, Some('$' | '@')) {
return None;
}
}
if !func_names.iter().any(|f| f == func_name) {
return None;
}
let after_paren = &before_cursor[open_pos + 1..];
if after_paren.contains(',') {
return None;
}
let trimmed = after_paren.trim_start();
if trimmed.starts_with('"') || trimmed.starts_with('\'') {
return None;
}
let partial: String = trimmed
.chars()
.take_while(|c| c.is_alphanumeric() || *c == '.' || *c == '_')
.collect();
let whitespace_len = after_paren.len() - trimmed.len();
let start_pos = open_pos + 1 + whitespace_len;
Some(LibraryContext { partial, start_pos })
}