Return all diffs

Agus Zubiaga , Ben Brandt , and Richard Feldman created

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>

Change summary

crates/acp_thread/src/acp_thread.rs    |  15 +--
crates/agent_ui/src/acp/thread_view.rs | 102 +++++++++++++++------------
crates/agent_ui/src/agent_diff.rs      |   6 -
crates/zed/src/zed.rs                  |   2 
4 files changed, 65 insertions(+), 60 deletions(-)

Detailed changes

crates/acp_thread/src/acp_thread.rs 🔗

@@ -137,16 +137,15 @@ impl AgentThreadEntry {
         match self {
             Self::UserMessage(message) => message.to_markdown(cx),
             Self::AssistantMessage(message) => message.to_markdown(cx),
-            Self::ToolCall(too_call) => too_call.to_markdown(cx),
+            Self::ToolCall(tool_call) => tool_call.to_markdown(cx),
         }
     }
 
-    // todo! return all diffs?
-    pub fn first_diff(&self) -> Option<&Diff> {
+    pub fn diffs(&self) -> impl Iterator<Item = &Diff> {
         if let AgentThreadEntry::ToolCall(call) = self {
-            call.first_diff()
+            itertools::Either::Left(call.diffs())
         } else {
-            None
+            itertools::Either::Right(std::iter::empty())
         }
     }
 
@@ -197,8 +196,8 @@ impl ToolCall {
         }
     }
 
