diff options
-rw-r--r-- | README.md | 24 | ||||
-rw-r--r-- | workstack.go | 42 | ||||
-rw-r--r-- | ws/main.go | 261 |
3 files changed, 242 insertions, 85 deletions
@@ -1,32 +1,30 @@ # To Do - [ ] ZSH Completion -- [ ] Add descriptions to task -- [ ] Edit task with editor -- [ ] Create a script to add from dump file into tasks -- [ ] Add "start" command +- [ ] Create intermediate text format for editing in editor / importing - [ ] Add tore-like reminder for shellrc -- [ ] Rename a task -- [ ] import: read multiple lines from stdin and import them as taks -- [ ] parsing text as Tasks, maybe helper program? - [ ] clocking with a 'start' command +- [ ] tag edit # Testing - [ ] add - [ ] done -- [ ] undone +- [ ] undo - [ ] del - [ ] pc - [ ] <no arg> +- [ ] top - [ ] ls - [ ] list - [ ] tag -- [ ] tagd -- [ ] tagl - -# Bugs -- [ ] bug: undo a task with a tag that no longer exists +- [ ] tag del +- [ ] tag add +- [ ] tag list # Done +- [x] Edit task +- [x] Add descriptions to task +- [x] added `-d` flag to commands to act on done tasks +- [x] bug: undo a task with a tag that no longer exists - [x] After pc/del do "ls" command - [x] Better help for commands & options - [x] Put a task at the top diff --git a/workstack.go b/workstack.go index 45b5d67..bfff166 100644 --- a/workstack.go +++ b/workstack.go @@ -8,6 +8,7 @@ package workstack import ( "fmt" "os" + "strings" "time" ) @@ -19,30 +20,47 @@ type TaskDone struct { type Tag string type Task struct { - Text string - Tag Tag + Text string + Description string + Tag Tag } +var ( + DateLayout string = "15:04:05 02/01/2006" +) + +const ( + TASK_LIST_COUNT = 5 + GOBDATA_FILENAME = "tasks.gob" +) + +// For formatting +const ( + RESET = "\033[0m" + CYAN = "\033[36m" + DESCRIPTION_PAD = " " +) + func (t TaskDone) String() string { return fmt.Sprintf("(%s) %s", t.Date.Format(DateLayout), t.Task) } func (t Task) String() string { + var s string + if t.Tag != "" { - return fmt.Sprintf("{%s} %s", t.Tag, t.Text) + s = fmt.Sprintf("{%s} %s", t.Tag, t.Text) } else { - return fmt.Sprintf("%s", t.Text) + s = fmt.Sprintf("%s", t.Text) } -} -var ( - DateLayout string = "15:04:05 02/01/2006" -) + if t.Description != "" { + s += "\n" + DESCRIPTION_PAD + + CYAN + strings.ReplaceAll(t.Description, "\n", "\n"+DESCRIPTION_PAD) + RESET + } -const ( - TASK_LIST_COUNT = 5 - GOBDATA_FILENAME = "workstack.gob" -) + return s +} func GetGobdataPath() string { p := os.Getenv("HOME") @@ -16,7 +16,7 @@ var ( // Stack of active tasks Tags []ws.Tag // Active tasks - Tasks []ws.Task + TasksDo []ws.Task // Completed tasks TasksDone []ws.TaskDone // Persistent storage for Tasks @@ -29,7 +29,7 @@ func AddTask(tagName string, taskText string) { } if tagName == "" { - Tasks = append(Tasks, ws.Task{Text: taskText}) + TasksDo = append(TasksDo, ws.Task{Text: taskText}) return } @@ -47,47 +47,95 @@ func AddTask(tagName string, taskText string) { fmt.Printf("No tag '%s' found.\n", tagName) os.Exit(1) } - Tasks = append(Tasks, ws.Task{Text: taskText, Tag: tag}) + TasksDo = append(TasksDo, ws.Task{Text: taskText, Tag: tag}) } -func DeleteTask(n int) { - Tasks = append(Tasks[:n-1], Tasks[n:]...) +func DeleteTask(shouldDeleteDone bool, n int) { + if shouldDeleteDone { + TasksDone = append(TasksDone[:n-1], TasksDone[n:]...) + } else { + TasksDo = append(TasksDo[:n-1], TasksDo[n:]...) + } } // save, delete and append task func ProcrastinateTask(n int) { - t := Tasks[n-1] - Tasks = append(Tasks[:n-1], Tasks[n:]...) - Tasks = append(Tasks, t) + t := TasksDo[n-1] + TasksDo = append(TasksDo[:n-1], TasksDo[n:]...) + TasksDo = append(TasksDo, t) } func TopTask(n int) { var NewTasks []ws.Task - NewTasks = append(NewTasks, Tasks[n-1]) - NewTasks = append(NewTasks, Tasks[:n-1]...) - if n < len(Tasks) { - NewTasks = append(NewTasks, Tasks[n:]...) + NewTasks = append(NewTasks, TasksDo[n-1]) + NewTasks = append(NewTasks, TasksDo[:n-1]...) + if n < len(TasksDo) { + NewTasks = append(NewTasks, TasksDo[n:]...) } - Tasks = NewTasks + TasksDo = NewTasks } func DoneTask(n int) { TasksDone = append(TasksDone, ws.TaskDone{ - Task: Tasks[n-1], + Task: TasksDo[n-1], Date: time.Now(), }) - Tasks = append(Tasks[:n-1], Tasks[n:]...) + TasksDo = append(TasksDo[:n-1], TasksDo[n:]...) } -func UndoneTask(n int) { - Tasks = append(Tasks, TasksDone[n-1].Task) +func UndoTask(n int) { + TasksDo = append(TasksDo, TasksDone[n-1].Task) TasksDone = append(TasksDone[:n-1], TasksDone[n:]...) } +func DescribeTask(shouldDescribeDone bool, n int, description string) { + var task *ws.Task + + if shouldDescribeDone { + task = &TasksDone[n-1].Task + } else { + task = &TasksDo[n-1] + } + + task.Description = description +} + +func EditTask(shouldEditDone bool, n int, newText string, newTag ws.Tag) { + var task *ws.Task + + if shouldEditDone { + task = &TasksDone[n-1].Task + } else { + task = &TasksDo[n-1] + } + + if newTag != "" { + found := false + for _, v := range Tags { + if newTag == v { + found = true + break + } + } + + if !found { + fmt.Printf("No tag '%s' found.\n", newTag) + os.Exit(1) + } + + task.Tag = newTag + } + + if newText != "" { + task.Text = newText + } + +} + func ListTasks(showDone bool) { - if len(Tasks) == 0 && len(TasksDone) == 0 { + if len(TasksDo) == 0 && len(TasksDone) == 0 { fmt.Println("No tasks.") return } @@ -96,11 +144,11 @@ func ListTasks(showDone bool) { showDone = false } - if len(Tasks) > 0 { + if len(TasksDo) > 0 { if showDone { fmt.Println("Active:") } - for i, t := range Tasks { + for i, t := range TasksDo { fmt.Printf("% 2d. %s\n", i+1, t) } } @@ -113,16 +161,6 @@ func ListTasks(showDone bool) { } } -func LsTasks() { - if len(Tasks) == 0 { - fmt.Println("No tasks.") - return - } - for i := 0; i < len(Tasks) && i < ws.TASK_LIST_COUNT; i++ { - fmt.Printf("%2d. %s\n", i+1, Tasks[i]) - } -} - func AddTag(tagName ws.Tag) { for i := 0; i < len(Tags); i++ { if tagName == Tags[i] { @@ -133,7 +171,23 @@ func AddTag(tagName ws.Tag) { Tags = append(Tags, tagName) } +// Delete tag number n +// Only deletes if the tag is not used by any task func DeleteTag(n int) { + // Check if the tag is currently used + for i, v := range TasksDo { + if v.Tag == Tags[n-1] { + fmt.Printf("Tag is used by task: %d. %s\n", i+1, v) + os.Exit(1) + } + } + for i, v := range TasksDone { + if v.Task.Tag == Tags[n-1] { + fmt.Printf("Tag is used by done task: %d. %s\n", i+1, v.Task) + os.Exit(1) + } + } + Tags = append(Tags[:n-1], Tags[n:]...) } @@ -155,16 +209,18 @@ COMMANDS } func wsUsage() { - fmt.Println(`usage: ws <command> [flag] + fmt.Println(`usage: ws <command> COMMANDS - task Add a new task + add Add a new task done Mark an active task as done - undone Undo a done task + undo Undo a done task + desc Describe a task with more words + edit Edit a task's text or tag del Delete an active task pc Procrastinate an active task top Put a task to the top of the stack - ls Short list of active tasks - list List all active and done tasks`) + list List all active and done tasks + tag tag commands`) } func main() { @@ -184,7 +240,7 @@ func main() { panic(err) } else { dec = gob.NewDecoder(f) - err = dec.Decode(&Tasks) + err = dec.Decode(&TasksDo) if err != nil { panic(err) } @@ -201,7 +257,7 @@ func main() { // When no arguments are provided display the first task. if len(os.Args) == 1 { - LsTasks() + ListTasks(false) os.Exit(0) } @@ -222,23 +278,37 @@ func main() { // Delete an active task case "del": var n int + var shouldDeleteDone bool flagSet := flag.NewFlagSet("del", flag.ExitOnError) flagSet.IntVar(&n, "n", 1, "Set task number") + flagSet.BoolVar(&shouldDeleteDone, "d", false, "Should delete a done task") flagSet.Parse(os.Args[2:]) - if len(Tasks) <= 0 { - fmt.Println("No tasks to delete.") - os.Exit(1) - } + if !shouldDeleteDone { + if len(TasksDo) == 0 { + fmt.Println("No tasks to edit.") + os.Exit(1) + } - if len(Tasks) < n { - fmt.Println("Task number out of range") - os.Exit(1) + if len(TasksDo) < n { + fmt.Println("Task number out of range.") + os.Exit(1) + } + } else { + if len(TasksDone) == 0 { + fmt.Println("No done tasks to edit.") + os.Exit(1) + } + + if len(TasksDone) < n { + fmt.Println("Done task number out of range.") + os.Exit(1) + } } - DeleteTask(n) - ListTasks(false) + DeleteTask(shouldDeleteDone, n) + ListTasks(shouldDeleteDone) // Mark an active task as done case "done": @@ -248,12 +318,12 @@ func main() { flagSet.IntVar(&n, "n", 1, "Set task number") flagSet.Parse(os.Args[2:]) - if len(Tasks) <= 0 { + if len(TasksDo) <= 0 { fmt.Println("No tasks to be done.") os.Exit(1) } - if len(Tasks) < n { + if len(TasksDo) < n { fmt.Println("Task number out of range") os.Exit(1) } @@ -261,11 +331,86 @@ func main() { DoneTask(n) ListTasks(true) + case "desc": + var n int + var shouldDescribeDone bool + + flagSet := flag.NewFlagSet("desc", flag.ExitOnError) + flagSet.BoolVar(&shouldDescribeDone, "d", false, "Should describe a done task") + flagSet.IntVar(&n, "n", 1, "Set task number") + flagSet.Parse(os.Args[2:]) + + description := strings.Join(flagSet.Args(), " ") + + if !shouldDescribeDone { + if len(TasksDo) == 0 { + fmt.Println("No tasks to describe.") + os.Exit(1) + } + + if len(TasksDo) < n { + fmt.Println("Task number out of range.") + os.Exit(1) + } + } else { + if len(TasksDone) == 0 { + fmt.Println("No done tasks to describe.") + os.Exit(1) + } + + if len(TasksDone) < n { + fmt.Println("Done task number out of range.") + os.Exit(1) + } + } + + DescribeTask(shouldDescribeDone, n, description) + + ListTasks(shouldDescribeDone) + + case "edit": + var n int + var shouldEditDone bool + var newTag, newText string + + flagSet := flag.NewFlagSet("edit", flag.ExitOnError) + flagSet.IntVar(&n, "n", 1, "Set task number") + flagSet.StringVar(&newTag, "t", "", "Set tag name") + flagSet.BoolVar(&shouldEditDone, "d", false, "Should edit a done task") + flagSet.Parse(os.Args[2:]) + + if !shouldEditDone { + if len(TasksDo) == 0 { + fmt.Println("No tasks to edit.") + os.Exit(1) + } + + if len(TasksDo) < n { + fmt.Println("Task number out of range.") + os.Exit(1) + } + } else { + if len(TasksDone) == 0 { + fmt.Println("No done tasks to edit.") + os.Exit(1) + } + + if len(TasksDone) < n { + fmt.Println("Done task number out of range.") + os.Exit(1) + } + } + + newText = strings.Join(flagSet.Args(), " ") + + EditTask(shouldEditDone, n, newText, ws.Tag(newTag)) + ListTasks(shouldEditDone) + // Undo a done task - case "undone": + case "undo": var n int - flagSet := flag.NewFlagSet("undone", flag.ExitOnError) + flagSet := flag.NewFlagSet("undo", flag.ExitOnError) flagSet.IntVar(&n, "n", 1, "Set task number") flagSet.Parse(os.Args[2:]) @@ -279,7 +424,7 @@ func main() { os.Exit(1) } - UndoneTask(n) + UndoTask(n) ListTasks(true) // Procrastinate an active task @@ -290,12 +435,12 @@ func main() { flagSet.IntVar(&n, "n", 1, "Set task number") flagSet.Parse(os.Args[2:]) - if len(Tasks) <= 0 { + if len(TasksDo) <= 0 { fmt.Println("No tasks to procrastinate.") os.Exit(1) } - if len(Tasks) < n { + if len(TasksDo) < n { fmt.Println("Task number out of range") os.Exit(1) } @@ -316,7 +461,7 @@ func main() { os.Exit(1) } - if len(Tasks) <= 1 { + if len(TasksDo) <= 1 { fmt.Println("No tasks to put to the top.") os.Exit(1) } @@ -326,7 +471,7 @@ func main() { os.Exit(1) } - if len(Tasks) < n { + if len(TasksDo) < n { fmt.Println("Task number out of range") os.Exit(1) } @@ -334,10 +479,6 @@ func main() { TopTask(n) ListTasks(false) - // Short list of active tasks - case "ls": - LsTasks() - // List all active and done tasks case "list": ListTasks(true) @@ -395,7 +536,7 @@ func main() { panic(err) } enc := gob.NewEncoder(f) - err = enc.Encode(Tasks) + err = enc.Encode(TasksDo) if err != nil { panic(err) } |