Browse Source

enhance(undo): undo/redo is page-scoped now

rcmerci 1 year ago
parent
commit
53970880d3

+ 4 - 4
src/main/frontend/db_worker.cljs

@@ -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]

+ 27 - 14
src/main/frontend/handler/history.cljs

@@ -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))))))

+ 6 - 6
src/main/frontend/worker/state.cljs

@@ -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))
 

+ 56 - 32
src/main/frontend/worker/undo_redo.cljs

@@ -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
 

+ 35 - 20
src/test/frontend/test/generators.cljs

@@ -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)))))

+ 2 - 2
src/test/frontend/test/helper.cljs

@@ -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]]

+ 15 - 11
src/test/frontend/worker/undo_redo_test.cljs

@@ -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)