packages feed

errata-0.4.0.0: example/Main.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications  #-}

module Main
    where

import           Data.List (intersperse)
import qualified Data.Text as T
import qualified Data.Text.Lazy.IO as TL
import           Errata
import           Errata.Styles

-------------------------------------------------------------
-- Definitions from the Haddock example for 'prettyErrors' --
-------------------------------------------------------------

data ParseError = ParseError
    { peFile       :: FilePath
    , peLine       :: Int
    , peCol        :: Int
    , peUnexpected :: T.Text
    , peExpected   :: [T.Text]
    }

toErrata :: ParseError -> Errata
toErrata (ParseError fp l c unexpected expected) =
    errataSimple
        (Just "an error occured!")
        (blockSimple basicStyle basicPointer fp
            (Just "error: invalid syntax")
            (l, c, c + T.length unexpected, Just "this one")
            (Just $ "unexpected " <> unexpected <> "\nexpected " <> T.intercalate ", " expected))
        Nothing

printErrors :: T.Text -> [ParseError] -> IO ()
printErrors source es = TL.putStrLn $ prettyErrors source (toErrata <$> es)

--------------
-- Examples --
--------------

-- | From the Haddock for 'prettyErrors'.
jsonExample :: IO ()
jsonExample = printErrors
    "{\n    \"bad\": [1, 2,]\n    }"
    [ParseError "./comma.json" 2 18 "]" ["null", "true", "false", "\"", "-", "digit", "[", "{"]]

-- | From the README.
foldExample :: IO ()
foldExample = TL.putStrLn $ prettyErrors @String
    "sum xs = fold (+) 0 xs"
    [ Errata
        (Just "\x1b[31m─────── NAME UNKNOWN ───────\x1b[0m\n\nThe name \x1b[31mfold\x1b[0m was not found.\n")
        [ Block
            fancyRedStyle
            ("file.hs", 1, 10)
            Nothing
            [Pointer 1 10 14 False Nothing fancyRedPointer]
            Nothing
        ]
        (Just "\nDid you mean to use one of these?\n\n    \x1b[31mfoldl\x1b[0m\n    \x1b[31mfoldr\x1b[0m")
    ]

-- | The fold example with no decorations and padding.
foldNoDecorExample :: IO ()
foldNoDecorExample = TL.putStrLn $ prettyErrors @String
    "sum xs = fold (+) 0 xs"
    [ Errata
        (Just "\x1b[31m─────── NAME UNKNOWN ───────\x1b[0m\n\nThe name \x1b[31mfold\x1b[0m was not found.\n")
        [ Block
            (fancyRedStyle { styleEnableDecorations = False, stylePaddingTop = False })
            ("file.hs", 1, 10)
            Nothing
            [Pointer 1 10 14 False Nothing fancyRedPointer]
            Nothing
        ]
        (Just "\nDid you mean to use one of these?\n\n    \x1b[31mfoldl\x1b[0m\n    \x1b[31mfoldr\x1b[0m")
    ]

-- | From the README.
ifExample :: IO ()
ifExample = TL.putStrLn $ prettyErrors @String
    "foo = if 1 > 2\n    then 100\n    else \"uh oh\""
    [ Errata
        (Just "\x1b[31merror[E001]: mismatching types in `if` expression\x1b[0m")
        [ Block
            fancyRedStyle
            ("file.hs", 3, 10)
            Nothing
            [ Pointer 2 10 13 False (Just "\x1b[31mthis has type `Int`\x1b[0m") fancyRedPointer
            , Pointer 3 10 17 False (Just "\x1b[31mbut this has type `String`\x1b[0m") fancyRedPointer
            ]
            Nothing
        , Block
            fancyYellowStyle
            ("file.hs", 1, 7)
            Nothing
            [ Pointer 1 7 9 True Nothing fancyYellowPointer
            , Pointer 2 5 9 True Nothing fancyYellowPointer
            , Pointer 3 5 9 True (Just "\x1b[33min this `if` expression\x1b[0m") fancyYellowPointer
            ]
            Nothing
        ]
        (Just "\n\x1b[33mnote: use --explain E001 to learn more\x1b[0m")
    ]

-- | The if example with a mixed-colors block.
ifExample' :: IO ()
ifExample' = TL.putStrLn $ prettyErrors @String
    "foo = if 1 > 2\n    then 100\n    else \"uh oh\""
    [ Errata
        (Just "\x1b[31merror[E001]: mismatching types in `if` expression\x1b[0m")
        [ Block
            fancyYellowStyle
            ("file.hs", 3, 10)
            Nothing
            [ Pointer 2 10 13 False (Just "\x1b[31mthis has type `Int`\x1b[0m") fancyRedPointer
            , Pointer 3 10 17 False (Just "\x1b[31mbut this has type `String`\x1b[0m") fancyRedPointer
            , Pointer 1 7 9 True Nothing (fancyYellowPointer { styleUnderline = "\x1b[33m-\x1b[0m" })
            , Pointer 2 5 9 True Nothing (fancyYellowPointer { styleUnderline = "\x1b[33m-\x1b[0m" })
            , Pointer 3 5 9 True (Just "\x1b[33min this `if` expression\x1b[0m") (fancyYellowPointer { styleUnderline = "\x1b[33m-\x1b[0m" })
            ]
            Nothing
        ]
        (Just "\n\x1b[33mnote: use --explain E001 to learn more\x1b[0m")
    ]

-- | From the documentation for premade styles.
stylesExample :: Style -> PointerStyle -> IO ()
stylesExample style pstyle = TL.putStrLn $ prettyErrors @String
    "line 1 foo bar do\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8 baz end"
    [ Errata
        (Just "error header message")
        [ Block
            style
            ("file.ext", 1, 16)
            (Just "block header message")
            [ Pointer 1 16 18 True (Just "start label") pstyle
            , Pointer 2 6 7 False (Just "unconnected label") pstyle
            , Pointer 3 6 7 True (Just "middle label") pstyle
            , Pointer 8 6 7 True (Just "inner label") pstyle
            , Pointer 8 12 15 True (Just "end label") pstyle
            ]
            (Just "block body message")
        ]
        (Just "error body message")
    ]

-- | From a test, but with colors.
noDecorExample :: IO ()
noDecorExample = TL.putStrLn $ prettyErrors @String
    "line 1 foo bar do\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8 baz end"
    [ Errata
        (Just "error header message")
        [ Block
            (fancyRedStyle { styleEnableDecorations = False, stylePaddingTop = False })
            ("file.ext", 1, 16)
            (Just "block header message")
            [ Pointer 1 16 18 True (Just "start label") fancyRedPointer
            , Pointer 2 6 7 False (Just "unconnected label") fancyRedPointer
            , Pointer 3 6 7 True (Just "middle label") fancyRedPointer
            , Pointer 8 6 7 True (Just "inner label") fancyRedPointer
            , Pointer 8 12 15 True (Just "end label") fancyRedPointer
            ]
            (Just "block body message")
        ]
        (Just "error body message")
    ]

main :: IO ()
main = sequence_ . intersperse (putStrLn "") $
    [ jsonExample
    , foldExample
    , foldNoDecorExample
    , ifExample
    , ifExample'
    , noDecorExample
    ] <> map (uncurry stylesExample $) themes
    where
        themes =
            [ (basicStyle, basicPointer)
            , (fancyStyle, fancyPointer)
            , (fancyRedStyle, fancyRedPointer)
            , (fancyYellowStyle, fancyYellowPointer)
            ]