feat: added Pominal
This is the first commit of Pominal, a Pomodoro application for the terminal. This commit includes: - Timers for work sessions, short breaks and long breaks. - Flags to allow users to configure Pominal to their preferences. - Graceful shutdown when a SIGINT or SIGTERM is sent to the application.
This commit is contained in:
parent
322ec7b8f0
commit
a68cc534b5
4 changed files with 200 additions and 2 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
tags
|
||||
Pominal
|
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
|
@ -0,0 +1 @@
|
|||
* @dananglin
|
|
@ -1,3 +1,3 @@
|
|||
# pominal
|
||||
# Pominal
|
||||
|
||||
Pominal is a Pomodoro application in Go for the terminal.
|
||||
Pominal is a Pomodoro application for the terminal. Pominal is written in Go.
|
||||
|
|
195
main.go
Normal file
195
main.go
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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()
|
||||
}
|
Loading…
Reference in a new issue