mirror of https://github.com/liamg/aminal.git
Vttest (#173)
* Correct handling of DeviceAttributes request * added DECCOLM support * added DECALN control sequence support * added NEL support * bug fix: Erase To Cursor should be inclusive * added support for 'Origin Mode' (DECOM) -- top/bottom margins only * vttest test 1 screen 3: margin tests fixes * added support for intermediate controls inside CSI sequence * added support for LNM (Line Feed/New Line Mode) * removed obsolete 'intermediate' parameter * window resize on programmatic CSI resize * DECCOLM should clear screen on both set and reset * bug fix in autowrap mode * TestCursorMovement runs all test cases; screen template images updated Signed-off-by: Max Risuhin <risuhin.max@gmail.com> * bug fix: line mode messing with autowrap * added ResetVerticalMargins() method * IsAutoWrap(), IsNewLineMode() * corrected DECALN * fixed NEL to work in Line Feed mode * tyding up: removed map of 1 element * Terminal state moved out from buffer * fix
This commit is contained in:
parent
3ea8a70874
commit
4e7b8b40e7
310
buffer/buffer.go
310
buffer/buffer.go
|
@ -11,29 +11,16 @@ import (
|
|||
|
||||
type Buffer struct {
|
||||
lines []Line
|
||||
cursorX uint16
|
||||
cursorY uint16
|
||||
viewHeight uint16
|
||||
viewWidth uint16
|
||||
cursorAttr CellAttributes
|
||||
displayChangeHandlers []chan bool
|
||||
savedX uint16
|
||||
savedY uint16
|
||||
scrollLinesFromBottom uint
|
||||
topMargin uint // see DECSTBM docs - this is for scrollable regions
|
||||
bottomMargin uint // see DECSTBM docs - this is for scrollable regions
|
||||
replaceMode bool // overwrite character at cursor or insert new
|
||||
originMode bool // see DECOM docs - whether cursor is positioned within the margins or not
|
||||
lineFeedMode bool
|
||||
autoWrap bool
|
||||
dirty bool
|
||||
selectionStart *Position
|
||||
selectionEnd *Position
|
||||
selectionComplete bool // whether the selected text can update or whether it is final
|
||||
selectionExpanded bool // whether the selection to word expansion has already run on this point
|
||||
selectionClickTime time.Time
|
||||
defaultCell Cell
|
||||
maxLines uint64
|
||||
terminalState *TerminalState
|
||||
}
|
||||
|
||||
type Position struct {
|
||||
|
@ -42,24 +29,17 @@ type Position struct {
|
|||
}
|
||||
|
||||
// NewBuffer creates a new terminal buffer
|
||||
func NewBuffer(viewCols uint16, viewLines uint16, attr CellAttributes, maxLines uint64) *Buffer {
|
||||
func NewBuffer(terminalState *TerminalState) *Buffer {
|
||||
b := &Buffer{
|
||||
cursorX: 0,
|
||||
cursorY: 0,
|
||||
lines: []Line{},
|
||||
cursorAttr: attr,
|
||||
autoWrap: true,
|
||||
defaultCell: Cell{attr: attr},
|
||||
maxLines: maxLines,
|
||||
lines: []Line{},
|
||||
terminalState: terminalState,
|
||||
}
|
||||
b.SetVerticalMargins(0, uint(viewLines-1))
|
||||
b.ResizeView(viewCols, viewLines)
|
||||
return b
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetURLAtPosition(col uint16, viewRow uint16) string {
|
||||
|
||||
row := buffer.convertViewLineToRawLine((viewRow)) - uint64(buffer.scrollLinesFromBottom)
|
||||
row := buffer.convertViewLineToRawLine((viewRow)) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
||||
|
||||
cell := buffer.GetRawCell(col, row)
|
||||
if cell == nil || cell.Rune() == 0x00 {
|
||||
|
@ -79,7 +59,7 @@ func (buffer *Buffer) GetURLAtPosition(col uint16, viewRow uint16) string {
|
|||
candidate = fmt.Sprintf("%c%s", cell.Rune(), candidate)
|
||||
}
|
||||
|
||||
for i := col + 1; i < buffer.viewWidth; i++ {
|
||||
for i := col + 1; i < buffer.terminalState.viewWidth; i++ {
|
||||
cell := buffer.GetRawCell(i, row)
|
||||
if cell == nil {
|
||||
break
|
||||
|
@ -104,7 +84,7 @@ func (buffer *Buffer) GetURLAtPosition(col uint16, viewRow uint16) string {
|
|||
|
||||
func (buffer *Buffer) SelectWordAtPosition(col uint16, viewRow uint16) {
|
||||
|
||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.scrollLinesFromBottom)
|
||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
||||
|
||||
cell := buffer.GetRawCell(col, row)
|
||||
if cell == nil || cell.Rune() == 0x00 {
|
||||
|
@ -125,7 +105,7 @@ func (buffer *Buffer) SelectWordAtPosition(col uint16, viewRow uint16) {
|
|||
start = i
|
||||
}
|
||||
|
||||
for i := col; i < buffer.viewWidth; i++ {
|
||||
for i := col; i < buffer.terminalState.viewWidth; i++ {
|
||||
cell := buffer.GetRawCell(i, row)
|
||||
if cell == nil {
|
||||
break
|
||||
|
@ -197,7 +177,7 @@ func (buffer *Buffer) GetSelectedText() string {
|
|||
line := buffer.lines[row]
|
||||
|
||||
minX := 0
|
||||
maxX := int(buffer.viewWidth) - 1
|
||||
maxX := int(buffer.terminalState.viewWidth) - 1
|
||||
if row == y1 {
|
||||
minX = x1
|
||||
} else if !line.wrapped {
|
||||
|
@ -221,7 +201,7 @@ func (buffer *Buffer) GetSelectedText() string {
|
|||
}
|
||||
|
||||
func (buffer *Buffer) StartSelection(col uint16, viewRow uint16) {
|
||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.scrollLinesFromBottom)
|
||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
||||
if buffer.selectionComplete {
|
||||
buffer.selectionEnd = nil
|
||||
|
||||
|
@ -270,7 +250,7 @@ func (buffer *Buffer) EndSelection(col uint16, viewRow uint16, complete bool) {
|
|||
return
|
||||
}
|
||||
|
||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.scrollLinesFromBottom)
|
||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
||||
|
||||
if int(col) == buffer.selectionStart.Col && int(row) == int(buffer.selectionStart.Line) && complete {
|
||||
return
|
||||
|
@ -311,7 +291,7 @@ func (buffer *Buffer) InSelection(col uint16, row uint16) bool {
|
|||
x2 = buffer.selectionEnd.Col
|
||||
}
|
||||
|
||||
rawY := int(buffer.convertViewLineToRawLine(row) - uint64(buffer.scrollLinesFromBottom))
|
||||
rawY := int(buffer.convertViewLineToRawLine(row) - uint64(buffer.terminalState.scrollLinesFromBottom))
|
||||
return (rawY > y1 || (rawY == y1 && int(col) >= x1)) && (rawY < y2 || (rawY == y2 && int(col) <= x2))
|
||||
}
|
||||
|
||||
|
@ -323,47 +303,16 @@ func (buffer *Buffer) IsDirty() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetAutoWrap(enabled bool) {
|
||||
buffer.autoWrap = enabled
|
||||
}
|
||||
|
||||
func (buffer *Buffer) IsAutoWrap() bool {
|
||||
return buffer.autoWrap
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetOriginMode(enabled bool) {
|
||||
buffer.originMode = enabled
|
||||
buffer.SetPosition(0, 0)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetInsertMode() {
|
||||
buffer.replaceMode = false
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetReplaceMode() {
|
||||
buffer.replaceMode = true
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetVerticalMargins(top uint, bottom uint) {
|
||||
buffer.topMargin = top
|
||||
buffer.bottomMargin = bottom
|
||||
}
|
||||
|
||||
// ResetVerticalMargins resets margins to extreme positions
|
||||
func (buffer *Buffer) ResetVerticalMargins() {
|
||||
buffer.SetVerticalMargins(0, uint(buffer.viewHeight-1))
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetScrollOffset() uint {
|
||||
return buffer.scrollLinesFromBottom
|
||||
return buffer.terminalState.scrollLinesFromBottom
|
||||
}
|
||||
|
||||
func (buffer *Buffer) HasScrollableRegion() bool {
|
||||
return buffer.topMargin > 0 || buffer.bottomMargin < uint(buffer.ViewHeight())-1
|
||||
return buffer.terminalState.topMargin > 0 || buffer.terminalState.bottomMargin < uint(buffer.ViewHeight())-1
|
||||
}
|
||||
|
||||
func (buffer *Buffer) InScrollableRegion() bool {
|
||||
return buffer.HasScrollableRegion() && uint(buffer.cursorY) >= buffer.topMargin && uint(buffer.cursorY) <= buffer.bottomMargin
|
||||
return buffer.HasScrollableRegion() && uint(buffer.terminalState.cursorY) >= buffer.terminalState.topMargin && uint(buffer.terminalState.cursorY) <= buffer.terminalState.bottomMargin
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ScrollDown(lines uint16) {
|
||||
|
@ -374,10 +323,10 @@ func (buffer *Buffer) ScrollDown(lines uint16) {
|
|||
return
|
||||
}
|
||||
|
||||
if uint(lines) > buffer.scrollLinesFromBottom {
|
||||
lines = uint16(buffer.scrollLinesFromBottom)
|
||||
if uint(lines) > buffer.terminalState.scrollLinesFromBottom {
|
||||
lines = uint16(buffer.terminalState.scrollLinesFromBottom)
|
||||
}
|
||||
buffer.scrollLinesFromBottom -= uint(lines)
|
||||
buffer.terminalState.scrollLinesFromBottom -= uint(lines)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ScrollUp(lines uint16) {
|
||||
|
@ -388,36 +337,36 @@ func (buffer *Buffer) ScrollUp(lines uint16) {
|
|||
return
|
||||
}
|
||||
|
||||
if uint(lines)+buffer.scrollLinesFromBottom >= (uint(buffer.Height()) - uint(buffer.ViewHeight())) {
|
||||
buffer.scrollLinesFromBottom = uint(buffer.Height()) - uint(buffer.ViewHeight())
|
||||
if uint(lines)+buffer.terminalState.scrollLinesFromBottom >= (uint(buffer.Height()) - uint(buffer.ViewHeight())) {
|
||||
buffer.terminalState.scrollLinesFromBottom = uint(buffer.Height()) - uint(buffer.ViewHeight())
|
||||
} else {
|
||||
buffer.scrollLinesFromBottom += uint(lines)
|
||||
buffer.terminalState.scrollLinesFromBottom += uint(lines)
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ScrollPageDown() {
|
||||
buffer.ScrollDown(buffer.viewHeight)
|
||||
buffer.ScrollDown(buffer.terminalState.viewHeight)
|
||||
}
|
||||
func (buffer *Buffer) ScrollPageUp() {
|
||||
buffer.ScrollUp(buffer.viewHeight)
|
||||
buffer.ScrollUp(buffer.terminalState.viewHeight)
|
||||
}
|
||||
func (buffer *Buffer) ScrollToEnd() {
|
||||
defer buffer.emitDisplayChange()
|
||||
buffer.scrollLinesFromBottom = 0
|
||||
buffer.terminalState.scrollLinesFromBottom = 0
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SaveCursor() {
|
||||
buffer.savedX = buffer.cursorX
|
||||
buffer.savedY = buffer.cursorY
|
||||
buffer.savedX = buffer.terminalState.cursorX
|
||||
buffer.savedY = buffer.terminalState.cursorY
|
||||
}
|
||||
|
||||
func (buffer *Buffer) RestoreCursor() {
|
||||
buffer.cursorX = buffer.savedX
|
||||
buffer.cursorY = buffer.savedY
|
||||
buffer.terminalState.cursorX = buffer.savedX
|
||||
buffer.terminalState.cursorY = buffer.savedY
|
||||
}
|
||||
|
||||
func (buffer *Buffer) CursorAttr() *CellAttributes {
|
||||
return &buffer.cursorAttr
|
||||
return &buffer.terminalState.cursorAttr
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetCell(viewCol uint16, viewRow uint16) *Cell {
|
||||
|
@ -444,57 +393,57 @@ func (buffer *Buffer) emitDisplayChange() {
|
|||
// Column returns cursor column
|
||||
func (buffer *Buffer) CursorColumn() uint16 {
|
||||
// @todo originMode and left margin
|
||||
return buffer.cursorX
|
||||
return buffer.terminalState.cursorX
|
||||
}
|
||||
|
||||
// Line returns cursor line
|
||||
func (buffer *Buffer) CursorLine() uint16 {
|
||||
if buffer.originMode {
|
||||
result := buffer.cursorY - uint16(buffer.topMargin)
|
||||
if buffer.terminalState.OriginMode {
|
||||
result := buffer.terminalState.cursorY - uint16(buffer.terminalState.topMargin)
|
||||
if result < 0 {
|
||||
result = 0
|
||||
}
|
||||
return result
|
||||
}
|
||||
return buffer.cursorY
|
||||
return buffer.terminalState.cursorY
|
||||
}
|
||||
|
||||
func (buffer *Buffer) TopMargin() uint {
|
||||
return buffer.topMargin
|
||||
return buffer.terminalState.topMargin
|
||||
}
|
||||
|
||||
func (buffer *Buffer) BottomMargin() uint {
|
||||
return buffer.bottomMargin
|
||||
return buffer.terminalState.bottomMargin
|
||||
}
|
||||
|
||||
// translates the cursor line to the raw buffer line
|
||||
func (buffer *Buffer) RawLine() uint64 {
|
||||
return buffer.convertViewLineToRawLine(buffer.cursorY)
|
||||
return buffer.convertViewLineToRawLine(buffer.terminalState.cursorY)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) convertViewLineToRawLine(viewLine uint16) uint64 {
|
||||
rawHeight := buffer.Height()
|
||||
if int(buffer.viewHeight) > rawHeight {
|
||||
if int(buffer.terminalState.viewHeight) > rawHeight {
|
||||
return uint64(viewLine)
|
||||
}
|
||||
return uint64(int(viewLine) + (rawHeight - int(buffer.viewHeight)))
|
||||
return uint64(int(viewLine) + (rawHeight - int(buffer.terminalState.viewHeight)))
|
||||
}
|
||||
|
||||
func (buffer *Buffer) convertRawLineToViewLine(rawLine uint64) uint16 {
|
||||
rawHeight := buffer.Height()
|
||||
if int(buffer.viewHeight) > rawHeight {
|
||||
if int(buffer.terminalState.viewHeight) > rawHeight {
|
||||
return uint16(rawLine)
|
||||
}
|
||||
return uint16(int(rawLine) - (rawHeight - int(buffer.viewHeight)))
|
||||
return uint16(int(rawLine) - (rawHeight - int(buffer.terminalState.viewHeight)))
|
||||
}
|
||||
|
||||
// Width returns the width of the buffer in columns
|
||||
func (buffer *Buffer) Width() uint16 {
|
||||
return buffer.viewWidth
|
||||
return buffer.terminalState.viewWidth
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ViewWidth() uint16 {
|
||||
return buffer.viewWidth
|
||||
return buffer.terminalState.viewWidth
|
||||
}
|
||||
|
||||
func (buffer *Buffer) Height() int {
|
||||
|
@ -502,7 +451,7 @@ func (buffer *Buffer) Height() int {
|
|||
}
|
||||
|
||||
func (buffer *Buffer) ViewHeight() uint16 {
|
||||
return buffer.viewHeight
|
||||
return buffer.terminalState.viewHeight
|
||||
}
|
||||
|
||||
func (buffer *Buffer) deleteLine() {
|
||||
|
@ -530,8 +479,8 @@ func (buffer *Buffer) insertLine() {
|
|||
copy(out[pos+1:], buffer.lines[pos:])
|
||||
buffer.lines = out
|
||||
} else {
|
||||
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin))
|
||||
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin))
|
||||
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.terminalState.topMargin))
|
||||
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.terminalState.bottomMargin))
|
||||
before := buffer.lines[:topIndex]
|
||||
after := buffer.lines[bottomIndex+1:]
|
||||
out := make([]Line, len(buffer.lines))
|
||||
|
@ -558,7 +507,7 @@ func (buffer *Buffer) InsertBlankCharacters(count int) {
|
|||
index := int(buffer.RawLine())
|
||||
for i := 0; i < count; i++ {
|
||||
cells := buffer.lines[index].cells
|
||||
buffer.lines[index].cells = append(cells[:buffer.cursorX], append([]Cell{buffer.defaultCell}, cells[buffer.cursorX:]...)...)
|
||||
buffer.lines[index].cells = append(cells[:buffer.terminalState.cursorX], append([]Cell{buffer.terminalState.defaultCell}, cells[buffer.terminalState.cursorX:]...)...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -569,7 +518,7 @@ func (buffer *Buffer) InsertLines(count int) {
|
|||
return
|
||||
}
|
||||
|
||||
buffer.cursorX = 0
|
||||
buffer.terminalState.cursorX = 0
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
buffer.insertLine()
|
||||
|
@ -584,7 +533,7 @@ func (buffer *Buffer) DeleteLines(count int) {
|
|||
return
|
||||
}
|
||||
|
||||
buffer.cursorX = 0
|
||||
buffer.terminalState.cursorX = 0
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
buffer.deleteLine()
|
||||
|
@ -601,12 +550,12 @@ func (buffer *Buffer) Index() {
|
|||
|
||||
if buffer.InScrollableRegion() {
|
||||
|
||||
if uint(buffer.cursorY) < buffer.bottomMargin {
|
||||
buffer.cursorY++
|
||||
if uint(buffer.terminalState.cursorY) < buffer.terminalState.bottomMargin {
|
||||
buffer.terminalState.cursorY++
|
||||
} else {
|
||||
|
||||
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin))
|
||||
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin))
|
||||
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.terminalState.topMargin))
|
||||
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.terminalState.bottomMargin))
|
||||
|
||||
for i := topIndex; i < bottomIndex; i++ {
|
||||
buffer.lines[i] = buffer.lines[i+1]
|
||||
|
@ -618,15 +567,15 @@ func (buffer *Buffer) Index() {
|
|||
return
|
||||
}
|
||||
|
||||
if buffer.cursorY >= buffer.ViewHeight()-1 {
|
||||
if buffer.terminalState.cursorY >= buffer.ViewHeight()-1 {
|
||||
buffer.lines = append(buffer.lines, newLine())
|
||||
maxLines := buffer.getMaxLines()
|
||||
if uint64(len(buffer.lines)) > maxLines {
|
||||
copy(buffer.lines, buffer.lines[ uint64(len(buffer.lines)) - maxLines:])
|
||||
copy(buffer.lines, buffer.lines[uint64(len(buffer.lines))-maxLines:])
|
||||
buffer.lines = buffer.lines[:maxLines]
|
||||
}
|
||||
} else {
|
||||
buffer.cursorY++
|
||||
buffer.terminalState.cursorY++
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -636,12 +585,12 @@ func (buffer *Buffer) ReverseIndex() {
|
|||
|
||||
if buffer.InScrollableRegion() {
|
||||
|
||||
if uint(buffer.cursorY) > buffer.topMargin {
|
||||
buffer.cursorY--
|
||||
if uint(buffer.terminalState.cursorY) > buffer.terminalState.topMargin {
|
||||
buffer.terminalState.cursorY--
|
||||
} else {
|
||||
|
||||
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin))
|
||||
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin))
|
||||
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.terminalState.topMargin))
|
||||
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.terminalState.bottomMargin))
|
||||
|
||||
for i := bottomIndex; i > topIndex; i-- {
|
||||
buffer.lines[i] = buffer.lines[i-1]
|
||||
|
@ -652,8 +601,8 @@ func (buffer *Buffer) ReverseIndex() {
|
|||
return
|
||||
}
|
||||
|
||||
if buffer.cursorY > 0 {
|
||||
buffer.cursorY--
|
||||
if buffer.terminalState.cursorY > 0 {
|
||||
buffer.terminalState.cursorY--
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -661,13 +610,13 @@ func (buffer *Buffer) ReverseIndex() {
|
|||
func (buffer *Buffer) Write(runes ...rune) {
|
||||
|
||||
// scroll to bottom on input
|
||||
buffer.scrollLinesFromBottom = 0
|
||||
buffer.terminalState.scrollLinesFromBottom = 0
|
||||
|
||||
for _, r := range runes {
|
||||
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
if buffer.replaceMode {
|
||||
if buffer.terminalState.ReplaceMode {
|
||||
|
||||
if buffer.CursorColumn() >= buffer.Width() {
|
||||
// @todo replace rune at position 0 on next line down
|
||||
|
@ -675,27 +624,27 @@ func (buffer *Buffer) Write(runes ...rune) {
|
|||
}
|
||||
|
||||
for int(buffer.CursorColumn()) >= len(line.cells) {
|
||||
line.cells = append(line.cells, buffer.defaultCell)
|
||||
line.cells = append(line.cells, buffer.terminalState.defaultCell)
|
||||
}
|
||||
line.cells[buffer.cursorX].attr = buffer.cursorAttr
|
||||
line.cells[buffer.cursorX].setRune(r)
|
||||
line.cells[buffer.terminalState.cursorX].attr = buffer.terminalState.cursorAttr
|
||||
line.cells[buffer.terminalState.cursorX].setRune(r)
|
||||
buffer.incrementCursorPosition()
|
||||
continue
|
||||
}
|
||||
|
||||
if buffer.CursorColumn() >= buffer.Width() { // if we're after the line, move to next
|
||||
|
||||
if buffer.autoWrap {
|
||||
if buffer.terminalState.AutoWrap {
|
||||
|
||||
buffer.NewLineEx(true)
|
||||
|
||||
newLine := buffer.getCurrentLine()
|
||||
if len(newLine.cells) == 0 {
|
||||
newLine.cells = append(newLine.cells, buffer.defaultCell)
|
||||
newLine.cells = append(newLine.cells, buffer.terminalState.defaultCell)
|
||||
}
|
||||
cell := &newLine.cells[0]
|
||||
cell.setRune(r)
|
||||
cell.attr = buffer.cursorAttr
|
||||
cell.attr = buffer.terminalState.cursorAttr
|
||||
|
||||
} else {
|
||||
// no more room on line and wrapping is disabled
|
||||
|
@ -706,12 +655,12 @@ func (buffer *Buffer) Write(runes ...rune) {
|
|||
} else {
|
||||
|
||||
for int(buffer.CursorColumn()) >= len(line.cells) {
|
||||
line.cells = append(line.cells, buffer.defaultCell)
|
||||
line.cells = append(line.cells, buffer.terminalState.defaultCell)
|
||||
}
|
||||
|
||||
cell := &line.cells[buffer.CursorColumn()]
|
||||
cell.setRune(r)
|
||||
cell.attr = buffer.cursorAttr
|
||||
cell.attr = buffer.terminalState.cursorAttr
|
||||
}
|
||||
|
||||
buffer.incrementCursorPosition()
|
||||
|
@ -722,7 +671,7 @@ func (buffer *Buffer) incrementCursorPosition() {
|
|||
// we can increment one column past the end of the line.
|
||||
// this is effectively the beginning of the next line, except when we \r etc.
|
||||
if buffer.CursorColumn() < buffer.Width() {
|
||||
buffer.cursorX++
|
||||
buffer.terminalState.cursorX++
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -730,12 +679,12 @@ func (buffer *Buffer) inDoWrap() bool {
|
|||
// xterm uses 'do_wrap' flag for this special terminal state
|
||||
// we use the cursor position right after the boundary
|
||||
// let's see how it works out
|
||||
return buffer.cursorX == buffer.viewWidth // @todo rightMargin
|
||||
return buffer.terminalState.cursorX == buffer.terminalState.viewWidth // @todo rightMargin
|
||||
}
|
||||
|
||||
func (buffer *Buffer) Backspace() {
|
||||
|
||||
if buffer.cursorX == 0 {
|
||||
if buffer.terminalState.cursorX == 0 {
|
||||
line := buffer.getCurrentLine()
|
||||
if line.wrapped {
|
||||
buffer.MovePosition(int16(buffer.Width()-1), -1)
|
||||
|
@ -757,14 +706,14 @@ func (buffer *Buffer) CarriageReturn() {
|
|||
if line == nil {
|
||||
break
|
||||
}
|
||||
if line.wrapped && buffer.cursorY > 0 {
|
||||
buffer.cursorY--
|
||||
if line.wrapped && buffer.terminalState.cursorY > 0 {
|
||||
buffer.terminalState.cursorY--
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
buffer.cursorX = 0
|
||||
buffer.terminalState.cursorX = 0
|
||||
}
|
||||
|
||||
func (buffer *Buffer) Tab() {
|
||||
|
@ -772,11 +721,11 @@ func (buffer *Buffer) Tab() {
|
|||
max := tabSize
|
||||
|
||||
// @todo rightMargin
|
||||
if buffer.cursorX < buffer.viewWidth {
|
||||
max = int(buffer.viewWidth - buffer.cursorX - 1)
|
||||
if buffer.terminalState.cursorX < buffer.terminalState.viewWidth {
|
||||
max = int(buffer.terminalState.viewWidth - buffer.terminalState.cursorX - 1)
|
||||
}
|
||||
|
||||
shift := tabSize - (int(buffer.cursorX+1) % tabSize)
|
||||
shift := tabSize - (int(buffer.terminalState.cursorX+1) % tabSize)
|
||||
|
||||
if shift > max {
|
||||
shift = max
|
||||
|
@ -793,8 +742,8 @@ func (buffer *Buffer) NewLine() {
|
|||
|
||||
func (buffer *Buffer) NewLineEx(forceCursorToMargin bool) {
|
||||
|
||||
if buffer.IsNewLineMode() || forceCursorToMargin {
|
||||
buffer.cursorX = 0
|
||||
if buffer.terminalState.IsNewLineMode() || forceCursorToMargin {
|
||||
buffer.terminalState.cursorX = 0
|
||||
}
|
||||
buffer.Index()
|
||||
|
||||
|
@ -807,16 +756,8 @@ func (buffer *Buffer) NewLineEx(forceCursorToMargin bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetNewLineMode() {
|
||||
buffer.lineFeedMode = false
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetLineFeedMode() {
|
||||
buffer.lineFeedMode = true
|
||||
}
|
||||
|
||||
func (buffer *Buffer) IsNewLineMode() bool {
|
||||
return buffer.lineFeedMode == false
|
||||
return buffer.terminalState.LineFeedMode == false
|
||||
}
|
||||
|
||||
func (buffer *Buffer) MovePosition(x int16, y int16) {
|
||||
|
@ -847,9 +788,9 @@ func (buffer *Buffer) SetPosition(col uint16, line uint16) {
|
|||
useLine := line
|
||||
maxLine := buffer.ViewHeight() - 1
|
||||
|
||||
if buffer.originMode {
|
||||
useLine += uint16(buffer.topMargin)
|
||||
maxLine = uint16(buffer.bottomMargin)
|
||||
if buffer.terminalState.OriginMode {
|
||||
useLine += uint16(buffer.terminalState.topMargin)
|
||||
maxLine = uint16(buffer.terminalState.bottomMargin)
|
||||
// @todo left and right margins
|
||||
}
|
||||
if useLine > maxLine {
|
||||
|
@ -861,15 +802,15 @@ func (buffer *Buffer) SetPosition(col uint16, line uint16) {
|
|||
//logrus.Errorf("Cannot set cursor position: column %d is outside of the current view width (%d columns)", col, buffer.ViewWidth())
|
||||
}
|
||||
|
||||
buffer.cursorX = useCol
|
||||
buffer.cursorY = useLine
|
||||
buffer.terminalState.cursorX = useCol
|
||||
buffer.terminalState.cursorY = useLine
|
||||
}
|
||||
|
||||
func (buffer *Buffer) GetVisibleLines() []Line {
|
||||
lines := []Line{}
|
||||
|
||||
for i := buffer.Height() - int(buffer.ViewHeight()); i < buffer.Height(); i++ {
|
||||
y := i - int(buffer.scrollLinesFromBottom)
|
||||
y := i - int(buffer.terminalState.scrollLinesFromBottom)
|
||||
if y >= 0 && y < len(buffer.lines) {
|
||||
lines = append(lines, buffer.lines[y])
|
||||
}
|
||||
|
@ -889,7 +830,7 @@ func (buffer *Buffer) Clear() {
|
|||
|
||||
// creates if necessary
|
||||
func (buffer *Buffer) getCurrentLine() *Line {
|
||||
return buffer.getViewLine(buffer.cursorY)
|
||||
return buffer.getViewLine(buffer.terminalState.cursorY)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) getViewLine(index uint16) *Line {
|
||||
|
@ -921,9 +862,9 @@ func (buffer *Buffer) EraseLine() {
|
|||
func (buffer *Buffer) EraseLineToCursor() {
|
||||
defer buffer.emitDisplayChange()
|
||||
line := buffer.getCurrentLine()
|
||||
for i := 0; i <= int(buffer.cursorX); i++ {
|
||||
for i := 0; i <= int(buffer.terminalState.cursorX); i++ {
|
||||
if i < len(line.cells) {
|
||||
line.cells[i].erase(buffer.defaultCell.attr.BgColour)
|
||||
line.cells[i].erase(buffer.terminalState.defaultCell.attr.BgColour)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -933,9 +874,9 @@ func (buffer *Buffer) EraseLineFromCursor() {
|
|||
line := buffer.getCurrentLine()
|
||||
|
||||
if len(line.cells) > 0 {
|
||||
cx := buffer.cursorX
|
||||
cx := buffer.terminalState.cursorX
|
||||
if int(cx) < len(line.cells) {
|
||||
line.cells = line.cells[:buffer.cursorX]
|
||||
line.cells = line.cells[:buffer.terminalState.cursorX]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -962,14 +903,14 @@ func (buffer *Buffer) DeleteChars(n int) {
|
|||
defer buffer.emitDisplayChange()
|
||||
|
||||
line := buffer.getCurrentLine()
|
||||
if int(buffer.cursorX) >= len(line.cells) {
|
||||
if int(buffer.terminalState.cursorX) >= len(line.cells) {
|
||||
return
|
||||
}
|
||||
before := line.cells[:buffer.cursorX]
|
||||
if int(buffer.cursorX)+n >= len(line.cells) {
|
||||
n = len(line.cells) - int(buffer.cursorX)
|
||||
before := line.cells[:buffer.terminalState.cursorX]
|
||||
if int(buffer.terminalState.cursorX)+n >= len(line.cells) {
|
||||
n = len(line.cells) - int(buffer.terminalState.cursorX)
|
||||
}
|
||||
after := line.cells[int(buffer.cursorX)+n:]
|
||||
after := line.cells[int(buffer.terminalState.cursorX)+n:]
|
||||
line.cells = append(before, after...)
|
||||
}
|
||||
|
||||
|
@ -978,13 +919,13 @@ func (buffer *Buffer) EraseCharacters(n int) {
|
|||
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
max := int(buffer.cursorX) + n
|
||||
max := int(buffer.terminalState.cursorX) + n
|
||||
if max > len(line.cells) {
|
||||
max = len(line.cells)
|
||||
}
|
||||
|
||||
for i := int(buffer.cursorX); i < max; i++ {
|
||||
line.cells[i].erase(buffer.defaultCell.attr.BgColour)
|
||||
for i := int(buffer.terminalState.cursorX); i < max; i++ {
|
||||
line.cells[i].erase(buffer.terminalState.defaultCell.attr.BgColour)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -992,13 +933,13 @@ func (buffer *Buffer) EraseDisplayFromCursor() {
|
|||
defer buffer.emitDisplayChange()
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
max := int(buffer.cursorX)
|
||||
max := int(buffer.terminalState.cursorX)
|
||||
if max > len(line.cells) {
|
||||
max = len(line.cells)
|
||||
}
|
||||
|
||||
line.cells = line.cells[:max]
|
||||
for i := buffer.cursorY + 1; i < buffer.ViewHeight(); i++ {
|
||||
for i := buffer.terminalState.cursorY + 1; i < buffer.ViewHeight(); i++ {
|
||||
rawLine := buffer.convertViewLineToRawLine(i)
|
||||
if int(rawLine) < len(buffer.lines) {
|
||||
buffer.lines[int(rawLine)].cells = []Cell{}
|
||||
|
@ -1010,13 +951,13 @@ func (buffer *Buffer) EraseDisplayToCursor() {
|
|||
defer buffer.emitDisplayChange()
|
||||
line := buffer.getCurrentLine()
|
||||
|
||||
for i := 0; i <= int(buffer.cursorX); i++ {
|
||||
for i := 0; i <= int(buffer.terminalState.cursorX); i++ {
|
||||
if i >= len(line.cells) {
|
||||
break
|
||||
}
|
||||
line.cells[i].erase(buffer.defaultCell.attr.BgColour)
|
||||
line.cells[i].erase(buffer.terminalState.defaultCell.attr.BgColour)
|
||||
}
|
||||
for i := uint16(0); i < buffer.cursorY; i++ {
|
||||
for i := uint16(0); i < buffer.terminalState.cursorY; i++ {
|
||||
rawLine := buffer.convertViewLineToRawLine(i)
|
||||
if int(rawLine) < len(buffer.lines) {
|
||||
buffer.lines[int(rawLine)].cells = []Cell{}
|
||||
|
@ -1028,19 +969,19 @@ func (buffer *Buffer) ResizeView(width uint16, height uint16) {
|
|||
|
||||
defer buffer.emitDisplayChange()
|
||||
|
||||
if buffer.viewHeight == 0 {
|
||||
buffer.viewWidth = width
|
||||
buffer.viewHeight = height
|
||||
if buffer.terminalState.viewHeight == 0 {
|
||||
buffer.terminalState.viewWidth = width
|
||||
buffer.terminalState.viewHeight = height
|
||||
return
|
||||
}
|
||||
|
||||
// @todo scroll to bottom on resize
|
||||
line := buffer.getCurrentLine()
|
||||
cXFromEndOfLine := len(line.cells) - int(buffer.cursorX+1)
|
||||
cXFromEndOfLine := len(line.cells) - int(buffer.terminalState.cursorX+1)
|
||||
|
||||
cursorYMovement := 0
|
||||
|
||||
if width < buffer.viewWidth { // wrap lines if we're shrinking
|
||||
if width < buffer.terminalState.viewWidth { // wrap lines if we're shrinking
|
||||
for i := 0; i < len(buffer.lines); i++ {
|
||||
line := &buffer.lines[i]
|
||||
//line.Cleanse()
|
||||
|
@ -1060,7 +1001,7 @@ func (buffer *Buffer) ResizeView(width uint16, height uint16) {
|
|||
}
|
||||
}
|
||||
|
||||
if i+1 <= int(buffer.cursorY) {
|
||||
if i+1 <= int(buffer.terminalState.cursorY) {
|
||||
cursorYMovement++
|
||||
}
|
||||
|
||||
|
@ -1072,7 +1013,7 @@ func (buffer *Buffer) ResizeView(width uint16, height uint16) {
|
|||
|
||||
}
|
||||
}
|
||||
} else if width > buffer.viewWidth { // unwrap lines if we're growing
|
||||
} else if width > buffer.terminalState.viewWidth { // unwrap lines if we're growing
|
||||
for i := 0; i < len(buffer.lines)-1; i++ {
|
||||
line := &buffer.lines[i]
|
||||
//line.Cleanse()
|
||||
|
@ -1093,7 +1034,7 @@ func (buffer *Buffer) ResizeView(width uint16, height uint16) {
|
|||
line.cells = append(line.cells, nextLine.cells[:moveCount]...)
|
||||
if moveCount == len(nextLine.cells) {
|
||||
|
||||
if i+offset <= int(buffer.cursorY) {
|
||||
if i+offset <= int(buffer.terminalState.cursorY) {
|
||||
cursorYMovement--
|
||||
}
|
||||
|
||||
|
@ -1111,26 +1052,26 @@ func (buffer *Buffer) ResizeView(width uint16, height uint16) {
|
|||
}
|
||||
}
|
||||
|
||||
buffer.viewWidth = width
|
||||
buffer.viewHeight = height
|
||||
buffer.terminalState.viewWidth = width
|
||||
buffer.terminalState.viewHeight = height
|
||||
|
||||
cY := uint16(len(buffer.lines) - 1)
|
||||
if cY >= buffer.viewHeight {
|
||||
cY = buffer.viewHeight - 1
|
||||
if cY >= buffer.terminalState.viewHeight {
|
||||
cY = buffer.terminalState.viewHeight - 1
|
||||
}
|
||||
buffer.cursorY = cY
|
||||
buffer.terminalState.cursorY = cY
|
||||
|
||||
// position cursorX
|
||||
line = buffer.getCurrentLine()
|
||||
buffer.cursorX = uint16((len(line.cells) - cXFromEndOfLine) - 1)
|
||||
buffer.terminalState.cursorX = uint16((len(line.cells) - cXFromEndOfLine) - 1)
|
||||
|
||||
buffer.ResetVerticalMargins()
|
||||
buffer.terminalState.ResetVerticalMargins()
|
||||
}
|
||||
|
||||
func (buffer *Buffer) getMaxLines() uint64 {
|
||||
result := buffer.maxLines
|
||||
if result < uint64(buffer.viewHeight) {
|
||||
result = uint64(buffer.viewHeight)
|
||||
result := buffer.terminalState.maxLines
|
||||
if result < uint64(buffer.terminalState.viewHeight) {
|
||||
result = uint64(buffer.terminalState.viewHeight)
|
||||
}
|
||||
|
||||
return result
|
||||
|
@ -1161,4 +1102,3 @@ func (buffer *Buffer) Compare(path string) bool {
|
|||
}
|
||||
return bytes.Equal(f, bufferContent)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func TestTabbing(t *testing.T) {
|
||||
b := NewBuffer(30, 3, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(30, 3, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello")...)
|
||||
b.Tab()
|
||||
b.Write([]rune("x")...)
|
||||
|
@ -39,7 +39,7 @@ hell xxx good
|
|||
}
|
||||
|
||||
func TestOffsets(t *testing.T) {
|
||||
b := NewBuffer(10, 3, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(10, 3, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -60,7 +60,7 @@ func TestOffsets(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBufferCreation(t *testing.T) {
|
||||
b := NewBuffer(10, 20, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(10, 20, CellAttributes{}, 1000))
|
||||
assert.Equal(t, uint16(10), b.Width())
|
||||
assert.Equal(t, uint16(20), b.ViewHeight())
|
||||
assert.Equal(t, uint16(0), b.CursorColumn())
|
||||
|
@ -70,7 +70,7 @@ func TestBufferCreation(t *testing.T) {
|
|||
|
||||
func TestBufferWriteIncrementsCursorCorrectly(t *testing.T) {
|
||||
|
||||
b := NewBuffer(5, 4, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(5, 4, CellAttributes{}, 1000))
|
||||
|
||||
/*01234
|
||||
|-----
|
||||
|
@ -117,21 +117,21 @@ func TestBufferWriteIncrementsCursorCorrectly(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWritingNewLineAsFirstRuneOnWrappedLine(t *testing.T) {
|
||||
b := NewBuffer(3, 20, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(3, 20, CellAttributes{}, 1000))
|
||||
b.Write('a', 'b', 'c')
|
||||
assert.Equal(t, uint16(3), b.cursorX)
|
||||
assert.Equal(t, uint16(0), b.cursorY)
|
||||
assert.Equal(t, uint16(3), b.terminalState.cursorX)
|
||||
assert.Equal(t, uint16(0), b.terminalState.cursorY)
|
||||
b.NewLine()
|
||||
assert.Equal(t, uint16(0), b.cursorX)
|
||||
assert.Equal(t, uint16(1), b.cursorY)
|
||||
assert.Equal(t, uint16(0), b.terminalState.cursorX)
|
||||
assert.Equal(t, uint16(1), b.terminalState.cursorY)
|
||||
|
||||
b.Write('d', 'e', 'f')
|
||||
assert.Equal(t, uint16(3), b.cursorX)
|
||||
assert.Equal(t, uint16(1), b.cursorY)
|
||||
assert.Equal(t, uint16(3), b.terminalState.cursorX)
|
||||
assert.Equal(t, uint16(1), b.terminalState.cursorY)
|
||||
b.NewLine()
|
||||
|
||||
assert.Equal(t, uint16(0), b.cursorX)
|
||||
assert.Equal(t, uint16(2), b.cursorY)
|
||||
assert.Equal(t, uint16(0), b.terminalState.cursorX)
|
||||
assert.Equal(t, uint16(2), b.terminalState.cursorY)
|
||||
|
||||
require.Equal(t, 3, len(b.lines))
|
||||
assert.Equal(t, "abc", b.lines[0].String())
|
||||
|
@ -140,7 +140,7 @@ func TestWritingNewLineAsFirstRuneOnWrappedLine(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) {
|
||||
b := NewBuffer(3, 20, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(3, 20, CellAttributes{}, 1000))
|
||||
/*
|
||||
|abc
|
||||
|d
|
||||
|
@ -168,7 +168,7 @@ func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) {
|
|||
|
||||
func TestSetPosition(t *testing.T) {
|
||||
|
||||
b := NewBuffer(120, 80, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(120, 80, CellAttributes{}, 1000))
|
||||
assert.Equal(t, 0, int(b.CursorColumn()))
|
||||
assert.Equal(t, 0, int(b.CursorLine()))
|
||||
b.SetPosition(60, 10)
|
||||
|
@ -184,7 +184,7 @@ func TestSetPosition(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMovePosition(t *testing.T) {
|
||||
b := NewBuffer(120, 80, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(120, 80, CellAttributes{}, 1000))
|
||||
assert.Equal(t, 0, int(b.CursorColumn()))
|
||||
assert.Equal(t, 0, int(b.CursorLine()))
|
||||
b.MovePosition(-1, -1)
|
||||
|
@ -206,7 +206,7 @@ func TestMovePosition(t *testing.T) {
|
|||
|
||||
func TestVisibleLines(t *testing.T) {
|
||||
|
||||
b := NewBuffer(80, 10, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 10, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello 1")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -256,7 +256,7 @@ func TestVisibleLines(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClearWithoutFullView(t *testing.T) {
|
||||
b := NewBuffer(80, 10, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 10, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello 1")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -272,7 +272,7 @@ func TestClearWithoutFullView(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClearWithFullView(t *testing.T) {
|
||||
b := NewBuffer(80, 5, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello 1")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -303,7 +303,7 @@ func TestClearWithFullView(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCarriageReturn(t *testing.T) {
|
||||
b := NewBuffer(80, 20, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 20, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello!")...)
|
||||
b.CarriageReturn()
|
||||
b.Write([]rune("secret")...)
|
||||
|
@ -312,7 +312,7 @@ func TestCarriageReturn(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCarriageReturnOnFullLine(t *testing.T) {
|
||||
b := NewBuffer(20, 20, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(20, 20, CellAttributes{}, 1000))
|
||||
b.Write([]rune("abcdeabcdeabcdeabcde")...)
|
||||
b.CarriageReturn()
|
||||
b.Write([]rune("xxxxxxxxxxxxxxxxxxxx")...)
|
||||
|
@ -321,7 +321,7 @@ func TestCarriageReturnOnFullLine(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCarriageReturnOnFullLastLine(t *testing.T) {
|
||||
b := NewBuffer(20, 2, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(20, 2, CellAttributes{}, 1000))
|
||||
b.NewLine()
|
||||
b.Write([]rune("abcdeabcdeabcdeabcde")...)
|
||||
b.CarriageReturn()
|
||||
|
@ -332,7 +332,7 @@ func TestCarriageReturnOnFullLastLine(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCarriageReturnOnWrappedLine(t *testing.T) {
|
||||
b := NewBuffer(80, 6, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 6, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello!")...)
|
||||
b.CarriageReturn()
|
||||
b.Write([]rune("secret")...)
|
||||
|
@ -342,15 +342,15 @@ func TestCarriageReturnOnWrappedLine(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCarriageReturnOnLineThatDoesntExist(t *testing.T) {
|
||||
b := NewBuffer(6, 10, CellAttributes{}, 1000)
|
||||
b.cursorY = 3
|
||||
b := NewBuffer(NewTerminalState(6, 10, CellAttributes{}, 1000))
|
||||
b.terminalState.cursorY = 3
|
||||
b.CarriageReturn()
|
||||
assert.Equal(t, uint16(0), b.cursorX)
|
||||
assert.Equal(t, uint16(3), b.cursorY)
|
||||
assert.Equal(t, uint16(0), b.terminalState.cursorX)
|
||||
assert.Equal(t, uint16(3), b.terminalState.cursorY)
|
||||
}
|
||||
|
||||
func TestGetCell(t *testing.T) {
|
||||
b := NewBuffer(80, 20, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 20, CellAttributes{}, 1000))
|
||||
b.Write([]rune("Hello")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -366,7 +366,7 @@ func TestGetCell(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetCellWithHistory(t *testing.T) {
|
||||
b := NewBuffer(80, 2, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 2, CellAttributes{}, 1000))
|
||||
|
||||
b.Write([]rune("Hello")...)
|
||||
b.CarriageReturn()
|
||||
|
@ -384,7 +384,7 @@ func TestGetCellWithHistory(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetCellWithBadCursor(t *testing.T) {
|
||||
b := NewBuffer(80, 2, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 2, CellAttributes{}, 1000))
|
||||
b.Write([]rune("Hello\r\nthere\r\nsomething...")...)
|
||||
require.Nil(t, b.GetCell(8, 3))
|
||||
require.Nil(t, b.GetCell(90, 0))
|
||||
|
@ -392,20 +392,20 @@ func TestGetCellWithBadCursor(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCursorAttr(t *testing.T) {
|
||||
b := NewBuffer(80, 2, CellAttributes{}, 1000)
|
||||
assert.Equal(t, &b.cursorAttr, b.CursorAttr())
|
||||
b := NewBuffer(NewTerminalState(80, 2, CellAttributes{}, 1000))
|
||||
assert.Equal(t, &b.terminalState.cursorAttr, b.CursorAttr())
|
||||
}
|
||||
|
||||
func TestCursorPositionQuerying(t *testing.T) {
|
||||
b := NewBuffer(80, 20, CellAttributes{}, 1000)
|
||||
b.cursorX = 17
|
||||
b.cursorY = 9
|
||||
assert.Equal(t, b.cursorX, b.CursorColumn())
|
||||
assert.Equal(t, b.cursorY, b.CursorLine())
|
||||
b := NewBuffer(NewTerminalState(80, 20, CellAttributes{}, 1000))
|
||||
b.terminalState.cursorX = 17
|
||||
b.terminalState.cursorY = 9
|
||||
assert.Equal(t, b.terminalState.cursorX, b.CursorColumn())
|
||||
assert.Equal(t, b.terminalState.cursorY, b.CursorLine())
|
||||
}
|
||||
|
||||
func TestRawPositionQuerying(t *testing.T) {
|
||||
b := NewBuffer(80, 5, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
|
||||
b.Write([]rune("a")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -435,14 +435,14 @@ func TestRawPositionQuerying(t *testing.T) {
|
|||
b.NewLine()
|
||||
b.Write([]rune("a")...)
|
||||
|
||||
b.cursorX = 3
|
||||
b.cursorY = 4
|
||||
b.terminalState.cursorX = 3
|
||||
b.terminalState.cursorY = 4
|
||||
assert.Equal(t, uint64(9), b.RawLine())
|
||||
}
|
||||
|
||||
// CSI 2 K
|
||||
func TestEraseLine(t *testing.T) {
|
||||
b := NewBuffer(80, 5, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello, this is a test")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -454,7 +454,7 @@ func TestEraseLine(t *testing.T) {
|
|||
|
||||
// CSI 1 K
|
||||
func TestEraseLineToCursor(t *testing.T) {
|
||||
b := NewBuffer(80, 5, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello, this is a test")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -468,7 +468,7 @@ func TestEraseLineToCursor(t *testing.T) {
|
|||
|
||||
// CSI 0 K
|
||||
func TestEraseLineAfterCursor(t *testing.T) {
|
||||
b := NewBuffer(80, 5, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello, this is a test")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -479,7 +479,7 @@ func TestEraseLineAfterCursor(t *testing.T) {
|
|||
assert.Equal(t, "dele", b.lines[1].String())
|
||||
}
|
||||
func TestEraseDisplay(t *testing.T) {
|
||||
b := NewBuffer(80, 5, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -495,7 +495,7 @@ func TestEraseDisplay(t *testing.T) {
|
|||
}
|
||||
}
|
||||
func TestEraseDisplayToCursor(t *testing.T) {
|
||||
b := NewBuffer(80, 5, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -513,7 +513,7 @@ func TestEraseDisplayToCursor(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEraseDisplayFromCursor(t *testing.T) {
|
||||
b := NewBuffer(80, 5, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello")...)
|
||||
b.CarriageReturn()
|
||||
b.NewLine()
|
||||
|
@ -529,7 +529,7 @@ func TestEraseDisplayFromCursor(t *testing.T) {
|
|||
assert.Equal(t, "", lines[2].String())
|
||||
}
|
||||
func TestBackspace(t *testing.T) {
|
||||
b := NewBuffer(80, 5, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
|
||||
b.Write([]rune("hello")...)
|
||||
b.Backspace()
|
||||
b.Backspace()
|
||||
|
@ -539,7 +539,7 @@ func TestBackspace(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHorizontalResizeView(t *testing.T) {
|
||||
b := NewBuffer(80, 10, CellAttributes{}, 1000)
|
||||
b := NewBuffer(NewTerminalState(80, 10, CellAttributes{}, 1000))
|
||||
|
||||
// 60 characters
|
||||
b.Write([]rune(`hellohellohellohellohellohellohellohellohellohellohellohello`)...)
|
||||
|
@ -549,8 +549,8 @@ func TestHorizontalResizeView(t *testing.T) {
|
|||
|
||||
b.Write([]rune(`goodbyegoodbye`)...)
|
||||
|
||||
require.Equal(t, uint16(14), b.cursorX)
|
||||
require.Equal(t, uint16(1), b.cursorY)
|
||||
require.Equal(t, uint16(14), b.terminalState.cursorX)
|
||||
require.Equal(t, uint16(1), b.terminalState.cursorY)
|
||||
|
||||
b.ResizeView(40, 10)
|
||||
|
||||
|
@ -558,8 +558,8 @@ func TestHorizontalResizeView(t *testing.T) {
|
|||
hellohellohellohello
|
||||
goodbyegoodbye`
|
||||
|
||||
require.Equal(t, uint16(14), b.cursorX)
|
||||
require.Equal(t, uint16(2), b.cursorY)
|
||||
require.Equal(t, uint16(14), b.terminalState.cursorX)
|
||||
require.Equal(t, uint16(2), b.terminalState.cursorY)
|
||||
|
||||
lines := b.GetVisibleLines()
|
||||
strs := []string{}
|
||||
|
@ -612,8 +612,8 @@ goodbyegoodbye`
|
|||
}
|
||||
require.Equal(t, expected, strings.Join(strs, "\n"))
|
||||
|
||||
require.Equal(t, uint16(1), b.cursorY)
|
||||
require.Equal(t, uint16(14), b.cursorX)
|
||||
require.Equal(t, uint16(1), b.terminalState.cursorY)
|
||||
require.Equal(t, uint16(14), b.terminalState.cursorX)
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -623,7 +623,7 @@ dbye
|
|||
*/
|
||||
|
||||
func TestBufferMaxLines(t *testing.T) {
|
||||
b := NewBuffer(80, 2, CellAttributes{}, 2)
|
||||
b := NewBuffer(NewTerminalState(80, 2, CellAttributes{}, 2))
|
||||
|
||||
b.Write([]rune("hello")...)
|
||||
b.NewLine()
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
func (buffer *Buffer) GetHintAtPosition(col uint16, viewRow uint16) *hints.Hint {
|
||||
|
||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.scrollLinesFromBottom)
|
||||
row := buffer.convertViewLineToRawLine(viewRow) - uint64(buffer.terminalState.scrollLinesFromBottom)
|
||||
|
||||
cell := buffer.GetRawCell(col, row)
|
||||
if cell == nil || cell.Rune() == 0x00 {
|
||||
|
@ -32,7 +32,7 @@ func (buffer *Buffer) GetHintAtPosition(col uint16, viewRow uint16) *hints.Hint
|
|||
trimmed := strings.TrimLeft(candidate, " ")
|
||||
sx := col - uint16(len(trimmed)-1)
|
||||
|
||||
for i := col + 1; i < buffer.viewWidth; i++ {
|
||||
for i := col + 1; i < buffer.terminalState.viewWidth; i++ {
|
||||
cell := buffer.GetRawCell(i, row)
|
||||
if cell == nil {
|
||||
break
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package buffer
|
||||
|
||||
type TerminalState struct {
|
||||
scrollLinesFromBottom uint
|
||||
cursorX uint16
|
||||
cursorY uint16
|
||||
cursorAttr CellAttributes
|
||||
defaultCell Cell
|
||||
viewHeight uint16
|
||||
viewWidth uint16
|
||||
topMargin uint // see DECSTBM docs - this is for scrollable regions
|
||||
bottomMargin uint // see DECSTBM docs - this is for scrollable regions
|
||||
ReplaceMode bool // overwrite character at cursor or insert new
|
||||
OriginMode bool // see DECOM docs - whether cursor is positioned within the margins or not
|
||||
LineFeedMode bool
|
||||
AutoWrap bool
|
||||
maxLines uint64
|
||||
}
|
||||
|
||||
// NewTerminalMode creates a new terminal state
|
||||
func NewTerminalState(viewCols uint16, viewLines uint16, attr CellAttributes, maxLines uint64) *TerminalState {
|
||||
b := &TerminalState{
|
||||
cursorX: 0,
|
||||
cursorY: 0,
|
||||
cursorAttr: attr,
|
||||
AutoWrap: true,
|
||||
defaultCell: Cell{attr: attr},
|
||||
maxLines: maxLines,
|
||||
viewWidth: viewCols,
|
||||
viewHeight: viewLines,
|
||||
topMargin: 0,
|
||||
bottomMargin: uint(viewLines - 1),
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (terminalState *TerminalState) SetVerticalMargins(top uint, bottom uint) {
|
||||
terminalState.topMargin = top
|
||||
terminalState.bottomMargin = bottom
|
||||
}
|
||||
|
||||
// ResetVerticalMargins resets margins to extreme positions
|
||||
func (terminalState *TerminalState) ResetVerticalMargins() {
|
||||
terminalState.SetVerticalMargins(0, uint(terminalState.viewHeight-1))
|
||||
}
|
||||
|
||||
func (terminalState *TerminalState) IsNewLineMode() bool {
|
||||
return terminalState.LineFeedMode == false
|
||||
}
|
|
@ -400,7 +400,7 @@ func csiSetMarginsHandler(params []string, terminal *Terminal) error {
|
|||
top--
|
||||
bottom--
|
||||
|
||||
terminal.ActiveBuffer().SetVerticalMargins(uint(top), uint(bottom))
|
||||
terminal.terminalState.SetVerticalMargins(uint(top), uint(bottom))
|
||||
terminal.ActiveBuffer().SetPosition(0, 0)
|
||||
|
||||
return nil
|
||||
|
|
|
@ -25,15 +25,15 @@ func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error {
|
|||
switch modeStr {
|
||||
case "4":
|
||||
if enabled { // @todo support replace mode
|
||||
terminal.ActiveBuffer().SetInsertMode()
|
||||
terminal.SetInsertMode()
|
||||
} else {
|
||||
terminal.ActiveBuffer().SetReplaceMode()
|
||||
terminal.SetReplaceMode()
|
||||
}
|
||||
case "20":
|
||||
if enabled {
|
||||
terminal.ActiveBuffer().SetNewLineMode()
|
||||
terminal.SetNewLineMode()
|
||||
} else {
|
||||
terminal.ActiveBuffer().SetLineFeedMode()
|
||||
terminal.SetLineFeedMode()
|
||||
}
|
||||
case "?1":
|
||||
terminal.modes.ApplicationCursorKeys = enabled
|
||||
|
@ -61,11 +61,11 @@ func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error {
|
|||
*/
|
||||
case "?6":
|
||||
// DECOM
|
||||
terminal.ActiveBuffer().SetOriginMode(enabled)
|
||||
terminal.SetOriginMode(enabled)
|
||||
case "?7":
|
||||
// auto-wrap mode
|
||||
//DECAWM
|
||||
terminal.ActiveBuffer().SetAutoWrap(enabled)
|
||||
terminal.SetAutoWrap(enabled)
|
||||
case "?9":
|
||||
if enabled {
|
||||
terminal.logger.Infof("Turning on X10 mouse mode")
|
||||
|
|
|
@ -8,7 +8,7 @@ func screenStateHandler(pty chan rune, terminal *Terminal) error {
|
|||
case '8': // DECALN -- Screen Alignment Pattern
|
||||
// hide cursor?
|
||||
buffer := terminal.ActiveBuffer()
|
||||
buffer.ResetVerticalMargins()
|
||||
terminal.ResetVerticalMargins()
|
||||
buffer.ScrollToEnd()
|
||||
|
||||
// Fill the whole screen with E's
|
||||
|
@ -16,7 +16,7 @@ func screenStateHandler(pty chan rune, terminal *Terminal) error {
|
|||
for count > 0 {
|
||||
buffer.Write('E')
|
||||
count--
|
||||
if count > 0 && !buffer.IsAutoWrap() && count%buffer.ViewWidth() == 0 {
|
||||
if count > 0 && !terminal.IsAutoWrap() && count%buffer.ViewWidth() == 0 {
|
||||
buffer.Index()
|
||||
buffer.CarriageReturn()
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ type Terminal struct {
|
|||
charWidth float32
|
||||
charHeight float32
|
||||
lastBuffer uint8
|
||||
terminalState *buffer.TerminalState
|
||||
platformDependentSettings platform.PlatformDependentSettings
|
||||
}
|
||||
|
||||
|
@ -66,20 +67,10 @@ type Winsize struct {
|
|||
|
||||
func New(pty platform.Pty, logger *zap.SugaredLogger, config *config.Config) *Terminal {
|
||||
t := &Terminal{
|
||||
buffers: []*buffer.Buffer{
|
||||
buffer.NewBuffer(1, 1, buffer.CellAttributes{
|
||||
FgColour: config.ColourScheme.Foreground,
|
||||
BgColour: config.ColourScheme.Background,
|
||||
}, config.MaxLines),
|
||||
buffer.NewBuffer(1, 1, buffer.CellAttributes{
|
||||
FgColour: config.ColourScheme.Foreground,
|
||||
BgColour: config.ColourScheme.Background,
|
||||
}, config.MaxLines),
|
||||
buffer.NewBuffer(1, 1, buffer.CellAttributes{
|
||||
FgColour: config.ColourScheme.Foreground,
|
||||
BgColour: config.ColourScheme.Background,
|
||||
}, config.MaxLines),
|
||||
},
|
||||
terminalState: buffer.NewTerminalState(1, 1, buffer.CellAttributes{
|
||||
FgColour: config.ColourScheme.Foreground,
|
||||
BgColour: config.ColourScheme.Background,
|
||||
}, config.MaxLines),
|
||||
pty: pty,
|
||||
logger: logger,
|
||||
config: config,
|
||||
|
@ -89,7 +80,12 @@ func New(pty platform.Pty, logger *zap.SugaredLogger, config *config.Config) *Te
|
|||
},
|
||||
platformDependentSettings: pty.GetPlatformDependentSettings(),
|
||||
}
|
||||
t.activeBuffer = t.buffers[MainBuffer]
|
||||
t.buffers = []*buffer.Buffer{
|
||||
buffer.NewBuffer(t.terminalState),
|
||||
buffer.NewBuffer(t.terminalState),
|
||||
buffer.NewBuffer(t.terminalState),
|
||||
}
|
||||
t.activeBuffer = t.buffers[0]
|
||||
return t
|
||||
|
||||
}
|
||||
|
@ -252,7 +248,7 @@ func (terminal *Terminal) Write(data []byte) error {
|
|||
}
|
||||
|
||||
func (terminal *Terminal) WriteReturn() error {
|
||||
if terminal.ActiveBuffer().IsNewLineMode() {
|
||||
if terminal.terminalState.IsNewLineMode() {
|
||||
return terminal.Write([]byte{0x0d, 0x0a})
|
||||
} else {
|
||||
return terminal.Write([]byte{0x0d})
|
||||
|
@ -320,3 +316,36 @@ func (terminal *Terminal) SetSize(newCols uint, newLines uint) error {
|
|||
terminal.emitResize()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (terminal *Terminal) SetAutoWrap(enabled bool) {
|
||||
terminal.terminalState.AutoWrap = enabled
|
||||
}
|
||||
|
||||
func (terminal *Terminal) IsAutoWrap() bool {
|
||||
return terminal.terminalState.AutoWrap
|
||||
}
|
||||
|
||||
func (terminal *Terminal) SetOriginMode(enabled bool) {
|
||||
terminal.terminalState.OriginMode = enabled
|
||||
terminal.ActiveBuffer().SetPosition(0, 0)
|
||||
}
|
||||
|
||||
func (terminal *Terminal) SetInsertMode() {
|
||||
terminal.terminalState.ReplaceMode = false
|
||||
}
|
||||
|
||||
func (terminal *Terminal) SetReplaceMode() {
|
||||
terminal.terminalState.ReplaceMode = true
|
||||
}
|
||||
|
||||
func (terminal *Terminal) SetNewLineMode() {
|
||||
terminal.terminalState.LineFeedMode = false
|
||||
}
|
||||
|
||||
func (terminal *Terminal) SetLineFeedMode() {
|
||||
terminal.terminalState.LineFeedMode = true
|
||||
}
|
||||
|
||||
func (terminal *Terminal) ResetVerticalMargins() {
|
||||
terminal.terminalState.ResetVerticalMargins()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue