From c2b7f94f8780f4efdd1823136c3b81368f76d221 Mon Sep 17 00:00:00 2001 From: Liam Galvin Date: Fri, 10 Aug 2018 12:53:47 +0100 Subject: [PATCH] fix resizing --- README.md | 9 +++++ buffer/buffer.go | 92 ++++++++++++++++++++++++++++++++++++++++++- buffer/buffer_test.go | 87 +++++++++++++++++++++++++++++++++++++--- buffer/line.go | 15 +++++++ 4 files changed, 196 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 77d073a..7ae0cf5 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,15 @@ Ensure you have your latest graphics card drivers installed before use. - Bullshit graphical effects - Multi platform support +## What isn't supported? + +- Suspend/Continue (^S, ^Q). This is archaic bullshit that annoys more people than it helps. Basically: + + +![Overheating](https://imgs.xkcd.com/comics/workflow.png) + + + ## Build Dependencies - Go 1.10.3+ diff --git a/buffer/buffer.go b/buffer/buffer.go index 9c841d3..601f904 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -270,7 +270,7 @@ func (buffer *Buffer) Clear() { func (buffer *Buffer) getCurrentLine() *Line { if buffer.cursorY >= buffer.ViewHeight() { - panic(fmt.Sprintf("cursor is outside of view: %d %d", buffer.cursorX, buffer.cursorY)) + panic(fmt.Sprintf("cursor is outside of view: y=%d h=%d", buffer.cursorY, buffer.viewHeight)) } if len(buffer.lines) < int(buffer.ViewHeight()) { @@ -377,8 +377,96 @@ func (buffer *Buffer) EraseDisplayToCursor() { func (buffer *Buffer) ResizeView(width uint16, height uint16) { defer buffer.emitDisplayChange() + + if buffer.viewHeight == 0 { + buffer.viewWidth = width + buffer.viewHeight = height + return + } + + // @todo scroll to bottom on resize + line := buffer.getCurrentLine() + cXFromEndOfLine := len(line.cells) - int(buffer.cursorX+1) + + if width < buffer.viewWidth { // wrap lines if we're shrinking + for i := 0; i < len(buffer.lines); i++ { + line := &buffer.lines[i] + //line.Cleanse() + if len(line.cells) > int(width) { // only try wrapping a line if it's too long + sillyCells := line.cells[width:] // grab the cells we need to wrap + line.cells = line.cells[:width] + + // we need to move cut cells to the next line + // if the next line is wrapped anyway, we can push them onto the beginning of that line + // otherwise, we need add a new wrapped line + if i+1 < len(buffer.lines) { + nextLine := &buffer.lines[i+1] + if nextLine.wrapped { + nextLine.cells = append(sillyCells, nextLine.cells...) + continue + } + } + + newLine := newLine() + newLine.setWrapped(true) + newLine.cells = sillyCells + after := append([]Line{newLine}, buffer.lines[i+1:]...) + buffer.lines = append(buffer.lines[:i+1], after...) + + } + } + } else if width > buffer.viewWidth { // unwrap lines if we're growing + for i := 0; i < len(buffer.lines)-1; i++ { + line := &buffer.lines[i] + //line.Cleanse() + for offset := 1; i+offset < len(buffer.lines); offset++ { + nextLine := &buffer.lines[i+offset] + //nextLine.Cleanse() + if !nextLine.wrapped { // if the next line wasn't wrapped, we don't need to move characters back to this line + break + } + spaceOnLine := int(width) - len(line.cells) + if spaceOnLine <= 0 { // no more space to unwrap + break + } + moveCount := spaceOnLine + if moveCount > len(nextLine.cells) { + moveCount = len(nextLine.cells) + } + line.cells = append(line.cells, nextLine.cells[:moveCount]...) + if moveCount == len(nextLine.cells) { + // if we unwrapped all cells off the next line, delete it + buffer.lines = append(buffer.lines[:i+offset], buffer.lines[i+offset+1:]...) + + offset-- + + } else { + // otherwise just remove the characters we moved up a line + nextLine.cells = nextLine.cells[moveCount:] + } + } + + } + } + + // @todo handle vertical resize? + + if buffer.Height() < int(buffer.viewHeight) { + // we might need to move back up if the buffer is now smaller + if int(buffer.cursorY) < buffer.Height()-1 { + buffer.cursorY = uint16(buffer.Height() - 1) + } else { + buffer.cursorY = uint16(buffer.Height() - 1) + } + } else { + buffer.cursorY = buffer.viewHeight - 1 + } + buffer.viewWidth = width buffer.viewHeight = height - // @todo wrap/unwrap + // position cursorX + line = buffer.getCurrentLine() + buffer.cursorX = uint16((len(line.cells) - cXFromEndOfLine) - 1) + } diff --git a/buffer/buffer_test.go b/buffer/buffer_test.go index 4bf8163..35f38bf 100644 --- a/buffer/buffer_test.go +++ b/buffer/buffer_test.go @@ -1,6 +1,7 @@ package buffer import ( + "strings" "testing" "time" @@ -268,11 +269,6 @@ func TestCarriageReturnOnLineThatDoesntExist(t *testing.T) { assert.Equal(t, uint16(3), b.cursorY) } -func TestResizeView(t *testing.T) { - b := NewBuffer(80, 20, CellAttributes{}) - b.ResizeView(40, 10) -} - func TestGetCell(t *testing.T) { b := NewBuffer(80, 20, CellAttributes{}) b.Write([]rune("Hello\r\nthere\r\nsomething...")...) @@ -430,3 +426,84 @@ func TestBackspaceWithWrap(t *testing.T) { assert.Equal(t, "hello\x00\x00\x00\x00\x00", lines[0].String()) assert.Equal(t, "\x00\x00\x00\x00\x00", lines[1].String()) } + +func TestHorizontalResizeView(t *testing.T) { + b := NewBuffer(80, 10, CellAttributes{}) + + // 60 characters + b.Write([]rune( + `hellohellohellohellohellohellohellohellohellohellohellohello +goodbyegoodbye`)...) + + require.Equal(t, uint16(14), b.cursorX) + require.Equal(t, uint16(1), b.cursorY) + + b.ResizeView(40, 10) + + expected := `hellohellohellohellohellohellohellohello +hellohellohellohello +goodbyegoodbye` + + require.Equal(t, uint16(14), b.cursorX) + require.Equal(t, uint16(2), b.cursorY) + + lines := b.GetVisibleLines() + strs := []string{} + for _, l := range lines { + strs = append(strs, l.String()) + } + require.Equal(t, expected, strings.Join(strs, "\n")) + + b.ResizeView(20, 10) + + expected = `hellohellohellohello +hellohellohellohello +hellohellohellohello +goodbyegoodbye` + + lines = b.GetVisibleLines() + strs = []string{} + for _, l := range lines { + strs = append(strs, l.String()) + } + require.Equal(t, expected, strings.Join(strs, "\n")) + + b.ResizeView(10, 10) + + expected = `hellohello +hellohello +hellohello +hellohello +hellohello +hellohello +goodbyegoo +dbye` + + lines = b.GetVisibleLines() + strs = []string{} + for _, l := range lines { + strs = append(strs, l.String()) + } + require.Equal(t, expected, strings.Join(strs, "\n")) + + b.ResizeView(80, 20) + + expected = `hellohellohellohellohellohellohellohellohellohellohellohello +goodbyegoodbye` + + lines = b.GetVisibleLines() + strs = []string{} + for _, l := range lines { + strs = append(strs, l.String()) + } + require.Equal(t, expected, strings.Join(strs, "\n")) + + require.Equal(t, uint16(1), b.cursorY) + require.Equal(t, uint16(14), b.cursorX) +} + +/* +hellohellohellohellohellohellohellohellohellohellohellohello +goodbyegoo +dbye +*/ diff --git a/buffer/line.go b/buffer/line.go index 2284645..dacefd4 100644 --- a/buffer/line.go +++ b/buffer/line.go @@ -12,6 +12,21 @@ func newLine() Line { } } +// Cleanse removes null bytes from the end of the row +func (line *Line) Cleanse() { + cut := 0 + for i := len(line.cells) - 1; i >= 0; i-- { + if line.cells[i].r != 0 { + break + } + cut++ + } + if cut == 0 { + return + } + line.cells = line.cells[:len(line.cells)-cut] +} + func (line *Line) setWrapped(wrapped bool) { line.wrapped = wrapped }