Browse Source

feat(editor): paste-block-tree-after-target

rcmerci 4 years ago
parent
commit
b3f792bc7a
2 changed files with 116 additions and 56 deletions
  1. 112 56
      src/main/frontend/handler/editor.cljs
  2. 4 0
      src/main/frontend/util/property.cljs

+ 112 - 56
src/main/frontend/handler/editor.cljs

@@ -282,8 +282,8 @@
 (defn- remove-non-existed-refs!
 (defn- remove-non-existed-refs!
   [refs]
   [refs]
   (remove (fn [x] (and (vector? x)
   (remove (fn [x] (and (vector? x)
-                      (= :block/uuid (first x))
-                      (nil? (db/entity x)))) refs))
+                       (= :block/uuid (first x))
+                       (nil? (db/entity x)))) refs))
 
 
 (defn- wrap-parse-block
 (defn- wrap-parse-block
   [{:block/keys [content format parent left page uuid pre-block?] :as block}]
   [{:block/keys [content format parent left page uuid pre-block?] :as block}]
@@ -581,7 +581,6 @@
                value)]
                value)]
     [properties value]))
     [properties value]))
 
 
-
 (defn insert-new-block!
 (defn insert-new-block!
   ([state]
   ([state]
    (insert-new-block! state nil))
    (insert-new-block! state nil))
@@ -725,8 +724,8 @@
           format (or (db/get-page-format (state/get-current-page))
           format (or (db/get-page-format (state/get-current-page))
                      (state/get-preferred-format))
                      (state/get-preferred-format))
           cond-fn (fn [marker] (or (and (= :markdown format)
           cond-fn (fn [marker] (or (and (= :markdown format)
-                                       (util/safe-re-find (re-pattern (str "#*\\s*" marker)) content))
-                                  (util/starts-with? content "TODO")))
+                                        (util/safe-re-find (re-pattern (str "#*\\s*" marker)) content))
+                                   (util/starts-with? content "TODO")))
           [new-content marker] (cond
           [new-content marker] (cond
                                  (cond-fn "TODO")
                                  (cond-fn "TODO")
                                  [(string/replace-first content "TODO" "DOING") "DOING"]
                                  [(string/replace-first content "TODO" "DOING") "DOING"]
@@ -749,7 +748,6 @@
         (state/set-edit-content! edit-input-id new-content)
         (state/set-edit-content! edit-input-id new-content)
         (util/set-caret-pos! current-input new-pos)))))
         (util/set-caret-pos! current-input new-pos)))))
 
 
-
 (defn set-marker
 (defn set-marker
   [{:block/keys [uuid marker content format properties] :as block} new-marker]
   [{:block/keys [uuid marker content format properties] :as block} new-marker]
   (let [new-content (-> (string/replace-first content marker new-marker)
   (let [new-content (-> (string/replace-first content marker new-marker)
@@ -1164,9 +1162,8 @@
               (edit-block! {:block/uuid block-id} :max nil block-id))
               (edit-block! {:block/uuid block-id} :max nil block-id))
             (let [page-id (some-> (db/entity [:block/uuid block-id])
             (let [page-id (some-> (db/entity [:block/uuid block-id])
                                   :block/page
                                   :block/page
-                                  :db/id
+                                  :db/id)]
 
 
-                                  )]
               (when-let [page-name (:block/name (db/entity page-id))]
               (when-let [page-name (:block/name (db/entity page-id))]
                 (route-handler/redirect! {:to :page
                 (route-handler/redirect! {:to :page
                                           :path-params {:name page-name}})
                                           :path-params {:name page-name}})
@@ -1908,6 +1905,14 @@
     (state/set-editor-show-block-search! false)
     (state/set-editor-show-block-search! false)
     (util/cursor-move-forward input 2)))
     (util/cursor-move-forward input 2)))
 
 
+(defn- get-block-tree-insert-pos-after-target
+  "return [target-block sibling? delete-editing-block? editing-block]"
+  ([target-block-id sibling?]
+   (get-block-tree-insert-pos-after-target target-block-id sibling? nil))
+  ([target-block-id sibling? editing-block]
+   (when-let [target-block (db/pull target-block-id)]
+     [target-block sibling? false (or editing-block target-block)])))
+
 (defn- get-block-tree-insert-pos-at-point
 (defn- get-block-tree-insert-pos-at-point
   "return [target-block sibling? delete-editing-block? editing-block]"
   "return [target-block sibling? delete-editing-block? editing-block]"
   []
   []
@@ -1988,8 +1993,8 @@
                      :block/page (select-keys page [:db/id])
                      :block/page (select-keys page [:db/id])
                      :block/format format
                      :block/format format
                      :block/properties (apply dissoc (:block/properties block)
                      :block/properties (apply dissoc (:block/properties block)
-                                         (concat [:id :custom_id :custom-id]
-                                                 exclude-properties))
+                                              (concat [:id :custom_id :custom-id]
+                                                      exclude-properties))
                      :block/meta (dissoc (:block/meta block) :start-pos :end-pos)
                      :block/meta (dissoc (:block/meta block) :start-pos :end-pos)
                      :block/content new-content
                      :block/content new-content
                      :block/title new-title
                      :block/title new-title
@@ -1999,14 +2004,17 @@
          (assoc m :block/file (select-keys file [:db/id]))
          (assoc m :block/file (select-keys file [:db/id]))
          m)))))
          m)))))
 
 
