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