1use std::{hash::Hash, sync::Arc, time::Duration};
13
14use gpui_kit::{Context, Task};
15use rustc_hash::FxHashMap;
16use web_time::Instant;
17
18use super::{
19 ColumnOf, KeyOf, LiveGridDelegate, LiveGridState,
20 edit::{EditError, EditId, EditState},
21};
22use crate::{
23 model::{GridRow, OwnedValue},
24 store::StoreSnapshot,
25};
26
27pub(crate) struct PendingValues<K, C> {
31 rows: FxHashMap<K, Box<[(C, OwnedValue)]>>,
32}
33
34impl<K, C> Default for PendingValues<K, C> {
35 fn default() -> Self {
36 Self {
37 rows: FxHashMap::default(),
38 }
39 }
40}
41
42impl<K: Eq + Hash, C> PendingValues<K, C> {
43 pub(crate) fn is_empty(&self) -> bool {
45 self.rows.is_empty()
46 }
47
48 pub(crate) fn get(&self, key: &K) -> Option<&[(C, OwnedValue)]> {
50 self.rows.get(key).map(|values| &**values)
51 }
52}
53
54struct PendingEdit {
56 id: EditId,
57 value: OwnedValue,
58 state: EditState,
60 accepted: Option<(Instant, OwnedValue)>,
62}
63
64struct CellEdits<C> {
67 column: C,
68 edits: Vec<PendingEdit>,
69 failed: Option<(EditState, Option<Instant>)>,
71}
72
73impl<C> CellEdits<C> {
74 fn is_empty(&self) -> bool {
75 self.edits.is_empty() && self.failed.is_none()
76 }
77
78 fn newest(&self) -> Option<&PendingEdit> {
80 self.edits.last()
81 }
82}
83
84#[derive(Clone, Copy)]
86pub(super) struct CellEditMarks<'a> {
87 pub(super) value: Option<&'a OwnedValue>,
89 pub(super) state: Option<&'a EditState>,
91 pub(super) pending: bool,
93 pub(super) failed: bool,
95}
96
97pub(super) struct PendingEdits<K, C> {
100 rows: FxHashMap<K, Vec<CellEdits<C>>>,
101 ids: FxHashMap<EditId, (K, C)>,
103 values: Arc<PendingValues<K, C>>,
106 timer: Option<(Instant, Task<()>)>,
108}
109
110impl<K, C> Default for PendingEdits<K, C> {
111 fn default() -> Self {
112 Self {
113 rows: FxHashMap::default(),
114 ids: FxHashMap::default(),
115 values: Arc::new(PendingValues::default()),
116 timer: None,
117 }
118 }
119}
120
121impl<K: Clone + Eq + Hash, C: PartialEq + Copy> PendingEdits<K, C> {
122 pub(super) fn is_empty(&self) -> bool {
125 self.rows.is_empty()
126 }
127
128 fn cell(&self, key: &K, column: C) -> Option<&CellEdits<C>> {
129 self.rows
130 .get(key)?
131 .iter()
132 .find(|cell| cell.column == column)
133 }
134
135 fn cell_mut(&mut self, key: &K, column: C) -> &mut CellEdits<C> {
136 let cells = self.rows.entry(key.clone()).or_default();
137 let ix = match cells.iter().position(|cell| cell.column == column) {
138 Some(ix) => ix,
139 None => {
140 cells.push(CellEdits {
141 column,
142 edits: Vec::new(),
143 failed: None,
144 });
145 cells.len() - 1
146 }
147 };
148 &mut cells[ix]
149 }
150
151 pub(super) fn overlay(&self, key: &K, column: C) -> Option<&OwnedValue> {
154 Some(&self.cell(key, column)?.newest()?.value)
155 }
156
157 pub(super) fn marks(&self, key: &K, column: C) -> Option<CellEditMarks<'_>> {
159 let cell = self.cell(key, column)?;
160 let newest = cell.newest();
161 Some(CellEditMarks {
162 value: newest.map(|edit| &edit.value),
163 state: newest
164 .map(|edit| &edit.state)
165 .or_else(|| cell.failed.as_ref().map(|(state, _)| state)),
166 pending: newest.is_some_and(|edit| edit.state == EditState::Pending),
167 failed: cell.failed.is_some(),
168 })
169 }
170
171 pub(super) fn state(&self, key: &K, column: C) -> Option<&EditState> {
174 self.marks(key, column)?.state
175 }
176
177 pub(super) fn count(&self) -> usize {
179 self.ids.len()
180 }
181
182 pub(super) fn begin(&mut self, id: EditId, key: K, column: C, value: OwnedValue) {
185 let cell = self.cell_mut(&key, column);
186 cell.failed = None;
187 cell.edits.push(PendingEdit {
188 id,
189 value,
190 state: EditState::Pending,
191 accepted: None,
192 });
193 self.ids.insert(id, (key, column));
194 self.publish();
195 }
196
197 pub(super) fn resolve(
201 &mut self,
202 id: EditId,
203 result: Result<(), EditError>,
204 now: Instant,
205 store_value: impl FnOnce(&K, C) -> Option<OwnedValue>,
206 mark_until: Option<Instant>,
207 ) -> bool {
208 let Some((key, column)) = self.ids.get(&id).cloned() else {
209 return false;
210 };
211 let store_value = store_value(&key, column);
212 let cell = self.cell_mut(&key, column);
213 let Some(ix) = cell
214 .edits
215 .iter()
216 .position(|edit| edit.id == id && edit.state == EditState::Pending)
217 else {
218 return false;
219 };
220 match result {
221 Ok(()) => {
222 let edit = &mut cell.edits[ix];
223 edit.state = EditState::Accepted;
224 edit.accepted = Some((now, store_value.unwrap_or_else(|| edit.value.clone())));
225 }
226 Err(error) => {
227 cell.edits.remove(ix);
228 cell.failed = Some((EditState::Failed(error), mark_until));
229 self.ids.remove(&id);
230 }
231 }
232 self.publish();
233 true
234 }
235
236 pub(super) fn clear_failed(&mut self, key: &K, column: C) -> bool {
238 let Some(cells) = self.rows.get_mut(key) else {
239 return false;
240 };
241 let Some(cell) = cells.iter_mut().find(|cell| cell.column == column) else {
242 return false;
243 };
244 if cell.failed.take().is_none() {
245 return false;
246 }
247 self.prune();
248 true
249 }
250
251 pub(super) fn follow<R>(
257 &mut self,
258 snapshot: &StoreSnapshot<R>,
259 now: Instant,
260 settle: Duration,
261 ) -> bool
262 where
263 R: GridRow<Key = K, Column = C>,
264 C: crate::model::GridColumns,
265 {
266 let before = (self.ids.len(), self.marks_count());
267 let mut gone: Vec<EditId> = Vec::new();
268 self.rows.retain(|key, cells| {
269 let Some(row) = snapshot.row_id(key).and_then(|id| snapshot.row(id)) else {
270 gone.extend(
271 cells
272 .iter()
273 .flat_map(|cell| cell.edits.iter().map(|edit| edit.id)),
274 );
275 return false;
276 };
277 for cell in cells.iter_mut() {
278 if cell.edits.iter().any(|edit| edit.accepted.is_some()) {
279 let store = OwnedValue::from_cell(row.value(cell.column));
280 cell.edits.retain(|edit| {
281 let Some((at, at_answer)) = &edit.accepted else {
282 return true;
283 };
284 let settled = store.cmp_total(&edit.value).is_eq()
285 || !store.cmp_total(at_answer).is_eq()
286 || now >= *at + settle;
287 if settled {
288 gone.push(edit.id);
289 }
290 !settled
291 });
292 }
293 if cell
294 .failed
295 .as_ref()
296 .is_some_and(|(_, until)| until.is_some_and(|until| now >= until))
297 {
298 cell.failed = None;
299 }
300 }
301 cells.retain(|cell| !cell.is_empty());
302 !cells.is_empty()
303 });
304 for id in gone {
305 self.ids.remove(&id);
306 }
307 let changed = before != (self.ids.len(), self.marks_count());
308 if changed {
309 self.publish();
310 }
311 changed
312 }
313
314 fn marks_count(&self) -> usize {
315 self.rows
316 .values()
317 .flatten()
318 .filter(|cell| cell.failed.is_some())
319 .count()
320 }
321
322 pub(super) fn next_deadline(&self, settle: Duration) -> Option<Instant> {
325 self.rows
326 .values()
327 .flatten()
328 .flat_map(|cell| {
329 cell.edits
330 .iter()
331 .filter_map(|edit| edit.accepted.as_ref().map(|(at, _)| *at + settle))
332 .chain(cell.failed.as_ref().and_then(|(_, until)| *until))
333 })
334 .min()
335 }
336
337 fn prune(&mut self) {
339 self.rows.retain(|_, cells| {
340 cells.retain(|cell| !cell.is_empty());
341 !cells.is_empty()
342 });
343 self.publish();
344 }
345
346 fn publish(&mut self) {
348 self.rows.retain(|_, cells| {
349 cells.retain(|cell| !cell.is_empty());
350 !cells.is_empty()
351 });
352 let rows = self
353 .rows
354 .iter()
355 .filter_map(|(key, cells)| {
356 let values: Box<[(C, OwnedValue)]> = cells
357 .iter()
358 .filter_map(|cell| Some((cell.column, cell.newest()?.value.clone())))
359 .collect();
360 (!values.is_empty()).then(|| (key.clone(), values))
361 })
362 .collect();
363 self.values = Arc::new(PendingValues { rows });
364 }
365}
366
367impl<D: LiveGridDelegate> LiveGridState<D> {
368 pub fn resolve_edit(
377 &mut self,
378 edit: EditId,
379 result: Result<(), EditError>,
380 cx: &mut Context<Self>,
381 ) -> bool {
382 let now = self.store.now();
383 let mark_until = self
384 .edit_setup
385 .policy()
386 .error_mark_timeout()
387 .map(|timeout| now + timeout);
388 let snapshot = self.snapshot.clone();
389 let resolved = self.pending_edits.resolve(
390 edit,
391 result,
392 now,
393 |key, column| {
394 let row = snapshot.row(snapshot.row_id(key)?)?;
395 Some(OwnedValue::from_cell(row.value(column)))
396 },
397 mark_until,
398 );
399 if resolved {
400 self.follow_pending_edits(cx);
402 cx.notify();
403 }
404 resolved
405 }
406
407 pub fn edit_state(&self, key: &KeyOf<D>, column: ColumnOf<D>) -> Option<&EditState> {
411 self.pending_edits.state(key, column)
412 }
413
414 pub fn pending_edit_count(&self) -> usize {
416 self.pending_edits.count()
417 }
418
419 pub fn clear_edit_error(
422 &mut self,
423 key: &KeyOf<D>,
424 column: ColumnOf<D>,
425 cx: &mut Context<Self>,
426 ) -> bool {
427 let cleared = self.pending_edits.clear_failed(key, column);
428 if cleared {
429 cx.notify();
430 }
431 cleared
432 }
433
434 pub(crate) fn pending_values(&self) -> Arc<PendingValues<super::KeyOf<D>, super::ColumnOf<D>>> {
438 self.pending_edits.values.clone()
439 }
440
441 #[cfg(any(test, feature = "test-support"))]
445 pub fn set_test_pending_values(
446 &mut self,
447 values: impl IntoIterator<Item = (super::KeyOf<D>, super::ColumnOf<D>, OwnedValue)>,
448 cx: &mut Context<Self>,
449 ) {
450 let mut rows: FxHashMap<_, Vec<(super::ColumnOf<D>, OwnedValue)>> = FxHashMap::default();
451 for (key, column, value) in values {
452 let row = rows.entry(key).or_default();
453 row.retain(|(edited, _)| *edited != column);
454 row.push((column, value));
455 }
456 let rows = rows
457 .into_iter()
458 .map(|(key, values)| (key, values.into_boxed_slice()))
459 .collect();
460 self.pending_edits.values = Arc::new(PendingValues { rows });
461 cx.notify();
462 }
463
464 pub(super) fn begin_pending_edit(
467 &mut self,
468 edit: EditId,
469 key: KeyOf<D>,
470 column: ColumnOf<D>,
471 value: OwnedValue,
472 cx: &mut Context<Self>,
473 ) {
474 self.pending_edits.begin(edit, key, column, value);
475 cx.notify();
476 }
477
478 pub(super) fn follow_pending_edits(&mut self, cx: &mut Context<Self>) {
482 if self.pending_edits.is_empty() {
483 self.pending_edits.timer = None;
484 return;
485 }
486 let now = self.store.now();
487 let settle = self.edit_setup.policy().settle_timeout();
488 if self.pending_edits.follow(&self.snapshot, now, settle) {
489 cx.notify();
490 }
491 let next = self.pending_edits.next_deadline(settle);
492 match next {
493 None => self.pending_edits.timer = None,
494 Some(deadline)
495 if self
496 .pending_edits
497 .timer
498 .as_ref()
499 .is_some_and(|(at, _)| *at == deadline) => {}
500 Some(deadline) => {
501 let wait = deadline.saturating_duration_since(now);
502 let task = cx.spawn(async move |this, cx| {
503 cx.background_executor().timer(wait).await;
504 let _ = this.update(cx, |state, cx| {
505 state.pending_edits.timer = None;
506 state.follow_pending_edits(cx);
507 });
508 });
509 self.pending_edits.timer = Some((deadline, task));
510 }
511 }
512 }
513
514 pub(super) fn follow_edit_shown(&mut self, cx: &mut Context<Self>) {
517 self.end_hidden_edit(cx);
518 }
519}
520
521#[cfg(test)]
522mod tests {
523 use super::*;
524
525 #[test]
526 fn test_pending_edits_stack() {
527 let now = Instant::now();
528 let mut pending = PendingEdits::<u32, u16>::default();
529 assert!(pending.is_empty());
530 assert_eq!(pending.overlay(&1, 0), None);
531 assert!(pending.values.is_empty());
532
533 pending.begin(EditId::new(1), 1, 0, OwnedValue::Int(5));
535 pending.begin(EditId::new(2), 1, 0, OwnedValue::Int(6));
536 assert_eq!(pending.overlay(&1, 0), Some(&OwnedValue::Int(6)));
537 assert_eq!(pending.count(), 2);
538 assert_eq!(pending.state(&1, 0), Some(&EditState::Pending));
539 assert_eq!(pending.values.get(&1), Some(&[(0, OwnedValue::Int(6))][..]));
540 assert_eq!(pending.overlay(&1, 1), None);
541 assert_eq!(pending.overlay(&2, 0), None);
542
543 let error = EditError::new("Price out of band");
545 assert!(pending.resolve(EditId::new(1), Err(error.clone()), now, |_, _| None, None));
546 let marks = pending.marks(&1, 0).expect("marks");
547 assert!(marks.failed && marks.pending);
548 assert_eq!(marks.value, Some(&OwnedValue::Int(6)));
549 assert_eq!(pending.count(), 1);
550 assert!(pending.resolve(
552 EditId::new(2),
553 Ok(()),
554 now,
555 |_, _| Some(OwnedValue::Int(1)),
556 None
557 ));
558 assert_eq!(pending.state(&1, 0), Some(&EditState::Accepted));
559 assert!(!pending.resolve(EditId::new(7), Ok(()), now, |_, _| None, None));
560 assert_eq!(
561 pending.next_deadline(Duration::from_secs(5)),
562 Some(now + Duration::from_secs(5))
563 );
564 assert!(pending.clear_failed(&1, 0));
566 assert!(!pending.clear_failed(&1, 0));
567 assert_eq!(pending.overlay(&1, 0), Some(&OwnedValue::Int(6)));
568 }
569}