aboutsummaryrefslogtreecommitdiffstats
path: root/console/prompter.go
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2016-05-06 17:40:23 +0800
committerPéter Szilágyi <peterke@gmail.com>2016-05-30 22:25:23 +0800
commitffaf58f0a98bd987bbe76e8669bb22c405dcd62a (patch)
tree4cd484504d4a7089cfd85d22a2a9fa62c79abb7d /console/prompter.go
parentab664c7e17009729d1083d8d4f7c37eb387012d3 (diff)
downloaddexon-ffaf58f0a98bd987bbe76e8669bb22c405dcd62a.tar.gz
dexon-ffaf58f0a98bd987bbe76e8669bb22c405dcd62a.tar.zst
dexon-ffaf58f0a98bd987bbe76e8669bb22c405dcd62a.zip
cmd, console: split off the console into a reusable package
Diffstat (limited to 'console/prompter.go')
-rw-r--r--console/prompter.go156
1 files changed, 156 insertions, 0 deletions
diff --git a/console/prompter.go b/console/prompter.go
new file mode 100644
index 000000000..5039e8b1c
--- /dev/null
+++ b/console/prompter.go
@@ -0,0 +1,156 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package console
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/peterh/liner"
+)
+
+// TerminalPrompter holds the stdin line reader (also using stdout for printing
+// prompts). Only this reader may be used for input because it keeps an internal
+// buffer.
+var TerminalPrompter = newTerminalPrompter()
+
+// UserPrompter defines the methods needed by the console to promt the user for
+// various types of inputs.
+type UserPrompter interface {
+ // PromptInput displays the given prompt to the user and requests some textual
+ // data to be entered, returning the input of the user.
+ PromptInput(prompt string) (string, error)
+
+ // PromptPassword displays the given prompt to the user and requests some textual
+ // data to be entered, but one which must not be echoed out into the terminal.
+ // The method returns the input provided by the user.
+ PromptPassword(prompt string) (string, error)
+
+ // PromptConfirm displays the given prompt to the user and requests a boolean
+ // choice to be made, returning that choice.
+ PromptConfirm(prompt string) (bool, error)
+
+ // SetScrollHistory sets the the input scrollback history that the prompter will
+ // allow the user to scoll back to.
+ SetScrollHistory(history []string)
+
+ // SetWordCompleter sets the completion function that the prompter will call to
+ // fetch completion candidates when the user presses tab.
+ SetWordCompleter(completer WordCompleter)
+}
+
+// WordCompleter takes the currently edited line with the cursor position and
+// returns the completion candidates for the partial word to be completed. If
+// the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello,
+// wo!!!", 9) is passed to the completer which may returns ("Hello, ", {"world",
+// "Word"}, "!!!") to have "Hello, world!!!".
+type WordCompleter func(line string, pos int) (string, []string, string)
+
+// terminalPrompter is a UserPrompter backed by the liner package. It supports
+// prompting the user for various input, among others for non-echoing password
+// input.
+type terminalPrompter struct {
+ *liner.State
+ warned bool
+ supported bool
+ normalMode liner.ModeApplier
+ rawMode liner.ModeApplier
+}
+
+// newTerminalPrompter creates a liner based user input prompter working off the
+// standard input and output streams.
+func newTerminalPrompter() *terminalPrompter {
+ r := new(terminalPrompter)
+ // Get the original mode before calling NewLiner.
+ // This is usually regular "cooked" mode where characters echo.
+ normalMode, _ := liner.TerminalMode()
+ // Turn on liner. It switches to raw mode.
+ r.State = liner.NewLiner()
+ rawMode, err := liner.TerminalMode()
+ if err != nil || !liner.TerminalSupported() {
+ r.supported = false
+ } else {
+ r.supported = true
+ r.normalMode = normalMode
+ r.rawMode = rawMode
+ // Switch back to normal mode while we're not prompting.
+ normalMode.ApplyMode()
+ }
+ r.SetCtrlCAborts(true)
+ r.SetTabCompletionStyle(liner.TabPrints)
+
+ return r
+}
+
+// PromptInput displays the given prompt to the user and requests some textual
+// data to be entered, returning the input of the user.
+func (r *terminalPrompter) PromptInput(prompt string) (string, error) {
+ if r.supported {
+ r.rawMode.ApplyMode()
+ defer r.normalMode.ApplyMode()
+ } else {
+ // liner tries to be smart about printing the prompt
+ // and doesn't print anything if input is redirected.
+ // Un-smart it by printing the prompt always.
+ fmt.Print(prompt)
+ prompt = ""
+ defer fmt.Println()
+ }
+ return r.State.Prompt(prompt)
+}
+
+// PromptPassword displays the given prompt to the user and requests some textual
+// data to be entered, but one which must not be echoed out into the terminal.
+// The method returns the input provided by the user.
+func (r *terminalPrompter) PromptPassword(prompt string) (passwd string, err error) {
+ if r.supported {
+ r.rawMode.ApplyMode()
+ defer r.normalMode.ApplyMode()
+ return r.State.PasswordPrompt(prompt)
+ }
+ if !r.warned {
+ fmt.Println("!! Unsupported terminal, password will be echoed.")
+ r.warned = true
+ }
+ // Just as in Prompt, handle printing the prompt here instead of relying on liner.
+ fmt.Print(prompt)
+ passwd, err = r.State.Prompt("")
+ fmt.Println()
+ return passwd, err
+}
+
+// PromptConfirm displays the given prompt to the user and requests a boolean
+// choice to be made, returning that choice.
+func (r *terminalPrompter) PromptConfirm(prompt string) (bool, error) {
+ input, err := r.Prompt(prompt + " [y/N] ")
+ if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" {
+ return true, nil
+ }
+ return false, err
+}
+
+// SetScrollHistory sets the the input scrollback history that the prompter will
+// allow the user to scoll back to.
+func (r *terminalPrompter) SetScrollHistory(history []string) {
+ r.State.ReadHistory(strings.NewReader(strings.Join(history, "\n")))
+}
+
+// SetWordCompleter sets the completion function that the prompter will call to
+// fetch completion candidates when the user presses tab.
+func (r *terminalPrompter) SetWordCompleter(completer WordCompleter) {
+ r.State.SetWordCompleter(liner.WordCompleter(completer))
+}