diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d7bdd748 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.golden linguist-generated diff --git a/.gitignore b/.gitignore index b3087b09..eafbdee6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ cmd/ +!*.test diff --git a/.golangci.yml b/.golangci.yml index 36f9966b..1d7f0d46 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -14,7 +14,7 @@ linters: - godot - godox - goimports - - gomnd + - mnd - goprintffuncname - gosec - misspell diff --git a/ansi/baseelement.go b/ansi/baseelement.go index d4b6a4f3..6bd29455 100644 --- a/ansi/baseelement.go +++ b/ansi/baseelement.go @@ -38,7 +38,6 @@ func renderText(w io.Writer, p termenv.Profile, rules StylePrimitive, s string) } out := termenv.String(s) - if rules.Upper != nil && *rules.Upper { out = termenv.String(strings.ToUpper(s)) } @@ -79,35 +78,69 @@ func renderText(w io.Writer, p termenv.Profile, rules StylePrimitive, s string) _, _ = io.WriteString(w, out.String()) } +func (e *BaseElement) StyleOverrideRender(w io.Writer, ctx RenderContext, style StylePrimitive) error { + bs := ctx.blockStack + st1 := cascadeStylePrimitives(bs.Current().Style.StylePrimitive, style) + st2 := cascadeStylePrimitives(bs.With(e.Style), style) + + return e.doRender(w, ctx.options.ColorProfile, st1, st2) +} + func (e *BaseElement) Render(w io.Writer, ctx RenderContext) error { bs := ctx.blockStack + st1 := bs.Current().Style.StylePrimitive + st2 := bs.With(e.Style) + return e.doRender(w, ctx.options.ColorProfile, st1, st2) +} - renderText(w, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, e.Prefix) +func (e *BaseElement) doRender(w io.Writer, p termenv.Profile, st1, st2 StylePrimitive) error { + renderText(w, p, st1, e.Prefix) defer func() { - renderText(w, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, e.Suffix) + renderText(w, p, st1, e.Suffix) }() - rules := bs.With(e.Style) // render unstyled prefix/suffix - renderText(w, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, rules.BlockPrefix) + renderText(w, p, st1, st2.BlockPrefix) defer func() { - renderText(w, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, rules.BlockSuffix) + renderText(w, p, st1, st2.BlockSuffix) }() // render styled prefix/suffix - renderText(w, ctx.options.ColorProfile, rules, rules.Prefix) + renderText(w, p, st2, st2.Prefix) defer func() { - renderText(w, ctx.options.ColorProfile, rules, rules.Suffix) + renderText(w, p, st2, st2.Suffix) }() s := e.Token - if len(rules.Format) > 0 { + if len(st2.Format) > 0 { var err error - s, err = formatToken(rules.Format, s) + s, err = formatToken(st2.Format, s) if err != nil { return err } } - renderText(w, ctx.options.ColorProfile, rules, s) + renderText(w, p, st2, escapeReplacer.Replace(s)) return nil } + +// https://www.markdownguide.org/basic-syntax/#characters-you-can-escape +var escapeReplacer = strings.NewReplacer( + "\\\\", "\\", + "\\`", "`", + "\\*", "*", + "\\_", "_", + "\\{", "{", + "\\}", "}", + "\\[", "[", + "\\]", "]", + "\\<", "<", + "\\>", ">", + "\\(", "(", + "\\)", ")", + "\\#", "#", + "\\+", "+", + "\\-", "-", + "\\.", ".", + "\\!", "!", + "\\|", "|", +) diff --git a/ansi/blockelement.go b/ansi/blockelement.go index aed1b658..2ca7bb34 100644 --- a/ansi/blockelement.go +++ b/ansi/blockelement.go @@ -4,7 +4,7 @@ import ( "bytes" "io" - "github.com/muesli/reflow/wordwrap" + "github.com/charmbracelet/x/ansi" ) // BlockElement provides a render buffer for children of a block element. @@ -30,10 +30,14 @@ func (e *BlockElement) Finish(w io.Writer, ctx RenderContext) error { bs := ctx.blockStack if e.Margin { + s := ansi.Wordwrap( + bs.Current().Block.String(), + int(bs.Width(ctx)), + " ,.;-+|", + ) + mw := NewMarginWriter(ctx, w, bs.Current().Style) - _, err := mw.Write( - wordwrap.Bytes(bs.Current().Block.Bytes(), int(bs.Width(ctx)))) - if err != nil { + if _, err := io.WriteString(mw, s); err != nil { return err } diff --git a/ansi/blockstack.go b/ansi/blockstack.go index 5440dcbc..0af22a8f 100644 --- a/ansi/blockstack.go +++ b/ansi/blockstack.go @@ -59,10 +59,10 @@ func (s BlockStack) Margin() uint { // Width returns the available rendering width. func (s BlockStack) Width(ctx RenderContext) uint { - if s.Indent()+s.Margin()*2 > uint(ctx.options.WordWrap) { + if s.Indent()*s.Margin() > uint(ctx.options.WordWrap) { return 0 } - return uint(ctx.options.WordWrap) - s.Indent() - s.Margin()*2 + return uint(ctx.options.WordWrap) - s.Indent()*s.Margin() } // Parent returns the current BlockElement's parent. diff --git a/ansi/codeblock.go b/ansi/codeblock.go index 9b578194..f0c3017f 100644 --- a/ansi/codeblock.go +++ b/ansi/codeblock.go @@ -124,6 +124,7 @@ func (e *CodeBlockElement) Render(w io.Writer, ctx RenderContext) error { if len(theme) > 0 { renderText(iw, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, rules.BlockPrefix) + err := quick.Highlight(iw, e.Code, e.Language, "terminal256", theme) if err != nil { return err diff --git a/ansi/codespan.go b/ansi/codespan.go new file mode 100644 index 00000000..6551bf3f --- /dev/null +++ b/ansi/codespan.go @@ -0,0 +1,14 @@ +package ansi + +import "io" + +// A CodeSpanElement is used to render codespan. +type CodeSpanElement struct { + Text string + Style StylePrimitive +} + +func (e *CodeSpanElement) Render(w io.Writer, ctx RenderContext) error { + renderText(w, ctx.options.ColorProfile, e.Style, e.Style.Prefix+e.Text+e.Style.Suffix) + return nil +} diff --git a/ansi/elements.go b/ansi/elements.go index ce41eb3a..161857ab 100644 --- a/ansi/elements.go +++ b/ansi/elements.go @@ -17,6 +17,10 @@ type ElementRenderer interface { Render(w io.Writer, ctx RenderContext) error } +type StyleOverriderElementRenderer interface { + StyleOverrideRender(w io.Writer, ctx RenderContext, style StylePrimitive) error +} + // ElementFinisher is called when leaving a markdown node. type ElementFinisher interface { Finish(w io.Writer, ctx RenderContext) error @@ -63,8 +67,11 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element { // Paragraph case ast.KindParagraph: - if node.Parent() != nil && node.Parent().Kind() == ast.KindListItem { - return Element{} + if node.Parent() != nil { + kind := node.Parent().Kind() + if kind == ast.KindListItem { + return Element{} + } } return Element{ Renderer: &ParagraphElement{ @@ -76,10 +83,9 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element { // Blockquote case ast.KindBlockquote: e := &BlockElement{ - Block: &bytes.Buffer{}, - Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.options.Styles.BlockQuote, false), - Margin: true, - Newline: true, + Block: &bytes.Buffer{}, + Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.options.Styles.BlockQuote, false), + Margin: true, } return Element{ Entering: "\n", @@ -176,16 +182,16 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element { case ast.KindEmphasis: n := node.(*ast.Emphasis) - s := string(n.Text(source)) - style := ctx.options.Styles.Emph - if n.Level > 1 { - style = ctx.options.Styles.Strong + var children []ElementRenderer + nn := n.FirstChild() + for nn != nil { + children = append(children, tr.NewElement(nn, source).Renderer) + nn = nn.NextSibling() } - return Element{ - Renderer: &BaseElement{ - Token: html.UnescapeString(s), - Style: style, + Renderer: &EmphasisElement{ + Level: n.Level, + Children: children, }, } @@ -213,26 +219,45 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element { // Links case ast.KindLink: n := node.(*ast.Link) + var children []ElementRenderer + nn := n.FirstChild() + for nn != nil { + children = append(children, tr.NewElement(nn, source).Renderer) + nn = nn.NextSibling() + } return Element{ Renderer: &LinkElement{ - Text: textFromChildren(node, source), - BaseURL: ctx.options.BaseURL, - URL: string(n.Destination), + BaseURL: ctx.options.BaseURL, + URL: string(n.Destination), + Children: children, }, } case ast.KindAutoLink: n := node.(*ast.AutoLink) u := string(n.URL(source)) - label := string(n.Label(source)) + + var children []ElementRenderer + nn := n.FirstChild() + for nn != nil { + children = append(children, tr.NewElement(nn, source).Renderer) + nn = nn.NextSibling() + } + + if len(children) == 0 { + children = append(children, &BaseElement{ + Token: u, + }) + } + if n.AutoLinkType == ast.AutoLinkEmail && !strings.HasPrefix(strings.ToLower(u), "mailto:") { u = "mailto:" + u } return Element{ Renderer: &LinkElement{ - Text: label, - BaseURL: ctx.options.BaseURL, - URL: u, + Children: children, + BaseURL: ctx.options.BaseURL, + URL: u, }, } @@ -281,45 +306,43 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element { } case ast.KindCodeSpan: - // n := node.(*ast.CodeSpan) - e := &BlockElement{ - Block: &bytes.Buffer{}, - Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.options.Styles.Code, false), - } + n := node.(*ast.CodeSpan) + s := string(n.Text(source)) return Element{ - Renderer: e, - Finisher: e, + Renderer: &CodeSpanElement{ + Text: html.UnescapeString(s), + Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.options.Styles.Code, false).StylePrimitive, + }, } // Tables case astext.KindTable: table := node.(*astext.Table) - te := &TableElement{table: table} + te := &TableElement{ + table: table, + } return Element{ Entering: "\n", + Exiting: "\n", Renderer: te, Finisher: te, } case astext.KindTableCell: - s := "" - n := node.FirstChild() - for n != nil { - switch t := n.(type) { - case *ast.AutoLink: - s += string(t.Label(source)) - default: - s += string(n.Text(source)) - } - - n = n.NextSibling() + n := node.(*astext.TableCell) + var children []ElementRenderer + nn := n.FirstChild() + for nn != nil { + children = append(children, tr.NewElement(nn, source).Renderer) + nn = nn.NextSibling() } + r := &TableCellElement{ + Children: children, + Head: node.Parent().Kind() == astext.KindTableHeader, + } return Element{ - Renderer: &TableCellElement{ - Text: s, - Head: node.Parent().Kind() == astext.KindTableHeader, - }, + Renderer: r, } case astext.KindTableHeader: @@ -358,13 +381,13 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element { Newline: true, } return Element{ - Entering: "\n", Renderer: e, Finisher: e, } case astext.KindDefinitionTerm: return Element{ + Entering: "\n", Renderer: &BaseElement{ Style: ctx.options.Styles.DefinitionTerm, }, @@ -372,6 +395,7 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element { case astext.KindDefinitionDescription: return Element{ + Exiting: "\n", Renderer: &BaseElement{ Style: ctx.options.Styles.DefinitionDescription, }, @@ -398,21 +422,3 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element { return Element{} } } - -func textFromChildren(node ast.Node, source []byte) string { - var s string - for c := node.FirstChild(); c != nil; c = c.NextSibling() { - if c.Kind() == ast.KindText { - cn := c.(*ast.Text) - s += string(cn.Segment.Value(source)) - - if cn.HardLineBreak() || (cn.SoftLineBreak()) { - s += "\n" - } - } else { - s += string(c.Text(source)) - } - } - - return s -} diff --git a/ansi/emphasis.go b/ansi/emphasis.go new file mode 100644 index 00000000..5e210840 --- /dev/null +++ b/ansi/emphasis.go @@ -0,0 +1,44 @@ +package ansi + +import ( + "io" +) + +// A EmphasisElement is used to render emphasis. +type EmphasisElement struct { + Children []ElementRenderer + Level int +} + +func (e *EmphasisElement) Render(w io.Writer, ctx RenderContext) error { + style := ctx.options.Styles.Emph + if e.Level > 1 { + style = ctx.options.Styles.Strong + } + + return e.doRender(w, ctx, style) +} + +func (e *EmphasisElement) StyleOverrideRender(w io.Writer, ctx RenderContext, style StylePrimitive) error { + base := ctx.options.Styles.Emph + if e.Level > 1 { + base = ctx.options.Styles.Strong + } + return e.doRender(w, ctx, cascadeStylePrimitives(base, style)) +} + +func (e *EmphasisElement) doRender(w io.Writer, ctx RenderContext, style StylePrimitive) error { + for _, child := range e.Children { + if r, ok := child.(StyleOverriderElementRenderer); ok { + if err := r.StyleOverrideRender(w, ctx, style); err != nil { + return err + } + } else { + if err := child.Render(w, ctx); err != nil { + return err + } + } + } + + return nil +} diff --git a/ansi/link.go b/ansi/link.go index 9028395b..054612f3 100644 --- a/ansi/link.go +++ b/ansi/link.go @@ -1,75 +1,48 @@ package ansi import ( + "bytes" "io" "net/url" ) // A LinkElement is used to render hyperlinks. type LinkElement struct { - Text string - BaseURL string - URL string - Child ElementRenderer + BaseURL string + URL string + Children []ElementRenderer } func (e *LinkElement) Render(w io.Writer, ctx RenderContext) error { - var textRendered bool - if len(e.Text) > 0 && e.Text != e.URL { - textRendered = true - - el := &BaseElement{ - Token: e.Text, - Style: ctx.options.Styles.LinkText, - } - err := el.Render(w, ctx) - if err != nil { - return err - } - } - - /* - if node.LastChild != nil { - if node.LastChild.Type == bf.Image { - el := tr.NewElement(node.LastChild) - err := el.Renderer.Render(w, node.LastChild, tr) - if err != nil { - return err - } + for _, child := range e.Children { + if r, ok := child.(StyleOverriderElementRenderer); ok { + st := ctx.options.Styles.LinkText + if err := r.StyleOverrideRender(w, ctx, st); err != nil { + return err + } + } else { + var b bytes.Buffer + if err := child.Render(&b, ctx); err != nil { + return err + } + el := &BaseElement{ + Token: b.String(), + Style: ctx.options.Styles.LinkText, } - if len(node.LastChild.Literal) > 0 && - string(node.LastChild.Literal) != string(node.LinkData.Destination) { - textRendered = true - el := &BaseElement{ - Token: string(node.LastChild.Literal), - Style: ctx.style[LinkText], - } - err := el.Render(w, node.LastChild, tr) - if err != nil { - return err - } + if err := el.Render(w, ctx); err != nil { + return err } } - */ + } u, err := url.Parse(e.URL) - if err == nil && - "#"+u.Fragment != e.URL { // if the URL only consists of an anchor, ignore it - pre := " " - style := ctx.options.Styles.Link - if !textRendered { - pre = "" - style.BlockPrefix = "" - style.BlockSuffix = "" - } - + if err == nil && "#"+u.Fragment != e.URL { // if the URL only consists of an anchor, ignore it el := &BaseElement{ Token: resolveRelativeURL(e.BaseURL, e.URL), - Prefix: pre, - Style: style, + Prefix: " ", + Style: ctx.options.Styles.Link, } - err := el.Render(w, ctx) - if err != nil { + if err := el.Render(w, ctx); err != nil { return err } } diff --git a/ansi/renderer.go b/ansi/renderer.go index e312550b..160bf489 100644 --- a/ansi/renderer.go +++ b/ansi/renderer.go @@ -136,14 +136,10 @@ func (r *ANSIRenderer) renderNode(w util.BufWriter, source []byte, node ast.Node } func isChild(node ast.Node) bool { - if node.Parent() != nil && node.Parent().Kind() == ast.KindBlockquote { - // skip paragraph within blockquote to avoid reflowing text - return true - } for n := node.Parent(); n != nil; n = n.Parent() { // These types are already rendered by their parent switch n.Kind() { - case ast.KindLink, ast.KindImage, ast.KindEmphasis, astext.KindStrikethrough, astext.KindTableCell: + case ast.KindCodeSpan, ast.KindAutoLink, ast.KindLink, ast.KindImage, ast.KindEmphasis, astext.KindStrikethrough, astext.KindTableCell: return true } } diff --git a/ansi/renderer_test.go b/ansi/renderer_test.go index e391e917..056e4b32 100644 --- a/ansi/renderer_test.go +++ b/ansi/renderer_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/charmbracelet/x/exp/golden" "github.com/muesli/termenv" "github.com/yuin/goldmark" emoji "github.com/yuin/goldmark-emoji" @@ -18,10 +19,8 @@ import ( ) const ( - generateExamples = false - generateIssues = false - examplesDir = "../styles/examples/" - issuesDir = "../testdata/issues/" + examplesDir = "../styles/examples/" + issuesDir = "../testdata/issues/" ) func TestRenderer(t *testing.T) { @@ -32,68 +31,50 @@ func TestRenderer(t *testing.T) { for _, f := range files { bn := strings.TrimSuffix(filepath.Base(f), ".md") - sn := filepath.Join(examplesDir, bn+".style") - tn := filepath.Join("../testdata", bn+".test") - - in, err := os.ReadFile(f) - if err != nil { - t.Fatal(err) - } - b, err := os.ReadFile(sn) - if err != nil { - t.Fatal(err) - } - - options := Options{ - WordWrap: 80, - ColorProfile: termenv.TrueColor, - } - err = json.Unmarshal(b, &options.Styles) - if err != nil { - t.Fatal(err) - } - - md := goldmark.New( - goldmark.WithExtensions( - extension.GFM, - extension.DefinitionList, - emoji.Emoji, - ), - goldmark.WithParserOptions( - parser.WithAutoHeadingID(), - ), - ) - - ar := NewRenderer(options) - md.SetRenderer( - renderer.NewRenderer( - renderer.WithNodeRenderers(util.Prioritized(ar, 1000)))) - - var buf bytes.Buffer - err = md.Convert(in, &buf) - if err != nil { - t.Error(err) - } - - // generate - if generateExamples { - err = os.WriteFile(tn, buf.Bytes(), 0o644) + t.Run(bn, func(t *testing.T) { + sn := filepath.Join(examplesDir, bn+".style") + + in, err := os.ReadFile(f) + if err != nil { + t.Fatal(err) + } + b, err := os.ReadFile(sn) if err != nil { t.Fatal(err) } - continue - } - - // verify - td, err := os.ReadFile(tn) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(td, buf.Bytes()) { - t.Errorf("Rendered output for %s doesn't match!\nExpected: `\n%s`\nGot: `\n%s`\n", - bn, string(td), buf.String()) - } + + options := Options{ + WordWrap: 80, + ColorProfile: termenv.TrueColor, + } + err = json.Unmarshal(b, &options.Styles) + if err != nil { + t.Fatal(err) + } + + md := goldmark.New( + goldmark.WithExtensions( + extension.GFM, + extension.DefinitionList, + emoji.Emoji, + ), + goldmark.WithParserOptions( + parser.WithAutoHeadingID(), + ), + ) + + ar := NewRenderer(options) + md.SetRenderer( + renderer.NewRenderer( + renderer.WithNodeRenderers(util.Prioritized(ar, 1000)))) + + var buf bytes.Buffer + if err := md.Convert(in, &buf); err != nil { + t.Error(err) + } + + golden.RequireEqual(t, buf.Bytes()) + }) } } @@ -106,8 +87,6 @@ func TestRendererIssues(t *testing.T) { for _, f := range files { bn := strings.TrimSuffix(filepath.Base(f), ".md") t.Run(bn, func(t *testing.T) { - tn := filepath.Join(issuesDir, bn+".test") - in, err := os.ReadFile(f) if err != nil { t.Fatal(err) @@ -143,30 +122,11 @@ func TestRendererIssues(t *testing.T) { renderer.WithNodeRenderers(util.Prioritized(ar, 1000)))) var buf bytes.Buffer - err = md.Convert(in, &buf) - if err != nil { + if err := md.Convert(in, &buf); err != nil { t.Error(err) } - // generate - if generateIssues { - err = os.WriteFile(tn, buf.Bytes(), 0o644) - if err != nil { - t.Fatal(err) - } - return - } - - // verify - td, err := os.ReadFile(tn) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(td, buf.Bytes()) { - t.Errorf("Rendered output for %s doesn't match!\nExpected: `\n%s`\nGot: `\n%s`\n", - bn, string(td), buf.String()) - } + golden.RequireEqual(t, buf.Bytes()) }) } } diff --git a/ansi/style.go b/ansi/style.go index fd521471..a660a455 100644 --- a/ansi/style.go +++ b/ansi/style.go @@ -140,14 +140,21 @@ type StyleConfig struct { func cascadeStyles(s ...StyleBlock) StyleBlock { var r StyleBlock - for _, v := range s { r = cascadeStyle(r, v, true) } return r } -func cascadeStyle(parent StyleBlock, child StyleBlock, toBlock bool) StyleBlock { +func cascadeStylePrimitives(s ...StylePrimitive) StylePrimitive { + var r StylePrimitive + for _, v := range s { + r = cascadeStylePrimitive(r, v, true) + } + return r +} + +func cascadeStylePrimitive(parent, child StylePrimitive, toBlock bool) StylePrimitive { s := child s.Color = parent.Color @@ -166,8 +173,6 @@ func cascadeStyle(parent StyleBlock, child StyleBlock, toBlock bool) StyleBlock s.Blink = parent.Blink if toBlock { - s.Indent = parent.Indent - s.Margin = parent.Margin s.BlockPrefix = parent.BlockPrefix s.BlockSuffix = parent.BlockSuffix s.Prefix = parent.Prefix @@ -180,12 +185,6 @@ func cascadeStyle(parent StyleBlock, child StyleBlock, toBlock bool) StyleBlock if child.BackgroundColor != nil { s.BackgroundColor = child.BackgroundColor } - if child.Indent != nil { - s.Indent = child.Indent - } - if child.Margin != nil { - s.Margin = child.Margin - } if child.Underline != nil { s.Underline = child.Underline } @@ -240,3 +239,19 @@ func cascadeStyle(parent StyleBlock, child StyleBlock, toBlock bool) StyleBlock return s } + +func cascadeStyle(parent StyleBlock, child StyleBlock, toBlock bool) StyleBlock { + s := child + s.StylePrimitive = cascadeStylePrimitive(parent.StylePrimitive, child.StylePrimitive, toBlock) + + if toBlock { + s.Indent = parent.Indent + s.Margin = parent.Margin + } + + if child.Indent != nil { + s.Indent = child.Indent + } + + return s +} diff --git a/ansi/stylewriter.go b/ansi/stylewriter.go deleted file mode 100644 index 28c01e06..00000000 --- a/ansi/stylewriter.go +++ /dev/null @@ -1,33 +0,0 @@ -package ansi - -import ( - "bytes" - "io" -) - -// StyleWriter is a Writer that applies styling on whatever you write to it. -type StyleWriter struct { - ctx RenderContext - w io.Writer - buf bytes.Buffer - rules StylePrimitive -} - -// NewStyleWriter returns a new StyleWriter. -func NewStyleWriter(ctx RenderContext, w io.Writer, rules StylePrimitive) *StyleWriter { - return &StyleWriter{ - ctx: ctx, - w: w, - rules: rules, - } -} - -func (w *StyleWriter) Write(b []byte) (int, error) { - return w.buf.Write(b) -} - -// Close must be called when you're finished writing to a StyleWriter. -func (w *StyleWriter) Close() error { - renderText(w.w, w.ctx.options.ColorProfile, w.rules, w.buf.String()) - return nil -} diff --git a/ansi/table.go b/ansi/table.go index f6f549da..e8d26f13 100644 --- a/ansi/table.go +++ b/ansi/table.go @@ -1,34 +1,33 @@ package ansi import ( + "bytes" "io" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/table" "github.com/muesli/reflow/indent" - "github.com/olekukonko/tablewriter" astext "github.com/yuin/goldmark/extension/ast" ) // A TableElement is used to render tables. type TableElement struct { - writer *tablewriter.Table - styleWriter *StyleWriter - header []string - cell []string - table *astext.Table + lipgloss *table.Table + table *astext.Table + header []string + row []string } // A TableRowElement is used to render a single row in a table. -type TableRowElement struct { -} +type TableRowElement struct{} // A TableHeadElement is used to render a table's head element. -type TableHeadElement struct { -} +type TableHeadElement struct{} // A TableCellElement is used to render a single cell in a row. type TableCellElement struct { - Text string - Head bool + Children []ElementRenderer + Head bool } func (e *TableElement) Render(w io.Writer, ctx RenderContext) error { @@ -49,76 +48,122 @@ func (e *TableElement) Render(w io.Writer, ctx RenderContext) error { }) style := bs.With(rules.StylePrimitive) - ctx.table.styleWriter = NewStyleWriter(ctx, iw, style) - renderText(w, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, rules.BlockPrefix) - renderText(ctx.table.styleWriter, ctx.options.ColorProfile, style, rules.Prefix) - table := tablewriter.NewWriter(ctx.table.styleWriter) + renderText(iw, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, rules.BlockPrefix) + renderText(iw, ctx.options.ColorProfile, style, rules.Prefix) + width := int(ctx.blockStack.Width(ctx)) + ctx.table.lipgloss = table.New().Width(width) + + return nil +} + +func (e *TableElement) setStyles(ctx RenderContext) { + ctx.table.lipgloss = ctx.table.lipgloss.StyleFunc(func(row, col int) lipgloss.Style { + st := lipgloss.NewStyle().Inline(true) + if m := ctx.options.Styles.Table.Margin; m != nil { + st = st.Padding(0, int(*m)) + } + if row == 0 { + st = st.Bold(true) + } - alignments := make([]int, len(e.table.Alignments)) - for i, a := range e.table.Alignments { - switch a { + switch e.table.Alignments[col] { case astext.AlignLeft: - alignments[i] = tablewriter.ALIGN_LEFT + st = st.Align(lipgloss.Left).PaddingRight(0) case astext.AlignCenter: - alignments[i] = tablewriter.ALIGN_CENTER + st = st.Align(lipgloss.Center) case astext.AlignRight: - alignments[i] = tablewriter.ALIGN_RIGHT + st = st.Align(lipgloss.Right).PaddingLeft(0) } - } - table.SetColumnAlignment(alignments) - ctx.table.writer = table - return nil + return st + }) } -func (e *TableElement) Finish(w io.Writer, ctx RenderContext) error { +func (e *TableElement) setBorders(ctx RenderContext) { rules := ctx.options.Styles.Table - - ctx.table.writer.SetBorders(tablewriter.Border{Left: false, Top: false, Right: false, Bottom: false}) - if rules.CenterSeparator != nil { - ctx.table.writer.SetCenterSeparator(*rules.CenterSeparator) - } - if rules.ColumnSeparator != nil { - ctx.table.writer.SetColumnSeparator(*rules.ColumnSeparator) - } - if rules.RowSeparator != nil { - ctx.table.writer.SetRowSeparator(*rules.RowSeparator) + border := lipgloss.NormalBorder() + + if rules.RowSeparator != nil && rules.ColumnSeparator != nil { + border = lipgloss.Border{ + Top: *rules.RowSeparator, + Bottom: *rules.RowSeparator, + Left: *rules.ColumnSeparator, + Right: *rules.ColumnSeparator, + Middle: *rules.CenterSeparator, + } } + ctx.table.lipgloss.Border(border) + ctx.table.lipgloss.BorderTop(false) + ctx.table.lipgloss.BorderLeft(false) + ctx.table.lipgloss.BorderRight(false) + ctx.table.lipgloss.BorderBottom(false) +} + +func (e *TableElement) Finish(_ io.Writer, ctx RenderContext) error { + rules := ctx.options.Styles.Table - ctx.table.writer.Render() - ctx.table.writer = nil + e.setStyles(ctx) + e.setBorders(ctx) - renderText(ctx.table.styleWriter, ctx.options.ColorProfile, ctx.blockStack.With(rules.StylePrimitive), rules.Suffix) - renderText(ctx.table.styleWriter, ctx.options.ColorProfile, ctx.blockStack.Current().Style.StylePrimitive, rules.BlockSuffix) - return ctx.table.styleWriter.Close() + ow := ctx.blockStack.Current().Block + if _, err := ow.WriteString(ctx.table.lipgloss.String()); err != nil { + return err + } + + renderText(ow, ctx.options.ColorProfile, ctx.blockStack.With(rules.StylePrimitive), rules.Suffix) + renderText(ow, ctx.options.ColorProfile, ctx.blockStack.Current().Style.StylePrimitive, rules.BlockSuffix) + ctx.table.lipgloss = nil + return nil } -func (e *TableRowElement) Finish(w io.Writer, ctx RenderContext) error { - if ctx.table.writer == nil { +func (e *TableRowElement) Finish(_ io.Writer, ctx RenderContext) error { + if ctx.table.lipgloss == nil { return nil } - ctx.table.writer.Append(ctx.table.cell) - ctx.table.cell = []string{} + ctx.table.lipgloss.Row(ctx.table.row...) + ctx.table.row = []string{} return nil } -func (e *TableHeadElement) Finish(w io.Writer, ctx RenderContext) error { - if ctx.table.writer == nil { +func (e *TableHeadElement) Finish(_ io.Writer, ctx RenderContext) error { + if ctx.table.lipgloss == nil { return nil } - ctx.table.writer.SetHeader(ctx.table.header) + ctx.table.lipgloss.Headers(ctx.table.header...) ctx.table.header = []string{} return nil } -func (e *TableCellElement) Render(w io.Writer, ctx RenderContext) error { +func (e *TableCellElement) Render(_ io.Writer, ctx RenderContext) error { + var b bytes.Buffer + style := ctx.options.Styles.Table.StylePrimitive + for _, child := range e.Children { + if r, ok := child.(StyleOverriderElementRenderer); ok { + if err := r.StyleOverrideRender(&b, ctx, style); err != nil { + return err + } + } else { + var bb bytes.Buffer + if err := child.Render(&bb, ctx); err != nil { + return err + } + el := &BaseElement{ + Token: bb.String(), + Style: style, + } + if err := el.Render(&b, ctx); err != nil { + return err + } + } + } + if e.Head { - ctx.table.header = append(ctx.table.header, e.Text) + ctx.table.header = append(ctx.table.header, b.String()) } else { - ctx.table.cell = append(ctx.table.cell, e.Text) + ctx.table.row = append(ctx.table.row, b.String()) } return nil diff --git a/ansi/testdata/TestRenderer/block_quote.golden b/ansi/testdata/TestRenderer/block_quote.golden new file mode 100644 index 00000000..2b420000 --- /dev/null +++ b/ansi/testdata/TestRenderer/block_quote.golden @@ -0,0 +1,2 @@ + +=> First line of quote Second line                                                 diff --git a/testdata/code.test b/ansi/testdata/TestRenderer/code.golden similarity index 100% rename from testdata/code.test rename to ansi/testdata/TestRenderer/code.golden diff --git a/testdata/code_block.test b/ansi/testdata/TestRenderer/code_block.golden similarity index 100% rename from testdata/code_block.test rename to ansi/testdata/TestRenderer/code_block.golden diff --git a/testdata/emoji.test b/ansi/testdata/TestRenderer/emoji.golden similarity index 100% rename from testdata/emoji.test rename to ansi/testdata/TestRenderer/emoji.golden diff --git a/testdata/emph.test b/ansi/testdata/TestRenderer/emph.golden similarity index 100% rename from testdata/emph.test rename to ansi/testdata/TestRenderer/emph.golden diff --git a/testdata/enumeration.test b/ansi/testdata/TestRenderer/enumeration.golden similarity index 100% rename from testdata/enumeration.test rename to ansi/testdata/TestRenderer/enumeration.golden diff --git a/ansi/testdata/TestRenderer/heading.golden b/ansi/testdata/TestRenderer/heading.golden new file mode 100644 index 00000000..134dfc58 --- /dev/null +++ b/ansi/testdata/TestRenderer/heading.golden @@ -0,0 +1,3 @@ +=> h1 <= +## h2 +### h3 \ No newline at end of file diff --git a/testdata/hr.test b/ansi/testdata/TestRenderer/hr.golden similarity index 100% rename from testdata/hr.test rename to ansi/testdata/TestRenderer/hr.golden diff --git a/testdata/image.test b/ansi/testdata/TestRenderer/image.golden similarity index 100% rename from testdata/image.test rename to ansi/testdata/TestRenderer/image.golden diff --git a/testdata/link.test b/ansi/testdata/TestRenderer/link.golden similarity index 100% rename from testdata/link.test rename to ansi/testdata/TestRenderer/link.golden diff --git a/testdata/list.test b/ansi/testdata/TestRenderer/list.golden similarity index 97% rename from testdata/list.test rename to ansi/testdata/TestRenderer/list.golden index 4ba92dc8..8374a0aa 100644 --- a/testdata/list.test +++ b/ansi/testdata/TestRenderer/list.golden @@ -1,4 +1,4 @@ β€’ First Item                                                                     -    β€’ Nested List Item                                                           +    β€’ Nested List Item                                                             β€’ Second Item                                                                    diff --git a/testdata/ordered_list.test b/ansi/testdata/TestRenderer/ordered_list.golden similarity index 100% rename from testdata/ordered_list.test rename to ansi/testdata/TestRenderer/ordered_list.golden diff --git a/testdata/strikethrough.test b/ansi/testdata/TestRenderer/strikethrough.golden similarity index 100% rename from testdata/strikethrough.test rename to ansi/testdata/TestRenderer/strikethrough.golden diff --git a/testdata/strong.test b/ansi/testdata/TestRenderer/strong.golden similarity index 100% rename from testdata/strong.test rename to ansi/testdata/TestRenderer/strong.golden diff --git a/ansi/testdata/TestRenderer/table.golden b/ansi/testdata/TestRenderer/table.golden new file mode 100644 index 00000000..eae2429a --- /dev/null +++ b/ansi/testdata/TestRenderer/table.golden @@ -0,0 +1,5 @@ + +Label β”‚Value β”‚URL +──────────────────┼────────────────┼──────────────────────────────────────────── +First β”‚foo β”‚https://charm.sh https://charm.sh +Second β”‚bar β”‚https://charm.sh https://charm.sh diff --git a/ansi/testdata/TestRenderer/table_align.golden b/ansi/testdata/TestRenderer/table_align.golden new file mode 100644 index 00000000..4507bc62 --- /dev/null +++ b/ansi/testdata/TestRenderer/table_align.golden @@ -0,0 +1,5 @@ + +Label β”‚ Value β”‚ URL +──────────────────┼────────────────┼──────────────────────────────────────────── +First β”‚ foo β”‚ charm.sh +Second β”‚ bar β”‚ https://charm.sh https://charm.sh diff --git a/testdata/task.test b/ansi/testdata/TestRenderer/task.golden similarity index 100% rename from testdata/task.test rename to ansi/testdata/TestRenderer/task.golden diff --git a/ansi/testdata/TestRendererIssues/106.golden b/ansi/testdata/TestRendererIssues/106.golden new file mode 100644 index 00000000..a1c0f80d --- /dev/null +++ b/ansi/testdata/TestRendererIssues/106.golden @@ -0,0 +1,17 @@ + +                                                                                  + β€’ `hi`                                                                           + β€’ \hi                                                                            + β€’ *hi                                                                            + β€’ _hi                                                                            + β€’ {hi}                                                                           + β€’ [hi]                                                                           + β€’                                                                            + β€’ (hi)                                                                           + β€’ # hi                                                                           + β€’ + hi                                                                           + β€’ - hi                                                                           + β€’ . hi                                                                           + β€’ ! hi                                                                           + β€’ | hi                                                                           + diff --git a/ansi/testdata/TestRendererIssues/107.golden b/ansi/testdata/TestRendererIssues/107.golden new file mode 100644 index 00000000..424915d8 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/107.golden @@ -0,0 +1,7 @@ + +                                                                                  +   [Mount]                                                                        +   Options=reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,noauto,_netdev, + allow_other,uid=1000,gid=1000,IdentityFile=/PATH/TO/SSH-KEY/id_rsa,              + StrictHostKeyChecking=no                                                         + diff --git a/ansi/testdata/TestRendererIssues/117.golden b/ansi/testdata/TestRendererIssues/117.golden new file mode 100644 index 00000000..22637fc2 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/117.golden @@ -0,0 +1,6 @@ + +                                                                                  + cmd β”‚descr + ──────────────────────────────────────┼───────────────────────────────────────── +  glow config  β”‚open glow config + diff --git a/ansi/testdata/TestRendererIssues/149.golden b/ansi/testdata/TestRendererIssues/149.golden new file mode 100644 index 00000000..00a99f11 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/149.golden @@ -0,0 +1,5 @@ + + a http://example.com/with-a-big-path/likely-to-use-more-than-one-line?why-       + not=after-all-why-                                                               + not-use-queryparams-too&abc=123                                                  + diff --git a/ansi/testdata/TestRendererIssues/172.golden b/ansi/testdata/TestRendererIssues/172.golden new file mode 100644 index 00000000..e70f14c2 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/172.golden @@ -0,0 +1,6 @@ + +                                                                                  + β”‚ The quick brown fox jumps over the lazy dog. The quick brown fox jumps over    + β”‚ the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox + β”‚ jumps over the lazy dog.                                                       + diff --git a/ansi/testdata/TestRendererIssues/237.golden b/ansi/testdata/TestRendererIssues/237.golden new file mode 100644 index 00000000..1b97b5ef --- /dev/null +++ b/ansi/testdata/TestRendererIssues/237.golden @@ -0,0 +1,9 @@ + +                                                                                  + β”‚ Content Security Policy (CSP) is an added layer of security that helps to      + β”‚ detect and mitigate certain types of attacks, including Cross-Site Scripting   + β”‚ (XSS) and data injection attacks. These attacks are used for everything from   + β”‚ data theft, to site defacement, to malware distribution.                       + β”‚                                                                                + β”‚ from MDN                                                                       + diff --git a/ansi/testdata/TestRendererIssues/239.golden b/ansi/testdata/TestRendererIssues/239.golden new file mode 100644 index 00000000..8cf5a609 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/239.golden @@ -0,0 +1,9 @@ + +                                                                                  + First term                                                                       + 🠢 Definition one of first term.                                                  +                                                                                  + Second term                                                                      + 🠢 Definition one of second term.                                                 +                                                                                  + diff --git a/ansi/testdata/TestRendererIssues/257.golden b/ansi/testdata/TestRendererIssues/257.golden new file mode 100644 index 00000000..0a3b9c2c --- /dev/null +++ b/ansi/testdata/TestRendererIssues/257.golden @@ -0,0 +1,4 @@ + +                                                                                  +   set runtimepath^=$XDG_CONFIG_HOME/vim                                          + diff --git a/ansi/testdata/TestRendererIssues/290.golden b/ansi/testdata/TestRendererIssues/290.golden new file mode 100644 index 00000000..95ed4cd4 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/290.golden @@ -0,0 +1,6 @@ + +                                                                                  + β€’ test@example.com mailto:test@example.com                                       + β€’ https://google.com https://google.com                                          + β€’ https://google.com https://google.com                                          + diff --git a/ansi/testdata/TestRendererIssues/312.golden b/ansi/testdata/TestRendererIssues/312.golden new file mode 100644 index 00000000..81dc1de2 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/312.golden @@ -0,0 +1,17 @@ + +  File                                                                            +                                                                                  + bold text with URL https://www.example.com                                       +                                                                                  + italic text with URL https://www.example.com                                     +                                                                                  + URL https://www.example.com                                                      +                                                                                  + url with bold within https://www.example.com                                     +                                                                                  + url with italic within https://www.example.com                                   +                                                                                  + entire url text is bold https://www.example.com                                  +                                                                                  + entire url text is italic https://www.example.com                                + diff --git a/ansi/testdata/TestRendererIssues/313.golden b/ansi/testdata/TestRendererIssues/313.golden new file mode 100644 index 00000000..c1c25dfe --- /dev/null +++ b/ansi/testdata/TestRendererIssues/313.golden @@ -0,0 +1,8 @@ + +                                                                                  + β”‚ This is a block quote                                                          + β”‚                                                                                + β”‚ β”‚ This is the nested quote                                                     + β”‚                                                                                + β”‚ This is part of the outer block quote.                                         + diff --git a/ansi/testdata/TestRendererIssues/315.golden b/ansi/testdata/TestRendererIssues/315.golden new file mode 100644 index 00000000..e7ca1fc2 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/315.golden @@ -0,0 +1,6 @@ + +                                                                                  + Expression β”‚Value β”‚Type + ────────────────────────────────────────────┼──────────────────┼──────────────── +  (1 >= 26) || (12 >= 6) {.java} β”‚s β”‚a + diff --git a/ansi/testdata/TestRendererIssues/316.golden b/ansi/testdata/TestRendererIssues/316.golden new file mode 100644 index 00000000..131cba89 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/316.golden @@ -0,0 +1,7 @@ + +                                                                                  + Aβ”‚B + ──────────────────────────────────────────────────────────┼───────────────────── + Here https://example.comβ”‚hello + https://autolink.com https://autolink.comβ”‚world + diff --git a/testdata/issues/42.test b/ansi/testdata/TestRendererIssues/42.golden similarity index 55% rename from testdata/issues/42.test rename to ansi/testdata/TestRendererIssues/42.golden index 09f61ffc..3e12e449 100644 --- a/testdata/issues/42.test +++ b/ansi/testdata/TestRendererIssues/42.golden @@ -1,6 +1,6 @@ - If you want to make a more significant change, please first open an issue    - https://github.com/twpayne/chezmoi/issues/new to discuss the change that you - want to make. Dave Cheney gives a good rationale                             - https://dave.cheney.net/2019/02/18/talk-then-code as to why this is important. + If you want to make a more significant change, please first open an issue        + https://github.com/twpayne/chezmoi/issues/new to discuss the change that you     + want to make. Dave Cheney gives a good rationale                                 + https://dave.cheney.net/2019/02/18/talk-then-code as to why this is important.   diff --git a/testdata/issues/43.test b/ansi/testdata/TestRendererIssues/43.golden similarity index 94% rename from testdata/issues/43.test rename to ansi/testdata/TestRendererIssues/43.golden index f88488f2..c2092e26 100644 --- a/testdata/issues/43.test +++ b/ansi/testdata/TestRendererIssues/43.golden @@ -1,10 +1,10 @@ -                                                                              - β€’ Getting started                                                            - β€’ Developing locally                                                         - β€’ Documentation changes                                                      - β€’ Contributing changes                                                       - β€’ Managing releases                                                          - β€’ Packaging                                                                  - β€’ Updating the website                                                       +                                                                                  + β€’ Getting started                                                                + β€’ Developing locally                                                             + β€’ Documentation changes                                                          + β€’ Contributing changes                                                           + β€’ Managing releases                                                              + β€’ Packaging                                                                      + β€’ Updating the website                                                           diff --git a/ansi/testdata/TestRendererIssues/44.golden b/ansi/testdata/TestRendererIssues/44.golden new file mode 100644 index 00000000..cf12febd --- /dev/null +++ b/ansi/testdata/TestRendererIssues/44.golden @@ -0,0 +1,9 @@ + +                                                                                  + Distri…β”‚Architectures β”‚Package + ───────┼───────────────────────────────────┼──────────────────────────────────── + Debian β”‚ amd64 ,  arm64 ,  armel ,  i386 ,…β”‚ deb  https://github.com/twpayne/ch… + RedHat β”‚ aarch64 ,  armhfp ,  i686 ,  ppc6…β”‚ rpm  https://github.com/twpayne/ch… + OpenSU…β”‚ aarch64 ,  armhfp ,  i686 ,  ppc6…β”‚ rpm  https://github.com/twpayne/ch… + Ubuntu β”‚ amd64 ,  arm64 ,  armel ,  i386 ,…β”‚ deb  https://github.com/twpayne/ch… + diff --git a/testdata/issues/46_1.test b/ansi/testdata/TestRendererIssues/46_1.golden similarity index 94% rename from testdata/issues/46_1.test rename to ansi/testdata/TestRendererIssues/46_1.golden index 0eb404ed..3150d107 100644 --- a/testdata/issues/46_1.test +++ b/ansi/testdata/TestRendererIssues/46_1.golden @@ -1,6 +1,6 @@ -                                                                              - β€’ Navigation                                                                 -   β€’ Familiar shortcuts (arrows, ~, -, @), quick reference                    -                                                                              +                                                                                  + β€’ Navigation                                                                     +   β€’ Familiar shortcuts (arrows, ~, -, @), quick reference                        +                                                                                  diff --git a/ansi/testdata/TestRendererIssues/46_2.golden b/ansi/testdata/TestRendererIssues/46_2.golden new file mode 100644 index 00000000..62aa4370 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/46_2.golden @@ -0,0 +1,14 @@ + +                                                                                  + Dependency β”‚Installa…β”‚Operation + ─────────────────────────────────────────────┼─────────┼──────────────────────── + xdg-open (Linux), open(1) (macOS), cygstart …β”‚base β”‚desktop opener + file, coreutils (cp, mv, rm), xargs β”‚base β”‚file type, copy, move a… + tar, (un)zip [atool/bsdtar for more formats] β”‚base β”‚create, list, extract t… + archivemount, fusermount(3) β”‚optional β”‚mount, unmount archives + sshfs, rclone https://rclone.org/, fusermoun…β”‚optional β”‚mount, unmount remotes + trash-cli β”‚optional β”‚trash files (default ac… + vlock (Linux), bashlock (macOS), lock(1) (BS…β”‚optional β”‚terminal locker (fallba… + advcpmv (Linux) (integration https://github.…β”‚optional β”‚copy, move progress +  $VISUAL  (else  $EDITOR ),  $PAGER ,  $SHEL…β”‚optional β”‚fallback vi, less, sh + diff --git a/testdata/issues/47.test b/ansi/testdata/TestRendererIssues/47.golden similarity index 81% rename from testdata/issues/47.test rename to ansi/testdata/TestRendererIssues/47.golden index 581f1101..20b0fd9b 100644 --- a/testdata/issues/47.test +++ b/ansi/testdata/TestRendererIssues/47.golden @@ -1,3 +1,3 @@ - Example:                                                     + Example:                                                        diff --git a/testdata/issues/48.test b/ansi/testdata/TestRendererIssues/48.golden similarity index 93% rename from testdata/issues/48.test rename to ansi/testdata/TestRendererIssues/48.golden index 4673d85a..ee82302f 100644 --- a/testdata/issues/48.test +++ b/ansi/testdata/TestRendererIssues/48.golden @@ -1,17 +1,17 @@ - emoji in text                                                                -                                                                              - πŸ™ ⚑ 🐱 = ❀️                                                                -                                                                              - emoji in header                                                              -                                                                              - ## πŸ™ ⚑ 🐱 = ❀️                                                             -                                                                              - no emoji in code blocks                                                      -                                                                              -   :octopus: :zap: :cat: = :heart:                                            -                                                                              - no emoji in inline code                                                      -                                                                              -  :octopus: :zap: :cat: = :heart:                                             + emoji in text                                                                    +                                                                                  + πŸ™ ⚑ 🐱 = ❀️                                                                    +                                                                                  + emoji in header                                                                  +                                                                                  + ## πŸ™ ⚑ 🐱 = ❀️                                                                 +                                                                                  + no emoji in code blocks                                                          +                                                                                  +   :octopus: :zap: :cat: = :heart:                                                +                                                                                  + no emoji in inline code                                                          +                                                                                  +  :octopus: :zap: :cat: = :heart:                                                 diff --git a/ansi/testdata/TestRendererIssues/60.golden b/ansi/testdata/TestRendererIssues/60.golden new file mode 100644 index 00000000..c6e009b5 --- /dev/null +++ b/ansi/testdata/TestRendererIssues/60.golden @@ -0,0 +1,28 @@ + +                                                                                  + Library β”‚Version + ───────────────────────────────────────────────────────────────┼──────────────── + ESMF https://github.com/esmf-org/esmf β”‚v8.6.1 + FMS https://github.com/NOAA-GFDL/FMS/ β”‚2024.01.02 + netCDF https://github.com/Unidata/netcdf-c β”‚4.9.2 + netCDF Fortran https://github.com/Unidata/netcdf-fortran β”‚4.6.1 + netCDF C++ https://github.com/Unidata/netcdf-cxx4 β”‚4.3.1 + HDF5 https://portal.hdfgroup.org/display/support β”‚1.10.11 + HDF4 https://portal.hdfgroup.org/display/support β”‚4.2.16-2 + GFE https://github.com/Goddard-Fortran-Ecosystem/GFE β”‚v1.16.0 + xgboost https://github.com/dmlc/xgboost β”‚v1.6.0 + libyaml https://github.com/yaml/libyaml.git β”‚0.2.5 + antlr2 https://www.antlr2.org/ β”‚2.7.7 + GSL https://www.gnu.org/software/gsl/ β”‚2.7 + jpeg http://www.ijg.org/ β”‚9e + zlib http://www.zlib.net/ β”‚1.3.1 + szip https://support.hdfgroup.org/doc_resource/SZIP/ β”‚2.1.1 + cURL https://curl.haxx.se/ β”‚8.8.0 + UDUNITS2 https://github.com/GMAO-SI-Team/UDUNITS-2.git β”‚2.2.28 + NCO http://nco.sourceforge.net/ β”‚5.2.6 + CDO https://code.mpimet.mpg.de/projects/cdo β”‚2.3.0 + nccmp https://gitlab.com/remikz/nccmp β”‚1.9.1.0 + HDF-EOS2 https://wiki.earthdata.nasa.gov/display/DAS β”‚3.0 + HDF-EOS5 https://wiki.earthdata.nasa.gov/display/DAS β”‚2.0 + SDP Toolkit https://wiki.earthdata.nasa.gov/display/DAS β”‚5.2.20 + diff --git a/ansi/testdata/TestRendererIssues/79.golden b/ansi/testdata/TestRendererIssues/79.golden new file mode 100644 index 00000000..6d3fa20a --- /dev/null +++ b/ansi/testdata/TestRendererIssues/79.golden @@ -0,0 +1,9 @@ + + Preceding blockquote paragraph                                                   +                                                                                  + β”‚ 1st blockquote paragraph                                                       + β”‚                                                                                + β”‚   quoted code block                                                            + β”‚                                                                                + β”‚ 2nd blockquote paragraph                                                       + diff --git a/dracula.go b/dracula.go index 0b0eb56a..cf0a7dfc 100644 --- a/dracula.go +++ b/dracula.go @@ -208,9 +208,6 @@ var DraculaStyleConfig = ansi.StyleConfig{ StyleBlock: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{}, }, - CenterSeparator: stringPtr("β”Ό"), - ColumnSeparator: stringPtr("β”‚"), - RowSeparator: stringPtr("─"), }, DefinitionDescription: ansi.StylePrimitive{ BlockPrefix: "\n🠢 ", diff --git a/glamour_test.go b/glamour_test.go index 3a20b470..38979f65 100644 --- a/glamour_test.go +++ b/glamour_test.go @@ -7,14 +7,12 @@ import ( "os" "strings" "testing" -) -const ( - generate = false - markdown = "testdata/readme.markdown.in" - testFile = "testdata/readme.test" + "github.com/charmbracelet/x/exp/golden" ) +const markdown = "testdata/readme.markdown.in" + func TestTermRendererWriter(t *testing.T) { r, err := NewTermRenderer( WithStandardStyle(DarkStyle), @@ -42,25 +40,7 @@ func TestTermRendererWriter(t *testing.T) { t.Fatal(err) } - // generate - if generate { - err := os.WriteFile(testFile, b, 0o644) - if err != nil { - t.Fatal(err) - } - return - } - - // verify - td, err := os.ReadFile(testFile) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(td, b) { - t.Errorf("Rendered output doesn't match!\nExpected: `\n%s`\nGot: `\n%s`\n", - string(td), b) - } + golden.RequireEqual(t, b) } func TestTermRenderer(t *testing.T) { @@ -81,16 +61,7 @@ func TestTermRenderer(t *testing.T) { t.Fatal(err) } - // verify - td, err := os.ReadFile(testFile) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(td, []byte(b)) { - t.Errorf("Rendered output doesn't match!\nExpected: `\n%s`\nGot: `\n%s`\n", - string(td), b) - } + golden.RequireEqual(t, []byte(b)) } func TestWithEmoji(t *testing.T) { @@ -133,16 +104,7 @@ func TestWithPreservedNewLines(t *testing.T) { t.Fatal(err) } - // verify - td, err := os.ReadFile("testdata/preserved_newline.test") - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(td, []byte(b)) { - t.Errorf("Rendered output doesn't match!\nExpected: `\n%s`\nGot: `\n%s`\n", - string(td), b) - } + golden.RequireEqual(t, []byte(b)) } func TestStyles(t *testing.T) { @@ -219,16 +181,7 @@ func TestRenderHelpers(t *testing.T) { t.Error(err) } - // verify - td, err := os.ReadFile(testFile) - if err != nil { - t.Fatal(err) - } - - if b != string(td) { - t.Errorf("Rendered output doesn't match!\nExpected: `\n%s`\nGot: `\n%s`\n", - string(td), b) - } + golden.RequireEqual(t, []byte(b)) } func TestCapitalization(t *testing.T) { @@ -250,15 +203,7 @@ func TestCapitalization(t *testing.T) { t.Fatal(err) } - // expected outcome - td, err := os.ReadFile("testdata/capitalization.test") - if err != nil { - t.Fatal(err) - } - - if string(td) != b { - t.Errorf("Rendered output doesn't match!\nExpected: `\n%s`\nGot: `\n%s`\n", td, b) - } + golden.RequireEqual(t, []byte(b)) } func FuzzData(f *testing.F) { diff --git a/go.mod b/go.mod index 1674306e..7dc33b68 100644 --- a/go.mod +++ b/go.mod @@ -4,23 +4,26 @@ go 1.21 require ( github.com/alecthomas/chroma/v2 v2.14.0 + github.com/charmbracelet/lipgloss v0.12.1 + github.com/charmbracelet/x/ansi v0.1.4 + github.com/charmbracelet/x/exp/golden v0.0.0-20240715153702-9ba8adf781c4 github.com/microcosm-cc/bluemonday v1.0.27 github.com/muesli/reflow v0.3.0 github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a - github.com/olekukonko/tablewriter v0.0.5 github.com/yuin/goldmark v1.7.4 github.com/yuin/goldmark-emoji v1.0.3 ) require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/aymanbagabas/go-udiff v0.2.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/net v0.27.0 // indirect golang.org/x/sys v0.22.0 // indirect ) diff --git a/go.sum b/go.sum index 9ff84c44..3a815755 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,16 @@ github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 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/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs= +github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8= +github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= +github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/exp/golden v0.0.0-20240715153702-9ba8adf781c4 h1:6KzMkQeAF56rggw2NZu1L+TH7j9+DM1/2Kmh7KUxg1I= +github.com/charmbracelet/x/exp/golden v0.0.0-20240715153702-9ba8adf781c4/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= @@ -16,24 +24,21 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= diff --git a/styles.go b/styles.go index a8c16f32..1d735ebe 100644 --- a/styles.go +++ b/styles.go @@ -6,9 +6,11 @@ import ( "github.com/charmbracelet/glamour/ansi" ) -const defaultListIndent = 2 -const defaultListLevelIndent = 4 -const defaultMargin = 2 +const ( + defaultListIndent = 2 + defaultListLevelIndent = 4 + defaultMargin = 2 +) var ( // ASCIIStyleConfig uses only ASCII characters. @@ -108,11 +110,7 @@ var ( Margin: uintPtr(defaultMargin), }, }, - Table: ansi.StyleTable{ - CenterSeparator: stringPtr("+"), - ColumnSeparator: stringPtr("|"), - RowSeparator: stringPtr("-"), - }, + Table: ansi.StyleTable{}, DefinitionDescription: ansi.StylePrimitive{ BlockPrefix: "\n* ", }, @@ -322,9 +320,6 @@ var ( StyleBlock: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{}, }, - CenterSeparator: stringPtr("β”Ό"), - ColumnSeparator: stringPtr("β”‚"), - RowSeparator: stringPtr("─"), }, DefinitionDescription: ansi.StylePrimitive{ BlockPrefix: "\n🠢 ", @@ -534,9 +529,6 @@ var ( StyleBlock: ansi.StyleBlock{ StylePrimitive: ansi.StylePrimitive{}, }, - CenterSeparator: stringPtr("β”Ό"), - ColumnSeparator: stringPtr("β”‚"), - RowSeparator: stringPtr("─"), }, DefinitionDescription: ansi.StylePrimitive{ BlockPrefix: "\n🠢 ", @@ -640,11 +632,7 @@ var ( Suffix: " ", }, }, - Table: ansi.StyleTable{ - CenterSeparator: stringPtr("β”Ό"), - ColumnSeparator: stringPtr("β”‚"), - RowSeparator: stringPtr("─"), - }, + Table: ansi.StyleTable{}, DefinitionList: ansi.StyleBlock{}, DefinitionTerm: ansi.StylePrimitive{}, DefinitionDescription: ansi.StylePrimitive{ diff --git a/styles/ascii.json b/styles/ascii.json index 048a7298..be13b917 100644 --- a/styles/ascii.json +++ b/styles/ascii.json @@ -72,11 +72,7 @@ "code_block": { "margin": 2 }, - "table": { - "center_separator": "+", - "column_separator": "|", - "row_separator": "-" - }, + "table": {}, "definition_list": {}, "definition_term": {}, "definition_description": { diff --git a/styles/dark.json b/styles/dark.json index 0ba3bd5a..47bf0afc 100644 --- a/styles/dark.json +++ b/styles/dark.json @@ -180,11 +180,7 @@ } } }, - "table": { - "center_separator": "β”Ό", - "column_separator": "β”‚", - "row_separator": "─" - }, + "table": {}, "definition_list": {}, "definition_term": {}, "definition_description": { diff --git a/styles/dracula.json b/styles/dracula.json index ef0d2927..4bbcd497 100644 --- a/styles/dracula.json +++ b/styles/dracula.json @@ -177,11 +177,7 @@ } } }, - "table": { - "center_separator": "β”Ό", - "column_separator": "β”‚", - "row_separator": "─" - }, + "table": {}, "definition_list": {}, "definition_term": {}, "definition_description": { diff --git a/styles/light.json b/styles/light.json index ced0b54b..08533592 100644 --- a/styles/light.json +++ b/styles/light.json @@ -179,11 +179,7 @@ } } }, - "table": { - "center_separator": "β”Ό", - "column_separator": "β”‚", - "row_separator": "─" - }, + "table": {}, "definition_list": {}, "definition_term": {}, "definition_description": { diff --git a/styles/pink.json b/styles/pink.json index 10070eb5..39a7f9ec 100644 --- a/styles/pink.json +++ b/styles/pink.json @@ -79,11 +79,7 @@ "background_color": "236" }, "code_block": {}, - "table": { - "center_separator": "β”Ό", - "column_separator": "β”‚", - "row_separator": "─" - }, + "table": {}, "definition_list": {}, "definition_term": {}, "definition_description": { diff --git a/styles/tokyo_night.json b/styles/tokyo_night.json index 801278c8..82dd7755 100644 --- a/styles/tokyo_night.json +++ b/styles/tokyo_night.json @@ -169,11 +169,7 @@ } } }, - "table": { - "center_separator": "β”Ό", - "column_separator": "β”‚", - "row_separator": "─" - }, + "table": {}, "definition_list": {}, "definition_term": {}, "definition_description": { diff --git a/testdata/capitalization.test b/testdata/TestCapitalization.golden similarity index 94% rename from testdata/capitalization.test rename to testdata/TestCapitalization.golden index 2c684356..fe6e26f0 100644 --- a/testdata/capitalization.test +++ b/testdata/TestCapitalization.golden @@ -1,7 +1,7 @@ -  EVERYTHING IS UPPERCASE                                                     -                                                                              - ## Everything Is Titled                                                      -                                                                              - ### everything is lowercase                                                  +  EVERYTHING IS UPPERCASE                                                         +                                                                                  + ## Everything Is Titled                                                          +                                                                                  + ### everything is lowercase                                                       diff --git a/testdata/readme.test b/testdata/TestRenderHelpers.golden similarity index 90% rename from testdata/readme.test rename to testdata/TestRenderHelpers.golden index f9f36d27..73b0e93d 100644 --- a/testdata/readme.test +++ b/testdata/TestRenderHelpers.golden @@ -1,40 +1,39 @@ -  Gold                                                                        -                                                                              - Render markdown on the CLI, with pizzazz!                                    -                                                                              - ## What is it?                                                               -                                                                              - Gold is a Golang library that allows you to use JSON based stylesheets to    - render Markdown files in the terminal. Just like CSS, you can define color   - and style attributes on Markdown elements. The difference is that you use    - ANSI color and terminal codes instead of CSS properties and hex colors.      -                                                                              - ## Usage                                                                     -                                                                              - See cmd/gold /cmd/gold/.                                                     -                                                                              - ## Example Output                                                            -                                                                              - Image: Gold Dark Style β†’                                                     - https://github.com/charmbracelet/gold/raw/master/styles/gallery/dark.png     -                                                                              - Check out the Gold Style Gallery                                             - https://github.com/charmbracelet/gold/blob/master/styles/gallery/README.md!  -                                                                              - ## Colors                                                                    -                                                                              - Currently  gold  uses the Aurora ANSI colors                                 - https://godoc.org/github.com/logrusorgru/aurora#Index.                       -                                                                              - ## Development                                                               -                                                                              - Style definitions located in  styles/  can be embedded into the binary by    - running statik https://github.com/rakyll/statik:                             -                                                                              -   statik -f -src styles -include "*.json"                                    -                                                                              - You can re-generate screenshots of all available styles by running           - gallery.sh . This requires  termshot  and  pngcrush  installed on your       - system!                                                                      +  Gold                                                                            +                                                                                  + Render markdown on the CLI, with pizzazz!                                        +                                                                                  + ## What is it?                                                                   +                                                                                  + Gold is a Golang library that allows you to use JSON based stylesheets to render + Markdown files in the terminal. Just like CSS, you can define color and style    + attributes on Markdown elements. The difference is that you use ANSI color and   + terminal codes instead of CSS properties and hex colors.                         +                                                                                  + ## Usage                                                                         +                                                                                  + See cmd/gold /cmd/gold/.                                                         +                                                                                  + ## Example Output                                                                +                                                                                  + Image: Gold Dark Style β†’                                                         + https://github.com/charmbracelet/gold/raw/master/styles/gallery/dark.png         +                                                                                  + Check out the Gold Style Gallery                                                 + https://github.com/charmbracelet/gold/blob/master/styles/gallery/README.md!      +                                                                                  + ## Colors                                                                        +                                                                                  + Currently  gold  uses the Aurora ANSI colors                                     + https://godoc.org/github.com/logrusorgru/aurora#Index.                           +                                                                                  + ## Development                                                                   +                                                                                  + Style definitions located in  styles/  can be embedded into the binary by        + running statik https://github.com/rakyll/statik:                                 +                                                                                  +   statik -f -src styles -include "*.json"                                        +                                                                                  + You can re-generate screenshots of all available styles by running  gallery.sh . + This requires  termshot  and  pngcrush  installed on your system!                diff --git a/testdata/TestTermRenderer.golden b/testdata/TestTermRenderer.golden new file mode 100644 index 00000000..73b0e93d --- /dev/null +++ b/testdata/TestTermRenderer.golden @@ -0,0 +1,39 @@ + +  Gold                                                                            +                                                                                  + Render markdown on the CLI, with pizzazz!                                        +                                                                                  + ## What is it?                                                                   +                                                                                  + Gold is a Golang library that allows you to use JSON based stylesheets to render + Markdown files in the terminal. Just like CSS, you can define color and style    + attributes on Markdown elements. The difference is that you use ANSI color and   + terminal codes instead of CSS properties and hex colors.                         +                                                                                  + ## Usage                                                                         +                                                                                  + See cmd/gold /cmd/gold/.                                                         +                                                                                  + ## Example Output                                                                +                                                                                  + Image: Gold Dark Style β†’                                                         + https://github.com/charmbracelet/gold/raw/master/styles/gallery/dark.png         +                                                                                  + Check out the Gold Style Gallery                                                 + https://github.com/charmbracelet/gold/blob/master/styles/gallery/README.md!      +                                                                                  + ## Colors                                                                        +                                                                                  + Currently  gold  uses the Aurora ANSI colors                                     + https://godoc.org/github.com/logrusorgru/aurora#Index.                           +                                                                                  + ## Development                                                                   +                                                                                  + Style definitions located in  styles/  can be embedded into the binary by        + running statik https://github.com/rakyll/statik:                                 +                                                                                  +   statik -f -src styles -include "*.json"                                        +                                                                                  + You can re-generate screenshots of all available styles by running  gallery.sh . + This requires  termshot  and  pngcrush  installed on your system!                + diff --git a/testdata/TestTermRendererWriter.golden b/testdata/TestTermRendererWriter.golden new file mode 100644 index 00000000..73b0e93d --- /dev/null +++ b/testdata/TestTermRendererWriter.golden @@ -0,0 +1,39 @@ + +  Gold                                                                            +                                                                                  + Render markdown on the CLI, with pizzazz!                                        +                                                                                  + ## What is it?                                                                   +                                                                                  + Gold is a Golang library that allows you to use JSON based stylesheets to render + Markdown files in the terminal. Just like CSS, you can define color and style    + attributes on Markdown elements. The difference is that you use ANSI color and   + terminal codes instead of CSS properties and hex colors.                         +                                                                                  + ## Usage                                                                         +                                                                                  + See cmd/gold /cmd/gold/.                                                         +                                                                                  + ## Example Output                                                                +                                                                                  + Image: Gold Dark Style β†’                                                         + https://github.com/charmbracelet/gold/raw/master/styles/gallery/dark.png         +                                                                                  + Check out the Gold Style Gallery                                                 + https://github.com/charmbracelet/gold/blob/master/styles/gallery/README.md!      +                                                                                  + ## Colors                                                                        +                                                                                  + Currently  gold  uses the Aurora ANSI colors                                     + https://godoc.org/github.com/logrusorgru/aurora#Index.                           +                                                                                  + ## Development                                                                   +                                                                                  + Style definitions located in  styles/  can be embedded into the binary by        + running statik https://github.com/rakyll/statik:                                 +                                                                                  +   statik -f -src styles -include "*.json"                                        +                                                                                  + You can re-generate screenshots of all available styles by running  gallery.sh . + This requires  termshot  and  pngcrush  installed on your system!                + diff --git a/testdata/preserved_newline.test b/testdata/TestWithPreservedNewLines.golden similarity index 100% rename from testdata/preserved_newline.test rename to testdata/TestWithPreservedNewLines.golden diff --git a/testdata/block_quote.test b/testdata/block_quote.test deleted file mode 100644 index 57dbd967..00000000 --- a/testdata/block_quote.test +++ /dev/null @@ -1,3 +0,0 @@ - -=> First line of quote                                                             -=> Second line                                                                     diff --git a/testdata/heading.test b/testdata/heading.test deleted file mode 100644 index b29a7e2a..00000000 --- a/testdata/heading.test +++ /dev/null @@ -1,3 +0,0 @@ - => h1 <= - ## h2 - ### h3 \ No newline at end of file diff --git a/testdata/issues/106.md b/testdata/issues/106.md new file mode 100644 index 00000000..c37d79b0 --- /dev/null +++ b/testdata/issues/106.md @@ -0,0 +1,14 @@ +- \`hi\` +- \\hi +- \*hi +- \_hi +- \{hi\} +- \[hi\] +- \ +- \(hi\) +- \# hi +- \+ hi +- \- hi +- \. hi +- \! hi +- \| hi diff --git a/testdata/issues/107.md b/testdata/issues/107.md new file mode 100644 index 00000000..ffa94e32 --- /dev/null +++ b/testdata/issues/107.md @@ -0,0 +1,4 @@ +```ini +[Mount] +Options=reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,noauto,_netdev,allow_other,uid=1000,gid=1000,IdentityFile=/PATH/TO/SSH-KEY/id_rsa,StrictHostKeyChecking=no +``` diff --git a/testdata/issues/117.md b/testdata/issues/117.md new file mode 100644 index 00000000..8e00f2d4 --- /dev/null +++ b/testdata/issues/117.md @@ -0,0 +1,3 @@ +| cmd | descr | +| ------------- | ---------------- | +| `glow config` | open glow config | diff --git a/testdata/issues/149.md b/testdata/issues/149.md new file mode 100644 index 00000000..d6fec510 --- /dev/null +++ b/testdata/issues/149.md @@ -0,0 +1,3 @@ +[a](http://example.com/with-a-big-path/likely-to-use-more-than-one-line?why-not=after-all-why-not-use-queryparams-too&abc=123) + + diff --git a/testdata/issues/172.md b/testdata/issues/172.md new file mode 100644 index 00000000..90990de0 --- /dev/null +++ b/testdata/issues/172.md @@ -0,0 +1,3 @@ + + +> The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. diff --git a/testdata/issues/237.md b/testdata/issues/237.md new file mode 100644 index 00000000..ece6450b --- /dev/null +++ b/testdata/issues/237.md @@ -0,0 +1,3 @@ +> Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft, to site defacement, to malware distribution. +> +> from MDN diff --git a/testdata/issues/239.md b/testdata/issues/239.md new file mode 100644 index 00000000..7b37c37e --- /dev/null +++ b/testdata/issues/239.md @@ -0,0 +1,5 @@ +First term +: Definition one of first term. + +Second term +: Definition one of second term. diff --git a/testdata/issues/257.md b/testdata/issues/257.md new file mode 100644 index 00000000..c648ac7c --- /dev/null +++ b/testdata/issues/257.md @@ -0,0 +1,3 @@ +```vim +set runtimepath^=$XDG_CONFIG_HOME/vim +``` diff --git a/testdata/issues/290.md b/testdata/issues/290.md new file mode 100644 index 00000000..1e928721 --- /dev/null +++ b/testdata/issues/290.md @@ -0,0 +1,3 @@ +- **test@example.com** +- https://google.com +- _https://google.com_ diff --git a/testdata/issues/312.md b/testdata/issues/312.md new file mode 100644 index 00000000..53aae826 --- /dev/null +++ b/testdata/issues/312.md @@ -0,0 +1,15 @@ +# File + +**bold text with [URL](https://www.example.com)** + +_italic text with [URL](https://www.example.com)_ + +[URL](https://www.example.com) + +[url with **bold** within](https://www.example.com) + +[url with _italic_ within](https://www.example.com) + +[**entire url text is bold**](https://www.example.com) + +[_entire url text is italic_](https://www.example.com) diff --git a/testdata/issues/313.md b/testdata/issues/313.md new file mode 100644 index 00000000..4aab7d5f --- /dev/null +++ b/testdata/issues/313.md @@ -0,0 +1,5 @@ +> This is a block quote +> +> > This is the nested quote +> +> This is part of the outer block quote. diff --git a/testdata/issues/315.md b/testdata/issues/315.md new file mode 100644 index 00000000..0c066127 --- /dev/null +++ b/testdata/issues/315.md @@ -0,0 +1,3 @@ +| Expression | Value | Type | +| --------------------------------- | ----- | ---- | +| `(1 >= 26) \|\| (12 >= 6)`{.java} | s | a | diff --git a/testdata/issues/316.md b/testdata/issues/316.md new file mode 100644 index 00000000..2a81a0bd --- /dev/null +++ b/testdata/issues/316.md @@ -0,0 +1,4 @@ +| A | B | +| --------------------------: | ----- | +| [Here](https://example.com) | hello | +| https://autolink.com | world | diff --git a/testdata/issues/44.test b/testdata/issues/44.test deleted file mode 100644 index 63da718e..00000000 --- a/testdata/issues/44.test +++ /dev/null @@ -1,13 +0,0 @@ - -                                                                              -  DISTRIBUTION β”‚ ARCHITECTURES β”‚ PACKAGE                   - ───────────────┼────────────────────────────────┼──────────                  -  Debian β”‚ amd64, arm64, armel, i386, β”‚ deb                   -  β”‚ ppc64, ppc64le β”‚                   -  RedHat β”‚ aarch64, armhfp, i686, ppc64, β”‚ rpm                   -  β”‚ ppc64le, x86_64 β”‚                   -  OpenSUSE β”‚ aarch64, armhfp, i686, ppc64, β”‚ rpm                   -  β”‚ ppc64le, x86_64 β”‚                   -  Ubuntu β”‚ amd64, arm64, armel, i386, β”‚ deb                   -  β”‚ ppc64, ppc64le β”‚                   - diff --git a/testdata/issues/46_2.test b/testdata/issues/46_2.test deleted file mode 100644 index 8bb49776..00000000 --- a/testdata/issues/46_2.test +++ /dev/null @@ -1,21 +0,0 @@ - -                                                                              -  DEPENDENCY β”‚ INSTALLATION β”‚ OPERATION        - ─────────────────────────────────┼──────────────┼───────────────────────────────── -  xdg-open (Linux), open(1) β”‚ base β”‚ desktop opener             -  (macOS), cygstart (Cygwin) β”‚ β”‚                            -  file, coreutils (cp, mv, rm), β”‚ base β”‚ file type, copy, move and  -  xargs β”‚ β”‚ remove                     -  tar, (un)zip [atool/bsdtar for β”‚ base β”‚ create, list, extract tar, -  more formats] β”‚ β”‚ gzip, bzip2, zip           -  archivemount, fusermount(3) β”‚ optional β”‚ mount, unmount archives    -  sshfs, rclone, fusermount(3) β”‚ optional β”‚ mount, unmount remotes     -  trash-cli β”‚ optional β”‚ trash files (default       - action:                                                                   -  β”‚ β”‚ rm)                        -  vlock (Linux), bashlock β”‚ optional β”‚ terminal locker (fallback: -  (macOS), lock(1) (BSD) β”‚ β”‚ cmatrix)                   -  advcpmv (Linux) (integration) β”‚ optional β”‚ copy, move progress        -  $VISUAL (else $EDITOR), β”‚ optional β”‚ fallback vi, less, sh      -  $PAGER, $SHELL β”‚ β”‚                            - diff --git a/testdata/issues/60.md b/testdata/issues/60.md new file mode 100644 index 00000000..8f673692 --- /dev/null +++ b/testdata/issues/60.md @@ -0,0 +1,25 @@ +| Library | Version | +| ----------------------------------------------------------- | ---------- | +| [ESMF](https://github.com/esmf-org/esmf) | v8.6.1 | +| [FMS](https://github.com/NOAA-GFDL/FMS/) | 2024.01.02 | +| [netCDF](https://github.com/Unidata/netcdf-c) | 4.9.2 | +| [netCDF Fortran](https://github.com/Unidata/netcdf-fortran) | 4.6.1 | +| [netCDF C++](https://github.com/Unidata/netcdf-cxx4) | 4.3.1 | +| [HDF5](https://portal.hdfgroup.org/display/support) | 1.10.11 | +| [HDF4](https://portal.hdfgroup.org/display/support) | 4.2.16-2 | +| [GFE](https://github.com/Goddard-Fortran-Ecosystem/GFE) | v1.16.0 | +| [xgboost](https://github.com/dmlc/xgboost) | v1.6.0 | +| [libyaml](https://github.com/yaml/libyaml.git) | 0.2.5 | +| [antlr2](https://www.antlr2.org/) | 2.7.7 | +| [GSL](https://www.gnu.org/software/gsl/) | 2.7 | +| [jpeg](http://www.ijg.org/) | 9e | +| [zlib](http://www.zlib.net/) | 1.3.1 | +| [szip](https://support.hdfgroup.org/doc_resource/SZIP/) | 2.1.1 | +| [cURL](https://curl.haxx.se/) | 8.8.0 | +| [UDUNITS2](https://github.com/GMAO-SI-Team/UDUNITS-2.git) | 2.2.28 | +| [NCO](http://nco.sourceforge.net/) | 5.2.6 | +| [CDO](https://code.mpimet.mpg.de/projects/cdo) | 2.3.0 | +| [nccmp](https://gitlab.com/remikz/nccmp) | 1.9.1.0 | +| [HDF-EOS2](https://wiki.earthdata.nasa.gov/display/DAS) | 3.0 | +| [HDF-EOS5](https://wiki.earthdata.nasa.gov/display/DAS) | 2.0 | +| [SDP Toolkit](https://wiki.earthdata.nasa.gov/display/DAS) | 5.2.20 | diff --git a/testdata/issues/79.md b/testdata/issues/79.md new file mode 100644 index 00000000..d5a64b81 --- /dev/null +++ b/testdata/issues/79.md @@ -0,0 +1,9 @@ +Preceding blockquote paragraph + + + +> 1st blockquote paragraph +> +> quoted code block +> +> 2nd blockquote paragraph diff --git a/testdata/table.test b/testdata/table.test deleted file mode 100644 index 0da2654e..00000000 --- a/testdata/table.test +++ /dev/null @@ -1,5 +0,0 @@ - - LABEL | VALUE | URL - ---------+-------+------------------- - First | foo | https://charm.sh - Second | bar | https://charm.sh diff --git a/testdata/table_align.test b/testdata/table_align.test deleted file mode 100644 index 984330f6..00000000 --- a/testdata/table_align.test +++ /dev/null @@ -1,5 +0,0 @@ - - LABEL | VALUE | URL - ---------+-------+------------------- - First | foo | charm.sh - Second | bar | https://charm.sh