1- use anyhow:: { Context as _, Result } ;
1+ use anyhow:: { Context as _, Result , bail } ;
22use but_core:: { TreeStatusKind , ref_metadata:: StackId } ;
33
44use crate :: utils:: PaniclessSubtraction ;
55
66/// A struct for tracking what stack and commit a hunk belongs to as its line numbers shift with
77/// new changes come in from other commits and/or stacks.
8- #[ derive( Debug , Clone , Copy , PartialEq ) ]
8+ #[ derive( Debug , Clone , PartialEq ) ]
99pub struct HunkRange {
1010 /// The kind of change that was performed on the path of the parent-diff.
1111 pub change_type : TreeStatusKind ,
@@ -21,7 +21,117 @@ pub struct HunkRange {
2121 pub line_shift : i32 ,
2222}
2323
24+ fn get_left_right ( start : u32 , lines : u32 ) -> Result < ( i32 , i32 ) > {
25+ let left = if lines == 0 { start } else { start - 1 } ;
26+ Ok ( ( i32:: try_from ( left) ?, i32:: try_from ( left + lines) ?) )
27+ }
28+
29+ fn set_left_right ( hunk_range : & mut HunkRange , left : i32 , right : i32 ) -> Result < ( ) > {
30+ hunk_range. lines = u32:: try_from ( right - left) ?;
31+ hunk_range. start = u32:: try_from ( if hunk_range. lines == 0 {
32+ left
33+ } else {
34+ left + 1
35+ } ) ?;
36+ Ok ( ( ) )
37+ }
38+
39+ pub ( crate ) struct ReceiveResult {
40+ pub left : Option < HunkRange > ,
41+ pub incoming_line_shift_change : i32 ,
42+ pub right : Option < HunkRange > ,
43+ }
44+
2445impl HunkRange {
46+ pub ( crate ) fn receive (
47+ mut self ,
48+ incoming_start : u32 ,
49+ incoming_lines : u32 ,
50+ ) -> Result < ReceiveResult > {
51+ let ( incoming_left, incoming_right) = get_left_right ( incoming_start, incoming_lines) ?;
52+ let ( existing_left, existing_right) = get_left_right ( self . start , self . lines ) ?;
53+ let existing_lines = self . lines ;
54+ let existing_line_shift = self . line_shift ;
55+
56+ // Calculate if part or all of self will remain to the left of incoming
57+ // existing L R
58+ // incoming left 1-><-2-><-3
59+ let mut left_existing = if incoming_left <= existing_left {
60+ // 1. There will be no hunk to the left of incoming.
61+ None
62+ } else if incoming_left < existing_right {
63+ // 2. There will be a trimmed hunk to the left of incoming.
64+ let mut left_existing = self . clone ( ) ;
65+ set_left_right ( & mut left_existing, existing_left, incoming_left) ?;
66+ Some ( left_existing)
67+ } else {
68+ // 3. There will be an untrimmed hunk to the left of incoming.
69+ Some ( self . clone ( ) )
70+ } ;
71+
72+ // Calculate if part or all of self will remain to the right of incoming
73+ // existing L R
74+ // incoming right 3-><-2-><-1
75+ let mut right_existing = if incoming_right >= existing_right {
76+ // 1. There will be no hunk to the right of incoming.
77+ None
78+ } else if incoming_right > existing_left {
79+ // 2. There will be a trimmed hunk to the right of incoming.
80+ set_left_right ( & mut self , incoming_right, existing_right) ?;
81+ Some ( self )
82+ } else {
83+ // 3. There will be an untrimmed hunk to the right of incoming.
84+ Some ( self )
85+ } ;
86+
87+ let incoming_line_shift_change = if let ( None , None ) = ( & left_existing, & right_existing) {
88+ // No trace of existing hunk range, so attribute all its line shift
89+ // to the incoming hunk range.
90+ existing_line_shift
91+ } else {
92+ // Deduct trimmed lines from the `line_shift` of existing hunk(s) and
93+ // add them to the `line_shift` of incoming
94+ fn get_lines ( hunk_range_option : & Option < HunkRange > ) -> u32 {
95+ hunk_range_option
96+ . as_ref ( )
97+ . map_or ( 0 , |hunk_range| hunk_range. lines )
98+ }
99+ let existing_lines_after_trimming =
100+ get_lines ( & left_existing) + get_lines ( & right_existing) ;
101+ let Some ( trimmed_lines) = existing_lines. checked_sub ( existing_lines_after_trimming)
102+ else {
103+ bail ! ( "when calculating trimmed lines" )
104+ } ;
105+ let trimmed_lines = i32:: try_from ( trimmed_lines) ?;
106+ if let ( Some ( left_existing) , Some ( right_existing) ) =
107+ ( & mut left_existing, & mut right_existing)
108+ {
109+ // Here, both `left_existing` and `right_existing` will have the original
110+ // `line_shift` from `existing_hunk_range`.
111+ // Distribute proportionally according to their `lines`.
112+ let total_lines = i32:: try_from ( left_existing. lines + right_existing. lines ) ?;
113+ let total_net_line_shift = left_existing. line_shift - trimmed_lines;
114+ left_existing. line_shift = if total_lines == 0 {
115+ // Arbitrarily split equally to avoid a division by zero.
116+ total_net_line_shift / 2
117+ } else {
118+ total_net_line_shift * i32:: try_from ( left_existing. lines ) ? / total_lines
119+ } ;
120+ right_existing. line_shift = total_net_line_shift - left_existing. line_shift ;
121+ } else if let Some ( left_existing) = & mut left_existing {
122+ left_existing. line_shift -= trimmed_lines;
123+ } else if let Some ( right_existing) = & mut right_existing {
124+ right_existing. line_shift -= trimmed_lines;
125+ }
126+ trimmed_lines
127+ } ;
128+ Ok ( ReceiveResult {
129+ left : left_existing,
130+ incoming_line_shift_change,
131+ right : right_existing,
132+ } )
133+ }
134+
25135 /// See if this range intersects with the hunk identified with `start` and `lines`.
26136 pub ( crate ) fn intersects ( & self , start : u32 , lines : u32 ) -> Result < bool > {
27137 if self . change_type == TreeStatusKind :: Deletion {
@@ -64,46 +174,4 @@ impl HunkRange {
64174
65175 Ok ( self . start <= incoming_last_line && last_line >= start)
66176 }
67-
68- pub ( crate ) fn contains ( & self , start : u32 , lines : u32 ) -> bool {
69- if lines == 0 {
70- // Special case when only adding lines.
71- return self . start <= start && self . start + self . lines > start + 1 ;
72- }
73- start > self . start && start + lines <= self . start + self . lines
74- }
75-
76- pub ( crate ) fn covered_by ( & self , start : u32 , lines : u32 ) -> bool {
77- if start == 0 && lines == 0 {
78- // Special when adding lines at the top of the file.
79- return false ;
80- }
81- self . start >= start && self . start + self . lines <= start + lines
82- }
83-
84- pub ( crate ) fn precedes ( & self , start : u32 ) -> Result < bool > {
85- let last_line = ( self . start + self . lines )
86- . sub_or_err ( 1 )
87- . context ( "While calculating the last line" ) ?;
88-
89- Ok ( last_line < start)
90- }
91-
92- pub ( crate ) fn follows ( & self , start : u32 , lines : u32 ) -> Result < bool > {
93- if start == 0 && lines == 0 {
94- // Special case when adding lines at the top of the file.
95- return Ok ( true ) ;
96- }
97-
98- if lines == 0 {
99- // Special case when only adding lines.
100- return Ok ( self . start > start) ;
101- }
102-
103- let incoming_last_line = ( start + lines)
104- . sub_or_err ( 1 )
105- . context ( "While calculating the last line of the incoming hunk" ) ?;
106-
107- Ok ( self . start > incoming_last_line)
108- }
109177}
0 commit comments