Example #1
0
func newBuffer(promptLen int) *buffer {
	b := new(buffer)
	_, b.winColumns = term.GetWinsizeInChar()
	b.promptLen = promptLen
	b.data = make([]int, BufferLen, BufferCap)

	return b
}
Example #2
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 os.Error) {
	var anotherLine []int  // 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.
	go term.TrapWinsize()

	go func() {
		for {
			<-term.WinsizeChan // Wait for.

			_, ln.buf.winColumns = term.GetWinsizeInChar()
			ln.buf.refresh()
		}
	}()

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

		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.String())
			}

			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.String())
			}
			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.String())
			}

			return "", ErrCtrlD

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

			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.String())
					}

					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
}