mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
show file size diff for binary files (#141)
This commit is contained in:
parent
f287d6a351
commit
76e52cc7a2
4 changed files with 309 additions and 182 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -101,6 +101,12 @@ dependencies = [
|
||||||
"constant_time_eq",
|
"constant_time_eq",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytesize"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cassowary"
|
name = "cassowary"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
|
@ -323,6 +329,7 @@ dependencies = [
|
||||||
"asyncgit",
|
"asyncgit",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
"bytesize",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ asyncgit = { path = "./asyncgit", version = "0.7" }
|
||||||
crossterm = "0.17"
|
crossterm = "0.17"
|
||||||
clap = { version = "2.33", default-features = false }
|
clap = { version = "2.33", default-features = false }
|
||||||
tui = { version = "0.9", default-features = false, features = ['crossterm'] }
|
tui = { version = "0.9", default-features = false, features = ['crossterm'] }
|
||||||
|
bytesize = { version = "1.0.1", default-features = false}
|
||||||
itertools = "0.9"
|
itertools = "0.9"
|
||||||
rayon-core = "1.7"
|
rayon-core = "1.7"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use git2::{
|
||||||
Repository,
|
Repository,
|
||||||
};
|
};
|
||||||
use scopetime::scope_time;
|
use scopetime::scope_time;
|
||||||
use std::{fs, path::Path};
|
use std::{cell::RefCell, fs, path::Path, rc::Rc};
|
||||||
use utils::{get_head_repo, work_dir};
|
use utils::{get_head_repo, work_dir};
|
||||||
|
|
||||||
/// type of diff of a single line
|
/// type of diff of a single line
|
||||||
|
|
@ -75,6 +75,10 @@ pub struct FileDiff {
|
||||||
pub lines: usize,
|
pub lines: usize,
|
||||||
///
|
///
|
||||||
pub untracked: bool,
|
pub untracked: bool,
|
||||||
|
/// old and new file size in bytes
|
||||||
|
pub sizes: (u64, u64),
|
||||||
|
/// size delta in bytes
|
||||||
|
pub size_delta: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_diff_raw<'a>(
|
pub(crate) fn get_diff_raw<'a>(
|
||||||
|
|
@ -152,110 +156,128 @@ fn raw_diff_to_file_diff<'a>(
|
||||||
diff: &'a Diff,
|
diff: &'a Diff,
|
||||||
work_dir: &Path,
|
work_dir: &Path,
|
||||||
) -> Result<FileDiff> {
|
) -> Result<FileDiff> {
|
||||||
let mut res: FileDiff = FileDiff::default();
|
let res = Rc::new(RefCell::new(FileDiff::default()));
|
||||||
let mut current_lines = Vec::new();
|
{
|
||||||
let mut current_hunk: Option<HunkHeader> = None;
|
let mut current_lines = Vec::new();
|
||||||
|
let mut current_hunk: Option<HunkHeader> = None;
|
||||||
|
|
||||||
let mut adder = |header: &HunkHeader, lines: &Vec<DiffLine>| {
|
let res_cell = Rc::clone(&res);
|
||||||
res.hunks.push(Hunk {
|
let adder = move |header: &HunkHeader,
|
||||||
header_hash: hash(header),
|
lines: &Vec<DiffLine>| {
|
||||||
lines: lines.clone(),
|
let mut res = res_cell.borrow_mut();
|
||||||
});
|
res.hunks.push(Hunk {
|
||||||
res.lines += lines.len();
|
header_hash: hash(header),
|
||||||
};
|
lines: lines.clone(),
|
||||||
|
});
|
||||||
|
res.lines += lines.len();
|
||||||
|
};
|
||||||
|
|
||||||
let mut put = |hunk: Option<DiffHunk>, line: git2::DiffLine| {
|
let res_cell = Rc::clone(&res);
|
||||||
if let Some(hunk) = hunk {
|
let mut put = |delta: DiffDelta,
|
||||||
let hunk_header = HunkHeader::from(hunk);
|
hunk: Option<DiffHunk>,
|
||||||
|
line: git2::DiffLine| {
|
||||||
match current_hunk {
|
|
||||||
None => current_hunk = Some(hunk_header),
|
|
||||||
Some(h) if h != hunk_header => {
|
|
||||||
adder(&h, ¤t_lines);
|
|
||||||
current_lines.clear();
|
|
||||||
current_hunk = Some(hunk_header)
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
let line_type = match line.origin() {
|
|
||||||
'H' => DiffLineType::Header,
|
|
||||||
'<' | '-' => DiffLineType::Delete,
|
|
||||||
'>' | '+' => DiffLineType::Add,
|
|
||||||
_ => DiffLineType::None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let diff_line = DiffLine {
|
|
||||||
content: String::from_utf8_lossy(line.content())
|
|
||||||
.to_string(),
|
|
||||||
line_type,
|
|
||||||
};
|
|
||||||
|
|
||||||
current_lines.push(diff_line);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_file_diff = if diff.deltas().len() == 1 {
|
|
||||||
// it's safe to unwrap here because we check first that diff.deltas has a single element.
|
|
||||||
let delta: DiffDelta = diff.deltas().next().unwrap();
|
|
||||||
|
|
||||||
if delta.status() == Delta::Untracked {
|
|
||||||
let relative_path =
|
|
||||||
delta.new_file().path().ok_or_else(|| {
|
|
||||||
Error::Generic(
|
|
||||||
"new file path is unspecified.".to_string(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let newfile_path = work_dir.join(relative_path);
|
|
||||||
|
|
||||||
if let Some(newfile_content) =
|
|
||||||
new_file_content(&newfile_path)
|
|
||||||
{
|
{
|
||||||
let mut patch = Patch::from_buffers(
|
let mut res = res_cell.borrow_mut();
|
||||||
&[],
|
res.sizes = (
|
||||||
None,
|
delta.old_file().size(),
|
||||||
newfile_content.as_bytes(),
|
delta.new_file().size(),
|
||||||
Some(&newfile_path),
|
);
|
||||||
None,
|
res.size_delta = (res.sizes.1 as i64)
|
||||||
)?;
|
.saturating_sub(res.sizes.0 as i64);
|
||||||
|
}
|
||||||
|
if let Some(hunk) = hunk {
|
||||||
|
let hunk_header = HunkHeader::from(hunk);
|
||||||
|
|
||||||
patch
|
match current_hunk {
|
||||||
.print(&mut |_delta, hunk:Option<DiffHunk>, line: git2::DiffLine| {
|
None => current_hunk = Some(hunk_header),
|
||||||
put(hunk,line);
|
Some(h) if h != hunk_header => {
|
||||||
|
adder(&h, ¤t_lines);
|
||||||
|
current_lines.clear();
|
||||||
|
current_hunk = Some(hunk_header)
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
let line_type = match line.origin() {
|
||||||
|
'H' => DiffLineType::Header,
|
||||||
|
'<' | '-' => DiffLineType::Delete,
|
||||||
|
'>' | '+' => DiffLineType::Add,
|
||||||
|
_ => DiffLineType::None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let diff_line = DiffLine {
|
||||||
|
content: String::from_utf8_lossy(line.content())
|
||||||
|
.to_string(),
|
||||||
|
line_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
current_lines.push(diff_line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_file_diff = if diff.deltas().len() == 1 {
|
||||||
|
// it's safe to unwrap here because we check first that diff.deltas has a single element.
|
||||||
|
let delta: DiffDelta = diff.deltas().next().unwrap();
|
||||||
|
|
||||||
|
if delta.status() == Delta::Untracked {
|
||||||
|
let relative_path =
|
||||||
|
delta.new_file().path().ok_or_else(|| {
|
||||||
|
Error::Generic(
|
||||||
|
"new file path is unspecified."
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let newfile_path = work_dir.join(relative_path);
|
||||||
|
|
||||||
|
if let Some(newfile_content) =
|
||||||
|
new_file_content(&newfile_path)
|
||||||
|
{
|
||||||
|
let mut patch = Patch::from_buffers(
|
||||||
|
&[],
|
||||||
|
None,
|
||||||
|
newfile_content.as_bytes(),
|
||||||
|
Some(&newfile_path),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
patch
|
||||||
|
.print(&mut |delta, hunk:Option<DiffHunk>, line: git2::DiffLine| {
|
||||||
|
put(delta,hunk,line);
|
||||||
true
|
true
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
true
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if !new_file_diff {
|
||||||
|
diff.print(
|
||||||
|
DiffFormat::Patch,
|
||||||
|
move |delta, hunk, line: git2::DiffLine| {
|
||||||
|
put(delta, hunk, line);
|
||||||
|
true
|
||||||
|
},
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if !new_file_diff {
|
if !current_lines.is_empty() {
|
||||||
diff.print(
|
adder(¤t_hunk.unwrap(), ¤t_lines);
|
||||||
DiffFormat::Patch,
|
}
|
||||||
|_, hunk, line: git2::DiffLine| {
|
|
||||||
put(hunk, line);
|
if new_file_diff {
|
||||||
true
|
res.borrow_mut().untracked = true;
|
||||||
},
|
}
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
let res = Rc::try_unwrap(res).expect("rc error");
|
||||||
if !current_lines.is_empty() {
|
Ok(res.into_inner())
|
||||||
adder(¤t_hunk.unwrap(), ¤t_lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_file_diff {
|
|
||||||
res.untracked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_file_content(path: &Path) -> Option<String> {
|
fn new_file_content(path: &Path) -> Option<String> {
|
||||||
|
|
@ -279,7 +301,7 @@ mod tests {
|
||||||
use super::get_diff;
|
use super::get_diff;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::sync::{
|
use crate::sync::{
|
||||||
stage_add_file,
|
commit, stage_add_file,
|
||||||
status::{get_status, StatusType},
|
status::{get_status, StatusType},
|
||||||
tests::{get_statuses, repo_init, repo_init_empty},
|
tests::{get_statuses, repo_init, repo_init_empty},
|
||||||
};
|
};
|
||||||
|
|
@ -455,4 +477,34 @@ mod tests {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_diff_delta_size() -> Result<()> {
|
||||||
|
let file_path = Path::new("bar");
|
||||||
|
let (_td, repo) = repo_init_empty().unwrap();
|
||||||
|
let root = repo.path().parent().unwrap();
|
||||||
|
let repo_path = root.as_os_str().to_str().unwrap();
|
||||||
|
|
||||||
|
File::create(&root.join(file_path))?.write_all(b"\x00")?;
|
||||||
|
|
||||||
|
stage_add_file(repo_path, file_path).unwrap();
|
||||||
|
|
||||||
|
commit(repo_path, "commit").unwrap();
|
||||||
|
|
||||||
|
File::create(&root.join(file_path))?
|
||||||
|
.write_all(b"\x00\x02")?;
|
||||||
|
|
||||||
|
let diff = get_diff(
|
||||||
|
repo_path,
|
||||||
|
String::from(file_path.to_str().unwrap()),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
dbg!(&diff);
|
||||||
|
assert_eq!(diff.sizes, (1, 2));
|
||||||
|
assert_eq!(diff.size_delta, 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use crate::{
|
||||||
ui::{calc_scroll_top, style::SharedTheme},
|
ui::{calc_scroll_top, style::SharedTheme},
|
||||||
};
|
};
|
||||||
use asyncgit::{hash, sync, DiffLine, DiffLineType, FileDiff, CWD};
|
use asyncgit::{hash, sync, DiffLine, DiffLineType, FileDiff, CWD};
|
||||||
|
use bytesize::ByteSize;
|
||||||
use crossterm::event::Event;
|
use crossterm::event::Event;
|
||||||
use std::{borrow::Cow, cmp, path::Path};
|
use std::{borrow::Cow, cmp, path::Path};
|
||||||
use strings::commands;
|
use strings::commands;
|
||||||
|
|
@ -29,7 +30,7 @@ struct Current {
|
||||||
|
|
||||||
///
|
///
|
||||||
pub struct DiffComponent {
|
pub struct DiffComponent {
|
||||||
diff: FileDiff,
|
diff: Option<FileDiff>,
|
||||||
selection: usize,
|
selection: usize,
|
||||||
selected_hunk: Option<usize>,
|
selected_hunk: Option<usize>,
|
||||||
current_size: (u16, u16),
|
current_size: (u16, u16),
|
||||||
|
|
@ -48,7 +49,7 @@ impl DiffComponent {
|
||||||
queue,
|
queue,
|
||||||
current: Current::default(),
|
current: Current::default(),
|
||||||
selected_hunk: None,
|
selected_hunk: None,
|
||||||
diff: FileDiff::default(),
|
diff: None,
|
||||||
current_size: (0, 0),
|
current_size: (0, 0),
|
||||||
selection: 0,
|
selection: 0,
|
||||||
scroll_top: 0,
|
scroll_top: 0,
|
||||||
|
|
@ -56,8 +57,11 @@ impl DiffComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
const fn can_scroll(&self) -> bool {
|
fn can_scroll(&self) -> bool {
|
||||||
self.diff.lines > 1
|
self.diff
|
||||||
|
.as_ref()
|
||||||
|
.map(|diff| diff.lines > 1)
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
pub fn current(&self) -> (String, bool) {
|
pub fn current(&self) -> (String, bool) {
|
||||||
|
|
@ -66,7 +70,7 @@ impl DiffComponent {
|
||||||
///
|
///
|
||||||
pub fn clear(&mut self) -> Result<()> {
|
pub fn clear(&mut self) -> Result<()> {
|
||||||
self.current = Current::default();
|
self.current = Current::default();
|
||||||
self.diff = FileDiff::default();
|
self.diff = None;
|
||||||
self.scroll_top = 0;
|
self.scroll_top = 0;
|
||||||
self.selection = 0;
|
self.selection = 0;
|
||||||
self.selected_hunk = None;
|
self.selected_hunk = None;
|
||||||
|
|
@ -88,12 +92,13 @@ impl DiffComponent {
|
||||||
is_stage,
|
is_stage,
|
||||||
hash,
|
hash,
|
||||||
};
|
};
|
||||||
self.diff = diff;
|
|
||||||
self.scroll_top = 0;
|
|
||||||
self.selection = 0;
|
|
||||||
|
|
||||||
self.selected_hunk =
|
self.selected_hunk =
|
||||||
Self::find_selected_hunk(&self.diff, self.selection)?;
|
Self::find_selected_hunk(&diff, self.selection)?;
|
||||||
|
|
||||||
|
self.diff = Some(diff);
|
||||||
|
self.scroll_top = 0;
|
||||||
|
self.selection = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -103,30 +108,34 @@ impl DiffComponent {
|
||||||
&mut self,
|
&mut self,
|
||||||
move_type: ScrollType,
|
move_type: ScrollType,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let old = self.selection;
|
if let Some(diff) = &self.diff {
|
||||||
|
let old = self.selection;
|
||||||
|
|
||||||
let max = self.diff.lines.saturating_sub(1) as usize;
|
let max = diff.lines.saturating_sub(1) as usize;
|
||||||
|
|
||||||
self.selection = match move_type {
|
self.selection = match move_type {
|
||||||
ScrollType::Down => old.saturating_add(1),
|
ScrollType::Down => old.saturating_add(1),
|
||||||
ScrollType::Up => old.saturating_sub(1),
|
ScrollType::Up => old.saturating_sub(1),
|
||||||
ScrollType::Home => 0,
|
ScrollType::Home => 0,
|
||||||
ScrollType::End => max,
|
ScrollType::End => max,
|
||||||
ScrollType::PageDown => self.selection.saturating_add(
|
ScrollType::PageDown => {
|
||||||
self.current_size.1.saturating_sub(1) as usize,
|
self.selection.saturating_add(
|
||||||
),
|
self.current_size.1.saturating_sub(1)
|
||||||
ScrollType::PageUp => self.selection.saturating_sub(
|
as usize,
|
||||||
self.current_size.1.saturating_sub(1) as usize,
|
)
|
||||||
),
|
}
|
||||||
};
|
ScrollType::PageUp => self.selection.saturating_sub(
|
||||||
|
self.current_size.1.saturating_sub(1) as usize,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
self.selection = cmp::min(max, self.selection);
|
self.selection = cmp::min(max, self.selection);
|
||||||
|
|
||||||
if old != self.selection {
|
if old != self.selection {
|
||||||
self.selected_hunk =
|
self.selected_hunk =
|
||||||
Self::find_selected_hunk(&self.diff, self.selection)?;
|
Self::find_selected_hunk(&diff, self.selection)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,49 +163,96 @@ impl DiffComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_text(&self, width: u16, height: u16) -> Result<Vec<Text>> {
|
fn get_text(&self, width: u16, height: u16) -> Result<Vec<Text>> {
|
||||||
let selection = self.selection;
|
|
||||||
|
|
||||||
let min = self.scroll_top;
|
|
||||||
let max = min + height as usize;
|
|
||||||
|
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut line_cursor = 0_usize;
|
if let Some(diff) = &self.diff {
|
||||||
let mut lines_added = 0_usize;
|
if diff.hunks.is_empty() {
|
||||||
|
let is_positive = diff.size_delta >= 0;
|
||||||
|
let delta_byte_size =
|
||||||
|
ByteSize::b(diff.size_delta.abs() as u64);
|
||||||
|
let sign = if is_positive { "+" } else { "-" };
|
||||||
|
res.extend(vec![
|
||||||
|
Text::Raw(Cow::from("size: ")),
|
||||||
|
Text::Styled(
|
||||||
|
Cow::from(format!(
|
||||||
|
"{}",
|
||||||
|
ByteSize::b(diff.sizes.0)
|
||||||
|
)),
|
||||||
|
self.theme.text(false, false),
|
||||||
|
),
|
||||||
|
Text::Raw(Cow::from(" -> ")),
|
||||||
|
Text::Styled(
|
||||||
|
Cow::from(format!(
|
||||||
|
"{}",
|
||||||
|
ByteSize::b(diff.sizes.1)
|
||||||
|
)),
|
||||||
|
self.theme.text(false, false),
|
||||||
|
),
|
||||||
|
Text::Raw(Cow::from(" (")),
|
||||||
|
Text::Styled(
|
||||||
|
Cow::from(format!(
|
||||||
|
"{}{:}",
|
||||||
|
sign, delta_byte_size
|
||||||
|
)),
|
||||||
|
self.theme.diff_line(
|
||||||
|
if is_positive {
|
||||||
|
DiffLineType::Add
|
||||||
|
} else {
|
||||||
|
DiffLineType::Delete
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text::Raw(Cow::from(")")),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
let selection = self.selection;
|
||||||
|
|
||||||
for (i, hunk) in self.diff.hunks.iter().enumerate() {
|
let min = self.scroll_top;
|
||||||
let hunk_selected =
|
let max = min + height as usize;
|
||||||
self.selected_hunk.map_or(false, |s| s == i);
|
|
||||||
|
|
||||||
if lines_added >= height as usize {
|
let mut line_cursor = 0_usize;
|
||||||
break;
|
let mut lines_added = 0_usize;
|
||||||
}
|
|
||||||
|
|
||||||
let hunk_len = hunk.lines.len();
|
for (i, hunk) in diff.hunks.iter().enumerate() {
|
||||||
let hunk_min = line_cursor;
|
let hunk_selected =
|
||||||
let hunk_max = line_cursor + hunk_len;
|
self.selected_hunk.map_or(false, |s| s == i);
|
||||||
|
|
||||||
if Self::hunk_visible(hunk_min, hunk_max, min, max) {
|
if lines_added >= height as usize {
|
||||||
for (i, line) in hunk.lines.iter().enumerate() {
|
break;
|
||||||
if line_cursor >= min && line_cursor <= max {
|
|
||||||
Self::add_line(
|
|
||||||
&mut res,
|
|
||||||
width,
|
|
||||||
line,
|
|
||||||
selection == line_cursor,
|
|
||||||
hunk_selected,
|
|
||||||
i == hunk_len as usize - 1,
|
|
||||||
&self.theme,
|
|
||||||
);
|
|
||||||
lines_added += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
line_cursor += 1;
|
let hunk_len = hunk.lines.len();
|
||||||
|
let hunk_min = line_cursor;
|
||||||
|
let hunk_max = line_cursor + hunk_len;
|
||||||
|
|
||||||
|
if Self::hunk_visible(
|
||||||
|
hunk_min, hunk_max, min, max,
|
||||||
|
) {
|
||||||
|
for (i, line) in hunk.lines.iter().enumerate()
|
||||||
|
{
|
||||||
|
if line_cursor >= min
|
||||||
|
&& line_cursor <= max
|
||||||
|
{
|
||||||
|
Self::add_line(
|
||||||
|
&mut res,
|
||||||
|
width,
|
||||||
|
line,
|
||||||
|
selection == line_cursor,
|
||||||
|
hunk_selected,
|
||||||
|
i == hunk_len as usize - 1,
|
||||||
|
&self.theme,
|
||||||
|
);
|
||||||
|
lines_added += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_cursor += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
line_cursor += hunk_len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
line_cursor += hunk_len;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,26 +328,34 @@ impl DiffComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unstage_hunk(&mut self) -> Result<()> {
|
fn unstage_hunk(&mut self) -> Result<()> {
|
||||||
if let Some(hunk) = self.selected_hunk {
|
if let Some(diff) = &self.diff {
|
||||||
let hash = self.diff.hunks[hunk].header_hash;
|
if let Some(hunk) = self.selected_hunk {
|
||||||
sync::unstage_hunk(CWD, self.current.path.clone(), hash)?;
|
let hash = diff.hunks[hunk].header_hash;
|
||||||
self.queue_update();
|
sync::unstage_hunk(
|
||||||
|
CWD,
|
||||||
|
self.current.path.clone(),
|
||||||
|
hash,
|
||||||
|
)?;
|
||||||
|
self.queue_update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stage_hunk(&mut self) -> Result<()> {
|
fn stage_hunk(&mut self) -> Result<()> {
|
||||||
if let Some(hunk) = self.selected_hunk {
|
if let Some(diff) = &self.diff {
|
||||||
let path = self.current.path.clone();
|
if let Some(hunk) = self.selected_hunk {
|
||||||
if self.diff.untracked {
|
let path = self.current.path.clone();
|
||||||
sync::stage_add_file(CWD, Path::new(&path))?;
|
if diff.untracked {
|
||||||
} else {
|
sync::stage_add_file(CWD, Path::new(&path))?;
|
||||||
let hash = self.diff.hunks[hunk].header_hash;
|
} else {
|
||||||
sync::stage_hunk(CWD, path, hash)?;
|
let hash = diff.hunks[hunk].header_hash;
|
||||||
}
|
sync::stage_hunk(CWD, path, hash)?;
|
||||||
|
}
|
||||||
|
|
||||||
self.queue_update();
|
self.queue_update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -306,21 +370,22 @@ impl DiffComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_hunk(&self) -> Result<()> {
|
fn reset_hunk(&self) -> Result<()> {
|
||||||
if let Some(hunk) = self.selected_hunk {
|
if let Some(diff) = &self.diff {
|
||||||
let hash = self.diff.hunks[hunk].header_hash;
|
if let Some(hunk) = self.selected_hunk {
|
||||||
|
let hash = diff.hunks[hunk].header_hash;
|
||||||
|
|
||||||
self.queue
|
self.queue
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("try using queue in immutable diff")
|
.expect("try using queue in immutable diff")
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.push_back(InternalEvent::ConfirmAction(
|
.push_back(InternalEvent::ConfirmAction(
|
||||||
Action::ResetHunk(
|
Action::ResetHunk(
|
||||||
self.current.path.clone(),
|
self.current.path.clone(),
|
||||||
hash,
|
hash,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -466,10 +531,12 @@ impl Component for DiffComponent {
|
||||||
if !self.is_immutable()
|
if !self.is_immutable()
|
||||||
&& !self.is_stage() =>
|
&& !self.is_stage() =>
|
||||||
{
|
{
|
||||||
if self.diff.untracked {
|
if let Some(diff) = &self.diff {
|
||||||
self.reset_untracked()?;
|
if diff.untracked {
|
||||||
} else {
|
self.reset_untracked()?;
|
||||||
self.reset_hunk()?;
|
} else {
|
||||||
|
self.reset_hunk()?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue