Printer abstraction

This commit is contained in:
tjp 2024-01-23 22:42:18 -07:00
parent dd2a06c1e1
commit 865c6dc230
6 changed files with 129 additions and 58 deletions

View File

@ -559,43 +559,20 @@ func parseURL(str string, state *BrowserState, defaultScheme string) (*url.URL,
}
func print(state *BrowserState) error {
if state.Modal != nil {
defer func() { state.Modal = nil }()
return state.Printer.PrintModal(state, state.Modal)
}
if state.Body == nil {
return ErrMustBeOnAPage
}
if state.Quiet {
return nil
}
defer func() { state.Modal = nil }()
if state.Body == nil && state.Modal == nil {
return ErrMustBeOnAPage
}
out := []byte(state.Formatted)
if state.Modal != nil {
out = state.Modal
}
if state.Modal != nil || state.Pager == "never" {
_, err := os.Stdout.Write(out)
return err
}
lessarg := []string{}
switch state.Pager {
case "auto":
lessarg = []string{"-F"}
fallthrough
case "always":
less, err := exec.LookPath("less")
if err != nil {
return err
}
cmd := exec.Command(less, lessarg...)
cmd.Stdin = bytes.NewBuffer(out)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
return errors.New("invalid 'pager' value in configuration")
return state.Printer.PrintPage(state, state.Formatted)
}
func Print(state *BrowserState) error {

6
go.mod
View File

@ -10,21 +10,23 @@ require (
require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/bubbles v0.17.1 // indirect
github.com/charmbracelet/bubbletea v0.25.0 // indirect
github.com/charmbracelet/lipgloss v0.9.1 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/text v0.3.8 // indirect
)

8
go.sum
View File

@ -2,8 +2,12 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4=
github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o=
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
@ -27,6 +31,8 @@ github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+Ei
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
@ -50,6 +56,8 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=

20
main.go
View File

@ -44,14 +44,8 @@ func main() {
state.Quiet = true
}
if urls := flag.Args(); len(urls) > 0 {
if err := Go(state, urls[0]); err != nil {
writeError(err.Error())
}
}
// runInteractivePrompt(state)
runTUI(state)
runInteractivePrompt(state, flag.Args())
// runTUI(state, flag.Args())
}
func buildReadline(prompt string, conf *Config) (*readline.Instance, error) {
@ -106,7 +100,15 @@ func buildInitialState() (*BrowserState, error) {
return state, nil
}
func runInteractivePrompt(state *BrowserState) {
func runInteractivePrompt(state *BrowserState, args []string) {
state.Printer = PromptPrinter{}
if len(args) > 0 {
if err := Go(state, args[0]); err != nil {
writeError(err.Error())
}
}
for {
state.Readline.SetPrompt(Prompt)
line, err := state.Readline.Readline()

View File

@ -1,7 +1,11 @@
package main
import (
"bytes"
"errors"
"net/url"
"os"
"os/exec"
"github.com/chzyer/readline"
)
@ -21,6 +25,7 @@ type BrowserState struct {
CurrentTour *Tour
Readline *readline.Instance
Printer Printer
}
type History struct {
@ -60,3 +65,43 @@ func NewBrowserState(conf *Config) *BrowserState {
state.CurrentTour = &state.DefaultTour
return state
}
type Printer interface {
PrintModal(*BrowserState, []byte) error
PrintPage(*BrowserState, string) error
}
type PromptPrinter struct{}
func (_ PromptPrinter) PrintModal(state *BrowserState, contents []byte) error {
_, err := os.Stdout.Write(contents)
return err
}
func (_ PromptPrinter) PrintPage(state *BrowserState, body string) error {
if state.Quiet {
return nil
}
lessarg := []string{}
switch state.Pager {
case "auto":
lessarg = []string{"-F"}
fallthrough
case "always":
less, err := exec.LookPath("less")
if err != nil {
return err
}
cmd := exec.Command(less, lessarg...)
cmd.Stdin = bytes.NewBufferString(body)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
case "never":
_, err := os.Stdout.WriteString(body)
return err
default:
return errors.New("invalid 'pager' value in configuration")
}
}

65
tui.go
View File

@ -4,40 +4,77 @@ import (
"os"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/bubbles/viewport"
)
type TUIModel struct {
State *BrowserState
Viewport viewport.Model
inited bool
}
func NewTUIModel(state *BrowserState) TUIModel {
return TUIModel{State: state}
func NewTUIModel(state *BrowserState) *TUIModel {
return &TUIModel{State: state}
}
func (model TUIModel) Init() tea.Cmd {
func (model *TUIModel) Init() tea.Cmd {
return nil
}
func (model TUIModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (model *TUIModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "ctrl+d", "q":
case "ctrl+c", "q":
return model, tea.Quit
case "g":
model.Viewport.GotoTop()
return model, nil
case "G":
model.Viewport.GotoBottom()
return model, nil
}
case tea.WindowSizeMsg:
model.inited = true
model.Viewport.Width = msg.Width
model.Viewport.Height = msg.Height - 1
}
var cmd tea.Cmd
model.Viewport, cmd = model.Viewport.Update(msg)
return model, cmd
}
func (model *TUIModel) View() string {
return model.Viewport.View()
}
func runTUI(state *BrowserState, args []string) {
model := NewTUIModel(state)
state.Printer = (*TUIPrinter)(model)
if len(args) > 0 {
if err := Go(state, args[0]); err != nil {
writeError(err.Error())
}
}
return model, nil
}
func (model TUIModel) View() string {
return "pardon our dust"
}
func runTUI(state *BrowserState) {
p := tea.NewProgram(NewTUIModel(state))
p := tea.NewProgram(model, tea.WithAltScreen())
if _, err := p.Run(); err != nil {
writeError(err.Error())
os.Exit(1)
}
}
type TUIPrinter TUIModel
func (p *TUIPrinter) PrintModal(state *BrowserState, contents []byte) error {
(*TUIModel)(p).Viewport.SetContent(string(contents))
return nil
}
func (p *TUIPrinter) PrintPage(state *BrowserState, body string) error {
(*TUIModel)(p).Viewport.SetContent(body)
return nil
}