reovim_plugin_treesitter/
registry.rs1use std::{
7 collections::HashMap,
8 path::Path,
9 sync::{Arc, RwLock},
10};
11
12use tree_sitter::Language;
13
14pub trait LanguageSupport: Send + Sync + 'static {
20 fn language_id(&self) -> &'static str;
22
23 fn file_extensions(&self) -> &'static [&'static str];
25
26 fn tree_sitter_language(&self) -> Language;
28
29 fn highlights_query(&self) -> &'static str;
31
32 fn folds_query(&self) -> Option<&'static str> {
34 None
35 }
36
37 fn textobjects_query(&self) -> Option<&'static str> {
39 None
40 }
41
42 fn decorations_query(&self) -> Option<&'static str> {
44 None
45 }
46
47 fn injections_query(&self) -> Option<&'static str> {
49 None
50 }
51
52 fn context_query(&self) -> Option<&'static str> {
63 None
64 }
65}
66
67pub struct RegisteredLanguage {
69 pub support: Arc<dyn LanguageSupport>,
71 language: Language,
73}
74
75impl RegisteredLanguage {
76 pub fn new(support: Arc<dyn LanguageSupport>) -> Self {
78 let language = support.tree_sitter_language();
79 Self { support, language }
80 }
81
82 pub fn language_id(&self) -> &'static str {
84 self.support.language_id()
85 }
86
87 pub fn language(&self) -> &Language {
89 &self.language
90 }
91
92 pub fn file_extensions(&self) -> &'static [&'static str] {
94 self.support.file_extensions()
95 }
96
97 pub fn highlights_query(&self) -> &'static str {
99 self.support.highlights_query()
100 }
101
102 pub fn folds_query(&self) -> Option<&'static str> {
104 self.support.folds_query()
105 }
106
107 pub fn textobjects_query(&self) -> Option<&'static str> {
109 self.support.textobjects_query()
110 }
111
112 pub fn decorations_query(&self) -> Option<&'static str> {
114 self.support.decorations_query()
115 }
116
117 pub fn injections_query(&self) -> Option<&'static str> {
119 self.support.injections_query()
120 }
121
122 pub fn context_query(&self) -> Option<&'static str> {
124 self.support.context_query()
125 }
126}
127
128pub struct LanguageRegistry {
133 languages: RwLock<HashMap<String, Arc<RegisteredLanguage>>>,
135 extension_map: RwLock<HashMap<String, String>>,
137}
138
139impl Default for LanguageRegistry {
140 fn default() -> Self {
141 Self::new()
142 }
143}
144
145impl LanguageRegistry {
146 #[must_use]
148 pub fn new() -> Self {
149 Self {
150 languages: RwLock::new(HashMap::new()),
151 extension_map: RwLock::new(HashMap::new()),
152 }
153 }
154
155 pub fn register(&self, support: Arc<dyn LanguageSupport>) {
157 let id = support.language_id().to_string();
158 let extensions = support.file_extensions();
159
160 let registered = Arc::new(RegisteredLanguage::new(support));
161
162 self.languages
164 .write()
165 .unwrap()
166 .insert(id.clone(), registered);
167
168 let mut ext_map = self.extension_map.write().unwrap();
170 for ext in extensions {
171 ext_map.insert((*ext).to_string(), id.clone());
172 }
173
174 tracing::debug!(language_id = %id, extensions = ?extensions, "Registered language");
175 }
176
177 #[must_use]
179 pub fn detect_language(&self, path: &str) -> Option<String> {
180 Path::new(path)
181 .extension()
182 .and_then(|e| e.to_str())
183 .and_then(|ext| self.extension_map.read().unwrap().get(ext).cloned())
184 }
185
186 #[must_use]
188 pub fn get(&self, id: &str) -> Option<Arc<RegisteredLanguage>> {
189 self.languages.read().unwrap().get(id).cloned()
190 }
191
192 #[must_use]
194 pub fn get_language(&self, id: &str) -> Option<Language> {
195 self.languages
196 .read()
197 .unwrap()
198 .get(id)
199 .map(|l| l.language().clone())
200 }
201
202 #[must_use]
204 pub fn is_registered(&self, id: &str) -> bool {
205 self.languages.read().unwrap().contains_key(id)
206 }
207
208 #[must_use]
210 pub fn language_ids(&self) -> Vec<String> {
211 self.languages.read().unwrap().keys().cloned().collect()
212 }
213}