From 7764b5f2674c77eb63261748f586036670f39cc5 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 4 Jan 2024 11:38:40 +0000 Subject: [PATCH] Rework FilePosition to cache the last known width. This way, it doesn't have to eagerly throw away its 'fine' position: it can keep it until it's next called on, and then reuse it if the new render width happens to match. I think it's also simpler than the previous approach! --- src/file.rs | 145 +++++++++++++++++++++++----------------------------- 1 file changed, 64 insertions(+), 81 deletions(-) diff --git a/src/file.rs b/src/file.rs index 7c13dc2..2841378 100644 --- a/src/file.rs +++ b/src/file.rs @@ -12,17 +12,28 @@ use super::tui::{ }; #[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum FilePosition { - Coarse(isize), // bottom of this item is at bottom of screen - Fine(isize, usize), // line #n of this item is just off bottom of screen +struct FilePosition { + item: isize, // The selected item in the file + line: usize, // The line number within that item + + // 'line' only makes sense for a particular render width, because + // when items are rewrapped to a different width, their line + // numbers change. So we also store the width for which 'line' + // makes sense. Whenever we render at a different width, we must + // reset 'line' to the bottom of the current item. + // + // Setting this to None is a special case. It means that if + // line==0 then we focus the top of the item, otherwise we focus + // the bottom + width: Option, } impl FilePosition { - fn item(&self) -> isize { - match self { - FilePosition::Coarse(item) => *item, - FilePosition::Fine(item, _) => *item, - } + fn item_top(item: isize) -> Self { + FilePosition { item, line: 0, width: None } + } + fn item_bottom(item: isize) -> Self { + FilePosition { item, line: 1, width: None } } } @@ -268,7 +279,8 @@ impl File { contents.update_items(client); // FIXME: once we have an LDB, that's where initial pos comes from - let initial_pos = FilePosition::Coarse(contents.last_index() as isize); + let initial_pos = FilePosition::item_bottom( + contents.last_index() as isize); let ff = File { contents, @@ -318,13 +330,13 @@ impl File { let (w, h) = self.last_size.expect( "ensure_enough_rendered before resize"); - let (item, line) = self.refine_pos(w); + self.update_pos_for_size(w, h); - let mut item = item; + let mut item = self.pos.item; let mut lines_rendered = 0; // We count most items we render for their full height, but // not the one we can only see the top segment of - let mut line_count_override = Some(line); + let mut line_count_override = Some(self.pos.line); loop { let item_height = self.ensure_item_rendered(item, w).len(); @@ -369,27 +381,17 @@ impl File { } } - fn refine_pos(&mut self, w: usize) -> (isize, usize) { - let (newpos, item, line) = match self.pos { - pos @ FilePosition::Fine(item, line) => (pos, item, line), - FilePosition::Coarse(item) => { - let rendered = self.ensure_item_rendered(item, w); - let line = rendered.len(); - (FilePosition::Fine(item, line), item, line) - } + fn update_pos_for_size(&mut self, w: usize, h: usize) { + if self.pos.width == None && self.pos.line == 0 { + // Special case: look at the _top_ of the item + self.pos.width = Some(w); + self.move_down(h.saturating_sub(1)); + } else if self.pos.width != Some(w) { + let rendered = self.ensure_item_rendered(self.pos.item, w); + self.pos.line = rendered.len(); + self.pos.width = Some(w); }; - - self.pos = newpos; - (item, line) - } - - fn coarsen_pos(&mut self) { - let newpos = match self.pos { - pos @ FilePosition::Coarse(_) => pos, - FilePosition::Fine(item, _line) => FilePosition::Coarse(item), - }; - - self.pos = newpos; + dbg!(self.pos); } fn fix_overshoot_at_top(&mut self) { @@ -407,59 +409,48 @@ impl File { fn at_top(&mut self) -> bool { self.ensure_enough_rendered().is_some() } fn move_up(&mut self, distance: usize) { - let (w, _h) = self.last_size.expect("move_up before resize"); - let (item, line) = match self.pos { - FilePosition::Fine(item, line) => (item, line), - _ => panic!("coarse position reached move_up()"), - }; + let (w, h) = self.last_size.expect("move_up before resize"); + self.update_pos_for_size(w, h); - let mut item = item; - let mut line = line; let mut remaining = distance; while remaining > 0 { - let this = min(line, remaining); + let this = min(self.pos.line, remaining); remaining -= this; - line -= this; - if line == 0 { - if item <= self.contents.first_index() { + self.pos.line -= this; + if self.pos.line == 0 { + if self.pos.item <= self.contents.first_index() { break; } - item -= 1; - line = self.ensure_item_rendered(item, w).len(); + self.pos.item -= 1; + self.pos.line = self.ensure_item_rendered(self.pos.item, w) + .len(); } } - self.pos = FilePosition::Fine(item, line); self.fix_overshoot_at_top(); self.after_setting_pos(); } fn move_down_inner(&mut self, distance: usize) { - let (w, _h) = self.last_size.expect("move_down before resize"); - let (item, line) = match self.pos { - FilePosition::Fine(item, line) => (item, line), - _ => panic!("coarse position reached move_down()"), - }; + let (w, h) = self.last_size.expect("move_down before resize"); + self.update_pos_for_size(w, h); - let mut item = item; - let mut line = line; let mut remaining = distance; while remaining > 0 { - let mut len = self.ensure_item_rendered(item, w).len(); - if line == len { - if item >= self.contents.last_index() { + let mut len = self.ensure_item_rendered(self.pos.item, w).len(); + if self.pos.line == len { + if self.pos.item >= self.contents.last_index() { break; } - item += 1; - line = 0; - len = self.ensure_item_rendered(item, w).len(); + self.pos.item += 1; + self.pos.line = 0; + len = self.ensure_item_rendered(self.pos.item, w).len(); } - let this = min(remaining, len - line); + let this = min(remaining, len - self.pos.line); remaining -= this; - line += this; + self.pos.line += this; } - self.pos = FilePosition::Fine(item, line); self.ensure_enough_rendered(); } @@ -469,16 +460,13 @@ impl File { } fn goto_top(&mut self) { - self.pos = FilePosition::Fine(self.contents.first_index(), 0); + self.pos = FilePosition::item_top(self.contents.first_index()); self.fix_overshoot_at_top(); self.after_setting_pos(); } fn goto_bottom(&mut self) { - let (w, _h) = self.last_size.expect("goto_bottom before resize"); - let item = self.contents.last_index(); - let line = self.ensure_item_rendered(item, w).len(); - self.pos = FilePosition::Fine(item, line); + self.pos = FilePosition::item_bottom(self.contents.last_index()); self.ensure_enough_rendered(); self.after_setting_pos(); } @@ -494,14 +482,9 @@ impl File { if !any_new { // Can't extend this any further into the past self.contents.extender = None; - if self.pos.item() < self.contents.origin { - self.coarsen_pos(); - if self.pos.item() < - self.contents.first_index() - { - self.pos = FilePosition::Coarse( - self.contents.first_index()); - } + if self.pos.item < self.contents.first_index() { + self.pos = FilePosition::item_top( + self.contents.first_index()); } } @@ -551,7 +534,7 @@ impl File { purpose: SelectionPurpose, client: &mut Client) -> LogicalAction { - let item = self.pos.item(); + let item = self.pos.item; let selection = self.last_selectable_above(htype, item).or_else( || self.first_selectable_below(htype, item + 1)); @@ -742,8 +725,9 @@ impl fn resize(&mut self, w: usize, h: usize) { if self.last_size != Some((w, h)) { self.last_size = Some((w, h)); - self.coarsen_pos(); self.rendered.clear(); + self.update_pos_for_size(w, h); + self.fix_overshoot_at_top(); } self.ensure_enough_rendered(); self.after_setting_pos(); @@ -753,10 +737,9 @@ impl -> (Vec, CursorPosition) { assert_eq!(self.last_size, Some((w, h)), "last resize() inconsistent with draw()"); - let (start_item, start_line) = match self.pos { - FilePosition::Fine(item, line) => (item, line), - _ => panic!("coarse position reached draw()"), - }; + assert_eq!(self.pos.width, Some(w), + "file position inconsistent with draw()"); + let (start_item, start_line) = (self.pos.item, self.pos.line); let mut item = start_item; let mut lines = Vec::new(); -- 2.30.2