module Test.DocTest.Internal.Runner.Example (
  Result (..)
, mkResult
) where

import           Data.Char
import           Data.List

import           Test.DocTest.Internal.Util
import           Test.DocTest.Internal.Parse

maxBy :: (Ord a) => (b -> a) -> b -> b -> b
maxBy :: forall a b. Ord a => (b -> a) -> b -> b -> b
maxBy b -> a
f b
x b
y = case a -> a -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (b -> a
f b
x) (b -> a
f b
y) of
  Ordering
LT -> b
y
  Ordering
EQ -> b
x
  Ordering
GT -> b
x

data Result = Equal | NotEqual [String]
  deriving (Result -> Result -> Bool
(Result -> Result -> Bool)
-> (Result -> Result -> Bool) -> Eq Result
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Result -> Result -> Bool
$c/= :: Result -> Result -> Bool
== :: Result -> Result -> Bool
$c== :: Result -> Result -> Bool
Eq, Int -> Result -> ShowS
[Result] -> ShowS
Result -> String
(Int -> Result -> ShowS)
-> (Result -> String) -> ([Result] -> ShowS) -> Show Result
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Result] -> ShowS
$cshowList :: [Result] -> ShowS
show :: Result -> String
$cshow :: Result -> String
showsPrec :: Int -> Result -> ShowS
$cshowsPrec :: Int -> Result -> ShowS
Show)

mkResult :: ExpectedResult -> [String] -> Result
mkResult :: ExpectedResult -> [String] -> Result
mkResult ExpectedResult
expected_ [String]
actual_ =
  case ExpectedResult
expected ExpectedResult -> [String] -> Match LinesDivergence
`matches` [String]
actual of
  Match LinesDivergence
Full            -> Result
Equal
  Partial LinesDivergence
partial -> [String] -> Result
NotEqual (ExpectedResult -> [String] -> LinesDivergence -> [String]
formatNotEqual ExpectedResult
expected [String]
actual LinesDivergence
partial)
  where
    -- use show to escape special characters in output lines if any output line
    -- contains any unsafe character
    escapeOutput :: ShowS
escapeOutput
      | (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isSafe) (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String]
expectedAsString [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
actual_) = ShowS
forall a. [a] -> [a]
init ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
forall a. [a] -> [a]
tail ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
forall a. Show a => a -> String
show ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
stripEnd
      | Bool
otherwise = ShowS
forall a. a -> a
id

    actual :: [String]
    actual :: [String]
actual = ShowS -> [String] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ShowS
escapeOutput [String]
actual_

    expected :: ExpectedResult
    expected :: ExpectedResult
expected = (ExpectedLine -> ExpectedLine) -> ExpectedResult -> ExpectedResult
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (ShowS -> ExpectedLine -> ExpectedLine
transformExcpectedLine ShowS
escapeOutput) ExpectedResult
expected_

    expectedAsString :: [String]
    expectedAsString :: [String]
expectedAsString = (ExpectedLine -> String) -> ExpectedResult -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\ExpectedLine
x -> case ExpectedLine
x of
        ExpectedLine [LineChunk]
str -> (LineChunk -> String) -> [LineChunk] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap LineChunk -> String
lineChunkToString [LineChunk]
str
        ExpectedLine
WildCardLine -> String
"..." ) ExpectedResult
expected_

    isSafe :: Char -> Bool
    isSafe :: Char -> Bool
isSafe Char
c = Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' ' Bool -> Bool -> Bool
|| (Char -> Bool
isPrint Char
c Bool -> Bool -> Bool
&& (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isSpace) Char
c)

    chunksMatch :: [LineChunk] -> String -> Match ChunksDivergence
    chunksMatch :: [LineChunk] -> String -> Match ChunksDivergence
chunksMatch [] String
"" = Match ChunksDivergence
forall a. Match a
Full
    chunksMatch [LineChunk String
xs] String
ys =
      if ShowS
stripEnd String
xs String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== ShowS
stripEnd String
ys
      then Match ChunksDivergence
forall a. Match a
Full
      else ChunksDivergence -> Match ChunksDivergence
