1use std::borrow::Cow;
2
3use crate::{constants::*, ranges::EffectRange, AstNode, ByteRange, CodeRange, Range};
4use regex::Regex;
5
6pub enum GritMetaValue {
7 Underscore,
8 Dots,
9 Variable(String),
10}
11
12pub trait Language: Sized {
13 type Node<'a>: AstNode;
14
15 fn language_name(&self) -> &'static str;
16
17 fn snippet_context_strings(&self) -> &[(&'static str, &'static str)];
18
19 fn metavariable_prefix(&self) -> &'static str {
20 "$"
21 }
22
23 fn comment_prefix(&self) -> &'static str {
24 "//"
25 }
26
27 fn metavariable_prefix_substitute(&self) -> &'static str {
28 "ยต"
29 }
30
31 fn metavariable_regex(&self) -> &'static Regex {
32 &VARIABLE_REGEX
33 }
34
35 fn replaced_metavariable_regex(&self) -> &'static Regex {
36 &REPLACED_VARIABLE_REGEX
37 }
38
39 fn metavariable_bracket_regex(&self) -> &'static Regex {
40 &BRACKET_VAR_REGEX
41 }
42
43 fn exact_variable_regex(&self) -> &'static Regex {
44 &EXACT_VARIABLE_REGEX
45 }
46
47 fn exact_replaced_variable_regex(&self) -> &'static Regex {
48 &EXACT_REPLACED_VARIABLE_REGEX
49 }
50
51 fn is_comment(&self, node: &Self::Node<'_>) -> bool;
52
53 fn is_metavariable(&self, node: &Self::Node<'_>) -> bool;
54
55 #[allow(unused_variables)]
56 fn is_statement(&self, node: &Self::Node<'_>) -> bool {
57 false
58 }
59
60 fn comment_text_range(&self, node: &Self::Node<'_>) -> Option<ByteRange> {
62 Some(node.byte_range())
63 }
64
65 fn align_padding<'a>(
68 &self,
69 node: &Self::Node<'a>,
70 range: &CodeRange,
71 skip_ranges: &[CodeRange],
72 new_padding: Option<usize>,
73 offset: usize,
74 substitutions: &mut [(EffectRange, String)],
75 ) -> Cow<'a, str>;
76
77 fn pad_snippet<'a>(&self, snippet: &'a str, padding: &str) -> Cow<'a, str>;
81
82 fn substitute_metavariable_prefix(&self, src: &str) -> String {
83 self.metavariable_regex()
84 .replace_all(
85 src,
86 format!("{}$1", self.metavariable_prefix_substitute()).as_str(),
87 )
88 .to_string()
89 }
90
91 fn snippet_metavariable_to_grit_metavariable(&self, src: &str) -> Option<GritMetaValue> {
92 src.trim()
93 .strip_prefix(self.metavariable_prefix_substitute())
94 .map(|s| match s {
95 "_" => GritMetaValue::Underscore,
96 "..." => GritMetaValue::Dots,
97 _ => {
98 let mut s = s.to_owned();
99 s.insert_str(0, self.metavariable_prefix());
100 GritMetaValue::Variable(s)
101 }
102 })
103 }
104
105 #[allow(unused_variables)]
113 fn check_replacements(&self, node: Self::Node<'_>, replacements: &mut Vec<Replacement>) {}
114
115 #[allow(unused_variables)]
116 fn take_padding(&self, current: char, next: Option<char>) -> Option<char> {
117 if current.is_whitespace() {
118 Some(current)
119 } else {
120 None
121 }
122 }
123
124 fn get_skip_padding_ranges(&self, node: &Self::Node<'_>) -> Vec<CodeRange>;
125
126 fn should_pad_snippet(&self) -> bool {
130 false
131 }
132
133 fn make_single_line_comment(&self, text: &str) -> String {
134 format!("// {text}\n")
135 }
136}
137
138#[derive(Clone, Debug)]
139pub struct Replacement {
140 pub range: Range,
141 pub replacement: &'static str,
142}
143
144impl Replacement {
145 pub fn new(range: Range, replacement: &'static str) -> Self {
146 Self { range, replacement }
147 }
148}
149
150impl From<&Replacement> for (std::ops::Range<usize>, usize) {
151 fn from(replacement: &Replacement) -> Self {
152 (
153 (replacement.range.start_byte as usize)..(replacement.range.end_byte as usize),
154 replacement.replacement.len(),
155 )
156 }
157}