hsinspect 0.0.5 → 0.0.6
raw patch · 4 files changed
+33/−96 lines, 4 filesdep +ghcflags
Dependencies added: ghcflags
Files
- hsinspect.cabal +10/−5
- library/HsInspect/Modules.hs +1/−0
- library/HsInspect/Plugin.hs +0/−74
- library/HsInspect/Search.hs +22/−17
hsinspect.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: hsinspect-version: 0.0.5+version: 0.0.6 synopsis: Inspect Haskell source files. license: GPL-3.0-or-later license-file: LICENSE@@ -18,6 +18,11 @@ -- https://www.haskell.org/cabal/users-guide/cabal-projectindex.html +flag ghcflags+ description: Generate .ghc.flags files during compilation+ manual: True+ default: False+ common deps build-depends: , base >=4.11 && <5@@ -29,13 +34,14 @@ ghc-options: -Wall -Werror=missing-home-modules default-language: Haskell2010+ if flag(ghcflags)+ ghc-options: -fplugin GhcFlags.Plugin+ build-depends: ghcflags executable hsinspect import: deps hs-source-dirs: exe- build-depends:- , hsinspect-+ build-depends: hsinspect main-is: Main.hs ghc-options: -threaded @@ -48,7 +54,6 @@ HsInspect.Imports HsInspect.Modules HsInspect.Packages- HsInspect.Plugin HsInspect.Search HsInspect.Sexp HsInspect.Util
library/HsInspect/Modules.hs view
@@ -20,6 +20,7 @@ modules :: GHC.GhcMonad m => [String] -> m [Hit] modules homeModules = do+ -- TODO where is base? dflags <- GHC.getSessionDynFlags let Just dbs = GHC.pkgDatabase dflags loaded = Set.fromList . explicitPackages $ GHC.pkgState dflags
− library/HsInspect/Plugin.hs
@@ -1,74 +0,0 @@--- | A ghc plugin that creates a .ghc.flags file populated with the flags that--- were last used to invoke ghc for some modules, for consumption by--- hsinspect.------ https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/extending_ghc.html#compiler-plugins-module HsInspect.Plugin- ( plugin,- )-where--import qualified Config as GHC-import Control.Monad (when)-import Control.Monad.IO.Class (liftIO)-import Data.Foldable (traverse_)-import Data.List (stripPrefix)-import qualified GHC-import qualified GhcPlugins as GHC-import System.Directory (doesDirectoryExist)-import System.Environment-import System.IO.Error (catchIOError)--plugin :: GHC.Plugin-plugin =- GHC.defaultPlugin- { GHC.installCoreToDos = install,- GHC.pluginRecompile = GHC.purePlugin- }--install :: [GHC.CommandLineOption] -> [GHC.CoreToDo] -> GHC.CoreM [GHC.CoreToDo]-install _ core = do- dflags <- GHC.getDynFlags- args <- liftIO $ getArgs-- -- downstream tools shouldn't use this plugin, or all hell will break loose- let ghcFlags = unwords $ replace ["-fplugin", "HsInspect.Plugin"] [] args-- -- TODO this currently only supports ghc being called with directories and- -- home modules, we should also support calling with explicit file names.- paths = GHC.importPaths dflags-- -- TODO we might want to filter out some include directories, e.g. build tool- -- autogen folders.- writeGhcFlags path =- whenM (doesDirectoryExist path)- $ writeFile (path <> "/.ghc.flags") ghcFlags-- enable = case GHC.hscTarget dflags of- GHC.HscInterpreted -> False- GHC.HscNothing -> False- _ -> True-- when enable $ liftIO . ignoreIOExceptions $ do- traverse_ writeGhcFlags paths- writeFile ".ghc.version" GHC.cProjectVersion-- pure core---- from Data.List.Extra-replace :: Eq a => [a] -> [a] -> [a] -> [a]-replace [] _ _ = error "Extra.replace, first argument cannot be empty"-replace from to xs | Just xs' <- stripPrefix from xs = to ++ replace from to xs'-replace from to (x : xs) = x : replace from to xs-replace _ _ [] = []---- from Control.Monad.Extra-whenM :: Monad m => m Bool -> m () -> m ()-whenM b t = ifM b t (return ())--ifM :: Monad m => m Bool -> m a -> m a -> m a-ifM b t f = do b' <- b; if b' then t else f---- from System.Directory.Internal-ignoreIOExceptions :: IO () -> IO ()-ignoreIOExceptions io = io `catchIOError` (\_ -> pure ())
library/HsInspect/Search.hs view
@@ -15,6 +15,7 @@ import Control.Monad import Control.Monad.IO.Class import Data.List (isSuffixOf)+import Data.Set (Set) import qualified Data.Set as Set import qualified GHC import GHC.PackageDb@@ -22,47 +23,51 @@ import HsInspect.Util import HscTypes (ModIface (..)) import Json+import qualified Name as GHC import PackageConfig import Packages (explicitPackages) import TcRnMonad (initTcRnIf) search :: GHC.GhcMonad m => String -> m [Hit]-search query = do- liftIO . putStrLn . show $ "SEARCH: " <> query+search _query = do -- TODO support home modules+ -- TODO where is base? dflags <- GHC.getSessionDynFlags -- TODO this logic is used in Packages / Modules, share let Just ((snd =<<) -> allPkgs) = GHC.pkgDatabase dflags explicit = Set.fromList . explicitPackages $ GHC.pkgState dflags pkgs = filter (\(packageConfigId -> pid) -> Set.member pid explicit) allPkgs- symbols <- join <$> traverse getSymbols pkgs- pure $ Hit <$> symbols+ join <$> traverse getSymbols pkgs -- ^ TODO filter and rank the symbols by the search -- TODO Maybe haddock-html-getSymbols :: GHC.GhcMonad m => PackageConfig -> m [String]+getSymbols :: GHC.GhcMonad m => PackageConfig -> m [Hit] getSymbols pkg = do- let findHis dir = do- liftIO . putStrLn . show $ "WALKING: " <> dir- filter (".hi" `isSuffixOf`) <$> liftIO (walk dir)+ let findHis dir = filter (".hi" `isSuffixOf`) <$> liftIO (walk dir)+ exposed = Set.fromList $ fst <$> exposedModules pkg his <- join <$> traverse findHis (importDirs pkg)- join <$> traverse hiToSymbols his+ join <$> traverse (hiToSymbols exposed) his --- TODO filter out hidden modules-hiToSymbols :: GHC.GhcMonad m => FilePath -> m [String]-hiToSymbols hi = do- liftIO . putStrLn . show $ "PARSING: " <> hi+hiToSymbols :: GHC.GhcMonad m => Set GHC.ModuleName -> FilePath -> m [Hit]+hiToSymbols exposed hi = do env <- GHC.getSession iface <- liftIO $ initTcRnIf 'z' env () () $ readBinIface IgnoreHiWay QuietBinIFaceReading hi- pure [show . length $ mi_exports iface]+ let m = mi_module iface+ pure+ $ if not $ Set.member (GHC.moduleName m) exposed+ then []+ else do+ -- FIXME the Name is not very useful, use loadDecls+ decl <- GHC.getName . snd <$> mi_decls iface+ pure $ Hit m decl -data Hit = Hit String+data Hit = Hit GHC.Module GHC.Name instance ToSexp Hit where- toSexp (Hit txt) = toSexp txt+ toSexp (Hit _ name) = toSexp . GHC.getOccString $ name instance ToJson Hit where- json (Hit txt) = JSString txt+ json (Hit _ name) = JSString . GHC.getOccString $ name