Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create UI object for interacting with terminal #13

Open
Polpetta opened this issue Jan 26, 2022 · 0 comments
Open

Create UI object for interacting with terminal #13

Polpetta opened this issue Jan 26, 2022 · 0 comments
Assignees
Labels
enhancement New feature or request

Comments

@Polpetta
Copy link
Contributor

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"

@Polpetta Polpetta added the enhancement New feature or request label Jan 26, 2022
@Polpetta Polpetta self-assigned this Jan 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant