@@ -84,10 +84,11 @@ use std::cmp::max;
8484use std:: cmp:: Ordering :: Equal ;
8585use std:: default:: Default ;
8686use std:: env;
87+ use std:: ffi:: OsString ;
8788use std:: io:: { self , Read , Write } ;
8889use std:: iter:: repeat;
8990use std:: path:: PathBuf ;
90- use std:: process;
91+ use std:: process:: { self , Command , Stdio } ;
9192use std:: rc:: Rc ;
9293use std:: str;
9394use std:: sync:: { Arc , Mutex } ;
@@ -343,6 +344,31 @@ pub trait CompilerCalls<'a> {
343344#[ derive( Copy , Clone ) ]
344345pub 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+
346372fn 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+
378446impl < ' a > CompilerCalls < ' a > for RustcDefaultCalls {
379447 fn early_callback ( & mut self ,
380448 matches : & getopts:: Matches ,
0 commit comments