kitty/tools/tui/password.go
2022-11-14 15:41:49 +05:30

91 lines
2.0 KiB
Go

package tui
import (
"errors"
"fmt"
"strings"
"github.com/mattn/go-runewidth"
)
type KilledBySignal struct {
Msg string
SignalName string
}
func (self *KilledBySignal) Error() string { return self.Msg }
var Canceled = errors.New("Canceled by user")
func ReadPassword(prompt string, kill_if_signaled bool) (password string, err error) {
loop, err := CreateLoop()
shadow := ""
if err != nil {
return
}
loop.OnText = func(loop *Loop, text string, from_key_event bool, in_bracketed_paste bool) error {
old_width := runewidth.StringWidth(password)
password += text
new_width := runewidth.StringWidth(password)
if new_width > old_width {
extra := strings.Repeat("*", new_width-old_width)
loop.QueueWriteString(extra)
shadow += extra
}
return nil
}
loop.OnKeyEvent = func(loop *Loop, event *KeyEvent) error {
if event.MatchesPressOrRepeat("backscape") || event.MatchesPressOrRepeat("delete") {
event.Handled = true
if len(password) > 0 {
old_width := runewidth.StringWidth(password)
password = password[:len(password)-1]
new_width := runewidth.StringWidth(password)
delta := new_width - old_width
if delta > 0 {
if delta > len(shadow) {
delta = len(shadow)
}
shadow = shadow[:len(shadow)-delta]
loop.QueueWriteString(strings.Repeat("\x08\x1b[P", delta))
}
} else {
loop.Beep()
}
}
if event.MatchesPressOrRepeat("enter") || event.MatchesPressOrRepeat("return") {
event.Handled = true
if password == "" {
loop.Quit(1)
} else {
loop.Quit(0)
}
}
if event.MatchesPressOrRepeat("esc") {
event.Handled = true
loop.Quit(1)
return Canceled
}
return nil
}
err = loop.Run()
if err != nil {
return
}
ds := loop.DeathSignalName()
if ds != "" {
if kill_if_signaled {
loop.KillIfSignalled()
return
}
return "", &KilledBySignal{Msg: fmt.Sprint("Killed by signal: ", ds), SignalName: ds}
}
if loop.ExitCode() != 0 {
password = ""
}
return password, nil
}