git.rs

  1use std::ops::Range;
  2use std::sync::Arc;
  3
  4use sum_tree::Bias;
  5use text::{Anchor, Point};
  6
  7pub use git2 as libgit;
  8use libgit::{DiffOptions as GitOptions, Patch as GitPatch};
  9
 10#[derive(Debug, Clone, Copy)]
 11pub enum DiffHunkStatus {
 12    Added,
 13    Modified,
 14    Removed,
 15}
 16
 17#[derive(Debug)]
 18pub struct DiffHunk<T> {
 19    pub buffer_range: Range<T>,
 20    pub head_range: Range<usize>,
 21}
 22
 23impl DiffHunk<u32> {
 24    pub fn status(&self) -> DiffHunkStatus {
 25        if self.head_range.is_empty() {
 26            DiffHunkStatus::Added
 27        } else if self.buffer_range.is_empty() {
 28            DiffHunkStatus::Removed
 29        } else {
 30            DiffHunkStatus::Modified
 31        }
 32    }
 33}
 34
 35pub struct BufferDiff {
 36    hunks: Arc<[DiffHunk<Anchor>]>,
 37}
 38
 39impl BufferDiff {
 40    pub fn new() -> BufferDiff {
 41        BufferDiff {
 42            hunks: Arc::new([]),
 43        }
 44    }
 45
 46    pub fn hunks(&self) -> Arc<[DiffHunk<Anchor>]> {
 47        self.hunks.clone()
 48    }
 49
 50    pub fn update(&mut self, head: &str, buffer: &text::BufferSnapshot) {
 51        let head = head.as_bytes();
 52        let current = buffer.as_rope().to_string().into_bytes();
 53
 54        let mut options = GitOptions::default();
 55        options.context_lines(0);
 56        let patch = match GitPatch::from_buffers(head, None, &current, None, Some(&mut options)) {
 57            Ok(patch) => patch,
 58            Err(_) => {
 59                //Reset hunks in case of failure to avoid showing a stale (potentially erroneous) diff
 60                self.hunks = Arc::new([]);
 61                return;
 62            }
 63        };
 64
 65        let mut hunks = Vec::new();
 66        for index in 0..patch.num_hunks() {
 67            let (hunk, _) = match patch.hunk(index) {
 68                Ok(it) => it,
 69                Err(_) => continue,
 70            };
 71
 72            let new_start = hunk.new_start() - 1;
 73            let new_end = new_start + hunk.new_lines();
 74            let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left);
 75            let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left);
 76            let buffer_range = start_anchor..end_anchor;
 77
 78            let old_start = hunk.old_start() as usize - 1;
 79            let old_end = old_start + hunk.old_lines() as usize;
 80            let head_range = old_start..old_end;
 81
 82            hunks.push(DiffHunk {
 83                buffer_range,
 84                head_range,
 85            });
 86        }
 87
 88        self.hunks = hunks.into();
 89    }
 90}
 91
 92#[derive(Debug, Clone, Copy)]
 93pub enum GitDiffEdit {
 94    Added(u32),
 95    Modified(u32),
 96    Removed(u32),
 97}
 98
 99impl GitDiffEdit {
100    pub fn line(self) -> u32 {
101        use GitDiffEdit::*;
102
103        match self {
104            Added(line) | Modified(line) | Removed(line) => line,
105        }
106    }
107}