@@ -25,6 +25,10 @@ use std::ops::Range;
2525/// - The first commit returned here is the first eligible commit to be responsible for parts of `file_path`.
2626/// * `file_path`
2727/// - A *slash-separated* worktree-relative path to the file to blame.
28+ /// * `range`
29+ /// - A 1-based inclusive range, in order to mirror `git`’s behaviour. `Some(20..40)` represents
30+ /// 21 lines, spanning from line 20 up to and including line 40. This will be converted to
31+ /// `19..40` internally as the algorithm uses 0-based ranges that are exclusive at the end.
2832/// * `resource_cache`
2933/// - Used for diffing trees.
3034///
@@ -61,6 +65,7 @@ pub fn file<E>(
6165 traverse : impl IntoIterator < Item = Result < gix_traverse:: commit:: Info , E > > ,
6266 resource_cache : & mut gix_diff:: blob:: Platform ,
6367 file_path : & BStr ,
68+ range : Option < Range < u32 > > ,
6469) -> Result < Outcome , Error >
6570where
6671 E : Into < Box < dyn std:: error:: Error + Send + Sync + ' static > > ,
@@ -85,19 +90,37 @@ where
8590 . tokenize ( )
8691 . map ( |token| interner. intern ( token) )
8792 . count ( )
88- } ;
93+ } as u32 ;
8994
9095 // Binary or otherwise empty?
9196 if num_lines_in_blamed == 0 {
9297 return Ok ( Outcome :: default ( ) ) ;
9398 }
9499
95- let mut hunks_to_blame = vec ! [ {
96- let range_in_blamed_file = 0 ..num_lines_in_blamed as u32 ;
97- UnblamedHunk {
98- range_in_blamed_file: range_in_blamed_file. clone( ) ,
99- suspects: [ ( suspect, range_in_blamed_file) ] . into( ) ,
100+ // This function assumes that `range` has 1-based inclusive line numbers and converts it to the
101+ // format internally used: 0-based line numbers stored in ranges that are exclusive at the
102+ // end.
103+ let one_based_inclusive_to_zero_based_exclusive_range = || -> Result < Range < u32 > , Error > {
104+ if let Some ( range) = range {
105+ if range. start == 0 {
106+ return Err ( Error :: InvalidLineRange ) ;
107+ }
108+ let start = range. start - 1 ;
109+ let end = range. end ;
110+ if start >= num_lines_in_blamed || end > num_lines_in_blamed || start == end {
111+ return Err ( Error :: InvalidLineRange ) ;
112+ }
113+ Ok ( start..end)
114+ } else {
115+ Ok ( 0 ..num_lines_in_blamed)
100116 }
117+ } ;
118+
119+ let range_in_blamed_file = one_based_inclusive_to_zero_based_exclusive_range ( ) ?;
120+
121+ let mut hunks_to_blame = vec ! [ UnblamedHunk {
122+ range_in_blamed_file: range_in_blamed_file. clone( ) ,
123+ suspects: [ ( suspect, range_in_blamed_file) ] . into( ) ,
101124 } ] ;
102125
103126 let mut out = Vec :: new ( ) ;
0 commit comments