Johan Rong il y a 1 mois
Parent
commit
1f108932dd
7 fichiers modifiés avec 174 ajouts et 35 suppressions
  1. 3 1
      go.mod
  2. 8 0
      go.sum
  3. 42 2
      internal/buffer/buffer.go
  4. 23 5
      internal/buffer/cursor.go
  5. 66 18
      internal/tui/model.go
  6. 31 8
      internal/tui/update.go
  7. 1 1
      main.go

+ 3 - 1
go.mod

@@ -5,6 +5,8 @@ go 1.26.4
 require charm.land/bubbletea/v2 v2.0.8
 
 require (
+	charm.land/bubbles/v2 v2.1.1 // indirect
+	charm.land/lipgloss/v2 v2.0.4 // indirect
 	github.com/charmbracelet/colorprofile v0.4.3 // indirect
 	github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7 // indirect
 	github.com/charmbracelet/x/ansi v0.11.7 // indirect
@@ -15,7 +17,7 @@ require (
 	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/mattn/go-runewidth v0.0.23 // indirect
+	github.com/mattn/go-runewidth v0.0.24 // indirect
 	github.com/muesli/cancelreader v0.2.2 // indirect
 	github.com/rivo/uniseg v0.4.7 // indirect
 	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect

+ 8 - 0
go.sum

@@ -1,7 +1,12 @@
+charm.land/bubbles/v2 v2.1.1 h1:7r55WzBxpo/R3z98hGmY7KKPd3ET6vsf0Fb9sDHOV60=
+charm.land/bubbles/v2 v2.1.1/go.mod h1:GE6M31gaWZVXzGw73OeuTTgy4lX+OtkH0E5ymnNsHxo=
 charm.land/bubbletea/v2 v2.0.8 h1:SxTJMhCAI3lbPmy4SgX5LWZ24AdINr4I6UEqzZvYJuY=
 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/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/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/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7 h1:3FmWoGNWK4STvqg0O0Aeav2T7rodWJAPeF0QpH+8gFw=
@@ -10,6 +15,7 @@ github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dA
 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/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
 github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
 github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
@@ -26,6 +32,8 @@ github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW
 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/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=

+ 42 - 2
internal/buffer/buffer.go

@@ -1,10 +1,50 @@
 package buffer
 
 import (
+	"fmt"
+	"strconv"
     "github.com/gboncoffee/gopiecetable"
 )
 
-type Document struct {
+type Buffer struct {
 	Table *gopiecetable.PieceTable[rune]
 	CM CursorManager
-}
+}
+
+func (buf Buffer) Insert(content rune) {
+	for i := range buf.CM.Cursors {
+		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))
+		}
+
+		cur.Offset++
+		buf.CM.DeduplicateAndSort()
+	}
+}
+
+func (buf *Buffer) Delete() {
+	for i := range buf.CM.Cursors {
+		cur := &buf.CM.Cursors[i]
+		
+		if cur.Offset > 0 {
+			cur.Offset--
+		}
+
+		err := buf.Table.Delete(cur.Offset)
+		if err != nil {
+		}
+
+		buf.CM.DeduplicateAndSort()
+	}
+}
+
+func (buf *Buffer) Undo() {
+    num, _ := buf.Table.Undo()
+    for i := range buf.CM.Cursors {
+        buf.CM.Cursors[i].Offset = num
+    }
+
+    buf.CM.DeduplicateAndSort()
+}

+ 23 - 5
internal/buffer/cursor.go

@@ -1,8 +1,8 @@
 package buffer
 
 import (
-	"os"
-	"fmt"
+	"slices"
+	"cmp"
 )
 
 type Cursor struct {
@@ -20,6 +20,24 @@ func (cm *CursorManager) AddCursor(cur Cursor) {
 }
 
 func (cm *CursorManager) DeduplicateAndSort() {
-    fmt.Printf("moose: todo: implement DeduplicateAndSort()")
-	os.Exit(1)
-}
+	if len(cm.Cursors) <= 1 {
+		return
+	}
+
+	primaryCursor := cm.Cursors[cm.PrimaryIdx]
+
+	slices.SortFunc(cm.Cursors, func(a, b Cursor) int {
+		return cmp.Compare(a.Offset, b.Offset)
+	})
+
+	cm.Cursors = slices.CompactFunc(cm.Cursors, func(a, b Cursor) bool {
+		return a.Offset == b.Offset
+	})
+
+	for i, cur := range cm.Cursors {
+		if cur == primaryCursor {
+			cm.PrimaryIdx = i
+			break
+		}
+	}
+}