-(defn- paste-block-tree-at-point
-  ([tree exclude-properties] (paste-block-tree-at-point tree exclude-properties nil))
+(defn- paste-block-vec-tree-at-target
+  ([tree exclude-properties]
+   (paste-block-vec-tree-at-target tree exclude-properties nil nil))
   ([tree exclude-properties content-update-fn]
   ([tree exclude-properties content-update-fn]
+   (paste-block-vec-tree-at-target tree exclude-properties content-update-fn nil))
+  ([tree exclude-properties content-update-fn get-pos-fn]
    (let [repo (state/get-current-repo)
    (let [repo (state/get-current-repo)
          page (:block/page (db/entity (:db/id (state/get-edit-block))))
          page (:block/page (db/entity (:db/id (state/get-edit-block))))
          file (:block/file page)]
          file (:block/file page)]
      (when-let [[target-block sibling? delete-editing-block? editing-block]
      (when-let [[target-block sibling? delete-editing-block? editing-block]
-                (get-block-tree-insert-pos-at-point)]
+                ((or get-pos-fn get-block-tree-insert-pos-at-point))]
        (let [target-block (outliner-core/block target-block)
        (let [target-block (outliner-core/block target-block)
              editing-block (outliner-core/block editing-block)
              editing-block (outliner-core/block editing-block)
              format (or (:block/format target-block) (state/get-preferred-format))
              format (or (:block/format target-block) (state/get-preferred-format))
@@ -2033,6 +2041,57 @@
          (db/refresh! repo {:key :block/insert :data new-blocks})
          (db/refresh! repo {:key :block/insert :data new-blocks})
          (last metadata-replaced-blocks))))))
          (last metadata-replaced-blocks))))))
 
 
+(defn- tree->vec-tree
+  "tree:
+  [
+  {
+    :content 'this is a block',
+    :props {\"key\" \"value\" \"key2\" \"value2\"},
+    :children [
+      { :content 'this is child block' }
+    ]
+  },
+  {
+    :content 'this is sibling block'
+  }
+  ]"
+  [tree]
+  (into []
+        (mapcat
+         (fn [e]
+           (let [e* (select-keys e [:content :props])]
+             (if-let [children (:children e)]
+               [e* (tree->vec-tree (:children e))]
+               [e*])))
+         tree)))
+
+(defn- vec-tree->vec-block-tree
+  [tree format]
+  (let [loc (zip/vector-zip tree)]
+    (loop [loc loc]
+      (if (zip/end? loc)
+        (zip/root loc)
+        (let [node (zip/node loc)]
+          (if (vector? node)
+            (recur (zip/next loc))
+            (let [content (:content node)
+                  props (into [] (:props node))
+                  content* (str "- "
+                                (property/insert-properties format content props))
+                  ast (mldoc/->edn content* (mldoc/default-config format))
+                  blocks (block/extract-blocks ast content* true format)
+                  fst-block (first blocks)]
+              (assert fst-block "fst-block shouldn't be nil")
+              (recur (zip/next (zip/replace loc fst-block))))))))))
+
+(defn paste-block-tree-after-target
+  [target-block-id sibling? tree format]
+  (let [vec-tree (tree->vec-tree tree)
+        block-tree (vec-tree->vec-block-tree vec-tree format)]
+    (paste-block-vec-tree-at-target
+     block-tree [] nil
+     #(get-block-tree-insert-pos-after-target target-block-id sibling?))))
+
 (defn template-on-chosen-handler
 (defn template-on-chosen-handler
   [_input id _q format _edit-block _edit-content]
   [_input id _q format _edit-block _edit-content]
   (fn [[_template db-id] _click?]
   (fn [[_template db-id] _click?]
@@ -2145,8 +2204,8 @@
 (defn- select-up-down [direction]
 (defn- select-up-down [direction]
   (let [selected (first (state/get-selection-blocks))
   (let [selected (first (state/get-selection-blocks))
         f (case direction
         f (case direction
-                :up get-prev-block-non-collapsed
-                :down get-next-block-non-collapsed)
+            :up get-prev-block-non-collapsed
+            :down get-next-block-non-collapsed)
         sibling-block (f selected)]
         sibling-block (f selected)]
     (when (and sibling-block (dom/attr sibling-block "blockid"))
     (when (and sibling-block (dom/attr sibling-block "blockid"))
       (clear-selection! nil)
       (clear-selection! nil)
@@ -2159,8 +2218,8 @@
         line-pos (util/get-first-or-last-line-pos input)
         line-pos (util/get-first-or-last-line-pos input)
         repo (state/get-current-repo)
         repo (state/get-current-repo)
         f (case direction
         f (case direction
-                :up get-prev-block-non-collapsed
-                :down get-next-block-non-collapsed)
+            :up get-prev-block-non-collapsed
+            :down get-next-block-non-collapsed)
         sibling-block (f (gdom/getElement (state/get-editing-block-dom-id)))
         sibling-block (f (gdom/getElement (state/get-editing-block-dom-id)))
         {:block/keys [uuid content format]} (state/get-edit-block)]
         {:block/keys [uuid content format]} (state/get-edit-block)]
     (when sibling-block
     (when sibling-block
@@ -2385,7 +2444,7 @@
         (let [repo (state/get-current-repo)]
         (let [repo (state/get-current-repo)]
           (db/refresh! repo
           (db/refresh! repo
                        {:key :block/change :data [(:data current-node)]}))))
                        {:key :block/change :data [(:data current-node)]}))))
-  (state/set-editor-op! :nil)))
+    (state/set-editor-op! :nil)))
 
 
 (defn keydown-tab-handler
 (defn keydown-tab-handler
   [direction]
   [direction]
@@ -2563,7 +2622,6 @@
                  timeout)))
                  timeout)))
       (edit-box-on-change! e block id))))
       (edit-box-on-change! e block id))))
 
 
