Переглянути джерело

Upgrade moose to Go 1.27 and refresh dependencies (#2)

* Bump Go to 1.27
* Upgrade dependencies
* Adapt to clipboard v0.9 API
* Apply Go 1.27 modernizations
fred 1 тиждень тому
батько
коміт
6ae3760244
7 змінених файлів з 278 додано та 292 видалено
  1. 12 12
      go.mod
  2. 20 20
      go.sum
  3. 13 33
      internal/buffer/buffer.go
  4. 5 3
      internal/buffer/edit.go
  5. 2 2
      internal/buffer/lineindex.go
  6. 9 4
      internal/editor/action.go
  7. 217 218
      internal/layout/layout.go

+ 12 - 12
go.mod

@@ -1,27 +1,27 @@
 module moose
 
-go 1.26.4
+go 1.27
 
 require (
+	github.com/creasty/defaults v1.8.0
 	github.com/davecgh/go-spew v1.1.1
-	github.com/gdamore/tcell/v3 v3.4.0
+	github.com/gdamore/tcell/v3 v3.4.2
 	github.com/yuin/gopher-lua v1.1.2
 	github.com/zyedidia/rope v0.0.0-20210616205215-37fbf22eab3a
-	golang.design/x/clipboard v0.8.0
+	golang.design/x/clipboard v0.9.0
 )
 
 require (
 	github.com/clipperhouse/displaywidth v0.11.0 // indirect
 	github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
-	github.com/creasty/defaults v1.8.0 // indirect
-	github.com/ebitengine/purego v0.10.1 // indirect
+	github.com/ebitengine/purego v0.10.2 // indirect
 	github.com/gdamore/encoding v1.0.1 // indirect
-	github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
+	github.com/lucasb-eyer/go-colorful v1.4.1 // indirect
 	golang.design/x/x11 v0.2.0 // indirect
-	golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 // indirect
-	golang.org/x/image v0.28.0 // indirect
-	golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f // indirect
-	golang.org/x/sys v0.46.0 // indirect
-	golang.org/x/term v0.43.0 // indirect
-	golang.org/x/text v0.37.0 // indirect
+	golang.org/x/exp/shiny v0.0.0-20260820142414-ca536658362e // indirect
+	golang.org/x/image v0.45.0 // indirect
+	golang.org/x/mobile v0.0.0-20260821190718-4776eadac327 // indirect
+	golang.org/x/sys v0.47.0 // indirect
+	golang.org/x/term v0.45.0 // indirect
+	golang.org/x/text v0.41.0 // indirect
 )

+ 20 - 20
go.sum

@@ -6,31 +6,31 @@ github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYK
 github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=
-github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
+github.com/ebitengine/purego v0.10.2 h1:W809HbnvzAxgdm+aOvlSekrM16wGCdT/e76+9tS7gzE=
+github.com/ebitengine/purego v0.10.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
 github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
 github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
-github.com/gdamore/tcell/v3 v3.4.0 h1:VUym1HQZiYodA5PGQrqLxF7QwqQndcAUwQD7G7XUy5E=
-github.com/gdamore/tcell/v3 v3.4.0/go.mod h1:fjKxNiIFwbzTxDU+i+AAMz+xPOgXVaZq5tbShsKseHc=
-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/gdamore/tcell/v3 v3.4.2 h1:gGW+6z2Bz5Wl2mNwFlm9+eRmg2JQrWcKjSkL1LRfpNU=
+github.com/gdamore/tcell/v3 v3.4.2/go.mod h1:Oe5U3S3jm3NzypswDNUhe+LUnF5CoFq2b4sepD++QHo=
+github.com/lucasb-eyer/go-colorful v1.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss=
+github.com/lucasb-eyer/go-colorful v1.4.1/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yuin/gopher-lua v1.1.2 h1:yF/FjE3hD65tBbt0VXLE13HWS9h34fdzJmrWRXwobGA=
 github.com/yuin/gopher-lua v1.1.2/go.mod h1:7aRmXIWl37SqRf0koeyylBEzJ+aPt8A+mmkQ4f1ntR8=
 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.design/x/clipboard v0.8.0 h1:6VEcH28wwcSgKc+vnxHHDWiRjrakTQIAJnPUrt3aOgg=
-golang.design/x/clipboard v0.8.0/go.mod h1:s0pwrtA3Q9fgnVtGDmP5ZK/pp55cQKB23esKsjwWhWM=
+golang.design/x/clipboard v0.9.0 h1:DIN7Fur+vO3WM22WlSx7aXmYmqdvjE2OAkoPuMZ9OTA=
+golang.design/x/clipboard v0.9.0/go.mod h1:s0pwrtA3Q9fgnVtGDmP5ZK/pp55cQKB23esKsjwWhWM=
 golang.design/x/x11 v0.2.0 h1:Uiwu2guGihsJX/ZCzpoDPFz5gR/Qntm08mvoBCmRydo=
 golang.design/x/x11 v0.2.0/go.mod h1:/5q1mFkdc1rL8mvB7DsQFi6as4tIkBv4FXjcP07mrkE=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 h1:Wdx0vgH5Wgsw+lF//LJKmWOJBLWX6nprsMqnf99rYDE=
-golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8=
-golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
-golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
-golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f h1:/n+PL2HlfqeSiDCuhdBbRNlGS/g2fM4OHufalHaTVG8=
-golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f/go.mod h1:ESkJ836Z6LpG6mTVAhA48LpfW/8fNR0ifStlH2axyfg=
+golang.org/x/exp/shiny v0.0.0-20260820142414-ca536658362e h1:mgkLKe+BiqOosZJGpeE20YfAihQVaZI3s532sJcwiMY=
+golang.org/x/exp/shiny v0.0.0-20260820142414-ca536658362e/go.mod h1:6E/1vVnyETZOZ3VE5X+A7fJ3zHILeAEV+WO0ZGk3iwA=
+golang.org/x/image v0.45.0 h1:FMb1nTbH5H9vF55SriQHgFw5GnNL9Jg6L25BwXKzhB0=
+golang.org/x/image v0.45.0/go.mod h1:n62x/7RqlwXDvGsSU4u6IUTUf6KghUZ9Bt7cG/T9Fx4=
+golang.org/x/mobile v0.0.0-20260821190718-4776eadac327 h1:D/wiQ6AoTYjDtSD0HMPhU8O40NUP8EF0UmDhIYCnG4I=
+golang.org/x/mobile v0.0.0-20260821190718-4776eadac327/go.mod h1:D9q8rgXu13Q3uuM+Vuy6F/DG1WF/giTPLtqQ9on5B1M=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -46,20 +46,20 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
-golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
+golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
-golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
+golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
+golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
-golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
+golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

+ 13 - 33
internal/buffer/buffer.go

@@ -17,15 +17,15 @@ type BufferManager struct {
 }
 
 func (bm *BufferManager) Current() *Buffer {
-    if len(bm.Buffers) == 0 {
-        return nil
-    }
+	if len(bm.Buffers) == 0 {
+		return nil
+	}
 
-    if bm.CurrentIdx < 0 || bm.CurrentIdx >= len(bm.Buffers) {
-        bm.CurrentIdx = len(bm.Buffers) - 1
-    }
+	if bm.CurrentIdx < 0 || bm.CurrentIdx >= len(bm.Buffers) {
+		bm.CurrentIdx = len(bm.Buffers) - 1
+	}
 
-    return &bm.Buffers[bm.CurrentIdx]
+	return &bm.Buffers[bm.CurrentIdx]
 }
 
 // TODO: implement BufferType (BufferNormal, BufferVisual, BufferInteractive).
@@ -104,11 +104,7 @@ func (buf *Buffer) Insert(content string) {
 	for i := range buf.CM.Cursors {
 		cur := &buf.CM.Cursors[i]
 
-		pos := cur.Offset + delta
-
-		if pos < 0 {
-			pos = 0
-		}
+		pos := max(cur.Offset+delta, 0)
 		if pos > buf.Rope.Len() {
 			pos = buf.Rope.Len()
 		}
@@ -157,10 +153,7 @@ func (buf *Buffer) Delete() {
 			size = 1
 		}
 
-		start := pos - size
-		if start < 0 {
-			start = 0
-		}
+		start := max(pos-size, 0)
 
 		deleted := append([]byte{}, buf.Rope.Slice(start, pos)...)
 		edit := Edit{Offset: start, Deleted: deleted}
@@ -348,10 +341,7 @@ func (buf *Buffer) MoveVert(dir int) {
 		lineEnd := lineContentEnd(buf, targetLine)
 		lineLen := runeCount(buf.Rope, lineStart, lineEnd)
 
-		goal := cur.Goal
-		if goal > lineLen {
-			goal = lineLen
-		}
+		goal := min(cur.Goal, lineLen)
 
 		cur.Offset = OffsetForLineCol(buf, targetLine, goal)
 	}
@@ -417,11 +407,7 @@ func LineCount(buf *Buffer) int {
 }
 
 func LineCol(buf *Buffer, offset int) (line, col int) {
-	offset = normalizeOffset(buf.Rope, offset)
-
-	if offset < 0 {
-		offset = 0
-	}
+	offset = max(normalizeOffset(buf.Rope, offset), 0)
 	if offset > buf.Rope.Len() {
 		offset = buf.Rope.Len()
 	}
@@ -470,10 +456,7 @@ func OffsetForLineCol(buf *Buffer, line int, col int) int {
 
 func LineText(buf *Buffer, line int) string {
 	start := OffsetForLine(buf, line)
-	end := lineContentEnd(buf, line)
-	if end < start {
-		end = start
-	}
+	end := max(lineContentEnd(buf, line), start)
 
 	return string(buf.Rope.Slice(start, end))
 }
@@ -527,10 +510,7 @@ func prevRuneStart(r *rope.Node, offset int) int {
 		size = 1
 	}
 
-	start := offset - size
-	if start < 0 {
-		start = 0
-	}
+	start := max(offset-size, 0)
 
 	return start
 }

+ 5 - 3
internal/buffer/edit.go

@@ -1,5 +1,7 @@
 package buffer
 
+import "slices"
+
 type Edit struct {
 	Offset   int
 	Deleted  []byte
@@ -86,8 +88,8 @@ func (buf *Buffer) Undo() bool {
 	t := buf.History.done[len(buf.History.done)-1]
 	buf.History.done = buf.History.done[:len(buf.History.done)-1]
 
-	for i := len(t.Edits) - 1; i >= 0; i-- {
-		buf.apply(t.Edits[i].Invert())
+	for _, v := range slices.Backward(t.Edits) {
+		buf.apply(v.Invert())
 	}
 
 	buf.CM.Cursors = cloneCursors(t.CursorsBefore)
@@ -114,4 +116,4 @@ func (buf *Buffer) Redo() bool {
 
 	buf.History.done = append(buf.History.done, t)
 	return true
-}
+}

+ 2 - 2
internal/buffer/lineindex.go

@@ -69,7 +69,7 @@ func (li *LineIndex) InsertAt(pos int, data []byte) {
 	})
 
 	var newStarts []int
-	for j := 0; j < len(data); j++ {
+	for j := range data {
 		if data[j] == '\n' {
 			newStarts = append(newStarts, pos+j+1)
 		}
@@ -105,4 +105,4 @@ func (li *LineIndex) RemoveAt(start, end int) {
 	}
 
 	li.starts = result
-}
+}

+ 9 - 4
internal/editor/action.go

@@ -1,6 +1,7 @@
 package editor
 
 import (
+	"context"
 	"moose/internal/about"
 	"moose/internal/buffer"
 	"moose/internal/layout"
@@ -511,13 +512,17 @@ func DefaultActionManager() ActionManager {
 				Commands: []string{"p", "paste"},
 				AskArgs:  false,
 				Callback: func(m *Model, args []string) {
-					text := clipboard.Read(clipboard.FmtText)
+					text, err := clipboard.Read(context.Background(), clipboard.FmtText)
+					if err != nil {
+						m.DebugLog = "moose.error:Clipboard read failed: " + err.Error()
+						return
+					}
 
-					if string(text) != "" {
+					if s := string(text); s != "" {
 						if m.Mode == ModePalette {
-							m.BM.PaletteBuffer.Insert(string(text))
+							m.BM.PaletteBuffer.Insert(s)
 						} else {
-							m.BM.Current().Insert(string(text))
+							m.BM.Current().Insert(s)
 						}
 					}
 				},

+ 217 - 218
internal/layout/layout.go

@@ -1,5 +1,7 @@
 package layout
 
+import "slices"
+
 type LayoutManager struct {
 	Workspaces   map[int]Workspace
 	ActiveIdx    int
@@ -11,22 +13,23 @@ type Workspace struct { // TODO: this is not necessary..
 }
 
 func (w Workspace) IsEmpty() bool {
-    return !isPopulated(w.RootContainer)
+	return !isPopulated(w.RootContainer)
 }
 
 type SplitType int
+
 const (
 	SplitHorizontal SplitType = iota
 	SplitVertical
 )
 
-func (s *SplitType) SetOpposite()  SplitType{
+func (s *SplitType) SetOpposite() SplitType {
 	if *s == SplitHorizontal {
 		*s = SplitVertical
-        return SplitHorizontal
+		return SplitHorizontal
 	} else {
 		*s = SplitHorizontal
-        return SplitVertical
+		return SplitVertical
 	}
 }
 
@@ -51,7 +54,7 @@ func (Container) isContainerNode() {}
 
 func NewLayoutManager() LayoutManager {
 	return LayoutManager{
-		Workspaces: map[int]Workspace{0: NewWorkspace()},
+		Workspaces:   map[int]Workspace{0: NewWorkspace()},
 		CurrentSplit: SplitHorizontal,
 	}
 }
@@ -63,14 +66,14 @@ func NewWorkspace() Workspace {
 }
 
 func NewContainerEmpty() Container {
-    return Container{
-        Children: [2]ContainerNode{
-            nil,
-            nil,
-        },
-        Split:          SplitHorizontal,
-        ActiveChildIdx: 0,
-    }
+	return Container{
+		Children: [2]ContainerNode{
+			nil,
+			nil,
+		},
+		Split:          SplitHorizontal,
+		ActiveChildIdx: 0,
+	}
 }
 
 func (c *Container) WalkAndMutateActive(fn func(cb *ContainerBuffers) ContainerNode) {
@@ -112,237 +115,233 @@ func (lm *LayoutManager) InsertBuffer(bufferIdx int, newNode bool) {
 }
 
 func (lm *LayoutManager) CycleActiveBuffer(horisontal float32, vertical float32) {
-    if horisontal == 0 && vertical == 0 {
-        return
-    }
+	if horisontal == 0 && vertical == 0 {
+		return
+	}
 
-    ws := lm.Workspaces[lm.ActiveIdx]
+	ws := lm.Workspaces[lm.ActiveIdx]
 
-    if newRoot, moved := ws.RootContainer.NavigateContainer(horisontal, vertical); moved {
-        ws.RootContainer = newRoot
-        lm.Workspaces[lm.ActiveIdx] = ws
-    }
+	if newRoot, moved := ws.RootContainer.NavigateContainer(horisontal, vertical); moved {
+		ws.RootContainer = newRoot
+		lm.Workspaces[lm.ActiveIdx] = ws
+	}
 }
 
 func (c Container) NavigateContainer(horisontal float32, vertical float32) (Container, bool) {
-    activeChild := c.Children[c.ActiveChildIdx]
-
-    if childContainer, ok := activeChild.(Container); ok {
-        updatedChild, moved := childContainer.NavigateContainer(horisontal, vertical)
-        if moved {
-            c.Children[c.ActiveChildIdx] = updatedChild
-            return c, true
-        }
-    }
-
-    targetIdx := -1
-
-    if horisontal != 0 && c.Split == SplitHorizontal {
-        if horisontal < 0 && c.ActiveChildIdx == 1 {
-            targetIdx = 0 // Try moving Left
-        } else if horisontal > 0 && c.ActiveChildIdx == 0 {
-            targetIdx = 1 // Try moving Right
-        }
-    } else if vertical != 0 && c.Split == SplitVertical {
-        if vertical < 0 && c.ActiveChildIdx == 1 {
-            targetIdx = 0 // Try moving Up
-        } else if vertical > 0 && c.ActiveChildIdx == 0 {
-            targetIdx = 1 // Try moving Down
-        }
-    }
-
-    if targetIdx != -1 && isPopulated(c.Children[targetIdx]) {
-        c.ActiveChildIdx = targetIdx
-        return c, true
-    }
-
-    return c, false
+	activeChild := c.Children[c.ActiveChildIdx]
+
+	if childContainer, ok := activeChild.(Container); ok {
+		updatedChild, moved := childContainer.NavigateContainer(horisontal, vertical)
+		if moved {
+			c.Children[c.ActiveChildIdx] = updatedChild
+			return c, true
+		}
+	}
+
+	targetIdx := -1
+
+	if horisontal != 0 && c.Split == SplitHorizontal {
+		if horisontal < 0 && c.ActiveChildIdx == 1 {
+			targetIdx = 0 // Try moving Left
+		} else if horisontal > 0 && c.ActiveChildIdx == 0 {
+			targetIdx = 1 // Try moving Right
+		}
+	} else if vertical != 0 && c.Split == SplitVertical {
+		if vertical < 0 && c.ActiveChildIdx == 1 {
+			targetIdx = 0 // Try moving Up
+		} else if vertical > 0 && c.ActiveChildIdx == 0 {
+			targetIdx = 1 // Try moving Down
+		}
+	}
+
+	if targetIdx != -1 && isPopulated(c.Children[targetIdx]) {
+		c.ActiveChildIdx = targetIdx
+		return c, true
+	}
+
+	return c, false
 }
 
 func isPopulated(node ContainerNode) bool {
-    if node == nil {
-        return false
-    }
-
-    switch n := node.(type) {
-    case ContainerBuffers:
-        return len(n.Buffers) > 0
-    case Container:
-        return isPopulated(n.Children[0]) || isPopulated(n.Children[1])
-    default:
-        return false
-    }
+	if node == nil {
+		return false
+	}
+
+	switch n := node.(type) {
+	case ContainerBuffers:
+		return len(n.Buffers) > 0
+	case Container:
+		return isPopulated(n.Children[0]) || isPopulated(n.Children[1])
+	default:
+		return false
+	}
 }
 
 func (c Container) GetActiveBufferIdx() int {
-    switch child := c.Children[c.ActiveChildIdx].(type) {
-    case ContainerBuffers:
-        if len(child.Buffers) > 0 && child.ActiveIdx < len(child.Buffers) {
-            return child.Buffers[child.ActiveIdx]
-        }
-    case Container:
-        return child.GetActiveBufferIdx()
-    }
-    return -1
+	switch child := c.Children[c.ActiveChildIdx].(type) {
+	case ContainerBuffers:
+		if len(child.Buffers) > 0 && child.ActiveIdx < len(child.Buffers) {
+			return child.Buffers[child.ActiveIdx]
+		}
+	case Container:
+		return child.GetActiveBufferIdx()
+	}
+	return -1
 }
 
 func (lm LayoutManager) GetActiveBufferIdx() int {
-    ws, ok := lm.Workspaces[lm.ActiveIdx]
-    if !ok {
-        return -1
-    }
-    return ws.RootContainer.GetActiveBufferIdx()
+	ws, ok := lm.Workspaces[lm.ActiveIdx]
+	if !ok {
+		return -1
+	}
+	return ws.RootContainer.GetActiveBufferIdx()
 }
 
 func (c Container) RemoveActive() (ContainerNode, bool) {
-    activeChild := c.Children[c.ActiveChildIdx]
-
-    switch child := activeChild.(type) {
-    case ContainerBuffers:
-        if len(child.Buffers) == 0 {
-            return nil, true
-        }
-
-        child.Buffers = append(child.Buffers[:child.ActiveIdx], child.Buffers[child.ActiveIdx+1:]...)
-
-        if child.ActiveIdx >= len(child.Buffers) && len(child.Buffers) > 0 {
-            child.ActiveIdx = len(child.Buffers) - 1
-        }
-
-        if len(child.Buffers) == 0 {
-            siblingIdx := 1 - c.ActiveChildIdx
-            if siblingIdx < 0 || siblingIdx >= len(c.Children) || c.Children[siblingIdx] == nil {
-                return nil, true
-            }
-            return c.Children[siblingIdx], false
-        }
-
-        c.Children[c.ActiveChildIdx] = child
-        return c, false
-
-    case Container:
-        updatedChild, childIsEmpty := child.RemoveActive()
-        if childIsEmpty {
-            siblingIdx := 1 - c.ActiveChildIdx
-            if siblingIdx < 0 || siblingIdx >= len(c.Children) || c.Children[siblingIdx] == nil {
-                return nil, true
-            }
-            return c.Children[siblingIdx], false
-        }
-
-        c.Children[c.ActiveChildIdx] = updatedChild
-        return c, false
-
-    default:
-        return nil, true
-    }
+	activeChild := c.Children[c.ActiveChildIdx]
+
+	switch child := activeChild.(type) {
+	case ContainerBuffers:
+		if len(child.Buffers) == 0 {
+			return nil, true
+		}
+
+		child.Buffers = append(child.Buffers[:child.ActiveIdx], child.Buffers[child.ActiveIdx+1:]...)
+
+		if child.ActiveIdx >= len(child.Buffers) && len(child.Buffers) > 0 {
+			child.ActiveIdx = len(child.Buffers) - 1
+		}
+
+		if len(child.Buffers) == 0 {
+			siblingIdx := 1 - c.ActiveChildIdx
+			if siblingIdx < 0 || siblingIdx >= len(c.Children) || c.Children[siblingIdx] == nil {
+				return nil, true
+			}
+			return c.Children[siblingIdx], false
+		}
+
+		c.Children[c.ActiveChildIdx] = child
+		return c, false
+
+	case Container:
+		updatedChild, childIsEmpty := child.RemoveActive()
+		if childIsEmpty {
+			siblingIdx := 1 - c.ActiveChildIdx
+			if siblingIdx < 0 || siblingIdx >= len(c.Children) || c.Children[siblingIdx] == nil {
+				return nil, true
+			}
+			return c.Children[siblingIdx], false
+		}
+
+		c.Children[c.ActiveChildIdx] = updatedChild
+		return c, false
+
+	default:
+		return nil, true
+	}
 }
 
 func (c Container) ContainsBufferIdx(target int) bool {
-    switch child := c.Children[c.ActiveChildIdx].(type) {
-    case ContainerBuffers:
-        for _, idx := range child.Buffers {
-            if idx == target {
-                return true
-            }
-        }
-    case Container:
-        if child.ContainsBufferIdx(target) {
-            return true
-        }
-    }
-
-    for _, node := range c.Children {
-        switch child := node.(type) {
-        case ContainerBuffers:
-            for _, idx := range child.Buffers {
-                if idx == target {
-                    return true
-                }
-            }
-        case Container:
-            if child.ContainsBufferIdx(target) {
-                return true
-            }
-        }
-    }
-
-    return false
+	switch child := c.Children[c.ActiveChildIdx].(type) {
+	case ContainerBuffers:
+		if slices.Contains(child.Buffers, target) {
+			return true
+		}
+	case Container:
+		if child.ContainsBufferIdx(target) {
+			return true
+		}
+	}
+
+	for _, node := range c.Children {
+		switch child := node.(type) {
+		case ContainerBuffers:
+			if slices.Contains(child.Buffers, target) {
+				return true
+			}
+		case Container:
+			if child.ContainsBufferIdx(target) {
+				return true
+			}
+		}
+	}
+
+	return false
 }
 
 func (c *Container) ActivateBufferIdx(target int) bool {
-    for i, child := range c.Children {
-        switch n := child.(type) {
-        case ContainerBuffers:
-            for j, idx := range n.Buffers {
-                if idx == target {
-                    c.ActiveChildIdx = i
-                    n.ActiveIdx = j
-                    c.Children[i] = n
-                    return true
-                }
-            }
-        case Container:
-            if n.ActivateBufferIdx(target) {
-                c.Children[i] = n
-                return true
-            }
-        }
-    }
-    return false
+	for i, child := range c.Children {
+		switch n := child.(type) {
+		case ContainerBuffers:
+			for j, idx := range n.Buffers {
+				if idx == target {
+					c.ActiveChildIdx = i
+					n.ActiveIdx = j
+					c.Children[i] = n
+					return true
+				}
+			}
+		case Container:
+			if n.ActivateBufferIdx(target) {
+				c.Children[i] = n
+				return true
+			}
+		}
+	}
+	return false
 }
 
 func (lm *LayoutManager) RemoveActiveBufferAndReindex(removedIdx int) int {
-    ws := lm.Workspaces[lm.ActiveIdx]
-
-    if !ws.RootContainer.ContainsBufferIdx(removedIdx) {
-        return ws.RootContainer.GetActiveBufferIdx()
-    }
-
-    if !ws.RootContainer.ActivateBufferIdx(removedIdx) {
-        return ws.RootContainer.GetActiveBufferIdx()
-    }
-
-    newRoot, isEmpty := ws.RootContainer.RemoveActive()
-    if newRoot == nil || isEmpty {
-        ws.RootContainer = NewContainerEmpty()
-        lm.Workspaces[lm.ActiveIdx] = ws
-        return -1
-    }
-
-    switch root := newRoot.(type) {
-    case Container:
-        ws.RootContainer = root
-    case ContainerBuffers:
-        ws.RootContainer = Container{
-            Children:       [2]ContainerNode{root, nil},
-            Split:          ws.RootContainer.Split,
-            ActiveChildIdx: 0,
-        }
-    }
-
-    ws.RootContainer.DecrementIndicesAbove(removedIdx)
-    lm.Workspaces[lm.ActiveIdx] = ws
-
-    return ws.RootContainer.GetActiveBufferIdx()
+	ws := lm.Workspaces[lm.ActiveIdx]
+
+	if !ws.RootContainer.ContainsBufferIdx(removedIdx) {
+		return ws.RootContainer.GetActiveBufferIdx()
+	}
+
+	if !ws.RootContainer.ActivateBufferIdx(removedIdx) {
+		return ws.RootContainer.GetActiveBufferIdx()
+	}
+
+	newRoot, isEmpty := ws.RootContainer.RemoveActive()
+	if newRoot == nil || isEmpty {
+		ws.RootContainer = NewContainerEmpty()
+		lm.Workspaces[lm.ActiveIdx] = ws
+		return -1
+	}
+
+	switch root := newRoot.(type) {
+	case Container:
+		ws.RootContainer = root
+	case ContainerBuffers:
+		ws.RootContainer = Container{
+			Children:       [2]ContainerNode{root, nil},
+			Split:          ws.RootContainer.Split,
+			ActiveChildIdx: 0,
+		}
+	}
+
+	ws.RootContainer.DecrementIndicesAbove(removedIdx)
+	lm.Workspaces[lm.ActiveIdx] = ws
+
+	return ws.RootContainer.GetActiveBufferIdx()
 }
 
 func (c Container) DecrementIndicesAbove(threshold int) {
-    for i := range c.Children {
-        if c.Children[i] == nil {
-            continue
-        }
-        switch child := c.Children[i].(type) {
-        case ContainerBuffers:
-            for bIdx := range child.Buffers {
-                if child.Buffers[bIdx] > threshold {
-                    child.Buffers[bIdx]--
-                }
-            }
-            c.Children[i] = child
-        case Container:
-            child.DecrementIndicesAbove(threshold)
-            c.Children[i] = child
-        }
-    }
-}
+	for i := range c.Children {
+		if c.Children[i] == nil {
+			continue
+		}
+		switch child := c.Children[i].(type) {
+		case ContainerBuffers:
+			for bIdx := range child.Buffers {
+				if child.Buffers[bIdx] > threshold {
+					child.Buffers[bIdx]--
+				}
+			}
+			c.Children[i] = child
+		case Container:
+			child.DecrementIndicesAbove(threshold)
+			c.Children[i] = child
+		}
+	}
+}