Selaa lähdekoodia

fix: cache key bindings

Junyi Du 2 vuotta sitten
vanhempi
sitoutus
bd3479abda

+ 15 - 8
src/main/frontend/modules/shortcut/data_helper.cljs

@@ -13,14 +13,21 @@
             [frontend.handler.config :as config-handler])
   (:import [goog.ui KeyboardShortcutHandler]))
 
-(defn get-bindings
-  []
-  (->> (vals @shortcut-config/config)
+;; function vals->bindings is too time-consuming. Here we cache the results.
+(defn- flatten-key-bindings
+  [config]
+  (->> config
        (into {})
        (map (fn [[k {:keys [binding]}]]
               {k binding}))
        (into {})))
 
+(def m-flatten-key-bindings (util/memoize-last flatten-key-bindings))
+
+(defn get-bindings
+  []
+  (m-flatten-key-bindings (vals @shortcut-config/config)))
+
 (defn- mod-key [shortcut]
   (str/replace shortcut #"(?i)mod"
                (if util/mac? "meta" "ctrl")))
@@ -48,11 +55,11 @@
 (defn normalize-user-keyname
   [k]
   (let [keynames {";" "semicolon"
-                   "=" "equals"
-                   "-" "dash"
-                   "[" "open-square-bracket"
-                   "]" "close-square-bracket"
-                   "'" "single-quote"}]
+                  "=" "equals"
+                  "-" "dash"
+                  "[" "open-square-bracket"
+                  "]" "close-square-bracket"
+                  "'" "single-quote"}]
     (some-> k
             (util/safe-lower-case)
             (str/replace #"[;=-\[\]']" (fn [s]

+ 18 - 0
src/main/frontend/util.cljc

@@ -1432,3 +1432,21 @@
                       (.then (fn [blob]
                                (js/navigator.clipboard.write (clj->js [(js/ClipboardItem. (clj->js {(.-type blob) blob}))]))))
                       (.catch js/console.error)))))))
+
+
+(defn memoize-last
+  "Different from core.memoize, it only cache the last result.
+   Returns a memoized version of a referentially transparent function. The
+  memoized version of the function cache the the last result, and replay when calls 
+   with the same arguments, or update cache when with different arguments."
+  [f]
+  (let [last-mem (atom nil)
+        last-args (atom nil)]
+    (fn [& args]
+      (if (or (nil? @last-mem)
+              (not= @last-args args))
+        (let [ret (apply f args)]
+          (reset! last-args args)
+          (reset! last-mem ret)
+          ret)
+        @last-mem))))

+ 77 - 1
src/test/frontend/util_test.cljs

@@ -1,6 +1,7 @@
 (ns frontend.util-test
   (:require [cljs.test :refer [deftest is testing]]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [frontend.modules.shortcut.data-helper :as shortcut-data-helper]))
 
 (deftest test-find-first
   (testing "find-first"
@@ -31,3 +32,78 @@
     (is (= (util/node-path.join "content://a/b" "../d.md") "content://a/d.md"))
     (is (= (util/node-path.join "https://logseq.com/a/b" "c/d.md") "https://logseq.com/a/b/c/d.md"))))
 
+(deftest test-memoize-last
+  (testing "memoize-last add test"
+    (let [actual-ops (atom 0)
+          m+         (util/memoize-last (fn [x1 x2]
+                                           (swap! actual-ops inc) ;; side effect for counting
+                                           (+ x1 x2)))]
+      (is (= (m+ 1 1) 2))
+      (is (= @actual-ops 1))
+      (is (= (m+ 1 1) 2))
+      (is (= (m+ 1 1) 2))
+      (is (= @actual-ops 1))
+      (is (= (m+ 1 2) 3))
+      (is (= @actual-ops 2))
+      (is (= (m+ 2 3) 5))
+      (is (= @actual-ops 3))
+      (is (= (m+ 3 5) 8))
+      (is (= @actual-ops 4))
+      (is (= (m+ 3 5) 8))
+      (is (= @actual-ops 4))))
+
+  (testing "memoize-last nested mapping test"
+    (let [actual-ops (atom 0)
+          flatten-f  (util/memoize-last (fn [& args]
+                                           (swap! actual-ops inc) ;; side effect for counting
+                                           (apply #'shortcut-data-helper/flatten-key-bindings args)))
+          target     (atom {:part1 {:date-picker/complete         {:binding "enter"
+                                                                   :fn      "ui-handler/shortcut-complete"}
+                                    :date-picker/prev-day         {:binding "left"
+                                                                   :fn      "ui-handler/shortcut-prev-day"}}
+                            :part2 {:date-picker/next-day         {:binding "right"
+                                                                   :fn      "ui-handler/shortcut-next-day"}
+                                    :date-picker/prev-week        {:binding ["up" "ctrl+p"]
+                                                                   :fn      "ui-handler/shortcut-prev-week"}}})]
+      (is (= (flatten-f (vals @target)) {:date-picker/complete         "enter"
+                                         :date-picker/prev-day         "left"
+                                         :date-picker/next-day         "right"
+                                         :date-picker/prev-week        ["up" "ctrl+p"]}))
+      (is (= @actual-ops 1))
+      (is (= (flatten-f (vals @target)) {:date-picker/complete         "enter"
+                                         :date-picker/prev-day         "left"
+                                         :date-picker/next-day         "right"
+                                         :date-picker/prev-week        ["up" "ctrl+p"]}))
+      (is (= @actual-ops 1))
+      ;; edit value
+      (swap! target assoc-in [:part1 :date-picker/complete :binding] "tab")
+      (is (= (flatten-f (vals @target)) {:date-picker/complete         "tab"
+                                         :date-picker/prev-day         "left"
+                                         :date-picker/next-day         "right"
+                                         :date-picker/prev-week        ["up" "ctrl+p"]}))
+      (is (= @actual-ops 2))
+      (is (= (flatten-f (vals @target)) {:date-picker/complete         "tab"
+                                         :date-picker/prev-day         "left"
+                                         :date-picker/next-day         "right"
+                                         :date-picker/prev-week        ["up" "ctrl+p"]}))
+      (is (= @actual-ops 2))
+      (is (= (flatten-f (vals @target)) {:date-picker/complete         "tab"
+                                         :date-picker/prev-day         "left"
+                                         :date-picker/next-day         "right"
+                                         :date-picker/prev-week        ["up" "ctrl+p"]}))
+      (is (= @actual-ops 2))
+      ;; edit key
+      (swap! target assoc :part3 {:date-picker/next-week {:binding "down"
+                                                          :fn      "ui-handler/shortcut-next-week"}})
+      (is (= (flatten-f (vals @target)) {:date-picker/complete         "tab"
+                                         :date-picker/prev-day         "left"
+                                         :date-picker/next-day         "right"
+                                         :date-picker/prev-week        ["up" "ctrl+p"]
+                                         :date-picker/next-week        "down"}))
+      (is (= @actual-ops 3))
+      (is (= (flatten-f (vals @target)) {:date-picker/complete         "tab"
+                                         :date-picker/prev-day         "left"
+                                         :date-picker/next-day         "right"
+                                         :date-picker/prev-week        ["up" "ctrl+p"]
+                                         :date-picker/next-week        "down"}))
+      (is (= @actual-ops 3)))))