1package termui
2
3import (
4 "fmt"
5 "github.com/MichaelMure/git-bug/bug"
6 "github.com/MichaelMure/git-bug/cache"
7 "github.com/MichaelMure/git-bug/util"
8 "github.com/jroimartin/gocui"
9)
10
11type bugTable struct {
12 cache cache.RepoCacher
13 allIds []string
14 bugs []*bug.Snapshot
15 cursor int
16}
17
18func newBugTable(cache cache.RepoCacher) *bugTable {
19 return &bugTable{
20 cache: cache,
21 cursor: 0,
22 }
23}
24
25func (bt *bugTable) paginate(max int) error {
26 allIds, err := bt.cache.AllBugIds()
27 if err != nil {
28 return err
29 }
30
31 bt.allIds = allIds
32
33 return bt.doPaginate(allIds, max)
34}
35
36func (bt *bugTable) nextPage(max int) error {
37 allIds, err := bt.cache.AllBugIds()
38 if err != nil {
39 return err
40 }
41
42 bt.allIds = allIds
43
44 if bt.cursor+max >= len(allIds) {
45 return nil
46 }
47
48 bt.cursor += max
49
50 return bt.doPaginate(allIds, max)
51}
52
53func (bt *bugTable) previousPage(max int) error {
54 allIds, err := bt.cache.AllBugIds()
55 if err != nil {
56 return err
57 }
58
59 bt.allIds = allIds
60
61 bt.cursor = maxInt(0, bt.cursor-max)
62
63 return bt.doPaginate(allIds, max)
64}
65
66func (bt *bugTable) doPaginate(allIds []string, max int) error {
67 // clamp the cursor
68 bt.cursor = maxInt(bt.cursor, 0)
69 bt.cursor = minInt(bt.cursor, len(allIds)-1)
70
71 nb := minInt(len(allIds)-bt.cursor, max)
72
73 // slice the data
74 ids := allIds[bt.cursor : bt.cursor+nb]
75
76 bt.bugs = make([]*bug.Snapshot, len(ids))
77
78 for i, id := range ids {
79 b, err := bt.cache.ResolveBug(id)
80 if err != nil {
81 return err
82 }
83
84 bt.bugs[i] = b.Snapshot()
85 }
86
87 return nil
88}
89
90func (bt *bugTable) layout(g *gocui.Gui) error {
91 maxX, maxY := g.Size()
92
93 v, err := g.SetView("header", -1, -1, maxX, 3)
94
95 if err != nil {
96 if err != gocui.ErrUnknownView {
97 return err
98 }
99
100 v.Frame = false
101 }
102
103 v.Clear()
104 ui.bugTable.renderHeader(v, maxX)
105
106 v, err = g.SetView("bugTable", -1, 1, maxX, maxY-2)
107
108 if err != nil {
109 if err != gocui.ErrUnknownView {
110 return err
111 }
112
113 v.Frame = false
114 v.Highlight = true
115 v.SelBgColor = gocui.ColorWhite
116 v.SelFgColor = gocui.ColorBlack
117
118 _, err = g.SetCurrentView("bugTable")
119
120 if err != nil {
121 return err
122 }
123 }
124
125 _, tableHeight := v.Size()
126 err = bt.paginate(tableHeight)
127 if err != nil {
128 return err
129 }
130
131 v.Clear()
132 ui.bugTable.render(v, maxX)
133
134 v, err = g.SetView("footer", -1, maxY-3, maxX, maxY)
135
136 if err != nil {
137 if err != gocui.ErrUnknownView {
138 return err
139 }
140
141 v.Frame = false
142 }
143
144 v.Clear()
145 ui.bugTable.renderFooter(v, maxX)
146
147 v, err = g.SetView("instructions", -1, maxY-2, maxX, maxY)
148
149 if err != nil {
150 if err != gocui.ErrUnknownView {
151 return err
152 }
153
154 v.Frame = false
155 v.BgColor = gocui.ColorBlue
156
157 fmt.Fprintf(v, "[q] Quit [h] Previous page [j] Down [k] Up [l] Next page [enter] Open bug")
158 }
159
160 return nil
161}
162
163func (bt *bugTable) getTableLength() int {
164 return len(bt.bugs)
165}
166
167func (bt *bugTable) getColumnWidths(maxX int) map[string]int {
168 m := make(map[string]int)
169 m["id"] = 10
170 m["status"] = 8
171
172 left := maxX - m["id"] - m["status"]
173
174 m["summary"] = maxInt(30, left/3)
175 left -= m["summary"]
176
177 m["author"] = maxInt(left*2/5, 15)
178 m["title"] = maxInt(left-m["author"], 10)
179
180 return m
181}
182
183func (bt *bugTable) render(v *gocui.View, maxX int) {
184 columnWidths := bt.getColumnWidths(maxX)
185
186 for _, b := range bt.bugs {
187 person := bug.Person{}
188 if len(b.Comments) > 0 {
189 create := b.Comments[0]
190 person = create.Author
191 }
192
193 id := util.LeftPaddedString(b.HumanId(), columnWidths["id"], 2)
194 status := util.LeftPaddedString(b.Status.String(), columnWidths["status"], 2)
195 title := util.LeftPaddedString(b.Title, columnWidths["title"], 2)
196 author := util.LeftPaddedString(person.Name, columnWidths["author"], 2)
197 summary := util.LeftPaddedString(b.Summary(), columnWidths["summary"], 2)
198
199 fmt.Fprintf(v, "%s %s %s %s %s\n", id, status, title, author, summary)
200 }
201}
202
203func (bt *bugTable) renderHeader(v *gocui.View, maxX int) {
204 columnWidths := bt.getColumnWidths(maxX)
205
206 id := util.LeftPaddedString("ID", columnWidths["id"], 2)
207 status := util.LeftPaddedString("STATUS", columnWidths["status"], 2)
208 title := util.LeftPaddedString("TITLE", columnWidths["title"], 2)
209 author := util.LeftPaddedString("AUTHOR", columnWidths["author"], 2)
210 summary := util.LeftPaddedString("SUMMARY", columnWidths["summary"], 2)
211
212 fmt.Fprintf(v, "\n")
213 fmt.Fprintf(v, "%s %s %s %s %s\n", id, status, title, author, summary)
214
215}
216
217func (bt *bugTable) renderFooter(v *gocui.View, maxX int) {
218 fmt.Fprintf(v, "Showing %d of %d bugs", len(bt.bugs), len(bt.allIds))
219}
220
221func maxInt(a, b int) int {
222 if a > b {
223 return a
224 }
225 return b
226}
227
228func minInt(a, b int) int {
229 if a > b {
230 return b
231 }
232 return a
233}