-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add multi input console type to clean up final questions (#46)
* feat: Add multi input console type to clean up final questions cleaning up the final questions in the CLI was a suggestion by Adam. This does this by cleanly showing the other options in a form-like component. * various bits of QOL, console research, debugging, and fixes * run make format * make format/make lint
- Loading branch information
Jake Gealer
authored
Nov 26, 2021
1 parent
36e23f0
commit c63fa42
Showing
45 changed files
with
5,234 additions
and
523 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package console | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"sync" | ||
"sync/atomic" | ||
) | ||
|
||
type chunk struct { | ||
a []byte | ||
n int | ||
} | ||
|
||
type chunkReader struct { | ||
chunks []chunk | ||
pendingErr error | ||
pipe io.Reader | ||
stopLoop uintptr | ||
m sync.Mutex | ||
} | ||
|
||
func (c *chunkReader) loop() { | ||
// Used to stop racing before the start of the loop. | ||
c.m.Unlock() | ||
|
||
for atomic.LoadUintptr(&c.stopLoop) == 0 { | ||
// Read from the pipe. | ||
a := make([]byte, 3) | ||
n, err := c.pipe.Read(a) | ||
if atomic.LoadUintptr(&c.stopLoop) == 1 { | ||
// The loop was stopped in the time since last run. | ||
return | ||
} | ||
|
||
// Handle setting the values in the struct. | ||
c.m.Lock() | ||
if err == nil { | ||
// Append to the chunks. | ||
doAppend := true | ||
for _, v := range c.chunks { | ||
if bytes.Equal(v.a, a) && v.n == n { | ||
doAppend = false | ||
break | ||
} | ||
} | ||
if doAppend { | ||
c.chunks = append(c.chunks, chunk{a: a, n: n}) | ||
} | ||
c.m.Unlock() | ||
} else { | ||
// In this case, put the error and stop the loop. | ||
c.pendingErr = err | ||
c.m.Unlock() | ||
return | ||
} | ||
} | ||
} | ||
|
||
func (c *chunkReader) close() { | ||
atomic.StoreUintptr(&c.stopLoop, 1) | ||
} | ||
|
||
func (c *chunkReader) flush() []chunk { | ||
c.m.Lock() | ||
defer c.m.Unlock() | ||
a := c.chunks | ||
c.chunks = []chunk{} | ||
return a | ||
} | ||
|
||
func newChunkReader(r io.Reader) *chunkReader { | ||
c := chunkReader{chunks: []chunk{}, pipe: r} | ||
c.m.Lock() | ||
go c.loop() | ||
return &c | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package console | ||
|
||
import ( | ||
"os" | ||
"sync" | ||
|
||
"github.com/buger/goterm" | ||
"golang.org/x/term" | ||
) | ||
|
||
type gotermTerminal struct { | ||
raw *term.State | ||
m sync.Mutex | ||
} | ||
|
||
func (*gotermTerminal) Height() int { | ||
return goterm.Height() | ||
} | ||
|
||
func (*gotermTerminal) Width() int { | ||
return goterm.Width() | ||
} | ||
|
||
func (g *gotermTerminal) Print(items ...interface{}) (int, error) { | ||
g.m.Lock() | ||
defer g.m.Unlock() | ||
if g.raw != nil { | ||
_ = term.Restore(0, g.raw) | ||
} | ||
n, err := goterm.Print(items...) | ||
if err == nil && g.raw != nil { | ||
g.raw, err = term.MakeRaw(0) | ||
if err != nil { | ||
return 0, err | ||
} | ||
} | ||
return n, err | ||
} | ||
|
||
func (g *gotermTerminal) Sync(s string) error { | ||
// TODO: There are better ways of doing this. I took a few hours to attempt to resync the screen line by line. | ||
// TODO-1: However, by piping the whole chunk out, I find this stops the tearing a lot. | ||
g.m.Lock() | ||
defer g.m.Unlock() | ||
if g.raw != nil { | ||
_ = term.Restore(0, g.raw) | ||
} | ||
_, err := os.Stdout.Write([]byte(s)) | ||
if err == nil && g.raw != nil { | ||
g.raw, err = term.MakeRaw(0) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return err | ||
} | ||
|
||
func (g *gotermTerminal) Clear() { | ||
g.m.Lock() | ||
defer g.m.Unlock() | ||
if g.raw != nil { | ||
_ = term.Restore(0, g.raw) | ||
} | ||
goterm.Clear() | ||
if g.raw != nil { | ||
g.raw, _ = term.MakeRaw(0) | ||
} | ||
} | ||
|
||
func (g *gotermTerminal) Println(items ...interface{}) (int, error) { | ||
g.m.Lock() | ||
defer g.m.Unlock() | ||
if g.raw != nil { | ||
_ = term.Restore(0, g.raw) | ||
} | ||
n, err := goterm.Println(items...) | ||
if err == nil && g.raw != nil { | ||
g.raw, err = term.MakeRaw(0) | ||
if err != nil { | ||
return 0, err | ||
} | ||
} | ||
return n, err | ||
} | ||
|
||
func (g *gotermTerminal) Flush() { | ||
g.m.Lock() | ||
defer g.m.Unlock() | ||
if g.raw != nil { | ||
_ = term.Restore(0, g.raw) | ||
} | ||
goterm.Flush() | ||
if g.raw != nil { | ||
g.raw, _ = term.MakeRaw(0) | ||
} | ||
} | ||
|
||
func (g *gotermTerminal) SignalInterrupt() { | ||
g.m.Lock() | ||
if g.raw != nil { | ||
_ = term.Restore(0, g.raw) | ||
g.raw = nil | ||
} | ||
g.m.Unlock() | ||
os.Exit(1) | ||
} | ||
|
||
func (g *gotermTerminal) MakeRaw() error { | ||
g.m.Lock() | ||
raw, err := term.MakeRaw(0) | ||
if raw != nil { | ||
g.raw = raw | ||
} | ||
g.m.Unlock() | ||
return err | ||
} | ||
|
||
func (g *gotermTerminal) Unraw() error { | ||
var err error | ||
g.m.Lock() | ||
if g.raw != nil { | ||
err = term.Restore(0, g.raw) | ||
g.raw = nil | ||
} | ||
g.m.Unlock() | ||
return err | ||
} | ||
|
||
func (*gotermTerminal) BufferInputs() bool { | ||
return true | ||
} |
Oops, something went wrong.