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;
11mod diagnostic_set;
12mod language_registry;
13
14pub mod language_settings;
15mod manifest;
16pub mod modeline;
17mod outline;
18pub mod proto;
19mod syntax_map;
20mod task_context;
21mod text_diff;
22mod toolchain;
23
24#[cfg(test)]
25pub mod buffer_tests;
26
27pub use crate::language_settings::{AutoIndentMode, EditPredictionsMode, IndentGuideSettings};
28use anyhow::{Context as _, Result};
29use async_trait::async_trait;
30use collections::{HashMap, HashSet};
31use futures::Future;
32use futures::future::LocalBoxFuture;
33use futures::lock::OwnedMutexGuard;
34use gpui::{App, AsyncApp, Entity};
35use http_client::HttpClient;
36
37pub use language_core::highlight_map::{HighlightId, HighlightMap};
38
39pub use language_core::{
40 BlockCommentConfig, BracketPair, BracketPairConfig, BracketPairContent, BracketsConfig,
41 BracketsPatternConfig, CodeLabel, CodeLabelBuilder, DebugVariablesConfig, DebuggerTextObject,
42 DecreaseIndentConfig, Grammar, GrammarId, HighlightsConfig, IndentConfig, InjectionConfig,
43 InjectionPatternConfig, JsxTagAutoCloseConfig, LanguageConfig, LanguageConfigOverride,
44 LanguageId, LanguageMatcher, OrderedListConfig, OutlineConfig, Override, OverrideConfig,
45 OverrideEntry, PromptResponseContext, RedactionConfig, RunnableCapture, RunnableConfig,
46 SoftWrap, Symbol, TaskListConfig, TextObject, TextObjectConfig, ToLspPosition,
47 WrapCharactersConfig, auto_indent_using_last_non_empty_line_default, deserialize_regex,
48 deserialize_regex_vec, regex_json_schema, regex_vec_json_schema, serialize_regex,
49};
50pub use language_registry::{
51 LanguageName, LanguageServerStatusUpdate, LoadedLanguage, ServerHealth,
52};
53use lsp::{
54 CodeActionKind, InitializeParams, LanguageServerBinary, LanguageServerBinaryOptions, Uri,
55};
56pub use manifest::{ManifestDelegate, ManifestName, ManifestProvider, ManifestQuery};
57pub use modeline::{ModelineSettings, parse_modeline};
58use parking_lot::Mutex;
59use regex::Regex;
60use semver::Version;
61use serde_json::Value;
62use settings::WorktreeId;
63use smol::future::FutureExt as _;
64use std::{
65 ffi::OsStr,
66 fmt::Debug,
67 hash::Hash,
68 mem,
69 ops::{DerefMut, Range},
70 path::{Path, PathBuf},
71 str,
72 sync::{Arc, LazyLock},
73};
74use syntax_map::{QueryCursorHandle, SyntaxSnapshot};
75use task::RunnableTag;
76pub use task_context::{ContextLocation, ContextProvider, RunnableRange};
77pub use text_diff::{
78 DiffOptions, apply_diff_patch, apply_reversed_diff_patch, char_diff, line_diff, text_diff,
79 text_diff_with_options, unified_diff, unified_diff_with_context, unified_diff_with_offsets,
80 word_diff_ranges,
81};
82use theme::SyntaxTheme;
83pub use toolchain::{
84 LanguageToolchainStore, LocalLanguageToolchainStore, Toolchain, ToolchainList, ToolchainLister,
85 ToolchainMetadata, ToolchainScope,
86};
87use tree_sitter::{self, QueryCursor, WasmStore, wasmtime};
88use util::rel_path::RelPath;
89
90pub use buffer::Operation;
91pub use buffer::*;
92pub use diagnostic::{Diagnostic, DiagnosticSourceKind};
93pub use diagnostic_set::{DiagnosticEntry, DiagnosticEntryRef, DiagnosticGroup};
94pub use language_registry::{
95 AvailableLanguage, BinaryStatus, LanguageNotFound, LanguageQueries, LanguageRegistry,
96 QUERY_FILENAME_PREFIXES,
97};
98pub use lsp::{LanguageServerId, LanguageServerName};
99pub use outline::*;
100pub use syntax_map::{
101 OwnedSyntaxLayer, SyntaxLayer, SyntaxMapMatches, ToTreeSitterPoint, TreeSitterOptions,
102};
103pub use text::{AnchorRangeExt, LineEnding};
104pub use tree_sitter::{Node, Parser, Tree, TreeCursor};
105
106pub(crate) fn to_settings_soft_wrap(value: language_core::SoftWrap) -> settings::SoftWrap {
107 match value {
108 language_core::SoftWrap::None => settings::SoftWrap::None,
109 language_core::SoftWrap::PreferLine => settings::SoftWrap::PreferLine,
110 language_core::SoftWrap::EditorWidth => settings::SoftWrap::EditorWidth,
111 language_core::SoftWrap::Bounded => settings::SoftWrap::Bounded,
112 }
113}
114
115static QUERY_CURSORS: Mutex<Vec<QueryCursor>> = Mutex::new(vec![]);
116static PARSERS: Mutex<Vec<Parser>> = Mutex::new(vec![]);
117
118#[ztracing::instrument(skip_all)]
119pub fn with_parser<F, R>(func: F) -> R
120where
121 F: FnOnce(&mut Parser) -> R,
122{
123 let mut parser = PARSERS.lock().pop().unwrap_or_else(|| {
124 let mut parser = Parser::new();
125 parser
126 .set_wasm_store(WasmStore::new(&WASM_ENGINE).unwrap())
127 .unwrap();
128 parser
129 });
130 parser.set_included_ranges(&[]).unwrap();
131 let result = func(&mut parser);
132 PARSERS.lock().push(parser);
133 result
134}
135
136pub fn with_query_cursor<F, R>(func: F) -> R
137where
138 F: FnOnce(&mut QueryCursor) -> R,
139{
140 let mut cursor = QueryCursorHandle::new();
141 func(cursor.deref_mut())
142}
143
144static WASM_ENGINE: LazyLock<wasmtime::Engine> = LazyLock::new(|| {
145 wasmtime::Engine::new(&wasmtime::Config::new()).expect("Failed to create Wasmtime engine")
146});
147
148/// A shared grammar for plain text, exposed for reuse by downstream crates.
149pub static PLAIN_TEXT: LazyLock<Arc<Language>> = LazyLock::new(|| {
150 Arc::new(Language::new(
151 LanguageConfig {
152 name: "Plain Text".into(),
153 soft_wrap: Some(SoftWrap::EditorWidth),
154 matcher: LanguageMatcher {
155 path_suffixes: vec!["txt".to_owned()],
156 first_line_pattern: None,
157 modeline_aliases: vec!["text".to_owned(), "txt".to_owned()],
158 },
159 brackets: BracketPairConfig {
160 pairs: vec![
161 BracketPair {
162 start: "(".to_string(),
163 end: ")".to_string(),
164 close: true,
165 surround: true,
166 newline: false,
167 },
168 BracketPair {
169 start: "[".to_string(),
170 end: "]".to_string(),
171 close: true,
172 surround: true,
173 newline: false,
174 },
175 BracketPair {
176 start: "{".to_string(),
177 end: "}".to_string(),
178 close: true,
179 surround: true,
180 newline: false,
181 },
182 BracketPair {
183 start: "\"".to_string(),
184 end: "\"".to_string(),
185 close: true,
186 surround: true,
187 newline: false,
188 },
189 BracketPair {
190 start: "'".to_string(),
191 end: "'".to_string(),
192 close: true,
193 surround: true,
194 newline: false,
195 },
196 ],
197 disabled_scopes_by_bracket_ix: Default::default(),
198 },
199 ..Default::default()
200 },
201 None,
202 ))
203});
204
205#[derive(Debug, Clone, PartialEq, Eq, Hash)]
206pub struct Location {
207 pub buffer: Entity<Buffer>,
208 pub range: Range<Anchor>,
209}
210
211type ServerBinaryCache = futures::lock::Mutex<Option<(bool, LanguageServerBinary)>>;
212type DownloadableLanguageServerBinary = LocalBoxFuture<'static, Result<LanguageServerBinary>>;
213pub type LanguageServerBinaryLocations = LocalBoxFuture<
214 'static,
215 (
216 Result<LanguageServerBinary>,
217 Option<DownloadableLanguageServerBinary>,
218 ),
219>;
220/// Represents a Language Server, with certain cached sync properties.
221/// Uses [`LspAdapter`] under the hood, but calls all 'static' methods
222/// once at startup, and caches the results.
223pub struct CachedLspAdapter {
224 pub name: LanguageServerName,
225 pub disk_based_diagnostic_sources: Vec<String>,
226 pub disk_based_diagnostics_progress_token: Option<String>,
227 language_ids: HashMap<LanguageName, String>,
228 pub adapter: Arc<dyn LspAdapter>,
229 cached_binary: Arc<ServerBinaryCache>,
230}
231
232impl Debug for CachedLspAdapter {
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 f.debug_struct("CachedLspAdapter")
235 .field("name", &self.name)
236 .field(
237 "disk_based_diagnostic_sources",
238 &self.disk_based_diagnostic_sources,
239 )
240 .field(
241 "disk_based_diagnostics_progress_token",
242 &self.disk_based_diagnostics_progress_token,
243 )
244 .field("language_ids", &self.language_ids)
245 .finish_non_exhaustive()
246 }
247}
248
249impl CachedLspAdapter {
250 pub fn new(adapter: Arc<dyn LspAdapter>) -> Arc<Self> {
251 let name = adapter.name();
252 let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources();
253 let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token();
254 let language_ids = adapter.language_ids();
255
256 Arc::new(CachedLspAdapter {
257 name,
258 disk_based_diagnostic_sources,
259 disk_based_diagnostics_progress_token,
260 language_ids,
261 adapter,
262 cached_binary: Default::default(),
263 })
264 }
265
266 pub fn name(&self) -> LanguageServerName {
267 self.adapter.name()
268 }
269
270 pub async fn get_language_server_command(
271 self: Arc<Self>,
272 delegate: Arc<dyn LspAdapterDelegate>,
273 toolchains: Option<Toolchain>,
274 binary_options: LanguageServerBinaryOptions,
275 cx: &mut AsyncApp,
276 ) -> LanguageServerBinaryLocations {
277 let cached_binary = self.cached_binary.clone().lock_owned().await;
278 self.adapter.clone().get_language_server_command(
279 delegate,
280 toolchains,
281 binary_options,
282 cached_binary,
283 cx.clone(),
284 )
285 }
286
287 pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
288 self.adapter.code_action_kinds()
289 }
290
291 pub fn process_diagnostics(
292 &self,
293 params: &mut lsp::PublishDiagnosticsParams,
294 server_id: LanguageServerId,
295 ) {
296 self.adapter.process_diagnostics(params, server_id)
297 }
298
299 pub fn retain_old_diagnostic(&self, previous_diagnostic: &Diagnostic) -> bool {
300 self.adapter.retain_old_diagnostic(previous_diagnostic)
301 }
302
303 pub fn underline_diagnostic(&self, diagnostic: &lsp::Diagnostic) -> bool {
304 self.adapter.underline_diagnostic(diagnostic)
305 }
306
307 pub fn diagnostic_message_to_markdown(&self, message: &str) -> Option<String> {
308 self.adapter.diagnostic_message_to_markdown(message)
309 }
310
311 pub async fn process_completions(&self, completion_items: &mut [lsp::CompletionItem]) {
312 self.adapter.process_completions(completion_items).await
313 }
314
315 pub async fn labels_for_completions(
316 &self,
317 completion_items: &[lsp::CompletionItem],
318 language: &Arc<Language>,
319 ) -> Result<Vec<Option<CodeLabel>>> {
320 self.adapter
321 .clone()
322 .labels_for_completions(completion_items, language)
323 .await
324 }
325
326 pub async fn labels_for_symbols(
327 &self,
328 symbols: &[Symbol],
329 language: &Arc<Language>,
330 ) -> Result<Vec<Option<CodeLabel>>> {
331 self.adapter
332 .clone()
333 .labels_for_symbols(symbols, language)
334 .await
335 }
336
337 pub fn language_id(&self, language_name: &LanguageName) -> String {
338 self.language_ids
339 .get(language_name)
340 .cloned()
341 .unwrap_or_else(|| language_name.lsp_id())
342 }
343
344 pub async fn initialization_options_schema(
345 &self,
346 delegate: &Arc<dyn LspAdapterDelegate>,
347 cx: &mut AsyncApp,
348 ) -> Option<serde_json::Value> {
349 self.adapter
350 .clone()
351 .initialization_options_schema(
352 delegate,
353 self.cached_binary.clone().lock_owned().await,
354 cx,
355 )
356 .await
357 }
358
359 pub async fn settings_schema(
360 &self,
361 delegate: &Arc<dyn LspAdapterDelegate>,
362 cx: &mut AsyncApp,
363 ) -> Option<serde_json::Value> {
364 self.adapter
365 .clone()
366 .settings_schema(delegate, self.cached_binary.clone().lock_owned().await, cx)
367 .await
368 }
369
370 pub fn process_prompt_response(&self, context: &PromptResponseContext, cx: &mut AsyncApp) {
371 self.adapter.process_prompt_response(context, cx)
372 }
373}
374
375/// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application
376// e.g. to display a notification or fetch data from the web.
377#[async_trait]
378pub trait LspAdapterDelegate: Send + Sync {
379 fn show_notification(&self, message: &str, cx: &mut App);
380 fn http_client(&self) -> Arc<dyn HttpClient>;
381 fn worktree_id(&self) -> WorktreeId;
382 fn worktree_root_path(&self) -> &Path;
383 fn resolve_relative_path(&self, path: PathBuf) -> PathBuf;
384 fn update_status(&self, language: LanguageServerName, status: BinaryStatus);
385 fn registered_lsp_adapters(&self) -> Vec<Arc<dyn LspAdapter>>;
386 async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>>;
387
388 async fn npm_package_installed_version(
389 &self,
390 package_name: &str,
391 ) -> Result<Option<(PathBuf, Version)>>;
392 async fn which(&self, command: &OsStr) -> Option<PathBuf>;
393 async fn shell_env(&self) -> HashMap<String, String>;
394 async fn read_text_file(&self, path: &RelPath) -> Result<String>;
395 async fn try_exec(&self, binary: LanguageServerBinary) -> Result<()>;
396}
397
398#[async_trait(?Send)]
399pub trait LspAdapter: 'static + Send + Sync + DynLspInstaller {
400 fn name(&self) -> LanguageServerName;
401
402 fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams, _: LanguageServerId) {}
403
404 /// When processing new `lsp::PublishDiagnosticsParams` diagnostics, whether to retain previous one(s) or not.
405 fn retain_old_diagnostic(&self, _previous_diagnostic: &Diagnostic) -> bool {
406 false
407 }
408
409 /// Whether to underline a given diagnostic or not, when rendering in the editor.
410 ///
411 /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnosticTag
412 /// states that
413 /// > Clients are allowed to render diagnostics with this tag faded out instead of having an error squiggle.
414 /// for the unnecessary diagnostics, so do not underline them.
415 fn underline_diagnostic(&self, _diagnostic: &lsp::Diagnostic) -> bool {
416 true
417 }
418
419 /// Post-processes completions provided by the language server.
420 async fn process_completions(&self, _: &mut [lsp::CompletionItem]) {}
421
422 fn diagnostic_message_to_markdown(&self, _message: &str) -> Option<String> {
423 None
424 }
425
426 async fn labels_for_completions(
427 self: Arc<Self>,
428 completions: &[lsp::CompletionItem],
429 language: &Arc<Language>,
430 ) -> Result<Vec<Option<CodeLabel>>> {
431 let mut labels = Vec::new();
432 for (ix, completion) in completions.iter().enumerate() {
433 let label = self.label_for_completion(completion, language).await;
434 if let Some(label) = label {
435 labels.resize(ix + 1, None);
436 *labels.last_mut().unwrap() = Some(label);
437 }
438 }
439 Ok(labels)
440 }
441
442 async fn label_for_completion(
443 &self,
444 _: &lsp::CompletionItem,
445 _: &Arc<Language>,
446 ) -> Option<CodeLabel> {
447 None
448 }
449
450 async fn labels_for_symbols(
451 self: Arc<Self>,
452 symbols: &[Symbol],
453 language: &Arc<Language>,
454 ) -> Result<Vec<Option<CodeLabel>>> {
455 let mut labels = Vec::new();
456 for (ix, symbol) in symbols.iter().enumerate() {
457 let label = self.label_for_symbol(symbol, language).await;
458 if let Some(label) = label {
459 labels.resize(ix + 1, None);
460 *labels.last_mut().unwrap() = Some(label);
461 }
462 }
463 Ok(labels)
464 }
465
466 async fn label_for_symbol(
467 &self,
468 _symbol: &Symbol,
469 _language: &Arc<Language>,
470 ) -> Option<CodeLabel> {
471 None
472 }
473
474 /// Returns initialization options that are going to be sent to a LSP server as a part of [`lsp::InitializeParams`]
475 async fn initialization_options(
476 self: Arc<Self>,
477 _: &Arc<dyn LspAdapterDelegate>,
478 _cx: &mut AsyncApp,
479 ) -> Result<Option<Value>> {
480 Ok(None)
481 }
482
483 /// Returns the JSON schema of the initialization_options for the language server.
484 async fn initialization_options_schema(
485 self: Arc<Self>,
486 _delegate: &Arc<dyn LspAdapterDelegate>,
487 _cached_binary: OwnedMutexGuard<Option<(bool, LanguageServerBinary)>>,
488 _cx: &mut AsyncApp,
489 ) -> Option<serde_json::Value> {
490 None
491 }
492
493 /// Returns the JSON schema of the settings for the language server.
494 /// This corresponds to the `settings` field in `LspSettings`, which is used
495 /// to respond to `workspace/configuration` requests from the language server.
496 async fn settings_schema(
497 self: Arc<Self>,
498 _delegate: &Arc<dyn LspAdapterDelegate>,
499 _cached_binary: OwnedMutexGuard<Option<(bool, LanguageServerBinary)>>,
500 _cx: &mut AsyncApp,
501 ) -> Option<serde_json::Value> {
502 None
503 }
504
505 async fn workspace_configuration(
506 self: Arc<Self>,
507 _: &Arc<dyn LspAdapterDelegate>,
508 _: Option<Toolchain>,
509 _: Option<Uri>,
510 _cx: &mut AsyncApp,
511 ) -> Result<Value> {
512 Ok(serde_json::json!({}))
513 }
514
515 async fn additional_initialization_options(
516 self: Arc<Self>,
517 _target_language_server_id: LanguageServerName,
518 _: &Arc<dyn LspAdapterDelegate>,
519 ) -> Result<Option<Value>> {
520 Ok(None)
521 }
522
523 async fn additional_workspace_configuration(
524 self: Arc<Self>,
525 _target_language_server_id: LanguageServerName,
526 _: &Arc<dyn LspAdapterDelegate>,
527 _cx: &mut AsyncApp,
528 ) -> Result<Option<Value>> {
529 Ok(None)
530 }
531
532 /// Returns a list of code actions supported by a given LspAdapter
533 fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
534 None
535 }
536
537 fn disk_based_diagnostic_sources(&self) -> Vec<String> {
538 Default::default()
539 }
540
541 fn disk_based_diagnostics_progress_token(&self) -> Option<String> {
542 None
543 }
544
545 fn language_ids(&self) -> HashMap<LanguageName, String> {
546 HashMap::default()
547 }
548
549 /// Support custom initialize params.
550 fn prepare_initialize_params(
551 &self,
552 original: InitializeParams,
553 _: &App,
554 ) -> Result<InitializeParams> {
555 Ok(original)
556 }
557
558 /// Method only implemented by the default JSON language server adapter.
559 /// Used to provide dynamic reloading of the JSON schemas used to
560 /// provide autocompletion and diagnostics in Zed setting and keybind
561 /// files
562 fn is_primary_zed_json_schema_adapter(&self) -> bool {
563 false
564 }
565
566 /// True for the extension adapter and false otherwise.
567 fn is_extension(&self) -> bool {
568 false
569 }
570
571 /// Called when a user responds to a ShowMessageRequest from this language server.
572 /// This allows adapters to intercept preference selections (like "Always" or "Never")
573 /// for settings that should be persisted to Zed's settings file.
574 fn process_prompt_response(&self, _context: &PromptResponseContext, _cx: &mut AsyncApp) {}
575}
576
577pub trait LspInstaller {
578 type BinaryVersion;
579 fn check_if_user_installed(
580 &self,
581 _: &dyn LspAdapterDelegate,
582 _: Option<Toolchain>,
583 _: &AsyncApp,
584 ) -> impl Future<Output = Option<LanguageServerBinary>> {
585 async { None }
586 }
587
588 fn fetch_latest_server_version(
589 &self,
590 delegate: &dyn LspAdapterDelegate,
591 pre_release: bool,
592 cx: &mut AsyncApp,
593 ) -> impl Future<Output = Result<Self::BinaryVersion>>;
594
595 fn check_if_version_installed(
596 &self,
597 _version: &Self::BinaryVersion,
598 _container_dir: &PathBuf,
599 _delegate: &dyn LspAdapterDelegate,
600 ) -> impl Send + Future<Output = Option<LanguageServerBinary>> {
601 async { None }
602 }
603
604 fn fetch_server_binary(
605 &self,
606 latest_version: Self::BinaryVersion,
607 container_dir: PathBuf,
608 delegate: &dyn LspAdapterDelegate,
609 ) -> impl Send + Future<Output = Result<LanguageServerBinary>>;
610
611 fn cached_server_binary(
612 &self,
613 container_dir: PathBuf,
614 delegate: &dyn LspAdapterDelegate,
615 ) -> impl Future<Output = Option<LanguageServerBinary>>;
616}
617
618#[async_trait(?Send)]
619pub trait DynLspInstaller {
620 async fn try_fetch_server_binary(
621 &self,
622 delegate: &Arc<dyn LspAdapterDelegate>,
623 container_dir: PathBuf,
624 pre_release: bool,
625 cx: &mut AsyncApp,
626 ) -> Result<LanguageServerBinary>;
627
628 fn get_language_server_command(
629 self: Arc<Self>,
630 delegate: Arc<dyn LspAdapterDelegate>,
631 toolchains: Option<Toolchain>,
632 binary_options: LanguageServerBinaryOptions,
633 cached_binary: OwnedMutexGuard<Option<(bool, LanguageServerBinary)>>,
634 cx: AsyncApp,
635 ) -> LanguageServerBinaryLocations;
636}
637
638#[async_trait(?Send)]
639impl<LI, BinaryVersion> DynLspInstaller for LI
640where
641 BinaryVersion: Send + Sync,
642 LI: LspInstaller<BinaryVersion = BinaryVersion> + LspAdapter,
643{
644 async fn try_fetch_server_binary(
645 &self,
646 delegate: &Arc<dyn LspAdapterDelegate>,
647 container_dir: PathBuf,
648 pre_release: bool,
649 cx: &mut AsyncApp,
650 ) -> Result<LanguageServerBinary> {
651 let name = self.name();
652
653 log::debug!("fetching latest version of language server {:?}", name.0);
654 delegate.update_status(name.clone(), BinaryStatus::CheckingForUpdate);
655
656 let latest_version = self
657 .fetch_latest_server_version(delegate.as_ref(), pre_release, cx)
658 .await?;
659
660 if let Some(binary) = cx
661 .background_executor()
662 .await_on_background(self.check_if_version_installed(
663 &latest_version,
664 &container_dir,
665 delegate.as_ref(),
666 ))
667 .await
668 {
669 log::debug!("language server {:?} is already installed", name.0);
670 delegate.update_status(name.clone(), BinaryStatus::None);
671 Ok(binary)
672 } else {
673 log::debug!("downloading language server {:?}", name.0);
674 delegate.update_status(name.clone(), BinaryStatus::Downloading);
675 let binary = cx
676 .background_executor()
677 .await_on_background(self.fetch_server_binary(
678 latest_version,
679 container_dir,
680 delegate.as_ref(),
681 ))
682 .await;
683
684 delegate.update_status(name.clone(), BinaryStatus::None);
685 binary
686 }
687 }
688 fn get_language_server_command(
689 self: Arc<Self>,
690 delegate: Arc<dyn LspAdapterDelegate>,
691 toolchain: Option<Toolchain>,
692 binary_options: LanguageServerBinaryOptions,
693 mut cached_binary: OwnedMutexGuard<Option<(bool, LanguageServerBinary)>>,
694 mut cx: AsyncApp,
695 ) -> LanguageServerBinaryLocations {
696 async move {
697 let cached_binary_deref = cached_binary.deref_mut();
698 // First we check whether the adapter can give us a user-installed binary.
699 // If so, we do *not* want to cache that, because each worktree might give us a different
700 // binary:
701 //
702 // worktree 1: user-installed at `.bin/gopls`
703 // worktree 2: user-installed at `~/bin/gopls`
704 // worktree 3: no gopls found in PATH -> fallback to Zed installation
705 //
706 // We only want to cache when we fall back to the global one,
707 // because we don't want to download and overwrite our global one
708 // for each worktree we might have open.
709 if binary_options.allow_path_lookup
710 && let Some(binary) = self
711 .check_if_user_installed(delegate.as_ref(), toolchain, &mut cx)
712 .await
713 {
714 log::info!(
715 "found user-installed language server for {}. path: {:?}, arguments: {:?}",
716 self.name().0,
717 binary.path,
718 binary.arguments
719 );
720 return (Ok(binary), None);
721 }
722
723 if let Some((pre_release, cached_binary)) = cached_binary_deref
724 && *pre_release == binary_options.pre_release
725 {
726 return (Ok(cached_binary.clone()), None);
727 }
728
729 if !binary_options.allow_binary_download {
730 return (
731 Err(anyhow::anyhow!("downloading language servers disabled")),
732 None,
733 );
734 }
735
736 let Some(container_dir) = delegate.language_server_download_dir(&self.name()).await
737 else {
738 return (
739 Err(anyhow::anyhow!("no language server download dir defined")),
740 None,
741 );
742 };
743
744 let last_downloaded_binary = self
745 .cached_server_binary(container_dir.to_path_buf(), delegate.as_ref())
746 .await
747 .context(
748 "did not find existing language server binary, falling back to downloading",
749 );
750 let download_binary = async move {
751 let mut binary = self
752 .try_fetch_server_binary(
753 &delegate,
754 container_dir.to_path_buf(),
755 binary_options.pre_release,
756 &mut cx,
757 )
758 .await;
759
760 if let Err(error) = binary.as_ref() {
761 if let Some(prev_downloaded_binary) = self
762 .cached_server_binary(container_dir.to_path_buf(), delegate.as_ref())
763 .await
764 {
765 log::info!(
766 "failed to fetch newest version of language server {:?}. \
767 error: {:?}, falling back to using {:?}",
768 self.name(),
769 error,
770 prev_downloaded_binary.path
771 );
772 binary = Ok(prev_downloaded_binary);
773 } else {
774 delegate.update_status(
775 self.name(),
776 BinaryStatus::Failed {
777 error: format!("{error:?}"),
778 },
779 );
780 }
781 }
782
783 if let Ok(binary) = &binary {
784 *cached_binary = Some((binary_options.pre_release, binary.clone()));
785 }
786
787 binary
788 }
789 .boxed_local();
790 (last_downloaded_binary, Some(download_binary))
791 }
792 .boxed_local()
793 }
794}
795
796/// Represents a language for the given range. Some languages (e.g. HTML)
797/// interleave several languages together, thus a single buffer might actually contain
798/// several nested scopes.
799#[derive(Clone, Debug)]
800pub struct LanguageScope {
801 language: Arc<Language>,
802 override_id: Option<u32>,
803}
804
805#[doc(hidden)]
806#[cfg(any(test, feature = "test-support"))]
807pub struct FakeLspAdapter {
808 pub name: &'static str,
809 pub initialization_options: Option<Value>,
810 pub prettier_plugins: Vec<&'static str>,
811 pub disk_based_diagnostics_progress_token: Option<String>,
812 pub disk_based_diagnostics_sources: Vec<String>,
813 pub language_server_binary: LanguageServerBinary,
814
815 pub capabilities: lsp::ServerCapabilities,
816 pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
817 pub label_for_completion: Option<
818 Box<
819 dyn 'static
820 + Send
821 + Sync
822 + Fn(&lsp::CompletionItem, &Arc<Language>) -> Option<CodeLabel>,
823 >,
824 >,
825}
826
827pub struct Language {
828 pub(crate) id: LanguageId,
829 pub(crate) config: LanguageConfig,
830 pub(crate) grammar: Option<Arc<Grammar>>,
831 pub(crate) context_provider: Option<Arc<dyn ContextProvider>>,
832 pub(crate) toolchain: Option<Arc<dyn ToolchainLister>>,
833 pub(crate) manifest_name: Option<ManifestName>,
834}
835
836impl Language {
837 pub fn new(config: LanguageConfig, ts_language: Option<tree_sitter::Language>) -> Self {
838 Self::new_with_id(LanguageId::new(), config, ts_language)
839 }
840
841 pub fn id(&self) -> LanguageId {
842 self.id
843 }
844
845 fn new_with_id(
846 id: LanguageId,
847 config: LanguageConfig,
848 ts_language: Option<tree_sitter::Language>,
849 ) -> Self {
850 Self {
851 id,
852 config,
853 grammar: ts_language.map(|ts_language| Arc::new(Grammar::new(ts_language))),
854 context_provider: None,
855 toolchain: None,
856 manifest_name: None,
857 }
858 }
859
860 pub fn with_context_provider(mut self, provider: Option<Arc<dyn ContextProvider>>) -> Self {
861 self.context_provider = provider;
862 self
863 }
864
865 pub fn with_toolchain_lister(mut self, provider: Option<Arc<dyn ToolchainLister>>) -> Self {
866 self.toolchain = provider;
867 self
868 }
869
870 pub fn with_manifest(mut self, name: Option<ManifestName>) -> Self {
871 self.manifest_name = name;
872 self
873 }
874
875 pub fn with_queries(mut self, queries: LanguageQueries) -> Result<Self> {
876 if let Some(grammar) = self.grammar.take() {
877 let grammar =
878 Arc::try_unwrap(grammar).map_err(|_| anyhow::anyhow!("cannot mutate grammar"))?;
879 let grammar = grammar.with_queries(queries, &mut self.config)?;
880 self.grammar = Some(Arc::new(grammar));
881 }
882 Ok(self)
883 }
884
885 pub fn with_highlights_query(self, source: &str) -> Result<Self> {
886 self.with_grammar_query(|grammar| grammar.with_highlights_query(source))
887 }
888
889 pub fn with_runnable_query(self, source: &str) -> Result<Self> {
890 self.with_grammar_query(|grammar| grammar.with_runnable_query(source))
891 }
892
893 pub fn with_outline_query(self, source: &str) -> Result<Self> {
894 self.with_grammar_query_and_name(|grammar, name| grammar.with_outline_query(source, name))
895 }
896
897 pub fn with_text_object_query(self, source: &str) -> Result<Self> {
898 self.with_grammar_query_and_name(|grammar, name| {
899 grammar.with_text_object_query(source, name)
900 })
901 }
902
903 pub fn with_debug_variables_query(self, source: &str) -> Result<Self> {
904 self.with_grammar_query_and_name(|grammar, name| {
905 grammar.with_debug_variables_query(source, name)
906 })
907 }
908
909 pub fn with_brackets_query(self, source: &str) -> Result<Self> {
910 self.with_grammar_query_and_name(|grammar, name| grammar.with_brackets_query(source, name))
911 }
912
913 pub fn with_indents_query(self, source: &str) -> Result<Self> {
914 self.with_grammar_query_and_name(|grammar, name| grammar.with_indents_query(source, name))
915 }
916
917 pub fn with_injection_query(self, source: &str) -> Result<Self> {
918 self.with_grammar_query_and_name(|grammar, name| grammar.with_injection_query(source, name))
919 }
920
921 pub fn with_override_query(mut self, source: &str) -> Result<Self> {
922 if let Some(grammar_arc) = self.grammar.take() {
923 let grammar = Arc::try_unwrap(grammar_arc)
924 .map_err(|_| anyhow::anyhow!("cannot mutate grammar"))?;
925 let grammar = grammar.with_override_query(
926 source,
927 &self.config.name,
928 &self.config.overrides,
929 &mut self.config.brackets,
930 &self.config.scope_opt_in_language_servers,
931 )?;
932 self.grammar = Some(Arc::new(grammar));
933 }
934 Ok(self)
935 }
936
937 pub fn with_redaction_query(self, source: &str) -> Result<Self> {
938 self.with_grammar_query_and_name(|grammar, name| grammar.with_redaction_query(source, name))
939 }
940
941 fn with_grammar_query(
942 mut self,
943 build: impl FnOnce(Grammar) -> Result<Grammar>,
944 ) -> Result<Self> {
945 if let Some(grammar_arc) = self.grammar.take() {
946 let grammar = Arc::try_unwrap(grammar_arc)
947 .map_err(|_| anyhow::anyhow!("cannot mutate grammar"))?;
948 self.grammar = Some(Arc::new(build(grammar)?));
949 }
950 Ok(self)
951 }
952
953 fn with_grammar_query_and_name(
954 mut self,
955 build: impl FnOnce(Grammar, &LanguageName) -> Result<Grammar>,
956 ) -> Result<Self> {
957 if let Some(grammar_arc) = self.grammar.take() {
958 let grammar = Arc::try_unwrap(grammar_arc)
959 .map_err(|_| anyhow::anyhow!("cannot mutate grammar"))?;
960 self.grammar = Some(Arc::new(build(grammar, &self.config.name)?));
961 }
962 Ok(self)
963 }
964
965 pub fn name(&self) -> LanguageName {
966 self.config.name.clone()
967 }
968 pub fn manifest(&self) -> Option<&ManifestName> {
969 self.manifest_name.as_ref()
970 }
971
972 pub fn code_fence_block_name(&self) -> Arc<str> {
973 self.config
974 .code_fence_block_name
975 .clone()
976 .unwrap_or_else(|| self.config.name.as_ref().to_lowercase().into())
977 }
978
979 pub fn matches_kernel_language(&self, kernel_language: &str) -> bool {
980 let kernel_language_lower = kernel_language.to_lowercase();
981
982 if self.code_fence_block_name().to_lowercase() == kernel_language_lower {
983 return true;
984 }
985
986 if self.config.name.as_ref().to_lowercase() == kernel_language_lower {
987 return true;
988 }
989
990 self.config
991 .kernel_language_names
992 .iter()
993 .any(|name| name.to_lowercase() == kernel_language_lower)
994 }
995
996 pub fn context_provider(&self) -> Option<Arc<dyn ContextProvider>> {
997 self.context_provider.clone()
998 }
999
1000 pub fn toolchain_lister(&self) -> Option<Arc<dyn ToolchainLister>> {
1001 self.toolchain.clone()
1002 }
1003
1004 pub fn highlight_text<'a>(
1005 self: &'a Arc<Self>,
1006 text: &'a Rope,
1007 range: Range<usize>,
1008 ) -> Vec<(Range<usize>, HighlightId)> {
1009 let mut result = Vec::new();
1010 if let Some(grammar) = &self.grammar {
1011 let tree = parse_text(grammar, text, None);
1012 let captures =
1013 SyntaxSnapshot::single_tree_captures(range.clone(), text, &tree, self, |grammar| {
1014 grammar
1015 .highlights_config
1016 .as_ref()
1017 .map(|config| &config.query)
1018 });
1019 let highlight_maps = vec![grammar.highlight_map()];
1020 let mut offset = 0;
1021 for chunk in
1022 BufferChunks::new(text, range, Some((captures, highlight_maps)), false, None)
1023 {
1024 let end_offset = offset + chunk.text.len();
1025 if let Some(highlight_id) = chunk.syntax_highlight_id {
1026 result.push((offset..end_offset, highlight_id));
1027 }
1028 offset = end_offset;
1029 }
1030 }
1031 result
1032 }
1033
1034 pub fn path_suffixes(&self) -> &[String] {
1035 &self.config.matcher.path_suffixes
1036 }
1037
1038 pub fn should_autoclose_before(&self, c: char) -> bool {
1039 c.is_whitespace() || self.config.autoclose_before.contains(c)
1040 }
1041
1042 pub fn set_theme(&self, theme: &SyntaxTheme) {
1043 if let Some(grammar) = self.grammar.as_ref()
1044 && let Some(highlights_config) = &grammar.highlights_config
1045 {
1046 *grammar.highlight_map.lock() =
1047 build_highlight_map(highlights_config.query.capture_names(), theme);
1048 }
1049 }
1050
1051 pub fn grammar(&self) -> Option<&Arc<Grammar>> {
1052 self.grammar.as_ref()
1053 }
1054
1055 pub fn default_scope(self: &Arc<Self>) -> LanguageScope {
1056 LanguageScope {
1057 language: self.clone(),
1058 override_id: None,
1059 }
1060 }
1061
1062 pub fn lsp_id(&self) -> String {
1063 self.config.name.lsp_id()
1064 }
1065
1066 pub fn prettier_parser_name(&self) -> Option<&str> {
1067 self.config.prettier_parser_name.as_deref()
1068 }
1069
1070 pub fn config(&self) -> &LanguageConfig {
1071 &self.config
1072 }
1073}
1074
1075#[inline]
1076pub fn build_highlight_map(capture_names: &[&str], theme: &SyntaxTheme) -> HighlightMap {
1077 HighlightMap::from_ids(
1078 capture_names
1079 .iter()
1080 .map(|capture_name| theme.highlight_id(capture_name).map(HighlightId::new)),
1081 )
1082}
1083
1084impl LanguageScope {
1085 pub fn path_suffixes(&self) -> &[String] {
1086 self.language.path_suffixes()
1087 }
1088
1089 pub fn language_name(&self) -> LanguageName {
1090 self.language.config.name.clone()
1091 }
1092
1093 pub fn collapsed_placeholder(&self) -> &str {
1094 self.language.config.collapsed_placeholder.as_ref()
1095 }
1096
1097 /// Returns line prefix that is inserted in e.g. line continuations or
1098 /// in `toggle comments` action.
1099 pub fn line_comment_prefixes(&self) -> &[Arc<str>] {
1100 Override::as_option(
1101 self.config_override().map(|o| &o.line_comments),
1102 Some(&self.language.config.line_comments),
1103 )
1104 .map_or([].as_slice(), |e| e.as_slice())
1105 }
1106
1107 /// Config for block comments for this language.
1108 pub fn block_comment(&self) -> Option<&BlockCommentConfig> {
1109 Override::as_option(
1110 self.config_override().map(|o| &o.block_comment),
1111 self.language.config.block_comment.as_ref(),
1112 )
1113 }
1114
1115 /// Config for documentation-style block comments for this language.
1116 pub fn documentation_comment(&self) -> Option<&BlockCommentConfig> {
1117 self.language.config.documentation_comment.as_ref()
1118 }
1119
1120 /// Returns list markers that are inserted unchanged on newline (e.g., `- `, `* `, `+ `).
1121 pub fn unordered_list(&self) -> &[Arc<str>] {
1122 &self.language.config.unordered_list
1123 }
1124
1125 /// Returns configuration for ordered lists with auto-incrementing numbers (e.g., `1. ` becomes `2. `).
1126 pub fn ordered_list(&self) -> &[OrderedListConfig] {
1127 &self.language.config.ordered_list
1128 }
1129
1130 /// Returns configuration for task list continuation, if any (e.g., `- [x] ` continues as `- [ ] `).
1131 pub fn task_list(&self) -> Option<&TaskListConfig> {
1132 self.language.config.task_list.as_ref()
1133 }
1134
1135 /// Returns additional regex patterns that act as prefix markers for creating
1136 /// boundaries during rewrapping.
1137 ///
1138 /// By default, Zed treats as paragraph and comment prefixes as boundaries.
1139 pub fn rewrap_prefixes(&self) -> &[Regex] {
1140 &self.language.config.rewrap_prefixes
1141 }
1142
1143 /// Returns a list of language-specific word characters.
1144 ///
1145 /// By default, Zed treats alphanumeric characters (and '_') as word characters for
1146 /// the purpose of actions like 'move to next word end` or whole-word search.
1147 /// It additionally accounts for language's additional word characters.
1148 pub fn word_characters(&self) -> Option<&HashSet<char>> {
1149 Override::as_option(
1150 self.config_override().map(|o| &o.word_characters),
1151 Some(&self.language.config.word_characters),
1152 )
1153 }
1154
1155 /// Returns a list of language-specific characters that are considered part of
1156 /// a completion query.
1157 pub fn completion_query_characters(&self) -> Option<&HashSet<char>> {
1158 Override::as_option(
1159 self.config_override()
1160 .map(|o| &o.completion_query_characters),
1161 Some(&self.language.config.completion_query_characters),
1162 )
1163 }
1164
1165 /// Returns a list of language-specific characters that are considered part of
1166 /// identifiers during linked editing operations.
1167 pub fn linked_edit_characters(&self) -> Option<&HashSet<char>> {
1168 Override::as_option(
1169 self.config_override().map(|o| &o.linked_edit_characters),
1170 Some(&self.language.config.linked_edit_characters),
1171 )
1172 }
1173
1174 /// Returns whether to prefer snippet `label` over `new_text` to replace text when
1175 /// completion is accepted.
1176 ///
1177 /// In cases like when cursor is in string or renaming existing function,
1178 /// you don't want to expand function signature instead just want function name
1179 /// to replace existing one.
1180 pub fn prefers_label_for_snippet_in_completion(&self) -> bool {
1181 self.config_override()
1182 .and_then(|o| o.prefer_label_for_snippet)
1183 .unwrap_or(false)
1184 }
1185
1186 /// Returns a list of bracket pairs for a given language with an additional
1187 /// piece of information about whether the particular bracket pair is currently active for a given language.
1188 pub fn brackets(&self) -> impl Iterator<Item = (&BracketPair, bool)> {
1189 let mut disabled_ids = self
1190 .config_override()
1191 .map_or(&[] as _, |o| o.disabled_bracket_ixs.as_slice());
1192 self.language
1193 .config
1194 .brackets
1195 .pairs
1196 .iter()
1197 .enumerate()
1198 .map(move |(ix, bracket)| {
1199 let mut is_enabled = true;
1200 if let Some(next_disabled_ix) = disabled_ids.first()
1201 && ix == *next_disabled_ix as usize
1202 {
1203 disabled_ids = &disabled_ids[1..];
1204 is_enabled = false;
1205 }
1206 (bracket, is_enabled)
1207 })
1208 }
1209
1210 pub fn should_autoclose_before(&self, c: char) -> bool {
1211 c.is_whitespace() || self.language.config.autoclose_before.contains(c)
1212 }
1213
1214 pub fn language_allowed(&self, name: &LanguageServerName) -> bool {
1215 let config = &self.language.config;
1216 let opt_in_servers = &config.scope_opt_in_language_servers;
1217 if opt_in_servers.contains(name) {
1218 if let Some(over) = self.config_override() {
1219 over.opt_into_language_servers.contains(name)
1220 } else {
1221 false
1222 }
1223 } else {
1224 true
1225 }
1226 }
1227
1228 pub fn override_name(&self) -> Option<&str> {
1229 let id = self.override_id?;
1230 let grammar = self.language.grammar.as_ref()?;
1231 let override_config = grammar.override_config.as_ref()?;
1232 override_config.values.get(&id).map(|e| e.name.as_str())
1233 }
1234
1235 fn config_override(&self) -> Option<&LanguageConfigOverride> {
1236 let id = self.override_id?;
1237 let grammar = self.language.grammar.as_ref()?;
1238 let override_config = grammar.override_config.as_ref()?;
1239 override_config.values.get(&id).map(|e| &e.value)
1240 }
1241}
1242
1243impl Hash for Language {
1244 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1245 self.id.hash(state)
1246 }
1247}
1248
1249impl PartialEq for Language {
1250 fn eq(&self, other: &Self) -> bool {
1251 self.id.eq(&other.id)
1252 }
1253}
1254
1255impl Eq for Language {}
1256
1257impl Debug for Language {
1258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1259 f.debug_struct("Language")
1260 .field("name", &self.config.name)
1261 .finish()
1262 }
1263}
1264
1265pub(crate) fn parse_text(grammar: &Grammar, text: &Rope, old_tree: Option<Tree>) -> Tree {
1266 with_parser(|parser| {
1267 parser
1268 .set_language(&grammar.ts_language)
1269 .expect("incompatible grammar");
1270 let mut chunks = text.chunks_in_range(0..text.len());
1271 parser
1272 .parse_with_options(
1273 &mut move |offset, _| {
1274 chunks.seek(offset);
1275 chunks.next().unwrap_or("").as_bytes()
1276 },
1277 old_tree.as_ref(),
1278 None,
1279 )
1280 .unwrap()
1281 })
1282}
1283
1284pub trait CodeLabelExt {
1285 fn fallback_for_completion(
1286 item: &lsp::CompletionItem,
1287 language: Option<&Language>,
1288 ) -> CodeLabel;
1289}
1290
1291impl CodeLabelExt for CodeLabel {
1292 fn fallback_for_completion(
1293 item: &lsp::CompletionItem,
1294 language: Option<&Language>,
1295 ) -> CodeLabel {
1296 let highlight_id = item.kind.and_then(|kind| {
1297 let grammar = language?.grammar()?;
1298 use lsp::CompletionItemKind as Kind;
1299 match kind {
1300 Kind::CLASS => grammar.highlight_id_for_name("type"),
1301 Kind::CONSTANT => grammar.highlight_id_for_name("constant"),
1302 Kind::CONSTRUCTOR => grammar.highlight_id_for_name("constructor"),
1303 Kind::ENUM => grammar
1304 .highlight_id_for_name("enum")
1305 .or_else(|| grammar.highlight_id_for_name("type")),
1306 Kind::ENUM_MEMBER => grammar
1307 .highlight_id_for_name("variant")
1308 .or_else(|| grammar.highlight_id_for_name("property")),
1309 Kind::FIELD => grammar.highlight_id_for_name("property"),
1310 Kind::FUNCTION => grammar.highlight_id_for_name("function"),
1311 Kind::INTERFACE => grammar.highlight_id_for_name("type"),
1312 Kind::METHOD => grammar
1313 .highlight_id_for_name("function.method")
1314 .or_else(|| grammar.highlight_id_for_name("function")),
1315 Kind::OPERATOR => grammar.highlight_id_for_name("operator"),
1316 Kind::PROPERTY => grammar.highlight_id_for_name("property"),
1317 Kind::STRUCT => grammar.highlight_id_for_name("type"),
1318 Kind::VARIABLE => grammar.highlight_id_for_name("variable"),
1319 Kind::KEYWORD => grammar.highlight_id_for_name("keyword"),
1320 _ => None,
1321 }
1322 });
1323
1324 let label = &item.label;
1325 let label_length = label.len();
1326 let runs = highlight_id
1327 .map(|highlight_id| vec![(0..label_length, highlight_id)])
1328 .unwrap_or_default();
1329 let text = if let Some(detail) = item.detail.as_deref().filter(|detail| detail != label) {
1330 format!("{label} {detail}")
1331 } else if let Some(description) = item
1332 .label_details
1333 .as_ref()
1334 .and_then(|label_details| label_details.description.as_deref())
1335 .filter(|description| description != label)
1336 {
1337 format!("{label} {description}")
1338 } else {
1339 label.clone()
1340 };
1341 let filter_range = item
1342 .filter_text
1343 .as_deref()
1344 .and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
1345 .unwrap_or(0..label_length);
1346 CodeLabel {
1347 text,
1348 runs,
1349 filter_range,
1350 }
1351 }
1352}
1353
1354#[cfg(any(test, feature = "test-support"))]
1355impl Default for FakeLspAdapter {
1356 fn default() -> Self {
1357 Self {
1358 name: "the-fake-language-server",
1359 capabilities: lsp::LanguageServer::full_capabilities(),
1360 initializer: None,
1361 disk_based_diagnostics_progress_token: None,
1362 initialization_options: None,
1363 disk_based_diagnostics_sources: Vec::new(),
1364 prettier_plugins: Vec::new(),
1365 language_server_binary: LanguageServerBinary {
1366 path: "/the/fake/lsp/path".into(),
1367 arguments: vec![],
1368 env: Default::default(),
1369 },
1370 label_for_completion: None,
1371 }
1372 }
1373}
1374
1375#[cfg(any(test, feature = "test-support"))]
1376impl LspInstaller for FakeLspAdapter {
1377 type BinaryVersion = ();
1378
1379 async fn fetch_latest_server_version(
1380 &self,
1381 _: &dyn LspAdapterDelegate,
1382 _: bool,
1383 _: &mut AsyncApp,
1384 ) -> Result<Self::BinaryVersion> {
1385 unreachable!()
1386 }
1387
1388 async fn check_if_user_installed(
1389 &self,
1390 _: &dyn LspAdapterDelegate,
1391 _: Option<Toolchain>,
1392 _: &AsyncApp,
1393 ) -> Option<LanguageServerBinary> {
1394 Some(self.language_server_binary.clone())
1395 }
1396
1397 async fn fetch_server_binary(
1398 &self,
1399 _: (),
1400 _: PathBuf,
1401 _: &dyn LspAdapterDelegate,
1402 ) -> Result<LanguageServerBinary> {
1403 unreachable!();
1404 }
1405
1406 async fn cached_server_binary(
1407 &self,
1408 _: PathBuf,
1409 _: &dyn LspAdapterDelegate,
1410 ) -> Option<LanguageServerBinary> {
1411 unreachable!();
1412 }
1413}
1414
1415#[cfg(any(test, feature = "test-support"))]
1416#[async_trait(?Send)]
1417impl LspAdapter for FakeLspAdapter {
1418 fn name(&self) -> LanguageServerName {
1419 LanguageServerName(self.name.into())
1420 }
1421
1422 fn disk_based_diagnostic_sources(&self) -> Vec<String> {
1423 self.disk_based_diagnostics_sources.clone()
1424 }
1425
1426 fn disk_based_diagnostics_progress_token(&self) -> Option<String> {
1427 self.disk_based_diagnostics_progress_token.clone()
1428 }
1429
1430 async fn initialization_options(
1431 self: Arc<Self>,
1432 _: &Arc<dyn LspAdapterDelegate>,
1433 _cx: &mut AsyncApp,
1434 ) -> Result<Option<Value>> {
1435 Ok(self.initialization_options.clone())
1436 }
1437
1438 async fn label_for_completion(
1439 &self,
1440 item: &lsp::CompletionItem,
1441 language: &Arc<Language>,
1442 ) -> Option<CodeLabel> {
1443 let label_for_completion = self.label_for_completion.as_ref()?;
1444 label_for_completion(item, language)
1445 }
1446
1447 fn is_extension(&self) -> bool {
1448 false
1449 }
1450}
1451
1452pub fn point_to_lsp(point: PointUtf16) -> lsp::Position {
1453 lsp::Position::new(point.row, point.column)
1454}
1455
1456pub fn point_from_lsp(point: lsp::Position) -> Unclipped<PointUtf16> {
1457 Unclipped(PointUtf16::new(point.line, point.character))
1458}
1459
1460pub fn range_to_lsp(range: Range<PointUtf16>) -> Result<lsp::Range> {
1461 anyhow::ensure!(
1462 range.start <= range.end,
1463 "Inverted range provided to an LSP request: {:?}-{:?}",
1464 range.start,
1465 range.end
1466 );
1467 Ok(lsp::Range {
1468 start: point_to_lsp(range.start),
1469 end: point_to_lsp(range.end),
1470 })
1471}
1472
1473pub fn range_from_lsp(range: lsp::Range) -> Range<Unclipped<PointUtf16>> {
1474 let mut start = point_from_lsp(range.start);
1475 let mut end = point_from_lsp(range.end);
1476 if start > end {
1477 // We debug instead of warn so that this is not logged by default unless explicitly requested.
1478 // Using warn would write to the log file, and since we receive an enormous amount of
1479 // range_from_lsp calls (especially during completions), that can hang the main thread.
1480 //
1481 // See issue #36223.
1482 zlog::debug!("range_from_lsp called with inverted range {start:?}-{end:?}");
1483 mem::swap(&mut start, &mut end);
1484 }
1485 start..end
1486}
1487
1488#[doc(hidden)]
1489#[cfg(any(test, feature = "test-support"))]
1490pub fn rust_lang() -> Arc<Language> {
1491 use std::borrow::Cow;
1492
1493 let language = Language::new(
1494 LanguageConfig {
1495 name: "Rust".into(),
1496 matcher: LanguageMatcher {
1497 path_suffixes: vec!["rs".to_string()],
1498 ..Default::default()
1499 },
1500 line_comments: vec!["// ".into(), "/// ".into(), "//! ".into()],
1501 brackets: BracketPairConfig {
1502 pairs: vec![
1503 BracketPair {
1504 start: "{".into(),
1505 end: "}".into(),
1506 close: true,
1507 surround: false,
1508 newline: true,
1509 },
1510 BracketPair {
1511 start: "[".into(),
1512 end: "]".into(),
1513 close: true,
1514 surround: false,
1515 newline: true,
1516 },
1517 BracketPair {
1518 start: "(".into(),
1519 end: ")".into(),
1520 close: true,
1521 surround: false,
1522 newline: true,
1523 },
1524 BracketPair {
1525 start: "<".into(),
1526 end: ">".into(),
1527 close: false,
1528 surround: false,
1529 newline: true,
1530 },
1531 BracketPair {
1532 start: "\"".into(),
1533 end: "\"".into(),
1534 close: true,
1535 surround: false,
1536 newline: false,
1537 },
1538 ],
1539 ..Default::default()
1540 },
1541 ..Default::default()
1542 },
1543 Some(tree_sitter_rust::LANGUAGE.into()),
1544 )
1545 .with_queries(LanguageQueries {
1546 outline: Some(Cow::from(include_str!(
1547 "../../grammars/src/rust/outline.scm"
1548 ))),
1549 indents: Some(Cow::from(include_str!(
1550 "../../grammars/src/rust/indents.scm"
1551 ))),
1552 brackets: Some(Cow::from(include_str!(
1553 "../../grammars/src/rust/brackets.scm"
1554 ))),
1555 text_objects: Some(Cow::from(include_str!(
1556 "../../grammars/src/rust/textobjects.scm"
1557 ))),
1558 highlights: Some(Cow::from(include_str!(
1559 "../../grammars/src/rust/highlights.scm"
1560 ))),
1561 injections: Some(Cow::from(include_str!(
1562 "../../grammars/src/rust/injections.scm"
1563 ))),
1564 overrides: Some(Cow::from(include_str!(
1565 "../../grammars/src/rust/overrides.scm"
1566 ))),
1567 redactions: None,
1568 runnables: Some(Cow::from(include_str!(
1569 "../../grammars/src/rust/runnables.scm"
1570 ))),
1571 debugger: Some(Cow::from(include_str!(
1572 "../../grammars/src/rust/debugger.scm"
1573 ))),
1574 })
1575 .expect("Could not parse queries");
1576 Arc::new(language)
1577}
1578
1579#[doc(hidden)]
1580#[cfg(any(test, feature = "test-support"))]
1581pub fn markdown_lang() -> Arc<Language> {
1582 use std::borrow::Cow;
1583
1584 let language = Language::new(
1585 LanguageConfig {
1586 name: "Markdown".into(),
1587 matcher: LanguageMatcher {
1588 path_suffixes: vec!["md".into()],
1589 ..Default::default()
1590 },
1591 ..LanguageConfig::default()
1592 },
1593 Some(tree_sitter_md::LANGUAGE.into()),
1594 )
1595 .with_queries(LanguageQueries {
1596 brackets: Some(Cow::from(include_str!(
1597 "../../grammars/src/markdown/brackets.scm"
1598 ))),
1599 injections: Some(Cow::from(include_str!(
1600 "../../grammars/src/markdown/injections.scm"
1601 ))),
1602 highlights: Some(Cow::from(include_str!(
1603 "../../grammars/src/markdown/highlights.scm"
1604 ))),
1605 indents: Some(Cow::from(include_str!(
1606 "../../grammars/src/markdown/indents.scm"
1607 ))),
1608 outline: Some(Cow::from(include_str!(
1609 "../../grammars/src/markdown/outline.scm"
1610 ))),
1611 ..LanguageQueries::default()
1612 })
1613 .expect("Could not parse markdown queries");
1614 Arc::new(language)
1615}
1616
1617#[cfg(test)]
1618mod tests {
1619 use super::*;
1620 use gpui::{TestAppContext, rgba};
1621 use pretty_assertions::assert_matches;
1622
1623 #[test]
1624 fn test_highlight_map() {
1625 let theme = SyntaxTheme::new(
1626 [
1627 ("function", rgba(0x100000ff)),
1628 ("function.method", rgba(0x200000ff)),
1629 ("function.async", rgba(0x300000ff)),
1630 ("variable.builtin.self.rust", rgba(0x400000ff)),
1631 ("variable.builtin", rgba(0x500000ff)),
1632 ("variable", rgba(0x600000ff)),
1633 ]
1634 .iter()
1635 .map(|(name, color)| (name.to_string(), (*color).into())),
1636 );
1637
1638 let capture_names = &[
1639 "function.special",
1640 "function.async.rust",
1641 "variable.builtin.self",
1642 ];
1643
1644 let map = build_highlight_map(capture_names, &theme);
1645 assert_eq!(
1646 theme.get_capture_name(map.get(0).unwrap()),
1647 Some("function")
1648 );
1649 assert_eq!(
1650 theme.get_capture_name(map.get(1).unwrap()),
1651 Some("function.async")
1652 );
1653 assert_eq!(
1654 theme.get_capture_name(map.get(2).unwrap()),
1655 Some("variable.builtin")
1656 );
1657 }
1658
1659 #[gpui::test(iterations = 10)]
1660
1661 async fn test_language_loading(cx: &mut TestAppContext) {
1662 let languages = LanguageRegistry::test(cx.executor());
1663 let languages = Arc::new(languages);
1664 languages.register_native_grammars([
1665 ("json", tree_sitter_json::LANGUAGE),
1666 ("rust", tree_sitter_rust::LANGUAGE),
1667 ]);
1668 languages.register_test_language(LanguageConfig {
1669 name: "JSON".into(),
1670 grammar: Some("json".into()),
1671 matcher: LanguageMatcher {
1672 path_suffixes: vec!["json".into()],
1673 ..Default::default()
1674 },
1675 ..Default::default()
1676 });
1677 languages.register_test_language(LanguageConfig {
1678 name: "Rust".into(),
1679 grammar: Some("rust".into()),
1680 matcher: LanguageMatcher {
1681 path_suffixes: vec!["rs".into()],
1682 ..Default::default()
1683 },
1684 ..Default::default()
1685 });
1686 assert_eq!(
1687 languages.language_names(),
1688 &[
1689 LanguageName::new_static("JSON"),
1690 LanguageName::new_static("Plain Text"),
1691 LanguageName::new_static("Rust"),
1692 ]
1693 );
1694
1695 let rust1 = languages.language_for_name("Rust");
1696 let rust2 = languages.language_for_name("Rust");
1697
1698 // Ensure language is still listed even if it's being loaded.
1699 assert_eq!(
1700 languages.language_names(),
1701 &[
1702 LanguageName::new_static("JSON"),
1703 LanguageName::new_static("Plain Text"),
1704 LanguageName::new_static("Rust"),
1705 ]
1706 );
1707
1708 let (rust1, rust2) = futures::join!(rust1, rust2);
1709 assert!(Arc::ptr_eq(&rust1.unwrap(), &rust2.unwrap()));
1710
1711 // Ensure language is still listed even after loading it.
1712 assert_eq!(
1713 languages.language_names(),
1714 &[
1715 LanguageName::new_static("JSON"),
1716 LanguageName::new_static("Plain Text"),
1717 LanguageName::new_static("Rust"),
1718 ]
1719 );
1720
1721 // Loading an unknown language returns an error.
1722 assert!(languages.language_for_name("Unknown").await.is_err());
1723 }
1724
1725 #[gpui::test]
1726 async fn test_completion_label_omits_duplicate_data() {
1727 let regular_completion_item_1 = lsp::CompletionItem {
1728 label: "regular1".to_string(),
1729 detail: Some("detail1".to_string()),
1730 label_details: Some(lsp::CompletionItemLabelDetails {
1731 detail: None,
1732 description: Some("description 1".to_string()),
1733 }),
1734 ..lsp::CompletionItem::default()
1735 };
1736
1737 let regular_completion_item_2 = lsp::CompletionItem {
1738 label: "regular2".to_string(),
1739 label_details: Some(lsp::CompletionItemLabelDetails {
1740 detail: None,
1741 description: Some("description 2".to_string()),
1742 }),
1743 ..lsp::CompletionItem::default()
1744 };
1745
1746 let completion_item_with_duplicate_detail_and_proper_description = lsp::CompletionItem {
1747 detail: Some(regular_completion_item_1.label.clone()),
1748 ..regular_completion_item_1.clone()
1749 };
1750
1751 let completion_item_with_duplicate_detail = lsp::CompletionItem {
1752 detail: Some(regular_completion_item_1.label.clone()),
1753 label_details: None,
1754 ..regular_completion_item_1.clone()
1755 };
1756
1757 let completion_item_with_duplicate_description = lsp::CompletionItem {
1758 label_details: Some(lsp::CompletionItemLabelDetails {
1759 detail: None,
1760 description: Some(regular_completion_item_2.label.clone()),
1761 }),
1762 ..regular_completion_item_2.clone()
1763 };
1764
1765 assert_eq!(
1766 CodeLabel::fallback_for_completion(®ular_completion_item_1, None).text,
1767 format!(
1768 "{} {}",
1769 regular_completion_item_1.label,
1770 regular_completion_item_1.detail.unwrap()
1771 ),
1772 "LSP completion items with both detail and label_details.description should prefer detail"
1773 );
1774 assert_eq!(
1775 CodeLabel::fallback_for_completion(®ular_completion_item_2, None).text,
1776 format!(
1777 "{} {}",
1778 regular_completion_item_2.label,
1779 regular_completion_item_2
1780 .label_details
1781 .as_ref()
1782 .unwrap()
1783 .description
1784 .as_ref()
1785 .unwrap()
1786 ),
1787 "LSP completion items without detail but with label_details.description should use that"
1788 );
1789 assert_eq!(
1790 CodeLabel::fallback_for_completion(
1791 &completion_item_with_duplicate_detail_and_proper_description,
1792 None
1793 )
1794 .text,
1795 format!(
1796 "{} {}",
1797 regular_completion_item_1.label,
1798 regular_completion_item_1
1799 .label_details
1800 .as_ref()
1801 .unwrap()
1802 .description
1803 .as_ref()
1804 .unwrap()
1805 ),
1806 "LSP completion items with both detail and label_details.description should prefer description only if the detail duplicates the completion label"
1807 );
1808 assert_eq!(
1809 CodeLabel::fallback_for_completion(&completion_item_with_duplicate_detail, None).text,
1810 regular_completion_item_1.label,
1811 "LSP completion items with duplicate label and detail, should omit the detail"
1812 );
1813 assert_eq!(
1814 CodeLabel::fallback_for_completion(&completion_item_with_duplicate_description, None)
1815 .text,
1816 regular_completion_item_2.label,
1817 "LSP completion items with duplicate label and detail, should omit the detail"
1818 );
1819 }
1820
1821 #[test]
1822 fn test_deserializing_comments_backwards_compat() {
1823 // current version of `block_comment` and `documentation_comment` work
1824 {
1825 let config: LanguageConfig = ::toml::from_str(
1826 r#"
1827 name = "Foo"
1828 block_comment = { start = "a", end = "b", prefix = "c", tab_size = 1 }
1829 documentation_comment = { start = "d", end = "e", prefix = "f", tab_size = 2 }
1830 "#,
1831 )
1832 .unwrap();
1833 assert_matches!(config.block_comment, Some(BlockCommentConfig { .. }));
1834 assert_matches!(
1835 config.documentation_comment,
1836 Some(BlockCommentConfig { .. })
1837 );
1838
1839 let block_config = config.block_comment.unwrap();
1840 assert_eq!(block_config.start.as_ref(), "a");
1841 assert_eq!(block_config.end.as_ref(), "b");
1842 assert_eq!(block_config.prefix.as_ref(), "c");
1843 assert_eq!(block_config.tab_size, 1);
1844
1845 let doc_config = config.documentation_comment.unwrap();
1846 assert_eq!(doc_config.start.as_ref(), "d");
1847 assert_eq!(doc_config.end.as_ref(), "e");
1848 assert_eq!(doc_config.prefix.as_ref(), "f");
1849 assert_eq!(doc_config.tab_size, 2);
1850 }
1851
1852 // former `documentation` setting is read into `documentation_comment`
1853 {
1854 let config: LanguageConfig = ::toml::from_str(
1855 r#"
1856 name = "Foo"
1857 documentation = { start = "a", end = "b", prefix = "c", tab_size = 1}
1858 "#,
1859 )
1860 .unwrap();
1861 assert_matches!(
1862 config.documentation_comment,
1863 Some(BlockCommentConfig { .. })
1864 );
1865
1866 let config = config.documentation_comment.unwrap();
1867 assert_eq!(config.start.as_ref(), "a");
1868 assert_eq!(config.end.as_ref(), "b");
1869 assert_eq!(config.prefix.as_ref(), "c");
1870 assert_eq!(config.tab_size, 1);
1871 }
1872
1873 // old block_comment format is read into BlockCommentConfig
1874 {
1875 let config: LanguageConfig = ::toml::from_str(
1876 r#"
1877 name = "Foo"
1878 block_comment = ["a", "b"]
1879 "#,
1880 )
1881 .unwrap();
1882 assert_matches!(config.block_comment, Some(BlockCommentConfig { .. }));
1883
1884 let config = config.block_comment.unwrap();
1885 assert_eq!(config.start.as_ref(), "a");
1886 assert_eq!(config.end.as_ref(), "b");
1887 assert_eq!(config.prefix.as_ref(), "");
1888 assert_eq!(config.tab_size, 0);
1889 }
1890 }
1891}