editor.cljs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. (ns frontend.handler.file-based.editor
  2. "File-based graph implementation"
  3. (:require [clojure.string :as string]
  4. [frontend.config :as config]
  5. [frontend.commands :as commands]
  6. [frontend.format.block :as block]
  7. [frontend.db :as db]
  8. [frontend.format.mldoc :as mldoc]
  9. [frontend.state :as state]
  10. [frontend.modules.outliner.op :as outliner-op]
  11. [frontend.modules.outliner.ui :as ui-outliner-tx]
  12. [frontend.util :as util]
  13. [frontend.util.clock :as clock]
  14. [frontend.util.drawer :as drawer]
  15. [frontend.handler.file-based.status :as status]
  16. [frontend.handler.property.file :as property-file]
  17. [frontend.handler.file-based.property :as file-property-handler]
  18. [frontend.handler.file-based.property.util :as property-util]
  19. [logseq.db.frontend.schema :as db-schema]
  20. [logseq.common.util.block-ref :as block-ref]))
  21. (defn- remove-non-existed-refs!
  22. [refs]
  23. (remove (fn [x] (or
  24. (and (vector? x)
  25. (= :block/uuid (first x))
  26. (nil? (db/entity x)))
  27. (nil? x))) refs))
  28. (defn- with-marker-time
  29. [content block format new-marker old-marker]
  30. (if (and (state/enable-timetracking?) new-marker)
  31. (try
  32. (let [logbook-exists? (and (:block/body block) (drawer/get-logbook (:block/body block)))
  33. new-marker (string/trim (string/lower-case (name new-marker)))
  34. old-marker (when old-marker (string/trim (string/lower-case (name old-marker))))
  35. new-content (cond
  36. (or (and (nil? old-marker) (or (= new-marker "doing")
  37. (= new-marker "now")))
  38. (and (= old-marker "todo") (= new-marker "doing"))
  39. (and (= old-marker "later") (= new-marker "now"))
  40. (and (= old-marker new-marker "now") (not logbook-exists?))
  41. (and (= old-marker new-marker "doing") (not logbook-exists?)))
  42. (clock/clock-in format content)
  43. (or
  44. (and (= old-marker "doing") (= new-marker "todo"))
  45. (and (= old-marker "now") (= new-marker "later"))
  46. (and (contains? #{"now" "doing"} old-marker)
  47. (= new-marker "done")))
  48. (clock/clock-out format content)
  49. :else
  50. content)]
  51. new-content)
  52. (catch :default _e
  53. content))
  54. content))
  55. (defn- with-timetracking
  56. [block value]
  57. (if (and (state/enable-timetracking?)
  58. (not= (:block/content block) value))
  59. (let [format (:block/format block)
  60. new-marker (last (util/safe-re-find (status/marker-pattern format) (or value "")))
  61. new-value (with-marker-time value block format
  62. new-marker
  63. (:block/marker block))]
  64. new-value)
  65. value))
  66. (defn wrap-parse-block
  67. [{:block/keys [content format left page uuid level pre-block?] :as block
  68. :or {format :markdown}}]
  69. (let [repo (state/get-current-repo)
  70. block (or (and (:db/id block) (db/pull (:db/id block))) block)
  71. block (merge block
  72. (block/parse-title-and-body uuid format pre-block? (:block/content block)))
  73. properties (:block/properties block)
  74. properties (if (and (= format :markdown)
  75. (number? (:heading properties)))
  76. (dissoc properties :heading)
  77. properties)
  78. real-content (:block/content block)
  79. content (if (and (seq properties) real-content (not= real-content content))
  80. (property-file/with-built-in-properties-when-file-based repo properties content format)
  81. content)
  82. content (drawer/with-logbook block content)
  83. content (with-timetracking block content)
  84. first-block? (= left page)
  85. ast (mldoc/->edn (string/trim content) format)
  86. first-elem-type (first (ffirst ast))
  87. first-elem-meta (second (ffirst ast))
  88. properties? (contains? #{"Property_Drawer" "Properties"} first-elem-type)
  89. markdown-heading? (and (= format :markdown)
  90. (= "Heading" first-elem-type)
  91. (nil? (:size first-elem-meta)))
  92. block-with-title? (mldoc/block-with-title? first-elem-type)
  93. content (string/triml content)
  94. content (string/replace content (block-ref/->block-ref uuid) "")
  95. [content content'] (cond
  96. (and first-block? properties?)
  97. [content content]
  98. markdown-heading?
  99. [content content]
  100. :else
  101. (let [content' (str (config/get-block-pattern format) (if block-with-title? " " "\n") content)]
  102. [content content']))
  103. block (assoc block
  104. :block/content content'
  105. :block/format format)
  106. block (apply dissoc block (remove #{:block/pre-block?} db-schema/retract-attributes))
  107. block (block/parse-block block)
  108. block (if (and first-block? (:block/pre-block? block))
  109. block
  110. (dissoc block :block/pre-block?))
  111. block (update block :block/refs remove-non-existed-refs!)
  112. block (if (and left (not= (:block/left block) left)) (assoc block :block/left left) block)
  113. new-properties (merge
  114. (select-keys properties (file-property-handler/hidden-properties))
  115. (:block/properties block))]
  116. (-> block
  117. (dissoc :block.temp/top?
  118. :block.temp/bottom?)
  119. (assoc :block/content content
  120. :block/properties new-properties)
  121. (merge (if level {:block/level level} {})))))
  122. (defn- set-block-property-aux!
  123. [block-or-id key value]
  124. (when-let [block (cond (string? block-or-id) (db/entity [:block/uuid (uuid block-or-id)])
  125. (uuid? block-or-id) (db/entity [:block/uuid block-or-id])
  126. :else block-or-id)]
  127. (let [format (:block/format block)
  128. content (:block/content block)
  129. properties (:block/properties block)
  130. properties (if (nil? value)
  131. (dissoc properties key)
  132. (assoc properties key value))
  133. content (if (nil? value)
  134. (property-util/remove-property format key content)
  135. (property-util/insert-property format content key value))
  136. content (property-util/remove-empty-properties content)]
  137. {:block/uuid (:block/uuid block)
  138. :block/properties properties
  139. :block/properties-order (or (keys properties) [])
  140. :block/content content})))
  141. (defn- set-heading-aux!
  142. [block-id heading]
  143. (let [block (db/pull [:block/uuid block-id])
  144. format (:block/format block)
  145. old-heading (get-in block [:block/properties :heading])]
  146. (if (= format :markdown)
  147. (cond
  148. ;; nothing changed
  149. (or (and (nil? old-heading) (nil? heading))
  150. (and (true? old-heading) (true? heading))
  151. (= old-heading heading))
  152. nil
  153. (or (and (nil? old-heading) (true? heading))
  154. (and (true? old-heading) (nil? heading)))
  155. (set-block-property-aux! block :heading heading)
  156. (and (or (nil? heading) (true? heading))
  157. (number? old-heading))
  158. (let [block' (set-block-property-aux! block :heading heading)
  159. content (commands/clear-markdown-heading (:block/content block'))]
  160. (merge block' {:block/content content}))
  161. (and (or (nil? old-heading) (true? old-heading))
  162. (number? heading))
  163. (let [block' (set-block-property-aux! block :heading nil)
  164. properties (assoc (:block/properties block) :heading heading)
  165. content (commands/set-markdown-heading (:block/content block') heading)]
  166. (merge block' {:block/content content :block/properties properties}))
  167. ;; heading-num1 -> heading-num2
  168. :else
  169. (let [properties (assoc (:block/properties block) :heading heading)
  170. content (-> block
  171. :block/content
  172. commands/clear-markdown-heading
  173. (commands/set-markdown-heading heading))]
  174. {:block/uuid (:block/uuid block)
  175. :block/properties properties
  176. :block/content content}))
  177. (set-block-property-aux! block :heading heading))))
  178. (defn batch-set-heading! [block-ids heading]
  179. (ui-outliner-tx/transact!
  180. {:outliner-op :save-block}
  181. (doseq [block-id block-ids]
  182. (when-let [block (set-heading-aux! block-id heading)]
  183. (outliner-op/save-block! block)))))
  184. (defn set-blocks-id!
  185. "Persist block uuid to file if the uuid is valid, and it's not persisted in file.
  186. Accepts a list of uuids."
  187. [block-ids]
  188. (let [block-ids (remove nil? block-ids)
  189. col (map (fn [block-id]
  190. (when-let [block (db/entity [:block/uuid block-id])]
  191. (when-not (:block/pre-block? block)
  192. [block-id :id (str block-id)])))
  193. block-ids)
  194. col (remove nil? col)]
  195. (file-property-handler/batch-set-block-property-aux! col)))