mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-14 20:37:55 -05:00
Add support for Obsidian type blockquote alerts
* Make the alert type parsing more flexible to support more types * Add `AlertTitle` and `AlertSign` (for folding) Note that GitHub will not render callouts with alert title/sign. See https://help.obsidian.md/Editing+and+formatting/Callouts Closes #12805 Closes #12801
This commit is contained in:
parent
0c453420e6
commit
e651d29801
5 changed files with 129 additions and 39 deletions
|
@ -24,6 +24,20 @@ Blockquote render hook templates receive the following [context]:
|
|||
|
||||
(`string`) Applicable when [`Type`](#type) is `alert`, this is the alert type converted to lowercase. See the [alerts](#alerts) section below.
|
||||
|
||||
###### AlertTitle
|
||||
|
||||
{{< new-in 0.134.0 >}}
|
||||
|
||||
(`hstring.HTML`) Applicable when [`Type`](#type) is `alert` when using [Obsidian callouts] syntax, this is the alert title converted to HTML.
|
||||
|
||||
###### AlertSign
|
||||
|
||||
{{< new-in 0.134.0 >}}
|
||||
|
||||
(`string`) Applicable when [`Type`](#type) is `alert` when using [Obsidian callouts] syntax, this is one of "+", "-" or "" (empty string) to indicate the presence of a foldable sign.
|
||||
|
||||
[Obsidian callouts]: https://help.obsidian.md/Editing+and+formatting/Callouts
|
||||
|
||||
###### Attributes
|
||||
|
||||
(`map`) The [Markdown attributes], available if you configure your site as follows:
|
||||
|
@ -117,13 +131,13 @@ Also known as _callouts_ or _admonitions_, alerts are blockquotes used to emphas
|
|||
|
||||
|
||||
{{% note %}}
|
||||
This syntax is compatible with the GitHub Alert Markdown extension.
|
||||
This syntax is compatible with both the GitHub Alert Markdown extension and Obsidian's callout syntax.
|
||||
But note that GitHub will not recognize callouts with one of Obsidian's extensions (e.g. callout title or the foldable sign).
|
||||
{{% /note %}}
|
||||
|
||||
|
||||
The first line of each alert is an alert designator consisting of an exclamation point followed by the alert type, wrapped within brackets.
|
||||
|
||||
The blockquote render hook below renders a multilingual alert if an alert desginator is present, otherwise it renders a blockquote according to the CommonMark specification.
|
||||
The blockquote render hook below renders a multilingual alert if an alert designator is present, otherwise it renders a blockquote according to the CommonMark specification.
|
||||
|
||||
{{< code file=layouts/_default/_markup/render-blockquote.html copy=true >}}
|
||||
{{ $emojis := dict
|
||||
|
|
|
@ -109,6 +109,16 @@ type BlockquoteContext interface {
|
|||
// The GitHub alert type converted to lowercase, e.g. "note".
|
||||
// Only set if Type is "alert".
|
||||
AlertType() string
|
||||
|
||||
// The alert title.
|
||||
// Currently only relevant for Obsidian alerts.
|
||||
// GitHub does not suport alert titles and will not render alerts with titles.
|
||||
AlertTitle() hstring.HTML
|
||||
|
||||
// The alert sign, "+" or "-" or "" used to indicate folding.
|
||||
// Currently only relevant for Obsidian alerts.
|
||||
// GitHub does not suport alert signs and will not render alerts with signs.
|
||||
AlertSign() string
|
||||
}
|
||||
|
||||
type PositionerSourceTargetProvider interface {
|
||||
|
|
|
@ -74,8 +74,8 @@ func (r *htmlRenderer) renderBlockquote(w util.BufWriter, src []byte, node ast.N
|
|||
ordinal := ctx.GetAndIncrementOrdinal(ast.KindBlockquote)
|
||||
|
||||
typ := typeRegular
|
||||
alertType := resolveGitHubAlert(string(text))
|
||||
if alertType != "" {
|
||||
alert := resolveBlockQuoteAlert(string(text))
|
||||
if alert.typ != "" {
|
||||
typ = typeAlert
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ func (r *htmlRenderer) renderBlockquote(w util.BufWriter, src []byte, node ast.N
|
|||
bqctx := &blockquoteContext{
|
||||
BaseContext: render.NewBaseContext(ctx, renderer, n, src, nil, ordinal),
|
||||
typ: typ,
|
||||
alertType: alertType,
|
||||
alert: alert,
|
||||
text: hstring.HTML(text),
|
||||
AttributesHolder: attributes.New(n.Attributes(), attributes.AttributesOwnerGeneral),
|
||||
}
|
||||
|
@ -133,11 +133,9 @@ func (r *htmlRenderer) renderBlockquoteDefault(
|
|||
|
||||
type blockquoteContext struct {
|
||||
hooks.BaseContext
|
||||
|
||||
text hstring.HTML
|
||||
alertType string
|
||||
typ string
|
||||
|
||||
text hstring.HTML
|
||||
typ string
|
||||
alert blockQuoteAlert
|
||||
*attributes.AttributesHolder
|
||||
}
|
||||
|
||||
|
@ -146,25 +144,40 @@ func (c *blockquoteContext) Type() string {
|
|||
}
|
||||
|
||||
func (c *blockquoteContext) AlertType() string {
|
||||
return c.alertType
|
||||
return c.alert.typ
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) AlertTitle() hstring.HTML {
|
||||
return hstring.HTML(c.alert.title)
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) AlertSign() string {
|
||||
return c.alert.sign
|
||||
}
|
||||
|
||||
func (c *blockquoteContext) Text() hstring.HTML {
|
||||
return c.text
|
||||
}
|
||||
|
||||
// https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts
|
||||
// Five types:
|
||||
// [!NOTE], [!TIP], [!WARNING], [!IMPORTANT], [!CAUTION]
|
||||
// Note that GitHub's implementation is case-insensitive.
|
||||
var gitHubAlertRe = regexp.MustCompile(`(?i)^<p>\[!(NOTE|TIP|WARNING|IMPORTANT|CAUTION)\]`)
|
||||
var blockQuoteAlertRe = regexp.MustCompile(`^<p>\[!([a-zA-Z]+)\](-|\+)?[^\S\r\n]?([^\n]*)\n?`)
|
||||
|
||||
// resolveGitHubAlert returns one of note, tip, warning, important or caution.
|
||||
// An empty string if no match.
|
||||
func resolveGitHubAlert(s string) string {
|
||||
m := gitHubAlertRe.FindStringSubmatch(s)
|
||||
if len(m) == 2 {
|
||||
return strings.ToLower(m[1])
|
||||
func resolveBlockQuoteAlert(s string) blockQuoteAlert {
|
||||
m := blockQuoteAlertRe.FindStringSubmatch(s)
|
||||
if len(m) == 4 {
|
||||
return blockQuoteAlert{
|
||||
typ: strings.ToLower(m[1]),
|
||||
sign: m[2],
|
||||
title: m[3],
|
||||
}
|
||||
}
|
||||
return ""
|
||||
|
||||
return blockQuoteAlert{}
|
||||
}
|
||||
|
||||
// Blockquote alert syntax was introduced by GitHub, but is also used
|
||||
// by Obsidian which also support some extended attributes: More types, alert titles and a +/- sign for folding.
|
||||
type blockQuoteAlert struct {
|
||||
typ string
|
||||
sign string
|
||||
title string
|
||||
}
|
||||
|
|
|
@ -109,3 +109,48 @@ Content: {{ .Content }}
|
|||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/p1/index.html", "Content: <blockquote>\n</blockquote>\n")
|
||||
}
|
||||
|
||||
func TestBlockquObsidianWithTitleAndSign(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: "Home"
|
||||
---
|
||||
|
||||
> [!danger]
|
||||
> Do not approach or handle without protective gear.
|
||||
|
||||
|
||||
> [!tip] Callouts can have custom titles
|
||||
> Like this one.
|
||||
|
||||
> [!tip] Title-only callout
|
||||
|
||||
> [!faq]- Foldable negated callout
|
||||
> Yes! In a foldable callout, the contents are hidden when the callout is collapsed
|
||||
|
||||
> [!faq]+ Foldable callout
|
||||
> Yes! In a foldable callout, the contents are hidden when the callout is collapsed
|
||||
|
||||
-- layouts/index.html --
|
||||
{{ .Content }}
|
||||
-- layouts/_default/_markup/render-blockquote.html --
|
||||
AlertType: {{ .AlertType }}|
|
||||
AlertTitle: {{ .AlertTitle }}|
|
||||
AlertSign: {{ .AlertSign | safeHTML }}|
|
||||
Text: {{ .Text }}|
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/index.html",
|
||||
"AlertType: tip|\nAlertTitle: Callouts can have custom titles|\nAlertSign: |",
|
||||
"AlertType: tip|\nAlertTitle: Title-only callout</p>|\nAlertSign: |",
|
||||
"AlertType: faq|\nAlertTitle: Foldable negated callout|\nAlertSign: -|\nText: <p>Yes!",
|
||||
"AlertType: faq|\nAlertTitle: Foldable callout|\nAlertSign: +|",
|
||||
"AlertType: danger|\nAlertTitle: |\nAlertSign: |\nText: <p>Do not approach or handle without protective gear.</p>\n|",
|
||||
)
|
||||
}
|
||||
|
|
|
@ -19,42 +19,50 @@ import (
|
|||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestResolveGitHubAlert(t *testing.T) {
|
||||
func TestResolveBlockQuoteAlert(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := qt.New(t)
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
expected blockQuoteAlert
|
||||
}{
|
||||
{
|
||||
input: "[!NOTE]",
|
||||
expected: "note",
|
||||
expected: blockQuoteAlert{typ: "note"},
|
||||
},
|
||||
{
|
||||
input: "[!WARNING]",
|
||||
expected: "warning",
|
||||
input: "[!FaQ]",
|
||||
expected: blockQuoteAlert{typ: "faq"},
|
||||
},
|
||||
{
|
||||
input: "[!TIP]",
|
||||
expected: "tip",
|
||||
input: "[!NOTE]+",
|
||||
expected: blockQuoteAlert{typ: "note", sign: "+"},
|
||||
},
|
||||
{
|
||||
input: "[!IMPORTANT]",
|
||||
expected: "important",
|
||||
input: "[!NOTE]-",
|
||||
expected: blockQuoteAlert{typ: "note", sign: "-"},
|
||||
},
|
||||
{
|
||||
input: "[!CAUTION]",
|
||||
expected: "caution",
|
||||
input: "[!NOTE] This is a note",
|
||||
expected: blockQuoteAlert{typ: "note", title: "This is a note"},
|
||||
},
|
||||
{
|
||||
input: "[!FOO]",
|
||||
expected: "",
|
||||
input: "[!NOTE]+ This is a note",
|
||||
expected: blockQuoteAlert{typ: "note", sign: "+", title: "This is a note"},
|
||||
},
|
||||
{
|
||||
input: "[!NOTE]+ This is a title\nThis is not.",
|
||||
expected: blockQuoteAlert{typ: "note", sign: "+", title: "This is a title"},
|
||||
},
|
||||
{
|
||||
input: "[!NOTE]\nThis is not.",
|
||||
expected: blockQuoteAlert{typ: "note"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c.Assert(resolveGitHubAlert("<p>"+test.input), qt.Equals, test.expected)
|
||||
for i, test := range tests {
|
||||
c.Assert(resolveBlockQuoteAlert("<p>"+test.input), qt.Equals, test.expected, qt.Commentf("Test %d", i))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue