use std::sync::Arc;
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, Position};
use super::member::{line_byte_offset, receiver_class_at};
use crate::document::ast::ParsedDoc;
use crate::types::type_map::{TypeMap, enclosing_class_at, members_of_class};
#[allow(clippy::too_many_arguments)]
pub(super) fn match_arm_completions(
source: &str,
doc: &ParsedDoc,
other_docs: &[Arc<ParsedDoc>],
position: Position,
get_type_map: &dyn Fn() -> Arc<TypeMap>,
analysis: Option<&mir_analyzer::FileAnalysis>,
) -> Option<Vec<CompletionItem>> {
let start_line = position.line as usize;
let end_line = start_line.saturating_sub(5);
let all_lines: Vec<&str> = source.lines().collect();
let type_map_cell: std::cell::OnceCell<Arc<TypeMap>> = std::cell::OnceCell::new();
for line_idx in (end_line..=start_line).rev() {
let line = all_lines.get(line_idx).copied()?;
if let Some(cap) = extract_match_subject(line) {
let class_name = if cap == "this" {
enclosing_class_at(source, doc, position)?
} else {
let subject_byte = line.find(&format!("${cap}"))?;
let var_offset = line_byte_offset(doc, line_idx as u32, subject_byte + 1);
analysis
.and_then(|a| receiver_class_at(a, var_offset))
.or_else(|| {
let type_map = type_map_cell.get_or_init(get_type_map);
type_map.get(&format!("${cap}")).map(str::to_owned)
})?
};
let all_docs: Vec<&ParsedDoc> = std::iter::once(doc)
.chain(other_docs.iter().map(|d| d.as_ref()))
.collect();
for d in &all_docs {
let members = members_of_class(d, &class_name);
if !members.constants.is_empty() {
return Some(
members
.constants
.iter()
.map(|c| CompletionItem {
label: format!("{class_name}::{c}"),
kind: Some(CompletionItemKind::CONSTANT),
..Default::default()
})
.collect(),
);
}
}
}
}
None
}
pub(super) fn extract_match_subject(line: &str) -> Option<String> {
let trimmed = line.trim();
let after = trimmed.strip_prefix("match")?.trim_start();
let after = after.strip_prefix('(')?;
let inner: String = after.chars().take_while(|&c| c != ')').collect();
let var = inner.trim().trim_start_matches('$');
if var.is_empty() {
None
} else {
Some(var.to_string())
}
}