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