T12: sessions: new (+, Cmd-T), close (row x, Cmd-W), select (press, Cmd-1 to 8, Cmd-9 last, Ctrl-Tab, Ctrl-Shift-Tab), Ghostty's keys off macOS; rows show the title and the folder with ~; an ended program closes its row, the last row the window, and the app quits; a bell marks a hidden row; new sessions follow the Shell and Start in settings
This commit is contained in:
6 files changed
+391
-52
No files matched your search
+112
@@ -0,0 +1,112 @@
|
||||
use std::path::{MAIN_SEPARATOR, Path, PathBuf};
|
||||
|
||||
use ely_gpui_component::{
|
||||
buttons::{ButtonVariant, IconButton},
|
||||
lists::ListItem,
|
||||
primitives::{Icon, IconName},
|
||||
theme::{ActiveTheme, ControlSize},
|
||||
};
|
||||
use gpui::{App, Entity, Subscription};
|
||||
|
||||
use crate::{
|
||||
term::TerminalView,
|
||||
workspace::{TITLE, Workspace},
|
||||
};
|
||||
|
||||
/// A program on its own terminal, as the sessions column lists it.
|
||||
pub struct Session {
|
||||
pub terminal: Entity<TerminalView>,
|
||||
/// The program's title; empty while it names none.
|
||||
pub title: String,
|
||||
/// The folder it reports, else the one it started in.
|
||||
pub folder: PathBuf,
|
||||
/// A bell rang while it was hidden.
|
||||
pub bell: bool,
|
||||
pub _events: Subscription,
|
||||
}
|
||||
|
||||
/// `folder` as a row shows it: the home folder as `~`.
|
||||
pub fn shown(folder: &Path, home: Option<&Path>) -> String {
|
||||
match home.and_then(|home| folder.strip_prefix(home).ok()) {
|
||||
Some(rest) if rest.as_os_str().is_empty() => "~".into(),
|
||||
Some(rest) => format!("~{MAIN_SEPARATOR}{}", rest.display()),
|
||||
None => folder.display().to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The session shown once session `closed` of `left + 1` is gone, `active` having been shown.
|
||||
pub fn after_close(active: usize, closed: usize, left: usize) -> usize {
|
||||
if closed < active || active == left {
|
||||
active - 1
|
||||
} else {
|
||||
active
|
||||
}
|
||||
}
|
||||
|
||||
/// Session `index`'s row: its title and folder, a bell while one rang unseen, a button that closes it.
|
||||
pub fn row(
|
||||
workspace: &Entity<Workspace>,
|
||||
index: usize,
|
||||
session: &Session,
|
||||
active: bool,
|
||||
home: Option<&Path>,
|
||||
cx: &App,
|
||||
) -> ListItem {
|
||||
let pid = session.terminal.read(cx).pid() as usize;
|
||||
let title = if session.title.is_empty() {
|
||||
TITLE
|
||||
} else {
|
||||
&session.title
|
||||
};
|
||||
let (select, close) = (workspace.clone(), workspace.clone());
|
||||
let item = ListItem::new(("session", pid), title.to_string())
|
||||
.description(shown(&session.folder, home))
|
||||
.selected(active)
|
||||
.strong(session.bell)
|
||||
.on_click(move |_, window, cx| select.update(cx, |this, cx| this.select(index, window, cx)))
|
||||
.trailing(
|
||||
IconButton::new(("close-session", pid), IconName::X)
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.size(ControlSize::Sm)
|
||||
.tooltip("Close session")
|
||||
.on_click(move |_, window, cx| {
|
||||
cx.stop_propagation();
|
||||
close.update(cx, |this, cx| this.close(index, window, cx))
|
||||
}),
|
||||
);
|
||||
if session.bell {
|
||||
item.leading(Icon::new(IconName::Bell).color(cx.theme().colors.warning))
|
||||
} else {
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn folders_under_home_read_from_a_tilde() {
|
||||
let home = PathBuf::from("/Users/ely");
|
||||
let shown = |folder: &str| shown(Path::new(folder), Some(&home));
|
||||
assert_eq!(shown("/Users/ely"), "~");
|
||||
assert_eq!(
|
||||
shown("/Users/ely/src/app"),
|
||||
format!("~{MAIN_SEPARATOR}src/app")
|
||||
);
|
||||
assert_eq!(
|
||||
shown("/Users/elyse"),
|
||||
"/Users/elyse",
|
||||
"a sibling is no child"
|
||||
);
|
||||
assert_eq!(shown("/tmp"), "/tmp");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closing_shows_the_next_session_or_keeps_the_shown_one() {
|
||||
assert_eq!(after_close(2, 0, 3), 1, "one before it went");
|
||||
assert_eq!(after_close(1, 1, 3), 1, "it went: the next one");
|
||||
assert_eq!(after_close(3, 3, 3), 2, "the last went: the one before");
|
||||
assert_eq!(after_close(1, 2, 3), 1, "one after it went");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user