Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiline commit message #114

Merged
merged 1 commit into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::input::InputState;
use crate::{
accessors,
cmdbar::CommandBar,
Expand All @@ -18,6 +19,7 @@ use anyhow::{anyhow, Result};
use asyncgit::{sync, AsyncNotification, CWD};
use crossbeam_channel::Sender;
use crossterm::event::{Event, KeyEvent};
use std::cell::Cell;
use std::{cell::RefCell, rc::Rc};
use strings::{commands, order};
use tui::{
Expand All @@ -28,6 +30,7 @@ use tui::{
widgets::{Block, Borders, Tabs},
Frame,
};

///
pub struct App {
do_quit: bool,
Expand All @@ -45,6 +48,10 @@ pub struct App {
stashlist_tab: StashList,
queue: Queue,
theme: SharedTheme,

// "Flags"
requires_redraw: Cell<bool>,
set_polling: bool,
}

// public interface
Expand Down Expand Up @@ -85,6 +92,8 @@ impl App {
stashlist_tab: StashList::new(&queue, theme.clone()),
queue,
theme,
requires_redraw: Cell::new(false),
set_polling: true,
}
}

Expand Down Expand Up @@ -182,6 +191,17 @@ impl App {
if flags.contains(NeedsUpdate::COMMANDS) {
self.update_commands();
}
} else if let InputEvent::State(polling_state) = ev {
if let InputState::Paused = polling_state {
if let Err(e) = self.commit.show_editor() {
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.set_polling = true;
}
}

Ok(())
Expand Down Expand Up @@ -230,6 +250,21 @@ impl App {
|| self.stashing_tab.anything_pending()
|| self.inspect_commit_popup.any_work_pending()
}

///
pub fn requires_redraw(&self) -> bool {
if self.requires_redraw.get() {
self.requires_redraw.set(false);
true
} else {
false
}
}

///
pub const fn set_polling(&self) -> bool {
self.set_polling
}
}

// private impls
Expand Down Expand Up @@ -314,6 +349,7 @@ impl App {

fn process_queue(&mut self) -> Result<NeedsUpdate> {
let mut flags = NeedsUpdate::empty();

loop {
let front = self.queue.borrow_mut().pop_front();
if let Some(e) = front {
Expand Down Expand Up @@ -369,6 +405,9 @@ impl App {
self.inspect_commit_popup.open(id)?;
flags.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS)
}
InternalEvent::SuspendPolling => {
self.set_polling = false;
}
};

Ok(flags)
Expand Down
14 changes: 14 additions & 0 deletions src/components/changes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ impl Component for ChangesComponent {
some_selection,
self.focused(),
));
out.push(CommandInfo::new(
commands::COMMIT_OPEN_EDITOR,
!self.is_empty(),
self.focused() || force_all,
));
out.push(
CommandInfo::new(
commands::COMMIT_OPEN,
Expand Down Expand Up @@ -266,6 +271,15 @@ impl Component for ChangesComponent {
.push_back(InternalEvent::OpenCommit);
Ok(true)
}
keys::OPEN_COMMIT_EDITOR
if !self.is_working_dir
&& !self.is_empty() =>
{
self.queue
.borrow_mut()
.push_back(InternalEvent::SuspendPolling);
Ok(true)
}
keys::STATUS_STAGE_FILE => {
try_or_popup!(
self,
Expand Down
69 changes: 66 additions & 3 deletions src/components/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,24 @@ use super::{
textinput::TextInputComponent, visibility_blocking,
CommandBlocking, CommandInfo, Component, DrawableComponent,
};
use crate::strings::COMMIT_EDITOR_MSG;
use crate::{
keys,
get_app_config_path, keys,
queue::{InternalEvent, NeedsUpdate, Queue},
strings,
ui::style::SharedTheme,
};
use anyhow::Result;
use anyhow::{anyhow, Result};
use asyncgit::{
sync::{self, CommitId},
CWD,
};
use crossterm::event::Event;
use std::env;
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;
use std::process::Command;
use strings::commands;
use sync::HookResult;
use tui::{backend::Backend, layout::Rect, Frame};
Expand Down Expand Up @@ -121,8 +127,65 @@ impl CommitComponent {
}
}

pub fn show_editor(&mut self) -> Result<()> {
const COMMIT_MSG_FILE_NAME: &str = "COMMITMSG_EDITOR";
let mut config_path: PathBuf = get_app_config_path()?;
config_path.push(COMMIT_MSG_FILE_NAME);

let mut file = File::create(&config_path)?;
file.write_all(COMMIT_EDITOR_MSG.as_bytes())?;
drop(file);

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")
})?;

Command::new(command)
.args(editor)
.status()
.map_err(|e| anyhow!("\"{}\": {}", command, e))?;

let mut message = String::new();

let mut file = File::open(&config_path)?;
file.read_to_string(&mut message)?;
drop(file);
std::fs::remove_file(&config_path)?;

let message: String = message
.lines()
.flat_map(|l| {
if l.starts_with('#') {
vec![]
} else {
vec![l, "\n"]
}
})
.collect();

if !message.chars().all(char::is_whitespace) {
return self.commit_msg(message);
}

Ok(())
}

fn commit(&mut self) -> Result<()> {
let mut msg = self.input.get_text().clone();
self.commit_msg(self.input.get_text().clone())
}

fn commit_msg(&mut self, msg: String) -> Result<()> {
let mut msg = msg;
if let HookResult::NotOk(e) =
sync::hooks_commit_msg(CWD, &mut msg)?
{
Expand Down
2 changes: 2 additions & 0 deletions src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub const EXIT: KeyEvent =
pub const EXIT_POPUP: KeyEvent = no_mod(KeyCode::Esc);
pub const CLOSE_MSG: KeyEvent = no_mod(KeyCode::Enter);
pub const OPEN_COMMIT: KeyEvent = no_mod(KeyCode::Char('c'));
pub const OPEN_COMMIT_EDITOR: KeyEvent =
with_mod(KeyCode::Char('C'), KeyModifiers::SHIFT);
pub const OPEN_HELP: KeyEvent = no_mod(KeyCode::Char('h'));
pub const MOVE_LEFT: KeyEvent = no_mod(KeyCode::Left);
pub const MOVE_RIGHT: KeyEvent = no_mod(KeyCode::Right);
Expand Down
7 changes: 5 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,7 @@ fn main() -> Result<()> {
}
}

//TODO: disable input polling while external editor open
input.set_polling(!app.any_work_pending());
input.set_polling(app.set_polling());

if needs_draw {
draw(&mut terminal, &app)?;
Expand Down Expand Up @@ -164,6 +163,10 @@ fn draw<B: Backend>(
terminal: &mut Terminal<B>,
app: &App,
) -> io::Result<()> {
if app.requires_redraw() {
terminal.resize(terminal.size()?)?;
}

terminal.draw(|mut f| {
if let Err(e) = app.draw(&mut f) {
log::error!("failed to draw: {:?}", e)
Expand Down
2 changes: 2 additions & 0 deletions src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub enum InternalEvent {
TabSwitch,
///
InspectCommit(CommitId),
///
SuspendPolling,
}

///
Expand Down
10 changes: 10 additions & 0 deletions src/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub static MSG_TITLE_ERROR: &str = "Error";
pub static COMMIT_TITLE: &str = "Commit";
pub static COMMIT_TITLE_AMEND: &str = "Commit (Amend)";
pub static COMMIT_MSG: &str = "type commit message..";
pub static COMMIT_EDITOR_MSG: &str = r##"
# Enter your commit message
# Lines starting with '#' will be ignored
# Empty commit message will abort the commit"##;
pub static STASH_POPUP_TITLE: &str = "Stash";
pub static STASH_POPUP_MSG: &str = "type name (optional)";
pub static CONFIRM_TITLE_RESET: &str = "Reset";
Expand Down Expand Up @@ -150,6 +154,12 @@ pub mod commands {
CMD_GROUP_COMMIT,
);
///
pub static COMMIT_OPEN_EDITOR: CommandText = CommandText::new(
"Commit editor [C]",
"open commit editor (available in non-empty stage)",
CMD_GROUP_COMMIT,
);
///
pub static COMMIT_ENTER: CommandText = CommandText::new(
"Commit [enter]",
"commit (available when commit message is non-empty)",
Expand Down