diff --git a/buffer/buffer.go b/buffer/buffer.go index f301374..a0061f0 100644 --- a/buffer/buffer.go +++ b/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) } - diff --git a/buffer/buffer_test.go b/buffer/buffer_test.go index 1b48f2e..993bdf9 100644 --- a/buffer/buffer_test.go +++ b/buffer/buffer_test.go @@ -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() diff --git a/buffer/hint.go b/buffer/hint.go index 666ef61..5bf2a83 100644 --- a/buffer/hint.go +++ b/buffer/hint.go @@ -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 diff --git a/buffer/terminal_state.go b/buffer/terminal_state.go new file mode 100644 index 0000000..97088ce --- /dev/null +++ b/buffer/terminal_state.go @@ -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 +} diff --git a/terminal/csi.go b/terminal/csi.go index ec172af..dc19982 100644 --- a/terminal/csi.go +++ b/terminal/csi.go @@ -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 diff --git a/terminal/modes.go b/terminal/modes.go index ae69664..5ba6c94 100644 --- a/terminal/modes.go +++ b/terminal/modes.go @@ -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") diff --git a/terminal/scr_state.go b/terminal/scr_state.go index 752c20d..ff64c83 100644 --- a/terminal/scr_state.go +++ b/terminal/scr_state.go @@ -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() } diff --git a/terminal/terminal.go b/terminal/terminal.go index a9986be..025ec02 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -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() +}