ghc-mod/Language/Haskell/GhcMod/Find.hs

164 lines
4.8 KiB
Haskell
Raw Normal View History

{-# LANGUAGE CPP, BangPatterns #-}
2014-04-24 08:02:50 +00:00
module Language.Haskell.GhcMod.Find
#ifndef SPEC
2015-06-01 14:54:50 +00:00
( Symbol
, SymbolDb
2014-07-17 05:04:28 +00:00
, loadSymbolDb
, lookupSymbol
, dumpSymbol
, findSymbol
2014-07-18 06:13:30 +00:00
, lookupSym
, isOutdated
-- * Load 'SymbolDb' asynchronously
, AsyncSymbolDb
, newAsyncSymbolDb
, getAsyncSymbolDb
)
#endif
where
2014-04-24 08:02:50 +00:00
2014-05-11 22:40:00 +00:00
import Language.Haskell.GhcMod.Convert
import Language.Haskell.GhcMod.Gap
import Language.Haskell.GhcMod.Monad
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
import Language.Haskell.GhcMod.World
import qualified GHC as G
import Name
import Module
import Exception
import Control.Arrow
import Control.Applicative
import Control.Monad
import Control.Monad.Trans.Control
import Control.Concurrent
import Data.Function
import Data.List
import Data.Map (Map)
import qualified Data.Map as M
import System.Directory.ModTime
2015-08-03 01:09:56 +00:00
import Prelude
2014-04-24 08:02:50 +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'\].
data SymbolDb = SymbolDb
{ table :: Map Symbol [ModuleString]
, timestamp :: ModTime
} deriving (Show, Read)
isOutdated :: IOish m => SymbolDb -> GhcModT m Bool
isOutdated db =
isOlderThan (timestamp db) <$> timedPackageCaches
----------------------------------------------------------------
2014-07-18 06:42:05 +00:00
-- | Looking up 'SymbolDb' with 'Symbol' to \['ModuleString'\]
-- which will be concatenated. 'loadSymbolDb' is called internally.
findSymbol :: IOish m => Symbol -> GhcModT m String
findSymbol sym = loadSymbolDb >>= lookupSymbol sym
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]
lookupSym sym db = M.findWithDefault [] sym $ table db
---------------------------------------------------------------
2014-07-17 05:04:28 +00:00
-- | Loading a file and creates 'SymbolDb'.
loadSymbolDb :: IOish m => GhcModT m SymbolDb
loadSymbolDb = do
2015-06-01 14:54:50 +00:00
ghcMod <- liftIO ghcModExecutable
readProc <- gmReadProcess
(!db, !ts) <- first M.fromAscList . read <$> liftIO
(readProc ghcMod ["--verbose", "error", "dumpsym"] "")
return SymbolDb {
table = db
, timestamp = ts
2015-06-01 14:54:50 +00:00
}
----------------------------------------------------------------
-- used 'ghc-mod dumpsym'
-- | Dumps a tuple of
-- (\[('Symbol',\['ModuleString'\])\], 'ModTime') to stdout
dumpSymbol :: IOish m => GhcModT m String
dumpSymbol = do
timestamp <- liftIO getCurrentModTime
st <- runGmPkgGhc getGlobalSymbolTable
return . show $ (st, timestamp)
2014-07-22 20:54:18 +00:00
-- | Check whether given file is older than any file from the given set.
-- Returns True if given file does not exist.
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
-- | Browsing all functions in all system modules.
2015-06-01 14:54:50 +00:00
getGlobalSymbolTable :: LightGhc [(Symbol, [ModuleString])]
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) =
map (\name -> (getOccString name, modStr)) names
where
names = G.modInfoExports inf
modStr = ModuleString $ moduleNameString $ moduleName mdl
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
where
tieup x = (head (map fst x), map snd x)
----------------------------------------------------------------
data AsyncSymbolDb = AsyncSymbolDb (MVar (Either SomeException SymbolDb))
asyncLoadSymbolDb :: IOish m
=> MVar (Either SomeException SymbolDb)
-> GhcModT m ()
asyncLoadSymbolDb mv = void $
liftBaseWith $ \run -> forkIO $ void $ run $ do
edb <- gtry loadSymbolDb
liftIO $ putMVar mv edb
newAsyncSymbolDb :: IOish m => GhcModT m AsyncSymbolDb
newAsyncSymbolDb = do
mv <- liftIO newEmptyMVar
asyncLoadSymbolDb mv
return $ AsyncSymbolDb mv
getAsyncSymbolDb :: forall m. IOish m => AsyncSymbolDb -> GhcModT m SymbolDb
getAsyncSymbolDb (AsyncSymbolDb mv) = do
2016-01-04 05:02:30 +00:00
db <- liftIO $ handleEx <$> takeMVar mv
outdated <- isOutdated db
if outdated
then do
asyncLoadSymbolDb mv
2016-01-04 05:02:30 +00:00
liftIO $ handleEx <$> readMVar mv
else do
liftIO $ putMVar mv $ Right db
return db
where
2016-01-04 05:02:30 +00:00
handleEx edb =
case edb of
Left ex -> throw ex
2016-01-04 05:02:30 +00:00
Right db -> db