aboutsummaryrefslogtreecommitdiff
path: root/src/app/mod.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-22 02:21:12 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-22 02:23:35 +0300
commit0148c1379b76c5c0a9d05588c07dcb06ec32f93f (patch)
tree554766d959c738ca2aa433c3d4b4a7de25568008 /src/app/mod.rs
parenta9ff4012bdca0dbdbc77bc6e53a430689fc899be (diff)
feat: add git worktree workspace management
refs #137
Diffstat (limited to 'src/app/mod.rs')
-rw-r--r--src/app/mod.rs76
1 files changed, 76 insertions, 0 deletions
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 107a4393..a1ae4a74 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -17,6 +17,7 @@ mod session;
pub mod state;
mod terminal_targets;
mod theme_sync;
+mod worktrees;
use std::collections::{HashMap, HashSet};
use std::future::pending;
@@ -234,6 +235,7 @@ impl App {
sidebar_width,
sidebar_width_source,
sidebar_section_split,
+ collapsed_space_keys,
) = if no_session {
(
Vec::new(),
@@ -243,6 +245,7 @@ impl App {
config.ui.sidebar_width,
state::SidebarWidthSource::ConfigDefault,
0.5_f32,
+ std::collections::HashSet::new(),
)
} else if let Some(snap) = crate::persist::load() {
let (ws, terminals, terminal_runtimes) = crate::persist::restore(
@@ -271,6 +274,7 @@ impl App {
state::SidebarWidthSource::ConfigDefault
},
snap.sidebar_section_split.unwrap_or(0.5),
+ snap.collapsed_space_keys,
)
} else {
crate::logging::session_restored(ws.len(), "ok");
@@ -288,6 +292,7 @@ impl App {
state::SidebarWidthSource::ConfigDefault
},
snap.sidebar_section_split.unwrap_or(0.5),
+ snap.collapsed_space_keys,
)
}
} else {
@@ -299,6 +304,7 @@ impl App {
config.ui.sidebar_width,
state::SidebarWidthSource::ConfigDefault,
0.5_f32,
+ std::collections::HashSet::new(),
)
};
@@ -320,6 +326,8 @@ impl App {
(18, 36)
});
+ let worktree_directory = crate::worktree::expand_tilde_path(&config.worktrees.directory);
+
info!(
pane_scrollback_limit_bytes = config.advanced.scrollback_limit_bytes,
"using pane scrollback configuration"
@@ -357,12 +365,24 @@ impl App {
detach_requested: false,
request_new_workspace: false,
request_new_tab: false,
+ request_new_linked_worktree: None,
+ request_open_existing_worktree: None,
+ request_new_workspace_cwd: None,
+ request_remove_linked_worktree: None,
+ request_submit_worktree_create: false,
+ request_submit_worktree_open: false,
+ request_submit_worktree_remove: false,
request_reload_config: false,
request_client_sound_config_reload: false,
request_clipboard_write: None,
creating_new_tab: false,
requested_new_tab_name: None,
rename_pane_target: None,
+ worktree_create: None,
+ worktree_open: None,
+ worktree_remove: None,
+ worktree_directory,
+ collapsed_space_keys,
request_complete_onboarding: false,
name_input: String::new(),
name_input_replace_on_type: false,
@@ -569,6 +589,47 @@ impl App {
needs_render = true;
}
+ if let Some(ws_idx) = self.state.request_new_linked_worktree.take() {
+ self.open_new_linked_worktree_dialog(ws_idx);
+ needs_render = true;
+ }
+
+ if let Some(ws_idx) = self.state.request_open_existing_worktree.take() {
+ self.open_existing_worktree_dialog(ws_idx);
+ needs_render = true;
+ }
+
+ if let Some(cwd) = self.state.request_new_workspace_cwd.take() {
+ if let Err(err) = self.create_workspace_with_options(cwd, true) {
+ tracing::error!(err = %err, "failed to create workspace at requested cwd");
+ self.state.mode = Mode::Navigate;
+ }
+ needs_render = true;
+ }
+
+ if let Some(ws_idx) = self.state.request_remove_linked_worktree.take() {
+ self.open_remove_linked_worktree_confirmation(ws_idx);
+ needs_render = true;
+ }
+
+ if self.state.request_submit_worktree_create {
+ self.state.request_submit_worktree_create = false;
+ self.start_worktree_add();
+ needs_render = true;
+ }
+
+ if self.state.request_submit_worktree_open {
+ self.state.request_submit_worktree_open = false;
+ self.open_selected_existing_worktree();
+ needs_render = true;
+ }
+
+ if self.state.request_submit_worktree_remove {
+ self.state.request_submit_worktree_remove = false;
+ self.start_worktree_remove();
+ needs_render = true;
+ }
+
if self.state.request_reload_config {
self.state.request_reload_config = false;
self.reload_config();
@@ -945,6 +1006,11 @@ impl App {
self.state.default_shell = config.terminal.default_shell.clone();
}
+ if !invalid_section("worktrees") {
+ self.state.worktree_directory =
+ crate::worktree::expand_tilde_path(&config.worktrees.directory);
+ }
+
if !invalid_section("theme") {
self.state.palette = resolve_palette_with_legacy_accent(config, !invalid_section("ui"));
self.state.theme_name = config
@@ -1105,6 +1171,15 @@ impl App {
Mode::RenameWorkspace | Mode::RenameTab | Mode::RenamePane => {
input::handle_rename_key(&mut self.state, key_event);
}
+ Mode::NewLinkedWorktree => {
+ self.handle_worktree_create_key(key_event);
+ }
+ Mode::OpenExistingWorktree => {
+ self.handle_worktree_open_key(key_event);
+ }
+ Mode::ConfirmRemoveWorktree => {
+ self.handle_worktree_remove_key(key_event);
+ }
Mode::Resize => {
input::handle_resize_key(&mut self.state, key);
}
@@ -1255,6 +1330,7 @@ mod tests {
resolved_identity_cwd,
branch: Some("render-dirty-test".into()),
ahead_behind: Some((1, 0)),
+ space: None,
}],
});