block.cljs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. (ns frontend.format.block
  2. "Block code needed by app but not graph-parser"
  3. (:require [clojure.string :as string]
  4. [logseq.graph-parser.block :as gp-block]
  5. [frontend.config :as config]
  6. [frontend.db :as db]
  7. [frontend.format :as format]
  8. [frontend.state :as state]
  9. [logseq.graph-parser.property :as gp-property]
  10. [logseq.graph-parser.mldoc :as gp-mldoc]))
  11. (defn extract-blocks
  12. "Wrapper around logseq.graph-parser.block/extract-blocks that adds in system state"
  13. [blocks content with-id? format]
  14. (gp-block/extract-blocks blocks content with-id? format
  15. {:user-config (state/get-config)
  16. :block-pattern (config/get-block-pattern format)
  17. :supported-formats (config/supported-formats)
  18. :db (db/get-db (state/get-current-repo))
  19. :date-formatter (state/get-date-formatter)}))
  20. (defn page-name->map
  21. "Wrapper around logseq.graph-parser.block/page-name->map that adds in db"
  22. ([original-page-name with-id?]
  23. (page-name->map original-page-name with-id? true))
  24. ([original-page-name with-id? with-timestamp?]
  25. (gp-block/page-name->map original-page-name with-id? (db/get-db (state/get-current-repo)) with-timestamp? (state/get-date-formatter))))
  26. (defn with-parent-and-left
  27. [page-id blocks]
  28. (loop [blocks (map (fn [block] (assoc block :block/level-spaces (:block/level block))) blocks)
  29. parents [{:page/id page-id ; db id or a map {:block/name "xxx"}
  30. :block/level 0
  31. :block/level-spaces 0}]
  32. result []]
  33. (if (empty? blocks)
  34. (map #(dissoc % :block/level-spaces) result)
  35. (let [[block & others] blocks
  36. level-spaces (:block/level-spaces block)
  37. {:block/keys [uuid level parent] :as last-parent} (last parents)
  38. parent-spaces (:block/level-spaces last-parent)
  39. [blocks parents result]
  40. (cond
  41. (= level-spaces parent-spaces) ; sibling
  42. (let [block (assoc block
  43. :block/parent parent
  44. :block/left [:block/uuid uuid]
  45. :block/level level)
  46. parents' (conj (vec (butlast parents)) block)
  47. result' (conj result block)]
  48. [others parents' result'])
  49. (> level-spaces parent-spaces) ; child
  50. (let [parent (if uuid [:block/uuid uuid] (:page/id last-parent))
  51. block (cond->
  52. (assoc block
  53. :block/parent parent
  54. :block/left parent)
  55. ;; fix block levels with wrong order
  56. ;; For example:
  57. ;; - a
  58. ;; - b
  59. ;; What if the input indentation is two spaces instead of 4 spaces
  60. (>= (- level-spaces parent-spaces) 1)
  61. (assoc :block/level (inc level)))
  62. parents' (conj parents block)
  63. result' (conj result block)]
  64. [others parents' result'])
  65. (< level-spaces parent-spaces)
  66. (cond
  67. (some #(= (:block/level-spaces %) (:block/level-spaces block)) parents) ; outdent
  68. (let [parents' (vec (filter (fn [p] (<= (:block/level-spaces p) level-spaces)) parents))
  69. left (last parents')
  70. blocks (cons (assoc (first blocks)
  71. :block/level (dec level)
  72. :block/left [:block/uuid (:block/uuid left)])
  73. (rest blocks))]
  74. [blocks parents' result])
  75. :else
  76. (let [[f r] (split-with (fn [p] (<= (:block/level-spaces p) level-spaces)) parents)
  77. left (first r)
  78. parent-id (if-let [block-id (:block/uuid (last f))]
  79. [:block/uuid block-id]
  80. page-id)
  81. block (cond->
  82. (assoc block
  83. :block/parent parent-id
  84. :block/left [:block/uuid (:block/uuid left)]
  85. :block/level (:block/level left)
  86. :block/level-spaces (:block/level-spaces left)))
  87. parents' (->> (concat f [block]) vec)
  88. result' (conj result block)]
  89. [others parents' result'])))]
  90. (recur blocks parents result)))))
  91. (defn parse-block
  92. ([block]
  93. (parse-block block nil))
  94. ([{:block/keys [uuid content page format] :as block} {:keys [with-id?]
  95. :or {with-id? true}}]
  96. (when-not (string/blank? content)
  97. (let [block (dissoc block :block/pre-block?)
  98. ast (format/to-edn content format nil)
  99. blocks (extract-blocks ast content with-id? format)
  100. new-block (first blocks)
  101. parent-refs (->> (db/get-block-parent (state/get-current-repo) uuid)
  102. :block/path-refs
  103. (map :db/id))
  104. {:block/keys [refs]} new-block
  105. ref-pages (filter :block/name refs)
  106. path-ref-pages (->> (concat ref-pages parent-refs [(:db/id page)])
  107. (remove nil?))
  108. block (cond->
  109. (merge
  110. block
  111. new-block
  112. {:block/path-refs path-ref-pages})
  113. (> (count blocks) 1)
  114. (assoc :block/warning :multiple-blocks))
  115. block (dissoc block :block/title :block/body :block/level)]
  116. (if uuid (assoc block :block/uuid uuid) block)))))
  117. (defn parse-title-and-body
  118. ([block]
  119. (when (map? block)
  120. (merge block
  121. (parse-title-and-body (:block/uuid block)
  122. (:block/format block)
  123. (:block/pre-block? block)
  124. (:block/content block)))))
  125. ([block-uuid format pre-block? content]
  126. (when-not (string/blank? content)
  127. (let [content (if pre-block? content
  128. (str (config/get-block-pattern format) " " (string/triml content)))]
  129. (if-let [result (state/get-block-ast block-uuid content)]
  130. result
  131. (let [ast (->> (format/to-edn content format (gp-mldoc/default-config format))
  132. (map first))
  133. title (when (gp-block/heading-block? (first ast))
  134. (:title (second (first ast))))
  135. body (vec (if title (rest ast) ast))
  136. body (drop-while gp-property/properties-ast? body)
  137. result (cond->
  138. (if (seq body) {:block/body body} {})
  139. title
  140. (assoc :block/title title))]
  141. (state/add-block-ast-cache! block-uuid content result)
  142. result))))))
  143. (defn macro-subs
  144. [macro-content arguments]
  145. (loop [s macro-content
  146. args arguments
  147. n 1]
  148. (if (seq args)
  149. (recur
  150. (string/replace s (str "$" n) (first args))
  151. (rest args)
  152. (inc n))
  153. s)))
  154. (defn break-line-paragraph?
  155. [[typ break-lines]]
  156. (and (= typ "Paragraph")
  157. (every? #(= % ["Break_Line"]) break-lines)))
  158. (defn trim-paragraph-special-break-lines
  159. [ast]
  160. (let [[typ paras] ast]
  161. (if (= typ "Paragraph")
  162. (let [indexed-paras (map-indexed vector paras)]
  163. [typ (->> (filter
  164. #(let [[index value] %]
  165. (not (and (> index 0)
  166. (= value ["Break_Line"])
  167. (contains? #{"Timestamp" "Macro"}
  168. (first (nth paras (dec index)))))))
  169. indexed-paras)
  170. (map #(last %)))])
  171. ast)))
  172. (defn trim-break-lines!
  173. [ast]
  174. (drop-while break-line-paragraph?
  175. (map trim-paragraph-special-break-lines ast)))