module HsDev.Scan (
enumCabal, CompileFlag, ModuleToScan, ProjectToScan,
enumProject, enumDirectory,
scanProjectFile,
scanModule, upToDate, rescanModule, changedModule, changedModules
) where
import Control.Monad.Error
import qualified Data.Map as M
import Data.Traversable (traverse)
import Language.Haskell.GhcMod (defaultOptions)
import HsDev.Symbols
import HsDev.Database
import HsDev.Tools.GhcMod
import HsDev.Tools.GhcMod.InferType (inferTypes)
import HsDev.Inspect
import HsDev.Project
import HsDev.Util
enumCabal :: [String] -> Cabal -> ErrorT String IO [ModuleLocation]
enumCabal = list
type CompileFlag = String
type ModuleToScan = (ModuleLocation, [CompileFlag])
type ProjectToScan = (Project, [ModuleToScan])
enumProject :: Project -> ErrorT String IO ProjectToScan
enumProject p = do
p' <- loadProject p
srcs <- projectSources p'
return (p', [(FileModule (entity src) (Just p'), extensionsOpts (extensions src)) | src <- srcs])
enumDirectory :: FilePath -> ErrorT String IO ([ProjectToScan], [ModuleToScan])
enumDirectory dir = do
files <- liftException $ traverseDirectory dir
let
projects = filter cabalFile files
sources = filter haskellSource files
projs <- mapM (enumProject . project) projects
let
projPaths = map (projectPath . fst) projs
standalone = map (\f -> FileModule f Nothing) $ filter (\s -> not (any (`isParent` s) projPaths)) sources
return (projs, [(s, []) | s <- standalone])
scanProjectFile :: [String] -> FilePath -> ErrorT String IO Project
scanProjectFile _ f = do
proj <- (liftIO $ locateProject f) >>= maybe (throwError "Can't locate project") return
loadProject proj
scanModule :: [String] -> ModuleLocation -> ErrorT String IO InspectedModule
scanModule opts (FileModule f _) = inspectFile opts f >>= traverse (runGhcMod defaultOptions . inferTypes opts Cabal)
scanModule opts (CabalModule c p n) = browse opts c n p
scanModule _ (ModuleSource _) = throwError "Can inspect only modules in file or cabal"
upToDate :: [String] -> InspectedModule -> ErrorT String IO Bool
upToDate opts (Inspected insp m _) = case m of
FileModule f _ -> liftM (== insp) $ fileInspection f opts
CabalModule _ _ _ -> return $ insp == browseInspection opts
_ -> return False
rescanModule :: [String] -> InspectedModule -> ErrorT String IO (Maybe InspectedModule)
rescanModule opts im = do
up <- upToDate opts im
if up
then return Nothing
else fmap Just $ scanModule opts (inspectedId im)
changedModule :: Database -> [String] -> ModuleLocation -> ErrorT String IO Bool
changedModule db opts m = maybe (return True) (liftM not . upToDate opts) m' where
m' = M.lookup m (databaseModules db)
changedModules :: Database -> [String] -> [ModuleLocation] -> ErrorT String IO [ModuleLocation]
changedModules db opts ms = filterM (changedModule db opts) ms