Replace AnsiType with write_ansi (#523)

This commit is contained in:
Koxiaet 2020-12-28 06:56:32 +00:00 committed by GitHub
parent 52bed07ce8
commit 5be7d18475
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 238 additions and 424 deletions

View File

@ -2,10 +2,7 @@
//! //!
//! cargo run --example event-poll-read //! cargo run --example event-poll-read
use std::{ use std::{io::stdout, time::Duration};
io::{stdout, Write},
time::Duration,
};
use crossterm::{ use crossterm::{
cursor::position, cursor::position,

View File

@ -34,7 +34,7 @@ pub fn read_line() -> Result<String> {
} }
} }
return Ok(line); Ok(line)
} }
fn main() { fn main() {

View File

@ -2,7 +2,7 @@
//! //!
//! cargo run --example event-read //! cargo run --example event-read
use std::io::{stdout, Write}; use std::io::stdout;
use crossterm::{ use crossterm::{
cursor::position, cursor::position,

View File

@ -1,4 +0,0 @@
/// Wrapper type for write dynamic ansi string
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[doc(hidden)]
pub struct Ansi<T>(pub T);

View File

@ -1,6 +1,5 @@
use std::{fmt::Display, io::Write}; use std::fmt;
use std::io::{self, Write};
use crate::{execute, queue};
use super::error::Result; use super::error::Result;
@ -11,14 +10,12 @@ use super::error::Result;
/// In order to understand how to use and execute commands, /// In order to understand how to use and execute commands,
/// it is recommended that you take a look at [Command Api](../#command-api) chapter. /// it is recommended that you take a look at [Command Api](../#command-api) chapter.
pub trait Command { pub trait Command {
type AnsiType: Display; /// Write an ANSI representation of this commmand to the given writer.
/// Returns an ANSI code representation of this command.
/// An ANSI code can manipulate the terminal by writing it to the terminal buffer. /// An ANSI code can manipulate the terminal by writing it to the terminal buffer.
/// However, only Windows 10 and UNIX systems support this. /// However, only Windows 10 and UNIX systems support this.
/// ///
/// This method does not need to be accessed manually, as it is used by the crossterm's [Command Api](../#command-api) /// This method does not need to be accessed manually, as it is used by the crossterm's [Command Api](../#command-api)
fn ansi_code(&self) -> Self::AnsiType; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result;
/// Execute this command. /// Execute this command.
/// ///
@ -39,12 +36,9 @@ pub trait Command {
} }
} }
impl<T: Command> Command for &T { impl<T: Command + ?Sized> Command for &T {
type AnsiType = T::AnsiType; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
(**self).write_ansi(f)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
T::ansi_code(self)
} }
#[inline] #[inline]
@ -60,23 +54,19 @@ impl<T: Command> Command for &T {
} }
} }
/// An interface for commands that can be queued for further execution. /// An interface for types that can queue commands for further execution.
pub trait QueueableCommand<T: Display>: Sized { pub trait QueueableCommand {
/// Queues the given command for further execution. /// Queues the given command for further execution.
fn queue(&mut self, command: impl Command<AnsiType = T>) -> Result<&mut Self>; fn queue(&mut self, command: impl Command) -> Result<&mut Self>;
} }
/// An interface for commands that are directly executed. /// An interface for types that can directly execute commands.
pub trait ExecutableCommand<T: Display>: Sized { pub trait ExecutableCommand {
/// Executes the given command directly. /// Executes the given command directly.
fn execute(&mut self, command: impl Command<AnsiType = T>) -> Result<&mut Self>; fn execute(&mut self, command: impl Command) -> Result<&mut Self>;
} }
impl<T, A> QueueableCommand<A> for T impl<T: Write + ?Sized> QueueableCommand for T {
where
A: Display,
T: Write,
{
/// Queues the given command for further execution. /// Queues the given command for further execution.
/// ///
/// Queued commands will be executed in the following cases: /// Queued commands will be executed in the following cases:
@ -128,17 +118,24 @@ where
/// and can therefore not be written to the given `writer`. /// and can therefore not be written to the given `writer`.
/// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html) /// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html)
/// and [queue](./trait.QueueableCommand.html) for those old Windows versions. /// and [queue](./trait.QueueableCommand.html) for those old Windows versions.
fn queue(&mut self, command: impl Command<AnsiType = A>) -> Result<&mut Self> { fn queue(&mut self, command: impl Command) -> Result<&mut Self> {
queue!(self, command)?; #[cfg(windows)]
if !command.is_ansi_code_supported() {
command.execute_winapi(|| {
write_command_ansi(self, &command)?;
// winapi doesn't support queuing
self.flush()?;
Ok(())
})?;
return Ok(self);
}
write_command_ansi(self, command)?;
Ok(self) Ok(self)
} }
} }
impl<T, A> ExecutableCommand<A> for T impl<T: Write + ?Sized> ExecutableCommand for T {
where
A: Display,
T: Write,
{
/// Executes the given command directly. /// Executes the given command directly.
/// ///
/// The given command its ANSI escape code will be written and flushed onto `Self`. /// The given command its ANSI escape code will be written and flushed onto `Self`.
@ -179,8 +176,56 @@ where
/// and can therefore not be written to the given `writer`. /// and can therefore not be written to the given `writer`.
/// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html) /// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html)
/// and [queue](./trait.QueueableCommand.html) for those old Windows versions. /// and [queue](./trait.QueueableCommand.html) for those old Windows versions.
fn execute(&mut self, command: impl Command<AnsiType = A>) -> Result<&mut Self> { fn execute(&mut self, command: impl Command) -> Result<&mut Self> {
execute!(self, command)?; self.queue(command)?;
self.flush()?;
Ok(self) Ok(self)
} }
} }
/// Writes the ANSI representation of a command to the given writer.
fn write_command_ansi<C: Command>(
io: &mut (impl io::Write + ?Sized),
command: C,
) -> io::Result<()> {
struct Adapter<T> {
inner: T,
res: io::Result<()>,
}
impl<T: Write> fmt::Write for Adapter<T> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.inner.write_all(s.as_bytes()).map_err(|e| {
self.res = Err(e);
fmt::Error
})
}
}
let mut adapter = Adapter {
inner: io,
res: Ok(()),
};
command
.write_ansi(&mut adapter)
.map_err(|fmt::Error| match adapter.res {
Ok(()) => panic!(
"<{}>::write_ansi incorrectly errored",
std::any::type_name::<C>()
),
Err(e) => e,
})
}
/// Executes the ANSI representation of a command, using the given `fmt::Write`.
pub(crate) fn execute_fmt(f: &mut impl fmt::Write, command: impl Command) -> fmt::Result {
#[cfg(windows)]
if !command.is_ansi_code_supported() {
return command
.execute_winapi(|| panic!("this writer should not be possible to use here"))
.map_err(|_| fmt::Error);
}
command.write_ansi(f)
}

