diff options
Diffstat (limited to 'markup/highlight')
-rw-r--r-- | markup/highlight/config.go | 4 | ||||
-rw-r--r-- | markup/highlight/highlight.go | 87 | ||||
-rw-r--r-- | markup/highlight/integration_test.go | 85 |
3 files changed, 162 insertions, 14 deletions
diff --git a/markup/highlight/config.go b/markup/highlight/config.go index 80991f21b..d55958d35 100644 --- 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 index 7e5704132..5b19d6e8e 100644 --- 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 new file mode 100644 index 000000000..2b4379bc2 --- /dev/null +++ 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>", + ) +} |