1
1
use super :: {
2
+ utils:: scroll_horizontal:: HorizontalScroll ,
2
3
utils:: scroll_vertical:: VerticalScroll , CommandBlocking ,
3
- Direction , DrawableComponent , ScrollType ,
4
+ Direction , DrawableComponent , HorizontalScrollType , ScrollType ,
4
5
} ;
5
6
use crate :: {
6
7
components:: { CommandInfo , Component , EventState } ,
7
8
keys:: { key_match, SharedKeyConfig } ,
8
9
queue:: { Action , InternalEvent , NeedsUpdate , Queue , ResetItem } ,
9
10
string_utils:: tabs_to_spaces,
11
+ string_utils:: trim_offset,
10
12
strings, try_or_popup,
11
13
ui:: style:: SharedTheme ,
12
14
} ;
@@ -102,13 +104,15 @@ impl Selection {
102
104
pub struct DiffComponent {
103
105
repo : RepoPathRef ,
104
106
diff : Option < FileDiff > ,
107
+ longest_line : usize ,
105
108
pending : bool ,
106
109
selection : Selection ,
107
110
selected_hunk : Option < usize > ,
108
111
current_size : Cell < ( u16 , u16 ) > ,
109
112
focused : bool ,
110
113
current : Current ,
111
- scroll : VerticalScroll ,
114
+ vertical_scroll : VerticalScroll ,
115
+ horizontal_scroll : HorizontalScroll ,
112
116
queue : Queue ,
113
117
theme : SharedTheme ,
114
118
key_config : SharedKeyConfig ,
@@ -131,9 +135,11 @@ impl DiffComponent {
131
135
pending : false ,
132
136
selected_hunk : None ,
133
137
diff : None ,
138
+ longest_line : 0 ,
134
139
current_size : Cell :: new ( ( 0 , 0 ) ) ,
135
140
selection : Selection :: Single ( 0 ) ,
136
- scroll : VerticalScroll :: new ( ) ,
141
+ vertical_scroll : VerticalScroll :: new ( ) ,
142
+ horizontal_scroll : HorizontalScroll :: new ( ) ,
137
143
theme,
138
144
key_config,
139
145
is_immutable,
@@ -155,7 +161,9 @@ impl DiffComponent {
155
161
pub fn clear ( & mut self , pending : bool ) {
156
162
self . current = Current :: default ( ) ;
157
163
self . diff = None ;
158
- self . scroll . reset ( ) ;
164
+ self . longest_line = 0 ;
165
+ self . vertical_scroll . reset ( ) ;
166
+ self . horizontal_scroll . reset ( ) ;
159
167
self . selection = Selection :: Single ( 0 ) ;
160
168
self . selected_hunk = None ;
161
169
self . pending = pending;
@@ -182,8 +190,27 @@ impl DiffComponent {
182
190
183
191
self . diff = Some ( diff) ;
184
192
193
+ self . longest_line = self
194
+ . diff
195
+ . iter ( )
196
+ . flat_map ( |diff| diff. hunks . iter ( ) )
197
+ . flat_map ( |hunk| hunk. lines . iter ( ) )
198
+ . map ( |line| {
199
+ let converted_content = tabs_to_spaces (
200
+ line. content . as_ref ( ) . to_string ( ) ,
201
+ ) ;
202
+
203
+ converted_content. len ( )
204
+ } )
205
+ . max ( )
206
+ . map_or ( 0 , |len| {
207
+ // Each hunk uses a 1-character wide vertical bar to its left to indicate
208
+ // selection.
209
+ len + 1
210
+ } ) ;
211
+
185
212
if reset_selection {
186
- self . scroll . reset ( ) ;
213
+ self . vertical_scroll . reset ( ) ;
187
214
self . selection = Selection :: Single ( 0 ) ;
188
215
self . update_selection ( 0 ) ;
189
216
} else {
@@ -241,6 +268,11 @@ impl DiffComponent {
241
268
self . diff . as_ref ( ) . map_or ( 0 , |diff| diff. lines )
242
269
}
243
270
271
+ fn max_scroll_right ( & self ) -> usize {
272
+ self . longest_line
273
+ . saturating_sub ( self . current_size . get ( ) . 0 . into ( ) )
274
+ }
275
+
244
276
fn modify_selection ( & mut self , direction : Direction ) {
245
277
if self . diff . is_some ( ) {
246
278
self . selection . modify ( direction, self . lines_count ( ) ) ;
@@ -340,7 +372,7 @@ impl DiffComponent {
340
372
Span :: raw( Cow :: from( ")" ) ) ,
341
373
] ) ] ) ;
342
374
} else {
343
- let min = self . scroll . get_top ( ) ;
375
+ let min = self . vertical_scroll . get_top ( ) ;
344
376
let max = min + height as usize ;
345
377
346
378
let mut line_cursor = 0_usize ;
@@ -378,6 +410,8 @@ impl DiffComponent {
378
410
hunk_selected,
379
411
i == hunk_len - 1 ,
380
412
& self . theme ,
413
+ self . horizontal_scroll
414
+ . get_right ( ) ,
381
415
) ) ;
382
416
lines_added += 1 ;
383
417
}
@@ -400,6 +434,7 @@ impl DiffComponent {
400
434
selected_hunk : bool ,
401
435
end_of_hunk : bool ,
402
436
theme : & SharedTheme ,
437
+ scrolled_right : usize ,
403
438
) -> Spans < ' a > {
404
439
let style = theme. diff_hunk_marker ( selected_hunk) ;
405
440
@@ -418,18 +453,22 @@ impl DiffComponent {
418
453
}
419
454
} ;
420
455
456
+ let content =
457
+ tabs_to_spaces ( line. content . as_ref ( ) . to_string ( ) ) ;
458
+ let content = trim_offset ( & content, scrolled_right) ;
459
+
421
460
let filled = if selected {
422
461
// selected line
423
- format ! ( "{:w$}\n " , line . content , w = width as usize )
462
+ format ! ( "{content :w$}\n " , w = width as usize )
424
463
} else {
425
464
// weird eof missing eol line
426
- format ! ( "{}\n " , line . content )
465
+ format ! ( "{content }\n " )
427
466
} ;
428
467
429
468
Spans :: from ( vec ! [
430
469
left_side_of_line,
431
470
Span :: styled(
432
- Cow :: from( tabs_to_spaces ( filled) ) ,
471
+ Cow :: from( filled) ,
433
472
theme. diff_line( line. line_type, selected) ,
434
473
) ,
435
474
] )
@@ -606,14 +645,20 @@ impl DrawableComponent for DiffComponent {
606
645
r. height . saturating_sub ( 2 ) ,
607
646
) ) ;
608
647
648
+ let current_width = self . current_size . get ( ) . 0 ;
609
649
let current_height = self . current_size . get ( ) . 1 ;
610
650
611
- self . scroll . update (
651
+ self . vertical_scroll . update (
612
652
self . selection . get_end ( ) ,
613
653
self . lines_count ( ) ,
614
654
usize:: from ( current_height) ,
615
655
) ;
616
656
657
+ self . horizontal_scroll . update_no_selection (
658
+ self . longest_line ,
659
+ current_width. into ( ) ,
660
+ ) ;
661
+
617
662
let title = format ! (
618
663
"{}{}" ,
619
664
strings:: title_diff( & self . key_config) ,
@@ -643,7 +688,11 @@ impl DrawableComponent for DiffComponent {
643
688
) ;
644
689
645
690
if self . focused ( ) {
646
- self . scroll . draw ( f, r, & self . theme ) ;
691
+ self . vertical_scroll . draw ( f, r, & self . theme ) ;
692
+
693
+ if self . max_scroll_right ( ) > 0 {
694
+ self . horizontal_scroll . draw ( f, r, & self . theme ) ;
695
+ }
647
696
}
648
697
649
698
Ok ( ( ) )
@@ -754,6 +803,18 @@ impl Component for DiffComponent {
754
803
{
755
804
self . move_selection ( ScrollType :: PageDown ) ;
756
805
Ok ( EventState :: Consumed )
806
+ } else if key_match (
807
+ e,
808
+ self . key_config . keys . move_right ,
809
+ ) {
810
+ self . horizontal_scroll
811
+ . move_right ( HorizontalScrollType :: Right ) ;
812
+ Ok ( EventState :: Consumed )
813
+ } else if key_match ( e, self . key_config . keys . move_left )
814
+ {
815
+ self . horizontal_scroll
816
+ . move_right ( HorizontalScrollType :: Left ) ;
817
+ Ok ( EventState :: Consumed )
757
818
} else if key_match (
758
819
e,
759
820
self . key_config . keys . stage_unstage_item ,
0 commit comments