hsdev 0.3.1.1 → 0.3.1.2
raw patch · 28 files changed
+385/−144 lines, 28 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Data.LookupTable: insertTable :: (Ord k, MonadIO m) => k -> v -> LookupTable k v -> m v
+ Data.LookupTable: insertTableM :: (Ord k, MonadIO m) => k -> m v -> LookupTable k v -> m v
+ Data.LookupTable: storeInTable :: (Ord k, MonadIO m) => LookupTable k v -> k -> v -> m v
+ Data.LookupTable: storeInTableM :: (Ord k, MonadIO m) => LookupTable k v -> k -> m v -> m v
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Symbols.Types.Import
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToRow.ToRow HsDev.Symbols.Types.Import
+ HsDev.Database.SQLite.Select: qImport :: Text -> Select Text
+ HsDev.Inspect.Resolve: loadEnv :: SessionMonad m => Maybe Path -> m (Environment, FixitiesTable)
+ HsDev.Inspect.Resolve: saveEnv :: SessionMonad m => Maybe Path -> Environment -> FixitiesTable -> m ()
+ HsDev.Inspect.Types: toImport :: ImportDecl SrcSpanInfo -> Import
+ HsDev.Server.Types: [checkLintOpts] :: Command -> [String]
+ HsDev.Server.Types: [lintHlintOpts] :: Command -> [String]
+ HsDev.Server.Types: [sessionResolveEnvironment] :: Session -> LookupTable (Maybe Path) (Environment, FixitiesTable)
+ HsDev.Symbols.Location: [_installedModuleExposed] :: ModuleLocation -> Bool
+ HsDev.Symbols.Location: installedModuleExposed :: Traversal' ModuleLocation Bool
+ HsDev.Symbols.Types: Import :: Position -> Text -> Bool -> Maybe Text -> Import
+ HsDev.Symbols.Types: ImportedSymbol :: Symbol -> ModuleId -> ImportedSymbol
+ HsDev.Symbols.Types: [_importAs] :: Import -> Maybe Text
+ HsDev.Symbols.Types: [_importName] :: Import -> Text
+ HsDev.Symbols.Types: [_importPosition] :: Import -> Position
+ HsDev.Symbols.Types: [_importQualified] :: Import -> Bool
+ HsDev.Symbols.Types: [_importedFrom] :: ImportedSymbol -> ModuleId
+ HsDev.Symbols.Types: [_importedSymbol] :: ImportedSymbol -> Symbol
+ HsDev.Symbols.Types: data Import
+ HsDev.Symbols.Types: data ImportedSymbol
+ HsDev.Symbols.Types: importAs :: Lens' Import (Maybe Text)
+ HsDev.Symbols.Types: importName :: Lens' Import Text
+ HsDev.Symbols.Types: importPosition :: Lens' Import Position
+ HsDev.Symbols.Types: importQualified :: Lens' Import Bool
+ HsDev.Symbols.Types: importedFrom :: Lens' ImportedSymbol ModuleId
+ HsDev.Symbols.Types: importedSymbol :: Lens' ImportedSymbol Symbol
+ HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.Import
+ HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.Import
+ HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.ImportedSymbol
+ HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.Import
+ HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.ImportedSymbol
+ HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.Import
+ HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.ImportedSymbol
+ HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.Import
+ HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.ImportedSymbol
+ HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.Import
+ HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.ImportedSymbol
- HsDev.Database.Update: scan :: UpdateMonad m => m [(Only Int) :. (ModuleLocation :. Inspection)] -> [ModuleToScan] -> [String] -> ([ModuleToScan] -> m ()) -> m ()
+ HsDev.Database.Update: scan :: UpdateMonad m => m [Only Int :. (ModuleLocation :. Inspection)] -> [ModuleToScan] -> [String] -> ([ModuleToScan] -> m ()) -> m ()
- HsDev.Inspect.Types: Resolved :: ModuleName () -> Parsed -> [Symbol] -> [Text] -> [Symbol] -> Table -> [Fixity] -> Resolved
+ HsDev.Inspect.Types: Resolved :: ModuleName () -> Parsed -> [Symbol] -> [Import] -> [Symbol] -> Table -> [Fixity] -> Resolved
- HsDev.Inspect.Types: [_resolvedImports] :: Resolved -> [Text]
+ HsDev.Inspect.Types: [_resolvedImports] :: Resolved -> [Import]
- HsDev.Inspect.Types: resolvedImports :: Lens' Resolved [Text]
+ HsDev.Inspect.Types: resolvedImports :: Lens' Resolved [Import]
- HsDev.Project.Types: entity :: forall a_a1nyJ a_a1oXv. Lens (Extensions a_a1nyJ) (Extensions a_a1oXv) a_a1nyJ a_a1oXv
+ HsDev.Project.Types: entity :: forall a_a1nSR a_a1phD. Lens (Extensions a_a1nSR) (Extensions a_a1phD) a_a1nSR a_a1phD
- HsDev.Project.Types: extensions :: forall a_a1nyJ. Lens' (Extensions a_a1nyJ) [Extension]
+ HsDev.Project.Types: extensions :: forall a_a1nSR. Lens' (Extensions a_a1nSR) [Extension]
- HsDev.Project.Types: ghcOptions :: forall a_a1nyJ. Lens' (Extensions a_a1nyJ) [Text]
+ HsDev.Project.Types: ghcOptions :: forall a_a1nSR. Lens' (Extensions a_a1nSR) [Text]
- HsDev.Scan.Browse: ghcModuleLocation :: PackageConfig -> Module -> ModuleLocation
+ HsDev.Scan.Browse: ghcModuleLocation :: PackageConfig -> Module -> Bool -> ModuleLocation
- HsDev.Scan.Browse: packageDbModules :: GhcM [(PackageConfig, Module)]
+ HsDev.Scan.Browse: packageDbModules :: GhcM [(PackageConfig, Module, Bool)]
- HsDev.Server.Message: message :: forall a_a1MIm a_a1MNL. Lens (Message a_a1MIm) (Message a_a1MNL) a_a1MIm a_a1MNL
+ HsDev.Server.Message: message :: forall a_a1NdS a_a1Njh. Lens (Message a_a1NdS) (Message a_a1Njh) a_a1NdS a_a1Njh
- HsDev.Server.Message: messageId :: forall a_a1MIm. Lens' (Message a_a1MIm) (Maybe String)
+ HsDev.Server.Message: messageId :: forall a_a1NdS. Lens' (Message a_a1NdS) (Maybe String)
- HsDev.Server.Types: CheckLint :: [FileSource] -> [String] -> Bool -> Command
+ HsDev.Server.Types: CheckLint :: [FileSource] -> [String] -> [String] -> Bool -> Command
- HsDev.Server.Types: Lint :: [FileSource] -> Command
+ HsDev.Server.Types: Lint :: [FileSource] -> [String] -> Command
- HsDev.Server.Types: Session :: Connection -> String -> SessionLog -> Watcher -> (Path -> Maybe Text -> IO ()) -> GhcWorker -> Worker (ServerM IO) -> IO () -> IO () -> Chan (IO ()) -> [(String, String)] -> Session
+ HsDev.Server.Types: Session :: Connection -> String -> SessionLog -> Watcher -> (Path -> Maybe Text -> IO ()) -> GhcWorker -> Worker (ServerM IO) -> LookupTable (Maybe Path) (Environment, FixitiesTable) -> IO () -> IO () -> Chan (IO ()) -> [(String, String)] -> Session
- HsDev.Symbols.Location: InstalledModule :: [Path] -> ModulePackage -> Text -> ModuleLocation
+ HsDev.Symbols.Location: InstalledModule :: [Path] -> ModulePackage -> Text -> Bool -> ModuleLocation
- HsDev.Symbols.Parsed: pos :: Annotated ast => Lens' (ast Ann) Position
+ HsDev.Symbols.Parsed: pos :: (Annotated ast, SrcInfo isrc, Data isrc) => Lens' (ast isrc) Position
- HsDev.Symbols.Types: Module :: ModuleId -> Maybe Text -> [Text] -> [Symbol] -> [Fixity] -> Map Name [Symbol] -> Maybe Parsed -> Module
+ HsDev.Symbols.Types: Module :: ModuleId -> Maybe Text -> [Import] -> [Symbol] -> [Fixity] -> Map Name [Symbol] -> Maybe Parsed -> Module
- HsDev.Symbols.Types: [_moduleImports] :: Module -> [Text]
+ HsDev.Symbols.Types: [_moduleImports] :: Module -> [Import]
- HsDev.Symbols.Types: inspectedKey :: forall k_a2h5i t_a2h5j a_a2h5k k_a2k2t. Lens (Inspected k_a2h5i t_a2h5j a_a2h5k) (Inspected k_a2k2t t_a2h5j a_a2h5k) k_a2h5i k_a2k2t
+ HsDev.Symbols.Types: inspectedKey :: forall k_a2hAY t_a2hAZ a_a2hB0 k_a2kLM. Lens (Inspected k_a2hAY t_a2hAZ a_a2hB0) (Inspected k_a2kLM t_a2hAZ a_a2hB0) k_a2hAY k_a2kLM
- HsDev.Symbols.Types: inspection :: forall k_a2h5i t_a2h5j a_a2h5k. Lens' (Inspected k_a2h5i t_a2h5j a_a2h5k) Inspection
+ HsDev.Symbols.Types: inspection :: forall k_a2hAY t_a2hAZ a_a2hB0. Lens' (Inspected k_a2hAY t_a2hAZ a_a2hB0) Inspection
- HsDev.Symbols.Types: inspectionResult :: forall k_a2h5i t_a2h5j a_a2h5k a_a2k2u. Lens (Inspected k_a2h5i t_a2h5j a_a2h5k) (Inspected k_a2h5i t_a2h5j a_a2k2u) (Either HsDevError a_a2h5k) (Either HsDevError a_a2k2u)
+ HsDev.Symbols.Types: inspectionResult :: forall k_a2hAY t_a2hAZ a_a2hB0 a_a2kLN. Lens (Inspected k_a2hAY t_a2hAZ a_a2hB0) (Inspected k_a2hAY t_a2hAZ a_a2kLN) (Either HsDevError a_a2hB0) (Either HsDevError a_a2kLN)
- HsDev.Symbols.Types: inspectionTags :: forall k_a2h5i t_a2h5j a_a2h5k t_a2k2v. Lens (Inspected k_a2h5i t_a2h5j a_a2h5k) (Inspected k_a2h5i t_a2k2v a_a2h5k) (Set t_a2h5j) (Set t_a2k2v)
+ HsDev.Symbols.Types: inspectionTags :: forall k_a2hAY t_a2hAZ a_a2hB0 t_a2kLO. Lens (Inspected k_a2hAY t_a2hAZ a_a2hB0) (Inspected k_a2hAY t_a2kLO a_a2hB0) (Set t_a2hAZ) (Set t_a2kLO)
- HsDev.Symbols.Types: moduleImports :: Lens' Module [Text]
+ HsDev.Symbols.Types: moduleImports :: Lens' Module [Import]
- HsDev.Symbols.Types: scopeQualifier :: forall a_a2h5l. Lens' (Scoped a_a2h5l) (Maybe Text)
+ HsDev.Symbols.Types: scopeQualifier :: forall a_a2hB1. Lens' (Scoped a_a2hB1) (Maybe Text)
- HsDev.Symbols.Types: scoped :: forall a_a2h5l a_a2jYx. Lens (Scoped a_a2h5l) (Scoped a_a2jYx) a_a2h5l a_a2jYx
+ HsDev.Symbols.Types: scoped :: forall a_a2hB1 a_a2kGX. Lens (Scoped a_a2hB1) (Scoped a_a2kGX) a_a2hB1 a_a2kGX
- HsDev.Tools.HLint: hlint :: FilePath -> Maybe Text -> ExceptT String IO [Note OutputMessage]
+ HsDev.Tools.HLint: hlint :: [String] -> FilePath -> Maybe Text -> ExceptT String IO [Note OutputMessage]
- HsDev.Tools.Types: note :: forall a_a1PVy a_a1Q7t. Lens (Note a_a1PVy) (Note a_a1Q7t) a_a1PVy a_a1Q7t
+ HsDev.Tools.Types: note :: forall a_a1Qr4 a_a1QCZ. Lens (Note a_a1Qr4) (Note a_a1QCZ) a_a1Qr4 a_a1QCZ
- HsDev.Tools.Types: noteLevel :: forall a_a1PVy. Lens' (Note a_a1PVy) (Maybe Severity)
+ HsDev.Tools.Types: noteLevel :: forall a_a1Qr4. Lens' (Note a_a1Qr4) (Maybe Severity)
- HsDev.Tools.Types: noteRegion :: forall a_a1PVy. Lens' (Note a_a1PVy) Region
+ HsDev.Tools.Types: noteRegion :: forall a_a1Qr4. Lens' (Note a_a1Qr4) Region
- HsDev.Tools.Types: noteSource :: forall a_a1PVy. Lens' (Note a_a1PVy) ModuleLocation
+ HsDev.Tools.Types: noteSource :: forall a_a1Qr4. Lens' (Note a_a1Qr4) ModuleLocation
Files
- data/hsdev.sql +6/−2
- hsdev.cabal +2/−1
- src/Data/LookupTable.hs +20/−1
- src/HsDev/Client/Commands.hs +21/−16
- src/HsDev/Database/SQLite.hs +65/−23
- src/HsDev/Database/SQLite/Instances.hs +10/−2
- src/HsDev/Database/SQLite/Select.hs +13/−1
- src/HsDev/Database/Update.hs +22/−23
- src/HsDev/Inspect.hs +2/−3
- src/HsDev/Inspect/Resolve.hs +37/−11
- src/HsDev/Inspect/Types.hs +9/−5
- src/HsDev/PackageDb.hs +2/−2
- src/HsDev/Sandbox.hs +1/−1
- src/HsDev/Scan/Browse.hs +15/−15
- src/HsDev/Server/Base.hs +3/−0
- src/HsDev/Server/Types.hs +17/−9
- src/HsDev/Symbols.hs +1/−1
- src/HsDev/Symbols/Documented.hs +2/−2
- src/HsDev/Symbols/Location.hs +13/−12
- src/HsDev/Symbols/Parsed.hs +1/−1
- src/HsDev/Symbols/Types.hs +59/−3
- src/HsDev/Tools/HDocs.hs +1/−1
- src/HsDev/Tools/HLint.hs +4/−4
- src/HsDev/Watcher.hs +1/−1
- tests/Test.hs +26/−1
- tests/data/ModuleTwo.broken.hs +29/−0
- tests/data/base.sql +1/−1
- tests/test-package/ModuleThree.hs +2/−2
data/hsdev.sql view
@@ -115,7 +115,8 @@ from projects_deps as pdbs, modules as m where (m.package_name == pdbs.package_name) and - (m.package_version == pdbs.package_version) + (m.package_version == pdbs.package_version) and + m.exposed union select p.cabal, m.id from projects as p, modules as m @@ -126,6 +127,7 @@ where (m.package_name == ps.package_name) and (m.package_version == ps.package_version) and + m.exposed and (ps.package_db in ('user-db', 'global-db')); -- symbols @@ -161,6 +163,7 @@ package_name text, -- package name of module package_version text, -- package version installed_name text, -- if not null, should be equal to name + exposed integer, -- is module exposed or hidden -- some other location other_location text, -- anything @@ -186,6 +189,7 @@ create table imports ( module_id integer not null, line integer not null, -- line number of import + column integer not null, -- column of impoty module_name text not null, -- import module name qualified integer not null, -- is import qualified alias text, -- imported with `as` @@ -194,7 +198,7 @@ import_module_id -- `id` of imported module ); -create unique index imports_position_index on imports (module_id, line); +create unique index imports_position_index on imports (module_id, line, column); -- symbols bringed into scope by import, with qualifier create view imported_scopes as
hsdev.cabal view
@@ -1,5 +1,5 @@ name: hsdev -version: 0.3.1.1 +version: 0.3.1.2 synopsis: Haskell development library description: Haskell development library and tool with support of autocompletion, symbol info, go to declaration, find references, hayoo search etc. @@ -17,6 +17,7 @@ tests/test-package/test-package.cabal tests/data/base.sql tests/data/ModuleTwo.modified.hs + tests/data/ModuleTwo.broken.hs data/hsdev.sql source-repository head
src/Data/LookupTable.hs view
@@ -3,7 +3,8 @@ newLookupTable, lookupTable, lookupTableM, cacheInTableM, hasLookupTable, - cachedInTable + cachedInTable, + insertTable, insertTableM, storeInTable, storeInTableM ) where import Control.Monad.IO.Class @@ -44,3 +45,21 @@ -- | Make function caching results in @LookupTable@ cachedInTable :: (Ord k, MonadIO m) => LookupTable k v -> (k -> m v) -> k -> m v cachedInTable tbl fn key = cacheInTableM tbl key (fn key) + +-- | Insert value into table and return it +insertTable :: (Ord k, MonadIO m) => k -> v -> LookupTable k v -> m v +insertTable key value tbl = liftIO $ modifyMVar tbl $ \tbl' -> return (M.insert key value tbl', value) + +-- | Insert value into table and return it +insertTableM :: (Ord k, MonadIO m) => k -> m v -> LookupTable k v -> m v +insertTableM key mvalue tbl = do + value <- mvalue + insertTable key value tbl + +-- | @insertTable@ with flipped args +storeInTable :: (Ord k, MonadIO m) => LookupTable k v -> k -> v -> m v +storeInTable tbl key value = insertTable key value tbl + +-- | @insertTable@ with flipped args +storeInTableM :: (Ord k, MonadIO m) => LookupTable k v -> k -> m v -> m v +storeInTableM tbl key mvalue = insertTableM key mvalue tbl
src/HsDev/Client/Commands.hs view
@@ -181,13 +181,12 @@ runCommand (InfoSymbol sq filters True _) = toValue $ do let (conds, params) = targetFilters "m" (Just "s") filters - rs <- queryNamed @SymbolId + queryNamed @SymbolId (toQuery $ mconcat [ qSymbolId, where_ ["s.name like :pattern escape '\\'"], where_ conds]) ([":pattern" := likePattern sq] ++ params) - return rs runCommand (InfoSymbol sq filters False _) = toValue $ do let (conds, params) = targetFilters "m" (Just "s") filters @@ -214,26 +213,32 @@ (Only mid) let fixities' = fromMaybe [] (fixities >>= fromJSON') + imports' <- query @_ @Import (toQuery $ mconcat [ + qImport "i", + where_ ["i.module_id = ?"]]) + (Only mid) exports' <- query @_ @Symbol (toQuery $ mconcat [ qSymbol, from_ ["exports as e"], where_ ["e.module_id == ?", "e.symbol_id == s.id"]]) (Only mid) - return $ Module mheader docs mempty exports' fixities' mempty Nothing + return $ Module mheader docs imports' exports' fixities' mempty Nothing runCommand (InfoProject (Left projName)) = toValue $ findProject projName runCommand (InfoProject (Right projPath)) = toValue $ liftIO $ searchProject (view path projPath) runCommand (InfoSandbox sandbox') = toValue $ liftIO $ searchSandbox sandbox' runCommand (Lookup nm fpath) = toValue $ - query @_ @Symbol (toQuery $ mconcat [ + fmap (map (\(s :. m) -> ImportedSymbol s m)) $ query @_ @(Symbol :. ModuleId) (toQuery $ mconcat [ qSymbol, + qModuleId, from_ ["projects_modules_scope as pms", "modules as srcm", "exports as e"], where_ [ "pms.cabal is srcm.cabal", - "srcm.file == ?", - "pms.module_id == e.module_id", - "m.id == s.module_id", - "s.id == e.symbol_id", - "s.name == ?"]]) + "srcm.file = ?", + "pms.module_id = e.module_id", + "m.id = s.module_id", + "s.id = e.symbol_id", + "e.module_id = mu.id", + "s.name = ?"]]) (fpath ^. path, nm) runCommand (Whois nm fpath) = toValue $ do let @@ -387,15 +392,15 @@ "name" .= nm, "line" .= line, "column" .= column]) rs -runCommand (Lint fs) = toValue $ liftM concat $ forM fs $ \fsrc -> do +runCommand (Lint fs lints) = toValue $ liftM concat $ forM fs $ \fsrc -> do FileSource f c <- actualFileContents fsrc - liftIO $ hsdevLift $ HLint.hlint (view path f) c + liftIO $ hsdevLift $ HLint.hlint lints (view path f) c runCommand (Check fs ghcs' clear) = toValue $ Log.scope "check" $ liftM concat $ mapM (runCheck ghcs' clear) fs -runCommand (CheckLint fs ghcs' clear) = toValue $ do +runCommand (CheckLint fs ghcs' lints clear) = toValue $ do fs' <- mapM actualFileContents fs checkMsgs <- liftM concat $ mapM (runCheck ghcs' clear) fs' - lintMsgs <- liftIO $ hsdevLift $ liftM concat $ mapM (\(FileSource f c) -> HLint.hlint (view path f) c) fs' + lintMsgs <- liftIO $ hsdevLift $ liftM concat $ mapM (\(FileSource f c) -> HLint.hlint lints (view path f) c) fs' return $ checkMsgs ++ lintMsgs runCommand (Types fs ghcs' clear) = toValue $ do liftM concat $ forM fs $ \fsrc@(FileSource file msrc) -> do @@ -420,7 +425,7 @@ if actual' && hasTypes' then do types' <- query @_ @(Region :. Types.TypedExpr) "select line, column, line_to, column_to, expr, type from types where module_id = ?;" (Only mid') - liftM Just $ forM types' $ \(rgn :. texpr) -> return $ Tools.Note { + liftM Just $ forM types' $ \(rgn :. texpr) -> return Tools.Note { Tools._noteSource = modId ^. moduleLocation, Tools._noteRegion = rgn, Tools._noteLevel = Nothing, @@ -576,7 +581,7 @@ if null ns then do ns' <- Update.cachedWarnings [m ^. moduleId . moduleLocation] - when (not $ null ns') $ + unless (null ns') $ Log.sendLog Log.Trace $ "returning {} cached warnings for {}" ~~ length ns' ~~ file return ns' else return ns @@ -649,7 +654,7 @@ -- | Get file contents actualFileContents :: CommandMonad m => FileSource -> m FileSource -actualFileContents (FileSource fpath Nothing) = fmap (FileSource fpath) (fmap (fmap snd) $ getFileContents fpath) +actualFileContents (FileSource fpath Nothing) = fmap (FileSource fpath . fmap snd) (getFileContents fpath) actualFileContents fcts = return fcts -- | Set session by source
src/HsDev/Database/SQLite.hs view
@@ -240,11 +240,11 @@ removeModuleContents :: SessionMonad m => Int -> m () removeModuleContents mid = scope "remove-module-contents" $ do - execute "delete from imports where module_id == ?;" (Only mid) - execute "delete from exports where module_id == ?;" (Only mid) - execute "delete from scopes where module_id == ?;" (Only mid) - execute "delete from names where module_id == ?;" (Only mid) - execute "delete from types where module_id == ?;" (Only mid) + execute "delete from imports where module_id = ?;" (Only mid) + execute "delete from exports where module_id = ?;" (Only mid) + execute "delete from scopes where module_id = ?;" (Only mid) + execute "delete from names where module_id = ?;" (Only mid) + execute "delete from types where module_id = ?;" (Only mid) execute "delete from symbols where module_id = ?;" (Only mid) removeModule :: SessionMonad m => Int -> m () @@ -257,11 +257,11 @@ mmid <- lookupModuleLocation (im ^. inspectedKey) case mmid of Nothing -> do - execute "insert into modules (file, cabal, install_dirs, package_name, package_version, installed_name, other_location, name, docs, fixities, tags, inspection_error, inspection_time, inspection_opts) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" + execute "insert into modules (file, cabal, install_dirs, package_name, package_version, installed_name, exposed, other_location, name, docs, fixities, tags, inspection_error, inspection_time, inspection_opts) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" moduleData lastRow Just mid' -> do - execute "update modules set file = ?, cabal = ?, install_dirs = ?, package_name = ?, package_version = ?, installed_name = ?, other_location = ?, name = ?, docs = ?, fixities = ?, tags = ?, inspection_error = ?, inspection_time = ?, inspection_opts = ? where id == ?;" + execute "update modules set file = ?, cabal = ?, install_dirs = ?, package_name = ?, package_version = ?, installed_name = ?, exposed = ?, other_location = ?, name = ?, docs = ?, fixities = ?, tags = ?, inspection_error = ?, inspection_time = ?, inspection_opts = ? where id == ?;" (moduleData :. Only mid') return mid' where @@ -272,6 +272,7 @@ im ^? inspectedKey . modulePackage . packageName, im ^? inspectedKey . modulePackage . packageVersion, im ^? inspectedKey . installedModuleName, + im ^? inspectedKey . installedModuleExposed, im ^? inspectedKey . otherLocationName) :. ( msum [im ^? inspected . moduleId . moduleName, im ^? inspectedKey . installedModuleName], @@ -283,6 +284,50 @@ fromMaybe InspectionNone (im ^? inspection) asDict tags = object [fromString (Display.display t) .= True | t <- S.toList tags] +upsertModules :: SessionMonad m => [InspectedModule] -> m [Int] +upsertModules ims = scope "upsert-modules" $ bracket_ initTemp removeTemp $ do + executeMany "insert into upserted_modules (file, cabal, install_dirs, package_name, package_version, installed_name, exposed, other_location, name, docs, fixities, tags, inspection_error, inspection_time, inspection_opts) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" $ map moduleData ims + execute_ "update upserted_modules set id = (select m.id from modules as m where (m.file = upserted_modules.file) or ((m.package_name = upserted_modules.package_name) and (m.package_version = upserted_modules.package_version) and (m.installed_name = upserted_modules.installed_name)) or (m.other_location = upserted_modules.other_location));" + execute_ "insert or replace into modules (id, file, cabal, install_dirs, package_name, package_version, installed_name, exposed, other_location, name, docs, fixities, tags, inspection_error, inspection_time, inspection_opts) select id, file, cabal, install_dirs, package_name, package_version, installed_name, exposed, other_location, name, docs, fixities, tags, inspection_error, inspection_time, inspection_opts from upserted_modules where id is not null;" + execute_ "insert into modules (file, cabal, install_dirs, package_name, package_version, installed_name, exposed, other_location, name, docs, fixities, tags, inspection_error, inspection_time, inspection_opts) select file, cabal, install_dirs, package_name, package_version, installed_name, exposed, other_location, name, docs, fixities, tags, inspection_error, inspection_time, inspection_opts from upserted_modules where id is null;" + execute_ "update upserted_modules set id = (select m.id from modules as m where (m.file = upserted_modules.file) or ((m.package_name = upserted_modules.package_name) and (m.package_version = upserted_modules.package_version) and (m.installed_name = upserted_modules.installed_name)) or (m.other_location = upserted_modules.other_location)) where id is null;" + + execute_ "delete from imports where module_id in (select id from upserted_modules);" + execute_ "delete from exports where module_id in (select id from upserted_modules);" + execute_ "delete from scopes where module_id in (select id from upserted_modules);" + execute_ "delete from names where module_id in (select id from upserted_modules);" + execute_ "delete from types where module_id in (select id from upserted_modules);" + execute_ "delete from symbols where module_id in (select id from upserted_modules);" + + liftM (map fromOnly) $ query_ "select id from upserted_modules;" + where + initTemp :: SessionMonad m => m () + initTemp = do + execute_ "create temporary table upserted_modules as select * from modules where 0;" + execute_ "create index upserted_modules_id_index on upserted_modules (id);" + + removeTemp :: SessionMonad m => m () + removeTemp = execute_ "drop table if exists upserted_modules;" + + moduleData im = ( + im ^? inspectedKey . moduleFile . path, + im ^? inspectedKey . moduleProject . _Just . projectCabal, + fmap (encode . map (view path)) (im ^? inspectedKey . moduleInstallDirs), + im ^? inspectedKey . modulePackage . packageName, + im ^? inspectedKey . modulePackage . packageVersion, + im ^? inspectedKey . installedModuleName, + im ^? inspectedKey . installedModuleExposed, + im ^? inspectedKey . otherLocationName) + :. ( + msum [im ^? inspected . moduleId . moduleName, im ^? inspectedKey . installedModuleName], + im ^? inspected . moduleDocs, + fmap encode $ im ^? inspected . moduleFixities, + encode $ asDict $ im ^. inspectionTags, + fmap show $ im ^? inspectionResult . _Left) + :. + fromMaybe InspectionNone (im ^? inspection) + asDict tags = object [fromString (Display.display t) .= True | t <- S.toList tags] + insertModuleSymbols :: SessionMonad m => InspectedModule -> m () insertModuleSymbols im = scope "insert-module-symbols" $ do [Only mid] <- queryNamed "select id from modules where ((file is null and :file is null) or file = :file) and ((package_name is null and :package_name is null) or package_name = :package_name) and ((package_version is null and :package_version is null) or package_version = :package_version) and ((other_location is null and :other_location is null) or other_location = :other_location) and ((installed_name is null and :installed_name is null) or installed_name = :installed_name);" [ @@ -308,12 +353,13 @@ importRow idecl@(ImportDecl _ mname qual _ _ _ alias specList) = ( mid, idecl ^. pos . positionLine, + idecl ^. pos . positionColumn, getModuleName mname, qual, fmap getModuleName alias, maybe False getHiding specList, fmap makeImportList specList) - executeMany "insert into imports (module_id, line, module_name, qualified, alias, hiding, import_list) values (?, ?, ?, ?, ?, ?, ?);" + executeMany "insert into imports (module_id, line, column, module_name, qualified, alias, hiding, import_list) values (?, ?, ?, ?, ?, ?, ?, ?);" (map importRow imps) executeNamed "update imports set import_module_id = (select im.id from modules as im, projects_modules_scope as ps where ((ps.cabal is null and :cabal is null) or (ps.cabal == :cabal)) and ps.module_id == im.id and im.name == imports.module_name) where module_id == :module_id;" [ ":cabal" := im ^? inspectedKey . moduleProject . _Just . projectCabal, @@ -400,7 +446,7 @@ symbolsColumns :: [String] symbolsColumns = [ "name", "module_name", - "file", "cabal", "install_dirs", "package_name", "package_version", "installed_name", "other_location", + "file", "cabal", "install_dirs", "package_name", "package_version", "installed_name", "exposed", "other_location", "docs", "line", "column", "what", "type", "parent", "constructors", "args", "context", "associate", "pat_type", "pat_constructor"] @@ -428,7 +474,7 @@ insertMissingModules :: SessionMonad m => String -> m () insertMissingModules tableName = scope "insert-missing-modules" $ do - execute_ (fromString ("insert into modules (file, cabal, install_dirs, package_name, package_version, installed_name, other_location, name) select distinct file, cabal, install_dirs, package_name, package_version, installed_name, other_location, module_name from {} where module_id is null;" ~~ tableName)) + execute_ (fromString ("insert into modules (file, cabal, install_dirs, package_name, package_version, installed_name, exposed, other_location, name) select distinct file, cabal, install_dirs, package_name, package_version, installed_name, exposed, other_location, module_name from {} where module_id is null;" ~~ tableName)) updateModuleIds tableName insertMissingSymbols :: SessionMonad m => String -> m () @@ -490,11 +536,11 @@ from_ ["exports as e"], where_ ["e.module_id == ?", "e.symbol_id == s.id"]]) (Only mid) - inames <- query @_ @(Only Text) "select distinct module_name from imports where module_id == ?;" (Only mid) + imps <- query @_ @Import "select line, column, module_name, qualified, alias from imports where module_id == ?;" (Only mid) return $ Module { _moduleId = mid', _moduleDocs = mdocs, - _moduleImports = map fromOnly inames, + _moduleImports = imps, _moduleExports = syms, _moduleFixities = fromMaybe [] (mfixities >>= fromJSON'), _moduleScope = mempty, @@ -515,11 +561,11 @@ from_ ["exports as e"], where_ ["e.module_id == ?", "e.symbol_id == s.id"]]) (Only mid) - inames <- query @_ @(Only Text) "select distinct module_name from imports where module_id == ?;" (Only mid) + imps <- query @_ @Import "select line, column, module_name, qualified, alias from imports where module_id == ?;" (Only mid) return $ Module { _moduleId = mid', _moduleDocs = mdocs, - _moduleImports = map fromOnly inames, + _moduleImports = imps, _moduleExports = syms, _moduleFixities = fromMaybe [] (mfixities >>= fromJSON'), _moduleScope = mempty, @@ -573,18 +619,14 @@ -- | Update a bunch of modules updateModules :: SessionMonad m => [InspectedModule] -> m () updateModules ims = scope "update-modules" $ transaction_ Immediate $ do - mapM_ (upsertModule >=> removeModuleContents) ims - insertModulesSymbols ims + ids <- upsertModules ims + insertModulesSymbols $ zip ids ims -- | Update symbols of bunch of modules -insertModulesSymbols :: SessionMonad m => [InspectedModule] -> m () +insertModulesSymbols :: SessionMonad m => [(Int, InspectedModule)] -> m () insertModulesSymbols ims = scope "update-modules" $ timer "updated modules" $ do - ids <- mapM lookupId (ims ^.. each . inspectedKey) - let - imods = zip ids ims - - insertModulesDefs imods - insertModulesExports imods + insertModulesDefs ims + insertModulesExports ims where insertModulesDefs :: SessionMonad m => [(Int, InspectedModule)] -> m () insertModulesDefs imods = executeMany "insert into symbols (name, module_id, docs, line, column, what, type, parent, constructors, args, context, associate, pat_type, pat_constructor) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" $ do
src/HsDev/Database/SQLite/Instances.hs view
@@ -76,11 +76,12 @@ pname <- field pver <- field iname <- field + iexposed <- field other <- field - maybe (fail $ "Can't parse module location: {}" ~~ show (file, cabal, dirs, pname, pver, iname, other)) return $ msum [ + maybe (fail $ "Can't parse module location: {}" ~~ show (file, cabal, dirs, pname, pver, iname, iexposed, other)) return $ msum [ FileModule <$> file <*> pure (project <$> cabal), - InstalledModule <$> maybe (pure []) fromJSON' dirs <*> (ModulePackage <$> pname <*> pver) <*> iname, + InstalledModule <$> maybe (pure []) fromJSON' dirs <*> (ModulePackage <$> pname <*> pver) <*> iname <*> iexposed, OtherLocation <$> other, pure NoLocation] @@ -92,6 +93,7 @@ toField $ mloc ^? modulePackage . packageName, toField $ mloc ^? modulePackage . packageVersion, toField $ mloc ^? installedModuleName, + toField $ mloc ^? installedModuleExposed, toField $ mloc ^? otherLocationName] instance FromRow ModuleId where @@ -99,6 +101,12 @@ instance ToRow ModuleId where toRow mid = toField (mid ^. moduleName) : toRow (mid ^. moduleLocation) + +instance FromRow Import where + fromRow = Import <$> fromRow <*> field <*> field <*> field + +instance ToRow Import where + toRow (Import p n q a) = toRow p ++ [toField n, toField q, toField a] instance FromRow SymbolId where fromRow = SymbolId <$> field <*> fromRow
src/HsDev/Database/SQLite/Select.hs view
@@ -2,7 +2,7 @@ module HsDev.Database.SQLite.Select ( Select(..), select_, from_, where_, buildQuery, toQuery, - qSymbolId, qSymbol, qModuleLocation, qModuleId, qBuildInfo, + qSymbolId, qSymbol, qModuleLocation, qModuleId, qImport, qBuildInfo, qNSymbol, qNote ) where @@ -54,6 +54,7 @@ "m.package_name", "m.package_version", "m.installed_name", + "m.exposed", "m.other_location"], from_ ["modules as m", "symbols as s"], where_ ["m.id == s.module_id"]] @@ -84,6 +85,7 @@ "{ml}.package_name", "{ml}.package_version", "{ml}.installed_name", + "{ml}.exposed", "{ml}.other_location"], from_ ["modules as {ml}"]] @@ -97,9 +99,19 @@ "mu.package_name", "mu.package_version", "mu.installed_name", + "mu.exposed", "mu.other_location"], from_ ["modules as mu"], where_ ["mu.name is not null"]] + +qImport :: Text -> Select Text +qImport i = template ["i" ~% i] [ + select_ [ + "{i}.line", "{i}.column", + "{i}.module_name", + "{i}.qualified", + "{i}.alias"], + from_ ["imports as {i}"]] qBuildInfo :: Select Text qBuildInfo = mconcat [
src/HsDev/Database/Update.hs view
@@ -29,12 +29,12 @@ import Control.Concurrent.MVar import Control.DeepSeq import Control.Exception (ErrorCall, evaluate, displayException) -import Control.Lens hiding ((.=)) +import Control.Lens import Control.Monad.Catch (catch, handle, MonadThrow, bracket_) import Control.Monad.Except import Control.Monad.Reader import Control.Monad.Writer -import Control.Monad.State (get, modify, evalStateT) +import Control.Monad.State (get, modify, runStateT) import Data.Aeson import Data.List (intercalate) import Data.String (fromString) @@ -57,7 +57,7 @@ import HsDev.Sandbox import qualified HsDev.Stack as S import HsDev.Symbols -import HsDev.Tools.Ghc.Session hiding (Session, wait, evaluate) +import HsDev.Tools.Ghc.Session hiding (Session, evaluate) import HsDev.Tools.Ghc.Types (fileTypes, TypedExpr) import HsDev.Tools.Types import HsDev.Tools.HDocs @@ -222,7 +222,7 @@ runInspect mloc $ withInspection (return inspection') $ dirtyTag' $ preload (mloc ^?! moduleFile) defines (opts ++ mopts) mcts' ploaded <- runTasks (map pload ms') - sendUpdateAction $ Log.scope "preloaded" $ transact $ mapM_ SQLite.upsertModule $ map (fmap (view asModule)) ploaded + mapM_ (SQLite.upsertModule . fmap (view asModule)) ploaded let mlocs' = ploaded ^.. each . inspected . preloadedId . moduleLocation @@ -231,14 +231,14 @@ let mcabal = mproj ^? _Just . projectCabal - env <- loadEnvironment mcabal - fixities <- loadFixities mcabal + (env, fixities) <- loadEnv mcabal Log.sendLog Log.Trace $ "resolved environment: {} modules" ~~ M.size env case orderBy (preview inspected) ploaded of Left err -> Log.sendLog Log.Error ("failed order dependencies for files: {}" ~~ show err) Right ordered -> do - ms'' <- flip evalStateT (env, fixities) $ runTasks (map inspect' ordered) + (ms'', (updEnv, updFixities)) <- flip runStateT (env, fixities) $ runTasks (map inspect' ordered) + saveEnv mcabal updEnv updFixities mlocs'' <- timer "updated scanned modules" $ do Log.sendLog Log.Trace $ case mproj of Just proj -> "inserting data for resolved modules of project: {}" ~~ proj @@ -288,9 +288,9 @@ mproj <- locateProjectInfo fpath' return $ FileModule fpath' mproj let - filesMods = liftM concat $ forM fpaths' $ \fpath' -> SQLite.query "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.other_location, m.inspection_time, m.inspection_opts from modules as m where m.file == ?;" (SQLite.Only fpath') + filesMods = liftM concat $ forM fpaths' $ \fpath' -> SQLite.query "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.exposed, m.other_location, m.inspection_time, m.inspection_opts from modules as m where m.file == ?;" (SQLite.Only fpath') scan filesMods [(mloc, opts, mcts) | (mloc, (FileSource _ mcts, opts)) <- zip mlocs fsrcs] [] $ \mlocs' -> do - mapM_ (watch . flip watchModule) (map (view _1) mlocs') + mapM_ ((watch . flip watchModule) . view _1) mlocs' S.ScanContents dmods _ _ <- fmap mconcat $ mapM (S.enumDependent . view (_1 . moduleFile . path)) mlocs' Log.sendLog Log.Trace $ "dependent modules: {}" ~~ length dmods scanModules [] (mlocs' ++ dmods) @@ -330,14 +330,14 @@ pkgs <- SQLite.query "select package_name, package_version from package_dbs where package_db == ?;" (SQLite.Only $ topPackageDb pdbs) if S.fromList packages' == S.fromList pkgs - then Log.sendLog Log.Trace $ "nothing changes, all packages the same" + then Log.sendLog Log.Trace "nothing changes, all packages the same" else do mlocs <- liftM (filter (`S.member` packageDbMods)) $ (inSessionGhc $ listModules opts pdbs packages') Log.sendLog Log.Trace $ "{} modules found" ~~ length mlocs let - packageDbMods' = SQLite.query "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.other_location, m.inspection_time, m.inspection_opts from modules as m, package_dbs as ps where m.package_name == ps.package_name and m.package_version == ps.package_version and ps.package_db == ?;" (SQLite.Only (topPackageDb pdbs)) + packageDbMods' = SQLite.query "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.exposed, m.other_location, m.inspection_time, m.inspection_opts from modules as m, package_dbs as ps where m.package_name == ps.package_name and m.package_version == ps.package_version and ps.package_db == ?;" (SQLite.Only (topPackageDb pdbs)) scan packageDbMods' ((,,) <$> mlocs <*> pure [] <*> pure Nothing) opts $ \mlocs' -> do ms <- inSessionGhc $ browseModules opts pdbs (mlocs' ^.. each . _1) Log.sendLog Log.Trace $ "scanned {} modules" ~~ length ms @@ -361,14 +361,14 @@ pkgs <- liftM concat $ forM (packageDbs pdbs) $ \pdb -> SQLite.query "select package_name, package_version from package_dbs where package_db == ?;" (SQLite.Only pdb) if S.fromList packages' == S.fromList pkgs - then Log.sendLog Log.Trace $ "nothing changes, all packages the same" + then Log.sendLog Log.Trace "nothing changes, all packages the same" else do mlocs <- liftM (filter (`S.member` packageDbMods)) $ (inSessionGhc $ listModules opts pdbs packages') Log.sendLog Log.Trace $ "{} modules found" ~~ length mlocs let - packageDbStackMods = liftM concat $ forM (packageDbs pdbs) $ \pdb -> SQLite.query "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.other_location, m.inspection_time, m.inspection_opts from modules as m, package_dbs as ps where m.package_name == ps.package_name and m.package_version == ps.package_version and ps.package_db == ?;" (SQLite.Only pdb) + packageDbStackMods = liftM concat $ forM (packageDbs pdbs) $ \pdb -> SQLite.query "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.exposed, m.other_location, m.inspection_time, m.inspection_opts from modules as m, package_dbs as ps where m.package_name == ps.package_name and m.package_version == ps.package_version and ps.package_db == ?;" (SQLite.Only pdb) scan packageDbStackMods ((,,) <$> mlocs <*> pure [] <*> pure Nothing) opts $ \mlocs' -> do ms <- inSessionGhc $ browseModules opts pdbs (mlocs' ^.. each . _1) Log.sendLog Log.Trace $ "scanned {} modules" ~~ length ms @@ -405,7 +405,7 @@ -- | Refine project info and update if necessary refineProjectInfo :: UpdateMonad m => Project -> m Project refineProjectInfo proj = do - [(SQLite.Only exist)] <- SQLite.query "select count(*) > 0 from projects where cabal == ?;" (SQLite.Only (proj ^. projectCabal)) + [SQLite.Only exist] <- SQLite.query "select count(*) > 0 from projects where cabal == ?;" (SQLite.Only (proj ^. projectCabal)) if exist then SQLite.loadProject (proj ^. projectCabal) else runTask "scanning" (proj ^. projectCabal) $ do @@ -435,9 +435,8 @@ watch (\w -> watchProject w proj opts) S.ScanContents _ [(_, sources)] _ <- S.enumProject proj let - projMods = SQLite.query "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.other_location, m.inspection_time, m.inspection_opts from modules as m where m.file is not null and m.cabal == ?;" (SQLite.Only $ proj ^. projectCabal) - scan projMods sources opts $ \mlocs' -> do - scanModules opts mlocs' + projMods = SQLite.query "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.exposed, m.other_location, m.inspection_time, m.inspection_opts from modules as m where m.file is not null and m.cabal == ?;" (SQLite.Only $ proj ^. projectCabal) + scan projMods sources opts $ scanModules opts -- Scan docs -- inSessionGhc $ do @@ -459,7 +458,7 @@ runTasks_ $ map (scanPackageDb opts) pdbss -- TODO: Don't rescan mapMOf_ (each . _1) (watch . flip watchModule) standSrcs let - standaloneMods = SQLite.query "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.other_location, m.inspection_time, m.inspection_opts from modules as m where m.cabal is null and m.file is not null and m.file like ? escape '\\';" (SQLite.Only $ SQLite.escapeLike dir `T.append` "%") + standaloneMods = SQLite.query "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.exposed, m.other_location, m.inspection_time, m.inspection_opts from modules as m where m.cabal is null and m.file is not null and m.file like ? escape '\\';" (SQLite.Only $ SQLite.escapeLike dir `T.append` "%") scan standaloneMods standSrcs opts $ scanModules opts scanContents :: UpdateMonad m => [String] -> S.ScanContents -> m () @@ -467,7 +466,7 @@ projs <- liftM (map SQLite.fromOnly) $ SQLite.query_ "select cabal from projects;" pdbs <- liftM (map SQLite.fromOnly) $ SQLite.query_ "select package_db from package_dbs;" let - filesMods = SQLite.query_ "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.other_location, m.inspection_time, m.inspection_opts from modules as m where m.file is not null and m.cabal is null;" + filesMods = SQLite.query_ "select m.id, m.file, m.cabal, m.install_dirs, m.package_name, m.package_version, m.installed_name, m.exposed, m.other_location, m.inspection_time, m.inspection_opts from modules as m where m.file is not null and m.cabal is null;" runTasks_ [scanPackageDb opts pdbs' | pdbs' <- pdbss, topPackageDb pdbs' `notElem` pdbs] runTasks_ [scanProject opts (view projectCabal p) | (p, _) <- projSrcs, view projectCabal p `notElem` projs] mapMOf_ (each . _1) (watch . flip watchModule) standSrcs @@ -486,13 +485,13 @@ (nm, doc) <- M.toList mdocs return (doc, nm, mname, pname, pver) Log.sendLog Log.Trace "docs set" - | otherwise = Log.sendLog Log.Warning $ "hdocs not supported" + | otherwise = Log.sendLog Log.Warning "hdocs not supported" -- | Scan docs for inspected modules scanDocs :: UpdateMonad m => [Module] -> m () scanDocs | hdocsSupported = runTasks_ . map scanDocs' - | otherwise = const $ Log.sendLog Log.Warning $ "hdocs not supported" + | otherwise = const $ Log.sendLog Log.Warning "hdocs not supported" where scanDocs' m = runTask "scanning docs" (view (moduleId . moduleLocation) m) $ Log.scope "docs" $ do mid <- SQLite.lookupModule (m ^. moduleId) @@ -544,7 +543,7 @@ -- | Generic scan function. Removed obsolete modules and calls callback on changed modules. scan :: UpdateMonad m - => m [(SQLite.Only Int) SQLite.:. ModuleLocation SQLite.:. Inspection] + => m [SQLite.Only Int SQLite.:. ModuleLocation SQLite.:. Inspection] -- ^ Get affected modules, obsolete will be removed, changed will be updated -> [S.ModuleToScan] -- ^ Actual modules, other will be removed @@ -554,7 +553,7 @@ -- ^ Update function -> m () scan part' mlocs opts act = Log.scope "scan" $ do - mlocs' <- liftM (M.fromList . map (\((SQLite.Only mid) SQLite.:. (m SQLite.:. i)) -> (m, (mid, i)))) part' + mlocs' <- liftM (M.fromList . map (\(SQLite.Only mid SQLite.:. (m SQLite.:. i)) -> (m, (mid, i)))) part' let obsolete = M.filterWithKey (\k _ -> k `S.notMember` S.fromList (map (^. _1) mlocs)) mlocs' changed <- S.changedModules (M.map snd mlocs') opts mlocs
src/HsDev/Inspect.hs view
@@ -52,7 +52,6 @@ import HsDev.Inspect.Resolve import HsDev.Sandbox (searchPackageDbStack) import HsDev.Symbols -import HsDev.Symbols.Name (fromModuleName_) import HsDev.Symbols.Resolve (refineSymbol, refineTable, RefineTable) import qualified HsDev.Symbols.HaskellNames as HN import HsDev.Tools.Base @@ -135,7 +134,7 @@ analyzeResolve (AnalyzeEnv env _ rtable) m = case m ^. moduleSource of Nothing -> m Just msrc -> over moduleSymbols (refineSymbol stbl) $ m { - _moduleImports = map (fromModuleName_ . void . H.importModule) idecls', + _moduleImports = map (toImport . dropScope) idecls', _moduleExports = map HN.fromSymbol $ N.exportedSymbols tbl msrc, _moduleFixities = [Fixity (void assoc) (fromMaybe 0 pr) (fixName opName) | H.InfixDecl _ assoc pr ops <- decls', opName <- map getOpName ops], @@ -192,7 +191,7 @@ hdocsWorkaround = False pdbs <- case view (moduleId . moduleLocation) m of FileModule fpath _ -> searchPackageDbStack fpath - InstalledModule _ _ _ -> return userDb + InstalledModule{} -> return userDb _ -> return userDb docsMap <- if hdocsWorkaround then liftIO $ hdocsProcess (fromMaybe (T.unpack $ view (moduleId . moduleName) m) (preview (moduleId . moduleLocation . moduleFile . path) m)) opts
src/HsDev/Inspect/Resolve.hs view
@@ -2,6 +2,7 @@ module HsDev.Inspect.Resolve ( -- * Prepare + loadEnv, saveEnv, loadEnvironment, loadFixities, withEnv, -- * Resolving resolveModule, resolvePreloaded, resolve, @@ -34,6 +35,7 @@ import System.Log.Simple import Text.Format +import Data.LookupTable import System.Directory.Paths import HsDev.Database.SQLite as SQLite import qualified HsDev.Display as Display @@ -65,7 +67,7 @@ _resolvedModule = void mn, _resolvedSource = annotated, _resolvedDefs = getSymbols decls', - _resolvedImports = map (fromModuleName_ . void . H.importModule) idecls', + _resolvedImports = map (toImport . dropScope) idecls', _resolvedExports = N.exportedSymbols tbl m, _resolvedScope = tbl, _resolvedFixities = [H.Fixity (void assoc) (fromMaybe 0 pr) (fixName opName) @@ -87,6 +89,18 @@ decls' = map (N.annotateDecl (N.initialScope (N.dropAnn mn) tbl)) decls mn = N.getModuleName m +-- | Load environment and fixities from cache or sql +loadEnv :: SessionMonad m => Maybe Path -> m (Environment, FixitiesTable) +loadEnv mcabal = do + envTable <- askSession sessionResolveEnvironment + cacheInTableM envTable mcabal $ (,) <$> loadEnvironment mcabal <*> loadFixities mcabal + +-- | Save environment and fixities to cache +saveEnv :: SessionMonad m => Maybe Path -> Environment -> FixitiesTable -> m () +saveEnv mcabal env fixities = do + envTable <- askSession sessionResolveEnvironment + void $ insertTable mcabal (env, fixities) envTable + -- | Load environment from sql loadEnvironment :: SessionMonad m => Maybe Path -> m Environment loadEnvironment mcabal = transaction_ Deferred $ do @@ -125,12 +139,21 @@ -- | Run with temporary table for environment withEnv :: SessionMonad m => Maybe Path -> m a -> m a -withEnv mcabal = withTemporaryTable "env" ["module text not null", "name text not null", "what text not null", "id integer not null"] . (initEnv >>) where - initEnv = transaction_ Immediate $ do - execute_ "create unique index env_id_index on env (id);" - execute_ "create unique index env_symbol_index on env (module, name, what);" - executeNamed "insert into env select m.name, s.name, s.what, min(s.id) from modules as m, symbols as s where m.id = s.module_id and s.id in (select distinct e.symbol_id from exports as e where e.module_id in (select ps.module_id from projects_modules_scope as ps where ps.cabal is :cabal)) group by m.name, s.name, s.what;" [ - ":cabal" := mcabal] +withEnv mcabal = (initEnv >>) where + initEnv = do + execute_ "create temporary table if not exists resolve (cabal text);" + execute_ "create temporary table if not exists env (module text not null, name text not null, what text not null, id integer not null);" + execute_ "create unique index if not exists env_id_index on env (id);" + execute_ "create unique index if not exists env_symbol_index on env (module, name, what);" + + curEnv <- query_ "select cabal from resolve;" + unless (fmap fromOnly (listToMaybe curEnv) == Just mcabal) $ do + execute_ "delete from resolve;" + execute "insert into resolve values (?);" (Only mcabal) + execute_ "delete from env;" + executeNamed "insert into env select m.name, s.name, s.what, min(s.id) from modules as m, symbols as s where m.id = s.module_id and s.id in (select distinct e.symbol_id from exports as e where e.module_id in (select ps.module_id from projects_modules_scope as ps where ps.cabal is :cabal)) group by m.name, s.name, s.what;" [ + ":cabal" := mcabal] + [Only cnt] <- query_ @(Only Int) "select count(*) from env;" sendLog Trace $ "created env table with {} symbols" ~~ cnt @@ -151,11 +174,11 @@ mmid <- lookupModuleLocation (im ^. inspectedKey) case mmid of Nothing -> do - execute "insert into modules (file, cabal, install_dirs, package_name, package_version, installed_name, other_location, name, docs, fixities, tags, inspection_error, inspection_time, inspection_opts) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" + execute "insert into modules (file, cabal, install_dirs, package_name, package_version, installed_name, exposed, other_location, name, docs, fixities, tags, inspection_error, inspection_time, inspection_opts) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" moduleData lastRow Just mid' -> do - execute "update modules set file = ?, cabal = ?, install_dirs = ?, package_name = ?, package_version = ?, installed_name = ?, other_location = ?, name = ?, docs = ?, fixities = ?, tags = ?, inspection_error = ?, inspection_time = ?, inspection_opts = ? where id == ?;" + execute "update modules set file = ?, cabal = ?, install_dirs = ?, package_name = ?, package_version = ?, installed_name = ?, exposed = ?, other_location = ?, name = ?, docs = ?, fixities = ?, tags = ?, inspection_error = ?, inspection_time = ?, inspection_opts = ? where id == ?;" (moduleData :. Only mid') return mid' where @@ -166,6 +189,7 @@ im ^? inspectedKey . modulePackage . packageName, im ^? inspectedKey . modulePackage . packageVersion, im ^? inspectedKey . installedModuleName, + im ^? inspectedKey . installedModuleExposed, im ^? inspectedKey . otherLocationName) :. ( msum [im ^? inspected . resolvedModule . moduleName_, im ^? inspectedKey . installedModuleName], @@ -195,12 +219,13 @@ importRow idecl@(H.ImportDecl _ mname qual _ _ _ alias specList) = ( mid, idecl ^. pos . positionLine, + idecl ^. pos . positionColumn, getModuleName mname, qual, fmap getModuleName alias, maybe False getHiding specList, fmap makeImportList specList) - executeMany "insert into imports (module_id, line, module_name, qualified, alias, hiding, import_list) values (?, ?, ?, ?, ?, ?, ?);" + executeMany "insert into imports (module_id, line, column, module_name, qualified, alias, hiding, import_list) values (?, ?, ?, ?, ?, ?, ?, ?);" (map importRow imps) executeNamed "update imports set import_module_id = (select im.id from modules as im, projects_modules_scope as ps where ((ps.cabal is null and :cabal is null) or (ps.cabal == :cabal)) and ps.module_id == im.id and im.name == imports.module_name) where module_id == :module_id;" [ ":cabal" := im ^? inspectedKey . moduleProject . _Just . projectCabal, @@ -368,7 +393,7 @@ insertModulesImports :: SessionMonad m => [(Int, InspectedResolved)] -> m () insertModulesImports imods = scope "imports" $ do - executeMany "insert into imports (module_id, line, module_name, qualified, alias, hiding, import_list) values (?, ?, ?, ?, ?, ?, ?);" $ do + executeMany "insert into imports (module_id, line, column, module_name, qualified, alias, hiding, import_list) values (?, ?, ?, ?, ?, ?, ?, ?);" $ do (mid, im) <- imods let p = im ^?! inspected . resolvedSource @@ -376,6 +401,7 @@ return ( mid, idecl ^. pos . positionLine, + idecl ^. pos . positionColumn, getModuleName mname, qual, fmap getModuleName alias,
src/HsDev/Inspect/Types.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE TemplateHaskell #-} module HsDev.Inspect.Types ( - Preloaded(..), preloadedId, preloadedMode, preloadedModule, asModule, preloaded, + Preloaded(..), preloadedId, preloadedMode, preloadedModule, asModule, toImport, preloaded, InspectedPreloaded, Environment, FixitiesTable, Resolved(..), resolvedModule, resolvedSource, resolvedDefs, resolvedImports, resolvedExports, resolvedScope, resolvedFixities, @@ -14,16 +14,16 @@ import Control.DeepSeq import Control.Lens -import Data.Functor import Data.Map (Map) import qualified Data.Map as M +import Data.String import Data.Text (Text) import qualified Language.Haskell.Exts as H import qualified Language.Haskell.Names as N import qualified Language.Haskell.Names.GlobalSymbolTable as N import HsDev.Symbols.Types -import HsDev.Symbols.Parsed (Parsed) +import HsDev.Symbols.Parsed (Parsed, pos) -- | Preloaded module with contents and extensions data Preloaded = Preloaded { @@ -41,7 +41,7 @@ g' p = Module { _moduleId = _preloadedId p, _moduleDocs = Nothing, - _moduleImports = map (fromModuleName_ . void . H.importModule) idecls, + _moduleImports = map toImport idecls, _moduleExports = mempty, _moduleFixities = mempty, _moduleScope = mempty, @@ -52,6 +52,10 @@ _preloadedId = _moduleId m, _preloadedModule = maybe (_preloadedModule p) dropScope (_moduleSource m) } +toImport :: H.ImportDecl H.SrcSpanInfo -> Import +toImport idecl@(H.ImportDecl _ mname qual _ _ _ alias _) = Import (idecl ^. pos) (fromString $ getModuleName mname) qual (fmap (fromString . getModuleName) alias) where + getModuleName (H.ModuleName _ s) = s + type InspectedPreloaded = Inspected ModuleLocation ModuleTag Preloaded -- | Symbols environment, used to resolve names in source @@ -65,7 +69,7 @@ _resolvedModule :: H.ModuleName (), _resolvedSource :: Parsed, _resolvedDefs :: [Symbol], - _resolvedImports :: [Text], + _resolvedImports :: [Import], _resolvedExports :: [N.Symbol], _resolvedScope :: N.Table, _resolvedFixities :: [H.Fixity] }
src/HsDev/PackageDb.hs view
@@ -57,8 +57,8 @@ pname = ModulePackage (pack . show . disp . pkgName $ sourcePackageId pinfo) (pack . show . disp . pkgVersion $ sourcePackageId pinfo) - pmods = map (InstalledModule (map fromFilePath $ libraryDirs pinfo) pname) names - names = map (pack . show . disp) (exposedModules pinfo) ++ map (pack . show . disp) (hiddenModules pinfo) + pmods = [InstalledModule (map fromFilePath $ libraryDirs pinfo) pname nm exposed' | (nm, exposed') <- names] + names = zip (map (pack . show . disp) (exposedModules pinfo)) (repeat True) ++ zip (map (pack . show . disp) (hiddenModules pinfo)) (repeat False) subst Nothing f = f subst (Just libdir') f = case splitPaths f of ("$topdir":rest) -> joinPaths (fromFilePath libdir' : rest)
src/HsDev/Sandbox.hs view
@@ -170,7 +170,7 @@ getModuleOpts opts m = do pdbs <- case view (moduleId . moduleLocation) m of FileModule fpath _ -> searchPackageDbStack fpath - InstalledModule _ _ _ -> return userDb + InstalledModule{} -> return userDb _ -> return userDb pkgs <- browsePackages opts pdbs return $ (pdbs, concat [
src/HsDev/Scan/Browse.hs view
@@ -71,7 +71,7 @@ listModules opts dbs pkgs = do tmpSession dbs (opts ++ packagesOpts) ms <- packageDbModules - return [ghcModuleLocation p m | (p, m) <- ms] + return [ghcModuleLocation p m e | (p, m, e) <- ms] where packagesOpts = ["-package " ++ show p | p <- pkgs] @@ -90,19 +90,19 @@ midTbl <- newLookupTable sidTbl <- newLookupTable let - lookupModuleId p' m' = lookupTable (ghcModuleLocation p' m') (ghcModuleId p' m') midTbl - liftM catMaybes $ sequence [browseModule' lookupModuleId (cacheInTableM sidTbl) p m | (p, m) <- ms, ghcModuleLocation p m `S.member` mlocs'] + lookupModuleId p' m' e' = lookupTable (ghcModuleLocation p' m' e') (ghcModuleId p' m' e') midTbl + liftM catMaybes $ sequence [browseModule' lookupModuleId (cacheInTableM sidTbl) p m e | (p, m, e) <- ms, ghcModuleLocation p m e `S.member` mlocs'] where - browseModule' :: (GHC.PackageConfig -> GHC.Module -> GhcM ModuleId) -> (GHC.Name -> GhcM Symbol -> GhcM Symbol) -> GHC.PackageConfig -> GHC.Module -> GhcM (Maybe InspectedModule) - browseModule' modId' sym' p m = tryT $ runInspect (ghcModuleLocation p m) $ inspect_ (return $ InspectionAt 0 (map fromString opts)) (browseModule modId' sym' p m) + browseModule' :: (GHC.PackageConfig -> GHC.Module -> Bool -> GhcM ModuleId) -> (GHC.Name -> GhcM Symbol -> GhcM Symbol) -> GHC.PackageConfig -> GHC.Module -> Bool -> GhcM (Maybe InspectedModule) + browseModule' modId' sym' p m e = tryT $ runInspect (ghcModuleLocation p m e) $ inspect_ (return $ InspectionAt 0 (map fromString opts)) (browseModule modId' sym' p m e) mlocs' = S.fromList mlocs -browseModule :: (GHC.PackageConfig -> GHC.Module -> GhcM ModuleId) -> (GHC.Name -> GhcM Symbol -> GhcM Symbol) -> GHC.PackageConfig -> GHC.Module -> GhcM Module -browseModule modId lookSym package' m = do +browseModule :: (GHC.PackageConfig -> GHC.Module -> Bool -> GhcM ModuleId) -> (GHC.Name -> GhcM Symbol -> GhcM Symbol) -> GHC.PackageConfig -> GHC.Module -> Bool -> GhcM Module +browseModule modId lookSym package' m exposed' = do df <- GHC.getSessionDynFlags mi <- GHC.getModuleInfo m >>= maybe (hsdevError $ BrowseNoModuleInfo thisModule) return ds <- mapM (\n -> lookSym n (toDecl df mi n)) (GHC.modInfoExports mi) - myModId <- modId package' m + myModId <- modId package' m exposed' let dirAssoc GHC.InfixL = AssocLeft () dirAssoc GHC.InfixR = AssocRight () @@ -121,7 +121,7 @@ mloc df m' = do pkg' <- maybe (hsdevError $ OtherError $ "Error getting module package: " ++ GHC.moduleNameString (GHC.moduleName m')) return $ GHC.lookupPackage df (moduleUnitId m') - modId pkg' m' + modId pkg' m' (GHC.moduleName m `notElem` GHC.hiddenModules pkg') toDecl df minfo n = do tyInfo <- GHC.modInfoLookupName minfo n tyResult <- maybe (GHC.lookupName n) (return . Just) tyInfo @@ -174,23 +174,23 @@ (map (fromString . GHC.moduleNameString . Compat.exposedModuleName) $ GHC.exposedModules pc) (GHC.exposed pc) -ghcModuleLocation :: GHC.PackageConfig -> GHC.Module -> ModuleLocation +ghcModuleLocation :: GHC.PackageConfig -> GHC.Module -> Bool -> ModuleLocation ghcModuleLocation p m = InstalledModule (map fromString $ GHC.libraryDirs p) (readPackage p) (fromString $ GHC.moduleNameString $ GHC.moduleName m) -ghcModuleId :: GHC.PackageConfig -> GHC.Module -> ModuleId -ghcModuleId p m = ModuleId (fromString mname') (ghcModuleLocation p m) where +ghcModuleId :: GHC.PackageConfig -> GHC.Module -> Bool -> ModuleId +ghcModuleId p m e = ModuleId (fromString mname') (ghcModuleLocation p m e) where mname' = GHC.moduleNameString $ GHC.moduleName m packageConfigs :: GhcM [GHC.PackageConfig] packageConfigs = liftM (fromMaybe [] . pkgDatabase) GHC.getSessionDynFlags -packageDbModules :: GhcM [(GHC.PackageConfig, GHC.Module)] +packageDbModules :: GhcM [(GHC.PackageConfig, GHC.Module, Bool)] packageDbModules = do pkgs <- packageConfigs dflags <- GHC.getSessionDynFlags - return [(p, m) | + return [(p, m, exposed') | p <- pkgs, - mn <- map Compat.exposedModuleName (GHC.exposedModules p) ++ GHC.hiddenModules p, + (mn, exposed') <- zip (map Compat.exposedModuleName (GHC.exposedModules p)) (repeat True) ++ zip (GHC.hiddenModules p) (repeat False), m <- lookupModule_ dflags mn] -- Lookup module everywhere
src/HsDev/Server/Base.hs view
@@ -46,6 +46,7 @@ import Control.Concurrent.Util import qualified Control.Concurrent.FiniteChan as F +import Data.LookupTable import System.Directory.Paths import qualified System.Directory.Watcher as Watcher @@ -112,6 +113,7 @@ writeChan (W.watcherChan watcher) (W.WatchedModule, W.Event W.Modified (view path fpath) tm) uw <- startWorker (withSession sess . withSqlConnection) id logAll + resolveEnvTable <- newLookupTable return $ Session sqlDb @@ -124,6 +126,7 @@ #endif ghcw uw + resolveEnvTable (do withLog (sessionLogger slog) $ Log.sendLog Log.Trace "stopping server" signalQSem waitSem)
src/HsDev/Server/Types.hs view
@@ -40,10 +40,12 @@ import System.Log.Simple as Log import Control.Concurrent.Worker +import Data.LookupTable import System.Directory.Paths import Text.Format (Formattable(..)) import HsDev.Error (hsdevError) +import HsDev.Inspect.Types import HsDev.Server.Message import HsDev.Watcher.Types (Watcher) import HsDev.Tools.Ghc.Worker (GhcWorker, GhcM) @@ -74,6 +76,7 @@ #endif sessionGhc :: GhcWorker, sessionUpdater :: Worker (ServerM IO), + sessionResolveEnvironment :: LookupTable (Maybe Path) (Environment, FixitiesTable), sessionExit :: IO (), sessionWait :: IO (), sessionClients :: F.Chan (IO ()), @@ -461,7 +464,9 @@ UnresolvedSymbols { unresolvedFiles :: [Path] } | Lint { - lintFiles :: [FileSource] } | + lintFiles :: [FileSource], + lintHlintOpts :: [String] + } | Check { checkFiles :: [FileSource], checkGhcOpts :: [String], @@ -469,6 +474,7 @@ CheckLint { checkLintFiles :: [FileSource], checkLintGhcOpts :: [String], + checkLintOpts :: [String], checkLinkClear :: Bool } | Types { typesFiles :: [FileSource], @@ -526,9 +532,9 @@ paths f (FindUsages l c fpath) = FindUsages <$> pure l <*> pure c <*> paths f fpath paths f (Complete n g fpath) = Complete n g <$> paths f fpath paths f (UnresolvedSymbols fs) = UnresolvedSymbols <$> traverse (paths f) fs - paths f (Lint fs) = Lint <$> traverse (paths f) fs + paths f (Lint fs lints) = Lint <$> traverse (paths f) fs <*> pure lints paths f (Check fs ghcs c) = Check <$> traverse (paths f) fs <*> pure ghcs <*> pure c - paths f (CheckLint fs ghcs c) = CheckLint <$> traverse (paths f) fs <*> pure ghcs <*> pure c + paths f (CheckLint fs ghcs lints c) = CheckLint <$> traverse (paths f) fs <*> pure ghcs <*> pure lints <*> pure c paths f (Types fs ghcs c) = Types <$> traverse (paths f) fs <*> pure ghcs <*> pure c paths f (GhcEval e mf) = GhcEval e <$> traverse (paths f) mf paths f (GhcType e mf) = GhcType e <$> traverse (paths f) mf @@ -583,9 +589,9 @@ cmd "hayoo" "find declarations online via Hayoo" (Hayoo <$> strArgument idm <*> hayooPageArg <*> hayooPagesArg), cmd "cabal" "cabal commands" (subparser $ cmd "list" "list cabal packages" (CabalList <$> many (textArgument idm))), cmd "unresolveds" "list unresolved symbols in source file" (UnresolvedSymbols <$> many fileArg), - cmd "lint" "lint source files or file contents" (Lint <$> many cmdP), + cmd "lint" "lint source files or file contents" (Lint <$> many cmdP <*> lintOpts), cmd "check" "check source files or file contents" (Check <$> many cmdP <*> ghcOpts <*> clearFlag), - cmd "check-lint" "check and lint source files or file contents" (CheckLint <$> many cmdP <*> ghcOpts <*> clearFlag), + cmd "check-lint" "check and lint source files or file contents" (CheckLint <$> many cmdP <*> ghcOpts <*> lintOpts <*> clearFlag), cmd "types" "get types for file expressions" (Types <$> many cmdP <*> ghcOpts <*> clearFlag), cmd "autofixes" "get autofixes by output messages" (AutoFix <$> option readJSON (long "data" <> metavar "message" <> help "messages to make fixes for")), cmd "refactor" "apply some refactors and get rest updated" (Refactor <$> @@ -644,6 +650,7 @@ holdFlag :: Parser Bool inferFlag :: Parser Bool inspectionFlag :: Parser Bool +lintOpts :: Parser [String] localsFlag :: Parser Bool moduleArg :: Parser Text packageArg :: Parser Text @@ -666,6 +673,7 @@ holdFlag = switch (long "hold" <> short 'h' <> help "don't return any response") inferFlag = switch (long "infer" <> help "infer types") inspectionFlag = switch (long "inspection" <> short 'i' <> help "return inspection data") +lintOpts = many (strOption (long "lint" <> metavar "option" <> short 'l' <> help "options for hlint")) localsFlag = switch (long "locals" <> short 'l' <> help "look in local declarations") moduleArg = textOption (long "module" <> metavar "name" <> short 'm' <> help "module name") packageArg = textOption (long "package" <> metavar "name" <> help "module package") @@ -710,9 +718,9 @@ toJSON (Hayoo q p ps) = cmdJson "hayoo" ["query" .= q, "page" .= p, "pages" .= ps] toJSON (CabalList ps) = cmdJson "cabal list" ["packages" .= ps] toJSON (UnresolvedSymbols fs) = cmdJson "unresolveds" ["files" .= fs] - toJSON (Lint fs) = cmdJson "lint" ["files" .= fs] + toJSON (Lint fs lints) = cmdJson "lint" ["files" .= fs, "lint-opts" .= lints] toJSON (Check fs ghcs c) = cmdJson "check" ["files" .= fs, "ghc-opts" .= ghcs, "clear" .= c] - toJSON (CheckLint fs ghcs c) = cmdJson "check-lint" ["files" .= fs, "ghc-opts" .= ghcs, "clear" .= c] + toJSON (CheckLint fs ghcs lints c) = cmdJson "check-lint" ["files" .= fs, "ghc-opts" .= ghcs, "lint-opts" .= lints, "clear" .= c] toJSON (Types fs ghcs c) = cmdJson "types" ["files" .= fs, "ghc-opts" .= ghcs, "clear" .= c] toJSON (AutoFix ns) = cmdJson "autofixes" ["messages" .= ns] toJSON (Refactor ns rests pure') = cmdJson "refactor" ["messages" .= ns, "rest" .= rests, "pure" .= pure'] @@ -765,9 +773,9 @@ guardCmd "hayoo" v *> (Hayoo <$> v .:: "query" <*> (v .:: "page" <|> pure 0) <*> (v .:: "pages" <|> pure 1)), guardCmd "cabal list" v *> (CabalList <$> v .::?! "packages"), guardCmd "unresolveds" v *> (UnresolvedSymbols <$> v .::?! "files"), - guardCmd "lint" v *> (Lint <$> v .::?! "files"), + guardCmd "lint" v *> (Lint <$> v .::?! "files" <*> v .::?! "lint-opts"), guardCmd "check" v *> (Check <$> v .::?! "files" <*> v .::?! "ghc-opts" <*> (v .:: "clear" <|> pure False)), - guardCmd "check-lint" v *> (CheckLint <$> v .::?! "files" <*> v .::?! "ghc-opts" <*> (v .:: "clear" <|> pure False)), + guardCmd "check-lint" v *> (CheckLint <$> v .::?! "files" <*> v .::?! "ghc-opts" <*> v .::?! "lint-opts" <*> (v .:: "clear" <|> pure False)), guardCmd "types" v *> (Types <$> v .::?! "files" <*> v .::?! "ghc-opts" <*> (v .:: "clear" <|> pure False)), guardCmd "autofixes" v *> (AutoFix <$> v .:: "messages"), guardCmd "refactor" v *> (Refactor <$> v .:: "messages" <*> v .::?! "rest" <*> (v .:: "pure" <|> pure True)),
src/HsDev/Symbols.hs view
@@ -73,7 +73,7 @@ pkgMap = M.unionsWith mergePkgs [M.singleton m' [p] | p <- pkgs, m' <- view packageModules p] mergePkgs ls rs = if null es then hs else es where (es, hs) = partition (view packageExposed) $ uniqueBy (view package) (ls ++ rs) - imps = delete (view (moduleId . moduleName) m) (m ^. moduleImports) + imps = delete (view (moduleId . moduleName) m) (m ^.. moduleImports . each . importName) -- | Options for GHC of module and project moduleOpts :: [PackageConfig] -> Module -> [String]
src/HsDev/Symbols/Documented.hs view
@@ -34,13 +34,13 @@ instance Documented ModuleLocation where brief (FileModule f _) = f - brief (InstalledModule _ pkg n) = format "{} from {}" ~~ n ~~ brief pkg + brief (InstalledModule _ pkg n _) = format "{} from {}" ~~ n ~~ brief pkg brief (OtherLocation src) = src brief NoLocation = "<no-location>" detailed (FileModule f mproj) = case mproj of Nothing -> f Just proj -> format "{} in project {}" ~~ f ~~ brief proj - detailed (InstalledModule pdb pkg n) = format "{} from {} ({})" ~~ n ~~ brief pkg ~~ show pdb + detailed (InstalledModule pdb pkg n _) = format "{} from {} ({})" ~~ n ~~ brief pkg ~~ show pdb detailed l = brief l instance Documented Project where
src/HsDev/Symbols/Location.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE OverloadedStrings, TemplateHaskell #-} module HsDev.Symbols.Location ( - ModulePackage(..), mkPackage, PackageConfig(..), ModuleLocation(..), locationId, noLocation, + ModulePackage(..), mkPackage, PackageConfig(..), + ModuleLocation(..), locationId, noLocation, ModuleId(..), moduleName, moduleLocation, SymbolId(..), symbolName, symbolModule, Position(..), Region(..), region, regionAt, regionLines, regionStr, @@ -9,7 +10,7 @@ packageName, packageVersion, package, packageModules, packageExposed, - moduleFile, moduleProject, moduleInstallDirs, modulePackage, installedModuleName, otherLocationName, + moduleFile, moduleProject, moduleInstallDirs, modulePackage, installedModuleName, installedModuleExposed, otherLocationName, positionLine, positionColumn, regionFrom, regionTo, locationModule, locationPosition, @@ -104,13 +105,13 @@ -- | Location of module data ModuleLocation = FileModule { _moduleFile :: Path, _moduleProject :: Maybe Project } | - InstalledModule { _moduleInstallDirs :: [Path], _modulePackage :: ModulePackage, _installedModuleName :: Text } | + InstalledModule { _moduleInstallDirs :: [Path], _modulePackage :: ModulePackage, _installedModuleName :: Text, _installedModuleExposed :: Bool } | OtherLocation { _otherLocationName :: Text } | NoLocation instance Eq ModuleLocation where FileModule lfile _ == FileModule rfile _ = lfile == rfile - InstalledModule ldirs _ lname == InstalledModule rdirs _ rname = ldirs == rdirs && lname == rname + InstalledModule ldirs _ lname _ == InstalledModule rdirs _ rname _ = ldirs == rdirs && lname == rname OtherLocation l == OtherLocation r = l == r NoLocation == NoLocation = True _ == _ = False @@ -119,11 +120,11 @@ compare l r = compare (locType l, locNames l) (locType r, locNames r) where locType :: ModuleLocation -> Int locType (FileModule _ _) = 0 - locType (InstalledModule _ _ _) = 1 + locType (InstalledModule _ _ _ _) = 1 locType (OtherLocation _) = 2 locType NoLocation = 3 locNames (FileModule f _) = [f] - locNames (InstalledModule dirs _ nm) = nm : dirs + locNames (InstalledModule dirs _ nm _) = nm : dirs locNames (OtherLocation n) = [n] locNames NoLocation = [] @@ -131,13 +132,13 @@ locationId :: ModuleLocation -> Text locationId (FileModule fpath _) = fpath -locationId (InstalledModule dirs mpack nm) = T.intercalate ":" (take 1 dirs ++ [pack (show mpack), nm]) +locationId (InstalledModule dirs mpack nm _) = T.intercalate ":" (take 1 dirs ++ [pack (show mpack), nm]) locationId (OtherLocation src) = src locationId NoLocation = "<no-location>" instance NFData ModuleLocation where rnf (FileModule f p) = rnf f `seq` rnf p - rnf (InstalledModule d p n) = rnf d `seq` rnf p `seq` rnf n + rnf (InstalledModule d p n e) = rnf d `seq` rnf p `seq` rnf n `seq` rnf e rnf (OtherLocation s) = rnf s rnf NoLocation = () @@ -146,7 +147,7 @@ instance Display ModuleLocation where display (FileModule f _) = display f - display (InstalledModule _ _ n) = view unpacked n + display (InstalledModule _ _ n _) = view unpacked n display (OtherLocation s) = view unpacked s display NoLocation = "<no-location>" displayType _ = "module" @@ -156,14 +157,14 @@ instance ToJSON ModuleLocation where toJSON (FileModule f p) = object $ noNulls ["file" .= f, "project" .= fmap (view projectCabal) p] - toJSON (InstalledModule c p n) = object $ noNulls ["dirs" .= c, "package" .= show p, "name" .= n] + toJSON (InstalledModule c p n e) = object $ noNulls ["dirs" .= c, "package" .= show p, "name" .= n, "exposed" .= e] toJSON (OtherLocation s) = object ["source" .= s] toJSON NoLocation = object [] instance FromJSON ModuleLocation where parseJSON = withObject "module location" $ \v -> (FileModule <$> v .:: "file" <*> (fmap project <$> (v .::? "project"))) <|> - (InstalledModule <$> v .::?! "dirs" <*> (readPackage =<< (v .:: "package")) <*> v .:: "name") <|> + (InstalledModule <$> v .::?! "dirs" <*> (readPackage =<< (v .:: "package")) <*> v .:: "name" <*> v .:: "exposed") <|> (OtherLocation <$> v .:: "source") <|> (pure NoLocation) where @@ -171,7 +172,7 @@ instance Paths ModuleLocation where paths f (FileModule fpath p) = FileModule <$> paths f fpath <*> traverse (paths f) p - paths f (InstalledModule c p n) = InstalledModule <$> traverse (paths f) c <*> pure p <*> pure n + paths f (InstalledModule c p n e) = InstalledModule <$> traverse (paths f) c <*> pure p <*> pure n <*> pure e paths _ (OtherLocation s) = pure $ OtherLocation s paths _ NoLocation = pure NoLocation
src/HsDev/Symbols/Parsed.hs view
@@ -93,7 +93,7 @@ file = annL . fileL -- | Get source location -pos :: Annotated ast => Lens' (ast Ann) Position +pos :: (Annotated ast, SrcInfo isrc, Data isrc) => Lens' (ast isrc) Position pos = annL . positionL -- | Definition position, if binder - returns current position
src/HsDev/Symbols/Types.hs view
@@ -2,11 +2,13 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module HsDev.Symbols.Types ( + Import(..), importPosition, importName, importQualified, importAs, Module(..), moduleSymbols, exportedSymbols, scopeSymbols, fixitiesMap, moduleFixities, moduleId, moduleDocs, moduleImports, moduleExports, moduleScope, moduleSource, Symbol(..), symbolId, symbolDocs, symbolPosition, symbolInfo, SymbolInfo(..), functionType, parentClass, parentType, selectorConstructors, typeArgs, typeContext, familyAssociate, symbolInfoType, symbolType, patternType, patternConstructor, Scoped(..), scopeQualifier, scoped, SymbolUsage(..), symbolUsed, symbolUsedQualifier, symbolUsedIn, symbolUsedRegion, + ImportedSymbol(..), importedSymbol, importedFrom, infoOf, nullifyInfo, Inspection(..), inspectionAt, inspectionOpts, fresh, Inspected(..), inspection, inspectedKey, inspectionTags, inspectionResult, inspected, InspectM(..), runInspect, continueInspect, inspect, inspect_, withInspection, @@ -34,6 +36,7 @@ import Data.Aeson.Types (Pair, Parser) import Data.List (intercalate) import Data.Maybe (catMaybes) +import Data.Maybe.JustIf import Data.Monoid (Any(..)) import Data.Function import Data.Ord @@ -88,11 +91,43 @@ rnf (UnQual l n) = rnf l `seq` rnf n rnf (Special l s) = rnf l `seq` rnf s +-- | Import +data Import = Import { + _importPosition :: Position, -- source line of import + _importName :: Text, -- imported module name + _importQualified :: Bool, -- is import qualified + _importAs :: Maybe Text } -- alias of import + deriving (Eq, Ord) + +instance NFData Import where + rnf (Import p n q a) = rnf p `seq` rnf n `seq` rnf q `seq` rnf a + +instance Show Import where + show (Import _ n q a) = concat $ catMaybes [ + Just "import", + "qualified" `justIf` q, + Just $ show n, + fmap (("as " ++) . show) a] + +instance ToJSON Import where + toJSON (Import p n q a) = object [ + "pos" .= p, + "name" .= n, + "qualified" .= q, + "as" .= a] + +instance FromJSON Import where + parseJSON = withObject "import" $ \v -> Import <$> + v .:: "pos" <*> + v .:: "name" <*> + v .:: "qualified" <*> + v .:: "as" + -- | Module data Module = Module { _moduleId :: ModuleId, _moduleDocs :: Maybe Text, - _moduleImports :: [Text], -- list of module names imported + _moduleImports :: [Import], -- list of module names imported _moduleExports :: [Symbol], -- exported module symbols _moduleFixities :: [Fixity], -- fixities of operators _moduleScope :: Map Name [Symbol], -- symbols in scope, only for source modules @@ -148,6 +183,7 @@ toJSON m = object $ noNulls [ "id" .= _moduleId m, "docs" .= _moduleDocs m, + "imports" .= _moduleImports m, "exports" .= _moduleExports m, "fixities" .= _moduleFixities m] @@ -155,7 +191,7 @@ parseJSON = withObject "module" $ \v -> Module <$> v .:: "id" <*> v .::? "docs" <*> - pure mempty <*> + v .::?! "imports" <*> v .::?! "exports" <*> v .::?! "fixities" <*> pure mempty <*> @@ -343,6 +379,24 @@ v .:: "in" <*> v .:: "at" +-- | Symbol with module it's exported from +data ImportedSymbol = ImportedSymbol { + _importedSymbol :: Symbol, + _importedFrom :: ModuleId } + deriving (Eq, Ord) + +instance Show ImportedSymbol where + show (ImportedSymbol s m) = show s ++ " imported from " ++ show m + +instance ToJSON ImportedSymbol where + toJSON (ImportedSymbol s m) = objectUnion (toJSON s) $ object [ + "imported" .= m] + +instance FromJSON ImportedSymbol where + parseJSON = withObject "imported-symbol" $ \v -> ImportedSymbol <$> + parseJSON (Object v) <*> + v .:: "imported" + -- | Inspection data data Inspection = -- | No inspection @@ -514,7 +568,7 @@ showError :: HsDevError -> String showError e = unlines $ ("\terror: " ++ show e) : case mi of FileModule f p -> ["file: " ++ f ^. path, "project: " ++ maybe "" (view (projectPath . path)) p] - InstalledModule c p n -> ["cabal: " ++ show c, "package: " ++ show p, "name: " ++ T.unpack n] + InstalledModule c p n _ -> ["cabal: " ++ show c, "package: " ++ show p, "name: " ++ T.unpack n] OtherLocation src -> ["other location: " ++ T.unpack src] NoLocation -> ["no location"] @@ -552,11 +606,13 @@ PatConstructor args p -> "\t" `T.append` T.intercalate ", " (catMaybes [Just "pattern constructor", Just $ "args: {}" ~~ T.unwords args, fmap ("pat-type: {}" ~~) p]) PatSelector t p _ -> "\t" `T.append` T.intercalate ", " (catMaybes [Just "pattern selector", fmap ("type: {}" ~~) t, fmap ("pat-type: {}" ~~) p]) +makeLenses ''Import makeLenses ''Module makeLenses ''Symbol makeLenses ''SymbolInfo makeLenses ''Scoped makeLenses ''SymbolUsage +makeLenses ''ImportedSymbol makeLenses ''Inspection makeLenses ''Inspected
src/HsDev/Tools/HDocs.hs view
@@ -71,7 +71,7 @@ haddockSession pdbs opts liftGhc $ case mloc' of (FileModule fpath _) -> hsdevLiftWith (ToolError "hdocs") $ liftM snd $ HDocs.readSourceGhc opts (view path fpath) - (InstalledModule _ _ mname) -> do + (InstalledModule _ _ mname _) -> do df <- GHC.getSessionDynFlags liftIO $ hsdevLiftWith (ToolError "hdocs") $ HDocs.moduleDocsF df (T.unpack mname) _ -> hsdevError $ ToolError "hdocs" $ "Can't get docs for: " ++ show mloc'
src/HsDev/Tools/HLint.hs view
@@ -14,7 +14,7 @@ import qualified Data.Text as T import Data.Ord import Data.String (fromString) -import Language.Haskell.HLint3 (autoSettings, parseModuleEx, applyHints, Idea(..), parseErrorMessage, ParseFlags(..), CppFlags(..)) +import Language.Haskell.HLint3 (argsSettings, parseModuleEx, applyHints, Idea(..), parseErrorMessage, ParseFlags(..), CppFlags(..)) import Language.Haskell.Exts.SrcLoc import qualified Language.Haskell.HLint3 as HL (Severity(..)) @@ -23,11 +23,11 @@ import HsDev.Tools.Base import HsDev.Util (readFileUtf8) -hlint :: FilePath -> Maybe Text -> ExceptT String IO [Note OutputMessage] -hlint file msrc = do +hlint :: [String] -> FilePath -> Maybe Text -> ExceptT String IO [Note OutputMessage] +hlint opts file msrc = do file' <- liftIO $ canonicalize file cts <- maybe (liftIO $ readFileUtf8 file') return msrc - (flags, classify, hint) <- liftIO autoSettings + (flags, classify, hint) <- liftIO $ argsSettings opts p <- liftIO $ parseModuleEx (flags { cppFlags = CppSimple }) file' (Just $ T.unpack cts) m <- either (throwError . parseErrorMessage) return p return $ map (recalcTabs cts 8 . indentIdea cts . fromIdea) $
src/HsDev/Watcher.hs view
@@ -54,7 +54,7 @@ unwatchModule :: Watcher -> ModuleLocation -> IO () unwatchModule w (FileModule f Nothing) = void $ unwatchDir w (takeDirectory $ view path f) unwatchModule _ (FileModule _ (Just _)) = return () -unwatchModule _ (InstalledModule _ _ _) = return () +unwatchModule _ InstalledModule{} = return () unwatchModule _ _ = return () -- | Unwatch package-db
tests/Test.hs view
@@ -33,13 +33,18 @@ exports :: Maybe Value -> S.Set String exports v = mkSet (v ^.. traverseArray . key "exports" . traverseArray . key "id" . key "name" . _Just) +imports :: Maybe Value -> S.Set Import +imports v = mkSet $ do + is <- v ^.. traverseArray . key "imports" . traverseArray + maybeToList $ Import <$> pos (is ^. key "pos") <*> (is ^. key "name") <*> (is ^. key "qualified") <*> (is ^. key "as") + rgn :: Maybe Value -> Maybe Region rgn v = Region <$> pos (v ^. key "from") <*> pos (v ^. key "to") pos :: Maybe Value -> Maybe Position pos v = Position <$> (v ^. key "line") <*> (v ^. key "column") -mkSet :: [String] -> S.Set String +mkSet :: Ord a => [a] -> S.Set a mkSet = S.fromList @@ -73,6 +78,13 @@ checks <- send s ["check", "--file", dir </> "tests/test-package/ModuleThree.hs"] checks `shouldNotSatisfy` null + it "should return module imports" $ do + three <- send s ["module", "ModuleThree", "--exact"] + imports three `shouldBe` mkSet [ + Import (Position 8 1) "Control.Concurrent.Chan" False Nothing, + Import (Position 10 1) "ModuleOne" False Nothing, + Import (Position 11 1) "ModuleTwo" False (Just "T")] + it "should pass extensions when checks" $ do checks <- send s ["check", "--file", dir </> "tests/test-package/ModuleTwo.hs"] (checks ^.. traverseArray . key "level" . _Just) `shouldSatisfy` (("error" :: String) `notElem`) @@ -163,6 +175,19 @@ it "should return type of expression in context of module" $ do tys <- send s ["ghc", "type", "twice twice", "--file", dir </> "tests/test-package/ModuleTwo.hs"] tys ^.. traverseArray . key @String "ok" . _Just `shouldBe` ["(a -> a) -> a -> a"] + + it "shouldn't lose module info if unsaved source is broken" $ do + brokenContents <- fmap unpack $ readFileUtf8 $ dir </> "tests/data/ModuleTwo.broken.hs" + void $ send s ["set-file-contents", "--file", dir </> "tests/test-package/ModuleTwo.hs", "--contents", brokenContents] + -- Call scan to wait until file updated + void $ send s ["scan", "--file", dir </> "tests/test-package/ModuleTwo.hs"] + + two <- send s ["module", "ModuleTwo", "--exact"] + exports two `shouldBe` mkSet ["untypedFoo", "twice", "overloadedStrings", "useUntypedFoo"] + + it "should complete qualified names" $ do + comps <- send s ["complete", "T.o", "--file", dir </> "tests/test-package/ModuleThree.hs"] + comps ^.. traverseArray . key "id" . key @String "name" . _Just `shouldBe` ["overloadedStrings"] _ <- runIO $ send s ["exit"] return ()
+ tests/data/ModuleTwo.broken.hs view
@@ -0,0 +1,29 @@+module ModuleTwo ( + untypedFoo, + twice, + overloadedStrings, + useUntypedFoo + ) where + +import ModuleOne (untypedFoo) + +import Data.String + +broken :: IO () +broken = do + s <- + +-- | Apply function twice +twice :: (a -> a) -> a -> a +twice f = f . f + +data MyString = MyString String + +instance IsString MyString where + fromString = MyString + +overloadedStrings :: MyString +overloadedStrings = "Hello" + +useUntypedFoo :: Int -> Int -> Int +useUntypedFoo x y = untypedFoo x (untypedFoo x x)
tests/data/base.sql view
@@ -1,4 +1,4 @@ insert into package_dbs values ('global-db', 'base', '4.9.0.0'); -insert into modules values (1, null, null, json('[]'), 'base', '4.9.0.0', 'Control.Concurrent.Chan', null, 'Control.Concurrent.Chan', null, json('[]'), json('{}'), null, null, json('[]')); +insert into modules values (1, null, null, json('[]'), 'base', '4.9.0.0', 'Control.Concurrent.Chan', 1, null, 'Control.Concurrent.Chan', null, json('[]'), json('{}'), null, null, json('[]')); insert into symbols values (1, 'newChan', 1, null, null, null, 'function', 'IO (Chan a)', null, null, null, null, null, null, null); insert into exports values (1, 1);
tests/test-package/ModuleThree.hs view
@@ -2,13 +2,13 @@ smth, module ModuleOne, - module ModuleTwo + module T ) where import Control.Concurrent.Chan import ModuleOne (test) -import ModuleTwo hiding (twice) +import ModuleTwo as T hiding (twice) smth :: [Int] -> [Int] smth [] = [1]