mirror of
https://github.com/logseq/logseq.git
synced 2026-06-01 19:01:22 +00:00
deps: diff-merge
dev: graph parser IoC hook test: use test db for diff-merge tests fix: ci lint dev: refactoring post block-parsing process feat: diff-merge 2 way merge integration fix: key namespace of uuid in fix-duplicated-id fix: duplicated uuid ci
This commit is contained in:
@@ -225,6 +225,7 @@
|
||||
db-utils/seq-flatten)))
|
||||
|
||||
(defn set-file-last-modified-at!
|
||||
"Refresh file timestamps to DB"
|
||||
[repo path last-modified-at]
|
||||
(when (and repo path last-modified-at)
|
||||
(when-let [conn (conn/get-db repo false)]
|
||||
@@ -459,6 +460,34 @@ independent of format as format specific heading characters are stripped"
|
||||
blocks-map (zipmap (map :db/id blocks) blocks)]
|
||||
(keep blocks-map sorted-ids)))
|
||||
|
||||
;; Diverged of get-sorted-page-block-ids
|
||||
(defn get-sorted-page-block-ids-and-levels
|
||||
"page-name: the page name, original name
|
||||
return: a list with elements in:
|
||||
:id - a list of block ids, sorted by :block/left
|
||||
:level - the level of the block, 1 for root, 2 for children of root, etc."
|
||||
[page-name]
|
||||
{:pre [(string? page-name)]}
|
||||
(let [sanitized-page (gp-util/page-name-sanity-lc page-name)
|
||||
page-id (:db/id (db-utils/entity [:block/name sanitized-page]))
|
||||
root (db-utils/entity page-id)]
|
||||
(loop [result []
|
||||
children (sort-by-left (:block/_parent root) root)
|
||||
;; BFS log of walking depth
|
||||
levels (repeat (count children) 1)]
|
||||
(if (seq children)
|
||||
(let [child (first children)
|
||||
cur-level (first levels)
|
||||
next-children (sort-by-left (:block/_parent child) child)]
|
||||
(recur (conj result {:id (:db/id child) :level cur-level})
|
||||
(concat
|
||||
next-children
|
||||
(rest children))
|
||||
(concat
|
||||
(repeat (count next-children) (inc cur-level))
|
||||
(rest levels))))
|
||||
result))))
|
||||
|
||||
(defn has-children?
|
||||
([block-id]
|
||||
(has-children? (conn/get-db) block-id))
|
||||
|
||||
93
src/main/frontend/fs/diff_merge.cljc
Normal file
93
src/main/frontend/fs/diff_merge.cljc
Normal file
@@ -0,0 +1,93 @@
|
||||
(ns frontend.fs.diff-merge
|
||||
;; Disable clj linters since we don't support clj
|
||||
#?(:clj {:clj-kondo/config {:linters {:unresolved-namespace {:level :off}
|
||||
:unresolved-symbol {:level :off}}}})
|
||||
(:require #?(:org.babashka/nbb ["@logseq/diff-merge$default" :refer [Merger Differ visualizeAsHTML attach_uuids]]
|
||||
:default ["@logseq/diff-merge" :refer [Differ Merger visualizeAsHTML attach_uuids]])
|
||||
[logseq.graph-parser.block :as gp-block]
|
||||
[logseq.graph-parser.property :as gp-property]
|
||||
[logseq.graph-parser.utf8 :as utf8]
|
||||
[cljs-bean.core :as bean]
|
||||
[frontend.db.utils :as db-utils]
|
||||
[frontend.db.model :as db-model]))
|
||||
|
||||
;; (defn diff-merge
|
||||
;; "N-ways diff & merge
|
||||
;; Accept: blocks
|
||||
;; https://github.com/logseq/diff-merge/blob/44546f2427f20bd417b898c8ba7b7d10a9254774/lib/mldoc.ts#L17-L22
|
||||
;; https://github.com/logseq/diff-merge/blob/85ca7e9bf7740d3880ed97d535a4f782a963395d/lib/merge.ts#L40"
|
||||
;; [base & branches]
|
||||
;; ()
|
||||
;; (let [merger (Merger.)]
|
||||
;; (.mergeBlocks merger (bean/->js base) (bean/->js branches))))
|
||||
|
||||
(defn diff
|
||||
"2-ways diff
|
||||
Accept: blocks
|
||||
https://github.com/logseq/diff-merge/blob/44546f2427f20bd417b898c8ba7b7d10a9254774/lib/mldoc.ts#L17-L22"
|
||||
[base income]
|
||||
(let [differ (Differ.)]
|
||||
(.diff_logseqMode differ (bean/->js base) (bean/->js income))))
|
||||
|
||||
;; (defonce getHTML visualizeAsHTML)
|
||||
|
||||
(defonce attachUUID attach_uuids)
|
||||
|
||||
(defn db->diff-blocks
|
||||
"db: datascript db
|
||||
page-name: string"
|
||||
[page-name]
|
||||
{:pre (string? page-name)}
|
||||
(let [walked (db-model/get-sorted-page-block-ids-and-levels page-name)
|
||||
blocks (db-utils/pull-many [:block/uuid :block/content :block/level] (map :id walked))
|
||||
levels (map :level walked)
|
||||
blocks (map (fn [block level]
|
||||
{:uuid (str (:block/uuid block)) ;; Force to be string
|
||||
:body (:block/content block)
|
||||
:level level})
|
||||
blocks levels)]
|
||||
blocks))
|
||||
|
||||
;; TODO Junyi: merge back to gp-block/extract-blocks
|
||||
;; From back to first to ensure end_pos is correct
|
||||
(defn ast->diff-blocks
|
||||
"Prepare the blocks for diff-merge
|
||||
blocks: ast of blocks
|
||||
content: corresponding raw content"
|
||||
[blocks content format {:keys [user-config block-pattern]}]
|
||||
{:pre [(string? content) (contains? #{:markdown :org} format)]}
|
||||
(let [encoded-content (utf8/encode content)]
|
||||
(loop [headings []
|
||||
blocks (reverse blocks)
|
||||
properties {}
|
||||
end-pos (.-length encoded-content)]
|
||||
(if (seq blocks)
|
||||
(let [[block pos-meta] (first blocks)
|
||||
;; fix start_pos
|
||||
pos-meta (assoc pos-meta :end_pos end-pos)]
|
||||
(cond
|
||||
(gp-block/heading-block? block)
|
||||
(let [content (gp-block/get-block-content encoded-content block format pos-meta block-pattern)]
|
||||
(recur (conj headings {:body content
|
||||
:level (:level (second block))
|
||||
:uuid (:id properties)})
|
||||
(rest blocks) {} (:start_pos pos-meta))) ;; The current block's start pos is the next block's end pos
|
||||
|
||||
(gp-property/properties-ast? block)
|
||||
(let [new-props (:properties (gp-block/extract-properties (second block) (assoc user-config :format format)))]
|
||||
;; sending the current end pos to next, as it's not finished yet
|
||||
;; supports multiple properties sub-block possible in future
|
||||
(recur headings (rest blocks) (merge properties new-props) (:end_pos pos-meta)))
|
||||
|
||||
:else
|
||||
(recur headings (rest blocks) properties (:end_pos pos-meta))))
|
||||
(if (empty? properties)
|
||||
(reverse headings)
|
||||
(let [[block _] (first blocks)
|
||||
pos-meta {:start_pos 0 :end_pos end-pos}
|
||||
content (gp-block/get-block-content encoded-content block format pos-meta block-pattern)
|
||||
uuid (:id properties)]
|
||||
(cons {:body content
|
||||
:level 1
|
||||
:uuid uuid}
|
||||
(reverse headings))))))))
|
||||
@@ -23,6 +23,7 @@
|
||||
;; all IPC paths must be normalized! (via gp-util/path-normalize)
|
||||
|
||||
(defn- set-missing-block-ids!
|
||||
"For every referred block in the content, fix their block ids in files if missing."
|
||||
[content]
|
||||
(when (string? content)
|
||||
(doseq [block-id (block-ref/get-all-block-ref-ids content)]
|
||||
@@ -43,7 +44,8 @@
|
||||
(p/catch #(js/console.error "❌ Bak Error: " path %))))
|
||||
|
||||
_ (file-handler/alter-file repo path content {:re-render-root? true
|
||||
:from-disk? true})]
|
||||
:from-disk? true
|
||||
:fs/event :fs/local-file-change})]
|
||||
(set-missing-block-ids! content)
|
||||
(db/set-file-last-modified-at! repo path mtime)))
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
[logseq.graph-parser :as graph-parser]
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[logseq.graph-parser.config :as gp-config]
|
||||
[frontend.fs.diff-merge :as diff-merge]
|
||||
[frontend.fs :as fs]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[promesa.core :as p]
|
||||
[clojure.string :as string]
|
||||
[promesa.core :as p]))
|
||||
[cljs-bean.core :as bean]))
|
||||
|
||||
(defn- page-exists-in-another-file
|
||||
"Conflict of files towards same page"
|
||||
@@ -20,12 +22,18 @@
|
||||
current-file))))
|
||||
|
||||
(defn- validate-existing-file
|
||||
"Handle the case when the file is already exists in db
|
||||
Likely caused by renaming between caps and non-caps, then cause file system
|
||||
bugs on some OS
|
||||
e.g. on macOS, it doesn't fire the file change event when renaming between
|
||||
caps and non-caps"
|
||||
[repo-url file-page file-path]
|
||||
(when-let [current-file (page-exists-in-another-file repo-url file-page file-path)]
|
||||
(when (not= file-path current-file)
|
||||
(cond
|
||||
(= (string/lower-case current-file)
|
||||
(string/lower-case file-path))
|
||||
;; TODO: handle case sensitive file system
|
||||
(= (gp-util/path-normalize (string/lower-case current-file))
|
||||
(gp-util/path-normalize (string/lower-case file-path)))
|
||||
;; case renamed
|
||||
(when-let [file (db/pull [:file/path current-file])]
|
||||
(p/let [disk-content (fs/read-file "" current-file)]
|
||||
@@ -41,17 +49,53 @@
|
||||
:clear? false}]))))))
|
||||
|
||||
(defn- validate-and-get-blocks-to-delete
|
||||
"An implementation for the delete-blocks-fn in graph-parser/parse-file"
|
||||
[repo-url db file-page file-path retain-uuid-blocks]
|
||||
(validate-existing-file repo-url file-page file-path)
|
||||
(graph-parser/get-blocks-to-delete db file-page file-path retain-uuid-blocks))
|
||||
|
||||
(defn- diff-merge-uuids
|
||||
"Infer new uuids from existing DB data and diff with the new AST
|
||||
Return a list of uuids for the new blocks"
|
||||
[format ast content {:keys [page-name] :as options}]
|
||||
(let [base-diffblocks (diff-merge/db->diff-blocks page-name)
|
||||
income-diffblocks (diff-merge/ast->diff-blocks ast content format options)
|
||||
diff-ops (diff-merge/diff base-diffblocks income-diffblocks)
|
||||
new-uuids (diff-merge/attachUUID diff-ops (map :uuid base-diffblocks))]
|
||||
(bean/->clj new-uuids)))
|
||||
|
||||
(defn- reset-file!-impl
|
||||
"Parse file considering diff-merge with local or remote file
|
||||
Decide how to treat the parsed file based on the file's triggering event
|
||||
options -
|
||||
:fs/reset-event - the event that triggered the file update
|
||||
:fs/local-file-change - file changed on local disk
|
||||
:fs/remote-file-change - file changed on remote"
|
||||
[repo-url file content {:fs/keys [event] :as options}]
|
||||
(let [db-conn (db/get-db repo-url false)]
|
||||
(case event
|
||||
;; the file is already in db, so we can use the existing file's blocks
|
||||
;; to do the diff-merge
|
||||
:fs/local-file-change
|
||||
(graph-parser/parse-file db-conn file content (assoc-in options [:extract-options :resolve-uuid-fn] diff-merge-uuids))
|
||||
|
||||
;; TODO Junyi: 3 ways to handle remote file change
|
||||
;; The file is on remote, so we should have
|
||||
;; 1. a "common ancestor" file locally
|
||||
;; the worst case is that the file is not in db, so we should use the
|
||||
;; empty file as the common ancestor
|
||||
;; 2. a "remote version" just fetched from remote
|
||||
|
||||
;; default to parse the file
|
||||
(graph-parser/parse-file db-conn file content options))))
|
||||
|
||||
(defn reset-file!
|
||||
"Main fn for updating a db with the results of a parsed file"
|
||||
([repo-url file-path content]
|
||||
(reset-file! repo-url file-path content {}))
|
||||
([repo-url file-path content {:keys [verbose] :as options}]
|
||||
([repo-url file-path content {:keys [verbose extracted-block-ids] :as options}]
|
||||
(let [new? (nil? (db/entity [:file/path file-path]))
|
||||
options (merge (dissoc options :verbose)
|
||||
options (merge (dissoc options :verbose :extracted-block-ids)
|
||||
{:new? new?
|
||||
:delete-blocks-fn (partial validate-and-get-blocks-to-delete repo-url)
|
||||
;; Options here should also be present in gp-cli/parse-graph
|
||||
@@ -60,7 +104,8 @@
|
||||
:date-formatter (state/get-date-formatter)
|
||||
:block-pattern (config/get-block-pattern (gp-util/get-format file-path))
|
||||
:supported-formats (gp-config/supported-formats)
|
||||
:filename-format (state/get-filename-format repo-url)
|
||||
:extracted-block-ids (:extracted-block-ids options)}
|
||||
:filename-format (state/get-filename-format repo-url)}
|
||||
;; To avoid skipping the `:or` bounds for keyword destructuring
|
||||
(when (some? extracted-block-ids) {:extracted-block-ids extracted-block-ids})
|
||||
(when (some? verbose) {:verbose verbose}))})]
|
||||
(:tx (graph-parser/parse-file (db/get-db repo-url false) file-path content options)))))
|
||||
(:tx (reset-file!-impl repo-url file-path content options)))))
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"Write any in-DB file, e.g. repo config, page, whiteboard, etc."
|
||||
[repo path content {:keys [reset? re-render-root? from-disk? skip-compare? new-graph? verbose
|
||||
skip-db-transact? extracted-block-ids]
|
||||
:fs/keys [event]
|
||||
:or {reset? true
|
||||
re-render-root? false
|
||||
from-disk? false
|
||||
@@ -156,7 +157,7 @@
|
||||
(let [opts {:new-graph? new-graph?
|
||||
:from-disk? from-disk?
|
||||
:skip-db-transact? skip-db-transact?
|
||||
:extracted-block-ids extracted-block-ids}
|
||||
:fs/event event}
|
||||
result (if reset?
|
||||
(do
|
||||
(when-not skip-db-transact?
|
||||
@@ -167,6 +168,8 @@
|
||||
opts)))
|
||||
(file-common-handler/reset-file!
|
||||
repo path content (merge opts
|
||||
;; To avoid skipping the `:or` bounds for keyword destructuring
|
||||
(when (some? extracted-block-ids) {:extracted-block-ids extracted-block-ids})
|
||||
(when (some? verbose) {:verbose verbose}))))
|
||||
(db/set-file-content! repo path content opts))]
|
||||
(-> (p/let [_ (when-not from-disk?
|
||||
|
||||
@@ -141,8 +141,9 @@
|
||||
(merge {:new-graph? new-graph?
|
||||
:re-render-root? false
|
||||
:from-disk? true
|
||||
:skip-db-transact? skip-db-transact?
|
||||
:extracted-block-ids extracted-block-ids}
|
||||
:skip-db-transact? skip-db-transact?}
|
||||
;; To avoid skipping the `:or` bounds for keyword destructuring
|
||||
(when (some? extracted-block-ids) {:extracted-block-ids extracted-block-ids})
|
||||
(when (some? verbose) {:verbose verbose}))))
|
||||
(state/set-parsing-state! (fn [m]
|
||||
(update m :finished inc)))
|
||||
|
||||
339
src/test/frontend/fs/diff_merge_test.cljs
Normal file
339
src/test/frontend/fs/diff_merge_test.cljs
Normal file
@@ -0,0 +1,339 @@
|
||||
(ns frontend.fs.diff-merge-test
|
||||
(:require [datascript.core :as d]
|
||||
[cljs.test :refer [deftest are is]]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.graph-parser :as graph-parser]
|
||||
[frontend.fs.diff-merge :as fs-diff]
|
||||
[frontend.handler.common.file :as file-common-handler]
|
||||
[frontend.db.conn :as conn]
|
||||
[logseq.graph-parser.mldoc :as gp-mldoc]
|
||||
[cljs-bean.core :as bean]))
|
||||
|
||||
(defn test-db->diff-blocks
|
||||
"A hijacked version of db->diff-blocks for testing.
|
||||
It overwrites the internal db getter with the test db connection."
|
||||
[conn & args]
|
||||
(with-redefs [conn/get-db (constantly @conn)]
|
||||
(apply fs-diff/db->diff-blocks args)))
|
||||
|
||||
(defn org-text->diffblocks
|
||||
[text]
|
||||
(-> (gp-mldoc/->edn text (gp-mldoc/default-config :org))
|
||||
(fs-diff/ast->diff-blocks text :org {:block-pattern "-"})))
|
||||
|
||||
(deftest org->ast->diff-blocks-test
|
||||
(are [text diff-blocks]
|
||||
(= (org-text->diffblocks text)
|
||||
diff-blocks)
|
||||
":PROPERTIES:
|
||||
:ID: 72289d9a-eb2f-427b-ad97-b605a4b8c59b
|
||||
:END:
|
||||
#+tItLe: Well parsed!"
|
||||
[{:body ":PROPERTIES:\n:ID: 72289d9a-eb2f-427b-ad97-b605a4b8c59b\n:END:\n#+tItLe: Well parsed!"
|
||||
:uuid "72289d9a-eb2f-427b-ad97-b605a4b8c59b"
|
||||
:level 1}]
|
||||
|
||||
"#+title: Howdy"
|
||||
[{:body "#+title: Howdy" :uuid nil :level 1}]
|
||||
|
||||
":PROPERTIES:
|
||||
:fiction: [[aldsjfklsda]]
|
||||
:END:\n#+title: Howdy"
|
||||
[{:body ":PROPERTIES:\n:fiction: [[aldsjfklsda]]\n:END:\n#+title: Howdy"
|
||||
:uuid nil
|
||||
:level 1}]))
|
||||
|
||||
(deftest db<->ast-diff-blocks-test
|
||||
(let [conn (ldb/start-conn)
|
||||
text ":PROPERTIES:
|
||||
:ID: 72289d9a-eb2f-427b-ad97-b605a4b8c59b
|
||||
:END:
|
||||
#+tItLe: Well parsed!"]
|
||||
(graph-parser/parse-file conn "foo.org" text {})
|
||||
(is (= (test-db->diff-blocks conn "Well parsed!")
|
||||
(org-text->diffblocks text)))))
|
||||
|
||||
(defn text->diffblocks
|
||||
[text]
|
||||
(-> (gp-mldoc/->edn text (gp-mldoc/default-config :markdown))
|
||||
(fs-diff/ast->diff-blocks text :markdown {:block-pattern "-"})))
|
||||
|
||||
(deftest md->ast->diff-blocks-test
|
||||
(are [text diff-blocks]
|
||||
(= (text->diffblocks text)
|
||||
diff-blocks)
|
||||
"- a
|
||||
\t- b
|
||||
\t\t- c"
|
||||
[{:body "a" :uuid nil :level 1}
|
||||
{:body "b" :uuid nil :level 2}
|
||||
{:body "c" :uuid nil :level 3}]
|
||||
|
||||
"## hello
|
||||
\t- world
|
||||
\t\t- nice
|
||||
\t\t\t- nice
|
||||
\t\t\t- bingo
|
||||
\t\t\t- world"
|
||||
[{:body "## hello" :uuid nil :level 2}
|
||||
{:body "world" :uuid nil :level 2}
|
||||
{:body "nice" :uuid nil :level 3}
|
||||
{:body "nice" :uuid nil :level 4}
|
||||
{:body "bingo" :uuid nil :level 4}
|
||||
{:body "world" :uuid nil :level 4}]
|
||||
|
||||
"# a
|
||||
## b
|
||||
### c
|
||||
#### d
|
||||
### e
|
||||
- f
|
||||
\t- g
|
||||
\t\t- h
|
||||
\t- i
|
||||
- j"
|
||||
[{:body "# a" :uuid nil :level 1}
|
||||
{:body "## b" :uuid nil :level 2}
|
||||
{:body "### c" :uuid nil :level 3}
|
||||
{:body "#### d" :uuid nil :level 4}
|
||||
{:body "### e" :uuid nil :level 3}
|
||||
{:body "f" :uuid nil :level 1}
|
||||
{:body "g" :uuid nil :level 2}
|
||||
{:body "h" :uuid nil :level 3}
|
||||
{:body "i" :uuid nil :level 2}
|
||||
{:body "j" :uuid nil :level 1}]
|
||||
|
||||
"- a\n id:: 63e25526-3612-4fb1-8cf9-f66db1254a58
|
||||
\t- b
|
||||
\t\t- c"
|
||||
[{:body "a\n id:: 63e25526-3612-4fb1-8cf9-f66db1254a58"
|
||||
:uuid "63e25526-3612-4fb1-8cf9-f66db1254a58" :level 1}
|
||||
{:body "b" :uuid nil :level 2}
|
||||
{:body "c" :uuid nil :level 3}]))
|
||||
|
||||
(deftest diff-test
|
||||
(are [text1 text2 diffs]
|
||||
(= (bean/->clj (fs-diff/diff (text->diffblocks text1)
|
||||
(text->diffblocks text2)))
|
||||
diffs)
|
||||
"## hello
|
||||
\t- world
|
||||
\t\t- nice
|
||||
\t\t\t- nice
|
||||
\t\t\t- bingo
|
||||
\t\t\t- world"
|
||||
"## Halooooo
|
||||
\t- world
|
||||
\t\t- nice
|
||||
\t\t\t- nice
|
||||
\t\t\t- bingo
|
||||
\t\t\t- world"
|
||||
[[[-1 {:body "## hello"
|
||||
:level 2
|
||||
:uuid nil}]
|
||||
[1 {:body "## Halooooo"
|
||||
:level 2
|
||||
:uuid nil}]]
|
||||
[[0 {:body "world"
|
||||
:level 2
|
||||
:uuid nil}]]
|
||||
[[0 {:body "nice"
|
||||
:level 3
|
||||
:uuid nil}]]
|
||||
[[0 {:body "nice"
|
||||
:level 4
|
||||
:uuid nil}]]
|
||||
[[0 {:body "bingo"
|
||||
:level 4
|
||||
:uuid nil}]]
|
||||
[[0 {:body "world"
|
||||
:level 4
|
||||
:uuid nil}]]]
|
||||
|
||||
"## hello
|
||||
\t- world
|
||||
\t id:: 63e25526-3612-4fb1-8cf9-abcd12354abc
|
||||
\t\t- nice
|
||||
\t\t\t- nice
|
||||
\t\t\t- bingo
|
||||
\t\t\t- world"
|
||||
"## Halooooo
|
||||
\t- world
|
||||
\t\t- nice
|
||||
\t\t\t- nice
|
||||
\t\t\t- bingo
|
||||
\t\t\t- world"
|
||||
[[[-1 {:body "## hello"
|
||||
:level 2
|
||||
:uuid nil}]
|
||||
[1 {:body "## Halooooo"
|
||||
:level 2
|
||||
:uuid nil}]
|
||||
[1 {:body "world"
|
||||
:level 2
|
||||
:uuid nil}]]
|
||||
[[-1 {:body "world\n id:: 63e25526-3612-4fb1-8cf9-abcd12354abc"
|
||||
:level 2
|
||||
:uuid "63e25526-3612-4fb1-8cf9-abcd12354abc"}]]
|
||||
[[0 {:body "nice"
|
||||
:level 3
|
||||
:uuid nil}]]
|
||||
[[0 {:body "nice"
|
||||
:level 4
|
||||
:uuid nil}]]
|
||||
[[0 {:body "bingo"
|
||||
:level 4
|
||||
:uuid nil}]]
|
||||
[[0 {:body "world"
|
||||
:level 4
|
||||
:uuid nil}]]]
|
||||
|
||||
""
|
||||
"- abc def"
|
||||
[[[1 {:body "abc def"
|
||||
:level 1
|
||||
:uuid nil}]]]))
|
||||
|
||||
(deftest db->diffblocks
|
||||
(let [conn (ldb/start-conn)]
|
||||
(graph-parser/parse-file conn
|
||||
"foo.md"
|
||||
(str "- abc
|
||||
id:: 11451400-0000-0000-0000-000000000000\n"
|
||||
"- def
|
||||
id:: 63246324-6324-6324-6324-632463246324\n")
|
||||
{})
|
||||
(graph-parser/parse-file conn
|
||||
"bar.md"
|
||||
(str "- ghi
|
||||
id:: 11451411-1111-1111-1111-111111111111\n"
|
||||
"\t- jkl
|
||||
\t id:: 63241234-1234-1234-1234-123412341234\n")
|
||||
{})
|
||||
(are [page-name diff-blocks] (= (test-db->diff-blocks conn page-name)
|
||||
diff-blocks)
|
||||
"foo"
|
||||
[{:body "abc\nid:: 11451400-0000-0000-0000-000000000000" :uuid "11451400-0000-0000-0000-000000000000" :level 1}
|
||||
{:body "def\nid:: 63246324-6324-6324-6324-632463246324" :uuid "63246324-6324-6324-6324-632463246324" :level 1}]
|
||||
|
||||
"bar"
|
||||
[{:body "ghi\nid:: 11451411-1111-1111-1111-111111111111" :uuid "11451411-1111-1111-1111-111111111111" :level 1}
|
||||
{:body "jkl\nid:: 63241234-1234-1234-1234-123412341234" :uuid "63241234-1234-1234-1234-123412341234" :level 2}])
|
||||
|
||||
(are [page-name text new-uuids] (= (let [old-blks (test-db->diff-blocks conn page-name)
|
||||
new-blks (text->diffblocks text)
|
||||
diff-ops (fs-diff/diff old-blks new-blks)]
|
||||
(bean/->clj (fs-diff/attachUUID diff-ops (bean/->js (map :uuid old-blks)) "NEW_ID")))
|
||||
new-uuids)
|
||||
"foo"
|
||||
"- abc
|
||||
- def"
|
||||
["11451400-0000-0000-0000-000000000000"
|
||||
"NEW_ID"]
|
||||
|
||||
"bar"
|
||||
"- ghi
|
||||
\t- jkl"
|
||||
["11451411-1111-1111-1111-111111111111"
|
||||
"NEW_ID"]
|
||||
|
||||
"non exist page"
|
||||
"- k\n\t- l"
|
||||
["NEW_ID" "NEW_ID"]
|
||||
|
||||
"another non exist page"
|
||||
":PROPERTIES:
|
||||
:ID: 72289d9a-eb2f-427b-ad97-b605a4b8c59b
|
||||
:END:
|
||||
#+tItLe: Well parsed!"
|
||||
["72289d9a-eb2f-427b-ad97-b605a4b8c59b"])))
|
||||
|
||||
(deftest ast->diff-blocks-test
|
||||
(are [ast text diff-blocks]
|
||||
(= (fs-diff/ast->diff-blocks ast text :org {:block-pattern "-"})
|
||||
diff-blocks)
|
||||
[[["Properties" [["TiTlE" "Howdy" []]]] nil]]
|
||||
"#+title: Howdy"
|
||||
[{:body "#+title: Howdy", :level 1, :uuid nil}])
|
||||
|
||||
(are [ast text diff-blocks]
|
||||
(= (fs-diff/ast->diff-blocks ast text :org {:block-pattern "-" :user-config {:property-pages/enabled? true}})
|
||||
diff-blocks)
|
||||
[[["Property_Drawer" [["foo" "#bar" [["Tag" [["Plain" "bar"]]]]] ["baz" "#bing" [["Tag" [["Plain" "bing"]]]]]]] {:start_pos 0, :end_pos 22}]]
|
||||
"foo:: #bar\nbaz:: #bing"
|
||||
[{:body "foo:: #bar\nbaz:: #bing", :level 1, :uuid nil}]))
|
||||
|
||||
(deftest ast-empty-diff-test
|
||||
(are [ast text diff-ops]
|
||||
(= (bean/->clj (->> (fs-diff/ast->diff-blocks ast text :org {:block-pattern "-" :user-config {:property-pages/enabled? true}})
|
||||
(fs-diff/diff [])))
|
||||
diff-ops)
|
||||
[[["Property_Drawer" [["foo" "#bar" [["Tag" [["Plain" "bar"]]]]] ["baz" "#bing" [["Tag" [["Plain" "bing"]]]]]]] {:start_pos 0, :end_pos 22}]]
|
||||
"foo:: #bar\nbaz:: #bing"
|
||||
[[[1 {:body "foo:: #bar\nbaz:: #bing", :level 1, :uuid nil}]]]))
|
||||
|
||||
;; Ensure diff-merge-uuids follows the id:: in the content
|
||||
(deftest diff-merge-uuid-extract-test
|
||||
(let [conn (ldb/start-conn)
|
||||
foo-content (str "- abc
|
||||
id:: 11451400-0000-0000-0000-000000000000\n"
|
||||
"- def
|
||||
id:: 63246324-6324-6324-6324-632463246324\n")
|
||||
bar-content (str "- ghi
|
||||
id:: 11451411-1111-1111-1111-111111111111\n"
|
||||
"\t- jkl
|
||||
\t id:: 63241234-1234-1234-1234-123412341234\n") ]
|
||||
(graph-parser/parse-file conn "foo.md" foo-content {})
|
||||
(graph-parser/parse-file conn "bar.md" bar-content {})
|
||||
(are [ast content page-name uuids]
|
||||
(= (with-redefs [conn/get-db (constantly @conn)]
|
||||
(#'file-common-handler/diff-merge-uuids :markdown ast content {:page-name page-name
|
||||
:block-pattern "-"}))
|
||||
uuids)
|
||||
|
||||
(gp-mldoc/->edn (str foo-content "- newline\n") (gp-mldoc/default-config :markdown))
|
||||
(str foo-content "- newline\n")
|
||||
"foo"
|
||||
["11451400-0000-0000-0000-000000000000"
|
||||
"63246324-6324-6324-6324-632463246324"
|
||||
nil]
|
||||
|
||||
(gp-mldoc/->edn (str bar-content "- newline\n") (gp-mldoc/default-config :markdown))
|
||||
(str bar-content "- newline\n")
|
||||
"bar"
|
||||
["11451411-1111-1111-1111-111111111111"
|
||||
"63241234-1234-1234-1234-123412341234"
|
||||
nil])))
|
||||
|
||||
;; Ensure diff-merge-uuids keeps the block uuids unchanged at best effort
|
||||
(deftest diff-merge-uuid-persist-test
|
||||
(let [conn (ldb/start-conn)
|
||||
foo-content (str "- abc\n"
|
||||
"- def\n")
|
||||
bar-content (str "- ghi\n"
|
||||
"\t- jkl\n")]
|
||||
(graph-parser/parse-file conn "foo.md" foo-content {})
|
||||
(graph-parser/parse-file conn "bar.md" bar-content {})
|
||||
(are [ast content page-name uuids]
|
||||
(= (with-redefs [conn/get-db (constantly @conn)]
|
||||
(#'file-common-handler/diff-merge-uuids :markdown ast content {:page-name page-name
|
||||
:block-pattern "-"}))
|
||||
;; Get all uuids under the page
|
||||
(conj (->> page-name
|
||||
(test-db->diff-blocks conn)
|
||||
(map :uuid)
|
||||
(vec)) nil))
|
||||
|
||||
(gp-mldoc/->edn (str foo-content "- newline\n") (gp-mldoc/default-config :markdown))
|
||||
(str foo-content "- newline\n")
|
||||
"foo"
|
||||
["11451400-0000-0000-0000-000000000000"
|
||||
"63246324-6324-6324-6324-632463246324"
|
||||
nil]
|
||||
|
||||
(gp-mldoc/->edn (str bar-content "- newline\n") (gp-mldoc/default-config :markdown))
|
||||
(str bar-content "- newline\n")
|
||||
"bar"
|
||||
["11451411-1111-1111-1111-111111111111"
|
||||
"63241234-1234-1234-1234-123412341234"
|
||||
nil])))
|
||||
Reference in New Issue
Block a user