Skip to content
This repository has been archived by the owner on Apr 19, 2024. It is now read-only.

[WIP] Make survey work under mintty on Windows #456

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions core/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ var TemplateFuncsNoColor = map[string]interface{}{
},
}

//RunTemplate returns two formatted strings given a template and
//the data it requires. The first string returned is generated for
//user-facing output and may or may not contain ANSI escape codes
//for colored output. The second string does not contain escape codes
//and can be used by the renderer for layout purposes.
// RunTemplate returns two formatted strings given a template and
// the data it requires. The first string returned is generated for
// user-facing output and may or may not contain ANSI escape codes
// for colored output. The second string does not contain escape codes
// and can be used by the renderer for layout purposes.
func RunTemplate(tmpl string, data interface{}) (string, string, error) {
tPair, err := GetTemplatePair(tmpl)
if err != nil {
Expand All @@ -52,11 +52,11 @@ var (
memoMutex = &sync.RWMutex{}
)

//GetTemplatePair returns a pair of compiled templates where the
//first template is generated for user-facing output and the
//second is generated for use by the renderer. The second
//template does not contain any color escape codes, whereas
//the first template may or may not depending on DisableColor.
// GetTemplatePair returns a pair of compiled templates where the
// first template is generated for user-facing output and the
// second is generated for use by the renderer. The second
// template does not contain any color escape codes, whereas
// the first template may or may not depending on DisableColor.
func GetTemplatePair(tmpl string) ([2]*template.Template, error) {
memoMutex.RLock()
if t, ok := memoizedGetTemplate[tmpl]; ok {
Expand Down
11 changes: 6 additions & 5 deletions core/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,12 @@ func (err errFieldNotMatch) Is(target error) bool { // implements the dynamic er
//
// Usage:
// err := survey.Ask(qs, &v);
// if err != nil {
// if name, ok := core.IsFieldNotMatch(err); ok {
// [...name is the not matched question name]
// }
// }
//
// if err != nil {
// if name, ok := core.IsFieldNotMatch(err); ok {
// [...name is the not matched question name]
// }
// }
func IsFieldNotMatch(err error) (string, bool) {
if err != nil {
if v, ok := err.(errFieldNotMatch); ok {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module github.com/AlecAivazis/survey/v2

require (
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2
github.com/cli/safeexec v1.0.0
github.com/creack/pty v1.1.17
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -21,7 +23,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
6 changes: 5 additions & 1 deletion input.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package survey

import (
"errors"
"fmt"

"github.com/AlecAivazis/survey/v2/core"
"github.com/AlecAivazis/survey/v2/terminal"
Expand Down Expand Up @@ -152,10 +153,13 @@ func (i *Input) Prompt(config *PromptConfig) (interface{}, error) {

// start reading runes from the standard in
rr := i.NewRuneReader()
_ = rr.SetTermMode()
if err := rr.SetTermMode(); err != nil {
return "", fmt.Errorf("SetTermMode: %w", err)
}
defer func() {
_ = rr.RestoreTermMode()
}()

cursor := i.NewCursor()
if !config.ShowCursor {
cursor.Hide() // hide the cursor
Expand Down
4 changes: 3 additions & 1 deletion select.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,9 @@ func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
}

rr := s.NewRuneReader()
_ = rr.SetTermMode()
if err := rr.SetTermMode(); err != nil {
return "", fmt.Errorf("SetTermMode: %w", err)
}
defer func() {
_ = rr.RestoreTermMode()
}()
Expand Down
1 change: 0 additions & 1 deletion survey.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,6 @@ in the documentation. For example:
}

survey.AskOne(prompt, &name)

*/
func AskOne(p Prompt, response interface{}, opts ...AskOpt) error {
err := Ask([]*Question{{Prompt: p}}, response, opts...)
Expand Down
1 change: 1 addition & 0 deletions terminal/display_posix.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows

package terminal
Expand Down
1 change: 1 addition & 0 deletions terminal/output.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows

package terminal
Expand Down
71 changes: 71 additions & 0 deletions terminal/runereader.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package terminal

import (
"bufio"
"fmt"
"unicode"

Expand Down Expand Up @@ -377,6 +378,76 @@ func (rr *RuneReader) ReadLineWithDefault(mask rune, d []rune, onRunes ...OnRune
}
}

const (
normalKeypad = '['
applicationKeypad = 'O'
)

// readRunePOSIX parses escape sequences such as ESC [ A for arrow keys.
//
// See https://vt100.net/docs/vt102-ug/appendixc.html
func readRunePOSIX(reader *bufio.Reader, discardNext bool) (rune, int, error) {
r, size, err := reader.ReadRune()
if err != nil {
return r, size, err
}

if r != KeyEscape {
return r, size, err
}

if reader.Buffered() == 0 {
// no more characters so must be `Esc` key
return KeyEscape, 1, nil
}

r, size, err = reader.ReadRune()
if err != nil {
return r, size, err
}

// ESC O ... or ESC [ ...?
if r != normalKeypad && r != applicationKeypad {
return r, size, fmt.Errorf("unexpected escape sequence from terminal: %q", []rune{KeyEscape, r})
}

keypad := r

r, size, err = reader.ReadRune()
if err != nil {
return r, size, err
}

switch r {
case 'A': // ESC [ A or ESC O A
return KeyArrowUp, 1, nil
case 'B': // ESC [ B or ESC O B
return KeyArrowDown, 1, nil
case 'C': // ESC [ C or ESC O C
return KeyArrowRight, 1, nil
case 'D': // ESC [ D or ESC O D
return KeyArrowLeft, 1, nil
case 'F': // ESC [ F or ESC O F
return SpecialKeyEnd, 1, nil
case 'H': // ESC [ H or ESC O H
return SpecialKeyHome, 1, nil
case '3': // ESC [ 3
if keypad == normalKeypad {
if discardNext {
// discard the following '~' key from buffer
_, _ = reader.Discard(1)
}
return SpecialKeyDelete, 1, nil
}
}

if discardNext {
// discard the following '~' key from buffer
_, _ = reader.Discard(1)
}
return IgnoreKey, 1, nil
}

func runeWidth(r rune) int {
switch width.LookupRune(r).Kind() {
case width.EastAsianWide, width.EastAsianFullwidth:
Expand Down
1 change: 1 addition & 0 deletions terminal/runereader_bsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build darwin || dragonfly || freebsd || netbsd || openbsd
// +build darwin dragonfly freebsd netbsd openbsd

package terminal
Expand Down
1 change: 1 addition & 0 deletions terminal/runereader_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2013 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.
//go:build linux && !ppc64le
// +build linux,!ppc64le

package terminal
Expand Down
65 changes: 2 additions & 63 deletions terminal/runereader_posix.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows

// The terminal mode manipulation code is derived heavily from:
Expand All @@ -11,16 +12,10 @@ package terminal
import (
"bufio"
"bytes"
"fmt"
"syscall"
"unsafe"
)

const (
normalKeypad = '['
applicationKeypad = 'O'
)

type runeReaderState struct {
term syscall.Termios
reader *bufio.Reader
Expand Down Expand Up @@ -70,62 +65,6 @@ func (rr *RuneReader) RestoreTermMode() error {
return nil
}

// ReadRune Parse escape sequences such as ESC [ A for arrow keys.
// See https://vt100.net/docs/vt102-ug/appendixc.html
func (rr *RuneReader) ReadRune() (rune, int, error) {
r, size, err := rr.state.reader.ReadRune()
if err != nil {
return r, size, err
}

if r != KeyEscape {
return r, size, err
}

if rr.state.reader.Buffered() == 0 {
// no more characters so must be `Esc` key
return KeyEscape, 1, nil
}

r, size, err = rr.state.reader.ReadRune()
if err != nil {
return r, size, err
}

// ESC O ... or ESC [ ...?
if r != normalKeypad && r != applicationKeypad {
return r, size, fmt.Errorf("unexpected escape sequence from terminal: %q", []rune{KeyEscape, r})
}

keypad := r

r, size, err = rr.state.reader.ReadRune()
if err != nil {
return r, size, err
}

switch r {
case 'A': // ESC [ A or ESC O A
return KeyArrowUp, 1, nil
case 'B': // ESC [ B or ESC O B
return KeyArrowDown, 1, nil
case 'C': // ESC [ C or ESC O C
return KeyArrowRight, 1, nil
case 'D': // ESC [ D or ESC O D
return KeyArrowLeft, 1, nil
case 'F': // ESC [ F or ESC O F
return SpecialKeyEnd, 1, nil
case 'H': // ESC [ H or ESC O H
return SpecialKeyHome, 1, nil
case '3': // ESC [ 3
if keypad == normalKeypad {
// discard the following '~' key from buffer
_, _ = rr.state.reader.Discard(1)
return SpecialKeyDelete, 1, nil
}
}

// discard the following '~' key from buffer
_, _ = rr.state.reader.Discard(1)
return IgnoreKey, 1, nil
return readRunePOSIX(rr.state.reader, true)
}
1 change: 1 addition & 0 deletions terminal/runereader_ppc64le.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build ppc64le && linux
// +build ppc64le,linux

package terminal
Expand Down
Loading