module Darcs.Patch.Viewing ( xmlSummary, plainSummary )
where
import Prelude hiding ( pi, readFile )
import Control.Monad.State.Strict ( gets )
import Control.Monad.Trans ( liftIO )
import Storage.Hashed.Monad( TreeIO, fileExists, readFile, tree, virtualTreeIO )
import Storage.Hashed.AnchoredPath( floatPath )
import ByteStringUtils (linesPS )
import qualified Data.ByteString as BS (null, concat)
import qualified Data.ByteString.Lazy as BL (toChunks)
import Darcs.Patch.FileName ( FileName, fn2fp )
import Printer ( Doc, empty, vcat,
text, blueText, Color(Cyan,Magenta), lineColor,
minus, plus, ($$), (<+>), (<>),
prefix, renderString,
userchunkPS,
)
import Darcs.Patch.Core ( Patch(..), Named(..),
patchcontents )
import Darcs.Patch.Prim ( Prim(..), primIsHunk, isHunk, formatFileName, showPrim, FileNameFormat(..), Conflict(..),
Effect, IsConflictedPrim(IsC), ConflictState(..),
DirPatchType(..), FilePatchType(..) )
import Darcs.Patch.Patchy ( Patchy, Apply, ShowPatch(..), identity )
import Darcs.Patch.Show ( showPatch_, showNamedPrefix )
import Darcs.Patch.Info ( showPatchInfo, humanFriendly )
import Darcs.Patch.Apply ( applyToTree )
#include "impossible.h"
#include "gadts.h"
import Darcs.Witnesses.Ordered ( RL(..), FL(..),
mapFL, mapFL_FL, reverseRL )
instance ShowPatch Prim where
showPatch = showPrim OldFormat
showContextPatch p@(FP _ (Hunk _ _ _)) = showContextHunk (PP p)
showContextPatch (Split ps) =
do x <- showContextSeries (mapFL_FL PP ps)
return $ blueText "(" $$ x <> blueText ")"
showContextPatch p = return $ showPatch p
summary = vcat . map summChunkToLine . genSummary . (:[]) . IsC Okay
thing _ = "change"
plainSummary :: (Conflict e, Effect e) => e C(x y) -> Doc
plainSummary = vcat . map summChunkToLine . genSummary . conflictedEffect
instance ShowPatch Patch where
showPatch = showPatch_
showContextPatch (PP x) | primIsHunk x = showContextHunk (PP x)
showContextPatch (ComP NilFL) = return $ blueText "{" $$ blueText "}"
showContextPatch (ComP ps) =
do x <- showContextSeries ps
return $ blueText "{" $$ x $$ blueText "}"
showContextPatch p = return $ showPatch p
summary = plainSummary
thing _ = "change"
showContextSeries :: (Apply p, ShowPatch p, Effect p) => FL p C(x y) -> TreeIO Doc
showContextSeries patches = scs identity patches
where scs :: (Apply p, ShowPatch p, Effect p) => Prim C(w x) -> FL p C(x y) -> TreeIO Doc
scs pold (p:>:ps) = do
s' <- gets tree >>= liftIO . applyToTree p
case isHunk p of
Nothing -> do a <- showContextPatch p
b <- liftIO $ virtualTreeIO (scs identity ps) s'
return $ a $$ fst b
Just hp ->
case ps of
NilFL -> coolContextHunk pold hp identity
(p2:>:_) ->
case isHunk p2 of
Nothing -> do a <- coolContextHunk pold hp identity
b <- liftIO $ virtualTreeIO (scs hp ps) s'
return $ a $$ fst b
Just hp2 -> do a <- coolContextHunk pold hp hp2
b <- liftIO $ virtualTreeIO (scs hp ps) s'
return $ a $$ fst b
scs _ NilFL = return empty
showContextHunk :: (Apply p, ShowPatch p, Effect p) => p C(x y) -> TreeIO Doc
showContextHunk p = case isHunk p of
Just h -> coolContextHunk identity h identity
Nothing -> return $ showPatch p
coolContextHunk :: Prim C(a b) -> Prim C(b c) -> Prim C(c d) -> TreeIO Doc
coolContextHunk prev p@(FP f (Hunk l o n)) next = do
let path = floatPath $ fn2fp f
have <- fileExists path
content <- if have then Just `fmap` readFile path else return Nothing
case (linesPS . BS.concat . BL.toChunks) `fmap` content of
Nothing -> return $ showPatch p
Just ls ->
let numpre = case prev of
(FP f' (Hunk lprev _ nprev))
| f' == f &&
l (lprev + length nprev + 3) < 3 &&
lprev < l ->
max 0 $ l (lprev + length nprev + 3)
_ -> if l >= 4 then 3 else l 1
pre = take numpre $ drop (l numpre 1) ls
numpost = case next of
(FP f' (Hunk lnext _ _))
| f' == f && lnext < l+length n+4 &&
lnext > l ->
lnext (l+length n)
_ -> 3
cleanedls = case reverse ls of
(x:xs) | BS.null x -> reverse xs
_ -> ls
post = take numpost $ drop (max 0 $ l+length o1) cleanedls
in return $ blueText "hunk" <+> formatFileName OldFormat f <+> text (show l)
$$ prefix " " (vcat $ map userchunkPS pre)
$$ lineColor Magenta (prefix "-" (vcat $ map userchunkPS o))
$$ lineColor Cyan (prefix "+" (vcat $ map userchunkPS n))
$$ prefix " " (vcat $ map userchunkPS post)
coolContextHunk _ _ _ = impossible
xmlSummary :: (Effect p, Patchy p, Conflict p) => Named p C(x y) -> Doc
xmlSummary p = text "<summary>"
$$ (vcat . map summChunkToXML . genSummary . conflictedEffect . patchcontents $ p)
$$ text "</summary>"
escapeXML :: String -> Doc
escapeXML = text . strReplace '\'' "'" . strReplace '"' """ .
strReplace '>' ">" . strReplace '<' "<" . strReplace '&' "&"
strReplace :: Char -> String -> String -> String
strReplace _ _ [] = []
strReplace x y (z:zs)
| x == z = y ++ (strReplace x y zs)
| otherwise = z : (strReplace x y zs)
data SummChunk = SummChunk SummDetail ConflictState
deriving (Ord, Eq)
data SummDetail = SummAddDir FileName
| SummRmDir FileName
| SummFile SummOp FileName Int Int Int
| SummMv FileName FileName
| SummNone
deriving (Ord, Eq)
data SummOp = SummAdd | SummRm | SummMod deriving (Ord, Eq)
genSummary :: [IsConflictedPrim] -> [SummChunk]
genSummary p
= combine $ concatMap s2 p
where s2 :: IsConflictedPrim -> [SummChunk]
s2 (IsC c x) = map (\d -> SummChunk d c) $ s x
s :: Prim C(x y) -> [SummDetail]
s (FP f (Hunk _ o n)) = [SummFile SummMod f (length o) (length n) 0]
s (FP f (Binary _ _)) = [SummFile SummMod f 0 0 0]
s (FP f AddFile) = [SummFile SummAdd f 0 0 0]
s (FP f RmFile) = [SummFile SummRm f 0 0 0]
s (FP f (TokReplace _ _ _)) = [SummFile SummMod f 0 0 1]
s (DP d AddDir) = [SummAddDir d]
s (DP d RmDir) = [SummRmDir d]
s (Split xs) = concat $ mapFL s xs
s (Move f1 f2) = [SummMv f1 f2]
s (ChangePref _ _ _) = [SummNone]
s Identity = [SummNone]
combine (x1@(SummChunk d1 c1) : x2@(SummChunk d2 c2) : ss)
= case combineDetail d1 d2 of
Nothing -> x1 : combine (x2:ss)
Just d3 -> combine $ SummChunk d3 (combineConflitStates c1 c2) : ss
combine (x:ss) = x : combine ss
combine [] = []
combineDetail (SummFile o1 f1 r1 a1 x1) (SummFile o2 f2 r2 a2 x2) | f1 == f2 =
do o3 <- combineOp o1 o2
return $ SummFile o3 f1 (r1 + r2) (a1 + a2) (x1 + x2)
combineDetail _ _ = Nothing
combineConflitStates Conflicted _ = Conflicted
combineConflitStates _ Conflicted = Conflicted
combineConflitStates Duplicated _ = Duplicated
combineConflitStates _ Duplicated = Duplicated
combineConflitStates Okay Okay = Okay
combineOp SummAdd SummRm = Nothing
combineOp SummRm SummAdd = Nothing
combineOp SummAdd _ = Just SummAdd
combineOp _ SummAdd = Just SummAdd
combineOp SummRm _ = Just SummRm
combineOp _ SummRm = Just SummRm
combineOp SummMod SummMod = Just SummMod
summChunkToXML :: SummChunk -> Doc
summChunkToXML (SummChunk detail c) =
case detail of
SummRmDir f -> xconf c "remove_directory" (xfn f)
SummAddDir f -> xconf c "add_directory" (xfn f)
SummFile SummRm f _ _ _ -> xconf c "remove_file" (xfn f)
SummFile SummAdd f _ _ _ -> xconf c "add_file" (xfn f)
SummFile SummMod f r a x -> xconf c "modify_file" $ xfn f <> xrm r <> xad a <> xrp x
SummMv f1 f2 -> text "<move from=\"" <> xfn f1
<> text "\" to=\"" <> xfn f2 <> text"\"/>"
SummNone -> empty
where
xconf Okay t x = text ('<':t++">") $$ x $$ text ("</"++t++">")
xconf Conflicted t x = text ('<':t++" conflict='true'>") $$ x $$ text ("</"++t++">")
xconf Duplicated t x = text ('<':t++" duplicate='true'>") $$ x $$ text ("</"++t++">")
xfn = escapeXML . dropDotSlash .fn2fp
xad 0 = empty
xad a = text "<added_lines num='" <> text (show a) <> text "'/>"
xrm 0 = empty
xrm a = text "<removed_lines num='" <> text (show a) <> text "'/>"
xrp 0 = empty
xrp a = text "<replaced_tokens num='" <> text (show a) <> text "'/>"
summChunkToLine :: SummChunk -> Doc
summChunkToLine (SummChunk detail c) =
case detail of
SummRmDir f -> lconf c "R" $ text (fn2fp f) <> text "/"
SummAddDir f -> lconf c "A" $ text (fn2fp f) <> text "/"
SummFile SummRm f _ _ _ -> lconf c "R" $ text (fn2fp f)
SummFile SummAdd f _ _ _ -> lconf c "A" $ text (fn2fp f)
SummFile SummMod f r a x -> lconf c "M" $ text (fn2fp f) <+> rm r <+> ad a <+> rp x
SummMv f1 f2 -> text " " <> text (fn2fp f1)
<> text " -> " <> text (fn2fp f2)
SummNone -> case c of
Okay -> empty
_ -> lconf c "" empty
where
lconf Okay t x = text t <+> x
lconf Conflicted t x = text (t ++ "!") <+> x
lconf Duplicated t x = text t <+> x <+> text "duplicate"
ad 0 = empty
ad a = plus <> text (show a)
rm 0 = empty
rm a = minus <> text (show a)
rp 0 = empty
rp a = text "r" <> text (show a)
dropDotSlash :: FilePath -> FilePath
dropDotSlash ('.':'/':str) = dropDotSlash str
dropDotSlash str = str
instance (Conflict p, ShowPatch p) => ShowPatch (Named p) where
showPatch (NamedP n [] p) = showPatchInfo n <> showPatch p
showPatch (NamedP n d p) = showNamedPrefix n d <+> showPatch p
showContextPatch (NamedP n [] p) = showContextPatch p >>= return . (showPatchInfo n <>)
showContextPatch (NamedP n d p) = showContextPatch p >>= return . (showNamedPrefix n d <+>)
description (NamedP n _ _) = humanFriendly n
summary p = description p $$ text "" $$
prefix " " (plainSummary p)
showNicely p@(NamedP _ _ pt) = description p $$
prefix " " (showNicely pt)
instance (Conflict p, ShowPatch p) => Show (Named p C(x y)) where
show = renderString . showPatch
instance (Conflict p, Apply p, Effect p, ShowPatch p) => ShowPatch (FL p) where
showPatch xs = vcat (mapFL showPatch xs)
showContextPatch = showContextSeries
description = vcat . mapFL description
summary = vcat . mapFL summary
thing x = thing (helperx x) ++ "s"
where helperx :: FL a C(x y) -> a C(x y)
helperx _ = undefined
things = thing
instance (Conflict p, Apply p, ShowPatch p) => ShowPatch (RL p) where
showPatch = showPatch . reverseRL
showContextPatch = showContextPatch . reverseRL
description = description . reverseRL
summary = summary . reverseRL
thing = thing . reverseRL
things = things . reverseRL
instance (Conflict p, Patchy p) => Patchy (FL p)
instance (Conflict p, Patchy p) => Patchy (RL p)