aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaymaekers Luca <raymaekers.luca@gmail.com>2024-11-12 15:58:32 +0100
committerRaymaekers Luca <raymaekers.luca@gmail.com>2024-11-12 15:59:29 +0100
commit17197ee1db2a196b3a78baa328bbb73400381823 (patch)
tree5e3d7b42c2515291a28c6b9542ca5f3d1023ad24
parentda7dc88dd52671ddd3733cc8c5320ed6c5654c89 (diff)
Added "nvim" command
-rw-r--r--README.md6
-rw-r--r--workstack.go1
-rw-r--r--ws/main.go186
3 files changed, 192 insertions, 1 deletions
diff --git a/README.md b/README.md
index 0ccd0e6..b09e824 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# To Do
- [ ] ZSH Completion
-- [ ] Create intermediate text format for editing in editor / importing
+- [ ] import command
+- [ ] better temp file path & delete on exit
+- [ ] nvim command can edit done tasks as well (with flag)
- [ ] Add tore-like reminder for shellrc
- [ ] clocking with a 'start' command
- [ ] tag edit
@@ -21,6 +23,8 @@
- [ ] tag list
# Done
+- [x] Added nvim command
+- [x] Create intermediate text format for editing in editor / importing
- [x] Edit task
- [x] Add descriptions to task
- [x] added `-d` flag to commands to act on done tasks
diff --git a/workstack.go b/workstack.go
index bfff166..d6d1c07 100644
--- a/workstack.go
+++ b/workstack.go
@@ -32,6 +32,7 @@ var (
const (
TASK_LIST_COUNT = 5
GOBDATA_FILENAME = "tasks.gob"
+ TMPFILE = "tasks.ws"
)
// For formatting
diff --git a/ws/main.go b/ws/main.go
index f663cf6..57a279d 100644
--- a/ws/main.go
+++ b/ws/main.go
@@ -5,7 +5,9 @@ import (
"errors"
"flag"
"fmt"
+ "io/ioutil"
"os"
+ "os/exec"
"strings"
"time"
@@ -22,6 +24,167 @@ var (
// Persistent storage for Tasks
)
+// Parse and import file
+func ImportTasks(tmpfile string) ([]ws.Task, error) {
+ f, err := os.Open(tmpfile)
+ if err != nil {
+ panic(err)
+ }
+
+ content, err := ioutil.ReadAll(f)
+ if err != nil {
+ panic(err)
+ }
+
+ // TODO: better size choices
+ parsedTasks := make([]ws.Task, len(content))
+ tagName := make([]byte, len(content))
+
+ tasksLen := 0
+ tagLen := 0
+ textLen := 0
+
+ isInsideTag := false
+ isInsideText := false
+
+ firstTime := true
+
+ for i := 0; i < len(content); i++ {
+ // consume leading whitespace
+ if firstTime {
+ for content[i] == ' ' || content[i] == '\n' || content[i] == '\t' {
+ i++
+ }
+ }
+
+ if content[i] == '{' {
+ isInsideTag = true
+ isInsideText = false
+
+ if firstTime {
+ firstTime = false
+ continue
+ }
+
+ text := trimEnd(content, i-1, textLen)
+ parsedTasks[tasksLen].Text = string(text)
+
+ textLen = 0
+
+ tasksLen++
+ i++
+ }
+
+ if content[i] == '}' {
+ isInsideTag = false
+ isInsideText = true
+
+ tag := make([]byte, tagLen)
+ copy(tag, tagName)
+ parsedTasks[tasksLen].Tag = ws.Tag(tag)
+
+ tagLen = 0
+ tagName[0] = 0
+ i++
+
+ // consume whitespace
+ for content[i] == ' ' || content[i] == '\n' || content[i] == '\t' {
+ i++
+ }
+ }
+
+ // this can happen whe consuming whitespace
+ if i == len(content) {
+ break
+ }
+
+ if isInsideTag {
+ tagName[tagLen] = content[i]
+ tagLen++
+ } else if isInsideText {
+ textLen++
+ }
+ }
+
+ if !isInsideTag && !isInsideText && tasksLen == 0 {
+ return nil, nil
+ }
+
+ if isInsideTag {
+ return nil, errors.New("Syntax error: Brace not closed.")
+ }
+
+ // get that last task in
+ text := trimEnd(content, len(content)-1, textLen)
+ parsedTasks[tasksLen].Text = string(text)
+
+ tasksLen++
+
+ // Check if the tags are valid
+ for i := 0; i < tasksLen; i++ {
+ if parsedTasks[i].Tag == "" {
+ continue
+ }
+
+ found := 0
+ for ; found < len(Tags); found++ {
+ if parsedTasks[i].Tag == Tags[found] {
+ break
+ }
+ }
+
+ if found == len(Tags) {
+ return nil, errors.New(fmt.Sprintf("Syntax Error: Invalid tag '%s'.", parsedTasks[i].Tag))
+ }
+ }
+
+ return parsedTasks[:tasksLen], nil
+}
+
+// export the tasks to custom file format and edit in nvim
+func NvimTask() {
+ f, err := os.Create(ws.TMPFILE)
+ if err != nil {
+ panic(err)
+ }
+
+ // export tasks
+ for _, v := range TasksDo {
+ fmt.Fprintf(f, "{%s} %s\n", v.Tag, v.Text)
+ if v.Description != "" {
+ fmt.Fprintf(f, "%s\n", v.Description)
+ }
+ }
+
+ var importedTasks []ws.Task
+ for {
+ // edit in nvim
+ nvimCmd := exec.Command("nvim", ws.TMPFILE)
+ nvimCmd.Stdout = os.Stdout
+ nvimCmd.Stdin = os.Stdin
+ nvimCmd.Stderr = os.Stderr
+
+ err = nvimCmd.Run()
+ if err != nil {
+ panic(err)
+ }
+
+ importedTasks, err = ImportTasks(ws.TMPFILE)
+ if err == nil {
+ break
+ }
+
+ var choice string
+ fmt.Printf("%s\nRetry? [y/N] ", err)
+ fmt.Scanf("%s", &choice)
+ if choice == "" || choice[0] == 'n' || choice[0] == 'N' {
+ os.Exit(1)
+ }
+ }
+
+ TasksDo = importedTasks
+}
+
func AddTask(tagName string, taskText string) {
if taskText == "" {
fmt.Println("Task text is required.")
@@ -219,10 +382,26 @@ COMMANDS
del Delete an active task
pc Procrastinate an active task
top Put a task to the top of the stack
+ nvim Edit tasks in nvim
list List all active and done tasks
tag tag commands`)
}
+// Return part of a slice with trailing white space removed
+func trimEnd(text []byte, endOffset, length int) []byte {
+ end := endOffset
+ start := endOffset - length + 1
+ for ; end > start; end-- {
+ if text[end] != '\n' &&
+ text[end] != ' ' &&
+ text[end] != '\t' &&
+ text[end] != 0 {
+ break
+ }
+ }
+ return text[start : end+1]
+}
+
func main() {
var (
dec *gob.Decoder
@@ -406,6 +585,13 @@ func main() {
EditTask(shouldEditDone, n, newText, ws.Tag(newTag))
ListTasks(shouldEditDone)
+ case "nvim":
+ flagSet := flag.NewFlagSet("nvim", flag.ExitOnError)
+ flagSet.Parse(os.Args[2:])
+
+ NvimTask()
+ ListTasks(false)
+
// Undo a done task
case "undo":
var n int