2015-06-01 12:59:38 +00:00
|
|
|
{-# LANGUAGE CPP, BangPatterns, DoAndIfThenElse #-}
|
2014-04-24 08:02:50 +00:00
|
|
|
|
2014-07-17 14:15:02 +00:00
|
|
|
module Language.Haskell.GhcMod.Find
|
|
|
|
#ifndef SPEC
|
2015-06-01 14:54:50 +00:00
|
|
|
( Symbol
|
2014-07-16 09:14:12 +00:00
|
|
|
, SymbolDb
|
2014-07-17 05:04:28 +00:00
|
|
|
, loadSymbolDb
|
2014-07-16 09:14:12 +00:00
|
|
|
, lookupSymbol
|
|
|
|
, dumpSymbol
|
|
|
|
, findSymbol
|
2014-07-18 06:13:30 +00:00
|
|
|
, lookupSym
|
2014-09-20 03:25:46 +00:00
|
|
|
, isOutdated
|
2014-07-17 14:15:02 +00:00
|
|
|
)
|
|
|
|
#endif
|
|
|
|
where
|
2014-04-24 08:02:50 +00:00
|
|
|
|
2014-07-16 09:14:12 +00:00
|
|
|
import Control.Applicative ((<$>))
|
2015-06-01 12:59:38 +00:00
|
|
|
import Control.Monad (when, void, (<=<))
|
2014-04-24 08:02:50 +00:00
|
|
|
import Data.Function (on)
|
|
|
|
import Data.List (groupBy, sort)
|
|
|
|
import qualified GHC as G
|
2014-05-11 22:40:00 +00:00
|
|
|
import Language.Haskell.GhcMod.Convert
|
2015-06-01 12:59:38 +00:00
|
|
|
import Language.Haskell.GhcMod.Gap (listVisibleModules)
|
2014-07-16 09:14:12 +00:00
|
|
|
import Language.Haskell.GhcMod.Monad
|
2015-06-01 12:59:38 +00:00
|
|
|
import Language.Haskell.GhcMod.PathsAndFiles
|
2014-04-24 12:08:45 +00:00
|
|
|
import Language.Haskell.GhcMod.Types
|
2014-09-23 04:47:32 +00:00
|
|
|
import Language.Haskell.GhcMod.Utils
|
2015-06-01 12:59:38 +00:00
|
|
|
import Language.Haskell.GhcMod.World (timedPackageCaches)
|
2014-07-16 09:14:12 +00:00
|
|
|
import Name (getOccString)
|
2015-03-03 20:12:43 +00:00
|
|
|
import Module (moduleName)
|
2014-09-23 04:47:32 +00:00
|
|
|
import System.Directory (doesFileExist, getModificationTime)
|
2015-06-01 12:59:38 +00:00
|
|
|
import System.FilePath ((</>))
|
2014-07-16 09:14:12 +00:00
|
|
|
import System.IO
|
2014-04-24 08:02:50 +00:00
|
|
|
|
2015-03-05 15:50:06 +00:00
|
|
|
import Data.Map (Map)
|
|
|
|
import qualified Data.Map as M
|
2014-04-24 08:02:50 +00:00
|
|
|
|
2014-07-16 09:14:12 +00:00
|
|
|
----------------------------------------------------------------
|
|
|
|
|
2014-07-17 05:04:28 +00:00
|
|
|
-- | Type of function and operation names.
|
2014-04-24 08:02:50 +00:00
|
|
|
type Symbol = String
|
2014-07-17 05:04:28 +00:00
|
|
|
-- | Database from 'Symbol' to \['ModuleString'\].
|
2015-06-01 12:59:38 +00:00
|
|
|
data SymbolDb = SymbolDb
|
|
|
|
{ table :: Map Symbol [ModuleString]
|
2014-09-20 03:25:46 +00:00
|
|
|
, symbolDbCachePath :: FilePath
|
|
|
|
} deriving (Show)
|
|
|
|
|
2015-06-01 12:59:38 +00:00
|
|
|
isOutdated :: (GmEnv m, IOish m) => SymbolDb -> m Bool
|
|
|
|
isOutdated db =
|
|
|
|
liftIO . (isOlderThan (symbolDbCachePath db) <=< timedPackageCaches) =<< cradle
|
2014-07-16 09:14:12 +00:00
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
|
|
|
2014-07-18 06:42:05 +00:00
|
|
|
-- | Looking up 'SymbolDb' with 'Symbol' to \['ModuleString'\]
|
|
|
|
-- which will be concatenated. 'loadSymbolDb' is called internally.
|
2014-07-12 09:16:16 +00:00
|
|
|
findSymbol :: IOish m => Symbol -> GhcModT m String
|
2014-08-28 09:54:01 +00:00
|
|
|
findSymbol sym = loadSymbolDb >>= lookupSymbol sym
|
2014-07-16 09:14:12 +00:00
|
|
|
|
2014-07-17 05:04:28 +00:00
|
|
|
-- | Looking up 'SymbolDb' with 'Symbol' to \['ModuleString'\]
|
|
|
|
-- which will be concatenated.
|
2014-07-18 06:13:30 +00:00
|
|
|
lookupSymbol :: IOish m => Symbol -> SymbolDb -> GhcModT m String
|
|
|
|
lookupSymbol sym db = convert' $ lookupSym sym db
|
|
|
|
|
|
|
|
lookupSym :: Symbol -> SymbolDb -> [ModuleString]
|
2015-06-01 12:59:38 +00:00
|
|
|
lookupSym sym db = M.findWithDefault [] sym $ table db
|
2014-07-16 09:14:12 +00:00
|
|
|
|
|
|
|
---------------------------------------------------------------
|
|
|
|
|
2014-07-17 05:04:28 +00:00
|
|
|
-- | Loading a file and creates 'SymbolDb'.
|
2014-10-14 17:52:58 +00:00
|
|
|
loadSymbolDb :: IOish m => GhcModT m SymbolDb
|
2014-09-20 03:25:46 +00:00
|
|
|
loadSymbolDb = do
|
2015-06-01 14:54:50 +00:00
|
|
|
ghcMod <- liftIO ghcModExecutable
|
|
|
|
tmpdir <- cradleTempDir <$> cradle
|
|
|
|
file <- liftIO $ chop <$> readProcess ghcMod ["dumpsym", tmpdir] ""
|
|
|
|
!db <- M.fromAscList . map conv . lines <$> liftIO (readFile file)
|
|
|
|
return $ SymbolDb
|
|
|
|
{ table = db
|
|
|
|
, symbolDbCachePath = file
|
|
|
|
}
|
2014-04-24 08:02:50 +00:00
|
|
|
where
|
2015-06-01 14:54:50 +00:00
|
|
|
conv :: String -> (Symbol, [ModuleString])
|
2014-07-16 09:14:12 +00:00
|
|
|
conv = read
|
2015-06-01 12:59:38 +00:00
|
|
|
chop :: String -> String
|
2014-07-16 09:14:12 +00:00
|
|
|
chop "" = ""
|
|
|
|
chop xs = init xs
|
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
|
|
-- used 'ghc-mod dumpsym'
|
|
|
|
|
2014-07-17 05:04:28 +00:00
|
|
|
-- | Dumping a set of ('Symbol',\['ModuleString'\]) to a file
|
|
|
|
-- if the file does not exist or is invalid.
|
|
|
|
-- The file name is printed.
|
2014-08-06 17:37:59 +00:00
|
|
|
|
2014-10-14 17:52:58 +00:00
|
|
|
dumpSymbol :: IOish m => FilePath -> GhcModT m String
|
2015-06-01 12:59:38 +00:00
|
|
|
dumpSymbol dir = do
|
|
|
|
crdl <- cradle
|
|
|
|
runGmPkgGhc $ do
|
|
|
|
create <- liftIO $ isOlderThan cache =<< timedPackageCaches crdl
|
|
|
|
when create $
|
|
|
|
liftIO . writeSymbolCache cache =<< getGlobalSymbolTable
|
2014-07-22 20:54:18 +00:00
|
|
|
return $ unlines [cache]
|
2015-06-01 12:59:38 +00:00
|
|
|
where
|
|
|
|
cache = dir </> symbolCacheFile
|
2014-07-22 20:54:18 +00:00
|
|
|
|
|
|
|
writeSymbolCache :: FilePath
|
2015-06-01 14:54:50 +00:00
|
|
|
-> [(Symbol, [ModuleString])]
|
2014-07-22 20:54:18 +00:00
|
|
|
-> IO ()
|
2014-08-14 02:11:02 +00:00
|
|
|
writeSymbolCache cache sm =
|
2014-07-22 20:54:18 +00:00
|
|
|
void . withFile cache WriteMode $ \hdl ->
|
2015-06-01 14:54:50 +00:00
|
|
|
mapM (hPrint hdl) sm
|
2014-07-22 20:54:18 +00:00
|
|
|
|
2015-06-01 12:59:38 +00:00
|
|
|
-- | Check whether given file is older than any file from the given set.
|
|
|
|
-- Returns True if given file does not exist.
|
|
|
|
isOlderThan :: FilePath -> [TimedFile] -> IO Bool
|
|
|
|
isOlderThan cache files = do
|
|
|
|
exist <- doesFileExist cache
|
|
|
|
if not exist
|
|
|
|
then return True
|
|
|
|
else do
|
|
|
|
tCache <- getModificationTime cache
|
|
|
|
return $ any (tCache <=) $ map tfTime files -- including equal just in case
|
2014-05-14 16:54:56 +00:00
|
|
|
|
2015-03-03 20:12:43 +00:00
|
|
|
-- | Browsing all functions in all system modules.
|
2015-06-01 14:54:50 +00:00
|
|
|
getGlobalSymbolTable :: LightGhc [(Symbol, [ModuleString])]
|
2015-03-03 20:12:43 +00:00
|
|
|
getGlobalSymbolTable = do
|
2015-06-01 14:54:50 +00:00
|
|
|
df <- G.getSessionDynFlags
|
|
|
|
let mods = listVisibleModules df
|
|
|
|
moduleInfos <- mapM G.getModuleInfo mods
|
|
|
|
return $ collectModules
|
|
|
|
$ extractBindings `concatMap` (moduleInfos `zip` mods)
|
2014-07-23 22:08:47 +00:00
|
|
|
|
2015-01-16 14:47:56 +00:00
|
|
|
extractBindings :: (Maybe G.ModuleInfo, G.Module)
|
2014-07-23 22:08:47 +00:00
|
|
|
-> [(Symbol, ModuleString)]
|
2015-06-01 14:54:50 +00:00
|
|
|
extractBindings (Nothing, _) = []
|
|
|
|
extractBindings (Just inf, mdl) =
|
2015-06-01 15:10:37 +00:00
|
|
|
map (\name -> (getOccString name, modStr)) names
|
2014-07-16 09:14:12 +00:00
|
|
|
where
|
2015-06-01 15:10:37 +00:00
|
|
|
names = G.modInfoExports inf
|
|
|
|
modStr = ModuleString $ moduleNameString $ moduleName mdl
|
2014-07-16 09:14:12 +00:00
|
|
|
|
2015-06-01 14:54:50 +00:00
|
|
|
collectModules :: [(Symbol, ModuleString)]
|
|
|
|
-> [(Symbol, [ModuleString])]
|
2014-07-23 22:08:47 +00:00
|
|
|
collectModules = map tieup . groupBy ((==) `on` fst) . sort
|
2014-07-16 09:14:12 +00:00
|
|
|
where
|
|
|
|
tieup x = (head (map fst x), map snd x)
|