88-- Portability : portable
99-- Author : Stephan Wehr (wehr@factisresearch.com) and JP Moresmau (jp@moresmau.fr)
1010--
11- -- Generates a string output that is similar to diff normal mode
11+ -- Generates a string output that is similar to diff normal mode.
1212-----------------------------------------------------------------------------
1313module Data.Algorithm.DiffOutput where
1414import Data.Algorithm.Diff
1515import Text.PrettyPrint hiding ((<>) )
1616import Data.Char
1717import Data.List
1818
19- -- | Converts Diffs to DiffOperations
19+ -- | Converts 'Diff's to 'DiffOperation's.
2020diffToLineRanges :: [Diff [String ]] -> [DiffOperation LineRange ]
2121diffToLineRanges = toLineRange 1 1
2222 where
2323 toLineRange :: Int -> Int -> [Diff [String ]] -> [DiffOperation LineRange ]
2424 toLineRange _ _ [] = []
25+ -- If the lines are the same, we just move forward.
2526 toLineRange leftLine rightLine (Both ls _: rs)=
2627 let lins= length ls
2728 in toLineRange (leftLine+ lins) (rightLine+ lins) rs
29+ -- A 'Change' is introduced when an addition is followed by a deletion, or vice versa.
2830 toLineRange leftLine rightLine (Second lsS: First lsF: rs)=
2931 toChange leftLine rightLine lsF lsS rs
3032 toLineRange leftLine rightLine (First lsF: Second lsS: rs)=
3133 toChange leftLine rightLine lsF lsS rs
34+ -- Introduce 'Addition's.
3235 toLineRange leftLine rightLine (Second lsS: rs)=
3336 let linesS= length lsS
3437 diff= Addition (LineRange (rightLine,rightLine+ linesS- 1 ) lsS) (leftLine- 1 )
3538 in diff : toLineRange leftLine (rightLine+ linesS) rs
39+ -- Introduce 'Deletion's.
3640 toLineRange leftLine rightLine (First lsF: rs)=
3741 let linesF= length lsF
3842 diff= Deletion (LineRange (leftLine,leftLine+ linesF- 1 ) lsF) (rightLine- 1 )
3943 in diff: toLineRange(leftLine+ linesF) rightLine rs
44+ -- | Build 'Change's from adjacent additions and deletions.
45+ toChange :: Int -- ^ Current left line number.
46+ -> Int -- ^ Current right line number.
47+ -> [String ] -- ^ Lines from the 'First' list (corresponding to deletions).
48+ -> [String ] -- ^ Lines from the 'Second' list (corresponding to additions).
49+ -> [Diff [String ]] -- ^ Remaining 'Diff's.
50+ -> [DiffOperation LineRange ]
4051 toChange leftLine rightLine lsF lsS rs=
4152 let linesS= length lsS
4253 linesF= length lsF
4354 in Change (LineRange (leftLine,leftLine+ linesF- 1 ) lsF) (LineRange (rightLine,rightLine+ linesS- 1 ) lsS)
4455 : toLineRange (leftLine+ linesF) (rightLine+ linesS) rs
4556
46- -- | pretty print the differences. The output is similar to the output of the diff utility
57+ -- | Pretty print the differences. The output is similar to the output of the @ diff@ utility.
4758--
4859-- > > putStr (ppDiff (getGroupedDiff ["a","b","c","d","e"] ["a","c","d","f"]))
4960-- > 2d1
@@ -59,7 +70,7 @@ ppDiff gdiff =
5970 render (prettyDiffs diffLineRanges) ++ " \n "
6071
6172
62- -- | pretty print of diff operations
73+ -- | Pretty print of diff operations.
6374prettyDiffs :: [DiffOperation LineRange ] -> Doc
6475prettyDiffs [] = empty
6576prettyDiffs (d : rest) = prettyDiff d $$ prettyDiffs rest
@@ -80,58 +91,86 @@ prettyDiffs (d : rest) = prettyDiff d $$ prettyDiffs rest
8091 prettyLines start lins =
8192 vcat (map (\ l -> char start <+> text l) lins)
8293
83- -- | Parse pretty printed Diffs as DiffOperations
94+ -- | Parse pretty printed 'Diff's as 'DiffOperation's.
8495parsePrettyDiffs :: String -> [DiffOperation LineRange ]
8596parsePrettyDiffs = reverse . doParse [] . lines
8697 where
87- doParse diffs [] = diffs
88- doParse diffs s =
98+ -- | Parsing entry point that iteratively accumulates 'DiffOperation's
99+ -- until the input is exhausted.
100+ doParse :: [DiffOperation LineRange ] -> [String ] -> [DiffOperation LineRange ]
101+ -- NOTE: Incorrectly formatted lines are ignored.
102+ doParse acc [] = acc
103+ doParse acc s =
89104 let (mnd,r) = parseDiff s
90105 in case mnd of
91- Just nd -> doParse (nd: diffs) r
92- _ -> doParse diffs r
106+ Just nd -> doParse (nd: acc) r
107+ _ -> doParse acc r
108+
109+ parseDiff :: [String ] -> (Maybe (DiffOperation LineRange ), [String ])
93110 parseDiff [] = (Nothing ,[] )
94111 parseDiff (h: rs) = let
95112 (r1,hrs1) = parseRange h
96113 in case hrs1 of
114+ -- In each case, we pass the left line range,
115+ -- the remaining string after the type character,
116+ -- which must contain the right line range,
117+ -- and the remaining lines to parse.
97118 (' d' : hrs2) -> parseDel r1 hrs2 rs
98119 (' a' : hrs2) -> parseAdd r1 hrs2 rs
99120 (' c' : hrs2) -> parseChange r1 hrs2 rs
100121 _ -> (Nothing ,rs)
122+
123+ parseDel :: (LineNo , LineNo ) -> String -> [String ] -> (Maybe (DiffOperation LineRange ), [String ])
101124 parseDel r1 hrs2 rs = let
125+ -- NOTE: the wildcard should correspond to the end of line,
126+ -- but is ignored for simplicity.
102127 (r2,_) = parseRange hrs2
103128 (ls,rs2) = span (isPrefixOf " <" ) rs
104129 in (Just $ Deletion (LineRange r1 (map (drop 2 ) ls)) (fst r2), rs2)
130+
131+ parseAdd :: (LineNo , LineNo ) -> String -> [String ] -> (Maybe (DiffOperation LineRange ), [String ])
105132 parseAdd r1 hrs2 rs = let
133+ -- NOTE: the wildcard should correspond to the end of line,
134+ -- but is ignored for simplicity.
106135 (r2,_) = parseRange hrs2
107136 (ls,rs2) = span (isPrefixOf " >" ) rs
108137 in (Just $ Addition (LineRange r2 (map (drop 2 ) ls)) (fst r1), rs2)
138+
139+ parseChange :: (LineNo , LineNo ) -> String -> [String ] -> (Maybe (DiffOperation LineRange ), [String ])
109140 parseChange r1 hrs2 rs = let
141+ -- NOTE: the wildcard should correspond to the end of line,
142+ -- but is ignored for simplicity.
110143 (r2,_) = parseRange hrs2
111144 (ls1,rs2) = span (isPrefixOf " <" ) rs
112145 in case rs2 of
146+ -- The left and right diff of a 'Change' are separated by a "---" line.
113147 (" ---" : rs3) -> let
114148 (ls2,rs4) = span (isPrefixOf " >" ) rs3
115149 in (Just $ Change (LineRange r1 (map (drop 2 ) ls1)) (LineRange r2 (map (drop 2 ) ls2)), rs4)
116150 _ -> (Nothing ,rs2)
151+
117152 parseRange :: String -> ((LineNo , LineNo ),String )
118153 parseRange l = let
119154 (fstLine,rs) = span isDigit l
120155 (sndLine,rs3) = case rs of
156+ -- The comma is used to separate
157+ -- the start and end line numbers in a range,
158+ -- but is omitted if they are the same.
159+ -- i.e. the range is a single line.
121160 (' ,' : rs2) -> span isDigit rs2
122161 _ -> (fstLine,rs)
123162 in ((read fstLine,read sndLine),rs3)
124163
125- -- | Line number alias
164+ -- | Line number alias.
126165type LineNo = Int
127166
128- -- | Line Range: start, end and contents
167+ -- | Line Range: start, end and contents.
129168data LineRange = LineRange { lrNumbers :: (LineNo , LineNo )
130169 , lrContents :: [String ]
131170 }
132- deriving (Show ,Read ,Eq ,Ord )
171+ deriving (Show , Read , Eq , Ord )
133172
134- -- | Diff Operation representing changes to apply
173+ -- | Diff operation representing changes to apply.
135174data DiffOperation a = Deletion a LineNo
136175 | Addition a LineNo
137176 | Change a a
0 commit comments