1
1
//! Facilities to produce git-formatted diffs.
2
2
3
- use std:: cmp:: Ordering ;
4
- use std:: ops:: Range ;
5
-
6
3
use crate :: blob:: GitDiff ;
4
+ use bstr:: ByteSlice ;
7
5
use imara_diff:: intern:: { InternedInput , Interner , Token } ;
8
6
use imara_diff:: Sink ;
7
+ use std:: cmp:: Ordering ;
8
+ use std:: ops:: Range ;
9
9
10
10
// Explanation for the following numbers can be found here:
11
11
// https://github.com/git/git/blob/324fbaab88126196bd42e7fa383ee94e165d61b5/xdiff/xdiffi.c#L535
@@ -29,9 +29,11 @@ pub(super) mod types {
29
29
use crate :: blob:: git_diff:: ChangeGroup ;
30
30
31
31
/// A [`Sink`](imara_diff::Sink) that creates a diff like git would.
32
+ ///
33
+ /// See the [diff slider repository](https://github.com/mhagger/diff-slider-tools) for more information.
32
34
pub struct GitDiff < ' a , T >
33
35
where
34
- T : std :: fmt :: Display ,
36
+ T : AsRef < [ u8 ] > ,
35
37
{
36
38
pub ( crate ) after : & ' a [ imara_diff:: intern:: Token ] ,
37
39
pub ( crate ) interner : & ' a imara_diff:: intern:: Interner < T > ,
@@ -75,7 +77,7 @@ impl PartialOrd for Score {
75
77
#[ derive( PartialEq , Debug ) ]
76
78
pub struct ChangeGroup {
77
79
/// Range indicating the lines of the previous block.
78
- /// To actually see how the previous block looked like, you need to combine this range with
80
+ /// To actually see what the previous block looked like, you need to combine this range with
79
81
/// the [`InternedInput`].
80
82
pub before : Range < usize > ,
81
83
/// Range indicating the lines of the new block
@@ -86,16 +88,28 @@ pub struct ChangeGroup {
86
88
pub change_kind : ChangeKind ,
87
89
}
88
90
89
- // Calculate the indentation of a single line
90
- fn get_indent ( s : String ) -> Option < u8 > {
91
+ impl ChangeGroup {
92
+ /// Return [before](Self::before) and [after](Self::after) as `u32` ranges for use in [Sink::process_change()].
93
+ ///
94
+ /// This is useful for creating [unified diffs](crate::blob::UnifiedDiff), for example.
95
+ pub fn as_u32_ranges ( & self ) -> ( Range < u32 > , Range < u32 > ) {
96
+ (
97
+ self . before . start as u32 ..self . before . end as u32 ,
98
+ self . after . start as u32 ..self . after . end as u32 ,
99
+ )
100
+ }
101
+ }
102
+
103
+ // Calculate the indentation of a single line as number of tabs.
104
+ fn get_indent ( s : & [ u8 ] ) -> Option < u8 > {
91
105
let mut indent = 0 ;
92
106
93
- for char in s. chars ( ) {
94
- if !char. is_whitespace ( ) {
107
+ for char in s. bytes ( ) {
108
+ if !char. is_ascii_whitespace ( ) {
95
109
return Some ( indent) ;
96
- } else if char == ' ' {
110
+ } else if char == b ' ' {
97
111
indent += 1 ;
98
- } else if char == '\t' {
112
+ } else if char == b '\t' {
99
113
indent += 8 - indent % 8 ;
100
114
}
101
115
@@ -107,26 +121,21 @@ fn get_indent(s: String) -> Option<u8> {
107
121
None
108
122
}
109
123
110
- fn measure_and_score_change < T : std:: fmt:: Display > (
111
- lines : & [ Token ] ,
112
- split : usize ,
113
- interner : & Interner < T > ,
114
- score : & mut Score ,
115
- ) {
124
+ fn measure_and_score_change < T : AsRef < [ u8 ] > > ( lines : & [ Token ] , split : usize , interner : & Interner < T > , score : & mut Score ) {
116
125
// Gather information about the surroundings of the change
117
126
let end_of_file = split >= lines. len ( ) ;
118
127
let mut indent: Option < u8 > = if split >= lines. len ( ) {
119
128
None
120
129
} else {
121
- get_indent ( interner[ lines[ split] ] . to_string ( ) )
130
+ get_indent ( interner[ lines[ split] ] . as_ref ( ) )
122
131
} ;
123
132
let mut pre_blank = 0 ;
124
133
let mut pre_indent: Option < u8 > = None ;
125
134
let mut post_blank = 0 ;
126
135
let mut post_indent: Option < u8 > = None ;
127
136
128
137
for line in ( 0 ..=split. saturating_sub ( 1 ) ) . rev ( ) {
129
- pre_indent = get_indent ( interner[ lines[ line] ] . to_string ( ) ) ;
138
+ pre_indent = get_indent ( interner[ lines[ line] ] . as_ref ( ) ) ;
130
139
if pre_indent. is_none ( ) {
131
140
pre_blank += 1 ;
132
141
if pre_blank == MAX_BLANKS {
@@ -136,7 +145,7 @@ fn measure_and_score_change<T: std::fmt::Display>(
136
145
}
137
146
}
138
147
for line in split + 1 ..lines. len ( ) {
139
- post_indent = get_indent ( interner[ lines[ line] ] . to_string ( ) ) ;
148
+ post_indent = get_indent ( interner[ lines[ line] ] . as_ref ( ) ) ;
140
149
if post_indent. is_none ( ) {
141
150
post_blank += 1 ;
142
151
if post_blank == MAX_BLANKS {
@@ -191,7 +200,7 @@ fn measure_and_score_change<T: std::fmt::Display>(
191
200
192
201
impl < ' a , T > GitDiff < ' a , T >
193
202
where
194
- T : std :: fmt :: Display ,
203
+ T : AsRef < [ u8 ] > ,
195
204
{
196
205
/// Create a new instance of [`GitDiff`] that can then be passed to [`imara_diff::diff`]
197
206
/// and generate a more human-readable diff.
@@ -206,7 +215,7 @@ where
206
215
207
216
impl < T > Sink for GitDiff < ' _ , T >
208
217
where
209
- T : std :: fmt :: Display ,
218
+ T : AsRef < [ u8 ] > ,
210
219
{
211
220
type Out = Vec < ChangeGroup > ;
212
221
0 commit comments