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