1
+ use gix_hash:: ObjectId ;
2
+ use gix_object:: bstr:: BString ;
3
+ use smallvec:: SmallVec ;
4
+ use std:: ops:: RangeInclusive ;
1
5
use std:: {
2
6
num:: NonZeroU32 ,
3
7
ops:: { AddAssign , Range , SubAssign } ,
4
8
} ;
5
9
6
- use gix_hash:: ObjectId ;
7
- use gix_object:: bstr:: BString ;
8
- use smallvec:: SmallVec ;
9
-
10
10
use crate :: file:: function:: tokens_for_diffing;
11
11
use crate :: Error ;
12
12
13
13
/// A type to represent one or more line ranges to blame in a file.
14
14
///
15
- /// This type handles the conversion between git's 1-based inclusive ranges and the internal
15
+ /// It handles the conversion between git's 1-based inclusive ranges and the internal
16
16
/// 0-based exclusive ranges used by the blame algorithm.
17
17
///
18
18
/// # Examples
@@ -21,18 +21,18 @@ use crate::Error;
21
21
/// use gix_blame::BlameRanges;
22
22
///
23
23
/// // Blame lines 20 through 40 (inclusive)
24
- /// let range = BlameRanges::from_range(20..41 );
24
+ /// let range = BlameRanges::from_range(20..=40 );
25
25
///
26
26
/// // Blame multiple ranges
27
27
/// let mut ranges = BlameRanges::new();
28
- /// ranges.add_range(1..5 ); // Lines 1-4
29
- /// ranges.add_range(10..15 ); // Lines 10-14
28
+ /// ranges.add_range(1..=4 ); // Lines 1-4
29
+ /// ranges.add_range(10..=14 ); // Lines 10-14
30
30
/// ```
31
31
///
32
32
/// # Line Number Representation
33
33
///
34
34
/// This type uses 1-based inclusive ranges to mirror `git`'s behaviour:
35
- /// - A range of `20..41 ` represents 21 lines, spanning from line 20 up to and including line 40
35
+ /// - A range of `20..=40 ` represents 21 lines, spanning from line 20 up to and including line 40
36
36
/// - This will be converted to `19..40` internally as the algorithm uses 0-based ranges that are exclusive at the end
37
37
///
38
38
/// # Empty Ranges
@@ -43,59 +43,60 @@ use crate::Error;
43
43
pub struct BlameRanges {
44
44
/// The ranges to blame, stored as 1-based inclusive ranges
45
45
/// An empty Vec means blame the entire file
46
- ranges : Vec < Range < u32 > > ,
46
+ ranges : Vec < RangeInclusive < u32 > > ,
47
47
}
48
48
49
+ /// Lifecycle
49
50
impl BlameRanges {
50
51
/// Create a new empty BlameRanges instance.
51
52
///
52
53
/// An empty instance means to blame the entire file.
53
54
pub fn new ( ) -> Self {
54
- Self { ranges : Vec :: new ( ) }
55
- }
56
-
57
- /// Add a single range to blame.
58
- ///
59
- /// The range should be 1-based inclusive.
60
- /// If the new range overlaps with or is adjacent to an existing range,
61
- /// they will be merged into a single range.
62
- pub fn add_range ( & mut self , new_range : Range < u32 > ) {
63
- self . merge_range ( new_range) ;
55
+ Self :: default ( )
64
56
}
65
57
66
58
/// Create from a single range.
67
59
///
68
- /// The range should be 1-based inclusive , similar to git's line number format.
69
- pub fn from_range ( range : Range < u32 > ) -> Self {
60
+ /// The range is 1-based, similar to git's line number format.
61
+ pub fn from_range ( range : RangeInclusive < u32 > ) -> Self {
70
62
Self { ranges : vec ! [ range] }
71
63
}
72
64
73
65
/// Create from multiple ranges.
74
66
///
75
- /// All ranges should be 1-based inclusive .
67
+ /// All ranges are 1-based.
76
68
/// Overlapping or adjacent ranges will be merged.
77
- pub fn from_ranges ( ranges : Vec < Range < u32 > > ) -> Self {
69
+ pub fn from_ranges ( ranges : Vec < RangeInclusive < u32 > > ) -> Self {
78
70
let mut result = Self :: new ( ) ;
79
71
for range in ranges {
80
72
result. merge_range ( range) ;
81
73
}
82
74
result
83
75
}
76
+ }
77
+
78
+ impl BlameRanges {
79
+ /// Add a single range to blame.
80
+ ///
81
+ /// The range should be 1-based inclusive.
82
+ /// If the new range overlaps with or is adjacent to an existing range,
83
+ /// they will be merged into a single range.
84
+ pub fn add_range ( & mut self , new_range : RangeInclusive < u32 > ) {
85
+ self . merge_range ( new_range) ;
86
+ }
84
87
85
88
/// Attempts to merge the new range with any existing ranges.
86
- /// If no merge is possible, adds it as a new range.
87
- fn merge_range ( & mut self , new_range : Range < u32 > ) {
88
- // First check if this range can be merged with any existing range
89
+ /// If no merge is possible, add it as a new range.
90
+ fn merge_range ( & mut self , new_range : RangeInclusive < u32 > ) {
91
+ // Check if this range can be merged with any existing range
89
92
for range in & mut self . ranges {
90
93
// Check if ranges overlap or are adjacent
91
- if new_range. start <= range. end && range. start <= new_range. end {
92
- // Merge the ranges by taking the minimum start and maximum end
93
- range. start = range. start . min ( new_range. start ) ;
94
- range. end = range. end . max ( new_range. end ) ;
94
+ if new_range. start ( ) <= range. end ( ) && range. start ( ) <= new_range. end ( ) {
95
+ * range = * range. start ( ) . min ( new_range. start ( ) ) ..=* range. end ( ) . max ( new_range. end ( ) ) ;
95
96
return ;
96
97
}
97
98
}
98
- // If no overlap found, add as new range
99
+ // If no overlap found, add it as a new range
99
100
self . ranges . push ( new_range) ;
100
101
}
101
102
@@ -118,11 +119,11 @@ impl BlameRanges {
118
119
119
120
let mut result = Vec :: with_capacity ( self . ranges . len ( ) ) ;
120
121
for range in & self . ranges {
121
- if range. start == 0 {
122
+ if * range. start ( ) == 0 {
122
123
return Err ( Error :: InvalidLineRange ) ;
123
124
}
124
- let start = range. start - 1 ;
125
- let end = range. end ;
125
+ let start = range. start ( ) - 1 ;
126
+ let end = * range. end ( ) ;
126
127
if start >= max_lines || end > max_lines || start == end {
127
128
return Err ( Error :: InvalidLineRange ) ;
128
129
}
0 commit comments