func main() { flag.Parse() log.SetFlags(0) log.SetPrefix("--- FAIL: ") if *DebugKey { Lookup() return } if *DebugWinSize { win := term.DetectWinSize() defer win.Close() fmt.Println("[Resize the window: should print a number every time]") for i := 0; i < 7; i++ { select { case <-win.Change: fmt.Printf("%d ", i) case <-time.After(13 * time.Second): fmt.Print("\ntimed out\n") return } } return } if !*IsInteractive { pr, pw = io.Pipe() term.Input = pr } TestCharMode() TestEchoMode() if *IsInteractive { TestPassword() } TestEditLine() if *IsInteractive { TestDetectSize() } }
func TestDetectSize() { fmt.Print("\n=== RUN TestDetectSize\n") ter, _ := term.New() defer func() { if err := ter.Restore(); err != nil { log.Print(err) } }() row, col, err := ter.GetSize() if err != nil { panic(err) } winSize := term.DetectWinSize() fmt.Println("[Change the size of the terminal]") // I want to finish the test. go func() { time.Sleep(10 * time.Second) winSize.Change <- true }() <-winSize.Change winSize.Close() row2, col2, err := ter.GetSize() if err != nil { panic(err) } if row == row2 && col == col2 { log.Print("the terminal size got the same value") return } }
// Read reads charactes from input to write them to output, enabling line editing. // The errors that could return are to indicate if Ctrl+D was pressed, and for // both input/output errors. func (ln *Line) Read() (line string, err error) { var anotherLine []rune // For lines got from history. var isHistoryUsed bool // If the history has been accessed. var action keyAction in := bufio.NewReader(term.Input) // Read input. esc := make([]byte, 2) // For escape sequences. extEsc := make([]byte, 3) // Extended escape sequences. // Print the primary prompt. if err = ln.Prompt(); err != nil { return "", err } // == Detect change of window size. winSize := term.DetectWinSize() go func() { for { select { case <-winSize.Change: // Wait for. _, col, err := ln.ter.GetSize() if err != nil { ln.buf.columns = col ln.buf.refresh() } } } }() defer winSize.Close() for ; ; action = 0 { char, _, err := in.ReadRune() if err != nil { return "", inputError(err.Error()) } _S: switch char { default: if err = ln.buf.insertRune(char); err != nil { return "", err } continue case sys.K_RETURN: line = ln.buf.toString() if ln.useHistory { ln.hist.Add(line) } if _, err = term.Output.Write(CRLF); err != nil { return "", outputError(err.Error()) } return strings.TrimSpace(line), nil case sys.K_TAB: // TODO: disabled by now continue case sys.K_BACK, sys.K_CTRL_H: if err = ln.buf.deleteCharPrev(); err != nil { return "", err } continue case sys.K_CTRL_C: if err = ln.buf.insertRunes(CtrlC); err != nil { return "", err } if _, err = term.Output.Write(CRLF); err != nil { return "", outputError(err.Error()) } ChanCtrlC <- 1 //TODO: is really necessary? if err = ln.Prompt(); err != nil { return "", err } continue case sys.K_CTRL_D: if err = ln.buf.insertRunes(CtrlD); err != nil { return "", err } if _, err = term.Output.Write(CRLF); err != nil { return "", outputError(err.Error()) } ln.Restore() ChanCtrlD <- 1 return "", ErrCtrlD // Escape sequence case sys.K_ESCAPE: // Ctrl+[ ("\x1b" in hexadecimal, "033" in octal) if _, err = in.Read(esc); err != nil { return "", inputError(err.Error()) } if esc[0] == 79 { // 'O' switch esc[1] { case 72: // Home: "\x1b O H" action = _HOME break _S case 70: // End: "\x1b O F" action = _END break _S } } if esc[0] == 91 { // '[' switch esc[1] { case 65: // Up: "\x1b [ A" if !ln.useHistory { continue } action = _UP break _S case 66: // Down: "\x1b [ B" if !ln.useHistory { continue } action = _DOWN break _S case 68: // "\x1b [ D" action = _LEFT break _S case 67: // "\x1b [ C" action = _RIGHT break _S } // Extended escape. if esc[1] > 48 && esc[1] < 55 { if _, err = in.Read(extEsc); err != nil { return "", inputError(err.Error()) } if extEsc[0] == 126 { // '~' switch esc[1] { //case 50: // Insert: "\x1b [ 2 ~" case 51: // Delete: "\x1b [ 3 ~" if err = ln.buf.deleteChar(); err != nil { return "", err } continue //case 53: // RePag: "\x1b [ 5 ~" //case 54: // AvPag: "\x1b [ 6 ~" } } if esc[1] == 49 && extEsc[0] == 59 && extEsc[1] == 53 { // "1;5" switch extEsc[2] { case 68: // Ctrl+left arrow: "\x1b [ 1 ; 5 D" // move to last word if err = ln.buf.wordBackward(); err != nil { return "", err } continue case 67: // Ctrl+right arrow: "\x1b [ 1 ; 5 C" // move to next word if err = ln.buf.wordForward(); err != nil { return "", err } continue } } } } continue case sys.K_CTRL_T: // Swap actual character by the previous one. if err = ln.buf.swap(); err != nil { return "", err } continue case sys.K_CTRL_L: // Clear screen. if _, err = term.Output.Write(DelScreenToUpper); err != nil { return "", err } if err = ln.Prompt(); err != nil { return "", err } continue case sys.K_CTRL_U: // Delete the whole line. if err = ln.buf.deleteLine(); err != nil { return "", err } if err = ln.Prompt(); err != nil { return "", err } continue case sys.K_CTRL_K: // Delete from current to end of line. if err = ln.buf.deleteToRight(); err != nil { return "", err } continue case sys.K_CTRL_P: // Up if !ln.useHistory { continue } action = _UP case sys.K_CTRL_N: // Down if !ln.useHistory { continue } action = _DOWN case sys.K_CTRL_B: // Left action = _LEFT case sys.K_CTRL_F: // Right action = _RIGHT case sys.K_CTRL_A: // Start of line. action = _HOME case sys.K_CTRL_E: // End of line. action = _END } switch action { case _UP, _DOWN: // Up and down arrow: history if action == _UP { anotherLine, err = ln.hist.Prev() } else { anotherLine, err = ln.hist.Next() } if err != nil { continue } // Update the current history entry before to overwrite it with // the next one. // TODO: it has to be removed before of to be saved the history if !isHistoryUsed { ln.hist.Add(ln.buf.toString()) } isHistoryUsed = true ln.buf.grow(len(anotherLine)) ln.buf.size = len(anotherLine) + ln.buf.promptLen copy(ln.buf.data[ln.lenPS1:], anotherLine) if err = ln.buf.refresh(); err != nil { return "", err } continue case _LEFT: if _, err = ln.buf.backward(); err != nil { return "", err } continue case _RIGHT: if _, err = ln.buf.forward(); err != nil { return "", err } continue case _HOME: if err = ln.buf.start(); err != nil { return "", err } continue case _END: if _, err = ln.buf.end(); err != nil { return "", err } continue } } }