module Language.Haskell.Modules.Common
( groupBy'
, withCurrentDirectory
, ModuleResult(..)
, doResult
, reportResult
, fixExport
) where
import Control.Exception.Lifted as IO (bracket, catch, throw)
import Control.Monad.Trans (MonadIO, liftIO)
import Control.Monad.Trans.Control (MonadBaseControl)
import Data.List (groupBy, sortBy)
import Data.Monoid ((<>))
import Data.Sequence as Seq (Seq, (|>))
import qualified Language.Haskell.Exts.Annotated as A (ExportSpec)
import Language.Haskell.Exts.Annotated.Simplify (sExportSpec)
import Language.Haskell.Exts.Pretty (prettyPrint)
import qualified Language.Haskell.Exts.Syntax as S (ExportSpec(EModuleContents), ModuleName(..))
import Language.Haskell.Modules.ModuVerse (delName, ModuVerse, putModuleAnew, unloadModule)
import Language.Haskell.Modules.SourceDirs (modulePath, PathKey(..), APath(..))
import Language.Haskell.Modules.Util.DryIO (createDirectoryIfMissing, MonadDryRun(..), removeFileIfPresent, replaceFile, tildeBackup)
import Language.Haskell.Modules.Util.QIO (MonadVerbosity(..), qLnPutStr, quietly)
import Prelude hiding (writeFile, writeFile)
import System.Directory (getCurrentDirectory, setCurrentDirectory)
import System.FilePath (dropExtension, takeDirectory)
import System.IO.Error (isDoesNotExistError)
toEq :: Ord a => (a -> a -> Ordering) -> (a -> a -> Bool)
toEq cmp a b =
case cmp a b of
EQ -> True
_ -> False
groupBy' :: Ord a => (a -> a -> Ordering) -> [a] -> [[a]]
groupBy' cmp xs = groupBy (toEq cmp) $ sortBy cmp xs
withCurrentDirectory :: (MonadIO m, MonadBaseControl IO m) => FilePath -> m a -> m a
withCurrentDirectory path action =
bracket (liftIO getCurrentDirectory >>= \ save -> liftIO (setCurrentDirectory path) >> return save)
(liftIO . setCurrentDirectory)
(const action)
data ModuleResult
= Unchanged S.ModuleName PathKey
| ToBeRemoved S.ModuleName PathKey
| JustRemoved S.ModuleName PathKey
| ToBeModified S.ModuleName PathKey String
| JustModified S.ModuleName PathKey
| ToBeCreated S.ModuleName String
| JustCreated S.ModuleName PathKey
deriving (Show, Eq, Ord)
reportResult :: ModuleResult -> String
reportResult (Unchanged _ key) = "unchanged " ++ show key
reportResult (JustModified _ key) = "modified " ++ show key
reportResult (JustCreated _ key) = "created " ++ show key
reportResult (JustRemoved _ key) = "removed " ++ show key
reportResult (ToBeModified _ key _) = "to be modified " ++ show key
reportResult (ToBeCreated name _) = "to be created " ++ show name
reportResult (ToBeRemoved _ key) = "to be removed: " ++ show key
doResult :: (ModuVerse m, MonadDryRun m, MonadVerbosity m) => ModuleResult -> m ModuleResult
doResult x@(Unchanged name _) =
do quietly (qLnPutStr ("unchanged: " ++ prettyPrint name))
return x
doResult (ToBeRemoved name key) =
do qLnPutStr ("removing: " ++ prettyPrint name)
let path = unPathKey key
unloadModule key
removeFileIfPresent path `IO.catch` (\ (e :: IOError) -> if isDoesNotExistError e then return () else throw e)
delName name
return $ JustRemoved name key
doResult (ToBeModified name key text) =
do qLnPutStr ("modifying: " ++ prettyPrint name)
let path = unPathKey key
replaceFile tildeBackup path text
_key <- putModuleAnew name
return $ JustModified name key
doResult (ToBeCreated name text) =
do qLnPutStr ("creating: " ++ prettyPrint name)
path <- modulePath "hs" name
createDirectoryIfMissing True (takeDirectory . dropExtension . unAPath $ path)
replaceFile tildeBackup (unAPath path) text
key <- putModuleAnew name
return $ JustCreated name key
doResult x@(JustCreated {}) = return x
doResult x@(JustModified {}) = return x
doResult x@(JustRemoved {}) = return x
fixExport :: [S.ModuleName] -> S.ModuleName -> S.ModuleName
-> A.ExportSpec l -> String -> String -> String -> Seq String -> Seq String
fixExport inNames outName thisName e pref s suff r =
case sExportSpec e of
S.EModuleContents name
| thisName == outName && elem name inNames -> r
| elem name inNames -> r |> pref <> prettyPrint (S.EModuleContents outName) <> suff
_ -> r |> pref <> s <> suff