Skip to content

Commit d39570f

Browse files
authored
Merge pull request #1820 from rbtcollins/1800
Bug 1800: only open terminfo once
2 parents 892dc98 + 9d2164a commit d39570f

File tree

8 files changed

+384
-247
lines changed

8 files changed

+384
-247
lines changed

src/cli/common.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use std::process::{Command, Stdio};
1414
use std::sync::Arc;
1515
use std::time::Duration;
1616
use std::{cmp, iter};
17+
use term2::Terminal;
1718
use wait_timeout::ChildExt;
1819

1920
pub fn confirm(question: &str, default: bool) -> Result<bool> {

src/cli/log.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::term2;
22
use std::fmt;
33
use std::io::Write;
4+
use term2::Terminal;
45

56
macro_rules! warn {
67
( $ ( $ arg : tt ) * ) => ( $crate::log::warn_fmt ( format_args ! ( $ ( $ arg ) * ) ) )

src/cli/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod download_tracker;
2222
mod errors;
2323
mod help;
2424
mod job;
25+
mod markdown;
2526
mod proxy_mode;
2627
mod rustup_mode;
2728
mod self_update;

src/cli/markdown.rs

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Write Markdown to the terminal
2+
3+
use crate::term2::{color, Attr, Terminal};
4+
use markdown::tokenize;
5+
use markdown::{Block, ListItem, Span};
6+
use std::io;
7+
8+
// Handles the wrapping of text written to the console
9+
struct LineWrapper<'a, T: Terminal> {
10+
indent: u32,
11+
margin: u32,
12+
pos: u32,
13+
pub w: &'a mut T,
14+
}
15+
16+
impl<'a, T: Terminal + 'a> LineWrapper<'a, T> {
17+
// Just write a newline
18+
fn write_line(&mut self) {
19+
let _ = writeln!(self.w);
20+
// Reset column position to start of line
21+
self.pos = 0;
22+
}
23+
// Called before writing text to ensure indent is applied
24+
fn write_indent(&mut self) {
25+
if self.pos == 0 {
26+
// Write a space for each level of indent
27+
for _ in 0..self.indent {
28+
let _ = write!(self.w, " ");
29+
}
30+
self.pos = self.indent;
31+
}
32+
}
33+
// Write a non-breaking word
34+
fn write_word(&mut self, word: &str) {
35+
// Ensure correct indentation
36+
self.write_indent();
37+
let word_len = word.len() as u32;
38+
39+
// If this word goes past the margin
40+
if self.pos + word_len > self.margin {
41+
// And adding a newline would give us more space
42+
if self.pos > self.indent {
43+
// Then add a newline!
44+
self.write_line();
45+
self.write_indent();
46+
}
47+
}
48+
49+
// Write the word
50+
let _ = write!(self.w, "{}", word);
51+
self.pos += word_len;
52+
}
53+
fn write_space(&mut self) {
54+
if self.pos > self.indent {
55+
if self.pos < self.margin {
56+
self.write_word(" ");
57+
} else {
58+
self.write_line();
59+
}
60+
}
61+
}
62+
// Writes a span of text which wraps at the margin
63+
fn write_span(&mut self, text: &str) {
64+
// Allow words to wrap on whitespace
65+
let mut is_first = true;
66+
for word in text.split(char::is_whitespace) {
67+
if is_first {
68+
is_first = false;
69+
} else {
70+
self.write_space();
71+
}
72+
self.write_word(word);
73+
}
74+
}
75+
// Constructor
76+
fn new(w: &'a mut T, indent: u32, margin: u32) -> Self {
77+
LineWrapper {
78+
indent,
79+
margin,
80+
pos: indent,
81+
w,
82+
}
83+
}
84+
}
85+
86+
// Handles the formatting of text
87+
struct LineFormatter<'a, T: Terminal + io::Write> {
88+
wrapper: LineWrapper<'a, T>,
89+
attrs: Vec<Attr>,
90+
}
91+
92+
impl<'a, T: Terminal + io::Write + 'a> LineFormatter<'a, T> {
93+
fn new(w: &'a mut T, indent: u32, margin: u32) -> Self {
94+
LineFormatter {
95+
wrapper: LineWrapper::new(w, indent, margin),
96+
attrs: Vec::new(),
97+
}
98+
}
99+
fn push_attr(&mut self, attr: Attr) {
100+
self.attrs.push(attr);
101+
let _ = self.wrapper.w.attr(attr);
102+
}
103+
fn pop_attr(&mut self) {
104+
self.attrs.pop();
105+
let _ = self.wrapper.w.reset();
106+
for attr in &self.attrs {
107+
let _ = self.wrapper.w.attr(*attr);
108+
}
109+
}
110+
fn do_spans(&mut self, spans: Vec<Span>) {
111+
for span in spans {
112+
match span {
113+
Span::Break => {}
114+
Span::Text(text) => {
115+
self.wrapper.write_span(&text);
116+
}
117+
Span::Code(code) => {
118+
self.push_attr(Attr::Bold);
119+
self.wrapper.write_word(&code);
120+
self.pop_attr();
121+
}
122+
Span::Emphasis(spans) => {
123+
self.push_attr(Attr::ForegroundColor(color::BRIGHT_RED));
124+
self.do_spans(spans);
125+
self.pop_attr();
126+
}
127+
_ => {}
128+
}
129+
}
130+
}
131+
fn do_block(&mut self, b: Block) {
132+
match b {
133+
Block::Header(spans, _) => {
134+
self.push_attr(Attr::Bold);
135+
self.wrapper.write_line();
136+
self.do_spans(spans);
137+
self.wrapper.write_line();
138+
self.pop_attr();
139+
}
140+
Block::CodeBlock(code) => {
141+
self.wrapper.write_line();
142+
self.wrapper.indent += 2;
143+
for line in code.lines() {
144+
// Don't word-wrap code lines
145+
self.wrapper.write_word(line);
146+
self.wrapper.write_line();
147+
}
148+
self.wrapper.indent -= 2;
149+
}
150+
Block::Paragraph(spans) => {
151+
self.wrapper.write_line();
152+
self.do_spans(spans);
153+
self.wrapper.write_line();
154+
}
155+
Block::UnorderedList(items) => {
156+
self.wrapper.write_line();
157+
for item in items {
158+
self.wrapper.indent += 2;
159+
match item {
160+
ListItem::Simple(spans) => {
161+
self.do_spans(spans);
162+
}
163+
ListItem::Paragraph(blocks) => {
164+
for block in blocks {
165+
self.do_block(block);
166+
}
167+
}
168+
}
169+
self.wrapper.write_line();
170+
self.wrapper.indent -= 2;
171+
}
172+
}
173+
_ => {}
174+
}
175+
}
176+
}
177+
178+
pub fn md<'a, S: AsRef<str>, T: Terminal + io::Write + 'a>(t: &'a mut T, content: S) {
179+
let mut f = LineFormatter::new(t, 0, 79);
180+
let blocks = tokenize(content.as_ref());
181+
for b in blocks {
182+
f.do_block(b);
183+
}
184+
}

