github.com/xiaq/elvish@v0.12.0/website/highlighter.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"html"
     8  	"log"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/elves/elvish/edit/highlight"
    13  	"github.com/elves/elvish/parse"
    14  )
    15  
    16  var (
    17  	scanner = bufio.NewScanner(os.Stdin)
    18  	lineno  = 0
    19  )
    20  
    21  func scan() bool {
    22  	lineno++
    23  	return scanner.Scan()
    24  }
    25  
    26  func main() {
    27  	for scan() {
    28  		line := scanner.Text()
    29  		trimmed := strings.TrimLeft(line, " ")
    30  		indent := line[:len(line)-len(trimmed)]
    31  		if trimmed == "```elvish" || trimmed == "```elvish-bad" {
    32  			bad := trimmed == "```elvish-bad"
    33  			highlighted := convert(collectFenced(indent), bad)
    34  			fmt.Printf("%s<pre><code>%s</code></pre>\n", indent, highlighted)
    35  		} else if trimmed == "```elvish-transcript" {
    36  			highlighted := convertTranscript(collectFenced(indent))
    37  			fmt.Printf("%s<pre><code>%s</code></pre>\n", indent, highlighted)
    38  		} else {
    39  			fmt.Println(line)
    40  		}
    41  	}
    42  }
    43  
    44  func collectFenced(indent string) string {
    45  	var buf bytes.Buffer
    46  	for scan() {
    47  		line := scanner.Text()
    48  		if !strings.HasPrefix(line, indent) {
    49  			log.Fatalf("bad indent of line %d: %q", lineno, line)
    50  		}
    51  		unindented := line[len(indent):]
    52  		if unindented == "```" {
    53  			break
    54  		}
    55  		if buf.Len() > 0 {
    56  			buf.WriteRune('\n')
    57  		}
    58  		buf.WriteString(unindented)
    59  	}
    60  	return buf.String()
    61  }
    62  
    63  const (
    64  	ps1 = "~> "
    65  	ps2 = "   "
    66  )
    67  
    68  func convertTranscript(transcript string) string {
    69  	scanner := bufio.NewScanner(bytes.NewBufferString(transcript))
    70  	var buf bytes.Buffer
    71  
    72  	overread := false
    73  	var line string
    74  	for overread || scanner.Scan() {
    75  		if overread {
    76  			overread = false
    77  		} else {
    78  			line = scanner.Text()
    79  		}
    80  		if strings.HasPrefix(line, ps1) {
    81  			elvishBuf := bytes.NewBufferString(line[len(ps1):] + "\n")
    82  			for scanner.Scan() {
    83  				line = scanner.Text()
    84  				if strings.HasPrefix(line, ps2) {
    85  					elvishBuf.WriteString(line + "\n")
    86  				} else {
    87  					overread = true
    88  					break
    89  				}
    90  			}
    91  			buf.WriteString(html.EscapeString(ps1))
    92  			buf.WriteString(convert(elvishBuf.String(), false))
    93  		} else {
    94  			buf.WriteString(html.EscapeString(line))
    95  			buf.WriteString("<br>")
    96  		}
    97  	}
    98  	return buf.String()
    99  }
   100  
   101  func convert(text string, bad bool) string {
   102  	n, err := parse.Parse("highlight", text)
   103  	if err != nil && !bad {
   104  		log.Printf("parsing %q: %v", text, err)
   105  	}
   106  
   107  	styling := highlight.Styling{}
   108  
   109  	e := highlight.Emitter{
   110  		func(s string) bool {
   111  			return true
   112  		},
   113  		styling.Add,
   114  	}
   115  	e.EmitAll(n)
   116  
   117  	var buf bytes.Buffer
   118  
   119  	openedSpan := false
   120  	currentStyle := ""
   121  
   122  	applier := styling.Apply()
   123  	for i, r := range text {
   124  		applier.At(i)
   125  		style := applier.Get()
   126  		if style != currentStyle {
   127  			if openedSpan {
   128  				buf.WriteString("</span>")
   129  			}
   130  			var classes []string
   131  			for _, c := range strings.Split(style, ";") {
   132  				if c != "" {
   133  					classes = append(classes, "sgr-"+c)
   134  				}
   135  			}
   136  			fmt.Fprintf(&buf, `<span class="%s">`, strings.Join(classes, " "))
   137  			openedSpan = true
   138  			currentStyle = style
   139  		}
   140  		if r == '\n' {
   141  			buf.WriteString("<br>")
   142  		} else {
   143  			buf.WriteString(html.EscapeString(string(r)))
   144  		}
   145  	}
   146  	if openedSpan {
   147  		buf.WriteString("</span>")
   148  	}
   149  	return buf.String()
   150  }