From 7c26c8e091851957d58da7b82f390a3de9bc7e80 Mon Sep 17 00:00:00 2001 From: Jeremy Rose <172423086+nornagon-openai@users.noreply.github.com> Date: Fri, 15 Aug 2025 13:55:44 -0400 Subject: [PATCH] tui: skip identical consecutive entries in local composer history (#2352) This PR avoids inserting duplicate consecutive messages into the Chat Composer's local history. --- .../src/bottom_pane/chat_composer_history.rs | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/codex-rs/tui/src/bottom_pane/chat_composer_history.rs b/codex-rs/tui/src/bottom_pane/chat_composer_history.rs index 93accfcd..04b745d1 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer_history.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer_history.rs @@ -55,11 +55,18 @@ impl ChatComposerHistory { /// Record a message submitted by the user in the current session so it can /// be recalled later. pub fn record_local_submission(&mut self, text: &str) { - if !text.is_empty() { - self.local_history.push(text.to_string()); - self.history_cursor = None; - self.last_history_text = None; + if text.is_empty() { + return; } + + // Avoid inserting a duplicate if identical to the previous entry. + if self.local_history.last().is_some_and(|prev| prev == text) { + return; + } + + self.local_history.push(text.to_string()); + self.history_cursor = None; + self.last_history_text = None; } /// Should Up/Down key presses be interpreted as history navigation given @@ -187,6 +194,29 @@ mod tests { use codex_core::protocol::Op; use std::sync::mpsc::channel; + #[test] + fn duplicate_submissions_are_not_recorded() { + let mut history = ChatComposerHistory::new(); + + // Empty submissions are ignored. + history.record_local_submission(""); + assert_eq!(history.local_history.len(), 0); + + // First entry is recorded. + history.record_local_submission("hello"); + assert_eq!(history.local_history.len(), 1); + assert_eq!(history.local_history.last().unwrap(), "hello"); + + // Identical consecutive entry is skipped. + history.record_local_submission("hello"); + assert_eq!(history.local_history.len(), 1); + + // Different entry is recorded. + history.record_local_submission("world"); + assert_eq!(history.local_history.len(), 2); + assert_eq!(history.local_history.last().unwrap(), "world"); + } + #[test] fn navigation_with_async_fetch() { let (tx, rx) = channel::();