Browse Source

fix: there's no auto-complete if the cursor moves back to [[]]

Related to #538
Tienson Qin 4 years ago
parent
commit
829c8ebb8d

+ 11 - 11
src/main/frontend/commands.cljs

@@ -346,10 +346,10 @@
                          (when-let [i (string/index-of (util/safe-subs edit-content current-pos) end-pattern)]
                            (+ current-pos i)))
                        current-pos)
-          prefix (subs edit-content 0 current-pos)
-          space? (when (and last-pattern prefix)
-                   (let [s (when-let [last-index (string/last-index-of prefix last-pattern)]
-                             (util/safe-subs prefix 0 last-index))]
+          orig-prefix (subs edit-content 0 current-pos)
+          space? (when (and last-pattern orig-prefix)
+                   (let [s (when-let [last-index (string/last-index-of orig-prefix last-pattern)]
+                             (util/safe-subs orig-prefix 0 last-index))]
                      (not
                       (or
                        (and s
@@ -362,9 +362,9 @@
                    space?)
           prefix (if (string/blank? last-pattern)
                    (if space?
-                     (util/concat-without-spaces prefix value)
-                     (str prefix value))
-                   (util/replace-last last-pattern prefix value space?))
+                     (util/concat-without-spaces orig-prefix value)
+                     (str orig-prefix value))
+                   (util/replace-last last-pattern orig-prefix value space?))
           postfix (subs edit-content current-pos)
           postfix (if postfix-fn (postfix-fn postfix) postfix)
           new-value (cond
@@ -373,14 +373,14 @@
 
                       :else
                       (str prefix postfix))
-          new-pos (- (+ (count prefix)
-                        (or forward-pos 0))
+          new-pos (- (count prefix)
                      (or backward-pos 0))]
       (state/set-block-content-and-last-pos! id new-value new-pos)
       (cursor/move-cursor-to input
-                             (if (or backward-pos forward-pos)
+                             (if (and (or backward-pos forward-pos)
+                                      (not= end-pattern "]]"))
                                new-pos
-                               (+ new-pos 1))))))
+                               (inc new-pos))))))
 
 (defn simple-insert!
   [id value

+ 3 - 2
src/main/frontend/components/editor.cljs

@@ -105,11 +105,12 @@
                  (when (state/sub :editor/show-page-search-hashtag?)
                    (util/safe-subs edit-content pos current-pos))
                  (when (> (count edit-content) current-pos)
-                   (util/safe-subs edit-content pos current-pos)))
+                   (util/safe-subs edit-content pos current-pos))
+                 "")
               matched-pages (when-not (string/blank? q)
                               (editor-handler/get-matched-pages q))
               matched-pages (cond
-                              (contains? (set (map string/lower-case matched-pages)) q)
+                              (contains? (set (map string/lower-case matched-pages)) (string/trim q))
                               matched-pages
 
                               (empty? matched-pages)

+ 3 - 13
src/main/frontend/diff.cljs

@@ -5,7 +5,8 @@
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
             [cljs-bean.core :as bean]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [frontend.text :as text]))
 
 ;; TODO: replace with diff-match-patch
 (defn diff
@@ -33,17 +34,6 @@
 (def inline-special-chars
   #{\* \_ \/ \` \+ \^ \~ \$})
 
-(defn- get-current-line-by-pos
-  [s pos]
-  (let [lines (string/split-lines s)
-        result (reduce (fn [acc line]
-                         (let [new-pos (+ acc (count line))]
-                           (if (>= new-pos pos)
-                             (reduced line)
-                             (inc new-pos)))) 0 lines)]
-    (when (string? result)
-      result)))
-
 (defn- markdown-link?
   [markup current-line pos]
   (and current-line
@@ -75,7 +65,7 @@
 
                       :else
                       (recur r1 t2 (inc i1) i2))))
-            current-line (get-current-line-by-pos markup pos)]
+            current-line (text/get-current-line-by-pos markup pos)]
         (cond
           (and (= (util/nth-safe markup pos)
                   (util/nth-safe markup (inc pos))

+ 114 - 94
src/main/frontend/handler/editor.cljs

@@ -220,8 +220,8 @@
   ([block pos id]
    (edit-block! block pos id nil))
   ([block pos id {:keys [custom-content tail-len move-cursor?]
-                         :or {tail-len 0
-                              move-cursor? true}}]
+                  :or {tail-len 0
+                       move-cursor? true}}]
    (when-not config/publishing?
      (when-let [block-id (:block/uuid block)]
        (let [block (or (db/pull [:block/uuid block-id]) block)
@@ -512,12 +512,12 @@
         blocks-container-id (when-let [id (:id config)]
                               (and (util/uuid-string? id) (medley/uuid id)))
         new-last-block (let [first-block-id {:db/id (:db/id first-block)}]
-                           (assoc last-block
-                                  :block/left first-block-id
-                                  :block/parent (if child?
-                                                  first-block-id
-                                                  ;; sibling
-                                                  (:block/parent first-block))))
+                         (assoc last-block
+                                :block/left first-block-id
+                                :block/parent (if child?
+                                                first-block-id
+                                                ;; sibling
+                                                (:block/parent first-block))))
         blocks [first-block new-last-block]
         blocks-atom (if blocks-container-id
                       (db/get-block-blocks-cache-atom repo blocks-container-id)
@@ -599,16 +599,16 @@
                                  :data [current-block next-block]}]
                        (db/refresh! repo opts)))]
     (if (or (:ref? config)
-              (not sibling?)
-              zooming?)
-        (refresh-fn)
-        (do
-          (profile "update cache " (update-cache-for-block-insert! repo config block blocks))
-          (state/add-tx! refresh-fn)))
-      ;; WORKAROUND: The block won't refresh itself even if the content is empty.
-      (when block-self?
-        (gobj/set input "value" ""))
-      (profile "ok handler" (ok-handler next-block))))
+            (not sibling?)
+            zooming?)
+      (refresh-fn)
+      (do
+        (profile "update cache " (update-cache-for-block-insert! repo config block blocks))
+        (state/add-tx! refresh-fn)))
+    ;; WORKAROUND: The block won't refresh itself even if the content is empty.
+    (when block-self?
+      (gobj/set input "value" ""))
+    (profile "ok handler" (ok-handler next-block))))
 
 (defn clear-when-saved!
   []
@@ -703,23 +703,23 @@
                             (wrap-parse-block)
                             (assoc :block/uuid (or custom-uuid (db/new-block-id))))
               [block-m sibling?] (cond
-                                     before?
-                                     (let [block (db/pull (:db/id (:block/left block)))
-                                           sibling? (if (:block/name block) false sibling?)]
-                                       [block sibling?])
+                                   before?
+                                   (let [block (db/pull (:db/id (:block/left block)))
+                                         sibling? (if (:block/name block) false sibling?)]
+                                     [block sibling?])
 
-                                     sibling?
-                                     [(db/pull (:db/id block)) sibling?]
+                                   sibling?
+                                   [(db/pull (:db/id block)) sibling?]
 
-                                     last-block
-                                     [last-block true]
+                                   last-block
+                                   [last-block true]
 
-                                     block
-                                     [(db/pull (:db/id block)) sibling?]
+                                   block
+                                   [(db/pull (:db/id block)) sibling?]
 
-                                     ;; FIXME: assert
-                                     :else
-                                     nil)]
+                                   ;; FIXME: assert
+                                   :else
+                                   nil)]
 
           (when block-m
             (outliner-insert-block! {:skip-save-current-block? true} block-m new-block sibling?)
@@ -1194,8 +1194,8 @@
                (remove (fn [block] (some-> (:db/id (:block/page block)) (not= page-id))))
                ;; expand collapsed blocks
                (mapv (fn [b] (if (:collapsed (:block/properties b))
-                           (vec (tree/sort-blocks (db/get-block-children repo (:block/uuid b)) b))
-                           [b])) )
+                               (vec (tree/sort-blocks (db/get-block-children repo (:block/uuid b)) b))
+                               [b])) )
                (flatten))
           block-ids* (mapv :block/uuid blocks*)
           level-blocks-map (blocks-with-level blocks*)
