160 lines
5.3 KiB
Scheme
160 lines
5.3 KiB
Scheme
;;; GNU Guix web site
|
|
;;; Copyright © 2016, 2017, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
|
|
;;; Copyright © 2023 Florian Pelz <pelzflorian@pelzflorian.de>
|
|
;;;
|
|
;;; This file is part of the GNU Guix web site.
|
|
;;;
|
|
;;; The GNU Guix web site is free software; you can redistribute it and/or modify it
|
|
;;; under the terms of the GNU Affero General Public License as published by
|
|
;;; the Free Software Foundation; either version 3 of the License, or (at
|
|
;;; your option) any later version.
|
|
;;;
|
|
;;; The GNU Guix web site is distributed in the hope that it will be useful, but
|
|
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;;; GNU Affero General Public License for more details.
|
|
;;;
|
|
;;; You should have received a copy of the GNU Affero General Public License
|
|
;;; along with the GNU Guix web site. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
(define-module (apps blog utils)
|
|
#:use-module (apps aux lists)
|
|
#:use-module (apps aux web)
|
|
#:use-module (apps i18n)
|
|
#:use-module (haunt post)
|
|
#:use-module (ice-9 match)
|
|
#:use-module (srfi srfi-1)
|
|
#:use-module (srfi srfi-19)
|
|
#:use-module (syntax-highlight)
|
|
#:use-module (syntax-highlight scheme)
|
|
#:use-module (syntax-highlight lexers)
|
|
#:export (post-groups->tag-list
|
|
post-url-path
|
|
posts/latest
|
|
syntax-highlight
|
|
change-image-to-video
|
|
tag-first?
|
|
tag-system-path
|
|
tag-url-path))
|
|
|
|
|
|
(define (post-groups->tag-list groups)
|
|
"Return a list of Haunt tags from the list of grouped posts.
|
|
|
|
GROUPS (association list)
|
|
An association list of tags mapped to posts, as returned by the
|
|
posts/group-by-tag procedure from (haunt post) module."
|
|
(cond ((null? groups) '())
|
|
(else
|
|
(cons (car (first groups))
|
|
(post-groups->tag-list (rest groups))))))
|
|
|
|
|
|
(define (post-url-path post)
|
|
"Return a URL path for the POST in the form blog/YYYY/POST-SLUG.
|
|
|
|
POST (<post>)
|
|
A post object as defined in (haunt post) module."
|
|
;; Note: End the path with a slash so 'localized-root-path' down the road
|
|
;; prepends the language tag.
|
|
(url-path-join "blog"
|
|
(date->string (post-date post) "~Y")
|
|
(post-slug post)
|
|
""))
|
|
|
|
|
|
(define (posts/latest posts n)
|
|
"Return the latest N posts from the given list of posts."
|
|
(let ((latest-posts (posts/reverse-chronological posts)))
|
|
(cond
|
|
((null? posts) '())
|
|
((<= (length posts) n) latest-posts)
|
|
(else (list-head latest-posts n)))))
|
|
|
|
|
|
(define (tag-first? tag-a tag-b)
|
|
"Return true if TAG-A goes first than TAG-B alphabetically.
|
|
|
|
This predicate is used for sorting tags.
|
|
|
|
TAG-A, TAG-B (string)
|
|
A tag as used by Haunt posts. For example: 'User interface'."
|
|
(string<? (string-downcase tag-a) (string-downcase tag-b)))
|
|
|
|
|
|
(define (tag-system-path tag)
|
|
"Return a system path for the TAG in the form blog/tags/TAG-SLUG.
|
|
|
|
The path is relative to the website directory.
|
|
|
|
TAG (string)
|
|
A tag as used by Haunt posts. For example: 'Scheme API'."
|
|
(string-append "blog/tags/" (slugify tag)))
|
|
|
|
|
|
(define (tag-url-path tag)
|
|
"Return a URL path for the TAG in the form blog/tags/TAG-SLUG.
|
|
|
|
TAG (string)
|
|
A tag as used by Haunt posts. For example: 'Scheme API'."
|
|
;; Note: End the path with a slash so 'localized-root-path' down the road
|
|
;; prepends the language tag.
|
|
(url-path-join "blog" "tags" (slugify tag) ""))
|
|
|
|
|
|
|
|
;;;
|
|
;;; Syntax highlighting.
|
|
;;;
|
|
|
|
(define %default-special-prefixes
|
|
'("define" "syntax"))
|
|
|
|
(define lex-scheme/guix
|
|
;; Specialized lexer for the Scheme we use in Guix.
|
|
;; TODO: Add #~, #$, etc.
|
|
(make-scheme-lexer (cons* "with-imported-modules"
|
|
"gexp" "ungexp"
|
|
"ungexp-native" "ungexp-splicing"
|
|
"ungexp-native-splicing"
|
|
"mlet" "mlet*"
|
|
"match"
|
|
%default-special-symbols)
|
|
%default-special-prefixes))
|
|
|
|
(define (syntax-highlight sxml)
|
|
"Recurse over SXML and syntax-highlight code snippets."
|
|
(match sxml
|
|
(('code ('@ ('class "language-scheme")) code-snippet)
|
|
`(code ,(highlights->sxml
|
|
(highlight lex-scheme/guix code-snippet))))
|
|
((tag ('@ attributes ...) body ...)
|
|
`(,tag (@ ,@attributes) ,@(map syntax-highlight body)))
|
|
((tag body ...)
|
|
`(,tag ,@(map syntax-highlight body)))
|
|
((? string? str)
|
|
str)))
|
|
|
|
(define (change-image-to-video sxml)
|
|
"Replace <img> tags in SXML that refer to WebM videos with proper <video>
|
|
tags. This hack allows one to refer to a video from a Markdown document."
|
|
(match sxml
|
|
(('img ('@ attributes ...) body ...)
|
|
(let ((src (match (assoc 'src attributes)
|
|
((_ url) url)))
|
|
(alt (match (assoc 'alt attributes)
|
|
((_ text) text))))
|
|
(if (string-suffix? ".webm" src)
|
|
`(video (@ (src ,src)
|
|
(poster ,(string-append src ".poster.png"))
|
|
(controls "controls"))
|
|
(p ,(G_ `(a (@ (href ,src) (class "link-subtle"))
|
|
"Download video."))))
|
|
sxml)))
|
|
((tag ('@ attributes ...) body ...)
|
|
`(,tag (@ ,@attributes) ,@(map change-image-to-video body)))
|
|
((tag body ...)
|
|
`(,tag ,@(map change-image-to-video body)))
|
|
((? string? str)
|
|
str)))
|