aboutsummaryrefslogtreecommitdiff
path: root/vendor/console/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/console/src')
-rw-r--r--vendor/console/src/ansi.rs438
-rw-r--r--vendor/console/src/common_term.rs72
-rw-r--r--vendor/console/src/kb.rs29
-rw-r--r--vendor/console/src/lib.rs104
-rw-r--r--vendor/console/src/term.rs632
-rw-r--r--vendor/console/src/unix_term.rs362
-rw-r--r--vendor/console/src/utils.rs962
-rw-r--r--vendor/console/src/wasm_term.rs54
-rw-r--r--vendor/console/src/windows_term/colors.rs451
-rw-r--r--vendor/console/src/windows_term/mod.rs563
10 files changed, 0 insertions, 3667 deletions
diff --git a/vendor/console/src/ansi.rs b/vendor/console/src/ansi.rs
deleted file mode 100644
index 3a3c96c..0000000
--- a/vendor/console/src/ansi.rs
+++ /dev/null
@@ -1,438 +0,0 @@
-use std::{
- borrow::Cow,
- iter::{FusedIterator, Peekable},
- str::CharIndices,
-};
-
-#[derive(Debug, Clone, Copy)]
-enum State {
- Start,
- S1,
- S2,
- S3,
- S4,
- S5,
- S6,
- S7,
- S8,
- S9,
- S10,
- S11,
- Trap,
-}
-
-impl Default for State {
- fn default() -> Self {
- Self::Start
- }
-}
-
-impl State {
- fn is_final(&self) -> bool {
- #[allow(clippy::match_like_matches_macro)]
- match self {
- Self::S3 | Self::S5 | Self::S6 | Self::S7 | Self::S8 | Self::S9 | Self::S11 => true,
- _ => false,
- }
- }
-
- fn is_trapped(&self) -> bool {
- #[allow(clippy::match_like_matches_macro)]
- match self {
- Self::Trap => true,
- _ => false,
- }
- }
-
- fn transition(&mut self, c: char) {
- *self = match c {
- '\u{1b}' | '\u{9b}' => match self {
- Self::Start => Self::S1,
- _ => Self::Trap,
- },
- '(' | ')' => match self {
- Self::S1 => Self::S2,
- Self::S2 | Self::S4 => Self::S4,
- _ => Self::Trap,
- },
- ';' => match self {
- Self::S1 | Self::S2 | Self::S4 => Self::S4,
- Self::S5 | Self::S6 | Self::S7 | Self::S8 | Self::S10 => Self::S10,
- _ => Self::Trap,
- },
-
- '[' | '#' | '?' => match self {
- Self::S1 | Self::S2 | Self::S4 => Self::S4,
- _ => Self::Trap,
- },
- '0'..='2' => match self {
- Self::S1 | Self::S4 => Self::S5,
- Self::S2 => Self::S3,
- Self::S5 => Self::S6,
- Self::S6 => Self::S7,
- Self::S7 => Self::S8,
- Self::S8 => Self::S9,
- Self::S10 => Self::S5,
- _ => Self::Trap,
- },
- '3'..='9' => match self {
- Self::S1 | Self::S4 => Self::S5,
- Self::S2 => Self::S5,
- Self::S5 => Self::S6,
- Self::S6 => Self::S7,
- Self::S7 => Self::S8,
- Self::S8 => Self::S9,
- Self::S10 => Self::S5,
- _ => Self::Trap,
- },
- 'A'..='P' | 'R' | 'Z' | 'c' | 'f'..='n' | 'q' | 'r' | 'y' | '=' | '>' | '<' => {
- match self {
- Self::S1
- | Self::S2
- | Self::S4
- | Self::S5
- | Self::S6
- | Self::S7
- | Self::S8
- | Self::S10 => Self::S11,
- _ => Self::Trap,
- }
- }
- _ => Self::Trap,
- };
- }
-}
-
-#[derive(Debug)]
-struct Matches<'a> {
- s: &'a str,
- it: Peekable<CharIndices<'a>>,
-}
-
-impl<'a> Matches<'a> {
- fn new(s: &'a str) -> Self {
- let it = s.char_indices().peekable();
- Self { s, it }
- }
-}
-
-#[derive(Debug)]
-struct Match<'a> {
- text: &'a str,
- start: usize,
- end: usize,
-}
-
-impl<'a> Match<'a> {
- #[inline]
- pub fn as_str(&self) -> &'a str {
- &self.text[self.start..self.end]
- }
-}
-
-impl<'a> Iterator for Matches<'a> {
- type Item = Match<'a>;
-
- fn next(&mut self) -> Option<Self::Item> {
- find_ansi_code_exclusive(&mut self.it).map(|(start, end)| Match {
- text: self.s,
- start,
- end,
- })
- }
-}
-
-impl<'a> FusedIterator for Matches<'a> {}
-
-fn find_ansi_code_exclusive(it: &mut Peekable<CharIndices>) -> Option<(usize, usize)> {
- 'outer: loop {
- if let (start, '\u{1b}') | (start, '\u{9b}') = it.peek()? {
- let start = *start;
- let mut state = State::default();
- let mut maybe_end = None;
-
- loop {
- let item = it.peek();
-
- if let Some((idx, c)) = item {
- state.transition(*c);
-
- if state.is_final() {
- maybe_end = Some(*idx);
- }
- }
-
- // The match is greedy so run till we hit the trap state no matter what. A valid
- // match is just one that was final at some point
- if state.is_trapped() || item.is_none() {
- match maybe_end {
- Some(end) => {
- // All possible final characters are a single byte so it's safe to make
- // the end exclusive by just adding one
- return Some((start, end + 1));
- }
- // The character we are peeking right now might be the start of a match so
- // we want to continue the loop without popping off that char
- None => continue 'outer,
- }
- }
-
- it.next();
- }
- }
-
- it.next();
- }
-}
-
-/// Helper function to strip ansi codes.
-pub fn strip_ansi_codes(s: &str) -> Cow<str> {
- let mut char_it = s.char_indices().peekable();
- match find_ansi_code_exclusive(&mut char_it) {
- Some(_) => {
- let stripped: String = AnsiCodeIterator::new(s)
- .filter_map(|(text, is_ansi)| if is_ansi { None } else { Some(text) })
- .collect();
- Cow::Owned(stripped)
- }
- None => Cow::Borrowed(s),
- }
-}
-
-/// An iterator over ansi codes in a string.
-///
-/// This type can be used to scan over ansi codes in a string.
-/// It yields tuples in the form `(s, is_ansi)` where `s` is a slice of
-/// the original string and `is_ansi` indicates if the slice contains
-/// ansi codes or string values.
-pub struct AnsiCodeIterator<'a> {
- s: &'a str,
- pending_item: Option<(&'a str, bool)>,
- last_idx: usize,
- cur_idx: usize,
- iter: Matches<'a>,
-}
-
-impl<'a> AnsiCodeIterator<'a> {
- /// Creates a new ansi code iterator.
- pub fn new(s: &'a str) -> AnsiCodeIterator<'a> {
- AnsiCodeIterator {
- s,
- pending_item: None,
- last_idx: 0,
- cur_idx: 0,
- iter: Matches::new(s),
- }
- }
-
- /// Returns the string slice up to the current match.
- pub fn current_slice(&self) -> &str {
- &self.s[..self.cur_idx]
- }
-
- /// Returns the string slice from the current match to the end.
- pub fn rest_slice(&self) -> &str {
- &self.s[self.cur_idx..]
- }
-}
-
-impl<'a> Iterator for AnsiCodeIterator<'a> {
- type Item = (&'a str, bool);
-
- fn next(&mut self) -> Option<(&'a str, bool)> {
- if let Some(pending_item) = self.pending_item.take() {
- self.cur_idx += pending_item.0.len();
- Some(pending_item)
- } else if let Some(m) = self.iter.next() {
- let s = &self.s[self.last_idx..m.start];
- self.last_idx = m.end;
- if s.is_empty() {
- self.cur_idx = m.end;
- Some((m.as_str(), true))
- } else {
- self.cur_idx = m.start;
- self.pending_item = Some((m.as_str(), true));
- Some((s, false))
- }
- } else if self.last_idx < self.s.len() {
- let rv = &self.s[self.last_idx..];
- self.cur_idx = self.s.len();
- self.last_idx = self.s.len();
- Some((rv, false))
- } else {
- None
- }
- }
-}
-
-impl<'a> FusedIterator for AnsiCodeIterator<'a> {}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- use lazy_static::lazy_static;
- use proptest::prelude::*;
- use regex::Regex;
-
- // The manual dfa `State` is a handwritten translation from the previously used regex. That
- // regex is kept here and used to ensure that the new matches are the same as the old
- lazy_static! {
- static ref STRIP_ANSI_RE: Regex = Regex::new(
- r"[\x1b\x9b]([()][012AB]|[\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><])",
- )
- .unwrap();
- }
-
- impl<'a, 'b> PartialEq<Match<'a>> for regex::Match<'b> {
- fn eq(&self, other: &Match<'a>) -> bool {
- self.start() == other.start && self.end() == other.end
- }
- }
-
- proptest! {
- #[test]
- fn dfa_matches_old_regex(s in r"([\x1b\x9b]?.*){0,5}") {
- let old_matches: Vec<_> = STRIP_ANSI_RE.find_iter(&s).collect();
- let new_matches: Vec<_> = Matches::new(&s).collect();
- assert_eq!(old_matches, new_matches);
- }
- }
-
- #[test]
- fn dfa_matches_regex_on_small_strings() {
- // To make sure the test runs in a reasonable time this is a slimmed down list of
- // characters to reduce the groups that are only used with each other along with one
- // arbitrarily chosen character not used in the regex (' ')
- const POSSIBLE_BYTES: &[u8] = &[b' ', 0x1b, 0x9b, b'(', b'0', b'[', b';', b'3', b'C'];
-
- fn check_all_strings_of_len(len: usize) {
- _check_all_strings_of_len(len, &mut Vec::with_capacity(len));
- }
-
- fn _check_all_strings_of_len(len: usize, chunk: &mut Vec<u8>) {
- if len == 0 {
- if let Ok(s) = std::str::from_utf8(chunk) {
- let old_matches: Vec<_> = STRIP_ANSI_RE.find_iter(s).collect();
- let new_matches: Vec<_> = Matches::new(s).collect();
- assert_eq!(old_matches, new_matches);
- }
-
- return;
- }
-
- for b in POSSIBLE_BYTES {
- chunk.push(*b);
- _check_all_strings_of_len(len - 1, chunk);
- chunk.pop();
- }
- }
-
- for str_len in 0..=6 {
- check_all_strings_of_len(str_len);
- }
- }
-
- #[test]
- fn complex_data() {
- let s = std::fs::read_to_string(
- std::path::Path::new("tests")
- .join("data")
- .join("sample_zellij_session.log"),
- )
- .unwrap();
-
- let old_matches: Vec<_> = STRIP_ANSI_RE.find_iter(&s).collect();
- let new_matches: Vec<_> = Matches::new(&s).collect();
- assert_eq!(old_matches, new_matches);
- }
-
- #[test]
- fn state_machine() {
- let ansi_code = "\x1b)B";
- let mut state = State::default();
- assert!(!state.is_final());
-
- for c in ansi_code.chars() {
- state.transition(c);
- }
- assert!(state.is_final());
-
- state.transition('A');
- assert!(state.is_trapped());
- }
-
- #[test]
- fn back_to_back_entry_char() {
- let s = "\x1b\x1bf";
- let matches: Vec<_> = Matches::new(s).map(|m| m.as_str()).collect();
- assert_eq!(&["\x1bf"], matches.as_slice());
- }
-
- #[test]
- fn early_paren_can_use_many_chars() {
- let s = "\x1b(C";
- let matches: Vec<_> = Matches::new(s).map(|m| m.as_str()).collect();
- assert_eq!(&[s], matches.as_slice());
- }
-
- #[test]
- fn long_run_of_digits() {
- let s = "\u{1b}00000";
- let matches: Vec<_> = Matches::new(s).map(|m| m.as_str()).collect();
- assert_eq!(&[s], matches.as_slice());
- }
-
- #[test]
- fn test_ansi_iter_re_vt100() {
- let s = "\x1b(0lpq\x1b)Benglish";
- let mut iter = AnsiCodeIterator::new(s);
- assert_eq!(iter.next(), Some(("\x1b(0", true)));
- assert_eq!(iter.next(), Some(("lpq", false)));
- assert_eq!(iter.next(), Some(("\x1b)B", true)));
- assert_eq!(iter.next(), Some(("english", false)));
- }
-
- #[test]
- fn test_ansi_iter_re() {
- use crate::style;
- let s = format!("Hello {}!", style("World").red().force_styling(true));
- let mut iter = AnsiCodeIterator::new(&s);
- assert_eq!(iter.next(), Some(("Hello ", false)));
- assert_eq!(iter.current_slice(), "Hello ");
- assert_eq!(iter.rest_slice(), "\x1b[31mWorld\x1b[0m!");
- assert_eq!(iter.next(), Some(("\x1b[31m", true)));
- assert_eq!(iter.current_slice(), "Hello \x1b[31m");
- assert_eq!(iter.rest_slice(), "World\x1b[0m!");
- assert_eq!(iter.next(), Some(("World", false)));
- assert_eq!(iter.current_slice(), "Hello \x1b[31mWorld");
- assert_eq!(iter.rest_slice(), "\x1b[0m!");
- assert_eq!(iter.next(), Some(("\x1b[0m", true)));
- assert_eq!(iter.current_slice(), "Hello \x1b[31mWorld\x1b[0m");
- assert_eq!(iter.rest_slice(), "!");
- assert_eq!(iter.next(), Some(("!", false)));
- assert_eq!(iter.current_slice(), "Hello \x1b[31mWorld\x1b[0m!");
- assert_eq!(iter.rest_slice(), "");
- assert_eq!(iter.next(), None);
- }
-
- #[test]
- fn test_ansi_iter_re_on_multi() {
- use crate::style;
- let s = format!("{}", style("a").red().bold().force_styling(true));
- let mut iter = AnsiCodeIterator::new(&s);
- assert_eq!(iter.next(), Some(("\x1b[31m", true)));
- assert_eq!(iter.current_slice(), "\x1b[31m");
- assert_eq!(iter.rest_slice(), "\x1b[1ma\x1b[0m");
- assert_eq!(iter.next(), Some(("\x1b[1m", true)));
- assert_eq!(iter.current_slice(), "\x1b[31m\x1b[1m");
- assert_eq!(iter.rest_slice(), "a\x1b[0m");
- assert_eq!(iter.next(), Some(("a", false)));
- assert_eq!(iter.current_slice(), "\x1b[31m\x1b[1ma");
- assert_eq!(iter.rest_slice(), "\x1b[0m");
- assert_eq!(iter.next(), Some(("\x1b[0m", true)));
- assert_eq!(iter.current_slice(), "\x1b[31m\x1b[1ma\x1b[0m");
- assert_eq!(iter.rest_slice(), "");
- assert_eq!(iter.next(), None);
- }
-}
diff --git a/vendor/console/src/common_term.rs b/vendor/console/src/common_term.rs
deleted file mode 100644
index 020660a..0000000
--- a/vendor/console/src/common_term.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-use std::io;
-
-use crate::term::Term;
-
-pub fn move_cursor_down(out: &Term, n: usize) -> io::Result<()> {
- if n > 0 {
- out.write_str(&format!("\x1b[{}B", n))
- } else {
- Ok(())
- }
-}
-
-pub fn move_cursor_up(out: &Term, n: usize) -> io::Result<()> {
- if n > 0 {
- out.write_str(&format!("\x1b[{}A", n))
- } else {
- Ok(())
- }
-}
-pub fn move_cursor_left(out: &Term, n: usize) -> io::Result<()> {
- if n > 0 {
- out.write_str(&format!("\x1b[{}D", n))
- } else {
- Ok(())
- }
-}
-
-pub fn move_cursor_right(out: &Term, n: usize) -> io::Result<()> {
- if n > 0 {
- out.write_str(&format!("\x1b[{}C", n))
- } else {
- Ok(())
- }
-}
-
-#[inline]
-pub fn move_cursor_to(out: &Term, x: usize, y: usize) -> io::Result<()> {
- out.write_str(&format!("\x1B[{};{}H", y + 1, x + 1))
-}
-
-pub fn clear_chars(out: &Term, n: usize) -> io::Result<()> {
- if n > 0 {
- out.write_str(&format!("\x1b[{}D\x1b[0K", n))
- } else {
- Ok(())
- }
-}
-
-#[inline]
-pub fn clear_line(out: &Term) -> io::Result<()> {
- out.write_str("\r\x1b[2K")
-}
-
-#[inline]
-pub fn clear_screen(out: &Term) -> io::Result<()> {
- out.write_str("\r\x1b[2J\r\x1b[H")
-}
-
-#[inline]
-pub fn clear_to_end_of_screen(out: &Term) -> io::Result<()> {
- out.write_str("\r\x1b[0J")
-}
-
-#[inline]
-pub fn show_cursor(out: &Term) -> io::Result<()> {
- out.write_str("\x1b[?25h")
-}
-
-#[inline]
-pub fn hide_cursor(out: &Term) -> io::Result<()> {
- out.write_str("\x1b[?25l")
-}
diff --git a/vendor/console/src/kb.rs b/vendor/console/src/kb.rs
deleted file mode 100644
index 5258c13..0000000
--- a/vendor/console/src/kb.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-/// Key mapping
-///
-/// This is an incomplete mapping of keys that are supported for reading
-/// from the keyboard.
-#[non_exhaustive]
-#[derive(Clone, PartialEq, Eq, Debug, Hash)]
-pub enum Key {
- Unknown,
- /// Unrecognized sequence containing Esc and a list of chars
- UnknownEscSeq(Vec<char>),
- ArrowLeft,
- ArrowRight,
- ArrowUp,
- ArrowDown,
- Enter,
- Escape,
- Backspace,
- Home,
- End,
- Tab,
- BackTab,
- Alt,
- Del,
- Shift,
- Insert,
- PageUp,
- PageDown,
- Char(char),
-}
diff --git a/vendor/console/src/lib.rs b/vendor/console/src/lib.rs
deleted file mode 100644
index 1b18afc..0000000
--- a/vendor/console/src/lib.rs
+++ /dev/null
@@ -1,104 +0,0 @@
-//! console is a library for Rust that provides access to various terminal
-//! features so you can build nicer looking command line interfaces. It
-//! comes with various tools and utilities for working with Terminals and
-//! formatting text.
-//!
-//! Best paired with other libraries in the family:
-//!
-//! * [dialoguer](https://docs.rs/dialoguer)
-//! * [indicatif](https://docs.rs/indicatif)
-//!
-//! # Terminal Access
-//!
-//! The terminal is abstracted through the `console::Term` type. It can
-//! either directly provide access to the connected terminal or by buffering
-//! up commands. A buffered terminal will however not be completely buffered
-//! on windows where cursor movements are currently directly passed through.
-//!
-//! Example usage:
-//!
-//! ```
-//! # fn test() -> Result<(), Box<dyn std::error::Error>> {
-//! use std::thread;
-//! use std::time::Duration;
-//!
-//! use console::Term;
-//!
-//! let term = Term::stdout();
-//! term.write_line("Hello World!")?;
-//! thread::sleep(Duration::from_millis(2000));
-//! term.clear_line()?;
-//! # Ok(()) } test().unwrap();
-//! ```
-//!
-//! # Colors and Styles
-//!
-//! `console` automaticaly detects when to use colors based on the tty flag. It also
-//! provides higher level wrappers for styling text and other things that can be
-//! displayed with the `style` function and utility types.
-//!
-//! Example usage:
-//!
-//! ```
-//! use console::style;
-//!
-//! println!("This is {} neat", style("quite").cyan());
-//! ```
-//!
-//! You can also store styles and apply them to text later:
-//!
-//! ```
-//! use console::Style;
-//!
-//! let cyan = Style::new().cyan();
-//! println!("This is {} neat", cyan.apply_to("quite"));
-//! ```
-//!
-//! # Working with ANSI Codes
-//!
-//! The crate provids the function `strip_ansi_codes` to remove ANSI codes
-//! from a string as well as `measure_text_width` to calculate the width of a
-//! string as it would be displayed by the terminal. Both of those together
-//! are useful for more complex formatting.
-//!
-//! # Unicode Width Support
-//!
-//! By default this crate depends on the `unicode-width` crate to calculate
-//! the width of terminal characters. If you do not need this you can disable
-//! the `unicode-width` feature which will cut down on dependencies.
-//!
-//! # Features
-//!
-//! By default all features are enabled. The following features exist:
-//!
-//! * `unicode-width`: adds support for unicode width calculations
-//! * `ansi-parsing`: adds support for parsing ansi codes (this adds support
-//! for stripping and taking ansi escape codes into account for length
-//! calculations).
-
-pub use crate::kb::Key;
-pub use crate::term::{
- user_attended, user_attended_stderr, Term, TermFamily, TermFeatures, TermTarget,
-};
-pub use crate::utils::{
- colors_enabled, colors_enabled_stderr, measure_text_width, pad_str, pad_str_with,
- set_colors_enabled, set_colors_enabled_stderr, style, truncate_str, Alignment, Attribute,
- Color, Emoji, Style, StyledObject,
-};
-
-#[cfg(feature = "ansi-parsing")]
-pub use crate::ansi::{strip_ansi_codes, AnsiCodeIterator};
-
-mod common_term;
-mod kb;
-mod term;
-#[cfg(unix)]
-mod unix_term;
-mod utils;
-#[cfg(target_arch = "wasm32")]
-mod wasm_term;
-#[cfg(windows)]
-mod windows_term;
-
-#[cfg(feature = "ansi-parsing")]
-mod ansi;
diff --git a/vendor/console/src/term.rs b/vendor/console/src/term.rs
deleted file mode 100644
index 0a40258..0000000
--- a/vendor/console/src/term.rs
+++ /dev/null
@@ -1,632 +0,0 @@
-use std::fmt::{Debug, Display};
-use std::io::{self, Read, Write};
-use std::sync::{Arc, Mutex};
-
-#[cfg(unix)]
-use std::os::unix::io::{AsRawFd, RawFd};
-#[cfg(windows)]
-use std::os::windows::io::{AsRawHandle, RawHandle};
-
-use crate::{kb::Key, utils::Style};
-
-#[cfg(unix)]
-trait TermWrite: Write + Debug + AsRawFd + Send {}
-#[cfg(unix)]
-impl<T: Write + Debug + AsRawFd + Send> TermWrite for T {}
-
-#[cfg(unix)]
-trait TermRead: Read + Debug + AsRawFd + Send {}
-#[cfg(unix)]
-impl<T: Read + Debug + AsRawFd + Send> TermRead for T {}
-
-#[cfg(unix)]
-#[derive(Debug, Clone)]
-pub struct ReadWritePair {
- #[allow(unused)]
- read: Arc<Mutex<dyn TermRead>>,
- write: Arc<Mutex<dyn TermWrite>>,
- style: Style,
-}
-
-/// Where the term is writing.
-#[derive(Debug, Clone)]
-pub enum TermTarget {
- Stdout,
- Stderr,
- #[cfg(unix)]
- ReadWritePair(ReadWritePair),
-}
-
-#[derive(Debug)]
-pub struct TermInner {
- target: TermTarget,
- buffer: Option<Mutex<Vec<u8>>>,
-}
-
-/// The family of the terminal.
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum TermFamily {
- /// Redirected to a file or file like thing.
- File,
- /// A standard unix terminal.
- UnixTerm,
- /// A cmd.exe like windows console.
- WindowsConsole,
- /// A dummy terminal (for instance on wasm)
- Dummy,
-}
-
-/// Gives access to the terminal features.
-#[derive(Debug, Clone)]
-pub struct TermFeatures<'a>(&'a Term);
-
-impl<'a> TermFeatures<'a> {
- /// Check if this is a real user attended terminal (`isatty`)
- #[inline]
- pub fn is_attended(&self) -> bool {
- is_a_terminal(self.0)
- }
-
- /// Check if colors are supported by this terminal.
- ///
- /// This does not check if colors are enabled. Currently all terminals
- /// are considered to support colors
- #[inline]
- pub fn colors_supported(&self) -> bool {
- is_a_color_terminal(self.0)
- }
-
- /// Check if this terminal is an msys terminal.
- ///
- /// This is sometimes useful to disable features that are known to not
- /// work on msys terminals or require special handling.
- #[inline]
- pub fn is_msys_tty(&self) -> bool {
- #[cfg(windows)]
- {
- msys_tty_on(self.0)
- }
- #[cfg(not(windows))]
- {
- false
- }
- }
-
- /// Check if this terminal wants emojis.
- #[inline]
- pub fn wants_emoji(&self) -> bool {
- self.is_attended() && wants_emoji()
- }
-
- /// Return the family of the terminal.
- #[inline]
- pub fn family(&self) -> TermFamily {
- if !self.is_attended() {
- return TermFamily::File;
- }
- #[cfg(windows)]
- {
- TermFamily::WindowsConsole
- }
- #[cfg(unix)]
- {
- TermFamily::UnixTerm
- }
- #[cfg(target_arch = "wasm32")]
- {
- TermFamily::Dummy
- }
- }
-}
-
-/// Abstraction around a terminal.
-///
-/// A terminal can be cloned. If a buffer is used it's shared across all
-/// clones which means it largely acts as a handle.
-#[derive(Clone, Debug)]
-pub struct Term {
- inner: Arc<TermInner>,
- pub(crate) is_msys_tty: bool,
- pub(crate) is_tty: bool,
-}
-
-impl Term {
- fn with_inner(inner: TermInner) -> Term {
- let mut term = Term {
- inner: Arc::new(inner),
- is_msys_tty: false,
- is_tty: false,
- };
-
- term.is_msys_tty = term.features().is_msys_tty();
- term.is_tty = term.features().is_attended();
- term
- }
-
- /// Return a new unbuffered terminal.
- #[inline]
- pub fn stdout() -> Term {
- Term::with_inner(TermInner {
- target: TermTarget::Stdout,
- buffer: None,
- })
- }
-
- /// Return a new unbuffered terminal to stderr.
- #[inline]
- pub fn stderr() -> Term {
- Term::with_inner(TermInner {
- target: TermTarget::Stderr,
- buffer: None,
- })
- }
-
- /// Return a new buffered terminal.
- pub fn buffered_stdout() -> Term {
- Term::with_inner(TermInner {
- target: TermTarget::Stdout,
- buffer: Some(Mutex::new(vec![])),
- })
- }
-
- /// Return a new buffered terminal to stderr.
- pub fn buffered_stderr() -> Term {
- Term::with_inner(TermInner {
- target: TermTarget::Stderr,
- buffer: Some(Mutex::new(vec![])),
- })
- }
-
- /// Return a terminal for the given Read/Write pair styled like stderr.
- #[cfg(unix)]
- pub fn read_write_pair<R, W>(read: R, write: W) -> Term
- where
- R: Read + Debug + AsRawFd + Send + 'static,
- W: Write + Debug + AsRawFd + Send + 'static,
- {
- Self::read_write_pair_with_style(read, write, Style::new().for_stderr())
- }
-
- /// Return a terminal for the given Read/Write pair.
- #[cfg(unix)]
- pub fn read_write_pair_with_style<R, W>(read: R, write: W, style: Style) -> Term
- where
- R: Read + Debug + AsRawFd + Send + 'static,
- W: Write + Debug + AsRawFd + Send + 'static,
- {
- Term::with_inner(TermInner {
- target: TermTarget::ReadWritePair(ReadWritePair {
- read: Arc::new(Mutex::new(read)),
- write: Arc::new(Mutex::new(write)),
- style,
- }),
- buffer: None,
- })
- }
-
- /// Return the style for this terminal.
- #[inline]
- pub fn style(&self) -> Style {
- match self.inner.target {
- TermTarget::Stderr => Style::new().for_stderr(),
- TermTarget::Stdout => Style::new().for_stdout(),
- #[cfg(unix)]
- TermTarget::ReadWritePair(ReadWritePair { ref style, .. }) => style.clone(),
- }
- }
-
- /// Return the target of this terminal.
- #[inline]
- pub fn target(&self) -> TermTarget {
- self.inner.target.clone()
- }
-
- #[doc(hidden)]
- pub fn write_str(&self, s: &str) -> io::Result<()> {
- match self.inner.buffer {
- Some(ref buffer) => buffer.lock().unwrap().write_all(s.as_bytes()),
- None => self.write_through(s.as_bytes()),
- }
- }
-
- /// Write a string to the terminal and add a newline.
- pub fn write_line(&self, s: &str) -> io::Result<()> {
- match self.inner.buffer {
- Some(ref mutex) => {
- let mut buffer = mutex.lock().unwrap();
- buffer.extend_from_slice(s.as_bytes());
- buffer.push(b'\n');
- Ok(())
- }
- None => self.write_through(format!("{}\n", s).as_bytes()),
- }
- }
-
- /// Read a single character from the terminal.
- ///
- /// This does not echo the character and blocks until a single character
- /// or complete key chord is entered. If the terminal is not user attended
- /// the return value will be an error.
- pub fn read_char(&self) -> io::Result<char> {
- if !self.is_tty {
- return Err(io::Error::new(
- io::ErrorKind::NotConnected,
- "Not a terminal",
- ));
- }
- loop {
- match self.read_key()? {
- Key::Char(c) => {
- return Ok(c);
- }
- Key::Enter => {
- return Ok('\n');
- }
- _ => {}
- }
- }
- }
-
- /// Read a single key form the terminal.
- ///
- /// This does not echo anything. If the terminal is not user attended
- /// the return value will always be the unknown key.
- pub fn read_key(&self) -> io::Result<Key> {
- if !self.is_tty {
- Ok(Key::Unknown)
- } else {
- read_single_key()
- }
- }
-
- /// Read one line of input.
- ///
- /// This does not include the trailing newline. If the terminal is not
- /// user attended the return value will always be an empty string.
- pub fn read_line(&self) -> io::Result<String> {
- if !self.is_tty {
- return Ok("".into());
- }
- let mut rv = String::new();
- io::stdin().read_line(&mut rv)?;
- let len = rv.trim_end_matches(&['\r', '\n'][..]).len();
- rv.truncate(len);
- Ok(rv)
- }
-
- /// Read one line of input with initial text.
- ///
- /// This does not include the trailing newline. If the terminal is not
- /// user attended the return value will always be an empty string.
- pub fn read_line_initial_text(&self, initial: &str) -> io::Result<String> {
- if !self.is_tty {
- return Ok("".into());
- }
- self.write_str(initial)?;
-
- let mut chars: Vec<char> = initial.chars().collect();
-
- loop {
- match self.read_key()? {
- Key::Backspace => {
- if chars.pop().is_some() {
- self.clear_chars(1)?;
- }
- self.flush()?;
- }
- Key::Char(chr) => {
- chars.push(chr);
- let mut bytes_char = [0; 4];
- chr.encode_utf8(&mut bytes_char);
- self.write_str(chr.encode_utf8(&mut bytes_char))?;
- self.flush()?;
- }
- Key::Enter => {
- self.write_line("")?;
- break;
- }
- _ => (),
- }
- }
- Ok(chars.iter().collect::<String>())
- }
-
- /// Read a line of input securely.
- ///
- /// This is similar to `read_line` but will not echo the output. This
- /// also switches the terminal into a different mode where not all
- /// characters might be accepted.
- pub fn read_secure_line(&self) -> io::Result<String> {
- if !self.is_tty {
- return Ok("".into());
- }
- match read_secure() {
- Ok(rv) => {
- self.write_line("")?;
- Ok(rv)
- }
- Err(err) => Err(err),
- }
- }
-
- /// Flush internal buffers.
- ///
- /// This forces the contents of the internal buffer to be written to
- /// the terminal. This is unnecessary for unbuffered terminals which
- /// will automatically flush.
- pub fn flush(&self) -> io::Result<()> {
- if let Some(ref buffer) = self.inner.buffer {
- let mut buffer = buffer.lock().unwrap();
- if !buffer.is_empty() {
- self.write_through(&buffer[..])?;
- buffer.clear();
- }
- }
- Ok(())
- }
-
- /// Check if the terminal is indeed a terminal.
- #[inline]
- pub fn is_term(&self) -> bool {
- self.is_tty
- }
-
- /// Check for common terminal features.
- #[inline]
- pub fn features(&self) -> TermFeatures<'_> {
- TermFeatures(self)
- }
-
- /// Return the terminal size in rows and columns or gets sensible defaults.
- #[inline]
- pub fn size(&self) -> (u16, u16) {
- self.size_checked().unwrap_or((24, DEFAULT_WIDTH))
- }
-
- /// Return the terminal size in rows and columns.
- ///
- /// If the size cannot be reliably determined `None` is returned.
- #[inline]
- pub fn size_checked(&self) -> Option<(u16, u16)> {
- terminal_size(self)
- }
-
- /// Move the cursor to row `x` and column `y`. Values are 0-based.
- #[inline]
- pub fn move_cursor_to(&self, x: usize, y: usize) -> io::Result<()> {
- move_cursor_to(self, x, y)
- }
-
- /// Move the cursor up by `n` lines, if possible.
- ///
- /// If there are less than `n` lines above the current cursor position,
- /// the cursor is moved to the top line of the terminal (i.e., as far up as possible).
- #[inline]
- pub fn move_cursor_up(&self, n: usize) -> io::Result<()> {
- move_cursor_up(self, n)
- }
-
- /// Move the cursor down by `n` lines, if possible.
- ///
- /// If there are less than `n` lines below the current cursor position,
- /// the cursor is moved to the bottom line of the terminal (i.e., as far down as possible).
- #[inline]
- pub fn move_cursor_down(&self, n: usize) -> io::Result<()> {
- move_cursor_down(self, n)
- }
-
- /// Move the cursor `n` characters to the left, if possible.
- ///
- /// If there are fewer than `n` characters to the left of the current cursor position,
- /// the cursor is moved to the beginning of the line (i.e., as far to the left as possible).
- #[inline]
- pub fn move_cursor_left(&self, n: usize) -> io::Result<()> {
- move_cursor_left(self, n)
- }
-
- /// Move the cursor `n` characters to the right.
- ///
- /// If there are fewer than `n` characters to the right of the current cursor position,
- /// the cursor is moved to the end of the current line (i.e., as far to the right as possible).
- #[inline]
- pub fn move_cursor_right(&self, n: usize) -> io::Result<()> {
- move_cursor_right(self, n)
- }
-
- /// Clear the current line.
- ///
- /// Position the cursor at the beginning of the current line.
- #[inline]
- pub fn clear_line(&self) -> io::Result<()> {
- clear_line(self)
- }
-
- /// Clear the last `n` lines before the current line.
- ///
- /// Position the cursor at the beginning of the first line that was cleared.
- pub fn clear_last_lines(&self, n: usize) -> io::Result<()> {
- self.move_cursor_up(n)?;
- for _ in 0..n {
- self.clear_line()?;
- self.move_cursor_down(1)?;
- }
- self.move_cursor_up(n)?;
- Ok(())
- }
-
- /// Clear the entire screen.
- ///
- /// Move the cursor to the upper left corner of the screen.
- #[inline]
- pub fn clear_screen(&self) -> io::Result<()> {
- clear_screen(self)
- }
-
- /// Clear everything from the current cursor position to the end of the screen.
- /// The cursor stays in its position.
- #[inline]
- pub fn clear_to_end_of_screen(&self) -> io::Result<()> {
- clear_to_end_of_screen(self)
- }
-
- /// Clear the last `n` characters of the current line.
- #[inline]
- pub fn clear_chars(&self, n: usize) -> io::Result<()> {
- clear_chars(self, n)
- }
-
- /// Set the terminal title.
- pub fn set_title<T: Display>(&self, title: T) {
- if !self.is_tty {
- return;
- }
- set_title(title);
- }
-
- /// Make the cursor visible again.
- #[inline]
- pub fn show_cursor(&self) -> io::Result<()> {
- show_cursor(self)
- }
-
- /// Hide the cursor.
- #[inline]
- pub fn hide_cursor(&self) -> io::Result<()> {
- hide_cursor(self)
- }
-
- // helpers
-
- #[cfg(all(windows, feature = "windows-console-colors"))]
- fn write_through(&self, bytes: &[u8]) -> io::Result<()> {
- if self.is_msys_tty || !self.is_tty {
- self.write_through_common(bytes)
- } else {
- match self.inner.target {
- TermTarget::Stdout => console_colors(self, Console::stdout()?, bytes),
- TermTarget::Stderr => console_colors(self, Console::stderr()?, bytes),
- }
- }
- }
-
- #[cfg(not(all(windows, feature = "windows-console-colors")))]
- fn write_through(&self, bytes: &[u8]) -> io::Result<()> {
- self.write_through_common(bytes)
- }
-
- pub(crate) fn write_through_common(&self, bytes: &[u8]) -> io::Result<()> {
- match self.inner.target {
- TermTarget::Stdout => {
- io::stdout().write_all(bytes)?;
- io::stdout().flush()?;
- }
- TermTarget::Stderr => {
- io::stderr().write_all(bytes)?;
- io::stderr().flush()?;
- }
- #[cfg(unix)]
- TermTarget::ReadWritePair(ReadWritePair { ref write, .. }) => {
- let mut write = write.lock().unwrap();
- write.write_all(bytes)?;
- write.flush()?;
- }
- }
- Ok(())
- }
-}
-
-/// A fast way to check if the application has a user attended for stdout.
-///
-/// This means that stdout is connected to a terminal instead of a
-/// file or redirected by other means. This is a shortcut for
-/// checking the `is_attended` feature on the stdout terminal.
-#[inline]
-pub fn user_attended() -> bool {
- Term::stdout().features().is_attended()
-}
-
-/// A fast way to check if the application has a user attended for stderr.
-///
-/// This means that stderr is connected to a terminal instead of a
-/// file or redirected by other means. This is a shortcut for
-/// checking the `is_attended` feature on the stderr terminal.
-#[inline]
-pub fn user_attended_stderr() -> bool {
- Term::stderr().features().is_attended()
-}
-
-#[cfg(unix)]
-impl AsRawFd for Term {
- fn as_raw_fd(&self) -> RawFd {
- match self.inner.target {
- TermTarget::Stdout => libc::STDOUT_FILENO,
- TermTarget::Stderr => libc::STDERR_FILENO,
- TermTarget::ReadWritePair(ReadWritePair { ref write, .. }) => {
- write.lock().unwrap().as_raw_fd()
- }
- }
- }
-}
-
-#[cfg(windows)]
-impl AsRawHandle for Term {
- fn as_raw_handle(&self) -> RawHandle {
- use windows_sys::Win32::System::Console::{
- GetStdHandle, STD_ERROR_HANDLE, STD_OUTPUT_HANDLE,
- };
-
- unsafe {
- GetStdHandle(match self.inner.target {
- TermTarget::Stdout => STD_OUTPUT_HANDLE,
- TermTarget::Stderr => STD_ERROR_HANDLE,
- }) as RawHandle
- }
- }
-}
-
-impl Write for Term {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- match self.inner.buffer {
- Some(ref buffer) => buffer.lock().unwrap().write_all(buf),
- None => self.write_through(buf),
- }?;
- Ok(buf.len())
- }
-
- fn flush(&mut self) -> io::Result<()> {
- Term::flush(self)
- }
-}
-
-impl<'a> Write for &'a Term {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- match self.inner.buffer {
- Some(ref buffer) => buffer.lock().unwrap().write_all(buf),
- None => self.write_through(buf),
- }?;
- Ok(buf.len())
- }
-
- fn flush(&mut self) -> io::Result<()> {
- Term::flush(self)
- }
-}
-
-impl Read for Term {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- io::stdin().read(buf)
- }
-}
-
-impl<'a> Read for &'a Term {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- io::stdin().read(buf)
- }
-}
-
-#[cfg(unix)]
-pub use crate::unix_term::*;
-#[cfg(target_arch = "wasm32")]
-pub use crate::wasm_term::*;
-#[cfg(windows)]
-pub use crate::windows_term::*;
diff --git a/vendor/console/src/unix_term.rs b/vendor/console/src/unix_term.rs
deleted file mode 100644
index 8e1e592..0000000
--- a/vendor/console/src/unix_term.rs
+++ /dev/null
@@ -1,362 +0,0 @@
-use std::env;
-use std::fmt::Display;
-use std::fs;
-use std::io;
-use std::io::{BufRead, BufReader};
-use std::mem;
-use std::os::unix::io::AsRawFd;
-use std::ptr;
-use std::str;
-
-use crate::kb::Key;
-use crate::term::Term;
-
-pub use crate::common_term::*;
-
-pub const DEFAULT_WIDTH: u16 = 80;
-
-#[inline]
-pub fn is_a_terminal(out: &Term) -> bool {
- unsafe { libc::isatty(out.as_raw_fd()) != 0 }
-}
-
-pub fn is_a_color_terminal(out: &Term) -> bool {
- if !is_a_terminal(out) {
- return false;
- }
-
- if env::var("NO_COLOR").is_ok() {
- return false;
- }
-
- match env::var("TERM") {
- Ok(term) => term != "dumb",
- Err(_) => false,
- }
-}
-
-pub fn c_result<F: FnOnce() -> libc::c_int>(f: F) -> io::Result<()> {
- let res = f();
- if res != 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(())
- }
-}
-
-pub fn terminal_size(out: &Term) -> Option<(u16, u16)> {
- unsafe {
- if libc::isatty(libc::STDOUT_FILENO) != 1 {
- return None;
- }
-
- let mut winsize: libc::winsize = std::mem::zeroed();
-
- // FIXME: ".into()" used as a temporary fix for a libc bug
- // https://github.com/rust-lang/libc/pull/704
- #[allow(clippy::useless_conversion)]
- libc::ioctl(out.as_raw_fd(), libc::TIOCGWINSZ.into(), &mut winsize);
- if winsize.ws_row > 0 && winsize.ws_col > 0 {
- Some((winsize.ws_row as u16, winsize.ws_col as u16))
- } else {
- None
- }
- }
-}
-
-pub fn read_secure() -> io::Result<String> {
- let f_tty;
- let fd = unsafe {
- if libc::isatty(libc::STDIN_FILENO) == 1 {
- f_tty = None;
- libc::STDIN_FILENO
- } else {
- let f = fs::OpenOptions::new()
- .read(true)
- .write(true)
- .open("/dev/tty")?;
- let fd = f.as_raw_fd();
- f_tty = Some(BufReader::new(f));
- fd
- }
- };
-
- let mut termios = core::mem::MaybeUninit::uninit();
- c_result(|| unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) })?;
- let mut termios = unsafe { termios.assume_init() };
- let original = termios;
- termios.c_lflag &= !libc::ECHO;
- c_result(|| unsafe { libc::tcsetattr(fd, libc::TCSAFLUSH, &termios) })?;
- let mut rv = String::new();
-
- let read_rv = if let Some(mut f) = f_tty {
- f.read_line(&mut rv)
- } else {
- io::stdin().read_line(&mut rv)
- };
-
- c_result(|| unsafe { libc::tcsetattr(fd, libc::TCSAFLUSH, &original) })?;
-
- read_rv.map(|_| {
- let len = rv.trim_end_matches(&['\r', '\n'][..]).len();
- rv.truncate(len);
- rv
- })
-}
-
-fn poll_fd(fd: i32, timeout: i32) -> io::Result<bool> {
- let mut pollfd = libc::pollfd {
- fd,
- events: libc::POLLIN,
- revents: 0,
- };
- let ret = unsafe { libc::poll(&mut pollfd as *mut _, 1, timeout) };
- if ret < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(pollfd.revents & libc::POLLIN != 0)
- }
-}
-
-#[cfg(target_os = "macos")]
-fn select_fd(fd: i32, timeout: i32) -> io::Result<bool> {
- unsafe {
- let mut read_fd_set: libc::fd_set = mem::zeroed();
-
- let mut timeout_val;
- let timeout = if timeout < 0 {
- ptr::null_mut()
- } else {
- timeout_val = libc::timeval {
- tv_sec: (timeout / 1000) as _,
- tv_usec: (timeout * 1000) as _,
- };
- &mut timeout_val
- };
-
- libc::FD_ZERO(&mut read_fd_set);
- libc::FD_SET(fd, &mut read_fd_set);
- let ret = libc::select(
- fd + 1,
- &mut read_fd_set,
- ptr::null_mut(),
- ptr::null_mut(),
- timeout,
- );
- if ret < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(libc::FD_ISSET(fd, &read_fd_set))
- }
- }
-}
-
-fn select_or_poll_term_fd(fd: i32, timeout: i32) -> io::Result<bool> {
- // There is a bug on macos that ttys cannot be polled, only select()
- // works. However given how problematic select is in general, we
- // normally want to use poll there too.
- #[cfg(target_os = "macos")]
- {
- if unsafe { libc::isatty(fd) == 1 } {
- return select_fd(fd, timeout);
- }
- }
- poll_fd(fd, timeout)
-}
-
-fn read_single_char(fd: i32) -> io::Result<Option<char>> {
- // timeout of zero means that it will not block
- let is_ready = select_or_poll_term_fd(fd, 0)?;
-
- if is_ready {
- // if there is something to be read, take 1 byte from it
- let mut buf: [u8; 1] = [0];
-
- read_bytes(fd, &mut buf, 1)?;
- Ok(Some(buf[0] as char))
- } else {
- //there is nothing to be read
- Ok(None)
- }
-}
-
-// Similar to libc::read. Read count bytes into slice buf from descriptor fd.
-// If successful, return the number of bytes read.
-// Will return an error if nothing was read, i.e when called at end of file.
-fn read_bytes(fd: i32, buf: &mut [u8], count: u8) -> io::Result<u8> {
- let read = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, count as usize) };
- if read < 0 {
- Err(io::Error::last_os_error())
- } else if read == 0 {
- Err(io::Error::new(
- io::ErrorKind::UnexpectedEof,
- "Reached end of file",
- ))
- } else if buf[0] == b'\x03' {
- Err(io::Error::new(
- io::ErrorKind::Interrupted,
- "read interrupted",
- ))
- } else {
- Ok(read as u8)
- }
-}
-
-fn read_single_key_impl(fd: i32) -> Result<Key, io::Error> {
- loop {
- match read_single_char(fd)? {
- Some('\x1b') => {
- // Escape was read, keep reading in case we find a familiar key
- break if let Some(c1) = read_single_char(fd)? {
- if c1 == '[' {
- if let Some(c2) = read_single_char(fd)? {
- match c2 {
- 'A' => Ok(Key::ArrowUp),
- 'B' => Ok(Key::ArrowDown),
- 'C' => Ok(Key::ArrowRight),
- 'D' => Ok(Key::ArrowLeft),
- 'H' => Ok(Key::Home),
- 'F' => Ok(Key::End),
- 'Z' => Ok(Key::BackTab),
- _ => {
- let c3 = read_single_char(fd)?;
- if let Some(c3) = c3 {
- if c3 == '~' {
- match c2 {
- '1' => Ok(Key::Home), // tmux
- '2' => Ok(Key::Insert),
- '3' => Ok(Key::Del),
- '4' => Ok(Key::End), // tmux
- '5' => Ok(Key::PageUp),
- '6' => Ok(Key::PageDown),
- '7' => Ok(Key::Home), // xrvt
- '8' => Ok(Key::End), // xrvt
- _ => Ok(Key::UnknownEscSeq(vec![c1, c2, c3])),
- }
- } else {
- Ok(Key::UnknownEscSeq(vec![c1, c2, c3]))
- }
- } else {
- // \x1b[ and 1 more char
- Ok(Key::UnknownEscSeq(vec![c1, c2]))
- }
- }
- }
- } else {
- // \x1b[ and no more input
- Ok(Key::UnknownEscSeq(vec![c1]))
- }
- } else {
- // char after escape is not [
- Ok(Key::UnknownEscSeq(vec![c1]))
- }
- } else {
- //nothing after escape
- Ok(Key::Escape)
- };
- }
- Some(c) => {
- let byte = c as u8;
- let mut buf: [u8; 4] = [byte, 0, 0, 0];
-
- break if byte & 224u8 == 192u8 {
- // a two byte unicode character
- read_bytes(fd, &mut buf[1..], 1)?;
- Ok(key_from_utf8(&buf[..2]))
- } else if byte & 240u8 == 224u8 {
- // a three byte unicode character
- read_bytes(fd, &mut buf[1..], 2)?;
- Ok(key_from_utf8(&buf[..3]))
- } else if byte & 248u8 == 240u8 {
- // a four byte unicode character
- read_bytes(fd, &mut buf[1..], 3)?;
- Ok(key_from_utf8(&buf[..4]))
- } else {
- Ok(match c {
- '\n' | '\r' => Key::Enter,
- '\x7f' => Key::Backspace,
- '\t' => Key::Tab,
- '\x01' => Key::Home, // Control-A (home)
- '\x05' => Key::End, // Control-E (end)
- '\x08' => Key::Backspace, // Control-H (8) (Identical to '\b')
- _ => Key::Char(c),
- })
- };
- }
- None => {
- // there is no subsequent byte ready to be read, block and wait for input
- // negative timeout means that it will block indefinitely
- match select_or_poll_term_fd(fd, -1) {
- Ok(_) => continue,
- Err(_) => break Err(io::Error::last_os_error()),
- }
- }
- }
- }
-}
-
-pub fn read_single_key() -> io::Result<Key> {
- let tty_f;
- let fd = unsafe {
- if libc::isatty(libc::STDIN_FILENO) == 1 {
- libc::STDIN_FILENO
- } else {
- tty_f = fs::OpenOptions::new()
- .read(true)
- .write(true)
- .open("/dev/tty")?;
- tty_f.as_raw_fd()
- }
- };
- let mut termios = core::mem::MaybeUninit::uninit();
- c_result(|| unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) })?;
- let mut termios = unsafe { termios.assume_init() };
- let original = termios;
- unsafe { libc::cfmakeraw(&mut termios) };
- termios.c_oflag = original.c_oflag;
- c_result(|| unsafe { libc::tcsetattr(fd, libc::TCSADRAIN, &termios) })?;
- let rv: io::Result<Key> = read_single_key_impl(fd);
- c_result(|| unsafe { libc::tcsetattr(fd, libc::TCSADRAIN, &original) })?;
-
- // if the user hit ^C we want to signal SIGINT to outselves.
- if let Err(ref err) = rv {
- if err.kind() == io::ErrorKind::Interrupted {
- unsafe {
- libc::raise(libc::SIGINT);
- }
- }
- }
-
- rv
-}
-
-pub fn key_from_utf8(buf: &[u8]) -> Key {
- if let Ok(s) = str::from_utf8(buf) {
- if let Some(c) = s.chars().next() {
- return Key::Char(c);
- }
- }
- Key::Unknown
-}
-
-#[cfg(not(target_os = "macos"))]
-lazy_static::lazy_static! {
- static ref IS_LANG_UTF8: bool = match std::env::var("LANG") {
- Ok(lang) => lang.to_uppercase().ends_with("UTF-8"),
- _ => false,
- };
-}
-
-#[cfg(target_os = "macos")]
-pub fn wants_emoji() -> bool {
- true
-}
-
-#[cfg(not(target_os = "macos"))]
-pub fn wants_emoji() -> bool {
- *IS_LANG_UTF8
-}
-
-pub fn set_title<T: Display>(title: T) {
- print!("\x1b]0;{}\x07", title);
-}
diff --git a/vendor/console/src/utils.rs b/vendor/console/src/utils.rs
deleted file mode 100644
index 9e6b942..0000000
--- a/vendor/console/src/utils.rs
+++ /dev/null
@@ -1,962 +0,0 @@
-use std::borrow::Cow;
-use std::collections::BTreeSet;
-use std::env;
-use std::fmt;
-use std::sync::atomic::{AtomicBool, Ordering};
-
-use lazy_static::lazy_static;
-
-use crate::term::{wants_emoji, Term};
-
-#[cfg(feature = "ansi-parsing")]
-use crate::ansi::{strip_ansi_codes, AnsiCodeIterator};
-
-#[cfg(not(feature = "ansi-parsing"))]
-fn strip_ansi_codes(s: &str) -> &str {
- s
-}
-
-fn default_colors_enabled(out: &Term) -> bool {
- (out.features().colors_supported()
- && &env::var("CLICOLOR").unwrap_or_else(|_| "1".into()) != "0")
- || &env::var("CLICOLOR_FORCE").unwrap_or_else(|_| "0".into()) != "0"
-}
-
-lazy_static! {
- static ref STDOUT_COLORS: AtomicBool = AtomicBool::new(default_colors_enabled(&Term::stdout()));
- static ref STDERR_COLORS: AtomicBool = AtomicBool::new(default_colors_enabled(&Term::stderr()));
-}
-
-/// Returns `true` if colors should be enabled for stdout.
-///
-/// This honors the [clicolors spec](http://bixense.com/clicolors/).
-///
-/// * `CLICOLOR != 0`: ANSI colors are supported and should be used when the program isn't piped.
-/// * `CLICOLOR == 0`: Don't output ANSI color escape codes.
-/// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what.
-#[inline]
-pub fn colors_enabled() -> bool {
- STDOUT_COLORS.load(Ordering::Relaxed)
-}
-
-/// Forces colorization on or off for stdout.
-///
-/// This overrides the default for the current process and changes the return value of the
-/// `colors_enabled` function.
-#[inline]
-pub fn set_colors_enabled(val: bool) {
- STDOUT_COLORS.store(val, Ordering::Relaxed)
-}
-
-/// Returns `true` if colors should be enabled for stderr.
-///
-/// This honors the [clicolors spec](http://bixense.com/clicolors/).
-///
-/// * `CLICOLOR != 0`: ANSI colors are supported and should be used when the program isn't piped.
-/// * `CLICOLOR == 0`: Don't output ANSI color escape codes.
-/// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what.
-#[inline]
-pub fn colors_enabled_stderr() -> bool {
- STDERR_COLORS.load(Ordering::Relaxed)
-}
-
-/// Forces colorization on or off for stderr.
-///
-/// This overrides the default for the current process and changes the return value of the
-/// `colors_enabled` function.
-#[inline]
-pub fn set_colors_enabled_stderr(val: bool) {
- STDERR_COLORS.store(val, Ordering::Relaxed)
-}
-
-/// Measure the width of a string in terminal characters.
-pub fn measure_text_width(s: &str) -> usize {
- str_width(&strip_ansi_codes(s))
-}
-
-/// A terminal color.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Color {
- Black,
- Red,
- Green,
- Yellow,
- Blue,
- Magenta,
- Cyan,
- White,
- Color256(u8),
-}
-
-impl Color {
- #[inline]
- fn ansi_num(self) -> usize {
- match self {
- Color::Black => 0,
- Color::Red => 1,
- Color::Green => 2,
- Color::Yellow => 3,
- Color::Blue => 4,
- Color::Magenta => 5,
- Color::Cyan => 6,
- Color::White => 7,
- Color::Color256(x) => x as usize,
- }
- }
-
- #[inline]
- fn is_color256(self) -> bool {
- #[allow(clippy::match_like_matches_macro)]
- match self {
- Color::Color256(_) => true,
- _ => false,
- }
- }
-}
-
-/// A terminal style attribute.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
-pub enum Attribute {
- Bold,
- Dim,
- Italic,
- Underlined,
- Blink,
- BlinkFast,
- Reverse,
- Hidden,
- StrikeThrough,
-}
-
-impl Attribute {
- #[inline]
- fn ansi_num(self) -> usize {
- match self {
- Attribute::Bold => 1,
- Attribute::Dim => 2,
- Attribute::Italic => 3,
- Attribute::Underlined => 4,
- Attribute::Blink => 5,
- Attribute::BlinkFast => 6,
- Attribute::Reverse => 7,
- Attribute::Hidden => 8,
- Attribute::StrikeThrough => 9,
- }
- }
-}
-
-/// Defines the alignment for padding operations.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Alignment {
- Left,
- Center,
- Right,
-}
-
-/// A stored style that can be applied.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct Style {
- fg: Option<Color>,
- bg: Option<Color>,
- fg_bright: bool,
- bg_bright: bool,
- attrs: BTreeSet<Attribute>,
- force: Option<bool>,
- for_stderr: bool,
-}
-
-impl Default for Style {
- fn default() -> Style {
- Style::new()
- }
-}
-
-impl Style {
- /// Returns an empty default style.
- pub fn new() -> Style {
- Style {
- fg: None,
- bg: None,
- fg_bright: false,
- bg_bright: false,
- attrs: BTreeSet::new(),
- force: None,
- for_stderr: false,
- }
- }
-
- /// Creates a style from a dotted string.
- ///
- /// Effectively the string is split at each dot and then the
- /// terms in between are applied. For instance `red.on_blue` will
- /// create a string that is red on blue background. `9.on_12` is
- /// the same, but using 256 color numbers. Unknown terms are
- /// ignored.
- pub fn from_dotted_str(s: &str) -> Style {
- let mut rv = Style::new();
- for part in s.split('.') {
- rv = match part {
- "black" => rv.black(),
- "red" => rv.red(),
- "green" => rv.green(),
- "yellow" => rv.yellow(),
- "blue" => rv.blue(),
- "magenta" => rv.magenta(),
- "cyan" => rv.cyan(),
- "white" => rv.white(),
- "bright" => rv.bright(),
- "on_black" => rv.on_black(),
- "on_red" => rv.on_red(),
- "on_green" => rv.on_green(),
- "on_yellow" => rv.on_yellow(),
- "on_blue" => rv.on_blue(),
- "on_magenta" => rv.on_magenta(),
- "on_cyan" => rv.on_cyan(),
- "on_white" => rv.on_white(),
- "on_bright" => rv.on_bright(),
- "bold" => rv.bold(),
- "dim" => rv.dim(),
- "underlined" => rv.underlined(),
- "blink" => rv.blink(),
- "blink_fast" => rv.blink_fast(),
- "reverse" => rv.reverse(),
- "hidden" => rv.hidden(),
- "strikethrough" => rv.strikethrough(),
- on_c if on_c.starts_with("on_") => {
- if let Ok(n) = on_c[3..].parse::<u8>() {
- rv.on_color256(n)
- } else {
- continue;
- }
- }
- c => {
- if let Ok(n) = c.parse::<u8>() {
- rv.color256(n)
- } else {
- continue;
- }
- }
- };
- }
- rv
- }
-
- /// Apply the style to something that can be displayed.
- pub fn apply_to<D>(&self, val: D) -> StyledObject<D> {
- StyledObject {
- style: self.clone(),
- val,
- }
- }
-
- /// Forces styling on or off.
- ///
- /// This overrides the automatic detection.
- #[inline]
- pub fn force_styling(mut self, value: bool) -> Style {
- self.force = Some(value);
- self
- }
-
- /// Specifies that style is applying to something being written on stderr.
- #[inline]
- pub fn for_stderr(mut self) -> Style {
- self.for_stderr = true;
- self
- }
-
- /// Specifies that style is applying to something being written on stdout.
- ///
- /// This is the default behaviour.
- #[inline]
- pub fn for_stdout(mut self) -> Style {
- self.for_stderr = false;
- self
- }
-
- /// Sets a foreground color.
- #[inline]
- pub fn fg(mut self, color: Color) -> Style {
- self.fg = Some(color);
- self
- }
-
- /// Sets a background color.
- #[inline]
- pub fn bg(mut self, color: Color) -> Style {
- self.bg = Some(color);
- self
- }
-
- /// Adds a attr.
- #[inline]
- pub fn attr(mut self, attr: Attribute) -> Style {
- self.attrs.insert(attr);
- self
- }
-
- #[inline]
- pub fn black(self) -> Style {
- self.fg(Color::Black)
- }
- #[inline]
- pub fn red(self) -> Style {
- self.fg(Color::Red)
- }
- #[inline]
- pub fn green(self) -> Style {
- self.fg(Color::Green)
- }
- #[inline]
- pub fn yellow(self) -> Style {
- self.fg(Color::Yellow)
- }
- #[inline]
- pub fn blue(self) -> Style {
- self.fg(Color::Blue)
- }
- #[inline]
- pub fn magenta(self) -> Style {
- self.fg(Color::Magenta)
- }
- #[inline]
- pub fn cyan(self) -> Style {
- self.fg(Color::Cyan)
- }
- #[inline]
- pub fn white(self) -> Style {
- self.fg(Color::White)
- }
- #[inline]
- pub fn color256(self, color: u8) -> Style {
- self.fg(Color::Color256(color))
- }
-
- #[inline]
- pub fn bright(mut self) -> Style {
- self.fg_bright = true;
- self
- }
-
- #[inline]
- pub fn on_black(self) -> Style {
- self.bg(Color::Black)
- }
- #[inline]
- pub fn on_red(self) -> Style {
- self.bg(Color::Red)
- }
- #[inline]
- pub fn on_green(self) -> Style {
- self.bg(Color::Green)
- }
- #[inline]
- pub fn on_yellow(self) -> Style {
- self.bg(Color::Yellow)
- }
- #[inline]
- pub fn on_blue(self) -> Style {
- self.bg(Color::Blue)
- }
- #[inline]
- pub fn on_magenta(self) -> Style {
- self.bg(Color::Magenta)
- }
- #[inline]
- pub fn on_cyan(self) -> Style {
- self.bg(Color::Cyan)
- }
- #[inline]
- pub fn on_white(self) -> Style {
- self.bg(Color::White)
- }
- #[inline]
- pub fn on_color256(self, color: u8) -> Style {
- self.bg(Color::Color256(color))
- }
-
- #[inline]
- pub fn on_bright(mut self) -> Style {
- self.bg_bright = true;
- self
- }
-
- #[inline]
- pub fn bold(self) -> Style {
- self.attr(Attribute::Bold)
- }
- #[inline]
- pub fn dim(self) -> Style {
- self.attr(Attribute::Dim)
- }
- #[inline]
- pub fn italic(self) -> Style {
- self.attr(Attribute::Italic)
- }
- #[inline]
- pub fn underlined(self) -> Style {
- self.attr(Attribute::Underlined)
- }
- #[inline]
- pub fn blink(self) -> Style {
- self.attr(Attribute::Blink)
- }
- #[inline]
- pub fn blink_fast(self) -> Style {
- self.attr(Attribute::BlinkFast)
- }
- #[inline]
- pub fn reverse(self) -> Style {
- self.attr(Attribute::Reverse)
- }
- #[inline]
- pub fn hidden(self) -> Style {
- self.attr(Attribute::Hidden)
- }
- #[inline]
- pub fn strikethrough(self) -> Style {
- self.attr(Attribute::StrikeThrough)
- }
-}
-
-/// Wraps an object for formatting for styling.
-///
-/// Example:
-///
-/// ```rust,no_run
-/// # use console::style;
-/// format!("Hello {}", style("World").cyan());
-/// ```
-///
-/// This is a shortcut for making a new style and applying it
-/// to a value:
-///
-/// ```rust,no_run
-/// # use console::Style;
-/// format!("Hello {}", Style::new().cyan().apply_to("World"));
-/// ```
-pub fn style<D>(val: D) -> StyledObject<D> {
- Style::new().apply_to(val)
-}
-
-/// A formatting wrapper that can be styled for a terminal.
-#[derive(Clone)]
-pub struct StyledObject<D> {
- style: Style,
- val: D,
-}
-
-impl<D> StyledObject<D> {
- /// Forces styling on or off.
- ///
- /// This overrides the automatic detection.
- #[inline]
- pub fn force_styling(mut self, value: bool) -> StyledObject<D> {
- self.style = self.style.force_styling(value);
- self
- }
-
- /// Specifies that style is applying to something being written on stderr
- #[inline]
- pub fn for_stderr(mut self) -> StyledObject<D> {
- self.style = self.style.for_stderr();
- self
- }
-
- /// Specifies that style is applying to something being written on stdout
- ///
- /// This is the default
- #[inline]
- pub fn for_stdout(mut self) -> StyledObject<D> {
- self.style = self.style.for_stdout();
- self
- }
-
- /// Sets a foreground color.
- #[inline]
- pub fn fg(mut self, color: Color) -> StyledObject<D> {
- self.style = self.style.fg(color);
- self
- }
-
- /// Sets a background color.
- #[inline]
- pub fn bg(mut self, color: Color) -> StyledObject<D> {
- self.style = self.style.bg(color);
- self
- }
-
- /// Adds a attr.
- #[inline]
- pub fn attr(mut self, attr: Attribute) -> StyledObject<D> {
- self.style = self.style.attr(attr);
- self
- }
-
- #[inline]
- pub fn black(self) -> StyledObject<D> {
- self.fg(Color::Black)
- }
- #[inline]
- pub fn red(self) -> StyledObject<D> {
- self.fg(Color::Red)
- }
- #[inline]
- pub fn green(self) -> StyledObject<D> {
- self.fg(Color::Green)
- }
- #[inline]
- pub fn yellow(self) -> StyledObject<D> {
- self.fg(Color::Yellow)
- }
- #[inline]
- pub fn blue(self) -> StyledObject<D> {
- self.fg(Color::Blue)
- }
- #[inline]
- pub fn magenta(self) -> StyledObject<D> {
- self.fg(Color::Magenta)
- }
- #[inline]
- pub fn cyan(self) -> StyledObject<D> {
- self.fg(Color::Cyan)
- }
- #[inline]
- pub fn white(self) -> StyledObject<D> {
- self.fg(Color::White)
- }
- #[inline]
- pub fn color256(self, color: u8) -> StyledObject<D> {
- self.fg(Color::Color256(color))
- }
-
- #[inline]
- pub fn bright(mut self) -> StyledObject<D> {
- self.style = self.style.bright();
- self
- }
-
- #[inline]
- pub fn on_black(self) -> StyledObject<D> {
- self.bg(Color::Black)
- }
- #[inline]
- pub fn on_red(self) -> StyledObject<D> {
- self.bg(Color::Red)
- }
- #[inline]
- pub fn on_green(self) -> StyledObject<D> {
- self.bg(Color::Green)
- }
- #[inline]
- pub fn on_yellow(self) -> StyledObject<D> {
- self.bg(Color::Yellow)
- }
- #[inline]
- pub fn on_blue(self) -> StyledObject<D> {
- self.bg(Color::Blue)
- }
- #[inline]
- pub fn on_magenta(self) -> StyledObject<D> {
- self.bg(Color::Magenta)
- }
- #[inline]
- pub fn on_cyan(self) -> StyledObject<D> {
- self.bg(Color::Cyan)
- }
- #[inline]
- pub fn on_white(self) -> StyledObject<D> {
- self.bg(Color::White)
- }
- #[inline]
- pub fn on_color256(self, color: u8) -> StyledObject<D> {
- self.bg(Color::Color256(color))
- }
-
- #[inline]
- pub fn on_bright(mut self) -> StyledObject<D> {
- self.style = self.style.on_bright();
- self
- }
-
- #[inline]
- pub fn bold(self) -> StyledObject<D> {
- self.attr(Attribute::Bold)
- }
- #[inline]
- pub fn dim(self) -> StyledObject<D> {
- self.attr(Attribute::Dim)
- }
- #[inline]
- pub fn italic(self) -> StyledObject<D> {
- self.attr(Attribute::Italic)
- }
- #[inline]
- pub fn underlined(self) -> StyledObject<D> {
- self.attr(Attribute::Underlined)
- }
- #[inline]
- pub fn blink(self) -> StyledObject<D> {
- self.attr(Attribute::Blink)
- }
- #[inline]
- pub fn blink_fast(self) -> StyledObject<D> {
- self.attr(Attribute::BlinkFast)
- }
- #[inline]
- pub fn reverse(self) -> StyledObject<D> {
- self.attr(Attribute::Reverse)
- }
- #[inline]
- pub fn hidden(self) -> StyledObject<D> {
- self.attr(Attribute::Hidden)
- }
- #[inline]
- pub fn strikethrough(self) -> StyledObject<D> {
- self.attr(Attribute::StrikeThrough)
- }
-}
-
-macro_rules! impl_fmt {
- ($name:ident) => {
- impl<D: fmt::$name> fmt::$name for StyledObject<D> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let mut reset = false;
- if self
- .style
- .force
- .unwrap_or_else(|| match self.style.for_stderr {
- true => colors_enabled_stderr(),
- false => colors_enabled(),
- })
- {
- if let Some(fg) = self.style.fg {
- if fg.is_color256() {
- write!(f, "\x1b[38;5;{}m", fg.ansi_num())?;
- } else if self.style.fg_bright {
- write!(f, "\x1b[38;5;{}m", fg.ansi_num() + 8)?;
- } else {
- write!(f, "\x1b[{}m", fg.ansi_num() + 30)?;
- }
- reset = true;
- }
- if let Some(bg) = self.style.bg {
- if bg.is_color256() {
- write!(f, "\x1b[48;5;{}m", bg.ansi_num())?;
- } else if self.style.bg_bright {
- write!(f, "\x1b[48;5;{}m", bg.ansi_num() + 8)?;
- } else {
- write!(f, "\x1b[{}m", bg.ansi_num() + 40)?;
- }
- reset = true;
- }
- for attr in &self.style.attrs {
- write!(f, "\x1b[{}m", attr.ansi_num())?;
- reset = true;
- }
- }
- fmt::$name::fmt(&self.val, f)?;
- if reset {
- write!(f, "\x1b[0m")?;
- }
- Ok(())
- }
- }
- };
-}
-
-impl_fmt!(Binary);
-impl_fmt!(Debug);
-impl_fmt!(Display);
-impl_fmt!(LowerExp);
-impl_fmt!(LowerHex);
-impl_fmt!(Octal);
-impl_fmt!(Pointer);
-impl_fmt!(UpperExp);
-impl_fmt!(UpperHex);
-
-/// "Intelligent" emoji formatter.
-///
-/// This struct intelligently wraps an emoji so that it is rendered
-/// only on systems that want emojis and renders a fallback on others.
-///
-/// Example:
-///
-/// ```rust
-/// use console::Emoji;
-/// println!("[3/4] {}Downloading ...", Emoji("🚚 ", ""));
-/// println!("[4/4] {} Done!", Emoji("✨", ":-)"));
-/// ```
-#[derive(Copy, Clone)]
-pub struct Emoji<'a, 'b>(pub &'a str, pub &'b str);
-
-impl<'a, 'b> Emoji<'a, 'b> {
- pub fn new(emoji: &'a str, fallback: &'b str) -> Emoji<'a, 'b> {
- Emoji(emoji, fallback)
- }
-}
-
-impl<'a, 'b> fmt::Display for Emoji<'a, 'b> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if wants_emoji() {
- write!(f, "{}", self.0)
- } else {
- write!(f, "{}", self.1)
- }
- }
-}
-
-fn str_width(s: &str) -> usize {
- #[cfg(feature = "unicode-width")]
- {
- use unicode_width::UnicodeWidthStr;
- s.width()
- }
- #[cfg(not(feature = "unicode-width"))]
- {
- s.chars().count()
- }
-}
-
-#[cfg(feature = "ansi-parsing")]
-fn char_width(c: char) -> usize {
- #[cfg(feature = "unicode-width")]
- {
- use unicode_width::UnicodeWidthChar;
- c.width().unwrap_or(0)
- }
- #[cfg(not(feature = "unicode-width"))]
- {
- let _c = c;
- 1
- }
-}
-
-/// Truncates a string to a certain number of characters.
-///
-/// This ensures that escape codes are not screwed up in the process.
-/// If the maximum length is hit the string will be truncated but
-/// escapes code will still be honored. If truncation takes place
-/// the tail string will be appended.
-pub fn truncate_str<'a>(s: &'a str, width: usize, tail: &str) -> Cow<'a, str> {
- #[cfg(feature = "ansi-parsing")]
- {
- use std::cmp::Ordering;
- let mut iter = AnsiCodeIterator::new(s);
- let mut length = 0;
- let mut rv = None;
-
- while let Some(item) = iter.next() {
- match item {
- (s, false) => {
- if rv.is_none() {
- if str_width(s) + length > width - str_width(tail) {
- let ts = iter.current_slice();
-
- let mut s_byte = 0;
- let mut s_width = 0;
- let rest_width = width - str_width(tail) - length;
- for c in s.chars() {
- s_byte += c.len_utf8();
- s_width += char_width(c);
- match s_width.cmp(&rest_width) {
- Ordering::Equal => break,
- Ordering::Greater => {
- s_byte -= c.len_utf8();
- break;
- }
- Ordering::Less => continue,
- }
- }
-
- let idx = ts.len() - s.len() + s_byte;
- let mut buf = ts[..idx].to_string();
- buf.push_str(tail);
- rv = Some(buf);
- }
- length += str_width(s);
- }
- }
- (s, true) => {
- if rv.is_some() {
- rv.as_mut().unwrap().push_str(s);
- }
- }
- }
- }
-
- if let Some(buf) = rv {
- Cow::Owned(buf)
- } else {
- Cow::Borrowed(s)
- }
- }
-
- #[cfg(not(feature = "ansi-parsing"))]
- {
- if s.len() <= width - tail.len() {
- Cow::Borrowed(s)
- } else {
- Cow::Owned(format!(
- "{}{}",
- s.get(..width - tail.len()).unwrap_or_default(),
- tail
- ))
- }
- }
-}
-
-/// Pads a string to fill a certain number of characters.
-///
-/// This will honor ansi codes correctly and allows you to align a string
-/// on the left, right or centered. Additionally truncation can be enabled
-/// by setting `truncate` to a string that should be used as a truncation
-/// marker.
-pub fn pad_str<'a>(
- s: &'a str,
- width: usize,
- align: Alignment,
- truncate: Option<&str>,
-) -> Cow<'a, str> {
- pad_str_with(s, width, align, truncate, ' ')
-}
-/// Pads a string with specific padding to fill a certain number of characters.
-///
-/// This will honor ansi codes correctly and allows you to align a string
-/// on the left, right or centered. Additionally truncation can be enabled
-/// by setting `truncate` to a string that should be used as a truncation
-/// marker.
-pub fn pad_str_with<'a>(
- s: &'a str,
- width: usize,
- align: Alignment,
- truncate: Option<&str>,
- pad: char,
-) -> Cow<'a, str> {
- let cols = measure_text_width(s);
-
- if cols >= width {
- return match truncate {
- None => Cow::Borrowed(s),
- Some(tail) => truncate_str(s, width, tail),
- };
- }
-
- let diff = width - cols;
-
- let (left_pad, right_pad) = match align {
- Alignment::Left => (0, diff),
- Alignment::Right => (diff, 0),
- Alignment::Center => (diff / 2, diff - diff / 2),
- };
-
- let mut rv = String::new();
- for _ in 0..left_pad {
- rv.push(pad);
- }
- rv.push_str(s);
- for _ in 0..right_pad {
- rv.push(pad);
- }
- Cow::Owned(rv)
-}
-
-#[test]
-fn test_text_width() {
- let s = style("foo")
- .red()
- .on_black()
- .bold()
- .force_styling(true)
- .to_string();
- assert_eq!(
- measure_text_width(&s),
- if cfg!(feature = "ansi-parsing") {
- 3
- } else if cfg!(feature = "unicode-width") {
- 17
- } else {
- 21
- }
- );
-}
-
-#[test]
-#[cfg(all(feature = "unicode-width", feature = "ansi-parsing"))]
-fn test_truncate_str() {
- let s = format!("foo {}", style("bar").red().force_styling(true));
- assert_eq!(
- &truncate_str(&s, 5, ""),
- &format!("foo {}", style("b").red().force_styling(true))
- );
- let s = format!("foo {}", style("bar").red().force_styling(true));
- assert_eq!(
- &truncate_str(&s, 5, "!"),
- &format!("foo {}", style("!").red().force_styling(true))
- );
- let s = format!("foo {} baz", style("bar").red().force_styling(true));
- assert_eq!(
- &truncate_str(&s, 10, "..."),
- &format!("foo {}...", style("bar").red().force_styling(true))
- );
- let s = format!("foo {}", style("バー").red().force_styling(true));
- assert_eq!(
- &truncate_str(&s, 5, ""),
- &format!("foo {}", style("").red().force_styling(true))
- );
- let s = format!("foo {}", style("バー").red().force_styling(true));
- assert_eq!(
- &truncate_str(&s, 6, ""),
- &format!("foo {}", style("バ").red().force_styling(true))
- );
-}
-
-#[test]
-fn test_truncate_str_no_ansi() {
- assert_eq!(&truncate_str("foo bar", 5, ""), "foo b");
- assert_eq!(&truncate_str("foo bar", 5, "!"), "foo !");
- assert_eq!(&truncate_str("foo bar baz", 10, "..."), "foo bar...");
-}
-
-#[test]
-fn test_pad_str() {
- assert_eq!(pad_str("foo", 7, Alignment::Center, None), " foo ");
- assert_eq!(pad_str("foo", 7, Alignment::Left, None), "foo ");
- assert_eq!(pad_str("foo", 7, Alignment::Right, None), " foo");
- assert_eq!(pad_str("foo", 3, Alignment::Left, None), "foo");
- assert_eq!(pad_str("foobar", 3, Alignment::Left, None), "foobar");
- assert_eq!(pad_str("foobar", 3, Alignment::Left, Some("")), "foo");
- assert_eq!(
- pad_str("foobarbaz", 6, Alignment::Left, Some("...")),
- "foo..."
- );
-}
-
-#[test]
-fn test_pad_str_with() {
- assert_eq!(
- pad_str_with("foo", 7, Alignment::Center, None, '#'),
- "##foo##"
- );
- assert_eq!(
- pad_str_with("foo", 7, Alignment::Left, None, '#'),
- "foo####"
- );
- assert_eq!(
- pad_str_with("foo", 7, Alignment::Right, None, '#'),
- "####foo"
- );
- assert_eq!(pad_str_with("foo", 3, Alignment::Left, None, '#'), "foo");
- assert_eq!(
- pad_str_with("foobar", 3, Alignment::Left, None, '#'),
- "foobar"
- );
- assert_eq!(
- pad_str_with("foobar", 3, Alignment::Left, Some(""), '#'),
- "foo"
- );
- assert_eq!(
- pad_str_with("foobarbaz", 6, Alignment::Left, Some("..."), '#'),
- "foo..."
- );
-}
diff --git a/vendor/console/src/wasm_term.rs b/vendor/console/src/wasm_term.rs
deleted file mode 100644
index 764bc34..0000000
--- a/vendor/console/src/wasm_term.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use std::fmt::Display;
-use std::io;
-
-use crate::kb::Key;
-use crate::term::Term;
-
-pub use crate::common_term::*;
-
-pub const DEFAULT_WIDTH: u16 = 80;
-
-#[inline]
-pub fn is_a_terminal(_out: &Term) -> bool {
- #[cfg(target = "wasm32-wasi")]
- {
- unsafe { libc::isatty(out.as_raw_fd()) != 0 }
- }
- #[cfg(not(target = "wasm32-wasi"))]
- {
- false
- }
-}
-
-#[inline]
-pub fn is_a_color_terminal(_out: &Term) -> bool {
- // We currently never report color terminals. For discussion see
- // the issue in the WASI repo: https://github.com/WebAssembly/WASI/issues/162
- false
-}
-
-#[inline]
-pub fn terminal_size(_out: &Term) -> Option<(u16, u16)> {
- None
-}
-
-pub fn read_secure() -> io::Result<String> {
- Err(io::Error::new(
- io::ErrorKind::Other,
- "unsupported operation",
- ))
-}
-
-pub fn read_single_key() -> io::Result<Key> {
- Err(io::Error::new(
- io::ErrorKind::Other,
- "unsupported operation",
- ))
-}
-
-#[inline]
-pub fn wants_emoji() -> bool {
- false
-}
-
-pub fn set_title<T: Display>(_title: T) {}
diff --git a/vendor/console/src/windows_term/colors.rs b/vendor/console/src/windows_term/colors.rs
deleted file mode 100644
index dc8209d..0000000
--- a/vendor/console/src/windows_term/colors.rs
+++ /dev/null
@@ -1,451 +0,0 @@
-use std::io;
-use std::mem;
-use std::os::windows::io::AsRawHandle;
-use std::str::Bytes;
-
-use windows_sys::Win32::Foundation::HANDLE;
-use windows_sys::Win32::System::Console::{
- GetConsoleScreenBufferInfo, SetConsoleTextAttribute, CONSOLE_SCREEN_BUFFER_INFO,
- FOREGROUND_BLUE as FG_BLUE, FOREGROUND_GREEN as FG_GREEN, FOREGROUND_INTENSITY as FG_INTENSITY,
- FOREGROUND_RED as FG_RED,
-};
-
-use crate::Term;
-
-type WORD = u16;
-
-const FG_CYAN: WORD = FG_BLUE | FG_GREEN;
-const FG_MAGENTA: WORD = FG_BLUE | FG_RED;
-const FG_YELLOW: WORD = FG_GREEN | FG_RED;
-const FG_WHITE: WORD = FG_BLUE | FG_GREEN | FG_RED;
-
-/// Query the given handle for information about the console's screen buffer.
-///
-/// The given handle should represent a console. Otherwise, an error is
-/// returned.
-///
-/// This corresponds to calling [`GetConsoleScreenBufferInfo`].
-///
-/// [`GetConsoleScreenBufferInfo`]: https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
-pub fn screen_buffer_info(h: HANDLE) -> io::Result<ScreenBufferInfo> {
- unsafe {
- let mut info: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
- let rc = GetConsoleScreenBufferInfo(h, &mut info);
- if rc == 0 {
- return Err(io::Error::last_os_error());
- }
- Ok(ScreenBufferInfo(info))
- }
-}
-
-/// Set the text attributes of the console represented by the given handle.
-///
-/// This corresponds to calling [`SetConsoleTextAttribute`].
-///
-/// [`SetConsoleTextAttribute`]: https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute
-pub fn set_text_attributes(h: HANDLE, attributes: u16) -> io::Result<()> {
- if unsafe { SetConsoleTextAttribute(h, attributes) } == 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(())
- }
-}
-
-/// Represents console screen buffer information such as size, cursor position
-/// and styling attributes.
-///
-/// This wraps a [`CONSOLE_SCREEN_BUFFER_INFO`].
-///
-/// [`CONSOLE_SCREEN_BUFFER_INFO`]: https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
-#[derive(Clone)]
-pub struct ScreenBufferInfo(CONSOLE_SCREEN_BUFFER_INFO);
-
-impl ScreenBufferInfo {
- /// Returns the character attributes associated with this console.
- ///
- /// This corresponds to `wAttributes`.
- ///
- /// See [`char info`] for more details.
- ///
- /// [`char info`]: https://docs.microsoft.com/en-us/windows/console/char-info-str
- pub fn attributes(&self) -> u16 {
- self.0.wAttributes
- }
-}
-
-/// A Windows console.
-///
-/// This represents a very limited set of functionality available to a Windows
-/// console. In particular, it can only change text attributes such as color
-/// and intensity. This may grow over time. If you need more routines, please
-/// file an issue and/or PR.
-///
-/// There is no way to "write" to this console. Simply write to
-/// stdout or stderr instead, while interleaving instructions to the console
-/// to change text attributes.
-///
-/// A common pitfall when using a console is to forget to flush writes to
-/// stdout before setting new text attributes.
-#[derive(Debug)]
-pub struct Console {
- kind: HandleKind,
- start_attr: TextAttributes,
- cur_attr: TextAttributes,
-}
-
-#[derive(Clone, Copy, Debug)]
-enum HandleKind {
- Stdout,
- Stderr,
-}
-
-impl HandleKind {
- fn handle(&self) -> HANDLE {
- match *self {
- HandleKind::Stdout => io::stdout().as_raw_handle() as HANDLE,
- HandleKind::Stderr => io::stderr().as_raw_handle() as HANDLE,
- }
- }
-}
-
-impl Console {
- /// Get a console for a standard I/O stream.
- fn create_for_stream(kind: HandleKind) -> io::Result<Console> {
- let h = kind.handle();
- let info = screen_buffer_info(h)?;
- let attr = TextAttributes::from_word(info.attributes());
- Ok(Console {
- kind: kind,
- start_attr: attr,
- cur_attr: attr,
- })
- }
-
- /// Create a new Console to stdout.
- ///
- /// If there was a problem creating the console, then an error is returned.
- pub fn stdout() -> io::Result<Console> {
- Self::create_for_stream(HandleKind::Stdout)
- }
-
- /// Create a new Console to stderr.
- ///
- /// If there was a problem creating the console, then an error is returned.
- pub fn stderr() -> io::Result<Console> {
- Self::create_for_stream(HandleKind::Stderr)
- }
-
- /// Applies the current text attributes.
- fn set(&mut self) -> io::Result<()> {
- set_text_attributes(self.kind.handle(), self.cur_attr.to_word())
- }
-
- /// Apply the given intensity and color attributes to the console
- /// foreground.
- ///
- /// If there was a problem setting attributes on the console, then an error
- /// is returned.
- pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
- self.cur_attr.fg_color = color;
- self.cur_attr.fg_intense = intense;
- self.set()
- }
-
- /// Apply the given intensity and color attributes to the console
- /// background.
- ///
- /// If there was a problem setting attributes on the console, then an error
- /// is returned.
- pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
- self.cur_attr.bg_color = color;
- self.cur_attr.bg_intense = intense;
- self.set()
- }
-
- /// Reset the console text attributes to their original settings.
- ///
- /// The original settings correspond to the text attributes on the console
- /// when this `Console` value was created.
- ///
- /// If there was a problem setting attributes on the console, then an error
- /// is returned.
- pub fn reset(&mut self) -> io::Result<()> {
- self.cur_attr = self.start_attr;
- self.set()
- }
-}
-
-/// A representation of text attributes for the Windows console.
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-struct TextAttributes {
- fg_color: Color,
- fg_intense: Intense,
- bg_color: Color,
- bg_intense: Intense,
-}
-
-impl TextAttributes {
- fn to_word(&self) -> WORD {
- let mut w = 0;
- w |= self.fg_color.to_fg();
- w |= self.fg_intense.to_fg();
- w |= self.bg_color.to_bg();
- w |= self.bg_intense.to_bg();
- w
- }
-
- fn from_word(word: WORD) -> TextAttributes {
- TextAttributes {
- fg_color: Color::from_fg(word),
- fg_intense: Intense::from_fg(word),
- bg_color: Color::from_bg(word),
- bg_intense: Intense::from_bg(word),
- }
- }
-}
-
-/// Whether to use intense colors or not.
-#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum Intense {
- Yes,
- No,
-}
-
-impl Intense {
- fn to_bg(&self) -> WORD {
- self.to_fg() << 4
- }
-
- fn from_bg(word: WORD) -> Intense {
- Intense::from_fg(word >> 4)
- }
-
- fn to_fg(&self) -> WORD {
- match *self {
- Intense::No => 0,
- Intense::Yes => FG_INTENSITY,
- }
- }
-
- fn from_fg(word: WORD) -> Intense {
- if word & FG_INTENSITY > 0 {
- Intense::Yes
- } else {
- Intense::No
- }
- }
-}
-
-/// The set of available colors for use with a Windows console.
-#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum Color {
- Black,
- Blue,
- Green,
- Red,
- Cyan,
- Magenta,
- Yellow,
- White,
-}
-
-impl Color {
- fn to_bg(&self) -> WORD {
- self.to_fg() << 4
- }
-
- fn from_bg(word: WORD) -> Color {
- Color::from_fg(word >> 4)
- }
-
- fn to_fg(&self) -> WORD {
- match *self {
- Color::Black => 0,
- Color::Blue => FG_BLUE,
- Color::Green => FG_GREEN,
- Color::Red => FG_RED,
- Color::Cyan => FG_CYAN,
- Color::Magenta => FG_MAGENTA,
- Color::Yellow => FG_YELLOW,
- Color::White => FG_WHITE,
- }
- }
-
- fn from_fg(word: WORD) -> Color {
- match word & 0b111 {
- FG_BLUE => Color::Blue,
- FG_GREEN => Color::Green,
- FG_RED => Color::Red,
- FG_CYAN => Color::Cyan,
- FG_MAGENTA => Color::Magenta,
- FG_YELLOW => Color::Yellow,
- FG_WHITE => Color::White,
- _ => Color::Black,
- }
- }
-}
-
-pub fn console_colors(out: &Term, mut con: Console, bytes: &[u8]) -> io::Result<()> {
- use crate::ansi::AnsiCodeIterator;
- use std::str::from_utf8;
-
- let s = from_utf8(bytes).expect("data to be printed is not an ansi string");
- let mut iter = AnsiCodeIterator::new(s);
-
- while !iter.rest_slice().is_empty() {
- if let Some((part, is_esc)) = iter.next() {
- if !is_esc {
- out.write_through_common(part.as_bytes())?;
- } else if part == "\x1b[0m" {
- con.reset()?;
- } else if let Some((intense, color, fg_bg)) = driver(parse_color, part) {
- match fg_bg {
- FgBg::Foreground => con.fg(intense, color),
- FgBg::Background => con.bg(intense, color),
- }?;
- } else if driver(parse_attr, part).is_none() {
- out.write_through_common(part.as_bytes())?;
- }
- }
- }
-
- Ok(())
-}
-
-#[derive(Debug, PartialEq, Eq)]
-enum FgBg {
- Foreground,
- Background,
-}
-
-impl FgBg {
- fn new(byte: u8) -> Option<Self> {
- match byte {
- b'3' => Some(Self::Foreground),
- b'4' => Some(Self::Background),
- _ => None,
- }
- }
-}
-
-fn driver<Out>(parse: fn(Bytes<'_>) -> Option<Out>, part: &str) -> Option<Out> {
- let mut bytes = part.bytes();
-
- loop {
- while bytes.next()? != b'\x1b' {}
-
- if let ret @ Some(_) = (parse)(bytes.clone()) {
- return ret;
- }
- }
-}
-
-// `driver(parse_color, s)` parses the equivalent of the regex
-// \x1b\[(3|4)8;5;(8|9|1[0-5])m
-// for intense or
-// \x1b\[(3|4)([0-7])m
-// for normal
-fn parse_color(mut bytes: Bytes<'_>) -> Option<(Intense, Color, FgBg)> {
- parse_prefix(&mut bytes)?;
-
- let fg_bg = FgBg::new(bytes.next()?)?;
- let (intense, color) = match bytes.next()? {
- b @ b'0'..=b'7' => (Intense::No, normal_color_ansi_from_byte(b)?),
- b'8' => {
- if &[bytes.next()?, bytes.next()?, bytes.next()?] != b";5;" {
- return None;
- }
- (Intense::Yes, parse_intense_color_ansi(&mut bytes)?)
- }
- _ => return None,
- };
-
- parse_suffix(&mut bytes)?;
- Some((intense, color, fg_bg))
-}
-
-// `driver(parse_attr, s)` parses the equivalent of the regex
-// \x1b\[([1-8])m
-fn parse_attr(mut bytes: Bytes<'_>) -> Option<u8> {
- parse_prefix(&mut bytes)?;
- let attr = match bytes.next()? {
- attr @ b'1'..=b'8' => attr,
- _ => return None,
- };
- parse_suffix(&mut bytes)?;
- Some(attr)
-}
-
-fn parse_prefix(bytes: &mut Bytes<'_>) -> Option<()> {
- if bytes.next()? == b'[' {
- Some(())
- } else {
- None
- }
-}
-
-fn parse_intense_color_ansi(bytes: &mut Bytes<'_>) -> Option<Color> {
- let color = match bytes.next()? {
- b'8' => Color::Black,
- b'9' => Color::Red,
- b'1' => match bytes.next()? {
- b'0' => Color::Green,
- b'1' => Color::Yellow,
- b'2' => Color::Blue,
- b'3' => Color::Magenta,
- b'4' => Color::Cyan,
- b'5' => Color::White,
- _ => return None,
- },
- _ => return None,
- };
- Some(color)
-}
-
-fn normal_color_ansi_from_byte(b: u8) -> Option<Color> {
- let color = match b {
- b'0' => Color::Black,
- b'1' => Color::Red,
- b'2' => Color::Green,
- b'3' => Color::Yellow,
- b'4' => Color::Blue,
- b'5' => Color::Magenta,
- b'6' => Color::Cyan,
- b'7' => Color::White,
- _ => return None,
- };
- Some(color)
-}
-
-fn parse_suffix(bytes: &mut Bytes<'_>) -> Option<()> {
- if bytes.next()? == b'm' {
- Some(())
- } else {
- None
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn color_parsing() {
- let intense_color = "leading bytes \x1b[38;5;10m trailing bytes";
- let parsed = driver(parse_color, intense_color).unwrap();
- assert_eq!(parsed, (Intense::Yes, Color::Green, FgBg::Foreground));
-
- let normal_color = "leading bytes \x1b[40m trailing bytes";
- let parsed = driver(parse_color, normal_color).unwrap();
- assert_eq!(parsed, (Intense::No, Color::Black, FgBg::Background));
- }
-
- #[test]
- fn attr_parsing() {
- let attr = "leading bytes \x1b[1m trailing bytes";
- let parsed = driver(parse_attr, attr).unwrap();
- assert_eq!(parsed, b'1');
- }
-}
diff --git a/vendor/console/src/windows_term/mod.rs b/vendor/console/src/windows_term/mod.rs
deleted file mode 100644
index c4ec193..0000000
--- a/vendor/console/src/windows_term/mod.rs
+++ /dev/null
@@ -1,563 +0,0 @@
-use std::cmp;
-use std::env;
-use std::ffi::OsStr;
-use std::fmt::Display;
-use std::io;
-use std::iter::once;
-use std::mem;
-use std::os::raw::c_void;
-use std::os::windows::ffi::OsStrExt;
-use std::os::windows::io::AsRawHandle;
-use std::slice;
-use std::{char, mem::MaybeUninit};
-
-use encode_unicode::error::InvalidUtf16Tuple;
-use encode_unicode::CharExt;
-use windows_sys::Win32::Foundation::{CHAR, HANDLE, INVALID_HANDLE_VALUE, MAX_PATH};
-use windows_sys::Win32::Storage::FileSystem::{
- FileNameInfo, GetFileInformationByHandleEx, FILE_NAME_INFO,
-};
-use windows_sys::Win32::System::Console::{
- FillConsoleOutputAttribute, FillConsoleOutputCharacterA, GetConsoleCursorInfo, GetConsoleMode,
- GetConsoleScreenBufferInfo, GetNumberOfConsoleInputEvents, GetStdHandle, ReadConsoleInputW,
- SetConsoleCursorInfo, SetConsoleCursorPosition, SetConsoleMode, SetConsoleTitleW,
- CONSOLE_CURSOR_INFO, CONSOLE_SCREEN_BUFFER_INFO, COORD, INPUT_RECORD, KEY_EVENT,
- KEY_EVENT_RECORD, STD_ERROR_HANDLE, STD_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
-};
-use windows_sys::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY;
-
-use crate::common_term;
-use crate::kb::Key;
-use crate::term::{Term, TermTarget};
-
-#[cfg(feature = "windows-console-colors")]
-mod colors;
-
-#[cfg(feature = "windows-console-colors")]
-pub use self::colors::*;
-
-const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 0x4;
-pub const DEFAULT_WIDTH: u16 = 79;
-
-pub fn as_handle(term: &Term) -> HANDLE {
- // convert between windows_sys::Win32::Foundation::HANDLE and std::os::windows::raw::HANDLE
- term.as_raw_handle() as HANDLE
-}
-
-pub fn is_a_terminal(out: &Term) -> bool {
- let (fd, others) = match out.target() {
- TermTarget::Stdout => (STD_OUTPUT_HANDLE, [STD_INPUT_HANDLE, STD_ERROR_HANDLE]),
- TermTarget::Stderr => (STD_ERROR_HANDLE, [STD_INPUT_HANDLE, STD_OUTPUT_HANDLE]),
- };
-
- if unsafe { console_on_any(&[fd]) } {
- // False positives aren't possible. If we got a console then
- // we definitely have a tty on stdin.
- return true;
- }
-
- // At this point, we *could* have a false negative. We can determine that
- // this is true negative if we can detect the presence of a console on
- // any of the other streams. If another stream has a console, then we know
- // we're in a Windows console and can therefore trust the negative.
- if unsafe { console_on_any(&others) } {
- return false;
- }
-
- msys_tty_on(out)
-}
-
-pub fn is_a_color_terminal(out: &Term) -> bool {
- if !is_a_terminal(out) {
- return false;
- }
- if msys_tty_on(out) {
- return match env::var("TERM") {
- Ok(term) => term != "dumb",
- Err(_) => true,
- };
- }
- enable_ansi_on(out)
-}
-
-fn enable_ansi_on(out: &Term) -> bool {
- unsafe {
- let handle = as_handle(out);
-
- let mut dw_mode = 0;
- if GetConsoleMode(handle, &mut dw_mode) == 0 {
- return false;
- }
-
- dw_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
- if SetConsoleMode(handle, dw_mode) == 0 {
- return false;
- }
-
- true
- }
-}
-
-unsafe fn console_on_any(fds: &[STD_HANDLE]) -> bool {
- for &fd in fds {
- let mut out = 0;
- let handle = GetStdHandle(fd);
- if GetConsoleMode(handle, &mut out) != 0 {
- return true;
- }
- }
- false
-}
-
-pub fn terminal_size(out: &Term) -> Option<(u16, u16)> {
- use windows_sys::Win32::System::Console::SMALL_RECT;
-
- // convert between windows_sys::Win32::Foundation::HANDLE and std::os::windows::raw::HANDLE
- let handle = out.as_raw_handle();
- let hand = handle as windows_sys::Win32::Foundation::HANDLE;
-
- if hand == INVALID_HANDLE_VALUE {
- return None;
- }
-
- let zc = COORD { X: 0, Y: 0 };
- let mut csbi = CONSOLE_SCREEN_BUFFER_INFO {
- dwSize: zc,
- dwCursorPosition: zc,
- wAttributes: 0,
- srWindow: SMALL_RECT {
- Left: 0,
- Top: 0,
- Right: 0,
- Bottom: 0,
- },
- dwMaximumWindowSize: zc,
- };
- if unsafe { GetConsoleScreenBufferInfo(hand, &mut csbi) } == 0 {
- return None;
- }
-
- let rows = (csbi.srWindow.Bottom - csbi.srWindow.Top + 1) as u16;
- let columns = (csbi.srWindow.Right - csbi.srWindow.Left + 1) as u16;
-
- Some((rows, columns))
-}
-
-pub fn move_cursor_to(out: &Term, x: usize, y: usize) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::move_cursor_to(out, x, y);
- }
- if let Some((hand, _)) = get_console_screen_buffer_info(as_handle(out)) {
- unsafe {
- SetConsoleCursorPosition(
- hand,
- COORD {
- X: x as i16,
- Y: y as i16,
- },
- );
- }
- }
- Ok(())
-}
-
-pub fn move_cursor_up(out: &Term, n: usize) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::move_cursor_up(out, n);
- }
-
- if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
- move_cursor_to(out, 0, csbi.dwCursorPosition.Y as usize - n)?;
- }
- Ok(())
-}
-
-pub fn move_cursor_down(out: &Term, n: usize) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::move_cursor_down(out, n);
- }
-
- if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
- move_cursor_to(out, 0, csbi.dwCursorPosition.Y as usize + n)?;
- }
- Ok(())
-}
-
-pub fn move_cursor_left(out: &Term, n: usize) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::move_cursor_left(out, n);
- }
-
- if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
- move_cursor_to(
- out,
- csbi.dwCursorPosition.X as usize - n,
- csbi.dwCursorPosition.Y as usize,
- )?;
- }
- Ok(())
-}
-
-pub fn move_cursor_right(out: &Term, n: usize) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::move_cursor_right(out, n);
- }
-
- if let Some((_, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
- move_cursor_to(
- out,
- csbi.dwCursorPosition.X as usize + n,
- csbi.dwCursorPosition.Y as usize,
- )?;
- }
- Ok(())
-}
-
-pub fn clear_line(out: &Term) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::clear_line(out);
- }
- if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
- unsafe {
- let width = csbi.srWindow.Right - csbi.srWindow.Left;
- let pos = COORD {
- X: 0,
- Y: csbi.dwCursorPosition.Y,
- };
- let mut written = 0;
- FillConsoleOutputCharacterA(hand, b' ' as CHAR, width as u32, pos, &mut written);
- FillConsoleOutputAttribute(hand, csbi.wAttributes, width as u32, pos, &mut written);
- SetConsoleCursorPosition(hand, pos);
- }
- }
- Ok(())
-}
-
-pub fn clear_chars(out: &Term, n: usize) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::clear_chars(out, n);
- }
- if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
- unsafe {
- let width = cmp::min(csbi.dwCursorPosition.X, n as i16);
- let pos = COORD {
- X: csbi.dwCursorPosition.X - width,
- Y: csbi.dwCursorPosition.Y,
- };
- let mut written = 0;
- FillConsoleOutputCharacterA(hand, b' ' as CHAR, width as u32, pos, &mut written);
- FillConsoleOutputAttribute(hand, csbi.wAttributes, width as u32, pos, &mut written);
- SetConsoleCursorPosition(hand, pos);
- }
- }
- Ok(())
-}
-
-pub fn clear_screen(out: &Term) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::clear_screen(out);
- }
- if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
- unsafe {
- let cells = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32; // as u32, or else this causes stack overflows.
- let pos = COORD { X: 0, Y: 0 };
- let mut written = 0;
- FillConsoleOutputCharacterA(hand, b' ' as CHAR, cells, pos, &mut written); // cells as u32 no longer needed.
- FillConsoleOutputAttribute(hand, csbi.wAttributes, cells, pos, &mut written);
- SetConsoleCursorPosition(hand, pos);
- }
- }
- Ok(())
-}
-
-pub fn clear_to_end_of_screen(out: &Term) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::clear_to_end_of_screen(out);
- }
- if let Some((hand, csbi)) = get_console_screen_buffer_info(as_handle(out)) {
- unsafe {
- let bottom = csbi.srWindow.Right as u32 * csbi.srWindow.Bottom as u32;
- let cells = bottom - (csbi.dwCursorPosition.X as u32 * csbi.dwCursorPosition.Y as u32); // as u32, or else this causes stack overflows.
- let pos = COORD {
- X: 0,
- Y: csbi.dwCursorPosition.Y,
- };
- let mut written = 0;
- FillConsoleOutputCharacterA(hand, b' ' as CHAR, cells, pos, &mut written); // cells as u32 no longer needed.
- FillConsoleOutputAttribute(hand, csbi.wAttributes, cells, pos, &mut written);
- SetConsoleCursorPosition(hand, pos);
- }
- }
- Ok(())
-}
-
-pub fn show_cursor(out: &Term) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::show_cursor(out);
- }
- if let Some((hand, mut cci)) = get_console_cursor_info(as_handle(out)) {
- unsafe {
- cci.bVisible = 1;
- SetConsoleCursorInfo(hand, &cci);
- }
- }
- Ok(())
-}
-
-pub fn hide_cursor(out: &Term) -> io::Result<()> {
- if out.is_msys_tty {
- return common_term::hide_cursor(out);
- }
- if let Some((hand, mut cci)) = get_console_cursor_info(as_handle(out)) {
- unsafe {
- cci.bVisible = 0;
- SetConsoleCursorInfo(hand, &cci);
- }
- }
- Ok(())
-}
-
-fn get_console_screen_buffer_info(hand: HANDLE) -> Option<(HANDLE, CONSOLE_SCREEN_BUFFER_INFO)> {
- let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = unsafe { mem::zeroed() };
- match unsafe { GetConsoleScreenBufferInfo(hand, &mut csbi) } {
- 0 => None,
- _ => Some((hand, csbi)),
- }
-}
-
-fn get_console_cursor_info(hand: HANDLE) -> Option<(HANDLE, CONSOLE_CURSOR_INFO)> {
- let mut cci: CONSOLE_CURSOR_INFO = unsafe { mem::zeroed() };
- match unsafe { GetConsoleCursorInfo(hand, &mut cci) } {
- 0 => None,
- _ => Some((hand, cci)),
- }
-}
-
-pub fn key_from_key_code(code: VIRTUAL_KEY) -> Key {
- use windows_sys::Win32::UI::Input::KeyboardAndMouse;
-
- match code {
- KeyboardAndMouse::VK_LEFT => Key::ArrowLeft,
- KeyboardAndMouse::VK_RIGHT => Key::ArrowRight,
- KeyboardAndMouse::VK_UP => Key::ArrowUp,
- KeyboardAndMouse::VK_DOWN => Key::ArrowDown,
- KeyboardAndMouse::VK_RETURN => Key::Enter,
- KeyboardAndMouse::VK_ESCAPE => Key::Escape,
- KeyboardAndMouse::VK_BACK => Key::Backspace,
- KeyboardAndMouse::VK_TAB => Key::Tab,
- KeyboardAndMouse::VK_HOME => Key::Home,
- KeyboardAndMouse::VK_END => Key::End,
- KeyboardAndMouse::VK_DELETE => Key::Del,
- KeyboardAndMouse::VK_SHIFT => Key::Shift,
- KeyboardAndMouse::VK_MENU => Key::Alt,
- _ => Key::Unknown,
- }
-}
-
-pub fn read_secure() -> io::Result<String> {
- let mut rv = String::new();
- loop {
- match read_single_key()? {
- Key::Enter => {
- break;
- }
- Key::Char('\x08') => {
- if !rv.is_empty() {
- let new_len = rv.len() - 1;
- rv.truncate(new_len);
- }
- }
- Key::Char(c) => {
- rv.push(c);
- }
- _ => {}
- }
- }
- Ok(rv)
-}
-
-pub fn read_single_key() -> io::Result<Key> {
- let key_event = read_key_event()?;
-
- let unicode_char = unsafe { key_event.uChar.UnicodeChar };
- if unicode_char == 0 {
- Ok(key_from_key_code(key_event.wVirtualKeyCode))
- } else {
- // This is a unicode character, in utf-16. Try to decode it by itself.
- match char::from_utf16_tuple((unicode_char, None)) {
- Ok(c) => {
- // Maintain backward compatibility. The previous implementation (_getwch()) would return
- // a special keycode for `Enter`, while ReadConsoleInputW() prefers to use '\r'.
- if c == '\r' {
- Ok(Key::Enter)
- } else if c == '\x08' {
- Ok(Key::Backspace)
- } else if c == '\x1B' {
- Ok(Key::Escape)
- } else {
- Ok(Key::Char(c))
- }
- }
- // This is part of a surrogate pair. Try to read the second half.
- Err(InvalidUtf16Tuple::MissingSecond) => {
- // Confirm that there is a next character to read.
- if get_key_event_count()? == 0 {
- let message = format!(
- "Read invlid utf16 {}: {}",
- unicode_char,
- InvalidUtf16Tuple::MissingSecond
- );
- return Err(io::Error::new(io::ErrorKind::InvalidData, message));
- }
-
- // Read the next character.
- let next_event = read_key_event()?;
- let next_surrogate = unsafe { next_event.uChar.UnicodeChar };
-
- // Attempt to decode it.
- match char::from_utf16_tuple((unicode_char, Some(next_surrogate))) {
- Ok(c) => Ok(Key::Char(c)),
-
- // Return an InvalidData error. This is the recommended value for UTF-related I/O errors.
- // (This error is given when reading a non-UTF8 file into a String, for example.)
- Err(e) => {
- let message = format!(
- "Read invalid surrogate pair ({}, {}): {}",
- unicode_char, next_surrogate, e
- );
- Err(io::Error::new(io::ErrorKind::InvalidData, message))
- }
- }
- }
-
- // Return an InvalidData error. This is the recommended value for UTF-related I/O errors.
- // (This error is given when reading a non-UTF8 file into a String, for example.)
- Err(e) => {
- let message = format!("Read invalid utf16 {}: {}", unicode_char, e);
- Err(io::Error::new(io::ErrorKind::InvalidData, message))
- }
- }
- }
-}
-
-fn get_stdin_handle() -> io::Result<HANDLE> {
- let handle = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
- if handle == INVALID_HANDLE_VALUE {
- Err(io::Error::last_os_error())
- } else {
- Ok(handle)
- }
-}
-
-/// Get the number of pending events in the ReadConsoleInput queue. Note that while
-/// these aren't necessarily key events, the only way that multiple events can be
-/// put into the queue simultaneously is if a unicode character spanning multiple u16's
-/// is read.
-///
-/// Therefore, this is accurate as long as at least one KEY_EVENT has already been read.
-fn get_key_event_count() -> io::Result<u32> {
- let handle = get_stdin_handle()?;
- let mut event_count: u32 = unsafe { mem::zeroed() };
-
- let success = unsafe { GetNumberOfConsoleInputEvents(handle, &mut event_count) };
- if success == 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(event_count)
- }
-}
-
-fn read_key_event() -> io::Result<KEY_EVENT_RECORD> {
- let handle = get_stdin_handle()?;
- let mut buffer: INPUT_RECORD = unsafe { mem::zeroed() };
-
- let mut events_read: u32 = unsafe { mem::zeroed() };
-
- let mut key_event: KEY_EVENT_RECORD;
- loop {
- let success = unsafe { ReadConsoleInputW(handle, &mut buffer, 1, &mut events_read) };
- if success == 0 {
- return Err(io::Error::last_os_error());
- }
- if events_read == 0 {
- return Err(io::Error::new(
- io::ErrorKind::Other,
- "ReadConsoleInput returned no events, instead of waiting for an event",
- ));
- }
-
- if events_read == 1 && buffer.EventType != KEY_EVENT as u16 {
- // This isn't a key event; ignore it.
- continue;
- }
-
- key_event = unsafe { mem::transmute(buffer.Event) };
-
- if key_event.bKeyDown == 0 {
- // This is a key being released; ignore it.
- continue;
- }
-
- return Ok(key_event);
- }
-}
-
-pub fn wants_emoji() -> bool {
- // If WT_SESSION is set, we can assume we're running in the nne
- // Windows Terminal. The correct way to detect this is not available
- // yet. See https://github.com/microsoft/terminal/issues/1040
- env::var("WT_SESSION").is_ok()
-}
-
-/// Returns true if there is an MSYS tty on the given handle.
-pub fn msys_tty_on(term: &Term) -> bool {
- let handle = term.as_raw_handle();
- unsafe {
- // Check whether the Windows 10 native pty is enabled
- {
- let mut out = MaybeUninit::uninit();
- let res = GetConsoleMode(handle as HANDLE, out.as_mut_ptr());
- if res != 0 // If res is true then out was initialized.
- && (out.assume_init() & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
- == ENABLE_VIRTUAL_TERMINAL_PROCESSING
- {
- return true;
- }
- }
-
- let size = mem::size_of::<FILE_NAME_INFO>();
- let mut name_info_bytes = vec![0u8; size + MAX_PATH as usize * mem::size_of::<u16>()];
- let res = GetFileInformationByHandleEx(
- handle as HANDLE,
- FileNameInfo,
- &mut *name_info_bytes as *mut _ as *mut c_void,
- name_info_bytes.len() as u32,
- );
- if res == 0 {
- return false;
- }
- let name_info: &FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const FILE_NAME_INFO);
- let s = slice::from_raw_parts(
- name_info.FileName.as_ptr(),
- name_info.FileNameLength as usize / 2,
- );
- let name = String::from_utf16_lossy(s);
- // This checks whether 'pty' exists in the file name, which indicates that
- // a pseudo-terminal is attached. To mitigate against false positives
- // (e.g., an actual file name that contains 'pty'), we also require that
- // either the strings 'msys-' or 'cygwin-' are in the file name as well.)
- let is_msys = name.contains("msys-") || name.contains("cygwin-");
- let is_pty = name.contains("-pty");
- is_msys && is_pty
- }
-}
-
-pub fn set_title<T: Display>(title: T) {
- let buffer: Vec<u16> = OsStr::new(&format!("{}", title))
- .encode_wide()
- .chain(once(0))
- .collect();
- unsafe {
- SetConsoleTitleW(buffer.as_ptr());
- }
-}