mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -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) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ func (h chromaHighlighter) Highlight(code, lang string, opts interface{}) (strin
|
|||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -103,13 +103,15 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts i
|
|||
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 {
|
||||
return HightlightResult{}, err
|
||||
}
|
||||
|
||||
return HightlightResult{
|
||||
Body: template.HTML(b.String()),
|
||||
highlighted: template.HTML(b.String()),
|
||||
innerLow: low,
|
||||
innerHigh: high,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -127,7 +129,8 @@ func (h chromaHighlighter) RenderCodeblock(w hugio.FlexiWriter, ctx hooks.Codebl
|
|||
|
||||
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 {
|
||||
|
@ -141,14 +144,22 @@ func (h chromaHighlighter) GetIdentity() identity.Identity {
|
|||
}
|
||||
|
||||
type HightlightResult struct {
|
||||
Body template.HTML
|
||||
innerLow int
|
||||
innerHigh int
|
||||
highlighted template.HTML
|
||||
}
|
||||
|
||||
func (h HightlightResult) Highlighted() template.HTML {
|
||||
return h.Body
|
||||
func (h HightlightResult) Wrapped() template.HTML {
|
||||
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
|
||||
if 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)
|
||||
}
|
||||
|
||||
w := &byteCountFlexiWriter{delegate: fw}
|
||||
|
||||
if lexer == nil {
|
||||
wrapper := getPreWrapper(lang)
|
||||
wrapper := getPreWrapper(lang, w)
|
||||
fmt.Fprint(w, wrapper.Start(true, ""))
|
||||
fmt.Fprint(w, gohtml.EscapeString(code))
|
||||
fmt.Fprint(w, wrapper.End(true))
|
||||
return nil
|
||||
return low, high, nil
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
options := cfg.ToHTMLOptions()
|
||||
options = append(options, getHtmlPreWrapper(lang))
|
||||
preWrapper := getPreWrapper(lang, w)
|
||||
options = append(options, html.WithPreWrapper(preWrapper))
|
||||
|
||||
formatter := html.New(options...)
|
||||
|
||||
writeDivStart(w, attributes)
|
||||
|
||||
if err := formatter.Format(w, style, iterator); err != nil {
|
||||
return err
|
||||
return 0, 0, err
|
||||
}
|
||||
writeDivEnd(w)
|
||||
|
||||
return nil
|
||||
return preWrapper.low, preWrapper.high, nil
|
||||
}
|
||||
|
||||
func getPreWrapper(language string) preWrapper {
|
||||
return preWrapper{language: language}
|
||||
}
|
||||
|
||||
func getHtmlPreWrapper(language string) html.Option {
|
||||
return html.WithPreWrapper(getPreWrapper(language))
|
||||
func getPreWrapper(language string, writeCounter *byteCountFlexiWriter) *preWrapper {
|
||||
return &preWrapper{language: language, writeCounter: writeCounter}
|
||||
}
|
||||
|
||||
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
|
||||
if code {
|
||||
language = p.language
|
||||
}
|
||||
w := &strings.Builder{}
|
||||
WritePreStart(w, language, styleAttr)
|
||||
p.low = p.writeCounter.counter + w.Len()
|
||||
return w.String()
|
||||
}
|
||||
|
||||
|
@ -229,7 +244,8 @@ func WritePreStart(w io.Writer, language, styleAttr string) {
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -258,3 +274,31 @@ func writeDivStart(w hugio.FlexiWriter, attrs []attributes.Attribute) {
|
|||
func writeDivEnd(w hugio.FlexiWriter) {
|
||||
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