-
 (defn- get-current-page-format
 (defn- get-current-page-format
   []
   []
   (when-let [page (state/get-current-page)]
   (when-let [page (state/get-current-page)]
@@ -2579,7 +2637,7 @@
         tree* (->> tree
         tree* (->> tree
                    (mapv #(assoc % :level (- (:block/level %) prefix-level)))
                    (mapv #(assoc % :level (- (:block/level %) prefix-level)))
                    (blocks-vec->tree))]
                    (blocks-vec->tree))]
-    (paste-block-tree-at-point tree* [])))
+    (paste-block-vec-tree-at-target tree* [])))
 
 
 (defn- paste-segmented-text
 (defn- paste-segmented-text
   [format text]
   [format text]
@@ -2589,8 +2647,8 @@
                      (mapv (fn [p] (->> (string/trim p)
                      (mapv (fn [p] (->> (string/trim p)
                                         ((fn [p]
                                         ((fn [p]
                                            (if (util/safe-re-find (if (= format :org)
                                            (if (util/safe-re-find (if (= format :org)
-                                                          #"\s*\*+\s+"
-                                                          #"\s*-\s+") p)
+                                                                    #"\s*\*+\s+"
+                                                                    #"\s*-\s+") p)
                                              p
                                              p
                                              (str (if (= format :org) "* " "- ") p))))))
                                              (str (if (= format :org) "* " "- ") p))))))
                            paragraphs))]
                            paragraphs))]
@@ -2610,7 +2668,7 @@
          (= (string/trim text) (string/trim (:copy/content copied-blocks))))
          (= (string/trim text) (string/trim (:copy/content copied-blocks))))
       (do
       (do
         ;; copy from logseq internally
         ;; copy from logseq internally
-        (paste-block-tree-at-point copied-block-tree [])
+        (paste-block-vec-tree-at-target copied-block-tree [])
         (util/stop e))
         (util/stop e))
 
 
       (do
       (do
@@ -2649,35 +2707,35 @@
   [id]
   [id]
   (fn [e]
   (fn [e]
     (if-let [handled
     (if-let [handled
-               (let [pick-one-allowed-item
-                     (fn [items]
-                       (if (util/electron?)
-                         (let [existed-file-path (js/window.apis.getFilePathFromClipboard)
-                               existed-file-path (if (and
-                                                      (string? existed-file-path)
-                                                      (not util/mac?)
-                                                      (not util/win32?)) ; FIXME: linux
-                                                   (when (util/safe-re-find #"^(/[^/ ]*)+/?$" existed-file-path)
-                                                     existed-file-path)
+             (let [pick-one-allowed-item
+                   (fn [items]
+                     (if (util/electron?)
+                       (let [existed-file-path (js/window.apis.getFilePathFromClipboard)
+                             existed-file-path (if (and
+                                                    (string? existed-file-path)
+                                                    (not util/mac?)
+                                                    (not util/win32?)) ; FIXME: linux
+                                                 (when (util/safe-re-find #"^(/[^/ ]*)+/?$" existed-file-path)
                                                    existed-file-path)
                                                    existed-file-path)
-                               has-file-path? (not (string/blank? existed-file-path))
-                               has-image? (js/window.apis.isClipboardHasImage)]
-                           (if (or has-image? has-file-path?)
-                             [:asset (js/File. #js[] (if has-file-path? existed-file-path "image.png"))]))
-
-                         (when (and items (.-length items))
-                           (let [files (. (js/Array.from items) (filter #(= (.-kind %) "file")))
-                                 it (gobj/get files 0) ;;; TODO: support multiple files
-                                 mime (and it (.-type it))]
-                             (cond
-                               (contains? #{"image/jpeg" "image/png" "image/jpg" "image/gif"} mime) [:asset (. it getAsFile)])))))
-                     clipboard-data (gobj/get e "clipboardData")
-                     items (or (.-items clipboard-data)
-                               (.-files clipboard-data))
-                     picked (pick-one-allowed-item items)]
-                 (if (get picked 1)
-                   (match picked
-                     [:asset file] (set-asset-pending-file file))))]
+                                                 existed-file-path)
+                             has-file-path? (not (string/blank? existed-file-path))
+                             has-image? (js/window.apis.isClipboardHasImage)]
+                         (if (or has-image? has-file-path?)
+                           [:asset (js/File. #js[] (if has-file-path? existed-file-path "image.png"))]))
+
+                       (when (and items (.-length items))
+                         (let [files (. (js/Array.from items) (filter #(= (.-kind %) "file")))
+                               it (gobj/get files 0) ;;; TODO: support multiple files
+                               mime (and it (.-type it))]
+                           (cond
+                             (contains? #{"image/jpeg" "image/png" "image/jpg" "image/gif"} mime) [:asset (. it getAsFile)])))))
+                   clipboard-data (gobj/get e "clipboardData")
+                   items (or (.-items clipboard-data)
+                             (.-files clipboard-data))
+                   picked (pick-one-allowed-item items)]
+               (if (get picked 1)
+                 (match picked
+                   [:asset file] (set-asset-pending-file file))))]
       (util/stop e)
       (util/stop e)
       (paste-text (.getData (gobj/get e "clipboardData") "text") e))))
       (paste-text (.getData (gobj/get e "clipboardData") "text") e))))
 
 
@@ -2736,7 +2794,6 @@
       :else
       :else
       (js/document.execCommand "copy"))))
       (js/document.execCommand "copy"))))
 
 
-
 (defn shortcut-cut
 (defn shortcut-cut
   "shortcut cut action:
   "shortcut cut action:
   * when in selection mode, cut selected blocks
   * when in selection mode, cut selected blocks
@@ -2907,15 +2964,14 @@
         (if (> level max-level)
         (if (> level max-level)
           nil
           nil
           (let [blocks-to-expand (->> blocks-with-level
           (let [blocks-to-expand (->> blocks-with-level
-                                 (filter (fn [b] (= (:block/level b) level)))
-                                 (filter (fn [{:block/keys [properties]}]
-                                           (contains? properties :collapsed))))]
+                                      (filter (fn [b] (= (:block/level b) level)))
+                                      (filter (fn [{:block/keys [properties]}]
+                                                (contains? properties :collapsed))))]
             (if (empty? blocks-to-expand)
             (if (empty? blocks-to-expand)
               (recur (inc level))
               (recur (inc level))
               (doseq [{:block/keys [uuid]} blocks-to-expand]
               (doseq [{:block/keys [uuid]} blocks-to-expand]
                 (expand-block! uuid)))))))))
                 (expand-block! uuid)))))))))
 
 
-
 (defn collapse!
 (defn collapse!
   []
   []
   (cond
   (cond

+ 4 - 0
src/main/frontend/util/property.cljs

@@ -224,6 +224,10 @@
          :else
          :else
          content)))))
          content)))))
 
 
+(defn insert-properties
+  [format content kvs]
+  (reduce (fn [content [k v]] (insert-property format content k v)) content kvs))
+
 (defn remove-property
 (defn remove-property
   ([format key content]
   ([format key content]
    (remove-property format key content true))
    (remove-property format key content true))