Skip to content

Commit

Permalink
fix(format): bypass escaping of links, add strikethrough escape (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
princjef authored Nov 30, 2020
1 parent c0b3572 commit 4de33c0
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 25 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import "github.com/princjef/gomarkdoc"
```

Package gomarkdoc formats documentation for one or more packages as markdown for usage outside of the main godoc\.org site\. It supports custom templates for tweaking representation of documentation at fine\-grained levels\, exporting both exported and unexported symbols\, and custom formatters for different backends\.
Package gomarkdoc formats documentation for one or more packages as markdown for usage outside of the main https://pkg.go.dev site\. It supports custom templates for tweaking representation of documentation at fine\-grained levels\, exporting both exported and unexported symbols\, and custom formatters for different backends\.

### Command Line Usage

Expand Down
2 changes: 1 addition & 1 deletion cmd/gomarkdoc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import "github.com/princjef/gomarkdoc/cmd/gomarkdoc"

Package gomarkdoc provides a command line interface for writing golang documentation in markdown format\.

See github\.com/princjef/gomarkdoc for full documentation of this tool\.
See https://github.com/princjef/gomarkdoc for full documentation of this tool\.

## Index

Expand Down
3 changes: 2 additions & 1 deletion cmd/gomarkdoc/doc.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Package gomarkdoc provides a command line interface for writing golang
// documentation in markdown format.
//
// See github.com/princjef/gomarkdoc for full documentation of this tool.
// See https://github.com/princjef/gomarkdoc for full documentation of this
// tool.
package main
8 changes: 4 additions & 4 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Package gomarkdoc formats documentation for one or more packages as markdown
// for usage outside of the main godoc.org site. It supports custom templates
// for tweaking representation of documentation at fine-grained levels,
// exporting both exported and unexported symbols, and custom formatters for
// different backends.
// for usage outside of the main https://pkg.go.dev site. It supports custom
// templates for tweaking representation of documentation at fine-grained
// levels, exporting both exported and unexported symbols, and custom formatters
// for different backends.
//
// Command Line Usage
//
Expand Down
6 changes: 3 additions & 3 deletions format/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Each of the formats in this package contains the same set of formatting function

## type [AzureDevOpsMarkdown](<https://github.com/princjef/gomarkdoc/blob/master/format/devops.go#L17>)

AzureDevOpsMarkdown provides a Format which is compatible with Azure DevOps's syntax and semantics\. See the Azure DevOps documentation for more details about their markdown format: https://docs\.microsoft\.com/en\-us/azure/devops/project/wiki/markdown\-guidance?view=azure\-devops
AzureDevOpsMarkdown provides a Format which is compatible with Azure DevOps's syntax and semantics\. See the Azure DevOps documentation for more details about their markdown format: https://docs.microsoft.com/en-us/azure/devops/project/wiki/markdown-guidance?view=azure-devops

```go
type AzureDevOpsMarkdown struct{}
Expand Down Expand Up @@ -157,7 +157,7 @@ ListEntry generates an unordered list entry with the provided text at the provid
func (f *AzureDevOpsMarkdown) LocalHref(headerText string) (string, error)
```

LocalHref generates an href for navigating to a header with the given headerText located within the same document as the href itself\. Link generation follows the guidelines here: https://docs\.microsoft\.com/en\-us/azure/devops/project/wiki/markdown\-guidance?view=azure\-devops\#anchor\-links
LocalHref generates an href for navigating to a header with the given headerText located within the same document as the href itself\. Link generation follows the guidelines here: https://docs.microsoft.com/en-us/azure/devops/project/wiki/markdown-guidance?view=azure-devops#anchor-links

### func \(\*AzureDevOpsMarkdown\) [Paragraph](<https://github.com/princjef/gomarkdoc/blob/master/format/devops.go#L134>)

Expand Down Expand Up @@ -242,7 +242,7 @@ type Format interface {

## type [GitHubFlavoredMarkdown](<https://github.com/princjef/gomarkdoc/blob/master/format/github.go#L16>)

GitHubFlavoredMarkdown provides a Format which is compatible with GitHub Flavored Markdown's syntax and semantics\. See GitHub's documentation for more details about their markdown format: https://guides\.github\.com/features/mastering\-markdown/
GitHubFlavoredMarkdown provides a Format which is compatible with GitHub Flavored Markdown's syntax and semantics\. See GitHub's documentation for more details about their markdown format: https://guides.github.com/features/mastering-markdown/

```go
type GitHubFlavoredMarkdown struct{}
Expand Down
39 changes: 37 additions & 2 deletions format/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"github.com/russross/blackfriday/v2"
"mvdan.cc/xurls/v2"
)

// bold converts the provided text to bold
Expand Down Expand Up @@ -120,10 +121,44 @@ func paragraph(text string) string {
return fmt.Sprintf("%s\n\n", escape(text))
}

var specialCharacterRegex = regexp.MustCompile("([\\\\`*_{}\\[\\]()<>#+-.!])")
var (
specialCharacterRegex = regexp.MustCompile("([\\\\`*_{}\\[\\]()<>#+-.!~])")
urlRegex = xurls.Strict() // Require a scheme in URLs
)

