Skip to content

Commit

Permalink
feat(format): expose formatting utils in new formatcore package (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
bastantoine committed Jun 16, 2021
1 parent da13949 commit e4b16b3
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 110 deletions.
84 changes: 42 additions & 42 deletions format/README.md

Large diffs are not rendered by default.

23 changes: 12 additions & 11 deletions format/devops.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"regexp"
"strings"

"github.com/princjef/gomarkdoc/format/formatcore"
"github.com/princjef/gomarkdoc/lang"
)

Expand All @@ -18,25 +19,25 @@ type AzureDevOpsMarkdown struct{}

// Bold converts the provided text to bold
func (f *AzureDevOpsMarkdown) Bold(text string) (string, error) {
return bold(text), nil
return formatcore.Bold(text), nil
}

// CodeBlock wraps the provided code as a code block and tags it with the
// provided language (or no language if the empty string is provided).
func (f *AzureDevOpsMarkdown) CodeBlock(language, code string) (string, error) {
return gfmCodeBlock(language, code), nil
return formatcore.GFMCodeBlock(language, code), nil
}

// Header converts the provided text into a header of the provided level. The
// level is expected to be at least 1.
func (f *AzureDevOpsMarkdown) Header(level int, text string) (string, error) {
return header(level, escape(text))
return formatcore.Header(level, formatcore.Escape(text))
}

// RawHeader converts the provided text into a header of the provided level
// without escaping the header text. The level is expected to be at least 1.
func (f *AzureDevOpsMarkdown) RawHeader(level int, text string) (string, error) {
return header(level, text)
return formatcore.Header(level, text)
}

