Only highlight the openable things

Kirill Bulatov created

Change summary

crates/terminal/src/terminal.rs           | 50 ++++++++++++--
crates/terminal_view/src/terminal_view.rs | 85 +++++++++++++++---------
2 files changed, 94 insertions(+), 41 deletions(-)

Detailed changes

crates/terminal/src/terminal.rs 🔗

@@ -33,6 +33,7 @@ use mappings::mouse::{
 use procinfo::LocalProcessInfo;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
+use smol::channel::Sender;
 use util::truncate_and_trailoff;
 
 use std::{
@@ -89,10 +90,12 @@ pub enum Event {
     Wakeup,
     BlinkChanged,
     SelectionsChanged,
-    Open {
-        is_url: bool,
-        maybe_url_or_path: String,
+    OpenUrl(String),
+    ProbePathOpen {
+        maybe_path: String,
+        can_open_tx: Sender<bool>,
     },
+    OpenPath(String),
 }
 
 #[derive(Clone)]
@@ -874,12 +877,43 @@ impl Terminal {
 
                 if let Some((maybe_url_or_path, is_url, url_match)) = found_url {
                     if *open {
-                        cx.emit(Event::Open {
-                            is_url,
-                            maybe_url_or_path,
-                        })
+                        let event = if is_url {
+                            Event::OpenUrl(maybe_url_or_path)
+                        } else {
+                            Event::OpenPath(maybe_url_or_path)
+                        };
+                        cx.emit(event);
                     } else {
-                        self.update_selected_word(prev_hovered_word, maybe_url_or_path, url_match);
+                        if is_url {
+                            self.update_selected_word(
+                                prev_hovered_word,
+                                maybe_url_or_path,
+                                url_match,
+                            );
+                        } else {
+                            let (can_open_tx, can_open_rx) = smol::channel::bounded(1);
+                            cx.emit(Event::ProbePathOpen {
+                                maybe_path: maybe_url_or_path.clone(),
+                                can_open_tx,
+                            });
+
+                            cx.spawn(|terminal, mut cx| async move {
+                                let can_open = can_open_rx.recv().await.unwrap_or(false);
+                                terminal.update(&mut cx, |terminal, cx| {
+                                    if can_open {
+                                        terminal.update_selected_word(
+                                            prev_hovered_word,
+                                            maybe_url_or_path,
+                                            url_match,
+                                        );
+                                    } else {
+                                        terminal.last_content.last_hovered_word.take();
+                                    }
+                                    cx.notify();
+                                });
+                            })
+                            .detach();
+                        };
                     }
                 }
             }

crates/terminal_view/src/terminal_view.rs 🔗

@@ -166,42 +166,27 @@ impl TerminalView {
                         .detach();
                 }
             }
-            Event::Open {
-                is_url,
-                maybe_url_or_path,
+            Event::ProbePathOpen {
+                maybe_path,
+                can_open_tx,
             } => {
-                if *is_url {
-                    cx.platform().open_url(maybe_url_or_path);
-                } else if let Some(workspace) = workspace.upgrade(cx) {
-                    let path_like =
-                        PathLikeWithPosition::parse_str(maybe_url_or_path.as_str(), |path_str| {
-                            Ok::<_, std::convert::Infallible>(Path::new(path_str).to_path_buf())
-                        })
-                        .expect("infallible");
-                    let maybe_path = path_like.path_like;
-                    workspace.update(cx, |workspace, cx| {
-                        let potential_abs_paths = if maybe_path.is_absolute() {
-                            vec![maybe_path]
-                        } else {
+                let can_open = !possible_open_targets(&workspace, maybe_path, cx).is_empty();
+                can_open_tx.send_blocking(can_open).ok();
+            }
+            Event::OpenUrl(url) => cx.platform().open_url(url),
+            Event::OpenPath(maybe_path) => {
+                let potential_abs_paths = possible_open_targets(&workspace, maybe_path, cx);
+                if let Some(path) = potential_abs_paths.into_iter().next() {
+                    // TODO kb change selections using path_like row & column
+                    let visible = path.path_like.is_dir();
+                    if let Some(workspace) = workspace.upgrade(cx) {
+                        workspace.update(cx, |workspace, cx| {
                             workspace
-                                .worktrees(cx)
-                                .map(|worktree| worktree.read(cx).abs_path().join(&maybe_path))
-                                .collect()
-                        };
-
-                        for path in potential_abs_paths {
-                            if path.exists() {
-                                let visible = path.is_dir();
-                                workspace
-                                    .open_abs_path(path, visible, cx)
-                                    .detach_and_log_err(cx);
-                                break;
-                            }
-                        }
-                    });
+                                .open_abs_path(path.path_like, visible, cx)
+                                .detach_and_log_err(cx);
+                        });
+                    }
                 }
-
-                // TODO kb let terminal know if we cannot open the string + remove the error message when folder open returns None
             }
             _ => cx.emit(event.clone()),
         })
@@ -389,6 +374,40 @@ impl TerminalView {
     }
 }
 
+fn possible_open_targets(
+    workspace: &WeakViewHandle<Workspace>,
+    maybe_path: &String,
+    cx: &mut ViewContext<'_, '_, TerminalView>,
+) -> Vec<PathLikeWithPosition<PathBuf>> {
+    let path_like = PathLikeWithPosition::parse_str(maybe_path.as_str(), |path_str| {
+        Ok::<_, std::convert::Infallible>(Path::new(path_str).to_path_buf())
+    })
+    .expect("infallible");
+    let maybe_path = path_like.path_like;
+    let potential_abs_paths = if maybe_path.is_absolute() {
+        vec![maybe_path]
+    } else if let Some(workspace) = workspace.upgrade(cx) {
+        workspace.update(cx, |workspace, cx| {
+            workspace
+                .worktrees(cx)
+                .map(|worktree| worktree.read(cx).abs_path().join(&maybe_path))
+                .collect()
+        })
+    } else {
+        Vec::new()
+    };
+
+    potential_abs_paths
+        .into_iter()
+        .filter(|path| path.exists())
+        .map(|path| PathLikeWithPosition {
+            path_like: path,
+            row: path_like.row,
+            column: path_like.column,
+        })
+        .collect()
+}
+
 pub fn regex_search_for_query(query: project::search::SearchQuery) -> Option<RegexSearch> {
     let searcher = match query {
         project::search::SearchQuery::Text { query, .. } => RegexSearch::new(&query),