extension.rs

  1mod capabilities;
  2pub mod extension_builder;
  3mod extension_events;
  4mod extension_host_proxy;
  5mod extension_manifest;
  6mod types;
  7
  8use std::path::{Path, PathBuf};
  9use std::sync::Arc;
 10
 11use ::lsp::LanguageServerName;
 12use anyhow::{Context as _, Result, bail};
 13use async_trait::async_trait;
 14use fs::normalize_path;
 15use gpui::{App, Task};
 16use language::LanguageName;
 17use semver::Version;
 18use task::{SpawnInTerminal, ZedDebugConfig};
 19use util::rel_path::RelPath;
 20
 21pub use crate::capabilities::*;
 22pub use crate::extension_events::*;
 23pub use crate::extension_host_proxy::*;
 24pub use crate::extension_manifest::*;
 25pub use crate::types::*;
 26
 27/// Initializes the `extension` crate.
 28pub fn init(cx: &mut App) {
 29    extension_events::init(cx);
 30    ExtensionHostProxy::default_global(cx);
 31}
 32
 33#[async_trait]
 34pub trait WorktreeDelegate: Send + Sync + 'static {
 35    fn id(&self) -> u64;
 36    fn root_path(&self) -> String;
 37    async fn read_text_file(&self, path: &RelPath) -> Result<String>;
 38    async fn which(&self, binary_name: String) -> Option<String>;
 39    async fn shell_env(&self) -> Vec<(String, String)>;
 40}
 41
 42pub trait ProjectDelegate: Send + Sync + 'static {
 43    fn worktree_ids(&self) -> Vec<u64>;
 44}
 45
 46pub trait KeyValueStoreDelegate: Send + Sync + 'static {
 47    fn insert(&self, key: String, docs: String) -> Task<Result<()>>;
 48}
 49
 50#[async_trait]
 51pub trait Extension: Send + Sync + 'static {
 52    /// Returns the [`ExtensionManifest`] for this extension.
 53    fn manifest(&self) -> Arc<ExtensionManifest>;
 54
 55    /// Returns the path to this extension's working directory.
 56    fn work_dir(&self) -> Arc<Path>;
 57
 58    /// Returns a path relative to this extension's working directory.
 59    fn path_from_extension(&self, path: &Path) -> PathBuf {
 60        normalize_path(&self.work_dir().join(path))
 61    }
 62
 63    async fn language_server_command(
 64        &self,
 65        language_server_id: LanguageServerName,
 66        language_name: LanguageName,
 67        worktree: Arc<dyn WorktreeDelegate>,
 68    ) -> Result<Command>;
 69
 70    async fn language_server_initialization_options(
 71        &self,
 72        language_server_id: LanguageServerName,
 73        language_name: LanguageName,
 74        worktree: Arc<dyn WorktreeDelegate>,
 75    ) -> Result<Option<String>>;
 76
 77    async fn language_server_workspace_configuration(
 78        &self,
 79        language_server_id: LanguageServerName,
 80        worktree: Arc<dyn WorktreeDelegate>,
 81    ) -> Result<Option<String>>;
 82
 83    async fn language_server_initialization_options_schema(
 84        &self,
 85        language_server_id: LanguageServerName,
 86        worktree: Arc<dyn WorktreeDelegate>,
 87    ) -> Result<Option<String>>;
 88
 89    async fn language_server_workspace_configuration_schema(
 90        &self,
 91        language_server_id: LanguageServerName,
 92        worktree: Arc<dyn WorktreeDelegate>,
 93    ) -> Result<Option<String>>;
 94
 95    async fn language_server_additional_initialization_options(
 96        &self,
 97        language_server_id: LanguageServerName,
 98        target_language_server_id: LanguageServerName,
 99        worktree: Arc<dyn WorktreeDelegate>,
100    ) -> Result<Option<String>>;
101
102    async fn language_server_additional_workspace_configuration(
103        &self,
104        language_server_id: LanguageServerName,
105        target_language_server_id: LanguageServerName,
106        worktree: Arc<dyn WorktreeDelegate>,
107    ) -> Result<Option<String>>;
108
109    async fn labels_for_completions(
110        &self,
111        language_server_id: LanguageServerName,
112        completions: Vec<Completion>,
113    ) -> Result<Vec<Option<CodeLabel>>>;
114
115    async fn labels_for_symbols(
116        &self,
117        language_server_id: LanguageServerName,
118        symbols: Vec<Symbol>,
119    ) -> Result<Vec<Option<CodeLabel>>>;
120
121    async fn complete_slash_command_argument(
122        &self,
123        command: SlashCommand,
124        arguments: Vec<String>,
125    ) -> Result<Vec<SlashCommandArgumentCompletion>>;
126
127    async fn run_slash_command(
128        &self,
129        command: SlashCommand,
130        arguments: Vec<String>,
131        worktree: Option<Arc<dyn WorktreeDelegate>>,
132    ) -> Result<SlashCommandOutput>;
133
134    async fn context_server_command(
135        &self,
136        context_server_id: Arc<str>,
137        project: Arc<dyn ProjectDelegate>,
138    ) -> Result<Command>;
139
140    async fn context_server_configuration(
141        &self,
142        context_server_id: Arc<str>,
143        project: Arc<dyn ProjectDelegate>,
144    ) -> Result<Option<ContextServerConfiguration>>;
145
146    async fn suggest_docs_packages(&self, provider: Arc<str>) -> Result<Vec<String>>;
147
148    async fn index_docs(
149        &self,
150        provider: Arc<str>,
151        package_name: Arc<str>,
152        kv_store: Arc<dyn KeyValueStoreDelegate>,
153    ) -> Result<()>;
154
155    async fn get_dap_binary(
156        &self,
157        dap_name: Arc<str>,
158        config: DebugTaskDefinition,
159        user_installed_path: Option<PathBuf>,
160        worktree: Arc<dyn WorktreeDelegate>,
161    ) -> Result<DebugAdapterBinary>;
162
163    async fn dap_request_kind(
164        &self,
165        dap_name: Arc<str>,
166        config: serde_json::Value,
167    ) -> Result<StartDebuggingRequestArgumentsRequest>;
168
169    async fn dap_config_to_scenario(&self, config: ZedDebugConfig) -> Result<DebugScenario>;
170
171    async fn dap_locator_create_scenario(
172        &self,
173        locator_name: String,
174        build_config_template: BuildTaskTemplate,
175        resolved_label: String,
176        debug_adapter_name: String,
177    ) -> Result<Option<DebugScenario>>;
178    async fn run_dap_locator(
179        &self,
180        locator_name: String,
181        config: SpawnInTerminal,
182    ) -> Result<DebugRequest>;
183}
184
185pub fn parse_wasm_extension_version(extension_id: &str, wasm_bytes: &[u8]) -> Result<Version> {
186    let mut version = None;
187
188    for part in wasmparser::Parser::new(0).parse_all(wasm_bytes) {
189        if let wasmparser::Payload::CustomSection(s) =
190            part.context("error parsing wasm extension")?
191            && s.name() == "zed:api-version"
192        {
193            version = parse_wasm_extension_version_custom_section(s.data());
194            if version.is_none() {
195                bail!(
196                    "extension {} has invalid zed:api-version section: {:?}",
197                    extension_id,
198                    s.data()
199                );
200            }
201        }
202    }
203
204    // The reason we wait until we're done parsing all of the Wasm bytes to return the version
205    // is to work around a panic that can happen inside of Wasmtime when the bytes are invalid.
206    //
207    // By parsing the entirety of the Wasm bytes before we return, we're able to detect this problem
208    // earlier as an `Err` rather than as a panic.
209    version.with_context(|| format!("extension {extension_id} has no zed:api-version section"))
210}
211
212fn parse_wasm_extension_version_custom_section(data: &[u8]) -> Option<Version> {
213    if data.len() == 6 {
214        Some(Version::new(
215            u16::from_be_bytes([data[0], data[1]]) as _,
216            u16::from_be_bytes([data[2], data[3]]) as _,
217            u16::from_be_bytes([data[4], data[5]]) as _,
218        ))
219    } else {
220        None
221    }
222}