diff --git a/main.go b/main.go index 0c30e31..3eb5a76 100644 --- a/main.go +++ b/main.go @@ -26,8 +26,92 @@ const ( BackSpace rune = 127 ) -func readKey() (rune, error) { +type Buffer struct { + buf []rune + cursor int +} + +func (lb Buffer) String() string { + return string(lb.buf) +} + +func (lb *Buffer) AddChar(c rune, echo bool) { + if lb.cursor == len(lb.buf) { + lb.buf = append(lb.buf, c) + lb.cursor++ + if echo { + fmt.Printf("%c", c) + } + } else { + lb.buf = append(lb.buf[:lb.cursor+1], lb.buf[lb.cursor:]...) + lb.buf[lb.cursor] = c + lb.cursor++ + if echo { + s := string(lb.buf[lb.cursor-1:]) + fmt.Printf("%s\033[%dD", s, len(s)-1) + } + } +} + +func (lb *Buffer) controlInput(c rune) { + switch c { + case BackSpace: + lb.cursor-- + fmt.Printf("%c", BackSpace) + case LeftArrow: + if lb.cursor > 0 { + lb.cursor-- + fmt.Print("\033[1D") + } + case RightArrow: + if lb.cursor < len(lb.buf) { + lb.cursor++ + fmt.Print("\033[1C") + } + } +} + +func (lb *Buffer) seedContent(s string) { + for _, r := range s { + lb.AddChar(r, false) + } +} + +func GetText(prompt string, content string, endChar rune, includeEndChar bool) string { + b := Buffer{make([]rune, 0, (len(content)+1)*2), 0} + b.seedContent(content) + + fmt.Print(prompt) + fmt.Print(b.String()) + + var ch rune + var err error reader := bufio.NewReader(os.Stdin) + + for { + ch, err = readKey(reader) + if err != nil { + continue + } + if ch == endChar { + if includeEndChar { + b.AddChar(ch, true) + } + break + } + + if isControl(ch) { + b.controlInput(ch) + } else { + b.AddChar(ch, true) + } + + } + return b.String() +} + + +func readKey(reader *bufio.Reader) (rune, error) { char, _, err := reader.ReadRune() if err != nil { return 0, err @@ -57,11 +141,11 @@ func readKey() (rune, error) { char = PageUp case escSeq == "\033[6~": char = PageDown - case IsHomeKey(escSeq): + case isHomeKey(escSeq): char = Home - case IsEndKey(escSeq): + case isEndKey(escSeq): char = End - case escSeq == "\033[3~" || escSeq == "\033[P": + case isDeleteKey(escSeq): char = Delete default: fmt.Printf("Odd sequence: %s\n", escSeq[1:]) @@ -70,7 +154,25 @@ func readKey() (rune, error) { return char, nil } -func IsHomeKey(seq string) bool { +func isControl(c rune) bool { + switch c { + case UpArrow, DownArrow, RightArrow, LeftArrow, PageUp, PageDown, Home, End, Delete, BackSpace, CarriageReturn, NewLine, Escape: + return true + default: + return false + } +} + +func isDeleteKey(seq string) bool { + switch seq { + case "\033[3~", "\033[P": + return true + default: + return false + } +} + +func isHomeKey(seq string) bool { switch seq { case "\033[1~", "\033[7~", "\033[H", "\033OH": return true @@ -79,7 +181,7 @@ func IsHomeKey(seq string) bool { } } -func IsEndKey(seq string) bool { +func isEndKey(seq string) bool { switch seq { case "\033[4~", "\033[8~", "\033[F", "\033OF": return true @@ -120,17 +222,11 @@ func PrintKey(k rune) { func main() { termios.SetCharMode() + defer termios.Restore() + str := "Hi" for { - k, e := readKey() - if e != nil { - fmt.Println(e.Error()) - continue - } - PrintKey(k) - if k == NewLine || k == CarriageReturn { - break - } + fmt.Print("\n") + str = GetText("> ", str, '\n', false) } - termios.Restore() }