liso/
lib.rs

1//! Liso (LEE-soh) is an acronym for Line Input with Simultaneous Output. It is
2//! a library for a particular kind of text-based Rust application; one where
3//! the user is expected to give command input at a prompt, but output can
4//! occur at any time. It provides simple line editing, and prevents input from
5//! clashing with output. It can be used asynchronously (with `tokio`) or
6//! synchronously (without).
7//!
8//! # Usage
9//!
10//! Create an [`InputOutput`](struct.InputOutput.html) object with
11//! `InputOutput::new()`. Liso will automatically configure itself based on how
12//! your program is being used.
13//!
14//! Your `InputOutput` instance can be used to send output or receive input.
15//! Call `clone_output` to create an [`OutputOnly`](struct.OutputOnly.html)
16//! instance, which can only be used to send output. You can call
17//! `clone_output` as many times as you like, as well as cloning the
18//! `OutputOnly`s directly. An unlimited number of threads or tasks can send
19//! output through Liso, but only one thread/task can receive user input:
20//! whichever one currently holds the `InputOutput` instance.
21//!
22//! If the `global` feature is enabled, which it is by default, then you
23//! don't *have* to create `OutputOnly` instances and keep them around in order
24//! to send output. See [the "Global" section](#global) for more information.
25//!
26//! Liso can work with `String`s and `&str`s directly. If you want to add style
27//! or color, create a [`Line`](struct.Line.html), either manually or using
28//! the convenient [`liso!` macro](macro.liso.html). Send output to the
29//! user by calling [`println()`](struct.Output.html#method.println) or
30//! [`wrapln()`](struct.Output.html#method.wrapln), whichever you prefer. Any
31//! styling and color information is reset after the line is output, so you
32//! don't have to worry about dangling attributes.
33//!
34//! Liso supports a prompt line, which is presented ahead of the user input.
35//! Use [`prompt()`](struct.Output.html#method.prompt) to set it. Styling and
36//! color information is *not* reset between the prompt and the current input
37//! text, so you can style/color the input text by having the desired
38//! styles/colors active at the end of the prompt line.
39//!
40//! Liso supports an optional status line, which "hangs out" above the input
41//! text. Use [`status()`](struct.Output.html#method.status) to set it. Printed
42//! text appears above the status line, the prompt and any in-progress input
43//! appears below it. Use this to present contextual or frequently-changing
44//! information.
45//!
46//! Liso supports "notices", temporary messages that appear in place of the
47//! prompt and input for a limited time. Use
48//! [`notice()`](struct.Output.html#method.notice) to display one. The notice
49//! will disappear when the allotted time elapses, when the user presses any
50//! key, or when another notice is displayed, whichever happens first. You
51//! should only use this in direct response to user input; in fact, the only
52//! legitimate use may be to complain about an unknown control character. (See
53//! [`Response`](enum.Response.html) for an example of this use.)
54//!
55//! # Global
56//!
57//! If the `global` feature is enabled (which it is by default), you can call
58//! [`output()`](fn.output.html) to get a valid `OutputOnly` instance any time
59//! that an `InputOutput` instance is alive. This will panic if there is *not*
60//! an `InputOutput` instance alive, so you'll still have to have one.
61//!
62//! With `global` enabled, you can also use the
63//! [`println!`](macro.println.html) or [`wrapln!`](macro.wrapln.html) macros
64//! to perform output directly and conveniently. `println!(...)` is equivalent
65//! to `output().println!(liso!(...))`.
66//!
67//! Using the `output()` function, or the `println!`/`wrapln!` macros, is
68//! noticeably less efficient than creating an `OutputOnly` instance ahead of
69//! time, whether by calling `clone_output()` or by calling `output()` and
70//! caching the result. But, it's probably okay as long as you're not hoping to
71//! do it hundreds of thousands of times per second.
72//!
73//! # History
74//!
75//! If the `history` feature is enabled (which it is by default), Liso supports
76//! a rudimentary command history. It provides a conservative default that
77//! isn't backed by any file. Try:
78//!
79//! ```rust
80//! # let io = liso::InputOutput::new();
81//! # let some_path = "DefinitelyDoesNotExist";
82//! # #[cfg(feature="history")]
83//! io.swap_history(liso::History::from_file(some_path).unwrap());
84//! ```
85//!
86//! to make it backed by a file, and see [`History`](struct.History.html) for
87//! more information.
88//!
89//! # Completion
90//!
91//! If the `completion` feature is enabled (which it is by default), Liso
92//! supports tab completion. Implement [`Completor`](trait.Completor.html),
93//! then use [`set_completor`](struct.Output.html#method.set_completor) to make
94//! your new completor active. See the linked documentation for more
95//! information.
96//!
97//! # Pipe mode
98//!
99//! If *either* stdin or stdout is not a tty, *or* the `TERM` environment
100//! variable is set to either `dumb` or `pipe`, Liso enters "pipe mode". In
101//! this mode, status lines, notices, and prompts are not outputted, style
102//! information is discarded, and every line of input is passed directly to
103//! your program without any processing of control characters or escape
104//! sequences. This means that a program using Liso will behave nicely when
105//! used in a pipeline, or with a relatively unsophisticated terminal.
106//!
107//! `TERM=dumb` is respected out of backwards compatibility with old UNIXes and
108//! real terminals that identify this way. `TERM=pipe` is present as an
109//! alternative for those who would rather not perpetuate an ableist slur, but
110//! is not compatible with other UNIX utilities and conventions. On UNIX. you
111//! can activate "pipe mode" without running afoul of any of this by piping the
112//! output of the Liso-enabled program to `cat`, as in `my_liso_program | cat`.
113
114use std::{
115    any::Any,
116    borrow::Cow,
117    str::FromStr,
118    sync::mpsc as std_mpsc,
119    time::{Duration, Instant},
120};
121
122#[cfg(not(feature = "global"))]
123use std::sync::atomic::{AtomicBool, Ordering};
124
125#[cfg(feature = "history")]
126use std::sync::{Arc, RwLock, RwLockReadGuard};
127
128#[cfg(feature = "completion")]
129use std::num::NonZeroU32;
130
131use bitflags::bitflags;
132use crossterm::event::Event;
133use crossterm::style::{
134    Attribute as CtAttribute, Attributes as CtAttributes, Color as CtColor,
135};
136use tokio::sync::mpsc as tokio_mpsc;
137
138mod line;
139pub use line::*;
140mod term;
141mod worker;
142use term::*;
143#[cfg(unix)]
144mod unix_util;
145
146#[cfg(feature = "history")]
147mod history;
148#[cfg(feature = "history")]
149pub use history::*;
150
151#[cfg(feature = "completion")]
152mod completion;
153#[cfg(feature = "completion")]
154pub use completion::*;
155
156#[cfg(feature = "capture-stderr")]
157mod stderr_capture;
158
159/// When handling input ourselves, this is the amount of time to wait after
160/// receiving an escape before we're sure we don't have an escape sequence on
161/// our hands.
162///
163/// This is fairly long to ensure that, even on a 300 baud modem, we would
164/// *definitely* have received another character in the sequence before this
165/// deadline elapses. (I say that it's fairly long, but curses waits an entire
166/// **second**, which is much, much, much too long!)
167///
168/// If Crossterm input is being used, this is ignored.
169const ESCAPE_DELAY: Duration = Duration::new(0, 1000000000 / 24);
170
171/// We have to handle errors. There are two kinds we'll routinely face:
172///
173/// - Error writing to `Stdout`
174/// - Error sending out a `Response`
175///
176/// The correct answer to both is to quietly, calmly, close down our thread. We
177/// abuse the `?` operator to make this quick and easy. Since we don't actually
178/// need any of the error information, we can condense it all down into this,
179/// the "an error happened and we don't care what" type.
180struct DummyError {}
181type LifeOrDeath = std::result::Result<(), DummyError>;
182impl From<std::io::Error> for DummyError {
183    fn from(_: std::io::Error) -> DummyError {
184        DummyError {}
185    }
186}
187impl<T> From<tokio_mpsc::error::SendError<T>> for DummyError {
188    fn from(_: tokio_mpsc::error::SendError<T>) -> DummyError {
189        DummyError {}
190    }
191}
192impl<T> From<std_mpsc::SendError<T>> for DummyError {
193    fn from(_: std_mpsc::SendError<T>) -> DummyError {
194        DummyError {}
195    }
196}
197impl From<std_mpsc::RecvError> for DummyError {
198    fn from(_: std_mpsc::RecvError) -> DummyError {
199        DummyError {}
200    }
201}
202impl From<std_mpsc::RecvTimeoutError> for DummyError {
203    fn from(_: std_mpsc::RecvTimeoutError) -> DummyError {
204        DummyError {}
205    }
206}
207
208/// Colors we support outputting. For compatibility, we only support the 3-bit
209/// ANSI colors.
210///
211/// Here's a short list of reasons not to use color as the only source of
212/// certain information:
213///
214/// - Some terminals don't support color at all.
215/// - Some terminals support color, but not all the ANSI colors. (e.g. the
216///   Atari ST's VT52 emulator in medium-res mode, which supports white, black,
217///   red, green, and none of the other colors.)
218/// - Some users will be using unexpected themes. White on black, black on
219///   white, green on black, yellow on orange, and "Solarized" are all common.
220/// - Many users have some form of colorblindness. The most common form,
221///   affecting as much as 8% of the population, would make `Red`, `Yellow`,
222///   and `Green` hard to distinguish from one another. Every other imaginable
223///   variation also exists.
224///
225/// And some guidelines to adhere to:
226///
227/// - Respect the [set the `NO_COLOR` environment variable][1], if it is set.
228/// - Never assume you know what color `None` is. It could be white, black, or
229///   something entirely unexpected.
230/// - Never specify a foreground color of `White` or `Black` without also
231///   specifying a background color, or vice versa.
232/// - Never specify the same color for both foreground and background at the
233///   same time.
234/// - Instead of setting white-on-black or black-on-white, consider using
235///   [inverse video](struct.Style.html#associatedconstant.INVERSE) to achieve
236///   your goal instead.
237///
238/// [1]: http://no-color.org/
239#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
240#[derive(Clone, Copy, Debug, Eq, PartialEq)]
241#[repr(u8)]
242pub enum Color {
243    /// Absence of light. The color of space. (Some terminals will render this
244    /// as a dark gray instead.)
245    Black = 0,
246    /// The color of blood, danger, and rage.
247    Red = 1,
248    /// The color of plants, safety, and circadian stasis.
249    Green = 2,
250    /// The color of all the worst chemicals.
251    Yellow = 3,
252    /// The color of a calm ocean.
253    Blue = 4,
254    /// The color of a clear sky.
255    Cyan = 5,
256    /// A color that occurs rarely in nature, but often in screenshots of GEM.
257    Magenta = 6,
258    /// A (roughly) equal mix of all wavelengths of light.
259    White = 7,
260}
261
262impl Color {
263    // Convert to the equivalent Crossterm color.
264    fn to_crossterm(self) -> CtColor {
265        match self {
266            Color::Black => CtColor::Black,
267            Color::Red => CtColor::DarkRed,
268            Color::Green => CtColor::DarkGreen,
269            Color::Yellow => CtColor::DarkYellow,
270            Color::Blue => CtColor::DarkBlue,
271            Color::Cyan => CtColor::DarkCyan,
272            Color::Magenta => CtColor::DarkMagenta,
273            Color::White => CtColor::Grey,
274        }
275    }
276    // Convert to an Atari ST 16-color palette index (bright).
277    fn to_atari16_bright(self) -> u8 {
278        match self {
279            Color::Black => 8,
280            Color::Red => 1,
281            Color::Green => 2,
282            Color::Yellow => 13,
283            Color::Blue => 4,
284            Color::Cyan => 9,
285            Color::Magenta => 12,
286            Color::White => 0,
287        }
288    }
289    // Convert to an Atari ST 16-color palette index (dim).
290    fn to_atari16_dim(self) -> u8 {
291        match self {
292            Color::Black => 15,
293            Color::Red => 3,
294            Color::Green => 5,
295            Color::Yellow => 11,
296            Color::Blue => 6,
297            Color::Cyan => 10,
298            Color::Magenta => 14,
299            Color::White => 7,
300        }
301    }
302    // Convert to the nearest Atari ST 4-color palette index.
303    fn to_atari4(self) -> u8 {
304        match self {
305            Color::Black => 15,
306            Color::Red => 1,
307            Color::Green => 2,
308            Color::Yellow => 2,
309            Color::Blue => 3,
310            Color::Cyan => 2,
311            Color::Magenta => 1,
312            Color::White => 0,
313        }
314    }
315}
316
317bitflags! {
318    /// Styles we support outputting.
319    ///
320    /// Some terminals don't support any of this, and some don't support all of
321    /// it. On any standards-compliant terminal, unsupported features will be
322    /// ignored. Even on standards-compliant terminals, these are very open to
323    /// interpretation.
324    #[cfg_attr(feature="serde", derive(serde::Serialize, serde::Deserialize))]
325    #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
326    pub struct Style: u32 {
327        /// No styling at all. (A nice alias for `Style::empty()`.)
328        const PLAIN = 0;
329        /// Prints in a bolder font and/or a brighter color.
330        const BOLD = 1 << 0;
331        /// Prints in a thinner font and/or a dimmer color.
332        const DIM = 1 << 1;
333        /// Prints with a line under the baseline.
334        const UNDERLINE = 1 << 2;
335        /// Prints with the foreground and background colors reversed. (Some
336        /// terminals that don't support color *do* support this.)
337        ///
338        /// Liso toggles this whenever a control sequence is inserted into a
339        /// [`Line`](struct.Line.html):
340        ///
341        /// ```rust
342        /// # use liso::liso;
343        /// assert_eq!(liso!("Type \x03 to quit."),
344        ///            liso!("Type ", ^inverse, "^C", ^inverse, " to quit."));
345        const INVERSE = 1 << 3;
346        /// An alias for [`INVERSE`](#associatedconstant.INVERSE). I prefer to
347        /// use the term "inverse video" rather than "reverse video", as the
348        /// latter might be confused for some kind of "mirrored video" feature.
349        #[doc(alias="INVERSE")]
350        const REVERSE = 1 << 3;
351        /// Prints in an italic font.
352        const ITALIC = 1 << 4;
353    }
354}
355
356impl Style {
357    fn as_crossterm(&self) -> CtAttributes {
358        let mut ret = CtAttributes::default();
359        if self.contains(Style::BOLD) {
360            ret.set(CtAttribute::Bold)
361        }
362        if self.contains(Style::DIM) {
363            ret.set(CtAttribute::Dim)
364        }
365        if self.contains(Style::UNDERLINE) {
366            ret.set(CtAttribute::Underlined)
367        }
368        if self.contains(Style::INVERSE) {
369            ret.set(CtAttribute::Reverse)
370        }
371        if self.contains(Style::ITALIC) {
372            ret.set(CtAttribute::Italic)
373        }
374        ret
375    }
376}
377
378/// This struct contains all the methods that the
379/// [`OutputOnly`](struct.OutputOnly.html) and
380/// [`InputOutput`](struct.InputOutput.html) structs have in common. Any method
381/// of this struct may be called on either of the other structs.
382pub struct Output {
383    tx: std_mpsc::Sender<Request>,
384}
385
386/// Sends output to the terminal. You can have more than one of these, shared
387/// freely among threads and tasks. Give one to every thread, task, or object
388/// that needs to produce output.
389pub struct OutputOnly(Output);
390
391/// Receives input from, and sends output to, the terminal. You can *send
392/// output* from any number of threads
393/// (see [`Output::clone_output`](struct.Output.html#method.clone_output)), but
394/// only one thread at a time may have ownership of the overlying `InputOutput`
395/// type and therefore the ability to *receive input*.
396pub struct InputOutput {
397    output: Output,
398    rx: tokio_mpsc::UnboundedReceiver<Response>,
399    #[cfg(feature = "history")]
400    history: Arc<RwLock<History>>,
401    death_count: u32,
402}
403
404/// Number of times that we will report `Response::Dead` before we decide that
405/// our caller isn't handling it correctly, and panic.
406const MAX_DEATH_COUNT: u32 = 9;
407
408/// Something sent *to* the Liso thread.
409enum Request {
410    /// Sent by `println`
411    Output(Line),
412    /// Sent by `wrapln`
413    #[cfg(feature = "wrap")]
414    OutputWrapped(Line),
415    /// Sent by `echoln`
416    OutputEcho(Line),
417    /// Sent by `status`
418    Status(Option<Line>),
419    /// Sent by `notice`
420    Notice(Line, Duration),
421    /// Sent by `prompt`
422    Prompt {
423        line: Option<Line>,
424        input_allowed: bool,
425        clear_input: bool,
426    },
427    /// Sent by `suspend_and_run`
428    SuspendAndRun(Box<dyn FnMut() + Send>),
429    /// Sent by the input task, when some input is inputted
430    Bell,
431    /// Sent when we're cleaning up
432    Die,
433    /// Sent whenever some raw input is received. This is an implementation
434    /// detail of the specific worker used; for the pipe worker, this is an
435    /// entire line, and for the tty worker, this is a block of raw input.
436    ///
437    /// Raw input is printable characters and simple control characters. Any
438    /// possible, meaningful escape sequences must already have been parsed
439    /// out. (The pipe worker doesn't interpret escape sequences and therefore
440    /// does no such processing.)
441    RawInput(String),
442    /// Used to implement notices.
443    Heartbeat,
444    /// If the crossterm event system is being used, this is an event received.
445    /// This can be the case even if the crossterm *input* system isn't being
446    /// used.
447    CrosstermEvent(crossterm::event::Event),
448    /// Sent by `send_custom`.
449    Custom(Box<dyn Any + Send>),
450    /// Sent when the `History` is changed.
451    #[cfg(feature = "history")]
452    BumpHistory,
453    /// Sent when the `Completor` is to be replaced.
454    #[cfg(feature = "completion")]
455    SetCompletor(Option<Box<dyn Completor>>),
456    /// Sent when some captured stderr is received.
457    #[cfg(feature = "capture-stderr")]
458    StderrLine(String),
459}
460
461/// Input received from the user, or a special condition. Returned by any of
462/// the following [`InputOutput`](struct.InputOutput.html) methods:
463///
464/// - [`read_async`](struct.InputOutput.html#method.read_async) (asynchronous)
465/// - [`read_blocking`](struct.InputOutput.html#method.read_blocking)
466///   (synchronous, waiting forever)
467/// - [`read_timeout`](struct.InputOutput.html#method.read_timeout)
468///   (synchronous with timeout)
469/// - [`read_deadline`](struct.InputOutput.html#method.read_deadline)
470///   (synchronous with deadline)
471/// - [`try_read`](struct.InputOutput.html#method.try_read)
472///   (polled)
473///
474/// Example usage:
475///
476/// ```rust
477/// # use liso::{Response, liso};
478/// # use std::time::Duration;
479/// # let mut io = liso::InputOutput::new();
480/// // near the top
481/// io.prompt(liso!(fg=green, bold, "> ", reset), true, false);
482/// // in your main loop
483/// # let response = Response::Input(String::new());
484/// # for _ in 0 .. 1 {
485/// match response {
486///   Response::Input(line) => {
487///     io.echoln(liso!(fg=green, dim, "> ", fg=none, &line));
488///     match line.as_str() {
489///       "hello" => io.println("World!"),
490///       "world" => io.println("Hello!"),
491///       _ => io.println("何って?"),
492///     }
493///   },
494///   Response::Discarded(line) => {
495///     io.echoln(liso!(bold+dim, "X ", -bold, line));
496///   },
497///   Response::Dead => return,
498///   Response::Quit => break,
499///   // (handle any other variants you want)
500///   other => {
501///       io.notice(format!("unknown key {}",
502///                         other.as_unknown() as char),
503///                 Duration::from_secs(1));
504///   },
505/// }
506/// # break;
507/// # }
508/// ```
509///
510///
511#[derive(Debug)]
512#[non_exhaustive]
513pub enum Response {
514    /// Sent when the user finishes entering a line of input. This is the
515    /// entire line. This is the most interesting, and common, variant that
516    /// you will receive.
517    ///
518    /// In case you don't want to do in-depth parsing of the user's input, you
519    /// can match against static string literals with a little work. You may
520    /// also want to use [`echoln`](struct.Output.html#method.echoln) to echo
521    /// the user's input. See the top of this documentation for an example of
522    /// both.
523    Input(String),
524    /// Sent when the terminal or the IO thread have died. Once you receive
525    /// this once, you will never receive any other `Response` from Liso again.
526    /// Your program should exit soon after, or at the very least should close
527    /// down that `InputOutput` instance.
528    ///
529    /// If your program receives `Response::Dead` on the same `InputOutput`
530    /// instance too many times, Liso will panic. This is to ensure that even
531    /// a poorly-written program that ignores `Response::Dead` will still exit
532    /// soon after after user input is permanently cut off, whether by a hangup
533    /// condition or by a bug in Liso.
534    Dead,
535    /// Sent when the user types control-C, which normally means they want your
536    /// program to quit.
537    Quit,
538    /// Sent when the user types control-G, discarding their current input. The
539    /// passed string is what the state of their input was when they hit
540    /// control-G. You should pass this to `echoln`, along with some kind of
541    /// feedback that the input was discarded.
542    Discarded(String),
543    /// Sent when the user types control-D on an empty line, which normally
544    /// means that they are done providing input (possibly temporarily).
545    Finish,
546    /// Sent when the user types control-T, which on some BSDs is a standard
547    /// way to request that a program give a status report or other progress
548    /// information.
549    Info,
550    /// Sent when the user types control-backslash, or when a break condition
551    /// is detected. The meaning of this is application-specific. If you're
552    /// running on a real, physical terminal line, this usually indicates an
553    /// excessively noisy line, or a disconnect ("break") in the line.
554    Break,
555    /// Sent when the user presses Escape.
556    Escape,
557    /// Sent when the user presses control-X.
558    Swap,
559    /// Sent whenever `send_custom` is called. This can be used to interrupt
560    /// the input thread when it's doing a `read_blocking` call.
561    Custom(Box<dyn Any + Send>),
562    /// Sent when the user presses an unknown control character with the given
563    /// value (which will be between 0 and 31 inclusive).
564    ///
565    /// Don't use particular values of `Unknown` for any specific purpose.
566    /// Later versions of Liso may add additional `Response` variants for new
567    /// control keys, or handle more control keys itself, replacing the
568    /// `Unknown(...)` values those keys used to send. See the top of this file
569    /// for an example of how this variant should be used (i.e. not directly).
570    Unknown(u8),
571}
572
573impl Response {
574    /// Returns the control code that triggered this response, e.g. 10 for
575    /// `Input`, 3 for `Quit`, ... Use this to produce a generic "unknown key
576    /// key ^X" kind of message for any `Response` variants you don't handle,
577    /// perhaps with code like. See the top of this file for an example.
578    pub fn as_unknown(&self) -> u8 {
579        match self {
580            &Response::Input(_) => 10,
581            &Response::Discarded(_) => 7,
582            &Response::Custom(_) => 0,
583            &Response::Quit => 3,
584            &Response::Finish => 4,
585            &Response::Info => 20,
586            &Response::Dead | &Response::Break => 28,
587            &Response::Escape => 27,
588            &Response::Swap => 24,
589            &Response::Unknown(x) => x,
590        }
591    }
592}
593
594impl Output {
595    fn send(&self, thing: Request) {
596        self.tx.send(thing).expect("Liso output has stopped");
597    }
598    /// Prints a (possibly styled) line of regular output to the screen.
599    ///
600    /// Note: As usual with `Output` methods, you can pass a
601    /// [`Line`](struct.Line.html), a plain `String`/`&str`, or a `Cow<str>`
602    /// here. See also the [`liso!`](macro.liso.html) macro.
603    pub fn println<T>(&self, line: T)
604    where
605        T: Into<Line>,
606    {
607        self.send(Request::Output(line.into()))
608    }
609    /// Prints a (possibly styled) line of regular output to the screen,
610    /// wrapping it to the width of the terminal. Only available with the
611    /// "wrap" feature, which is enabled by default.
612    ///
613    /// Note: As usual with `Output` methods, you can pass a
614    /// [`Line`](struct.Line.html), a plain `String`/`&str`, or a `Cow<str>`
615    /// here. See also the [`liso!`](macro.liso.html) macro.
616    #[cfg(feature = "wrap")]
617    pub fn wrapln<T>(&self, line: T)
618    where
619        T: Into<Line>,
620    {
621        self.send(Request::OutputWrapped(line.into()))
622    }
623    /// Prints a (possibly styled) line of regular output to the screen, but
624    /// only if we are being run interactively. Use this if you want to to echo
625    /// commands entered by the user, so that echoed commands will not gum up
626    /// the output when we are outputting to a pipe.
627    ///
628    /// Note: As usual with `Output` methods, you can pass a
629    /// [`Line`](struct.Line.html), a plain `String`/`&str`, or a `Cow<str>`
630    /// here. See also the [`liso!`](macro.liso.html) macro.
631    pub fn echoln<T>(&self, line: T)
632    where
633        T: Into<Line>,
634    {
635        self.send(Request::OutputEcho(line.into()))
636    }
637    /// Sets the status line to the given (possibly styled) text. This will be
638    /// displayed above the prompt, but below printed output. (Does nothing in
639    /// pipe mode.)
640    ///
641    /// Note: `status(Some(""))` and `status(None)` will have different
642    /// results! The former will display a *blank* status line, while the
643    /// latter will display *no* status line.
644    ///
645    /// Note: As usual with `Output` methods, you can pass a
646    /// [`Line`](struct.Line.html), a plain `String`/`&str`, or a `Cow<str>`
647    /// here. See also the [`liso!`](macro.liso.html) macro.
648    pub fn status<T>(&self, line: Option<T>)
649    where
650        T: Into<Line>,
651    {
652        self.send(Request::Status(line.map(T::into)))
653    }
654    /// Removes the status line. This is equivalent to `status(None)` but
655    /// without needing a turbofish.
656    pub fn remove_status(&self) {
657        self.send(Request::Status(None))
658    }
659    /// Displays a (possibly styled) notice that temporarily replaces the
660    /// prompt. The notice will disappear when the allotted time elapses, when
661    /// the user presses any key, or when another notice is displayed,
662    /// whichever happens first. (Does nothing in pipe mode.)
663    ///
664    /// You should only use this in direct response to user input; in fact, the
665    /// only legitimate use may be to complain about an unknown control
666    /// character. (See [`Response`][1] for an example of this use.)
667    ///
668    /// Note: As usual with `Output` methods, you can pass a
669    /// [`Line`](struct.Line.html), a plain `String`/`&str`, or a `Cow<str>`
670    /// here. See also the [`liso!`](macro.liso.html) macro.
671    ///
672    /// [1]: enum.Response.html
673    pub fn notice<T>(&self, line: T, max_duration: Duration)
674    where
675        T: Into<Line>,
676    {
677        self.send(Request::Notice(line.into(), max_duration))
678    }
679    /// Sets the prompt to the given (possibly styled) text. The prompt is
680    /// displayed in front of the user's input, unless we are running in pipe
681    /// mode.
682    ///
683    /// The default prompt is blank, with input allowed.
684    ///
685    /// - `input_allowed`: True if the user should be allowed to write input.
686    /// - `clear_input`: True if any existing partial input should be cleared
687    ///   when the new prompt is displayed. (If `input_allowed` is false, this
688    ///   should probably be `true`.)
689    ///
690    /// Note: If the prompt is styled, whatever style is active at the end of
691    /// the prompt will be used when displaying the user's input. This is the
692    /// only circumstance in which Liso will not automatically reset style
693    /// information for you at the end of a `Line`.
694    ///
695    /// Note: When running in pipe mode, input is always allowed, there is no
696    /// way to clear buffered input, and prompts are never displayed. In short,
697    /// this function does nothing at all in pipe mode.
698    ///
699    /// Note: As usual with `Output` methods, you can pass a
700    /// [`Line`](struct.Line.html), a plain `String`/`&str`, or a `Cow<str>`
701    /// here. See also the [`liso!`](macro.liso.html) macro.
702    pub fn prompt<T>(&self, line: T, input_allowed: bool, clear_input: bool)
703    where
704        T: Into<Line>,
705    {
706        let line: Line = line.into();
707        self.send(Request::Prompt {
708            line: if line.is_empty() { None } else { Some(line) },
709            input_allowed,
710            clear_input,
711        })
712    }
713    /// Removes the prompt. The boolean parameters have the same meaning as for
714    /// `prompt`.
715    #[deprecated = "Use `prompt` with a blank line instead."]
716    #[doc(hidden)]
717    pub fn remove_prompt(&self, input_allowed: bool, clear_input: bool) {
718        self.send(Request::Prompt {
719            line: None,
720            input_allowed,
721            clear_input,
722        })
723    }
724    /// Get the user's attention with an audible or visible bell.
725    pub fn bell(&self) {
726        self.send(Request::Bell)
727    }
728    /// Use this when you need to perform some work that outputs directly to
729    /// stdout/stderr and can't run it through Liso. Prompt, status, and input
730    /// in progress will be erased from the screen, and the terminal will be
731    /// put back into normal mode. When the function returns, Liso will set up
732    /// the terminal, display the prompt, and continue as normal.
733    ///
734    /// Bear in mind that this will run in a separate thread, possibly after a
735    /// short delay. If you need to return a value, wait for completion, or
736    /// otherwise communicate with the main program, you should use the usual
737    /// inter-thread communication primitives, such as channels or atomics.
738    ///
739    /// Note that you **cannot** use this to create a subprocess that will read
740    /// from stdin! Even though *output* is suspended, Liso will still be
741    /// reading from stdin in another thread, and thus, will be competing with
742    /// the subprocess for user input. (On sane UNIXes, this will result in
743    /// your program being suspended by your shell, and then misbehaving when
744    /// it resumes.) If you want to create a subprocess that can use stdin and
745    /// stdout, you'll have to write your own pipe handling based around Liso.
746    /// If you want to create a subprocess that can interactively use the
747    /// terminal—you have to drop the `InputOutput` instance, and all of the
748    /// existing `Output` instances will go dead as a result. Just don't do it!
749    pub fn suspend_and_run<F: 'static + FnMut() + Send>(&self, f: F) {
750        self.send(Request::SuspendAndRun(Box::new(f)))
751    }
752    /// Make a new `OutputOnly` that can also output to the terminal. The clone
753    /// and the original can be stored in separate places, even in different
754    /// threads or tasks. All output will go to the same terminal, without any
755    /// conflict between other threads doing output simultaneously or with user
756    /// input.
757    ///
758    /// For `OutputOnly`, this is the same as `clone`. For `InputOutput`, you
759    /// must call this method instead, as this makes it clear that you are not
760    /// trying to clone the `Input` half of that `InputOutput`.
761    pub fn clone_output(&self) -> OutputOnly {
762        OutputOnly(Output {
763            tx: self.tx.clone(),
764        })
765    }
766    #[deprecated = "Use `clone_output` instead."]
767    #[doc(hidden)]
768    pub fn clone_sender(&self) -> OutputOnly {
769        self.clone_output()
770    }
771    /// Send the given value to the input thread, wrapped in a
772    /// [`Response::Custom`](enum.Response.html#variant.Custom).
773    pub fn send_custom<T: Any + Send>(&self, value: T) {
774        self.send(Request::Custom(Box::new(value)))
775    }
776    /// Send the given already-boxed value to the input thread, wrapped in a
777    /// [`Response::Custom`](enum.Response.html#variant.Custom).
778    pub fn send_custom_box(&self, value: Box<dyn Any + Send>) {
779        self.send(Request::Custom(value))
780    }
781    /// Provide a new `Completor` for doing tab completion.
782    #[cfg(feature = "completion")]
783    pub fn set_completor(&self, completor: Option<Box<dyn Completor>>) {
784        self.send(Request::SetCompletor(completor))
785    }
786}
787
788impl Drop for InputOutput {
789    fn drop(&mut self) {
790        #[cfg(feature = "global")]
791        {
792            *LISO_OUTPUT_TX.lock() = None;
793        }
794        self.actually_blocking_die();
795        #[cfg(not(feature = "global"))]
796        LISO_IS_ACTIVE.store(false, Ordering::Release);
797        #[cfg(feature = "capture-stderr")]
798        stderr_capture::wait_until_not_captured();
799    }
800}
801
802impl core::ops::Deref for InputOutput {
803    type Target = Output;
804    fn deref(&self) -> &Output {
805        &self.output
806    }
807}
808
809impl InputOutput {
810    #[allow(clippy::new_without_default)]
811    pub fn new() -> InputOutput {
812        let we_are_alone;
813        #[cfg(feature = "global")]
814        let mut global_lock = LISO_OUTPUT_TX.lock();
815        #[cfg(feature = "global")]
816        {
817            we_are_alone = global_lock.is_none();
818        }
819        #[cfg(not(feature = "global"))]
820        match LISO_IS_ACTIVE.compare_exchange(
821            false,
822            true,
823            Ordering::Acquire,
824            Ordering::Relaxed,
825        ) {
826            Ok(_) => we_are_alone = true,
827            Err(_) => we_are_alone = false,
828        }
829        if !we_are_alone {
830            panic!(
831                "Tried to have multiple `liso::InputOutput` instances \
832                        active at the same time!"
833            )
834        }
835        let (request_tx, request_rx) = std_mpsc::channel();
836        let (response_tx, response_rx) = tokio_mpsc::unbounded_channel();
837        let request_tx_clone = request_tx.clone();
838        #[cfg(feature = "history")]
839        let history = Arc::new(RwLock::new(History::new()));
840        #[cfg(feature = "history")]
841        let history_clone = history.clone();
842        std::thread::Builder::new()
843            .name("Liso output thread".to_owned())
844            .spawn(move || {
845                #[cfg(feature = "history")]
846                let _ = worker::worker(
847                    request_tx_clone,
848                    request_rx,
849                    response_tx,
850                    history_clone,
851                );
852                #[cfg(not(feature = "history"))]
853                let _ =
854                    worker::worker(request_tx_clone, request_rx, response_tx);
855            })
856            .unwrap();
857        #[cfg(feature = "global")]
858        {
859            *global_lock = Some(request_tx.clone());
860        }
861        InputOutput {
862            output: Output { tx: request_tx },
863            rx: response_rx,
864            death_count: 0,
865            #[cfg(feature = "history")]
866            history,
867        }
868    }
869    /// Erase the prompt/status lines, put the terminal in a sensible mode,
870    /// and otherwise clean up everything we've done to the terminal. This will
871    /// happen automatically when this `InputOutput` instance is dropped; you
872    /// only need this method if you want to shut Liso down asynchronously for
873    /// some reason.
874    ///
875    /// If `Output`s cloned from this `InputOutput` exist, they will be "dead";
876    /// calling their methods will panic!
877    pub async fn die(mut self) {
878        if self.output.tx.send(Request::Die).is_err() {
879            // already dead!
880            return;
881        }
882        loop {
883            if let Response::Dead = self.read_async().await {
884                break;
885            }
886        }
887    }
888    fn actually_blocking_die(&mut self) {
889        if self.output.tx.send(Request::Die).is_err() {
890            // already dead!
891            return;
892        }
893        loop {
894            match self.try_read() {
895                None => std::thread::yield_now(),
896                Some(Response::Dead) => break,
897                _ => (),
898            }
899        }
900    }
901    /// Erase the prompt/status lines, put the terminal in a sensible mode,
902    /// and otherwise clean up everything we've done to the terminal. This will
903    /// happen automatically when this `InputOutput` instance is dropped, so
904    /// you probably don't need to call this manually.
905    ///
906    /// If `OutputOnly`s cloned from this `InputOutput` exist, they will be
907    /// "dead"; calling their methods will panic!
908    pub fn blocking_die(mut self) {
909        self.actually_blocking_die()
910    }
911    fn report_death(&mut self) {
912        self.death_count = self.death_count.saturating_add(1);
913        if self.death_count >= MAX_DEATH_COUNT {
914            panic!("Client program is looping forever despite receiving `Response::Dead` {} times. Program bug!", MAX_DEATH_COUNT);
915        }
916    }
917    /// Read a [`Response`](enum.Response.html) from the user, blocking this
918    /// task until something is received.
919    ///
920    /// This is an asynchronous function. To read from non-asynchronous code,
921    /// you should use `read_blocking` instead.
922    ///
923    /// If `Response::Dead` is received too many times, Liso will assume your
924    /// program is ignoring it and panic! Avoid this problem by handling
925    /// `Response::Dead` correctly.
926    pub async fn read_async(&mut self) -> Response {
927        match self.rx.recv().await {
928            None => {
929                self.report_death();
930                Response::Dead
931            }
932            Some(x) => x,
933        }
934    }
935    #[deprecated = "Use `read_async` instead."]
936    #[doc(hidden)]
937    pub async fn read(&mut self) -> Response {
938        self.read_async().await
939    }
940    /// Read a [`Response`](enum.Response.html) from the user, blocking this
941    /// thread until the given `timeout` elapses or something is received.
942    ///
943    /// This is a synchronous function. To achieve the same effect
944    /// asynchronously, you can wrap `read_async` in `tokio::time::timeout`.
945    ///
946    /// If `Response::Dead` is received too many times, Liso will assume your
947    /// program is ignoring it and panic! Avoid this problem by handling
948    /// `Response::Dead` correctly.
949    pub fn read_timeout(&mut self, timeout: Duration) -> Option<Response> {
950        let rt = tokio::runtime::Builder::new_current_thread()
951            .enable_time()
952            .build()
953            .expect(
954                "Couldn't create temporary Tokio runtime for `read_timeout`",
955            );
956        rt.block_on(async {
957            let timeout = tokio::time::timeout(timeout, self.rx.recv());
958            match timeout.await {
959                Ok(None) => {
960                    self.report_death();
961                    Some(Response::Dead)
962                }
963                Ok(Some(x)) => Some(x),
964                Err(_) => None,
965            }
966        })
967    }
968    /// Read a [`Response`](enum.Response.html) from the user, blocking this
969    /// thread until the given `deadline` is reached or something is received.
970    ///
971    /// This is a synchronous function. To achieve the same effect
972    /// asynchronously, you can wrap `read_async` in `tokio::time::timeout_at`.
973    ///
974    /// If `Response::Dead` is received too many times, Liso will assume your
975    /// program is ignoring it and panic! Avoid this problem by handling
976    /// `Response::Dead` correctly.
977    pub fn read_deadline(&mut self, deadline: Instant) -> Option<Response> {
978        let rt = tokio::runtime::Builder::new_current_thread()
979            .enable_time()
980            .build()
981            .expect(
982                "Couldn't create temporary Tokio runtime for `read_deadline`",
983            );
984        rt.block_on(async {
985            let timeout = tokio::time::timeout_at(
986                tokio::time::Instant::from_std(deadline),
987                self.rx.recv(),
988            );
989            match timeout.await {
990                Ok(None) => {
991                    self.report_death();
992                    Some(Response::Dead)
993                }
994                Ok(Some(x)) => Some(x),
995                Err(_) => None,
996            }
997        })
998    }
999    /// Read a [`Response`](enum.Response.html) from the user, blocking this
1000    /// thread until something is received.
1001    ///
1002    /// This is a synchronous function. To read from asynchronous code, you
1003    /// should use `read_async` instead.
1004    ///
1005    /// If `Response::Dead` is received too many times, Liso will assume your
1006    /// program is ignoring it and panic! Avoid this problem by handling
1007    /// `Response::Dead` correctly.
1008    pub fn read_blocking(&mut self) -> Response {
1009        match self.rx.blocking_recv() {
1010            None => {
1011                self.report_death();
1012                Response::Dead
1013            }
1014            Some(x) => x,
1015        }
1016    }
1017    #[deprecated = "Use `read_blocking` instead."]
1018    #[doc(hidden)]
1019    pub fn blocking_read(&mut self) -> Response {
1020        self.read_blocking()
1021    }
1022    /// Read a [`Response`](enum.Response.html) from the user, if one is
1023    /// available. If no inputs are currently available, return immediately
1024    /// instead of blocking or waiting.
1025    ///
1026    /// If `Response::Dead` is received too many times, Liso will assume your
1027    /// program is ignoring it and panic! Avoid this problem by handling
1028    /// `Response::Dead` correctly.
1029    pub fn try_read(&mut self) -> Option<Response> {
1030        use tokio::sync::mpsc::error::TryRecvError;
1031        match self.rx.try_recv() {
1032            Ok(x) => Some(x),
1033            Err(TryRecvError::Disconnected) => {
1034                self.report_death();
1035                Some(Response::Dead)
1036            }
1037            Err(TryRecvError::Empty) => None,
1038        }
1039    }
1040    /// Provide a new `History` for Liso to use. Returns the old `History`
1041    /// instance.
1042    #[cfg(feature = "history")]
1043    pub fn swap_history(&self, mut history: History) -> History {
1044        let mut lock = self.history.write().unwrap();
1045        std::mem::swap(&mut history, &mut *lock);
1046        drop(lock);
1047        let _ = self.tx.send(Request::BumpHistory);
1048        history
1049    }
1050    /// Lock the `History` for reading and return a reference to it. Make it
1051    /// brief!
1052    #[cfg(feature = "history")]
1053    pub fn read_history(&self) -> RwLockReadGuard<History> {
1054        self.history.read().unwrap()
1055    }
1056}
1057
1058impl core::ops::Deref for OutputOnly {
1059    type Target = Output;
1060    fn deref(&self) -> &Output {
1061        &self.0
1062    }
1063}
1064
1065impl Clone for OutputOnly {
1066    fn clone(&self) -> OutputOnly {
1067        self.clone_output()
1068    }
1069}
1070
1071#[cfg(feature = "wrap")]
1072fn convert_subset_slice_to_range(outer: &str, inner: &str) -> (usize, usize) {
1073    if inner.is_empty() {
1074        return (0, 0);
1075    }
1076    let outer_start = outer.as_ptr() as usize;
1077    let outer_end = outer_start.checked_add(outer.len()).unwrap();
1078    let inner_start = inner.as_ptr() as usize;
1079    let inner_end = inner_start.checked_add(inner.len()).unwrap();
1080    assert!(inner_start >= outer_start);
1081    assert!(inner_end <= outer_end);
1082    (inner_start - outer_start, inner_end - outer_start)
1083}
1084
1085/// Produce an `Option<Color>` from a name or expression. For internal use by
1086/// the [`liso!`](macro.liso.html) and [`liso_add!`](macro.liso_add.html)
1087/// macros.
1088#[macro_export]
1089#[doc(hidden)]
1090macro_rules! color {
1091    (Black) => {
1092        Some($crate::Color::Black)
1093    };
1094    (Red) => {
1095        Some($crate::Color::Red)
1096    };
1097    (Green) => {
1098        Some($crate::Color::Green)
1099    };
1100    (Yellow) => {
1101        Some($crate::Color::Yellow)
1102    };
1103    (Blue) => {
1104        Some($crate::Color::Blue)
1105    };
1106    (Cyan) => {
1107        Some($crate::Color::Cyan)
1108    };
1109    (Magenta) => {
1110        Some($crate::Color::Magenta)
1111    };
1112    (White) => {
1113        Some($crate::Color::White)
1114    };
1115    (none) => {
1116        None
1117    };
1118    (black) => {
1119        Some($crate::Color::Black)
1120    };
1121    (red) => {
1122        Some($crate::Color::Red)
1123    };
1124    (green) => {
1125        Some($crate::Color::Green)
1126    };
1127    (yellow) => {
1128        Some($crate::Color::Yellow)
1129    };
1130    (blue) => {
1131        Some($crate::Color::Blue)
1132    };
1133    (cyan) => {
1134        Some($crate::Color::Cyan)
1135    };
1136    (magenta) => {
1137        Some($crate::Color::Magenta)
1138    };
1139    (white) => {
1140        Some($crate::Color::White)
1141    };
1142    (none) => {
1143        None
1144    };
1145    ($other:expr) => {
1146        $other
1147    };
1148}
1149
1150/// Add some pieces to a [`Line`](struct.Line.html). More convenient than
1151/// calling its methods.
1152///
1153/// ```rust
1154/// # use liso::{liso_add, Line, Style};
1155/// let mut line_a = Line::new();
1156/// line_a.add_text("Hello ");
1157/// line_a.set_style(Style::BOLD);
1158/// line_a.add_text("World!");
1159/// let mut line_b = Line::new();
1160/// liso_add!(line_b, "Hello ", bold, "World!");
1161/// assert_eq!(line_a, line_b);
1162/// ```
1163///
1164/// Use the [`liso!` macro](macro.liso.html) to build an entire line in a
1165/// single go. See that macro's documentation for more information on the
1166/// syntax.
1167#[macro_export]
1168macro_rules! liso_add {
1169    // Reset all style and color
1170    // `reset`
1171    ($line:ident, reset, $($rest:tt)*) => {
1172        $line.reset_all();
1173        $crate::liso_add!($line, $($rest)*);
1174    };
1175    ($line:ident, reset) => {
1176        $line.reset_all();
1177    };
1178    // Set fg/bg color
1179    // (`fg` | `bg`) `=` <COLOR>
1180    ($line:ident, fg = $color:tt, $($rest:tt)*) => {
1181        $line.set_fg_color($crate::color!($color));
1182        $crate::liso_add!($line, $($rest)*);
1183    };
1184    ($line:ident, fg = $color:tt) => {
1185        $line.set_fg_color($crate::color!($color));
1186    };
1187    ($line:ident, bg = $color:tt, $($rest:tt)*) => {
1188        $line.set_bg_color($crate::color!($color));
1189        $crate::liso_add!($line, $($rest)*);
1190    };
1191    ($line:ident, bg = $color:tt) => {
1192        $line.set_bg_color($crate::color!($color));
1193    };
1194    ($line:ident, fg = $color:expr, $($rest:tt)*) => {
1195        $line.set_fg_color($color);
1196        $crate::liso_add!($line, $($rest)*);
1197    };
1198    ($line:ident, fg = $color:expr) => {
1199        $line.set_fg_color($color);
1200    };
1201    ($line:ident, bg = $color:expr, $($rest:tt)*) => {
1202        $line.set_bg_color($color);
1203        $crate::liso_add!($line, $($rest)*);
1204    };
1205    ($line:ident, bg = $color:expr) => {
1206        $line.set_bg_color($color);
1207    };
1208    // Clear styles
1209    // `plain`
1210    ($line:ident, plain $($rest:tt)*) => {
1211        $line.set_style($crate::Style::PLAIN);
1212        $crate::liso_add!($line, $($rest)*);
1213    };
1214    // SET styles
1215    // `bold` | `dim` | `underline` | `inverse` | `reverse` | `italic`
1216    ($line:ident, bold $($rest:tt)*) => {
1217        $line.set_style($crate::Style::BOLD);
1218        $crate::liso_add!($line, $($rest)*);
1219    };
1220    ($line:ident, dim $($rest:tt)*) => {
1221        $line.set_style($crate::Style::DIM);
1222        $crate::liso_add!($line, $($rest)*);
1223    };
1224    ($line:ident, underline $($rest:tt)*) => {
1225        $line.set_style($crate::Style::UNDERLINE);
1226        $crate::liso_add!($line, $($rest)*);
1227    };
1228    ($line:ident, inverse $($rest:tt)*) => {
1229        $line.set_style($crate::Style::INVERSE);
1230        $crate::liso_add!($line, $($rest)*);
1231    };
1232    ($line:ident, reverse $($rest:tt)*) => {
1233        $line.set_style($crate::Style::INVERSE);
1234        $crate::liso_add!($line, $($rest)*);
1235    };
1236    ($line:ident, italic $($rest:tt)*) => {
1237        $line.set_style($crate::Style::ITALIC);
1238        $crate::liso_add!($line, $($rest)*);
1239    };
1240    // ADD styles
1241    // `+` (`bold` | `dim` | `underline` | `inverse` | `reverse` | `italic`)
1242    ($line:ident, +bold $($rest:tt)*) => {
1243        $line.activate_style($crate::Style::BOLD);
1244        $crate::liso_add!($line, $($rest)*);
1245    };
1246    ($line:ident, +dim $($rest:tt)*) => {
1247        $line.activate_style($crate::Style::DIM);
1248        $crate::liso_add!($line, $($rest)*);
1249    };
1250    ($line:ident, +underline $($rest:tt)*) => {
1251        $line.activate_style($crate::Style::UNDERLINE);
1252        $crate::liso_add!($line, $($rest)*);
1253    };
1254    ($line:ident, +inverse $($rest:tt)*) => {
1255        $line.activate_style($crate::Style::INVERSE);
1256        $crate::liso_add!($line, $($rest)*);
1257    };
1258    ($line:ident, +reverse $($rest:tt)*) => {
1259        $line.activate_style($crate::Style::INVERSE);
1260        $crate::liso_add!($line, $($rest)*);
1261    };
1262    ($line:ident, +italic $($rest:tt)*) => {
1263        $line.activate_style($crate::Style::ITALIC);
1264        $crate::liso_add!($line, $($rest)*);
1265    };
1266    // REMOVE styles
1267    // `-` (`bold` | `dim` | `underline` | `inverse` | `reverse` | `italic`)
1268    ($line:ident, -bold $($rest:tt)*) => {
1269        $line.deactivate_style($crate::Style::BOLD);
1270        $crate::liso_add!($line, $($rest)*);
1271    };
1272    ($line:ident, -dim $($rest:tt)*) => {
1273        $line.deactivate_style($crate::Style::DIM);
1274        $crate::liso_add!($line, $($rest)*);
1275    };
1276    ($line:ident, -underline $($rest:tt)*) => {
1277        $line.deactivate_style($crate::Style::UNDERLINE);
1278        $crate::liso_add!($line, $($rest)*);
1279    };
1280    ($line:ident, -inverse $($rest:tt)*) => {
1281        $line.deactivate_style($crate::Style::INVERSE);
1282        $crate::liso_add!($line, $($rest)*);
1283    };
1284    ($line:ident, -reverse $($rest:tt)*) => {
1285        $line.deactivate_style($crate::Style::INVERSE);
1286        $crate::liso_add!($line, $($rest)*);
1287    };
1288    ($line:ident, -italic $($rest:tt)*) => {
1289        $line.deactivate_style($crate::Style::ITALIC);
1290        $crate::liso_add!($line, $($rest)*);
1291    };
1292    // TOGGLE styles
1293    // `^` (`bold` | `dim` | `underline` | `inverse` | `reverse` | `italic`)
1294    ($line:ident, ^bold $($rest:tt)*) => {
1295        $line.toggle_style($crate::Style::BOLD);
1296        $crate::liso_add!($line, $($rest)*);
1297    };
1298    ($line:ident, ^dim $($rest:tt)*) => {
1299        $line.toggle_style($crate::Style::DIM);
1300        $crate::liso_add!($line, $($rest)*);
1301    };
1302    ($line:ident, ^underline $($rest:tt)*) => {
1303        $line.toggle_style($crate::Style::UNDERLINE);
1304        $crate::liso_add!($line, $($rest)*);
1305    };
1306    ($line:ident, ^inverse $($rest:tt)*) => {
1307        $line.toggle_style($crate::Style::INVERSE);
1308        $crate::liso_add!($line, $($rest)*);
1309    };
1310    ($line:ident, ^reverse $($rest:tt)*) => {
1311        $line.toggle_style($crate::Style::INVERSE);
1312        $crate::liso_add!($line, $($rest)*);
1313    };
1314    ($line:ident, ^italic $($rest:tt)*) => {
1315        $line.toggle_style($crate::Style::ITALIC);
1316        $crate::liso_add!($line, $($rest)*);
1317    };
1318    // Interpret ANSI sequences
1319    // `ansi` <text>
1320    ($line:ident, ansi $expr:expr, $($rest:tt)*) => {
1321        $line.add_ansi_text($expr);
1322        $crate::liso_add!($line, $($rest)*);
1323    };
1324    ($line:ident, ansi $expr:expr) => {
1325        $line.add_ansi_text($expr);
1326    };
1327    // Anything else: text to output.
1328    ($line:ident, $expr:expr, $($rest:tt)*) => {
1329        $line.add_text($expr);
1330        $crate::liso_add!($line, $($rest)*);
1331    };
1332    ($line:ident, $expr:expr) => {
1333        $line.add_text($expr);
1334    };
1335    // Strip double commas
1336    ($line:ident,, $($rest:tt)*) => {
1337        $crate::liso_add!($line, $($rest)*);
1338    };
1339    // Finish munching
1340    ($line:ident$(,)*) => {
1341    };
1342}
1343
1344/// Constructs a [`Line`](struct.Line.html) from pieces. More convenient than
1345/// creating a `Line` and calling its methods.
1346///
1347/// You can use the [`liso_add!` macro](macro.liso_add.html) to conveniently
1348/// add pieces to an existing `Line`.
1349///
1350/// Setting style doesn't affect color, and vice versa.
1351///
1352/// - `plain`  
1353///   Clear all styles.
1354/// - `<style>`  
1355///   *Set* the style, clearing any other styles.
1356/// - `+<style>`  
1357///   Enable this style, leaving other styles unaffected.
1358/// - `-<style>`  
1359///   Disable this style, leaving other styles unaffected.
1360/// - `^<style>`  
1361///   Toggle this style, leaving other styles unaffected.
1362/// - `fg = <color>`  
1363///   Set the foreground color.
1364/// - `bg = <color>`  
1365///   Set the background color.
1366/// - `reset`  
1367///   Clear all style and color information.
1368/// - `ansi <text>`
1369///   Text to output, with interpretation of some ANSI escape sequences found
1370///   in the text.
1371/// - `<text>`  
1372///   Text to output.
1373///
1374/// You have to put a comma after `fg = ...`, `bg = ...`, `reset`, and text.
1375/// They are optional everywhere else.
1376///
1377/// ```rust
1378/// # use liso::liso;
1379/// # let error_message = "Hello World!";
1380/// let line = liso!(fg = red, "Error: ", bold, format!("{}", error_message));
1381/// let line = liso!("Do you want to proceed? This is a ", bold+underline,
1382///                  "really", plain, " bad idea!");
1383/// ```
1384///
1385/// `<style>` may be `bold`, `dim`, `inverse`/`reverse`, `italic`, or `plain`.
1386/// `<color>` may be the actual name of a [`Color`](enum.Color.html), the
1387/// lowercase equivalent, `None`/`none`, or any expression evaluating to an
1388/// `Option<Color>`. `<text>` may be anything that you could pass directly to
1389/// [`Line::add_text()`](struct.Line.html#method.add_text), including a simple
1390/// string literal or a call to `format!`.
1391#[macro_export]
1392macro_rules! liso {
1393    ($($rest:tt)*) => {
1394        {
1395            let mut line = $crate::Line::new();
1396            $crate::liso_add!(line, $($rest)*,);
1397            line
1398        }
1399    };
1400}
1401
1402#[deprecated = "This type was renamed to `InputOutput` to improve clarity.\n\
1403              To continue using this name without warnings, try `use \
1404              liso::InputOutput as IO;`"]
1405#[doc(hidden)]
1406pub type IO = InputOutput;
1407#[deprecated = "This type was split into `Output` and `OutputOnly` to improve \
1408              clarity.\nReplace with `&Output` or `OutputOnly` as needed."]
1409#[doc(hidden)]
1410pub type Sender = OutputOnly;
1411
1412#[cfg(not(feature = "global"))]
1413/// Used to prevent multiple Liso instances from being active at once.
1414static LISO_IS_ACTIVE: AtomicBool = AtomicBool::new(false);
1415
1416#[cfg(feature = "global")]
1417static LISO_OUTPUT_TX: parking_lot::Mutex<Option<std_mpsc::Sender<Request>>> =
1418    parking_lot::Mutex::new(None);
1419
1420/// If the `global` feature is enabled (which it is by default), and there is
1421/// an [`InputOutput`](struct.InputOutput.html) alive somewhere, you can call
1422/// `output()` to get an [`OutputOnly`](struct.OutputOnly.html) struct that you
1423/// can use to perform output on it. This is less efficient than creating an
1424/// `OutputOnly` directly with `clone_output()` and keeping it around, but it
1425/// is more convenient.
1426///
1427/// Calling `output()` when there is no
1428/// `InputOutput` alive will result in a panic.
1429#[cfg(feature = "global")]
1430pub fn output() -> OutputOnly {
1431    match &*LISO_OUTPUT_TX.lock() {
1432        None => {
1433            panic!("liso::output() called with no liso::InputOutput alive")
1434        }
1435        Some(x) => OutputOnly(Output { tx: x.clone() }),
1436    }
1437}
1438
1439/// If the `global` feature is enabled (which it is by default), you can use
1440/// `println!(...)` as convenient shorthand for `output().println(liso!(...))`.
1441/// This is less efficient than creating an `OutputOnly` with `clone_output()`
1442/// and keeping it around, but it is more convenient. You will have to
1443/// explicitly `use liso::println;`, or call it by its full path
1444/// (`liso::println!`) or Rust may be uncertain whether you meant to use this
1445/// or `std::println!`. **Panics if there is no `InputOutput` instance alive.**
1446///
1447/// Syntax is the same as the [`liso!`](macro.liso.html) macro.
1448#[cfg(feature = "global")]
1449#[macro_export]
1450macro_rules! println {
1451    ($($rest:tt)*) => {
1452        $crate::output().println(liso!($($rest)*))
1453    }
1454}
1455
1456/// If the `global` and `wrap` features are enabled (which they are by
1457/// default), you can use `wrapln!(...)` as convenient shorthand for
1458/// `output().println(liso!(...))`. This is less efficient than creating an
1459/// `OutputOnly` with `clone_output()` and keeping it around, but it is more
1460/// convenient. **Panics if there is no `InputOutput` instance alive.**
1461///
1462/// Syntax is the same as the [`liso!`](macro.liso.html) macro.
1463#[cfg(all(feature = "global", feature = "wrap"))]
1464#[macro_export]
1465macro_rules! wrapln {
1466    ($($rest:tt)*) => {
1467        $crate::output().wrapln(liso!($($rest)*))
1468    }
1469}