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