Skip to content

Commit

Permalink
Add command to edit selected file in editor
Browse files Browse the repository at this point in the history
Pressing `e` while looking at a file in the _Status_ view will launch an external editor with the current file opened. The editor chosen is determined by the default logic introduced in extrawurst#114.

An improvement to this in the future could be launching at the specific line at which the _Diff_ view is focused, but that seems to require a change in `FileDiff` which is a change bigger than this PR.

Fixes extrawurst#166
  • Loading branch information
jonstodle committed Jul 6, 2020
1 parent 4351b89 commit ee6b381
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 30 deletions.
14 changes: 12 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -48,6 +49,7 @@ pub struct App {

// "Flags"
requires_redraw: Cell<bool>,
file_to_open: Option<Box<PathBuf>>,
}

// public interface
Expand Down Expand Up @@ -96,6 +98,7 @@ impl App {
queue,
theme,
requires_redraw: Cell::new(false),
file_to_open: None,
}
}

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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)
}
};
Expand Down
29 changes: 5 additions & 24 deletions src/components/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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};

Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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();

Expand Down
1 change: 1 addition & 0 deletions src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
25 changes: 24 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -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<PathBuf> {
let mut path = dirs::cache_dir()
.ok_or_else(|| anyhow!("failed to find os cache dir."))?;
Expand Down
5 changes: 2 additions & 3 deletions src/queue.rs
Original file line number Diff line number Diff line change
@@ -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! {
Expand Down Expand Up @@ -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<Box<PathBuf>>),
}

///
Expand Down
6 changes: 6 additions & 0 deletions src/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
26 changes: 26 additions & 0 deletions src/tabs/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use asyncgit::{
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
use std::path::PathBuf;
use tui::layout::{Constraint, Direction, Layout};

///
Expand Down Expand Up @@ -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 {
self.can_focus_diff()
} else {
true
},
self.visible || force_all,
));
out.push(CommandInfo::new(
commands::DIFF_FOCUS_LEFT,
true,
Expand Down Expand Up @@ -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)
}
Expand Down

0 comments on commit ee6b381

Please sign in to comment.