diff options
author | Michael Muré <batolettre@gmail.com> | 2018-07-31 16:43:43 +0200 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2018-07-31 16:44:23 +0200 |
commit | 87669e0f18f282854d340a676834b939e34e5ed3 (patch) | |
tree | c78eaa155d2939d6647ab814c6710d0d3ed69a6e /termui | |
parent | eb39c5c29bc0e9b5e15a940a1b71bdac688b6535 (diff) | |
download | git-bug-87669e0f18f282854d340a676834b939e34e5ed3.tar.gz |
termui: use the editor to create a new bug
Diffstat (limited to 'termui')
-rw-r--r-- | termui/bug_table.go | 257 | ||||
-rw-r--r-- | termui/termui.go | 153 |
2 files changed, 251 insertions, 159 deletions
diff --git a/termui/bug_table.go b/termui/bug_table.go index 89f43c87..264dff2d 100644 --- a/termui/bug_table.go +++ b/termui/bug_table.go @@ -8,6 +8,8 @@ import ( "github.com/jroimartin/gocui" ) +const bugTableView = "bugTableView" + type bugTable struct { cache cache.RepoCacher allIds []string @@ -22,71 +24,6 @@ func newBugTable(cache cache.RepoCacher) *bugTable { } } -func (bt *bugTable) paginate(max int) error { - allIds, err := bt.cache.AllBugIds() - if err != nil { - return err - } - - bt.allIds = allIds - - return bt.doPaginate(allIds, max) -} - -func (bt *bugTable) nextPage(max int) error { - allIds, err := bt.cache.AllBugIds() - if err != nil { - return err - } - - bt.allIds = allIds - - if bt.cursor+max >= len(allIds) { - return nil - } - - bt.cursor += max - - return bt.doPaginate(allIds, max) -} - -func (bt *bugTable) previousPage(max int) error { - allIds, err := bt.cache.AllBugIds() - if err != nil { - return err - } - - bt.allIds = allIds - - bt.cursor = maxInt(0, bt.cursor-max) - - return bt.doPaginate(allIds, max) -} - -func (bt *bugTable) doPaginate(allIds []string, max int) error { - // clamp the cursor - bt.cursor = maxInt(bt.cursor, 0) - bt.cursor = minInt(bt.cursor, len(allIds)-1) - - nb := minInt(len(allIds)-bt.cursor, max) - - // slice the data - ids := allIds[bt.cursor : bt.cursor+nb] - - bt.bugs = make([]*bug.Snapshot, len(ids)) - - for i, id := range ids { - b, err := bt.cache.ResolveBug(id) - if err != nil { - return err - } - - bt.bugs[i] = b.Snapshot() - } - - return nil -} - func (bt *bugTable) layout(g *gocui.Gui) error { maxX, maxY := g.Size() @@ -101,9 +38,9 @@ func (bt *bugTable) layout(g *gocui.Gui) error { } v.Clear() - ui.bugTable.renderHeader(v, maxX) + bt.renderHeader(v, maxX) - v, err = g.SetView("bugTable", -1, 1, maxX, maxY-2) + v, err = g.SetView(bugTableView, -1, 1, maxX, maxY-2) if err != nil { if err != gocui.ErrUnknownView { @@ -115,21 +52,26 @@ func (bt *bugTable) layout(g *gocui.Gui) error { v.SelBgColor = gocui.ColorWhite v.SelFgColor = gocui.ColorBlack - _, err = g.SetCurrentView("bugTable") + _, err = g.SetCurrentView(bugTableView) if err != nil { return err } } - _, tableHeight := v.Size() - err = bt.paginate(tableHeight) + _, viewHeight := v.Size() + err = bt.paginate(viewHeight - 1) + if err != nil { + return err + } + + err = bt.cursorClamp(v) if err != nil { return err } v.Clear() - ui.bugTable.render(v, maxX) + bt.render(v, maxX) v, err = g.SetView("footer", -1, maxY-3, maxX, maxY) @@ -142,7 +84,7 @@ func (bt *bugTable) layout(g *gocui.Gui) error { } v.Clear() - ui.bugTable.renderFooter(v, maxX) + bt.renderFooter(v, maxX) v, err = g.SetView("instructions", -1, maxY-2, maxX, maxY) @@ -154,7 +96,103 @@ func (bt *bugTable) layout(g *gocui.Gui) error { v.Frame = false v.BgColor = gocui.ColorBlue - fmt.Fprintf(v, "[q] Quit [h] Previous page [j] Down [k] Up [l] Next page [enter] Open bug") + fmt.Fprintf(v, "[q] Quit [←,h] Previous page [↓,j] Down [↑,k] Up [→,l] Next page [enter] Open bug [n] New bug") + } + + return nil +} + +func (bt *bugTable) keybindings(g *gocui.Gui) error { + // Quit + if err := g.SetKeybinding(bugTableView, 'q', gocui.ModNone, quit); err != nil { + return err + } + + // Down + if err := g.SetKeybinding(bugTableView, 'j', gocui.ModNone, + bt.cursorDown); err != nil { + return err + } + if err := g.SetKeybinding(bugTableView, gocui.KeyArrowDown, gocui.ModNone, + bt.cursorDown); err != nil { + return err + } + // Up + if err := g.SetKeybinding(bugTableView, 'k', gocui.ModNone, + bt.cursorUp); err != nil { + return err + } + if err := g.SetKeybinding(bugTableView, gocui.KeyArrowUp, gocui.ModNone, + bt.cursorUp); err != nil { + return err + } + + // Previous page + if err := g.SetKeybinding(bugTableView, 'h', gocui.ModNone, + bt.previousPage); err != nil { + return err + } + if err := g.SetKeybinding(bugTableView, gocui.KeyArrowLeft, gocui.ModNone, + bt.previousPage); err != nil { + return err + } + if err := g.SetKeybinding(bugTableView, gocui.KeyPgup, gocui.ModNone, + bt.previousPage); err != nil { + return err + } + // Next page + if err := g.SetKeybinding(bugTableView, 'l', gocui.ModNone, + bt.nextPage); err != nil { + return err + } + if err := g.SetKeybinding(bugTableView, gocui.KeyArrowRight, gocui.ModNone, + bt.nextPage); err != nil { + return err + } + if err := g.SetKeybinding(bugTableView, gocui.KeyPgdn, gocui.ModNone, + bt.nextPage); err != nil { + return err + } + + // New bug + if err := g.SetKeybinding(bugTableView, 'n', gocui.ModNone, + newBugWithEditor); err != nil { + return err + } + + return nil +} + +func (bt *bugTable) paginate(max int) error { + allIds, err := bt.cache.AllBugIds() + if err != nil { + return err + } + + bt.allIds = allIds + + return bt.doPaginate(allIds, max) +} + +func (bt *bugTable) doPaginate(allIds []string, max int) error { + // clamp the cursor + bt.cursor = maxInt(bt.cursor, 0) + bt.cursor = minInt(bt.cursor, len(allIds)-1) + + nb := minInt(len(allIds)-bt.cursor, max) + + // slice the data + ids := allIds[bt.cursor : bt.cursor+nb] + + bt.bugs = make([]*bug.Snapshot, len(ids)) + + for i, id := range ids { + b, err := bt.cache.ResolveBug(id) + if err != nil { + return err + } + + bt.bugs[i] = b.Snapshot() } return nil @@ -218,16 +256,73 @@ func (bt *bugTable) renderFooter(v *gocui.View, maxX int) { fmt.Fprintf(v, "Showing %d of %d bugs", len(bt.bugs), len(bt.allIds)) } -func maxInt(a, b int) int { - if a > b { - return a +func (bt *bugTable) cursorDown(g *gocui.Gui, v *gocui.View) error { + _, y := v.Cursor() + y = minInt(y+1, bt.getTableLength()-1) + + err := v.SetCursor(0, y) + if err != nil { + return err } - return b + + return nil +} + +func (bt *bugTable) cursorUp(g *gocui.Gui, v *gocui.View) error { + _, y := v.Cursor() + y = maxInt(y-1, 0) + + err := v.SetCursor(0, y) + if err != nil { + return err + } + + return nil } -func minInt(a, b int) int { - if a > b { - return b +func (bt *bugTable) cursorClamp(v *gocui.View) error { + _, y := v.Cursor() + + y = minInt(y, bt.getTableLength()-1) + y = maxInt(y, 0) + + err := v.SetCursor(0, y) + if err != nil { + return err + } + + return nil +} + +func (bt *bugTable) nextPage(g *gocui.Gui, v *gocui.View) error { + _, max := v.Size() + + allIds, err := bt.cache.AllBugIds() + if err != nil { + return err } - return a + + bt.allIds = allIds + + if bt.cursor+max >= len(allIds) { + return nil + } + + bt.cursor += max + + return bt.doPaginate(allIds, max) +} + +func (bt *bugTable) previousPage(g *gocui.Gui, v *gocui.View) error { + _, max := v.Size() + allIds, err := bt.cache.AllBugIds() + if err != nil { + return err + } + + bt.allIds = allIds + + bt.cursor = maxInt(0, bt.cursor-max) + + return bt.doPaginate(allIds, max) } diff --git a/termui/termui.go b/termui/termui.go index e2e5ae24..9ac82fd3 100644 --- a/termui/termui.go +++ b/termui/termui.go @@ -2,104 +2,103 @@ package termui import ( "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/input" "github.com/MichaelMure/git-bug/repository" "github.com/jroimartin/gocui" + "github.com/pkg/errors" ) +var errTerminateMainloop = errors.New("terminate gocui mainloop") + type termUI struct { - cache cache.RepoCacher + g *gocui.Gui + gError chan error + cache cache.RepoCacher + activeWindow window + bugTable *bugTable } var ui *termUI +type window interface { + keybindings(g *gocui.Gui) error + layout(g *gocui.Gui) error +} + func Run(repo repository.Repo) error { c := cache.NewRepoCache(repo) ui = &termUI{ + gError: make(chan error, 1), cache: c, bugTable: newBugTable(c), } + ui.activeWindow = ui.bugTable + + initGui() + + err := <-ui.gError + + if err != nil && err != gocui.ErrQuit { + return err + } + + return nil +} + +func initGui() { g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { - return err + ui.gError <- err + return } - defer g.Close() + ui.g = g - g.SetManagerFunc(layout) + ui.g.SetManagerFunc(layout) - err = keybindings(g) + err = keybindings(ui.g) if err != nil { - return err + ui.g.Close() + ui.gError <- err + return } err = g.MainLoop() - if err != nil && err != gocui.ErrQuit { - return err + if err != nil && err != errTerminateMainloop { + ui.g.Close() + ui.gError <- err } - return nil + return } func layout(g *gocui.Gui) error { //maxX, maxY := g.Size() - ui.bugTable.layout(g) + g.Cursor = false - v, err := g.View("bugTable") - if err != nil { + if err := ui.activeWindow.layout(g); err != nil { return err } - cursorClamp(v) - return nil } func keybindings(g *gocui.Gui) error { - if err := g.SetKeybinding("", 'q', gocui.ModNone, quit); err != nil { - return err - } - if err := g.SetKeybinding("bugTable", 'j', gocui.ModNone, cursorDown); err != nil { - return err - } - if err := g.SetKeybinding("bugTable", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil { - return err - } - if err := g.SetKeybinding("bugTable", 'k', gocui.ModNone, cursorUp); err != nil { - return err - } - if err := g.SetKeybinding("bugTable", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil { + // Quit + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { return err } - if err := g.SetKeybinding("bugTable", 'h', gocui.ModNone, previousPage); err != nil { + if err := ui.bugTable.keybindings(g); err != nil { return err } - if err := g.SetKeybinding("bugTable", gocui.KeyArrowLeft, gocui.ModNone, previousPage); err != nil { - return err - } - if err := g.SetKeybinding("bugTable", gocui.KeyPgup, gocui.ModNone, previousPage); err != nil { - return err - } - if err := g.SetKeybinding("bugTable", 'l', gocui.ModNone, nextPage); err != nil { - return err - } - if err := g.SetKeybinding("bugTable", gocui.KeyArrowRight, gocui.ModNone, nextPage); err != nil { - return err - } - if err := g.SetKeybinding("bugTable", gocui.KeyPgdn, gocui.ModNone, nextPage); err != nil { - return err - } - - //err = g.SetKeybinding("bugTable", 'p', gocui.ModNone, playSelected) - //err = g.SetKeybinding("bugTable", gocui.KeyEnter, gocui.ModNone, playSelectedAndExit) - //err = g.SetKeybinding("bugTable", 'm', gocui.ModNone, loadNextRecords) return nil } @@ -108,50 +107,48 @@ func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } -func cursorDown(g *gocui.Gui, v *gocui.View) error { - _, y := v.Cursor() - y = minInt(y+1, ui.bugTable.getTableLength()-1) - - err := v.SetCursor(0, y) - if err != nil { - return err - } +func newBugWithEditor(g *gocui.Gui, v *gocui.View) error { + // This is somewhat hacky. + // As there is no way to pause gocui, run the editor, restart gocui, + // we have to stop it entirely and start a new one later. + // + // - an error channel is used to route the returned error of this new + // instance into the original launch function + // - a custom error (errTerminateMainloop) is used to terminate the original + // instance's mainLoop. This error is then filtered. - return nil -} + ui.g.Close() -func cursorUp(g *gocui.Gui, v *gocui.View) error { - _, y := v.Cursor() - y = maxInt(y-1, 0) + title, message, err := input.BugCreateEditorInput(ui.cache.Repository(), "", "") - err := v.SetCursor(0, y) + if err == input.ErrEmptyTitle { + // TODO: display proper error + return err + } if err != nil { return err } - return nil -} - -func cursorClamp(v *gocui.View) error { - _, y := v.Cursor() - - y = minInt(y, ui.bugTable.getTableLength()-1) - y = maxInt(y, 0) - - err := v.SetCursor(0, y) + _, err = ui.cache.NewBug(title, message) if err != nil { return err } - return nil + initGui() + + return errTerminateMainloop } -func nextPage(g *gocui.Gui, v *gocui.View) error { - _, maxY := v.Size() - return ui.bugTable.nextPage(maxY) +func maxInt(a, b int) int { + if a > b { + return a + } + return b } -func previousPage(g *gocui.Gui, v *gocui.View) error { - _, maxY := v.Size() - return ui.bugTable.previousPage(maxY) +func minInt(a, b int) int { + if a > b { + return b + } + return a } |