//! The `language` crate provides a large chunk of Zed's language-related//! features (the other big contributors being project and lsp crates that revolve around LSP features).//! Namely, this crate://! - Provides [`Language`], [`Grammar`] and [`LanguageRegistry`] types that//! 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)//! - 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.//!//! 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.mod buffer;mod diagnostic_set;mod highlight_map;mod language_registry;pub mod language_settings;mod manifest;mod outline;pub mod proto;mod syntax_map;mod task_context;mod text_diff;mod toolchain; #[cfg(test)]pub mod buffer_tests; use crate::language_settings::SoftWrap;pub use crate::language_settings::{EditPredictionsMode, IndentGuideSettings};use anyhow::{Context as _, Result};use async_trait::async_trait;use collections::{HashMap, HashSet, IndexSet};use futures::Future;use futures::future::LocalBoxFuture;use futures::lock::OwnedMutexGuard;use gpui::{App, AsyncApp, Entity, SharedString};pub use highlight_map::HighlightMap;use http_client::HttpClient;pub use language_registry::{ LanguageName, LanguageServerStatusUpdate, LoadedLanguage, ServerHealth,};use lsp::{CodeActionKind, InitializeParams, LanguageServerBinary, LanguageServerBinaryOptions};pub use manifest::{ManifestDelegate, ManifestName, ManifestProvider, ManifestQuery};use parking_lot::Mutex;use regex::Regex;use schemars::{JsonSchema, SchemaGenerator, json_schema};use serde::{Deserialize, Deserializer, Serialize, Serializer, de};use serde_json::Value;use settings::WorktreeId;use smol::future::FutureExt as _;use std::num::NonZeroU32;use std::{ ffi::OsStr, fmt::Debug, hash::Hash, mem, ops::{DerefMut, Range}, path::{Path, PathBuf}, str, sync::{ Arc, LazyLock, atomic::{AtomicUsize, Ordering::SeqCst}, },};use syntax_map::{QueryCursorHandle, SyntaxSnapshot};use task::RunnableTag;pub use task_context::{ContextLocation, ContextProvider, RunnableRange};pub use text_diff::{ DiffOptions, apply_diff_patch, line_diff, text_diff, text_diff_with_options, unified_diff,};use theme::SyntaxTheme;pub use toolchain::{ LanguageToolchainStore, LocalLanguageToolchainStore, Toolchain, ToolchainList, ToolchainLister, ToolchainMetadata, ToolchainScope,};use tree_sitter::{self, Query, QueryCursor, WasmStore, wasmtime};use util::rel_path::RelPath;use util::serde::default_true; pub use buffer::Operation;pub use buffer::*;pub use diagnostic_set::{DiagnosticEntry, DiagnosticEntryRef, DiagnosticGroup};pub use language_registry::{ AvailableLanguage, BinaryStatus, LanguageNotFound, LanguageQueries, LanguageRegistry, QUERY_FILENAME_PREFIXES,};pub use lsp::{LanguageServerId, LanguageServerName};pub use outline::*;pub use syntax_map::{ OwnedSyntaxLayer, SyntaxLayer, SyntaxMapMatches, ToTreeSitterPoint, TreeSitterOptions,};pub use text::{AnchorRangeExt, LineEnding};pub use tree_sitter::{Node, Parser, Tree, TreeCursor}; static QUERY_CURSORS: Mutex<Vec<QueryCursor>> = Mutex::new(vec![]);static PARSERS: Mutex<Vec<Parser>> = Mutex::new(vec![]); pub fn with_parser<F, R>(func: F) -> Rwhere F: FnOnce(&mut Parser) -> R,{ let mut parser = PARSERS.lock().pop().unwrap_or_else(|| { let mut parser = Parser::new(); parser .set_wasm_store(WasmStore::new(&WASM_ENGINE).unwrap()) .unwrap(); parser }); parser.set_included_ranges(&[]).unwrap(); let result = func(&mut parser); PARSERS.lock().push(parser); result} pub fn with_query_cursor<F, R>(func: F) -> Rwhere F: FnOnce(&mut QueryCursor) -> R,{ let mut cursor = QueryCursorHandle::new(); func(cursor.deref_mut())} static NEXT_LANGUAGE_ID: AtomicUsize = AtomicUsize::new(0);static NEXT_GRAMMAR_ID: AtomicUsize = AtomicUsize::new(0);static WASM_ENGINE: LazyLock<wasmtime::Engine> = LazyLock::new(|| { wasmtime::Engine::new(&wasmtime::Config::new()).expect("Failed to create Wasmtime engine")}); /// A shared grammar for plain text, exposed for reuse by downstream crates.pub static PLAIN_TEXT: LazyLock<Arc<Language>> = LazyLock::new(|| { Arc::new(Language::new( LanguageConfig { name: "Plain Text".into(), soft_wrap: Some(SoftWrap::EditorWidth), matcher: LanguageMatcher { path_suffixes: vec!["txt".to_owned()], first_line_pattern: None, }, ..Default::default() }, None, ))}); /// Types that represent a position in a buffer, and can be converted into/// an LSP position, to send to a language server.pub trait ToLspPosition { /// Converts the value into an LSP position. fn to_lsp_position(self) -> lsp::Position;} #[derive(Debug, Clone, PartialEq, Eq, Hash)]pub struct Location { pub buffer: Entity<Buffer>, pub range: Range<Anchor>,} type ServerBinaryCache = futures::lock::Mutex<Option<(bool, LanguageServerBinary)>>;type DownloadableLanguageServerBinary = LocalBoxFuture<'static, Result<LanguageServerBinary>>;pub type LanguageServerBinaryLocations = LocalBoxFuture< 'static, ( Result<LanguageServerBinary>, Option<DownloadableLanguageServerBinary>, ),>;/// Represents a Language Server, with certain cached sync properties./// Uses [`LspAdapter`] under the hood, but calls all 'static' methods/// once at startup, and caches the results.pub struct CachedLspAdapter { pub name: LanguageServerName, pub disk_based_diagnostic_sources: Vec<String>, pub disk_based_diagnostics_progress_token: Option<String>, language_ids: HashMap<LanguageName, String>, pub adapter: Arc<dyn LspAdapter>, cached_binary: Arc<ServerBinaryCache>,} impl Debug for CachedLspAdapter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CachedLspAdapter") .field("name", &self.name) .field( "disk_based_diagnostic_sources", &self.disk_based_diagnostic_sources, ) .field( "disk_based_diagnostics_progress_token", &self.disk_based_diagnostics_progress_token, ) .field("language_ids", &self.language_ids) .finish_non_exhaustive() }} impl CachedLspAdapter { pub fn new(adapter: Arc<dyn LspAdapter>) -> Arc<Self> { let name = adapter.name(); let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources(); let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token(); let language_ids = adapter.language_ids(); Arc::new(CachedLspAdapter { name, disk_based_diagnostic_sources, disk_based_diagnostics_progress_token, language_ids, adapter, cached_binary: Default::default(), }) } pub fn name(&self) -> LanguageServerName { self.adapter.name() } pub async fn get_language_server_command( self: Arc<Self>, delegate: Arc<dyn LspAdapterDelegate>, toolchains: Option<Toolchain>, binary_options: LanguageServerBinaryOptions, cx: &mut AsyncApp, ) -> LanguageServerBinaryLocations { let cached_binary = self.cached_binary.clone().lock_owned().await; self.adapter.clone().get_language_server_command( delegate, toolchains, binary_options, cached_binary, cx.clone(), ) } pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> { self.adapter.code_action_kinds() } pub fn process_diagnostics( &self, params: &mut lsp::PublishDiagnosticsParams, server_id: LanguageServerId, existing_diagnostics: Option<&'_ Buffer>, ) { self.adapter .process_diagnostics(params, server_id, existing_diagnostics) } pub fn retain_old_diagnostic(&self, previous_diagnostic: &Diagnostic, cx: &App) -> bool { self.adapter.retain_old_diagnostic(previous_diagnostic, cx) } pub fn underline_diagnostic(&self, diagnostic: &lsp::Diagnostic) -> bool { self.adapter.underline_diagnostic(diagnostic) } pub fn diagnostic_message_to_markdown(&self, message: &str) -> Option<String> { self.adapter.diagnostic_message_to_markdown(message) } pub async fn process_completions(&self, completion_items: &mut [lsp::CompletionItem]) { self.adapter.process_completions(completion_items).await } pub async fn labels_for_completions( &self, completion_items: &[lsp::CompletionItem], language: &Arc<Language>, ) -> Result<Vec<Option<CodeLabel>>> { self.adapter .clone() .labels_for_completions(completion_items, language) .await } pub async fn labels_for_symbols( &self, symbols: &[(String, lsp::SymbolKind)], language: &Arc<Language>, ) -> Result<Vec<Option<CodeLabel>>> { self.adapter .clone() .labels_for_symbols(symbols, language) .await } pub fn language_id(&self, language_name: &LanguageName) -> String { self.language_ids .get(language_name) .cloned() .unwrap_or_else(|| language_name.lsp_id()) }} /// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application// e.g. to display a notification or fetch data from the web.#[async_trait]pub trait LspAdapterDelegate: Send + Sync { fn show_notification(&self, message: &str, cx: &mut App); fn http_client(&self) -> Arc<dyn HttpClient>; fn worktree_id(&self) -> WorktreeId; fn worktree_root_path(&self) -> &Path; fn resolve_executable_path(&self, path: PathBuf) -> PathBuf; fn update_status(&self, language: LanguageServerName, status: BinaryStatus); fn registered_lsp_adapters(&self) -> Vec<Arc<dyn LspAdapter>>; async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>>; async fn npm_package_installed_version( &self, package_name: &str, ) -> Result<Option<(PathBuf, String)>>; async fn which(&self, command: &OsStr) -> Option<PathBuf>; async fn shell_env(&self) -> HashMap<String, String>; async fn read_text_file(&self, path: &RelPath) -> Result<String>; async fn try_exec(&self, binary: LanguageServerBinary) -> Result<()>;} #[async_trait(?Send)]pub trait LspAdapter: 'static + Send + Sync + DynLspInstaller { fn name(&self) -> LanguageServerName; fn process_diagnostics( &self, _: &mut lsp::PublishDiagnosticsParams, _: LanguageServerId, _: Option<&'_ Buffer>, ) { } /// When processing new `lsp::PublishDiagnosticsParams` diagnostics, whether to retain previous one(s) or not. fn retain_old_diagnostic(&self, _previous_diagnostic: &Diagnostic, _cx: &App) -> bool { false } /// Whether to underline a given diagnostic or not, when rendering in the editor. /// /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnosticTag /// states that /// > Clients are allowed to render diagnostics with this tag faded out instead of having an error squiggle. /// for the unnecessary diagnostics, so do not underline them. fn underline_diagnostic(&self, _diagnostic: &lsp::Diagnostic) -> bool { true } /// Post-processes completions provided by the language server. async fn process_completions(&self, _: &mut [lsp::CompletionItem]) {} fn diagnostic_message_to_markdown(&self, _message: &str) -> Option<String> { None } async fn labels_for_completions( self: Arc<Self>, completions: &[lsp::CompletionItem], language: &Arc<Language>, ) -> Result<Vec<Option<CodeLabel>>> { let mut labels = Vec::new(); for (ix, completion) in completions.iter().enumerate() { let label = self.label_for_completion(completion, language).await; if let Some(label) = label { labels.resize(ix + 1, None); *labels.last_mut().unwrap() = Some(label); } } Ok(labels) } async fn label_for_completion( &self, _: &lsp::CompletionItem, _: &Arc<Language>, ) -> Option<CodeLabel> { None } async fn labels_for_symbols( self: Arc<Self>, symbols: &[(String, lsp::SymbolKind)], language: &Arc<Language>, ) -> Result<Vec<Option<CodeLabel>>> { let mut labels = Vec::new(); for (ix, (name, kind)) in symbols.iter().enumerate() { let label = self.label_for_symbol(name, *kind, language).await; if let Some(label) = label { labels.resize(ix + 1, None); *labels.last_mut().unwrap() = Some(label); } } Ok(labels) } async fn label_for_symbol( &self, _: &str, _: lsp::SymbolKind, _: &Arc<Language>, ) -> Option<CodeLabel> { None } /// Returns initialization options that are going to be sent to a LSP server as a part of [`lsp::InitializeParams`] async fn initialization_options( self: Arc<Self>, _: &Arc<dyn LspAdapterDelegate>, ) -> Result<Option<Value>> { Ok(None) } async fn workspace_configuration( self: Arc<Self>, _: &Arc<dyn LspAdapterDelegate>, _: Option<Toolchain>, _cx: &mut AsyncApp, ) -> Result<Value> { Ok(serde_json::json!({})) } async fn additional_initialization_options( self: Arc<Self>, _target_language_server_id: LanguageServerName, _: &Arc<dyn LspAdapterDelegate>, ) -> Result<Option<Value>> { Ok(None) } async fn additional_workspace_configuration( self: Arc<Self>, _target_language_server_id: LanguageServerName, _: &Arc<dyn LspAdapterDelegate>, _cx: &mut AsyncApp, ) -> Result<Option<Value>> { Ok(None) } /// Returns a list of code actions supported by a given LspAdapter fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> { None } fn disk_based_diagnostic_sources(&self) -> Vec<String> { Default::default() } fn disk_based_diagnostics_progress_token(&self) -> Option<String> { None } fn language_ids(&self) -> HashMap<LanguageName, String> { HashMap::default() } /// Support custom initialize params. fn prepare_initialize_params( &self, original: InitializeParams, _: &App, ) -> Result<InitializeParams> { Ok(original) } /// Method only implemented by the default JSON language server adapter. /// Used to provide dynamic reloading of the JSON schemas used to /// provide autocompletion and diagnostics in Zed setting and keybind /// files fn is_primary_zed_json_schema_adapter(&self) -> bool { false } /// True for the extension adapter and false otherwise. fn is_extension(&self) -> bool { false }} pub trait LspInstaller { type BinaryVersion; fn check_if_user_installed( &self, _: &dyn LspAdapterDelegate, _: Option<Toolchain>, _: &AsyncApp, ) -> impl Future<Output = Option<LanguageServerBinary>> { async { None } } fn fetch_latest_server_version( &self, delegate: &dyn LspAdapterDelegate, pre_release: bool, cx: &mut AsyncApp, ) -> impl Future<Output = Result<Self::BinaryVersion>>; fn check_if_version_installed( &self, _version: &Self::BinaryVersion, _container_dir: &PathBuf, _delegate: &dyn LspAdapterDelegate, ) -> impl Future<Output = Option<LanguageServerBinary>> { async { None } } fn fetch_server_binary( &self, latest_version: Self::BinaryVersion,Lines 1–500 of 2954. Load more
//! The `language` crate provides a large chunk of Zed's language-related //! features (the other big contributors being project and lsp crates that revolve around LSP features). //! Namely, this crate: //! - Provides [`Language`], [`Grammar`] and [`LanguageRegistry`] types that //! 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) //! - 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. //! //! 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. mod buffer; mod diagnostic_set; mod highlight_map; mod language_registry; pub mod language_settings; mod manifest; mod outline; pub mod proto; mod syntax_map; mod task_context; mod text_diff; mod toolchain; #[cfg(test)] pub mod buffer_tests; use crate::language_settings::SoftWrap; pub use crate::language_settings::{EditPredictionsMode, IndentGuideSettings}; use anyhow::{Context as _, Result}; use async_trait::async_trait; use collections::{HashMap, HashSet, IndexSet}; use futures::Future; use futures::future::LocalBoxFuture; use futures::lock::OwnedMutexGuard; use gpui::{App, AsyncApp, Entity, SharedString}; pub use highlight_map::HighlightMap; use http_client::HttpClient; pub use language_registry::{ LanguageName, LanguageServerStatusUpdate, LoadedLanguage, ServerHealth, }; use lsp::{CodeActionKind, InitializeParams, LanguageServerBinary, LanguageServerBinaryOptions}; pub use manifest::{ManifestDelegate, ManifestName, ManifestProvider, ManifestQuery}; use parking_lot::Mutex; use regex::Regex; use schemars::{JsonSchema, SchemaGenerator, json_schema}; use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; use serde_json::Value; use settings::WorktreeId; use smol::future::FutureExt as _; use std::num::NonZeroU32; use std::{ ffi::OsStr, fmt::Debug, hash::Hash, mem, ops::{DerefMut, Range}, path::{Path, PathBuf}, str, sync::{ Arc, LazyLock, atomic::{AtomicUsize, Ordering::SeqCst}, }, }; use syntax_map::{QueryCursorHandle, SyntaxSnapshot}; use task::RunnableTag; pub use task_context::{ContextLocation, ContextProvider, RunnableRange}; pub use text_diff::{ DiffOptions, apply_diff_patch, line_diff, text_diff, text_diff_with_options, unified_diff, }; use theme::SyntaxTheme; pub use toolchain::{ LanguageToolchainStore, LocalLanguageToolchainStore, Toolchain, ToolchainList, ToolchainLister, ToolchainMetadata, ToolchainScope, }; use tree_sitter::{self, Query, QueryCursor, WasmStore, wasmtime}; use util::rel_path::RelPath; use util::serde::default_true; pub use buffer::Operation; pub use buffer::*; pub use diagnostic_set::{DiagnosticEntry, DiagnosticEntryRef, DiagnosticGroup}; pub use language_registry::{ AvailableLanguage, BinaryStatus, LanguageNotFound, LanguageQueries, LanguageRegistry, QUERY_FILENAME_PREFIXES, }; pub use lsp::{LanguageServerId, LanguageServerName}; pub use outline::*; pub use syntax_map::{ OwnedSyntaxLayer, SyntaxLayer, SyntaxMapMatches, ToTreeSitterPoint, TreeSitterOptions, }; pub use text::{AnchorRangeExt, LineEnding}; pub use tree_sitter::{Node, Parser, Tree, TreeCursor}; static QUERY_CURSORS: Mutex<Vec<QueryCursor>> = Mutex::new(vec![]); static PARSERS: Mutex<Vec<Parser>> = Mutex::new(vec![]); pub fn with_parser<F, R>(func: F) -> R where F: FnOnce(&mut Parser) -> R, { let mut parser = PARSERS.lock().pop().unwrap_or_else(|| { let mut parser = Parser::new(); parser .set_wasm_store(WasmStore::new(&WASM_ENGINE).unwrap()) .unwrap(); parser }); parser.set_included_ranges(&[]).unwrap(); let result = func(&mut parser); PARSERS.lock().push(parser); result } pub fn with_query_cursor<F, R>(func: F) -> R where F: FnOnce(&mut QueryCursor) -> R, { let mut cursor = QueryCursorHandle::new(); func(cursor.deref_mut()) } static NEXT_LANGUAGE_ID: AtomicUsize = AtomicUsize::new(0); static NEXT_GRAMMAR_ID: AtomicUsize = AtomicUsize::new(0); static WASM_ENGINE: LazyLock<wasmtime::Engine> = LazyLock::new(|| { wasmtime::Engine::new(&wasmtime::Config::new()).expect("Failed to create Wasmtime engine") }); /// A shared grammar for plain text, exposed for reuse by downstream crates. pub static PLAIN_TEXT: LazyLock<Arc<Language>> = LazyLock::new(|| { Arc::new(Language::new( LanguageConfig { name: "Plain Text".into(), soft_wrap: Some(SoftWrap::EditorWidth), matcher: LanguageMatcher { path_suffixes: vec!["txt".to_owned()], first_line_pattern: None, }, ..Default::default() }, None, )) }); /// Types that represent a position in a buffer, and can be converted into /// an LSP position, to send to a language server. pub trait ToLspPosition { /// Converts the value into an LSP position. fn to_lsp_position(self) -> lsp::Position; } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Location { pub buffer: Entity<Buffer>, pub range: Range<Anchor>, } type ServerBinaryCache = futures::lock::Mutex<Option<(bool, LanguageServerBinary)>>; type DownloadableLanguageServerBinary = LocalBoxFuture<'static, Result<LanguageServerBinary>>; pub type LanguageServerBinaryLocations = LocalBoxFuture< 'static, ( Result<LanguageServerBinary>, Option<DownloadableLanguageServerBinary>, ), >; /// Represents a Language Server, with certain cached sync properties. /// Uses [`LspAdapter`] under the hood, but calls all 'static' methods /// once at startup, and caches the results. pub struct CachedLspAdapter { pub name: LanguageServerName, pub disk_based_diagnostic_sources: Vec<String>, pub disk_based_diagnostics_progress_token: Option<String>, language_ids: HashMap<LanguageName, String>, pub adapter: Arc<dyn LspAdapter>, cached_binary: Arc<ServerBinaryCache>, } impl Debug for CachedLspAdapter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CachedLspAdapter") .field("name", &self.name) .field( "disk_based_diagnostic_sources", &self.disk_based_diagnostic_sources, ) .field( "disk_based_diagnostics_progress_token", &self.disk_based_diagnostics_progress_token, ) .field("language_ids", &self.language_ids) .finish_non_exhaustive() } } impl CachedLspAdapter { pub fn new(adapter: Arc<dyn LspAdapter>) -> Arc<Self> { let name = adapter.name(); let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources(); let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token(); let language_ids = adapter.language_ids(); Arc::new(CachedLspAdapter { name, disk_based_diagnostic_sources, disk_based_diagnostics_progress_token, language_ids, adapter, cached_binary: Default::default(), }) } pub fn name(&self) -> LanguageServerName { self.adapter.name() } pub async fn get_language_server_command( self: Arc<Self>, delegate: Arc<dyn LspAdapterDelegate>, toolchains: Option<Toolchain>, binary_options: LanguageServerBinaryOptions, cx: &mut AsyncApp, ) -> LanguageServerBinaryLocations { let cached_binary = self.cached_binary.clone().lock_owned().await; self.adapter.clone().get_language_server_command( delegate, toolchains, binary_options, cached_binary, cx.clone(), ) } pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> { self.adapter.code_action_kinds() } pub fn process_diagnostics( &self, params: &mut lsp::PublishDiagnosticsParams, server_id: LanguageServerId, existing_diagnostics: Option<&'_ Buffer>, ) { self.adapter .process_diagnostics(params, server_id, existing_diagnostics) } pub fn retain_old_diagnostic(&self, previous_diagnostic: &Diagnostic, cx: &App) -> bool { self.adapter.retain_old_diagnostic(previous_diagnostic, cx) } pub fn underline_diagnostic(&self, diagnostic: &lsp::Diagnostic) -> bool { self.adapter.underline_diagnostic(diagnostic) } pub fn diagnostic_message_to_markdown(&self, message: &str) -> Option<String> { self.adapter.diagnostic_message_to_markdown(message) } pub async fn process_completions(&self, completion_items: &mut [lsp::CompletionItem]) { self.adapter.process_completions(completion_items).await } pub async fn labels_for_completions( &self, completion_items: &[lsp::CompletionItem], language: &Arc<Language>, ) -> Result<Vec<Option<CodeLabel>>> { self.adapter .clone() .labels_for_completions(completion_items, language) .await } pub async fn labels_for_symbols( &self, symbols: &[(String, lsp::SymbolKind)], language: &Arc<Language>, ) -> Result<Vec<Option<CodeLabel>>> { self.adapter .clone() .labels_for_symbols(symbols, language) .await } pub fn language_id(&self, language_name: &LanguageName) -> String { self.language_ids .get(language_name) .cloned() .unwrap_or_else(|| language_name.lsp_id()) } } /// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application // e.g. to display a notification or fetch data from the web. #[async_trait] pub trait LspAdapterDelegate: Send + Sync { fn show_notification(&self, message: &str, cx: &mut App); fn http_client(&self) -> Arc<dyn HttpClient>; fn worktree_id(&self) -> WorktreeId; fn worktree_root_path(&self) -> &Path; fn resolve_executable_path(&self, path: PathBuf) -> PathBuf; fn update_status(&self, language: LanguageServerName, status: BinaryStatus); fn registered_lsp_adapters(&self) -> Vec<Arc<dyn LspAdapter>>; async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>>; async fn npm_package_installed_version( &self, package_name: &str, ) -> Result<Option<(PathBuf, String)>>; async fn which(&self, command: &OsStr) -> Option<PathBuf>; async fn shell_env(&self) -> HashMap<String, String>; async fn read_text_file(&self, path: &RelPath) -> Result<String>; async fn try_exec(&self, binary: LanguageServerBinary) -> Result<()>; } #[async_trait(?Send)] pub trait LspAdapter: 'static + Send + Sync + DynLspInstaller { fn name(&self) -> LanguageServerName; fn process_diagnostics( &self, _: &mut lsp::PublishDiagnosticsParams, _: LanguageServerId, _: Option<&'_ Buffer>, ) { } /// When processing new `lsp::PublishDiagnosticsParams` diagnostics, whether to retain previous one(s) or not. fn retain_old_diagnostic(&self, _previous_diagnostic: &Diagnostic, _cx: &App) -> bool { false } /// Whether to underline a given diagnostic or not, when rendering in the editor. /// /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnosticTag /// states that /// > Clients are allowed to render diagnostics with this tag faded out instead of having an error squiggle. /// for the unnecessary diagnostics, so do not underline them. fn underline_diagnostic(&self, _diagnostic: &lsp::Diagnostic) -> bool { true } /// Post-processes completions provided by the language server. async fn process_completions(&self, _: &mut [lsp::CompletionItem]) {} fn diagnostic_message_to_markdown(&self, _message: &str) -> Option<String> { None } async fn labels_for_completions( self: Arc<Self>, completions: &[lsp::CompletionItem], language: &Arc<Language>, ) -> Result<Vec<Option<CodeLabel>>> { let mut labels = Vec::new(); for (ix, completion) in completions.iter().enumerate() { let label = self.label_for_completion(completion, language).await; if let Some(label) = label { labels.resize(ix + 1, None); *labels.last_mut().unwrap() = Some(label); } } Ok(labels) } async fn label_for_completion( &self, _: &lsp::CompletionItem, _: &Arc<Language>, ) -> Option<CodeLabel> { None } async fn labels_for_symbols( self: Arc<Self>, symbols: &[(String, lsp::SymbolKind)], language: &Arc<Language>, ) -> Result<Vec<Option<CodeLabel>>> { let mut labels = Vec::new(); for (ix, (name, kind)) in symbols.iter().enumerate() { let label = self.label_for_symbol(name, *kind, language).await; if let Some(label) = label { labels.resize(ix + 1, None); *labels.last_mut().unwrap() = Some(label); } } Ok(labels) } async fn label_for_symbol( &self, _: &str, _: lsp::SymbolKind, _: &Arc<Language>, ) -> Option<CodeLabel> { None } /// Returns initialization options that are going to be sent to a LSP server as a part of [`lsp::InitializeParams`] async fn initialization_options( self: Arc<Self>, _: &Arc<dyn LspAdapterDelegate>, ) -> Result<Option<Value>> { Ok(None) } async fn workspace_configuration( self: Arc<Self>, _: &Arc<dyn LspAdapterDelegate>, _: Option<Toolchain>, _cx: &mut AsyncApp, ) -> Result<Value> { Ok(serde_json::json!({})) } async fn additional_initialization_options( self: Arc<Self>, _target_language_server_id: LanguageServerName, _: &Arc<dyn LspAdapterDelegate>, ) -> Result<Option<Value>> { Ok(None) } async fn additional_workspace_configuration( self: Arc<Self>, _target_language_server_id: LanguageServerName, _: &Arc<dyn LspAdapterDelegate>, _cx: &mut AsyncApp, ) -> Result<Option<Value>> { Ok(None) } /// Returns a list of code actions supported by a given LspAdapter fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> { None } fn disk_based_diagnostic_sources(&self) -> Vec<String> { Default::default() } fn disk_based_diagnostics_progress_token(&self) -> Option<String> { None } fn language_ids(&self) -> HashMap<LanguageName, String> { HashMap::default() } /// Support custom initialize params. fn prepare_initialize_params( &self, original: InitializeParams, _: &App, ) -> Result<InitializeParams> { Ok(original) } /// Method only implemented by the default JSON language server adapter. /// Used to provide dynamic reloading of the JSON schemas used to /// provide autocompletion and diagnostics in Zed setting and keybind /// files fn is_primary_zed_json_schema_adapter(&self) -> bool { false } /// True for the extension adapter and false otherwise. fn is_extension(&self) -> bool { false } } pub trait LspInstaller { type BinaryVersion; fn check_if_user_installed( &self, _: &dyn LspAdapterDelegate, _: Option<Toolchain>, _: &AsyncApp, ) -> impl Future<Output = Option<LanguageServerBinary>> { async { None } } fn fetch_latest_server_version( &self, delegate: &dyn LspAdapterDelegate, pre_release: bool, cx: &mut AsyncApp, ) -> impl Future<Output = Result<Self::BinaryVersion>>; fn check_if_version_installed( &self, _version: &Self::BinaryVersion, _container_dir: &PathBuf, _delegate: &dyn LspAdapterDelegate, ) -> impl Future<Output = Option<LanguageServerBinary>> { async { None } } fn fetch_server_binary( &self, latest_version: Self::BinaryVersion,