mirror of
https://github.com/extrawurst/gitui.git
synced 2024-11-27 00:14:52 +03:00
Add folder actions unstage/stage/reset
This commit is contained in:
parent
d22571c919
commit
1ca04496cb
@ -238,7 +238,7 @@ fn new_file_content(path: &Path) -> String {
|
||||
mod tests {
|
||||
use super::get_diff;
|
||||
use crate::sync::{
|
||||
stage_add,
|
||||
stage_add_file,
|
||||
status::{get_status, StatusType},
|
||||
tests::{repo_init, repo_init_empty},
|
||||
};
|
||||
@ -288,7 +288,7 @@ mod tests {
|
||||
.write_all(b"test\nfoo")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(stage_add(repo_path, file_path), true);
|
||||
assert_eq!(stage_add_file(repo_path, file_path), true);
|
||||
|
||||
let diff = get_diff(
|
||||
repo_path,
|
||||
@ -347,7 +347,7 @@ mod tests {
|
||||
assert_eq!(res.len(), 1);
|
||||
assert_eq!(res[0].path, "bar.txt");
|
||||
|
||||
let res = stage_add(repo_path, Path::new("bar.txt"));
|
||||
let res = stage_add_file(repo_path, Path::new("bar.txt"));
|
||||
assert_eq!(res, true);
|
||||
assert_eq!(get_status(repo_path, StatusType::Stage).len(), 1);
|
||||
assert_eq!(
|
||||
|
@ -9,8 +9,12 @@ pub mod utils;
|
||||
|
||||
pub use hooks::{hooks_commit_msg, hooks_post_commit, HookResult};
|
||||
pub use hunks::{stage_hunk, unstage_hunk};
|
||||
pub use reset::{reset_stage, reset_workdir};
|
||||
pub use utils::{commit, stage_add, stage_addremoved};
|
||||
pub use reset::{
|
||||
reset_stage, reset_workdir_file, reset_workdir_folder,
|
||||
};
|
||||
pub use utils::{
|
||||
commit, stage_add_all, stage_add_file, stage_addremoved,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -26,14 +26,14 @@ pub fn reset_stage(repo_path: &str, path: &Path) -> bool {
|
||||
}
|
||||
|
||||
///
|
||||
pub fn reset_workdir(repo_path: &str, path: &Path) -> bool {
|
||||
scope_time!("reset_workdir");
|
||||
pub fn reset_workdir_file(repo_path: &str, path: &str) -> bool {
|
||||
scope_time!("reset_workdir_file");
|
||||
|
||||
let repo = repo(repo_path);
|
||||
|
||||
// Note: early out for removing untracked files, due to bug in checkout_head code:
|
||||
// see https://github.com/libgit2/libgit2/issues/5089
|
||||
if let Ok(status) = repo.status_file(&path) {
|
||||
if let Ok(status) = repo.status_file(Path::new(path)) {
|
||||
let removed_file_wd = if status == Status::WT_NEW
|
||||
|| (status == Status::WT_MODIFIED | Status::INDEX_NEW)
|
||||
{
|
||||
@ -51,7 +51,7 @@ pub fn reset_workdir(repo_path: &str, path: &Path) -> bool {
|
||||
.update_index(true) // windows: needs this to be true WTF?!
|
||||
.allow_conflicts(true)
|
||||
.force()
|
||||
.path(&path);
|
||||
.path(path);
|
||||
|
||||
repo.checkout_index(None, Some(&mut checkout_opts)).is_ok()
|
||||
} else {
|
||||
@ -59,17 +59,36 @@ pub fn reset_workdir(repo_path: &str, path: &Path) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn reset_workdir_folder(repo_path: &str, path: &str) -> bool {
|
||||
scope_time!("reset_workdir_folder");
|
||||
|
||||
let repo = repo(repo_path);
|
||||
|
||||
let mut checkout_opts = CheckoutBuilder::new();
|
||||
checkout_opts
|
||||
.update_index(true) // windows: needs this to be true WTF?!
|
||||
.allow_conflicts(true)
|
||||
.remove_untracked(true)
|
||||
.force()
|
||||
.path(path);
|
||||
|
||||
repo.checkout_index(None, Some(&mut checkout_opts)).is_ok()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{reset_stage, reset_workdir};
|
||||
use super::{
|
||||
reset_stage, reset_workdir_file, reset_workdir_folder,
|
||||
};
|
||||
use crate::sync::{
|
||||
status::{get_status, StatusType},
|
||||
tests::{debug_cmd_print, repo_init, repo_init_empty},
|
||||
utils::stage_add,
|
||||
utils::{commit, stage_add_all, stage_add_file},
|
||||
};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::Write,
|
||||
io::{Error, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
@ -119,7 +138,7 @@ mod tests {
|
||||
|
||||
debug_cmd_print(repo_path, "git status");
|
||||
|
||||
stage_add(repo_path, Path::new("bar.txt"));
|
||||
stage_add_file(repo_path, Path::new("bar.txt"));
|
||||
|
||||
debug_cmd_print(repo_path, "git status");
|
||||
|
||||
@ -139,7 +158,7 @@ mod tests {
|
||||
1
|
||||
);
|
||||
|
||||
let res = reset_workdir(repo_path, Path::new("bar.txt"));
|
||||
let res = reset_workdir_file(repo_path, "bar.txt");
|
||||
assert_eq!(res, true);
|
||||
|
||||
debug_cmd_print(repo_path, "git status");
|
||||
@ -172,7 +191,7 @@ mod tests {
|
||||
1
|
||||
);
|
||||
|
||||
let res = reset_workdir(repo_path, Path::new("foo/bar.txt"));
|
||||
let res = reset_workdir_file(repo_path, "foo/bar.txt");
|
||||
assert_eq!(res, true);
|
||||
|
||||
debug_cmd_print(repo_path, "git status");
|
||||
@ -183,6 +202,56 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reset_folder() -> Result<(), Error> {
|
||||
let (_td, repo) = repo_init();
|
||||
let root = repo.path().parent().unwrap();
|
||||
let repo_path = root.as_os_str().to_str().unwrap();
|
||||
|
||||
{
|
||||
fs::create_dir(&root.join("foo"))?;
|
||||
File::create(&root.join("foo/file1.txt"))?
|
||||
.write_all(b"file1")?;
|
||||
File::create(&root.join("foo/file2.txt"))?
|
||||
.write_all(b"file1")?;
|
||||
File::create(&root.join("file3.txt"))?
|
||||
.write_all(b"file3")?;
|
||||
}
|
||||
|
||||
assert!(stage_add_all(repo_path, "*"));
|
||||
commit(repo_path, "msg");
|
||||
|
||||
{
|
||||
File::create(&root.join("foo/file1.txt"))?
|
||||
.write_all(b"file1\nadded line")?;
|
||||
fs::remove_file(&root.join("foo/file2.txt"))?;
|
||||
File::create(&root.join("foo/file4.txt"))?
|
||||
.write_all(b"file4")?;
|
||||
File::create(&root.join("foo/file5.txt"))?
|
||||
.write_all(b"file5")?;
|
||||
File::create(&root.join("file3.txt"))?
|
||||
.write_all(b"file3\nadded line")?;
|
||||
}
|
||||
|
||||
stage_add_file(repo_path, Path::new("foo/file5.txt"));
|
||||
|
||||
assert_eq!(
|
||||
get_status(repo_path, StatusType::WorkingDir).len(),
|
||||
4
|
||||
);
|
||||
assert_eq!(get_status(repo_path, StatusType::Stage).len(), 1);
|
||||
|
||||
assert!(reset_workdir_folder(repo_path, "foo"));
|
||||
|
||||
assert_eq!(
|
||||
get_status(repo_path, StatusType::WorkingDir).len(),
|
||||
1
|
||||
);
|
||||
assert_eq!(get_status(repo_path, StatusType::Stage).len(), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reset_untracked_in_subdir_and_index() {
|
||||
let (_td, repo) = repo_init();
|
||||
@ -219,7 +288,7 @@ mod tests {
|
||||
1
|
||||
);
|
||||
|
||||
let res = reset_workdir(repo_path, Path::new(file));
|
||||
let res = reset_workdir_file(repo_path, file);
|
||||
assert_eq!(res, true);
|
||||
|
||||
debug_cmd_print(repo_path, "git status");
|
||||
@ -243,7 +312,7 @@ mod tests {
|
||||
.write_all(b"test\nfoo")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(stage_add(repo_path, file_path), true);
|
||||
assert_eq!(stage_add_file(repo_path, file_path), true);
|
||||
|
||||
assert_eq!(reset_stage(repo_path, file_path), true);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! sync git api (various methods)
|
||||
|
||||
use git2::{Repository, RepositoryOpenFlags};
|
||||
use git2::{IndexAddOption, Repository, RepositoryOpenFlags};
|
||||
use scopetime::scope_time;
|
||||
use std::path::Path;
|
||||
|
||||
@ -63,8 +63,8 @@ pub fn commit(repo_path: &str, msg: &str) {
|
||||
}
|
||||
|
||||
/// add a file diff from workingdir to stage (will not add removed files see `stage_addremoved`)
|
||||
pub fn stage_add(repo_path: &str, path: &Path) -> bool {
|
||||
scope_time!("stage_add");
|
||||
pub fn stage_add_file(repo_path: &str, path: &Path) -> bool {
|
||||
scope_time!("stage_add_file");
|
||||
|
||||
let repo = repo(repo_path);
|
||||
|
||||
@ -78,6 +78,25 @@ pub fn stage_add(repo_path: &str, path: &Path) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// like `stage_add_file` but uses a pattern to match/glob multiple files/folders
|
||||
pub fn stage_add_all(repo_path: &str, pattern: &str) -> bool {
|
||||
scope_time!("stage_add_all");
|
||||
|
||||
let repo = repo(repo_path);
|
||||
|
||||
let mut index = repo.index().unwrap();
|
||||
|
||||
if index
|
||||
.add_all(vec![pattern], IndexAddOption::DEFAULT, None)
|
||||
.is_ok()
|
||||
{
|
||||
index.write().unwrap();
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// stage a removed file
|
||||
pub fn stage_addremoved(repo_path: &str, path: &Path) -> bool {
|
||||
scope_time!("stage_addremoved");
|
||||
@ -98,13 +117,12 @@ pub fn stage_addremoved(repo_path: &str, path: &Path) -> bool {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::sync::{
|
||||
stage_add,
|
||||
status::{get_status, StatusType},
|
||||
tests::{repo_init, repo_init_empty},
|
||||
};
|
||||
use std::{
|
||||
fs::{remove_file, File},
|
||||
io::Write,
|
||||
fs::{self, remove_file, File},
|
||||
io::{Error, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
@ -126,7 +144,7 @@ mod tests {
|
||||
|
||||
assert_eq!(status_count(StatusType::WorkingDir), 1);
|
||||
|
||||
assert_eq!(stage_add(repo_path, file_path), true);
|
||||
assert_eq!(stage_add_file(repo_path, file_path), true);
|
||||
|
||||
assert_eq!(status_count(StatusType::WorkingDir), 0);
|
||||
assert_eq!(status_count(StatusType::Stage), 1);
|
||||
@ -149,7 +167,7 @@ mod tests {
|
||||
.write_all(b"test\nfoo")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(stage_add(repo_path, file_path), true);
|
||||
assert_eq!(stage_add_file(repo_path, file_path), true);
|
||||
|
||||
commit(repo_path, "commit msg");
|
||||
}
|
||||
@ -161,7 +179,7 @@ mod tests {
|
||||
let root = repo.path().parent().unwrap();
|
||||
let repo_path = root.as_os_str().to_str().unwrap();
|
||||
|
||||
assert_eq!(stage_add(repo_path, file_path), false);
|
||||
assert_eq!(stage_add_file(repo_path, file_path), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -187,12 +205,40 @@ mod tests {
|
||||
|
||||
assert_eq!(status_count(StatusType::WorkingDir), 2);
|
||||
|
||||
assert_eq!(stage_add(repo_path, file_path), true);
|
||||
assert_eq!(stage_add_file(repo_path, file_path), true);
|
||||
|
||||
assert_eq!(status_count(StatusType::WorkingDir), 1);
|
||||
assert_eq!(status_count(StatusType::Stage), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_staging_folder() -> Result<(), Error> {
|
||||
let (_td, repo) = repo_init();
|
||||
let root = repo.path().parent().unwrap();
|
||||
let repo_path = root.as_os_str().to_str().unwrap();
|
||||
|
||||
let status_count = |s: StatusType| -> usize {
|
||||
get_status(repo_path, s).len()
|
||||
};
|
||||
|
||||
fs::create_dir_all(&root.join("a/d"))?;
|
||||
File::create(&root.join(Path::new("a/d/f1.txt")))?
|
||||
.write_all(b"foo")?;
|
||||
File::create(&root.join(Path::new("a/d/f2.txt")))?
|
||||
.write_all(b"foo")?;
|
||||
File::create(&root.join(Path::new("a/f3.txt")))?
|
||||
.write_all(b"foo")?;
|
||||
|
||||
assert_eq!(status_count(StatusType::WorkingDir), 3);
|
||||
|
||||
assert_eq!(stage_add_all(repo_path, "a/d"), true);
|
||||
|
||||
assert_eq!(status_count(StatusType::WorkingDir), 1);
|
||||
assert_eq!(status_count(StatusType::Stage), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_staging_deleted_file() {
|
||||
let file_path = Path::new("file1.txt");
|
||||
@ -211,7 +257,7 @@ mod tests {
|
||||
.write_all(b"test file1 content")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(stage_add(repo_path, file_path), true);
|
||||
assert_eq!(stage_add_file(repo_path, file_path), true);
|
||||
|
||||
commit(repo_path, "commit msg");
|
||||
|
||||
|
32
src/app.rs
32
src/app.rs
@ -17,7 +17,7 @@ use crossbeam_channel::Sender;
|
||||
use crossterm::event::Event;
|
||||
use itertools::Itertools;
|
||||
use log::trace;
|
||||
use std::{borrow::Cow, path::Path};
|
||||
use std::borrow::Cow;
|
||||
use strings::commands;
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
@ -299,7 +299,7 @@ impl App {
|
||||
loop {
|
||||
let front = self.queue.borrow_mut().pop_front();
|
||||
if let Some(e) = front {
|
||||
flags.insert(self.process_internal_event(&e));
|
||||
flags.insert(self.process_internal_event(e));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -311,35 +311,45 @@ impl App {
|
||||
|
||||
fn process_internal_event(
|
||||
&mut self,
|
||||
ev: &InternalEvent,
|
||||
ev: InternalEvent,
|
||||
) -> NeedsUpdate {
|
||||
let mut flags = NeedsUpdate::empty();
|
||||
match ev {
|
||||
InternalEvent::ResetFile(p) => {
|
||||
if sync::reset_workdir(CWD, Path::new(p.as_str())) {
|
||||
InternalEvent::ResetItem(reset_item) => {
|
||||
if reset_item.is_folder {
|
||||
if sync::reset_workdir_folder(
|
||||
CWD,
|
||||
reset_item.path.as_str(),
|
||||
) {
|
||||
flags.insert(NeedsUpdate::ALL);
|
||||
}
|
||||
} else if sync::reset_workdir_file(
|
||||
CWD,
|
||||
reset_item.path.as_str(),
|
||||
) {
|
||||
flags.insert(NeedsUpdate::ALL);
|
||||
}
|
||||
}
|
||||
InternalEvent::ConfirmResetFile(p) => {
|
||||
self.reset.open_for_path(p);
|
||||
InternalEvent::ConfirmResetItem(reset_item) => {
|
||||
self.reset.open_for_path(reset_item);
|
||||
flags.insert(NeedsUpdate::COMMANDS);
|
||||
}
|
||||
InternalEvent::AddHunk(hash) => {
|
||||
if let Some((path, is_stage)) = self.selected_path() {
|
||||
if is_stage {
|
||||
if sync::unstage_hunk(CWD, path, *hash) {
|
||||
if sync::unstage_hunk(CWD, path, hash) {
|
||||
flags.insert(NeedsUpdate::ALL);
|
||||
}
|
||||
} else if sync::stage_hunk(CWD, path, *hash) {
|
||||
} else if sync::stage_hunk(CWD, path, hash) {
|
||||
flags.insert(NeedsUpdate::ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
InternalEvent::ShowMsg(msg) => {
|
||||
self.msg.show_msg(msg);
|
||||
self.msg.show_msg(msg.as_str());
|
||||
flags.insert(NeedsUpdate::ALL);
|
||||
}
|
||||
InternalEvent::Update(u) => flags.insert(*u),
|
||||
InternalEvent::Update(u) => flags.insert(u),
|
||||
};
|
||||
|
||||
flags
|
||||
|
@ -6,12 +6,11 @@ use super::{
|
||||
use crate::{
|
||||
components::{CommandInfo, Component},
|
||||
keys,
|
||||
queue::{InternalEvent, NeedsUpdate, Queue},
|
||||
queue::{InternalEvent, NeedsUpdate, Queue, ResetItem},
|
||||
strings, ui,
|
||||
};
|
||||
use asyncgit::{hash, sync, StatusItem, StatusItemType, CWD};
|
||||
use crossterm::event::Event;
|
||||
use log::trace;
|
||||
use std::{borrow::Cow, convert::From, path::Path};
|
||||
use strings::commands;
|
||||
use tui::{
|
||||
@ -103,25 +102,28 @@ impl ChangesComponent {
|
||||
|
||||
fn index_add_remove(&mut self) -> bool {
|
||||
if let Some(tree_item) = self.selection() {
|
||||
if let FileTreeItemKind::File(i) = tree_item.kind {
|
||||
if self.is_working_dir {
|
||||
if let FileTreeItemKind::File(i) = tree_item.kind {
|
||||
if let Some(status) = i.status {
|
||||
let path = Path::new(i.path.as_str());
|
||||
return match status {
|
||||
StatusItemType::Deleted => {
|
||||
sync::stage_addremoved(CWD, path)
|
||||
}
|
||||
_ => sync::stage_add(CWD, path),
|
||||
_ => sync::stage_add_file(CWD, path),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
let path = Path::new(i.path.as_str());
|
||||
|
||||
return sync::reset_stage(CWD, path);
|
||||
//TODO: check if we can handle the one file case with it aswell
|
||||
return sync::stage_add_all(
|
||||
CWD,
|
||||
tree_item.info.full_path.as_str(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
//TODO:
|
||||
trace!("tbd");
|
||||
let path =
|
||||
Path::new(tree_item.info.full_path.as_str());
|
||||
return sync::reset_stage(CWD, path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,16 +132,16 @@ impl ChangesComponent {
|
||||
|
||||
fn dispatch_reset_workdir(&mut self) -> bool {
|
||||
if let Some(tree_item) = self.selection() {
|
||||
if let FileTreeItemKind::File(i) = tree_item.kind {
|
||||
let is_folder =
|
||||
matches!(tree_item.kind, FileTreeItemKind::Path(_));
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::ConfirmResetFile(i.path),
|
||||
InternalEvent::ConfirmResetItem(ResetItem {
|
||||
path: tree_item.info.full_path,
|
||||
is_folder,
|
||||
}),
|
||||
);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
//TODO:
|
||||
trace!("tbd");
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@ -282,22 +284,22 @@ impl Component for ChangesComponent {
|
||||
out: &mut Vec<CommandInfo>,
|
||||
_force_all: bool,
|
||||
) -> CommandBlocking {
|
||||
let some_selection =
|
||||
self.selection().is_some() && self.is_file_seleted();
|
||||
let some_selection = self.selection().is_some();
|
||||
|
||||
if self.is_working_dir {
|
||||
out.push(CommandInfo::new(
|
||||
commands::STAGE_FILE,
|
||||
commands::STAGE_ITEM,
|
||||
some_selection,
|
||||
self.focused,
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::RESET_FILE,
|
||||
commands::RESET_ITEM,
|
||||
some_selection,
|
||||
self.focused,
|
||||
));
|
||||
} else {
|
||||
out.push(CommandInfo::new(
|
||||
commands::UNSTAGE_FILE,
|
||||
commands::UNSTAGE_ITEM,
|
||||
some_selection,
|
||||
self.focused,
|
||||
));
|
||||
|
@ -151,17 +151,37 @@ impl FileTreeItems {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
fn push_dirs(
|
||||
item_path: &Path,
|
||||
///
|
||||
pub(crate) fn find_parent_index(
|
||||
&self,
|
||||
path: &str,
|
||||
index: usize,
|
||||
) -> usize {
|
||||
if let Some(parent_path) = Path::new(path).parent() {
|
||||
let parent_path = parent_path.to_str().unwrap();
|
||||
for i in (0..=index).rev() {
|
||||
let item = &self.0[i];
|
||||
let item_path = &item.info.full_path;
|
||||
if item_path == parent_path {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn push_dirs<'a>(
|
||||
item_path: &'a Path,
|
||||
nodes: &mut BinaryHeap<FileTreeItem>,
|
||||
paths_added: &mut BTreeSet<String>, //TODO: use a ref string here
|
||||
paths_added: &mut BTreeSet<&'a Path>,
|
||||
collapsed: &BTreeSet<&String>,
|
||||
) {
|
||||
for c in item_path.ancestors().skip(1) {
|
||||
if c.parent().is_some() {
|
||||
let path_string = String::from(c.to_str().unwrap());
|
||||
if !paths_added.contains(&path_string) {
|
||||
paths_added.insert(path_string.clone());
|
||||
if !paths_added.contains(c) {
|
||||
paths_added.insert(c);
|
||||
let is_collapsed =
|
||||
collapsed.contains(&path_string);
|
||||
nodes.push(FileTreeItem::new_path(
|
||||
@ -292,4 +312,25 @@ mod tests {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_parent() {
|
||||
//0 a/
|
||||
//1 b/
|
||||
//2 c
|
||||
//3 d
|
||||
|
||||
let res = FileTreeItems::new(
|
||||
&string_vec_to_status(&[
|
||||
"a/b/c", //
|
||||
"a/b/d", //
|
||||
]),
|
||||
&BTreeSet::new(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
res.find_parent_index(&String::from("a/b/c"), 3),
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use super::{
|
||||
DrawableComponent,
|
||||
};
|
||||
use crate::{
|
||||
queue::{InternalEvent, Queue},
|
||||
queue::{InternalEvent, Queue, ResetItem},
|
||||
strings, ui,
|
||||
};
|
||||
|
||||
@ -20,7 +20,7 @@ use tui::{
|
||||
|
||||
///
|
||||
pub struct ResetComponent {
|
||||
path: String,
|
||||
target: Option<ResetItem>,
|
||||
visible: bool,
|
||||
queue: Queue,
|
||||
}
|
||||
@ -107,21 +107,24 @@ impl ResetComponent {
|
||||
///
|
||||
pub fn new(queue: Queue) -> Self {
|
||||
Self {
|
||||
path: String::default(),
|
||||
target: None,
|
||||
visible: false,
|
||||
queue,
|
||||
}
|
||||
}
|
||||
///
|
||||
pub fn open_for_path(&mut self, path: &str) {
|
||||
self.path = path.to_string();
|
||||
pub fn open_for_path(&mut self, item: ResetItem) {
|
||||
self.target = Some(item);
|
||||
self.show();
|
||||
}
|
||||
///
|
||||
pub fn confirm(&mut self) {
|
||||
self.hide();
|
||||
if let Some(target) = self.target.take() {
|
||||
self.queue
|
||||
.borrow_mut()
|
||||
.push_back(InternalEvent::ResetFile(self.path.clone()));
|
||||
.push_back(InternalEvent::ResetItem(target));
|
||||
}
|
||||
|
||||
self.hide();
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use super::filetree::{
|
||||
FileTreeItem, FileTreeItemKind, FileTreeItems, PathCollapsed,
|
||||
};
|
||||
use asyncgit::StatusItem;
|
||||
use std::{cmp, collections::BTreeSet, path::Path};
|
||||
use std::{cmp, collections::BTreeSet};
|
||||
|
||||
///
|
||||
#[derive(Default)]
|
||||
@ -193,7 +193,8 @@ impl StatusTree {
|
||||
if collapsed)
|
||||
{
|
||||
SelectionChange::new(
|
||||
self.find_parent_index(&item_path, current_selection),
|
||||
self.tree
|
||||
.find_parent_index(&item_path, current_selection),
|
||||
false,
|
||||
)
|
||||
} else if matches!(item_kind, FileTreeItemKind::Path(PathCollapsed(collapsed))
|
||||
@ -290,27 +291,6 @@ impl StatusTree {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_parent_index(
|
||||
&self,
|
||||
path: &str,
|
||||
current_index: usize,
|
||||
) -> usize {
|
||||
let path = Path::new(path);
|
||||
|
||||
if let Some(path) = path.parent() {
|
||||
for i in (0..=current_index).rev() {
|
||||
let item = self.tree.items().get(i).unwrap();
|
||||
let item_path = &item.info.full_path;
|
||||
//TODO: use parameter path here
|
||||
if item_path.ends_with(path.to_str().unwrap()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -353,27 +333,6 @@ mod tests {
|
||||
assert_eq!(res.selection, Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_parent() {
|
||||
let items = string_vec_to_status(&[
|
||||
"a/b/c", //
|
||||
"a/b/d", //
|
||||
]);
|
||||
|
||||
//0 a/
|
||||
//1 b/
|
||||
//2 c
|
||||
//3 d
|
||||
|
||||
let mut res = StatusTree::default();
|
||||
res.update(&items);
|
||||
|
||||
assert_eq!(
|
||||
res.find_parent_index(&String::from("a/b/c"), 3),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keep_selected_item() {
|
||||
let mut res = StatusTree::default();
|
||||
|
12
src/queue.rs
12
src/queue.rs
@ -13,12 +13,20 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
/// data of item that is supposed to be reset
|
||||
pub struct ResetItem {
|
||||
/// path to the item (folder/file)
|
||||
pub path: String,
|
||||
/// are talking about a folder here? otherwise it's a single file
|
||||
pub is_folder: bool,
|
||||
}
|
||||
|
||||
///
|
||||
pub enum InternalEvent {
|
||||
///
|
||||
ConfirmResetFile(String),
|
||||
ConfirmResetItem(ResetItem),
|
||||
///
|
||||
ResetFile(String),
|
||||
ResetItem(ResetItem),
|
||||
///
|
||||
AddHunk(u64),
|
||||
///
|
||||
|
@ -79,21 +79,21 @@ pub mod commands {
|
||||
CMD_GROUP_COMMIT,
|
||||
);
|
||||
///
|
||||
pub static STAGE_FILE: CommandText = CommandText::new(
|
||||
"Stage File [enter]",
|
||||
"stage currently selected file",
|
||||
pub static STAGE_ITEM: CommandText = CommandText::new(
|
||||
"Stage Item [enter]",
|
||||
"stage currently selected file or entire path",
|
||||
CMD_GROUP_CHANGES,
|
||||
);
|
||||
///
|
||||
pub static UNSTAGE_FILE: CommandText = CommandText::new(
|
||||
"Unstage File [enter]",
|
||||
"remove currently selected file from stage",
|
||||
pub static UNSTAGE_ITEM: CommandText = CommandText::new(
|
||||
"Unstage Item [enter]",
|
||||
"unstage currently selected file or entire path",
|
||||
CMD_GROUP_CHANGES,
|
||||
);
|
||||
///
|
||||
pub static RESET_FILE: CommandText = CommandText::new(
|
||||
"Reset File [D]",
|
||||
"revert changes in selected file",
|
||||
pub static RESET_ITEM: CommandText = CommandText::new(
|
||||
"Reset Item [D]",
|
||||
"revert changes in selected file or entire path",
|
||||
CMD_GROUP_CHANGES,
|
||||
);
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user