-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Labels
enhancementNew feature or requestNew feature or request
Description
We cannot directly interact with the TTY using fmt.Println because this makes the program difficult to test and difficult to change the output render if we ever need it. We have to create an abstraction layer (like UI object) where we can change stuff in the future if we need it.
I attached a draft of module we could use to perform such output to the user.
package main
import (
"github.com/charmbracelet/glamour"
"io"
"os"
)
// StdOutput is a shorthand for io.Writer
type StdOutput io.Writer
// StdError is a shorthand for io.Writer
type StdError io.Writer
// LogLevel indicates the verbosity of what's going to be printed on screen
type LogLevel uint8
// UIOptions is a typedef for setting additional options to the UI
type UIOptions func(*UI, string) string
const (
// DEBUG will print debug information on screen
DEBUG LogLevel = iota
// VERBOSE will print almost everything
VERBOSE
// NORMAL is for output that is printed without any particular flag
NORMAL
// QUIET only prints warnings (error will always be printed on StdError)
QUIET
)
// UI is the final endpoint for writing output to the final user. It represents a TTY, and it has functionalities for rendering the output even in a markdown flavoured fashion
type UI struct {
output StdOutput
error StdError
term *glamour.TermRenderer
}
// WithMarkdownSyntax is an UIOptions to print the content in a markdown rendered output
func WithMarkdownSyntax(ui *UI, content string) string {
render, err := ui.term.Render(content)
if err != nil {
panic(err)
}
return render
}
func (U *UI) out(level LogLevel, s string, opts ...UIOptions) (n int, err error) {
// FIXME loglevel
out := s
for _, opt := range opts {
out = opt(U, s)
}
return U.output.Write([]byte(out))
}
func (U *UI) markdownOut(s string) (n int, err error) {
render, err := U.term.Render(s)
if err != nil {
return 0, err
}
return U.output.Write([]byte(render))
}
func (U *UI) err(s string) (n int, err error) {
return U.error.Write([]byte(s))
}
// NewStdOut returns os.Stdout
func NewStdOut() StdOutput {
return os.Stdout
}
// NewStdErr returns os.Stderr
func NewStdErr() StdError {
return os.Stderr
}
// NewUI creates a brand-new UI struct
func NewUI(out StdOutput, errorOut StdError) *UI {
glamourTermRender, err := glamour.NewTermRenderer(
glamour.WithAutoStyle(),
glamour.WithWordWrap(80),
glamour.WithEmoji(),
)
if err != nil {
panic("Impossible to create terminal. Is this a valid TTY?")
}
return &UI{
output: out,
error: errorOut,
term: glamourTermRender,
}
}This means we have to check out all the fmt.Print{ln}... in the codebase and rewrite it using this object.
Combined with #11 we can easily have this object injected in all the classes we need to output stuff without ever bothering about initializing one "by hand"
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request