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