mirror of
https://github.com/openai/codex.git
synced 2026-04-28 18:32:04 +03:00
4.2 KiB
4.2 KiB
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()andrender_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
HistoryCellthe 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) }
}
DON’Ts
- Don’t duplicate layout computations in multiple methods: avoid copy-pasting the same
Layout::vertical([...]).areas(area)block incursor_pos()andrender_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 ...
- Don’t 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,
));
}
- Don’t over-emit patch output or choose the wrong stream: on success, don’t 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
}