Skip to content

Commit d3926cc

Browse files
bepalecthomas
authored andcommitted
Add WithPreWrapper option
1 parent 3aaf3e5 commit d3926cc

2 files changed

Lines changed: 118 additions & 22 deletions

File tree

formatters/html/html.go

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,18 @@ func WithClasses() Option { return func(f *Formatter) { f.Classes = true } }
2626
func TabWidth(width int) Option { return func(f *Formatter) { f.tabWidth = width } }
2727

2828
// PreventSurroundingPre prevents the surrounding pre tags around the generated code
29-
func PreventSurroundingPre() Option { return func(f *Formatter) { f.preventSurroundingPre = true } }
29+
func PreventSurroundingPre() Option {
30+
return func(f *Formatter) {
31+
f.preWrapper = nopPreWrapper
32+
}
33+
}
34+
35+
// WithPreWrapper allows control of the surrounding pre tags.
36+
func WithPreWrapper(wrapper PreWrapper) Option {
37+
return func(f *Formatter) {
38+
f.preWrapper = wrapper
39+
}
40+
}
3041

3142
// WithLineNumbers formats output with line numbers.
3243
func WithLineNumbers() Option {
@@ -64,24 +75,65 @@ func BaseLineNumber(n int) Option {
6475
func New(options ...Option) *Formatter {
6576
f := &Formatter{
6677
baseLineNumber: 1,
78+
preWrapper: defaultPreWrapper,
6779
}
6880
for _, option := range options {
6981
option(f)
7082
}
7183
return f
7284
}
7385

86+
// PreWrapper defines the operations supported in WithPreWrapper.
87+
type PreWrapper interface {
88+
// Start is called to write a start <pre> element.
89+
// The code flag tells whether this block surrounds
90+
// highlighted code. This will be false when surrounding
91+
// line numbers.
92+
Start(code bool, styleAttr string) string
93+
94+
// End is called to write the end </pre> element.
95+
End(code bool) string
96+
}
97+
98+
type preWrapper struct {
99+
start func(code bool, styleAttr string) string
100+
end func(code bool) string
101+
}
102+
103+
func (p preWrapper) Start(code bool, styleAttr string) string {
104+
return p.start(code, styleAttr)
105+
}
106+
107+
func (p preWrapper) End(code bool) string {
108+
return p.end(code)
109+
}
110+
111+
var (
112+
nopPreWrapper = preWrapper{
113+
start: func(code bool, styleAttr string) string { return "" },
114+
end: func(code bool) string { return "" },
115+
}
116+
defaultPreWrapper = preWrapper{
117+
start: func(code bool, styleAttr string) string {
118+
return fmt.Sprintf("<pre%s>", styleAttr)
119+
},
120+
end: func(code bool) string {
121+
return "</pre>"
122+
},
123+
}
124+
)
125+
74126
// Formatter that generates HTML.
75127
type Formatter struct {
76-
standalone bool
77-
prefix string
78-
Classes bool // Exported field to detect when classes are being used
79-
preventSurroundingPre bool
80-
tabWidth int
81-
lineNumbers bool
82-
lineNumbersInTable bool
83-
highlightRanges highlightRanges
84-
baseLineNumber int
128+
standalone bool
129+
prefix string
130+
Classes bool // Exported field to detect when classes are being used
131+
preWrapper PreWrapper
132+
tabWidth int
133+
lineNumbers bool
134+
lineNumbersInTable bool
135+
highlightRanges highlightRanges
136+
baseLineNumber int
85137
}
86138

87139
type highlightRanges [][2]int
@@ -129,9 +181,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
129181
fmt.Fprintf(w, "<div%s>\n", f.styleAttr(css, chroma.Background))
130182
fmt.Fprintf(w, "<table%s><tr>", f.styleAttr(css, chroma.LineTable))
131183
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD))
132-
if !f.preventSurroundingPre {
133-
fmt.Fprintf(w, "<pre%s>", f.styleAttr(css, chroma.Background))
134-
}
184+
fmt.Fprintf(w, f.preWrapper.Start(false, f.styleAttr(css, chroma.Background)))
135185
for index := range lines {
136186
line := f.baseLineNumber + index
137187
highlight, next := f.shouldHighlight(highlightIndex, line)
@@ -148,16 +198,13 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
148198
fmt.Fprintf(w, "</span>")
149199
}
150200
}
151-
if !f.preventSurroundingPre {
152-
fmt.Fprint(w, "</pre>")
153-
}
201+
fmt.Fprint(w, f.preWrapper.End(false))
154202
fmt.Fprint(w, "</td>\n")
155203
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD, "width:100%"))
156204
}
157205

