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

4.0 KiB
Raw Blame History

DOs

  • Document boolean returns: Explain exactly what true/false mean and what the caller must do.
impl ChatWidget<'_> {
    /// Handle Ctrl+C.
    /// Returns true if the caller should exit the app.
    /// Returns false if handled internally (interrupt or hint shown).
    pub(crate) fn on_ctrl_c(&mut self) -> bool { /* ... */ }
}
  • Delegate exit to the app: Let the widget decide intent; the app performs the exit.
// codex-rs/tui/src/app.rs
if widget.on_ctrl_c() {
    // Widget signaled exit
    let _ = self.app_event_tx.send(AppEvent::ExitRequest);
}
  • Interrupt only when a task is running: Forward Op::Interrupt if busy; never exit in this branch.
// codex-rs/tui/src/chatwidget.rs
if self.bottom_pane.is_task_running() {
    self.bottom_pane.clear_ctrl_c_quit_hint();
    self.submit_op(Op::Interrupt);
    return false; // handled internally
}
  • Show a quit hint when idle; exit on second press: First Ctrl+C shows “Ctrl+C to quit”; second Ctrl+C exits.
// Still in on_ctrl_c()
if self.bottom_pane.ctrl_c_quit_hint_visible() {
    return true; // caller should exit
} else {
    self.bottom_pane.show_ctrl_c_quit_hint();
    return false; // handled internally
}
  • Clear the hint on other activity: Any keypress or task start should remove the hint.
// On any key event
pub(crate) fn handle_key_event(&mut self, key_event: KeyEvent) {
    self.bottom_pane.clear_ctrl_c_quit_hint();
    /* ...existing handling... */
}

// When a task starts
EventMsg::TaskStarted => {
    self.bottom_pane.clear_ctrl_c_quit_hint();
    self.bottom_pane.set_task_running(true);
    self.request_redraw();
}
  • Update UI through dedicated APIs and request redraws: Keep state changes localized and visible.
// codex-rs/tui/src/bottom_pane/mod.rs
pub(crate) fn show_ctrl_c_quit_hint(&mut self) {
    self.ctrl_c_quit_hint = true;
    self.composer.set_ctrl_c_quit_hint(true, self.has_input_focus);
    self.request_redraw();
}

pub(crate) fn clear_ctrl_c_quit_hint(&mut self) {
    if self.ctrl_c_quit_hint {
        self.ctrl_c_quit_hint = false;
        self.composer.set_ctrl_c_quit_hint(false, self.has_input_focus);
        self.request_redraw();
    }
}
  • Keep responsibilities layered: BottomPane owns hint state; Composer renders it; Widget orchestrates behavior; App decides process exit.
// codex-rs/tui/src/bottom_pane/chat_composer.rs (rendering)
let bs = if has_focus {
    if self.ctrl_c_quit_hint {
        BlockState { right_title: "Ctrl+C to quit".into(), border_style: Style::default() }
    } else {
        BlockState { right_title: "Enter to send | Ctrl+D to quit | Ctrl+J for newline".into(),
                     border_style: Style::default() }
    }
} else { /* ... */ };

DON'Ts

  • Dont leave boolean semantics implicit: Undocumented booleans invite misuse.
// BAD: What does true mean here?
pub(crate) fn on_ctrl_c(&mut self) -> bool { /* ... */ }
  • Dont exit immediately on first Ctrl+C when idle: Show the hint first to prevent accidental exits.
// BAD: Immediate exit on first Ctrl+C
if key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL) {
    let _ = self.app_event_tx.send(AppEvent::ExitRequest);
}
  • Dont send interrupts when idle: Only interrupt when work is active.
// BAD: Unconditionally interrupting
self.submit_op(Op::Interrupt); // will do nothing useful when idle
  • Dont forget to clear the hint on activity: Otherwise the UI will exit on next Ctrl+C unexpectedly.
// BAD: No hint clearing at the start of key handling
pub(crate) fn handle_key_event(&mut self, key_event: KeyEvent) {
    /* ...handles keys but leaves hint visible... */
}
  • Dont mutate UI state without a redraw: Users wont see the updated hint/title.
// BAD: State changed, but no redraw requested
self.ctrl_c_quit_hint = true;
self.composer.set_ctrl_c_quit_hint(true, self.has_input_focus);
// missing: self.request_redraw();