diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07a6ab0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +tags +Pominal diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..49cadbc --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @dananglin diff --git a/README.md b/README.md index 5d991b6..84e650c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# pominal +# Pominal -Pominal is a Pomodoro application in Go for the terminal. \ No newline at end of file +Pominal is a Pomodoro application for the terminal. Pominal is written in Go. diff --git a/main.go b/main.go new file mode 100644 index 0000000..e70cd45 --- /dev/null +++ b/main.go @@ -0,0 +1,195 @@ +/* + Pominal + Copyright (C) 2019 Daniel Anglin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package main + +import ( + "flag" + "fmt" + "os" + "os/exec" + "os/signal" + "syscall" + "time" +) + +const ( + workTimerLabel string = "Work" + shortBreakTimerLabel string = "Short break" + longBreakTimerLabel string = "Long break" +) + +var ( + workTimer string + shortBreakTimer string + longBreakTimer string + maxWorkCycles int +) + +func init() { + flag.StringVar(&workTimer, "work-timer", "25m", "sets the timer for your work session.") + flag.StringVar(&shortBreakTimer, "short-break-timer", "5m", "sets the timer for your short break.") + flag.StringVar(&longBreakTimer, "long-break-timer", "20m", "sets the timer for your long break.") + flag.IntVar(&maxWorkCycles, "max-work-cycles", 4, "sets the maximum number of work cycles to complete before taking a long break.") + flag.Parse() +} + +type Pominal struct { + timer *time.Timer + finish time.Time + work time.Duration + shortBreak time.Duration + longBreak time.Duration + maxWorkCycles int + session int + label string +} + +func main() { + + workTimeDuration, err := time.ParseDuration(workTimer) + if err != nil { + fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) + os.Exit(1) + } + + shortBreakTimeDuration, err := time.ParseDuration(shortBreakTimer) + if err != nil { + fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) + os.Exit(1) + } + + longBreakTimeDuration, err := time.ParseDuration(longBreakTimer) + if err != nil { + fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) + os.Exit(1) + } + + pominal := NewPominal(workTimeDuration, shortBreakTimeDuration, longBreakTimeDuration, maxWorkCycles) + + pominal.Run() +} + +// NewPominal creates a new pominal instance +func NewPominal(w, s, l time.Duration, m int) Pominal { + + return Pominal{ + work: w, + shortBreak: s, + longBreak: l, + maxWorkCycles: m, + session: 1, + } +} + +// Run Pominal +func (p *Pominal) Run() { + workCycleCount := 1 + t := time.NewTicker(1 * time.Second) + s := make(chan os.Signal, 1) + + signal.Notify(s, syscall.SIGINT, syscall.SIGTERM) + p.Start(workTimerLabel, workCycleCount) + +infinite: + for { + select { + case sig := <-s: + fmt.Printf("\n\nReceived signal '%s'. Closing Pominal.\n", sig) + p.timer.Stop() + t.Stop() + break infinite + case <-t.C: + printScreen(p.TimeRemaining(), p.session, workCycleCount, p.maxWorkCycles, p.label) + case <-p.timer.C: + fmt.Printf("\n%s timer has finished\n", p.label) + p.timer.Stop() + t.Stop() + time.Sleep(2 * time.Second) + switch p.label { + case workTimerLabel: + if workCycleCount >= p.maxWorkCycles { + p.Start(longBreakTimerLabel, workCycleCount) + } else { + p.Start(shortBreakTimerLabel, workCycleCount) + } + case shortBreakTimerLabel: + workCycleCount++ + p.Start(workTimerLabel, workCycleCount) + case longBreakTimerLabel: + workCycleCount = 1 + p.IncrementCount() + p.Start(workTimerLabel, workCycleCount) + } + t = time.NewTicker(1 * time.Second) + } + } +} + +// Start begins the timer specified by the +// label argument. +func (p *Pominal) Start(label string, workCycleCount int) { + var d time.Duration + + switch label { + case workTimerLabel: + d = p.work + case shortBreakTimerLabel: + d = p.shortBreak + case longBreakTimerLabel: + d = p.longBreak + } + + p.label = label + if p.timer == nil { + p.timer = time.NewTimer(d) + } else { + p.timer.Reset(d) + } + + p.finish = time.Now().Add(d) + + printScreen(p.TimeRemaining(), p.session, workCycleCount, p.maxWorkCycles, p.label) +} + +// IncrementCount increments the pominal session count +func (p *Pominal) IncrementCount() { + p.session++ +} + +// TimeRemaining returns the remaining time left +// on the timer +func (p *Pominal) TimeRemaining() time.Duration { + return p.finish.Sub(time.Now()) +} + +// printScreen prints the details of the Pominal session on screen including +// the current work cycle number, the timer's current label and the time remaining +// on the timer. +func printScreen(remaining time.Duration, pominalCount, workCycle, maxWorkCycle int, label string) { + clearScreen() + fmt.Printf("Pominal session: %d\nWork cycle: %d of %d\n\n", pominalCount, workCycle, maxWorkCycle) + fmt.Printf("Timer: %s\nTime remaining: %02d:%02d", label, int(remaining.Minutes()), int(remaining.Seconds())%60) +} + +// clearScreen clears the terminal screen +func clearScreen() { + cmd := exec.Command("clear") + cmd.Stdout = os.Stdout + cmd.Run() +}