module Language.Haskell.BuildWrapper.Src where
import Data.Maybe (isNothing)
import Language.Haskell.BuildWrapper.Base
import Language.Haskell.Exts.Annotated
import Language.Haskell.Exts.Parser
import qualified Language.Haskell.Exts.Syntax as S
import qualified Data.Map as DM
import qualified Data.Text as T
import Data.Char (isSpace)
import Data.List (foldl', isPrefixOf)
import Control.Monad.Trans.State.Lazy (State, get, evalState, put, runState)
getHSEAST :: String
-> [String]
-> ParseResult (Module SrcSpanInfo, [Comment])
getHSEAST input options=do
let
input' = dropWhile (=='\n') . dropWhile (/='\n') $ input
topPragmas = case getTopPragmas input' of
ParseOk pragmas -> pragmas
_ -> []
optionsPragmas = [ optionsPragma | S.OptionsPragma _ _ optionsPragma <- topPragmas ]
optionsFromPragmas = concatMap words optionsPragmas
#if MIN_VERSION_haskell_src_exts(1,14,0)
exts=EnableExtension MultiParamTypeClasses : EnableExtension PatternGuards : map (\x->classifyExtension $ if "-X" `isPrefixOf` x then tail $ tail x else x) (options ++ optionsFromPragmas)
#else
exts=MultiParamTypeClasses : PatternGuards : map (\x->classifyExtension $ if "-X" `isPrefixOf` x then tail $ tail x else x) (options ++ optionsFromPragmas)
#endif
extsFull=if "-fglasgow-exts" `elem` options ++ optionsFromPragmas
then exts ++ glasgowExts
else exts
parseMode=defaultParseMode {extensions=extsFull,ignoreLinePragmas=False,ignoreLanguagePragmas=False,fixities = Just baseFixities}
parseFileContentsWithComments parseMode input
getHSEOutline :: (Module SrcSpanInfo, [Comment])
-> [OutlineDef]
getHSEOutline (Module _ _ _ _ decls,comments)=let
odecls = concatMap declOutline decls
(d2,m2) = runState (mapM (addComment False) odecls) $ commentMap
in evalState (mapM (addComment True) d2) m2
where
declOutline :: Decl SrcSpanInfo -> [OutlineDef]
declOutline (DataFamDecl l _ h _) = [mkOutlineDef (headDecl h) [Data,Family] (makeSpan l)]
declOutline (DataInsDecl l _ t cons _) = [mkOutlineDefWithChildren (typeDecl t) [Data,Instance] (makeSpan l) (map qualConDeclOutline cons)]
declOutline (DataDecl l _ _ h cons _) = [mkOutlineDefWithChildren (headDecl h) [Data] (makeSpan l) (map qualConDeclOutline cons)]
declOutline (TypeFamDecl l h _) = [mkOutlineDef (headDecl h) [Type,Family] (makeSpan l)]
declOutline (TypeInsDecl l t1 _) = [mkOutlineDef (typeDecl t1) [Type,Instance] (makeSpan l)]
declOutline (TypeDecl l h t) = [OutlineDef (headDecl h) [Type] (makeSpan l) [] (Just $ typeDecl t) Nothing Nothing]
declOutline (ClassDecl l _ h _ cdecls) = [mkOutlineDefWithChildren (headDecl h) [Class] (makeSpan l) (maybe [] (concatMap classDecl) cdecls)]
declOutline (FunBind l matches) = let
n=matchDecl $ head matches
(ty,l2)=addTypeInfo n l
in [OutlineDef n [Function] (makeSpan l2) [] ty Nothing Nothing]
#if MIN_VERSION_haskell_src_exts(1,16,0)
declOutline (PatBind l (PVar _ n) _ _)=let
#else
declOutline (PatBind l (PVar _ n) _ _ _)=let
#endif
nd=nameDecl n
(ty,l2)=addTypeInfo nd l
in [OutlineDef nd [Function] (makeSpan l2) [] ty Nothing Nothing]
#if MIN_VERSION_haskell_src_exts(1,16,0)
declOutline (InstDecl l _ h idecls)=[mkOutlineDefWithChildren (iheadRule h) [Instance] (makeSpan l) (maybe [] (concatMap instDecl) idecls)]
#else
declOutline (InstDecl l _ h idecls)=[mkOutlineDefWithChildren (iheadDecl h) [Instance] (makeSpan l) (maybe [] (concatMap instDecl) idecls)]
#endif
declOutline (SpliceDecl l e)=[mkOutlineDef (spliceDecl e) [Splice] (makeSpan l)]
declOutline _ = []
#if MIN_VERSION_haskell_src_exts(1,16,0)
iheadRule (IRule _ _ _ h) =iheadDecl h
iheadRule (IParen _ r)=iheadRule r
#endif
qualConDeclOutline :: QualConDecl SrcSpanInfo-> OutlineDef
qualConDeclOutline (QualConDecl l _ _ con)=let
(n,defs)=conDecl con
in mkOutlineDefWithChildren n [Constructor] (makeSpan l) defs
declOutlineInClass :: Decl SrcSpanInfo -> [OutlineDef]
declOutlineInClass (TypeSig l ns _)=map (\n->mkOutlineDef (nameDecl n) [Function] (makeSpan l)) ns
declOutlineInClass o=declOutline o
headDecl :: DeclHead a -> T.Text
#if MIN_VERSION_haskell_src_exts(1,16,0)
headDecl (DHead _ n )=nameDecl n
headDecl (DHInfix _ _ n)=nameDecl n
headDecl (DHApp _ h _)=headDecl h
#else
headDecl (DHead _ n _)=nameDecl n
headDecl (DHInfix _ _ n _)=nameDecl n
#endif
headDecl (DHParen _ h)=headDecl h
typeDecl :: Type SrcSpanInfo -> T.Text
typeDecl = T.pack . prettyPrint
matchDecl :: Match a -> T.Text
matchDecl (Match _ n _ _ _)=nameDecl n
matchDecl (InfixMatch _ _ n _ _ _)=nameDecl n
iheadDecl :: InstHead SrcSpanInfo -> T.Text
#if MIN_VERSION_haskell_src_exts(1,16,0)
iheadDecl (IHCon _ qn)= qnameDecl qn
iheadDecl (IHApp _ i t)= T.concat [iheadDecl i, " ",typeDecl t]
iheadDecl (IHInfix _ t1 qn)= T.concat [typeDecl t1, " ", qnameDecl qn]
#else
iheadDecl (IHead _ qn ts)= T.concat [qnameDecl qn, " ", T.intercalate " " (map typeDecl ts)]
iheadDecl (IHInfix _ t1 qn t2)= T.concat [typeDecl t1, " ", qnameDecl qn, " ", typeDecl t2]
#endif
iheadDecl (IHParen _ i)=iheadDecl i
conDecl :: ConDecl SrcSpanInfo -> (T.Text,[OutlineDef])
conDecl (ConDecl _ n _)=(nameDecl n,[])
conDecl (InfixConDecl _ _ n _)=(nameDecl n,[])
conDecl (RecDecl _ n fields)=(nameDecl n,concatMap fieldDecl fields)
fieldDecl :: FieldDecl SrcSpanInfo -> [OutlineDef]
fieldDecl (FieldDecl l ns _)=map (\n->mkOutlineDef (nameDecl n) [Field] (makeSpan l)) ns
classDecl :: ClassDecl SrcSpanInfo -> [OutlineDef]
classDecl (ClsDecl _ d) = declOutlineInClass d
classDecl _ = []
instDecl :: InstDecl SrcSpanInfo -> [OutlineDef]
instDecl (InsDecl _ d) = declOutlineInClass d
instDecl _ = []
spliceDecl :: Exp SrcSpanInfo -> T.Text
spliceDecl (SpliceExp _ sp)= spliceName sp
spliceDecl (App _ e1 _)=spliceDecl e1
spliceDecl (Var _ qn)=qnameDecl qn
spliceDecl _ = ""
spliceName :: Splice SrcSpanInfo -> T.Text
spliceName (IdSplice _ n)=T.pack n
spliceName (ParenSplice _ e)=spliceDecl e
typeMap :: DM.Map T.Text (T.Text,SrcSpanInfo)
typeMap = foldr buildTypeMap DM.empty decls
buildTypeMap :: Decl SrcSpanInfo -> DM.Map T.Text (T.Text,SrcSpanInfo) -> DM.Map T.Text (T.Text,SrcSpanInfo)
buildTypeMap (TypeSig ssi ns t) m=let
td=typeDecl t
in if T.null td
then m
else foldr (\n2 m2->DM.insert (nameDecl n2) (td,ssi) m2) m ns
buildTypeMap _ m=m
addTypeInfo :: T.Text -> SrcSpanInfo -> (Maybe T.Text,SrcSpanInfo)
addTypeInfo t ss1=let
m=DM.lookup t typeMap
in case m of
Nothing->(Nothing,ss1)
Just (ty,ss2)->let
end = lastEnd $ srcSpanEndLine (srcInfoSpan ss2)
in if end == (srcSpanStartLine (srcInfoSpan ss1) 1)
then (Just ty,combSpanInfo ss2 ss1)
else (Just ty,ss1)
lastEnd end
| (end+1) `DM.member` commentMap= lastEnd $ end+1
| otherwise = end
commentMap:: DM.Map Int (Int,Int,Bool,T.Text)
commentMap = foldl' buildCommentMap DM.empty comments
addComment:: Bool -> OutlineDef -> State (DM.Map Int (Int,Int,Bool,T.Text)) OutlineDef
addComment checkNext od =do
cm<-get
let
st=iflLine $ ifsStart$ odLoc od
pl=map (flip DM.lookup cm) [st1,st,st+1]
(cm2,od2)= if isNothing $ odComment od
then case (pl,checkNext) of
((Just (_,stl,True,t):_),_)-> ( DM.delete (st1) cm,od{odComment=Just t,odStartLineComment=Just stl})
((_:Just (_,_,False,t):_),_) -> (DM.delete st cm,od{odComment=Just t})
((_:_:Just (_,_,False,t):_),True) -> (DM.delete (st+1) cm,od{odComment=Just t})
_ -> (cm,od)
else (cm,od)
(children,cm3)=runState (mapM (addComment checkNext) $ odChildren od2) cm2
put cm3
return od2{odChildren=children}
getHSEOutline _ = []
getModuleLocation :: (Module SrcSpanInfo, [Comment])
-> Maybe InFileSpan
getModuleLocation (Module _ (Just (ModuleHead _ (ModuleName l _) _ _)) _ _ _,_)=Just $ makeSpan l
getModuleLocation (Module l _ _ _ _,_)=Just $ makeSpan l
getModuleLocation _=Nothing
buildCommentMap :: DM.Map Int (Int,Int,Bool,T.Text)
-> Comment
-> DM.Map Int (Int,Int,Bool,T.Text)
buildCommentMap m (Comment _ ss txt)=let
txtTrimmed=dropWhile isSpace txt
st=srcSpanStartLine ss
stc=srcSpanStartColumn ss
in case txtTrimmed of
('|':rest)->DM.insert (srcSpanEndLine ss) (stc,srcSpanStartLine ss,True,T.pack $ dropWhile isSpace rest) m
('^':rest)->DM.insert st (1,st,False,T.pack $ dropWhile isSpace rest) m
_ | Just (stc2,sl,pos,t) <- DM.lookup (st1) m -> let
key = if pos then st else st1
in DM.insert key (stc2,sl,pos,T.concat [t,"\n",T.pack txt]) $ DM.delete (st1) m
_ -> m
getHSEImportExport :: (Module SrcSpanInfo, [Comment])
-> ([ExportDef],[ImportDef])
getHSEImportExport (Module _ mhead _ imps _,_)=(headExp mhead,impDefs imps)
where
headExp :: Maybe (ModuleHead SrcSpanInfo) ->[ExportDef]
headExp (Just (ModuleHead _ _ _ (Just (ExportSpecList _ exps))))=map expExp exps
headExp _ = []
expExp :: ExportSpec SrcSpanInfo -> ExportDef
#if MIN_VERSION_haskell_src_exts(1,16,0)
expExp (EVar l _ qn) = ExportDef (qnameDecl qn) IEVar (makeSpan l) []
#else
expExp (EVar l qn) = ExportDef (qnameDecl qn) IEVar (makeSpan l) []
#endif
expExp (EAbs l qn) = ExportDef (qnameDecl qn) IEAbs (makeSpan l) []
expExp (EThingAll l qn) = ExportDef (qnameDecl qn) IEThingAll (makeSpan l) []
expExp (EThingWith l qn cns) = ExportDef (qnameDecl qn) IEThingWith (makeSpan l) (map cnameDecl cns)
expExp (EModuleContents l mn) = ExportDef (mnnameDecl mn) IEModule (makeSpan l) []
impDefs :: [ImportDecl SrcSpanInfo] -> [ImportDef]
impDefs=map impDef
impDef :: ImportDecl SrcSpanInfo -> ImportDef
#if MIN_VERSION_haskell_src_exts(1,16,0)
impDef (ImportDecl l m qual _ _ pkg al specs)=ImportDef (mnnameDecl m) (fmap T.pack pkg) (makeSpan l) qual (hide specs) (alias al) (children specs)
#else
impDef (ImportDecl l m qual _ pkg al specs)=ImportDef (mnnameDecl m) (fmap T.pack pkg) (makeSpan l) qual (hide specs) (alias al) (children specs)
#endif
hide :: Maybe (ImportSpecList a)-> Bool
hide (Just (ImportSpecList _ b _))=b
hide _=False
alias :: Maybe (ModuleName a) -> T.Text
alias (Just mn)=mnnameDecl mn
alias Nothing =""
children :: Maybe (ImportSpecList SrcSpanInfo) -> Maybe [ImportSpecDef]
children (Just (ImportSpecList _ _ ss))=Just $ map child ss
children Nothing = Nothing
child :: ImportSpec SrcSpanInfo -> ImportSpecDef
#if MIN_VERSION_haskell_src_exts(1,16,0)
child (IVar l _ n)=ImportSpecDef (nameDecl n) IEVar (makeSpan l) []
#else
child (IVar l n)=ImportSpecDef (nameDecl n) IEVar (makeSpan l) []
#endif
child (IAbs l n)=ImportSpecDef (nameDecl n) IEAbs (makeSpan l) []
child (IThingAll l n) = ImportSpecDef (nameDecl n) IEThingAll (makeSpan l) []
child (IThingWith l n cns) = ImportSpecDef (nameDecl n) IEThingWith (makeSpan l) (map cnameDecl cns)
getHSEImportExport _=([],[])
nameDecl :: Name a -> T.Text
nameDecl (Ident _ s)=T.pack s
nameDecl (Symbol _ s)=T.pack s
cnameDecl :: CName a -> T.Text
cnameDecl (VarName _ s)=nameDecl s
cnameDecl (ConName _ s)=nameDecl s
qnameDecl :: QName a -> T.Text
qnameDecl (Qual _ _ n)=nameDecl n
qnameDecl (UnQual _ n)=nameDecl n
qnameDecl _ =""
mnnameDecl :: ModuleName a -> T.Text
mnnameDecl (ModuleName _ s)=T.pack s
makeSpan :: SrcSpanInfo -> InFileSpan
makeSpan si=let
sis=srcInfoSpan si
(sl,sc)=srcSpanStart sis
(el,ec)=srcSpanEnd sis
in InFileSpan (InFileLoc sl sc) (InFileLoc el ec)
knownExtensionNames :: [String]
knownExtensionNames = map show knownExtensions