Skip to content

Commit 7d5f961

Browse files
authored
Markdown (#387)
# Description This adds a hand-rolled markdown package, and some basic implementation/usage. Since markdown is so simple, it didn't make sense to add another dependency to a 3rd party library that has esoteric rules to customizing the output. Particularly because there doesn't seem to be a single great option with a lot of adoption and maintenance, and I don't want to get stuck with a 3rd party library that disappears and have to swap it later. I'd vendor it but there are license restrictions. This also enables us to add custom markdown if we want, although that quickly becomes a runaway train to generating markdown that's useless to other renderers. I've added a single custom markdown. There is support for `formatters` which is an interface with a bunch of methods for custom wrapping the text of each markdown tag/type. I've implemented a few as test cases within the package: `HTML`, `ANSITags`, and `ReMarkdown` (Produces a clean new markdown representation after parsing a markdown document) ## Changes * Added `markdown` package * templates look for a files ending in `.md` before `.template`, and if found use that file, after running through markdown parser. * Added `attack.md` in help files as a testcase/example. * Added some ansi classes related to markdown rendering as defaults. * `~` (sp1) = `command` ## Supported Markdown ### Rules * Two line breaks starts a new paragraph of text. * Single line breaks collapse into a single line, UNLESS the previous line ended with a double space (Not my convention!) * Most wrapping markdown can be nested, bold within emphasis inside of a Heading, etc. ### Headings Markdown: `# Heading` Html: ``` Html tag output: <h1>Heading</h1> ``` Ansitags: ``` <ansi fg="md-h1" bg="md-h1-bg"><ansi fg="md-h1-prefix" bg="md-h1-prefix-bg">.:</ansi> This is a <ansi fg="md-bold" bg="md-bold-bg">HEADING</ansi></ansi> ``` Note: Adding additional #'s will increment the `<h#>` Note: Ansitags only add the prefix for h1 ### Horizontal Lines Markdown: ``` --- === ::: ``` Html: ``` <hr /> ``` Ansitags: ``` <ansi fg="md-hr1" bg="md-hr1-bg">--------------------------------------------------------------------------------</ansi> <ansi fg="md-hr2" bg="md-hr2-bg">================================================================================</ansi> <ansi fg="md-hr3" bg="md-hr3-bg">::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::</ansi> ``` ### Lists **Markdown:** ``` - List Item 1 - List Sub 1 - List Item 2 - List Item 3 ``` **Html:** ``` <ul> <li>List Item 1 <ul> <li>List Sub 1</li> </ul> </li> <li>List Item 2</li> <li>List Item 3</li> </ul> ``` **Ansitags:** ``` <ansi fg="md-li" bg="md-li-bg">- List Item 1 <ansi fg="md-li" bg="md-li-bg">- List Sub 1</ansi></ansi> <ansi fg="md-li" bg="md-li-bg">- List Item 2</ansi> <ansi fg="md-li" bg="md-li-bg">- List Item 3</ansi> ``` ### Emphasis **Markdown:** `*Emphasize me*` **Html:** ``` <em>Emphasize me</em> ``` **Ansitags:** ``` <ansi fg="md-em" bg="md-em-bg">Emphasize me</ansi> ``` ### Bold **Markdown:** `**Bold me**` **Html:** ``` <strong>Bold me</strong> ``` **Ansitags:** ``` <ansi fg="md-bold" bg="md-bold-bg">Bold me</ansi> ``` ### Special **Markdown:** `~I'm Special~` **Html:** ``` <span data-special="1">I'm Special</span> ``` **Ansitags:** ``` <ansi fg="md-sp1" bg="md-sp1-bg">I'm Special</ansi> ``` **Notes:** Additional wrapping ~'s increment the number: `~~I'm Special~~`, `~~~I'm Special~~~` and so on. **Notes:** `~~` is typically treated as a strikethrough in markdown.
1 parent 2799c72 commit 7d5f961

File tree

15 files changed

+931
-5
lines changed

15 files changed

+931
-5
lines changed

_datafiles/world/default/ansi-aliases.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,13 @@ colors:
153153
mob-corpse: 67
154154
user-corpse: 143
155155
tip-text: 219
156-
character-joined: 120
156+
character-joined: 120
157+
md-h1-prefix: black-bold
158+
md-h1: magenta
159+
md-h2: yellow-bold
160+
md-li: white-bold
161+
md-sp1: command
162+
md-bold: 9
163+
md-em: 208
164+
md-hr1: 4
165+
md-hr2: 4
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Help for ~attack~
2+
3+
The ~attack~ command engages in combat with a player or NPC.
4+
Once started, combat continues until someone flees or someone dies.
5+
6+
## Usage:
7+
8+
~attack goblin~
9+
This would start combat with the goblin.
10+
11+
*Chance to Hit* is calculated as follows:
12+
13+
- **attackersSpeed** / (**atackerSpeed** + **defenderSpeed**) * **70** + **30**
14+
15+
You always have a minimum 5% chance to miss, and a minimum 5% chance to hit.
16+
17+
*Crit Chance* is calculated as follows:
18+
19+
- (**Strength** + **Speed**) / (**attackerLevel** - **defenderLevel**) + **5**
20+

_datafiles/world/default/templates/help/attack.template

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ until someone flees or someone dies.
55

66
<ansi fg="yellow">Usage: </ansi>
77

8-
<ansi fg="command">attack goblin</ansi>
8+
<ansi fg="command">attack goblin</ansi>
99
This would start combat with the goblin
1010

1111
When in combat your chance to hit is: <ansi fg="red">attackersSpeed / (atackerSpeed+defenderSpeed) * 70 + 30</ansi>
1212
You always have a minimum 5% chance to miss and a minimum 5% chance to hit
1313

1414
Crits Chance is calculated as follows: <ansi fg="red">(Strength+Speed) / (attackerLevel-defenderLevel) + 5</ansi>
15+

_datafiles/world/empty/ansi-aliases.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,13 @@ colors:
153153
mob-corpse: 67
154154
user-corpse: 143
155155
tip-text: 219
156-
character-joined: 120
156+
character-joined: 120
157+
md-h1-prefix: black-bold
158+
md-h1: magenta
159+
md-h2: yellow-bold
160+
md-li: white-bold
161+
md-sp1: command
162+
md-bold: 9
163+
md-em: 208
164+
md-hr1: 4
165+
md-hr2: 4
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Help for ~attack~
2+
3+
The ~attack~ command engages in combat with a player or NPC.
4+
Once started, combat continues until someone flees or someone dies.
5+
6+
## Usage:
7+
8+
~attack goblin~
9+
This would start combat with the goblin.
10+
11+
*Chance to Hit* is calculated as follows:
12+
13+
- **attackersSpeed** / (**atackerSpeed** + **defenderSpeed**) * **70** + **30**
14+
15+
You always have a minimum 5% chance to miss, and a minimum 5% chance to hit.
16+
17+
*Crit Chance* is calculated as follows:
18+
19+
- (**Strength** + **Speed**) / (**attackerLevel** - **defenderLevel**) + **5**
20+

internal/markdown/README.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
## Supported Markdown
2+
3+
### Rules
4+
5+
* Two line breaks starts a new paragraph of text.
6+
* Single line breaks collapse into a single line, UNLESS the previous line ended with a double space (Not my convention!)
7+
* Most wrapping markdown can be nested, bold within emphasis inside of a Heading, etc.
8+
9+
### Headings
10+
11+
Markdown:
12+
13+
`# Heading`
14+
15+
Html:
16+
17+
```
18+
Html tag output: <h1>Heading</h1>
19+
```
20+
21+
Ansitags:
22+
23+
```
24+
<ansi fg="md-h1" bg="md-h1-bg"><ansi fg="md-h1-prefix" bg="md-h1-prefix-bg">.:</ansi> This is a <ansi fg="md-bold" bg="md-bold-bg">HEADING</ansi></ansi>
25+
```
26+
27+
Note: Adding additional #'s will increment the `<h#>`
28+
Note: Ansitags only add the prefix for h1
29+
30+
### Horizontal Lines
31+
32+
Markdown:
33+
34+
```
35+
---
36+
===
37+
:::
38+
```
39+
40+
Html:
41+
42+
```
43+
<hr />
44+
```
45+
46+
Ansitags:
47+
48+
```
49+
<ansi fg="md-hr1" bg="md-hr1-bg">--------------------------------------------------------------------------------</ansi>
50+
<ansi fg="md-hr2" bg="md-hr2-bg">================================================================================</ansi>
51+
<ansi fg="md-hr3" bg="md-hr3-bg">::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::</ansi>
52+
```
53+
54+
### Lists
55+
56+
**Markdown:**
57+
58+
```
59+
- List Item 1
60+
- List Sub 1
61+
- List Item 2
62+
- List Item 3
63+
```
64+
65+
**Html:**
66+
67+
```
68+
<ul>
69+
<li>List Item 1
70+
<ul>
71+
<li>List Sub 1</li>
72+
</ul>
73+
</li>
74+
<li>List Item 2</li>
75+
<li>List Item 3</li>
76+
</ul>
77+
```
78+
79+
**Ansitags:**
80+
81+
```
82+
<ansi fg="md-li" bg="md-li-bg">- List Item 1
83+
<ansi fg="md-li" bg="md-li-bg">- List Sub 1</ansi></ansi>
84+
<ansi fg="md-li" bg="md-li-bg">- List Item 2</ansi>
85+
<ansi fg="md-li" bg="md-li-bg">- List Item 3</ansi>
86+
```
87+
88+
### Emphasis
89+
90+
**Markdown:**
91+
92+
`*Emphasize me*`
93+
94+
**Html:**
95+
96+
```
97+
<em>Emphasize me</em>
98+
```
99+
100+
**Ansitags:**
101+
102+
```
103+
<ansi fg="md-em" bg="md-em-bg">Emphasize me</ansi>
104+
```
105+
106+
### Bold
107+
108+
**Markdown:**
109+
110+
`**Bold me**`
111+
112+
**Html:**
113+
114+
```
115+
<strong>Bold me</strong>
116+
```
117+
118+
**Ansitags:**
119+
120+
```
121+
<ansi fg="md-bold" bg="md-bold-bg">Bold me</ansi>
122+
```
123+
124+
### Special
125+
126+
**Markdown:**
127+
128+
`~I'm Special~`
129+
130+
**Html:**
131+
132+
```
133+
<span data-special="1">I'm Special</span>
134+
```
135+
136+
**Ansitags:**
137+
138+
```
139+
<ansi fg="md-sp1" bg="md-sp1-bg">I'm Special</ansi>
140+
```
141+
142+
**Notes:** Additional wrapping ~'s increment the number: `~~I'm Special~~`, `~~~I'm Special~~~` and so on.
143+
**Notes:** `~~` is typically treated as a strikethrough in markdown.

internal/markdown/ast.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package markdown
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
// NodeType identifies the kind of AST node.
8+
type NodeType string
9+
10+
const (
11+
DocumentNode NodeType = "Document"
12+
HeadingNode NodeType = "Heading"
13+
ParagraphNode NodeType = "Paragraph"
14+
HorizontalLineNode NodeType = "HorizontalLine"
15+
HardBreakNode NodeType = "HardBreak"
16+
ListNode NodeType = "List"
17+
ListItemNode NodeType = "ListItem"
18+
TextNode NodeType = "Text"
19+
StrongNode NodeType = "Strong"
20+
EmphasisNode NodeType = "Emphasis"
21+
SpecialNode NodeType = "Special"
22+
)
23+
24+
var (
25+
activeFormatter Formatter = ReMarkdown{}
26+
)
27+
28+
func SetFormatter(newFormatter Formatter) {
29+
activeFormatter = newFormatter
30+
}
31+
32+
// Node is an element in the AST.
33+
type Node interface {
34+
Type() NodeType
35+
Children() []Node
36+
String(int) string
37+
}
38+
39+
// baseNode provides common fields.
40+
type baseNode struct {
41+
nodeType NodeType
42+
nodeChildren []Node
43+
level int
44+
content string
45+
}
46+
47+
func (n *baseNode) Type() NodeType { return n.nodeType }
48+
func (n *baseNode) Children() []Node { return n.nodeChildren }
49+
func (n *baseNode) String(depth int) string {
50+
ret := ``
51+
for _, c := range n.Children() {
52+
if n.Type() == ListNode {
53+
ret += c.String(depth - 1)
54+
} else {
55+
ret += c.String(depth + 1)
56+
}
57+
58+
}
59+
60+
switch n.Type() {
61+
case DocumentNode:
62+
return activeFormatter.Document(ret, depth)
63+
case HeadingNode:
64+
return activeFormatter.Heading(ret, n.level)
65+
case ParagraphNode:
66+
return activeFormatter.Paragraph(ret, depth)
67+
case HorizontalLineNode:
68+
return activeFormatter.HorizontalLine(n.content, depth)
69+
case HardBreakNode:
70+
return activeFormatter.HardBreak(ret, depth)
71+
case ListNode:
72+
return activeFormatter.List(ret, depth)
73+
case ListItemNode:
74+
return activeFormatter.ListItem(ret, depth)
75+
case TextNode:
76+
return activeFormatter.Text(n.content+ret, depth)
77+
case StrongNode:
78+
return activeFormatter.Strong(ret, depth)
79+
case EmphasisNode:
80+
return activeFormatter.Emphasis(ret, depth)
81+
case SpecialNode:
82+
return activeFormatter.Special(ret, n.level)
83+
default:
84+
return fmt.Sprintf(`[INVALID Node: type=%s depth=%d text=%s]`, n.Type(), depth, n.content+"/"+ret)
85+
}
86+
}

0 commit comments

Comments
 (0)