From 9ac2e9cba090e67ea4f70e6cef77a06ccbef1ba8 Mon Sep 17 00:00:00 2001 From: Liam Galvin Date: Wed, 8 Aug 2018 00:34:44 +0100 Subject: [PATCH] ess --- buffer/buffer.go | 62 ++++++++++++++++++++++++++----------------- buffer/buffer_test.go | 32 +++++++++++++++------- main.go | 3 +++ terminal/ansi.go | 33 +++++++++++++++++++++++ terminal/csi.go | 5 ++-- terminal/escapes.go | 2 +- 6 files changed, 100 insertions(+), 37 deletions(-) diff --git a/buffer/buffer.go b/buffer/buffer.go index b2c276a..0f1f8d5 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -12,6 +12,8 @@ type Buffer struct { viewWidth uint16 cursorAttr CellAttributes displayChangeHandlers []chan bool + savedX uint16 + savedY uint16 } // NewBuffer creates a new terminal buffer @@ -26,6 +28,16 @@ func NewBuffer(viewCols uint16, viewLines uint16, attr CellAttributes) *Buffer { return b } +func (buffer *Buffer) SaveCursor() { + buffer.savedX = buffer.cursorX + buffer.savedY = buffer.cursorY +} + +func (buffer *Buffer) RestoreCursor() { + buffer.cursorX = buffer.savedX + buffer.cursorY = buffer.savedY +} + func (buffer *Buffer) CursorAttr() *CellAttributes { return &buffer.cursorAttr } @@ -155,13 +167,11 @@ func (buffer *Buffer) incrementCursorPosition() { buffer.cursorX = 0 buffer.cursorY++ - _, err := buffer.getCurrentLine() - if err != nil { - line := newLine() - line.setWrapped(true) - buffer.lines = append(buffer.lines, line) - } + rawLine := int(buffer.RawLine()) + line := newLine() + line.setWrapped(true) + buffer.lines = append(append(buffer.lines[:rawLine], line), buffer.lines[rawLine:]...) } } } @@ -172,18 +182,19 @@ func (buffer *Buffer) CarriageReturn() { line, err := buffer.getCurrentLine() if err != nil { - buffer.ensureLinesExistToRawHeight() - line, err = buffer.getCurrentLine() - if err != nil { - panic(err) - } + + fmt.Println("Failed to get new line during carriage return") + + buffer.cursorX = 0 + return } + if buffer.cursorX == 0 && line.wrapped { - buffer.cursorY-- if len(line.cells) == 0 { rawLine := int(buffer.RawLine()) buffer.lines = append(buffer.lines[:rawLine], buffer.lines[rawLine+1:]...) } + buffer.cursorY-- } else { buffer.cursorX = 0 } @@ -196,20 +207,14 @@ func (buffer *Buffer) NewLine() { // if we're at the beginning of a line which wrapped from the previous one, and we need a new line, we can effectively not add a new line, and set the current one to non-wrapped if buffer.cursorX == 0 { line, err := buffer.getCurrentLine() - if err != nil { - buffer.ensureLinesExistToRawHeight() - line, err = buffer.getCurrentLine() - if err != nil { - panic(err) - } - } - if line.wrapped { + if err == nil && line != nil && line.wrapped { line.setWrapped(false) return } } if buffer.cursorY == buffer.viewHeight-1 { + buffer.ensureLinesExistToRawHeight() buffer.lines = append(buffer.lines, newLine()) } else { buffer.cursorY++ @@ -313,19 +318,28 @@ func (buffer *Buffer) EraseLineToCursor() { } } -func (buffer *Buffer) EraseLineAfterCursor() { +func (buffer *Buffer) EraseLineFromCursor() { defer buffer.emitDisplayChange() line, err := buffer.getCurrentLine() if err != nil { return } - max := int(buffer.cursorX + 1) + if line.wrapped && buffer.cursorX == 0 { + //panic("wtf") + return + } + + max := int(buffer.cursorX) if max > len(line.cells) { max = len(line.cells) } - line.cells = line.cells[:max] + fmt.Printf("Erase line from cursor, cursor is at %d\n", buffer.cursorX) + + for c := int(buffer.cursorX); c < len(line.cells); c++ { + line.cells[c].erase() + } } func (buffer *Buffer) EraseDisplay() { @@ -338,7 +352,7 @@ func (buffer *Buffer) EraseDisplay() { } } -func (buffer *Buffer) EraseDisplayAfterCursor() { +func (buffer *Buffer) EraseDisplayFromCursor() { defer buffer.emitDisplayChange() line, err := buffer.getCurrentLine() if err != nil { diff --git a/buffer/buffer_test.go b/buffer/buffer_test.go index 4132f6d..58a4848 100644 --- a/buffer/buffer_test.go +++ b/buffer/buffer_test.go @@ -10,13 +10,16 @@ import ( ) func TestOffsets(t *testing.T) { - b := NewBuffer(10, 8, CellAttributes{}) - test := "hellothere\r\nhellothere\r\nhellothere\r\nhellothere\r\nhellothere\r\nhellothere\r\nhellothere\r\nhellothere\r\nhellothere\r\nhellothere\r\nhellothere\r\nhellothere\r\n?" - b.Write([]rune(test)...) + b := NewBuffer(10, 3, CellAttributes{}) + b.Write([]rune("hello\r\n")...) + b.Write([]rune("hello\r\n")...) + b.Write([]rune("hello\r\n")...) + b.Write([]rune("hello\r\n")...) + b.Write([]rune("hello")...) assert.Equal(t, uint16(10), b.ViewWidth()) assert.Equal(t, uint16(10), b.Width()) - assert.Equal(t, uint16(8), b.ViewHeight()) - assert.Equal(t, 13, b.Height()) + assert.Equal(t, uint16(3), b.ViewHeight()) + assert.Equal(t, 5, b.Height()) } func TestBufferCreation(t *testing.T) { @@ -126,6 +129,15 @@ func TestWritingNewLineAsFirstRuneOnWrappedLine(t *testing.T) { func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) { b := NewBuffer(3, 20, CellAttributes{}) + /* + |abc + |d + |_ef + | + | + |z + */ + b.Write('a', 'b', 'c', 'd') b.Write(0x0a) b.Write('e', 'f') @@ -136,7 +148,7 @@ func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) { assert.Equal(t, "abc", b.lines[0].String()) assert.Equal(t, "d", b.lines[1].String()) - assert.Equal(t, "ef", b.lines[2].String()) + assert.Equal(t, "\x00ef", b.lines[2].String()) assert.Equal(t, "", b.lines[3].String()) assert.Equal(t, "", b.lines[4].String()) assert.Equal(t, "z", b.lines[5].String()) @@ -366,9 +378,9 @@ func TestEraseLineAfterCursor(t *testing.T) { b := NewBuffer(80, 5, CellAttributes{}) b.Write([]rune("hello, this is a test\r\ndeleted")...) b.MovePosition(-3, 0) - b.EraseLineAfterCursor() + b.EraseLineFromCursor() assert.Equal(t, "hello, this is a test", b.lines[0].String()) - assert.Equal(t, "delet", b.lines[1].String()) + assert.Equal(t, "dele", b.lines[1].String()) } func TestEraseDisplay(t *testing.T) { b := NewBuffer(80, 5, CellAttributes{}) @@ -392,11 +404,11 @@ func TestEraseDisplayToCursor(t *testing.T) { } -func TestEraseDisplayAfterCursor(t *testing.T) { +func TestEraseDisplayFromCursor(t *testing.T) { b := NewBuffer(80, 5, CellAttributes{}) b.Write([]rune("hello\r\nasdasd\r\nthings")...) b.MovePosition(-3, -1) - b.EraseDisplayAfterCursor() + b.EraseDisplayFromCursor() lines := b.GetVisibleLines() assert.Equal(t, "hello", lines[0].String()) assert.Equal(t, "asd", lines[1].String()) diff --git a/main.go b/main.go index fb0b97e..05efcab 100644 --- a/main.go +++ b/main.go @@ -19,8 +19,11 @@ func getConfig() config.Config { if ignore { return config.DefaultConfig } + conf := loadConfigFile() flag.BoolVar(&conf.DebugMode, "debug", conf.DebugMode, "Enable debug logging") + flag.BoolVar(&conf.Rendering.AlwaysRepaint, "always-repaint", conf.Rendering.AlwaysRepaint, "Always repaint the window, even when no changes have occurred") + flag.Parse() return conf } diff --git a/terminal/ansi.go b/terminal/ansi.go index 0433363..cee7b45 100644 --- a/terminal/ansi.go +++ b/terminal/ansi.go @@ -1,8 +1,41 @@ package terminal +// https://www.xfree86.org/4.8.0/ctlseqs.html +// https://vt100.net/docs/vt100-ug/chapter3.html + var ansiSequenceMap = map[rune]escapeSequenceHandler{ '[': csiHandler, 0x5d: oscHandler, + '7': saveCursorHandler, + '8': restoreCursorHandler, + 'D': indexHandler, + 'M': reverseIndexHandler, +} + +func indexHandler(buffer chan rune, terminal *Terminal) error { + // @todo is thus right? + // "This sequence causes the active position to move downward one line without changing the column position. If the active position is at the bottom margin, a scroll up is performed." + if terminal.buffer.CursorLine() == terminal.buffer.ViewHeight()-1 { + terminal.buffer.NewLine() + return nil + } + terminal.buffer.MovePosition(0, 1) + return nil +} + +func reverseIndexHandler(buffer chan rune, terminal *Terminal) error { + terminal.buffer.MovePosition(0, -1) + return nil +} + +func saveCursorHandler(buffer chan rune, terminal *Terminal) error { + terminal.buffer.SaveCursor() + return nil +} + +func restoreCursorHandler(buffer chan rune, terminal *Terminal) error { + terminal.buffer.RestoreCursor() + return nil } func ansiHandler(buffer chan rune, terminal *Terminal) error { diff --git a/terminal/csi.go b/terminal/csi.go index 528e7cf..e161f3e 100644 --- a/terminal/csi.go +++ b/terminal/csi.go @@ -195,7 +195,7 @@ func csiEraseInDisplayHandler(params []string, intermediate string, terminal *Te switch n { case "0", "": - terminal.buffer.EraseDisplayAfterCursor() + terminal.buffer.EraseDisplayFromCursor() case "1": terminal.buffer.EraseDisplayToCursor() case "2": @@ -209,6 +209,7 @@ func csiEraseInDisplayHandler(params []string, intermediate string, terminal *Te // CSI Ps K func csiEraseInLineHandler(params []string, intermediate string, terminal *Terminal) error { + n := "0" if len(params) > 0 { n = params[0] @@ -216,7 +217,7 @@ func csiEraseInLineHandler(params []string, intermediate string, terminal *Termi switch n { case "0", "": //erase adter cursor - terminal.buffer.EraseLineAfterCursor() + terminal.buffer.EraseLineFromCursor() case "1": // erase to cursor inclusive terminal.buffer.EraseLineToCursor() case "2": // erase entire diff --git a/terminal/escapes.go b/terminal/escapes.go index b362106..225feb8 100644 --- a/terminal/escapes.go +++ b/terminal/escapes.go @@ -38,7 +38,7 @@ func (terminal *Terminal) processInput(ctx context.Context, buffer chan rune) { continue } - terminal.logger.Debugf("Received character 0x%X: %s", b, string(b)) + terminal.logger.Debugf("Received character 0x%X: %q", b, string(b)) switch b { case 0x0a: