436 lines
18 KiB
EmacsLisp
436 lines
18 KiB
EmacsLisp
(require 'ox-publish)
|
|
(require 'denote)
|
|
(require 'denote-org)
|
|
(require 'async)
|
|
(require 'dash)
|
|
(require 's)
|
|
(require 'f)
|
|
(require 'comp-run)
|
|
(require 'simple-httpd)
|
|
|
|
(defun quick/async-export-function (chunk index)
|
|
"A function to build a future that will export all it's chunk's files"
|
|
(let ((func `(lambda ()
|
|
(setq lexical-binding t)
|
|
(shell-command (format "emacsclient -e '(message \"%s\")'" ',chunk))
|
|
(require 's)
|
|
(require 'org)
|
|
(require 'ox)
|
|
(require 'ox-html)
|
|
(require 'htmlize)
|
|
(require 'denote)
|
|
(require 'denote-org)
|
|
(cl-loop for file in ',chunk do
|
|
(setq find-file-hook '())
|
|
(setq org-mode-hook '())
|
|
;; (message "%s" file)
|
|
(shell-command (format "emacsclient -e '(message \"%s\")" file))
|
|
(with-current-buffer (find-file file)
|
|
(re-search-forward ".*EXPORT_FILE_NAME:" nil t)
|
|
(beginning-of-line)
|
|
(let* ((export-line? (search-forward "EXPORT_FILE_NAME" (line-end-position) t))
|
|
(export-line (when export-line?
|
|
(delete-and-extract-region
|
|
(line-beginning-position) (line-end-position))))
|
|
(find-file-hook '())
|
|
(gc-cons-threshold 50000000)
|
|
(org-mode-hook '())
|
|
(create-lockfiles nil)
|
|
(org-export-with-broken-links 'mark)
|
|
(org-export-with-section-numbers nil)
|
|
(org-html-html5-fancy t)
|
|
(org-html-validation-link nil)
|
|
(make-backup-files nil)
|
|
(org-export-with-title t)
|
|
(org-html-head-include-default-style nil)
|
|
(org-html-head "<link rel=\"stylesheet\" href=\"../static/pico.snazzy.css\">")
|
|
(denote-directory "/home/chris/docs/site/content")
|
|
(export-directory
|
|
(concat "/home/chris/"
|
|
"docs/site/public/"
|
|
(if (s-contains? "content/teaching" file)
|
|
"teaching/"
|
|
"notes/")))
|
|
(html-file (concat export-directory
|
|
(org-export-output-file-name ".html")))
|
|
(org-export-coding-system org-html-coding-system))
|
|
(denote-org-convert-links-to-file-type)
|
|
(widen)
|
|
(org-update-all-dblocks)
|
|
(replace-string-in-region "../site/content/" "./"
|
|
(point-min) (point-max))
|
|
(org-export-to-file 'html html-file)
|
|
(when export-line? (insert export-line))
|
|
(save-buffer)
|
|
(kill-buffer)))))))
|
|
|
|
(message "Exporting chunk %s out of %s with files: %s" index (- (length chunks) 1)
|
|
(mapcar (lambda (file)
|
|
(concat "\n" file)) chunk))
|
|
|
|
(async-start
|
|
func
|
|
'ignore)))
|
|
|
|
(defun quick/org-publish-site ()
|
|
"Publish my website by pushing files to specific locations"
|
|
(interactive)
|
|
(let* ((notes (--remove
|
|
(string-match-p "\\(_draft\\|_lesson\\|_sermon\\)" it)
|
|
(directory-files "~/docs/notes" t ".*.org$")))
|
|
(lessons (--remove
|
|
(s-contains? "_draft" it)
|
|
(directory-files "~/docs/notes/lessons" t ".*.org$")))
|
|
(denote-directory "~/docs/site/content")
|
|
(current-notes (directory-files "~/docs/site/content/notes" t ".*.org$"))
|
|
(current-lessons (directory-files "~/docs/site/content/teaching" t ".*.org$"))
|
|
(warning-minimum-level :error)
|
|
(inhibit-message)
|
|
(create-lockfiles nil)
|
|
(org-export-with-broken-links 'mark)
|
|
(org-export-with-section-numbers nil)
|
|
(org-export-with-author nil)
|
|
(org-export-with-timestamps nil)
|
|
(org-export-with-date nil)
|
|
(org-export-with-toc nil)
|
|
(org-html-html5-fancy t)
|
|
(org-html-validation-link nil)
|
|
(org-html-postamble "")
|
|
(make-backup-files nil)
|
|
(org-html-head-include-default-style nil)
|
|
(org-html-head "<link rel=\"stylesheet\" href=\"./static/pico.snazzy.css\">
|
|
<style>
|
|
li:nth-child(n+7) {
|
|
display: none;
|
|
}
|
|
</style>"))
|
|
|
|
(when (not
|
|
(= 0
|
|
(shell-command
|
|
"cmp --silent ~/docs/site/assets/pico.snazzy.css\
|
|
~/docs/site/public/static/pico.snazzy.css")))
|
|
(copy-file "~/docs/site/assets/pico.snazzy.css"
|
|
"~/docs/site/public/static/pico.snazzy.css" t))
|
|
|
|
(message "Starting site build")
|
|
(cl-loop for file in current-notes
|
|
do (delete-file file t))
|
|
(message "Removed all notes")
|
|
(cl-loop for file in current-lessons
|
|
do (delete-file file t))
|
|
(message "Removed all lessons")
|
|
(cl-loop for file in notes
|
|
do (let ((filename (concat "~/docs/site/content/notes/" (f-filename file))))
|
|
(copy-file
|
|
file filename t t)))
|
|
(message "Copied all notes")
|
|
(cl-loop for file in lessons
|
|
do (let ((filename (concat "~/docs/site/content/teaching/" (f-filename file))))
|
|
(copy-file
|
|
file filename t t)
|
|
(message "copied %s to %s" file filename)))
|
|
(message "Copied all lessons")
|
|
(find-file "~/docs/site/content/index.org")
|
|
(org-update-all-dblocks)
|
|
(org-export-to-file 'html "~/docs/site/public/index.html")
|
|
(kill-buffer)
|
|
|
|
(let* ((files (seq-concatenate
|
|
'list
|
|
(directory-files "~/docs/site/content/notes" t ".*.org$")
|
|
(directory-files "~/docs/site/content/teaching" t ".*.org$")))
|
|
(workers 8)
|
|
(chunks (seq-split files (ceiling (/ (length files) workers))))
|
|
(futures (cl-loop for chunk in chunks and index from 0
|
|
collect (quick/async-export-function chunk index))))
|
|
(mapcar #'async-get futures)
|
|
(message "Finished exporting all files"))))
|
|
|
|
(defun quick/org-copy-notes (plist)
|
|
(let ((files (--remove
|
|
(string-match-p (plist-get plist :exclude) it)
|
|
(directory-files (file-name-as-directory
|
|
(plist-get plist :denote-directory)) t ".*.org$")))
|
|
(create-lockfiles nil))
|
|
(dolist (file files)
|
|
(let ((filename (concat (file-name-as-directory
|
|
(plist-get plist :base-directory))
|
|
(f-filename file))))
|
|
(copy-file
|
|
file filename t t)))))
|
|
|
|
(defun chris/site-index-update (plist)
|
|
(save-excursion
|
|
(let ((warning-minimum-level :error)
|
|
(denote-directory "~/docs/site/content")
|
|
(file (concat (file-name-as-directory
|
|
(plist-get plist :base-directory)) "index.org")))
|
|
(find-file file)
|
|
(org-update-all-dblocks)
|
|
(denote-org-convert-links-to-file-type)
|
|
(save-buffer))))
|
|
|
|
(defun chris/preparation-function (plist)
|
|
(quick/org-copy-notes plist)
|
|
(chris/site-index-update plist))
|
|
|
|
(defun chris/ignore-export-name-filter-function (plist _backend)
|
|
(when (plist-member plist :export-file-name)
|
|
(let ((name (plist-get plist :export-file-name))
|
|
(key-gone (remove :export-file-name))
|
|
(new-plist (remove name)))
|
|
new-plist)))
|
|
|
|
(defun chris/site-premble (plist)
|
|
(let ((project (plist-get plist :title-of-project)))
|
|
(format "<nav>
|
|
<ul>
|
|
<li><strong>%s</strong></li>
|
|
</ul>
|
|
<ul>
|
|
<li><a href=\"/index\">Home</a></li>
|
|
<li><a href=\"/notes\">Notes</a></li>
|
|
<li><a href=\"/teaching\">Teaching</a></li>
|
|
</ul>
|
|
</nav>" (if (string=
|
|
(when (stringp project) project) "index")
|
|
"Chris Cochrun"))))
|
|
|
|
(setq org-publish-project-alist
|
|
`(("notes"
|
|
:denote-directory "~/docs/notes/"
|
|
:base-directory "~/docs/site/content/notes/"
|
|
:base-extension "org"
|
|
:publishing-directory "~/docs/site/public/notes/"
|
|
:exclude "\\(_draft\\|_lesson\\|_sermon\\)"
|
|
:preparation-function chris/preparation-function
|
|
:recursive nil
|
|
:with-toc t
|
|
:with-broken-links t
|
|
:with-author nil
|
|
:with-timestamps nil
|
|
:section-numbers nil
|
|
:html-postamble nil
|
|
:html-validation-link nil
|
|
:exlude-tags "revealonly\\|noexport"
|
|
:html-head-include-default-style nil
|
|
:html-doctype "html5"
|
|
:html-html5-fancy t
|
|
:html-self-link-headlines t
|
|
:htmlized-source t
|
|
;; :html-preamble chris/site-premble
|
|
:html-preamble-format (("en" "<nav>
|
|
<ul>
|
|
<li><strong><a href=\"/\">Chris Cochrun</a> > %t</strong></li>
|
|
</ul>
|
|
<ul>
|
|
<li><a href=\"/notes\">Notes</a></li>
|
|
<li><a href=\"/teaching\">Teaching</a></li>
|
|
</ul>
|
|
</nav>"))
|
|
:html-head "<link rel=\"stylesheet\" href=\"../static/pico.snazzy.css\" type=\"text/css\"/>"
|
|
:publishing-function org-html-publish-to-html)
|
|
("teaching"
|
|
:denote-directory "~/docs/notes/lessons/"
|
|
:base-directory "~/docs/site/content/teaching/"
|
|
:base-extension "org"
|
|
:publishing-directory "~/docs/site/public/teaching/"
|
|
:exclude "_draft"
|
|
:preparation-function chris/preparation-function
|
|
:recursive nil
|
|
:with-broken-links t
|
|
:with-author nil
|
|
:with-timestamps nil
|
|
:section-numbers nil
|
|
;; :html-preamble chris/site-premble
|
|
:html-postamble nil
|
|
:html-validation-link nil
|
|
:exlude-tags "revealonly\\|noexport"
|
|
:html-head-include-default-style nil
|
|
:html-doctype "html5"
|
|
:html-html5-fancy t
|
|
:html-self-link-headlines t
|
|
:htmlized-source t
|
|
:html-preamble-format (("en" "<nav>
|
|
<ul>
|
|
<li><strong><a href=\"/\">Chris Cochrun</a> > %t</strong></li>
|
|
</ul>
|
|
<ul>
|
|
<li><a href=\"/notes\">Notes</a></li>
|
|
<li><a href=\"/teaching\">Teaching</a></li>
|
|
</ul>
|
|
</nav>"))
|
|
:html-head "<link rel=\"stylesheet\" href=\"../static/pico.snazzy.css\" type=\"text/css\"/>"
|
|
:publishing-function org-html-publish-to-html)
|
|
("static"
|
|
:base-directory "~/docs/site/assets/"
|
|
:base-extension "css\\|txt\\|jpg\\|gif\\|png\\|webp\\|webm\\|mp4\\|js\\|html"
|
|
:recursive t
|
|
:publishing-directory "~/docs/site/public/static/"
|
|
:publishing-function org-publish-attachment)
|
|
("index"
|
|
:base-directory "~/docs/site/content"
|
|
:title-of-project "index"
|
|
:base-extension "org"
|
|
:preparation-function chris/site-index-update
|
|
:recursive nil
|
|
:with-broken-links t
|
|
:with-author nil
|
|
:with-timestamps nil
|
|
:with-toc nil
|
|
:section-numbers nil
|
|
;; :html-preamble chris/site-premble
|
|
:html-preamble-format (("en" "<nav>
|
|
<ul>
|
|
<li><strong>Chris Cochrun</strong></li>
|
|
</ul>
|
|
<ul>
|
|
<li><a href=\"/notes\">Notes</a></li>
|
|
<li><a href=\"/teaching\">Teaching</a></li>
|
|
</ul>
|
|
</nav>"))
|
|
:html-postamble nil
|
|
:html-validation-link nil
|
|
:exlude-tags "revealonly\\|noexport"
|
|
:html-head-include-default-style nil
|
|
:html-doctype "html5"
|
|
:html-html5-fancy t
|
|
:html-self-link-headlines t
|
|
:htmlized-source t
|
|
:html-head "<link rel=\"stylesheet\" href=\"./static/pico.snazzy.css\" type=\"text/css\"/>
|
|
<style>
|
|
li:nth-child(n+7) {
|
|
display: none;
|
|
}
|
|
</style>"
|
|
:publishing-directory "~/docs/site/public/"
|
|
:publishing-function org-html-publish-to-html)
|
|
("cochrun.xyz" :components ("index" "notes" "static" "teaching"))))
|
|
|
|
(defun quick/org-publish-projects (projects &optional workers)
|
|
"Publish all files belonging to the PROJECT.
|
|
If `:auto-sitemap' is set, publish the sitemap too. If
|
|
`:makeindex' is set, also produce a file \"theindex.org\".
|
|
|
|
If the optional argument WORKERS is used, this many instances
|
|
of emacs will be used in order to publish the files, else
|
|
it will use the correct amount of "
|
|
(dolist (project (org-publish-expand-projects projects))
|
|
(let ((plist (cdr project))
|
|
(create-lockfiles nil)
|
|
(org-publish-timestamp-directory
|
|
(concat (xdg-cache-home) "/org/timestamps/")))
|
|
(let ((fun (org-publish-property :preparation-function project)))
|
|
(cond
|
|
((functionp fun) (funcall fun plist))
|
|
((consp fun) (dolist (f fun) (funcall f plist)))))
|
|
;; Each project uses its own cache file.
|
|
(org-publish-initialize-cache (car project))
|
|
(when (org-publish-property :auto-sitemap project)
|
|
(let ((sitemap-filename
|
|
(or (org-publish-property :sitemap-filename project)
|
|
"sitemap.org")))
|
|
(org-publish-sitemap project sitemap-filename)))
|
|
;; Publish all files from PROJECT except "theindex.org". Its
|
|
;; publishing will be deferred until "theindex.inc" is
|
|
;; populated.
|
|
;; (message "%s" (org-publish-get-base-files project))
|
|
;; (message "%s" project)
|
|
(let* ((theindex
|
|
(expand-file-name "theindex.org"
|
|
(org-publish-property :base-directory project)))
|
|
(attachment-function? (eq #'org-publish-attachment
|
|
(org-publish-property :publishing-function project)))
|
|
(org-export-filter-options-functions '(#'chris/ignore-export-name-filter-function))
|
|
(workers (if workers workers
|
|
(* 2 (comp--effective-async-max-jobs))))
|
|
(files (org-publish-get-base-files project))
|
|
(chunks (if (> workers (length files))
|
|
files
|
|
(seq-split files (ceiling (/ (length files) workers)))))
|
|
(futures (unless attachment-function?
|
|
(cl-loop for chunk in chunks
|
|
collect (async-start
|
|
`(lambda ()
|
|
(require 'ox)
|
|
(require 'ox-html)
|
|
(require 'htmlize)
|
|
(let ((org-publish-use-timestamps-flag t)
|
|
(org-publish-timestamp-directory
|
|
(concat (xdg-cache-home) "/org/timestamps/"))
|
|
(create-lockfiles nil)
|
|
(org-publish-cache (org-publish-initialize-cache
|
|
(car ',project)))
|
|
(org-export-filter-options-functions ',org-export-filter-options-functions)
|
|
(files (if (listp ',chunk)
|
|
',chunk
|
|
(list ,chunk))))
|
|
(dolist (file files)
|
|
(unless (file-equal-p file ,theindex)
|
|
(org-publish-file file ',project))))))))))
|
|
|
|
(message "=========================")
|
|
(message "Starting project: %s" (car project))
|
|
(message "=========================")
|
|
(when attachment-function? (message "OOOOPS AATTTTAAAACH"))
|
|
(if attachment-function?
|
|
(dolist (file files)
|
|
(unless (file-equal-p file theindex)
|
|
(org-publish-file file project t)))
|
|
(mapcar #'async-wait futures))
|
|
|
|
;; Populate "theindex.inc", if needed, and publish
|
|
;; "theindex.org".
|
|
(when (org-publish-property :makeindex project)
|
|
(org-publish-index-generate-theindex
|
|
project (org-publish-property :base-directory project))
|
|
(org-publish-file theindex project t)))
|
|
(let ((fun (org-publish-property :completion-function project)))
|
|
(cond
|
|
((functionp fun) (funcall fun plist))
|
|
((consp fun) (dolist (f fun) (funcall f plist))))))))
|
|
|
|
(defun quick/org-async-function (files project theindex)
|
|
"Publish all files with only knowing the files"
|
|
(dolist (file files)
|
|
(unless (file-equal-p file theindex)
|
|
(org-publish-file file project t))))
|
|
|
|
(defun quick/org-publish (project &optional force async)
|
|
"Publish PROJECT. This is a more aggressively async version of
|
|
`org-publish'.
|
|
|
|
PROJECT is either a project name, as a string, or a project
|
|
alist (see `org-publish-project-alist' variable).
|
|
|
|
When optional argument FORCE is non-nil, force publishing all
|
|
files in PROJECT. With a non-nil optional argument ASYNC,
|
|
publishing will be done asynchronously, using as many workers
|
|
on your machine as makes sense."
|
|
(interactive
|
|
(list (assoc (completing-read "Publish project: "
|
|
org-publish-project-alist nil t)
|
|
org-publish-project-alist)
|
|
current-prefix-arg))
|
|
(let ((project (if (not (stringp project)) project
|
|
;; If this function is called in batch mode,
|
|
;; PROJECT is still a string here.
|
|
(assoc project org-publish-project-alist))))
|
|
(cond
|
|
((not project))
|
|
(async
|
|
(let ((org-publish-use-timestamps-flag
|
|
(and (not force) org-publish-use-timestamps-flag)))
|
|
;; Expand components right now as external process may not
|
|
;; be aware of complete `org-publish-project-alist'.
|
|
(quick/org-publish-projects
|
|
(org-publish-expand-projects (list project)))))
|
|
(t (save-window-excursion
|
|
(let ((org-publish-use-timestamps-flag
|
|
(and (not force) org-publish-use-timestamps-flag)))
|
|
(org-publish-projects (list project))))))))
|
|
|
|
(quick/org-publish "cochrun.xyz" nil t)
|
|
|
|
(httpd-serve-directory "~/docs/site/public")
|