Merge pull request #85 from kisaragi-hiu/hl_lines

highlightCodeBlock: expose params so they can be passed to Chroma by users
This commit is contained in:
Niklas Fasching 2022-07-25 17:26:51 +02:00 committed by GitHub
commit 9a9c046a14
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 141 additions and 8 deletions

View file

@ -55,7 +55,7 @@ func getWriter() org.Writer {
return w return w
} }
func highlightCodeBlock(source, lang string, inline bool) string { func highlightCodeBlock(source, lang string, inline bool, params map[string]string) string {
var w strings.Builder var w strings.Builder
l := lexers.Get(lang) l := lexers.Get(lang)
if l == nil { if l == nil {
@ -63,7 +63,14 @@ func highlightCodeBlock(source, lang string, inline bool) string {
} }
l = chroma.Coalesce(l) l = chroma.Coalesce(l)
it, _ := l.Tokenise(nil, source) it, _ := l.Tokenise(nil, source)
_ = html.New().Format(&w, styles.Get("github"), it) options := []html.Option{}
if params[":hl_lines"] != "" {
ranges := org.ParseRanges(params[":hl_lines"])
if ranges != nil {
options = append(options, html.HighlightLines(ranges))
}
}
_ = html.New(options...).Format(&w, styles.Get("github"), it)
if inline { if inline {
return `<div class="highlight-inline">` + "\n" + w.String() + "\n" + `</div>` return `<div class="highlight-inline">` + "\n" + w.String() + "\n" + `</div>`
} }

11
main.go
View file

@ -119,7 +119,7 @@ func render(args []string) {
} }
} }
func highlightCodeBlock(source, lang string, inline bool) string { func highlightCodeBlock(source, lang string, inline bool, params map[string]string) string {
var w strings.Builder var w strings.Builder
l := lexers.Get(lang) l := lexers.Get(lang)
if l == nil { if l == nil {
@ -127,7 +127,14 @@ func highlightCodeBlock(source, lang string, inline bool) string {
} }
l = chroma.Coalesce(l) l = chroma.Coalesce(l)
it, _ := l.Tokenise(nil, source) it, _ := l.Tokenise(nil, source)
_ = html.New().Format(&w, styles.Get("friendly"), it) options := []html.Option{}
if params[":hl_lines"] != "" {
ranges := org.ParseRanges(params[":hl_lines"])
if ranges != nil {
options = append(options, html.HighlightLines(ranges))
}
}
_ = html.New(options...).Format(&w, styles.Get("friendly"), it)
if inline { if inline {
return `<div class="highlight-inline">` + "\n" + w.String() + "\n" + `</div>` return `<div class="highlight-inline">` + "\n" + w.String() + "\n" + `</div>`
} }

View file

@ -19,7 +19,7 @@ import (
// HTMLWriter exports an org document into a html document. // HTMLWriter exports an org document into a html document.
type HTMLWriter struct { type HTMLWriter struct {
ExtendingWriter Writer ExtendingWriter Writer
HighlightCodeBlock func(source, lang string, inline bool) string HighlightCodeBlock func(source, lang string, inline bool, params map[string]string) string
PrettyRelativeLinks bool PrettyRelativeLinks bool
strings.Builder strings.Builder
@ -66,7 +66,7 @@ func NewHTMLWriter() *HTMLWriter {
document: &Document{Configuration: defaultConfig}, document: &Document{Configuration: defaultConfig},
log: defaultConfig.Log, log: defaultConfig.Log,
htmlEscape: true, htmlEscape: true,
HighlightCodeBlock: func(source, lang string, inline bool) string { HighlightCodeBlock: func(source, lang string, inline bool, params map[string]string) string {
if inline { if inline {
return fmt.Sprintf("<div class=\"highlight-inline\">\n<pre>\n%s\n</pre>\n</div>", html.EscapeString(source)) return fmt.Sprintf("<div class=\"highlight-inline\">\n<pre>\n%s\n</pre>\n</div>", html.EscapeString(source))
} }
@ -129,7 +129,7 @@ func (w *HTMLWriter) WriteBlock(b Block) {
if len(b.Parameters) >= 1 { if len(b.Parameters) >= 1 {
lang = strings.ToLower(b.Parameters[0]) lang = strings.ToLower(b.Parameters[0])
} }
content = w.HighlightCodeBlock(content, lang, false) content = w.HighlightCodeBlock(content, lang, false, params)
w.WriteString(fmt.Sprintf("<div class=\"src src-%s\">\n%s\n</div>\n", lang, content)) w.WriteString(fmt.Sprintf("<div class=\"src src-%s\">\n%s\n</div>\n", lang, content))
case "EXAMPLE": case "EXAMPLE":
w.WriteString(`<pre class="example">` + "\n" + html.EscapeString(content) + "\n</pre>\n") w.WriteString(`<pre class="example">` + "\n" + html.EscapeString(content) + "\n</pre>\n")
@ -159,7 +159,7 @@ func (w *HTMLWriter) WriteInlineBlock(b InlineBlock) {
switch b.Name { switch b.Name {
case "src": case "src":
lang := strings.ToLower(b.Parameters[0]) lang := strings.ToLower(b.Parameters[0])
content = w.HighlightCodeBlock(content, lang, true) content = w.HighlightCodeBlock(content, lang, true, nil)
w.WriteString(fmt.Sprintf("<div class=\"src src-inline src-%s\">\n%s\n</div>", lang, content)) w.WriteString(fmt.Sprintf("<div class=\"src src-inline src-%s\">\n%s\n</div>", lang, content))
case "export": case "export":
if strings.ToLower(b.Parameters[0]) == "html" { if strings.ToLower(b.Parameters[0]) == "html" {

12
org/testdata/hl-lines.html vendored Normal file
View file

@ -0,0 +1,12 @@
<p>Lines in a source block can be highlighted with <code class="verbatim">:hl_lines</code>.</p>
<div class="src src-emacs-lisp">
<div class="highlight">
<pre>
(+ 1 2)
(+ 1 2)
(+ 1 2)
(+ 1 2)
(+ 1 2)
</pre>
</div>
</div>

9
org/testdata/hl-lines.org vendored Normal file
View file

@ -0,0 +1,9 @@
Lines in a source block can be highlighted with =:hl_lines=.
#+begin_src emacs-lisp :hl_lines 3-4
(+ 1 2)
(+ 1 2)
(+ 1 2)
(+ 1 2)
(+ 1 2)
#+end_src

9
org/testdata/hl-lines.pretty_org vendored Normal file
View file

@ -0,0 +1,9 @@
Lines in a source block can be highlighted with =:hl_lines=.
#+BEGIN_SRC emacs-lisp :hl_lines 3-4
(+ 1 2)
(+ 1 2)
(+ 1 2)
(+ 1 2)
(+ 1 2)
#+END_SRC

View file

@ -1,5 +1,10 @@
package org package org
import (
"strconv"
"strings"
)
func isSecondBlankLine(d *Document, i int) bool { func isSecondBlankLine(d *Document, i int) bool {
if i-1 <= 0 { if i-1 <= 0 {
return false return false
@ -17,3 +22,49 @@ func isImageOrVideoLink(n Node) bool {
} }
return false return false
} }
// Parse ranges like this:
// "3-5" -> [[3, 5]]
// "3 8-10" -> [[3, 3], [8, 10]]
// "3 5 6" -> [[3, 3], [5, 5], [6, 6]]
//
// This is Hugo's hlLinesToRanges with "startLine" removed and errors
// ignored.
func ParseRanges(s string) [][2]int {
var ranges [][2]int
s = strings.TrimSpace(s)
if s == "" {
return ranges
}
fields := strings.Split(s, " ")
for _, field := range fields {
field = strings.TrimSpace(field)
if field == "" {
continue
}
numbers := strings.Split(field, "-")
var r [2]int
if len(numbers) > 1 {
first, err := strconv.Atoi(numbers[0])
if err != nil {
return ranges
}
second, err := strconv.Atoi(numbers[1])
if err != nil {
return ranges
}
r[0] = first
r[1] = second
} else {
first, err := strconv.Atoi(numbers[0])
if err != nil {
return ranges
}
r[0] = first
r[1] = first
}
ranges = append(ranges, r)
}
return ranges
}

38
org/util_test.go Normal file
View file

@ -0,0 +1,38 @@
package org
import (
"fmt"
"testing"
)
var parseRangesTests = map[string][][2]int{
"3-5": {{3, 5}},
"3 8-10": {{3, 3}, {8, 10}},
"3 5 6": {{3, 3}, {5, 5}, {6, 6}},
" 9-10 5-6 3 ": {{9, 10}, {5, 6}, {3, 3}},
}
func TestParseRanges(t *testing.T) {
for s, expected := range parseRangesTests {
t.Run(s, func(t *testing.T) {
actual := ParseRanges(s)
// If this fails it looks like:
// util_test.go:<line>: 9-10 5-6 3 :
// --- Actual
// +++ Expected
// @@ -1 +1 @@
// -[[9 10] [5 9] [3 3]]
// +[[9 10] [5 6] [3 3]]
if len(actual) != len(expected) {
t.Errorf("%v:\n%v", s, diff(fmt.Sprintf("%v", actual), fmt.Sprintf("%v", expected)))
} else {
for i := range actual {
if actual[i] != expected[i] {
t.Errorf("%v:\n%v", s, diff(fmt.Sprintf("%v", actual), fmt.Sprintf("%v", expected)))
}
}
}
})
}
}