show_bug.go

  1package termui
  2
  3import (
  4	"fmt"
  5
  6	"github.com/MichaelMure/git-bug/bug/operations"
  7	"github.com/MichaelMure/git-bug/cache"
  8	"github.com/MichaelMure/git-bug/util"
  9	"github.com/jroimartin/gocui"
 10)
 11
 12const showBugView = "showBugView"
 13const showBugSidebarView = "showBugSidebarView"
 14const showBugInstructionView = "showBugInstructionView"
 15
 16const timeLayout = "Jan _2 2006"
 17
 18type showBug struct {
 19	cache          cache.RepoCacher
 20	bug            cache.BugCacher
 21	childViews     []string
 22	selectableView []string
 23	selected       string
 24}
 25
 26func newShowBug(cache cache.RepoCacher) *showBug {
 27	return &showBug{
 28		cache: cache,
 29	}
 30}
 31
 32func (sb *showBug) layout(g *gocui.Gui) error {
 33	maxX, maxY := g.Size()
 34
 35	v, err := g.SetView(showBugView, 0, 0, maxX*2/3, maxY-2)
 36
 37	if err != nil {
 38		if err != gocui.ErrUnknownView {
 39			return err
 40		}
 41
 42		sb.childViews = append(sb.childViews, showBugView)
 43		v.Frame = false
 44	}
 45
 46	v.Clear()
 47	err = sb.renderMain(g, v)
 48	if err != nil {
 49		return err
 50	}
 51
 52	v, err = g.SetView(showBugSidebarView, maxX*2/3+1, 0, maxX-1, maxY-2)
 53
 54	if err != nil {
 55		if err != gocui.ErrUnknownView {
 56			return err
 57		}
 58
 59		sb.childViews = append(sb.childViews, showBugSidebarView)
 60		v.Frame = true
 61	}
 62
 63	v.Clear()
 64	sb.renderSidebar(v)
 65
 66	v, err = g.SetView(showBugInstructionView, -1, maxY-2, maxX, maxY)
 67
 68	if err != nil {
 69		if err != gocui.ErrUnknownView {
 70			return err
 71		}
 72
 73		sb.childViews = append(sb.childViews, showBugInstructionView)
 74		v.Frame = false
 75		v.BgColor = gocui.ColorBlue
 76
 77		fmt.Fprintf(v, "[q] Return [c] Comment [t] Change title [↓,j] Down [↑,k] Up")
 78	}
 79
 80	_, err = g.SetCurrentView(showBugView)
 81	return err
 82}
 83
 84func (sb *showBug) keybindings(g *gocui.Gui) error {
 85	// Return
 86	if err := g.SetKeybinding(showBugView, 'q', gocui.ModNone, sb.back); err != nil {
 87		return err
 88	}
 89
 90	// Scrolling
 91	if err := g.SetKeybinding(showBugView, gocui.KeyPgup, gocui.ModNone,
 92		sb.scrollUp); err != nil {
 93		return err
 94	}
 95	if err := g.SetKeybinding(showBugView, gocui.KeyPgdn, gocui.ModNone,
 96		sb.scrollDown); err != nil {
 97		return err
 98	}
 99
100	// Down
101	if err := g.SetKeybinding(showBugView, 'j', gocui.ModNone,
102		sb.selectNext); err != nil {
103		return err
104	}
105	if err := g.SetKeybinding(showBugView, gocui.KeyArrowDown, gocui.ModNone,
106		sb.selectNext); err != nil {
107		return err
108	}
109	// Up
110	if err := g.SetKeybinding(showBugView, 'k', gocui.ModNone,
111		sb.selectPrevious); err != nil {
112		return err
113	}
114	if err := g.SetKeybinding(showBugView, gocui.KeyArrowUp, gocui.ModNone,
115		sb.selectPrevious); err != nil {
116		return err
117	}
118
119	// Comment
120	if err := g.SetKeybinding(showBugView, 'c', gocui.ModNone,
121		sb.comment); err != nil {
122		return err
123	}
124
125	// Title
126	if err := g.SetKeybinding(showBugView, 't', gocui.ModNone,
127		sb.title); err != nil {
128		return err
129	}
130
131	// Labels
132
133	return nil
134}
135
136func (sb *showBug) disable(g *gocui.Gui) error {
137	for _, view := range sb.childViews {
138		if err := g.DeleteView(view); err != nil {
139			return err
140		}
141	}
142	return nil
143}
144
145func (sb *showBug) renderMain(g *gocui.Gui, mainView *gocui.View) error {
146	maxX, _ := mainView.Size()
147	x0, y0, _, _, _ := g.ViewPosition(mainView.Name())
148	snap := sb.bug.Snapshot()
149
150	v, err := g.SetView("showBugHeader", x0, y0, maxX+1, y0+4)
151
152	if err != nil {
153		if err != gocui.ErrUnknownView {
154			return err
155		}
156
157		sb.childViews = append(sb.childViews, "showBugHeader")
158		v.Frame = false
159	}
160	y0 += 4
161
162	v.Clear()
163	header1 := fmt.Sprintf("[%s] %s", snap.HumanId(), snap.Title)
164	fmt.Fprintf(v, util.LeftPaddedString(header1, maxX-1, 0)+"\n\n")
165
166	header2 := fmt.Sprintf("[%s] %s opened this bug on %s",
167		snap.Status, snap.Author.Name, snap.CreatedAt.Format(timeLayout))
168	fmt.Fprintf(v, util.LeftPaddedString(header2, maxX-1, 0))
169
170	for i, op := range snap.Operations {
171		viewName := fmt.Sprintf("op%d", i)
172
173		switch op.(type) {
174
175		case operations.CreateOperation:
176			create := op.(operations.CreateOperation)
177			content, lines := util.TextWrap(create.Message, maxX)
178
179			v, err := sb.createOpView(g, viewName, x0, y0, maxX+1, lines, true)
180			if err != nil {
181				return err
182			}
183			fmt.Fprint(v, content)
184			y0 += lines + 2
185
186		case operations.AddCommentOperation:
187			comment := op.(operations.AddCommentOperation)
188
189			header := fmt.Sprintf("%s commented on %s",
190				comment.Author.Name, comment.Time().Format(timeLayout))
191			header = util.LeftPaddedString(header, maxX, 6)
192			message, lines := util.TextWrap(comment.Message, maxX)
193
194			v, err := sb.createOpView(g, viewName, x0, y0, maxX+1, lines+2, true)
195			if err != nil {
196				return err
197			}
198			fmt.Fprint(v, header, "\n\n", message)
199			y0 += lines + 3
200		}
201	}
202
203	return nil
204}
205
206func (sb *showBug) createOpView(g *gocui.Gui, name string, x0 int, y0 int, maxX int, height int, selectable bool) (*gocui.View, error) {
207	v, err := g.SetView(name, x0, y0, maxX, y0+height+1)
208
209	if err != nil {
210		if err != gocui.ErrUnknownView {
211			return nil, err
212		}
213
214		sb.childViews = append(sb.childViews, name)
215
216		if selectable {
217			sb.selectableView = append(sb.selectableView, name)
218		}
219	}
220
221	v.Frame = sb.selected == name
222
223	v.Clear()
224
225	return v, nil
226}
227
228func (sb *showBug) renderSidebar(v *gocui.View) {
229	maxX, _ := v.Size()
230	snap := sb.bug.Snapshot()
231
232	title := util.LeftPaddedString("LABEL", maxX, 2)
233	fmt.Fprintf(v, title+"\n\n")
234
235	for _, label := range snap.Labels {
236		fmt.Fprintf(v, util.LeftPaddedString(label.String(), maxX, 2))
237		fmt.Fprintln(v)
238	}
239}
240
241func (sb *showBug) back(g *gocui.Gui, v *gocui.View) error {
242	ui.activateWindow(ui.bugTable)
243	return nil
244}
245
246func (sb *showBug) scrollUp(g *gocui.Gui, v *gocui.View) error {
247	return nil
248}
249
250func (sb *showBug) scrollDown(g *gocui.Gui, v *gocui.View) error {
251	return nil
252}
253
254func (sb *showBug) selectPrevious(g *gocui.Gui, v *gocui.View) error {
255	if len(sb.selectableView) == 0 {
256		return nil
257	}
258
259	for i, name := range sb.selectableView {
260		if name == sb.selected {
261			sb.selected = sb.selectableView[maxInt(i-1, 0)]
262			return nil
263		}
264	}
265
266	if sb.selected == "" {
267		sb.selected = sb.selectableView[0]
268	}
269
270	return nil
271}
272
273func (sb *showBug) selectNext(g *gocui.Gui, v *gocui.View) error {
274	if len(sb.selectableView) == 0 {
275		return nil
276	}
277
278	for i, name := range sb.selectableView {
279		if name == sb.selected {
280			sb.selected = sb.selectableView[minInt(i+1, len(sb.childViews)-1)]
281			return nil
282		}
283	}
284
285	if sb.selected == "" {
286		sb.selected = sb.selectableView[0]
287	}
288
289	return nil
290}
291
292func (sb *showBug) comment(g *gocui.Gui, v *gocui.View) error {
293	return addCommentWithEditor(sb.bug)
294}
295
296func (sb *showBug) title(g *gocui.Gui, v *gocui.View) error {
297	return setTitleWithEditor(sb.bug)
298}