func newBuffer(promptLen int) *buffer { b := new(buffer) _, b.winColumns = term.GetWinsizeInChar() b.promptLen = promptLen b.data = make([]int, BufferLen, BufferCap) return b }
// 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 }