commit d863dde6c653700fb540d7e4dd6605c7186f59da
parent 580b214a4ccda8d8542a8c3d5869c5436c05c3e9
Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Date: Wed, 15 Jun 2022 13:51:29 +0200
markup/highlight: Add hl_inline option
Closes #9442
Closes #9635
Closes #9638
Diffstat:
4 files changed, 163 insertions(+), 14 deletions(-)
diff --git a/markup/highlight/config.go b/markup/highlight/config.go
@@ -72,6 +72,9 @@ type Config struct {
// A space separated list of line numbers, e.g. “3-8 10-20”.
Hl_Lines string
+ // If set, the markup will not be wrapped in any container.
+ Hl_inline bool
+
// A parsed and ready to use list of line ranges.
HL_lines_parsed [][2]int `json:"-"`
@@ -93,6 +96,7 @@ func (cfg Config) ToHTMLOptions() []html.Option {
html.LineNumbersInTable(cfg.LineNumbersInTable),
html.WithClasses(!cfg.NoClasses),
html.LinkableLineNumbers(cfg.AnchorLineNos, lineAnchors),
+ html.InlineCode(cfg.Hl_inline),
}
if cfg.Hl_Lines != "" || cfg.HL_lines_parsed != nil {
diff --git a/markup/highlight/highlight.go b/markup/highlight/highlight.go
@@ -88,6 +88,7 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts a
var b strings.Builder
attributes := ctx.(hooks.AttributesOptionsSliceProvider).AttributesSlice()
+
options := ctx.Options()
if err := applyOptionsFromMap(options, &cfg); err != nil {
@@ -108,8 +109,13 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts a
return HightlightResult{}, err
}
+ highlighted := b.String()
+ if high == 0 {
+ high = len(highlighted)
+ }
+
return HightlightResult{
- highlighted: template.HTML(b.String()),
+ highlighted: template.HTML(highlighted),
innerLow: low,
innerHigh: high,
}, nil
@@ -117,6 +123,7 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts a
func (h chromaHighlighter) RenderCodeblock(w hugio.FlexiWriter, ctx hooks.CodeblockContext) error {
cfg := h.cfg
+
attributes := ctx.(hooks.AttributesOptionsSliceProvider).AttributesSlice()
if err := applyOptionsFromMap(ctx.Options(), &cfg); err != nil {
@@ -158,8 +165,6 @@ func (h HightlightResult) Inner() template.HTML {
}
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)
@@ -176,11 +181,15 @@ func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes.
w := &byteCountFlexiWriter{delegate: fw}
if lexer == nil {
- wrapper := getPreWrapper(lang, w)
- fmt.Fprint(w, wrapper.Start(true, ""))
- fmt.Fprint(w, gohtml.EscapeString(code))
- fmt.Fprint(w, wrapper.End(true))
- return low, high, nil
+ if cfg.Hl_inline {
+ fmt.Fprint(w, fmt.Sprintf("<code%s>%s</code>", inlineCodeAttrs(lang), gohtml.EscapeString(code)))
+ } else {
+ preWrapper := getPreWrapper(lang, w)
+ fmt.Fprint(w, preWrapper.Start(true, ""))
+ fmt.Fprint(w, gohtml.EscapeString(code))
+ fmt.Fprint(w, preWrapper.End(true))
+ }
+ return 0, 0, nil
}
style := styles.Get(cfg.Style)
@@ -194,20 +203,51 @@ func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes.
return 0, 0, err
}
+ if !cfg.Hl_inline {
+ writeDivStart(w, attributes)
+ }
+
options := cfg.ToHTMLOptions()
- preWrapper := getPreWrapper(lang, w)
- options = append(options, html.WithPreWrapper(preWrapper))
+ var wrapper html.PreWrapper
+
+ if cfg.Hl_inline {
+ wrapper = startEnd{
+ start: func(code bool, styleAttr string) string {
+ if code {
+ return fmt.Sprintf(`<code%s>`, inlineCodeAttrs(lang))
+ }
+ return ``
+ },
+ end: func(code bool) string {
+ if code {
+ return `</code>`
+ }
+
+ return ``
+ },
+ }
- formatter := html.New(options...)
+ } else {
+ wrapper = getPreWrapper(lang, w)
+ }
+
+ options = append(options, html.WithPreWrapper(wrapper))
- writeDivStart(w, attributes)
+ formatter := html.New(options...)
if err := formatter.Format(w, style, iterator); err != nil {
return 0, 0, err
}
- writeDivEnd(w)
- return preWrapper.low, preWrapper.high, nil
+ if !cfg.Hl_inline {
+ writeDivEnd(w)
+ }
+
+ if p, ok := wrapper.(*preWrapper); ok {
+ return p.low, p.high, nil
+ }
+
+ return 0, 0, nil
}
func getPreWrapper(language string, writeCounter *byteCountFlexiWriter) *preWrapper {
@@ -232,6 +272,12 @@ func (p *preWrapper) Start(code bool, styleAttr string) string {
return w.String()
}
+func inlineCodeAttrs(lang string) string {
+ if lang == "" {
+ }
+ return fmt.Sprintf(` class="code-inline language-%s"`, lang)
+}
+
func WritePreStart(w io.Writer, language, styleAttr string) {
fmt.Fprintf(w, `<pre tabindex="0"%s>`, styleAttr)
fmt.Fprint(w, "<code")
@@ -249,6 +295,19 @@ func (p *preWrapper) End(code bool) string {
return preEnd
}
+type startEnd struct {
+ start func(code bool, styleAttr string) string
+ end func(code bool) string
+}
+
+func (s startEnd) Start(code bool, styleAttr string) string {
+ return s.start(code, styleAttr)
+}
+
+func (s startEnd) End(code bool) string {
+ return s.end(code)
+}
+
func WritePreEnd(w io.Writer) {
fmt.Fprint(w, preEnd)
}
diff --git a/markup/highlight/integration_test.go b/markup/highlight/integration_test.go
@@ -0,0 +1,85 @@
+// Copyright 2022 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package highlight_test
+
+import (
+ "testing"
+
+ "github.com/gohugoio/hugo/hugolib"
+)
+
+func TestHighlightInline(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- config.toml --
+[markup]
+[markup.highlight]
+codeFences = true
+noClasses = false
+-- content/p1.md --
+---
+title: "p1"
+---
+
+## Inline in Shortcode
+
+Inline:{{< highlight emacs "hl_inline=true" >}}(message "this highlight shortcode"){{< /highlight >}}:End.
+Inline Unknown:{{< highlight foo "hl_inline=true" >}}(message "this highlight shortcode"){{< /highlight >}}:End.
+
+## Inline in code block
+
+Not sure if this makes sense, but add a test for it:
+
+§§§bash {hl_inline=true}
+(message "highlight me")
+§§§
+
+## HighlightCodeBlock in hook
+
+§§§html
+(message "highlight me 2")
+§§§
+
+## Unknown lexer
+
+§§§foo {hl_inline=true}
+(message "highlight me 3")
+§§§
+
+
+-- layouts/_default/_markup/render-codeblock-html.html --
+{{ $opts := dict "hl_inline" true }}
+{{ $result := transform.HighlightCodeBlock . $opts }}
+HighlightCodeBlock: Wrapped:{{ $result.Wrapped }}|Inner:{{ $result.Inner }}
+-- layouts/_default/single.html --
+{{ .Content }}
+`
+
+ b := hugolib.NewIntegrationTestBuilder(
+ hugolib.IntegrationTestConfig{
+ T: t,
+ TxtarString: files,
+ NeedsOsFS: false,
+ },
+ ).Build()
+
+ b.AssertFileContent("public/p1/index.html",
+ "Inline:<code class=\"code-inline language-emacs\"><span class=\"p\">(</span><span class=\"nf\">message</span> <span class=\"s\">"this highlight shortcode"</span><span class=\"p\">)</span></code>:End.",
+ "Inline Unknown:<code class=\"code-inline language-foo\">(message "this highlight shortcode")</code>:End.",
+ "Not sure if this makes sense, but add a test for it:</p>\n<code class=\"code-inline language-bash\"><span class=\"o\">(</span>message <span class=\"s2\">"highlight me"</span><span class=\"o\">)</span>\n</code>",
+ "HighlightCodeBlock: Wrapped:<code class=\"code-inline language-html\">(message "highlight me 2")</code>|Inner:<code class=\"code-inline language-html\">(message "highlight me 2")</code>",
+ "<code class=\"code-inline language-foo\">(message "highlight me 3")\n</code>",
+ )
+}
diff --git a/markup/internal/attributes/attributes.go b/markup/internal/attributes/attributes.go
@@ -30,6 +30,7 @@ var chromaHightlightProcessingAttributes = map[string]bool{
"anchorLineNos": true,
"guessSyntax": true,
"hl_Lines": true,
+ "hl_inline": true,
"lineAnchors": true,
"lineNos": true,
"lineNoStart": true,