Skip to content

Commit b3d771a

Browse files
committed
WIP
1 parent acd6503 commit b3d771a

File tree

2 files changed

+132
-35
lines changed

2 files changed

+132
-35
lines changed

html/html.go

+6-22
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package html
22

3-
import . "golang.org/x/net/html"
4-
53
// WalkStatus allows NodeVisitor to have some control over the tree traversal.
64
// It is returned from NodeVisitor and different values allow Node.Walk to
75
// decide which node to go to next.
@@ -24,26 +22,14 @@ type NodeVisitor interface {
2422
}
2523

2624
func Walk(n *Node, visitor NodeVisitor) WalkStatus {
27-
isContainer := n.FirstChild != nil
28-
29-
// some special case that are container but can be self closing
30-
if n.Type == ElementNode {
31-
switch n.Data {
32-
case "td":
33-
isContainer = true
34-
}
35-
}
36-
3725
status := visitor.Visit(n, true)
3826

3927
if status == Terminate {
40-
// even if terminating, close container node
41-
if isContainer {
42-
visitor.Visit(n, false)
43-
}
28+
// even if terminating, close node
29+
visitor.Visit(n, false)
4430
}
4531

46-
if isContainer && status != SkipChildren {
32+
if status != SkipChildren {
4733
child := n.FirstChild
4834
for child != nil {
4935
status = Walk(child, visitor)
@@ -54,11 +40,9 @@ func Walk(n *Node, visitor NodeVisitor) WalkStatus {
5440
}
5541
}
5642

57-
if isContainer {
58-
status = visitor.Visit(n, false)
59-
if status == Terminate {
60-
return status
61-
}
43+
status = visitor.Visit(n, false)
44+
if status == Terminate {
45+
return status
6246
}
6347

6448
return GoToNext

renderer.go

+126-13
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,9 @@ func (r *renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.Wal
354354
case *ast.HTMLBlock:
355355
r.renderHTMLBlock(w, node)
356356

357+
case *ast.HTMLSpan:
358+
r.renderHTMLSpan(node)
359+
357360
case *ast.CodeBlock:
358361
r.renderCodeBlock(w, node)
359362

@@ -367,9 +370,6 @@ func (r *renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.Wal
367370
case *ast.Code:
368371
r.inlineAccumulator.WriteString(BlueBgItalic(string(node.Literal)))
369372

370-
case *ast.HTMLSpan:
371-
r.inlineAccumulator.WriteString(Red(string(node.Literal)))
372-
373373
case *ast.Table:
374374
if entering {
375375
r.table = newTableRenderer()
@@ -502,6 +502,103 @@ func (r *renderer) renderFormattedCodeBlock(w io.Writer, code string) {
502502
_, _ = fmt.Fprintf(w, "\n\n")
503503
}
504504

505+
func (r *renderer) renderHTMLSpan(node *ast.HTMLSpan) {
506+
doc, err := html.Parse(bytes.NewReader(node.Literal))
507+
if err != nil {
508+
// if there is a parsing error, fallback to a simple render
509+
r.inlineAccumulator.WriteString(Red(string(node.Literal)))
510+
return
511+
}
512+
513+
htmlWalker.WalkFunc(doc, func(node *html.Node, entering bool) htmlWalker.WalkStatus {
514+
switch node.Type {
515+
case html.CommentNode, html.DoctypeNode:
516+
// Not rendered
517+
518+
case html.DocumentNode:
519+
520+
case html.ElementNode:
521+
switch node.Data {
522+
case "html", "body":
523+
return htmlWalker.GoToNext
524+
525+
case "head":
526+
return htmlWalker.SkipChildren
527+
528+
case "a":
529+
if entering {
530+
r.inlineAccumulator.WriteString("[")
531+
} else {
532+
href, alt := getAHTMLAttr(node.Attr)
533+
r.inlineAccumulator.WriteString("](")
534+
r.inlineAccumulator.WriteString(Blue(href))
535+
if len(alt) > 0 {
536+
r.inlineAccumulator.WriteString(" ")
537+
r.inlineAccumulator.WriteString(alt)
538+
}
539+
r.inlineAccumulator.WriteString(")")
540+
}
541+
542+
case "strong", "b":
543+
if entering {
544+
r.inlineAccumulator.WriteString(boldOn)
545+
} else {
546+
// This is super silly but some terminals, instead of having
547+
// the ANSI code SGR 21 do "bold off" like the logic would guide,
548+
// do "double underline" instead. This is madness.
549+
550+
// To resolve that problem, we take a snapshot of the escape state,
551+
// remove the bold, then output "reset all" + snapshot
552+
es := text.EscapeState{}
553+
es.Witness(r.inlineAccumulator.String())
554+
es.Bold = false
555+
r.inlineAccumulator.WriteString(resetAll)
556+
r.inlineAccumulator.WriteString(es.String())
557+
}
558+
559+
case "i", "em":
560+
if entering {
561+
r.inlineAccumulator.WriteString(italicOn)
562+
} else {
563+
r.inlineAccumulator.WriteString(italicOff)
564+
}
565+
566+
case "s":
567+
if entering {
568+
r.inlineAccumulator.WriteString(crossedOutOn)
569+
} else {
570+
r.inlineAccumulator.WriteString(crossedOutOff)
571+
}
572+
573+
case "kbd":
574+
if entering {
575+
r.inlineAccumulator.WriteString("⦏")
576+
} else {
577+
r.inlineAccumulator.WriteString("⦎")
578+
}
579+
580+
default:
581+
if entering {
582+
r.inlineAccumulator.WriteString(Red(renderRawHtml(node)))
583+
}
584+
return htmlWalker.SkipChildren
585+
}
586+
587+
case html.TextNode:
588+
if entering {
589+
t := strings.TrimSpace(node.Data)
590+
t = strings.ReplaceAll(t, "\n", "")
591+
r.inlineAccumulator.WriteString(t)
592+
}
593+
594+
default:
595+
panic("unhandled case")
596+
}
597+
598+
return htmlWalker.GoToNext
599+
})
600+
}
601+
505602
func (r *renderer) renderHTMLBlock(w io.Writer, node *ast.HTMLBlock) {
506603
var buf bytes.Buffer
507604

@@ -586,14 +683,18 @@ func (r *renderer) renderHTMLBlock(w io.Writer, node *ast.HTMLBlock) {
586683
}
587684

588685
case "img":
589-
flushInline()
590-
src, title := getImgHTMLAttr(node.Attr)
591-
str, _ := r.renderImage(src, title, r.lineWidth-len(r.pad()))
592-
r.inlineAccumulator.WriteString(str)
686+
if entering {
687+
flushInline()
688+
src, title := getImgHTMLAttr(node.Attr)
689+
str, _ := r.renderImage(src, title, r.lineWidth-len(r.pad()))
690+
r.inlineAccumulator.WriteString(str)
691+
}
593692

594693
case "hr":
595-
flushInline()
596-
r.renderHorizontalRule(&buf)
694+
if entering {
695+
flushInline()
696+
r.renderHorizontalRule(&buf)
697+
}
597698

598699
case "ul", "ol":
599700
if !entering {
@@ -727,14 +828,26 @@ func (r *renderer) renderHTMLBlock(w io.Writer, node *ast.HTMLBlock) {
727828
r.inlineAccumulator.WriteString(crossedOutOff)
728829
}
729830

831+
case "kbd":
832+
if entering {
833+
r.inlineAccumulator.WriteString("⦏")
834+
} else {
835+
r.inlineAccumulator.WriteString("⦎")
836+
}
837+
730838
default:
731-
r.inlineAccumulator.WriteString(Red(renderRawHtml(node)))
839+
if entering {
840+
r.inlineAccumulator.WriteString(Red(renderRawHtml(node)))
841+
}
842+
return htmlWalker.SkipChildren
732843
}
733844

734845
case html.TextNode:
735-
t := strings.TrimSpace(node.Data)
736-
t = strings.ReplaceAll(t, "\n", "")
737-
r.inlineAccumulator.WriteString(t)
846+
if entering {
847+
t := strings.TrimSpace(node.Data)
848+
t = strings.ReplaceAll(t, "\n", "")
849+
r.inlineAccumulator.WriteString(t)
850+
}
738851

739852
default:
740853
panic("unhandled case")

0 commit comments

Comments
 (0)