Skip to content

Commit 128b7d2

Browse files
danilo-lealagu-zmgsloan
authored
agent: Improve edit file tool card (#29448)
⚠️ Work in progress until all of the to-dos are knocked out: - [x] Disable soft-wrapping - [x] Make it foldable only after a certain number of lines - [x] Display tool status errors - [x] Fix horizontal scroll now that we've disabled soft-wrap - [ ] Don't render unnecessary extra lines (will be added later, on a follow-up PR) Release Notes: - N/A --------- Co-authored-by: Agus Zubiaga <hi@aguz.me> Co-authored-by: Michael Sloan <mgsloan@gmail.com>
1 parent fbb0fe4 commit 128b7d2

File tree

4 files changed

+132
-60
lines changed

4 files changed

+132
-60
lines changed

crates/assistant_tools/src/edit_file_tool.rs

Lines changed: 106 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use gpui::{
1111
};
1212
use language::{
1313
Anchor, Buffer, Capability, LanguageRegistry, LineEnding, OffsetRangeExt, Rope, TextBuffer,
14+
language_settings::SoftWrap,
1415
};
1516
use language_model::{LanguageModelRequestMessage, LanguageModelToolSchemaFormat};
1617
use project::Project;
@@ -274,7 +275,9 @@ pub struct EditFileToolCard {
274275
project: Entity<Project>,
275276
diff_task: Option<Task<Result<()>>>,
276277
preview_expanded: bool,
278+
error_expanded: bool,
277279
full_height_expanded: bool,
280+
total_lines: Option<u32>,
278281
editor_unique_id: EntityId,
279282
}
280283

@@ -293,11 +296,13 @@ impl EditFileToolCard {
293296
window,
294297
cx,
295298
);
296-
editor.set_show_scrollbars(false, cx);
297299
editor.set_show_gutter(false, cx);
298300
editor.disable_inline_diagnostics();
299-
editor.disable_scrolling(cx);
300301
editor.disable_expand_excerpt_buttons(cx);
302+
editor.set_soft_wrap_mode(SoftWrap::None, cx);
303+
editor.scroll_manager.set_forbid_vertical_scroll(true);
304+
editor.set_show_scrollbars(false, cx);
305+
editor.set_read_only(true);
301306
editor.set_show_breakpoints(false, cx);
302307
editor.set_show_code_actions(false, cx);
303308
editor.set_show_git_diff_gutter(false, cx);
@@ -312,7 +317,9 @@ impl EditFileToolCard {
312317
multibuffer,
313318
diff_task: None,
314319
preview_expanded: true,
320+
error_expanded: false,
315321
full_height_expanded: false,
322+
total_lines: None,
316323
}
317324
}
318325

