소스 검색

feat: more bash-like basic in block editing shortcuts (#1741)

* feat: shortcut for kill line, cursor begin/end, kill block

* feat: move or kill by word

* fix: off by one

* fix: til alt need be avoid on mac

* feat: false to disable shortcut

* fix: disable more binding for mac

* fix: add prevent default for global shortcuts

* fix: ctrl+shift+b/f move by word for mac

* fix: move backword zero bug
Weihua 4 년 전
부모
커밋
018d517348

+ 51 - 7
src/main/frontend/handler/editor.cljs

@@ -337,7 +337,7 @@
   ([block value]
    (save-block-if-changed! block value nil))
   ([block value
-    {:keys []
+    {:keys [force?]
      :as opts}]
    (let [{:block/keys [uuid content file page format repo content properties]} block
          repo (or repo (state/get-current-repo))
@@ -352,6 +352,9 @@
          (util/format "Block with the id % already exists!" block-id)]
         :error)
 
+       force?
+       (save-block-inner! repo block value opts)
+
        :else
        (let [content-changed? (not= (string/trim content) (string/trim value))]
          (when (and content-changed? page)
@@ -1113,7 +1116,7 @@
 (defn save-current-block!
   ([]
    (save-current-block! {}))
-  ([opts]
+  ([{:keys [force?] :as opts}]
    ;; non English input method
    (when-not (state/editor-in-composition?)
      (when-let [repo (state/get-current-repo)]
@@ -1135,11 +1138,16 @@
                  db-content-without-heading (and db-content
                                                  (util/safe-subs db-content (:block/level db-block)))
                  value (and elem (gobj/get elem "value"))]
-             (when (and block value db-content-without-heading
-                        (or
-                         (not= (string/trim db-content-without-heading)
-                               (string/trim value))))
-               (save-block-aux! db-block value (:block/format db-block) opts)))
+             (cond
+               force?
+               (save-block-aux! db-block value (:block/format db-block) opts)
+
+               (and block value db-content-without-heading
+                    (or
+                     (not= (string/trim db-content-without-heading)
+                           (string/trim value))))
+               (save-block-aux! db-block value (:block/format db-block) opts))
+             )
            (catch js/Error error
              (log/error :save-block-failed error))))))))
 
@@ -2545,3 +2553,39 @@
 
         :else
         nil))))
+
+(defn clear-block-content! []
+  (save-current-block! {:force? true})
+  (state/set-edit-content! (state/get-edit-input-id) ""))
+
+(defn kill-line-before! []
+  (save-current-block! {:force? true})
+  (util/kill-line-before! (state/get-input)))
+
+(defn kill-line-after! []
+  (save-current-block! {:force? true})
+  (util/kill-line-after! (state/get-input)))
+
+(defn beginning-of-block []
+  (util/move-cursor-to (state/get-input) 0))
+
+(defn end-of-block []
+  (util/move-cursor-to-end (state/get-input)))
+
+(defn cursor-forward-word []
+  (util/move-cursor-forward-by-word (state/get-input)))
+
+(defn cursor-backward-word []
+  (util/move-cursor-backward-by-word (state/get-input)))
+
+(defn backward-kill-word []
+  (let [input (state/get-input)]
+    (save-current-block! {:force? true})
+    (util/backward-kill-word input)
+    (state/set-edit-content! (state/get-edit-input-id) (.-value input))))
+
+(defn forward-kill-word []
+  (let [input (state/get-input)]
+    (save-current-block! {:force? true})
+    (util/forward-kill-word input)
+    (state/set-edit-content! (state/get-edit-input-id) (.-value input))))

+ 20 - 0
src/main/frontend/modules/shortcut/binding.cljc

@@ -58,6 +58,26 @@
    :editor/delete "delete"
    :editor/delete-selection ["backspace" "delete"]
 
+   ;; clear the block content
+   :editor/clear-block (if mac? "ctrl+l" "alt+l")
+   ;; kill the line before the cursor position
+   :editor/kill-line-before (if mac? "ctrl+u" "alt+u")
+   ;; kill the line after the cursor position
+   :editor/kill-line-after (if mac? false "alt+k")
+   ;; go to the beginning of the block
+   :editor/beginning-of-block (if mac? false "alt+a")
+   ;; go to the end of the block
+   :editor/end-of-block (if mac? false "alt+e")
+   ;; forward one word
+   :editor/forward-word (if mac? "ctrl+shift+f" "alt+f")
+   ;; backward one word
+   :editor/backward-word (if mac? "ctrl+shift+b" "alt+b")
+   ;; kill one word backward
+   :editor/backward-kill-word (if mac? "ctrl+w" "alt+w")
+   ;; kill one word forward
+   :editor/forward-kill-word (if mac? false "alt+d")
+
+
    :editor/selection-up "up"
    :editor/selection-down "down"
 

+ 13 - 7
src/main/frontend/modules/shortcut/core.cljs

