geocsv-js/docs/js/compiled/out/devtools/formatters/templating.cljs

210 lines
7.4 KiB
Clojure

(ns devtools.formatters.templating
(:require-macros [devtools.oops :refer [oget oset ocall oapply safe-call unchecked-aget]])
(:require [clojure.walk :refer [prewalk]]
[devtools.util :refer [pprint-str]]
[devtools.protocols :refer [ITemplate IGroup ISurrogate IFormat]]
[devtools.formatters.helpers :refer [pref cljs-value?]]
[devtools.formatters.state :refer [get-current-state prevent-recursion?]]
[clojure.string :as string]))
; -- object marking support -------------------------------------------------------------------------------------------------
(defn mark-as-group! [value]
(specify! value IGroup)
value)
(defn group? [value]
(satisfies? IGroup value))
(defn mark-as-template! [value]
(specify! value ITemplate)
value)
(defn template? [value]
(satisfies? ITemplate value))
(defn mark-as-surrogate! [value]
(specify! value ISurrogate)
value)
(defn surrogate? [value]
(satisfies? ISurrogate value))
(defn reference? [value]
(and (group? value)
(= (unchecked-aget value 0) "object")))
; ---------------------------------------------------------------------------------------------------------------------------
(defn make-group [& items]
(let [group (mark-as-group! #js [])]
(doseq [item items]
(if (some? item)
(if (coll? item)
(.apply (unchecked-aget group "push") group (mark-as-group! (into-array item))) ; convenience helper to splat cljs collections
(.push group (pref item)))))
group))
(defn make-template
[tag style & children]
(let [tag (pref tag)
style (pref style)
template (mark-as-template! #js [tag (if (empty? style)
#js {}
#js {"style" style})])]
(doseq [child children]
(if (some? child)
(if (coll? child)
(.apply (unchecked-aget template "push") template (mark-as-template! (into-array (keep pref child)))) ; convenience helper to splat cljs collections
(if-let [child-value (pref child)]
(.push template child-value)))))
template))
(defn concat-templates! [template & templates]
(mark-as-template! (.apply (oget template "concat") template (into-array (map into-array (keep pref templates))))))
(defn extend-template! [template & args]
(concat-templates! template args))
(defn make-surrogate
; passing :target as body means that targt object body should be rendered using standard templates
; see <surrogate-body> in markup.cljs
([object] (make-surrogate object nil))
([object header] (make-surrogate object header nil))
([object header body] (make-surrogate object header body 0))
([object header body start-index]
(mark-as-surrogate! (js-obj
"target" object
"header" header
"body" body
"startIndex" (or start-index 0)))))
(defn get-surrogate-target [surrogate]
{:pre [(surrogate? surrogate)]}
(oget surrogate "target"))
(defn get-surrogate-header [surrogate]
{:pre [(surrogate? surrogate)]}
(oget surrogate "header"))
(defn get-surrogate-body [surrogate]
{:pre [(surrogate? surrogate)]}
(oget surrogate "body"))
(defn get-surrogate-start-index [surrogate]
{:pre [(surrogate? surrogate)]}
(oget surrogate "startIndex"))
(defn make-reference [object & [state-override-fn]]
{:pre [(or (nil? state-override-fn) (fn? state-override-fn))]}
(if (nil? object)
; this code is duplicated in markup.cljs <nil>
(make-template :span :nil-style :nil-label)
(let [sub-state (if (some? state-override-fn)
(state-override-fn (get-current-state))
(get-current-state))]
(make-group "object" #js {"object" object
"config" sub-state}))))
; -- JSON ML support --------------------------------------------------------------------------------------------------------
; a renderer from hiccup-like data markup to json-ml
;
; [[tag style] child1 child2 ...] -> #js [tag #js {"style" ...} child1 child2 ...]
;
(declare render-json-ml*)
(def ^:dynamic *current-render-stack* [])
(def ^:dynamic *current-render-path* [])
(defn print-preview [markup]
(binding [*print-level* 1]
(pr-str markup)))
(defn add-stack-separators [stack]
(interpose "-------------" stack))
(defn replace-fns-with-markers [stack]
(let [f (fn [v]
(if (fn? v)
"##fn##"
v))]
(prewalk f stack)))
(defn pprint-render-calls [stack]
(map pprint-str stack))
(defn pprint-render-stack [stack]
(string/join "\n" (-> stack
reverse
replace-fns-with-markers
pprint-render-calls
add-stack-separators)))
(defn pprint-render-path [path]
(pprint-str path))
(defn assert-markup-error [msg]
(assert false (str msg "\n"
"Render path: " (pprint-render-path *current-render-path*) "\n"
"Render stack:\n"
(pprint-render-stack *current-render-stack*))))
(defn surrogate-markup? [markup]
(and (sequential? markup) (= (first markup) "surrogate")))
(defn render-special [name args]
(case name
"surrogate" (let [obj (first args)
converted-args (map render-json-ml* (rest args))]
(apply make-surrogate (concat [obj] converted-args)))
"reference" (let [obj (first args)
converted-obj (if (surrogate-markup? obj) (render-json-ml* obj) obj)]
(apply make-reference (concat [converted-obj] (rest args))))
(assert-markup-error (str "no matching special tag name: '" name "'"))))
(defn emptyish? [v]
(if (or (seqable? v) (array? v) (string? v))
(empty? v)
false))
(defn render-subtree [tag children]
(let [[html-tag style] tag]
(apply make-template html-tag style (map render-json-ml* (remove emptyish? (map pref children))))))
(defn render-json-ml* [markup]
(if-not (sequential? markup)
markup
(binding [*current-render-path* (conj *current-render-path* (first markup))]
(let [tag (pref (first markup))]
(cond
(string? tag) (render-special tag (rest markup))
(sequential? tag) (render-subtree tag (rest markup))
:else (assert-markup-error (str "invalid json-ml markup at " (print-preview markup) ":")))))))
(defn render-json-ml [markup]
(binding [*current-render-stack* (conj *current-render-stack* markup)
*current-render-path* (conj *current-render-path* "<render-json-ml>")]
(render-json-ml* markup)))
; -- template rendering -----------------------------------------------------------------------------------------------------
(defn ^:dynamic assert-failed-markup-rendering [initial-value value]
(assert false (str "result of markup rendering must be a template,\n"
"resolved to " (pprint-str value)
"initial value: " (pprint-str initial-value))))
(defn render-markup* [initial-value value]
(cond
(fn? value) (recur initial-value (value))
(keyword? value) (recur initial-value (pref value))
(sequential? value) (recur initial-value (render-json-ml value))
(template? value) value
(surrogate? value) value
(reference? value) value
:else (assert-failed-markup-rendering initial-value value)))
(defn render-markup [value]
(render-markup* value value))