errata-0.2.0.0: Source code error pretty printing
Copyright(c) 2020 comp
LicenseMIT
Maintaineronecomputer00@gmail.com
Stabilitystable
Portabilityportable
Safe HaskellNone
LanguageHaskell2010

Errata

Description

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 Blocks. 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

Error format data

data Errata Source #

A collection of information for pretty printing an error.

Constructors

Errata 

Fields

errataSimple Source #

Arguments

:: Maybe Header

The header.

-> Block

The block.

-> Maybe Body

The body.

-> Errata 

Creates a simple error that has a single block, with an optional header or body.

Blocks and pointers

data Block Source #

Information about a block in the source code, such as pointers and messages.

Each block has a style associated with it.

Constructors

Block 

Fields

  • blockStyle :: Style

    The style of the block.

  • blockLocation :: (FilePath, Line, Column)

    The filepath, line, and column of the block. These start at 1.

    This is used for sorting errors, as well as to create the text that details the location.

  • blockHeader :: Maybe Header

    The header message for the block. This will appear below the location and above the source lines.

  • blockPointers :: [Pointer]

    The block's pointers. These are used to "point out" parts of the source code in this block.

    The locations of each of these pointers must be non-overlapping. If the pointers are touching at a boundary however, that is allowed.

  • blockBody :: Maybe Body

    The body message for the block. This will appear below the source lines.

blockSimple Source #

Arguments

:: 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.

blockSimple' Source #

Arguments

:: 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.

blockConnected Source #

Arguments

:: 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.

blockConnected' Source #

Arguments

:: 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.

blockMerged Source #

Arguments

:: 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.

blockMerged' Source #

Arguments

:: 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.

data Pointer Source #

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.

Constructors

Pointer 

Fields

Styling options

data Style Source #

Stylization options for a block, e.g. characters to use.

Constructors

Style 

Fields

  • styleLocation :: (FilePath, Line, Column) -> Text

    Shows the location of a block at a file, line, and column.

  • styleNumber :: Line -> Text

    Shows the line number n for a source line. The result should visually be the same length as just show n.

  • styleLine :: [(Column, Column)] -> Text -> Text

    Stylize a source line.

    Column pointers of the text that are being underlined are given for highlighting purposes. The result of this should visually take up the same space as the original line.

  • styleEllipsis :: Text

    The text to use as an ellipsis in the position of line numbers for when lines are omitted. This should visually be one character.

  • styleLinePrefix :: Text

    The prefix before the source lines.

  • styleUnderline :: Text

    The text to underline a character in a pointer. This should visually be one character.

  • styleVertical :: Text

    The text to use as a vertical bar when connecting pointers. This should visually be one character.

  • styleHorizontal :: Text

    The text to use as a horizontal bar when connecting pointers. This should visually be one character.

  • styleDownRight :: Text

    The text to use as a connector downwards and rightwards when connecting pointers. This should visually be one character.

  • styleUpRight :: Text

    The text to use as a connector upwards and rightwards when connecting pointers. This should visually be one character.

  • styleUpDownRight :: Text

    The text to use as a connector upwards, downwards, and rightwards when connecting pointers. This should visually be one character.

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.