{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- | Printer for fixity maps.
module Ormolu.Fixity.Printer
  ( printFixityMap,
  )
where

import qualified Data.Char as Char
import qualified Data.Map.Strict as Map
import Data.Text (Text)
import qualified Data.Text.Lazy as TL
import Data.Text.Lazy.Builder (Builder)
import qualified Data.Text.Lazy.Builder as B
import qualified Data.Text.Lazy.Builder.Int as B
import Ormolu.Fixity.Internal

-- | Print out a textual representation of a 'FixityMap'.
printFixityMap :: FixityMap -> Text
printFixityMap :: FixityMap -> Text
printFixityMap =
  Text -> Text
TL.toStrict
    (Text -> Text) -> (FixityMap -> Text) -> FixityMap -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
B.toLazyText
    (Builder -> Text) -> (FixityMap -> Builder) -> FixityMap -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
    ([Builder] -> Builder)
-> (FixityMap -> [Builder]) -> FixityMap -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((FixityDirection, Int, String) -> Builder)
-> [(FixityDirection, Int, String)] -> [Builder]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (FixityDirection, Int, String) -> Builder
renderOne
    ([(FixityDirection, Int, String)] -> [Builder])
-> (FixityMap -> [(FixityDirection, Int, String)])
-> FixityMap
-> [Builder]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((String, FixityInfo) -> [(FixityDirection, Int, String)])
-> [(String, FixityInfo)] -> [(FixityDirection, Int, String)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (String, FixityInfo) -> [(FixityDirection, Int, String)]
decompose
    ([(String, FixityInfo)] -> [(FixityDirection, Int, String)])
-> (FixityMap -> [(String, FixityInfo)])
-> FixityMap
-> [(FixityDirection, Int, String)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FixityMap -> [(String, FixityInfo)]
forall k a. Map k a -> [(k, a)]
Map.toList
  where
    decompose :: (String, FixityInfo) -> [(FixityDirection, Int, String)]
    decompose :: (String, FixityInfo) -> [(FixityDirection, Int, String)]
decompose (String
operator, FixityInfo {Int
Maybe FixityDirection
fiMaxPrecedence :: FixityInfo -> Int
fiMinPrecedence :: FixityInfo -> Int
fiDirection :: FixityInfo -> Maybe FixityDirection
fiMaxPrecedence :: Int
fiMinPrecedence :: Int
fiDirection :: Maybe FixityDirection
..}) =
      let forDirection :: a -> [(a, Int, String)]
forDirection a
dir =
            (a
dir, Int
fiMinPrecedence, String
operator)
              (a, Int, String) -> [(a, Int, String)] -> [(a, Int, String)]
forall a. a -> [a] -> [a]
: [ (a
dir, Int
fiMaxPrecedence, String
operator)
                  | Int
fiMinPrecedence Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
fiMaxPrecedence
                ]
       in case Maybe FixityDirection
fiDirection of
            Maybe FixityDirection
Nothing -> (FixityDirection -> [(FixityDirection, Int, String)])
-> [FixityDirection] -> [(FixityDirection, Int, String)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap FixityDirection -> [(FixityDirection, Int, String)]
forall a. a -> [(a, Int, String)]
forDirection [FixityDirection
InfixL, FixityDirection
InfixR]
            Just FixityDirection
dir -> FixityDirection -> [(FixityDirection, Int, String)]
forall a. a -> [(a, Int, String)]
forDirection FixityDirection
dir
    renderOne :: (FixityDirection, Int, String) -> Builder
    renderOne :: (FixityDirection, Int, String) -> Builder
renderOne (FixityDirection
fixityDirection, Int
n, String
operator) =
      [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
        [ case FixityDirection
fixityDirection of
            FixityDirection
InfixL -> Builder
"infixl"
            FixityDirection
InfixR -> Builder
"infixr"
            FixityDirection
InfixN -> Builder
"infix",
          Builder
" ",
          Int -> Builder
forall a. Integral a => a -> Builder
B.decimal Int
n,
          Builder
" ",
          if String -> Bool
isTickedOperator String
operator
            then Builder
"`" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> String -> Builder
B.fromString String
operator Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"`"
            else String -> Builder
B.fromString String
operator,
          Builder
"\n"
        ]
    isTickedOperator :: String -> Bool
isTickedOperator [] = Bool
True
    isTickedOperator (Char
x : String
_) = Char -> Bool
Char.isLetter Char
x