To add support for github md alerts

This commit is contained in:
Noam Asor 2024-08-27 15:39:39 +02:00 committed by Manuel Rüger
parent c001ad98cf
commit 035db7b7b3
6 changed files with 328 additions and 14 deletions

View File

@ -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

View File

@ -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
@ -48,20 +49,45 @@ 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
}
@ -69,6 +95,8 @@ func ClassifyingBlockQuote(literal string) BlockQuoteType {
// 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) {
@ -80,7 +108,29 @@ 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 {
@ -88,7 +138,7 @@ func ParseBlockQuoteType(node ast.Node, source []byte) BlockQuoteType {
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
}

View File

@ -12,7 +12,7 @@ b</p>
<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>
@ -28,7 +28,73 @@ that runs long</li>
<!-- 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>

View File

@ -12,7 +12,7 @@
<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>
@ -27,7 +27,72 @@
<!-- 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>

View File

@ -13,7 +13,7 @@ b</p>
<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>
@ -29,7 +29,73 @@ that runs long</li>
<!-- 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>

View File

@ -26,6 +26,65 @@
> <!-- Info -->
> Test
## Fourth Heading - Warn should not get picked as block quote
> **TIP:**
>
> 1. Note number one
> 1. Note number two
>
>> a
>> b
>
> **Warn (Should not be picked as blockquote type)**
## Simple Blockquote
> This paragraph is a simple blockquote
## GH Alerts Heading
### Note Type Alert Heading
> [!NOTE]
>
> * Note bullet 1
> * Note bullet 2
### Tip Type Alert Heading
> [!TIP]
>
> * Tip bullet 1
> * Tip bullet 2
### Warning Type Alert Heading
> [!WARNING]
>
> * Warning bullet 1
> * Warning bullet 2
### Important/Caution Type Alert Heading
> [!IMPORTANT]
>
> * Important bullet 1
> * Important bullet 2
> [!CAUTION]
>
> * Important bullet 1
> * Important bullet 2
### Should not be picked up and converted into blockquote macro
> [[!NOTE]
> [!NOTE
> [Hey !NOTE]
> [NOTE]