git-mediate-1.0.8.1: src/Resolution.hs
{-# LANGUAGE NoImplicitPrelude, RecordWildCards, BangPatterns #-}
module Resolution
( Result(..)
, NewContent(..)
, Untabify(..)
, resolveContent
, fullySuccessful
) where
import Conflict (Conflict(..))
import qualified Conflict
import Prelude.Compat
data Resolution
= NoResolution
| Resolution String
| PartialResolution String
resolveConflict :: Conflict -> Resolution
resolveConflict conflict@Conflict{..}
| cBodyA == cBodyBase = Resolution $ unlines cBodyB
| cBodyB == cBodyBase = Resolution $ unlines cBodyA
| cBodyA == cBodyB = Resolution $ unlines cBodyA
| matchTop > 0 || matchBottom > 0 =
PartialResolution $ unlines $
take matchTop cBodyA ++
Conflict.prettyLines conflict
{ cBodyA = unmatched cBodyA
, cBodyBase = unmatched cBodyBase
, cBodyB = unmatched cBodyB
} ++
takeEnd matchBottom cBodyA
| otherwise = NoResolution
where
match base a b
| null base = lengthOfCommonPrefix a b
| otherwise = minimum $ map (lengthOfCommonPrefix base) [a, b]
matchTop = match cBodyBase cBodyA cBodyB
revBottom = reverse . drop matchTop
matchBottom = match (revBottom cBodyBase) (revBottom cBodyA) (revBottom cBodyB)
dropEnd count xs = take (length xs - count) xs
takeEnd count xs = drop (length xs - count) xs
unmatched xs = drop matchTop $ dropEnd matchBottom xs
lengthOfCommonPrefix :: Eq a => [a] -> [a] -> Int
lengthOfCommonPrefix x y = length $ takeWhile id $ zipWith (==) x y
data Result = Result
{ _resolvedSuccessfully :: !Int
, _reducedConflicts :: !Int
, _failedToResolve :: !Int
}
fullySuccessful :: Result -> Bool
fullySuccessful (Result _ reduced failed) = reduced == 0 && failed == 0
instance Semigroup Result where
Result x0 y0 z0 <> Result x1 y1 z1 = Result (x0+x1) (y0+y1) (z0+z1)
instance Monoid Result where mempty = Result 0 0 0
data NewContent = NewContent
{ _result :: !Result
, _newContent :: !String
}
instance Semigroup NewContent where
NewContent x0 y0 <> NewContent x1 y1 = NewContent (x0<>x1) (y0<>y1)
instance Monoid NewContent where mempty = NewContent mempty mempty
newtype Untabify = Untabify { mUntabifySize :: Maybe Int }
untabifyStr :: Int -> String -> String
untabifyStr size =
go 0
where
cyclicInc col
| col >= size - 1 = 0
| otherwise = col + 1
go !col ('\t':rest) = replicate (size - col) ' ' ++ go 0 rest
go !col (x:rest) = x : go (cyclicInc col) rest
go _ [] = []
untabify :: Int -> Conflict -> Conflict
untabify = Conflict.setBodyStrings . untabifyStr
resolveContent :: Untabify -> [Either String Conflict] -> NewContent
resolveContent (Untabify mUntabifySize) =
foldMap go
where
untabified = maybe id untabify mUntabifySize
go (Left line) = NewContent mempty (unlines [line])
go (Right conflict) =
case resolveConflict (untabified conflict) of
NoResolution -> NewContent (Result 0 0 1)
(Conflict.pretty (untabified conflict))
Resolution trivialLines -> NewContent (Result 1 0 0) trivialLines
PartialResolution newLines -> NewContent (Result 0 1 0) newLines