Skip to content

Commit a8081a0

Browse files
committed
More fixes.
1 parent a753b50 commit a8081a0

File tree

4 files changed

+184
-31
lines changed

4 files changed

+184
-31
lines changed

Diff for: Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ termcolor = "0.3"
5353
toml = "0.4.2"
5454
url = "1.1"
5555
clap = "2.31.2"
56+
unicode-width = "0.1.5"
5657

5758
# Not actually needed right now but required to make sure that rls/cargo build
5859
# with the same set of features in rust-lang/rust

Diff for: src/cargo/core/compiler/job_queue.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ impl<'a> JobQueue<'a> {
182182
// and then immediately return.
183183
let mut error = None;
184184
let mut progress = Progress::with_style("Building", ProgressStyle::Ratio, cx.bcx.config);
185-
let queue_len = self.queue.len();
185+
let total = self.queue.len();
186186
loop {
187187
// Dequeue as much work as we can, learning about everything
188188
// possible that can run. Note that this is also the point where we
@@ -226,12 +226,12 @@ impl<'a> JobQueue<'a> {
226226
// to the jobserver itself.
227227
tokens.truncate(self.active.len() - 1);
228228

229-
let count = queue_len - self.queue.len();
229+
let count = total - self.queue.len();
230230
let active_names = self.active.iter().map(|key| match key.mode {
231231
CompileMode::Doc { .. } => format!("{}(doc)", key.pkg.name()),
232232
_ => key.pkg.name().to_string(),
233233
}).collect::<Vec<_>>();
234-
drop(progress.tick_now(count, queue_len, format!(": {}", active_names.join(", "))));
234+
drop(progress.tick_now(count, total, &format!(": {}", active_names.join(", "))));
235235
let event = self.rx.recv().unwrap();
236236
progress.clear();
237237

@@ -259,9 +259,12 @@ impl<'a> JobQueue<'a> {
259259
info!("end: {:?}", key);
260260

261261
// self.active.remove_item(&key); // <- switch to this when stabilized.
262-
if let Some(pos) = self.active.iter().position(|k| *k == key) {
263-
self.active.remove(pos);
264-
}
262+
let pos = self
263+
.active
264+
.iter()
265+
.position(|k| *k == key)
266+
.expect("an unrecorded package has finished compiling");
267+
self.active.remove(pos);
265268
if !self.active.is_empty() {
266269
assert!(!tokens.is_empty());
267270
drop(tokens.pop());

Diff for: src/cargo/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ extern crate tar;
4444
extern crate tempfile;
4545
extern crate termcolor;
4646
extern crate toml;
47+
extern crate unicode_width;
4748
extern crate url;
4849

4950
use std::fmt;

Diff for: src/cargo/util/progress.rs

+173-25
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use std::time::{Duration, Instant};
55
use core::shell::Verbosity;
66
use util::{CargoResult, Config};
77

8+
use unicode_width::UnicodeWidthChar;
9+
810
pub struct Progress<'cfg> {
911
state: Option<State<'cfg>>,
1012
}
@@ -16,15 +18,19 @@ pub enum ProgressStyle {
1618

1719
struct State<'cfg> {
1820
config: &'cfg Config,
19-
style: ProgressStyle,
20-
max_width: usize,
21-
width: usize,
21+
format: Format,
2222
first: bool,
2323
last_update: Instant,
2424
name: String,
2525
done: bool,
2626
}
2727

28+
struct Format {
29+
style: ProgressStyle,
30+
max_width: usize,
31+
width: usize,
32+
}
33+
2834
impl<'cfg> Progress<'cfg> {
2935
pub fn with_style(name: &str, style: ProgressStyle, cfg: &'cfg Config) -> Progress<'cfg> {
3036
// report no progress when -q (for quiet) or TERM=dumb are set
@@ -39,9 +45,11 @@ impl<'cfg> Progress<'cfg> {
3945
Progress {
4046
state: cfg.shell().err_width().map(|n| State {
4147
config: cfg,
42-
style,
43-
max_width: n,
44-
width: cmp::min(n, 80),
48+
format: Format {
49+
style,
50+
max_width: n,
51+
width: cmp::min(n, 80),
52+
},
4553
first: true,
4654
last_update: Instant::now(),
4755
name: name.to_string(),
@@ -56,18 +64,18 @@ impl<'cfg> Progress<'cfg> {
5664

5765
pub fn tick(&mut self, cur: usize, max: usize) -> CargoResult<()> {
5866
match self.state {
59-
Some(ref mut s) => s.tick(cur, max, String::new(), true),
67+
Some(ref mut s) => s.tick(cur, max, "", true),
6068
None => Ok(()),
6169
}
6270
}
6371

6472
pub fn clear(&mut self) {
6573
if let Some(ref mut s) = self.state {
66-
clear(s.max_width, s.config);
74+
clear(s.format.max_width, s.config);
6775
}
6876
}
6977

70-
pub fn tick_now(&mut self, cur: usize, max: usize, msg: String) -> CargoResult<()> {
78+
pub fn tick_now(&mut self, cur: usize, max: usize, msg: &str) -> CargoResult<()> {
7179
match self.state {
7280
Some(ref mut s) => s.tick(cur, max, msg, false),
7381
None => Ok(()),
@@ -76,7 +84,7 @@ impl<'cfg> Progress<'cfg> {
7684
}
7785

7886
impl<'cfg> State<'cfg> {
79-
fn tick(&mut self, cur: usize, max: usize, msg: String, throttle: bool) -> CargoResult<()> {
87+
fn tick(&mut self, cur: usize, max: usize, msg: &str, throttle: bool) -> CargoResult<()> {
8088
if self.done {
8189
return Ok(());
8290
}
@@ -109,6 +117,22 @@ impl<'cfg> State<'cfg> {
109117
self.last_update = Instant::now();
110118
}
111119

120+
if cur == max {
121+
self.done = true;
122+
}
123+
124+
// Write out a pretty header, then the progress bar itself, and then
125+
// return back to the beginning of the line for the next print.
126+
if let Some(string) = self.format.progress_status(cur, max, msg) {
127+
self.config.shell().status_header(&self.name)?;
128+
write!(self.config.shell().err(), "{}\r", string)?;
129+
}
130+
Ok(())
131+
}
132+
}
133+
134+
impl Format {
135+
fn progress_status(&self, cur: usize, max: usize, msg: &str) -> Option<String> {
112136
// Render the percentage at the far right and then figure how long the
113137
// progress bar is
114138
let pct = (cur as f64) / (max as f64);
@@ -120,9 +144,11 @@ impl<'cfg> State<'cfg> {
120144
let extra_len = stats.len() + 2 /* [ and ] */ + 15 /* status header */;
121145
let display_width = match self.width.checked_sub(extra_len) {
122146
Some(n) => n,
123-
None => return Ok(()),
147+
None => return None,
124148
};
125-
let mut string = String::from("[");
149+
150+
let mut string = String::with_capacity(self.max_width);
151+
string.push('[');
126152
let hashes = display_width as f64 * pct;
127153
let hashes = hashes as usize;
128154

@@ -132,7 +158,6 @@ impl<'cfg> State<'cfg> {
132158
string.push_str("=");
133159
}
134160
if cur == max {
135-
self.done = true;
136161
string.push_str("=");
137162
} else {
138163
string.push_str(">");
@@ -146,19 +171,26 @@ impl<'cfg> State<'cfg> {
146171
string.push_str("]");
147172
string.push_str(&stats);
148173

149-
let avail_msg_len = self.max_width - self.width;
150-
if avail_msg_len >= msg.len() + 3 {
151-
string.push_str(&msg);
152-
} else if avail_msg_len >= 4 {
153-
string.push_str(&msg[..(avail_msg_len - 3)]);
154-
string.push_str("...");
174+
let mut avail_msg_len = self.max_width - self.width;
175+
let mut ellipsis_pos = 0;
176+
if avail_msg_len > 3 {
177+
for c in msg.chars() {
178+
let display_width = c.width().unwrap_or(0);
179+
if avail_msg_len >= display_width {
180+
avail_msg_len -= display_width;
181+
string.push(c);
182+
if avail_msg_len >= 3 {
183+
ellipsis_pos = string.len();
184+
}
185+
} else {
186+
string.truncate(ellipsis_pos);
187+
string.push_str("...");
188+
break;
189+
}
190+
}
155191
}
156192

157-
// Write out a pretty header, then the progress bar itself, and then
158-
// return back to the beginning of the line for the next print.
159-
self.config.shell().status_header(&self.name)?;
160-
write!(self.config.shell().err(), "{}\r", string)?;
161-
Ok(())
193+
Some(string)
162194
}
163195
}
164196

@@ -169,6 +201,122 @@ fn clear(width: usize, config: &Config) {
169201

170202
impl<'cfg> Drop for State<'cfg> {
171203
fn drop(&mut self) {
172-
clear(self.max_width, self.config);
204+
clear(self.format.max_width, self.config);
173205
}
174206
}
207+
208+
#[test]
209+
fn test_progress_status() {
210+
let format = Format {
211+
style: ProgressStyle::Ratio,
212+
width: 40,
213+
max_width: 60,
214+
};
215+
assert_eq!(
216+
format.progress_status(0, 4, ""),
217+
Some("[ ] 0/4".to_string())
218+
);
219+
assert_eq!(
220+
format.progress_status(1, 4, ""),
221+
Some("[===> ] 1/4".to_string())
222+
);
223+
assert_eq!(
224+
format.progress_status(2, 4, ""),
225+
Some("[========> ] 2/4".to_string())
226+
);
227+
assert_eq!(
228+
format.progress_status(3, 4, ""),
229+
Some("[=============> ] 3/4".to_string())
230+
);
231+
assert_eq!(
232+
format.progress_status(4, 4, ""),
233+
Some("[===================] 4/4".to_string())
234+
);
235+
236+
assert_eq!(
237+
format.progress_status(3999, 4000, ""),
238+
Some("[===========> ] 3999/4000".to_string())
239+
);
240+
assert_eq!(
241+
format.progress_status(4000, 4000, ""),
242+
Some("[=============] 4000/4000".to_string())
243+
);
244+
245+
assert_eq!(
246+
format.progress_status(3, 4, ": short message"),
247+
Some("[=============> ] 3/4: short message".to_string())
248+
);
249+
assert_eq!(
250+
format.progress_status(3, 4, ": msg thats just fit"),
251+
Some("[=============> ] 3/4: msg thats just fit".to_string())
252+
);
253+
assert_eq!(
254+
format.progress_status(3, 4, ": msg that's just fit"),
255+
Some("[=============> ] 3/4: msg that's just...".to_string())
256+
);
257+
258+
// combining diacritics have width zero and thus can fit max_width.
259+
let zalgo_msg = "z̸̧̢̗͉̝̦͍̱ͧͦͨ̑̅̌ͥ́͢a̢ͬͨ̽ͯ̅̑ͥ͋̏̑ͫ̄͢͏̫̝̪̤͎̱̣͍̭̞̙̱͙͍̘̭͚l̶̡̛̥̝̰̭̹̯̯̞̪͇̱̦͙͔̘̼͇͓̈ͨ͗ͧ̓͒ͦ̀̇ͣ̈ͭ͊͛̃̑͒̿̕͜g̸̷̢̩̻̻͚̠͓̞̥͐ͩ͌̑ͥ̊̽͋͐̐͌͛̐̇̑ͨ́ͅo͙̳̣͔̰̠̜͕͕̞̦̙̭̜̯̹̬̻̓͑ͦ͋̈̉͌̃ͯ̀̂͠ͅ ̸̡͎̦̲̖̤̺̜̮̱̰̥͔̯̅̏ͬ̂ͨ̋̃̽̈́̾̔̇ͣ̚͜͜h̡ͫ̐̅̿̍̀͜҉̛͇̭̹̰̠͙̞ẽ̶̙̹̳̖͉͎̦͂̋̓ͮ̔ͬ̐̀͂̌͑̒͆̚͜͠ ͓͓̟͍̮̬̝̝̰͓͎̼̻ͦ͐̾̔͒̃̓͟͟c̮̦͍̺͈͚̯͕̄̒͐̂͊̊͗͊ͤͣ̀͘̕͝͞o̶͍͚͍̣̮͌ͦ̽̑ͩ̅ͮ̐̽̏͗́͂̅ͪ͠m̷̧͖̻͔̥̪̭͉͉̤̻͖̩̤͖̘ͦ̂͌̆̂ͦ̒͊ͯͬ͊̉̌ͬ͝͡e̵̹̣͍̜̺̤̤̯̫̹̠̮͎͙̯͚̰̼͗͐̀̒͂̉̀̚͝͞s̵̲͍͙͖̪͓͓̺̱̭̩̣͖̣ͤͤ͂̎̈͗͆ͨͪ̆̈͗͝͠";
260+
assert_eq!(
261+
format.progress_status(3, 4, zalgo_msg),
262+
Some("[=============> ] 3/4".to_string() + zalgo_msg)
263+
);
264+
265+
// some non-ASCII ellipsize test
266+
assert_eq!(
267+
format.progress_status(3, 4, "_123456789123456e\u{301}\u{301}8\u{301}90a"),
268+
Some("[=============> ] 3/4_123456789123456e\u{301}\u{301}...".to_string())
269+
);
270+
assert_eq!(
271+
format.progress_status(3, 4, ":每個漢字佔據了兩個字元"),
272+
Some("[=============> ] 3/4:每個漢字佔據了...".to_string())
273+
);
274+
}
275+
276+
#[test]
277+
fn test_progress_status_percentage() {
278+
let format = Format {
279+
style: ProgressStyle::Percentage,
280+
width: 40,
281+
max_width: 60,
282+
};
283+
assert_eq!(
284+
format.progress_status(0, 77, ""),
285+
Some("[ ] 0.00%".to_string())
286+
);
287+
assert_eq!(
288+
format.progress_status(1, 77, ""),
289+
Some("[ ] 1.30%".to_string())
290+
);
291+
assert_eq!(
292+
format.progress_status(76, 77, ""),
293+
Some("[=============> ] 98.70%".to_string())
294+
);
295+
assert_eq!(
296+
format.progress_status(77, 77, ""),
297+
Some("[===============] 100.00%".to_string())
298+
);
299+
}
300+
301+
#[test]
302+
fn test_progress_status_too_short() {
303+
let format = Format {
304+
style: ProgressStyle::Percentage,
305+
width: 25,
306+
max_width: 25,
307+
};
308+
assert_eq!(
309+
format.progress_status(1, 1, ""),
310+
Some("[] 100.00%".to_string())
311+
);
312+
313+
let format = Format {
314+
style: ProgressStyle::Percentage,
315+
width: 24,
316+
max_width: 24,
317+
};
318+
assert_eq!(
319+
format.progress_status(1, 1, ""),
320+
None
321+
);
322+
}

0 commit comments

Comments
 (0)