edit predictions: Polish up ⌥ preview experience (#24380)
- Do not accept with just `tab` in `when_holding_modifer` mode - Fix fake cursor for jumps when destination row is outside viewport - Use current preview state for deciding whether to show modifiers in popovers - Stay in preview state if ⌥ isn't released after accepting a jump Release Notes: - N/A
This commit is contained in:
parent
c24f22cd14
commit
13089d7ec6
4 changed files with 110 additions and 125 deletions
|
@ -82,8 +82,8 @@ use gpui::{
|
|||
Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId,
|
||||
FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext, Modifiers, MouseButton,
|
||||
MouseDownEvent, PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled,
|
||||
StyledText, Subscription, Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
|
||||
UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window,
|
||||
StyledText, Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection,
|
||||
UnderlineStyle, UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window,
|
||||
};
|
||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||
use hover_popover::{hide_hover, HoverState};
|
||||
|
@ -468,7 +468,7 @@ pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
|
|||
type CompletionId = usize;
|
||||
|
||||
pub(crate) enum EditDisplayMode {
|
||||
TabAccept(bool),
|
||||
TabAccept,
|
||||
DiffPopover,
|
||||
Inline,
|
||||
}
|
||||
|
@ -493,15 +493,6 @@ struct InlineCompletionState {
|
|||
invalidation_range: Range<Anchor>,
|
||||
}
|
||||
|
||||
impl InlineCompletionState {
|
||||
pub fn is_move(&self) -> bool {
|
||||
match &self.completion {
|
||||
InlineCompletion::Move { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum InlineCompletionHighlight {}
|
||||
|
||||
pub enum MenuInlineCompletionsPolicy {
|
||||
|
@ -1499,10 +1490,14 @@ impl Editor {
|
|||
if self.pending_rename.is_some() {
|
||||
key_context.add("renaming");
|
||||
}
|
||||
|
||||
let mut showing_completions = false;
|
||||
|
||||
match self.context_menu.borrow().as_ref() {
|
||||
Some(CodeContextMenu::Completions(_)) => {
|
||||
key_context.add("menu");
|
||||
key_context.add("showing_completions");
|
||||
showing_completions = true;
|
||||
}
|
||||
Some(CodeContextMenu::CodeActions(_)) => {
|
||||
key_context.add("menu");
|
||||
|
@ -1532,6 +1527,10 @@ impl Editor {
|
|||
if self.has_active_inline_completion() {
|
||||
key_context.add("copilot_suggestion");
|
||||
key_context.add("inline_completion");
|
||||
|
||||
if showing_completions || self.inline_completion_requires_modifier(cx) {
|
||||
key_context.add("inline_completion_requires_modifier");
|
||||
}
|
||||
}
|
||||
|
||||
if self.selection_mark_mode {
|
||||
|
@ -4664,7 +4663,7 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
fn inline_completion_preview_mode(&self, cx: &App) -> language::InlineCompletionPreviewMode {
|
||||
fn inline_completion_requires_modifier(&self, cx: &App) -> bool {
|
||||
let cursor = self.selections.newest_anchor().head();
|
||||
|
||||
self.buffer
|
||||
|
@ -4672,8 +4671,9 @@ impl Editor {
|
|||
.text_anchor_for_position(cursor, cx)
|
||||
.map(|(buffer, _)| {
|
||||
all_language_settings(buffer.read(cx).file(), cx).inline_completions_preview_mode()
|
||||
== InlineCompletionPreviewMode::WhenHoldingModifier
|
||||
})
|
||||
.unwrap_or_default()
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn should_show_inline_completions_in_buffer(
|
||||
|
@ -5042,9 +5042,7 @@ impl Editor {
|
|||
return true;
|
||||
}
|
||||
|
||||
has_completion
|
||||
&& self.inline_completion_preview_mode(cx)
|
||||
== InlineCompletionPreviewMode::WhenHoldingModifier
|
||||
has_completion && self.inline_completion_requires_modifier(cx)
|
||||
}
|
||||
|
||||
fn update_inline_completion_preview(
|
||||
|
@ -5053,23 +5051,13 @@ impl Editor {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
// Moves jump directly without a preview step
|
||||
if self
|
||||
.active_inline_completion
|
||||
.as_ref()
|
||||
.map_or(true, |c| c.is_move())
|
||||
{
|
||||
self.previewing_inline_completion = false;
|
||||
cx.notify();
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.show_inline_completions_in_menu(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.previewing_inline_completion = modifiers.alt;
|
||||
self.update_visible_inline_completion(window, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn update_visible_inline_completion(
|
||||
|
@ -5198,7 +5186,7 @@ impl Editor {
|
|||
|
||||
let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
|
||||
if provider.show_tab_accept_marker() {
|
||||
EditDisplayMode::TabAccept(self.previewing_inline_completion)
|
||||
EditDisplayMode::TabAccept
|
||||
} else {
|
||||
EditDisplayMode::Inline
|
||||
}
|
||||
|
@ -5494,8 +5482,6 @@ impl Editor {
|
|||
min_width: Pixels,
|
||||
max_width: Pixels,
|
||||
cursor_point: Point,
|
||||
start_row: DisplayRow,
|
||||
line_layouts: &[LineWithInvisibles],
|
||||
style: &EditorStyle,
|
||||
accept_keystroke: &gpui::Keystroke,
|
||||
window: &Window,
|
||||
|
@ -5556,9 +5542,8 @@ impl Editor {
|
|||
Some(completion) => self.render_edit_prediction_cursor_popover_preview(
|
||||
completion,
|
||||
cursor_point,
|
||||
start_row,
|
||||
line_layouts,
|
||||
style,
|
||||
window,
|
||||
cx,
|
||||
)?,
|
||||
|
||||
|
@ -5566,9 +5551,8 @@ impl Editor {
|
|||
Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
|
||||
stale_completion,
|
||||
cursor_point,
|
||||
start_row,
|
||||
line_layouts,
|
||||
style,
|
||||
window,
|
||||
cx,
|
||||
)?,
|
||||
|
||||
|
@ -5599,19 +5583,6 @@ impl Editor {
|
|||
|
||||
let has_completion = self.active_inline_completion.is_some();
|
||||
|
||||
let is_move = self
|
||||
.active_inline_completion
|
||||
.as_ref()
|
||||
.map_or(false, |c| c.is_move());
|
||||
|
||||
let modifier_color = if !has_completion {
|
||||
Color::Muted
|
||||
} else if window.modifiers() == accept_keystroke.modifiers {
|
||||
Color::Accent
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
|
||||
Some(
|
||||
h_flex()
|
||||
.h(self.edit_prediction_cursor_popover_height())
|
||||
|
@ -5631,18 +5602,15 @@ impl Editor {
|
|||
ui::render_modifiers(
|
||||
&accept_keystroke.modifiers,
|
||||
PlatformStyle::platform(),
|
||||
Some(modifier_color),
|
||||
!is_move,
|
||||
Some(if !has_completion {
|
||||
Color::Muted
|
||||
} else {
|
||||
Color::Default
|
||||
}),
|
||||
true,
|
||||
),
|
||||
))
|
||||
.child(if is_move {
|
||||
div()
|
||||
.child(ui::Key::new(&accept_keystroke.key, None))
|
||||
.font(buffer_font.clone())
|
||||
.into_any()
|
||||
} else {
|
||||
Label::new("Preview").into_any_element()
|
||||
})
|
||||
.child(Label::new("Preview").into_any_element())
|
||||
.opacity(if has_completion { 1.0 } else { 0.4 }),
|
||||
)
|
||||
.into_any(),
|
||||
|
@ -5653,9 +5621,8 @@ impl Editor {
|
|||
&self,
|
||||
completion: &InlineCompletionState,
|
||||
cursor_point: Point,
|
||||
start_row: DisplayRow,
|
||||
line_layouts: &[LineWithInvisibles],
|
||||
style: &EditorStyle,
|
||||
window: &Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) -> Option<Div> {
|
||||
use text::ToPoint as _;
|
||||
|
@ -5732,6 +5699,7 @@ impl Editor {
|
|||
|
||||
let preview = h_flex()
|
||||
.gap_1()
|
||||
.min_w_16()
|
||||
.child(styled_text)
|
||||
.when(len_total > first_line_len, |parent| parent.child("…"));
|
||||
|
||||
|
@ -5764,18 +5732,46 @@ impl Editor {
|
|||
None,
|
||||
&style.syntax,
|
||||
);
|
||||
let base = h_flex().gap_3().flex_1().child(render_relative_row_jump(
|
||||
"Jump ",
|
||||
cursor_point.row,
|
||||
target.text_anchor.to_point(&snapshot).row,
|
||||
));
|
||||
|
||||
if highlighted_text.text.is_empty() {
|
||||
return Some(base);
|
||||
}
|
||||
|
||||
let cursor_color = self.current_user_player_color(cx).cursor;
|
||||
|
||||
let start_point = range_around_target.start.to_point(&snapshot);
|
||||
let end_point = range_around_target.end.to_point(&snapshot);
|
||||
let target_point = target.text_anchor.to_point(&snapshot);
|
||||
|
||||
let cursor_relative_position = line_layouts
|
||||
.get(start_point.row.saturating_sub(start_row.0) as usize)
|
||||
let styled_text = highlighted_text.to_styled_text(&style.text);
|
||||
let text_len = highlighted_text.text.len();
|
||||
|
||||
let cursor_relative_position = window
|
||||
.text_system()
|
||||
.layout_line(
|
||||
highlighted_text.text,
|
||||
style.text.font_size.to_pixels(window.rem_size()),
|
||||
// We don't need to include highlights
|
||||
// because we are only using this for the cursor position
|
||||
&[TextRun {
|
||||
len: text_len,
|
||||
font: style.text.font(),
|
||||
color: style.text.color,
|
||||
background_color: None,
|
||||
underline: None,
|
||||
strikethrough: None,
|
||||
}],
|
||||
)
|
||||
.log_err()
|
||||
.map(|line| {
|
||||
let start_column_x = line.x_for_index(start_point.column as usize);
|
||||
let target_column_x = line.x_for_index(target_point.column as usize);
|
||||
target_column_x - start_column_x
|
||||
line.x_for_index(
|
||||
target_point.column.saturating_sub(start_point.column) as usize
|
||||
)
|
||||
});
|
||||
|
||||
let fade_before = start_point.column > 0;
|
||||
|
@ -5783,56 +5779,40 @@ impl Editor {
|
|||
|
||||
let background = cx.theme().colors().elevated_surface_background;
|
||||
|
||||
Some(
|
||||
h_flex()
|
||||
.gap_3()
|
||||
.flex_1()
|
||||
.child(render_relative_row_jump(
|
||||
"Jump ",
|
||||
cursor_point.row,
|
||||
target.text_anchor.to_point(&snapshot).row,
|
||||
let preview = h_flex()
|
||||
.relative()
|
||||
.child(styled_text)
|
||||
.when(fade_before, |parent| {
|
||||
parent.child(div().absolute().top_0().left_0().w_4().h_full().bg(
|
||||
linear_gradient(
|
||||
90.,
|
||||
linear_color_stop(background, 0.),
|
||||
linear_color_stop(background.opacity(0.), 1.),
|
||||
),
|
||||
))
|
||||
.when(!highlighted_text.text.is_empty(), |parent| {
|
||||
parent.child(
|
||||
h_flex()
|
||||
.relative()
|
||||
.child(highlighted_text.to_styled_text(&style.text))
|
||||
.when(fade_before, |parent| {
|
||||
parent.child(
|
||||
div().absolute().top_0().left_0().w_4().h_full().bg(
|
||||
linear_gradient(
|
||||
90.,
|
||||
linear_color_stop(background, 0.),
|
||||
linear_color_stop(background.opacity(0.), 1.),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
.when(fade_after, |parent| {
|
||||
parent.child(
|
||||
div().absolute().top_0().right_0().w_4().h_full().bg(
|
||||
linear_gradient(
|
||||
-90.,
|
||||
linear_color_stop(background, 0.),
|
||||
linear_color_stop(background.opacity(0.), 1.),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
.when_some(cursor_relative_position, |parent, position| {
|
||||
parent.child(
|
||||
div()
|
||||
.w(px(2.))
|
||||
.h_full()
|
||||
.bg(cursor_color)
|
||||
.absolute()
|
||||
.top_0()
|
||||
.left(position),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
.when(fade_after, |parent| {
|
||||
parent.child(div().absolute().top_0().right_0().w_4().h_full().bg(
|
||||
linear_gradient(
|
||||
-90.,
|
||||
linear_color_stop(background, 0.),
|
||||
linear_color_stop(background.opacity(0.), 1.),
|
||||
),
|
||||
))
|
||||
})
|
||||
.when_some(cursor_relative_position, |parent, position| {
|
||||
parent.child(
|
||||
div()
|
||||
.w(px(2.))
|
||||
.h_full()
|
||||
.bg(cursor_color)
|
||||
.absolute()
|
||||
.top_0()
|
||||
.left(position),
|
||||
)
|
||||
});
|
||||
|
||||
Some(base.child(preview))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1653,7 +1653,7 @@ impl EditorElement {
|
|||
if let Some(inline_completion) = editor.active_inline_completion.as_ref() {
|
||||
match &inline_completion.completion {
|
||||
InlineCompletion::Edit {
|
||||
display_mode: EditDisplayMode::TabAccept(_),
|
||||
display_mode: EditDisplayMode::TabAccept,
|
||||
..
|
||||
} => padding += INLINE_ACCEPT_SUGGESTION_EM_WIDTHS,
|
||||
_ => {}
|
||||
|
@ -3238,8 +3238,6 @@ impl EditorElement {
|
|||
min_width,
|
||||
max_width,
|
||||
cursor_point,
|
||||
start_row,
|
||||
&line_layouts,
|
||||
style,
|
||||
accept_keystroke.as_ref()?,
|
||||
window,
|
||||
|
@ -3714,8 +3712,7 @@ impl EditorElement {
|
|||
}
|
||||
|
||||
match display_mode {
|
||||
EditDisplayMode::TabAccept(previewing) => {
|
||||
let previewing = *previewing;
|
||||
EditDisplayMode::TabAccept => {
|
||||
let range = &edits.first()?.0;
|
||||
let target_display_point = range.end.to_display_point(editor_snapshot);
|
||||
|
||||
|
@ -3723,14 +3720,22 @@ impl EditorElement {
|
|||
target_display_point.row(),
|
||||
editor_snapshot.line_len(target_display_point.row()),
|
||||
);
|
||||
let origin = self.editor.update(cx, |editor, _cx| {
|
||||
editor.display_to_pixel_point(target_line_end, editor_snapshot, window)
|
||||
})?;
|
||||
let (previewing_inline_completion, origin) =
|
||||
self.editor.update(cx, |editor, _cx| {
|
||||
Some((
|
||||
editor.previewing_inline_completion,
|
||||
editor.display_to_pixel_point(
|
||||
target_line_end,
|
||||
editor_snapshot,
|
||||
window,
|
||||
)?,
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut element = inline_completion_accept_indicator(
|
||||
"Accept",
|
||||
None,
|
||||
previewing,
|
||||
previewing_inline_completion,
|
||||
self.editor.focus_handle(cx),
|
||||
window,
|
||||
cx,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue