@@ -84,10 +84,11 @@ use std::cmp::max;
84
84
use std:: cmp:: Ordering :: Equal ;
85
85
use std:: default:: Default ;
86
86
use std:: env;
87
+ use std:: ffi:: OsString ;
87
88
use std:: io:: { self , Read , Write } ;
88
89
use std:: iter:: repeat;
89
90
use std:: path:: PathBuf ;
90
- use std:: process;
91
+ use std:: process:: { self , Command , Stdio } ;
91
92
use std:: rc:: Rc ;
92
93
use std:: str;
93
94
use std:: sync:: { Arc , Mutex } ;
@@ -343,6 +344,31 @@ pub trait CompilerCalls<'a> {
343
344
#[ derive( Copy , Clone ) ]
344
345
pub struct RustcDefaultCalls ;
345
346
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
+
346
372
fn handle_explain ( code : & str ,
347
373
descriptions : & errors:: registry:: Registry ,
348
374
output : ErrorOutputType ) {
@@ -354,19 +380,28 @@ fn handle_explain(code: &str,
354
380
match descriptions. find_description ( & normalised) {
355
381
Some ( ref description) => {
356
382
let mut is_in_code_block = false ;
383
+ let mut text = String :: new ( ) ;
384
+
357
385
// Slice off the leading newline and print.
358
386
for line in description[ 1 ..] . lines ( ) {
359
387
let indent_level = line. find ( |c : char | !c. is_whitespace ( ) )
360
388
. unwrap_or_else ( || line. len ( ) ) ;
361
389
let dedented_line = & line[ indent_level..] ;
362
390
if dedented_line. starts_with ( "```" ) {
363
391
is_in_code_block = !is_in_code_block;
364
- println ! ( "{}" , & line[ ..( indent_level+3 ) ] ) ;
392
+ text . push_str ( & line[ ..( indent_level+3 ) ] ) ;
365
393
} else if is_in_code_block && dedented_line. starts_with ( "# " ) {
366
394
continue ;
367
395
} else {
368
- println ! ( "{}" , line) ;
396
+ text . push_str ( line) ;
369
397
}
398
+ text. push ( '\n' ) ;
399
+ }
400
+
401
+ if stdout_isatty ( ) {
402
+ show_content_with_pager ( & text) ;
403
+ } else {
404
+ print ! ( "{}" , text) ;
370
405
}
371
406
}
372
407
None => {
@@ -375,6 +410,39 @@ fn handle_explain(code: &str,
375
410
}
376
411
}
377
412
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
+
378
446
impl < ' a > CompilerCalls < ' a > for RustcDefaultCalls {
379
447
fn early_callback ( & mut self ,
380
448
matches : & getopts:: Matches ,
0 commit comments