@@ -21,13 +21,19 @@
   [id]
   (let [shortcut (or (state/get-shortcut id)
                      (get (apply merge @binding-profile) id))]
-    (when-not shortcut
-      (log/error :shortcut/binding-not-found {:id id}))
-    (->>
-     (if (string? shortcut)
-       [shortcut]
-       shortcut)
-     (mapv mod-key))))
+    (cond
+      (nil? shortcut)
+      (log/error :shortcut/binding-not-found {:id id})
+
+      (false? shortcut)
+      (log/debug :shortcut/disabled {:id id})
+
+      :else
+      (->>
+       (if (string? shortcut)
+         [shortcut]
+         shortcut)
+       (mapv mod-key)))))
 
 (def global-keys #js
   [KeyCodes/TAB

+ 17 - 6
src/main/frontend/modules/shortcut/handler.cljs

@@ -34,7 +34,16 @@
     :editor/insert-link editor-handler/html-link-format!
     :editor/select-all-blocks editor-handler/select-all-blocks!
     :editor/move-block-up (editor-handler/move-up-down true)
-    :editor/move-block-down (editor-handler/move-up-down false)}))
+    :editor/move-block-down (editor-handler/move-up-down false)
+    :editor/clear-block editor-handler/clear-block-content!
+    :editor/kill-line-before editor-handler/kill-line-before!
+    :editor/kill-line-after editor-handler/kill-line-after!
+    :editor/beginning-of-block editor-handler/beginning-of-block
+    :editor/end-of-block editor-handler/end-of-block
+    :editor/forward-word editor-handler/cursor-forward-word
+    :editor/backward-word editor-handler/cursor-backward-word
+    :editor/backward-kill-word editor-handler/backward-kill-word
+    :editor/forward-kill-word editor-handler/forward-kill-word}))
 
 (def handler
   [;; global editor shortcut
@@ -53,12 +62,14 @@
     :editor/redo history/redo!}
 
    ;; global
-   {:ui/toggle-brackets config-handler/toggle-ui-show-brackets!
-    :go/search route-handler/go-to-search!
-    :go/journals route-handler/go-to-journals!
+   (before
+    m/prevent-default-behavior
+    {:ui/toggle-brackets config-handler/toggle-ui-show-brackets!
+     :go/search route-handler/go-to-search!
+     :go/journals route-handler/go-to-journals!
 
-    :search/re-index search-handler/rebuild-indices!
-    :graph/re-index #(repo-handler/re-index! nfs-handler/rebuild-index!)}
+     :search/re-index search-handler/rebuild-indices!
+     :graph/re-index #(repo-handler/re-index! nfs-handler/rebuild-index!)})
 
    ;; non-editing only
    (before

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

@@ -644,6 +644,24 @@
      (let [pos (count (gobj/get input "value"))]
        (move-cursor-to input pos))))
 
+#?(:cljs
+   (defn kill-line-before!
+     [input]
+     (let [val (.-value input)
+           end (.-selectionStart input)
+           n-pos (string/last-index-of val \newline (dec end))
+           start (if n-pos (inc n-pos) 0)]
+       (.setRangeText input "" start end))))
+
+#?(:cljs
+   (defn kill-line-after!
+     [input]
+     (let [val   (.-value input)
+           start (.-selectionStart input)
+           end   (or (string/index-of val \newline start)
+                     (count val))]
+       (.setRangeText input "" start end))))
+
 #?(:cljs
    (defn move-cursor-up
      "Move cursor up. If EOL, always move cursor to previous EOL."
@@ -1309,3 +1327,77 @@
    coll))
 
 (def pprint clojure.pprint/pprint)
+
+#?(:cljs
+   (defn move-cursor-forward-by-word
+     [input]
+     (let [val   (.-value input)
+           current (.-selectionStart input)
+           current (loop [idx current]
+                     (if (#{\space \newline} (nth-safe val idx))
+                       (recur (inc idx))
+                       idx))
+           idx (or (->> [(string/index-of val \space current)
+                         (string/index-of val \newline current)]
+                        (remove nil?)
+                        (apply min))
+                   (count val))]
+       (move-cursor-to input idx))))
+
+#?(:cljs
+   (defn move-cursor-backward-by-word
+     [input]
+     (let [val     (.-value input)
+           current (.-selectionStart input)
+           prev    (or
+                    (->> [(string/last-index-of val \space (dec current))
+                          (string/last-index-of val \newline (dec current))]
+                         (remove nil?)
+                         (apply max))
+                    0)
+           idx     (if (zero? prev)
+                     0
+                     (->
+                      (loop [idx prev]
+                        (if (#{\space \newline} (nth-safe val idx))
+                          (recur (dec idx))
+                          idx))
+                      inc))]
+       (move-cursor-to input idx))))
+
+#?(:cljs
+   (defn backward-kill-word
+     [input]
+     (let [val     (.-value input)
+           current (.-selectionStart input)
+           prev    (or
+                    (->> [(string/last-index-of val \space (dec current))
+                          (string/last-index-of val \newline (dec current))]
+                         (remove nil?)
+                         (apply max))
+                    0)
+           idx     (if (zero? prev)
+                     0
+                     (->
+                      (loop [idx prev]
+                        (if (#{\space \newline} (nth-safe val idx))
+                          (recur (dec idx))
+                          idx))
+                      inc))]
+       (.setRangeText input "" idx current))))
+
+#?(:cljs
+   (defn forward-kill-word
+     [input]
+     (let [val   (.-value input)
+           current (.-selectionStart input)
+           current (loop [idx current]
+                     (if (#{\space \newline} (nth-safe val idx))
+                       (recur (inc idx))
+                       idx))
+           idx (or (->> [(string/index-of val \space current)
+                         (string/index-of val \newline current)]
+                        (remove nil?)
+                        (apply min))
+                   (count val))]
+       (.setRangeText input "" current (inc idx)))))