src/cli/rustup_mode.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::errors::*;
33
use crate::help::*;
44
use crate::self_update;
55
use crate::term2;
6+
use crate::term2::Terminal;
67
use clap::{App, AppSettings, Arg, ArgGroup, ArgMatches, Shell, SubCommand};
78
use rustup::dist::dist::{PartialTargetTriple, PartialToolchainDesc, TargetTriple};
89
use rustup::dist::manifest::Component;
@@ -813,7 +814,7 @@ fn show(cfg: &Cfg) -> Result<()> {
813814
}
814815
}
815816

816-
fn print_header(t: &mut term2::Terminal<std::io::Stdout>, s: &str) -> Result<()> {
817+
fn print_header(t: &mut term::StdoutTerminal, s: &str) -> Result<()> {
817818
t.attr(term2::Attr::Bold)?;
818819
writeln!(t, "{}", s)?;
819820
writeln!(t, "{}", iter::repeat("-").take(s.len()).collect::<String>())?;

src/cli/self_update.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
3333
use crate::common::{self, Confirm};
3434
use crate::errors::*;
35+
use crate::markdown::md;
3536
use crate::term2;
3637
use rustup::dist::dist;
3738
use rustup::utils::utils;
@@ -227,11 +228,12 @@ pub fn install(no_prompt: bool, verbose: bool, mut opts: InstallOpts) -> Result<
227228
check_existence_of_rustc_or_cargo_in_path(no_prompt)?;
228229
do_anti_sudo_check(no_prompt)?;
229230

231+
let mut term = term2::stdout();
230232
if !do_msvc_check(&opts)? {
231233
if no_prompt {
232234
warn!("installing msvc toolchain without its prerequisites");
233235
} else {
234-
term2::stdout().md(MSVC_MESSAGE);
236+
md(&mut term, MSVC_MESSAGE);
235237
if !common::confirm("\nContinue? (Y/n)", true)? {
236238
info!("aborting installation");
237239
return Ok(());
@@ -242,10 +244,10 @@ pub fn install(no_prompt: bool, verbose: bool, mut opts: InstallOpts) -> Result<
242244
if !no_prompt {
243245
let msg = pre_install_msg(opts.no_modify_path)?;
244246

245-
term2::stdout().md(msg);
247+
md(&mut term, msg);
246248

247249
loop {
248-
term2::stdout().md(current_install_opts(&opts));
250+
md(&mut term, current_install_opts(&opts));
249251
match common::confirm_advanced()? {
250252
Confirm::No => {
251253
info!("aborting installation");
@@ -312,7 +314,7 @@ pub fn install(no_prompt: bool, verbose: bool, mut opts: InstallOpts) -> Result<
312314
cargo_home = cargo_home
313315
)
314316
};
315-
term2::stdout().md(msg);
317+
md(&mut term, msg);
316318

317319
if !no_prompt {
318320
// On windows, where installation happens in a console
@@ -745,7 +747,7 @@ pub fn uninstall(no_prompt: bool) -> Result<()> {
745747
if !no_prompt {
746748
println!();
747749
let msg = format!(pre_uninstall_msg!(), cargo_home = canonical_cargo_home()?);
748-
term2::stdout().md(msg);
750+
md(&mut term2::stdout(), msg);
749751
if !common::confirm("\nContinue? (y/N)", false)? {
750752
info!("aborting uninstallation");
751753
return Ok(());

0 commit comments

Comments
 (0)