vim: Fix clipping when navigating over inlay hints (#22813)

This fixes the issue described in this comment:
https://github.com/zed-industries/zed/pull/22439#issuecomment-2563896422

Essentially, we'd clip in the wrong direction when there were multi-line
inlay hints.

It also fixes inline completions for non-Zeta-providers showing up in
normal mode.

Release Notes:

- N/A
This commit is contained in:
Thorsten Ball 2025-01-08 10:41:43 +01:00 committed by GitHub
parent dffdf99228
commit f9ee28db5e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 76 additions and 13 deletions

View file

@ -63,6 +63,10 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
false false
} }
fn show_completions_in_normal_mode() -> bool {
false
}
fn is_enabled( fn is_enabled(
&self, &self,
buffer: &Model<Buffer>, buffer: &Model<Buffer>,

View file

@ -42,7 +42,7 @@ use fold_map::{FoldMap, FoldSnapshot};
use gpui::{ use gpui::{
AnyElement, Font, HighlightStyle, LineLayout, Model, ModelContext, Pixels, UnderlineStyle, AnyElement, Font, HighlightStyle, LineLayout, Model, ModelContext, Pixels, UnderlineStyle,
}; };
pub(crate) use inlay_map::Inlay; pub use inlay_map::Inlay;
use inlay_map::{InlayMap, InlaySnapshot}; use inlay_map::{InlayMap, InlaySnapshot};
pub use inlay_map::{InlayOffset, InlayPoint}; pub use inlay_map::{InlayOffset, InlayPoint};
use invisibles::{is_invisible, replacement}; use invisibles::{is_invisible, replacement};

View file

@ -33,7 +33,7 @@ enum Transform {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Inlay { pub struct Inlay {
pub(crate) id: InlayId, pub(crate) id: InlayId,
pub position: Anchor, pub position: Anchor,
pub text: text::Rope, pub text: text::Rope,

View file

@ -258,7 +258,7 @@ pub fn render_parsed_markdown(
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum InlayId { pub enum InlayId {
InlineCompletion(usize), InlineCompletion(usize),
Hint(usize), Hint(usize),
} }
@ -3592,7 +3592,7 @@ impl Editor {
} }
} }
fn splice_inlays( pub fn splice_inlays(
&self, &self,
to_remove: Vec<InlayId>, to_remove: Vec<InlayId>,
to_insert: Vec<Inlay>, to_insert: Vec<Inlay>,
@ -4883,7 +4883,7 @@ impl Editor {
} }
} }
fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> { pub fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
Some(self.inline_completion_provider.as_ref()?.provider.clone()) Some(self.inline_completion_provider.as_ref()?.provider.clone())
} }

View file

@ -325,6 +325,10 @@ impl InlineCompletionProvider for FakeInlineCompletionProvider {
false false
} }
fn show_completions_in_normal_mode() -> bool {
false
}
fn is_enabled( fn is_enabled(
&self, &self,
_buffer: &gpui::Model<language::Buffer>, _buffer: &gpui::Model<language::Buffer>,

View file

@ -21,6 +21,7 @@ pub trait InlineCompletionProvider: 'static + Sized {
fn name() -> &'static str; fn name() -> &'static str;
fn display_name() -> &'static str; fn display_name() -> &'static str;
fn show_completions_in_menu() -> bool; fn show_completions_in_menu() -> bool;
fn show_completions_in_normal_mode() -> bool;
fn is_enabled( fn is_enabled(
&self, &self,
buffer: &Model<Buffer>, buffer: &Model<Buffer>,
@ -61,6 +62,7 @@ pub trait InlineCompletionProviderHandle {
cx: &AppContext, cx: &AppContext,
) -> bool; ) -> bool;
fn show_completions_in_menu(&self) -> bool; fn show_completions_in_menu(&self) -> bool;
fn show_completions_in_normal_mode(&self) -> bool;
fn refresh( fn refresh(
&self, &self,
buffer: Model<Buffer>, buffer: Model<Buffer>,
@ -101,6 +103,10 @@ where
T::show_completions_in_menu() T::show_completions_in_menu()
} }
fn show_completions_in_normal_mode(&self) -> bool {
T::show_completions_in_normal_mode()
}
fn is_enabled( fn is_enabled(
&self, &self,
buffer: &Model<Buffer>, buffer: &Model<Buffer>,

View file

@ -106,6 +106,10 @@ impl InlineCompletionProvider for SupermavenCompletionProvider {
false false
} }
fn show_completions_in_normal_mode() -> bool {
false
}
fn is_enabled(&self, buffer: &Model<Buffer>, cursor_position: Anchor, cx: &AppContext) -> bool { fn is_enabled(&self, buffer: &Model<Buffer>, cursor_position: Anchor, cx: &AppContext) -> bool {
if !self.supermaven.read(cx).is_enabled() { if !self.supermaven.read(cx).is_enabled() {
return false; return false;

View file

@ -1206,6 +1206,7 @@ fn up_down_buffer_rows(
times: isize, times: isize,
text_layout_details: &TextLayoutDetails, text_layout_details: &TextLayoutDetails,
) -> (DisplayPoint, SelectionGoal) { ) -> (DisplayPoint, SelectionGoal) {
let bias = if times < 0 { Bias::Left } else { Bias::Right };
let start = map.display_point_to_fold_point(point, Bias::Left); let start = map.display_point_to_fold_point(point, Bias::Left);
let begin_folded_line = map.fold_point_to_display_point( let begin_folded_line = map.fold_point_to_display_point(
map.fold_snapshot map.fold_snapshot
@ -1229,14 +1230,14 @@ fn up_down_buffer_rows(
let mut begin_folded_line = map.fold_point_to_display_point( let mut begin_folded_line = map.fold_point_to_display_point(
map.fold_snapshot map.fold_snapshot
.clip_point(FoldPoint::new(new_row, 0), Bias::Left), .clip_point(FoldPoint::new(new_row, 0), bias),
); );
let mut i = 0; let mut i = 0;
while i < goal_wrap && begin_folded_line.row() < map.max_point().row() { while i < goal_wrap && begin_folded_line.row() < map.max_point().row() {
let next_folded_line = DisplayPoint::new(begin_folded_line.row().next_row(), 0); let next_folded_line = DisplayPoint::new(begin_folded_line.row().next_row(), 0);
if map if map
.display_point_to_fold_point(next_folded_line, Bias::Right) .display_point_to_fold_point(next_folded_line, bias)
.row() .row()
== new_row == new_row
{ {
@ -1254,10 +1255,7 @@ fn up_down_buffer_rows(
}; };
( (
map.clip_point( map.clip_point(DisplayPoint::new(begin_folded_line.row(), new_col), bias),
DisplayPoint::new(begin_folded_line.row(), new_col),
Bias::Left,
),
goal, goal,
) )
} }
@ -2484,7 +2482,11 @@ fn section_motion(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::test::NeovimBackedTestContext; use crate::{
state::Mode,
test::{NeovimBackedTestContext, VimTestContext},
};
use editor::display_map::Inlay;
use indoc::indoc; use indoc::indoc;
#[gpui::test] #[gpui::test]
@ -3146,4 +3148,35 @@ mod test {
}ˇ» }ˇ»
"}); "});
} }
#[gpui::test]
async fn test_clipping_with_inlay_hints(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(
indoc! {"
struct Foo {
ˇ
}
"},
Mode::Normal,
);
cx.update_editor(|editor, cx| {
let range = editor.selections.newest_anchor().range();
let inlay_text = " field: int,\n field2: string\n field3: float";
let inlay = Inlay::inline_completion(1, range.start, inlay_text);
editor.splice_inlays(vec![], vec![inlay], cx);
});
cx.simulate_keystrokes("j");
cx.assert_state(
indoc! {"
struct Foo {
ˇ}
"},
Mode::Normal,
);
}
} }

View file

@ -1196,7 +1196,15 @@ impl Vim {
editor.set_input_enabled(vim.editor_input_enabled()); editor.set_input_enabled(vim.editor_input_enabled());
editor.set_autoindent(vim.should_autoindent()); editor.set_autoindent(vim.should_autoindent());
editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine); editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine);
editor.set_inline_completions_enabled(matches!(vim.mode, Mode::Insert | Mode::Replace));
let enable_inline_completions = match vim.mode {
Mode::Insert | Mode::Replace => true,
Mode::Normal => editor
.inline_completion_provider()
.map_or(false, |provider| provider.show_completions_in_normal_mode()),
_ => false,
};
editor.set_inline_completions_enabled(enable_inline_completions);
}); });
cx.notify() cx.notify()
} }

View file

@ -979,6 +979,10 @@ impl inline_completion::InlineCompletionProvider for ZetaInlineCompletionProvide
true true
} }
fn show_completions_in_normal_mode() -> bool {
true
}
fn is_enabled( fn is_enabled(
&self, &self,
buffer: &Model<Buffer>, buffer: &Model<Buffer>,