Make slash command output streamable (#19632)
This PR adds support for streaming output from slash commands In this PR we are focused primarily on the interface of the `SlashCommand` trait to support streaming the output. We will follow up later with support for extensions and context servers to take advantage of the streaming nature. Release Notes: - N/A --------- Co-authored-by: David Soria Parra <davidsp@anthropic.com> Co-authored-by: Antonio Scandurra <me@as-cii.com> Co-authored-by: David <david@anthropic.com> Co-authored-by: Antonio <antonio@zed.dev> Co-authored-by: Max <max@zed.dev> Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com> Co-authored-by: Will <will@zed.dev>
This commit is contained in:
parent
f6fbf662b4
commit
b129e18396
14 changed files with 1130 additions and 501 deletions
|
@ -75,8 +75,8 @@ use gpui::{
|
|||
AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardEntry,
|
||||
ClipboardItem, Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent,
|
||||
FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext,
|
||||
ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString,
|
||||
Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
|
||||
ListSizingBehavior, Model, ModelContext, MouseButton, PaintQuad, ParentElement, Pixels, Render,
|
||||
SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
|
||||
TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle, View,
|
||||
ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle, WeakView, WindowContext,
|
||||
};
|
||||
|
@ -1849,7 +1849,7 @@ impl Editor {
|
|||
editor
|
||||
.update(cx, |editor, cx| {
|
||||
editor.unfold_ranges(
|
||||
[fold_range.start..fold_range.end],
|
||||
&[fold_range.start..fold_range.end],
|
||||
true,
|
||||
false,
|
||||
cx,
|
||||
|
@ -1861,6 +1861,7 @@ impl Editor {
|
|||
.into_any()
|
||||
}),
|
||||
merge_adjacent: true,
|
||||
..Default::default()
|
||||
};
|
||||
let display_map = cx.new_model(|cx| {
|
||||
DisplayMap::new(
|
||||
|
@ -6810,7 +6811,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
self.transact(cx, |this, cx| {
|
||||
this.unfold_ranges(unfold_ranges, true, true, cx);
|
||||
this.unfold_ranges(&unfold_ranges, true, true, cx);
|
||||
this.buffer.update(cx, |buffer, cx| {
|
||||
for (range, text) in edits {
|
||||
buffer.edit([(range, text)], None, cx);
|
||||
|
@ -6904,7 +6905,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
self.transact(cx, |this, cx| {
|
||||
this.unfold_ranges(unfold_ranges, true, true, cx);
|
||||
this.unfold_ranges(&unfold_ranges, true, true, cx);
|
||||
this.buffer.update(cx, |buffer, cx| {
|
||||
for (range, text) in edits {
|
||||
buffer.edit([(range, text)], None, cx);
|
||||
|
@ -8256,7 +8257,7 @@ impl Editor {
|
|||
to_unfold.push(selection.start..selection.end);
|
||||
}
|
||||
}
|
||||
self.unfold_ranges(to_unfold, true, true, cx);
|
||||
self.unfold_ranges(&to_unfold, true, true, cx);
|
||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges(new_selection_ranges);
|
||||
});
|
||||
|
@ -8387,7 +8388,7 @@ impl Editor {
|
|||
auto_scroll: Option<Autoscroll>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
this.unfold_ranges([range.clone()], false, true, cx);
|
||||
this.unfold_ranges(&[range.clone()], false, true, cx);
|
||||
this.change_selections(auto_scroll, cx, |s| {
|
||||
if replace_newest {
|
||||
s.delete(s.newest_anchor().id);
|
||||
|
@ -8598,7 +8599,10 @@ impl Editor {
|
|||
|
||||
select_next_state.done = true;
|
||||
self.unfold_ranges(
|
||||
new_selections.iter().map(|selection| selection.range()),
|
||||
&new_selections
|
||||
.iter()
|
||||
.map(|selection| selection.range())
|
||||
.collect::<Vec<_>>(),
|
||||
false,
|
||||
false,
|
||||
cx,
|
||||
|
@ -8667,7 +8671,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
if let Some(next_selected_range) = next_selected_range {
|
||||
self.unfold_ranges([next_selected_range.clone()], false, true, cx);
|
||||
self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
|
||||
self.change_selections(Some(Autoscroll::newest()), cx, |s| {
|
||||
if action.replace_newest {
|
||||
s.delete(s.newest_anchor().id);
|
||||
|
@ -8744,7 +8748,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
self.unfold_ranges(
|
||||
selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
|
||||
&selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
|
||||
false,
|
||||
true,
|
||||
cx,
|
||||
|
@ -10986,7 +10990,7 @@ impl Editor {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.unfold_ranges(ranges, true, true, cx);
|
||||
self.unfold_ranges(&ranges, true, true, cx);
|
||||
}
|
||||
|
||||
pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
|
||||
|
@ -11004,7 +11008,7 @@ impl Editor {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.unfold_ranges(ranges, true, true, cx);
|
||||
self.unfold_ranges(&ranges, true, true, cx);
|
||||
}
|
||||
|
||||
pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
|
||||
|
@ -11022,13 +11026,13 @@ impl Editor {
|
|||
.iter()
|
||||
.any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
|
||||
|
||||
self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
|
||||
self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
|
||||
}
|
||||
|
||||
pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
self.unfold_ranges(
|
||||
[Point::zero()..display_map.max_point().to_point(&display_map)],
|
||||
&[Point::zero()..display_map.max_point().to_point(&display_map)],
|
||||
true,
|
||||
true,
|
||||
cx,
|
||||
|
@ -11104,39 +11108,63 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
/// Removes any folds whose ranges intersect any of the given ranges.
|
||||
pub fn unfold_ranges<T: ToOffset + Clone>(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = Range<T>>,
|
||||
ranges: &[Range<T>],
|
||||
inclusive: bool,
|
||||
auto_scroll: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let mut unfold_ranges = Vec::new();
|
||||
self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
|
||||
map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
|
||||
});
|
||||
}
|
||||
|
||||
/// Removes any folds with the given ranges.
|
||||
pub fn remove_folds_with_type<T: ToOffset + Clone>(
|
||||
&mut self,
|
||||
ranges: &[Range<T>],
|
||||
type_id: TypeId,
|
||||
auto_scroll: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
|
||||
map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
|
||||
});
|
||||
}
|
||||
|
||||
fn remove_folds_with<T: ToOffset + Clone>(
|
||||
&mut self,
|
||||
ranges: &[Range<T>],
|
||||
auto_scroll: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
|
||||
) {
|
||||
if ranges.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut buffers_affected = HashMap::default();
|
||||
let multi_buffer = self.buffer().read(cx);
|
||||
for range in ranges {
|
||||
if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
|
||||
buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
|
||||
};
|
||||
unfold_ranges.push(range);
|
||||
}
|
||||
|
||||
let mut ranges = unfold_ranges.into_iter().peekable();
|
||||
if ranges.peek().is_some() {
|
||||
self.display_map
|
||||
.update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
|
||||
if auto_scroll {
|
||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
||||
}
|
||||
|
||||
for buffer in buffers_affected.into_values() {
|
||||
self.sync_expanded_diff_hunks(buffer, cx);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
self.scrollbar_marker_state.dirty = true;
|
||||
self.active_indent_guides_state.dirty = true;
|
||||
self.display_map.update(cx, update);
|
||||
if auto_scroll {
|
||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
||||
}
|
||||
|
||||
for buffer in buffers_affected.into_values() {
|
||||
self.sync_expanded_diff_hunks(buffer, cx);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
self.scrollbar_marker_state.dirty = true;
|
||||
self.active_indent_guides_state.dirty = true;
|
||||
}
|
||||
|
||||
pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue