From b9c021134240d4e9d1849dd3537b420fc95a8978 Mon Sep 17 00:00:00 2001 From: Anas Elgarhy Date: Sun, 5 Oct 2025 20:34:38 +0300 Subject: [PATCH 1/6] feat(cli): add the ability to specify a custom keybinding/symbols file via the cli Introduce `-k`,`--key-bindings`, `-s`, and `--key-symbols` arguments to allow the user from specifying a custom config files without having to change the global ones this change is meant to address github issue: #2714 --- src/args.rs | 28 ++++++++++++++++++++++++++++ src/keys/key_config.rs | 33 ++++++++++++++++++++++++++++----- src/main.rs | 2 +- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/args.rs b/src/args.rs index afc59c3754..06d66a055f 100644 --- a/src/args.rs +++ b/src/args.rs @@ -29,6 +29,8 @@ pub struct CliArgs { pub select_file: Option, pub repo_path: RepoPath, pub notify_watcher: bool, + pub key_bindings_path: Option, + pub key_symbols_path: Option, } pub fn process_cmdline() -> Result { @@ -80,11 +82,21 @@ pub fn process_cmdline() -> Result { let notify_watcher: bool = *arg_matches.get_one(WATCHER_FLAG_ID).unwrap_or(&false); + let key_bindings_path = arg_matches + .get_one::("key_bindings") + .map(PathBuf::from); + + let key_symbols_path = arg_matches + .get_one::("key_symbols") + .map(PathBuf::from); + Ok(CliArgs { theme, select_file, repo_path, notify_watcher, + key_bindings_path, + key_symbols_path, }) } @@ -103,6 +115,22 @@ fn app() -> ClapApp { {all-args}{after-help} ", + ) + .arg( + Arg::new("key_bindings") + .help("Use a custom keybindings file") + .short('k') + .long("key-bindings") + .value_name("KEY_LIST_FILENAME") + .num_args(1), + ) + .arg( + Arg::new("key_symbols") + .help("Use a custom symbols file") + .short('s') + .long("key-symblos") + .value_name("KEY_SYMBOLS_FILENAME") + .num_args(1), ) .arg( Arg::new(THEME_FLAG_ID) diff --git a/src/keys/key_config.rs b/src/keys/key_config.rs index 9cd4eb73f2..e34379e481 100644 --- a/src/keys/key_config.rs +++ b/src/keys/key_config.rs @@ -1,8 +1,11 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use crossterm::event::{KeyCode, KeyModifiers}; use std::{fs::canonicalize, path::PathBuf, rc::Rc}; -use crate::{args::get_app_config_path, strings::symbol}; +use crate::{ + args::{get_app_config_path, CliArgs}, + strings::symbol, +}; use super::{ key_list::{GituiKeyEvent, KeysList}, @@ -34,9 +37,29 @@ impl KeyConfig { .map_or_else(|_| Ok(symbols_file), Ok) } - pub fn init() -> Result { - let keys = KeysList::init(Self::get_config_file()?); - let symbols = KeySymbols::init(Self::get_symbols_file()?); + pub fn init(cli_args: &CliArgs) -> Result { + let keys = if let Some(path) = &cli_args.key_bindings_path { + if !path.exists() { + return Err(anyhow!( + "The custom key bindings file dosen't exists" + )); + } + KeysList::init(path.to_path_buf()) + } else { + KeysList::init(Self::get_config_file()?) + }; + + let symbols = if let Some(path) = &cli_args.key_symbols_path { + if !path.exists() { + return Err(anyhow!( + "The custom key symbols file dosen't exists" + )); + } + KeySymbols::init(path.to_path_buf()) + } else { + KeySymbols::init(Self::get_symbols_file()?) + }; + Ok(Self { keys, symbols }) } diff --git a/src/main.rs b/src/main.rs index f14b109508..741a558445 100644 --- a/src/main.rs +++ b/src/main.rs @@ -170,7 +170,7 @@ fn main() -> Result<()> { asyncgit::register_tracing_logging(); ensure_valid_path(&cliargs.repo_path)?; - let key_config = KeyConfig::init() + let key_config = KeyConfig::init(&cliargs) .map_err(|e| log_eprintln!("KeyConfig loading error: {e}")) .unwrap_or_default(); let theme = Theme::init(&cliargs.theme); From d6ddd9b3bc7b43df6cee39ca6df749f1aa5949b0 Mon Sep 17 00:00:00 2001 From: Anas Elgarhy Date: Sun, 5 Oct 2025 22:29:32 +0300 Subject: [PATCH 2/6] fix(tests): dont take &CliArgs --- src/keys/key_config.rs | 22 +++++++++++----------- src/main.rs | 9 ++++++--- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/keys/key_config.rs b/src/keys/key_config.rs index e34379e481..3a42863a34 100644 --- a/src/keys/key_config.rs +++ b/src/keys/key_config.rs @@ -2,10 +2,7 @@ use anyhow::{anyhow, Result}; use crossterm::event::{KeyCode, KeyModifiers}; use std::{fs::canonicalize, path::PathBuf, rc::Rc}; -use crate::{ - args::{get_app_config_path, CliArgs}, - strings::symbol, -}; +use crate::{args::get_app_config_path, strings::symbol}; use super::{ key_list::{GituiKeyEvent, KeysList}, @@ -37,8 +34,11 @@ impl KeyConfig { .map_or_else(|_| Ok(symbols_file), Ok) } - pub fn init(cli_args: &CliArgs) -> Result { - let keys = if let Some(path) = &cli_args.key_bindings_path { + pub fn init( + key_bindings_path: Option<&PathBuf>, + key_symbols_path: Option<&PathBuf>, + ) -> Result { + let keys = if let Some(path) = key_bindings_path { if !path.exists() { return Err(anyhow!( "The custom key bindings file dosen't exists" @@ -49,7 +49,7 @@ impl KeyConfig { KeysList::init(Self::get_config_file()?) }; - let symbols = if let Some(path) = &cli_args.key_symbols_path { + let symbols = if let Some(path) = key_symbols_path { if !path.exists() { return Err(anyhow!( "The custom key symbols file dosen't exists" @@ -208,7 +208,7 @@ mod tests { // testing let result = std::panic::catch_unwind(|| { - let loaded_config = KeyConfig::init().unwrap(); + let loaded_config = KeyConfig::init(None, None).unwrap(); assert_eq!( loaded_config.keys.move_down, KeysList::default().move_down @@ -223,7 +223,7 @@ mod tests { &original_key_symbols_path, ) .unwrap(); - let loaded_config = KeyConfig::init().unwrap(); + let loaded_config = KeyConfig::init(None, None).unwrap(); assert_eq!( loaded_config.keys.move_down, KeysList::default().move_down @@ -235,7 +235,7 @@ mod tests { &original_key_list_path, ) .unwrap(); - let loaded_config = KeyConfig::init().unwrap(); + let loaded_config = KeyConfig::init(None, None).unwrap(); assert_eq!( loaded_config.keys.move_down, GituiKeyEvent::new( @@ -246,7 +246,7 @@ mod tests { assert_eq!(loaded_config.symbols.esc, "Esc"); fs::remove_file(&original_key_symbols_path).unwrap(); - let loaded_config = KeyConfig::init().unwrap(); + let loaded_config = KeyConfig::init(None, None).unwrap(); assert_eq!( loaded_config.keys.move_down, GituiKeyEvent::new( diff --git a/src/main.rs b/src/main.rs index 741a558445..77770a7826 100644 --- a/src/main.rs +++ b/src/main.rs @@ -170,9 +170,12 @@ fn main() -> Result<()> { asyncgit::register_tracing_logging(); ensure_valid_path(&cliargs.repo_path)?; - let key_config = KeyConfig::init(&cliargs) - .map_err(|e| log_eprintln!("KeyConfig loading error: {e}")) - .unwrap_or_default(); + let key_config = KeyConfig::init( + cliargs.key_bindings_path.as_ref(), + cliargs.key_symbols_path.as_ref(), + ) + .map_err(|e| log_eprintln!("KeyConfig loading error: {e}")) + .unwrap_or_default(); let theme = Theme::init(&cliargs.theme); setup_terminal()?; From 9c5fbd546d91f835c6c799d77054d030ab03656b Mon Sep 17 00:00:00 2001 From: Anas Elgarhy Date: Sun, 5 Oct 2025 22:41:04 +0300 Subject: [PATCH 3/6] fix(args.rs): fix clippy errors --- src/keys/key_config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/keys/key_config.rs b/src/keys/key_config.rs index 3a42863a34..22d469353b 100644 --- a/src/keys/key_config.rs +++ b/src/keys/key_config.rs @@ -44,7 +44,7 @@ impl KeyConfig { "The custom key bindings file dosen't exists" )); } - KeysList::init(path.to_path_buf()) + KeysList::init(path.clone()) } else { KeysList::init(Self::get_config_file()?) }; @@ -55,7 +55,7 @@ impl KeyConfig { "The custom key symbols file dosen't exists" )); } - KeySymbols::init(path.to_path_buf()) + KeySymbols::init(path.clone()) } else { KeySymbols::init(Self::get_symbols_file()?) }; From ae6c033650a226d4c7e10946643afd832f8036b6 Mon Sep 17 00:00:00 2001 From: Anas Elgarhy Date: Sat, 29 Nov 2025 08:46:04 +0200 Subject: [PATCH 4/6] fix(args.rs): rebase master and fix typo --- src/args.rs | 12 +++++++----- src/main.rs | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/args.rs b/src/args.rs index 06d66a055f..22c6cc8d92 100644 --- a/src/args.rs +++ b/src/args.rs @@ -20,6 +20,8 @@ const WORKDIR_FLAG_ID: &str = "workdir"; const FILE_FLAG_ID: &str = "file"; const GIT_DIR_FLAG_ID: &str = "directory"; const WATCHER_FLAG_ID: &str = "watcher"; +const KEY_BINDINGS_FLAG_ID: &str = "key_bindings"; +const KEY_SYMBOLS_FLAG_ID: &str = "key_symbols"; const DEFAULT_THEME: &str = "theme.ron"; const DEFAULT_GIT_DIR: &str = "."; @@ -83,11 +85,11 @@ pub fn process_cmdline() -> Result { *arg_matches.get_one(WATCHER_FLAG_ID).unwrap_or(&false); let key_bindings_path = arg_matches - .get_one::("key_bindings") + .get_one::(KEY_BINDINGS_FLAG_ID) .map(PathBuf::from); let key_symbols_path = arg_matches - .get_one::("key_symbols") + .get_one::(KEY_SYMBOLS_FLAG_ID) .map(PathBuf::from); Ok(CliArgs { @@ -117,7 +119,7 @@ fn app() -> ClapApp { ", ) .arg( - Arg::new("key_bindings") + Arg::new(KEY_BINDINGS_FLAG_ID) .help("Use a custom keybindings file") .short('k') .long("key-bindings") @@ -125,10 +127,10 @@ fn app() -> ClapApp { .num_args(1), ) .arg( - Arg::new("key_symbols") + Arg::new(KEY_SYMBOLS_FLAG_ID) .help("Use a custom symbols file") .short('s') - .long("key-symblos") + .long("key-symbols") .value_name("KEY_SYMBOLS_FILENAME") .num_args(1), ) diff --git a/src/main.rs b/src/main.rs index 77770a7826..12fbb71ddd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -215,6 +215,8 @@ fn main() -> Result<()> { select_file: None, theme: args.theme, notify_watcher: args.notify_watcher, + key_bindings_path: args.key_bindings_path, + key_symbols_path: args.key_symbols_path, } } _ => break, From 6fd73296a6490e371814ae9b9c1b03e4dc713a9d Mon Sep 17 00:00:00 2001 From: Anas Elgarhy Date: Sat, 29 Nov 2025 09:14:02 +0200 Subject: [PATCH 5/6] refactor(key_config.rs): remove the redundant checks --- src/keys/key_config.rs | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/keys/key_config.rs b/src/keys/key_config.rs index 22d469353b..45a9ffdfb2 100644 --- a/src/keys/key_config.rs +++ b/src/keys/key_config.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use crossterm::event::{KeyCode, KeyModifiers}; use std::{fs::canonicalize, path::PathBuf, rc::Rc}; @@ -38,27 +38,16 @@ impl KeyConfig { key_bindings_path: Option<&PathBuf>, key_symbols_path: Option<&PathBuf>, ) -> Result { - let keys = if let Some(path) = key_bindings_path { - if !path.exists() { - return Err(anyhow!( - "The custom key bindings file dosen't exists" - )); - } - KeysList::init(path.clone()) - } else { - KeysList::init(Self::get_config_file()?) - }; - - let symbols = if let Some(path) = key_symbols_path { - if !path.exists() { - return Err(anyhow!( - "The custom key symbols file dosen't exists" - )); - } - KeySymbols::init(path.clone()) - } else { - KeySymbols::init(Self::get_symbols_file()?) - }; + let keys = KeysList::init( + key_bindings_path + .unwrap_or(&Self::get_config_file()?) + .clone(), + ); + let symbols = KeySymbols::init( + key_symbols_path + .unwrap_or(&Self::get_symbols_file()?) + .clone(), + ); Ok(Self { keys, symbols }) } From 6cf2bf53a35fcc0fdf66a65e53488bb9470e640c Mon Sep 17 00:00:00 2001 From: Anas Elgarhy Date: Mon, 1 Dec 2025 18:08:34 +0200 Subject: [PATCH 6/6] docs(changlog): add new feat entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1ef0d9864..ab50ac8a39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * add `use_selection_fg` to theme file to allow customizing selection foreground color [[@Upsylonbare](https://github.com/Upsylonbare)] ([#2515](https://github.com/gitui-org/gitui/pull/2515)) * add "go to line" command for the blame view [[@andrea-berling](https://github.com/andrea-berling)] ([#2262](https://github.com/extrawurst/gitui/pull/2262)) * add `--file` cli flag to open the files tab with the given file already selected [[@laktak](https://github.com/laktak)] ([#2510](https://github.com/gitui-org/gitui/issues/2510)) +* add the ability to specify a custom keybinding/symbols file via the cli [[@0x61nas](https://github.com/0x61nas)] ([#2731](https://github.com/gitui-org/gitui/pull/2731)) ### Changed * execute git-hooks directly if possible (on *nix) else use sh instead of bash (without reading SHELL variable) [[@Joshix](https://github.com/Joshix-1)] ([#2483](https://github.com/extrawurst/gitui/pull/2483))