diff --git a/Cargo.lock b/Cargo.lock index 4a5dec4734..4178d91c71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5070,6 +5070,7 @@ dependencies = [ "multi_buffer", "ordered-float 2.10.1", "parking_lot", + "postage", "pretty_assertions", "project", "rand 0.8.5", @@ -20462,6 +20463,7 @@ dependencies = [ "parking_lot", "paths", "picker", + "postage", "pretty_assertions", "profiling", "project", diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 339f98ae8b..7c8fe56475 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -92,6 +92,7 @@ uuid.workspace = true workspace.workspace = true zed_actions.workspace = true workspace-hack.workspace = true +postage.workspace = true [dev-dependencies] ctor.workspace = true diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 7c36a41046..05688ee6ee 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -176,17 +176,15 @@ use snippet::Snippet; use std::{ any::TypeId, borrow::Cow, - cell::OnceCell, - cell::RefCell, + cell::{OnceCell, RefCell}, cmp::{self, Ordering, Reverse}, iter::Peekable, mem, num::NonZeroU32, - ops::Not, - ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive}, + ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive}, path::{Path, PathBuf}, rc::Rc, - sync::Arc, + sync::{Arc, LazyLock}, time::{Duration, Instant}, }; use sum_tree::TreeMap; @@ -237,6 +235,21 @@ pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction"; pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict"; pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.)); +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LastCursorPosition { + pub path: PathBuf, + pub worktree_path: Arc, + pub point: Point, +} + +pub static LAST_CURSOR_POSITION_WATCH: LazyLock<( + Mutex>>, + postage::watch::Receiver>, +)> = LazyLock::new(|| { + let (sender, receiver) = postage::watch::channel(); + (Mutex::new(sender), receiver) +}); + pub type RenderDiffHunkControlsFn = Arc< dyn Fn( u32, @@ -3018,10 +3031,28 @@ impl Editor { let new_cursor_position = newest_selection.head(); let selection_start = newest_selection.start; + let new_cursor_point = new_cursor_position.to_point(buffer); + if let Some(project) = self.project() + && let Some((path, worktree_path)) = + self.file_at(new_cursor_point, cx).and_then(|file| { + file.as_local().and_then(|file| { + let worktree = + project.read(cx).worktree_for_id(file.worktree_id(cx), cx)?; + Some((file.abs_path(cx), worktree.read(cx).abs_path())) + }) + }) + { + *LAST_CURSOR_POSITION_WATCH.0.lock().borrow_mut() = Some(LastCursorPosition { + path, + worktree_path, + point: new_cursor_point, + }); + } + if effects.nav_history.is_none() || effects.nav_history == Some(true) { self.push_to_nav_history( *old_cursor_position, - Some(new_cursor_position.to_point(buffer)), + Some(new_cursor_point), false, effects.nav_history == Some(true), cx, diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d69efaf6c0..3634fbb692 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -164,6 +164,7 @@ zed_actions.workspace = true zeta.workspace = true zlog.workspace = true zlog_settings.workspace = true +postage.workspace = true [target.'cfg(target_os = "windows")'.dependencies] windows.workspace = true diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index df30d4dd7b..374445cbaf 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -17,6 +17,7 @@ use fs::{Fs, RealFs}; use futures::{StreamExt, channel::oneshot, future}; use git::GitHostingProviderRegistry; use gpui::{App, AppContext as _, Application, AsyncApp, Focusable as _, UpdateGlobal as _}; +use postage::stream::Stream as _; use gpui_tokio::Tokio; use http_client::{Url, read_proxy_from_env}; @@ -747,6 +748,49 @@ pub fn main() { } }) .detach(); + + if let Ok(selection_change_command) = env::var("ZED_SELECTION_CHANGE_CMD") { + log::info!( + "Will run {} when the selection changes", + selection_change_command + ); + let mut cursor_reciever = editor::LAST_CURSOR_POSITION_WATCH.1.clone(); + cx.background_spawn(async move { + while let Some(mut cursor) = cursor_reciever.recv().await { + loop { + // todo! Check if it's changed meanwhile and refresh. + if let Some(cursor) = dbg!(&cursor) { + let status = smol::process::Command::new(&selection_change_command) + .arg(cursor.worktree_path.as_ref()) + .arg(format!( + "{}:{}:{}", + cursor.path.display(), + cursor.point.row + 1, + cursor.point.column + 1 + )) + .status() + .await; + match status { + Ok(status) => { + if !status.success() { + log::error!("Command failed with status {}", status); + } + } + Err(err) => { + log::error!("Command failed with error {}", err); + } + } + } + let new_cursor = cursor_reciever.borrow(); + if *new_cursor == cursor { + break; + } + cursor = new_cursor.clone(); + } + } + }) + .detach(); + } }); }