1
0
mirror of https://github.com/alecthomas/chroma.git synced 2025-03-19 21:10:15 +02:00
chroma/cmd/chromad/main.go

173 lines
4.3 KiB
Go
Raw Normal View History

package main
import (
2019-07-16 18:45:54 +10:00
"encoding/json"
2019-07-16 22:19:43 +10:00
"fmt"
"html/template"
"log"
"net/http"
"sort"
"strings"
2019-07-16 15:45:06 +10:00
rice "github.com/GeertJohan/go.rice"
"github.com/alecthomas/kong"
2019-07-19 18:57:05 +10:00
konghcl "github.com/alecthomas/kong-hcl"
2019-07-16 16:05:37 +10:00
"github.com/gorilla/csrf"
2019-07-16 21:31:37 +10:00
"github.com/gorilla/handlers"
2019-07-16 15:45:06 +10:00
"github.com/gorilla/mux"
"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/formatters/html"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
)
2019-07-16 15:45:06 +10:00
var (
templateFiles = rice.MustFindBox("templates")
staticFiles = rice.MustFindBox("static")
2019-07-16 15:45:06 +10:00
htmlTemplate = template.Must(template.New("html").Parse(templateFiles.MustString("index.html.tmpl")))
)
type context struct {
Background template.CSS
SelectedLanguage string
Languages []string
SelectedStyle string
Styles []string
2019-07-16 16:05:37 +10:00
CSRFField template.HTML
2019-07-16 22:19:43 +10:00
Version string
}
2019-07-16 18:45:54 +10:00
func index(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r)
err := htmlTemplate.Execute(w, &ctx)
if err != nil {
panic(err)
}
2019-07-16 18:45:54 +10:00
}
type renderRequest struct {
Language string `json:"language"`
Style string `json:"style"`
Text string `json:"text"`
2019-07-16 21:31:37 +10:00
Classes bool `json:"classes"`
2019-07-16 18:45:54 +10:00
}
type renderResponse struct {
Error string `json:"error,omitempty"`
HTML string `json:"html,omitempty"`
Language string `json:"language,omitempty"`
Background string `json:"background,omitempty"`
}
2019-07-16 18:45:54 +10:00
func renderHandler(w http.ResponseWriter, r *http.Request) {
req := &renderRequest{}
err := json.NewDecoder(r.Body).Decode(&req)
var rep *renderResponse
if err != nil {
rep = &renderResponse{Error: err.Error()}
} else {
rep, err = render(req)
if err != nil {
rep = &renderResponse{Error: err.Error()}
}
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(rep)
}
func render(req *renderRequest) (*renderResponse, error) {
language := lexers.Get(req.Language)
if language == nil {
2019-07-16 18:45:54 +10:00
language = lexers.Analyse(req.Text)
if language != nil {
2019-07-16 18:45:54 +10:00
req.Language = language.Config().Name
}
}
if language == nil {
language = lexers.Fallback
}
2019-07-16 23:24:37 +10:00
tokens, err := chroma.Coalesce(language).Tokenise(nil, req.Text)
if err != nil {
2019-07-16 18:45:54 +10:00
return nil, err
}
2019-07-16 18:45:54 +10:00
style := styles.Get(req.Style)
if style == nil {
style = styles.Fallback
}
buf := &strings.Builder{}
2019-07-16 21:31:37 +10:00
options := []html.Option{}
if req.Classes {
2019-07-16 23:24:37 +10:00
options = append(options, html.WithClasses(), html.Standalone())
2019-07-16 21:31:37 +10:00
}
formatter := html.New(options...)
2019-07-16 18:45:54 +10:00
err = formatter.Format(buf, style, tokens)
if err != nil {
2019-07-16 18:45:54 +10:00
return nil, err
}
2019-07-16 21:31:37 +10:00
lang := language.Config().Name
if language == lexers.Fallback {
lang = ""
}
2019-07-16 18:45:54 +10:00
return &renderResponse{
2019-07-16 21:31:37 +10:00
Language: lang,
2019-07-16 18:45:54 +10:00
HTML: buf.String(),
Background: html.StyleEntryToCSS(style.Get(chroma.Background)),
}, nil
}
2019-07-16 18:45:54 +10:00
func newContext(r *http.Request) context {
ctx := context{
2019-07-16 18:45:54 +10:00
SelectedStyle: "monokailight",
CSRFField: csrf.TemplateField(r),
2019-07-16 22:19:43 +10:00
Version: fmt.Sprintf("%d", staticFiles.Time().Unix()),
}
2019-07-16 18:45:54 +10:00
style := styles.Get(ctx.SelectedStyle)
if style == nil {
style = styles.Fallback
}
2019-07-16 18:45:54 +10:00
ctx.Background = template.CSS(html.StyleEntryToCSS(style.Get(chroma.Background)))
if ctx.SelectedStyle == "" {
ctx.SelectedStyle = "monokailight"
}
for _, lexer := range lexers.Registry.Lexers {
ctx.Languages = append(ctx.Languages, lexer.Config().Name)
}
sort.Strings(ctx.Languages)
for _, style := range styles.Registry {
ctx.Styles = append(ctx.Styles, style.Name)
}
sort.Strings(ctx.Styles)
return ctx
}
func main() {
2019-07-16 16:05:37 +10:00
var cli struct {
Config kong.ConfigFlag `help:"Load configuration." placeholder:"FILE"`
Bind string `help:"HTTP bind address." default:"127.0.0.1:8080"`
CSRFKey string `help:"CSRF key." default:""`
}
ctx := kong.Parse(&cli, kong.Configuration(konghcl.Loader))
log.Println("Starting")
2019-07-16 15:45:06 +10:00
router := mux.NewRouter()
2019-07-16 18:45:54 +10:00
router.Handle("/", http.HandlerFunc(index)).Methods("GET")
2019-07-16 21:47:36 +10:00
router.Handle("/api/render", http.HandlerFunc(renderHandler)).Methods("POST")
2019-07-16 18:45:54 +10:00
router.Handle("/static/{file:.*}", http.StripPrefix("/static/", http.FileServer(staticFiles.HTTPBox()))).Methods("GET")
2019-07-16 15:45:06 +10:00
2019-07-16 16:05:37 +10:00
options := []csrf.Option{}
if cli.CSRFKey == "" {
options = append(options, csrf.Secure(false))
}
2019-07-16 21:31:37 +10:00
root := handlers.CORS()(csrf.Protect([]byte(cli.CSRFKey), options...)(router))
err := http.ListenAndServe(cli.Bind, root)
ctx.FatalIfErrorf(err)
}