@@ -12,8 +12,10 @@ const (
12
12
listSelectViewName = "listSelectViewName"
13
13
)
14
14
15
- func ListSelect (gui * gocui.Gui , title string , slice interface {}) (
16
- <- chan int , CleanUpFunc , error ,
15
+ // Displays a list created from given slice / array.
16
+ // If allowMultipleSelection is set, you can select multiple entries with space.
17
+ func ListSelect (gui * gocui.Gui , title string , slice interface {}, allowMultipleSelection bool ) (
18
+ <- chan []int , CleanUpFunc , error ,
17
19
) {
18
20
value := reflect .ValueOf (slice )
19
21
if k := value .Kind (); k != reflect .Slice && k != reflect .Array {
@@ -27,7 +29,7 @@ func ListSelect(gui *gocui.Gui, title string, slice interface{}) (
27
29
idxLen , _ := buf .WriteString (strconv .Itoa (i ))
28
30
buf .WriteByte ('.' )
29
31
strBytes := []byte (fmt .Sprint (value .Index (i ).Interface ()))
30
- if bLen := len (strBytes ) + idxLen + 1 ; bLen > listW {
32
+ if bLen := len (strBytes ) + idxLen + 2 ; bLen > listW {
31
33
listW = bLen
32
34
}
33
35
buf .Write (strBytes )
@@ -36,14 +38,16 @@ func ListSelect(gui *gocui.Gui, title string, slice interface{}) (
36
38
listW += 2
37
39
38
40
cleanUp := cleanUpFunc (gui , listSelectViewName )
39
- selectedIdx , v , err := listSelect (gui , title , listW , listH )
41
+ selectedIdxes , v , err := listSelect (gui , title , listW , listH , allowMultipleSelection )
40
42
if v != nil {
41
43
buf .WriteTo (v )
42
44
}
43
- return selectedIdx , cleanUp , err
45
+ return selectedIdxes , cleanUp , err
44
46
}
45
47
46
- func listSelect (gui * gocui.Gui , title string , listW , listH int ) (chan int , * gocui.View , error ) {
48
+ func listSelect (gui * gocui.Gui , title string , listW , listH int , multiSel bool ) (
49
+ chan []int , * gocui.View , error ,
50
+ ) {
47
51
w , h := gui .Size ()
48
52
49
53
//TODO wrap list if list is too wide (handle moving up & down correctly)
@@ -68,9 +72,10 @@ func listSelect(gui *gocui.Gui, title string, listW, listH int) (chan int, *gocu
68
72
v .Highlight = true
69
73
v .Editable = true
70
74
71
- selectedIdx := make (chan int )
75
+ selectedIdxes := make (chan [] int )
72
76
chanClosed := false
73
77
78
+ idxs := make ([]int , 0 , listH )
74
79
v .Editor = gocui .EditorFunc (func (v * gocui.View , key gocui.Key , ch rune , mod gocui.Modifier ) {
75
80
switch {
76
81
case key == gocui .KeyArrowDown || ch == 'j' :
@@ -82,23 +87,55 @@ func listSelect(gui *gocui.Gui, title string, listW, listH int) (chan int, *gocu
82
87
}
83
88
case key == gocui .KeyArrowUp || ch == 'k' :
84
89
v .MoveCursor (0 , - 1 , false )
85
- case key == gocui .KeyEnter :
86
- //TODO mutliple selection (color / highlight selected row) (buffered channels?)
90
+ case key == gocui .KeySpace :
91
+ if ! multiSel {
92
+ return
93
+ }
87
94
_ , oy := v .Origin ()
88
95
_ , y := v .Cursor ()
89
96
y += oy
97
+ idxsIdx := - 1
98
+ for i , v := range idxs {
99
+ if v == y {
100
+ idxsIdx = i
101
+ break
102
+ }
103
+ }
104
+ v .SetCursor (0 , y - oy )
105
+ if idxsIdx != - 1 {
106
+ idxs = append (idxs [:idxsIdx ], idxs [idxsIdx + 1 :]... )
107
+ v .EditDelete (false )
108
+ } else {
109
+ idxs = append (idxs , y )
110
+ v .EditWrite ('*' )
111
+ }
112
+ case key == gocui .KeyEnter :
90
113
if ! chanClosed {
91
- selectedIdx <- y
92
- close (selectedIdx )
114
+ _ , oy := v .Origin ()
115
+ _ , y := v .Cursor ()
116
+ y += oy
117
+ contains := false
118
+ for _ , v := range idxs {
119
+ if v == y {
120
+ contains = true
121
+ break
122
+ }
123
+ }
124
+ if ! contains {
125
+ idxs = append (idxs , y )
126
+ }
127
+
128
+ selectedIdxes <- idxs
129
+ close (selectedIdxes )
93
130
chanClosed = true
94
131
}
95
132
case key == gocui .KeyCtrlQ || key == gocui .KeyEsc || ch == 'q' :
96
133
if ! chanClosed {
97
- close (selectedIdx )
134
+ close (selectedIdxes )
98
135
chanClosed = true
99
136
}
100
137
}
101
138
})
102
139
103
- return selectedIdx , v , err
140
+ return selectedIdxes , v , err
104
141
}
0 commit comments