mirror of
https://github.com/makew0rld/amfora.git
synced 2024-12-04 14:46:29 -05:00
Switched from Write to ReadFrom for all renderers and finished gemtext renderer
This commit is contained in:
parent
d2d5846b0e
commit
fedc1fd11a
@ -2,37 +2,42 @@ package render
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
urlPkg "net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/makeworld-the-better-one/amfora/config"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"gitlab.com/tslocum/cview"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Renderer for gemtext. Other Renderers are in renderer.go.
|
// Renderer for gemtext. Other Renderers are in renderer.go.
|
||||||
|
|
||||||
type GemtextRenderer struct {
|
type GemtextRenderer struct {
|
||||||
r *io.PipeReader
|
// Buffers and I/O
|
||||||
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
|
|
||||||
|
|
||||||
|
r *io.PipeReader
|
||||||
|
w *io.PipeWriter
|
||||||
links chan string
|
links chan string
|
||||||
|
|
||||||
// numLinks is the number of links that exist so far.
|
// Configurable options
|
||||||
numLinks int
|
|
||||||
// width is the number of columns to wrap to.
|
// width is the number of columns to wrap to.
|
||||||
width int
|
width int
|
||||||
// proxied is whether the request is through the gemini:// scheme.
|
// proxied is whether the request is through the gemini:// scheme.
|
||||||
proxied bool
|
proxied bool
|
||||||
|
ansiEnabled bool
|
||||||
|
colorEnabled bool
|
||||||
|
|
||||||
|
// State
|
||||||
|
|
||||||
// pre indicates whether the renderer is currently in a preformatted block
|
// pre indicates whether the renderer is currently in a preformatted block
|
||||||
// or not.
|
// or not.
|
||||||
pre bool
|
pre bool
|
||||||
|
// numLinks is the number of links that exist so far.
|
||||||
|
numLinks int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGemtextRenderer.
|
// NewGemtextRenderer.
|
||||||
@ -43,29 +48,268 @@ type GemtextRenderer struct {
|
|||||||
// 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()
|
pr, pw := io.Pipe()
|
||||||
scanReader, scanWriter := io.Pipe()
|
|
||||||
scanner := bufio.NewScanner(scanReader)
|
|
||||||
links := make(chan string, 10)
|
links := make(chan string, 10)
|
||||||
|
|
||||||
return &GemtextRenderer{
|
ansiEnabled := false
|
||||||
r: pr,
|
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
|
||||||
w: pw,
|
ansiEnabled = true
|
||||||
scanner: scanner,
|
|
||||||
scanWriter: scanWriter,
|
|
||||||
lineEnd: make([]byte, 0),
|
|
||||||
links: links,
|
|
||||||
numLinks: 0,
|
|
||||||
width: width,
|
|
||||||
proxied: proxied,
|
|
||||||
pre: false,
|
|
||||||
}
|
}
|
||||||
|
colorEnabled := false
|
||||||
|
if viper.GetBool("a-general.color") {
|
||||||
|
colorEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return &GemtextRenderer{
|
||||||
|
r: pr,
|
||||||
|
w: pw,
|
||||||
|
links: links,
|
||||||
|
width: width,
|
||||||
|
proxied: proxied,
|
||||||
|
ansiEnabled: ansiEnabled,
|
||||||
|
colorEnabled: colorEnabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderLine handles all lines except preformatted markings. The input line
|
||||||
|
// should not end with any line delimiters, but the output line does.
|
||||||
|
func (r *GemtextRenderer) renderLine(line string) string {
|
||||||
|
if r.pre {
|
||||||
|
if r.ansiEnabled {
|
||||||
|
line = cview.TranslateANSI(line)
|
||||||
|
// The TranslateANSI function 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.
|
||||||
|
line = strings.ReplaceAll(line, "[-:-:-]",
|
||||||
|
fmt.Sprintf("[%s:%s:-]", config.GetColorString("preformatted_text"), config.GetColorString("bg")),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set color at beginning and end of line to prevent background glitches
|
||||||
|
// where the terminal background color slips through. This only happens on
|
||||||
|
// preformatted blocks with ANSI characters.
|
||||||
|
line = fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")) +
|
||||||
|
line + fmt.Sprintf("[%s:%s:-]", config.GetColorString("regular_text"), config.GetColorString("bg"))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
line = ansiRegex.ReplaceAllString(line, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
return line + "\n"
|
||||||
|
}
|
||||||
|
// Not preformatted, regular lines
|
||||||
|
|
||||||
|
wrappedLines := make([]string, 0) // Final result
|
||||||
|
|
||||||
|
// ANSI not allowed in regular text - see #59
|
||||||
|
line = ansiRegex.ReplaceAllString(line, "")
|
||||||
|
|
||||||
|
if strings.HasPrefix(line, "#") {
|
||||||
|
// Headings
|
||||||
|
var tag string
|
||||||
|
if viper.GetBool("a-general.color") {
|
||||||
|
if strings.HasPrefix(line, "###") {
|
||||||
|
tag = fmt.Sprintf("[%s::b]", config.GetColorString("hdg_3"))
|
||||||
|
} else if strings.HasPrefix(line, "##") {
|
||||||
|
tag = fmt.Sprintf("[%s::b]", config.GetColorString("hdg_2"))
|
||||||
|
} else if strings.HasPrefix(line, "#") {
|
||||||
|
tag = fmt.Sprintf("[%s::b]", config.GetColorString("hdg_1"))
|
||||||
|
}
|
||||||
|
wrappedLines = append(wrappedLines, wrapLine(line, r.width, tag, "[-::-]", true)...)
|
||||||
|
} else {
|
||||||
|
// Just bold, no colors
|
||||||
|
wrappedLines = append(wrappedLines, wrapLine(line, r.width, "[::b]", "[-::-]", true)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Links
|
||||||
|
} else if strings.HasPrefix(line, "=>") && len([]rune(line)) >= 3 {
|
||||||
|
// Trim whitespace and separate link from link text
|
||||||
|
|
||||||
|
line = strings.Trim(line[2:], " \t") // Remove `=>` part too
|
||||||
|
delim := strings.IndexAny(line, " \t") // Whitespace between link and link text
|
||||||
|
|
||||||
|
var url string
|
||||||
|
var linkText string
|
||||||
|
if delim == -1 {
|
||||||
|
// No link text
|
||||||
|
url = line
|
||||||
|
linkText = url
|
||||||
|
} else {
|
||||||
|
// There is link text
|
||||||
|
url = line[:delim]
|
||||||
|
linkText = strings.Trim(line[delim:], " \t")
|
||||||
|
if viper.GetBool("a-general.show_link") {
|
||||||
|
linkText += " (" + url + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(line) == "" || strings.TrimSpace(url) == "" {
|
||||||
|
// Link was just whitespace, return it
|
||||||
|
return "=>\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
r.links <- url
|
||||||
|
r.numLinks++
|
||||||
|
num := r.numLinks // Visible link number, one-indexed
|
||||||
|
|
||||||
|
var indent int
|
||||||
|
if num > 99 {
|
||||||
|
// Indent link text by 3 or more spaces
|
||||||
|
indent = len(strconv.Itoa(num)) + 4 // +4 indent for spaces and brackets
|
||||||
|
} else {
|
||||||
|
// One digit and two digit links have the same spacing - see #60
|
||||||
|
indent = 5 // +4 indent for spaces and brackets, and 1 for link number
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spacing after link number: 1 or 2 spaces?
|
||||||
|
var spacing string
|
||||||
|
if num > 9 {
|
||||||
|
// One space to keep it in line with other links - see #60
|
||||||
|
spacing = " "
|
||||||
|
} else {
|
||||||
|
// One digit numbers use two spaces
|
||||||
|
spacing = " "
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap and add link text
|
||||||
|
// Wrap the link text, but add some spaces to indent the wrapped lines past the link number
|
||||||
|
// Set the style tags
|
||||||
|
// Add them to the first line
|
||||||
|
|
||||||
|
var wrappedLink []string
|
||||||
|
|
||||||
|
if viper.GetBool("a-general.color") {
|
||||||
|
pU, err := urlPkg.Parse(url)
|
||||||
|
if !r.proxied && err == nil &&
|
||||||
|
(pU.Scheme == "" || pU.Scheme == "gemini" || pU.Scheme == "about") {
|
||||||
|
// A gemini link
|
||||||
|
// Add the link text in blue (in a region), and a gray link number to the left of it
|
||||||
|
// Those are the default colors, anyway
|
||||||
|
|
||||||
|
wrappedLink = wrapLine(linkText, r.width,
|
||||||
|
strings.Repeat(" ", indent)+
|
||||||
|
`["`+strconv.Itoa(num-1)+`"][`+config.GetColorString("amfora_link")+`]`,
|
||||||
|
`[-][""]`,
|
||||||
|
false, // Don't indent the first line, it's the one with link number
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add special stuff to first line, like the link number
|
||||||
|
wrappedLink[0] = fmt.Sprintf(`[%s::b][`, config.GetColorString("link_number")) +
|
||||||
|
strconv.Itoa(num) + "[]" + "[-::-]" + spacing +
|
||||||
|
`["` + strconv.Itoa(num-1) + `"][` + config.GetColorString("amfora_link") + `]` +
|
||||||
|
wrappedLink[0] + `[-][""]`
|
||||||
|
} else {
|
||||||
|
// Not a gemini link
|
||||||
|
|
||||||
|
wrappedLink = wrapLine(linkText, r.width,
|
||||||
|
strings.Repeat(" ", indent)+
|
||||||
|
`["`+strconv.Itoa(num-1)+`"][`+config.GetColorString("foreign_link")+`]`,
|
||||||
|
`[-][""]`,
|
||||||
|
false, // Don't indent the first line, it's the one with link number
|
||||||
|
)
|
||||||
|
|
||||||
|
wrappedLink[0] = fmt.Sprintf(`[%s::b][`, config.GetColorString("link_number")) +
|
||||||
|
strconv.Itoa(num) + "[]" + "[-::-]" + spacing +
|
||||||
|
`["` + strconv.Itoa(num-1) + `"][` + config.GetColorString("foreign_link") + `]` +
|
||||||
|
wrappedLink[0] + `[-][""]`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No colors allowed
|
||||||
|
|
||||||
|
wrappedLink = wrapLine(linkText, r.width,
|
||||||
|
strings.Repeat(" ", len(strconv.Itoa(num))+4)+ // +4 for spaces and brackets
|
||||||
|
`["`+strconv.Itoa(num-1)+`"]`,
|
||||||
|
`[""]`,
|
||||||
|
false, // Don't indent the first line, it's the one with link number
|
||||||
|
)
|
||||||
|
|
||||||
|
wrappedLink[0] = `[::b][` + strconv.Itoa(num) + "[][::-] " +
|
||||||
|
`["` + strconv.Itoa(num-1) + `"]` +
|
||||||
|
wrappedLink[0] + `[""]`
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedLines = append(wrappedLines, wrappedLink...)
|
||||||
|
|
||||||
|
// Lists
|
||||||
|
} else if strings.HasPrefix(line, "* ") {
|
||||||
|
if viper.GetBool("a-general.bullets") {
|
||||||
|
// Wrap list item, and indent wrapped lines past the bullet
|
||||||
|
wrappedItem := wrapLine(line[1:], r.width,
|
||||||
|
fmt.Sprintf(" [%s]", config.GetColorString("list_text")),
|
||||||
|
"[-]", false)
|
||||||
|
// Add bullet
|
||||||
|
wrappedItem[0] = fmt.Sprintf(" [%s]\u2022", config.GetColorString("list_text")) +
|
||||||
|
wrappedItem[0] + "[-]"
|
||||||
|
wrappedLines = append(wrappedLines, wrappedItem...)
|
||||||
|
}
|
||||||
|
// Optionally list lines could be colored here too, if color is enabled
|
||||||
|
} else if strings.HasPrefix(line, ">") {
|
||||||
|
// It's a quote line, add extra quote symbols and italics to the start of each wrapped line
|
||||||
|
|
||||||
|
if len(line) == 1 {
|
||||||
|
// Just an empty quote line
|
||||||
|
wrappedLines = append(wrappedLines, fmt.Sprintf("[%s::i]>[-::-]", config.GetColorString("quote_text")))
|
||||||
|
} else {
|
||||||
|
// Remove beginning quote and maybe space
|
||||||
|
line = strings.TrimPrefix(line, ">")
|
||||||
|
line = strings.TrimPrefix(line, " ")
|
||||||
|
wrappedLines = append(wrappedLines,
|
||||||
|
wrapLine(line, r.width, fmt.Sprintf("[%s::i]> ", config.GetColorString("quote_text")),
|
||||||
|
"[-::-]", true)...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if strings.TrimSpace(line) == "" {
|
||||||
|
// Just add empty line without processing
|
||||||
|
wrappedLines = append(wrappedLines, "")
|
||||||
|
} else {
|
||||||
|
// Regular line, just wrap it
|
||||||
|
wrappedLines = append(wrappedLines, wrapLine(line, r.width,
|
||||||
|
fmt.Sprintf("[%s]", config.GetColorString("regular_text")),
|
||||||
|
"[-]", true)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(wrappedLines, "\n") + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ren *GemtextRenderer) ReadFrom(r io.Reader) (int64, error) {
|
||||||
|
// Go through lines, render, and write each line
|
||||||
|
// TODO: Should writes be buffered?
|
||||||
|
|
||||||
|
var n int64
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
scanner.Split(ScanLines)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
n += int64(len(scanner.Bytes()))
|
||||||
|
line := scanner.Text()
|
||||||
|
|
||||||
|
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")),
|
||||||
|
))
|
||||||
|
|
||||||
|
}
|
||||||
|
return n, scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write will panic, use ReadFrom instead.
|
||||||
|
func (r *GemtextRenderer) Write(p []byte) (n int, err error) {
|
||||||
|
// This renderer is line based, and so it can't process arbitrary bytes.
|
||||||
|
// 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 (r *GemtextRenderer) Read(p []byte) (n int, err error) {
|
||||||
|
return r.r.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *GemtextRenderer) Links() <-chan string {
|
func (r *GemtextRenderer) Links() <-chan string {
|
||||||
return r.links
|
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)
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package render
|
package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -12,17 +13,45 @@ import (
|
|||||||
|
|
||||||
// Renderer renderers network bytes into something that can be displayed on a
|
// Renderer renderers network bytes into something that can be displayed on a
|
||||||
// cview.TextView.
|
// cview.TextView.
|
||||||
|
//
|
||||||
|
// Write calls may block if the Lines channel buffer is full.
|
||||||
|
//
|
||||||
|
// Current implementations don't actually implement io.Writer, and calling Write
|
||||||
|
// will panic. ReadFrom should be used instead.
|
||||||
type Renderer interface {
|
type Renderer interface {
|
||||||
io.ReadWriter
|
io.ReadWriter
|
||||||
|
io.ReaderFrom
|
||||||
|
|
||||||
// Links returns a channel that yields Link URLs as they are parsed.
|
// 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
|
// It is buffered. The channel might be closed to indicate links aren't supported
|
||||||
// for this renderer.
|
// for this renderer.
|
||||||
Links() <-chan string
|
Links() <-chan string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScanLines is copied from bufio.ScanLines and is used with bufio.Scanner.
|
||||||
|
// The only difference is that this func doesn't get rid of the end-of-line marker.
|
||||||
|
// This is so that the number of read bytes can be counted correctly in ReadFrom.
|
||||||
|
//
|
||||||
|
// It also simplifes code by no longer having to append a newline character.
|
||||||
|
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
|
if atEOF && len(data) == 0 {
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
if i := bytes.IndexByte(data, '\n'); i >= 0 {
|
||||||
|
// We have a full newline-terminated line.
|
||||||
|
return i + 1, data[0 : i+1], nil
|
||||||
|
}
|
||||||
|
// If we're at EOF, we have a final, non-terminated line. Return it.
|
||||||
|
if atEOF {
|
||||||
|
return len(data), data, nil
|
||||||
|
}
|
||||||
|
// Request more data.
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlaintextRenderer escapes text for cview usage and does nothing else.
|
||||||
type PlaintextRenderer struct {
|
type PlaintextRenderer struct {
|
||||||
*io.PipeReader
|
r *io.PipeReader
|
||||||
w *io.PipeWriter
|
w *io.PipeWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,10 +60,33 @@ func NewPlaintextRenderer() *PlaintextRenderer {
|
|||||||
return &PlaintextRenderer{pr, pw}
|
return &PlaintextRenderer{pr, pw}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ren *PlaintextRenderer) ReadFrom(r io.Reader) (int64, error) {
|
||||||
|
// Go through lines and escape bytes and write each line
|
||||||
|
// TODO: Should writes be buffered?
|
||||||
|
|
||||||
|
var n int64
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
scanner.Split(ScanLines)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
n += int64(len(scanner.Bytes()))
|
||||||
|
|
||||||
|
//nolint:errcheck
|
||||||
|
ren.w.Write(cview.EscapeBytes(scanner.Bytes()))
|
||||||
|
}
|
||||||
|
return n, scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write will panic, use ReadFrom instead.
|
||||||
func (r *PlaintextRenderer) Write(p []byte) (n int, err error) {
|
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
|
// This function would normally use cview.EscapeBytes
|
||||||
// How can this be avoided by users of this func?
|
// But the escaping will fail if the Write bytes end in the middle of a tag
|
||||||
return r.w.Write(cview.EscapeBytes(p))
|
// So instead it just panics, because it should never be used.
|
||||||
|
panic("func Write not allowed for PlaintextRenderer")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PlaintextRenderer) Read(p []byte) (n int, err error) {
|
||||||
|
return r.r.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PlaintextRenderer) Links() <-chan string {
|
func (r *PlaintextRenderer) Links() <-chan string {
|
||||||
@ -43,11 +95,13 @@ func (r *PlaintextRenderer) Links() <-chan string {
|
|||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ANSIRenderer escapes text for cview usage, as well as converting ANSI codes
|
||||||
|
// into cview tags if the config allows it.
|
||||||
type ANSIRenderer struct {
|
type ANSIRenderer struct {
|
||||||
*io.PipeReader
|
r *io.PipeReader
|
||||||
pw *io.PipeWriter
|
w *io.PipeWriter
|
||||||
ansiWriter io.Writer // cview.ANSIWriter
|
ansiWriter io.Writer // cview.ANSIWriter
|
||||||
buf bytes.Buffer
|
buf bytes.Buffer // Where ansiWriter writes to
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewANSIRenderer() *ANSIRenderer {
|
func NewANSIRenderer() *ANSIRenderer {
|
||||||
@ -63,25 +117,56 @@ func NewANSIRenderer() *ANSIRenderer {
|
|||||||
return &ANSIRenderer{pr, pw, ansiWriter, buf}
|
return &ANSIRenderer{pr, pw, ansiWriter, buf}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write will panic, use ReadFrom instead.
|
||||||
func (r *ANSIRenderer) Write(p []byte) (n int, err error) {
|
func (r *ANSIRenderer) Write(p []byte) (n int, err error) {
|
||||||
if r.ansiWriter == nil {
|
// This function would normally use cview.EscapeBytes among other things.
|
||||||
// ANSI disabled
|
// But the escaping will fail if the Write bytes end in the middle of a tag
|
||||||
return r.pw.Write(ansiRegex.ReplaceAll(p, []byte{}))
|
// So instead it just panics, because it should never be used.
|
||||||
}
|
panic("func Write not allowed for ANSIRenderer")
|
||||||
// ANSI enabled
|
}
|
||||||
|
|
||||||
r.buf.Reset()
|
func (ren *ANSIRenderer) ReadFrom(r io.Reader) (int64, error) {
|
||||||
r.ansiWriter.Write(p) // Shouldn't error because everything it writes to are all bytes.Buffer
|
// Go through lines, render, and write each line
|
||||||
return r.pw.Write(
|
// TODO: Should writes be buffered?
|
||||||
// The ANSIWriter injects tags like [-:-:-]
|
|
||||||
// but this will reset the background to use the user's terminal color.
|
var n int64
|
||||||
// These tags need to be replaced with resets that use the theme color.
|
scanner := bufio.NewScanner(r)
|
||||||
bytes.ReplaceAll(
|
scanner.Split(ScanLines)
|
||||||
r.buf.Bytes(),
|
|
||||||
[]byte("[-:-:-]"),
|
for scanner.Scan() {
|
||||||
[]byte(fmt.Sprintf("[-:%s:-]", config.GetColorString("bg"))),
|
n += int64(len(scanner.Bytes()))
|
||||||
),
|
line := scanner.Bytes()
|
||||||
)
|
line = cview.EscapeBytes(line)
|
||||||
|
|
||||||
|
if ren.ansiWriter == nil {
|
||||||
|
// ANSI disabled
|
||||||
|
line = ansiRegex.ReplaceAll(scanner.Bytes(), nil)
|
||||||
|
} else {
|
||||||
|
// ANSI enabled
|
||||||
|
|
||||||
|
ren.buf.Reset()
|
||||||
|
|
||||||
|
// Shouldn't error because everything it writes to are all bytes.Buffer
|
||||||
|
ren.ansiWriter.Write(line) //nolint:errcheck
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
line = bytes.ReplaceAll(
|
||||||
|
ren.buf.Bytes(),
|
||||||
|
[]byte("[-:-:-]"),
|
||||||
|
[]byte(fmt.Sprintf("[-:%s:-]", config.GetColorString("bg"))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ren.w.Write(line) //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ANSIRenderer) Read(p []byte) (n int, err error) {
|
||||||
|
return r.r.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ANSIRenderer) Links() <-chan string {
|
func (r *ANSIRenderer) Links() <-chan string {
|
||||||
|
Loading…
Reference in New Issue
Block a user