@@ -56,6 +56,72 @@ pub(super) fn diff_lines(original: &str, updated: &str) -> Vec<Line<'static>> {
56
56
lines
57
57
}
58
58
59
+ /// Attempts, where possible to trim the supplied diff to less than `max_lines`.
60
+ pub ( super ) fn remove_excess_context ( lines : & mut Vec < Line > , max_lines : usize ) {
61
+ struct ContextBlock {
62
+ start : usize ,
63
+ length : usize ,
64
+ to_take : usize ,
65
+ }
66
+
67
+ let mut blocks = Vec :: new ( ) ;
68
+ let mut current_block = None ;
69
+ for ( offset, line) in lines. iter ( ) . enumerate ( ) {
70
+ let is_context = line
71
+ . spans
72
+ . first ( )
73
+ . map ( |span| span. content . starts_with ( ' ' ) )
74
+ . unwrap_or ( false ) ;
75
+ if is_context {
76
+ current_block
77
+ . get_or_insert ( ContextBlock {
78
+ start : offset,
79
+ length : 0 ,
80
+ to_take : 0 ,
81
+ } )
82
+ . length += 1 ;
83
+ } else if let Some ( block) = current_block. take ( ) {
84
+ blocks. push ( block) ;
85
+ }
86
+ }
87
+ blocks. extend ( current_block) ;
88
+
89
+ let mut to_reclaim = ( lines. len ( ) as isize ) . saturating_sub ( max_lines as isize ) ;
90
+ while to_reclaim > 0 {
91
+ if let Some ( block) = blocks. iter_mut ( ) . max_by_key ( |b| b. length - b. to_take ) {
92
+ if block. length - block. to_take == 0 {
93
+ // We've run out of context to remove.
94
+ break ;
95
+ }
96
+ if block. to_take > 0 {
97
+ to_reclaim -= 1 ;
98
+ }
99
+ block. to_take += 1 ;
100
+ }
101
+ }
102
+
103
+ let old_lines = std:: mem:: take ( lines) ;
104
+ let mut block_index = 0 ;
105
+ blocks. retain ( |block| block. to_take > 0 ) ;
106
+ for ( offset, line) in old_lines. into_iter ( ) . enumerate ( ) {
107
+ let Some ( block) = blocks. get ( block_index) else {
108
+ lines. push ( line) ;
109
+ continue ;
110
+ } ;
111
+ let skip_start = block. start + ( block. length - block. to_take ) / 2 ;
112
+ let skip_end = skip_start + block. to_take ;
113
+ if offset < skip_start {
114
+ lines. push ( line) ;
115
+ continue ;
116
+ }
117
+ if offset >= skip_end {
118
+ lines. push ( Line :: from ( "..." ) ) ;
119
+ lines. push ( line) ;
120
+ block_index += 1 ;
121
+ }
122
+ }
123
+ }
124
+
59
125
#[ test]
60
126
fn test_diff_lines ( ) {
61
127
fn line_to_string ( line : & Line ) -> String {
@@ -115,3 +181,75 @@ fn test_diff_lines() {
115
181
] ;
116
182
assert_eq ! ( lines, expected) ;
117
183
}
184
+
185
+ #[ test]
186
+ fn test_remove_excess_context ( ) {
187
+ fn to_lines ( text : & str ) -> Vec < Line < ' static > > {
188
+ text. lines ( )
189
+ . filter_map ( |l| {
190
+ // Ignore @ - it's just there to tell indoc where the left margin is.
191
+ if !l. starts_with ( '@' ) {
192
+ Some ( Line :: from ( l. to_owned ( ) ) )
193
+ } else {
194
+ None
195
+ }
196
+ } )
197
+ . collect ( )
198
+ }
199
+
200
+ fn to_text ( lines : & [ Line ] ) -> String {
201
+ lines
202
+ . iter ( )
203
+ . map ( |line| {
204
+ line. spans
205
+ . iter ( )
206
+ . map ( |s| s. content . as_ref ( ) )
207
+ . collect :: < Vec < _ > > ( )
208
+ . join ( "" )
209
+ } )
210
+ . collect :: < Vec < _ > > ( )
211
+ . join ( "\n " )
212
+ }
213
+
214
+ use indoc:: indoc;
215
+
216
+ let mut lines = to_lines ( indoc ! { r#"
217
+ @
218
+ [common]
219
+ -version = 1
220
+ +version = 2
221
+ a
222
+ b
223
+ c
224
+ d
225
+ e
226
+ f
227
+ g
228
+ h
229
+ i
230
+ j
231
+ k
232
+ +foo = 1
233
+ l
234
+ m
235
+ n
236
+ "# } ) ;
237
+ remove_excess_context ( & mut lines, 11 ) ;
238
+ assert_eq ! (
239
+ to_text( & lines) ,
240
+ to_text( & to_lines( indoc! { r#"
241
+ @
242
+ [common]
243
+ -version = 1
244
+ +version = 2
245
+ a
246
+ ...
247
+ j
248
+ k
249
+ +foo = 1
250
+ l
251
+ m
252
+ n
253
+ "# } ) )
254
+ ) ;
255
+ }
0 commit comments