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, ¤t, 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}