nat/lib/styler/styler.go

227 lines
4.3 KiB
Go

package styler
import (
"bufio"
"bytes"
"fmt"
"log"
"os"
"os/exec"
"strings"
"unicode/utf8"
)
type Highlighter string
const (
HIGHLIGHTER_NONE Highlighter = "none"
HIGHLIGHTER_PYTHON Highlighter = "python"
HIGHLIGHTER_GO Highlighter = "go"
)
func (h Highlighter) Cmd() string {
switch h {
case HIGHLIGHTER_PYTHON:
return ".highlighers/highlighter-wrapper.py"
case HIGHLIGHTER_GO:
return ".highlighers/highligher.gobin"
case HIGHLIGHTER_NONE:
fallthrough
default:
return ""
}
}
type Styler struct {
listOfLangsFirst map[string]string
listOfLangsLast map[string]string
listOfStyles map[string]string
config StylerConfig
}
func (s *Styler) Legacy() [3]map[string]string {
return [3]map[string]string{
s.listOfLangsFirst,
s.listOfLangsLast,
s.listOfStyles,
}
}
type StylerConfig struct {
Highlighter Highlighter `json:"highlighter"` // The name of the highlighter.
}
func New(config ...StylerConfig) *Styler {
s := &Styler{
listOfLangsFirst: make(map[string]string),
listOfLangsLast: make(map[string]string),
listOfStyles: make(map[string]string),
}
// default settings
s.config.Highlighter = "none"
if len(config) > 0 {
s.config = config[0]
}
return s
}
func isBad(s string) bool {
for i := 0; i < len(s); i++ {
b := s[i]
if ('a' <= b && b <= 'z') ||
('A' <= b && b <= 'Z') ||
('0' <= b && b <= '9') ||
b == ' ' {
continue
} else {
return false
}
}
return true
}
func sanitize(s string) string {
if !utf8.ValidString(s) {
return s
}
if len(s) < 30 {
return s
}
if !isBad(s) {
return s
}
return ""
}
func (s *Styler) Highlight(paste string, lang string, style string) (string, string, string, string, error) {
var supported_lang, supported_styles bool
lang = sanitize(lang)
style = sanitize(style)
lang, supported_lang = s.listOfLangsFirst[lang]
if supported_lang {
lang = sanitize(lang)
} else {
lang = ""
}
style, supported_styles = s.listOfStyles[style]
if supported_styles {
style = sanitize(style)
} else {
style = ""
}
if lang == "" {
lang = "autodetect"
}
if !supported_lang && lang != "autodetect" {
lang = "text"
}
// Same with the styles,
if !supported_styles {
style = "autodetect"
}
switch s.config.Highlighter {
case HIGHLIGHTER_PYTHON:
if _, err := os.Stat(s.config.Highlighter.Cmd()); os.IsNotExist(err) {
return "", "", "", "", err
}
cmd := exec.Command(s.config.Highlighter.Cmd(), lang, style)
cmd.Stdin = strings.NewReader(paste)
var stdout bytes.Buffer
cmd.Stdout = &stdout
var stderr bytes.Buffer
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
return "", "", "", "", err
}
return stdout.String(), stderr.String(), lang, style, nil
case "none":
fallthrough
default:
return paste, "", lang, style, nil
}
}
func (s *Styler) loadSupportedStyles() {
switch s.config.Highlighter {
case HIGHLIGHTER_PYTHON:
arg := "getstyles"
out, err := exec.Command(s.config.Highlighter.Cmd(), arg).Output()
if err != nil {
log.Fatal(err)
}
// Loop lexers and add them to respectively map,
for _, line := range strings.Split(string(out), "\n") {
if line == "" {
continue
}
s.listOfStyles[line] = strings.Title(line)
}
case "none":
fallthrough
default:
return
}
}
func (st *Styler) loadSupportedLangs() error {
switch st.config.Highlighter {
case HIGHLIGHTER_PYTHON:
var prioLexers map[string]string
// Initialize maps,
prioLexers = make(map[string]string)
// Get prioritized lexers and put them in a separate map,
file, err := os.Open("static/prio-lexers")
if err != nil {
return err
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
prioLexers[scanner.Text()] = "1"
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
file.Close()
arg := "getlexers"
out, err := exec.Command(st.config.Highlighter.Cmd(), arg).Output()
if err != nil {
return fmt.Errorf("%w: %s", err, string(out))
}
// Loop lexers and add them to respectively map,
for _, line := range strings.Split(string(out), "\n") {
if line == "" {
continue
}
s := strings.Split(line, ";")
if len(s) != 2 {
os.Exit(1)
}
s[0] = strings.Title(s[0])
if prioLexers[s[0]] == "1" {
st.listOfLangsFirst[s[0]] = s[1]
} else {
st.listOfLangsLast[s[0]] = s[1]
}
}
return nil
case "none":
fallthrough
default:
return nil
}
}