core.cljs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. (ns frontend.modules.file.core
  2. (:require [clojure.string :as string]
  3. [frontend.config :as config]
  4. [frontend.date :as date]
  5. [frontend.db :as db]
  6. [frontend.db.utils :as db-utils]
  7. [frontend.state :as state]
  8. [frontend.util :as util]
  9. [frontend.util.property :as property]
  10. [frontend.handler.file :as file-handler]))
  11. (defn- indented-block-content
  12. [content spaces-tabs]
  13. (let [lines (string/split-lines content)]
  14. (string/join (str "\n" spaces-tabs) lines)))
  15. (defn- content-with-collapsed-state
  16. "Only accept nake content (without any indentation)"
  17. [format content collapsed? properties]
  18. (cond
  19. collapsed?
  20. (property/insert-property format content :collapsed true)
  21. (and (:collapsed properties) (false? collapsed?))
  22. (property/remove-property format :collapsed content)
  23. :else
  24. content))
  25. (defn transform-content
  26. [{:block/keys [collapsed? format pre-block? unordered content heading-level left page parent properties]} level {:keys [heading-to-list?]}]
  27. (let [content (or content "")
  28. pre-block? (or pre-block?
  29. (and (= page parent left) ; first block
  30. (= :markdown format)
  31. (string/includes? (first (string/split-lines content)) ":: ")))
  32. markdown? (= format :markdown)
  33. content (cond
  34. pre-block?
  35. (let [content (string/trim content)]
  36. (str content "\n"))
  37. :else
  38. (let [markdown-top-heading? (and markdown?
  39. (= parent page)
  40. (not unordered)
  41. heading-level)
  42. [prefix spaces-tabs]
  43. (cond
  44. (= format :org)
  45. [(->>
  46. (repeat level "*")
  47. (apply str)) ""]
  48. markdown-top-heading?
  49. ["" ""]
  50. :else
  51. (let [level (if (and heading-to-list? heading-level)
  52. (if (> heading-level 1)
  53. (dec heading-level)
  54. heading-level)
  55. level)
  56. spaces-tabs (->>
  57. (repeat (dec level) (state/get-export-bullet-indentation))
  58. (apply str))]
  59. [(str spaces-tabs "-") (str spaces-tabs " ")]))
  60. content (if heading-to-list?
  61. (-> (string/replace content #"^\s?#+\s+" "")
  62. (string/replace #"^\s?#+\s?$" ""))
  63. content)
  64. content (content-with-collapsed-state format content collapsed? properties)
  65. new-content (indented-block-content (string/trim content) spaces-tabs)
  66. sep (if (or markdown-top-heading?
  67. (string/blank? new-content))
  68. ""
  69. " ")]
  70. (str prefix sep new-content)))]
  71. content))
  72. (defn- tree->file-content-aux
  73. [tree {:keys [init-level] :as opts}]
  74. (let [block-contents (transient [])]
  75. (loop [[f & r] tree level init-level]
  76. (if (nil? f)
  77. (->> block-contents persistent! flatten (remove nil?))
  78. (let [page? (nil? (:block/page f))
  79. content (if page? nil (transform-content f level opts))
  80. new-content
  81. (if-let [children (seq (:block/children f))]
  82. (cons content (tree->file-content-aux children {:init-level (inc level)}))
  83. [content])]
  84. (conj! block-contents new-content)
  85. (recur r level))))))
  86. (defn tree->file-content
  87. [tree opts]
  88. (->> (tree->file-content-aux tree opts) (string/join "\n")))
  89. (def init-level 1)
  90. (defn- transact-file-tx-if-not-exists!
  91. [page ok-handler]
  92. (when-let [repo (state/get-current-repo)]
  93. (when (:block/name page)
  94. (let [format (name (get page :block/format
  95. (state/get-preferred-format)))
  96. title (string/capitalize (:block/name page))
  97. whiteboard-page? (:block/whiteboard? page)
  98. format (if whiteboard-page? "edn" format)
  99. journal-page? (date/valid-journal-title? title)
  100. filename (if journal-page?
  101. (date/date->file-name journal-page?)
  102. (-> (or (:block/original-name page) (:block/name page))
  103. (util/file-name-sanity)))
  104. sub-dir (cond
  105. journal-page? (config/get-journals-directory)
  106. whiteboard-page? (config/get-whiteboards-directory)
  107. :else (config/get-pages-directory))
  108. ext (if (= format "markdown") "md" format)
  109. file-path (config/get-page-file-path repo sub-dir filename ext)
  110. file {:file/path file-path}
  111. tx [{:file/path file-path}
  112. {:block/name (:block/name page)
  113. :block/file file}]]
  114. (db/transact! tx)
  115. (when ok-handler (ok-handler))))))
  116. (defn- remove-transit-ids [block] (dissoc block :db/id :block/file))
  117. (defn save-tree-aux!
  118. [page-block tree]
  119. (let [page-block (db/pull '[* {:block/file [:file/path]}] (:db/id page-block))
  120. file-path (get-in page-block [:block/file :file/path])
  121. whiteboard? (:block/whiteboard? page-block)
  122. new-content (if whiteboard?
  123. (util/pp-str {:blocks (map remove-transit-ids tree)
  124. :pages (list (remove-transit-ids page-block))})
  125. (tree->file-content tree {:init-level init-level}))
  126. _ (assert (string? file-path) "File path should satisfy string?")
  127. ;; FIXME: name conflicts between multiple graphs
  128. files [[file-path new-content]]
  129. repo (state/get-current-repo)]
  130. (file-handler/alter-files-handler! repo files {} {})))
  131. (defn save-tree!
  132. [page-block tree]
  133. {:pre [(map? page-block)]}
  134. (let [ok-handler #(save-tree-aux! page-block tree)
  135. file (or (:block/file page-block)
  136. (when-let [page (:db/id (:block/page page-block))]
  137. (:block/file (db-utils/entity page))))]
  138. (if file
  139. (ok-handler)
  140. (transact-file-tx-if-not-exists! page-block ok-handler))))