mod capabilities; pub mod extension_builder; mod extension_events; mod extension_host_proxy; mod extension_manifest; mod types; use std::path::{Path, PathBuf}; use std::sync::Arc; use ::lsp::LanguageServerName; use anyhow::{Context as _, Result, bail}; use async_trait::async_trait; use fs::normalize_path; use gpui::{App, Task}; use language::LanguageName; use semantic_version::SemanticVersion; use task::{SpawnInTerminal, ZedDebugConfig}; pub use crate::capabilities::*; pub use crate::extension_events::*; pub use crate::extension_host_proxy::*; pub use crate::extension_manifest::*; pub use crate::types::*; /// Initializes the `extension` crate. pub fn init(cx: &mut App) { extension_events::init(cx); ExtensionHostProxy::default_global(cx); } #[async_trait] pub trait WorktreeDelegate: Send + Sync + 'static { fn id(&self) -> u64; fn root_path(&self) -> String; async fn read_text_file(&self, path: PathBuf) -> Result; async fn which(&self, binary_name: String) -> Option; async fn shell_env(&self) -> Vec<(String, String)>; } pub trait ProjectDelegate: Send + Sync + 'static { fn worktree_ids(&self) -> Vec; } pub trait KeyValueStoreDelegate: Send + Sync + 'static { fn insert(&self, key: String, docs: String) -> Task>; } #[async_trait] pub trait Extension: Send + Sync + 'static { /// Returns the [`ExtensionManifest`] for this extension. fn manifest(&self) -> Arc; /// Returns the path to this extension's working directory. fn work_dir(&self) -> Arc; /// Returns a path relative to this extension's working directory. fn path_from_extension(&self, path: &Path) -> PathBuf { normalize_path(&self.work_dir().join(path)) } async fn language_server_command( &self, language_server_id: LanguageServerName, language_name: LanguageName, worktree: Arc, ) -> Result; async fn language_server_initialization_options( &self, language_server_id: LanguageServerName, language_name: LanguageName, worktree: Arc, ) -> Result>; async fn language_server_workspace_configuration( &self, language_server_id: LanguageServerName, worktree: Arc, ) -> Result>; async fn language_server_additional_initialization_options( &self, language_server_id: LanguageServerName, target_language_server_id: LanguageServerName, worktree: Arc, ) -> Result>; async fn language_server_additional_workspace_configuration( &self, language_server_id: LanguageServerName, target_language_server_id: LanguageServerName, worktree: Arc, ) -> Result>; async fn labels_for_completions( &self, language_server_id: LanguageServerName, completions: Vec, ) -> Result>>; async fn labels_for_symbols( &self, language_server_id: LanguageServerName, symbols: Vec, ) -> Result>>; async fn complete_slash_command_argument( &self, command: SlashCommand, arguments: Vec, ) -> Result>; async fn run_slash_command( &self, command: SlashCommand, arguments: Vec, worktree: Option>, ) -> Result; async fn context_server_command( &self, context_server_id: Arc, project: Arc, ) -> Result; async fn context_server_configuration( &self, context_server_id: Arc, project: Arc, ) -> Result>; async fn suggest_docs_packages(&self, provider: Arc) -> Result>; async fn index_docs( &self, provider: Arc, package_name: Arc, kv_store: Arc, ) -> Result<()>; async fn get_dap_binary( &self, dap_name: Arc, config: DebugTaskDefinition, user_installed_path: Option, worktree: Arc, ) -> Result; async fn dap_request_kind( &self, dap_name: Arc, config: serde_json::Value, ) -> Result; async fn dap_config_to_scenario(&self, config: ZedDebugConfig) -> Result; async fn dap_locator_create_scenario( &self, locator_name: String, build_config_template: BuildTaskTemplate, resolved_label: String, debug_adapter_name: String, ) -> Result>; async fn run_dap_locator( &self, locator_name: String, config: SpawnInTerminal, ) -> Result; } pub fn parse_wasm_extension_version( extension_id: &str, wasm_bytes: &[u8], ) -> Result { let mut version = None; for part in wasmparser::Parser::new(0).parse_all(wasm_bytes) { if let wasmparser::Payload::CustomSection(s) = part.context("error parsing wasm extension")? && s.name() == "zed:api-version" { version = parse_wasm_extension_version_custom_section(s.data()); if version.is_none() { bail!( "extension {} has invalid zed:api-version section: {:?}", extension_id, s.data() ); } } } // The reason we wait until we're done parsing all of the Wasm bytes to return the version // is to work around a panic that can happen inside of Wasmtime when the bytes are invalid. // // By parsing the entirety of the Wasm bytes before we return, we're able to detect this problem // earlier as an `Err` rather than as a panic. version.with_context(|| format!("extension {extension_id} has no zed:api-version section")) } fn parse_wasm_extension_version_custom_section(data: &[u8]) -> Option { if data.len() == 6 { Some(SemanticVersion::new( u16::from_be_bytes([data[0], data[1]]) as _, u16::from_be_bytes([data[2], data[3]]) as _, u16::from_be_bytes([data[4], data[5]]) as _, )) } else { None } }