forall a. a -> Match a
Partial (ChunksDivergence -> Match ChunksDivergence)
-> ChunksDivergence -> Match ChunksDivergence
forall a b. (a -> b) -> a -> b
$ String -> String -> ChunksDivergence
matchingPrefix String
xs String
ys
    chunksMatch (LineChunk String
x : [LineChunk]
xs) String
ys =
      if String
x String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
ys
      then (ChunksDivergence -> ChunksDivergence)
-> Match ChunksDivergence -> Match ChunksDivergence
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (String -> ChunksDivergence -> ChunksDivergence
prependText String
x) (Match ChunksDivergence -> Match ChunksDivergence)
-> Match ChunksDivergence -> Match ChunksDivergence
forall a b. (a -> b) -> a -> b
$ ([LineChunk]
xs [LineChunk] -> String -> Match ChunksDivergence
`chunksMatch` Int -> ShowS
forall a. Int -> [a] -> [a]
drop (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
x) String
ys)
      else ChunksDivergence -> Match ChunksDivergence
forall a. a -> Match a
Partial (ChunksDivergence -> Match ChunksDivergence)
-> ChunksDivergence -> Match ChunksDivergence
forall a b. (a -> b) -> a -> b
$ String -> String -> ChunksDivergence
matchingPrefix String
x String
ys
    chunksMatch zs :: [LineChunk]
zs@(LineChunk
WildCardChunk : [LineChunk]
xs) (Char
_:String
ys) =
      -- Prefer longer matches.
      (ChunksDivergence -> ChunksDivergence)
-> Match ChunksDivergence -> Match ChunksDivergence
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ChunksDivergence -> ChunksDivergence
prependWildcard (Match ChunksDivergence -> Match ChunksDivergence)
-> Match ChunksDivergence -> Match ChunksDivergence
forall a b. (a -> b) -> a -> b
$ (Match ChunksDivergence -> Match Int)
-> Match ChunksDivergence
-> Match ChunksDivergence
-> Match ChunksDivergence
forall a b. Ord a => (b -> a) -> b -> b -> b
maxBy
        ((ChunksDivergence -> Int) -> Match ChunksDivergence -> Match Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((ChunksDivergence -> Int) -> Match ChunksDivergence -> Match Int)
-> (ChunksDivergence -> Int) -> Match ChunksDivergence -> Match Int
forall a b. (a -> b) -> a -> b
$ String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (String -> Int)
-> (ChunksDivergence -> String) -> ChunksDivergence -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ChunksDivergence -> String
matchText)
        ([LineChunk] -> String -> Match ChunksDivergence
chunksMatch [LineChunk]
xs String
ys)
        ([LineChunk] -> String -> Match ChunksDivergence
chunksMatch [LineChunk]
zs String
ys)
    chunksMatch [LineChunk
WildCardChunk] [] = Match ChunksDivergence
forall a. Match a
Full
    chunksMatch (LineChunk
WildCardChunk:[LineChunk]
_) [] = ChunksDivergence -> Match ChunksDivergence
forall a. a -> Match a
Partial (String -> String -> ChunksDivergence
ChunksDivergence String
"" String
"")
    chunksMatch [] (Char
_:String
_) = ChunksDivergence -> Match ChunksDivergence
forall a. a -> Match a
Partial (String -> String -> ChunksDivergence
ChunksDivergence String
"" String
"")

    matchingPrefix :: String -> String -> ChunksDivergence
matchingPrefix String
xs String
ys =
      let common :: String
common = ((Char, Char) -> Char) -> [(Char, Char)] -> String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Char, Char) -> Char
forall a b. (a, b) -> a
fst (((Char, Char) -> Bool) -> [(Char, Char)] -> [(Char, Char)]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (\(Char
x, Char
y) -> Char
x Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
y) (String
xs String -> String -> [(Char, Char)]
forall a b. [a] -> [b] -> [(a, b)]
`zip` String
ys)) in
      String -> String -> ChunksDivergence
ChunksDivergence String
common String
common

    matches :: ExpectedResult -> [String] -> Match LinesDivergence
    matches :: ExpectedResult -> [String] -> Match LinesDivergence
matches (ExpectedLine [LineChunk]
x : ExpectedResult
xs) (String
y : [String]
ys) =
      case [LineChunk]
