//! 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, Uri,};pub use manifest::{ManifestDelegate, ManifestName, ManifestProvider, ManifestQuery};use parking_lot::Mutex;use regex::Regex;use schemars::{JsonSchema, SchemaGenerator, json_schema};use semver::Version;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, apply_reversed_diff_patch, char_diff, line_diff, text_diff, text_diff_with_options, unified_diff, unified_diff_with_context, unified_diff_with_offsets, word_diff_ranges,};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![]); #[ztracing::instrument(skip_all)]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, }, brackets: BracketPairConfig { pairs: vec![ BracketPair { start: "(".to_string(), end: ")".to_string(), close: true, surround: true, newline: false, }, BracketPair { start: "[".to_string(), end: "]".to_string(), close: true, surround: true, newline: false, }, BracketPair { start: "{".to_string(), end: "}".to_string(), close: true, surround: true, newline: false, }, BracketPair { start: "\"".to_string(), end: "\"".to_string(), close: true, surround: true, newline: false, }, BracketPair { start: "'".to_string(), end: "'".to_string(), close: true, surround: true, newline: false, }, ], disabled_scopes_by_bracket_ix: Default::default(), }, ..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>,} #[derive(Debug, Clone)]pub struct Symbol { pub name: String, pub kind: lsp::SymbolKind, pub container_name: Option<String>,} 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: &[Symbol], 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()) } pub async fn initialization_options_schema( &self, delegate: &Arc<dyn LspAdapterDelegate>, cx: &mut AsyncApp, ) -> Option<serde_json::Value> { self.adapter .clone() .initialization_options_schema( delegate, self.cached_binary.clone().lock_owned().await, cx, ) .await } pub async fn settings_schema( &self, delegate: &Arc<dyn LspAdapterDelegate>, cx: &mut AsyncApp, ) -> Option<serde_json::Value> { self.adapter .clone() .settings_schema(delegate, self.cached_binary.clone().lock_owned().await, cx) .await } pub fn process_prompt_response(&self, context: &PromptResponseContext, cx: &mut AsyncApp) { self.adapter.process_prompt_response(context, cx) }} /// [`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_relative_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, Version)>>; 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<()>;} /// Context provided to LSP adapters when a user responds to a ShowMessageRequest prompt./// This allows adapters to intercept preference selections (like "Always" or "Never")/// and potentially persist them to Zed's settings.#[derive(Debug, Clone)]pub struct PromptResponseContext { /// The original message shown to the user pub message: String, /// The action (button) the user selected pub selected_action: lsp::MessageActionItem,} #[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: &[Symbol], language: &Arc<Language>, ) -> Result<Vec<Option<CodeLabel>>> { let mut labels = Vec::new(); for (ix, symbol) in symbols.iter().enumerate() { let label = self.label_for_symbol(symbol, 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, _symbol: &Symbol, _language: &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) } /// Returns the JSON schema of the initialization_options for the language server. async fn initialization_options_schema( self: Arc<Self>,Lines 1–500 of 3086. 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, Uri, }; pub use manifest::{ManifestDelegate, ManifestName, ManifestProvider, ManifestQuery}; use parking_lot::Mutex; use regex::Regex; use schemars::{JsonSchema, SchemaGenerator, json_schema}; use semver::Version; 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, apply_reversed_diff_patch, char_diff, line_diff, text_diff, text_diff_with_options, unified_diff, unified_diff_with_context, unified_diff_with_offsets, word_diff_ranges, }; 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![]); #[ztracing::instrument(skip_all)] 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, }, brackets: BracketPairConfig { pairs: vec![ BracketPair { start: "(".to_string(), end: ")".to_string(), close: true, surround: true, newline: false, }, BracketPair { start: "[".to_string(), end: "]".to_string(), close: true, surround: true, newline: false, }, BracketPair { start: "{".to_string(), end: "}".to_string(), close: true, surround: true, newline: false, }, BracketPair { start: "\"".to_string(), end: "\"".to_string(), close: true, surround: true, newline: false, }, BracketPair { start: "'".to_string(), end: "'".to_string(), close: true, surround: true, newline: false, }, ], disabled_scopes_by_bracket_ix: Default::default(), }, ..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>, } #[derive(Debug, Clone)] pub struct Symbol { pub name: String, pub kind: lsp::SymbolKind, pub container_name: Option<String>, } 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: &[Symbol], 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()) } pub async fn initialization_options_schema( &self, delegate: &Arc<dyn LspAdapterDelegate>, cx: &mut AsyncApp, ) -> Option<serde_json::Value> { self.adapter .clone() .initialization_options_schema( delegate, self.cached_binary.clone().lock_owned().await, cx, ) .await } pub async fn settings_schema( &self, delegate: &Arc<dyn LspAdapterDelegate>, cx: &mut AsyncApp, ) -> Option<serde_json::Value> { self.adapter .clone() .settings_schema(delegate, self.cached_binary.clone().lock_owned().await, cx) .await } pub fn process_prompt_response(&self, context: &PromptResponseContext, cx: &mut AsyncApp) { self.adapter.process_prompt_response(context, cx) } } /// [`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_relative_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, Version)>>; 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<()>; } /// Context provided to LSP adapters when a user responds to a ShowMessageRequest prompt. /// This allows adapters to intercept preference selections (like "Always" or "Never") /// and potentially persist them to Zed's settings. #[derive(Debug, Clone)] pub struct PromptResponseContext { /// The original message shown to the user pub message: String, /// The action (button) the user selected pub selected_action: lsp::MessageActionItem, } #[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: &[Symbol], language: &Arc<Language>, ) -> Result<Vec<Option<CodeLabel>>> { let mut labels = Vec::new(); for (ix, symbol) in symbols.iter().enumerate() { let label = self.label_for_symbol(symbol, 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, _symbol: &Symbol, _language: &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) } /// Returns the JSON schema of the initialization_options for the language server. async fn initialization_options_schema( self: Arc<Self>,