Make edit prediction bindings backwards compatible with existing user keymaps (#24802)
Release Notes: - N/A --------- Co-authored-by: Antonio <antonio@zed.dev>
This commit is contained in:
parent
d57f5937d4
commit
c3afeda80b
8 changed files with 185 additions and 106 deletions
|
@ -49,7 +49,6 @@ gpui.workspace = true
|
|||
http_client.workspace = true
|
||||
indoc.workspace = true
|
||||
inline_completion.workspace = true
|
||||
inventory.workspace = true
|
||||
itertools.workspace = true
|
||||
language.workspace = true
|
||||
linkify.workspace = true
|
||||
|
|
|
@ -194,8 +194,7 @@ pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
|
|||
pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
|
||||
|
||||
pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
|
||||
pub(crate) const EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT: &str =
|
||||
"edit_prediction_requires_modifier";
|
||||
pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
|
||||
|
||||
pub fn render_parsed_markdown(
|
||||
element_id: impl Into<ElementId>,
|
||||
|
@ -1545,13 +1544,10 @@ impl Editor {
|
|||
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");
|
||||
|
@ -1579,15 +1575,11 @@ impl Editor {
|
|||
}
|
||||
|
||||
if has_active_edit_prediction {
|
||||
key_context.add("copilot_suggestion");
|
||||
key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
|
||||
if showing_completions
|
||||
|| self.edit_prediction_requires_modifier()
|
||||
// Require modifier key when the cursor is on leading whitespace, to allow `tab`
|
||||
// bindings to insert tab characters.
|
||||
|| (self.edit_prediction_requires_modifier_in_leading_space && self.edit_prediction_cursor_on_leading_whitespace)
|
||||
{
|
||||
key_context.add(EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT);
|
||||
if self.edit_prediction_in_conflict() {
|
||||
key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
|
||||
} else {
|
||||
key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
|
||||
key_context.add("copilot_suggestion");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1598,16 +1590,40 @@ impl Editor {
|
|||
key_context
|
||||
}
|
||||
|
||||
pub fn edit_prediction_in_conflict(&self) -> bool {
|
||||
let showing_completions = self
|
||||
.context_menu
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map_or(false, |context| {
|
||||
matches!(context, CodeContextMenu::Completions(_))
|
||||
});
|
||||
|
||||
showing_completions
|
||||
|| self.edit_prediction_requires_modifier()
|
||||
// Require modifier key when the cursor is on leading whitespace, to allow `tab`
|
||||
// bindings to insert tab characters.
|
||||
|| (self.edit_prediction_requires_modifier_in_leading_space && self.edit_prediction_cursor_on_leading_whitespace)
|
||||
}
|
||||
|
||||
pub fn accept_edit_prediction_keybind(
|
||||
&self,
|
||||
window: &Window,
|
||||
cx: &App,
|
||||
) -> AcceptEditPredictionBinding {
|
||||
let key_context = self.key_context_internal(true, window, cx);
|
||||
let in_conflict = self.edit_prediction_in_conflict();
|
||||
AcceptEditPredictionBinding(
|
||||
window
|
||||
.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
|
||||
.into_iter()
|
||||
.filter(|binding| {
|
||||
!in_conflict
|
||||
|| binding
|
||||
.keystrokes()
|
||||
.first()
|
||||
.map_or(false, |keystroke| keystroke.modifiers.modified())
|
||||
})
|
||||
.rev()
|
||||
.next(),
|
||||
)
|
||||
|
|
|
@ -15,14 +15,13 @@ use crate::{
|
|||
items::BufferSearchHighlights,
|
||||
mouse_context_menu::{self, MenuPosition, MouseContextMenu},
|
||||
scroll::{axis_pair, scroll_amount::ScrollAmount, AxisPair},
|
||||
AcceptEditPrediction, BlockId, ChunkReplacement, CursorShape, CustomBlockId, DisplayPoint,
|
||||
DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode,
|
||||
BlockId, ChunkReplacement, CursorShape, CustomBlockId, DisplayPoint, DisplayRow,
|
||||
DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode,
|
||||
EditorSettings, EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GoToHunk,
|
||||
GoToPrevHunk, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor,
|
||||
InlineCompletion, JumpData, LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point,
|
||||
RevertSelectedHunks, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap,
|
||||
StickyHeaderExcerpt, ToPoint, ToggleFold, CURSORS_VISIBLE_FOR,
|
||||
EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT, FILE_HEADER_HEIGHT,
|
||||
StickyHeaderExcerpt, ToPoint, ToggleFold, CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT,
|
||||
GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
|
||||
};
|
||||
use buffer_diff::{DiffHunkSecondaryStatus, DiffHunkStatus};
|
||||
|
@ -35,11 +34,11 @@ use gpui::{
|
|||
point, px, quad, relative, size, svg, transparent_black, Action, AnyElement, App,
|
||||
AvailableSpace, Axis, Bounds, ClickEvent, ClipboardItem, ContentMask, Context, Corner, Corners,
|
||||
CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Focusable as _,
|
||||
FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement,
|
||||
KeyBindingContextPredicate, Keystroke, Length, ModifiersChangedEvent, MouseButton,
|
||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta,
|
||||
ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled,
|
||||
Subscription, TextRun, TextStyleRefinement, WeakEntity, Window,
|
||||
FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Keystroke, Length,
|
||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
||||
ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size,
|
||||
StatefulInteractiveElement, Style, Styled, Subscription, TextRun, TextStyleRefinement,
|
||||
WeakEntity, Window,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
|
@ -55,7 +54,7 @@ use multi_buffer::{
|
|||
RowInfo, ToOffset,
|
||||
};
|
||||
use project::project_settings::{GitGutterSetting, ProjectSettings};
|
||||
use settings::{KeyBindingValidator, KeyBindingValidatorRegistration, Settings};
|
||||
use settings::Settings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
|
@ -75,7 +74,7 @@ use ui::{
|
|||
POPOVER_Y_PADDING,
|
||||
};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use util::{markdown::MarkdownString, RangeExt, ResultExt};
|
||||
use util::{RangeExt, ResultExt};
|
||||
use workspace::{item::Item, notifications::NotifyTaskExt, Workspace};
|
||||
|
||||
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.;
|
||||
|
@ -5834,50 +5833,6 @@ impl AcceptEditPredictionBinding {
|
|||
}
|
||||
}
|
||||
|
||||
struct AcceptEditPredictionsBindingValidator;
|
||||
|
||||
inventory::submit! { KeyBindingValidatorRegistration(|| Box::new(AcceptEditPredictionsBindingValidator)) }
|
||||
|
||||
impl KeyBindingValidator for AcceptEditPredictionsBindingValidator {
|
||||
fn action_type_id(&self) -> TypeId {
|
||||
TypeId::of::<AcceptEditPrediction>()
|
||||
}
|
||||
|
||||
fn validate(&self, binding: &gpui::KeyBinding) -> Result<(), MarkdownString> {
|
||||
use KeyBindingContextPredicate::*;
|
||||
|
||||
if binding.keystrokes().len() == 1 && binding.keystrokes()[0].modifiers.modified() {
|
||||
return Ok(());
|
||||
}
|
||||
let required_predicate =
|
||||
Not(Identifier(EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT.into()).into());
|
||||
match binding.predicate() {
|
||||
Some(predicate) if required_predicate.is_superset(&predicate) => {
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let negated_requires_modifier_key_context = MarkdownString::inline_code(&format!(
|
||||
"!{}",
|
||||
EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT
|
||||
));
|
||||
Err(MarkdownString(format!(
|
||||
"{} can only be bound to a single keystroke with modifiers, so \
|
||||
that pressing these modifiers can be used for prediction \
|
||||
preview.\n\n\
|
||||
This restriction does not apply when the context requires {}, \
|
||||
since these bindings are not used for prediction preview. For \
|
||||
example, in the default keymap `tab` requires {}, and `alt-tab` \
|
||||
is used otherwise.\n\n\
|
||||
See [the documentation]({}) for more details.",
|
||||
MarkdownString::inline_code(AcceptEditPrediction.name()),
|
||||
negated_requires_modifier_key_context.clone(),
|
||||
negated_requires_modifier_key_context,
|
||||
"https://zed.dev/docs/completions#edit-predictions",
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn prepaint_gutter_button(
|
||||
button: IconButton,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue