diff --git a/go.mod b/go.mod index 0dafbeaec..e3616a99f 100644 --- a/go.mod +++ b/go.mod @@ -100,7 +100,7 @@ require ( github.com/urfave/cli v1.20.0 github.com/xanzy/go-gitlab v0.31.0 github.com/yohcop/openid-go v1.0.0 - github.com/yuin/goldmark v1.1.32 + github.com/yuin/goldmark v1.2.1 github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 diff --git a/go.sum b/go.sum index 2bb1864dc..159a5b5f7 100644 --- a/go.sum +++ b/go.sum @@ -715,6 +715,8 @@ github.com/yuin/goldmark v1.1.25 h1:isv+Q6HQAmmL2Ofcmg8QauBmDPlUUnSoNhEcC940Rds= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32 h1:5tjfNdR2ki3yYQ842+eX2sQHeiwpKJ0RnHO4IYOc4V8= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 h1:VWSxtAiQNh3zgHJpdpkpVYjTPqRE3P6UZCOPa1nRDio= github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691/go.mod h1:YLF3kDffRfUH/bTxOxHhV6lxwIB3Vfj91rEwNMS9MXo= github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 h1:gZucqLjL1eDzVWrXj4uiWeMbAopJlBR2mKQAsTGdPwo= diff --git a/vendor/github.com/yuin/goldmark/README.md b/vendor/github.com/yuin/goldmark/README.md index b0923c930..8cf7c5a0e 100644 --- a/vendor/github.com/yuin/goldmark/README.md +++ b/vendor/github.com/yuin/goldmark/README.md @@ -203,6 +203,18 @@ heading {#id .className attrName=attrValue} ============ ``` +### Table extension +The Table extension implements [Table(extension)](https://github.github.com/gfm/#tables-extension-), as +defined in [GitHub Flavored Markdown Spec](https://github.github.com/gfm/). + +Specs are defined for XHTML, so specs use some deprecated attributes for HTML5. + +You can override alignment rendering method via options. + +| Functional option | Type | Description | +| ----------------- | ---- | ----------- | +| `extension.WithTableCellAlignMethod` | `extension.TableCellAlignMethod` | Option indicates how are table cells aligned. | + ### Typographer extension The Typographer extension translates plain ASCII punctuation characters into typographic-punctuation HTML entities. @@ -219,7 +231,7 @@ Default substitutions are: | `<<` | `«` | | `>>` | `»` | -You can override the defualt substitutions via `extensions.WithTypographicSubstitutions`: +You can override the default substitutions via `extensions.WithTypographicSubstitutions`: ```go markdown := goldmark.New( diff --git a/vendor/github.com/yuin/goldmark/extension/table.go b/vendor/github.com/yuin/goldmark/extension/table.go index 91ba33199..f0e994e83 100644 --- a/vendor/github.com/yuin/goldmark/extension/table.go +++ b/vendor/github.com/yuin/goldmark/extension/table.go @@ -15,7 +15,113 @@ import ( "github.com/yuin/goldmark/util" ) -var tableDelimRegexp = regexp.MustCompile(`^[\s\-\|\:]+$`) +// TableCellAlignMethod indicates how are table cells aligned in HTML format.indicates how are table cells aligned in HTML format. +type TableCellAlignMethod int + +const ( + // TableCellAlignDefault renders alignments by default method. + // With XHTML, alignments are rendered as an align attribute. + // With HTML5, alignments are rendered as a style attribute. + TableCellAlignDefault TableCellAlignMethod = iota + + // TableCellAlignAttribute renders alignments as an align attribute. + TableCellAlignAttribute + + // TableCellAlignStyle renders alignments as a style attribute. + TableCellAlignStyle + + // TableCellAlignNone does not care about alignments. + // If you using classes or other styles, you can add these attributes + // in an ASTTransformer. + TableCellAlignNone +) + +// TableConfig struct holds options for the extension. +type TableConfig struct { + html.Config + + // TableCellAlignMethod indicates how are table celss aligned. + TableCellAlignMethod TableCellAlignMethod +} + +// TableOption interface is a functional option interface for the extension. +type TableOption interface { + renderer.Option + // SetTableOption sets given option to the extension. + SetTableOption(*TableConfig) +} + +// NewTableConfig returns a new Config with defaults. +func NewTableConfig() TableConfig { + return TableConfig{ + Config: html.NewConfig(), + TableCellAlignMethod: TableCellAlignDefault, + } +} + +// SetOption implements renderer.SetOptioner. +func (c *TableConfig) SetOption(name renderer.OptionName, value interface{}) { + switch name { + case optTableCellAlignMethod: + c.TableCellAlignMethod = value.(TableCellAlignMethod) + default: + c.Config.SetOption(name, value) + } +} + +type withTableHTMLOptions struct { + value []html.Option +} + +func (o *withTableHTMLOptions) SetConfig(c *renderer.Config) { + if o.value != nil { + for _, v := range o.value { + v.(renderer.Option).SetConfig(c) + } + } +} + +func (o *withTableHTMLOptions) SetTableOption(c *TableConfig) { + if o.value != nil { + for _, v := range o.value { + v.SetHTMLOption(&c.Config) + } + } +} + +// WithTableHTMLOptions is functional option that wraps goldmark HTMLRenderer options. +func WithTableHTMLOptions(opts ...html.Option) TableOption { + return &withTableHTMLOptions{opts} +} + +const optTableCellAlignMethod renderer.OptionName = "TableTableCellAlignMethod" + +type withTableCellAlignMethod struct { + value TableCellAlignMethod +} + +func (o *withTableCellAlignMethod) SetConfig(c *renderer.Config) { + c.Options[optTableCellAlignMethod] = o.value +} + +func (o *withTableCellAlignMethod) SetTableOption(c *TableConfig) { + c.TableCellAlignMethod = o.value +} + +// WithTableCellAlignMethod is a functional option that indicates how are table cells aligned in HTML format. +func WithTableCellAlignMethod(a TableCellAlignMethod) TableOption { + return &withTableCellAlignMethod{a} +} + +func isTableDelim(bs []byte) bool { + for _, b := range bs { + if !(util.IsSpace(b) || b == '-' || b == '|' || b == ':') { + return false + } + } + return true +} + var tableDelimLeft = regexp.MustCompile(`^\s*\:\-+\s*$`) var tableDelimRight = regexp.MustCompile(`^\s*\-+\:\s*$`) var tableDelimCenter = regexp.MustCompile(`^\s*\:\-+\:\s*$`) @@ -37,22 +143,31 @@ func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text. if lines.Len() < 2 { return } - alignments := b.parseDelimiter(lines.At(1), reader) - if alignments == nil { - return - } - header := b.parseRow(lines.At(0), alignments, true, reader) - if header == nil || len(alignments) != header.ChildCount() { - return - } - table := ast.NewTable() - table.Alignments = alignments - table.AppendChild(table, ast.NewTableHeader(header)) - for i := 2; i < lines.Len(); i++ { - table.AppendChild(table, b.parseRow(lines.At(i), alignments, false, reader)) + for i := 1; i < lines.Len(); i++ { + alignments := b.parseDelimiter(lines.At(i), reader) + if alignments == nil { + continue + } + header := b.parseRow(lines.At(i-1), alignments, true, reader) + if header == nil || len(alignments) != header.ChildCount() { + return + } + table := ast.NewTable() + table.Alignments = alignments + table.AppendChild(table, ast.NewTableHeader(header)) + for j := i + 1; j < lines.Len(); j++ { + table.AppendChild(table, b.parseRow(lines.At(j), alignments, false, reader)) + } + node.Lines().SetSliced(0, i-1) + node.Parent().InsertAfter(node.Parent(), node, table) + if node.Lines().Len() == 0 { + node.Parent().RemoveChild(node.Parent(), node) + } else { + last := node.Lines().At(i - 2) + last.Stop = last.Stop - 1 // trim last newline(\n) + node.Lines().Set(i-2, last) + } } - node.Parent().InsertBefore(node.Parent(), node, table) - node.Parent().RemoveChild(node.Parent(), node) } func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments []ast.Alignment, isHeader bool, reader text.Reader) *ast.TableRow { @@ -100,7 +215,7 @@ func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments [] func (b *tableParagraphTransformer) parseDelimiter(segment text.Segment, reader text.Reader) []ast.Alignment { line := segment.Value(reader.Source()) - if !tableDelimRegexp.Match(line) { + if !isTableDelim(line) { return nil } cols := bytes.Split(line, []byte{'|'}) @@ -131,16 +246,16 @@ func (b *tableParagraphTransformer) parseDelimiter(segment text.Segment, reader // TableHTMLRenderer is a renderer.NodeRenderer implementation that // renders Table nodes. type TableHTMLRenderer struct { - html.Config + TableConfig } // NewTableHTMLRenderer returns a new TableHTMLRenderer. -func NewTableHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { +func NewTableHTMLRenderer(opts ...TableOption) renderer.NodeRenderer { r := &TableHTMLRenderer{ - Config: html.NewConfig(), + TableConfig: NewTableConfig(), } for _, opt := range opts { - opt.SetHTMLOption(&r.Config) + opt.SetTableOption(&r.TableConfig) } return r } @@ -281,14 +396,33 @@ func (r *TableHTMLRenderer) renderTableCell(w util.BufWriter, source []byte, nod tag = "th" } if entering { - align := "" + fmt.Fprintf(w, "<%s", tag) if n.Alignment != ast.AlignNone { - if _, ok := n.AttributeString("align"); !ok { // Skip align render if overridden - // TODO: "align" is deprecated. style="text-align:%s" instead? - align = fmt.Sprintf(` align="%s"`, n.Alignment.String()) + amethod := r.TableConfig.TableCellAlignMethod + if amethod == TableCellAlignDefault { + if r.Config.XHTML { + amethod = TableCellAlignAttribute + } else { + amethod = TableCellAlignStyle + } + } + switch amethod { + case TableCellAlignAttribute: + if _, ok := n.AttributeString("align"); !ok { // Skip align render if overridden + fmt.Fprintf(w, ` align="%s"`, n.Alignment.String()) + } + case TableCellAlignStyle: + v, ok := n.AttributeString("style") + var cob util.CopyOnWriteBuffer + if ok { + cob = util.NewCopyOnWriteBuffer(v.([]byte)) + cob.AppendByte(';') + } + style := fmt.Sprintf("text-align:%s", n.Alignment.String()) + cob.Append(util.StringToReadOnlyBytes(style)) + n.SetAttributeString("style", cob.Bytes()) } } - fmt.Fprintf(w, "<%s", tag) if n.Attributes() != nil { if tag == "td" { html.RenderAttributes(w, n, TableTdCellAttributeFilter) // @@ -296,7 +430,7 @@ func (r *TableHTMLRenderer) renderTableCell(w util.BufWriter, source []byte, nod html.RenderAttributes(w, n, TableThCellAttributeFilter) // } } - fmt.Fprintf(w, "%s>", align) + _ = w.WriteByte('>') } else { fmt.Fprintf(w, "\n", tag) } @@ -304,16 +438,26 @@ func (r *TableHTMLRenderer) renderTableCell(w util.BufWriter, source []byte, nod } type table struct { + options []TableOption } // Table is an extension that allow you to use GFM tables . -var Table = &table{} +var Table = &table{ + options: []TableOption{}, +} + +// NewTable returns a new extension with given options. +func NewTable(opts ...TableOption) goldmark.Extender { + return &table{ + options: opts, + } +} func (e *table) Extend(m goldmark.Markdown) { m.Parser().AddOptions(parser.WithParagraphTransformers( util.Prioritized(NewTableParagraphTransformer(), 200), )) m.Renderer().AddOptions(renderer.WithNodeRenderers( - util.Prioritized(NewTableHTMLRenderer(), 500), + util.Prioritized(NewTableHTMLRenderer(e.options...), 500), )) } diff --git a/vendor/github.com/yuin/goldmark/extension/typographer.go b/vendor/github.com/yuin/goldmark/extension/typographer.go index 8e9523100..2c3473094 100644 --- a/vendor/github.com/yuin/goldmark/extension/typographer.go +++ b/vendor/github.com/yuin/goldmark/extension/typographer.go @@ -223,7 +223,7 @@ func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser if len(line) > 4 { after = util.ToRune(line, 4) } - if len(line) == 3 || unicode.IsSpace(after) || unicode.IsPunct(after) { + if len(line) == 3 || util.IsSpaceRune(after) || util.IsPunctRune(after) { node := gast.NewString(s.Substitutions[Apostrophe]) node.SetCode(true) block.Advance(1) diff --git a/vendor/github.com/yuin/goldmark/parser/delimiter.go b/vendor/github.com/yuin/goldmark/parser/delimiter.go index 612d7b737..8259f6224 100644 --- a/vendor/github.com/yuin/goldmark/parser/delimiter.go +++ b/vendor/github.com/yuin/goldmark/parser/delimiter.go @@ -3,7 +3,6 @@ package parser import ( "fmt" "strings" - "unicode" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/text" @@ -128,10 +127,10 @@ func ScanDelimiter(line []byte, before rune, min int, processor DelimiterProcess } canOpen, canClose := false, false - beforeIsPunctuation := unicode.IsPunct(before) - beforeIsWhitespace := unicode.IsSpace(before) - afterIsPunctuation := unicode.IsPunct(after) - afterIsWhitespace := unicode.IsSpace(after) + beforeIsPunctuation := util.IsPunctRune(before) + beforeIsWhitespace := util.IsSpaceRune(before) + afterIsPunctuation := util.IsPunctRune(after) + afterIsWhitespace := util.IsSpaceRune(after) isLeft := !afterIsWhitespace && (!afterIsPunctuation || beforeIsWhitespace || beforeIsPunctuation) diff --git a/vendor/github.com/yuin/goldmark/renderer/html/html.go b/vendor/github.com/yuin/goldmark/renderer/html/html.go index 537a256fe..e545a736b 100644 --- a/vendor/github.com/yuin/goldmark/renderer/html/html.go +++ b/vendor/github.com/yuin/goldmark/renderer/html/html.go @@ -564,7 +564,7 @@ func (r *Renderer) renderImage(w util.BufWriter, source []byte, node ast.Node, e _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true))) } _, _ = w.WriteString(`" alt="`) - _, _ = w.Write(n.Text(source)) + _, _ = w.Write(util.EscapeHTML(n.Text(source))) _ = w.WriteByte('"') if n.Title != nil { _, _ = w.WriteString(` title="`) diff --git a/vendor/github.com/yuin/goldmark/util/util.go b/vendor/github.com/yuin/goldmark/util/util.go index ef113c4ae..fc1438dc1 100644 --- a/vendor/github.com/yuin/goldmark/util/util.go +++ b/vendor/github.com/yuin/goldmark/util/util.go @@ -8,6 +8,7 @@ import ( "regexp" "sort" "strconv" + "unicode" "unicode/utf8" ) @@ -27,6 +28,7 @@ func NewCopyOnWriteBuffer(buffer []byte) CopyOnWriteBuffer { } // Write writes given bytes to the buffer. +// Write allocate new buffer and clears it at the first time. func (b *CopyOnWriteBuffer) Write(value []byte) { if !b.copied { b.buffer = make([]byte, 0, len(b.buffer)+20) @@ -35,7 +37,20 @@ func (b *CopyOnWriteBuffer) Write(value []byte) { b.buffer = append(b.buffer, value...) } +// Append appends given bytes to the buffer. +// Append copy buffer at the first time. +func (b *CopyOnWriteBuffer) Append(value []byte) { + if !b.copied { + tmp := make([]byte, len(b.buffer), len(b.buffer)+20) + copy(tmp, b.buffer) + b.buffer = tmp + b.copied = true + } + b.buffer = append(b.buffer, value...) +} + // WriteByte writes the given byte to the buffer. +// WriteByte allocate new buffer and clears it at the first time. func (b *CopyOnWriteBuffer) WriteByte(c byte) { if !b.copied { b.buffer = make([]byte, 0, len(b.buffer)+20) @@ -44,6 +59,18 @@ func (b *CopyOnWriteBuffer) WriteByte(c byte) { b.buffer = append(b.buffer, c) } +// AppendByte appends given bytes to the buffer. +// AppendByte copy buffer at the first time. +func (b *CopyOnWriteBuffer) AppendByte(c byte) { + if !b.copied { + tmp := make([]byte, len(b.buffer), len(b.buffer)+20) + copy(tmp, b.buffer) + b.buffer = tmp + b.copied = true + } + b.buffer = append(b.buffer, c) +} + // Bytes returns bytes of this buffer. func (b *CopyOnWriteBuffer) Bytes() []byte { return b.buffer @@ -777,11 +804,21 @@ func IsPunct(c byte) bool { return punctTable[c] == 1 } +// IsPunct returns true if the given rune is a punctuation, otherwise false. +func IsPunctRune(r rune) bool { + return int32(r) <= 256 && IsPunct(byte(r)) || unicode.IsPunct(r) +} + // IsSpace returns true if the given character is a space, otherwise false. func IsSpace(c byte) bool { return spaceTable[c] == 1 } +// IsSpace returns true if the given rune is a space, otherwise false. +func IsSpaceRune(r rune) bool { + return int32(r) <= 256 && IsSpace(byte(r)) || unicode.IsSpace(r) +} + // IsNumeric returns true if the given character is a numeric, otherwise false. func IsNumeric(c byte) bool { return c >= '0' && c <= '9' diff --git a/vendor/modules.txt b/vendor/modules.txt index c9e9e241e..ec09b5fc6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -715,7 +715,7 @@ github.com/xi2/xz # github.com/yohcop/openid-go v1.0.0 ## explicit github.com/yohcop/openid-go -# github.com/yuin/goldmark v1.1.32 +# github.com/yuin/goldmark v1.2.1 ## explicit github.com/yuin/goldmark github.com/yuin/goldmark/ast