182 lines
3.5 KiB
Go
182 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
"tipitypy/colorizer"
|
|
"tipitypy/db"
|
|
"tipitypy/reader"
|
|
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
func finish(start time.Time, stat *db.Stat, finished bool) {
|
|
past := time.Since(start)
|
|
stat.Skipped = globalStat.Skipped
|
|
stat.Correct = globalStat.Correct
|
|
stat.False = globalStat.False
|
|
stat.Words = globalStat.Words
|
|
stat.Finished = finished
|
|
stat.CPM = float64(globalStat.Correct) / past.Minutes()
|
|
stat.WPM = float64(globalStat.Words) / past.Minutes()
|
|
stat.TimeTaken = past.Milliseconds()
|
|
fmt.Printf("\r\n%s", colorizer.Colors.Reset())
|
|
fmt.Print(past)
|
|
fmt.Printf("\r\n")
|
|
}
|
|
|
|
func ChooseToPrintColorized(r rune) string {
|
|
if Color {
|
|
return colorizer.Accept(r)
|
|
}
|
|
return string(r)
|
|
}
|
|
|
|
type ExitState int
|
|
|
|
const (
|
|
ExitStateUnspecified = 1 + iota
|
|
ExitStateBreak
|
|
ExitStateQuit
|
|
ExitStateSuccess
|
|
)
|
|
|
|
func SingleCycle() (*db.Stat, int, error) {
|
|
words, err := reader.GetSourceWords()
|
|
if err != nil {
|
|
return nil, ExitStateUnspecified, fmt.Errorf("reader.GetSourceLine: %w", err)
|
|
}
|
|
source := strings.Join(words, " ")
|
|
log.Print("Start of tipitypy")
|
|
if Color {
|
|
colorizer.PrintColorized(source)
|
|
} else {
|
|
fmt.Println(source)
|
|
}
|
|
fmt.Printf("\r\n")
|
|
//
|
|
wordIdx := 0
|
|
wordRunesI := 0
|
|
p := []rune(source)
|
|
i := 0
|
|
//
|
|
finished := true
|
|
stat := &db.Stat{
|
|
InHardMode: HardMode,
|
|
}
|
|
reader := bufio.NewReader(os.Stdin)
|
|
startTime := time.Now()
|
|
defer finish(startTime, stat, finished)
|
|
for {
|
|
r, _, err := reader.ReadRune()
|
|
if err != nil {
|
|
return stat, ExitStateUnspecified, fmt.Errorf("reader.ReadRune: %w", err)
|
|
}
|
|
log.Printf("Read %c ; %d as rune", r, r)
|
|
|
|
// CTRL + Q
|
|
if r == 17 {
|
|
finished = false
|
|
return stat, ExitStateQuit, nil
|
|
}
|
|
// CTRL + C
|
|
if r == 3 {
|
|
finished = false
|
|
return stat, ExitStateBreak, nil
|
|
}
|
|
// CTRL + D
|
|
if r == 4 {
|
|
// TODO: Delete
|
|
}
|
|
// CTRL + S
|
|
if r == 19 {
|
|
globalStat.Skipped++
|
|
fmt.Printf("%s", ChooseToPrintColorized(p[i]))
|
|
i++
|
|
continue
|
|
}
|
|
// Check
|
|
if p[i] == r {
|
|
globalStat.Correct++
|
|
fmt.Printf("%s", ChooseToPrintColorized(r))
|
|
i++
|
|
wordRunesI++
|
|
if r == ' ' {
|
|
globalStat.Words++
|
|
wordIdx++
|
|
wordRunesI = 0
|
|
}
|
|
if i == len(p) {
|
|
break
|
|
}
|
|
} else {
|
|
if HardMode && r != ' ' {
|
|
log.Printf("removing %d runes", wordRunesI)
|
|
fmt.Printf("%s", strings.Repeat("\b", wordRunesI))
|
|
colorizer.ClearFromCursorToEnd()
|
|
i -= wordRunesI
|
|
wordRunesI = 0
|
|
}
|
|
globalStat.False++
|
|
}
|
|
}
|
|
globalStat.Words++
|
|
return stat, ExitStateSuccess, nil
|
|
}
|
|
|
|
func main() {
|
|
logFile, err := os.OpenFile("ttt.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer logFile.Close()
|
|
log.SetOutput(logFile)
|
|
ParseOptions()
|
|
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
|
|
|
// Put the terminal in raw mode to read characters as they are typed
|
|
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
|
if err != nil {
|
|
fmt.Printf("term.MakeRaw: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
defer term.Restore(int(os.Stdin.Fd()), oldState) // Restore terminal state at the end
|
|
// This way we can still defer term.Restore function
|
|
c := make(chan bool, 1)
|
|
c <- true
|
|
if IsEndless {
|
|
go func() {
|
|
for {
|
|
c <- true
|
|
}
|
|
}()
|
|
} else {
|
|
close(c)
|
|
}
|
|
//
|
|
outer:
|
|
for range c {
|
|
stat, state, err := SingleCycle()
|
|
if err != nil {
|
|
log.Printf("ERROR: %v", err)
|
|
}
|
|
switch state {
|
|
case ExitStateQuit:
|
|
break outer
|
|
case ExitStateSuccess:
|
|
if err := db.Connect().Create(stat).Error; err != nil {
|
|
log.Printf("ERROR: dbc.Create: %v", err)
|
|
}
|
|
}
|
|
globalStat.Reset()
|
|
}
|
|
}
|