-    pub fn first_diff(&self) -> Option<&Diff> {
-        self.content.iter().find_map(|content| match content {
+    pub fn diffs(&self) -> impl Iterator<Item = &Diff> {
+        self.content.iter().filter_map(|content| match content {
             ToolCallContent::ContentBlock { .. } => None,
             ToolCallContent::Diff { diff } => Some(diff),
         })
@@ -623,7 +622,7 @@ impl AcpThread {
         for entry in self.entries.iter().rev() {
             match entry {
                 AgentThreadEntry::UserMessage(_) => return false,
-                AgentThreadEntry::ToolCall(call) if call.first_diff().is_some() => return true,
+                AgentThreadEntry::ToolCall(call) if call.diffs().next().is_some() => return true,
                 AgentThreadEntry::ToolCall(_) | AgentThreadEntry::AssistantMessage(_) => {}
             }
         }

crates/agent_ui/src/acp/thread_view.rs 🔗

@@ -574,62 +574,70 @@ impl AcpThreadView {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        let Some(multibuffer) = self.entry_diff_multibuffer(entry_ix, cx) else {
+        let Some(multibuffers) = self.entry_diff_multibuffers(entry_ix, cx) else {
             return;
         };
 
-        if self.diff_editors.contains_key(&multibuffer.entity_id()) {
-            return;
-        }
+        let multibuffers = multibuffers.collect::<Vec<_>>();
 
-        let editor = cx.new(|cx| {
-            let mut editor = Editor::new(
-                EditorMode::Full {
-                    scale_ui_elements_with_buffer_font_size: false,
-                    show_active_line_background: false,
-                    sized_by_content: true,
-                },
-                multibuffer.clone(),
-                None,
-                window,
-                cx,
-            );
-            editor.set_show_gutter(false, cx);
-            editor.disable_inline_diagnostics();
-            editor.disable_expand_excerpt_buttons(cx);
-            editor.set_show_vertical_scrollbar(false, cx);
-            editor.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
-            editor.set_soft_wrap_mode(SoftWrap::None, cx);
-            editor.scroll_manager.set_forbid_vertical_scroll(true);
-            editor.set_show_indent_guides(false, cx);
-            editor.set_read_only(true);
-            editor.set_show_breakpoints(false, cx);
-            editor.set_show_code_actions(false, cx);
-            editor.set_show_git_diff_gutter(false, cx);
-            editor.set_expand_all_diff_hunks(cx);
-            editor.set_text_style_refinement(TextStyleRefinement {
-                font_size: Some(
-                    TextSize::Small
-                        .rems(cx)
-                        .to_pixels(ThemeSettings::get_global(cx).agent_font_size(cx))
-                        .into(),
-                ),
-                ..Default::default()
+        for multibuffer in multibuffers {
+            if self.diff_editors.contains_key(&multibuffer.entity_id()) {
+                return;
+            }
+
+            let editor = cx.new(|cx| {
+                let mut editor = Editor::new(
+                    EditorMode::Full {
+                        scale_ui_elements_with_buffer_font_size: false,
+                        show_active_line_background: false,
+                        sized_by_content: true,
+                    },
+                    multibuffer.clone(),
+                    None,
+                    window,
+                    cx,
+                );
+                editor.set_show_gutter(false, cx);
+                editor.disable_inline_diagnostics();
+                editor.disable_expand_excerpt_buttons(cx);
+                editor.set_show_vertical_scrollbar(false, cx);
+                editor.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
+                editor.set_soft_wrap_mode(SoftWrap::None, cx);
+                editor.scroll_manager.set_forbid_vertical_scroll(true);
+                editor.set_show_indent_guides(false, cx);
+                editor.set_read_only(true);
+                editor.set_show_breakpoints(false, cx);
+                editor.set_show_code_actions(false, cx);
+                editor.set_show_git_diff_gutter(false, cx);
+                editor.set_expand_all_diff_hunks(cx);
+                editor.set_text_style_refinement(TextStyleRefinement {
+                    font_size: Some(
+                        TextSize::Small
+                            .rems(cx)
+                            .to_pixels(ThemeSettings::get_global(cx).agent_font_size(cx))
+                            .into(),
+                    ),
+                    ..Default::default()
+                });
+                editor
             });
-            editor
-        });
-        let entity_id = multibuffer.entity_id();
-        cx.observe_release(&multibuffer, move |this, _, _| {
-            this.diff_editors.remove(&entity_id);
-        })
-        .detach();
+            let entity_id = multibuffer.entity_id();
+            cx.observe_release(&multibuffer, move |this, _, _| {
+                this.diff_editors.remove(&entity_id);
+            })
+            .detach();
 
-        self.diff_editors.insert(entity_id, editor);
+            self.diff_editors.insert(entity_id, editor);
+        }
     }
 
-    fn entry_diff_multibuffer(&self, entry_ix: usize, cx: &App) -> Option<Entity<MultiBuffer>> {
+    fn entry_diff_multibuffers(
+        &self,
+        entry_ix: usize,
+        cx: &App,
+    ) -> Option<impl Iterator<Item = Entity<MultiBuffer>>> {
         let entry = self.thread()?.read(cx).entries().get(entry_ix)?;
-        entry.first_diff().map(|diff| diff.multibuffer.clone())
+        Some(entry.diffs().map(|diff| diff.multibuffer.clone()))
     }
 
     fn authenticate(&mut self, window: &mut Window, cx: &mut Context<Self>) {

crates/agent_ui/src/agent_diff.rs 🔗

@@ -1506,8 +1506,7 @@ impl AgentDiff {
                     .read(cx)
                     .entries()
                     .last()
-                    .and_then(|entry| entry.first_diff())
-                    .is_some()
+                    .map_or(false, |entry| entry.diffs().next().is_some())
                 {
                     self.update_reviewing_editors(workspace, window, cx);
                 }
@@ -1517,8 +1516,7 @@ impl AgentDiff {
                     .read(cx)
                     .entries()
                     .get(*ix)
-                    .and_then(|entry| entry.first_diff())
-                    .is_some()
+                    .map_or(false, |entry| entry.diffs().next().is_some())
                 {
                     self.update_reviewing_editors(workspace, window, cx);
                 }

crates/zed/src/zed.rs 🔗

@@ -19,7 +19,7 @@ use collections::VecDeque;
 use debugger_ui::debugger_panel::DebugPanel;
 use editor::ProposedChangesEditorToolbar;
 use editor::{Editor, MultiBuffer};
-use feature_flags::FeatureFlagAppExt;
+use feature_flags::{FeatureFlagAppExt, PanicFeatureFlag};
 use futures::future::Either;
 use futures::{StreamExt, channel::mpsc, select_biased};
 use git_ui::git_panel::GitPanel;