(ns devtools.formatters.markup (:require-macros [devtools.util :refer [oget oset ocall oapply safe-call]] [devtools.formatters.markup :refer [emit-markup-db]]) (:require [devtools.formatters.helpers :refer [bool? cljs-function? cljs-type? cljs-instance? should-render-instance? expandable? abbreviated? abbreviate-long-string get-constructor pref should-render? get-more-marker wrap-arity fetch-fields-values]] [devtools.formatters.printing :refer [managed-print-via-writer managed-print-via-protocol]] [devtools.formatters.state :refer [set-prevent-recursion set-managed-print-level reset-depth-limits]] [devtools.formatters.templating :refer [get-surrogate-body get-surrogate-target get-surrogate-start-index get-surrogate-header]] [devtools.munging :as munging])) ; reusable hiccup-like templates (declare get-markup-db) ; -- cljs printing --------------------------------------------------------------------------------------------------------- (defn print-with [method value tag & [max-level]] (let [job-fn #(method value tag (get-markup-db))] (if (some? max-level) (binding [*print-level* (inc max-level)] ; when printing do at most print-level deep recursion (job-fn)) (job-fn)))) (defn print-via-writer [value tag & [max-level]] (print-with managed-print-via-writer value tag max-level)) (defn print-via-protocol [value tag & [max-level]] (print-with managed-print-via-protocol value tag max-level)) ; -- references ------------------------------------------------------------------------------------------------------------- (defn [& children] (let [inner-markup (concat [:expandable-inner-tag] children)] [:expandable-tag :expandable-symbol inner-markup])) (defn [& args] (concat ["surrogate"] args)) (defn [& [object header body start-index]] (let [header (if (some? body) ( header) header)] ( object header body start-index))) (defn [& args] (concat ["reference"] args)) (defn [& args] ( (apply args))) (defn [& children] ( nil [:circular-reference-tag :circular-ref-icon] (concat [:circular-reference-body-tag] children))) (defn [object] (let [reference ( object #(set-prevent-recursion % true))] [:native-reference-wrapper-tag :native-reference-background [:native-reference-tag reference]])) (defn [object] ( ( object :header-expander-symbol :target) reset-depth-limits)) ; -- simple markup ---------------------------------------------------------------------------------------------------------- (defn [& children] (concat [:cljs-land-tag] children)) (defn [] ; this code is duplicated in templating.cljs, see make-reference [:nil-tag :nil-label]) (defn [bool] [:bool-tag (str bool)]) (defn [keyword] [:keyword-tag (str keyword)]) (defn [symbol] [:symbol-tag (str symbol)]) (defn [number] (if (integer? number) [:integer-tag number] [:float-tag number])) ; -- string markup ---------------------------------------------------------------------------------------------------------- (defn [string] (let [dq (pref :dq) re-nl (js/RegExp. "\n" "g") nl-marker (pref :new-line-string-replacer) inline-string (.replace string re-nl nl-marker) max-inline-string-size (+ (pref :string-prefix-limit) (pref :string-postfix-limit)) quote-string (fn [s] (str dq s dq)) should-abbreviate? (> (count inline-string) max-inline-string-size)] (if should-abbreviate? (let [abbreviated-string (abbreviate-long-string inline-string (pref :string-abbreviation-marker) (pref :string-prefix-limit) (pref :string-postfix-limit)) abbreviated-string-markup [:string-tag (quote-string abbreviated-string)] string-with-nl-markers (.replace string re-nl (str nl-marker "\n")) details-markup [:expanded-string-tag string-with-nl-markers]] ( string abbreviated-string-markup details-markup)) [:string-tag (quote-string inline-string)]))) ; -- generic preview markup ------------------------------------------------------------------------------------------------- (defn [value] (print-via-writer value :header-tag (pref :max-print-level))) ; -- body-related templates ------------------------------------------------------------------------------------------------- (defn [markup] [:body-tag markup]) (defn [markups-lists] (let [prepend-li-tag (fn [line] (if line (concat [:aligned-li-tag] line))) aligned-lines (keep prepend-li-tag markups-lists)] ( (concat [:standard-ol-no-margin-tag] aligned-lines)))) (defn [markups-lists & [no-margin?]] (let [ol-tag (if no-margin? :standard-ol-no-margin-tag :standard-ol-tag) li-tag (if no-margin? :standard-li-no-margin-tag :standard-li-tag) prepend-li-tag (fn [line] (if line (concat [li-tag] line))) lines-markups (keep prepend-li-tag markups-lists)] (concat [ol-tag] lines-markups))) (defn [o] ( [[( o)]])) ; -- generic details markup ------------------------------------------------------------------------------------------------- (defn [value] [:index-tag value :line-index-separator]) (defn- body-line [index value] (let [index-markup ( index) value-markup (print-via-writer value :item-tag (pref :body-line-max-print-level))] [index-markup value-markup])) ; TODO: this fn is screaming for rewrite (defn- prepare-body-lines [data starting-index] (loop [work data index starting-index lines []] (if (empty? work) lines (recur (rest work) (inc index) (conj lines (body-line index (first work))))))) (defn- body-lines [value starting-index] (let [seq (seq value) max-number-body-items (pref :max-number-body-items) chunk (take max-number-body-items seq) rest (drop max-number-body-items seq) lines (prepare-body-lines chunk starting-index) continue? (not (empty? (take 1 rest)))] (if-not continue? lines (let [more-label-markup [:body-items-more-tag :body-items-more-label] start-index (+ starting-index max-number-body-items) more-markup ( rest more-label-markup :target start-index)] (conj lines [more-markup]))))) (defn
[value starting-index] (let [has-continuation? (pos? starting-index) body-markup ( (body-lines value starting-index) has-continuation?)] (if has-continuation? body-markup ( body-markup)))) ; -- generic list template -------------------------------------------------------------------------------------------------- (defn [items _opts] ( (map list items))) (defn [items max-count & [opts]] (let [items-markups (take max-count items) more-count (- (count items) max-count) more? (pos? more-count) separator (or (:separator opts) :list-separator) more-symbol (if more? (if-let [more-symbol (:more-symbol opts)] (if (fn? more-symbol) (more-symbol more-count) more-symbol) (get-more-marker more-count))) preview-markup (concat [(or (:tag opts) :list-tag) (or (:open-symbol opts) :list-open-symbol)] (interpose separator items-markups) (if more? [separator more-symbol]) [(or (:close-symbol opts) :list-close-symbol)])] (if more? (let [details-markup (:details opts) default-details-fn (partial items opts)] ( nil preview-markup (or details-markup default-details-fn))) preview-markup))) ; -- mete-related markup ---------------------------------------------------------------------------------------------------- (defn [metadata] (let [body [:meta-body-tag ( metadata)] header [:meta-header-tag "meta"]] [:meta-reference-tag ( metadata header body)])) (defn [metadata & children] (concat [:meta-wrapper-tag] children [( metadata)])) ; -- function markup -------------------------------------------------------------------------------------------------------- (defn [fn-obj ns _name arities prefix] {:pre [(fn? fn-obj)]} (let [arities (map wrap-arity arities) make-arity-markup-list (fn [arity] [[:fn-multi-arity-args-indent-tag prefix] [:fn-args-tag arity]]) arities-markupts-lists (if (> (count arities) 1) (map make-arity-markup-list arities)) ns-markups-list (if-not (empty? ns) [:ns-icon [:fn-ns-name-tag ns]]) native-markups-list [:native-icon ( fn-obj)]] ( (concat arities-markupts-lists [ns-markups-list native-markups-list])))) (defn [arities] (let [multi-arity? (> (count arities) 1)] [:fn-args-tag (wrap-arity (if multi-arity? (pref :multi-arity-symbol) (first arities)))])) (defn [fn-obj] {:pre [(fn? fn-obj)]} (let [[ns name] (munging/parse-fn-info fn-obj) lambda? (empty? name) spacer-symbol (pref :spacer) rest-symbol (pref :rest-symbol) multi-arity-symbol (pref :multi-arity-symbol) arities (munging/extract-arities fn-obj true spacer-symbol multi-arity-symbol rest-symbol) arities-markup ( arities) name-markup (if-not lambda? [:fn-name-tag name]) icon-markup (if lambda? :lambda-icon :fn-icon) prefix-markup [:fn-prefix-tag icon-markup name-markup] preview-markup [:fn-header-tag prefix-markup arities-markup] details-fn (partial fn-obj ns name arities prefix-markup)] ( fn-obj preview-markup details-fn))) ; -- type markup ------------------------------------------------------------------------------------------------------------ (defn [basis-item] [:type-basis-item-tag (name basis-item)]) (defn [basis] (let [item-markups (map basis) children-markups (interpose :type-basis-item-separator item-markups)] (concat [:type-basis-tag] children-markups))) (defn [constructor-fn ns _name basis] (let [ns-markup (if-not (empty? ns) [:ns-icon [:fn-ns-name-tag ns]]) basis-markup (if (empty? basis) [:empty-basis-symbol] [:basis-icon ( basis)]) native-markup [:native-icon ( constructor-fn)]] ( [basis-markup ns-markup native-markup]))) (defn [constructor-fn & [header-tag]] (let [[ns name basis] (munging/parse-constructor-info constructor-fn) name-markup [:type-name-tag name] preview-markup [(or header-tag :type-header-tag) :type-symbol name-markup] details-markup-fn (partial constructor-fn ns name basis)] [:type-wrapper-tag :type-header-background [:type-ref-tag ( constructor-fn preview-markup details-markup-fn)]])) (defn [constructor-fn & [header-tag]] [:standalone-type-tag ( constructor-fn header-tag)]) ; -- protocols markup ------------------------------------------------------------------------------------------------------- (defn [arity-fn] ( arity-fn)) (defn [fns] ( (map fns))) (defn [fns & [max-fns]] (let [max-fns (or max-fns (pref :max-protocol-method-arities-list)) more? (> (count fns) max-fns) aritites-markups (map (take max-fns fns)) preview-markup (concat [:protocol-method-arities-header-tag :protocol-method-arities-header-open-symbol] (interpose :protocol-method-arities-list-header-separator aritites-markups) (if more? [:protocol-method-arities-more-symbol]) [:protocol-method-arities-header-close-symbol])] (if more? (let [details-markup-fn (partial fns)] ( nil preview-markup details-markup-fn)) preview-markup))) (defn [name arities] [:protocol-method-tag :method-icon [:protocol-method-name-tag name] ( arities)]) (defn [obj ns _name selector _fast?] (let [protocol-obj (munging/get-protocol-object selector) ns-markups-list (if-not (empty? ns) [:ns-icon [:protocol-ns-name-tag ns]]) native-markups-list (if (some? protocol-obj) [:native-icon ( protocol-obj)]) methods (munging/collect-protocol-methods obj selector) methods-markups (map (fn [[name arities]] ( name arities)) methods) methods-markups-lists (map list methods-markups)] ( (concat methods-markups-lists [ns-markups-list native-markups-list])))) (defn [obj protocol & [tag]] (let [{:keys [ns name selector fast?]} protocol preview-markup [(or tag :protocol-name-tag) name] prefix-markup [(if fast? :fast-protocol-tag :slow-protocol-tag) :protocol-background]] (if (some? obj) (let [details-markup-fn (partial obj ns name selector fast?)] (conj prefix-markup ( obj preview-markup details-markup-fn))) (conj prefix-markup preview-markup)))) (defn [more-count] (let [fake-protocol {:name (get-more-marker more-count)}] ( nil fake-protocol :protocol-more-tag))) (defn [obj protocols & [max-protocols]] (let [max-protocols (or max-protocols (pref :max-list-protocols)) protocols-markups (map (partial obj) protocols)] ( protocols-markups max-protocols {:tag :protocols-header-tag :open-symbol :protocols-list-open-symbol :close-symbol :protocols-list-close-symbol :separator :header-protocol-separator :more-symbol }))) ; -- instance fields markup ------------------------------------------------------------------------------------------------- (defn [name value] [:header-field-tag [:header-field-name-tag (str name)] :header-field-value-spacer [:header-field-value-tag ( ( value) #(set-managed-print-level % 1))] :header-field-separator]) (defn [field] (let [[name value] field] [:body-field-tr-tag [:body-field-td1-tag :body-field-symbol [:body-field-name-tag (str name)]] [:body-field-td2-tag :body-field-value-spacer] [:body-field-td3-tag [:body-field-value-tag ( value)]]])) (defn [fields & [max-fields]] (if (zero? (count fields)) [:fields-header-tag :fields-header-no-fields-symbol] (let [max-fields (or max-fields (pref :max-instance-header-fields)) more? (> (count fields) max-fields) fields-markups (map (fn [[name value]] ( name value)) (take max-fields fields))] (concat [:fields-header-tag :fields-header-open-symbol] fields-markups [(if more? :more-fields-symbol) :fields-header-close-symbol])))) (defn [fields obj] (let [protocols (munging/scan-protocols obj) has-protocols? (not (empty? protocols)) fields-markup (if-not (zero? (count fields)) [:fields-icon (concat [:instance-body-fields-table-tag] (map fields))]) protocols-list-markup (if has-protocols? [:protocols-icon ( obj protocols)]) native-markup [:native-icon ( obj)]] ( [fields-markup protocols-list-markup native-markup]))) ; -- type/record instance markup -------------------------------------------------------------------------------------------- (defn [value] (let [constructor-fn (get-constructor value) [_ns _name basis] (munging/parse-constructor-info constructor-fn) custom-printing? (implements? IPrintWithWriter value) type-markup ( constructor-fn :instance-type-header-tag) fields (fetch-fields-values value basis) fields-markup ( fields (if custom-printing? 0)) fields-details-markup-fn #( fields value) fields-preview-markup [:instance-value-tag ( value fields-markup fields-details-markup-fn)] custom-printing-markup (if custom-printing? [:instance-custom-printing-wrapper-tag :instance-custom-printing-background (print-via-protocol value :instance-custom-printing-tag)])] [:instance-header-tag :instance-header-background fields-preview-markup custom-printing-markup type-markup])) ; --------------------------------------------------------------------------------------------------------------------------- (defn
[value] ( ( value))) (defn [surrogate] (or (get-surrogate-header surrogate) ( (get-surrogate-target surrogate)))) (defn [surrogate] (let [target (get-surrogate-target surrogate)] (if (seqable? target) (let [starting-index (get-surrogate-start-index surrogate)] (
target starting-index)) ( target)))) (defn [surrogate] (if-let [body (get-surrogate-body surrogate)] (if (= :target body) ( surrogate) body))) ; --------------------------------------------------------------------------------------------------------------------------- (defn [value] (cond (should-render? :render-nils value nil?) () (should-render? :render-bools value bool?) ( value) (should-render? :render-strings value string?) ( value) (should-render? :render-numbers value number?) ( value) (should-render? :render-keywords value keyword?) ( value) (should-render? :render-symbols value symbol?) ( value) (should-render? :render-instances value should-render-instance?) ( value) (should-render? :render-types value cljs-type?) ( value) (should-render? :render-functions value cljs-function?) ( value))) ; --------------------------------------------------------------------------------------------------------------------------- (def ^:dynamic *markup-db*) ; emit-markup-db macro will generate a map of all markup in this namespace: ; ; {:atomic ; :reference ; :native-reference ; ...} ; ; we generate it only on first call and cache it in *markup-db* ; emitting markup db statically into def would prevent dead-code elimination ; (defn get-markup-db [] (if (nil? *markup-db*) (set! *markup-db* (emit-markup-db))) *markup-db*)