Add ZED_SELECTION_CHANGE_CMD to run a command on selection change

This commit is contained in:
Michael Sloan 2025-08-19 14:46:16 -06:00
parent 69b1c6d6f5
commit 2755cd8ec7
No known key found for this signature in database
5 changed files with 85 additions and 6 deletions

2
Cargo.lock generated
View file

@ -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",

View file

@ -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

View file

@ -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<Path>,
pub point: Point,
}
pub static LAST_CURSOR_POSITION_WATCH: LazyLock<(
Mutex<postage::watch::Sender<Option<LastCursorPosition>>>,
postage::watch::Receiver<Option<LastCursorPosition>>,
)> = 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,

View file

@ -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

View file

@ -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();
}
});
}