Skip to content

Commit

Permalink
Add parent sibling variables (needed for indexes)
Browse files Browse the repository at this point in the history
  • Loading branch information
oaguy1 committed Jan 5, 2024
1 parent 2e0fcb7 commit ad3fb92
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 29 deletions.
89 changes: 62 additions & 27 deletions src/cl-yassg.lisp
Original file line number Diff line number Diff line change
@@ -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 ()
Expand All @@ -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 '()))

Expand All @@ -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"))
Expand All @@ -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 "/")
Expand All @@ -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))
2 changes: 1 addition & 1 deletion src/packages.lisp
Original file line number Diff line number Diff line change
@@ -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))
2 changes: 1 addition & 1 deletion todo.org
Original file line number Diff line number Diff line change
@@ -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)
Expand Down

0 comments on commit ad3fb92

Please sign in to comment.