Skip to content

Commit

Permalink
Rework output processing
Browse files Browse the repository at this point in the history
Currently the output processing is done via first casting the byte
slice to a string and then processing each rune in that.
This causes issues where we need to process the byte stream in a raw
format.
This commit alters the processing of the output stream, only casting to
a rune when appropriate.
This is a nessesarry prerequisite for transparent printing features.
This commit also adds the state to the terminal struct, this allows for
no side effects when dealing with multiple threads altering the state.
  • Loading branch information
mgazza authored and andydotxyz committed Dec 30, 2023
1 parent dace678 commit 1c9d526
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 55 deletions.
133 changes: 78 additions & 55 deletions output.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package terminal

import (
"time"
"unicode/utf8"

"fyne.io/fyne/v2/widget"
)
Expand Down Expand Up @@ -79,8 +80,6 @@ var decSpecialGraphics = map[rune]rune{
'~': '·', // centered dot
}

var previous *parseState

type parseState struct {
code string
esc int
Expand All @@ -92,67 +91,44 @@ func (t *Terminal) handleOutput(buf []byte) {
if t.hasSelectedText() {
t.clearSelectedText()
}
state := &parseState{}
if previous != nil {
state = previous
previous = nil
} else {
state.esc = noEscape
if t.state == nil {
t.state = &parseState{
esc: noEscape,
}
}
var (
size int
r rune
i = -1
)
for {
i++
buf = buf[size:]
r, size = utf8.DecodeRune(buf)
if size == 0 {
break
}

for i, r := range []rune(string(buf)) {
if r == asciiEscape {
state.esc = i
t.state.esc = i
continue
}
if state.esc == i-1 {
if r == '[' {
if t.state.esc == i-1 {
if cont := t.parseEscState(r); cont {
continue
}
switch r {
case '\\':
t.handleOSC(state.code)
state.code = ""
state.osc = false
case ']':
state.osc = true
case '(', ')':
state.vt100 = r
case '7':
t.savedRow = t.cursorRow
t.savedCol = t.cursorCol
case '8':
t.cursorRow = t.savedRow
t.cursorCol = t.savedCol
case 'D':
t.scrollDown()
case 'M':
t.scrollUp()
case '=', '>':
}
state.esc = noEscape
t.state.esc = noEscape
continue
}
if state.osc {
if r == asciiBell || r == 0 {
t.handleOSC(state.code)
state.code = ""
state.osc = false
} else {
state.code += string(r)
}
if t.state.osc {
t.parseOSC(r)
continue
} else if state.vt100 != 0 {
t.handleVT100(string([]rune{state.vt100, r}))
state.vt100 = 0
} else if t.state.vt100 != 0 {
t.handleVT100(string([]rune{t.state.vt100, r}))
t.state.vt100 = 0
continue
} else if state.esc != noEscape {
state.code += string(r)
if (r < '0' || r > '9') && r != ';' && r != '=' && r != '?' && r != '>' {
t.handleEscape(state.code)
state.code = ""
state.esc = noEscape
}
} else if t.state.esc != noEscape {
t.parseEscape(r)
continue
}

Expand All @@ -173,9 +149,56 @@ func (t *Terminal) handleOutput(buf []byte) {
}

// record progress for next chunk of buffer
if state.esc != noEscape {
state.esc = -1 - (len(state.code))
previous = state
if t.state.esc != noEscape {
t.state.esc = -1
}
}

func (t *Terminal) parseEscState(r rune) (shouldContinue bool) {
switch r {
case '[':
return true
case '\\':
if t.state.osc {
t.handleOSC(t.state.code)
}
t.state.code = ""
t.state.osc = false
case ']':
t.state.osc = true
case '(', ')':
t.state.vt100 = r
case '7':
t.savedRow = t.cursorRow
t.savedCol = t.cursorCol
case '8':
t.cursorRow = t.savedRow
t.cursorCol = t.savedCol
case 'D':
t.scrollDown()
case 'M':
t.scrollUp()
case '=', '>':
}
return false
}

func (t *Terminal) parseEscape(r rune) {
t.state.code += string(r)
if (r < '0' || r > '9') && r != ';' && r != '=' && r != '?' && r != '>' {
t.handleEscape(t.state.code)
t.state.code = ""
t.state.esc = noEscape
}
}

func (t *Terminal) parseOSC(r rune) {
if r == asciiBell || r == 0 {
t.handleOSC(t.state.code)
t.state.code = ""
t.state.osc = false
} else {
t.state.code += string(r)
}
}

Expand Down
1 change: 1 addition & 0 deletions term.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type Terminal struct {
}
newLineMode bool // new line mode or line feed mode
bracketedPasteMode bool
state *parseState
}

// Cursor is used for displaying a specific cursor.
Expand Down

0 comments on commit 1c9d526

Please sign in to comment.