mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 00:02:23 +00:00
fix(sidebar): put a new tab in its repo group on the first frame (#411)
Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
This commit is contained in:
@@ -275,6 +275,7 @@ impl RemoteTerminal {
|
||||
let win = win_size(size, cell_w, cell_h);
|
||||
|
||||
let workspace = spawn_workspace(owner.as_deref(), route);
|
||||
let spawned_in = cwd.clone();
|
||||
|
||||
let owner = owner.filter(|_| {
|
||||
route.is_local()
|
||||
@@ -305,9 +306,24 @@ impl RemoteTerminal {
|
||||
|
||||
let mut term = Self::from_stream(stream, size)?;
|
||||
term.route = route.clone();
|
||||
term.seed_cwd(spawned_in);
|
||||
Ok((term, pane_id))
|
||||
}
|
||||
|
||||
/// Remember the directory the daemon was asked to spawn in, so everything
|
||||
/// that keys off a pane's cwd — the sidebar's repo grouping above all —
|
||||
/// has an answer before the shell gets far enough to report its own via
|
||||
/// OSC 7. The reader thread is already running, so a report that beat us
|
||||
/// here wins: it describes where the shell actually landed, which is not
|
||||
/// always where we asked (a missing directory sends the daemon home, an
|
||||
/// rc file may `cd` on its own).
|
||||
fn seed_cwd(&self, cwd: Option<PathBuf>) {
|
||||
let Some(cwd) = cwd else { return };
|
||||
if let Ok(mut guard) = self.cwd.lock() {
|
||||
guard.get_or_insert(cwd);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attach(size: TermSize, cell_w: u16, cell_h: u16, pane_id: u64) -> anyhow::Result<Self> {
|
||||
Self::attach_on(&PaneRoute::Local, size, cell_w, cell_h, pane_id)
|
||||
}
|
||||
@@ -3210,6 +3226,65 @@ mod tests {
|
||||
assert!(poll(&|s| s.is_none()), "a None report clears the session");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_spawn_directory_answers_until_the_shell_reports_its_own() {
|
||||
crate::core::config::pin_test_config_dir();
|
||||
let (client_side, mut daemon_side) = UnixStream::pair().unwrap();
|
||||
let term = RemoteTerminal::from_stream(client_side, TermSize::new(80, 24)).unwrap();
|
||||
let poll = |want: Option<&str>| {
|
||||
let want = want.map(PathBuf::from);
|
||||
for _ in 0..200 {
|
||||
if term.foreground_cwd() == want {
|
||||
return true;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
}
|
||||
false
|
||||
};
|
||||
|
||||
assert_eq!(term.foreground_cwd(), None, "nothing known before the seed");
|
||||
term.seed_cwd(Some(PathBuf::from("/repo/tty7")));
|
||||
assert_eq!(
|
||||
term.foreground_cwd(),
|
||||
Some(PathBuf::from("/repo/tty7")),
|
||||
"the sidebar can group this pane without waiting for the shell"
|
||||
);
|
||||
|
||||
DaemonMsg::Cwd(PathBuf::from("/repo/tty7/crates"))
|
||||
.encode(&mut daemon_side)
|
||||
.unwrap();
|
||||
daemon_side.flush().unwrap();
|
||||
assert!(
|
||||
poll(Some("/repo/tty7/crates")),
|
||||
"the shell's own report replaces the seed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_shell_report_that_beat_the_seed_wins() {
|
||||
crate::core::config::pin_test_config_dir();
|
||||
let (client_side, mut daemon_side) = UnixStream::pair().unwrap();
|
||||
let term = RemoteTerminal::from_stream(client_side, TermSize::new(80, 24)).unwrap();
|
||||
|
||||
DaemonMsg::Cwd(PathBuf::from("/somewhere/else"))
|
||||
.encode(&mut daemon_side)
|
||||
.unwrap();
|
||||
daemon_side.flush().unwrap();
|
||||
for _ in 0..200 {
|
||||
if term.foreground_cwd().is_some() {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
}
|
||||
|
||||
term.seed_cwd(Some(PathBuf::from("/repo/tty7")));
|
||||
assert_eq!(
|
||||
term.foreground_cwd(),
|
||||
Some(PathBuf::from("/somewhere/else")),
|
||||
"where the shell actually landed beats where we asked it to"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marks_record_the_row_each_one_landed_on() {
|
||||
let (client_side, mut daemon_side) = UnixStream::pair().unwrap();
|
||||
|
||||
+13
-4
@@ -2312,9 +2312,9 @@ impl Tty7App {
|
||||
if !self.guard_local_spawn(window, cx) {
|
||||
return;
|
||||
}
|
||||
let pane_ws = self.window_workspace(cx);
|
||||
let group = self.spawn_group(cwd.as_deref(), cx);
|
||||
let tab = match new_terminal(
|
||||
pane_ws,
|
||||
self.window_workspace(cx),
|
||||
Some(self.workspace),
|
||||
self.font_size,
|
||||
cwd,
|
||||
@@ -2336,7 +2336,11 @@ impl Tty7App {
|
||||
self.remember_active_pane(window, cx);
|
||||
self.maximized = None;
|
||||
let insert_at = self.new_tab_insert_at(cx);
|
||||
self.tabs.insert(insert_at, Tab::new(Pane::leaf(tab)));
|
||||
let new_tab = Tab::new(Pane::leaf(tab));
|
||||
if let Some(group) = group {
|
||||
*new_tab.sidebar_group.borrow_mut() = group;
|
||||
}
|
||||
self.tabs.insert(insert_at, new_tab);
|
||||
self.active = insert_at;
|
||||
self.focus_active(window, cx);
|
||||
self.save_session(cx);
|
||||
@@ -2968,6 +2972,7 @@ impl Tty7App {
|
||||
let view = source.read(cx);
|
||||
(view.local_cwd(), view.shell_spec())
|
||||
};
|
||||
let group = self.spawn_group(cwd.as_deref(), cx);
|
||||
let new = match new_terminal(
|
||||
self.window_workspace(cx),
|
||||
Some(self.workspace),
|
||||
@@ -3000,7 +3005,11 @@ impl Tty7App {
|
||||
self.remember_active_pane(window, cx);
|
||||
self.maximized = None;
|
||||
let insert_at = self.new_tab_insert_at(cx);
|
||||
self.tabs.insert(insert_at, Tab::new(Pane::leaf(new)));
|
||||
let tab = Tab::new(Pane::leaf(new));
|
||||
if let Some(group) = group {
|
||||
*tab.sidebar_group.borrow_mut() = group;
|
||||
}
|
||||
self.tabs.insert(insert_at, tab);
|
||||
self.active = insert_at;
|
||||
self.focus_active(window, cx);
|
||||
}
|
||||
|
||||
@@ -746,6 +746,28 @@ impl Tty7App {
|
||||
self.activate(i, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
/// Which group a tab about to be spawned in `cwd` belongs to, when the
|
||||
/// repo probe for that directory has already landed. `Some(None)` means
|
||||
/// the cache knows it is not a repo; a bare `None` means it never looked.
|
||||
///
|
||||
/// A tab's group otherwise starts empty and only fills in once its shell
|
||||
/// has started and reported a cwd, which parks every new tab in the
|
||||
/// scratch group at the bottom of the sidebar until then. A tab spawned
|
||||
/// from one already sitting in a repo inherits a warm cache, so seeding
|
||||
/// it here lands the tab in its group on the first frame.
|
||||
pub(crate) fn spawn_group(
|
||||
&self,
|
||||
cwd: Option<&Path>,
|
||||
cx: &gpui::App,
|
||||
) -> Option<Option<PathBuf>> {
|
||||
let host = self
|
||||
.window_workspace(cx)
|
||||
.as_ref()
|
||||
.map_or(crate::ui::host_ops::HostId::LOCAL, |ws| ws.target.host_id());
|
||||
cx.try_global::<GitStatusCache>()?
|
||||
.known_repo_for(host, cwd?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
||||
Reference in New Issue
Block a user