diff --git a/src/command.rs b/src/command.rs index 5deb756..6842f82 100644 --- a/src/command.rs +++ b/src/command.rs @@ -27,7 +27,7 @@ pub trait Command { /// /// This method does not need to be accessed manually, as it is used by the crossterm's [Command Api](../#command-api) #[cfg(windows)] - fn execute_winapi(&self) -> Result<()>; + fn execute_winapi(&self, writer: impl FnMut() -> Result<()>) -> Result<()>; /// Returns whether the ansi code representation of this command is supported by windows. /// @@ -49,8 +49,8 @@ impl Command for &T { #[inline] #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - T::execute_winapi(self) + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { + T::execute_winapi(self, _writer) } #[cfg(windows)] diff --git a/src/cursor.rs b/src/cursor.rs index f9151ac..6807273 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -76,7 +76,7 @@ impl Command for MoveTo { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::move_to(self.0, self.1) } } @@ -105,7 +105,7 @@ impl Command for MoveToNextLine { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::move_to_next_line(self.0) } } @@ -134,7 +134,7 @@ impl Command for MoveToPreviousLine { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::move_to_previous_line(self.0) } } @@ -162,7 +162,7 @@ impl Command for MoveToColumn { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::move_to_column(self.0) } } @@ -190,7 +190,7 @@ impl Command for MoveUp { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::move_up(self.0) } } @@ -218,7 +218,7 @@ impl Command for MoveRight { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::move_right(self.0) } } @@ -246,7 +246,7 @@ impl Command for MoveDown { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::move_down(self.0) } } @@ -274,7 +274,7 @@ impl Command for MoveLeft { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::move_left(self.0) } } @@ -299,7 +299,7 @@ impl Command for SavePosition { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::save_position() } } @@ -324,7 +324,7 @@ impl Command for RestorePosition { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::restore_position() } } @@ -346,7 +346,7 @@ impl Command for Hide { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::show_cursor(false) } } @@ -368,7 +368,7 @@ impl Command for Show { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::show_cursor(true) } } @@ -391,7 +391,7 @@ impl Command for EnableBlinking { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { Ok(()) } } @@ -414,7 +414,7 @@ impl Command for DisableBlinking { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { Ok(()) } } diff --git a/src/event.rs b/src/event.rs index 50f27c7..450ede0 100644 --- a/src/event.rs +++ b/src/event.rs @@ -232,7 +232,7 @@ impl Command for EnableMouseCapture { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::windows::enable_mouse_capture() } @@ -256,7 +256,7 @@ impl Command for DisableMouseCapture { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::windows::disable_mouse_capture() } diff --git a/src/macros.rs b/src/macros.rs index f7fccd2..8115b55 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -33,7 +33,40 @@ macro_rules! handle_command { if command.is_ansi_code_supported() { write_ansi_code!($writer, command.ansi_code()) } else { - command.execute_winapi().map_err($crate::ErrorKind::from) + 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)] @@ -160,7 +193,7 @@ macro_rules! impl_display { (for $($t:ty),+) => { $(impl ::std::fmt::Display for $t { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), ::std::fmt::Error> { - $crate::queue!(f, self).map_err(|_| ::std::fmt::Error) + $crate::handle_fmt_command!(f, self).map_err(|_| ::std::fmt::Error) } })* } @@ -312,7 +345,10 @@ mod tests { self.value } - fn execute_winapi(&self) -> CrosstermResult<()> { + fn execute_winapi( + &self, + _writer: impl FnMut() -> CrosstermResult<()>, + ) -> CrosstermResult<()> { self.stream.borrow_mut().push(self.value); Ok(()) } diff --git a/src/style.rs b/src/style.rs index 6773725..015891e 100644 --- a/src/style.rs +++ b/src/style.rs @@ -216,7 +216,7 @@ impl Command for SetForegroundColor { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::windows::set_foreground_color(self.0) } } @@ -249,7 +249,7 @@ impl Command for SetBackgroundColor { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::windows::set_background_color(self.0) } } @@ -298,7 +298,7 @@ impl Command for SetColors { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { if let Some(color) = self.0.foreground { sys::windows::set_foreground_color(color)?; } @@ -334,7 +334,7 @@ impl Command for SetAttribute { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { // attributes are not supported by WinAPI. Ok(()) } @@ -364,7 +364,7 @@ impl Command for SetAttributes { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { // attributes are not supported by WinAPI. Ok(()) } @@ -391,7 +391,7 @@ where } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { Ok(()) } } @@ -412,7 +412,7 @@ impl Command for ResetColor { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::windows::reset() } } @@ -431,8 +431,8 @@ impl Command for Print { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - print!("{}", self.0); + fn execute_winapi(&self, mut writer: impl FnMut() -> Result<()>) -> Result<()> { + writer()?; Ok(()) } } diff --git a/src/style/styled_content.rs b/src/style/styled_content.rs index bdfe3f4..fa8bb42 100644 --- a/src/style/styled_content.rs +++ b/src/style/styled_content.rs @@ -6,7 +6,7 @@ use std::{ }; use crate::{ - queue, + handle_fmt_command, style::{ Attribute, Color, Colorize, ContentStyle, ResetColor, SetAttributes, SetBackgroundColor, SetForegroundColor, Styler, @@ -96,16 +96,16 @@ impl Display for StyledContent { let mut reset = false; if let Some(bg) = self.style.background_color { - queue!(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?; + handle_fmt_command!(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?; reset = true; } if let Some(fg) = self.style.foreground_color { - queue!(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?; + handle_fmt_command!(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?; reset = true; } if !self.style.attributes.is_empty() { - queue!(f, SetAttributes(self.style.attributes)).map_err(|_| fmt::Error)?; + handle_fmt_command!(f, SetAttributes(self.style.attributes)).map_err(|_| fmt::Error)?; reset = true; } @@ -115,7 +115,7 @@ impl Display for StyledContent { // color (39m)" and "reset background color (49m)"; consider using // these. if reset { - queue!(f, ResetColor).map_err(|_| fmt::Error)?; + handle_fmt_command!(f, ResetColor).map_err(|_| fmt::Error)?; } Ok(()) diff --git a/src/terminal.rs b/src/terminal.rs index 0c565ac..58f81f4 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -147,7 +147,7 @@ impl Command for EnterAlternateScreen { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { let alternate_screen = ScreenBuffer::create(); alternate_screen.show()?; Ok(()) @@ -187,7 +187,7 @@ impl Command for LeaveAlternateScreen { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { let screen_buffer = ScreenBuffer::from(Handle::current_out_handle()?); screen_buffer.show()?; Ok(()) @@ -226,7 +226,7 @@ impl Command for ScrollUp { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::scroll_up(self.0) } } @@ -247,7 +247,7 @@ impl Command for ScrollDown { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::scroll_down(self.0) } } @@ -276,7 +276,7 @@ impl Command for Clear { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::clear(self.0) } } @@ -297,7 +297,7 @@ impl Command for SetSize { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::set_size(self.0, self.1) } } @@ -318,7 +318,7 @@ impl<'a> Command for SetTitle<'a> { } #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { + fn execute_winapi(&self, _writer: impl FnMut() -> Result<()>) -> Result<()> { sys::set_window_title(self.0) } }