mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
can select/diff stages items
This commit is contained in:
parent
7409512dd8
commit
034c2533af
6 changed files with 150 additions and 113 deletions
118
src/app.rs
118
src/app.rs
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ fn main() -> Result<()> {
|
|||
|
||||
terminal.clear()?;
|
||||
|
||||
let mut app = App::default();
|
||||
let mut app = App::new();
|
||||
|
||||
loop {
|
||||
app.update();
|
||||
|
|
|
|||
Loading…
Reference in a new issue