Example #1
0
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()
	}
}
Example #2
0
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
	}
}
Example #3
0
// 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
		}
	}
}