2014-06-29 08:28:28 +00:00
|
|
|
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, CPP #-}
|
2014-06-27 16:38:15 +00:00
|
|
|
|
|
|
|
module Language.Haskell.GhcMod.FillSig (
|
2014-07-11 01:10:37 +00:00
|
|
|
sig
|
2014-06-27 16:38:15 +00:00
|
|
|
) where
|
|
|
|
|
|
|
|
import Data.Char (isSymbol)
|
2014-07-17 08:16:44 +00:00
|
|
|
import Data.List (find)
|
2014-06-27 16:38:15 +00:00
|
|
|
import Exception (ghandle, SomeException(..))
|
2014-06-28 19:43:51 +00:00
|
|
|
import GHC (GhcMonad, Id, ParsedModule(..), TypecheckedModule(..), DynFlags, SrcSpan, Type, GenLocated(L))
|
2014-06-27 16:38:15 +00:00
|
|
|
import qualified GHC as G
|
|
|
|
import qualified Language.Haskell.GhcMod.Gap as Gap
|
2014-06-28 19:43:51 +00:00
|
|
|
import Language.Haskell.GhcMod.Convert
|
|
|
|
import Language.Haskell.GhcMod.Monad
|
2014-06-27 16:38:15 +00:00
|
|
|
import Language.Haskell.GhcMod.SrcUtils
|
2014-07-11 02:12:05 +00:00
|
|
|
import CoreMonad (liftIO)
|
2014-06-27 16:38:15 +00:00
|
|
|
import Outputable (PprStyle)
|
|
|
|
import qualified Type as Ty
|
|
|
|
import qualified HsBinds as Ty
|
|
|
|
import qualified Class as Ty
|
|
|
|
import qualified Language.Haskell.Exts.Annotated as HE
|
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
|
|
-- INTIAL CODE FROM FUNCTION OR INSTANCE SIGNATURE
|
|
|
|
----------------------------------------------------------------
|
|
|
|
|
|
|
|
-- Possible signatures we can find: function or instance
|
|
|
|
data SigInfo = Signature SrcSpan [G.RdrName] (G.HsType G.RdrName)
|
|
|
|
| InstanceDecl SrcSpan G.Class
|
|
|
|
|
|
|
|
-- Signature for fallback operation via haskell-src-exts
|
2014-07-11 01:10:37 +00:00
|
|
|
data HESigInfo = HESignature HE.SrcSpan [HE.Name HE.SrcSpanInfo] (HE.Type HE.SrcSpanInfo)
|
2014-06-27 16:38:15 +00:00
|
|
|
|
|
|
|
-- | Create a initial body from a signature.
|
2014-07-12 09:16:16 +00:00
|
|
|
sig :: IOish m
|
|
|
|
=> FilePath -- ^ A target file.
|
2014-06-27 16:38:15 +00:00
|
|
|
-> Int -- ^ Line number.
|
|
|
|
-> Int -- ^ Column number.
|
2014-07-12 09:16:16 +00:00
|
|
|
-> GhcModT m String
|
2014-06-28 19:43:51 +00:00
|
|
|
sig file lineNo colNo = ghandle handler body
|
2014-06-27 16:38:15 +00:00
|
|
|
where
|
|
|
|
body = inModuleContext file $ \dflag style -> do
|
2014-06-28 19:43:51 +00:00
|
|
|
opt <- options
|
2014-06-27 16:38:15 +00:00
|
|
|
modSum <- Gap.fileModSummary file
|
2014-06-27 17:32:05 +00:00
|
|
|
whenFound opt (getSignature modSum lineNo colNo) $ \s -> case s of
|
|
|
|
Signature loc names ty ->
|
|
|
|
("function", fourInts loc, map (initialBody dflag style ty) names)
|
2014-07-17 08:16:44 +00:00
|
|
|
InstanceDecl loc cls ->
|
2014-06-27 17:32:05 +00:00
|
|
|
("instance", fourInts loc, map (\x -> initialBody dflag style (G.idType x) x)
|
|
|
|
(Ty.classMethods cls))
|
2014-07-11 01:10:37 +00:00
|
|
|
|
2014-06-27 16:38:15 +00:00
|
|
|
handler (SomeException _) = do
|
2014-06-28 19:43:51 +00:00
|
|
|
opt <- options
|
2014-06-27 16:38:15 +00:00
|
|
|
-- Code cannot be parsed by ghc module
|
|
|
|
-- Fallback: try to get information via haskell-src-exts
|
|
|
|
whenFound opt (getSignatureFromHE file lineNo colNo) $
|
2014-07-11 01:10:37 +00:00
|
|
|
\(HESignature loc names ty) ->
|
2014-06-27 16:38:15 +00:00
|
|
|
("function", fourIntsHE loc, map (initialBody undefined undefined ty) names)
|
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
|
|
-- a. Code for getting the information
|
|
|
|
|
|
|
|
-- Get signature from ghc parsing and typechecking
|
2014-06-28 19:43:51 +00:00
|
|
|
getSignature :: GhcMonad m => G.ModSummary -> Int -> Int -> m (Maybe SigInfo)
|
2014-06-27 16:38:15 +00:00
|
|
|
getSignature modSum lineNo colNo = do
|
|
|
|
p@ParsedModule{pm_parsed_source = ps} <- G.parseModule modSum
|
|
|
|
-- Inspect the parse tree to find the signature
|
|
|
|
case listifyParsedSpans ps (lineNo, colNo) :: [G.LHsDecl G.RdrName] of
|
|
|
|
[L loc (G.SigD (Ty.TypeSig names (L _ ty)))] ->
|
|
|
|
-- We found a type signature
|
|
|
|
return $ Just $ Signature loc (map G.unLoc names) ty
|
|
|
|
[L _ (G.InstD _)] -> do
|
|
|
|
-- We found an instance declaration
|
|
|
|
TypecheckedModule{tm_renamed_source = Just tcs
|
|
|
|
,tm_checked_module_info = minfo} <- G.typecheckModule p
|
2014-07-15 05:44:02 +00:00
|
|
|
case Gap.getClass $ listifyRenamedSpans tcs (lineNo, colNo) of
|
|
|
|
Just (clsName,loc) -> obtainClassInfo minfo clsName loc
|
|
|
|
Nothing -> return Nothing
|
2014-06-27 16:38:15 +00:00
|
|
|
_ -> return Nothing
|
2014-06-28 19:43:51 +00:00
|
|
|
where obtainClassInfo :: GhcMonad m => G.ModuleInfo -> G.Name -> SrcSpan -> m (Maybe SigInfo)
|
2014-06-27 16:38:15 +00:00
|
|
|
obtainClassInfo minfo clsName loc = do
|
|
|
|
tyThing <- G.modInfoLookupName minfo clsName
|
|
|
|
return $ do Ty.ATyCon clsCon <- tyThing -- In Maybe
|
|
|
|
cls <- G.tyConClass_maybe clsCon
|
|
|
|
return $ InstanceDecl loc cls
|
|
|
|
|
|
|
|
-- Get signature from haskell-src-exts
|
2014-06-28 19:43:51 +00:00
|
|
|
getSignatureFromHE :: GhcMonad m => FilePath -> Int -> Int -> m (Maybe HESigInfo)
|
2014-06-27 16:38:15 +00:00
|
|
|
getSignatureFromHE file lineNo colNo = do
|
|
|
|
presult <- liftIO $ HE.parseFile file
|
|
|
|
return $ case presult of
|
|
|
|
HE.ParseOk (HE.Module _ _ _ _ mdecls) -> do
|
|
|
|
HE.TypeSig (HE.SrcSpanInfo s _) names ty <- find (typeSigInRangeHE lineNo colNo) mdecls
|
|
|
|
return $ HESignature s names ty
|
|
|
|
_ -> Nothing
|
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
|
|
-- b. Code for generating initial code
|
|
|
|
|
|
|
|
-- A list of function arguments, and whether they are functions or normal arguments
|
|
|
|
-- is built from either a function signature or an instance signature
|
|
|
|
data FnArg = FnArgFunction | FnArgNormal
|
|
|
|
|
|
|
|
initialBody :: FnArgsInfo ty name => DynFlags -> PprStyle -> ty -> name -> String
|
|
|
|
initialBody dflag style ty name = initialBody' (getFnName dflag style name) (getFnArgs ty)
|
|
|
|
|
|
|
|
initialBody' :: String -> [FnArg] -> String
|
|
|
|
initialBody' fname args =
|
|
|
|
case initialBodyArgs args infiniteVars infiniteFns of
|
|
|
|
[] -> fname
|
|
|
|
arglist -> if isSymbolName fname
|
2014-07-17 08:16:44 +00:00
|
|
|
then head arglist ++ " " ++ fname ++ " " ++ unwords (tail arglist)
|
|
|
|
else fname ++ " " ++ unwords arglist
|
2014-06-27 16:38:15 +00:00
|
|
|
++ " = " ++ (if isSymbolName fname then "" else '_':fname) ++ "_body"
|
|
|
|
|
|
|
|
initialBodyArgs :: [FnArg] -> [String] -> [String] -> [String]
|
|
|
|
initialBodyArgs [] _ _ = []
|
|
|
|
initialBodyArgs (FnArgFunction:xs) vs (f:fs) = f : initialBodyArgs xs vs fs
|
|
|
|
initialBodyArgs (FnArgNormal:xs) (v:vs) fs = v : initialBodyArgs xs vs fs
|
|
|
|
initialBodyArgs _ _ _ = error "This should never happen" -- Lists are infinite
|
|
|
|
|
|
|
|
-- Getting the initial body of function and instances differ
|
|
|
|
-- This is because for functions we only use the parsed file
|
|
|
|
-- (so the full file doesn't have to be type correct)
|
|
|
|
-- but for instances we need to get information about the class
|
|
|
|
|
|
|
|
class FnArgsInfo ty name | ty -> name, name -> ty where
|
|
|
|
getFnName :: DynFlags -> PprStyle -> name -> String
|
|
|
|
getFnArgs :: ty -> [FnArg]
|
|
|
|
|
|
|
|
instance FnArgsInfo (G.HsType G.RdrName) (G.RdrName) where
|
2014-07-15 05:44:02 +00:00
|
|
|
getFnName dflag style name = showOccName dflag style $ Gap.occName name
|
2014-06-27 16:38:15 +00:00
|
|
|
getFnArgs (G.HsForAllTy _ _ _ (L _ iTy)) = getFnArgs iTy
|
|
|
|
getFnArgs (G.HsParTy (L _ iTy)) = getFnArgs iTy
|
|
|
|
getFnArgs (G.HsFunTy (L _ lTy) (L _ rTy)) = (if fnarg lTy then FnArgFunction else FnArgNormal):getFnArgs rTy
|
2014-07-17 08:16:44 +00:00
|
|
|
where fnarg ty = case ty of
|
|
|
|
(G.HsForAllTy _ _ _ (L _ iTy)) -> fnarg iTy
|
|
|
|
(G.HsParTy (L _ iTy)) -> fnarg iTy
|
|
|
|
(G.HsFunTy _ _) -> True
|
|
|
|
_ -> False
|
2014-06-27 16:38:15 +00:00
|
|
|
getFnArgs _ = []
|
|
|
|
|
|
|
|
instance FnArgsInfo (HE.Type HE.SrcSpanInfo) (HE.Name HE.SrcSpanInfo) where
|
|
|
|
getFnName _ _ (HE.Ident _ s) = s
|
|
|
|
getFnName _ _ (HE.Symbol _ s) = s
|
|
|
|
getFnArgs (HE.TyForall _ _ _ iTy) = getFnArgs iTy
|
|
|
|
getFnArgs (HE.TyParen _ iTy) = getFnArgs iTy
|
|
|
|
getFnArgs (HE.TyFun _ lTy rTy) = (if fnarg lTy then FnArgFunction else FnArgNormal):getFnArgs rTy
|
2014-07-17 08:16:44 +00:00
|
|
|
where fnarg ty = case ty of
|
|
|
|
(HE.TyForall _ _ _ iTy) -> fnarg iTy
|
|
|
|
(HE.TyParen _ iTy) -> fnarg iTy
|
|
|
|
(HE.TyFun _ _ _) -> True
|
|
|
|
_ -> False
|
2014-06-27 16:38:15 +00:00
|
|
|
getFnArgs _ = []
|
|
|
|
|
|
|
|
instance FnArgsInfo Type Id where
|
|
|
|
getFnName dflag style method = showOccName dflag style $ G.getOccName method
|
|
|
|
getFnArgs = getFnArgs' . Ty.dropForAlls
|
|
|
|
where getFnArgs' ty | Just (lTy,rTy) <- Ty.splitFunTy_maybe ty =
|
|
|
|
maybe (if Ty.isPredTy lTy then getFnArgs' rTy else FnArgNormal:getFnArgs' rTy)
|
|
|
|
(\_ -> FnArgFunction:getFnArgs' rTy)
|
|
|
|
$ Ty.splitFunTy_maybe lTy
|
|
|
|
getFnArgs' ty | Just (_,iTy) <- Ty.splitForAllTy_maybe ty = getFnArgs' iTy
|
|
|
|
getFnArgs' _ = []
|
|
|
|
|
|
|
|
-- Infinite supply of variable and function variable names
|
|
|
|
infiniteVars, infiniteFns :: [String]
|
|
|
|
infiniteVars = infiniteSupply ["x","y","z","t","u","v","w"]
|
|
|
|
infiniteFns = infiniteSupply ["f","g","h"]
|
|
|
|
infiniteSupply :: [String] -> [String]
|
|
|
|
infiniteSupply initialSupply = initialSupply ++ concatMap (\n -> map (\v -> v ++ show n) initialSupply) ([1 .. ] :: [Integer])
|
|
|
|
|
|
|
|
-- Check whether a String is a symbol name
|
|
|
|
isSymbolName :: String -> Bool
|
|
|
|
isSymbolName (c:_) = c `elem` "!#$%&*+./<=>?@\\^|-~" || isSymbol c
|
|
|
|
isSymbolName [] = error "This should never happen"
|