158-
if !f.preventSurroundingPre {
159-
fmt.Fprintf(w, "<pre%s>", f.styleAttr(css, chroma.Background))
160-
}
206+
fmt.Fprintf(w, f.preWrapper.Start(true, f.styleAttr(css, chroma.Background)))
207+
161208
highlightIndex = 0
162209
for index, tokens := range lines {
163210
// 1-based line number.
@@ -187,9 +234,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
187234
}
188235
}
189236

190-
if !f.preventSurroundingPre {
191-
fmt.Fprint(w, "</pre>")
192-
}
237+
fmt.Fprintf(w, f.preWrapper.End(true))
193238

194239
if wrapInTable {
195240
fmt.Fprint(w, "</td></tr></table>\n")

formatters/html/html_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package html
22

33
import (
44
"bytes"
5+
"fmt"
56
"io/ioutil"
67
"strings"
78
"testing"
@@ -106,3 +107,53 @@ func TestTableLineNumberNewlines(t *testing.T) {
106107
</span><span class="lnt">4
107108
</span>`)
108109
}
110+
111+
func TestWithPreWrapper(t *testing.T) {
112+
wrapper := preWrapper{
113+
start: func(code bool, styleAttr string) string {
114+
return fmt.Sprintf("<foo%s id=\"code-%t\">", styleAttr, code)
115+
},
116+
end: func(code bool) string {
117+
return fmt.Sprintf("</foo>")
118+
},
119+
}
120+
121+
format := func(f *Formatter) string {
122+
it, err := lexers.Get("bash").Tokenise(nil, "echo FOO")
123+
assert.NoError(t, err)
124+
125+
var buf bytes.Buffer
126+
err = f.Format(&buf, styles.Fallback, it)
127+
assert.NoError(t, err)
128+
129+
return buf.String()
130+
}
131+
132+
t.Run("Regular", func(t *testing.T) {
133+
s := format(New(WithClasses()))
134+
assert.Equal(t, s, `<pre class="chroma"><span class="nb">echo</span> FOO</pre>`)
135+
})
136+
137+
t.Run("PreventSurroundingPre", func(t *testing.T) {
138+
s := format(New(PreventSurroundingPre(), WithClasses()))
139+
assert.Equal(t, s, `<span class="nb">echo</span> FOO`)
140+
})
141+
142+
t.Run("Wrapper", func(t *testing.T) {
143+
s := format(New(WithPreWrapper(wrapper), WithClasses()))
144+
assert.Equal(t, s, `<foo class="chroma" id="code-true"><span class="nb">echo</span> FOO</foo>`)
145+
})
146+
147+
t.Run("Wrapper, LineNumbersInTable", func(t *testing.T) {
148+
s := format(New(WithPreWrapper(wrapper), WithClasses(), WithLineNumbers(), LineNumbersInTable()))
149+
150+
assert.Equal(t, s, `<div class="chroma">
151+
<table class="lntable"><tr><td class="lntd">
152+
<foo class="chroma" id="code-false"><span class="lnt">1
153+
</span></foo></td>
154+
<td class="lntd">
155+
<foo class="chroma" id="code-true"><span class="nb">echo</span> FOO</foo></td></tr></table>
156+
</div>
157+
`)
158+
})
159+
}

0 commit comments

Comments
 (0)