var devOpsWhitespaceRegex = regexp.MustCompile(`\s`)
Expand Down Expand Up @@ -96,20 +97,20 @@ func (f *AzureDevOpsMarkdown) CodeHref(loc lang.Location) (string, error) {

// Link generates a link with the given text and href values.
func (f *AzureDevOpsMarkdown) Link(text, href string) (string, error) {
return link(text, href), nil
return formatcore.Link(text, href), nil
}

// ListEntry generates an unordered list entry with the provided text at the
// provided zero-indexed depth. A depth of 0 is considered the topmost level of
// list.
func (f *AzureDevOpsMarkdown) ListEntry(depth int, text string) (string, error) {
return listEntry(depth, text), nil
return formatcore.ListEntry(depth, text), nil
}

// Accordion generates a collapsible content. The accordion's visible title
// while collapsed is the provided title and the expanded content is the body.
func (f *AzureDevOpsMarkdown) Accordion(title, body string) (string, error) {
return gfmAccordion(title, body), nil
return formatcore.GFMAccordion(title, body), nil
}

// AccordionHeader generates the header visible when an accordion is collapsed.
Expand All @@ -120,22 +121,22 @@ func (f *AzureDevOpsMarkdown) Accordion(title, body string) (string, error) {
//
// accordion := format.AccordionHeader("Accordion Title") + "Accordion Body" + format.AccordionTerminator()
func (f *AzureDevOpsMarkdown) AccordionHeader(title string) (string, error) {
return gfmAccordionHeader(title), nil
return formatcore.GFMAccordionHeader(title), nil
}

// AccordionTerminator generates the code necessary to terminate an accordion
// after the body. It is expected to be used in conjunction with
// AccordionHeader(). See AccordionHeader for a full description.
func (f *AzureDevOpsMarkdown) AccordionTerminator() (string, error) {
return gfmAccordionTerminator(), nil
return formatcore.GFMAccordionTerminator(), nil
}

// Paragraph formats a paragraph with the provided text as the contents.
func (f *AzureDevOpsMarkdown) Paragraph(text string) (string, error) {
return paragraph(text), nil
return formatcore.Paragraph(text), nil
}

// Escape escapes special markdown characters from the provided text.
func (f *AzureDevOpsMarkdown) Escape(text string) string {
return escape(text)
return formatcore.Escape(text)
}
129 changes: 129 additions & 0 deletions format/formatcore/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->

# formatcore

```go
import "github.com/princjef/gomarkdoc/format/formatcore"
```

## Index

- [func Bold(text string) string](<#func-bold>)
- [func CodeBlock(code string) string](<#func-codeblock>)
- [func Escape(text string) string](<#func-escape>)
- [func GFMAccordion(title, body string) string](<#func-gfmaccordion>)
- [func GFMAccordionHeader(title string) string](<#func-gfmaccordionheader>)
- [func GFMAccordionTerminator() string](<#func-gfmaccordionterminator>)
- [func GFMCodeBlock(language, code string) string](<#func-gfmcodeblock>)
- [func Header(level int, text string) (string, error)](<#func-header>)
- [func Link(text, href string) string](<#func-link>)
- [func ListEntry(depth int, text string) string](<#func-listentry>)
- [func Paragraph(text string) string](<#func-paragraph>)
- [func PlainText(text string) string](<#func-plaintext>)


## func [Bold](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L14>)

```go
func Bold(text string) string
```

Bold converts the provided text to bold

## func [CodeBlock](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L24>)

```go
func CodeBlock(code string) string
```

CodeBlock wraps the provided code as a code block\. Language syntax highlighting is not supported\.

## func [Escape](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L132>)

```go
func Escape(text string) string
```

Escape escapes the special characters in the provided text\, but leaves URLs found intact\. Note that the URLs included must begin with a scheme to skip the escaping\.

## func [GFMAccordion](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L95>)

```go
func GFMAccordion(title, body string) string
```

GFMAccordion generates a collapsible content\. The accordion's visible title while collapsed is the provided title and the expanded content is the body\.

## func [GFMAccordionHeader](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L108>)

```go
func GFMAccordionHeader(title string) string
```

GFMAccordionHeader generates the header visible when an accordion is collapsed\.

The GFMAccordionHeader is expected to be used in conjunction with GFMAccordionTerminator\(\) when the demands of the body's rendering requires it to be generated independently\. The result looks conceptually like the following:

```
accordion := GFMAccordionHeader("Accordion Title") + "Accordion Body" + GFMAccordionTerminator()
```

## func [GFMAccordionTerminator](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L115>)

```go
func GFMAccordionTerminator() string
```

GFMAccordionTerminator generates the code necessary to terminate an accordion after the body\. It is expected to be used in conjunction with GFMAccordionHeader\(\)\. See GFMAccordionHeader for a full description\.

## func [GFMCodeBlock](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L39>)

```go
func GFMCodeBlock(language, code string) string
```

GFMCodeBlock wraps the provided code as a code block and tags it with the provided language \(or no language if the empty string is provided\)\, using the triple backtick format from GitHub Flavored Markdown\.

## func [Header](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L45>)

```go
func Header(level int, text string) (string, error)
```

Header converts the provided text into a header of the provided level\. The level is expected to be at least 1\.

## func [Link](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L68>)

```go
func Link(text, href string) string
```

Link generates a link with the given text and href values\.

## func [ListEntry](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L83>)

```go
func ListEntry(depth int, text string) string
```

ListEntry generates an unordered list entry with the provided text at the provided zero\-indexed depth\. A depth of 0 is considered the topmost level of list\.

## func [Paragraph](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L120>)

```go
func Paragraph(text string) string
```

Paragraph formats a paragraph with the provided text as the contents\.

## func [PlainText](<https://github.com/princjef/gomarkdoc/blob/master/format/formatcore/base.go#L169>)

```go
func PlainText(text string) string
```

PlainText converts a markdown string to the plain text that appears in the rendered output\.



Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)
65 changes: 34 additions & 31 deletions format/base.go → format/formatcore/base.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package format
package formatcore

import (
"errors"
Expand All @@ -10,18 +10,18 @@ import (
"mvdan.cc/xurls/v2"
)

// bold converts the provided text to bold
func bold(text string) string {
// Bold converts the provided text to bold
func Bold(text string) string {
if text == "" {
return ""
}

return fmt.Sprintf("**%s**", escape(text))
return fmt.Sprintf("**%s**", Escape(text))
}

// codeBlock wraps the provided code as a code block. Language syntax
// CodeBlock wraps the provided code as a code block. Language syntax
// highlighting is not supported.
func codeBlock(code string) string {
func CodeBlock(code string) string {
var builder strings.Builder

lines := strings.Split(code, "\n")
Expand All @@ -33,16 +33,16 @@ func codeBlock(code string) string {
return builder.String()
}

// gfmCodeBlock wraps the provided code as a code block and tags it with the
// GFMCodeBlock wraps the provided code as a code block and tags it with the
// provided language (or no language if the empty string is provided), using
// the triple backtick format from GitHub Flavored Markdown.
func gfmCodeBlock(language, code string) string {
func GFMCodeBlock(language, code string) string {
return fmt.Sprintf("```%s\n%s\n```\n\n", language, strings.TrimSpace(code))
}

// header converts the provided text into a header of the provided level. The
// Header converts the provided text into a header of the provided level. The
// level is expected to be at least 1.
func header(level int, text string) (string, error) {
func Header(level int, text string) (string, error) {
if level < 1 {
return "", errors.New("format: header level cannot be less than 1")
}
Expand All @@ -64,8 +64,8 @@ func header(level int, text string) (string, error) {
}
}

// link generates a link with the given text and href values.
func link(text, href string) string {
// Link generates a link with the given text and href values.
func Link(text, href string) string {
if text == "" {
return ""
}
Expand All @@ -77,10 +77,10 @@ func link(text, href string) string {
return fmt.Sprintf("[%s](<%s>)", text, href)
}

// listEntry generates an unordered list entry with the provided text at the
// ListEntry generates an unordered list entry with the provided text at the
// provided zero-indexed depth. A depth of 0 is considered the topmost level of
// list.
func listEntry(depth int, text string) string {
func ListEntry(depth int, text string) string {
// TODO: this is a weird special case
if text == "" {
return ""
Expand All @@ -90,43 +90,46 @@ func listEntry(depth int, text string) string {
return fmt.Sprintf("%s- %s\n", prefix, text)
}

// gfmAccordion generates a collapsible content. The accordion's visible title
// GFMAccordion generates a collapsible content. The accordion's visible title
// while collapsed is the provided title and the expanded content is the body.
func gfmAccordion(title, body string) string {
return fmt.Sprintf("<details><summary>%s</summary>\n<p>\n\n%s</p>\n</details>\n\n", title, escape(body))
func GFMAccordion(title, body string) string {
return fmt.Sprintf("<details><summary>%s</summary>\n<p>\n\n%s</p>\n</details>\n\n", title, Escape(body))
}

// gfmAccordionHeader generates the header visible when an accordion is
// GFMAccordionHeader generates the header visible when an accordion is
// collapsed.
//
// The gfmAccordionHeader is expected to be used in conjunction with
// gfmAccordionTerminator() when the demands of the body's rendering requires
// The GFMAccordionHeader is expected to be used in conjunction with
// GFMAccordionTerminator() when the demands of the body's rendering requires
// it to be generated independently. The result looks conceptually like the
// following:
//
// accordion := gfmAccordionHeader("Accordion Title") + "Accordion Body" + gfmAccordionTerminator()
func gfmAccordionHeader(title string) string {
// accordion := GFMAccordionHeader("Accordion Title") + "Accordion Body" + GFMAccordionTerminator()
func GFMAccordionHeader(title string) string {
return fmt.Sprintf("<details><summary>%s</summary>\n<p>\n\n", title)
}

// gfmAccordionTerminator generates the code necessary to terminate an
// GFMAccordionTerminator generates the code necessary to terminate an
// accordion after the body. It is expected to be used in conjunction with
// gfmAccordionHeader(). See gfmAccordionHeader for a full description.
func gfmAccordionTerminator() string {
// GFMAccordionHeader(). See GFMAccordionHeader for a full description.
func GFMAccordionTerminator() string {
return "</p>\n</details>\n\n"
}

// paragraph formats a paragraph with the provided text as the contents.
func paragraph(text string) string {
return fmt.Sprintf("%s\n\n", escape(text))
// Paragraph formats a paragraph with the provided text as the contents.
func Paragraph(text string) string {
return fmt.Sprintf("%s\n\n", Escape(text))
}

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

func escape(text string) string {
// Escape escapes the special characters in the provided text, but leaves URLs
// found intact. Note that the URLs included must begin with a scheme to skip
// the escaping.
func Escape(text string) string {
b := []byte(text)

var (
Expand Down Expand Up @@ -161,9 +164,9 @@ func escapeRaw(segment []byte) []byte {
return specialCharacterRegex.ReplaceAll(segment, []byte("\\$1"))
}

// plainText converts a markdown string to the plain text that appears in the
// PlainText converts a markdown string to the plain text that appears in the
// rendered output.
func plainText(text string) string {
func PlainText(text string) string {
md := blackfriday.New(blackfriday.WithExtensions(blackfriday.CommonExtensions))
node := md.Parse([]byte(text))

Expand Down
6 changes: 3 additions & 3 deletions format/base_test.go → format/formatcore/base_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package format
package formatcore

import (
"testing"
Expand Down Expand Up @@ -51,7 +51,7 @@ func TestPlainText(t *testing.T) {
for _, test := range tests {
t.Run(test.in, func(t *testing.T) {
is := is.New(t)
is.Equal(plainText(test.in), test.out) // Wrong output for plainText()
is.Equal(PlainText(test.in), test.out) // Wrong output for plainText()
})
}
}
Expand Down Expand Up @@ -95,7 +95,7 @@ func TestEscape(t *testing.T) {
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()
is.Equal(Escape(test.in), test.out) // Wrong output for escape()
})
}
}
Loading

0 comments on commit e4b16b3

Please sign in to comment.