{-# LANGUAGE CPP, DeriveFunctor #-}
module Location where

import           Control.DeepSeq (deepseq, NFData(rnf))
import           SrcLoc hiding (Located)
import qualified SrcLoc as GHC
import           FastString (unpackFS)

#if __GLASGOW_HASKELL__ < 702
import           Outputable (showPpr)
#endif

-- | A thing with a location attached.
data Located a = Located Location a
  deriving (Eq, Show, Functor)

instance NFData a => NFData (Located a) where
  rnf (Located loc a) = loc `deepseq` a `deepseq` ()

-- | Convert a GHC located thing to a located thing.
toLocated :: GHC.Located a -> Located a
toLocated (L loc a) = Located (toLocation loc) a

-- | Discard location information.
unLoc :: Located a -> a
unLoc (Located _ a) = a

-- | Add dummy location information.
noLocation :: a -> Located a
noLocation = Located (UnhelpfulLocation "<no location info>")

-- | A line number.
type Line = Int

-- | A combination of file name and line number.
data Location = UnhelpfulLocation String | Location FilePath Line
  deriving Eq

instance Show Location where
  show (UnhelpfulLocation s) = s
  show (Location file line)  = file ++ ":" ++ show line

instance NFData Location where
  rnf (UnhelpfulLocation str) = str `deepseq` ()
  rnf (Location file line)    = file `deepseq` line `deepseq` ()

-- |
-- Create a list from a location, by repeatedly increasing the line number by
-- one.
enumerate :: Location -> [Location]
enumerate loc = case loc of
  UnhelpfulLocation _ -> repeat loc
  Location file line  -> map (Location file) [line ..]

-- | Convert a GHC source span to a location.
toLocation :: SrcSpan -> Location
#if __GLASGOW_HASKELL__ < 702
toLocation loc
  | isGoodSrcLoc start = Location (unpackFS $ srcLocFile start) (srcLocLine start)
  | otherwise          = (UnhelpfulLocation . showPpr) start
  where
    start = srcSpanStart loc
#else
toLocation loc = case loc of
  UnhelpfulSpan str -> UnhelpfulLocation (unpackFS str)
  RealSrcSpan sp    -> Location (unpackFS . srcSpanFile $ sp) (srcSpanStartLine sp)
#endif