feat(config): only render diffs

This commit is contained in:
graelo 2020-06-01 11:57:23 +02:00
parent 92ced5d3bd
commit fb07f64c97

View file

@ -45,8 +45,10 @@ impl<'a> View<'a> {
} }
} }
/// Move focus onto the previous hint. /// Move focus onto the previous hint, returning both the index of the
pub fn prev(&mut self) { /// previously focused match, and the index of the newly focused one.
fn prev_focus_index(&mut self) -> (usize, usize) {
let old_index = self.focus_index;
if self.focus_wrap_around { if self.focus_wrap_around {
if self.focus_index == 0 { if self.focus_index == 0 {
self.focus_index = self.matches.len() - 1; self.focus_index = self.matches.len() - 1;
@ -58,10 +60,14 @@ impl<'a> View<'a> {
self.focus_index -= 1; self.focus_index -= 1;
} }
} }
let new_index = self.focus_index;
(old_index, new_index)
} }
/// Move focus onto the next hint. /// Move focus onto the next hint, returning both the index of the
pub fn next(&mut self) { /// previously focused match, and the index of the newly focused one.
fn next_focus_index(&mut self) -> (usize, usize) {
let old_index = self.focus_index;
if self.focus_wrap_around { if self.focus_wrap_around {
if self.focus_index == self.matches.len() - 1 { if self.focus_index == self.matches.len() - 1 {
self.focus_index = 0; self.focus_index = 0;
@ -73,6 +79,26 @@ impl<'a> View<'a> {
self.focus_index += 1; self.focus_index += 1;
} }
} }
let new_index = self.focus_index;
(old_index, new_index)
}
/// Returns screen offset of a given `Match`.
///
/// If multibyte characters occur before the hint (in the "prefix"), then
/// their compouding takes less space on screen when printed: for
/// instance ´ + e = é. Consequently the hint offset has to be adjusted
/// to the left.
fn match_offsets(&self, mat: &model::Match<'a>) -> (usize, usize) {
let offset_x = {
let line = &self.model.lines[mat.y as usize];
let prefix = &line[0..mat.x as usize];
let adjust = prefix.len() - prefix.chars().count();
(mat.x as usize) - (adjust)
};
let offset_y = mat.y as usize;
(offset_x, offset_y)
} }
/// Render entire model lines on provided writer. /// Render entire model lines on provided writer.
@ -107,6 +133,7 @@ impl<'a> View<'a> {
/// If a Mach is "focused", it is then rendered with the `focused_*g` colors. /// If a Mach is "focused", it is then rendered with the `focused_*g` colors.
/// ///
/// # Note /// # Note
///
/// This writes directly on the writer, avoiding extra allocation. /// This writes directly on the writer, avoiding extra allocation.
fn render_matched_text( fn render_matched_text(
stdout: &mut dyn io::Write, stdout: &mut dyn io::Write,
@ -237,7 +264,41 @@ impl<'a> View<'a> {
} }
} }
/// Render the view on the provided writer. /// Convenience function that renders both the matched text and its hint,
/// if focused.
fn render_match(&self, stdout: &mut dyn io::Write, mat: &model::Match<'a>, focused: bool) {
let text = mat.text;
let (offset_x, offset_y) = self.match_offsets(mat);
View::render_matched_text(
stdout,
text,
focused,
(offset_x, offset_y),
&self.rendering_colors,
);
if !focused {
// If not focused, render the hint (e.g. "eo") as an overlay on
// top of the rendered matched text, aligned at its leading or the
// trailing edge.
let extra_offset = match self.hint_alignment {
HintAlignment::Leading => 0,
HintAlignment::Trailing => text.len() - mat.hint.len(),
};
View::render_matched_hint(
stdout,
&mat.hint,
(offset_x + extra_offset, offset_y),
&self.rendering_colors,
&self.hint_style,
);
}
}
/// Full nender the view on the provided writer.
/// ///
/// This renders in 3 phases: /// This renders in 3 phases:
/// - all lines are rendered verbatim /// - all lines are rendered verbatim
@ -256,54 +317,34 @@ impl<'a> View<'a> {
View::render_base_text(stdout, self.model.lines, &self.rendering_colors); View::render_base_text(stdout, self.model.lines, &self.rendering_colors);
for (index, mat) in self.matches.iter().enumerate() { for (index, mat) in self.matches.iter().enumerate() {
// 2. Render the match's text.
// If multibyte characters occur before the hint (in the "prefix"), then
// their compouding takes less space on screen when printed: for
// instance ´ + e = é. Consequently the hint offset has to be adjusted
// to the left.
let offset_x = {
let line = &self.model.lines[mat.y as usize];
let prefix = &line[0..mat.x as usize];
let adjust = prefix.len() - prefix.chars().count();
(mat.x as usize) - (adjust)
};
let offset_y = mat.y as usize;
let text = &mat.text;
let focused = index == self.focus_index; let focused = index == self.focus_index;
self.render_match(stdout, mat, focused);
View::render_matched_text(
stdout,
text,
focused,
(offset_x, offset_y),
&self.rendering_colors,
);
if !focused {
// 3. If not focused, render the hint (e.g. "eo") as an overlay on
// top of the rendered matched text, aligned at its leading or the
// trailing edge.
let extra_offset = match self.hint_alignment {
HintAlignment::Leading => 0,
HintAlignment::Trailing => text.len() - mat.hint.len(),
};
View::render_matched_hint(
stdout,
&mat.hint,
(offset_x + extra_offset, offset_y),
&self.rendering_colors,
&self.hint_style,
);
}
} }
stdout.flush().unwrap(); stdout.flush().unwrap();
} }
/// Render the previous match with its hint, and render the newly focused
/// match without its hint. This is more efficient than a full render.
fn diff_render(
&self,
stdout: &mut dyn io::Write,
old_focus_index: usize,
new_focus_index: usize,
) {
// Render the previously focused match as non-focused
let mat = self.matches.get(old_focus_index).unwrap();
let focused = false;
self.render_match(stdout, mat, focused);
// Render the previously focused match as non-focused
let mat = self.matches.get(new_focus_index).unwrap();
let focused = true;
self.render_match(stdout, mat, focused);
stdout.flush().unwrap();
}
/// Listen to keys entered on stdin, moving focus accordingly, or /// Listen to keys entered on stdin, moving focus accordingly, or
/// selecting one match. /// selecting one match.
/// ///
@ -327,7 +368,7 @@ impl<'a> View<'a> {
if next_key.is_none() { if next_key.is_none() {
// Nothing in the buffer. Wait for a bit... // Nothing in the buffer. Wait for a bit...
std::thread::sleep(std::time::Duration::from_millis(100)); std::thread::sleep(std::time::Duration::from_millis(50));
continue; continue;
} }
@ -343,26 +384,40 @@ impl<'a> View<'a> {
} }
// Move focus to next/prev match. // Move focus to next/prev match.
event::Key::Up => self.prev(), event::Key::Up => {
event::Key::Down => self.next(), let (old_index, focused_index) = self.prev_focus_index();
event::Key::Left => self.prev(), self.diff_render(writer, old_index, focused_index);
event::Key::Right => self.next(), }
event::Key::Down => {
let (old_index, focused_index) = self.next_focus_index();
self.diff_render(writer, old_index, focused_index);
}
event::Key::Left => {
let (old_index, focused_index) = self.prev_focus_index();
self.diff_render(writer, old_index, focused_index);
}
event::Key::Right => {
let (old_index, focused_index) = self.next_focus_index();
self.diff_render(writer, old_index, focused_index);
}
event::Key::Char(_ch @ 'n') => { event::Key::Char(_ch @ 'n') => {
if self.model.reverse { let (old_index, focused_index) = if self.model.reverse {
self.prev() self.prev_focus_index()
} else { } else {
self.next() self.next_focus_index()
} };
self.diff_render(writer, old_index, focused_index);
} }
event::Key::Char(_ch @ 'N') => { event::Key::Char(_ch @ 'N') => {
if self.model.reverse { let (old_index, focused_index) = if self.model.reverse {
self.next() self.next_focus_index()
} else { } else {
self.prev() self.prev_focus_index()
} };
self.diff_render(writer, old_index, focused_index);
} }
// Yank/copy
event::Key::Char(_ch @ 'y') => { event::Key::Char(_ch @ 'y') => {
let text = self.matches.get(self.focus_index).unwrap().text; let text = self.matches.get(self.focus_index).unwrap().text;
return Event::Match((text.to_string(), false)); return Event::Match((text.to_string(), false));
@ -410,8 +465,7 @@ impl<'a> View<'a> {
_ => (), _ => (),
} }
// Render on stdout if we did not exit earlier. // End of event processing loop.
self.full_render(writer);
} }
Event::Exit Event::Exit