diff options
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | go.mod | 1 | ||||
-rw-r--r-- | go.sum | 3 | ||||
-rw-r--r-- | golings/cmd/print.go | 72 | ||||
-rw-r--r-- | golings/cmd/root.go | 1 | ||||
-rw-r--r-- | golings/cmd/run.go | 34 | ||||
-rw-r--r-- | golings/cmd/watch.go | 96 |
7 files changed, 199 insertions, 14 deletions
@@ -1,2 +1,8 @@ test: @cd golings && go test -coverprofile=coverage.out -v $$(go list ./... | grep -v fixtures/error1) + +watch: + @go run golings/golings.go watch + +run: + @go run golings/golings.go run
\ No newline at end of file @@ -4,6 +4,7 @@ go 1.19 require ( github.com/fatih/color v1.13.0 + github.com/fsnotify/fsnotify v1.6.0 github.com/jedib0t/go-pretty/v6 v6.4.0 github.com/onsi/ginkgo/v2 v2.5.0 github.com/onsi/gomega v1.24.0 @@ -4,6 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= @@ -59,6 +61,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= diff --git a/golings/cmd/print.go b/golings/cmd/print.go new file mode 100644 index 0000000..c5565c7 --- /dev/null +++ b/golings/cmd/print.go @@ -0,0 +1,72 @@ +package cmd + +import ( + "os" + "os/exec" + "runtime" + + "github.com/fatih/color" + "github.com/mauricioabreu/golings/golings/exercises" + "github.com/mauricioabreu/golings/golings/ui" +) + +func PrintHint(infoFile string) { + ClearScreen() + exercise, err := exercises.NextPending(infoFile) + if err != nil { + color.Red("Failed to find next exercises") + } + color.Yellow(exercise.Hint) +} + +func PrintList(infoFile string) { + ClearScreen() + exs, err := exercises.List(infoFile) + if err != nil { + color.Red("Failed to list exercises") + } + ui.PrintList(os.Stdout, exs) +} + +func RunNextExercise(infoFile string) { + ClearScreen() + exercise, err := exercises.NextPending(infoFile) + if err != nil { + color.Red("Failed to find next exercises") + } + + result, err := exercise.Run() + if err != nil { + color.Cyan("Failed to compile the exercise %s\n\n", result.Exercise.Path) + color.White("Check the output below: \n\n") + color.Red(result.Err) + color.Red(result.Out) + color.Yellow("If you feel stuck, ask a hint by executing `golings hint %s`", result.Exercise.Name) + } else { + color.Green("Congratulations!\n\n") + color.Green("Here is the output of your program:\n\n") + color.Cyan(result.Out) + if result.Exercise.State() == exercises.Pending { + color.White("Remove the 'I AM NOT DONE' from the file to keep going\n") + color.Red("exercise is still pending") + } + } +} + +func ClearScreen() { + if runtime.GOOS == "windows" { + cmd := exec.Command("cmd", "/c", "cls") + cmd.Stdout = os.Stdout + err := cmd.Run() + if err != nil { + color.Red("Clear terminal command error") + } + } else { + cmd := exec.Command("clear") + cmd.Stdout = os.Stdout + err := cmd.Run() + if err != nil { + color.Red("Clear terminal command error") + } + } +} diff --git a/golings/cmd/root.go b/golings/cmd/root.go index 4f79311..23b4583 100644 --- a/golings/cmd/root.go +++ b/golings/cmd/root.go @@ -19,6 +19,7 @@ func NewRootCmd(version string) *cobra.Command { rootCmd.AddCommand(ListCmd("info.toml")) rootCmd.AddCommand(RunCmd("info.toml")) rootCmd.AddCommand(VerifyCmd("info.toml")) + rootCmd.AddCommand(WatchCmd("info.toml")) return rootCmd } diff --git a/golings/cmd/run.go b/golings/cmd/run.go index 552c234..7d5fef8 100644 --- a/golings/cmd/run.go +++ b/golings/cmd/run.go @@ -27,20 +27,7 @@ func RunCmd(infoFile string) *cobra.Command { exercise, err = exercises.Find(args[0], infoFile) } - spinner := progressbar.NewOptions( - -1, // a negative number makes turns the progress bar into a spinner - progressbar.OptionEnableColorCodes(true), - progressbar.OptionSetDescription(color.WhiteString("Running exercise: %s", exercise.Name)), - progressbar.OptionOnCompletion(func() { - color.White("\nRunning complete!\n\n") - }), - ) - go func() { - for x := 0; x < 100; x++ { - spinner.Add(1) // nolint - time.Sleep(250 * time.Millisecond) - } - }() + spinner := RunSpinner(exercise.Name) if errors.Is(err, exercises.ErrExerciseNotFound) { color.White("No exercise found for '%s'", args[0]) @@ -71,3 +58,22 @@ func RunCmd(infoFile string) *cobra.Command { }, } } + +func RunSpinner(exercise string) *progressbar.ProgressBar { + spinner := progressbar.NewOptions( + -1, // a negative number makes turns the progress bar into a spinner + progressbar.OptionEnableColorCodes(true), + progressbar.OptionSetDescription(color.WhiteString("Running exercise: %s", exercise)), + progressbar.OptionOnCompletion(func() { + color.White("\nRunning complete!\n\n") + }), + ) + go func() { + for x := 0; x < 100; x++ { + spinner.Add(1) // nolint + time.Sleep(250 * time.Millisecond) + } + }() + + return spinner +} diff --git a/golings/cmd/watch.go b/golings/cmd/watch.go new file mode 100644 index 0000000..4693e3f --- /dev/null +++ b/golings/cmd/watch.go @@ -0,0 +1,96 @@ +package cmd + +import ( + "bufio" + "fmt" + "io/fs" + "log" + "os" + "path/filepath" + "strings" + + "github.com/fatih/color" + "github.com/fsnotify/fsnotify" + "github.com/spf13/cobra" +) + +func WatchCmd(infoFile string) *cobra.Command { + return &cobra.Command{ + Use: "watch", + Short: "Run a single exercise", + RunE: func(cmd *cobra.Command, args []string) error { + RunNextExercise(infoFile) + reader := bufio.NewReader(os.Stdin) + update := make(chan string) + + go WatchEvents(update) + + for { + go func() { + for range update { + RunNextExercise(infoFile) + } + }() + + cmdString, err := reader.ReadString('\n') + if err != nil { + fmt.Fprintln(os.Stderr, err) + } + cmdStr := strings.TrimSuffix(cmdString, "\n") + + switch cmdStr { + case "list": + PrintList(infoFile) + case "hint": + PrintHint(infoFile) + case "quit": + color.Green("Bye by golings o/") + os.Exit(0) + case "q": + color.Green("Bye by golings o/") + os.Exit(0) + case "exit": + color.Green("Bye by golings o/") + os.Exit(0) + default: + color.Yellow("only list or hint command are avaliable") + } + } + }, + } +} + +func WatchEvents(updateF chan<- string) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + + path, _ := os.Getwd() + directories := fmt.Sprintf("%s/exercises", path) + + err = filepath.WalkDir(directories, func(path_dir string, d fs.DirEntry, err error) error { + if err != nil { + log.Fatal(err) + return err + } + if d.IsDir() { + err = watcher.Add(path_dir) + + if err != nil { + log.Fatal(err) + } + } + return nil + }) + + if err != nil { + log.Fatal("Error in file path:", err.Error()) + } + + for event := range watcher.Events { + if event.Has(fsnotify.Write) { + updateF <- event.Name + } + } +} |