This commit is contained in:
Liam Galvin 2018-08-08 00:34:44 +01:00
parent 132ce51f78
commit 9ac2e9cba0
6 changed files with 100 additions and 37 deletions

View File

@ -12,6 +12,8 @@ type Buffer struct {
viewWidth uint16 viewWidth uint16
cursorAttr CellAttributes cursorAttr CellAttributes
displayChangeHandlers []chan bool displayChangeHandlers []chan bool
savedX uint16
savedY uint16
} }
// NewBuffer creates a new terminal buffer // NewBuffer creates a new terminal buffer
@ -26,6 +28,16 @@ func NewBuffer(viewCols uint16, viewLines uint16, attr CellAttributes) *Buffer {
return b 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 { func (buffer *Buffer) CursorAttr() *CellAttributes {
return &buffer.cursorAttr return &buffer.cursorAttr
} }
@ -155,13 +167,11 @@ func (buffer *Buffer) incrementCursorPosition() {
buffer.cursorX = 0 buffer.cursorX = 0
buffer.cursorY++ buffer.cursorY++
_, err := buffer.getCurrentLine() rawLine := int(buffer.RawLine())
if err != nil {
line := newLine()
line.setWrapped(true)
buffer.lines = append(buffer.lines, line)
}
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() line, err := buffer.getCurrentLine()
if err != nil { if err != nil {
buffer.ensureLinesExistToRawHeight()
line, err = buffer.getCurrentLine() fmt.Println("Failed to get new line during carriage return")
if err != nil {
panic(err) buffer.cursorX = 0
} return
} }
if buffer.cursorX == 0 && line.wrapped { if buffer.cursorX == 0 && line.wrapped {
buffer.cursorY--
if len(line.cells) == 0 { if len(line.cells) == 0 {
rawLine := int(buffer.RawLine()) rawLine := int(buffer.RawLine())
buffer.lines = append(buffer.lines[:rawLine], buffer.lines[rawLine+1:]...) buffer.lines = append(buffer.lines[:rawLine], buffer.lines[rawLine+1:]...)
} }
buffer.cursorY--
} else { } else {
buffer.cursorX = 0 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 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 { if buffer.cursorX == 0 {
line, err := buffer.getCurrentLine() line, err := buffer.getCurrentLine()
if err != nil { if err == nil && line != nil && line.wrapped {
buffer.ensureLinesExistToRawHeight()
line, err = buffer.getCurrentLine()
if err != nil {
panic(err)
}
}
if line.wrapped {
line.setWrapped(false) line.setWrapped(false)
return return
} }
} }
if buffer.cursorY == buffer.viewHeight-1 { if buffer.cursorY == buffer.viewHeight-1 {
buffer.ensureLinesExistToRawHeight()
buffer.lines = append(buffer.lines, newLine()) buffer.lines = append(buffer.lines, newLine())
} else { } else {
buffer.cursorY++ buffer.cursorY++
@ -313,19 +318,28 @@ func (buffer *Buffer) EraseLineToCursor() {
} }
} }
func (buffer *Buffer) EraseLineAfterCursor() { func (buffer *Buffer) EraseLineFromCursor() {
defer buffer.emitDisplayChange() defer buffer.emitDisplayChange()
line, err := buffer.getCurrentLine() line, err := buffer.getCurrentLine()
if err != nil { if err != nil {
return return
} }
max := int(buffer.cursorX + 1) if line.wrapped && buffer.cursorX == 0 {
//panic("wtf")
return
}
max := int(buffer.cursorX)
if max > len(line.cells) { if max > len(line.cells) {
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() { func (buffer *Buffer) EraseDisplay() {
@ -338,7 +352,7 @@ func (buffer *Buffer) EraseDisplay() {
} }
} }
func (buffer *Buffer) EraseDisplayAfterCursor() { func (buffer *Buffer) EraseDisplayFromCursor() {
defer buffer.emitDisplayChange() defer buffer.emitDisplayChange()
line, err := buffer.getCurrentLine() line, err := buffer.getCurrentLine()
if err != nil { if err != nil {

View File

@ -10,13 +10,16 @@ import (
) )
func TestOffsets(t *testing.T) { func TestOffsets(t *testing.T) {
b := NewBuffer(10, 8, CellAttributes{}) b := NewBuffer(10, 3, 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("hello\r\n")...)
b.Write([]rune(test)...) 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.ViewWidth())
assert.Equal(t, uint16(10), b.Width()) assert.Equal(t, uint16(10), b.Width())
assert.Equal(t, uint16(8), b.ViewHeight()) assert.Equal(t, uint16(3), b.ViewHeight())
assert.Equal(t, 13, b.Height()) assert.Equal(t, 5, b.Height())
} }
func TestBufferCreation(t *testing.T) { func TestBufferCreation(t *testing.T) {
@ -126,6 +129,15 @@ func TestWritingNewLineAsFirstRuneOnWrappedLine(t *testing.T) {
func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) { func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) {
b := NewBuffer(3, 20, CellAttributes{}) b := NewBuffer(3, 20, CellAttributes{})
/*
|abc
|d
|_ef
|
|
|z
*/
b.Write('a', 'b', 'c', 'd') b.Write('a', 'b', 'c', 'd')
b.Write(0x0a) b.Write(0x0a)
b.Write('e', 'f') b.Write('e', 'f')
@ -136,7 +148,7 @@ func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) {
assert.Equal(t, "abc", b.lines[0].String()) assert.Equal(t, "abc", b.lines[0].String())
assert.Equal(t, "d", b.lines[1].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[3].String())
assert.Equal(t, "", b.lines[4].String()) assert.Equal(t, "", b.lines[4].String())
assert.Equal(t, "z", b.lines[5].String()) assert.Equal(t, "z", b.lines[5].String())
@ -366,9 +378,9 @@ func TestEraseLineAfterCursor(t *testing.T) {
b := NewBuffer(80, 5, CellAttributes{}) b := NewBuffer(80, 5, CellAttributes{})
b.Write([]rune("hello, this is a test\r\ndeleted")...) b.Write([]rune("hello, this is a test\r\ndeleted")...)
b.MovePosition(-3, 0) b.MovePosition(-3, 0)
b.EraseLineAfterCursor() b.EraseLineFromCursor()
assert.Equal(t, "hello, this is a test", b.lines[0].String()) 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) { func TestEraseDisplay(t *testing.T) {
b := NewBuffer(80, 5, CellAttributes{}) 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 := NewBuffer(80, 5, CellAttributes{})
b.Write([]rune("hello\r\nasdasd\r\nthings")...) b.Write([]rune("hello\r\nasdasd\r\nthings")...)
b.MovePosition(-3, -1) b.MovePosition(-3, -1)
b.EraseDisplayAfterCursor() b.EraseDisplayFromCursor()
lines := b.GetVisibleLines() lines := b.GetVisibleLines()
assert.Equal(t, "hello", lines[0].String()) assert.Equal(t, "hello", lines[0].String())
assert.Equal(t, "asd", lines[1].String()) assert.Equal(t, "asd", lines[1].String())

View File

@ -19,8 +19,11 @@ func getConfig() config.Config {
if ignore { if ignore {
return config.DefaultConfig return config.DefaultConfig
} }
conf := loadConfigFile() conf := loadConfigFile()
flag.BoolVar(&conf.DebugMode, "debug", conf.DebugMode, "Enable debug logging") 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() flag.Parse()
return conf return conf
} }

View File

@ -1,8 +1,41 @@
package terminal package terminal
// https://www.xfree86.org/4.8.0/ctlseqs.html
// https://vt100.net/docs/vt100-ug/chapter3.html
var ansiSequenceMap = map[rune]escapeSequenceHandler{ var ansiSequenceMap = map[rune]escapeSequenceHandler{
'[': csiHandler, '[': csiHandler,
0x5d: oscHandler, 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 { func ansiHandler(buffer chan rune, terminal *Terminal) error {

View File

@ -195,7 +195,7 @@ func csiEraseInDisplayHandler(params []string, intermediate string, terminal *Te
switch n { switch n {
case "0", "": case "0", "":
terminal.buffer.EraseDisplayAfterCursor() terminal.buffer.EraseDisplayFromCursor()
case "1": case "1":
terminal.buffer.EraseDisplayToCursor() terminal.buffer.EraseDisplayToCursor()
case "2": case "2":
@ -209,6 +209,7 @@ func csiEraseInDisplayHandler(params []string, intermediate string, terminal *Te
// CSI Ps K // CSI Ps K
func csiEraseInLineHandler(params []string, intermediate string, terminal *Terminal) error { func csiEraseInLineHandler(params []string, intermediate string, terminal *Terminal) error {
n := "0" n := "0"
if len(params) > 0 { if len(params) > 0 {
n = params[0] n = params[0]
@ -216,7 +217,7 @@ func csiEraseInLineHandler(params []string, intermediate string, terminal *Termi
switch n { switch n {
case "0", "": //erase adter cursor case "0", "": //erase adter cursor
terminal.buffer.EraseLineAfterCursor() terminal.buffer.EraseLineFromCursor()
case "1": // erase to cursor inclusive case "1": // erase to cursor inclusive
terminal.buffer.EraseLineToCursor() terminal.buffer.EraseLineToCursor()
case "2": // erase entire case "2": // erase entire

View File

@ -38,7 +38,7 @@ func (terminal *Terminal) processInput(ctx context.Context, buffer chan rune) {
continue 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 { switch b {
case 0x0a: case 0x0a: