Copyright | (c) 2020 comp |
---|---|
License | MIT |
Maintainer | onecomputer00@gmail.com |
Stability | stable |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
This module is for creating pretty error messages. We assume very little about the format you want to use, so much of this module is to allow you to customize your error messages.
To get started, see the documentation for prettyErrors
. When using this module, we recommend you turn on the
OverloadedStrings
extension and import Data.Text at the very least due to the use of Text
(strict).
The overall workflow to use the printer is to convert your error type to Errata
, which entails converting your errors
to Errata
by filling in messages and Block
s. You can create Errata
and Block
from their constructors, or use
the convenience functions for common usecases, like errataSimple
and blockSimple
.
For easier reading, we define:
type Line = Int type Column = Int type Header = Text type Body = Text type Label = Text
Synopsis
- data Errata = Errata {
- errataHeader :: Maybe Header
- errataBlock :: Block
- errataBlocks :: [Block]
- errataBody :: Maybe Body
- errataSimple :: Maybe Header -> Block -> Maybe Body -> Errata
- data Block = Block {
- blockStyle :: Style
- blockLocation :: (FilePath, Line, Column)
- blockHeader :: Maybe Header
- blockPointers :: [Pointer]
- blockBody :: Maybe Body
- blockSimple :: Style -> FilePath -> Maybe Header -> (Line, Column, Column, Maybe Label) -> Maybe Body -> Block
- blockSimple' :: Style -> FilePath -> Maybe Header -> (Line, Column, Maybe Label) -> Maybe Body -> Block
- blockConnected :: Style -> FilePath -> Maybe Header -> (Line, Column, Column, Maybe Label) -> (Line, Column, Column, Maybe Label) -> Maybe Body -> Block
- blockConnected' :: Style -> FilePath -> Maybe Header -> (Line, Column, Maybe Label) -> (Line, Column, Maybe Label) -> Maybe Body -> Block
- blockMerged :: Style -> FilePath -> Maybe Header -> (Line, Column, Column, Maybe Label) -> (Line, Column, Column, Maybe Label) -> Maybe Label -> Maybe Body -> Block
- blockMerged' :: Style -> FilePath -> Maybe Header -> (Line, Column, Maybe Label) -> (Line, Column, Maybe Label) -> Maybe Label -> Maybe Body -> Block
- data Pointer = Pointer {}
- data Style = Style {
- styleLocation :: (FilePath, Line, Column) -> Text
- styleNumber :: Line -> Text
- styleLine :: [(Column, Column)] -> Text -> Text
- styleEllipsis :: Text
- styleLinePrefix :: Text
- styleUnderline :: Text
- styleVertical :: Text
- styleHorizontal :: Text
- styleDownRight :: Text
- styleUpRight :: Text
- styleUpDownRight :: Text
- basicStyle :: Style
- fancyStyle :: Style
- fancyRedStyle :: Style
- fancyYellowStyle :: Style
- prettyErrors :: Source source => source -> [Errata] -> Text
- prettyErrorsNE :: Source source => source -> NonEmpty Errata -> Text
Error format data
A collection of information for pretty printing an error.
Errata | |
|
Creates a simple error that has a single block, with an optional header or body.
Blocks and pointers
Information about a block in the source code, such as pointers and messages.
Each block has a style associated with it.
Block | |
|
:: Style | The style of the pointer. |
-> FilePath | The filepath. |
-> Maybe Header | The header message. |
-> (Line, Column, Column, Maybe Label) | The line number and column span, starting at 1, and a label. |
-> Maybe Body | The body message. |
-> Block |
A simple block that points to only one line and optionally has a label, header, or body message.
:: Style | The style of the pointer. |
-> FilePath | The filepath. |
-> Maybe Header | The header message. |
-> (Line, Column, Maybe Label) | The line number and column, starting at 1, and a label. |
-> Maybe Body | The body message. |
-> Block |
A variant of blockSimple
that only points at one column.
:: Style | The style of the pointer. |
-> FilePath | The filepath. |
-> Maybe Header | The header message. |
-> (Line, Column, Column, Maybe Label) | The first line number and column span, starting at 1, and a label. |
-> (Line, Column, Column, Maybe Label) | The second line number and column span, starting at 1, and a label. |
-> Maybe Body | The body message. |
-> Block |
A block that points to two parts of the source that are visually connected together.
:: Style | The style of the pointer. |
-> FilePath | The filepath. |
-> Maybe Header | The header message. |
-> (Line, Column, Maybe Label) | The first line number and column, starting at 1, and a label. |
-> (Line, Column, Maybe Label) | The second line number and column, starting at 1, and a label. |
-> Maybe Body | The body message. |
-> Block |
A variant of blockConnected
where the pointers point at only one column.
:: Style | The style of the pointer. |
-> FilePath | The filepath. |
-> Maybe Header | The header message. |
-> (Line, Column, Column, Maybe Label) | The first line number and column span, starting at 1, and a label. |
-> (Line, Column, Column, Maybe Label) | The second line number and column span, starting at 1, and a label. |
-> Maybe Label | The label for when the two pointers are merged into one. |
-> Maybe Body | The body message. |
-> Block |
A block that points to two parts of the source that are visually connected together.
If the two parts of the source happen to be on the same line, the pointers are merged into one.
:: Style | The style of the pointer. |
-> FilePath | The filepath. |
-> Maybe Header | The header message. |
-> (Line, Column, Maybe Label) | The first line number and column, starting at 1, and a label. |
-> (Line, Column, Maybe Label) | The second line number and column, starting at 1, and a label. |
-> Maybe Label | The label for when the two pointers are merged into one. |
-> Maybe Body | The body message. |
-> Block |
A variant of blockMerged
where the pointers point at only one column.
A pointer is the span of the source code at a line, from one column to another. Each of the positions start at 1.
A pointer may also have a label that will display inline.
A pointer may also be connected to all the other pointers within the same block.
Pointer | |
|
Styling options
Stylization options for a block, e.g. characters to use.
Style | |
|
basicStyle :: Style Source #
A basic style using only ASCII characters.
Errors should look like so:
error header message --> file.ext:1:16 block header message | 1 | line 1 foo bar do | ________________^^ start label 2 | | line 2 | | ^ unconnected label 3 | | line 3 . | |______^ middle label 6 | | line 6 7 | | line 7 baz end | |______^_____^^^ end label | | | | inner label block body message error body message
fancyStyle :: Style Source #
A fancy style using Unicode characters.
Errors should look like so:
error header message → file.ext:1:16 block header message │ 1 │ line 1 foo bar do │ ┌────────────────^^ start label 2 │ │ line 2 │ │ ^ unconnected label 3 │ │ line 3 . │ ├──────^ middle label 6 │ │ line 6 7 │ │ line 7 baz end │ └──────^─────^^^ end label │ │ │ └ inner label block body message error body message
fancyRedStyle :: Style Source #
A fancy style using Unicode characters and ANSI colors, similar to fancyStyle
. Most things are colored red.
fancyYellowStyle :: Style Source #
A fancy style using Unicode characters and ANSI colors, similar to fancyStyle
. Most things are colored yellow.
Pretty printer
prettyErrors :: Source source => source -> [Errata] -> Text Source #
Pretty prints errors. The original source is required. Returns Text
(lazy). If the list is empty,
an empty string is returned.
Suppose we had an error of this type:
data ParseError = ParseError { peFile :: FilePath , peLine :: Int , peCol :: Int , peUnexpected :: T.Text , peExpected :: [T.Text] }
Then we can create a simple pretty printer like so:
import qualified Data.List.NonEmpty as N import qualified Data.Text as T import qualified Data.Text.Lazy.IO as TL import Errata toErrata :: ParseError ->Errata
toErrata (ParseError fp l c unexpected expected) =errataSimple
(Just "an error occured!") (blockSimple
basicStyle
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)
Note that in the above example, we have OverloadedStrings
enabled to reduce uses of pack
.
An example error message from this might be:
an error occured! --> ./comma.json:2:18 error: invalid syntax | 2 | "bad": [1, 2,] | ^ this one unexpected ] expected null, true, false, ", -, digit, [, {
prettyErrorsNE :: Source source => source -> NonEmpty Errata -> Text Source #
A variant of prettyErrors
for non-empty lists. You can ensure the output is never an empty string.