From 363caa6301d67c97b29e83e168ed9c83f571355c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20B=C3=BCtow?= <1224732+snonux@users.noreply.github.com> Date: Fri, 20 Jun 2025 20:43:03 +0300 Subject: Add task sorting --- internal/task/sort_test.go | 28 +++++++++++++++++++ internal/task/task.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 internal/task/sort_test.go (limited to 'internal/task') diff --git a/internal/task/sort_test.go b/internal/task/sort_test.go new file mode 100644 index 0000000..4b034c9 --- /dev/null +++ b/internal/task/sort_test.go @@ -0,0 +1,28 @@ +package task + +import ( + "reflect" + "testing" +) + +func TestSortTasks(t *testing.T) { + tasks := []Task{ + {ID: 2, Due: "20240102T000000Z", Priority: "M", Tags: []string{"b", "a"}}, + {ID: 1, Due: "20240101T000000Z", Priority: "H", Tags: []string{"a"}}, + {ID: 3, Due: "", Priority: "", Tags: []string{"c"}}, + {ID: 4, Due: "20240101T000000Z", Priority: "L", Tags: []string{"a"}}, + {ID: 5, Due: "20240101T000000Z", Priority: "H", Tags: []string{"b"}}, + {ID: 6, Due: "20240101T000000Z", Priority: "H", Tags: []string{"b"}}, + } + + SortTasks(tasks) + + var ids []int + for _, tsk := range tasks { + ids = append(ids, tsk.ID) + } + want := []int{1, 5, 6, 4, 2, 3} + if !reflect.DeepEqual(ids, want) { + t.Fatalf("unexpected order: %v", ids) + } +} diff --git a/internal/task/task.go b/internal/task/task.go index fb07333..d10c500 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -8,8 +8,10 @@ import ( "io" "os" "os/exec" + "sort" "strconv" "strings" + "time" ) // Task represents a taskwarrior task as returned by `task export`. @@ -222,3 +224,68 @@ func EditCmd(id int) *exec.Cmd { func Edit(id int) error { return EditCmd(id).Run() } + +// SortTasks orders tasks by due date, priority, tag names and id. +// Tasks without a due date are placed after tasks with a due date. +func SortTasks(tasks []Task) { + joinTags := func(tags []string) string { + if len(tags) == 0 { + return "" + } + cpy := append([]string(nil), tags...) + sort.Strings(cpy) + return strings.Join(cpy, ",") + } + + priVal := func(p string) int { + switch p { + case "H": + return 3 + case "M": + return 2 + case "L": + return 1 + default: + return 0 + } + } + + parseDue := func(s string) (time.Time, bool) { + if s == "" { + return time.Time{}, false + } + t, err := time.Parse("20060102T150405Z", s) + if err != nil { + return time.Time{}, false + } + return t, true + } + + sort.Slice(tasks, func(i, j int) bool { + ti, tj := tasks[i], tasks[j] + + di, iok := parseDue(ti.Due) + dj, jok := parseDue(tj.Due) + if iok && !jok { + return true + } + if !iok && jok { + return false + } + if iok && jok && !di.Equal(dj) { + return di.Before(dj) + } + + pi, pj := priVal(ti.Priority), priVal(tj.Priority) + if pi != pj { + return pi > pj + } + + tgI, tgJ := joinTags(ti.Tags), joinTags(tj.Tags) + if tgI != tgJ { + return tgI < tgJ + } + + return ti.ID < tj.ID + }) +} -- cgit v1.2.3