1mod buffer;
2mod diagnostic_set;
3mod highlight_map;
4mod outline;
5pub mod proto;
6#[cfg(test)]
7mod tests;
8
9use anyhow::{anyhow, Context, Result};
10use client::http::HttpClient;
11use collections::HashMap;
12use futures::{
13 future::{BoxFuture, Shared},
14 FutureExt, TryFutureExt,
15};
16use gpui::{MutableAppContext, Task};
17use highlight_map::HighlightMap;
18use lazy_static::lazy_static;
19use parking_lot::{Mutex, RwLock};
20use regex::Regex;
21use serde::{de, Deserialize, Deserializer};
22use serde_json::Value;
23use std::{
24 any::Any,
25 cell::RefCell,
26 mem,
27 ops::Range,
28 path::{Path, PathBuf},
29 str,
30 sync::Arc,
31};
32use theme::SyntaxTheme;
33use tree_sitter::{self, Query};
34use util::ResultExt;
35
36#[cfg(any(test, feature = "test-support"))]
37use futures::channel::mpsc;
38
39pub use buffer::Operation;
40pub use buffer::*;
41pub use diagnostic_set::DiagnosticEntry;
42pub use outline::{Outline, OutlineItem};
43pub use tree_sitter::{Parser, Tree};
44
45thread_local! {
46 static PARSER: RefCell<Parser> = RefCell::new(Parser::new());
47}
48
49lazy_static! {
50 pub static ref PLAIN_TEXT: Arc<Language> = Arc::new(Language::new(
51 LanguageConfig {
52 name: "Plain Text".into(),
53 ..Default::default()
54 },
55 None,
56 ));
57}
58
59pub trait ToLspPosition {
60 fn to_lsp_position(self) -> lsp::Position;
61}
62
63#[derive(Clone, Debug, PartialEq, Eq, Hash)]
64pub struct LanguageServerName(pub Arc<str>);
65
66use async_trait::async_trait;
67
68#[async_trait]
69pub trait LspAdapter: 'static + Send + Sync {
70 async fn name(&self) -> LanguageServerName;
71 async fn fetch_latest_server_version(
72 &self,
73 http: Arc<dyn HttpClient>,
74 ) -> Result<Box<dyn 'static + Send + Any>>;
75 async fn fetch_server_binary(
76 &self,
77 version: Box<dyn 'static + Send + Any>,
78 http: Arc<dyn HttpClient>,
79 container_dir: PathBuf,
80 ) -> Result<PathBuf>;
81 async fn cached_server_binary(&self, container_dir: PathBuf) -> Option<PathBuf>;
82
83 async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
84
85 async fn label_for_completion(
86 &self,
87 _: &lsp::CompletionItem,
88 _: &Language,
89 ) -> Option<CodeLabel> {
90 None
91 }
92
93 async fn label_for_symbol(
94 &self,
95 _: &str,
96 _: lsp::SymbolKind,
97 _: &Language,
98 ) -> Option<CodeLabel> {
99 None
100 }
101
102 async fn server_args(&self) -> Vec<String> {
103 Vec::new()
104 }
105
106 async fn initialization_options(&self) -> Option<Value> {
107 None
108 }
109
110 async fn disk_based_diagnostic_sources(&self) -> Vec<String> {
111 Default::default()
112 }
113
114 async fn disk_based_diagnostics_progress_token(&self) -> Option<String> {
115 None
116 }
117
118 async fn id_for_language(&self, _name: &str) -> Option<String> {
119 None
120 }
121}
122
123#[derive(Clone, Debug, PartialEq, Eq)]
124pub struct CodeLabel {
125 pub text: String,
126 pub runs: Vec<(Range<usize>, HighlightId)>,
127 pub filter_range: Range<usize>,
128}
129
130#[derive(Deserialize)]
131pub struct LanguageConfig {
132 pub name: Arc<str>,
133 pub path_suffixes: Vec<String>,
134 pub brackets: Vec<BracketPair>,
135 #[serde(default = "auto_indent_using_last_non_empty_line_default")]
136 pub auto_indent_using_last_non_empty_line: bool,
137 #[serde(default, deserialize_with = "deserialize_regex")]
138 pub increase_indent_pattern: Option<Regex>,
139 #[serde(default, deserialize_with = "deserialize_regex")]
140 pub decrease_indent_pattern: Option<Regex>,
141 #[serde(default)]
142 pub autoclose_before: String,
143 pub line_comment: Option<String>,
144}
145
146impl Default for LanguageConfig {
147 fn default() -> Self {
148 Self {
149 name: "".into(),
150 path_suffixes: Default::default(),
151 brackets: Default::default(),
152 auto_indent_using_last_non_empty_line: auto_indent_using_last_non_empty_line_default(),
153 increase_indent_pattern: Default::default(),
154 decrease_indent_pattern: Default::default(),
155 autoclose_before: Default::default(),
156 line_comment: Default::default(),
157 }
158 }
159}
160
161fn auto_indent_using_last_non_empty_line_default() -> bool {
162 true
163}
164
165fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D::Error> {
166 let source = Option::<String>::deserialize(d)?;
167 if let Some(source) = source {
168 Ok(Some(regex::Regex::new(&source).map_err(de::Error::custom)?))
169 } else {
170 Ok(None)
171 }
172}
173
174#[cfg(any(test, feature = "test-support"))]
175pub struct FakeLspAdapter {
176 pub name: &'static str,
177 pub capabilities: lsp::ServerCapabilities,
178 pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
179 pub disk_based_diagnostics_progress_token: Option<String>,
180 pub disk_based_diagnostics_sources: Vec<String>,
181}
182
183#[derive(Clone, Debug, Deserialize)]
184pub struct BracketPair {
185 pub start: String,
186 pub end: String,
187 pub close: bool,
188 pub newline: bool,
189}
190
191pub struct Language {
192 pub(crate) config: LanguageConfig,
193 pub(crate) grammar: Option<Arc<Grammar>>,
194 pub(crate) adapter: Option<Arc<dyn LspAdapter>>,
195
196 #[cfg(any(test, feature = "test-support"))]
197 fake_adapter: Option<(
198 mpsc::UnboundedSender<lsp::FakeLanguageServer>,
199 Arc<FakeLspAdapter>,
200 )>,
201}
202
203pub struct Grammar {
204 pub(crate) ts_language: tree_sitter::Language,
205 pub(crate) highlights_query: Option<Query>,
206 pub(crate) brackets_query: Option<Query>,
207 pub(crate) indents_query: Option<Query>,
208 pub(crate) outline_query: Option<Query>,
209 pub(crate) highlight_map: Mutex<HighlightMap>,
210}
211
212#[derive(Clone)]
213pub enum LanguageServerBinaryStatus {
214 CheckingForUpdate,
215 Downloading,
216 Downloaded,
217 Cached,
218 Failed { error: String },
219}
220
221pub struct LanguageRegistry {
222 languages: RwLock<Vec<Arc<Language>>>,
223 language_server_download_dir: Option<Arc<Path>>,
224 lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
225 lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc<Language>, LanguageServerBinaryStatus)>,
226 login_shell_env_loaded: Shared<Task<()>>,
227 lsp_binary_paths: Mutex<
228 HashMap<
229 LanguageServerName,
230 Shared<BoxFuture<'static, Result<PathBuf, Arc<anyhow::Error>>>>,
231 >,
232 >,
233}
234
235impl LanguageRegistry {
236 pub fn new(login_shell_env_loaded: Task<()>) -> Self {
237 let (lsp_binary_statuses_tx, lsp_binary_statuses_rx) = async_broadcast::broadcast(16);
238 Self {
239 language_server_download_dir: None,
240 languages: Default::default(),
241 lsp_binary_statuses_tx,
242 lsp_binary_statuses_rx,
243 login_shell_env_loaded: login_shell_env_loaded.shared(),
244 lsp_binary_paths: Default::default(),
245 }
246 }
247
248 #[cfg(any(test, feature = "test-support"))]
249 pub fn test() -> Self {
250 Self::new(Task::ready(()))
251 }
252
253 pub fn add(&self, language: Arc<Language>) {
254 self.languages.write().push(language.clone());
255 }
256
257 pub fn set_theme(&self, theme: &SyntaxTheme) {
258 for language in self.languages.read().iter() {
259 language.set_theme(theme);
260 }
261 }
262
263 pub fn set_language_server_download_dir(&mut self, path: impl Into<Arc<Path>>) {
264 self.language_server_download_dir = Some(path.into());
265 }
266
267 pub fn get_language(&self, name: &str) -> Option<Arc<Language>> {
268 self.languages
269 .read()
270 .iter()
271 .find(|language| language.name().to_lowercase() == name.to_lowercase())
272 .cloned()
273 }
274
275 pub fn to_vec(&self) -> Vec<Arc<Language>> {
276 self.languages.read().iter().cloned().collect()
277 }
278
279 pub fn language_names(&self) -> Vec<String> {
280 self.languages
281 .read()
282 .iter()
283 .map(|language| language.name().to_string())
284 .collect()
285 }
286
287 pub fn select_language(&self, path: impl AsRef<Path>) -> Option<Arc<Language>> {
288 let path = path.as_ref();
289 let filename = path.file_name().and_then(|name| name.to_str());
290 let extension = path.extension().and_then(|name| name.to_str());
291 let path_suffixes = [extension, filename];
292 self.languages
293 .read()
294 .iter()
295 .find(|language| {
296 language
297 .config
298 .path_suffixes
299 .iter()
300 .any(|suffix| dbg!(path_suffixes.contains(&Some(dbg!(suffix.as_str())))))
301 })
302 .cloned()
303 }
304
305 pub fn start_language_server(
306 self: &Arc<Self>,
307 server_id: usize,
308 language: Arc<Language>,
309 root_path: Arc<Path>,
310 http_client: Arc<dyn HttpClient>,
311 cx: &mut MutableAppContext,
312 ) -> Option<Task<Result<lsp::LanguageServer>>> {
313 #[cfg(any(test, feature = "test-support"))]
314 if language.fake_adapter.is_some() {
315 let language = language.clone();
316 return Some(cx.spawn(|cx| async move {
317 let (servers_tx, fake_adapter) = language.fake_adapter.as_ref().unwrap();
318 let (server, mut fake_server) = lsp::LanguageServer::fake(
319 fake_adapter.name.to_string(),
320 fake_adapter.capabilities.clone(),
321 cx.clone(),
322 );
323
324 if let Some(initializer) = &fake_adapter.initializer {
325 initializer(&mut fake_server);
326 }
327
328 let servers_tx = servers_tx.clone();
329 cx.background()
330 .spawn(async move {
331 if fake_server
332 .try_receive_notification::<lsp::notification::Initialized>()
333 .await
334 .is_some()
335 {
336 servers_tx.unbounded_send(fake_server).ok();
337 }
338 })
339 .detach();
340 Ok(server)
341 }));
342 }
343
344 let download_dir = self
345 .language_server_download_dir
346 .clone()
347 .ok_or_else(|| anyhow!("language server download directory has not been assigned"))
348 .log_err()?;
349
350 let this = self.clone();
351 let adapter = language.adapter.clone()?;
352 let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone();
353 let login_shell_env_loaded = self.login_shell_env_loaded.clone();
354 Some(cx.spawn(|cx| async move {
355 login_shell_env_loaded.await;
356 let server_binary_path = this
357 .lsp_binary_paths
358 .lock()
359 .entry(adapter.name().await)
360 .or_insert_with(|| {
361 get_server_binary_path(
362 adapter.clone(),
363 language.clone(),
364 http_client,
365 download_dir,
366 lsp_binary_statuses,
367 )
368 .map_err(Arc::new)
369 .boxed()
370 .shared()
371 })
372 .clone()
373 .map_err(|e| anyhow!(e));
374
375 let server_binary_path = server_binary_path.await?;
376 let server_args = adapter.server_args().await;
377 let server = lsp::LanguageServer::new(
378 server_id,
379 &server_binary_path,
380 &server_args,
381 &root_path,
382 cx,
383 )?;
384 Ok(server)
385 }))
386 }
387
388 pub fn language_server_binary_statuses(
389 &self,
390 ) -> async_broadcast::Receiver<(Arc<Language>, LanguageServerBinaryStatus)> {
391 self.lsp_binary_statuses_rx.clone()
392 }
393}
394
395async fn get_server_binary_path(
396 adapter: Arc<dyn LspAdapter>,
397 language: Arc<Language>,
398 http_client: Arc<dyn HttpClient>,
399 download_dir: Arc<Path>,
400 statuses: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
401) -> Result<PathBuf> {
402 let container_dir = download_dir.join(adapter.name().await.0.as_ref());
403 if !container_dir.exists() {
404 smol::fs::create_dir_all(&container_dir)
405 .await
406 .context("failed to create container directory")?;
407 }
408
409 let path = fetch_latest_server_binary_path(
410 adapter.clone(),
411 language.clone(),
412 http_client,
413 &container_dir,
414 statuses.clone(),
415 )
416 .await;
417 if let Err(error) = path.as_ref() {
418 if let Some(cached_path) = adapter.cached_server_binary(container_dir).await {
419 statuses
420 .broadcast((language.clone(), LanguageServerBinaryStatus::Cached))
421 .await?;
422 return Ok(cached_path);
423 } else {
424 statuses
425 .broadcast((
426 language.clone(),
427 LanguageServerBinaryStatus::Failed {
428 error: format!("{:?}", error),
429 },
430 ))
431 .await?;
432 }
433 }
434 path
435}
436
437async fn fetch_latest_server_binary_path(
438 adapter: Arc<dyn LspAdapter>,
439 language: Arc<Language>,
440 http_client: Arc<dyn HttpClient>,
441 container_dir: &Path,
442 lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
443) -> Result<PathBuf> {
444 let container_dir: Arc<Path> = container_dir.into();
445 lsp_binary_statuses_tx
446 .broadcast((
447 language.clone(),
448 LanguageServerBinaryStatus::CheckingForUpdate,
449 ))
450 .await?;
451 let version_info = adapter
452 .fetch_latest_server_version(http_client.clone())
453 .await?;
454 lsp_binary_statuses_tx
455 .broadcast((language.clone(), LanguageServerBinaryStatus::Downloading))
456 .await?;
457 let path = adapter
458 .fetch_server_binary(version_info, http_client, container_dir.clone())
459 .await?;
460 lsp_binary_statuses_tx
461 .broadcast((language.clone(), LanguageServerBinaryStatus::Downloaded))
462 .await?;
463 Ok(path)
464}
465
466impl Language {
467 pub fn new(config: LanguageConfig, ts_language: Option<tree_sitter::Language>) -> Self {
468 Self {
469 config,
470 grammar: ts_language.map(|ts_language| {
471 Arc::new(Grammar {
472 highlights_query: None,
473 brackets_query: None,
474 indents_query: None,
475 outline_query: None,
476 ts_language,
477 highlight_map: Default::default(),
478 })
479 }),
480 adapter: None,
481
482 #[cfg(any(test, feature = "test-support"))]
483 fake_adapter: None,
484 }
485 }
486
487 pub fn lsp_adapter(&self) -> Option<Arc<dyn LspAdapter>> {
488 self.adapter.clone()
489 }
490
491 pub fn with_highlights_query(mut self, source: &str) -> Result<Self> {
492 let grammar = self.grammar_mut();
493 grammar.highlights_query = Some(Query::new(grammar.ts_language, source)?);
494 Ok(self)
495 }
496
497 pub fn with_brackets_query(mut self, source: &str) -> Result<Self> {
498 let grammar = self.grammar_mut();
499 grammar.brackets_query = Some(Query::new(grammar.ts_language, source)?);
500 Ok(self)
501 }
502
503 pub fn with_indents_query(mut self, source: &str) -> Result<Self> {
504 let grammar = self.grammar_mut();
505 grammar.indents_query = Some(Query::new(grammar.ts_language, source)?);
506 Ok(self)
507 }
508
509 pub fn with_outline_query(mut self, source: &str) -> Result<Self> {
510 let grammar = self.grammar_mut();
511 grammar.outline_query = Some(Query::new(grammar.ts_language, source)?);
512 Ok(self)
513 }
514
515 fn grammar_mut(&mut self) -> &mut Grammar {
516 Arc::get_mut(self.grammar.as_mut().unwrap()).unwrap()
517 }
518
519 pub fn with_lsp_adapter(mut self, lsp_adapter: Arc<dyn LspAdapter>) -> Self {
520 self.adapter = Some(lsp_adapter);
521 self
522 }
523
524 #[cfg(any(test, feature = "test-support"))]
525 pub fn set_fake_lsp_adapter(
526 &mut self,
527 fake_lsp_adapter: FakeLspAdapter,
528 ) -> mpsc::UnboundedReceiver<lsp::FakeLanguageServer> {
529 let (servers_tx, servers_rx) = mpsc::unbounded();
530 let adapter = Arc::new(fake_lsp_adapter);
531 self.fake_adapter = Some((servers_tx, adapter.clone()));
532 self.adapter = Some(adapter);
533 servers_rx
534 }
535
536 pub fn name(&self) -> Arc<str> {
537 self.config.name.clone()
538 }
539
540 pub fn line_comment_prefix(&self) -> Option<&str> {
541 self.config.line_comment.as_deref()
542 }
543
544 pub async fn disk_based_diagnostic_sources(&self) -> Vec<String> {
545 match self.adapter.as_ref() {
546 Some(adapter) => adapter.disk_based_diagnostic_sources().await,
547 None => Vec::new(),
548 }
549 }
550
551 pub async fn disk_based_diagnostics_progress_token(&self) -> Option<String> {
552 if let Some(adapter) = self.adapter.as_ref() {
553 adapter.disk_based_diagnostics_progress_token().await
554 } else {
555 None
556 }
557 }
558
559 pub async fn process_diagnostics(&self, diagnostics: &mut lsp::PublishDiagnosticsParams) {
560 if let Some(processor) = self.adapter.as_ref() {
561 processor.process_diagnostics(diagnostics).await;
562 }
563 }
564
565 pub async fn label_for_completion(
566 &self,
567 completion: &lsp::CompletionItem,
568 ) -> Option<CodeLabel> {
569 self.adapter
570 .as_ref()?
571 .label_for_completion(completion, self)
572 .await
573 }
574
575 pub async fn label_for_symbol(&self, name: &str, kind: lsp::SymbolKind) -> Option<CodeLabel> {
576 self.adapter
577 .as_ref()?
578 .label_for_symbol(name, kind, self)
579 .await
580 }
581
582 pub fn highlight_text<'a>(
583 &'a self,
584 text: &'a Rope,
585 range: Range<usize>,
586 ) -> Vec<(Range<usize>, HighlightId)> {
587 let mut result = Vec::new();
588 if let Some(grammar) = &self.grammar {
589 let tree = grammar.parse_text(text, None);
590 let mut offset = 0;
591 for chunk in BufferChunks::new(text, range, Some(&tree), self.grammar.as_ref(), vec![])
592 {
593 let end_offset = offset + chunk.text.len();
594 if let Some(highlight_id) = chunk.syntax_highlight_id {
595 if !highlight_id.is_default() {
596 result.push((offset..end_offset, highlight_id));
597 }
598 }
599 offset = end_offset;
600 }
601 }
602 result
603 }
604
605 pub fn brackets(&self) -> &[BracketPair] {
606 &self.config.brackets
607 }
608
609 pub fn path_suffixes(&self) -> &[String] {
610 &self.config.path_suffixes
611 }
612
613 pub fn should_autoclose_before(&self, c: char) -> bool {
614 c.is_whitespace() || self.config.autoclose_before.contains(c)
615 }
616
617 pub fn set_theme(&self, theme: &SyntaxTheme) {
618 if let Some(grammar) = self.grammar.as_ref() {
619 if let Some(highlights_query) = &grammar.highlights_query {
620 *grammar.highlight_map.lock() =
621 HighlightMap::new(highlights_query.capture_names(), theme);
622 }
623 }
624 }
625
626 pub fn grammar(&self) -> Option<&Arc<Grammar>> {
627 self.grammar.as_ref()
628 }
629}
630
631impl Grammar {
632 fn parse_text(&self, text: &Rope, old_tree: Option<Tree>) -> Tree {
633 PARSER.with(|parser| {
634 let mut parser = parser.borrow_mut();
635 parser
636 .set_language(self.ts_language)
637 .expect("incompatible grammar");
638 let mut chunks = text.chunks_in_range(0..text.len());
639 parser
640 .parse_with(
641 &mut move |offset, _| {
642 chunks.seek(offset);
643 chunks.next().unwrap_or("").as_bytes()
644 },
645 old_tree.as_ref(),
646 )
647 .unwrap()
648 })
649 }
650
651 pub fn highlight_map(&self) -> HighlightMap {
652 self.highlight_map.lock().clone()
653 }
654
655 pub fn highlight_id_for_name(&self, name: &str) -> Option<HighlightId> {
656 let capture_id = self
657 .highlights_query
658 .as_ref()?
659 .capture_index_for_name(name)?;
660 Some(self.highlight_map.lock().get(capture_id))
661 }
662}
663
664impl CodeLabel {
665 pub fn plain(text: String, filter_text: Option<&str>) -> Self {
666 let mut result = Self {
667 runs: Vec::new(),
668 filter_range: 0..text.len(),
669 text,
670 };
671 if let Some(filter_text) = filter_text {
672 if let Some(ix) = result.text.find(filter_text) {
673 result.filter_range = ix..ix + filter_text.len();
674 }
675 }
676 result
677 }
678}
679
680#[cfg(any(test, feature = "test-support"))]
681impl Default for FakeLspAdapter {
682 fn default() -> Self {
683 Self {
684 name: "the-fake-language-server",
685 capabilities: lsp::LanguageServer::full_capabilities(),
686 initializer: None,
687 disk_based_diagnostics_progress_token: None,
688 disk_based_diagnostics_sources: Vec::new(),
689 }
690 }
691}
692
693#[cfg(any(test, feature = "test-support"))]
694#[async_trait]
695impl LspAdapter for FakeLspAdapter {
696 async fn name(&self) -> LanguageServerName {
697 LanguageServerName(self.name.into())
698 }
699
700 async fn fetch_latest_server_version(
701 &self,
702 _: Arc<dyn HttpClient>,
703 ) -> Result<Box<dyn 'static + Send + Any>> {
704 unreachable!();
705 }
706
707 async fn fetch_server_binary(
708 &self,
709 _: Box<dyn 'static + Send + Any>,
710 _: Arc<dyn HttpClient>,
711 _: PathBuf,
712 ) -> Result<PathBuf> {
713 unreachable!();
714 }
715
716 async fn cached_server_binary(&self, _: PathBuf) -> Option<PathBuf> {
717 unreachable!();
718 }
719
720 async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
721
722 async fn disk_based_diagnostic_sources(&self) -> Vec<String> {
723 self.disk_based_diagnostics_sources.clone()
724 }
725
726 async fn disk_based_diagnostics_progress_token(&self) -> Option<String> {
727 self.disk_based_diagnostics_progress_token.clone()
728 }
729}
730
731pub fn point_to_lsp(point: PointUtf16) -> lsp::Position {
732 lsp::Position::new(point.row, point.column)
733}
734
735pub fn point_from_lsp(point: lsp::Position) -> PointUtf16 {
736 PointUtf16::new(point.line, point.character)
737}
738
739pub fn range_to_lsp(range: Range<PointUtf16>) -> lsp::Range {
740 lsp::Range {
741 start: point_to_lsp(range.start),
742 end: point_to_lsp(range.end),
743 }
744}
745
746pub fn range_from_lsp(range: lsp::Range) -> Range<PointUtf16> {
747 let mut start = point_from_lsp(range.start);
748 let mut end = point_from_lsp(range.end);
749 if start > end {
750 mem::swap(&mut start, &mut end);
751 }
752 start..end
753}