reset_file.cljs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. (ns frontend.handler.file-based.reset-file
  2. "Fns for resetting a db file with parsed file content"
  3. (:require [frontend.config :as config]
  4. [frontend.state :as state]
  5. [frontend.db :as db]
  6. [frontend.db.file-based.model :as file-model]
  7. [logseq.graph-parser :as graph-parser]
  8. [logseq.common.util :as common-util]
  9. [frontend.fs.diff-merge :as diff-merge]
  10. [frontend.fs :as fs]
  11. [frontend.context.i18n :refer [t]]
  12. [promesa.core :as p]
  13. [clojure.string :as string]
  14. [cljs-bean.core :as bean]
  15. [lambdaisland.glogi :as log]))
  16. (defn- page-exists-in-another-file
  17. "Conflict of files towards same page"
  18. [repo-url page file]
  19. (when-let [page-name (:block/name page)]
  20. (let [current-file (:file/path (file-model/get-page-file repo-url page-name))]
  21. (when (not= file current-file)
  22. current-file))))
  23. (defn- validate-existing-file
  24. "Handle the case when the file is already exists in db
  25. Likely caused by renaming between caps and non-caps, then cause file system
  26. bugs on some OS
  27. e.g. on macOS, it doesn't fire the file change event when renaming between
  28. caps and non-caps"
  29. [repo-url file-page file-path]
  30. (when-let [current-file (page-exists-in-another-file repo-url file-page file-path)]
  31. (when (not= file-path current-file)
  32. (cond
  33. ;; TODO: handle case sensitive file system
  34. (= (common-util/path-normalize (string/lower-case current-file))
  35. (common-util/path-normalize (string/lower-case file-path)))
  36. ;; case renamed
  37. (when-let [file (db/pull [:file/path current-file])]
  38. (p/let [disk-content (fs/read-file "" current-file)]
  39. (fs/backup-db-file! repo-url current-file (:file/content file) disk-content))
  40. (db/transact! repo-url [{:db/id (:db/id file)
  41. :file/path file-path}]))
  42. :else
  43. (let [error (t :file/validate-existing-file-error current-file file-path)]
  44. (state/pub-event! [:notification/show
  45. {:content error
  46. :status :error
  47. :clear? false}]))))))
  48. (defn- validate-and-get-blocks-to-delete
  49. "An implementation for the delete-blocks-fn in graph-parser/parse-file"
  50. [repo-url db file-page file-path retain-uuid-blocks]
  51. (validate-existing-file repo-url file-page file-path)
  52. (graph-parser/get-blocks-to-delete db file-page file-path retain-uuid-blocks))
  53. (defn- diff-merge-uuids-2ways
  54. "Infer new uuids from existing DB data and diff with the new AST
  55. Return a list of uuids for the new blocks"
  56. [format ast content {:keys [page-name] :as options}]
  57. (try
  58. (let [base-diffblocks (diff-merge/db->diff-blocks page-name)
  59. income-diffblocks (diff-merge/ast->diff-blocks ast content format options)
  60. diff-ops (diff-merge/diff base-diffblocks income-diffblocks)
  61. new-uuids (diff-merge/attachUUID diff-ops (map :uuid base-diffblocks))]
  62. (bean/->clj new-uuids))
  63. (catch js/Error e
  64. (log/error :diff-merge/diff-merge-2way-calling-failed e))))
  65. (defn- reset-file!*
  66. "Parse file considering diff-merge with local or remote file
  67. Decide how to treat the parsed file based on the file's triggering event
  68. options -
  69. :fs/reset-event - the event that triggered the file update
  70. :fs/local-file-change - file changed on local disk
  71. :fs/remote-file-change - file changed on remote"
  72. [repo-url file-path content {:fs/keys [event] :as options}]
  73. (when-let [db-conn (db/get-db repo-url false)]
  74. (case event
  75. ;; the file is already in db, so we can use the existing file's blocks
  76. ;; to do the diff-merge
  77. :fs/local-file-change
  78. (graph-parser/parse-file db-conn file-path content (assoc-in options [:extract-options :resolve-uuid-fn] diff-merge-uuids-2ways))
  79. ;; default to parse the file
  80. (graph-parser/parse-file db-conn file-path content options))))
  81. (defn reset-file!
  82. "Main fn for updating a db with the results of a parsed file"
  83. ([repo-url file-path content]
  84. (reset-file! repo-url file-path content {}))
  85. ([repo-url file-path content {:keys [verbose extracted-block-ids _ctime _mtime] :as options}]
  86. (let [options (merge (dissoc options :verbose :extracted-block-ids)
  87. {:delete-blocks-fn (partial validate-and-get-blocks-to-delete repo-url)
  88. ;; Options here should also be present in gp-cli/parse-graph
  89. :extract-options (merge
  90. {:user-config (state/get-config)
  91. :date-formatter (state/get-date-formatter)
  92. :block-pattern (config/get-block-pattern (common-util/get-format file-path))
  93. :filename-format (state/get-filename-format repo-url)}
  94. ;; To avoid skipping the `:or` bounds for keyword destructuring
  95. (when (some? extracted-block-ids) {:extracted-block-ids extracted-block-ids})
  96. (when (some? verbose) {:verbose verbose}))})]
  97. (:tx (reset-file!* repo-url file-path content options)))))