func escape(text string) string {
return specialCharacterRegex.ReplaceAllString(text, "\\$1")
b := []byte(text)

var (
cursor int
builder strings.Builder
)

for _, urlLoc := range urlRegex.FindAllIndex(b, -1) {
// Walk through each found URL, escaping the text before the URL and
// leaving the text in the URL unchanged.
if urlLoc[0] > cursor {
// Escape the previous section if its length is nonzero
builder.Write(escapeRaw(b[cursor:urlLoc[0]]))
}

// Add the unescaped URL to the end of it
builder.Write(b[urlLoc[0]:urlLoc[1]])

// Move the cursor forward for the next iteration
cursor = urlLoc[1]
}

// Escape the end of the string after the last URL if there's anything left
if len(b) > cursor {
builder.Write(escapeRaw(b[cursor:]))
}

return builder.String()
}

func escapeRaw(segment []byte) []byte {
return specialCharacterRegex.ReplaceAll(segment, []byte("\\$1"))
}

// plainText converts a markdown string to the plain text that appears in the
Expand Down
99 changes: 86 additions & 13 deletions format/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,95 @@ import (
)

func TestPlainText(t *testing.T) {
tests := map[string]string{
"plain text": "plain text",
"[linked](https://foo.bar)": "linked",
"[linked 2](<https://foo.bar>)": "linked 2",
"type [foo](<https://foo.bar>)": "type foo",
"**bold** text": "bold text",
"*italicized* text": "italicized text",
"~~strikethrough~~ text": "strikethrough text",
"paragraph 1\n\nparagraph 2": "paragraph 1 paragraph 2",
"# header\n\nparagraph": "header paragraph",
tests := []struct {
in, out string
}{
{
in: "plain text",
out: "plain text",
},
{
in: "[linked](https://foo.bar)",
out: "linked",
},
{
in: "[linked 2](<https://foo.bar>)",
out: "linked 2",
},
{
in: "type [foo](<https://foo.bar>)",
out: "type foo",
},
{
in: "**bold** text",
out: "bold text",
},
{
in: "*italicized* text",
out: "italicized text",
},
{
in: "~~strikethrough~~ text",
out: "strikethrough text",
},
{
in: "paragraph 1\n\nparagraph 2",
out: "paragraph 1 paragraph 2",
},
{
in: "# header\n\nparagraph",
out: "header paragraph",
},
}

for in, out := range tests {
t.Run(in, func(t *testing.T) {
for _, test := range tests {
t.Run(test.in, func(t *testing.T) {
is := is.New(t)
is.Equal(plainText(in), out) // Wrong output for plainText()
is.Equal(plainText(test.in), test.out) // Wrong output for plainText()
})
}
}

func TestEscape(t *testing.T) {
tests := []struct {
in, out string
}{
{
in: "plain text",
out: `plain text`,
},
{
in: "**bold** text",
out: `\*\*bold\*\* text`,
},
{
in: "*italicized* text",
out: `\*italicized\* text`,
},
{
in: "~~strikethrough~~ text",
out: `\~\~strikethrough\~\~ text`,
},
{
in: "# header",
out: `\# header`,
},
{
in: "markdown [link](https://foo.bar)",
out: `markdown \[link\]\(https://foo.bar\)`,
},
{
in: "# header then complex URL: http://abc.def/sdfklj/sdf?key=value&special=%323%20sd " +
"with http://simple.url and **bold** after",
out: `\# header then complex URL: http://abc.def/sdfklj/sdf?key=value&special=%323%20sd ` +
`with http://simple.url and \*\*bold\*\* after`,
},
}

for _, test := range tests {
t.Run(test.in, func(t *testing.T) {
is := is.New(t)
is.Equal(escape(test.in), test.out) // Wrong output for escape()
})
}
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/matryer/is v1.3.0
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mvdan/xurls v1.1.0 // indirect
github.com/onsi/ginkgo v1.14.2 // indirect
github.com/onsi/gomega v1.10.3 // indirect
github.com/pelletier/go-toml v1.6.0 // indirect
Expand All @@ -21,4 +22,6 @@ require (
github.com/stretchr/testify v1.4.0 // indirect
github.com/x-cray/logrus-prefixed-formatter v0.5.2
gopkg.in/src-d/go-git.v4 v4.13.1
mvdan.cc/xurls v1.1.0
mvdan.cc/xurls/v2 v2.2.0
)
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mvdan/xurls v1.1.0 h1:OpuDelGQ1R1ueQ6sSryzi6P+1RtBpfQHM8fJwlE45ww=
github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
Expand Down Expand Up @@ -163,6 +165,7 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
Expand Down Expand Up @@ -306,8 +309,10 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v2 v2.0.7 h1:beaAg8eacCdMQS9Y7obFEtkY7gQl0uZ6Zayb3ry41VY=
gopkg.in/cheggaaa/pb.v2 v2.0.7/go.mod h1:0CiZ1p8pvtxBlQpLXkHuUTpdJ1shm3OqCF1QugkjHL4=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fatih/color.v1 v1.7.0 h1:bYGjb+HezBM6j/QmgBfgm1adxHpzzrss6bj4r9ROppk=
gopkg.in/fatih/color.v1 v1.7.0/go.mod h1:P7yosIhqIl/sX8J8UypY5M+dDpD2KmyfP5IRs5v/fo0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
Expand Down Expand Up @@ -337,3 +342,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
mvdan.cc/xurls v1.1.0 h1:kj0j2lonKseISJCiq1Tfk+iTv65dDGCl0rTbanXJGGc=
mvdan.cc/xurls v1.1.0/go.mod h1:TNWuhvo+IqbUCmtUIb/3LJSQdrzel8loVpgFm0HikbI=
mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A=
mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8=

0 comments on commit 4de33c0

Please sign in to comment.