Add String node

This commit is contained in:
yuin 2019-05-30 16:07:04 +09:00
parent 7b1dd7b221
commit 05645dd3c4
4 changed files with 209 additions and 158 deletions

View file

@ -56,8 +56,26 @@ const (
textSoftLineBreak = 1 << iota textSoftLineBreak = 1 << iota
textHardLineBreak textHardLineBreak
textRaw textRaw
textCode
) )
func textFlagsString(flags uint8) string {
buf := []string{}
if flags&textSoftLineBreak != 0 {
buf = append(buf, "SoftLineBreak")
}
if flags&textHardLineBreak != 0 {
buf = append(buf, "HardLineBreak")
}
if flags&textRaw != 0 {
buf = append(buf, "Raw")
}
if flags&textCode != 0 {
buf = append(buf, "Code")
}
return strings.Join(buf, ", ")
}
// Inline implements Inline.Inline. // Inline implements Inline.Inline.
func (n *Text) Inline() { func (n *Text) Inline() {
} }
@ -130,7 +148,11 @@ func (n *Text) Text(source []byte) []byte {
// Dump implements Node.Dump. // Dump implements Node.Dump.
func (n *Text) Dump(source []byte, level int) { func (n *Text) Dump(source []byte, level int) {
fmt.Printf("%sText: \"%s\"\n", strings.Repeat(" ", level), strings.TrimRight(string(n.Text(source)), "\n")) fs := textFlagsString(n.flags)
if len(fs) != 0 {
fs = "(" + fs + ")"
}
fmt.Printf("%sText%s: \"%s\"\n", strings.Repeat(" ", level), fs, strings.TrimRight(string(n.Text(source)), "\n"))
} }
// KindText is a NodeKind of the Text node. // KindText is a NodeKind of the Text node.
@ -192,6 +214,77 @@ func MergeOrReplaceTextSegment(parent Node, n Node, s textm.Segment) {
} }
} }
// A String struct is a textual content that has a concrete value
type String struct {
BaseInline
Value []byte
flags uint8
}
// Inline implements Inline.Inline.
func (n *String) Inline() {
}
// IsRaw returns true if this text should be rendered without unescaping
// back slash escapes and resolving references.
func (n *String) IsRaw() bool {
return n.flags&textRaw != 0
}
// SetRaw sets whether this text should be rendered as raw contents.
func (n *String) SetRaw(v bool) {
if v {
n.flags |= textRaw
} else {
n.flags = n.flags &^ textRaw
}
}
// IsCode returns true if this text should be rendered without any
// modifications.
func (n *String) IsCode() bool {
return n.flags&textCode != 0
}
// SetCode sets whether this text should be rendered without any modifications.
func (n *String) SetCode(v bool) {
if v {
n.flags |= textCode
} else {
n.flags = n.flags &^ textCode
}
}
// Text implements Node.Text.
func (n *String) Text(source []byte) []byte {
return n.Value
}
// Dump implements Node.Dump.
func (n *String) Dump(source []byte, level int) {
fs := textFlagsString(n.flags)
if len(fs) != 0 {
fs = "(" + fs + ")"
}
fmt.Printf("%sString%s: \"%s\"\n", strings.Repeat(" ", level), fs, strings.TrimRight(string(n.Value), "\n"))
}
// KindString is a NodeKind of the String node.
var KindString = NewNodeKind("String")
// Kind implements Node.Kind.
func (n *String) Kind() NodeKind {
return KindString
}
// NewString returns a new String node.
func NewString(v []byte) *String {
return &String{
Value: v,
}
}
// A CodeSpan struct represents a code span of Markdown text. // A CodeSpan struct represents a code span of Markdown text.
type CodeSpan struct { type CodeSpan struct {
BaseInline BaseInline

View file

@ -1,32 +0,0 @@
package ast
import (
gast "github.com/yuin/goldmark/ast"
)
// A TypographicText struct represents text that
// typographic text replaces certain punctuations.
type TypographicText struct {
gast.BaseInline
Value []byte
}
// Dump implements Node.Dump.
func (n *TypographicText) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, nil)
}
// KindTypographicText is a NodeKind of the TypographicText node.
var KindTypographicText = gast.NewNodeKind("TypographicText")
// Kind implements Node.Kind.
func (n *TypographicText) Kind() gast.NodeKind {
return KindTypographicText
}
// NewTypographicText returns a new TypographicText node.
func NewTypographicText(value []byte) *TypographicText {
return &TypographicText{
Value: value,
}
}

