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

4.2 KiB
Raw Blame History

DOs

  • Factor layout logic into a helper: centralize the split between the transient “active history cell” and the bottom pane, and reuse it in both cursor_pos() and render_ref().
impl ChatWidget<'_> {
    fn layout_areas(&self, area: Rect) -> [Rect; 2] {
        Layout::vertical([
            Constraint::Max(
                self.active_history_cell
                    .as_ref()
                    .map_or(0, |c| c.desired_height(area.width)),
            ),
            Constraint::Min(self.bottom_pane.desired_height(area.width)),
        ])
        .areas(area)
    }

    pub fn cursor_pos(&self, area: Rect) -> Option<(u16, u16)> {
        let [_, bottom] = self.layout_areas(area);
        self.bottom_pane.cursor_pos(bottom)
    }
}

impl WidgetRef for &ChatWidget<'_> {
    fn render_ref(&self, area: Rect, buf: &mut Buffer) {
        let [active, bottom] = self.layout_areas(area);
        (&self.bottom_pane).render(bottom, buf);
        if let Some(cell) = &self.active_history_cell {
            cell.render_ref(active, buf);
        }
    }
}
  • Pass structured events to constructors: prefer handing HistoryCell the full event (e.g., PatchApplyEndEvent) instead of plucking out fields at the call site.
// ChatWidget handler
EventMsg::PatchApplyEnd(event) => {
    self.add_to_history(HistoryCell::new_patch_apply_end(event));
}

// HistoryCell API
pub(crate) fn new_patch_apply_end(event: PatchApplyEndEvent) -> Self {
    let success = event.success;
    let stdout = event.stdout;
    let stderr = event.stderr;
    // ... build lines based on success/stdout/stderr ...
    HistoryCell::PatchApplyResult { view: TextBlock::new(lines) }
}
  • Be stingy with patch-apply output: success paths generally have non-empty stdout; avoid repeating content already shown earlier and limit any additional output to a small snippet.
pub(crate) fn new_patch_apply_end(event: PatchApplyEndEvent) -> Self {
    let status = if event.success {
        "patch applied".green()
    } else {
        "patch failed".red().bold()
    };

    let mut lines = vec![
        Line::from(vec!["patch".magenta().bold(), " ".into(), status]),
        "".into(),
    ];

    // Optional, terse snippet only if helpful.
    let src = if event.success {
        &event.stdout // success should have useful stdout
    } else if event.stderr.trim().is_empty() {
        &event.stdout
    } else {
        &event.stderr
    };

    if !src.trim().is_empty() {
        for l in src.lines().take(5) {
            lines.push(ansi_escape_line(l).dim());
        }
        let remaining = src.lines().count().saturating_sub(5);
        if remaining > 0 {
            lines.push(Line::from(format!("... {remaining} additional lines")).dim());
        }
        lines.push("".into());
    }

    HistoryCell::PatchApplyResult { view: TextBlock::new(lines) }
}

DONTs

  • Dont duplicate layout computations in multiple methods: avoid copy-pasting the same Layout::vertical([...]).areas(area) block in cursor_pos() and render_ref().
// Avoid this duplication in multiple places:
let [active_cell_area, bottom_pane_area] = Layout::vertical([
    Constraint::Max(self.active_history_cell.as_ref().map_or(0, |c| c.desired_height(area.width))),
    Constraint::Min(self.bottom_pane.desired_height(area.width)),
]).areas(area);
// ... the same block repeated elsewhere ...
  • Dont peel event fields at the call site just to rewrap them: it scatters knowledge of the event shape and increases churn.
// Anti-pattern: extracting fields here instead of passing the event
EventMsg::PatchApplyEnd(event) => {
    self.add_to_history(HistoryCell::new_patch_apply_end(
        event.stdout, event.stderr, event.success,
    ));
}
  • Dont over-emit patch output or choose the wrong stream: on success, dont show stderr; avoid flooding the UI with long logs right after an apply-begin summary.
// Anti-patterns:
// 1) Showing stderr on success or assuming stdout might be empty on success
let src = if event.success { &event.stderr } else { &event.stdout }; // wrong

// 2) Dumping unbounded output
for l in src.lines() {
    lines.push(Line::from(l.to_string())); // too verbose; no limit
}