mirror of https://github.com/liamg/aminal.git
ess
This commit is contained in:
parent
132ce51f78
commit
9ac2e9cba0
|
@ -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 {
|
||||||
|
|
|
@ -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())
|
||||||
|
|
3
main.go
3
main.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue