summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Oram <dev@mitmaro.ca>2021-06-29 09:39:22 -0230
committerTim Oram <dev@mitmaro.ca>2021-07-05 16:27:53 -0230
commit566e548b3b7b69f4ed67e5182f3fb6dfa65b0e61 (patch)
tree605deac9940513e71df18508d6dfc29fdef33ebd
parentc8cd3dabfbe727ba8aaf9750965e35598fac835e (diff)
Add documentation to the view crate
-rw-r--r--src/process/mod.rs1
-rw-r--r--src/view/src/action.rs6
-rw-r--r--src/view/src/lib.rs32
-rw-r--r--src/view/src/line_segment.rs4
-rw-r--r--src/view/src/render_context.rs9
-rw-r--r--src/view/src/sender.rs36
-rw-r--r--src/view/src/testutil.rs16
-rw-r--r--src/view/src/thread.rs4
-rw-r--r--src/view/src/util.rs1
-rw-r--r--src/view/src/view_data.rs4
-rw-r--r--src/view/src/view_data_updater.rs12
-rw-r--r--src/view/src/view_line.rs10
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 {