Skip to content

Commit 1421bed

Browse files
committed
Auto merge of #42732 - cengizIO:master, r=Mark-Simulacrum
Add pager support for `rustc --explain EXXXX` Hello! Fixes #32665. Thanks! **EDIT:** _I've limited access to a Windows machine so this is taking longer than I've anticipated_. 🐢 cc @alexcrichton @nikomatsakis @Mark-Simulacrum @retep998 @ollie27 @afiune
2 parents 2fbba5b + 06de114 commit 1421bed

File tree

1 file changed

+71
-3
lines changed

1 file changed

+71
-3
lines changed

src/librustc_driver/lib.rs

+71-3
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,11 @@ use std::cmp::max;
8484
use std::cmp::Ordering::Equal;
8585
use std::default::Default;
8686
use std::env;
87+
use std::ffi::OsString;
8788
use std::io::{self, Read, Write};
8889
use std::iter::repeat;
8990
use std::path::PathBuf;
90-
use std::process;
91+
use std::process::{self, Command, Stdio};
9192
use std::rc::Rc;
9293
use std::str;
9394
use std::sync::{Arc, Mutex};
@@ -343,6 +344,31 @@ pub trait CompilerCalls<'a> {
343344
#[derive(Copy, Clone)]
344345
pub struct RustcDefaultCalls;
345346

347+
// FIXME remove these and use winapi 0.3 instead
348+
// Duplicates: bootstrap/compile.rs, librustc_errors/emitter.rs
349+
#[cfg(unix)]
350+
fn stdout_isatty() -> bool {
351+
unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
352+
}
353+
354+
#[cfg(windows)]
355+
fn stdout_isatty() -> bool {
356+
type DWORD = u32;
357+
type BOOL = i32;
358+
type HANDLE = *mut u8;
359+
type LPDWORD = *mut u32;
360+
const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
361+
extern "system" {
362+
fn GetStdHandle(which: DWORD) -> HANDLE;
363+
fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL;
364+
}
365+
unsafe {
366+
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
367+
let mut out = 0;
368+
GetConsoleMode(handle, &mut out) != 0
369+
}
370+
}
371+
346372
fn handle_explain(code: &str,
347373
descriptions: &errors::registry::Registry,
348374
output: ErrorOutputType) {
@@ -354,19 +380,28 @@ fn handle_explain(code: &str,
354380
match descriptions.find_description(&normalised) {
355381
Some(ref description) => {
356382
let mut is_in_code_block = false;
383+
let mut text = String::new();
384+
357385
// Slice off the leading newline and print.
358386
for line in description[1..].lines() {
359387
let indent_level = line.find(|c: char| !c.is_whitespace())
360388
.unwrap_or_else(|| line.len());
361389
let dedented_line = &line[indent_level..];
362390
if dedented_line.starts_with("```") {
363391
is_in_code_block = !is_in_code_block;
364-
println!("{}", &line[..(indent_level+3)]);
392+
text.push_str(&line[..(indent_level+3)]);
365393
} else if is_in_code_block && dedented_line.starts_with("# ") {
366394
continue;
367395
} else {
368-
println!("{}", line);
396+
text.push_str(line);
369397
}
398+
text.push('\n');
399+
}
400+
401+
if stdout_isatty() {
402+
show_content_with_pager(&text);
403+
} else {
404+
print!("{}", text);
370405
}
371406
}
372407
None => {
@@ -375,6 +410,39 @@ fn handle_explain(code: &str,
375410
}
376411
}
377412

413+
fn show_content_with_pager(content: &String) {
414+
let pager_name = env::var_os("PAGER").unwrap_or_else(|| if cfg!(windows) {
415+
OsString::from("more.com")
416+
} else {
417+
OsString::from("less")
418+
});
419+
420+
let mut fallback_to_println = false;
421+
422+
match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
423+
Ok(mut pager) => {
424+
if let Some(mut pipe) = pager.stdin.as_mut() {
425+
if pipe.write_all(content.as_bytes()).is_err() {
426+
fallback_to_println = true;
427+
}
428+
}
429+
430+
if pager.wait().is_err() {
431+
fallback_to_println = true;
432+
}
433+
}
434+
Err(_) => {
435+
fallback_to_println = true;
436+
}
437+
}
438+
439+
// If pager fails for whatever reason, we should still print the content
440+
// to standard output
441+
if fallback_to_println {
442+
print!("{}", content);
443+
}
444+
}
445+
378446
impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
379447
fn early_callback(&mut self,
380448
matches: &getopts::Matches,

0 commit comments

Comments
 (0)