pretty-diff 0.2.0.3 → 0.3.0.0
raw patch · 4 files changed
+370/−196 lines, 4 files
Files
- CHANGELOG.md +5/−0
- pretty-diff.cabal +2/−2
- src/Pretty/Diff.hs +158/−71
- test/Main.hs +205/−123
CHANGELOG.md view
@@ -1,3 +1,8 @@+# 0.3.0.0++- `pretty/above/below` take `Text` instead of `Show a` allowing the user to decide how to turn stuff into a string.+- Add `prettyMultilines` to render multiline input.+ # 0.2.0.3 - Relax version bounds to support `tasty-1.4`
pretty-diff.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 98c3c2b107783295340487cd6c2daf7788c7660e79fc169f7701b57b33c5fd28+-- hash: b037055a0de30dc728cde0c1b44091182200bbc9adf3ae7ef583d2d6495921c4 name: pretty-diff-version: 0.2.0.3+version: 0.3.0.0 synopsis: Pretty printing a diff of two values. description: Please see the README at <https://github.com/stoeffel/pretty-diff>. category: Diffing
src/Pretty/Diff.hs view
@@ -26,9 +26,11 @@ ( -- * Configuration Config (Config, separatorText, wrapping), Wrapping (Wrap, NoWrap),+ Context (FullContext, Surrounding), -- * pretty printing pretty,+ prettyMultilines, above, below, )@@ -37,56 +39,55 @@ import qualified Data.Algorithm.Diff as Diff import Data.Default (Default, def) import Data.Function ((&))-import Data.List (transpose)+import Data.List (take, transpose, zipWith) import Data.Maybe (fromMaybe, mapMaybe) import Data.String (IsString)-import qualified Data.Text as Text import Data.Text (Text)+import qualified Data.Text as Text import Prelude -- | Configuration for `Pretty.Diff.pretty`.-data Config- = Config- { -- | Text that gets displayed inbetween the diffed values- --- -- @- -- Diff.pretty def { Diff.separatorText = "differing" } "1234" "_23"- -- @- --- -- Will create a string that looks like this:- --- -- @- -- ▼ ▼- -- "1234"- -- ╷- -- │ differing- -- ╵- -- "_23"- -- ▲- -- @- separatorText :: Maybe Text,- -- | Wrapping text to multiple lines if they are longer than the provided length.- -- This is useful in combination with [terminal-size](https://hackage.haskell.org/package/terminal-size).- --- -- @- -- Diff.pretty def { Diff.wrapping = Diff.Wrap 6 } "0900000000" "9000000000"- -- @- --- -- Will create a string that looks like this:- --- -- @- -- ▼- -- "09000- -- 00000"- -- ╷- -- │- -- ╵- -- "90000- -- 00000"- -- ▲- -- @- wrapping :: Wrapping- }+data Config = Config+ { -- | Text that gets displayed inbetween the diffed values+ --+ -- @+ -- Diff.pretty def { Diff.separatorText = "differing" } "1234" "_23"+ -- @+ --+ -- Will create a string that looks like this:+ --+ -- @+ -- ▼ ▼+ -- "1234"+ -- ╷+ -- │ differing+ -- ╵+ -- "_23"+ -- ▲+ -- @+ separatorText :: Maybe Text,+ -- | Wrapping text to multiple lines if they are longer than the provided length.+ -- This is useful in combination with [terminal-size](https://hackage.haskell.org/package/terminal-size).+ --+ -- @+ -- Diff.pretty def { Diff.wrapping = Diff.Wrap 6 } "0900000000" "9000000000"+ -- @+ --+ -- Will create a string that looks like this:+ --+ -- @+ -- ▼+ -- "09000+ -- 00000"+ -- ╷+ -- │+ -- ╵+ -- "90000+ -- 00000"+ -- ▲+ -- @+ wrapping :: Wrapping+ } instance Default Config where def = Config {separatorText = Nothing, wrapping = NoWrap}@@ -96,31 +97,53 @@ = Wrap Int | NoWrap +-- | Define how much context surrounding diffs you'd like to show.+data Context+ = FullContext+ | Surrounding Int Text+ -- | Printing a full diff of both values separated by some pipes.-pretty :: Show a => Config -> a -> a -> Text-pretty Config {separatorText, wrapping} x y =- [ above wrapping x y,+pretty :: Config -> Text -> Text -> Text+pretty config x y =+ prettyMultilines config FullContext [x] [y]++-- | Printing a full diff of both values separated by some pipes.+prettyMultilines :: Config -> Context -> [Text] -> [Text] -> Text+prettyMultilines Config {separatorText, wrapping} context xs ys =+ [ zipWith (\x y -> above' wrapping x y) xs ys & extractContext context (False, [], []) & mconcat, separator separatorText,- below wrapping x y+ zipWith (\x y -> below' wrapping x y) xs ys & extractContext context (False, [], []) & mconcat ] & mconcat -- | Printing The first value and the diff indicator above. -- -- @--- Diff.above Diff.NoWrap "1234" "_23"+-- Diff.above Diff.NoWrap Diff.FullContext "1234" "_23" -- @ -- -- @ -- ▼ ▼ -- "1234" -- @-above :: Show a => Wrapping -> a -> a -> Text+above :: Wrapping -> Text -> Text -> Text above wrapping x y =- wrap wrapping [diffLine First down x y, Text.pack (show x)]- & filterEmptyLines- & Text.unlines+ let (_, res) = above' wrapping x y+ in res +above' :: Wrapping -> Text -> Text -> (Bool, Text)+above' wrapping x y =+ let diffs =+ Diff.getDiff+ (Text.unpack x)+ (Text.unpack y)+ in ( any (hasDiff First) diffs,+ withDiffLine First down diffs+ & wrap wrapping+ & filterEmptyLines+ & Text.unlines+ )+ -- | Printing The second value and the diff indicator below. -- -- @@@ -131,12 +154,24 @@ -- "_23" -- ▲ -- @-below :: Show a => Wrapping -> a -> a -> Text+below :: Wrapping -> Text -> Text -> Text below wrapping x y =- wrap wrapping [Text.pack (show y), diffLine Second up x y]- & filterEmptyLines- & Text.unlines+ let (_, res) = below' wrapping x y+ in res +below' :: Wrapping -> Text -> Text -> (Bool, Text)+below' wrapping x y =+ let diffs =+ Diff.getDiff+ (Text.unpack x)+ (Text.unpack y)+ in ( any (hasDiff Second) diffs,+ withDiffLine Second up diffs+ & wrap wrapping+ & filterEmptyLines+ & Text.unlines+ )+ wrap :: Wrapping -> [Text] -> [Text] wrap wrapping text = case wrapping of@@ -154,25 +189,77 @@ data Position = First | Second -diffLine :: Show a => Position -> Char -> a -> a -> Text.Text-diffLine pos differ a b =- Diff.getDiff- (show a)- (show b)- & mapMaybe (toDiffLine pos differ)- & Text.pack- & Text.stripEnd+withDiffLine :: Position -> Char -> [Diff.Diff Char] -> [Text]+withDiffLine pos differ diffs =+ let (content, indicators) =+ diffs+ & mapMaybe (toDiffLine pos differ)+ & unzip+ in case pos of+ First -> [Text.pack indicators & Text.stripEnd, Text.pack content & Text.stripEnd]+ Second -> [Text.pack content & Text.stripEnd, Text.pack indicators & Text.stripEnd] -toDiffLine :: Position -> Char -> Diff.Diff a -> Maybe Char+toDiffLine :: Position -> Char -> Diff.Diff Char -> Maybe (Char, Char) toDiffLine pos c d = case d of- Diff.First _ -> case pos of- First -> Just c+ Diff.First x -> case pos of+ First -> Just (x, c) Second -> Nothing- Diff.Second _ -> case pos of+ Diff.Second x -> case pos of First -> Nothing- Second -> Just c- Diff.Both _ _ -> Just ' '+ Second -> Just (x, c)+ Diff.Both x _ -> Just (x, ' ')++extractContext :: Context -> (Bool, [Text], [Text]) -> [(Bool, Text)] -> [Text]+extractContext FullContext _ xs = map (\(_, a) -> a) xs+extractContext context@(Surrounding c sep) (hadDiff, acc, before) xs =+ case xs of+ [] ->+ if length before <= c+ then acc ++ before+ else acc ++ take c before ++ [sep, "\n"]+ (True, x) : rest ->+ extractContext+ context+ ( True,+ acc ++ splitSurrounding c sep hadDiff before ++ [x],+ []+ )+ rest+ (False, x) : rest ->+ extractContext+ context+ ( hadDiff,+ acc,+ before ++ [x]+ )+ rest++splitSurrounding :: Int -> Text -> Bool -> [Text] -> [Text]+splitSurrounding n sep hadDiff xs =+ if hadDiff+ then+ if length xs <= n * 2+ then xs+ else take n xs ++ [sep, "\n"] ++ takeRight n xs+ else+ if length xs <= n+ then xs+ else [sep, "\n"] ++ takeRight n xs++takeRight :: Int -> [a] -> [a]+takeRight i xs = reverse (take i (reverse xs))++hasDiff :: Position -> Diff.Diff Char -> Bool+hasDiff pos d =+ case d of+ Diff.First _ -> case pos of+ First -> True+ Second -> False+ Diff.Second x -> case pos of+ First -> False+ Second -> True+ Diff.Both x _ -> False separator :: Maybe Text -> Text separator maybeComparison =
test/Main.hs view
@@ -22,134 +22,224 @@ tests = testGroup "Pretty.Diff"- [ testCase "Comparing two equal strings" $- expectDiffToEqual- ( Diff.pretty- def- "Hello"- "Hello"- )- [ "Hello" & showText,- "╷",- "│",- "╵",- "Hello" & showText- ],- testCase "Removed characters" $- expectDiffToEqual- ( Diff.pretty- def- "Hello"- "Hel"- )- [ " ▼▼" & forEscapedString,- "Hello" & showText,- "╷",- "│",- "╵",- "Hel" & showText- ],- testCase "Added characters" $- expectDiffToEqual- ( Diff.pretty- def- "Hel"- "Hello"- )- [ "Hel" & showText,- "╷",- "│",- "╵",- "Hello" & showText,- " ▲▲" & forEscapedString- ],- testCase "Changed characters" $- expectDiffToEqual- ( Diff.pretty- def- "Axxx"- "Bxxx"- )- [ "▼" & forEscapedString,- "Axxx" & showText,- "╷",- "│",- "╵",- "Bxxx" & showText,- "▲" & forEscapedString- ],- testCase "Mixed changes" $- expectDiffToEqual- ( Diff.pretty- def- "12345"- "1004"- )- [ " ▼▼ ▼" & forEscapedString,- "12345" & showText,- "╷",- "│",- "╵",- "1004" & showText,- " ▲▲" & forEscapedString- ],- testCase "Multiline changes (on first and last line)" $- expectDiffToEqual- ( Diff.pretty- def- { Diff.wrapping = Diff.Wrap $ 5 + 1 -- + 1 because of "- }- "0900000000"- "9000000000"- )- [ "▼" & forEscapedString,- "09000" & showTextPrefix,- "00000" & showTextPostfix,- "╷",- "│",- "╵",- "90000" & showTextPrefix,- "00000" & showTextPostfix,- " ▲"- ],- testCase "Multiline changes (inbetween)" $+ [ testGroup+ "pretty"+ [ testCase "Comparing two equal strings" $+ expectDiffToEqual+ ( Diff.pretty+ def+ "Hello"+ "Hello"+ )+ [ "Hello",+ "╷",+ "│",+ "╵",+ "Hello"+ ],+ testCase "Removed characters" $+ expectDiffToEqual+ ( Diff.pretty+ def+ "Hello"+ "Hel"+ )+ [ " ▼▼",+ "Hello",+ "╷",+ "│",+ "╵",+ "Hel"+ ],+ testCase "Added characters" $+ expectDiffToEqual+ ( Diff.pretty+ def+ "Hel"+ "Hello"+ )+ [ "Hel",+ "╷",+ "│",+ "╵",+ "Hello",+ " ▲▲"+ ],+ testCase "Changed characters" $+ expectDiffToEqual+ ( Diff.pretty+ def+ "Axxx"+ "Bxxx"+ )+ [ "▼",+ "Axxx",+ "╷",+ "│",+ "╵",+ "Bxxx",+ "▲"+ ],+ testCase "Mixed changes" $+ expectDiffToEqual+ ( Diff.pretty+ def+ "12345"+ "1004"+ )+ [ " ▼▼ ▼",+ "12345",+ "╷",+ "│",+ "╵",+ "1004",+ " ▲▲"+ ],+ testCase "Multiline changes (on first and last line)" $+ expectDiffToEqual+ ( Diff.pretty+ def {Diff.wrapping = Diff.Wrap 5}+ "0900000000"+ "9000000000"+ )+ [ "▼",+ "09000",+ "00000",+ "╷",+ "│",+ "╵",+ "90000",+ "00000",+ " ▲"+ ],+ testCase "Multiline changes (inbetween)" $+ expectDiffToEqual+ ( Diff.pretty+ def+ { Diff.wrapping = Diff.Wrap 5+ }+ "0000090000"+ "0090000000"+ )+ [ "00000",+ "▼",+ "90000",+ "╷",+ "│",+ "╵",+ "00900",+ " ▲",+ "00000"+ ],+ testCase "With separator text" $+ expectDiffToEqual+ ( Diff.pretty+ Diff.Config+ { Diff.separatorText = Just "equals",+ Diff.wrapping = Diff.Wrap 5+ }+ "0000090000"+ "0090000000"+ )+ [ "00000",+ "▼",+ "90000",+ "╷",+ "│ equals",+ "╵",+ "00900",+ " ▲",+ "00000"+ ]+ ],+ testGroup+ "prettyMultilines"+ [ testCase "Multiline content with full context" $+ expectDiffToEqual+ ( Diff.prettyMultilines+ def+ Diff.FullContext+ ["a", "b", "c", "d", "e", "f"]+ ["a", "b", "c", "D", "e", "f"]+ )+ [ "a",+ "b",+ "c",+ "▼",+ "d",+ "e",+ "f",+ "╷",+ "│",+ "╵",+ "a",+ "b",+ "c",+ "D",+ "▲",+ "e",+ "f"+ ]+ ],+ testCase "Multiline content with narrow context" $ expectDiffToEqual- ( Diff.pretty+ ( Diff.prettyMultilines def- { Diff.wrapping = Diff.Wrap $ 5 + 1 -- + 1 because of "- }- "0000090000"- "0090000000"+ (Diff.Surrounding 1 "...")+ ["a", "b", "c", "d", "e", "f"]+ ["a", "b", "c", "D", "e", "f"] )- [ "00000" & showTextPrefix,+ [ "...",+ "c", "▼",- "90000" & showTextPostfix,+ "d",+ "e",+ "...", "╷", "│", "╵",- "00900" & showTextPrefix,- " ▲" & forEscapedString,- "00000" & showTextPostfix+ "...",+ "c",+ "D",+ "▲",+ "e",+ "..." ],- testCase "With separator text" $+ testCase "Multiline content with narrow context and multiple diffs" $ expectDiffToEqual- ( Diff.pretty- Diff.Config- { Diff.separatorText = Just "equals",- Diff.wrapping = Diff.Wrap $ 5 + 1 -- + 1 because of "- }- "0000090000"- "0090000000"+ ( Diff.prettyMultilines+ def {Diff.wrapping = Diff.Wrap 3}+ (Diff.Surrounding 1 "...")+ ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]+ ["A", "b", "c", "D", "e", "f", "g", "h", "i", "JJJJJJ"] )- [ "00000" & showTextPrefix,+ [ "▼",+ "a",+ "b",+ "c", "▼",- "90000" & showTextPostfix,+ "d",+ "e",+ "...",+ "i",+ "▼",+ "j", "╷",- "│ equals",+ "│", "╵",- "00900" & showTextPrefix,- " ▲" & forEscapedString,- "00000" & showTextPostfix+ "A",+ "▲",+ "b",+ "c",+ "D",+ "▲",+ "e",+ "...",+ "i",+ "JJJ",+ "▲▲▲",+ "JJJ",+ "▲▲▲" ] ] @@ -163,11 +253,3 @@ Text.IO.putStrLn expected throw e )--forEscapedString x = " " <> x--showText = Text.pack . show--showTextPrefix x = "\"" <> x--showTextPostfix x = x <> "\""