From ad3fb921ddd69d85b9dba70c8edd4a25464f5ccd Mon Sep 17 00:00:00 2001 From: Lily Hughes-Robinson Date: Thu, 4 Jan 2024 22:31:22 -0500 Subject: [PATCH] Add parent sibling variables (needed for indexes) --- src/cl-yassg.lisp | 89 +++++++++++++++++++++++++++++++++-------------- src/packages.lisp | 2 +- todo.org | 2 +- 3 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/cl-yassg.lisp b/src/cl-yassg.lisp index 5e989f8..4b29eab 100644 --- a/src/cl-yassg.lisp +++ b/src/cl-yassg.lisp @@ -1,14 +1,16 @@ (in-package #:cl-yassg) +;; globals (defvar *excluded-dirs* '("assets" "templates" ".git")) (defvar *excluded-files* '()) - (defvar *templates* '()) (defun register-template (key func) + "Register a template function to a given key" (setf *templates* (acons key func *templates*))) (defun exclude-file (filename) + "Exclude files of a given name" (push filename *excluded-files*)) (defclass page-node () @@ -26,9 +28,11 @@ :accessor node-variables))) (defmethod node-path-name ((node page-node)) + "give the directory name of the current node" (first (last (pathname-directory (node-path node))))) -(defmethod parse-node-variables ((node page-node)) +(defmethod parse-node-variables ((node page-node) curr-path) + "parse a source file into variables, parse markdown into html and store as variable" (unless (node-is-file-p node) (return-from parse-node-variables '())) @@ -40,49 +44,75 @@ (with-open-file (stream (node-path node)) (do ((line (read-line stream nil) (read-line stream nil))) ((null line)) - (cond ((and (not parsing-metadata) (not parsed-metadata) (string= line "---")) + (cond ((and (not parsing-metadata) (not parsed-metadata) (string= line "---")) ;; first line (setf parsing-metadata t)) - ((and parsing-metadata (string= line "---")) + ((and parsing-metadata (string= line "---")) ;; second '---' (setf parsing-metadata nil) (setf parsed-metadata t)) - (parsing-metadata + (parsing-metadata ;; read metadata (multiple-value-bind (key value) (parse-metadata-line line) (setf vars (acons key value vars)))) - (t (setf body (concatenate 'string body line)))))) + (t (setf body (concatenate 'string body (format nil "~A~%" line))))))) ;; read markdown (setf vars (acons "body" body vars)) - (setf body-html (with-output-to-string (stream) - (parse-string-and-print-to-stream body stream))) + + (setf body-html (with-output-to-string (stream) ;; convert to html + (let ((3bmd-code-blocks:*code-blocks* t) + (3bmd-code-blocks:*renderer* :pygments)) + (parse-string-and-print-to-stream body stream)))) (setf vars (acons "body-html" body-html vars)) + + (let* ((filename (file-namestring (node-path node))) + (new-filename (concatenate 'string (subseq filename 0 (- (length filename) 3)) ".html")) + (link (merge-pathnames new-filename curr-path))) + (setf vars (acons "link" link vars))) vars)) -(defmethod parse-tree-variables ((node page-node)) - (let ((vars (parse-node-variables node)) - (leaf-vars '())) +(defmethod parse-tree-variables ((node page-node) curr-path) + "parse all the variables for a node and its children" + (let ((node-vars (parse-node-variables node curr-path)) ;; current node variables + (file-child-vars '()) ;; file children variables + (dir-child-vars '())) ;; dir children variables + + ;; iterate through children, appending variables to the approproate let form (dolist (child (node-children node)) (if (node-is-file-p child) - (setf leaf-vars (append leaf-vars (parse-tree-variables child))) - (let ((key "")) - (setf key (first (last (pathname-directory (node-path child))))) - (setf vars (acons key (parse-tree-variables child) vars))))) - (unless (null leaf-vars) - (let ((key "")) - (if (node-is-file-p node) - (setf key (file-namestring (node-path node))) - (setf key (first (last (pathname-directory (node-path node)))))) - (setf vars (acons key leaf-vars vars)))) - (setf (node-variables node) (remove-if #'null vars)))) - -(defmethod node-to-files ((node page-node) dst-dir) + (let ((key (file-namestring (node-path child)))) + (setf file-child-vars (acons key (parse-tree-variables child curr-path) file-child-vars))) + (let* ((key (node-path-name child)) + (new-path (ensure-trailing-slash (merge-pathnames key curr-path))) + (child-vars (parse-tree-variables child new-path))) + (setf node-vars (acons key child-vars node-vars) + dir-child-vars (acons key child-vars dir-child-vars))))) + + ;; if there are file children variables, attach them to current node + (unless (null file-child-vars) + (setf node-vars (append file-child-vars node-vars))) + + ;; if there are dir children variables, attach them to file siblings (allows for aggregate pages) + (unless (null dir-child-vars) + (dolist (child (node-children node)) + (when (node-is-file-p child) + (setf (node-variables child) (append (node-variables child) dir-child-vars))))) + + ;; set this nodes variables + (setf (node-variables node) (remove-if #'null node-vars)))) + + +(defmethod tree-to-files ((node page-node) dst-dir) + "convert node and its children into files using the registered templates" (let ((curr-dir (if (node-is-file-p node) (ensure-trailing-slash dst-dir) (ensure-trailing-slash (merge-pathnames (node-path-name node) dst-dir))))) + ;; ensure current directory exists (ensure-directories-exist curr-dir) + ;; convert all children (dolist (child (node-children node)) - (node-to-files child curr-dir)) + (tree-to-files child curr-dir)) + ;; if node is file, pass its variables into the appropriate template and write the result to a file (if (node-is-file-p node) (let* ((filename (file-namestring (node-path node))) (new-filename (concatenate 'string (subseq filename 0 (- (length filename) 3)) ".html")) @@ -96,10 +126,12 @@ (format stream "~A" (apply-template template-name (node-variables node)))))))) (defun parse-metadata-line (line) + "Turn a colon seperated string into a key value pair" (let ((data (split ":" line))) (values (car data) (string-trim '(#\Space #\Tab #\Newline) (cadr data))))) (defun ensure-trailing-slash (dir) + "Ensure directory has trailing slash, required for most directory operations" (let ((str (format nil "~A" dir))) (if (not (eql (char str (- (length str) 1)) #\/)) (concatenate 'string str "/") @@ -123,15 +155,18 @@ node)) (defun make-site (input-dir output-dir) + "Read and parse all the files in input directory, apply appropriate templates, output to output-dir" (let ((root-node (make-site-tree (ensure-trailing-slash input-dir)))) - (parse-tree-variables root-node) - (node-to-files root-node output-dir))) + (parse-tree-variables root-node "/") + (tree-to-files root-node output-dir))) (defun apply-key-args (function assoc-list) + "Apply an assoc list as key arguments to a given function" (let ((apply-list (loop for (key . value) in assoc-list collect (read-from-string (concatenate 'string ":" key)) collect value))) (apply function apply-list))) (defun apply-template (template vars) + "Apply variables to the templates with a given key" (apply-key-args (cdr (assoc template *templates* :test #'equal)) vars)) diff --git a/src/packages.lisp b/src/packages.lisp index 474c483..c1edebc 100644 --- a/src/packages.lisp +++ b/src/packages.lisp @@ -1,4 +1,4 @@ (defpackage #:cl-yassg - (:use #:cl #:uiop/filesystem #:3bmd #:str) + (:use #:cl #:uiop/filesystem #:3bmd #:3bmd-code-blocks #:str) (:shadow :html) (:export :register-template :exclude-file :make-site)) diff --git a/todo.org b/todo.org index 1ee05f9..7930aa5 100644 --- a/todo.org +++ b/todo.org @@ -1,5 +1,5 @@ * Tasks -** TODO Handle handing nested variables to sibling nodes (e.g. posts to index node) +** DONE Handle handing nested variables to parent sibling nodes (e.g. posts to index node) ** TODO Handle drafts ** TODO Handle assets ** TODO Handle auto-creating indexes (e.g. post lists)