diff --git a/src/app.rs b/src/app.rs index e64cc2dd31..052e71f77b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -18,6 +18,7 @@ use anyhow::{anyhow, Result}; use asyncgit::{sync, AsyncNotification, CWD}; use crossbeam_channel::Sender; use crossterm::event::{Event, KeyEvent}; +use std::path::PathBuf; use std::{cell::Cell, cell::RefCell, rc::Rc}; use tui::{ backend::Backend, @@ -48,6 +49,7 @@ pub struct App { // "Flags" requires_redraw: Cell, + file_to_open: Option>, } // public interface @@ -96,6 +98,7 @@ impl App { queue, theme, requires_redraw: Cell::new(false), + file_to_open: None, } } @@ -196,12 +199,18 @@ impl App { } else if let InputEvent::State(polling_state) = ev { self.external_editor_popup.hide(); if let InputState::Paused = polling_state { - if let Err(e) = self.commit.show_editor() { + let result = match self.file_to_open.take() { + Some(path) => crate::open_file_in_editor(&path), + None => self.commit.show_editor(), + }; + + if let Err(e) = result { let msg = format!("failed to launch editor:\n{}", e); log::error!("{}", msg.as_str()); self.msg.show_msg(msg.as_str())?; } + self.requires_redraw.set(true); self.input.set_polling(true); } @@ -408,9 +417,10 @@ impl App { self.inspect_commit_popup.open(id)?; flags.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS) } - InternalEvent::SuspendPolling => { + InternalEvent::OpenExternalEditor(path) => { self.input.set_polling(false); self.external_editor_popup.show()?; + self.file_to_open = path; flags.insert(NeedsUpdate::COMMANDS) } }; diff --git a/src/components/commit.rs b/src/components/commit.rs index 7430a2eca1..3bc6edb634 100644 --- a/src/components/commit.rs +++ b/src/components/commit.rs @@ -8,7 +8,7 @@ use crate::{ strings::{self, commands}, ui::style::SharedTheme, }; -use anyhow::{anyhow, Result}; +use anyhow::Result; use asyncgit::{ sync::{self, CommitId, HookResult}, CWD, @@ -20,11 +20,9 @@ use crossterm::{ }; use scopeguard::defer; use std::{ - env, fs::File, io::{self, Read, Write}, path::PathBuf, - process::Command, }; use tui::{backend::Backend, layout::Rect, Frame}; @@ -94,9 +92,9 @@ impl Component for CommitComponent { } keys::OPEN_COMMIT_EDITOR => { - self.queue - .borrow_mut() - .push_back(InternalEvent::SuspendPolling); + self.queue.borrow_mut().push_back( + InternalEvent::OpenExternalEditor(None), + ); self.hide(); } @@ -159,29 +157,12 @@ impl CommitComponent { file.write_all(strings::COMMIT_EDITOR_MSG.as_bytes())?; } - let mut editor = env::var("GIT_EDTIOR") - .ok() - .or_else(|| env::var("VISUAL").ok()) - .or_else(|| env::var("EDITOR").ok()) - .unwrap_or_else(|| String::from("vi")); - editor - .push_str(&format!(" {}", config_path.to_string_lossy())); - - let mut editor = editor.split_whitespace(); - - let command = editor.next().ok_or_else(|| { - anyhow!("unable to read editor command") - })?; - io::stdout().execute(LeaveAlternateScreen)?; defer! { io::stdout().execute(EnterAlternateScreen).expect("reset terminal"); } - Command::new(command) - .args(editor) - .status() - .map_err(|e| anyhow!("\"{}\": {}", command, e))?; + crate::open_file_in_editor(&config_path)?; let mut message = String::new(); diff --git a/src/keys.rs b/src/keys.rs index 119bcb7639..dda890ad73 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -48,6 +48,7 @@ pub const SHIFT_UP: KeyEvent = pub const SHIFT_DOWN: KeyEvent = with_mod(KeyCode::Down, KeyModifiers::SHIFT); pub const ENTER: KeyEvent = no_mod(KeyCode::Enter); +pub const EDIT_FILE: KeyEvent = no_mod(KeyCode::Char('e')); pub const STATUS_STAGE_FILE: KeyEvent = no_mod(KeyCode::Enter); pub const STATUS_STAGE_ALL: KeyEvent = no_mod(KeyCode::Char('a')); pub const STATUS_RESET_FILE: KeyEvent = diff --git a/src/main.rs b/src/main.rs index 9c331c74c4..2c0e00d913 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,12 +45,13 @@ use scopeguard::defer; use scopetime::scope_time; use simplelog::{Config, LevelFilter, WriteLogger}; use spinner::Spinner; +use std::process::Command; use std::{ env, fs, fs::File, io::{self, Write}, panic, - path::PathBuf, + path::{Path, PathBuf}, process, time::{Duration, Instant}, }; @@ -243,6 +244,28 @@ fn migrate_config() -> Result<()> { Ok(()) } +fn open_file_in_editor(path: &Path) -> Result<()> { + let mut editor = env::var("GIT_EDITOR") + .ok() + .or_else(|| env::var("VISUAL").ok()) + .or_else(|| env::var("EDITOR").ok()) + .unwrap_or_else(|| String::from("vi")); + editor.push_str(&format!(" {}", path.to_string_lossy())); + + let mut editor = editor.split_whitespace(); + + let command = editor + .next() + .ok_or_else(|| anyhow!("unable to read editor command"))?; + + Command::new(command) + .args(editor) + .status() + .map_err(|e| anyhow!("\"{}\": {}", command, e))?; + + Ok(()) +} + fn get_app_cache_path() -> Result { let mut path = dirs::cache_dir() .ok_or_else(|| anyhow!("failed to find os cache dir."))?; diff --git a/src/queue.rs b/src/queue.rs index c2a79a1e0a..a4dc921add 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,6 +1,7 @@ use crate::tabs::StashingOptions; use asyncgit::sync::CommitId; use bitflags::bitflags; +use std::path::PathBuf; use std::{cell::RefCell, collections::VecDeque, rc::Rc}; bitflags! { @@ -49,9 +50,7 @@ pub enum InternalEvent { /// InspectCommit(CommitId), /// - //TODO: make this a generic OpenExternalEditor to also use it for other places - //(see https://github.com/extrawurst/gitui/issues/166) - SuspendPolling, + OpenExternalEditor(Option>), } /// diff --git a/src/strings.rs b/src/strings.rs index 03f7e7ddf1..cd4fbd91b5 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -172,6 +172,12 @@ pub mod commands { CMD_GROUP_COMMIT, ); /// + pub static EDIT_ITEM: CommandText = CommandText::new( + "Edit Item [e]", + "edit the currently selected file in an external editor", + CMD_GROUP_CHANGES, + ); + /// pub static STAGE_ITEM: CommandText = CommandText::new( "Stage Item [enter]", "stage currently selected file or entire path", diff --git a/src/tabs/status.rs b/src/tabs/status.rs index a0459515eb..bd123be250 100644 --- a/src/tabs/status.rs +++ b/src/tabs/status.rs @@ -18,6 +18,7 @@ use asyncgit::{ }; use crossbeam_channel::Sender; use crossterm::event::Event; +use std::path::PathBuf; use tui::layout::{Constraint, Direction, Layout}; /// @@ -312,6 +313,15 @@ impl Component for Status { { let focus_on_diff = self.focus == Focus::Diff; + out.push(CommandInfo::new( + commands::EDIT_ITEM, + if focus_on_diff { + true + } else { + self.can_focus_diff() + }, + self.visible || force_all, + )); out.push(CommandInfo::new( commands::DIFF_FOCUS_LEFT, true, @@ -371,6 +381,22 @@ impl Component for Status { keys::FOCUS_STAGE => { self.switch_focus(Focus::Stage) } + keys::EDIT_FILE + if self.can_focus_diff() + || self.focus == Focus::Diff => + { + if let Some((path, _)) = self.selected_path() + { + self.queue.borrow_mut().push_back( + InternalEvent::OpenExternalEditor( + Some(Box::new(PathBuf::from( + path, + ))), + ), + ); + } + Ok(true) + } keys::FOCUS_RIGHT if self.can_focus_diff() => { self.switch_focus(Focus::Diff) }