aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md24
-rw-r--r--workstack.go42
-rw-r--r--ws/main.go261
3 files changed, 242 insertions, 85 deletions
diff --git a/README.md b/README.md
index 3044d73..0ccd0e6 100644
--- a/README.md
+++ b/README.md
@@ -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")
diff --git a/ws/main.go b/ws/main.go
index 2030fc5..f663cf6 100644
--- a/ws/main.go
+++ b/ws/main.go
@@ -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)
}