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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
//! A module that contains all the actions related to the terminal.
//! Like clearing and scrolling in the terminal or getting the window size from the terminal.

use std::fmt;

#[cfg(windows)]
use crossterm_utils::supports_ansi;
use crossterm_utils::{impl_display, write_cout, Command, Result};

#[cfg(windows)]
use super::WinApiTerminal;
use super::{AnsiTerminal, ClearType, ITerminal};

/// Allows you to preform actions on the terminal.
///
/// # Features:
///
/// - Clearing (all lines, current line, from cursor down and up, until new line)
/// - Scrolling (Up, down)
/// - Get the size of the terminal
/// - Set the size of the terminal
/// - Alternate screen
/// - Raw screen
/// - Exit the current process
///
/// Check `/examples/` in the library for more specific examples.
pub struct Terminal {
    #[cfg(windows)]
    terminal: Box<(dyn ITerminal + Sync + Send)>,
    #[cfg(unix)]
    terminal: AnsiTerminal,
}

impl Terminal {
    /// Create new terminal instance whereon terminal related actions can be performed.
    pub fn new() -> Terminal {
        #[cfg(windows)]
        let terminal = if supports_ansi() {
            Box::from(AnsiTerminal::new()) as Box<(dyn ITerminal + Sync + Send)>
        } else {
            WinApiTerminal::new() as Box<(dyn ITerminal + Sync + Send)>
        };

        #[cfg(unix)]
        let terminal = AnsiTerminal::new();

        Terminal { terminal }
    }

    /// Clear the current cursor by specifying the `ClearType`.
    ///
    /// # Example
    /// ```rust
    /// # use crossterm_terminal as crossterm;
    /// # use crossterm_terminal::terminal;
    /// let mut term = terminal();
    ///
    /// // clear all cells in terminal.
    /// term.clear(crossterm::ClearType::All);
    /// // clear all cells from the cursor position downwards in terminal.
    /// term.clear(crossterm::ClearType::FromCursorDown);
    /// // clear all cells from the cursor position upwards in terminal.
    /// term.clear(crossterm::ClearType::FromCursorUp);
    /// // clear current line cells in terminal.
    /// term.clear(crossterm::ClearType::CurrentLine);
    /// // clear all cells from cursor position until new line in terminal.
    /// term.clear(crossterm::ClearType::UntilNewLine);
    /// ```
    pub fn clear(&self, clear_type: ClearType) -> Result<()> {
        self.terminal.clear(clear_type)
    }

    /// Get the terminal size `(x,y)`.
    pub fn size(&self) -> Result<(u16, u16)> {
        self.terminal.size()
    }

    /// Scroll `n` lines up in the current terminal.
    ///
    /// # Parameter
    /// - `count`: the number of rows should be shifted up.
    pub fn scroll_up(&self, count: u16) -> Result<()> {
        self.terminal.scroll_up(count)
    }

    /// Scroll `n` lines down in the current terminal.
    ///
    /// # Parameter
    /// - `count`: the number of rows should be shifted down.
    pub fn scroll_down(&self, count: u16) -> Result<()> {
        self.terminal.scroll_down(count)
    }

    /// Set the terminal size. Note that not all terminals can be set to a very small scale.
    ///
    /// ```rust
    /// # use crossterm_terminal::terminal;
    /// let mut term = terminal();
    ///
    /// // Set of the size to X: 10 and Y: 10
    /// let size = term.set_size(10,10);
    /// ```
    pub fn set_size(&self, width: u16, height: u16) -> Result<()> {
        self.terminal.set_size(width, height)
    }

    // TODO - Marked as no_run, because it's failing on Travis CI
    /// Exit the current process.
    ///
    /// ```no_run
    /// # use crossterm_terminal::terminal;
    /// let mut term = terminal();
    ///
    /// let size = term.exit();
    /// ```
    pub fn exit(&self) {
        crate::sys::exit();
    }

    /// Write any displayable content to the current terminal screen.
    ///
    /// ```rust
    /// # use crossterm_terminal::terminal;
    /// let mut term = terminal();
    ///
    /// let size = term.write("Some text \n Some text on new line");
    /// ```
    ///
    /// This will also flush the standard output.
    pub fn write<D: fmt::Display>(&self, value: D) -> Result<usize> {
        write_cout!(format!("{}", value))
    }
}

/// Get a `Terminal` instance whereon terminal related actions can be performed.
pub fn terminal() -> Terminal {
    Terminal::new()
}

/// When executed, this command will scroll up the terminal buffer by the given number of times.
///
/// See `crossterm/examples/command.rs` for more information on how to execute commands.
pub struct ScrollUp(pub u16);

impl Command for ScrollUp {
    type AnsiType = String;

    fn ansi_code(&self) -> Self::AnsiType {
        super::ansi_terminal::get_scroll_up_ansi(self.0)
    }

    #[cfg(windows)]
    fn execute_winapi(&self) -> Result<()> {
        WinApiTerminal::new().scroll_up(self.0)
    }
}

/// When executed, this command will scroll down the terminal buffer by the given number of times.
///
/// See `crossterm/examples/command.rs` for more information on how to execute commands.
pub struct ScrollDown(pub u16);

impl Command for ScrollDown {
    type AnsiType = String;

    fn ansi_code(&self) -> Self::AnsiType {
        super::ansi_terminal::get_scroll_down_ansi(self.0)
    }

    #[cfg(windows)]
    fn execute_winapi(&self) -> Result<()> {
        WinApiTerminal::new().scroll_down(self.0)
    }
}

/// When executed, this command will clear the terminal buffer based on the type provided.
///
/// See `crossterm/examples/command.rs` for more information on how to execute commands.
pub struct Clear(pub ClearType);

impl Command for Clear {
    type AnsiType = &'static str;

    fn ansi_code(&self) -> Self::AnsiType {
        match self.0 {
            ClearType::All => {
                return super::ansi_terminal::CLEAR_ALL;
            }
            ClearType::FromCursorDown => {
                return super::ansi_terminal::CLEAR_FROM_CURSOR_DOWN;
            }
            ClearType::FromCursorUp => {
                return super::ansi_terminal::CLEAR_FROM_CURSOR_UP;
            }
            ClearType::CurrentLine => return super::ansi_terminal::CLEAR_FROM_CURRENT_LINE,
            ClearType::UntilNewLine => return super::ansi_terminal::CLEAR_UNTIL_NEW_LINE,
        }
    }

    #[cfg(windows)]
    fn execute_winapi(&self) -> Result<()> {
        WinApiTerminal::new().clear(self.0.clone())
    }
}

/// When executed, this command will set the terminal sie to the given (`width` and `height`)
///
/// See `crossterm/examples/command.rs` for more information on how to execute commands.
pub struct SetSize(pub u16, pub u16);

impl Command for SetSize {
    type AnsiType = String;

    fn ansi_code(&self) -> Self::AnsiType {
        super::ansi_terminal::get_set_size_ansi(self.0, self.1)
    }

    #[cfg(windows)]
    fn execute_winapi(&self) -> Result<()> {
        WinApiTerminal::new().set_size(self.0, self.1)
    }
}

impl_display!(for ScrollUp);
impl_display!(for ScrollDown);
impl_display!(for SetSize);
impl_display!(for Clear);