diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d281724334..964039762a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2121,6 +2121,7 @@ impl Editor { let action_ix = action_ix.unwrap_or(actions_menu.selected_item); let action = actions_menu.actions.get(action_ix)?.clone(); let buffer = actions_menu.buffer; + let replica_id = editor.read(cx).replica_id(cx); let apply_code_actions = workspace.project().clone().update(cx, |project, cx| { project.apply_code_action(buffer, action, true, cx) @@ -2128,10 +2129,31 @@ impl Editor { Some(cx.spawn(|workspace, mut cx| async move { let project_transaction = apply_code_actions.await?; - // TODO: replace this with opening a single tab that is a multibuffer + let mut ranges_to_highlight = Vec::new(); + let excerpt_buffer = cx.add_model(|cx| { + let mut multibuffer = MultiBuffer::new(replica_id); + for (buffer, transaction) in &project_transaction.0 { + let snapshot = buffer.read(cx).snapshot(); + ranges_to_highlight.extend( + multibuffer.push_excerpts_with_context_lines( + buffer.clone(), + snapshot + .edited_ranges_for_transaction::(transaction) + .collect(), + 1, + cx, + ), + ); + } + multibuffer + }); + workspace.update(&mut cx, |workspace, cx| { - for (buffer, _) in project_transaction.0 { - workspace.open_item(BufferItemHandle(buffer), cx); + let editor = workspace.open_item(MultiBufferItemHandle(excerpt_buffer), cx); + if let Some(editor) = editor.act_as::(cx) { + editor.update(cx, |editor, cx| { + editor.highlight_ranges::(ranges_to_highlight, Color::blue(), cx); + }); } }); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 6828696b90..d051a13d7c 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -586,6 +586,71 @@ impl MultiBuffer { self.insert_excerpts_after(&ExcerptId::max(), buffer, ranges, cx) } + pub fn push_excerpts_with_context_lines( + &mut self, + buffer: ModelHandle, + ranges: Vec>, + context_line_count: u32, + cx: &mut ModelContext, + ) -> Vec> + where + O: text::ToPoint + text::ToOffset, + { + let buffer_id = buffer.id(); + let buffer_snapshot = buffer.read(cx).snapshot(); + let max_point = buffer_snapshot.max_point(); + + let mut range_counts = Vec::new(); + let mut excerpt_ranges = Vec::new(); + let mut range_iter = ranges + .iter() + .map(|range| { + range.start.to_point(&buffer_snapshot)..range.end.to_point(&buffer_snapshot) + }) + .peekable(); + while let Some(range) = range_iter.next() { + let excerpt_start = Point::new(range.start.row.saturating_sub(context_line_count), 0); + let mut excerpt_end = + Point::new(range.end.row + 1 + context_line_count, 0).min(max_point); + let mut ranges_in_excerpt = 1; + + while let Some(next_range) = range_iter.peek() { + if next_range.start.row <= excerpt_end.row + context_line_count { + excerpt_end = + Point::new(range.end.row + 1 + context_line_count, 0).min(max_point); + ranges_in_excerpt += 1; + range_iter.next(); + } else { + break; + } + } + + excerpt_ranges.push(excerpt_start..excerpt_end); + range_counts.push(ranges_in_excerpt); + } + + let excerpt_ids = self.push_excerpts(buffer, excerpt_ranges, cx); + + let mut anchor_ranges = Vec::new(); + let mut ranges = ranges.into_iter(); + for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.into_iter()) { + anchor_ranges.extend(ranges.by_ref().take(range_count).map(|range| { + let start = Anchor { + buffer_id, + excerpt_id: excerpt_id.clone(), + text_anchor: buffer_snapshot.anchor_after(range.start), + }; + let end = Anchor { + buffer_id, + excerpt_id: excerpt_id.clone(), + text_anchor: buffer_snapshot.anchor_after(range.end), + }; + start..end + })) + } + anchor_ranges + } + pub fn insert_excerpts_after( &mut self, prev_excerpt_id: &ExcerptId, @@ -2850,6 +2915,31 @@ mod tests { } } + #[gpui::test] + fn test_excerpts_with_context_lines(cx: &mut MutableAppContext) { + let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(20, 3, 'a'), cx)); + + let multibuffer = cx.add_model(|_| MultiBuffer::new(0)); + let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| { + multibuffer.push_excerpts_with_context_lines( + buffer.clone(), + vec![ + Point::new(3, 2)..Point::new(4, 2), + Point::new(7, 3)..Point::new(7, 1), + Point::new(15, 0)..Point::new(15, 0), + ], + 2, + cx, + ) + }); + + let snapshot = multibuffer.read(cx).snapshot(cx); + assert_eq!( + snapshot.text(), + "bbb\nccc\nddd\neee\nfff\nggg\n\nnnn\nooo\nppp\nqqq\nrrr\n" + ); + } + #[gpui::test] fn test_empty_excerpt_buffer(cx: &mut MutableAppContext) { let multibuffer = cx.add_model(|_| MultiBuffer::new(0));