aboutsummaryrefslogtreecommitdiff
path: root/pkg/cli/query.go
diff options
context:
space:
mode:
authorStefan Majewsky <majewsky@gmx.net>2020-03-28 13:53:10 +0100
committerStefan Majewsky <majewsky@gmx.net>2020-03-28 13:53:10 +0100
commited2b9374599cc00467b1c27121318748cc0d3f49 (patch)
tree65e3200a0b69817b24ae772223d1d852a52458af /pkg/cli/query.go
parent1ff86053ddef8de3247909434e0bd7000c060f2a (diff)
downloadgofu-ed2b9374599cc00467b1c27121318748cc0d3f49.tar.gz
rename pkg/ to internal/
Diffstat (limited to 'pkg/cli/query.go')
-rw-r--r--pkg/cli/query.go295
1 files changed, 0 insertions, 295 deletions
diff --git a/pkg/cli/query.go b/pkg/cli/query.go
deleted file mode 100644
index add170c..0000000
--- a/pkg/cli/query.go
+++ /dev/null
@@ -1,295 +0,0 @@
-/*******************************************************************************
-*
-* Copyright 2017 Stefan Majewsky <majewsky@gmx.net>
-*
-* This program is free software: you can redistribute it and/or modify it under
-* the terms of the GNU General Public License as published by the Free Software
-* Foundation, either version 3 of the License, or (at your option) any later
-* version.
-*
-* This program 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 General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License along with
-* this program. If not, see <http://www.gnu.org/licenses/>.
-*
-*******************************************************************************/
-
-package cli
-
-import (
- "errors"
- "fmt"
- "io"
- "regexp"
- "strconv"
- "strings"
-
- terminal "golang.org/x/crypto/ssh/terminal"
-)
-
-//cannot use `var errInterrupted = errors.New("Interrupted!")` because golint
-//complains about the formatting of the error message
-type errInterrupted struct{}
-
-func (e errInterrupted) Error() string {
- return "Interrupted!"
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TUI implementation for when stdin is a terminal
-
-type terminalTUI struct {
- i *Implementation
-}
-
-func (t terminalTUI) Print(w io.Writer, msg string) {
- w.Write([]byte(msg))
-}
-
-func (t terminalTUI) ReadLine(prompt string) (string, error) {
- if prompt != "" {
- t.i.safeStdout().Write([]byte(strings.TrimSpace(prompt) + " "))
- }
- str, err := t.i.stdinBuf.ReadString('\n')
- return strings.TrimSpace(str), err
-}
-
-func (t terminalTUI) Confirm(question string) (bool, error) {
- out := t.i.safeStdout()
- out.Write([]byte(strings.TrimSpace(question) + " [y/n] "))
-
- buf := buffer{Input: t.i.stdin}
- for {
- switch string(buf.getNextInput()) {
- case "y", "Y":
- out.Write([]byte("-> yes\n"))
- return true, nil
- case "n", "N":
- out.Write([]byte("-> no\n"))
- return false, nil
- case "\x03": // Ctrl-C
- return false, errInterrupted{}
- }
- }
-}
-
-//Choice is a thing that the user can choose during Query().
-type Choice struct {
- //If given, the choice can be selected by pressing the key that
- //produces this character.
- Shortcut byte
- //The display string that describes this choice.
- Text string
- //The string to return from Implementation.Query().
- Return string
-}
-
-func (c Choice) hasShortcut() bool {
- return c.Shortcut != '\000'
-}
-
-func (t terminalTUI) Query(prompt string, choices ...Choice) (string, error) {
- if len(choices) == 0 {
- panic("no choices")
- }
-
- //disable line wrap; unexpected wrapping would confuse our cursor-moving code
- out := t.i.safeStdout()
- out.Write([]byte("\x1B[?7l"))
- defer out.Write([]byte("\x1B[?7h"))
-
- //display question
- out.Write([]byte(strings.TrimSuffix(prompt, "\n") + "\n"))
- selected := 0
-
- buf := buffer{Input: t.i.stdin}
-OUTER:
- for {
- displayChoices(out, choices, selected)
-
- input := buf.getNextInput()
- if len(input) == 1 {
- //single character entered - check if a shortcut matches
- for idx, choice := range choices {
- if choice.Shortcut == input[0] {
- selected = idx
- break OUTER
- }
- }
- }
-
- switch string(input) {
- case "\r", "\n":
- break OUTER
- case "\x1B[A": // Up arrow key
- selected--
- if selected < 0 {
- selected = 0
- }
- case "\x1B[B": // Down arrow key
- selected++
- if selected >= len(choices) {
- selected = len(choices) - 1
- }
- case "\x03": // Ctrl-C
- return "", errInterrupted{}
- }
-
- //prepare to re-render choices
- removeDisplayLines(out, len(choices))
- }
-
- //clear query display
- removeDisplayLines(out, len(choices)+1)
-
- //display question + chosen answer
- fmt.Fprintf(out, "%s -> %s\n",
- strings.TrimSuffix(prompt, "\n"),
- strings.TrimSpace(choices[selected].Text),
- )
-
- return choices[selected].Return, nil
-}
-
-func removeDisplayLines(stdout io.Writer, n int) {
- for idx := 0; idx < n; idx++ {
- stdout.Write([]byte("\x1B[A\x1B[2K"))
- }
-}
-
-func displayChoices(out io.Writer, choices []Choice, selectedIndex int) {
- hasShortcuts := false
- for _, choice := range choices {
- if choice.hasShortcut() {
- hasShortcuts = true
- break
- }
- }
-
- for idx, choice := range choices {
- text := " " + strings.TrimSpace(choice.Text) + " \n"
- if hasShortcuts {
- shortcut := choice.Shortcut
- if !choice.hasShortcut() {
- shortcut = ' '
- }
- text = fmt.Sprintf(" [%c]%s", shortcut, text)
- }
-
- if idx == selectedIndex {
- fmt.Fprintf(out, "\x1B[0;7m%s\x1B[0m", text)
- } else {
- out.Write([]byte(text))
- }
- }
-}
-
-var ansiEscapeRx = regexp.MustCompile(`^\x1B\[[\x20-\x3F]*[\x40-\x7E]`)
-
-type buffer struct {
- Input io.Reader
- buf [128]byte
- fill int
-}
-
-func (b *buffer) getNextInput() []byte {
- //do we have a simple input character?
- if b.fill > 0 && b.buf[0] != '\x1B' {
- result := append([]byte(nil), b.buf[0])
- copy(b.buf[0:], b.buf[1:])
- b.fill--
- return result
- }
-
- //do we have a full ANSI escape sequence?
- match := ansiEscapeRx.Find(b.buf[0:b.fill])
- if match != nil {
- result := append([]byte(nil), match...)
- copy(b.buf[0:], b.buf[len(match):])
- b.fill -= len(match)
- return result
- }
-
- oldState, err := terminal.MakeRaw(0)
- if err != nil {
- panic(err)
- }
- defer terminal.Restore(0, oldState)
-
- //fill buffer some more
- n, err := b.Input.Read(b.buf[b.fill:])
- if err != nil {
- panic(err)
- }
- b.fill += n
-
- return b.getNextInput() //restart
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TUI implementation for when stdin is a pipe (also used in unit tests)
-
-type pipeTUI struct {
- i *Implementation
-}
-
-//AnsiColorCodeRx is a regexp that matches ANSI escape sequences of the type SGR.
-var AnsiColorCodeRx = regexp.MustCompile("\x1B" + `\[[0-9;]*m`)
-
-func (t *pipeTUI) Print(w io.Writer, msg string) {
- w.Write([]byte(AnsiColorCodeRx.ReplaceAllString(msg, "")))
-}
-
-func (t *pipeTUI) ReadLine(prompt string) (string, error) {
- str, err := t.i.stdinBuf.ReadString('\n')
- str = strings.TrimSpace(str)
- if err == nil {
- fmt.Fprintf(t.i.stderr, "%s %s\n", strings.TrimSpace(prompt), str)
- }
- return str, err
-}
-
-func (t *pipeTUI) Confirm(question string) (bool, error) {
- question = strings.TrimSpace(question)
- str, err := t.ReadLine(question)
- if err != nil {
- return false, err
- }
- //recognize /[01tTfF]|true|false/
- ok, err := strconv.ParseBool(str)
- if err != nil {
- //recognize /[yY]|yes|no/
- ok = strings.HasPrefix(question, "y") || strings.HasPrefix(question, "Y")
- }
- fmt.Fprintf(t.i.stderr, "%s -> %v (%s)\n", question, ok, str)
- return ok, nil
-}
-
-//Query for the pipe TUI is limited to exact matches of the choice text, or
-//matching by choice shortcut.
-func (t *pipeTUI) Query(prompt string, choices ...Choice) (string, error) {
- str, err := t.i.stdinBuf.ReadString('\n')
- if err != nil {
- return str, err
- }
- str = strings.TrimSpace(str)
-
- //prefer exact match on choice.Text
- for _, choice := range choices {
- if choice.Text == str {
- fmt.Fprintf(t.i.stderr, "%s -> %s\n", prompt, choice.Text)
- return choice.Return, nil
- }
- }
- //allow match on choice.Shortcut
- for _, choice := range choices {
- if string(choice.Shortcut) == str {
- fmt.Fprintf(t.i.stderr, "%s -> %s\n", prompt, choice.Text)
- return choice.Return, nil
- }
- }
- fmt.Fprintf(t.i.stderr, "%s -> [%s]\n", prompt, str)
- return "", errors.New("cannot match input with available choices")
-}