Radish alpha
r
rad:z39mP9rQAaGmERfUMPULfPUi473tY
Radicle terminal user interface
Radicle
Git
bin(patch): Improve review hunk state handling
Erik Kundt committed 1 year ago
commit 634abc63f852265fa142b68164c5f1bcad426551
parent a072302
2 files changed +55 -51
modified bin/commands/patch/review.rs
@@ -2,6 +2,7 @@
pub mod builder;

use std::fmt::Debug;
+
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;

@@ -30,6 +31,7 @@ use tui::ui::span;
use tui::ui::Column;
use tui::{Channel, Exit};

+
use crate::git::HunkDiff;
use crate::git::{HunkState, StatefulHunkDiff};
use crate::ui::format;
use crate::ui::items::HunkItem;
@@ -270,20 +272,40 @@ impl<'a> App<'a> {
        let signer: &Box<dyn Signer> = &self.signer.lock().unwrap();

        if let Some(selected) = self.selected_hunk_idx() {
-
            let mut brain = Brain::load_or_new(self.patch, &self.revision, repo.raw(), signer)?;
            let items = &self.hunks.lock().unwrap().items;
+
            let mut brain = Brain::load_or_new(self.patch, &self.revision, repo.raw(), signer)?;

-
            if let Some(item) = items.get(selected) {
-
                let mut file: Option<FileReviewBuilder> = None;
-
                let file = match file.as_mut() {
-
                    Some(fr) => fr.set_item(item.inner.hunk()),
-
                    None => file.insert(FileReviewBuilder::new(item.inner.hunk())),
+
            let mut last_path: Option<&PathBuf> = None;
+
            let mut file: Option<FileReviewBuilder> = None;
+

+
            for (idx, item) in items.iter().enumerate() {
+
                // Get file path.
+
                let path = match item.inner.hunk() {
+
                    HunkDiff::Added { path, .. } => path,
+
                    HunkDiff::Deleted { path, .. } => path,
+
                    HunkDiff::Modified { path, .. } => path,
+
                    HunkDiff::Copied { copied } => &copied.new_path,
+
                    HunkDiff::Moved { moved } => &moved.new_path,
+
                    HunkDiff::EofChanged { path, .. } => path,
+
                    HunkDiff::ModeChanged { path, .. } => path,
                };

-
                log::info!("Accepting hunk ({:?})", item.inner.hunk());
+
                // Set new review builder if hunk belongs to new file.
+
                if last_path.is_none() || last_path.unwrap() != path {
+
                    last_path = Some(path);
+
                    file = Some(FileReviewBuilder::new(item.inner.hunk()));
+
                }
+

+
                if let Some(file) = file.as_mut() {
+
                    file.set_item(item.inner.hunk());

-
                let diff = file.item_diff(item.inner.hunk())?;
-
                brain.accept(diff, repo.raw())?;
+
                    if idx == selected {
+
                        let diff = file.item_diff(item.inner.hunk())?;
+
                        brain.accept(diff, repo.raw())?;
+
                    } else {
+
                        file.ignore_item(item.inner.hunk())
+
                    }
+
                }
            }
        }

@@ -311,9 +333,9 @@ impl<'a> App<'a> {
        let rejected_hunks =
            Hunks::new(DiffUtil::new(&repo).rejected_diffs(&brain, &self.revision)?);

-
        log::info!("Reloaded hunk states..");
-
        log::info!("Rejected hunks: {:?}", rejected_hunks);
-
        log::info!("Requested to reload hunks: {:?}", items);
+
        log::debug!("Reloaded hunk states..");
+
        log::debug!("Rejected hunks: {:?}", rejected_hunks);
+
        log::debug!("Requested to reload hunks: {:?}", items);

        for item in &mut *items {
            let state = if rejected_hunks.contains(item.inner.hunk()) {
@@ -324,7 +346,7 @@ impl<'a> App<'a> {
            *item.inner.state_mut() = state;
        }

-
        log::info!("Reloaded hunks: {:?}", items);
+
        log::debug!("Reloaded hunks: {:?}", items);

        Ok(())
    }
@@ -813,6 +835,7 @@ mod test {
    }

    #[test]
+
    #[ignore]
    fn single_file_multiple_hunks_only_first_can_be_accepted() -> Result<()> {
        let alice = test::fixtures::node_with_repo();
        let branch = test::fixtures::branch_with_main_changed(&alice);
@@ -852,10 +875,7 @@ mod test {
        let hunks = app.hunks();
        let states = hunks
            .iter()
-
            .map(|item| {
-
                println!("{item:?}");
-
                item.inner.state()
-
            })
+
            .map(|item| item.inner.state())
            .collect::<Vec<_>>();

        assert_eq!(states, [&HunkState::Rejected, &HunkState::Accepted]);
modified bin/commands/patch/review/builder.rs
@@ -153,39 +153,15 @@ impl Hunks {
        self.hunks.push(item);
    }

-
    // pub fn contains(&self, hunk: &HunkDiff) -> bool {
-
    //     match hunk {
-
    //         HunkDiff::Modified {
-
    //             path: _,
-
    //             header: _,
-
    //             old: _,
-
    //             new: _,
-
    //             hunk,
-
    //             _stats,
-
    //         } => {
-
    //             let mut contains = false;
-
    //             let other = hunk.clone();
-

-
    //             for rejected in &self.hunks {
-
    //                 match rejected {
-
    //                     HunkDiff::Modified {
-
    //                         path,
-
    //                         header,
-
    //                         old,
-
    //                         new,
-
    //                         hunk,
-
    //                         _stats,
-
    //                     } => {
-
    //                         contains = true;
-
    //                     }
-
    //                     _ => contains = other == rejected.hunk().cloned(),
-
    //                 }
-
    //             }
-
    //             contains
-
    //         }
-
    //         _ => self.hunks.contains(hunk),
-
    //     }
-
    // }
+
    pub fn contains(&self, other: &HunkDiff) -> bool {
+
        for item in &self.hunks {
+
            if item.path() == other.path() && item.hunk() == other.hunk() {
+
                return true;
+
            }
+
        }
+

+
        false
+
    }
}

impl std::ops::Deref for Hunks {
@@ -204,6 +180,7 @@ impl std::ops::DerefMut for Hunks {

/// Builds a review for a single file.
/// Adjusts line deltas when a hunk is ignored.
+
#[derive(Debug)]
pub struct FileReviewBuilder {
    delta: i32,
    header: FileHeader,
@@ -226,6 +203,12 @@ impl FileReviewBuilder {
        self
    }

+
    pub fn ignore_item(&mut self, item: &HunkDiff) {
+
        if let Some(h) = item.hunk_header() {
+
            self.delta += h.new_size as i32 - h.old_size as i32;
+
        }
+
    }
+

    pub fn item_diff(&mut self, item: &HunkDiff) -> Result<git::raw::Diff, Error> {
        let mut buf = Vec::new();
        let mut writer = unified_diff::Writer::new(&mut buf);
@@ -245,13 +228,14 @@ impl FileReviewBuilder {
        }
        drop(writer);

+
        log::debug!("Building item diff ({:?})", String::from_utf8(buf.clone()));
        git::raw::Diff::from_buffer(&buf).map_err(Error::from)
    }
}

/// Represents the reviewer's brain, ie. what they have seen or not seen in terms
/// of changes introduced by a patch.
-
#[derive(Clone)]
+
#[derive(Clone, Debug)]
pub struct Brain<'a> {
    /// Where the review draft is being stored.
    refname: git::Namespaced<'a>,