use crate::parser;
use crate::parser::ast::SectionType;
use tower_lsp::lsp_types::{DocumentSymbol, Position, Range, SymbolKind};
pub fn build_section_children_for_doc(doc: &parser::GctfDocument) -> Vec<DocumentSymbol> {
let mut all_children: Vec<DocumentSymbol> = Vec::new();
for s in &doc.sections {
let mut children: Vec<DocumentSymbol> = Vec::new();
if s.section_type == SectionType::Asserts {
for (idx, line) in s.raw_content.lines().enumerate() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with("//") {
continue;
}
let line_num = (s.start_line + idx + 1) as u32;
#[expect(deprecated)]
let mut assertion_symbol = DocumentSymbol {
name: trimmed.to_string(),
detail: Some("assertion".to_string()),
kind: SymbolKind::STRING,
tags: None,
deprecated: None,
range: Range::new(
Position::new(line_num, 0),
Position::new(line_num, trimmed.len() as u32),
),
selection_range: Range::new(
Position::new(line_num, 0),
Position::new(line_num, trimmed.len() as u32),
),
children: None,
};
let mut var_children = Vec::new();
let mut offset = 0usize;
while let Some(open) = trimmed[offset..].find("{{") {
let abs_open = offset + open;
let Some(close_rel) = trimmed[abs_open..].find("}}") else {
break;
};
let abs_close = abs_open + close_rel + 2;
let inner = trimmed[abs_open + 2..abs_close - 2].trim();
if !inner.is_empty() {
#[expect(deprecated)]
var_children.push(DocumentSymbol {
name: inner.to_string(),
detail: Some("variable reference".to_string()),
kind: SymbolKind::VARIABLE,
tags: None,
deprecated: None,
range: Range::new(
Position::new(line_num, abs_open as u32),
Position::new(line_num, abs_close as u32),
),
selection_range: Range::new(
Position::new(line_num, (abs_open + 2) as u32),
Position::new(line_num, (abs_close - 2) as u32),
),
children: None,
});
}
offset = abs_close;
}
if !var_children.is_empty() {
assertion_symbol.children = Some(var_children);
}
children.push(assertion_symbol);
}
}
if s.section_type == SectionType::Extract {
for (idx, line) in s.raw_content.lines().enumerate() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with("//") {
continue;
}
let Some((name, expr)) = trimmed.split_once('=') else {
continue;
};
let var_name = name.trim();
let line_num = (s.start_line + idx + 1) as u32;
let expr_trimmed = expr.trim();
#[expect(deprecated)]
children.push(DocumentSymbol {
name: var_name.to_string(),
detail: Some(format!("extract: {}", expr_trimmed)),
kind: SymbolKind::VARIABLE,
tags: None,
deprecated: None,
range: Range::new(
Position::new(line_num, 0),
Position::new(line_num, trimmed.len() as u32),
),
selection_range: Range::new(
Position::new(line_num, 0),
Position::new(line_num, var_name.len() as u32),
),
children: None,
});
}
}
if !children.is_empty() {
all_children.extend(children);
}
}
all_children
}