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

5.1 KiB
Raw Blame History

Burst Paste Edge Cases: Review Guide (PR #2683)

DOs

  • Bold the keyword, then colon + concise description below each, with a short code example.

  • Idempotent setters: perform side effects only when state changes.

fn set_disable_paste_burst(&mut self, disabled: bool) {
    let was_disabled = self.disable_paste_burst;
    self.disable_paste_burst = disabled;
    if disabled && !was_disabled {
        self.paste_burst.clear_window_after_non_char();
    }
}
  • Constructor wiring: pass config flags into new() and centralize side effects via a setter.
impl ChatComposer {
    pub fn new(..., disable_paste_burst: bool) -> Self {
        let mut this = Self { /* fields */, paste_burst: PasteBurst::default(), disable_paste_burst: false };
        this.set_disable_paste_burst(disable_paste_burst);
        this
    }
}
  • Doc comments: use ///, describe behavior and return values succinctly.
/// Flush buffered content if the inter-key timeout elapsed.
/// Returns Some(buffered_or_saved_text) when a flush occurs, otherwise None.
pub fn flush_if_due(&mut self, now: Instant) -> Option<String> { /* ... */ }
  • Clear, accurate names: use descriptive enum variants (e.g., RetainFirstChar).
pub(crate) enum CharDecision {
    BeginBuffer { retro_chars: u16 },
    BufferAppend,
    RetainFirstChar,
    BeginBufferFromPending,
}
  • Unicode-safe heuristics: count characters, not bytes; document rationale.
/// Begin buffering if recent text likely came from a paste (whitespace or long).
let looks_pastey = grabbed.chars().any(|c| c.is_whitespace()) || grabbed.chars().count() >= 16;
  • Tight, total returns: ensure boolean helpers return on all paths; prefer single expression.
pub(crate) fn handle_paste_burst_tick(&mut self, fr: FrameRequester) -> bool {
    if self.bottom_pane.flush_paste_burst_if_due() {
        self.request_redraw();
        true
    } else if self.bottom_pane.is_in_paste_burst() {
        fr.schedule_frame_in(ChatComposer::recommended_paste_flush_delay());
        true
    } else {
        false
    }
}
  • Friendly imports: import types once; avoid fully qualified paths where possible.
use std::time::Duration;

pub(crate) fn request_redraw_in(&self, dur: Duration) {
    self.frame_requester.schedule_frame_in(dur);
}
  • Smart redraw scheduling: request immediate redraw on flush; schedule a delayed tick while capturing.
if needs_redraw { self.request_redraw(); }
if self.composer.is_in_paste_burst() {
    self.request_redraw_in(ChatComposer::recommended_paste_flush_delay());
}
  • Simple tests: keep helpers minimal; human-like typing with a small delay; use simple literals.
fn type_chars_humanlike(c: &mut ChatComposer, chars: &[char]) {
    for &ch in chars {
        let _ = c.handle_key_event(KeyEvent::new(KeyCode::Char(ch), KeyModifiers::NONE));
        std::thread::sleep(ChatComposer::recommended_paste_flush_delay());
        let _ = c.flush_paste_burst_if_due();
    }
}

let count = 32;
  • Exact fixtures: keep field names and timestamp formats precise.
// Right
{"ts":"2025-08-09T15:58:15.433Z","dir":"to_tui","kind":"app_event","variant":"RequestRedraw"}
// Wrong
{"ts":"2025-08-09T15:58:15.4F33Z","fdir":"to_tui","kind":"app_event","variant":"RequestRedraw"}

DONTs

  • Repeat side effects: dont re-clear state when the flag was already set.
// Dont: always clear even if already disabled.
self.disable_paste_burst = disabled;
if disabled { self.paste_burst.clear_window_after_non_char(); }
  • Use // where /// is required: dont skimp on doc comments or omit return-value notes.
// Dont
// Timer: flush buffered burst if timeout elapsed.
  • Hide config outside constructors: dont require callers to set flags post-init without a clear reason.
// Dont
let mut c = ChatComposer::new(...);
c.set_disable_paste_burst(true); // Wire it through new() instead.
  • Fully qualify types repeatedly: dont write long paths when an import suffices.
// Dont
pub fn request_redraw_in(&self, dur: std::time::Duration) { /* ... */ }
  • Count bytes for heuristics: dont use .len() on String slices for thresholds.
// Dont
let looks_pastey = grabbed.contains(' ') || grabbed.len() >= 16;
  • Leave partial returns: dont forget a return value in one branch.
// Dont
if flushed { self.request_redraw(); /* missing return */ }
  • Overuse #[cfg(test)] inside mod tests: dont gate internals unnecessarily within a test module.
// Dont
#[cfg(test)]
fn helper_in_mod_tests_only() { /* ... */ }
  • Introduce fixture typos: dont change keys (dirfdir) or corrupt timestamps.
// Dont
{"ts":"...15.4F33Z","fdir":"to_tui", ...}
  • Redundant renders: dont render and also schedule a follow-up in the same frame when a flush just happened.
// Dont
self.draw_now();
fr.schedule_frame_in(Duration::from_millis(1));
  • Aggressive retro-grab: dont yank preceding text unless the heuristic clearly indicates paste.
// Dont
begin_with_retro_grabbed(before.to_string(), now); // without checking whitespace/length