diff --git a/pkg/cli/cmd/ls/ls.go b/pkg/cli/cmd/ls/ls.go index f0ddd047..52aa089f 100644 --- a/pkg/cli/cmd/ls/ls.go +++ b/pkg/cli/cmd/ls/ls.go @@ -19,6 +19,7 @@ import ( "database/sql" "fmt" "strings" + "time" "github.com/dnote/dnote/pkg/cli/context" "github.com/dnote/dnote/pkg/cli/infra" @@ -55,7 +56,7 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command { Aliases: []string{"l", "notes"}, Short: "List all notes", Example: example, - RunE: NewRun(ctx, false), + RunE: NewRun(ctx, false, false), PreRunE: preRun, Deprecated: deprecationWarning, } @@ -64,7 +65,7 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command { } // NewRun returns a new run function for ls -func NewRun(ctx context.DnoteCtx, nameOnly bool) infra.RunEFunc { +func NewRun(ctx context.DnoteCtx, nameOnly bool, timestamps bool) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { if len(args) == 0 { if err := printBooks(ctx, nameOnly); err != nil { @@ -75,7 +76,7 @@ func NewRun(ctx context.DnoteCtx, nameOnly bool) infra.RunEFunc { } bookName := args[0] - if err := printNotes(ctx, bookName); err != nil { + if err := printNotes(ctx, bookName, timestamps); err != nil { return errors.Wrapf(err, "viewing book '%s'", bookName) } @@ -92,6 +93,7 @@ type bookInfo struct { // noteInfo is an information about the note to be printed on screen type noteInfo struct { RowID int + AddedOn int64 Body string } @@ -163,7 +165,23 @@ func printBooks(ctx context.DnoteCtx, nameOnly bool) error { return nil } -func printNotes(ctx context.DnoteCtx, bookName string) error { +func printNoteLine(info noteInfo, showTimestamp bool) { + body, isExcerpt := formatBody(info.Body) + + rowid := log.ColorYellow.Sprintf("(%d)", info.RowID) + preface := "" + if showTimestamp { + local_time := time.Unix(0, info.AddedOn).Format(time.DateTime) + preface = log.ColorYellow.Sprintf(" [%s]", local_time) + } + if isExcerpt { + body = fmt.Sprintf("%s %s", body, log.ColorYellow.Sprintf("[---More---]")) + } + + log.Plainf("%s%s %s\n", rowid, preface, body) +} + +func printNotes(ctx context.DnoteCtx, bookName string, timestamps bool) error { db := ctx.DB var bookUUID string @@ -174,7 +192,7 @@ func printNotes(ctx context.DnoteCtx, bookName string) error { return errors.Wrap(err, "querying the book") } - rows, err := db.Query(`SELECT rowid, body FROM notes WHERE book_uuid = ? AND deleted = ? ORDER BY added_on ASC;`, bookUUID, false) + rows, err := db.Query(`SELECT rowid, added_on, body FROM notes WHERE book_uuid = ? AND deleted = ? ORDER BY added_on ASC;`, bookUUID, false) if err != nil { return errors.Wrap(err, "querying notes") } @@ -183,7 +201,7 @@ func printNotes(ctx context.DnoteCtx, bookName string) error { infos := []noteInfo{} for rows.Next() { var info noteInfo - err = rows.Scan(&info.RowID, &info.Body) + err = rows.Scan(&info.RowID, &info.AddedOn, &info.Body) if err != nil { return errors.Wrap(err, "scanning a row") } @@ -194,14 +212,7 @@ func printNotes(ctx context.DnoteCtx, bookName string) error { log.Infof("on book %s\n", bookName) for _, info := range infos { - body, isExcerpt := formatBody(info.Body) - - rowid := log.ColorYellow.Sprintf("(%d)", info.RowID) - if isExcerpt { - body = fmt.Sprintf("%s %s", body, log.ColorYellow.Sprintf("[---More---]")) - } - - log.Plainf("%s %s\n", rowid, body) + printNoteLine(info, timestamps) } return nil diff --git a/pkg/cli/cmd/view/view.go b/pkg/cli/cmd/view/view.go index 62f50a53..be8e58a2 100644 --- a/pkg/cli/cmd/view/view.go +++ b/pkg/cli/cmd/view/view.go @@ -39,6 +39,7 @@ var example = ` var nameOnly bool var contentOnly bool +var timestamps bool func preRun(cmd *cobra.Command, args []string) error { if len(args) > 2 { @@ -62,6 +63,7 @@ func NewCmd(ctx context.DnoteCtx) *cobra.Command { f := cmd.Flags() f.BoolVarP(&nameOnly, "name-only", "", false, "print book names only") f.BoolVarP(&contentOnly, "content-only", "", false, "print the note content only") + f.BoolVarP(×tamps, "timestamps", "t", false, "print creation timestamp of notes rather than IDs") return cmd } @@ -71,7 +73,11 @@ func newRun(ctx context.DnoteCtx) infra.RunEFunc { var run infra.RunEFunc if len(args) == 0 { - run = ls.NewRun(ctx, nameOnly) + if timestamps { + return errors.New("timestamps flag is only valid when viewing notes") + } + + run = ls.NewRun(ctx, nameOnly, false) } else if len(args) == 1 { if nameOnly { return errors.New("--name-only flag is only valid when viewing books") @@ -80,7 +86,7 @@ func newRun(ctx context.DnoteCtx) infra.RunEFunc { if utils.IsNumber(args[0]) { run = cat.NewRun(ctx, contentOnly) } else { - run = ls.NewRun(ctx, false) + run = ls.NewRun(ctx, false, timestamps) } } else if len(args) == 2 { // DEPRECATED: passing book name to view command is deprecated diff --git a/pkg/cli/main_test.go b/pkg/cli/main_test.go index 5da1915e..bae47381 100644 --- a/pkg/cli/main_test.go +++ b/pkg/cli/main_test.go @@ -106,6 +106,39 @@ func TestInit(t *testing.T) { assert.NotEqual(t, lastSyncAt, "", "last sync at should not be empty") } +func TestViewNote(t *testing.T) { + // tests successful if command does not return an error + t.Run("using default", func(t *testing.T) { + // Setup + db := database.InitTestDB(t, fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.DnoteDBFileName), nil) + testutils.Setup3(t, db) + + // Execute + testutils.RunDnoteCmd(t, opts, binaryName, "view", "js") + defer testutils.RemoveDir(t, testDir) + }) + + t.Run("content only", func(t *testing.T) { + // Setup + db := database.InitTestDB(t, fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.DnoteDBFileName), nil) + testutils.Setup3(t, db) + + // Execute + testutils.RunDnoteCmd(t, opts, binaryName, "view", "js", "--content-only") + defer testutils.RemoveDir(t, testDir) + }) + + t.Run("with timestamps", func(t *testing.T) { + // Setup + db := database.InitTestDB(t, fmt.Sprintf("%s/%s/%s", testDir, consts.DnoteDirName, consts.DnoteDBFileName), nil) + testutils.Setup3(t, db) + + // Execute + testutils.RunDnoteCmd(t, opts, binaryName, "view", "js", "-t") + defer testutils.RemoveDir(t, testDir) + }) +} + func TestAddNote(t *testing.T) { t.Run("new book", func(t *testing.T) { testDir, opts := setupTestEnv(t)