on main help sign in
chore: address review requests
4b0f84fKujtim Hoxha
internal/csync/slices.go+5−14
@@ -112,15 +112,6 @@ func (s *Slice[T]) Len() int {
112 return len(s.inner)
112 return len(s.inner)
113}
113}
114
114
115−// Slice returns a copy of the underlying slice.
116−func (s *Slice[T]) Slice() []T {
117− s.mu.RLock()
118− defer s.mu.RUnlock()
119− result := make([]T, len(s.inner))
120− copy(result, s.inner)
121− return result
122−}
123−
124// SetSlice replaces the entire slice with a new one.
115// SetSlice replaces the entire slice with a new one.
125func (s *Slice[T]) SetSlice(items []T) {
116func (s *Slice[T]) SetSlice(items []T) {
126 s.mu.Lock()
117 s.mu.Lock()
@@ -138,10 +129,8 @@ func (s *Slice[T]) Clear() {
138
129
139// Seq returns an iterator that yields elements from the slice.
130// Seq returns an iterator that yields elements from the slice.
140func (s *Slice[T]) Seq() iter.Seq[T] {
131func (s *Slice[T]) Seq() iter.Seq[T] {
141− // Take a snapshot to avoid holding the lock during iteration
142− items := s.Slice()
143 return func(yield func(T) bool) {
132 return func(yield func(T) bool) {
144− for _, v := range items {
133+ for _, v := range s.Seq2() {
145 if !yield(v) {
134 if !yield(v) {
146 return
135 return
147 }
136 }
@@ -151,8 +140,10 @@ func (s *Slice[T]) Seq() iter.Seq[T] {
151
140
152// Seq2 returns an iterator that yields index-value pairs from the slice.
141// Seq2 returns an iterator that yields index-value pairs from the slice.
153func (s *Slice[T]) Seq2() iter.Seq2[int, T] {
142func (s *Slice[T]) Seq2() iter.Seq2[int, T] {
154− // Take a snapshot to avoid holding the lock during iteration
143+ s.mu.RLock()
155− items := s.Slice()
144+ items := make([]T, len(s.inner))
145+ copy(items, s.inner)
146+ s.mu.RUnlock()
156 return func(yield func(int, T) bool) {
147 return func(yield func(int, T) bool) {
157 for i, v := range items {
148 for i, v := range items {
158 if !yield(i, v) {
149 if !yield(i, v) {
internal/csync/slices_test.go+4−3
@@ -1,6 +1,7 @@
1package csync
1package csync
2
2
3import (
3import (
4+ "slices"
4 "sync"
5 "sync"
5 "sync/atomic"
6 "sync/atomic"
6 "testing"
7 "testing"
@@ -145,7 +146,7 @@ func TestSlice(t *testing.T) {
145 assert.Equal(t, 4, s.Len())
146 assert.Equal(t, 4, s.Len())
146
147
147 expected := []int{1, 2, 4, 5}
148 expected := []int{1, 2, 4, 5}
148− actual := s.Slice()
149+ actual := slices.Collect(s.Seq())
149 assert.Equal(t, expected, actual)
150 assert.Equal(t, expected, actual)
150
151
151 // Delete out of bounds
152 // Delete out of bounds
@@ -203,7 +204,7 @@ func TestSlice(t *testing.T) {
203 s.SetSlice(newItems)
204 s.SetSlice(newItems)
204
205
205 assert.Equal(t, 3, s.Len())
206 assert.Equal(t, 3, s.Len())
206− assert.Equal(t, newItems, s.Slice())
207+ assert.Equal(t, newItems, slices.Collect(s.Seq()))
207
208
208 // Verify it's a copy
209 // Verify it's a copy
209 newItems[0] = 999
210 newItems[0] = 999
@@ -224,7 +225,7 @@ func TestSlice(t *testing.T) {
224 original := []int{1, 2, 3}
225 original := []int{1, 2, 3}
225 s := NewSliceFrom(original)
226 s := NewSliceFrom(original)
226
227
227− copy := s.Slice()
228+ copy := slices.Collect(s.Seq())
228 assert.Equal(t, original, copy)
229 assert.Equal(t, original, copy)
229
230
230 // Verify it's a copy
231 // Verify it's a copy
internal/tui/exp/list/filterable.go+1−1
@@ -97,7 +97,7 @@ func NewFilterableList[T FilterableItem](items []T, opts ...filterableListOption
97 f.list = New[T](items, f.listOptions...).(*list[T])
97 f.list = New[T](items, f.listOptions...).(*list[T])
98
98
99 f.updateKeyMaps()
99 f.updateKeyMaps()
100− f.items = f.list.items.Slice()
100+ f.items = slices.Collect(f.list.items.Seq())
101
101
102 if f.inputHidden {
102 if f.inputHidden {
103 return f
103 return f
internal/tui/exp/list/filterable_group.go+2−1
@@ -2,6 +2,7 @@ package list
2
2
3import (
3import (
4 "regexp"
4 "regexp"
5+ "slices"
5 "sort"
6 "sort"
6 "strings"
7 "strings"
7
8
@@ -179,7 +180,7 @@ func (f *filterableGroupList[T]) inputHeight() int {
179
180
180func (f *filterableGroupList[T]) Filter(query string) tea.Cmd {
181func (f *filterableGroupList[T]) Filter(query string) tea.Cmd {
181 var cmds []tea.Cmd
182 var cmds []tea.Cmd
182− for _, item := range f.items.Slice() {
183+ for _, item := range slices.Collect(f.items.Seq()) {
183 if i, ok := any(item).(layout.Focusable); ok {
184 if i, ok := any(item).(layout.Focusable); ok {
184 cmds = append(cmds, i.Blur())
185 cmds = append(cmds, i.Blur())
185 }
186 }
internal/tui/exp/list/grouped.go+3−1
@@ -1,6 +1,8 @@
1package list
1package list
2
2
3import (
3import (
4+ "slices"
5+
4 tea "github.com/charmbracelet/bubbletea/v2"
6 tea "github.com/charmbracelet/bubbletea/v2"
5 "github.com/charmbracelet/crush/internal/csync"
7 "github.com/charmbracelet/crush/internal/csync"
6 "github.com/charmbracelet/crush/internal/tui/components/core/layout"
8 "github.com/charmbracelet/crush/internal/tui/components/core/layout"
@@ -89,7 +91,7 @@ func (g *groupedList[T]) convertItems() {
89func (g *groupedList[T]) SetGroups(groups []Group[T]) tea.Cmd {
91func (g *groupedList[T]) SetGroups(groups []Group[T]) tea.Cmd {
90 g.groups = groups
92 g.groups = groups
91 g.convertItems()
93 g.convertItems()
92− return g.SetItems(g.items.Slice())
94+ return g.SetItems(slices.Collect(g.items.Seq()))
93}
95}
94
96
95func (g *groupedList[T]) Groups() []Group[T] {
97func (g *groupedList[T]) Groups() []Group[T] {
internal/tui/exp/list/list.go+11−10
@@ -1,6 +1,7 @@
1package list
1package list
2
2
3import (
3import (
4+ "slices"
4 "strings"
5 "strings"
5
6
6 "github.com/charmbracelet/bubbles/v2/key"
7 "github.com/charmbracelet/bubbles/v2/key"
@@ -201,7 +202,7 @@ func (l *list[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
201 return l, nil
202 return l, nil
202 case anim.StepMsg:
203 case anim.StepMsg:
203 var cmds []tea.Cmd
204 var cmds []tea.Cmd
204− for _, item := range l.items.Slice() {
205+ for _, item := range slices.Collect(l.items.Seq()) {
205 if i, ok := any(item).(HasAnim); ok && i.Spinning() {
206 if i, ok := any(item).(HasAnim); ok && i.Spinning() {
206 updated, cmd := i.Update(msg)
207 updated, cmd := i.Update(msg)
207 cmds = append(cmds, cmd)
208 cmds = append(cmds, cmd)
@@ -288,7 +289,7 @@ func (l *list[T]) viewPosition() (int, int) {
288
289
289func (l *list[T]) recalculateItemPositions() {
290func (l *list[T]) recalculateItemPositions() {
290 currentContentHeight := 0
291 currentContentHeight := 0
291− for _, item := range l.items.Slice() {
292+ for _, item := range slices.Collect(l.items.Seq()) {
292 rItem, ok := l.renderedItems.Get(item.ID())
293 rItem, ok := l.renderedItems.Get(item.ID())
293 if !ok {
294 if !ok {
294 continue
295 continue
@@ -561,7 +562,7 @@ func (l *list[T]) focusSelectedItem() tea.Cmd {
561 return nil
562 return nil
562 }
563 }
563 var cmds []tea.Cmd
564 var cmds []tea.Cmd
564− for _, item := range l.items.Slice() {
565+ for _, item := range slices.Collect(l.items.Seq()) {
565 if f, ok := any(item).(layout.Focusable); ok {
566 if f, ok := any(item).(layout.Focusable); ok {
566 if item.ID() == l.selectedItem && !f.IsFocused() {
567 if item.ID() == l.selectedItem && !f.IsFocused() {
567 cmds = append(cmds, f.Focus())
568 cmds = append(cmds, f.Focus())
@@ -580,7 +581,7 @@ func (l *list[T]) blurSelectedItem() tea.Cmd {
580 return nil
581 return nil
581 }
582 }
582 var cmds []tea.Cmd
583 var cmds []tea.Cmd
583− for _, item := range l.items.Slice() {
584+ for _, item := range slices.Collect(l.items.Seq()) {
584 if f, ok := any(item).(layout.Focusable); ok {
585 if f, ok := any(item).(layout.Focusable); ok {
585 if item.ID() == l.selectedItem && f.IsFocused() {
586 if item.ID() == l.selectedItem && f.IsFocused() {
586 cmds = append(cmds, f.Blur())
587 cmds = append(cmds, f.Blur())
@@ -655,7 +656,7 @@ func (l *list[T]) AppendItem(item T) tea.Cmd {
655
656
656 l.items.Append(item)
657 l.items.Append(item)
657 l.indexMap = csync.NewMap[string, int]()
658 l.indexMap = csync.NewMap[string, int]()
658− for inx, item := range l.items.Slice() {
659+ for inx, item := range slices.Collect(l.items.Seq()) {
659 l.indexMap.Set(item.ID(), inx)
660 l.indexMap.Set(item.ID(), inx)
660 }
661 }
661 if l.width > 0 && l.height > 0 {
662 if l.width > 0 && l.height > 0 {
@@ -702,7 +703,7 @@ func (l *list[T]) DeleteItem(id string) tea.Cmd {
702 }
703 }
703 l.items.Delete(inx)
704 l.items.Delete(inx)
704 l.renderedItems.Del(id)
705 l.renderedItems.Del(id)
705− for inx, item := range l.items.Slice() {
706+ for inx, item := range slices.Collect(l.items.Seq()) {
706 l.indexMap.Set(item.ID(), inx)
707 l.indexMap.Set(item.ID(), inx)
707 }
708 }
708
709
@@ -767,7 +768,7 @@ func (l *list[T]) IsFocused() bool {
767
768
768// Items implements List.
769// Items implements List.
769func (l *list[T]) Items() []T {
770func (l *list[T]) Items() []T {
770− return l.items.Slice()
771+ return slices.Collect(l.items.Seq())
771}
772}
772
773
773func (l *list[T]) incrementOffset(n int) {
774func (l *list[T]) incrementOffset(n int) {
@@ -822,7 +823,7 @@ func (l *list[T]) PrependItem(item T) tea.Cmd {
822 }
823 }
823 l.items.Prepend(item)
824 l.items.Prepend(item)
824 l.indexMap = csync.NewMap[string, int]()
825 l.indexMap = csync.NewMap[string, int]()
825− for inx, item := range l.items.Slice() {
826+ for inx, item := range slices.Collect(l.items.Seq()) {
826 l.indexMap.Set(item.ID(), inx)
827 l.indexMap.Set(item.ID(), inx)
827 }
828 }
828 if l.width > 0 && l.height > 0 {
829 if l.width > 0 && l.height > 0 {
@@ -926,7 +927,7 @@ func (l *list[T]) SelectedItem() *T {
926func (l *list[T]) SetItems(items []T) tea.Cmd {
927func (l *list[T]) SetItems(items []T) tea.Cmd {
927 l.items.SetSlice(items)
928 l.items.SetSlice(items)
928 var cmds []tea.Cmd
929 var cmds []tea.Cmd
929− for inx, item := range l.items.Slice() {
930+ for inx, item := range slices.Collect(l.items.Seq()) {
930 if i, ok := any(item).(Indexable); ok {
931 if i, ok := any(item).(Indexable); ok {
931 i.SetIndex(inx)
932 i.SetIndex(inx)
932 }
933 }
@@ -949,7 +950,7 @@ func (l *list[T]) reset(selectedItem string) tea.Cmd {
949 l.selectedItem = selectedItem
950 l.selectedItem = selectedItem
950 l.indexMap = csync.NewMap[string, int]()
951 l.indexMap = csync.NewMap[string, int]()
951 l.renderedItems = csync.NewMap[string, renderedItem]()
952 l.renderedItems = csync.NewMap[string, renderedItem]()
952− for inx, item := range l.items.Slice() {
953+ for inx, item := range slices.Collect(l.items.Seq()) {
953 l.indexMap.Set(item.ID(), inx)
954 l.indexMap.Set(item.ID(), inx)
954 if l.width > 0 && l.height > 0 {
955 if l.width > 0 && l.height > 0 {
955 cmds = append(cmds, item.SetSize(l.width, l.height))
956 cmds = append(cmds, item.SetSize(l.width, l.height))