@@ -6,12 +6,11 @@ use helix_core::{Rope, RopeSlice};
6
6
use imara_diff:: intern:: InternedInput ;
7
7
use parking_lot:: Mutex ;
8
8
use tokio:: sync:: mpsc:: UnboundedReceiver ;
9
- use tokio:: sync:: { Notify , RwLockReadGuard } ;
10
- use tokio:: time:: { timeout, timeout_at, Duration , Instant } ;
9
+ use tokio:: sync:: Notify ;
10
+ use tokio:: time:: { timeout, timeout_at, Duration } ;
11
11
12
12
use crate :: diff:: {
13
- Event , RedrawHandle , RenderStrategy , ALGORITHM , DIFF_DEBOUNCE_TIME_ASYNC ,
14
- DIFF_DEBOUNCE_TIME_SYNC , SYNC_DIFF_TIMEOUT ,
13
+ Event , RenderLock , ALGORITHM , DIFF_DEBOUNCE_TIME_ASYNC , DIFF_DEBOUNCE_TIME_SYNC ,
15
14
} ;
16
15
17
16
use super :: line_cache:: InternedRopeLines ;
@@ -24,19 +23,19 @@ pub(super) struct DiffWorker {
24
23
pub channel : UnboundedReceiver < Event > ,
25
24
pub hunks : Arc < Mutex < Vec < Hunk > > > ,
26
25
pub new_hunks : Vec < Hunk > ,
27
- pub redraw_handle : RedrawHandle ,
28
- pub difff_finished_notify : Arc < Notify > ,
26
+ pub redraw_notify : Arc < Notify > ,
27
+ pub diff_finished_notify : Arc < Notify > ,
29
28
}
30
29
31
30
impl DiffWorker {
32
31
async fn accumulate_events ( & mut self , event : Event ) -> ( Option < Rope > , Option < Rope > ) {
33
- let mut accumulator = EventAccumulator :: new ( & self . redraw_handle ) ;
32
+ let mut accumulator = EventAccumulator :: new ( ) ;
34
33
accumulator. handle_event ( event) . await ;
35
34
accumulator
36
35
. accumulate_debounced_events (
37
36
& mut self . channel ,
38
- self . redraw_handle . clone ( ) ,
39
- self . difff_finished_notify . clone ( ) ,
37
+ self . redraw_notify . clone ( ) ,
38
+ self . diff_finished_notify . clone ( ) ,
40
39
)
41
40
. await ;
42
41
( accumulator. doc , accumulator. diff_base )
@@ -80,7 +79,7 @@ impl DiffWorker {
80
79
/// To improve performance this function tries to reuse the allocation of the old diff previously stored in `self.line_diffs`
81
80
fn apply_hunks ( & mut self ) {
82
81
swap ( & mut * self . hunks . lock ( ) , & mut self . new_hunks ) ;
83
- self . difff_finished_notify . notify_waiters ( ) ;
82
+ self . diff_finished_notify . notify_waiters ( ) ;
84
83
self . new_hunks . clear ( ) ;
85
84
}
86
85
@@ -91,24 +90,18 @@ impl DiffWorker {
91
90
}
92
91
}
93
92
94
- struct EventAccumulator < ' a > {
93
+ struct EventAccumulator {
95
94
diff_base : Option < Rope > ,
96
95
doc : Option < Rope > ,
97
- render_stratagey : RenderStrategy ,
98
- redraw_handle : & ' a RedrawHandle ,
99
- render_lock : Option < RwLockReadGuard < ' a , ( ) > > ,
100
- timeout : Instant ,
96
+ render_lock : Option < RenderLock > ,
101
97
}
102
98
103
- impl < ' a > EventAccumulator < ' a > {
104
- fn new ( redraw_handle : & ' a RedrawHandle ) -> EventAccumulator < ' a > {
99
+ impl < ' a > EventAccumulator {
100
+ fn new ( ) -> EventAccumulator {
105
101
EventAccumulator {
106
102
diff_base : None ,
107
103
doc : None ,
108
- render_stratagey : RenderStrategy :: Async ,
109
104
render_lock : None ,
110
- redraw_handle,
111
- timeout : Instant :: now ( ) ,
112
105
}
113
106
}
114
107
@@ -122,25 +115,33 @@ impl<'a> EventAccumulator<'a> {
122
115
* dst = Some ( event. text ) ;
123
116
124
117
// always prefer the most synchronous requested render mode
125
- if event. render_strategy > self . render_stratagey {
126
- if self . render_lock . is_none ( ) {
127
- self . timeout = Instant :: now ( ) + Duration :: from_millis ( SYNC_DIFF_TIMEOUT ) ;
128
- self . render_lock = Some ( self . redraw_handle . 1 . read ( ) . await ) ;
118
+ if let Some ( render_lock) = event. render_lock {
119
+ match & mut self . render_lock {
120
+ Some ( RenderLock { timeout, .. } ) => {
121
+ // A timeout of `None` means that the render should
122
+ // always wait for the diff to complete (so no timeout)
123
+ // remove the existing timeout, otherwise keep the previous timeout
124
+ // because it will be shorter then the current timeout
125
+ if render_lock. timeout . is_none ( ) {
126
+ timeout. take ( ) ;
127
+ }
128
+ }
129
+ None => self . render_lock = Some ( render_lock) ,
129
130
}
130
- self . render_stratagey = event. render_strategy
131
131
}
132
132
}
133
133
134
134
async fn accumulate_debounced_events (
135
135
& mut self ,
136
136
channel : & mut UnboundedReceiver < Event > ,
137
- redraw_handle : RedrawHandle ,
137
+ redraw_notify : Arc < Notify > ,
138
138
diff_finished_notify : Arc < Notify > ,
139
139
) {
140
140
let async_debounce = Duration :: from_millis ( DIFF_DEBOUNCE_TIME_ASYNC ) ;
141
141
let sync_debounce = Duration :: from_millis ( DIFF_DEBOUNCE_TIME_SYNC ) ;
142
142
loop {
143
- let debounce = if self . render_stratagey == RenderStrategy :: Async {
143
+ // if we are not blocking rendering use a much longer timeout
144
+ let debounce = if self . render_lock . is_none ( ) {
144
145
async_debounce
145
146
} else {
146
147
sync_debounce
@@ -154,24 +155,30 @@ impl<'a> EventAccumulator<'a> {
154
155
}
155
156
156
157
// setup task to trigger the rendering
157
- // with the chosen render stragey
158
- match self . render_stratagey {
159
- RenderStrategy :: Async => {
158
+ match self . render_lock . take ( ) {
159
+ // diff is performed outside of the rendering loop
160
+ // request a redraw after the diff is done
161
+ None => {
160
162
tokio:: spawn ( async move {
161
163
diff_finished_notify. notified ( ) . await ;
162
- redraw_handle . 0 . notify_one ( ) ;
164
+ redraw_notify . notify_one ( ) ;
163
165
} ) ;
164
166
}
165
- RenderStrategy :: SyncWithTimeout => {
166
- let timeout = self . timeout ;
167
+ // diff is performed inside the rendering loop
168
+ // block redraw until the diff is done or the timeout is expired
169
+ Some ( RenderLock {
170
+ lock,
171
+ timeout : Some ( timeout) ,
172
+ } ) => {
167
173
tokio:: spawn ( async move {
168
174
let res = {
169
175
// Acquire a lock on the redraw handle.
170
176
// The lock will block the rendering from occurring while held.
171
177
// The rendering waits for the diff if it doesn't time out
172
- let _render_guard = redraw_handle. 1 . read ( ) ;
173
178
timeout_at ( timeout, diff_finished_notify. notified ( ) ) . await
174
179
} ;
180
+ // we either reached the timeout or the diff is finished, release the render lock
181
+ drop ( lock) ;
175
182
if res. is_ok ( ) {
176
183
// Diff finished in time we are done.
177
184
return ;
@@ -180,15 +187,19 @@ impl<'a> EventAccumulator<'a> {
180
187
// and wait until the diff occurs to trigger an async redraw
181
188
log:: warn!( "Diff computation timed out, update of diffs might appear delayed" ) ;
182
189
diff_finished_notify. notified ( ) . await ;
183
- redraw_handle . 0 . notify_one ( ) ;
190
+ redraw_notify . notify_one ( ) ;
184
191
} ) ;
185
192
}
186
- RenderStrategy :: Sync => {
193
+ // a blocking diff is performed inside the rendering loop
194
+ // block redraw until the diff is done
195
+ Some ( RenderLock {
196
+ lock,
197
+ timeout : None ,
198
+ } ) => {
187
199
tokio:: spawn ( async move {
188
- // Aquire a lock on the redraw handle.
189
- // The lock will block the rendering from occuring while held.
190
- let _render_guard = redraw_handle. 1 . read ( ) ;
191
- diff_finished_notify. notified ( ) . await
200
+ diff_finished_notify. notified ( ) . await ;
201
+ // diff is done release the lock
202
+ drop ( lock)
192
203
} ) ;
193
204
}
194
205
} ;
0 commit comments