fix(outliner): support random blocks passed into blocks->vec-tree

pull/1656/head
defclass 2021-03-26 21:36:17 +08:00
parent 3e848232c2
commit abc529cb9f
2 changed files with 176 additions and 53 deletions

View File

@ -62,6 +62,78 @@
other-children)]
(recur others children))))))))
(defn ->db-id
[x]
(cond
(map? x)
(:db/id x)
(number? x)
x
:else
(throw (js/Error "Unknown db/id format"))))
(defn- prepare-blocks
"Preparing blocks: index blocks,filter ids,and update some keys."
[blocks]
(loop [[f & r] blocks
ids #{}
parents #{}
;; {[parent left] uuid}
indexed-by-position {}
;; {db-id block}
indexed-by-id {}]
(if (nil? f)
{:ids ids :parents parents
:indexed-by-position indexed-by-position
:indexed-by-id indexed-by-id}
(let [f (cond-> f
(not (:block/dummy? f))
(dissoc f :block/meta))
{:block/keys [parent left] db-id :db/id} f
new-ids (conj ids db-id)
new-parents (conj parents (->db-id parent))
new-indexed-by-position
(let [position (mapv ->db-id [parent left])]
(when (get indexed-by-position position)
(throw (js/Error. "Two block occupy the same position")))
(assoc indexed-by-position position db-id))
new-indexed-by-id
(assoc indexed-by-id db-id f)]
(recur r new-ids new-parents
new-indexed-by-position new-indexed-by-id)))))
(defn- find-last-node
[root-node-id indexed-by-position indexed-by-id]
(assert (some? root-node-id) "root-node-id should satisfy some?.")
(assert (and (map? indexed-by-position) (seq indexed-by-position))
"indexed-position's format is wrong.")
(assert (and (map? indexed-by-id) (seq indexed-by-id))
"indexed-by-id's format is wrong.")
(let [init-node {:db/id root-node-id}]
(loop [node init-node
;; from top to bottom
;; direction :down :right
direction :down]
(case direction
:down
(let [id (:db/id node)]
(if-let [new-node-db-id (get indexed-by-position [id id])]
(let [new-node (get indexed-by-id new-node-db-id)]
(recur new-node :right))
node))
:right
(let [{parent :block/parent db-id :db/id} node]
(if-let [new-node-db-id (get indexed-by-position [(->db-id parent) db-id])]
(let [new-node (get indexed-by-id new-node-db-id)]
(recur new-node :right))
(recur node :down)))
(throw (js/Error. (util/format "Unknown direction: %s" direction)))))))
(defn- get-all-refs
[block]
(let [refs (if-let [refs (seq (:block/refs-with-children block))]
@ -69,46 +141,42 @@
(:block/refs block))]
(distinct refs)))
(defn prepare-blocks
"Preparing blocks: reverse, update some keys"
[blocks]
(loop [[f & r] blocks
new (list)]
(if (nil? f)
new
(let [f (cond-> f
(not (:block/dummy? f))
(dissoc f :block/meta))]
(recur r (cons f new))))))
(defn- wrap-refs-with-children
([block]
(->> (get-all-refs block)
(remove nil?)
(assoc block :block/refs-with-children)))
([block other-children]
(->>
(cons block other-children)
(mapcat get-all-refs)
(remove nil?)
distinct)))
(defn blocks->vec-tree-by-parent
[col]
(let [blocks (prepare-blocks col)]
(loop [[f & r] blocks
tree (list)
parent? false]
(if (nil? f)
tree
(let [{:block/keys [parent left]} f
f (assoc f :block/refs-with-children
(->> (get-all-refs f) (remove nil?)))]
(cond
parent?
(let [refs-with-children (->> (mapcat get-all-refs (cons f tree))
(remove nil?)
distinct)
(let [{:keys [ids parents indexed-by-position indexed-by-id]} (prepare-blocks col)
root-id (first (set/difference parents ids))
last-node (find-last-node root-id indexed-by-position indexed-by-id)
last-node (wrap-refs-with-children last-node)]
(loop [{:block/keys [parent left] :as node} last-node
tree (list node)
;; from bottom to top
;; direction :up :left
direction (if (= parent left) :up :left)]
(if-let [left-node (get indexed-by-id (->db-id left))]
(let [new-direction (if (= (:block/parent left-node) (:block/left left-node)) :up :left)
left-node (wrap-refs-with-children left-node)]
(case direction
:left
(recur left-node (conj tree left-node) new-direction)
:up
(let [refs-with-children (wrap-refs-with-children left-node tree)
new-ks {:block/children tree
:block/refs-with-children refs-with-children}
f (-> (merge f new-ks) (list))]
(if (not= parent left)
(recur r f false)
(recur r f true)))
(not= parent left)
(recur r (conj tree f) false)
(= parent left) ; first child of parent
(recur r (conj tree f) true)))))))
tree (merge left-node new-ks)]
(recur left-node (list tree) new-direction))))
tree))))
;; recursively with children content for tree
(defn get-block-content-rec

View File

@ -3,63 +3,118 @@
[cljs-run-test :refer [run-test]]
[frontend.handler.block :as block]))
(def blocks->vec-tree-args
(def blocks->vec-tree-sequential-block-args
'[{:block/parent {:db/id 20},
:block/left {:db/id 20},
:block/pre-block? true,
:block/level 2}
{:block/parent {:db/id 20},
:block/left {:db/id 25},
:block/title [["Plain" "level 1"]],
:block/level 2}
{:block/parent {:db/id 26},
:block/left {:db/id 20},
:db/id 25
:block/uuid "uuid-25"
:block/pre-block? true,
:block/level 2}
{:block/parent {:db/id 20},
:block/left {:db/id 25},
:db/id 26
:block/uuid "uuid-26"
:block/title [["Plain" "level 1"]],
:block/level 2}
{:block/parent {:db/id 26},
:block/left {:db/id 26},
:db/id 27
:block/uuid "uuid-27"
:block/title [["Plain" "level 2"]],
:block/level 3}
{:block/parent {:db/id 27},
:block/left {:db/id 27},
:db/id 28
:block/uuid "uuid-28"
:block/title [["Plain" "level 3"]],
:block/level 4}
{:block/parent {:db/id 27},
:block/left {:db/id 28},
:db/id 29
:block/uuid "uuid-29"
:block/title [],
:block/level 4}])
(def blocks->vec-tree-random-block-args
'[{:block/parent {:db/id 26},
:block/left {:db/id 26},
:db/id 27
:block/uuid "uuid-27"
:block/title [["Plain" "level 2"]],
:block/level 3}
{:block/parent {:db/id 27},
:block/left {:db/id 27},
:block/title [["Plain" "level 3"]],
:block/level 4}
{:block/parent {:db/id 20},
:block/left {:db/id 25},
:db/id 26
:block/uuid "uuid-26"
:block/title [["Plain" "level 1"]],
:block/level 2}
{:block/parent {:db/id 20},
:block/left {:db/id 20},
:db/id 25
:block/uuid "uuid-25"
:block/pre-block? true,
:block/level 2}
{:block/parent {:db/id 27},
:block/left {:db/id 28},
:db/id 29
:block/uuid "uuid-29"
:block/title [],
:block/level 4}
{:block/parent {:db/id 27},
:block/left {:db/id 27},
:db/id 28
:block/uuid "uuid-28"
:block/title [["Plain" "level 3"]],
:block/level 4}])
(def blocks->vec-tree-return
'({:block/parent {:db/id 20},
'[{:block/parent {:db/id 20},
:block/left {:db/id 20},
:db/id 25,
:block/uuid "uuid-25",
:block/pre-block? true,
:block/level 2,
:block/refs-with-children ()}
{:block/parent {:db/id 20},
:block/left {:db/id 25},
:db/id 26,
:block/uuid "uuid-26",
:block/title [["Plain" "level 1"]],
:block/level 2,
:block/refs-with-children (),
:block/children
({:block/parent {:db/id 26},
:block/left {:db/id 26},
:db/id 27,
:block/uuid "uuid-27",
:block/title [["Plain" "level 2"]],
:block/level 3,
:block/refs-with-children (),
:block/children
({:block/parent {:db/id 27},
:block/left {:db/id 27},
:db/id 28,
:block/uuid "uuid-28",
:block/title [["Plain" "level 3"]],
:block/level 4,
:block/refs-with-children ()}
{:block/parent {:db/id 27},
:block/left {:db/id 28},
:db/id 29,
:block/uuid "uuid-29",
:block/title [],
:block/level 4,
:block/refs-with-children ()})})}))
:block/refs-with-children ()})})}])
(deftest test-blocks->vec-tree
(let [should-r (vec blocks->vec-tree-return)]
(let [r (block/blocks->vec-tree blocks->vec-tree-args)]
(let [r (block/blocks->vec-tree blocks->vec-tree-sequential-block-args)]
(is (= should-r (vec r))))
(let [r (block/blocks->vec-tree-by-parent blocks->vec-tree-args)]
(let [r (block/blocks->vec-tree-by-parent blocks->vec-tree-sequential-block-args)]
(is (= should-r (vec r))))))
(run-test test-blocks->vec-tree)
(deftest test-blocks->vec-tree-random-block
(let [should-r (vec blocks->vec-tree-return)
r (block/blocks->vec-tree-by-parent blocks->vec-tree-random-block-args)]
(is (= should-r (vec r)))))