From d2d5846b0eaffa615048a86bb634fd4f8a99f742 Mon Sep 17 00:00:00 2001 From: makeworld Date: Mon, 1 Mar 2021 19:05:26 -0500 Subject: [PATCH] ANSI and plain renderers done but untested, gemtext started --- render/gemtext.go | 71 ++++++++++++++++++++++++++++++++++++ render/render.go | 3 +- render/renderer.go | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 render/gemtext.go create mode 100644 render/renderer.go diff --git a/render/gemtext.go b/render/gemtext.go new file mode 100644 index 0000000..b13368d --- /dev/null +++ b/render/gemtext.go @@ -0,0 +1,71 @@ +package render + +import ( + "bufio" + "io" +) + +// Renderer for gemtext. Other Renderers are in renderer.go. + +type GemtextRenderer struct { + r *io.PipeReader + w *io.PipeWriter + + // scanner is used to process line by line. + scanner *bufio.Scanner + + // scanWriter is used to send data to the scanner, which reads out of the other + // end of the pipe. + scanWriter *io.PipeWriter + + // lineEnd holds the rest of line when the Read call cuts off the line being returned. + lineEnd []byte + + links chan string + + // numLinks is the number of links that exist so far. + numLinks int + // width is the number of columns to wrap to. + width int + // proxied is whether the request is through the gemini:// scheme. + proxied bool + + // pre indicates whether the renderer is currently in a preformatted block + // or not. + pre bool +} + +// NewGemtextRenderer. +// +// width is the number of columns to wrap to. +// +// proxied is whether the request is through the gemini:// scheme. +// If it's not a gemini:// page, set this to true. +func NewGemtextRenderer(width int, proxied bool) *GemtextRenderer { + pr, pw := io.Pipe() + scanReader, scanWriter := io.Pipe() + scanner := bufio.NewScanner(scanReader) + links := make(chan string, 10) + + return &GemtextRenderer{ + r: pr, + w: pw, + scanner: scanner, + scanWriter: scanWriter, + lineEnd: make([]byte, 0), + links: links, + numLinks: 0, + width: width, + proxied: proxied, + pre: false, + } +} + +func (r *GemtextRenderer) Links() <-chan string { + return r.links +} + +func (r *GemtextRenderer) Write(p []byte) (n int, err error) { + // Just write to the scanner, all logic is in Read() + return r.scanWriter.Write(p) +} diff --git a/render/render.go b/render/render.go index d21bcc2..9036220 100644 --- a/render/render.go +++ b/render/render.go @@ -41,7 +41,7 @@ func RenderPlainText(s string) string { // It used to add a left margin, now this is done elsewhere. // The function is kept for convenience and in case rendering // is needed in the future. - return s + return cview.Escape(s) } // wrapLine wraps a line to the provided width, and adds the provided prefix and suffix to each wrapped line. @@ -270,7 +270,6 @@ func convertRegularGemini(s string, numLinks, width int, proxied bool) (string, // It also returns a slice of link URLs. // // width is the number of columns to wrap to. -// leftMargin is the number of blank spaces to prepend to each line. // // proxied is whether the request is through the gemini:// scheme. // If it's not a gemini:// page, set this to true. diff --git a/render/renderer.go b/render/renderer.go new file mode 100644 index 0000000..68278f2 --- /dev/null +++ b/render/renderer.go @@ -0,0 +1,91 @@ +package render + +import ( + "bytes" + "fmt" + "io" + + "github.com/makeworld-the-better-one/amfora/config" + "github.com/spf13/viper" + "gitlab.com/tslocum/cview" +) + +// Renderer renderers network bytes into something that can be displayed on a +// cview.TextView. +type Renderer interface { + io.ReadWriter + + // Links returns a channel that yields Link URLs as they are parsed. + // It is buffered. The channel might be closed to indicate links are supported + // for this renderer. + Links() <-chan string +} + +type PlaintextRenderer struct { + *io.PipeReader + w *io.PipeWriter +} + +func NewPlaintextRenderer() *PlaintextRenderer { + pr, pw := io.Pipe() + return &PlaintextRenderer{pr, pw} +} + +func (r *PlaintextRenderer) Write(p []byte) (n int, err error) { + // TODO: The escaping will fail if the Write bytes end in the middle of a tag + // How can this be avoided by users of this func? + return r.w.Write(cview.EscapeBytes(p)) +} + +func (r *PlaintextRenderer) Links() <-chan string { + ch := make(chan string) + close(ch) + return ch +} + +type ANSIRenderer struct { + *io.PipeReader + pw *io.PipeWriter + ansiWriter io.Writer // cview.ANSIWriter + buf bytes.Buffer +} + +func NewANSIRenderer() *ANSIRenderer { + pr, pw := io.Pipe() + + var ansiWriter io.Writer = nil // When ANSI is disabled + var buf bytes.Buffer + + if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") { + // ANSI enabled + ansiWriter = cview.ANSIWriter(&buf) + } + return &ANSIRenderer{pr, pw, ansiWriter, buf} +} + +func (r *ANSIRenderer) Write(p []byte) (n int, err error) { + if r.ansiWriter == nil { + // ANSI disabled + return r.pw.Write(ansiRegex.ReplaceAll(p, []byte{})) + } + // ANSI enabled + + r.buf.Reset() + r.ansiWriter.Write(p) // Shouldn't error because everything it writes to are all bytes.Buffer + return r.pw.Write( + // The ANSIWriter injects tags like [-:-:-] + // but this will reset the background to use the user's terminal color. + // These tags need to be replaced with resets that use the theme color. + bytes.ReplaceAll( + r.buf.Bytes(), + []byte("[-:-:-]"), + []byte(fmt.Sprintf("[-:%s:-]", config.GetColorString("bg"))), + ), + ) +} + +func (r *ANSIRenderer) Links() <-chan string { + ch := make(chan string) + close(ch) + return ch +}