1mod buffer;
2mod highlight_map;
3pub mod proto;
4#[cfg(test)]
5mod tests;
6
7use anyhow::{anyhow, Result};
8pub use buffer::Operation;
9pub use buffer::*;
10use gpui::{executor::Background, AppContext};
11use highlight_map::HighlightMap;
12use lazy_static::lazy_static;
13use lsp::LanguageServer;
14use parking_lot::Mutex;
15use serde::Deserialize;
16use std::{collections::HashSet, path::Path, str, sync::Arc};
17use theme::SyntaxTheme;
18use tree_sitter::{self, Query};
19pub use tree_sitter::{Parser, Tree};
20
21lazy_static! {
22 pub static ref PLAIN_TEXT: Arc<Language> = Arc::new(Language::new(
23 LanguageConfig {
24 name: "Plain Text".to_string(),
25 path_suffixes: Default::default(),
26 brackets: Default::default(),
27 line_comment: None,
28 language_server: None,
29 },
30 None,
31 ));
32}
33
34#[derive(Default, Deserialize)]
35pub struct LanguageConfig {
36 pub name: String,
37 pub path_suffixes: Vec<String>,
38 pub brackets: Vec<BracketPair>,
39 pub line_comment: Option<String>,
40 pub language_server: Option<LanguageServerConfig>,
41}
42
43#[derive(Default, Deserialize)]
44pub struct LanguageServerConfig {
45 pub binary: String,
46 pub disk_based_diagnostic_sources: HashSet<String>,
47 #[cfg(any(test, feature = "test-support"))]
48 #[serde(skip)]
49 pub fake_server: Option<(Arc<LanguageServer>, Arc<std::sync::atomic::AtomicBool>)>,
50}
51
52#[derive(Clone, Debug, Deserialize)]
53pub struct BracketPair {
54 pub start: String,
55 pub end: String,
56 pub close: bool,
57 pub newline: bool,
58}
59
60pub struct Language {
61 pub(crate) config: LanguageConfig,
62 pub(crate) grammar: Option<Arc<Grammar>>,
63}
64
65pub struct Grammar {
66 pub(crate) ts_language: tree_sitter::Language,
67 pub(crate) highlights_query: Query,
68 pub(crate) brackets_query: Query,
69 pub(crate) indents_query: Query,
70 pub(crate) highlight_map: Mutex<HighlightMap>,
71}
72
73#[derive(Default)]
74pub struct LanguageRegistry {
75 languages: Vec<Arc<Language>>,
76}
77
78impl LanguageRegistry {
79 pub fn new() -> Self {
80 Self::default()
81 }
82
83 pub fn add(&mut self, language: Arc<Language>) {
84 self.languages.push(language);
85 }
86
87 pub fn set_theme(&self, theme: &SyntaxTheme) {
88 for language in &self.languages {
89 language.set_theme(theme);
90 }
91 }
92
93 pub fn get_language(&self, name: &str) -> Option<&Arc<Language>> {
94 self.languages
95 .iter()
96 .find(|language| language.name() == name)
97 }
98
99 pub fn select_language(&self, path: impl AsRef<Path>) -> Option<&Arc<Language>> {
100 let path = path.as_ref();
101 let filename = path.file_name().and_then(|name| name.to_str());
102 let extension = path.extension().and_then(|name| name.to_str());
103 let path_suffixes = [extension, filename];
104 self.languages.iter().find(|language| {
105 language
106 .config
107 .path_suffixes
108 .iter()
109 .any(|suffix| path_suffixes.contains(&Some(suffix.as_str())))
110 })
111 }
112}
113
114impl Language {
115 pub fn new(config: LanguageConfig, ts_language: Option<tree_sitter::Language>) -> Self {
116 Self {
117 config,
118 grammar: ts_language.map(|ts_language| {
119 Arc::new(Grammar {
120 brackets_query: Query::new(ts_language, "").unwrap(),
121 highlights_query: Query::new(ts_language, "").unwrap(),
122 indents_query: Query::new(ts_language, "").unwrap(),
123 ts_language,
124 highlight_map: Default::default(),
125 })
126 }),
127 }
128 }
129
130 pub fn with_highlights_query(mut self, source: &str) -> Result<Self> {
131 let grammar = self
132 .grammar
133 .as_mut()
134 .and_then(Arc::get_mut)
135 .ok_or_else(|| anyhow!("grammar does not exist or is already being used"))?;
136 grammar.highlights_query = Query::new(grammar.ts_language, source)?;
137 Ok(self)
138 }
139
140 pub fn with_brackets_query(mut self, source: &str) -> Result<Self> {
141 let grammar = self
142 .grammar
143 .as_mut()
144 .and_then(Arc::get_mut)
145 .ok_or_else(|| anyhow!("grammar does not exist or is already being used"))?;
146 grammar.brackets_query = Query::new(grammar.ts_language, source)?;
147 Ok(self)
148 }
149
150 pub fn with_indents_query(mut self, source: &str) -> Result<Self> {
151 let grammar = self
152 .grammar
153 .as_mut()
154 .and_then(Arc::get_mut)
155 .ok_or_else(|| anyhow!("grammar does not exist or is already being used"))?;
156 grammar.indents_query = Query::new(grammar.ts_language, source)?;
157 Ok(self)
158 }
159
160 pub fn name(&self) -> &str {
161 self.config.name.as_str()
162 }
163
164 pub fn line_comment_prefix(&self) -> Option<&str> {
165 self.config.line_comment.as_deref()
166 }
167
168 pub fn start_server(
169 &self,
170 root_path: &Path,
171 cx: &AppContext,
172 ) -> Result<Option<Arc<lsp::LanguageServer>>> {
173 if let Some(config) = &self.config.language_server {
174 #[cfg(any(test, feature = "test-support"))]
175 if let Some((server, started)) = &config.fake_server {
176 started.store(true, std::sync::atomic::Ordering::SeqCst);
177 return Ok(Some(server.clone()));
178 }
179
180 const ZED_BUNDLE: Option<&'static str> = option_env!("ZED_BUNDLE");
181 let binary_path = if ZED_BUNDLE.map_or(Ok(false), |b| b.parse())? {
182 cx.platform()
183 .path_for_resource(Some(&config.binary), None)?
184 } else {
185 Path::new(&config.binary).to_path_buf()
186 };
187 lsp::LanguageServer::new(&binary_path, root_path, cx.background().clone()).map(Some)
188 } else {
189 Ok(None)
190 }
191 }
192
193 pub fn disk_based_diagnostic_sources(&self) -> Option<&HashSet<String>> {
194 self.config
195 .language_server
196 .as_ref()
197 .map(|config| &config.disk_based_diagnostic_sources)
198 }
199
200 pub fn brackets(&self) -> &[BracketPair] {
201 &self.config.brackets
202 }
203
204 pub fn set_theme(&self, theme: &SyntaxTheme) {
205 if let Some(grammar) = self.grammar.as_ref() {
206 *grammar.highlight_map.lock() =
207 HighlightMap::new(grammar.highlights_query.capture_names(), theme);
208 }
209 }
210}
211
212impl Grammar {
213 pub fn highlight_map(&self) -> HighlightMap {
214 self.highlight_map.lock().clone()
215 }
216}
217
218#[cfg(any(test, feature = "test-support"))]
219impl LanguageServerConfig {
220 pub async fn fake(executor: Arc<Background>) -> (Self, lsp::FakeLanguageServer) {
221 let (server, fake) = lsp::LanguageServer::fake(executor).await;
222 fake.started
223 .store(false, std::sync::atomic::Ordering::SeqCst);
224 let started = fake.started.clone();
225 (
226 Self {
227 fake_server: Some((server, started)),
228 ..Default::default()
229 },
230 fake,
231 )
232 }
233}