parser.go 3.54 KB
package utils

import (
	"bytes"
	"fmt"
	"unicode/utf8"
)

func ParseTerminalData(p []byte) (lines []string) {
	c := bytes.NewReader(p)
	pasteActive := false
	var line []rune
	var pos int
	var remainder []byte
	var inBuf [256]byte
	for {
		rest := remainder
		lineOk := false
		for !lineOk {
			var key rune
			key, rest = bytesToKey(rest, pasteActive)
			if key == utf8.RuneError {
				break
			}
			if !pasteActive {
				if key == keyPasteStart {
					pasteActive = true
					if len(line) == 0 {
					}
					continue
				}
			} else if key == keyPasteEnd {
				pasteActive = false
				continue
			}

			switch key {
			case keyBackspace:
				if pos == 0 {
					continue
				}
				line, pos = EraseNPreviousChars(1, pos, line)
			case keyAltLeft:
				// move left by a word.
				pos -= CountToLeftWord(pos, line)
			case keyAltRight:
				// move right by a word.
				pos += CountToRightWord(pos, line)
			case keyLeft:
				if pos == 0 {
					continue
				}
				pos--
			case keyRight:
				if pos == len(line) {
					continue
				}
				pos++
			case keyHome:
				if pos == 0 {
					continue
				}
				pos = 0
			case keyEnd:
				if pos == len(line) {
					continue
				}
				pos = len(line)
			case keyUp:
				line = []rune{}
				pos = 0
			case keyDown:
				line = []rune{}
				pos = 0
			case keyEnter:
				lines = append(lines, string(line))
				line = line[:0]
				pos = 0
				lineOk = true
			case keyDeleteWord:
				// Delete zero or more spaces and then one or more characters.
				line, pos = EraseNPreviousChars(CountToLeftWord(pos, line), pos, line)
			case keyDeleteLine:
				line = line[:pos]
			case keyCtrlD:
				// Erase the character under the current position.
				// The EOF case when the line is empty is handled in
				// readLine().
				if pos < len(line) {
					pos++
					line, pos = EraseNPreviousChars(1, pos, line)
				}
			case keyCtrlU:
				line = line[:0]
			case keyClearScreen:
			default:
				if !isPrintable(key) {
					fmt.Println("could not printable: ", []byte(string(key)), " ", key)
					continue
				}
				line, pos = AddKeyToLine(key, pos, line)
			}

		}
		if len(rest) > 0 {
			n := copy(inBuf[:], rest)
			remainder = inBuf[:n]
		} else {
			remainder = nil
		}

		// remainder is a slice at the beginning of t.inBuf
		// containing a partial key sequence
		readBuf := inBuf[len(remainder):]

		var n int
		n, err := c.Read(readBuf)
		if err != nil {
			if len(line) > 0 {
				lines = append(lines, string(line))
			} else if len(rest) > 0 {
				lines = append(lines, string(rest))
			}

			return
		}
		remainder = inBuf[:n+len(remainder)]
	}
}

func EraseNPreviousChars(n, cPos int, line []rune) ([]rune, int) {
	if n == 0 {
		return line, cPos
	}
	if cPos < n {
		n = cPos
	}
	cPos -= n
	copy(line[cPos:], line[n+cPos:])
	return line[:len(line)-n], cPos
}

func CountToLeftWord(currentPos int, line []rune) int {
	if currentPos == 0 {
		return 0
	}

	pos := currentPos - 1
	for pos > 0 {
		if line[pos] != ' ' {
			break
		}
		pos--
	}
	for pos > 0 {
		if line[pos] == ' ' {
			pos++
			break
		}
		pos--
	}

	return currentPos - pos
}

func CountToRightWord(currentPos int, line []rune) int {
	pos := currentPos
	for pos < len(line) {
		if line[pos] == ' ' {
			break
		}
		pos++
	}
	for pos < len(line) {
		if line[pos] != ' ' {
			break
		}
		pos++
	}
	return pos - currentPos
}

func AddKeyToLine(key rune, pos int, line []rune) ([]rune, int) {
	if len(line) == cap(line) {
		newLine := make([]rune, len(line), 2*(1+len(line)))
		copy(newLine, line)
		line = newLine
	}
	line = line[:len(line)+1]
	copy(line[pos+1:], line[pos:])
	line[pos] = key
	pos++
	return line, pos
}