+ 66 - 18
internal/tui/model.go

@@ -3,32 +3,78 @@ package tui
 import (
     "moose/internal/buffer"
 
-	"github.com/gboncoffee/gopiecetable"
-	tea "charm.land/bubbletea/v2"
+    "github.com/gboncoffee/gopiecetable"
+    tea "charm.land/bubbletea/v2"
+    "charm.land/bubbles/v2/key"
+    "charm.land/bubbles/v2/viewport"
 )
 
+type Keymap = struct {
+	left, right, up, down, tab, backtab, newline, delete, undo, redo, quit key.Binding
+}
+
+func (m *EditorModel) updateKeybindings() {
+}
+
 type EditorModel struct {
-    Document buffer.Document
+    Buffer buffer.Buffer
     Viewport viewport.Model
+    Keymap Keymap
     Width int
     Height int
 }
 
 func NewEditorModel() EditorModel {
-	initialDoc := buffer.Document{
-		Table: gopiecetable.FromString("Welcome to your modular multi-cursor editor.\nPress Ctrl+A to spawn cursors.\nType freely to modify text blocks."),
-		CM: buffer.CursorManager{
-			Cursors: []buffer.Cursor{{Offset: 0}},
-			PrimaryIdx: 0,
-		},
-	}
-
-	vp := viewport.New(0, 0)
-
-	return EditorModel{
-		Document: initialDoc,
-		Viewport: vp,
-	}
+    initialBuf := buffer.Buffer{
+        Table: gopiecetable.FromString(""),
+        CM: buffer.CursorManager{
+            Cursors: []buffer.Cursor{{Offset: 0}},
+            PrimaryIdx: 0,
+        },
+    }
+
+    vp := viewport.New()
+    vp.SetContent(gopiecetable.String(initialBuf.Table))
+
+    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"),
+            ),
+        },
+    }
 }
 
 func (m EditorModel) Init() tea.Cmd {
@@ -36,5 +82,7 @@ func (m EditorModel) Init() tea.Cmd {
 }
 
 func (m EditorModel) View() tea.View {
-    return tea.NewView(gopiecetable.String(m.Document.Table))
+	v := tea.NewView(m.Viewport.View())
+	v.AltScreen = true
+    return v
 }

+ 31 - 8
internal/tui/update.go

@@ -1,7 +1,9 @@
 package tui
 
 import (
+    "github.com/gboncoffee/gopiecetable"
     tea "charm.land/bubbletea/v2"
+    "charm.land/bubbles/v2/key"
 )
 
 func (m EditorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
@@ -9,18 +11,39 @@ func (m EditorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
     case tea.WindowSizeMsg:
         m.Width = msg.Width
         m.Height = msg.Height
-        m.Viewport.Width = msg.Width
-        m.Viewport.Height = msg.Height - 2 // Space for footer status bar
+        m.Viewport.SetWidth(msg.Width)
+
+        h := msg.Height - 2
+        if h < 0 {
+            h = 0
+        }
+        m.Viewport.SetHeight(h)
         return m, nil
 
-    case tea.KeyMsg:
-        switch msg.String() {
-        case "ctrl+c":
+    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:
-            m.Document.Table.Insert(msg.String(), m.Document.Table.Size())
-            m.Viewport.SetContent(m.RenderBufferToString())
+            key := msg.Key()
+            if key.Text != "" {
+                for _, r := range key.Text {
+                    //_ = m.Buffer.Table.Insert(m.Buffer.Table.Size(), r)
+                    m.Buffer.Insert(r)
+                }
+            }
         }
+
+        m.Viewport.SetContent(gopiecetable.String(m.Buffer.Table))
     }
+
     return m, nil
-}
+}

+ 1 - 1
main.go

@@ -11,7 +11,7 @@ import (
 func main() {
     model := tui.NewEditorModel()
 
-    p := tea.NewProgram(model, tea.WithAltScreen())
+    p := tea.NewProgram(model)
     if _, err := p.Run(); err != nil {
         fmt.Printf("moose: error: %v", err)
         os.Exit(1)