mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
markup/highlight: Rework the return value from HighlightCodeblock
To make it possible to render it with a custom HTML ("<div>") wrapper. Updates #9573
This commit is contained in:
parent
39261b689e
commit
3ad39001df
2 changed files with 118 additions and 23 deletions
|
@ -113,6 +113,57 @@ Go Language: golang|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHighlightCodeblock(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
files := `
|
||||||
|
-- config.toml --
|
||||||
|
[markup]
|
||||||
|
[markup.highlight]
|
||||||
|
anchorLineNos = false
|
||||||
|
codeFences = true
|
||||||
|
guessSyntax = false
|
||||||
|
hl_Lines = ''
|
||||||
|
lineAnchors = ''
|
||||||
|
lineNoStart = 1
|
||||||
|
lineNos = false
|
||||||
|
lineNumbersInTable = true
|
||||||
|
noClasses = false
|
||||||
|
style = 'monokai'
|
||||||
|
tabWidth = 4
|
||||||
|
-- layouts/_default/_markup/render-codeblock.html --
|
||||||
|
{{ $result := transform.HighlightCodeBlock . }}
|
||||||
|
Inner: |{{ $result.Inner | safeHTML }}|
|
||||||
|
Wrapped: |{{ $result.Wrapped | safeHTML }}|
|
||||||
|
-- layouts/_default/single.html --
|
||||||
|
{{ .Content }}
|
||||||
|
-- content/p1.md --
|
||||||
|
---
|
||||||
|
title: "p1"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Go Code
|
||||||
|
|
||||||
|
§§§go
|
||||||
|
fmt.Println("Hello, World!");
|
||||||
|
§§§
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
b := hugolib.NewIntegrationTestBuilder(
|
||||||
|
hugolib.IntegrationTestConfig{
|
||||||
|
T: t,
|
||||||
|
TxtarString: files,
|
||||||
|
NeedsOsFS: false,
|
||||||
|
},
|
||||||
|
).Build()
|
||||||
|
|
||||||
|
b.AssertFileContent("public/p1/index.html",
|
||||||
|
"Inner: |<span class=\"line\"><span class=\"cl\"><span class=\"nx\">fmt</span><span class=\"p\">.</span><span class=\"nf\">Println</span><span class=\"p\">(</span><span class=\"s\">"Hello, World!"</span><span class=\"p\">);</span></span></span>|",
|
||||||
|
"Wrapped: |<div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-go\" data-lang=\"go\"><span class=\"line\"><span class=\"cl\"><span class=\"nx\">fmt</span><span class=\"p\">.</span><span class=\"nf\">Println</span><span class=\"p\">(</span><span class=\"s\">"Hello, World!"</span><span class=\"p\">);</span></span></span></code></pre></div>|",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func TestCodeChomp(t *testing.T) {
|
func TestCodeChomp(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ func (h chromaHighlighter) Highlight(code, lang string, opts interface{}) (strin
|
||||||
}
|
}
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
|
|
||||||
if err := highlight(&b, code, lang, nil, cfg); err != nil {
|
if _, _, err := highlight(&b, code, lang, nil, cfg); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,13 +103,15 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts i
|
||||||
return HightlightResult{}, err
|
return HightlightResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err := highlight(&b, ctx.Inner(), ctx.Type(), attributes, cfg)
|
low, high, err := highlight(&b, ctx.Inner(), ctx.Type(), attributes, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return HightlightResult{}, err
|
return HightlightResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return HightlightResult{
|
return HightlightResult{
|
||||||
Body: template.HTML(b.String()),
|
highlighted: template.HTML(b.String()),
|
||||||
|
innerLow: low,
|
||||||
|
innerHigh: high,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +129,8 @@ func (h chromaHighlighter) RenderCodeblock(w hugio.FlexiWriter, ctx hooks.Codebl
|
||||||
|
|
||||||
code := text.Puts(ctx.Inner())
|
code := text.Puts(ctx.Inner())
|
||||||
|
|
||||||
return highlight(w, code, ctx.Type(), attributes, cfg)
|
_, _, err := highlight(w, code, ctx.Type(), attributes, cfg)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h chromaHighlighter) IsDefaultCodeBlockRenderer() bool {
|
func (h chromaHighlighter) IsDefaultCodeBlockRenderer() bool {
|
||||||
|
@ -141,14 +144,22 @@ func (h chromaHighlighter) GetIdentity() identity.Identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
type HightlightResult struct {
|
type HightlightResult struct {
|
||||||
Body template.HTML
|
innerLow int
|
||||||
|
innerHigh int
|
||||||
|
highlighted template.HTML
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HightlightResult) Highlighted() template.HTML {
|
func (h HightlightResult) Wrapped() template.HTML {
|
||||||
return h.Body
|
return h.highlighted
|
||||||
}
|
}
|
||||||
|
|
||||||
func highlight(w hugio.FlexiWriter, code, lang string, attributes []attributes.Attribute, cfg Config) error {
|
func (h HightlightResult) Inner() template.HTML {
|
||||||
|
return h.highlighted[h.innerLow:h.innerHigh]
|
||||||
|
}
|
||||||
|
|
||||||
|
func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes.Attribute, cfg Config) (int, int, error) {
|
||||||
|
var low, high int
|
||||||
|
|
||||||
var lexer chroma.Lexer
|
var lexer chroma.Lexer
|
||||||
if lang != "" {
|
if lang != "" {
|
||||||
lexer = lexers.Get(lang)
|
lexer = lexers.Get(lang)
|
||||||
|
@ -162,12 +173,14 @@ func highlight(w hugio.FlexiWriter, code, lang string, attributes []attributes.A
|
||||||
lang = strings.ToLower(lexer.Config().Name)
|
lang = strings.ToLower(lexer.Config().Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w := &byteCountFlexiWriter{delegate: fw}
|
||||||
|
|
||||||
if lexer == nil {
|
if lexer == nil {
|
||||||
wrapper := getPreWrapper(lang)
|
wrapper := getPreWrapper(lang, w)
|
||||||
fmt.Fprint(w, wrapper.Start(true, ""))
|
fmt.Fprint(w, wrapper.Start(true, ""))
|
||||||
fmt.Fprint(w, gohtml.EscapeString(code))
|
fmt.Fprint(w, gohtml.EscapeString(code))
|
||||||
fmt.Fprint(w, wrapper.End(true))
|
fmt.Fprint(w, wrapper.End(true))
|
||||||
return nil
|
return low, high, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
style := styles.Get(cfg.Style)
|
style := styles.Get(cfg.Style)
|
||||||
|
@ -178,42 +191,44 @@ func highlight(w hugio.FlexiWriter, code, lang string, attributes []attributes.A
|
||||||
|
|
||||||
iterator, err := lexer.Tokenise(nil, code)
|
iterator, err := lexer.Tokenise(nil, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
options := cfg.ToHTMLOptions()
|
options := cfg.ToHTMLOptions()
|
||||||
options = append(options, getHtmlPreWrapper(lang))
|
preWrapper := getPreWrapper(lang, w)
|
||||||
|
options = append(options, html.WithPreWrapper(preWrapper))
|
||||||
|
|
||||||
formatter := html.New(options...)
|
formatter := html.New(options...)
|
||||||
|
|
||||||
writeDivStart(w, attributes)
|
writeDivStart(w, attributes)
|
||||||
|
|
||||||
if err := formatter.Format(w, style, iterator); err != nil {
|
if err := formatter.Format(w, style, iterator); err != nil {
|
||||||
return err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
writeDivEnd(w)
|
writeDivEnd(w)
|
||||||
|
|
||||||
return nil
|
return preWrapper.low, preWrapper.high, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPreWrapper(language string) preWrapper {
|
func getPreWrapper(language string, writeCounter *byteCountFlexiWriter) *preWrapper {
|
||||||
return preWrapper{language: language}
|
return &preWrapper{language: language, writeCounter: writeCounter}
|
||||||
}
|
|
||||||
|
|
||||||
func getHtmlPreWrapper(language string) html.Option {
|
|
||||||
return html.WithPreWrapper(getPreWrapper(language))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type preWrapper struct {
|
type preWrapper struct {
|
||||||
language string
|
low int
|
||||||
|
high int
|
||||||
|
writeCounter *byteCountFlexiWriter
|
||||||
|
language string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p preWrapper) Start(code bool, styleAttr string) string {
|
func (p *preWrapper) Start(code bool, styleAttr string) string {
|
||||||
var language string
|
var language string
|
||||||
if code {
|
if code {
|
||||||
language = p.language
|
language = p.language
|
||||||
}
|
}
|
||||||
w := &strings.Builder{}
|
w := &strings.Builder{}
|
||||||
WritePreStart(w, language, styleAttr)
|
WritePreStart(w, language, styleAttr)
|
||||||
|
p.low = p.writeCounter.counter + w.Len()
|
||||||
return w.String()
|
return w.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +244,8 @@ func WritePreStart(w io.Writer, language, styleAttr string) {
|
||||||
|
|
||||||
const preEnd = "</code></pre>"
|
const preEnd = "</code></pre>"
|
||||||
|
|
||||||
func (p preWrapper) End(code bool) string {
|
func (p *preWrapper) End(code bool) string {
|
||||||
|
p.high = p.writeCounter.counter
|
||||||
return preEnd
|
return preEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,3 +274,31 @@ func writeDivStart(w hugio.FlexiWriter, attrs []attributes.Attribute) {
|
||||||
func writeDivEnd(w hugio.FlexiWriter) {
|
func writeDivEnd(w hugio.FlexiWriter) {
|
||||||
w.WriteString("</div>")
|
w.WriteString("</div>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type byteCountFlexiWriter struct {
|
||||||
|
delegate hugio.FlexiWriter
|
||||||
|
counter int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *byteCountFlexiWriter) Write(p []byte) (int, error) {
|
||||||
|
n, err := w.delegate.Write(p)
|
||||||
|
w.counter += n
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *byteCountFlexiWriter) WriteByte(c byte) error {
|
||||||
|
w.counter++
|
||||||
|
return w.delegate.WriteByte(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *byteCountFlexiWriter) WriteString(s string) (int, error) {
|
||||||
|
n, err := w.delegate.WriteString(s)
|
||||||
|
w.counter += n
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *byteCountFlexiWriter) WriteRune(r rune) (int, error) {
|
||||||
|
n, err := w.delegate.WriteRune(r)
|
||||||
|
w.counter += n
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue