mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 01:18:21 +00:00
Add command to edit selected file in editor
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 #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 #166
This commit is contained in:
parent
b9ec27d89c
commit
52f31adb98
7 changed files with 76 additions and 30 deletions
14
src/app.rs
14
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<bool>,
|
||||
file_to_open: Option<Box<PathBuf>>,
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
25
src/main.rs
25
src/main.rs
|
|
@ -43,12 +43,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},
|
||||
};
|
||||
|
|
@ -241,6 +242,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."))?;
|
||||
|
|
|
|||
|
|
@ -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<Box<PathBuf>>),
|
||||
}
|
||||
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue