use crate::editor::position::Position;
use crate::schema::context::{CursorContext, CompletionMode};
use regex::Regex;
fn leading_indent(s: &str) -> usize {
s.chars().take_while(|c| c.is_whitespace()).count()
}
pub fn detect_context(lines: &[String], trigger: &Position) -> Option<CursorContext> {
if lines.is_empty() {
return Some(CursorContext { section_path: vec![], mode: CompletionMode::Key });
}
let key_re = Regex::new(r"^\s*(?:-\s*)?([^\s:#]+)\s*:").ok()?;
let mut stack: Vec<(usize, String)> = Vec::new();
let last_idx = std::cmp::min(trigger.line, lines.len() - 1);
for (i, line) in lines.iter().take(last_idx + 1).enumerate() {
let slice = if i == trigger.line {
let col = std::cmp::min(trigger.column, line.len());
&line[..col]
} else {
line.as_str()
};
if slice.trim().is_empty() {
continue;
}
let indent = leading_indent(slice);
if let Some(cap) = key_re.captures(slice) {
let key = cap.get(1).unwrap().as_str().to_string();
while let Some(&(last_indent, _)) = stack.last() {
if last_indent >= indent {
stack.pop();
} else {
break;
}
}
stack.push((indent, key));
}
}
let mut section_stack = stack;
let current_line = lines.get(trigger.line).map(|s| s.as_str()).unwrap_or("");
if let Some(colon_pos) = current_line.find(':') {
let key_part = current_line[..colon_pos].trim();
let key_name = key_part.strip_prefix("- ").unwrap_or(key_part).trim().to_string();
let current_key_indent = leading_indent(¤t_line[..colon_pos]);
if let Some((last_indent, last_key)) = section_stack.last()
&& *last_indent == current_key_indent && last_key == &key_name {
section_stack.pop();
}
let section_path = section_stack.iter().map(|(_, k)| k.clone()).collect::<Vec<_>>();
if trigger.column > colon_pos {
return Some(CursorContext {
section_path,
mode: CompletionMode::Value { key: key_name },
});
} else {
return Some(CursorContext { section_path, mode: CompletionMode::Key });
}
}
let section_path = section_stack.iter().map(|(_, k)| k.clone()).collect::<Vec<_>>();
Some(CursorContext { section_path, mode: CompletionMode::Key })
}