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
}
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4