diff --git a/asyncgit/src/sync/utils.rs b/asyncgit/src/sync/utils.rs index ecb45813..f1ecc4ff 100644 --- a/asyncgit/src/sync/utils.rs +++ b/asyncgit/src/sync/utils.rs @@ -144,6 +144,29 @@ pub fn stage_addremoved(repo_path: &str, path: &Path) -> Result<()> { Ok(()) } +/// get string from config +pub fn get_config_string( + repo_path: &str, + key: &str, +) -> Result> { + let repo = repo(repo_path)?; + let cfg = repo.config()?; + + // this code doesnt match what the doc says regarding what + // gets returned when but it actually works + let entry_res = cfg.get_entry(key); + + let entry = match entry_res { + Ok(ent) => ent, + Err(_) => return Ok(None), + }; + + if !entry.has_value() { + Ok(None) + } else { + Ok(entry.value().map(|s| s.to_string())) + } +} /// helper function pub(crate) fn bytes2string(bytes: &[u8]) -> Result { Ok(String::from_utf8(bytes.to_vec())?) @@ -177,7 +200,23 @@ mod tests { false ); } + #[test] + fn test_get_config() { + let bad_dir_cfg = + get_config_string("oodly_noodly", "this.doesnt.exist"); + assert!(bad_dir_cfg.is_err()); + let (_td, repo) = repo_init().unwrap(); + let path = repo.path(); + let rpath = path.as_os_str().to_str().unwrap(); + let bad_cfg = get_config_string(rpath, "this.doesnt.exist"); + assert!(bad_cfg.is_ok()); + assert!(bad_cfg.unwrap().is_none()); + // repo init sets user.name + let good_cfg = get_config_string(rpath, "user.name"); + assert!(good_cfg.is_ok()); + assert!(good_cfg.unwrap().is_some()); + } #[test] fn test_staging_one_file() { let file_path = Path::new("file1.txt"); diff --git a/src/components/externaleditor.rs b/src/components/externaleditor.rs index e11c5817..b20b2f78 100644 --- a/src/components/externaleditor.rs +++ b/src/components/externaleditor.rs @@ -8,7 +8,9 @@ use crate::{ ui::{self, style::SharedTheme}, }; use anyhow::{anyhow, bail, Result}; -use asyncgit::{sync::utils::repo_work_dir, CWD}; +use asyncgit::{ + sync::utils::get_config_string, sync::utils::repo_work_dir, CWD, +}; use crossterm::{ event::Event, terminal::{EnterAlternateScreen, LeaveAlternateScreen}, @@ -66,26 +68,43 @@ impl ExternalEditorComponent { let editor = env::var("GIT_EDITOR") .ok() + .or_else(|| get_config_string(CWD, "core.editor").ok()?) .or_else(|| env::var("VISUAL").ok()) .or_else(|| env::var("EDITOR").ok()) .unwrap_or_else(|| String::from("vi")); // TODO: proper handling arguments containing whitespaces // This does not do the right thing if the input is `editor --something "with spaces"` - let mut editor = editor.split_whitespace(); - let command = editor.next().ok_or_else(|| { - anyhow!("unable to read editor command") - })?; + // deal with "editor name with spaces" p1 p2 p3 + // and with "editor_no_spaces" p1 p2 p3 + // does not address spaces in pn + let mut echars = editor.chars().peekable(); - let mut editor: Vec<&OsStr> = - editor.map(|s| OsStr::new(s)).collect(); + let command: String = if *echars.peek().ok_or_else(|| { + anyhow!("editor configuration set to empty string") + })? == '\"' + { + echars + .by_ref() + .skip(1) + .take_while(|c| *c != '\"') + .collect() + } else { + echars.by_ref().take_while(|c| *c != ' ').collect() + }; - editor.push(path.as_os_str()); + let remainder_str = echars.collect::(); + let remainder = remainder_str.split_whitespace(); - Command::new(command) + let mut args: Vec<&OsStr> = + remainder.map(|s| OsStr::new(s)).collect(); + + args.push(path.as_os_str()); + + Command::new(command.clone()) .current_dir(work_dir) - .args(editor) + .args(args) .status() .map_err(|e| anyhow!("\"{}\": {}", command, e))?;