Commit 9b9a9229 authored by Dave Cooper's avatar Dave Cooper

Merge branch 'master' of into extra-impls

parents 340d1892 d24b1757
......@@ -8,10 +8,5 @@ auto-save-list
# Output files
\ No newline at end of file
# stage: build
# script: make
stage: build
script: make
- site-gen
stage: deploy
title: FAQ - E-mail on push
<!-- <div class="sideBox LHS">
<div>This Page</div>
<a href="#introduction">Introduction</a>
<a href="#news">News</a>
<a href="#description">What is it</a>
<a href="#services">Services</a>
<a href="#about">About this site</a>
</div> -->
<div class="sideBox RHS">
<div>Recent projects
<tr><td colspan="2">
<a href="projects.shtml">More projects...</a>
<tr><td colspan="2">
<a href="recent-projects.xml" class='rss-icon'>RSS</a>
<h2> FAQ: E-mail on push</h2>
......@@ -4,8 +4,8 @@ title: Frequently asked questions
<h1><i class="far fa-question-circle"></i>&nbsp;FAQ - Frequently Asked Questions</h1>
<div class="tocbox">
<h4 class="bg-primary" style="color:white;">Table of contents</h4>
<div class="table-of-contents">
<h1 class="bg-primary" style="color:white;">Table of contents</h1>
<li><a href="#faq-email-on-push">GitLab: configure e-mail on push</a></li>
......@@ -16,7 +16,7 @@ title: Frequently asked questions
GitLab supports sending an e-mail upon pushing
commits by project members. In order to use the functionality,
please follow the steps outlined in <a href="/faq/emailonpush/"
please follow the steps outlined in <a href="/faq/emailonpush"
>our configuration guide</a>.
title: Welcome to!
<main id="bodyText">
<a class="btn btn-primary float-right" href="/downloads"><i class="fa fa-arrow-right"></i>&nbsp;&nbsp;Get started</a>
......@@ -16,23 +18,22 @@ title: Welcome to!
<article id="news">
<h2>Latest Lisp news<i class="fa fa-rss float-right"></i></h2>
<div id="widgetmain" style="text-align:left;overflow-y:auto;overflow-x:hidden;width:331px;background-color:#transparent; border:1px solid #333333;padding: 8px;"><div id="rsswidget" style="height:300px;">
<script src="//"></script>
<!-- <a class="twitter-timeline" data-height="300" data-chrome="nofooter noborders noheader" href="">Tweets by planet_lisp</a> <script async src="" charset="utf-8"></script> -->
<div style="padding-top: 10px;"></div>
<script src="" type="text/javascript"></script>
<div id="clnet-news">
<h2 style="padding-top: 10px;"><a href="/news">Latest news</a></h2>
<h2><a href="/news">Latest news</a></h2>
{{> newsbox}}
<p><a href="/news" style="font-size: 80%">Read more...</a></p>
<p><a href="/news" style="font-size: 80%">Read more...</a></p>
<h2 style="padding-top: 10px;">Latest Lisp news</h2>
<div id="widgetmain" style="text-align:left;overflow-y:auto;overflow-x:hidden;width:331px;background-color:#transparent; border:1px solid #333333;padding: 8px;">
<div id="rsswidget" style="height:300px;">
<script src="//"></script>
<div style="padding-top: 10px;"></div>
<script src="" type="text/javascript"></script>
<article id="description">
......@@ -2,6 +2,39 @@ title: News
# Latest news
### 2018-10-26
As announced on October 12, empty projects have been removed (archived) from the system. After
further cleaning, the updated list consists of these 23 projects:
* cl-amqp
* cl-cactus-kev
* cl-captcha
* cl-gdbm
* clint
* cl-lazy-list
* cl-lexer
* clotnet
* cl-player
* cl-rundown
* cltl3
* decl
* defmud
* docutrack
* israel-lisp
* lisp-interface-library
* mk-defsystem
* movies
* objective-cl
* phorplay
* same
* trivial-features
* umpa-lumpa
### 2018-10-12 *Intent to remove unused projects*
As per [the announcement](,
......@@ -52,7 +52,6 @@ title: Projects
<a href="/project/chtml-matcher">chtml-matcher</a><br/>
<a href="/project/cinline">cinline</a><br/>
<a href="/project/cl+j">cl+j</a><br/>
<a href="/project/cl-amqp">cl-amqp</a><br/>
<a href="/project/cl-api">cl-api</a><br/>
<a href="/project/cl-applescript">cl-applescript</a><br/>
<a href="/project/cl-bench">cl-bench</a><br/>
......@@ -61,9 +60,7 @@ title: Projects
<a href="/project/cl-blog">cl-blog</a><br/>
<a href="/project/cl-buchberger">cl-buchberger</a><br/>
<a href="/project/cl-bzip2">cl-bzip2</a><br/>
<a href="/project/cl-cactus-kev">cl-cactus-kev</a><br/>
<a href="/project/cl-cairo2">cl-cairo2</a><br/>
<a href="/project/cl-captcha">cl-captcha</a><br/>
<a href="/project/cl-carbon">cl-carbon</a><br/>
<a href="/project/cl-cli-parser">cl-cli-parser</a><br/>
<a href="/project/cl-clickatell">cl-clickatell</a><br/>
......@@ -87,7 +84,6 @@ title: Projects
<a href="/project/cl-fltk">cl-fltk</a><br/>
<a href="/project/cl-fuse">cl-fuse</a><br/>
<a href="/project/cl-gd">cl-gd</a><br/>
<a href="/project/cl-gdbm">cl-gdbm</a><br/>
<a href="/project/cl-glpk">cl-glpk</a><br/>
<a href="/project/cl-godb">cl-godb</a><br/>
<a href="/project/cl-graph">cl-graph</a><br/>
......@@ -105,8 +101,6 @@ title: Projects
<a href="/project/cl-kanren-trs">cl-kanren-trs</a><br/>
<a href="/project/cl-l10n">cl-l10n</a><br/>
<a href="/project/cl-launchpad">cl-launchpad</a><br/>
<a href="/project/cl-lazy-list">cl-lazy-list</a><br/>
<a href="/project/cl-lexer">cl-lexer</a><br/>
<a href="/project/cl-librarian">cl-librarian</a><br/>
<a href="/project/cl-libtai">cl-libtai</a><br/>
<a href="/project/cl-machine-learning">cl-machine-learning</a><br/>
......@@ -144,7 +138,6 @@ title: Projects
<a href="/project/cl-quasi-quote">cl-quasi-quote</a><br/>
<a href="/project/cl-rdbms">cl-rdbms</a><br/>
<a href="/project/cl-rope">cl-rope</a><br/>
<a href="/project/cl-rundown">cl-rundown</a><br/>
<a href="/project/cl-sbml">cl-sbml</a><br/>
<a href="/project/cl-screen">cl-screen</a><br/>
<a href="/project/cl-selenium">cl-selenium</a><br/>
......@@ -206,16 +199,13 @@ title: Projects
<a href="/project/clonsigna">clonsigna</a><br/>
<a href="/project/closer">closer</a><br/>
<a href="/project/closure">closure</a><br/>
<a href="/project/clotnet">clotnet</a><br/>
<a href="/project/clouchdb">clouchdb</a><br/>
<a href="/project/cloud">cloud</a><br/>
<a href="/project/clpython">clpython</a><br/>
<a href="/project/clsql-fluid">clsql-fluid</a><br/>
<a href="/project/clsql-mysql-introspect">clsql-mysql-introspect</a><br/>
<a href="/project/cltl3">cltl3</a><br/>
<a href="/project/cmucl">cmucl</a><br/>
<a href="/project/common-lisp-beginner">common-lisp-beginner</a><br/>
<a href="/project/common-math">common-math</a><br/>
<a href="/project/commonqt">commonqt</a><br/>
<a href="/project/computed-class">computed-class</a><br/>
<a href="/project/core-services">core-services</a><br/>
......@@ -227,16 +217,13 @@ title: Projects
<a href="/project/de-setf-amqp">de-setf-amqp</a><br/>
<a href="/project/de-setf-graphics">de-setf-graphics</a><br/>
<a href="/project/de.setf.amqp">de.setf.amqp</a><br/>
<a href="/project/decl">decl</a><br/>
<a href="/project/defclass-star">defclass-star</a><br/>
<a href="/project/defdoc">defdoc</a><br/>
<a href="/project/defeditor">defeditor</a><br/>
<a href="/project/definer">definer</a><br/>
<a href="/project/defmud">defmud</a><br/>
<a href="/project/defplayer">defplayer</a><br/>
<a href="/project/defwm">defwm</a><br/>
<a href="/project/docudown">docudown</a><br/>
<a href="/project/docutrack">docutrack</a><br/>
<a href="/project/drakma">drakma</a><br/>
<a href="/project/dynamic-classes">dynamic-classes</a><br/>
<a href="/project/dyslexia">dyslexia</a><br/>
......@@ -290,7 +277,6 @@ title: Projects
<a href="/project/ip-interfaces">ip-interfaces</a><br/>
<a href="/project/isidorus">isidorus</a><br/>
<a href="/project/iso8601-date">iso8601-date</a><br/>
<a href="/project/israel-lisp">israel-lisp</a><br/>
<a href="/project/iterate">iterate</a><br/>
<a href="/project/iterate-clsql">iterate-clsql</a><br/>
<a href="/project/jess-parse">jess-parse</a><br/>
......@@ -306,7 +292,6 @@ title: Projects
<a href="/project/limp">limp</a><br/>
<a href="/project/lineal">lineal</a><br/>
<a href="/project/linedit">linedit</a><br/>
<a href="/project/lisp-interface-library">lisp-interface-library</a><br/>
<a href="/project/lisp-matrix">lisp-matrix</a><br/>
<a href="/project/lisp-on-lines">lisp-on-lines</a><br/>
<a href="/project/lisp-res-kit">lisp-res-kit</a><br/>
......@@ -320,7 +305,6 @@ title: Projects
<a href="/project/local-time">local-time</a><br/>
<a href="/project/log4cl">log4cl</a><br/>
<a href="/project/log5">log5</a><br/>
<a href="/project/lost+found">lost+found</a><br/>
<a href="/project/lw-vim-mode">lw-vim-mode</a><br/>
<a href="/project/macho">macho</a><br/>
<a href="/project/macondoolap">macondoolap</a><br/>
......@@ -335,14 +319,11 @@ title: Projects
<a href="/project/mirror">mirror</a><br/>
<a href="/project/misc-extensions">misc-extensions</a><br/>
<a href="/project/misrouted">misrouted</a><br/>
<a href="/project/mit-cadr">mit-cadr</a><br/>
<a href="/project/mk-defsystem">mk-defsystem</a><br/>
<a href="/project/mkcl">mkcl</a><br/>
<a href="/project/mod-lisp">mod-lisp</a><br/>
<a href="/project/modisc">modisc</a><br/>
<a href="/project/moptilities">moptilities</a><br/>
<a href="/project/morphologie">morphologie</a><br/>
<a href="/project/movies">movies</a><br/>
<a href="/project/movitz">movitz</a><br/>
<a href="/project/named-readtables">named-readtables</a><br/>
<a href="/project/names-and-paths">names-and-paths</a><br/>
......@@ -352,19 +333,16 @@ title: Projects
<a href="/project/noctool">noctool</a><br/>
<a href="/project/nrw-xmcl">nrw-xmcl</a><br/>
<a href="/project/nxtlisp">nxtlisp</a><br/>
<a href="/project/objective-cl">objective-cl</a><br/>
<a href="/project/oct">oct</a><br/>
<a href="/project/okra">okra</a><br/>
<a href="/project/openair">openair</a><br/>
<a href="/project/osicat">osicat</a><br/>
<a href="/project/pal">pal</a><br/>
<a href="/project/parenscript">parenscript</a><br/>
<a href="/project/parse-declarations">parse-declarations</a><br/>
<a href="/project/patty">patty</a><br/>
<a href="/project/pg">pg</a><br/>
<a href="/project/pg-introspect">pg-introspect</a><br/>
<a href="/project/phemlock">phemlock</a><br/>
<a href="/project/phorplay">phorplay</a><br/>
<a href="/project/plain-odbc">plain-odbc</a><br/>
<a href="/project/plexippus-xpath">plexippus-xpath</a><br/>
<a href="/project/ply">ply</a><br/>
......@@ -386,7 +364,6 @@ title: Projects
<a href="/project/rucksack">rucksack</a><br/>
<a href="/project/s-xml">s-xml</a><br/>
<a href="/project/s-xml-rpc">s-xml-rpc</a><br/>
<a href="/project/same">same</a><br/>
<a href="/project/sapaclisp">sapaclisp</a><br/>
<a href="/project/sb-simd">sb-simd</a><br/>
<a href="/project/sequence-iterators">sequence-iterators</a><br/>
......@@ -415,7 +392,6 @@ title: Projects
<a href="/project/tinaa">tinaa</a><br/>
<a href="/project/tioga">tioga</a><br/>
<a href="/project/trivial-backtrace">trivial-backtrace</a><br/>
<a href="/project/trivial-features">trivial-features</a><br/>
<a href="/project/trivial-freeimage">trivial-freeimage</a><br/>
<a href="/project/trivial-garbage">trivial-garbage</a><br/>
<a href="/project/trivial-gray-streams">trivial-gray-streams</a><br/>
......@@ -428,7 +404,6 @@ title: Projects
<a href="/project/ucs-sort">ucs-sort</a><br/>
<a href="/project/ucw">ucw</a><br/>
<a href="/project/ucw-extras">ucw-extras</a><br/>
<a href="/project/umpa-lumpa">umpa-lumpa</a><br/>
<a href="/project/unetwork">unetwork</a><br/>
<a href="/project/uri-template">uri-template</a><br/>
<a href="/project/usocket">usocket</a><br/>
......@@ -2,7 +2,7 @@ title: Project Hosting
<a style="clear:both;" class="btn btn-primary float-right" href="/phub"><i class="fa fa-arrow-right"></i>&nbsp;&nbsp; projects</a>
<div style="clear:both;margin-top: 20px;" class="tocbox">
<div style="clear:both;margin-top: 20px;" class="table-of-contents">
<h4 class="bg-primary" style="color:white;">Table of contents</h4>
<li><a href="#whatyouget">What you get</a></li>
;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
(in-package :cl-site)
; Makes pathname from prefix + page object
(defun make-path (prefix suffix &optional page)
"Constructs a pathname from `prefix` and `suffix`.
`suffix` can be one of
1. a context (an alist) with a ':content' element
2. a pathname
3. a string identifying a path
`prefix` can be one of
1. a pathname
2. a string identifying a path
(let ((path (if page (cdr (assoc :content suffix)) suffix)))
(if (pathnamep prefix)
(merge-pathnames path prefix)
(pathname (concatenate 'string
prefix path)))))
; Slurps file from a string
(defun file-to-string (filepath)
"Reads the file designated by pathname `filepath` into memory,
returning the string with the sequence read from the file"
(with-open-file (stream filepath)
(let ((data (make-string (file-length stream))))
(read-sequence data stream)
(defun file-to-list (path)
(with-open-file (f path :direction :input)
(loop for line = (read-line f nil)
while line
collect line)))
(defun header-lines (lines)
"Returns a list of lines (subset of `lines`)
which form the header of the document `lines` were read from
(loop for line in lines
thereis (when (string= line "---") header-lines)
collect line into header-lines))
(defun content-lines (lines)
"Returns a list of lines (subset of `lines`)
which remain after the header (if any) has been discarded
(loop with found-header-p = nil
for line in lines
when found-header-p
collect line into post-header-lines
when (string= line "---")
do (setf found-header-p t)
collect line into content-lines
finally (return (if found-header-p post-header-lines content-lines))))
(defun populate-pages ()
"Each page is an alist containing info to be sent to the template via the context."
(mapcar (lambda (p)
(list (cons :content (file-namestring p))
(list (cons :content (enough-namestring p *pages-dir*))
(cons :title (file-namestring p))))
;; At this point there could be duplicate, i.e. a .html and a .lisp file.
(directory (make-pathname :defaults *PAGES-DIR* :name :wild :type "html"))
(directory (make-pathname :defaults *PAGES-DIR* :name :wild :type "md"))
(directory (make-pathname :defaults *PAGES-DIR* :name :wild :type "lisp")))))
(directory (merge-pathnames #P"**/*.html" *pages-dir*))
(directory (merge-pathnames #P"**/*.md" *pages-dir*))
(directory (merge-pathnames #P"**/*.lisp" *pages-dir*)))))
(defvar *render-hooks* '()
"A list of hooks wrapping the rendering of the page.")
(defmacro def-render-hook (name (&rest args) &rest body)
"Registers a hook to wrap the rendering of the page.
Provides a `call-next-hook` function which dispatches
execution to the next wrapper and ultimately to the
page renderer.
The wrapper should return the value received from
a `call-next-hook` to its caller as it is the content
of the rendered page.
(let ((next-hooks-sym (gensym)))
(defun ,name (,@args ,next-hooks-sym)
(flet ((call-next-hook (content context)
(funcall (pop ,next-hooks-sym) content context ,next-hooks-sym)))
(push (function ,name) *render-hooks*))))
(defun render-page-with-hooks (content context)
(let ((hooks (reverse (cons #'(lambda (p c next-processors)
(process-page p c))
(funcall (pop hooks) content context hooks)))
(defun page-content (page) (rest (assoc :content page)))
......@@ -57,6 +125,11 @@
(cons (second plist)
(plist-values (rest (rest plist))))))
(defun alist-from-hash (hash)
(loop for k being each hash-key of hash
using (hash-value v)
collect (cons k v)))
(defmacro with-cl-who-string ((&rest args) &body body)
"Form. Sets up body to be evaluated with cl-who:with-html-output and return the resulting string instead
......@@ -5,21 +5,20 @@ body{
.main {
overflow: auto;
padding-top: 20px;
padding-bottom: 20px;
font-size: 0.9rem;
line-height: 1.2rem;
overflow: auto;
.footer {
position: absolute;
height: 70px;
padding-bottom: 8px;
padding-top: 5px;
padding-top: 5px;
.cl-navbar {
border-width: 0px;
......@@ -55,9 +55,6 @@
<li class="nav-item">
<a class="nav-link" href="/contribute">Contribute</a>
<li class="nav-item">
<a class="nav-link" href="/lisp-companies">Stories</a>
<li class="nav-item">
<a class="nav-link" href="/about">About</a>
......@@ -66,11 +63,17 @@
<main role="main" class="container main">
<div style="margin-left:auto;margin-right:auto;padding-bottom:120px;text-align:center">
<a class="btn btn-secondary" style="color: #29aa22;border-color: #29aa22" href="{{{rel-path}}}">
<i class="far fa-edit"></i>&nbsp;Edit this page
<footer class="footer border-top bg-light">
<div class="container">
<div class="container">
<div class="row">
<div class="col-md-6" style="font-style: italic;">
<div style="text-align:left;width:100%; margin-bottom: 0px;">
;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
(in-package :cl-site)
(defun make-site (&optional output-dir)
(let ((*output-dir* (or output-dir *output-dir*)))
(format t "Generating site in ~A.~%" *output-dir*)
(process-static *static-dir* *output-dir*)
(process-static (merge-pathnames #P"static/" *layout-dir*) *output-dir*)
(let ((pages (populate-pages)))
(preprocess-md-pages pages)
(preprocess-lisp-pages pages))
(process-pages (populate-pages))))
(defun check-deps-fn (dependencies)
#'(lambda (processed-pages)
(every (lambda (d)
(member d processed-pages :test #'string=))
(defun make-site (&optional (output-dir *output-dir*))
(let ((pages (populate-pages)))
(ensure-directories-exist output-dir)
(format t "Generating site in ~A.~%" output-dir)
(process-static *static-dir* output-dir)
(process-static (merge-pathnames #P"static/" *layout-dir*) output-dir)
(loop with processed-pages = '()
with delayed-pages = '()
for page in pages
for page-path = (make-path *pages-dir* page t)
for rel-page-path = (enough-namestring page-path *pages-dir*)
for page-lines = (file-to-list page-path)
for header = (yaml:parse (format nil "~{~A~%~}" (header-lines page-lines)))
for content = (format nil "~{~A~%~}" (content-lines page-lines))
;; order in the context is very important: for any key appearing twice
;; (e.g. the 'title' key), the first will be selected; by taking the header values
;; first, the default 'title' being the content filename, can be overridden in the page's metadata
for context = (append (when header (alist-from-hash header)) page)
for dependencies = (cdr (assoc "depends-on" context :test #'string=))
for satisfies-deps-fn = (check-deps-fn dependencies)
for satisfies-deps-p = (funcall satisfies-deps-fn processed-pages)
do (push (list satisfies-deps-fn content context rel-page-path) delayed-pages)
do (loop for resolved-deps-pages =
(loop for delay in delayed-pages
for satisfies-deps-p = (funcall (car delay) processed-pages)
when satisfies-deps-p
collect delay
and do (progn
(format t "Rendering ~a~%" (fourth delay))
(push (fourth delay) processed-pages)
(render-page-with-hooks (second delay) (third delay))))
while resolved-deps-pages
do (setf delayed-pages
(set-difference delayed-pages resolved-deps-pages :key #'fourth :test #'string=))))))
(defun make-clean (&optional (output-dir *output-dir*))
;;;; -*-Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
(in-package :cl-site)
(defun split-content-from-header (path)
"Returns two values; the header and the content"
(with-open-file (f path :direction :input)
(let ((header-lines
(loop for line = (read-line f nil)
unless line do (return-from split-content-from-header
(values nil lines))
when (string= line "---") return lines
collect line into lines)))
(loop for line = (read-line f nil)
unless line
return (values header-lines lines)
collect line into lines))))
;; Copies/renders static assets (imgs, etc)
(defun process-static (static-dir output-dir)
(let ((destination-root (merge-pathnames #P"static/" output-dir))
......@@ -45,75 +30,55 @@
(with-output-to-string (output-stream)
(render-template template context output-stream)))
(defun render-page (page template-path)
(let ((page-path (make-path *PAGES-DIR* page t))
(output-path (make-path *OUTPUT-DIR* page t)))
(header content)
(split-content-from-header page-path)
(let* ((header-text (format nil "~{~A~%~}" header))
(content-text (format nil "~{~A~%~}" content))
(parsed-header (when header (yaml:parse header-text)))
(when parsed-header
(loop for k being each hash-key of parsed-header
using (hash-value v)
collect (cons k v)))))
(with-open-file (output-stream output-path
:direction :output
:if-exists :supersede)
(render-template template-path
(append (acons :page-content (render-template* content-text) page-context) *GLOBAL-CONTEXT*)
(defun preprocess-md-pages (pages)
(format t "Preprocessing Markdown pages..~%")
(dolist (page pages)
(let* ((md-file (make-path *pages-dir* page t)))
(when (string= (pathname-type md-file) "md")
(let ((html-file (make-pathname :defaults md-file :type "html")))
(format t "Transforming ~A to ~A~%" md-file html-file)
(header content)
(split-content-from-header md-file)
(let ((md-content (with-output-to-string (out) (cl-markdown:markdown (format nil "~{~A~%~}" content) :stream out))))
(with-open-file (f html-file
:direction :output
:if-exists :supersede)
(when header
(format f "~{~A~%~}---~%" header))
(format f "~A~%" md-content)))))))))
(defun preprocess-lisp-pages (pages)
(format t "Preprocessing LHTML pages..~%")
(dolist (page pages)
(let* ((lisp-file (page-content page)))
(when (string-equal (pathname-type lisp-file) "lisp")
(let ((html-file (make-pathname :defaults lisp-file :type "html")))
(format t "Loading ~a and creating fresh ~a...~%"
(file-namestring lisp-file) html-file)
(let (*computed-page-content*)
(load (merge-pathnames lisp-file *pages-dir*))
(with-open-file (out (merge-pathnames html-file *pages-dir*) :if-exists :supersede
:if-does-not-exist :create :direction :output)
(let ((headers (getf *computed-page-content* :headers)))
(mapc #'(lambda(key value) (format out "~a: ~a~%" key value))
(plist-keys headers)(plist-values headers)))
(format out "---~%")
(write-string (getf *computed-page-content* :html-string) out))))))))