mirror of
https://github.com/alecthomas/chroma.git
synced 2025-03-31 22:05:17 +02:00
Make playground completely dynamic.
This commit is contained in:
parent
2332264124
commit
bdb587cd37
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -32,63 +33,91 @@ type context struct {
|
|||||||
Languages []string
|
Languages []string
|
||||||
SelectedStyle string
|
SelectedStyle string
|
||||||
Styles []string
|
Styles []string
|
||||||
Text string
|
|
||||||
HTML template.HTML
|
|
||||||
Error string
|
|
||||||
CSRFField template.HTML
|
CSRFField template.HTML
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler(w http.ResponseWriter, r *http.Request) {
|
func index(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := contextFromRequest(r)
|
ctx := newContext(r)
|
||||||
style := styles.Get(ctx.SelectedStyle)
|
err := htmlTemplate.Execute(w, &ctx)
|
||||||
if style == nil {
|
if err != nil {
|
||||||
style = styles.Fallback
|
panic(err)
|
||||||
}
|
}
|
||||||
ctx.Background = template.CSS(html.StyleEntryToCSS(style.Get(chroma.Background)))
|
}
|
||||||
|
|
||||||
language := lexers.Get(ctx.SelectedLanguage)
|
type renderRequest struct {
|
||||||
|
Language string `json:"language"`
|
||||||
|
Style string `json:"style"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type renderResponse struct {
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
HTML string `json:"html,omitempty"`
|
||||||
|
Language string `json:"language,omitempty"`
|
||||||
|
Background string `json:"background,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
if language == nil {
|
||||||
language = lexers.Analyse(ctx.Text)
|
language = lexers.Analyse(req.Text)
|
||||||
if language != nil {
|
if language != nil {
|
||||||
ctx.SelectedLanguage = language.Config().Name
|
req.Language = language.Config().Name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if language == nil {
|
if language == nil {
|
||||||
language = lexers.Fallback
|
language = lexers.Fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens, err := language.Tokenise(nil, ctx.Text)
|
tokens, err := language.Tokenise(nil, req.Text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error = err.Error()
|
return nil, err
|
||||||
} else {
|
|
||||||
buf := &strings.Builder{}
|
|
||||||
formatter := html.New()
|
|
||||||
err = formatter.Format(buf, style, tokens)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Error = err.Error()
|
|
||||||
} else {
|
|
||||||
ctx.HTML = template.HTML(buf.String()) // nolint: gosec
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = htmlTemplate.Execute(w, &ctx)
|
style := styles.Get(req.Style)
|
||||||
if err != nil {
|
if style == nil {
|
||||||
panic(err)
|
style = styles.Fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf := &strings.Builder{}
|
||||||
|
formatter := html.New()
|
||||||
|
err = formatter.Format(buf, style, tokens)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &renderResponse{
|
||||||
|
Language: language.Config().Name,
|
||||||
|
HTML: buf.String(),
|
||||||
|
Background: html.StyleEntryToCSS(style.Get(chroma.Background)),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func contextFromRequest(r *http.Request) context {
|
func newContext(r *http.Request) context {
|
||||||
err := r.ParseForm()
|
|
||||||
ctx := context{
|
ctx := context{
|
||||||
SelectedLanguage: r.Form.Get("language"),
|
SelectedStyle: "monokailight",
|
||||||
SelectedStyle: r.Form.Get("style"),
|
CSRFField: csrf.TemplateField(r),
|
||||||
Text: r.Form.Get("text"),
|
|
||||||
CSRFField: csrf.TemplateField(r),
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
style := styles.Get(ctx.SelectedStyle)
|
||||||
ctx.Error = err.Error()
|
if style == nil {
|
||||||
return ctx
|
style = styles.Fallback
|
||||||
}
|
}
|
||||||
|
ctx.Background = template.CSS(html.StyleEntryToCSS(style.Get(chroma.Background)))
|
||||||
if ctx.SelectedStyle == "" {
|
if ctx.SelectedStyle == "" {
|
||||||
ctx.SelectedStyle = "monokailight"
|
ctx.SelectedStyle = "monokailight"
|
||||||
}
|
}
|
||||||
@ -114,8 +143,9 @@ func main() {
|
|||||||
log.Println("Starting")
|
log.Println("Starting")
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
router.Handle("/", http.HandlerFunc(handler))
|
router.Handle("/", http.HandlerFunc(index)).Methods("GET")
|
||||||
router.Handle("/static/{file:.*}", http.StripPrefix("/static/", http.FileServer(staticFiles.HTTPBox())))
|
router.Handle("/api/render", http.HandlerFunc(renderHandler))
|
||||||
|
router.Handle("/static/{file:.*}", http.StripPrefix("/static/", http.FileServer(staticFiles.HTTPBox()))).Methods("GET")
|
||||||
|
|
||||||
options := []csrf.Option{}
|
options := []csrf.Option{}
|
||||||
if cli.CSRFKey == "" {
|
if cli.CSRFKey == "" {
|
||||||
|
@ -1,7 +1,73 @@
|
|||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
var style = document.createElement('style');
|
||||||
|
var ref = document.querySelector('script');
|
||||||
|
ref.parentNode.insertBefore(style, ref);
|
||||||
|
|
||||||
var form = document.getElementById('chroma');
|
var form = document.getElementById('chroma');
|
||||||
|
var textArea = form.elements["text"];
|
||||||
|
var csrfToken = form.elements["gorilla.csrf.Token"].value;
|
||||||
var elms = document.getElementsByTagName("select");
|
var elms = document.getElementsByTagName("select");
|
||||||
for (e of elms) {
|
var output = document.getElementById("output");
|
||||||
e.addEventListener('change', () => form.submit());
|
|
||||||
|
function debounce(func, wait, immediate) {
|
||||||
|
var timeout;
|
||||||
|
return function () {
|
||||||
|
var context = this, args = arguments;
|
||||||
|
var later = function () {
|
||||||
|
timeout = null;
|
||||||
|
if (!immediate) func.apply(context, args);
|
||||||
|
};
|
||||||
|
var callNow = immediate && !timeout;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(later, wait);
|
||||||
|
if (callNow) func.apply(context, args);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function getFormJSON() {
|
||||||
|
return {
|
||||||
|
"language": document.getElementById("language").value,
|
||||||
|
"style": document.getElementById("style").value,
|
||||||
|
"text": document.getElementById("text").value,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update(event) {
|
||||||
|
fetch("api/render", {
|
||||||
|
method: 'POST', // *GET, POST, PUT, DELETE, etc.
|
||||||
|
mode: 'cors', // no-cors, cors, *same-origin
|
||||||
|
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
|
||||||
|
credentials: 'same-origin', // include, *same-origin, omit
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-Token': csrfToken,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
// 'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
redirect: 'follow', // manual, *follow, error
|
||||||
|
referrer: 'no-referrer', // no-referrer, *client
|
||||||
|
body: JSON.stringify(getFormJSON()),
|
||||||
|
|
||||||
|
}).then(data => {
|
||||||
|
data.json().then(
|
||||||
|
value => {
|
||||||
|
style.innerHTML = "#output { " + value.background + "}";
|
||||||
|
output.innerHTML = value.html;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}).catch(reason => {
|
||||||
|
console.log(reason);
|
||||||
|
})
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventHandler = (event) => update(event);
|
||||||
|
for (e of elms) {
|
||||||
|
e.addEventListener('change', eventHandler);
|
||||||
|
}
|
||||||
|
form.addEventListener('submit', eventHandler);
|
||||||
|
|
||||||
|
var debouncedEventHandler = debounce(eventHandler, 250);
|
||||||
|
textArea.addEventListener('input', debouncedEventHandler);
|
||||||
|
textArea.addEventListener('change', debouncedEventHandler)
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#output {
|
#output {
|
||||||
{{.Background}}
|
{{.Background}}
|
||||||
}
|
}
|
||||||
|
|
||||||
#output pre {
|
#output pre {
|
||||||
@ -20,7 +20,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{{if .Error}}<div class="notification">{{.Error}}</div>{{end}}
|
<div class="notification is-hidden"></div>
|
||||||
|
|
||||||
<h1 class="title">Chroma Playground</h1>
|
<h1 class="title">Chroma Playground</h1>
|
||||||
|
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<label class="label">Language</label>
|
<label class="label">Language</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select name="language">
|
<select name="language" id="language">
|
||||||
<option value="" disabled{{if eq "" $.SelectedLanguage}} selected{{end}}>Language</option>
|
<option value="" disabled{{if eq "" $.SelectedLanguage}} selected{{end}}>Language</option>
|
||||||
{{- range .Languages}}
|
{{- range .Languages}}
|
||||||
<option value="{{.}}"{{if eq . $.SelectedLanguage}} selected{{end}}>{{.}}</option>
|
<option value="{{.}}"{{if eq . $.SelectedLanguage}} selected{{end}}>{{.}}</option>
|
||||||
@ -45,7 +45,7 @@
|
|||||||
<label class="label">Style</label>
|
<label class="label">Style</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<div class="select">
|
<div class="select">
|
||||||
<select name="style">
|
<select name="style" id="style">
|
||||||
<option value="" disabled{{if eq "" $.SelectedStyle}} selected{{end}}>Style</option>
|
<option value="" disabled{{if eq "" $.SelectedStyle}} selected{{end}}>Style</option>
|
||||||
{{- range .Styles}}
|
{{- range .Styles}}
|
||||||
<option value="{{.}}"{{if eq . $.SelectedStyle}} selected{{end}}>{{.}}</option>
|
<option value="{{.}}"{{if eq . $.SelectedStyle}} selected{{end}}>{{.}}</option>
|
||||||
@ -59,22 +59,20 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Code</label>
|
<label class="label">Code</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<textarea class="textarea" name="text" rows="25" cols="80">{{.Text}}</textarea>
|
<textarea class="textarea" id="text" name="text" rows="25" cols="80"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-link">Submit</button>
|
<button type="submit" class="button is-link">Submit</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<label class="label">Output</label>
|
<label class="label">Output</label>
|
||||||
<div class="field box" id="output">
|
<div class="field box" id="output"></div>
|
||||||
{{.HTML}}
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user