@@ -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,18 +23,18 @@ 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 ,
26
+ pub redraw_notify : Arc < Notify > ,
28
27
pub difff_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 ( ) ,
37
+ self . redraw_notify . clone ( ) ,
39
38
self . difff_finished_notify . clone ( ) ,
40
39
)
41
40
. await ;
@@ -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
@@ -121,26 +114,34 @@ impl<'a> EventAccumulator<'a> {
121
114
122
115
* dst = Some ( event. text ) ;
123
116
124
- // 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 ) ;
117
+ // always prefer the most synchronus requested render mode
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,31 @@ 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
+ // with the choosen render stragey
159
+ match self . render_lock . take ( ) {
160
+ // diff is performed outside of the rendering loop
161
+ // request a redraw after the diff is done
162
+ None => {
160
163
tokio:: spawn ( async move {
161
164
diff_finished_notify. notified ( ) . await ;
162
- redraw_handle . 0 . notify_one ( ) ;
165
+ redraw_notify . notify_one ( ) ;
163
166
} ) ;
164
167
}
165
- RenderStrategy :: SyncWithTimeout => {
166
- let timeout = self . timeout ;
168
+ // diff is performed inside the rendering loop
169
+ // block redraw until the diff is done or the timeout is expired
170
+ Some ( RenderLock {
171
+ lock,
172
+ timeout : Some ( timeout) ,
173
+ } ) => {
167
174
tokio:: spawn ( async move {
168
175
let res = {
169
- // Acquire a lock on the redraw handle.
170
- // The lock will block the rendering from occurring while held.
176
+ // Aquire a lock on the redraw handle.
177
+ // The lock will block the rendering from occuring while held.
171
178
// The rendering waits for the diff if it doesn't time out
172
- let _render_guard = redraw_handle. 1 . read ( ) ;
173
179
timeout_at ( timeout, diff_finished_notify. notified ( ) ) . await
174
180
} ;
181
+ // we either reached the timeout or the diff is finished, release the render lock
182
+ drop ( lock) ;
175
183
if res. is_ok ( ) {
176
184
// Diff finished in time we are done.
177
185
return ;
@@ -180,15 +188,19 @@ impl<'a> EventAccumulator<'a> {
180
188
// and wait until the diff occurs to trigger an async redraw
181
189
log:: warn!( "Diff computation timed out, update of diffs might appear delayed" ) ;
182
190
diff_finished_notify. notified ( ) . await ;
183
- redraw_handle . 0 . notify_one ( ) ;
191
+ redraw_notify . notify_one ( ) ;
184
192
} ) ;
185
193
}
186
- RenderStrategy :: Sync => {
194
+ // a blocking diff is performed inside the rendering loop
195
+ // block redraw until the diff is done
196
+ Some ( RenderLock {
197
+ lock,
198
+ timeout : None ,
199
+ } ) => {
187
200
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
201
+ diff_finished_notify. notified ( ) . await ;
202
+ // diff is done release the lock
203
+ drop ( lock)
192
204
} ) ;
193
205
}
194
206
} ;
0 commit comments