{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
module HieDb.Query where
import Algebra.Graph.AdjacencyMap (AdjacencyMap, edges, vertexSet, vertices, overlay)
import Algebra.Graph.AdjacencyMap.Algorithm (dfs)
import Algebra.Graph.Export.Dot hiding ((:=))
import qualified Algebra.Graph.Export.Dot as G
import GHC
import Compat.HieTypes
import Module
import Name
import System.Directory
import System.FilePath
import Control.Monad (foldM, forM_)
import Control.Monad.IO.Class
import Data.List (foldl', intercalate)
import Data.List.NonEmpty (NonEmpty(..))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Set (Set)
import qualified Data.Set as Set
import qualified Data.Text as T
import Data.IORef
import Database.SQLite.Simple
import HieDb.Dump (sourceCode)
import HieDb.Types
import HieDb.Utils
import qualified HieDb.Html as Html
getAllIndexedMods :: HieDb -> IO [HieModuleRow]
getAllIndexedMods :: HieDb -> IO [HieModuleRow]
getAllIndexedMods (HieDb -> Connection
getConn -> Connection
conn) = Connection -> Query -> IO [HieModuleRow]
forall r. FromRow r => Connection -> Query -> IO [r]
query_ Connection
conn Query
"SELECT * FROM mods"
resolveUnitId :: HieDb -> ModuleName -> IO (Either HieDbErr UnitId)
resolveUnitId :: HieDb -> ModuleName -> IO (Either HieDbErr UnitId)
resolveUnitId (HieDb -> Connection
getConn -> Connection
conn) ModuleName
mn = do
[ModuleInfo]
luid <- Connection -> Query -> Only ModuleName -> IO [ModuleInfo]
forall q r.
(ToRow q, FromRow r) =>
Connection -> Query -> q -> IO [r]
query Connection
conn Query
"SELECT mod, unit, is_boot, hs_src, is_real, hash FROM mods WHERE mod = ? and is_boot = 0" (ModuleName -> Only ModuleName
forall a. a -> Only a
Only ModuleName
mn)
Either HieDbErr UnitId -> IO (Either HieDbErr UnitId)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either HieDbErr UnitId -> IO (Either HieDbErr UnitId))
-> Either HieDbErr UnitId -> IO (Either HieDbErr UnitId)
forall a b. (a -> b) -> a -> b
$ case [ModuleInfo]
luid of
[] -> HieDbErr -> Either HieDbErr UnitId
forall a b. a -> Either a b
Left (HieDbErr -> Either HieDbErr UnitId)
-> HieDbErr -> Either HieDbErr UnitId
forall a b. (a -> b) -> a -> b
$ ModuleName -> Maybe UnitId -> HieDbErr
NotIndexed ModuleName
mn Maybe UnitId
forall a. Maybe a
Nothing
[ModuleInfo
x] -> UnitId -> Either HieDbErr UnitId
forall a b. b -> Either a b
Right (UnitId -> Either HieDbErr UnitId)
-> UnitId -> Either HieDbErr UnitId
forall a b. (a -> b) -> a -> b
$ ModuleInfo -> UnitId
modInfoUnit ModuleInfo
x
(ModuleInfo
x:[ModuleInfo]
xs) -> HieDbErr -> Either HieDbErr UnitId
forall a b. a -> Either a b
Left (HieDbErr -> Either HieDbErr UnitId)
-> HieDbErr -> Either HieDbErr UnitId
forall a b. (a -> b) -> a -> b
$ NonEmpty ModuleInfo -> HieDbErr
AmbiguousUnitId (NonEmpty ModuleInfo -> HieDbErr)
-> NonEmpty ModuleInfo -> HieDbErr
forall a b. (a -> b) -> a -> b
$ ModuleInfo
x ModuleInfo -> [ModuleInfo] -> NonEmpty ModuleInfo
forall a. a -> [a] -> NonEmpty a
:| [ModuleInfo]
xs
findReferences :: HieDb -> Bool -> OccName -> Maybe ModuleName -> Maybe UnitId -> [FilePath] -> IO [Res RefRow]
findReferences :: HieDb
-> Bool
-> OccName
-> Maybe ModuleName
-> Maybe UnitId
-> [FilePath]
-> IO [Res RefRow]
findReferences (HieDb -> Connection
getConn -> Connection
conn) Bool
isReal OccName
occ Maybe ModuleName
mn Maybe UnitId
uid [FilePath]
exclude =
Connection -> Query -> [NamedParam] -> IO [Res RefRow]
forall r.
FromRow r =>
Connection -> Query -> [NamedParam] -> IO [r]
queryNamed Connection
conn Query
thisQuery ([Text
":occ" Text -> OccName -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= OccName
occ, Text
":mod" Text -> Maybe ModuleName -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= Maybe ModuleName
mn, Text
":unit" Text -> Maybe UnitId -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= Maybe UnitId
uid, Text
":real" Text -> Bool -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= Bool
isReal] [NamedParam] -> [NamedParam] -> [NamedParam]
forall a. [a] -> [a] -> [a]
++ [NamedParam]
excludedFields)
where
excludedFields :: [NamedParam]
excludedFields = (Int -> FilePath -> NamedParam)
-> [Int] -> [FilePath] -> [NamedParam]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (\Int
n FilePath
f -> (Text
":exclude" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> FilePath -> Text
T.pack (Int -> FilePath
forall a. Show a => a -> FilePath
show Int
n)) Text -> FilePath -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= FilePath
f) [Int
1 :: Int ..] [FilePath]
exclude
thisQuery :: Query
thisQuery =
Query
"SELECT refs.*,mods.mod,mods.unit,mods.is_boot,mods.hs_src,mods.is_real,mods.hash \
\FROM refs JOIN mods USING (hieFile) \
\WHERE refs.occ = :occ AND (:mod IS NULL OR refs.mod = :mod) AND (:unit is NULL OR refs.unit = :unit) AND \
\((NOT :real) OR (mods.is_real AND mods.hs_src IS NOT NULL))"
Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> Query
" AND mods.hs_src NOT IN (" Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> Text -> Query
Query (Text -> [Text] -> Text
T.intercalate Text
"," ((NamedParam -> Text) -> [NamedParam] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (\(Text
l := v
_) -> Text
l) [NamedParam]
excludedFields)) Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> Query
")"
lookupHieFile :: HieDb -> ModuleName -> UnitId -> IO (Maybe HieModuleRow)
lookupHieFile :: HieDb -> ModuleName -> UnitId -> IO (Maybe HieModuleRow)
lookupHieFile (HieDb -> Connection
getConn -> Connection
conn) ModuleName
mn UnitId
uid = do
[HieModuleRow]
files <- Connection -> Query -> (ModuleName, UnitId) -> IO [HieModuleRow]
forall q r.
(ToRow q, FromRow r) =>
Connection -> Query -> q -> IO [r]
query Connection
conn Query
"SELECT * FROM mods WHERE mod = ? AND unit = ? AND is_boot = 0" (ModuleName
mn, UnitId
uid)
case [HieModuleRow]
files of
[] -> Maybe HieModuleRow -> IO (Maybe HieModuleRow)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe HieModuleRow
forall a. Maybe a
Nothing
[HieModuleRow
x] -> Maybe HieModuleRow -> IO (Maybe HieModuleRow)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe HieModuleRow -> IO (Maybe HieModuleRow))
-> Maybe HieModuleRow -> IO (Maybe HieModuleRow)
forall a b. (a -> b) -> a -> b
$ HieModuleRow -> Maybe HieModuleRow
forall a. a -> Maybe a
Just HieModuleRow
x
[HieModuleRow]
xs ->
FilePath -> IO (Maybe HieModuleRow)
forall a. HasCallStack => FilePath -> a
error (FilePath -> IO (Maybe HieModuleRow))
-> FilePath -> IO (Maybe HieModuleRow)
forall a b. (a -> b) -> a -> b
$ FilePath
"DB invariant violated, (mod,unit) in mods not unique: "
FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ (FilePath, UnitId) -> FilePath
forall a. Show a => a -> FilePath
show (ModuleName -> FilePath
moduleNameString ModuleName
mn, UnitId
uid) FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
". Entries: "
FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath -> [FilePath] -> FilePath
forall a. [a] -> [[a]] -> [a]
intercalate FilePath
", " ((HieModuleRow -> FilePath) -> [HieModuleRow] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map HieModuleRow -> FilePath
hieModuleHieFile [HieModuleRow]
xs)
lookupHieFileFromSource :: HieDb -> FilePath -> IO (Maybe HieModuleRow)
lookupHieFileFromSource :: HieDb -> FilePath -> IO (Maybe HieModuleRow)
lookupHieFileFromSource (HieDb -> Connection
getConn -> Connection
conn) FilePath
fp = do
[HieModuleRow]
files <- Connection -> Query -> Only FilePath -> IO [HieModuleRow]
forall q r.
(ToRow q, FromRow r) =>
Connection -> Query -> q -> IO [r]
query Connection
conn Query
"SELECT * FROM mods WHERE hs_src = ?" (FilePath -> Only FilePath
forall a. a -> Only a
Only FilePath
fp)
case [HieModuleRow]
files of
[] -> Maybe HieModuleRow -> IO (Maybe HieModuleRow)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe HieModuleRow
forall a. Maybe a
Nothing
[HieModuleRow
x] -> Maybe HieModuleRow -> IO (Maybe HieModuleRow)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe HieModuleRow -> IO (Maybe HieModuleRow))
-> Maybe HieModuleRow -> IO (Maybe HieModuleRow)
forall a b. (a -> b) -> a -> b
$ HieModuleRow -> Maybe HieModuleRow
forall a. a -> Maybe a
Just HieModuleRow
x
[HieModuleRow]
xs ->
FilePath -> IO (Maybe HieModuleRow)
forall a. HasCallStack => FilePath -> a
error (FilePath -> IO (Maybe HieModuleRow))
-> FilePath -> IO (Maybe HieModuleRow)
forall a b. (a -> b) -> a -> b
$ FilePath
"DB invariant violated, hs_src in mods not unique: "
FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath -> FilePath
forall a. Show a => a -> FilePath
show FilePath
fp FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
". Entries: "
FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath -> [FilePath] -> FilePath
forall a. [a] -> [[a]] -> [a]
intercalate FilePath
", " ((HieModuleRow -> FilePath) -> [HieModuleRow] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map ([SQLData] -> FilePath
forall a. Show a => a -> FilePath
show ([SQLData] -> FilePath)
-> (HieModuleRow -> [SQLData]) -> HieModuleRow -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HieModuleRow -> [SQLData]
forall a. ToRow a => a -> [SQLData]
toRow) [HieModuleRow]
xs)
findTypeRefs :: HieDb -> Bool -> OccName -> Maybe ModuleName -> Maybe UnitId -> [FilePath] -> IO [Res TypeRef]
findTypeRefs :: HieDb
-> Bool
-> OccName
-> Maybe ModuleName
-> Maybe UnitId
-> [FilePath]
-> IO [Res TypeRef]
findTypeRefs (HieDb -> Connection
getConn -> Connection
conn) Bool
isReal OccName
occ Maybe ModuleName
mn Maybe UnitId
uid [FilePath]
exclude
= Connection -> Query -> [NamedParam] -> IO [Res TypeRef]
forall r.
FromRow r =>
Connection -> Query -> [NamedParam] -> IO [r]
queryNamed Connection
conn Query
thisQuery ([Text
":occ" Text -> OccName -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= OccName
occ, Text
":mod" Text -> Maybe ModuleName -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= Maybe ModuleName
mn, Text
":unit" Text -> Maybe UnitId -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= Maybe UnitId
uid, Text
":real" Text -> Bool -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= Bool
isReal] [NamedParam] -> [NamedParam] -> [NamedParam]
forall a. [a] -> [a] -> [a]
++ [NamedParam]
excludedFields)
where
excludedFields :: [NamedParam]
excludedFields = (Int -> FilePath -> NamedParam)
-> [Int] -> [FilePath] -> [NamedParam]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (\Int
n FilePath
f -> (Text
":exclude" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> FilePath -> Text
T.pack (Int -> FilePath
forall a. Show a => a -> FilePath
show Int
n)) Text -> FilePath -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= FilePath
f) [Int
1 :: Int ..] [FilePath]
exclude
thisQuery :: Query
thisQuery =
Query
"SELECT typerefs.*, mods.mod,mods.unit,mods.is_boot,mods.hs_src,mods.is_real,mods.hash \
\FROM typerefs JOIN mods ON typerefs.hieFile = mods.hieFile \
\JOIN typenames ON typerefs.id = typenames.id \
\WHERE typenames.name = :occ AND (:mod IS NULL OR typenames.mod = :mod) AND \
\(:unit IS NULL OR typenames.unit = :unit) AND ((NOT :real) OR (mods.is_real AND mods.hs_src IS NOT NULL))"
Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> Query
" AND mods.hs_src NOT IN (" Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> Text -> Query
Query (Text -> [Text] -> Text
T.intercalate Text
"," ((NamedParam -> Text) -> [NamedParam] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (\(Text
l := v
_) -> Text
l) [NamedParam]
excludedFields)) Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> Query
")"
Query -> Query -> Query
forall a. Semigroup a => a -> a -> a
<> Query
" ORDER BY typerefs.depth ASC"
findDef :: HieDb -> OccName -> Maybe ModuleName -> Maybe UnitId -> IO [Res DefRow]
findDef :: HieDb
-> OccName -> Maybe ModuleName -> Maybe UnitId -> IO [Res DefRow]
findDef HieDb
conn OccName
occ Maybe ModuleName
mn Maybe UnitId
uid
= Connection -> Query -> [NamedParam] -> IO [Res DefRow]
forall r.
FromRow r =>
Connection -> Query -> [NamedParam] -> IO [r]
queryNamed (HieDb -> Connection
getConn HieDb
conn) Query
"SELECT defs.*, mods.mod,mods.unit,mods.is_boot,mods.hs_src,mods.is_real,mods.hash \
\FROM defs JOIN mods USING (hieFile) \
\WHERE occ = :occ AND (:mod IS NULL OR mod = :mod) AND (:unit IS NULL OR unit = :unit)"
[Text
":occ" Text -> OccName -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= OccName
occ,Text
":mod" Text -> Maybe ModuleName -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= Maybe ModuleName
mn, Text
":unit" Text -> Maybe UnitId -> NamedParam
forall v. ToField v => Text -> v -> NamedParam
:= Maybe UnitId
uid]
findOneDef :: HieDb -> OccName -> Maybe ModuleName -> Maybe UnitId -> IO (Either HieDbErr (Res DefRow))
findOneDef :: HieDb
-> OccName
-> Maybe ModuleName
-> Maybe UnitId
-> IO (Either HieDbErr (Res DefRow))
findOneDef HieDb
conn OccName
occ Maybe ModuleName
mn Maybe UnitId
muid = [Res DefRow] -> Either HieDbErr (Res DefRow)
forall h. [h :. ModuleInfo] -> Either HieDbErr (h :. ModuleInfo)
wrap ([Res DefRow] -> Either HieDbErr (Res DefRow))
-> IO [Res DefRow] -> IO (Either HieDbErr (Res DefRow))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HieDb
-> OccName -> Maybe ModuleName -> Maybe UnitId -> IO [Res DefRow]
findDef HieDb
conn OccName
occ Maybe ModuleName
mn Maybe UnitId
muid
where
wrap :: [h :. ModuleInfo] -> Either HieDbErr (h :. ModuleInfo)
wrap [h :. ModuleInfo
x] = (h :. ModuleInfo) -> Either HieDbErr (h :. ModuleInfo)
forall a b. b -> Either a b
Right h :. ModuleInfo
x
wrap [] = HieDbErr -> Either HieDbErr (h :. ModuleInfo)
forall a b. a -> Either a b
Left (HieDbErr -> Either HieDbErr (h :. ModuleInfo))
-> HieDbErr -> Either HieDbErr (h :. ModuleInfo)
forall a b. (a -> b) -> a -> b
$ OccName -> Maybe ModuleName -> Maybe UnitId -> HieDbErr
NameNotFound OccName
occ Maybe ModuleName
mn Maybe UnitId
muid
wrap (h :. ModuleInfo
x:[h :. ModuleInfo]
xs) = HieDbErr -> Either HieDbErr (h :. ModuleInfo)
forall a b. a -> Either a b
Left (HieDbErr -> Either HieDbErr (h :. ModuleInfo))
-> HieDbErr -> Either HieDbErr (h :. ModuleInfo)
forall a b. (a -> b) -> a -> b
$ NonEmpty ModuleInfo -> HieDbErr
AmbiguousUnitId ((h :. ModuleInfo) -> ModuleInfo
forall h t. (h :. t) -> t
defUnit h :. ModuleInfo
x ModuleInfo -> [ModuleInfo] -> NonEmpty ModuleInfo
forall a. a -> [a] -> NonEmpty a
:| ((h :. ModuleInfo) -> ModuleInfo)
-> [h :. ModuleInfo] -> [ModuleInfo]
forall a b. (a -> b) -> [a] -> [b]
map (h :. ModuleInfo) -> ModuleInfo
forall h t. (h :. t) -> t
defUnit [h :. ModuleInfo]
xs)
defUnit :: (h :. t) -> t
defUnit (h
_:.t
i) = t
i
searchDef :: HieDb -> String -> IO [Res DefRow]
searchDef :: HieDb -> FilePath -> IO [Res DefRow]
searchDef HieDb
conn FilePath
cs
= Connection -> Query -> Only FilePath -> IO [Res DefRow]
forall q r.
(ToRow q, FromRow r) =>
Connection -> Query -> q -> IO [r]
query (HieDb -> Connection
getConn HieDb
conn) Query
"SELECT defs.*,mods.mod,mods.unit,mods.is_boot,mods.hs_src,mods.is_real,mods.hash \
\FROM defs JOIN mods USING (hieFile) \
\WHERE occ LIKE ? \
\LIMIT 200" (FilePath -> Only FilePath
forall a. a -> Only a
Only (FilePath -> Only FilePath) -> FilePath -> Only FilePath
forall a b. (a -> b) -> a -> b
$ Char
'_'Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:FilePath
csFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
"%")
withTarget
:: HieDb
-> HieTarget
-> (HieFile -> a)
-> IO (Either HieDbErr a)
withTarget :: HieDb -> HieTarget -> (HieFile -> a) -> IO (Either HieDbErr a)
withTarget HieDb
conn HieTarget
target HieFile -> a
f = case HieTarget
target of
Left FilePath
fp -> FilePath -> IO (Either HieDbErr a)
forall a. FilePath -> IO (Either a a)
processHieFile FilePath
fp
Right (ModuleName
mn,Maybe UnitId
muid) -> do
Either HieDbErr UnitId
euid <- IO (Either HieDbErr UnitId)
-> (UnitId -> IO (Either HieDbErr UnitId))
-> Maybe UnitId
-> IO (Either HieDbErr UnitId)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (HieDb -> ModuleName -> IO (Either HieDbErr UnitId)
resolveUnitId HieDb
conn ModuleName
mn) (Either HieDbErr UnitId -> IO (Either HieDbErr UnitId)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either HieDbErr UnitId -> IO (Either HieDbErr UnitId))
-> (UnitId -> Either HieDbErr UnitId)
-> UnitId
-> IO (Either HieDbErr UnitId)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UnitId -> Either HieDbErr UnitId
forall a b. b -> Either a b
Right) Maybe UnitId
muid
case Either HieDbErr UnitId
euid of
Left HieDbErr
err -> Either HieDbErr a -> IO (Either HieDbErr a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either HieDbErr a -> IO (Either HieDbErr a))
-> Either HieDbErr a -> IO (Either HieDbErr a)
forall a b. (a -> b) -> a -> b
$ HieDbErr -> Either HieDbErr a
forall a b. a -> Either a b
Left HieDbErr
err
Right UnitId
uid -> do
Maybe HieModuleRow
mModRow <- HieDb -> ModuleName -> UnitId -> IO (Maybe HieModuleRow)
lookupHieFile HieDb
conn ModuleName
mn UnitId
uid
case Maybe HieModuleRow
mModRow of
Maybe HieModuleRow
Nothing -> Either HieDbErr a -> IO (Either HieDbErr a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either HieDbErr a -> IO (Either HieDbErr a))
-> Either HieDbErr a -> IO (Either HieDbErr a)
forall a b. (a -> b) -> a -> b
$ HieDbErr -> Either HieDbErr a
forall a b. a -> Either a b
Left (ModuleName -> Maybe UnitId -> HieDbErr
NotIndexed ModuleName
mn (Maybe UnitId -> HieDbErr) -> Maybe UnitId -> HieDbErr
forall a b. (a -> b) -> a -> b
$ UnitId -> Maybe UnitId
forall a. a -> Maybe a
Just UnitId
uid)
Just HieModuleRow
modRow -> FilePath -> IO (Either HieDbErr a)
forall a. FilePath -> IO (Either a a)
processHieFile (HieModuleRow -> FilePath
hieModuleHieFile HieModuleRow
modRow)
where
processHieFile :: FilePath -> IO (Either a a)
processHieFile FilePath
fp = do
FilePath
fp' <- FilePath -> IO FilePath
canonicalizePath FilePath
fp
IORef NameCache
nc <- NameCache -> IO (IORef NameCache)
forall a. a -> IO (IORef a)
newIORef (NameCache -> IO (IORef NameCache))
-> IO NameCache -> IO (IORef NameCache)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO NameCache
makeNc
IORef NameCache -> DbMonad (Either a a) -> IO (Either a a)
forall a. IORef NameCache -> DbMonad a -> IO a
runDbM IORef NameCache
nc (DbMonad (Either a a) -> IO (Either a a))
-> DbMonad (Either a a) -> IO (Either a a)
forall a b. (a -> b) -> a -> b
$ do
a -> Either a a
forall a b. b -> Either a b
Right (a -> Either a a) -> DbMonadT IO a -> DbMonad (Either a a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> (HieFile -> DbMonadT IO a) -> DbMonadT IO a
forall (m :: * -> *) a.
(NameCacheMonad m, MonadIO m) =>
FilePath -> (HieFile -> m a) -> m a
withHieFile FilePath
fp' (a -> DbMonadT IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (a -> DbMonadT IO a) -> (HieFile -> a) -> HieFile -> DbMonadT IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HieFile -> a
f)
type Vertex = (String, String, String, Int, Int, Int, Int)
declRefs :: HieDb -> IO ()
declRefs :: HieDb -> IO ()
declRefs HieDb
db = do
AdjacencyMap Vertex
graph <- HieDb -> IO (AdjacencyMap Vertex)
getGraph HieDb
db
FilePath -> FilePath -> IO ()
writeFile
FilePath
"refs.dot"
( Style Vertex FilePath -> AdjacencyMap Vertex -> FilePath
forall s a g.
(IsString s, Monoid s, Ord a, ToGraph g, ToVertex g ~ a) =>
Style a s -> g -> s
export
( ( (Vertex -> FilePath) -> Style Vertex FilePath
forall s a. Monoid s => (a -> s) -> Style a s
defaultStyle ( \( FilePath
_, FilePath
hie, FilePath
occ, Int
_, Int
_, Int
_, Int
_ ) -> FilePath
hie FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
":" FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
occ ) )
{ vertexAttributes :: Vertex -> [Attribute FilePath]
vertexAttributes = \( FilePath
mod', FilePath
_, FilePath
occ, Int
_, Int
_, Int
_, Int
_ ) ->
[ FilePath
"label" FilePath -> FilePath -> Attribute FilePath
forall s. s -> s -> Attribute s
G.:= ( FilePath
mod' FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
"." FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> Int -> FilePath -> FilePath
forall a. Int -> [a] -> [a]
drop Int
1 FilePath
occ )
, FilePath
"fillcolor" FilePath -> FilePath -> Attribute FilePath
forall s. s -> s -> Attribute s
G.:= case FilePath
occ of (Char
'v':FilePath
_) -> FilePath
"red"; (Char
't':FilePath
_) -> FilePath
"blue";FilePath
_ -> FilePath
"black"
]
}
)
AdjacencyMap Vertex
graph
)
getGraph :: HieDb -> IO (AdjacencyMap Vertex)
getGraph :: HieDb -> IO (AdjacencyMap Vertex)
getGraph (HieDb -> Connection
getConn -> Connection
conn) = do
[Vertex :. Vertex]
es <-
Connection -> Query -> IO [Vertex :. Vertex]
forall r. FromRow r => Connection -> Query -> IO [r]
query_ Connection
conn Query
"SELECT mods.mod, decls.hieFile, decls.occ, decls.sl, decls.sc, decls.el, decls.ec, \
\rmods.mod, ref_decl.hieFile, ref_decl.occ, ref_decl.sl, ref_decl.sc, ref_decl.el, ref_decl.ec \
\FROM decls JOIN refs ON refs.hieFile = decls.hieFile \
\JOIN mods ON mods.hieFile = decls.hieFile \
\JOIN mods AS rmods ON rmods.mod = refs.mod AND rmods.unit = refs.unit AND rmods.is_boot = 0 \
\JOIN decls AS ref_decl ON ref_decl.hieFile = rmods.hieFile AND ref_decl.occ = refs.occ \
\WHERE ((refs.sl > decls.sl) OR (refs.sl = decls.sl AND refs.sc > decls.sc)) \
\AND ((refs.el < decls.el) OR (refs.el = decls.el AND refs.ec <= decls.ec))"
[Vertex]
vs <-
Connection -> Query -> IO [Vertex]
forall r. FromRow r => Connection -> Query -> IO [r]
query_ Connection
conn Query
"SELECT mods.mod, decls.hieFile, decls.occ, decls.sl, decls.sc, decls.el, decls.ec \
\FROM decls JOIN mods USING (hieFile)"
AdjacencyMap Vertex -> IO (AdjacencyMap Vertex)
forall (m :: * -> *) a. Monad m => a -> m a
return (AdjacencyMap Vertex -> IO (AdjacencyMap Vertex))
-> AdjacencyMap Vertex -> IO (AdjacencyMap Vertex)
forall a b. (a -> b) -> a -> b
$ AdjacencyMap Vertex -> AdjacencyMap Vertex -> AdjacencyMap Vertex
forall a.
Ord a =>
AdjacencyMap a -> AdjacencyMap a -> AdjacencyMap a
overlay ( [Vertex] -> AdjacencyMap Vertex
forall a. Ord a => [a] -> AdjacencyMap a
vertices [Vertex]
vs ) ( [(Vertex, Vertex)] -> AdjacencyMap Vertex
forall a. Ord a => [(a, a)] -> AdjacencyMap a
edges ( ((Vertex :. Vertex) -> (Vertex, Vertex))
-> [Vertex :. Vertex] -> [(Vertex, Vertex)]
forall a b. (a -> b) -> [a] -> [b]
map (\( Vertex
x :. Vertex
y ) -> ( Vertex
x, Vertex
y )) [Vertex :. Vertex]
es ) )
getVertices :: HieDb -> [Symbol] -> IO [Vertex]
getVertices :: HieDb -> [Symbol] -> IO [Vertex]
getVertices (HieDb -> Connection
getConn -> Connection
conn) [Symbol]
ss = Set Vertex -> [Vertex]
forall a. Set a -> [a]
Set.toList (Set Vertex -> [Vertex]) -> IO (Set Vertex) -> IO [Vertex]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Set Vertex -> Symbol -> IO (Set Vertex))
-> Set Vertex -> [Symbol] -> IO (Set Vertex)
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM Set Vertex -> Symbol -> IO (Set Vertex)
f Set Vertex
forall a. Set a
Set.empty [Symbol]
ss
where
f :: Set Vertex -> Symbol -> IO (Set Vertex)
f :: Set Vertex -> Symbol -> IO (Set Vertex)
f Set Vertex
vs Symbol
s = (Set Vertex -> Vertex -> Set Vertex)
-> Set Vertex -> [Vertex] -> Set Vertex
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' ((Vertex -> Set Vertex -> Set Vertex)
-> Set Vertex -> Vertex -> Set Vertex
forall a b c. (a -> b -> c) -> b -> a -> c
flip Vertex -> Set Vertex -> Set Vertex
forall a. Ord a => a -> Set a -> Set a
Set.insert) Set Vertex
vs ([Vertex] -> Set Vertex) -> IO [Vertex] -> IO (Set Vertex)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Symbol -> IO [Vertex]
one Symbol
s
one :: Symbol -> IO [Vertex]
one :: Symbol -> IO [Vertex]
one Symbol
s = do
let n :: FilePath
n = NameSpace -> Char
toNsChar (OccName -> NameSpace
occNameSpace (OccName -> NameSpace) -> OccName -> NameSpace
forall a b. (a -> b) -> a -> b
$ Symbol -> OccName
symName Symbol
s) Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
: OccName -> FilePath
occNameString (Symbol -> OccName
symName Symbol
s)
m :: FilePath
m = ModuleName -> FilePath
moduleNameString (ModuleName -> FilePath) -> ModuleName -> FilePath
forall a b. (a -> b) -> a -> b
$ Module -> ModuleName
moduleName (Module -> ModuleName) -> Module -> ModuleName
forall a b. (a -> b) -> a -> b
$ Symbol -> Module
symModule Symbol
s
u :: FilePath
u = UnitId -> FilePath
unitIdString (Module -> UnitId
moduleUnitId (Module -> UnitId) -> Module -> UnitId
forall a b. (a -> b) -> a -> b
$ Symbol -> Module
symModule Symbol
s)
Connection
-> Query -> (FilePath, FilePath, FilePath) -> IO [Vertex]
forall q r.
(ToRow q, FromRow r) =>
Connection -> Query -> q -> IO [r]
query Connection
conn Query
"SELECT mods.mod, decls.hieFile, decls.occ, decls.sl, decls.sc, decls.el, decls.ec \
\FROM decls JOIN mods USING (hieFile) \
\WHERE ( decls.occ = ? AND mods.mod = ? AND mods.unit = ? ) " (FilePath
n, FilePath
m, FilePath
u)
getReachable :: HieDb -> [Symbol] -> IO [Vertex]
getReachable :: HieDb -> [Symbol] -> IO [Vertex]
getReachable HieDb
db [Symbol]
symbols = ([Vertex], [Vertex]) -> [Vertex]
forall a b. (a, b) -> a
fst (([Vertex], [Vertex]) -> [Vertex])
-> IO ([Vertex], [Vertex]) -> IO [Vertex]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HieDb -> [Symbol] -> IO ([Vertex], [Vertex])
getReachableUnreachable HieDb
db [Symbol]
symbols
getUnreachable :: HieDb -> [Symbol] -> IO [Vertex]
getUnreachable :: HieDb -> [Symbol] -> IO [Vertex]
getUnreachable HieDb
db [Symbol]
symbols = ([Vertex], [Vertex]) -> [Vertex]
forall a b. (a, b) -> b
snd (([Vertex], [Vertex]) -> [Vertex])
-> IO ([Vertex], [Vertex]) -> IO [Vertex]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HieDb -> [Symbol] -> IO ([Vertex], [Vertex])
getReachableUnreachable HieDb
db [Symbol]
symbols
html :: (NameCacheMonad m, MonadIO m) => HieDb -> [Symbol] -> m ()
html :: HieDb -> [Symbol] -> m ()
html HieDb
db [Symbol]
symbols = do
Map FilePath (ModuleName, Set Span)
m <- IO (Map FilePath (ModuleName, Set Span))
-> m (Map FilePath (ModuleName, Set Span))
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Map FilePath (ModuleName, Set Span))
-> m (Map FilePath (ModuleName, Set Span)))
-> IO (Map FilePath (ModuleName, Set Span))
-> m (Map FilePath (ModuleName, Set Span))
forall a b. (a -> b) -> a -> b
$ HieDb -> [Symbol] -> IO (Map FilePath (ModuleName, Set Span))
getAnnotations HieDb
db [Symbol]
symbols
[(FilePath, (ModuleName, Set Span))]
-> ((FilePath, (ModuleName, Set Span)) -> m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ (Map FilePath (ModuleName, Set Span)
-> [(FilePath, (ModuleName, Set Span))]
forall k a. Map k a -> [(k, a)]
Map.toList Map FilePath (ModuleName, Set Span)
m) (((FilePath, (ModuleName, Set Span)) -> m ()) -> m ())
-> ((FilePath, (ModuleName, Set Span)) -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \(FilePath
fp, (ModuleName
mod', Set Span
sps)) -> do
[Text]
code <- FilePath -> m [Text]
forall (m :: * -> *).
(NameCacheMonad m, MonadIO m) =>
FilePath -> m [Text]
sourceCode FilePath
fp
let fp' :: FilePath
fp' = FilePath -> FilePath -> FilePath
replaceExtension FilePath
fp FilePath
"html"
IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ FilePath -> IO ()
putStrLn (FilePath -> IO ()) -> FilePath -> IO ()
forall a b. (a -> b) -> a -> b
$ ModuleName -> FilePath
moduleNameString ModuleName
mod' FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
": " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
fp'
IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ FilePath -> ModuleName -> [Text] -> [Span] -> IO ()
Html.generate FilePath
fp' ModuleName
mod' [Text]
code ([Span] -> IO ()) -> [Span] -> IO ()
forall a b. (a -> b) -> a -> b
$ Set Span -> [Span]
forall a. Set a -> [a]
Set.toList Set Span
sps
getAnnotations :: HieDb -> [Symbol] -> IO (Map FilePath (ModuleName, Set Html.Span))
getAnnotations :: HieDb -> [Symbol] -> IO (Map FilePath (ModuleName, Set Span))
getAnnotations HieDb
db [Symbol]
symbols = do
([Vertex]
rs, [Vertex]
us) <- HieDb -> [Symbol] -> IO ([Vertex], [Vertex])
getReachableUnreachable HieDb
db [Symbol]
symbols
let m1 :: Map FilePath (ModuleName, Set Span)
m1 = (Map FilePath (ModuleName, Set Span)
-> Vertex -> Map FilePath (ModuleName, Set Span))
-> Map FilePath (ModuleName, Set Span)
-> [Vertex]
-> Map FilePath (ModuleName, Set Span)
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Color
-> Map FilePath (ModuleName, Set Span)
-> Vertex
-> Map FilePath (ModuleName, Set Span)
f Color
Html.Reachable) Map FilePath (ModuleName, Set Span)
forall k a. Map k a
Map.empty [Vertex]
rs
m2 :: Map FilePath (ModuleName, Set Span)
m2 = (Map FilePath (ModuleName, Set Span)
-> Vertex -> Map FilePath (ModuleName, Set Span))
-> Map FilePath (ModuleName, Set Span)
-> [Vertex]
-> Map FilePath (ModuleName, Set Span)
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Color
-> Map FilePath (ModuleName, Set Span)
-> Vertex
-> Map FilePath (ModuleName, Set Span)
f Color
Html.Unreachable) Map FilePath (ModuleName, Set Span)
m1 [Vertex]
us
Map FilePath (ModuleName, Set Span)
-> IO (Map FilePath (ModuleName, Set Span))
forall (m :: * -> *) a. Monad m => a -> m a
return Map FilePath (ModuleName, Set Span)
m2
where
f :: Html.Color
-> Map FilePath (ModuleName, Set Html.Span)
-> Vertex
-> Map FilePath (ModuleName, Set Html.Span)
f :: Color
-> Map FilePath (ModuleName, Set Span)
-> Vertex
-> Map FilePath (ModuleName, Set Span)
f Color
c Map FilePath (ModuleName, Set Span)
m Vertex
v =
let (FilePath
fp, ModuleName
mod', Span
sp) = Color -> Vertex -> (FilePath, ModuleName, Span)
g Color
c Vertex
v
in ((ModuleName, Set Span)
-> (ModuleName, Set Span) -> (ModuleName, Set Span))
-> FilePath
-> (ModuleName, Set Span)
-> Map FilePath (ModuleName, Set Span)
-> Map FilePath (ModuleName, Set Span)
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith (ModuleName, Set Span)
-> (ModuleName, Set Span) -> (ModuleName, Set Span)
h FilePath
fp (ModuleName
mod', Span -> Set Span
forall a. a -> Set a
Set.singleton Span
sp) Map FilePath (ModuleName, Set Span)
m
g :: Html.Color -> Vertex -> (FilePath, ModuleName, Html.Span)
g :: Color -> Vertex -> (FilePath, ModuleName, Span)
g Color
c (FilePath
mod', FilePath
fp, FilePath
_, Int
sl, Int
sc, Int
el, Int
ec) = (FilePath
fp, FilePath -> ModuleName
mkModuleName FilePath
mod', Span :: Int -> Int -> Int -> Int -> Color -> Span
Html.Span
{ spStartLine :: Int
Html.spStartLine = Int
sl
, spStartColumn :: Int
Html.spStartColumn = Int
sc
, spEndLine :: Int
Html.spEndLine = Int
el
, spEndColumn :: Int
Html.spEndColumn = Int
ec
, spColor :: Color
Html.spColor = Color
c
})
h :: (ModuleName, Set Html.Span)
-> (ModuleName, Set Html.Span)
-> (ModuleName, Set Html.Span)
h :: (ModuleName, Set Span)
-> (ModuleName, Set Span) -> (ModuleName, Set Span)
h (ModuleName
m, Set Span
sps) (ModuleName
_, Set Span
sps') = (ModuleName
m, Set Span
sps Set Span -> Set Span -> Set Span
forall a. Semigroup a => a -> a -> a
<> Set Span
sps')
getReachableUnreachable :: HieDb -> [Symbol] -> IO ([Vertex], [Vertex])
getReachableUnreachable :: HieDb -> [Symbol] -> IO ([Vertex], [Vertex])
getReachableUnreachable HieDb
db [Symbol]
symbols = do
[Vertex]
vs <- HieDb -> [Symbol] -> IO [Vertex]
getVertices HieDb
db [Symbol]
symbols
AdjacencyMap Vertex
graph <- HieDb -> IO (AdjacencyMap Vertex)
getGraph HieDb
db
let (Set Vertex
xs, Set Vertex
ys) = AdjacencyMap Vertex -> [Vertex] -> (Set Vertex, Set Vertex)
forall a. Ord a => AdjacencyMap a -> [a] -> (Set a, Set a)
splitByReachability AdjacencyMap Vertex
graph [Vertex]
vs
([Vertex], [Vertex]) -> IO ([Vertex], [Vertex])
forall (m :: * -> *) a. Monad m => a -> m a
return (Set Vertex -> [Vertex]
forall a. Set a -> [a]
Set.toList Set Vertex
xs, Set Vertex -> [Vertex]
forall a. Set a -> [a]
Set.toList Set Vertex
ys)
splitByReachability :: Ord a => AdjacencyMap a -> [a] -> (Set a, Set a)
splitByReachability :: AdjacencyMap a -> [a] -> (Set a, Set a)
splitByReachability AdjacencyMap a
m [a]
vs = let s :: Set a
s = [a] -> Set a
forall a. Ord a => [a] -> Set a
Set.fromList ([a] -> AdjacencyMap a -> [a]
forall a. Ord a => [a] -> AdjacencyMap a -> [a]
dfs [a]
vs AdjacencyMap a
m) in (Set a
s, AdjacencyMap a -> Set a
forall a. AdjacencyMap a -> Set a
vertexSet AdjacencyMap a
m Set a -> Set a -> Set a
forall a. Ord a => Set a -> Set a -> Set a
Set.\\ Set a
s)