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