summaryrefslogtreecommitdiffstats
path: root/src/display
diff options
context:
space:
mode:
authorTim Oram <dev@mitmaro.ca>2024-02-09 13:53:36 -0330
committerTim Oram <dev@mitmaro.ca>2024-02-15 20:27:06 -0330
commitcaf3d1169f70c2fff557b740f3ca577c0ec23fff (patch)
treed4af0add03383a0f020e6430ba4994ce12e6a407 /src/display
parentbae935621588026c521849e986dd0f9a4feb37e6 (diff)
Move display crate into core
Diffstat (limited to 'src/display')
-rw-r--r--src/display/Cargo.toml27
-rw-r--r--src/display/Makefile.toml2
-rw-r--r--src/display/README.md3
-rw-r--r--src/display/build.rs11
-rw-r--r--src/display/src/color_mode.rs86
-rw-r--r--src/display/src/crossterm.rs165
-rw-r--r--src/display/src/display_color.rs43
-rw-r--r--src/display/src/error.rs45
-rw-r--r--src/display/src/lib.rs751
-rw-r--r--src/display/src/size.rs29
-rw-r--r--src/display/src/testutil.rs22
-rw-r--r--src/display/src/testutil/mockable_tui.rs150
-rw-r--r--src/display/src/testutil/mockcrossterm.rs199
-rw-r--r--src/display/src/testutil/state.rs11
-rw-r--r--src/display/src/tui.rs109
-rw-r--r--src/display/src/utils.rs514
16 files changed, 0 insertions, 2167 deletions
diff --git a/src/display/Cargo.toml b/src/display/Cargo.toml
deleted file mode 100644
index 4de4d2b..0000000
--- a/src/display/Cargo.toml
+++ /dev/null
@@ -1,27 +0,0 @@
-[package]
-name = "girt-display"
-version = "2.3.0"
-authors = ["Tim Oram <dev@mitmaro.ca>"]
-license = "GPL-3.0-or-later"
-description = "Display modules for git-interactive-rebase-tool"
-homepage = "https://gitrebasetool.mitmaro.ca/"
-repository = "https://github.com/MitMaro/git-interactive-rebase-tool"
-edition = "2021"
-keywords = ["git", "cli"]
-categories = ["command-line-interface", "command-line-utilities"]
-readme = "README.md"
-
-[lib]
-name = "display"
-
-[dependencies]
-crossterm = "0.26.1"
-thiserror = "1.0.44"
-girt-config = {version = "2.3.0", path = "../config"}
-
-[dev-dependencies]
-rstest = "0.18.1"
-serial_test = "2.0.0"
-
-[build-dependencies]
-rustc_version = "0.4.0"
diff --git a/src/display/Makefile.toml b/src/display/Makefile.toml
deleted file mode 100644
index c19ab5c..0000000
--- a/src/display/Makefile.toml
+++ /dev/null
@@ -1,2 +0,0 @@
-extend = "../../Makefile.toml"
-
diff --git a/src/display/README.md b/src/display/README.md
deleted file mode 100644
index 40b4f2d..0000000
--- a/src/display/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# GIRT Display Crate
-
-This crate is part of the larger [Git Interactive Rebase Tool](../../README.md). The version is pinned to the root project, and this crate does not follow SemVer.
diff --git a/src/display/build.rs b/src/display/build.rs
deleted file mode 100644
index 4cc52ff..0000000
--- a/src/display/build.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use rustc_version::{version_meta, Channel};
-
-fn main() {
- // allow unknown lints in nightly builds
- if let Ok(meta) = version_meta() {
- if meta.channel == Channel::Nightly {
- println!("cargo:rustc-cfg=allow_unknown_lints");
- println!("cargo:rustc-cfg=include_nightly_lints");
- }
- }
-}
diff --git a/src/display/src/color_mode.rs b/src/display/src/color_mode.rs
deleted file mode 100644
index 1476521..0000000
--- a/src/display/src/color_mode.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-/// Represents the color mode of a terminal interface.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-#[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 == Self::FourBit || self == Self::EightBit || self == Self::TrueColor
- }
-
- /// Has true color support.
- #[inline]
- #[must_use]
- pub fn has_true_color(self) -> bool {
- self == Self::TrueColor
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn color_mode_has_minimum_four_bit_color_two_tone() {
- assert!(!ColorMode::TwoTone.has_minimum_four_bit_color());
- }
-
- #[test]
- fn color_mode_has_minimum_four_bit_color_three_bit() {
- assert!(!ColorMode::ThreeBit.has_minimum_four_bit_color());
- }
-
- #[test]
- fn color_mode_has_minimum_four_bit_color_four_bit() {
- assert!(ColorMode::FourBit.has_minimum_four_bit_color());
- }
-
- #[test]
- fn color_mode_has_minimum_four_bit_color_eight_bit() {
- assert!(ColorMode::EightBit.has_minimum_four_bit_color());
- }
-
- #[test]
- fn color_mode_has_minimum_four_bit_color_true_color() {
- assert!(ColorMode::TrueColor.has_minimum_four_bit_color());
- }
-
- #[test]
- fn color_mode_has_true_color_two_tone() {
- assert!(!ColorMode::TwoTone.has_true_color());
- }
-
- #[test]
- fn color_mode_has_true_color_three_bit() {
- assert!(!ColorMode::ThreeBit.has_true_color());
- }
-
- #[test]
- fn color_mode_has_true_color_four_bit() {
- assert!(!ColorMode::FourBit.has_true_color());
- }
-
- #[test]
- fn color_mode_has_true_color_eight_bit() {
- assert!(!ColorMode::EightBit.has_true_color());
- }
-
- #[test]
- fn color_mode_has_true_color_true_color() {
- assert!(ColorMode::TrueColor.has_true_color());
- }
-}
diff --git a/src/display/src/crossterm.rs b/src/display/src/crossterm.rs
deleted file mode 100644
index e96a4e3..0000000
--- a/src/display/src/crossterm.rs
+++ /dev/null
@@ -1,165 +0,0 @@
-use std::io::{stdout, BufWriter, Stdout, Write};
-
-use crossterm::{
- cursor::{Hide, MoveTo, MoveToColumn, MoveToNextLine, Show},
- event::{
- DisableMouseCapture,
- EnableMouseCapture,
- KeyboardEnhancementFlags,
- PopKeyboardEnhancementFlags,
- PushKeyboardEnhancementFlags,
- },
- style::{available_color_count, Attribute, Colors, Print, ResetColor, SetAttribute, SetColors},
- terminal::{
- disable_raw_mode,
- enable_raw_mode,
- size,
- Clear,
- ClearType,
- DisableLineWrap,
- EnableLineWrap,
- EnterAlternateScreen,
- LeaveAlternateScreen,
- },
- Command,
- QueueableCommand,
-};
-
-use crate::{color_mode::ColorMode, error::DisplayError, 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,
- window: BufWriter<Stdout>,
-}
-
-impl Tui for CrossTerm {
- #[inline]
- fn get_color_mode(&self) -> ColorMode {
- self.color_mode
- }
-
- #[inline]
- fn reset(&mut self) -> Result<(), DisplayError> {
- self.queue_command(ResetColor)?;
- self.queue_command(SetAttribute(Attribute::Reset))?;
- self.queue_command(Clear(ClearType::All))?;
- self.queue_command(MoveTo(0, 0))
- }
-
- #[inline]
- fn flush(&mut self) -> Result<(), DisplayError> {
- self.window.flush().map_err(DisplayError::Unexpected)
- }
-
- #[inline]
- fn print(&mut self, s: &str) -> Result<(), DisplayError> {
- self.queue_command(Print(s))
- }
-
- #[inline]
- fn set_color(&mut self, colors: Colors) -> Result<(), DisplayError> {
- self.queue_command(SetColors(colors))
- }
-
- #[inline]
- fn set_dim(&mut self, dim: bool) -> Result<(), DisplayError> {
- self.queue_command(SetAttribute(
- if dim {
- Attribute::Dim
- }
- else {
- Attribute::NormalIntensity
- },
- ))
- }
-
- #[inline]
- fn set_underline(&mut self, underline: bool) -> Result<(), DisplayError> {
- self.queue_command(SetAttribute(
- if underline {
- Attribute::Underlined
- }
- else {
- Attribute::NoUnderline
- },
- ))
- }
-
- #[inline]
- fn set_reverse(&mut self, reverse: bool) -> Result<(), DisplayError> {
- self.queue_command(SetAttribute(
- if reverse {
- Attribute::Reverse
- }
- else {
- Attribute::NoReverse
- },
- ))
- }
-
- #[inline]
- fn get_size(&self) -> Size {
- size().map_or_else(
- |_| Size::new(0, 0),
- |(width, height)| Size::new(usize::from(width), usize::from(height)),
- )
- }
-
- #[inline]
- fn move_to_column(&mut self, x: u16) -> Result<(), DisplayError> {
- self.queue_command(MoveToColumn(x))
- }
-
- #[inline]
- fn move_next_line(&mut self) -> Result<(), DisplayError> {
- self.queue_command(MoveToNextLine(1))
- }
-
- #[inline]
- fn start(&mut self) -> Result<(), DisplayError> {
- self.queue_command(EnterAlternateScreen)?;
- self.queue_command(DisableLineWrap)?;
- self.queue_command(Hide)?;
- self.queue_command(EnableMouseCapture)?;
- // this will fail on terminals without support, so ignore any errors
- let _command_result = self.queue_command(PushKeyboardEnhancementFlags(
- KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
- | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
- | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
- | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES,
- ));
- enable_raw_mode().map_err(DisplayError::Unexpected)?;
- self.flush()
- }
-
- #[inline]
- fn end(&mut self) -> Result<(), DisplayError> {
- // this will fail on terminals without support, so ignore any errors
- let _command_result = self.queue_command(PopKeyboardEnhancementFlags);
- self.queue_command(DisableMouseCapture)?;
- self.queue_command(Show)?;
- self.queue_command(EnableLineWrap)?;
- self.queue_command(LeaveAlternateScreen)?;
- disable_raw_mode().map_err(DisplayError::Unexpected)?;
- self.flush()
- }
-}
-
-impl CrossTerm {
- /// Create a new instance.
- #[inline]
- #[must_use]
- pub fn new() -> Self {
- Self {
- window: BufWriter::new(stdout()),
- color_mode: detect_color_mode(available_color_count()),
- }
- }
-
- fn queue_command(&mut self, command: impl Command) -> Result<(), DisplayError> {
- let _result = self.window.queue(command).map_err(DisplayError::Unexpected)?;
- Ok(())
- }
-}
diff --git a/src/display/src/display_color.rs b/src/display/src/display_color.rs
deleted file mode 100644
index 1207f6b..0000000
--- a/src/display/src/display_color.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-/// 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 the merge action.
- ActionUpdateRef,
- /// 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/error.rs b/src/display/src/error.rs
deleted file mode 100644
index 7f8e952..0000000
--- a/src/display/src/error.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use std::io;
-
-use thiserror::Error;
-
-/// A display error.
-#[derive(Error, Debug)]
-#[non_exhaustive]
-pub enum DisplayError {
- /// An unexpected error occurred.
- #[error("Unexpected error")]
- Unexpected(io::Error),
-}
-
-impl PartialEq for DisplayError {
- #[inline]
- #[allow(clippy::pattern_type_mismatch)]
- fn eq(&self, other: &Self) -> bool {
- match (self, other) {
- (Self::Unexpected(self_io_error), Self::Unexpected(other_io_error)) => {
- self_io_error.kind() == other_io_error.kind()
- },
- }
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn partial_eq_io_error_same() {
- assert_eq!(
- DisplayError::Unexpected(io::Error::from(io::ErrorKind::Other)),
- DisplayError::Unexpected(io::Error::from(io::ErrorKind::Other))
- );
- }
-
- #[test]
- fn partial_eq_io_error_different() {
- assert_ne!(
- DisplayError::Unexpected(io::Error::from(io::ErrorKind::Other)),
- DisplayError::Unexpected(io::Error::from(io::ErrorKind::NotFound))
- );
- }
-}
diff --git a/src/display/src/lib.rs b/src/display/src/lib.rs
deleted file mode 100644
index a74de2e..0000000
--- a/src/display/src/lib.rs
+++ /dev/null
@@ -1,751 +0,0 @@
-// LINT-REPLACE-START
-// This section is autogenerated, do not modify directly
-// nightly sometimes removes/renames lints
-#![cfg_attr(allow_unknown_lints, allow(unknown_lints))]
-#![cfg_attr(allow_unknown_lints, allow(renamed_and_removed_lints))]
-// enable all rustc's built-in lints
-#![deny(
- future_incompatible,
- nonstandard_style,
- rust_2018_compatibility,
- rust_2018_idioms,
- rust_2021_compatibility,
- unused,
- warnings
-)]
-// rustc's additional allowed by default lints
-#![deny(
- absolute_paths_not_starting_with_crate,
- deprecated_in_future,
- elided_lifetimes_in_paths,
- explicit_outlives_requirements,
- ffi_unwind_calls,
- keyword_idents,
- let_underscore_drop,
- macro_use_extern_crate,
- meta_variable_misuse,
- missing_abi,
- missing_copy_implementations,
- missing_debug_implementations,
- missing_docs,
- non_ascii_idents,
- noop_method_call,
- pointer_structural_match,
- rust_2021_incompatible_closure_captures,
- rust_2021_incompatible_or_patterns,
- rust_2021_prefixes_incompatible_syntax,
- rust_2021_prelude_collisions,
- single_use_lifetimes,
- trivial_casts,
- trivial_numeric_casts,
- unreachable_pub,
- unsafe_code,
- unsafe_op_in_unsafe_fn,
- unused_crate_dependencies,
- unused_extern_crates,
- unused_import_braces,
- unused_lifetimes,
- unused_macro_rules,
- unused_qualifications,
- unused_results,
- unused_tuple_struct_fields,
- variant_size_differences
-)]
-// enable all of Clippy's lints
-#![deny(clippy::all, clippy::cargo, clippy::pedantic, clippy::restriction)]
-#![allow(
- clippy::absolute_paths,
- clippy::arithmetic_side_effects,
- clippy::arithmetic_side_effects,
- clippy::blanket_clippy_restriction_lints,
- clippy::bool_to_int_with_if,
- clippy::default_numeric_fallback,
- clippy::else_if_without_else,
- clippy::expect_used,
- clippy::float_arithmetic,
- clippy::implicit_return,
- clippy::indexing_slicing,
- clippy::map_err_ignore,
- clippy::min_ident_chars,
- clippy::missing_docs_in_private_items,
- clippy::missing_trait_methods,
- clippy::module_name_repetitions,
- clippy::needless_raw_string_hashes,
- clippy::needless_raw_strings,
- clippy::new_without_default,
- clippy::non_ascii_literal,
- clippy::option_if_let_else,
- clippy::pattern_type_mismatch,
- clippy::pub_use,
- clippy::pub_with_shorthand,
- clippy::question_mark_used,
- clippy::redundant_closure_call,
- clippy::redundant_pub_crate,
- clippy::ref_patterns,
- clippy::self_named_module_files,
- clippy::single_call_fn,
- clippy::std_instead_of_alloc,
- clippy::std_instead_of_core,
- clippy::tabs_in_doc_comments,
- clippy::tests_outside_test_module,
- clippy::too_many_lines,
- clippy::unwrap_used
-)]
-#![deny(
- rustdoc::bare_urls,
- rustdoc::broken_intra_doc_links,
- rustdoc::invalid_codeblock_attributes,
- rustdoc::invalid_html_tags,
- rustdoc::missing_crate_level_docs,
- rustdoc::private_doc_tests,
- rustdoc::private_intra_doc_links
-)]
-// allow some things in tests
-#![cfg_attr(
- test,
- allow(
- let_underscore_drop,
- clippy::cognitive_complexity,
- clippy::let_underscore_must_use,
- clippy::let_underscore_untyped,
- clippy::needless_pass_by_value,
- clippy::panic,
- clippy::shadow_reuse,
- clippy::shadow_unrelated,
- clippy::undocumented_unsafe_blocks,
- clippy::unimplemented,
- clippy::unreachable
- )
-)]
-// allowable upcoming nightly lints
-#![cfg_attr(include_nightly_lints, allow(clippy::absolute_paths, clippy::arc_with_non_send_sync))]
-// LINT-REPLACE-END
-
-//! Git Interactive Rebase Tool - Display Module
-//!
-//! # Description
-//! This module is used to handle working with the terminal display.
-//!
-//! ```
-//! use config::Theme;
-//! use display::{CrossTerm, Display, DisplayColor};
-//! let theme = Theme::new();
-//! let tui = CrossTerm::new();
-//! let mut display = Display::new(tui, &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;
-#[cfg(not(tarpaulin_include))]
-mod crossterm;
-mod display_color;
-mod error;
-mod size;
-#[cfg(not(tarpaulin_include))]
-pub mod testutil;
-mod tui;
-mod utils;
-
-use ::crossterm::style::{Color, Colors};
-use config::Theme;
-
-use self::utils::register_selectable_color_pairs;
-pub use self::{
- color_mode::ColorMode,
- crossterm::CrossTerm,
- display_color::DisplayColor,
- error::DisplayError,
- size::Size,
- tui::Tui,
-};
-
-/// A high level interface to the terminal display.
-#[derive(Debug)]
-pub struct Display<T: Tui> {
- action_break: (Colors, Colors),
- action_drop: (Colors, Colors),
- action_edit: (Colors, Colors),
- action_exec: (Colors, Colors),
- action_fixup: (Colors, Colors),
- action_label: (Colors, Colors),
- action_merge: (Colors, Colors),
- action_pick: (Colors, Colors),
- action_reset: (Colors, Colors),
- action_reword: (Colors, Colors),
- action_squash: (Colors, Colors),
- action_update_ref: (Colors, Colors),
- tui: T,
- diff_add: (Colors, Colors),
- diff_change: (Colors, Colors),
- diff_context: (Colors, Colors),
- diff_remove: (Colors, Colors),
- diff_whitespace: (Colors, Colors),
- indicator: (Colors, Colors),
- normal: (Colors, Colors),
-}
-
-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();
- let normal = register_selectable_color_pairs(
- color_mode,
- theme.color_foreground,
- theme.color_background,
- theme.color_selected_background,
- );
- let indicator = register_selectable_color_pairs(
- color_mode,
- theme.color_indicator,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_break = register_selectable_color_pairs(
- color_mode,
- theme.color_action_break,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_drop = register_selectable_color_pairs(
- color_mode,
- theme.color_action_drop,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_edit = register_selectable_color_pairs(
- color_mode,
- theme.color_action_edit,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_exec = register_selectable_color_pairs(
- color_mode,
- theme.color_action_exec,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_fixup = register_selectable_color_pairs(
- color_mode,
- theme.color_action_fixup,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_pick = register_selectable_color_pairs(
- color_mode,
- theme.color_action_pick,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_reword = register_selectable_color_pairs(
- color_mode,
- theme.color_action_reword,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_squash = register_selectable_color_pairs(
- color_mode,
- theme.color_action_squash,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_label = register_selectable_color_pairs(
- color_mode,
- theme.color_action_label,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_reset = register_selectable_color_pairs(
- color_mode,
- theme.color_action_reset,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_merge = register_selectable_color_pairs(
- color_mode,
- theme.color_action_merge,
- theme.color_background,
- theme.color_selected_background,
- );
- let action_update_ref = register_selectable_color_pairs(
- color_mode,
- theme.color_action_update_ref,
- theme.color_background,
- theme.color_selected_background,
- );
- let diff_add = register_selectable_color_pairs(
- color_mode,
- theme.color_diff_add,
- theme.color_background,
- theme.color_selected_background,
- );
- let diff_change = register_selectable_color_pairs(
- color_mode,
- theme.color_diff_change,
- theme.color_background,
- theme.color_selected_background,
- );
- let diff_remove = register_selectable_color_pairs(
- color_mode,
- theme.color_diff_remove,
- theme.color_background,
- theme.color_selected_background,
- );
- let diff_context = register_selectable_color_pairs(
- color_mode,
- theme.color_diff_context,
- theme.color_background,
- theme.color_selected_background,
- );
- let diff_whitespace = register_selectable_color_pairs(
- color_mode,
- theme.color_diff_whitespace,
- theme.color_background,
- theme.color_selected_background,
- );
-
- Self {
- action_break,
- action_drop,
- action_edit,
- action_exec,
- action_fixup,
- action_label,
- action_merge,
- action_pick,
- action_reset,
- action_reword,
- action_squash,
- action_update_ref,
- tui,
- diff_add,
- diff_change,
- diff_context,
- diff_remove,
- diff_whitespace,
- indicator,
- normal,
- }
- }
-
- /// Draws a string of text to the terminal interface.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn draw_str(&mut self, s: &str) -> Result<(), DisplayError> {
- self.tui.print(s)
- }
-
- /// Clear the terminal interface and reset any style and color attributes.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn clear(&mut self) -> Result<(), DisplayError> {
- self.color(DisplayColor::Normal, false)?;
- self.set_style(false, false, false)?;
- 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.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn refresh(&mut self) -> Result<(), DisplayError> {
- 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.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn color(&mut self, color: DisplayColor, selected: bool) -> Result<(), DisplayError> {
- self.tui.set_color(
- if selected {
- match color {
- DisplayColor::ActionBreak => self.action_break.1,
- DisplayColor::ActionDrop => self.action_drop.1,
- DisplayColor::ActionEdit => self.action_edit.1,
- DisplayColor::ActionExec => self.action_exec.1,
- DisplayColor::ActionFixup => self.action_fixup.1,
- DisplayColor::ActionPick => self.action_pick.1,
- DisplayColor::ActionReword => self.action_reword.1,
- DisplayColor::ActionSquash => self.action_squash.1,
- DisplayColor::ActionLabel => self.action_label.1,
- DisplayColor::ActionReset => self.action_reset.1,
- DisplayColor::ActionMerge => self.action_merge.1,
- DisplayColor::ActionUpdateRef => self.action_update_ref.1,
- DisplayColor::Normal => self.normal.1,
- DisplayColor::IndicatorColor => self.indicator.1,
- DisplayColor::DiffAddColor => self.diff_add.1,
- DisplayColor::DiffRemoveColor => self.diff_remove.1,
- DisplayColor::DiffChangeColor => self.diff_change.1,
- DisplayColor::DiffContextColor => self.diff_context.1,
- DisplayColor::DiffWhitespaceColor => self.diff_whitespace.1,
- }
- }
- else {
- match color {
- DisplayColor::ActionBreak => self.action_break.0,
- DisplayColor::ActionDrop => self.action_drop.0,
- DisplayColor::ActionEdit => self.action_edit.0,
- DisplayColor::ActionExec => self.action_exec.0,
- DisplayColor::ActionFixup => self.action_fixup.0,
- DisplayColor::ActionPick => self.action_pick.0,
- DisplayColor::ActionReword => self.action_reword.0,
- DisplayColor::ActionSquash => self.action_squash.0,
- DisplayColor::ActionLabel => self.action_label.0,
- DisplayColor::ActionReset => self.action_reset.0,
- DisplayColor::ActionMerge => self.action_merge.0,
- DisplayColor::ActionUpdateRef => self.action_update_ref.0,
- DisplayColor::Normal => self.normal.0,
- DisplayColor::IndicatorColor => self.indicator.0,
- DisplayColor::DiffAddColor => self.diff_add.0,
- DisplayColor::DiffRemoveColor => self.diff_remove.0,
- DisplayColor::DiffChangeColor => self.diff_change.0,
- DisplayColor::DiffContextColor => self.diff_context.0,
- DisplayColor::DiffWhitespaceColor => self.diff_whitespace.0,
- }
- },
- )
- }
-
- /// Set the style attributes of text drawn to the terminal interface. This will only change text
- /// drawn to the terminal after this function call.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn set_style(&mut self, dim: bool, underline: bool, reverse: bool) -> Result<(), DisplayError> {
- self.set_dim(dim)?;
- self.set_underline(underline)?;
- 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.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn get_window_size(&self) -> Size {
- self.tui.get_size()
- }
-
- /// Reset the cursor position to the start of the line.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn ensure_at_line_start(&mut self) -> Result<(), DisplayError> {
- self.tui.move_to_column(0)
- }
-
- /// Move the cursor position `right` characters from the end of the line.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn move_from_end_of_line(&mut self, right: u16) -> Result<(), DisplayError> {
- let width = self.get_window_size().width().try_into().unwrap_or(u16::MAX);
- self.tui.move_to_column(width - right)
- }
-
- /// Move the cursor to the next line.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn next_line(&mut self) -> Result<(), DisplayError> {
- self.tui.move_next_line()
- }
-
- /// Start the terminal interface interactions. This should be called before any terminal
- /// interactions are performed.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn start(&mut self) -> Result<(), DisplayError> {
- 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.
- ///
- /// # Errors
- /// Will error if the underlying terminal interface is in an error state.
- #[inline]
- pub fn end(&mut self) -> Result<(), DisplayError> {
- self.tui.end()?;
- self.tui.flush()
- }
-
- fn set_dim(&mut self, on: bool) -> Result<(), DisplayError> {
- self.tui.set_dim(on)
- }
-
- fn set_underline(&mut self, on: bool) -> Result<(), DisplayError> {
- self.tui.set_underline(on)
- }
-
- fn set_reverse(&mut self, on: bool) -> Result<(), DisplayError> {
- self.tui.set_reverse(on)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use ::crossterm::style::Color as CrosstermColor;
- use rstest::rstest;
-
- use super::*;
- use crate::testutil::{CrossTerm, State};
-
- #[test]
- fn draw_str() {
- let mut display = Display::new(CrossTerm::new(), &Theme::new());
- display.draw_str("Test String").unwrap();
- assert_eq!(display.tui.get_output(), &["Test String"]);
- }
-
- #[test]
- fn clear() {
- let mut display = Display::new(CrossTerm::new(), &Theme::new());
- display.draw_str("Test String").unwrap();
- display.set_dim(true).unwrap();
- display.set_reverse(true).unwrap();
- display.set_underline(true).unwrap();
- display.clear().unwrap();
- assert!(display.tui.get_output().is_empty());
- assert!(!display.tui.is_dimmed());
- assert!(!display.tui.is_reverse());
- assert!(!display.tui.is_underline());
- }
-
- #[test]
- fn refresh() {
- let mut display = Display::new(CrossTerm::new(), &Theme::new());
- display.refresh().unwrap();
- assert!(!display.tui.is_dirty());
- }
-
- #[rstest]
- #[case::action_break(DisplayColor::ActionBreak, false, CrosstermColor::White, CrosstermColor::Reset)]
- #[case::action_break_selected(
- DisplayColor::ActionBreak,
- true,
- CrosstermColor::White,
- CrosstermColor::AnsiValue(237)