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);
+ }
+ }
}