Editor end of input context (#40735)

This is needed for #38914 and seems generally useful to have for
contextual keybindings.

Release Notes:

- N/A

---------

Co-authored-by: David Kleingeld <davidsk@zed.dev>
This commit is contained in:
Julia Ryan
2025-10-20 17:08:51 -07:00
committed by GitHub
co-authored by David Kleingeld
parent 62516e8f1f
commit 267052f891
2 changed files with 40 additions and 8 deletions
+19 -8
View File
@@ -2466,15 +2466,15 @@ impl Editor {
})
}
pub fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
self.key_context_internal(self.has_active_edit_prediction(), window, cx)
}
fn key_context_internal(
&self,
has_active_edit_prediction: bool,
window: &Window,
cx: &App,
window: &mut Window,
cx: &mut App,
) -> KeyContext {
let mut key_context = KeyContext::new_with_defaults();
key_context.add("Editor");
@@ -2551,6 +2551,17 @@ impl Editor {
key_context.add("selection_mode");
}
let disjoint = self.selections.disjoint_anchors();
let snapshot = self.snapshot(window, cx);
let snapshot = snapshot.buffer_snapshot();
if self.mode == EditorMode::SingleLine
&& let [selection] = disjoint
&& selection.start == selection.end
&& selection.end.to_offset(snapshot) == snapshot.len()
{
key_context.add("end_of_input");
}
key_context
}
@@ -2604,8 +2615,8 @@ impl Editor {
pub fn accept_edit_prediction_keybind(
&self,
accept_partial: bool,
window: &Window,
cx: &App,
window: &mut Window,
cx: &mut App,
) -> AcceptEditPredictionBinding {
let key_context = self.key_context_internal(true, window, cx);
let in_conflict = self.edit_prediction_in_conflict();
@@ -2747,7 +2758,7 @@ impl Editor {
self.buffer().read(cx).title(cx)
}
pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
let git_blame_gutter_max_author_length = self
.render_git_blame_gutter(cx)
.then(|| {
@@ -9285,7 +9296,7 @@ impl Editor {
fn render_edit_prediction_accept_keybind(
&self,
window: &mut Window,
cx: &App,
cx: &mut App,
) -> Option<AnyElement> {
let accept_binding = self.accept_edit_prediction_keybind(false, window, cx);
let accept_keystroke = accept_binding.keystroke()?;
@@ -9331,7 +9342,7 @@ impl Editor {
label: impl Into<SharedString>,
icon: Option<IconName>,
window: &mut Window,
cx: &App,
cx: &mut App,
) -> Stateful<Div> {
let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
+21
View File
@@ -26838,3 +26838,24 @@ async fn test_copy_line_without_trailing_newline(cx: &mut TestAppContext) {
cx.assert_editor_state("line1\nline2\nˇ");
}
#[gpui::test]
async fn test_end_of_editor_context(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
cx.set_state("line1\nline2ˇ");
cx.update_editor(|e, window, cx| {
e.set_mode(EditorMode::SingleLine);
assert!(e.key_context(window, cx).contains("end_of_input"));
});
cx.set_state("ˇline1\nline2");
cx.update_editor(|e, window, cx| {
assert!(!e.key_context(window, cx).contains("end_of_input"));
});
cx.set_state("line1ˇ\nline2");
cx.update_editor(|e, window, cx| {
assert!(!e.key_context(window, cx).contains("end_of_input"));
});
}