Skip to content

Commit

Permalink
To add support for github md alerts
Browse files Browse the repository at this point in the history
  • Loading branch information
prokod authored and mrueg committed Sep 26, 2024
1 parent c001ad9 commit 035db7b
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 14 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,15 @@ some long code block
Block Quotes are converted to Confluence Info/Warn/Note box when the following conditions are met

1. The BlockQuote is on the root level of the document (not nested)
1. The first line of the BlockQuote contains one of the following patterns `Info/Warn/Note`
1. The first line of the BlockQuote contains one of the following patterns `Info/Warn/Note` or [Github MD Alerts style](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) `[!NOTE]/[!TIP]/[!IMPORTANT]/[!WARNING]/[!CAUTION]`

| Github Alerts | Confluence |
|---------------|------------|
| Tip (green lightbulb) | Tip (green checkmark in circle) |
| Note (blue I in circle) | Info (blue I in circle) |
| Important (purple exclamation mark in speech bubble) | Info (blue I in circle) |
| Warning (yellow exclamation mark in triangle) | Note (yellow exclamation mark in triangle) |
| Caution (red exclamation mark in hexagon) | Warning (red exclamation mark in hexagon) |

In any other case the default behaviour will be resumed and html `<blockquote>` tag will be used

Expand Down
70 changes: 60 additions & 10 deletions pkg/mark/renderer/blockquote.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ const (
Info BlockQuoteType = iota
Note
Warn
Tip
None
)

func (t BlockQuoteType) String() string {
return []string{"info", "note", "warn", "none"}[t]
return []string{"info", "note", "warning", "tip", "none"}[t]
}

type BlockQuoteLevelMap map[ast.Node]int
Expand All @@ -48,27 +49,54 @@ func (m BlockQuoteLevelMap) Level(node ast.Node) int {
return m[node]
}

type BlockQuoteClassifier struct {
patternMap map[string]*regexp.Regexp
}

func LegacyBlockQuoteClassifier() BlockQuoteClassifier {
return BlockQuoteClassifier{
patternMap: map[string]*regexp.Regexp{
"info": regexp.MustCompile(`(?i)info`),
"note": regexp.MustCompile(`(?i)note`),
"warn": regexp.MustCompile(`(?i)warn`),
"tip": regexp.MustCompile(`(?i)tip`),
},
}
}

func GHAlertsBlockQuoteClassifier() BlockQuoteClassifier {
return BlockQuoteClassifier{
patternMap: map[string]*regexp.Regexp{
"info": regexp.MustCompile(`(?i)^\!(note|important)`),
"note": regexp.MustCompile(`(?i)^\!warning`),
"warn": regexp.MustCompile(`(?i)^\!caution`),
"tip": regexp.MustCompile(`(?i)^\!tip`),
},
}
}

