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