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