From 40d24a500b6a9062122147be332a41fe48a8371a Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Sun, 15 Oct 2023 00:18:54 +1100 Subject: Unify emacs packages with webapps: use bom as an example Emacs commands: bom; bom-state Paths in webapps: /bom; /bom-state/ Next step: generalise the transformation with macros etc. --- bom.el | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 16 deletions(-) (limited to 'bom.el') diff --git a/bom.el b/bom.el index 06b9784..d70226d 100644 --- a/bom.el +++ b/bom.el @@ -125,25 +125,62 @@ Used as parentfn for hierarchy." "")) (defun bom-format-areas-org (areas) - "Format hierarchy of AREAS with forecasts. + "Format hierarchy of AREAS with forecasts into an org string. -We use the description of the first root as the state name." +We use the description of the first root as the state name. + +The org string is ready to be inserted into an empty buffer for +display or export." (let ((state-name (dom-attr (car (hierarchy-roots areas)) 'description))) - (format "#+title: %s weather forecast\n\n%s" + (format "#+title: %s weather forecast + +[[elisp:(bom)][Back]] + +%s" state-name (string-join (hierarchy-map 'bom-format-area areas 1) "\n")))) +(defun bom-to-path (sexp) + "Given an SEXP, return the corresponding path. + +For example, (foo \"bar\" \"baz\") is converted to /foo/bar/baz." + (pcase-let ((`(,func . ,args) (read sexp))) + (unless (and (symbolp func) + (memq func '(bom bom-state)) + (every 'stringp args)) + (user-error "Invalid sexp: %s" sexp)) + (format "/%s" + (string-join (cons (format "%s" func) args) "/")))) + +(defun bom-elisp-link-export (link desc format) + "An :export function for elisp: links. + +Takes a LINK with description DESC, assuming FORMAT is html, +output the http path." + (cl-assert (eq format 'html)) + (format "%s" + (bom-to-path link) + (or desc link))) + (defun bom-org-to-html (s) "Export org string S to html and return the html string." (with-temp-buffer (insert s) - (let ((org-html-postamble)) - (org-export-as 'html)))) + (let ((org-html-postamble) + (org-link-parameters + (cons '("elisp" :export bom-elisp-link-export) + org-link-parameters))) + (let ((result + (org-export-as 'html))) + (pop org-link-parameters) + result)))) (defun bom-format-areas-html (areas) - "Format hierarchy of AREAS with forecasts to html." + "Format hierarchy of AREAS with forecasts to an html string. + +The html string can be sent as a web server response." (bom-org-to-html (bom-format-areas-org areas))) (defun bom-get-forecasts (state) @@ -156,26 +193,62 @@ We use the description of the first root as the state name." (hierarchy-sort areas 'bom-area-lessp) areas)) -(defun bom-format-state-lists-org () +(defun bom-state-org (state) + (bom-format-areas-org + (bom-get-forecasts state))) + +(defun bom-org () + "Format the \"landing page\" org string. + +The string is ready for display or export." (format - "#+title: Australia weather forecast\n%s" + "#+title: Australia weather forecast + +%s" (mapconcat - (lambda (pair) (format "[[file:%s][%s]]" + (lambda (pair) (format "[[elisp:(bom-state \"%s\")][%s]]" (car pair) (upcase (format "%s" (car pair))))) bom-state-files " "))) -(defun bom-format-state-lists-html () - (bom-org-to-html (bom-format-state-lists-org))) +;;;###autoload +(defun bom-state (state) + "Open bom forecast for STATE." + (interactive "sState: ") + (with-current-buffer (get-buffer-create "*bom*") + (erase-buffer) + (insert (bom-states-org state)) + (goto-char (point-min)) + (org-mode)) + (display-buffer "*bom*")) + +;;;###autoload +(defun bom () + "Entrypoint - open bom landing page." + (interactive) + (with-current-buffer (get-buffer-create "*bom*") + (erase-buffer) + (insert (bom-org)) + (goto-char (point-min)) + (org-mode)) + (display-buffer "*bom*")) + +(defun bom-from-path (path) + "Translate a GET PATH to a pair of function and string args. + +For example, if path is /foo/bar/baz, then we return +(foo . (\"bar\" \"baz\"))" + (unless (string-prefix-p "/" path) + (user-error "Invalid path: %s" path)) + (pcase-let ((`(,func-name . ,args) (cdr (split-string path "/")))) + (unless (memq (intern func-name) '(bom bom-state)) + (user-error "Invalid path: %s" path)) + (cons (intern (format "%s-org" func-name)) args))) (defun bom-serve (path) "Serve based on PATH." - (setq path (substring path 1)) - (if (string-empty-p path) - (bom-format-state-lists-html) - (bom-format-areas-html - (bom-get-forecasts path)))) + (bom-org-to-html (apply (bom-from-path path)))) ;;;###autoload (defun bom-start () -- cgit v1.2.3