Skip to content

Commit

Permalink
Introduce mermaid flowchart syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
nao1215 committed May 6, 2024
1 parent cbbac3b commit 1b28ab0
Show file tree
Hide file tree
Showing 18 changed files with 785 additions and 57 deletions.
82 changes: 81 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# What is markdown package
The Package markdown is a simple markdown builder in golang. The markdown package assembles Markdown using method chaining, not uses a template engine like [html/template](https://pkg.go.dev/html/template). The syntax of Markdown follows **GitHub Markdown**.

The markdown package was initially developed to save test results in [nao1215/spectest](https://github.com/nao1215/spectest). Therefore, the markdown package implements the features required by spectest. For example, the markdown package supports **mermaid sequence diagrams (entity relationship diagram, sequence diagram, pie chart)**, which was a necessary feature in spectest.
The markdown package was initially developed to save test results in [nao1215/spectest](https://github.com/nao1215/spectest). Therefore, the markdown package implements the features required by spectest. For example, the markdown package supports **mermaid sequence diagrams (entity relationship diagram, sequence diagram, flowchart, pie chart)**, which was a necessary feature in spectest.

Additionally, complex code that increases the complexity of the library, such as generating nested lists, will not be added. I want to keep this library as simple as possible.

Expand All @@ -34,6 +34,7 @@ Additionally, complex code that increases the complexity of the library, such as
- [x] Alerts; NOTE, TIP, IMPORTANT, CAUTION, WARNING
- [x] mermaid sequence diagram
- [x] mermaid entity relationship diagram
- [x] mermaid flowchart
- [x] mermaid pie chart

### Features not in Markdown syntax
Expand Down Expand Up @@ -523,6 +524,85 @@ erDiagram
}
```

### Flowchart syntax

```go
package main

import (
"io"
"os"

"github.com/nao1215/markdown"
"github.com/nao1215/markdown/mermaid/flowchart"
)

//go:generate go run main.go

func main() {
f, err := os.Create("generated.md")
if err != nil {
panic(err)
}

fc := flowchart.NewFlowchart(
io.Discard,
flowchart.WithTitle("mermaid flowchart builder"),
flowchart.WithOrientalTopToBottom(),
).
NodeWithText("A", "Node A").
StadiumNode("B", "Node B").
SubroutineNode("C", "Node C").
DatabaseNode("D", "Database").
LinkWithArrowHead("A", "B").
LinkWithArrowHeadAndText("B", "D", "send original data").
LinkWithArrowHead("B", "C").
DottedLinkWithText("C", "D", "send filtered data").
String()

err = markdown.NewMarkdown(f).
H2("Flowchart").
CodeBlocks(markdown.SyntaxHighlightMermaid, fc).
Build()

if err != nil {
panic(err)
}
}
```

Plain text output: [markdown is here](./doc/flowchart/generated.md)
````
## Flowchart
```mermaid
---
title: mermaid flowchart builder
---
flowchart TB
A["Node A"]
B(["Node B"])
C[["Node C"]]
D[("Database")]
A-->B
B-->|"send original data"|D
B-->C
C-. "send filtered data" .-> D
```
````

Mermaid output:
```mermaid
flowchart TB
A["Node A"]
B(["Node B"])
C[["Node C"]]
D[("Database")]
A-->B
B-->|"send original data"|D
B-->C
C-. "send filtered data" .-> D
```

### Pie chart syntax

```go
Expand Down
15 changes: 15 additions & 0 deletions doc/flowchart/generated.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Flowchart
```mermaid
---
title: mermaid flowchart builder
---
flowchart TB
A["Node A"]
B(["Node B"])
C[["Node C"]]
D[("Database")]
A-->B
B-->|"send original data"|D
B-->C
C-. "send filtered data" .-> D
```
45 changes: 45 additions & 0 deletions doc/flowchart/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//go:build linux || darwin

// Package main is generating flowchart.
package main

import (
"io"
"os"

"github.com/nao1215/markdown"
"github.com/nao1215/markdown/mermaid/flowchart"
)

//go:generate go run main.go

func main() {
f, err := os.Create("generated.md")
if err != nil {
panic(err)
}

fc := flowchart.NewFlowchart(
io.Discard,
flowchart.WithTitle("mermaid flowchart builder"),
flowchart.WithOrientalTopToBottom(),
).
NodeWithText("A", "Node A").
StadiumNode("B", "Node B").
SubroutineNode("C", "Node C").
DatabaseNode("D", "Database").
LinkWithArrowHead("A", "B").
LinkWithArrowHeadAndText("B", "D", "send original data").
LinkWithArrowHead("B", "C").
DottedLinkWithText("C", "D", "send filtered data").
String()

err = markdown.NewMarkdown(f).
H2("Flowchart").
CodeBlocks(markdown.SyntaxHighlightMermaid, fc).
Build()

if err != nil {
panic(err)
}
}
12 changes: 12 additions & 0 deletions internal/lf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Package internal package is used to store the internal implementation of the mermaid package.
package internal

import "runtime"

// LineFeed return line feed for current OS.
func LineFeed() string {
if runtime.GOOS == "windows" {
return "\r\n"
}
return "\n"
}
28 changes: 28 additions & 0 deletions internal/lf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Package internal package is used to store the internal implementation of the mermaid package.
package internal

import (
"runtime"
"testing"
)

func TestLineFeed(t *testing.T) {
t.Parallel()

t.Run("should return line feed for current OS", func(t *testing.T) {
t.Parallel()

got := LineFeed()

switch runtime.GOOS {
case "windows":
if got != "\r\n" {
t.Errorf("expected \\r\\n, but got %s", got)
}
default:
if got != "\n" {
t.Errorf("expected \\n, but got %s", got)
}
}
})
}
23 changes: 8 additions & 15 deletions markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ package markdown
import (
"fmt"
"io"
"runtime"
"strings"

"github.com/nao1215/markdown/internal"
"github.com/olekukonko/tablewriter"
)

Expand Down Expand Up @@ -126,7 +126,7 @@ func NewMarkdown(w io.Writer) *Markdown {

// String returns markdown text.
func (m *Markdown) String() string {
return strings.Join(m.body, lineFeed())
return strings.Join(m.body, internal.LineFeed())
}

// Error returns error.
Expand Down Expand Up @@ -238,7 +238,8 @@ func (m *Markdown) H6f(format string, args ...interface{}) *Markdown {
func (m *Markdown) Details(summary, text string) *Markdown {
m.body = append(
m.body,
fmt.Sprintf("<details><summary>%s</summary>%s%s%s</details>", summary, lineFeed(), text, lineFeed()))
fmt.Sprintf("<details><summary>%s</summary>%s%s%s</details>",
summary, internal.LineFeed(), text, internal.LineFeed()))
return m
}

Expand Down Expand Up @@ -288,7 +289,7 @@ func (m *Markdown) CheckBox(set []CheckBoxSet) *Markdown {
// Blockquote is markdown blockquote.
// If you set text "Hello", it will be converted to "> Hello".
func (m *Markdown) Blockquote(text string) *Markdown {
lines := strings.Split(text, lineFeed())
lines := strings.Split(text, internal.LineFeed())
for _, line := range lines {
m.body = append(m.body, fmt.Sprintf("> %s", line))
}
Expand All @@ -302,7 +303,7 @@ func (m *Markdown) Blockquote(text string) *Markdown {
// ```".
func (m *Markdown) CodeBlocks(lang SyntaxHighlight, text string) *Markdown {
m.body = append(m.body,
fmt.Sprintf("```%s%s%s%s```", lang, lineFeed(), text, lineFeed()))
fmt.Sprintf("```%s%s%s%s```", lang, internal.LineFeed(), text, internal.LineFeed()))
return m
}

Expand Down Expand Up @@ -345,7 +346,7 @@ func (m *Markdown) Table(t TableSet) *Markdown {

buf := &strings.Builder{}
table := tablewriter.NewWriter(buf)
table.SetNewLine(lineFeed())
table.SetNewLine(internal.LineFeed())
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
table.SetHeader(t.Header)
Expand Down Expand Up @@ -379,7 +380,7 @@ func (m *Markdown) CustomTable(t TableSet, options TableOptions) *Markdown {

buf := &strings.Builder{}
table := tablewriter.NewWriter(buf)
table.SetNewLine(lineFeed())
table.SetNewLine(internal.LineFeed())
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
table.SetAutoWrapText(options.AutoWrapText)
Expand All @@ -401,11 +402,3 @@ func (m *Markdown) LF() *Markdown {
m.body = append(m.body, " ")
return m
}

// lineFeed return line feed for current OS.
func lineFeed() string {
if runtime.GOOS == "windows" {
return "\r\n"
}
return "\n"
}
11 changes: 6 additions & 5 deletions markdown_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/nao1215/markdown/internal"
)

func TestPlainText(t *testing.T) {
Expand Down Expand Up @@ -117,7 +118,7 @@ func TestMarkdownDetailsf(t *testing.T) {

m := NewMarkdown(os.Stdout)
m.Detailsf("Hello", "Good %s", "World")
want := fmt.Sprintf("<details><summary>Hello</summary>%sGood World%s</details>", lineFeed(), lineFeed())
want := fmt.Sprintf("<details><summary>Hello</summary>%sGood World%s</details>", internal.LineFeed(), internal.LineFeed())
got := m.body[0]

if diff := cmp.Diff(want, got); diff != "" {
Expand Down Expand Up @@ -189,7 +190,7 @@ func TestMarkdownBlockquote(t *testing.T) {
t.Parallel()

m := NewMarkdown(os.Stdout)
m.Blockquote(fmt.Sprintf("%s%s%s%s%s", "Hello", lineFeed(), "Good", lineFeed(), "World"))
m.Blockquote(fmt.Sprintf("%s%s%s%s%s", "Hello", internal.LineFeed(), "Good", internal.LineFeed(), "World"))
want := []string{
"> Hello",
"> Good",
Expand All @@ -209,7 +210,7 @@ func TestMarkdownCodeBlocks(t *testing.T) {

m := NewMarkdown(os.Stdout)
m.CodeBlocks(SyntaxHighlightGo, "Hello")
want := []string{fmt.Sprintf("```go%sHello%s```", lineFeed(), lineFeed())}
want := []string{fmt.Sprintf("```go%sHello%s```", internal.LineFeed(), internal.LineFeed())}
got := m.body

if diff := cmp.Diff(want, got); diff != "" {
Expand Down Expand Up @@ -279,7 +280,7 @@ func TestMarkdownTable(t *testing.T) {
m.Table(set)
want := []string{
fmt.Sprintf("| NAME | AGE |%s|-------|-----|%s| David | 23 |%s",
lineFeed(), lineFeed(), lineFeed()),
internal.LineFeed(), internal.LineFeed(), internal.LineFeed()),
}
got := m.body

Expand Down Expand Up @@ -388,7 +389,7 @@ func TestMarkdownCustomTable(t *testing.T) {
})
want := []string{
fmt.Sprintf("| Name | Age |%s|-------|-----|%s| David | 23 |%s",
lineFeed(), lineFeed(), lineFeed()),
internal.LineFeed(), internal.LineFeed(), internal.LineFeed()),
}
got := m.body

Expand Down
8 changes: 5 additions & 3 deletions mermaid/er/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package er
import (
"fmt"
"strings"

"github.com/nao1215/markdown/internal"
)

// Entity is a entity of entity relationship.
Expand All @@ -24,9 +26,9 @@ func (e *Entity) string() string {
"%s%s {%s%s%s%s}",
" ", // indent
e.Name,
lineFeed(),
strings.Join(attrs, lineFeed()),
lineFeed(),
internal.LineFeed(),
strings.Join(attrs, internal.LineFeed()),
internal.LineFeed(),
" ", // indent
)
}
Expand Down
Loading

0 comments on commit 1b28ab0

Please sign in to comment.