View file

@ -3,10 +3,7 @@ package extension
import ( import (
"github.com/yuin/goldmark" "github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast" gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text" "github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util" "github.com/yuin/goldmark/util"
) )
@ -147,13 +144,15 @@ func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser
if len(line) > 2 { if len(line) > 2 {
if c == '-' { if c == '-' {
if s.Substitutions[EmDash] != nil && line[1] == '-' && line[2] == '-' { // --- if s.Substitutions[EmDash] != nil && line[1] == '-' && line[2] == '-' { // ---
node := ast.NewTypographicText(s.Substitutions[EmDash]) node := gast.NewString(s.Substitutions[EmDash])
node.SetCode(true)
block.Advance(3) block.Advance(3)
return node return node
} }
} else if c == '.' { } else if c == '.' {
if s.Substitutions[Ellipsis] != nil && line[1] == '.' && line[2] == '.' { // ... if s.Substitutions[Ellipsis] != nil && line[1] == '.' && line[2] == '.' { // ...
node := ast.NewTypographicText(s.Substitutions[Ellipsis]) node := gast.NewString(s.Substitutions[Ellipsis])
node.SetCode(true)
block.Advance(3) block.Advance(3)
return node return node
} }
@ -163,20 +162,23 @@ func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser
if len(line) > 1 { if len(line) > 1 {
if c == '<' { if c == '<' {
if s.Substitutions[LeftAngleQuote] != nil && line[1] == '<' { // << if s.Substitutions[LeftAngleQuote] != nil && line[1] == '<' { // <<
node := ast.NewTypographicText(s.Substitutions[LeftAngleQuote]) node := gast.NewString(s.Substitutions[LeftAngleQuote])
node.SetCode(true)
block.Advance(2) block.Advance(2)
return node return node
} }
return nil return nil
} else if c == '>' { } else if c == '>' {
if s.Substitutions[RightAngleQuote] != nil && line[1] == '>' { // >> if s.Substitutions[RightAngleQuote] != nil && line[1] == '>' { // >>
node := ast.NewTypographicText(s.Substitutions[RightAngleQuote]) node := gast.NewString(s.Substitutions[RightAngleQuote])
node.SetCode(true)
block.Advance(2) block.Advance(2)
return node return node
} }
return nil return nil
} else if s.Substitutions[EnDash] != nil && c == '-' && line[1] == '-' { // -- } else if s.Substitutions[EnDash] != nil && c == '-' && line[1] == '-' { // --
node := ast.NewTypographicText(s.Substitutions[EnDash]) node := gast.NewString(s.Substitutions[EnDash])
node.SetCode(true)
block.Advance(2) block.Advance(2)
return node return node
} }
@ -188,24 +190,28 @@ func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser
} }
if c == '\'' { if c == '\'' {
if s.Substitutions[LeftSingleQuote] != nil && d.CanOpen && !d.CanClose { if s.Substitutions[LeftSingleQuote] != nil && d.CanOpen && !d.CanClose {
node := ast.NewTypographicText(s.Substitutions[LeftSingleQuote]) node := gast.NewString(s.Substitutions[LeftSingleQuote])
node.SetCode(true)
block.Advance(1) block.Advance(1)
return node return node
} }
if s.Substitutions[RightSingleQuote] != nil && d.CanClose && !d.CanOpen { if s.Substitutions[RightSingleQuote] != nil && d.CanClose && !d.CanOpen {
node := ast.NewTypographicText(s.Substitutions[RightSingleQuote]) node := gast.NewString(s.Substitutions[RightSingleQuote])
node.SetCode(true)
block.Advance(1) block.Advance(1)
return node return node
} }
} }
if c == '"' { if c == '"' {
if s.Substitutions[LeftDoubleQuote] != nil && d.CanOpen && !d.CanClose { if s.Substitutions[LeftDoubleQuote] != nil && d.CanOpen && !d.CanClose {
node := ast.NewTypographicText(s.Substitutions[LeftDoubleQuote]) node := gast.NewString(s.Substitutions[LeftDoubleQuote])
node.SetCode(true)
block.Advance(1) block.Advance(1)
return node return node
} }
if s.Substitutions[RightDoubleQuote] != nil && d.CanClose && !d.CanOpen { if s.Substitutions[RightDoubleQuote] != nil && d.CanClose && !d.CanOpen {
node := ast.NewTypographicText(s.Substitutions[RightDoubleQuote]) node := gast.NewString(s.Substitutions[RightDoubleQuote])
node.SetCode(true)
block.Advance(1) block.Advance(1)
return node return node
} }
@ -218,35 +224,6 @@ func (s *typographerParser) CloseBlock(parent gast.Node, pc parser.Context) {
// nothing to do // nothing to do
} }
// TypographerHTMLRenderer is a renderer.NodeRenderer implementation that
// renders Typographer nodes.
type TypographerHTMLRenderer struct {
html.Config
}
// NewTypographerHTMLRenderer returns a new TypographerHTMLRenderer.
func NewTypographerHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
r := &TypographerHTMLRenderer{
Config: html.NewConfig(),
}
for _, opt := range opts {
opt.SetHTMLOption(&r.Config)
}
return r
}
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *TypographerHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindTypographicText, r.renderTypographicText)
}
func (r *TypographerHTMLRenderer) renderTypographicText(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
w.Write(n.(*ast.TypographicText).Value)
}
return gast.WalkContinue, nil
}
type typographer struct { type typographer struct {
options []TypographerOption options []TypographerOption
} }
@ -265,7 +242,4 @@ func (e *typographer) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers( m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewTypographerParser(e.options...), 9999), util.Prioritized(NewTypographerParser(e.options...), 9999),
)) ))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewTypographerHTMLRenderer(), 500),
))
} }

