Skip to main content

only_semantic/
folding.rs

1use text_size::TextRange;
2
3use crate::{SemanticSnapshot, TaskStepAst};
4
5/// Kind of folding range produced for editor consumers.
6///
7/// Args:
8/// None.
9///
10/// Returns:
11/// Stable folding categories for namespace and task blocks.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum FoldingRangeKind {
14    Namespace,
15    Task,
16    CommandBlock,
17}
18
19/// Foldable source span for editor consumers.
20///
21/// Args:
22/// None.
23///
24/// Returns:
25/// One semantic folding range and its category.
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct FoldingRange {
28    pub range: TextRange,
29    pub kind: FoldingRangeKind,
30}
31
32/// Builds folding ranges from one semantic snapshot.
33///
34/// Args:
35/// snapshot: Immutable semantic snapshot for one document version.
36///
37/// Returns:
38/// Foldable ranges for namespace blocks and task blocks.
39pub 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}