Colophon

Table of Contents

1 weblog.el

Everything I use to generate this weblog is on this file. The color scheme here looks heinous, I know.

(defun org-global-props (&optional property buffer)
  "Get the plists of global org properties of current buffer."
  (unless property (setq property "PROPERTY"))
  (with-current-buffer (or buffer (current-buffer))
    (org-element-map (org-element-parse-buffer) 'keyword
      (lambda (el)
        (when (string-match property (org-element-property :key el)) el)))))


(defun weblog-get-property (property)
  "I send thee the org-mode buffer property you wish to know."
  (org-element-property :value (car (org-global-props property))))


(defun print-to-file (filename data)
  (with-temp-file filename
    (erase-buffer) ;; otherwise it will append instead of rewrite
    (prin1 data (current-buffer))))


(defun print-to-file-raw (filename data)
  (with-temp-file filename
    (erase-buffer) ;; otherwise it will append instead of rewrite
    (princ data (current-buffer))))


(defun read-from-file (filename)
  (with-temp-buffer
    (insert-file-contents filename)
    (cl-assert (eq (point) (point-min)))
    (read (current-buffer))))


(defun current-filename ()
  (file-name-nondirectory (buffer-file-name (current-buffer))))

(defun weblog-regenerate-file (&optional filename)
  (interactive)
  (let ((out-file (org-html-export-to-html nil nil nil nil nil))
        (filename (current-filename)))
    ;; this send the created file to the right place
    ;; output-path is defined in the directory variables file
    (rename-file out-file (concat output-path out-file) t)))