@@ -329,7 +336,7 @@ impl EditFileToolCard {
329336
let buffer_diff = build_buffer_diff(old_text, &buffer, &language_registry, cx).await?;
330337

331338
this.update(cx, |this, cx| {
332-
this.multibuffer.update(cx, |multibuffer, cx| {
339+
this.total_lines = this.multibuffer.update(cx, |multibuffer, cx| {
333340
let snapshot = buffer.read(cx).snapshot();
334341
let diff = buffer_diff.read(cx);
335342
let diff_hunk_ranges = diff
@@ -345,7 +352,10 @@ impl EditFileToolCard {
345352
);
346353
debug_assert!(is_newly_added);
347354
multibuffer.add_diff(buffer_diff, cx);
355+
let end = multibuffer.len(cx);
356+
Some(multibuffer.snapshot(cx).offset_to_point(end).row + 1)
348357
});
358+
349359
cx.notify();
350360
})
351361
}));
@@ -360,7 +370,10 @@ impl ToolCard for EditFileToolCard {
360370
workspace: WeakEntity<Workspace>,
361371
cx: &mut Context<Self>,
362372
) -> impl IntoElement {
363-
let failed = matches!(status, ToolUseStatus::Error(_));
373+
let (failed, error_message) = match status {
374+
ToolUseStatus::Error(err) => (true, Some(err.to_string())),
375+
_ => (false, None),
376+
};
364377

365378
let path_label_button = h_flex()
366379
.id(("edit-tool-path-label-button", self.editor_unique_id))
@@ -452,9 +465,26 @@ impl ToolCard for EditFileToolCard {
452465
.map(|container| {
453466
if failed {
454467
container.child(
455-
Icon::new(IconName::Close)
456-
.size(IconSize::Small)
457-
.color(Color::Error),
468+
h_flex()
469+
.gap_1()
470+
.child(
471+
Icon::new(IconName::Close)
472+
.size(IconSize::Small)
473+
.color(Color::Error),
474+
)
475+
.child(
476+
Disclosure::new(
477+
("edit-file-error-disclosure", self.editor_unique_id),
478+
self.error_expanded,
479+
)
480+
.opened_icon(IconName::ChevronUp)
481+
.closed_icon(IconName::ChevronDown)
482+
.on_click(cx.listener(
483+
move |this, _event, _window, _cx| {
484+
this.error_expanded = !this.error_expanded;
485+
},
486+
)),
487+
),
458488
)
459489
} else {
460490
container.child(
@@ -473,8 +503,14 @@ impl ToolCard for EditFileToolCard {
473503
}
474504
});
475505

476-
let editor = self.editor.update(cx, |editor, cx| {
477-
editor.render(window, cx).into_any_element()
506+
let (editor, editor_line_height) = self.editor.update(cx, |editor, cx| {
507+
let line_height = editor
508+
.style()
509+
.map(|style| style.text.line_height_in_pixels(window.rem_size()))
510+
.unwrap_or_default();
511+
512+
let element = editor.render(window, cx);
513+
(element.into_any_element(), line_height)
478514
});
479515

480516
let (full_height_icon, full_height_tooltip_label) = if self.full_height_expanded {
@@ -498,6 +534,9 @@ impl ToolCard for EditFileToolCard {
498534

499535
let border_color = cx.theme().colors().border.opacity(0.6);
500536

537+
const DEFAULT_COLLAPSED_LINES: u32 = 10;
538+
let is_collapsible = self.total_lines.unwrap_or(0) > DEFAULT_COLLAPSED_LINES;
539+
501540
v_flex()
502541
.mb_2()
503542
.border_1()
@@ -506,50 +545,79 @@ impl ToolCard for EditFileToolCard {
506545
.rounded_lg()
507546
.overflow_hidden()
508547
.child(codeblock_header)
509-
.when(!failed && self.preview_expanded, |card| {
548+
.when(failed && self.error_expanded, |card| {
510549
card.child(
511550
v_flex()
512-
.relative()
513-
.overflow_hidden()
551+
.p_2()
552+
.gap_1()
514553
.border_t_1()
554+
.border_dashed()
515555
.border_color(border_color)
516556
.bg(cx.theme().colors().editor_background)
557+
.rounded_b_md()
558+
.child(
559+
Label::new("Error")
560+
.size(LabelSize::XSmall)
561+
.color(Color::Error),
562+
)
563+
.child(
564+
div()
565+
.rounded_md()
566+
.text_ui_sm(cx)
567+
.bg(cx.theme().colors().editor_background)
568+
.children(
569+
error_message
570+
.map(|error| div().child(error).into_any_element()),
571+
),
572+
),
573+
)
574+
})
575+
.when(!failed && self.preview_expanded, |card| {
576+
card.child(
577+
v_flex()
578+
.relative()
517579
.map(|editor_container| {
518580
if self.full_height_expanded {
519581
editor_container.h_full()
520582
} else {
521-
editor_container.max_h_64()
583+
editor_container
584+
.h(DEFAULT_COLLAPSED_LINES as f32 * editor_line_height)
522585
}
523586
})
524-
.child(div().pl_1().child(editor))
525-
.when(!self.full_height_expanded, |editor_container| {
526-
editor_container.child(gradient_overlay)
527-
}),
528-
)
529-
})
530-
.when(!failed && self.preview_expanded, |card| {
531-
card.child(
532-
h_flex()
533-
.id(("edit-tool-card-inner-hflex", self.editor_unique_id))
534-
.flex_none()
535-
.cursor_pointer()
536-
.h_5()
537-
.justify_center()
538-
.rounded_b_md()
587+
.overflow_hidden()
539588
.border_t_1()
540589
.border_color(border_color)
541590
.bg(cx.theme().colors().editor_background)
542-
.hover(|style| style.bg(cx.theme().colors().element_hover.opacity(0.1)))
543-
.child(
544-
Icon::new(full_height_icon)
545-
.size(IconSize::Small)
546-
.color(Color::Muted),
547-
)
548-
.tooltip(Tooltip::text(full_height_tooltip_label))
549-
.on_click(cx.listener(move |this, _event, _window, _cx| {
550-
this.full_height_expanded = !this.full_height_expanded;
551-
})),
591+
.child(div().pl_1().child(editor))
592+
.when(
593+
!self.full_height_expanded && is_collapsible,
594+
|editor_container| editor_container.child(gradient_overlay),
595+
),
552596
)
597+
.when(is_collapsible, |editor_container| {
598+
editor_container.child(
599+
h_flex()
600+
.id(("expand-button", self.editor_unique_id))
601+
.flex_none()
602+
.cursor_pointer()
603+
.h_5()
604+
.justify_center()
605+
.rounded_b_md()
606+
.border_t_1()
607+
.border_color(border_color)
608+
.bg(cx.theme().colors().editor_background)
609+
.hover(|style| style.bg(cx.theme().colors().element_hover.opacity(0.1)))
610+
.child(
611+
Icon::new(full_height_icon)
612+
.size(IconSize::Small)
613+
.color(Color::Muted),
614+
)
615+
.tooltip(Tooltip::text(full_height_tooltip_label))
616+
.on_click(cx.listener(move |this, _event, _window, _cx| {
617+
this.full_height_expanded = !this.full_height_expanded;
618+
})),
619+
)
620+
})
553621
})
554622
}
555623
}

crates/editor/src/editor.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,6 @@ pub struct Editor {
871871
show_breadcrumbs: bool,
872872
show_gutter: bool,
873873
show_scrollbars: bool,
874-
disable_scrolling: bool,
875874
disable_expand_excerpt_buttons: bool,
876875
show_line_numbers: Option<bool>,
877876
use_relative_line_numbers: Option<bool>,
@@ -1668,7 +1667,6 @@ impl Editor {
16681667
blink_manager: blink_manager.clone(),
16691668
show_local_selections: true,
16701669
show_scrollbars: true,
1671-
disable_scrolling: false,
16721670
mode,
16731671
show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
16741672
show_gutter: mode.is_full(),
@@ -16487,11 +16485,6 @@ impl Editor {
1648716485
cx.notify();
1648816486
}
1648916487

16490-
pub fn disable_scrolling(&mut self, cx: &mut Context<Self>) {
16491-
self.disable_scrolling = true;
16492-
cx.notify();
16493-
}
16494-
1649516488
pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
1649616489
self.show_line_numbers = Some(show_line_numbers);
1649716490
cx.notify();

crates/editor/src/element.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5678,9 +5678,7 @@ impl EditorElement {
56785678
}
56795679

56805680
fn paint_mouse_listeners(&mut self, layout: &EditorLayout, window: &mut Window, cx: &mut App) {
5681-
if !self.editor.read(cx).disable_scrolling {
5682-
self.paint_scroll_wheel_listener(layout, window, cx);
5683-
}
5681+
self.paint_scroll_wheel_listener(layout, window, cx);
56845682

56855683
window.on_mouse_event({
56865684
let position_map = layout.position_map.clone();

crates/editor/src/scroll.rs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,6 @@ impl ScrollManager {
184184
window: &mut Window,
185185
cx: &mut Context<Editor>,
186186
) {
187-
if self.forbid_vertical_scroll {
188-
return;
189-
}
190187
let (new_anchor, top_row) = if scroll_position.y <= 0. {
191188
(
192189
ScrollAnchor {
@@ -258,10 +255,16 @@ impl ScrollManager {
258255
window: &mut Window,
259256
cx: &mut Context<Editor>,
260257
) {
261-
if self.forbid_vertical_scroll {
262-
return;
263-
}
264-
self.anchor = anchor;
258+
let adjusted_anchor = if self.forbid_vertical_scroll {
259+
ScrollAnchor {
260+
offset: gpui::Point::new(anchor.offset.x, self.anchor.offset.y),
261+
anchor: self.anchor.anchor,
262+
}
263+
} else {
264+
anchor
265+
};
266+
267+
self.anchor = adjusted_anchor;
265268
cx.emit(EditorEvent::ScrollPositionChanged { local, autoscroll });
266269
self.show_scrollbars(window, cx);
267270
self.autoscroll_request.take();
@@ -404,11 +407,12 @@ impl Editor {
404407
window: &mut Window,
405408
cx: &mut Context<Self>,
406409
) {
410+
let mut delta = scroll_delta;
407411
if self.scroll_manager.forbid_vertical_scroll {
408-
return;
412+
delta.y = 0.0;
409413
}
410414
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
411-
let position = self.scroll_manager.anchor.scroll_position(&display_map) + scroll_delta;
415+
let position = self.scroll_manager.anchor.scroll_position(&display_map) + delta;
412416
self.set_scroll_position_taking_display_map(position, true, false, display_map, window, cx);
413417
}
414418

@@ -418,10 +422,12 @@ impl Editor {
418422
window: &mut Window,
419423
cx: &mut Context<Self>,
420424
) {
425+
let mut position = scroll_position;
421426
if self.scroll_manager.forbid_vertical_scroll {
422-
return;
427+
let current_position = self.scroll_position(cx);
428+
position.y = current_position.y;
423429
}
424-
self.set_scroll_position_internal(scroll_position, true, false, window, cx);
430+
self.set_scroll_position_internal(position, true, false, window, cx);
425431
}
426432

427433
/// Scrolls so that `row` is at the top of the editor view.
@@ -480,8 +486,15 @@ impl Editor {
480486
self.edit_prediction_preview
481487
.set_previous_scroll_position(None);
482488

489+
let adjusted_position = if self.scroll_manager.forbid_vertical_scroll {
490+
let current_position = self.scroll_manager.anchor.scroll_position(&display_map);
491+
gpui::Point::new(scroll_position.x, current_position.y)
492+
} else {
493+
scroll_position
494+
};
495+
483496
self.scroll_manager.set_scroll_position(
484-
scroll_position,
497+
adjusted_position,
485498
&display_map,
486499
local,
487500
autoscroll,

0 commit comments

Comments
 (0)