From a5869aaa8913f09a88bf69074f2458692c36f131 Mon Sep 17 00:00:00 2001 From: Bel LaPointe Date: Mon, 6 Nov 2023 10:33:32 -0700 Subject: [PATCH] split cli.go into add.go, edit.go too --- cmd/pttodo-cli/add.go | 46 +++++++ cmd/pttodo-cli/cli.go | 281 +---------------------------------------- cmd/pttodo-cli/edit.go | 161 +++++++++++++++++++++++ 3 files changed, 211 insertions(+), 277 deletions(-) create mode 100644 cmd/pttodo-cli/add.go create mode 100644 cmd/pttodo-cli/edit.go diff --git a/cmd/pttodo-cli/add.go b/cmd/pttodo-cli/add.go new file mode 100644 index 0000000..8c5891a --- /dev/null +++ b/cmd/pttodo-cli/add.go @@ -0,0 +1,46 @@ +package main + +import ( + "io/ioutil" + "os" + + "gogs.inhome.blapointe.com/bel/pttodo/pttodo" + "gopkg.in/yaml.v2" +) + +func add(config config) error { + if config.add == "" { + return nil + } + v := pttodo.Todo{ + Todo: config.add, + Schedule: pttodo.Schedule(config.addSchedule), + Tags: config.addTags, + } + return _add(config.targets, v) +} + +func _add(filepaths []string, todo pttodo.Todo) error { + target := filepaths[0] + var original pttodo.Root + + r, err := filePathReader(target) + if err != nil { + return err + } + if err := yaml.NewDecoder(r).Decode(&original); err != nil { + return err + } + + original.Todo = append([]pttodo.Todo{todo}, original.Todo...) + original.AutoMove() + + c, err := yaml.Marshal(original) + if err != nil { + return err + } else if err := ioutil.WriteFile(target, c, os.ModePerm); err != nil { + return err + } + + return nil +} diff --git a/cmd/pttodo-cli/cli.go b/cmd/pttodo-cli/cli.go index fe2d61b..cdcbdaa 100644 --- a/cmd/pttodo-cli/cli.go +++ b/cmd/pttodo-cli/cli.go @@ -8,14 +8,9 @@ import ( "io/ioutil" "log" "os" - "os/exec" "path" "sort" "strings" - "syscall" - "time" - - "github.com/google/uuid" "gogs.inhome.blapointe.com/bel/pttodo/pttodo" @@ -48,20 +43,11 @@ func main() { func _main() error { config := getConfig() - if config.add != "" { - v := pttodo.Todo{ - Todo: config.add, - Schedule: pttodo.Schedule(config.addSchedule), - Tags: config.addTags, - } - if err := add(config.targets, v); err != nil { - return err - } + if err := add(config); err != nil { + return err } - if config.edit { - if err := edit(config.targets); err != nil { - return err - } + if err := edit(config); err != nil { + return err } return dump(os.Stdout, config.targets, config.tags, config.search, config.root) } @@ -119,265 +105,6 @@ func verifyFile(path string) error { return nil } -func add(filepaths []string, todo pttodo.Todo) error { - target := filepaths[0] - var original pttodo.Root - - r, err := filePathReader(target) - if err != nil { - return err - } - if err := yaml.NewDecoder(r).Decode(&original); err != nil { - return err - } - - original.Todo = append([]pttodo.Todo{todo}, original.Todo...) - original.AutoMove() - - c, err := yaml.Marshal(original) - if err != nil { - return err - } else if err := ioutil.WriteFile(target, c, os.ModePerm); err != nil { - return err - } - - return nil -} - -func edit(filepaths []string) error { - tempDir, err := ioutil.TempDir(os.TempDir(), "edit-pttodo-*") - if err != nil { - return err - } - originals := map[string]pttodo.Root{} - lastModified := map[string]time.Time{} - for _, target := range filepaths { - var original pttodo.Root - if r, err := filePathReader(target); err != nil { - return err - } else if err := yaml.NewDecoder(r).Decode(&original); err != nil { - return err - } else if c, err := yaml.Marshal(original.Todo); err != nil { - return err - } else if err := ioutil.WriteFile(path.Join(tempDir, path.Base(target)), c, os.ModePerm); err != nil { - return err - } - originals[target] = original - if info, _ := os.Stat(target); info != nil { - lastModified[target] = info.ModTime() - } else { - lastModified[target] = time.Time{} - } - } - if err := vimd(tempDir); err != nil { - return err - } - for _, target := range filepaths { - for { - err := func() error { - var todos []pttodo.Todo - if r, err := filePathReader(path.Join(tempDir, path.Base(target))); err != nil { - return err - } else if err := yaml.NewDecoder(r).Decode(&todos); err != nil { - return err - } - return nil - }() - if err == nil { - break - } - log.Printf("%s, press to resume editing", err) - b := make([]byte, 1) - if _, err := os.Stdin.Read(b); err != nil { - return err - } - if err := vimd(tempDir); err != nil { - return err - } - } - } - for _, target := range filepaths { - r, err := filePathReader(path.Join(tempDir, path.Base(target))) - if err != nil { - return err - } - var newTodos []pttodo.Todo - if err := yaml.NewDecoder(r).Decode(&newTodos); err != nil { - return err - } - original := originals[target] - - newTodoIDs := map[string]struct{}{} - for i := range newTodos { - newTodoIDs[newTodos[i].ID()] = struct{}{} - } - - for i := range original.Todo { - if _, ok := newTodoIDs[original.Todo[i].ID()]; !ok { - original.Todo[i].TS = pttodo.TS(time.Now().Unix()) - if string(original.Todo[i].Schedule) != "" && !original.Todo[i].Triggered() { - original.Scheduled = append(original.Scheduled, original.Todo[i]) - } - original.Done = append(original.Done, original.Todo[i]) - } - } - original.Todo = newTodos - - original.AutoMove() - - c, err := yaml.Marshal(original) - if err != nil { - return err - } - outputPath := target - if info, _ := os.Stat(target); info != nil && info.ModTime() != lastModified[target] { - outputPath = fmt.Sprintf("%s.%s.%s", outputPath, uuid.New().String(), path.Ext(outputPath)) - } - if err := ioutil.WriteFile(outputPath, c, os.ModePerm); err != nil { - return err - } - } - return nil -} - -func _edit(filepaths []string) error { - tempDir, err := ioutil.TempDir(os.TempDir(), "edit-pttodo") - if err != nil { - return err - } - cpOne := func(filepath string) error { - f, err := os.Create(path.Join(tempDir, path.Base(filepath))) - if err != nil { - return err - } - if _, err := os.Stat(filepath); err == nil { - g, err := os.Open(filepath) - if err != nil { - return err - } - if _, err := io.Copy(f, g); err != nil { - return err - } - g.Close() - } - f.Close() - return nil - } - cp := func() error { - for _, filepath := range filepaths { - if err := cpOne(filepath); err != nil { - return err - } - } - return nil - } - vi := func() error { - return vimd(tempDir) - } - verifyOne := func(tempFile string) error { - for { - err := verifyFile(tempFile) - if err == nil { - break - } - log.Printf("%v, press to resume editing", err) - b := make([]byte, 1) - if _, err := os.Stdin.Read(b); err != nil { - break - } - if err := vi(); err != nil { - return err - } - } - return verifyFile(tempFile) - } - verify := func() error { - for _, filepath := range filepaths { - tempFile := path.Join(tempDir, path.Base(filepath)) - if err := verifyOne(tempFile); err != nil { - return err - } - } - return nil - } - saveOne := func(filepath string) error { - tempFile := path.Join(tempDir, path.Base(filepath)) - var rootTemp, rootOld pttodo.Root - if a, err := filePathReader(tempFile); err != nil { - return err - } else if err := yaml.NewDecoder(a).Decode(&rootTemp); err != nil { - return err - } else if b, err := filePathReader(filepath); err != nil { - return err - } else if err := yaml.NewDecoder(b).Decode(&rootOld); err != nil { - return err - } else if rootTemp.Equals(rootOld) { - //log.Printf("no changes to %s", filepath) - return nil - } - return os.Rename(tempFile, filepath) - } - save := func() error { - for _, filepath := range filepaths { - if err := saveOne(filepath); err != nil { - return err - } - } - return nil - } - - for _, foo := range []func() error{cp, vi, verify, save} { - if err := foo(); err != nil { - return err - } - } - os.RemoveAll(tempDir) - - return nil -} - -func vimd(d string) error { - bin := "vim" - editorbin, err := exec.LookPath(bin) - if err != nil { - editorbin, err = exec.LookPath("vi") - } - if err != nil { - return err - } - args := []string{editorbin, "-p"} - files, err := listDir(d) - if err != nil { - return err - } - args = append(args, files...) - cpid, err := syscall.ForkExec( - editorbin, - args, - &syscall.ProcAttr{ - Dir: d, - Env: os.Environ(), - Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()}, - Sys: nil, - }, - ) - if err != nil { - return err - } - proc, err := os.FindProcess(cpid) - if err != nil { - return err - } - state, err := proc.Wait() - if err != nil { - return err - } - if exitCode := state.ExitCode(); exitCode != 0 { - return fmt.Errorf("bad exit code on vim: %d, state: %+v", exitCode, state) - } - return nil -} - func merge(filepath string, mergeTargetFilePath string) error { baseReader, err := filePathReader(filepath) if err != nil { diff --git a/cmd/pttodo-cli/edit.go b/cmd/pttodo-cli/edit.go new file mode 100644 index 0000000..71f30d3 --- /dev/null +++ b/cmd/pttodo-cli/edit.go @@ -0,0 +1,161 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path" + "syscall" + "time" + + "github.com/google/uuid" + "gogs.inhome.blapointe.com/bel/pttodo/pttodo" + "gopkg.in/yaml.v2" +) + +func edit(config config) error { + if !config.edit { + return nil + } + return _edit(config.targets) +} + +func _edit(filepaths []string) error { + tempDir, err := ioutil.TempDir(os.TempDir(), "edit-pttodo-*") + if err != nil { + return err + } + originals := map[string]pttodo.Root{} + lastModified := map[string]time.Time{} + for _, target := range filepaths { + var original pttodo.Root + if r, err := filePathReader(target); err != nil { + return err + } else if err := yaml.NewDecoder(r).Decode(&original); err != nil { + return err + } else if c, err := yaml.Marshal(original.Todo); err != nil { + return err + } else if err := ioutil.WriteFile(path.Join(tempDir, path.Base(target)), c, os.ModePerm); err != nil { + return err + } + originals[target] = original + if info, _ := os.Stat(target); info != nil { + lastModified[target] = info.ModTime() + } else { + lastModified[target] = time.Time{} + } + } + if err := vimd(tempDir); err != nil { + return err + } + for _, target := range filepaths { + for { + err := func() error { + var todos []pttodo.Todo + if r, err := filePathReader(path.Join(tempDir, path.Base(target))); err != nil { + return err + } else if err := yaml.NewDecoder(r).Decode(&todos); err != nil { + return err + } + return nil + }() + if err == nil { + break + } + log.Printf("%s, press to resume editing", err) + b := make([]byte, 1) + if _, err := os.Stdin.Read(b); err != nil { + return err + } + if err := vimd(tempDir); err != nil { + return err + } + } + } + for _, target := range filepaths { + r, err := filePathReader(path.Join(tempDir, path.Base(target))) + if err != nil { + return err + } + var newTodos []pttodo.Todo + if err := yaml.NewDecoder(r).Decode(&newTodos); err != nil { + return err + } + original := originals[target] + + newTodoIDs := map[string]struct{}{} + for i := range newTodos { + newTodoIDs[newTodos[i].ID()] = struct{}{} + } + + for i := range original.Todo { + if _, ok := newTodoIDs[original.Todo[i].ID()]; !ok { + original.Todo[i].TS = pttodo.TS(time.Now().Unix()) + if string(original.Todo[i].Schedule) != "" && !original.Todo[i].Triggered() { + original.Scheduled = append(original.Scheduled, original.Todo[i]) + } + original.Done = append(original.Done, original.Todo[i]) + } + } + original.Todo = newTodos + + original.AutoMove() + + c, err := yaml.Marshal(original) + if err != nil { + return err + } + outputPath := target + if info, _ := os.Stat(target); info != nil && info.ModTime() != lastModified[target] { + outputPath = fmt.Sprintf("%s.%s.%s", outputPath, uuid.New().String(), path.Ext(outputPath)) + } + if err := ioutil.WriteFile(outputPath, c, os.ModePerm); err != nil { + return err + } + } + return nil +} + +func vimd(d string) error { + bin := "vim" + editorbin, err := exec.LookPath(bin) + if err != nil { + editorbin, err = exec.LookPath("vi") + } + if err != nil { + return err + } + args := []string{editorbin, "-p"} + files, err := listDir(d) + if err != nil { + return err + } + args = append(args, files...) + cpid, err := syscall.ForkExec( + editorbin, + args, + &syscall.ProcAttr{ + Dir: d, + Env: os.Environ(), + Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()}, + Sys: nil, + }, + ) + if err != nil { + return err + } + proc, err := os.FindProcess(cpid) + if err != nil { + return err + } + state, err := proc.Wait() + if err != nil { + return err + } + if exitCode := state.ExitCode(); exitCode != 0 { + return fmt.Errorf("bad exit code on vim: %d, state: %+v", exitCode, state) + } + return nil +}