diff options
-rw-r--r-- | bridge/github/export.go | 2 | ||||
-rw-r--r-- | bridge/gitlab/export.go | 2 | ||||
-rw-r--r-- | bridge/jira/export.go | 2 | ||||
-rw-r--r-- | bug/op_create.go | 2 | ||||
-rw-r--r-- | bug/op_create_test.go | 2 | ||||
-rw-r--r-- | bug/operation.go | 14 | ||||
-rw-r--r-- | bug/snapshot.go | 13 | ||||
-rw-r--r-- | cache/bug_excerpt.go | 21 | ||||
-rw-r--r-- | cache/repo_cache.go | 27 | ||||
-rw-r--r-- | commands/json_common.go | 55 | ||||
-rw-r--r-- | commands/ls.go | 152 | ||||
-rw-r--r-- | commands/show.go | 198 | ||||
-rw-r--r-- | commands/user_ls.go | 47 | ||||
-rw-r--r-- | doc/man/git-bug-ls.1 | 2 | ||||
-rw-r--r-- | doc/man/git-bug-show.1 | 8 | ||||
-rw-r--r-- | doc/man/git-bug-user-ls.1 | 4 | ||||
-rw-r--r-- | doc/md/git-bug_ls.md | 2 | ||||
-rw-r--r-- | doc/md/git-bug_show.md | 5 | ||||
-rw-r--r-- | doc/md/git-bug_user_ls.md | 3 | ||||
-rw-r--r-- | graphql/models/lazy_bug.go | 8 | ||||
-rw-r--r-- | misc/bash_completion/git-bug | 9 | ||||
-rw-r--r-- | misc/powershell_completion/git-bug | 13 | ||||
-rw-r--r-- | misc/zsh_completion/git-bug | 8 | ||||
-rw-r--r-- | termui/bug_table.go | 3 | ||||
-rw-r--r-- | termui/show_bug.go | 2 |
25 files changed, 457 insertions, 147 deletions
diff --git a/bridge/github/export.go b/bridge/github/export.go index b939d878..57b52ee0 100644 --- a/bridge/github/export.go +++ b/bridge/github/export.go @@ -173,7 +173,7 @@ func (ge *githubExporter) ExportAll(ctx context.Context, repo *cache.RepoCache, // ignore issues created before since date // TODO: compare the Lamport time instead of using the unix time - if snapshot.CreatedAt.Before(since) { + if snapshot.CreateTime.Before(since) { out <- core.NewExportNothing(b.Id(), "bug created before the since date") continue } diff --git a/bridge/gitlab/export.go b/bridge/gitlab/export.go index 918e6b5e..f32ec0e3 100644 --- a/bridge/gitlab/export.go +++ b/bridge/gitlab/export.go @@ -131,7 +131,7 @@ func (ge *gitlabExporter) ExportAll(ctx context.Context, repo *cache.RepoCache, // ignore issues created before since date // TODO: compare the Lamport time instead of using the unix time - if snapshot.CreatedAt.Before(since) { + if snapshot.CreateTime.Before(since) { out <- core.NewExportNothing(b.Id(), "bug created before the since date") continue } diff --git a/bridge/jira/export.go b/bridge/jira/export.go index 37066263..e6167966 100644 --- a/bridge/jira/export.go +++ b/bridge/jira/export.go @@ -165,7 +165,7 @@ func (je *jiraExporter) ExportAll(ctx context.Context, repo *cache.RepoCache, si // ignore issues whose last modification date is before the query date // TODO: compare the Lamport time instead of using the unix time - if snapshot.CreatedAt.Before(since) { + if snapshot.CreateTime.Before(since) { out <- core.NewExportNothing(b.Id(), "bug created before the since date") continue } diff --git a/bug/op_create.go b/bug/op_create.go index b2af438b..d63d6d2f 100644 --- a/bug/op_create.go +++ b/bug/op_create.go @@ -48,7 +48,7 @@ func (op *CreateOperation) Apply(snapshot *Snapshot) { snapshot.Comments = []Comment{comment} snapshot.Author = op.Author - snapshot.CreatedAt = op.Time() + snapshot.CreateTime = op.Time() snapshot.Timeline = []TimelineItem{ &CreateTimelineItem{ diff --git a/bug/op_create_test.go b/bug/op_create_test.go index ec53b04b..ad9a30fd 100644 --- a/bug/op_create_test.go +++ b/bug/op_create_test.go @@ -38,7 +38,7 @@ func TestCreate(t *testing.T) { Author: rene, Participants: []identity.Interface{rene}, Actors: []identity.Interface{rene}, - CreatedAt: create.Time(), + CreateTime: create.Time(), Timeline: []TimelineItem{ &CreateTimelineItem{ CommentTimelineItem: NewCommentTimelineItem(id, comment), diff --git a/bug/operation.go b/bug/operation.go index 20d44f6c..91df4ef2 100644 --- a/bug/operation.go +++ b/bug/operation.go @@ -36,8 +36,6 @@ type Operation interface { Id() entity.Id // Time return the time when the operation was added Time() time.Time - // GetUnixTime return the unix timestamp when the operation was added - GetUnixTime() int64 // GetFiles return the files needed by this operation GetFiles() []git.Hash // Apply the operation to a Snapshot to create the final state @@ -89,8 +87,9 @@ func idOperation(op Operation) entity.Id { type OpBase struct { OperationType OperationType `json:"type"` Author identity.Interface `json:"author"` - UnixTime int64 `json:"timestamp"` - Metadata map[string]string `json:"metadata,omitempty"` + // TODO: part of the data model upgrade, this should eventually be a timestamp + lamport + UnixTime int64 `json:"timestamp"` + Metadata map[string]string `json:"metadata,omitempty"` // Not serialized. Store the op's id in memory. id entity.Id // Not serialized. Store the extra metadata in memory, @@ -142,11 +141,6 @@ func (op *OpBase) Time() time.Time { return time.Unix(op.UnixTime, 0) } -// GetUnixTime return the unix timestamp when the operation was added -func (op *OpBase) GetUnixTime() int64 { - return op.UnixTime -} - // GetFiles return the files needed by this operation func (op *OpBase) GetFiles() []git.Hash { return nil @@ -158,7 +152,7 @@ func opBaseValidate(op Operation, opType OperationType) error { return fmt.Errorf("incorrect operation type (expected: %v, actual: %v)", opType, op.base().OperationType) } - if op.GetUnixTime() == 0 { + if op.Time().Unix() == 0 { return fmt.Errorf("time not set") } diff --git a/bug/snapshot.go b/bug/snapshot.go index 39366c6d..11df04b2 100644 --- a/bug/snapshot.go +++ b/bug/snapshot.go @@ -19,7 +19,7 @@ type Snapshot struct { Author identity.Interface Actors []identity.Interface Participants []identity.Interface - CreatedAt time.Time + CreateTime time.Time Timeline []TimelineItem @@ -32,7 +32,7 @@ func (snap *Snapshot) Id() entity.Id { } // Return the last time a bug was modified -func (snap *Snapshot) LastEditTime() time.Time { +func (snap *Snapshot) EditTime() time.Time { if len(snap.Operations) == 0 { return time.Unix(0, 0) } @@ -40,15 +40,6 @@ func (snap *Snapshot) LastEditTime() time.Time { return snap.Operations[len(snap.Operations)-1].Time() } -// Return the last timestamp a bug was modified -func (snap *Snapshot) LastEditUnix() int64 { - if len(snap.Operations) == 0 { - return 0 - } - - return snap.Operations[len(snap.Operations)-1].GetUnixTime() -} - // GetCreateMetadata return the creation metadata func (snap *Snapshot) GetCreateMetadata(key string) (string, bool) { return snap.Operations[0].GetMetadata(key) diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go index 36c7dcfe..e9d5863f 100644 --- a/cache/bug_excerpt.go +++ b/cache/bug_excerpt.go @@ -3,6 +3,7 @@ package cache import ( "encoding/gob" "fmt" + "time" "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/entity" @@ -22,8 +23,8 @@ type BugExcerpt struct { CreateLamportTime lamport.Time EditLamportTime lamport.Time - CreateUnixTime int64 - EditUnixTime int64 + createUnixTime int64 + editUnixTime int64 Status bug.Status Labels []bug.Label @@ -79,8 +80,8 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { Id: b.Id(), CreateLamportTime: b.CreateLamportTime(), EditLamportTime: b.EditLamportTime(), - CreateUnixTime: b.FirstOp().GetUnixTime(), - EditUnixTime: snap.LastEditUnix(), + createUnixTime: b.FirstOp().Time().Unix(), + editUnixTime: snap.EditTime().Unix(), Status: snap.Status, Labels: snap.Labels, Actors: actorsIds, @@ -105,6 +106,14 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt { return e } +func (b *BugExcerpt) CreateTime() time.Time { + return time.Unix(b.createUnixTime, 0) +} + +func (b *BugExcerpt) EditTime() time.Time { + return time.Unix(b.editUnixTime, 0) +} + /* * Sorting */ @@ -144,7 +153,7 @@ func (b BugsByCreationTime) Less(i, j int) bool { // by the first sorting using the logical clock. That means that if users // synchronize their bugs regularly, the timestamp will rarely be used, and // should still provide a kinda accurate sorting when needed. - return b[i].CreateUnixTime < b[j].CreateUnixTime + return b[i].createUnixTime < b[j].createUnixTime } func (b BugsByCreationTime) Swap(i, j int) { @@ -172,7 +181,7 @@ func (b BugsByEditTime) Less(i, j int) bool { // by the first sorting using the logical clock. That means that if users // synchronize their bugs regularly, the timestamp will rarely be used, and // should still provide a kinda accurate sorting when needed. - return b[i].EditUnixTime < b[j].EditUnixTime + return b[i].editUnixTime < b[j].editUnixTime } func (b BugsByEditTime) Swap(i, j int) { diff --git a/cache/repo_cache.go b/cache/repo_cache.go index f2e1c7d0..e70904df 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -29,15 +29,8 @@ const identityCacheFile = "identity-cache" // 1: original format // 2: added cache for identities with a reference in the bug cache -const formatVersion = 2 - -type ErrInvalidCacheFormat struct { - message string -} - -func (e ErrInvalidCacheFormat) Error() string { - return e.message -} +// 3: CreateUnixTime --> createUnixTime, EditUnixTime --> editUnixTime +const formatVersion = 3 var _ repository.RepoCommon = &RepoCache{} @@ -99,10 +92,8 @@ func NewNamedRepoCache(r repository.ClockedRepo, name string) (*RepoCache, error if err == nil { return c, nil } - if _, ok := err.(ErrInvalidCacheFormat); ok { - return nil, err - } + // Cache is either missing, broken or outdated. Rebuilding. err = c.buildCache() if err != nil { return nil, err @@ -254,10 +245,8 @@ func (c *RepoCache) loadBugCache() error { return err } - if aux.Version != 2 { - return ErrInvalidCacheFormat{ - message: fmt.Sprintf("unknown cache format version %v", aux.Version), - } + if aux.Version != formatVersion { + return fmt.Errorf("unknown cache format version %v", aux.Version) } c.bugExcerpts = aux.Excerpts @@ -286,10 +275,8 @@ func (c *RepoCache) loadIdentityCache() error { return err } - if aux.Version != 2 { - return ErrInvalidCacheFormat{ - message: fmt.Sprintf("unknown cache format version %v", aux.Version), - } + if aux.Version != formatVersion { + return fmt.Errorf("unknown cache format version %v", aux.Version) } c.identitiesExcerpts = aux.Excerpts diff --git a/commands/json_common.go b/commands/json_common.go new file mode 100644 index 00000000..9a144a1e --- /dev/null +++ b/commands/json_common.go @@ -0,0 +1,55 @@ +package commands + +import ( + "time" + + "github.com/MichaelMure/git-bug/cache" + "github.com/MichaelMure/git-bug/identity" + "github.com/MichaelMure/git-bug/util/lamport" +) + +type JSONIdentity struct { + Id string `json:"id"` + HumanId string `json:"human_id"` + Name string `json:"name"` + Login string `json:"login"` +} + +func NewJSONIdentity(i identity.Interface) JSONIdentity { + return JSONIdentity{ + Id: i.Id().String(), + HumanId: i.Id().Human(), + Name: i.Name(), + Login: i.Login(), + } +} + +func NewJSONIdentityFromExcerpt(excerpt *cache.IdentityExcerpt) JSONIdentity { + return JSONIdentity{ + Id: excerpt.Id.String(), + HumanId: excerpt.Id.Human(), + Name: excerpt.Name, + Login: excerpt.Login, + } +} + +func NewJSONIdentityFromLegacyExcerpt(excerpt *cache.LegacyAuthorExcerpt) JSONIdentity { + return JSONIdentity{ + Name: excerpt.Name, + Login: excerpt.Login, + } +} + +type JSONTime struct { + Timestamp int64 `json:"timestamp"` + Time time.Time `json:"time"` + Lamport lamport.Time `json:"lamport,omitempty"` +} + +func NewJSONTime(t time.Time, l lamport.Time) JSONTime { + return JSONTime{ + Timestamp: t.Unix(), + Time: t, + Lamport: l, + } +} diff --git a/commands/ls.go b/commands/ls.go index 68cb0cfc..34f1f982 100644 --- a/commands/ls.go +++ b/commands/ls.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "strings" - "time" text "github.com/MichaelMure/go-term-text" "github.com/spf13/cobra" @@ -61,6 +60,8 @@ func runLsBug(_ *cobra.Command, args []string) error { } switch lsOutputFormat { + case "org-mode": + return lsOrgmodeFormatter(backend, bugExcerpt) case "plain": return lsPlainFormatter(backend, bugExcerpt) case "json": @@ -72,11 +73,11 @@ func runLsBug(_ *cobra.Command, args []string) error { } } -type JSONBug struct { - Id string `json:"id"` - HumanId string `json:"human_id"` - CreationTime time.Time `json:"creation_time"` - LastEdited time.Time `json:"last_edited"` +type JSONBugExcerpt struct { + Id string `json:"id"` + HumanId string `json:"human_id"` + CreateTime JSONTime `json:"create_time"` + EditTime JSONTime `json:"edit_time"` Status string `json:"status"` Labels []bug.Label `json:"labels"` @@ -89,29 +90,19 @@ type JSONBug struct { Metadata map[string]string `json:"metadata"` } -type JSONIdentity struct { - Id string `json:"id"` - HumanId string `json:"human_id"` - Name string `json:"name"` - Login string `json:"login"` -} - func lsJsonFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerpt) error { - jsonBugs := make([]JSONBug, len(bugExcerpts)) + jsonBugs := make([]JSONBugExcerpt, len(bugExcerpts)) for i, b := range bugExcerpts { - jsonBug := JSONBug{ - b.Id.String(), - b.Id.Human(), - time.Unix(b.CreateUnixTime, 0), - time.Unix(b.EditUnixTime, 0), - b.Status.String(), - b.Labels, - b.Title, - []JSONIdentity{}, - []JSONIdentity{}, - JSONIdentity{}, - b.LenComments, - b.CreateMetadata, + jsonBug := JSONBugExcerpt{ + Id: b.Id.String(), + HumanId: b.Id.Human(), + CreateTime: NewJSONTime(b.CreateTime(), b.CreateLamportTime), + EditTime: NewJSONTime(b.EditTime(), b.EditLamportTime), + Status: b.Status.String(), + Labels: b.Labels, + Title: b.Title, + Comments: b.LenComments, + Metadata: b.CreateMetadata, } if b.AuthorId != "" { @@ -119,41 +110,27 @@ func lsJsonFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerpt) if err != nil { return err } - - jsonBug.Author.Name = author.DisplayName() - jsonBug.Author.Login = author.Login - jsonBug.Author.Id = author.Id.String() - jsonBug.Author.HumanId = author.Id.Human() + jsonBug.Author = NewJSONIdentityFromExcerpt(author) } else { - jsonBug.Author.Name = b.LegacyAuthor.DisplayName() - jsonBug.Author.Login = b.LegacyAuthor.Login + jsonBug.Author = NewJSONIdentityFromLegacyExcerpt(&b.LegacyAuthor) } - for _, element := range b.Actors { + jsonBug.Actors = make([]JSONIdentity, len(b.Actors)) + for i, element := range b.Actors { actor, err := backend.ResolveIdentityExcerpt(element) if err != nil { return err } - - jsonBug.Actors = append(jsonBug.Actors, JSONIdentity{ - actor.Id.String(), - actor.Id.Human(), - actor.Name, - actor.Login, - }) + jsonBug.Actors[i] = NewJSONIdentityFromExcerpt(actor) } - for _, element := range b.Participants { + jsonBug.Participants = make([]JSONIdentity, len(b.Participants)) + for i, element := range b.Participants { participant, err := backend.ResolveIdentityExcerpt(element) if err != nil { return err } - jsonBug.Participants = append(jsonBug.Participants, JSONIdentity{ - participant.Id.String(), - participant.Id.Human(), - participant.DisplayName(), - participant.Login, - }) + jsonBug.Participants[i] = NewJSONIdentityFromExcerpt(participant) } jsonBugs[i] = jsonBug @@ -207,11 +184,84 @@ func lsDefaultFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerp func lsPlainFormatter(_ *cache.RepoCache, bugExcerpts []*cache.BugExcerpt) error { for _, b := range bugExcerpts { - fmt.Printf("[%s] %s\n", b.Status, b.Title) + fmt.Printf("%s [%s] %s\n", b.Id.Human(), b.Status, b.Title) } return nil } +func lsOrgmodeFormatter(backend *cache.RepoCache, bugExcerpts []*cache.BugExcerpt) error { + fmt.Println("+TODO: OPEN | CLOSED") + + for _, b := range bugExcerpts { + status := strings.Title(b.Status.String()) + + var title string + if link, ok := b.CreateMetadata["github-url"]; ok { + title = fmt.Sprintf("[%s][%s]", link, b.Title) + } else { + title = b.Title + } + + var name string + if b.AuthorId != "" { + author, err := backend.ResolveIdentityExcerpt(b.AuthorId) + if err != nil { + return err + } + name = author.DisplayName() + } else { + name = b.LegacyAuthor.DisplayName() + } + + labels := b.Labels + var labelsString string + if len(labels) > 0 { + labelsString = fmt.Sprintf(":%s:", strings.Replace(fmt.Sprint(labels), " ", ":", -1)) + } else { + labelsString = "" + } + + fmt.Printf("* %s %s [%s] %s: %s %s\n", + b.Id.Human(), + status, + b.CreateTime(), + name, + title, + labelsString, + ) + + fmt.Printf("** Last Edited: %s\n", b.EditTime().String()) + + fmt.Printf("** Actors:\n") + for _, element := range b.Actors { + actor, err := backend.ResolveIdentityExcerpt(element) + if err != nil { + return err + } + + fmt.Printf(": %s %s\n", + actor.Id.Human(), + actor.DisplayName(), + ) + } + + fmt.Printf("** Participants:\n") + for _, element := range b.Participants { + participant, err := backend.ResolveIdentityExcerpt(element) + if err != nil { + return err + } + + fmt.Printf(": %s %s\n", + participant.Id.Human(), + participant.DisplayName(), + ) + } + } + + return nil +} + // Finish the command flags transformation into the query.Query func completeQuery() error { for _, str := range lsStatusQuery { @@ -294,5 +344,5 @@ func init() { lsCmd.Flags().StringVarP(&lsSortDirection, "direction", "d", "asc", "Select the sorting direction. Valid values are [asc,desc]") lsCmd.Flags().StringVarP(&lsOutputFormat, "format", "f", "default", - "Select the output formatting style. Valid values are [default, plain(text), json]") + "Select the output formatting style. Valid values are [default,plain,json,org-mode]") } diff --git a/commands/show.go b/commands/show.go index 0bb3dc4a..2f4e46ed 100644 --- a/commands/show.go +++ b/commands/show.go @@ -1,22 +1,26 @@ package commands import ( + "encoding/json" "errors" "fmt" "strings" + "github.com/spf13/cobra" + + "github.com/MichaelMure/git-bug/bug" "github.com/MichaelMure/git-bug/cache" _select "github.com/MichaelMure/git-bug/commands/select" "github.com/MichaelMure/git-bug/util/colors" "github.com/MichaelMure/git-bug/util/interrupt" - "github.com/spf13/cobra" ) var ( - showFieldsQuery string + showFieldsQuery string + showOutputFormat string ) -func runShowBug(cmd *cobra.Command, args []string) error { +func runShowBug(_ *cobra.Command, args []string) error { backend, err := cache.NewRepoCache(repo) if err != nil { return err @@ -35,16 +39,16 @@ func runShowBug(cmd *cobra.Command, args []string) error { return errors.New("invalid bug: no comment") } - firstComment := snapshot.Comments[0] - if showFieldsQuery != "" { switch showFieldsQuery { case "author": - fmt.Printf("%s\n", firstComment.Author.DisplayName()) + fmt.Printf("%s\n", snapshot.Author.DisplayName()) case "authorEmail": - fmt.Printf("%s\n", firstComment.Author.Email()) + fmt.Printf("%s\n", snapshot.Author.Email()) case "createTime": - fmt.Printf("%s\n", firstComment.FormatTime()) + fmt.Printf("%s\n", snapshot.CreateTime.String()) + case "lastEdit": + fmt.Printf("%s\n", snapshot.EditTime().String()) case "humanId": fmt.Printf("%s\n", snapshot.Id().Human()) case "id": @@ -74,16 +78,33 @@ func runShowBug(cmd *cobra.Command, args []string) error { return nil } + switch showOutputFormat { + case "org-mode": + return showOrgmodeFormatter(snapshot) + case "json": + return showJsonFormatter(snapshot) + case "default": + return showDefaultFormatter(snapshot) + default: + return fmt.Errorf("unknown format %s", showOutputFormat) + } +} + +func showDefaultFormatter(snapshot *bug.Snapshot) error { // Header - fmt.Printf("[%s] %s %s\n\n", - colors.Yellow(snapshot.Status), + fmt.Printf("%s [%s] %s\n\n", colors.Cyan(snapshot.Id().Human()), + colors.Yellow(snapshot.Status), snapshot.Title, ) - fmt.Printf("%s opened this issue %s\n\n", - colors.Magenta(firstComment.Author.DisplayName()), - firstComment.FormatTimeRel(), + fmt.Printf("%s opened this issue %s\n", + colors.Magenta(snapshot.Author.DisplayName()), + snapshot.CreateTime.String(), + ) + + fmt.Printf("This was last edited at %s\n\n", + snapshot.EditTime().String(), ) // Labels @@ -143,6 +164,151 @@ func runShowBug(cmd *cobra.Command, args []string) error { return nil } +type JSONBugSnapshot struct { + Id string `json:"id"` + HumanId string `json:"human_id"` + CreateTime JSONTime `json:"create_time"` + EditTime JSONTime `json:"edit_time"` + Status string `json:"status"` + Labels []bug.Label `json:"labels"` + Title string `json:"title"` + Author JSONIdentity `json:"author"` + Actors []JSONIdentity `json:"actors"` + Participants []JSONIdentity `json:"participants"` + Comments []JSONComment `json:"comments"` +} + +type JSONComment struct { + Id string `json:"id"` + HumanId string `json:"human_id"` + Author JSONIdentity `json:"author"` + Message string `json:"message"` +} + +func NewJSONComment(comment bug.Comment) JSONComment { + return JSONComment{ + Id: comment.Id().String(), + HumanId: comment.Id().Human(), + Author: NewJSONIdentity(comment.Author), + Message: comment.Message, + } +} + +func showJsonFormatter(snapshot *bug.Snapshot) error { + jsonBug := JSONBugSnapshot{ + Id: snapshot.Id().String(), + HumanId: snapshot.Id().Human(), + CreateTime: NewJSONTime(snapshot.CreateTime, 0), + EditTime: NewJSONTime(snapshot.EditTime(), 0), + Status: snapshot.Status.String(), + Labels: snapshot.Labels, + Title: snapshot.Title, + Author: NewJSONIdentity(snapshot.Author), + } + + jsonBug.Actors = make([]JSONIdentity, len(snapshot.Actors)) + for i, element := range snapshot.Actors { + jsonBug.Actors[i] = NewJSONIdentity(element) + } + + jsonBug.Participants = make([]JSONIdentity, len(snapshot.Participants)) + for i, element := range snapshot.Participants { + jsonBug.Participants[i] = NewJSONIdentity(element) + } + + jsonBug.Comments = make([]JSONComment, len(snapshot.Comments)) + for i, comment := range snapshot.Comments { + jsonBug.Comments[i] = NewJSONComment(comment) + } + + jsonObject, _ := json.MarshalIndent(jsonBug, "", " ") + fmt.Printf("%s\n", jsonObject) + + return nil +} + +func showOrgmodeFormatter(snapshot *bug.Snapshot) error { + // Header + fmt.Printf("%s [%s] %s\n", + snapshot.Id().Human(), + snapshot.Status, + snapshot.Title, + ) + + fmt.Printf("* Author: %s\n", + snapshot.Author.DisplayName(), + ) + + fmt.Printf("* Creation Time: %s\n", + snapshot.CreateTime.String(), + ) + + fmt.Printf("* Last Edit: %s\n", + snapshot.EditTime().String(), + ) + + // Labels + var labels = make([]string, len(snapshot.Labels)) + for i, label := range snapshot.Labels { + labels[i] = string(label) + } + + fmt.Printf("* Labels:\n") + if len(labels) > 0 { + fmt.Printf("** %s\n", + strings.Join(labels, "\n** "), + ) + } + + // Actors + var actors = make([]string, len(snapshot.Actors)) + for i, actor := range snapshot.Actors { + actors[i] = fmt.Sprintf("%s %s", + actor.Id().Human(), + actor.DisplayName(), + ) + } + + fmt.Printf("* Actors:\n** %s\n", + strings.Join(actors, "\n** "), + ) + + // Participants + var participants = make([]string, len(snapshot.Participants)) + for i, participant := range snapshot.Participants { + participants[i] = fmt.Sprintf("%s %s", + participant.Id().Human(), + participant.DisplayName(), + ) + } + + fmt.Printf("* Participants:\n** %s\n", + strings.Join(participants, "\n** "), + ) + + fmt.Printf("* Comments:\n") + + for i, comment := range snapshot.Comments { + var message string + fmt.Printf("** #%d %s\n", + i, + comment.Author.DisplayName(), + ) + + if comment.Message == "" { + message = "No description provided." + } else { + message = strings.ReplaceAll(comment.Message, "\n", "\n: ") + } + + fmt.Printf(": %s\n", + message, + ) + } + + return nil +} + var showCmd = &cobra.Command{ Use: "show [<id>]", Short: "Display the details of a bug.", @@ -152,6 +318,8 @@ var showCmd = &cobra.Command{ func init() { RootCmd.AddCommand(showCmd) - showCmd.Flags().StringVarP(&showFieldsQuery, "field", "f", "", - "Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]") + showCmd.Flags().StringVarP(&showFieldsQuery, "field", "", "", + "Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants]") + showCmd.Flags().StringVarP(&showOutputFormat, "format", "f", "default", + "Select the output formatting style. Valid values are [default,json,org-mode]") } diff --git a/commands/user_ls.go b/commands/user_ls.go index 609ff5a4..b3fb32e6 100644 --- a/commands/user_ls.go +++ b/commands/user_ls.go @@ -1,15 +1,21 @@ package commands import ( + "encoding/json" "fmt" + "github.com/spf13/cobra" + "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/util/colors" "github.com/MichaelMure/git-bug/util/interrupt" - "github.com/spf13/cobra" ) -func runUserLs(cmd *cobra.Command, args []string) error { +var ( + userLsOutputFormat string +) + +func runUserLs(_ *cobra.Command, _ []string) error { backend, err := cache.NewRepoCache(repo) if err != nil { return err @@ -17,21 +23,48 @@ func runUserLs(cmd *cobra.Command, args []string) error { defer backend.Close() interrupt.RegisterCleaner(backend.Close) - for _, id := range backend.AllIdentityIds() { - i, err := backend.ResolveIdentityExcerpt(id) + ids := backend.AllIdentityIds() + var users []*cache.IdentityExcerpt + for _, id := range ids { + user, err := backend.ResolveIdentityExcerpt(id) if err != nil { return err } + users = append(users, user) + } + + switch userLsOutputFormat { + case "json": + return userLsJsonFormatter(users) + case "default": + return userLsDefaultFormatter(users) + default: + return fmt.Errorf("unknown format %s", userLsOutputFormat) + } +} +func userLsDefaultFormatter(users []*cache.IdentityExcerpt) error { + for _, user := range users { fmt.Printf("%s %s\n", - colors.Cyan(i.Id.Human()), - i.DisplayName(), + colors.Cyan(user.Id.Human()), + user.DisplayName(), ) } return nil } +func userLsJsonFormatter(users []*cache.IdentityExcerpt) error { + jsonUsers := make([]JSONIdentity, len(users)) + for i, user := range users { + jsonUsers[i] = NewJSONIdentityFromExcerpt(user) + } + + jsonObject, _ := json.MarshalIndent(jsonUsers, "", " ") + fmt.Printf("%s\n", jsonObject) + return nil +} + var userLsCmd = &cobra.Command{ Use: "ls", Short: "List identities.", @@ -42,4 +75,6 @@ var userLsCmd = &cobra.Command{ func init() { userCmd.AddCommand(userLsCmd) userLsCmd.Flags().SortFlags = false + userLsCmd.Flags().StringVarP(&userLsOutputFormat, "format", "f", "default", + "Select the output formatting style. Valid values are [default,json]") } diff --git a/doc/man/git-bug-ls.1 b/doc/man/git-bug-ls.1 index 5d05cfcd..54a64cd2 100644 --- a/doc/man/git-bug-ls.1 +++ b/doc/man/git-bug-ls.1 @@ -59,7 +59,7 @@ You can pass an additional query to filter and order the list. This query can be .PP \fB\-f\fP, \fB\-\-format\fP="default" - Select the output formatting style. Valid values are [default, plain(text), json] + Select the output formatting style. Valid values are [default,plain,json,org\-mode] .PP \fB\-h\fP, \fB\-\-help\fP[=false] diff --git a/doc/man/git-bug-show.1 b/doc/man/git-bug-show.1 index dae1877b..eb694dac 100644 --- a/doc/man/git-bug-show.1 +++ b/doc/man/git-bug-show.1 @@ -19,8 +19,12 @@ Display the details of a bug. .SH OPTIONS .PP -\fB\-f\fP, \fB\-\-field\fP="" - Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants] +\fB\-\-field\fP="" + Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants] + +.PP +\fB\-f\fP, \fB\-\-format\fP="default" + Select the output formatting style. Valid values are [default,json,org\-mode] .PP \fB\-h\fP, \fB\-\-help\fP[=false] diff --git a/doc/man/git-bug-user-ls.1 b/doc/man/git-bug-user-ls.1 index 1ccddb8a..14bc9ff7 100644 --- a/doc/man/git-bug-user-ls.1 +++ b/doc/man/git-bug-user-ls.1 @@ -19,6 +19,10 @@ List identities. .SH OPTIONS .PP +\fB\-f\fP, \fB\-\-format\fP="default" + Select the output formatting style. Valid values are [default,json] + +.PP \fB\-h\fP, \fB\-\-help\fP[=false] help for ls diff --git a/doc/md/git-bug_ls.md b/doc/md/git-bug_ls.md index 32941e4f..1532efab 100644 --- a/doc/md/git-bug_ls.md +++ b/doc/md/git-bug_ls.md @@ -35,7 +35,7 @@ git bug ls --status closed --by creation -n, --no strings Filter by absence of something. Valid values are [label] -b, --by string Sort the results by a characteristic. Valid values are [id,creation,edit] (default "creation") -d, --direction string Select the sorting direction. Valid values are [asc,desc] (default "asc") - -f, --format string Select the output formatting style. Valid values are [default, plain(text), json] (default "default") + -f, --format string Select the output formatting style. Valid values are [default,plain,json,org-mode] (default "default") -h, --help help for ls ``` diff --git a/doc/md/git-bug_show.md b/doc/md/git-bug_show.md index 40152ede..e6d2d7ba 100644 --- a/doc/md/git-bug_show.md +++ b/doc/md/git-bug_show.md @@ -13,8 +13,9 @@ git-bug show [<id>] [flags] ### Options ``` - -f, --field string Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants] - -h, --help help for show + --field string Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants] + -f, --format string Select the output formatting style. Valid values are [default,json,org-mode] (default "default") + -h, --help help for show ``` ### SEE ALSO diff --git a/doc/md/git-bug_user_ls.md b/doc/md/git-bug_user_ls.md index d390ed2a..51f01712 100644 --- a/doc/md/git-bug_user_ls.md +++ b/doc/md/git-bug_user_ls.md @@ -13,7 +13,8 @@ git-bug user ls [flags] ### Options ``` - -h, --help help for ls + -f, --format string Select the output formatting style. Valid values are [default,json] (default "default") + -h, --help help for ls ``` ### SEE ALSO diff --git a/graphql/models/lazy_bug.go b/graphql/models/lazy_bug.go index 6034e80d..a7840df2 100644 --- a/graphql/models/lazy_bug.go +++ b/graphql/models/lazy_bug.go @@ -81,7 +81,7 @@ func (lb *lazyBug) Id() entity.Id { } func (lb *lazyBug) LastEdit() time.Time { - return time.Unix(lb.excerpt.EditUnixTime, 0) + return lb.excerpt.EditTime() } func (lb *lazyBug) Status() bug.Status { @@ -133,7 +133,7 @@ func (lb *lazyBug) Participants() ([]IdentityWrapper, error) { } func (lb *lazyBug) CreatedAt() time.Time { - return time.Unix(lb.excerpt.CreateUnixTime, 0) + return lb.excerpt.CreateTime() } func (lb *lazyBug) Timeline() ([]bug.TimelineItem, error) { @@ -163,7 +163,7 @@ func NewLoadedBug(snap *bug.Snapshot) *loadedBug { } func (l *loadedBug) LastEdit() time.Time { - return l.Snapshot.LastEditTime() + return l.Snapshot.EditTime() } func (l *loadedBug) Status() bug.Status { @@ -203,7 +203,7 @@ func (l *loadedBug) Participants() ([]IdentityWrapper, error) { } func (l *loadedBug) CreatedAt() time.Time { - return l.Snapshot.CreatedAt + return l.Snapshot.CreateTime } func (l *loadedBug) Timeline() ([]bug.TimelineItem, error) { diff --git a/misc/bash_completion/git-bug b/misc/bash_completion/git-bug index 93c75b8e..c3e62849 100644 --- a/misc/bash_completion/git-bug +++ b/misc/bash_completion/git-bug @@ -933,8 +933,11 @@ _git-bug_show() flags+=("--field=") two_word_flags+=("--field") - two_word_flags+=("-f") local_nonpersistent_flags+=("--field=") + flags+=("--format=") + two_word_flags+=("--format") + two_word_flags+=("-f") + local_nonpersistent_flags+=("--format=") must_have_one_flag=() must_have_one_noun=() @@ -1122,6 +1125,10 @@ _git-bug_user_ls() flags_with_completion=() flags_completion=() + flags+=("--format=") + two_word_flags+=("--format") + two_word_flags+=("-f") + local_nonpersistent_flags+=("--format=") must_have_one_flag=() must_have_one_noun=() diff --git a/misc/powershell_completion/git-bug b/misc/powershell_completion/git-bug index 9f51c785..0a8c7b5a 100644 --- a/misc/powershell_completion/git-bug +++ b/misc/powershell_completion/git-bug @@ -159,8 +159,8 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock { [CompletionResult]::new('--by', 'by', [CompletionResultType]::ParameterName, 'Sort the results by a characteristic. Valid values are [id,creation,edit]') [CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Select the sorting direction. Valid values are [asc,desc]') [CompletionResult]::new('--direction', 'direction', [CompletionResultType]::ParameterName, 'Select the sorting direction. Valid values are [asc,desc]') - [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default, plain(text), json]') - [CompletionResult]::new('--format', 'format', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default, plain(text), json]') + [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,plain,json,org-mode]') + [CompletionResult]::new('--format', 'format', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,plain,json,org-mode]') break } 'git-bug;ls-id' { @@ -179,8 +179,9 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock { break } 'git-bug;show' { - [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]') - [CompletionResult]::new('--field', 'field', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]') + [CompletionResult]::new('--field', 'field', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants]') + [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,json,org-mode]') + [CompletionResult]::new('--format', 'format', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,json,org-mode]') break } 'git-bug;status' { @@ -221,6 +222,8 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock { break } 'git-bug;user;ls' { + [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,json]') + [CompletionResult]::new('--format', 'format', [CompletionResultType]::ParameterName, 'Select the output formatting style. Valid values are [default,json]') break } 'git-bug;version' { @@ -242,4 +245,4 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock { }) $completions.Where{ $_.CompletionText -like "$wordToComplete*" } | Sort-Object -Property ListItemText -} +}
\ No newline at end of file diff --git a/misc/zsh_completion/git-bug b/misc/zsh_completion/git-bug index 18a49668..ba15f3bc 100644 --- a/misc/zsh_completion/git-bug +++ b/misc/zsh_completion/git-bug @@ -304,7 +304,7 @@ function _git-bug_ls { '(*-n *--no)'{\*-n,\*--no}'[Filter by absence of something. Valid values are [label]]:' \ '(-b --by)'{-b,--by}'[Sort the results by a characteristic. Valid values are [id,creation,edit]]:' \ '(-d --direction)'{-d,--direction}'[Select the sorting direction. Valid values are [asc,desc]]:' \ - '(-f --format)'{-f,--format}'[Select the output formatting style. Valid values are [default, plain(text), json]]:' + '(-f --format)'{-f,--format}'[Select the output formatting style. Valid values are [default,plain,json,org-mode]]:' } function _git-bug_ls-id { @@ -329,7 +329,8 @@ function _git-bug_select { function _git-bug_show { _arguments \ - '(-f --field)'{-f,--field}'[Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]]:' + '--field[Select field to display. Valid values are [author,authorEmail,createTime,lastEdit,humanId,id,labels,shortId,status,title,actors,participants]]:' \ + '(-f --format)'{-f,--format}'[Select the output formatting style. Valid values are [default,json,org-mode]]:' } @@ -443,7 +444,8 @@ function _git-bug_user_create { } function _git-bug_user_ls { - _arguments + _arguments \ + '(-f --format)'{-f,--format}'[Select the output formatting style. Valid values are [default,json]]:' } function _git-bug_version { diff --git a/termui/bug_table.go b/termui/bug_table.go index 2913ac80..e57a2b15 100644 --- a/termui/bug_table.go +++ b/termui/bug_table.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "strings" - "time" "github.com/MichaelMure/go-term-text" "github.com/awesome-gocui/gocui" @@ -315,7 +314,7 @@ func (bt *bugTable) render(v *gocui.View, maxX int) { authorDisplayName = excerpt.LegacyAuthor.DisplayName() } - lastEditTime := time.Unix(excerpt.EditUnixTime, 0) + lastEditTime := excerpt.EditTime() id := text.LeftPadMaxLine(excerpt.Id.Human(), columnWidths["id"], 1) status := text.LeftPadMaxLine(excerpt.Status.String(), columnWidths["status"], 1) diff --git a/termui/show_bug.go b/termui/show_bug.go index e483117a..a5da519f 100644 --- a/termui/show_bug.go +++ b/termui/show_bug.go @@ -219,7 +219,7 @@ func (sb *showBug) renderMain(g *gocui.Gui, mainView *gocui.View) error { colors.Bold(snap.Title), colors.Yellow(snap.Status), colors.Magenta(snap.Author.DisplayName()), - snap.CreatedAt.Format(timeLayout), + snap.CreateTime.Format(timeLayout), edited, ) bugHeader, lines := text.Wrap(bugHeader, maxX) |