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::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 current = buffer.as_rope().to_string().into_bytes();
 52        let patch = match GitPatch::from_buffers(head.as_bytes(), None, &current, None, None) {
 53            Ok(patch) => patch,
 54            Err(_) => {
 55                //Reset hunks in case of failure to avoid showing a stale (potentially erroneous) diff
 56                self.hunks = Arc::new([]);
 57                return;
 58            }
 59        };
 60
 61        let mut hunks = Vec::new();
 62        for index in 0..patch.num_hunks() {
 63            let (hunk, _) = match patch.hunk(index) {
 64                Ok(it) => it,
 65                Err(_) => break,
 66            };
 67
 68            let new_start = hunk.new_start();
 69            let new_end = new_start + hunk.new_lines();
 70            let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left);
 71            let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left);
 72            let buffer_range = start_anchor..end_anchor;
 73
 74            let old_start = hunk.old_start() as usize;
 75            let old_end = old_start + hunk.old_lines() as usize;
 76            let head_range = old_start..old_end;
 77
 78            hunks.push(DiffHunk {
 79                buffer_range,
 80                head_range,
 81            });
 82        }
 83
 84        self.hunks = hunks.into();
 85    }
 86}
 87
 88#[derive(Debug, Clone, Copy)]
 89pub enum GitDiffEdit {
 90    Added(u32),
 91    Modified(u32),
 92    Removed(u32),
 93}
 94
 95impl GitDiffEdit {
 96    pub fn line(self) -> u32 {
 97        use GitDiffEdit::*;
 98
 99        match self {
100            Added(line) | Modified(line) | Removed(line) => line,
101        }
102    }
103}
104
105// struct DiffTracker {
106//     track_line_num: u32,
107//     edits: Vec<GitDiffEdit>,
108// }
109
110// impl DiffTracker {
111//     fn new() -> DiffTracker {
112//         DiffTracker {
113//             track_line_num: 0,
114//             edits: Vec::new(),
115//         }
116//     }
117
118//     fn attempt_finalize_file(&mut self, base_path: &Path) -> Result<()> {
119//         let relative = if let Some(relative) = self.last_file_path.clone() {
120//             relative
121//         } else {
122//             return Ok(());
123//         };
124
125//         let mut path = base_path.to_path_buf();
126//         path.push(relative);
127//         path = canonicalize(path).map_err(Error::Io)?;
128
129//         self.diffs.push(GitFileDiff {
130//             path,
131//             edits: take(&mut self.edits),
132//         });
133
134//         Ok(())
135//     }
136
137//     fn handle_diff_line(
138//         &mut self,
139//         delta: DiffDelta,
140//         line: DiffLine,
141//         base_path: &Path,
142//     ) -> Result<()> {
143//         let path = match (delta.old_file().path(), delta.new_file().path()) {
144//             (Some(old), _) => old,
145//             (_, Some(new)) => new,
146//             (_, _) => return Err(Error::DeltaMissingPath),
147//         };
148
149//         if self.last_file_path.as_deref() != Some(path) {
150//             self.attempt_finalize_file(base_path)?;
151//             self.last_file_path = Some(path.to_path_buf());
152//             self.track_line_num = 0;
153//         }
154
155//         match line.origin_value() {
156//             DiffLineType::Context => {
157//                 self.track_line_num = line.new_lineno().ok_or(Error::ContextMissingLineNum)?;
158//             }
159
160//             DiffLineType::Deletion => {
161//                 self.track_line_num += 1;
162//                 self.edits.push(GitDiffEdit::Removed(self.track_line_num));
163//             }
164
165//             DiffLineType::Addition => {
166//                 let addition_line_num = line.new_lineno().ok_or(Error::AdditionMissingLineNum)?;
167//                 self.track_line_num = addition_line_num;
168
169//                 let mut replaced = false;
170//                 for rewind_index in (0..self.edits.len()).rev() {
171//                     let edit = &mut self.edits[rewind_index];
172
173//                     if let GitDiffEdit::Removed(removed_line_num) = *edit {
174//                         match removed_line_num.cmp(&addition_line_num) {
175//                             Ordering::Equal => {
176//                                 *edit = GitDiffEdit::Modified(addition_line_num);
177//                                 replaced = true;
178//                                 break;
179//                             }
180
181//                             Ordering::Greater => continue,
182//                             Ordering::Less => break,
183//                         }
184//                     }
185//                 }
186
187//                 if !replaced {
188//                     self.edits.push(GitDiffEdit::Added(addition_line_num));
189//                 }
190//             }
191
192//             _ => {}
193//         }
194
195//         Ok(())
196//     }
197// }