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
//! The text caret's shape: a block, a bar or an underline, set on the
//! terminal's own cursor with DECSCUSR (`CSI n SP q`), which xterm, the
//! kitty, WezTerm, iTerm2, Windows Terminal, foot, Alacritty and the VTE
//! terminals follow. A terminal that does not know it ignores it.
//!
//! ```
//! use rich_interact::caret::{CaretShape, RESET};
//!
//! assert_eq!(CaretShape::Bar.sequence(), "\x1b[6 q");
//! assert_eq!(CaretShape::Block.blinking(), "\x1b[1 q");
//! assert_eq!(RESET, "\x1b[0 q");
//! ```
/// Puts the terminal's cursor back to the shape its user chose.
pub const RESET: &str = "\x1b[0 q";
/// How the caret looks.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum CaretShape {
/// A cell-sized block over the character after the caret.
#[default]
Block,
/// A thin bar before the character after the caret, as most editors
/// show it while typing.
Bar,
/// A line under the character after the caret.
Underline,
}
impl CaretShape {
/// DECSCUSR for the shape, steady.
pub fn sequence(self) -> &'static str {
match self {
CaretShape::Block => "\x1b[2 q",
CaretShape::Underline => "\x1b[4 q",
CaretShape::Bar => "\x1b[6 q",
}
}
/// DECSCUSR for the shape, blinking.
pub fn blinking(self) -> &'static str {
match self {
CaretShape::Block => "\x1b[1 q",
CaretShape::Underline => "\x1b[3 q",
CaretShape::Bar => "\x1b[5 q",
}
}
/// The shape by name: `block`, `bar` (or `beam`, `line`) or
/// `underline` (or `underscore`).
pub fn parse(name: &str) -> Option<CaretShape> {
match name.trim().to_ascii_lowercase().as_str() {
"block" => Some(CaretShape::Block),
"bar" | "beam" | "line" => Some(CaretShape::Bar),
"underline" | "underscore" => Some(CaretShape::Underline),
_ => None,
}
}
}