Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
chelnak committed Jul 17, 2022
0 parents commit 634c760
Show file tree
Hide file tree
Showing 13 changed files with 364 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json

name: ci

on:
pull_request:
branches:
- main
workflow_dispatch:

env:
GO_VERSION: 1.18

jobs:
lint:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}

- name: lint
uses: golangci/golangci-lint-action@v2
with:
version: latest
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
tag:
@git tag -a $(version) -m "Release $(version)"
@git push --follow-tags

lint:
@golangci-lint run ./...
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# You spin me right round

YSMRR is a simple multi-line spinner pacakge for your terminal.

## Installing

```bash
go get -u github.com/chelnak/ysmrr
```

## Usage


```bash
// Create a new spinner manager
sm := ysmrr.NewSpinnerManager()

// Add a spinner
mySpinner := sm.AddSpinner("Spinny things...")

// Start the spinners that have been added to the group
sm.Start()

// Set the spinner to complete
time.Sleep(2 * time.Second)
mySpinner.Complete()

// Stop the spinners in the group
time.Sleep(2 * time.Second)
sm.Stop()
```

See [](examples/main.go) for a more in-depth example.
11 changes: 11 additions & 0 deletions examples/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module examples

go 1.18

require (
github.com/chelnak/ysmrr v0.0.0-20220717103525-a36685263ba3 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
)
16 changes: 16 additions & 0 deletions examples/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
github.com/chelnak/ysmrr v0.0.0-20220717103525-a36685263ba3 h1:JsDGKKrt6GYOXbkVxsddX9OkXakqsm7Qs8CG9p8Jaaw=
github.com/chelnak/ysmrr v0.0.0-20220717103525-a36685263ba3/go.mod h1:+OYa1/7fcaEDTEcO6oIH12iAbgDVDFJsNBIe2qsVQMw=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
40 changes: 40 additions & 0 deletions examples/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"time"

"github.com/chelnak/ysmrr"
)

func main() {
// Create a new spinner manager
sm := ysmrr.NewSpinnerManager()

// Set up our spinners
downloading := sm.AddSpinner("Downloading...")
installing := sm.AddSpinner("Installing...")
running := sm.AddSpinner("Running...")

// Start the spinners that have been added to the group
sm.Start()

// Set downloading to complete
time.Sleep(2 * time.Second)
downloading.Complete()

// Update the message of the installing spinner
time.Sleep(2 * time.Second)
installing.Update("Installing updated...")

// Set installing to complete
time.Sleep(2 * time.Second)
installing.Complete()

// Set running to error
time.Sleep(2 * time.Second)
running.Error()

// Stop the spinners in the group
time.Sleep(2 * time.Second)
sm.Stop()
}
11 changes: 11 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/chelnak/ysmrr

go 1.18

require github.com/fatih/color v1.13.0

require (
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
)
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
27 changes: 27 additions & 0 deletions golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
linters:
enable:
- dogsled
- dupl
- gofmt
- goimports
- gosec
- misspell
- nakedret
- stylecheck
- unconvert
- unparam
- whitespace
- errcheck
- gosimple
- staticcheck
- ineffassign
- unused
issues:
exclude-rules:
- linters:
- dogsled
text: "declaration has 3 blank identifiers"
path: _test\.go
- linters:
- dupl
path: _test\.go
114 changes: 114 additions & 0 deletions manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Package ysmrr provides a simple interface for creating and managing
// multiple spinners.
package ysmrr

import (
"fmt"
"io"
"os"
"time"

"github.com/chelnak/ysmrr/pkg/tput"
"github.com/fatih/color"
)

var Dots = []string{
"⠋",
"⠙",
"⠹",
"⠸",
"⠼",
"⠴",
"⠦",
"⠧",
"⠇",
"⠏",
}

// SpinnerManager manages spinners
type SpinnerManager interface {
AddSpinner(msg string) *spinner
Start()
Stop()
}

type spinnerManager struct {
Spinners []*spinner
chars []string
frameDuration time.Duration
writer io.Writer
done chan bool
ticks *time.Ticker
pos int
}

// AddSpinner adds a new spinner to the manager
func (sm *spinnerManager) AddSpinner(msg string) *spinner {
c := color.New(color.FgHiGreen)
spinner := NewSpinner(msg, c)

sm.Spinners = append(sm.Spinners, spinner)
return spinner
}

// Start signals that all spinners should start
func (sm *spinnerManager) Start() {
sm.ticks = time.NewTicker(sm.frameDuration)
go sm.render()
}

// Stop signals that all spinners should complete
func (sm *spinnerManager) Stop() {
sm.done <- true
sm.ticks.Stop()
}

func (sm *spinnerManager) setNextPos() {
sm.pos += 1
if sm.pos >= len(sm.chars) {
sm.pos = 0
}
}

func (sm *spinnerManager) renderFrame() {
for _, s := range sm.Spinners {
if s.complete {
fmt.Fprintf(sm.writer, "\r✓ %s\n", s.msg)
} else if s.err {
fmt.Fprintf(sm.writer, "\r✗ %s\n", s.msg)
} else {
s.c.Fprintf(sm.writer, "%s", sm.chars[sm.pos])
fmt.Fprintf(sm.writer, " %s\r", s.msg)
fmt.Fprint(sm.writer, "\n")
}
}
sm.setNextPos()
}

func (sm *spinnerManager) render() {
tput.Sc()

tput.Civis()
defer tput.Cnorm()

for {
select {
case <-sm.done:
return
case <-sm.ticks.C:
sm.renderFrame()
}

tput.Rc()
}
}

// NewSpinnerManager creates a new spinner manager
func NewSpinnerManager() SpinnerManager {
return &spinnerManager{
chars: Dots,
frameDuration: 250 * time.Millisecond,
writer: os.Stdout,
done: make(chan bool),
}
}
25 changes: 25 additions & 0 deletions pkg/tput/tput.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Package tput provides convenience functions for sending escape sequences to the terminal.
// The escape codes used have been derrived from the tput program.
package tput

import "fmt"

// Sc saves the current position of the cursor
func Sc() {
fmt.Printf("\u001b7")
}

// Rc restores the cursor to the saved position
func Rc() {
fmt.Printf("\u001b8")
}

// Civis hides the cursor
func Civis() {
fmt.Printf("\u001b[?25l")
}

// Cnorm shows the cursor
func Cnorm() {
fmt.Printf("\u001b[?12l[?25h")
}
33 changes: 33 additions & 0 deletions spinner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package ysmrr

import "github.com/fatih/color"

// Spinner manages a single spinner
type spinner struct {
c *color.Color
msg string
complete bool
err bool
}

// Update updates the spinner message
func (s *spinner) Update(msg string) {
s.msg = msg
}

// Complete marks the spinner as complete
func (s *spinner) Complete() {
s.complete = true
}

// Error marks the spinner as error
func (s *spinner) Error() {
s.err = true
}

func NewSpinner(msg string, c *color.Color) *spinner {
return &spinner{
c: c,
msg: msg,
}
}

0 comments on commit 634c760

Please sign in to comment.