packages feed

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 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]