View file

@ -183,6 +183,7 @@ func (r *Renderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindLink, r.renderLink) reg.Register(ast.KindLink, r.renderLink)
reg.Register(ast.KindRawHTML, r.renderRawHTML) reg.Register(ast.KindRawHTML, r.renderRawHTML)
reg.Register(ast.KindText, r.renderText) reg.Register(ast.KindText, r.renderText)
reg.Register(ast.KindString, r.renderString)
} }
func (r *Renderer) writeLines(w util.BufWriter, source []byte, n ast.Node) { func (r *Renderer) writeLines(w util.BufWriter, source []byte, n ast.Node) {
@ -198,40 +199,38 @@ func (r *Renderer) renderDocument(w util.BufWriter, source []byte, node ast.Node
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
var attrNameID = []byte("id")
func (r *Renderer) renderHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { func (r *Renderer) renderHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Heading) n := node.(*ast.Heading)
if entering { if entering {
w.WriteString("<h") _, _ = w.WriteString("<h")
w.WriteByte("0123456"[n.Level]) _ = w.WriteByte("0123456"[n.Level])
if n.Attributes() != nil { if n.Attributes() != nil {
r.RenderAttributes(w, node) r.RenderAttributes(w, node)
} }
w.WriteByte('>') _ = w.WriteByte('>')
} else { } else {
w.WriteString("</h") _, _ = w.WriteString("</h")
w.WriteByte("0123456"[n.Level]) _ = w.WriteByte("0123456"[n.Level])
w.WriteString(">\n") _, _ = w.WriteString(">\n")
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
func (r *Renderer) renderBlockquote(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { func (r *Renderer) renderBlockquote(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering { if entering {
w.WriteString("<blockquote>\n") _, _ = w.WriteString("<blockquote>\n")
} else { } else {
w.WriteString("</blockquote>\n") _, _ = w.WriteString("</blockquote>\n")
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
func (r *Renderer) renderCodeBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { func (r *Renderer) renderCodeBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering { if entering {
w.WriteString("<pre><code>") _, _ = w.WriteString("<pre><code>")
r.writeLines(w, source, n) r.writeLines(w, source, n)
} else { } else {
w.WriteString("</code></pre>\n") _, _ = w.WriteString("</code></pre>\n")
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
@ -239,17 +238,17 @@ func (r *Renderer) renderCodeBlock(w util.BufWriter, source []byte, n ast.Node,
func (r *Renderer) renderFencedCodeBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { func (r *Renderer) renderFencedCodeBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.FencedCodeBlock) n := node.(*ast.FencedCodeBlock)
if entering { if entering {
w.WriteString("<pre><code") _, _ = w.WriteString("<pre><code")
language := n.Language(source) language := n.Language(source)
if language != nil { if language != nil {
w.WriteString(" class=\"language-") _, _ = w.WriteString(" class=\"language-")
r.Writer.Write(w, language) r.Writer.Write(w, language)
w.WriteString("\"") _, _ = w.WriteString("\"")
} }
w.WriteByte('>') _ = w.WriteByte('>')
r.writeLines(w, source, n) r.writeLines(w, source, n)
} else { } else {
w.WriteString("</code></pre>\n") _, _ = w.WriteString("</code></pre>\n")
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
@ -261,18 +260,18 @@ func (r *Renderer) renderHTMLBlock(w util.BufWriter, source []byte, node ast.Nod
l := n.Lines().Len() l := n.Lines().Len()
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
line := n.Lines().At(i) line := n.Lines().At(i)
w.Write(line.Value(source)) _, _ = w.Write(line.Value(source))
} }
} else { } else {
w.WriteString("<!-- raw HTML omitted -->\n") _, _ = w.WriteString("<!-- raw HTML omitted -->\n")
} }
} else { } else {
if n.HasClosure() { if n.HasClosure() {
if r.Unsafe { if r.Unsafe {
closure := n.ClosureLine closure := n.ClosureLine
w.Write(closure.Value(source)) _, _ = w.Write(closure.Value(source))
} else { } else {
w.WriteString("<!-- raw HTML omitted -->\n") _, _ = w.WriteString("<!-- raw HTML omitted -->\n")
} }
} }
} }
@ -286,41 +285,41 @@ func (r *Renderer) renderList(w util.BufWriter, source []byte, node ast.Node, en
tag = "ol" tag = "ol"
} }
if entering { if entering {
w.WriteByte('<') _ = w.WriteByte('<')
w.WriteString(tag) _, _ = w.WriteString(tag)
if n.IsOrdered() && n.Start != 1 { if n.IsOrdered() && n.Start != 1 {
fmt.Fprintf(w, " start=\"%d\">\n", n.Start) fmt.Fprintf(w, " start=\"%d\">\n", n.Start)
} else { } else {
w.WriteString(">\n") _, _ = w.WriteString(">\n")
} }
} else { } else {
w.WriteString("</") _, _ = w.WriteString("</")
w.WriteString(tag) _, _ = w.WriteString(tag)
w.WriteString(">\n") _, _ = w.WriteString(">\n")
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
func (r *Renderer) renderListItem(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { func (r *Renderer) renderListItem(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering { if entering {
w.WriteString("<li>") _, _ = w.WriteString("<li>")
fc := n.FirstChild() fc := n.FirstChild()
if fc != nil { if fc != nil {
if _, ok := fc.(*ast.TextBlock); !ok { if _, ok := fc.(*ast.TextBlock); !ok {
w.WriteByte('\n') _ = w.WriteByte('\n')
} }
} }
} else { } else {
w.WriteString("</li>\n") _, _ = w.WriteString("</li>\n")
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
func (r *Renderer) renderParagraph(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { func (r *Renderer) renderParagraph(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering { if entering {
w.WriteString("<p>") _, _ = w.WriteString("<p>")
} else { } else {
w.WriteString("</p>\n") _, _ = w.WriteString("</p>\n")
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
@ -328,7 +327,7 @@ func (r *Renderer) renderParagraph(w util.BufWriter, source []byte, n ast.Node,
func (r *Renderer) renderTextBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { func (r *Renderer) renderTextBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering { if !entering {
if _, ok := n.NextSibling().(ast.Node); ok && n.FirstChild() != nil { if _, ok := n.NextSibling().(ast.Node); ok && n.FirstChild() != nil {
w.WriteByte('\n') _ = w.WriteByte('\n')
} }
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
@ -339,9 +338,9 @@ func (r *Renderer) renderThemanticBreak(w util.BufWriter, source []byte, n ast.N
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
if r.XHTML { if r.XHTML {
w.WriteString("<hr />\n") _, _ = w.WriteString("<hr />\n")
} else { } else {
w.WriteString("<hr>\n") _, _ = w.WriteString("<hr>\n")
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
@ -351,22 +350,22 @@ func (r *Renderer) renderAutoLink(w util.BufWriter, source []byte, node ast.Node
if !entering { if !entering {
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
w.WriteString(`<a href="`) _, _ = w.WriteString(`<a href="`)
url := n.URL(source) url := n.URL(source)
label := n.Label(source) label := n.Label(source)
if n.AutoLinkType == ast.AutoLinkEmail && !bytes.HasPrefix(bytes.ToLower(url), []byte("mailto:")) { if n.AutoLinkType == ast.AutoLinkEmail && !bytes.HasPrefix(bytes.ToLower(url), []byte("mailto:")) {
w.WriteString("mailto:") _, _ = w.WriteString("mailto:")
} }
w.Write(util.EscapeHTML(util.URLEscape(url, false))) _, _ = w.Write(util.EscapeHTML(util.URLEscape(url, false)))
w.WriteString(`">`) _, _ = w.WriteString(`">`)
w.Write(util.EscapeHTML(label)) _, _ = w.Write(util.EscapeHTML(label))
w.WriteString(`</a>`) _, _ = w.WriteString(`</a>`)
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
func (r *Renderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { func (r *Renderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering { if entering {
w.WriteString("<code>") _, _ = w.WriteString("<code>")
for c := n.FirstChild(); c != nil; c = c.NextSibling() { for c := n.FirstChild(); c != nil; c = c.NextSibling() {
segment := c.(*ast.Text).Segment segment := c.(*ast.Text).Segment
value := segment.Value(source) value := segment.Value(source)
@ -381,7 +380,7 @@ func (r *Renderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, e
} }
return ast.WalkSkipChildren, nil return ast.WalkSkipChildren, nil
} }
w.WriteString("</code>") _, _ = w.WriteString("</code>")
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
@ -392,13 +391,13 @@ func (r *Renderer) renderEmphasis(w util.BufWriter, source []byte, node ast.Node
tag = "strong" tag = "strong"
} }
if entering { if entering {
w.WriteByte('<') _ = w.WriteByte('<')
w.WriteString(tag) _, _ = w.WriteString(tag)
w.WriteByte('>') _ = w.WriteByte('>')
} else { } else {
w.WriteString("</") _, _ = w.WriteString("</")
w.WriteString(tag) _, _ = w.WriteString(tag)
w.WriteByte('>') _ = w.WriteByte('>')
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
@ -406,19 +405,19 @@ func (r *Renderer) renderEmphasis(w util.BufWriter, source []byte, node ast.Node
func (r *Renderer) renderLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { func (r *Renderer) renderLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Link) n := node.(*ast.Link)
if entering { if entering {
w.WriteString("<a href=\"") _, _ = w.WriteString("<a href=\"")
if r.Unsafe || !IsDangerousURL(n.Destination) { if r.Unsafe || !IsDangerousURL(n.Destination) {
w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true))) _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
} }
w.WriteByte('"') _ = w.WriteByte('"')
if n.Title != nil { if n.Title != nil {
w.WriteString(` title="`) _, _ = w.WriteString(` title="`)
r.Writer.Write(w, n.Title) r.Writer.Write(w, n.Title)
w.WriteByte('"') _ = w.WriteByte('"')
} }
w.WriteByte('>') _ = w.WriteByte('>')
} else { } else {
w.WriteString("</a>") _, _ = w.WriteString("</a>")
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
@ -427,22 +426,22 @@ func (r *Renderer) renderImage(w util.BufWriter, source []byte, node ast.Node, e
return ast.WalkContinue, nil return ast.WalkContinue, nil
} }
n := node.(*ast.Image) n := node.(*ast.Image)
w.WriteString("<img src=\"") _, _ = w.WriteString("<img src=\"")
if r.Unsafe || !IsDangerousURL(n.Destination) { if r.Unsafe || !IsDangerousURL(n.Destination) {
w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true))) _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
} }
w.WriteString(`" alt="`) _, _ = w.WriteString(`" alt="`)
w.Write(n.Text(source)) _, _ = w.Write(n.Text(source))
w.WriteByte('"') _ = w.WriteByte('"')
if n.Title != nil { if n.Title != nil {
w.WriteString(` title="`) _, _ = w.WriteString(` title="`)
r.Writer.Write(w, n.Title) r.Writer.Write(w, n.Title)
w.WriteByte('"') _ = w.WriteByte('"')
} }
if r.XHTML { if r.XHTML {
w.WriteString(" />") _, _ = w.WriteString(" />")
} else { } else {
w.WriteString(">") _, _ = w.WriteString(">")
} }
return ast.WalkSkipChildren, nil return ast.WalkSkipChildren, nil
} }
@ -456,11 +455,11 @@ func (r *Renderer) renderRawHTML(w util.BufWriter, source []byte, node ast.Node,
l := n.Segments.Len() l := n.Segments.Len()
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
segment := n.Segments.At(i) segment := n.Segments.At(i)
w.Write(segment.Value(source)) _, _ = w.Write(segment.Value(source))
} }
return ast.WalkSkipChildren, nil return ast.WalkSkipChildren, nil
} }
w.WriteString("<!-- raw HTML omitted -->") _, _ = w.WriteString("<!-- raw HTML omitted -->")
return ast.WalkSkipChildren, nil return ast.WalkSkipChildren, nil
} }
@ -476,12 +475,29 @@ func (r *Renderer) renderText(w util.BufWriter, source []byte, node ast.Node, en
r.Writer.Write(w, segment.Value(source)) r.Writer.Write(w, segment.Value(source))
if n.HardLineBreak() || (n.SoftLineBreak() && r.HardWraps) { if n.HardLineBreak() || (n.SoftLineBreak() && r.HardWraps) {
if r.XHTML { if r.XHTML {
w.WriteString("<br />\n") _, _ = w.WriteString("<br />\n")
} else { } else {
w.WriteString("<br>\n") _, _ = w.WriteString("<br>\n")
} }
} else if n.SoftLineBreak() { } else if n.SoftLineBreak() {
w.WriteByte('\n') _ = w.WriteByte('\n')
}
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderString(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
n := node.(*ast.String)
if n.IsCode() {
_, _ = w.Write(n.Value)
} else {
if n.IsRaw() {
r.Writer.RawWrite(w, n.Value)
} else {
r.Writer.Write(w, n.Value)
} }
} }
return ast.WalkContinue, nil return ast.WalkContinue, nil
@ -490,11 +506,11 @@ func (r *Renderer) renderText(w util.BufWriter, source []byte, node ast.Node, en
// RenderAttributes renders given node's attributes. // RenderAttributes renders given node's attributes.
func (r *Renderer) RenderAttributes(w util.BufWriter, node ast.Node) { func (r *Renderer) RenderAttributes(w util.BufWriter, node ast.Node) {
for _, attr := range node.Attributes() { for _, attr := range node.Attributes() {
w.WriteString(" ") _, _ = w.WriteString(" ")
w.Write(attr.Name) _, _ = w.Write(attr.Name)
w.WriteString(`="`) _, _ = w.WriteString(`="`)
w.Write(util.EscapeHTML(attr.Value)) _, _ = w.Write(util.EscapeHTML(attr.Value))
w.WriteByte('"') _ = w.WriteByte('"')
} }
} }
@ -516,11 +532,11 @@ func escapeRune(writer util.BufWriter, r rune) {
if r < 256 { if r < 256 {
v := util.EscapeHTMLByte(byte(r)) v := util.EscapeHTMLByte(byte(r))
if v != nil { if v != nil {
writer.Write(v) _, _ = writer.Write(v)
return return
} }
} }
writer.WriteRune(util.ToValidRune(r)) _, _ = writer.WriteRune(util.ToValidRune(r))
} }
func (d *defaultWriter) RawWrite(writer util.BufWriter, source []byte) { func (d *defaultWriter) RawWrite(writer util.BufWriter, source []byte) {
@ -529,21 +545,21 @@ func (d *defaultWriter) RawWrite(writer util.BufWriter, source []byte) {
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
v := util.EscapeHTMLByte(source[i]) v := util.EscapeHTMLByte(source[i])
if v != nil { if v != nil {
writer.Write(source[i-n : i]) _, _ = writer.Write(source[i-n : i])
n = 0 n = 0
writer.Write(v) _, _ = writer.Write(v)
continue continue
} }
n++ n++
} }
if n != 0 { if n != 0 {
writer.Write(source[l-n:]) _, _ = writer.Write(source[l-n:])
} }
} }
func (d *defaultWriter) Write(writer util.BufWriter, source []byte) { func (d *defaultWriter) Write(writer util.BufWriter, source []byte) {
escaped := false escaped := false
ok := false var ok bool
limit := len(source) limit := len(source)
n := 0 n := 0
for i := 0; i < limit; i++ { for i := 0; i < limit; i++ {