1
0
mirror of https://github.com/makew0rld/amfora.git synced 2024-12-04 14:46:29 -05:00

Gemtext renderer too now

This commit is contained in:
makeworld 2021-05-17 10:44:34 -04:00
parent 74971e51e5
commit c1ecbf71b9
3 changed files with 59 additions and 47 deletions

View File

@ -1,7 +1,6 @@
# Notes # Notes
## Stream (#9) ## Stream (#9)
- Refactor renderers to work with `Write`, maybe drop `ReadFrom` entirely
- Then make handlers and stuff part of `tab` - Then make handlers and stuff part of `tab`
- Go through process of loading a page from the very beginning and line up all the parts - Go through process of loading a page from the very beginning and line up all the parts
- Also handle non-network pages like `about:` pages, where `Raw` already exists and just needs to be rendered - Also handle non-network pages like `about:` pages, where `Raw` already exists and just needs to be rendered

View File

@ -18,8 +18,10 @@ import (
type GemtextRenderer struct { type GemtextRenderer struct {
// Buffers and I/O // Buffers and I/O
r *io.PipeReader readOut *io.PipeReader
w *io.PipeWriter readIn *io.PipeWriter
writeIn *io.PipeWriter
writeOut *io.PipeReader
links chan string links chan string
// Configurable options // Configurable options
@ -79,7 +81,8 @@ func wrapLine(line string, width int, prefix, suffix string, includeFirst bool)
// proxied is whether the request is through the gemini:// scheme. // proxied is whether the request is through the gemini:// scheme.
// If it's not a gemini:// page, set this to true. // If it's not a gemini:// page, set this to true.
func NewGemtextRenderer(width int, proxied bool) *GemtextRenderer { func NewGemtextRenderer(width int, proxied bool) *GemtextRenderer {
pr, pw := io.Pipe() pr1, pw1 := io.Pipe()
pr2, pw2 := io.Pipe()
ansiEnabled := false ansiEnabled := false
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") { if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
@ -90,15 +93,53 @@ func NewGemtextRenderer(width int, proxied bool) *GemtextRenderer {
colorEnabled = true colorEnabled = true
} }
return &GemtextRenderer{ ren := GemtextRenderer{
r: pr, readOut: pr1,
w: pw, readIn: pw1,
writeIn: pw2,
writeOut: pr2,
links: make(chan string, 10), links: make(chan string, 10),
width: width, width: width,
proxied: proxied, proxied: proxied,
ansiEnabled: ansiEnabled, ansiEnabled: ansiEnabled,
colorEnabled: colorEnabled, colorEnabled: colorEnabled,
} }
go ren.handler()
return &ren
}
// handler is supposed to run in a goroutine as soon as the renderer is created.
// It handles the buffering and parsing in the background.
func (ren *GemtextRenderer) handler() {
// Go through lines, render, and write each line
// Splits on lines and drops terminators, unlike the other renderers
scanner := bufio.NewScanner(ren.writeOut)
for scanner.Scan() {
line := scanner.Text()
// Process the one possibly invisible line
if strings.HasPrefix(line, "```") {
ren.pre = !ren.pre
continue
}
// Render line and write it
//nolint:errcheck
ren.readIn.Write([]byte(ren.renderLine(line)))
}
// Everything has been read, no more links
close(ren.links)
if err := scanner.Err(); err != nil {
// Close the ends this func touches, shouldn't matter really
ren.writeOut.CloseWithError(err)
ren.readIn.CloseWithError(err)
}
} }
// renderLine handles all lines except preformatted markings. The input line // renderLine handles all lines except preformatted markings. The input line
@ -310,48 +351,19 @@ func (ren *GemtextRenderer) renderLine(line string) string {
return strings.Join(wrappedLines, "\n") + "\n" return strings.Join(wrappedLines, "\n") + "\n"
} }
func (ren *GemtextRenderer) ReadFrom(r io.Reader) (int64, error) {
// Go through lines, render, and write each line
var n int64
scanner := bufio.NewScanner(r)
scanner.Split(ScanLines)
for scanner.Scan() {
n += int64(len(scanner.Bytes()))
line := scanner.Text()
// Process the one possibly invisible line
if strings.HasPrefix(line, "```") {
ren.pre = !ren.pre
continue
}
// Render line and write it
//nolint:errcheck
ren.w.Write([]byte(
ren.renderLine(strings.TrimRight(line, "\r\n")),
))
}
// Everything has been read, no more links
close(ren.links)
return n, scanner.Err()
}
// Write will panic, use ReadFrom instead.
func (ren *GemtextRenderer) Write(p []byte) (n int, err error) { func (ren *GemtextRenderer) Write(p []byte) (n int, err error) {
// This renderer is line based, and so it can't process arbitrary bytes. return ren.writeIn.Write(p)
// One solution would be to handle rendering on the other end of the pipe,
// the Read call, but it's simpler to just implement ReadFrom.
panic("func Write not allowed for GemtextRenderer")
} }
func (ren *GemtextRenderer) Read(p []byte) (n int, err error) { func (ren *GemtextRenderer) Read(p []byte) (n int, err error) {
return ren.r.Read(p) return ren.readOut.Read(p)
}
func (ren *GemtextRenderer) Close() error {
// Close user-facing ends of the pipes. Shouldn't matter which ends though
ren.writeIn.Close()
ren.readOut.Close()
return nil
} }
func (ren *GemtextRenderer) Links() <-chan string { func (ren *GemtextRenderer) Links() <-chan string {

View File

@ -16,7 +16,8 @@ import (
// cview.TextView. // cview.TextView.
// //
// Calling Close when all writing is done is not a no-op, it will stop the the // Calling Close when all writing is done is not a no-op, it will stop the the
// goroutine that runs for each Renderer. // goroutine that runs for each Renderer, and will also allow the Links channel
// to be closed. Close should be called once all the data has been copied
// //
// Write calls may block if the Lines channel buffer is full. // Write calls may block if the Lines channel buffer is full.
type Renderer interface { type Renderer interface {