1//! The `language` crate provides a large chunk of Zed's language-related
2//! features (the other big contributors being project and lsp crates that revolve around LSP features).
3//! Namely, this crate:
4//! - Provides [`Language`], [`Grammar`] and [`LanguageRegistry`] types that
5//! use Tree-sitter to provide syntax highlighting to the editor; note though that `language` doesn't perform the highlighting by itself. It only maps ranges in a buffer to colors. Treesitter is also used for buffer outlines (lists of symbols in a buffer)
6//! - Exposes [`LanguageConfig`] that describes how constructs (like brackets or line comments) should be handled by the editor for a source file of a particular language.
7//!
8//! Notably we do *not* assign a single language to a single file; in real world a single file can consist of multiple programming languages - HTML is a good example of that - and `language` crate tends to reflect that status quo in its API.
9mod buffer;
10mod diagnostic_set;
11mod highlight_map;
12mod language_registry;
13pub mod language_settings;
14mod manifest;
15mod outline;
16pub mod proto;
17mod syntax_map;
18mod task_context;
19mod text_diff;
20mod toolchain;
21
22#[cfg(test)]
23pub mod buffer_tests;
24
25use crate::language_settings::SoftWrap;
26pub use crate::language_settings::{EditPredictionsMode, IndentGuideSettings};
27use anyhow::{Context as _, Result};
28use async_trait::async_trait;
29use collections::{HashMap, HashSet, IndexSet};
30use futures::Future;
31use futures::future::LocalBoxFuture;
32use futures::lock::OwnedMutexGuard;
33use gpui::{App, AsyncApp, Entity, SharedString};
34pub use highlight_map::HighlightMap;
35use http_client::HttpClient;
36pub use language_registry::{
37 LanguageName, LanguageServerStatusUpdate, LoadedLanguage, ServerHealth,
38};
39use lsp::{
40 CodeActionKind, InitializeParams, LanguageServerBinary, LanguageServerBinaryOptions, Uri,
41};
42pub use manifest::{ManifestDelegate, ManifestName, ManifestProvider, ManifestQuery};
43use parking_lot::Mutex;
44use regex::Regex;
45use schemars::{JsonSchema, SchemaGenerator, json_schema};
46use semver::Version;
47use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
48use serde_json::Value;
49use settings::WorktreeId;
50use smol::future::FutureExt as _;
51use std::num::NonZeroU32;
52use std::{
53 ffi::OsStr,
54 fmt::Debug,
55 hash::Hash,
56 mem,
57 ops::{DerefMut, Range},
58 path::{Path, PathBuf},
59 str,
60 sync::{
61 Arc, LazyLock,
62 atomic::{AtomicUsize, Ordering::SeqCst},
63 },
64};
65use syntax_map::{QueryCursorHandle, SyntaxSnapshot};
66use task::RunnableTag;
67pub use task_context::{ContextLocation, ContextProvider, RunnableRange};
68pub use text_diff::{
69 DiffOptions, apply_diff_patch, line_diff, text_diff, text_diff_with_options, unified_diff,
70 word_diff_ranges,
71};
72use theme::SyntaxTheme;
73pub use toolchain::{
74 LanguageToolchainStore, LocalLanguageToolchainStore, Toolchain, ToolchainList, ToolchainLister,
75 ToolchainMetadata, ToolchainScope,
76};
77use tree_sitter::{self, Query, QueryCursor, WasmStore, wasmtime};
78use util::rel_path::RelPath;
79use util::serde::default_true;
80
81pub use buffer::Operation;
82pub use buffer::*;
83pub use diagnostic_set::{DiagnosticEntry, DiagnosticEntryRef, DiagnosticGroup};
84pub use language_registry::{
85 AvailableLanguage, BinaryStatus, LanguageNotFound, LanguageQueries, LanguageRegistry,
86 QUERY_FILENAME_PREFIXES,
87};
88pub use lsp::{LanguageServerId, LanguageServerName};
89pub use outline::*;
90pub use syntax_map::{
91 OwnedSyntaxLayer, SyntaxLayer, SyntaxMapMatches, ToTreeSitterPoint, TreeSitterOptions,
92};
93pub use text::{AnchorRangeExt, LineEnding};
94pub use tree_sitter::{Node, Parser, Tree, TreeCursor};
95
96static QUERY_CURSORS: Mutex<Vec<QueryCursor>> = Mutex::new(vec![]);
97static PARSERS: Mutex<Vec<Parser>> = Mutex::new(vec![]);
98
99pub fn with_parser<F, R>(func: F) -> R
100where
101 F: FnOnce(&mut Parser) -> R,
102{
103 let mut parser = PARSERS.lock().pop().unwrap_or_else(|| {
104 let mut parser = Parser::new();
105 parser
106 .set_wasm_store(WasmStore::new(&WASM_ENGINE).unwrap())
107 .unwrap();
108 parser
109 });
110 parser.set_included_ranges(&[]).unwrap();
111 let result = func(&mut parser);
112 PARSERS.lock().push(parser);
113 result
114}
115
116pub fn with_query_cursor<F, R>(func: F) -> R
117where
118 F: FnOnce(&mut QueryCursor) -> R,
119{
120 let mut cursor = QueryCursorHandle::new();
121 func(cursor.deref_mut())
122}
123
124static NEXT_LANGUAGE_ID: AtomicUsize = AtomicUsize::new(0);
125static NEXT_GRAMMAR_ID: AtomicUsize = AtomicUsize::new(0);
126static WASM_ENGINE: LazyLock<wasmtime::Engine> = LazyLock::new(|| {
127 wasmtime::Engine::new(&wasmtime::Config::new()).expect("Failed to create Wasmtime engine")
128});
129
130/// A shared grammar for plain text, exposed for reuse by downstream crates.
131pub static PLAIN_TEXT: LazyLock<Arc<Language>> = LazyLock::new(|| {
132 Arc::new(Language::new(
133 LanguageConfig {
134 name: "Plain Text".into(),
135 soft_wrap: Some(SoftWrap::EditorWidth),
136 matcher: LanguageMatcher {
137 path_suffixes: vec!["txt".to_owned()],
138 first_line_pattern: None,
139 },
140 brackets: BracketPairConfig {
141 pairs: vec![
142 BracketPair {
143 start: "(".to_string(),
144 end: ")".to_string(),
145 close: true,
146 surround: true,
147 newline: false,
148 },
149 BracketPair {
150 start: "[".to_string(),
151 end: "]".to_string(),
152 close: true,
153 surround: true,
154 newline: false,
155 },
156 BracketPair {
157 start: "{".to_string(),
158 end: "}".to_string(),
159 close: true,
160 surround: true,
161 newline: false,
162 },
163 BracketPair {
164 start: "\"".to_string(),
165 end: "\"".to_string(),
166 close: true,
167 surround: true,
168 newline: false,
169 },
170 BracketPair {
171 start: "'".to_string(),
172 end: "'".to_string(),
173 close: true,
174 surround: true,
175 newline: false,
176 },
177 ],
178 disabled_scopes_by_bracket_ix: Default::default(),
179 },
180 ..Default::default()
181 },
182 None,
183 ))
184});
185
186/// Types that represent a position in a buffer, and can be converted into
187/// an LSP position, to send to a language server.
188pub trait ToLspPosition {
189 /// Converts the value into an LSP position.
190 fn to_lsp_position(self) -> lsp::Position;
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, Hash)]
194pub struct Location {
195 pub buffer: Entity<Buffer>,
196 pub range: Range<Anchor>,
197}
198
199type ServerBinaryCache = futures::lock::Mutex<Option<(bool, LanguageServerBinary)>>;
200type DownloadableLanguageServerBinary = LocalBoxFuture<'static, Result<LanguageServerBinary>>;
201pub type LanguageServerBinaryLocations = LocalBoxFuture<
202 'static,
203 (
204 Result<LanguageServerBinary>,
205 Option<DownloadableLanguageServerBinary>,
206 ),
207>;
208/// Represents a Language Server, with certain cached sync properties.
209/// Uses [`LspAdapter`] under the hood, but calls all 'static' methods
210/// once at startup, and caches the results.
211pub struct CachedLspAdapter {
212 pub name: LanguageServerName,
213 pub disk_based_diagnostic_sources: Vec<String>,
214 pub disk_based_diagnostics_progress_token: Option<String>,
215 language_ids: HashMap<LanguageName, String>,
216 pub adapter: Arc<dyn LspAdapter>,
217 cached_binary: Arc<ServerBinaryCache>,
218}
219
220impl Debug for CachedLspAdapter {
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 f.debug_struct("CachedLspAdapter")
223 .field("name", &self.name)
224 .field(
225 "disk_based_diagnostic_sources",
226 &self.disk_based_diagnostic_sources,
227 )
228 .field(
229 "disk_based_diagnostics_progress_token",
230 &self.disk_based_diagnostics_progress_token,
231 )
232 .field("language_ids", &self.language_ids)
233 .finish_non_exhaustive()
234 }
235}
236
237impl CachedLspAdapter {
238 pub fn new(adapter: Arc<dyn LspAdapter>) -> Arc<Self> {
239 let name = adapter.name();
240 let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources();
241 let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token();
242 let language_ids = adapter.language_ids();
243
244 Arc::new(CachedLspAdapter {
245 name,
246 disk_based_diagnostic_sources,
247 disk_based_diagnostics_progress_token,
248 language_ids,
249 adapter,
250 cached_binary: Default::default(),
251 })
252 }
253
254 pub fn name(&self) -> LanguageServerName {
255 self.adapter.name()
256 }
257
258 pub async fn get_language_server_command(
259 self: Arc<Self>,
260 delegate: Arc<dyn LspAdapterDelegate>,
261 toolchains: Option<Toolchain>,
262 binary_options: LanguageServerBinaryOptions,
263 cx: &mut AsyncApp,
264 ) -> LanguageServerBinaryLocations {
265 let cached_binary = self.cached_binary.clone().lock_owned().await;
266 self.adapter.clone().get_language_server_command(
267 delegate,
268 toolchains,
269 binary_options,
270 cached_binary,
271 cx.clone(),
272 )
273 }
274
275 pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
276 self.adapter.code_action_kinds()
277 }
278
279 pub fn process_diagnostics(
280 &self,
281 params: &mut lsp::PublishDiagnosticsParams,
282 server_id: LanguageServerId,
283 existing_diagnostics: Option<&'_ Buffer>,
284 ) {
285 self.adapter
286 .process_diagnostics(params, server_id, existing_diagnostics)
287 }
288
289 pub fn retain_old_diagnostic(&self, previous_diagnostic: &Diagnostic, cx: &App) -> bool {
290 self.adapter.retain_old_diagnostic(previous_diagnostic, cx)
291 }
292
293 pub fn underline_diagnostic(&self, diagnostic: &lsp::Diagnostic) -> bool {
294 self.adapter.underline_diagnostic(diagnostic)
295 }
296
297 pub fn diagnostic_message_to_markdown(&self, message: &str) -> Option<String> {
298 self.adapter.diagnostic_message_to_markdown(message)
299 }
300
301 pub async fn process_completions(&self, completion_items: &mut [lsp::CompletionItem]) {
302 self.adapter.process_completions(completion_items).await
303 }
304
305 pub async fn labels_for_completions(
306 &self,
307 completion_items: &[lsp::CompletionItem],
308 language: &Arc<Language>,
309 ) -> Result<Vec<Option<CodeLabel>>> {
310 self.adapter
311 .clone()
312 .labels_for_completions(completion_items, language)
313 .await
314 }
315
316 pub async fn labels_for_symbols(
317 &self,
318 symbols: &[(String, lsp::SymbolKind)],
319 language: &Arc<Language>,
320 ) -> Result<Vec<Option<CodeLabel>>> {
321 self.adapter
322 .clone()
323 .labels_for_symbols(symbols, language)
324 .await
325 }
326
327 pub fn language_id(&self, language_name: &LanguageName) -> String {
328 self.language_ids
329 .get(language_name)
330 .cloned()
331 .unwrap_or_else(|| language_name.lsp_id())
332 }
333}
334
335/// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application
336// e.g. to display a notification or fetch data from the web.
337#[async_trait]
338pub trait LspAdapterDelegate: Send + Sync {
339 fn show_notification(&self, message: &str, cx: &mut App);
340 fn http_client(&self) -> Arc<dyn HttpClient>;
341 fn worktree_id(&self) -> WorktreeId;
342 fn worktree_root_path(&self) -> &Path;
343 fn resolve_executable_path(&self, path: PathBuf) -> PathBuf;
344 fn update_status(&self, language: LanguageServerName, status: BinaryStatus);
345 fn registered_lsp_adapters(&self) -> Vec<Arc<dyn LspAdapter>>;
346 async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>>;
347
348 async fn npm_package_installed_version(
349 &self,
350 package_name: &str,
351 ) -> Result<Option<(PathBuf, Version)>>;
352 async fn which(&self, command: &OsStr) -> Option<PathBuf>;
353 async fn shell_env(&self) -> HashMap<String, String>;
354 async fn read_text_file(&self, path: &RelPath) -> Result<String>;
355 async fn try_exec(&self, binary: LanguageServerBinary) -> Result<()>;
356}
357
358#[async_trait(?Send)]
359pub trait LspAdapter: 'static + Send + Sync + DynLspInstaller {
360 fn name(&self) -> LanguageServerName;
361
362 fn process_diagnostics(
363 &self,
364 _: &mut lsp::PublishDiagnosticsParams,
365 _: LanguageServerId,
366 _: Option<&'_ Buffer>,
367 ) {
368 }
369
370 /// When processing new `lsp::PublishDiagnosticsParams` diagnostics, whether to retain previous one(s) or not.
371 fn retain_old_diagnostic(&self, _previous_diagnostic: &Diagnostic, _cx: &App) -> bool {
372 false
373 }
374
375 /// Whether to underline a given diagnostic or not, when rendering in the editor.
376 ///
377 /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnosticTag
378 /// states that
379 /// > Clients are allowed to render diagnostics with this tag faded out instead of having an error squiggle.
380 /// for the unnecessary diagnostics, so do not underline them.
381 fn underline_diagnostic(&self, _diagnostic: &lsp::Diagnostic) -> bool {
382 true
383 }
384
385 /// Post-processes completions provided by the language server.
386 async fn process_completions(&self, _: &mut [lsp::CompletionItem]) {}
387
388 fn diagnostic_message_to_markdown(&self, _message: &str) -> Option<String> {
389 None
390 }
391
392 async fn labels_for_completions(
393 self: Arc<Self>,
394 completions: &[lsp::CompletionItem],
395 language: &Arc<Language>,
396 ) -> Result<Vec<Option<CodeLabel>>> {
397 let mut labels = Vec::new();
398 for (ix, completion) in completions.iter().enumerate() {
399 let label = self.label_for_completion(completion, language).await;
400 if let Some(label) = label {
401 labels.resize(ix + 1, None);
402 *labels.last_mut().unwrap() = Some(label);
403 }
404 }
405 Ok(labels)
406 }
407
408 async fn label_for_completion(
409 &self,
410 _: &lsp::CompletionItem,
411 _: &Arc<Language>,
412 ) -> Option<CodeLabel> {
413 None
414 }
415
416 async fn labels_for_symbols(
417 self: Arc<Self>,
418 symbols: &[(String, lsp::SymbolKind)],
419 language: &Arc<Language>,
420 ) -> Result<Vec<Option<CodeLabel>>> {
421 let mut labels = Vec::new();
422 for (ix, (name, kind)) in symbols.iter().enumerate() {
423 let label = self.label_for_symbol(name, *kind, language).await;
424 if let Some(label) = label {
425 labels.resize(ix + 1, None);
426 *labels.last_mut().unwrap() = Some(label);
427 }
428 }
429 Ok(labels)
430 }
431
432 async fn label_for_symbol(
433 &self,
434 _: &str,
435 _: lsp::SymbolKind,
436 _: &Arc<Language>,
437 ) -> Option<CodeLabel> {
438 None
439 }
440
441 /// Returns initialization options that are going to be sent to a LSP server as a part of [`lsp::InitializeParams`]
442 async fn initialization_options(
443 self: Arc<Self>,
444 _: &Arc<dyn LspAdapterDelegate>,
445 ) -> Result<Option<Value>> {
446 Ok(None)
447 }
448
449 async fn workspace_configuration(
450 self: Arc<Self>,
451 _: &Arc<dyn LspAdapterDelegate>,
452 _: Option<Toolchain>,
453 _: Option<Uri>,
454 _cx: &mut AsyncApp,
455 ) -> Result<Value> {
456 Ok(serde_json::json!({}))
457 }
458
459 async fn additional_initialization_options(
460 self: Arc<Self>,
461 _target_language_server_id: LanguageServerName,
462 _: &Arc<dyn LspAdapterDelegate>,
463 ) -> Result<Option<Value>> {
464 Ok(None)
465 }
466
467 async fn additional_workspace_configuration(
468 self: Arc<Self>,
469 _target_language_server_id: LanguageServerName,
470 _: &Arc<dyn LspAdapterDelegate>,
471 _cx: &mut AsyncApp,
472 ) -> Result<Option<Value>> {
473 Ok(None)
474 }
475
476 /// Returns a list of code actions supported by a given LspAdapter
477 fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
478 None
479 }
480
481 fn disk_based_diagnostic_sources(&self) -> Vec<String> {
482 Default::default()
483 }
484
485 fn disk_based_diagnostics_progress_token(&self) -> Option<String> {
486 None
487 }
488
489 fn language_ids(&self) -> HashMap<LanguageName, String> {
490 HashMap::default()
491 }
492
493 /// Support custom initialize params.
494 fn prepare_initialize_params(
495 &self,
496 original: InitializeParams,
497 _: &App,
498 ) -> Result<InitializeParams> {
499 Ok(original)
500 }
501
502 /// Method only implemented by the default JSON language server adapter.
503 /// Used to provide dynamic reloading of the JSON schemas used to
504 /// provide autocompletion and diagnostics in Zed setting and keybind
505 /// files
506 fn is_primary_zed_json_schema_adapter(&self) -> bool {
507 false
508 }
509
510 /// True for the extension adapter and false otherwise.
511 fn is_extension(&self) -> bool {
512 false
513 }
514}
515
516pub trait LspInstaller {
517 type BinaryVersion;
518 fn check_if_user_installed(
519 &self,
520 _: &dyn LspAdapterDelegate,
521 _: Option<Toolchain>,
522 _: &AsyncApp,
523 ) -> impl Future<Output = Option<LanguageServerBinary>> {
524 async { None }
525 }
526
527 fn fetch_latest_server_version(
528 &self,
529 delegate: &dyn LspAdapterDelegate,
530 pre_release: bool,
531 cx: &mut AsyncApp,
532 ) -> impl Future<Output = Result<Self::BinaryVersion>>;
533
534 fn check_if_version_installed(
535 &self,
536 _version: &Self::BinaryVersion,
537 _container_dir: &PathBuf,
538 _delegate: &dyn LspAdapterDelegate,
539 ) -> impl Send + Future<Output = Option<LanguageServerBinary>> {
540 async { None }
541 }
542
543 fn fetch_server_binary(
544 &self,
545 latest_version: Self::BinaryVersion,
546 container_dir: PathBuf,
547 delegate: &dyn LspAdapterDelegate,
548 ) -> impl Send + Future<Output = Result<LanguageServerBinary>>;
549
550 fn cached_server_binary(
551 &self,
552 container_dir: PathBuf,
553 delegate: &dyn LspAdapterDelegate,
554 ) -> impl Future<Output = Option<LanguageServerBinary>>;
555}
556
557#[async_trait(?Send)]
558pub trait DynLspInstaller {
559 async fn try_fetch_server_binary(
560 &self,
561 delegate: &Arc<dyn LspAdapterDelegate>,
562 container_dir: PathBuf,
563 pre_release: bool,
564 cx: &mut AsyncApp,
565 ) -> Result<LanguageServerBinary>;
566 fn get_language_server_command(
567 self: Arc<Self>,
568 delegate: Arc<dyn LspAdapterDelegate>,
569 toolchains: Option<Toolchain>,
570 binary_options: LanguageServerBinaryOptions,
571 cached_binary: OwnedMutexGuard<Option<(bool, LanguageServerBinary)>>,
572 cx: AsyncApp,
573 ) -> LanguageServerBinaryLocations;
574}
575
576#[async_trait(?Send)]
577impl<LI, BinaryVersion> DynLspInstaller for LI
578where
579 BinaryVersion: Send + Sync,
580 LI: LspInstaller<BinaryVersion = BinaryVersion> + LspAdapter,
581{
582 async fn try_fetch_server_binary(
583 &self,
584 delegate: &Arc<dyn LspAdapterDelegate>,
585 container_dir: PathBuf,
586 pre_release: bool,
587 cx: &mut AsyncApp,
588 ) -> Result<LanguageServerBinary> {
589 let name = self.name();
590
591 log::debug!("fetching latest version of language server {:?}", name.0);
592 delegate.update_status(name.clone(), BinaryStatus::CheckingForUpdate);
593
594 let latest_version = self
595 .fetch_latest_server_version(delegate.as_ref(), pre_release, cx)
596 .await?;
597
598 if let Some(binary) = cx
599 .background_executor()
600 .await_on_background(self.check_if_version_installed(
601 &latest_version,
602 &container_dir,
603 delegate.as_ref(),
604 ))
605 .await
606 {
607 log::debug!("language server {:?} is already installed", name.0);
608 delegate.update_status(name.clone(), BinaryStatus::None);
609 Ok(binary)
610 } else {
611 log::debug!("downloading language server {:?}", name.0);
612 delegate.update_status(name.clone(), BinaryStatus::Downloading);
613 let binary = cx
614 .background_executor()
615 .await_on_background(self.fetch_server_binary(
616 latest_version,
617 container_dir,
618 delegate.as_ref(),
619 ))
620 .await;
621
622 delegate.update_status(name.clone(), BinaryStatus::None);
623 binary
624 }
625 }
626 fn get_language_server_command(
627 self: Arc<Self>,
628 delegate: Arc<dyn LspAdapterDelegate>,
629 toolchain: Option<Toolchain>,
630 binary_options: LanguageServerBinaryOptions,
631 mut cached_binary: OwnedMutexGuard<Option<(bool, LanguageServerBinary)>>,
632 mut cx: AsyncApp,
633 ) -> LanguageServerBinaryLocations {
634 async move {
635 let cached_binary_deref = cached_binary.deref_mut();
636 // First we check whether the adapter can give us a user-installed binary.
637 // If so, we do *not* want to cache that, because each worktree might give us a different
638 // binary:
639 //
640 // worktree 1: user-installed at `.bin/gopls`
641 // worktree 2: user-installed at `~/bin/gopls`
642 // worktree 3: no gopls found in PATH -> fallback to Zed installation
643 //
644 // We only want to cache when we fall back to the global one,
645 // because we don't want to download and overwrite our global one
646 // for each worktree we might have open.
647 if binary_options.allow_path_lookup
648 && let Some(binary) = self
649 .check_if_user_installed(delegate.as_ref(), toolchain, &mut cx)
650 .await
651 {
652 log::info!(
653 "found user-installed language server for {}. path: {:?}, arguments: {:?}",
654 self.name().0,
655 binary.path,
656 binary.arguments
657 );
658 return (Ok(binary), None);
659 }
660
661 if !binary_options.allow_binary_download {
662 return (
663 Err(anyhow::anyhow!("downloading language servers disabled")),
664 None,
665 );
666 }
667
668 if let Some((pre_release, cached_binary)) = cached_binary_deref
669 && *pre_release == binary_options.pre_release
670 {
671 return (Ok(cached_binary.clone()), None);
672 }
673
674 let Some(container_dir) = delegate.language_server_download_dir(&self.name()).await
675 else {
676 return (
677 Err(anyhow::anyhow!("no language server download dir defined")),
678 None,
679 );
680 };
681
682 let last_downloaded_binary = self
683 .cached_server_binary(container_dir.to_path_buf(), delegate.as_ref())
684 .await
685 .context(
686 "did not find existing language server binary, falling back to downloading",
687 );
688 let download_binary = async move {
689 let mut binary = self
690 .try_fetch_server_binary(
691 &delegate,
692 container_dir.to_path_buf(),
693 binary_options.pre_release,
694 &mut cx,
695 )
696 .await;
697
698 if let Err(error) = binary.as_ref() {
699 if let Some(prev_downloaded_binary) = self
700 .cached_server_binary(container_dir.to_path_buf(), delegate.as_ref())
701 .await
702 {
703 log::info!(
704 "failed to fetch newest version of language server {:?}. \
705 error: {:?}, falling back to using {:?}",
706 self.name(),
707 error,
708 prev_downloaded_binary.path
709 );
710 binary = Ok(prev_downloaded_binary);
711 } else {
712 delegate.update_status(
713 self.name(),
714 BinaryStatus::Failed {
715 error: format!("{error:?}"),
716 },
717 );
718 }
719 }
720
721 if let Ok(binary) = &binary {
722 *cached_binary = Some((binary_options.pre_release, binary.clone()));
723 }
724
725 binary
726 }
727 .boxed_local();
728 (last_downloaded_binary, Some(download_binary))
729 }
730 .boxed_local()
731 }
732}
733
734#[derive(Clone, Debug, Default, PartialEq, Eq)]
735pub struct CodeLabel {
736 /// The text to display.
737 pub text: String,
738 /// Syntax highlighting runs.
739 pub runs: Vec<(Range<usize>, HighlightId)>,
740 /// The portion of the text that should be used in fuzzy filtering.
741 pub filter_range: Range<usize>,
742}
743
744#[derive(Clone, Debug, Default, PartialEq, Eq)]
745pub struct CodeLabelBuilder {
746 /// The text to display.
747 text: String,
748 /// Syntax highlighting runs.
749 runs: Vec<(Range<usize>, HighlightId)>,
750 /// The portion of the text that should be used in fuzzy filtering.
751 filter_range: Range<usize>,
752}
753
754#[derive(Clone, Deserialize, JsonSchema, Debug)]
755pub struct LanguageConfig {
756 /// Human-readable name of the language.
757 pub name: LanguageName,
758 /// The name of this language for a Markdown code fence block
759 pub code_fence_block_name: Option<Arc<str>>,
760 // The name of the grammar in a WASM bundle (experimental).
761 pub grammar: Option<Arc<str>>,
762 /// The criteria for matching this language to a given file.
763 #[serde(flatten)]
764 pub matcher: LanguageMatcher,
765 /// List of bracket types in a language.
766 #[serde(default)]
767 pub brackets: BracketPairConfig,
768 /// If set to true, auto indentation uses last non empty line to determine
769 /// the indentation level for a new line.
770 #[serde(default = "auto_indent_using_last_non_empty_line_default")]
771 pub auto_indent_using_last_non_empty_line: bool,
772 // Whether indentation of pasted content should be adjusted based on the context.
773 #[serde(default)]
774 pub auto_indent_on_paste: Option<bool>,
775 /// A regex that is used to determine whether the indentation level should be
776 /// increased in the following line.
777 #[serde(default, deserialize_with = "deserialize_regex")]
778 #[schemars(schema_with = "regex_json_schema")]
779 pub increase_indent_pattern: Option<Regex>,
780 /// A regex that is used to determine whether the indentation level should be
781 /// decreased in the following line.
782 #[serde(default, deserialize_with = "deserialize_regex")]
783 #[schemars(schema_with = "regex_json_schema")]
784 pub decrease_indent_pattern: Option<Regex>,
785 /// A list of rules for decreasing indentation. Each rule pairs a regex with a set of valid
786 /// "block-starting" tokens. When a line matches a pattern, its indentation is aligned with
787 /// the most recent line that began with a corresponding token. This enables context-aware
788 /// outdenting, like aligning an `else` with its `if`.
789 #[serde(default)]
790 pub decrease_indent_patterns: Vec<DecreaseIndentConfig>,
791 /// A list of characters that trigger the automatic insertion of a closing
792 /// bracket when they immediately precede the point where an opening
793 /// bracket is inserted.
794 #[serde(default)]
795 pub autoclose_before: String,
796 /// A placeholder used internally by Semantic Index.
797 #[serde(default)]
798 pub collapsed_placeholder: String,
799 /// A line comment string that is inserted in e.g. `toggle comments` action.
800 /// A language can have multiple flavours of line comments. All of the provided line comments are
801 /// used for comment continuations on the next line, but only the first one is used for Editor::ToggleComments.
802 #[serde(default)]
803 pub line_comments: Vec<Arc<str>>,
804 /// Delimiters and configuration for recognizing and formatting block comments.
805 #[serde(default)]
806 pub block_comment: Option<BlockCommentConfig>,
807 /// Delimiters and configuration for recognizing and formatting documentation comments.
808 #[serde(default, alias = "documentation")]
809 pub documentation_comment: Option<BlockCommentConfig>,
810 /// A list of additional regex patterns that should be treated as prefixes
811 /// for creating boundaries during rewrapping, ensuring content from one
812 /// prefixed section doesn't merge with another (e.g., markdown list items).
813 /// By default, Zed treats as paragraph and comment prefixes as boundaries.
814 #[serde(default, deserialize_with = "deserialize_regex_vec")]
815 #[schemars(schema_with = "regex_vec_json_schema")]
816 pub rewrap_prefixes: Vec<Regex>,
817 /// A list of language servers that are allowed to run on subranges of a given language.
818 #[serde(default)]
819 pub scope_opt_in_language_servers: Vec<LanguageServerName>,
820 #[serde(default)]
821 pub overrides: HashMap<String, LanguageConfigOverride>,
822 /// A list of characters that Zed should treat as word characters for the
823 /// purpose of features that operate on word boundaries, like 'move to next word end'
824 /// or a whole-word search in buffer search.
825 #[serde(default)]
826 pub word_characters: HashSet<char>,
827 /// Whether to indent lines using tab characters, as opposed to multiple
828 /// spaces.
829 #[serde(default)]
830 pub hard_tabs: Option<bool>,
831 /// How many columns a tab should occupy.
832 #[serde(default)]
833 #[schemars(range(min = 1, max = 128))]
834 pub tab_size: Option<NonZeroU32>,
835 /// How to soft-wrap long lines of text.
836 #[serde(default)]
837 pub soft_wrap: Option<SoftWrap>,
838 /// When set, selections can be wrapped using prefix/suffix pairs on both sides.
839 #[serde(default)]
840 pub wrap_characters: Option<WrapCharactersConfig>,
841 /// The name of a Prettier parser that will be used for this language when no file path is available.
842 /// If there's a parser name in the language settings, that will be used instead.
843 #[serde(default)]
844 pub prettier_parser_name: Option<String>,
845 /// If true, this language is only for syntax highlighting via an injection into other
846 /// languages, but should not appear to the user as a distinct language.
847 #[serde(default)]
848 pub hidden: bool,
849 /// If configured, this language contains JSX style tags, and should support auto-closing of those tags.
850 #[serde(default)]
851 pub jsx_tag_auto_close: Option<JsxTagAutoCloseConfig>,
852 /// A list of characters that Zed should treat as word characters for completion queries.
853 #[serde(default)]
854 pub completion_query_characters: HashSet<char>,
855 /// A list of characters that Zed should treat as word characters for linked edit operations.
856 #[serde(default)]
857 pub linked_edit_characters: HashSet<char>,
858 /// A list of preferred debuggers for this language.
859 #[serde(default)]
860 pub debuggers: IndexSet<SharedString>,
861 /// A list of import namespace segments that aren't expected to appear in file paths. For
862 /// example, "super" and "crate" in Rust.
863 #[serde(default)]
864 pub ignored_import_segments: HashSet<Arc<str>>,
865 /// Regular expression that matches substrings to omit from import paths, to make the paths more
866 /// similar to how they are specified when imported. For example, "/mod\.rs$" or "/__init__\.py$".
867 #[serde(default, deserialize_with = "deserialize_regex")]
868 #[schemars(schema_with = "regex_json_schema")]
869 pub import_path_strip_regex: Option<Regex>,
870}
871
872#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
873pub struct DecreaseIndentConfig {
874 #[serde(default, deserialize_with = "deserialize_regex")]
875 #[schemars(schema_with = "regex_json_schema")]
876 pub pattern: Option<Regex>,
877 #[serde(default)]
878 pub valid_after: Vec<String>,
879}
880
881#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
882pub struct LanguageMatcher {
883 /// Given a list of `LanguageConfig`'s, the language of a file can be determined based on the path extension matching any of the `path_suffixes`.
884 #[serde(default)]
885 pub path_suffixes: Vec<String>,
886 /// A regex pattern that determines whether the language should be assigned to a file or not.
887 #[serde(
888 default,
889 serialize_with = "serialize_regex",
890 deserialize_with = "deserialize_regex"
891 )]
892 #[schemars(schema_with = "regex_json_schema")]
893 pub first_line_pattern: Option<Regex>,
894}
895
896/// The configuration for JSX tag auto-closing.
897#[derive(Clone, Deserialize, JsonSchema, Debug)]
898pub struct JsxTagAutoCloseConfig {
899 /// The name of the node for a opening tag
900 pub open_tag_node_name: String,
901 /// The name of the node for an closing tag
902 pub close_tag_node_name: String,
903 /// The name of the node for a complete element with children for open and close tags
904 pub jsx_element_node_name: String,
905 /// The name of the node found within both opening and closing
906 /// tags that describes the tag name
907 pub tag_name_node_name: String,
908 /// Alternate Node names for tag names.
909 /// Specifically needed as TSX represents the name in `<Foo.Bar>`
910 /// as `member_expression` rather than `identifier` as usual
911 #[serde(default)]
912 pub tag_name_node_name_alternates: Vec<String>,
913 /// Some grammars are smart enough to detect a closing tag
914 /// that is not valid i.e. doesn't match it's corresponding
915 /// opening tag or does not have a corresponding opening tag
916 /// This should be set to the name of the node for invalid
917 /// closing tags if the grammar contains such a node, otherwise
918 /// detecting already closed tags will not work properly
919 #[serde(default)]
920 pub erroneous_close_tag_node_name: Option<String>,
921 /// See above for erroneous_close_tag_node_name for details
922 /// This should be set if the node used for the tag name
923 /// within erroneous closing tags is different from the
924 /// normal tag name node name
925 #[serde(default)]
926 pub erroneous_close_tag_name_node_name: Option<String>,
927}
928
929/// The configuration for block comments for this language.
930#[derive(Clone, Debug, JsonSchema, PartialEq)]
931pub struct BlockCommentConfig {
932 /// A start tag of block comment.
933 pub start: Arc<str>,
934 /// A end tag of block comment.
935 pub end: Arc<str>,
936 /// A character to add as a prefix when a new line is added to a block comment.
937 pub prefix: Arc<str>,
938 /// A indent to add for prefix and end line upon new line.
939 #[schemars(range(min = 1, max = 128))]
940 pub tab_size: u32,
941}
942
943impl<'de> Deserialize<'de> for BlockCommentConfig {
944 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
945 where
946 D: Deserializer<'de>,
947 {
948 #[derive(Deserialize)]
949 #[serde(untagged)]
950 enum BlockCommentConfigHelper {
951 New {
952 start: Arc<str>,
953 end: Arc<str>,
954 prefix: Arc<str>,
955 tab_size: u32,
956 },
957 Old([Arc<str>; 2]),
958 }
959
960 match BlockCommentConfigHelper::deserialize(deserializer)? {
961 BlockCommentConfigHelper::New {
962 start,
963 end,
964 prefix,
965 tab_size,
966 } => Ok(BlockCommentConfig {
967 start,
968 end,
969 prefix,
970 tab_size,
971 }),
972 BlockCommentConfigHelper::Old([start, end]) => Ok(BlockCommentConfig {
973 start,
974 end,
975 prefix: "".into(),
976 tab_size: 0,
977 }),
978 }
979 }
980}
981
982/// Represents a language for the given range. Some languages (e.g. HTML)
983/// interleave several languages together, thus a single buffer might actually contain
984/// several nested scopes.
985#[derive(Clone, Debug)]
986pub struct LanguageScope {
987 language: Arc<Language>,
988 override_id: Option<u32>,
989}
990
991#[derive(Clone, Deserialize, Default, Debug, JsonSchema)]
992pub struct LanguageConfigOverride {
993 #[serde(default)]
994 pub line_comments: Override<Vec<Arc<str>>>,
995 #[serde(default)]
996 pub block_comment: Override<BlockCommentConfig>,
997 #[serde(skip)]
998 pub disabled_bracket_ixs: Vec<u16>,
999 #[serde(default)]
1000 pub word_characters: Override<HashSet<char>>,
1001 #[serde(default)]
1002 pub completion_query_characters: Override<HashSet<char>>,
1003 #[serde(default)]
1004 pub linked_edit_characters: Override<HashSet<char>>,
1005 #[serde(default)]
1006 pub opt_into_language_servers: Vec<LanguageServerName>,
1007 #[serde(default)]
1008 pub prefer_label_for_snippet: Option<bool>,
1009}
1010
1011#[derive(Clone, Deserialize, Debug, Serialize, JsonSchema)]
1012#[serde(untagged)]
1013pub enum Override<T> {
1014 Remove { remove: bool },
1015 Set(T),
1016}
1017
1018impl<T> Default for Override<T> {
1019 fn default() -> Self {
1020 Override::Remove { remove: false }
1021 }
1022}
1023
1024impl<T> Override<T> {
1025 fn as_option<'a>(this: Option<&'a Self>, original: Option<&'a T>) -> Option<&'a T> {
1026 match this {
1027 Some(Self::Set(value)) => Some(value),
1028 Some(Self::Remove { remove: true }) => None,
1029 Some(Self::Remove { remove: false }) | None => original,
1030 }
1031 }
1032}
1033
1034impl Default for LanguageConfig {
1035 fn default() -> Self {
1036 Self {
1037 name: LanguageName::new_static(""),
1038 code_fence_block_name: None,
1039 grammar: None,
1040 matcher: LanguageMatcher::default(),
1041 brackets: Default::default(),
1042 auto_indent_using_last_non_empty_line: auto_indent_using_last_non_empty_line_default(),
1043 auto_indent_on_paste: None,
1044 increase_indent_pattern: Default::default(),
1045 decrease_indent_pattern: Default::default(),
1046 decrease_indent_patterns: Default::default(),
1047 autoclose_before: Default::default(),
1048 line_comments: Default::default(),
1049 block_comment: Default::default(),
1050 documentation_comment: Default::default(),
1051 rewrap_prefixes: Default::default(),
1052 scope_opt_in_language_servers: Default::default(),
1053 overrides: Default::default(),
1054 word_characters: Default::default(),
1055 collapsed_placeholder: Default::default(),
1056 hard_tabs: None,
1057 tab_size: None,
1058 soft_wrap: None,
1059 wrap_characters: None,
1060 prettier_parser_name: None,
1061 hidden: false,
1062 jsx_tag_auto_close: None,
1063 completion_query_characters: Default::default(),
1064 linked_edit_characters: Default::default(),
1065 debuggers: Default::default(),
1066 ignored_import_segments: Default::default(),
1067 import_path_strip_regex: None,
1068 }
1069 }
1070}
1071
1072#[derive(Clone, Debug, Deserialize, JsonSchema)]
1073pub struct WrapCharactersConfig {
1074 /// Opening token split into a prefix and suffix. The first caret goes
1075 /// after the prefix (i.e., between prefix and suffix).
1076 pub start_prefix: String,
1077 pub start_suffix: String,
1078 /// Closing token split into a prefix and suffix. The second caret goes
1079 /// after the prefix (i.e., between prefix and suffix).
1080 pub end_prefix: String,
1081 pub end_suffix: String,
1082}
1083
1084fn auto_indent_using_last_non_empty_line_default() -> bool {
1085 true
1086}
1087
1088fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D::Error> {
1089 let source = Option::<String>::deserialize(d)?;
1090 if let Some(source) = source {
1091 Ok(Some(regex::Regex::new(&source).map_err(de::Error::custom)?))
1092 } else {
1093 Ok(None)
1094 }
1095}
1096
1097fn regex_json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
1098 json_schema!({
1099 "type": "string"
1100 })
1101}
1102
1103fn serialize_regex<S>(regex: &Option<Regex>, serializer: S) -> Result<S::Ok, S::Error>
1104where
1105 S: Serializer,
1106{
1107 match regex {
1108 Some(regex) => serializer.serialize_str(regex.as_str()),
1109 None => serializer.serialize_none(),
1110 }
1111}
1112
1113fn deserialize_regex_vec<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<Regex>, D::Error> {
1114 let sources = Vec::<String>::deserialize(d)?;
1115 sources
1116 .into_iter()
1117 .map(|source| regex::Regex::new(&source))
1118 .collect::<Result<_, _>>()
1119 .map_err(de::Error::custom)
1120}
1121
1122fn regex_vec_json_schema(_: &mut SchemaGenerator) -> schemars::Schema {
1123 json_schema!({
1124 "type": "array",
1125 "items": { "type": "string" }
1126 })
1127}
1128
1129#[doc(hidden)]
1130#[cfg(any(test, feature = "test-support"))]
1131pub struct FakeLspAdapter {
1132 pub name: &'static str,
1133 pub initialization_options: Option<Value>,
1134 pub prettier_plugins: Vec<&'static str>,
1135 pub disk_based_diagnostics_progress_token: Option<String>,
1136 pub disk_based_diagnostics_sources: Vec<String>,
1137 pub language_server_binary: LanguageServerBinary,
1138
1139 pub capabilities: lsp::ServerCapabilities,
1140 pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
1141 pub label_for_completion: Option<
1142 Box<
1143 dyn 'static
1144 + Send
1145 + Sync
1146 + Fn(&lsp::CompletionItem, &Arc<Language>) -> Option<CodeLabel>,
1147 >,
1148 >,
1149}
1150
1151/// Configuration of handling bracket pairs for a given language.
1152///
1153/// This struct includes settings for defining which pairs of characters are considered brackets and
1154/// also specifies any language-specific scopes where these pairs should be ignored for bracket matching purposes.
1155#[derive(Clone, Debug, Default, JsonSchema)]
1156#[schemars(with = "Vec::<BracketPairContent>")]
1157pub struct BracketPairConfig {
1158 /// A list of character pairs that should be treated as brackets in the context of a given language.
1159 pub pairs: Vec<BracketPair>,
1160 /// A list of tree-sitter scopes for which a given bracket should not be active.
1161 /// N-th entry in `[Self::disabled_scopes_by_bracket_ix]` contains a list of disabled scopes for an n-th entry in `[Self::pairs]`
1162 pub disabled_scopes_by_bracket_ix: Vec<Vec<String>>,
1163}
1164
1165impl BracketPairConfig {
1166 pub fn is_closing_brace(&self, c: char) -> bool {
1167 self.pairs.iter().any(|pair| pair.end.starts_with(c))
1168 }
1169}
1170
1171#[derive(Deserialize, JsonSchema)]
1172pub struct BracketPairContent {
1173 #[serde(flatten)]
1174 pub bracket_pair: BracketPair,
1175 #[serde(default)]
1176 pub not_in: Vec<String>,
1177}
1178
1179impl<'de> Deserialize<'de> for BracketPairConfig {
1180 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
1181 where
1182 D: Deserializer<'de>,
1183 {
1184 let result = Vec::<BracketPairContent>::deserialize(deserializer)?;
1185 let (brackets, disabled_scopes_by_bracket_ix) = result
1186 .into_iter()
1187 .map(|entry| (entry.bracket_pair, entry.not_in))
1188 .unzip();
1189
1190 Ok(BracketPairConfig {
1191 pairs: brackets,
1192 disabled_scopes_by_bracket_ix,
1193 })
1194 }
1195}
1196
1197/// Describes a single bracket pair and how an editor should react to e.g. inserting
1198/// an opening bracket or to a newline character insertion in between `start` and `end` characters.
1199#[derive(Clone, Debug, Default, Deserialize, PartialEq, JsonSchema)]
1200pub struct BracketPair {
1201 /// Starting substring for a bracket.
1202 pub start: String,
1203 /// Ending substring for a bracket.
1204 pub end: String,
1205 /// True if `end` should be automatically inserted right after `start` characters.
1206 pub close: bool,
1207 /// True if selected text should be surrounded by `start` and `end` characters.
1208 #[serde(default = "default_true")]
1209 pub surround: bool,
1210 /// True if an extra newline should be inserted while the cursor is in the middle
1211 /// of that bracket pair.
1212 pub newline: bool,
1213}
1214
1215#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
1216pub struct LanguageId(usize);
1217
1218impl LanguageId {
1219 pub(crate) fn new() -> Self {
1220 Self(NEXT_LANGUAGE_ID.fetch_add(1, SeqCst))
1221 }
1222}
1223
1224pub struct Language {
1225 pub(crate) id: LanguageId,
1226 pub(crate) config: LanguageConfig,
1227 pub(crate) grammar: Option<Arc<Grammar>>,
1228 pub(crate) context_provider: Option<Arc<dyn ContextProvider>>,
1229 pub(crate) toolchain: Option<Arc<dyn ToolchainLister>>,
1230 pub(crate) manifest_name: Option<ManifestName>,
1231}
1232
1233#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
1234pub struct GrammarId(pub usize);
1235
1236impl GrammarId {
1237 pub(crate) fn new() -> Self {
1238 Self(NEXT_GRAMMAR_ID.fetch_add(1, SeqCst))
1239 }
1240}
1241
1242pub struct Grammar {
1243 id: GrammarId,
1244 pub ts_language: tree_sitter::Language,
1245 pub(crate) error_query: Option<Query>,
1246 pub highlights_config: Option<HighlightsConfig>,
1247 pub(crate) brackets_config: Option<BracketsConfig>,
1248 pub(crate) redactions_config: Option<RedactionConfig>,
1249 pub(crate) runnable_config: Option<RunnableConfig>,
1250 pub(crate) indents_config: Option<IndentConfig>,
1251 pub outline_config: Option<OutlineConfig>,
1252 pub text_object_config: Option<TextObjectConfig>,
1253 pub embedding_config: Option<EmbeddingConfig>,
1254 pub(crate) injection_config: Option<InjectionConfig>,
1255 pub(crate) override_config: Option<OverrideConfig>,
1256 pub(crate) debug_variables_config: Option<DebugVariablesConfig>,
1257 pub(crate) imports_config: Option<ImportsConfig>,
1258 pub(crate) highlight_map: Mutex<HighlightMap>,
1259}
1260
1261pub struct HighlightsConfig {
1262 pub query: Query,
1263 pub identifier_capture_indices: Vec<u32>,
1264}
1265
1266struct IndentConfig {
1267 query: Query,
1268 indent_capture_ix: u32,
1269 start_capture_ix: Option<u32>,
1270 end_capture_ix: Option<u32>,
1271 outdent_capture_ix: Option<u32>,
1272 suffixed_start_captures: HashMap<u32, SharedString>,
1273}
1274
1275pub struct OutlineConfig {
1276 pub query: Query,
1277 pub item_capture_ix: u32,
1278 pub name_capture_ix: u32,
1279 pub context_capture_ix: Option<u32>,
1280 pub extra_context_capture_ix: Option<u32>,
1281 pub open_capture_ix: Option<u32>,
1282 pub close_capture_ix: Option<u32>,
1283 pub annotation_capture_ix: Option<u32>,
1284}
1285
1286#[derive(Debug, Clone, Copy, PartialEq)]
1287pub enum DebuggerTextObject {
1288 Variable,
1289 Scope,
1290}
1291
1292impl DebuggerTextObject {
1293 pub fn from_capture_name(name: &str) -> Option<DebuggerTextObject> {
1294 match name {
1295 "debug-variable" => Some(DebuggerTextObject::Variable),
1296 "debug-scope" => Some(DebuggerTextObject::Scope),
1297 _ => None,
1298 }
1299 }
1300}
1301
1302#[derive(Debug, Clone, Copy, PartialEq)]
1303pub enum TextObject {
1304 InsideFunction,
1305 AroundFunction,
1306 InsideClass,
1307 AroundClass,
1308 InsideComment,
1309 AroundComment,
1310}
1311
1312impl TextObject {
1313 pub fn from_capture_name(name: &str) -> Option<TextObject> {
1314 match name {
1315 "function.inside" => Some(TextObject::InsideFunction),
1316 "function.around" => Some(TextObject::AroundFunction),
1317 "class.inside" => Some(TextObject::InsideClass),
1318 "class.around" => Some(TextObject::AroundClass),
1319 "comment.inside" => Some(TextObject::InsideComment),
1320 "comment.around" => Some(TextObject::AroundComment),
1321 _ => None,
1322 }
1323 }
1324
1325 pub fn around(&self) -> Option<Self> {
1326 match self {
1327 TextObject::InsideFunction => Some(TextObject::AroundFunction),
1328 TextObject::InsideClass => Some(TextObject::AroundClass),
1329 TextObject::InsideComment => Some(TextObject::AroundComment),
1330 _ => None,
1331 }
1332 }
1333}
1334
1335pub struct TextObjectConfig {
1336 pub query: Query,
1337 pub text_objects_by_capture_ix: Vec<(u32, TextObject)>,
1338}
1339
1340#[derive(Debug)]
1341pub struct EmbeddingConfig {
1342 pub query: Query,
1343 pub item_capture_ix: u32,
1344 pub name_capture_ix: Option<u32>,
1345 pub context_capture_ix: Option<u32>,
1346 pub collapse_capture_ix: Option<u32>,
1347 pub keep_capture_ix: Option<u32>,
1348}
1349
1350struct InjectionConfig {
1351 query: Query,
1352 content_capture_ix: u32,
1353 language_capture_ix: Option<u32>,
1354 patterns: Vec<InjectionPatternConfig>,
1355}
1356
1357struct RedactionConfig {
1358 pub query: Query,
1359 pub redaction_capture_ix: u32,
1360}
1361
1362#[derive(Clone, Debug, PartialEq)]
1363enum RunnableCapture {
1364 Named(SharedString),
1365 Run,
1366}
1367
1368struct RunnableConfig {
1369 pub query: Query,
1370 /// A mapping from capture indice to capture kind
1371 pub extra_captures: Vec<RunnableCapture>,
1372}
1373
1374struct OverrideConfig {
1375 query: Query,
1376 values: HashMap<u32, OverrideEntry>,
1377}
1378
1379#[derive(Debug)]
1380struct OverrideEntry {
1381 name: String,
1382 range_is_inclusive: bool,
1383 value: LanguageConfigOverride,
1384}
1385
1386#[derive(Default, Clone)]
1387struct InjectionPatternConfig {
1388 language: Option<Box<str>>,
1389 combined: bool,
1390}
1391
1392#[derive(Debug)]
1393struct BracketsConfig {
1394 query: Query,
1395 open_capture_ix: u32,
1396 close_capture_ix: u32,
1397 patterns: Vec<BracketsPatternConfig>,
1398}
1399
1400#[derive(Clone, Debug, Default)]
1401struct BracketsPatternConfig {
1402 newline_only: bool,
1403 rainbow_exclude: bool,
1404}
1405
1406pub struct DebugVariablesConfig {
1407 pub query: Query,
1408 pub objects_by_capture_ix: Vec<(u32, DebuggerTextObject)>,
1409}
1410
1411pub struct ImportsConfig {
1412 pub query: Query,
1413 pub import_ix: u32,
1414 pub name_ix: Option<u32>,
1415 pub namespace_ix: Option<u32>,
1416 pub source_ix: Option<u32>,
1417 pub list_ix: Option<u32>,
1418 pub wildcard_ix: Option<u32>,
1419 pub alias_ix: Option<u32>,
1420}
1421
1422impl Language {
1423 pub fn new(config: LanguageConfig, ts_language: Option<tree_sitter::Language>) -> Self {
1424 Self::new_with_id(LanguageId::new(), config, ts_language)
1425 }
1426
1427 pub fn id(&self) -> LanguageId {
1428 self.id
1429 }
1430
1431 fn new_with_id(
1432 id: LanguageId,
1433 config: LanguageConfig,
1434 ts_language: Option<tree_sitter::Language>,
1435 ) -> Self {
1436 Self {
1437 id,
1438 config,
1439 grammar: ts_language.map(|ts_language| {
1440 Arc::new(Grammar {
1441 id: GrammarId::new(),
1442 highlights_config: None,
1443 brackets_config: None,
1444 outline_config: None,
1445 text_object_config: None,
1446 embedding_config: None,
1447 indents_config: None,
1448 injection_config: None,
1449 override_config: None,
1450 redactions_config: None,
1451 runnable_config: None,
1452 error_query: Query::new(&ts_language, "(ERROR) @error").ok(),
1453 debug_variables_config: None,
1454 imports_config: None,
1455 ts_language,
1456 highlight_map: Default::default(),
1457 })
1458 }),
1459 context_provider: None,
1460 toolchain: None,
1461 manifest_name: None,
1462 }
1463 }
1464
1465 pub fn with_context_provider(mut self, provider: Option<Arc<dyn ContextProvider>>) -> Self {
1466 self.context_provider = provider;
1467 self
1468 }
1469
1470 pub fn with_toolchain_lister(mut self, provider: Option<Arc<dyn ToolchainLister>>) -> Self {
1471 self.toolchain = provider;
1472 self
1473 }
1474
1475 pub fn with_manifest(mut self, name: Option<ManifestName>) -> Self {
1476 self.manifest_name = name;
1477 self
1478 }
1479
1480 pub fn with_queries(mut self, queries: LanguageQueries) -> Result<Self> {
1481 if let Some(query) = queries.highlights {
1482 self = self
1483 .with_highlights_query(query.as_ref())
1484 .context("Error loading highlights query")?;
1485 }
1486 if let Some(query) = queries.brackets {
1487 self = self
1488 .with_brackets_query(query.as_ref())
1489 .context("Error loading brackets query")?;
1490 }
1491 if let Some(query) = queries.indents {
1492 self = self
1493 .with_indents_query(query.as_ref())
1494 .context("Error loading indents query")?;
1495 }
1496 if let Some(query) = queries.outline {
1497 self = self
1498 .with_outline_query(query.as_ref())
1499 .context("Error loading outline query")?;
1500 }
1501 if let Some(query) = queries.embedding {
1502 self = self
1503 .with_embedding_query(query.as_ref())
1504 .context("Error loading embedding query")?;
1505 }
1506 if let Some(query) = queries.injections {
1507 self = self
1508 .with_injection_query(query.as_ref())
1509 .context("Error loading injection query")?;
1510 }
1511 if let Some(query) = queries.overrides {
1512 self = self
1513 .with_override_query(query.as_ref())
1514 .context("Error loading override query")?;
1515 }
1516 if let Some(query) = queries.redactions {
1517 self = self
1518 .with_redaction_query(query.as_ref())
1519 .context("Error loading redaction query")?;
1520 }
1521 if let Some(query) = queries.runnables {
1522 self = self
1523 .with_runnable_query(query.as_ref())
1524 .context("Error loading runnables query")?;
1525 }
1526 if let Some(query) = queries.text_objects {
1527 self = self
1528 .with_text_object_query(query.as_ref())
1529 .context("Error loading textobject query")?;
1530 }
1531 if let Some(query) = queries.debugger {
1532 self = self
1533 .with_debug_variables_query(query.as_ref())
1534 .context("Error loading debug variables query")?;
1535 }
1536 if let Some(query) = queries.imports {
1537 self = self
1538 .with_imports_query(query.as_ref())
1539 .context("Error loading imports query")?;
1540 }
1541 Ok(self)
1542 }
1543
1544 pub fn with_highlights_query(mut self, source: &str) -> Result<Self> {
1545 let grammar = self.grammar_mut()?;
1546 let query = Query::new(&grammar.ts_language, source)?;
1547
1548 let mut identifier_capture_indices = Vec::new();
1549 for name in [
1550 "variable",
1551 "constant",
1552 "constructor",
1553 "function",
1554 "function.method",
1555 "function.method.call",
1556 "function.special",
1557 "property",
1558 "type",
1559 "type.interface",
1560 ] {
1561 identifier_capture_indices.extend(query.capture_index_for_name(name));
1562 }
1563
1564 grammar.highlights_config = Some(HighlightsConfig {
1565 query,
1566 identifier_capture_indices,
1567 });
1568
1569 Ok(self)
1570 }
1571
1572 pub fn with_runnable_query(mut self, source: &str) -> Result<Self> {
1573 let grammar = self.grammar_mut()?;
1574
1575 let query = Query::new(&grammar.ts_language, source)?;
1576 let extra_captures: Vec<_> = query
1577 .capture_names()
1578 .iter()
1579 .map(|&name| match name {
1580 "run" => RunnableCapture::Run,
1581 name => RunnableCapture::Named(name.to_string().into()),
1582 })
1583 .collect();
1584
1585 grammar.runnable_config = Some(RunnableConfig {
1586 extra_captures,
1587 query,
1588 });
1589
1590 Ok(self)
1591 }
1592
1593 pub fn with_outline_query(mut self, source: &str) -> Result<Self> {
1594 let query = Query::new(&self.expect_grammar()?.ts_language, source)?;
1595 let mut item_capture_ix = 0;
1596 let mut name_capture_ix = 0;
1597 let mut context_capture_ix = None;
1598 let mut extra_context_capture_ix = None;
1599 let mut open_capture_ix = None;
1600 let mut close_capture_ix = None;
1601 let mut annotation_capture_ix = None;
1602 if populate_capture_indices(
1603 &query,
1604 &self.config.name,
1605 "outline",
1606 &[],
1607 &mut [
1608 Capture::Required("item", &mut item_capture_ix),
1609 Capture::Required("name", &mut name_capture_ix),
1610 Capture::Optional("context", &mut context_capture_ix),
1611 Capture::Optional("context.extra", &mut extra_context_capture_ix),
1612 Capture::Optional("open", &mut open_capture_ix),
1613 Capture::Optional("close", &mut close_capture_ix),
1614 Capture::Optional("annotation", &mut annotation_capture_ix),
1615 ],
1616 ) {
1617 self.grammar_mut()?.outline_config = Some(OutlineConfig {
1618 query,
1619 item_capture_ix,
1620 name_capture_ix,
1621 context_capture_ix,
1622 extra_context_capture_ix,
1623 open_capture_ix,
1624 close_capture_ix,
1625 annotation_capture_ix,
1626 });
1627 }
1628 Ok(self)
1629 }
1630
1631 pub fn with_text_object_query(mut self, source: &str) -> Result<Self> {
1632 let query = Query::new(&self.expect_grammar()?.ts_language, source)?;
1633
1634 let mut text_objects_by_capture_ix = Vec::new();
1635 for (ix, name) in query.capture_names().iter().enumerate() {
1636 if let Some(text_object) = TextObject::from_capture_name(name) {
1637 text_objects_by_capture_ix.push((ix as u32, text_object));
1638 } else {
1639 log::warn!(
1640 "unrecognized capture name '{}' in {} textobjects TreeSitter query",
1641 name,
1642 self.config.name,
1643 );
1644 }
1645 }
1646
1647 self.grammar_mut()?.text_object_config = Some(TextObjectConfig {
1648 query,
1649 text_objects_by_capture_ix,
1650 });
1651 Ok(self)
1652 }
1653
1654 pub fn with_embedding_query(mut self, source: &str) -> Result<Self> {
1655 let query = Query::new(&self.expect_grammar()?.ts_language, source)?;
1656 let mut item_capture_ix = 0;
1657 let mut name_capture_ix = None;
1658 let mut context_capture_ix = None;
1659 let mut collapse_capture_ix = None;
1660 let mut keep_capture_ix = None;
1661 if populate_capture_indices(
1662 &query,
1663 &self.config.name,
1664 "embedding",
1665 &[],
1666 &mut [
1667 Capture::Required("item", &mut item_capture_ix),
1668 Capture::Optional("name", &mut name_capture_ix),
1669 Capture::Optional("context", &mut context_capture_ix),
1670 Capture::Optional("keep", &mut keep_capture_ix),
1671 Capture::Optional("collapse", &mut collapse_capture_ix),
1672 ],
1673 ) {
1674 self.grammar_mut()?.embedding_config = Some(EmbeddingConfig {
1675 query,
1676 item_capture_ix,
1677 name_capture_ix,
1678 context_capture_ix,
1679 collapse_capture_ix,
1680 keep_capture_ix,
1681 });
1682 }
1683 Ok(self)
1684 }
1685
1686 pub fn with_debug_variables_query(mut self, source: &str) -> Result<Self> {
1687 let query = Query::new(&self.expect_grammar()?.ts_language, source)?;
1688
1689 let mut objects_by_capture_ix = Vec::new();
1690 for (ix, name) in query.capture_names().iter().enumerate() {
1691 if let Some(text_object) = DebuggerTextObject::from_capture_name(name) {
1692 objects_by_capture_ix.push((ix as u32, text_object));
1693 } else {
1694 log::warn!(
1695 "unrecognized capture name '{}' in {} debugger TreeSitter query",
1696 name,
1697 self.config.name,
1698 );
1699 }
1700 }
1701
1702 self.grammar_mut()?.debug_variables_config = Some(DebugVariablesConfig {
1703 query,
1704 objects_by_capture_ix,
1705 });
1706 Ok(self)
1707 }
1708
1709 pub fn with_imports_query(mut self, source: &str) -> Result<Self> {
1710 let query = Query::new(&self.expect_grammar()?.ts_language, source)?;
1711
1712 let mut import_ix = 0;
1713 let mut name_ix = None;
1714 let mut namespace_ix = None;
1715 let mut source_ix = None;
1716 let mut list_ix = None;
1717 let mut wildcard_ix = None;
1718 let mut alias_ix = None;
1719 if populate_capture_indices(
1720 &query,
1721 &self.config.name,
1722 "imports",
1723 &[],
1724 &mut [
1725 Capture::Required("import", &mut import_ix),
1726 Capture::Optional("name", &mut name_ix),
1727 Capture::Optional("namespace", &mut namespace_ix),
1728 Capture::Optional("source", &mut source_ix),
1729 Capture::Optional("list", &mut list_ix),
1730 Capture::Optional("wildcard", &mut wildcard_ix),
1731 Capture::Optional("alias", &mut alias_ix),
1732 ],
1733 ) {
1734 self.grammar_mut()?.imports_config = Some(ImportsConfig {
1735 query,
1736 import_ix,
1737 name_ix,
1738 namespace_ix,
1739 source_ix,
1740 list_ix,
1741 wildcard_ix,
1742 alias_ix,
1743 });
1744 }
1745 return Ok(self);
1746 }
1747
1748 pub fn with_brackets_query(mut self, source: &str) -> Result<Self> {
1749 let query = Query::new(&self.expect_grammar()?.ts_language, source)?;
1750 let mut open_capture_ix = 0;
1751 let mut close_capture_ix = 0;
1752 if populate_capture_indices(
1753 &query,
1754 &self.config.name,
1755 "brackets",
1756 &[],
1757 &mut [
1758 Capture::Required("open", &mut open_capture_ix),
1759 Capture::Required("close", &mut close_capture_ix),
1760 ],
1761 ) {
1762 let patterns = (0..query.pattern_count())
1763 .map(|ix| {
1764 let mut config = BracketsPatternConfig::default();
1765 for setting in query.property_settings(ix) {
1766 let setting_key = setting.key.as_ref();
1767 if setting_key == "newline.only" {
1768 config.newline_only = true
1769 }
1770 if setting_key == "rainbow.exclude" {
1771 config.rainbow_exclude = true
1772 }
1773 }
1774 config
1775 })
1776 .collect();
1777 self.grammar_mut()?.brackets_config = Some(BracketsConfig {
1778 query,
1779 open_capture_ix,
1780 close_capture_ix,
1781 patterns,
1782 });
1783 }
1784 Ok(self)
1785 }
1786
1787 pub fn with_indents_query(mut self, source: &str) -> Result<Self> {
1788 let query = Query::new(&self.expect_grammar()?.ts_language, source)?;
1789 let mut indent_capture_ix = 0;
1790 let mut start_capture_ix = None;
1791 let mut end_capture_ix = None;
1792 let mut outdent_capture_ix = None;
1793 if populate_capture_indices(
1794 &query,
1795 &self.config.name,
1796 "indents",
1797 &["start."],
1798 &mut [
1799 Capture::Required("indent", &mut indent_capture_ix),
1800 Capture::Optional("start", &mut start_capture_ix),
1801 Capture::Optional("end", &mut end_capture_ix),
1802 Capture::Optional("outdent", &mut outdent_capture_ix),
1803 ],
1804 ) {
1805 let mut suffixed_start_captures = HashMap::default();
1806 for (ix, name) in query.capture_names().iter().enumerate() {
1807 if let Some(suffix) = name.strip_prefix("start.") {
1808 suffixed_start_captures.insert(ix as u32, suffix.to_owned().into());
1809 }
1810 }
1811
1812 self.grammar_mut()?.indents_config = Some(IndentConfig {
1813 query,
1814 indent_capture_ix,
1815 start_capture_ix,
1816 end_capture_ix,
1817 outdent_capture_ix,
1818 suffixed_start_captures,
1819 });
1820 }
1821 Ok(self)
1822 }
1823
1824 pub fn with_injection_query(mut self, source: &str) -> Result<Self> {
1825 let query = Query::new(&self.expect_grammar()?.ts_language, source)?;
1826 let mut language_capture_ix = None;
1827 let mut injection_language_capture_ix = None;
1828 let mut content_capture_ix = None;
1829 let mut injection_content_capture_ix = None;
1830 if populate_capture_indices(
1831 &query,
1832 &self.config.name,
1833 "injections",
1834 &[],
1835 &mut [
1836 Capture::Optional("language", &mut language_capture_ix),
1837 Capture::Optional("injection.language", &mut injection_language_capture_ix),
1838 Capture::Optional("content", &mut content_capture_ix),
1839 Capture::Optional("injection.content", &mut injection_content_capture_ix),
1840 ],
1841 ) {
1842 language_capture_ix = match (language_capture_ix, injection_language_capture_ix) {
1843 (None, Some(ix)) => Some(ix),
1844 (Some(_), Some(_)) => {
1845 anyhow::bail!("both language and injection.language captures are present");
1846 }
1847 _ => language_capture_ix,
1848 };
1849 content_capture_ix = match (content_capture_ix, injection_content_capture_ix) {
1850 (None, Some(ix)) => Some(ix),
1851 (Some(_), Some(_)) => {
1852 anyhow::bail!("both content and injection.content captures are present")
1853 }
1854 _ => content_capture_ix,
1855 };
1856 let patterns = (0..query.pattern_count())
1857 .map(|ix| {
1858 let mut config = InjectionPatternConfig::default();
1859 for setting in query.property_settings(ix) {
1860 match setting.key.as_ref() {
1861 "language" | "injection.language" => {
1862 config.language.clone_from(&setting.value);
1863 }
1864 "combined" | "injection.combined" => {
1865 config.combined = true;
1866 }
1867 _ => {}
1868 }
1869 }
1870 config
1871 })
1872 .collect();
1873 if let Some(content_capture_ix) = content_capture_ix {
1874 self.grammar_mut()?.injection_config = Some(InjectionConfig {
1875 query,
1876 language_capture_ix,
1877 content_capture_ix,
1878 patterns,
1879 });
1880 } else {
1881 log::error!(
1882 "missing required capture in injections {} TreeSitter query: \
1883 content or injection.content",
1884 &self.config.name,
1885 );
1886 }
1887 }
1888 Ok(self)
1889 }
1890
1891 pub fn with_override_query(mut self, source: &str) -> anyhow::Result<Self> {
1892 let query = Query::new(&self.expect_grammar()?.ts_language, source)?;
1893
1894 let mut override_configs_by_id = HashMap::default();
1895 for (ix, mut name) in query.capture_names().iter().copied().enumerate() {
1896 let mut range_is_inclusive = false;
1897 if name.starts_with('_') {
1898 continue;
1899 }
1900 if let Some(prefix) = name.strip_suffix(".inclusive") {
1901 name = prefix;
1902 range_is_inclusive = true;
1903 }
1904
1905 let value = self.config.overrides.get(name).cloned().unwrap_or_default();
1906 for server_name in &value.opt_into_language_servers {
1907 if !self
1908 .config
1909 .scope_opt_in_language_servers
1910 .contains(server_name)
1911 {
1912 util::debug_panic!(
1913 "Server {server_name:?} has been opted-in by scope {name:?} but has not been marked as an opt-in server"
1914 );
1915 }
1916 }
1917
1918 override_configs_by_id.insert(
1919 ix as u32,
1920 OverrideEntry {
1921 name: name.to_string(),
1922 range_is_inclusive,
1923 value,
1924 },
1925 );
1926 }
1927
1928 let referenced_override_names = self.config.overrides.keys().chain(
1929 self.config
1930 .brackets
1931 .disabled_scopes_by_bracket_ix
1932 .iter()
1933 .flatten(),
1934 );
1935
1936 for referenced_name in referenced_override_names {
1937 if !override_configs_by_id
1938 .values()
1939 .any(|entry| entry.name == *referenced_name)
1940 {
1941 anyhow::bail!(
1942 "language {:?} has overrides in config not in query: {referenced_name:?}",
1943 self.config.name
1944 );
1945 }
1946 }
1947
1948 for entry in override_configs_by_id.values_mut() {
1949 entry.value.disabled_bracket_ixs = self
1950 .config
1951 .brackets
1952 .disabled_scopes_by_bracket_ix
1953 .iter()
1954 .enumerate()
1955 .filter_map(|(ix, disabled_scope_names)| {
1956 if disabled_scope_names.contains(&entry.name) {
1957 Some(ix as u16)
1958 } else {
1959 None
1960 }
1961 })
1962 .collect();
1963 }
1964
1965 self.config.brackets.disabled_scopes_by_bracket_ix.clear();
1966
1967 let grammar = self.grammar_mut()?;
1968 grammar.override_config = Some(OverrideConfig {
1969 query,
1970 values: override_configs_by_id,
1971 });
1972 Ok(self)
1973 }
1974
1975 pub fn with_redaction_query(mut self, source: &str) -> anyhow::Result<Self> {
1976 let query = Query::new(&self.expect_grammar()?.ts_language, source)?;
1977 let mut redaction_capture_ix = 0;
1978 if populate_capture_indices(
1979 &query,
1980 &self.config.name,
1981 "redactions",
1982 &[],
1983 &mut [Capture::Required("redact", &mut redaction_capture_ix)],
1984 ) {
1985 self.grammar_mut()?.redactions_config = Some(RedactionConfig {
1986 query,
1987 redaction_capture_ix,
1988 });
1989 }
1990 Ok(self)
1991 }
1992
1993 fn expect_grammar(&self) -> Result<&Grammar> {
1994 self.grammar
1995 .as_ref()
1996 .map(|grammar| grammar.as_ref())
1997 .context("no grammar for language")
1998 }
1999
2000 fn grammar_mut(&mut self) -> Result<&mut Grammar> {
2001 Arc::get_mut(self.grammar.as_mut().context("no grammar for language")?)
2002 .context("cannot mutate grammar")
2003 }
2004
2005 pub fn name(&self) -> LanguageName {
2006 self.config.name.clone()
2007 }
2008 pub fn manifest(&self) -> Option<&ManifestName> {
2009 self.manifest_name.as_ref()
2010 }
2011
2012 pub fn code_fence_block_name(&self) -> Arc<str> {
2013 self.config
2014 .code_fence_block_name
2015 .clone()
2016 .unwrap_or_else(|| self.config.name.as_ref().to_lowercase().into())
2017 }
2018
2019 pub fn context_provider(&self) -> Option<Arc<dyn ContextProvider>> {
2020 self.context_provider.clone()
2021 }
2022
2023 pub fn toolchain_lister(&self) -> Option<Arc<dyn ToolchainLister>> {
2024 self.toolchain.clone()
2025 }
2026
2027 pub fn highlight_text<'a>(
2028 self: &'a Arc<Self>,
2029 text: &'a Rope,
2030 range: Range<usize>,
2031 ) -> Vec<(Range<usize>, HighlightId)> {
2032 let mut result = Vec::new();
2033 if let Some(grammar) = &self.grammar {
2034 let tree = grammar.parse_text(text, None);
2035 let captures =
2036 SyntaxSnapshot::single_tree_captures(range.clone(), text, &tree, self, |grammar| {
2037 grammar
2038 .highlights_config
2039 .as_ref()
2040 .map(|config| &config.query)
2041 });
2042 let highlight_maps = vec![grammar.highlight_map()];
2043 let mut offset = 0;
2044 for chunk in
2045 BufferChunks::new(text, range, Some((captures, highlight_maps)), false, None)
2046 {
2047 let end_offset = offset + chunk.text.len();
2048 if let Some(highlight_id) = chunk.syntax_highlight_id
2049 && !highlight_id.is_default()
2050 {
2051 result.push((offset..end_offset, highlight_id));
2052 }
2053 offset = end_offset;
2054 }
2055 }
2056 result
2057 }
2058
2059 pub fn path_suffixes(&self) -> &[String] {
2060 &self.config.matcher.path_suffixes
2061 }
2062
2063 pub fn should_autoclose_before(&self, c: char) -> bool {
2064 c.is_whitespace() || self.config.autoclose_before.contains(c)
2065 }
2066
2067 pub fn set_theme(&self, theme: &SyntaxTheme) {
2068 if let Some(grammar) = self.grammar.as_ref()
2069 && let Some(highlights_config) = &grammar.highlights_config
2070 {
2071 *grammar.highlight_map.lock() =
2072 HighlightMap::new(highlights_config.query.capture_names(), theme);
2073 }
2074 }
2075
2076 pub fn grammar(&self) -> Option<&Arc<Grammar>> {
2077 self.grammar.as_ref()
2078 }
2079
2080 pub fn default_scope(self: &Arc<Self>) -> LanguageScope {
2081 LanguageScope {
2082 language: self.clone(),
2083 override_id: None,
2084 }
2085 }
2086
2087 pub fn lsp_id(&self) -> String {
2088 self.config.name.lsp_id()
2089 }
2090
2091 pub fn prettier_parser_name(&self) -> Option<&str> {
2092 self.config.prettier_parser_name.as_deref()
2093 }
2094
2095 pub fn config(&self) -> &LanguageConfig {
2096 &self.config
2097 }
2098}
2099
2100impl LanguageScope {
2101 pub fn path_suffixes(&self) -> &[String] {
2102 self.language.path_suffixes()
2103 }
2104
2105 pub fn language_name(&self) -> LanguageName {
2106 self.language.config.name.clone()
2107 }
2108
2109 pub fn collapsed_placeholder(&self) -> &str {
2110 self.language.config.collapsed_placeholder.as_ref()
2111 }
2112
2113 /// Returns line prefix that is inserted in e.g. line continuations or
2114 /// in `toggle comments` action.
2115 pub fn line_comment_prefixes(&self) -> &[Arc<str>] {
2116 Override::as_option(
2117 self.config_override().map(|o| &o.line_comments),
2118 Some(&self.language.config.line_comments),
2119 )
2120 .map_or([].as_slice(), |e| e.as_slice())
2121 }
2122
2123 /// Config for block comments for this language.
2124 pub fn block_comment(&self) -> Option<&BlockCommentConfig> {
2125 Override::as_option(
2126 self.config_override().map(|o| &o.block_comment),
2127 self.language.config.block_comment.as_ref(),
2128 )
2129 }
2130
2131 /// Config for documentation-style block comments for this language.
2132 pub fn documentation_comment(&self) -> Option<&BlockCommentConfig> {
2133 self.language.config.documentation_comment.as_ref()
2134 }
2135
2136 /// Returns additional regex patterns that act as prefix markers for creating
2137 /// boundaries during rewrapping.
2138 ///
2139 /// By default, Zed treats as paragraph and comment prefixes as boundaries.
2140 pub fn rewrap_prefixes(&self) -> &[Regex] {
2141 &self.language.config.rewrap_prefixes
2142 }
2143
2144 /// Returns a list of language-specific word characters.
2145 ///
2146 /// By default, Zed treats alphanumeric characters (and '_') as word characters for
2147 /// the purpose of actions like 'move to next word end` or whole-word search.
2148 /// It additionally accounts for language's additional word characters.
2149 pub fn word_characters(&self) -> Option<&HashSet<char>> {
2150 Override::as_option(
2151 self.config_override().map(|o| &o.word_characters),
2152 Some(&self.language.config.word_characters),
2153 )
2154 }
2155
2156 /// Returns a list of language-specific characters that are considered part of
2157 /// a completion query.
2158 pub fn completion_query_characters(&self) -> Option<&HashSet<char>> {
2159 Override::as_option(
2160 self.config_override()
2161 .map(|o| &o.completion_query_characters),
2162 Some(&self.language.config.completion_query_characters),
2163 )
2164 }
2165
2166 /// Returns a list of language-specific characters that are considered part of
2167 /// identifiers during linked editing operations.
2168 pub fn linked_edit_characters(&self) -> Option<&HashSet<char>> {
2169 Override::as_option(
2170 self.config_override().map(|o| &o.linked_edit_characters),
2171 Some(&self.language.config.linked_edit_characters),
2172 )
2173 }
2174
2175 /// Returns whether to prefer snippet `label` over `new_text` to replace text when
2176 /// completion is accepted.
2177 ///
2178 /// In cases like when cursor is in string or renaming existing function,
2179 /// you don't want to expand function signature instead just want function name
2180 /// to replace existing one.
2181 pub fn prefers_label_for_snippet_in_completion(&self) -> bool {
2182 self.config_override()
2183 .and_then(|o| o.prefer_label_for_snippet)
2184 .unwrap_or(false)
2185 }
2186
2187 /// Returns a list of bracket pairs for a given language with an additional
2188 /// piece of information about whether the particular bracket pair is currently active for a given language.
2189 pub fn brackets(&self) -> impl Iterator<Item = (&BracketPair, bool)> {
2190 let mut disabled_ids = self
2191 .config_override()
2192 .map_or(&[] as _, |o| o.disabled_bracket_ixs.as_slice());
2193 self.language
2194 .config
2195 .brackets
2196 .pairs
2197 .iter()
2198 .enumerate()
2199 .map(move |(ix, bracket)| {
2200 let mut is_enabled = true;
2201 if let Some(next_disabled_ix) = disabled_ids.first()
2202 && ix == *next_disabled_ix as usize
2203 {
2204 disabled_ids = &disabled_ids[1..];
2205 is_enabled = false;
2206 }
2207 (bracket, is_enabled)
2208 })
2209 }
2210
2211 pub fn should_autoclose_before(&self, c: char) -> bool {
2212 c.is_whitespace() || self.language.config.autoclose_before.contains(c)
2213 }
2214
2215 pub fn language_allowed(&self, name: &LanguageServerName) -> bool {
2216 let config = &self.language.config;
2217 let opt_in_servers = &config.scope_opt_in_language_servers;
2218 if opt_in_servers.contains(name) {
2219 if let Some(over) = self.config_override() {
2220 over.opt_into_language_servers.contains(name)
2221 } else {
2222 false
2223 }
2224 } else {
2225 true
2226 }
2227 }
2228
2229 pub fn override_name(&self) -> Option<&str> {
2230 let id = self.override_id?;
2231 let grammar = self.language.grammar.as_ref()?;
2232 let override_config = grammar.override_config.as_ref()?;
2233 override_config.values.get(&id).map(|e| e.name.as_str())
2234 }
2235
2236 fn config_override(&self) -> Option<&LanguageConfigOverride> {
2237 let id = self.override_id?;
2238 let grammar = self.language.grammar.as_ref()?;
2239 let override_config = grammar.override_config.as_ref()?;
2240 override_config.values.get(&id).map(|e| &e.value)
2241 }
2242}
2243
2244impl Hash for Language {
2245 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2246 self.id.hash(state)
2247 }
2248}
2249
2250impl PartialEq for Language {
2251 fn eq(&self, other: &Self) -> bool {
2252 self.id.eq(&other.id)
2253 }
2254}
2255
2256impl Eq for Language {}
2257
2258impl Debug for Language {
2259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2260 f.debug_struct("Language")
2261 .field("name", &self.config.name)
2262 .finish()
2263 }
2264}
2265
2266impl Grammar {
2267 pub fn id(&self) -> GrammarId {
2268 self.id
2269 }
2270
2271 fn parse_text(&self, text: &Rope, old_tree: Option<Tree>) -> Tree {
2272 with_parser(|parser| {
2273 parser
2274 .set_language(&self.ts_language)
2275 .expect("incompatible grammar");
2276 let mut chunks = text.chunks_in_range(0..text.len());
2277 parser
2278 .parse_with_options(
2279 &mut move |offset, _| {
2280 chunks.seek(offset);
2281 chunks.next().unwrap_or("").as_bytes()
2282 },
2283 old_tree.as_ref(),
2284 None,
2285 )
2286 .unwrap()
2287 })
2288 }
2289
2290 pub fn highlight_map(&self) -> HighlightMap {
2291 self.highlight_map.lock().clone()
2292 }
2293
2294 pub fn highlight_id_for_name(&self, name: &str) -> Option<HighlightId> {
2295 let capture_id = self
2296 .highlights_config
2297 .as_ref()?
2298 .query
2299 .capture_index_for_name(name)?;
2300 Some(self.highlight_map.lock().get(capture_id))
2301 }
2302
2303 pub fn debug_variables_config(&self) -> Option<&DebugVariablesConfig> {
2304 self.debug_variables_config.as_ref()
2305 }
2306
2307 pub fn imports_config(&self) -> Option<&ImportsConfig> {
2308 self.imports_config.as_ref()
2309 }
2310}
2311
2312impl CodeLabelBuilder {
2313 pub fn respan_filter_range(&mut self, filter_text: Option<&str>) {
2314 self.filter_range = filter_text
2315 .and_then(|filter| self.text.find(filter).map(|ix| ix..ix + filter.len()))
2316 .unwrap_or(0..self.text.len());
2317 }
2318
2319 pub fn push_str(&mut self, text: &str, highlight: Option<HighlightId>) {
2320 let start_ix = self.text.len();
2321 self.text.push_str(text);
2322 if let Some(highlight) = highlight {
2323 let end_ix = self.text.len();
2324 self.runs.push((start_ix..end_ix, highlight));
2325 }
2326 }
2327
2328 pub fn build(mut self) -> CodeLabel {
2329 if self.filter_range.end == 0 {
2330 self.respan_filter_range(None);
2331 }
2332 CodeLabel {
2333 text: self.text,
2334 runs: self.runs,
2335 filter_range: self.filter_range,
2336 }
2337 }
2338}
2339
2340impl CodeLabel {
2341 pub fn fallback_for_completion(
2342 item: &lsp::CompletionItem,
2343 language: Option<&Language>,
2344 ) -> Self {
2345 let highlight_id = item.kind.and_then(|kind| {
2346 let grammar = language?.grammar()?;
2347 use lsp::CompletionItemKind as Kind;
2348 match kind {
2349 Kind::CLASS => grammar.highlight_id_for_name("type"),
2350 Kind::CONSTANT => grammar.highlight_id_for_name("constant"),
2351 Kind::CONSTRUCTOR => grammar.highlight_id_for_name("constructor"),
2352 Kind::ENUM => grammar
2353 .highlight_id_for_name("enum")
2354 .or_else(|| grammar.highlight_id_for_name("type")),
2355 Kind::ENUM_MEMBER => grammar
2356 .highlight_id_for_name("variant")
2357 .or_else(|| grammar.highlight_id_for_name("property")),
2358 Kind::FIELD => grammar.highlight_id_for_name("property"),
2359 Kind::FUNCTION => grammar.highlight_id_for_name("function"),
2360 Kind::INTERFACE => grammar.highlight_id_for_name("type"),
2361 Kind::METHOD => grammar
2362 .highlight_id_for_name("function.method")
2363 .or_else(|| grammar.highlight_id_for_name("function")),
2364 Kind::OPERATOR => grammar.highlight_id_for_name("operator"),
2365 Kind::PROPERTY => grammar.highlight_id_for_name("property"),
2366 Kind::STRUCT => grammar.highlight_id_for_name("type"),
2367 Kind::VARIABLE => grammar.highlight_id_for_name("variable"),
2368 Kind::KEYWORD => grammar.highlight_id_for_name("keyword"),
2369 _ => None,
2370 }
2371 });
2372
2373 let label = &item.label;
2374 let label_length = label.len();
2375 let runs = highlight_id
2376 .map(|highlight_id| vec![(0..label_length, highlight_id)])
2377 .unwrap_or_default();
2378 let text = if let Some(detail) = item.detail.as_deref().filter(|detail| detail != label) {
2379 format!("{label} {detail}")
2380 } else if let Some(description) = item
2381 .label_details
2382 .as_ref()
2383 .and_then(|label_details| label_details.description.as_deref())
2384 .filter(|description| description != label)
2385 {
2386 format!("{label} {description}")
2387 } else {
2388 label.clone()
2389 };
2390 let filter_range = item
2391 .filter_text
2392 .as_deref()
2393 .and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
2394 .unwrap_or(0..label_length);
2395 Self {
2396 text,
2397 runs,
2398 filter_range,
2399 }
2400 }
2401
2402 pub fn plain(text: String, filter_text: Option<&str>) -> Self {
2403 Self::filtered(text.clone(), text.len(), filter_text, Vec::new())
2404 }
2405
2406 pub fn filtered(
2407 text: String,
2408 label_len: usize,
2409 filter_text: Option<&str>,
2410 runs: Vec<(Range<usize>, HighlightId)>,
2411 ) -> Self {
2412 assert!(label_len <= text.len());
2413 let filter_range = filter_text
2414 .and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
2415 .unwrap_or(0..label_len);
2416 Self::new(text, filter_range, runs)
2417 }
2418
2419 pub fn new(
2420 text: String,
2421 filter_range: Range<usize>,
2422 runs: Vec<(Range<usize>, HighlightId)>,
2423 ) -> Self {
2424 assert!(
2425 text.get(filter_range.clone()).is_some(),
2426 "invalid filter range"
2427 );
2428 runs.iter().for_each(|(range, _)| {
2429 assert!(
2430 text.get(range.clone()).is_some(),
2431 "invalid run range with inputs. Requested range {range:?} in text '{text}'",
2432 );
2433 });
2434 Self {
2435 runs,
2436 filter_range,
2437 text,
2438 }
2439 }
2440
2441 pub fn text(&self) -> &str {
2442 self.text.as_str()
2443 }
2444
2445 pub fn filter_text(&self) -> &str {
2446 &self.text[self.filter_range.clone()]
2447 }
2448}
2449
2450impl From<String> for CodeLabel {
2451 fn from(value: String) -> Self {
2452 Self::plain(value, None)
2453 }
2454}
2455
2456impl From<&str> for CodeLabel {
2457 fn from(value: &str) -> Self {
2458 Self::plain(value.to_string(), None)
2459 }
2460}
2461
2462impl Ord for LanguageMatcher {
2463 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2464 self.path_suffixes.cmp(&other.path_suffixes).then_with(|| {
2465 self.first_line_pattern
2466 .as_ref()
2467 .map(Regex::as_str)
2468 .cmp(&other.first_line_pattern.as_ref().map(Regex::as_str))
2469 })
2470 }
2471}
2472
2473impl PartialOrd for LanguageMatcher {
2474 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2475 Some(self.cmp(other))
2476 }
2477}
2478
2479impl Eq for LanguageMatcher {}
2480
2481impl PartialEq for LanguageMatcher {
2482 fn eq(&self, other: &Self) -> bool {
2483 self.path_suffixes == other.path_suffixes
2484 && self.first_line_pattern.as_ref().map(Regex::as_str)
2485 == other.first_line_pattern.as_ref().map(Regex::as_str)
2486 }
2487}
2488
2489#[cfg(any(test, feature = "test-support"))]
2490impl Default for FakeLspAdapter {
2491 fn default() -> Self {
2492 Self {
2493 name: "the-fake-language-server",
2494 capabilities: lsp::LanguageServer::full_capabilities(),
2495 initializer: None,
2496 disk_based_diagnostics_progress_token: None,
2497 initialization_options: None,
2498 disk_based_diagnostics_sources: Vec::new(),
2499 prettier_plugins: Vec::new(),
2500 language_server_binary: LanguageServerBinary {
2501 path: "/the/fake/lsp/path".into(),
2502 arguments: vec![],
2503 env: Default::default(),
2504 },
2505 label_for_completion: None,
2506 }
2507 }
2508}
2509
2510#[cfg(any(test, feature = "test-support"))]
2511impl LspInstaller for FakeLspAdapter {
2512 type BinaryVersion = ();
2513
2514 async fn fetch_latest_server_version(
2515 &self,
2516 _: &dyn LspAdapterDelegate,
2517 _: bool,
2518 _: &mut AsyncApp,
2519 ) -> Result<Self::BinaryVersion> {
2520 unreachable!()
2521 }
2522
2523 async fn check_if_user_installed(
2524 &self,
2525 _: &dyn LspAdapterDelegate,
2526 _: Option<Toolchain>,
2527 _: &AsyncApp,
2528 ) -> Option<LanguageServerBinary> {
2529 Some(self.language_server_binary.clone())
2530 }
2531
2532 async fn fetch_server_binary(
2533 &self,
2534 _: (),
2535 _: PathBuf,
2536 _: &dyn LspAdapterDelegate,
2537 ) -> Result<LanguageServerBinary> {
2538 unreachable!();
2539 }
2540
2541 async fn cached_server_binary(
2542 &self,
2543 _: PathBuf,
2544 _: &dyn LspAdapterDelegate,
2545 ) -> Option<LanguageServerBinary> {
2546 unreachable!();
2547 }
2548}
2549
2550#[cfg(any(test, feature = "test-support"))]
2551#[async_trait(?Send)]
2552impl LspAdapter for FakeLspAdapter {
2553 fn name(&self) -> LanguageServerName {
2554 LanguageServerName(self.name.into())
2555 }
2556
2557 fn disk_based_diagnostic_sources(&self) -> Vec<String> {
2558 self.disk_based_diagnostics_sources.clone()
2559 }
2560
2561 fn disk_based_diagnostics_progress_token(&self) -> Option<String> {
2562 self.disk_based_diagnostics_progress_token.clone()
2563 }
2564
2565 async fn initialization_options(
2566 self: Arc<Self>,
2567 _: &Arc<dyn LspAdapterDelegate>,
2568 ) -> Result<Option<Value>> {
2569 Ok(self.initialization_options.clone())
2570 }
2571
2572 async fn label_for_completion(
2573 &self,
2574 item: &lsp::CompletionItem,
2575 language: &Arc<Language>,
2576 ) -> Option<CodeLabel> {
2577 let label_for_completion = self.label_for_completion.as_ref()?;
2578 label_for_completion(item, language)
2579 }
2580
2581 fn is_extension(&self) -> bool {
2582 false
2583 }
2584}
2585
2586enum Capture<'a> {
2587 Required(&'static str, &'a mut u32),
2588 Optional(&'static str, &'a mut Option<u32>),
2589}
2590
2591fn populate_capture_indices(
2592 query: &Query,
2593 language_name: &LanguageName,
2594 query_type: &str,
2595 expected_prefixes: &[&str],
2596 captures: &mut [Capture<'_>],
2597) -> bool {
2598 let mut found_required_indices = Vec::new();
2599 'outer: for (ix, name) in query.capture_names().iter().enumerate() {
2600 for (required_ix, capture) in captures.iter_mut().enumerate() {
2601 match capture {
2602 Capture::Required(capture_name, index) if capture_name == name => {
2603 **index = ix as u32;
2604 found_required_indices.push(required_ix);
2605 continue 'outer;
2606 }
2607 Capture::Optional(capture_name, index) if capture_name == name => {
2608 **index = Some(ix as u32);
2609 continue 'outer;
2610 }
2611 _ => {}
2612 }
2613 }
2614 if !name.starts_with("_")
2615 && !expected_prefixes
2616 .iter()
2617 .any(|&prefix| name.starts_with(prefix))
2618 {
2619 log::warn!(
2620 "unrecognized capture name '{}' in {} {} TreeSitter query \
2621 (suppress this warning by prefixing with '_')",
2622 name,
2623 language_name,
2624 query_type
2625 );
2626 }
2627 }
2628 let mut missing_required_captures = Vec::new();
2629 for (capture_ix, capture) in captures.iter().enumerate() {
2630 if let Capture::Required(capture_name, _) = capture
2631 && !found_required_indices.contains(&capture_ix)
2632 {
2633 missing_required_captures.push(*capture_name);
2634 }
2635 }
2636 let success = missing_required_captures.is_empty();
2637 if !success {
2638 log::error!(
2639 "missing required capture(s) in {} {} TreeSitter query: {}",
2640 language_name,
2641 query_type,
2642 missing_required_captures.join(", ")
2643 );
2644 }
2645 success
2646}
2647
2648pub fn point_to_lsp(point: PointUtf16) -> lsp::Position {
2649 lsp::Position::new(point.row, point.column)
2650}
2651
2652pub fn point_from_lsp(point: lsp::Position) -> Unclipped<PointUtf16> {
2653 Unclipped(PointUtf16::new(point.line, point.character))
2654}
2655
2656pub fn range_to_lsp(range: Range<PointUtf16>) -> Result<lsp::Range> {
2657 anyhow::ensure!(
2658 range.start <= range.end,
2659 "Inverted range provided to an LSP request: {:?}-{:?}",
2660 range.start,
2661 range.end
2662 );
2663 Ok(lsp::Range {
2664 start: point_to_lsp(range.start),
2665 end: point_to_lsp(range.end),
2666 })
2667}
2668
2669pub fn range_from_lsp(range: lsp::Range) -> Range<Unclipped<PointUtf16>> {
2670 let mut start = point_from_lsp(range.start);
2671 let mut end = point_from_lsp(range.end);
2672 if start > end {
2673 // We debug instead of warn so that this is not logged by default unless explicitly requested.
2674 // Using warn would write to the log file, and since we receive an enormous amount of
2675 // range_from_lsp calls (especially during completions), that can hang the main thread.
2676 //
2677 // See issue #36223.
2678 zlog::debug!("range_from_lsp called with inverted range {start:?}-{end:?}");
2679 mem::swap(&mut start, &mut end);
2680 }
2681 start..end
2682}
2683
2684#[doc(hidden)]
2685#[cfg(any(test, feature = "test-support"))]
2686pub fn rust_lang() -> Arc<Language> {
2687 use std::borrow::Cow;
2688
2689 let language = Language::new(
2690 LanguageConfig {
2691 name: "Rust".into(),
2692 matcher: LanguageMatcher {
2693 path_suffixes: vec!["rs".to_string()],
2694 ..Default::default()
2695 },
2696 line_comments: vec!["// ".into(), "/// ".into(), "//! ".into()],
2697 ..Default::default()
2698 },
2699 Some(tree_sitter_rust::LANGUAGE.into()),
2700 )
2701 .with_queries(LanguageQueries {
2702 outline: Some(Cow::from(include_str!(
2703 "../../languages/src/rust/outline.scm"
2704 ))),
2705 indents: Some(Cow::from(include_str!(
2706 "../../languages/src/rust/indents.scm"
2707 ))),
2708 brackets: Some(Cow::from(include_str!(
2709 "../../languages/src/rust/brackets.scm"
2710 ))),
2711 text_objects: Some(Cow::from(include_str!(
2712 "../../languages/src/rust/textobjects.scm"
2713 ))),
2714 highlights: Some(Cow::from(include_str!(
2715 "../../languages/src/rust/highlights.scm"
2716 ))),
2717 embedding: Some(Cow::from(include_str!(
2718 "../../languages/src/rust/embedding.scm"
2719 ))),
2720 injections: Some(Cow::from(include_str!(
2721 "../../languages/src/rust/injections.scm"
2722 ))),
2723 overrides: Some(Cow::from(include_str!(
2724 "../../languages/src/rust/overrides.scm"
2725 ))),
2726 redactions: None,
2727 runnables: Some(Cow::from(include_str!(
2728 "../../languages/src/rust/runnables.scm"
2729 ))),
2730 debugger: Some(Cow::from(include_str!(
2731 "../../languages/src/rust/debugger.scm"
2732 ))),
2733 imports: Some(Cow::from(include_str!(
2734 "../../languages/src/rust/imports.scm"
2735 ))),
2736 })
2737 .expect("Could not parse queries");
2738 Arc::new(language)
2739}
2740
2741#[doc(hidden)]
2742#[cfg(any(test, feature = "test-support"))]
2743pub fn markdown_lang() -> Arc<Language> {
2744 use std::borrow::Cow;
2745
2746 let language = Language::new(
2747 LanguageConfig {
2748 name: "Markdown".into(),
2749 matcher: LanguageMatcher {
2750 path_suffixes: vec!["md".into()],
2751 ..Default::default()
2752 },
2753 ..LanguageConfig::default()
2754 },
2755 Some(tree_sitter_md::LANGUAGE.into()),
2756 )
2757 .with_queries(LanguageQueries {
2758 brackets: Some(Cow::from(include_str!(
2759 "../../languages/src/markdown/brackets.scm"
2760 ))),
2761 injections: Some(Cow::from(include_str!(
2762 "../../languages/src/markdown/injections.scm"
2763 ))),
2764 highlights: Some(Cow::from(include_str!(
2765 "../../languages/src/markdown/highlights.scm"
2766 ))),
2767 indents: Some(Cow::from(include_str!(
2768 "../../languages/src/markdown/indents.scm"
2769 ))),
2770 outline: Some(Cow::from(include_str!(
2771 "../../languages/src/markdown/outline.scm"
2772 ))),
2773 ..LanguageQueries::default()
2774 })
2775 .expect("Could not parse markdown queries");
2776 Arc::new(language)
2777}
2778
2779#[cfg(test)]
2780mod tests {
2781 use super::*;
2782 use gpui::TestAppContext;
2783 use pretty_assertions::assert_matches;
2784
2785 #[gpui::test(iterations = 10)]
2786 async fn test_language_loading(cx: &mut TestAppContext) {
2787 let languages = LanguageRegistry::test(cx.executor());
2788 let languages = Arc::new(languages);
2789 languages.register_native_grammars([
2790 ("json", tree_sitter_json::LANGUAGE),
2791 ("rust", tree_sitter_rust::LANGUAGE),
2792 ]);
2793 languages.register_test_language(LanguageConfig {
2794 name: "JSON".into(),
2795 grammar: Some("json".into()),
2796 matcher: LanguageMatcher {
2797 path_suffixes: vec!["json".into()],
2798 ..Default::default()
2799 },
2800 ..Default::default()
2801 });
2802 languages.register_test_language(LanguageConfig {
2803 name: "Rust".into(),
2804 grammar: Some("rust".into()),
2805 matcher: LanguageMatcher {
2806 path_suffixes: vec!["rs".into()],
2807 ..Default::default()
2808 },
2809 ..Default::default()
2810 });
2811 assert_eq!(
2812 languages.language_names(),
2813 &[
2814 LanguageName::new_static("JSON"),
2815 LanguageName::new_static("Plain Text"),
2816 LanguageName::new_static("Rust"),
2817 ]
2818 );
2819
2820 let rust1 = languages.language_for_name("Rust");
2821 let rust2 = languages.language_for_name("Rust");
2822
2823 // Ensure language is still listed even if it's being loaded.
2824 assert_eq!(
2825 languages.language_names(),
2826 &[
2827 LanguageName::new_static("JSON"),
2828 LanguageName::new_static("Plain Text"),
2829 LanguageName::new_static("Rust"),
2830 ]
2831 );
2832
2833 let (rust1, rust2) = futures::join!(rust1, rust2);
2834 assert!(Arc::ptr_eq(&rust1.unwrap(), &rust2.unwrap()));
2835
2836 // Ensure language is still listed even after loading it.
2837 assert_eq!(
2838 languages.language_names(),
2839 &[
2840 LanguageName::new_static("JSON"),
2841 LanguageName::new_static("Plain Text"),
2842 LanguageName::new_static("Rust"),
2843 ]
2844 );
2845
2846 // Loading an unknown language returns an error.
2847 assert!(languages.language_for_name("Unknown").await.is_err());
2848 }
2849
2850 #[gpui::test]
2851 async fn test_completion_label_omits_duplicate_data() {
2852 let regular_completion_item_1 = lsp::CompletionItem {
2853 label: "regular1".to_string(),
2854 detail: Some("detail1".to_string()),
2855 label_details: Some(lsp::CompletionItemLabelDetails {
2856 detail: None,
2857 description: Some("description 1".to_string()),
2858 }),
2859 ..lsp::CompletionItem::default()
2860 };
2861
2862 let regular_completion_item_2 = lsp::CompletionItem {
2863 label: "regular2".to_string(),
2864 label_details: Some(lsp::CompletionItemLabelDetails {
2865 detail: None,
2866 description: Some("description 2".to_string()),
2867 }),
2868 ..lsp::CompletionItem::default()
2869 };
2870
2871 let completion_item_with_duplicate_detail_and_proper_description = lsp::CompletionItem {
2872 detail: Some(regular_completion_item_1.label.clone()),
2873 ..regular_completion_item_1.clone()
2874 };
2875
2876 let completion_item_with_duplicate_detail = lsp::CompletionItem {
2877 detail: Some(regular_completion_item_1.label.clone()),
2878 label_details: None,
2879 ..regular_completion_item_1.clone()
2880 };
2881
2882 let completion_item_with_duplicate_description = lsp::CompletionItem {
2883 label_details: Some(lsp::CompletionItemLabelDetails {
2884 detail: None,
2885 description: Some(regular_completion_item_2.label.clone()),
2886 }),
2887 ..regular_completion_item_2.clone()
2888 };
2889
2890 assert_eq!(
2891 CodeLabel::fallback_for_completion(®ular_completion_item_1, None).text,
2892 format!(
2893 "{} {}",
2894 regular_completion_item_1.label,
2895 regular_completion_item_1.detail.unwrap()
2896 ),
2897 "LSP completion items with both detail and label_details.description should prefer detail"
2898 );
2899 assert_eq!(
2900 CodeLabel::fallback_for_completion(®ular_completion_item_2, None).text,
2901 format!(
2902 "{} {}",
2903 regular_completion_item_2.label,
2904 regular_completion_item_2
2905 .label_details
2906 .as_ref()
2907 .unwrap()
2908 .description
2909 .as_ref()
2910 .unwrap()
2911 ),
2912 "LSP completion items without detail but with label_details.description should use that"
2913 );
2914 assert_eq!(
2915 CodeLabel::fallback_for_completion(
2916 &completion_item_with_duplicate_detail_and_proper_description,
2917 None
2918 )
2919 .text,
2920 format!(
2921 "{} {}",
2922 regular_completion_item_1.label,
2923 regular_completion_item_1
2924 .label_details
2925 .as_ref()
2926 .unwrap()
2927 .description
2928 .as_ref()
2929 .unwrap()
2930 ),
2931 "LSP completion items with both detail and label_details.description should prefer description only if the detail duplicates the completion label"
2932 );
2933 assert_eq!(
2934 CodeLabel::fallback_for_completion(&completion_item_with_duplicate_detail, None).text,
2935 regular_completion_item_1.label,
2936 "LSP completion items with duplicate label and detail, should omit the detail"
2937 );
2938 assert_eq!(
2939 CodeLabel::fallback_for_completion(&completion_item_with_duplicate_description, None)
2940 .text,
2941 regular_completion_item_2.label,
2942 "LSP completion items with duplicate label and detail, should omit the detail"
2943 );
2944 }
2945
2946 #[test]
2947 fn test_deserializing_comments_backwards_compat() {
2948 // current version of `block_comment` and `documentation_comment` work
2949 {
2950 let config: LanguageConfig = ::toml::from_str(
2951 r#"
2952 name = "Foo"
2953 block_comment = { start = "a", end = "b", prefix = "c", tab_size = 1 }
2954 documentation_comment = { start = "d", end = "e", prefix = "f", tab_size = 2 }
2955 "#,
2956 )
2957 .unwrap();
2958 assert_matches!(config.block_comment, Some(BlockCommentConfig { .. }));
2959 assert_matches!(
2960 config.documentation_comment,
2961 Some(BlockCommentConfig { .. })
2962 );
2963
2964 let block_config = config.block_comment.unwrap();
2965 assert_eq!(block_config.start.as_ref(), "a");
2966 assert_eq!(block_config.end.as_ref(), "b");
2967 assert_eq!(block_config.prefix.as_ref(), "c");
2968 assert_eq!(block_config.tab_size, 1);
2969
2970 let doc_config = config.documentation_comment.unwrap();
2971 assert_eq!(doc_config.start.as_ref(), "d");
2972 assert_eq!(doc_config.end.as_ref(), "e");
2973 assert_eq!(doc_config.prefix.as_ref(), "f");
2974 assert_eq!(doc_config.tab_size, 2);
2975 }
2976
2977 // former `documentation` setting is read into `documentation_comment`
2978 {
2979 let config: LanguageConfig = ::toml::from_str(
2980 r#"
2981 name = "Foo"
2982 documentation = { start = "a", end = "b", prefix = "c", tab_size = 1}
2983 "#,
2984 )
2985 .unwrap();
2986 assert_matches!(
2987 config.documentation_comment,
2988 Some(BlockCommentConfig { .. })
2989 );
2990
2991 let config = config.documentation_comment.unwrap();
2992 assert_eq!(config.start.as_ref(), "a");
2993 assert_eq!(config.end.as_ref(), "b");
2994 assert_eq!(config.prefix.as_ref(), "c");
2995 assert_eq!(config.tab_size, 1);
2996 }
2997
2998 // old block_comment format is read into BlockCommentConfig
2999 {
3000 let config: LanguageConfig = ::toml::from_str(
3001 r#"
3002 name = "Foo"
3003 block_comment = ["a", "b"]
3004 "#,
3005 )
3006 .unwrap();
3007 assert_matches!(config.block_comment, Some(BlockCommentConfig { .. }));
3008
3009 let config = config.block_comment.unwrap();
3010 assert_eq!(config.start.as_ref(), "a");
3011 assert_eq!(config.end.as_ref(), "b");
3012 assert_eq!(config.prefix.as_ref(), "");
3013 assert_eq!(config.tab_size, 0);
3014 }
3015 }
3016}