about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-07-04 23:50:11 +0000
committerbors <bors@rust-lang.org>2017-07-04 23:50:11 +0000
commit1421bedd511c4c12889c2f22ec0b3e1687715372 (patch)
treec44bc8038b7450c167f13e1174de83b7352a29a5
parent2fbba5bdbadeef403a64e9e1568cdad225cbcec1 (diff)
parent06de114f898f6f92a3c6cfb220dc957611087ef9 (diff)
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
-rw-r--r--src/librustc_driver/lib.rs74
1 files changed, 71 insertions, 3 deletions
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index f2aacbc629f..802ed2a489b 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -84,10 +84,11 @@ use std::cmp::max;
 use std::cmp::Ordering::Equal;
 use std::default::Default;
 use std::env;
+use std::ffi::OsString;
 use std::io::{self, Read, Write};
 use std::iter::repeat;
 use std::path::PathBuf;
-use std::process;
+use std::process::{self, Command, Stdio};
 use std::rc::Rc;
 use std::str;
 use std::sync::{Arc, Mutex};
@@ -343,6 +344,31 @@ pub trait CompilerCalls<'a> {
 #[derive(Copy, Clone)]
 pub struct RustcDefaultCalls;
 
+// FIXME remove these and use winapi 0.3 instead
+// Duplicates: bootstrap/compile.rs, librustc_errors/emitter.rs
+#[cfg(unix)]
+fn stdout_isatty() -> bool {
+    unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
+}
+
+#[cfg(windows)]
+fn stdout_isatty() -> bool {
+    type DWORD = u32;
+    type BOOL = i32;
+    type HANDLE = *mut u8;
+    type LPDWORD = *mut u32;
+    const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
+    extern "system" {
+        fn GetStdHandle(which: DWORD) -> HANDLE;
+        fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL;
+    }
+    unsafe {
+        let handle = GetStdHandle(STD_OUTPUT_HANDLE);
+        let mut out = 0;
+        GetConsoleMode(handle, &mut out) != 0
+    }
+}
+
 fn handle_explain(code: &str,
                   descriptions: &errors::registry::Registry,
                   output: ErrorOutputType) {
@@ -354,6 +380,8 @@ fn handle_explain(code: &str,
     match descriptions.find_description(&normalised) {
         Some(ref description) => {
             let mut is_in_code_block = false;
+            let mut text = String::new();
+
             // Slice off the leading newline and print.
             for line in description[1..].lines() {
                 let indent_level = line.find(|c: char| !c.is_whitespace())
@@ -361,12 +389,19 @@ fn handle_explain(code: &str,
                 let dedented_line = &line[indent_level..];
                 if dedented_line.starts_with("```") {
                     is_in_code_block = !is_in_code_block;
-                    println!("{}", &line[..(indent_level+3)]);
+                    text.push_str(&line[..(indent_level+3)]);
                 } else if is_in_code_block && dedented_line.starts_with("# ") {
                     continue;
                 } else {
-                    println!("{}", line);
+                    text.push_str(line);
                 }
+                text.push('\n');
+            }
+
+            if stdout_isatty() {
+                show_content_with_pager(&text);
+            } else {
+                print!("{}", text);
             }
         }
         None => {
@@ -375,6 +410,39 @@ fn handle_explain(code: &str,
     }
 }
 
+fn show_content_with_pager(content: &String) {
+    let pager_name = env::var_os("PAGER").unwrap_or_else(|| if cfg!(windows) {
+        OsString::from("more.com")
+    } else {
+        OsString::from("less")
+    });
+
+    let mut fallback_to_println = false;
+
+    match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
+        Ok(mut pager) => {
+            if let Some(mut pipe) = pager.stdin.as_mut() {
+                if pipe.write_all(content.as_bytes()).is_err() {
+                    fallback_to_println = true;
+                }
+            }
+
+            if pager.wait().is_err() {
+                fallback_to_println = true;
+            }
+        }
+        Err(_) => {
+            fallback_to_println = true;
+        }
+    }
+
+    // If pager fails for whatever reason, we should still print the content
+    // to standard output
+    if fallback_to_println {
+        print!("{}", content);
+    }
+}
+
 impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
     fn early_callback(&mut self,
                       matches: &getopts::Matches,