1use text_size::TextRange;
2
3use crate::{SemanticSnapshot, TaskStepAst};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum FoldingRangeKind {
14 Namespace,
15 Task,
16 CommandBlock,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct FoldingRange {
28 pub range: TextRange,
29 pub kind: FoldingRangeKind,
30}
31
32pub fn folding_ranges(snapshot: &SemanticSnapshot) -> Vec<FoldingRange> {
40 let mut ranges = snapshot
41 .document
42 .tasks
43 .iter()
44 .map(|task| FoldingRange {
45 range: task.range,
46 kind: FoldingRangeKind::Task,
47 })
48 .collect::<Vec<_>>();
49
50 ranges.extend(snapshot.document.tasks.iter().flat_map(|task| {
51 task.steps.iter().filter_map(|step| match step {
52 TaskStepAst::CommandBlock(block) if block.line_ranges.len() > 1 => Some(FoldingRange {
53 range: TextRange::new(
54 block.line_ranges[0].start(),
55 block
56 .line_ranges
57 .last()
58 .expect("multi-line block must have a final line")
59 .end(),
60 ),
61 kind: FoldingRangeKind::CommandBlock,
62 }),
63 _ => None,
64 })
65 }));
66
67 for namespace in &snapshot.document.namespaces {
68 let namespace_range = snapshot
69 .document
70 .tasks
71 .iter()
72 .filter(|task| task.namespace.as_deref() == Some(namespace.name.as_str()))
73 .fold(namespace.range, |range, task| {
74 TextRange::new(range.start(), range.end().max(task.range.end()))
75 });
76 let namespace_range = namespace.close_range.map_or(namespace_range, |close| {
77 TextRange::new(namespace_range.start(), close.end())
78 });
79
80 ranges.push(FoldingRange {
81 range: namespace_range,
82 kind: FoldingRangeKind::Namespace,
83 });
84 }
85
86 ranges
87}