--------------------------------------------------------------------------------
-- | There are a number of steps that sort items: 'Imports' and 'ModuleHeader',
-- and maybe more in the future.  This module provides consistent sorting
-- utilities.
{-# LANGUAGE LambdaCase #-}
module Language.Haskell.Stylish.Ordering
    ( compareLIE
    , compareWrappedName
    , unwrapName
    ) where


--------------------------------------------------------------------------------
import           Data.Char                    (isUpper)
import           Data.Ord                     (comparing)
import           GHC.Hs
import           RdrName                      (RdrName)
import           SrcLoc                       (unLoc)


--------------------------------------------------------------------------------
import           Language.Haskell.Stylish.GHC (showOutputable)
import           Outputable                   (Outputable)


--------------------------------------------------------------------------------
-- | NOTE: Can we get rid off this by adding a properly sorting newtype around
-- 'RdrName'?
compareLIE :: LIE GhcPs -> LIE GhcPs -> Ordering
compareLIE = comparing $ ieKey . unLoc
  where
    -- | The implementation is a bit hacky to get proper sorting for input specs:
    -- constructors first, followed by functions, and then operators.
    ieKey :: IE GhcPs -> (Int, String)
    ieKey = \case
        IEVar _ n             -> nameKey n
        IEThingAbs _ n        -> nameKey n
        IEThingAll _ n        -> nameKey n
        IEThingWith _ n _ _ _ -> nameKey n
        IEModuleContents _ n  -> nameKey n
        _                     -> (2, "")


--------------------------------------------------------------------------------
compareWrappedName :: IEWrappedName RdrName -> IEWrappedName RdrName -> Ordering
compareWrappedName = comparing nameKey


--------------------------------------------------------------------------------
unwrapName :: IEWrappedName n -> n
unwrapName (IEName n)    = unLoc n
unwrapName (IEPattern n) = unLoc n
unwrapName (IEType n)    = unLoc n


--------------------------------------------------------------------------------
nameKey :: Outputable name => name -> (Int, String)
nameKey n = case showOutputable n of
    o@('(' : _)             -> (2, o)
    o@(o0 : _) | isUpper o0 -> (0, o)
    o                       -> (1, o)