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