summaryrefslogtreecommitdiffstats
path: root/src/process
diff options
context:
space:
mode:
authorTim Oram <dev@mitmaro.ca>2021-05-17 09:20:08 -0230
committerTim Oram <dev@mitmaro.ca>2021-06-12 11:06:54 -0230
commit92f0349f8518855276d6c314c59bd0b3e08bad98 (patch)
tree3eae4d5ef261c0fa2e122b1ecbec968e2b2d085e /src/process
parent4242e8d01adc0ab8b5fe2fbd437026eb760abbdb (diff)
Move the render stage to a separate thread
This decouples the render of the view from the process loop and renders the view in a separate thread.
Diffstat (limited to 'src/process')
-rw-r--r--src/process/error.rs19
-rw-r--r--src/process/mod.rs59
-rw-r--r--src/process/modules.rs9
-rw-r--r--src/process/process_module.rs11
-rw-r--r--src/process/testutil.rs31
-rw-r--r--src/process/window_size_error.rs6
6 files changed, 93 insertions, 42 deletions
diff --git a/src/process/error.rs b/src/process/error.rs
index f5920bb..5b66a3a 100644
--- a/src/process/error.rs
+++ b/src/process/error.rs
@@ -11,6 +11,7 @@ use crate::{
render_context::RenderContext,
view_data::ViewData,
view_line::ViewLine,
+ ViewSender,
},
};
@@ -29,14 +30,19 @@ impl ProcessModule for Error {
ProcessResult::new()
}
- fn build_view_data(&mut self, _: &RenderContext, _: &TodoFile) -> &mut ViewData {
- &mut self.view_data
+ fn build_view_data(&mut self, _: &RenderContext, _: &TodoFile) -> &ViewData {
+ &self.view_data
}
- fn handle_events(&mut self, event_handler: &EventHandler, _: &mut TodoFile) -> ProcessResult {
+ fn handle_events(
+ &mut self,
+ event_handler: &EventHandler,
+ view_sender: &ViewSender,
+ _: &mut TodoFile,
+ ) -> ProcessResult {
let event = event_handler.read_event(&INPUT_OPTIONS, |event, _| event);
let mut result = ProcessResult::from(event);
- if handle_view_data_scroll(event, &mut self.view_data).is_none() {
+ if handle_view_data_scroll(event, view_sender).is_none() {
if let Event::Key(_) = event {
result = result.state(self.return_state);
}
@@ -49,7 +55,10 @@ impl Error {
pub fn new() -> Self {
Self {
return_state: State::List,
- view_data: ViewData::new(|updater| updater.set_show_title(true)),
+ view_data: ViewData::new(|updater| {
+ updater.set_show_title(true);
+ updater.set_retain_scroll_position(false);
+ }),
}
}
diff --git a/src/process/mod.rs b/src/process/mod.rs
index 3b915d2..cb63418 100644
--- a/src/process/mod.rs
+++ b/src/process/mod.rs
@@ -11,7 +11,7 @@ mod tests;
#[cfg(test)]
pub mod testutil;
-use std::process::Command;
+use std::{process::Command, thread};
use anyhow::{anyhow, Result};
@@ -19,35 +19,44 @@ use crate::{
input::{Event, EventHandler, MetaEvent},
process::{exit_status::ExitStatus, modules::Modules, process_result::ProcessResult, state::State},
todo_file::TodoFile,
- view::{render_context::RenderContext, View},
+ view::{render_context::RenderContext, spawn_view_thread, View, ViewSender},
};
pub struct Process {
- exit_status: Option<ExitStatus>,
event_handler: EventHandler,
+ exit_status: Option<ExitStatus>,
rebase_todo: TodoFile,
render_context: RenderContext,
state: State,
- view: View,
+ threads: Vec<thread::JoinHandle<()>>,
+ view_sender: ViewSender,
}
impl Process {
pub(crate) fn new(rebase_todo: TodoFile, event_handler: EventHandler, view: View) -> Self {
let view_size = view.get_view_size();
+ let mut threads = vec![];
+
+ let (view_sender, view_thread) = spawn_view_thread(view);
+ threads.push(view_thread);
+
Self {
- render_context: RenderContext::new(view_size.width() as u16, view_size.height() as u16),
- exit_status: None,
event_handler,
+ exit_status: None,
rebase_todo,
+ render_context: RenderContext::new(view_size.width() as u16, view_size.height() as u16),
state: State::List,
- view,
+ threads,
+ view_sender,
}
}
pub(crate) fn run(&mut self, mut modules: Modules<'_>) -> Result<Option<ExitStatus>> {
- if self.view.start().is_err() {
- return Ok(Some(ExitStatus::StateError));
+ if self.view_sender.start().is_err() {
+ self.exit_status = Some(ExitStatus::StateError);
+ return Ok(self.exit_status);
}
+
self.handle_process_result(
&mut modules,
&ProcessResult::new().event(Event::Resize(
@@ -58,13 +67,21 @@ impl Process {
self.activate(&mut modules, State::List);
while self.exit_status.is_none() {
let view_data = modules.build_view_data(self.state, &self.render_context, &self.rebase_todo);
- if self.view.render(view_data).is_err() {
+ if self.view_sender.render(view_data).is_err() {
self.exit_status = Some(ExitStatus::StateError);
continue;
}
-
loop {
- let result = modules.handle_input(self.state, &self.event_handler, &mut self.rebase_todo);
+ if self.view_sender.is_poisoned() {
+ self.exit_status = Some(ExitStatus::StateError);
+ break;
+ }
+ let result = modules.handle_input(
+ self.state,
+ &self.event_handler,
+ &self.view_sender,
+ &mut self.rebase_todo,
+ );
if let Some(event) = result.event {
if event != Event::None {
@@ -74,7 +91,7 @@ impl Process {
}
}
}
- if self.view.end().is_err() {
+ if self.view_sender.stop().is_err() {
return Ok(Some(ExitStatus::StateError));
}
if let Some(status) = self.exit_status {
@@ -82,6 +99,17 @@ impl Process {
self.rebase_todo.write_file()?;
}
}
+
+ if self.view_sender.end().is_err() {
+ return Ok(Some(ExitStatus::StateError));
+ }
+
+ while let Some(handle) = self.threads.pop() {
+ if handle.join().is_err() {
+ return Ok(Some(ExitStatus::StateError));
+ }
+ }
+
Ok(self.exit_status)
}
@@ -113,6 +141,7 @@ impl Process {
self.exit_status = Some(ExitStatus::Kill);
},
Some(Event::Resize(width, height)) => {
+ self.view_sender.resize(width, height);
self.render_context.update(width, height);
if self.state != State::WindowSizeError && self.render_context.is_window_too_small() {
self.state = State::WindowSizeError;
@@ -140,7 +169,7 @@ impl Process {
}
fn run_command(&mut self, external_command: &(String, Vec<String>)) -> Result<MetaEvent> {
- self.view.end()?;
+ self.view_sender.stop()?;
let mut cmd = Command::new(external_command.0.clone());
cmd.args(external_command.1.clone());
@@ -157,7 +186,7 @@ impl Process {
})
.map_err(|err| anyhow!(err));
- self.view.start()?;
+ self.view_sender.stop()?;
result
}
diff --git a/src/process/modules.rs b/src/process/modules.rs
index 057d54b..790cace 100644
--- a/src/process/modules.rs
+++ b/src/process/modules.rs
@@ -15,7 +15,7 @@ use crate::{
},
show_commit::ShowCommit,
todo_file::TodoFile,
- view::{render_context::RenderContext, view_data::ViewData},
+ view::{render_context::RenderContext, view_data::ViewData, ViewSender},
};
pub struct Modules<'m> {
@@ -69,7 +69,7 @@ impl<'m> Modules<'m> {
state: State,
render_context: &RenderContext,
rebase_todo: &TodoFile,
- ) -> &mut ViewData {
+ ) -> &ViewData {
self.get_mut_module(state).build_view_data(render_context, rebase_todo)
}
@@ -77,9 +77,11 @@ impl<'m> Modules<'m> {
&mut self,
state: State,
event_handler: &EventHandler,
+ view_sender: &ViewSender,
rebase_todo: &mut TodoFile,
) -> ProcessResult {
- self.get_mut_module(state).handle_events(event_handler, rebase_todo)
+ self.get_mut_module(state)
+ .handle_events(event_handler, view_sender, rebase_todo)
}
pub fn set_error_message(&mut self, error: &anyhow::Error) {
@@ -125,6 +127,7 @@ mod tests {
modules.handle_input(
state,
&test_context.event_handler_context.event_handler,
+ &test_context.event_handler_context.view_sender,
&mut test_context.rebase_todo_file,
);
modules.build_view_data(state, &test_context.render_context, &test_context.rebase_todo_file);
diff --git a/src/process/process_module.rs b/src/process/process_module.rs
index 736185c..37fdcd4 100644
--- a/src/process/process_module.rs
+++ b/src/process/process_module.rs
@@ -2,7 +2,7 @@ use crate::{
input::EventHandler,
process::{process_result::ProcessResult, state::State},
todo_file::TodoFile,
- view::{render_context::RenderContext, view_data::ViewData},
+ view::{render_context::RenderContext, view_data::ViewData, ViewSender},
};
pub trait ProcessModule {
@@ -12,7 +12,12 @@ pub trait ProcessModule {
fn deactivate(&mut self) {}
- fn build_view_data(&mut self, _render_context: &RenderContext, _rebase_todo: &TodoFile) -> &mut ViewData;
+ fn build_view_data(&mut self, _render_context: &RenderContext, _rebase_todo: &TodoFile) -> &ViewData;
- fn handle_events(&mut self, _event_handler: &EventHandler, _rebase_todo: &mut TodoFile) -> ProcessResult;
+ fn handle_events(
+ &mut self,
+ _event_handler: &EventHandler,
+ _view_sender: &ViewSender,
+ _rebase_todo: &mut TodoFile,
+ ) -> ProcessResult;
}
diff --git a/src/process/testutil.rs b/src/process/testutil.rs
index 4b99014..25e83b0 100644
--- a/src/process/testutil.rs
+++ b/src/process/testutil.rs
@@ -26,7 +26,7 @@ pub struct TestContext<'t> {
}
impl<'t> TestContext<'t> {
- fn get_build_data<'tc>(&self, module: &'tc mut dyn ProcessModule) -> &'tc mut ViewData {
+ fn get_build_data<'tc>(&self, module: &'tc mut dyn ProcessModule) -> &'tc ViewData {
module.build_view_data(&self.render_context, &self.rebase_todo_file)
}
@@ -39,25 +39,26 @@ impl<'t> TestContext<'t> {
module.deactivate();
}
- pub fn update_view_data_size(&self, module: &'_ mut dyn ProcessModule) {
- let view_data = self.get_build_data(module);
- view_data.set_view_size(self.render_context.width(), self.render_context.height());
- }
-
- pub fn build_view_data<'tc>(&self, module: &'tc mut dyn ProcessModule) -> &'tc mut ViewData {
- let view_data = self.get_build_data(module);
- view_data.set_view_size(self.render_context.width(), self.render_context.height());
- view_data
+ pub fn build_view_data<'tc>(&self, module: &'tc mut dyn ProcessModule) -> &'tc ViewData {
+ self.get_build_data(module)
}
pub fn handle_event(&mut self, module: &'_ mut dyn ProcessModule) -> ProcessResult {
- module.handle_events(&self.event_handler_context.event_handler, &mut self.rebase_todo_file)
+ module.handle_events(
+ &self.event_handler_context.event_handler,
+ &self.event_handler_context.view_sender,
+ &mut self.rebase_todo_file,
+ )
}
pub fn handle_n_events(&mut self, module: &'_ mut dyn ProcessModule, n: usize) -> Vec<ProcessResult> {
let mut results = vec![];
for _ in 0..n {
- results.push(module.handle_events(&self.event_handler_context.event_handler, &mut self.rebase_todo_file));
+ results.push(module.handle_events(
+ &self.event_handler_context.event_handler,
+ &self.event_handler_context.view_sender,
+ &mut self.rebase_todo_file,
+ ));
}
results
}
@@ -65,7 +66,11 @@ impl<'t> TestContext<'t> {
pub fn handle_all_events(&mut self, module: &'_ mut dyn ProcessModule) -> Vec<ProcessResult> {
let mut results = vec![];
for _ in 0..self.event_handler_context.number_events {
- results.push(module.handle_events(&self.event_handler_context.event_handler, &mut self.rebase_todo_file));
+ results.push(module.handle_events(
+ &self.event_handler_context.event_handler,
+ &self.event_handler_context.view_sender,
+ &mut self.rebase_todo_file,
+ ));
}
results
}
diff --git a/src/process/window_size_error.rs b/src/process/window_size_error.rs
index 19249e1..c519ce2 100644
--- a/src/process/window_size_error.rs
+++ b/src/process/window_size_error.rs
@@ -4,7 +4,7 @@ use crate::{
input::{Event, EventHandler, InputOptions},
process::{process_module::ProcessModule, process_result::ProcessResult, state::State},
todo_file::TodoFile,
- view::{render_context::RenderContext, view_data::ViewData, view_line::ViewLine},
+ view::{render_context::RenderContext, view_data::ViewData, view_line::ViewLine, ViewSender},
};
const HEIGHT_ERROR_MESSAGE: &str = "Window too small, increase height to continue";
@@ -26,7 +26,7 @@ impl ProcessModule for WindowSizeError {
ProcessResult::new()
}
- fn build_view_data(&mut self, context: &RenderContext, _: &TodoFile) -> &mut ViewData {
+ fn build_view_data(&mut self, context: &RenderContext, _: &TodoFile) -> &ViewData {
let view_width = context.width();
let message = if !context.is_minimum_view_width() {
if view_width >= SHORT_ERROR_MESSAGE.len() {
@@ -52,7 +52,7 @@ impl ProcessModule for WindowSizeError {
&mut self.view_data
}
- fn handle_events(&mut self, event_handler: &EventHandler, _: &mut TodoFile) -> ProcessResult {
+ fn handle_events(&mut self, event_handler: &EventHandler, _: &ViewSender, _: &mut TodoFile) -> ProcessResult {
let event = event_handler.read_event(&INPUT_OPTIONS, |event, _| event);
let mut result = ProcessResult::from(event);