Skip to content

Commit 02de8e8

Browse files
FranciscoTGouveiarami3l
authored andcommitted
feat(terminal): implement the TermLike trait for ColorableTerminal
To later support `indicatif`'s progress bars, this trait was implemented for the `ColorableTerminal`. It is important to note that `is_a_tty` and `color_choice` will be needed for deciding if progress bars are written to the stdout (or hidden) and if they are styled with colors, respectively.
1 parent 675b346 commit 02de8e8

File tree

3 files changed

+147
-3
lines changed

3 files changed

+147
-3
lines changed

Cargo.lock

Lines changed: 52 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ cfg-if = "1.0"
4646
chrono = { version = "0.4", default-features = false, features = ["std"] }
4747
clap = { version = "4", features = ["derive", "wrap_help"] }
4848
clap_complete = "4"
49+
console = "0.16"
4950
curl = { version = "0.4.44", optional = true }
5051
effective-limits = "0.5.5"
5152
enum-map = "2.5.0"
@@ -54,6 +55,7 @@ flate2 = { version = "1.1.1", default-features = false, features = ["zlib-rs"] }
5455
fs_at = "0.2.1"
5556
git-testament = "0.2"
5657
home = "0.5.4"
58+
indicatif = "0.18"
5759
itertools = "0.14"
5860
libc = "0.2"
5961
opener = "0.8.0"

src/process/terminalsource.rs

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use console::Term;
2+
use indicatif::TermLike;
13
use std::{
24
io::{self, Write},
35
mem::MaybeUninit,
@@ -6,8 +8,8 @@ use std::{
68
sync::{Arc, Mutex, MutexGuard},
79
};
810

9-
pub(crate) use termcolor::Color;
10-
use termcolor::{ColorChoice, ColorSpec, StandardStream, StandardStreamLock, WriteColor};
11+
pub(crate) use termcolor::{Color, ColorChoice};
12+
use termcolor::{ColorSpec, StandardStream, StandardStreamLock, WriteColor};
1113

1214
use super::Process;
1315
#[cfg(feature = "test")]
@@ -53,6 +55,8 @@ pub struct ColorableTerminal {
5355
// source is important because otherwise parallel constructed terminals
5456
// would not be locked out.
5557
inner: Arc<Mutex<TerminalInner>>,
58+
is_a_tty: bool,
59+
color_choice: ColorChoice,
5660
}
5761

5862
/// Internal state for ColorableTerminal
@@ -84,10 +88,11 @@ impl ColorableTerminal {
8488
/// then color commands will be sent to the stream.
8589
/// Otherwise color commands are discarded.
8690
pub(super) fn new(stream: StreamSelector, process: &Process) -> Self {
91+
let is_a_tty = stream.is_a_tty(process);
8792
let choice = match process.var("RUSTUP_TERM_COLOR") {
8893
Ok(s) if s.eq_ignore_ascii_case("always") => ColorChoice::Always,
8994
Ok(s) if s.eq_ignore_ascii_case("never") => ColorChoice::Never,
90-
_ if stream.is_a_tty(process) => ColorChoice::Auto,
95+
_ if is_a_tty => ColorChoice::Auto,
9196
_ => ColorChoice::Never,
9297
};
9398
let inner = match stream {
@@ -104,6 +109,8 @@ impl ColorableTerminal {
104109
};
105110
ColorableTerminal {
106111
inner: Arc::new(Mutex::new(inner)),
112+
is_a_tty,
113+
color_choice: choice,
107114
}
108115
}
109116

@@ -179,6 +186,14 @@ impl ColorableTerminal {
179186
};
180187
Ok(())
181188
}
189+
190+
pub fn is_a_tty(&self) -> bool {
191+
self.is_a_tty
192+
}
193+
194+
pub fn color_choice(&self) -> ColorChoice {
195+
self.color_choice
196+
}
182197
}
183198

184199
#[derive(Copy, Clone, Debug)]
@@ -223,6 +238,81 @@ impl io::Write for ColorableTerminalLocked {
223238
}
224239
}
225240

241+
impl TermLike for ColorableTerminal {
242+
fn width(&self) -> u16 {
243+
Term::stdout().size().1
244+
}
245+
246+
fn move_cursor_up(&self, n: usize) -> io::Result<()> {
247+
// As the ProgressBar may try to move the cursor up by 0 lines,
248+
// we need to handle that case to avoid writing an escape sequence
249+
// that would mess up the terminal.
250+
if n == 0 {
251+
return Ok(());
252+
}
253+
let mut t = self.lock();
254+
write!(t, "\x1b[{n}A")?;
255+
t.flush()
256+
}
257+
258+
fn move_cursor_down(&self, n: usize) -> io::Result<()> {
259+
if n == 0 {
260+
return Ok(());
261+
}
262+
let mut t = self.lock();
263+
write!(t, "\x1b[{n}B")?;
264+
t.flush()
265+
}
266+
267+
fn move_cursor_right(&self, n: usize) -> io::Result<()> {
268+
if n == 0 {
269+
return Ok(());
270+
}
271+
let mut t = self.lock();
272+
write!(t, "\x1b[{n}C")?;
273+
t.flush()
274+
}
275+
276+
fn move_cursor_left(&self, n: usize) -> io::Result<()> {
277+
if n == 0 {
278+
return Ok(());
279+
}
280+
let mut t = self.lock();
281+
write!(t, "\x1b[{n}D")?;
282+
t.flush()
283+
}
284+
285+
fn write_line(&self, line: &str) -> io::Result<()> {
286+
let mut t = self.lock();
287+
t.write_all(line.as_bytes())?;
288+
t.write_all(b"\n")?;
289+
t.flush()
290+
}
291+
292+
fn write_str(&self, s: &str) -> io::Result<()> {
293+
let mut t = self.lock();
294+
t.write_all(s.as_bytes())?;
295+
t.flush()
296+
}
297+
298+
fn clear_line(&self) -> io::Result<()> {
299+
let mut t = self.lock();
300+
t.write_all(b"\r\x1b[2K")?;
301+
t.flush()
302+
}
303+
304+
fn flush(&self) -> io::Result<()> {
305+
let mut t = self.lock();
306+
t.flush()
307+
}
308+
}
309+
310+
impl std::fmt::Debug for ColorableTerminal {
311+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
312+
write!(f, "ColorableTerminal {{ inner: ... }}")
313+
}
314+
}
315+
226316
#[cfg(test)]
227317
mod tests {
228318
use std::collections::HashMap;

0 commit comments

Comments
 (0)