gitui/src/components/changes.rs
Stephan Dilly 7d1e3643b1
support push to origin (#266)
* use  vendored ssl
2020-09-02 02:08:41 +02:00

323 lines
9 KiB
Rust

use super::{
filetree::FileTreeComponent,
utils::filetree::{FileTreeItem, FileTreeItemKind},
CommandBlocking, DrawableComponent,
};
use crate::{
components::{CommandInfo, Component},
keys::SharedKeyConfig,
queue::{Action, InternalEvent, NeedsUpdate, Queue, ResetItem},
strings, try_or_popup,
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{cached, sync, StatusItem, StatusItemType, CWD};
use crossterm::event::Event;
use std::path::Path;
use tui::{backend::Backend, layout::Rect, Frame};
///
pub struct ChangesComponent {
title: String,
files: FileTreeComponent,
is_working_dir: bool,
queue: Queue,
branch_name: cached::BranchName,
key_config: SharedKeyConfig,
}
impl ChangesComponent {
///
pub fn new(
title: &str,
focus: bool,
is_working_dir: bool,
queue: Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
) -> Self {
Self {
title: title.into(),
files: FileTreeComponent::new(
title,
focus,
Some(queue.clone()),
theme,
key_config.clone(),
),
is_working_dir,
queue,
branch_name: cached::BranchName::new(CWD),
key_config,
}
}
pub fn update(&mut self) -> Result<()> {
if self.is_working_dir {
if let Ok(branch_name) = self.branch_name.lookup() {
self.files.set_title(format!(
"{} - {{{}}}",
&self.title, branch_name,
))
}
}
Ok(())
}
///
pub fn branch_name(&self) -> Option<String> {
self.branch_name.last()
}
///
pub fn set_items(&mut self, list: &[StatusItem]) -> Result<()> {
self.files.update(list)?;
Ok(())
}
///
pub fn selection(&self) -> Option<FileTreeItem> {
self.files.selection()
}
///
pub fn focus_select(&mut self, focus: bool) {
self.files.focus(focus);
self.files.show_selection(focus);
}
/// returns true if list is empty
pub fn is_empty(&self) -> bool {
self.files.is_empty()
}
///
pub fn is_file_seleted(&self) -> bool {
self.files.is_file_seleted()
}
fn index_add_remove(&mut self) -> Result<bool> {
if let Some(tree_item) = self.selection() {
if self.is_working_dir {
if let FileTreeItemKind::File(i) = tree_item.kind {
let path = Path::new(i.path.as_str());
match i.status {
StatusItemType::Deleted => {
sync::stage_addremoved(CWD, path)?
}
_ => sync::stage_add_file(CWD, path)?,
};
return Ok(true);
} else {
//TODO: check if we can handle the one file case with it aswell
sync::stage_add_all(
CWD,
tree_item.info.full_path.as_str(),
)?;
return Ok(true);
}
} else {
let path = tree_item.info.full_path.as_str();
sync::reset_stage(CWD, path)?;
return Ok(true);
}
}
Ok(false)
}
fn index_add_all(&mut self) -> Result<()> {
sync::stage_add_all(CWD, "*")?;
self.queue
.borrow_mut()
.push_back(InternalEvent::Update(NeedsUpdate::ALL));
Ok(())
}
fn stage_remove_all(&mut self) -> Result<()> {
sync::reset_stage(CWD, "*")?;
self.queue
.borrow_mut()
.push_back(InternalEvent::Update(NeedsUpdate::ALL));
Ok(())
}
fn dispatch_reset_workdir(&mut self) -> bool {
if let Some(tree_item) = self.selection() {
let is_folder =
matches!(tree_item.kind, FileTreeItemKind::Path(_));
self.queue.borrow_mut().push_back(
InternalEvent::ConfirmAction(Action::Reset(
ResetItem {
path: tree_item.info.full_path,
is_folder,
},
)),
);
return true;
}
false
}
fn add_to_ignore(&mut self) -> bool {
if let Some(tree_item) = self.selection() {
if let Err(e) =
sync::add_to_ignore(CWD, &tree_item.info.full_path)
{
self.queue.borrow_mut().push_back(
InternalEvent::ShowErrorMsg(format!(
"ignore error:\n{}\nfile:\n{:?}",
e, tree_item.info.full_path
)),
);
} else {
self.queue.borrow_mut().push_back(
InternalEvent::Update(NeedsUpdate::ALL),
);
return true;
}
}
false
}
}
impl DrawableComponent for ChangesComponent {
fn draw<B: Backend>(
&self,
f: &mut Frame<B>,
r: Rect,
) -> Result<()> {
self.files.draw(f, r)?;
Ok(())
}
}
impl Component for ChangesComponent {
fn commands(
&self,
out: &mut Vec<CommandInfo>,
force_all: bool,
) -> CommandBlocking {
self.files.commands(out, force_all);
let some_selection = self.selection().is_some();
if self.is_working_dir {
out.push(CommandInfo::new(
strings::commands::stage_all(&self.key_config),
some_selection,
self.focused(),
));
out.push(CommandInfo::new(
strings::commands::stage_item(&self.key_config),
some_selection,
self.focused(),
));
out.push(CommandInfo::new(
strings::commands::reset_item(&self.key_config),
some_selection,
self.focused(),
));
out.push(CommandInfo::new(
strings::commands::ignore_item(&self.key_config),
some_selection,
self.focused(),
));
} else {
out.push(CommandInfo::new(
strings::commands::unstage_item(&self.key_config),
some_selection,
self.focused(),
));
out.push(CommandInfo::new(
strings::commands::unstage_all(&self.key_config),
some_selection,
self.focused(),
));
out.push(
CommandInfo::new(
strings::commands::commit_open(&self.key_config),
!self.is_empty(),
self.focused() || force_all,
)
.order(-1),
);
}
CommandBlocking::PassingOn
}
fn event(&mut self, ev: Event) -> Result<bool> {
if self.files.event(ev)? {
return Ok(true);
}
if self.focused() {
if let Event::Key(e) = ev {
return if e == self.key_config.open_commit
&& !self.is_working_dir
&& !self.is_empty()
{
self.queue
.borrow_mut()
.push_back(InternalEvent::OpenCommit);
Ok(true)
} else if e == self.key_config.enter {
try_or_popup!(
self,
"staging error:",
self.index_add_remove()
);
self.queue.borrow_mut().push_back(
InternalEvent::Update(NeedsUpdate::ALL),
);
Ok(true)
} else if e == self.key_config.status_stage_all
&& !self.is_empty()
{
if self.is_working_dir {
try_or_popup!(
self,
"staging error:",
self.index_add_all()
);
} else {
self.stage_remove_all()?;
}
Ok(true)
} else if e == self.key_config.status_reset_item
&& self.is_working_dir
{
Ok(self.dispatch_reset_workdir())
} else if e == self.key_config.status_ignore_file
&& self.is_working_dir
&& !self.is_empty()
{
Ok(self.add_to_ignore())
} else {
Ok(false)
};
}
}
Ok(false)
}
fn focused(&self) -> bool {
self.files.focused()
}
fn focus(&mut self, focus: bool) {
self.files.focus(focus)
}
}