cargo_plugin_utils/
scrolling.rs1use std::io::Write;
4
5use anyhow::Context;
6use console::Term;
7
8pub fn get_terminal_size() -> anyhow::Result<(u16, u16)> {
10 let term = Term::stdout();
11 term.size_checked().context("Failed to get terminal size")
12}
13
14pub fn set_scrolling_region(top: u16, bottom: u16) -> anyhow::Result<()> {
19 let mut stderr = std::io::stderr();
22 write!(stderr, "\x1b[{};{}r", top, bottom).context("Failed to set scrolling region")?;
23 stderr.flush().context("Failed to flush stdout")?;
24 Ok(())
25}
26
27pub fn reset_scrolling_region() -> anyhow::Result<()> {
31 let mut stderr = std::io::stderr();
33 write!(stderr, "\x1b[r").context("Failed to reset scrolling region")?;
34 stderr.flush().context("Failed to flush stdout")?;
35 Ok(())
36}
37
38pub fn clear_scrolling_region() -> anyhow::Result<()> {
42 let mut stderr = std::io::stderr();
48 write!(stderr, "\x1b[J").context("Failed to clear scrolling region")?;
52 stderr.flush().context("Failed to flush stdout")?;
53 Ok(())
54}
55
56pub fn move_cursor_to_line(line: u16) -> anyhow::Result<()> {
58 let mut stderr = std::io::stderr();
61 write!(stderr, "\x1b[{};1H", line).context("Failed to move cursor to line")?;
62 stderr.flush().context("Failed to flush stdout")?;
63 Ok(())
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn test_get_terminal_size() {
72 let _size = get_terminal_size();
75 }
76
77 #[test]
78 fn test_set_scrolling_region() {
79 let _ = set_scrolling_region(1u16, 10u16);
82 }
83
84 #[test]
85 fn test_set_scrolling_region_single_line() {
86 let _ = set_scrolling_region(5u16, 5u16);
88 }
89
90 #[test]
91 fn test_set_scrolling_region_large() {
92 let _ = set_scrolling_region(1u16, 1000u16);
94 }
95
96 #[test]
97 fn test_reset_scrolling_region() {
98 let _ = reset_scrolling_region();
100 }
101
102 #[test]
103 fn test_clear_scrolling_region() {
104 let _ = clear_scrolling_region();
106 }
107
108 #[test]
109 fn test_move_cursor_to_line() {
110 let _ = move_cursor_to_line(5u16);
112 }
113
114 #[test]
115 fn test_move_cursor_to_line_first() {
116 let _ = move_cursor_to_line(1u16);
118 }
119
120 #[test]
121 fn test_move_cursor_to_line_large() {
122 let _ = move_cursor_to_line(1000u16);
124 }
125
126 #[test]
127 fn test_scrolling_region_sequence() {
128 let _ = set_scrolling_region(1u16, 5u16);
130 let _ = move_cursor_to_line(1u16);
131 let _ = clear_scrolling_region();
132 let _ = reset_scrolling_region();
133 }
134
135 #[test]
136 fn test_scrolling_region_typical_usage() {
137 let term_rows = 24u16;
140 let scroll_lines = 5u16;
141 let region_top = term_rows - scroll_lines + 1; let _ = set_scrolling_region(region_top, term_rows);
144 let _ = move_cursor_to_line(region_top);
145 let _ = clear_scrolling_region();
147 let _ = reset_scrolling_region();
148 }
149
150 #[test]
151 fn test_scrolling_region_full_terminal() {
152 let _ = set_scrolling_region(1u16, 24u16);
154 let _ = reset_scrolling_region();
155 }
156
157 #[test]
158 fn test_multiple_cursor_moves() {
159 for line in 1u16..=10 {
161 let _ = move_cursor_to_line(line);
162 }
163 }
164
165 #[test]
166 fn test_set_and_reset_multiple_times() {
167 for idx in 1u16..=5 {
169 let _ = set_scrolling_region(idx, idx + 10);
170 let _ = reset_scrolling_region();
171 }
172 }
173}