@@ -354,6 +354,9 @@ func (r *renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.Wal
354
354
case * ast.HTMLBlock :
355
355
r .renderHTMLBlock (w , node )
356
356
357
+ case * ast.HTMLSpan :
358
+ r .renderHTMLSpan (node )
359
+
357
360
case * ast.CodeBlock :
358
361
r .renderCodeBlock (w , node )
359
362
@@ -367,9 +370,6 @@ func (r *renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.Wal
367
370
case * ast.Code :
368
371
r .inlineAccumulator .WriteString (BlueBgItalic (string (node .Literal )))
369
372
370
- case * ast.HTMLSpan :
371
- r .inlineAccumulator .WriteString (Red (string (node .Literal )))
372
-
373
373
case * ast.Table :
374
374
if entering {
375
375
r .table = newTableRenderer ()
@@ -502,6 +502,103 @@ func (r *renderer) renderFormattedCodeBlock(w io.Writer, code string) {
502
502
_ , _ = fmt .Fprintf (w , "\n \n " )
503
503
}
504
504
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
+
505
602
func (r * renderer ) renderHTMLBlock (w io.Writer , node * ast.HTMLBlock ) {
506
603
var buf bytes.Buffer
507
604
@@ -586,14 +683,18 @@ func (r *renderer) renderHTMLBlock(w io.Writer, node *ast.HTMLBlock) {
586
683
}
587
684
588
685
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
+ }
593
692
594
693
case "hr" :
595
- flushInline ()
596
- r .renderHorizontalRule (& buf )
694
+ if entering {
695
+ flushInline ()
696
+ r .renderHorizontalRule (& buf )
697
+ }
597
698
598
699
case "ul" , "ol" :
599
700
if ! entering {
@@ -727,14 +828,26 @@ func (r *renderer) renderHTMLBlock(w io.Writer, node *ast.HTMLBlock) {
727
828
r .inlineAccumulator .WriteString (crossedOutOff )
728
829
}
729
830
831
+ case "kbd" :
832
+ if entering {
833
+ r .inlineAccumulator .WriteString ("⦏" )
834
+ } else {
835
+ r .inlineAccumulator .WriteString ("⦎" )
836
+ }
837
+
730
838
default :
731
- r .inlineAccumulator .WriteString (Red (renderRawHtml (node )))
839
+ if entering {
840
+ r .inlineAccumulator .WriteString (Red (renderRawHtml (node )))
841
+ }
842
+ return htmlWalker .SkipChildren
732
843
}
733
844
734
845
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
+ }
738
851
739
852
default :
740
853
panic ("unhandled case" )
0 commit comments