2026-04-12 18:30:30 +02:00
|
|
|
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
|
|
package markdown
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
|
2026-05-12 00:53:09 +02:00
|
|
|
"github.com/alecthomas/chroma/v2/lexers"
|
2026-04-12 18:30:30 +02:00
|
|
|
"github.com/yuin/goldmark/ast"
|
|
|
|
|
"github.com/yuin/goldmark/text"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func (g *ASTTransformer) transformCodeblockLanguage(v *ast.FencedCodeBlock, reader text.Reader) {
|
2026-04-29 19:49:55 +02:00
|
|
|
if v.Info == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-04-12 18:30:30 +02:00
|
|
|
src := reader.Source()
|
|
|
|
|
info := v.Info.Segment.Value(src)
|
2026-05-12 00:53:09 +02:00
|
|
|
|
|
|
|
|
// Parse Pandoc style attributes
|
|
|
|
|
// https://pandoc.org/MANUAL.html#extension-fenced_code_attributes
|
|
|
|
|
//
|
|
|
|
|
// For example,
|
|
|
|
|
// ```{.haskell .numberLines}
|
|
|
|
|
// ...
|
|
|
|
|
// ```
|
|
|
|
|
// Should have a language of "haskell", not "{.haskell .numberLines}"
|
|
|
|
|
if trimmed := bytes.TrimSpace(info); bytes.HasPrefix(trimmed, []byte{'{'}) && bytes.HasSuffix(trimmed, []byte{'}'}) {
|
|
|
|
|
attributes := trimmed[1 : len(trimmed)-1]
|
|
|
|
|
for attribute := range bytes.SplitSeq(attributes, []byte{' '}) {
|
|
|
|
|
if class, found := bytes.CutPrefix(attribute, []byte{'.'}); found {
|
|
|
|
|
if lexer := lexers.Get(string(class)); lexer != nil {
|
|
|
|
|
lang := class
|
|
|
|
|
langInx := bytes.Index(info, lang)
|
|
|
|
|
start := v.Info.Segment.Start + langInx
|
|
|
|
|
end := start + len(lang)
|
|
|
|
|
v.Info = ast.NewTextSegment(text.NewSegment(start, end))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-12 18:30:30 +02:00
|
|
|
// Strip language after commas
|
|
|
|
|
//
|
|
|
|
|
// For example,
|
|
|
|
|
// ```rust,ignore
|
|
|
|
|
// ...
|
|
|
|
|
// ```
|
|
|
|
|
// Should have a language of "rust", not "rust,ignore"
|
|
|
|
|
if i := bytes.IndexByte(info, ','); i != -1 {
|
|
|
|
|
start := v.Info.Segment.Start
|
|
|
|
|
v.Info = ast.NewTextSegment(text.NewSegment(start, start+i))
|
|
|
|
|
}
|
|
|
|
|
}
|