diff --git a/crates/ely_browser_core/src/state.rs b/crates/ely_browser_core/src/state.rs index 4f2ad54..b5e203c 100644 --- a/crates/ely_browser_core/src/state.rs +++ b/crates/ely_browser_core/src/state.rs @@ -128,6 +128,29 @@ impl BrowserCore { pub fn restore_last_archived_tab(&mut self) -> Result { let archived_tab = self.archived_tabs.pop().ok_or(CoreError::NoArchivedTabs)?; let tab = archived_tab.into_tab(); + self.restore_tab(tab) + } + + pub fn restore_archived_tab_match(&mut self, query: &str) -> Result, CoreError> { + let normalized_query = query.trim().to_lowercase(); + if normalized_query.is_empty() { + return Ok(None); + } + + let Some(index) = self + .archived_tabs + .iter() + .rposition(|archived| tab_matches_query(archived.tab(), &normalized_query)) + else { + return Ok(None); + }; + + let archived_tab = self.archived_tabs.remove(index); + let tab = archived_tab.into_tab(); + self.restore_tab(tab).map(Some) + } + + fn restore_tab(&mut self, tab: BrowserTab) -> Result { let tab_id = tab.id().clone(); let insert_index = self .tabs @@ -219,6 +242,11 @@ impl BrowserCore { self.command_query.clear(); } } + CommandIntent::ScopedSearch { scope: CommandScope::Archive, query } + if self.restore_archived_tab_match(query)?.is_some() => + { + self.command_query.clear(); + } _ => {} } diff --git a/crates/ely_browser_core/tests/commands.rs b/crates/ely_browser_core/tests/commands.rs index 113c7a4..d05411d 100644 --- a/crates/ely_browser_core/tests/commands.rs +++ b/crates/ely_browser_core/tests/commands.rs @@ -84,6 +84,47 @@ fn restore_tab_command_reopens_last_archived_tab() -> Result<(), Box> Ok(()) } +#[test] +fn archive_scoped_search_restores_matching_archived_tab() -> Result<(), Box> { + let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; + core.open_tab(UrlText::parse("https://example.com")?); + let servo_tab_id = core.open_tab(UrlText::parse("https://servo.org")?); + core.close_active_tab()?; + + core.set_command_query("@archive servo"); + let intent = core.submit_command()?; + let snapshot = core.snapshot()?; + + assert_eq!( + intent, + Some(CommandIntent::ScopedSearch { + scope: ely_domain::CommandScope::Archive, + query: "servo".to_string() + }) + ); + assert_eq!(snapshot.active_tab_id, servo_tab_id); + assert!(snapshot.archived_tabs.is_empty()); + assert_eq!(snapshot.command_query, ""); + Ok(()) +} + +#[test] +fn archive_scoped_search_preserves_query_without_match() -> Result<(), Box> { + let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; + core.open_tab(UrlText::parse("https://example.com")?); + core.close_active_tab()?; + let active_tab_id = core.active_tab()?.id().clone(); + + core.set_command_query("@archive absent"); + core.submit_command()?; + let snapshot = core.snapshot()?; + + assert_eq!(snapshot.active_tab_id, active_tab_id); + assert_eq!(snapshot.archived_tabs.len(), 1); + assert_eq!(snapshot.command_query, "@archive absent"); + Ok(()) +} + #[test] fn unknown_command_preserves_query() -> Result<(), Box> { let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; diff --git a/crates/ely_browser_core/tests/tabs.rs b/crates/ely_browser_core/tests/tabs.rs index c86e30f..24ea2e7 100644 --- a/crates/ely_browser_core/tests/tabs.rs +++ b/crates/ely_browser_core/tests/tabs.rs @@ -73,6 +73,42 @@ fn restores_last_archived_tab() -> Result<(), Box> { Ok(()) } +#[test] +fn restores_matching_archived_tab() -> Result<(), Box> { + let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; + let example_tab_id = core.open_tab(UrlText::parse("https://example.com")?); + let servo_tab_id = core.open_tab(UrlText::parse("https://servo.org")?); + + core.close_active_tab()?; + core.select_tab(&example_tab_id)?; + core.close_active_tab()?; + + let restored_tab_id = core.restore_archived_tab_match("servo")?; + let snapshot = core.snapshot()?; + + assert_eq!(restored_tab_id, Some(servo_tab_id.clone())); + assert_eq!(snapshot.active_tab_id, servo_tab_id); + assert_eq!(snapshot.archived_tabs.len(), 1); + assert_eq!(snapshot.archived_tabs[0].tab().id(), &example_tab_id); + Ok(()) +} + +#[test] +fn ignores_empty_archived_tab_match_query() -> Result<(), Box> { + let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; + core.open_tab(UrlText::parse("https://example.com")?); + core.close_active_tab()?; + let active_tab_id = core.active_tab()?.id().clone(); + + let restored_tab_id = core.restore_archived_tab_match(" ")?; + let snapshot = core.snapshot()?; + + assert_eq!(restored_tab_id, None); + assert_eq!(snapshot.active_tab_id, active_tab_id); + assert_eq!(snapshot.archived_tabs.len(), 1); + Ok(()) +} + #[test] fn restore_without_archived_tabs_returns_error() -> Result<(), Box> { let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?; diff --git a/crates/ely_domain/src/command.rs b/crates/ely_domain/src/command.rs index 9331048..9b2ddbd 100644 --- a/crates/ely_domain/src/command.rs +++ b/crates/ely_domain/src/command.rs @@ -2,6 +2,7 @@ use crate::{DomainError, UrlText}; #[derive(Clone, Debug, Eq, PartialEq)] pub enum CommandScope { + Archive, Tabs, Bookmarks, History, @@ -43,6 +44,7 @@ impl CommandIntent { fn parse_scope(value: &str) -> Option<(CommandScope, &str)> { let (scope, query) = value.split_once(' ')?; let scope = match scope { + "@archive" => CommandScope::Archive, "@tabs" => CommandScope::Tabs, "@bookmarks" => CommandScope::Bookmarks, "@history" => CommandScope::History,