Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ disktree --help # options: apparent size, follow links, skip hidden, …
what is marked; and the disk, free now and after the marks, with the way
to the review screen. Drag its left edge to resize it; double-click the
edge to reset.
The search icon beside a selected folder's path opens Google in your
browser with the platform and displayed path, asking what the folder is,
what application it belongs to, and the risks of deleting it.

One colour is kept apart: amber marks the selection, the main action, and
what can be had back.
Expand Down
1 change: 1 addition & 0 deletions crates/disktree-app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ disktree-core = { workspace = true }
anyhow = { workspace = true }
dunce = "1.0.5"
rustc-hash = { workspace = true }
url = "2.5"

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61", features = ["Win32_System_Console"] }
Expand Down
20 changes: 20 additions & 0 deletions crates/disktree-app/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2219,6 +2219,26 @@ impl Disktree {
cx.notify();
}

/// Research the path shown beside the clicked button in the browser.
pub fn search_folder(&self, path: &Path, cx: &Context<'_, Self>) {
let platform = match std::env::consts::OS {
"macos" => "macOS",
"windows" => "Windows",
"linux" => "Linux",
other => other,
};
let path = crate::marks::display_path(path, self.home.as_deref());
let query = format!(
"{platform} \"{path}\" what is this folder, what application \
does it belong to, and what are the risks of deleting it?"
);
let mut url = url::Url::parse("https://www.google.com/search")
.expect("the Google search URL is valid");
// Encode the whole query so punctuation in a path stays search text.
url.query_pairs_mut().append_pair("q", &query);
cx.open_url(url.as_str());
}

/// Show the tile a key acts on in Finder (or the file manager), selected.
pub fn reveal_target(&mut self, cx: &mut Context<'_, Self>) {
let crumbs =
Expand Down
42 changes: 42 additions & 0 deletions crates/disktree-app/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,48 @@ fn the_first_scan_shows_what_it_is_doing_then_the_treemap(
assert!(hidden_present, "hidden directories are part of the tree");
}

/// Clicking the panel researches its displayed folder, even when punctuation
/// in the name would otherwise split the URL into parameters or a fragment.
#[gpui_kit::test]
fn folder_search_opens_google_with_the_displayed_path(cx: &mut TestAppContext) {
cx.update(gpui_omarchy::init);
let temp = fixture();
let name = "cache & data+#日本";
std::fs::create_dir(temp.path().join(name)).expect("mkdir");
let (view, cx) = view_over(temp.path(), cx);
cx.simulate_resize(gpui_kit::size(px(1400.), px(900.)));
update(&view, cx, |app, cx| {
app.home = Some(temp.path().to_path_buf());
let folder = child_crumbs(app, &[], name);
app.select(Some(folder), cx);
});
draw(cx);
assert!(cx.cx.opened_url().is_none());
let button = cx.debug_bounds("search-folder").expect("search icon");
cx.simulate_click(button.center(), gpui_kit::Modifiers::none());
let opened = cx.cx.opened_url().expect("opened browser URL");
let url = url::Url::parse(&opened).expect("valid URL");
assert_eq!(url.host_str(), Some("www.google.com"));
assert_eq!(url.path(), "/search");
assert!(url.fragment().is_none());
let pairs: Vec<_> = url.query_pairs().collect();
assert_eq!(pairs.len(), 1);
assert_eq!(pairs[0].0, "q");
let query = &pairs[0].1;
let separator = std::path::MAIN_SEPARATOR;
assert!(query.contains(&format!("~{separator}{name}")));
assert!(query.contains("what is this folder"));
assert!(query.contains("what application does it belong to"));
assert!(query.contains("risks of deleting it"));
let platform = match std::env::consts::OS {
"macos" => "macOS",
"windows" => "Windows",
"linux" => "Linux",
other => other,
};
assert!(query.starts_with(platform));
}

#[gpui_kit::test]
fn keys_walk_the_tree_and_mark_what_is_selected(cx: &mut TestAppContext) {
cx.update(gpui_omarchy::init);
Expand Down
55 changes: 47 additions & 8 deletions crates/disktree-app/src/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,14 +1066,53 @@ fn selection_section(
)
.child(
div()
.text_size(text::CAPTION)
.text_color(theme.secondary)
.whitespace_nowrap()
.overflow_hidden()
.text_ellipsis()
.child(path.as_deref().map_or_else(String::new, |path| {
crate::marks::display_path(path, app.home.as_deref())
})),
.flex()
.flex_row()
.items_center()
.gap(space::XS)
.min_w_0()
.child(
div()
.min_w_0()
.text_size(text::CAPTION)
.text_color(theme.secondary)
.whitespace_nowrap()
.overflow_hidden()
.text_ellipsis()
.child(path.as_deref().map_or_else(
String::new,
|path| {
crate::marks::display_path(
path,
app.home.as_deref(),
)
},
)),
)
.when(node.is_dir(), |row| {
row.children(path.clone().map(|path| {
with_tooltip(
gpui_omarchy::icon_button(
"search-folder",
gpui_omarchy::IconName::Search,
"Look up this folder on Google",
ButtonVariant::Secondary,
cx,
)
.debug_selector(|| "search-folder".into())
.tab_stop(false)
.flex_shrink_0()
.on_click(cx.listener(
move |this, _, window, cx| {
this.search_folder(&path, cx);
window.focus(&this.focus, cx);
},
)),
"Search Google for this folder's purpose, \
application, and deletion risks",
)
}))
}),
);

let (number, unit) = match app.options.metric {
Expand Down