Skip to content

Commit

Permalink
fix(format): bypass escaping of links, add strikethrough escape
Browse files Browse the repository at this point in the history
  • Loading branch information
princjef committed Nov 30, 2020
1 parent c0b3572 commit a0cc4e4
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 26 deletions.
6 changes: 3 additions & 3 deletions 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 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\.

### Command Line Usage

Expand Down Expand Up @@ -66,7 +66,7 @@ If you want to redirect output for each processed package to a file\, you can al
gomarkdoc --output '{{.Dir}}/README.md' ./...
```

You can see all of the data available to the output template in the PackageSpec struct in the github\.com/princjef/gomarkdoc/cmd/gomarkdoc package\.
You can see all of the data available to the output template in the PackageSpec struct in the github.com/princjef/gomarkdoc/cmd/gomarkdoc package\.

### Template Overrides

Expand Down Expand Up @@ -130,7 +130,7 @@ If you're experiencing difficulty with gomarkdoc or just want to get more inform
gomarkdoc -vv -o README.md .
```

Some features of gomarkdoc rely on being able to detect information from the git repository containing the project\. Since individual local git repositories may be configured differently from person to person\, you may want to manually specify the information for the repository to remove any inconsistencies\. This can be achieved with the \-\-repository\.url\, \-\-repository\.default\-branch and \-\-repository\.path options\. For example\, this repository would be configured with:
Some features of gomarkdoc rely on being able to detect information from the git repository containing the project\. Since individual local git repositories may be configured differently from person to person\, you may want to manually specify the information for the repository to remove any inconsistencies\. This can be achieved with the \-\-repository\.url\, \-\-repository.default\-branch and \-\-repository.path options\. For example\, this repository would be configured with:

```
gomarkdoc --repository.url "https://github.com/princjef/gomarkdoc" --repository.defaultBranch master --repository.path / -o README.md .
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 github.com/princjef/gomarkdoc for full documentation of this tool\.

## Index

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.Relaxed()
)

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=
8 changes: 4 additions & 4 deletions lang/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ type Func struct {
func NewFunc(cfg *Config, doc *doc.Func, examples []*doc.Example) *Func
```

NewFunc creates a new Func from the corresponding documentation construct from the standard library\, the related token\.FileSet for the package and the list of examples for the package\.
NewFunc creates a new Func from the corresponding documentation construct from the standard library\, the related token.FileSet for the package and the list of examples for the package\.

### func \(\*Func\) [Doc](<https://github.com/princjef/gomarkdoc/blob/master/lang/func.go#L65>)

Expand Down Expand Up @@ -459,7 +459,7 @@ type Location struct {
func NewLocation(cfg *Config, node ast.Node) Location
```

NewLocation returns a location for the provided Config and ast\.Node combination\. This is typically not called directly\, but is made available via the Location\(\) methods of various lang constructs\.
NewLocation returns a location for the provided Config and ast.Node combination\. This is typically not called directly\, but is made available via the Location\(\) methods of various lang constructs\.

## type [Package](<https://github.com/princjef/gomarkdoc/blob/master/lang/package.go#L23-L27>)

Expand Down Expand Up @@ -656,7 +656,7 @@ type Type struct {
func NewType(cfg *Config, doc *doc.Type, examples []*doc.Example) *Type
```

NewType creates a Type from the raw documentation representation of the type\, the token\.FileSet for the package's files and the full list of examples from the containing package\.
NewType creates a Type from the raw documentation representation of the type\, the token.FileSet for the package's files and the full list of examples from the containing package\.

### func \(\*Type\) [Consts](<https://github.com/princjef/gomarkdoc/blob/master/lang/type.go#L120>)

Expand Down Expand Up @@ -770,7 +770,7 @@ type Value struct {
func NewValue(cfg *Config, doc *doc.Value) *Value
```

NewValue creates a new Value from the raw const or var documentation and the token\.FileSet of files for the containing package\.
NewValue creates a new Value from the raw const or var documentation and the token.FileSet of files for the containing package\.

### func \(\*Value\) [Decl](<https://github.com/princjef/gomarkdoc/blob/master/lang/value.go#L45>)

Expand Down

0 comments on commit a0cc4e4

Please sign in to comment.