diff options
author | Tim Oram <dev@mitmaro.ca> | 2021-06-29 09:39:22 -0230 |
---|---|---|
committer | Tim Oram <dev@mitmaro.ca> | 2021-07-05 16:27:53 -0230 |
commit | 566e548b3b7b69f4ed67e5182f3fb6dfa65b0e61 (patch) | |
tree | 605deac9940513e71df18508d6dfc29fdef33ebd | |
parent | c8cd3dabfbe727ba8aaf9750965e35598fac835e (diff) |
Add documentation to the view crate
-rw-r--r-- | src/process/mod.rs | 1 | ||||
-rw-r--r-- | src/view/src/action.rs | 6 | ||||
-rw-r--r-- | src/view/src/lib.rs | 32 | ||||
-rw-r--r-- | src/view/src/line_segment.rs | 4 | ||||
-rw-r--r-- | src/view/src/render_context.rs | 9 | ||||
-rw-r--r-- | src/view/src/sender.rs | 36 | ||||
-rw-r--r-- | src/view/src/testutil.rs | 16 | ||||
-rw-r--r-- | src/view/src/thread.rs | 4 | ||||
-rw-r--r-- | src/view/src/util.rs | 1 | ||||
-rw-r--r-- | src/view/src/view_data.rs | 4 | ||||
-rw-r--r-- | src/view/src/view_data_updater.rs | 12 | ||||
-rw-r--r-- | src/view/src/view_line.rs | 10 |
12 files changed, 124 insertions, 11 deletions
diff --git a/src/process/mod.rs b/src/process/mod.rs index 74eb8dc..fb5947d 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -31,6 +31,7 @@ impl Process { event_handler: EventHandler, view: View<C>, ) -> Self { + #[allow(deprecated)] let view_size = view.get_view_size(); let mut threads = vec![]; diff --git a/src/view/src/action.rs b/src/view/src/action.rs index 9b78ad0..426ad6e 100644 --- a/src/view/src/action.rs +++ b/src/view/src/action.rs @@ -1,9 +1,15 @@ +/// An action to send to the thread handling updates to the view. #[derive(Debug, Copy, Clone)] #[allow(clippy::exhaustive_enums)] pub enum ViewAction { + /// Stop processing actions. Stop, + /// Force a refresh of the view. Refresh, + /// Render the latest `ViewData`. Render, + /// Start processing actions. Start, + /// End the thread and the processing of actions. End, } diff --git a/src/view/src/lib.rs b/src/view/src/lib.rs index e918bc8..362d8b3 100644 --- a/src/view/src/lib.rs +++ b/src/view/src/lib.rs @@ -64,8 +64,6 @@ )] // LINT-REPLACE-END #![allow( - missing_docs, - rustdoc::missing_crate_level_docs, clippy::as_conversions, clippy::default_numeric_fallback, clippy::else_if_without_else, @@ -74,8 +72,6 @@ clippy::float_arithmetic, clippy::integer_arithmetic, clippy::integer_division, - clippy::missing_errors_doc, - clippy::missing_panics_doc, clippy::module_name_repetitions, clippy::new_without_default, clippy::non_ascii_literal, @@ -85,6 +81,16 @@ clippy::wildcard_enum_match_arm )] +//! Git Interactive Rebase Tool - View Module +//! +//! # Description +//! This module is used to handle working with the view. +//! +//! ## Test Utilities +//! To facilitate testing the usages of this crate, a set of testing utilities are provided. Since +//! these utilities are not tested, and often are optimized for developer experience than +//! performance should only be used in test code. + mod action; mod line_segment; mod render_context; @@ -121,6 +127,7 @@ const TITLE: &str = "Git Interactive Rebase Tool"; const TITLE_SHORT: &str = "Git Rebase"; const TITLE_HELP_INDICATOR_LABEL: &str = "Help: "; +/// Represents a view. #[derive(Debug)] pub struct View<C: Tui> { character_vertical_spacing: String, @@ -130,6 +137,7 @@ pub struct View<C: Tui> { } impl<C: Tui> View<C> { + /// Create a new instance of the view. #[inline] pub fn new(display: Display<C>, character_vertical_spacing: &str, help_indicator_key: &str) -> Self { Self { @@ -140,21 +148,35 @@ impl<C: Tui> View<C> { } } + /// End processing of the view. + /// + /// # Errors + /// Results in an error if the terminal cannot be started. #[inline] pub fn start(&mut self) -> Result<()> { self.display.start() } + /// End the view processing. + /// + /// # Errors + /// Results in an error if the terminal cannot be ended. #[inline] pub fn end(&mut self) -> Result<()> { self.display.end() } + /// Get the size of the view. #[inline] + #[deprecated = "This leaks internals of the Display and will eventually be removed"] pub fn get_view_size(&self) -> Size { self.display.get_window_size() } + /// Render a slice. + /// + /// # Errors + /// Results in an error if there are errors with interacting with the terminal. #[inline] pub fn render(&mut self, render_slice: &RenderSlice) -> Result<()> { let current_render_version = render_slice.get_version(); @@ -162,7 +184,7 @@ impl<C: Tui> View<C> { return Ok(()); } self.last_render_version = current_render_version; - let view_size = self.get_view_size(); + let view_size = self.display.get_window_size(); let window_height = view_size.height(); self.display.clear()?; diff --git a/src/view/src/line_segment.rs b/src/view/src/line_segment.rs index 07b39c3..b56924d 100644 --- a/src/view/src/line_segment.rs +++ b/src/view/src/line_segment.rs @@ -40,6 +40,7 @@ impl SegmentPartial { } } +/// Represents a segment in a larger line. #[derive(Clone, Debug)] pub struct LineSegment { color: DisplayColor, @@ -51,18 +52,21 @@ pub struct LineSegment { } impl LineSegment { + /// Create a new instance with just the content. #[must_use] #[inline] pub fn new(text: &str) -> Self { Self::new_with_color_and_style(text, DisplayColor::Normal, false, false, false) } + /// Create a new instance with added color. #[must_use] #[inline] pub fn new_with_color(text: &str, color: DisplayColor) -> Self { Self::new_with_color_and_style(text, color, false, false, false) } + /// Create a new instance with added color and style. #[must_use] #[inline] pub fn new_with_color_and_style( diff --git a/src/view/src/render_context.rs b/src/view/src/render_context.rs index ac93281..039a999 100644 --- a/src/view/src/render_context.rs +++ b/src/view/src/render_context.rs @@ -2,6 +2,7 @@ const MINIMUM_WINDOW_HEIGHT: usize = 5; // title + pad top + line + pad bottom + const MINIMUM_COMPACT_WINDOW_WIDTH: usize = 20; // ">s ccc mmmmmmmmmmmmm".len() const MINIMUM_FULL_WINDOW_WIDTH: usize = 34; // " > squash cccccccc mmmmmmmmmmmmm %".len() +/// Represents data associated with rendering content. #[derive(Debug, Copy, Clone)] pub struct RenderContext { height: usize, @@ -9,6 +10,7 @@ pub struct RenderContext { } impl RenderContext { + /// Create a new instance with a width and height. #[must_use] #[inline] pub const fn new(width: u16, height: u16) -> Self { @@ -18,42 +20,49 @@ impl RenderContext { } } + /// Update the recorded width and height. #[inline] pub fn update(&mut self, width: u16, height: u16) { self.width = width as usize; self.height = height as usize; } + /// Get the width of the terminal window. #[must_use] #[inline] pub const fn width(&self) -> usize { self.width } + /// Get the height of the terminal window. #[must_use] #[inline] pub const fn height(&self) -> usize { self.height } + /// Is the terminal window width at least the minimal supported width. #[must_use] #[inline] pub const fn is_minimum_view_width(&self) -> bool { self.width > MINIMUM_COMPACT_WINDOW_WIDTH } + /// Is the terminal window height at least the minimal supported height. #[must_use] #[inline] pub const fn is_minimum_view_height(&self) -> bool { self.height > MINIMUM_WINDOW_HEIGHT } + /// Is the terminal window large enough to render lines using their full width. #[must_use] #[inline] pub const fn is_full_width(&self) -> bool { self.width >= MINIMUM_FULL_WINDOW_WIDTH } + /// Is the terminal window too small to render content. #[must_use] #[inline] pub const fn is_window_too_small(&self) -> bool { diff --git a/src/view/src/sender.rs b/src/view/src/sender.rs index 2975d44..608670c 100644 --- a/src/view/src/sender.rs +++ b/src/view/src/sender.rs @@ -12,6 +12,11 @@ use anyhow::{anyhow, Error, Result}; use super::{action::ViewAction, render_slice::RenderSlice, view_data::ViewData}; +fn map_send_err(_: mpsc::SendError<ViewAction>) -> Error { + anyhow!("Unable to send data") +} + +/// Represents a message sender and receiver for passing actions between threads. #[derive(Clone, Debug)] pub struct Sender { poisoned: Arc<AtomicBool>, @@ -19,11 +24,8 @@ pub struct Sender { render_slice: Arc<Mutex<RenderSlice>>, } -fn map_send_err(_: mpsc::SendError<ViewAction>) -> Error { - anyhow!("Unable to send data") -} - impl Sender { + /// Create a new instance. #[inline] pub fn new(sender: mpsc::Sender<ViewAction>) -> Self { Self { @@ -33,37 +35,53 @@ impl Sender { } } + /// Clone the poisoned flag. #[inline] pub fn clone_poisoned(&self) -> Arc<AtomicBool> { Arc::clone(&self.poisoned) } + /// Is the sender poisoned, and not longer accepting actions. #[inline] pub fn is_poisoned(&self) -> bool { self.poisoned.load(Ordering::Relaxed) } + /// Clone the render slice. #[inline] pub fn clone_render_slice(&self) -> Arc<Mutex<RenderSlice>> { Arc::clone(&self.render_slice) } + /// Queue a start action. + /// + /// # Errors + /// Results in an error if the sender has been closed. #[inline] pub fn start(&self) -> Result<()> { self.sender.send(ViewAction::Start).map_err(map_send_err) } + /// Queue a stop action. + /// + /// # Errors + /// Results in an error if the sender has been closed. #[inline] pub fn stop(&self) -> Result<()> { self.sender.send(ViewAction::Stop).map_err(map_send_err) } + /// Queue an end action. + /// + /// # Errors + /// Results in an error if the sender has been closed. #[inline] pub fn end(&self) -> Result<()> { self.stop()?; self.sender.send(ViewAction::End).map_err(map_send_err) } + /// Queue a scroll up action. #[inline] pub fn scroll_up(&self) { self.render_slice @@ -73,6 +91,7 @@ impl Sender { .record_scroll_up(); } + /// Queue a scroll down action. #[inline] pub fn scroll_down(&self) { self.render_slice @@ -82,6 +101,7 @@ impl Sender { .record_scroll_down(); } + /// Queue a scroll left action. #[inline] pub fn scroll_left(&self) { self.render_slice @@ -91,6 +111,7 @@ impl Sender { .record_scroll_left(); } + /// Queue a scroll right action. #[inline] pub fn scroll_right(&self) { self.render_slice @@ -100,6 +121,7 @@ impl Sender { .record_scroll_right(); } + /// Queue a scroll up a page action. #[inline] pub fn scroll_page_up(&self) { self.render_slice @@ -109,6 +131,7 @@ impl Sender { .record_page_up(); } + /// Queue a scroll down a page action. #[inline] pub fn scroll_page_down(&self) { self.render_slice @@ -118,6 +141,7 @@ impl Sender { .record_page_down(); } + /// Queue a resize action. #[inline] pub fn resize(&self, width: u16, height: u16) { self.render_slice @@ -127,6 +151,10 @@ impl Sender { .record_resize(width as usize, height as usize); } + /// Sync the `ViewData` and queue a render action. + /// + /// # Errors + /// Results in an error if the sender has been closed. #[inline] pub fn render(&self, view_data: &ViewData) -> Result<()> { self.render_slice diff --git a/src/view/src/testutil.rs b/src/view/src/testutil.rs index 69eb4fa..6a34721 100644 --- a/src/view/src/testutil.rs +++ b/src/view/src/testutil.rs @@ -1,3 +1,4 @@ +//! Utilities for writing tests that interact with input events. use std::{ sync::{mpsc, mpsc::Receiver}, time::Duration, @@ -48,6 +49,7 @@ fn render_style(color: DisplayColor, dimmed: bool, underline: bool, reversed: bo } } +/// Render a `ViewLine` to a `String` using similar logic that is used in the `View`. #[must_use] #[inline] pub fn render_view_line(view_line: &ViewLine) -> String { @@ -125,8 +127,7 @@ fn render_view_data(view_data: &ViewData) -> Vec<String> { lines } -#[inline] -pub fn _assert_rendered_output(output: &[String], expected: &[String]) { +pub(crate) fn _assert_rendered_output(output: &[String], expected: &[String]) { let mut mismatch = false; let mut error_output = vec![ String::from("\nUnexpected output!"), @@ -170,12 +171,15 @@ pub fn _assert_rendered_output(output: &[String], expected: &[String]) { } } +/// Assert the rendered output from a `ViewData`. Generally this function is not used directly, +/// instead use the `assert_rendered_output!` macro. #[inline] pub fn _assert_rendered_output_from_view_data(view_data: &ViewData, expected: &[String]) { let output = render_view_data(view_data); _assert_rendered_output(&output, expected); } +/// Assert the rendered output from a `ViewData`. #[macro_export] macro_rules! assert_rendered_output { ($view_data:expr) => { @@ -259,19 +263,24 @@ fn action_to_string(action: ViewAction) -> String { }) } +/// Context for a `ViewSender` test. #[derive(Debug)] pub struct TestContext { + /// The sender instance. pub sender: ViewSender, + /// The receiver instance. pub receiver: Receiver<ViewAction>, } impl TestContext { + /// Drop the receiver, useful for testing disconnect error handling. #[inline] pub fn drop_receiver(&mut self) { let (_, receiver) = mpsc::channel(); self.receiver = receiver; } + /// Assert that render actions were sent. #[inline] pub fn assert_render_action(&self, actions: &[&str]) { assert_view_sender_actions( @@ -284,7 +293,9 @@ impl TestContext { ); } + /// Assert that certain messages were sent by the `ViewSender`. #[inline] + #[allow(clippy::missing_panics_doc)] pub fn assert_sent_messages(&self, messages: Vec<&str>) { let mut mismatch = false; let mut error_output = vec![ @@ -324,6 +335,7 @@ impl TestContext { } } +/// Provide an `ViewSender` instance for use within a test. #[inline] pub fn with_view_sender<C>(callback: C) where C: FnOnce(TestContext) { diff --git a/src/view/src/thread.rs b/src/view/src/thread.rs index 36a3b30..b8b99bc 100644 --- a/src/view/src/thread.rs +++ b/src/view/src/thread.rs @@ -11,6 +11,10 @@ use super::{action::ViewAction, sender::Sender, View}; const MINIMUM_TICK_RATE: Duration = Duration::from_millis(20); // ~50 Hz update +/// Spawn a thread that will handle rendering contents to the `View`. +/// +/// # Panics +/// This may panic if there is an unexpected error in the processing of the `View`, i.e. a bug. #[inline] pub fn spawn_view_thread<T: Tui + Send + 'static>(view: View<T>) -> (Sender, JoinHandle<()>) { let (sender, receiver) = mpsc::channel(); diff --git a/src/view/src/util.rs b/src/view/src/util.rs index 26bef35..f088f43 100644 --- a/src/view/src/util.rs +++ b/src/view/src/util.rs @@ -2,6 +2,7 @@ use input::{Event, MetaEvent}; use super::ViewSender; +/// Utility function to handle scroll events. #[inline] pub fn handle_view_data_scroll(event: Event, view_sender: &ViewSender) -> Option<Event> { match event { diff --git a/src/view/src/view_data.rs b/src/view/src/view_data.rs index a3293e9..a4c07cf 100644 --- a/src/view/src/view_data.rs +++ b/src/view/src/view_data.rs @@ -2,6 +2,7 @@ use uuid::Uuid; use super::{ViewDataUpdater, ViewLine}; +/// Represents the content to be rendered to the `View`. #[derive(Debug)] pub struct ViewData { lines: Vec<ViewLine>, @@ -18,6 +19,7 @@ pub struct ViewData { } impl ViewData { + /// Create a new instance using a `ViewDataUpdater`. #[inline] pub fn new<C>(callback: C) -> Self where C: FnOnce(&mut ViewDataUpdater<'_>) { @@ -39,12 +41,14 @@ impl ViewData { view_data } + /// Does the instance contain any content. #[must_use] #[inline] pub fn is_empty(&self) -> bool { self.lines.is_empty() && self.lines_leading.is_empty() && self.lines_trailing.is_empty() } + /// Update the view data using a `ViewDataUpdater`. This allows for batch updating of the `ViewData`. #[inline] pub fn update_view_data<C>(&mut self, callback: C) where C: FnOnce(&mut ViewDataUpdater<'_>) { diff --git a/src/view/src/view_data_updater.rs b/src/view/src/view_data_updater.rs index 636729e..7c076e4 100644 --- a/src/view/src/view_data_updater.rs +++ b/src/view/src/view_data_updater.rs @@ -1,5 +1,6 @@ use super::{ViewData, ViewLine}; +/// An updater utility for a `ViewData`. #[derive(Debug)] pub struct ViewDataUpdater<'v> { modified: bool, @@ -14,66 +15,77 @@ impl<'v> ViewDataUpdater<'v> { } } + /// Clear content of the view data. #[inline] pub fn clear(&mut self) { self.modified = true; self.view_data.clear(); } + /// Clear just the body of the view data. #[inline] pub fn clear_body(&mut self) { self.modified = true; self.view_data.clear_body(); } + /// Set the line that must be visible during render. #[inline] pub fn ensure_line_visible(&mut self, row_index: usize) { self.modified = true; self.view_data.ensure_line_visible(row_index); } + /// Set the column to must be visible during render. #[inline] pub fn ensure_column_visible(&mut self, column_index: usize) { self.modified = true; self.view_data.ensure_column_visible(column_index); } + /// Set if to show title. #[inline] pub fn set_show_title(&mut self, show: bool) { self.modified = true; self.view_data.set_show_title(show); } + /// Set if to show help. #[inline] pub fn set_show_help(&mut self, show: bool) { self.modified = true; self.view_data.set_show_help(show); } + /// Push a new leading line to the view data. #[inline] pub fn push_leading_line(&mut self, view_line: ViewLine) { self.modified = true; self.view_data.push_leading_line(view_line); } + /// Push a new body line to the view data. #[inline] pub fn push_line(&mut self, view_line: ViewLine) { self.modified = true; self.view_data.push_line(view_line); } + /// Push a new trailing line to the view data. #[inline] pub fn push_trailing_line(&mut self, view_line: ViewLine) { self.modified = true; self.view_data.push_trailing_line(view_line); } + /// Set the scroll position retain value of the view data. #[inline] pub fn set_retain_scroll_position(&mut self, value: bool) { self.modified = true; self.view_data.set_retain_scroll_position(value); } + /// Reset the scroll position of the view data. #[inline] pub fn reset_scroll_position(&mut self) { self.modified = true; diff --git a/src/view/src/view_line.rs b/src/view/src/view_line.rs index cbcfc8f..8d6b016 100644 --- a/src/view/src/view_line.rs +++ b/src/view/src/view_line.rs @@ -2,6 +2,7 @@ use display::DisplayColor; use super::LineSegment; +/// Represents a line in the view. #[derive(Debug)] pub struct ViewLine { pinned_segments: usize, @@ -11,12 +12,14 @@ pub struct ViewLine { } impl ViewLine { + /// Create a new instance that contains no content. #[must_use] #[inline] pub fn new_empty_line() -> Self { Self::new_with_pinned_segments(vec![], 1) } + /// Create a new instance with all segments pinned. #[must_use] #[inline] pub fn new_pinned(segments: Vec<LineSegment>) -> Self { @@ -24,6 +27,7 @@ impl ViewLine { Self::new_with_pinned_segments(segments, segments_length) } + /// Create a new instance with a number of pinned leading segments. #[must_use] #[inline] pub fn new_with_pinned_segments(segments: Vec<LineSegment>, pinned_segments: usize) -> Self { @@ -35,6 +39,7 @@ impl ViewLine { } } + /// Set that this line is selected. #[must_use] #[inline] pub const fn set_selected(mut self, selected: bool) -> Self { @@ -42,6 +47,7 @@ impl ViewLine { self } + /// Set a padding character. #[must_use] #[inline] pub fn set_padding(mut self, c: char) -> Self { @@ -49,6 +55,7 @@ impl ViewLine { self } + /// Set the padding character with a related color and style. #[must_use] #[inline] pub fn set_padding_with_color_and_style( @@ -69,18 +76,21 @@ impl ViewLine { self } + /// Get the number of pinned line segments. #[must_use] #[inline] pub const fn get_number_of_pinned_segment(&self) -> usize { self.pinned_segments } + /// Get the view line segments. #[must_use] #[inline] pub const fn get_segments(&self) -> &Vec<LineSegment> { &self.segments } + /// Is the line selected. #[must_use] #[inline] pub const fn get_selected(&self) -> bool { |