mirror of https://github.com/liamg/aminal.git
Merge pull request #31 from liamg/fix-vim-bug
Fix vim bug and dodgy deletes
This commit is contained in:
commit
9bae0f4aa6
|
@ -19,6 +19,7 @@ type Buffer struct {
|
||||||
bottomMargin 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
|
replaceMode bool // overwrite character at cursor or insert new
|
||||||
autoWrap bool
|
autoWrap bool
|
||||||
|
dirty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBuffer creates a new terminal buffer
|
// NewBuffer creates a new terminal buffer
|
||||||
|
@ -35,6 +36,14 @@ func NewBuffer(viewCols uint16, viewLines uint16, attr CellAttributes) *Buffer {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (buffer *Buffer) IsDirty() bool {
|
||||||
|
if !buffer.dirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
buffer.dirty = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) SetAutoWrap(enabled bool) {
|
func (buffer *Buffer) SetAutoWrap(enabled bool) {
|
||||||
buffer.autoWrap = enabled
|
buffer.autoWrap = enabled
|
||||||
}
|
}
|
||||||
|
@ -132,23 +141,8 @@ func (buffer *Buffer) GetCell(viewCol uint16, viewRow uint16) *Cell {
|
||||||
return &line.cells[viewCol]
|
return &line.cells[viewCol]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) AttachDisplayChangeHandler(handler chan bool) {
|
|
||||||
if buffer.displayChangeHandlers == nil {
|
|
||||||
buffer.displayChangeHandlers = []chan bool{}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.displayChangeHandlers = append(buffer.displayChangeHandlers, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (buffer *Buffer) emitDisplayChange() {
|
func (buffer *Buffer) emitDisplayChange() {
|
||||||
for _, channel := range buffer.displayChangeHandlers {
|
buffer.dirty = true
|
||||||
go func(c chan bool) {
|
|
||||||
select {
|
|
||||||
case c <- true:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}(channel)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Column returns cursor column
|
// Column returns cursor column
|
||||||
|
@ -219,7 +213,7 @@ func (buffer *Buffer) insertLine() {
|
||||||
copy(out[0:], before)
|
copy(out[0:], before)
|
||||||
|
|
||||||
pos := buffer.RawLine()
|
pos := buffer.RawLine()
|
||||||
for i := topIndex; i <= bottomIndex; i++ {
|
for i := topIndex; i < bottomIndex; i++ {
|
||||||
if i < pos {
|
if i < pos {
|
||||||
out[i] = buffer.lines[i]
|
out[i] = buffer.lines[i]
|
||||||
} else {
|
} else {
|
||||||
|
@ -227,7 +221,7 @@ func (buffer *Buffer) insertLine() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(out[bottomIndex:], after)
|
copy(out[bottomIndex+1:], after)
|
||||||
|
|
||||||
out[pos] = newLine()
|
out[pos] = newLine()
|
||||||
buffer.lines = out
|
buffer.lines = out
|
||||||
|
@ -260,17 +254,18 @@ func (buffer *Buffer) Index() {
|
||||||
|
|
||||||
if uint(buffer.cursorY) < buffer.bottomMargin {
|
if uint(buffer.cursorY) < buffer.bottomMargin {
|
||||||
buffer.cursorY++
|
buffer.cursorY++
|
||||||
|
} else {
|
||||||
|
|
||||||
|
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin))
|
||||||
|
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin))
|
||||||
|
|
||||||
|
for i := topIndex; i < bottomIndex; i++ {
|
||||||
|
buffer.lines[i] = buffer.lines[i+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.lines[bottomIndex] = newLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin))
|
|
||||||
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin))
|
|
||||||
|
|
||||||
for i := topIndex; i < bottomIndex; i++ {
|
|
||||||
buffer.lines[i] = buffer.lines[i+1]
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.lines[buffer.RawLine()] = newLine()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,17 +284,17 @@ func (buffer *Buffer) ReverseIndex() {
|
||||||
|
|
||||||
if uint(buffer.cursorY) > buffer.topMargin {
|
if uint(buffer.cursorY) > buffer.topMargin {
|
||||||
buffer.cursorY--
|
buffer.cursorY--
|
||||||
|
} else {
|
||||||
|
|
||||||
|
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin))
|
||||||
|
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin))
|
||||||
|
|
||||||
|
for i := bottomIndex; i > topIndex; i-- {
|
||||||
|
buffer.lines[i] = buffer.lines[i-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.lines[topIndex] = newLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin))
|
|
||||||
bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin))
|
|
||||||
|
|
||||||
for i := bottomIndex; i > topIndex; i-- {
|
|
||||||
buffer.lines[i] = buffer.lines[i-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.lines[buffer.RawLine()] = newLine()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,10 +515,9 @@ func (buffer *Buffer) EraseLineFromCursor() {
|
||||||
|
|
||||||
if len(line.cells) > 0 {
|
if len(line.cells) > 0 {
|
||||||
cx := buffer.cursorX
|
cx := buffer.cursorX
|
||||||
if int(cx) >= len(line.cells) {
|
if int(cx) < len(line.cells) {
|
||||||
return // nothing to delete
|
line.cells = line.cells[:buffer.cursorX]
|
||||||
}
|
}
|
||||||
line.cells = line.cells[:buffer.cursorX]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
max := int(buffer.ViewWidth()) - len(line.cells)
|
max := int(buffer.ViewWidth()) - len(line.cells)
|
||||||
|
@ -545,6 +539,15 @@ func (buffer *Buffer) EraseDisplay() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (buffer *Buffer) DeleteChars(n int) {
|
||||||
|
defer buffer.emitDisplayChange()
|
||||||
|
|
||||||
|
line := buffer.getCurrentLine()
|
||||||
|
before := line.cells[:buffer.cursorX]
|
||||||
|
after := line.cells[int(buffer.cursorX)+n:]
|
||||||
|
line.cells = append(before, after...)
|
||||||
|
}
|
||||||
|
|
||||||
func (buffer *Buffer) EraseCharacters(n int) {
|
func (buffer *Buffer) EraseCharacters(n int) {
|
||||||
defer buffer.emitDisplayChange()
|
defer buffer.emitDisplayChange()
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package buffer
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
@ -298,29 +297,6 @@ func TestCursorAttr(t *testing.T) {
|
||||||
assert.Equal(t, &b.cursorAttr, b.CursorAttr())
|
assert.Equal(t, &b.cursorAttr, b.CursorAttr())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAttachingHandlers(t *testing.T) {
|
|
||||||
b := NewBuffer(80, 2, CellAttributes{})
|
|
||||||
displayHandler := make(chan bool, 1)
|
|
||||||
b.AttachDisplayChangeHandler(displayHandler)
|
|
||||||
require.Equal(t, 1, len(b.displayChangeHandlers))
|
|
||||||
assert.Equal(t, b.displayChangeHandlers[0], displayHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmitDisplayHandlers(t *testing.T) {
|
|
||||||
b := NewBuffer(80, 2, CellAttributes{})
|
|
||||||
displayHandler := make(chan bool, 1)
|
|
||||||
b.AttachDisplayChangeHandler(displayHandler)
|
|
||||||
b.emitDisplayChange()
|
|
||||||
time.Sleep(time.Millisecond * 50)
|
|
||||||
ok := false
|
|
||||||
select {
|
|
||||||
case <-displayHandler:
|
|
||||||
ok = true
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
assert.True(t, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCursorPositionQuerying(t *testing.T) {
|
func TestCursorPositionQuerying(t *testing.T) {
|
||||||
b := NewBuffer(80, 20, CellAttributes{})
|
b := NewBuffer(80, 20, CellAttributes{})
|
||||||
b.cursorX = 17
|
b.cursorX = 17
|
||||||
|
|
|
@ -158,8 +158,6 @@ func (gui *GUI) key(w *glfw.Window, key glfw.Key, scancode int, action glfw.Acti
|
||||||
if gui.terminal.IsApplicationCursorKeysModeEnabled() {
|
if gui.terminal.IsApplicationCursorKeysModeEnabled() {
|
||||||
gui.terminal.Write([]byte{
|
gui.terminal.Write([]byte{
|
||||||
0x1b,
|
0x1b,
|
||||||
'O',
|
|
||||||
0x1b,
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
gui.terminal.Write([]byte{
|
gui.terminal.Write([]byte{
|
||||||
|
|
|
@ -28,7 +28,7 @@ var csiSequences = []csiMapping{
|
||||||
csiMapping{id: 'l', handler: csiResetModeHandler, expectedParams: &expectedParams{min: 1, max: 1}, description: "Reset Mode (RM)"},
|
csiMapping{id: 'l', handler: csiResetModeHandler, expectedParams: &expectedParams{min: 1, max: 1}, description: "Reset Mode (RM)"},
|
||||||
csiMapping{id: 'm', handler: sgrSequenceHandler, description: "Character Attributes (SGR)"},
|
csiMapping{id: 'm', handler: sgrSequenceHandler, description: "Character Attributes (SGR)"},
|
||||||
csiMapping{id: 'n', handler: csiDeviceStatusReportHandler, description: "Device Status Report (DSR)"},
|
csiMapping{id: 'n', handler: csiDeviceStatusReportHandler, description: "Device Status Report (DSR)"},
|
||||||
csiMapping{id: 'r', handler: csiSetMarginsHandler, expectedParams: &expectedParams{min: 2, max: 2}, description: "Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM), VT100"},
|
csiMapping{id: 'r', handler: csiSetMarginsHandler, expectedParams: &expectedParams{min: 0, max: 2}, description: "Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM), VT100"},
|
||||||
csiMapping{id: 't', handler: csiWindowManipulation, description: "Window manipulation"},
|
csiMapping{id: 't', handler: csiWindowManipulation, description: "Window manipulation"},
|
||||||
csiMapping{id: 'A', handler: csiCursorUpHandler, description: "Cursor Up Ps Times (default = 1) (CUU)"},
|
csiMapping{id: 'A', handler: csiCursorUpHandler, description: "Cursor Up Ps Times (default = 1) (CUU)"},
|
||||||
csiMapping{id: 'B', handler: csiCursorDownHandler, description: "Cursor Down Ps Times (default = 1) (CUD)"},
|
csiMapping{id: 'B', handler: csiCursorDownHandler, description: "Cursor Down Ps Times (default = 1) (CUD)"},
|
||||||
|
@ -77,8 +77,9 @@ CSI:
|
||||||
if sequence.expectedParams != nil && (uint8(len(params)) < sequence.expectedParams.min || uint8(len(params)) > sequence.expectedParams.max) {
|
if sequence.expectedParams != nil && (uint8(len(params)) < sequence.expectedParams.min || uint8(len(params)) > sequence.expectedParams.max) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
terminal.logger.Debugf("CSI 0x%02X (ESC[%s%s%s) %s", final, param, intermediate, string(final), sequence.description)
|
x, y := terminal.ActiveBuffer().CursorColumn(), terminal.ActiveBuffer().CursorLine()
|
||||||
err := sequence.handler(params, intermediate, terminal)
|
err := sequence.handler(params, intermediate, terminal)
|
||||||
|
terminal.logger.Debugf("CSI 0x%02X (ESC[%s%s%s) %s - %d,%d -> %d,%d", final, param, intermediate, string(final), sequence.description, x, y, terminal.ActiveBuffer().CursorColumn(), terminal.ActiveBuffer().CursorLine())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,8 +378,7 @@ func csiDeleteHandler(params []string, intermediate string, terminal *Terminal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
terminal.ActiveBuffer().EraseCharacters(n)
|
terminal.ActiveBuffer().DeleteChars(n)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ func (terminal *Terminal) processInput(ctx context.Context, pty chan rune) {
|
||||||
} else {
|
} else {
|
||||||
//terminal.logger.Debugf("Received character 0x%X: %q", b, string(b))
|
//terminal.logger.Debugf("Received character 0x%X: %q", b, string(b))
|
||||||
if b >= 0x20 {
|
if b >= 0x20 {
|
||||||
terminal.logger.Debugf("%c", b)
|
//terminal.logger.Debugf("%c", b)
|
||||||
terminal.ActiveBuffer().Write(b)
|
terminal.ActiveBuffer().Write(b)
|
||||||
} else {
|
} else {
|
||||||
terminal.logger.Error("Non-readable rune received: 0x%X", b)
|
terminal.logger.Error("Non-readable rune received: 0x%X", b)
|
||||||
|
|
|
@ -100,7 +100,7 @@ func (terminal *Terminal) SetBracketedPasteMode(enabled bool) {
|
||||||
func (terminal *Terminal) CheckDirty() bool {
|
func (terminal *Terminal) CheckDirty() bool {
|
||||||
d := terminal.isDirty
|
d := terminal.isDirty
|
||||||
terminal.isDirty = false
|
terminal.isDirty = false
|
||||||
return d
|
return d || terminal.ActiveBuffer().IsDirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (terminal *Terminal) SetDirty() {
|
func (terminal *Terminal) SetDirty() {
|
||||||
|
@ -166,12 +166,6 @@ func (terminal *Terminal) GetCell(col uint16, row uint16) *buffer.Cell {
|
||||||
return terminal.ActiveBuffer().GetCell(col, row)
|
return terminal.ActiveBuffer().GetCell(col, row)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (terminal *Terminal) AttachDisplayChangeHandler(handler chan bool) {
|
|
||||||
for i := range terminal.buffers {
|
|
||||||
terminal.buffers[i].AttachDisplayChangeHandler(handler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (terminal *Terminal) AttachTitleChangeHandler(handler chan bool) {
|
func (terminal *Terminal) AttachTitleChangeHandler(handler chan bool) {
|
||||||
terminal.titleHandlers = append(terminal.titleHandlers, handler)
|
terminal.titleHandlers = append(terminal.titleHandlers, handler)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue