packages feed

hiedb 0.5.0.1 → 0.6.0.0

raw patch · 10 files changed

+258/−174 lines, 10 filesdep ~ghcPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: ghc

API changes (from Hackage documentation)

- HieDb.Utils: genRefsAndDecls :: FilePath -> Module -> Map Identifier [(Span, IdentifierDetails a)] -> ([RefRow], [DeclRow])
+ HieDb.Create: SkipOptions :: Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> SkipOptions
+ HieDb.Create: [skipDecls] :: SkipOptions -> Bool
+ HieDb.Create: [skipDefs] :: SkipOptions -> Bool
+ HieDb.Create: [skipExports] :: SkipOptions -> Bool
+ HieDb.Create: [skipImports] :: SkipOptions -> Bool
+ HieDb.Create: [skipRefs] :: SkipOptions -> Bool
+ HieDb.Create: [skipTypeRefs] :: SkipOptions -> Bool
+ HieDb.Create: [skipTypes] :: SkipOptions -> Bool
+ HieDb.Create: addRefsFromLoadedInternal :: MonadIO m => HieDb -> FilePath -> SourceFile -> Fingerprint -> SkipOptions -> HieFile -> m ()
+ HieDb.Create: data SkipOptions
+ HieDb.Create: defaultSkipOptions :: SkipOptions
+ HieDb.Create: instance GHC.Show.Show HieDb.Create.SkipOptions
+ HieDb.Run: [skipIndexingOptions] :: Options -> SkipOptions
+ HieDb.Types: ImportRow :: FilePath -> ModuleName -> Int -> Int -> Int -> Int -> ImportRow
+ HieDb.Types: [importECol] :: ImportRow -> Int
+ HieDb.Types: [importELine] :: ImportRow -> Int
+ HieDb.Types: [importModuleName] :: ImportRow -> ModuleName
+ HieDb.Types: [importSCol] :: ImportRow -> Int
+ HieDb.Types: [importSLine] :: ImportRow -> Int
+ HieDb.Types: [importSrc] :: ImportRow -> FilePath
+ HieDb.Types: data ImportRow
+ HieDb.Types: instance Database.SQLite.Simple.FromRow.FromRow HieDb.Types.ImportRow
+ HieDb.Types: instance Database.SQLite.Simple.ToRow.ToRow HieDb.Types.ImportRow
+ HieDb.Utils: AstInfo :: [RefRow] -> [DeclRow] -> [ImportRow] -> AstInfo
+ HieDb.Utils: [astInfoDecls] :: AstInfo -> [DeclRow]
+ HieDb.Utils: [astInfoImports] :: AstInfo -> [ImportRow]
+ HieDb.Utils: [astInfoRefs] :: AstInfo -> [RefRow]
+ HieDb.Utils: data AstInfo
+ HieDb.Utils: genAstInfo :: FilePath -> Module -> Map Identifier [(Span, IdentifierDetails a)] -> AstInfo
+ HieDb.Utils: instance GHC.Base.Monoid HieDb.Utils.AstInfo
+ HieDb.Utils: instance GHC.Base.Semigroup HieDb.Utils.AstInfo
+ HieDb.Utils: type TypeIndexing a = StateT (IntMap IntSet) IO a
- HieDb.Create: addRefsFrom :: (MonadIO m, NameCacheMonad m) => HieDb -> Maybe FilePath -> FilePath -> m Bool
+ HieDb.Create: addRefsFrom :: (MonadIO m, NameCacheMonad m) => HieDb -> Maybe FilePath -> SkipOptions -> FilePath -> m Bool
- HieDb.Create: addRefsFromLoaded_unsafe :: MonadIO m => HieDb -> FilePath -> SourceFile -> Fingerprint -> HieFile -> m ()
+ HieDb.Create: addRefsFromLoaded_unsafe :: MonadIO m => HieDb -> FilePath -> SourceFile -> Fingerprint -> SkipOptions -> HieFile -> m ()
- HieDb.Run: Options :: FilePath -> Bool -> Bool -> Bool -> Maybe Natural -> Bool -> Bool -> Maybe FilePath -> Options
+ HieDb.Run: Options :: FilePath -> Bool -> Bool -> Bool -> Maybe Natural -> Bool -> Bool -> Maybe FilePath -> SkipOptions -> Options
- HieDb.Utils: addTypeRef :: HieDb -> FilePath -> Array TypeIndex HieTypeFlat -> Array TypeIndex (Maybe Int64) -> RealSrcSpan -> TypeIndex -> IO ()
+ HieDb.Utils: addTypeRef :: HieDb -> FilePath -> Array TypeIndex HieTypeFlat -> Array TypeIndex (Maybe Int64) -> RealSrcSpan -> TypeIndex -> TypeIndexing ()

Files