(defun weblog-save ()

  (interactive)

  ;; in case it’s the first time we use this file
  (unless (file-exists-p "metadata.el")
    (print-to-file "metadata.el" nil))

  (let ((out-file (org-html-export-to-html nil nil nil nil nil))
        (filename (current-filename))
        (title (weblog-get-property "TITLE"))
        (creation-date (weblog-get-property "DATE"))
        (updating-date (time-convert nil 'integer))
        (categories (weblog-get-property "CATEGORIES"))
        ;; this means we need to update files sequentially,
        ;; otherwise two updates might delete each other
        (metadata-file (read-from-file "metadata.el"))
        (metadata-here nil))

    ;; this send the created file to the right place
    ;; output-path is defined in the directory variables file
    (rename-file out-file (concat output-path out-file) t)

    (setq metadata-here (list :filename filename
                              :title title
                              :creation-date creation-date
                              :updating-date updating-date
                              :categories categories))

    ;; we delete the older entry for out-file
    (setq metadata-file (assoc-delete-all out-file metadata-file))

    (push (cons out-file metadata-here) metadata-file)

    ;; this saves the metadata for indexing
    (print-to-file "metadata.el" nil)
    (print-to-file "metadata.el" metadata-file)))


(defun seconds-to-date (secs)
  "1576029729 → 2019-12-10."
  (format-time-string "%Y-%m-%d" (seconds-to-time secs)))

(defun date-to-seconds (date)
  "2019-12-10 → 1576029729."
  (if date
      (progn
        ;; gentle massage to make `parse-iso8601-time-string' like it.
        (if (string-match "[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}$" date)
            ;; this could depend on the timezone, -03 here.
            (setq date (concat date "T00:00:00-03:00")))
        ;; we get something in the form (1583385598301698708 . 1000000000),
        ;; where we need to divide `car' by `cdr' to get the actual seconds.
        (let ((parsed (time-convert (parse-iso8601-time-string date) t)))
          (/ (car parsed) (cdr parsed))))
    nil))


(defun weblog-later-entry-date-p (1st 2nd)
  "If you give me two instances of something
  like (\"humanistic-script.html\"
  :filename \"humanistic-script.org\" :title \"origin and
  development of humanistic script\" :creation-date
  \"2020-03-03\" :updating-date 1583222269 :categories nil) I can
  decide which is more recent."
  (setq 1st (getf (cdr 1st) :creation-date))
  (setq 2nd (getf (cdr 2nd) :creation-date))
  (unless 1st (setq 1st "1915-01-01"))
  (unless 2nd (setq 2nd "1915-01-01"))
  (setq 1st (date-to-seconds 1st))
  (setq 2nd (date-to-seconds 2nd))
  (> 1st 2nd))


(defun weblog-build-entry (proplist)
  "Give me a property list and a list of properties you want,
and I shall give thee a table row thus arranged."
  (let (out)
    (setq out (concat
               "| "
               "~" (getf proplist :creation-date) "~"

               " | "
               "[[file:" (getf proplist :filename)
               "][" (getf proplist :title) "]]"

               "| "
               (if (equal (getf proplist :creation-date)
                          (seconds-to-date (getf proplist :updating-date)))
                   " "
                 ;; else
                 (concat "~" (seconds-to-date (getf proplist :updating-date)) "~"))

               " | "
               (getf proplist :categories)

               "|"
               ))
    out))



(defun weblog-build-index ()
  (let (out)
    (setq out (concat out "| creation | title | last update | categories |\n"))
    (setq out (concat out "|--\n"))
    (dolist (i (read-from-file "~/omnia/weblog/metadata.el"))
      (unless (equal (car i) "index.html")
        (setq out (concat out (weblog-build-entry (cdr i)) "\n"))))
    (setq out (concat out "\n"))
    (print-to-file-raw "~/omnia/weblog/index-by-update.txt" out)))

(defun weblog-build-index-creation ()
  (let (out)
    (setq out (concat out "| creation | title | last update | categories |\n"))
    (setq out (concat out "|--\n"))
    (dolist (i (sort (read-from-file "~/omnia/weblog/metadata.el") 'weblog-later-entry-date-p))
      (unless (equal (car i) "index.html")
        (setq out (concat out (weblog-build-entry (cdr i)) "\n"))))
    (setq out (concat out "\n"))
    (print-to-file-raw "~/omnia/weblog/index-by-creation.txt" out)))


(defun weblog-generate-everything ()
  (interactive)
  (find-file "index.org")
  (weblog-build-index)
  (weblog-save)
  (kill-buffer))


(global-set-key (kbd "H-g") 'weblog-save)
(global-set-key (kbd "H-G") 'weblog-generate-everything)
(global-set-key (kbd "H-r") 'weblog-regenerate-file-with-transformations)
(global-set-key (kbd "H-R") 'weblog-regenerate-file)

(setq org-html-preamble-format
      '(("en"
         "
<center>
<a href='index.html'>
<img id='nomen' src='etc/tarhuntas.svg' />
</a></center>
<div id='preamble-links'>
<ul>
<li><a href='index.html'>index χρονικός</a><br>
<p class='explanation'>ordered by creation date</p></li>
<li><a href='index-recentiorum.html'>index recentiorum</a><br>
<p class='explanation'>ordered by last update</p></li>
<li>index verborum<br>
<p class='explanation'>as a word list</p></li>
<li><a href='colophon.html'>κολοϕών</a></li>
</ul>
</div>")))


(setq org-html-postamble-format '(("en" "<hr>
· copyright © <span class=\"author\">Edgard Bikelis (<i>eſb</i>)</span> · %C ·</br>
created using <span class=\"creator\">%c</span></br>
<!-- created on %d</br> -->
<!-- modified on %C</br> -->
<!-- exported on <span class=\"date\">%T</span> -->
<hr>")))


(defun collect-occurrences (regex)
  "I give thee a list of all the regex matches in the current
buffer, with each match as its own list with all the groups it got."
  (let (out)
    (goto-char 0)
    ;; while we find matches
    (while (re-search-forward regex nil 1)
      (let ((n 0)(this-occurrence))
        ;; while we still have groups in this match
        (while (match-string n)
          ;; push it to this matches list
          (push (substring-no-properties (match-string n)) this-occurrence)
          (setq n (1+ n)))
        ;; and finally push this matches list to the list of all matches
        (push (reverse this-occurrence) out)))
    out))


;; to do transformations like
;;
;; @grk{lalala} → <span lang='grk'>lalala</span>
;; @ruby{base|gloss} → <ruby>base<rt>gloss</rt></ruby>
;;
;; In order to find it easily, we use @ at the start, followed by the
;; function that should be called to take care of the arguments. For
;; the arguments to be potentially infinite, we input them inside {},
;; divided by |, so we always grab all of them inside the {}, and
;; divide them by simply splitting it by |.
;;
;; "@\\([^{]+\\){\\([^}]+\\)}"


(setq weblog-transformations
      '(
        ("gloss" . weblog-gloss)
        ))

(defun weblog-gloss (lst)
  "I expect something like (\"@gloss{δ᾽|autem}\" \"gloss\" \"δ᾽|autem\").
I will split the last item using | and them return a list with
the `car` of what you gave me and the replacement in <ruby> form."
  (let ((original (car lst))
        (arguments (split-string (car (last lst)) "|")))

    (if (> (length arguments) 2)
        (message "You gave me more arguments than I know what to make of them: %s" arguments)
      (concat "@@html:<ruby>"
              (nth 0 arguments)
              " <rt>"
              (nth 1 arguments)
              "</rt> "
              "</ruby>@@ "))))



(defun weblog-regenerate-file-with-transformations ()
  (interactive)
  (save-buffer)
  (let ((changelings (collect-occurrences "@\\([^{]+\\){\\([^}]+\\)}")))
    (dolist (each changelings)
      (goto-char 0)
      (if (assoc (nth 1 each) weblog-transformations)
          (replace-string (nth 0 each)
                          (apply (cdr (assoc (nth 1 each) weblog-transformations))
                                 (list each)))))
  (weblog-regenerate-file)
  (revert-buffer t t)))


;;;; doesn’t work for some reason
;; (defun weblog-regenerate-everything-with-transformations ()
;;   (interactive)
;;   (cd "~/omnia/weblog/")
;;   (let ((victims (directory-files "~/omnia/weblog/" t "^[^#.]+\\.org")))
;;     (dolist (victim victims)
;;       (let* ((victim-name (concat (file-name-base victim) ".html")))
;;      (with-temp-buffer
;;        (insert-file-contents victim)
;;        ;; the transformations
;;        (let ((changelings (collect-occurrences "@\\([^{]+\\){\\([^}]+\\)}")))
;;          (dolist (each changelings)
;;            (goto-char 0)
;;            (if (assoc (nth 1 each) weblog-transformations)
;;                (replace-string (nth 0 each)
;;                                (apply (cdr (assoc (nth 1 each) weblog-transformations))
;;                                       (list each))))))
;;        (org-export-to-file 'html (concat "~/omnia/weblog/out/" victim-name)))))))


(defmacro measure-time (&rest body)
  "Measure the time it takes to evaluate BODY."
  `(let ((time (current-time)))
     ,@body
     (message "%.06f" (float-time (time-since time)))))

· copyright © Edgard Bikelis (eſb) · 2020-03-02 ·
created using Emacs 28.0.50 (Org mode 9.3)