x [LineChunk] -> String -> Match ChunksDivergence
`chunksMatch` String
y of
      Match ChunksDivergence
Full -> (LinesDivergence -> LinesDivergence)
-> Match LinesDivergence -> Match LinesDivergence
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap LinesDivergence -> LinesDivergence
incLineNo (Match LinesDivergence -> Match LinesDivergence)
-> Match LinesDivergence -> Match LinesDivergence
forall a b. (a -> b) -> a -> b
$ ExpectedResult
xs ExpectedResult -> [String] -> Match LinesDivergence
`matches` [String]
ys
      Partial ChunksDivergence
partial -> LinesDivergence -> Match LinesDivergence
forall a. a -> Match a
Partial (Int -> String -> LinesDivergence
LinesDivergence Int
1 (ChunksDivergence -> String
expandedWildcards ChunksDivergence
partial))
    matches zs :: ExpectedResult
zs@(ExpectedLine
WildCardLine : ExpectedResult
xs) us :: [String]
us@(String
_ : [String]
ys) =
      -- Prefer longer matches, and later ones of equal length.
      let matchWithoutWC :: Match LinesDivergence
matchWithoutWC = ExpectedResult
xs ExpectedResult -> [String] -> Match LinesDivergence
`matches` [String]
us in
      let matchWithWC :: Match LinesDivergence
matchWithWC    = (LinesDivergence -> LinesDivergence)
-> Match LinesDivergence -> Match LinesDivergence
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap LinesDivergence -> LinesDivergence
incLineNo (ExpectedResult
zs ExpectedResult -> [String] -> Match LinesDivergence
`matches` [String]
ys) in
      let key :: LinesDivergence -> (Int, Int)
key (LinesDivergence Int
lineNo String
line) = (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
line, Int
lineNo) in
      (Match LinesDivergence -> Match (Int, Int))
-> Match LinesDivergence
-> Match LinesDivergence
-> Match LinesDivergence
forall a b. Ord a => (b -> a) -> b -> b -> b
maxBy ((LinesDivergence -> (Int, Int))
-> Match LinesDivergence -> Match (Int, Int)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap LinesDivergence -> (Int, Int)
key) Match LinesDivergence
matchWithoutWC Match LinesDivergence
matchWithWC
    matches [ExpectedLine
WildCardLine] [] = Match LinesDivergence
forall a. Match a
Full
    matches [] [] = Match LinesDivergence
forall a. Match a
Full
    matches [] [String]
_  = LinesDivergence -> Match LinesDivergence
forall a. a -> Match a
Partial (Int -> String -> LinesDivergence
LinesDivergence Int
1 String
"")
    matches ExpectedResult
_  [] = LinesDivergence -> Match LinesDivergence
forall a. a -> Match a
Partial (Int -> String -> LinesDivergence
LinesDivergence Int
1 String
"")

-- Note: order of constructors matters, so that full matches sort as
-- greater than partial.
data Match a = Partial a | Full
  deriving (Match a -> Match a -> Bool
(Match a -> Match a -> Bool)
-> (Match a -> Match a -> Bool) -> Eq (Match a)
forall a. Eq a => Match a -> Match a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Match a -> Match a -> Bool
$c/= :: forall a. Eq a => Match a -> Match a -> Bool
== :: Match a -> Match a -> Bool
$c== :: forall a. Eq a => Match a -> Match a -> Bool
Eq, Eq (Match a)
Eq (Match a)
-> (Match a -> Match a -> Ordering)
-> (Match a -> Match a -> Bool)
-> (Match a -> Match a -> Bool)
-> (Match a -> Match a -> Bool)
-> (Match a -> Match a -> Bool)
-> (Match a -> Match a -> Match a)
-> (Match a -> Match a -> Match a)
-> Ord (Match a)
Match a -> Match a -> Bool
Match a -> Match a -> Ordering
Match a -> Match a -> Match a
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall {a}. Ord a => Eq (Match a)
forall a. Ord a => Match a -> Match a -> Bool
forall a. Ord a => Match a -> Match a -> Ordering
forall a. Ord a => Match a -> Match a -> Match a
min :: Match a -> Match a -> Match a
$cmin :: forall a. Ord a => Match a -> Match a -> Match a
max :: Match a -> Match a -> Match a
$cmax :: forall a. Ord a => Match a -> Match a -> Match a
>= :: Match a -> Match a -> Bool
$c>= :: forall a. Ord a => Match a -> Match a -> Bool
> :: Match a -> Match a -> Bool
$c> :: forall a. Ord a => Match a -> Match a -> Bool
<= :: Match a -> Match a -> Bool
$c<= :: forall a. Ord a => Match a -> Match a -> Bool
< :: Match a -> Match a -> Bool
$c< :: forall a. Ord a => Match a -> Match a -> Bool
compare :: Match a -> Match a -> Ordering
$ccompare :: forall a. Ord a => Match a -> Match a -> Ordering
Ord, Int -> Match a -> ShowS
[Match a] -> ShowS
Match a -> String
(Int -> Match a -> ShowS)
-> (Match a -> String) -> ([Match a] -> ShowS) -> Show (Match a)
forall a. Show a => Int -> Match a -> ShowS
forall a. Show a => [Match a] -> ShowS
forall a. Show a => Match a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Match a] -> ShowS
$cshowList :: forall a. Show a => [Match a] -> ShowS
show :: Match a -> String
$cshow :: forall a. Show a => Match a -> String
showsPrec :: Int -> Match a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Match a -> ShowS
Show)

