block.cljs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. (ns frontend.format.block
  2. (:require [clojure.string :as string]
  3. [clojure.walk :as walk]
  4. [cljs.core.match :as match]
  5. [frontend.config :as config]
  6. [frontend.date :as date]
  7. [frontend.db :as db]
  8. [frontend.format :as format]
  9. [frontend.state :as state]
  10. [frontend.text :as text]
  11. [frontend.utf8 :as utf8]
  12. [frontend.util :as util]
  13. [frontend.util.property :as property]
  14. [lambdaisland.glogi :as log]
  15. [medley.core :as medley]
  16. [frontend.format.mldoc :as mldoc]))
  17. (defn heading-block?
  18. [block]
  19. (and
  20. (vector? block)
  21. (= "Heading" (first block))))
  22. (defn get-tag
  23. [block]
  24. (when-let [tag-value (and (vector? block)
  25. (= "Tag" (first block))
  26. (second block))]
  27. (->
  28. (map (fn [e]
  29. (match/match e
  30. ["Plain" s]
  31. s
  32. ["Link" t]
  33. (let [{full_text :full_text} t]
  34. full_text)
  35. ["Nested_link" t]
  36. (let [ {content :content} t]
  37. content)
  38. :else
  39. ""
  40. )) tag-value)
  41. (string/join))))
  42. (defn get-page-reference
  43. [block]
  44. (let [page (cond
  45. (and (vector? block) (= "Link" (first block)))
  46. (let [typ (first (:url (second block)))]
  47. ;; {:url ["File" "file:../pages/hello_world.org"], :label [["Plain" "hello world"]], :title nil}
  48. (or
  49. (and
  50. (= typ "Page_ref")
  51. (string? (second (:url (second block))))
  52. (second (:url (second block))))
  53. (and
  54. (= typ "Search")
  55. (text/page-ref? (second (:url (second block))))
  56. (text/page-ref-un-brackets! (second (:url (second block)))))
  57. (and
  58. (= typ "Search")
  59. (not (contains? #{\# \* \/ \[} (first (second (:url (second block))))))
  60. (let [page (second (:url (second block)))
  61. ext (some-> (util/get-file-ext page) keyword)]
  62. (when (and (not (util/starts-with? page "http:"))
  63. (not (util/starts-with? page "https:"))
  64. (not (util/starts-with? page "file:"))
  65. (or (= ext :excalidraw)
  66. (not (contains? (config/supported-formats) ext))))
  67. page)))
  68. (and
  69. (= typ "Complex")
  70. (= (:protocol (second (:url (second block)))) "file")
  71. (:link (second (:url (second block)))))
  72. (and
  73. (= typ "File")
  74. (second (first (:label (second block)))))))
  75. (and (vector? block) (= "Nested_link" (first block)))
  76. (let [content (:content (last block))]
  77. (subs content 2 (- (count content) 2)))
  78. (and (vector? block)
  79. (= "Macro" (first block)))
  80. (let [{:keys [name arguments]} (second block)
  81. argument (string/join ", " arguments)]
  82. (when (= name "embed")
  83. (text/page-ref-un-brackets! argument)))
  84. (and (vector? block)
  85. (= "Tag" (first block)))
  86. (let [text (get-tag block)]
  87. (text/page-ref-un-brackets! text))
  88. :else
  89. nil)]
  90. (text/block-ref-un-brackets! page)))
  91. (defn get-block-reference
  92. [block]
  93. (when-let [block-id (cond
  94. (and (vector? block)
  95. (= "Block_reference" (first block)))
  96. (last block)
  97. (and (vector? block)
  98. (= "Link" (first block))
  99. (map? (second block))
  100. (= "Block_ref" (first (:url (second block)))))
  101. (second (:url (second block)))
  102. (and (vector? block)
  103. (= "Macro" (first block)))
  104. (let [{:keys [name arguments]} (second block)]
  105. (when (and (= name "embed")
  106. (string? (first arguments))
  107. (string/starts-with? (first arguments) "((")
  108. (string/ends-with? (first arguments) "))"))
  109. (subs (first arguments) 2 (- (count (first arguments)) 2))))
  110. (and (vector? block)
  111. (= "Link" (first block))
  112. (map? (second block)))
  113. (if (= "id" (:protocol (second (:url (second block)))))
  114. (:link (second (:url (second block))))
  115. (let [id (second (:url (second block)))]
  116. (text/block-ref-un-brackets! id)))
  117. :else
  118. nil)]
  119. (when (and block-id
  120. (util/uuid-string? block-id))
  121. block-id)))
  122. (defn paragraph-block?
  123. [block]
  124. (and
  125. (vector? block)
  126. (= "Paragraph" (first block))))
  127. (defn timestamp-block?
  128. [block]
  129. (and
  130. (vector? block)
  131. (= "Timestamp" (first block))))
  132. ;; TODO: we should move this to mldoc
  133. (defn extract-properties
  134. [properties]
  135. (when (seq properties)
  136. (let [properties (seq properties)
  137. page-refs (->>
  138. properties
  139. (remove (fn [[k _]]
  140. (contains? #{:background-color :background_color} (keyword k))))
  141. (map last)
  142. (map (fn [v]
  143. (when (string? v)
  144. (let [v (string/trim v)
  145. result (text/split-page-refs-without-brackets v {:un-brackets? false})]
  146. (if (coll? result)
  147. (map text/page-ref-un-brackets! result)
  148. [])))))
  149. (apply concat)
  150. (remove string/blank?))
  151. properties (->> properties
  152. (map (fn [[k v]]
  153. (let [k (-> (string/lower-case (name k))
  154. (string/replace " " "-")
  155. (string/replace "_" "-"))
  156. k (if (contains? #{"custom_id" "custom-id"} k)
  157. "id"
  158. k)
  159. v (if (coll? v)
  160. (remove string/blank? v)
  161. (if (string/blank? v)
  162. nil
  163. (text/parse-property k v)))
  164. k (keyword k)
  165. v (if (and
  166. (string? v)
  167. (contains? #{:alias :aliases :tags} k))
  168. (set [v])
  169. v)
  170. v (if (coll? v) (set v) v)]
  171. [k v])))
  172. (remove #(nil? (second %))))]
  173. {:properties (into {} properties)
  174. :properties-order (map first properties)
  175. :page-refs page-refs})))
  176. (defn- paragraph-timestamp-block?
  177. [block]
  178. (and (paragraph-block? block)
  179. (or (timestamp-block? (first (second block)))
  180. (timestamp-block? (second (second block))))))
  181. (defn extract-timestamps
  182. [block]
  183. (some->>
  184. (second block)
  185. (filter timestamp-block?)
  186. (map last)
  187. (into {})))
  188. ;; {"Deadline" {:date {:year 2020, :month 10, :day 20}, :wday "Tue", :time {:hour 8, :min 0}, :repetition [["DoublePlus"] ["Day"] 1], :active true}}
  189. (defn timestamps->scheduled-and-deadline
  190. [timestamps]
  191. (let [timestamps (medley/map-keys (comp keyword string/lower-case) timestamps)
  192. m (some->> (select-keys timestamps [:scheduled :deadline])
  193. (map (fn [[k v]]
  194. (let [{:keys [date repetition]} v
  195. {:keys [year month day]} date
  196. day (js/parseInt (str year (util/zero-pad month) (util/zero-pad day)))]
  197. (cond->
  198. (case k
  199. :scheduled
  200. {:scheduled day}
  201. :deadline
  202. {:deadline day})
  203. repetition
  204. (assoc :repeated? true))))))]
  205. (apply merge m)))
  206. (defn convert-page-if-journal
  207. "Convert journal file name to user' custom date format"
  208. [original-page-name]
  209. (when original-page-name
  210. (let [page-name (util/page-name-sanity-lc original-page-name)
  211. day (date/journal-title->int page-name)]
  212. (if day
  213. (let [original-page-name (date/int->journal-title day)]
  214. [original-page-name (util/page-name-sanity-lc original-page-name) day])
  215. [original-page-name page-name day]))))
  216. (defn page-name->map
  217. [original-page-name with-id?]
  218. (cond
  219. (and original-page-name (string? original-page-name))
  220. (let [original-page-name (util/remove-boundary-slashes original-page-name)
  221. [original-page-name page-name journal-day] (convert-page-if-journal original-page-name)
  222. namespace? (and (not (boolean (text/get-nested-page-name original-page-name)))
  223. (text/namespace-page? original-page-name))
  224. m (merge
  225. {:block/name page-name
  226. :block/original-name original-page-name}
  227. (when with-id?
  228. (if (db/entity [:block/name page-name])
  229. {}
  230. {:block/uuid (db/new-block-id)}))
  231. (when namespace?
  232. (let [namespace (first (util/split-last "/" original-page-name))]
  233. (when-not (string/blank? namespace)
  234. {:block/namespace {:block/name (util/page-name-sanity-lc namespace)}}))))]
  235. (if journal-day
  236. (merge m
  237. {:block/journal? true
  238. :block/journal-day journal-day})
  239. (assoc m :block/journal? false)))
  240. (and (map? original-page-name) (:block/uuid original-page-name))
  241. original-page-name
  242. (and (map? original-page-name) with-id?)
  243. (assoc original-page-name :block/uuid (db/new-block-id))
  244. :else
  245. nil))
  246. (defn with-page-refs
  247. [{:keys [title body tags refs marker priority] :as block} with-id?]
  248. (let [refs (->> (concat tags refs [marker priority])
  249. (remove string/blank?)
  250. (distinct))
  251. refs (atom refs)]
  252. (walk/prewalk
  253. (fn [form]
  254. ;; skip custom queries
  255. (when-not (and (vector? form)
  256. (= (first form) "Custom")
  257. (= (second form) "query"))
  258. (when-let [page (get-page-reference form)]
  259. (swap! refs conj page))
  260. (when-let [tag (get-tag form)]
  261. (let [tag (text/page-ref-un-brackets! tag)]
  262. (when (util/tag-valid? tag)
  263. (swap! refs conj tag))))
  264. form))
  265. (concat title body))
  266. (let [refs (remove string/blank? @refs)
  267. children-pages (->> (mapcat (fn [p]
  268. (let [p (if (map? p)
  269. (:block/original-name p)
  270. p)]
  271. (when (string? p)
  272. (let [p (or (text/get-nested-page-name p) p)]
  273. (when (text/namespace-page? p)
  274. (util/split-namespace-pages p))))))
  275. refs)
  276. (remove string/blank?)
  277. (distinct))
  278. refs (->> (distinct (concat refs children-pages))
  279. (remove nil?))
  280. refs (map (fn [ref] (page-name->map ref with-id?)) refs)]
  281. (assoc block :refs refs))))
  282. (defn with-block-refs
  283. [{:keys [title body] :as block}]
  284. (let [ref-blocks (atom nil)]
  285. (walk/postwalk
  286. (fn [form]
  287. (when-let [block (get-block-reference form)]
  288. (swap! ref-blocks conj block))
  289. form)
  290. (concat title body))
  291. (let [ref-blocks (->> @ref-blocks
  292. (filter util/uuid-string?))
  293. ref-blocks (map
  294. (fn [id]
  295. [:block/uuid (medley/uuid id)])
  296. ref-blocks)
  297. refs (distinct (concat (:refs block) ref-blocks))]
  298. (assoc block :refs refs))))
  299. (defn block-keywordize
  300. [block]
  301. (medley/map-keys
  302. (fn [k]
  303. (if (namespace k)
  304. k
  305. (keyword "block" k)))
  306. block))
  307. (defn safe-blocks
  308. [blocks]
  309. (map (fn [block]
  310. (if (map? block)
  311. (block-keywordize (util/remove-nils block))
  312. block))
  313. blocks))
  314. (defn with-path-refs
  315. [blocks]
  316. (loop [blocks blocks
  317. acc []
  318. parents []]
  319. (if (empty? blocks)
  320. acc
  321. (let [block (first blocks)
  322. cur-level (:block/level block)
  323. level-diff (- cur-level
  324. (get (last parents) :block/level 0))
  325. [path-refs parents]
  326. (cond
  327. (zero? level-diff) ; sibling
  328. (let [path-refs (mapcat :block/refs (drop-last parents))
  329. parents (conj (vec (butlast parents)) block)]
  330. [path-refs parents])
  331. (> level-diff 0) ; child
  332. (let [path-refs (mapcat :block/refs parents)]
  333. [path-refs (conj parents block)])
  334. (< level-diff 0) ; new parent
  335. (let [parents (vec (take-while (fn [p] (< (:block/level p) cur-level)) parents))
  336. path-refs (mapcat :block/refs parents)]
  337. [path-refs (conj parents block)]))
  338. path-ref-pages (->> path-refs
  339. (concat (:block/refs block))
  340. (map (fn [ref]
  341. (cond
  342. (map? ref)
  343. (:block/name ref)
  344. :else
  345. ref)))
  346. (remove string/blank?)
  347. (map (fn [ref]
  348. (if (string? ref)
  349. {:block/name (util/page-name-sanity-lc ref)}
  350. ref)))
  351. (remove vector?)
  352. (remove nil?)
  353. (distinct))]
  354. (recur (rest blocks)
  355. (conj acc (assoc block :block/path-refs path-ref-pages))
  356. parents)))))
  357. (defn block-tags->pages
  358. [{:keys [tags] :as block}]
  359. (if (seq tags)
  360. (assoc block :tags (map (fn [tag]
  361. (let [tag (text/page-ref-un-brackets! tag)]
  362. [:block/name (util/page-name-sanity-lc tag)])) tags))
  363. block))
  364. (defn- get-block-content
  365. [utf8-content block format block-content]
  366. (let [meta (:meta block)
  367. content (or block-content
  368. (if-let [end-pos (:end-pos meta)]
  369. (utf8/substring utf8-content
  370. (:start-pos meta)
  371. end-pos)
  372. (utf8/substring utf8-content
  373. (:start-pos meta))))
  374. content (when content
  375. (let [content (text/remove-level-spaces content format)]
  376. (if (or (:pre-block? block)
  377. (= (:format block) :org))
  378. content
  379. (text/remove-indentation-spaces content (inc (:level block)) false))))]
  380. (if (= format :org)
  381. content
  382. (property/->new-properties content))))
  383. (defn get-custom-id-or-new-id
  384. [properties]
  385. (or (when-let [custom-id (or (get-in properties [:properties :custom-id])
  386. (get-in properties [:properties :custom_id])
  387. (get-in properties [:properties :id]))]
  388. (let [custom-id (and (string? custom-id) (string/trim custom-id))]
  389. (when (and custom-id (util/uuid-string? custom-id))
  390. (uuid custom-id))))
  391. (db/new-block-id)))
  392. (defn get-page-refs-from-properties
  393. [properties]
  394. (let [page-refs (mapcat (fn [v] (cond
  395. (coll? v)
  396. v
  397. (text/page-ref? v)
  398. [(text/page-ref-un-brackets! v)]
  399. :else
  400. nil)) (vals properties))
  401. page-refs (remove string/blank? page-refs)]
  402. (map (fn [page] (page-name->map page true)) page-refs)))
  403. (defn with-page-block-refs
  404. [block with-id?]
  405. (some-> block
  406. (with-page-refs with-id?)
  407. with-block-refs
  408. block-tags->pages
  409. (update :refs (fn [col] (remove nil? col)))))
  410. (defn extract-blocks*
  411. [blocks pre-block-properties encoded-content]
  412. (let [first-block (first blocks)
  413. first-block-start-pos (get-in first-block [:block/meta :start-pos])
  414. blocks (if (or (> first-block-start-pos 0)
  415. (empty? blocks))
  416. (cons
  417. (merge
  418. (let [content (utf8/substring encoded-content 0 first-block-start-pos)
  419. id (get-custom-id-or-new-id {:properties @pre-block-properties})
  420. block {:uuid id
  421. :content content
  422. :level 1
  423. :meta {:start-pos 0
  424. :end-pos (or first-block-start-pos
  425. (utf8/length encoded-content))}
  426. :properties @pre-block-properties
  427. :properties-order (keys @pre-block-properties)
  428. :refs (->> (get-page-refs-from-properties @pre-block-properties)
  429. (map :block/original-name))
  430. :pre-block? true
  431. :unordered true}
  432. block (with-page-block-refs block false)]
  433. (block-keywordize block))
  434. (select-keys first-block [:block/format :block/page]))
  435. blocks)
  436. blocks)
  437. blocks (map (fn [block] (dissoc block :block/anchor)) blocks)]
  438. (with-path-refs blocks)))
  439. (defn extract-blocks
  440. [blocks content with-id? format]
  441. (try
  442. (let [encoded-content (utf8/encode content)
  443. last-pos (utf8/length encoded-content)
  444. pre-block-properties (atom nil)
  445. blocks
  446. (loop [headings []
  447. blocks (reverse blocks)
  448. timestamps {}
  449. properties {}
  450. last-pos last-pos
  451. last-level 1000
  452. children []
  453. block-all-content []]
  454. (if (seq blocks)
  455. (let [[block {:keys [start_pos _end_pos] :as block-content}] (first blocks)
  456. block-content (when (string? block-content) block-content)
  457. unordered? (:unordered (second block))
  458. markdown-heading? (and (:size (second block)) (= :markdown format))]
  459. (cond
  460. (paragraph-timestamp-block? block)
  461. (let [timestamps (extract-timestamps block)
  462. timestamps' (merge timestamps timestamps)]
  463. (recur headings (rest blocks) timestamps' properties last-pos last-level children (conj block-all-content block-content)))
  464. (property/properties-ast? block)
  465. (let [properties (extract-properties (second block))]
  466. (recur headings (rest blocks) timestamps properties last-pos last-level children (conj block-all-content block-content)))
  467. (heading-block? block)
  468. (let [id (get-custom-id-or-new-id properties)
  469. ref-pages-in-properties (->> (:page-refs properties)
  470. (remove string/blank?))
  471. block (second block)
  472. block (if markdown-heading?
  473. (assoc block
  474. :type :heading
  475. :level (if unordered? (:level block) 1)
  476. :heading-level (or (:size block) 6))
  477. block)
  478. level (:level block)
  479. [children current-block-children]
  480. (cond
  481. (< level last-level)
  482. (let [current-block-children (set (->> (filter #(< level (second %)) children)
  483. (map first)
  484. (map (fn [id]
  485. [:block/uuid id]))))
  486. others (vec (remove #(< level (second %)) children))]
  487. [(conj others [id level])
  488. current-block-children])
  489. (>= level last-level)
  490. [(conj children [id level])
  491. #{}])
  492. block (cond->
  493. (assoc block
  494. :uuid id
  495. :refs ref-pages-in-properties
  496. :children (or current-block-children [])
  497. :format format)
  498. (seq (:properties properties))
  499. (assoc :properties (:properties properties))
  500. (seq (:properties-order properties))
  501. (assoc :properties-order (:properties-order properties)))
  502. block (if (get-in block [:properties :collapsed])
  503. (assoc block :collapsed? true)
  504. block)
  505. block (-> block
  506. (assoc-in [:meta :start-pos] start_pos)
  507. (assoc-in [:meta :end-pos] last-pos)
  508. ((fn [block]
  509. (assoc block
  510. :content (get-block-content encoded-content block format (and block-content (string/join "\n" (reverse (conj block-all-content block-content)))))))))
  511. block (if (seq timestamps)
  512. (merge block (timestamps->scheduled-and-deadline timestamps))
  513. block)
  514. block (with-page-block-refs block with-id?)
  515. last-pos' (get-in block [:meta :start-pos])
  516. {:keys [created-at updated-at]} (:properties properties)
  517. block (cond-> block
  518. (and created-at (integer? created-at))
  519. (assoc :block/created-at created-at)
  520. (and updated-at (integer? updated-at))
  521. (assoc :block/updated-at updated-at))]
  522. (recur (conj headings block) (rest blocks) {} {} last-pos' (:level block) children []))
  523. :else
  524. (recur headings (rest blocks) timestamps properties last-pos last-level children (conj block-all-content block-content))))
  525. (do
  526. (when (seq properties)
  527. (let [properties (:properties properties)]
  528. (reset! pre-block-properties properties)))
  529. (-> (reverse headings)
  530. safe-blocks))))]
  531. (extract-blocks* blocks pre-block-properties encoded-content))
  532. (catch js/Error e
  533. (js/console.error "extract-blocks-failed")
  534. (log/error :exception e))))
  535. (defn with-parent-and-left
  536. [page-id blocks]
  537. (loop [blocks (map (fn [block] (assoc block :block/level-spaces (:block/level block))) blocks)
  538. parents [{:page/id page-id ; db id or a map {:block/name "xxx"}
  539. :block/level 0
  540. :block/level-spaces 0}]
  541. _sibling nil
  542. result []]
  543. (if (empty? blocks)
  544. (map #(dissoc % :block/level-spaces) result)
  545. (let [[block & others] blocks
  546. level-spaces (:block/level-spaces block)
  547. {:block/keys [uuid level parent] :as last-parent} (last parents)
  548. parent-spaces (:block/level-spaces last-parent)
  549. [blocks parents sibling result]
  550. (cond
  551. (= level-spaces parent-spaces) ; sibling
  552. (let [block (assoc block
  553. :block/parent parent
  554. :block/left [:block/uuid uuid]
  555. :block/level level)
  556. parents' (conj (vec (butlast parents)) block)
  557. result' (conj result block)]
  558. [others parents' block result'])
  559. (> level-spaces parent-spaces) ; child
  560. (let [parent (if uuid [:block/uuid uuid] (:page/id last-parent))
  561. block (cond->
  562. (assoc block
  563. :block/parent parent
  564. :block/left parent)
  565. ;; fix block levels with wrong order
  566. ;; For example:
  567. ;; - a
  568. ;; - b
  569. ;; What if the input indentation is two spaces instead of 4 spaces
  570. (>= (- level-spaces parent-spaces) 1)
  571. (assoc :block/level (inc level)))
  572. parents' (conj parents block)
  573. result' (conj result block)]
  574. [others parents' block result'])
  575. (< level-spaces parent-spaces)
  576. (cond
  577. (some #(= (:block/level-spaces %) (:block/level-spaces block)) parents) ; outdent
  578. (let [parents' (vec (filter (fn [p] (<= (:block/level-spaces p) level-spaces)) parents))
  579. left (last parents')
  580. blocks (cons (assoc (first blocks)
  581. :block/level (dec level)
  582. :block/left [:block/uuid (:block/uuid left)])
  583. (rest blocks))]
  584. [blocks parents' left result])
  585. :else
  586. (let [[f r] (split-with (fn [p] (<= (:block/level-spaces p) level-spaces)) parents)
  587. left (first r)
  588. parent-id (if-let [block-id (:block/uuid (last f))]
  589. [:block/uuid block-id]
  590. page-id)
  591. block (cond->
  592. (assoc block
  593. :block/parent parent-id
  594. :block/left [:block/uuid (:block/uuid left)]
  595. :block/level (:block/level left)
  596. :block/level-spaces (:block/level-spaces left)))
  597. parents' (->> (concat f [block]) vec)
  598. result' (conj result block)]
  599. [others parents' block result'])))]
  600. (recur blocks parents sibling result)))))
  601. (defn parse-block
  602. ([block]
  603. (parse-block block nil))
  604. ([{:block/keys [uuid content page format] :as block} {:keys [with-id?]
  605. :or {with-id? true}}]
  606. (when-not (string/blank? content)
  607. (let [block (dissoc block :block/pre-block?)
  608. ast (format/to-edn content format nil)
  609. blocks (extract-blocks ast content with-id? format)
  610. new-block (first blocks)
  611. parent-refs (->> (db/get-block-parent (state/get-current-repo) uuid)
  612. :block/path-refs
  613. (map :db/id))
  614. {:block/keys [refs]} new-block
  615. ref-pages (filter :block/name refs)
  616. path-ref-pages (->> (concat ref-pages parent-refs [(:db/id page)])
  617. (remove nil?))
  618. block (cond->
  619. (merge
  620. block
  621. new-block
  622. {:block/path-refs path-ref-pages})
  623. (> (count blocks) 1)
  624. (assoc :block/warning :multiple-blocks))
  625. block (dissoc block :block/title :block/body :block/level)]
  626. (if uuid (assoc block :block/uuid uuid) block)))))
  627. (defn parse-title-and-body
  628. ([block]
  629. (when (map? block)
  630. (merge block
  631. (parse-title-and-body (:block/uuid block)
  632. (:block/format block)
  633. (:block/pre-block? block)
  634. (:block/content block)))))
  635. ([block-uuid format pre-block? content]
  636. (when-not (string/blank? content)
  637. (let [content (if pre-block? content
  638. (str (config/get-block-pattern format) " " (string/triml content)))]
  639. (if-let [result (state/get-block-ast block-uuid content)]
  640. result
  641. (let [ast (->> (format/to-edn content format (mldoc/default-config format))
  642. (map first))
  643. title (when (heading-block? (first ast))
  644. (:title (second (first ast))))
  645. body (vec (if title (rest ast) ast))
  646. body (drop-while property/properties-ast? body)
  647. result (cond->
  648. (if (seq body) {:block/body body} {})
  649. title
  650. (assoc :block/title title))]
  651. (state/add-block-ast-cache! block-uuid content result)
  652. result))))))
  653. (defn macro-subs
  654. [macro-content arguments]
  655. (loop [s macro-content
  656. args arguments
  657. n 1]
  658. (if (seq args)
  659. (recur
  660. (string/replace s (str "$" n) (first args))
  661. (rest args)
  662. (inc n))
  663. s)))
  664. (defn break-line-paragraph?
  665. [[typ break-lines]]
  666. (and (= typ "Paragraph")
  667. (every? #(= % ["Break_Line"]) break-lines)))
  668. (defn trim-paragraph-special-break-lines
  669. [ast]
  670. (let [[typ paras] ast]
  671. (if (= typ "Paragraph")
  672. (let [indexed-paras (map-indexed vector paras)]
  673. [typ (->> (filter
  674. #(let [[index value] %]
  675. (not (and (> index 0)
  676. (= value ["Break_Line"])
  677. (contains? #{"Timestamp" "Macro"}
  678. (first (nth paras (dec index)))))))
  679. indexed-paras)
  680. (map #(last %)))])
  681. ast)))
  682. (defn trim-break-lines!
  683. [ast]
  684. (drop-while break-line-paragraph?
  685. (map trim-paragraph-special-break-lines ast)))