89 lines
4.1 KiB
Clojure
89 lines
4.1 KiB
Clojure
(ns devtools.formatters.budgeting
|
|
(:require-macros [devtools.oops :refer [oget oset ocall]])
|
|
(:require [devtools.formatters.templating :refer [render-markup]]
|
|
[devtools.formatters.state :refer [get-depth-budget set-depth-budget]]
|
|
[devtools.formatters.helpers :refer [pref]]
|
|
[devtools.formatters.markup :refer [<header-expander>]]))
|
|
|
|
; This functionality provides a workaround to issue #22 (https://github.com/binaryage/cljs-devtools/issues/22).
|
|
; The idea is to track hierarchy depth for json-ml(s) we are generating.
|
|
; If we are about to cross the depth limit hardcoded in WebKit,
|
|
; instead we render simple expandable placeholders which resume full rendering in their bodies (when expanded by user).
|
|
; Note that this technique has some quirks, it may break styling in some pathological cases.
|
|
|
|
; we need to reserve some depth levels for our expander symbol markup
|
|
(def header-expander-depth-cost 2)
|
|
|
|
; -- tracking over-budget values -------------------------------------------------------------------------------------------
|
|
|
|
; note: phantomjs does not have WeakSet, so we have to emulate it when testing
|
|
(def over-budget-values (if (exists? js/WeakSet) (js/WeakSet.) (volatile! #{})))
|
|
|
|
(defn add-over-budget-value! [value]
|
|
(if (volatile? over-budget-values)
|
|
(vreset! over-budget-values (conj @over-budget-values value))
|
|
(ocall over-budget-values "add" value)))
|
|
|
|
(defn delete-over-budget-value! [value]
|
|
(if (volatile? over-budget-values)
|
|
(vreset! over-budget-values (disj @over-budget-values value))
|
|
(ocall over-budget-values "delete" value)))
|
|
|
|
(defn has-over-budget-value? [value]
|
|
(if (volatile? over-budget-values)
|
|
(contains? @over-budget-values value)
|
|
(ocall over-budget-values "has" value)))
|
|
|
|
; -- depth budget accounting ------------------------------------------------------------------------------------------------
|
|
|
|
(defn object-reference? [json-ml]
|
|
(= (first json-ml) "object"))
|
|
|
|
(defn determine-depth [json-ml]
|
|
(if (array? json-ml)
|
|
(inc (apply max (map determine-depth json-ml)))
|
|
0))
|
|
|
|
(defn has-any-object-reference? [json-ml]
|
|
(if (array? json-ml)
|
|
(if (object-reference? json-ml)
|
|
true
|
|
(some has-any-object-reference? json-ml))))
|
|
|
|
(defn transfer-remaining-depth-budget! [object-reference depth-budget]
|
|
{:pre [(not (neg? depth-budget))]}
|
|
(let [data (second object-reference)
|
|
_ (assert (object? data))
|
|
config (oget data "config")]
|
|
(oset data ["config"] (set-depth-budget config depth-budget))))
|
|
|
|
(defn distribute-budget! [json-ml depth-budget]
|
|
{:pre [(not (neg? depth-budget))]}
|
|
(if (array? json-ml)
|
|
(let [new-depth-budget (dec depth-budget)]
|
|
(if (object-reference? json-ml)
|
|
(transfer-remaining-depth-budget! json-ml new-depth-budget)
|
|
(doseq [item json-ml]
|
|
(distribute-budget! item new-depth-budget)))))
|
|
json-ml)
|
|
|
|
; -- api --------------------------------------------------------------------------------------------------------------------
|
|
|
|
(defn was-over-budget?! [value]
|
|
(when (has-over-budget-value? value)
|
|
(delete-over-budget-value! value)
|
|
true))
|
|
|
|
(defn alter-json-ml-to-fit-in-remaining-budget! [value json-ml]
|
|
(if-let [initial-hierarchy-depth-budget (pref :initial-hierarchy-depth-budget)] ; this is hardcoded in InjectedScriptSource.js in WebKit, look for maxCustomPreviewRecursionDepth
|
|
(let [remaining-depth-budget (or (get-depth-budget) (dec initial-hierarchy-depth-budget))
|
|
depth (determine-depth json-ml)
|
|
final? (not (has-any-object-reference? json-ml))
|
|
needed-depth (if final? depth (+ depth header-expander-depth-cost))]
|
|
(if (>= remaining-depth-budget needed-depth)
|
|
(distribute-budget! json-ml remaining-depth-budget)
|
|
(let [expander-ml (render-markup (<header-expander> value))]
|
|
(add-over-budget-value! value) ; we need to record over-budget values to for later was-over-budget?! check, see has-body* in formatters.core
|
|
expander-ml)))
|
|
json-ml))
|