editor.cljs 9.6 KB

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