mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
chore(tui): wip instrument resize render diagnostics
Add temporary resize-path diagnostics for terminal resize investigation. The logs capture terminal identity, platform, VSCode/macOS env markers, watchdog state, resize reflow mode, pre-render decisions, scheduled draw events, and terminal draw start/end timing boundaries. This is intentionally a WIP commit that should be amended as we learn which resize path needs a real fix.
This commit is contained in:
+89
-12
@@ -3385,12 +3385,34 @@ impl App {
|
||||
frame_requester: &tui::FrameRequester,
|
||||
resize_reflow_mode: tui::ResizeReflowMode,
|
||||
) -> bool {
|
||||
let pending_reflow_before = self.transcript_reflow.has_pending_reflow();
|
||||
let width = self.transcript_reflow.note_width(size.width);
|
||||
tracing::info!(
|
||||
event = "resize_width_observed",
|
||||
cols = size.width,
|
||||
rows = size.height,
|
||||
last_known_cols = last_known_screen_size.width,
|
||||
last_known_rows = last_known_screen_size.height,
|
||||
width_changed = width.changed,
|
||||
width_initialized = width.initialized,
|
||||
pending_reflow_before,
|
||||
resize_reflow_mode = ?resize_reflow_mode,
|
||||
"observed terminal width during draw pre-render"
|
||||
);
|
||||
if width.changed {
|
||||
self.chat_widget.on_terminal_resize(size.width);
|
||||
match resize_reflow_mode {
|
||||
tui::ResizeReflowMode::Debounced => {
|
||||
if self.schedule_resize_reflow() {
|
||||
let due_now = self.schedule_resize_reflow();
|
||||
tracing::info!(
|
||||
event = "resize_reflow_scheduled_debounced",
|
||||
cols = size.width,
|
||||
rows = size.height,
|
||||
due_now,
|
||||
debounce_ms = TRANSCRIPT_REFLOW_DEBOUNCE.as_millis(),
|
||||
"scheduled debounced resize reflow"
|
||||
);
|
||||
if due_now {
|
||||
frame_requester.schedule_frame();
|
||||
} else {
|
||||
frame_requester.schedule_frame_in(TRANSCRIPT_REFLOW_DEBOUNCE);
|
||||
@@ -3451,7 +3473,16 @@ impl App {
|
||||
let Some(deadline) = self.transcript_reflow.pending_until() else {
|
||||
return Ok(());
|
||||
};
|
||||
if Instant::now() < deadline || self.overlay.is_some() {
|
||||
let now = Instant::now();
|
||||
let due = now >= deadline;
|
||||
let overlay_active = self.overlay.is_some();
|
||||
if !due || overlay_active {
|
||||
tracing::info!(
|
||||
event = "resize_reflow_deferred",
|
||||
due,
|
||||
overlay_active,
|
||||
"resize reflow remains pending"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -3463,7 +3494,18 @@ impl App {
|
||||
let reflow_ran_during_stream =
|
||||
!self.transcript_cells.is_empty() && self.should_mark_reflow_as_stream_time();
|
||||
|
||||
tracing::info!(
|
||||
event = "resize_reflow_started",
|
||||
transcript_cells = self.transcript_cells.len(),
|
||||
reflow_ran_during_stream,
|
||||
"running resize reflow"
|
||||
);
|
||||
self.reflow_transcript_now(tui)?;
|
||||
tracing::info!(
|
||||
event = "resize_reflow_completed",
|
||||
reflow_ran_during_stream,
|
||||
"completed resize reflow"
|
||||
);
|
||||
|
||||
if reflow_ran_during_stream {
|
||||
self.transcript_reflow.mark_ran_during_stream();
|
||||
@@ -4236,19 +4278,38 @@ impl App {
|
||||
event: TuiEvent,
|
||||
) -> Result<AppRunControl> {
|
||||
let is_resize = matches!(event, TuiEvent::Resize);
|
||||
let render_trigger = match &event {
|
||||
TuiEvent::Draw => Some("draw"),
|
||||
TuiEvent::Resize => Some("resize"),
|
||||
TuiEvent::Key(_) | TuiEvent::Paste(_) => None,
|
||||
};
|
||||
if is_resize {
|
||||
let terminal_size = tui.terminal.size().ok();
|
||||
tracing::info!(
|
||||
event = "tui_resize_event_handled",
|
||||
cols = terminal_size.map(|size| size.width),
|
||||
rows = terminal_size.map(|size| size.height),
|
||||
terminal_name = ?tui.terminal_name(),
|
||||
resize_reflow_mode = ?tui.resize_reflow_mode(),
|
||||
target_os = std::env::consts::OS,
|
||||
is_windows = cfg!(windows),
|
||||
is_macos = cfg!(target_os = "macos"),
|
||||
"handling TUI resize event with forced full repaint"
|
||||
);
|
||||
tui.force_full_repaint();
|
||||
}
|
||||
if matches!(event, TuiEvent::Draw | TuiEvent::Resize) {
|
||||
if let Some(render_trigger) = render_trigger {
|
||||
tracing::info!(
|
||||
event = "tui_render_pre_render_started",
|
||||
render_trigger,
|
||||
"starting draw pre-render handling"
|
||||
);
|
||||
self.handle_draw_pre_render(tui)?;
|
||||
tracing::info!(
|
||||
event = "tui_render_pre_render_completed",
|
||||
render_trigger,
|
||||
"completed draw pre-render handling"
|
||||
);
|
||||
}
|
||||
|
||||
if self.overlay.is_some() {
|
||||
@@ -4267,6 +4328,7 @@ impl App {
|
||||
self.chat_widget.handle_paste(pasted);
|
||||
}
|
||||
TuiEvent::Draw | TuiEvent::Resize => {
|
||||
let render_trigger = render_trigger.unwrap_or("unknown");
|
||||
if self.backtrack_render_pending {
|
||||
self.backtrack_render_pending = false;
|
||||
self.render_transcript_once(tui);
|
||||
@@ -4280,15 +4342,30 @@ impl App {
|
||||
}
|
||||
// Allow widgets to process any pending timers before rendering.
|
||||
self.chat_widget.pre_draw_tick();
|
||||
tui.draw(
|
||||
self.chat_widget.desired_height(tui.terminal.size()?.width),
|
||||
|frame| {
|
||||
self.chat_widget.render(frame.area(), frame.buffer);
|
||||
if let Some((x, y)) = self.chat_widget.cursor_pos(frame.area()) {
|
||||
frame.set_cursor_position((x, y));
|
||||
}
|
||||
},
|
||||
)?;
|
||||
let terminal_size = tui.terminal.size()?;
|
||||
let desired_height = self.chat_widget.desired_height(terminal_size.width);
|
||||
tracing::info!(
|
||||
event = "tui_render_started",
|
||||
render_trigger,
|
||||
cols = terminal_size.width,
|
||||
rows = terminal_size.height,
|
||||
desired_height,
|
||||
"starting TUI render"
|
||||
);
|
||||
tui.draw(desired_height, |frame| {
|
||||
self.chat_widget.render(frame.area(), frame.buffer);
|
||||
if let Some((x, y)) = self.chat_widget.cursor_pos(frame.area()) {
|
||||
frame.set_cursor_position((x, y));
|
||||
}
|
||||
})?;
|
||||
tracing::info!(
|
||||
event = "tui_render_completed",
|
||||
render_trigger,
|
||||
cols = terminal_size.width,
|
||||
rows = terminal_size.height,
|
||||
desired_height,
|
||||
"completed TUI render"
|
||||
);
|
||||
if self.chat_widget.external_editor_state() == ExternalEditorState::Requested {
|
||||
self.chat_widget
|
||||
.set_external_editor_state(ExternalEditorState::Active);
|
||||
|
||||
+58
-2
@@ -302,6 +302,7 @@ pub struct Tui {
|
||||
notification_backend: Option<DesktopNotificationBackend>,
|
||||
notification_condition: NotificationCondition,
|
||||
is_zellij: bool,
|
||||
terminal_name: TerminalName,
|
||||
resize_reflow_mode: ResizeReflowMode,
|
||||
force_full_repaint: bool,
|
||||
// When false, enter_alt_screen() becomes a no-op (for Zellij scrollback support)
|
||||
@@ -334,6 +335,19 @@ impl Tui {
|
||||
mode = ?resize_reflow_mode,
|
||||
terminal_name = ?terminal_info.name,
|
||||
term_program = ?terminal_info.term_program,
|
||||
term_program_version = ?terminal_info.version,
|
||||
term = ?terminal_info.term,
|
||||
colorterm = ?std::env::var("COLORTERM").ok(),
|
||||
target_os = std::env::consts::OS,
|
||||
target_arch = std::env::consts::ARCH,
|
||||
is_windows = cfg!(windows),
|
||||
is_macos = cfg!(target_os = "macos"),
|
||||
vscode_ipc_hook_cli_present = std::env::var_os("VSCODE_IPC_HOOK_CLI").is_some(),
|
||||
vscode_injection_present = std::env::var_os("VSCODE_INJECTION").is_some(),
|
||||
vscode_shell_login_present = std::env::var_os("VSCODE_SHELL_LOGIN").is_some(),
|
||||
vscode_cwd_present = std::env::var_os("VSCODE_CWD").is_some(),
|
||||
term_session_id_present = std::env::var_os("TERM_SESSION_ID").is_some(),
|
||||
lc_terminal_present = std::env::var_os("LC_TERMINAL").is_some(),
|
||||
"configured terminal resize reflow mode"
|
||||
);
|
||||
|
||||
@@ -352,6 +366,7 @@ impl Tui {
|
||||
notification_backend: Some(detect_backend(NotificationMethod::default())),
|
||||
notification_condition: NotificationCondition::default(),
|
||||
is_zellij,
|
||||
terminal_name: terminal_info.name,
|
||||
resize_reflow_mode,
|
||||
force_full_repaint: false,
|
||||
alt_screen_enabled: true,
|
||||
@@ -384,6 +399,10 @@ impl Tui {
|
||||
self.resize_reflow_mode
|
||||
}
|
||||
|
||||
pub(crate) fn terminal_name(&self) -> TerminalName {
|
||||
self.terminal_name
|
||||
}
|
||||
|
||||
pub fn enhanced_keys_supported(&self) -> bool {
|
||||
self.enhanced_keys_supported
|
||||
}
|
||||
@@ -637,7 +656,26 @@ impl Tui {
|
||||
self.pending_viewport_area()?
|
||||
};
|
||||
|
||||
stdout().sync_update(|_| {
|
||||
let terminal_size = self.terminal.size().ok();
|
||||
let viewport_area = self.terminal.viewport_area;
|
||||
tracing::info!(
|
||||
event = "terminal_draw_started",
|
||||
requested_height = height,
|
||||
force_full_repaint,
|
||||
pending_viewport_area = pending_viewport_area.is_some(),
|
||||
pending_history_lines = self.pending_history_lines.len(),
|
||||
terminal_cols = terminal_size.map(|size| size.width),
|
||||
terminal_rows = terminal_size.map(|size| size.height),
|
||||
viewport_x = viewport_area.x,
|
||||
viewport_y = viewport_area.y,
|
||||
viewport_width = viewport_area.width,
|
||||
viewport_height = viewport_area.height,
|
||||
terminal_name = ?self.terminal_name,
|
||||
resize_reflow_mode = ?self.resize_reflow_mode,
|
||||
"starting terminal draw"
|
||||
);
|
||||
|
||||
let draw_result = stdout().sync_update(|_| {
|
||||
#[cfg(unix)]
|
||||
if let Some(prepared) = prepared_resume.take() {
|
||||
prepared.apply(&mut self.terminal)?;
|
||||
@@ -683,7 +721,25 @@ impl Tui {
|
||||
terminal.draw(|frame| {
|
||||
draw_fn(frame);
|
||||
})
|
||||
})?
|
||||
})?;
|
||||
|
||||
match &draw_result {
|
||||
Ok(()) => tracing::info!(
|
||||
event = "terminal_draw_completed",
|
||||
requested_height = height,
|
||||
force_full_repaint,
|
||||
"completed terminal draw"
|
||||
),
|
||||
Err(err) => tracing::warn!(
|
||||
event = "terminal_draw_failed",
|
||||
requested_height = height,
|
||||
force_full_repaint,
|
||||
error = %err,
|
||||
"terminal draw failed"
|
||||
),
|
||||
}
|
||||
|
||||
draw_result
|
||||
}
|
||||
|
||||
fn pending_viewport_area(&mut self) -> Result<Option<Rect>> {
|
||||
|
||||
@@ -242,6 +242,7 @@ pub struct TuiEventStream<S: EventSource + Default + Unpin = CrosstermEventSourc
|
||||
draw_stream: BroadcastStream<()>,
|
||||
resume_stream: WatchStream<()>,
|
||||
resize_watchdog: Option<ResizeWatchdog>,
|
||||
terminal_name: TerminalName,
|
||||
terminal_focused: Arc<AtomicBool>,
|
||||
poll_draw_first: bool,
|
||||
#[cfg(unix)]
|
||||
@@ -263,6 +264,26 @@ impl<S: EventSource + Default + Unpin> TuiEventStream<S> {
|
||||
let has_superset_env_markers = has_superset_env_markers();
|
||||
let enable_resize_watchdog =
|
||||
should_enable_superset_resize_watchdog(&terminal_info, has_superset_env_markers);
|
||||
tracing::info!(
|
||||
event = "terminal_resize_event_stream_configured",
|
||||
terminal_name = ?terminal_info.name,
|
||||
term_program = ?terminal_info.term_program,
|
||||
term_program_version = ?terminal_info.version,
|
||||
term = ?terminal_info.term,
|
||||
colorterm = ?std::env::var("COLORTERM").ok(),
|
||||
target_os = std::env::consts::OS,
|
||||
target_arch = std::env::consts::ARCH,
|
||||
is_windows = cfg!(windows),
|
||||
is_macos = cfg!(target_os = "macos"),
|
||||
vscode_ipc_hook_cli_present = std::env::var_os("VSCODE_IPC_HOOK_CLI").is_some(),
|
||||
vscode_injection_present = std::env::var_os("VSCODE_INJECTION").is_some(),
|
||||
vscode_shell_login_present = std::env::var_os("VSCODE_SHELL_LOGIN").is_some(),
|
||||
vscode_cwd_present = std::env::var_os("VSCODE_CWD").is_some(),
|
||||
term_session_id_present = std::env::var_os("TERM_SESSION_ID").is_some(),
|
||||
lc_terminal_present = std::env::var_os("LC_TERMINAL").is_some(),
|
||||
resize_watchdog_enabled = enable_resize_watchdog,
|
||||
"configured terminal resize event stream"
|
||||
);
|
||||
tracing::info!(
|
||||
event = "superset_resize_watchdog_configured",
|
||||
enabled = enable_resize_watchdog,
|
||||
@@ -279,6 +300,7 @@ impl<S: EventSource + Default + Unpin> TuiEventStream<S> {
|
||||
draw_stream: BroadcastStream::new(draw_rx),
|
||||
resume_stream,
|
||||
resize_watchdog,
|
||||
terminal_name: terminal_info.name,
|
||||
terminal_focused,
|
||||
poll_draw_first: false,
|
||||
#[cfg(unix)]
|
||||
@@ -342,8 +364,22 @@ impl<S: EventSource + Default + Unpin> TuiEventStream<S> {
|
||||
/// Poll the draw broadcast stream for the next draw event. Draw events are used to trigger a redraw of the TUI.
|
||||
pub fn poll_draw_event(&mut self, cx: &mut Context<'_>) -> Poll<Option<TuiEvent>> {
|
||||
match Pin::new(&mut self.draw_stream).poll_next(cx) {
|
||||
Poll::Ready(Some(Ok(()))) => Poll::Ready(Some(TuiEvent::Draw)),
|
||||
Poll::Ready(Some(Ok(()))) => {
|
||||
tracing::info!(
|
||||
event = "tui_draw_event_received",
|
||||
terminal_name = ?self.terminal_name,
|
||||
target_os = std::env::consts::OS,
|
||||
"received scheduled TUI draw event"
|
||||
);
|
||||
Poll::Ready(Some(TuiEvent::Draw))
|
||||
}
|
||||
Poll::Ready(Some(Err(BroadcastStreamRecvError::Lagged(_)))) => {
|
||||
tracing::warn!(
|
||||
event = "tui_draw_event_lagged",
|
||||
terminal_name = ?self.terminal_name,
|
||||
target_os = std::env::consts::OS,
|
||||
"scheduled TUI draw event lagged"
|
||||
);
|
||||
Poll::Ready(Some(TuiEvent::Draw))
|
||||
}
|
||||
Poll::Ready(None) => Poll::Ready(None),
|
||||
@@ -388,6 +424,10 @@ impl<S: EventSource + Default + Unpin> TuiEventStream<S> {
|
||||
event = "crossterm_resize_event_received",
|
||||
cols,
|
||||
rows,
|
||||
terminal_name = ?self.terminal_name,
|
||||
target_os = std::env::consts::OS,
|
||||
is_windows = cfg!(windows),
|
||||
is_macos = cfg!(target_os = "macos"),
|
||||
watchdog_enabled = self.resize_watchdog.is_some(),
|
||||
"received crossterm terminal resize event"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user