11//! Facilities to produce git-formatted diffs.
22
3- use std:: cmp:: Ordering ;
4- use std:: ops:: Range ;
5-
63use crate :: blob:: GitDiff ;
4+ use bstr:: ByteSlice ;
75use imara_diff:: intern:: { InternedInput , Interner , Token } ;
86use imara_diff:: Sink ;
7+ use std:: cmp:: Ordering ;
8+ use std:: ops:: Range ;
99
1010// Explanation for the following numbers can be found here:
1111// https://github.com/git/git/blob/324fbaab88126196bd42e7fa383ee94e165d61b5/xdiff/xdiffi.c#L535
@@ -29,9 +29,11 @@ pub(super) mod types {
2929 use crate :: blob:: git_diff:: ChangeGroup ;
3030
3131 /// 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.
3234 pub struct GitDiff < ' a , T >
3335 where
34- T : std :: fmt :: Display ,
36+ T : AsRef < [ u8 ] > ,
3537 {
3638 pub ( crate ) after : & ' a [ imara_diff:: intern:: Token ] ,
3739 pub ( crate ) interner : & ' a imara_diff:: intern:: Interner < T > ,
@@ -75,7 +77,7 @@ impl PartialOrd for Score {
7577#[ derive( PartialEq , Debug ) ]
7678pub struct ChangeGroup {
7779 /// 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
7981 /// the [`InternedInput`].
8082 pub before : Range < usize > ,
8183 /// Range indicating the lines of the new block
@@ -86,16 +88,28 @@ pub struct ChangeGroup {
8688 pub change_kind : ChangeKind ,
8789}
8890
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 > {
91105 let mut indent = 0 ;
92106
93- for char in s. chars ( ) {
94- if !char. is_whitespace ( ) {
107+ for char in s. bytes ( ) {
108+ if !char. is_ascii_whitespace ( ) {
95109 return Some ( indent) ;
96- } else if char == ' ' {
110+ } else if char == b ' ' {
97111 indent += 1 ;
98- } else if char == '\t' {
112+ } else if char == b '\t' {
99113 indent += 8 - indent % 8 ;
100114 }
101115
@@ -107,26 +121,21 @@ fn get_indent(s: String) -> Option<u8> {
107121 None
108122}
109123
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 ) {
116125 // Gather information about the surroundings of the change
117126 let end_of_file = split >= lines. len ( ) ;
118127 let mut indent: Option < u8 > = if split >= lines. len ( ) {
119128 None
120129 } else {
121- get_indent ( interner[ lines[ split] ] . to_string ( ) )
130+ get_indent ( interner[ lines[ split] ] . as_ref ( ) )
122131 } ;
123132 let mut pre_blank = 0 ;
124133 let mut pre_indent: Option < u8 > = None ;
125134 let mut post_blank = 0 ;
126135 let mut post_indent: Option < u8 > = None ;
127136
128137 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 ( ) ) ;
130139 if pre_indent. is_none ( ) {
131140 pre_blank += 1 ;
132141 if pre_blank == MAX_BLANKS {
@@ -136,7 +145,7 @@ fn measure_and_score_change<T: std::fmt::Display>(
136145 }
137146 }
138147 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 ( ) ) ;
140149 if post_indent. is_none ( ) {
141150 post_blank += 1 ;
142151 if post_blank == MAX_BLANKS {
@@ -191,7 +200,7 @@ fn measure_and_score_change<T: std::fmt::Display>(
191200
192201impl < ' a , T > GitDiff < ' a , T >
193202where
194- T : std :: fmt :: Display ,
203+ T : AsRef < [ u8 ] > ,
195204{
196205 /// Create a new instance of [`GitDiff`] that can then be passed to [`imara_diff::diff`]
197206 /// and generate a more human-readable diff.
@@ -206,7 +215,7 @@ where
206215
207216impl < T > Sink for GitDiff < ' _ , T >
208217where
209- T : std :: fmt :: Display ,
218+ T : AsRef < [ u8 ] > ,
210219{
211220 type Out = Vec < ChangeGroup > ;
212221
0 commit comments