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

98 lines
3.8 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**
- Use `poll()` before `read()`: Check for input with a short timeout so the event reader doesnt hold Crossterms global event lock, which can block `cursor::position()` and cause resize crashes.
```rust
use std::time::{Duration, Instant};
use crossterm::event::{self, Event};
const EVENT_POLL: Duration = Duration::from_millis(100);
loop {
if event::poll(EVENT_POLL)? {
match event::read()? {
Event::Key(k) => app_event_tx.send(AppEvent::KeyEvent(k)),
Event::Resize(_, _) => app_event_tx.send(AppEvent::RequestRedraw),
Event::Mouse(m) => match m.kind {
MouseEventKind::ScrollUp => scroll_event_helper.scroll_up(),
MouseEventKind::ScrollDown => scroll_event_helper.scroll_down(),
_ => {}
},
Event::Paste(p) => app_event_tx.send(AppEvent::Paste(p.replace("\r", "\n"))),
_ => {}
}
} else {
// No input this tick; do lightweight work or continue.
}
}
```
- Keep timeout comments precise: Explain why `poll()` is used and distinguish the poll interval from Crossterms separate lock timeout used by `cursor::position()`.
```rust
// Poll every 100ms to avoid holding Crossterms event lock.
// cursor::position() may wait up to ~2s to acquire that lock;
// if the reader holds it continuously, position reads can fail.
```
- Cite upstream patterns: Add a brief comment linking to Ratatuis own example so future maintainers recognize this is a known, recommended pattern.
```rust
// Pattern inspired by Ratatui inline example:
// https://github.com/ratatui/ratatui/blob/9836f0760d4a053d9d1eba78171be89cb22dc850/examples/apps/inline/src/main.rs#L98-L118
```
- Model Ratatuis tick/input loop: Optionally separate “ticks” from input so the UI can update periodically even without events (closer to Ratatui examples).
```rust
use std::time::{Duration, Instant};
use crossterm::event::{self, Event};
const TICK_RATE: Duration = Duration::from_millis(200);
let mut last_tick = Instant::now();
loop {
let timeout = TICK_RATE.saturating_sub(last_tick.elapsed());
if event::poll(timeout)? {
match event::read()? {
Event::Key(k) => app_event_tx.send(AppEvent::KeyEvent(k)),
Event::Resize(_, _) => app_event_tx.send(AppEvent::RequestRedraw),
_ => {}
}
}
if last_tick.elapsed() >= TICK_RATE {
app_event_tx.send(AppEvent::Tick);
last_tick = Instant::now();
}
}
```
- Use named durations to avoid confusion: Make differing timeouts explicit and keep comments in sync.
```rust
const EVENT_POLL: Duration = Duration::from_millis(100);
const CURSOR_LOCK_TIMEOUT_DOC: &str = "cursor::position() may wait ~2s for event lock";
```
**DONTs**
- Dont call `read()` in a tight loop: This can monopolize the event lock and make `cursor::position()` fail during resizes.
```rust
// BAD: holds the event lock continuously
while let Ok(event) = crossterm::event::read() {
handle(event); // Other code needing the event lock may starve
}
```
- Dont write ambiguous timeout comments: Avoid mixing numbers (e.g., “2 sec” vs “100ms”) without clarifying what each applies to.
```rust
// BAD: “Use 100ms so we dont hit the 2s timeout”
// Which timeout? For what call? Be explicit.
```
- Dont diverge from proven upstream patterns without reason: If you implement a different loop structure, explain why its needed for your app.
```rust
// If not following Ratatuis poll+read+tick pattern,
// document the rationale and implications for event lock usage.
```
- Dont redraw lazily on resize: Make resize explicit and cheap—request a redraw promptly.
```rust
match event {
crossterm::event::Event::Resize(_, _) => app_event_tx.send(AppEvent::RequestRedraw),
_ => {}
}
```