diff --git a/client.go b/client.go index e2fb574..5c3f536 100644 --- a/client.go +++ b/client.go @@ -6,11 +6,11 @@ import ( "io/ioutil" "net/http" "os" - "os/exec" "path" - "time" + "sync" "git.stephensearles.com/stephen/acedoc" + "git.stephensearles.com/stephen/idleshut" ) type editSession struct { @@ -19,6 +19,14 @@ type editSession struct { filename string doc *acedoc.Document tmpdir string + mtx sync.Mutex +} + +func (es *editSession) Clients() uint { + es.mtx.Lock() + defer es.mtx.Unlock() + + return es.clients } func (ch *CaddyHugo) newEditSession(docName string) (*editSession, error) { @@ -54,68 +62,15 @@ func (ch *CaddyHugo) newEditSession(docName string) (*editSession, error) { } func (ch *CaddyHugo) renderDraft(es *editSession) error { - hugoCmd := exec.Command("hugo", "--watch", "-D", "-d", es.tmpdir) - hugoCmd.Dir = ch.Dir - err := hugoCmd.Start() - if err != nil { - return fmt.Errorf("error starting hugo: %v", err) - } + var proc *idleshut.Process - renderer := HugoCmdRenderer{ - TmpDir: es.tmpdir, - SrcDir: ch.Dir, - Filename: es.filename, + f := func() { + if es.clients != 0 { + proc.Touch() + } } - go func() { - ticker := time.NewTicker(WebsocketFileTicker) - idleTicks := 0 - closeTicks := int(IdleWebsocketTimeout / WebsocketFileTicker) - - defer func() { - err = renderer.Stop() - if err != nil { - fmt.Println("error stopping hugo:", err) - } - }() - - for { - <-ticker.C - ch.mtx.Lock() - - // render if there are connected clients OR if we haven't - // been idle for very long. - if es.clients > 0 || idleTicks < 2 { - err := renderer.Render(es.doc.Contents()) - if err != nil { - fmt.Println("error saving document contents:", err) - } - } - - // count up idle time and possibly close down - if es.clients == 0 { - idleTicks++ - - if idleTicks >= closeTicks { - idleTime := time.Duration(idleTicks) * WebsocketFileTicker - err := ch.Publish() - fmt.Printf("idle for %v, quitting\n", idleTime) - if err != nil { - fmt.Printf(", error publishing: %v\n", err) - } - - es.doc.Close() - os.RemoveAll(es.tmpdir) - delete(ch.docs, es.filename) - ch.mtx.Unlock() - return - } - } else { - idleTicks = 0 - } - ch.mtx.Unlock() - } - }() + proc = idleshut.New(HugoCmdProcessConfig(ch, es, f)) return nil } diff --git a/hugo.go b/hugo.go index 83de9b5..377353e 100644 --- a/hugo.go +++ b/hugo.go @@ -5,7 +5,8 @@ import ( "io/ioutil" "os" "os/exec" - "sync" + + "git.stephensearles.com/stephen/idleshut" ) type HugoInteractor interface { @@ -13,55 +14,56 @@ type HugoInteractor interface { } type HugoRenderer interface { + WriteContent(contents string) error Start() error Stop() error } -type HugoCmdRenderer struct { - SrcDir string - TmpDir string - Filename string - - cmd *exec.Cmd - mtx sync.Mutex -} +func HugoCmdProcessConfig(ch *CaddyHugo, es *editSession, touchFn func()) idleshut.Config { + cmd := exec.Command("hugo", "--watch", "-D", "-d", es.tmpdir) + cmd.Dir = es.tmpdir -func (hcr *HugoCmdRenderer) Render(contents string) error { - err := ioutil.WriteFile(hcr.Filename, []byte(contents), 0644) - return err -} + return idleshut.Config{ + Tick: WebsocketFileTicker, + MaxIdleTicks: uint(IdleWebsocketTimeout/WebsocketFileTicker) + 1, + Start: func() error { -func (hcr *HugoCmdRenderer) Start() error { - hcr.mtx.Lock() - defer hcr.mtx.Unlock() + err := cmd.Start() + if err != nil { + return fmt.Errorf("error starting hugo: %v", err) + } - hcr.cmd = exec.Command("hugo", "--watch", "-D", "-d", hcr.TmpDir) - hcr.cmd.Dir = hcr.TmpDir + return nil + }, + Stop: func() error { + if cmd == nil { + return nil + } - err := hcr.cmd.Start() - if err != nil { - return fmt.Errorf("error starting hugo: %v", err) - } + err := cmd.Process.Signal(os.Interrupt) + if err != nil { + return fmt.Errorf("error signalling hugo to stop: %v", err) + } + err = cmd.Wait() + if err != nil { + return fmt.Errorf("error waiting for hugo to stop: %v", err) + return err + } - return nil -} + es.doc.Close() + os.RemoveAll(es.tmpdir) + delete(ch.docs, es.filename) -func (hcr *HugoCmdRenderer) Stop() error { - hcr.mtx.Lock() - defer hcr.mtx.Unlock() - - if hcr.cmd == nil { - return nil - } - - err := hcr.cmd.Process.Signal(os.Interrupt) - if err != nil { - return err + return nil + }, + IdleProcessError: func(err error) { + fmt.Println("error processing draft:", err) + }, + IdleTick: func() error { + return ioutil.WriteFile(es.filename, []byte(es.doc.Contents()), 0644) + }, + ActiveTick: func() error { + return ioutil.WriteFile(es.filename, []byte(es.doc.Contents()), 0644) + }, } - err = hcr.cmd.Wait() - if err != nil { - return err - } - - return nil }