diff --git a/src/cljs/athens/db.cljs b/src/cljs/athens/db.cljs index d8c5c899b2..2d23699505 100644 --- a/src/cljs/athens/db.cljs +++ b/src/cljs/athens/db.cljs @@ -188,7 +188,13 @@ [(inc ?o) ?new-o]] [(dec-after ?p ?at ?ch ?new-o) (after ?p ?at ?ch ?o) - [(dec ?o) ?new-o]]]) + [(dec ?o) ?new-o]] + [(plus-after ?p ?at ?ch ?new-o ?x) + (after ?p ?at ?ch ?o) + [(+ ?o ?x) ?new-o]] + [(minus-after ?p ?at ?ch ?new-o ?x) + (after ?p ?at ?ch ?o) + [(- ?o ?x) ?new-o]]]) (defn sort-block-children diff --git a/src/cljs/athens/events.cljs b/src/cljs/athens/events.cljs index b052645235..e3675033f2 100644 --- a/src/cljs/athens/events.cljs +++ b/src/cljs/athens/events.cljs @@ -340,6 +340,15 @@ @db/dsdb rules eid order))) +(defn plus-after + [eid order x] + (->> (d/q '[:find ?ch ?new-o + :keys db/id block/order + :in $ % ?p ?at ?x + :where (plus-after ?p ?at ?ch ?new-o ?x)] + @db/dsdb rules eid order x))) + + (reg-event-fx :up (fn [_ [_ uid]] @@ -600,6 +609,33 @@ (drop-bullet source-uid target-uid kind))) +;; TODO: convert to tree instead of flat map (handling indentation), write tests for markdown list parsing +(reg-event-fx + :paste + (fn [_ [_ uid text]] + (let [lines (clojure.string/split-lines text) + block (db/get-block [:block/uid uid]) + {b-order :block/order} block + parent (db/get-parent [:block/uid uid]) + {p-id :db/id} parent + now (now-ts) + new-datoms (map-indexed (fn [i x] + (let [start (subs x 0 2) + s (if (or (= start "- ") + (= start "* ")) + (subs x 2) + x)] + {:block/uid (gen-block-uid) + :create/time now + :edit/time now + :block/order (+ 1 i b-order) + :block/string s})) + lines) + reindex (plus-after p-id b-order (count lines)) + children (concat new-datoms reindex)] + {:dispatch [:transact [{:db/id p-id :block/children children}]]}))) + + (defn left-sidebar-drop-above [s-order t-order] (let [source-eid (d/q '[:find ?e . diff --git a/src/cljs/athens/keybindings.cljs b/src/cljs/athens/keybindings.cljs index c0e2faaaba..836558eae4 100644 --- a/src/cljs/athens/keybindings.cljs +++ b/src/cljs/athens/keybindings.cljs @@ -385,9 +385,9 @@ ;; XXX: what happens here when we have multi-block selection? In this case we pass in `uids` instead of `uid` (defn block-key-down [e uid state] - (let [{:keys [meta ctrl key-code]} (destruct-event e)] - (prn "DOWN" (destruct-event e)) - ;(swap! state) + (let [d-event (destruct-event e) + {:keys [meta ctrl key-code]} d-event] + (swap! state assoc :last-keydown d-event) (cond (arrow-key-direction e) (handle-arrow-key e uid state) (pair-char? e) (handle-pair-char e uid state) diff --git a/src/cljs/athens/listeners.cljs b/src/cljs/athens/listeners.cljs index 8318e8b957..27fbf95ee0 100644 --- a/src/cljs/athens/listeners.cljs +++ b/src/cljs/athens/listeners.cljs @@ -148,8 +148,7 @@ (->> blocks (map (fn [x] [:block/uid x])) (d/pull-many @dsdb '[:block/string]) - (map #(str "- " (:block/string %))) - (clojure.string/join "\r\n"))) + (map #(str "- " (:block/string %) "\n")))) (defn copy diff --git a/src/cljs/athens/views/blocks.cljs b/src/cljs/athens/views/blocks.cljs index 97123e0f32..2b7d6fc9d1 100644 --- a/src/cljs/athens/views/blocks.cljs +++ b/src/cljs/athens/views/blocks.cljs @@ -21,7 +21,8 @@ [komponentit.autosize :as autosize] [re-frame.core :refer [dispatch subscribe]] [reagent.core :as r] - [stylefy.core :as stylefy :refer [use-style]]) + [stylefy.core :as stylefy :refer [use-style]] + [clojure.string :as string]) (:import (goog.events EventType))) @@ -450,31 +451,25 @@ :on-click #(athens.keybindings/select-slash-cmd i state)} [:<> [(r/adapt-react-class icon)] [:span text] (when kbd [:kbd kbd])]])]])) -;; TODO: Count whitespace chars at the front of each line. Each char is one indentation level. + (defn paste + "if user does typical copy and paste, meta+v, and " [e uid state] (let [data (.. e -clipboardData (getData "text")) - lines (str/split-lines data)] - - (if (= 1 (count lines)) - (let [{:keys [head tail]} (destruct-event e) - new-str (str head data tail)] - (swap! state assoc :atom-string new-str)) - (let [now (now-ts) - datoms (map (fn [x] - [:db/id [:block/uid (gen-block-uid)] - :create/time now - :edit/time now - :block/order 0 ;; must reindex as well - :block/string x]) - lines)] - (dispatch [:transact [datoms]]))))) + is-block (re-find #"\r?\n" data) + last-keydown (:last-keydown @state) + {:keys [shift]} last-keydown] + ;; if `not shift`, do normal plain-text paste + (when (and is-block (not shift)) + (.. e preventDefault) + (dispatch [:paste uid data])))) + (defn block-on-change [e uid state] - (let [{:keys [value]} (destruct-event e)] - (prn "CHANGE" value) - (swap! state assoc :atom-string value))) + (let [] + (swap! state assoc :atom-string (.. e -target -value)))) + ;; Actual string contents - two elements, one for reading and one for writing ;; seems hacky, but so far no better way to click into the correct position with one conditional element @@ -554,7 +549,8 @@ :search/index 0 :dragging false :drag-target nil - :edit/time (:edit/time block)})] + :edit/time (:edit/time block) + :last-keydown nil})] (add-watch state :string-listener (fn [_context _atom old new] (let [{:keys [atom-string]} new]