Skip to content

Commit e12a341

Browse files
committed
Merge pull request bitc#13 from dan-t/findsymbol_command
Findsymbol command :+1:
2 parents a944b93 + 4c7bcfd commit e12a341

6 files changed

Lines changed: 150 additions & 3 deletions

File tree

hdevtools.cabal

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,7 @@ executable hdevtools
8181
cpp-options: -DENABLE_CABAL
8282

8383
if impl(ghc >= 7.9)
84-
build-depends: Cabal >= 1.22
84+
build-depends: Cabal >= 1.22,
85+
bin-package-db
86+
8587
cpp-options: -DENABLE_CABAL

src/CommandArgs.hs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ data HDevTools
7676
, line :: Int
7777
, col :: Int
7878
}
79+
| FindSymbol
80+
{ socket :: Maybe FilePath
81+
, ghcOpts :: [String]
82+
, symbol :: String
83+
, files :: [String]
84+
}
7985
deriving (Show, Data, Typeable)
8086

8187
dummyAdmin :: HDevTools
@@ -121,6 +127,14 @@ dummyType = Type
121127
, col = 0
122128
}
123129

130+
dummyFindSymbol :: HDevTools
131+
dummyFindSymbol = FindSymbol
132+
{ socket = Nothing
133+
, ghcOpts = []
134+
, symbol = ""
135+
, files = []
136+
}
137+
124138
admin :: Annotate Ann
125139
admin = record dummyAdmin
126140
[ socket := def += typFile += help "socket file to use"
@@ -164,8 +178,16 @@ type_ = record dummyType
164178
, col := def += typ "COLUMN" += argPos 2
165179
] += help "Get the type of the expression at the specified line and column"
166180

181+
findSymbol :: Annotate Ann
182+
findSymbol = record dummyFindSymbol
183+
[ socket := def += typFile += help "socket file to use"
184+
, ghcOpts := def += typ "OPTION" += help "ghc options"
185+
, symbol := def += typ "SYMBOL" += argPos 0
186+
, files := def += typFile += args
187+
] += help "List the modules where the given symbol could be found"
188+
167189
full :: String -> Annotate Ann
168-
full progName = modes_ [admin += auto, check, moduleFile, info, type_]
190+
full progName = modes_ [admin += auto, check, moduleFile, info, type_, findSymbol]
169191
+= helpArg [name "h", groupname "Help"]
170192
+= versionArg [groupname "Help"]
171193
+= program progName

src/CommandLoop.hs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module CommandLoop
99

1010
import Control.Monad (when)
1111
import Data.IORef
12-
import Data.List (find)
12+
import Data.List (find, intercalate)
1313
#if __GLASGOW_HASKELL__ < 709
1414
import Data.Traversable (traverse)
1515
#endif
@@ -27,6 +27,7 @@ import System.Posix.Files (getFileStatus, modificationTime)
2727

2828
import Types (ClientDirective(..), Command(..), CommandExtra(..))
2929
import Info (getIdentifierInfo, getType)
30+
import FindSymbol (findSymbol)
3031
import Cabal (getPackageGhcOpts)
3132
import Stack
3233

@@ -229,6 +230,21 @@ runCommand state clientSend (CmdType file (line, col)) = do
229230
, show endCol , " "
230231
, "\"", t, "\""
231232
]
233+
runCommand state clientSend (CmdFindSymbol symbol files) = do
234+
result <- withWarnings state False $ findSymbol symbol files
235+
case result of
236+
[] -> liftIO $ mapM_ clientSend
237+
[ ClientStderr $ "Couldn't find modules containing '" ++ symbol ++ "'"
238+
, ClientExit (ExitFailure 1)
239+
]
240+
modules -> liftIO $ mapM_ clientSend
241+
[ ClientStdout (formatModules modules)
242+
, ClientExit ExitSuccess
243+
]
244+
where
245+
formatModules = intercalate "\n"
246+
247+
232248

233249
#if __GLASGOW_HASKELL__ >= 706
234250
logAction :: IORef State -> ClientSend -> GHC.DynFlags -> GHC.Severity -> GHC.SrcSpan -> Outputable.PprStyle -> ErrUtils.MsgDoc -> IO ()