instance Functor Match where
  fmap :: forall a b. (a -> b) -> Match a -> Match b
fmap a -> b
f (Partial a
a) = b -> Match b
forall a. a -> Match a
Partial (a -> b
f a
a)
  fmap a -> b
_ Match a
Full = Match b
forall a. Match a
Full

data ChunksDivergence = ChunksDivergence { ChunksDivergence -> String
matchText :: String, ChunksDivergence -> String
expandedWildcards :: String }
  deriving (Int -> ChunksDivergence -> ShowS
[ChunksDivergence] -> ShowS
ChunksDivergence -> String
(Int -> ChunksDivergence -> ShowS)
-> (ChunksDivergence -> String)
-> ([ChunksDivergence] -> ShowS)
-> Show ChunksDivergence
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ChunksDivergence] -> ShowS
$cshowList :: [ChunksDivergence] -> ShowS
show :: ChunksDivergence -> String
$cshow :: ChunksDivergence -> String
showsPrec :: Int -> ChunksDivergence -> ShowS
$cshowsPrec :: Int -> ChunksDivergence -> ShowS
Show)

prependText :: String -> ChunksDivergence -> ChunksDivergence
prependText :: String -> ChunksDivergence -> ChunksDivergence
prependText String
s (ChunksDivergence String
mt String
wct) = String -> String -> ChunksDivergence
ChunksDivergence (String
sString -> ShowS
forall a. [a] -> [a] -> [a]
++String
mt) (String
sString -> ShowS
forall a. [a] -> [a] -> [a]
++String
wct)

prependWildcard :: ChunksDivergence -> ChunksDivergence
prependWildcard :: ChunksDivergence -> ChunksDivergence
prependWildcard (ChunksDivergence String
mt String
wct) = String -> String -> ChunksDivergence
ChunksDivergence String
mt (Char
'.'Char -> ShowS
forall a. a -> [a] -> [a]
:String
wct)

data LinesDivergence = LinesDivergence { LinesDivergence -> Int
_mismatchLineNo :: Int, LinesDivergence -> String
_partialLine :: String }
  deriving (Int -> LinesDivergence -> ShowS
[LinesDivergence] -> ShowS
LinesDivergence -> String
(Int -> LinesDivergence -> ShowS)
-> (LinesDivergence -> String)
-> ([LinesDivergence] -> ShowS)
-> Show LinesDivergence
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [LinesDivergence] -> ShowS
$cshowList :: [LinesDivergence] -> ShowS
show :: LinesDivergence -> String
$cshow :: LinesDivergence -> String
showsPrec :: Int -> LinesDivergence -> ShowS
$cshowsPrec :: Int -> LinesDivergence -> ShowS
Show)

incLineNo :: LinesDivergence -> LinesDivergence
incLineNo :: LinesDivergence -> LinesDivergence
incLineNo (LinesDivergence Int
lineNo String
partialLineMatch) = Int -> String -> LinesDivergence
LinesDivergence (Int
lineNo Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) String
partialLineMatch

