|  | // Copyright 2018 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | // This file contains a driver.UI implementation | 
|  | // that provides the readline functionality if possible. | 
|  |  | 
|  | //go:build (darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows) && !appengine && !android | 
|  |  | 
|  | package main | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "io" | 
|  | "os" | 
|  | "strings" | 
|  |  | 
|  | "github.com/google/pprof/driver" | 
|  | "golang.org/x/term" | 
|  | ) | 
|  |  | 
|  | func init() { | 
|  | newUI = newReadlineUI | 
|  | } | 
|  |  | 
|  | // readlineUI implements driver.UI interface using the | 
|  | // golang.org/x/term package. | 
|  | // The upstream pprof command implements the same functionality | 
|  | // using the github.com/chzyer/readline package. | 
|  | type readlineUI struct { | 
|  | term *term.Terminal | 
|  | } | 
|  |  | 
|  | func newReadlineUI() driver.UI { | 
|  | // disable readline UI in dumb terminal. (golang.org/issue/26254) | 
|  | if v := strings.ToLower(os.Getenv("TERM")); v == "" || v == "dumb" { | 
|  | return nil | 
|  | } | 
|  | // test if we can use term.ReadLine | 
|  | // that assumes operation in the raw mode. | 
|  | oldState, err := term.MakeRaw(0) | 
|  | if err != nil { | 
|  | return nil | 
|  | } | 
|  | term.Restore(0, oldState) | 
|  |  | 
|  | rw := struct { | 
|  | io.Reader | 
|  | io.Writer | 
|  | }{os.Stdin, os.Stderr} | 
|  | return &readlineUI{term: term.NewTerminal(rw, "")} | 
|  | } | 
|  |  | 
|  | // Read returns a line of text (a command) read from the user. | 
|  | // prompt is printed before reading the command. | 
|  | func (r *readlineUI) ReadLine(prompt string) (string, error) { | 
|  | r.term.SetPrompt(prompt) | 
|  |  | 
|  | // skip error checking because we tested it | 
|  | // when creating this readlineUI initially. | 
|  | oldState, _ := term.MakeRaw(0) | 
|  | defer term.Restore(0, oldState) | 
|  |  | 
|  | s, err := r.term.ReadLine() | 
|  | return s, err | 
|  | } | 
|  |  | 
|  | // Print shows a message to the user. | 
|  | // It formats the text as fmt.Print would and adds a final \n if not already present. | 
|  | // For line-based UI, Print writes to standard error. | 
|  | // (Standard output is reserved for report data.) | 
|  | func (r *readlineUI) Print(args ...interface{}) { | 
|  | r.print(false, args...) | 
|  | } | 
|  |  | 
|  | // PrintErr shows an error message to the user. | 
|  | // It formats the text as fmt.Print would and adds a final \n if not already present. | 
|  | // For line-based UI, PrintErr writes to standard error. | 
|  | func (r *readlineUI) PrintErr(args ...interface{}) { | 
|  | r.print(true, args...) | 
|  | } | 
|  |  | 
|  | func (r *readlineUI) print(withColor bool, args ...interface{}) { | 
|  | text := fmt.Sprint(args...) | 
|  | if !strings.HasSuffix(text, "\n") { | 
|  | text += "\n" | 
|  | } | 
|  | if withColor { | 
|  | text = colorize(text) | 
|  | } | 
|  | fmt.Fprint(r.term, text) | 
|  | } | 
|  |  | 
|  | // colorize prints the msg in red using ANSI color escapes. | 
|  | func colorize(msg string) string { | 
|  | const red = 31 | 
|  | var colorEscape = fmt.Sprintf("\033[0;%dm", red) | 
|  | var colorResetEscape = "\033[0m" | 
|  | return colorEscape + msg + colorResetEscape | 
|  | } | 
|  |  | 
|  | // IsTerminal reports whether the UI is known to be tied to an | 
|  | // interactive terminal (as opposed to being redirected to a file). | 
|  | func (r *readlineUI) IsTerminal() bool { | 
|  | const stdout = 1 | 
|  | return term.IsTerminal(stdout) | 
|  | } | 
|  |  | 
|  | // WantBrowser indicates whether browser should be opened with the -http option. | 
|  | func (r *readlineUI) WantBrowser() bool { | 
|  | return r.IsTerminal() | 
|  | } | 
|  |  | 
|  | // SetAutoComplete instructs the UI to call complete(cmd) to obtain | 
|  | // the auto-completion of cmd, if the UI supports auto-completion at all. | 
|  | func (r *readlineUI) SetAutoComplete(complete func(string) string) { | 
|  | // TODO: Implement auto-completion support. | 
|  | } |