diff options
| author | Paul Bütow <1224732+snonux@users.noreply.github.com> | 2025-06-20 20:43:03 +0300 |
|---|---|---|
| committer | Paul Bütow <1224732+snonux@users.noreply.github.com> | 2025-06-20 20:43:03 +0300 |
| commit | 363caa6301d67c97b29e83e168ed9c83f571355c (patch) | |
| tree | 1a4e7f87b15049e63924fa7a27d89f152c0a74b1 | |
| parent | d596388ebe915d164a9df22dae57be5f9fc2e465 (diff) | |
Add task sorting
| -rw-r--r-- | internal/task/sort_test.go | 28 | ||||
| -rw-r--r-- | internal/task/task.go | 67 | ||||
| -rw-r--r-- | internal/ui/table.go | 7 |
3 files changed, 101 insertions, 1 deletions
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 + }) +} diff --git a/internal/ui/table.go b/internal/ui/table.go index 755e077..a789ee4 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -89,13 +89,18 @@ func (m *Model) reload() error { return err } - var rows []atable.Row var filtered []task.Task for _, tsk := range tasks { if tsk.Status == "completed" { continue } filtered = append(filtered, tsk) + } + + task.SortTasks(filtered) + + var rows []atable.Row + for _, tsk := range filtered { rows = append(rows, taskToRow(tsk)) } |
