|
| 1 | +use std::collections::HashMap; |
| 2 | +use std::fs; |
| 3 | +use std::path::{Path, PathBuf}; |
| 4 | + |
| 5 | +use git2::{DiffOptions, IntoCString, Repository}; |
| 6 | + |
| 7 | +use crate::{LineChange, LineChanges}; |
| 8 | + |
| 9 | +pub struct Git { |
| 10 | + relative_path: PathBuf, |
| 11 | + repo: Repository, |
| 12 | + pub line_changes: Option<LineChanges>, |
| 13 | +} |
| 14 | +impl Git { |
| 15 | + pub fn from_path(filename: &Path) -> Option<Self> { |
| 16 | + if let Ok(repo) = Repository::discover(&filename) { |
| 17 | + let repo_path_absolute = fs::canonicalize(repo.workdir()?).ok()?; |
| 18 | + |
| 19 | + let absolute_path = fs::canonicalize(&filename).ok()?.to_path_buf(); |
| 20 | + let relative_path = absolute_path |
| 21 | + .strip_prefix(&repo_path_absolute) |
| 22 | + .ok()? |
| 23 | + .to_path_buf(); |
| 24 | + Some(Git { |
| 25 | + repo, |
| 26 | + relative_path, |
| 27 | + line_changes: None, |
| 28 | + }) |
| 29 | + } else { |
| 30 | + None |
| 31 | + } |
| 32 | + } |
| 33 | + |
| 34 | + /// Taken from https://github.com/sharkdp/bat/blob/master/src/diff.rs |
| 35 | + pub fn diff(&mut self) { |
| 36 | + let mut diff_options = DiffOptions::new(); |
| 37 | + let pathspec = if let Ok(p) = self.relative_path.clone().into_c_string() { |
| 38 | + p |
| 39 | + } else { |
| 40 | + return; |
| 41 | + }; |
| 42 | + diff_options.pathspec(pathspec); |
| 43 | + diff_options.context_lines(0); |
| 44 | + |
| 45 | + let diff = if let Ok(d) = self |
| 46 | + .repo |
| 47 | + .diff_index_to_workdir(None, Some(&mut diff_options)) |
| 48 | + { |
| 49 | + d |
| 50 | + } else { |
| 51 | + return; |
| 52 | + }; |
| 53 | + |
| 54 | + let mut line_changes: LineChanges = HashMap::new(); |
| 55 | + |
| 56 | + let mark_section = |
| 57 | + |line_changes: &mut LineChanges, start: u32, end: i32, change: LineChange| { |
| 58 | + for line in start..=end as u32 { |
| 59 | + line_changes.insert(line as usize, change); |
| 60 | + } |
| 61 | + }; |
| 62 | + |
| 63 | + let _ = diff.foreach( |
| 64 | + &mut |_, _| true, |
| 65 | + None, |
| 66 | + Some(&mut |delta, hunk| { |
| 67 | + let path = delta.new_file().path().unwrap_or_else(|| Path::new("")); |
| 68 | + |
| 69 | + if self.relative_path != path { |
| 70 | + return false; |
| 71 | + } |
| 72 | + |
| 73 | + let old_lines = hunk.old_lines(); |
| 74 | + let new_start = hunk.new_start(); |
| 75 | + let new_lines = hunk.new_lines(); |
| 76 | + let new_end = (new_start + new_lines) as i32 - 1; |
| 77 | + |
| 78 | + if old_lines == 0 && new_lines > 0 { |
| 79 | + mark_section(&mut line_changes, new_start, new_end, LineChange::Added); |
| 80 | + } else if new_lines == 0 && old_lines > 0 { |
| 81 | + if new_start == 0 { |
| 82 | + mark_section(&mut line_changes, 1, 1, LineChange::RemovedAbove); |
| 83 | + } else { |
| 84 | + mark_section( |
| 85 | + &mut line_changes, |
| 86 | + new_start, |
| 87 | + new_start as i32, |
| 88 | + LineChange::RemovedBelow, |
| 89 | + ); |
| 90 | + } |
| 91 | + } else { |
| 92 | + mark_section(&mut line_changes, new_start, new_end, LineChange::Modified); |
| 93 | + } |
| 94 | + |
| 95 | + true |
| 96 | + }), |
| 97 | + None, |
| 98 | + ); |
| 99 | + |
| 100 | + self.line_changes = Some(line_changes); |
| 101 | + } |
| 102 | +} |
| 103 | + |
0 commit comments