@@ -1509,15 +1509,15 @@
                                  (subs file-name last-dot-index)])
                               ["" ""])
             filename (str (gen-filename index file-base) ext)
-              filename (str path "/" filename)]
+            filename (str path "/" filename)]
                                         ;(js/console.debug "Write asset #" dir filename file)
         (if (util/electron?)
           (let [from (.-path file)
                 from (if (string/blank? from) nil from)]
             (p/then (js/window.apis.copyFileToAssets dir filename from)
                     #(p/resolved [filename (if (string? %) (js/File. #js[] %) file) (.join util/node-path dir filename)])))
-            (p/then (fs/write-file! repo dir filename (.stream file) nil)
-                    #(p/resolved [filename file]))))))))
+          (p/then (fs/write-file! repo dir filename (.stream file) nil)
+                  #(p/resolved [filename file]))))))))
 
 (defonce *assets-url-cache (atom {}))
 
@@ -1665,11 +1665,11 @@
     (when value
       (when-not (string/blank? selected) (reset! *selected-text selected))
       (let [[prefix _pos] (commands/simple-replace! input-id value selected
-                                                   {:backward-pos (count postfix)
-                                                    :check-fn (fn [new-value prefix-pos]
-                                                                (when (>= prefix-pos 0)
-                                                                  [(subs new-value prefix-pos (+ prefix-pos 2))
-                                                                   (+ prefix-pos 2)]))})]
+                                                    {:backward-pos (count postfix)
+                                                     :check-fn (fn [new-value prefix-pos]
+                                                                 (when (>= prefix-pos 0)
+                                                                   [(subs new-value prefix-pos (+ prefix-pos 2))
+                                                                    (+ prefix-pos 2)]))})]
         (case prefix
           "[["
           (do
@@ -1684,26 +1684,19 @@
           nil)))))
 
 (defn surround-by?
-  [input before after]
+  [input before end]
   (when input
     (let [value (gobj/get input "value")
-          pos (cursor/pos input)
-          start-pos (if (= :start before) 0 (- pos (count before)))
-          end-pos (if (= :end after) (count value) (+ pos (count after)))]
-      (when (>= (count value) end-pos)
-        (= (cond
-             (and (= :end after) (= :start before))
-             ""
-
-             (= :end after)
-             before
-
-             (= :start before)
-             after
+          pos (cursor/pos input)]
+      (text/surround-by? value pos before end))))
 
-             :else
-             (str before after))
-           (subs value start-pos end-pos))))))
+(defn wrapped-by?
+  [input before end]
+  (when input
+    (let [value (gobj/get input "value")
+          pos (dec (cursor/pos input))]
+      (when (>= pos 0)
+        (text/wrapped-by? value pos before end)))))
 
 (defn get-matched-pages
   [q]
@@ -1952,7 +1945,8 @@
   (when (and input
              (or (state/get-editor-show-page-search?)
                  (state/get-editor-show-page-search-hashtag?)
-                 (state/get-editor-show-block-search?)))
+                 (state/get-editor-show-block-search?))
+             (not (wrapped-by? input "[[" "]]")))
     (when (get-search-q)
       (let [value (gobj/get input "value")
             pos (:editor/last-saved-cursor @state/state)
@@ -2021,8 +2015,8 @@
         pos             (cursor/pos input)
         last-input-char (util/nth-safe (.-value input) (dec pos))]
 
-      ;; TODO: is it cross-browser compatible?
-      ;; (not= (gobj/get native-e "inputType") "insertFromPaste")
+    ;; TODO: is it cross-browser compatible?
+    ;; (not= (gobj/get native-e "inputType") "insertFromPaste")
     (if (= last-input-char (state/get-editor-command-trigger))
       (when (seq (get-matched-commands input))
         (reset! commands/*slash-caret-pos (cursor/get-caret-pos input))
@@ -2830,43 +2824,65 @@
           value (gobj/get input "value")
           c (util/nth-safe value (dec current-pos))]
       (when-not (state/get-editor-show-input)
-        (when (and (= "【" c (util/nth-safe value (dec (dec current-pos))))
-                   (> current-pos 0))
-          (commands/handle-step [:editor/input "[[]]" {:last-pattern "【【"
-                                                       :backward-pos 2}])
-          (commands/handle-step [:editor/search-page])
-          (reset! commands/*slash-caret-pos (cursor/get-caret-pos input)))
+        (cond
+          (and (= "【" c (util/nth-safe value (dec (dec current-pos))))
+               (> current-pos 0))
 
-        (when (and (= "(" c (util/nth-safe value (dec (dec current-pos))))
-                   (> current-pos 0))
-          (commands/handle-step [:editor/input "(())" {:last-pattern "(("
-                                                       :backward-pos 2}])
-          (commands/handle-step [:editor/search-block :reference])
-          (reset! commands/*slash-caret-pos (cursor/get-caret-pos input)))
+          (do
+            (commands/handle-step [:editor/input "[[]]" {:last-pattern "【【"
+                                                         :backward-pos 2}])
+            (commands/handle-step [:editor/search-page])
+            (reset! commands/*slash-caret-pos (cursor/get-caret-pos input)))
+
+          (and (not (contains? #{"ArrowDown" "ArrowLeft" "ArrowRight" "ArrowUp"} k))
+               (not (:editor/show-page-search? @state/state))
+               (not (:editor/show-page-search-hashtag? @state/state))
+               (wrapped-by? input "[[" "]]"))
+          (let [orig-pos (cursor/get-caret-pos input)
+                value (gobj/get input "value")
+                square-pos (string/last-index-of (subs value 0 (:pos orig-pos)) "[[")
+                pos (+ square-pos 2)
+                _ (state/set-last-pos! pos)
+                pos (assoc orig-pos :pos pos)
+                command-step (if (= \# (util/nth-safe value (dec square-pos)))
+                               :editor/search-page-hashtag
+                               :editor/search-page)]
+            (commands/handle-step [command-step])
+            (reset! commands/*slash-caret-pos pos))
+
+          (and (= "(" c (util/nth-safe value (dec (dec current-pos))))
+               (> current-pos 0))
+          (do
+            (commands/handle-step [:editor/input "(())" {:last-pattern "(("
+                                                         :backward-pos 2}])
+            (commands/handle-step [:editor/search-block :reference])
+            (reset! commands/*slash-caret-pos (cursor/get-caret-pos input)))
 
-        (when (and (= "〈" c)
-                   (= "《" (util/nth-safe value (dec (dec current-pos))))
-                   (> current-pos 0))
-          (commands/handle-step [:editor/input commands/angle-bracket {:last-pattern "《〈"
-                                                                       :backward-pos 0}])
-          (reset! commands/*angle-bracket-caret-pos (cursor/get-caret-pos input))
-          (reset! commands/*show-block-commands true))
-
-        (when (= c " ")
-          (when (or (= (util/nth-safe value (dec (dec current-pos))) "#")
-                    (not (state/get-editor-show-page-search?))
-                    (and (state/get-editor-show-page-search?)
-                         (not= (util/nth-safe value current-pos) "]")))
-            (state/set-editor-show-page-search-hashtag! false)))
-
-        (when (and @*show-commands (not= k (state/get-editor-command-trigger)))
+          (and (= "〈" c)
+               (= "《" (util/nth-safe value (dec (dec current-pos))))
+               (> current-pos 0))
+          (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))
+            (reset! commands/*show-block-commands true))
+
+          (and (= c " ")
+               (or (= (util/nth-safe value (dec (dec current-pos))) "#")
+                   (not (state/get-editor-show-page-search?))
+                   (and (state/get-editor-show-page-search?)
+                        (not= (util/nth-safe value current-pos) "]"))))
+          (state/set-editor-show-page-search-hashtag! false)
+
+          (and @*show-commands (not= k (state/get-editor-command-trigger)))
           (let [matched-commands (get-matched-commands input)]
             (if (seq matched-commands)
               (do
                 (reset! *show-commands true)
                 (reset! commands/*matched-commands matched-commands))
-              (reset! *show-commands false))))
-        (when (and @*show-block-commands (not= key-code 188)) ; not <
+              (reset! *show-commands false)))
+
+          (and @*show-block-commands (not= key-code 188)) ; not <
           (let [matched-block-commands (get-matched-block-commands input)]
             (if (seq matched-block-commands)
               (cond
@@ -2880,9 +2896,13 @@
 
                 :else
                 (reset! commands/*matched-block-commands matched-block-commands))
-              (reset! *show-block-commands false))))
-        (when (nil? @search-timeout)
-          (close-autocomplete-if-outside input))))))
+              (reset! *show-block-commands false)))
+
+          (nil? @search-timeout)
+          (close-autocomplete-if-outside input)
+
+          :else
+          nil)))))
 
 (defn editor-on-click!
   [id]

+ 1 - 0
src/main/frontend/search.cljs

@@ -130,6 +130,7 @@
                     (:name item))
                  result)
                 (remove nil?)
+                (map string/trim)
                 (distinct)
                 (filter (fn [name]
                           (exact-matched? q name))))))))))

+ 58 - 0
src/main/frontend/text.cljs

@@ -269,3 +269,61 @@
                             (not (string/starts-with? (string/lower-case line) key)))
                           lines)]
     (string/join "\n" new-lines)))
+
+(defn get-current-line-by-pos
+  [s pos]
+  (let [lines (string/split-lines s)
+        result (reduce (fn [acc line]
+                         (let [new-pos (+ acc (count line))]
+                           (if (>= new-pos pos)
+                             (reduced line)
+                             (inc new-pos)))) 0 lines)]
+    (when (string? result)
+      result)))
+
+(defn get-string-all-indexes
+  "Get all indexes of `value` in the string `s`."
+  [s value]
+  (loop [acc []
+         i 0]
+    (if-let [i (string/index-of s value i)]
+      (recur (conj acc i) (+ i (count value)))
+      acc)))
+
+(defn surround-by?
+  "`pos` must be surrounded by `before` and `and` in string `value`, e.g. ((|))"
+  [value pos before end]
+  (let [start-pos (if (= :start before) 0 (- pos (count before)))
+        end-pos (if (= :end end) (count value) (+ pos (count end)))]
+    (when (>= (count value) end-pos)
+      (= (cond
+           (and (= :end end) (= :start before))
+           ""
+
+           (= :end end)
+           before
+
+           (= :start before)
+           end
+
+           :else
+           (str before end))
+         (subs value start-pos end-pos)))))
+
+(defn wrapped-by?
+  "`pos` must be wrapped by `before` and `and` in string `value`, e.g. ((a|b))"
+  [value pos before end]
+  (let [before-matches (->> (get-string-all-indexes value before)
+                            (map (fn [i] [i :before])))
+        end-matches (->> (get-string-all-indexes value end)
+                         (map (fn [i] [i :end])))
+        indexes (sort-by first (concat before-matches end-matches [[pos :between]]))
+        ks (map second indexes)
+        q [:before :between :end]]
+    (true?
+     (reduce (fn [acc k]
+               (if (= q (conj acc k))
+                 (reduced true)
+                 (vec (take-last 2 (conj acc k)))))
+             []
+             ks))))

+ 12 - 0
src/test/frontend/text_test.cljs

@@ -125,4 +125,16 @@
                         "<2021-08-25 Wed>")
     "LATER hello world\nSCHEDULED: <2021-08-25 Wed>\nfoo:: bar\ntest"))
 
+(deftest get-string-all-indexes
+  []
+  (are [x y] (= x y)
+    (text/get-string-all-indexes "[[hello]] [[world]]" "[[")
+    [0 10]
+
+    (text/get-string-all-indexes "abc abc ab" "ab")
+    [0 4 8]
+
+    (text/get-string-all-indexes "abc abc ab" "ab")
+    [0 3]))
+
 #_(cljs.test/test-ns 'frontend.text-test)