Files
codex/prs/bolinfest/study/PR-1599-study.md
2025-09-02 15:17:45 -07:00

132 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
**DOs**
- Bold: Debounce redraws via a request event: Introduce `RequestRedraw` and schedule `Redraw` after ~100 ms.
```rust
// app_event.rs
pub(crate) enum AppEvent {
// ...
RequestRedraw,
Redraw,
// ...
}
// app.rs
const REDRAW_DEBOUNCE: Duration = Duration::from_millis(100);
```
- Bold: Scope locks tightly: Lock only to check/set the pending flag, then drop it before spawning the worker.
```rust
fn schedule_redraw(&self) {
{
let mut flag = self.pending_redraw.lock().unwrap();
if *flag {
return;
}
*flag = true;
} // lock dropped here
let tx = self.app_event_tx.clone();
let pending_redraw = self.pending_redraw.clone();
std::thread::spawn(move || {
std::thread::sleep(REDRAW_DEBOUNCE);
tx.send(AppEvent::Redraw);
let mut f = pending_redraw.lock().unwrap();
*f = false;
});
}
```
- Bold: Prefer `.clone()` ergonomics: Clone `Arc`s and senders with `.clone()` before moving into threads.
```rust
let tx = self.app_event_tx.clone();
let pending_redraw = self.pending_redraw.clone();
std::thread::spawn(move || {
// use tx, pending_redraw here
});
```
- Bold: Convert producers to `RequestRedraw`: Replace direct `Redraw` sends in all producers.
```rust
// On startup
self.app_event_tx.clone().send(AppEvent::RequestRedraw);
// On terminal resize
crossterm::event::Event::Resize(_, _) => {
app_event_tx.send(AppEvent::RequestRedraw);
}
// Bottom pane / widgets
self.app_event_tx.send(AppEvent::RequestRedraw);
```
- Bold: Handle the request distinctly: Turn `RequestRedraw` into a debounced `Redraw` in the main loop.
```rust
while let Ok(event) = self.app_event_rx.recv() {
match event {
AppEvent::RequestRedraw => self.schedule_redraw(),
AppEvent::Redraw => self.draw_next_frame(terminal)?,
_ => {}
}
}
```
- Bold: Keep helpers near first use: Place `schedule_redraw` just above the match block that calls it for readability.
```rust
impl App<'_> {
// ... fields, new(), etc.
// place here (near run/loop)
fn schedule_redraw(&self) { /* as above */ }
pub(crate) fn run(&mut self, terminal: &mut tui::Tui, mouse_capture: &mut MouseCapture) -> Result<()> {
// uses schedule_redraw in match
}
}
```
**DONTs**
- Bold: Dont hold locks across work: Avoid sleeping, sending, or spawning while a `MutexGuard` is alive.
```rust
// BAD: lock held far too long
fn schedule_redraw_bad(&self) {
let mut flag = self.pending_redraw.lock().unwrap();
if *flag { return; }
*flag = true;
std::thread::sleep(REDRAW_DEBOUNCE); // holding the lock
self.app_event_tx.send(AppEvent::Redraw); // still holding the lock
}
```
- Bold: Dont emit `Redraw` directly from producers: Always send `RequestRedraw` so the app can debounce.
```rust
// BAD
app_event_tx.send(AppEvent::Redraw);
// GOOD
app_event_tx.send(AppEvent::RequestRedraw);
```
- Bold: Dont reschedule when one is pending: Guard with a boolean to coalesce rapid requests.
```rust
// Inside schedule_redraw
let mut flag = self.pending_redraw.lock().unwrap();
if *flag { return; } // don't stack timers
*flag = true;
```
- Bold: Dont bury relevant helpers: Avoid scattering debounce logic far from where events are handled.
```rust
// BAD: helper lives in a distant module/file, hard to follow
// GOOD: helper is defined in the same impl, just above its call sites
```
- Bold: Dont prefer long-lived patterns when simple clones work: Use `.clone()` in place of `Arc::clone(&x)` when clarity is equal.
```rust
// Preferred here
let pending_redraw = self.pending_redraw.clone();
// Avoid when it adds noise
let pending_redraw = std::sync::Arc::clone(&self.pending_redraw); // noisier
```