module HsDev.Symbols (
locateProject, searchProject,
locateSourceDir,
standaloneInfo,
moduleOpts, projectTargetOpts,
setTag, hasTag, removeTag, dropTags,
inspectTag, inspectUntag,
module HsDev.Symbols.Types,
module HsDev.Symbols.Class,
module HsDev.Symbols.Documented,
module HsDev.Symbols.HaskellNames
) where
import Control.Applicative
import Control.Lens
import Control.Monad.Trans.Maybe
import Control.Monad.Except
import Control.Monad.State
import Data.List
import Data.Maybe (fromMaybe, listToMaybe, catMaybes)
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import System.Directory
import System.FilePath
import HsDev.Symbols.Types
import HsDev.Symbols.Class
import HsDev.Symbols.Documented (Documented(..))
import HsDev.Symbols.HaskellNames
import HsDev.Util (searchPath, uniqueBy, directoryContents)
import System.Directory.Paths
locateProject :: FilePath -> IO (Maybe Project)
locateProject file = do
file' <- canonicalizePath file
isDir <- doesDirectoryExist file'
if isDir then locateHere file' else locateParent (takeDirectory file')
where
locateHere p = do
cts <- filter (not . null . takeBaseName) <$> directoryContents p
return $ fmap (project . (p </>)) $ find ((== ".cabal") . takeExtension) cts
locateParent dir = do
cts <- filter (not . null . takeBaseName) <$> directoryContents dir
case find ((== ".cabal") . takeExtension) cts of
Nothing -> if isDrive dir then return Nothing else locateParent (takeDirectory dir)
Just cabalf -> return $ Just $ project (dir </> cabalf)
searchProject :: FilePath -> IO (Maybe Project)
searchProject file = runMaybeT $ searchPath file (MaybeT . locateProject) <|> mzero
locateSourceDir :: FilePath -> IO (Maybe (Extensions Path))
locateSourceDir f = runMaybeT $ do
file <- liftIO $ canonicalizePath f
p <- MaybeT $ locateProject file
proj <- lift $ loadProject p
MaybeT $ return $ findSourceDir proj (fromFilePath file)
standaloneInfo :: [PackageConfig] -> Module -> Info
standaloneInfo pkgs m = mempty { _infoDepends = pkgDeps ^.. each . package . packageName } where
pkgDeps = catMaybes [M.lookup mdep pkgMap >>= listToMaybe | mdep <- "Prelude" : imps]
pkgMap = M.unionsWith mergePkgs [M.singleton m' [p] | p <- pkgs, m' <- view packageModules p]
mergePkgs ls rs = if null es then hs else es where
(es, hs) = partition (view packageExposed) $ uniqueBy (view package) (ls ++ rs)
imps = delete (view (moduleId . moduleName) m) (m ^.. moduleImports . each . importName)
moduleOpts :: [PackageConfig] -> Module -> [String]
moduleOpts pkgs m = case view (moduleId . moduleLocation) m of
FileModule file proj -> concat [
hidePackages,
targetOpts absInfo]
where
infos' = maybe [standaloneInfo pkgs m] (`fileTargets` file) proj
info' = over infoDepends (filter validDep) (mconcat $ selfInfo : infos')
absInfo = maybe id (absolutise . view projectPath) proj info'
selfInfo
| proj ^? _Just . projectName `elem` map Just (infos' ^.. each . infoDepends . each) = fromMaybe mempty $
proj ^? _Just . projectDescription . _Just . projectLibrary . _Just . libraryBuildInfo
| otherwise = mempty
validDep d = d `elem` pkgs'
pkgs' = pkgs ^.. each . package . packageName
hidePackages
| null (info' ^. infoDepends) = []
| otherwise = ["-hide-all-packages"]
_ -> []
projectTargetOpts :: [PackageConfig] -> Project -> Info -> [String]
projectTargetOpts pkgs proj info = concat [hidePackages, targetOpts absInfo] where
info' = over infoDepends (filter validDep) (selfInfo `mappend` info)
absInfo = absolutise (view projectPath proj) info'
selfInfo
| proj ^. projectName `elem` (info ^.. infoDepends . each) = fromMaybe mempty $
proj ^? projectDescription . _Just . projectLibrary . _Just . libraryBuildInfo
| otherwise = mempty
validDep d = d `elem` pkgs'
pkgs' = pkgs ^.. each . package . packageName
hidePackages
| null (info' ^. infoDepends) = []
| otherwise = ["-hide-all-packages"]
setTag :: Ord t => t -> Inspected i t a -> Inspected i t a
setTag tag' = over inspectionTags (S.insert tag')
hasTag :: Ord t => t -> Inspected i t a -> Bool
hasTag tag' = has (inspectionTags . ix tag')
removeTag :: Ord t => t -> Inspected i t a -> Inspected i t a
removeTag tag' = over inspectionTags (S.delete tag')
dropTags :: Inspected i t a -> Inspected i t a
dropTags = set inspectionTags S.empty
inspectTag :: (Monad m, Ord t) => t -> InspectM k t m a -> InspectM k t m a
inspectTag tag' act = act <* modify (over _2 (S.insert tag'))
inspectUntag :: (Monad m, Ord t) => t -> InspectM k t m a -> InspectM k t m a
inspectUntag tag' act = act <* modify (over _2 (S.delete tag'))