1use std::collections::HashMap;
6use std::path::Path;
7
8use tree_sitter::{Language, Query};
9use tree_sitter_highlight::HighlightConfiguration;
10
11pub const HIGHLIGHT_NAMES: &[&str] = &[
15 "attribute",
16 "comment",
17 "constant",
18 "constant.builtin",
19 "constructor",
20 "function",
21 "function.builtin",
22 "keyword",
23 "label",
24 "number",
25 "operator",
26 "property",
27 "punctuation",
28 "punctuation.bracket",
29 "punctuation.delimiter",
30 "string",
31 "string.escape",
32 "string.special",
33 "tag",
34 "type",
35 "type.builtin",
36 "variable",
37 "variable.builtin",
38 "variable.parameter",
39];
40
41pub struct LangEntry {
42 pub language: Language,
43 pub config: Option<HighlightConfiguration>,
46 pub tags: Option<Query>,
49}
50
51pub struct LanguageRegistry {
52 entries: Vec<LangEntry>,
53 by_ext: HashMap<&'static str, usize>,
54}
55
56impl LanguageRegistry {
57 #[allow(clippy::too_many_lines)]
60 pub fn build() -> Self {
61 let mut r = Self {
62 entries: Vec::new(),
63 by_ext: HashMap::new(),
64 };
65
66 r.add(
67 "rust",
68 &["rs"],
69 tree_sitter_rust::LANGUAGE.into(),
70 tree_sitter_rust::HIGHLIGHTS_QUERY,
71 Some(tree_sitter_rust::TAGS_QUERY),
72 );
73 r.add(
74 "python",
75 &["py", "pyi"],
76 tree_sitter_python::LANGUAGE.into(),
77 tree_sitter_python::HIGHLIGHTS_QUERY,
78 Some(tree_sitter_python::TAGS_QUERY),
79 );
80 r.add(
81 "javascript",
82 &["js", "jsx", "mjs", "cjs"],
83 tree_sitter_javascript::LANGUAGE.into(),
84 &format!(
85 "{}\n{}",
86 tree_sitter_javascript::HIGHLIGHT_QUERY,
87 tree_sitter_javascript::JSX_HIGHLIGHT_QUERY
88 ),
89 Some(tree_sitter_javascript::TAGS_QUERY),
90 );
91 r.add(
92 "typescript",
93 &["ts", "mts", "cts"],
94 tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
95 &format!(
96 "{}\n{}",
97 tree_sitter_javascript::HIGHLIGHT_QUERY,
98 tree_sitter_typescript::HIGHLIGHTS_QUERY
99 ),
100 Some(tree_sitter_typescript::TAGS_QUERY),
101 );
102 r.add(
103 "tsx",
104 &["tsx"],
105 tree_sitter_typescript::LANGUAGE_TSX.into(),
106 &format!(
107 "{}\n{}\n{}",
108 tree_sitter_javascript::HIGHLIGHT_QUERY,
109 tree_sitter_javascript::JSX_HIGHLIGHT_QUERY,
110 tree_sitter_typescript::HIGHLIGHTS_QUERY
111 ),
112 Some(tree_sitter_typescript::TAGS_QUERY),
113 );
114 r.add(
115 "go",
116 &["go"],
117 tree_sitter_go::LANGUAGE.into(),
118 tree_sitter_go::HIGHLIGHTS_QUERY,
119 Some(tree_sitter_go::TAGS_QUERY),
120 );
121 r.add(
122 "c",
123 &["c", "h"],
124 tree_sitter_c::LANGUAGE.into(),
125 tree_sitter_c::HIGHLIGHT_QUERY,
126 Some(tree_sitter_c::TAGS_QUERY),
127 );
128 r.add(
129 "cpp",
130 &["cpp", "cc", "cxx", "hpp", "hh", "hxx"],
131 tree_sitter_cpp::LANGUAGE.into(),
132 tree_sitter_cpp::HIGHLIGHT_QUERY,
133 Some(tree_sitter_cpp::TAGS_QUERY),
134 );
135 r.add(
136 "java",
137 &["java"],
138 tree_sitter_java::LANGUAGE.into(),
139 tree_sitter_java::HIGHLIGHTS_QUERY,
140 Some(tree_sitter_java::TAGS_QUERY),
141 );
142 r.add(
143 "c-sharp",
144 &["cs"],
145 tree_sitter_c_sharp::LANGUAGE.into(),
146 tree_sitter_c_sharp::HIGHLIGHTS_QUERY,
147 Some(tree_sitter_c_sharp::TAGS_QUERY),
148 );
149 r.add(
150 "ruby",
151 &["rb"],
152 tree_sitter_ruby::LANGUAGE.into(),
153 tree_sitter_ruby::HIGHLIGHTS_QUERY,
154 Some(tree_sitter_ruby::TAGS_QUERY),
155 );
156 r.add(
157 "php",
158 &["php"],
159 tree_sitter_php::LANGUAGE_PHP.into(),
160 tree_sitter_php::HIGHLIGHTS_QUERY,
161 Some(tree_sitter_php::TAGS_QUERY),
162 );
163 r.add(
164 "bash",
165 &["sh", "bash", "zsh"],
166 tree_sitter_bash::LANGUAGE.into(),
167 tree_sitter_bash::HIGHLIGHT_QUERY,
168 None,
169 );
170 r.add(
171 "json",
172 &["json"],
173 tree_sitter_json::LANGUAGE.into(),
174 tree_sitter_json::HIGHLIGHTS_QUERY,
175 None,
176 );
177 r.add(
178 "html",
179 &["html", "htm"],
180 tree_sitter_html::LANGUAGE.into(),
181 tree_sitter_html::HIGHLIGHTS_QUERY,
182 None,
183 );
184 r.add(
185 "css",
186 &["css"],
187 tree_sitter_css::LANGUAGE.into(),
188 tree_sitter_css::HIGHLIGHTS_QUERY,
189 None,
190 );
191
192 r
193 }
194
195 fn add(
196 &mut self,
197 name: &'static str,
198 extensions: &'static [&'static str],
199 language: Language,
200 highlights: &str,
201 tags: Option<&str>,
202 ) {
203 let config = HighlightConfiguration::new(language.clone(), name, highlights, "", "")
204 .ok()
205 .map(|mut c| {
206 c.configure(HIGHLIGHT_NAMES);
207 c
208 });
209 let tags = tags.and_then(|q| Query::new(&language, q).ok());
210 let idx = self.entries.len();
211 self.entries.push(LangEntry {
212 language,
213 config,
214 tags,
215 });
216 for ext in extensions {
217 self.by_ext.insert(ext, idx);
218 }
219 }
220
221 pub fn for_path(&self, path: &str) -> Option<&LangEntry> {
223 let ext = Path::new(path).extension()?.to_str()?;
224 let &idx = self.by_ext.get(ext)?;
225 self.entries.get(idx)
226 }
227}
228
229impl Default for LanguageRegistry {
230 fn default() -> Self {
231 Self::build()
232 }
233}