formatNotEqual :: ExpectedResult -> [String] -> LinesDivergence -> [String]
formatNotEqual :: ExpectedResult -> [String] -> LinesDivergence -> [String]
formatNotEqual ExpectedResult
expected_ [String]
actual LinesDivergence
partial = String -> [String] -> [String]
formatLines String
"expected: " [String]
expected [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ String -> [String] -> [String]
formatLines String
" but got: " (Bool -> LinesDivergence -> [String] -> [String]
lineMarker Bool
wildcard LinesDivergence
partial [String]
actual)
  where
    expected :: [String]
    expected :: [String]
expected = (ExpectedLine -> String) -> ExpectedResult -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\ExpectedLine
x -> case ExpectedLine
x of
        ExpectedLine [LineChunk]
str -> (LineChunk -> String) -> [LineChunk] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap LineChunk -> String
lineChunkToString [LineChunk]
str
        ExpectedLine
WildCardLine -> String
"..." ) ExpectedResult
expected_

    formatLines :: String -> [String] -> [String]
    formatLines :: String -> [String] -> [String]
formatLines String
message [String]
xs = case [String]
xs of
      String
y:[String]
ys -> (String
message String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
y) String -> [String] -> [String]
forall a. a -> [a] -> [a]
: ShowS -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String
padding String -> ShowS
forall a. [a] -> [a] -> [a]
++) [String]
ys
      []   -> [String
message]
      where
        padding :: String
padding = Int -> Char -> String
forall a. Int -> a -> [a]
replicate (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
message) Char
' '

    wildcard :: Bool
    wildcard :: Bool
wildcard = (ExpectedLine -> Bool) -> ExpectedResult -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (\ExpectedLine
x -> case ExpectedLine
x of
        ExpectedLine [LineChunk]
xs -> (LineChunk -> Bool) -> [LineChunk] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (\LineChunk
y -> case LineChunk
y of { LineChunk
WildCardChunk -> Bool
True; LineChunk
_ -> Bool
False }) [LineChunk]
xs
        ExpectedLine
WildCardLine -> Bool
True ) ExpectedResult
expected_

lineChunkToString :: LineChunk -> String
lineChunkToString :: LineChunk -> String
lineChunkToString LineChunk
WildCardChunk = String
"..."
lineChunkToString (LineChunk String
str) = String
str

transformExcpectedLine :: (String -> String) -> ExpectedLine -> ExpectedLine
transformExcpectedLine :: ShowS -> ExpectedLine -> ExpectedLine
transformExcpectedLine ShowS
f (ExpectedLine [LineChunk]
xs) =
  [LineChunk] -> ExpectedLine
ExpectedLine ([LineChunk] -> ExpectedLine) -> [LineChunk] -> ExpectedLine
forall a b. (a -> b) -> a -> b
$ (LineChunk -> LineChunk) -> [LineChunk] -> [LineChunk]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\LineChunk
el -> case LineChunk
el of
    LineChunk String
s -> String -> LineChunk
LineChunk (String -> LineChunk) -> String -> LineChunk
forall a b. (a -> b) -> a -> b
$ ShowS
f String
s
    LineChunk
WildCardChunk -> LineChunk
WildCardChunk
  ) [LineChunk]
xs
transformExcpectedLine ShowS
_ ExpectedLine
WildCardLine = ExpectedLine
WildCardLine

lineMarker :: Bool -> LinesDivergence -> [String] -> [String]
lineMarker :: Bool -> LinesDivergence -> [String] -> [String]
lineMarker Bool
wildcard (LinesDivergence Int
row String
expanded) [String]
actual =
  let ([String]
pre, [String]
post) = Int -> [String] -> ([String], [String])
forall a. Int -> [a] -> ([a], [a])
splitAt Int
row [String]
actual in
  [String]
pre [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++
  [(if Bool
wildcard Bool -> Bool -> Bool
&& String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
expanded Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
30
    -- show expanded pattern if match is long, to help understanding what matched what
    then String
expanded
    else Int -> Char -> String
forall a. Int -> a -> [a]
replicate (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
expanded) Char
' ') String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"^"] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++
  [String]
post