summaryrefslogtreecommitdiffstats
path: root/src/show_commit/show_commit.rs
diff options
context:
space:
mode:
authorTim Oram <dev@mitmaro.ca>2019-09-22 13:03:11 -0230
committerTim Oram <dev@mitmaro.ca>2019-09-22 21:25:03 -0230
commit9627bf773000e440af772e42f00dabd97d6969d1 (patch)
tree9bda4927ca7ab7c6bc4abe9255ae0dc62cbad7ff /src/show_commit/show_commit.rs
parent153681545482b69713dda9eb9378c4427106b882 (diff)
Improve performance of show commit
This change adds two caches that greatly speed up show commit. The first is to cache the Unicode lengths so that they do not need to be calculated for every render. The second is to ensure that the call to libgit2 for retrieving the commit information is only made once and not on every render loop. This should reduce the overall render time by about ten fold.
Diffstat (limited to 'src/show_commit/show_commit.rs')
-rw-r--r--src/show_commit/show_commit.rs264
1 files changed, 51 insertions, 213 deletions
diff --git a/src/show_commit/show_commit.rs b/src/show_commit/show_commit.rs
index 561e967..810053e 100644
--- a/src/show_commit/show_commit.rs
+++ b/src/show_commit/show_commit.rs
@@ -1,5 +1,4 @@
use crate::commit::Commit;
-use crate::constants::MINIMUM_FULL_WINDOW_WIDTH;
use crate::display::DisplayColor;
use crate::git_interactive::GitInteractive;
use crate::input::{Input, InputHandler};
@@ -12,32 +11,46 @@ use crate::process::{
State,
};
use crate::scroll::ScrollPosition;
-use crate::show_commit::util::get_stat_item_segments;
-use crate::view::{LineSegment, View, ViewLine};
-use std::cmp;
-use unicode_segmentation::UnicodeSegmentation;
+use crate::show_commit::data::Data;
+use crate::view::View;
pub struct ShowCommit {
+ commit: Option<Result<Commit, String>>,
+ data: Data,
scroll_position: ScrollPosition,
}
impl ProcessModule for ShowCommit {
- fn activate(&mut self, _state: State, _git_interactive: &GitInteractive) {
+ fn activate(&mut self, _state: State, git_interactive: &GitInteractive) {
self.scroll_position.reset();
+ self.commit = Some(git_interactive.load_commit_stats());
}
- fn process(&mut self, git_interactive: &mut GitInteractive, _view: &View) -> ProcessResult {
+ fn deactivate(&mut self) {
+ self.data.reset();
+ }
+
+ fn process(&mut self, _git_interactive: &mut GitInteractive, view: &View) -> ProcessResult {
+ let (view_width, view_height) = view.get_view_size();
let mut result = ProcessResultBuilder::new();
- if let Err(e) = git_interactive.load_commit_stats() {
- result = result.error(e.as_str(), State::List(false));
+
+ if let Some(commit) = &self.commit {
+ match commit {
+ Ok(c) => self.data.update(&c, view_width, view_height),
+ Err(e) => {
+ result = result.error(e.as_str(), State::List(false));
+ self.data.reset()
+ },
+ }
}
+
result.build()
}
fn handle_input(
&mut self,
input_handler: &InputHandler,
- git_interactive: &mut GitInteractive,
+ _git_interactive: &mut GitInteractive,
view: &View,
) -> HandleInputResult
{
@@ -47,40 +60,24 @@ impl ProcessModule for ShowCommit {
let mut result = HandleInputResultBuilder::new(input);
match input {
Input::MoveCursorLeft => {
- self.scroll_position.scroll_left(
- view_width,
- self.get_max_line_length(
- git_interactive.get_commit_stats(),
- view_height >= MINIMUM_FULL_WINDOW_WIDTH,
- ),
- )
+ self.scroll_position
+ .scroll_left(view_width, self.get_max_line_length(view_height))
},
Input::MoveCursorRight => {
- self.scroll_position.scroll_right(
- view_width,
- self.get_max_line_length(
- git_interactive.get_commit_stats(),
- view_height >= MINIMUM_FULL_WINDOW_WIDTH,
- ),
- )
+ self.scroll_position
+ .scroll_right(view_width, self.get_max_line_length(view_height))
},
Input::MoveCursorDown => {
- self.scroll_position.scroll_down(
- view_height,
- self.get_commit_stats_length(git_interactive.get_commit_stats()),
- )
+ self.scroll_position
+ .scroll_down(view_height, self.get_commit_stats_length())
},
Input::MoveCursorUp => {
- self.scroll_position.scroll_up(
- view_height,
- self.get_commit_stats_length(git_interactive.get_commit_stats()),
- )
+ self.scroll_position
+ .scroll_up(view_height, self.get_commit_stats_length())
},
Input::Resize => {
- self.scroll_position.scroll_up(
- view_height as usize,
- self.get_commit_stats_length(git_interactive.get_commit_stats()),
- );
+ self.scroll_position
+ .scroll_up(view_height as usize, self.get_commit_stats_length());
},
_ => {
result = result.state(State::List(false));
@@ -89,105 +86,22 @@ impl ProcessModule for ShowCommit {
result.build()
}
- fn render(&self, view: &View, git_interactive: &GitInteractive) {
- let commit_data = git_interactive.get_commit_stats();
- let (window_width, window_height) = view.get_view_size();
+ fn render(&self, view: &View, _git_interactive: &GitInteractive) {
+ let (_, window_height) = view.get_view_size();
let view_height = window_height - 2;
- let is_full_width = window_width >= MINIMUM_FULL_WINDOW_WIDTH;
-
view.draw_title(false);
- let commit = match commit_data {
+ match &self.commit {
None => {
view.draw_error("Not commit data to show");
return;
},
- Some(c) => c,
- };
-
- let full_hash = commit.get_hash();
- let author = commit.get_author();
- let committer = commit.get_committer();
- let date = commit.get_date();
- let body = commit.get_body();
- let file_stats = commit.get_file_stats();
-
- let mut lines: Vec<ViewLine> = vec![];
-
- lines.push(ViewLine::new(vec![LineSegment::new_with_color(
- if is_full_width {
- format!("Commit: {}", full_hash)
- }
- else {
- let max_index = cmp::min(full_hash.len(), 8);
- format!("{:8} ", full_hash[0..max_index].to_string())
- }
- .as_str(),
- DisplayColor::IndicatorColor,
- )]));
-
- lines.push(ViewLine::new(vec![LineSegment::new(
- if is_full_width {
- format!("Date: {}", date.format("%c %z"))
- }
- else {
- format!("{}", date.format("%c %z"))
- }
- .as_str(),
- )]));
-
- if let Some(a) = author.to_string() {
- lines.push(ViewLine::new(vec![LineSegment::new(
- if is_full_width {
- format!("Author: {}", a)
- }
- else {
- format!("A: {}", a)
- }
- .as_str(),
- )]));
- }
-
- if let Some(c) = committer.to_string() {
- lines.push(ViewLine::new(vec![LineSegment::new(
- if is_full_width {
- format!("Committer: {}", c)
- }
- else {
- format!("C: {}", c)
- }
- .as_str(),
- )]))
+ Some(c) => c.as_ref().unwrap(), // safe unwrap
};
- match body {
- Some(b) => {
- for line in b.lines() {
- lines.push(ViewLine::new(vec![LineSegment::new(line)]));
- }
- },
- None => {},
- };
-
- lines.push(ViewLine::new(vec![LineSegment::new("")]));
-
- match file_stats {
- Some(stats) => {
- for stat in stats {
- lines.push(ViewLine::new(get_stat_item_segments(
- *stat.get_status(),
- stat.get_to_name().as_str(),
- stat.get_from_name().as_str(),
- is_full_width,
- )))
- }
- },
- None => {},
- }
-
view.draw_view_lines(
- lines,
+ self.data.get_lines(),
self.scroll_position.get_top_position(),
self.scroll_position.get_left_position(),
view_height,
@@ -201,13 +115,15 @@ impl ProcessModule for ShowCommit {
impl ShowCommit {
pub fn new() -> Self {
Self {
+ commit: None,
+ data: Data::new(),
scroll_position: ScrollPosition::new(3, 6, 3),
}
}
- fn get_commit_stats_length(&self, commit: &Option<Commit>) -> usize {
- match commit {
- Some(c) => {
+ fn get_commit_stats_length(&self) -> usize {
+ if let Some(commit) = &self.commit {
+ if let Ok(c) = commit {
let mut len = c.get_file_stats_length();
match c.get_body() {
@@ -216,94 +132,16 @@ impl ShowCommit {
},
None => {},
}
- len + 3 // author + date + commit hash
- },
- None => 0,
+ return len + 3; // author + date + commit hash
+ }
}
+ 0
}
- fn get_max_line_length(&self, commit: &Option<Commit>, is_full_width: bool) -> usize {
- match commit {
- Some(c) => {
- let full_hash = c.get_hash();
- let author = c.get_author();
- let committer = c.get_committer();
- let body = c.get_body();
- let file_stats = c.get_file_stats();
-
- let mut max_line_length = if is_full_width {
- full_hash.len() + 8 // 8 = "Commit: "
- }
- else {
- cmp::min(full_hash.len(), 8)
- };
-
- max_line_length = cmp::max(
- if is_full_width {
- 35 // "Date: Sun Jul 8 00:34:60 2001+09:30"
- }
- else {
- 29 // "Sun Jul 8 00:34:60 2001+09:30"
- },
- max_line_length,
- );
-
- if let Some(a) = author.to_string() {
- max_line_length = cmp::max(
- if is_full_width {
- UnicodeSegmentation::graphemes(a.as_str(), true).count() + 8 // 8 = "Author: "
- }
- else {
- UnicodeSegmentation::graphemes(a.as_str(), true).count() + 3 // 3 = "A: "
- },
- max_line_length,
- );
- }
-
- if let Some(c) = committer.to_string() {
- max_line_length = cmp::max(
- if is_full_width {
- UnicodeSegmentation::graphemes(c.as_str(), true).count() + 11 // 11 = "Committer: "
- }
- else {
- UnicodeSegmentation::graphemes(c.as_str(), true).count() + 3 // 3 = "C: "
- },
- max_line_length,
- );
- };
-
- if let Some(b) = body {
- for line in b.lines() {
- let line_length = UnicodeSegmentation::graphemes(line, true).count();
- if line_length > max_line_length {
- max_line_length = line_length;
- }
- }
- }
-
- if let Some(stats) = file_stats {
- let additional_line_length = if is_full_width {
- 13 // stat name + arrow
- }
- else {
- 3 // stat name + arrow
- };
-
- for stat in stats {
- let stat_line_length =
- UnicodeSegmentation::graphemes(stat.get_to_name().as_str(), true).count()
- + UnicodeSegmentation::graphemes(stat.get_from_name().as_str(), true).count()
- + additional_line_length;
-
- if stat_line_length > max_line_length {
- max_line_length = stat_line_length;
- }
- }
- }
-
- max_line_length
- },
- None => 0,
- }
+ fn get_max_line_length(&self, view_height: usize) -> usize {
+ self.data.get_max_line_length(
+ self.scroll_position.get_top_position(),
+ self.scroll_position.get_top_position() + view_height,
+ )
}
}