diff options
| author | Jan Tuomi <jans.tuomi@gmail.com> | 2024-02-06 16:26:36 +0200 |
|---|---|---|
| committer | Jan Tuomi <jans.tuomi@gmail.com> | 2024-02-08 09:02:42 +0200 |
| commit | 4e322c636bbc3e8064342931fe604bc6ddbe5f4e (patch) | |
| tree | b75cb0bf167b8c194f9e5b62e0ce81c694156303 | |
| parent | 9e8ff330d473ff5b540bbb9c6e66be4ac05ede81 (diff) | |
Implement basic site generation from .md
| -rw-r--r-- | app.scm | 100 | ||||
| -rw-r--r-- | article.md | 42 | ||||
| -rw-r--r-- | index.html | 80 |
3 files changed, 222 insertions, 0 deletions
@@ -0,0 +1,100 @@ +(import (chicken base) + utf8 + spiffy + nrepl + srfi-18 + (chicken file) + (chicken io) + (chicken port) + (chicken format) + (chicken pretty-print) + lowdown + sxml-transforms + matchable + (chicken irregex) + shell + srfi-13 + intarweb + html-parser) + +(define (pipe x . fns) + (match fns + [() x] + [(fn . rest) (apply pipe (fn x) rest)])) + +;; Convenience macro (@ expr ...) that expands to (lambda (x) (expr ... x)) +(define-syntax @ + (syntax-rules () + ((_ fn-body expr ...) + (lambda (x) (fn-body expr ... x))))) + +(define (replace from to str) + (irregex-replace/all from str to)) + +(define (html-unescape html) + (pipe html + (@ replace """ "\"") + (@ replace "'" "'") + (@ replace "&" "&") + (@ replace "<" "<") + (@ replace ">" ">"))) + +(define (highlight ext code) + (define tmp-file-path (create-temporary-file ext)) + (with-output-to-file tmp-file-path + (lambda () (display code))) + (define result (capture ,(format "highlight ~A -O html -f" tmp-file-path))) + (delete-file tmp-file-path) + result) + +(define (replace-code-block content return) + (define irx (irregex "^%lang (\\S+)%\\n?" 's)) + (define lang-match + (irregex-search irx content)) + (if (not lang-match) + (return content)) + + (define lang + (irregex-match-substring lang-match 1)) + + (pipe content + (@ replace irx "") + (@ html-unescape) + (@ highlight lang) + (@ string-trim-both))) + +(define (convert-md-to-html filepath) + (with-output-to-string + (lambda () (markdown->html (open-input-file filepath))))) + +(define (highlight-code-blocks html) + (define (highlight-match m) + (format "<code>~A</code>" + (call/cc (@ replace-code-block (irregex-match-substring m 1))))) + + (irregex-replace/all (irregex "<code>(.*?)</code>" 's) html + highlight-match)) + +(define (insert-to-page-tmpl tmpl-path html) + (format (read-string #f (open-input-file tmpl-path)) + html)) + +(define (app c) + (define html-text + (pipe "article.md" + (@ convert-md-to-html) + (@ highlight-code-blocks) + (@ insert-to-page-tmpl "index.html"))) + + (send-response body: html-text + headers: '((content-type #(text/html ((charset . utf-8))))))) + +(thread-start! + (lambda () + (print "starting nrepl on port 1234") + (nrepl-prompt (lambda () (display "#;0> "))) + (nrepl 1234))) + +(vhost-map `((".*" . ,(lambda (c) (app c))))) + +(start-server) diff --git a/article.md b/article.md new file mode 100644 index 0000000..bc0d9db --- /dev/null +++ b/article.md @@ -0,0 +1,42 @@ +# Article title + +Some text + +[Link text](http://example.com) + +Paragraph with inline _Python code_: `%lang py% import foo`, wonder what this looks like. + +## Subtitle + + %lang c% + int main() { + return 0; + } + +### Let's try a Lisp + + %lang scm% + (define foo (+ 1 2 3 4)) + +### And Rust + + %lang rs% + const STARTING_MISSILES: i32 = 8; + const READY_AMOUNT: i32 = 2; + + fn main() { + let mut missiles: i32 = STARTING_MISSILES; + let ready: i32 = READY_AMOUNT; + println!("Firing {} of my {} missiles...", ready, missiles); + missiles = missiles - ready; + println!("{} missiles left", missiles); + } + +## client side jäsä (juuso commission) + + %lang js% + alert("beenis"); + +> This is a quote +> +> > Alas poor Yorick diff --git a/index.html b/index.html new file mode 100644 index 0000000..b1e4892 --- /dev/null +++ b/index.html @@ -0,0 +1,80 @@ +<!doctype html> +<html lang="en"> +<head> + <title>Test</title> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link href="https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;1,300&display=swap" rel="stylesheet"> + <style> + /* highlight theme: Kwrite Editor */ + body.hl { background-color:#e0eaee; } + pre.hl { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New',monospace; white-space: pre-wrap; } + .hl.num { color:#b07e00; } + .hl.esc { color:#ff00ff; } + .hl.sng { color:#bf0303; } + .hl.pps { color:#818100; } + .hl.slc { color:#838183; font-style:italic; } + .hl.com { color:#838183; font-style:italic; } + .hl.ppc { color:#008200; } + .hl.opt { color:#000000; } + .hl.ipl { color:#0057ae; } + .hl.lin { color:#555555; user-select: none; } + .hl.hvr { cursor:help; } + .hl.erm { color:#ff0000; font-weight:bold; border:solid 1px red; margin-left: 3em; } + .hl.err { color:#ff0000; font-weight:bold; } + .hl.kwa { color:#000000; font-weight:bold; } + .hl.kwb { color:#0057ae; } + .hl.kwc { color:#000000; } + .hl.kwd { color:#010181; } + .hl.kwe { color:#0d5bc3; } + .hl.kwf { color:#750dc3; } + + body { + font-size: 20px; + margin: 20px auto; + padding: 10px 15px; + max-width: 800px; + + font-family: "Merriweather", serif; + font-optical-sizing: auto; + font-weight: 400; + font-style: normal; + + line-height: 35px; + } + + code { + padding: 8px 10px; + background-color: #eee; + } + + pre { + background-color: #eee; + padding: 8px 10px; + overflow-x: scroll; + } + pre code { + background-color: initial; + padding: 0; + } + + a { + color: #0090a0; + } + + blockquote { + display: block; + background-color: #0090a020; + background-opacity: 0.9; + border-left: 10px solid #0090a0; + margin: 15px 0; + padding: 2px 10px; + quotes: "\201C""\201D""\2018""\2019"; + } + </style> +</head> +<body> + ~A +</body> +</html> |
