diff --git a/web/src/main/frontend/commands.cljs b/web/src/main/frontend/commands.cljs index 0ad7375378..6e19ebe4ce 100644 --- a/web/src/main/frontend/commands.cljs +++ b/web/src/main/frontend/commands.cljs @@ -12,6 +12,7 @@ (defonce *show-block-commands (atom false)) (defonce angle-bracket "<") (defonce *angle-bracket-caret-pos (atom nil)) +(defonce *current-command (atom nil)) (defn ->page-reference [page] @@ -31,12 +32,10 @@ (defn ->inline [type] - (let [template (util/format "@@%s: %s@@" - type - type) - backward-pos (count (str type "@@"))] + (let [template (util/format "@@%s: @@" + type)] [[:editor/input template {:last-pattern slash - :backward-pos backward-pos}]])) + :backward-pos 2}]])) (defn embed-block [] @@ -154,6 +153,8 @@ {:keys [last-pattern postfix-fn backward-pos forward-pos] :or {last-pattern slash} :as option}] + (prn "Insert: "{:value value + :option option}) (let [input (gdom/getElement id) edit-content (gobj/get input "value") current-pos (:pos (util/get-caret-pos input)) @@ -167,6 +168,8 @@ new-pos (- (+ (count prefix) (or forward-pos 0)) (or backward-pos 0))] + (prn {:prefix prefix + :postfix postfix}) (state/set-heading-content-and-last-pos! id new-value new-pos) (util/move-cursor-to input (if (or backward-pos forward-pos) diff --git a/web/src/main/frontend/components/editor.cljs b/web/src/main/frontend/components/editor.cljs index d8f457a666..eabce0f2be 100644 --- a/web/src/main/frontend/components/editor.cljs +++ b/web/src/main/frontend/components/editor.cljs @@ -21,6 +21,7 @@ *angle-bracket-caret-pos *matched-block-commands *show-block-commands]] + [frontend.format.block :as block] [medley.core :as medley] [cljs-time.core :as t] [cljs-time.coerce :as tc] @@ -129,12 +130,6 @@ 100)] (reset! *image-uploading-process process))))))) -(defn with-levels - [text format {:heading/keys [level pre-heading?]}] - (let [pattern (config/get-heading-pattern format) - prefix (if pre-heading? "" (str (apply str (repeat level pattern)) " "))] - (str prefix (string/triml text)))) - (rum/defc commands < rum/reactive [id format] (when (and (util/react *show-commands) @@ -146,6 +141,7 @@ (ui/auto-complete (map first matched) {:on-chosen (fn [chosen] + (reset! commands/*current-command chosen) (let [restore-slash? (not (contains? #{"Page Reference" "Link" "Image Link" @@ -253,10 +249,9 @@ (state/set-editor-show-block-search false) (let [uuid-string (str (:heading/uuid chosen))] (insert-command! id - (util/format "((%s))" uuid-string) + (str "@@embed: " uuid-string) format - {:last-pattern (str "((" q) - :postfix-fn (fn [s] (util/replace-first "))" s ""))}) + {:last-pattern "@@embed: "}) ;; Save it so it'll be remembered when next time it got parsing (handler/set-heading-property! (:heading/uuid chosen) "CUSTOM_ID" @@ -506,7 +501,7 @@ heading)] (set-last-edit-heading! (:heading/uuid heading) value) ;; save the current heading and insert a new heading - (let [value-with-levels (with-levels value format heading) + (let [value-with-levels (block/with-levels value format heading) [_first-heading last-heading _new-heading-content] (handler/insert-new-heading! heading value-with-levels) last-id (:heading/uuid last-heading)] (handler/edit-heading! last-id :max format id) @@ -537,7 +532,7 @@ (dec level) level) :else level) - new-value (with-levels value format (assoc heading :heading/level final-level))] + new-value (block/with-levels value format (assoc heading :heading/level final-level))] (set-last-edit-heading! (:heading/uuid heading) value) (handler/save-heading-if-changed! heading new-value))) @@ -726,7 +721,7 @@ input :upload-images)) (when (:db/id (db/entity repo [:heading/uuid (:heading/uuid heading)])) - (let [new-value (with-levels value format heading)] + (let [new-value (block/with-levels value format heading)] (let [cache [(:heading/uuid heading) value]] (when (not= @*last-edit-heading cache) (handler/save-heading-if-changed! heading new-value) diff --git a/web/src/main/frontend/components/hiccup.cljs b/web/src/main/frontend/components/hiccup.cljs index 6699fdc05c..62e4fd103e 100644 --- a/web/src/main/frontend/components/hiccup.cljs +++ b/web/src/main/frontend/components/hiccup.cljs @@ -23,6 +23,7 @@ [frontend.util :as util :refer-macros [profile]] [frontend.mixins :as mixins] ["/frontend/utils" :as utils] + [frontend.format.block :as block] [clojure.walk :as walk])) ;; local state @@ -205,6 +206,8 @@ [element {:id id} s])) +(declare headings-container) + (rum/defc inline < rum/reactive [config item] (match item @@ -332,6 +335,16 @@ ["Export_Snippet" "hiccup" s] (reader/read-string s) + ["Export_Snippet" "embed" s] + (when s + (let [s (string/trim s)] + (if (util/uuid-string? s) + (let [id (uuid s) + headings (db/get-heading-and-children (state/get-current-repo) id) + headings (map (fn [h] (assoc h "embed?" true)) headings)] + [:div.embed-block + (headings-container headings config)])))) + ["Break_Line"] [:br] ["Hard_Break_Line"] @@ -461,12 +474,20 @@ [:span.bullet]]]])) (defn- build-id - [config ref? sidebar?] - (cond - ref? (str (:id config) "-") - sidebar? (str "sidebar-" (:id config) "-") - (:custom-query? config) (str "custom-query-" (:id config) "-") - :else nil)) + [config ref? sidebar? embed?] + (cond->> + "" + (:id config) + (str (:id config) "-") + + (:custom-query? config) + (str "custom-query-") + + embed? + (str "embed-") + + sidebar? + (str "sidebar-"))) (rum/defc dnd-separator [heading margin-left bottom top? nested?] @@ -578,7 +599,24 @@ (rum/defc heading-content-or-editor < rum/reactive [config {:heading/keys [uuid title level body meta content dummy? page format repo children pre-heading? collapsed? idx] :as heading} edit-input-id heading-id slide?] - (let [edit? (state/sub [:editor/editing? edit-input-id])] + (let [current-edit-input-id (state/sub-edit-input-id) + edit? (= current-edit-input-id edit-input-id) + follower? (and (not edit?) + current-edit-input-id + (when-let [s (util/extract-uuid current-edit-input-id)] + (= (cljs.core/uuid s) uuid))) + heading (if follower? + (let [content (state/sub [:editor/content current-edit-input-id]) + content (block/with-levels content format heading) + new-heading (first (second (first (block/parse-heading + (assoc heading :heading/content content) format))))] + (merge new-heading + {:heading/level level + :heading/meta meta + :heading/dummy? dummy? + :heading/children children + :heading/pre-heading? pre-heading?})) + heading)] (if edit? [:div {:id (str "editor-" edit-input-id)} (editor/box (string/trim content) @@ -644,7 +682,7 @@ (when (and (not pre-heading?) (seq body)) [:div.heading-body {:style {:display (if collapsed? "none" "")}} ;; TODO: consistent id instead of the idx (since it could be changed later) - (for [[idx child] (medley/indexed body)] + (for [[idx child] (medley/indexed (:heading/body heading))] (rum/with-key (heading-child (block config child)) @@ -670,7 +708,8 @@ sidebar? (boolean (:sidebar? config)) slide? (boolean (:slide? config)) doc-mode? (:document/mode? config) - unique-dom-id (build-id config ref? sidebar?) + embed? (:embed? config) + unique-dom-id (build-id config ref? sidebar? embed?) edit-input-id (str "edit-heading-" unique-dom-id uuid) heading-id (str "ls-heading-" unique-dom-id uuid) has-child? (boolean diff --git a/web/src/main/frontend/db.cljs b/web/src/main/frontend/db.cljs index c04d478035..35129f3b27 100644 --- a/web/src/main/frontend/db.cljs +++ b/web/src/main/frontend/db.cljs @@ -165,6 +165,7 @@ :db/isComponent true} :heading/meta {} :heading/properties {} + :heading/properties-meta {} :heading/created-at {} :heading/last-modified-at {} :heading/body {} diff --git a/web/src/main/frontend/format/block.cljs b/web/src/main/frontend/format/block.cljs index 563a4e7c9c..ed8254eabf 100644 --- a/web/src/main/frontend/format/block.cljs +++ b/web/src/main/frontend/format/block.cljs @@ -5,6 +5,7 @@ [frontend.format :as format] [frontend.utf8 :as utf8] [medley.core :as medley] + [frontend.config :as config] [datascript.core :as d] [clojure.set :as set])) @@ -67,7 +68,7 @@ (= "Property_Drawer" (first block)))) (defn extract-properties - [[_ properties start-pos end-pos]] + [[_ properties start-pos end-pos :as all]] {:properties (into {} properties) :start-pos start-pos :end-pos end-pos}) @@ -163,7 +164,8 @@ (let [heading (-> (assoc (second block) :body (vec (reverse heading-body)) :timestamps timestamps - :properties (:properties properties)) + :properties (:properties properties) + :properties-meta (dissoc properties :properties)) (assoc-in [:meta :end-pos] last-pos)) heading (with-refs heading) last-pos' (get-in heading [:meta :pos]) @@ -238,6 +240,7 @@ {:heading/meta meta :heading/marker (get heading :heading/marker "nil") :heading/properties (get heading :heading/properties []) + :heading/properties-meta (get heading :heading/properties-meta []) :heading/file file :heading/format format :heading/page page @@ -268,3 +271,9 @@ :pages pages :start-pos start-pos :end-pos (+ start-pos content-length)}))) + +(defn with-levels + [text format {:heading/keys [level pre-heading?]}] + (let [pattern (config/get-heading-pattern format) + prefix (if pre-heading? "" (str (apply str (repeat level pattern)) " "))] + (str prefix (string/triml text)))) diff --git a/web/src/main/frontend/handler.cljs b/web/src/main/frontend/handler.cljs index 1027b976ab..2f05205d75 100644 --- a/web/src/main/frontend/handler.cljs +++ b/web/src/main/frontend/handler.cljs @@ -1042,38 +1042,44 @@ key (string/upper-case (name key)) value (name value)] (when-let [heading (db/pull [:heading/uuid heading-id])] - (let [{:heading/keys [file page content properties meta]} heading - {:keys [properties start-pos end-pos]} properties - new-content (if (and start-pos - end-pos - (> end-pos start-pos)) - (let [encoded (utf8/encode content) - properties (utf8/substring encoded start-pos end-pos) - properties (let [lines (string/split-lines properties) - property-check? #(re-find (re-pattern - (util/format ":%s:" key)) - %)] - (if (some property-check? lines) - (str - (->> (map (fn [line] - (if (property-check? line) - (util/format " :%s: %s" key value) - line)) lines) - (string/join "\n")) - "\n") - (str properties - (util/format "\n :%s: %s\n" key value)))) - prefix (utf8/substring encoded 0 start-pos) - postfix (when (> (:end-pos meta) end-pos) - (utf8/substring encoded end-pos (:end-pos meta)))] - (str prefix properties postfix)) - (let [properties (util/format - "\n :PROPERTIES:\n :%s: %s\n :END:\n" - key value)] - (let [[heading-line & others] (string/split-lines content)] - (str heading-line properties - (string/join "\n" others)))))] - (save-heading-if-changed! heading new-content))))) + (let [{:heading/keys [file page content properties properties-meta meta]} heading + {:keys [start-pos end-pos]} properties-meta + start-pos (- start-pos (:pos meta))] + (cond + (and start-pos end-pos (> end-pos start-pos)) + (let [encoded (utf8/encode content) + properties (utf8/substring encoded start-pos end-pos) + lines (string/split-lines properties) + property-check? #(re-find (re-pattern + (util/format ":%s:" key)) + %) + has-property? (some property-check? lines)] + (when-not (and has-property? + (some #(string/includes? % (str ":" key ": " value)) lines)) ; same key-value, skip it + (let [properties (if has-property? + (str + (->> (map (fn [line] + (if (property-check? line) + (util/format " :%s: %s" key value) + line)) lines) + (string/join "\n")) + "\n") + (str properties + (util/format "\n :%s: %s\n" key value))) + prefix (utf8/substring encoded 0 start-pos) + postfix (when (> (:end-pos meta) end-pos) + (utf8/substring encoded end-pos (:end-pos meta))) + new-content (str prefix properties postfix)] + (save-heading-if-changed! heading new-content)))) + + :else + (let [properties (util/format + "\n :PROPERTIES:\n :%s: %s\n :END:\n" + key value) + [heading-line & others] (string/split-lines content) + new-content (str heading-line properties + (string/join "\n" others))] + (save-heading-if-changed! heading new-content))))))) ;; FIXME: not working for nested parent (defn- unchanged-sibling? diff --git a/web/src/main/frontend/util.cljs b/web/src/main/frontend/util.cljs index d45cfb8ba7..6e79e1d790 100644 --- a/web/src/main/frontend/util.cljs +++ b/web/src/main/frontend/util.cljs @@ -568,11 +568,15 @@ (if (<= (count s) n) s (subs s 0 n))) - -(defonce uuid-pattern #"^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$") +(def uuid-pattern "[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}") +(defonce exactly-uuid-pattern (re-pattern (str "^" uuid-pattern "$"))) (defn uuid-string? [s] - (re-find uuid-pattern s)) + (re-find exactly-uuid-pattern s)) + +(defn extract-uuid + [s] + (re-find (re-pattern uuid-pattern) s)) (defn drop-nth [n coll] (keep-indexed #(if (not= %1 n) %2) coll))