add CharCache::truncate_shrink_min(), speed up LineNumbers view
4 files changed, 53 insertions(+), 25 deletions(-)

M cursive/src/main.rs
M cursive/src/views/editor.rs
M cursive/src/views/gutter.rs
M src/cache.rs
M cursive/src/main.rs +2 -1
@@ 152,7 152,8 @@ fn parse_esc_seq_events<V: View>(view: V
     })
 }
 
-fn layout_default<S>(editor: EditorView, gutter_style: S) -> Canvas<LinearLayout> where S: 'static + Into<Style> {
+fn layout_default<S>(editor: EditorView, gutter_style: S) -> Canvas<LinearLayout>
+where S: 'static + Into<Style> {
     let line_numbers = LineNumbers::new(
         {
             let props = editor.props();

          
M cursive/src/views/editor.rs +1 -4
@@ 459,10 459,7 @@ impl View for EditorView {
 
                 /* Shrink the space string to save memory.
                  * Tolerate jitter so we don't have to grow again on every layout. */
-                const SPACE_CACHE_TOLERANCE: usize = 15; // XXX what is sensible?
-                if self.space.len_chars().saturating_sub(self.view_width) > SPACE_CACHE_TOLERANCE {
-                    self.space.truncate_shrink(self.view_width);
-                }
+                self.space.truncate_shrink_min(self.view_width, 15); // XXX what is sensible?
 
                 // highlight lines
                 for line_idx in self.lines_to_highlight.iter() {

          
M cursive/src/views/gutter.rs +42 -19
@@ 1,16 1,21 @@ 
-use std::ops::Range;
-use frappe::Signal;
-use cursive::Printer;
-use cursive::view::View;
-use cursive::theme::Style;
-use cursive::vec::Vec2;
-use super::unicode_width::UnicodeWidthStr;
+use {
+    std::ops::Range,
+    frappe::Signal,
+    cursive::{
+        Printer,
+        view::View,
+        theme::Style,
+        vec::Vec2
+    },
+    ted::cache::CharCache
+};
 
 pub struct LineNumbers {
     range: Signal<Range<usize>>,
     style: Style,
     pad_right: usize,
 
+    space: CharCache,
     size: Vec2
 }
 

          
@@ 20,26 25,41 @@ impl LineNumbers {
         Self {
             style: style.into(),
             pad_right: 1,
+            space: CharCache::new(' '),
             size: range.sample_with(|range| Vec2::new(
-                Self::width_of(&*range),
+                range_width(&*range),
                 range.end - range.start
             )),
-            range: range
+            range
         }
     }
 }
 
 impl LineNumbers {
     fn width(&self) -> usize {
-        self.range.sample_with(|range| Self::width_of(&*range)) + self.pad_right
+        self.num_width() + self.pad_right
     }
 
-    fn width_of(range: &Range<usize>) -> usize {
-        if range.start < range.end {
-            (range.end - 1).to_string().width()
-        } else {
-            0
-        }
+    fn num_width(&self) -> usize {
+        self.range.sample_with(|range| range_width(&*range))
+    }
+}
+
+fn num_width(mut num: usize) -> usize {
+    // numbers are in ASCII range so one column per char
+    let mut width = 1;
+    while num >= 10 {
+        num /= 10;
+        width += 1;
+    }
+    width
+}
+
+fn range_width(range: &Range<usize>) -> usize {
+    if range.start < range.end {
+        num_width(range.end - 1)
+    } else {
+        0
     }
 }
 

          
@@ 50,6 70,11 @@ impl View for LineNumbers {
 
     fn layout(&mut self, size: Vec2) {
         self.size = size;
+
+        // allocate enough for line 1
+        self.space.grow(self.size.x - self.pad_right - 1);
+        self.space.grow(self.pad_right);
+        self.space.truncate_shrink_min(self.size.x, 10);
     }
 
     fn draw(&self, printer: &Printer) {

          
@@ 61,9 86,7 @@ impl View for LineNumbers {
                 printer.print((0, y), &format!("{:1$}", line, num_width)); // XXX cache number strings
 
                 // print right padding
-                for x in num_width..self.size.x {
-                    printer.print((x, y), " ");
-                }
+                printer.print((num_width, y), self.space.get(self.pad_right).expect("not enough space allocated"));
             }
         });
     }

          
M src/cache.rs +8 -1
@@ 39,7 39,7 @@ pub struct CharCache {
 impl CharCache {
     pub fn new(char: char) -> Self {
         Self {
-            char:  char,
+            char,
             cache: char.to_string()
         }
     }

          
@@ 74,4 74,11 @@ impl CharCache {
         self.cache.truncate(len_chars * self.char.len_utf8());
         self.cache.shrink_to_fit();
     }
+
+    /// Calls `truncate_shrink` only if at least `min` chars would be truncated.
+    pub fn truncate_shrink_min(&mut self, len_chars: usize, min: usize) {
+        if self.len_chars().saturating_sub(len_chars) >= min {
+            self.truncate_shrink(len_chars);
+        }
+    }
 }