2016-01-09 14:27:21 +00:00
|
|
|
{-# LANGUAGE CPP, DeriveGeneric #-}
|
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
|
2016-01-04 04:27:31 +00:00
|
|
|
-- * Load 'SymbolDb' asynchronously
|
|
|
|
, AsyncSymbolDb
|
|
|
|
, newAsyncSymbolDb
|
|
|
|
, getAsyncSymbolDb
|
2014-07-17 14:15:02 +00:00
|
|
|
)
|
|
|
|
#endif
|
|
|
|
where
|
2014-04-24 08:02:50 +00:00
|
|
|
|
2014-05-11 22:40:00 +00:00
|
|
|
import Language.Haskell.GhcMod.Convert
|
2016-01-04 04:27:31 +00:00
|
|
|
import Language.Haskell.GhcMod.Gap
|
2014-07-16 09:14:12 +00:00
|
|
|
import Language.Haskell.GhcMod.Monad
|
2016-01-04 04:27:31 +00:00
|
|
|
import Language.Haskell.GhcMod.Output
|
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
|
2016-01-04 04:27:31 +00:00
|
|
|
import Language.Haskell.GhcMod.World
|
|
|
|
|
|
|
|
import qualified GHC as G
|
|
|
|
import Name
|
|
|
|
import Module
|
|
|
|
import Exception
|
|
|
|
|
|
|
|
import Control.Applicative
|
|
|
|
import Control.Monad
|
|
|
|
import Control.Monad.Trans.Control
|
|
|
|
import Control.Concurrent
|
2016-01-09 14:27:21 +00:00
|
|
|
import Control.DeepSeq
|
2016-01-04 04:27:31 +00:00
|
|
|
import Data.Function
|
|
|
|
import Data.List
|
2016-01-09 14:27:21 +00:00
|
|
|
import qualified Data.ByteString.Lazy as BS
|
|
|
|
import Data.Binary
|
|
|
|
import GHC.Generics (Generic)
|
2016-01-04 04:27:31 +00:00
|
|
|
import Data.Map (Map)
|
|
|
|
import qualified Data.Map as M
|
2015-11-26 13:48:26 +00:00
|
|
|
import System.Directory.ModTime
|
2015-08-03 01:09:56 +00:00
|
|
|
import Prelude
|
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]
|
2016-01-05 10:38:25 +00:00
|
|
|
, timestamp :: ModTime
|
2016-01-09 14:27:21 +00:00
|
|
|
} deriving (Generic)
|
|
|
|
|
|
|
|
instance Binary SymbolDb
|
|
|
|
instance NFData SymbolDb
|
2014-09-20 03:25:46 +00:00
|
|
|
|
2015-08-07 04:47:34 +00:00
|
|
|
isOutdated :: IOish m => SymbolDb -> GhcModT m Bool
|
2015-06-01 12:59:38 +00:00
|
|
|
isOutdated db =
|
2016-01-05 10:38:25 +00:00
|
|
|
isOlderThan (timestamp db) <$> timedPackageCaches
|
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
|
2016-01-05 10:38:25 +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'.
|
2016-01-05 10:38:25 +00:00
|
|
|
loadSymbolDb :: IOish m => GhcModT m SymbolDb
|
|
|
|
loadSymbolDb = do
|
2015-06-01 14:54:50 +00:00
|
|
|
ghcMod <- liftIO ghcModExecutable
|
2016-01-09 14:27:21 +00:00
|
|
|
readProc <- gmReadProcess'
|
|
|
|
out <- liftIO $ readProc ghcMod ["--verbose", "error", "dumpsym"] ""
|
|
|
|
return $!! decode out
|
2014-07-16 09:14:12 +00:00
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
|
|
-- used 'ghc-mod dumpsym'
|
|
|
|
|
2016-01-09 14:27:21 +00:00
|
|
|
-- | Dumps a 'Binary' representation of 'SymbolDb' to stdout
|
|
|
|
dumpSymbol :: IOish m => GhcModT m ()
|
2016-01-05 10:38:25 +00:00
|
|
|
dumpSymbol = do
|
2016-01-09 14:27:21 +00:00
|
|
|
ts <- liftIO getCurrentModTime
|
2016-01-05 10:38:25 +00:00
|
|
|
st <- runGmPkgGhc getGlobalSymbolTable
|
2016-01-09 14:27:21 +00:00
|
|
|
liftIO . BS.putStr $ encode SymbolDb {
|
|
|
|
table = M.fromAscList st
|
|
|
|
, timestamp = ts
|
|
|
|
}
|
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.
|
2016-01-05 10:38:25 +00:00
|
|
|
isOlderThan :: ModTime -> [TimedFile] -> Bool
|
|
|
|
isOlderThan tCache files =
|
|
|
|
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)
|
2016-01-04 04:27:31 +00:00
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
|
|
|
2016-01-05 10:38:25 +00:00
|
|
|
data AsyncSymbolDb = AsyncSymbolDb (MVar (Either SomeException SymbolDb))
|
2016-01-04 04:27:31 +00:00
|
|
|
|
|
|
|
asyncLoadSymbolDb :: IOish m
|
2016-01-05 10:38:25 +00:00
|
|
|
=> MVar (Either SomeException SymbolDb)
|
2016-01-04 04:27:31 +00:00
|
|
|
-> GhcModT m ()
|
2016-01-05 10:38:25 +00:00
|
|
|
asyncLoadSymbolDb mv = void $
|
2016-01-04 04:27:31 +00:00
|
|
|
liftBaseWith $ \run -> forkIO $ void $ run $ do
|
2016-01-05 10:38:25 +00:00
|
|
|
edb <- gtry loadSymbolDb
|
2016-01-04 04:27:31 +00:00
|
|
|
liftIO $ putMVar mv edb
|
|
|
|
|
2016-01-05 10:38:25 +00:00
|
|
|
newAsyncSymbolDb :: IOish m => GhcModT m AsyncSymbolDb
|
|
|
|
newAsyncSymbolDb = do
|
2016-01-04 04:27:31 +00:00
|
|
|
mv <- liftIO newEmptyMVar
|
2016-01-05 10:38:25 +00:00
|
|
|
asyncLoadSymbolDb mv
|
|
|
|
return $ AsyncSymbolDb mv
|
2016-01-04 04:27:31 +00:00
|
|
|
|
|
|
|
getAsyncSymbolDb :: forall m. IOish m => AsyncSymbolDb -> GhcModT m SymbolDb
|
2016-01-05 10:38:25 +00:00
|
|
|
getAsyncSymbolDb (AsyncSymbolDb mv) = do
|
2016-01-04 05:02:30 +00:00
|
|
|
db <- liftIO $ handleEx <$> takeMVar mv
|
|
|
|
outdated <- isOutdated db
|
|
|
|
if outdated
|
|
|
|
then do
|
2016-01-05 10:38:25 +00:00
|
|
|
asyncLoadSymbolDb mv
|
2016-01-04 05:02:30 +00:00
|
|
|
liftIO $ handleEx <$> readMVar mv
|
|
|
|
else do
|
|
|
|
liftIO $ putMVar mv $ Right db
|
|
|
|
return db
|
2016-01-04 04:27:31 +00:00
|
|
|
where
|
2016-01-04 05:02:30 +00:00
|
|
|
handleEx edb =
|
2016-01-04 04:27:31 +00:00
|
|
|
case edb of
|
|
|
|
Left ex -> throw ex
|
2016-01-04 05:02:30 +00:00
|
|
|
Right db -> db
|