@@ -2,12 +2,14 @@ use std::{
2
2
collections:: HashMap ,
3
3
ops:: Range ,
4
4
path:: { Path , PathBuf } ,
5
+ time:: { Duration , Instant } ,
5
6
} ;
6
7
7
8
use git2:: { Oid , Repository } ;
9
+ use ropey:: Rope ;
8
10
use similar:: DiffTag ;
9
11
10
- use crate :: { LineDiff , LineDiffs , RepoRoot } ;
12
+ use crate :: { rope :: RopeLine , LineDiff , LineDiffs , RepoRoot } ;
11
13
12
14
pub struct Git {
13
15
repo : Repository ,
@@ -17,7 +19,7 @@ pub struct Git {
17
19
18
20
/// A cache mapping absolute file paths to file contents
19
21
/// in the HEAD commit.
20
- head_cache : HashMap < PathBuf , String > ,
22
+ head_cache : HashMap < PathBuf , Rope > ,
21
23
}
22
24
23
25
impl std:: fmt:: Debug for Git {
@@ -54,7 +56,7 @@ impl Git {
54
56
path. strip_prefix ( & self . root ) . ok ( )
55
57
}
56
58
57
- pub fn read_file_from_head ( & mut self , file : & Path ) -> Option < & str > {
59
+ pub fn read_file_from_head ( & mut self , file : & Path ) -> Option < & Rope > {
58
60
let current_head = Self :: head_commit_id ( & self . repo ) ?;
59
61
// TODO: Check cache validity on events like WindowChange
60
62
// instead of on every keypress ? Will require hooks.
@@ -70,30 +72,35 @@ impl Git {
70
72
let blob = object. peel_to_blob ( ) . ok ( ) ?;
71
73
let contents = std:: str:: from_utf8 ( blob. content ( ) ) . ok ( ) ?;
72
74
self . head_cache
73
- . insert ( file. to_path_buf ( ) , contents . to_string ( ) ) ;
75
+ . insert ( file. to_path_buf ( ) , Rope :: from_str ( contents ) ) ;
74
76
}
75
77
76
- self . head_cache . get ( file) . map ( |s| s . as_str ( ) )
78
+ self . head_cache . get ( file)
77
79
}
78
80
79
- pub fn line_diff_with_head ( & mut self , file : & Path , contents : & str ) -> LineDiffs {
80
- let base = match self . read_file_from_head ( file) {
81
- Some ( b ) => b ,
81
+ pub fn line_diff_with_head ( & mut self , file : & Path , contents : & Rope ) -> LineDiffs {
82
+ let old = match self . read_file_from_head ( file) {
83
+ Some ( rope ) => RopeLine :: from_rope ( rope ) ,
82
84
None => return LineDiffs :: new ( ) ,
83
85
} ;
84
- let mut config = similar:: TextDiff :: configure ( ) ;
85
- config. timeout ( std:: time:: Duration :: from_millis ( 250 ) ) ;
86
+ let new = RopeLine :: from_rope ( contents) ;
86
87
87
88
let mut line_diffs: LineDiffs = HashMap :: new ( ) ;
88
-
89
89
let mut mark_lines = |range : Range < usize > , change : LineDiff | {
90
90
for line in range {
91
91
line_diffs. insert ( line, change) ;
92
92
}
93
93
} ;
94
94
95
- let diff = config. diff_lines ( base, contents) ;
96
- for op in diff. ops ( ) {
95
+ let timeout = Duration :: from_millis ( 250 ) ;
96
+ let diff = similar:: capture_diff_slices_deadline (
97
+ similar:: Algorithm :: Myers ,
98
+ & old,
99
+ & new,
100
+ Some ( Instant :: now ( ) + timeout) ,
101
+ ) ;
102
+
103
+ for op in diff {
97
104
let ( tag, _, line_range) = op. as_tag_tuple ( ) ;
98
105
let start = line_range. start ;
99
106
match tag {
@@ -173,15 +180,16 @@ mod test {
173
180
exec_git_cmd ( "commit -m message" , git_dir) ;
174
181
175
182
let mut git = Git :: discover_from_path ( & file) . unwrap ( ) ;
183
+ let rope = Rope :: from_str ( contents) ;
176
184
assert_eq ! (
177
- Some ( contents ) ,
185
+ Some ( & rope ) ,
178
186
git. read_file_from_head( & file) ,
179
187
"Wrong blob contents from HEAD on clean index"
180
188
) ;
181
189
182
190
fs:: write ( & file, "new text" ) . expect ( "Could not write to file" ) ;
183
191
assert_eq ! (
184
- Some ( contents ) ,
192
+ Some ( & rope ) ,
185
193
git. read_file_from_head( & file) ,
186
194
"Wrong blob contents from HEAD when index is dirty"
187
195
) ;
0 commit comments