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