diff options
-rw-r--r-- | ennum-html.el (renamed from ennu-html.el) | 110 | ||||
-rw-r--r-- | ennum-image.el (renamed from ennu-image.el) | 20 | ||||
-rw-r--r-- | ennum.el (renamed from ennu.el) | 336 |
3 files changed, 238 insertions, 228 deletions
diff --git a/ennu-html.el b/ennum-html.el index 10025bc..1475927 100644 --- a/ennu-html.el +++ b/ennum-html.el @@ -4,7 +4,7 @@ (require 'subr-x) (require 'xmlgen) -(defconst ennu--iso-639-1-alist +(defconst ennum--iso-639-1-alist '(("ab" . "аҧсуа бызшәа, аҧсшәа") ("aa" . "Afaraf") ("af" . "Afrikaans") @@ -193,16 +193,16 @@ (defun expand-file-name* (name default-directory) (expand-file-name name (concat "/" default-directory))) -(org-export-define-derived-backend 'ennu-html 'html +(org-export-define-derived-backend 'ennum-html 'html :translate-alist - '((inner-template . ennu-html-inner-template) - (link . ennu-html-link)) + '((inner-template . ennum-html-inner-template) + (link . ennum-html-link)) :options-alist '((:summary "SUMMARY" nil nil parse) (:thumbnail "THUMBNAIL" nil nil t) (:translation-group "TRANSLATION_GROUP" nil nil t))) -(defun ennu-html-inner-template (contents info) +(defun ennum-html-inner-template (contents info) (concat ;; Table of contents (let ((depth (plist-get info :with-toc))) @@ -222,7 +222,7 @@ ,@(when author `(" by " (a :class "p-author h-card" - :href ,(ennu--absolute-uri "") + :href ,(ennum--absolute-uri "") ,(car (plist-get info :author))))) ,@(when date `(" on " @@ -234,12 +234,12 @@ (format "<p>In other languages: %s</p>" (mapconcat (lambda (translation) - (let ((lang (ennu-post-language translation)) - (slug (ennu-post-slug translation))) + (let ((lang (ennum-post-language translation)) + (slug (ennum-post-slug translation))) (replace-regexp-in-string "<a " (format "<a hreflang=\"%s\" " lang) - (ennu-export-post slug - (map-elt ennu--iso-639-1-alist lang nil 'string=) + (ennum-export-post slug + (map-elt ennum--iso-639-1-alist lang nil 'string=) (org-export-backend-name (plist-get info :back-end)))))) translations @@ -251,7 +251,7 @@ (lambda (tag) (replace-regexp-in-string "<a " "<a class=\"p-category\" " - (ennu-export-tag (ennu-add-tongue-suffix tag (plist-get info :language)) + (ennum-export-tag (ennum-add-tongue-suffix tag (plist-get info :language)) tag (org-export-backend-name (plist-get info :back-end))))) tags ", "))) @@ -264,7 +264,7 @@ (org-html-footnote-section info) "</article>")) -(defun ennu-html-link (link desc info) +(defun ennum-html-link (link desc info) ;; We override the html link transcoder to handle image links ;; differently. We cannot use the `:export' property of ;; `org-link-parameters' since those functions cannot access the @@ -278,9 +278,9 @@ ;; larger image as specified by the :image-link-width setting. (format "<a href=\"%s\">%s</a>" (expand-file-name* - (ennu-image-output-filename - path (ennu-setting :image-link-width)) - (ennu-setting :images-directory)) + (ennum-image-output-filename + path (ennum-setting :image-link-width)) + (ennum-setting :images-directory)) (replace-regexp-in-string (rx (group (or "src" "data")) "=\"file://") "\\1=\"" (org-html-link @@ -288,96 +288,96 @@ (org-element-put-property link :path (url-encode-url (expand-file-name* - (ennu-image-output-filename - path (ennu-setting :default-image-width)) - (ennu-setting :images-directory)))) + (ennum-image-output-filename + path (ennum-setting :default-image-width)) + (ennum-setting :images-directory)))) :type "file") desc info)))) ;; Pass other link types to org-html-link (_ (org-html-link link desc info))))) -(defmacro ennu-follow (path) - `(ennu-with-current-directory (ennu-setting :working-directory) +(defmacro ennum-follow (path) + `(ennum-with-current-directory (ennum-setting :working-directory) (find-file ,path))) ;; TODO: Pass title through org-export-data-with-backend or something ;; similar in order to export org syntax in title -(defun ennu-export-post (path desc backend) +(defun ennum-export-post (path desc backend) (pcase backend - ((or 'ennu-html 'html) - (let ((filename (concat (expand-file-name path (ennu-setting :posts-directory)) + ((or 'ennum-html 'html) + (let ((filename (concat (expand-file-name path (ennum-setting :posts-directory)) ".org"))) (xmlgen `(a :href ,(url-encode-url - (expand-file-name* path (ennu-setting :posts-directory))) - ,(or desc (ennu-post-title (ennu-read-post filename))))))))) + (expand-file-name* path (ennum-setting :posts-directory))) + ,(or desc (ennum-post-title (ennum-read-post filename))))))))) -(defun ennu-follow-post (path) - (ennu-follow (expand-file-name (concat path ".org") - (ennu-setting :posts-directory)))) +(defun ennum-follow-post (path) + (ennum-follow (expand-file-name (concat path ".org") + (ennum-setting :posts-directory)))) (org-link-set-parameters "post" - :export 'ennu-export-post - :follow 'ennu-follow-post) + :export 'ennum-export-post + :follow 'ennum-follow-post) -(defun ennu-follow-image (path) - (ennu-follow (expand-file-name path (ennu-setting :images-directory)))) +(defun ennum-follow-image (path) + (ennum-follow (expand-file-name path (ennum-setting :images-directory)))) (org-link-set-parameters - "image" :follow 'ennu-follow-image) + "image" :follow 'ennum-follow-image) -(defun ennu-export-thumbnail (path desc backend) +(defun ennum-export-thumbnail (path desc backend) (pcase backend - ((or 'ennu-html 'html) + ((or 'ennum-html 'html) (xmlgen `(img :src ,(url-encode-url (expand-file-name* - (ennu-image-output-filename - path (ennu-setting :thumbnail-image-width)) - (ennu-setting :images-directory)))))))) + (ennum-image-output-filename + path (ennum-setting :thumbnail-image-width)) + (ennum-setting :images-directory)))))))) (org-link-set-parameters "thumbnail" - :export 'ennu-export-thumbnail - :follow 'ennu-follow-image) + :export 'ennum-export-thumbnail + :follow 'ennum-follow-image) -(defun ennu-export-video (path desc backend) +(defun ennum-export-video (path desc backend) (pcase backend - ((or 'ennu-html 'html) - (let ((video-directory (ennu-setting :video-directory))) + ((or 'ennum-html 'html) + (let ((video-directory (ennum-setting :video-directory))) (xmlgen `(video :src ,(url-encode-url (expand-file-name* path video-directory)) :poster ,(url-encode-url - (expand-file-name* (ennu-video-poster path) video-directory)) + (expand-file-name* (ennum-video-poster path) video-directory)) :preload "none" :controls "")))))) (org-link-set-parameters - "video" :export 'ennu-export-video) + "video" :export 'ennum-export-video) -(defun ennu-export-static (path desc backend) +(defun ennum-export-static (path desc backend) (pcase backend - ((or 'ennu-html 'html) + ((or 'ennum-html 'html) (xmlgen `(a :href ,(url-encode-url - (expand-file-name* path (ennu-setting :static-directory))) + (expand-file-name* path (ennum-setting :static-directory))) ,desc))))) (org-link-set-parameters - "static" :export 'ennu-export-static) + "static" :export 'ennum-export-static) (org-link-set-parameters - "tangle" :export 'ennu-export-static) + "tangle" :export 'ennum-export-static) -(defun ennu-export-tag (tag desc backend) +(defun ennum-export-tag (tag desc backend) (pcase backend - ((or 'ennu-html 'html) + ((or 'ennum-html 'html) (xmlgen `(a :href ,(url-encode-url - (expand-file-name* tag (ennu-setting :tag-directory))) + (expand-file-name* tag (ennum-setting :tag-directory))) ,(or desc tag)))))) (org-link-set-parameters - "tag" :export 'ennu-export-tag) + "tag" :export 'ennum-export-tag) -(provide 'ennu-html) +(provide 'ennum-html) diff --git a/ennu-image.el b/ennum-image.el index 34c3e7e..3a8aa23 100644 --- a/ennu-image.el +++ b/ennum-image.el @@ -6,18 +6,18 @@ ;; Check if all necessary image types are supported (seq-do (lambda (image-type) (unless (image-type-available-p image-type) - (lwarn '(ennu) :error "`%s' image type not supported" image-type))) + (lwarn '(ennum) :error "`%s' image type not supported" image-type))) '(jpeg png svg)) ;; Check for existence of external image processing utilities (seq-do (lambda (external-program) (unless (executable-find external-program) - (lwarn '(ennu) :error "`%s' not found" external-program))) + (lwarn '(ennum) :error "`%s' not found" external-program))) '("convert" "identify" "jpegtran" "optipng")) -(defun ennu-image-resize-image (infile-path outfile-path width) +(defun ennum-image-resize-image (infile-path outfile-path width) "A simple shell wrapper around ImageMagick's convert" - (ennu-image--assert-file-exists infile-path) + (ennum-image--assert-file-exists infile-path) (cl-case (image-type infile-path) (svg (copy-file infile-path outfile-path t)) @@ -26,9 +26,9 @@ infile-path "-resize" (format "%d>" width) outfile-path))) outfile-path) -(defun ennu-image-optimize-image (image-path) +(defun ennum-image-optimize-image (image-path) "A simple shell wrapper around jpegtran and optipng" - (ennu-image--assert-file-exists image-path) + (ennum-image--assert-file-exists image-path) (cl-case (image-type image-path) (jpeg (call-process "jpegtran" nil nil nil "-optimize" @@ -38,8 +38,8 @@ (call-process "optipng" nil nil nil image-path))) image-path) -(defun ennu-image-get-width (image-path) - (ennu-image--assert-file-exists image-path) +(defun ennum-image-get-width (image-path) + (ennum-image--assert-file-exists image-path) (cl-case (image-type image-path) (svg 1e+INF) (otherwise @@ -48,8 +48,8 @@ "-format" "%w" image-path) (string-to-number (buffer-string)))))) -(defun ennu-image--assert-file-exists (path) +(defun ennum-image--assert-file-exists (path) (unless (file-exists-p path) (error "File %s does not exist" path))) -(provide 'ennu-image) +(provide 'ennum-image) @@ -1,7 +1,7 @@ ;; -*- lexical-binding: t -*- -(require 'ennu-html) -(require 'ennu-image) +(require 'ennum-html) +(require 'ennum-image) (require 'ox) (require 'seq) (require 'cl) @@ -9,43 +9,43 @@ (require 'memoize) (require 'simple-httpd) -(defvar ennu-version "0.1.0" - "Ennu version string") +(defvar ennum-version "0.1.0" + "Ennum version string") -(cl-defstruct (ennu-post (:constructor ennu-make-post) +(cl-defstruct (ennum-post (:constructor ennum-make-post) (:copier nil)) filename slug author date language links tangle summary tags thumbnail title translation-group) -(cl-defstruct (ennu-operation (:constructor ennu-make-operation) +(cl-defstruct (ennum-operation (:constructor ennum-make-operation) (:copier nil)) inputs outputs publish) -(defun ennu-posts (posts-directory) - (sort (seq-map 'ennu-read-post +(defun ennum-posts (posts-directory) + (sort (seq-map 'ennum-read-post (file-expand-wildcards (concat (file-name-as-directory - (ennu-setting :posts-directory)) + (ennum-setting :posts-directory)) "*.org"))) - 'ennu-later-post-p)) + 'ennum-later-post-p)) -(defun ennu-later-post-p (post1 post2) - (time-less-p (ennu-post-date post2) - (ennu-post-date post1))) +(defun ennum-later-post-p (post1 post2) + (time-less-p (ennum-post-date post2) + (ennum-post-date post1))) -(defun ennu-read-post (filename) - (ennu--read-post +(defun ennum-read-post (filename) + (ennum--read-post filename (file-attribute-modification-time (file-attributes filename)))) -(defmemoize ennu--read-post (filename last-modified) - (ennu-with-file-contents filename - (let ((metadata (org-export-get-environment 'ennu-html)) - (export (apply-partially 'org-export-with-backend 'ennu-html))) +(defmemoize ennum--read-post (filename last-modified) + (ennum-with-file-contents filename + (let ((metadata (org-export-get-environment 'ennum-html)) + (export (apply-partially 'org-export-with-backend 'ennum-html))) (seq-do (lambda (key) (unless (plist-member metadata key) (user-error "Metadata %s not specified" key))) - ennu-mandatory-metadata) + ennum-mandatory-metadata) (let* ((tree (org-element-parse-buffer)) (links (org-element-map tree 'link (lambda (link) @@ -54,7 +54,7 @@ (let ((link-type (org-element-property :type link))) (when (member link-type (list "image" "static" "video")) (cons link-type (org-element-property :path link)))))))))) - (ennu-make-post + (ennum-make-post :filename filename :slug (file-name-base filename) :author (when-let (author (plist-get metadata :author)) @@ -79,16 +79,16 @@ (seq-some (lambda (link) (pcase link (`("image" . ,path) path) - (`("video" . ,path) (ennu-video-poster path)))) + (`("video" . ,path) (ennum-video-poster path)))) links)) :title (funcall export (first (plist-get metadata :title))) :translation-group (or (plist-get metadata :translation-group) (file-name-base filename))))))) -(defvar ennu-mandatory-metadata +(defvar ennum-mandatory-metadata (list :title :date)) -(defmacro ennu-with-file-contents (file &rest body) +(defmacro ennum-with-file-contents (file &rest body) "Create a temporary buffer, insert contents of FILE into that buffer and evaluate BODY. The value returned is the value of the last form in BODY." @@ -97,76 +97,76 @@ last form in BODY." (insert-file-contents ,file) ,@body)) -(defun ennu--org-output-filename (filename) +(defun ennum--org-output-filename (filename) (concat (file-name-sans-extension filename) ".html")) -(defun ennu-publish-post (posts) +(defun ennum-publish-post (posts) (let ((link-publish-operations - (seq-mapcat 'ennu-publish-link (seq-mapcat 'ennu-post-links posts))) - (input-post-files (seq-map 'ennu-post-filename posts))) + (seq-mapcat 'ennum-publish-link (seq-mapcat 'ennum-post-links posts))) + (input-post-files (seq-map 'ennum-post-filename posts))) (append (list - (ennu-make-operation + (ennum-make-operation :inputs (append input-post-files - (seq-mapcat 'ennu-operation-inputs link-publish-operations)) - :outputs (seq-map 'ennu--org-output-filename input-post-files) + (seq-mapcat 'ennum-operation-inputs link-publish-operations)) + :outputs (seq-map 'ennum--org-output-filename input-post-files) :publish (lambda (&rest output-files) (seq-mapn (lambda (post output-file) - (let ((system-time-locale (map-elt (ennu-setting :locale-alist) - (ennu-post-language post) nil 'string=))) - (ennu-with-file-contents (ennu-post-filename post) + (let ((system-time-locale (map-elt (ennum-setting :locale-alist) + (ennum-post-language post) nil 'string=))) + (ennum-with-file-contents (ennum-post-filename post) (org-export-to-file - 'ennu-html output-file nil nil nil nil + 'ennum-html output-file nil nil nil nil (list :translations (seq-remove (apply-partially 'equal post) posts)))))) posts output-files)))) - (ennu--filter-map + (ennum--filter-map (lambda (post) - (when (ennu-post-tangle post) - (ennu-make-operation - :inputs (list (ennu-post-filename post)) + (when (ennum-post-tangle post) + (ennum-make-operation + :inputs (list (ennum-post-filename post)) :outputs (seq-map (lambda (tangle-output) - (ennu--expand-relative tangle-output - (ennu-setting :static-directory))) - (ennu-post-tangle post)) + (ennum--expand-relative tangle-output + (ennum-setting :static-directory))) + (ennum-post-tangle post)) :publish (lambda (&rest output-files) ;; TODO: Handle tangle outputs that are nested ;; into directories, and when each tangle output ;; is nested into a different directory. (let ((post-file-copy (concat (file-name-directory (first output-files)) - (file-name-nondirectory (ennu-post-filename post))))) - (copy-file (ennu-post-filename post) post-file-copy) + (file-name-nondirectory (ennum-post-filename post))))) + (copy-file (ennum-post-filename post) post-file-copy) (org-babel-tangle-file post-file-copy) (delete-file post-file-copy)))))) posts) link-publish-operations))) -(defun ennu-publish-generic (other-files-directory file) - (ennu-make-operation +(defun ennum-publish-generic (other-files-directory file) + (ennum-make-operation :inputs (list file) :outputs (list (string-remove-prefix (file-name-as-directory other-files-directory) (pcase (file-name-extension file) - ("org" (ennu--org-output-filename file)) + ("org" (ennum--org-output-filename file)) (_ file)))) :publish (lambda (output-file) (pcase (file-name-extension file) - ("org" (ennu-with-file-contents file + ("org" (ennum-with-file-contents file (org-export-to-file 'html output-file))) - (_ (ennu-copy file output-file)))))) + (_ (ennum-copy file output-file)))))) -(defun ennu-video-poster (video) - (pcase (directory-files (ennu-setting :images-directory) nil +(defun ennum-video-poster (video) + (pcase (directory-files (ennum-setting :images-directory) nil (concat (file-name-sans-extension video) "\\.\\(jpg\\|png\\)$")) (`(,poster . ,_) poster) (`() (user-error "Poster for %s not found" video)))) -(defun ennu-add-tongue-suffix (filename tongue) +(defun ennum-add-tongue-suffix (filename tongue) (pcase tongue ("en" filename) (_ (format "%s.%s%s" @@ -174,26 +174,26 @@ last form in BODY." tongue (file-name-extension filename t))))) -(defun ennu-index-filename (filename-prefix tongue &optional extension page-number) +(defun ennum-index-filename (filename-prefix tongue &optional extension page-number) (let ((extension (if extension (concat "." extension) ""))) - (ennu-add-tongue-suffix + (ennum-add-tongue-suffix (if page-number (format "%s-%s%s" filename-prefix page-number extension) (concat filename-prefix extension)) tongue))) -(defun ennu-publish-index (filename-prefix title posts-per-page posts) - (let* ((tongue (ennu-post-language (first posts))) +(defun ennum-publish-index (filename-prefix title posts-per-page posts) + (let* ((tongue (ennum-post-language (first posts))) (number-of-pages (ceiling (length posts) posts-per-page)) (page-numbers (number-sequence 1 number-of-pages))) - (ennu-make-operation - :inputs (seq-map 'ennu-post-filename posts) - :outputs (cons (ennu-add-tongue-suffix (format "%s.html" filename-prefix) tongue) - (seq-map (apply-partially 'ennu-index-filename filename-prefix tongue "html") + (ennum-make-operation + :inputs (seq-map 'ennum-post-filename posts) + :outputs (cons (ennum-add-tongue-suffix (format "%s.html" filename-prefix) tongue) + (seq-map (apply-partially 'ennum-index-filename filename-prefix tongue "html") page-numbers)) :publish (lambda (home-page &rest output-files) - (let ((system-time-locale (map-elt (ennu-setting :locale-alist) tongue nil 'string=))) + (let ((system-time-locale (map-elt (ennum-setting :locale-alist) tongue nil 'string=))) (seq-mapn (lambda (posts page-number output-file) (with-temp-buffer @@ -201,30 +201,30 @@ last form in BODY." (insert (format "#+LANGUAGE: %s\n" tongue)) (insert "#+OPTIONS: num:nil toc:nil\n\n") (seq-do (lambda (post) - (insert (format "* [[post:%s]]\n" (ennu-post-slug post))) - (insert (format-time-string "/%b %e, %Y/\n\n" (ennu-post-date post))) - (when-let ((thumbnail (ennu-post-thumbnail post))) + (insert (format "* [[post:%s]]\n" (ennum-post-slug post))) + (insert (format-time-string "/%b %e, %Y/\n\n" (ennum-post-date post))) + (when-let ((thumbnail (ennum-post-thumbnail post))) (insert (format "[[thumbnail:%s]]\n\n" thumbnail))) - (when-let ((summary (ennu-post-summary post))) + (when-let ((summary (ennum-post-summary post))) (insert summary) (insert "\n\n")) - (when-let ((tags (ennu-post-tags post))) + (when-let ((tags (ennum-post-tags post))) (insert "Tags: ") (insert (string-join (seq-map (lambda (tag) - (format "[[tag:%s][%s]]" (ennu-add-tongue-suffix tag tongue) tag)) + (format "[[tag:%s][%s]]" (ennum-add-tongue-suffix tag tongue) tag)) tags) ", ")) (insert "\n\n"))) posts) (unless (= page-number 1) - (insert (format "[[./%s][Newer posts]]\n" - (ennu-index-filename (file-name-nondirectory filename-prefix) + (insert (format "[[./%s][Newer posts]]\n\n" + (ennum-index-filename (file-name-nondirectory filename-prefix) tongue nil (1- page-number))))) (unless (= page-number number-of-pages) (insert (format "[[./%s][Older posts]]\n" - (ennu-index-filename (file-name-nondirectory filename-prefix) + (ennum-index-filename (file-name-nondirectory filename-prefix) tongue nil (1+ page-number))))) (org-export-to-file 'html output-file))) (seq-partition posts posts-per-page) @@ -232,18 +232,18 @@ last form in BODY." output-files)) (copy-file (first output-files) home-page))))) -(defun ennu--absolute-uri (path) +(defun ennum--absolute-uri (path) (format "%s://%s/%s" - (ennu-setting :blog-scheme) - (ennu-setting :blog-domain) + (ennum-setting :blog-scheme) + (ennum-setting :blog-domain) path)) -(defun ennu--atom-date (date) +(defun ennum--atom-date (date) (format-time-string "%Y-%m-%dT%H:%M:%SZ" date)) -(defun ennu-publish-feed (feed-file title rights posts) - (ennu-make-operation - :inputs (seq-map 'ennu-post-filename posts) +(defun ennum-publish-feed (feed-file title rights posts) + (ennum-make-operation + :inputs (seq-map 'ennum-post-filename posts) :outputs (list feed-file) :publish (lambda (output-file) @@ -251,40 +251,40 @@ last form in BODY." (insert (xmlgen `(feed :xmlns "http://www.w3.org/2005/Atom" - (id ,(ennu--absolute-uri "")) + (id ,(ennum--absolute-uri "")) (title ,title) - (updated ,(ennu--atom-date (ennu-post-date (first posts)))) - (link :rel "self" :href ,(ennu--absolute-uri feed-file)) + (updated ,(ennum--atom-date (ennum-post-date (first posts)))) + (link :rel "self" :href ,(ennum--absolute-uri feed-file)) (generator - ,(format "Emacs %d.%d Org-mode %s ennu %s" - emacs-major-version emacs-minor-version (org-version) ennu-version)) + ,(format "Emacs %d.%d Org-mode %s ennum %s" + emacs-major-version emacs-minor-version (org-version) ennum-version)) (rights ,rights) - ,@(seq-map 'ennu--feed-entry posts)))))))) + ,@(seq-map 'ennum--feed-entry posts)))))))) -(defun ennu--feed-entry (post) - (let ((link (ennu--absolute-uri (ennu--org-output-filename - (ennu-post-filename post))))) +(defun ennum--feed-entry (post) + (let ((link (ennum--absolute-uri (ennum--org-output-filename + (ennum-post-filename post))))) `(entry (id ,link) - (title :xml:lang ,(ennu-post-language post) ,(ennu-post-title post)) - (updated ,(ennu--atom-date (ennu-post-date post))) + (title :xml:lang ,(ennum-post-language post) ,(ennum-post-title post)) + (updated ,(ennum--atom-date (ennum-post-date post))) ,@(when org-export-with-author `((author - (name ,(ennu-post-author post)) + (name ,(ennum-post-author post)) (email ,user-mail-address)))) - (content :type "html" :xml:lang ,(ennu-post-language post) - ,(ennu-with-file-contents (ennu-post-filename post) - (org-export-as 'ennu-html nil nil t))) + (content :type "html" :xml:lang ,(ennum-post-language post) + ,(ennum-with-file-contents (ennum-post-filename post) + (org-export-as 'ennum-html nil nil t))) (link :rel "alternate" :href ,link) ,@(seq-map (lambda (tag) `(category :term ,tag)) - (ennu-post-tags post))))) + (ennum-post-tags post))))) -(defun ennu-setting (property) +(defun ennum-setting (property) (pcase property ((or :blog-domain :blog-license :blog-title :images-directory :output-directory :posts-directory :static-directory :tag-directory :video-directory :working-directory) - (or (plist-get ennu-blog property) + (or (plist-get ennum-blog property) (user-error "Property %s not defined" property))) ((or :atom-feed-number-of-posts :atom-feed-file :blog-scheme :default-image-width @@ -301,35 +301,35 @@ last form in BODY." :locale-alist '(("en" . "C")) :tag-directory "tag" :thumbnail-image-width 320) - ennu-blog) + ennum-blog) property)) (_ (error "Unknown property %s" property)))) -(defun ennu-image-output-filename (image width) +(defun ennum-image-output-filename (image width) (format "%s-%spx.%s" (file-name-sans-extension image) width (file-name-extension image))) -(defun ennu--expand-relative (name directory) +(defun ennum--expand-relative (name directory) (concat (file-name-as-directory directory) name)) -(defun ennu-publish-image (widths image) - (ennu-make-operation +(defun ennum-publish-image (widths image) + (ennum-make-operation :inputs (list image) - :outputs (seq-map (apply-partially 'ennu-image-output-filename image) + :outputs (seq-map (apply-partially 'ennum-image-output-filename image) widths) :publish (lambda (&rest output-files) (seq-mapn (lambda (output-file width) - (ennu-image-optimize-image - (ennu-image-resize-image image output-file width))) + (ennum-image-optimize-image + (ennum-image-resize-image image output-file width))) output-files widths)))) -(defun ennu-publish-copy (file) - (ennu-make-operation +(defun ennum-publish-copy (file) + (ennum-make-operation :inputs (list file) :outputs (list file) - :publish (apply-partially 'ennu-copy file))) + :publish (apply-partially 'ennum-copy file))) (defun newest-file (files) (pcase files @@ -339,10 +339,10 @@ last form in BODY." file1 file2)) tail head)))) -(defun ennu-mkdir-p (directory) +(defun ennum-mkdir-p (directory) (make-directory directory t)) -(defun ennu-copy (source destination) +(defun ennum-copy (source destination) "Copy file or directory from SOURCE to DESTINATION. Overwrite if DESTINATION already exists." (if (file-directory-p source) @@ -350,56 +350,65 @@ if DESTINATION already exists." (make-directory (file-name-directory destination) t) (copy-file source destination t))) -(defun ennu--filter-map (function sequence) +(defun ennum--filter-map (function sequence) (seq-filter 'identity (seq-map function sequence))) -(defun ennu--do-operation (temporary-directory operation) +;; TODO: What if a file was removed from the inputs? Detect that +;; change as well. + +;; Two separate problems +;; - tracking of list of inputs +;; - depending on a function of inputs, or equivalently intermediate files + +;; Solve both problems with an "ennum store" +(defun ennum--do-operation (temporary-directory operation) + ;; TODO: Check all outputs were created correctly. (let* ((expand (lambda (directory file) (expand-file-name file directory))) - (inputs (ennu-operation-inputs operation)) - (outputs (ennu-operation-outputs operation)) + (inputs (ennum-operation-inputs operation)) + (outputs (ennum-operation-outputs operation)) (absolute-outputs (seq-map (apply-partially expand temporary-directory) outputs)) (previous-outputs - (seq-map (apply-partially expand (ennu-setting :output-directory)) + (seq-map (apply-partially expand (ennum-setting :output-directory)) outputs))) (cond ((and (seq-every-p 'file-exists-p previous-outputs) (file-newer-than-file-p (newest-file previous-outputs) (newest-file inputs))) (message "Skipping publishing %s to %s" inputs outputs) - (seq-mapn 'ennu-copy previous-outputs absolute-outputs)) + (seq-mapn 'ennum-copy previous-outputs absolute-outputs)) (t (message "Publishing %s to %s" inputs outputs) - (seq-do 'ennu-mkdir-p + (seq-do 'ennum-mkdir-p (seq-uniq (seq-map 'file-name-directory absolute-outputs))) - (apply (ennu-operation-publish operation) absolute-outputs))))) + (apply (ennum-operation-publish operation) absolute-outputs))))) -(defun ennu-publish-static-file (file) - (ennu-make-operation +(defun ennum-publish-static-file (file) + (ennum-make-operation :inputs (list file) :outputs (list file) - :publish (apply-partially 'ennu-copy file))) + :publish (apply-partially 'ennum-copy file))) -(defun ennu-publish-link (link) +(defun ennum-publish-link (link) (pcase link (`("image" . ,path) (list - (ennu-publish-image - (list (ennu-setting :default-image-width) - (ennu-setting :image-link-width)) - (ennu--expand-relative path (ennu-setting :images-directory))))) + (ennum-publish-image + (list (ennum-setting :default-image-width) + (ennum-setting :image-link-width)) + (ennum--expand-relative path (ennum-setting :images-directory))))) (`("static" . ,path) (list - (ennu-publish-copy (ennu--expand-relative path (ennu-setting :static-directory))))) + (ennum-publish-copy (ennum--expand-relative path (ennum-setting :static-directory))))) (`("video" . ,path) (list - (ennu-publish-copy (ennu--expand-relative path (ennu-setting :video-directory))) - (ennu-publish-copy (ennu--expand-relative (ennu-video-poster path) - (ennu-setting :images-directory))))))) + (ennum-publish-copy (ennum--expand-relative path (ennum-setting :video-directory))) + (ennum-publish-copy (ennum--expand-relative (ennum-video-poster path) + (ennum-setting :images-directory))))))) -(defmacro ennu-with-current-directory (directory &rest body) +(defmacro ennum-with-current-directory (directory &rest body) "Change to DIRECTORY, evaluate BODY and restore the current working directory. The value returned is the value of the last form in BODY." @@ -409,19 +418,19 @@ form in BODY." (unwind-protect (progn (cd ,directory) ,@body) (cd ,current-directory-symbol))))) -(defmacro ennu-with-temporary-directory (temporary-directory &rest body) +(defmacro ennum-with-temporary-directory (temporary-directory &rest body) "Create temporary directory, evaluate BODY with the absolute path of that directory assigned to TEMPORARY-DIRECTORY and finally delete the temporary directory. The value returned is the value of the last form in BODY." (declare (indent defun)) - `(let ((,temporary-directory (make-temp-file "ennu" t))) + `(let ((,temporary-directory (make-temp-file "ennum" t))) (chmod ,temporary-directory #o755) (unwind-protect (progn ,@body) (delete-directory ,temporary-directory t)))) -(defun ennu-many-to-many-group-by (function sequence) +(defun ennum-many-to-many-group-by (function sequence) "Apply FUNCTION to each element of SEQUENCE. Separate the elements of SEQUENCE into an alist using the results as keys. Keys are compared using `equal'." @@ -437,55 +446,55 @@ as keys. Keys are compared using `equal'." (seq-reverse sequence) nil)) -(defun ennu-publish () +(defun ennum-publish () (interactive) (let ((make-backup-files nil) - (blog-title (ennu-setting :blog-title)) - (posts-per-page (ennu-setting :index-posts-per-page))) - (ennu-with-current-directory (ennu-setting :working-directory) - (ennu-with-temporary-directory temporary-directory + (blog-title (ennum-setting :blog-title)) + (posts-per-page (ennum-setting :index-posts-per-page))) + (ennum-with-current-directory (ennum-setting :working-directory) + (ennum-with-temporary-directory temporary-directory (seq-do - (apply-partially 'ennu--do-operation temporary-directory) + (apply-partially 'ennum--do-operation temporary-directory) (append - (let ((posts (ennu-posts (ennu-setting :posts-directory)))) + (let ((posts (ennum-posts (ennum-setting :posts-directory)))) (append ;; Publish posts (seq-mapcat (pcase-lambda (`(,translation-group . ,posts)) - (ennu-publish-post posts)) - (seq-group-by 'ennu-post-translation-group posts)) + (ennum-publish-post posts)) + (seq-group-by 'ennum-post-translation-group posts)) ;; Publish feed - (list (ennu-publish-feed (ennu-setting :atom-feed-file) + (list (ennum-publish-feed (ennum-setting :atom-feed-file) blog-title - (ennu-setting :blog-license) - (seq-take posts (ennu-setting :atom-feed-number-of-posts)))) + (ennum-setting :blog-license) + (seq-take posts (ennum-setting :atom-feed-number-of-posts)))) ;; Publish indices (seq-map (pcase-lambda (`(,tongue . ,posts)) - (ennu-publish-index "index" blog-title posts-per-page posts)) - (seq-group-by 'ennu-post-language posts)) + (ennum-publish-index "index" blog-title posts-per-page posts)) + (seq-group-by 'ennum-post-language posts)) (seq-mapcat (pcase-lambda (`(,tag . ,posts)) (seq-map (pcase-lambda (`(,tongue . ,posts)) - (ennu-publish-index - (ennu--expand-relative tag (ennu-setting :tag-directory)) + (ennum-publish-index + (ennum--expand-relative tag (ennum-setting :tag-directory)) tag posts-per-page posts)) - (seq-group-by 'ennu-post-language posts))) - (ennu-many-to-many-group-by 'ennu-post-tags posts)) + (seq-group-by 'ennum-post-language posts))) + (ennum-many-to-many-group-by 'ennum-post-tags posts)) ;; Publish thumbnails (seq-map - (apply-partially 'ennu-publish-image (list (ennu-setting :thumbnail-image-width))) + (apply-partially 'ennum-publish-image (list (ennum-setting :thumbnail-image-width))) (seq-map (lambda (image) - (ennu--expand-relative image (ennu-setting :images-directory))) - (seq-uniq (ennu--filter-map 'ennu-post-thumbnail posts)))))) + (ennum--expand-relative image (ennum-setting :images-directory))) + (seq-uniq (ennum--filter-map 'ennum-post-thumbnail posts)))))) ;; Publish other files - (when-let ((other-files-directory (ennu-setting :other-files-directory))) - (seq-map (apply-partially 'ennu-publish-generic other-files-directory) + (when-let ((other-files-directory (ennum-setting :other-files-directory))) + (seq-map (apply-partially 'ennum-publish-generic other-files-directory) (seq-map (apply-partially 'string-remove-prefix (file-name-as-directory (expand-file-name default-directory))) (directory-files-recursively other-files-directory ".")))))) ;; Replace old output directory - (let ((output (ennu-setting :output-directory))) + (let ((output (ennum-setting :output-directory))) (delete-directory output t) (rename-file temporary-directory output t)))))) @@ -493,10 +502,11 @@ as keys. Keys are compared using `equal'." ;;; ;;; Test HTTP server to serve the blog locally -(defun ennu-server-start () +;; TODO: Why can't simple-httpd itself handle the unhexing? +(defun ennum-server-start () (interactive) - (setq httpd-root (expand-file-name (ennu-setting :output-directory) - (ennu-setting :working-directory))) + (setq httpd-root (expand-file-name (ennum-setting :output-directory) + (ennum-setting :working-directory))) (defun httpd/ (proc uri-path query request) (let* ((uri-path (httpd-unhex uri-path)) (file-path (httpd-gen-path uri-path))) @@ -514,11 +524,11 @@ as keys. Keys are compared using `equal'." ;; appended. (t (httpd-serve-root proc httpd-root (concat uri-path ".html") request))))) (httpd-start) - (message "Ennu web server listening at http://localhost:%d" httpd-port)) + (message "Ennum web server listening at http://localhost:%d" httpd-port)) -(defun ennu-server-stop () +(defun ennum-server-stop () (interactive) (httpd-stop) - (message "Ennu web server stopped")) + (message "Ennum web server stopped")) -(provide 'ennu) +(provide 'ennum) |