diff --git a/crates/ide-host/src/plugins/market.rs b/crates/ide-host/src/plugins/market.rs index 061b5df2..fdf2e514 100644 --- a/crates/ide-host/src/plugins/market.rs +++ b/crates/ide-host/src/plugins/market.rs @@ -165,6 +165,16 @@ impl PluginHost { self.save_state(); } + /// The marketplace's plugins: read again when `refresh`, else the last index + /// read. Reads it when none has been read yet (first run, nothing cached). + pub fn market_listing(&self, refresh: bool) -> IdeResult> { + if refresh || (self.market.read().is_none() && self.market_url().is_some()) { + self.refresh_market() + } else { + Ok(self.market_plugins()) + } + } + /// Reads the index again, keeps it, and applies its revocations. Announces /// updates of installed plugins it hasn't announced yet. pub fn refresh_market(&self) -> IdeResult> { diff --git a/crates/ide-host/tests/distribution.rs b/crates/ide-host/tests/distribution.rs index a8d97934..38002f36 100644 --- a/crates/ide-host/tests/distribution.rs +++ b/crates/ide-host/tests/distribution.rs @@ -196,6 +196,28 @@ fn write_index( path.to_string_lossy().into_owned() } +/// First run: nothing cached yet, so listing the marketplace reads its index +/// instead of showing an empty one; later listings use the index read. +#[test] +fn the_first_marketplace_listing_reads_the_index() { + let f = fixture(); + assert!(f.host.market_listing(false).unwrap().is_empty(), "no marketplace configured"); + + let market = f.work.join("market"); + std::fs::create_dir_all(&market).unwrap(); + let key = PublisherKey::from_seed([3; 32]); + let (_, entry) = signed_hello(&market, "1.0.0", &key, &f.host); + let url = write_index(&market, &key, vec![entry], vec![], false); + f.host.set_market_url(Some(url.clone())); + + let listed = f.host.market_listing(false).unwrap(); + assert_eq!(listed.iter().map(|p| p.entry.id.as_str()).collect::>(), ["acme.hello"]); + + std::fs::remove_file(&url).unwrap(); + assert_eq!(f.host.market_listing(false).unwrap().len(), 1, "the index read is kept"); + assert!(f.host.market_listing(true).is_err(), "refresh reads the index again"); +} + #[test] fn signed_marketplace_plugins_install_and_revocations_disable_them() { let f = fixture(); diff --git a/src-tauri/src/commands/plugin.rs b/src-tauri/src/commands/plugin.rs index 998c591a..bb565af4 100644 --- a/src-tauri/src/commands/plugin.rs +++ b/src-tauri/src/commands/plugin.rs @@ -473,7 +473,7 @@ pub async fn plugin_web_read_file(path: PathBuf) -> IdeResult { Ok(base64::engine::general_purpose::STANDARD.encode(bytes)) } -/// The marketplace's plugins: read again when `refresh`, else the last index read. +/// The marketplace's plugins: read again when `refresh` or when none was read yet, else the last index read. #[tauri::command] #[specta::specta] pub async fn plugin_market( @@ -481,7 +481,7 @@ pub async fn plugin_market( refresh: bool, ) -> IdeResult> { let host = state.plugins.clone(); - blocking(move || if refresh { host.refresh_market() } else { Ok(host.market_plugins()) }).await + blocking(move || host.market_listing(refresh)).await } #[tauri::command] diff --git a/src/bindings.ts b/src/bindings.ts index eacf7e40..364c5b4b 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -999,7 +999,7 @@ export const commands = { pluginWebReadFile: (path: string) => typedError(__TAURI_INVOKE("plugin_web_read_file", { path })), /** Gutter markers of plugin `line-markers` providers for an open document. */ pluginLineMarkers: (doc: DocId) => typedError(__TAURI_INVOKE("plugin_line_markers", { doc })), - /** The marketplace's plugins: read again when `refresh`, else the last index read. */ + /** The marketplace's plugins: read again when `refresh` or when none was read yet, else the last index read. */ pluginMarket: (refresh: boolean) => typedError(__TAURI_INVOKE("plugin_market", { refresh })), pluginMarketUrl: () => __TAURI_INVOKE("plugin_market_url"), /** Sets the marketplace index URL (an `https://…/index.json` or a local path; `None` for the default). */