{-# LANGUAGE ViewPatterns #-} module Darcs.Patch.Prim.Canonize ( canonizeFL ) where import Darcs.Prelude import qualified Data.ByteString as B (ByteString, empty) import Darcs.Patch.FileHunk ( FileHunk(..), IsHunk(..) ) import Darcs.Patch.Prim.Class ( PrimConstruct(primFromHunk) , PrimCoalesce(sortCoalesceFL) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), joinGapsFL, mapFL_FL, concatFL ) import Darcs.Patch.Witnesses.Sealed ( unseal, Gap(..), unFreeLeft ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoercePEnd ) import Darcs.Util.Diff ( DiffAlgorithm, getChanges ) canonizeHunk :: Gap w => DiffAlgorithm -> FileHunk oid wX wY -> w (FL (FileHunk oid)) canonizeHunk _ (FileHunk f line old new) | null old || null new || old == [B.empty] || new == [B.empty] = freeGap (FileHunk f line old new :>: NilFL) canonizeHunk da (FileHunk f line old new) = makeHoley f line $ getChanges da old new makeHoley :: Gap w => oid -> Int -> [(Int, [B.ByteString], [B.ByteString])] -> w (FL (FileHunk oid)) makeHoley f line = joinGapsFL . map (\(l, o, n) -> freeGap (FileHunk f (l + line) o n)) -- | It can sometimes be handy to have a canonical representation of a given -- patch. We achieve this by defining a canonical form for each patch type, -- and a function 'canonize' which takes a patch and puts it into -- canonical form. This routine is used by the diff function to create an -- optimal patch (based on an LCS algorithm) from a simple hunk describing the -- old and new version of a file. canonize :: (IsHunk prim, PrimConstruct prim) => DiffAlgorithm -> prim wX wY -> FL prim wX wY canonize da p | Just fh <- isHunk p = mapFL_FL primFromHunk $ unseal unsafeCoercePEnd $ unFreeLeft $ canonizeHunk da fh canonize _ p = p :>: NilFL -- | Put a sequence of primitive patches into canonical form. -- -- Even if the patches are just hunk patches, -- this is not necessarily the same set of results as you would get -- if you applied the sequence to a specific tree and recalculated -- a diff. -- -- XXX Why not? How does it differ? The implementation for Prim.V1 does -- sortCoalesceFL and then invokes the diff algorithm for each hunk. How can -- that be any different to applying the sequence and then taking the diff? -- Is this merely because diff does not sort by file path? -- -- Besides, diff and apply /must/ be inverses in the sense that for any two -- states {start, end}, we have -- -- prop> diff start (apply (diff start end)) == end canonizeFL :: (IsHunk prim, PrimCoalesce prim, PrimConstruct prim) => DiffAlgorithm -> FL prim wX wY -> FL prim wX wY -- Note: it is important to first coalesce and then canonize, since -- coalescing can produce non-canonical hunks (while hunks resulting -- from canonizing a single hunk cannot be coalesced). See issue525, -- in particular msg20270 for details. canonizeFL da = concatFL . mapFL_FL (canonize da) . sortCoalesceFL