Skip to content

Commit d70fb7b

Browse files
committed
feat: support search in network locations via GIO
Searching (including type-to-search) previously did nothing when browsing a network location without a gvfs-fuse path, such as shares opened through the Networks page or a URI typed into the location bar, because no SearchLocation could be derived for them. Add SearchLocation::Network and a gvfs mounter backend that searches recursively over the location URI with GIO enumerate_children, the same approach scan_path already uses for listing gvfs mounts. This works without gvfs-fuse, is faster than walking the fuse path since entries arrive in batches without per-file stat round trips, and returns to the network view when the search is cleared. The network:/// browse tree is excluded since its entries are servers that may not be mounted. This change was developed with AI assistance. I have reviewed and tested the changes and understand them in full.
1 parent 089ad2b commit d70fb7b

6 files changed

Lines changed: 452 additions & 120 deletions

File tree

src/app.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,14 +1617,33 @@ impl App {
16171617
if let Some(tab) = self.tab_model.data_mut::<Tab>(tab) {
16181618
let location_opt = match term_opt {
16191619
Some(term) => {
1620-
let search_location = if let Some(path) = tab.location.path_opt() {
1621-
Some(SearchLocation::Path(path.clone()))
1622-
} else if tab.location.is_recents() {
1623-
Some(SearchLocation::Recents)
1624-
} else if tab.location.is_trash() {
1625-
Some(SearchLocation::Trash)
1626-
} else {
1627-
None
1620+
let search_location = match &tab.location {
1621+
// Network locations are searched through GIO. The
1622+
// network:/// browse tree is excluded, as its entries
1623+
// are servers that may not be mounted
1624+
Location::Network(uri, display_name, path)
1625+
if !uri.starts_with("network://") =>
1626+
{
1627+
Some(SearchLocation::Network(
1628+
uri.clone(),
1629+
display_name.clone(),
1630+
path.clone(),
1631+
))
1632+
}
1633+
Location::Search(search_location @ SearchLocation::Network(..), ..) => {
1634+
Some(search_location.clone())
1635+
}
1636+
location => {
1637+
if let Some(path) = location.path_opt() {
1638+
Some(SearchLocation::Path(path.clone()))
1639+
} else if location.is_recents() {
1640+
Some(SearchLocation::Recents)
1641+
} else if location.is_trash() {
1642+
Some(SearchLocation::Trash)
1643+
} else {
1644+
None
1645+
}
1646+
}
16281647
};
16291648

16301649
search_location.map(|search_location| {
@@ -1642,6 +1661,10 @@ impl App {
16421661
None => match &tab.location {
16431662
Location::Search(search_location, ..) => match search_location {
16441663
SearchLocation::Path(path) => Some((Location::Path(path.clone()), false)),
1664+
SearchLocation::Network(uri, display_name, path) => Some((
1665+
Location::Network(uri.clone(), display_name.clone(), path.clone()),
1666+
false,
1667+
)),
16451668
SearchLocation::Recents => Some((Location::Recents, false)),
16461669
SearchLocation::Trash => Some((Location::Trash, false)),
16471670
},
@@ -4948,7 +4971,10 @@ impl Application for App {
49484971
) => {
49494972
command.arg(path);
49504973
}
4951-
Some(Location::Network(uri, ..)) => {
4974+
Some(
4975+
Location::Network(uri, ..)
4976+
| Location::Search(SearchLocation::Network(uri, ..), ..),
4977+
) => {
49524978
command.arg(uri);
49534979
}
49544980
Some(Location::Recents | Location::Search(SearchLocation::Recents, ..)) => {

src/dialog.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -774,14 +774,33 @@ impl App {
774774
fn search_set(&mut self, term_opt: Option<String>) -> Task<Message> {
775775
let location_opt = match term_opt {
776776
Some(term) => {
777-
let search_location = if let Some(path) = self.tab.location.path_opt() {
778-
Some(SearchLocation::Path(path.clone()))
779-
} else if self.tab.location.is_recents() {
780-
Some(SearchLocation::Recents)
781-
} else if self.tab.location.is_trash() {
782-
Some(SearchLocation::Trash)
783-
} else {
784-
None
777+
let search_location = match &self.tab.location {
778+
// Network locations are searched through GIO. The
779+
// network:/// browse tree is excluded, as its entries
780+
// are servers that may not be mounted
781+
Location::Network(uri, display_name, path)
782+
if !uri.starts_with("network://") =>
783+
{
784+
Some(SearchLocation::Network(
785+
uri.clone(),
786+
display_name.clone(),
787+
path.clone(),
788+
))
789+
}
790+
Location::Search(search_location @ SearchLocation::Network(..), ..) => {
791+
Some(search_location.clone())
792+
}
793+
location => {
794+
if let Some(path) = location.path_opt() {
795+
Some(SearchLocation::Path(path.clone()))
796+
} else if location.is_recents() {
797+
Some(SearchLocation::Recents)
798+
} else if location.is_trash() {
799+
Some(SearchLocation::Trash)
800+
} else {
801+
None
802+
}
803+
}
785804
};
786805

787806
search_location.map(|search_location| {
@@ -799,6 +818,10 @@ impl App {
799818
None => match &self.tab.location {
800819
Location::Search(search_location, ..) => match search_location {
801820
SearchLocation::Path(path) => Some((Location::Path(path.clone()), false)),
821+
SearchLocation::Network(uri, display_name, path) => Some((
822+
Location::Network(uri.clone(), display_name.clone(), path.clone()),
823+
false,
824+
)),
802825
SearchLocation::Recents => Some((Location::Recents, false)),
803826
SearchLocation::Trash => Some((Location::Trash, false)),
804827
},

src/menu.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ pub fn context_menu<'a>(
396396
children.push(sort_item(fl!("sort-by-size"), HeadingOptions::Size));
397397
}
398398
}
399-
(_, Location::Network(..)) => {
399+
(_, Location::Network(..) | Location::Search(SearchLocation::Network(..), ..)) => {
400400
if selected > 0 {
401401
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
402402
children.push(menu_item(fl!("open"), Action::Open).into());

0 commit comments

Comments
 (0)