aboutsummaryrefslogtreecommitdiff
path: root/mingling
diff options
context:
space:
mode:
Diffstat (limited to 'mingling')
-rw-r--r--mingling/src/confirm.rs5
-rw-r--r--mingling/src/confirm/count.rs75
-rw-r--r--mingling/src/confirm/predicate.rs60
-rw-r--r--mingling/src/lib.rs6
-rw-r--r--mingling/src/osc94.rs5
-rw-r--r--mingling/src/osc94/guard.rs203
-rw-r--r--mingling/src/osc94/state.rs243
-rw-r--r--mingling/src/res.rs7
-rw-r--r--mingling/src/res/confirm.rs184
-rw-r--r--mingling/src/res/confirmer.rs268
-rw-r--r--mingling/src/res/osc94.rs69
-rw-r--r--mingling/src/setups.rs7
-rw-r--r--mingling/src/setups/confirm.rs (renamed from mingling/src/setups/confirmer.rs)22
-rw-r--r--mingling/src/setups/osc94.rs89
14 files changed, 960 insertions, 283 deletions
diff --git a/mingling/src/confirm.rs b/mingling/src/confirm.rs
new file mode 100644
index 0000000..45a12d8
--- /dev/null
+++ b/mingling/src/confirm.rs
@@ -0,0 +1,5 @@
+mod predicate;
+pub use predicate::*;
+
+mod count;
+pub use count::*;
diff --git a/mingling/src/confirm/count.rs b/mingling/src/confirm/count.rs
new file mode 100644
index 0000000..c9f9db7
--- /dev/null
+++ b/mingling/src/confirm/count.rs
@@ -0,0 +1,75 @@
+/// Specifies the maximum number of attempts for a confirmation prompt.
+///
+/// # Default Implementations
+///
+/// `ConfirmCount` implements the following traits by default:
+///
+/// - [`Debug`] — for formatted output and debugging.
+/// - [`Clone`] — to create a copy of the value.
+/// - [`Copy`] — since the enum holds no heap-allocated data, it can be trivially copied.
+/// - [`PartialEq`] — allows comparing two `ConfirmCount` values for equality.
+/// - [`Eq`] — provides full equality semantics (as opposed to just partial).
+/// - [`From<T>`] for all primitive integer types (`i8`–`i128`, `isize`, `u8`–`u128`, `usize`),
+/// allowing convenient conversion from a raw number.
+///
+/// # What the Numbers Mean
+///
+/// The numeric value passed to a `From` conversion represents the **maximum number of times**
+/// the confirmation prompt will be shown to the user. For example:
+///
+/// - `ConfirmCount::from(3)` → asks at most **3** times before giving up.
+/// - `ConfirmCount::from(1)` → asks exactly **1** time.
+/// - `ConfirmCount::from(0)` → interpreted as [`ConfirmCount::Loop`], meaning it will keep asking
+/// indefinitely until a valid answer is parsed.
+///
+/// # Examples
+///
+/// ```
+/// use mingling::confirm::ConfirmCount;
+///
+/// // Convert from a numeric value
+/// let count: ConfirmCount = 3.into();
+/// assert_eq!(count, ConfirmCount::Max(3));
+///
+/// // Zero means loop forever
+/// let loop_count: ConfirmCount = 0.into();
+/// assert_eq!(loop_count, ConfirmCount::Loop);
+///
+/// // Large values are capped at usize::MAX
+/// let big: ConfirmCount = i128::MAX.into();
+/// assert_eq!(big, ConfirmCount::Max(usize::MAX));
+///
+/// // From a usize directly
+/// let from_usize = ConfirmCount::from(5usize);
+/// assert_eq!(from_usize, ConfirmCount::Max(5));
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ConfirmCount {
+ /// Loop indefinitely until the user gives a parseable answer.
+ Loop,
+ /// Ask at most the specified number of times.
+ Max(usize),
+}
+
+macro_rules! impl_from_for_Confirm_count {
+ ($($t:ty),*) => {
+ $(
+ impl From<$t> for ConfirmCount {
+ fn from(n: $t) -> Self {
+ if n == 0 {
+ ConfirmCount::Loop
+ } else {
+ match usize::try_from(n) {
+ Ok(max) => ConfirmCount::Max(max),
+ Err(_) => ConfirmCount::Max(usize::MAX),
+ }
+ }
+ }
+ }
+ )*
+ };
+}
+
+impl_from_for_Confirm_count!(
+ i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
+);
diff --git a/mingling/src/confirm/predicate.rs b/mingling/src/confirm/predicate.rs
new file mode 100644
index 0000000..786a459
--- /dev/null
+++ b/mingling/src/confirm/predicate.rs
@@ -0,0 +1,60 @@
+/// Defines how to parse user confirmation input.
+///
+/// A type implementing this trait determines which user input strings are treated as "yes" or "no".
+pub trait ConfirmPredicate {
+ /// Parses the user's input string, returning whether it is "yes".
+ ///
+ /// Returns `Some(true)` for yes, `Some(false)` for no,
+ /// and `None` if the input cannot be parsed (requiring re-entry).
+ fn is_yes(str: &str) -> Option<bool>;
+}
+
+/// A `ConfirmPredicate` implementation that accepts "y"/"yes" as yes and "n"/"no" as no.
+///
+/// Input comparison is case-insensitive and automatically trims leading/trailing whitespace.
+///
+/// # Examples
+///
+/// ```
+/// use mingling::res::ResConfirm;
+/// use mingling::confirm::YesConfirm;
+///
+/// let confirm = ResConfirm::default();
+/// let confirmed = confirm.ask::<YesConfirm>("Continue? [y/n] ");
+/// ```
+pub struct YesConfirm;
+
+/// A `ConfirmPredicate` implementation that accepts "true"/"t" as yes and "false"/"f" as no.
+///
+/// Input comparison is case-insensitive and automatically trims leading/trailing whitespace.
+///
+/// # Examples
+///
+/// ```
+/// use mingling::res::ResConfirm;
+/// use mingling::confirm::TrueConfirm;
+///
+/// let confirm = ResConfirm::default();
+/// let confirmed = confirm.ask::<TrueConfirm>("Enable this feature? [true/false] ");
+/// ```
+pub struct TrueConfirm;
+
+impl ConfirmPredicate for YesConfirm {
+ fn is_yes(str: &str) -> Option<bool> {
+ match str.trim().to_lowercase().as_str() {
+ "y" | "yes" => Some(true),
+ "n" | "no" => Some(false),
+ _ => None,
+ }
+ }
+}
+
+impl ConfirmPredicate for TrueConfirm {
+ fn is_yes(str: &str) -> Option<bool> {
+ match str.trim().to_lowercase().as_str() {
+ "true" | "t" => Some(true),
+ "false" | "f" => Some(false),
+ _ => None,
+ }
+ }
+}
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index 59ef7a6..9d38a2a 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -23,6 +23,9 @@ pub mod CRATE_ROOT {
#[cfg(feature = "core")]
pub mod metadata;
+/// Support for the `OSC 9;4` protocol
+pub mod osc94;
+
#[cfg(feature = "core")]
mod example_docs;
@@ -143,6 +146,9 @@ pub use mingling_macros::Grouped;
#[cfg(feature = "structural_renderer")]
pub use mingling_macros::StructuralData;
+/// Mingling's confirmation module, providing core support for Confirmer
+pub mod confirm;
+
#[doc = include_str!("docs/docsrs_examples.md")]
#[cfg(all(feature = "core", feature = "docs_rs"))]
#[allow(nonstandard_style)]
diff --git a/mingling/src/osc94.rs b/mingling/src/osc94.rs
new file mode 100644
index 0000000..7dabb4a
--- /dev/null
+++ b/mingling/src/osc94.rs
@@ -0,0 +1,5 @@
+mod guard;
+pub use guard::*;
+
+mod state;
+pub use state::*;
diff --git a/mingling/src/osc94/guard.rs b/mingling/src/osc94/guard.rs
new file mode 100644
index 0000000..d17f483
--- /dev/null
+++ b/mingling/src/osc94/guard.rs
@@ -0,0 +1,203 @@
+use crate::osc94::OSC94State;
+
+/// A guard for modifying process state.
+///
+/// Obtained via [`crate::res::ResOSC94::get_mut`]. When the guard is dropped, the process state is
+/// automatically restored to `OS94State::Clean`, so no manual cleanup is needed.
+///
+/// # Example
+///
+/// Create a guard via [`crate::res::ResOSC94`], and the state is automatically restored to Clean
+/// when the guard is dropped:
+///
+/// ```
+/// use mingling::res::ResOSC94;
+/// use mingling::osc94::OSC94Guard;
+///
+/// let osc94 = ResOSC94::default();
+/// {
+/// let mut guard: OSC94Guard = osc94.get_mut();
+/// guard.set_progress(0.5);
+/// // When leaving this scope, the guard is dropped and the process state is automatically restored to Clean
+/// }
+/// ```
+pub struct OSC94Guard {
+ pub(crate) is_support: bool,
+ pub(crate) msg: OSC94State,
+}
+
+impl OSC94Guard {
+ /// Set the process state to Clean.
+ ///
+ /// Indicates that the process has finished or is in a normal, problem-free state.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use mingling::res::ResOSC94;
+ /// use mingling::osc94::{OSC94Guard, OSC94State};
+ ///
+ /// let osc94 = ResOSC94::default();
+ /// let mut guard: OSC94Guard = osc94.get_mut();
+ /// guard.set_progress(0.5);
+ /// guard.set_clean_state();
+ /// assert_eq!(guard.state(), OSC94State::Clean);
+ /// ```
+ pub fn set_clean_state(&mut self) {
+ self.msg = OSC94State::Clean;
+ if self.is_support {
+ self.msg.send();
+ }
+ }
+
+ /// Set the process state to Error.
+ ///
+ /// Indicates that an error occurred during process execution.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use mingling::res::ResOSC94;
+ /// use mingling::osc94::{OSC94Guard, OSC94State};
+ ///
+ /// let osc94 = ResOSC94::default();
+ /// let mut guard: OSC94Guard = osc94.get_mut();
+ /// guard.set_error_state();
+ /// assert_eq!(guard.state(), OSC94State::Error);
+ /// ```
+ pub fn set_error_state(&mut self) {
+ self.msg = OSC94State::Error;
+ if self.is_support {
+ self.msg.send();
+ }
+ }
+
+ /// Set the process state to Warn.
+ ///
+ /// Indicates that a warning occurred during process execution, but it has not
+ /// reached the level of an error.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use mingling::res::ResOSC94;
+ /// use mingling::osc94::{OSC94Guard, OSC94State};
+ ///
+ /// let osc94 = ResOSC94::default();
+ /// let mut guard: OSC94Guard = osc94.get_mut();
+ /// guard.set_warn_state();
+ /// assert_eq!(guard.state(), OSC94State::Warn);
+ /// ```
+ pub fn set_warn_state(&mut self) {
+ self.msg = OSC94State::Warn;
+ if self.is_support {
+ self.msg.send();
+ }
+ }
+
+ /// Set the process state to Unknown.
+ ///
+ /// Indicates that the process state cannot be determined or has not been defined.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use mingling::res::ResOSC94;
+ /// use mingling::osc94::{OSC94Guard, OSC94State};
+ ///
+ /// let osc94 = ResOSC94::default();
+ /// let mut guard: OSC94Guard = osc94.get_mut();
+ /// guard.set_unknown_state();
+ /// assert_eq!(guard.state(), OSC94State::Unknown);
+ /// ```
+ pub fn set_unknown_state(&mut self) {
+ self.msg = OSC94State::Unknown;
+ if self.is_support {
+ self.msg.send();
+ }
+ }
+
+ /// Set the progress of the process.
+ ///
+ /// The `progress` parameter should be between `0.0` and `1.0`. `0.0` indicates
+ /// the start of the task, and `1.0` indicates the completion of the task.
+ /// Values outside this range are not clamped, but it is recommended to keep them
+ /// within this range.
+ ///
+ /// # Parameters
+ ///
+ /// * `progress` - The progress value, ranging from `0.0` to `1.0`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use mingling::res::ResOSC94;
+ /// use mingling::osc94::OSC94Guard;
+ ///
+ /// let osc94 = ResOSC94::default();
+ /// let mut guard: OSC94Guard = osc94.get_mut();
+ /// guard.set_progress(0.5);
+ /// assert_eq!(guard.progress(), 0.5);
+ /// ```
+ pub fn set_progress(&mut self, progress: f32) {
+ self.msg = OSC94State::Normal(progress);
+ if self.is_support {
+ self.msg.send();
+ }
+ }
+
+ /// Get the current process state.
+ ///
+ /// # Returns
+ ///
+ /// Returns the current [`OSC94State`] value, representing the state of the process.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use mingling::res::ResOSC94;
+ /// use mingling::osc94::{OSC94Guard, OSC94State};
+ ///
+ /// let osc94 = ResOSC94::default();
+ /// let guard: OSC94Guard = osc94.get_mut();
+ /// assert_eq!(guard.state(), OSC94State::Clean);
+ /// ```
+ #[must_use]
+ pub const fn state(&self) -> OSC94State {
+ self.msg
+ }
+
+ /// Get the current progress value.
+ ///
+ /// Returns the actual progress value only when the state is [`OSC94State::Normal`];
+ /// otherwise returns `0.0`.
+ ///
+ /// # Returns
+ ///
+ /// Returns an `f32` progress value, ranging from `0.0` to `1.0`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use mingling::res::ResOSC94;
+ /// use mingling::osc94::OSC94Guard;
+ ///
+ /// let osc94 = ResOSC94::default();
+ /// let mut guard: OSC94Guard = osc94.get_mut();
+ /// guard.set_progress(0.25);
+ /// assert_eq!(guard.progress(), 0.25);
+ /// ```
+ #[must_use]
+ pub const fn progress(&self) -> f32 {
+ match self.msg {
+ OSC94State::Normal(progress) => progress,
+ _ => 0.0,
+ }
+ }
+}
+
+impl Drop for OSC94Guard {
+ fn drop(&mut self) {
+ OSC94State::Clean.send();
+ }
+}
diff --git a/mingling/src/osc94/state.rs b/mingling/src/osc94/state.rs
new file mode 100644
index 0000000..1fff7ac
--- /dev/null
+++ b/mingling/src/osc94/state.rs
@@ -0,0 +1,243 @@
+/// `OSC 9;4` protocol message
+///
+/// Used to send task progress notification messages to the terminal via ANSI escape sequences.
+///
+/// This protocol follows the [Windows Terminal Progress Bar Sequences](https://learn.microsoft.com/en-us/windows/terminal/tutorials/progress-bar-sequences) specification.
+/// The status codes (0-4) represent: clear progress, normal, error, indeterminate, and warning states, respectively.
+///
+/// # Examples
+///
+/// ```rust
+/// use mingling::osc94::OSC94State;
+///
+/// // Set progress to 50%
+/// let state = OSC94State::Normal(0.5);
+/// assert_eq!(state.state_code(), 1);
+/// assert_eq!(state.progress(), 50.0);
+///
+/// // Generate escape sequence string
+/// let seq = state.to_escape_sequence();
+/// assert_eq!(seq, "\x1b]9;4;1;50\x07");
+///
+/// // Convert to string (Display implementation)
+/// let s = format!("{state}");
+/// assert_eq!(s, "\x1b]9;4;1;50\x07");
+///
+/// // Convert via From
+/// let s2: String = state.into();
+/// assert_eq!(s2, "\x1b]9;4;1;50\x07");
+///
+/// // Error state
+/// let err = OSC94State::Error;
+/// assert_eq!(err.state_code(), 2);
+/// ```
+///
+/// # Use Cases
+///
+/// In command-line tools or scripts, the [`OSC94State::send`] method can be used to directly send progress notifications to the terminal.
+/// Supported terminals include: `Windows Terminal`, `kitty`, `iTerm2`, `WezTerm`, `foot`, etc.
+///
+/// ```
+/// use mingling::osc94::OSC94State;
+///
+/// // Send progress 100%
+/// OSC94State::Normal(1.0).send();
+/// // Send completion (clear) message
+/// OSC94State::Clean.send();
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum OSC94State {
+ /// Clear/hide progress
+ Clean,
+ /// Normal state, corresponding to status code `1`, carries a progress value (0.0 to 1.0)
+ Normal(f32),
+ /// Error state, corresponding to status code `2`
+ Error,
+ /// Indeterminate state, corresponding to status code `3`
+ Unknown,
+ /// Warning state, corresponding to status code `4`
+ Warn,
+}
+
+impl OSC94State {
+ /// Returns the status code for the `OSC 9;4` protocol.
+ ///
+ /// Status code meanings:
+ /// - `0`: Clear progress (`Clean`)
+ /// - `1`: Normal state (`Normal`)
+ /// - `2`: Error state (`Error`)
+ /// - `3`: Indeterminate state (`Unknown`)
+ /// - `4`: Warning state (`Warn`)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling::osc94::OSC94State;
+ ///
+ /// assert_eq!(OSC94State::Clean.state_code(), 0);
+ /// assert_eq!(OSC94State::Normal(0.5).state_code(), 1);
+ /// assert_eq!(OSC94State::Error.state_code(), 2);
+ /// assert_eq!(OSC94State::Unknown.state_code(), 3);
+ /// assert_eq!(OSC94State::Warn.state_code(), 4);
+ /// ```
+ ///
+ /// # Return Value
+ ///
+ /// Returns the corresponding status code (`u8` type), ranging from `0` to `4`.
+ #[must_use]
+ pub const fn state_code(&self) -> u8 {
+ match self {
+ Self::Clean => 0,
+ Self::Normal(_) => 1,
+ Self::Error => 2,
+ Self::Unknown => 3,
+ Self::Warn => 4,
+ }
+ }
+
+ /// Returns the progress value (0-100), used for the `Normal` state, clamped to a valid range.
+ ///
+ /// This function converts the progress value (between 0.0 and 1.0) stored in the `Normal` variant
+ /// into a percentage (0 to 100) and rounds it. For non-`Normal` states (such as `Clean`, `Error`,
+ /// `Unknown`, `Warn`), it returns a fixed `0.0`, because only the `Normal` state carries progress information.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling::osc94::OSC94State;
+ ///
+ /// // Progress conversion in normal state
+ /// assert_eq!(OSC94State::Normal(0.5).progress(), 50.0);
+ /// assert_eq!(OSC94State::Normal(1.0).progress(), 100.0);
+ /// assert_eq!(OSC94State::Normal(0.0).progress(), 0.0);
+ ///
+ /// // Out-of-range values are clamped to 0-100
+ /// assert_eq!(OSC94State::Normal(1.5).progress(), 100.0);
+ /// assert_eq!(OSC94State::Normal(-0.5).progress(), 0.0);
+ ///
+ /// // Rounding behavior
+ /// assert_eq!(OSC94State::Normal(0.335).progress(), 34.0);
+ /// assert_eq!(OSC94State::Normal(0.999).progress(), 100.0);
+ ///
+ /// // Non-Normal states return 0.0
+ /// assert_eq!(OSC94State::Clean.progress(), 0.0);
+ /// assert_eq!(OSC94State::Error.progress(), 0.0);
+ /// assert_eq!(OSC94State::Unknown.progress(), 0.0);
+ /// assert_eq!(OSC94State::Warn.progress(), 0.0);
+ /// ```
+ ///
+ /// # Return Value
+ ///
+ /// Returns an `f32` progress percentage, ranging from `0.0` to `100.0` (inclusive).
+ /// For the `Normal` state, returns the rounded result of converting its progress value to a percentage;
+ /// for other states, always returns `0.0`.
+ #[must_use]
+ pub const fn progress(&self) -> f32 {
+ match self {
+ Self::Normal(progress) => (progress.clamp(0.0, 1.0) * 100.0).round(),
+ _ => 0.0,
+ }
+ }
+
+ /// Converts the message to the corresponding `OSC 9;4` escape sequence string.
+ ///
+ /// This method generates an ANSI escape sequence conforming to the
+ /// [Windows Terminal Progress Bar Sequences](https://learn.microsoft.com/en-us/windows/terminal/tutorials/progress-bar-sequences)
+ /// protocol based on the current state, in the format `\x1b]9;4;{status_code};{progress}\x07`.
+ ///
+ /// Escape sequence format description:
+ /// - `\x1b]`: ESC character followed by `]`, marking the start of an OSC (Operating System Command) sequence.
+ /// - `9;4`: Indicates the `OSC 9;4` protocol (task progress notification).
+ /// - `{status_code}`: Task status, ranging from `0` (clear), `1` (normal), `2` (error), `3` (indeterminate), to `4` (warning).
+ /// - `{progress}`: Task progress percentage (0-100), only meaningful for the `Normal` state.
+ /// - `\x07`: BEL character, marking the end of the OSC sequence.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling::osc94::OSC94State;
+ ///
+ /// // Clear progress
+ /// let clean = OSC94State::Clean;
+ /// assert_eq!(clean.to_escape_sequence(), "\x1b]9;4;0;0\x07");
+ ///
+ /// // Normal state, progress 50%
+ /// let normal = OSC94State::Normal(0.5);
+ /// assert_eq!(normal.to_escape_sequence(), "\x1b]9;4;1;50\x07");
+ ///
+ /// // Normal state, progress 100%
+ /// let complete = OSC94State::Normal(1.0);
+ /// assert_eq!(complete.to_escape_sequence(), "\x1b]9;4;1;100\x07");
+ ///
+ /// // Error state
+ /// let error = OSC94State::Error;
+ /// assert_eq!(error.to_escape_sequence(), "\x1b]9;4;2;0\x07");
+ ///
+ /// // Indeterminate state
+ /// let unknown = OSC94State::Unknown;
+ /// assert_eq!(unknown.to_escape_sequence(), "\x1b]9;4;3;0\x07");
+ ///
+ /// // Warning state
+ /// let warn = OSC94State::Warn;
+ /// assert_eq!(warn.to_escape_sequence(), "\x1b]9;4;4;0\x07");
+ /// ```
+ ///
+ /// # Return Value
+ ///
+ /// Returns a `String` containing an ANSI escape sequence conforming to the `OSC 9;4` protocol standard.
+ /// This string can be directly output to a terminal that supports this protocol (such as `Windows Terminal`,
+ /// `kitty`, `iTerm2`, `WezTerm`, `foot`, etc.) to display a task progress notification.
+ #[must_use]
+ pub fn to_escape_sequence(&self) -> String {
+ format!("\x1b]9;4;{};{}\x07", self.state_code(), self.progress())
+ }
+
+ /// Sends the OSC 9;4 message to the terminal via stdout.
+ ///
+ /// This method outputs the escape sequence of the current state to standard output and flushes the buffer,
+ /// allowing terminals that support the
+ /// [Windows Terminal Progress Bar Sequences](https://learn.microsoft.com/en-us/windows/terminal/tutorials/progress-bar-sequences)
+ /// protocol to display the corresponding task progress notification.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling::osc94::OSC94State;
+ ///
+ /// // Send normal state, progress 50%
+ /// OSC94State::Normal(0.5).send();
+ ///
+ /// // Send error state
+ /// OSC94State::Error.send();
+ ///
+ /// // Send clear progress message
+ /// OSC94State::Clean.send();
+ /// ```
+ ///
+ /// # Panics
+ ///
+ /// Panics if the stdout stream cannot be flushed.
+ pub fn send(&self) {
+ use std::io::Write;
+ print!("{}", self.to_escape_sequence());
+ std::io::stdout().flush().unwrap();
+ }
+}
+
+impl std::fmt::Display for OSC94State {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.to_escape_sequence())
+ }
+}
+
+impl From<OSC94State> for String {
+ fn from(msg: OSC94State) -> Self {
+ msg.to_escape_sequence()
+ }
+}
+
+impl From<&OSC94State> for String {
+ fn from(msg: &OSC94State) -> Self {
+ msg.to_escape_sequence()
+ }
+}
diff --git a/mingling/src/res.rs b/mingling/src/res.rs
index ab524cd..0314e39 100644
--- a/mingling/src/res.rs
+++ b/mingling/src/res.rs
@@ -7,5 +7,8 @@ pub use dirs::*;
mod exit_code;
pub use exit_code::*;
-mod confirmer;
-pub use confirmer::*;
+mod confirm;
+pub use confirm::*;
+
+mod osc94;
+pub use osc94::*;
diff --git a/mingling/src/res/confirm.rs b/mingling/src/res/confirm.rs
new file mode 100644
index 0000000..e07e9a2
--- /dev/null
+++ b/mingling/src/res/confirm.rs
@@ -0,0 +1,184 @@
+use std::io::{BufRead, Write};
+
+use crate::confirm::{ConfirmCount, ConfirmPredicate};
+
+/// A confirm for interactive confirmation.
+///
+/// This structure caches the confirmed state to avoid repeated prompts.
+///
+/// Typically, `ResConfirm` is registered via `ConfirmSetup`, and then injected into functions
+/// through Mingling's resource injection system.
+///
+/// # Registration
+///
+/// Before use, the `ConfirmSetup` must be registered with the program:
+///
+/// ```
+/// # use mingling::MockProgramCollect as ThisProgram;
+/// use mingling::setup::ConfirmSetup;
+/// use mingling::Program;
+///
+/// let mut program = Program::<ThisProgram>::new();
+/// program.with_setup(ConfirmSetup);
+/// ```
+///
+/// # Examples
+///
+/// ```
+/// use mingling::res::ResConfirm;
+/// use mingling::confirm::YesConfirm;
+///
+/// // In actual use, obtain the registered Confirm through the resource injection system
+/// let confirm = ResConfirm::new_confirmed();
+/// assert!(confirm.ask::<YesConfirm>("Continue? [y/n] "));
+/// ```
+#[derive(Debug, Default, Clone, Copy)]
+pub struct ResConfirm {
+ pub(crate) confirmed: bool,
+}
+
+impl ResConfirm {
+ /// Creates a new `ResConfirm` instance.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling::res::ResConfirm;
+ ///
+ /// let confirm = ResConfirm::new();
+ /// ```
+ #[must_use]
+ pub const fn new() -> Self {
+ Self { confirmed: false }
+ }
+
+ /// Creates a `Confirm` instance in the confirmed state.
+ ///
+ /// The returned `Confirm` will directly return `true` when calling [`ask`](ResConfirm::ask) or
+ /// [`try_ask`](ResConfirm::try_ask), without prompting the user.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling::res::ResConfirm;
+ /// use mingling::confirm::YesConfirm;
+ ///
+ /// let confirm = ResConfirm::new_confirmed();
+ /// assert!(confirm.ask::<YesConfirm>("Continue? [y/n] "));
+ /// ```
+ #[must_use]
+ pub const fn new_confirmed() -> Self {
+ Self { confirmed: true }
+ }
+
+ /// Marks the Confirm as confirmed.
+ ///
+ /// After calling this method, subsequent calls to [`ask`](ResConfirm::ask) or
+ /// [`try_ask`](ResConfirm::try_ask) on this Confirm will directly return `true`
+ /// without prompting the user.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling::res::ResConfirm;
+ /// use mingling::confirm::YesConfirm;
+ ///
+ /// let mut confirm = ResConfirm::new();
+ /// confirm.set_confirmed();
+ /// assert!(confirm.ask::<YesConfirm>("Continue? [y/n] "));
+ /// ```
+ pub const fn set_confirmed(&mut self) {
+ self.confirmed = true;
+ }
+
+ /// Asks the user a confirmation question, with at most one attempt.
+ ///
+ /// Returns `false` if the user provides an unrecognizable answer.
+ /// Returns `true` directly if already confirmed previously.
+ ///
+ /// # Parameters
+ ///
+ /// * `ask` - The prompt text to display to the user.
+ ///
+ /// # Returns
+ ///
+ /// Returns a boolean indicating whether the user confirmed. Returns `false` if the user's input
+ /// could not be parsed or the maximum number of attempts was reached.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling::res::ResConfirm;
+ /// use mingling::confirm::YesConfirm;
+ ///
+ /// let confirm = ResConfirm::new_confirmed();
+ /// let confirmed = confirm.ask::<YesConfirm>("Delete this file? [y/n] ");
+ /// ```
+ pub fn ask<P: ConfirmPredicate>(&self, ask: impl AsRef<str>) -> bool {
+ self.try_ask::<P>(ask, ConfirmCount::Max(1))
+ .unwrap_or(false)
+ }
+
+ /// Asks the user a confirmation question, allowing a specified maximum number of attempts.
+ ///
+ /// # Parameters
+ ///
+ /// * `ask` - The prompt text to display to the user.
+ /// * `count` - The maximum number of attempts. Passing `0` means unlimited attempts (loop
+ /// indefinitely), passing a positive integer means at most that many attempts.
+ ///
+ /// # Returns
+ ///
+ /// Returns `Some(true)` for confirmation, `Some(false)` for rejection.
+ /// Returns `None` if the maximum number of attempts is reached without being able to parse
+ /// the user's input.
+ ///
+ /// # Panics
+ ///
+ /// This function panics when the standard error output (`stderr`) cannot be flushed or when
+ /// reading from standard input fails.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling::res::ResConfirm;
+ /// use mingling::confirm::YesConfirm;
+ ///
+ /// let confirm = ResConfirm::new_confirmed();
+ /// let confirmed = confirm.try_ask::<YesConfirm>("Confirm execution? [y/n] ", 3);
+ /// ```
+ pub fn try_ask<P: ConfirmPredicate>(
+ &self,
+ ask: impl AsRef<str>,
+ count: impl Into<ConfirmCount>,
+ ) -> Option<bool> {
+ if self.confirmed {
+ return Some(true);
+ }
+
+ let count = count.into();
+ let mut attempts = 0usize;
+
+ loop {
+ eprint!("{}", ask.as_ref());
+ std::io::stderr().flush().unwrap();
+
+ let stdin = std::io::stdin();
+ let mut input = String::new();
+ stdin.lock().read_line(&mut input).unwrap();
+ if let Some(result) = P::is_yes(&input) {
+ return Some(result);
+ }
+
+ attempts += 1;
+ match count {
+ ConfirmCount::Loop => {}
+ ConfirmCount::Max(max) => {
+ if attempts >= max {
+ return None;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/mingling/src/res/confirmer.rs b/mingling/src/res/confirmer.rs
deleted file mode 100644
index 900562b..0000000
--- a/mingling/src/res/confirmer.rs
+++ /dev/null
@@ -1,268 +0,0 @@
-use std::io::{BufRead, Write};
-
-/// A confirmer for interactive confirmation.
-///
-/// This structure caches the confirmed state to avoid repeated prompts.
-///
-/// Typically, `Confirmer` is registered via [`ConfirmerSetup`], and then injected into functions
-/// through Mingling's resource injection system.
-///
-/// # Registration
-///
-/// Before use, the [`ConfirmerSetup`] must be registered with the program:
-///
-/// ```
-/// # use mingling::MockProgramCollect as ThisProgram;
-/// use mingling::setup::ConfirmerSetup;
-/// use mingling::Program;
-///
-/// let mut program = Program::<ThisProgram>::new();
-/// program.with_setup(ConfirmerSetup);
-/// ```
-///
-/// # Examples
-///
-/// ```
-/// use mingling::res::{Confirmer, YesConfirm};
-///
-/// // In actual use, obtain the registered confirmer through the resource injection system
-/// let confirmer = Confirmer::new_confirmed();
-/// assert!(confirmer.ask::<YesConfirm>("Continue? [y/n] "));
-/// ```
-#[derive(Debug, Default, Clone, Copy)]
-pub struct Confirmer {
- pub(crate) confirmed: bool,
-}
-
-impl Confirmer {
- /// Creates a new `Confirmer` instance.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling::res::Confirmer;
- ///
- /// let confirmer = Confirmer::new();
- /// ```
- #[must_use]
- pub const fn new() -> Self {
- Self { confirmed: false }
- }
-
- /// Creates a `Confirmer` instance in the confirmed state.
- ///
- /// The returned `Confirmer` will directly return `true` when calling [`ask`](Confirmer::ask) or
- /// [`try_ask`](Confirmer::try_ask), without prompting the user.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling::res::{Confirmer, YesConfirm};
- ///
- /// let confirmer = Confirmer::new_confirmed();
- /// assert!(confirmer.ask::<YesConfirm>("Continue? [y/n] "));
- /// ```
- #[must_use]
- pub const fn new_confirmed() -> Self {
- Self { confirmed: true }
- }
-
- /// Marks the confirmer as confirmed.
- ///
- /// After calling this method, subsequent calls to [`ask`](Confirmer::ask) or
- /// [`try_ask`](Confirmer::try_ask) on this confirmer will directly return `true`
- /// without prompting the user.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling::res::{Confirmer, YesConfirm};
- ///
- /// let mut confirmer = Confirmer::new();
- /// confirmer.set_confirmed();
- /// assert!(confirmer.ask::<YesConfirm>("Continue? [y/n] "));
- /// ```
- pub const fn set_confirmed(&mut self) {
- self.confirmed = true;
- }
-
- /// Asks the user a confirmation question, with at most one attempt.
- ///
- /// Returns `false` if the user provides an unrecognizable answer.
- /// Returns `true` directly if already confirmed previously.
- ///
- /// # Parameters
- ///
- /// * `ask` - The prompt text to display to the user.
- ///
- /// # Returns
- ///
- /// Returns a boolean indicating whether the user confirmed. Returns `false` if the user's input
- /// could not be parsed or the maximum number of attempts was reached.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling::res::{Confirmer, YesConfirm};
- ///
- /// let confirmer = Confirmer::new_confirmed();
- /// let confirmed = confirmer.ask::<YesConfirm>("Delete this file? [y/n] ");
- /// ```
- pub fn ask<P: ConfirmerPredicate>(&self, ask: impl AsRef<str>) -> bool {
- self.try_ask::<P>(ask, ConfirmerCount::Max(1))
- .unwrap_or(false)
- }
-
- /// Asks the user a confirmation question, allowing a specified maximum number of attempts.
- ///
- /// # Parameters
- ///
- /// * `ask` - The prompt text to display to the user.
- /// * `count` - The maximum number of attempts. Passing `0` means unlimited attempts (loop
- /// indefinitely), passing a positive integer means at most that many attempts.
- ///
- /// # Returns
- ///
- /// Returns `Some(true)` for confirmation, `Some(false)` for rejection.
- /// Returns `None` if the maximum number of attempts is reached without being able to parse
- /// the user's input.
- ///
- /// # Panics
- ///
- /// This function panics when the standard error output (`stderr`) cannot be flushed or when
- /// reading from standard input fails.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling::res::{Confirmer, YesConfirm};
- ///
- /// let confirmer = Confirmer::new_confirmed();
- /// let confirmed = confirmer.try_ask::<YesConfirm>("Confirm execution? [y/n] ", 3);
- /// ```
- pub fn try_ask<P: ConfirmerPredicate>(
- &self,
- ask: impl AsRef<str>,
- count: impl Into<ConfirmerCount>,
- ) -> Option<bool> {
- if self.confirmed {
- return Some(true);
- }
-
- let count = count.into();
- let mut attempts = 0usize;
-
- loop {
- eprint!("{}", ask.as_ref());
- std::io::stderr().flush().unwrap();
-
- let stdin = std::io::stdin();
- let mut input = String::new();
- stdin.lock().read_line(&mut input).unwrap();
- if let Some(result) = P::is_yes(&input) {
- return Some(result);
- }
-
- attempts += 1;
- match count {
- ConfirmerCount::Loop => {}
- ConfirmerCount::Max(max) => {
- if attempts >= max {
- return None;
- }
- }
- }
- }
- }
-}
-
-/// Specifies the maximum number of attempts for a confirmation prompt.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum ConfirmerCount {
- /// Loop indefinitely until the user gives a parseable answer.
- Loop,
- /// Ask at most the specified number of times.
- Max(usize),
-}
-
-macro_rules! impl_from_for_confirmer_count {
- ($($t:ty),*) => {
- $(
- impl From<$t> for ConfirmerCount {
- fn from(n: $t) -> Self {
- if n == 0 {
- ConfirmerCount::Loop
- } else {
- match usize::try_from(n) {
- Ok(max) => ConfirmerCount::Max(max),
- Err(_) => ConfirmerCount::Max(usize::MAX),
- }
- }
- }
- }
- )*
- };
-}
-
-impl_from_for_confirmer_count!(
- i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
-);
-
-/// Defines how to parse user confirmation input.
-///
-/// A type implementing this trait determines which user input strings are treated as "yes" or "no".
-pub trait ConfirmerPredicate {
- /// Parses the user's input string, returning whether it is "yes".
- ///
- /// Returns `Some(true)` for yes, `Some(false)` for no,
- /// and `None` if the input cannot be parsed (requiring re-entry).
- fn is_yes(str: &str) -> Option<bool>;
-}
-
-/// A `ConfirmerPredicate` implementation that accepts "y"/"yes" as yes and "n"/"no" as no.
-///
-/// Input comparison is case-insensitive and automatically trims leading/trailing whitespace.
-///
-/// # Examples
-///
-/// ```
-/// use mingling::res::{Confirmer, YesConfirm};
-///
-/// let confirmer = Confirmer::default();
-/// let confirmed = confirmer.ask::<YesConfirm>("Continue? [y/n] ");
-/// ```
-pub struct YesConfirm;
-
-/// A `ConfirmerPredicate` implementation that accepts "true"/"t" as yes and "false"/"f" as no.
-///
-/// Input comparison is case-insensitive and automatically trims leading/trailing whitespace.
-///
-/// # Examples
-///
-/// ```
-/// use mingling::res::{Confirmer, TrueConfirm};
-///
-/// let confirmer = Confirmer::default();
-/// let confirmed = confirmer.ask::<TrueConfirm>("Enable this feature? [true/false] ");
-/// ```
-pub struct TrueConfirm;
-
-impl ConfirmerPredicate for YesConfirm {
- fn is_yes(str: &str) -> Option<bool> {
- match str.trim().to_lowercase().as_str() {
- "y" | "yes" => Some(true),
- "n" | "no" => Some(false),
- _ => None,
- }
- }
-}
-
-impl ConfirmerPredicate for TrueConfirm {
- fn is_yes(str: &str) -> Option<bool> {
- match str.trim().to_lowercase().as_str() {
- "true" | "t" => Some(true),
- "false" | "f" => Some(false),
- _ => None,
- }
- }
-}
diff --git a/mingling/src/res/osc94.rs b/mingling/src/res/osc94.rs
new file mode 100644
index 0000000..ea8538d
--- /dev/null
+++ b/mingling/src/res/osc94.rs
@@ -0,0 +1,69 @@
+use crate::osc94::{OSC94Guard, OSC94State};
+
+/// Process `OSC 9;4` status.
+///
+/// Provides support for the `OSC 9;4` protocol. You can inject it into the execution flow
+/// through Mingling's resource injection system, and use it to control your process state.
+///
+/// Typically, `ResOSC94` is registered via `OSC94Setup`, and then injected into functions
+/// through Mingling's resource injection system.
+///
+/// # Registration
+///
+/// Before use, the `OSC94Setup` must be registered with the program:
+///
+/// ```
+/// # use mingling::MockProgramCollect as ThisProgram;
+/// use mingling::setup::OSC94Setup;
+/// use mingling::Program;
+///
+/// let mut program = Program::<ThisProgram>::new();
+/// program.with_setup(OSC94Setup);
+/// ```
+///
+/// # Example
+///
+/// ```
+/// use mingling::res::ResOSC94;
+/// use mingling::osc94::OSC94State;
+///
+/// let osc94 = ResOSC94::default();
+/// let mut guard = osc94.get_mut();
+///
+/// guard.set_progress(0.5);
+/// assert_eq!(guard.state(), OSC94State::Normal(0.5));
+/// ```
+#[derive(Debug, Default, Clone, Copy)]
+pub struct ResOSC94 {
+ pub(crate) is_support: bool,
+}
+
+impl ResOSC94 {
+ /// Get a guard for modifying progress.
+ ///
+ /// The returned [`OSC94Guard`] allows you to set the process state and progress.
+ /// If the current environment supports the `OSC 9;4` protocol, state changes will
+ /// be sent to the terminal in real time.
+ ///
+ /// # Returns
+ ///
+ /// Returns an [`OSC94Guard`] with an initial state of [`OSC94State::Clean`].
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use mingling::res::ResOSC94;
+ /// use mingling::osc94::OSC94State;
+ ///
+ /// let osc94 = ResOSC94::default();
+ /// let guard = osc94.get_mut();
+ /// assert_eq!(guard.state(), OSC94State::Clean);
+ /// ```
+ #[must_use]
+ pub const fn get_mut(&self) -> OSC94Guard {
+ OSC94Guard {
+ is_support: self.is_support,
+ msg: OSC94State::Clean,
+ }
+ }
+}
diff --git a/mingling/src/setups.rs b/mingling/src/setups.rs
index b2af90c..e1835e7 100644
--- a/mingling/src/setups.rs
+++ b/mingling/src/setups.rs
@@ -8,8 +8,8 @@ pub mod picker;
mod basic;
pub use basic::*;
-mod confirmer;
-pub use confirmer::*;
+mod confirm;
+pub use confirm::*;
mod dirs;
pub use dirs::*;
@@ -17,6 +17,9 @@ pub use dirs::*;
mod exit_code;
pub use exit_code::*;
+mod osc94;
+pub use osc94::*;
+
#[cfg(feature = "repl")]
mod repl_basic;
#[cfg(feature = "repl")]
diff --git a/mingling/src/setups/confirmer.rs b/mingling/src/setups/confirm.rs
index 1824745..46ead36 100644
--- a/mingling/src/setups/confirmer.rs
+++ b/mingling/src/setups/confirm.rs
@@ -2,12 +2,12 @@ use mingling_core::{
Program, ProgramCollect, config, hook::ProgramHook, setup::ProgramSetup, this,
};
-use crate::res::Confirmer;
+use crate::res::ResConfirm;
-/// Confirmer setup for managing confirmation state
+/// Confirm setup for managing confirmation state
///
/// This Setup manages the confirmation flag within the program's resource
-/// store. It registers a [`Confirmer`] resource and sets up a hook that
+/// store. It registers a [`ResConfirm`] resource and sets up a hook that
/// checks the user's confirmation mode during program execution.
///
/// # Usage
@@ -19,18 +19,18 @@ use crate::res::Confirmer;
/// ```rust
/// # use mingling::MockProgramCollect as ThisProgram;
/// use mingling::Program;
-/// use mingling::setup::ConfirmerSetup;
+/// use mingling::setup::ConfirmSetup;
///
/// let mut program = Program::<ThisProgram>::new();
-/// program.with_setup(ConfirmerSetup);
+/// program.with_setup(ConfirmSetup);
/// ```
///
/// # Behavior
///
-/// - Registers a [`Confirmer`] resource that tracks confirmation state.
+/// - Registers a [`ResConfirm`] resource that tracks confirmation state.
/// - At the beginning of command execution, checks whether the user's
/// confirmation mode is set to `Skip`.
-/// - If confirmation is skipped, the [`Confirmer`] resource is updated
+/// - If confirmation is skipped, the [`ResConfirm`] resource is updated
/// to record the confirmed state.
///
/// # Notes
@@ -38,20 +38,20 @@ use crate::res::Confirmer;
/// - This Setup applies uniformly to all subcommands of the entire program.
/// - The confirmation state is determined by the global `config` setting;
/// it does not support per-command overrides.
-pub struct ConfirmerSetup;
+pub struct ConfirmSetup;
-impl<C> ProgramSetup<C> for ConfirmerSetup
+impl<C> ProgramSetup<C> for ConfirmSetup
where
C: ProgramCollect<Enum = C> + 'static,
{
fn setup(self, program: &mut Program<C>) {
- program.with_resource(Confirmer::new());
+ program.with_resource(ResConfirm::new());
program.with_hook(ProgramHook::empty().on_pre_dispatch::<_, ()>(|_| {
let p = this::<C>();
let confirmed = p.user_context.confirmation == config::ConfirmationMode::Skip;
if confirmed {
- p.modify_res(|c: &mut Confirmer| {
+ p.modify_res(|c: &mut ResConfirm| {
c.set_confirmed();
});
}
diff --git a/mingling/src/setups/osc94.rs b/mingling/src/setups/osc94.rs
new file mode 100644
index 0000000..b08312f
--- /dev/null
+++ b/mingling/src/setups/osc94.rs
@@ -0,0 +1,89 @@
+use mingling_core::{Program, ProgramCollect, setup::ProgramSetup};
+
+use crate::res::ResOSC94;
+
+/// `OSC 9;4` Setup for managing terminal progress notification state
+///
+/// This Setup manages the terminal's `OSC 9;4` protocol support state within the
+/// program's resource store. It registers an [`ResOSC94`] resource that tracks whether
+/// the current terminal supports the protocol, and provides a helper resource that
+/// can be used to send progress notification messages.
+///
+/// # Usage
+///
+/// This Setup can be registered using the
+/// [`Program`](https://docs.rs/mingling/latest/mingling/struct.Program.html)
+/// `with_setup` method, for example:
+///
+/// ```rust
+/// # use mingling::MockProgramCollect as ThisProgram;
+/// use mingling::Program;
+/// use mingling::setup::OSC94Setup;
+///
+/// let mut program = Program::<ThisProgram>::new();
+/// program.with_setup(OSC94Setup);
+/// ```
+///
+/// # Behavior
+///
+/// - Registers an [`ResOSC94`] resource that tracks whether the current terminal
+/// supports the `OSC 9;4` protocol.
+/// - The support check inspects various environment variables such as `TERM_PROGRAM`,
+/// `WT_SESSION`, `VTE_VERSION`, and `TERM`.
+///
+/// # Notes
+///
+/// - The support state is determined at setup time and stored in the resource store.
+/// - Use `OSC94Message` to construct and send progress notification messages.
+pub struct OSC94Setup;
+
+impl<C> ProgramSetup<C> for OSC94Setup
+where
+ C: ProgramCollect<Enum = C> + 'static,
+{
+ fn setup(self, program: &mut Program<C>) {
+ program.with_resource(ResOSC94 {
+ is_support: is_support_osc94(),
+ });
+ }
+}
+
+/// Check whether the current terminal environment supports the `OSC 9;4` protocol
+///
+/// This function inspects various environment variables to determine whether the
+/// current terminal supports Microsoft's
+/// [OSC 9;4 protocol](https://learn.microsoft.com/en-us/windows/terminal/tutorials/progress-bar-sequences),
+/// which allows sending task progress notifications via ANSI escape sequences.
+///
+/// Supported terminal environments include:
+/// - **`TERM_PROGRAM`**: `ghostty`, `WezTerm`, `iTerm.app`
+/// - **`WT_SESSION`**: Windows Terminal
+/// - **`VTE_VERSION`**: VTE-based terminals (such as GNOME Terminal, Konsole, etc.)
+/// - **`TERM`**: terminal emulators containing `xterm`
+///
+/// Returns `true` if the current terminal supports the `OSC 9;4` protocol, so that
+/// progress notification escape sequences can be safely sent.
+fn is_support_osc94() -> bool {
+ if let Ok(program) = std::env::var("TERM_PROGRAM") {
+ match program.as_str() {
+ "ghostty" | "WezTerm" | "iTerm.app" => return true,
+ _ => {}
+ }
+ }
+
+ if std::env::var("WT_SESSION").is_ok() {
+ return true;
+ }
+
+ if std::env::var("VTE_VERSION").is_ok() {
+ return true;
+ }
+
+ if let Ok(term) = std::env::var("TERM")
+ && term.contains("xterm")
+ {
+ return true;
+ }
+
+ false
+}