View File

@ -46,7 +46,7 @@ pub use sys::position;
#[cfg(windows)] #[cfg(windows)]
use crate::Result; use crate::Result;
use crate::{impl_display, Ansi, Command}; use crate::{impl_display, Command};
use std::fmt; use std::fmt;
mod ansi; mod ansi;
@ -61,18 +61,9 @@ pub(crate) mod sys;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveTo(pub u16, pub u16); pub struct MoveTo(pub u16, pub u16);
impl fmt::Display for Ansi<MoveTo> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::move_to_csi_sequence(f, (self.0).0, (self.0).1)
}
}
impl Command for MoveTo { impl Command for MoveTo {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::move_to_csi_sequence(f, self.0, self.1)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -90,18 +81,9 @@ impl Command for MoveTo {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveToNextLine(pub u16); pub struct MoveToNextLine(pub u16);
impl fmt::Display for Ansi<MoveToNextLine> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::move_to_next_line_csi_sequence(f, (self.0).0)
}
}
impl Command for MoveToNextLine { impl Command for MoveToNextLine {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::move_to_next_line_csi_sequence(f, self.0)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -119,18 +101,9 @@ impl Command for MoveToNextLine {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveToPreviousLine(pub u16); pub struct MoveToPreviousLine(pub u16);
impl fmt::Display for Ansi<MoveToPreviousLine> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::move_to_previous_line_csi_sequence(f, (self.0).0)
}
}
impl Command for MoveToPreviousLine { impl Command for MoveToPreviousLine {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::move_to_previous_line_csi_sequence(f, self.0)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -147,18 +120,9 @@ impl Command for MoveToPreviousLine {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveToColumn(pub u16); pub struct MoveToColumn(pub u16);
impl fmt::Display for Ansi<MoveToColumn> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::move_to_column_csi_sequence(f, (self.0).0)
}
}
impl Command for MoveToColumn { impl Command for MoveToColumn {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::move_to_column_csi_sequence(f, self.0)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -175,18 +139,9 @@ impl Command for MoveToColumn {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveUp(pub u16); pub struct MoveUp(pub u16);
impl fmt::Display for Ansi<MoveUp> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::move_up_csi_sequence(f, (self.0).0)
}
}
impl Command for MoveUp { impl Command for MoveUp {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::move_up_csi_sequence(f, self.0)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -203,18 +158,9 @@ impl Command for MoveUp {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveRight(pub u16); pub struct MoveRight(pub u16);
impl fmt::Display for Ansi<MoveRight> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::move_right_csi_sequence(f, (self.0).0)
}
}
impl Command for MoveRight { impl Command for MoveRight {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::move_right_csi_sequence(f, self.0)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -231,18 +177,9 @@ impl Command for MoveRight {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveDown(pub u16); pub struct MoveDown(pub u16);
impl fmt::Display for Ansi<MoveDown> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::move_down_csi_sequence(f, (self.0).0)
}
}
impl Command for MoveDown { impl Command for MoveDown {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::move_down_csi_sequence(f, self.0)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -259,18 +196,9 @@ impl Command for MoveDown {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MoveLeft(pub u16); pub struct MoveLeft(pub u16);
impl fmt::Display for Ansi<MoveLeft> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::move_left_csi_sequence(f, (self.0).0)
}
}
impl Command for MoveLeft { impl Command for MoveLeft {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::move_left_csi_sequence(f, self.0)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -291,11 +219,8 @@ impl Command for MoveLeft {
pub struct SavePosition; pub struct SavePosition;
impl Command for SavePosition { impl Command for SavePosition {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::SAVE_POSITION_CSI_SEQUENCE)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
ansi::SAVE_POSITION_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -316,11 +241,8 @@ impl Command for SavePosition {
pub struct RestorePosition; pub struct RestorePosition;
impl Command for RestorePosition { impl Command for RestorePosition {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::RESTORE_POSITION_CSI_SEQUENCE)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
ansi::RESTORE_POSITION_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -338,11 +260,8 @@ impl Command for RestorePosition {
pub struct Hide; pub struct Hide;
impl Command for Hide { impl Command for Hide {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::HIDE_CSI_SEQUENCE)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
ansi::HIDE_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -360,11 +279,8 @@ impl Command for Hide {
pub struct Show; pub struct Show;
impl Command for Show { impl Command for Show {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::SHOW_CSI_SEQUENCE)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
ansi::SHOW_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -383,11 +299,8 @@ impl Command for Show {
pub struct EnableBlinking; pub struct EnableBlinking;
impl Command for EnableBlinking { impl Command for EnableBlinking {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::ENABLE_BLINKING_CSI_SEQUENCE)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
ansi::ENABLE_BLINKING_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -406,11 +319,8 @@ impl Command for EnableBlinking {
pub struct DisableBlinking; pub struct DisableBlinking;
impl Command for DisableBlinking { impl Command for DisableBlinking {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::DISABLE_BLINKING_CSI_SEQUENCE)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
ansi::DISABLE_BLINKING_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -436,7 +346,7 @@ impl_display!(for DisableBlinking);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io::{self, stdout, Write}; use std::io::{self, stdout};
use crate::execute; use crate::execute;

View File

@ -1,13 +1,13 @@
//! This module provides cursor related ANSI escape codes. //! This module provides cursor related ANSI escape codes.
use crate::csi; use crate::csi;
use std::fmt::{self, Formatter}; use std::fmt;
pub(crate) fn move_to_csi_sequence(f: &mut Formatter, x: u16, y: u16) -> fmt::Result { pub(crate) fn move_to_csi_sequence(f: &mut impl fmt::Write, x: u16, y: u16) -> fmt::Result {
write!(f, csi!("{};{}H"), y + 1, x + 1) write!(f, csi!("{};{}H"), y + 1, x + 1)
} }
pub(crate) fn move_up_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { pub(crate) fn move_up_csi_sequence(f: &mut impl fmt::Write, count: u16) -> fmt::Result {
if count != 0 { if count != 0 {
write!(f, csi!("{}A"), count) write!(f, csi!("{}A"), count)
} else { } else {
@ -15,7 +15,7 @@ pub(crate) fn move_up_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result
} }
} }
pub(crate) fn move_right_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { pub(crate) fn move_right_csi_sequence(f: &mut impl fmt::Write, count: u16) -> fmt::Result {
if count != 0 { if count != 0 {
write!(f, csi!("{}C"), count) write!(f, csi!("{}C"), count)
} else { } else {
@ -23,7 +23,7 @@ pub(crate) fn move_right_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Res
} }
} }
pub(crate) fn move_down_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { pub(crate) fn move_down_csi_sequence(f: &mut impl fmt::Write, count: u16) -> fmt::Result {
if count != 0 { if count != 0 {
write!(f, csi!("{}B"), count) write!(f, csi!("{}B"), count)
} else { } else {
@ -31,7 +31,7 @@ pub(crate) fn move_down_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Resu
} }
} }
pub(crate) fn move_left_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { pub(crate) fn move_left_csi_sequence(f: &mut impl fmt::Write, count: u16) -> fmt::Result {
if count != 0 { if count != 0 {
write!(f, csi!("{}D"), count) write!(f, csi!("{}D"), count)
} else { } else {
@ -39,15 +39,18 @@ pub(crate) fn move_left_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Resu
} }
} }
pub(crate) fn move_to_column_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { pub(crate) fn move_to_column_csi_sequence(f: &mut impl fmt::Write, count: u16) -> fmt::Result {
write!(f, csi!("{}G"), count) write!(f, csi!("{}G"), count)
} }
pub(crate) fn move_to_previous_line_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { pub(crate) fn move_to_previous_line_csi_sequence(
f: &mut impl fmt::Write,
count: u16,
) -> fmt::Result {
write!(f, csi!("{}F"), count) write!(f, csi!("{}F"), count)
} }
pub(crate) fn move_to_next_line_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { pub(crate) fn move_to_next_line_csi_sequence(f: &mut impl fmt::Write, count: u16) -> fmt::Result {
write!(f, csi!("{}E"), count) write!(f, csi!("{}E"), count)
} }

View File

@ -70,6 +70,7 @@
//! Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder for more of //! Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder for more of
//! them (`event-*`). //! them (`event-*`).
use std::fmt;
use std::time::Duration; use std::time::Duration;
use parking_lot::RwLock; use parking_lot::RwLock;
@ -225,10 +226,8 @@ where
pub struct EnableMouseCapture; pub struct EnableMouseCapture;
impl Command for EnableMouseCapture { impl Command for EnableMouseCapture {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::ENABLE_MOUSE_MODE_CSI_SEQUENCE)
fn ansi_code(&self) -> Self::AnsiType {
ansi::ENABLE_MOUSE_MODE_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -249,10 +248,8 @@ impl Command for EnableMouseCapture {
pub struct DisableMouseCapture; pub struct DisableMouseCapture;
impl Command for DisableMouseCapture { impl Command for DisableMouseCapture {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::DISABLE_MOUSE_MODE_CSI_SEQUENCE)
fn ansi_code(&self) -> Self::AnsiType {
ansi::DISABLE_MOUSE_MODE_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]

View File

@ -231,7 +231,6 @@
//! [flush]: https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush //! [flush]: https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush
pub use crate::{ pub use crate::{
ansi::Ansi,
command::{Command, ExecutableCommand, QueueableCommand}, command::{Command, ExecutableCommand, QueueableCommand},
error::{ErrorKind, Result}, error::{ErrorKind, Result},
}; };
@ -248,7 +247,6 @@ pub mod terminal;
/// A module to query if the current instance is a tty. /// A module to query if the current instance is a tty.
pub mod tty; pub mod tty;
mod ansi;
#[cfg(windows)] #[cfg(windows)]
/// A module that exposes one function to check if the current terminal supports ansi sequences. /// A module that exposes one function to check if the current terminal supports ansi sequences.
pub mod ansi_support; pub mod ansi_support;

View File

@ -5,77 +5,6 @@ macro_rules! csi {
($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) }; ($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) };
} }
/// Writes an ansi code to the given writer.
#[doc(hidden)]
#[macro_export]
macro_rules! write_ansi_code {
($writer:expr, $ansi_code:expr) => {{
use std::io::{self, ErrorKind};
write!($writer, "{}", $ansi_code)
.map_err(|e| io::Error::new(ErrorKind::Other, e))
.map_err($crate::ErrorKind::IoError)
}};
}
/// Writes/executes the given command.
#[doc(hidden)]
#[macro_export]
macro_rules! handle_command {
($writer:expr, $command:expr) => {{
// Silent warning when the macro is used inside the `command` module
#[allow(unused_imports)]
use $crate::{write_ansi_code, Command};
#[cfg(windows)]
{
let command = $command;
if command.is_ansi_code_supported() {
write_ansi_code!($writer, command.ansi_code())
} else {
command
.execute_winapi(|| {
write!($writer, "{}", command.ansi_code())?;
// winapi doesn't support queuing
$writer.flush()?;
Ok(())
})
.map_err($crate::ErrorKind::from)
}
}
#[cfg(unix)]
{
write_ansi_code!($writer, $command.ansi_code())
}
}};
}
// Offer the same functionality as queue! macro, but is used only internally and with std::fmt::Write as $writer
// The difference is in case of winapi we ignore the $writer and use a fake one
#[doc(hidden)]
#[macro_export]
macro_rules! handle_fmt_command {
($writer:expr, $command:expr) => {{
use $crate::{write_ansi_code, Command};
#[cfg(windows)]
{
let command = $command;
if command.is_ansi_code_supported() {
write_ansi_code!($writer, command.ansi_code())
} else {
command
.execute_winapi(|| panic!("this writer should not be possible to use here"))
.map_err($crate::ErrorKind::from)
}
}
#[cfg(unix)]
{
write_ansi_code!($writer, $command.ansi_code())
}
}};
}
/// Queues one or more command(s) for further execution. /// Queues one or more command(s) for further execution.
/// ///
/// Queued commands must be flushed to the underlying device to be executed. /// Queued commands must be flushed to the underlying device to be executed.
@ -129,11 +58,14 @@ macro_rules! handle_fmt_command {
/// ///
#[macro_export] #[macro_export]
macro_rules! queue { macro_rules! queue {
($writer:expr $(, $command:expr)* $(,)?) => { ($writer:expr $(, $command:expr)* $(,)?) => {{
Ok(()) $( use ::std::io::Write;
.and_then(|()| $crate::handle_command!($writer, $command))
)* // This allows the macro to take both mut impl Write and &mut impl Write.
} Ok($writer.by_ref())
$(.and_then(|writer| $crate::QueueableCommand::queue(writer, $command)))*
.map(|_| ())
}}
} }
/// Executes one or more command(s). /// Executes one or more command(s).
@ -179,12 +111,15 @@ macro_rules! queue {
/// and [queue](macro.queue.html) for those old Windows versions. /// and [queue](macro.queue.html) for those old Windows versions.
#[macro_export] #[macro_export]
macro_rules! execute { macro_rules! execute {
($writer:expr $(, $command:expr)* $(,)? ) => { ($writer:expr $(, $command:expr)* $(,)? ) => {{
use ::std::io::Write;
// Queue each command, then flush // Queue each command, then flush
$crate::queue!($writer $(, $command)*).and_then(|()| { $crate::queue!($writer $(, $command)*)
$writer.flush().map_err($crate::ErrorKind::IoError) .and_then(|()| {
}) ::std::io::Write::flush($writer.by_ref()).map_err($crate::ErrorKind::IoError)
} })
}}
} }
#[doc(hidden)] #[doc(hidden)]
@ -192,8 +127,8 @@ macro_rules! execute {
macro_rules! impl_display { macro_rules! impl_display {
(for $($t:ty),+) => { (for $($t:ty),+) => {
$(impl ::std::fmt::Display for $t { $(impl ::std::fmt::Display for $t {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), ::std::fmt::Error> { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
$crate::handle_fmt_command!(f, self).map_err(|_| ::std::fmt::Error) $crate::command::execute_fmt(f, self)
} }
})* })*
} }
@ -215,6 +150,7 @@ macro_rules! impl_from {
mod tests { mod tests {
use std::io; use std::io;
use std::str; use std::str;
// Helper for execute tests to confirm flush // Helper for execute tests to confirm flush
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub(self) struct FakeWrite { pub(self) struct FakeWrite {
@ -239,7 +175,7 @@ mod tests {
#[cfg(not(windows))] #[cfg(not(windows))]
mod unix { mod unix {
use std::io::Write; use std::fmt;
use super::FakeWrite; use super::FakeWrite;
use crate::command::Command; use crate::command::Command;
@ -247,10 +183,8 @@ mod tests {
pub struct FakeCommand; pub struct FakeCommand;
impl Command for FakeCommand { impl Command for FakeCommand {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str("cmd")
fn ansi_code(&self) -> Self::AnsiType {
"cmd"
} }
} }
@ -305,9 +239,10 @@ mod tests {
#[cfg(windows)] #[cfg(windows)]
mod windows { mod windows {
use std::fmt;
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::Debug; use std::fmt::Debug;
use std::io::Write;
use super::FakeWrite; use super::FakeWrite;
use crate::command::Command; use crate::command::Command;
@ -339,10 +274,8 @@ mod tests {
} }
impl<'a> Command for FakeCommand<'a> { impl<'a> Command for FakeCommand<'a> {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(self.value)
fn ansi_code(&self) -> Self::AnsiType {
self.value
} }
fn execute_winapi( fn execute_winapi(
@ -379,12 +312,12 @@ mod tests {
} }
// We need this for type inference, for whatever reason. // We need this for type inference, for whatever reason.
const EMPTY_RESULT: [&'static str; 0] = []; const EMPTY_RESULT: [&str; 0] = [];
// TODO: confirm that the correct sink was used, based on // TODO: confirm that the correct sink was used, based on
// is_ansi_code_supported // is_ansi_code_supported
match (writer.buffer.is_empty(), stream.is_empty()) { match (writer.buffer.is_empty(), stream.is_empty()) {
(true, true) if stream_result == &EMPTY_RESULT => {} (true, true) if stream_result == EMPTY_RESULT => {}
(true, true) => panic!( (true, true) => panic!(
"Neither the event stream nor the writer were populated. Expected {:?}", "Neither the event stream nor the writer were populated. Expected {:?}",
stream_result stream_result

View File

@ -110,12 +110,14 @@
//! ); //! );
//! ``` //! ```
use std::{env, fmt::Display}; use std::{
env,
fmt::{self, Display},
};
#[cfg(windows)] #[cfg(windows)]
use crate::Result; use crate::Result;
use crate::{impl_display, Ansi, Command}; use crate::{impl_display, Command};
use std::fmt;
pub use self::{ pub use self::{
attributes::Attributes, attributes::Attributes,
@ -201,18 +203,9 @@ pub fn available_color_count() -> u16 {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetForegroundColor(pub Color); pub struct SetForegroundColor(pub Color);
impl fmt::Display for Ansi<SetForegroundColor> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::set_fg_csi_sequence(f, (self.0).0)
}
}
impl Command for SetForegroundColor { impl Command for SetForegroundColor {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::set_fg_csi_sequence(f, self.0)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -234,18 +227,9 @@ impl Command for SetForegroundColor {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetBackgroundColor(pub Color); pub struct SetBackgroundColor(pub Color);
impl fmt::Display for Ansi<SetBackgroundColor> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::set_bg_csi_sequence(f, (self.0).0)
}
}
impl Command for SetBackgroundColor { impl Command for SetBackgroundColor {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::set_bg_csi_sequence(f, self.0)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -277,25 +261,16 @@ impl Command for SetBackgroundColor {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetColors(pub Colors); pub struct SetColors(pub Colors);
impl fmt::Display for Ansi<SetColors> { impl Command for SetColors {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
if let Some(color) = (self.0).0.foreground { if let Some(color) = self.0.foreground {
ansi::set_fg_csi_sequence(f, color)?; ansi::set_fg_csi_sequence(f, color)?;
} }
if let Some(color) = (self.0).0.background { if let Some(color) = self.0.background {
ansi::set_bg_csi_sequence(f, color)?; ansi::set_bg_csi_sequence(f, color)?;
} }
Ok(()) Ok(())
} }
}
impl Command for SetColors {
type AnsiType = Ansi<Self>;
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
}
#[cfg(windows)] #[cfg(windows)]
fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> {
@ -319,18 +294,9 @@ impl Command for SetColors {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetAttribute(pub Attribute); pub struct SetAttribute(pub Attribute);
impl fmt::Display for Ansi<SetAttribute> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::set_attr_csi_sequence(f, (self.0).0)
}
}
impl Command for SetAttribute { impl Command for SetAttribute {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::set_attr_csi_sequence(f, self.0)
#[inline]
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -350,17 +316,9 @@ impl Command for SetAttribute {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetAttributes(pub Attributes); pub struct SetAttributes(pub Attributes);
impl fmt::Display for Ansi<SetAttributes> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ansi::set_attrs_csi_sequence(f, (self.0).0)
}
}
impl Command for SetAttributes { impl Command for SetAttributes {
type AnsiType = Ansi<Self>; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::set_attrs_csi_sequence(f, self.0)
fn ansi_code(&self) -> Self::AnsiType {
Ansi(*self)
} }
#[cfg(windows)] #[cfg(windows)]
@ -378,16 +336,11 @@ impl Command for SetAttributes {
/// ///
/// Commands must be executed/queued for execution otherwise they do nothing. /// Commands must be executed/queued for execution otherwise they do nothing.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct PrintStyledContent<D: Display + Clone>(pub StyledContent<D>); pub struct PrintStyledContent<D: Display>(pub StyledContent<D>);
impl<D> Command for PrintStyledContent<D> impl<D: Display> Command for PrintStyledContent<D> {
where fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
D: Display + Clone, write!(f, "{}", self.0)
{
type AnsiType = StyledContent<D>;
fn ansi_code(&self) -> Self::AnsiType {
self.0.clone()
} }
#[cfg(windows)] #[cfg(windows)]
@ -405,10 +358,8 @@ where
pub struct ResetColor; pub struct ResetColor;
impl Command for ResetColor { impl Command for ResetColor {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::RESET_CSI_SEQUENCE)
fn ansi_code(&self) -> Self::AnsiType {
ansi::RESET_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -421,28 +372,22 @@ impl Command for ResetColor {
/// ///
/// Commands must be executed/queued for execution otherwise they do nothing. /// Commands must be executed/queued for execution otherwise they do nothing.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Print<T: Display + Clone>(pub T); pub struct Print<T: Display>(pub T);
impl<T: Display + Clone> Command for Print<T> { impl<T: Display> Command for Print<T> {
type AnsiType = T; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
write!(f, "{}", self.0)
fn ansi_code(&self) -> Self::AnsiType {
self.0.clone()
} }
#[cfg(windows)] #[cfg(windows)]
fn execute_winapi(&self, mut writer: impl FnMut() -> Result<()>) -> Result<()> { fn execute_winapi(&self, mut writer: impl FnMut() -> Result<()>) -> Result<()> {
writer()?; writer()
Ok(())
} }
} }
impl<T: Display + Clone> Display for Print<T> { impl<T: Display> Display for Print<T> {
fn fmt( fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
&self, self.0.fmt(f)
f: &mut ::std::fmt::Formatter<'_>,
) -> ::std::result::Result<(), ::std::fmt::Error> {
write!(f, "{}", self.ansi_code())
} }
} }

View File

@ -8,19 +8,22 @@ use crate::{
style::{Attribute, Attributes, Color, Colored}, style::{Attribute, Attributes, Color, Colored},
}; };
pub(crate) fn set_fg_csi_sequence(f: &mut Formatter, fg_color: Color) -> fmt::Result { pub(crate) fn set_fg_csi_sequence(f: &mut impl fmt::Write, fg_color: Color) -> fmt::Result {
write!(f, csi!("{}m"), Colored::ForegroundColor(fg_color)) write!(f, csi!("{}m"), Colored::ForegroundColor(fg_color))
} }
pub(crate) fn set_bg_csi_sequence(f: &mut Formatter, bg_color: Color) -> fmt::Result { pub(crate) fn set_bg_csi_sequence(f: &mut impl fmt::Write, bg_color: Color) -> fmt::Result {
write!(f, csi!("{}m"), Colored::BackgroundColor(bg_color)) write!(f, csi!("{}m"), Colored::BackgroundColor(bg_color))
} }
pub(crate) fn set_attr_csi_sequence(f: &mut Formatter, attribute: Attribute) -> fmt::Result { pub(crate) fn set_attr_csi_sequence(f: &mut impl fmt::Write, attribute: Attribute) -> fmt::Result {
write!(f, csi!("{}m"), attribute.sgr()) write!(f, csi!("{}m"), attribute.sgr())
} }
pub(crate) fn set_attrs_csi_sequence(f: &mut Formatter, attributes: Attributes) -> fmt::Result { pub(crate) fn set_attrs_csi_sequence(
f: &mut impl fmt::Write,
attributes: Attributes,
) -> fmt::Result {
for attr in Attribute::iterator() { for attr in Attribute::iterator() {
if attributes.has(attr) { if attributes.has(attr) {
write!(f, csi!("{}m"), attr.sgr())?; write!(f, csi!("{}m"), attr.sgr())?;
@ -32,7 +35,7 @@ pub(crate) fn set_attrs_csi_sequence(f: &mut Formatter, attributes: Attributes)
pub(crate) const RESET_CSI_SEQUENCE: &str = csi!("0m"); pub(crate) const RESET_CSI_SEQUENCE: &str = csi!("0m");
impl fmt::Display for Colored { impl fmt::Display for Colored {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let color; let color;
match *self { match *self {

View File

@ -6,7 +6,7 @@ use std::{
}; };
use crate::{ use crate::{
handle_fmt_command, command::execute_fmt,
style::{ style::{
Attribute, Color, Colorize, ContentStyle, ResetColor, SetAttributes, SetBackgroundColor, Attribute, Color, Colorize, ContentStyle, ResetColor, SetAttributes, SetBackgroundColor,
SetForegroundColor, Styler, SetForegroundColor, Styler,
@ -98,16 +98,16 @@ impl<D: Display> Display for StyledContent<D> {
let mut reset = false; let mut reset = false;
if let Some(bg) = self.style.background_color { if let Some(bg) = self.style.background_color {
handle_fmt_command!(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?; execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
reset_background = true; reset_background = true;
} }
if let Some(fg) = self.style.foreground_color { if let Some(fg) = self.style.foreground_color {
handle_fmt_command!(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?; execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?;
reset_foreground = true; reset_foreground = true;
} }
if !self.style.attributes.is_empty() { if !self.style.attributes.is_empty() {
handle_fmt_command!(f, SetAttributes(self.style.attributes)).map_err(|_| fmt::Error)?; execute_fmt(f, SetAttributes(self.style.attributes)).map_err(|_| fmt::Error)?;
reset = true; reset = true;
} }
@ -117,14 +117,14 @@ impl<D: Display> Display for StyledContent<D> {
// NOTE: This will reset colors even though self has no colors, hence produce unexpected // NOTE: This will reset colors even though self has no colors, hence produce unexpected
// resets. // resets.
// TODO: reset the set attributes only. // TODO: reset the set attributes only.
handle_fmt_command!(f, ResetColor).map_err(|_| fmt::Error)?; execute_fmt(f, ResetColor).map_err(|_| fmt::Error)?;
} else { } else {
// NOTE: Since the above bug, we do not need to reset colors when we reset attributes. // NOTE: Since the above bug, we do not need to reset colors when we reset attributes.
if reset_background { if reset_background {
handle_fmt_command!(f, SetBackgroundColor(Color::Reset)).map_err(|_| fmt::Error)?; execute_fmt(f, SetBackgroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
} }
if reset_foreground { if reset_foreground {
handle_fmt_command!(f, SetForegroundColor(Color::Reset)).map_err(|_| fmt::Error)?; execute_fmt(f, SetForegroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
} }
} }

View File

@ -81,6 +81,8 @@
//! //!
//! For manual execution control check out [crossterm::queue](../macro.queue.html). //! For manual execution control check out [crossterm::queue](../macro.queue.html).
use std::fmt;
#[cfg(windows)] #[cfg(windows)]
use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer}; use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@ -121,10 +123,8 @@ pub fn size() -> Result<(u16, u16)> {
pub struct DisableLineWrap; pub struct DisableLineWrap;
impl Command for DisableLineWrap { impl Command for DisableLineWrap {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::DISABLE_LINE_WRAP_CSI_SEQUENCE)
fn ansi_code(&self) -> Self::AnsiType {
ansi::DISABLE_LINE_WRAP_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -142,10 +142,8 @@ impl Command for DisableLineWrap {
pub struct EnableLineWrap; pub struct EnableLineWrap;
impl Command for EnableLineWrap { impl Command for EnableLineWrap {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::ENABLE_LINE_WRAP_CSI_SEQUENCE)
fn ansi_code(&self) -> Self::AnsiType {
ansi::ENABLE_LINE_WRAP_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -184,10 +182,8 @@ impl Command for EnableLineWrap {
pub struct EnterAlternateScreen; pub struct EnterAlternateScreen;
impl Command for EnterAlternateScreen { impl Command for EnterAlternateScreen {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::ENTER_ALTERNATE_SCREEN_CSI_SEQUENCE)
fn ansi_code(&self) -> Self::AnsiType {
ansi::ENTER_ALTERNATE_SCREEN_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -224,10 +220,8 @@ impl Command for EnterAlternateScreen {
pub struct LeaveAlternateScreen; pub struct LeaveAlternateScreen;
impl Command for LeaveAlternateScreen { impl Command for LeaveAlternateScreen {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(ansi::LEAVE_ALTERNATE_SCREEN_CSI_SEQUENCE)
fn ansi_code(&self) -> Self::AnsiType {
ansi::LEAVE_ALTERNATE_SCREEN_CSI_SEQUENCE
} }
#[cfg(windows)] #[cfg(windows)]
@ -263,10 +257,8 @@ pub enum ClearType {
pub struct ScrollUp(pub u16); pub struct ScrollUp(pub u16);
impl Command for ScrollUp { impl Command for ScrollUp {
type AnsiType = String; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::scroll_up_csi_sequence(f, self.0)
fn ansi_code(&self) -> Self::AnsiType {
ansi::scroll_up_csi_sequence(self.0)
} }
#[cfg(windows)] #[cfg(windows)]
@ -284,10 +276,8 @@ impl Command for ScrollUp {
pub struct ScrollDown(pub u16); pub struct ScrollDown(pub u16);
impl Command for ScrollDown { impl Command for ScrollDown {
type AnsiType = String; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::scroll_down_csi_sequence(f, self.0)
fn ansi_code(&self) -> Self::AnsiType {
ansi::scroll_down_csi_sequence(self.0)
} }
#[cfg(windows)] #[cfg(windows)]
@ -307,16 +297,14 @@ impl Command for ScrollDown {
pub struct Clear(pub ClearType); pub struct Clear(pub ClearType);
impl Command for Clear { impl Command for Clear {
type AnsiType = &'static str; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
f.write_str(match self.0 {
fn ansi_code(&self) -> Self::AnsiType {
match self.0 {
ClearType::All => ansi::CLEAR_ALL_CSI_SEQUENCE, ClearType::All => ansi::CLEAR_ALL_CSI_SEQUENCE,
ClearType::FromCursorDown => ansi::CLEAR_FROM_CURSOR_DOWN_CSI_SEQUENCE, ClearType::FromCursorDown => ansi::CLEAR_FROM_CURSOR_DOWN_CSI_SEQUENCE,
ClearType::FromCursorUp => ansi::CLEAR_FROM_CURSOR_UP_CSI_SEQUENCE, ClearType::FromCursorUp => ansi::CLEAR_FROM_CURSOR_UP_CSI_SEQUENCE,
ClearType::CurrentLine => ansi::CLEAR_FROM_CURRENT_LINE_CSI_SEQUENCE, ClearType::CurrentLine => ansi::CLEAR_FROM_CURRENT_LINE_CSI_SEQUENCE,
ClearType::UntilNewLine => ansi::CLEAR_UNTIL_NEW_LINE_CSI_SEQUENCE, ClearType::UntilNewLine => ansi::CLEAR_UNTIL_NEW_LINE_CSI_SEQUENCE,
} })
} }
#[cfg(windows)] #[cfg(windows)]
@ -334,10 +322,8 @@ impl Command for Clear {
pub struct SetSize(pub u16, pub u16); pub struct SetSize(pub u16, pub u16);
impl Command for SetSize { impl Command for SetSize {
type AnsiType = String; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::set_size_csi_sequence(f, self.0, self.1)
fn ansi_code(&self) -> Self::AnsiType {
ansi::set_size_csi_sequence(self.0, self.1)
} }
#[cfg(windows)] #[cfg(windows)]
@ -355,10 +341,8 @@ impl Command for SetSize {
pub struct SetTitle<'a>(pub &'a str); pub struct SetTitle<'a>(pub &'a str);
impl<'a> Command for SetTitle<'a> { impl<'a> Command for SetTitle<'a> {
type AnsiType = String; fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
ansi::set_title_ansi_sequence(f, self.0)
fn ansi_code(&self) -> Self::AnsiType {
ansi::set_title_ansi_sequence(self.0)
} }
#[cfg(windows)] #[cfg(windows)]
@ -374,10 +358,7 @@ impl_display!(for Clear);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{ use std::{io::stdout, thread, time};
io::{stdout, Write},
thread, time,
};
use crate::execute; use crate::execute;

View File

@ -1,5 +1,7 @@
//! This module provides terminal related ANSI escape codes. //! This module provides terminal related ANSI escape codes.
use std::fmt;
use crate::csi; use crate::csi;
pub(crate) const CLEAR_ALL_CSI_SEQUENCE: &str = csi!("2J"); pub(crate) const CLEAR_ALL_CSI_SEQUENCE: &str = csi!("2J");
@ -12,18 +14,22 @@ pub(crate) const LEAVE_ALTERNATE_SCREEN_CSI_SEQUENCE: &str = csi!("?1049l");
pub(crate) const DISABLE_LINE_WRAP_CSI_SEQUENCE: &str = csi!("?7l"); pub(crate) const DISABLE_LINE_WRAP_CSI_SEQUENCE: &str = csi!("?7l");
pub(crate) const ENABLE_LINE_WRAP_CSI_SEQUENCE: &str = csi!("?7h"); pub(crate) const ENABLE_LINE_WRAP_CSI_SEQUENCE: &str = csi!("?7h");
pub(crate) fn scroll_up_csi_sequence(count: u16) -> String { pub(crate) fn scroll_up_csi_sequence(f: &mut impl fmt::Write, count: u16) -> fmt::Result {
format!(csi!("{}S"), count) write!(f, csi!("{}S"), count)
} }
pub(crate) fn scroll_down_csi_sequence(count: u16) -> String { pub(crate) fn scroll_down_csi_sequence(f: &mut impl fmt::Write, count: u16) -> fmt::Result {
format!(csi!("{}T"), count) write!(f, csi!("{}T"), count)
} }
pub(crate) fn set_size_csi_sequence(width: u16, height: u16) -> String { pub(crate) fn set_size_csi_sequence(
format!(csi!("8;{};{}t"), height, width) f: &mut impl fmt::Write,
width: u16,
height: u16,
) -> fmt::Result {
write!(f, csi!("8;{};{}t"), height, width)
} }
pub(crate) fn set_title_ansi_sequence(title: &str) -> String { pub(crate) fn set_title_ansi_sequence(f: &mut impl fmt::Write, title: &str) -> fmt::Result {
format!("\x1B]0;{}\x07", title) write!(f, "\x1B]0;{}\x07", title)
} }