A very long way towards extensible formatters

This commit is contained in:
Simon Brooke 2017-08-07 23:21:45 +01:00
parent 91367e40aa
commit 19a715c4fb

View file

@ -6,6 +6,7 @@
[noir.io :as io]
[noir.session :as session]
[markdown.core :as md]
[taoensso.timbre :as timbre]
[smeagol.authenticate :as auth]
[clj-yaml.core :as yaml]
[clojure.data.json :as json]))
@ -35,7 +36,7 @@
;;;;
;;;; Right, doing the data visualisation thing is tricky. Doing it in the
;;;; pipeline doesn't work, because the md-to-html-string filter messes up
;;;; both YAML and JSON notation. So we need to extract the visualisation YAML
;;;; both YAML and JSON notation. So we need to extract the visualisation
;;;; fragments from the Markdown text and replace them with tokens we will
;;;; recognise afterwards, perform md-to-html-string, and then replace our
;;;; tokens with the transformed visualisation specification.
@ -88,6 +89,63 @@
"\n</div>"))
;; TODO: This isn't (yet) exactly what I want. The formatters ought to be configurable
;; without editing the Smeagol code directly. But it's a long way in the right direction.
(def ^:dynamic *formatters*
{"vega" process-vega
"vis" process-vega
"mermaid" process-mermaid})
(defn get-first-token
"Return the first space-separated token of this `string`."
[^String string]
(if string (first (cs/split string #"[^a-zA-Z0-9]+"))))
(defn- process-markdown-fragment
"Within the context of `process-text`, process a fragment believed to be markdown.
As with `process-text`, this function returns a map with two top-level keys:
`:inclusions`, a map of constructed keywords to inclusion specifications,
and `:text`, an HTML text string with the keywords present where the
corresponding inclusion should be inserted."
[index result fragments processed]
(process-text
(+ index 1)
result
(rest fragments)
(cons (first fragments) processed)))
(defn- apply-formatter
"Within the context of `process-text`, process a fragment for which an explicit
§formatter has been identified.
As with `process-text`, this function returns a map with two top-level keys:
`:inclusions`, a map of constructed keywords to inclusion specifications,
and `:text`, an HTML text string with the keywords present where the
corresponding inclusion should be inserted."
[index result fragments processed fragment token formatter]
(let
[kw (keyword (str "inclusion-" index))]
(process-text
(+ index 1)
(assoc
result
:inclusions
(assoc
(:inclusions result)
kw
(apply
formatter
(list
(subs fragment (count token))
index))))
(rest fragments)
(cons kw processed))))
(defn process-text
"Process this `text`, assumed to be markdown potentially containing both local links
and YAML visualisation specifications, and return a map comprising JSON visualisation
@ -99,6 +157,9 @@
([text]
(process-text 0 {:inclusions {}} (cs/split text #"```") '()))
([index result fragments processed]
(let [fragment (first fragments)
first-token (get-first-token fragment)
formatter (*formatters* first-token)]
(cond
(empty? fragments)
(assoc result :text
@ -106,39 +167,10 @@
(md/md-to-html-string
(cs/join "\n\n" (reverse processed))
:heading-anchors true)))
;;; TODO: refactor; generalise extension architecture
(clojure.string/starts-with? (first fragments) "vis")
(let [kw (keyword (str "inclusion-" index))]
(process-text
(+ index 1)
(assoc
result
:inclusions
(assoc
(:inclusions result)
kw
(process-vega
(subs (first fragments) 3)
index)))
(rest fragments)
(cons kw processed)))
(clojure.string/starts-with? (first fragments) "mermaid")
(let [kw (keyword (str "inclusion-" index))]
(process-text
(+ index 1)
(assoc
result
:inclusions
(assoc
(:inclusions result)
kw
(process-mermaid
(subs (first fragments) 7)
index)))
(rest fragments)
(cons kw processed)))
formatter
(apply-formatter index result fragments processed fragment first-token formatter)
true
(process-text (+ index 1) result (rest fragments) (cons (first fragments) processed)))))
(process-markdown-fragment index result fragments processed)))))
(defn reintegrate-inclusions