github.com/attic-labs/noms@v0.0.0-20210827224422-e5fa29d95e8b/go/util/outputpager/page_output.go (about) 1 // Copyright 2016 Attic Labs, Inc. All rights reserved. 2 // Licensed under the Apache License, version 2.0: 3 // http://www.apache.org/licenses/LICENSE-2.0 4 5 package outputpager 6 7 import ( 8 "io" 9 "os" 10 "os/exec" 11 "sync" 12 13 "github.com/attic-labs/kingpin" 14 goisatty "github.com/mattn/go-isatty" 15 16 "github.com/attic-labs/noms/go/d" 17 ) 18 19 var ( 20 noPager bool 21 ) 22 23 type Pager struct { 24 Writer io.Writer 25 stdin, stdout *os.File 26 mtx *sync.Mutex 27 doneCh chan struct{} 28 } 29 30 func Start() *Pager { 31 if noPager || !IsStdoutTty() { 32 return &Pager{os.Stdout, nil, nil, nil, nil} 33 } 34 35 lessPath, err := exec.LookPath("less") 36 d.Chk.NoError(err) 37 38 // -F ... Quit if entire file fits on first screen. 39 // -S ... Chop (truncate) long lines rather than wrapping. 40 // -R ... Output "raw" control characters. 41 // -X ... Don't use termcap init/deinit strings. 42 cmd := exec.Command(lessPath, "-FSRX") 43 44 stdin, stdout, err := os.Pipe() 45 d.Chk.NoError(err) 46 cmd.Stdout = os.Stdout 47 cmd.Stderr = os.Stderr 48 cmd.Stdin = stdin 49 cmd.Start() 50 51 p := &Pager{stdout, stdin, stdout, &sync.Mutex{}, make(chan struct{})} 52 go func() { 53 err := cmd.Wait() 54 d.Chk.NoError(err) 55 p.closePipe() 56 p.doneCh <- struct{}{} 57 }() 58 return p 59 } 60 61 func (p *Pager) Stop() { 62 if p.Writer != os.Stdout { 63 p.closePipe() 64 // Wait until less has fully exited, otherwise it might not have printed the terminal restore characters. 65 <-p.doneCh 66 } 67 } 68 69 func (p *Pager) closePipe() { 70 p.mtx.Lock() 71 defer p.mtx.Unlock() 72 if p.stdin != nil { 73 // Closing the pipe will cause any outstanding writes to stdout fail, and fail from now on. 74 p.stdin.Close() 75 p.stdout.Close() 76 p.stdin, p.stdout = nil, nil 77 } 78 } 79 80 func RegisterOutputpagerFlags(cmd *kingpin.CmdClause) { 81 cmd.Flag("no-pager", "suppress paging functionality").BoolVar(&noPager) 82 } 83 84 func IsStdoutTty() bool { 85 return goisatty.IsTerminal(os.Stdout.Fd()) 86 }