diff --git a/src/documents/document/place.rs b/src/documents/document/place.rs index 89e691f..202f6c1 100644 --- a/src/documents/document/place.rs +++ b/src/documents/document/place.rs @@ -160,6 +160,10 @@ impl MarkdownDocument { self.top = None; // The view moved: center again. self.centered = None; + self.editor.update(cx, |editor, cx| { + editor.centered = None; + cx.notify(); + }); cx.notify(); } diff --git a/src/documents/document/tests/look.rs b/src/documents/document/tests/look.rs index bb70af8..9658dcc 100644 --- a/src/documents/document/tests/look.rs +++ b/src/documents/document/tests/look.rs @@ -280,3 +280,63 @@ fn switching_to_visual_centers_the_caret_block(cx: &mut TestAppContext) { let off = off_middle(&document, 50, cx); assert!(off < px(4.0), "{off:?} from the middle"); } + +/// How far line 100 sits from mid-view. +fn source_off( + document: &gpui::Entity, + cx: &mut gpui::VisualTestContext, +) -> gpui::Pixels { + let drawn = cx.debug_bounds("editor-line-100").expect("line drawn"); + let editor = document.read_with(cx, |document, _| document.editor().clone()); + let middle = editor.read_with(cx, |editor, _| { + editor.metrics.top + editor.metrics.height / 2.0 + }); + (drawn.center().y - middle).abs() +} + +/// Typewriter source, caret on line 100. +fn source_at_100( + cx: &mut TestAppContext, +) -> ( + gpui::Entity, + &mut gpui::VisualTestContext, +) { + let (document, cx) = open(&long(), MarkdownMode::Source, cx); + document.update(cx, |document, cx| { + document.set_look(typewriter(), cx); + document.set_caret(100, 0, cx); + }); + frames(cx); + (document, cx) +} + +#[gpui::test] +fn a_mode_round_trip_centers_the_source_line(cx: &mut TestAppContext) { + let (document, cx) = source_at_100(cx); + for mode in [MarkdownMode::Visual, MarkdownMode::Source] { + document.update_in(cx, |document, window, cx| { + document.set_mode(mode, window, cx) + }); + frames(cx); + } + let off = source_off(&document, cx); + assert!(off < px(4.0), "{off:?} from the middle"); +} + +#[gpui::test] +fn revealing_the_caret_line_keeps_it_centered(cx: &mut TestAppContext) { + let (document, cx) = source_at_100(cx); + document.update_in(cx, |document, window, cx| document.reveal(100, window, cx)); + frames(cx); + let off = source_off(&document, cx); + assert!(off < px(4.0), "{off:?} from the middle"); +} + +#[gpui::test] +fn a_small_resize_centers_the_source_line(cx: &mut TestAppContext) { + let (document, cx) = source_at_100(cx); + cx.simulate_resize(gpui::size(px(1000.0), px(1070.0))); + frames(cx); + let off = source_off(&document, cx); + assert!(off < px(4.0), "{off:?} from the middle"); +} diff --git a/src/editor/state.rs b/src/editor/state.rs index f245b34..1fe77f2 100644 --- a/src/editor/state.rs +++ b/src/editor/state.rs @@ -122,8 +122,8 @@ pub struct CodeEditor { /// The text before an input method's composition began, while it runs. pub(crate) composing: Option, pub(crate) metrics: Metrics, - /// The caret row typewriter scrolling last centered. - pub(crate) centered: Option, + /// The caret row, view and line heights last centered. + pub(crate) centered: Option<(usize, Pixels, Pixels)>, /// Typewriter room rows at each end. pub(crate) pad: usize, pub(crate) dragging: bool, diff --git a/src/editor/view.rs b/src/editor/view.rs index 86c2fda..bc3fb6d 100644 --- a/src/editor/view.rs +++ b/src/editor/view.rs @@ -272,11 +272,11 @@ impl Render for CodeEditor { self.metrics.advance = advance; self.metrics.line = line; // Typewriter: the caret's row centers as it moves. - let row = self.primary_row(); - if self.options.typewriter && self.centered != Some(row) { + let centered = (self.primary_row(), self.metrics.height, line); + if self.options.typewriter && self.centered != Some(centered) { self.scroll - .scroll_to_item_strict(row, ScrollStrategy::Center); - self.centered = Some(row); + .scroll_to_item_strict(centered.0, ScrollStrategy::Center); + self.centered = Some(centered); } let sticky = if self.options.sticky { self.sticky(cx)