Improve local tool execution and shell output panes
This commit is contained in:
@@ -448,7 +448,7 @@ impl TryFrom<GrepResult> for api::request::input::tool_call_result::Result {
|
||||
|
||||
fn try_from(result: GrepResult) -> Result<Self, Self::Error> {
|
||||
match result {
|
||||
GrepResult::Success { matched_files } => Ok(
|
||||
GrepResult::Success { matched_files, .. } => Ok(
|
||||
api::request::input::tool_call_result::Result::Grep(api::GrepResult {
|
||||
result: Some(api::grep_result::Result::Success(
|
||||
api::grep_result::Success {
|
||||
|
||||
@@ -1166,15 +1166,22 @@ impl AIAgentActionResultType {
|
||||
Self::RequestFileEdits(RequestFileEditsResult::Cancelled) => {
|
||||
"apply_file_diffs: cancelled".to_string()
|
||||
}
|
||||
Self::Grep(GrepResult::Success { matched_files }) => {
|
||||
format!(
|
||||
Self::Grep(GrepResult::Success {
|
||||
matched_files,
|
||||
truncated,
|
||||
}) => {
|
||||
let mut summary = format!(
|
||||
"grep: [{}]",
|
||||
matched_files
|
||||
.iter()
|
||||
.map(|f| f.file_path.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
);
|
||||
if *truncated {
|
||||
summary.push_str(" (additional matches omitted)");
|
||||
}
|
||||
summary
|
||||
}
|
||||
Self::Grep(GrepResult::Error(error)) => format!("grep: error={error}"),
|
||||
Self::Grep(GrepResult::Cancelled) => "grep: cancelled".to_string(),
|
||||
@@ -1294,7 +1301,10 @@ impl AIAgentActionResultType {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum GrepResult {
|
||||
Success { matched_files: Vec<GrepFileMatch> },
|
||||
Success {
|
||||
matched_files: Vec<GrepFileMatch>,
|
||||
truncated: bool,
|
||||
},
|
||||
Error(String),
|
||||
Cancelled,
|
||||
}
|
||||
@@ -1302,12 +1312,22 @@ pub enum GrepResult {
|
||||
impl Display for GrepResult {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
GrepResult::Success { matched_files } => {
|
||||
GrepResult::Success {
|
||||
matched_files,
|
||||
truncated,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"Grep found matches in: [{}]",
|
||||
matched_files.iter().format(", ")
|
||||
)
|
||||
)?;
|
||||
if *truncated {
|
||||
write!(
|
||||
f,
|
||||
". Additional matches were omitted; narrow the query or path to retrieve them"
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
GrepResult::Error(error) => write!(f, "Grep error: {error}"),
|
||||
GrepResult::Cancelled => write!(f, "Grep cancelled"),
|
||||
|
||||
@@ -38,6 +38,8 @@ pub struct ScrollTarget {
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ClippedScrollData {
|
||||
scroll_start_px: Pixels,
|
||||
max_scroll_start_px: Pixels,
|
||||
follow_end: bool,
|
||||
pub(super) scroll_to_position: Option<ScrollTarget>,
|
||||
selection_scroll_anchor: Option<ClippedSelectionScrollAnchor>,
|
||||
}
|
||||
@@ -77,6 +79,36 @@ impl ClippedScrollStateHandle {
|
||||
self.clipped_scroll_data.lock().scroll_start_px
|
||||
}
|
||||
|
||||
/// Returns whether the scrollable is currently at its maximum scroll position.
|
||||
///
|
||||
/// The maximum is captured during layout, so consumers can implement follow-output
|
||||
/// behavior without estimating content or viewport sizes themselves.
|
||||
pub fn is_scrolled_to_end(&self) -> bool {
|
||||
let data = self.clipped_scroll_data.lock();
|
||||
data.scroll_start_px.as_f32() + 0.5 >= data.max_scroll_start_px.as_f32()
|
||||
}
|
||||
|
||||
/// Controls whether layout should keep the scroll position docked to the content's end.
|
||||
pub fn set_follow_end(&self, follow_end: bool) {
|
||||
self.clipped_scroll_data.lock().follow_end = follow_end;
|
||||
}
|
||||
|
||||
pub(in crate::elements) fn update_scroll_extent(
|
||||
&self,
|
||||
visible_px: Pixels,
|
||||
total_size: Pixels,
|
||||
) -> Pixels {
|
||||
let max_scroll_start_px = (total_size - visible_px).max(Pixels::zero());
|
||||
let mut data = self.clipped_scroll_data.lock();
|
||||
data.max_scroll_start_px = max_scroll_start_px;
|
||||
data.scroll_start_px = if data.follow_end {
|
||||
max_scroll_start_px
|
||||
} else {
|
||||
data.scroll_start_px.min(max_scroll_start_px)
|
||||
};
|
||||
data.scroll_start_px
|
||||
}
|
||||
|
||||
pub fn scroll_by(&self, delta: Pixels) {
|
||||
self.scroll_to(self.scroll_start() + delta);
|
||||
}
|
||||
@@ -417,12 +449,8 @@ impl Element for ClippedScrollable {
|
||||
// Make sure that the new layout doesn't put the scroll bar in an invalid
|
||||
// location.
|
||||
if let Some(scroll_data) = self.scroll_data(app) {
|
||||
let max_scroll_top =
|
||||
(scroll_data.total_size - scroll_data.visible_px).max(Pixels::zero());
|
||||
let scroll_top = scroll_data.scroll_start;
|
||||
if scroll_top > max_scroll_top {
|
||||
self.state.scroll_to(max_scroll_top);
|
||||
}
|
||||
self.state
|
||||
.update_scroll_extent(scroll_data.visible_px, scroll_data.total_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1577,10 +1577,12 @@ impl SelectableElement for NewScrollable {
|
||||
|
||||
impl ClippedScrollStateHandle {
|
||||
fn scroll_data(&self, viewport_size: Vector2F, child_size: Vector2F, axis: Axis) -> ScrollData {
|
||||
let visible_px = viewport_size.along(axis).into_pixels();
|
||||
let total_size = child_size.along(axis).into_pixels();
|
||||
ScrollData {
|
||||
scroll_start: self.scroll_start(),
|
||||
visible_px: (viewport_size.along(axis)).into_pixels(),
|
||||
total_size: child_size.along(axis).into_pixels(),
|
||||
scroll_start: self.update_scroll_extent(visible_px, total_size),
|
||||
visible_px,
|
||||
total_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user