162 lines
3.6 KiB
Go
162 lines
3.6 KiB
Go
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
|
|
}
|