// ClassifyingBlockQuote compares a string against a set of patterns and returns a BlockQuoteType
func ClassifyingBlockQuote(literal string) BlockQuoteType {
infoPattern := regexp.MustCompile(`info|Info|INFO`)
notePattern := regexp.MustCompile(`note|Note|NOTE`)
warnPattern := regexp.MustCompile(`warn|Warn|WARN`)
func (classifier BlockQuoteClassifier) ClassifyingBlockQuote(literal string) BlockQuoteType {

var t = None
switch {
case infoPattern.MatchString(literal):
case classifier.patternMap["info"].MatchString(literal):
t = Info
case notePattern.MatchString(literal):
case classifier.patternMap["note"].MatchString(literal):
t = Note
case warnPattern.MatchString(literal):
case classifier.patternMap["warn"].MatchString(literal):
t = Warn
case classifier.patternMap["tip"].MatchString(literal):
t = Tip
}
return t
}

// ParseBlockQuoteType parses the first line of a blockquote and returns its type
func ParseBlockQuoteType(node ast.Node, source []byte) BlockQuoteType {
var t = None
var legacyClassifier = LegacyBlockQuoteClassifier()
var ghAlertsClassifier = GHAlertsBlockQuoteClassifier()

countParagraphs := 0
_ = ast.Walk(node, func(node ast.Node, entering bool) (ast.WalkStatus, error) {
Expand All @@ -80,15 +108,37 @@ func ParseBlockQuoteType(node ast.Node, source []byte) BlockQuoteType {
if countParagraphs < 2 && entering {
if node.Kind() == ast.KindText {
n := node.(*ast.Text)
t = ClassifyingBlockQuote(string(n.Text(source)))
t = legacyClassifier.ClassifyingBlockQuote(string(n.Text(source)))
// If the node is a text node but classification returned none do not give up!
// Find the next two sibling nodes midNode and rightNode,
// 1. If both are also a text node
// 2. and the original node (node) text value is '['
// 3. and the rightNode text value is ']'
// It means with high degree of confidence that the original md doc contains a Github alert type of blockquote
// Classifying the next text type node (midNode) will confirm that.
if t == None {
midNode := node.NextSibling()
rightNode := midNode.NextSibling()

if midNode.Kind() == ast.KindText {
midTextNode := midNode.(*ast.Text)
if rightNode != nil && rightNode.Kind() == ast.KindText {
rightTextNode := rightNode.(*ast.Text)

if string(n.Text(source)) == "[" && string(rightTextNode.Text(source)) == "]" {
t = ghAlertsClassifier.ClassifyingBlockQuote(string(midTextNode.Text(source)))
}
}
}
}
countParagraphs += 1
}
if node.Kind() == ast.KindHTMLBlock {

n := node.(*ast.HTMLBlock)
for i := 0; i < n.BaseBlock.Lines().Len(); i++ {
line := n.BaseBlock.Lines().At(i)
t = ClassifyingBlockQuote(string(line.Value(source)))
t = legacyClassifier.ClassifyingBlockQuote(string(line.Value(source)))
if t != None {
break
}
Expand Down
68 changes: 67 additions & 1 deletion pkg/mark/testdata/quotes-droph1.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ <h2 id="first-heading">First Heading</h2>
<p><strong>Warn (Should not be picked as blockquote type)</strong></p>
</ac:rich-text-body></ac:structured-macro>
<h2 id="second-heading">Second Heading</h2>
<ac:structured-macro ac:name="warn"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<ac:structured-macro ac:name="warning"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p><strong>Warn</strong></p>
<ul>
<li>Warn bullet 1</li>
Expand All @@ -28,7 +28,73 @@ <h2 id="third-heading">Third Heading</h2>
<!-- Info -->
<p>Test</p>
</ac:rich-text-body></ac:structured-macro>
<h2 id="fourth-heading---warn-should-not-get-picked-as-block-quote">Fourth Heading - Warn should not get picked as block quote</h2>
<ac:structured-macro ac:name="tip"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p><strong>TIP:</strong></p>
<ol>
<li>Note number one</li>
<li>Note number two</li>
</ol>
<blockquote>
<p>a
b</p>
</blockquote>
<p><strong>Warn (Should not be picked as blockquote type)</strong></p>
</ac:rich-text-body></ac:structured-macro>
<h2 id="simple-blockquote">Simple Blockquote</h2>
<blockquote>
<p>This paragraph is a simple blockquote</p>
</blockquote>
<h2 id="gh-alerts-heading">GH Alerts Heading</h2>
<h3 id="note-type-alert-heading">Note Type Alert Heading</h3>
<ac:structured-macro ac:name="info"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!NOTE]</p>
<ul>
<li>Note bullet 1</li>
<li>Note bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="tip-type-alert-heading">Tip Type Alert Heading</h3>
<ac:structured-macro ac:name="tip"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!TIP]</p>
<ul>
<li>Tip bullet 1</li>
<li>Tip bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="warning-type-alert-heading">Warning Type Alert Heading</h3>
<ac:structured-macro ac:name="note"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!WARNING]</p>
<ul>
<li>Warning bullet 1</li>
<li>Warning bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="importantcaution-type-alert-heading">Important/Caution Type Alert Heading</h3>
<ac:structured-macro ac:name="info"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!IMPORTANT]</p>
<ul>
<li>Important bullet 1</li>
<li>Important bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<ac:structured-macro ac:name="warning"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!CAUTION]</p>
<ul>
<li>Important bullet 1</li>
<li>Important bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="should-not-be-picked-up-and-converted-into-blockquote-macro">Should not be picked up and converted into blockquote macro</h3>
<blockquote>
<p>[[!NOTE]</p>
</blockquote>
<blockquote>
<p>[!NOTE</p>
</blockquote>
<blockquote>
<p>[Hey !NOTE]</p>
</blockquote>
<blockquote>
<p>[NOTE]</p>
</blockquote>
67 changes: 66 additions & 1 deletion pkg/mark/testdata/quotes-stripnewlines.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ <h2 id="first-heading">First Heading</h2>
<p><strong>Warn (Should not be picked as blockquote type)</strong></p>
</ac:rich-text-body></ac:structured-macro>
<h2 id="second-heading">Second Heading</h2>
<ac:structured-macro ac:name="warn"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<ac:structured-macro ac:name="warning"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p><strong>Warn</strong></p>
<ul>
<li>Warn bullet 1</li>
Expand All @@ -27,7 +27,72 @@ <h2 id="third-heading">Third Heading</h2>
<!-- Info -->
<p>Test</p>
</ac:rich-text-body></ac:structured-macro>
<h2 id="fourth-heading---warn-should-not-get-picked-as-block-quote">Fourth Heading - Warn should not get picked as block quote</h2>
<ac:structured-macro ac:name="tip"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p><strong>TIP:</strong></p>
<ol>
<li>Note number one</li>
<li>Note number two</li>
</ol>
<blockquote>
<p>a b</p>
</blockquote>
<p><strong>Warn (Should not be picked as blockquote type)</strong></p>
</ac:rich-text-body></ac:structured-macro>
<h2 id="simple-blockquote">Simple Blockquote</h2>
<blockquote>
<p>This paragraph is a simple blockquote</p>
</blockquote>
<h2 id="gh-alerts-heading">GH Alerts Heading</h2>
<h3 id="note-type-alert-heading">Note Type Alert Heading</h3>
<ac:structured-macro ac:name="info"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!NOTE]</p>
<ul>
<li>Note bullet 1</li>
<li>Note bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="tip-type-alert-heading">Tip Type Alert Heading</h3>
<ac:structured-macro ac:name="tip"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!TIP]</p>
<ul>
<li>Tip bullet 1</li>
<li>Tip bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="warning-type-alert-heading">Warning Type Alert Heading</h3>
<ac:structured-macro ac:name="note"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!WARNING]</p>
<ul>
<li>Warning bullet 1</li>
<li>Warning bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="importantcaution-type-alert-heading">Important/Caution Type Alert Heading</h3>
<ac:structured-macro ac:name="info"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!IMPORTANT]</p>
<ul>
<li>Important bullet 1</li>
<li>Important bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<ac:structured-macro ac:name="warning"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!CAUTION]</p>
<ul>
<li>Important bullet 1</li>
<li>Important bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="should-not-be-picked-up-and-converted-into-blockquote-macro">Should not be picked up and converted into blockquote macro</h3>
<blockquote>
<p>[[!NOTE]</p>
</blockquote>
<blockquote>
<p>[!NOTE</p>
</blockquote>
<blockquote>
<p>[Hey !NOTE]</p>
</blockquote>
<blockquote>
<p>[NOTE]</p>
</blockquote>
68 changes: 67 additions & 1 deletion pkg/mark/testdata/quotes.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ <h2 id="first-heading">First Heading</h2>
<p><strong>Warn (Should not be picked as blockquote type)</strong></p>
</ac:rich-text-body></ac:structured-macro>
<h2 id="second-heading">Second Heading</h2>
<ac:structured-macro ac:name="warn"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<ac:structured-macro ac:name="warning"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p><strong>Warn</strong></p>
<ul>
<li>Warn bullet 1</li>
Expand All @@ -29,7 +29,73 @@ <h2 id="third-heading">Third Heading</h2>
<!-- Info -->
<p>Test</p>
</ac:rich-text-body></ac:structured-macro>
<h2 id="fourth-heading---warn-should-not-get-picked-as-block-quote">Fourth Heading - Warn should not get picked as block quote</h2>
<ac:structured-macro ac:name="tip"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p><strong>TIP:</strong></p>
<ol>
<li>Note number one</li>
<li>Note number two</li>
</ol>
<blockquote>
<p>a
b</p>
</blockquote>
<p><strong>Warn (Should not be picked as blockquote type)</strong></p>
</ac:rich-text-body></ac:structured-macro>
<h2 id="simple-blockquote">Simple Blockquote</h2>
<blockquote>
<p>This paragraph is a simple blockquote</p>
</blockquote>
<h2 id="gh-alerts-heading">GH Alerts Heading</h2>
<h3 id="note-type-alert-heading">Note Type Alert Heading</h3>
<ac:structured-macro ac:name="info"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!NOTE]</p>
<ul>
<li>Note bullet 1</li>
<li>Note bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="tip-type-alert-heading">Tip Type Alert Heading</h3>
<ac:structured-macro ac:name="tip"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!TIP]</p>
<ul>
<li>Tip bullet 1</li>
<li>Tip bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="warning-type-alert-heading">Warning Type Alert Heading</h3>
<ac:structured-macro ac:name="note"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!WARNING]</p>
<ul>
<li>Warning bullet 1</li>
<li>Warning bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="importantcaution-type-alert-heading">Important/Caution Type Alert Heading</h3>
<ac:structured-macro ac:name="info"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!IMPORTANT]</p>
<ul>
<li>Important bullet 1</li>
<li>Important bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<ac:structured-macro ac:name="warning"><ac:parameter ac:name="icon">true</ac:parameter><ac:rich-text-body>
<p>[!CAUTION]</p>
<ul>
<li>Important bullet 1</li>
<li>Important bullet 2</li>
</ul>
</ac:rich-text-body></ac:structured-macro>
<h3 id="should-not-be-picked-up-and-converted-into-blockquote-macro">Should not be picked up and converted into blockquote macro</h3>
<blockquote>
<p>[[!NOTE]</p>
</blockquote>
<blockquote>
<p>[!NOTE</p>
</blockquote>
<blockquote>
<p>[Hey !NOTE]</p>
</blockquote>
<blockquote>
<p>[NOTE]</p>
</blockquote>
Loading

0 comments on commit 035db7b

Please sign in to comment.