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