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
53pub struct RegisteredLanguage {
55 pub support: Arc<dyn LanguageSupport>,
57 language: Language,
59}
60
61impl RegisteredLanguage {
62 pub fn new(support: Arc<dyn LanguageSupport>) -> Self {
64 let language = support.tree_sitter_language();
65 Self { support, language }
66 }
67
68 pub fn language_id(&self) -> &'static str {
70 self.support.language_id()
71 }
72
73 pub fn language(&self) -> &Language {
75 &self.language
76 }
77
78 pub fn file_extensions(&self) -> &'static [&'static str] {
80 self.support.file_extensions()
81 }
82
83 pub fn highlights_query(&self) -> &'static str {
85 self.support.highlights_query()
86 }
87
88 pub fn folds_query(&self) -> Option<&'static str> {
90 self.support.folds_query()
91 }
92
93 pub fn textobjects_query(&self) -> Option<&'static str> {
95 self.support.textobjects_query()
96 }
97
98 pub fn decorations_query(&self) -> Option<&'static str> {
100 self.support.decorations_query()
101 }
102
103 pub fn injections_query(&self) -> Option<&'static str> {
105 self.support.injections_query()
106 }
107}
108
109pub struct LanguageRegistry {
114 languages: RwLock<HashMap<String, Arc<RegisteredLanguage>>>,
116 extension_map: RwLock<HashMap<String, String>>,
118}
119
120impl Default for LanguageRegistry {
121 fn default() -> Self {
122 Self::new()
123 }
124}
125
126impl LanguageRegistry {
127 #[must_use]
129 pub fn new() -> Self {
130 Self {
131 languages: RwLock::new(HashMap::new()),
132 extension_map: RwLock::new(HashMap::new()),
133 }
134 }
135
136 pub fn register(&self, support: Arc<dyn LanguageSupport>) {
138 let id = support.language_id().to_string();
139 let extensions = support.file_extensions();
140
141 let registered = Arc::new(RegisteredLanguage::new(support));
142
143 self.languages
145 .write()
146 .unwrap()
147 .insert(id.clone(), registered);
148
149 let mut ext_map = self.extension_map.write().unwrap();
151 for ext in extensions {
152 ext_map.insert((*ext).to_string(), id.clone());
153 }
154
155 tracing::debug!(language_id = %id, extensions = ?extensions, "Registered language");
156 }
157
158 #[must_use]
160 pub fn detect_language(&self, path: &str) -> Option<String> {
161 Path::new(path)
162 .extension()
163 .and_then(|e| e.to_str())
164 .and_then(|ext| self.extension_map.read().unwrap().get(ext).cloned())
165 }
166
167 #[must_use]
169 pub fn get(&self, id: &str) -> Option<Arc<RegisteredLanguage>> {
170 self.languages.read().unwrap().get(id).cloned()
171 }
172
173 #[must_use]
175 pub fn get_language(&self, id: &str) -> Option<Language> {
176 self.languages
177 .read()
178 .unwrap()
179 .get(id)
180 .map(|l| l.language().clone())
181 }
182
183 #[must_use]
185 pub fn is_registered(&self, id: &str) -> bool {
186 self.languages.read().unwrap().contains_key(id)
187 }
188
189 #[must_use]
191 pub fn language_ids(&self) -> Vec<String> {
192 self.languages.read().unwrap().keys().cloned().collect()
193 }
194}