hugo

Unnamed repository; edit this file 'description' to name the repository.

git clone git://git.shimmy1996.com/hugo.git
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:
Mmarkup/highlight/config.go | 4++++
Mmarkup/highlight/highlight.go | 87++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Amarkup/highlight/integration_test.go | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmarkup/internal/attributes/attributes.go | 1+
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\">&#34;this highlight shortcode&#34;</span><span class=\"p\">)</span></code>:End.",
+		"Inline Unknown:<code class=\"code-inline language-foo\">(message &#34;this highlight shortcode&#34;)</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\">&#34;highlight me&#34;</span><span class=\"o\">)</span>\n</code>",
+		"HighlightCodeBlock: Wrapped:<code class=\"code-inline language-html\">(message &#34;highlight me 2&#34;)</code>|Inner:<code class=\"code-inline language-html\">(message &#34;highlight me 2&#34;)</code>",
+		"<code class=\"code-inline language-foo\">(message &#34;highlight me 3&#34;)\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,