on master help sign in
fix: align view after jumplist_picker (#3743)
453a75aNarazaki Shuji
helix-term/src/commands.rs+7−8
@@ -1667,12 +1667,7 @@ fn search_impl(
1667 };
1667 };
1668
1668
1669 doc.set_selection(view.id, selection);
1669 doc.set_selection(view.id, selection);
1670− // TODO: is_cursor_in_view does the same calculation as ensure_cursor_in_view
1670+ view.ensure_cursor_in_view_center(doc, scrolloff);
1671− if view.is_cursor_in_view(doc, 0) {
1672− view.ensure_cursor_in_view(doc, scrolloff);
1673− } else {
1674− align_view(doc, view, Align::Center)
1675− }
1676 };
1671 };
1677}
1672}
1678
1673
@@ -2434,8 +2429,10 @@ fn jumplist_picker(cx: &mut Context) {
2434 (),
2429 (),
2435 |cx, meta, action| {
2430 |cx, meta, action| {
2436 cx.editor.switch(meta.id, action);
2431 cx.editor.switch(meta.id, action);
2432+ let config = cx.editor.config();
2437 let (view, doc) = current!(cx.editor);
2433 let (view, doc) = current!(cx.editor);
2438 doc.set_selection(view.id, meta.selection.clone());
2434 doc.set_selection(view.id, meta.selection.clone());
2435+ view.ensure_cursor_in_view_center(doc, config.scrolloff);
2439 },
2436 },
2440 |editor, meta| {
2437 |editor, meta| {
2441 let doc = &editor.documents.get(&meta.id)?;
2438 let doc = &editor.documents.get(&meta.id)?;
@@ -4205,6 +4202,7 @@ fn match_brackets(cx: &mut Context) {
4205
4202
4206fn jump_forward(cx: &mut Context) {
4203fn jump_forward(cx: &mut Context) {
4207 let count = cx.count();
4204 let count = cx.count();
4205+ let config = cx.editor.config();
4208 let view = view_mut!(cx.editor);
4206 let view = view_mut!(cx.editor);
4209 let doc_id = view.doc;
4207 let doc_id = view.doc;
4210
4208
@@ -4218,12 +4216,13 @@ fn jump_forward(cx: &mut Context) {
4218 }
4216 }
4219
4217
4220 doc.set_selection(view.id, selection);
4218 doc.set_selection(view.id, selection);
4221− align_view(doc, view, Align::Center);
4219+ view.ensure_cursor_in_view_center(doc, config.scrolloff);
4222 };
4220 };
4223}
4221}
4224
4222
4225fn jump_backward(cx: &mut Context) {
4223fn jump_backward(cx: &mut Context) {
4226 let count = cx.count();
4224 let count = cx.count();
4225+ let config = cx.editor.config();
4227 let (view, doc) = current!(cx.editor);
4226 let (view, doc) = current!(cx.editor);
4228 let doc_id = doc.id();
4227 let doc_id = doc.id();
4229
4228
@@ -4237,7 +4236,7 @@ fn jump_backward(cx: &mut Context) {
4237 }
4236 }
4238
4237
4239 doc.set_selection(view.id, selection);
4238 doc.set_selection(view.id, selection);
4240− align_view(doc, view, Align::Center);
4239+ view.ensure_cursor_in_view_center(doc, config.scrolloff);
4241 };
4240 };
4242}
4241}
4243
4242
helix-view/src/view.rs+60−29
@@ -1,4 +1,4 @@
1−use crate::{editor::GutterType, graphics::Rect, Document, DocumentId, ViewId};
1+use crate::{align_view, editor::GutterType, graphics::Rect, Align, Document, DocumentId, ViewId};
2use helix_core::{
2use helix_core::{
3 pos_at_visual_coords, visual_coords_at_pos, Position, RopeSlice, Selection, Transaction,
3 pos_at_visual_coords, visual_coords_at_pos, Position, RopeSlice, Selection, Transaction,
4};
4};
@@ -169,6 +169,15 @@ impl View {
169 &self,
169 &self,
170 doc: &Document,
170 doc: &Document,
171 scrolloff: usize,
171 scrolloff: usize,
172+ ) -> Option<(usize, usize)> {
173+ self.offset_coords_to_in_view_center(doc, scrolloff, false)
174+ }
175+
176+ pub fn offset_coords_to_in_view_center(
177+ &self,
178+ doc: &Document,
179+ scrolloff: usize,
180+ centering: bool,
172 ) -> Option<(usize, usize)> {
181 ) -> Option<(usize, usize)> {
173 let cursor = doc
182 let cursor = doc
174 .selection(self.id)
183 .selection(self.id)
@@ -180,47 +189,69 @@ impl View {
180
189
181 let inner_area = self.inner_area(doc);
190 let inner_area = self.inner_area(doc);
182 let last_line = (self.offset.row + inner_area.height as usize).saturating_sub(1);
191 let last_line = (self.offset.row + inner_area.height as usize).saturating_sub(1);
183−
184− // - 1 so we have at least one gap in the middle.
185− // a height of 6 with padding of 3 on each side will keep shifting the view back and forth
186− // as we type
187− let scrolloff = scrolloff.min(inner_area.height.saturating_sub(1) as usize / 2);
188−
189 let last_col = self.offset.col + inner_area.width.saturating_sub(1) as usize;
192 let last_col = self.offset.col + inner_area.width.saturating_sub(1) as usize;
190
193
194+ let new_offset = |scrolloff: usize| {
195+ // - 1 so we have at least one gap in the middle.
196+ // a height of 6 with padding of 3 on each side will keep shifting the view back and forth
197+ // as we type
198+ let scrolloff = scrolloff.min(inner_area.height.saturating_sub(1) as usize / 2);
199+
191− let row = if line > last_line.saturating_sub(scrolloff) {
200+ let row = if line > last_line.saturating_sub(scrolloff) {
192− // scroll down
201+ // scroll down
193− self.offset.row + line - (last_line.saturating_sub(scrolloff))
202+ self.offset.row + line - (last_line.saturating_sub(scrolloff))
194− } else if line < self.offset.row + scrolloff {
203+ } else if line < self.offset.row + scrolloff {
195− // scroll up
204+ // scroll up
205+ line.saturating_sub(scrolloff)
206+ } else {
207+ self.offset.row
208+ };
209+
210+ let col = if col > last_col.saturating_sub(scrolloff) {
211+ // scroll right
196− line.saturating_sub(scrolloff)
212+ self.offset.col + col - (last_col.saturating_sub(scrolloff))
197− } else {
213+ } else if col < self.offset.col + scrolloff {
214+ // scroll left
215+ col.saturating_sub(scrolloff)
216+ } else {
198− self.offset.row
217+ self.offset.col
218+ };
219+ (row, col)
199 };
220 };
200−
221+ let current_offset = (self.offset.row, self.offset.col);
201− let col = if col > last_col.saturating_sub(scrolloff) {
222+ if centering {
202− // scroll right
223+ // return None if cursor is out of view
203− self.offset.col + col - (last_col.saturating_sub(scrolloff))
224+ let offset = new_offset(0);
204− } else if col < self.offset.col + scrolloff {
225+ (offset == current_offset).then(|| {
205− // scroll left
226+ if scrolloff == 0 {
206− col.saturating_sub(scrolloff)
227+ offset
228+ } else {
229+ new_offset(scrolloff)
230+ }
231+ })
207 } else {
232 } else {
208− self.offset.col
233+ // return None if cursor is in (view - scrolloff)
209− };
234+ let offset = new_offset(scrolloff);
210− if row == self.offset.row && col == self.offset.col {
235+ (offset != current_offset).then(|| offset) // TODO: use 'then_some' when 1.62 <= MSRV
211− None
212− } else {
213− Some((row, col))
214 }
236 }
215 }
237 }
216
238
217 pub fn ensure_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) {
239 pub fn ensure_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) {
218− if let Some((row, col)) = self.offset_coords_to_in_view(doc, scrolloff) {
240+ if let Some((row, col)) = self.offset_coords_to_in_view_center(doc, scrolloff, false) {
219 self.offset.row = row;
241 self.offset.row = row;
220 self.offset.col = col;
242 self.offset.col = col;
221 }
243 }
222 }
244 }
223
245
246+ pub fn ensure_cursor_in_view_center(&mut self, doc: &Document, scrolloff: usize) {
247+ if let Some((row, col)) = self.offset_coords_to_in_view_center(doc, scrolloff, true) {
248+ self.offset.row = row;
249+ self.offset.col = col;
250+ } else {
251+ align_view(doc, self, Align::Center);
252+ }
253+ }
254+
224 pub fn is_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) -> bool {
255 pub fn is_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) -> bool {
225 self.offset_coords_to_in_view(doc, scrolloff).is_none()
256 self.offset_coords_to_in_view(doc, scrolloff).is_none()
226 }
257 }