rust_analyzer/cli/
progress_report.rs
1use std::io::{self, Write};
5
6pub(crate) struct ProgressReport<'a> {
8 curr: f32,
9 text: String,
10 hidden: bool,
11
12 len: usize,
13 pos: u64,
14 msg: Option<Box<dyn Fn() -> String + 'a>>,
15}
16
17impl<'a> ProgressReport<'a> {
18 pub(crate) fn new(len: usize) -> ProgressReport<'a> {
19 ProgressReport { curr: 0.0, text: String::new(), hidden: false, len, pos: 0, msg: None }
20 }
21
22 pub(crate) fn hidden() -> ProgressReport<'a> {
23 ProgressReport { curr: 0.0, text: String::new(), hidden: true, len: 0, pos: 0, msg: None }
24 }
25
26 pub(crate) fn set_message(&mut self, msg: impl Fn() -> String + 'a) {
27 if !self.hidden {
28 self.msg = Some(Box::new(msg));
29 }
30 self.tick();
31 }
32
33 pub(crate) fn println<I: Into<String>>(&mut self, msg: I) {
34 self.clear();
35 println!("{}", msg.into());
36 self.tick();
37 }
38
39 pub(crate) fn inc(&mut self, delta: u64) {
40 self.pos += delta;
41 if self.len == 0 {
42 self.set_value(0.0)
43 } else {
44 self.set_value((self.pos as f32) / (self.len as f32))
45 }
46 self.tick();
47 }
48
49 pub(crate) fn finish_and_clear(&mut self) {
50 self.clear();
51 }
52
53 pub(crate) fn tick(&mut self) {
54 if self.hidden {
55 return;
56 }
57 let percent = (self.curr * 100.0) as u32;
58 let text = format!(
59 "{}/{} {percent:3>}% {}",
60 self.pos,
61 self.len,
62 self.msg.as_ref().map_or_else(String::new, |it| it())
63 );
64 self.update_text(&text);
65 }
66
67 fn update_text(&mut self, text: &str) {
68 let mut common_prefix_length = 0;
70 let common_length = usize::min(self.text.len(), text.len());
71
72 while common_prefix_length < common_length
73 && text.chars().nth(common_prefix_length).unwrap()
74 == self.text.chars().nth(common_prefix_length).unwrap()
75 {
76 common_prefix_length += 1;
77 }
78
79 let mut output = String::new();
81 output += &'\x08'.to_string().repeat(self.text.len() - common_prefix_length);
82 output.extend(text.chars().skip(common_prefix_length));
84
85 if let Some(overlap_count) = self.text.len().checked_sub(text.len()) {
87 if overlap_count > 0 {
88 output += &" ".repeat(overlap_count);
89 output += &"\x08".repeat(overlap_count);
90 }
91 }
92
93 let _ = io::stdout().write(output.as_bytes());
94 let _ = io::stdout().flush();
95 text.clone_into(&mut self.text);
96 }
97
98 fn set_value(&mut self, value: f32) {
99 self.curr = value.clamp(0.0, 1.0);
100 }
101
102 fn clear(&mut self) {
103 if self.hidden {
104 return;
105 }
106
107 let spaces = " ".repeat(self.text.len());
109 let backspaces = "\x08".repeat(self.text.len());
110 print!("{backspaces}{spaces}{backspaces}");
111 let _ = io::stdout().flush();
112
113 self.text = String::new();
114 }
115}