Select tabs from command scope
This commit is contained in:
@@ -32,8 +32,14 @@ impl ElyShell {
|
|||||||
let command_input =
|
let command_input =
|
||||||
cx.new(|cx| InputState::new(window, cx).placeholder("Search or enter address"));
|
cx.new(|cx| InputState::new(window, cx).placeholder("Search or enter address"));
|
||||||
|
|
||||||
let command_subscription =
|
let command_subscription = cx.subscribe_in(
|
||||||
cx.subscribe(&command_input, |shell: &mut Self, input, event: &InputEvent, cx| {
|
&command_input,
|
||||||
|
window,
|
||||||
|
|shell: &mut Self, input, event: &InputEvent, window, cx| {
|
||||||
|
let mut submitted_intent = None;
|
||||||
|
let mut sync_address = false;
|
||||||
|
let submitted = matches!(event, InputEvent::PressEnter { .. });
|
||||||
|
|
||||||
let ShellState::Ready(core) = &mut shell.state else {
|
let ShellState::Ready(core) = &mut shell.state else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -41,12 +47,22 @@ impl ElyShell {
|
|||||||
let value = input.read(cx).value().to_string();
|
let value = input.read(cx).value().to_string();
|
||||||
core.set_command_query(value);
|
core.set_command_query(value);
|
||||||
|
|
||||||
if matches!(event, InputEvent::PressEnter { .. }) {
|
if submitted {
|
||||||
shell.last_intent = core.submit_command().ok().flatten();
|
submitted_intent = core.submit_command().ok().flatten();
|
||||||
|
sync_address = core.command_query().is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if submitted {
|
||||||
|
shell.last_intent = submitted_intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if sync_address {
|
||||||
|
shell.sync_address_input(window, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let state = match InitialBrowserConfig::ely_defaults().and_then(|config| {
|
let state = match InitialBrowserConfig::ely_defaults().and_then(|config| {
|
||||||
BrowserCore::new(config).map_err(|error| match error {
|
BrowserCore::new(config).map_err(|error| match error {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use ely_domain::{
|
use ely_domain::{
|
||||||
BrowserTab, CommandIntent, DomainError, Profile, ProfileId, ProfileKind, Space, SpaceId, TabId,
|
BrowserTab, CommandIntent, CommandScope, DomainError, Profile, ProfileId, ProfileKind, Space,
|
||||||
UrlText,
|
SpaceId, TabId, UrlText,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::CoreError;
|
use crate::CoreError;
|
||||||
@@ -140,6 +140,11 @@ impl BrowserCore {
|
|||||||
self.command_query = query.into();
|
self.command_query = query.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn command_query(&self) -> &str {
|
||||||
|
&self.command_query
|
||||||
|
}
|
||||||
|
|
||||||
pub fn submit_command(&mut self) -> Result<Option<CommandIntent>, CoreError> {
|
pub fn submit_command(&mut self) -> Result<Option<CommandIntent>, CoreError> {
|
||||||
let query = self.command_query.trim();
|
let query = self.command_query.trim();
|
||||||
if query.is_empty() {
|
if query.is_empty() {
|
||||||
@@ -147,9 +152,18 @@ impl BrowserCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let intent = CommandIntent::parse(query)?;
|
let intent = CommandIntent::parse(query)?;
|
||||||
if let CommandIntent::Navigate(url) = &intent {
|
match &intent {
|
||||||
self.open_tab(url.clone());
|
CommandIntent::Navigate(url) => {
|
||||||
self.command_query.clear();
|
self.open_tab(url.clone());
|
||||||
|
self.command_query.clear();
|
||||||
|
}
|
||||||
|
CommandIntent::ScopedSearch { scope: CommandScope::Tabs, query } => {
|
||||||
|
if let Some(tab_id) = self.find_tab_match(query) {
|
||||||
|
self.select_tab(&tab_id)?;
|
||||||
|
self.command_query.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(intent))
|
Ok(Some(intent))
|
||||||
@@ -199,6 +213,14 @@ impl BrowserCore {
|
|||||||
.ok_or(CoreError::MissingActiveTab)
|
.ok_or(CoreError::MissingActiveTab)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_tab_match(&self, query: &str) -> Option<TabId> {
|
||||||
|
let normalized_query = query.trim().to_lowercase();
|
||||||
|
self.tabs
|
||||||
|
.iter()
|
||||||
|
.find(|tab| tab_matches_query(tab, &normalized_query))
|
||||||
|
.map(|tab| tab.id().clone())
|
||||||
|
}
|
||||||
|
|
||||||
fn build_tab(&self, url: UrlText) -> BrowserTab {
|
fn build_tab(&self, url: UrlText) -> BrowserTab {
|
||||||
let title = tab_title(&url);
|
let title = tab_title(&url);
|
||||||
BrowserTab::new(
|
BrowserTab::new(
|
||||||
@@ -219,11 +241,17 @@ fn tab_title(url: &UrlText) -> String {
|
|||||||
url.display_host()
|
url.display_host()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tab_matches_query(tab: &BrowserTab, normalized_query: &str) -> bool {
|
||||||
|
tab.title().to_lowercase().contains(normalized_query)
|
||||||
|
|| tab.url().as_str().to_lowercase().contains(normalized_query)
|
||||||
|
|| tab.display_url().to_lowercase().contains(normalized_query)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use ely_domain::UrlText;
|
use ely_domain::{CommandIntent, CommandScope, UrlText};
|
||||||
|
|
||||||
use super::{BrowserCore, InitialBrowserConfig};
|
use super::{BrowserCore, InitialBrowserConfig};
|
||||||
use crate::CoreError;
|
use crate::CoreError;
|
||||||
@@ -311,4 +339,39 @@ mod tests {
|
|||||||
assert_eq!(wrapped_tab_id, third_tab_id);
|
assert_eq!(wrapped_tab_id, third_tab_id);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tab_scoped_search_selects_matching_open_tab() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||||
|
let first_tab_id = core.active_tab()?.id().clone();
|
||||||
|
core.open_tab(UrlText::parse("https://example.com")?);
|
||||||
|
let servo_tab_id = core.open_tab(UrlText::parse("https://servo.org")?);
|
||||||
|
|
||||||
|
core.select_tab(&first_tab_id)?;
|
||||||
|
core.set_command_query("@tabs servo");
|
||||||
|
let intent = core.submit_command()?;
|
||||||
|
|
||||||
|
let snapshot = core.snapshot()?;
|
||||||
|
assert!(matches!(
|
||||||
|
intent,
|
||||||
|
Some(CommandIntent::ScopedSearch { scope: CommandScope::Tabs, query }) if query == "servo"
|
||||||
|
));
|
||||||
|
assert_eq!(snapshot.active_tab_id, servo_tab_id);
|
||||||
|
assert_eq!(snapshot.command_query, "");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tab_scoped_search_preserves_query_without_match() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||||
|
let active_tab_id = core.active_tab()?.id().clone();
|
||||||
|
|
||||||
|
core.set_command_query("@tabs absent");
|
||||||
|
core.submit_command()?;
|
||||||
|
|
||||||
|
let snapshot = core.snapshot()?;
|
||||||
|
assert_eq!(snapshot.active_tab_id, active_tab_id);
|
||||||
|
assert_eq!(snapshot.command_query, "@tabs absent");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user