From 05645dd3c4666d1c9454cf5ff3e4270ab6325cc6 Mon Sep 17 00:00:00 2001
From: yuin
Date: Thu, 30 May 2019 16:07:04 +0900
Subject: [PATCH] Add String node
---
ast/inline.go | 95 ++++++++++++++++++-
extension/ast/typographer.go | 32 -------
extension/typographer.go | 62 ++++--------
renderer/html/html.go | 178 +++++++++++++++++++----------------
4 files changed, 209 insertions(+), 158 deletions(-)
delete mode 100644 extension/ast/typographer.go
diff --git a/ast/inline.go b/ast/inline.go
index 7404a3f..8c20402 100644
--- a/ast/inline.go
+++ b/ast/inline.go
@@ -56,8 +56,26 @@ const (
textSoftLineBreak = 1 << iota
textHardLineBreak
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.
func (n *Text) Inline() {
}
@@ -130,7 +148,11 @@ func (n *Text) Text(source []byte) []byte {
// Dump implements Node.Dump.
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.
@@ -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.
type CodeSpan struct {
BaseInline
diff --git a/extension/ast/typographer.go b/extension/ast/typographer.go
deleted file mode 100644
index 4101b4d..0000000
--- a/extension/ast/typographer.go
+++ /dev/null
@@ -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,
- }
-}
diff --git a/extension/typographer.go b/extension/typographer.go
index 490fb5a..a267980 100644
--- a/extension/typographer.go
+++ b/extension/typographer.go
@@ -3,10 +3,7 @@ package extension
import (
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
- "github.com/yuin/goldmark/extension/ast"
"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/util"
)
@@ -147,13 +144,15 @@ func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser
if len(line) > 2 {
if c == '-' {
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)
return node
}
} else if c == '.' {
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)
return node
}
@@ -163,20 +162,23 @@ func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser
if len(line) > 1 {
if c == '<' {
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)
return node
}
return nil
} else if c == '>' {
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)
return node
}
return nil
} 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)
return node
}
@@ -188,24 +190,28 @@ func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser
}
if c == '\'' {
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)
return node
}
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)
return node
}
}
if c == '"' {
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)
return node
}
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)
return node
}
@@ -218,35 +224,6 @@ func (s *typographerParser) CloseBlock(parent gast.Node, pc parser.Context) {
// 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 {
options []TypographerOption
}
@@ -265,7 +242,4 @@ func (e *typographer) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewTypographerParser(e.options...), 9999),
))
- m.Renderer().AddOptions(renderer.WithNodeRenderers(
- util.Prioritized(NewTypographerHTMLRenderer(), 500),
- ))
}
diff --git a/renderer/html/html.go b/renderer/html/html.go
index 8551b78..bfd9761 100644
--- a/renderer/html/html.go
+++ b/renderer/html/html.go
@@ -183,6 +183,7 @@ func (r *Renderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindLink, r.renderLink)
reg.Register(ast.KindRawHTML, r.renderRawHTML)
reg.Register(ast.KindText, r.renderText)
+ reg.Register(ast.KindString, r.renderString)
}
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
}
-var attrNameID = []byte("id")
-
func (r *Renderer) renderHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Heading)
if entering {
- w.WriteString("')
+ _ = w.WriteByte('>')
} else {
- w.WriteString("\n")
+ _, _ = w.WriteString("\n")
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderBlockquote(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
- w.WriteString("\n")
+ _, _ = w.WriteString("\n")
} else {
- w.WriteString("
\n")
+ _, _ = w.WriteString("
\n")
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderCodeBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
- w.WriteString("")
+ _, _ = w.WriteString("")
r.writeLines(w, source, n)
} else {
- w.WriteString("
\n")
+ _, _ = w.WriteString("
\n")
}
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) {
n := node.(*ast.FencedCodeBlock)
if entering {
- w.WriteString("')
+ _ = w.WriteByte('>')
r.writeLines(w, source, n)
} else {
- w.WriteString("
\n")
+ _, _ = w.WriteString("\n")
}
return ast.WalkContinue, nil
}
@@ -261,18 +260,18 @@ func (r *Renderer) renderHTMLBlock(w util.BufWriter, source []byte, node ast.Nod
l := n.Lines().Len()
for i := 0; i < l; i++ {
line := n.Lines().At(i)
- w.Write(line.Value(source))
+ _, _ = w.Write(line.Value(source))
}
} else {
- w.WriteString("\n")
+ _, _ = w.WriteString("\n")
}
} else {
if n.HasClosure() {
if r.Unsafe {
closure := n.ClosureLine
- w.Write(closure.Value(source))
+ _, _ = w.Write(closure.Value(source))
} else {
- w.WriteString("\n")
+ _, _ = w.WriteString("\n")
}
}
}
@@ -286,41 +285,41 @@ func (r *Renderer) renderList(w util.BufWriter, source []byte, node ast.Node, en
tag = "ol"
}
if entering {
- w.WriteByte('<')
- w.WriteString(tag)
+ _ = w.WriteByte('<')
+ _, _ = w.WriteString(tag)
if n.IsOrdered() && n.Start != 1 {
fmt.Fprintf(w, " start=\"%d\">\n", n.Start)
} else {
- w.WriteString(">\n")
+ _, _ = w.WriteString(">\n")
}
} else {
- w.WriteString("")
- w.WriteString(tag)
- w.WriteString(">\n")
+ _, _ = w.WriteString("")
+ _, _ = w.WriteString(tag)
+ _, _ = w.WriteString(">\n")
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderListItem(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
- w.WriteString("")
+ _, _ = w.WriteString("")
fc := n.FirstChild()
if fc != nil {
if _, ok := fc.(*ast.TextBlock); !ok {
- w.WriteByte('\n')
+ _ = w.WriteByte('\n')
}
}
} else {
- w.WriteString("\n")
+ _, _ = w.WriteString("\n")
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderParagraph(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
- w.WriteString("")
+ _, _ = w.WriteString("
")
} else {
- w.WriteString("
\n")
+ _, _ = w.WriteString("
\n")
}
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) {
if !entering {
if _, ok := n.NextSibling().(ast.Node); ok && n.FirstChild() != nil {
- w.WriteByte('\n')
+ _ = w.WriteByte('\n')
}
}
return ast.WalkContinue, nil
@@ -339,9 +338,9 @@ func (r *Renderer) renderThemanticBreak(w util.BufWriter, source []byte, n ast.N
return ast.WalkContinue, nil
}
if r.XHTML {
- w.WriteString("
\n")
+ _, _ = w.WriteString("
\n")
} else {
- w.WriteString("
\n")
+ _, _ = w.WriteString("
\n")
}
return ast.WalkContinue, nil
}
@@ -351,22 +350,22 @@ func (r *Renderer) renderAutoLink(w util.BufWriter, source []byte, node ast.Node
if !entering {
return ast.WalkContinue, nil
}
- w.WriteString(``)
- w.Write(util.EscapeHTML(label))
- w.WriteString(``)
+ _, _ = w.Write(util.EscapeHTML(util.URLEscape(url, false)))
+ _, _ = w.WriteString(`">`)
+ _, _ = w.Write(util.EscapeHTML(label))
+ _, _ = w.WriteString(``)
return ast.WalkContinue, nil
}
func (r *Renderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
- w.WriteString("")
+ _, _ = w.WriteString("")
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
segment := c.(*ast.Text).Segment
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
}
- w.WriteString("")
+ _, _ = w.WriteString("")
return ast.WalkContinue, nil
}
@@ -392,13 +391,13 @@ func (r *Renderer) renderEmphasis(w util.BufWriter, source []byte, node ast.Node
tag = "strong"
}
if entering {
- w.WriteByte('<')
- w.WriteString(tag)
- w.WriteByte('>')
+ _ = w.WriteByte('<')
+ _, _ = w.WriteString(tag)
+ _ = w.WriteByte('>')
} else {
- w.WriteString("")
- w.WriteString(tag)
- w.WriteByte('>')
+ _, _ = w.WriteString("")
+ _, _ = w.WriteString(tag)
+ _ = w.WriteByte('>')
}
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) {
n := node.(*ast.Link)
if entering {
- w.WriteString("')
+ _ = w.WriteByte('>')
} else {
- w.WriteString("")
+ _, _ = w.WriteString("")
}
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
}
n := node.(*ast.Image)
- w.WriteString("
")
+ _, _ = w.WriteString(" />")
} else {
- w.WriteString(">")
+ _, _ = w.WriteString(">")
}
return ast.WalkSkipChildren, nil
}
@@ -456,11 +455,11 @@ func (r *Renderer) renderRawHTML(w util.BufWriter, source []byte, node ast.Node,
l := n.Segments.Len()
for i := 0; i < l; i++ {
segment := n.Segments.At(i)
- w.Write(segment.Value(source))
+ _, _ = w.Write(segment.Value(source))
}
return ast.WalkSkipChildren, nil
}
- w.WriteString("")
+ _, _ = w.WriteString("")
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))
if n.HardLineBreak() || (n.SoftLineBreak() && r.HardWraps) {
if r.XHTML {
- w.WriteString("
\n")
+ _, _ = w.WriteString("
\n")
} else {
- w.WriteString("
\n")
+ _, _ = w.WriteString("
\n")
}
} 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
@@ -490,11 +506,11 @@ func (r *Renderer) renderText(w util.BufWriter, source []byte, node ast.Node, en
// RenderAttributes renders given node's attributes.
func (r *Renderer) RenderAttributes(w util.BufWriter, node ast.Node) {
for _, attr := range node.Attributes() {
- w.WriteString(" ")
- w.Write(attr.Name)
- w.WriteString(`="`)
- w.Write(util.EscapeHTML(attr.Value))
- w.WriteByte('"')
+ _, _ = w.WriteString(" ")
+ _, _ = w.Write(attr.Name)
+ _, _ = w.WriteString(`="`)
+ _, _ = w.Write(util.EscapeHTML(attr.Value))
+ _ = w.WriteByte('"')
}
}
@@ -516,11 +532,11 @@ func escapeRune(writer util.BufWriter, r rune) {
if r < 256 {
v := util.EscapeHTMLByte(byte(r))
if v != nil {
- writer.Write(v)
+ _, _ = writer.Write(v)
return
}
}
- writer.WriteRune(util.ToValidRune(r))
+ _, _ = writer.WriteRune(util.ToValidRune(r))
}
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++ {
v := util.EscapeHTMLByte(source[i])
if v != nil {
- writer.Write(source[i-n : i])
+ _, _ = writer.Write(source[i-n : i])
n = 0
- writer.Write(v)
+ _, _ = writer.Write(v)
continue
}
n++
}
if n != 0 {
- writer.Write(source[l-n:])
+ _, _ = writer.Write(source[l-n:])
}
}
func (d *defaultWriter) Write(writer util.BufWriter, source []byte) {
escaped := false
- ok := false
+ var ok bool
limit := len(source)
n := 0
for i := 0; i < limit; i++ {