src/FindSymbol.hs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
{-# Language ScopedTypeVariables, CPP #-}
2+
3+
module FindSymbol
4+
( findSymbol
5+
) where
6+
7+
#if __GLASGOW_HASKELL__ < 710
8+
import Control.Applicative ((<$>))
9+
import qualified UniqFM
10+
#else
11+
import GHC.PackageDb (exposedName)
12+
import GhcMonad (liftIO)
13+
#endif
14+
15+
import Control.Monad (filterM)
16+
import Control.Exception
17+
import Data.List (find, nub)
18+
import Data.Maybe (catMaybes, isJust)
19+
import qualified GHC
20+
import qualified Packages as PKG
21+
import qualified Name
22+
import Exception (ghandle)
23+
24+
type SymbolName = String
25+
type ModuleName = String
26+
27+
findSymbol :: SymbolName -> [FilePath] -> GHC.Ghc [ModuleName]
28+
findSymbol symbol files = do
29+
-- for the findsymbol command GHC shouldn't output any warnings
30+
-- or errors to stdout for the loaded source files, we're only
31+
-- interested in the module graph of the loaded targets
32+
dynFlags <- GHC.getSessionDynFlags
33+
_ <- GHC.setSessionDynFlags dynFlags { GHC.log_action = \_ _ _ _ _ -> return () }
34+
35+
fileMods <- concat <$> mapM (findSymbolInFile symbol) files
36+
37+
-- reset the old log_action
38+
_ <- GHC.setSessionDynFlags dynFlags
39+
40+
pkgsMods <- findSymbolInPackages symbol
41+
return . nub . map (GHC.moduleNameString . GHC.moduleName) $ fileMods ++ pkgsMods
42+
43+
44+
findSymbolInFile :: SymbolName -> FilePath -> GHC.Ghc [GHC.Module]
45+
findSymbolInFile symbol file = do
46+
loadFile
47+
filterM (containsSymbol symbol) =<< fileModules
48+
where
49+
loadFile = do
50+
let noPhase = Nothing
51+
target <- GHC.guessTarget file noPhase
52+
GHC.setTargets [target]
53+
let handler err = GHC.printException err >> return GHC.Failed
54+
_ <- GHC.handleSourceError handler (GHC.load GHC.LoadAllTargets)
55+
return ()
56+
57+
fileModules = map GHC.ms_mod <$> GHC.getModuleGraph
58+
59+
60+
findSymbolInPackages :: SymbolName -> GHC.Ghc [GHC.Module]
61+
findSymbolInPackages symbol =
62+
filterM (containsSymbol symbol) =<< allExposedModules
63+
where
64+
allExposedModules :: GHC.Ghc [GHC.Module]
65+
allExposedModules = do
66+
modNames <- exposedModuleNames
67+
catMaybes <$> mapM findModule modNames
68+
where
69+
exposedModuleNames :: GHC.Ghc [GHC.ModuleName]
70+
#if __GLASGOW_HASKELL__ < 710
71+
exposedModuleNames =
72+
concatMap exposedModules
73+
. UniqFM.eltsUFM
74+
. PKG.pkgIdMap
75+
. GHC.pkgState
76+
<$> GHC.getSessionDynFlags
77+
#else
78+
exposedModuleNames = do
79+
dynFlags <- GHC.getSessionDynFlags
80+
pkgConfigs <- liftIO $ PKG.readPackageConfigs dynFlags
81+
return $ map exposedName (concatMap exposedModules pkgConfigs)
82+
#endif
83+
84+
exposedModules pkg = if PKG.exposed pkg then PKG.exposedModules pkg else []
85+
86+
findModule :: GHC.ModuleName -> GHC.Ghc (Maybe GHC.Module)
87+
findModule moduleName =
88+
ghandle (\(_ :: SomeException) -> return Nothing)
89+
(Just <$> GHC.findModule moduleName Nothing)
90+
91+
92+
containsSymbol :: SymbolName -> GHC.Module -> GHC.Ghc Bool
93+
containsSymbol symbol module_ =
94+
isJust . find (== symbol) <$> allExportedSymbols
95+
where
96+
allExportedSymbols =
97+
ghandle (\(_ :: SomeException) -> return [])
98+
(do info <- GHC.getModuleInfo module_
99+
return $ maybe [] (map Name.getOccString . GHC.modInfoExports) info)

src/Main.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ fileArg (ModuleFile {}) = Nothing
3535
fileArg args@(Check {}) = Just $ file args
3636
fileArg args@(Info {}) = Just $ file args
3737
fileArg args@(Type {}) = Just $ file args
38+
fileArg (FindSymbol {}) = Nothing
3839

3940
pathArg' :: HDevTools -> Maybe String
4041
pathArg' (Admin {}) = Nothing
4142
pathArg' (ModuleFile {}) = Nothing
4243
pathArg' args@(Check {}) = path args
4344
pathArg' args@(Info {}) = path args
4445
pathArg' args@(Type {}) = path args
46+
pathArg' (FindSymbol {}) = Nothing
4547

4648
pathArg :: HDevTools -> Maybe String
4749
pathArg args = case pathArg' args of
@@ -67,6 +69,7 @@ main = do
6769
ModuleFile {} -> doModuleFile sock args extra
6870
Info {} -> doInfo sock args extra
6971
Type {} -> doType sock args extra
72+
FindSymbol {} -> doFindSymbol sock args extra
7073

7174
doAdmin :: FilePath -> HDevTools -> CommandExtra -> IO ()
7275
doAdmin sock args _extra
@@ -108,3 +111,7 @@ doInfo = doFileCommand "info" $
108111
doType :: FilePath -> HDevTools -> CommandExtra -> IO ()
109112
doType = doFileCommand "type" $
110113
\args -> CmdType (file args) (line args, col args)
114+
115+
doFindSymbol :: FilePath -> HDevTools -> CommandExtra -> IO ()
116+
doFindSymbol sock args extra =
117+
serverCommand sock (CmdFindSymbol (symbol args) (files args)) extra

src/Types.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,5 @@ data Command
3838
| CmdModuleFile String
3939
| CmdInfo FilePath String
4040
| CmdType FilePath (Int, Int)
41+
| CmdFindSymbol String [String]
4142
deriving (Read, Show)

0 commit comments

Comments
 (0)