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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use crate::{constants::*, traverse, AstNode, ByteRange, CodeRange, Order, Range};
use regex::Regex;

pub enum GritMetaValue {
    Underscore,
    Dots,
    Variable(String),
}

pub trait Language: Sized {
    type Node<'a>: AstNode;

    fn language_name(&self) -> &'static str;

    fn snippet_context_strings(&self) -> &[(&'static str, &'static str)];

    fn alternate_metavariable_kinds(&self) -> &[&'static str] {
        &[]
    }

    fn metavariable_prefix(&self) -> &'static str {
        "$"
    }

    fn comment_prefix(&self) -> &'static str {
        "//"
    }

    fn metavariable_prefix_substitute(&self) -> &'static str {
        "µ"
    }

    fn metavariable_regex(&self) -> &'static Regex {
        &VARIABLE_REGEX
    }

    fn replaced_metavariable_regex(&self) -> &'static Regex {
        &REPLACED_VARIABLE_REGEX
    }

    fn metavariable_bracket_regex(&self) -> &'static Regex {
        &BRACKET_VAR_REGEX
    }

    fn exact_variable_regex(&self) -> &'static Regex {
        &EXACT_VARIABLE_REGEX
    }

    fn exact_replaced_variable_regex(&self) -> &'static Regex {
        &EXACT_REPLACED_VARIABLE_REGEX
    }

    fn is_comment(&self, node: &Self::Node<'_>) -> bool;

    fn is_metavariable(&self, node: &Self::Node<'_>) -> bool;

    #[allow(unused_variables)]
    fn is_statement(&self, node: &Self::Node<'_>) -> bool {
        false
    }

    // assumes trim doesn't do anything otherwise range is off
    fn comment_text_range(&self, node: &Self::Node<'_>) -> Option<ByteRange> {
        Some(node.byte_range())
    }

    // in languages we pad such as python or yaml there are
    // some kinds of nodes we don't want to pad, such as python strings.
    // this function identifies those nodes.
    #[allow(unused_variables)]
    fn should_skip_padding(&self, node: &Self::Node<'_>) -> bool {
        false
    }

    #[allow(unused_variables)]
    fn get_skip_padding_ranges_for_snippet(&self, snippet: &str) -> Vec<CodeRange> {
        Vec::new()
    }

    #[allow(unused_variables)]
    fn get_skip_padding_ranges(&self, node: &Self::Node<'_>) -> Vec<CodeRange> {
        let mut ranges = Vec::new();
        for n in traverse(node.walk(), Order::Pre) {
            if self.should_skip_padding(&n) {
                ranges.push(n.code_range())
            }
        }
        ranges
    }

    fn substitute_metavariable_prefix(&self, src: &str) -> String {
        self.metavariable_regex()
            .replace_all(
                src,
                format!("{}$1", self.metavariable_prefix_substitute()).as_str(),
            )
            .to_string()
    }

    fn snippet_metavariable_to_grit_metavariable(&self, src: &str) -> Option<GritMetaValue> {
        src.trim()
            .strip_prefix(self.metavariable_prefix_substitute())
            .map(|s| match s {
                "_" => GritMetaValue::Underscore,
                "..." => GritMetaValue::Dots,
                _ => {
                    let mut s = s.to_owned();
                    s.insert_str(0, self.metavariable_prefix());
                    GritMetaValue::Variable(s)
                }
            })
    }

    /// Check for nodes that should be removed or replaced.
    ///
    /// This is used to "repair" the program after rewriting, such as by
    /// deleting orphaned ranges (like a variable declaration without any
    /// variables). If the node should be removed, it adds a range with a `None`
    /// value. If the node should be replaced, it adds a range with the
    /// replacement value.
    #[allow(unused_variables)]
    fn check_replacements(&self, node: Self::Node<'_>, replacements: &mut Vec<Replacement>) {}

    #[allow(unused_variables)]
    fn take_padding(&self, current: char, next: Option<char>) -> Option<char> {
        if current.is_whitespace() {
            Some(current)
        } else {
            None
        }
    }

    /// Whether snippets should be padded.
    ///
    /// This is generally `true` for languages with relevant whitespace.
    fn should_pad_snippet(&self) -> bool {
        false
    }

    fn make_single_line_comment(&self, text: &str) -> String {
        format!("// {text}\n")
    }
}

#[derive(Clone, Debug)]
pub struct Replacement {
    pub range: Range,
    pub replacement: &'static str,
}

impl Replacement {
    pub fn new(range: Range, replacement: &'static str) -> Self {
        Self { range, replacement }
    }
}