Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"strings"
"time"

"github.com/charmbracelet/fang"
"github.com/notnmeyer/daylog-cli/internal/daylog"
Expand Down Expand Up @@ -88,7 +89,7 @@ func applyPrevFlag(cmd *cobra.Command, dl *daylog.DayLog) error {
return err
}
if showPrevious {
prev, err := file.PreviousLog(dl.ProjectPath, file.LogProvider{})
prev, err := file.PreviousLog(dl.ProjectPath, file.LogProvider{}, time.Now())
if err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"log"
"path/filepath"
"time"

"github.com/notnmeyer/daylog-cli/internal/daylog"
"github.com/notnmeyer/daylog-cli/internal/file"
Expand Down Expand Up @@ -35,7 +36,7 @@ var showCmd = &cobra.Command{
}

if showPrevious {
prev, err := file.PreviousLog(dl.ProjectPath, file.LogProvider{})
prev, err := file.PreviousLog(dl.ProjectPath, file.LogProvider{}, time.Now())
if err != nil {
log.Fatal(err)
}
Expand Down
42 changes: 38 additions & 4 deletions internal/daylog/daylog.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/adrg/xdg"
"github.com/markusmobius/go-dateparser"
"github.com/notnmeyer/daylog-cli/internal/editor"
"github.com/notnmeyer/daylog-cli/internal/file"
"github.com/notnmeyer/daylog-cli/internal/git"
"github.com/notnmeyer/daylog-cli/internal/output-formatter"
)
Expand Down Expand Up @@ -104,6 +105,10 @@ func (d *DayLog) Edit() error {
}

func (d *DayLog) Show(format string) (string, error) {
if err := createIfMissing(d); err != nil {
return "", err
}

contents, err := editor.Read(d.Path)
if err != nil {
return "", err
Expand Down Expand Up @@ -215,23 +220,52 @@ func createIfMissing(d *DayLog) error {
return nil
}

var file *os.File
var f *os.File
if os.IsNotExist(err) {
file, err = os.Create(d.Path)
f, err = os.Create(d.Path)
if err != nil {
return err
}
defer file.Close()
defer f.Close()
} else {
return err
}

year, month, day := d.Date.Year(), int(d.Date.Month()), d.Date.Day()
header := fmt.Sprintf("# %d/%02d/%02d\n\n", year, month, day)
_, err = file.WriteString(header)
_, err = f.WriteString(header)
if err != nil {
return err
}

if todos := carryOverTodos(d.ProjectPath, *d.Date); len(todos) > 0 {
_, err = f.WriteString(strings.Join(todos, "\n") + "\n")
if err != nil {
return err
}
}

return nil
}

// carryOverTodos reads the log before `before` and returns any lines containing "TODO".
func carryOverTodos(projectPath string, before time.Time) []string {
prev, err := file.PreviousLog(projectPath, file.NewLogProvider(), before)
if err != nil {
return nil
}

prevPath := filepath.Join(projectPath, prev, "log.md")
content, err := os.ReadFile(prevPath)
if err != nil {
return nil
}

var todos []string
for _, line := range strings.Split(string(content), "\n") {
if strings.Contains(line, "TODO") {
todos = append(todos, line)
}
}
return todos
}
56 changes: 56 additions & 0 deletions internal/daylog/daylog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ import (
"time"
)

// writePrevLog creates a log file for the given date under projectPath.
func writePrevLog(t *testing.T, projectPath, dateDir, content string) {
t.Helper()
dir := filepath.Join(projectPath, dateDir)
if err := os.MkdirAll(dir, 0755); err != nil {
t.Fatalf("creating prev log dir: %v", err)
}
path := filepath.Join(dir, "log.md")
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
t.Fatalf("writing prev log: %v", err)
}
}

func testDayLog(t *testing.T) *DayLog {
t.Helper()
dir := t.TempDir()
Expand Down Expand Up @@ -88,4 +101,47 @@ func TestAppend(t *testing.T) {
}
}

func TestCarryOverTodos(t *testing.T) {
tests := []struct {
name string
prevContent string
expectedFile string
}{
{
name: "no todos in previous log",
prevContent: "# 2025/12/01\n\ndid some work\n",
expectedFile: "# 2025/12/02\n\n",
},
{
name: "todos are copied to new log",
prevContent: "# 2025/12/01\n\n- TODO: write tests\n- TODO: fix bug\n- done thing\n",
expectedFile: "# 2025/12/02\n\n- TODO: write tests\n- TODO: fix bug\n",
},
{
name: "no previous log",
prevContent: "",
expectedFile: "# 2025/12/02\n\n",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dl := testDayLog(t)

if tt.prevContent != "" {
writePrevLog(t, dl.ProjectPath, "2025/12/01", tt.prevContent)
}

if err := createIfMissing(dl); err != nil {
t.Fatalf("createIfMissing() error = %v", err)
}

got := readFile(t, dl.Path)
if got != tt.expectedFile {
t.Errorf("file contents =\n%q\nwant\n%q", got, tt.expectedFile)
}
})
}
}