CHANGELOG.md view
@@ -1,16 +1,33 @@ # Revision history for hiedb +## 0.6.0.0 -- 2024-02-11++* Add index on column `unit` of table `mods`+* Add new table `imports` which indexes import statements+* Add new cli options that allow selectively skipping indexing of some things:+    `--skip-refs`              Skip refs table when indexing+    `--skip-decls`             Skip decls table when indexing+    `--skip-defs`              Skip defs table when indexing+    `--skip-exports`           Skip exports table when indexing+    `--skip-imports`           Skip imports table when indexing+    `--skip-types`             Skip types and typerefs table when indexing+    `--skip-typerefs`          Skip typerefs table when indexing+* Fix a bug where duplicate entries were inserted into `typerefs` table during indexing+* Fix a bug in `searchDef` query which was mistakenly not including ':' when searching by occurrence names+ ## 0.5.0.1 -- 2024-01-12 -- Fix incorrect Show Symbol instance in 0.5.0.0+* Fix incorrect Show Symbol instance in 0.5.0.0  ## 0.5.0.0 -- 2024-01-12 -- Handle duplicate record fields in GHC 9.8 instead of crashing+* Handle duplicate record fields in GHC 9.8 instead of crashing  ## 0.4.4.0 -- 2023-11-13+ * Add `--src-base-dir` option allowing for src file indexing in `mods`-* 9.8.1 support+* Support GHC 9.8.1+* Drop support for GHC 8.10 * Add `lookupHieFileFromHash` * Add `lookupPackage` * Add `removeDependencySrcFiles`
hiedb.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.4 name:                hiedb-version:             0.5.0.1+version:             0.6.0.0 synopsis:            Generates a references DB from .hie files description:         Tool and library to index and query a collection of `.hie` files bug-reports:         https://github.com/wz1000/HieDb/issues@@ -10,15 +10,17 @@ maintainer:          zubin.duggal@gmail.com copyright:           Zubin Duggal category:            Development-extra-source-files:-  CHANGELOG.md+extra-doc-files:   README.md+  CHANGELOG.md+extra-source-files:   test/data/*.hs   test/data/Sub/*.hs-tested-with:         GHC ==8.8.1 || ==8.8.2  || ==8.8.3  || ==8.8.4-                     || ==8.10.1 || ==8.10.2 || ==8.10.3 || ==8.10.4-                     || ==9.0.1-+tested-with:         GHC ==9.0.2+                      || ==9.2.8+                      || ==9.4.8+                      || ==9.6.4+                      || ==9.8.1  source-repository head   type: git@@ -28,13 +30,15 @@   default-language:    Haskell2010   build-depends:       base >= 4.12 && < 4.20   ghc-options:         -Wall-                       -Wincomplete-uni-patterns-                       -Wincomplete-record-updates                        -Wcompat                        -Widentities-                       -Wredundant-constraints+                       -Wincomplete-record-updates+                       -Wincomplete-uni-patterns                        -Wpartial-fields-                       -Wno-unrecognised-pragmas+                       -Wredundant-constraints+                       -Wunused-packages+                       -Wno-name-shadowing+ executable hiedb   import:              common-options   hs-source-dirs:      exe@@ -81,7 +85,6 @@   build-tool-depends: hiedb:hiedb   build-depends:      directory                     , filepath-                    , ghc >= 8.6                     , ghc-paths                     , hiedb                     , hspec
src/HieDb/Compat.hs view
@@ -25,7 +25,11 @@     , mkVarOccFS     , Name     , nameSrcSpan+#if __GLASGOW_HASKELL__ >= 903+    , NameCacheUpdater+#else     , NameCacheUpdater(..)+#endif     , NameCache     , nsNames     , initNameCache@@ -88,7 +92,6 @@  import Compat.HieTypes -#if __GLASGOW_HASKELL__ >= 900 import GHC.Data.FastString as FS import GHC.Driver.Session import GHC.Iface.Env@@ -114,33 +117,15 @@ #else import GHC.Utils.Outputable (showSDoc, ppr, (<+>), hang, text) #endif-#else-import DynFlags-import FastString-import Fingerprint-import FieldLabel-import Module-import Name-import NameCache-import Outputable (showSDoc, ppr, (<+>), hang, text)-#if __GLASGOW_HASKELL__ < 903-import IfaceEnv (NameCacheUpdater(..))-#endif-import IfaceType-import UniqSupply-import SrcLoc-import SysTools-import qualified Avail-#endif -#if __GLASGOW_HASKELL__ >= 900+import qualified Algebra.Graph.AdjacencyMap           as Graph+import qualified Algebra.Graph.AdjacencyMap.Algorithm as Graph+ import GHC.Types.SrcLoc import Compat.HieUtils  import qualified Data.Map as M import qualified Data.Set as S-import qualified Algebra.Graph.AdjacencyMap           as Graph-import qualified Algebra.Graph.AdjacencyMap.Algorithm as Graph   -- nodeInfo' :: Ord a => HieAST a -> NodeInfo a@@ -158,22 +143,7 @@                                         GT -> b : mergeSorted la bs     mergeSorted as [] = as     mergeSorted [] bs = bs-#else-import qualified FastString as FS -nodeInfo' :: HieAST TypeIndex -> NodeInfo TypeIndex-nodeInfo' = nodeInfo-type Unit = UnitId-unitString :: Unit -> String-unitString = unitIdString-stringToUnit :: String -> Unit-stringToUnit = stringToUnitId-moduleUnit :: Module -> Unit-moduleUnit = moduleUnitId-unhelpfulSpanFS :: FS.FastString -> FS.FastString-unhelpfulSpanFS = id-#endif- #if __GLASGOW_HASKELL__ < 902 type HiePath = FastString #endif@@ -238,8 +208,9 @@ -- This is horrible, we can improve it once -- https://gitlab.haskell.org/ghc/ghc/-/issues/24244 is addressed fieldNameSpace_maybe ns = fieldOcc_maybe (mkOccName ns "")-#endif+#else fieldNameSpace_maybe _ = Nothing+#endif  #if __GLASGOW_HASKELL__ < 907 fieldName :: FastString -> NameSpace
src/HieDb/Create.hs view
@@ -16,9 +16,11 @@ import Control.Exception import Control.Monad import Control.Monad.IO.Class+import Control.Monad.State.Strict (evalStateT)  import qualified Data.Array as A import qualified Data.Map as M+import qualified Data.IntMap.Strict as IMap  import Data.Int import Data.List ( isSuffixOf )@@ -35,7 +37,7 @@ import HieDb.Utils  sCHEMA_VERSION :: Integer-sCHEMA_VERSION = 7+sCHEMA_VERSION = 8  dB_VERSION :: Integer dB_VERSION = read (show sCHEMA_VERSION ++ "999" ++ show hieVersion)@@ -91,6 +93,7 @@                 \, CONSTRAINT real_has_src CHECK ( (NOT is_real) OR (hs_src IS NOT NULL) ) \                 \)"   execute_ conn "CREATE INDEX IF NOT EXISTS mod_hash ON mods(hieFile,hash)"+  execute_ conn "CREATE INDEX IF NOT EXISTS mod_unit ON mods(unit)"    execute_ conn "CREATE TABLE IF NOT EXISTS exports \                 \( hieFile TEXT NOT NULL \@@ -130,6 +133,19 @@                 \)"   execute_ conn "CREATE INDEX IF NOT EXISTS decls_mod ON decls(hieFile)" +  execute_ conn "CREATE TABLE IF NOT EXISTS imports \+                \( hieFile    TEXT NOT NULL \+                \, mod     TEXT NOT NULL \+                \, sl      INTEGER NOT NULL \+                \, sc      INTEGER NOT NULL \+                \, el      INTEGER NOT NULL \+                \, ec      INTEGER NOT NULL \+                \, FOREIGN KEY(hieFile) REFERENCES mods(hieFile) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED \+                \)"+  execute_ conn "CREATE INDEX IF NOT EXISTS imports_mod ON imports(mod)"+  execute_ conn "CREATE INDEX IF NOT EXISTS imports_hiefile ON imports(hieFile)"++   execute_ conn "CREATE TABLE IF NOT EXISTS defs \                 \( hieFile    TEXT NOT NULL \                 \, occ        TEXT NOT NULL \@@ -183,7 +199,7 @@             mod = moduleName m             uid = moduleUnit m         execute conn "INSERT INTO typenames(name,mod,unit) VALUES (?,?,?)" (occ,mod,uid)-        Just . fromOnly . head <$> query conn "SELECT id FROM typenames WHERE name = ? AND mod = ? AND unit = ?" (occ,mod,uid)+        fmap fromOnly . listToMaybe <$> query conn "SELECT id FROM typenames WHERE name = ? AND mod = ? AND unit = ?" (occ,mod,uid)  {-| Add references to types from given @.hie@ file to DB. -} addTypeRefs@@ -200,19 +216,47 @@     asts = getAsts $ hie_asts hf     addTypesFromAst :: HieAST TypeIndex -> IO ()     addTypesFromAst ast = do-      mapM_ (addTypeRef db path arr ixs (nodeSpan ast))-        $ mapMaybe (\x -> guard (any (not . isOccurrence) (identInfo x)) *> identType x)+      flip evalStateT IMap.empty+        $ mapM_ (addTypeRef db path arr ixs (nodeSpan ast))+        $ mapMaybe (\x -> guard (not (all isOccurrence (identInfo x))) *> identType x)         $ M.elems         $ nodeIdentifiers         $ nodeInfo' ast       mapM_ addTypesFromAst $ nodeChildren ast +-- | Options to skip indexing phases+data SkipOptions =+  SkipOptions+    { skipRefs :: Bool+    , skipDecls :: Bool+    , skipDefs :: Bool+    , skipExports :: Bool+    , skipImports :: Bool+    , skipTypes :: Bool+    -- ^ Note skip types will also skip type refs since it is dependent+    , skipTypeRefs :: Bool+    }+    deriving Show++defaultSkipOptions :: SkipOptions+defaultSkipOptions =+  SkipOptions+    { skipRefs = False+    , skipDecls = False+    , skipDefs = False+    , skipExports = False+    , skipImports = False+    , skipTypes = False+    -- ^ Note skip types will also skip type refs since it is dependent+    , skipTypeRefs = False+    }+ {-| Adds all references from given @.hie@ file to 'HieDb'. The indexing is skipped if the file was not modified since the last time it was indexed. The boolean returned is true if the file was actually indexed -}-addRefsFrom :: (MonadIO m, NameCacheMonad m) => HieDb -> Maybe FilePath -> FilePath -> m Bool-addRefsFrom c@(getConn -> conn) mSrcBaseDir path = do+addRefsFrom :: (MonadIO m, NameCacheMonad m) => HieDb -> Maybe FilePath -> SkipOptions -> FilePath ->  m Bool+addRefsFrom c@(getConn -> conn) mSrcBaseDir skipOptions path = do   hash <- liftIO $ getFileHash path   mods <- liftIO $ query conn "SELECT * FROM mods WHERE hieFile = ? AND hash = ?" (path, hash)   case mods of@@ -229,10 +273,10 @@                 (\srcBaseDir ->  do                     srcFullPath <- makeAbsolute (srcBaseDir </> hie_hs_file hieFile)                     fileExists <- doesFileExist srcFullPath-                    pure $ if fileExists then RealFile srcFullPath else (FakeFile Nothing)+                    pure $ if fileExists then RealFile srcFullPath else FakeFile Nothing                 )                 mSrcBaseDir-        addRefsFromLoaded c path srcfile hash hieFile+        addRefsFromLoadedInternal c path srcfile hash skipOptions hieFile  addRefsFromLoaded   :: MonadIO m@@ -244,11 +288,25 @@   -> Fingerprint -- ^ The hash of the @.hie@ file   -> HieFile -- ^ Data loaded from the @.hie@ file   -> m ()-addRefsFromLoaded-  db@(getConn -> conn) path sourceFile hash hf =+addRefsFromLoaded db path sourceFile hash hf =+    addRefsFromLoadedInternal db path sourceFile hash defaultSkipOptions hf++addRefsFromLoadedInternal+  :: MonadIO m+  => HieDb -- ^ HieDb into which we're adding the file+  -> FilePath -- ^ Path to @.hie@ file+  -> SourceFile -- ^ Path to .hs file from which @.hie@ file was created+                -- Also tells us if this is a real source file?+                -- i.e. does it come from user's project (as opposed to from project's dependency)?+  -> Fingerprint -- ^ The hash of the @.hie@ file+  -> SkipOptions -- ^ Skip indexing certain tables+  -> HieFile -- ^ Data loaded from the @.hie@ file+  -> m ()+addRefsFromLoadedInternal+  db@(getConn -> conn) path sourceFile hash skipOptions hf =     liftIO $ withTransaction conn $ do       deleteInternalTables conn path-      addRefsFromLoaded_unsafe db path sourceFile hash hf+      addRefsFromLoaded_unsafe db path sourceFile hash skipOptions hf  -- | Like 'addRefsFromLoaded' but without: --   1) using a transaction@@ -263,10 +321,11 @@                 -- Also tells us if this is a real source file?                 -- i.e. does it come from user's project (as opposed to from project's dependency)?   -> Fingerprint -- ^ The hash of the @.hie@ file+  -> SkipOptions -- ^ Skip indexing certain tables   -> HieFile -- ^ Data loaded from the @.hie@ file   -> m () addRefsFromLoaded_unsafe- db@(getConn -> conn) path sourceFile hash hf = liftIO $ do+ db@(getConn -> conn) path sourceFile hash skipOptions hf = liftIO $ do    let isBoot = "boot" `isSuffixOf` path       mod    = moduleName smod@@ -280,19 +339,28 @@    execute conn "INSERT INTO mods VALUES (?,?,?,?,?,?,?)" modrow -  let (rows,decls) = genRefsAndDecls path smod refmap-  executeMany conn "INSERT INTO refs  VALUES (?,?,?,?,?,?,?,?)" rows-  executeMany conn "INSERT INTO decls VALUES (?,?,?,?,?,?,?)" decls+  let AstInfo rows decls imports = genAstInfo path smod refmap +  unless (skipRefs skipOptions) $+    executeMany conn "INSERT INTO refs  VALUES (?,?,?,?,?,?,?,?)" rows+  unless (skipDecls skipOptions) $+    executeMany conn "INSERT INTO decls VALUES (?,?,?,?,?,?,?)" decls+  unless (skipImports skipOptions) $+    executeMany conn "INSERT INTO imports VALUES (?,?,?,?,?,?)" imports+   let defs = genDefRow path smod refmap-  forM defs $ \def -> do-    execute conn "INSERT INTO defs VALUES (?,?,?,?,?,?)" def+  unless (skipDefs skipOptions) $+    forM_ defs $ \def ->+      execute conn "INSERT INTO defs VALUES (?,?,?,?,?,?)" def    let exports = generateExports path $ hie_exports hf-  executeMany conn "INSERT INTO exports VALUES (?,?,?,?,?,?,?,?)" exports+  unless (skipExports skipOptions) $+    executeMany conn "INSERT INTO exports VALUES (?,?,?,?,?,?,?,?)" exports -  ixs <- addArr db (hie_types hf)-  addTypeRefs db path hf ixs+  unless (skipTypes skipOptions) $ do+    ixs <- addArr db (hie_types hf)+    unless (skipTypeRefs skipOptions) $ do+      addTypeRefs db path hf ixs  {-| Add path to .hs source given path to @.hie@ file which has already been indexed. No action is taken if the corresponding @.hie@ file has not been indexed yet.
src/HieDb/Query.hs view
@@ -40,7 +40,7 @@ getAllIndexedMods (getConn -> conn) = query_ conn "SELECT * FROM mods"  {-| List all module exports -}-getAllIndexedExports :: HieDb -> IO [(ExportRow)]+getAllIndexedExports :: HieDb -> IO [ExportRow] getAllIndexedExports (getConn -> conn) = query_ conn "SELECT * FROM exports"  {-| List all exports of the given module -}@@ -152,7 +152,7 @@   = query (getConn conn) "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" (Only $ '_':cs++"%")+                         \LIMIT 200" (Only $ '_':':':cs++"%")  {-| @withTarget db t f@ runs function @f@ with HieFile specified by HieTarget @t@. In case the target is given by ModuleName (and optionally Unit) it is first resolved
src/HieDb/Run.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ApplicativeDo #-} module HieDb.Run where  import Prelude hiding (mod)@@ -80,6 +81,7 @@   , reindex :: Bool   , keepMissing :: Bool   , srcBaseDir :: Maybe FilePath+  , skipIndexingOptions :: SkipOptions   }  data Command@@ -127,10 +129,28 @@   <*> switch (long "reindex" <> short 'r' <> help "Re-index all files in database before running command, deleting those with missing '.hie' files")   <*> switch (long "keep-missing" <> help "Keep missing files when re-indexing")   <*> optional (strOption (long "src-base-dir" <> help "Provide a base directory to index src files as real files"))+  <*> skipFlags   where     colourFlag = flag' True (long "colour" <> long "color" <> help "Force coloured output")             <|> flag' False (long "no-colour" <> long "no-color" <> help "Force uncoloured ouput")             <|> pure colr+    skipFlags = do+        refs <- switch (long "skip-refs" <> help "Skip refs table when indexing")+        decls <- switch (long "skip-decls" <> help "Skip decls table when indexing")+        defs <- switch (long "skip-defs" <> help "Skip defs table when indexing")+        exports <- switch (long "skip-exports" <> help "Skip exports table when indexing")+        imports <- switch (long "skip-imports" <> help "Skip imports table when indexing")+        types <- switch (long "skip-types" <> help "Skip types and typerefs table when indexing")+        typeRefs <- switch (long "skip-typerefs" <> help "Skip typerefs table when indexing")+        pure $ SkipOptions+          { skipRefs = refs+          , skipDecls = decls+          , skipDefs = defs+          , skipExports = exports+          , skipImports = imports+          , skipTypes = types+          , skipTypeRefs = typeRefs+          }  cmdParser :: Parser Command cmdParser@@ -222,7 +242,7 @@     Just w -> do       hPutStr hndl $ replicate w ' '       hPutStr hndl "\r"-      pure $ take (w-8) $ msg'+      pure $ take (w-8) msg'   liftIO $ hPutStr hndl msg   x <- act f   if x@@ -238,7 +258,7 @@    istart <- offsetTime   (length -> done, length -> skipped)<- runDbM nc $ partition id <$>-    zipWithM (\f n -> progress' h (length files) n (addRefsFrom conn (srcBaseDir opts)) f) files [0..]+    zipWithM (\f n -> progress' h (length files) n (addRefsFrom conn (srcBaseDir opts) (skipIndexingOptions opts)) f) files [0..]   indexTime <- istart    start <- offsetTime@@ -372,11 +392,7 @@         reportAmbiguousErr opts (Left $ NoNameAtPoint target sp)       forM_ names $ \name -> do         case nameSrcSpan name of-#if __GLASGOW_HASKELL__ >= 900           RealSrcSpan dsp _ -> do-#else-          RealSrcSpan dsp -> do-#endif             unless (quiet opts) $               hPutStrLn stderr $ unwords ["Name", ppName opts (nameOccName name),"at",ppSpan opts sp,"is defined at:"]             contents <- case nameModule_maybe name of@@ -510,7 +526,7 @@                   beforeLines = takeEnd n beforeLines'                   afterLines  = take    n afterLines' -                  (beforeChars,during') = BS.splitAt (sc-1) $ BS.concat $ intersperse "\n" $ duringLines+                  (beforeChars,during') = BS.splitAt (sc-1) $ BS.concat $ intersperse "\n" duringLines                   (during,afterChars) = BS.splitAt (BS.length during' - (BS.length (last duringLines) - ec) - 1) during'                    before = BS.unlines beforeLines <> beforeChars
src/HieDb/Types.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE BlockArguments #-}@@ -178,6 +177,25 @@ instance FromRow DeclRow where   fromRow = DeclRow <$> field <*> field <*> field <*> field                     <*> field <*> field <*> field++data ImportRow +  = ImportRow +    { importSrc :: FilePath+    , importModuleName :: ModuleName+    , importSLine :: Int +    , importSCol :: Int +    , importELine :: Int +    , importECol :: Int +    }++instance FromRow ImportRow where +  fromRow = +    ImportRow +      <$> field <*> field <*> field <*> field +      <*> field <*> field++instance ToRow ImportRow where +  toRow (ImportRow a b c d e f) = toRow ((a,b,c,d):.(e,f))  data TypeName = TypeName   { typeName :: OccName
src/HieDb/Utils.hs view
@@ -5,7 +5,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TupleSections #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE CPP #-} module HieDb.Utils where@@ -19,13 +18,11 @@ import qualified Compat.HieTypes as HieTypes import Compat.HieUtils import qualified Data.Map as M-+import qualified Data.Set as S  import System.Directory import System.FilePath -import Control.Arrow ( (&&&) )-import Data.Bifunctor ( bimap ) import Data.List (find) import Control.Monad.IO.Class import qualified Data.Array as A@@ -39,40 +36,59 @@ import HieDb.Types import HieDb.Compat import Database.SQLite.Simple-import Control.Concurrent+import Control.Monad.State.Strict (StateT, get, put)+import qualified Data.IntSet as ISet+import qualified Data.IntMap.Strict as IMap+import Data.IntMap.Strict (IntMap)+import Data.IntSet (IntSet)+import Control.Monad (guard, unless) -addTypeRef :: HieDb -> FilePath -> A.Array TypeIndex HieTypeFlat -> A.Array TypeIndex (Maybe Int64) -> RealSrcSpan -> TypeIndex -> IO ()+#if __GLASGOW_HASKELL__ >= 903+import Control.Concurrent.MVar (readMVar)+#endif++-- Each AST Node can have a lot of repetitive type information,+-- esp. when deriving is involved.+-- This StateT state keeps track of which types in AST we already indexed+-- to avoid having repeated (Depth, Type) inserted under the same RealSrcSpan.+--+-- This `IntMap IntSet` is morally `Map Depth (Set TypeIndex)` mapping+-- the depth (of a type within tree structure of types - ends up as typerefs.depth)+-- to a list of type IDs (identifiers of types assigned as id within typenames table)+-- that we already indexed.+type TypeIndexing a = StateT (IntMap IntSet) IO a++addTypeRef :: HieDb -> FilePath -> A.Array TypeIndex HieTypeFlat -> A.Array TypeIndex (Maybe Int64) -> RealSrcSpan -> TypeIndex -> TypeIndexing () addTypeRef (getConn -> conn) hf arr ixs sp = go 0   where     sl = srcSpanStartLine sp     sc = srcSpanStartCol sp     el = srcSpanEndLine sp     ec = srcSpanEndCol sp-    go :: TypeIndex -> Int -> IO ()-    go d i = do+    go :: Int -> TypeIndex -> TypeIndexing ()+    go depth i = do       case ixs A.! i of         Nothing -> pure ()         Just occ -> do-          let ref = TypeRef occ hf d sl sc el ec-          execute conn "INSERT INTO typerefs VALUES (?,?,?,?,?,?,?)" ref-      let next = go (d+1)+          let ref = TypeRef occ hf depth sl sc el ec+          indexed <- get+          let isTypeIndexed = ISet.member (fromIntegral occ) (IMap.findWithDefault ISet.empty depth indexed)+          unless isTypeIndexed $ do+            liftIO $ execute conn "INSERT INTO typerefs VALUES (?,?,?,?,?,?,?)" ref+            put $ IMap.alter (\case+                  Nothing -> Just $ ISet.singleton (fromIntegral occ)+                  Just s -> Just $ ISet.insert (fromIntegral occ) s+                ) depth indexed+      let next = go (depth + 1)       case arr A.! i of         HTyVarTy _ -> pure ()-#if __GLASGOW_HASKELL__ >= 808         HAppTy x (HieArgs xs) -> mapM_ next (x:map snd xs)-#else-        HAppTy x y -> mapM_ next [x,y]-#endif         HTyConApp _ (HieArgs xs) -> mapM_ (next . snd) xs         HForAllTy ((_ , a),_) b -> mapM_ next [a,b]-#if __GLASGOW_HASKELL__ >= 900         HFunTy a b c -> mapM_ next [a,b,c]-#else-        HFunTy a b -> mapM_ next [a,b]-#endif         HQualTy a b -> mapM_ next [a,b]         HLitTy _ -> pure ()-        HCastTy a -> go d a+        HCastTy a -> go depth a         HCoercionTy -> pure ()  makeNc :: IO NameCache@@ -122,11 +138,7 @@ #endif   return $ case lookupOrigNameCache nsns mdl occ of     Just name -> case nameSrcSpan name of-#if __GLASGOW_HASKELL__ >= 900       RealSrcSpan sp _ -> Right (sp, mdl)-#else-      RealSrcSpan sp -> Right (sp, mdl)-#endif       UnhelpfulSpan msg -> Left $ NameUnhelpfulSpan name (unpackFS $ unhelpfulSpanFS msg)     Nothing -> Left $ NameNotFound occ (Just $ moduleName mdl) (Just $ moduleUnit mdl) @@ -143,18 +155,10 @@  dynFlagsForPrinting :: LibDir -> IO DynFlags dynFlagsForPrinting (LibDir libdir) = do-  systemSettings <- initSysTools-#if __GLASGOW_HASKELL__ >= 808-                    libdir-#else-                    (Just libdir)-#endif-#if __GLASGOW_HASKELL__ >= 905+  systemSettings <- initSysTools libdir   return $ defaultDynFlags systemSettings-#elif __GLASGOW_HASKELL__ >= 810-  return $ defaultDynFlags systemSettings $ LlvmConfig [] []-#else-  return $ defaultDynFlags systemSettings ([], [])+#if __GLASGOW_HASKELL__ < 905+    (LlvmConfig [] []) #endif  isCons :: String -> Bool@@ -162,13 +166,28 @@ isCons (x:_) | isUpper x = True isCons _ = False -genRefsAndDecls :: FilePath -> Module -> M.Map Identifier [(Span, IdentifierDetails a)] -> ([RefRow],[DeclRow])-genRefsAndDecls path smdl refmap = genRows $ flat $ M.toList refmap+data AstInfo =+  AstInfo+    { astInfoRefs :: [RefRow]+    , astInfoDecls :: [DeclRow]+    , astInfoImports :: [ImportRow]+    }++instance Semigroup AstInfo where+  AstInfo r1 d1 i1 <> AstInfo r2 d2 i2 = AstInfo (r1 <> r2) (d1 <> d2) (i1 <> i2)++instance Monoid AstInfo where+  mempty = AstInfo [] [] []++genAstInfo :: FilePath -> Module -> M.Map Identifier [(Span, IdentifierDetails a)] -> AstInfo+genAstInfo path smdl refmap = genRows $ flat $ M.toList refmap   where     flat = concatMap (\(a,xs) -> map (a,) xs)     genRows = foldMap go-    go = bimap maybeToList maybeToList . (goRef &&& goDec)+    go = mkAstInfo +    mkAstInfo x = AstInfo (maybeToList $ goRef x) (maybeToList $ goDec x) (maybeToList $ goImport x)+     goRef (Right name, (sp,_))       | Just mod <- nameModule_maybe name = Just $           RefRow path occ (moduleName mod) (moduleUnit mod) sl sc el ec@@ -180,6 +199,16 @@             ec = srcSpanEndCol sp     goRef _ = Nothing +    goImport (Left modName, (sp, IdentifierDetails _ contextInfos)) = do+          _ <- guard $ not $ S.disjoint contextInfos $ S.fromList [IEThing Import, IEThing ImportAs, IEThing ImportHiding]+          let+            sl = srcSpanStartLine sp+            sc = srcSpanStartCol sp+            el = srcSpanEndLine sp+            ec = srcSpanEndCol sp+          Just $ ImportRow path modName sl sc el ec+    goImport _ = Nothing+     goDec (Right name,(_,dets))       | Just mod <- nameModule_maybe name       , mod == smdl@@ -211,11 +240,7 @@   where     genRows = mapMaybe go     getSpan name dets-#if __GLASGOW_HASKELL__ >= 900       | RealSrcSpan sp _ <- nameSrcSpan name = Just sp-#else-      | RealSrcSpan sp <- nameSrcSpan name = Just sp-#endif       | otherwise = do           (sp, _dets) <- find defSpan dets           pure sp
test/Main.hs view
@@ -2,7 +2,7 @@ module Main where  import GHC.Paths (libdir, ghc)-import HieDb (HieDb, HieModuleRow (..), LibDir (..), ModuleInfo (..), withHieDb, withHieFile, addRefsFromLoaded, deleteMissingRealFiles)+import HieDb (HieDb, HieModuleRow (..), LibDir (..), ModuleInfo (..), withHieDb, withHieFile, addRefsFromLoaded, deleteMissingRealFiles, defaultSkipOptions) import HieDb.Query (getAllIndexedMods, lookupHieFile, resolveUnitId, lookupHieFileFromSource) import HieDb.Run (Command (..), Options (..), runCommand) import HieDb.Types (HieDbErr (..), SourceFile(..), runDbM)@@ -88,6 +88,7 @@             -- Index a new real file, and delete it             let contents = unlines                   [ "module Test123 where"+                  , "import Prelude"                   , "foobarbaz :: Int"                   , "foobarbaz = 1"                   ]@@ -124,7 +125,6 @@             afterMods <- getAllIndexedMods conn             originalMods `shouldBe` afterMods - cliSpec :: Spec cliSpec =   -- TODO commands not covered: init, type-refs, ref-graph, dump, reachable, unreachable, html@@ -143,7 +143,7 @@               ]         (exitCode, actualStdin, _actualStdErr) <- runHieDbCli ["ls"]         exitCode `shouldBe` ExitSuccess-        (sort $ lines actualStdin) `shouldBe` (sort expectedOutput)+        sort (lines actualStdin) `shouldBe` sort expectedOutput      describe "name-refs" $       it "lists all references of given function" $ do@@ -167,8 +167,7 @@       it "Prints types of symbol under cursor" $         runHieDbCli ["point-types", "Module1", "10", "10" ]           `succeedsWithStdin` unlines-            [ "Int -> Bool" {- types of `even` function under cursor -}-            , "forall a. Integral a => a -> Bool"+            [ "Bool -> Bool" {- type of `not` function under cursor -}             ]       it "Fails for symbols that don't have type associated" $ do         (exitCode, actualStdout, actualStderr) <- runHieDbCli ["point-types", "Module1", "8", "21"]@@ -201,56 +200,22 @@             [ "Span: test/data/Sub/Module2.hs:11:7-23"             , "Constructors: {(ConDeclH98, ConDecl)}"             , "Identifiers:"-            , "Symbol:c::Data1Constructor1:Sub.Module2:main"+            , "Symbol:c:Data1Constructor1:Sub.Module2:main"             , "Data1Constructor1 defined at test/data/Sub/Module2.hs:11:7-23"-#if __GLASGOW_HASKELL__ >= 900             , "    Details:  Nothing {declaration of constructor bound at: test/data/Sub/Module2.hs:11:7-23}"-#else-            , "    IdentifierDetails Nothing {Decl ConDec (Just SrcSpanOneLine \"test/data/Sub/Module2.hs\" 11 7 24)}"-#endif             , "Types:\n"             ]       it "correctly prints type signatures" $         runHieDbCli ["point-info", "Module1", "10", "10"]           `succeedsWithStdin` unlines-#if __GLASGOW_HASKELL__ >= 904-            [ "Span: test/data/Module1.hs:10:8-11"-            , "Constructors: {(HsVar, HsExpr), (XExpr, HsExpr)}"-            , "Identifiers:"-            , "$dIntegral defined at <no location info>"-            , "    Details:  Just Integral Int {usage of evidence variable}"-            , "Symbol:v::even:GHC.Real:base"-            , "even defined at <no location info>"-            , "    Details:  Just forall a. Integral a => a -> Bool {usage}"-#elif __GLASGOW_HASKELL__ >= 902-            [ "Span: test/data/Module1.hs:10:8-11"-            , "Constructors: {(XExpr, HsExpr), (HsVar, HsExpr)}"-            , "Identifiers:"-            , "Symbol:v::even:GHC.Real:base"-            , "even defined at <no location info>"-            , "    Details:  Just forall a. Integral a => a -> Bool {usage}"-            , "$dIntegral defined at <no location info>"-            , "    Details:  Just Integral Int {usage of evidence variable}"-#elif __GLASGOW_HASKELL__ >= 900-            [ "Span: test/data/Module1.hs:10:8-11"-            , "Constructors: {(HsVar, HsExpr), (XExpr, HsExpr)}"-            , "Identifiers:"-            , "Symbol:v::even:GHC.Real:base"-            , "even defined at <no location info>"-            , "    Details:  Just forall a. Integral a => a -> Bool {usage}"-            , "$dIntegral defined at <no location info>"-            , "    Details:  Just Integral Int {usage of evidence variable}"-#else-            [ "Span: test/data/Module1.hs:10:8-11"-            , "Constructors: {(HsVar, HsExpr), (HsWrap, HsExpr)}"+            [ "Span: test/data/Module1.hs:10:8-10"+            , "Constructors: {(HsVar, HsExpr)}"             , "Identifiers:"-            , "Symbol:v::even:GHC.Real:base"-            , "even defined at <no location info>"-            , "    IdentifierDetails Just forall a. Integral a => a -> Bool {Use}"-#endif+            , "Symbol:v:not:GHC.Classes:ghc-prim"+            , "not defined at <no location info>"+            , "    Details:  Just Bool -> Bool {usage}"             , "Types:"-            , "Int -> Bool"-            , "forall a. Integral a => a -> Bool"+            , "Bool -> Bool"             , ""             ] @@ -388,4 +353,5 @@   , reindex = False   , keepMissing = False   , srcBaseDir = Nothing+  , skipIndexingOptions = defaultSkipOptions   }
test/data/Module1.hs view
@@ -5,9 +5,9 @@  import Sub.Module2 (showInt) -function1 :: Int -> String-function1 i =-    if even i then "It's even" else "It's odd"+function1 :: Bool -> String+function1 b =+    if not b then "It's false" else "It's true"  function2 :: Int -> IO () function2 i = putStrLn $ showInt i