mirror of https://github.com/logseq/logseq
enhance(undo): undo/redo is page-scoped now
parent
37d6196000
commit
53970880d3
|
@ -675,15 +675,15 @@
|
|||
(transit/write transit-w (rtc-core/get-block-update-log (uuid block-uuid))))
|
||||
|
||||
(undo
|
||||
[_this repo]
|
||||
[_this repo page-block-uuid-str]
|
||||
(when-let [conn (worker-state/get-datascript-conn repo)]
|
||||
(undo-redo/undo repo conn))
|
||||
(undo-redo/undo repo (uuid page-block-uuid-str) conn))
|
||||
nil)
|
||||
|
||||
(redo
|
||||
[_this repo]
|
||||
[_this repo page-block-uuid-str]
|
||||
(when-let [conn (worker-state/get-datascript-conn repo)]
|
||||
(undo-redo/redo repo conn)))
|
||||
(undo-redo/redo repo (uuid page-block-uuid-str) conn)))
|
||||
|
||||
(keep-alive
|
||||
[_this]
|
||||
|
|
|
@ -40,21 +40,34 @@
|
|||
(defn undo!
|
||||
[e]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(when (db-transact/request-finished?)
|
||||
(util/stop e)
|
||||
(p/do!
|
||||
(state/set-state! [:editor/last-replace-ref-content-tx repo] nil)
|
||||
(editor/save-current-block!)
|
||||
(state/clear-editor-action!)
|
||||
(state/set-block-op-type! nil)
|
||||
(let [^js worker @state/*db-worker]
|
||||
(.undo worker repo))))))
|
||||
;; TODO:
|
||||
;; 1. :block/name will be non-unique, switch to other way to get current-page-uuid
|
||||
;; 2. (state/get-current-page) return wrong result when editing in right-sidebar
|
||||
(when-let [current-page-uuid-str (some->> (state/get-current-page)
|
||||
(vector :block/name)
|
||||
db/entity
|
||||
:block/uuid
|
||||
str)]
|
||||
(when (db-transact/request-finished?)
|
||||
(util/stop e)
|
||||
(p/do!
|
||||
(state/set-state! [:editor/last-replace-ref-content-tx repo] nil)
|
||||
(editor/save-current-block!)
|
||||
(state/clear-editor-action!)
|
||||
(state/set-block-op-type! nil)
|
||||
(let [^js worker @state/*db-worker]
|
||||
(.undo worker repo current-page-uuid-str)))))))
|
||||
|
||||
(defn redo!
|
||||
[e]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(when (db-transact/request-finished?)
|
||||
(util/stop e)
|
||||
(state/clear-editor-action!)
|
||||
(let [^js worker @state/*db-worker]
|
||||
(.redo worker repo)))))
|
||||
(when-let [current-page-uuid-str (some->> (state/get-current-page)
|
||||
(vector :block/name)
|
||||
db/entity
|
||||
:block/uuid
|
||||
str)]
|
||||
(when (db-transact/request-finished?)
|
||||
(util/stop e)
|
||||
(state/clear-editor-action!)
|
||||
(let [^js worker @state/*db-worker]
|
||||
(.redo worker repo current-page-uuid-str))))))
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
[logseq.common.config :as common-config]
|
||||
[frontend.schema-register :include-macros true :as sr]))
|
||||
|
||||
(sr/defkeyword :undo/repo->undo-stack
|
||||
"{repo [first-op, second-op, ...]}")
|
||||
(sr/defkeyword :undo/repo->pege-block-uuid->undo-ops
|
||||
"{repo {<page-block-uuid> [op1 op2 ...]}}")
|
||||
|
||||
(sr/defkeyword :undo/repo->undo-stack
|
||||
"{repo [first-op, second-op, ...]}")
|
||||
(sr/defkeyword :undo/repo->pege-block-uuid->redo-ops
|
||||
"{repo {<page-block-uuid> [op1 op2 ...]}}")
|
||||
|
||||
(defonce *state (atom {:worker/object nil
|
||||
|
||||
|
@ -24,8 +24,8 @@
|
|||
|
||||
:rtc/downloading-graph? false
|
||||
|
||||
:undo/repo->undo-stack (atom {})
|
||||
:undo/repo->redo-stack (atom {})}))
|
||||
:undo/repo->pege-block-uuid->undo-ops (atom {})
|
||||
:undo/repo->pege-block-uuid->redo-ops (atom {})}))
|
||||
|
||||
(defonce *rtc-ws-url (atom nil))
|
||||
|
||||
|
|
|
@ -139,10 +139,18 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
|
||||
(def ^:private apply-conj-vec (partial apply (fnil conj [])))
|
||||
|
||||
(comment
|
||||
(def ^:private op-count-hard-limit 3000)
|
||||
(def ^:private op-count-limit 2000))
|
||||
|
||||
(defn- push-undo-ops
|
||||
[repo ops]
|
||||
(assert (undo-ops-validator ops) ops)
|
||||
(swap! (:undo/repo->undo-stack @worker-state/*state) update repo apply-conj-vec ops))
|
||||
[repo page-block-uuid ops]
|
||||
(assert (and (undo-ops-validator ops)
|
||||
(uuid? page-block-uuid))
|
||||
{:ops ops :page-block-uuid page-block-uuid})
|
||||
(swap! (:undo/repo->pege-block-uuid->undo-ops @worker-state/*state)
|
||||
update-in [repo page-block-uuid]
|
||||
apply-conj-vec ops))
|
||||
|
||||
(defn- pop-ops-helper
|
||||
[stack]
|
||||
|
@ -164,32 +172,38 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
[ops (subvec (vec stack) 0 i)]))
|
||||
|
||||
(defn- pop-undo-ops
|
||||
[repo]
|
||||
(let [repo->undo-stack (:undo/repo->undo-stack @worker-state/*state)
|
||||
undo-stack (@repo->undo-stack repo)
|
||||
[repo page-block-uuid]
|
||||
(assert (uuid? page-block-uuid) page-block-uuid)
|
||||
(let [repo->pege-block-uuid->undo-ops (:undo/repo->pege-block-uuid->undo-ops @worker-state/*state)
|
||||
undo-stack (get-in @repo->pege-block-uuid->undo-ops [repo page-block-uuid])
|
||||
[ops undo-stack*] (pop-ops-helper undo-stack)]
|
||||
(swap! repo->undo-stack assoc repo undo-stack*)
|
||||
(swap! repo->pege-block-uuid->undo-ops assoc-in [repo page-block-uuid] undo-stack*)
|
||||
ops))
|
||||
|
||||
(defn- empty-undo-stack?
|
||||
[repo]
|
||||
(empty? (@(:undo/repo->undo-stack @worker-state/*state) repo)))
|
||||
[repo page-block-uuid]
|
||||
(empty? (get-in @(:undo/repo->pege-block-uuid->undo-ops @worker-state/*state) [repo page-block-uuid])))
|
||||
|
||||
(defn- empty-redo-stack?
|
||||
[repo]
|
||||
(empty? (@(:undo/repo->redo-stack @worker-state/*state) repo)))
|
||||
[repo page-block-uuid]
|
||||
(empty? (get-in @(:undo/repo->pege-block-uuid->redo-ops @worker-state/*state) [repo page-block-uuid])))
|
||||
|
||||
(defn- push-redo-ops
|
||||
[repo ops]
|
||||
(assert (undo-ops-validator ops) ops)
|
||||
(swap! (:undo/repo->redo-stack @worker-state/*state) update repo apply-conj-vec ops))
|
||||
[repo page-block-uuid ops]
|
||||
(assert (and (undo-ops-validator ops)
|
||||
(uuid? page-block-uuid))
|
||||
{:ops ops :page-block-uuid page-block-uuid})
|
||||
(swap! (:undo/repo->pege-block-uuid->redo-ops @worker-state/*state)
|
||||
update-in [repo page-block-uuid]
|
||||
apply-conj-vec ops))
|
||||
|
||||
(defn- pop-redo-ops
|
||||
[repo]
|
||||
(let [repo->redo-stack (:undo/repo->redo-stack @worker-state/*state)
|
||||
redo-stack (@repo->redo-stack repo)
|
||||
[ops redo-stack*] (pop-ops-helper redo-stack)]
|
||||
(swap! repo->redo-stack assoc repo redo-stack*)
|
||||
[repo page-block-uuid]
|
||||
(assert (uuid? page-block-uuid) page-block-uuid)
|
||||
(let [repo->pege-block-uuid->redo-ops (:undo/repo->pege-block-uuid->redo-ops @worker-state/*state)
|
||||
undo-stack (get-in @repo->pege-block-uuid->redo-ops [repo page-block-uuid])
|
||||
[ops undo-stack*] (pop-ops-helper undo-stack)]
|
||||
(swap! repo->pege-block-uuid->redo-ops assoc-in [repo page-block-uuid] undo-stack*)
|
||||
ops))
|
||||
|
||||
(defn- normal-block?
|
||||
|
@ -277,8 +291,8 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
(conj [:push-undo-redo])))))))
|
||||
|
||||
(defn undo
|
||||
[repo conn]
|
||||
(if-let [ops (not-empty (pop-undo-ops repo))]
|
||||
[repo page-block-uuid conn]
|
||||
(if-let [ops (not-empty (pop-undo-ops repo page-block-uuid))]
|
||||
(let [redo-ops-to-push (transient [])]
|
||||
(batch-tx/with-batch-tx-mode conn
|
||||
(doseq [op ops]
|
||||
|
@ -288,16 +302,16 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
(some-> *undo-redo-info-for-test* (reset! {:op op :tx (second r)}))
|
||||
(conj! redo-ops-to-push rev-op)))))
|
||||
(when-let [rev-ops (not-empty (persistent! redo-ops-to-push))]
|
||||
(push-redo-ops repo (cons boundary rev-ops)))
|
||||
(push-redo-ops repo page-block-uuid (cons boundary rev-ops)))
|
||||
nil)
|
||||
|
||||
(when (empty-undo-stack? repo)
|
||||
(when (empty-undo-stack? repo page-block-uuid)
|
||||
(prn "No further undo information")
|
||||
::empty-undo-stack)))
|
||||
|
||||
(defn redo
|
||||
[repo conn]
|
||||
(if-let [ops (not-empty (pop-redo-ops repo))]
|
||||
[repo page-block-uuid conn]
|
||||
(if-let [ops (not-empty (pop-redo-ops repo page-block-uuid))]
|
||||
(let [undo-ops-to-push (transient [])]
|
||||
(batch-tx/with-batch-tx-mode conn
|
||||
(doseq [op ops]
|
||||
|
@ -307,10 +321,10 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
(some-> *undo-redo-info-for-test* (reset! {:op op :tx (second r)}))
|
||||
(conj! undo-ops-to-push rev-op)))))
|
||||
(when-let [rev-ops (not-empty (persistent! undo-ops-to-push))]
|
||||
(push-undo-ops repo (cons boundary rev-ops)))
|
||||
(push-undo-ops repo page-block-uuid (cons boundary rev-ops)))
|
||||
nil)
|
||||
|
||||
(when (empty-redo-stack? repo)
|
||||
(when (empty-redo-stack? repo page-block-uuid)
|
||||
(prn "No further redo information")
|
||||
::empty-redo-stack)))
|
||||
|
||||
|
@ -368,11 +382,21 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
{:block-uuid (:block/uuid entity-after)
|
||||
:block-origin-content (:block/content entity-before)}]]))))))
|
||||
|
||||
(defn- find-page-block-uuid
|
||||
[db-before db-after same-entity-datoms-coll]
|
||||
(some
|
||||
(fn [entity-datoms]
|
||||
(when-let [e (ffirst entity-datoms)]
|
||||
(or (some-> (d/entity db-before e) :block/page :block/uuid)
|
||||
(some-> (d/entity db-after e) :block/page :block/uuid))))
|
||||
same-entity-datoms-coll))
|
||||
|
||||
(defn- generate-undo-ops
|
||||
[repo db-before db-after same-entity-datoms-coll id->attr->datom gen-boundary-op?]
|
||||
(let [ops (mapcat (partial entity-datoms=>ops db-before db-after id->attr->datom) same-entity-datoms-coll)]
|
||||
(when (seq ops)
|
||||
(push-undo-ops repo (if gen-boundary-op? (cons boundary ops) ops)))))
|
||||
(when-let [page-block-uuid (find-page-block-uuid db-before db-after same-entity-datoms-coll)]
|
||||
(let [ops (mapcat (partial entity-datoms=>ops db-before db-after id->attr->datom) same-entity-datoms-coll)]
|
||||
(when (seq ops)
|
||||
(push-undo-ops repo page-block-uuid (if gen-boundary-op? (cons boundary ops) ops))))))
|
||||
|
||||
(defmethod db-listener/listen-db-changes :gen-undo-ops
|
||||
[_ {:keys [_tx-data tx-meta db-before db-after
|
||||
|
@ -385,8 +409,8 @@ when undo this op, this original entity-map will be transacted back into db")
|
|||
|
||||
(defn clear-undo-redo-stack
|
||||
[]
|
||||
(reset! (:undo/repo->undo-stack @worker-state/*state) {})
|
||||
(reset! (:undo/repo->redo-stack @worker-state/*state) {}))
|
||||
(reset! (:undo/repo->pege-block-uuid->redo-ops @worker-state/*state) {})
|
||||
(reset! (:undo/repo->pege-block-uuid->undo-ops @worker-state/*state) {}))
|
||||
|
||||
(comment
|
||||
|
||||
|
|
|
@ -5,26 +5,41 @@
|
|||
|
||||
|
||||
(defn gen-available-block-uuid
|
||||
[db]
|
||||
(gen/elements
|
||||
(->> (d/q '[:find ?block-uuid
|
||||
:where
|
||||
[?block :block/parent]
|
||||
[?block :block/left]
|
||||
[?block :block/uuid ?block-uuid]]
|
||||
db)
|
||||
(apply concat))))
|
||||
|
||||
[db & {:keys [page-uuid]}]
|
||||
(let [query (cond-> '[:find ?block-uuid]
|
||||
page-uuid
|
||||
(concat '[:in $ ?page-uuid])
|
||||
true
|
||||
(concat '[:where
|
||||
[?block :block/parent]
|
||||
[?block :block/left]
|
||||
[?block :block/uuid ?block-uuid]])
|
||||
page-uuid
|
||||
(concat '[[?block :block/page ?page]
|
||||
[?page :block/uuid ?page-uuid]]))]
|
||||
(gen/elements
|
||||
(->> (if page-uuid
|
||||
(d/q query db page-uuid)
|
||||
(d/q query db))
|
||||
(apply concat)))))
|
||||
|
||||
(defn gen-available-parent-left-pair
|
||||
"generate [<parent-uuid> <left-uuid>]"
|
||||
[db]
|
||||
(gen/elements
|
||||
(d/q '[:find ?parent-uuid ?left-uuid
|
||||
:where
|
||||
[?b :block/uuid]
|
||||
[?b :block/parent ?parent]
|
||||
[?b :block/left ?left]
|
||||
[?parent :block/uuid ?parent-uuid]
|
||||
[?left :block/uuid ?left-uuid]]
|
||||
db)))
|
||||
[db & {:keys [page-uuid]}]
|
||||
(let [query (cond-> '[:find ?parent-uuid ?left-uuid]
|
||||
page-uuid
|
||||
(concat '[:in $ ?page-uuid])
|
||||
true
|
||||
(concat '[:where
|
||||
[?b :block/uuid]
|
||||
[?b :block/parent ?parent]
|
||||
[?b :block/left ?left]
|
||||
[?parent :block/uuid ?parent-uuid]
|
||||
[?left :block/uuid ?left-uuid]])
|
||||
page-uuid
|
||||
(concat '[[?b :block/page ?page]
|
||||
[?page :block/uuid ?page-uuid]]))]
|
||||
(gen/elements
|
||||
(if page-uuid
|
||||
(d/q query db page-uuid)
|
||||
(d/q query db)))))
|
||||
|
|
|
@ -205,8 +205,8 @@ This can be called in synchronous contexts as no async fns should be invoked"
|
|||
{:re-render? false :verbose false :refresh? true})))
|
||||
|
||||
(defn initial-test-page-and-blocks
|
||||
[]
|
||||
(let [page-uuid (random-uuid)
|
||||
[& {:keys [page-uuid]}]
|
||||
(let [page-uuid (or page-uuid (random-uuid))
|
||||
first-block-uuid (random-uuid)
|
||||
second-block-uuid (random-uuid)
|
||||
page-id [:block/uuid page-uuid]]
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
[frontend.test.helper :as test-helper]
|
||||
[frontend.worker.undo-redo :as undo-redo]))
|
||||
|
||||
(def ^:private init-data (test-helper/initial-test-page-and-blocks))
|
||||
(def ^:private page-uuid (random-uuid))
|
||||
(def ^:private init-data (test-helper/initial-test-page-and-blocks {:page-uuid page-uuid}))
|
||||
|
||||
(defn- start-and-destroy-db
|
||||
[f]
|
||||
(test-helper/db-based-start-and-destroy-db
|
||||
|
@ -20,11 +22,13 @@
|
|||
|
||||
(defn- gen-block-uuid
|
||||
[db & {:keys [non-exist-frequency] :or {non-exist-frequency 1}}]
|
||||
(gen/frequency [[9 (t.gen/gen-available-block-uuid db)] [non-exist-frequency gen-non-exist-block-uuid]]))
|
||||
(gen/frequency [[9 (t.gen/gen-available-block-uuid db {:page-uuid page-uuid})]
|
||||
[non-exist-frequency gen-non-exist-block-uuid]]))
|
||||
|
||||
(defn- gen-parent-left-pair
|
||||
[db]
|
||||
(gen/frequency [[9 (t.gen/gen-available-parent-left-pair db)] [1 (gen/vector gen-non-exist-block-uuid 2)]]))
|
||||
(gen/frequency [[9 (t.gen/gen-available-parent-left-pair db {:page-uuid page-uuid})]
|
||||
[1 (gen/vector gen-non-exist-block-uuid 2)]]))
|
||||
|
||||
(defn- gen-move-block-op
|
||||
[db]
|
||||
|
@ -43,7 +47,7 @@
|
|||
|
||||
(defn- gen-remove-block-op
|
||||
[db]
|
||||
(gen/let [block-uuid (gen-block-uuid db {:non-exist-frequency 80})
|
||||
(gen/let [block-uuid (gen-block-uuid db {:non-exist-frequency 90})
|
||||
[parent left] (gen-parent-left-pair db)
|
||||
content gen/string-alphanumeric]
|
||||
[:frontend.worker.undo-redo/remove-block
|
||||
|
@ -116,7 +120,7 @@
|
|||
[conn]
|
||||
(binding [undo-redo/*undo-redo-info-for-test* (atom nil)]
|
||||
(loop [i 0]
|
||||
(let [r (undo-redo/undo test-helper/test-db-name-db-version conn)
|
||||
(let [r (undo-redo/undo test-helper/test-db-name-db-version page-uuid conn)
|
||||
current-db @conn]
|
||||
(check-block-count @undo-redo/*undo-redo-info-for-test* current-db)
|
||||
(if (not= :frontend.worker.undo-redo/empty-undo-stack r)
|
||||
|
@ -124,7 +128,7 @@
|
|||
(prn :undo-count i))))
|
||||
|
||||
(loop []
|
||||
(let [r (undo-redo/redo test-helper/test-db-name-db-version conn)
|
||||
(let [r (undo-redo/redo test-helper/test-db-name-db-version page-uuid conn)
|
||||
current-db @conn]
|
||||
(check-block-count @undo-redo/*undo-redo-info-for-test* current-db)
|
||||
(when (not= :frontend.worker.undo-redo/empty-redo-stack r)
|
||||
|
@ -132,12 +136,12 @@
|
|||
|
||||
(deftest undo-redo-gen-test
|
||||
(let [conn (db/get-db false)
|
||||
all-remove-ops (gen/generate (gen/vector (gen-op @conn {:remove-block-op 1000}) 20))]
|
||||
(#'undo-redo/push-undo-ops test-helper/test-db-name-db-version all-remove-ops)
|
||||
all-remove-ops (gen/generate (gen/vector (gen-op @conn {:remove-block-op 1000}) 100))]
|
||||
(#'undo-redo/push-undo-ops test-helper/test-db-name-db-version page-uuid all-remove-ops)
|
||||
(prn :block-count-before-init (count (get-db-block-set @conn)))
|
||||
(loop [i 0]
|
||||
(when (not= :frontend.worker.undo-redo/empty-undo-stack
|
||||
(undo-redo/undo test-helper/test-db-name-db-version conn))
|
||||
(undo-redo/undo test-helper/test-db-name-db-version page-uuid conn))
|
||||
(recur (inc i))))
|
||||
(prn :block-count (count (get-db-block-set @conn)))
|
||||
(undo-redo/clear-undo-redo-stack)
|
||||
|
@ -145,7 +149,7 @@
|
|||
(let [origin-graph-block-set (get-db-block-set @conn)
|
||||
ops (gen/generate (gen/vector (gen-op @conn {:move-block-op 1000 :boundary-op 500}) 300))]
|
||||
(prn :ops (count ops))
|
||||
(#'undo-redo/push-undo-ops test-helper/test-db-name-db-version ops)
|
||||
(#'undo-redo/push-undo-ops test-helper/test-db-name-db-version page-uuid ops)
|
||||
|
||||
(undo-all-then-redo-all conn)
|
||||
(undo-all-then-redo-all conn)
|
||||
|
@ -157,7 +161,7 @@
|
|||
(let [origin-graph-block-set (get-db-block-set @conn)
|
||||
ops (gen/generate (gen/vector (gen-op @conn) 1000))]
|
||||
(prn :ops (count ops))
|
||||
(#'undo-redo/push-undo-ops test-helper/test-db-name-db-version ops)
|
||||
(#'undo-redo/push-undo-ops test-helper/test-db-name-db-version page-uuid ops)
|
||||
|
||||
(undo-all-then-redo-all conn)
|
||||
(undo-all-then-redo-all conn)
|
||||
|
|
Loading…
Reference in New Issue