1pub mod extension_lsp_adapter;
2pub mod extension_settings;
3pub mod wasm_host;
4
5use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit};
6use anyhow::{anyhow, bail, Context as _, Result};
7use async_compression::futures::bufread::GzipDecoder;
8use async_tar::Archive;
9use client::{telemetry::Telemetry, Client, ExtensionMetadata, GetExtensionsResponse};
10use collections::{btree_map, BTreeMap, HashSet};
11use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder};
12use extension::Extension;
13pub use extension::ExtensionManifest;
14use fs::{Fs, RemoveOptions};
15use futures::{
16 channel::{
17 mpsc::{unbounded, UnboundedSender},
18 oneshot,
19 },
20 io::BufReader,
21 select_biased, AsyncReadExt as _, Future, FutureExt as _, StreamExt as _,
22};
23use gpui::{
24 actions, AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext,
25 SharedString, Task, WeakModel,
26};
27use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
28use language::{
29 LanguageConfig, LanguageMatcher, LanguageName, LanguageQueries, LoadedLanguage,
30 QUERY_FILENAME_PREFIXES,
31};
32use lsp::LanguageServerName;
33use node_runtime::NodeRuntime;
34use project::ContextProviderWithTasks;
35use release_channel::ReleaseChannel;
36use semantic_version::SemanticVersion;
37use serde::{Deserialize, Serialize};
38use settings::Settings;
39use std::ops::RangeInclusive;
40use std::str::FromStr;
41use std::{
42 cmp::Ordering,
43 path::{self, Path, PathBuf},
44 sync::Arc,
45 time::{Duration, Instant},
46};
47use url::Url;
48use util::ResultExt;
49use wasm_host::{
50 wit::{is_supported_wasm_api_version, wasm_api_version_range},
51 WasmExtension, WasmHost,
52};
53
54pub use extension::{
55 ExtensionLibraryKind, GrammarManifestEntry, OldExtensionManifest, SchemaVersion,
56};
57pub use extension_settings::ExtensionSettings;
58
59pub const RELOAD_DEBOUNCE_DURATION: Duration = Duration::from_millis(200);
60const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
61
62/// The current extension [`SchemaVersion`] supported by Zed.
63const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(1);
64
65/// Returns the [`SchemaVersion`] range that is compatible with this version of Zed.
66pub fn schema_version_range() -> RangeInclusive<SchemaVersion> {
67 SchemaVersion::ZERO..=CURRENT_SCHEMA_VERSION
68}
69
70/// Returns whether the given extension version is compatible with this version of Zed.
71pub fn is_version_compatible(
72 release_channel: ReleaseChannel,
73 extension_version: &ExtensionMetadata,
74) -> bool {
75 let schema_version = extension_version.manifest.schema_version.unwrap_or(0);
76 if CURRENT_SCHEMA_VERSION.0 < schema_version {
77 return false;
78 }
79
80 if let Some(wasm_api_version) = extension_version
81 .manifest
82 .wasm_api_version
83 .as_ref()
84 .and_then(|wasm_api_version| SemanticVersion::from_str(wasm_api_version).ok())
85 {
86 if !is_supported_wasm_api_version(release_channel, wasm_api_version) {
87 return false;
88 }
89 }
90
91 true
92}
93
94pub trait ExtensionRegistrationHooks: Send + Sync + 'static {
95 fn remove_user_themes(&self, _themes: Vec<SharedString>) {}
96
97 fn load_user_theme(&self, _theme_path: PathBuf, _fs: Arc<dyn Fs>) -> Task<Result<()>> {
98 Task::ready(Ok(()))
99 }
100
101 fn list_theme_names(
102 &self,
103 _theme_path: PathBuf,
104 _fs: Arc<dyn Fs>,
105 ) -> Task<Result<Vec<String>>> {
106 Task::ready(Ok(Vec::new()))
107 }
108
109 fn reload_current_theme(&self, _cx: &mut AppContext) {}
110
111 fn register_language(
112 &self,
113 _language: LanguageName,
114 _grammar: Option<Arc<str>>,
115 _matcher: language::LanguageMatcher,
116 _load: Arc<dyn Fn() -> Result<LoadedLanguage> + 'static + Send + Sync>,
117 ) {
118 }
119
120 fn register_lsp_adapter(&self, _language: LanguageName, _adapter: ExtensionLspAdapter) {}
121
122 fn remove_lsp_adapter(&self, _language: &LanguageName, _server_name: &LanguageServerName) {}
123
124 fn register_wasm_grammars(&self, _grammars: Vec<(Arc<str>, PathBuf)>) {}
125
126 fn remove_languages(
127 &self,
128 _languages_to_remove: &[LanguageName],
129 _grammars_to_remove: &[Arc<str>],
130 ) {
131 }
132
133 fn register_slash_command(
134 &self,
135 _extension: Arc<dyn Extension>,
136 _command: extension::SlashCommand,
137 ) {
138 }
139
140 fn register_context_server(
141 &self,
142 _id: Arc<str>,
143 _extension: WasmExtension,
144 _cx: &mut AppContext,
145 ) {
146 }
147
148 fn register_docs_provider(&self, _extension: Arc<dyn Extension>, _provider_id: Arc<str>) {}
149
150 fn register_snippets(&self, _path: &PathBuf, _snippet_contents: &str) -> Result<()> {
151 Ok(())
152 }
153
154 fn update_lsp_status(
155 &self,
156 _server_name: lsp::LanguageServerName,
157 _status: language::LanguageServerBinaryStatus,
158 ) {
159 }
160}
161
162pub struct ExtensionStore {
163 pub registration_hooks: Arc<dyn ExtensionRegistrationHooks>,
164 pub builder: Arc<ExtensionBuilder>,
165 pub extension_index: ExtensionIndex,
166 pub fs: Arc<dyn Fs>,
167 pub http_client: Arc<HttpClientWithUrl>,
168 pub telemetry: Option<Arc<Telemetry>>,
169 pub reload_tx: UnboundedSender<Option<Arc<str>>>,
170 pub reload_complete_senders: Vec<oneshot::Sender<()>>,
171 pub installed_dir: PathBuf,
172 pub outstanding_operations: BTreeMap<Arc<str>, ExtensionOperation>,
173 pub index_path: PathBuf,
174 pub modified_extensions: HashSet<Arc<str>>,
175 pub wasm_host: Arc<WasmHost>,
176 pub wasm_extensions: Vec<(Arc<ExtensionManifest>, WasmExtension)>,
177 pub tasks: Vec<Task<()>>,
178}
179
180#[derive(Clone, Copy)]
181pub enum ExtensionOperation {
182 Upgrade,
183 Install,
184 Remove,
185}
186
187#[derive(Clone)]
188pub enum Event {
189 ExtensionsUpdated,
190 StartedReloading,
191 ExtensionInstalled(Arc<str>),
192 ExtensionFailedToLoad(Arc<str>),
193}
194
195impl EventEmitter<Event> for ExtensionStore {}
196
197struct GlobalExtensionStore(Model<ExtensionStore>);
198
199impl Global for GlobalExtensionStore {}
200
201#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
202pub struct ExtensionIndex {
203 pub extensions: BTreeMap<Arc<str>, ExtensionIndexEntry>,
204 pub themes: BTreeMap<Arc<str>, ExtensionIndexThemeEntry>,
205 pub languages: BTreeMap<LanguageName, ExtensionIndexLanguageEntry>,
206}
207
208#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
209pub struct ExtensionIndexEntry {
210 pub manifest: Arc<ExtensionManifest>,
211 pub dev: bool,
212}
213
214#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
215pub struct ExtensionIndexThemeEntry {
216 pub extension: Arc<str>,
217 pub path: PathBuf,
218}
219
220#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
221pub struct ExtensionIndexLanguageEntry {
222 pub extension: Arc<str>,
223 pub path: PathBuf,
224 pub matcher: LanguageMatcher,
225 pub grammar: Option<Arc<str>>,
226}
227
228actions!(zed, [ReloadExtensions]);
229
230pub fn init(
231 registration_hooks: Arc<dyn ExtensionRegistrationHooks>,
232 fs: Arc<dyn Fs>,
233 client: Arc<Client>,
234 node_runtime: NodeRuntime,
235 cx: &mut AppContext,
236) {
237 ExtensionSettings::register(cx);
238
239 let store = cx.new_model(move |cx| {
240 ExtensionStore::new(
241 paths::extensions_dir().clone(),
242 None,
243 registration_hooks,
244 fs,
245 client.http_client().clone(),
246 client.http_client().clone(),
247 Some(client.telemetry().clone()),
248 node_runtime,
249 cx,
250 )
251 });
252
253 cx.on_action(|_: &ReloadExtensions, cx| {
254 let store = cx.global::<GlobalExtensionStore>().0.clone();
255 store.update(cx, |store, cx| drop(store.reload(None, cx)));
256 });
257
258 cx.set_global(GlobalExtensionStore(store));
259}
260
261impl ExtensionStore {
262 pub fn try_global(cx: &AppContext) -> Option<Model<Self>> {
263 cx.try_global::<GlobalExtensionStore>()
264 .map(|store| store.0.clone())
265 }
266
267 pub fn global(cx: &AppContext) -> Model<Self> {
268 cx.global::<GlobalExtensionStore>().0.clone()
269 }
270
271 #[allow(clippy::too_many_arguments)]
272 pub fn new(
273 extensions_dir: PathBuf,
274 build_dir: Option<PathBuf>,
275 extension_api: Arc<dyn ExtensionRegistrationHooks>,
276 fs: Arc<dyn Fs>,
277 http_client: Arc<HttpClientWithUrl>,
278 builder_client: Arc<dyn HttpClient>,
279 telemetry: Option<Arc<Telemetry>>,
280 node_runtime: NodeRuntime,
281 cx: &mut ModelContext<Self>,
282 ) -> Self {
283 let work_dir = extensions_dir.join("work");
284 let build_dir = build_dir.unwrap_or_else(|| extensions_dir.join("build"));
285 let installed_dir = extensions_dir.join("installed");
286 let index_path = extensions_dir.join("index.json");
287
288 let (reload_tx, mut reload_rx) = unbounded();
289 let mut this = Self {
290 registration_hooks: extension_api.clone(),
291 extension_index: Default::default(),
292 installed_dir,
293 index_path,
294 builder: Arc::new(ExtensionBuilder::new(builder_client, build_dir)),
295 outstanding_operations: Default::default(),
296 modified_extensions: Default::default(),
297 reload_complete_senders: Vec::new(),
298 wasm_host: WasmHost::new(
299 fs.clone(),
300 http_client.clone(),
301 node_runtime,
302 extension_api,
303 work_dir,
304 cx,
305 ),
306 wasm_extensions: Vec::new(),
307 fs,
308 http_client,
309 telemetry,
310 reload_tx,
311 tasks: Vec::new(),
312 };
313
314 // The extensions store maintains an index file, which contains a complete
315 // list of the installed extensions and the resources that they provide.
316 // This index is loaded synchronously on startup.
317 let (index_content, index_metadata, extensions_metadata) =
318 cx.background_executor().block(async {
319 futures::join!(
320 this.fs.load(&this.index_path),
321 this.fs.metadata(&this.index_path),
322 this.fs.metadata(&this.installed_dir),
323 )
324 });
325
326 // Normally, there is no need to rebuild the index. But if the index file
327 // is invalid or is out-of-date according to the filesystem mtimes, then
328 // it must be asynchronously rebuilt.
329 let mut extension_index = ExtensionIndex::default();
330 let mut extension_index_needs_rebuild = true;
331 if let Ok(index_content) = index_content {
332 if let Some(index) = serde_json::from_str(&index_content).log_err() {
333 extension_index = index;
334 if let (Ok(Some(index_metadata)), Ok(Some(extensions_metadata))) =
335 (index_metadata, extensions_metadata)
336 {
337 if index_metadata.mtime > extensions_metadata.mtime {
338 extension_index_needs_rebuild = false;
339 }
340 }
341 }
342 }
343
344 // Immediately load all of the extensions in the initial manifest. If the
345 // index needs to be rebuild, then enqueue
346 let load_initial_extensions = this.extensions_updated(extension_index, cx);
347 let mut reload_future = None;
348 if extension_index_needs_rebuild {
349 reload_future = Some(this.reload(None, cx));
350 }
351
352 cx.spawn(|this, mut cx| async move {
353 if let Some(future) = reload_future {
354 future.await;
355 }
356 this.update(&mut cx, |this, cx| this.auto_install_extensions(cx))
357 .ok();
358 this.update(&mut cx, |this, cx| this.check_for_updates(cx))
359 .ok();
360 })
361 .detach();
362
363 // Perform all extension loading in a single task to ensure that we
364 // never attempt to simultaneously load/unload extensions from multiple
365 // parallel tasks.
366 this.tasks.push(cx.spawn(|this, mut cx| {
367 async move {
368 load_initial_extensions.await;
369
370 let mut index_changed = false;
371 let mut debounce_timer = cx
372 .background_executor()
373 .spawn(futures::future::pending())
374 .fuse();
375 loop {
376 select_biased! {
377 _ = debounce_timer => {
378 if index_changed {
379 let index = this
380 .update(&mut cx, |this, cx| this.rebuild_extension_index(cx))?
381 .await;
382 this.update(&mut cx, |this, cx| this.extensions_updated(index, cx))?
383 .await;
384 index_changed = false;
385 }
386 }
387 extension_id = reload_rx.next() => {
388 let Some(extension_id) = extension_id else { break; };
389 this.update(&mut cx, |this, _| {
390 this.modified_extensions.extend(extension_id);
391 })?;
392 index_changed = true;
393 debounce_timer = cx
394 .background_executor()
395 .timer(RELOAD_DEBOUNCE_DURATION)
396 .fuse();
397 }
398 }
399 }
400
401 anyhow::Ok(())
402 }
403 .map(drop)
404 }));
405
406 // Watch the installed extensions directory for changes. Whenever changes are
407 // detected, rebuild the extension index, and load/unload any extensions that
408 // have been added, removed, or modified.
409 this.tasks.push(cx.background_executor().spawn({
410 let fs = this.fs.clone();
411 let reload_tx = this.reload_tx.clone();
412 let installed_dir = this.installed_dir.clone();
413 async move {
414 let (mut paths, _) = fs.watch(&installed_dir, FS_WATCH_LATENCY).await;
415 while let Some(events) = paths.next().await {
416 for event in events {
417 let Ok(event_path) = event.path.strip_prefix(&installed_dir) else {
418 continue;
419 };
420
421 if let Some(path::Component::Normal(extension_dir_name)) =
422 event_path.components().next()
423 {
424 if let Some(extension_id) = extension_dir_name.to_str() {
425 reload_tx.unbounded_send(Some(extension_id.into())).ok();
426 }
427 }
428 }
429 }
430 }
431 }));
432
433 this
434 }
435
436 pub fn reload(
437 &mut self,
438 modified_extension: Option<Arc<str>>,
439 cx: &mut ModelContext<Self>,
440 ) -> impl Future<Output = ()> {
441 let (tx, rx) = oneshot::channel();
442 self.reload_complete_senders.push(tx);
443 self.reload_tx
444 .unbounded_send(modified_extension)
445 .expect("reload task exited");
446 cx.emit(Event::StartedReloading);
447
448 async move {
449 rx.await.ok();
450 }
451 }
452
453 fn extensions_dir(&self) -> PathBuf {
454 self.installed_dir.clone()
455 }
456
457 pub fn outstanding_operations(&self) -> &BTreeMap<Arc<str>, ExtensionOperation> {
458 &self.outstanding_operations
459 }
460
461 pub fn installed_extensions(&self) -> &BTreeMap<Arc<str>, ExtensionIndexEntry> {
462 &self.extension_index.extensions
463 }
464
465 pub fn dev_extensions(&self) -> impl Iterator<Item = &Arc<ExtensionManifest>> {
466 self.extension_index
467 .extensions
468 .values()
469 .filter_map(|extension| extension.dev.then_some(&extension.manifest))
470 }
471
472 /// Returns the names of themes provided by extensions.
473 pub fn extension_themes<'a>(
474 &'a self,
475 extension_id: &'a str,
476 ) -> impl Iterator<Item = &'a Arc<str>> {
477 self.extension_index
478 .themes
479 .iter()
480 .filter_map(|(name, theme)| theme.extension.as_ref().eq(extension_id).then_some(name))
481 }
482
483 pub fn fetch_extensions(
484 &self,
485 search: Option<&str>,
486 cx: &mut ModelContext<Self>,
487 ) -> Task<Result<Vec<ExtensionMetadata>>> {
488 let version = CURRENT_SCHEMA_VERSION.to_string();
489 let mut query = vec![("max_schema_version", version.as_str())];
490 if let Some(search) = search {
491 query.push(("filter", search));
492 }
493
494 self.fetch_extensions_from_api("/extensions", &query, cx)
495 }
496
497 pub fn fetch_extensions_with_update_available(
498 &mut self,
499 cx: &mut ModelContext<Self>,
500 ) -> Task<Result<Vec<ExtensionMetadata>>> {
501 let schema_versions = schema_version_range();
502 let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
503 let extension_settings = ExtensionSettings::get_global(cx);
504 let extension_ids = self
505 .extension_index
506 .extensions
507 .iter()
508 .filter(|(id, entry)| !entry.dev && extension_settings.should_auto_update(id))
509 .map(|(id, _)| id.as_ref())
510 .collect::<Vec<_>>()
511 .join(",");
512 let task = self.fetch_extensions_from_api(
513 "/extensions/updates",
514 &[
515 ("min_schema_version", &schema_versions.start().to_string()),
516 ("max_schema_version", &schema_versions.end().to_string()),
517 (
518 "min_wasm_api_version",
519 &wasm_api_versions.start().to_string(),
520 ),
521 ("max_wasm_api_version", &wasm_api_versions.end().to_string()),
522 ("ids", &extension_ids),
523 ],
524 cx,
525 );
526 cx.spawn(move |this, mut cx| async move {
527 let extensions = task.await?;
528 this.update(&mut cx, |this, _cx| {
529 extensions
530 .into_iter()
531 .filter(|extension| {
532 this.extension_index.extensions.get(&extension.id).map_or(
533 true,
534 |installed_extension| {
535 installed_extension.manifest.version != extension.manifest.version
536 },
537 )
538 })
539 .collect()
540 })
541 })
542 }
543
544 pub fn fetch_extension_versions(
545 &self,
546 extension_id: &str,
547 cx: &mut ModelContext<Self>,
548 ) -> Task<Result<Vec<ExtensionMetadata>>> {
549 self.fetch_extensions_from_api(&format!("/extensions/{extension_id}"), &[], cx)
550 }
551
552 /// Installs any extensions that should be included with Zed by default.
553 ///
554 /// This can be used to make certain functionality provided by extensions
555 /// available out-of-the-box.
556 pub fn auto_install_extensions(&mut self, cx: &mut ModelContext<Self>) {
557 let extension_settings = ExtensionSettings::get_global(cx);
558
559 let extensions_to_install = extension_settings
560 .auto_install_extensions
561 .keys()
562 .filter(|extension_id| extension_settings.should_auto_install(extension_id))
563 .filter(|extension_id| {
564 let is_already_installed = self
565 .extension_index
566 .extensions
567 .contains_key(extension_id.as_ref());
568 !is_already_installed
569 })
570 .cloned()
571 .collect::<Vec<_>>();
572
573 cx.spawn(move |this, mut cx| async move {
574 for extension_id in extensions_to_install {
575 this.update(&mut cx, |this, cx| {
576 this.install_latest_extension(extension_id.clone(), cx);
577 })
578 .ok();
579 }
580 })
581 .detach();
582 }
583
584 pub fn check_for_updates(&mut self, cx: &mut ModelContext<Self>) {
585 let task = self.fetch_extensions_with_update_available(cx);
586 cx.spawn(move |this, mut cx| async move {
587 Self::upgrade_extensions(this, task.await?, &mut cx).await
588 })
589 .detach();
590 }
591
592 async fn upgrade_extensions(
593 this: WeakModel<Self>,
594 extensions: Vec<ExtensionMetadata>,
595 cx: &mut AsyncAppContext,
596 ) -> Result<()> {
597 for extension in extensions {
598 let task = this.update(cx, |this, cx| {
599 if let Some(installed_extension) =
600 this.extension_index.extensions.get(&extension.id)
601 {
602 let installed_version =
603 SemanticVersion::from_str(&installed_extension.manifest.version).ok()?;
604 let latest_version =
605 SemanticVersion::from_str(&extension.manifest.version).ok()?;
606
607 if installed_version >= latest_version {
608 return None;
609 }
610 }
611
612 Some(this.upgrade_extension(extension.id, extension.manifest.version, cx))
613 })?;
614
615 if let Some(task) = task {
616 task.await.log_err();
617 }
618 }
619 anyhow::Ok(())
620 }
621
622 fn fetch_extensions_from_api(
623 &self,
624 path: &str,
625 query: &[(&str, &str)],
626 cx: &mut ModelContext<'_, ExtensionStore>,
627 ) -> Task<Result<Vec<ExtensionMetadata>>> {
628 let url = self.http_client.build_zed_api_url(path, query);
629 let http_client = self.http_client.clone();
630 cx.spawn(move |_, _| async move {
631 let mut response = http_client
632 .get(url?.as_ref(), AsyncBody::empty(), true)
633 .await?;
634
635 let mut body = Vec::new();
636 response
637 .body_mut()
638 .read_to_end(&mut body)
639 .await
640 .context("error reading extensions")?;
641
642 if response.status().is_client_error() {
643 let text = String::from_utf8_lossy(body.as_slice());
644 bail!(
645 "status error {}, response: {text:?}",
646 response.status().as_u16()
647 );
648 }
649
650 let response: GetExtensionsResponse = serde_json::from_slice(&body)?;
651 Ok(response.data)
652 })
653 }
654
655 pub fn install_extension(
656 &mut self,
657 extension_id: Arc<str>,
658 version: Arc<str>,
659 cx: &mut ModelContext<Self>,
660 ) {
661 self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Install, cx)
662 .detach_and_log_err(cx);
663 }
664
665 fn install_or_upgrade_extension_at_endpoint(
666 &mut self,
667 extension_id: Arc<str>,
668 url: Url,
669 operation: ExtensionOperation,
670 cx: &mut ModelContext<Self>,
671 ) -> Task<Result<()>> {
672 let extension_dir = self.installed_dir.join(extension_id.as_ref());
673 let http_client = self.http_client.clone();
674 let fs = self.fs.clone();
675
676 match self.outstanding_operations.entry(extension_id.clone()) {
677 btree_map::Entry::Occupied(_) => return Task::ready(Ok(())),
678 btree_map::Entry::Vacant(e) => e.insert(operation),
679 };
680 cx.notify();
681
682 cx.spawn(move |this, mut cx| async move {
683 let _finish = util::defer({
684 let this = this.clone();
685 let mut cx = cx.clone();
686 let extension_id = extension_id.clone();
687 move || {
688 this.update(&mut cx, |this, cx| {
689 this.outstanding_operations.remove(extension_id.as_ref());
690 cx.notify();
691 })
692 .ok();
693 }
694 });
695
696 let mut response = http_client
697 .get(url.as_ref(), Default::default(), true)
698 .await
699 .map_err(|err| anyhow!("error downloading extension: {}", err))?;
700
701 fs.remove_dir(
702 &extension_dir,
703 RemoveOptions {
704 recursive: true,
705 ignore_if_not_exists: true,
706 },
707 )
708 .await?;
709
710 let content_length = response
711 .headers()
712 .get(http_client::http::header::CONTENT_LENGTH)
713 .and_then(|value| value.to_str().ok()?.parse::<usize>().ok());
714
715 let mut body = BufReader::new(response.body_mut());
716 let mut tar_gz_bytes = Vec::new();
717 body.read_to_end(&mut tar_gz_bytes).await?;
718
719 if let Some(content_length) = content_length {
720 let actual_len = tar_gz_bytes.len();
721 if content_length != actual_len {
722 bail!("downloaded extension size {actual_len} does not match content length {content_length}");
723 }
724 }
725 let decompressed_bytes = GzipDecoder::new(BufReader::new(tar_gz_bytes.as_slice()));
726 let archive = Archive::new(decompressed_bytes);
727 archive.unpack(extension_dir).await?;
728 this.update(&mut cx, |this, cx| {
729 this.reload(Some(extension_id.clone()), cx)
730 })?
731 .await;
732
733 if let ExtensionOperation::Install = operation {
734 this.update(&mut cx, |_, cx| {
735 cx.emit(Event::ExtensionInstalled(extension_id));
736 })
737 .ok();
738 }
739
740 anyhow::Ok(())
741 })
742 }
743
744 pub fn install_latest_extension(
745 &mut self,
746 extension_id: Arc<str>,
747 cx: &mut ModelContext<Self>,
748 ) {
749 log::info!("installing extension {extension_id} latest version");
750
751 let schema_versions = schema_version_range();
752 let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
753
754 let Some(url) = self
755 .http_client
756 .build_zed_api_url(
757 &format!("/extensions/{extension_id}/download"),
758 &[
759 ("min_schema_version", &schema_versions.start().to_string()),
760 ("max_schema_version", &schema_versions.end().to_string()),
761 (
762 "min_wasm_api_version",
763 &wasm_api_versions.start().to_string(),
764 ),
765 ("max_wasm_api_version", &wasm_api_versions.end().to_string()),
766 ],
767 )
768 .log_err()
769 else {
770 return;
771 };
772
773 self.install_or_upgrade_extension_at_endpoint(
774 extension_id,
775 url,
776 ExtensionOperation::Install,
777 cx,
778 )
779 .detach_and_log_err(cx);
780 }
781
782 pub fn upgrade_extension(
783 &mut self,
784 extension_id: Arc<str>,
785 version: Arc<str>,
786 cx: &mut ModelContext<Self>,
787 ) -> Task<Result<()>> {
788 self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Upgrade, cx)
789 }
790
791 fn install_or_upgrade_extension(
792 &mut self,
793 extension_id: Arc<str>,
794 version: Arc<str>,
795 operation: ExtensionOperation,
796 cx: &mut ModelContext<Self>,
797 ) -> Task<Result<()>> {
798 log::info!("installing extension {extension_id} {version}");
799 let Some(url) = self
800 .http_client
801 .build_zed_api_url(
802 &format!("/extensions/{extension_id}/{version}/download"),
803 &[],
804 )
805 .log_err()
806 else {
807 return Task::ready(Ok(()));
808 };
809
810 self.install_or_upgrade_extension_at_endpoint(extension_id, url, operation, cx)
811 }
812
813 pub fn uninstall_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
814 let extension_dir = self.installed_dir.join(extension_id.as_ref());
815 let work_dir = self.wasm_host.work_dir.join(extension_id.as_ref());
816 let fs = self.fs.clone();
817
818 match self.outstanding_operations.entry(extension_id.clone()) {
819 btree_map::Entry::Occupied(_) => return,
820 btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
821 };
822
823 cx.spawn(move |this, mut cx| async move {
824 let _finish = util::defer({
825 let this = this.clone();
826 let mut cx = cx.clone();
827 let extension_id = extension_id.clone();
828 move || {
829 this.update(&mut cx, |this, cx| {
830 this.outstanding_operations.remove(extension_id.as_ref());
831 cx.notify();
832 })
833 .ok();
834 }
835 });
836
837 fs.remove_dir(
838 &work_dir,
839 RemoveOptions {
840 recursive: true,
841 ignore_if_not_exists: true,
842 },
843 )
844 .await?;
845
846 fs.remove_dir(
847 &extension_dir,
848 RemoveOptions {
849 recursive: true,
850 ignore_if_not_exists: true,
851 },
852 )
853 .await?;
854
855 this.update(&mut cx, |this, cx| this.reload(None, cx))?
856 .await;
857 anyhow::Ok(())
858 })
859 .detach_and_log_err(cx)
860 }
861
862 pub fn install_dev_extension(
863 &mut self,
864 extension_source_path: PathBuf,
865 cx: &mut ModelContext<Self>,
866 ) -> Task<Result<()>> {
867 let extensions_dir = self.extensions_dir();
868 let fs = self.fs.clone();
869 let builder = self.builder.clone();
870
871 cx.spawn(move |this, mut cx| async move {
872 let mut extension_manifest =
873 ExtensionManifest::load(fs.clone(), &extension_source_path).await?;
874 let extension_id = extension_manifest.id.clone();
875
876 if !this.update(&mut cx, |this, cx| {
877 match this.outstanding_operations.entry(extension_id.clone()) {
878 btree_map::Entry::Occupied(_) => return false,
879 btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
880 };
881 cx.notify();
882 true
883 })? {
884 return Ok(());
885 }
886
887 let _finish = util::defer({
888 let this = this.clone();
889 let mut cx = cx.clone();
890 let extension_id = extension_id.clone();
891 move || {
892 this.update(&mut cx, |this, cx| {
893 this.outstanding_operations.remove(extension_id.as_ref());
894 cx.notify();
895 })
896 .ok();
897 }
898 });
899
900 cx.background_executor()
901 .spawn({
902 let extension_source_path = extension_source_path.clone();
903 async move {
904 builder
905 .compile_extension(
906 &extension_source_path,
907 &mut extension_manifest,
908 CompileExtensionOptions { release: false },
909 )
910 .await
911 }
912 })
913 .await?;
914
915 let output_path = &extensions_dir.join(extension_id.as_ref());
916 if let Some(metadata) = fs.metadata(output_path).await? {
917 if metadata.is_symlink {
918 fs.remove_file(
919 output_path,
920 RemoveOptions {
921 recursive: false,
922 ignore_if_not_exists: true,
923 },
924 )
925 .await?;
926 } else {
927 bail!("extension {extension_id} is already installed");
928 }
929 }
930
931 fs.create_symlink(output_path, extension_source_path)
932 .await?;
933
934 this.update(&mut cx, |this, cx| this.reload(None, cx))?
935 .await;
936 Ok(())
937 })
938 }
939
940 pub fn rebuild_dev_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
941 let path = self.installed_dir.join(extension_id.as_ref());
942 let builder = self.builder.clone();
943 let fs = self.fs.clone();
944
945 match self.outstanding_operations.entry(extension_id.clone()) {
946 btree_map::Entry::Occupied(_) => return,
947 btree_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Upgrade),
948 };
949
950 cx.notify();
951 let compile = cx.background_executor().spawn(async move {
952 let mut manifest = ExtensionManifest::load(fs, &path).await?;
953 builder
954 .compile_extension(
955 &path,
956 &mut manifest,
957 CompileExtensionOptions { release: true },
958 )
959 .await
960 });
961
962 cx.spawn(|this, mut cx| async move {
963 let result = compile.await;
964
965 this.update(&mut cx, |this, cx| {
966 this.outstanding_operations.remove(&extension_id);
967 cx.notify();
968 })?;
969
970 if result.is_ok() {
971 this.update(&mut cx, |this, cx| this.reload(Some(extension_id), cx))?
972 .await;
973 }
974
975 result
976 })
977 .detach_and_log_err(cx)
978 }
979
980 /// Updates the set of installed extensions.
981 ///
982 /// First, this unloads any themes, languages, or grammars that are
983 /// no longer in the manifest, or whose files have changed on disk.
984 /// Then it loads any themes, languages, or grammars that are newly
985 /// added to the manifest, or whose files have changed on disk.
986 fn extensions_updated(
987 &mut self,
988 new_index: ExtensionIndex,
989 cx: &mut ModelContext<Self>,
990 ) -> Task<()> {
991 let old_index = &self.extension_index;
992
993 // Determine which extensions need to be loaded and unloaded, based
994 // on the changes to the manifest and the extensions that we know have been
995 // modified.
996 let mut extensions_to_unload = Vec::default();
997 let mut extensions_to_load = Vec::default();
998 {
999 let mut old_keys = old_index.extensions.iter().peekable();
1000 let mut new_keys = new_index.extensions.iter().peekable();
1001 loop {
1002 match (old_keys.peek(), new_keys.peek()) {
1003 (None, None) => break,
1004 (None, Some(_)) => {
1005 extensions_to_load.push(new_keys.next().unwrap().0.clone());
1006 }
1007 (Some(_), None) => {
1008 extensions_to_unload.push(old_keys.next().unwrap().0.clone());
1009 }
1010 (Some((old_key, _)), Some((new_key, _))) => match old_key.cmp(new_key) {
1011 Ordering::Equal => {
1012 let (old_key, old_value) = old_keys.next().unwrap();
1013 let (new_key, new_value) = new_keys.next().unwrap();
1014 if old_value != new_value || self.modified_extensions.contains(old_key)
1015 {
1016 extensions_to_unload.push(old_key.clone());
1017 extensions_to_load.push(new_key.clone());
1018 }
1019 }
1020 Ordering::Less => {
1021 extensions_to_unload.push(old_keys.next().unwrap().0.clone());
1022 }
1023 Ordering::Greater => {
1024 extensions_to_load.push(new_keys.next().unwrap().0.clone());
1025 }
1026 },
1027 }
1028 }
1029 self.modified_extensions.clear();
1030 }
1031
1032 if extensions_to_load.is_empty() && extensions_to_unload.is_empty() {
1033 return Task::ready(());
1034 }
1035
1036 let reload_count = extensions_to_unload
1037 .iter()
1038 .filter(|id| extensions_to_load.contains(id))
1039 .count();
1040
1041 log::info!(
1042 "extensions updated. loading {}, reloading {}, unloading {}",
1043 extensions_to_load.len() - reload_count,
1044 reload_count,
1045 extensions_to_unload.len() - reload_count
1046 );
1047
1048 if let Some(telemetry) = &self.telemetry {
1049 for extension_id in &extensions_to_load {
1050 if let Some(extension) = new_index.extensions.get(extension_id) {
1051 telemetry.report_extension_event(
1052 extension_id.clone(),
1053 extension.manifest.version.clone(),
1054 );
1055 }
1056 }
1057 }
1058
1059 let themes_to_remove = old_index
1060 .themes
1061 .iter()
1062 .filter_map(|(name, entry)| {
1063 if extensions_to_unload.contains(&entry.extension) {
1064 Some(name.clone().into())
1065 } else {
1066 None
1067 }
1068 })
1069 .collect::<Vec<_>>();
1070 let languages_to_remove = old_index
1071 .languages
1072 .iter()
1073 .filter_map(|(name, entry)| {
1074 if extensions_to_unload.contains(&entry.extension) {
1075 Some(name.clone())
1076 } else {
1077 None
1078 }
1079 })
1080 .collect::<Vec<_>>();
1081 let mut grammars_to_remove = Vec::new();
1082 for extension_id in &extensions_to_unload {
1083 let Some(extension) = old_index.extensions.get(extension_id) else {
1084 continue;
1085 };
1086 grammars_to_remove.extend(extension.manifest.grammars.keys().cloned());
1087 for (language_server_name, config) in extension.manifest.language_servers.iter() {
1088 for language in config.languages() {
1089 self.registration_hooks
1090 .remove_lsp_adapter(&language, language_server_name);
1091 }
1092 }
1093 }
1094
1095 self.wasm_extensions
1096 .retain(|(extension, _)| !extensions_to_unload.contains(&extension.id));
1097 self.registration_hooks.remove_user_themes(themes_to_remove);
1098 self.registration_hooks
1099 .remove_languages(&languages_to_remove, &grammars_to_remove);
1100
1101 let languages_to_add = new_index
1102 .languages
1103 .iter()
1104 .filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
1105 .collect::<Vec<_>>();
1106 let mut grammars_to_add = Vec::new();
1107 let mut themes_to_add = Vec::new();
1108 let mut snippets_to_add = Vec::new();
1109 for extension_id in &extensions_to_load {
1110 let Some(extension) = new_index.extensions.get(extension_id) else {
1111 continue;
1112 };
1113
1114 grammars_to_add.extend(extension.manifest.grammars.keys().map(|grammar_name| {
1115 let mut grammar_path = self.installed_dir.clone();
1116 grammar_path.extend([extension_id.as_ref(), "grammars"]);
1117 grammar_path.push(grammar_name.as_ref());
1118 grammar_path.set_extension("wasm");
1119 (grammar_name.clone(), grammar_path)
1120 }));
1121 themes_to_add.extend(extension.manifest.themes.iter().map(|theme_path| {
1122 let mut path = self.installed_dir.clone();
1123 path.extend([Path::new(extension_id.as_ref()), theme_path.as_path()]);
1124 path
1125 }));
1126 snippets_to_add.extend(extension.manifest.snippets.iter().map(|snippets_path| {
1127 let mut path = self.installed_dir.clone();
1128 path.extend([Path::new(extension_id.as_ref()), snippets_path.as_path()]);
1129 path
1130 }));
1131 }
1132
1133 self.registration_hooks
1134 .register_wasm_grammars(grammars_to_add);
1135
1136 for (language_name, language) in languages_to_add {
1137 let mut language_path = self.installed_dir.clone();
1138 language_path.extend([
1139 Path::new(language.extension.as_ref()),
1140 language.path.as_path(),
1141 ]);
1142 self.registration_hooks.register_language(
1143 language_name.clone(),
1144 language.grammar.clone(),
1145 language.matcher.clone(),
1146 Arc::new(move || {
1147 let config = std::fs::read_to_string(language_path.join("config.toml"))?;
1148 let config: LanguageConfig = ::toml::from_str(&config)?;
1149 let queries = load_plugin_queries(&language_path);
1150 let context_provider =
1151 std::fs::read_to_string(language_path.join("tasks.json"))
1152 .ok()
1153 .and_then(|contents| {
1154 let definitions =
1155 serde_json_lenient::from_str(&contents).log_err()?;
1156 Some(Arc::new(ContextProviderWithTasks::new(definitions)) as Arc<_>)
1157 });
1158
1159 Ok(LoadedLanguage {
1160 config,
1161 queries,
1162 context_provider,
1163 toolchain_provider: None,
1164 })
1165 }),
1166 );
1167 }
1168
1169 let fs = self.fs.clone();
1170 let wasm_host = self.wasm_host.clone();
1171 let root_dir = self.installed_dir.clone();
1172 let api = self.registration_hooks.clone();
1173 let extension_entries = extensions_to_load
1174 .iter()
1175 .filter_map(|name| new_index.extensions.get(name).cloned())
1176 .collect::<Vec<_>>();
1177
1178 self.extension_index = new_index;
1179 cx.notify();
1180 cx.emit(Event::ExtensionsUpdated);
1181
1182 cx.spawn(|this, mut cx| async move {
1183 cx.background_executor()
1184 .spawn({
1185 let fs = fs.clone();
1186 async move {
1187 for theme_path in themes_to_add.into_iter() {
1188 api.load_user_theme(theme_path, fs.clone()).await.log_err();
1189 }
1190
1191 for snippets_path in &snippets_to_add {
1192 if let Some(snippets_contents) = fs.load(snippets_path).await.log_err()
1193 {
1194 api.register_snippets(snippets_path, &snippets_contents)
1195 .log_err();
1196 }
1197 }
1198 }
1199 })
1200 .await;
1201
1202 let mut wasm_extensions = Vec::new();
1203 for extension in extension_entries {
1204 if extension.manifest.lib.kind.is_none() {
1205 continue;
1206 };
1207
1208 let extension_path = root_dir.join(extension.manifest.id.as_ref());
1209 let wasm_extension = WasmExtension::load(
1210 extension_path,
1211 &extension.manifest,
1212 wasm_host.clone(),
1213 &cx,
1214 )
1215 .await;
1216
1217 if let Some(wasm_extension) = wasm_extension.log_err() {
1218 wasm_extensions.push((extension.manifest.clone(), wasm_extension));
1219 } else {
1220 this.update(&mut cx, |_, cx| {
1221 cx.emit(Event::ExtensionFailedToLoad(extension.manifest.id.clone()))
1222 })
1223 .ok();
1224 }
1225 }
1226
1227 this.update(&mut cx, |this, cx| {
1228 this.reload_complete_senders.clear();
1229
1230 for (manifest, wasm_extension) in &wasm_extensions {
1231 let extension = Arc::new(wasm_extension.clone());
1232
1233 for (language_server_id, language_server_config) in &manifest.language_servers {
1234 for language in language_server_config.languages() {
1235 this.registration_hooks.register_lsp_adapter(
1236 language.clone(),
1237 ExtensionLspAdapter {
1238 extension: wasm_extension.clone(),
1239 host: this.wasm_host.clone(),
1240 language_server_id: language_server_id.clone(),
1241 config: wit::LanguageServerConfig {
1242 name: language_server_id.0.to_string(),
1243 language_name: language.to_string(),
1244 },
1245 },
1246 );
1247 }
1248 }
1249
1250 for (slash_command_name, slash_command) in &manifest.slash_commands {
1251 this.registration_hooks.register_slash_command(
1252 extension.clone(),
1253 extension::SlashCommand {
1254 name: slash_command_name.to_string(),
1255 description: slash_command.description.to_string(),
1256 // We don't currently expose this as a configurable option, as it currently drives
1257 // the `menu_text` on the `SlashCommand` trait, which is not used for slash commands
1258 // defined in extensions, as they are not able to be added to the menu.
1259 tooltip_text: String::new(),
1260 requires_argument: slash_command.requires_argument,
1261 },
1262 );
1263 }
1264
1265 for (id, _context_server_entry) in &manifest.context_servers {
1266 this.registration_hooks.register_context_server(
1267 id.clone(),
1268 wasm_extension.clone(),
1269 cx,
1270 );
1271 }
1272
1273 for (provider_id, _provider) in &manifest.indexed_docs_providers {
1274 this.registration_hooks
1275 .register_docs_provider(extension.clone(), provider_id.clone());
1276 }
1277 }
1278
1279 this.wasm_extensions.extend(wasm_extensions);
1280 this.registration_hooks.reload_current_theme(cx);
1281 })
1282 .ok();
1283 })
1284 }
1285
1286 fn rebuild_extension_index(&self, cx: &mut ModelContext<Self>) -> Task<ExtensionIndex> {
1287 let fs = self.fs.clone();
1288 let work_dir = self.wasm_host.work_dir.clone();
1289 let extensions_dir = self.installed_dir.clone();
1290 let index_path = self.index_path.clone();
1291 let extension_api = self.registration_hooks.clone();
1292 cx.background_executor().spawn(async move {
1293 let start_time = Instant::now();
1294 let mut index = ExtensionIndex::default();
1295
1296 fs.create_dir(&work_dir).await.log_err();
1297 fs.create_dir(&extensions_dir).await.log_err();
1298
1299 let extension_paths = fs.read_dir(&extensions_dir).await;
1300 if let Ok(mut extension_paths) = extension_paths {
1301 while let Some(extension_dir) = extension_paths.next().await {
1302 let Ok(extension_dir) = extension_dir else {
1303 continue;
1304 };
1305
1306 if extension_dir
1307 .file_name()
1308 .map_or(false, |file_name| file_name == ".DS_Store")
1309 {
1310 continue;
1311 }
1312
1313 Self::add_extension_to_index(
1314 fs.clone(),
1315 extension_dir,
1316 &mut index,
1317 extension_api.clone(),
1318 )
1319 .await
1320 .log_err();
1321 }
1322 }
1323
1324 if let Ok(index_json) = serde_json::to_string_pretty(&index) {
1325 fs.save(&index_path, &index_json.as_str().into(), Default::default())
1326 .await
1327 .context("failed to save extension index")
1328 .log_err();
1329 }
1330
1331 log::info!("rebuilt extension index in {:?}", start_time.elapsed());
1332 index
1333 })
1334 }
1335
1336 async fn add_extension_to_index(
1337 fs: Arc<dyn Fs>,
1338 extension_dir: PathBuf,
1339 index: &mut ExtensionIndex,
1340 extension_api: Arc<dyn ExtensionRegistrationHooks>,
1341 ) -> Result<()> {
1342 let mut extension_manifest = ExtensionManifest::load(fs.clone(), &extension_dir).await?;
1343 let extension_id = extension_manifest.id.clone();
1344
1345 // TODO: distinguish dev extensions more explicitly, by the absence
1346 // of a checksum file that we'll create when downloading normal extensions.
1347 let is_dev = fs
1348 .metadata(&extension_dir)
1349 .await?
1350 .ok_or_else(|| anyhow!("directory does not exist"))?
1351 .is_symlink;
1352
1353 if let Ok(mut language_paths) = fs.read_dir(&extension_dir.join("languages")).await {
1354 while let Some(language_path) = language_paths.next().await {
1355 let language_path = language_path?;
1356 let Ok(relative_path) = language_path.strip_prefix(&extension_dir) else {
1357 continue;
1358 };
1359 let Ok(Some(fs_metadata)) = fs.metadata(&language_path).await else {
1360 continue;
1361 };
1362 if !fs_metadata.is_dir {
1363 continue;
1364 }
1365 let config = fs.load(&language_path.join("config.toml")).await?;
1366 let config = ::toml::from_str::<LanguageConfig>(&config)?;
1367
1368 let relative_path = relative_path.to_path_buf();
1369 if !extension_manifest.languages.contains(&relative_path) {
1370 extension_manifest.languages.push(relative_path.clone());
1371 }
1372
1373 index.languages.insert(
1374 config.name.clone(),
1375 ExtensionIndexLanguageEntry {
1376 extension: extension_id.clone(),
1377 path: relative_path,
1378 matcher: config.matcher,
1379 grammar: config.grammar,
1380 },
1381 );
1382 }
1383 }
1384
1385 if let Ok(mut theme_paths) = fs.read_dir(&extension_dir.join("themes")).await {
1386 while let Some(theme_path) = theme_paths.next().await {
1387 let theme_path = theme_path?;
1388 let Ok(relative_path) = theme_path.strip_prefix(&extension_dir) else {
1389 continue;
1390 };
1391
1392 let Some(theme_families) = extension_api
1393 .list_theme_names(theme_path.clone(), fs.clone())
1394 .await
1395 .log_err()
1396 else {
1397 continue;
1398 };
1399
1400 let relative_path = relative_path.to_path_buf();
1401 if !extension_manifest.themes.contains(&relative_path) {
1402 extension_manifest.themes.push(relative_path.clone());
1403 }
1404
1405 for theme_name in theme_families {
1406 index.themes.insert(
1407 theme_name.into(),
1408 ExtensionIndexThemeEntry {
1409 extension: extension_id.clone(),
1410 path: relative_path.clone(),
1411 },
1412 );
1413 }
1414 }
1415 }
1416
1417 let extension_wasm_path = extension_dir.join("extension.wasm");
1418 if fs.is_file(&extension_wasm_path).await {
1419 extension_manifest
1420 .lib
1421 .kind
1422 .get_or_insert(ExtensionLibraryKind::Rust);
1423 }
1424
1425 index.extensions.insert(
1426 extension_id.clone(),
1427 ExtensionIndexEntry {
1428 dev: is_dev,
1429 manifest: Arc::new(extension_manifest),
1430 },
1431 );
1432
1433 Ok(())
1434 }
1435}
1436
1437fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
1438 let mut result = LanguageQueries::default();
1439 if let Some(entries) = std::fs::read_dir(root_path).log_err() {
1440 for entry in entries {
1441 let Some(entry) = entry.log_err() else {
1442 continue;
1443 };
1444 let path = entry.path();
1445 if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
1446 if !remainder.ends_with(".scm") {
1447 continue;
1448 }
1449 for (name, query) in QUERY_FILENAME_PREFIXES {
1450 if remainder.starts_with(name) {
1451 if let Some(contents) = std::fs::read_to_string(&path).log_err() {
1452 match query(&mut result) {
1453 None => *query(&mut result) = Some(contents.into()),
1454 Some(r) => r.to_mut().push_str(contents.as_ref()),
1455 }
1456 }
1457 break;
1458 }
1459 }
1460 }
1461 }
1462 }
1463 result
1464}