func strPtr(s string) *string { return &s }
21 changes: 8 additions & 13 deletions internal/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
"strconv"
"strings"
"time"

"github.com/notnmeyer/daylog-cli/internal/dateutil"
)

type FileLogProvider interface {
Expand Down Expand Up @@ -59,15 +57,15 @@ func (LogProvider) GetLogs(projectPath string) ([]string, error) {
return validFiles, nil
}

func PreviousLog(path string, provider FileLogProvider) (string, error) {
func PreviousLog(path string, provider FileLogProvider, before time.Time) (string, error) {
// get all the logs
logs, err := provider.GetLogs(path)
if err != nil {
log.Fatal(err)
}

// find the most recent existing log before today
prev, exists := findPreviousLog(logs)
// find the most recent existing log before the given date
prev, exists := findPreviousLog(logs, before)
if !exists {
return "", fmt.Errorf("no previous log found")
}
Expand Down Expand Up @@ -101,16 +99,13 @@ func convertLogToDisplayName(log string) string {
return path.Join(split[0], split[1], split[2])
}

// logs is the list of keys of all logs. each log is YYYY/MM/DD, which the most recent date at index 0
func findPreviousLog(logs []string) (string, bool) {
todayTime, err := time.Parse("2006/01/02", dateutil.GetCurrent().String())
if err != nil {
return "", false
}

// logs is the list of keys of all logs. each log is YYYY/MM/DD, with the most recent date at index 0
func findPreviousLog(logs []string, before time.Time) (string, bool) {
// Compare dates only so time-of-day doesn't affect the result
beforeDate := time.Date(before.Year(), before.Month(), before.Day(), 0, 0, 0, 0, time.UTC)
for _, log := range logs {
l, err := time.Parse("2006/01/02", log)
if err == nil && l.Before(todayTime) {
if err == nil && l.Before(beforeDate) {
return log, true
}
}
Expand Down
23 changes: 5 additions & 18 deletions internal/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import (
"os"
"path/filepath"
"testing"

"github.com/notnmeyer/daylog-cli/internal/dateutil"
"time"
)

type mockLogProvider struct {
Expand Down Expand Up @@ -110,13 +109,7 @@ func TestConvertLogToDisplayName(t *testing.T) {
}

func TestPreviousLog(t *testing.T) {
dateutil.GetCurrent = func() dateutil.Date {
return dateutil.Date{
Year: 2025,
Month: 12,
Day: 2,
}
}
before := time.Date(2025, 12, 2, 0, 0, 0, 0, time.UTC)

tests := []struct {
name string
Expand Down Expand Up @@ -149,7 +142,7 @@ func TestPreviousLog(t *testing.T) {
logs: tt.logs,
}
t.Run(tt.name, func(t *testing.T) {
prev, err := PreviousLog("mock/path", provider)
prev, err := PreviousLog("mock/path", provider, before)
if err != nil && err.Error() != tt.err.Error() {
t.Errorf("findPreviousLog(%v) = found: %v, want: %v", tt.logs, err, tt.err)
}
Expand All @@ -161,13 +154,7 @@ func TestPreviousLog(t *testing.T) {
}

func TestFindPreviousLog(t *testing.T) {
dateutil.GetCurrent = func() dateutil.Date {
return dateutil.Date{
Year: 2025,
Month: 12,
Day: 2,
}
}
before := time.Date(2025, 12, 2, 0, 0, 0, 0, time.UTC)

tests := []struct {
name string
Expand Down Expand Up @@ -209,7 +196,7 @@ func TestFindPreviousLog(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
prev, found := findPreviousLog(tt.logs)
prev, found := findPreviousLog(tt.logs, before)
if found != tt.found {
t.Errorf("findPreviousLog(%v) = found: %v, want: %v", tt.logs, found, tt.found)
}
Expand Down
Loading