handle race condition while setting raw mode in windows (#2509)

This commit is contained in:
Patrick Devine 2024-02-14 21:28:35 -08:00 committed by GitHub
parent 9241a29336
commit 42e77e2a69
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 38 additions and 15 deletions

View file

@ -32,6 +32,8 @@ func (p *Prompt) placeholder() string {
type Terminal struct { type Terminal struct {
outchan chan rune outchan chan rune
rawmode bool
termios any
} }
type Instance struct { type Instance struct {
@ -60,6 +62,16 @@ func New(prompt Prompt) (*Instance, error) {
} }
func (i *Instance) Readline() (string, error) { func (i *Instance) Readline() (string, error) {
if !i.Terminal.rawmode {
fd := int(syscall.Stdin)
termios, err := SetRawMode(fd)
if err != nil {
return "", err
}
i.Terminal.rawmode = true
i.Terminal.termios = termios
}
prompt := i.Prompt.prompt() prompt := i.Prompt.prompt()
if i.Pasting { if i.Pasting {
// force alt prompt when pasting // force alt prompt when pasting
@ -67,13 +79,12 @@ func (i *Instance) Readline() (string, error) {
} }
fmt.Print(prompt) fmt.Print(prompt)
fd := int(syscall.Stdin) defer func() {
termios, err := SetRawMode(fd) fd := int(syscall.Stdin)
if err != nil { // nolint: errcheck
return "", err UnsetRawMode(fd, i.Terminal.termios)
} i.Terminal.rawmode = false
// nolint: errcheck }()
defer UnsetRawMode(fd, termios)
buf, _ := NewBuffer(i.Prompt) buf, _ := NewBuffer(i.Prompt)
@ -205,7 +216,8 @@ func (i *Instance) Readline() (string, error) {
case CharCtrlW: case CharCtrlW:
buf.DeleteWord() buf.DeleteWord()
case CharCtrlZ: case CharCtrlZ:
return handleCharCtrlZ(fd, termios) fd := int(syscall.Stdin)
return handleCharCtrlZ(fd, i.Terminal.termios)
case CharEnter: case CharEnter:
output := buf.String() output := buf.String()
if output != "" { if output != "" {
@ -236,8 +248,16 @@ func (i *Instance) HistoryDisable() {
} }
func NewTerminal() (*Terminal, error) { func NewTerminal() (*Terminal, error) {
fd := int(syscall.Stdin)
termios, err := SetRawMode(fd)
if err != nil {
return nil, err
}
t := &Terminal{ t := &Terminal{
outchan: make(chan rune), outchan: make(chan rune),
rawmode: true,
termios: termios,
} }
go t.ioloop() go t.ioloop()

View file

@ -6,8 +6,9 @@ import (
"syscall" "syscall"
) )
func handleCharCtrlZ(fd int, termios *Termios) (string, error) { func handleCharCtrlZ(fd int, termios any) (string, error) {
if err := UnsetRawMode(fd, termios); err != nil { t := termios.(*Termios)
if err := UnsetRawMode(fd, t); err != nil {
return "", err return "", err
} }

View file

@ -1,6 +1,6 @@
package readline package readline
func handleCharCtrlZ(fd int, state *State) (string, error) { func handleCharCtrlZ(fd int, state any) (string, error) {
// not supported // not supported
return "", nil return "", nil
} }

View file

@ -25,8 +25,9 @@ func SetRawMode(fd int) (*Termios, error) {
return termios, setTermios(fd, &newTermios) return termios, setTermios(fd, &newTermios)
} }
func UnsetRawMode(fd int, termios *Termios) error { func UnsetRawMode(fd int, termios any) error {
return setTermios(fd, termios) t := termios.(*Termios)
return setTermios(fd, t)
} }
// IsTerminal returns true if the given file descriptor is a terminal. // IsTerminal returns true if the given file descriptor is a terminal.

View file

@ -56,7 +56,8 @@ func SetRawMode(fd int) (*State, error) {
return &State{st}, nil return &State{st}, nil
} }
func UnsetRawMode(fd int, state *State) error { func UnsetRawMode(fd int, state any) error {
_, _, err := syscall.SyscallN(procSetConsoleMode.Addr(), uintptr(fd), uintptr(state.mode), 0) s := state.(*State)
_, _, err := syscall.SyscallN(procSetConsoleMode.Addr(), uintptr(fd), uintptr(s.mode), 0)
return err return err
} }