can select/diff stages items

This commit is contained in:
Stephan Dilly 2020-03-18 16:56:25 +01:00
parent 7409512dd8
commit 034c2533af
6 changed files with 150 additions and 113 deletions

View file

@ -2,12 +2,10 @@ use crate::{
components::{
CommandInfo, CommitComponent, Component, IndexComponent,
},
git_status::StatusLists,
git_utils::{self, Diff, DiffLine, DiffLineType},
tui_utils,
};
use crossterm::event::{Event, KeyCode, MouseEvent};
use git2::IndexAddOption;
use git2::{IndexAddOption, StatusShow};
use itertools::Itertools;
use std::{borrow::Cow, path::Path};
use tui::{
@ -18,18 +16,35 @@ use tui::{
Frame,
};
#[derive(Default)]
pub struct App {
status: StatusLists,
diff: Diff,
offset: u16,
do_quit: bool,
commit: CommitComponent,
index: IndexComponent,
// index_wt: IndexComponent,
index_wd: IndexComponent,
}
impl App {
///
pub fn new() -> Self {
Self {
diff: Diff::default(),
offset: 0,
do_quit: false,
commit: CommitComponent::default(),
index_wd: IndexComponent::new(
"Status [s]",
StatusShow::Workdir,
true,
),
index: IndexComponent::new(
"Index [i]",
StatusShow::Index,
false,
),
}
}
///
pub fn is_quit(&self) -> bool {
self.do_quit
@ -37,21 +52,19 @@ impl App {
}
impl App {
///
fn fetch_status(&mut self) {
let new_status = StatusLists::new();
if self.status != new_status {
self.status = new_status;
}
}
///
fn update_diff(&mut self) {
let new_diff = match self.index.selection() {
Some(i) => {
git_utils::get_diff(Path::new(i.path.as_str()))
}
let (idx, is_stage) = if self.index.focused() {
(&self.index, true)
} else {
(&self.index_wd, false)
};
let new_diff = match idx.selection() {
Some(i) => git_utils::get_diff(
Path::new(i.path.as_str()),
is_stage,
),
None => Diff::default(),
};
@ -105,16 +118,8 @@ impl App {
)
.split(chunks[0]);
self.index.draw(f, left_chunks[0]);
tui_utils::draw_list(
f,
left_chunks[1],
"Index [i]".to_string(),
self.status.index_items_pathlist().as_slice(),
None,
false,
);
self.index_wd.draw(f, left_chunks[0]);
self.index.draw(f, left_chunks[1]);
let txt = self
.diff
@ -159,6 +164,7 @@ impl App {
let mut cmds = self.commit.commands();
cmds.extend(self.index.commands());
cmds.extend(self.index_wd.commands());
cmds.extend(self.commands());
self.draw_commands(f, chunks_main[2], cmds);
@ -186,6 +192,9 @@ impl App {
if self.index.event(ev) {
return;
}
if self.index_wd.event(ev) {
return;
}
if !self.commit.is_visible() {
if ev == Event::Key(KeyCode::Esc.into())
@ -194,6 +203,14 @@ impl App {
self.do_quit = true;
}
if ev == Event::Key(KeyCode::Char('s').into()) {
self.index_wd.focus(true);
self.index.focus(false);
} else if ev == Event::Key(KeyCode::Char('i').into()) {
self.index.focus(true);
self.index_wd.focus(false);
}
if ev == Event::Key(KeyCode::PageDown.into()) {
self.scroll(true);
}
@ -251,35 +268,38 @@ impl App {
///
pub fn update(&mut self) {
self.fetch_status();
self.index.update();
self.index_wd.update();
self.update_diff();
}
fn index_add(&mut self) {
if let Some(i) = self.index.selection() {
let repo = git_utils::repo();
if self.index_wd.focused() {
if let Some(i) = self.index_wd.selection() {
let repo = git_utils::repo();
let mut index = repo.index().unwrap();
let mut index = repo.index().unwrap();
let path = Path::new(i.path.as_str());
let path = Path::new(i.path.as_str());
let cb = &mut |p: &Path, _matched_spec: &[u8]| -> i32 {
if p == path {
0
} else {
1
let cb =
&mut |p: &Path, _matched_spec: &[u8]| -> i32 {
if p == path {
0
} else {
1
}
};
if let Ok(_) = index.add_all(
path,
IndexAddOption::DISABLE_PATHSPEC_MATCH
| IndexAddOption::CHECK_PATHSPEC,
Some(cb as &mut git2::IndexMatchedPath),
) {
index.write().unwrap();
self.update();
}
};
if let Ok(_) = index.add_all(
path,
IndexAddOption::DISABLE_PATHSPEC_MATCH
| IndexAddOption::CHECK_PATHSPEC,
Some(cb as &mut git2::IndexMatchedPath),
) {
index.write().unwrap();
self.update();
}
}
}

View file

@ -9,24 +9,30 @@ use git2::StatusShow;
use std::cmp;
use tui::{backend::Backend, layout::Rect, Frame};
///
pub struct IndexComponent {
title: String,
items: Vec<StatusItem>,
index_type: StatusShow,
selection: Option<usize>,
}
impl Default for IndexComponent {
///
fn default() -> Self {
Self {
items: Vec::new(),
index_type: StatusShow::Workdir,
selection: None,
}
}
focused: bool,
}
impl IndexComponent {
///
pub fn new(
title: &str,
index_type: StatusShow,
focus: bool,
) -> Self {
Self {
title: title.to_string(),
items: Vec::new(),
index_type,
selection: None,
focused: focus,
}
}
///
pub fn update(&mut self) {
let new_status = git_status::get_index(self.index_type);
@ -67,45 +73,60 @@ impl Component for IndexComponent {
tui_utils::draw_list(
f,
r,
"Status [s]".to_string(),
self.title.to_string(),
self.items
.iter()
.map(|e| e.path.clone())
.collect::<Vec<_>>()
.as_slice(),
self.selection,
true,
if self.focused { self.selection } else { None },
self.focused,
);
}
fn commands(&self) -> Vec<CommandInfo> {
vec![
CommandInfo {
name: "Scroll [↑↓]".to_string(),
enabled: self.items.len() > 0,
},
CommandInfo {
name: "Stage File [enter]".to_string(),
enabled: self.selection.is_some(),
},
]
if self.focused {
return vec![
CommandInfo {
name: "Scroll [↑↓]".to_string(),
enabled: self.items.len() > 0,
},
CommandInfo {
name: "Stage File [enter]".to_string(),
enabled: self.selection.is_some(),
},
];
}
Vec::new()
}
fn event(&mut self, ev: Event) -> bool {
if let Event::Key(e) = ev {
return match e.code {
KeyCode::Down => {
self.move_selection(1);
true
}
KeyCode::Up => {
self.move_selection(-1);
true
}
_ => false,
};
if self.focused {
if let Event::Key(e) = ev {
return match e.code {
KeyCode::Down => {
self.move_selection(1);
true
}
KeyCode::Up => {
self.move_selection(-1);
true
}
_ => false,
};
}
}
false
}
///
fn focused(&self) -> bool {
self.focused
}
///
fn focus(&mut self, focus: bool) {
self.focused = focus
}
}

View file

@ -21,6 +21,12 @@ pub trait Component {
///
fn event(&mut self, ev: Event) -> bool;
///
fn focused(&self) -> bool {
false
}
///
fn focus(&mut self, _focus: bool) {}
///
fn is_visible(&self) -> bool {
true
}

View file

@ -32,29 +32,6 @@ pub struct StatusItem {
pub status: Option<StatusItemType>,
}
#[derive(Default, PartialEq)]
pub struct StatusLists {
pub wt_items: Vec<StatusItem>,
pub index_items: Vec<StatusItem>,
}
impl StatusLists {
///
pub fn new() -> Self {
let mut res = Self::default();
res.wt_items = get_index(StatusShow::Workdir);
res.index_items = get_index(StatusShow::Index);
res
}
///
pub fn index_items_pathlist(&self) -> Vec<String> {
self.index_items.iter().map(|e| e.path.clone()).collect()
}
}
pub fn get_index(show: StatusShow) -> Vec<StatusItem> {
let repo = git_utils::repo();

View file

@ -30,14 +30,28 @@ pub struct DiffLine {
pub struct Diff(pub Vec<DiffLine>);
///
pub fn get_diff(p: &Path) -> Diff {
pub fn get_diff(p: &Path, stage: bool) -> Diff {
let repo = repo();
let mut opt = DiffOptions::new();
opt.pathspec(p);
let diff =
repo.diff_index_to_workdir(None, Some(&mut opt)).unwrap();
let diff = if !stage {
// diff against stage
repo.diff_index_to_workdir(None, Some(&mut opt)).unwrap()
} else {
// diff against head
let ref_head = repo.head().unwrap();
let parent =
repo.find_commit(ref_head.target().unwrap()).unwrap();
let tree = parent.tree().unwrap();
repo.diff_tree_to_index(
Some(&tree),
Some(&repo.index().unwrap()),
Some(&mut opt),
)
.unwrap()
};
let mut res = Vec::new();
@ -93,7 +107,6 @@ pub fn commit(msg: &String) {
let reference = repo.head().unwrap();
let mut index = repo.index().unwrap();
let tree_id = index.write_tree().unwrap();
let tree = repo.find_tree(tree_id).unwrap();
let parent =
repo.find_commit(reference.target().unwrap()).unwrap();

View file

@ -31,7 +31,7 @@ fn main() -> Result<()> {
terminal.clear()?;
let mut app = App::default();
let mut app = App::new();
loop {
app.update();