1
0
Эх сурвалжийг харах

Use ropes instead of gopiecetable ++

Johan Rong 1 сар өмнө
parent
commit
de1630b9b0

+ 5 - 3
go.mod

@@ -2,10 +2,13 @@ module moose
 
 
 go 1.26.4
 go 1.26.4
 
 
-require charm.land/bubbletea/v2 v2.0.8
+require (
+	charm.land/bubbles/v2 v2.1.1
+	charm.land/bubbletea/v2 v2.0.8
+	github.com/zyedidia/rope v0.0.0-20210616205215-37fbf22eab3a
+)
 
 
 require (
 require (
-	charm.land/bubbles/v2 v2.1.1 // indirect
 	charm.land/lipgloss/v2 v2.0.4 // indirect
 	charm.land/lipgloss/v2 v2.0.4 // indirect
 	github.com/charmbracelet/colorprofile v0.4.3 // indirect
 	github.com/charmbracelet/colorprofile v0.4.3 // indirect
 	github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7 // indirect
 	github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7 // indirect
@@ -15,7 +18,6 @@ require (
 	github.com/charmbracelet/x/windows v0.2.2 // indirect
 	github.com/charmbracelet/x/windows v0.2.2 // indirect
 	github.com/clipperhouse/displaywidth v0.11.0 // indirect
 	github.com/clipperhouse/displaywidth v0.11.0 // indirect
 	github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
 	github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
-	github.com/gboncoffee/gopiecetable v1.0.0 // indirect
 	github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
 	github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
 	github.com/mattn/go-runewidth v0.0.24 // indirect
 	github.com/mattn/go-runewidth v0.0.24 // indirect
 	github.com/muesli/cancelreader v0.2.2 // indirect
 	github.com/muesli/cancelreader v0.2.2 // indirect

+ 6 - 10
go.sum

@@ -4,18 +4,16 @@ charm.land/bubbletea/v2 v2.0.8 h1:SxTJMhCAI3lbPmy4SgX5LWZ24AdINr4I6UEqzZvYJuY=
 charm.land/bubbletea/v2 v2.0.8/go.mod h1:2SkdgoTXluXJHOUwAoRlRXF/28vklb1rFl6GcgV1/ss=
 charm.land/bubbletea/v2 v2.0.8/go.mod h1:2SkdgoTXluXJHOUwAoRlRXF/28vklb1rFl6GcgV1/ss=
 charm.land/lipgloss/v2 v2.0.4 h1:lcPeVtcp23SNra7lHy8iYE4UC2aIipVQ47sbGyyxR5Q=
 charm.land/lipgloss/v2 v2.0.4 h1:lcPeVtcp23SNra7lHy8iYE4UC2aIipVQ47sbGyyxR5Q=
 charm.land/lipgloss/v2 v2.0.4/go.mod h1:0653x8epbZSzdDfO/XPS1a/uYPOBeSsCssOpJOqDzik=
 charm.land/lipgloss/v2 v2.0.4/go.mod h1:0653x8epbZSzdDfO/XPS1a/uYPOBeSsCssOpJOqDzik=
-github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
-github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
 github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
 github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
+github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
 github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
 github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
 github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
 github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
 github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7 h1:3FmWoGNWK4STvqg0O0Aeav2T7rodWJAPeF0QpH+8gFw=
 github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7 h1:3FmWoGNWK4STvqg0O0Aeav2T7rodWJAPeF0QpH+8gFw=
 github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7/go.mod h1:f/jRa757WUmaOZrbPspXymbg/GnbF+rwe4OLsG7aXYo=
 github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7/go.mod h1:f/jRa757WUmaOZrbPspXymbg/GnbF+rwe4OLsG7aXYo=
 github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
 github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
 github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
 github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
-github.com/charmbracelet/x/exp/golden v0.0.0-20241212170349-ad4b7ae0f25f h1:UytXHv0UxnsDFmL/7Z9Q5SBYPwSuRLXHbwx+6LycZ2w=
-github.com/charmbracelet/x/exp/golden v0.0.0-20241212170349-ad4b7ae0f25f/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
 github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
 github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
+github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
 github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
 github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
 github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
 github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
 github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
 github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
@@ -26,12 +24,8 @@ github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSE
 github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
 github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
 github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
 github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
 github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
 github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
-github.com/gboncoffee/gopiecetable v1.0.0 h1:24bQsimO5Cs3fTbgk7Bjn/dzgXXGKFzZi7cmJHBQZDU=
-github.com/gboncoffee/gopiecetable v1.0.0/go.mod h1:qsEeMiN4MVggYC0IF+I/addQ66+0oiFnAuwk21ibBlY=
 github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
 github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
 github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
-github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
 github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
 github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
 github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
 github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
@@ -40,8 +34,10 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
-golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
-golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
+github.com/zyedidia/rope v0.0.0-20210616205215-37fbf22eab3a h1:+VbuFCNAjzVffErUlm0TIAZClFxFZwJ1il6qtWrnIg8=
+github.com/zyedidia/rope v0.0.0-20210616205215-37fbf22eab3a/go.mod h1:IKo1js3O2gdESAUH9F3B33wlrRBnpJMJR/gXyY0a3ho=
+golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
+golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
 golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
 golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
 golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
 golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
 golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
 golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=

+ 134 - 21
internal/buffer/buffer.go

@@ -1,50 +1,163 @@
 package buffer
 package buffer
 
 
 import (
 import (
-	"fmt"
-	"strconv"
-    "github.com/gboncoffee/gopiecetable"
+	"unicode/utf8"
+
+	"github.com/zyedidia/rope"
 )
 )
 
 
 type Buffer struct {
 type Buffer struct {
-	Table *gopiecetable.PieceTable[rune]
+	Rope *rope.Node
 	CM CursorManager
 	CM CursorManager
 }
 }
 
 
-func (buf Buffer) Insert(content rune) {
+func (buf *Buffer) ensureRope() {
+	if buf.Rope == nil {
+		buf.Rope = rope.New([]byte{})
+	}
+}
+
+func (buf *Buffer) Insert(content rune) {
+	buf.ensureRope()
+	data := []byte(string(content))
+	shift := len(data)
+
 	for i := range buf.CM.Cursors {
 	for i := range buf.CM.Cursors {
 		cur := &buf.CM.Cursors[i]
 		cur := &buf.CM.Cursors[i]
-		err := buf.Table.Insert(cur.Offset, content)
-		if err != nil {
-			fmt.Println("moose: error" + err.Error() + strconv.Itoa(cur.Offset))
+		if cur.Offset < 0 {
+			cur.Offset = 0
+		}
+		if cur.Offset > buf.Rope.Len() {
+			cur.Offset = buf.Rope.Len()
 		}
 		}
 
 
-		cur.Offset++
-		buf.CM.DeduplicateAndSort()
+		buf.Rope.Insert(cur.Offset, data)
+		cur.Offset += shift
 	}
 	}
+
+	buf.CM.DeduplicateAndSort()
 }
 }
 
 
 func (buf *Buffer) Delete() {
 func (buf *Buffer) Delete() {
+	buf.ensureRope()
+
 	for i := range buf.CM.Cursors {
 	for i := range buf.CM.Cursors {
 		cur := &buf.CM.Cursors[i]
 		cur := &buf.CM.Cursors[i]
-		
-		if cur.Offset > 0 {
-			cur.Offset--
+		if cur.Offset <= 0 {
+			continue
+		}
+
+		if cur.Offset > buf.Rope.Len() {
+			cur.Offset = buf.Rope.Len()
+		}
+
+		left := buf.Rope.Slice(0, cur.Offset)
+		_, size := utf8.DecodeLastRune(left)
+		if size <= 0 {
+			size = 1
+		}
+
+		start := cur.Offset - size
+		if start < 0 {
+			start = 0
 		}
 		}
 
 
-		err := buf.Table.Delete(cur.Offset)
-		if err != nil {
+		buf.Rope.Remove(start, cur.Offset)
+		cur.Offset = start
+	}
+
+	buf.CM.DeduplicateAndSort()
+}
+
+func (buf *Buffer) MoveHoriz(dir int) {
+	buf.ensureRope()
+
+	for i := range buf.CM.Cursors {
+		cur := &buf.CM.Cursors[i]
+		if cur.Offset + dir < 0 || cur.Offset + dir > buf.Rope.Len() {
+			continue
 		}
 		}
 
 
-		buf.CM.DeduplicateAndSort()
+		cur.Offset += dir
+		_, goal := LineCol(buf.Rope, cur.Offset)
+		cur.Goal = goal
 	}
 	}
 }
 }
 
 
-func (buf *Buffer) Undo() {
-    num, _ := buf.Table.Undo()
-    for i := range buf.CM.Cursors {
-        buf.CM.Cursors[i].Offset = num
+func (buf *Buffer) MoveVert(dir int) {
+	buf.ensureRope()
+
+	for i := range buf.CM.Cursors {
+		cur := &buf.CM.Cursors[i]
+		line, _ := LineCol(buf.Rope, cur.Offset)
+
+		if line + dir < 0 || line + dir > LineCount(buf.Rope) {
+			continue
+		}
+
+		line += dir
+		cur.Offset = OffsetForLine(buf.Rope, line) + cur.Goal
+	}
+}
+
+func LineCount(r *rope.Node) int {
+    if r.Len() == 0 {
+        return 0
+    }
+
+    lines := r.Count(0, r.Len(), []byte{'\n'})
+
+    if r.At(r.Len()-1) != '\n' {
+        lines++
     }
     }
 
 
-    buf.CM.DeduplicateAndSort()
+    return lines
+}
+
+func LineCol(r *rope.Node, offset int) (line, col int) {
+    if offset < 0 {
+        offset = 0
+    }
+    if offset > r.Len() {
+        offset = r.Len()
+    }
+
+    line = r.Count(0, offset, []byte{'\n'})
+
+    lineStart := 0
+    for i := offset - 1; i >= 0; i-- {
+        if r.At(i) == '\n' {
+            lineStart = i + 1
+            break
+        }
+    }
+
+    col = offset - lineStart
+    return
+}
+
+func OffsetForLine(r *rope.Node, targetLine int) int {
+    if targetLine <= 0 {
+        return 0
+    }
+
+    line := 0
+    for i := 0; i < r.Len(); i++ {
+        if r.At(i) == '\n' {
+            line++
+            if line == targetLine {
+                return i + 1
+            }
+        }
+    }
+
+    return r.Len()
+}
+
+func (buf Buffer) String() string {
+	if buf.Rope == nil {
+		return ""
+	}
+
+	return string(buf.Rope.Value())
 }
 }

+ 1 - 0
internal/buffer/cursor.go

@@ -7,6 +7,7 @@ import (
 
 
 type Cursor struct {
 type Cursor struct {
     Offset int
     Offset int
+	Goal int
 }
 }
 
 
 type CursorManager struct {
 type CursorManager struct {

+ 55 - 65
internal/tui/model.go

@@ -1,88 +1,78 @@
 package tui
 package tui
 
 
 import (
 import (
-    "moose/internal/buffer"
+	"moose/internal/buffer"
 
 
-    "github.com/gboncoffee/gopiecetable"
-    tea "charm.land/bubbletea/v2"
-    "charm.land/bubbles/v2/key"
-    "charm.land/bubbles/v2/viewport"
+	"charm.land/bubbles/v2/key"
+	"charm.land/bubbles/v2/viewport"
+	tea "charm.land/bubbletea/v2"
 )
 )
 
 
 type Keymap = struct {
 type Keymap = struct {
-	left, right, up, down, tab, backtab, newline, delete, undo, redo, quit key.Binding
-}
-
-func (m *EditorModel) updateKeybindings() {
+	left, right, up, down, tab, backtab, newline, delete, quit key.Binding
 }
 }
 
 
 type EditorModel struct {
 type EditorModel struct {
-    Buffer buffer.Buffer
-    Viewport viewport.Model
-    Keymap Keymap
-    Width int
-    Height int
+	Buffer buffer.Buffer
+	Viewport viewport.Model
+	Keymap Keymap
+	Width int
+	Height int
 }
 }
 
 
 func NewEditorModel() EditorModel {
 func NewEditorModel() EditorModel {
-    initialBuf := buffer.Buffer{
-        Table: gopiecetable.FromString(""),
-        CM: buffer.CursorManager{
-            Cursors: []buffer.Cursor{{Offset: 0}},
-            PrimaryIdx: 0,
-        },
-    }
+	initialBuf := buffer.Buffer{
+		Rope: nil,
+		CM: buffer.CursorManager{
+			Cursors: []buffer.Cursor{{Offset: 0}},
+			PrimaryIdx: 0,
+		},
+	}
 
 
-    vp := viewport.New()
-    vp.SetContent(gopiecetable.String(initialBuf.Table))
+	vp := viewport.New()
+	vp.SetContent(initialBuf.String())
 
 
-    return EditorModel{
-        Buffer: initialBuf,
-        Viewport: vp,
-        Keymap: Keymap{
-            left: key.NewBinding(
-                key.WithKeys("left"),
-            ),
-            right: key.NewBinding(
-                key.WithKeys("right"),
-            ),
-            up: key.NewBinding(
-                key.WithKeys("up"),
-            ),
-            down: key.NewBinding(
-                key.WithKeys("down"),
-            ),
-            tab: key.NewBinding(
-                key.WithKeys("tab"),
-            ),
-            backtab: key.NewBinding(
-                key.WithKeys("shift+tab"),
-            ),
-            delete: key.NewBinding(
-                key.WithKeys("backspace"),
-            ),
-            newline: key.NewBinding(
-                key.WithKeys("enter"),
-            ),
-            undo: key.NewBinding(
-                key.WithKeys("ctrl+z"),
-            ),
-            redo: key.NewBinding(
-                key.WithKeys("ctrl+y"),
-            ),
-            quit: key.NewBinding(
-                key.WithKeys("ctrl+c"),
-            ),
-        },
-    }
+	return EditorModel{
+		Buffer:   initialBuf,
+		Viewport: vp,
+		Keymap: Keymap{
+			left: key.NewBinding(
+				key.WithKeys("left"),
+			),
+			right: key.NewBinding(
+				key.WithKeys("right"),
+			),
+			up: key.NewBinding(
+				key.WithKeys("up"),
+			),
+			down: key.NewBinding(
+				key.WithKeys("down"),
+			),
+			tab: key.NewBinding(
+				key.WithKeys("tab"),
+			),
+			backtab: key.NewBinding(
+				key.WithKeys("shift+tab"),
+			),
+			delete: key.NewBinding(
+				key.WithKeys("backspace"),
+			),
+			newline: key.NewBinding(
+				key.WithKeys("enter"),
+			),
+			quit: key.NewBinding(
+				key.WithKeys("ctrl+c"),
+			),
+		},
+	}
 }
 }
 
 
 func (m EditorModel) Init() tea.Cmd {
 func (m EditorModel) Init() tea.Cmd {
-    return nil
+	return nil
 }
 }
 
 
 func (m EditorModel) View() tea.View {
 func (m EditorModel) View() tea.View {
 	v := tea.NewView(m.Viewport.View())
 	v := tea.NewView(m.Viewport.View())
 	v.AltScreen = true
 	v.AltScreen = true
-    return v
-}
+	return v
+}

+ 42 - 39
internal/tui/update.go

@@ -1,49 +1,52 @@
 package tui
 package tui
 
 
 import (
 import (
-    "github.com/gboncoffee/gopiecetable"
-    tea "charm.land/bubbletea/v2"
-    "charm.land/bubbles/v2/key"
+	"charm.land/bubbles/v2/key"
+	tea "charm.land/bubbletea/v2"
 )
 )
 
 
 func (m EditorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 func (m EditorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
-    switch msg := msg.(type) {
-    case tea.WindowSizeMsg:
-        m.Width = msg.Width
-        m.Height = msg.Height
-        m.Viewport.SetWidth(msg.Width)
+	switch msg := msg.(type) {
+	case tea.WindowSizeMsg:
+		m.Width = msg.Width
+		m.Height = msg.Height
+		m.Viewport.SetWidth(msg.Width)
 
 
-        h := msg.Height - 2
-        if h < 0 {
-            h = 0
-        }
-        m.Viewport.SetHeight(h)
-        return m, nil
+		h := msg.Height - 2
+		if h < 0 {
+			h = 0
+		}
+		m.Viewport.SetHeight(h)
+		return m, nil
 
 
-    case tea.KeyPressMsg:
-        switch {
-        case key.Matches(msg, m.Keymap.quit):
-            return m, tea.Quit
-        case key.Matches(msg, m.Keymap.newline):
-            m.Buffer.Insert('\n')
-        case key.Matches(msg, m.Keymap.delete):
-            m.Buffer.Delete()
-        case key.Matches(msg, m.Keymap.undo):
-            m.Buffer.Undo()
-        case key.Matches(msg, m.Keymap.redo):
-            m.Buffer.Table.Redo()
-        default:
-            key := msg.Key()
-            if key.Text != "" {
-                for _, r := range key.Text {
-                    //_ = m.Buffer.Table.Insert(m.Buffer.Table.Size(), r)
-                    m.Buffer.Insert(r)
-                }
-            }
-        }
+	case tea.KeyPressMsg:
+		switch {
+		case key.Matches(msg, m.Keymap.quit):
+			return m, tea.Quit
+        case key.Matches(msg, m.Keymap.left):
+            m.Buffer.MoveHoriz(-1)
+        case key.Matches(msg, m.Keymap.right):
+            m.Buffer.MoveHoriz(1)
+        case key.Matches(msg, m.Keymap.down):
+            m.Buffer.MoveVert(1)
+        case key.Matches(msg, m.Keymap.up):
+            m.Buffer.MoveVert(-1)
+		case key.Matches(msg, m.Keymap.newline):
+			m.Buffer.Insert('\n')
+		case key.Matches(msg, m.Keymap.delete):
+			m.Buffer.Delete()
+		default:
+			key := msg.Key()
+			if key.Text != "" {
+				for _, r := range key.Text {
+					m.Buffer.Insert(r)
+				}
+			}
+		}
 
 
-        m.Viewport.SetContent(gopiecetable.String(m.Buffer.Table))
-    }
+		m.Viewport.SetContent(m.Buffer.String())
+        
+	}
 
 
-    return m, nil
-}
+	return m, nil
+}