浏览代码

refactor: cursor positions

Tienson Qin 3 年之前
父节点
当前提交
7c7312d830

+ 12 - 9
src/main/frontend/commands.cljs

@@ -23,9 +23,7 @@
 
 ;; TODO: move to frontend.handler.editor.commands
 
-(defonce *slash-caret-pos (atom nil))
 (defonce angle-bracket "<")
-(defonce *angle-bracket-caret-pos (atom nil))
 (defonce *current-command (atom nil))
 
 (def query-doc
@@ -305,14 +303,19 @@
 
 (defonce *matched-block-commands (atom (block-commands-map)))
 
+(defn reinit-matched-commands!
+  []
+  (reset! *matched-commands @*initial-commands))
+
+(defn reinit-matched-block-commands!
+  []
+  (reset! *matched-block-commands (block-commands-map)))
+
 (defn restore-state
   [restore-slash-caret-pos?]
-  (when restore-slash-caret-pos?
-    (reset! *slash-caret-pos nil))
   (state/clear-editor-action!)
-  (reset! *angle-bracket-caret-pos nil)
-  (reset! *matched-commands @*initial-commands)
-  (reset! *matched-block-commands (block-commands-map)))
+  (reinit-matched-commands!)
+  (reinit-matched-block-commands!))
 
 (defn insert!
   [id value
@@ -467,7 +470,7 @@
     (let [type (:type option)
           input (gdom/getElement input-id)
           beginning-of-line? (or (cursor/beginning-of-line? input)
-                                 (= 1 (:pos @*angle-bracket-caret-pos)))
+                                 (= 1 (:pos (:pos (state/get-editor-action-data)))))
           value (if (and (contains? #{"block" "properties"} type)
                          (not beginning-of-line?))
                   (str "\n" value)
@@ -540,7 +543,7 @@
   (when-let [input-id (state/get-edit-input-id)]
     (when-let [current-input (gdom/getElement input-id)]
       (let [edit-content (gobj/get current-input "value")
-            slash-pos (:pos @*slash-caret-pos)
+            slash-pos (:pos (:pos (state/get-editor-action-data)))
             [re-pattern new-line-re-pattern] (if (= :org format)
                                                [#"\*+\s" #"\n\*+\s"]
                                                [#"#+\s" #"\n#+\s"])

+ 86 - 92
src/main/frontend/components/editor.cljs

@@ -2,7 +2,7 @@
   (:require [clojure.string :as string]
             [goog.string :as gstring]
             [frontend.commands :as commands
-             :refer [*angle-bracket-caret-pos *first-command-group *matched-block-commands *matched-commands *slash-caret-pos]]
+             :refer [*first-command-group *matched-block-commands *matched-commands]]
             [frontend.components.block :as block]
             [frontend.components.datetime :as datetime-comp]
             [frontend.components.search :as search]
@@ -208,27 +208,26 @@
 (rum/defc template-search < rum/reactive
   {:will-unmount (fn [state] (reset! editor-handler/*selected-text nil) state)}
   [id _format]
-  (when (= :template-search (state/sub :editor/action))
-    (let [pos (state/get-editor-last-pos)
-          input (gdom/getElement id)]
-      (when input
-        (let [current-pos (cursor/pos input)
-              edit-content (state/sub [:editor/content id])
-              q (or
-                 (when (>= (count edit-content) current-pos)
-                   (subs edit-content pos current-pos))
-                 "")
-              matched-templates (editor-handler/get-matched-templates q)
-              non-exist-handler (fn [_state]
-                                  (state/set-editor-show-template-search! false))]
-          (ui/auto-complete
-           matched-templates
-           {:on-chosen   (editor-handler/template-on-chosen-handler id)
-            :on-enter    non-exist-handler
-            :empty-placeholder [:div.text-gray-500.px-4.py-2.text-sm "Search for a template"]
-            :item-render (fn [[template _block-db-id]]
-                           template)
-            :class       "black"}))))))
+  (let [pos (state/get-editor-last-pos)
+        input (gdom/getElement id)]
+    (when input
+      (let [current-pos (cursor/pos input)
+            edit-content (state/sub [:editor/content id])
+            q (or
+               (when (>= (count edit-content) current-pos)
+                 (subs edit-content pos current-pos))
+               "")
+            matched-templates (editor-handler/get-matched-templates q)
+            non-exist-handler (fn [_state]
+                                (state/set-editor-show-template-search! false))]
+        (ui/auto-complete
+         matched-templates
+         {:on-chosen   (editor-handler/template-on-chosen-handler id)
+          :on-enter    non-exist-handler
+          :empty-placeholder [:div.text-gray-500.px-4.py-2.text-sm "Search for a template"]
+          :item-render (fn [[template _block-db-id]]
+                         template)
+          :class       "black"})))))
 
 (rum/defcs input < rum/reactive
   (rum/local {} ::input-value)
@@ -239,45 +238,44 @@
       {;; enter
        13 (fn [state e]
             (let [input-value (get state ::input-value)
-                  input-option (state/get-editor-show-input)]
+                  input-option (:options (state/get-editor-show-input))]
               (when (seq @input-value)
                 ;; no new line input
                 (util/stop e)
                 (let [[_id on-submit] (:rum/args state)
-                      {:keys [pos]} @*slash-caret-pos
                       command (:command (first input-option))]
-                  (on-submit command @input-value pos))
+                  (on-submit command @input-value))
                 (reset! input-value nil))))})))
   [state _id on-submit]
   (when (= :input (state/sub :editor/action))
-    (when-let [input-option (state/sub :editor/action-data)]
-     (let [{:keys [pos]} (util/react *slash-caret-pos)
-           input-value (get state ::input-value)]
-       (when (seq input-option)
-         (let [command (:command (first input-option))]
-           [:div.p-2.rounded-md.shadow-lg
-            (for [{:keys [id placeholder type autoFocus] :as input-item} input-option]
-              [:div.my-3 {:key id}
-               [:input.form-input.block.w-full.pl-2.sm:text-sm.sm:leading-5
-                (merge
-                 (cond->
-                   {:key           (str "modal-input-" (name id))
-                    :id            (str "modal-input-" (name id))
-                    :type          (or type "text")
-                    :on-change     (fn [e]
-                                     (swap! input-value assoc id (util/evalue e)))
-                    :auto-complete (if (util/chrome?) "chrome-off" "off")}
-                   placeholder
-                   (assoc :placeholder placeholder)
-                   autoFocus
-                   (assoc :auto-focus true))
-                 (dissoc input-item :id))]])
-            (ui/button
-              "Submit"
-              :on-click
-              (fn [e]
-                (util/stop e)
-                (on-submit command @input-value pos)))]))))))
+    (when-let [action-data (state/sub :editor/action-data)]
+      (let [{:keys [pos options]} action-data
+            input-value (get state ::input-value)]
+        (when (seq options)
+          (let [command (:command (first options))]
+            [:div.p-2.rounded-md.shadow-lg
+             (for [{:keys [id placeholder type autoFocus] :as input-item} options]
+               [:div.my-3 {:key id}
+                [:input.form-input.block.w-full.pl-2.sm:text-sm.sm:leading-5
+                 (merge
+                  (cond->
+                    {:key           (str "modal-input-" (name id))
+                     :id            (str "modal-input-" (name id))
+                     :type          (or type "text")
+                     :on-change     (fn [e]
+                                      (swap! input-value assoc id (util/evalue e)))
+                     :auto-complete (if (util/chrome?) "chrome-off" "off")}
+                    placeholder
+                    (assoc :placeholder placeholder)
+                    autoFocus
+                    (assoc :auto-focus true))
+                  (dissoc input-item :id))]])
+             (ui/button
+               "Submit"
+               :on-click
+               (fn [e]
+                 (util/stop e)
+                 (on-submit command @input-value pos)))]))))))
 
 (rum/defc absolute-modal < rum/static
   [cp set-default-width? {:keys [top left rect]}]
@@ -332,14 +330,13 @@
      cp]))
 
 (rum/defc transition-cp < rum/reactive
-  [cp set-default-width? pos]
-  (when pos
-    (when-let [pos (rum/react pos)]
-      (ui/css-transition
-       {:class-names "fade"
-        :timeout     {:enter 500
-                      :exit  300}}
-       (absolute-modal cp set-default-width? pos)))))
+  [cp set-default-width?]
+  (when-let [pos (:pos (state/sub :editor/action-data))]
+    (ui/css-transition
+     {:class-names "fade"
+      :timeout     {:enter 500
+                    :exit  300}}
+     (absolute-modal cp set-default-width? pos))))
 
 (rum/defc image-uploader < rum/reactive
   [id format]
@@ -358,8 +355,7 @@
         [:div.flex.flex-row.align-center.rounded-md.shadow-sm.bg-base-2.px-1.py-1
          (ui/loading
           (util/format "Uploading %s%" (util/format "%2d" processing)))]
-        false
-        *slash-caret-pos)))])
+        false)))])
 
 (defn- set-up-key-down!
   [state format]
@@ -456,9 +452,9 @@
   (let [content (state/sub-edit-content)]
     (mock-textarea content)))
 
-(defn animated-modal
-  [key component set-default-width? *pos]
-  (when *pos
+(rum/defc animated-modal < rum/reactive
+  [key component set-default-width?]
+  (when-let [pos (:pos (state/get-editor-action-data))]
     (ui/css-transition
      {:key key
       :class-names {:enter "origin-top-left opacity-0 transform scale-95"
@@ -470,44 +466,42 @@
        (absolute-modal
         component
         set-default-width?
-        *pos)))))
+        pos)))))
 
 (rum/defc modals < rum/reactive
   "React to atom changes, find and render the correct modal"
   [id format]
-  (let [action (state/sub :editor/action)
-        slash-pos @*slash-caret-pos]
-    (ui/transition-group
-     (cond
-       (and (= action :commands) slash-pos)
-       (animated-modal "commands" (commands id format) true slash-pos)
+  (let [action (state/sub :editor/action)]
+    (cond
+      (= action :commands)
+      (animated-modal "commands" (commands id format) true)
 
-       (and (= action :block-commands) @*angle-bracket-caret-pos)
-       (animated-modal "block-commands" (block-commands id format) true (util/react *angle-bracket-caret-pos))
+      (= action :block-commands)
+      (animated-modal "block-commands" (block-commands id format) true)
 
-       (contains? #{:page-search :page-search-hashtag} action)
-       (animated-modal "page-search" (page-search id format) true slash-pos)
+      (contains? #{:page-search :page-search-hashtag} action)
+      (animated-modal "page-search" (page-search id format) true)
 
-       (= :block-search action)
-       (animated-modal "block-search" (block-search id format) false slash-pos)
+      (= :block-search action)
+      (animated-modal "block-search" (block-search id format) true)
 
-       (= :template-search action)
-       (animated-modal "template-search" (template-search id format) true slash-pos)
+      (= :template-search action)
+      (animated-modal "template-search" (template-search id format) true)
 
-       (= :datepicker action)
-       (animated-modal "date-picker" (datetime-comp/date-picker id format nil) false slash-pos)
+      (= :datepicker action)
+      (animated-modal "date-picker" (datetime-comp/date-picker id format nil) false)
 
-       (= :input action)
-       (animated-modal "input" (input id
-                                      (fn [command m _pos]
-                                        (editor-handler/handle-command-input command id format m)))
-                       true slash-pos)
+      (= :input action)
+      (animated-modal "input" (input id
+                                     (fn [command m]
+                                       (editor-handler/handle-command-input command id format m)))
+                      true)
 
-       (= :zotero action)
-       (animated-modal "zotero-search" (zotero/zotero-search id) false slash-pos)
+      (= :zotero action)
+      (animated-modal "zotero-search" (zotero/zotero-search id) false)
 
-       :else
-       nil))))
+      :else
+      nil)))
 
 (rum/defcs box < rum/reactive
   {:init (fn [state]

+ 28 - 29
src/main/frontend/handler/editor.cljs

@@ -4,9 +4,7 @@
             [clojure.string :as string]
             [clojure.walk :as w]
             [dommy.core :as dom]
-            [frontend.commands :as commands
-             :refer [*angle-bracket-caret-pos
-                     *slash-caret-pos]]
+            [frontend.commands :as commands]
             [frontend.config :as config]
             [frontend.date :as date]
             [frontend.db :as db]
@@ -1562,12 +1560,12 @@
           "[["
           (do
             (commands/handle-step [:editor/search-page])
-            (reset! commands/*slash-caret-pos (cursor/get-caret-pos input)))
+            (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)}))
 
           "(("
           (do
             (commands/handle-step [:editor/search-block :reference])
-            (reset! commands/*slash-caret-pos (cursor/get-caret-pos input)))
+            (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)}))
 
           nil)))))
 
@@ -1623,7 +1621,7 @@
   (try
     (let [edit-content (or (gobj/get input "value") "")
           pos (cursor/pos input)
-          last-slash-caret-pos (:pos @*slash-caret-pos)
+          last-slash-caret-pos (:pos (:pos (state/get-editor-action-data)))
           last-command (and last-slash-caret-pos (subs edit-content last-slash-caret-pos pos))]
       (when (> pos 0)
         (or
@@ -1641,7 +1639,7 @@
     (let [edit-content (gobj/get input "value")
           pos (cursor/pos input)
           last-command (subs edit-content
-                             (:pos @*angle-bracket-caret-pos)
+                             (:pos (:pos (state/get-editor-action-data)))
                              pos)]
       (when (> pos 0)
         (or
@@ -1831,15 +1829,20 @@
 
     ;; TODO: is it cross-browser compatible?
     ;; (not= (gobj/get native-e "inputType") "insertFromPaste")
-    (when (= last-input-char (state/get-editor-command-trigger))
-      (when (seq (get-matched-commands input))
-        (reset! commands/*slash-caret-pos (cursor/get-caret-pos input))
-        (state/set-editor-show-commands!)))
-
-    (if (= last-input-char commands/angle-bracket)
-      (when (seq (get-matched-block-commands input))
-        (reset! commands/*angle-bracket-caret-pos (cursor/get-caret-pos input))
+    (cond
+      (= last-input-char (state/get-editor-command-trigger))
+      (do
+        (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
+        (commands/reinit-matched-commands!)
+        (state/set-editor-show-commands!))
+
+      (= last-input-char commands/angle-bracket)
+      (do
+        (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
+        (commands/reinit-matched-block-commands!)
         (state/set-editor-show-block-commands!))
+
+      :else
       nil)))
 
 (defn block-on-chosen-handler
@@ -2237,7 +2240,7 @@
                            parent-id bounds
                            {:backward-pos backward-pos
                             :check-fn (fn [_ _ _]
-                                        (reset! commands/*slash-caret-pos new-pos)
+                                        (state/set-editor-action-data! {:pos new-pos})
                                         (commands/handle-step [:editor/search-page]))}))]
         (state/set-editor-show-page-search! false)
         (let [selection (get-selection-and-format)
@@ -2270,7 +2273,7 @@
                             parent-id bounds
                             {:backward-pos backward-pos
                              :check-fn     (fn [_ _ _]
-                                             (reset! commands/*slash-caret-pos new-pos)
+                                             (state/set-editor-action-data! {:pos new-pos})
                                              (commands/handle-step [:editor/search-block]))}))]
         (state/set-editor-show-block-search! false)
         (if-let [embed-ref (thingatpt/embed-macro-at-point input)]
@@ -2553,16 +2556,14 @@
            (= (util/nth-safe value (dec current-pos)) (state/get-editor-command-trigger)))
       (do
         (util/stop e)
-        (reset! *slash-caret-pos nil)
-        (state/clear-editor-action!)
+        (commands/restore-state true)
         (delete-and-update input (dec current-pos) current-pos))
 
       (and (> current-pos 1)
            (= (util/nth-safe value (dec current-pos)) commands/angle-bracket))
       (do
         (util/stop e)
-        (reset! *angle-bracket-caret-pos nil)
-        (state/clear-editor-action!)
+        (commands/restore-state true)
         (delete-and-update input (dec current-pos) current-pos))
 
       ;; pair
@@ -2703,7 +2704,7 @@
           (if (= key "#")
             (state/set-editor-last-pos! (inc (cursor/pos input))) ;; In keydown handler, the `#` is not inserted yet.
             (state/set-editor-last-pos! (cursor/pos input)))
-          (reset! commands/*slash-caret-pos (cursor/get-caret-pos input)))
+          (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)}))
 
         (let [sym "$"]
           (and (= key sym)
@@ -2748,9 +2749,7 @@
           (and (= :commands (state/get-editor-action)) (not= k (state/get-editor-command-trigger)))
           (let [matched-commands (get-matched-commands input)]
             (if (seq matched-commands)
-              (do
-                (state/set-editor-show-commands!)
-                (reset! commands/*matched-commands matched-commands))
+              (reset! commands/*matched-commands matched-commands)
               (state/clear-editor-action!)))
 
           (and (= :block-commands editor-action) (not= key-code 188)) ; not <
@@ -2795,7 +2794,7 @@
                                    :editor/search-page-hashtag
                                    :editor/search-page)]
                 (commands/handle-step [command-step])
-                (reset! commands/*slash-caret-pos pos))
+                (state/set-editor-action-data! {:pos pos}))
 
               (and blank-selected?
                    (contains? keycode/left-square-brackets-keys k)
@@ -2806,7 +2805,7 @@
                 (commands/handle-step [:editor/input "[[]]" {:backward-truncate-number 2
                                                              :backward-pos 2}])
                 (commands/handle-step [:editor/search-page])
-                (reset! commands/*slash-caret-pos (cursor/get-caret-pos input)))
+                (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)}))
 
               (and blank-selected?
                    (contains? keycode/left-paren-keys k)
@@ -2817,7 +2816,7 @@
                 (commands/handle-step [:editor/input "(())" {:backward-truncate-number 2
                                                              :backward-pos 2}])
                 (commands/handle-step [:editor/search-block :reference])
-                (reset! commands/*slash-caret-pos (cursor/get-caret-pos input)))
+                (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)}))
 
               (and (= "〈" c)
                    (= "《" (util/nth-safe value (dec (dec current-pos))))
@@ -2825,7 +2824,7 @@
               (do
                 (commands/handle-step [:editor/input commands/angle-bracket {:last-pattern "《〈"
                                                                              :backward-pos 0}])
-                (reset! commands/*angle-bracket-caret-pos (cursor/get-caret-pos input))
+                (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
                 (state/set-editor-show-block-commands!))
 
               (nil? @search-timeout)

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

@@ -598,6 +598,10 @@
   []
   (:editor/action @state))
 
+(defn get-editor-action-data
+  []
+  (:editor/action-data @state))
+
 (defn set-editor-show-page-search!
   [value]
   (set-editor-action! (when value :page-search)))
@@ -635,7 +639,7 @@
   [value]
   (if value
     (do
-      (set-editor-action-data! value)
+      (set-editor-action-data! (assoc (get-editor-action-data) :options value))
       (set-editor-action! :input))
     (do
       (set-editor-action! nil)
@@ -651,8 +655,6 @@
   []
   (when-not (get-editor-action) (set-editor-action! :block-commands)))
 
-
-
 (defn set-editor-show-zotero!
   [value]
   (set-state! :editor/show-zotero value))
@@ -660,9 +662,7 @@
 (defn clear-editor-action!
   []
   (swap! state (fn [state]
-                 (assoc state
-                        :editor/action nil
-                        :editor/action-data nil))))
+                 (assoc state :editor/action nil))))
 
 (defn set-edit-input-id!
   [input-id]

+ 4 - 3
src/main/frontend/util/cursor.cljs

@@ -111,9 +111,10 @@
 (defn beginning-of-line?
   [input]
   (let [[content pos] (get-input-content&pos input)]
-    (or (zero? pos)
-        (when-let [pre-char (subs content (dec pos) pos)]
-          (= pre-char \newline)))))
+    (when content
+      (or (zero? pos)
+         (when-let [pre-char (subs content (dec pos) pos)]
+           (= pre-char \newline))))))
 
 (defn move-cursor-to-line-end
   [input]