diff --git a/main.go b/main.go index 6500a95..3f31f3c 100644 --- a/main.go +++ b/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 `
` + "\n" + w.String() + "\n" + `
` + } return `
` + "\n" + w.String() + "\n" + `
` } diff --git a/org/html_writer.go b/org/html_writer.go index 8a35e17..1f4bb9c 100644 --- a/org/html_writer.go +++ b/org/html_writer.go @@ -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("
\n
\n%s\n
\n
", html.EscapeString(source)) + } return fmt.Sprintf("
\n
\n%s\n
\n
", 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("
\n%s\n
\n", lang, content)) case name == "EXAMPLE": w.WriteString(`
` + "\n" + html.EscapeString(content) + "\n
\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("
\n%s\n
", 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) { diff --git a/org/inline.go b/org/inline.go index 4ba0a96..f0f90b4 100644 --- a/org/inline.go +++ b/org/inline.go @@ -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) } diff --git a/org/org_writer.go b/org/org_writer.go index d5a33ff..8bca476 100644 --- a/org/org_writer.go +++ b/org/org_writer.go @@ -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...) diff --git a/org/testdata/inline.html b/org/testdata/inline.html index 9801b36..3ba6474 100644 --- a/org/testdata/inline.html +++ b/org/testdata/inline.html @@ -43,6 +43,17 @@ links with slashes do not become emphasis: +
+
+<h1>hello</h1>
+
+
+ +

+ +
  • +

    multiline emphasis is supported - and respects MaxEmphasisNewLines (default: 1) so this diff --git a/org/testdata/inline.org b/org/testdata/inline.org index d967879..dfd2b01 100644 --- a/org/testdata/inline.org +++ b/org/testdata/inline.org @@ -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]{

    hello

    } - =multiline emphasis is supported - and respects MaxEmphasisNewLines (default: 1)= /so this diff --git a/org/testdata/inline.pretty_org b/org/testdata/inline.pretty_org index f1320bb..e1bd477 100644 --- a/org/testdata/inline.pretty_org +++ b/org/testdata/inline.pretty_org @@ -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]{

    hello

    } - =multiline emphasis is supported - and respects MaxEmphasisNewLines (default: 1)= /so this diff --git a/org/writer.go b/org/writer.go index 189d72e..c84ce9f 100644 --- a/org/writer.go +++ b/org/writer.go @@ -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: