From 783f2ddb1bd0bc2b854be06f79f4fe8f021df528 Mon Sep 17 00:00:00 2001 From: Stephan Dilly Date: Wed, 18 Mar 2020 14:58:09 +0100 Subject: [PATCH] first shot at commands and better seperation of logic --- Cargo.lock | 12 +++- Cargo.toml | 1 + src/app.rs | 132 ++++++++++++++++++-------------------------- src/commit.rs | 141 +++++++++++++++++++++++++++++++++++++++++++++++ src/git_utils.rs | 13 ++++- src/main.rs | 1 + 6 files changed, 220 insertions(+), 80 deletions(-) create mode 100644 src/commit.rs diff --git a/Cargo.lock b/Cargo.lock index 113401b7..f40f06ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,6 +141,7 @@ version = "0.1.0" dependencies = [ "crossterm 0.15.0", "git2", + "itertools 0.9.0", "tui", ] @@ -173,6 +174,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "jobserver" version = "0.1.21" @@ -420,7 +430,7 @@ dependencies = [ "cassowary", "crossterm 0.14.2", "either", - "itertools", + "itertools 0.8.2", "log", "unicode-segmentation", "unicode-width", diff --git a/Cargo.toml b/Cargo.toml index b7f0237c..6c1e92ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ edition = "2018" [dependencies] git2 = "0.10" crossterm = "0.15" +itertools = "0.9" tui = { version = "0.8", default-features = false, features = ['crossterm'] } \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index f5edc823..c9e8a7fc 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,10 +1,12 @@ +use crate::commit::CommandInfo; +use crate::commit::{UICommit, UIElement}; use crate::{ - clear::Clear, git_status::StatusLists, git_utils::{self, Diff, DiffLine, DiffLineType}, }; use crossterm::event::{Event, KeyCode, MouseEvent}; use git2::IndexAddOption; +use itertools::Itertools; use std::{borrow::Cow, cmp, path::Path}; use tui::{ backend::Backend, @@ -21,8 +23,7 @@ pub struct App { diff: Diff, offset: u16, do_quit: bool, - show_popup: bool, - commit_msg: String, + commit: UICommit, } impl App { @@ -146,54 +147,38 @@ impl App { .scroll(self.offset) .render(f, chunks[1]); - // commands - { - let splitter = Text::Styled(Cow::from(" "), Style::default().bg(Color::Black)); - let t1 = Text::Styled( - Cow::from("Commit [c]"), - Style::default() - .fg(if self.index_empty() { - Color::DarkGray - } else { - Color::White - }) - .bg(Color::Blue), - ); - let t2 = Text::Styled( - Cow::from("Help [h]"), - Style::default().fg(Color::White).bg(Color::Blue), - ); - let t3 = Text::Styled( - Cow::from("Quit [q]"), - Style::default().fg(Color::White).bg(Color::Blue), - ); - Paragraph::new(vec![t1, splitter.clone(), t2, splitter.clone(), t3].iter()) - .alignment(Alignment::Left) - .render(f, chunks_main[2]); - } + let mut cmds = self.commit.commands(); + cmds.extend(self.commands()); - if self.show_popup { - let txt = if self.commit_msg.len() > 0 { - [Text::Raw(Cow::from(self.commit_msg.clone()))] - } else { - [Text::Styled( - Cow::from("type commit message.."), - Style::default().fg(Color::DarkGray), - )] - }; + self.draw_commands(f, chunks_main[2], cmds); - Clear::new( - Paragraph::new(txt.iter()) - .block(Block::default().title("Commit").borders(Borders::ALL)) - .alignment(Alignment::Left), - ) - .render(f, git_utils::centered_rect(60, 20, f.size())); + self.commit.draw(f, f.size()); + } + + fn commands(&self) -> Vec { + if !self.commit.is_visible() { + vec![ + CommandInfo { + name: "Scroll [↑↓]".to_string(), + enabled: true, + }, + CommandInfo { + name: "Quit [esc,q]".to_string(), + enabled: true, + }, + ] + } else { + Vec::new() } } /// pub fn event(&mut self, ev: Event) { - if !self.show_popup { + if self.commit.event(ev) { + return; + } + + if !self.commit.is_visible() { if ev == Event::Key(KeyCode::Esc.into()) || ev == Event::Key(KeyCode::Char('q').into()) { self.do_quit = true; @@ -222,48 +207,39 @@ impl App { if ev == Event::Key(KeyCode::Enter.into()) { self.index_add(); } - - if ev == Event::Key(KeyCode::Char('c').into()) { - if !self.index_empty() { - self.show_popup = true; - } - } - } else { - if let Event::Key(e) = ev { - match e.code { - KeyCode::Char(c) => self.commit_msg.push(c), - KeyCode::Enter if self.commit_msg.len() > 0 => self.commit(), - KeyCode::Backspace if self.commit_msg.len() > 0 => { - self.commit_msg.pop().unwrap(); - () - } - _ => (), - }; - } - - if ev == Event::Key(KeyCode::Esc.into()) || ev == Event::Key(KeyCode::Char('q').into()) - { - self.show_popup = false; - } } } + fn draw_commands(&self, f: &mut Frame, r: Rect, cmds: Vec) { + let splitter = Text::Styled(Cow::from(" "), Style::default().bg(Color::Black)); + + let style_enabled = Style::default().fg(Color::White).bg(Color::Blue); + + let style_disabled = Style::default().fg(Color::DarkGray).bg(Color::Blue); + let texts = cmds + .iter() + .map(|c| { + Text::Styled( + Cow::from(c.name.clone()), + if c.enabled { + style_enabled + } else { + style_disabled + }, + ) + }) + .collect::>(); + + Paragraph::new(texts.iter().intersperse(&splitter)) + .alignment(Alignment::Left) + .render(f, r); + } + /// pub fn update(&mut self) { self.fetch_status(); } - fn index_empty(&self) -> bool { - self.status.index_items.len() == 0 - } - - fn commit(&mut self) { - git_utils::commit(&self.commit_msg); - - self.show_popup = false; - self.commit_msg.clear(); - } - fn index_add(&mut self) { if let Some(i) = self.status_select { let repo = git_utils::repo(); diff --git a/src/commit.rs b/src/commit.rs new file mode 100644 index 00000000..44616b7d --- /dev/null +++ b/src/commit.rs @@ -0,0 +1,141 @@ +use crate::{clear::Clear, git_utils}; +use crossterm::event::{Event, KeyCode}; +use std::borrow::Cow; +use tui::backend::Backend; +use tui::layout::{Alignment, Rect}; +use tui::{ + style::{Color, Style}, + widgets::{Block, Borders, Paragraph, Text, Widget}, + Frame, +}; + +/// +pub struct CommandInfo { + pub name: String, + pub enabled: bool, +} + +/// +pub trait UIElement { + /// + fn draw(&self, f: &mut Frame, rect: Rect); + /// + fn commands(&self) -> Vec; + /// + fn event(&mut self, ev: Event) -> bool; + /// + fn is_visible(&self) -> bool; + /// + fn hide(&mut self); + /// + fn show(&mut self); +} + +#[derive(Default)] +pub struct UICommit { + msg: String, + // focused: bool, + visible: bool, +} + +impl UIElement for UICommit { + fn draw(&self, f: &mut Frame, _rect: Rect) { + if self.visible { + let txt = if self.msg.len() > 0 { + [Text::Raw(Cow::from(self.msg.clone()))] + } else { + [Text::Styled( + Cow::from("type commit message.."), + Style::default().fg(Color::DarkGray), + )] + }; + + Clear::new( + Paragraph::new(txt.iter()) + .block(Block::default().title("Commit").borders(Borders::ALL)) + .alignment(Alignment::Left), + ) + .render(f, git_utils::centered_rect(60, 20, f.size())); + } + } + + fn commands(&self) -> Vec { + if !self.visible { + vec![CommandInfo { + name: "Commit [c]".to_string(), + enabled: !git_utils::index_empty(), + }] + } else { + vec![ + CommandInfo { + name: "Commit [enter]".to_string(), + enabled: self.can_commit(), + }, + CommandInfo { + name: "Close [esc]".to_string(), + enabled: true, + }, + ] + } + } + + fn event(&mut self, ev: Event) -> bool { + if self.visible { + if let Event::Key(e) = ev { + return match e.code { + KeyCode::Esc => { + self.hide(); + true + } + KeyCode::Char(c) => { + self.msg.push(c); + true + } + KeyCode::Enter if self.can_commit() => { + self.commit(); + true + } + KeyCode::Backspace if self.msg.len() > 0 => { + self.msg.pop().unwrap(); + true + } + _ => false, + }; + } + } else { + if ev == Event::Key(KeyCode::Char('c').into()) { + if !git_utils::index_empty() { + self.show(); + return true; + } + } + } + + false + } + + fn is_visible(&self) -> bool { + self.visible + } + + fn hide(&mut self) { + self.visible = false + } + + fn show(&mut self) { + self.visible = true + } +} + +impl UICommit { + fn commit(&mut self) { + git_utils::commit(&self.msg); + self.msg.clear(); + + self.hide(); + } + + fn can_commit(&self) -> bool { + self.msg.len() > 0 + } +} diff --git a/src/git_utils.rs b/src/git_utils.rs index 4e013356..d146f565 100644 --- a/src/git_utils.rs +++ b/src/git_utils.rs @@ -1,4 +1,4 @@ -use git2::{DiffFormat, DiffOptions, Repository}; +use git2::{DiffFormat, DiffOptions, Repository, StatusOptions, StatusShow}; use std::path::Path; use tui::layout::{Constraint, Direction, Layout, Rect}; @@ -132,3 +132,14 @@ pub fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { ) .split(popup_layout[1])[1] } + +/// +pub fn index_empty() -> bool { + let repo = repo(); + + let statuses = repo + .statuses(Some(StatusOptions::default().show(StatusShow::Index))) + .unwrap(); + + statuses.is_empty() +} diff --git a/src/main.rs b/src/main.rs index f7484f2a..dc572747 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod app; mod clear; +mod commit; mod git_status; mod git_utils; mod poll;