123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- package hhc_common
- import (
- "fmt"
- "io"
- "strings"
- )
- type RunesEditor struct {
- currentLine []rune
- history [][]rune
- insertPosition int
- historyPosition int
- cleanLineOffset int
- cleanLineCount int
- lastTermWidth int
- lastTermPeekOut []string
- }
- func NewRunesEditor() *RunesEditor {
- re := &RunesEditor{}
- re.ClearAll()
- return re
- }
- func (re *RunesEditor) ClearHistory() {
- re.history = make([][]rune, 0)
- re.historyPosition = 0
- }
- func (re *RunesEditor) ClearCurrentLine(saveHistory bool) {
- if saveHistory {
- if len(re.currentLine) > 0 {
- l := len(re.history)
- if l > 0 {
- if !TestRuneSlice(re.history[l-1], re.currentLine) {
- re.history = append(re.history, re.currentLine)
- }
- } else {
- re.history = append(re.history, re.currentLine)
- }
- }
- }
- re.historyPosition = len(re.history)
- re.currentLine = []rune{}
- re.insertPosition = 0
- }
- func (re *RunesEditor) ClearAll() {
- re.ClearCurrentLine(false)
- re.ClearHistory()
- }
- func (re *RunesEditor) fixInsertPos() {
- bl := len(re.currentLine)
- if re.insertPosition > bl {
- re.insertPosition = bl
- }
- if re.insertPosition < 0 {
- re.insertPosition = 0
- }
- }
- func (re *RunesEditor) InsertString(s string) {
- rs := []rune(s)
- for _, v := range rs {
- re.InsertRune(v)
- }
- }
- func (re *RunesEditor) InsertRune(r rune) {
- re.fixInsertPos()
- p := re.insertPosition
- switch {
- case p == 0:
- nrs := make([]rune, 1, len(re.currentLine)+1)
- nrs[0] = r
- re.currentLine = append(nrs, re.currentLine...)
- break
- case p == len(re.currentLine):
- re.currentLine = append(re.currentLine, r)
- break
- default:
- left := re.currentLine[:p]
- right := re.currentLine[p:]
- nrs := make([]rune, 0, len(re.currentLine)+1)
- nrs = append(nrs, left...)
- nrs = append(nrs, r)
- re.currentLine = append(nrs, right...)
- break
- }
- re.insertPosition++
- re.fixInsertPos()
- }
- func (re *RunesEditor) MoveInsertPosition(offset int) {
- re.insertPosition += offset
- re.fixInsertPos()
- }
- func (re *RunesEditor) SetInsertPosition(pos int) {
- re.insertPosition = pos
- re.fixInsertPos()
- }
- func (re *RunesEditor) Backspace() {
- re.fixInsertPos()
- l := len(re.currentLine)
- switch {
- case re.insertPosition <= 0:
- re.insertPosition = 0
- return
- case l == 0:
- return
- case re.insertPosition == l:
- re.currentLine = re.currentLine[:l-1]
- break
- default:
- p := re.insertPosition
- left := re.currentLine[:p-1]
- right := re.currentLine[p:]
- nrs := make([]rune, 0, len(re.currentLine)-1)
- nrs = append(nrs, left...)
- re.currentLine = append(nrs, right...)
- break
- }
- re.insertPosition--
- re.fixInsertPos()
- }
- func (re *RunesEditor) Delete() {
- re.fixInsertPos()
- l := len(re.currentLine)
- switch {
- case re.insertPosition == l:
- return
- default:
- p := re.insertPosition
- left := re.currentLine[:p]
- right := re.currentLine[p+1:]
- nrs := make([]rune, 0, len(re.currentLine)-1)
- nrs = append(nrs, left...)
- re.currentLine = append(nrs, right...)
- break
- }
- re.fixInsertPos()
- }
- func (re *RunesEditor) Enter() []rune {
- ret := re.currentLine
- re.ClearCurrentLine(true)
- return ret
- }
- func (re *RunesEditor) Peek() []rune {
- return re.currentLine
- }
- func (re *RunesEditor) fixHistoryPos() {
- l := len(re.history)
- if re.historyPosition < 0 {
- re.historyPosition = 0
- }
- if re.historyPosition > l {
- re.historyPosition = l
- }
- }
- func (re *RunesEditor) LoadNextHistory() {
- l := len(re.history)
- if re.historyPosition == l {
- return
- }
- re.historyPosition++
- re.fixHistoryPos()
- if re.historyPosition == l {
- re.ClearCurrentLine(false)
- return
- }
- re.currentLine = re.history[re.historyPosition]
- re.MoveInsertPositionToEnd()
- }
- func (re *RunesEditor) LoadPreviousHistory() {
- l := len(re.history)
- if l == 0 {
- return
- }
- re.historyPosition--
- re.fixHistoryPos()
- if re.historyPosition == l {
- re.ClearCurrentLine(false)
- return
- }
- re.currentLine = re.history[re.historyPosition]
- re.MoveInsertPositionToEnd()
- }
- func (re *RunesEditor) MoveInsertPositionToEnd() {
- re.insertPosition = len(re.currentLine)
- }
- func (re *RunesEditor) MoveInsertPositionToHome() {
- re.insertPosition = 0
- }
- func (re *RunesEditor) GetInsertPosition() int {
- return re.insertPosition
- }
- func (re *RunesEditor) CleanTerminalPeek(wr io.Writer) {
- switch {
- case re.cleanLineOffset > 0:
- _, _ = fmt.Fprintf(wr, "\033[%dA", re.cleanLineOffset)
- break
- case re.cleanLineOffset < 0:
- _, _ = fmt.Fprintf(wr, "\033[%dB", -re.cleanLineOffset)
- break
- }
- for i := 0; i < re.cleanLineCount; i++ {
- _, _ = fmt.Fprint(wr, "\r\033[K")
- if i != re.cleanLineCount-1 {
- _, _ = fmt.Fprint(wr, "\033[1A")
- }
- }
- }
- func (re *RunesEditor) backTerminalPeekCursorToHome(wr io.Writer) {
- switch {
- case re.cleanLineOffset > 0:
- _, _ = fmt.Fprintf(wr, "\033[%dA", re.cleanLineOffset)
- break
- case re.cleanLineOffset < 0:
- _, _ = fmt.Fprintf(wr, "\033[%dB", -re.cleanLineOffset)
- break
- }
- for i := 0; i < re.cleanLineCount; i++ {
- if i != re.cleanLineCount-1 {
- _, _ = fmt.Fprint(wr, "\033[1A")
- } else {
- _, _ = fmt.Fprint(wr, "\r")
- }
- }
- }
- func (re *RunesEditor) RefreshTerminal(wr io.Writer, prefix string, termWidth int, diffRefresh bool) {
- if diffRefresh {
- if termWidth != re.lastTermWidth {
- re.hardRefreshTerminal(wr, prefix, termWidth)
- return
- }
- // TODO: Support for diffRefresh
- // Now temporary using hardRefresh instead.
- re.hardRefreshTerminal(wr, prefix, termWidth)
- return
- } else {
- re.hardRefreshTerminal(wr, prefix, termWidth)
- }
- }
- func (re *RunesEditor) hardRefreshTerminal(wr io.Writer, prefix string, termWidth int) {
- re.CleanTerminalPeek(wr)
- re.PeekIntoTerminal(prefix, termWidth, wr)
- }
- func (re *RunesEditor) PeekIntoTerminal(prefix string, termWidth int, wr io.Writer) {
- re.lastTermWidth = termWidth
- re.fixInsertPos()
- rc := re.Peek()
- sc := string(rc)
- psb := strings.Builder{}
- psb.WriteString(prefix)
- psb.WriteString(sc)
- ws := psb.String()
- lpre := len([]rune(prefix))
- curpos := lpre + re.insertPosition
- lines, cy, cx := PrintRunesIntoLinesWithCursor([]rune(ws), termWidth, curpos)
- re.lastTermPeekOut = lines
- re.cleanLineCount = len(lines)
- lastln := re.cleanLineCount - 1
- lastline := lines[lastln]
- lll := len(lastline)
- for i, line := range lines {
- _, _ = fmt.Fprint(wr, line)
- if i != lastln {
- _, _ = fmt.Fprint(wr, "\r\n")
- }
- }
- bky := lastln - cy
- bkx := lll - cx + 1
- re.cleanLineOffset = -bky
- //fmt.Println("BKY=", bky, "BKX=", bkx)
- switch {
- case bky > 0:
- _, _ = fmt.Fprintf(wr, "\033[%dA", bky)
- break
- case bky < 0:
- _, _ = fmt.Fprintf(wr, "\033[%dB", -bky)
- break
- }
- switch {
- case bkx > 0:
- _, _ = fmt.Fprintf(wr, "\033[%dD", bkx)
- break
- case bkx < 0:
- _, _ = fmt.Fprintf(wr, "\033[%dC", -bkx)
- break
- }
- }
|