Syntax Highlighting in Haunt
I wrote a post just under two years ago about setting up this blog, and then I didn't write anything else! Shame on me.
Part of the issue that got me stuck is that I wanted syntax highlighting. The "standard" option for Haunt is to use guile-syntax-highlight, but it has a pretty minimal set of supported languages. I wasn't satisfied with that.
In the past I've used Pygments, but I wasn't sure how to get that into Haunt.
Well, it's been two years: I think it's finally time to solve this problem!
I had some trouble finding a way to call an external process, passing something to its stdin and reading something from its stdout. In the end, I came up with something like this:
(define (syntax-highlight-strings strings language)
(let* ((push (pipe))
(pull (pipe))
(pid (spawn "pygmentize" `("pygmentize" "-f" "html" ,language)
#:input (car push)
#:output (cdr pull))))
;; Write to the subprocess, then close the port
(close-port (car push))
(for-each (lambda (b) (display b (cdr push))) strings)
(close-port (cdr push))
;; Read the result back in, then close the port.
(close-port (cdr pull))
(let ((result (get-string-all (car pull))))
(close-port (car pull))
(waitpid pid)
result)))
This uses spawn to start a pygmentize process, writes a list of strings to it, then reads the result back.
We can then walk the whole tree to find the shape that the guile-commonmark reader emits for fenced code blocks with a language (which is <pre><code class="language-scheme">...</code></pre>).
(define (syntax-highlight sxml-tree)
(define (language-from-attrs attrs)
(let ((classname (assoc-ref attrs 'class)))
(and classname
(string-prefix? "language-" (car classname))
(substring (car classname) 9))))
(match sxml-tree
(('pre ('code ('@ . attrs) . body))
(match (html->shtml (syntax-highlight-strings body (language-from-attrs attrs)))
(('*TOP* element "\n")
`(div (@ ,@attrs) ,element))))
((tag ('@ . attrs) . bodies)
(cons* tag (cons '@ attrs) (map syntax-highlight bodies)))
((tag . bodies)
(cons* tag (map syntax-highlight bodies)))
(_ sxml-tree)))
Then it's just a matter of calling syntax-highlight on the post-sxml in our theme's #:post-template function.
(define my-theme
(theme #:name "My Made Up Theme"
#:post-template (lambda (post)
`((h1 (,post-title post))
(div ,(syntax-highlight (post-sxml post)))))))
This sets up the HTML necessary for colours, but to get actual colours you'll need to generate a stylesheet with the colours you want. The pygmentize command can do this. This will print the styles to stdout:
pygmentize -S default -f html
You can include these styles in your page (either in a linked .css file, or inline) and colours should appear.