editor: Accept next line prediction (#44411)

Closes  [#20574](https://github.com/zed-industries/zed/issues/20574)

Release Notes:

- Replaced editor action editor::AcceptPartialEditPrediction with
editor::AcceptNextLineEditPrediction and
editor::AcceptNextWordEditPrediction

Tested manually on windows, attaching screen cap.
https://github.com/user-attachments/assets/fea04499-fd16-4b7d-a6aa-3661bb85cf4f

Updated existing test for accepting word prediction in copilot - it is
already marked as flaky, not sure what to do about it and I'm not really
confident creating new one without a working example.

Added migration of keymaps and new defaults for windows, linux, macos in
defaults and in cursor.

This should alleviate
[#21645](https://github.com/zed-industries/zed/issues/21645)
I used some work done in stale PR
https://github.com/zed-industries/zed/pull/25274, hopefully this one
makes it through!

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>
This commit is contained in:
teleoflexuousandAgus Zubiaga authored and GitHub committed 2025-12-15 18:28:59 +00:00
1 parent b8a7b2981b
commit ff08911ffe
14 files changed
+280 -183

No files matched your search

+4 -2
View File
@@ -746,7 +746,8 @@
"alt-tab": "editor::AcceptEditPrediction",
"alt-l": "editor::AcceptEditPrediction",
"tab": "editor::AcceptEditPrediction",
"alt-right": "editor::AcceptPartialEditPrediction",
"alt-right": "editor::AcceptNextWordEditPrediction",
"alt-down": "editor::AcceptNextLineEditPrediction",
},
},
{
@@ -754,7 +755,8 @@
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"alt-l": "editor::AcceptEditPrediction",
"alt-right": "editor::AcceptPartialEditPrediction",
"alt-right": "editor::AcceptNextWordEditPrediction",
"alt-down": "editor::AcceptNextLineEditPrediction",
},
},
{
+4 -2
View File
@@ -810,7 +810,8 @@
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"tab": "editor::AcceptEditPrediction",
"ctrl-cmd-right": "editor::AcceptPartialEditPrediction",
"ctrl-cmd-right": "editor::AcceptNextWordEditPrediction",
"ctrl-cmd-down": "editor::AcceptNextLineEditPrediction",
},
},
{
@@ -818,7 +819,8 @@
"use_key_equivalents": true,
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"ctrl-cmd-right": "editor::AcceptPartialEditPrediction",
"ctrl-cmd-right": "editor::AcceptNextWordEditPrediction",
"ctrl-cmd-down": "editor::AcceptNextLineEditPrediction",
},
},
{
+4 -2
View File
@@ -747,7 +747,8 @@
"alt-tab": "editor::AcceptEditPrediction",
"alt-l": "editor::AcceptEditPrediction",
"tab": "editor::AcceptEditPrediction",
"alt-right": "editor::AcceptPartialEditPrediction",
"alt-right": "editor::AcceptNextWordEditPrediction",
"alt-down": "editor::AcceptNextLineEditPrediction",
},
},
{
@@ -756,7 +757,8 @@
"bindings": {
"alt-tab": "editor::AcceptEditPrediction",
"alt-l": "editor::AcceptEditPrediction",
"alt-right": "editor::AcceptPartialEditPrediction",
"alt-right": "editor::AcceptNextWordEditPrediction",
"alt-down": "editor::AcceptNextLineEditPrediction",
},
},
{
+2 -1
View File
@@ -71,7 +71,8 @@
"context": "Editor && mode == full && edit_prediction",
"use_key_equivalents": true,
"bindings": {
"cmd-right": "editor::AcceptPartialEditPrediction",
"cmd-right": "editor::AcceptNextWordEditPrediction",
"cmd-down": "editor::AcceptNextLineEditPrediction",
},
},
{
+5 -3
View File
@@ -261,12 +261,14 @@ fn update_command_palette_filter(cx: &mut App) {
CommandPaletteFilter::update_global(cx, |filter, _| {
use editor::actions::{
AcceptEditPrediction, AcceptPartialEditPrediction, NextEditPrediction,
PreviousEditPrediction, ShowEditPrediction, ToggleEditPrediction,
AcceptEditPrediction, AcceptNextLineEditPrediction, AcceptNextWordEditPrediction,
NextEditPrediction, PreviousEditPrediction, ShowEditPrediction, ToggleEditPrediction,
};
let edit_prediction_actions = [
TypeId::of::<AcceptEditPrediction>(),
TypeId::of::<AcceptPartialEditPrediction>(),
TypeId::of::<AcceptNextWordEditPrediction>(),
TypeId::of::<AcceptNextLineEditPrediction>(),
TypeId::of::<AcceptEditPrediction>(),
TypeId::of::<ShowEditPrediction>(),
TypeId::of::<NextEditPrediction>(),
TypeId::of::<PreviousEditPrediction>(),
@@ -269,6 +269,7 @@ fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b:
#[cfg(test)]
mod tests {
use super::*;
use edit_prediction_types::EditPredictionGranularity;
use editor::{
Editor, ExcerptRange, MultiBuffer, MultiBufferOffset, SelectionEffects,
test::editor_lsp_test_context::EditorLspTestContext,
@@ -581,13 +582,15 @@ mod tests {
assert!(editor.has_active_edit_prediction());
// Accepting the first word of the suggestion should only accept the first word and still show the rest.
editor.accept_partial_edit_prediction(&Default::default(), window, cx);
editor.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
assert!(editor.has_active_edit_prediction());
assert_eq!(editor.text(cx), "one.copilot\ntwo\nthree\n");
assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
// Accepting next word should accept the non-word and copilot suggestion should be gone
editor.accept_partial_edit_prediction(&Default::default(), window, cx);
editor.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
assert!(!editor.has_active_edit_prediction());
assert_eq!(editor.text(cx), "one.copilot1\ntwo\nthree\n");
assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
@@ -623,7 +626,7 @@ mod tests {
assert!(editor.has_active_edit_prediction());
// Accepting the first word (non-word) of the suggestion should only accept the first word and still show the rest.
editor.accept_partial_edit_prediction(&Default::default(), window, cx);
editor.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
assert!(editor.has_active_edit_prediction());
assert_eq!(editor.text(cx), "one.123. \ntwo\nthree\n");
assert_eq!(
@@ -632,7 +635,7 @@ mod tests {
);
// Accepting next word should accept the next word and copilot suggestion should still exist
editor.accept_partial_edit_prediction(&Default::default(), window, cx);
editor.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
assert!(editor.has_active_edit_prediction());
assert_eq!(editor.text(cx), "one.123. copilot\ntwo\nthree\n");
assert_eq!(
@@ -641,7 +644,7 @@ mod tests {
);
// Accepting the whitespace should accept the non-word/whitespaces with newline and copilot suggestion should be gone
editor.accept_partial_edit_prediction(&Default::default(), window, cx);
editor.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
assert!(!editor.has_active_edit_prediction());
assert_eq!(editor.text(cx), "one.123. copilot\n 456\ntwo\nthree\n");
assert_eq!(
@@ -249,6 +249,12 @@ where
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EditPredictionGranularity {
Word,
Line,
Full,
}
/// Returns edits updated based on user edits since the old snapshot. None is returned if any user
/// edit is not a prefix of a predicted insertion.
pub fn interpolate_edits(
+2 -1
View File
@@ -370,7 +370,8 @@ actions!(
AcceptEditPrediction,
/// Accepts a partial edit prediction.
#[action(deprecated_aliases = ["editor::AcceptPartialCopilotSuggestion"])]
AcceptPartialEditPrediction,
AcceptNextWordEditPrediction,
AcceptNextLineEditPrediction,
/// Applies all diff hunks in the editor.
ApplyAllDiffHunks,
/// Applies the diff hunk at the current position.
+188 -163
View File
@@ -92,7 +92,9 @@ use collections::{BTreeMap, HashMap, HashSet, VecDeque};
use convert_case::{Case, Casing};
use dap::TelemetrySpawnLocation;
use display_map::*;
use edit_prediction_types::{EditPredictionDelegate, EditPredictionDelegateHandle};
use edit_prediction_types::{
EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionGranularity,
};
use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line};
use futures::{
@@ -2778,21 +2780,24 @@ impl Editor {
pub fn accept_edit_prediction_keybind(
&self,
accept_partial: bool,
granularity: EditPredictionGranularity,
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();
let bindings = if accept_partial {
window.bindings_for_action_in_context(&AcceptPartialEditPrediction, key_context)
} else {
window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
};
let bindings =
match granularity {
EditPredictionGranularity::Word => window
.bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
EditPredictionGranularity::Line => window
.bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
EditPredictionGranularity::Full => {
window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
}
};
// TODO: if the binding contains multiple keystrokes, display all of them, not
// just the first one.
AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
!in_conflict
|| binding
@@ -7633,9 +7638,9 @@ impl Editor {
}
}
pub fn accept_edit_prediction(
pub fn accept_partial_edit_prediction(
&mut self,
_: &AcceptEditPrediction,
granularity: EditPredictionGranularity,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -7647,47 +7652,59 @@ impl Editor {
return;
};
if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
return;
}
match &active_edit_prediction.completion {
EditPrediction::MoveWithin { target, .. } => {
let target = *target;
if let Some(position_map) = &self.last_position_map {
if position_map
.visible_row_range
.contains(&target.to_display_point(&position_map.snapshot).row())
|| !self.edit_prediction_requires_modifier()
{
self.unfold_ranges(&[target..target], true, false, cx);
// Note that this is also done in vim's handler of the Tab action.
self.change_selections(
SelectionEffects::scroll(Autoscroll::newest()),
window,
cx,
|selections| {
selections.select_anchor_ranges([target..target]);
},
);
self.clear_row_highlights::<EditPredictionPreview>();
if matches!(granularity, EditPredictionGranularity::Full) {
if let Some(position_map) = &self.last_position_map {
let target_row = target.to_display_point(&position_map.snapshot).row();
let is_visible = position_map.visible_row_range.contains(&target_row);
self.edit_prediction_preview
.set_previous_scroll_position(None);
} else {
self.edit_prediction_preview
.set_previous_scroll_position(Some(
position_map.snapshot.scroll_anchor,
));
self.highlight_rows::<EditPredictionPreview>(
target..target,
cx.theme().colors().editor_highlighted_line_background,
RowHighlightOptions {
autoscroll: true,
..Default::default()
},
cx,
);
self.request_autoscroll(Autoscroll::fit(), cx);
if is_visible || !self.edit_prediction_requires_modifier() {
self.unfold_ranges(&[target..target], true, false, cx);
self.change_selections(
SelectionEffects::scroll(Autoscroll::newest()),
window,
cx,
|selections| {
selections.select_anchor_ranges([target..target]);
},
);
self.clear_row_highlights::<EditPredictionPreview>();
self.edit_prediction_preview
.set_previous_scroll_position(None);
} else {
// Highlight and request scroll
self.edit_prediction_preview
.set_previous_scroll_position(Some(
position_map.snapshot.scroll_anchor,
));
self.highlight_rows::<EditPredictionPreview>(
target..target,
cx.theme().colors().editor_highlighted_line_background,
RowHighlightOptions {
autoscroll: true,
..Default::default()
},
cx,
);
self.request_autoscroll(Autoscroll::fit(), cx);
}
}
} else {
self.change_selections(
SelectionEffects::scroll(Autoscroll::newest()),
window,
cx,
|selections| {
selections.select_anchor_ranges([target..target]);
},
);
}
}
EditPrediction::MoveOutside { snapshot, target } => {
@@ -7703,126 +7720,131 @@ impl Editor {
cx,
);
if let Some(provider) = self.edit_prediction_provider() {
provider.accept(cx);
}
match granularity {
EditPredictionGranularity::Full => {
if let Some(provider) = self.edit_prediction_provider() {
provider.accept(cx);
}
// Store the transaction ID and selections before applying the edit
let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
let snapshot = self.buffer.read(cx).snapshot(cx);
let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
let snapshot = self.buffer.read(cx).snapshot(cx);
let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
self.buffer.update(cx, |buffer, cx| {
buffer.edit(edits.iter().cloned(), None, cx)
});
self.buffer.update(cx, |buffer, cx| {
buffer.edit(edits.iter().cloned(), None, cx)
});
self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
s.select_anchor_ranges([last_edit_end..last_edit_end]);
});
self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
s.select_anchor_ranges([last_edit_end..last_edit_end]);
});
let selections = self.selections.disjoint_anchors_arc();
if let Some(transaction_id_now) =
self.buffer.read(cx).last_transaction_id(cx)
{
if transaction_id_prev != Some(transaction_id_now) {
self.selection_history
.insert_transaction(transaction_id_now, selections);
}
}
let selections = self.selections.disjoint_anchors_arc();
if let Some(transaction_id_now) = self.buffer.read(cx).last_transaction_id(cx) {
let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
if has_new_transaction {
self.selection_history
.insert_transaction(transaction_id_now, selections);
self.update_visible_edit_prediction(window, cx);
if self.active_edit_prediction.is_none() {
self.refresh_edit_prediction(true, true, window, cx);
}
cx.notify();
}
_ => {
let snapshot = self.buffer.read(cx).snapshot(cx);
let cursor_offset = self
.selections
.newest::<MultiBufferOffset>(&self.display_snapshot(cx))
.head();
let insertion = edits.iter().find_map(|(range, text)| {
let range = range.to_offset(&snapshot);
if range.is_empty() && range.start == cursor_offset {
Some(text)
} else {
None
}
});
if let Some(text) = insertion {
let text_to_insert = match granularity {
EditPredictionGranularity::Word => {
let mut partial = text
.chars()
.by_ref()
.take_while(|c| c.is_alphabetic())
.collect::<String>();
if partial.is_empty() {
partial = text
.chars()
.by_ref()
.take_while(|c| c.is_whitespace() || !c.is_alphabetic())
.collect::<String>();
}
partial
}
EditPredictionGranularity::Line => {
if let Some(line) = text.split_inclusive('\n').next() {
line.to_string()
} else {
text.to_string()
}
}
EditPredictionGranularity::Full => unreachable!(),
};
cx.emit(EditorEvent::InputHandled {
utf16_range_to_replace: None,
text: text_to_insert.clone().into(),
});
self.insert_with_autoindent_mode(&text_to_insert, None, window, cx);
self.refresh_edit_prediction(true, true, window, cx);
cx.notify();
} else {
self.accept_partial_edit_prediction(
EditPredictionGranularity::Full,
window,
cx,
);
}
}
}
self.update_visible_edit_prediction(window, cx);
if self.active_edit_prediction.is_none() {
self.refresh_edit_prediction(true, true, window, cx);
}
cx.notify();
}
}
self.edit_prediction_requires_modifier_in_indent_conflict = false;
}
pub fn accept_partial_edit_prediction(
pub fn accept_next_word_edit_prediction(
&mut self,
_: &AcceptPartialEditPrediction,
_: &AcceptNextWordEditPrediction,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
return;
};
if self.selections.count() != 1 {
return;
}
self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
}
match &active_edit_prediction.completion {
EditPrediction::MoveWithin { target, .. } => {
let target = *target;
self.change_selections(
SelectionEffects::scroll(Autoscroll::newest()),
window,
cx,
|selections| {
selections.select_anchor_ranges([target..target]);
},
);
}
EditPrediction::MoveOutside { snapshot, target } => {
if let Some(workspace) = self.workspace() {
Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
.detach_and_log_err(cx);
}
}
EditPrediction::Edit { edits, .. } => {
self.report_edit_prediction_event(
active_edit_prediction.completion_id.clone(),
true,
cx,
);
pub fn accept_next_line_edit_prediction(
&mut self,
_: &AcceptNextLineEditPrediction,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
}
// Find an insertion that starts at the cursor position.
let snapshot = self.buffer.read(cx).snapshot(cx);
let cursor_offset = self
.selections
.newest::<MultiBufferOffset>(&self.display_snapshot(cx))
.head();
let insertion = edits.iter().find_map(|(range, text)| {
let range = range.to_offset(&snapshot);
if range.is_empty() && range.start == cursor_offset {
Some(text)
} else {
None
}
});
if let Some(text) = insertion {
let mut partial_completion = text
.chars()
.by_ref()
.take_while(|c| c.is_alphabetic())
.collect::<String>();
if partial_completion.is_empty() {
partial_completion = text
.chars()
.by_ref()
.take_while(|c| c.is_whitespace() || !c.is_alphabetic())
.collect::<String>();
}
cx.emit(EditorEvent::InputHandled {
utf16_range_to_replace: None,
text: partial_completion.clone().into(),
});
self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
self.refresh_edit_prediction(true, true, window, cx);
cx.notify();
} else {
self.accept_edit_prediction(&Default::default(), window, cx);
}
}
}
pub fn accept_edit_prediction(
&mut self,
_: &AcceptEditPrediction,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
}
fn discard_edit_prediction(
@@ -8042,21 +8064,23 @@ impl Editor {
cx: &mut Context<Self>,
) {
let mut modifiers_held = false;
if let Some(accept_keystroke) = self
.accept_edit_prediction_keybind(false, window, cx)
.keystroke()
{
modifiers_held = modifiers_held
|| (accept_keystroke.modifiers() == modifiers
&& accept_keystroke.modifiers().modified());
};
if let Some(accept_partial_keystroke) = self
.accept_edit_prediction_keybind(true, window, cx)
.keystroke()
{
modifiers_held = modifiers_held
|| (accept_partial_keystroke.modifiers() == modifiers
&& accept_partial_keystroke.modifiers().modified());
// Check bindings for all granularities.
// If the user holds the key for Word, Line, or Full, we want to show the preview.
let granularities = [
EditPredictionGranularity::Full,
EditPredictionGranularity::Line,
EditPredictionGranularity::Word,
];
for granularity in granularities {
if let Some(keystroke) = self
.accept_edit_prediction_keybind(granularity, window, cx)
.keystroke()
{
modifiers_held = modifiers_held
|| (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
}
}
if modifiers_held {
@@ -9476,7 +9500,8 @@ impl Editor {
window: &mut Window,
cx: &mut App,
) -> Option<AnyElement> {
let accept_binding = self.accept_edit_prediction_keybind(false, window, cx);
let accept_binding =
self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
let accept_keystroke = accept_binding.keystroke()?;
let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
+8 -3
View File
@@ -62,6 +62,7 @@ use multi_buffer::{
MultiBufferRow, RowInfo,
};
use edit_prediction_types::EditPredictionGranularity;
use project::{
Entry, ProjectPath,
debugger::breakpoint_store::{Breakpoint, BreakpointSessionState},
@@ -603,7 +604,8 @@ impl EditorElement {
register_action(editor, window, Editor::display_cursor_names);
register_action(editor, window, Editor::unique_lines_case_insensitive);
register_action(editor, window, Editor::unique_lines_case_sensitive);
register_action(editor, window, Editor::accept_partial_edit_prediction);
register_action(editor, window, Editor::accept_next_word_edit_prediction);
register_action(editor, window, Editor::accept_next_line_edit_prediction);
register_action(editor, window, Editor::accept_edit_prediction);
register_action(editor, window, Editor::restore_file);
register_action(editor, window, Editor::git_restore);
@@ -4900,8 +4902,11 @@ impl EditorElement {
let edit_prediction = if edit_prediction_popover_visible {
self.editor.update(cx, move |editor, cx| {
let accept_binding =
editor.accept_edit_prediction_keybind(false, window, cx);
let accept_binding = editor.accept_edit_prediction_keybind(
EditPredictionGranularity::Full,
window,
cx,
);
let mut element = editor.render_edit_prediction_cursor_popover(
min_width,
max_width,
+6
View File
@@ -159,3 +159,9 @@ pub(crate) mod m_2025_12_01 {
pub(crate) use settings::SETTINGS_PATTERNS;
}
pub(crate) mod m_2025_12_08 {
mod keymap;
pub(crate) use keymap::KEYMAP_PATTERNS;
}
@@ -0,0 +1,33 @@
use collections::HashMap;
use std::{ops::Range, sync::LazyLock};
use tree_sitter::{Query, QueryMatch};
use crate::MigrationPatterns;
use crate::patterns::KEYMAP_ACTION_STRING_PATTERN;
pub const KEYMAP_PATTERNS: MigrationPatterns =
&[(KEYMAP_ACTION_STRING_PATTERN, replace_string_action)];
fn replace_string_action(
contents: &str,
mat: &QueryMatch,
query: &Query,
) -> Option<(Range<usize>, String)> {
let action_name_ix = query.capture_index_for_name("action_name")?;
let action_name_node = mat.nodes_for_capture_index(action_name_ix).next()?;
let action_name_range = action_name_node.byte_range();
let action_name = contents.get(action_name_range.clone())?;
if let Some(new_action_name) = STRING_REPLACE.get(&action_name) {
return Some((action_name_range, new_action_name.to_string()));
}
None
}
static STRING_REPLACE: LazyLock<HashMap<&str, &str>> = LazyLock::new(|| {
HashMap::from_iter([(
"editor::AcceptPartialEditPrediction",
"editor::AcceptNextWordEditPrediction",
)])
});
+8
View File
@@ -139,6 +139,10 @@ pub fn migrate_keymap(text: &str) -> Result<Option<String>> {
migrations::m_2025_04_15::KEYMAP_PATTERNS,
&KEYMAP_QUERY_2025_04_15,
),
MigrationType::TreeSitter(
migrations::m_2025_12_08::KEYMAP_PATTERNS,
&KEYMAP_QUERY_2025_12_08,
),
];
run_migrations(text, migrations)
}
@@ -358,6 +362,10 @@ define_query!(
SETTINGS_QUERY_2025_11_20,
migrations::m_2025_11_20::SETTINGS_PATTERNS
);
define_query!(
KEYMAP_QUERY_2025_12_08,
migrations::m_2025_12_08::KEYMAP_PATTERNS
);
// custom query
static EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY: LazyLock<Query> = LazyLock::new(|| {
+2 -1
View File
@@ -58,7 +58,8 @@ In these cases, `alt-tab` is used instead to accept the prediction. When the lan
On Linux, `alt-tab` is often used by the window manager for switching windows, so `alt-l` is provided as the default binding for accepting predictions. `tab` and `alt-tab` also work, but aren't displayed by default.
{#action editor::AcceptPartialEditPrediction} ({#kb editor::AcceptPartialEditPrediction}) can be used to accept the current edit prediction up to the next word boundary.
{#action editor::AcceptNextWordEditPrediction} ({#kb editor::AcceptNextWordEditPrediction}) can be used to accept the current edit prediction up to the next word boundary.
{#action editor::AcceptNextLineEditPrediction} ({#kb editor::AcceptNextLineEditPrediction}) can be used to accept the current edit prediction up to the new line boundary.
## Configuring Edit Prediction Keybindings {#edit-predictions-keybinding}