split cli.go into add.go, edit.go too
This commit is contained in:
46
cmd/pttodo-cli/add.go
Normal file
46
cmd/pttodo-cli/add.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -8,14 +8,9 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
|
|
||||||
"gogs.inhome.blapointe.com/bel/pttodo/pttodo"
|
"gogs.inhome.blapointe.com/bel/pttodo/pttodo"
|
||||||
|
|
||||||
@@ -48,20 +43,11 @@ func main() {
|
|||||||
|
|
||||||
func _main() error {
|
func _main() error {
|
||||||
config := getConfig()
|
config := getConfig()
|
||||||
if config.add != "" {
|
if err := add(config); err != nil {
|
||||||
v := pttodo.Todo{
|
return err
|
||||||
Todo: config.add,
|
|
||||||
Schedule: pttodo.Schedule(config.addSchedule),
|
|
||||||
Tags: config.addTags,
|
|
||||||
}
|
|
||||||
if err := add(config.targets, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if config.edit {
|
if err := edit(config); err != nil {
|
||||||
if err := edit(config.targets); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return dump(os.Stdout, config.targets, config.tags, config.search, config.root)
|
return dump(os.Stdout, config.targets, config.tags, config.search, config.root)
|
||||||
}
|
}
|
||||||
@@ -119,265 +105,6 @@ func verifyFile(path string) error {
|
|||||||
return nil
|
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 <Enter> 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 <Enter> 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 {
|
func merge(filepath string, mergeTargetFilePath string) error {
|
||||||
baseReader, err := filePathReader(filepath)
|
baseReader, err := filePathReader(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
161
cmd/pttodo-cli/edit.go
Normal file
161
cmd/pttodo-cli/edit.go
Normal file
@@ -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 <Enter> 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user