From ae1ed6c11f3ae5675c80f377dd7f3996b3d621d0 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Thu, 2 Aug 2018 16:35:13 +0200 Subject: termui: implement addComment and setTitle --- cache/cache.go | 76 ++++++++++++++----------------------------- graphql/resolvers/mutation.go | 12 +++---- input/input.go | 35 ++++++++++++++++++++ termui/bug_table.go | 39 ++++++++++++---------- termui/show_bug.go | 38 ++++++++++++++++++---- termui/termui.go | 72 ++++++++++++++++++++++++++++++++++++++-- 6 files changed, 187 insertions(+), 85 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index cbd57802..d2595723 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -15,10 +15,6 @@ type Cacher interface { ResolveRepo(ref string) (RepoCacher, error) DefaultRepo() (RepoCacher, error) - - // Shortcut to resolve on the default repo for convenience - DefaultResolveBug(id string) (BugCacher, error) - DefaultResolveBugPrefix(prefix string) (BugCacher, error) } type RepoCacher interface { @@ -37,13 +33,13 @@ type BugCacher interface { ClearSnapshot() // Mutations - AddComment(message string) (BugCacher, error) - ChangeLabels(added []string, removed []string) (BugCacher, error) - Open() (BugCacher, error) - Close() (BugCacher, error) - SetTitle(title string) (BugCacher, error) + AddComment(message string) error + ChangeLabels(added []string, removed []string) error + Open() error + Close() error + SetTitle(title string) error - Commit() (BugCacher, error) + Commit() error } // Cacher ------------------------ @@ -86,26 +82,6 @@ func (c *RootCache) ResolveRepo(ref string) (RepoCacher, error) { return r, nil } -func (c *RootCache) DefaultResolveBug(id string) (BugCacher, error) { - repo, err := c.DefaultRepo() - - if err != nil { - return nil, err - } - - return repo.ResolveBug(id) -} - -func (c *RootCache) DefaultResolveBugPrefix(prefix string) (BugCacher, error) { - repo, err := c.DefaultRepo() - - if err != nil { - return nil, err - } - - return repo.ResolveBugPrefix(prefix) -} - // Repo ------------------------ type RepoCache struct { @@ -231,10 +207,10 @@ func (c *BugCache) ClearSnapshot() { c.snap = nil } -func (c *BugCache) AddComment(message string) (BugCacher, error) { +func (c *BugCache) AddComment(message string) error { author, err := bug.GetUser(c.repo) if err != nil { - return nil, err + return err } operations.Comment(c.bug, author, message) @@ -242,30 +218,30 @@ func (c *BugCache) AddComment(message string) (BugCacher, error) { // TODO: perf --> the snapshot could simply be updated with the new op c.ClearSnapshot() - return c, nil + return nil } -func (c *BugCache) ChangeLabels(added []string, removed []string) (BugCacher, error) { +func (c *BugCache) ChangeLabels(added []string, removed []string) error { author, err := bug.GetUser(c.repo) if err != nil { - return nil, err + return err } err = operations.ChangeLabels(nil, c.bug, author, added, removed) if err != nil { - return nil, err + return err } // TODO: perf --> the snapshot could simply be updated with the new op c.ClearSnapshot() - return c, nil + return nil } -func (c *BugCache) Open() (BugCacher, error) { +func (c *BugCache) Open() error { author, err := bug.GetUser(c.repo) if err != nil { - return nil, err + return err } operations.Open(c.bug, author) @@ -273,13 +249,13 @@ func (c *BugCache) Open() (BugCacher, error) { // TODO: perf --> the snapshot could simply be updated with the new op c.ClearSnapshot() - return c, nil + return nil } -func (c *BugCache) Close() (BugCacher, error) { +func (c *BugCache) Close() error { author, err := bug.GetUser(c.repo) if err != nil { - return nil, err + return err } operations.Close(c.bug, author) @@ -287,13 +263,13 @@ func (c *BugCache) Close() (BugCacher, error) { // TODO: perf --> the snapshot could simply be updated with the new op c.ClearSnapshot() - return c, nil + return nil } -func (c *BugCache) SetTitle(title string) (BugCacher, error) { +func (c *BugCache) SetTitle(title string) error { author, err := bug.GetUser(c.repo) if err != nil { - return nil, err + return err } operations.SetTitle(c.bug, author, title) @@ -301,13 +277,9 @@ func (c *BugCache) SetTitle(title string) (BugCacher, error) { // TODO: perf --> the snapshot could simply be updated with the new op c.ClearSnapshot() - return c, nil + return nil } -func (c *BugCache) Commit() (BugCacher, error) { - err := c.bug.Commit(c.repo) - if err != nil { - return nil, err - } - return c, nil +func (c *BugCache) Commit() error { + return c.bug.Commit(c.repo) } diff --git a/graphql/resolvers/mutation.go b/graphql/resolvers/mutation.go index 2960368a..a85459f2 100644 --- a/graphql/resolvers/mutation.go +++ b/graphql/resolvers/mutation.go @@ -46,7 +46,7 @@ func (r mutationResolver) Commit(ctx context.Context, repoRef *string, prefix st return bug.Snapshot{}, err } - b, err = b.Commit() + err = b.Commit() if err != nil { return bug.Snapshot{}, err } @@ -67,7 +67,7 @@ func (r mutationResolver) AddComment(ctx context.Context, repoRef *string, prefi return bug.Snapshot{}, err } - b, err = b.AddComment(message) + err = b.AddComment(message) if err != nil { return bug.Snapshot{}, err } @@ -88,7 +88,7 @@ func (r mutationResolver) ChangeLabels(ctx context.Context, repoRef *string, pre return bug.Snapshot{}, err } - b, err = b.ChangeLabels(added, removed) + err = b.ChangeLabels(added, removed) if err != nil { return bug.Snapshot{}, err } @@ -109,7 +109,7 @@ func (r mutationResolver) Open(ctx context.Context, repoRef *string, prefix stri return bug.Snapshot{}, err } - b, err = b.Open() + err = b.Open() if err != nil { return bug.Snapshot{}, err } @@ -130,7 +130,7 @@ func (r mutationResolver) Close(ctx context.Context, repoRef *string, prefix str return bug.Snapshot{}, err } - b, err = b.Close() + err = b.Close() if err != nil { return bug.Snapshot{}, err } @@ -151,7 +151,7 @@ func (r mutationResolver) SetTitle(ctx context.Context, repoRef *string, prefix return bug.Snapshot{}, err } - b, err = b.SetTitle(title) + err = b.SetTitle(title) if err != nil { return bug.Snapshot{}, err } diff --git a/input/input.go b/input/input.go index 300348af..530106e2 100644 --- a/input/input.go +++ b/input/input.go @@ -102,6 +102,41 @@ func BugCommentEditorInput(repo repository.Repo) (string, error) { return message, nil } +const bugTitleTemplate = ` + +# Please enter the new title. Only one line will used. +# Lines starting with '#' will be ignored, and an empty title aborts the operation. +` + +func BugTitleEditorInput(repo repository.Repo) (string, error) { + raw, err := LaunchEditorWithTemplate(repo, messageFilename, bugTitleTemplate) + + if err != nil { + return "", err + } + + lines := strings.Split(raw, "\n") + + var title string + for _, line := range lines { + if strings.HasPrefix(line, "#") { + continue + } + trimmed := strings.TrimSpace(line) + if trimmed == "" { + continue + } + title = trimmed + break + } + + if title == "" { + return "", ErrEmptyTitle + } + + return title, nil +} + func LaunchEditorWithTemplate(repo repository.Repo, fileName string, template string) (string, error) { path := fmt.Sprintf("%s/.git/%s", repo.GetPath(), fileName) diff --git a/termui/bug_table.go b/termui/bug_table.go index ae67c289..55b451af 100644 --- a/termui/bug_table.go +++ b/termui/bug_table.go @@ -14,16 +14,16 @@ const bugTableFooterView = "bugTableFooterView" const bugTableInstructionView = "bugTableInstructionView" type bugTable struct { - cache cache.RepoCacher + repo cache.RepoCacher allIds []string - bugs []*bug.Snapshot + bugs []cache.BugCacher pageCursor int selectCursor int } func newBugTable(cache cache.RepoCacher) *bugTable { return &bugTable{ - cache: cache, + repo: cache, pageCursor: 0, selectCursor: 0, } @@ -165,7 +165,7 @@ func (bt *bugTable) keybindings(g *gocui.Gui) error { // New bug if err := g.SetKeybinding(bugTableView, 'n', gocui.ModNone, - newBugWithEditor); err != nil { + bt.newBug); err != nil { return err } @@ -195,7 +195,7 @@ func (bt *bugTable) disable(g *gocui.Gui) error { } func (bt *bugTable) paginate(max int) error { - allIds, err := bt.cache.AllBugIds() + allIds, err := bt.repo.AllBugIds() if err != nil { return err } @@ -213,22 +213,22 @@ func (bt *bugTable) doPaginate(allIds []string, max int) error { nb := minInt(len(allIds)-bt.pageCursor, max) if nb < 0 { - bt.bugs = []*bug.Snapshot{} + bt.bugs = []cache.BugCacher{} return nil } // slice the data ids := allIds[bt.pageCursor : bt.pageCursor+nb] - bt.bugs = make([]*bug.Snapshot, len(ids)) + bt.bugs = make([]cache.BugCacher, len(ids)) for i, id := range ids { - b, err := bt.cache.ResolveBug(id) + b, err := bt.repo.ResolveBug(id) if err != nil { return err } - bt.bugs[i] = b.Snapshot() + bt.bugs[i] = b } return nil @@ -259,16 +259,17 @@ func (bt *bugTable) render(v *gocui.View, maxX int) { for _, b := range bt.bugs { person := bug.Person{} - if len(b.Comments) > 0 { - create := b.Comments[0] + snap := b.Snapshot() + if len(snap.Comments) > 0 { + create := snap.Comments[0] person = create.Author } - id := util.LeftPaddedString(b.HumanId(), columnWidths["id"], 2) - status := util.LeftPaddedString(b.Status.String(), columnWidths["status"], 2) - title := util.LeftPaddedString(b.Title, columnWidths["title"], 2) + id := util.LeftPaddedString(snap.HumanId(), columnWidths["id"], 2) + status := util.LeftPaddedString(snap.Status.String(), columnWidths["status"], 2) + title := util.LeftPaddedString(snap.Title, columnWidths["title"], 2) author := util.LeftPaddedString(person.Name, columnWidths["author"], 2) - summary := util.LeftPaddedString(b.Summary(), columnWidths["summary"], 2) + summary := util.LeftPaddedString(snap.Summary(), columnWidths["summary"], 2) fmt.Fprintf(v, "%s %s %s %s %s\n", id, status, title, author, summary) } @@ -330,7 +331,7 @@ func (bt *bugTable) cursorClamp(v *gocui.View) error { func (bt *bugTable) nextPage(g *gocui.Gui, v *gocui.View) error { _, max := v.Size() - allIds, err := bt.cache.AllBugIds() + allIds, err := bt.repo.AllBugIds() if err != nil { return err } @@ -348,7 +349,7 @@ func (bt *bugTable) nextPage(g *gocui.Gui, v *gocui.View) error { func (bt *bugTable) previousPage(g *gocui.Gui, v *gocui.View) error { _, max := v.Size() - allIds, err := bt.cache.AllBugIds() + allIds, err := bt.repo.AllBugIds() if err != nil { return err } @@ -360,6 +361,10 @@ func (bt *bugTable) previousPage(g *gocui.Gui, v *gocui.View) error { return bt.doPaginate(allIds, max) } +func (bt *bugTable) newBug(g *gocui.Gui, v *gocui.View) error { + return newBugWithEditor(bt.repo) +} + func (bt *bugTable) openBug(g *gocui.Gui, v *gocui.View) error { _, y := v.Cursor() ui.showBug.bug = bt.bugs[bt.pageCursor+y] diff --git a/termui/show_bug.go b/termui/show_bug.go index 9d1f5b76..34c7a922 100644 --- a/termui/show_bug.go +++ b/termui/show_bug.go @@ -2,7 +2,6 @@ package termui import ( "fmt" - "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/bug/operations" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/util" @@ -17,7 +16,7 @@ const timeLayout = "Jan _2 2006" type showBug struct { cache cache.RepoCacher - bug *bug.Snapshot + bug cache.BugCacher } func newShowBug(cache cache.RepoCacher) *showBug { @@ -65,7 +64,7 @@ func (sb *showBug) layout(g *gocui.Gui) error { v.Frame = false v.BgColor = gocui.ColorBlue - fmt.Fprintf(v, "[q] Return") + fmt.Fprintf(v, "[q] Return [c] Add comment [t] Change title") } _, err = g.SetCurrentView(showBugView) @@ -78,6 +77,7 @@ func (sb *showBug) keybindings(g *gocui.Gui) error { return err } + // Scrolling if err := g.SetKeybinding(showBugView, gocui.KeyPgup, gocui.ModNone, sb.scrollUp); err != nil { return err @@ -87,6 +87,20 @@ func (sb *showBug) keybindings(g *gocui.Gui) error { return err } + // Comment + if err := g.SetKeybinding(showBugView, 'c', gocui.ModNone, + sb.comment); err != nil { + return err + } + + // Title + if err := g.SetKeybinding(showBugView, 't', gocui.ModNone, + sb.title); err != nil { + return err + } + + // Labels + return nil } @@ -105,15 +119,16 @@ func (sb *showBug) disable(g *gocui.Gui) error { func (sb *showBug) renderMain(v *gocui.View) { maxX, _ := v.Size() + snap := sb.bug.Snapshot() - header1 := fmt.Sprintf("[%s] %s", sb.bug.HumanId(), sb.bug.Title) + header1 := fmt.Sprintf("[%s] %s", snap.HumanId(), snap.Title) fmt.Fprintf(v, util.LeftPaddedString(header1, maxX, 2)+"\n\n") header2 := fmt.Sprintf("[%s] %s opened this bug on %s", - sb.bug.Status, sb.bug.Author.Name, sb.bug.CreatedAt.Format(timeLayout)) + snap.Status, snap.Author.Name, snap.CreatedAt.Format(timeLayout)) fmt.Fprintf(v, util.LeftPaddedString(header2, maxX, 2)+"\n\n") - for _, op := range sb.bug.Operations { + for _, op := range snap.Operations { switch op.(type) { case operations.CreateOperation: @@ -133,11 +148,12 @@ func (sb *showBug) renderMain(v *gocui.View) { func (sb *showBug) renderSidebar(v *gocui.View) { maxX, _ := v.Size() + snap := sb.bug.Snapshot() title := util.LeftPaddedString("LABEL", maxX, 2) fmt.Fprintf(v, title+"\n\n") - for _, label := range sb.bug.Labels { + for _, label := range snap.Labels { fmt.Fprintf(v, util.LeftPaddedString(label.String(), maxX, 2)) fmt.Fprintln(v) } @@ -156,3 +172,11 @@ func (sb *showBug) scrollUp(g *gocui.Gui, v *gocui.View) error { func (sb *showBug) scrollDown(g *gocui.Gui, v *gocui.View) error { return nil } + +func (sb *showBug) comment(g *gocui.Gui, v *gocui.View) error { + return addCommentWithEditor(sb.bug) +} + +func (sb *showBug) title(g *gocui.Gui, v *gocui.View) error { + return setTitleWithEditor(sb.bug) +} diff --git a/termui/termui.go b/termui/termui.go index a5da4bda..2e109c3f 100644 --- a/termui/termui.go +++ b/termui/termui.go @@ -136,9 +136,9 @@ func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } -func newBugWithEditor(g *gocui.Gui, v *gocui.View) error { +func newBugWithEditor(repo cache.RepoCacher) error { // This is somewhat hacky. - // As there is no way to pause gocui, run the editor, restart gocui, + // As there is no way to pause gocui, run the editor and 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 @@ -158,7 +158,73 @@ func newBugWithEditor(g *gocui.Gui, v *gocui.View) error { if err == input.ErrEmptyTitle { ui.errorPopup.activate("Empty title, aborting.") } else { - _, err = ui.cache.NewBug(title, message) + _, err := repo.NewBug(title, message) + if err != nil { + return err + } + } + + initGui() + + return errTerminateMainloop +} + +func addCommentWithEditor(bug cache.BugCacher) error { + // This is somewhat hacky. + // As there is no way to pause gocui, run the editor and 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. + + ui.g.Close() + ui.g = nil + + message, err := input.BugCommentEditorInput(ui.cache.Repository()) + + if err != nil && err != input.ErrEmptyMessage { + return err + } + + if err == input.ErrEmptyMessage { + ui.errorPopup.activate("Empty message, aborting.") + } else { + err := bug.AddComment(message) + if err != nil { + return err + } + } + + initGui() + + return errTerminateMainloop +} + +func setTitleWithEditor(bug cache.BugCacher) error { + // This is somewhat hacky. + // As there is no way to pause gocui, run the editor and 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. + + ui.g.Close() + ui.g = nil + + title, err := input.BugTitleEditorInput(ui.cache.Repository()) + + if err != nil && err != input.ErrEmptyTitle { + return err + } + + if err == input.ErrEmptyTitle { + ui.errorPopup.activate("Empty title, aborting.") + } else { + err := bug.SetTitle(title) if err != nil { return err } -- cgit