diff options
-rwxr-xr-x | scripts/add-lints.bash | 6 | ||||
-rw-r--r-- | src/display/src/color_mode.rs | 13 | ||||
-rw-r--r-- | src/display/src/crossterm.rs | 2 | ||||
-rw-r--r-- | src/display/src/display_color.rs | 19 | ||||
-rw-r--r-- | src/display/src/lib.rs | 54 | ||||
-rw-r--r-- | src/display/src/mockcrossterm.rs | 12 | ||||
-rw-r--r-- | src/display/src/size.rs | 4 | ||||
-rw-r--r-- | src/display/src/testutil.rs | 2 | ||||
-rw-r--r-- | src/display/src/tui.rs | 15 |
9 files changed, 122 insertions, 5 deletions
diff --git a/scripts/add-lints.bash b/scripts/add-lints.bash index 246ffc4..cda29f9 100755 --- a/scripts/add-lints.bash +++ b/scripts/add-lints.bash @@ -61,7 +61,11 @@ content="\ )] // enable all of Clippy's lints #![deny(clippy::all, clippy::cargo, clippy::nursery, clippy::pedantic, clippy::restriction)] -#![allow(clippy::blanket_clippy_restriction_lints)] +#![allow( + clippy::blanket_clippy_restriction_lints, + clippy::implicit_return, + clippy::missing_docs_in_private_items, +)] #![deny( rustdoc::bare_urls, rustdoc::broken_intra_doc_links, diff --git a/src/display/src/color_mode.rs b/src/display/src/color_mode.rs index 4887de6..da3f5d7 100644 --- a/src/display/src/color_mode.rs +++ b/src/display/src/color_mode.rs @@ -1,19 +1,32 @@ use super::color_mode::ColorMode::{EightBit, FourBit, TrueColor}; +/// Represents the color mode of a terminal interface. #[derive(Copy, Clone, Debug, PartialEq)] +#[allow(clippy::exhaustive_enums)] pub enum ColorMode { + /// Supports 2 colors. TwoTone, + /// Supports 8 colors. ThreeBit, + /// Supports 16 colors. FourBit, + /// Supports 256 colors. EightBit, + /// Supports 24 bits of color. TrueColor, } impl ColorMode { + /// Supports 4 bit or more of color. + #[inline] + #[must_use] pub fn has_minimum_four_bit_color(self) -> bool { self == FourBit || self == EightBit || self == TrueColor } + /// Has true color support. + #[inline] + #[must_use] pub fn has_true_color(self) -> bool { self == TrueColor } diff --git a/src/display/src/crossterm.rs b/src/display/src/crossterm.rs index 3ddc4bc..1c2b8a2 100644 --- a/src/display/src/crossterm.rs +++ b/src/display/src/crossterm.rs @@ -26,6 +26,7 @@ use crossterm::{ use super::{color_mode::ColorMode, size::Size, tui::Tui, utils::detect_color_mode}; +/// A thin wrapper over the [Crossterm library](https://github.com/crossterm-rs/crossterm). #[derive(Debug)] pub struct CrossTerm { color_mode: ColorMode, @@ -149,6 +150,7 @@ impl Tui for CrossTerm { } impl CrossTerm { + /// Create a new instance. #[inline] #[must_use] pub fn new() -> Self { diff --git a/src/display/src/display_color.rs b/src/display/src/display_color.rs index 30b314e..1c7ba87 100644 --- a/src/display/src/display_color.rs +++ b/src/display/src/display_color.rs @@ -1,22 +1,41 @@ +/// An abstraction of colors to display. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[allow(clippy::exhaustive_enums)] pub enum DisplayColor { + /// The color for the break action. ActionBreak, + /// The color for the drop action. ActionDrop, + /// The color for the edit action. ActionEdit, + /// The color for the exec action. ActionExec, + /// The color for the fixup action. ActionFixup, + /// The color for the pick action. ActionPick, + /// The color for the reword action. ActionReword, + /// The color for the squash action. ActionSquash, + /// The color for the label action. ActionLabel, + /// The color for the reset action. ActionReset, + /// The color for the merge action. ActionMerge, + /// The color for added lines in a diff. DiffAddColor, + /// The color for changed lines in a diff. DiffChangeColor, + /// The color for removed lines in a diff. DiffRemoveColor, + /// The color for context lines in a diff. DiffContextColor, + /// The color for whitespace characters in a diff. DiffWhitespaceColor, + /// The color for indicator text. IndicatorColor, + /// The color for the standard text. Normal, } diff --git a/src/display/src/lib.rs b/src/display/src/lib.rs index f2bf203..9fd7327 100644 --- a/src/display/src/lib.rs +++ b/src/display/src/lib.rs @@ -50,7 +50,7 @@ #![allow( clippy::blanket_clippy_restriction_lints, clippy::implicit_return, - clippy::missing_docs_in_private_items, + clippy::missing_docs_in_private_items )] #![deny( rustdoc::bare_urls, @@ -63,8 +63,6 @@ )] // LINT-REPLACE-END #![allow( - missing_docs, - rustdoc::missing_crate_level_docs, clippy::as_conversions, clippy::cast_possible_truncation, clippy::default_numeric_fallback, @@ -78,6 +76,33 @@ clippy::unwrap_used, clippy::wildcard_enum_match_arm )] + +//! Git Interactive Rebase Tool - Display Module +//! +//! # Description +//! This module is used to handle working with the terminal display. +//! +//! ``` +//! use config::Config; +//! use display::{CrossTerm, Display, DisplayColor}; +//! let config = Config::new().expect("Could not load config"); +//! let tui = CrossTerm::new(); +//! let mut display = Display::new(tui, &config.theme); +//! +//! display.start(); +//! display.clear(); +//! display.draw_str("Hello world!"); +//! display.color(DisplayColor::IndicatorColor, false); +//! display.set_style(false, true, false); +//! display.draw_str("Hello colorful, underlined world!"); +//! display.refresh(); +//! display.end(); +//! ``` +//! +//! ## 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 color_mode; mod crossterm; mod display_color; @@ -92,8 +117,9 @@ use anyhow::Result; use config::Theme; use self::utils::register_selectable_color_pairs; -pub use self::{crossterm::CrossTerm, display_color::DisplayColor, size::Size, tui::Tui}; +pub use self::{color_mode::ColorMode, crossterm::CrossTerm, display_color::DisplayColor, size::Size, tui::Tui}; +/// A high level interface to the terminal display. #[derive(Debug)] pub struct Display<T: Tui> { action_break: (Colors, Colors), @@ -118,6 +144,7 @@ pub struct Display<T: Tui> { } impl<T: Tui> Display<T> { + /// Create a new display instance. #[inline] pub fn new(tui: T, theme: &Theme) -> Self { let color_mode = tui.get_color_mode(); @@ -253,11 +280,13 @@ impl<T: Tui> Display<T> { } } + /// Draws a string of text to the terminal interface. #[inline] pub fn draw_str(&mut self, s: &str) -> Result<()> { self.tui.print(s) } + /// Clear the terminal interface and reset any style and color attributes. #[inline] pub fn clear(&mut self) -> Result<()> { self.color(DisplayColor::Normal, false)?; @@ -265,11 +294,16 @@ impl<T: Tui> Display<T> { self.tui.reset() } + /// Force a refresh of the terminal interface. This normally should be called after after all + /// text has been drawn to the terminal interface. This is considered a slow operation, so + /// should be called only as needed. #[inline] pub fn refresh(&mut self) -> Result<()> { self.tui.flush() } + /// Set the color of text drawn to the terminal interface. This will only change text drawn to + /// the terminal after this function call. #[inline] pub fn color(&mut self, color: DisplayColor, selected: bool) -> Result<()> { self.tui.set_color( @@ -320,6 +354,8 @@ impl<T: Tui> Display<T> { ) } + /// Set the style attributes of text drawn to the terminal interface. This will only change text + /// drawn to the terminal after this function call. #[inline] pub fn set_style(&mut self, dim: bool, underline: bool, reverse: bool) -> Result<()> { self.set_dim(dim)?; @@ -327,33 +363,43 @@ impl<T: Tui> Display<T> { self.set_reverse(reverse) } + /// Get the width and height of the terminal interface. This can be a slow operation, so should + /// not be called unless absolutely needed. #[inline] pub fn get_window_size(&self) -> Size { self.tui.get_size() } + /// Reset the cursor position to the start of the line. #[inline] pub fn ensure_at_line_start(&mut self) -> Result<()> { self.tui.move_to_column(1) } + /// Move the cursor position `right` characters from the end of the line. #[inline] pub fn move_from_end_of_line(&mut self, right: u16) -> Result<()> { let width = self.get_window_size().width(); self.tui.move_to_column(width as u16 - right + 1) } + /// Move the cursor to the next line. #[inline] pub fn next_line(&mut self) -> Result<()> { self.tui.move_next_line() } + /// Start the terminal interface interactions. This should be called before any terminal + /// interactions are performed. #[inline] pub fn start(&mut self) -> Result<()> { self.tui.start()?; self.tui.flush() } + /// End the terminal interface interactions. This should be called after all terminal + /// interactions are complete. This resets the terminal interface to the default state, and + /// should be called on program exit. #[inline] pub fn end(&mut self) -> Result<()> { self.tui.end()?; diff --git a/src/display/src/mockcrossterm.rs b/src/display/src/mockcrossterm.rs index ab30a46..f8bdb7e 100644 --- a/src/display/src/mockcrossterm.rs +++ b/src/display/src/mockcrossterm.rs @@ -6,6 +6,7 @@ use crossterm::{ use super::{color_mode::ColorMode, size::Size, tui::Tui, utils::detect_color_mode}; +/// The state of the `CrossTerm` instance. #[derive(Copy, Clone, Debug, PartialEq)] pub enum State { New, @@ -13,6 +14,7 @@ pub enum State { Ended, } +/// A mocked version of `CrossTerm`, useful for testing. #[derive(Debug)] pub struct CrossTerm { attributes: Attributes, @@ -129,6 +131,7 @@ impl Tui for CrossTerm { } impl CrossTerm { + /// Create a new mocked version of `CrossTerm`. #[inline] #[must_use] pub fn new() -> Self { @@ -144,53 +147,62 @@ impl CrossTerm { } } + /// Get a representation of the rendered output. #[inline] #[must_use] pub const fn get_output(&self) -> &Vec<String> { &self.output } + /// Get the current state. #[inline] #[must_use] pub const fn get_state(&self) -> State { self.state } + /// Are colors enabled. #[inline] #[must_use] pub fn is_colors_enabled(&self, colors: Colors) -> bool { self.colors == colors } + /// Does the current style attributes contained dimmed. #[inline] #[must_use] pub fn is_dimmed(&self) -> bool { self.attributes.has(Attribute::Dim) } + /// Does the current style attributes contained reverse. #[inline] #[must_use] pub fn is_reverse(&self) -> bool { self.attributes.has(Attribute::Reverse) } + /// Does the current style attributes contained underlined. #[inline] #[must_use] pub fn is_underline(&self) -> bool { self.attributes.has(Attribute::Underlined) } + /// Update the size. #[inline] pub fn set_size(&mut self, size: Size) { self.size = size; } + /// Get the current cursor position. #[inline] #[must_use] pub const fn get_position(&self) -> (u16, u16) { self.position } + /// Has the output been flushed. #[inline] #[must_use] pub const fn is_dirty(&self) -> bool { diff --git a/src/display/src/size.rs b/src/display/src/size.rs index f20d054..57dd612 100644 --- a/src/display/src/size.rs +++ b/src/display/src/size.rs @@ -1,3 +1,4 @@ +/// Represents a terminal window size. #[derive(Copy, Clone, Debug, PartialEq)] pub struct Size { width: usize, @@ -5,18 +6,21 @@ pub struct Size { } impl Size { + /// Create a new instance with a width and height. #[inline] #[must_use] pub const fn new(width: usize, height: usize) -> Self { Self { width, height } } + /// Get the width. #[inline] #[must_use] pub const fn width(&self) -> usize { self.width } + /// Get the height. #[inline] #[must_use] pub const fn height(&self) -> usize { diff --git a/src/display/src/testutil.rs b/src/display/src/testutil.rs index 8b2a926..29d1ccc 100644 --- a/src/display/src/testutil.rs +++ b/src/display/src/testutil.rs @@ -1,6 +1,8 @@ +//! Utilities for writing tests that interact with the display. pub use super::mockcrossterm::CrossTerm; use super::*; +/// Assert the the content of the Display is an expected value. #[inline] pub fn assert_output(display: &Display<CrossTerm>, expected: &[&str]) { assert_eq!(display.tui.get_output().join(""), format!("{}\n", expected.join("\n"))); diff --git a/src/display/src/tui.rs b/src/display/src/tui.rs index caf74da..c352075 100644 --- a/src/display/src/tui.rs +++ b/src/display/src/tui.rs @@ -3,20 +3,35 @@ use crossterm::{event::Event, style::Colors}; use super::{color_mode::ColorMode, Size}; +/// An interface that describes interactions with a terminal interface. pub trait Tui { + /// Get the supported color mode. fn get_color_mode(&self) -> ColorMode; + /// Reset the terminal interface to a default state. fn reset(&mut self) -> Result<()>; + /// Flush the contents printed to the terminal interface. fn flush(&mut self) -> Result<()>; + /// Print text to the terminal interface. fn print(&mut self, s: &str) -> Result<()>; + /// Set the color attribute of text printed to the terminal interface. fn set_color(&mut self, colors: Colors) -> Result<()>; + /// Set the dimmed style attribute of text printed to the terminal interface. fn set_dim(&mut self, dim: bool) -> Result<()>; + /// Set the underlined style attribute of text printed to the terminal interface. fn set_underline(&mut self, underline: bool) -> Result<()>; + /// Set the reversed style attribute of text printed to the terminal interface. fn set_reverse(&mut self, reverse: bool) -> Result<()>; + /// Read the next input event from the terminal interface. fn read_event() -> Result<Option<Event>> where Self: Sized; + /// Get the number of columns and rows of the terminal interface. fn get_size(&self) -> Size; + /// Move the cursor position `x` characters from the start of the line. fn move_to_column(&mut self, x: u16) -> Result<()>; + /// Move the cursor to the next line. fn move_next_line(&mut self) -> Result<()>; + /// Start the terminal interface interactions. fn start(&mut self) -> Result<()>; + /// End the terminal interface interactions. fn end(&mut self) -> Result<()>; } |