-- |
-- Module      : Error.Diagnose.Style
-- Description : Custom style definitions
-- Copyright   : (c) Mesabloo, 2021-2022
-- License     : BSD3
-- Stability   : experimental
-- Portability : Portable
module Error.Diagnose.Style
  ( -- * Defining new style
    Annotation (..),
    Style,
    -- $defining_new_styles

    -- * Default style specification
    defaultStyle,

    -- * Re-exports
    reAnnotate,
  )
where

import Prettyprinter (Doc, reAnnotate)
import Prettyprinter.Render.Terminal (AnsiStyle, Color (..), bold, color, colorDull)

-- $defining_new_styles
--
-- Defining new color styles (one may call them "themes") is actually rather easy.
--
-- A 'Style' is a function from an annotated 'Doc'ument to another annotated 'Doc'ument.
-- Note that only the annotation type changes, hence the need of only providing a unidirectional mapping between those.
--
-- 'Annotation's are used when creating a 'Doc'ument and are simply placeholders to specify custom colors.
-- 'AnsiStyle' is the concrete annotation to specify custom colors when rendering a 'Doc'ument.
--
-- One may define additional styles as follows:
--
-- > myNewCustomStyle :: Style
-- > myNewCustomStyle = reAnnotate \case
-- >   -- all cases for all annotations
--
-- For simplicity's sake, a default style is given as 'defaultStyle'.

-- | Some annotations as placeholders for colors in a 'Doc'.
data Annotation
  = -- | The color of 'Error.Diagnose.Report.This' markers, depending on whether the report is an error
    --   report or a warning report.
    ThisColor
      Bool
  | -- | The color of 'Error.Diagnose.Report.Maybe' markers.
    MaybeColor
  | -- | The color of 'Error.Diagnose.Report.Where' markers.
    WhereColor
  | -- | The color for hints.
    --
    --   Note that the beginning @Hint:@ text will always be in bold.
    HintColor
  | -- | The color for file names.
    FileColor
  | -- | The color of the rule separating the code/markers from the line numbers.
    RuleColor
  | -- | The color of the @[error]@/@[warning]@ at the top, depending on whether
    --   this is an error or warning report.
    KindColor
      Bool
  | -- | The color in which to output the @<no line>@ information when the file was not found.
    NoLineColor
  | -- | Additional style to apply to marker rules (e.g. bold) on top of some
    --   already processed color annotation.
    MarkerStyle
      Annotation
  | -- | The color of the code when no marker is present.
    CodeStyle

-- | A style is a function which can be applied using 'reAnnotate'.
--
--   It transforms a 'Doc'ument containing 'Annotation's into a 'Doc'ument containing
--   color information.
type Style = Doc Annotation -> Doc AnsiStyle

-------------------------------------------

-- | The default style for diagnostics, where:
--
--   * 'Error.Diagnose.Report.This' markers are colored in red for errors and yellow for warnings
--   * 'Error.Diagnose.Report.Where' markers are colored in dull blue
--   * 'Error.Diagnose.Report.Maybe' markers are colored in magenta
--   * Marker rules are of the same color of the marker, but also in bold
--   * Hints are output in cyan
--   * The left rules are colored in bold black
--   * File names are output in dull green
--   * The @[error]@/@[warning]@ at the top is colored in red for errors and yellow for warnings
--   * The code is output in normal white
defaultStyle :: Style
defaultStyle :: Style
defaultStyle = (Annotation -> AnsiStyle) -> Style
forall ann ann'. (ann -> ann') -> Doc ann -> Doc ann'
reAnnotate Annotation -> AnsiStyle
style
  where
    style :: Annotation -> AnsiStyle
style = \case
      ThisColor Bool
isError -> Color -> AnsiStyle
color if Bool
isError then Color
Red else Color
Yellow
      Annotation
MaybeColor -> Color -> AnsiStyle
color Color
Magenta
      Annotation
WhereColor -> Color -> AnsiStyle
colorDull Color
Blue
      Annotation
HintColor -> Color -> AnsiStyle
color Color
Cyan
      Annotation
FileColor -> AnsiStyle
bold AnsiStyle -> AnsiStyle -> AnsiStyle
forall a. Semigroup a => a -> a -> a
<> Color -> AnsiStyle
colorDull Color
Green
      Annotation
RuleColor -> AnsiStyle
bold AnsiStyle -> AnsiStyle -> AnsiStyle
forall a. Semigroup a => a -> a -> a
<> Color -> AnsiStyle
color Color
Black
      KindColor Bool
isError -> AnsiStyle
bold AnsiStyle -> AnsiStyle -> AnsiStyle
forall a. Semigroup a => a -> a -> a
<> Annotation -> AnsiStyle
style (Bool -> Annotation
ThisColor Bool
isError)
      Annotation
NoLineColor -> AnsiStyle
bold AnsiStyle -> AnsiStyle -> AnsiStyle
forall a. Semigroup a => a -> a -> a
<> Color -> AnsiStyle
colorDull Color
Magenta
      MarkerStyle Annotation
st ->
        let ann :: AnsiStyle
ann = Annotation -> AnsiStyle
style Annotation
st
         in if AnsiStyle
ann AnsiStyle -> AnsiStyle -> Bool
forall a. Eq a => a -> a -> Bool
== Annotation -> AnsiStyle
style Annotation
CodeStyle
              then AnsiStyle
ann
              else AnsiStyle
bold AnsiStyle -> AnsiStyle -> AnsiStyle
forall a. Semigroup a => a -> a -> a
<> AnsiStyle
ann
      Annotation
CodeStyle -> Color -> AnsiStyle
color Color
White