Fix ^:displace and ^:replace handling in meta-merge

An object with the metadata flag `:displace` set signals that, if a merge
conflict appears, this object is to be discarded. Likewise, `^:replace` signals
that this object should be kept in a merge conflict.

However, previous functionality only tested if the right element had the
`:replace` flag, or if the left element had the `:displace` flag. This commit
resolves this by checking whether the left element has a `:replace` flag and the
right element has a `:displace` flag, and handles accordingly to the semantics
explained in the previous paragraph.

Whenever two elements where both has the `:displace` flag is merged, the
leftmost is picked, and their metadata is merged. Likewise for the `:replace`
flag. The elements will not lose their `:displace` and `:replace` flags, as they
have not really been preferred over another element.

The `nil?` tests have been placed at the top to reflect that nil is the lack of
a value, not a value itself. As such, elements will not be "preferred" or
"discarded" over nil/nothing.
This commit is contained in:
Jean Niklas L'orange 2012-12-28 04:28:34 +01:00
parent 9bb4a5acb3
commit 27fe39d618

View file

@ -232,23 +232,49 @@
:offline {:offline? true}
:debug {:debug true}}))
(defn- displace?
"Returns true if the object is marked as displaceable"
[obj]
(-> obj meta :displace))
(defn- replace?
"Returns true if the object is marked as replaceable"
[obj]
(-> obj meta :replace))
(defn- meta-merge
"Recursively merge values based on the information in their metadata."
[left right]
(cond (or (-> left meta :displace)
(-> right meta :replace))
(cond (nil? left) right
(nil? right) left
(and (displace? left) ;; Pick the rightmost
(displace? right)) ;; if both are marked as displaceable
(with-meta right
(merge (-> left meta (dissoc :displace))
(-> right meta (dissoc :replace))))
(merge (meta left) (meta right)))
(and (replace? left) ;; Pick the rightmost
(replace? right)) ;; if both are marked as replaceable
(with-meta right
(merge (meta left) (meta right)))
(or (displace? left)
(replace? right))
(with-meta right
(merge (-> right meta (dissoc :replace))
(-> left meta (dissoc :displace))))
(or (replace? left)
(displace? right))
(with-meta left
(merge (-> left meta (dissoc :replace))
(-> right meta (dissoc :displace))))
(-> left meta :reduce)
(-> left meta :reduce
(reduce left right)
(with-meta (meta left)))
(nil? left) right
(nil? right) left
(and (map? left) (map? right))
(merge-with meta-merge left right)