packages feed

errata-0.4.0.0: src/Errata/Styles.hs

{-# LANGUAGE OverloadedStrings #-}

{- |
Module      : Errata.Styles
Copyright   : (c) 2020- comp
License     : MIT
Maintainer  : onecomputer00@gmail.com
Stability   : stable
Portability : portable

Premade styles for blocks and pointers.
-}
module Errata.Styles
    ( basicStyle
    , basicPointer
    , fancyStyle
    , fancyPointer
    , fancyRedStyle
    , fancyRedPointer
    , fancyYellowStyle
    , fancyYellowPointer
    , highlight
    ) where

import           Data.Bifunctor (bimap, second)
import qualified Data.Text as T
import           Errata.Types

{- | A basic style using only ASCII characters.

Errors should look like so (with 'basicPointer'):

> 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
> 4 | | line 4
> 5 | | line 5
> . | |
> 7 | | line 7
> 8 | | line 8 baz end
>   | |______^_____^^^ end label
>   |        |
>   |        | inner label
> block body message
> error body message
-}
basicStyle :: Style
basicStyle = Style
    { styleLocation = \(fp, l, c) -> T.concat ["--> ", T.pack fp, ":", T.pack $ show l, ":", T.pack $ show c]
    , styleNumber = T.pack . show
    , styleLine = highlight
    , styleEllipsis = "."
    , styleLinePrefix = "|"
    , styleVertical = "|"
    , styleHorizontal = "_"
    , styleDownRight = " "
    , styleUpRight = "|"
    , styleUpDownRight = "|"
    , styleTabWidth = 4
    , styleExtraLinesAfter = 2
    , styleExtraLinesBefore = 1
    , stylePaddingTop = True
    , stylePaddingBottom = False
    , styleEnableDecorations = True
    , styleEnableLinePrefix = True
    }

-- | Pointers using only ASCII characters.
basicPointer :: PointerStyle
basicPointer = PointerStyle
    { styleHighlight = id
    , styleUnderline = "^"
    , styleHook = "|"
    , styleConnector = "|"
    , styleEnableHook = True
    }

{- | A fancy style using Unicode characters.

Errors should look like so (with 'fancyPointer'):

> 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
> 4 │ │ line 4
> 5 │ │ line 5
> . │ │
> 7 │ │ line 7
> 8 │ │ line 8 baz end
>   │ └──────^─────^^^ end label
>   │        │
>   │        └ inner label
-}
fancyStyle :: Style
fancyStyle = Style
    { styleLocation = \(fp, l, c) -> T.concat
        [ "→ ", T.pack fp, ":", T.pack $ show l, ":", T.pack $ show c
        ]
    , styleNumber = T.pack . show
    , styleLine = highlight
    , styleEllipsis = "."
    , styleLinePrefix = "│"
    , styleHorizontal = "─"
    , styleVertical = "│"
    , styleDownRight = "┌"
    , styleUpDownRight = "├"
    , styleUpRight = "└"
    , styleTabWidth = 4
    , styleExtraLinesAfter = 2
    , styleExtraLinesBefore = 1
    , stylePaddingTop = True
    , stylePaddingBottom = False
    , styleEnableDecorations = True
    , styleEnableLinePrefix = True
    }

-- | Pointers using Unicode characters and ANSI colors.
fancyPointer :: PointerStyle
fancyPointer = PointerStyle
    { styleHighlight = id
    , styleUnderline = "^"
    , styleHook = "└"
    , styleConnector = "│"
    , styleEnableHook = True
    }

-- | A fancy style using Unicode characters and ANSI colors, similar to 'fancyStyle'. Most things are colored red.
fancyRedStyle :: Style
fancyRedStyle = Style
    { styleLocation = \(fp, l, c) -> T.concat
        [ "\x1b[34m→\x1b[0m ", T.pack fp, ":", T.pack $ show l, ":", T.pack $ show c
        ]
    , styleNumber = T.pack . show
    , styleLine = highlight
    , styleEllipsis = "."
    , styleLinePrefix = "\x1b[34m│\x1b[0m"
    , styleHorizontal = "\x1b[31m─\x1b[0m"
    , styleVertical = "\x1b[31m│\x1b[0m"
    , styleDownRight = "\x1b[31m┌\x1b[0m"
    , styleUpDownRight = "\x1b[31m├\x1b[0m"
    , styleUpRight = "\x1b[31m└\x1b[0m"
    , styleTabWidth = 4
    , styleExtraLinesAfter = 2
    , styleExtraLinesBefore = 1
    , stylePaddingTop = True
    , stylePaddingBottom = False
    , styleEnableDecorations = True
    , styleEnableLinePrefix = True
    }

-- | Red pointers using Unicode characters and ANSI colors.
fancyRedPointer :: PointerStyle
fancyRedPointer = PointerStyle
    { styleHighlight = \x -> "\x1b[31m" <> x <> "\x1b[0m"
    , styleUnderline = "\x1b[31m^\x1b[0m"
    , styleHook = "\x1b[31m└\x1b[0m"
    , styleConnector = "\x1b[31m│\x1b[0m"
    , styleEnableHook = True
    }

-- | A fancy style using Unicode characters and ANSI colors, similar to 'fancyStyle'. Most things are colored yellow.
fancyYellowStyle :: Style
fancyYellowStyle = Style
    { styleLocation = \(fp, l, c) -> T.concat
        [ "\x1b[34m→\x1b[0m ", T.pack fp, ":", T.pack $ show l, ":", T.pack $ show c
        ]
    , styleNumber = T.pack . show
    , styleLine = highlight
    , styleEllipsis = "."
    , styleLinePrefix = "\x1b[34m│\x1b[0m"
    , styleHorizontal = "\x1b[33m─\x1b[0m"
    , styleVertical = "\x1b[33m│\x1b[0m"
    , styleDownRight = "\x1b[33m┌\x1b[0m"
    , styleUpRight = "\x1b[33m└\x1b[0m"
    , styleUpDownRight = "\x1b[33m├\x1b[0m"
    , styleTabWidth = 4
    , styleExtraLinesAfter = 2
    , styleExtraLinesBefore = 1
    , stylePaddingTop = True
    , stylePaddingBottom = False
    , styleEnableDecorations = True
    , styleEnableLinePrefix = True
    }

-- | Yellow pointers using Unicode characters and ANSI colors.
fancyYellowPointer :: PointerStyle
fancyYellowPointer = PointerStyle
    { styleHighlight = \x -> "\x1b[33m" <> x <> "\x1b[0m"
    , styleUnderline = "\x1b[33m^\x1b[0m"
    , styleHook = "\x1b[33m└\x1b[0m"
    , styleConnector = "\x1b[33m│\x1b[0m"
    , styleEnableHook = True
    }

-- | Adds highlighting to spans of text by modifying it with the given styles' highlights.
highlight
    :: [(PointerStyle, (Column, Column))] -- ^ Styles and columns to work on. These are sorted, starting at 1. They must not overlap.
    -> T.Text                             -- ^ Text to highlight.
    -> T.Text
highlight [] xs = xs
highlight ((p, (s, e)):ps) xs =
    let (pre, xs') = T.splitAt (s - 1) xs
        (txt, xs'') = T.splitAt (e - s) xs'
        hi = styleHighlight p
        ps' = second (both (\i -> i - e + 1)) <$> ps
    in pre <> hi txt <> highlight ps' xs''
    where
        both f = bimap f f