1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use crate::result::Result;
use std::env;
use std::fmt::{Debug, Formatter};
use termion::cursor::DetectCursorPos;
use termion::terminal_size;
pub struct Device<'a>(&'a mut dyn std::io::Write);
impl Device<'_> {
pub(crate) fn new<'a>(writer: &'a mut dyn std::io::Write) -> Device<'a> {
Device(writer)
}
pub(crate) fn size(&self) -> Result<(u16, u16)> {
Ok(terminal_size()?)
}
pub(crate) fn write(&mut self, text: &str) -> Result<()> {
Ok(self.0.write_all(text.as_ref())?)
}
pub(crate) fn clear(&mut self) -> Result<()> {
Ok(write!(self.0, "{}", termion::clear::All)?)
}
pub(crate) fn position(&mut self) -> Result<(u16, u16)> {
let position = self.0.cursor_pos()?;
Ok((position.0 - 1, position.1 - 1))
}
pub(crate) fn set_visible(&mut self, visible: bool) -> Result<()> {
match visible {
true => Ok(write!(self.0, "{}", termion::cursor::Show)?),
false => Ok(write!(self.0, "{}", termion::cursor::Hide)?),
}
}
pub(crate) fn goto(&mut self, column: u16, row: u16) -> Result<()> {
Ok(write!(
self.0,
"{}",
termion::cursor::Goto(column + 1, row + 1)
)?)
}
pub(crate) fn move_up(&mut self, rows: u16) -> Result<()> {
Ok(write!(self.0, "{}", termion::cursor::Up(rows))?)
}
pub(crate) fn move_down(&mut self, rows: u16) -> Result<()> {
Ok(write!(self.0, "{}", "\n".repeat(rows as usize))?)
}
pub(crate) fn move_left(&mut self, columns: u16) -> Result<()> {
Ok(write!(self.0, "{}", termion::cursor::Left(columns))?)
}
pub(crate) fn move_right(&mut self, columns: u16) -> Result<()> {
Ok(write!(self.0, "{}", termion::cursor::Right(columns))?)
}
pub(crate) fn save_position(&mut self) -> Result<()> {
if is_terminal_app() {
Ok(write!(self.0, "\\u001B[7")?)
} else {
Ok(write!(self.0, "{}", termion::cursor::Save)?)
}
}
pub(crate) fn restore_position(&mut self) -> Result<()> {
if is_terminal_app() {
Ok(write!(self.0, "\\u001B[8")?)
} else {
Ok(write!(self.0, "{}", termion::cursor::Restore)?)
}
}
pub(crate) fn clear_line(&mut self) -> Result<()> {
Ok(write!(self.0, "{}", termion::clear::CurrentLine)?)
}
pub(crate) fn clear_until_newline(&mut self) -> Result<()> {
Ok(write!(self.0, "{}", termion::clear::UntilNewline)?)
}
pub(crate) fn flush(&mut self) -> Result<()> {
Ok(self.0.flush()?)
}
}
impl Debug for Device<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("Stdout device with writer")
}
}
pub(crate) fn is_terminal_app() -> bool {
env::var("TERM_PROGRAM").unwrap_or_default() == "Apple_Terminal"
}