Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attention blocks within quote blocks for Note and Warning #21711

Merged
merged 11 commits into from
Nov 9, 2022
34 changes: 34 additions & 0 deletions modules/markup/markdown/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,37 @@ func IsColorPreview(node ast.Node) bool {
_, ok := node.(*ColorPreview)
return ok
}

const (
AttentionNote string = "note"
AttentionWarning string = "warning"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. It might be a good idea to explicitly define a subtype of string for the available highlight types.
  2. sphinx recognises the following things:

Admonitions: attention, caution, danger, error, hint, important, note, tip, warning and the generic admonition. (Most themes style only “note” and “warning” specially.)

We could think about also supporting the rest, but maybe that's also overkill for this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's overkill but this PR will make it easy to have it in the future by laying the groundwork

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a native English speaker, is admonition a better name for this feature than attention?

)

// Attention is an inline for a color preview
type Attention struct {
ast.BaseInline
AttentionType string
}

// Dump implements Node.Dump.
func (n *Attention) Dump(source []byte, level int) {
m := map[string]string{}
m["AttentionType"] = n.AttentionType
ast.DumpHelper(n, source, level, m, nil)
}

// KindAttention is the NodeKind for Attention
var KindAttention = ast.NewNodeKind("Attention")

// Kind implements Node.Kind.
func (n *Attention) Kind() ast.NodeKind {
return KindAttention
}

// NewAttention returns a new Attention node.
func NewAttention(attentionType string) *Attention {
return &Attention{
BaseInline: ast.BaseInline{},
AttentionType: attentionType,
}
}
40 changes: 40 additions & 0 deletions modules/markup/markdown/goldmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/common"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg"
giteautil "code.gitea.io/gitea/modules/util"

"github.com/microcosm-cc/bluemonday/css"
Expand All @@ -28,6 +29,8 @@ import (

var byteMailto = []byte("mailto:")

const hasThisBlockquoteBeenAttentionMarked = "has-this-blockquote-been-attention-marked"

// ASTTransformer is a default transformer of the goldmark tree.
type ASTTransformer struct{}

Expand Down Expand Up @@ -184,6 +187,20 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
if css.ColorHandler(strings.ToLower(string(colorContent))) {
v.AppendChild(v, NewColorPreview(colorContent))
}
case *ast.Emphasis:
// check if inside blockquote for attention, expected hierarchy is
// Emphasis < Paragraph < Blockquote
grandparent := n.Parent().Parent()
yardenshoham marked this conversation as resolved.
Show resolved Hide resolved
_, isInBlockquote := grandparent.(*ast.Blockquote)
_, blockquoteAlreadyAttentionMarked := grandparent.AttributeString(hasThisBlockquoteBeenAttentionMarked)
if !blockquoteAlreadyAttentionMarked && isInBlockquote {
yardenshoham marked this conversation as resolved.
Show resolved Hide resolved
fullText := strings.ToLower(string(n.Text(reader.Source())))
if fullText == AttentionNote || fullText == AttentionWarning {
v.SetAttributeString("class", []byte("attention-"+fullText))
v.Parent().InsertBefore(v.Parent(), v, NewAttention(fullText))
grandparent.SetAttributeString(hasThisBlockquoteBeenAttentionMarked, []byte("yes"))
}
}
}
return ast.WalkContinue, nil
})
Expand Down Expand Up @@ -273,6 +290,7 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(KindSummary, r.renderSummary)
reg.Register(KindIcon, r.renderIcon)
reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
reg.Register(KindAttention, r.renderAttention)
reg.Register(KindTaskCheckBoxListItem, r.renderTaskCheckBoxListItem)
reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
}
Expand Down Expand Up @@ -309,6 +327,28 @@ func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Nod
return ast.WalkContinue, nil
}

// renderAttention renders a quote marked with i.e. "> **Note**" or "> **Warning**" with a corresponding svg
func (r *HTMLRenderer) renderAttention(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
yardenshoham marked this conversation as resolved.
Show resolved Hide resolved
if entering {
_, _ = w.WriteString(`<span class="attention-`)
n := node.(*Attention)
_, _ = w.WriteString(n.AttentionType)
_, _ = w.WriteString(`">`)

var octiconType string
switch n.AttentionType {
case AttentionNote:
octiconType = "info"
case AttentionWarning:
octiconType = "warning"
}
_, _ = w.WriteString(string(svg.RenderHTML("octicon-" + octiconType)))
} else {
_, _ = w.WriteString("</span>\n")
}
return ast.WalkContinue, nil
}

func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Document)

Expand Down
6 changes: 6 additions & 0 deletions modules/markup/sanitizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ func createDefaultPolicy() *bluemonday.Policy {
// For color preview
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^color-preview$`)).OnElements("span")

// For attention
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^attention-\w+$`)).OnElements("span", "strong")
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^svg octicon-\w+$`)).OnElements("svg")
policy.AllowAttrs("viewBox", "width", "height", "aria-hidden").OnElements("svg")
policy.AllowAttrs("fill-rule", "d").OnElements("path")
yardenshoham marked this conversation as resolved.
Show resolved Hide resolved

// For Chroma markdown plugin
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+( display)?( is-loading)?$`)).OnElements("code")

Expand Down
14 changes: 14 additions & 0 deletions web_src/less/_base.less
Original file line number Diff line number Diff line change
Expand Up @@ -1732,6 +1732,20 @@ a.ui.card:hover,
border-radius: .15em;
}

.attention() {
font-weight: unset;
}

.attention-note {
.attention;
color: var(--color-info-text);
}

.attention-warning {
.attention;
color: var(--color-warning-text);
yardenshoham marked this conversation as resolved.
Show resolved Hide resolved
}

footer {
background-color: var(--color-footer);
border-top: 1px solid var(--color-secondary);
Expand Down