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:
commit
9a9c046a14
8 changed files with 141 additions and 8 deletions
|
@ -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
11
main.go
|
@ -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>`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
12
org/testdata/hl-lines.html
vendored
Normal 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
9
org/testdata/hl-lines.org
vendored
Normal 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
9
org/testdata/hl-lines.pretty_org
vendored
Normal 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
|
51
org/util.go
51
org/util.go
|
@ -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
38
org/util_test.go
Normal 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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue