1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use enum_dispatch::enum_dispatch;

use crate::core::parser::segments::base::ErasedSegment;
use crate::core::rules::context::RuleContext;
use crate::dialects::{SyntaxKind, SyntaxSet};

#[enum_dispatch]
pub trait BaseCrawler {
    fn works_on_unparsable(&self) -> bool {
        false
    }

    fn passes_filter(&self, segment: &ErasedSegment) -> bool {
        self.works_on_unparsable() || !segment.is_type(SyntaxKind::Unparsable)
    }

    fn crawl<'a>(&self, context: RuleContext<'a>) -> Vec<RuleContext<'a>>;
}

#[enum_dispatch(BaseCrawler)]
pub enum Crawler {
    RootOnlyCrawler,
    SegmentSeekerCrawler,
    TokenSeekerCrawler,
}

/// A crawler that doesn't crawl.
///
/// This just yields one context on the root-level (topmost) segment of the
/// file.
#[derive(Debug, Default, Clone)]
pub struct RootOnlyCrawler;

impl BaseCrawler for RootOnlyCrawler {
    fn crawl<'a>(&self, context: RuleContext<'a>) -> Vec<RuleContext<'a>> {
        if self.passes_filter(&context.segment) { vec![context.clone()] } else { Vec::new() }
    }
}

pub struct SegmentSeekerCrawler {
    types: SyntaxSet,
    provide_raw_stack: bool,
    allow_recurse: bool,
}

impl SegmentSeekerCrawler {
    pub fn new(types: SyntaxSet) -> Self {
        Self { types, provide_raw_stack: false, allow_recurse: true }
    }

    pub fn disallow_recurse(mut self) -> Self {
        self.allow_recurse = false;
        self
    }

    pub fn provide_raw_stack(mut self) -> Self {
        self.provide_raw_stack = true;
        self
    }

    fn is_self_match(&self, segment: &ErasedSegment) -> bool {
        self.types.contains(segment.get_type())
    }
}

impl BaseCrawler for SegmentSeekerCrawler {
    fn crawl<'a>(&self, mut context: RuleContext<'a>) -> Vec<RuleContext<'a>> {
        let mut acc = Vec::new();
        let mut self_match = false;

        if self.is_self_match(&context.segment) {
            self_match = true;
            acc.push(context.clone());
        }

        if context.segment.segments().is_empty() || (self_match && !self.allow_recurse) {
            return acc;
        }

        if !self.types.intersects(context.segment.descendant_type_set()) {
            if self.provide_raw_stack {
                context.raw_stack.append(&mut context.segment.get_raw_segments());
            }

            return acc;
        }

        context.parent_stack.push(context.segment.clone());
        for (idx, child) in context.segment.gather_segments().into_iter().enumerate() {
            context.segment = child;
            context.segment_idx = idx;

            acc.extend(self.crawl(context.clone()));
        }

        acc
    }
}

pub struct TokenSeekerCrawler;

impl BaseCrawler for TokenSeekerCrawler {
    fn crawl<'a>(&self, mut context: RuleContext<'a>) -> Vec<RuleContext<'a>> {
        let mut acc = Vec::new();

        if context.segment.segments().is_empty() {
            acc.push(context.clone());
        }

        context.parent_stack.push(context.segment.clone());
        for (idx, child) in context.segment.gather_segments().into_iter().enumerate() {
            context.segment = child;
            context.segment_idx = idx;

            acc.extend(self.crawl(context.clone()));
        }

        acc
    }
}