From 8bf3f5515fd201e4cc7c5529da396f32f0356ce4 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 10:27:38 -0700 Subject: [PATCH 01/12] boilerplate tip cmd --- pkg/cmd/root/root.go | 2 ++ pkg/cmd/tip/tip.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 pkg/cmd/tip/tip.go diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index d1b5d9a9dad..67b63407017 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -19,6 +19,7 @@ import ( releaseCmd "github.com/cli/cli/pkg/cmd/release" repoCmd "github.com/cli/cli/pkg/cmd/repo" creditsCmd "github.com/cli/cli/pkg/cmd/repo/credits" + tipCmd "github.com/cli/cli/pkg/cmd/tip" versionCmd "github.com/cli/cli/pkg/cmd/version" "github.com/cli/cli/pkg/cmdutil" "github.com/spf13/cobra" @@ -68,6 +69,7 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) *cobra.Command { cmd.AddCommand(creditsCmd.NewCmdCredits(f, nil)) cmd.AddCommand(gistCmd.NewCmdGist(f)) cmd.AddCommand(completionCmd.NewCmdCompletion(f.IOStreams)) + cmd.AddCommand(tipCmd.NewCmdTip(f, nil)) // the `api` command should not inherit any extra HTTP headers bareHTTPCmdFactory := *f diff --git a/pkg/cmd/tip/tip.go b/pkg/cmd/tip/tip.go new file mode 100644 index 00000000000..7588ea2b758 --- /dev/null +++ b/pkg/cmd/tip/tip.go @@ -0,0 +1,42 @@ +package tip + +import ( + "fmt" + + "github.com/cli/cli/pkg/cmdutil" + "github.com/cli/cli/pkg/iostreams" + "github.com/spf13/cobra" +) + +type TipOptions struct { + IO *iostreams.IOStreams + + Character string +} + +func NewCmdTip(f *cmdutil.Factory, runF func(*TipOptions) error) *cobra.Command { + opts := &TipOptions{ + IO: f.IOStreams, + } + + cmd := &cobra.Command{ + Use: "tip", + Short: "get a random tip about using gh", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if runF != nil { + return runF(opts) + } + return tipRun(opts) + }, + } + + cmd.Flags().StringVarP(&opts.Character, "character", "c", "clippy", "What helpful animated character you'd like a tip from") + + return cmd +} + +func tipRun(opts *TipOptions) error { + fmt.Println("hey") + return nil +} From 29c61ba3a169b3313d56abc61ea9c27e6b4a9c15 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 11:15:01 -0700 Subject: [PATCH 02/12] rename to protip --- pkg/cmd/{tip/tip.go => protip/protip.go} | 18 +++++++++--------- pkg/cmd/root/root.go | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) rename pkg/cmd/{tip/tip.go => protip/protip.go} (56%) diff --git a/pkg/cmd/tip/tip.go b/pkg/cmd/protip/protip.go similarity index 56% rename from pkg/cmd/tip/tip.go rename to pkg/cmd/protip/protip.go index 7588ea2b758..5a1f9eee5b3 100644 --- a/pkg/cmd/tip/tip.go +++ b/pkg/cmd/protip/protip.go @@ -1,4 +1,4 @@ -package tip +package protip import ( "fmt" @@ -8,35 +8,35 @@ import ( "github.com/spf13/cobra" ) -type TipOptions struct { +type ProtipOptions struct { IO *iostreams.IOStreams Character string } -func NewCmdTip(f *cmdutil.Factory, runF func(*TipOptions) error) *cobra.Command { - opts := &TipOptions{ +func NewCmdProtip(f *cmdutil.Factory, runF func(*ProtipOptions) error) *cobra.Command { + opts := &ProtipOptions{ IO: f.IOStreams, } cmd := &cobra.Command{ - Use: "tip", - Short: "get a random tip about using gh", + Use: "protip", + Short: "get a random protip about using gh", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if runF != nil { return runF(opts) } - return tipRun(opts) + return protipRun(opts) }, } - cmd.Flags().StringVarP(&opts.Character, "character", "c", "clippy", "What helpful animated character you'd like a tip from") + cmd.Flags().StringVarP(&opts.Character, "character", "c", "clippy", "What helpful animated character you'd like a protip from") return cmd } -func tipRun(opts *TipOptions) error { +func protipRun(opts *ProtipOptions) error { fmt.Println("hey") return nil } diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index 67b63407017..bd8b6fb6d02 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -16,10 +16,10 @@ import ( gistCmd "github.com/cli/cli/pkg/cmd/gist" issueCmd "github.com/cli/cli/pkg/cmd/issue" prCmd "github.com/cli/cli/pkg/cmd/pr" + protipCmd "github.com/cli/cli/pkg/cmd/protip" releaseCmd "github.com/cli/cli/pkg/cmd/release" repoCmd "github.com/cli/cli/pkg/cmd/repo" creditsCmd "github.com/cli/cli/pkg/cmd/repo/credits" - tipCmd "github.com/cli/cli/pkg/cmd/tip" versionCmd "github.com/cli/cli/pkg/cmd/version" "github.com/cli/cli/pkg/cmdutil" "github.com/spf13/cobra" @@ -69,7 +69,7 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) *cobra.Command { cmd.AddCommand(creditsCmd.NewCmdCredits(f, nil)) cmd.AddCommand(gistCmd.NewCmdGist(f)) cmd.AddCommand(completionCmd.NewCmdCompletion(f.IOStreams)) - cmd.AddCommand(tipCmd.NewCmdTip(f, nil)) + cmd.AddCommand(protipCmd.NewCmdProtip(f, nil)) // the `api` command should not inherit any extra HTTP headers bareHTTPCmdFactory := *f From e0452748d7cd141bcc5b50b76cf518f5b458d3ac Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 11:50:31 -0700 Subject: [PATCH 03/12] setup tcell --- go.mod | 1 + go.sum | 6 ++++ pkg/cmd/protip/protip.go | 73 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index fe0d39b7b36..481441f05d7 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/briandowns/spinner v1.11.1 github.com/charmbracelet/glamour v0.2.1-0.20200724174618-1246d13c1684 github.com/enescakir/emoji v1.0.0 + github.com/gdamore/tcell/v2 v2.0.0 github.com/google/go-cmp v0.5.2 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/go-version v1.2.1 diff --git a/go.sum b/go.sum index b3659cb766b..41254b58c41 100644 --- a/go.sum +++ b/go.sum @@ -68,6 +68,11 @@ github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkK github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU= +github.com/gdamore/tcell/v2 v2.0.0 h1:GRWG8aLfWAlekj9Q6W29bVvkHENc6hp79XOqG4AWDOs= +github.com/gdamore/tcell/v2 v2.0.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -341,6 +346,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/pkg/cmd/protip/protip.go b/pkg/cmd/protip/protip.go index 5a1f9eee5b3..907b4fab1e2 100644 --- a/pkg/cmd/protip/protip.go +++ b/pkg/cmd/protip/protip.go @@ -1,10 +1,12 @@ package protip import ( - "fmt" + "time" "github.com/cli/cli/pkg/cmdutil" "github.com/cli/cli/pkg/iostreams" + "github.com/gdamore/tcell/v2" + "github.com/mattn/go-runewidth" "github.com/spf13/cobra" ) @@ -37,6 +39,73 @@ func NewCmdProtip(f *cmdutil.Factory, runF func(*ProtipOptions) error) *cobra.Co } func protipRun(opts *ProtipOptions) error { - fmt.Println("hey") + tcell.SetEncodingFallback(tcell.EncodingFallbackASCII) + + s, err := tcell.NewScreen() + if err != nil { + return err + } + if err = s.Init(); err != nil { + return err + } + + s.SetStyle(tcell.StyleDefault. + Foreground(tcell.ColorGreen). + Background(tcell.ColorBlack)) + s.Clear() + + quit := make(chan struct{}) + go func() { + for { + ev := s.PollEvent() + switch ev := ev.(type) { + case *tcell.EventKey: + switch ev.Rune() { + case 'q': + close(quit) + return + } + switch ev.Key() { + case tcell.KeyEscape, tcell.KeyEnter: + close(quit) + return + case tcell.KeyCtrlL: + s.Sync() + } + case *tcell.EventResize: + s.Sync() + } + } + }() + +loop: + for { + select { + case <-quit: + break loop + case <-time.After(time.Millisecond * 50): + } + w, h := s.Size() + s.Clear() + emitStr(s, w/2-7, h/2, tcell.StyleDefault, "TODO a protip and stuff") + s.Show() + } + + s.Fini() + return nil } + +func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) { + for _, c := range str { + var comb []rune + w := runewidth.RuneWidth(c) + if w == 0 { + comb = []rune{c} + c = ' ' + w = 1 + } + s.SetContent(x, y, c, comb, style) + x += w + } +} From f3086c9e7b9be42177c775698f016584502ab277 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 12:43:14 -0700 Subject: [PATCH 04/12] start on sprites --- pkg/cmd/protip/protip.go | 22 ++++++++++ pkg/cmd/protip/sprites.go | 91 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 pkg/cmd/protip/sprites.go diff --git a/pkg/cmd/protip/protip.go b/pkg/cmd/protip/protip.go index 907b4fab1e2..3888b7bb329 100644 --- a/pkg/cmd/protip/protip.go +++ b/pkg/cmd/protip/protip.go @@ -1,6 +1,7 @@ package protip import ( + "strings" "time" "github.com/cli/cli/pkg/cmdutil" @@ -14,11 +15,16 @@ type ProtipOptions struct { IO *iostreams.IOStreams Character string + Sprites map[string]*Sprite + Sprite *Sprite } func NewCmdProtip(f *cmdutil.Factory, runF func(*ProtipOptions) error) *cobra.Command { opts := &ProtipOptions{ IO: f.IOStreams, + Sprites: map[string]*Sprite{ + "clippy": Clippy(), + }, } cmd := &cobra.Command{ @@ -26,6 +32,9 @@ func NewCmdProtip(f *cmdutil.Factory, runF func(*ProtipOptions) error) *cobra.Co Short: "get a random protip about using gh", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { + opts.Character = strings.ToLower(opts.Character) + // TODO error handling + opts.Sprite = opts.Sprites[opts.Character] if runF != nil { return runF(opts) } @@ -109,3 +118,16 @@ func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) { x += w } } + +// Want to be able to have a sprite that occupies a rectangle and can animate internally relative to +// its own geometry. + +type Sprite struct { + Width int + Height int + Frames []string +} + +func (s *Sprite) AddFrame(f string) { + s.Frames = append(s.Frames, f) +} diff --git a/pkg/cmd/protip/sprites.go b/pkg/cmd/protip/sprites.go new file mode 100644 index 00000000000..6a4dac1447e --- /dev/null +++ b/pkg/cmd/protip/sprites.go @@ -0,0 +1,91 @@ +package protip + +func Clippy() *Sprite { + s := &Sprite{15, 15} + s.AddFrame(` + ___ + ^ ^ + O O + | | + || | + ||_/ / + | | + | | + \___/ + +`) + s.AddFrame(` + ___ + ^ ^ + O O + | | + || | + ||_/ - + | | + | | + \___/ + +`) + s.AddFrame(` + ___ + ^ ^ + O O + | | + || | + ||_/ / + | | + | | + \___/ + +`) + s.AddFrame(` + ___ + ^ ^ + o O + | | + || | + ||_/ / + | | + | | + \___/ + +`) + s.AddFrame(` + ___ + ^ ^ + - O + | | + || | + ||_/ / + | | + | | + \___/ + +`) + s.AddFrame(` + ___ + ^ ^ + o O + | | + || | + ||_/ / + | | + | | + \___/ + +`) + s.AddFrame(` + ___ + ^ ^ + O O + | | + || | + ||_/ / + | | + | | + \___/ + +`) + + return s +} From c7a28bd8a027899fce8f1f681f068305d6903f17 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 12:48:24 -0700 Subject: [PATCH 05/12] sprites --- pkg/cmd/protip/protip.go | 10 ---------- pkg/cmd/protip/sprites.go | 20 +++++++++++++++++++- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/pkg/cmd/protip/protip.go b/pkg/cmd/protip/protip.go index 3888b7bb329..bdbf3755f48 100644 --- a/pkg/cmd/protip/protip.go +++ b/pkg/cmd/protip/protip.go @@ -121,13 +121,3 @@ func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) { // Want to be able to have a sprite that occupies a rectangle and can animate internally relative to // its own geometry. - -type Sprite struct { - Width int - Height int - Frames []string -} - -func (s *Sprite) AddFrame(f string) { - s.Frames = append(s.Frames, f) -} diff --git a/pkg/cmd/protip/sprites.go b/pkg/cmd/protip/sprites.go index 6a4dac1447e..3605a6a5296 100644 --- a/pkg/cmd/protip/sprites.go +++ b/pkg/cmd/protip/sprites.go @@ -1,7 +1,25 @@ package protip +type Sprite struct { + Width int + Height int + Frames []string + frameIx int +} + +func (s *Sprite) AddFrame(f string) { + s.Frames = append(s.Frames, f) +} + +func (s *Sprite) IncrFrame() { + s.frameIx = (s.frameIx + 1) % len(s.Frames) +} + func Clippy() *Sprite { - s := &Sprite{15, 15} + s := &Sprite{ + Width: 15, + Height: 15, + } s.AddFrame(` ___ ^ ^ From dd307e886da21d392074a8f23a8df65b2c38e98e Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 13:12:57 -0700 Subject: [PATCH 06/12] sprites working but gotta slow em down --- pkg/cmd/protip/protip.go | 18 +++++- pkg/cmd/protip/sprites.go | 124 +++++++++++++++++++++----------------- 2 files changed, 84 insertions(+), 58 deletions(-) diff --git a/pkg/cmd/protip/protip.go b/pkg/cmd/protip/protip.go index bdbf3755f48..10e5c2dce0b 100644 --- a/pkg/cmd/protip/protip.go +++ b/pkg/cmd/protip/protip.go @@ -94,9 +94,11 @@ loop: break loop case <-time.After(time.Millisecond * 50): } - w, h := s.Size() + //w, h := s.Size() + //w, _ := s.Size() s.Clear() - emitStr(s, w/2-7, h/2, tcell.StyleDefault, "TODO a protip and stuff") + //emitStr(s, w/2-7, h/2, tcell.StyleDefault, "TODO a protip and stuff") + drawSprite(s, 0, 0, tcell.StyleDefault, opts.Sprite) s.Show() } @@ -119,5 +121,17 @@ func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) { } } +func drawSprite(s tcell.Screen, x, y int, st tcell.Style, sp *Sprite) { + // TODO worry about animation later + cells := sp.Cells() + for y := 0; y < len(cells); y++ { + row := cells[y] + for x := 0; x < len(row); x++ { + s.SetContent(x, y, cells[y][x], nil, st) + } + } + sp.IncrFrame() +} + // Want to be able to have a sprite that occupies a rectangle and can animate internally relative to // its own geometry. diff --git a/pkg/cmd/protip/sprites.go b/pkg/cmd/protip/sprites.go index 3605a6a5296..2184417156c 100644 --- a/pkg/cmd/protip/sprites.go +++ b/pkg/cmd/protip/sprites.go @@ -1,5 +1,7 @@ package protip +import "strings" + type Sprite struct { Width int Height int @@ -15,93 +17,103 @@ func (s *Sprite) IncrFrame() { s.frameIx = (s.frameIx + 1) % len(s.Frames) } +func (s *Sprite) Cells() [][]rune { + out := [][]rune{} + curFrame := s.Frames[s.frameIx] + lines := strings.Split(curFrame, "\n") + for _, line := range lines { + out = append(out, []rune(line)) + } + return out +} + func Clippy() *Sprite { s := &Sprite{ Width: 15, Height: 15, } s.AddFrame(` - ___ - ^ ^ - O O - | | + ___ + ^ ^ + O O + | | || | - ||_/ / - | | - | | - \___/ + ||_/ / + | | + | | + \___/ `) s.AddFrame(` - ___ - ^ ^ - O O - | | + ___ + ^ ^ + O O + | | || | - ||_/ - - | | - | | - \___/ + ||_/ - + | | + | | + \___/ `) s.AddFrame(` - ___ - ^ ^ - O O - | | + ___ + ^ ^ + O O + | | || | - ||_/ / - | | - | | - \___/ + ||_/ / + | | + | | + \___/ `) s.AddFrame(` - ___ - ^ ^ - o O - | | + ___ + ^ ^ + o O + | | || | - ||_/ / - | | - | | - \___/ + ||_/ / + | | + | | + \___/ `) s.AddFrame(` - ___ - ^ ^ - - O - | | + ___ + ^ ^ + - O + | | || | - ||_/ / - | | - | | - \___/ + ||_/ / + | | + | | + \___/ `) s.AddFrame(` - ___ - ^ ^ - o O - | | + ___ + ^ ^ + o O + | | || | - ||_/ / - | | - | | - \___/ + ||_/ / + | | + | | + \___/ `) s.AddFrame(` - ___ - ^ ^ - O O - | | + ___ + ^ ^ + O O + | | || | - ||_/ / - | | - | | - \___/ + ||_/ / + | | + | | + \___/ `) From 7862e312715b664c7392c6e158a0b76603bbf971 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 14:17:16 -0700 Subject: [PATCH 07/12] draw protip --- pkg/cmd/protip/protip.go | 61 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/protip/protip.go b/pkg/cmd/protip/protip.go index 10e5c2dce0b..9eb94662c62 100644 --- a/pkg/cmd/protip/protip.go +++ b/pkg/cmd/protip/protip.go @@ -50,6 +50,8 @@ func NewCmdProtip(f *cmdutil.Factory, runF func(*ProtipOptions) error) *cobra.Co func protipRun(opts *ProtipOptions) error { tcell.SetEncodingFallback(tcell.EncodingFallbackASCII) + style := tcell.StyleDefault + s, err := tcell.NewScreen() if err != nil { return err @@ -58,7 +60,7 @@ func protipRun(opts *ProtipOptions) error { return err } - s.SetStyle(tcell.StyleDefault. + s.SetStyle(style. Foreground(tcell.ColorGreen). Background(tcell.ColorBlack)) s.Clear() @@ -92,13 +94,17 @@ loop: select { case <-quit: break loop - case <-time.After(time.Millisecond * 50): + case <-time.After(time.Millisecond * 500): } //w, h := s.Size() //w, _ := s.Size() s.Clear() - //emitStr(s, w/2-7, h/2, tcell.StyleDefault, "TODO a protip and stuff") - drawSprite(s, 0, 0, tcell.StyleDefault, opts.Sprite) + drawSprite(s, 0, 0, style, opts.Sprite) + tipLines := []string{ + "To merge a PR, review it until", + "it is approved.", + } + drawProtip(s, opts.Sprite.Width+2, style, tipLines) s.Show() } @@ -107,6 +113,53 @@ loop: return nil } +func drawProtip(s tcell.Screen, startX int, st tcell.Style, tipLines []string) { + // Should look like this: + /* + + *--------------------------------* + | To merge a PR, review it until | + | it is approved. | + |________________________________* + / + + */ + + width := len(tipLines[0]) + 5 + pad := func(s string) string { + out := " | " + s + spaces := width - len(out) - 1 + for i := 0; i < spaces; i++ { + out += " " + } + out += "|" + + return out + } + + // draw top border + topBorder := " *" + bottomBorder := " |" + for x := 0; x < width-4; x++ { + topBorder += "-" + bottomBorder += "_" + } + topBorder += "-*" + bottomBorder += "_*" + + emitStr(s, startX, 0, st, topBorder) + + y := 1 + + for iy, line := range tipLines { + emitStr(s, startX, y+iy, st, pad(line)) + y += iy + } + + emitStr(s, startX, y+1, st, bottomBorder) + emitStr(s, startX, y+2, st, "/") +} + func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) { for _, c := range str { var comb []rune From 9a914aa251206cac9979e6d6ea0495d7a2f15be3 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 14:23:02 -0700 Subject: [PATCH 08/12] protip tweaks --- pkg/cmd/protip/protip.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/protip/protip.go b/pkg/cmd/protip/protip.go index 9eb94662c62..fce0b94b9ba 100644 --- a/pkg/cmd/protip/protip.go +++ b/pkg/cmd/protip/protip.go @@ -104,7 +104,7 @@ loop: "To merge a PR, review it until", "it is approved.", } - drawProtip(s, opts.Sprite.Width+2, style, tipLines) + drawProtip(s, opts.Sprite.Width, 1, style, tipLines) s.Show() } @@ -113,7 +113,7 @@ loop: return nil } -func drawProtip(s tcell.Screen, startX int, st tcell.Style, tipLines []string) { +func drawProtip(s tcell.Screen, startX, startY int, st tcell.Style, tipLines []string) { // Should look like this: /* @@ -147,9 +147,9 @@ func drawProtip(s tcell.Screen, startX int, st tcell.Style, tipLines []string) { topBorder += "-*" bottomBorder += "_*" - emitStr(s, startX, 0, st, topBorder) + emitStr(s, startX, startY, st, topBorder) - y := 1 + y := startY + 1 for iy, line := range tipLines { emitStr(s, startX, y+iy, st, pad(line)) From 2e438818c7a237165ef4f692f654c59e1102e9d5 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 14:42:11 -0700 Subject: [PATCH 09/12] add some protips --- pkg/cmd/protip/protips.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 pkg/cmd/protip/protips.go diff --git a/pkg/cmd/protip/protips.go b/pkg/cmd/protip/protips.go new file mode 100644 index 00000000000..042a246e439 --- /dev/null +++ b/pkg/cmd/protip/protips.go @@ -0,0 +1,30 @@ +package protip + +import ( + "math/rand" + "time" +) + +func getProtip() []string { + rand.Seed(time.Now().UnixNano()) + tips := [][]string{ + { + "To merge a pull request, review it", + "until it is approved", + }, + { + "To have gh use a different editor you can run:", + "gh config set editor ", + }, + { + "If you prefer to use gh over ssh you can run:", + "gh config set git_protocol ssh", + }, + { + "You can search through issues using grep:", + "gh issue list -L200 | grep 'search term'", + }, + } + + return tips[rand.Intn(len(tips))] +} From 0ded65c8e3487e9369fdbc2d83d713d9dfcd76ab Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 15:03:43 -0700 Subject: [PATCH 10/12] finalize protip function and add manatee --- pkg/cmd/protip/protip.go | 27 +++++------- pkg/cmd/protip/sprites.go | 88 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 17 deletions(-) diff --git a/pkg/cmd/protip/protip.go b/pkg/cmd/protip/protip.go index fce0b94b9ba..0c11183fc75 100644 --- a/pkg/cmd/protip/protip.go +++ b/pkg/cmd/protip/protip.go @@ -23,7 +23,8 @@ func NewCmdProtip(f *cmdutil.Factory, runF func(*ProtipOptions) error) *cobra.Co opts := &ProtipOptions{ IO: f.IOStreams, Sprites: map[string]*Sprite{ - "clippy": Clippy(), + "clippy": Clippy(), + "manatee": Manatee(), }, } @@ -50,6 +51,8 @@ func NewCmdProtip(f *cmdutil.Factory, runF func(*ProtipOptions) error) *cobra.Co func protipRun(opts *ProtipOptions) error { tcell.SetEncodingFallback(tcell.EncodingFallbackASCII) + tip := getProtip() + style := tcell.StyleDefault s, err := tcell.NewScreen() @@ -96,15 +99,9 @@ loop: break loop case <-time.After(time.Millisecond * 500): } - //w, h := s.Size() - //w, _ := s.Size() s.Clear() drawSprite(s, 0, 0, style, opts.Sprite) - tipLines := []string{ - "To merge a PR, review it until", - "it is approved.", - } - drawProtip(s, opts.Sprite.Width, 1, style, tipLines) + drawProtip(s, opts.Sprite.Width, 1, style, tip) s.Show() } @@ -147,20 +144,20 @@ func drawProtip(s tcell.Screen, startX, startY int, st tcell.Style, tipLines []s topBorder += "-*" bottomBorder += "_*" - emitStr(s, startX, startY, st, topBorder) + drawStr(s, startX, startY, st, topBorder) y := startY + 1 for iy, line := range tipLines { - emitStr(s, startX, y+iy, st, pad(line)) + drawStr(s, startX, y+iy, st, pad(line)) y += iy } - emitStr(s, startX, y+1, st, bottomBorder) - emitStr(s, startX, y+2, st, "/") + drawStr(s, startX, y+1, st, bottomBorder) + drawStr(s, startX, y+2, st, "/") } -func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) { +func drawStr(s tcell.Screen, x, y int, style tcell.Style, str string) { for _, c := range str { var comb []rune w := runewidth.RuneWidth(c) @@ -175,7 +172,6 @@ func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) { } func drawSprite(s tcell.Screen, x, y int, st tcell.Style, sp *Sprite) { - // TODO worry about animation later cells := sp.Cells() for y := 0; y < len(cells); y++ { row := cells[y] @@ -185,6 +181,3 @@ func drawSprite(s tcell.Screen, x, y int, st tcell.Style, sp *Sprite) { } sp.IncrFrame() } - -// Want to be able to have a sprite that occupies a rectangle and can animate internally relative to -// its own geometry. diff --git a/pkg/cmd/protip/sprites.go b/pkg/cmd/protip/sprites.go index 2184417156c..4a17097819a 100644 --- a/pkg/cmd/protip/sprites.go +++ b/pkg/cmd/protip/sprites.go @@ -27,6 +27,94 @@ func (s *Sprite) Cells() [][]rune { return out } +func Manatee() *Sprite { + s := &Sprite{ + Width: 40, + Height: 14, + } + + s.AddFrame(` + _.---.._ + _ _.-' ''-. + .' '-,_.-' '''. + ( _ o : + '._ .-' '-._ \ \- ---] + '-.___.-') )..-' + (_/ + + + +(art by MJP) +`) + s.AddFrame(` + + _.---.._ + _ _.-' ''-. + .' '-,_.-' '''. + ( _ o : + '._ .-' '-._ \ \- ---] + '-.___.-') )..-' + (_/ + + +(art by MJP) +`) + s.AddFrame(` + + + _.---.._ + _ _.-' ''-. + .' '-,_.-' '''. + ( _ o : + '._ .-' '-._ \ \- ---] + '-.___.-') )..-' + (_/ + +(art by MJP) +`) + s.AddFrame(` + + + + _.---.._ + _ _.-' ''-. + .' '-,_.-' '''. + ( _ o : + '._ .-' '-._ \ \- ---] + '-.___.-') )..-' + (_/ +(art by MJP) +`) + s.AddFrame(` + + + _.---.._ + _ _.-' ''-. + .' '-,_.-' '''. + ( _ o : + '._ .-' '-._ \ \- ---] + '-.___.-') )..-' + (_/ + +(art by MJP) +`) + s.AddFrame(` + + _.---.._ + _ _.-' ''-. + .' '-,_.-' '''. + ( _ o : + '._ .-' '-._ \ \- ---] + '-.___.-') )..-' + (_/ + + +(art by MJP) +`) + + return s +} + func Clippy() *Sprite { s := &Sprite{ Width: 15, From 31649a1cf0e9f37f0591582fcecb94a4679f99b3 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 15:09:01 -0700 Subject: [PATCH 11/12] go mod tidy --- go.sum | 1 - 1 file changed, 1 deletion(-) diff --git a/go.sum b/go.sum index 41254b58c41..5ff63fb8e36 100644 --- a/go.sum +++ b/go.sum @@ -70,7 +70,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU= github.com/gdamore/tcell/v2 v2.0.0 h1:GRWG8aLfWAlekj9Q6W29bVvkHENc6hp79XOqG4AWDOs= github.com/gdamore/tcell/v2 v2.0.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= From 043811e2d7b6f8cbf4ae711f62f6f79e4427d4a1 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Fri, 30 Oct 2020 15:30:53 -0700 Subject: [PATCH 12/12] check character key --- pkg/cmd/protip/protip.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/protip/protip.go b/pkg/cmd/protip/protip.go index 0c11183fc75..683d09ebab8 100644 --- a/pkg/cmd/protip/protip.go +++ b/pkg/cmd/protip/protip.go @@ -1,6 +1,7 @@ package protip import ( + "fmt" "strings" "time" @@ -34,8 +35,12 @@ func NewCmdProtip(f *cmdutil.Factory, runF func(*ProtipOptions) error) *cobra.Co Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { opts.Character = strings.ToLower(opts.Character) - // TODO error handling - opts.Sprite = opts.Sprites[opts.Character] + if s, ok := opts.Sprites[opts.Character]; ok { + opts.Sprite = s + } else { + return fmt.Errorf("no such character: %s", opts.Character) + } + if runF != nil { return runF(opts) }