Beispiel #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()
	}
}
Beispiel #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
	}
}
Beispiel #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()
			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
		}
	}
}
Beispiel #4
0
// Reads charactes from input to write them to output, allowing 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.

	in := bufio.NewReader(input) // Read input.
	seq := make([]byte, 2)       // For escape sequences.
	seq2 := make([]byte, 1)      // Extended escape sequences.

	// Print the primary prompt.
	if err = ln.prompt(); err != nil {
		return "", err
	}

	// === Detect change of window size.
	wSize := term.DetectWinSize()

	go func() {
		for {
			<-wSize.Change // Wait for.

			// TODO(jwall): Check errors?
			_, ln.buf.winColumns, _ = tty.GetSize()
			ln.buf.refresh()
		}
	}()

	for {
		rune, _, err := in.ReadRune()
		if err != nil {
			return "", inputError(err.Error())
		}

		switch rune {
		default:
			if err = ln.buf.insertRune(rune); err != nil {
				return "", err
			}
			continue

		case 13: // enter
			line = ln.buf.toString()

			if ln.useHistory {
				ln.hist.Add(line)
			}
			if _, err = output.Write(_CR_LF); err != nil {
				return "", outputError(err.Error())
			}

			return strings.TrimSpace(line), nil

		case 127, 8: // backspace, Ctrl-h
			if err = ln.buf.deletePrev(); err != nil {
				return "", err
			}
			continue

		case 9: // horizontal tab
			// TODO: disabled by now
			continue

		case 3: // Ctrl-c
			if err = ln.buf.insertRunes(ctrlC); err != nil {
				return "", err
			}
			if _, err = output.Write(_CR_LF); err != nil {
				return "", outputError(err.Error())
			}
			if err = ln.prompt(); err != nil {
				return "", err
			}

			continue

		case 4: // Ctrl-d
			if err = ln.buf.insertRunes(ctrlD); err != nil {
				return "", err
			}
			if _, err = output.Write(_CR_LF); err != nil {
				return "", outputError(err.Error())
			}

			return "", ErrCtrlD

		// Escape sequence
		case 27: // Escape: Ctrl-[ ("033" in octal, "\x1b" in hexadecimal)
			if _, err = in.Read(seq); err != nil {
				return "", inputError(err.Error())
			}

			if seq[0] == 79 { // 'O'
				switch seq[1] {
				case 72: // Home: "\x1bOH"
					goto _start
				case 70: // End: "\x1bOF"
					goto _end
				}
			}

			if seq[0] == 91 { // Left square bracket: "["
				switch seq[1] {
				case 68: // "\x1b[D"
					goto _leftArrow
				case 67: // "\x1b[C"
					goto _rightArrow
				case 65, 66: // Up: "\x1b[A"; Down: "\x1b[B"
					goto _upDownArrow
				}

				// Extended escape.
				if seq[1] > 48 && seq[1] < 55 {
					if _, err = in.Read(seq2); err != nil {
						return "", inputError(err.Error())
					}

					if seq2[0] == 126 { // '~'
						switch seq[1] {
						//case 50: // Insert: "\x1b[2~"

						case 51: // Delete: "\x1b[3~"
							if err = ln.buf.delete(); err != nil {
								return "", err
							}
							//case 53: // RePag: "\x1b[5~"

							//case 54: // AvPag: "\x1b[6~"

						}
					}
				}
			}
			continue

		case 20: // Ctrl-t, swap actual character by the previous one.
			if err = ln.buf.swap(); err != nil {
				return "", err
			}
			continue

		case 21: // 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 11: // Ctrl+k, delete from current to end of line.
			if err = ln.buf.deleteRight(); err != nil {
				return "", err
			}
			continue

		case 1: // Ctrl+a, go to the start of the line.
			goto _start

		case 5: // Ctrl+e, go to the end of the line.
			goto _end

		case 2: // Ctrl-b
			goto _leftArrow

		case 6: // Ctrl-f
			goto _rightArrow

		case 16: // Ctrl-p
			seq[1] = 65
			goto _upDownArrow

		case 14: // Ctrl-n
			seq[1] = 66
			goto _upDownArrow
		}

	_upDownArrow: // Up and down arrow: history
		if !ln.useHistory {
			continue
		}

		// Up
		if seq[1] == 65 {
			anotherLine, err = ln.hist.Prev()
			// Down
		} 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.ps1Len:], anotherLine)

		if err = ln.buf.refresh(); err != nil {
			return "", err
		}
		continue

	_leftArrow:
		if err = ln.buf.backward(); err != nil {
			return "", err
		}
		continue

	_rightArrow:
		if err = ln.buf.forward(); err != nil {
			return "", err
		}
		continue

	_start:
		if err = ln.buf.start(); err != nil {
			return "", err
		}
		continue

	_end:
		if _, err = ln.buf.end(); err != nil {
			return "", err
		}
		continue
	}
	return
}