{-# LANGUAGE CPP #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
module Refact.Utils (
Module
, Stmt
, Expr
, Decl
, Name
, Pat
, Type
, Import
, FunBind
, M
, mergeAnns
, modifyAnnKey
, replaceAnnKey
, toGhcSrcSpan
, setSrcSpanFile
, findParent
) where
import Language.Haskell.GHC.ExactPrint
import Language.Haskell.GHC.ExactPrint.Types
import Data.Data
import FastString (FastString)
import SrcLoc
import qualified SrcLoc as GHC
import qualified RdrName as GHC
import qualified ApiAnnotation as GHC
import qualified FastString as GHC
import qualified GHC hiding (parseModule)
#if __GLASGOW_HASKELL__ >= 810
import GHC.Hs.Expr as GHC hiding (Stmt)
import GHC.Hs.ImpExp
#else
import HsExpr as GHC hiding (Stmt)
import HsImpExp
#endif
import Control.Monad.Trans.State
import qualified Data.Map as Map
import Data.Maybe
import qualified Refact.Types as R
import Data.Generics.Schemes
import Unsafe.Coerce
type M a = State Anns a
type Module = (GHC.Located (GHC.HsModule GHC.GhcPs))
type Expr = GHC.Located (GHC.HsExpr GHC.GhcPs)
type Type = GHC.Located (GHC.HsType GHC.GhcPs)
type Decl = GHC.Located (GHC.HsDecl GHC.GhcPs)
type Pat = GHC.Located (GHC.Pat GHC.GhcPs)
type Name = GHC.Located GHC.RdrName
type Stmt = ExprLStmt GHC.GhcPs
type Import = LImportDecl GHC.GhcPs
type FunBind = HsMatchContext GHC.RdrName
replace :: AnnKey
-> AnnKey
-> AnnKey
-> AnnKey
-> Anns -> Maybe Anns
replace old new inp parent anns = do
oldan <- Map.lookup old anns
newan <- Map.lookup new anns
oldDelta <- annEntryDelta <$> Map.lookup parent anns
return $ Map.insert inp (combine oldDelta new oldan newan) anns
combine :: DeltaPos -> AnnKey -> Annotation -> Annotation -> Annotation
combine oldDelta newkey oldann newann =
Ann { annEntryDelta = newEntryDelta
, annPriorComments = annPriorComments oldann ++ annPriorComments newann
, annFollowingComments = annFollowingComments oldann ++ annFollowingComments newann
, annsDP = removeComma (annsDP newann) ++ extraComma (annsDP oldann)
, annSortKey = annSortKey newann
, annCapturedSpan = annCapturedSpan newann}
where
removeComma = filter (\(kw, _) -> case kw of
G GHC.AnnComma
| AnnKey _ (CN "ArithSeq") <- newkey -> True
| otherwise -> False
AnnSemiSep -> False
_ -> True)
extraComma [] = []
extraComma (last -> x) = case fst x of
G GHC.AnnComma -> [x]
AnnSemiSep -> [x]
G GHC.AnnSemi -> [x]
_ -> []
newEntryDelta | deltaRow oldDelta > 0 = oldDelta
| otherwise = annEntryDelta oldann
findParent :: Data a => GHC.SrcSpan -> Anns -> a -> Maybe AnnKey
findParent ss as = something (findParentWorker ss as)
findParentWorker :: forall a . (Data a)
=> GHC.SrcSpan -> Anns -> a -> Maybe AnnKey
findParentWorker oldSS as a
| con == typeRepTyCon (typeRep (Proxy :: Proxy (GHC.Located GHC.RdrName))) && x == typeRep (Proxy :: Proxy GHC.SrcSpan)
= if ss == oldSS
&& isJust (Map.lookup (AnnKey ss cn) as)
then Just $ AnnKey ss cn
else Nothing
| otherwise = Nothing
where
(con, ~[x, _]) = splitTyConApp (typeOf a)
ss :: GHC.SrcSpan
ss = gmapQi 0 unsafeCoerce a
cn = gmapQi 1 (CN . show . toConstr) a
modifyAnnKey :: (Data old, Data new, Data mod) => mod -> Located old -> Located new -> M (Located new)
modifyAnnKey m e1 e2 = do
as <- get
let parentKey = fromMaybe (mkAnnKey e2) (findParent (getLoc e2) as m)
e2 <$ modify (\m' -> replaceAnnKey m' (mkAnnKey e1) (mkAnnKey e2) (mkAnnKey e2) parentKey)
replaceAnnKey ::
Anns -> AnnKey -> AnnKey -> AnnKey -> AnnKey -> Anns
replaceAnnKey a old new inp deltainfo =
fromMaybe a (replace old new inp deltainfo a)
toGhcSrcSpan :: FilePath -> R.SrcSpan -> SrcSpan
toGhcSrcSpan file R.SrcSpan{..} = mkSrcSpan (f startLine startCol) (f endLine endCol)
where
f = mkSrcLoc (GHC.mkFastString file)
setSrcSpanFile :: FastString -> SrcSpan -> SrcSpan
setSrcSpanFile file s
| RealSrcLoc start <- srcSpanStart s
, RealSrcLoc end <- srcSpanEnd s
= let start' = mkSrcLoc file (srcLocLine start) (srcLocCol start)
end' = mkSrcLoc file (srcLocLine end) (srcLocCol end)
in mkSrcSpan start' end'
setSrcSpanFile _ s = s