Implement inline source blocks
https://orgmode.org/manual/Structure-of-Code-Blocks.html
This commit is contained in:
parent
6ed46ba95d
commit
3018ace8d0
8 changed files with 83 additions and 21 deletions
5
main.go
5
main.go
|
@ -46,7 +46,7 @@ func main() {
|
|||
fmt.Fprint(os.Stdout, out)
|
||||
}
|
||||
|
||||
func highlightCodeBlock(source, lang string) string {
|
||||
func highlightCodeBlock(source, lang string, inline bool) string {
|
||||
var w strings.Builder
|
||||
l := lexers.Get(lang)
|
||||
if l == nil {
|
||||
|
@ -55,5 +55,8 @@ func highlightCodeBlock(source, lang string) string {
|
|||
l = chroma.Coalesce(l)
|
||||
it, _ := l.Tokenise(nil, source)
|
||||
_ = html.New().Format(&w, styles.Get("friendly"), it)
|
||||
if inline {
|
||||
return `<div class="highlight-inline">` + "\n" + w.String() + "\n" + `</div>`
|
||||
}
|
||||
return `<div class="highlight">` + "\n" + w.String() + "\n" + `</div>`
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
// HTMLWriter exports an org document into a html document.
|
||||
type HTMLWriter struct {
|
||||
ExtendingWriter Writer
|
||||
HighlightCodeBlock func(source, lang string) string
|
||||
HighlightCodeBlock func(source, lang string, inline bool) string
|
||||
|
||||
strings.Builder
|
||||
document *Document
|
||||
|
@ -61,7 +61,10 @@ func NewHTMLWriter() *HTMLWriter {
|
|||
document: &Document{Configuration: defaultConfig},
|
||||
log: defaultConfig.Log,
|
||||
htmlEscape: true,
|
||||
HighlightCodeBlock: func(source, lang string) string {
|
||||
HighlightCodeBlock: func(source, lang string, inline bool) string {
|
||||
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\">\n<pre>\n%s\n</pre>\n</div>", html.EscapeString(source))
|
||||
},
|
||||
footnotes: &footnotes{
|
||||
|
@ -103,24 +106,14 @@ func (w *HTMLWriter) WriteComment(Comment) {}
|
|||
func (w *HTMLWriter) WritePropertyDrawer(PropertyDrawer) {}
|
||||
|
||||
func (w *HTMLWriter) WriteBlock(b Block) {
|
||||
content := ""
|
||||
if isRawTextBlock(b.Name) {
|
||||
builder, htmlEscape := w.Builder, w.htmlEscape
|
||||
w.Builder, w.htmlEscape = strings.Builder{}, false
|
||||
WriteNodes(w, b.Children...)
|
||||
out := w.String()
|
||||
w.Builder, w.htmlEscape = builder, htmlEscape
|
||||
content = strings.TrimRightFunc(out, unicode.IsSpace)
|
||||
} else {
|
||||
content = w.WriteNodesAsString(b.Children...)
|
||||
}
|
||||
content := w.blockContent(b.Name, b.Children)
|
||||
switch name := b.Name; {
|
||||
case name == "SRC":
|
||||
lang := "text"
|
||||
if len(b.Parameters) >= 1 {
|
||||
lang = strings.ToLower(b.Parameters[0])
|
||||
}
|
||||
content = w.HighlightCodeBlock(content, lang)
|
||||
content = w.HighlightCodeBlock(content, lang, false)
|
||||
w.WriteString(fmt.Sprintf("<div class=\"src src-%s\">\n%s\n</div>\n", lang, content))
|
||||
case name == "EXAMPLE":
|
||||
w.WriteString(`<pre class="example">` + "\n" + html.EscapeString(content) + "\n</pre>\n")
|
||||
|
@ -139,6 +132,12 @@ func (w *HTMLWriter) WriteBlock(b Block) {
|
|||
}
|
||||
}
|
||||
|
||||
func (w *HTMLWriter) WriteInlineBlock(b InlineBlock) {
|
||||
content := w.blockContent(b.Name, b.Children)
|
||||
lang := strings.ToLower(b.Parameters[0])
|
||||
w.WriteString(fmt.Sprintf("<div class=\"src src-inline src-%s\">\n%s\n</div>", lang, content))
|
||||
}
|
||||
|
||||
func (w *HTMLWriter) WriteDrawer(d Drawer) {
|
||||
WriteNodes(w, d.Children...)
|
||||
}
|
||||
|
@ -483,6 +482,19 @@ func (w *HTMLWriter) withHTMLAttributes(input string, kvs ...string) string {
|
|||
return out.String()
|
||||
}
|
||||
|
||||
func (w *HTMLWriter) blockContent(name string, children []Node) string {
|
||||
if isRawTextBlock(name) {
|
||||
builder, htmlEscape := w.Builder, w.htmlEscape
|
||||
w.Builder, w.htmlEscape = strings.Builder{}, false
|
||||
WriteNodes(w, children...)
|
||||
out := w.String()
|
||||
w.Builder, w.htmlEscape = builder, htmlEscape
|
||||
return strings.TrimRightFunc(out, unicode.IsSpace)
|
||||
} else {
|
||||
return w.WriteNodesAsString(children...)
|
||||
}
|
||||
}
|
||||
|
||||
func setHTMLAttribute(attributes []h.Attribute, k, v string) []h.Attribute {
|
||||
for i, a := range attributes {
|
||||
if strings.ToLower(a.Key) == strings.ToLower(k) {
|
||||
|
|
|
@ -30,6 +30,12 @@ type Emphasis struct {
|
|||
Content []Node
|
||||
}
|
||||
|
||||
type InlineBlock struct {
|
||||
Name string
|
||||
Parameters []string
|
||||
Children []Node
|
||||
}
|
||||
|
||||
type LatexFragment struct {
|
||||
OpeningPair string
|
||||
ClosingPair string
|
||||
|
@ -58,6 +64,7 @@ var timestampRegexp = regexp.MustCompile(`^<(\d{4}-\d{2}-\d{2})( [A-Za-z]+)?( \d
|
|||
var footnoteRegexp = regexp.MustCompile(`^\[fn:([\w-]*?)(:(.*?))?\]`)
|
||||
var statisticsTokenRegexp = regexp.MustCompile(`^\[(\d+/\d+|\d+%)\]`)
|
||||
var latexFragmentRegexp = regexp.MustCompile(`(?s)^\\begin{(\w+)}(.*)\\end{(\w+)}`)
|
||||
var inlineBlockRegexp = regexp.MustCompile(`src_(\w+)(\[(.*)\])?{(.*)}`)
|
||||
|
||||
var timestampFormat = "2006-01-02 Mon 15:04"
|
||||
var datestampFormat = "2006-01-02 Mon"
|
||||
|
@ -77,7 +84,7 @@ func (d *Document) parseInline(input string) (nodes []Node) {
|
|||
case '^':
|
||||
consumed, node = d.parseSubOrSuperScript(input, current)
|
||||
case '_':
|
||||
consumed, node = d.parseSubScriptOrEmphasis(input, current)
|
||||
rewind, consumed, node = d.parseSubScriptOrEmphasisOrInlineBlock(input, current)
|
||||
case '*', '/', '+':
|
||||
consumed, node = d.parseEmphasis(input, current, false)
|
||||
case '=', '~':
|
||||
|
@ -94,8 +101,8 @@ func (d *Document) parseInline(input string) (nodes []Node) {
|
|||
consumed, node = d.parseLineBreak(input, current)
|
||||
case ':':
|
||||
rewind, consumed, node = d.parseAutoLink(input, current)
|
||||
current -= rewind
|
||||
}
|
||||
current -= rewind
|
||||
if consumed != 0 {
|
||||
if current > previous {
|
||||
nodes = append(nodes, Text{input[previous:current], false})
|
||||
|
@ -144,6 +151,16 @@ func (d *Document) parseLineBreak(input string, start int) (int, Node) {
|
|||
return i - start, LineBreak{i - start}
|
||||
}
|
||||
|
||||
func (d *Document) parseInlineBlock(input string, start int) (int, int, Node) {
|
||||
if !(strings.HasSuffix(input[:start], "src") && (start-4 < 0 || unicode.IsSpace(rune(input[start-4])))) {
|
||||
return 0, 0, nil
|
||||
}
|
||||
if m := inlineBlockRegexp.FindStringSubmatch(input[start-4:]); m != nil {
|
||||
return 3, len(m[0]), InlineBlock{"src", strings.Fields(m[1] + " " + m[3]), d.parseRawInline(m[4])}
|
||||
}
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
func (d *Document) parseExplicitLineBreakOrLatexFragment(input string, start int) (int, Node) {
|
||||
switch {
|
||||
case start+2 >= len(input):
|
||||
|
@ -190,11 +207,14 @@ func (d *Document) parseSubOrSuperScript(input string, start int) (int, Node) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
func (d *Document) parseSubScriptOrEmphasis(input string, start int) (int, Node) {
|
||||
if consumed, node := d.parseSubOrSuperScript(input, start); consumed != 0 {
|
||||
return consumed, node
|
||||
func (d *Document) parseSubScriptOrEmphasisOrInlineBlock(input string, start int) (int, int, Node) {
|
||||
if rewind, consumed, node := d.parseInlineBlock(input, start); consumed != 0 {
|
||||
return rewind, consumed, node
|
||||
} else if consumed, node := d.parseSubOrSuperScript(input, start); consumed != 0 {
|
||||
return 0, consumed, node
|
||||
}
|
||||
return d.parseEmphasis(input, start, false)
|
||||
consumed, node := d.parseEmphasis(input, start, false)
|
||||
return 0, consumed, node
|
||||
}
|
||||
|
||||
func (d *Document) parseOpeningBracket(input string, start int) (int, Node) {
|
||||
|
@ -355,6 +375,7 @@ func (n LineBreak) String() string { return orgWriter.WriteNodesAsString
|
|||
func (n ExplicitLineBreak) String() string { return orgWriter.WriteNodesAsString(n) }
|
||||
func (n StatisticToken) String() string { return orgWriter.WriteNodesAsString(n) }
|
||||
func (n Emphasis) String() string { return orgWriter.WriteNodesAsString(n) }
|
||||
func (n InlineBlock) String() string { return orgWriter.WriteNodesAsString(n) }
|
||||
func (n LatexFragment) String() string { return orgWriter.WriteNodesAsString(n) }
|
||||
func (n FootnoteLink) String() string { return orgWriter.WriteNodesAsString(n) }
|
||||
func (n RegularLink) String() string { return orgWriter.WriteNodesAsString(n) }
|
||||
|
|
|
@ -97,6 +97,16 @@ func (w *OrgWriter) WriteBlock(b Block) {
|
|||
w.WriteString("#+END_" + b.Name + "\n")
|
||||
}
|
||||
|
||||
func (w *OrgWriter) WriteInlineBlock(b InlineBlock) {
|
||||
w.WriteString(b.Name + "_" + b.Parameters[0])
|
||||
if len(b.Parameters) > 1 {
|
||||
w.WriteString("[" + strings.Join(b.Parameters[1:], " ") + "]")
|
||||
}
|
||||
w.WriteString("{")
|
||||
WriteNodes(w, b.Children...)
|
||||
w.WriteString("}")
|
||||
}
|
||||
|
||||
func (w *OrgWriter) WriteDrawer(d Drawer) {
|
||||
w.WriteString(w.indent + ":" + d.Name + ":\n")
|
||||
WriteNodes(w, d.Children...)
|
||||
|
|
11
org/testdata/inline.html
vendored
11
org/testdata/inline.html
vendored
|
@ -43,6 +43,17 @@ links with slashes do not become <em>emphasis</em>: <a href="https://somelinksho
|
|||
</li>
|
||||
<li>
|
||||
<p>
|
||||
inline source blocks like <div class="src src-inline src-html">
|
||||
<div class="highlight-inline">
|
||||
<pre>
|
||||
<h1>hello</h1>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<code class="verbatim">multiline emphasis is
|
||||
supported - and respects MaxEmphasisNewLines (default: 1)</code>
|
||||
<em>so this
|
||||
|
|
1
org/testdata/inline.org
vendored
1
org/testdata/inline.org
vendored
|
@ -8,6 +8,7 @@
|
|||
- links with slashes do not become /emphasis/: [[https://somelinkshouldntrenderaccidentalemphasis.com]]/ /emphasis/
|
||||
- _underlined_ *bold* =verbatim= ~code~ +strikethrough+
|
||||
- *bold string with an *asterisk inside*
|
||||
- inline source blocks like src_html[:eval no]{<h1>hello</h1>}
|
||||
- =multiline emphasis is
|
||||
supported - and respects MaxEmphasisNewLines (default: 1)=
|
||||
/so this
|
||||
|
|
1
org/testdata/inline.pretty_org
vendored
1
org/testdata/inline.pretty_org
vendored
|
@ -8,6 +8,7 @@
|
|||
- links with slashes do not become /emphasis/: [[https://somelinkshouldntrenderaccidentalemphasis.com]]/ /emphasis/
|
||||
- _underlined_ *bold* =verbatim= ~code~ +strikethrough+
|
||||
- *bold string with an *asterisk inside*
|
||||
- inline source blocks like src_html[:eval no]{<h1>hello</h1>}
|
||||
- =multiline emphasis is
|
||||
supported - and respects MaxEmphasisNewLines (default: 1)=
|
||||
/so this
|
||||
|
|
|
@ -18,6 +18,7 @@ type Writer interface {
|
|||
WriteNodeWithName(NodeWithName)
|
||||
WriteHeadline(Headline)
|
||||
WriteBlock(Block)
|
||||
WriteInlineBlock(InlineBlock)
|
||||
WriteExample(Example)
|
||||
WriteDrawer(Drawer)
|
||||
WritePropertyDrawer(PropertyDrawer)
|
||||
|
@ -57,6 +58,8 @@ func WriteNodes(w Writer, nodes ...Node) {
|
|||
w.WriteHeadline(n)
|
||||
case Block:
|
||||
w.WriteBlock(n)
|
||||
case InlineBlock:
|
||||
w.WriteInlineBlock(n)
|
||||
case Example:
|
||||
w.WriteExample(n)
|
||||
case Drawer:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue