From 0148c1379b76c5c0a9d05588c07dcb06ec32f93f Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Fri, 22 May 2026 02:21:12 +0300 Subject: feat: add git worktree workspace management refs #137 --- src/config/io.rs | 8 ++++++++ src/config/keybinds.rs | 25 +++++++++++++++++++++++++ src/config/model.rs | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) (limited to 'src/config') diff --git a/src/config/io.rs b/src/config/io.rs index 3b483ba6..fe415d1c 100644 --- a/src/config/io.rs +++ b/src/config/io.rs @@ -189,6 +189,14 @@ fn load_live_config_from_str(content: &str) -> Result> &mut invalid_sections, |section| config.advanced = section, ); + load_live_section( + table, + "worktrees", + "worktree config", + &mut diagnostics, + &mut invalid_sections, + |section| config.worktrees = section, + ); load_live_section( table, "experimental", diff --git a/src/config/keybinds.rs b/src/config/keybinds.rs index 1c9c491b..edd64c90 100644 --- a/src/config/keybinds.rs +++ b/src/config/keybinds.rs @@ -246,6 +246,9 @@ pub struct Keybinds { pub help: ActionKeybinds, pub settings: ActionKeybinds, pub new_workspace: ActionKeybinds, + pub new_worktree: ActionKeybinds, + pub open_worktree: ActionKeybinds, + pub remove_worktree: ActionKeybinds, pub rename_workspace: ActionKeybinds, pub close_workspace: ActionKeybinds, pub workspace_picker: ActionKeybinds, @@ -375,6 +378,9 @@ impl Config { help: action!("keys.help", &self.keys.help), settings: action!("keys.settings", &self.keys.settings), new_workspace: action!("keys.new_workspace", &self.keys.new_workspace), + new_worktree: action!("keys.new_worktree", &self.keys.new_worktree), + open_worktree: action!("keys.open_worktree", &self.keys.open_worktree), + remove_worktree: action!("keys.remove_worktree", &self.keys.remove_worktree), rename_workspace: action!("keys.rename_workspace", &self.keys.rename_workspace), close_workspace: action!("keys.close_workspace", &self.keys.close_workspace), workspace_picker: action!("keys.workspace_picker", &self.keys.workspace_picker), @@ -1041,6 +1047,25 @@ next_tab = "prefix+n" ); } + #[test] + fn new_worktree_defaults_to_prefix_shift_g() { + let kb = Config::default().keybinds(); + assert_eq!( + binding_triggers(&kb.new_worktree), + vec![BindingTrigger::Prefix(( + KeyCode::Char('g'), + KeyModifiers::SHIFT + ))] + ); + } + + #[test] + fn open_and_remove_worktree_keybinds_are_unset_by_default() { + let kb = Config::default().keybinds(); + assert!(kb.open_worktree.bindings.is_empty()); + assert!(kb.remove_worktree.bindings.is_empty()); + } + #[test] fn array_bindings_allow_prefix_and_modified_direct() { let config: Config = toml::from_str( diff --git a/src/config/model.rs b/src/config/model.rs index f270a69c..215e6f32 100644 --- a/src/config/model.rs +++ b/src/config/model.rs @@ -81,6 +81,7 @@ pub struct Config { pub terminal: TerminalConfig, pub keys: KeysConfig, pub ui: UiConfig, + pub worktrees: WorktreesConfig, pub advanced: AdvancedConfig, pub experimental: ExperimentalConfig, } @@ -103,6 +104,12 @@ pub struct KeysConfig { pub settings: BindingConfig, /// Create a new workspace. Default: "prefix+shift+n" pub new_workspace: BindingConfig, + /// Create a Git worktree from the selected workspace. Default: "prefix+shift+g" + pub new_worktree: BindingConfig, + /// Open an existing Git worktree from the selected workspace. Unset by default. + pub open_worktree: BindingConfig, + /// Delete the selected managed worktree checkout after confirmation. Unset by default. + pub remove_worktree: BindingConfig, /// Rename the selected workspace. Default: "prefix+shift+w" pub rename_workspace: BindingConfig, /// Close the selected workspace. Default: "prefix+shift+d" @@ -186,6 +193,13 @@ pub struct IndexedKeysConfig { pub agents: String, } +#[derive(Debug, Deserialize)] +#[serde(default)] +pub struct WorktreesConfig { + /// Root directory under which Herdr creates / checkouts. + pub directory: String, +} + #[derive(Debug, Deserialize)] #[serde(default)] pub struct UiConfig { @@ -288,6 +302,9 @@ impl Default for KeysConfig { help: BindingConfig::one("prefix+?"), settings: BindingConfig::one("prefix+s"), new_workspace: BindingConfig::one("prefix+shift+n"), + new_worktree: BindingConfig::one("prefix+shift+g"), + open_worktree: BindingConfig::empty(), + remove_worktree: BindingConfig::empty(), rename_workspace: BindingConfig::one("prefix+shift+w"), close_workspace: BindingConfig::one("prefix+shift+d"), workspace_picker: BindingConfig::one("prefix+w"), @@ -326,6 +343,14 @@ impl Default for KeysConfig { } } +impl Default for WorktreesConfig { + fn default() -> Self { + Self { + directory: "~/.herdr/worktrees".into(), + } + } +} + impl Default for UiConfig { fn default() -> Self { Self { @@ -431,6 +456,19 @@ show_agent_labels_on_pane_borders = true assert!(config.ui.show_agent_labels_on_pane_borders); } + #[test] + fn worktrees_directory_defaults_and_parses() { + let default_config = Config::default(); + assert_eq!(default_config.worktrees.directory, "~/.herdr/worktrees"); + + let toml = r#" +[worktrees] +directory = "~/Projects/herdr-worktrees" +"#; + let config: Config = toml::from_str(toml).unwrap(); + assert_eq!(config.worktrees.directory, "~/Projects/herdr-worktrees"); + } + #[test] fn prompt_new_tab_name_defaults_on_and_parses() { let default_config = Config::default(); -- cgit v1.2.3