1pub mod extension_builder;
2mod extension_lsp_adapter;
3mod extension_manifest;
4mod wasm_host;
5
6#[cfg(test)]
7mod extension_store_test;
8
9use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit};
10use anyhow::{anyhow, bail, Context as _, Result};
11use async_compression::futures::bufread::GzipDecoder;
12use async_tar::Archive;
13use client::{telemetry::Telemetry, Client, ExtensionMetadata, GetExtensionsResponse};
14use collections::{hash_map, BTreeMap, HashMap, HashSet};
15use extension_builder::{CompileExtensionOptions, ExtensionBuilder};
16use fs::{Fs, RemoveOptions};
17use futures::{
18 channel::{
19 mpsc::{unbounded, UnboundedSender},
20 oneshot,
21 },
22 io::BufReader,
23 select_biased, AsyncReadExt as _, Future, FutureExt as _, StreamExt as _,
24};
25use gpui::{actions, AppContext, Context, EventEmitter, Global, Model, ModelContext, Task};
26use language::{
27 ContextProviderWithTasks, LanguageConfig, LanguageMatcher, LanguageQueries, LanguageRegistry,
28 QUERY_FILENAME_PREFIXES,
29};
30use node_runtime::NodeRuntime;
31use serde::{Deserialize, Serialize};
32use std::{
33 cmp::Ordering,
34 path::{self, Path, PathBuf},
35 sync::Arc,
36 time::{Duration, Instant},
37};
38use theme::{ThemeRegistry, ThemeSettings};
39use url::Url;
40use util::{
41 http::{AsyncBody, HttpClient, HttpClientWithUrl},
42 maybe,
43 paths::EXTENSIONS_DIR,
44 ResultExt,
45};
46use wasm_host::{WasmExtension, WasmHost};
47
48pub use extension_manifest::{
49 ExtensionLibraryKind, ExtensionManifest, GrammarManifestEntry, OldExtensionManifest,
50};
51
52const RELOAD_DEBOUNCE_DURATION: Duration = Duration::from_millis(200);
53const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
54
55const CURRENT_SCHEMA_VERSION: i64 = 1;
56
57pub struct ExtensionStore {
58 builder: Arc<ExtensionBuilder>,
59 extension_index: ExtensionIndex,
60 fs: Arc<dyn Fs>,
61 http_client: Arc<HttpClientWithUrl>,
62 telemetry: Option<Arc<Telemetry>>,
63 reload_tx: UnboundedSender<Option<Arc<str>>>,
64 reload_complete_senders: Vec<oneshot::Sender<()>>,
65 installed_dir: PathBuf,
66 outstanding_operations: HashMap<Arc<str>, ExtensionOperation>,
67 index_path: PathBuf,
68 language_registry: Arc<LanguageRegistry>,
69 theme_registry: Arc<ThemeRegistry>,
70 modified_extensions: HashSet<Arc<str>>,
71 wasm_host: Arc<WasmHost>,
72 wasm_extensions: Vec<(Arc<ExtensionManifest>, WasmExtension)>,
73 tasks: Vec<Task<()>>,
74}
75
76#[derive(Clone)]
77pub enum ExtensionStatus {
78 NotInstalled,
79 Installing,
80 Upgrading,
81 Installed(Arc<str>),
82 Removing,
83}
84
85#[derive(Clone, Copy)]
86enum ExtensionOperation {
87 Upgrade,
88 Install,
89 Remove,
90}
91
92#[derive(Clone)]
93pub enum Event {
94 ExtensionsUpdated,
95 StartedReloading,
96 ExtensionInstalled(Arc<str>),
97 ExtensionFailedToLoad(Arc<str>),
98}
99
100impl EventEmitter<Event> for ExtensionStore {}
101
102struct GlobalExtensionStore(Model<ExtensionStore>);
103
104impl Global for GlobalExtensionStore {}
105
106#[derive(Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
107pub struct ExtensionIndex {
108 pub extensions: BTreeMap<Arc<str>, ExtensionIndexEntry>,
109 pub themes: BTreeMap<Arc<str>, ExtensionIndexThemeEntry>,
110 pub languages: BTreeMap<Arc<str>, ExtensionIndexLanguageEntry>,
111}
112
113#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
114pub struct ExtensionIndexEntry {
115 manifest: Arc<ExtensionManifest>,
116 dev: bool,
117}
118
119#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
120pub struct ExtensionIndexThemeEntry {
121 extension: Arc<str>,
122 path: PathBuf,
123}
124
125#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
126pub struct ExtensionIndexLanguageEntry {
127 extension: Arc<str>,
128 path: PathBuf,
129 matcher: LanguageMatcher,
130 grammar: Option<Arc<str>>,
131}
132
133actions!(zed, [ReloadExtensions]);
134
135pub fn init(
136 fs: Arc<fs::RealFs>,
137 client: Arc<Client>,
138 node_runtime: Arc<dyn NodeRuntime>,
139 language_registry: Arc<LanguageRegistry>,
140 theme_registry: Arc<ThemeRegistry>,
141 cx: &mut AppContext,
142) {
143 let store = cx.new_model(move |cx| {
144 ExtensionStore::new(
145 EXTENSIONS_DIR.clone(),
146 None,
147 fs,
148 client.http_client().clone(),
149 Some(client.telemetry().clone()),
150 node_runtime,
151 language_registry,
152 theme_registry,
153 cx,
154 )
155 });
156
157 cx.on_action(|_: &ReloadExtensions, cx| {
158 let store = cx.global::<GlobalExtensionStore>().0.clone();
159 store.update(cx, |store, cx| drop(store.reload(None, cx)));
160 });
161
162 cx.set_global(GlobalExtensionStore(store));
163}
164
165impl ExtensionStore {
166 pub fn global(cx: &AppContext) -> Model<Self> {
167 cx.global::<GlobalExtensionStore>().0.clone()
168 }
169
170 #[allow(clippy::too_many_arguments)]
171 pub fn new(
172 extensions_dir: PathBuf,
173 build_dir: Option<PathBuf>,
174 fs: Arc<dyn Fs>,
175 http_client: Arc<HttpClientWithUrl>,
176 telemetry: Option<Arc<Telemetry>>,
177 node_runtime: Arc<dyn NodeRuntime>,
178 language_registry: Arc<LanguageRegistry>,
179 theme_registry: Arc<ThemeRegistry>,
180 cx: &mut ModelContext<Self>,
181 ) -> Self {
182 let work_dir = extensions_dir.join("work");
183 let build_dir = build_dir.unwrap_or_else(|| extensions_dir.join("build"));
184 let installed_dir = extensions_dir.join("installed");
185 let index_path = extensions_dir.join("index.json");
186
187 let (reload_tx, mut reload_rx) = unbounded();
188 let mut this = Self {
189 extension_index: Default::default(),
190 installed_dir,
191 index_path,
192 builder: Arc::new(ExtensionBuilder::new(build_dir)),
193 outstanding_operations: Default::default(),
194 modified_extensions: Default::default(),
195 reload_complete_senders: Vec::new(),
196 wasm_host: WasmHost::new(
197 fs.clone(),
198 http_client.clone(),
199 node_runtime,
200 language_registry.clone(),
201 work_dir,
202 ),
203 wasm_extensions: Vec::new(),
204 fs,
205 http_client,
206 telemetry,
207 language_registry,
208 theme_registry,
209 reload_tx,
210 tasks: Vec::new(),
211 };
212
213 // The extensions store maintains an index file, which contains a complete
214 // list of the installed extensions and the resources that they provide.
215 // This index is loaded synchronously on startup.
216 let (index_content, index_metadata, extensions_metadata) =
217 cx.background_executor().block(async {
218 futures::join!(
219 this.fs.load(&this.index_path),
220 this.fs.metadata(&this.index_path),
221 this.fs.metadata(&this.installed_dir),
222 )
223 });
224
225 // Normally, there is no need to rebuild the index. But if the index file
226 // is invalid or is out-of-date according to the filesystem mtimes, then
227 // it must be asynchronously rebuilt.
228 let mut extension_index = ExtensionIndex::default();
229 let mut extension_index_needs_rebuild = true;
230 if let Some(index_content) = index_content.ok() {
231 if let Some(index) = serde_json::from_str(&index_content).log_err() {
232 extension_index = index;
233 if let (Ok(Some(index_metadata)), Ok(Some(extensions_metadata))) =
234 (index_metadata, extensions_metadata)
235 {
236 if index_metadata.mtime > extensions_metadata.mtime {
237 extension_index_needs_rebuild = false;
238 }
239 }
240 }
241 }
242
243 // Immediately load all of the extensions in the initial manifest. If the
244 // index needs to be rebuild, then enqueue
245 let load_initial_extensions = this.extensions_updated(extension_index, cx);
246 if extension_index_needs_rebuild {
247 let _ = this.reload(None, cx);
248 }
249
250 // Perform all extension loading in a single task to ensure that we
251 // never attempt to simultaneously load/unload extensions from multiple
252 // parallel tasks.
253 this.tasks.push(cx.spawn(|this, mut cx| {
254 async move {
255 load_initial_extensions.await;
256
257 let mut debounce_timer = cx
258 .background_executor()
259 .spawn(futures::future::pending())
260 .fuse();
261 loop {
262 select_biased! {
263 _ = debounce_timer => {
264 let index = this
265 .update(&mut cx, |this, cx| this.rebuild_extension_index(cx))?
266 .await;
267 this.update(&mut cx, |this, cx| this.extensions_updated(index, cx))?
268 .await;
269 }
270 extension_id = reload_rx.next() => {
271 let Some(extension_id) = extension_id else { break; };
272 this.update(&mut cx, |this, _| {
273 this.modified_extensions.extend(extension_id);
274 })?;
275 debounce_timer = cx
276 .background_executor()
277 .timer(RELOAD_DEBOUNCE_DURATION)
278 .fuse();
279 }
280 }
281 }
282
283 anyhow::Ok(())
284 }
285 .map(drop)
286 }));
287
288 // Watch the installed extensions directory for changes. Whenever changes are
289 // detected, rebuild the extension index, and load/unload any extensions that
290 // have been added, removed, or modified.
291 this.tasks.push(cx.background_executor().spawn({
292 let fs = this.fs.clone();
293 let reload_tx = this.reload_tx.clone();
294 let installed_dir = this.installed_dir.clone();
295 async move {
296 let mut paths = fs.watch(&installed_dir, FS_WATCH_LATENCY).await;
297 while let Some(paths) = paths.next().await {
298 for path in paths {
299 let Ok(event_path) = path.strip_prefix(&installed_dir) else {
300 continue;
301 };
302
303 if let Some(path::Component::Normal(extension_dir_name)) =
304 event_path.components().next()
305 {
306 if let Some(extension_id) = extension_dir_name.to_str() {
307 reload_tx.unbounded_send(Some(extension_id.into())).ok();
308 }
309 }
310 }
311 }
312 }
313 }));
314
315 this
316 }
317
318 fn reload(
319 &mut self,
320 modified_extension: Option<Arc<str>>,
321 cx: &mut ModelContext<Self>,
322 ) -> impl Future<Output = ()> {
323 let (tx, rx) = oneshot::channel();
324 self.reload_complete_senders.push(tx);
325 self.reload_tx
326 .unbounded_send(modified_extension)
327 .expect("reload task exited");
328 cx.emit(Event::StartedReloading);
329
330 async move {
331 rx.await.ok();
332 }
333 }
334
335 fn extensions_dir(&self) -> PathBuf {
336 self.installed_dir.clone()
337 }
338
339 pub fn extension_status(&self, extension_id: &str) -> ExtensionStatus {
340 match self.outstanding_operations.get(extension_id) {
341 Some(ExtensionOperation::Install) => ExtensionStatus::Installing,
342 Some(ExtensionOperation::Remove) => ExtensionStatus::Removing,
343 Some(ExtensionOperation::Upgrade) => ExtensionStatus::Upgrading,
344 None => match self.extension_index.extensions.get(extension_id) {
345 Some(extension) => ExtensionStatus::Installed(extension.manifest.version.clone()),
346 None => ExtensionStatus::NotInstalled,
347 },
348 }
349 }
350
351 pub fn dev_extensions(&self) -> impl Iterator<Item = &Arc<ExtensionManifest>> {
352 self.extension_index
353 .extensions
354 .values()
355 .filter_map(|extension| extension.dev.then_some(&extension.manifest))
356 }
357
358 /// Returns the names of themes provided by extensions.
359 pub fn extension_themes<'a>(
360 &'a self,
361 extension_id: &'a str,
362 ) -> impl Iterator<Item = &'a Arc<str>> {
363 self.extension_index
364 .themes
365 .iter()
366 .filter_map(|(name, theme)| theme.extension.as_ref().eq(extension_id).then_some(name))
367 }
368
369 pub fn fetch_extensions(
370 &self,
371 search: Option<&str>,
372 cx: &mut ModelContext<Self>,
373 ) -> Task<Result<Vec<ExtensionMetadata>>> {
374 let version = CURRENT_SCHEMA_VERSION.to_string();
375 let mut query = vec![("max_schema_version", version.as_str())];
376 if let Some(search) = search {
377 query.push(("filter", search));
378 }
379
380 let url = self.http_client.build_zed_api_url("/extensions", &query);
381 let http_client = self.http_client.clone();
382 cx.spawn(move |_, _| async move {
383 let mut response = http_client
384 .get(&url?.as_ref(), AsyncBody::empty(), true)
385 .await?;
386
387 let mut body = Vec::new();
388 response
389 .body_mut()
390 .read_to_end(&mut body)
391 .await
392 .context("error reading extensions")?;
393
394 if response.status().is_client_error() {
395 let text = String::from_utf8_lossy(body.as_slice());
396 bail!(
397 "status error {}, response: {text:?}",
398 response.status().as_u16()
399 );
400 }
401
402 let response: GetExtensionsResponse = serde_json::from_slice(&body)?;
403 Ok(response.data)
404 })
405 }
406
407 pub fn install_extension(
408 &mut self,
409 extension_id: Arc<str>,
410 version: Arc<str>,
411 cx: &mut ModelContext<Self>,
412 ) {
413 self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Install, cx)
414 }
415
416 fn install_or_upgrade_extension_at_endpoint(
417 &mut self,
418 extension_id: Arc<str>,
419 url: Url,
420 operation: ExtensionOperation,
421 cx: &mut ModelContext<Self>,
422 ) {
423 let extension_dir = self.installed_dir.join(extension_id.as_ref());
424 let http_client = self.http_client.clone();
425 let fs = self.fs.clone();
426
427 match self.outstanding_operations.entry(extension_id.clone()) {
428 hash_map::Entry::Occupied(_) => return,
429 hash_map::Entry::Vacant(e) => e.insert(operation),
430 };
431
432 cx.spawn(move |this, mut cx| async move {
433 let _finish = util::defer({
434 let this = this.clone();
435 let mut cx = cx.clone();
436 let extension_id = extension_id.clone();
437 move || {
438 this.update(&mut cx, |this, cx| {
439 this.outstanding_operations.remove(extension_id.as_ref());
440 cx.notify();
441 })
442 .ok();
443 }
444 });
445
446 let mut response = http_client
447 .get(&url.as_ref(), Default::default(), true)
448 .await
449 .map_err(|err| anyhow!("error downloading extension: {}", err))?;
450
451 fs.remove_dir(
452 &extension_dir,
453 RemoveOptions {
454 recursive: true,
455 ignore_if_not_exists: true,
456 },
457 )
458 .await?;
459
460 let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
461 let archive = Archive::new(decompressed_bytes);
462 archive.unpack(extension_dir).await?;
463 this.update(&mut cx, |this, cx| {
464 this.reload(Some(extension_id.clone()), cx)
465 })?
466 .await;
467
468 match operation {
469 ExtensionOperation::Install => {
470 this.update(&mut cx, |_, cx| {
471 cx.emit(Event::ExtensionInstalled(extension_id));
472 })
473 .ok();
474 }
475 _ => {}
476 }
477
478 anyhow::Ok(())
479 })
480 .detach_and_log_err(cx);
481 }
482
483 pub fn install_latest_extension(
484 &mut self,
485 extension_id: Arc<str>,
486 cx: &mut ModelContext<Self>,
487 ) {
488 log::info!("installing extension {extension_id} latest version");
489
490 let Some(url) = self
491 .http_client
492 .build_zed_api_url(&format!("/extensions/{extension_id}/download"), &[])
493 .log_err()
494 else {
495 return;
496 };
497
498 self.install_or_upgrade_extension_at_endpoint(
499 extension_id,
500 url,
501 ExtensionOperation::Install,
502 cx,
503 );
504 }
505
506 pub fn upgrade_extension(
507 &mut self,
508 extension_id: Arc<str>,
509 version: Arc<str>,
510 cx: &mut ModelContext<Self>,
511 ) {
512 self.install_or_upgrade_extension(extension_id, version, ExtensionOperation::Upgrade, cx)
513 }
514
515 fn install_or_upgrade_extension(
516 &mut self,
517 extension_id: Arc<str>,
518 version: Arc<str>,
519 operation: ExtensionOperation,
520 cx: &mut ModelContext<Self>,
521 ) {
522 log::info!("installing extension {extension_id} {version}");
523 let Some(url) = self
524 .http_client
525 .build_zed_api_url(
526 &format!("/extensions/{extension_id}/{version}/download"),
527 &[],
528 )
529 .log_err()
530 else {
531 return;
532 };
533
534 self.install_or_upgrade_extension_at_endpoint(extension_id, url, operation, cx);
535 }
536
537 pub fn uninstall_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
538 let extension_dir = self.installed_dir.join(extension_id.as_ref());
539 let fs = self.fs.clone();
540
541 match self.outstanding_operations.entry(extension_id.clone()) {
542 hash_map::Entry::Occupied(_) => return,
543 hash_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
544 };
545
546 cx.spawn(move |this, mut cx| async move {
547 let _finish = util::defer({
548 let this = this.clone();
549 let mut cx = cx.clone();
550 let extension_id = extension_id.clone();
551 move || {
552 this.update(&mut cx, |this, cx| {
553 this.outstanding_operations.remove(extension_id.as_ref());
554 cx.notify();
555 })
556 .ok();
557 }
558 });
559
560 fs.remove_dir(
561 &extension_dir,
562 RemoveOptions {
563 recursive: true,
564 ignore_if_not_exists: true,
565 },
566 )
567 .await?;
568
569 this.update(&mut cx, |this, cx| this.reload(None, cx))?
570 .await;
571 anyhow::Ok(())
572 })
573 .detach_and_log_err(cx)
574 }
575
576 pub fn install_dev_extension(
577 &mut self,
578 extension_source_path: PathBuf,
579 cx: &mut ModelContext<Self>,
580 ) -> Task<Result<()>> {
581 let extensions_dir = self.extensions_dir();
582 let fs = self.fs.clone();
583 let builder = self.builder.clone();
584
585 cx.spawn(move |this, mut cx| async move {
586 let mut extension_manifest =
587 ExtensionManifest::load(fs.clone(), &extension_source_path).await?;
588 let extension_id = extension_manifest.id.clone();
589
590 if !this.update(&mut cx, |this, cx| {
591 match this.outstanding_operations.entry(extension_id.clone()) {
592 hash_map::Entry::Occupied(_) => return false,
593 hash_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Remove),
594 };
595 cx.notify();
596 true
597 })? {
598 return Ok(());
599 }
600
601 let _finish = util::defer({
602 let this = this.clone();
603 let mut cx = cx.clone();
604 let extension_id = extension_id.clone();
605 move || {
606 this.update(&mut cx, |this, cx| {
607 this.outstanding_operations.remove(extension_id.as_ref());
608 cx.notify();
609 })
610 .ok();
611 }
612 });
613
614 cx.background_executor()
615 .spawn({
616 let extension_source_path = extension_source_path.clone();
617 async move {
618 builder
619 .compile_extension(
620 &extension_source_path,
621 &mut extension_manifest,
622 CompileExtensionOptions { release: false },
623 )
624 .await
625 }
626 })
627 .await?;
628
629 let output_path = &extensions_dir.join(extension_id.as_ref());
630 if let Some(metadata) = fs.metadata(&output_path).await? {
631 if metadata.is_symlink {
632 fs.remove_file(
633 &output_path,
634 RemoveOptions {
635 recursive: false,
636 ignore_if_not_exists: true,
637 },
638 )
639 .await?;
640 } else {
641 bail!("extension {extension_id} is already installed");
642 }
643 }
644
645 fs.create_symlink(output_path, extension_source_path)
646 .await?;
647
648 this.update(&mut cx, |this, cx| this.reload(None, cx))?
649 .await;
650 Ok(())
651 })
652 }
653
654 pub fn rebuild_dev_extension(&mut self, extension_id: Arc<str>, cx: &mut ModelContext<Self>) {
655 let path = self.installed_dir.join(extension_id.as_ref());
656 let builder = self.builder.clone();
657 let fs = self.fs.clone();
658
659 match self.outstanding_operations.entry(extension_id.clone()) {
660 hash_map::Entry::Occupied(_) => return,
661 hash_map::Entry::Vacant(e) => e.insert(ExtensionOperation::Upgrade),
662 };
663
664 cx.notify();
665 let compile = cx.background_executor().spawn(async move {
666 let mut manifest = ExtensionManifest::load(fs, &path).await?;
667 builder
668 .compile_extension(
669 &path,
670 &mut manifest,
671 CompileExtensionOptions { release: true },
672 )
673 .await
674 });
675
676 cx.spawn(|this, mut cx| async move {
677 let result = compile.await;
678
679 this.update(&mut cx, |this, cx| {
680 this.outstanding_operations.remove(&extension_id);
681 cx.notify();
682 })?;
683
684 if result.is_ok() {
685 this.update(&mut cx, |this, cx| this.reload(Some(extension_id), cx))?
686 .await;
687 }
688
689 result
690 })
691 .detach_and_log_err(cx)
692 }
693
694 /// Updates the set of installed extensions.
695 ///
696 /// First, this unloads any themes, languages, or grammars that are
697 /// no longer in the manifest, or whose files have changed on disk.
698 /// Then it loads any themes, languages, or grammars that are newly
699 /// added to the manifest, or whose files have changed on disk.
700 fn extensions_updated(
701 &mut self,
702 new_index: ExtensionIndex,
703 cx: &mut ModelContext<Self>,
704 ) -> Task<()> {
705 let old_index = &self.extension_index;
706
707 // Determine which extensions need to be loaded and unloaded, based
708 // on the changes to the manifest and the extensions that we know have been
709 // modified.
710 let mut extensions_to_unload = Vec::default();
711 let mut extensions_to_load = Vec::default();
712 {
713 let mut old_keys = old_index.extensions.iter().peekable();
714 let mut new_keys = new_index.extensions.iter().peekable();
715 loop {
716 match (old_keys.peek(), new_keys.peek()) {
717 (None, None) => break,
718 (None, Some(_)) => {
719 extensions_to_load.push(new_keys.next().unwrap().0.clone());
720 }
721 (Some(_), None) => {
722 extensions_to_unload.push(old_keys.next().unwrap().0.clone());
723 }
724 (Some((old_key, _)), Some((new_key, _))) => match old_key.cmp(&new_key) {
725 Ordering::Equal => {
726 let (old_key, old_value) = old_keys.next().unwrap();
727 let (new_key, new_value) = new_keys.next().unwrap();
728 if old_value != new_value || self.modified_extensions.contains(old_key)
729 {
730 extensions_to_unload.push(old_key.clone());
731 extensions_to_load.push(new_key.clone());
732 }
733 }
734 Ordering::Less => {
735 extensions_to_unload.push(old_keys.next().unwrap().0.clone());
736 }
737 Ordering::Greater => {
738 extensions_to_load.push(new_keys.next().unwrap().0.clone());
739 }
740 },
741 }
742 }
743 self.modified_extensions.clear();
744 }
745
746 if extensions_to_load.is_empty() && extensions_to_unload.is_empty() {
747 return Task::ready(());
748 }
749
750 let reload_count = extensions_to_unload
751 .iter()
752 .filter(|id| extensions_to_load.contains(id))
753 .count();
754
755 log::info!(
756 "extensions updated. loading {}, reloading {}, unloading {}",
757 extensions_to_load.len() - reload_count,
758 reload_count,
759 extensions_to_unload.len() - reload_count
760 );
761
762 if let Some(telemetry) = &self.telemetry {
763 for extension_id in &extensions_to_load {
764 if let Some(extension) = new_index.extensions.get(extension_id) {
765 telemetry.report_extension_event(
766 extension_id.clone(),
767 extension.manifest.version.clone(),
768 );
769 }
770 }
771 }
772
773 let themes_to_remove = old_index
774 .themes
775 .iter()
776 .filter_map(|(name, entry)| {
777 if extensions_to_unload.contains(&entry.extension) {
778 Some(name.clone().into())
779 } else {
780 None
781 }
782 })
783 .collect::<Vec<_>>();
784 let languages_to_remove = old_index
785 .languages
786 .iter()
787 .filter_map(|(name, entry)| {
788 if extensions_to_unload.contains(&entry.extension) {
789 Some(name.clone())
790 } else {
791 None
792 }
793 })
794 .collect::<Vec<_>>();
795 let mut grammars_to_remove = Vec::new();
796 for extension_id in &extensions_to_unload {
797 let Some(extension) = old_index.extensions.get(extension_id) else {
798 continue;
799 };
800 grammars_to_remove.extend(extension.manifest.grammars.keys().cloned());
801 for (language_server_name, config) in extension.manifest.language_servers.iter() {
802 self.language_registry
803 .remove_lsp_adapter(config.language.as_ref(), language_server_name);
804 }
805 }
806
807 self.wasm_extensions
808 .retain(|(extension, _)| !extensions_to_unload.contains(&extension.id));
809 self.theme_registry.remove_user_themes(&themes_to_remove);
810 self.language_registry
811 .remove_languages(&languages_to_remove, &grammars_to_remove);
812
813 let languages_to_add = new_index
814 .languages
815 .iter()
816 .filter(|(_, entry)| extensions_to_load.contains(&entry.extension))
817 .collect::<Vec<_>>();
818 let mut grammars_to_add = Vec::new();
819 let mut themes_to_add = Vec::new();
820 for extension_id in &extensions_to_load {
821 let Some(extension) = new_index.extensions.get(extension_id) else {
822 continue;
823 };
824
825 grammars_to_add.extend(extension.manifest.grammars.keys().map(|grammar_name| {
826 let mut grammar_path = self.installed_dir.clone();
827 grammar_path.extend([extension_id.as_ref(), "grammars"]);
828 grammar_path.push(grammar_name.as_ref());
829 grammar_path.set_extension("wasm");
830 (grammar_name.clone(), grammar_path)
831 }));
832 themes_to_add.extend(extension.manifest.themes.iter().map(|theme_path| {
833 let mut path = self.installed_dir.clone();
834 path.extend([Path::new(extension_id.as_ref()), theme_path.as_path()]);
835 path
836 }));
837 }
838
839 self.language_registry
840 .register_wasm_grammars(grammars_to_add);
841
842 for (language_name, language) in languages_to_add {
843 let mut language_path = self.installed_dir.clone();
844 language_path.extend([
845 Path::new(language.extension.as_ref()),
846 language.path.as_path(),
847 ]);
848 self.language_registry.register_language(
849 language_name.clone(),
850 language.grammar.clone(),
851 language.matcher.clone(),
852 move || {
853 let config = std::fs::read_to_string(language_path.join("config.toml"))?;
854 let config: LanguageConfig = ::toml::from_str(&config)?;
855 let queries = load_plugin_queries(&language_path);
856 let tasks = std::fs::read_to_string(language_path.join("tasks.json"))
857 .ok()
858 .and_then(|contents| {
859 let definitions = serde_json_lenient::from_str(&contents).log_err()?;
860 Some(Arc::new(ContextProviderWithTasks::new(definitions)) as Arc<_>)
861 });
862
863 Ok((config, queries, tasks))
864 },
865 );
866 }
867
868 let fs = self.fs.clone();
869 let wasm_host = self.wasm_host.clone();
870 let root_dir = self.installed_dir.clone();
871 let theme_registry = self.theme_registry.clone();
872 let extension_entries = extensions_to_load
873 .iter()
874 .filter_map(|name| new_index.extensions.get(name).cloned())
875 .collect::<Vec<_>>();
876
877 self.extension_index = new_index;
878 cx.notify();
879 cx.emit(Event::ExtensionsUpdated);
880
881 cx.spawn(|this, mut cx| async move {
882 cx.background_executor()
883 .spawn({
884 let fs = fs.clone();
885 async move {
886 for theme_path in &themes_to_add {
887 theme_registry
888 .load_user_theme(&theme_path, fs.clone())
889 .await
890 .log_err();
891 }
892 }
893 })
894 .await;
895
896 let mut wasm_extensions = Vec::new();
897 for extension in extension_entries {
898 if extension.manifest.lib.kind.is_none() {
899 continue;
900 };
901
902 let wasm_extension = maybe!(async {
903 let mut path = root_dir.clone();
904 path.extend([extension.manifest.clone().id.as_ref(), "extension.wasm"]);
905 let mut wasm_file = fs
906 .open_sync(&path)
907 .await
908 .context("failed to open wasm file")?;
909
910 let mut wasm_bytes = Vec::new();
911 wasm_file
912 .read_to_end(&mut wasm_bytes)
913 .context("failed to read wasm")?;
914
915 wasm_host
916 .load_extension(
917 wasm_bytes,
918 extension.manifest.clone().clone(),
919 cx.background_executor().clone(),
920 )
921 .await
922 .with_context(|| {
923 format!("failed to load wasm extension {}", extension.manifest.id)
924 })
925 })
926 .await;
927
928 if let Some(wasm_extension) = wasm_extension.log_err() {
929 wasm_extensions.push((extension.manifest.clone(), wasm_extension));
930 } else {
931 this.update(&mut cx, |_, cx| {
932 cx.emit(Event::ExtensionFailedToLoad(extension.manifest.id.clone()))
933 })
934 .ok();
935 }
936 }
937
938 this.update(&mut cx, |this, cx| {
939 this.reload_complete_senders.clear();
940
941 for (manifest, wasm_extension) in &wasm_extensions {
942 for (language_server_name, language_server_config) in &manifest.language_servers
943 {
944 this.language_registry.register_lsp_adapter(
945 language_server_config.language.clone(),
946 Arc::new(ExtensionLspAdapter {
947 extension: wasm_extension.clone(),
948 host: this.wasm_host.clone(),
949 config: wit::LanguageServerConfig {
950 name: language_server_name.0.to_string(),
951 language_name: language_server_config.language.to_string(),
952 },
953 }),
954 );
955 }
956 }
957 this.wasm_extensions.extend(wasm_extensions);
958 ThemeSettings::reload_current_theme(cx)
959 })
960 .ok();
961 })
962 }
963
964 fn rebuild_extension_index(&self, cx: &mut ModelContext<Self>) -> Task<ExtensionIndex> {
965 let fs = self.fs.clone();
966 let work_dir = self.wasm_host.work_dir.clone();
967 let extensions_dir = self.installed_dir.clone();
968 let index_path = self.index_path.clone();
969 cx.background_executor().spawn(async move {
970 let start_time = Instant::now();
971 let mut index = ExtensionIndex::default();
972
973 fs.create_dir(&work_dir).await.log_err();
974 fs.create_dir(&extensions_dir).await.log_err();
975
976 let extension_paths = fs.read_dir(&extensions_dir).await;
977 if let Ok(mut extension_paths) = extension_paths {
978 while let Some(extension_dir) = extension_paths.next().await {
979 let Ok(extension_dir) = extension_dir else {
980 continue;
981 };
982 Self::add_extension_to_index(fs.clone(), extension_dir, &mut index)
983 .await
984 .log_err();
985 }
986 }
987
988 if let Ok(index_json) = serde_json::to_string_pretty(&index) {
989 fs.save(&index_path, &index_json.as_str().into(), Default::default())
990 .await
991 .context("failed to save extension index")
992 .log_err();
993 }
994
995 log::info!("rebuilt extension index in {:?}", start_time.elapsed());
996 index
997 })
998 }
999
1000 async fn add_extension_to_index(
1001 fs: Arc<dyn Fs>,
1002 extension_dir: PathBuf,
1003 index: &mut ExtensionIndex,
1004 ) -> Result<()> {
1005 let mut extension_manifest = ExtensionManifest::load(fs.clone(), &extension_dir).await?;
1006 let extension_id = extension_manifest.id.clone();
1007
1008 // TODO: distinguish dev extensions more explicitly, by the absence
1009 // of a checksum file that we'll create when downloading normal extensions.
1010 let is_dev = fs
1011 .metadata(&extension_dir)
1012 .await?
1013 .ok_or_else(|| anyhow!("directory does not exist"))?
1014 .is_symlink;
1015
1016 if let Ok(mut language_paths) = fs.read_dir(&extension_dir.join("languages")).await {
1017 while let Some(language_path) = language_paths.next().await {
1018 let language_path = language_path?;
1019 let Ok(relative_path) = language_path.strip_prefix(&extension_dir) else {
1020 continue;
1021 };
1022 let Ok(Some(fs_metadata)) = fs.metadata(&language_path).await else {
1023 continue;
1024 };
1025 if !fs_metadata.is_dir {
1026 continue;
1027 }
1028 let config = fs.load(&language_path.join("config.toml")).await?;
1029 let config = ::toml::from_str::<LanguageConfig>(&config)?;
1030
1031 let relative_path = relative_path.to_path_buf();
1032 if !extension_manifest.languages.contains(&relative_path) {
1033 extension_manifest.languages.push(relative_path.clone());
1034 }
1035
1036 index.languages.insert(
1037 config.name.clone(),
1038 ExtensionIndexLanguageEntry {
1039 extension: extension_id.clone(),
1040 path: relative_path,
1041 matcher: config.matcher,
1042 grammar: config.grammar,
1043 },
1044 );
1045 }
1046 }
1047
1048 if let Ok(mut theme_paths) = fs.read_dir(&extension_dir.join("themes")).await {
1049 while let Some(theme_path) = theme_paths.next().await {
1050 let theme_path = theme_path?;
1051 let Ok(relative_path) = theme_path.strip_prefix(&extension_dir) else {
1052 continue;
1053 };
1054
1055 let Some(theme_family) = ThemeRegistry::read_user_theme(&theme_path, fs.clone())
1056 .await
1057 .log_err()
1058 else {
1059 continue;
1060 };
1061
1062 let relative_path = relative_path.to_path_buf();
1063 if !extension_manifest.themes.contains(&relative_path) {
1064 extension_manifest.themes.push(relative_path.clone());
1065 }
1066
1067 for theme in theme_family.themes {
1068 index.themes.insert(
1069 theme.name.into(),
1070 ExtensionIndexThemeEntry {
1071 extension: extension_id.clone(),
1072 path: relative_path.clone(),
1073 },
1074 );
1075 }
1076 }
1077 }
1078
1079 let extension_wasm_path = extension_dir.join("extension.wasm");
1080 if fs.is_file(&extension_wasm_path).await {
1081 extension_manifest
1082 .lib
1083 .kind
1084 .get_or_insert(ExtensionLibraryKind::Rust);
1085 }
1086
1087 index.extensions.insert(
1088 extension_id.clone(),
1089 ExtensionIndexEntry {
1090 dev: is_dev,
1091 manifest: Arc::new(extension_manifest),
1092 },
1093 );
1094
1095 Ok(())
1096 }
1097}
1098
1099fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
1100 let mut result = LanguageQueries::default();
1101 if let Some(entries) = std::fs::read_dir(root_path).log_err() {
1102 for entry in entries {
1103 let Some(entry) = entry.log_err() else {
1104 continue;
1105 };
1106 let path = entry.path();
1107 if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
1108 if !remainder.ends_with(".scm") {
1109 continue;
1110 }
1111 for (name, query) in QUERY_FILENAME_PREFIXES {
1112 if remainder.starts_with(name) {
1113 if let Some(contents) = std::fs::read_to_string(&path).log_err() {
1114 match query(&mut result) {
1115 None => *query(&mut result) = Some(contents.into()),
1116 Some(r) => r.to_mut().push_str(contents.as_ref()),
1117 }
1118 }
1119 break;
1120 }
1121 }
1122 }
1123 }
1124 }
1125 result
1126}