packages feed

hsdev 0.2.5.1 → 0.3.0.0

raw patch · 88 files changed

+11120/−10131 lines, 88 filesdep +direct-sqlitedep +haskell-namesdep +sqlite-simpledep ~basedep ~containersdep ~directoryPVP ok

version bump matches the API change (PVP)

Dependencies added: direct-sqlite, haskell-names, sqlite-simple, stm

Dependency ranges changed: base, containers, directory, haskell-src-exts, hdocs, hformat, text-region

API changes (from Hackage documentation)

- Control.Apply.Util: with :: a -> [a -> a] -> a
- Control.Concurrent.FiniteChan: dupChan :: Chan a -> IO (Chan a)
- Control.Concurrent.FiniteChan: instance GHC.Classes.Eq Control.Concurrent.FiniteChan.State
- Control.Concurrent.FiniteChan: instance GHC.Classes.Ord Control.Concurrent.FiniteChan.State
- Control.Concurrent.FiniteChan: instance GHC.Enum.Bounded Control.Concurrent.FiniteChan.State
- Control.Concurrent.FiniteChan: instance GHC.Enum.Enum Control.Concurrent.FiniteChan.State
- Control.Concurrent.FiniteChan: instance GHC.Read.Read Control.Concurrent.FiniteChan.State
- Control.Concurrent.FiniteChan: instance GHC.Show.Show Control.Concurrent.FiniteChan.State
- Control.Concurrent.FiniteChan: putChan :: Chan a -> a -> IO ()
- Control.Concurrent.FiniteChan: readChan :: Chan a -> IO [a]
- Control.Concurrent.Worker: [workerTouch] :: Worker m -> IO ()
- Control.Concurrent.Worker: [workerWrap] :: Worker m -> forall a. m a -> m a
- Control.Concurrent.Worker: inWorker_ :: (MonadIO m, MonadCatch m) => Worker m -> m a -> ExceptT SomeException IO a
- Control.Concurrent.Worker: pushTask :: (MonadCatch m, MonadIO m) => Worker m -> m a -> IO (Async a)
- Control.Concurrent.Worker: restartWorker :: Worker m -> IO ()
- Data.Async: Action :: (a -> IO a) -> Event a
- Data.Async: Append :: a -> Event a
- Data.Async: Async :: MVar a -> Chan (Event a) -> Async a
- Data.Async: Clear :: Event a
- Data.Async: Modify :: (a -> a) -> Event a
- Data.Async: Remove :: a -> Event a
- Data.Async: [asyncEvents] :: Async a -> Chan (Event a)
- Data.Async: [asyncVar] :: Async a -> MVar a
- Data.Async: class Eq a => Group a
- Data.Async: class NFData a
- Data.Async: data Async a
- Data.Async: data Event a
- Data.Async: event :: Group a => Event a -> a -> IO a
- Data.Async: modifyAsync :: Async a -> Event a -> IO ()
- Data.Async: newAsync :: (NFData a, Group a) => IO (Async a)
- Data.Async: readAsync :: Async a -> IO a
- Data.Deps: data Deps a
- Data.Group: add :: Group a => a -> a -> a
- Data.Group: class Eq a => Group a
- Data.Group: groupSum :: Group a => [a] -> a
- Data.Group: instance (GHC.Classes.Ord k, Data.Group.Group a) => Data.Group.Group (Data.Map.Base.Map k a)
- Data.Group: instance GHC.Classes.Eq a => Data.Group.Group [a]
- Data.Group: instance GHC.Classes.Ord a => Data.Group.Group (Data.Set.Base.Set a)
- Data.Group: sub :: Group a => a -> a -> a
- Data.Group: zero :: Group a => a
- Data.Maybe.JustIf: justWhen :: a -> (a -> Bool) -> Maybe a
- Data.Maybe.JustIf: soJust :: Bool -> a -> Maybe a
- HsDev.Cache: data Database
- HsDev.Cache: dump :: FilePath -> Database -> IO ()
- HsDev.Cache: escapePath :: FilePath -> FilePath
- HsDev.Cache: load :: FilePath -> IO (Either String Database)
- HsDev.Cache: packageDbCache :: PackageDb -> FilePath
- HsDev.Cache: projectCache :: Project -> FilePath
- HsDev.Cache: readVersion :: FilePath -> IO (Maybe [Int])
- HsDev.Cache: standaloneCache :: FilePath
- HsDev.Cache: versionCache :: FilePath
- HsDev.Cache: writeVersion :: FilePath -> IO ()
- HsDev.Cache.Structured: dump :: FilePath -> Structured -> IO ()
- HsDev.Cache.Structured: load :: FilePath -> IO (Either String Structured)
- HsDev.Cache.Structured: loadFiles :: (FilePath -> Bool) -> FilePath -> ExceptT String IO Structured
- HsDev.Cache.Structured: loadPackageDb :: PackageDb -> FilePath -> ExceptT String IO Structured
- HsDev.Cache.Structured: loadProject :: FilePath -> FilePath -> ExceptT String IO Structured
- HsDev.Commands: checkDeclaration :: (Declaration -> Bool) -> (ModuleDeclaration -> Bool)
- HsDev.Commands: checkModule :: (ModuleId -> Bool) -> (ModuleDeclaration -> Bool)
- HsDev.Commands: completions :: Database -> FilePath -> String -> Bool -> ExceptT String IO [Declaration]
- HsDev.Commands: fileCtx :: Database -> FilePath -> ExceptT String IO (FilePath, Module, Maybe Project)
- HsDev.Commands: fileCtxMaybe :: Database -> FilePath -> ExceptT String IO (FilePath, Maybe Module, Maybe Project)
- HsDev.Commands: fileModule :: Database -> FilePath -> ExceptT String IO Module
- HsDev.Commands: findDeclaration :: Database -> String -> ExceptT String IO [ModuleDeclaration]
- HsDev.Commands: findModule :: Database -> String -> ExceptT String IO [Module]
- HsDev.Commands: lookupSymbol :: Database -> FilePath -> String -> ExceptT String IO [ModuleDeclaration]
- HsDev.Commands: moduleCompletions :: Database -> [Module] -> String -> ExceptT String IO [String]
- HsDev.Commands: restrictPackageDb :: PackageDb -> ModuleId -> Bool
- HsDev.Commands: restrictPackageDbStack :: PackageDbStack -> ModuleId -> Bool
- HsDev.Commands: scope :: Database -> FilePath -> Bool -> ExceptT String IO [Declaration]
- HsDev.Commands: scopeModules :: Database -> FilePath -> ExceptT String IO [Module]
- HsDev.Commands: splitIdentifier :: String -> (Maybe String, String)
- HsDev.Commands: visibleFrom :: Maybe Project -> Module -> ModuleId -> Bool
- HsDev.Commands: whois :: Database -> FilePath -> String -> ExceptT String IO [Declaration]
- HsDev.Database: Database :: [InspectedModule] -> [Project] -> Database
- HsDev.Database: Structured :: Map PackageDb Database -> Map FilePath Database -> Database -> Structured
- HsDev.Database: [databaseModules] :: Database -> [InspectedModule]
- HsDev.Database: [databaseProjects] :: Database -> [Project]
- HsDev.Database: [structuredFiles] :: Structured -> Database
- HsDev.Database: [structuredPackageDbs] :: Structured -> Map PackageDb Database
- HsDev.Database: [structuredProjects] :: Structured -> Map FilePath Database
- HsDev.Database: allDeclarations :: Database -> [ModuleDeclaration]
- HsDev.Database: allModules :: Database -> [Module]
- HsDev.Database: allPackages :: Database -> [ModulePackage]
- HsDev.Database: append :: Database -> Database -> Database
- HsDev.Database: data Database
- HsDev.Database: data Map k a :: * -> * -> *
- HsDev.Database: data Structured
- HsDev.Database: databaseIntersection :: Database -> Database -> Database
- HsDev.Database: databaseLocals :: Database -> Database
- HsDev.Database: databasePackageDbs :: Database -> [PackageDb]
- HsDev.Database: filterDB :: (ModuleId -> Bool) -> (Project -> Bool) -> Database -> Database
- HsDev.Database: fromModule :: InspectedModule -> Database
- HsDev.Database: fromProject :: Project -> Database
- HsDev.Database: getInspected :: Database -> Module -> InspectedModule
- HsDev.Database: instance Control.DeepSeq.NFData HsDev.Database.Database
- HsDev.Database: instance Control.DeepSeq.NFData HsDev.Database.Structured
- HsDev.Database: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Database.Database
- HsDev.Database: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Database.Structured
- HsDev.Database: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Database.Database
- HsDev.Database: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Database.Structured
- HsDev.Database: instance Data.Group.Group HsDev.Database.Database
- HsDev.Database: instance Data.Group.Group HsDev.Database.Structured
- HsDev.Database: instance GHC.Base.Monoid HsDev.Database.Database
- HsDev.Database: instance GHC.Base.Monoid HsDev.Database.Structured
- HsDev.Database: instance GHC.Classes.Eq HsDev.Database.Database
- HsDev.Database: instance GHC.Classes.Eq HsDev.Database.Structured
- HsDev.Database: instance GHC.Classes.Ord HsDev.Database.Database
- HsDev.Database: instance GHC.Classes.Ord HsDev.Database.Structured
- HsDev.Database: lookupFile :: FilePath -> Database -> Maybe Module
- HsDev.Database: lookupInspected :: ModuleLocation -> Database -> Maybe InspectedModule
- HsDev.Database: lookupModule :: ModuleLocation -> Database -> Maybe Module
- HsDev.Database: merge :: Structured -> Database
- HsDev.Database: nullDatabase :: Database -> Bool
- HsDev.Database: packageDbDB :: PackageDb -> Database -> Database
- HsDev.Database: packageDbStackDB :: PackageDbStack -> Database -> Database
- HsDev.Database: projectDB :: Project -> Database -> Database
- HsDev.Database: refineProject :: Database -> Project -> Maybe Project
- HsDev.Database: remove :: Database -> Database -> Database
- HsDev.Database: selectDeclarations :: (ModuleDeclaration -> Bool) -> Database -> [ModuleDeclaration]
- HsDev.Database: selectModules :: (Module -> Bool) -> Database -> [Module]
- HsDev.Database: standaloneDB :: Database -> Database
- HsDev.Database: structured :: [Database] -> [Database] -> Database -> Either String Structured
- HsDev.Database: structurize :: Database -> Structured
- HsDev.Database.Async: clear :: MonadIO m => Async Database -> m Database -> m ()
- HsDev.Database.Async: update :: MonadIO m => Async Database -> m Database -> m ()
- HsDev.Database.Async: wait :: MonadIO m => Async Database -> m ()
- HsDev.Database.Update: getCache :: UpdateMonad m => (FilePath -> ExceptT String IO Structured) -> (Database -> Database) -> m Database
- HsDev.Database.Update: isStatus :: Value -> Bool
- HsDev.Database.Update: loadCache :: UpdateMonad m => (FilePath -> ExceptT String IO Structured) -> m Database
- HsDev.Database.Update: processEvent :: UpdateOptions -> Watched -> Event -> ClientM IO ()
- HsDev.Database.Update: readDB :: SessionMonad m => m Database
- HsDev.Database.Update: scanModule :: UpdateMonad m => [String] -> ModuleLocation -> Maybe String -> m ()
- HsDev.Database.Update: updateEvent :: ServerMonadBase m => Watched -> Event -> UpdateM m ()
- HsDev.Database.Update: waiter :: UpdateMonad m => m () -> m ()
- HsDev.Database.Update.Types: instance GHC.Base.Monad m => Control.Monad.Reader.Class.MonadReader HsDev.Database.Update.Types.UpdateOptions (HsDev.Database.Update.Types.UpdateM m)
- HsDev.Display: instance HsDev.Display.Display HsDev.PackageDb.PackageDb
- HsDev.Display: instance Text.Format.Formattable HsDev.PackageDb.PackageDb
- HsDev.Error: hsdevExcept :: MonadCatch m => m a -> ExceptT HsDevError m a
- HsDev.Error: hsdevLog :: MonadLog m => Level -> m a -> m a
- HsDev.Inspect: analyzeModule :: [String] -> Maybe FilePath -> String -> Either String Module
- HsDev.Inspect: inspectDocsChunk :: [String] -> [Module] -> IO [Module]
- HsDev.Inspect: inspectProject :: [(String, String)] -> [String] -> Project -> IO (Project, [InspectedModule])
- HsDev.PackageDb: GlobalDb :: PackageDb
- HsDev.PackageDb: PackageDb :: FilePath -> PackageDb
- HsDev.PackageDb: PackageDbStack :: [PackageDb] -> PackageDbStack
- HsDev.PackageDb: UserDb :: PackageDb
- HsDev.PackageDb: [_packageDbStack] :: PackageDbStack -> [PackageDb]
- HsDev.PackageDb: [_packageDb] :: PackageDb -> FilePath
- HsDev.PackageDb: data PackageDb
- HsDev.PackageDb: fromPackageDb :: FilePath -> PackageDbStack
- HsDev.PackageDb: fromPackageDbs :: [FilePath] -> PackageDbStack
- HsDev.PackageDb: globalDb :: PackageDbStack
- HsDev.PackageDb: instance Control.DeepSeq.NFData HsDev.PackageDb.PackageDb
- HsDev.PackageDb: instance Control.DeepSeq.NFData HsDev.PackageDb.PackageDbStack
- HsDev.PackageDb: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.PackageDb.PackageDb
- HsDev.PackageDb: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.PackageDb.PackageDbStack
- HsDev.PackageDb: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.PackageDb.PackageDb
- HsDev.PackageDb: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.PackageDb.PackageDbStack
- HsDev.PackageDb: instance GHC.Classes.Eq HsDev.PackageDb.PackageDb
- HsDev.PackageDb: instance GHC.Classes.Eq HsDev.PackageDb.PackageDbStack
- HsDev.PackageDb: instance GHC.Classes.Ord HsDev.PackageDb.PackageDb
- HsDev.PackageDb: instance GHC.Classes.Ord HsDev.PackageDb.PackageDbStack
- HsDev.PackageDb: instance GHC.Read.Read HsDev.PackageDb.PackageDb
- HsDev.PackageDb: instance GHC.Read.Read HsDev.PackageDb.PackageDbStack
- HsDev.PackageDb: instance GHC.Show.Show HsDev.PackageDb.PackageDb
- HsDev.PackageDb: instance GHC.Show.Show HsDev.PackageDb.PackageDbStack
- HsDev.PackageDb: instance System.Directory.Paths.Paths HsDev.PackageDb.PackageDb
- HsDev.PackageDb: instance System.Directory.Paths.Paths HsDev.PackageDb.PackageDbStack
- HsDev.PackageDb: isSubStack :: PackageDbStack -> PackageDbStack -> Bool
- HsDev.PackageDb: newtype PackageDbStack
- HsDev.PackageDb: packageDb :: Traversal' PackageDb FilePath
- HsDev.PackageDb: packageDbOpt :: PackageDb -> String
- HsDev.PackageDb: packageDbStack :: Iso' PackageDbStack [PackageDb]
- HsDev.PackageDb: packageDbStackOpts :: PackageDbStack -> [String]
- HsDev.PackageDb: packageDbStacks :: PackageDbStack -> [PackageDbStack]
- HsDev.PackageDb: packageDbs :: PackageDbStack -> [PackageDb]
- HsDev.PackageDb: topPackageDb :: PackageDbStack -> PackageDb
- HsDev.PackageDb: userDb :: PackageDbStack
- HsDev.Project: inTarget :: FilePath -> Info -> Bool
- HsDev.Project: infos :: ProjectDescription -> [Info]
- HsDev.Project.Types: absolutiseProjectPaths :: Project -> Project
- HsDev.Project.Types: relativiseProjectPaths :: Project -> Project
- HsDev.Scan: changedModule :: Database -> [String] -> ModuleLocation -> IO Bool
- HsDev.Scan: instance HsDev.Scan.EnumContents HsDev.PackageDb.PackageDbStack
- HsDev.Scan: rescanModule :: CommandMonad m => [(String, String)] -> [String] -> InspectedModule -> m (Maybe InspectedModule)
- HsDev.Scan: scanModule :: CommandMonad m => [(String, String)] -> [String] -> ModuleLocation -> Maybe String -> m InspectedModule
- HsDev.Scan.Browse: browse :: MonadLog m => [String] -> PackageDbStack -> m [InspectedModule]
- HsDev.Scan.Browse: browseDb :: MonadLog m => [String] -> PackageDbStack -> m [InspectedModule]
- HsDev.Scan.Browse: ghcPackageDb :: PackageConfig -> IO PackageDb
- HsDev.Scan.Browse: packageDbCandidate :: FilePath -> IO (Maybe PackageDb)
- HsDev.Scan.Browse: packageDbCandidate_ :: FilePath -> IO PackageDb
- HsDev.Scan.Browse: withPackages :: MonadLog m => [String] -> (DynFlags -> GhcM a) -> m a
- HsDev.Scan.Browse: withPackages_ :: MonadLog m => [String] -> GhcM a -> m a
- HsDev.Server.Base: readCache :: SessionMonad m => ServerOpts -> (FilePath -> ExceptT String IO Structured) -> m (Maybe Database)
- HsDev.Server.Base: withCache :: Monad m => ServerOpts -> a -> (FilePath -> m a) -> m a
- HsDev.Server.Base: writeCache :: SessionMonad m => ServerOpts -> Database -> m ()
- HsDev.Server.Commands: [serverCache] :: ServerOpts -> Maybe FilePath
- HsDev.Server.Commands: [serverLoad] :: ServerOpts -> Bool
- HsDev.Server.Message: notification :: ToJSON a => a -> Response
- HsDev.Server.Message: responsesById :: Maybe String -> [Message Response] -> [([Notification], Result)]
- HsDev.Server.Message: resultPart :: ToJSON a => a -> Notification
- HsDev.Server.Types: AddData :: [AddedContents] -> Command
- HsDev.Server.Types: AddedDatabase :: Database -> AddedContents
- HsDev.Server.Types: AddedModule :: InspectedModule -> AddedContents
- HsDev.Server.Types: AddedProject :: Project -> AddedContents
- HsDev.Server.Types: AutoFixFix :: [Note Correction] -> [Note Correction] -> Bool -> AutoFixCommand
- HsDev.Server.Types: AutoFixShow :: [Note OutputMessage] -> AutoFixCommand
- HsDev.Server.Types: InfoModules :: [TargetFilter] -> Command
- HsDev.Server.Types: InfoResolve :: FilePath -> Bool -> Command
- HsDev.Server.Types: SearchRegex :: SearchType
- HsDev.Server.Types: TargetCabal :: TargetFilter
- HsDev.Server.Types: TargetDepsOf :: String -> TargetFilter
- HsDev.Server.Types: TargetPackageDb :: PackageDb -> TargetFilter
- HsDev.Server.Types: TargetSandbox :: FilePath -> TargetFilter
- HsDev.Server.Types: [addedContents] :: Command -> [AddedContents]
- HsDev.Server.Types: [autoFixCommand] :: Command -> AutoFixCommand
- HsDev.Server.Types: [docsModules] :: Command -> [String]
- HsDev.Server.Types: [inferModules] :: Command -> [String]
- HsDev.Server.Types: [serverCache] :: ServerOpts -> Maybe FilePath
- HsDev.Server.Types: [serverLoad] :: ServerOpts -> Bool
- HsDev.Server.Types: [sessionDatabase] :: Session -> Async Database
- HsDev.Server.Types: [sessionReadCache] :: Session -> (FilePath -> ExceptT String IO Structured) -> ServerM IO (Maybe Database)
- HsDev.Server.Types: [sessionWriteCache] :: Session -> Database -> ServerM IO ()
- HsDev.Server.Types: data AddedContents
- HsDev.Server.Types: data AutoFixCommand
- HsDev.Server.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Server.Types.AddedContents
- HsDev.Server.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Server.Types.AutoFixCommand
- HsDev.Server.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Server.Types.AddedContents
- HsDev.Server.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Server.Types.AutoFixCommand
- HsDev.Server.Types: instance GHC.Show.Show HsDev.Server.Types.AddedContents
- HsDev.Server.Types: instance GHC.Show.Show HsDev.Server.Types.AutoFixCommand
- HsDev.Server.Types: instance HsDev.Util.FromCmd HsDev.Server.Types.AutoFixCommand
- HsDev.Server.Types: instance System.Directory.Paths.Paths [HsDev.Server.Types.TargetFilter]
- HsDev.Server.Types: serverReadCache :: SessionMonad m => (FilePath -> ExceptT String IO Structured) -> m (Maybe Database)
- HsDev.Server.Types: serverUpdateDB :: SessionMonad m => Database -> m ()
- HsDev.Server.Types: serverWriteCache :: SessionMonad m => Database -> m ()
- HsDev.Symbols: addDeclaration :: Declaration -> Module -> Module
- HsDev.Symbols: class Locals a
- HsDev.Symbols: class Symbol a
- HsDev.Symbols: clearDefinedIn :: Module -> Module
- HsDev.Symbols: decl :: Text -> DeclarationInfo -> Declaration
- HsDev.Symbols: declarationLocals :: Declaration -> [Declaration]
- HsDev.Symbols: definedIn :: Declaration -> ModuleId -> Declaration
- HsDev.Symbols: dropExternals :: Module -> Module
- HsDev.Symbols: export :: Export -> Text
- HsDev.Symbols: exported :: Declaration -> Export -> Bool
- HsDev.Symbols: importNames :: Import -> [Text]
- HsDev.Symbols: importQualifier :: Maybe Text -> Import -> Bool
- HsDev.Symbols: import_ :: Text -> Import
- HsDev.Symbols: imported :: Declaration -> ImportList -> Bool
- HsDev.Symbols: instance HsDev.Symbols.Locals HsDev.Symbols.Types.Declaration
- HsDev.Symbols: instance HsDev.Symbols.Locals HsDev.Symbols.Types.DeclarationInfo
- HsDev.Symbols: locals :: Locals a => a -> [Declaration]
- HsDev.Symbols: mergeExported :: [ModuleDeclaration] -> [ExportedDeclaration]
- HsDev.Symbols: moduleLocalDeclarations :: Module -> [Declaration]
- HsDev.Symbols: moduleLocals :: Module -> Module
- HsDev.Symbols: moduleModuleDeclarations :: Module -> [ModuleDeclaration]
- HsDev.Symbols: passImportSpec :: Declaration -> ImportSpec -> Bool
- HsDev.Symbols: passThingSpec :: Text -> ThingPart -> Bool
- HsDev.Symbols: scopes :: Declaration -> [Maybe Text]
- HsDev.Symbols: setDefinedIn :: Module -> Module
- HsDev.Symbols: sortDeclarations :: [Declaration] -> [Declaration]
- HsDev.Symbols: symbolDocs :: Symbol a => a -> Maybe Text
- HsDev.Symbols: symbolLocation :: Symbol a => a -> Location
- HsDev.Symbols: symbolName :: Symbol a => a -> Text
- HsDev.Symbols: symbolQualifiedName :: Symbol a => a -> Text
- HsDev.Symbols: unalias :: Module -> Text -> [Text]
- HsDev.Symbols: unnamedModuleId :: ModuleLocation -> ModuleId
- HsDev.Symbols: where_ :: Locals a => a -> [Declaration] -> a
- HsDev.Symbols.Class: class Symbol a
- HsDev.Symbols.Class: symbolDocs :: Symbol a => a -> Maybe Text
- HsDev.Symbols.Class: symbolLocation :: Symbol a => a -> Location
- HsDev.Symbols.Class: symbolModuleLocation :: Symbol a => a -> ModuleLocation
- HsDev.Symbols.Class: symbolName :: Symbol a => a -> Text
- HsDev.Symbols.Class: symbolQualifiedName :: Symbol a => a -> Text
- HsDev.Symbols.Location: ModuleSource :: Maybe String -> ModuleLocation
- HsDev.Symbols.Location: [_cabalModuleName] :: ModuleLocation -> String
- HsDev.Symbols.Location: [_modulePackageDb] :: ModuleLocation -> PackageDb
- HsDev.Symbols.Location: [_moduleSourceName] :: ModuleLocation -> Maybe String
- HsDev.Symbols.Location: cabalModuleName :: Traversal' ModuleLocation String
- HsDev.Symbols.Location: importedModulePath :: Text -> FilePath -> Text -> FilePath
- HsDev.Symbols.Location: modulePackageDb :: Traversal' ModuleLocation PackageDb
- HsDev.Symbols.Location: moduleSourceName :: Traversal' ModuleLocation (Maybe String)
- HsDev.Symbols.Location: moduleStandalone :: ModuleLocation -> Bool
- HsDev.Symbols.Location: packageOpt :: Maybe ModulePackage -> [String]
- HsDev.Symbols.Resolve: ResolveM :: ReaderT (Database, ModuleMap) (State ResolvedTree) a -> ResolveM a
- HsDev.Symbols.Resolve: ResolvedModule :: Module -> [Declaration] -> [Declaration] -> ResolvedModule
- HsDev.Symbols.Resolve: [_resolvedExports] :: ResolvedModule -> [Declaration]
- HsDev.Symbols.Resolve: [_resolvedModule] :: ResolvedModule -> Module
- HsDev.Symbols.Resolve: [_resolvedScope] :: ResolvedModule -> [Declaration]
- HsDev.Symbols.Resolve: [runResolveM] :: ResolveM a -> ReaderT (Database, ModuleMap) (State ResolvedTree) a
- HsDev.Symbols.Resolve: data ResolvedModule
- HsDev.Symbols.Resolve: exportsModule :: ResolvedModule -> Module
- HsDev.Symbols.Resolve: instance Control.Monad.Reader.Class.MonadReader (HsDev.Database.Database, HsDev.Symbols.Resolve.ModuleMap) HsDev.Symbols.Resolve.ResolveM
- HsDev.Symbols.Resolve: instance Control.Monad.State.Class.MonadState HsDev.Symbols.Resolve.ResolvedTree HsDev.Symbols.Resolve.ResolveM
- HsDev.Symbols.Resolve: instance GHC.Base.Applicative HsDev.Symbols.Resolve.ResolveM
- HsDev.Symbols.Resolve: instance GHC.Base.Functor HsDev.Symbols.Resolve.ResolveM
- HsDev.Symbols.Resolve: instance GHC.Base.Monad HsDev.Symbols.Resolve.ResolveM
- HsDev.Symbols.Resolve: mergeImported :: [Declaration] -> [Declaration]
- HsDev.Symbols.Resolve: newtype ResolveM a
- HsDev.Symbols.Resolve: resolve :: Traversable t => Database -> t Module -> t ResolvedModule
- HsDev.Symbols.Resolve: resolveImport :: Set String -> Module -> Import -> ResolveM [Declaration]
- HsDev.Symbols.Resolve: resolveImports :: Module -> [Import] -> ResolveM [Declaration]
- HsDev.Symbols.Resolve: resolveModule :: Module -> ResolveM ResolvedModule
- HsDev.Symbols.Resolve: resolveOne :: Database -> Module -> ResolvedModule
- HsDev.Symbols.Resolve: resolvedExports :: Lens' ResolvedModule [Declaration]
- HsDev.Symbols.Resolve: resolvedModule :: Lens' ResolvedModule Module
- HsDev.Symbols.Resolve: resolvedScope :: Lens' ResolvedModule [Declaration]
- HsDev.Symbols.Resolve: resolvedTopScope :: ResolvedModule -> [Declaration]
- HsDev.Symbols.Resolve: scopeModule :: ResolvedModule -> Module
- HsDev.Symbols.Resolve: type ResolvedTree = Map ModuleId ResolvedModule
- HsDev.Symbols.Types: Declaration :: Text -> Maybe ModuleId -> Maybe [Import] -> Maybe Text -> Maybe Position -> DeclarationInfo -> Declaration
- HsDev.Symbols.Types: ExportModule :: Text -> Export
- HsDev.Symbols.Types: ExportName :: Maybe Text -> Text -> ThingPart -> Export
- HsDev.Symbols.Types: ExportedDeclaration :: [ModuleId] -> Declaration -> ExportedDeclaration
- HsDev.Symbols.Types: Import :: Text -> Bool -> Maybe Text -> Maybe ImportList -> Maybe Position -> Import
- HsDev.Symbols.Types: ImportList :: Bool -> [ImportSpec] -> ImportList
- HsDev.Symbols.Types: ImportSpec :: Text -> ThingPart -> ImportSpec
- HsDev.Symbols.Types: ModuleDeclaration :: ModuleId -> Declaration -> ModuleDeclaration
- HsDev.Symbols.Types: ModuleId :: Text -> ModuleLocation -> ModuleId
- HsDev.Symbols.Types: ThingAll :: ThingPart
- HsDev.Symbols.Types: ThingNothing :: ThingPart
- HsDev.Symbols.Types: ThingWith :: [Text] -> ThingPart
- HsDev.Symbols.Types: TypeInfo :: Maybe Text -> [Text] -> Maybe Text -> [Text] -> TypeInfo
- HsDev.Symbols.Types: [_declarationDefined] :: Declaration -> Maybe ModuleId
- HsDev.Symbols.Types: [_declarationDocs] :: Declaration -> Maybe Text
- HsDev.Symbols.Types: [_declarationImported] :: Declaration -> Maybe [Import]
- HsDev.Symbols.Types: [_declarationModuleId] :: ModuleDeclaration -> ModuleId
- HsDev.Symbols.Types: [_declarationName] :: Declaration -> Text
- HsDev.Symbols.Types: [_declarationPosition] :: Declaration -> Maybe Position
- HsDev.Symbols.Types: [_declaration] :: Declaration -> DeclarationInfo
- HsDev.Symbols.Types: [_exportModule] :: Export -> Text
- HsDev.Symbols.Types: [_exportName] :: Export -> Text
- HsDev.Symbols.Types: [_exportPart] :: Export -> ThingPart
- HsDev.Symbols.Types: [_exportQualified] :: Export -> Maybe Text
- HsDev.Symbols.Types: [_exportedBy] :: ExportedDeclaration -> [ModuleId]
- HsDev.Symbols.Types: [_exportedDeclaration] :: ExportedDeclaration -> Declaration
- HsDev.Symbols.Types: [_hidingList] :: ImportList -> Bool
- HsDev.Symbols.Types: [_importAs] :: Import -> Maybe Text
- HsDev.Symbols.Types: [_importIsQualified] :: Import -> Bool
- HsDev.Symbols.Types: [_importList] :: Import -> Maybe ImportList
- HsDev.Symbols.Types: [_importModuleName] :: Import -> Text
- HsDev.Symbols.Types: [_importPosition] :: Import -> Maybe Position
- HsDev.Symbols.Types: [_importSpecName] :: ImportSpec -> Text
- HsDev.Symbols.Types: [_importSpecPart] :: ImportSpec -> ThingPart
- HsDev.Symbols.Types: [_importSpecs] :: ImportList -> [ImportSpec]
- HsDev.Symbols.Types: [_inspectedId] :: Inspected i t a -> i
- HsDev.Symbols.Types: [_localDeclarations] :: DeclarationInfo -> [Declaration]
- HsDev.Symbols.Types: [_moduleDeclaration] :: ModuleDeclaration -> Declaration
- HsDev.Symbols.Types: [_moduleDeclarations] :: Module -> [Declaration]
- HsDev.Symbols.Types: [_moduleIdLocation] :: ModuleId -> ModuleLocation
- HsDev.Symbols.Types: [_moduleIdName] :: ModuleId -> Text
- HsDev.Symbols.Types: [_moduleLocation] :: Module -> ModuleLocation
- HsDev.Symbols.Types: [_moduleName] :: Module -> Text
- HsDev.Symbols.Types: [_related] :: DeclarationInfo -> Maybe Text
- HsDev.Symbols.Types: [_typeInfoArgs] :: TypeInfo -> [Text]
- HsDev.Symbols.Types: [_typeInfoContext] :: TypeInfo -> Maybe Text
- HsDev.Symbols.Types: [_typeInfoDefinition] :: TypeInfo -> Maybe Text
- HsDev.Symbols.Types: [_typeInfoFunctions] :: TypeInfo -> [Text]
- HsDev.Symbols.Types: [_typeInfo] :: DeclarationInfo -> TypeInfo
- HsDev.Symbols.Types: data Declaration
- HsDev.Symbols.Types: data DeclarationInfo
- HsDev.Symbols.Types: data Export
- HsDev.Symbols.Types: data ExportedDeclaration
- HsDev.Symbols.Types: data Import
- HsDev.Symbols.Types: data ImportList
- HsDev.Symbols.Types: data ImportSpec
- HsDev.Symbols.Types: data ModuleDeclaration
- HsDev.Symbols.Types: data ModuleId
- HsDev.Symbols.Types: data ThingPart
- HsDev.Symbols.Types: data TypeInfo
- HsDev.Symbols.Types: declaration :: Lens' Declaration DeclarationInfo
- HsDev.Symbols.Types: declarationDefined :: Lens' Declaration (Maybe ModuleId)
- HsDev.Symbols.Types: declarationDocs :: Lens' Declaration (Maybe Text)
- HsDev.Symbols.Types: declarationImported :: Lens' Declaration (Maybe [Import])
- HsDev.Symbols.Types: declarationInfo :: DeclarationInfo -> Either (Maybe Text, [Declaration], Maybe Text) TypeInfo
- HsDev.Symbols.Types: declarationModuleId :: Lens' ModuleDeclaration ModuleId
- HsDev.Symbols.Types: declarationName :: Lens' Declaration Text
- HsDev.Symbols.Types: declarationPosition :: Lens' Declaration (Maybe Position)
- HsDev.Symbols.Types: declarationTypeCtor :: String -> TypeInfo -> DeclarationInfo
- HsDev.Symbols.Types: declarationTypeName :: DeclarationInfo -> Maybe String
- HsDev.Symbols.Types: exportModule :: Traversal' Export Text
- HsDev.Symbols.Types: exportName :: Traversal' Export Text
- HsDev.Symbols.Types: exportPart :: Traversal' Export ThingPart
- HsDev.Symbols.Types: exportQualified :: Traversal' Export (Maybe Text)
- HsDev.Symbols.Types: exportedBy :: Lens' ExportedDeclaration [ModuleId]
- HsDev.Symbols.Types: exportedDeclaration :: Lens' ExportedDeclaration Declaration
- HsDev.Symbols.Types: hidingList :: Lens' ImportList Bool
- HsDev.Symbols.Types: importAs :: Lens' Import (Maybe Text)
- HsDev.Symbols.Types: importIsQualified :: Lens' Import Bool
- HsDev.Symbols.Types: importList :: Lens' Import (Maybe ImportList)
- HsDev.Symbols.Types: importModuleName :: Lens' Import Text
- HsDev.Symbols.Types: importPosition :: Lens' Import (Maybe Position)
- HsDev.Symbols.Types: importSpecName :: Lens' ImportSpec Text
- HsDev.Symbols.Types: importSpecPart :: Lens' ImportSpec ThingPart
- HsDev.Symbols.Types: importSpecs :: Lens' ImportList [ImportSpec]
- HsDev.Symbols.Types: inspectedId :: forall i_a1K3e t_a1K3f a_a1K3g i_a1Mdf. Lens (Inspected i_a1K3e t_a1K3f a_a1K3g) (Inspected i_a1Mdf t_a1K3f a_a1K3g) i_a1K3e i_a1Mdf
- HsDev.Symbols.Types: instance (Control.DeepSeq.NFData i, Control.DeepSeq.NFData t, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (HsDev.Symbols.Types.Inspected i t a)
- HsDev.Symbols.Types: instance (GHC.Classes.Eq i, GHC.Classes.Eq t, GHC.Classes.Eq a) => GHC.Classes.Eq (HsDev.Symbols.Types.Inspected i t a)
- HsDev.Symbols.Types: instance (GHC.Classes.Ord i, GHC.Classes.Ord t, GHC.Classes.Ord a) => GHC.Classes.Ord (HsDev.Symbols.Types.Inspected i t a)
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.Declaration
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.DeclarationInfo
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.Export
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.ExportedDeclaration
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.Import
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.ImportList
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.ImportSpec
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.ModuleDeclaration
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.ModuleId
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.ThingPart
- HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.TypeInfo
- HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.Declaration
- HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.DeclarationInfo
- HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.Export
- HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.ExportedDeclaration
- 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.ImportList
- HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.ImportSpec
- HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.InspectedModule
- HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.ModuleDeclaration
- HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.ModuleId
- HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.ThingPart
- HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.TypeInfo
- HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.Declaration
- HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.DeclarationInfo
- HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.Export
- HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.ExportedDeclaration
- 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.ImportList
- HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.ImportSpec
- HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.InspectedModule
- HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.ModuleDeclaration
- HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.ModuleId
- HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.ThingPart
- HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.TypeInfo
- HsDev.Symbols.Types: instance Data.Foldable.Foldable (HsDev.Symbols.Types.Inspected i t)
- HsDev.Symbols.Types: instance Data.Traversable.Traversable (HsDev.Symbols.Types.Inspected i t)
- HsDev.Symbols.Types: instance GHC.Base.Functor (HsDev.Symbols.Types.Inspected i t)
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.Declaration
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.DeclarationInfo
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.Export
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.ExportedDeclaration
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.Import
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.ImportList
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.ImportSpec
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.ModuleDeclaration
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.ModuleId
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.ThingPart
- HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.TypeInfo
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.Declaration
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.DeclarationInfo
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.Export
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.ExportedDeclaration
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.Import
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.ImportList
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.ImportSpec
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.ModuleDeclaration
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.ModuleId
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.ThingPart
- HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.TypeInfo
- HsDev.Symbols.Types: instance GHC.Read.Read Data.Time.Clock.POSIX.POSIXTime
- HsDev.Symbols.Types: instance GHC.Read.Read HsDev.Symbols.Types.TypeInfo
- HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.Declaration
- HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.Export
- HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.ExportedDeclaration
- HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.Import
- HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.ImportList
- HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.ImportSpec
- HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.ModuleDeclaration
- HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.ModuleId
- HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.ThingPart
- HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.TypeInfo
- HsDev.Symbols.Types: instance HsDev.Symbols.Class.Symbol HsDev.Symbols.Types.Declaration
- HsDev.Symbols.Types: instance HsDev.Symbols.Class.Symbol HsDev.Symbols.Types.Module
- HsDev.Symbols.Types: instance HsDev.Symbols.Class.Symbol HsDev.Symbols.Types.ModuleDeclaration
- HsDev.Symbols.Types: instance HsDev.Symbols.Class.Symbol HsDev.Symbols.Types.ModuleId
- HsDev.Symbols.Types: instance HsDev.Symbols.Documented.Documented HsDev.Symbols.Types.Declaration
- HsDev.Symbols.Types: instance HsDev.Symbols.Documented.Documented HsDev.Symbols.Types.ModuleDeclaration
- HsDev.Symbols.Types: instance HsDev.Symbols.Documented.Documented HsDev.Symbols.Types.ModuleId
- HsDev.Symbols.Types: localDeclarations :: Traversal' DeclarationInfo [Declaration]
- HsDev.Symbols.Types: minimalDecl :: Lens' Declaration Declaration
- HsDev.Symbols.Types: moduleContents :: Module -> [String]
- HsDev.Symbols.Types: moduleDeclaration :: Lens' ModuleDeclaration Declaration
- HsDev.Symbols.Types: moduleDeclarations :: Lens' Module [Declaration]
- HsDev.Symbols.Types: moduleIdLocation :: Lens' ModuleId ModuleLocation
- HsDev.Symbols.Types: moduleIdName :: Lens' ModuleId Text
- HsDev.Symbols.Types: moduleLocation :: Lens' Module ModuleLocation
- HsDev.Symbols.Types: moduleName :: Lens' Module Text
- HsDev.Symbols.Types: related :: Traversal' DeclarationInfo (Maybe Text)
- HsDev.Symbols.Types: showTypeInfo :: TypeInfo -> String -> String -> String
- HsDev.Symbols.Types: typeInfo :: Traversal' DeclarationInfo TypeInfo
- HsDev.Symbols.Types: typeInfoArgs :: Lens' TypeInfo [Text]
- HsDev.Symbols.Types: typeInfoContext :: Lens' TypeInfo (Maybe Text)
- HsDev.Symbols.Types: typeInfoDefinition :: Lens' TypeInfo (Maybe Text)
- HsDev.Symbols.Types: typeInfoFunctions :: Lens' TypeInfo [Text]
- HsDev.Symbols.Util: allOf :: [a -> Bool] -> a -> Bool
- HsDev.Symbols.Util: anyOf :: [a -> Bool] -> a -> Bool
- HsDev.Symbols.Util: byFile :: ModuleId -> Bool
- HsDev.Symbols.Util: imports :: Module -> [Import]
- HsDev.Symbols.Util: inDepsOfFile :: Project -> FilePath -> ModuleId -> Bool
- HsDev.Symbols.Util: inDepsOfProject :: Project -> ModuleId -> Bool
- HsDev.Symbols.Util: inDepsOfTarget :: Info -> ModuleId -> Bool
- HsDev.Symbols.Util: inFile :: FilePath -> ModuleId -> Bool
- HsDev.Symbols.Util: inModule :: String -> ModuleId -> Bool
- HsDev.Symbols.Util: inModuleSource :: Maybe String -> ModuleId -> Bool
- HsDev.Symbols.Util: inPackage :: String -> ModuleId -> Bool
- HsDev.Symbols.Util: inPackageDb :: PackageDb -> ModuleId -> Bool
- HsDev.Symbols.Util: inPackageDbStack :: PackageDbStack -> ModuleId -> Bool
- HsDev.Symbols.Util: inProject :: Project -> ModuleId -> Bool
- HsDev.Symbols.Util: inScope :: Module -> Maybe String -> ModuleId -> Bool
- HsDev.Symbols.Util: inVersion :: String -> ModuleId -> Bool
- HsDev.Symbols.Util: installed :: ModuleId -> Bool
- HsDev.Symbols.Util: moduleImported :: ModuleId -> [Import] -> Bool
- HsDev.Symbols.Util: newestPackage :: Symbol a => [a] -> [a]
- HsDev.Symbols.Util: packageDbOf :: ModuleId -> Maybe PackageDb
- HsDev.Symbols.Util: packageOf :: ModuleId -> Maybe ModulePackage
- HsDev.Symbols.Util: preferredModule :: PackageDb -> Maybe Project -> [ModuleId] -> Maybe ModuleId
- HsDev.Symbols.Util: projectOf :: ModuleId -> Maybe Project
- HsDev.Symbols.Util: qualifier :: Module -> Maybe String -> [Import]
- HsDev.Symbols.Util: sourceModule :: Maybe Project -> [Module] -> Maybe Module
- HsDev.Symbols.Util: standalone :: ModuleId -> Bool
- HsDev.Symbols.Util: uniqueModules :: PackageDb -> Maybe Project -> [ModuleId] -> [ModuleId]
- HsDev.Symbols.Util: visible :: Project -> ModuleId -> ModuleId -> Bool
- HsDev.Symbols.Util: visibleModule :: PackageDb -> Maybe Project -> [Module] -> Maybe Module
- HsDev.Tools.AutoFix: Correction :: String -> Replace String -> Correction
- HsDev.Tools.AutoFix: [_correctionMessage] :: Correction -> String
- HsDev.Tools.AutoFix: [_corrector] :: Correction -> Replace String
- HsDev.Tools.AutoFix: autoFix :: [Note Correction] -> ([Note Correction], Maybe String) -> ([Note Correction], Maybe String)
- HsDev.Tools.AutoFix: correctionMessage :: Lens' Correction String
- HsDev.Tools.AutoFix: corrector :: Lens' Correction (Replace String)
- HsDev.Tools.AutoFix: data Correction
- HsDev.Tools.AutoFix: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Tools.AutoFix.Correction
- HsDev.Tools.AutoFix: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Tools.AutoFix.Correction
- HsDev.Tools.AutoFix: instance GHC.Classes.Eq HsDev.Tools.AutoFix.Correction
- HsDev.Tools.AutoFix: instance GHC.Show.Show HsDev.Tools.AutoFix.Correction
- HsDev.Tools.Base: parseRead :: String -> ReadM a -> Maybe a
- HsDev.Tools.Base: parseReads :: String -> ReadM a -> [a]
- HsDev.Tools.Base: readParse :: Read a => ReadM a
- HsDev.Tools.Base: type ReadM a = StateT String [] a
- HsDev.Tools.Ghc.Check: checkFile :: (MonadLog m, GhcMonad m) => [String] -> Module -> m [Note OutputMessage]
- HsDev.Tools.Ghc.Check: checkFiles :: (MonadLog m, GhcMonad m) => [String] -> [FilePath] -> Maybe Project -> m [Note OutputMessage]
- HsDev.Tools.Ghc.Check: checkSource :: (MonadLog m, GhcMonad m) => [String] -> Module -> String -> m [Note OutputMessage]
- HsDev.Tools.Ghc.Prelude: one :: a -> [a]
- HsDev.Tools.Ghc.Session: ghcSession :: [String] -> GhcM ()
- HsDev.Tools.Ghc.Session: ghciSession :: GhcM ()
- HsDev.Tools.Ghc.Session: haddockSession :: [String] -> GhcM ()
- HsDev.Tools.Ghc.Worker: addCmdOpts :: (MonadLog m, GhcMonad m) => [String] -> m ()
- HsDev.Tools.Ghc.Worker: data SessionTarget
- HsDev.Tools.Ghc.Worker: instance GHC.Classes.Eq HsDev.Tools.Ghc.Worker.SessionTarget
- HsDev.Tools.Ghc.Worker: instance GHC.Classes.Ord HsDev.Tools.Ghc.Worker.SessionTarget
- HsDev.Tools.Ghc.Worker: instance GHC.Show.Show HsDev.Tools.Ghc.Worker.SessionTarget
- HsDev.Tools.Ghc.Worker: instance Text.Format.Formattable HsDev.Tools.Ghc.Worker.SessionTarget
- HsDev.Tools.Ghc.Worker: listPackages :: GhcMonad m => m [ModulePackage]
- HsDev.Tools.Ghc.Worker: setCmdOpts :: (MonadLog m, GhcMonad m) => [String] -> m ()
- HsDev.Tools.HDocs: loadDocs :: [String] -> Module -> IO Module
- HsDev.Tools.HLint: hlintFile :: FilePath -> ExceptT String IO [Note OutputMessage]
- HsDev.Tools.HLint: hlintSource :: FilePath -> String -> ExceptT String IO [Note OutputMessage]
- HsDev.Tools.Hayoo: hayooAsDeclaration :: HayooSymbol -> Maybe ModuleDeclaration
- HsDev.Tools.Hayoo: instance HsDev.Symbols.Class.Symbol HsDev.Tools.Hayoo.HayooSymbol
- HsDev.Util: cutVersion :: Maybe [Int] -> Maybe [Int]
- HsDev.Util: eitherT :: MonadError String m => Either String a -> m a
- HsDev.Util: isParent :: FilePath -> FilePath -> Bool
- HsDev.Util: jsonUnion :: (ToJSON a, ToJSON b) => a -> b -> Value
- HsDev.Util: liftAsync :: (MonadThrow m, MonadCatch m, MonadIO m) => IO (Async a) -> ExceptT String m a
- HsDev.Util: liftEIO :: (MonadCatch m, MonadIO m) => IO a -> ExceptT String m a
- HsDev.Util: liftThrow :: (Show e, MonadError e m, MonadCatch m) => m a -> m a
- HsDev.Util: logException :: String -> (String -> IO ()) -> IO () -> IO ()
- HsDev.Util: sameVersion :: Maybe [Int] -> Maybe [Int] -> Bool
- HsDev.Util: strVersion :: Maybe [Int] -> String
- HsDev.Util: tabs :: Int -> String -> String
- HsDev.Util: withCmd :: String -> (Object -> Parser a) -> Value -> Parser a
- System.Directory.Watcher: events :: Watcher a -> IO [(a, Event)]
- System.Directory.Watcher: onEvent :: Watcher a -> (a -> Event -> IO ()) -> IO ()
+ Control.Concurrent.Util: sync :: MonadIO m => (IO () -> m a) -> m a
+ Control.Concurrent.Worker: joinWorker :: Worker m -> IO ()
+ Control.Concurrent.Worker: workerAlive :: Worker m -> IO Bool
+ Data.Deps: CyclicDeps :: [a] -> DepsError a
+ Data.Deps: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Deps.DepsError a)
+ Data.Deps: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.Deps.DepsError a)
+ Data.Deps: instance GHC.Read.Read a => GHC.Read.Read (Data.Deps.DepsError a)
+ Data.Deps: instance GHC.Show.Show a => GHC.Show.Show (Data.Deps.Deps a)
+ Data.Deps: instance GHC.Show.Show a => GHC.Show.Show (Data.Deps.DepsError a)
+ Data.Deps: linearize :: Ord a => Deps a -> Either (DepsError a) [a]
+ Data.Deps: newtype Deps a
+ Data.Deps: newtype DepsError a
+ Data.Deps: selfDepend :: Deps a -> Deps a
+ Data.LookupTable: cacheInTableM :: (Ord k, MonadIO m) => LookupTable k v -> k -> m v -> m v
+ Data.LookupTable: cachedInTable :: (Ord k, MonadIO m) => LookupTable k v -> (k -> m v) -> k -> m v
+ Data.LookupTable: hasLookupTable :: (Ord k, MonadIO m) => k -> LookupTable k v -> m (Maybe v)
+ Data.LookupTable: lookupTable :: (Ord k, MonadIO m) => k -> v -> LookupTable k v -> m v
+ Data.LookupTable: lookupTableM :: (Ord k, MonadIO m) => k -> m v -> LookupTable k v -> m v
+ Data.LookupTable: newLookupTable :: (Ord k, MonadIO m) => m (LookupTable k v)
+ Data.LookupTable: type LookupTable k v = MVar (Map k v)
+ HsDev.Client.Commands: instance Data.Aeson.Types.FromJSON.FromJSON System.Log.Simple.Base.Message
+ HsDev.Client.Commands: instance Data.Aeson.Types.ToJSON.ToJSON System.Log.Simple.Base.Message
+ HsDev.Database.SQLite: escapeLike :: Text -> Text
+ HsDev.Database.SQLite: execute :: (ToRow q, SessionMonad m) => Query -> q -> m ()
+ HsDev.Database.SQLite: executeMany :: (ToRow q, SessionMonad m) => Query -> [q] -> m ()
+ HsDev.Database.SQLite: executeNamed :: SessionMonad m => Query -> [NamedParam] -> m ()
+ HsDev.Database.SQLite: execute_ :: SessionMonad m => Query -> m ()
+ HsDev.Database.SQLite: initialize :: String -> IO Connection
+ HsDev.Database.SQLite: insertBuildInfo :: SessionMonad m => Info -> m Int
+ HsDev.Database.SQLite: insertModuleSymbols :: SessionMonad m => InspectedModule -> m ()
+ HsDev.Database.SQLite: insertPackageDb :: SessionMonad m => PackageDb -> [ModulePackage] -> m ()
+ HsDev.Database.SQLite: insertProject :: SessionMonad m => Project -> Maybe PackageDbStack -> m ()
+ HsDev.Database.SQLite: lastRow :: SessionMonad m => m Int
+ HsDev.Database.SQLite: loadModule :: SessionMonad m => Int -> m Module
+ HsDev.Database.SQLite: loadModules :: (SessionMonad m, ToRow q) => String -> q -> m [Module]
+ HsDev.Database.SQLite: loadProject :: SessionMonad m => Path -> m Project
+ HsDev.Database.SQLite: lookupModule :: SessionMonad m => ModuleId -> m (Maybe Int)
+ HsDev.Database.SQLite: lookupModuleLocation :: SessionMonad m => ModuleLocation -> m (Maybe Int)
+ HsDev.Database.SQLite: lookupSymbol :: SessionMonad m => Int -> SymbolId -> m (Maybe Int)
+ HsDev.Database.SQLite: privateMemory :: String
+ HsDev.Database.SQLite: purge :: SessionMonad m => m ()
+ HsDev.Database.SQLite: query :: (ToRow q, FromRow r, SessionMonad m) => Query -> q -> m [r]
+ HsDev.Database.SQLite: queryNamed :: (FromRow r, SessionMonad m) => Query -> [NamedParam] -> m [r]
+ HsDev.Database.SQLite: query_ :: (FromRow r, SessionMonad m) => Query -> m [r]
+ HsDev.Database.SQLite: removeModule :: SessionMonad m => Int -> m ()
+ HsDev.Database.SQLite: removeModuleContents :: SessionMonad m => Int -> m ()
+ HsDev.Database.SQLite: removePackageDb :: SessionMonad m => PackageDb -> m ()
+ HsDev.Database.SQLite: removeProject :: SessionMonad m => Project -> m ()
+ HsDev.Database.SQLite: sharedMemory :: String
+ HsDev.Database.SQLite: updateModule :: SessionMonad m => InspectedModule -> m ()
+ HsDev.Database.SQLite: updatePackageDb :: SessionMonad m => PackageDb -> [ModulePackage] -> m ()
+ HsDev.Database.SQLite: updateProject :: SessionMonad m => Project -> Maybe PackageDbStack -> m ()
+ HsDev.Database.SQLite: upsertModule :: SessionMonad m => InspectedModule -> m Int
+ HsDev.Database.SQLite: withTemporaryTable :: SessionMonad m => String -> [String] -> m a -> m a
+ HsDev.Database.SQLite.Instances: JSON :: a -> JSON a
+ HsDev.Database.SQLite.Instances: [getJSON] :: JSON a -> a
+ HsDev.Database.SQLite.Instances: instance Data.Aeson.Types.FromJSON.FromJSON a => Database.SQLite.Simple.FromField.FromField (HsDev.Database.SQLite.Instances.JSON a)
+ HsDev.Database.SQLite.Instances: instance Data.Aeson.Types.ToJSON.ToJSON a => Database.SQLite.Simple.ToField.ToField (HsDev.Database.SQLite.Instances.JSON a)
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromField.FromField Data.Aeson.Types.Internal.Value
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromField.FromField HsDev.PackageDb.Types.PackageDb
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromField.FromField Language.Haskell.Extension.Language
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Project.Types.Executable
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Project.Types.Info
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Project.Types.Library
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Project.Types.Project
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Project.Types.Test
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Symbols.Location.ModuleId
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Symbols.Location.ModuleLocation
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Symbols.Location.ModulePackage
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Symbols.Location.Position
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Symbols.Location.Region
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Symbols.Location.SymbolId
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Symbols.Types.Inspection
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Symbols.Types.Symbol
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Symbols.Types.SymbolUsage
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.FromRow.FromRow HsDev.Tools.Ghc.Types.TypedExpr
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToField.ToField Data.Aeson.Types.Internal.Value
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToField.ToField HsDev.PackageDb.Types.PackageDb
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToRow.ToRow HsDev.Symbols.Location.ModuleId
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToRow.ToRow HsDev.Symbols.Location.ModuleLocation
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToRow.ToRow HsDev.Symbols.Location.ModulePackage
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToRow.ToRow HsDev.Symbols.Location.Position
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToRow.ToRow HsDev.Symbols.Location.Region
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToRow.ToRow HsDev.Symbols.Location.SymbolId
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToRow.ToRow HsDev.Symbols.Types.Inspection
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToRow.ToRow HsDev.Symbols.Types.Symbol
+ HsDev.Database.SQLite.Instances: instance Database.SQLite.Simple.ToRow.ToRow HsDev.Tools.Ghc.Types.TypedExpr
+ HsDev.Database.SQLite.Instances: instance GHC.Classes.Eq a => GHC.Classes.Eq (HsDev.Database.SQLite.Instances.JSON a)
+ HsDev.Database.SQLite.Instances: instance GHC.Classes.Ord a => GHC.Classes.Ord (HsDev.Database.SQLite.Instances.JSON a)
+ HsDev.Database.SQLite.Instances: instance GHC.Read.Read a => GHC.Read.Read (HsDev.Database.SQLite.Instances.JSON a)
+ HsDev.Database.SQLite.Instances: instance GHC.Show.Show a => GHC.Show.Show (HsDev.Database.SQLite.Instances.JSON a)
+ HsDev.Database.SQLite.Instances: newtype JSON a
+ HsDev.Database.SQLite.Schema: commands :: [Query]
+ HsDev.Database.SQLite.Schema: schema :: Text
+ HsDev.Database.SQLite.Schema.TH: schemaExp :: ExpQ
+ HsDev.Database.SQLite.Select: Select :: [Text] -> [Text] -> [Text] -> Select
+ HsDev.Database.SQLite.Select: [selectColumns] :: Select -> [Text]
+ HsDev.Database.SQLite.Select: [selectConditions] :: Select -> [Text]
+ HsDev.Database.SQLite.Select: [selectTables] :: Select -> [Text]
+ HsDev.Database.SQLite.Select: buildQuery :: Select -> String
+ HsDev.Database.SQLite.Select: data Select
+ HsDev.Database.SQLite.Select: instance GHC.Base.Monoid HsDev.Database.SQLite.Select.Select
+ HsDev.Database.SQLite.Select: instance GHC.Classes.Eq HsDev.Database.SQLite.Select.Select
+ HsDev.Database.SQLite.Select: instance GHC.Classes.Ord HsDev.Database.SQLite.Select.Select
+ HsDev.Database.SQLite.Select: instance GHC.Read.Read HsDev.Database.SQLite.Select.Select
+ HsDev.Database.SQLite.Select: instance GHC.Show.Show HsDev.Database.SQLite.Select.Select
+ HsDev.Database.SQLite.Select: qBuildInfo :: Select
+ HsDev.Database.SQLite.Select: qModuleId :: Select
+ HsDev.Database.SQLite.Select: qModuleLocation :: Select
+ HsDev.Database.SQLite.Select: qSymbol :: Select
+ HsDev.Database.SQLite.Select: qSymbolId :: Select
+ HsDev.Database.SQLite.Select: select_ :: [Text] -> [Text] -> [Text] -> Select
+ HsDev.Database.SQLite.Select: toQuery :: Select -> Query
+ HsDev.Database.SQLite.Select: where_ :: [Text] -> Select
+ HsDev.Database.SQLite.Transaction: Deferred :: TransactionType
+ HsDev.Database.SQLite.Transaction: Exclusive :: TransactionType
+ HsDev.Database.SQLite.Transaction: Immediate :: TransactionType
+ HsDev.Database.SQLite.Transaction: Retries :: [Int] -> (SQLError -> Bool) -> Retries
+ HsDev.Database.SQLite.Transaction: [retriesError] :: Retries -> SQLError -> Bool
+ HsDev.Database.SQLite.Transaction: [retriesIntervals] :: Retries -> [Int]
+ HsDev.Database.SQLite.Transaction: beginTransaction :: MonadIO m => Connection -> TransactionType -> m ()
+ HsDev.Database.SQLite.Transaction: commitTransaction :: MonadIO m => Connection -> m ()
+ HsDev.Database.SQLite.Transaction: data Retries
+ HsDev.Database.SQLite.Transaction: data TransactionType
+ HsDev.Database.SQLite.Transaction: def :: Default a => a
+ HsDev.Database.SQLite.Transaction: instance Data.Default.Class.Default HsDev.Database.SQLite.Transaction.Retries
+ HsDev.Database.SQLite.Transaction: instance GHC.Classes.Eq HsDev.Database.SQLite.Transaction.TransactionType
+ HsDev.Database.SQLite.Transaction: instance GHC.Classes.Ord HsDev.Database.SQLite.Transaction.TransactionType
+ HsDev.Database.SQLite.Transaction: instance GHC.Read.Read HsDev.Database.SQLite.Transaction.TransactionType
+ HsDev.Database.SQLite.Transaction: instance GHC.Show.Show HsDev.Database.SQLite.Transaction.TransactionType
+ HsDev.Database.SQLite.Transaction: noRetry :: Retries
+ HsDev.Database.SQLite.Transaction: retry :: (MonadIO m, MonadCatch m) => Retries -> m a -> m a
+ HsDev.Database.SQLite.Transaction: retryForever :: Int -> Retries
+ HsDev.Database.SQLite.Transaction: retryN :: Int -> Int -> Retries
+ HsDev.Database.SQLite.Transaction: retry_ :: (MonadIO m, MonadCatch m) => m a -> m a
+ HsDev.Database.SQLite.Transaction: rollbackTransaction :: MonadIO m => Connection -> m ()
+ HsDev.Database.SQLite.Transaction: transaction :: SessionMonad m => TransactionType -> Retries -> m a -> m a
+ HsDev.Database.SQLite.Transaction: transaction_ :: SessionMonad m => TransactionType -> m a -> m a
+ HsDev.Database.SQLite.Transaction: withTransaction :: (MonadIO m, MonadMask m) => Connection -> TransactionType -> Retries -> m a -> m a
+ HsDev.Database.Update: applyUpdates :: UpdateOptions -> [(Watched, Event)] -> ClientM IO ()
+ HsDev.Database.Update: processEvents :: ([(Watched, Event)] -> IO ()) -> MVar (Async ()) -> MVar [(Watched, Event)] -> [(Watched, Event)] -> ClientM IO ()
+ HsDev.Database.Update: runTasks_ :: UpdateMonad m => [m ()] -> m ()
+ HsDev.Database.Update: scanFiles :: UpdateMonad m => [(FileSource, [String])] -> m ()
+ HsDev.Database.Update: scanPackageDbStackDocs :: UpdateMonad m => [String] -> PackageDbStack -> m ()
+ HsDev.Database.Update: setModTypes :: UpdateMonad m => ModuleId -> [Note TypedExpr] -> m ()
+ HsDev.Database.Update: updateEvents :: ServerMonadBase m => [(Watched, Event)] -> UpdateM m ()
+ HsDev.Database.Update.Types: UpdateState :: UpdateOptions -> Worker (ServerM IO) -> UpdateState
+ HsDev.Database.Update.Types: [_updateOptions] :: UpdateState -> UpdateOptions
+ HsDev.Database.Update.Types: [_updateWorker] :: UpdateState -> Worker (ServerM IO)
+ HsDev.Database.Update.Types: data UpdateState
+ HsDev.Database.Update.Types: instance GHC.Base.Monad m => Control.Monad.Reader.Class.MonadReader HsDev.Database.Update.Types.UpdateState (HsDev.Database.Update.Types.UpdateM m)
+ HsDev.Database.Update.Types: sendUpdateAction :: UpdateMonad m => ServerM IO () -> m ()
+ HsDev.Database.Update.Types: updateOptions :: Lens' UpdateState UpdateOptions
+ HsDev.Database.Update.Types: updateWorker :: Lens' UpdateState (Worker (ServerM IO))
+ HsDev.Database.Update.Types: withUpdateState :: SessionMonad m => UpdateOptions -> (UpdateState -> m a) -> m a
+ HsDev.Display: instance HsDev.Display.Display HsDev.PackageDb.Types.PackageDb
+ HsDev.Display: instance HsDev.Display.Display HsDev.PackageDb.Types.PackageDbStack
+ HsDev.Display: instance HsDev.Display.Display HsDev.Symbols.Types.ModuleTag
+ HsDev.Display: instance HsDev.Display.Display System.Directory.Paths.Path
+ HsDev.Display: instance Text.Format.Formattable HsDev.PackageDb.Types.PackageDb
+ HsDev.Display: instance Text.Format.Formattable HsDev.PackageDb.Types.PackageDbStack
+ HsDev.Display: instance Text.Format.Formattable HsDev.Sandbox.Sandbox
+ HsDev.Inspect: AnalyzeEnv :: Environment -> Map Name Fixity -> RefineTable -> AnalyzeEnv
+ HsDev.Inspect: Preloaded :: ModuleId -> ParseMode -> Module SrcSpanInfo -> Inspection -> Text -> Preloaded
+ HsDev.Inspect: [_analyzeEnv] :: AnalyzeEnv -> Environment
+ HsDev.Inspect: [_analyzeFixities] :: AnalyzeEnv -> Map Name Fixity
+ HsDev.Inspect: [_analyzeRefine] :: AnalyzeEnv -> RefineTable
+ HsDev.Inspect: [_preloadedId] :: Preloaded -> ModuleId
+ HsDev.Inspect: [_preloadedMode] :: Preloaded -> ParseMode
+ HsDev.Inspect: [_preloadedModule] :: Preloaded -> Module SrcSpanInfo
+ HsDev.Inspect: [_preloadedTime] :: Preloaded -> Inspection
+ HsDev.Inspect: [_preloaded] :: Preloaded -> Text
+ HsDev.Inspect: analyzeEnv :: Lens' AnalyzeEnv Environment
+ HsDev.Inspect: analyzeFixities :: Lens' AnalyzeEnv (Map Name Fixity)
+ HsDev.Inspect: analyzePreloaded :: AnalyzeEnv -> Preloaded -> Either String Module
+ HsDev.Inspect: analyzeRefine :: Lens' AnalyzeEnv RefineTable
+ HsDev.Inspect: analyzeResolve :: AnalyzeEnv -> Module -> Module
+ HsDev.Inspect: asModule :: Lens' Preloaded Module
+ HsDev.Inspect: data AnalyzeEnv
+ HsDev.Inspect: data Preloaded
+ HsDev.Inspect: fileContentsInspection :: [String] -> IO Inspection
+ HsDev.Inspect: fileContentsInspection_ :: [String] -> POSIXTime -> Inspection
+ HsDev.Inspect: installedInspection :: [String] -> IO Inspection
+ HsDev.Inspect: instance Control.DeepSeq.NFData HsDev.Inspect.Preloaded
+ HsDev.Inspect: instance GHC.Base.Monoid HsDev.Inspect.AnalyzeEnv
+ HsDev.Inspect: moduleAnalyzeEnv :: Module -> AnalyzeEnv
+ HsDev.Inspect: moduleInspection :: ModuleLocation -> [String] -> IO Inspection
+ HsDev.Inspect: preload :: Text -> [(String, String)] -> [String] -> ModuleLocation -> Maybe Text -> IO Preloaded
+ HsDev.Inspect: preloaded :: Lens' Preloaded Text
+ HsDev.Inspect: preloadedId :: Lens' Preloaded ModuleId
+ HsDev.Inspect: preloadedMode :: Lens' Preloaded ParseMode
+ HsDev.Inspect: preloadedModule :: Lens' Preloaded (Module SrcSpanInfo)
+ HsDev.Inspect: preloadedTime :: Lens' Preloaded Inspection
+ HsDev.Inspect: sourceInspection :: Path -> Maybe Text -> [String] -> IO Inspection
+ HsDev.Inspect.Order: order :: [Preloaded] -> Either (DepsError Path) [Preloaded]
+ HsDev.PackageDb: packageDbPath :: PackageDb -> IO Path
+ HsDev.PackageDb: readPackageDb :: PackageDb -> IO (Map ModulePackage [ModuleLocation])
+ HsDev.PackageDb.Types: GlobalDb :: PackageDb
+ HsDev.PackageDb.Types: PackageDb :: Path -> PackageDb
+ HsDev.PackageDb.Types: PackageDbStack :: [PackageDb] -> PackageDbStack
+ HsDev.PackageDb.Types: UserDb :: PackageDb
+ HsDev.PackageDb.Types: [_packageDbStack] :: PackageDbStack -> [PackageDb]
+ HsDev.PackageDb.Types: [_packageDb] :: PackageDb -> Path
+ HsDev.PackageDb.Types: data PackageDb
+ HsDev.PackageDb.Types: fromPackageDbs :: [Path] -> PackageDbStack
+ HsDev.PackageDb.Types: globalDb :: PackageDbStack
+ HsDev.PackageDb.Types: instance Control.DeepSeq.NFData HsDev.PackageDb.Types.PackageDb
+ HsDev.PackageDb.Types: instance Control.DeepSeq.NFData HsDev.PackageDb.Types.PackageDbStack
+ HsDev.PackageDb.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.PackageDb.Types.PackageDb
+ HsDev.PackageDb.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.PackageDb.Types.PackageDbStack
+ HsDev.PackageDb.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.PackageDb.Types.PackageDb
+ HsDev.PackageDb.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.PackageDb.Types.PackageDbStack
+ HsDev.PackageDb.Types: instance GHC.Classes.Eq HsDev.PackageDb.Types.PackageDb
+ HsDev.PackageDb.Types: instance GHC.Classes.Eq HsDev.PackageDb.Types.PackageDbStack
+ HsDev.PackageDb.Types: instance GHC.Classes.Ord HsDev.PackageDb.Types.PackageDb
+ HsDev.PackageDb.Types: instance GHC.Classes.Ord HsDev.PackageDb.Types.PackageDbStack
+ HsDev.PackageDb.Types: instance GHC.Show.Show HsDev.PackageDb.Types.PackageDb
+ HsDev.PackageDb.Types: instance GHC.Show.Show HsDev.PackageDb.Types.PackageDbStack
+ HsDev.PackageDb.Types: instance System.Directory.Paths.Paths HsDev.PackageDb.Types.PackageDb
+ HsDev.PackageDb.Types: instance System.Directory.Paths.Paths HsDev.PackageDb.Types.PackageDbStack
+ HsDev.PackageDb.Types: isSubStack :: PackageDbStack -> PackageDbStack -> Bool
+ HsDev.PackageDb.Types: newtype PackageDbStack
+ HsDev.PackageDb.Types: packageDb :: Traversal' PackageDb Path
+ HsDev.PackageDb.Types: packageDbOpt :: PackageDb -> String
+ HsDev.PackageDb.Types: packageDbStack :: Iso' PackageDbStack [PackageDb]
+ HsDev.PackageDb.Types: packageDbStackOpts :: PackageDbStack -> [String]
+ HsDev.PackageDb.Types: packageDbStacks :: PackageDbStack -> [PackageDbStack]
+ HsDev.PackageDb.Types: packageDbs :: PackageDbStack -> [PackageDb]
+ HsDev.PackageDb.Types: topPackageDb :: PackageDbStack -> PackageDb
+ HsDev.PackageDb.Types: userDb :: PackageDbStack
+ HsDev.Project: fileInTarget :: Path -> Info -> Bool
+ HsDev.Project: projectTargetFiles :: (MonadLog m, Target t) => Project -> t -> m [Path]
+ HsDev.Project: targetFiles :: Target t => t -> [Path]
+ HsDev.Project.Types: TargetInfo :: Maybe Text -> Info -> Maybe Path -> [[Text]] -> TargetInfo
+ HsDev.Project.Types: [_infoOtherModules] :: Info -> [[Text]]
+ HsDev.Project.Types: [_targetBuildInfo] :: TargetInfo -> Info
+ HsDev.Project.Types: [_targetInfoMain] :: TargetInfo -> Maybe Path
+ HsDev.Project.Types: [_targetInfoModules] :: TargetInfo -> [[Text]]
+ HsDev.Project.Types: [_targetInfoName] :: TargetInfo -> Maybe Text
+ HsDev.Project.Types: [_testMain] :: Test -> Maybe Path
+ HsDev.Project.Types: data TargetInfo
+ HsDev.Project.Types: infoOtherModules :: Lens' Info [[Text]]
+ HsDev.Project.Types: infos :: Traversal' ProjectDescription Info
+ HsDev.Project.Types: instance Data.Aeson.Types.FromJSON.FromJSON Language.Haskell.Extension.Extension
+ HsDev.Project.Types: instance Data.Aeson.Types.FromJSON.FromJSON Language.Haskell.Extension.Language
+ HsDev.Project.Types: instance Data.Aeson.Types.ToJSON.ToJSON Language.Haskell.Extension.Extension
+ HsDev.Project.Types: instance Data.Aeson.Types.ToJSON.ToJSON Language.Haskell.Extension.Language
+ HsDev.Project.Types: instance GHC.Classes.Eq HsDev.Project.Types.TargetInfo
+ HsDev.Project.Types: instance GHC.Classes.Ord HsDev.Project.Types.TargetInfo
+ HsDev.Project.Types: instance GHC.Show.Show HsDev.Project.Types.TargetInfo
+ HsDev.Project.Types: instance HsDev.Project.Types.Target HsDev.Project.Types.TargetInfo
+ HsDev.Project.Types: instance System.Directory.Paths.Paths HsDev.Project.Types.TargetInfo
+ HsDev.Project.Types: targetBuildInfo :: Lens' TargetInfo Info
+ HsDev.Project.Types: targetInfo :: Target a => a -> TargetInfo
+ HsDev.Project.Types: targetInfoMain :: Lens' TargetInfo (Maybe Path)
+ HsDev.Project.Types: targetInfoModules :: Lens' TargetInfo [[Text]]
+ HsDev.Project.Types: targetInfoName :: Lens' TargetInfo (Maybe Text)
+ HsDev.Project.Types: targetInfos :: ProjectDescription -> [TargetInfo]
+ HsDev.Project.Types: targetMain :: Target a => a -> Maybe Path
+ HsDev.Project.Types: targetModules :: Target a => a -> [[Text]]
+ HsDev.Project.Types: targetName :: Target a => Traversal' a Text
+ HsDev.Project.Types: testMain :: Lens' Test (Maybe Path)
+ HsDev.Sandbox: getProjectPackageDbStack :: Project -> GhcM PackageDbStack
+ HsDev.Sandbox: getProjectSandbox :: MonadLog m => Project -> m (Maybe Sandbox)
+ HsDev.Sandbox: getProjectTargetOpts :: [String] -> Project -> Info -> GhcM [String]
+ HsDev.Scan: enumDependent :: CommandMonad m => FilePath -> m ScanContents
+ HsDev.Scan: enumRescan :: CommandMonad m => FilePath -> m ScanContents
+ HsDev.Scan: getFileContents :: SessionMonad m => Path -> m (Maybe (POSIXTime, Text))
+ HsDev.Scan: instance HsDev.Scan.EnumContents HsDev.PackageDb.Types.PackageDbStack
+ HsDev.Scan: instance HsDev.Scan.EnumContents System.Directory.Paths.Path
+ HsDev.Scan.Browse: browseModules' :: [String] -> [ModuleLocation] -> GhcM [InspectedModule]
+ HsDev.Scan.Browse: modulesPackages :: [ModuleLocation] -> [ModulePackage]
+ HsDev.Scan.Browse: modulesPackagesGroups :: [ModuleLocation] -> [(ModulePackage, [ModuleLocation])]
+ HsDev.Scan.Browse: withEachPackage :: (ModulePackage -> [ModuleLocation] -> GhcM a) -> [ModuleLocation] -> GhcM [a]
+ HsDev.Server.Base: clientCommand :: CommandOptions -> Command -> ServerM IO Result
+ HsDev.Server.Base: findPath :: MonadIO m => CommandOptions -> FilePath -> m FilePath
+ HsDev.Server.Base: makeSocket :: ConnectionPort -> IO Socket
+ HsDev.Server.Base: parseCommand :: [String] -> Either String Command
+ HsDev.Server.Base: processClient :: SessionMonad m => String -> Chan ByteString -> (ByteString -> IO ()) -> m ()
+ HsDev.Server.Base: processClientSocket :: SessionMonad m => String -> Socket -> m ()
+ HsDev.Server.Base: processRequest :: SessionMonad m => CommandOptions -> Command -> m Result
+ HsDev.Server.Base: readCommand :: [String] -> Command
+ HsDev.Server.Base: sendServer :: Server -> CommandOptions -> [String] -> IO Result
+ HsDev.Server.Base: sendServer_ :: Server -> [String] -> IO Result
+ HsDev.Server.Base: setupServer :: ServerOpts -> ServerM IO ()
+ HsDev.Server.Base: shutdownServer :: ServerOpts -> ServerM IO ()
+ HsDev.Server.Base: sockAddr :: ConnectionPort -> HostAddress -> SockAddr
+ HsDev.Server.Base: startServer_ :: ServerOpts -> IO Server
+ HsDev.Server.Base: stopServer :: Server -> IO ()
+ HsDev.Server.Base: unMmap :: Response -> IO Response
+ HsDev.Server.Base: withServer :: ServerOpts -> (Server -> IO a) -> IO a
+ HsDev.Server.Base: withServer_ :: ServerOpts -> (Server -> IO a) -> IO a
+ HsDev.Server.Commands: [serverDbFile] :: ServerOpts -> Maybe FilePath
+ HsDev.Server.Message: decodeMessage :: FromJSON a => ByteString -> Either (Msg String) (Msg (Message a))
+ HsDev.Server.Message: encodeMessage :: ToJSON a => Msg (Message a) -> ByteString
+ HsDev.Server.Message.Lisp: decodeMsg :: FromJSON a => ByteString -> Either (Msg String) (Msg a)
+ HsDev.Server.Message.Lisp: encodeMsg :: ToJSON a => Msg a -> ByteString
+ HsDev.Server.Message.Lisp: isLisp :: Lens' (Msg a) Bool
+ HsDev.Server.Message.Lisp: jsonMsg :: a -> Msg a
+ HsDev.Server.Message.Lisp: lispMsg :: a -> Msg a
+ HsDev.Server.Message.Lisp: msg :: Lens (Msg a) (Msg b) a b
+ HsDev.Server.Message.Lisp: type Msg a = (Bool, a)
+ HsDev.Server.Types: FindUsages :: Text -> Command
+ HsDev.Server.Types: Refactor :: [Note Refact] -> [Note Refact] -> Bool -> Command
+ HsDev.Server.Types: Rename :: Text -> Text -> Path -> Command
+ HsDev.Server.Types: SetFileContents :: Path -> (Maybe Text) -> Command
+ HsDev.Server.Types: StopGhc :: Command
+ HsDev.Server.Types: TargetInstalled :: TargetFilter
+ HsDev.Server.Types: UnresolvedSymbols :: [Path] -> Command
+ HsDev.Server.Types: Whoat :: Int -> Int -> Path -> Command
+ HsDev.Server.Types: [checkClear] :: Command -> Bool
+ HsDev.Server.Types: [checkLinkClear] :: Command -> Bool
+ HsDev.Server.Types: [serverDbFile] :: ServerOpts -> Maybe FilePath
+ HsDev.Server.Types: [sessionClients] :: Session -> Chan (IO ())
+ HsDev.Server.Types: [sessionFileContents] :: Session -> Path -> Maybe Text -> IO ()
+ HsDev.Server.Types: [sessionSqlDatabase] :: Session -> Connection
+ HsDev.Server.Types: [sessionSqlPath] :: Session -> String
+ HsDev.Server.Types: [sessionUpdater] :: Session -> Worker (ServerM IO)
+ HsDev.Server.Types: [typesClear] :: Command -> Bool
+ HsDev.Server.Types: [unresolvedFiles] :: Command -> [Path]
+ HsDev.Server.Types: closeSqlConnection :: SessionMonad m => Connection -> m ()
+ HsDev.Server.Types: inSessionUpdater :: SessionMonad m => ServerM IO a -> m a
+ HsDev.Server.Types: instance (HsDev.Server.Types.CommandMonad m, GHC.Base.Monoid w) => HsDev.Server.Types.CommandMonad (Control.Monad.Trans.Writer.Lazy.WriterT w m)
+ HsDev.Server.Types: instance (HsDev.Server.Types.SessionMonad m, GHC.Base.Monoid w) => HsDev.Server.Types.SessionMonad (Control.Monad.Trans.Writer.Lazy.WriterT w m)
+ HsDev.Server.Types: instance Control.Monad.Morph.MFunctor HsDev.Server.Types.ClientM
+ HsDev.Server.Types: instance Control.Monad.Morph.MFunctor HsDev.Server.Types.ServerM
+ HsDev.Server.Types: instance HsDev.Server.Types.CommandMonad m => HsDev.Server.Types.CommandMonad (Control.Monad.Trans.Reader.ReaderT r m)
+ HsDev.Server.Types: instance HsDev.Server.Types.CommandMonad m => HsDev.Server.Types.CommandMonad (Control.Monad.Trans.State.Lazy.StateT s m)
+ HsDev.Server.Types: instance HsDev.Server.Types.SessionMonad m => HsDev.Server.Types.SessionMonad (Control.Monad.Trans.Reader.ReaderT r m)
+ HsDev.Server.Types: instance HsDev.Server.Types.SessionMonad m => HsDev.Server.Types.SessionMonad (Control.Monad.Trans.State.Lazy.StateT s m)
+ HsDev.Server.Types: localSession :: SessionMonad m => (Session -> Session) -> m a -> m a
+ HsDev.Server.Types: openSqlConnection :: SessionMonad m => m Connection
+ HsDev.Server.Types: serverSetFileContents :: SessionMonad m => Path -> Maybe Text -> m ()
+ HsDev.Server.Types: serverSqlDatabase :: SessionMonad m => m Connection
+ HsDev.Server.Types: serverWaitClients :: SessionMonad m => m ()
+ HsDev.Server.Types: withSqlConnection :: SessionMonad m => m a -> m a
+ HsDev.Server.Types: withSqlTransaction :: SessionMonad m => ServerM IO a -> m a
+ HsDev.Symbols: projectTargetOpts :: [PackageConfig] -> Project -> Info -> [String]
+ HsDev.Symbols.Class: class Sourced a
+ HsDev.Symbols.Class: instance HsDev.Symbols.Class.Sourced HsDev.Symbols.Location.ModuleId
+ HsDev.Symbols.Class: instance HsDev.Symbols.Class.Sourced HsDev.Symbols.Location.SymbolId
+ HsDev.Symbols.Class: sourcedDocs :: Sourced a => Traversal' a Text
+ HsDev.Symbols.Class: sourcedLocation :: Sourced a => Traversal' a Position
+ HsDev.Symbols.Class: sourcedModule :: Sourced a => Lens' a ModuleId
+ HsDev.Symbols.Class: sourcedModuleName :: Sourced a => Lens' a Text
+ HsDev.Symbols.Class: sourcedName :: Sourced a => Lens' a Text
+ HsDev.Symbols.Documented: instance HsDev.Symbols.Documented.Documented HsDev.Project.Types.Project
+ HsDev.Symbols.Documented: instance HsDev.Symbols.Documented.Documented HsDev.Symbols.Location.ModuleLocation
+ HsDev.Symbols.Documented: instance HsDev.Symbols.Documented.Documented HsDev.Symbols.Location.ModulePackage
+ HsDev.Symbols.HaskellNames: class ToEnvironment a
+ HsDev.Symbols.HaskellNames: environment :: ToEnvironment a => a -> Environment
+ HsDev.Symbols.HaskellNames: fromSymbol :: Symbol -> Symbol
+ HsDev.Symbols.HaskellNames: instance HsDev.Symbols.HaskellNames.ToEnvironment HsDev.Symbols.Types.Module
+ HsDev.Symbols.HaskellNames: instance HsDev.Symbols.HaskellNames.ToEnvironment [HsDev.Symbols.Types.Module]
+ HsDev.Symbols.HaskellNames: toSymbol :: Symbol -> Symbol
+ HsDev.Symbols.Location: ModuleId :: Text -> ModuleLocation -> ModuleId
+ HsDev.Symbols.Location: NoLocation :: ModuleLocation
+ HsDev.Symbols.Location: OtherLocation :: Text -> ModuleLocation
+ HsDev.Symbols.Location: SymbolId :: Text -> ModuleId -> SymbolId
+ HsDev.Symbols.Location: [_installedModuleName] :: ModuleLocation -> Text
+ HsDev.Symbols.Location: [_moduleInstallDirs] :: ModuleLocation -> [Path]
+ HsDev.Symbols.Location: [_moduleLocation] :: ModuleId -> ModuleLocation
+ HsDev.Symbols.Location: [_moduleName] :: ModuleId -> Text
+ HsDev.Symbols.Location: [_otherLocationName] :: ModuleLocation -> Text
+ HsDev.Symbols.Location: [_symbolModule] :: SymbolId -> ModuleId
+ HsDev.Symbols.Location: [_symbolName] :: SymbolId -> Text
+ HsDev.Symbols.Location: data ModuleId
+ HsDev.Symbols.Location: data SymbolId
+ HsDev.Symbols.Location: importPath :: Text -> Path
+ HsDev.Symbols.Location: installedModuleName :: Traversal' ModuleLocation Text
+ HsDev.Symbols.Location: instance Control.DeepSeq.NFData HsDev.Symbols.Location.ModuleId
+ HsDev.Symbols.Location: instance Control.DeepSeq.NFData HsDev.Symbols.Location.SymbolId
+ HsDev.Symbols.Location: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Location.ModuleId
+ HsDev.Symbols.Location: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Location.SymbolId
+ HsDev.Symbols.Location: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Location.ModuleId
+ HsDev.Symbols.Location: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Location.SymbolId
+ HsDev.Symbols.Location: instance GHC.Classes.Eq HsDev.Symbols.Location.ModuleId
+ HsDev.Symbols.Location: instance GHC.Classes.Eq HsDev.Symbols.Location.SymbolId
+ HsDev.Symbols.Location: instance GHC.Classes.Ord HsDev.Symbols.Location.ModuleId
+ HsDev.Symbols.Location: instance GHC.Classes.Ord HsDev.Symbols.Location.SymbolId
+ HsDev.Symbols.Location: instance GHC.Show.Show HsDev.Symbols.Location.ModuleId
+ HsDev.Symbols.Location: instance GHC.Show.Show HsDev.Symbols.Location.SymbolId
+ HsDev.Symbols.Location: mkPackage :: Text -> ModulePackage
+ HsDev.Symbols.Location: moduleInstallDirs :: Traversal' ModuleLocation [Path]
+ HsDev.Symbols.Location: moduleLocation :: Lens' ModuleId ModuleLocation
+ HsDev.Symbols.Location: moduleName :: Lens' ModuleId Text
+ HsDev.Symbols.Location: otherLocationName :: Traversal' ModuleLocation Text
+ HsDev.Symbols.Location: sourceRoot :: ModuleId -> Maybe Path
+ HsDev.Symbols.Location: sourceRoot_ :: ModuleId -> Path
+ HsDev.Symbols.Location: symbolModule :: Lens' SymbolId ModuleId
+ HsDev.Symbols.Location: symbolName :: Lens' SymbolId Text
+ HsDev.Symbols.Name: fromModuleName_ :: ModuleName () -> Text
+ HsDev.Symbols.Name: fromName :: Name -> Text
+ HsDev.Symbols.Name: fromName_ :: Name () -> Text
+ HsDev.Symbols.Name: nameIdent :: Name -> Text
+ HsDev.Symbols.Name: nameModule :: Name -> Maybe Text
+ HsDev.Symbols.Name: qualName :: String -> String -> Name
+ HsDev.Symbols.Name: toModuleName_ :: Text -> ModuleName ()
+ HsDev.Symbols.Name: toName :: Text -> Name
+ HsDev.Symbols.Name: toName_ :: Text -> Name ()
+ HsDev.Symbols.Name: type Name = QName ()
+ HsDev.Symbols.Name: unqualName :: String -> Name
+ HsDev.Symbols.Parsed: annL :: Annotated ast => Lens' (ast a) a
+ HsDev.Symbols.Parsed: binders :: Annotated ast => Traversal' (ast Ann) (ast Ann)
+ HsDev.Symbols.Parsed: declarations :: Data (ast Ann) => Traversal' (ast Ann) (Decl Ann)
+ HsDev.Symbols.Parsed: defPos :: Annotated ast => Traversal' (ast Ann) Position
+ HsDev.Symbols.Parsed: file :: Annotated ast => Lens' (ast Ann) FilePath
+ HsDev.Symbols.Parsed: fileL :: (SrcInfo isrc, Data isrc) => Lens' isrc FilePath
+ HsDev.Symbols.Parsed: globals :: Annotated ast => Traversal' (ast Ann) (ast Ann)
+ HsDev.Symbols.Parsed: imports :: Data (ast Ann) => Traversal' (ast Ann) (ImportDecl Ann)
+ HsDev.Symbols.Parsed: isBinder :: Annotated ast => ast Ann -> Bool
+ HsDev.Symbols.Parsed: isGlobal :: Annotated ast => ast Ann -> Bool
+ HsDev.Symbols.Parsed: isLocal :: Annotated ast => ast Ann -> Bool
+ HsDev.Symbols.Parsed: isReference :: Annotated ast => ast Ann -> Bool
+ HsDev.Symbols.Parsed: isUnresolved :: Annotated ast => ast Ann -> Bool
+ HsDev.Symbols.Parsed: locals :: Annotated ast => Traversal' (ast Ann) (ast Ann)
+ HsDev.Symbols.Parsed: moduleNames :: Data (ast Ann) => Traversal' (ast Ann) (ModuleName Ann)
+ HsDev.Symbols.Parsed: nameInfoL :: Lens' (Scoped a) (NameInfo a)
+ HsDev.Symbols.Parsed: named :: Annotated ast => Text -> Traversal' (ast Ann) (ast Ann)
+ HsDev.Symbols.Parsed: names :: Data (ast Ann) => Traversal' (ast Ann) (Name Ann)
+ HsDev.Symbols.Parsed: pos :: Annotated ast => Lens' (ast Ann) Position
+ HsDev.Symbols.Parsed: positionL :: (SrcInfo isrc, Data isrc) => Lens' isrc Position
+ HsDev.Symbols.Parsed: prettyPrint :: Pretty a => a -> String
+ HsDev.Symbols.Parsed: qnames :: Data (ast Ann) => Traversal' (ast Ann) (QName Ann)
+ HsDev.Symbols.Parsed: references :: Annotated ast => Traversal' (ast Ann) (ast Ann)
+ HsDev.Symbols.Parsed: refsTo :: Annotated ast => Name -> ast Ann -> Bool
+ HsDev.Symbols.Parsed: refsToName :: Annotated ast => Text -> ast Ann -> Bool
+ HsDev.Symbols.Parsed: regionL :: Annotated ast => Lens' (ast Ann) Region
+ HsDev.Symbols.Parsed: resolveError :: Annotated ast => ast Ann -> Maybe String
+ HsDev.Symbols.Parsed: resolvedName :: Annotated ast => Traversal' (ast Ann) Name
+ HsDev.Symbols.Parsed: symbolL :: Data a => Traversal' a Symbol
+ HsDev.Symbols.Parsed: symbolNameL :: Lens' Symbol Name
+ HsDev.Symbols.Parsed: type Ann = Scoped SrcSpanInfo
+ HsDev.Symbols.Parsed: type Parsed = Module Ann
+ HsDev.Symbols.Parsed: unresolveds :: Annotated ast => Traversal' (ast Ann) (ast Ann)
+ HsDev.Symbols.Parsed: usages :: Annotated ast => Name -> Traversal' (ast Ann) (ast Ann)
+ HsDev.Symbols.Resolve: refineSymbol :: RefineTable -> Symbol -> Symbol
+ HsDev.Symbols.Resolve: refineSymbols :: RefineTable -> Module -> Module
+ HsDev.Symbols.Resolve: refineTable :: [Symbol] -> RefineTable
+ HsDev.Symbols.Resolve: symbolUniqId :: Symbol -> (Text, Text, SymbolInfo)
+ HsDev.Symbols.Resolve: type RefineTable = Map (Text, Text, SymbolInfo) Symbol
+ HsDev.Symbols.Types: Constructor :: [Text] -> Text -> SymbolInfo
+ HsDev.Symbols.Types: DataFam :: [Text] -> [Text] -> Maybe Text -> SymbolInfo
+ HsDev.Symbols.Types: Method :: Maybe Text -> Text -> SymbolInfo
+ HsDev.Symbols.Types: OnlyHeaderTag :: ModuleTag
+ HsDev.Symbols.Types: PatConstructor :: [Text] -> Maybe Text -> SymbolInfo
+ HsDev.Symbols.Types: PatSelector :: Maybe Text -> Maybe Text -> Text -> SymbolInfo
+ HsDev.Symbols.Types: ScopeSymbol :: Maybe Text -> SymbolId -> ScopeSymbol
+ HsDev.Symbols.Types: Selector :: Maybe Text -> Text -> [Text] -> SymbolInfo
+ HsDev.Symbols.Types: Symbol :: SymbolId -> Maybe Text -> Maybe Position -> SymbolInfo -> Symbol
+ HsDev.Symbols.Types: SymbolUsage :: Symbol -> ModuleId -> Position -> SymbolUsage
+ HsDev.Symbols.Types: TypeFam :: [Text] -> [Text] -> Maybe Text -> SymbolInfo
+ HsDev.Symbols.Types: [_familyAssociate] :: SymbolInfo -> Maybe Text
+ HsDev.Symbols.Types: [_inspectedKey] :: Inspected k t a -> k
+ HsDev.Symbols.Types: [_moduleFixities] :: Module -> [Fixity]
+ HsDev.Symbols.Types: [_moduleId] :: Module -> ModuleId
+ HsDev.Symbols.Types: [_moduleScope] :: Module -> Map Name [Symbol]
+ HsDev.Symbols.Types: [_moduleSource] :: Module -> Maybe Parsed
+ HsDev.Symbols.Types: [_parentClass] :: SymbolInfo -> Text
+ HsDev.Symbols.Types: [_parentType] :: SymbolInfo -> Text
+ HsDev.Symbols.Types: [_patternConstructor] :: SymbolInfo -> Text
+ HsDev.Symbols.Types: [_patternType] :: SymbolInfo -> Maybe Text
+ HsDev.Symbols.Types: [_scopeQualifier] :: ScopeSymbol -> Maybe Text
+ HsDev.Symbols.Types: [_scopeSymbol] :: ScopeSymbol -> SymbolId
+ HsDev.Symbols.Types: [_selectorConstructors] :: SymbolInfo -> [Text]
+ HsDev.Symbols.Types: [_symbolDocs] :: Symbol -> Maybe Text
+ HsDev.Symbols.Types: [_symbolId] :: Symbol -> SymbolId
+ HsDev.Symbols.Types: [_symbolInfo] :: Symbol -> SymbolInfo
+ HsDev.Symbols.Types: [_symbolPosition] :: Symbol -> Maybe Position
+ HsDev.Symbols.Types: [_symbolUsedIn] :: SymbolUsage -> ModuleId
+ HsDev.Symbols.Types: [_symbolUsedPosition] :: SymbolUsage -> Position
+ HsDev.Symbols.Types: [_symbolUsed] :: SymbolUsage -> Symbol
+ HsDev.Symbols.Types: [_typeArgs] :: SymbolInfo -> [Text]
+ HsDev.Symbols.Types: [_typeContext] :: SymbolInfo -> [Text]
+ HsDev.Symbols.Types: data ScopeSymbol
+ HsDev.Symbols.Types: data Symbol
+ HsDev.Symbols.Types: data SymbolInfo
+ HsDev.Symbols.Types: data SymbolUsage
+ HsDev.Symbols.Types: exportedSymbols :: Traversal' Module Symbol
+ HsDev.Symbols.Types: familyAssociate :: Traversal' SymbolInfo (Maybe Text)
+ HsDev.Symbols.Types: fixitiesMap :: Lens' Module (Map Name Fixity)
+ HsDev.Symbols.Types: fresh :: Inspection -> Inspection -> Bool
+ HsDev.Symbols.Types: infoOf :: EmptySymbolInfo a => a -> SymbolInfo
+ HsDev.Symbols.Types: inspected :: Traversal (Inspected k t a) (Inspected k t b) a b
+ HsDev.Symbols.Types: inspectedKey :: forall k_a22lU t_a22lV a_a22lW k_a24tX. Lens (Inspected k_a22lU t_a22lV a_a22lW) (Inspected k_a24tX t_a22lV a_a22lW) k_a22lU k_a24tX
+ HsDev.Symbols.Types: inspectedTup :: Inspected k t a -> (Inspection, k, Set t, Maybe a)
+ HsDev.Symbols.Types: instance (Control.DeepSeq.NFData k, Control.DeepSeq.NFData t, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (HsDev.Symbols.Types.Inspected k t a)
+ HsDev.Symbols.Types: instance (Data.Aeson.Types.FromJSON.FromJSON k, GHC.Classes.Ord t, Data.Aeson.Types.FromJSON.FromJSON t, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (HsDev.Symbols.Types.Inspected k t a)
+ HsDev.Symbols.Types: instance (Data.Aeson.Types.ToJSON.ToJSON k, Data.Aeson.Types.ToJSON.ToJSON t, Data.Aeson.Types.ToJSON.ToJSON a) => Data.Aeson.Types.ToJSON.ToJSON (HsDev.Symbols.Types.Inspected k t a)
+ HsDev.Symbols.Types: instance (GHC.Base.Monoid a, HsDev.Symbols.Types.EmptySymbolInfo r) => HsDev.Symbols.Types.EmptySymbolInfo (a -> r)
+ HsDev.Symbols.Types: instance (GHC.Classes.Eq k, GHC.Classes.Eq t, GHC.Classes.Eq a) => GHC.Classes.Eq (HsDev.Symbols.Types.Inspected k t a)
+ HsDev.Symbols.Types: instance (GHC.Classes.Ord k, GHC.Classes.Ord t, GHC.Classes.Ord a) => GHC.Classes.Ord (HsDev.Symbols.Types.Inspected k t a)
+ HsDev.Symbols.Types: instance Control.DeepSeq.NFData (Language.Haskell.Exts.Syntax.Assoc ())
+ HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.Symbol
+ HsDev.Symbols.Types: instance Control.DeepSeq.NFData HsDev.Symbols.Types.SymbolInfo
+ HsDev.Symbols.Types: instance Control.DeepSeq.NFData Language.Haskell.Exts.Fixity.Fixity
+ HsDev.Symbols.Types: instance Control.DeepSeq.NFData Language.Haskell.Exts.Syntax.Boxed
+ HsDev.Symbols.Types: instance Control.DeepSeq.NFData l => Control.DeepSeq.NFData (Language.Haskell.Exts.Syntax.ModuleName l)
+ HsDev.Symbols.Types: instance Control.DeepSeq.NFData l => Control.DeepSeq.NFData (Language.Haskell.Exts.Syntax.Name l)
+ HsDev.Symbols.Types: instance Control.DeepSeq.NFData l => Control.DeepSeq.NFData (Language.Haskell.Exts.Syntax.QName l)
+ HsDev.Symbols.Types: instance Control.DeepSeq.NFData l => Control.DeepSeq.NFData (Language.Haskell.Exts.Syntax.SpecialCon l)
+ HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON (Language.Haskell.Exts.Syntax.Assoc ())
+ HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.ScopeSymbol
+ HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.Symbol
+ HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.SymbolInfo
+ HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Symbols.Types.SymbolUsage
+ HsDev.Symbols.Types: instance Data.Aeson.Types.FromJSON.FromJSON Language.Haskell.Exts.Fixity.Fixity
+ HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON (Language.Haskell.Exts.Syntax.Assoc ())
+ HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.ScopeSymbol
+ HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.Symbol
+ HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.SymbolInfo
+ HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Symbols.Types.SymbolUsage
+ HsDev.Symbols.Types: instance Data.Aeson.Types.ToJSON.ToJSON Language.Haskell.Exts.Fixity.Fixity
+ HsDev.Symbols.Types: instance Data.Foldable.Foldable (HsDev.Symbols.Types.Inspected k t)
+ HsDev.Symbols.Types: instance Data.Traversable.Traversable (HsDev.Symbols.Types.Inspected k t)
+ HsDev.Symbols.Types: instance GHC.Base.Functor (HsDev.Symbols.Types.Inspected k t)
+ HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.ScopeSymbol
+ HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.Symbol
+ HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.SymbolInfo
+ HsDev.Symbols.Types: instance GHC.Classes.Eq HsDev.Symbols.Types.SymbolUsage
+ HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.ScopeSymbol
+ HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.Symbol
+ HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.SymbolInfo
+ HsDev.Symbols.Types: instance GHC.Classes.Ord HsDev.Symbols.Types.SymbolUsage
+ HsDev.Symbols.Types: instance GHC.Read.Read Data.Time.Clock.Internal.POSIXTime.POSIXTime
+ HsDev.Symbols.Types: instance GHC.Read.Read HsDev.Symbols.Types.SymbolInfo
+ HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.ScopeSymbol
+ HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.Symbol
+ HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.SymbolInfo
+ HsDev.Symbols.Types: instance GHC.Show.Show HsDev.Symbols.Types.SymbolUsage
+ HsDev.Symbols.Types: instance HsDev.Symbols.Class.Sourced HsDev.Symbols.Types.Module
+ HsDev.Symbols.Types: instance HsDev.Symbols.Class.Sourced HsDev.Symbols.Types.Symbol
+ HsDev.Symbols.Types: instance HsDev.Symbols.Documented.Documented HsDev.Symbols.Location.ModuleId
+ HsDev.Symbols.Types: instance HsDev.Symbols.Documented.Documented HsDev.Symbols.Location.SymbolId
+ HsDev.Symbols.Types: instance HsDev.Symbols.Documented.Documented HsDev.Symbols.Types.Symbol
+ HsDev.Symbols.Types: instance HsDev.Symbols.Types.EmptySymbolInfo HsDev.Symbols.Types.SymbolInfo
+ HsDev.Symbols.Types: moduleFixities :: Lens' Module [Fixity]
+ HsDev.Symbols.Types: moduleScope :: Lens' Module (Map Name [Symbol])
+ HsDev.Symbols.Types: moduleSource :: Lens' Module (Maybe Parsed)
+ HsDev.Symbols.Types: moduleSymbols :: Traversal' Module Symbol
+ HsDev.Symbols.Types: nullifyInfo :: SymbolInfo -> SymbolInfo
+ HsDev.Symbols.Types: parentClass :: Traversal' SymbolInfo Text
+ HsDev.Symbols.Types: parentType :: Traversal' SymbolInfo Text
+ HsDev.Symbols.Types: patternConstructor :: Traversal' SymbolInfo Text
+ HsDev.Symbols.Types: patternType :: Traversal' SymbolInfo (Maybe Text)
+ HsDev.Symbols.Types: scopeQualifier :: Lens' ScopeSymbol (Maybe Text)
+ HsDev.Symbols.Types: scopeSymbol :: Lens' ScopeSymbol SymbolId
+ HsDev.Symbols.Types: scopeSymbols :: Traversal' Module (Symbol, [Name])
+ HsDev.Symbols.Types: selectorConstructors :: Traversal' SymbolInfo [Text]
+ HsDev.Symbols.Types: symbolDocs :: Lens' Symbol (Maybe Text)
+ HsDev.Symbols.Types: symbolId :: Lens' Symbol SymbolId
+ HsDev.Symbols.Types: symbolInfo :: Lens' Symbol SymbolInfo
+ HsDev.Symbols.Types: symbolPosition :: Lens' Symbol (Maybe Position)
+ HsDev.Symbols.Types: symbolType :: Symbol -> String
+ HsDev.Symbols.Types: symbolUsed :: Lens' SymbolUsage Symbol
+ HsDev.Symbols.Types: symbolUsedIn :: Lens' SymbolUsage ModuleId
+ HsDev.Symbols.Types: symbolUsedPosition :: Lens' SymbolUsage Position
+ HsDev.Symbols.Types: tag :: t -> Set t
+ HsDev.Symbols.Types: typeArgs :: Traversal' SymbolInfo [Text]
+ HsDev.Symbols.Types: typeContext :: Traversal' SymbolInfo [Text]
+ HsDev.Tools.AutoFix: instance Data.Text.Region.Types.Regioned a => Data.Text.Region.Types.Regioned (HsDev.Tools.Types.Note a)
+ HsDev.Tools.Ghc.Compat: getFixity :: Fixity -> (Int, FixityDirection)
+ HsDev.Tools.Ghc.Compat: recSelCtors :: RecSelParent -> [String]
+ HsDev.Tools.Ghc.Compat: recSelParent :: RecSelParent -> String
+ HsDev.Tools.Ghc.MGhc: currentSession :: MonadIO m => MGhcT s m (Maybe s)
+ HsDev.Tools.Ghc.MGhc: tempSession :: (MonadIO m, MonadMask m, ExceptionMonad m, Ord s) => s -> MGhcT s m a -> MGhcT s m a
+ HsDev.Tools.Ghc.Worker: SessionConfig :: SessionType -> [String] -> SessionConfig
+ HsDev.Tools.Ghc.Worker: SessionHaddock :: SessionType
+ HsDev.Tools.Ghc.Worker: SessionTmp :: SessionType
+ HsDev.Tools.Ghc.Worker: data SessionConfig
+ HsDev.Tools.Ghc.Worker: data SessionType
+ HsDev.Tools.Ghc.Worker: ghcRunWith :: GhcMonad m => (DynFlags -> DynFlags) -> [String] -> m a -> m a
+ HsDev.Tools.Ghc.Worker: ghcSession :: [String] -> GhcM ()
+ HsDev.Tools.Ghc.Worker: ghciSession :: GhcM ()
+ HsDev.Tools.Ghc.Worker: haddockSession :: [String] -> GhcM ()
+ HsDev.Tools.Ghc.Worker: instance GHC.Classes.Eq HsDev.Tools.Ghc.Worker.SessionConfig
+ HsDev.Tools.Ghc.Worker: instance GHC.Classes.Eq HsDev.Tools.Ghc.Worker.SessionType
+ HsDev.Tools.Ghc.Worker: instance GHC.Classes.Ord HsDev.Tools.Ghc.Worker.SessionConfig
+ HsDev.Tools.Ghc.Worker: instance GHC.Classes.Ord HsDev.Tools.Ghc.Worker.SessionType
+ HsDev.Tools.Ghc.Worker: instance GHC.Show.Show HsDev.Tools.Ghc.Worker.SessionConfig
+ HsDev.Tools.Ghc.Worker: instance GHC.Show.Show HsDev.Tools.Ghc.Worker.SessionType
+ HsDev.Tools.Ghc.Worker: instance Text.Format.Formattable HsDev.Tools.Ghc.Worker.SessionConfig
+ HsDev.Tools.Ghc.Worker: interpretedFlags :: DynFlags -> DynFlags
+ HsDev.Tools.Ghc.Worker: loadInteractive :: GhcMonad m => Path -> Maybe Text -> m ()
+ HsDev.Tools.Ghc.Worker: noLinkFlags :: DynFlags -> DynFlags
+ HsDev.Tools.Ghc.Worker: reload :: GhcMonad m => m ()
+ HsDev.Tools.Ghc.Worker: tmpSession :: [String] -> GhcM ()
+ HsDev.Tools.HDocs: hdocsPackage :: PackageConfig -> GhcM (Map Text (Map Text Text))
+ HsDev.Tools.HDocs: hdocsSupported :: Bool
+ HsDev.Tools.HDocs: readDocs :: Text -> [String] -> Path -> GhcM (Maybe (Map String String))
+ HsDev.Tools.HDocs: readModuleDocs :: [String] -> Module -> GhcM (Maybe (Map String String))
+ HsDev.Tools.HDocs: readProjectTargetDocs :: [String] -> Project -> [Path] -> GhcM (Map String (Map String String))
+ HsDev.Tools.HDocs: setModuleDocs :: MonadIO m => LookupTable (Text, Text) (Maybe Text) -> Map Text (Map Text Text) -> Module -> m Module
+ HsDev.Tools.HDocs: setSymbolDocs :: MonadIO m => LookupTable (Text, Text) (Maybe Text) -> Map Text Text -> Symbol -> m Symbol
+ HsDev.Tools.Hayoo: hayooAsSymbol :: HayooSymbol -> Maybe Symbol
+ HsDev.Tools.Hayoo: instance HsDev.Symbols.Class.Sourced HsDev.Tools.Hayoo.HayooSymbol
+ HsDev.Tools.Refact: Refact :: Text -> Replace Text -> Refact
+ HsDev.Tools.Refact: [_refactAction] :: Refact -> Replace Text
+ HsDev.Tools.Refact: [_refactMessage] :: Refact -> Text
+ HsDev.Tools.Refact: cut :: EditAction e s => Region -> e s
+ HsDev.Tools.Refact: data Refact
+ HsDev.Tools.Refact: fromPosition :: Position -> Point
+ HsDev.Tools.Refact: fromRegion :: Region -> Region
+ HsDev.Tools.Refact: instance Data.Aeson.Types.FromJSON.FromJSON HsDev.Tools.Refact.Refact
+ HsDev.Tools.Refact: instance Data.Aeson.Types.ToJSON.ToJSON HsDev.Tools.Refact.Refact
+ HsDev.Tools.Refact: instance Data.Text.Region.Types.Regioned HsDev.Tools.Refact.Refact
+ HsDev.Tools.Refact: instance GHC.Classes.Eq HsDev.Tools.Refact.Refact
+ HsDev.Tools.Refact: instance GHC.Show.Show HsDev.Tools.Refact.Refact
+ HsDev.Tools.Refact: paste :: EditAction e s => Point -> s -> e s
+ HsDev.Tools.Refact: refact :: [Refact] -> Text -> Text
+ HsDev.Tools.Refact: refactAction :: Lens' Refact (Replace Text)
+ HsDev.Tools.Refact: refactMessage :: Lens' Refact Text
+ HsDev.Tools.Refact: replace :: EditAction e s => Region -> s -> e s
+ HsDev.Tools.Refact: update :: Regioned a => [Refact] -> [a] -> [a]
+ HsDev.Types: SQLiteError :: String -> HsDevError
+ HsDev.Types: UnhandledError :: String -> HsDevError
+ HsDev.Util: fromJSON' :: FromJSON a => Value -> Maybe a
+ HsDev.Util: logAll :: (MonadLog m, MonadCatch m) => m () -> m ()
+ HsDev.Util: parseDT :: (Monad m, Text a) => String -> String -> m a
+ System.Directory.Paths: dirExists :: Path -> IO Bool
+ System.Directory.Paths: fileExists :: Path -> IO Bool
+ System.Directory.Paths: fromFilePath :: FilePath -> Path
+ System.Directory.Paths: instance System.Directory.Paths.Paths System.Directory.Paths.Path
+ System.Directory.Paths: isParent :: Path -> Path -> Bool
+ System.Directory.Paths: joinPaths :: [Path] -> Path
+ System.Directory.Paths: normPath :: Path -> Path
+ System.Directory.Paths: path :: Lens' Path FilePath
+ System.Directory.Paths: relPathTo :: Path -> Path -> Path
+ System.Directory.Paths: splitPaths :: Path -> [Path]
+ System.Directory.Paths: subPath :: Path -> Path -> Path
+ System.Directory.Paths: takeDir :: Path -> Path
+ System.Directory.Paths: type Path = Text
+ System.Directory.Watcher: eventGroup :: Watcher a -> NominalDiffTime -> ([(a, Event)] -> IO ()) -> IO ()
+ System.Directory.Watcher: instance GHC.Classes.Eq System.Directory.Watcher.Event
+ System.Directory.Watcher: instance GHC.Classes.Ord System.Directory.Watcher.Event
+ System.Directory.Watcher: instance GHC.Show.Show System.Directory.Watcher.Event
+ System.Directory.Watcher: onEvents :: Watcher a -> NominalDiffTime -> ([(a, Event)] -> IO ()) -> IO ()
+ System.Directory.Watcher: onEvents_ :: Watcher a -> ([(a, Event)] -> IO ()) -> IO ()
- Control.Concurrent.Worker: Worker :: Chan (Async (), m ()) -> (forall a. m a -> m a) -> MVar (Async ()) -> IO () -> Worker m
+ Control.Concurrent.Worker: Worker :: Chan (Async (), m ()) -> MVar (Async ()) -> Worker m
- Control.Concurrent.Worker: startWorker :: MonadIO m => (m () -> IO ()) -> (m () -> m ()) -> (forall a. m a -> m a) -> IO (Worker m)
+ Control.Concurrent.Worker: startWorker :: MonadIO m => (m () -> IO ()) -> (m () -> m ()) -> (m () -> m ()) -> IO (Worker m)
- Data.Deps: flatten :: Ord a => Deps a -> Deps a
+ Data.Deps: flatten :: Ord a => Deps a -> Either (DepsError a) (Deps a)
- Data.Help: brief :: Help a => a -> String
+ Data.Help: brief :: Help a => a -> Text
- Data.Help: detailed :: Help a => a -> [String]
+ Data.Help: detailed :: Help a => a -> [Text]
- Data.Help: help :: Help a => a -> [String]
+ Data.Help: help :: Help a => a -> [Text]
- Data.Help: indent :: String -> String
+ Data.Help: indent :: Text -> Text
- Data.Help: indentHelp :: [String] -> [String]
+ Data.Help: indentHelp :: [Text] -> [Text]
- Data.Help: indented :: Help a => a -> [String]
+ Data.Help: indented :: Help a => a -> [Text]
- HsDev.Database.Update: UpdateM :: ReaderT UpdateOptions (WriterT [ModuleLocation] (ClientM m)) a -> UpdateM m a
+ HsDev.Database.Update: UpdateM :: ReaderT UpdateState (WriterT [ModuleLocation] (ClientM m)) a -> UpdateM m a
- HsDev.Database.Update: [runUpdateM] :: UpdateM m a -> ReaderT UpdateOptions (WriterT [ModuleLocation] (ClientM m)) a
+ HsDev.Database.Update: [runUpdateM] :: UpdateM m a -> ReaderT UpdateState (WriterT [ModuleLocation] (ClientM m)) a
- HsDev.Database.Update: inferModTypes :: UpdateMonad m => [InspectedModule] -> m ()
+ HsDev.Database.Update: inferModTypes :: UpdateMonad m => [Module] -> m ()
- HsDev.Database.Update: runTasks :: UpdateMonad m => [m ()] -> m ()
+ HsDev.Database.Update: runTasks :: UpdateMonad m => [m a] -> m [a]
- HsDev.Database.Update: scan :: UpdateMonad m => (FilePath -> ExceptT String IO Structured) -> (Database -> Database) -> [ModuleToScan] -> [String] -> ([ModuleToScan] -> m ()) -> m ()
+ HsDev.Database.Update: scan :: UpdateMonad m => m [(Only Int) :. (ModuleLocation :. Inspection)] -> [ModuleToScan] -> [String] -> ([ModuleToScan] -> m ()) -> m ()
- HsDev.Database.Update: scanDirectory :: UpdateMonad m => [String] -> FilePath -> m ()
+ HsDev.Database.Update: scanDirectory :: UpdateMonad m => [String] -> Path -> m ()
- HsDev.Database.Update: scanDocs :: UpdateMonad m => [InspectedModule] -> m ()
+ HsDev.Database.Update: scanDocs :: UpdateMonad m => [Module] -> m ()
- HsDev.Database.Update: scanFile :: UpdateMonad m => [String] -> FilePath -> m ()
+ HsDev.Database.Update: scanFile :: UpdateMonad m => [String] -> Path -> m ()
- HsDev.Database.Update: scanFileContents :: UpdateMonad m => [String] -> FilePath -> Maybe String -> m ()
+ HsDev.Database.Update: scanFileContents :: UpdateMonad m => [String] -> Path -> Maybe Text -> m ()
- HsDev.Database.Update: scanProject :: UpdateMonad m => [String] -> FilePath -> m ()
+ HsDev.Database.Update: scanProject :: UpdateMonad m => [String] -> Path -> m ()
- HsDev.Database.Update: scanProjectFile :: UpdateMonad m => [String] -> FilePath -> m Project
+ HsDev.Database.Update: scanProjectFile :: UpdateMonad m => [String] -> Path -> m Project
- HsDev.Database.Update: scanProjectStack :: UpdateMonad m => [String] -> FilePath -> m ()
+ HsDev.Database.Update: scanProjectStack :: UpdateMonad m => [String] -> Path -> m ()
- HsDev.Database.Update: updater :: UpdateMonad m => Database -> m ()
+ HsDev.Database.Update: updater :: UpdateMonad m => [ModuleLocation] -> m ()
- HsDev.Database.Update.Types: UpdateM :: ReaderT UpdateOptions (WriterT [ModuleLocation] (ClientM m)) a -> UpdateM m a
+ HsDev.Database.Update.Types: UpdateM :: ReaderT UpdateState (WriterT [ModuleLocation] (ClientM m)) a -> UpdateM m a
- HsDev.Database.Update.Types: [runUpdateM] :: UpdateM m a -> ReaderT UpdateOptions (WriterT [ModuleLocation] (ClientM m)) a
+ HsDev.Database.Update.Types: [runUpdateM] :: UpdateM m a -> ReaderT UpdateState (WriterT [ModuleLocation] (ClientM m)) a
- HsDev.Database.Update.Types: type UpdateMonad m = (CommandMonad m, MonadReader UpdateOptions m, MonadWriter [ModuleLocation] m)
+ HsDev.Database.Update.Types: type UpdateMonad m = (CommandMonad m, MonadReader UpdateState m, MonadWriter [ModuleLocation] m)
- HsDev.Inspect: contentsInspection :: String -> [String] -> ExceptT String IO Inspection
+ HsDev.Inspect: contentsInspection :: Text -> [String] -> ExceptT String IO Inspection
- HsDev.Inspect: fileInspection :: FilePath -> [String] -> IO Inspection
+ HsDev.Inspect: fileInspection :: Path -> [String] -> IO Inspection
- HsDev.Inspect: inspectContents :: String -> [(String, String)] -> [String] -> String -> ExceptT String IO InspectedModule
+ HsDev.Inspect: inspectContents :: Text -> [(String, String)] -> [String] -> Text -> ExceptT String IO InspectedModule
- HsDev.Inspect: inspectDocs :: [String] -> Module -> IO Module
+ HsDev.Inspect: inspectDocs :: [String] -> Module -> GhcM Module
- HsDev.Inspect: inspectDocsGhc :: [String] -> Module -> Ghc Module
+ HsDev.Inspect: inspectDocsGhc :: [String] -> Module -> GhcM Module
- HsDev.Inspect: inspectFile :: [(String, String)] -> [String] -> FilePath -> Maybe String -> IO InspectedModule
+ HsDev.Inspect: inspectFile :: [(String, String)] -> [String] -> Path -> Maybe Project -> Maybe Text -> IO InspectedModule
- HsDev.Inspect: preprocess :: [(String, String)] -> FilePath -> String -> IO String
+ HsDev.Inspect: preprocess :: [(String, String)] -> Path -> Text -> IO Text
- HsDev.Inspect: preprocess_ :: [(String, String)] -> [String] -> FilePath -> String -> IO String
+ HsDev.Inspect: preprocess_ :: [(String, String)] -> [String] -> Path -> Text -> IO Text
- HsDev.Inspect: projectDirs :: Project -> IO [Extensions FilePath]
+ HsDev.Inspect: projectDirs :: Project -> IO [Extensions Path]
- HsDev.Inspect: projectSources :: Project -> IO [Extensions FilePath]
+ HsDev.Inspect: projectSources :: Project -> IO [Extensions Path]
- HsDev.Project: fileTarget :: Project -> FilePath -> Maybe Info
+ HsDev.Project: fileTarget :: Project -> Path -> Maybe Info
- HsDev.Project: fileTargets :: Project -> FilePath -> [Info]
+ HsDev.Project: fileTargets :: Project -> Path -> [Info]
- HsDev.Project: findSourceDir :: Project -> FilePath -> Maybe (Extensions FilePath)
+ HsDev.Project: findSourceDir :: Project -> Path -> Maybe (Extensions Path)
- HsDev.Project: infoSourceDirsDef :: Simple Lens Info [FilePath]
+ HsDev.Project: infoSourceDirsDef :: Lens' Info [Path]
- HsDev.Project: sourceDirs :: ProjectDescription -> [Extensions FilePath]
+ HsDev.Project: sourceDirs :: ProjectDescription -> [Extensions Path]
- HsDev.Project.Compat: componentName :: String -> String
+ HsDev.Project.Compat: componentName :: UnqualComponentName -> Text
- HsDev.Project.Types: Executable :: String -> FilePath -> Info -> Executable
+ HsDev.Project.Types: Executable :: Text -> Path -> Info -> Executable
- HsDev.Project.Types: Extensions :: [Extension] -> [String] -> a -> Extensions a
+ HsDev.Project.Types: Extensions :: [Extension] -> [Text] -> a -> Extensions a
- HsDev.Project.Types: Info :: [String] -> Maybe Language -> [Extension] -> [String] -> [FilePath] -> Info
+ HsDev.Project.Types: Info :: [Text] -> Maybe Language -> [Extension] -> [Text] -> [Path] -> [[Text]] -> Info
- HsDev.Project.Types: Library :: [[String]] -> Info -> Library
+ HsDev.Project.Types: Library :: [[Text]] -> Info -> Library
- HsDev.Project.Types: Project :: String -> FilePath -> FilePath -> Maybe ProjectDescription -> Project
+ HsDev.Project.Types: Project :: Text -> Path -> Path -> Maybe ProjectDescription -> Project
- HsDev.Project.Types: ProjectDescription :: String -> Maybe Library -> [Executable] -> [Test] -> ProjectDescription
+ HsDev.Project.Types: ProjectDescription :: Text -> Maybe Library -> [Executable] -> [Test] -> ProjectDescription
- HsDev.Project.Types: Test :: String -> Bool -> Info -> Test
+ HsDev.Project.Types: Test :: Text -> Bool -> Maybe Path -> Info -> Test
- HsDev.Project.Types: [_executableName] :: Executable -> String
+ HsDev.Project.Types: [_executableName] :: Executable -> Text
- HsDev.Project.Types: [_executablePath] :: Executable -> FilePath
+ HsDev.Project.Types: [_executablePath] :: Executable -> Path
- HsDev.Project.Types: [_ghcOptions] :: Extensions a -> [String]
+ HsDev.Project.Types: [_ghcOptions] :: Extensions a -> [Text]
- HsDev.Project.Types: [_infoDepends] :: Info -> [String]
+ HsDev.Project.Types: [_infoDepends] :: Info -> [Text]
- HsDev.Project.Types: [_infoGHCOptions] :: Info -> [String]
+ HsDev.Project.Types: [_infoGHCOptions] :: Info -> [Text]
- HsDev.Project.Types: [_infoSourceDirs] :: Info -> [FilePath]
+ HsDev.Project.Types: [_infoSourceDirs] :: Info -> [Path]
- HsDev.Project.Types: [_libraryModules] :: Library -> [[String]]
+ HsDev.Project.Types: [_libraryModules] :: Library -> [[Text]]
- HsDev.Project.Types: [_projectCabal] :: Project -> FilePath
+ HsDev.Project.Types: [_projectCabal] :: Project -> Path
- HsDev.Project.Types: [_projectName] :: Project -> String
+ HsDev.Project.Types: [_projectName] :: Project -> Text
- HsDev.Project.Types: [_projectPath] :: Project -> FilePath
+ HsDev.Project.Types: [_projectPath] :: Project -> Path
- HsDev.Project.Types: [_projectVersion] :: ProjectDescription -> String
+ HsDev.Project.Types: [_projectVersion] :: ProjectDescription -> Text
- HsDev.Project.Types: [_testName] :: Test -> String
+ HsDev.Project.Types: [_testName] :: Test -> Text
- HsDev.Project.Types: buildInfo :: Target a => a -> Info
+ HsDev.Project.Types: buildInfo :: Target a => Lens' a Info
- HsDev.Project.Types: entity :: forall a_a14TT a_a16Va. Lens (Extensions a_a14TT) (Extensions a_a16Va) a_a14TT a_a16Va
+ HsDev.Project.Types: entity :: forall a_a1hkw a_a1iMr. Lens (Extensions a_a1hkw) (Extensions a_a1iMr) a_a1hkw a_a1iMr
- HsDev.Project.Types: executableName :: Lens' Executable String
+ HsDev.Project.Types: executableName :: Lens' Executable Text
- HsDev.Project.Types: executablePath :: Lens' Executable FilePath
+ HsDev.Project.Types: executablePath :: Lens' Executable Path
- HsDev.Project.Types: extensions :: forall a_a14TT. Lens' (Extensions a_a14TT) [Extension]
+ HsDev.Project.Types: extensions :: forall a_a1hkw. Lens' (Extensions a_a1hkw) [Extension]
- HsDev.Project.Types: ghcOptions :: forall a_a14TT. Lens' (Extensions a_a14TT) [String]
+ HsDev.Project.Types: ghcOptions :: forall a_a1hkw. Lens' (Extensions a_a1hkw) [Text]
- HsDev.Project.Types: infoDepends :: Lens' Info [String]
+ HsDev.Project.Types: infoDepends :: Lens' Info [Text]
- HsDev.Project.Types: infoGHCOptions :: Lens' Info [String]
+ HsDev.Project.Types: infoGHCOptions :: Lens' Info [Text]
- HsDev.Project.Types: infoSourceDirs :: Lens' Info [FilePath]
+ HsDev.Project.Types: infoSourceDirs :: Lens' Info [Path]
- HsDev.Project.Types: libraryModules :: Lens' Library [[String]]
+ HsDev.Project.Types: libraryModules :: Lens' Library [[Text]]
- HsDev.Project.Types: projectCabal :: Lens' Project FilePath
+ HsDev.Project.Types: projectCabal :: Lens' Project Path
- HsDev.Project.Types: projectName :: Lens' Project String
+ HsDev.Project.Types: projectName :: Lens' Project Text
- HsDev.Project.Types: projectPath :: Lens' Project FilePath
+ HsDev.Project.Types: projectPath :: Lens' Project Path
- HsDev.Project.Types: projectVersion :: Lens' ProjectDescription String
+ HsDev.Project.Types: projectVersion :: Lens' ProjectDescription Text
- HsDev.Project.Types: testName :: Lens' Test String
+ HsDev.Project.Types: testName :: Lens' Test Text
- HsDev.Sandbox: Sandbox :: SandboxType -> FilePath -> Sandbox
+ HsDev.Sandbox: Sandbox :: SandboxType -> Path -> Sandbox
- HsDev.Sandbox: [_sandbox] :: Sandbox -> FilePath
+ HsDev.Sandbox: [_sandbox] :: Sandbox -> Path
- HsDev.Sandbox: cabalSandboxLib :: MonadLog m => m FilePath
+ HsDev.Sandbox: cabalSandboxLib :: GhcM FilePath
- HsDev.Sandbox: cabalSandboxPackageDb :: MonadLog m => m FilePath
+ HsDev.Sandbox: cabalSandboxPackageDb :: GhcM FilePath
- HsDev.Sandbox: findSandbox :: FilePath -> IO (Maybe Sandbox)
+ HsDev.Sandbox: findSandbox :: Path -> IO (Maybe Sandbox)
- HsDev.Sandbox: getModuleOpts :: MonadLog m => [String] -> Module -> m [String]
+ HsDev.Sandbox: getModuleOpts :: [String] -> Module -> GhcM [String]
- HsDev.Sandbox: guessSandboxType :: FilePath -> Maybe SandboxType
+ HsDev.Sandbox: guessSandboxType :: Path -> Maybe SandboxType
- HsDev.Sandbox: isSandbox :: FilePath -> Bool
+ HsDev.Sandbox: isSandbox :: Path -> Bool
- HsDev.Sandbox: projectSandbox :: FilePath -> IO (Maybe Sandbox)
+ HsDev.Sandbox: projectSandbox :: Path -> IO (Maybe Sandbox)
- HsDev.Sandbox: restorePackageDbStack :: MonadLog m => PackageDb -> m PackageDbStack
+ HsDev.Sandbox: restorePackageDbStack :: PackageDb -> GhcM PackageDbStack
- HsDev.Sandbox: sandbox :: Lens' Sandbox FilePath
+ HsDev.Sandbox: sandbox :: Lens' Sandbox Path
- HsDev.Sandbox: sandboxFromPath :: FilePath -> Maybe Sandbox
+ HsDev.Sandbox: sandboxFromPath :: Path -> Maybe Sandbox
- HsDev.Sandbox: sandboxPackageDbStack :: MonadLog m => Sandbox -> m PackageDbStack
+ HsDev.Sandbox: sandboxPackageDbStack :: Sandbox -> GhcM PackageDbStack
- HsDev.Sandbox: searchPackageDbStack :: MonadLog m => FilePath -> m PackageDbStack
+ HsDev.Sandbox: searchPackageDbStack :: Path -> GhcM PackageDbStack
- HsDev.Sandbox: searchSandbox :: FilePath -> IO (Maybe Sandbox)
+ HsDev.Sandbox: searchSandbox :: Path -> IO (Maybe Sandbox)
- HsDev.Scan: changedModules :: Database -> [String] -> [ModuleToScan] -> IO [ModuleToScan]
+ HsDev.Scan: changedModules :: SessionMonad m => Map ModuleLocation Inspection -> [String] -> [ModuleToScan] -> m [ModuleToScan]
- HsDev.Scan: scanModify :: CommandMonad m => ([String] -> PackageDbStack -> Module -> m Module) -> InspectedModule -> m InspectedModule
+ HsDev.Scan: scanModify :: CommandMonad m => ([String] -> Module -> m Module) -> InspectedModule -> m InspectedModule
- HsDev.Scan: scanProjectFile :: CommandMonad m => [String] -> FilePath -> m Project
+ HsDev.Scan: scanProjectFile :: CommandMonad m => [String] -> Path -> m Project
- HsDev.Scan: type ModuleToScan = (ModuleLocation, [CompileFlag], Maybe String)
+ HsDev.Scan: type ModuleToScan = (ModuleLocation, [CompileFlag], Maybe Text)
- HsDev.Scan: upToDate :: [String] -> InspectedModule -> IO Bool
+ HsDev.Scan: upToDate :: SessionMonad m => ModuleLocation -> [String] -> Inspection -> m Bool
- HsDev.Scan.Browse: browseModules :: MonadLog m => [String] -> PackageDbStack -> [ModuleLocation] -> m [InspectedModule]
+ HsDev.Scan.Browse: browseModules :: [String] -> PackageDbStack -> [ModuleLocation] -> GhcM [InspectedModule]
- HsDev.Scan.Browse: browsePackages :: MonadLog m => [String] -> PackageDbStack -> m [PackageConfig]
+ HsDev.Scan.Browse: browsePackages :: [String] -> PackageDbStack -> GhcM [PackageConfig]
- HsDev.Scan.Browse: browsePackagesDeps :: MonadLog m => [String] -> PackageDbStack -> m (Deps PackageConfig)
+ HsDev.Scan.Browse: browsePackagesDeps :: [String] -> PackageDbStack -> GhcM (Deps PackageConfig)
- HsDev.Scan.Browse: ghcModuleLocation :: PackageDb -> PackageConfig -> Module -> ModuleLocation
+ HsDev.Scan.Browse: ghcModuleLocation :: PackageConfig -> Module -> ModuleLocation
- HsDev.Scan.Browse: listModules :: MonadLog m => [String] -> PackageDbStack -> m [ModuleLocation]
+ HsDev.Scan.Browse: listModules :: [String] -> PackageDbStack -> [ModulePackage] -> GhcM [ModuleLocation]
- HsDev.Server.Base: inServer :: Server -> CommandOptions -> Command -> IO Result
+ HsDev.Server.Base: inServer :: Server -> ServerM IO a -> IO a
- HsDev.Server.Commands: ServerOpts :: ConnectionPort -> Int -> Maybe FilePath -> String -> Maybe FilePath -> Bool -> Bool -> ServerOpts
+ HsDev.Server.Commands: ServerOpts :: ConnectionPort -> Int -> Maybe FilePath -> String -> Maybe FilePath -> Bool -> ServerOpts
- HsDev.Server.Message: message :: forall a_a1CSm a_a1D0r. Lens (Message a_a1CSm) (Message a_a1D0r) a_a1CSm a_a1D0r
+ HsDev.Server.Message: message :: forall a_a1IqJ a_a1Iw8. Lens (Message a_a1IqJ) (Message a_a1Iw8) a_a1IqJ a_a1Iw8
- HsDev.Server.Message: messageId :: forall a_a1CSm. Lens' (Message a_a1CSm) (Maybe String)
+ HsDev.Server.Message: messageId :: forall a_a1IqJ. Lens' (Message a_a1IqJ) (Maybe String)
- HsDev.Server.Types: AutoFix :: AutoFixCommand -> Command
+ HsDev.Server.Types: AutoFix :: [Note OutputMessage] -> Command
- HsDev.Server.Types: CabalList :: [String] -> Command
+ HsDev.Server.Types: CabalList :: [Text] -> Command
- HsDev.Server.Types: Check :: [FileSource] -> [String] -> Command
+ HsDev.Server.Types: Check :: [FileSource] -> [String] -> Bool -> Command
- HsDev.Server.Types: CheckLint :: [FileSource] -> [String] -> Command
+ HsDev.Server.Types: CheckLint :: [FileSource] -> [String] -> Bool -> Command
- HsDev.Server.Types: Complete :: String -> Bool -> FilePath -> Command
+ HsDev.Server.Types: Complete :: Text -> Bool -> Path -> Command
- HsDev.Server.Types: FileSource :: FilePath -> Maybe String -> FileSource
+ HsDev.Server.Types: FileSource :: Path -> Maybe Text -> FileSource
- HsDev.Server.Types: InferTypes :: [FilePath] -> [FilePath] -> [String] -> Command
+ HsDev.Server.Types: InferTypes :: [Path] -> [Path] -> Command
- HsDev.Server.Types: InfoModule :: SearchQuery -> [TargetFilter] -> Command
+ HsDev.Server.Types: InfoModule :: SearchQuery -> [TargetFilter] -> Bool -> Bool -> Command
- HsDev.Server.Types: InfoProject :: (Either String FilePath) -> Command
+ HsDev.Server.Types: InfoProject :: (Either Text Path) -> Command
- HsDev.Server.Types: InfoSandbox :: FilePath -> Command
+ HsDev.Server.Types: InfoSandbox :: Path -> Command
- HsDev.Server.Types: InfoSymbol :: SearchQuery -> [TargetFilter] -> Bool -> Command
+ HsDev.Server.Types: InfoSymbol :: SearchQuery -> [TargetFilter] -> Bool -> Bool -> Command
- HsDev.Server.Types: Lookup :: String -> FilePath -> Command
+ HsDev.Server.Types: Lookup :: Text -> Path -> Command
- HsDev.Server.Types: RefineDocs :: [FilePath] -> [FilePath] -> [String] -> Command
+ HsDev.Server.Types: RefineDocs :: [Path] -> [Path] -> Command
- HsDev.Server.Types: Remove :: [FilePath] -> Bool -> [FilePath] -> [FilePath] -> Command
+ HsDev.Server.Types: Remove :: [Path] -> Bool -> [Path] -> [Path] -> Command
- HsDev.Server.Types: ResolveScope :: SearchQuery -> Bool -> FilePath -> Command
+ HsDev.Server.Types: ResolveScope :: SearchQuery -> Path -> Command
- HsDev.Server.Types: ResolveScopeModules :: SearchQuery -> FilePath -> Command
+ HsDev.Server.Types: ResolveScopeModules :: SearchQuery -> Path -> Command
- HsDev.Server.Types: Scan :: [FilePath] -> Bool -> [FilePath] -> [FileSource] -> [FilePath] -> [String] -> Bool -> Bool -> Command
+ HsDev.Server.Types: Scan :: [Path] -> Bool -> [Path] -> [FileSource] -> [Path] -> [String] -> Bool -> Bool -> Command
- HsDev.Server.Types: SearchQuery :: String -> SearchType -> SearchQuery
+ HsDev.Server.Types: SearchQuery :: Text -> SearchType -> SearchQuery
- HsDev.Server.Types: ServerOpts :: ConnectionPort -> Int -> Maybe FilePath -> String -> Maybe FilePath -> Bool -> Bool -> ServerOpts
+ HsDev.Server.Types: ServerOpts :: ConnectionPort -> Int -> Maybe FilePath -> String -> Maybe FilePath -> Bool -> ServerOpts
- HsDev.Server.Types: Session :: Async Database -> (Database -> ServerM IO ()) -> ((FilePath -> ExceptT String IO Structured) -> ServerM IO (Maybe Database)) -> SessionLog -> Watcher -> GhcWorker -> IO () -> IO () -> [(String, String)] -> Session
+ 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: SessionLog :: Log -> IO [String] -> IO () -> SessionLog
+ HsDev.Server.Types: SessionLog :: Log -> IO [Message] -> IO () -> SessionLog
- HsDev.Server.Types: TargetFile :: FilePath -> TargetFilter
+ HsDev.Server.Types: TargetFile :: Path -> TargetFilter
- HsDev.Server.Types: TargetModule :: String -> TargetFilter
+ HsDev.Server.Types: TargetModule :: Text -> TargetFilter
- HsDev.Server.Types: TargetPackage :: String -> TargetFilter
+ HsDev.Server.Types: TargetPackage :: Text -> TargetFilter
- HsDev.Server.Types: TargetProject :: String -> TargetFilter
+ HsDev.Server.Types: TargetProject :: Text -> TargetFilter
- HsDev.Server.Types: Types :: [FileSource] -> [String] -> Command
+ HsDev.Server.Types: Types :: [FileSource] -> [String] -> Bool -> Command
- HsDev.Server.Types: Whois :: String -> FilePath -> Command
+ HsDev.Server.Types: Whois :: Text -> Path -> Command
- HsDev.Server.Types: [cabalListPackages] :: Command -> [String]
+ HsDev.Server.Types: [cabalListPackages] :: Command -> [Text]
- HsDev.Server.Types: [docsFiles] :: Command -> [FilePath]
+ HsDev.Server.Types: [docsFiles] :: Command -> [Path]
- HsDev.Server.Types: [docsProjects] :: Command -> [FilePath]
+ HsDev.Server.Types: [docsProjects] :: Command -> [Path]
- HsDev.Server.Types: [fileContents] :: FileSource -> Maybe String
+ HsDev.Server.Types: [fileContents] :: FileSource -> Maybe Text
- HsDev.Server.Types: [fileSource] :: FileSource -> FilePath
+ HsDev.Server.Types: [fileSource] :: FileSource -> Path
- HsDev.Server.Types: [inferFiles] :: Command -> [FilePath]
+ HsDev.Server.Types: [inferFiles] :: Command -> [Path]
- HsDev.Server.Types: [inferProjects] :: Command -> [FilePath]
+ HsDev.Server.Types: [inferProjects] :: Command -> [Path]
- HsDev.Server.Types: [removeFiles] :: Command -> [FilePath]
+ HsDev.Server.Types: [removeFiles] :: Command -> [Path]
- HsDev.Server.Types: [removeProjects] :: Command -> [FilePath]
+ HsDev.Server.Types: [removeProjects] :: Command -> [Path]
- HsDev.Server.Types: [removeSandboxes] :: Command -> [FilePath]
+ HsDev.Server.Types: [removeSandboxes] :: Command -> [Path]
- HsDev.Server.Types: [scanPaths] :: Command -> [FilePath]
+ HsDev.Server.Types: [scanPaths] :: Command -> [Path]
- HsDev.Server.Types: [scanProjects] :: Command -> [FilePath]
+ HsDev.Server.Types: [scanProjects] :: Command -> [Path]
- HsDev.Server.Types: [scanSandboxes] :: Command -> [FilePath]
+ HsDev.Server.Types: [scanSandboxes] :: Command -> [Path]
- HsDev.Server.Types: [sessionListenLog] :: SessionLog -> IO [String]
+ HsDev.Server.Types: [sessionListenLog] :: SessionLog -> IO [Message]
- HsDev.Server.Types: serverListen :: SessionMonad m => m [String]
+ HsDev.Server.Types: serverListen :: SessionMonad m => m [Message]
- HsDev.Stack: MaybeT :: m (Maybe a) -> MaybeT a
+ HsDev.Stack: MaybeT :: m Maybe a -> MaybeT a
- HsDev.Stack: [runMaybeT] :: MaybeT a -> m (Maybe a)
+ HsDev.Stack: [runMaybeT] :: MaybeT a -> m Maybe a
- HsDev.Stack: build :: MonadLog m => [String] -> Maybe FilePath -> m ()
+ HsDev.Stack: build :: [String] -> Maybe FilePath -> GhcM ()
- HsDev.Stack: buildDeps :: MonadLog m => Maybe FilePath -> m ()
+ HsDev.Stack: buildDeps :: Maybe FilePath -> GhcM ()
- HsDev.Stack: configure :: MonadLog m => Maybe FilePath -> m ()
+ HsDev.Stack: configure :: Maybe FilePath -> GhcM ()
- HsDev.Stack: getStackEnv :: Paths -> Maybe StackEnv
+ HsDev.Stack: getStackEnv :: PathsConf -> Maybe StackEnv
- HsDev.Stack: path :: MonadLog m => Maybe FilePath -> m Paths
+ HsDev.Stack: path :: Maybe FilePath -> GhcM PathsConf
- HsDev.Stack: pathOf :: String -> Lens' Paths (Maybe FilePath)
+ HsDev.Stack: pathOf :: String -> Lens' PathsConf (Maybe FilePath)
- HsDev.Stack: projectEnv :: MonadLog m => FilePath -> m StackEnv
+ HsDev.Stack: projectEnv :: FilePath -> GhcM StackEnv
- HsDev.Stack: stack :: MonadLog m => [String] -> m String
+ HsDev.Stack: stack :: [String] -> GhcM String
- HsDev.Stack: stackCompiler :: MonadLog m => m String
+ HsDev.Stack: stackCompiler :: GhcM String
- HsDev.Symbols: locateSourceDir :: FilePath -> IO (Maybe (Extensions FilePath))
+ HsDev.Symbols: locateSourceDir :: FilePath -> IO (Maybe (Extensions Path))
- HsDev.Symbols.Documented: brief :: Documented a => a -> String
+ HsDev.Symbols.Documented: brief :: Documented a => a -> Text
- HsDev.Symbols.Documented: class Symbol a => Documented a where detailed = unlines . defaultDetailed
+ HsDev.Symbols.Documented: class Documented a
- HsDev.Symbols.Documented: defaultDetailed :: Documented a => a -> [String]
+ HsDev.Symbols.Documented: defaultDetailed :: (Sourced a, Documented a) => a -> [Text]
- HsDev.Symbols.Documented: detailed :: Documented a => a -> String
+ HsDev.Symbols.Documented: detailed :: (Documented a, Sourced a) => a -> Text
- HsDev.Symbols.Location: FileModule :: FilePath -> Maybe Project -> ModuleLocation
+ HsDev.Symbols.Location: FileModule :: Path -> Maybe Project -> ModuleLocation
- HsDev.Symbols.Location: InstalledModule :: PackageDb -> Maybe ModulePackage -> String -> ModuleLocation
+ HsDev.Symbols.Location: InstalledModule :: [Path] -> ModulePackage -> Text -> ModuleLocation
- HsDev.Symbols.Location: ModulePackage :: String -> String -> ModulePackage
+ HsDev.Symbols.Location: ModulePackage :: Text -> Text -> ModulePackage
- HsDev.Symbols.Location: [_moduleFile] :: ModuleLocation -> FilePath
+ HsDev.Symbols.Location: [_moduleFile] :: ModuleLocation -> Path
- HsDev.Symbols.Location: [_modulePackage] :: ModuleLocation -> Maybe ModulePackage
+ HsDev.Symbols.Location: [_modulePackage] :: ModuleLocation -> ModulePackage
- HsDev.Symbols.Location: [_packageName] :: ModulePackage -> String
+ HsDev.Symbols.Location: [_packageName] :: ModulePackage -> Text
- HsDev.Symbols.Location: [_packageVersion] :: ModulePackage -> String
+ HsDev.Symbols.Location: [_packageVersion] :: ModulePackage -> Text
- HsDev.Symbols.Location: calcTabs :: RecalcTabs a => String -> Int -> a -> a
+ HsDev.Symbols.Location: calcTabs :: RecalcTabs a => Text -> Int -> a -> a
- HsDev.Symbols.Location: locationId :: ModuleLocation -> String
+ HsDev.Symbols.Location: locationId :: ModuleLocation -> Text
- HsDev.Symbols.Location: moduleFile :: Traversal' ModuleLocation FilePath
+ HsDev.Symbols.Location: moduleFile :: Traversal' ModuleLocation Path
- HsDev.Symbols.Location: modulePackage :: Traversal' ModuleLocation (Maybe ModulePackage)
+ HsDev.Symbols.Location: modulePackage :: Traversal' ModuleLocation ModulePackage
- HsDev.Symbols.Location: packageName :: Lens' ModulePackage String
+ HsDev.Symbols.Location: packageName :: Lens' ModulePackage Text
- HsDev.Symbols.Location: packageVersion :: Lens' ModulePackage String
+ HsDev.Symbols.Location: packageVersion :: Lens' ModulePackage Text
- HsDev.Symbols.Location: recalcTabs :: RecalcTabs a => String -> Int -> a -> a
+ HsDev.Symbols.Location: recalcTabs :: RecalcTabs a => Text -> Int -> a -> a
- HsDev.Symbols.Location: regionStr :: Region -> String -> String
+ HsDev.Symbols.Location: regionStr :: Region -> Text -> Text
- HsDev.Symbols.Location: sourceModuleRoot :: Text -> FilePath -> FilePath
+ HsDev.Symbols.Location: sourceModuleRoot :: Text -> Path -> Path
- HsDev.Symbols.Types: Class :: TypeInfo -> DeclarationInfo
+ HsDev.Symbols.Types: Class :: [Text] -> [Text] -> SymbolInfo
- HsDev.Symbols.Types: Data :: TypeInfo -> DeclarationInfo
+ HsDev.Symbols.Types: Data :: [Text] -> [Text] -> SymbolInfo
- HsDev.Symbols.Types: Function :: Maybe Text -> [Declaration] -> Maybe Text -> DeclarationInfo
+ HsDev.Symbols.Types: Function :: Maybe Text -> SymbolInfo
- HsDev.Symbols.Types: Inspected :: Inspection -> i -> Set t -> Either HsDevError a -> Inspected i t a
+ HsDev.Symbols.Types: Inspected :: Inspection -> k -> Set t -> Either HsDevError a -> Inspected k t a
- HsDev.Symbols.Types: InspectionAt :: POSIXTime -> [String] -> Inspection
+ HsDev.Symbols.Types: InspectionAt :: POSIXTime -> [Text] -> Inspection
- HsDev.Symbols.Types: Module :: Text -> Maybe Text -> ModuleLocation -> Maybe [Export] -> [Import] -> [Declaration] -> Module
+ HsDev.Symbols.Types: Module :: ModuleId -> Maybe Text -> [Text] -> [Symbol] -> [Fixity] -> Map Name [Symbol] -> Maybe Parsed -> Module
- HsDev.Symbols.Types: NewType :: TypeInfo -> DeclarationInfo
+ HsDev.Symbols.Types: NewType :: [Text] -> [Text] -> SymbolInfo
- HsDev.Symbols.Types: Type :: TypeInfo -> DeclarationInfo
+ HsDev.Symbols.Types: Type :: [Text] -> [Text] -> SymbolInfo
- HsDev.Symbols.Types: [_functionType] :: DeclarationInfo -> Maybe Text
+ HsDev.Symbols.Types: [_functionType] :: SymbolInfo -> Maybe Text
- HsDev.Symbols.Types: [_inspectionOpts] :: Inspection -> [String]
+ HsDev.Symbols.Types: [_inspectionOpts] :: Inspection -> [Text]
- HsDev.Symbols.Types: [_inspectionResult] :: Inspected i t a -> Either HsDevError a
+ HsDev.Symbols.Types: [_inspectionResult] :: Inspected k t a -> Either HsDevError a
- HsDev.Symbols.Types: [_inspectionTags] :: Inspected i t a -> Set t
+ HsDev.Symbols.Types: [_inspectionTags] :: Inspected k t a -> Set t
- HsDev.Symbols.Types: [_inspection] :: Inspected i t a -> Inspection
+ HsDev.Symbols.Types: [_inspection] :: Inspected k t a -> Inspection
- HsDev.Symbols.Types: [_moduleExports] :: Module -> Maybe [Export]
+ HsDev.Symbols.Types: [_moduleExports] :: Module -> [Symbol]
- HsDev.Symbols.Types: [_moduleImports] :: Module -> [Import]
+ HsDev.Symbols.Types: [_moduleImports] :: Module -> [Text]
- HsDev.Symbols.Types: data Inspected i t a
+ HsDev.Symbols.Types: data Inspected k t a
- HsDev.Symbols.Types: functionType :: Traversal' DeclarationInfo (Maybe Text)
+ HsDev.Symbols.Types: functionType :: Traversal' SymbolInfo (Maybe Text)
- HsDev.Symbols.Types: inspection :: forall i_a1K3e t_a1K3f a_a1K3g. Lens' (Inspected i_a1K3e t_a1K3f a_a1K3g) Inspection
+ HsDev.Symbols.Types: inspection :: forall k_a22lU t_a22lV a_a22lW. Lens' (Inspected k_a22lU t_a22lV a_a22lW) Inspection
- HsDev.Symbols.Types: inspectionOpts :: Traversal' Inspection [String]
+ HsDev.Symbols.Types: inspectionOpts :: Traversal' Inspection [Text]
- HsDev.Symbols.Types: inspectionResult :: forall i_a1K3e t_a1K3f a_a1K3g a_a1Mdg. Lens (Inspected i_a1K3e t_a1K3f a_a1K3g) (Inspected i_a1K3e t_a1K3f a_a1Mdg) (Either HsDevError a_a1K3g) (Either HsDevError a_a1Mdg)
+ HsDev.Symbols.Types: inspectionResult :: forall k_a22lU t_a22lV a_a22lW a_a24tY. Lens (Inspected k_a22lU t_a22lV a_a22lW) (Inspected k_a22lU t_a22lV a_a24tY) (Either HsDevError a_a22lW) (Either HsDevError a_a24tY)
- HsDev.Symbols.Types: inspectionTags :: forall i_a1K3e t_a1K3f a_a1K3g t_a1Mdh. Lens (Inspected i_a1K3e t_a1K3f a_a1K3g) (Inspected i_a1K3e t_a1Mdh a_a1K3g) (Set t_a1K3f) (Set t_a1Mdh)
+ HsDev.Symbols.Types: inspectionTags :: forall k_a22lU t_a22lV a_a22lW t_a24tZ. Lens (Inspected k_a22lU t_a22lV a_a22lW) (Inspected k_a22lU t_a24tZ a_a22lW) (Set t_a22lV) (Set t_a24tZ)
- HsDev.Symbols.Types: moduleExports :: Lens' Module (Maybe [Export])
+ HsDev.Symbols.Types: moduleExports :: Lens' Module [Symbol]
- HsDev.Symbols.Types: moduleId :: Simple Lens Module ModuleId
+ HsDev.Symbols.Types: moduleId :: Lens' Module ModuleId
- HsDev.Symbols.Types: moduleImports :: Lens' Module [Import]
+ HsDev.Symbols.Types: moduleImports :: Lens' Module [Text]
- HsDev.Tools.AutoFix: corrections :: [Note OutputMessage] -> [Note Correction]
+ HsDev.Tools.AutoFix: corrections :: [Note OutputMessage] -> [Note Refact]
- HsDev.Tools.AutoFix: findCorrector :: Note OutputMessage -> Maybe (Note Correction)
+ HsDev.Tools.AutoFix: findCorrector :: Note OutputMessage -> Maybe (Note Refact)
- HsDev.Tools.AutoFix: match :: String -> ((Int -> Maybe String) -> Region -> Correction) -> CorrectorMatch
+ HsDev.Tools.AutoFix: match :: String -> ((Int -> Maybe Text) -> Region -> Refact) -> CorrectorMatch
- HsDev.Tools.AutoFix: type CorrectorMatch = Note OutputMessage -> Maybe (Note Correction)
+ HsDev.Tools.AutoFix: type CorrectorMatch = Note OutputMessage -> Maybe (Note Refact)
- HsDev.Tools.Base: at :: (Int -> Maybe String) -> Int -> String
+ HsDev.Tools.Base: at :: (Int -> Maybe a) -> Int -> a
- HsDev.Tools.Base: at_ :: (Int -> Maybe String) -> Int -> String
+ HsDev.Tools.Base: at_ :: IsString s => (Int -> Maybe s) -> Int -> s
- HsDev.Tools.Ghc.Check: PackageDb :: FilePath -> PackageDb
+ HsDev.Tools.Ghc.Check: PackageDb :: Path -> PackageDb
- HsDev.Tools.Ghc.Check: Project :: String -> FilePath -> FilePath -> Maybe ProjectDescription -> Project
+ HsDev.Tools.Ghc.Check: Project :: Text -> Path -> Path -> Maybe ProjectDescription -> Project
- HsDev.Tools.Ghc.Check: [_packageDb] :: PackageDb -> FilePath
+ HsDev.Tools.Ghc.Check: [_packageDb] :: PackageDb -> Path
- HsDev.Tools.Ghc.Check: [_projectCabal] :: Project -> FilePath
+ HsDev.Tools.Ghc.Check: [_projectCabal] :: Project -> Path
- HsDev.Tools.Ghc.Check: [_projectName] :: Project -> String
+ HsDev.Tools.Ghc.Check: [_projectName] :: Project -> Text
- HsDev.Tools.Ghc.Check: [_projectPath] :: Project -> FilePath
+ HsDev.Tools.Ghc.Check: [_projectPath] :: Project -> Path
- HsDev.Tools.Ghc.Check: check :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m [Note OutputMessage]
+ HsDev.Tools.Ghc.Check: check :: (MonadLog m, GhcMonad m) => Module -> Maybe Text -> m [Note OutputMessage]
- HsDev.Tools.Ghc.Compat: exposedModuleName :: ExposedModule unit mname -> mname
+ HsDev.Tools.Ghc.Compat: exposedModuleName :: (a, Maybe b) -> a
- HsDev.Tools.Ghc.Compat: type InstalledUnitId = UnitId
+ HsDev.Tools.Ghc.Compat: type InstalledUnitId = InstalledUnitId
- HsDev.Tools.Ghc.Session: interpretModule :: Module -> Maybe String -> GhcM ()
+ HsDev.Tools.Ghc.Session: interpretModule :: Module -> Maybe Text -> GhcM ()
- HsDev.Tools.Ghc.Types: TypedExpr :: String -> String -> TypedExpr
+ HsDev.Tools.Ghc.Types: TypedExpr :: Maybe Text -> Text -> TypedExpr
- HsDev.Tools.Ghc.Types: [_typedExpr] :: TypedExpr -> String
+ HsDev.Tools.Ghc.Types: [_typedExpr] :: TypedExpr -> Maybe Text
- HsDev.Tools.Ghc.Types: [_typedType] :: TypedExpr -> String
+ HsDev.Tools.Ghc.Types: [_typedType] :: TypedExpr -> Text
- HsDev.Tools.Ghc.Types: fileTypes :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m [Note TypedExpr]
+ HsDev.Tools.Ghc.Types: fileTypes :: (MonadLog m, GhcMonad m) => Module -> Maybe Text -> m [Note TypedExpr]
- HsDev.Tools.Ghc.Types: inferTypes :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m Module
+ HsDev.Tools.Ghc.Types: inferTypes :: (MonadLog m, GhcMonad m) => Module -> Maybe Text -> m Module
- HsDev.Tools.Ghc.Types: moduleTypes :: GhcMonad m => FilePath -> m [(SrcSpan, Type)]
+ HsDev.Tools.Ghc.Types: moduleTypes :: GhcMonad m => Path -> m [(SrcSpan, Type)]
- HsDev.Tools.Ghc.Types: typedExpr :: Lens' TypedExpr String
+ HsDev.Tools.Ghc.Types: typedExpr :: Lens' TypedExpr (Maybe Text)
- HsDev.Tools.Ghc.Types: typedType :: Lens' TypedExpr String
+ HsDev.Tools.Ghc.Types: typedType :: Lens' TypedExpr Text
- HsDev.Tools.Ghc.Worker: SessionGhc :: [String] -> SessionTarget
+ HsDev.Tools.Ghc.Worker: SessionGhc :: SessionType
- HsDev.Tools.Ghc.Worker: SessionGhci :: SessionTarget
+ HsDev.Tools.Ghc.Worker: SessionGhci :: SessionType
- HsDev.Tools.Ghc.Worker: makeTarget :: GhcMonad m => String -> Maybe String -> m Target
+ HsDev.Tools.Ghc.Worker: makeTarget :: GhcMonad m => Text -> Maybe Text -> m Target
- HsDev.Tools.Ghc.Worker: type GhcM a = MGhcT SessionTarget (LogT IO) a
+ HsDev.Tools.Ghc.Worker: type GhcM a = MGhcT SessionConfig (LogT IO) a
- HsDev.Tools.Ghc.Worker: type GhcWorker = Worker (MGhcT SessionTarget (LogT IO))
+ HsDev.Tools.Ghc.Worker: type GhcWorker = Worker (MGhcT SessionConfig (LogT IO))
- HsDev.Tools.Ghc.Worker: workerSession :: SessionTarget -> GhcM ()
+ HsDev.Tools.Ghc.Worker: workerSession :: SessionType -> [String] -> GhcM ()
- HsDev.Tools.HDocs: hdocs :: ModuleLocation -> [String] -> IO (Map String String)
+ HsDev.Tools.HDocs: hdocs :: ModuleLocation -> [String] -> GhcM (Map String String)
- HsDev.Tools.HDocs: hdocsCabal :: PackageDbStack -> [String] -> ExceptT String IO (Map String (Map String String))
+ HsDev.Tools.HDocs: hdocsCabal :: PackageDbStack -> [String] -> GhcM [(ModulePackage, (Map Text (Map Text Text)))]
- HsDev.Tools.HDocs: hdocsy :: [ModuleLocation] -> [String] -> IO [Map String String]
+ HsDev.Tools.HDocs: hdocsy :: [ModuleLocation] -> [String] -> GhcM [Map String String]
- HsDev.Tools.HDocs: setDocs :: Map String String -> Module -> Module
+ HsDev.Tools.HDocs: setDocs :: MonadIO m => LookupTable (Text, Text) (Maybe Text) -> Map Text Text -> Module -> m Module
- HsDev.Tools.HLint: hlint :: FilePath -> Maybe String -> ExceptT String IO [Note OutputMessage]
+ HsDev.Tools.HLint: hlint :: FilePath -> Maybe Text -> ExceptT String IO [Note OutputMessage]
- HsDev.Tools.Types: OutputMessage :: String -> Maybe String -> OutputMessage
+ HsDev.Tools.Types: OutputMessage :: Text -> Maybe Text -> OutputMessage
- HsDev.Tools.Types: [_messageSuggestion] :: OutputMessage -> Maybe String
+ HsDev.Tools.Types: [_messageSuggestion] :: OutputMessage -> Maybe Text
- HsDev.Tools.Types: [_message] :: OutputMessage -> String
+ HsDev.Tools.Types: [_message] :: OutputMessage -> Text
- HsDev.Tools.Types: message :: Lens' OutputMessage String
+ HsDev.Tools.Types: message :: Lens' OutputMessage Text
- HsDev.Tools.Types: messageSuggestion :: Lens' OutputMessage (Maybe String)
+ HsDev.Tools.Types: messageSuggestion :: Lens' OutputMessage (Maybe Text)
- HsDev.Tools.Types: note :: forall a_a1vCs a_a1vPy. Lens (Note a_a1vCs) (Note a_a1vPy) a_a1vCs a_a1vPy
+ HsDev.Tools.Types: note :: forall a_a1Osq a_a1OEl. Lens (Note a_a1Osq) (Note a_a1OEl) a_a1Osq a_a1OEl
- HsDev.Tools.Types: noteLevel :: forall a_a1vCs. Lens' (Note a_a1vCs) (Maybe Severity)
+ HsDev.Tools.Types: noteLevel :: forall a_a1Osq. Lens' (Note a_a1Osq) (Maybe Severity)
- HsDev.Tools.Types: noteRegion :: forall a_a1vCs. Lens' (Note a_a1vCs) Region
+ HsDev.Tools.Types: noteRegion :: forall a_a1Osq. Lens' (Note a_a1Osq) Region
- HsDev.Tools.Types: noteSource :: forall a_a1vCs. Lens' (Note a_a1vCs) ModuleLocation
+ HsDev.Tools.Types: noteSource :: forall a_a1Osq. Lens' (Note a_a1Osq) ModuleLocation
- HsDev.Tools.Types: outputMessage :: String -> OutputMessage
+ HsDev.Tools.Types: outputMessage :: Text -> OutputMessage
- HsDev.Types: FileNotFound :: FilePath -> HsDevError
+ HsDev.Types: FileNotFound :: Path -> HsDevError
- HsDev.Types: PackageNotFound :: String -> HsDevError
+ HsDev.Types: PackageNotFound :: Text -> HsDevError
- HsDev.Types: ProjectNotFound :: String -> HsDevError
+ HsDev.Types: ProjectNotFound :: Text -> HsDevError
- HsDev.Util: readFileUtf8 :: FilePath -> IO String
+ HsDev.Util: readFileUtf8 :: FilePath -> IO Text
- HsDev.Util: writeFileUtf8 :: FilePath -> String -> IO ()
+ HsDev.Util: writeFileUtf8 :: FilePath -> Text -> IO ()
- System.Directory.Paths: absolutise :: Paths a => FilePath -> a -> a
+ System.Directory.Paths: absolutise :: Paths a => Path -> a -> a
- System.Directory.Paths: relativise :: Paths a => FilePath -> a -> a
+ System.Directory.Paths: relativise :: Paths a => Path -> a -> a

Files

+ data/hsdev.sql view
@@ -0,0 +1,283 @@+pragma case_sensitive_like = true;
+
+create table hsdev (
+	option text,
+	value json
+);
+
+create table package_dbs (
+	package_db text, -- global, user or path
+	package_name text,
+	package_version text
+);
+
+create view latest_packages (
+	package_db,
+	package_name,
+	package_version
+) as
+select package_db, package_name, max(package_version)
+from package_dbs
+group by package_db, package_name;
+
+create table projects (
+	id integer primary key autoincrement,
+	name text,
+	cabal text,
+	version text,
+	package_db_stack json -- list of package-db
+);
+
+create unique index projects_id_index on projects (id);
+
+create table libraries (
+	project_id integer,
+	modules json, -- list of modules
+	build_info_id integer
+);
+
+create table executables (
+	project_id integer,
+	name text,
+	path text,
+	build_info_id integer
+);
+
+create table tests (
+	project_id integer,
+	name text,
+	enabled integer,
+	main text,
+	build_info_id integer
+);
+
+create view targets (
+	project_id,
+	build_info_id
+) as
+select project_id, build_info_id from libraries
+union
+select project_id, build_info_id from executables
+union
+select project_id, build_info_id from tests;
+
+create table build_infos(
+	id integer primary key autoincrement,
+	depends json, -- list of dependencies
+	language text,
+	extensions json, -- list of extensions
+	ghc_options json, -- list of ghc-options
+	source_dirs json, -- list of source directories
+	other_modules json -- list of other modules
+);
+
+create view projects_deps (
+	cabal,
+	package_name,
+	package_version
+) as
+select distinct p.cabal, deps.value, ps.package_version
+from projects as p, json_each(p.package_db_stack) as pdb_stack, build_infos as b, json_each(b.depends) as deps, targets as t, latest_packages as ps
+where (p.id == t.project_id) and (b.id == t.build_info_id) and (deps.value <> p.name) and (ps.package_name == deps.value) and (ps.package_db == pdb_stack.value);
+
+create view projects_modules_scope (
+	cabal,
+	module_id
+) as
+select pdbs.cabal, m.id
+from projects_deps as pdbs, modules as m
+where (m.package_name == pdbs.package_name) and (m.package_version == pdbs.package_version)
+union
+select p.cabal, m.id
+from projects as p, modules as m
+where (m.cabal == p.cabal)
+union
+select null, m.id
+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 in ('user-db', 'global-db'));
+
+create unique index build_infos_id_index on build_infos (id);
+
+create table symbols (
+	id integer primary key autoincrement,
+	name text,
+	module_id integer,
+	docs text,
+	line integer,
+	column integer,
+	what text, -- kind of symbol: function, method, ...
+	type text,
+	parent text,
+	constructors json, -- list of constructors for selector
+	args json, -- list of arguments for types
+	context json, -- list of contexts for types
+	associate text, -- associates for families
+	pat_type text,
+	pat_constructor text
+);
+
+create unique index symbols_id_index on symbols (id);
+create index symbols_module_id_index on symbols (module_id);
+create index symbols_name_index on symbols (name);
+
+create table modules (
+	id integer primary key autoincrement,
+	file text,
+	cabal text,
+	-- project_id integer,
+	install_dirs json, -- list of paths
+	package_name text,
+	package_version text,
+	installed_name text, -- if not null, should be equal to name
+	other_location text,
+
+	name text,
+	docs text,
+	fixities json, -- list of fixities
+	tags json, -- dict of tags, value not used, tag is set if present
+	inspection_error text,
+	inspection_time integer,
+	inspection_opts json -- list of flags
+);
+
+create unique index modules_id_index on modules (id);
+create index modules_name_index on modules (name);
+create unique index modules_file_index on modules (file) where file is not null;
+create unique index modules_installed_index on modules (package_name, package_version, installed_name) where
+	package_name is not null and
+	package_version is not null and
+	installed_name is not null;
+create unique index modules_other_locations_index on modules (other_location) where other_location is not null;
+
+create table imports (
+	module_id integer,
+	line integer,
+	module_name text,
+	qualified integer,
+	alias text,
+	hiding integer,
+	import_list json -- list of import specs
+);
+
+create index imports_module_id_index on imports (module_id);
+
+create view imported_modules as
+select i.*, im.id as imported_id
+from imports as i, projects_modules_scope as ps, modules as im, modules as m
+where
+	i.module_id == m.id and
+	((ps.cabal is null and m.cabal is null) or (ps.cabal == m.cabal)) and
+	ps.module_id == im.id and
+	im.name == i.module_name;
+
+create view imported_scopes as
+select i.*, q.value as qualifier, s.name as name, s.id as symbol_id
+from imported_modules as i, json_each(case when i.qualified then json_array(ifnull(i.alias, i.module_name)) else json_array(ifnull(i.alias, i.module_name), null) end) as q, symbols as s, exports as e
+where
+	e.module_id == i.imported_id and
+	e.symbol_id == s.id and
+	(i.import_list is null or (s.name in (select json_extract(import_list_item.value, '$.name') from json_each(i.import_list) as import_list_item) == not i.hiding));
+
+create table exports (
+	module_id integer,
+	symbol_id integer
+);
+
+create index exports_module_id_index on exports (module_id);
+
+create table scopes (
+	module_id integer,
+	qualifier text,
+	name text,
+	symbol_id integer
+);
+
+create index scopes_module_id_index on scopes (module_id);
+
+create view completions (
+	module_id,
+	completion,
+	symbol_id
+) as
+select id, (case when sc.qualifier is null then sc.name else sc.qualifier || '.' || sc.name end) as full_name, sc.symbol_id
+from modules as m, scopes as sc
+where (m.id == sc.module_id);
+
+create table names (
+	module_id integer,
+	qualifier text,
+	name text,
+	line integer,
+	column integer,
+	line_to integer,
+	column_to integer,
+	def_line integer,
+	def_column integer,
+	inferred_type text,
+	resolved_module text,
+	resolved_name text,
+	resolve_error text
+);
+
+create unique index names_position_index on names (module_id, line, column, line_to, column_to);
+create index names_module_id_index on names (module_id);
+
+create view definitions (
+	module_id,
+	name,
+	line,
+	column,
+	line_to,
+	column_to,
+	def_module_id,
+	def_line,
+	def_column,
+	local
+) as
+select module_id, name, line, column, line_to, column_to, module_id, def_line, def_column, 1
+from names
+where def_line is not null and def_column is not null
+union
+select n.module_id, n.resolved_name, n.line, n.column, n.line_to, n.column_to, s.module_id, s.line, s.column, 0
+from names as n, modules as srcm, modules as m, projects_modules_scope as ps, symbols as s
+where
+	(n.module_id == srcm.id) and
+	(srcm.cabal == ps.cabal) and
+	(m.id == ps.module_id) and
+	(m.name == n.resolved_module) and
+	(s.module_id == m.id) and
+	(s.name == n.resolved_name);
+
+create view sources_depends (
+	module_id,
+	module_file,
+	depends_id,
+	depends_file
+) as
+select m.id, m.file, im.id, im.file
+from modules as im, imports as i, modules as m, projects_modules_scope as ps
+where
+	(m.cabal is ps.cabal) and
+	(ps.module_id == im.id) and
+	(i.module_id == m.id) and
+	(im.name == i.module_name) and
+	(m.file is not null) and
+	(im.file is not null);
+
+create table types (
+	module_id integer,
+	line integer,
+	column integer,
+	line_to integer,
+	column_to integer,
+	expr text,
+	type text
+);
+
+create table file_contents (
+	file text not null,
+	contents text,
+	mtime integer
+);
+
+create unique index file_contents_index on file_contents (file);
hsdev.cabal view
@@ -1,318 +1,245 @@-name:                hsdev-version:             0.2.5.1-synopsis:            Haskell development library-description:-  Haskell development library and tool with support of autocompletion, symbol info, go to declaration, find references, hayoo search etc.-homepage:            https://github.com/mvoidex/hsdev-license:             BSD3-license-file:        LICENSE-author:              Alexandr `Voidex` Ruchkin-maintainer:          voidex@live.com--- copyright:-category:            Development-build-type:          Simple-cabal-version:       >=1.8-extra-source-files:-    tests/test-package/*.hs-    tests/test-package/test-package.cabal--source-repository head-  type: git-  location: git://github.com/mvoidex/hsdev.git--library-  hs-source-dirs: src-  ghc-options: -Wall -fno-warn-tabs-  exposed-modules:-    Control.Apply.Util-    Control.Concurrent.FiniteChan-    Control.Concurrent.Worker-    Control.Concurrent.Util-    Data.Async-    Data.Deps-    Data.Group-    Data.Help-    Data.Lisp-    Data.Maybe.JustIf-    HsDev-    HsDev.Cache-    HsDev.Cache.Structured-    HsDev.Client.Commands-    HsDev.Commands-    HsDev.Database-    HsDev.Database.Async-    HsDev.Database.Update-    HsDev.Database.Update.Types-    HsDev.Display-    HsDev.Error-    HsDev.Inspect-    HsDev.PackageDb-    HsDev.Project-    HsDev.Project.Compat-    HsDev.Project.Types-    HsDev.Scan-    HsDev.Scan.Browse-    HsDev.Server.Base-    HsDev.Server.Commands-    HsDev.Server.Message-    HsDev.Server.Types-    HsDev.Sandbox-    HsDev.Stack-    HsDev.Symbols-    HsDev.Symbols.Class-    HsDev.Symbols.Location-    HsDev.Symbols.Documented-    HsDev.Symbols.Resolve-    HsDev.Symbols.Types-    HsDev.Symbols.Util-    HsDev.Tools.AutoFix-    HsDev.Tools.Base-    HsDev.Tools.Cabal-    HsDev.Tools.ClearImports-    HsDev.Tools.Ghc.Check-    HsDev.Tools.Ghc.Compat-    HsDev.Tools.Ghc.MGhc-    HsDev.Tools.Ghc.Prelude-    HsDev.Tools.Ghc.Types-    HsDev.Tools.Ghc.Worker-    HsDev.Tools.Ghc.Session-    HsDev.Tools.Hayoo-    HsDev.Tools.HDocs-    HsDev.Tools.HLint-    HsDev.Tools.Types-    HsDev.Types-    HsDev.Util-    HsDev.Version-    HsDev.Watcher-    HsDev.Watcher.Types-    System.Directory.Paths-    System.Directory.Watcher--  if os(windows)-    build-depends:-      Win32 >= 2.3.0-    exposed-modules:-      System.Win32.PowerShell-      System.Win32.FileMapping.NamePool-      System.Win32.FileMapping.Memory-  else-    build-depends:-      unix >= 2.7.0--  if impl(ghc >= 8.2) && impl(ghc < 8.3)-    build-depends:-      haddock-api >= 2.18.0 && < 2.19.0,-      ghc == 8.2.*,-      ghc-boot,-      directory >= 1.2.6.0-  if impl(ghc >= 8.0) && impl(ghc < 8.2)-    build-depends:-      haddock-api >= 2.17.0 && < 2.18.0,-      ghc >= 8.0.0 && < 8.1.0,-      ghc-boot,-      directory >= 1.2.6.0-  if impl(ghc < 8.0)-    build-depends:-      haddock-api >= 2.16.0 && < 2.17.0,-      ghc >= 7.10.0 && < 7.11.0,-      bin-package-db,-      directory == 1.2.2.*--  build-depends:-    base >= 4.7 && < 5,-    aeson >= 0.7.0,-    aeson-pretty >= 0.7.0,-    array >= 0.5.0,-    async >= 2.0,-    attoparsec >= 0.11.0,-    bytestring >= 0.10.0,-    Cabal >= 1.22.0,-    containers >= 0.5.0,-    cpphs >= 1.19.0,-    data-default >= 0.5.0,-    deepseq >= 1.4.0,-    exceptions >= 0.6.0,-    filepath >= 1.4.0,-    fsnotify >= 0.2.1,-    ghc-paths >= 0.1.0,-    ghc-syb-utils >= 0.2.3,-    haskell-src-exts >= 1.18.0 && < 1.20.0,-    hdocs >= 0.5.0,-    hformat == 0.3.*,-    hlint >= 1.9.13 && < 2.1,-    HTTP >= 4000.2.0,-    lens >= 4.8,-    lifted-base >= 0.2,-    monad-control >= 1.0,-    monad-loops >= 0.4,-    mmorph >= 1.0 && < 1.2,-    mtl >= 2.2.0,-    network >= 2.6.0,-    optparse-applicative >= 0.11,-    process >= 1.2.0,-    regex-pcre-builtin >= 0.94,-    scientific >= 0.3,-    simple-log == 0.9.*,-    syb >= 0.5.1,-    template-haskell,-    text >= 1.2.0,-    text-region == 0.3.*,-    time >= 1.5.0,-    transformers >= 0.4.0,-    transformers-base >= 0.4.0,-    uniplate >= 1.6.0,-    unordered-containers >= 0.2.0,-    vector >= 0.10.0--executable hsdev-  main-is: hsdev.hs-  hs-source-dirs: tools-  ghc-options: -threaded -Wall -fno-warn-tabs "-with-rtsopts=-N4"--  build-depends:-    base >= 4.7 && < 5,-    hsdev,-    aeson >= 0.7.0,-    aeson-pretty >= 0.7.0,-    bytestring >= 0.10.0,-    containers >= 0.5.0,-    deepseq >= 1.4.0,-    directory,-    exceptions >= 0.6.0,-    filepath >= 1.4.0,-    monad-loops >= 0.4.0,-    mtl >= 2.2.0,-    network >= 2.6.0,-    optparse-applicative >= 0.11,-    process >= 1.2.0,-    text >= 1.2.0,-    transformers >= 0.4.0,-    unordered-containers >= 0.2.0--executable hsinspect-  main-is: hsinspect.hs-  hs-source-dirs: tools-  ghc-options: -Wall -fno-warn-tabs-  other-modules:-    Tool-  build-depends:-    base >= 4.7 && < 5,-    hsdev,-    aeson >= 0.7.0,-    aeson-pretty >= 0.7.0,-    bytestring >= 0.10.0,-    containers >= 0.5.0,-    data-default >= 0.5.0,-    directory,-    filepath >= 1.4.0,-    mtl >= 2.2.0,-    optparse-applicative >= 0.11,-    text >= 1.2.0,-    transformers >= 0.4.0,-    unordered-containers >= 0.2.0,-    vector >= 0.10.0--executable hsclearimports-  main-is: hsclearimports.hs-  hs-source-dirs: tools-  ghc-options: -Wall -fno-warn-tabs-  other-modules:-    Tool-  build-depends:-    base >= 4.7 && < 5,-    hsdev,-    aeson >= 0.7.0,-    aeson-pretty >= 0.7.0,-    bytestring >= 0.10.0,-    containers >= 0.5.0,-    data-default >= 0.5.0,-    directory,-    haskell-src-exts >= 1.18.0 && < 1.20.0,-    lens >= 4.8,-    mtl >= 2.2.0,-    optparse-applicative >= 0.11,-    text >= 1.2.0,-    transformers >= 0.4.0,-    unordered-containers >= 0.2.0--executable hscabal-  main-is: hscabal.hs-  hs-source-dirs: tools-  ghc-options: -Wall -fno-warn-tabs-  other-modules:-    Tool-  build-depends:-    base >= 4.7 && < 5,-    hsdev,-    aeson >= 0.7.0,-    aeson-pretty >= 0.7.0,-    bytestring >= 0.10.0,-    containers >= 0.5.0,-    data-default >= 0.5.0,-    mtl >= 2.2.0,-    optparse-applicative >= 0.11,-    text >= 1.2.0,-    transformers >= 0.4.0,-    unordered-containers >= 0.2.0--executable hshayoo-  main-is: hshayoo.hs-  hs-source-dirs: tools-  ghc-options: -Wall -fno-warn-tabs-  other-modules:-    Tool-  build-depends:-    base >= 4.7 && < 5,-    hsdev,-    aeson >= 0.7.0,-    aeson-pretty >= 0.7.0,-    bytestring >= 0.10.0,-    containers >= 0.5.0,-    data-default >= 0.5.0,-    mtl >= 2.2.0,-    optparse-applicative >= 0.11,-    text >= 1.2.0,-    transformers >= 0.4.0,-    unordered-containers >= 0.2.0--executable hsautofix-  main-is: hsautofix.hs-  hs-source-dirs: tools-  ghc-options: -Wall -fno-warn-tabs-  other-modules:-    Tool-  build-depends:-    base >= 4.7 && < 5,-    hsdev,-    aeson >= 0.7.0,-    aeson-pretty >= 0.7.0,-    bytestring >= 0.10.0,-    data-default >= 0.5.0,-    directory,-    filepath >= 1.4.0,-    lens >= 4.8,-    mtl >= 2.2.0,-    optparse-applicative >= 0.11,-    transformers >= 0.4.0--test-suite test-  main-is: Test.hs-  hs-source-dirs: tests-  ghc-options: -threaded -Wall -fno-warn-tabs-  type: exitcode-stdio-1.0-  build-depends:-    base >= 4.7 && < 5,-    hsdev,-    aeson >= 0.7.0,-    aeson-lens >= 0.5.0,-    async >= 2.0,-    data-default >= 0.5.0,-    deepseq >= 1.4.0,-    directory,-    filepath >= 1.4.0,-    containers >= 0.5.0,-    hformat == 0.3.*,-    hspec,-    lens >= 4.8,-    mtl >= 2.2.0,-    text >= 1.2.0+name:                hsdev
+version:             0.3.0.0
+synopsis:            Haskell development library
+description:
+  Haskell development library and tool with support of autocompletion, symbol info, go to declaration, find references, hayoo search etc.
+homepage:            https://github.com/mvoidex/hsdev
+license:             BSD3
+license-file:        LICENSE
+author:              Alexandr `Voidex` Ruchkin
+maintainer:          voidex@live.com
+-- copyright:
+category:            Development
+build-type:          Simple
+cabal-version:       >=1.8
+extra-source-files:
+  tests/test-package/*.hs
+  tests/test-package/test-package.cabal
+  tests/data/base.sql
+  tests/data/ModuleTwo.modified.hs
+  data/hsdev.sql
+
+source-repository head
+  type: git
+  location: git://github.com/mvoidex/hsdev.git
+
+flag docs
+  description: build with haddock/hdocs to support scanning docs
+  default: True
+
+library
+  hs-source-dirs: src
+  ghc-options: -Wall -fno-warn-tabs
+
+  if !flag(docs)
+    cpp-options: -DNODOCS
+
+  exposed-modules:
+    Control.Apply.Util
+    Control.Concurrent.FiniteChan
+    Control.Concurrent.Worker
+    Control.Concurrent.Util
+    Data.Deps
+    Data.Help
+    Data.Lisp
+    Data.Maybe.JustIf
+    Data.LookupTable
+    HsDev
+    HsDev.Client.Commands
+    HsDev.Database.SQLite
+    HsDev.Database.SQLite.Instances
+    HsDev.Database.SQLite.Schema
+    HsDev.Database.SQLite.Schema.TH
+    HsDev.Database.SQLite.Select
+    HsDev.Database.SQLite.Transaction
+    HsDev.Database.Update
+    HsDev.Database.Update.Types
+    HsDev.Display
+    HsDev.Error
+    HsDev.Inspect
+    HsDev.Inspect.Order
+    HsDev.PackageDb
+    HsDev.PackageDb.Types
+    HsDev.Project
+    HsDev.Project.Compat
+    HsDev.Project.Types
+    HsDev.Scan
+    HsDev.Scan.Browse
+    HsDev.Server.Base
+    HsDev.Server.Commands
+    HsDev.Server.Message
+    HsDev.Server.Message.Lisp
+    HsDev.Server.Types
+    HsDev.Sandbox
+    HsDev.Stack
+    HsDev.Symbols
+    HsDev.Symbols.Name
+    HsDev.Symbols.Class
+    HsDev.Symbols.HaskellNames
+    HsDev.Symbols.Location
+    HsDev.Symbols.Documented
+    HsDev.Symbols.Resolve
+    HsDev.Symbols.Parsed
+    HsDev.Symbols.Types
+    HsDev.Tools.AutoFix
+    HsDev.Tools.Base
+    HsDev.Tools.Cabal
+    HsDev.Tools.ClearImports
+    HsDev.Tools.Ghc.Check
+    HsDev.Tools.Ghc.Compat
+    HsDev.Tools.Ghc.MGhc
+    HsDev.Tools.Ghc.Prelude
+    HsDev.Tools.Ghc.Session
+    HsDev.Tools.Ghc.Types
+    HsDev.Tools.Ghc.Worker
+    HsDev.Tools.Hayoo
+    HsDev.Tools.HDocs
+    HsDev.Tools.HLint
+    HsDev.Tools.Refact
+    HsDev.Tools.Types
+    HsDev.Types
+    HsDev.Util
+    HsDev.Version
+    HsDev.Watcher
+    HsDev.Watcher.Types
+    System.Directory.Paths
+    System.Directory.Watcher
+
+  if os(windows)
+    build-depends:
+      Win32 >= 2.3.0
+    exposed-modules:
+      System.Win32.PowerShell
+      System.Win32.FileMapping.NamePool
+      System.Win32.FileMapping.Memory
+  else
+    build-depends:
+      unix >= 2.7.0
+
+  if impl(ghc >= 8.2) && impl(ghc < 8.3)
+    if flag(docs)
+      build-depends:
+        haddock-api >= 2.18.0 && < 2.19.0
+
+    build-depends:
+      ghc == 8.2.*,
+      ghc-boot,
+      directory >= 1.2.6.0
+  if impl(ghc >= 8.0) && impl(ghc < 8.2)
+    if flag(docs)
+      build-depends:
+        haddock-api >= 2.17.0 && < 2.18.0
+
+    build-depends:
+      ghc >= 8.0.0 && < 8.1.0,
+      ghc-boot,
+      directory >= 1.2.6.0
+  if impl(ghc < 8.0)
+    if flag(docs)
+      build-depends:
+        haddock-api >= 2.16.0 && < 2.17.0
+
+    build-depends:
+      ghc >= 7.10.0 && < 7.11.0,
+      bin-package-db,
+      directory == 1.2.2.*
+
+  if flag(docs)
+    build-depends:
+      hdocs >= 0.5.1
+
+  build-depends:
+    base >= 4.7 && < 5,
+    aeson >= 0.7.0,
+    aeson-pretty >= 0.7.0,
+    array >= 0.5.0,
+    async >= 2.0,
+    attoparsec >= 0.11.0,
+    bytestring >= 0.10.0,
+    Cabal >= 1.22.0,
+    containers >= 0.5,
+    cpphs >= 1.19.0,
+    data-default >= 0.5.0,
+    deepseq >= 1.4.0,
+    exceptions >= 0.6.0,
+    filepath >= 1.4.0,
+    fsnotify >= 0.2.1,
+    ghc-paths >= 0.1.0,
+    ghc-syb-utils >= 0.2.3,
+    haskell-names >= 0.9.0 && < 0.10.0,
+    haskell-src-exts >= 1.18.0 && < 1.21.0,
+    hformat >= 0.3.0,
+    hlint >= 1.9.13 && < 2.1,
+    HTTP >= 4000.2.0,
+    lens >= 4.8,
+    lifted-base >= 0.2,
+    monad-control >= 1.0,
+    monad-loops >= 0.4,
+    mmorph >= 1.0 && < 1.2,
+    mtl >= 2.2.0,
+    network >= 2.6.0,
+    optparse-applicative >= 0.11,
+    process >= 1.2.0,
+    regex-pcre-builtin >= 0.94,
+    scientific >= 0.3,
+    simple-log == 0.9.*,
+    stm == 2.4.*,
+    direct-sqlite >= 2.3.21,
+    sqlite-simple,
+    syb >= 0.5.1,
+    template-haskell,
+    text >= 1.2.0,
+    text-region >= 0.3,
+    time >= 1.5.0,
+    transformers >= 0.4.0,
+    transformers-base >= 0.4.0,
+    uniplate >= 1.6.0,
+    unordered-containers >= 0.2.0,
+    vector >= 0.10.0
+
+executable hsdev
+  main-is: hsdev.hs
+  hs-source-dirs: tools
+  ghc-options: -threaded -Wall -fno-warn-tabs "-with-rtsopts=-N4"
+
+  build-depends:
+    base >= 4.7 && < 5,
+    hsdev,
+    aeson >= 0.7.0,
+    aeson-pretty >= 0.7.0,
+    bytestring >= 0.10.0,
+    containers >= 0.5,
+    deepseq >= 1.4.0,
+    directory,
+    exceptions >= 0.6.0,
+    filepath >= 1.4.0,
+    monad-loops >= 0.4.0,
+    mtl >= 2.2.0,
+    network >= 2.6.0,
+    optparse-applicative >= 0.11,
+    process >= 1.2.0,
+    text >= 1.2.0,
+    transformers >= 0.4.0,
+    unordered-containers >= 0.2.0
+
+test-suite test
+  main-is: Test.hs
+  hs-source-dirs: tests
+  ghc-options: -threaded -Wall -fno-warn-tabs
+  type: exitcode-stdio-1.0
+  build-depends:
+    base >= 4.7 && < 5,
+    hsdev,
+    aeson >= 0.7.0,
+    aeson-lens >= 0.5.0,
+    async >= 2.0,
+    data-default >= 0.5.0,
+    deepseq >= 1.4.0,
+    directory,
+    filepath >= 1.4.0,
+    containers >= 0.5.0,
+    hformat == 0.3.*,
+    hspec,
+    lens >= 4.8,
+    mtl >= 2.2.0,
+    text >= 1.2.0
src/Control/Apply/Util.hs view
@@ -1,5 +1,5 @@ module Control.Apply.Util (
-	(&), chain, with
+	(&), chain
 	) where
 
 (&) :: a -> (a -> b) -> b
@@ -7,9 +7,3 @@ 
 chain :: [a -> a] -> a -> a
 chain = foldr (.) id
-
--- | Flipped version of 'chain', which can be used like this:
---
--- >foo `with` [f, g, h]
-with :: a -> [a -> a] -> a
-with = flip chain
src/Control/Concurrent/FiniteChan.hs view
@@ -1,73 +1,57 @@ module Control.Concurrent.FiniteChan (
 	Chan,
-	newChan, dupChan, openedChan, closedChan, doneChan, sendChan, putChan, getChan, readChan, closeChan, stopChan
+	newChan, openedChan, closedChan, doneChan, sendChan, getChan, closeChan, stopChan
 	) where
 
 import Control.Monad (void, when, liftM2)
-import qualified Control.Concurrent.Chan as C
-import Control.Concurrent.MVar
-import Data.Maybe
-
-data State = Opened | Closing | Closed deriving (Eq, Ord, Enum, Bounded, Read, Show)
+import Control.Monad.Loops
+import Control.Concurrent.STM
 
--- | 'Chan' is stoppable channel unline 'Control.Concurrent.Chan'
-data Chan a = Chan (C.Chan (Maybe a)) (MVar State)
+-- | 'Chan' is stoppable channel
+data Chan a = Chan {
+	chanOpened :: TMVar Bool,
+	chanQueue :: TQueue a }
 
--- | Create channel
+-- -- | Create new channel
 newChan :: IO (Chan a)
-newChan = liftM2 Chan C.newChan (newMVar Opened)
-
--- | Duplicate channel
-dupChan :: Chan a -> IO (Chan a)
-dupChan (Chan ch st) = liftM2 Chan (C.dupChan ch) (return st)
+newChan = liftM2 Chan (newTMVarIO True) newTQueueIO
 
 -- | Is channel opened
 openedChan :: Chan a -> IO Bool
-openedChan (Chan _ st) = (== Opened) <$> readMVar st
+openedChan = atomically . readTMVar . chanOpened
 
--- | Is channel closing/closed
+-- | Is channel closed
 closedChan :: Chan a -> IO Bool
-closedChan (Chan _ st) = (/= Opened) <$> readMVar st
+closedChan = fmap not . openedChan
 
--- | Is channed closed and all data allready read from it
+-- | Is channel closed and all data consumed
 doneChan :: Chan a -> IO Bool
-doneChan (Chan _ st) = (== Closed) <$> readMVar st
+doneChan ch = atomically $ do
+	o <- readTMVar $ chanOpened ch
+	e <- isEmptyTQueue (chanQueue ch)
+	return $ not o && e
 
--- | Write data to channel
+-- | Write data to channel if it is open
 sendChan :: Chan a -> a -> IO Bool
-sendChan (Chan ch st) v = do
-	state <- readMVar st
-	if state == Opened
-		then do
-			C.writeChan ch (Just v)
-			return True
-		else return False
-
--- | Put data to channel
-putChan :: Chan a -> a -> IO ()
-putChan ch = void . sendChan ch
+sendChan ch v = atomically $ do
+	o <- readTMVar $ chanOpened ch
+	when o $ writeTQueue (chanQueue ch) v
+	return o
 
 -- | Get data from channel
 getChan :: Chan a -> IO (Maybe a)
-getChan (Chan ch st) = do
-	state <- readMVar st
-	if state == Closed then return Nothing else do
-		r <- C.readChan ch
-		when (isNothing r) (void $ swapMVar st Closed)
-		return r
-
--- | Read channel contents
-readChan :: Chan a -> IO [a]
-readChan (Chan ch _) = (catMaybes . takeWhile isJust) <$> C.getChanContents ch
+getChan ch = atomically $ do
+	o <- readTMVar $ chanOpened ch
+	if o
+		then fmap Just (readTQueue (chanQueue ch))
+		else tryReadTQueue (chanQueue ch)
 
--- | Close channel. 'putChan' will still work, but no data will be available on other ending
+-- | Close channel
 closeChan :: Chan a -> IO ()
-closeChan (Chan ch st) = do
-	state <- readMVar st
-	when (state == Opened) $ do
-		_ <- swapMVar st Closing
-		C.writeChan ch Nothing
+closeChan ch = atomically $ void $ swapTMVar (chanOpened ch) False
 
--- | Stop channel and return all data
+-- | Close channel and read all messages
 stopChan :: Chan a -> IO [a]
-stopChan ch = closeChan ch >> readChan ch
+stopChan ch = do
+	closeChan ch
+	whileJust (getChan ch) return
src/Control/Concurrent/Util.hs view
@@ -1,5 +1,5 @@ module Control.Concurrent.Util (
-	fork, timeout
+	fork, timeout, sync
 	) where
 
 import Control.Concurrent
@@ -13,3 +13,10 @@ timeout :: Int -> IO a -> IO (Maybe a)
 timeout 0 act = liftM Just act
 timeout tm act = liftM (either Just (const Nothing)) $ race act (threadDelay tm)
+
+sync :: MonadIO m => (IO () -> m a) -> m a
+sync act = do
+	syncVar <- liftIO newEmptyMVar
+	r <- act $ putMVar syncVar ()
+	liftIO $ takeMVar syncVar
+	return r
src/Control/Concurrent/Worker.hs view
@@ -2,10 +2,9 @@ 
 module Control.Concurrent.Worker (
 	Worker(..), WorkerStopped(..),
-	startWorker, workerDone,
-	sendTask, pushTask,
-	restartWorker, stopWorker, syncTask,
-	inWorkerWith, inWorker, inWorker_,
+	startWorker, workerAlive, workerDone,
+	sendTask, stopWorker, joinWorker, syncTask,
+	inWorkerWith, inWorker,
 
 	module Control.Concurrent.Async
 	) where
@@ -14,7 +13,7 @@ import Control.Monad.IO.Class
 import Control.Monad.Catch
 import Control.Monad.Except
-import Data.Maybe (isJust)
+import Data.Maybe (isNothing)
 import Data.Typeable
 
 import Control.Concurrent.FiniteChan
@@ -22,34 +21,36 @@ 
 data Worker m = Worker {
 	workerChan :: Chan (Async (), m ()),
-	workerWrap :: forall a. m a -> m a,
-	workerTask :: MVar (Async ()),
-	workerTouch :: IO () }
+	workerTask :: MVar (Async ()) }
 
 data WorkerStopped = WorkerStopped deriving (Show, Typeable)
 
 instance Exception WorkerStopped
 
 -- | Create new worker
-startWorker :: MonadIO m => (m () -> IO ()) -> (m () -> m ()) -> (forall a. m a -> m a) -> IO (Worker m)
-startWorker run initialize wrap = do
+startWorker :: MonadIO m => (m () -> IO ()) -> (m () -> m ()) -> (m () -> m ()) -> IO (Worker m)
+startWorker run initialize handleErrs = do
 	ch <- newChan
 	taskVar <- newEmptyMVar
 	let
-		start = async $ run $ initialize go
+		job = onException (run $ initialize go) $ do
+			closeChan ch
+			abort
 		go = do
 			t <- fmap snd <$> liftIO (getChan ch)
-			maybe (return ()) (>> go) t
-	start >>= putMVar taskVar
-	let
-		restart = do
-			done <- doneChan ch
-			unless done $ do
-				task <- readMVar taskVar
-				stopped <- isJust <$> poll task
-				when stopped (start >>= void . swapMVar taskVar)
-	return $ Worker ch wrap taskVar restart
+			maybe (return ()) (\f' -> handleErrs f' >> go) t
+		abort = do
+			a <- fmap fst <$> liftIO (getChan ch)
+			maybe (return ()) (\a' -> liftIO (cancel a') >> abort) a
+	async job >>= putMVar taskVar
+	return $ Worker ch taskVar
 
+-- | Check whether worker alive
+workerAlive :: Worker m -> IO Bool
+workerAlive w = do
+	task <- readMVar $ workerTask w
+	isNothing <$> poll task
+
 workerDone :: Worker m -> IO Bool
 workerDone = doneChan . workerChan
 
@@ -57,7 +58,7 @@ sendTask w act = mfix $ \async' -> do
 	var <- newEmptyMVar
 	let
-		act' = (workerWrap w act >>= liftIO . putMVar var . Right) `catch` onError
+		act' = (act >>= liftIO . putMVar var . Right) `catch` onError
 		onError :: MonadIO m => SomeException -> m ()
 		onError = liftIO . putMVar var . Left
 		f = do
@@ -67,30 +68,25 @@ 			either throwM return r
 	async f
 
-pushTask :: (MonadCatch m, MonadIO m) => Worker m -> m a -> IO (Async a)
-pushTask w act = workerTouch w >> sendTask w act
-
-restartWorker :: Worker m -> IO ()
-restartWorker w = do
-	async' <- readMVar (workerTask w)
-	cancel async'
-	void $ waitCatch async'
-
+-- | Close worker channel
 stopWorker :: Worker m -> IO ()
 stopWorker = closeChan . workerChan
 
+-- | Stop worker and wait for it
+joinWorker :: Worker m -> IO ()
+joinWorker w = do
+	stopWorker w
+	async' <- readMVar $ workerTask w
+	void $ waitCatch async'
+
 -- | Send empty task and wait until worker run it
 syncTask :: (MonadCatch m, MonadIO m) => Worker m -> IO ()
-syncTask w = pushTask w (return ()) >>= void . wait
+syncTask w = sendTask w (return ()) >>= void . wait
 
 -- | Run action in worker and wait for result
 inWorkerWith :: (MonadIO m, MonadCatch m, MonadIO n) => (SomeException -> n a) -> Worker m -> m a -> n a
-inWorkerWith err w act = liftIO (pushTask w act) >>= (liftIO . waitCatch >=> either err return)
+inWorkerWith err w act = liftIO (sendTask w act) >>= (liftIO . waitCatch >=> either err return)
 
 -- | Run action in worker and wait for result
 inWorker :: (MonadIO m, MonadCatch m) => Worker m -> m a -> IO a
-inWorker w act = pushTask w act >>= liftIO . wait
-
--- | Run action in worker and wait for result
-inWorker_ :: (MonadIO m, MonadCatch m) => Worker m -> m a -> ExceptT SomeException IO a
-inWorker_ w act = liftIO (pushTask w act) >>= ExceptT . waitCatch
+inWorker w act = sendTask w act >>= liftIO . wait
− src/Data/Async.hs
@@ -1,47 +0,0 @@-module Data.Async (
-	Event(..),
-	event,
-	Async(..),
-	newAsync, readAsync, modifyAsync,
-
-	-- * Reexports
-	Group, NFData
-	) where
-
-import Control.DeepSeq (NFData, force)
-import Control.Monad (forM_)
-import Control.Concurrent
-
-import Data.Group (Group(..))
-
--- | Event on async value
-data Event a = Append a | Remove a | Clear | Modify (a -> a) | Action (a -> IO a)
-
--- | Event to function
-event :: Group a => Event a -> a -> IO a
-event (Append v) x = return $ add x v
-event (Remove v) x = return $ sub x v
-event Clear _ = return zero
-event (Modify p) x = return $ p x
-event (Action p) x = p x
-
-data Async a = Async {
-	asyncVar :: MVar a,
-	asyncEvents :: Chan (Event a) }
-
-newAsync :: (NFData a, Group a) => IO (Async a)
-newAsync = do
-	var <- newMVar zero
-	events <- newChan
-	_ <- forkIO $ do
-		evs <- getChanContents events
-		forM_ evs $ \e -> modifyMVar_ var $ \val -> do
-			x' <- event e val
-			force x' `seq` return x'
-	return $ Async var events
-
-readAsync :: Async a -> IO a
-readAsync = readMVar . asyncVar
-
-modifyAsync :: Async a -> Event a -> IO ()
-modifyAsync avar = writeChan (asyncEvents avar)
src/Data/Deps.hs view
@@ -1,70 +1,101 @@-{-# LANGUAGE FlexibleContexts, TypeFamilies #-}--module Data.Deps (-	Deps(..), depsMap,-	mapDeps,-	dep, deps,-	inverse, flatten-	) where--import Control.Lens-import Control.Monad.State-import Data.List (nub)-import Data.Map (Map)-import qualified Data.Map as M-import Data.Maybe (fromMaybe)---- | Dependency map-data Deps a = Deps {-	_depsMap :: Map a [a] }--depsMap :: Lens (Deps a) (Deps b) (Map a [a]) (Map b [b])-depsMap = lens _depsMap (const Deps)--instance Ord a => Monoid (Deps a) where-	mempty = Deps mempty-	mappend (Deps l) (Deps r) = Deps $ M.unionWith nubConcat l r--type instance Index (Deps a) = a-type instance IxValue (Deps a) = [a]--instance Ord a => Ixed (Deps a) where-	ix k = depsMap . ix k--instance Ord a => At (Deps a) where-	at k = depsMap . at k--mapDeps :: Ord b => (a -> b) -> Deps a -> Deps b-mapDeps f = Deps . M.mapKeys f . M.map (map f) . _depsMap---- | Make single dependency-dep :: a -> a -> Deps a-dep x y = deps x [y]---- | Make dependency for one target, note that order of dependencies is matter-deps :: a -> [a] -> Deps a-deps x ys = Deps $ M.singleton x ys---- | Inverse dependencies, i.e. make map where keys are dependencies and elements are targets depends on it-inverse :: Ord a => Deps a -> Deps a-inverse = mconcat . map (uncurry dep) . concatMap inverse' . M.toList . _depsMap where-	inverse' :: (a, [a]) -> [(a, a)]-	inverse' (m, ds) = zip ds (repeat m)---- | Flatten dependencies so that there will be no indirect dependencies-flatten :: Ord a => Deps a -> Deps a-flatten (Deps ds) = flip execState mempty . mapM_ flatten' . M.keys $ ds where-	-- flatten' :: a -> State (Deps a) [a]-	flatten' n = do-		d <- gets (M.lookup n . _depsMap)-		case d of-			Just d' -> return d'-			Nothing -> do-				let-					deps' = fromMaybe [] $ M.lookup n ds-				d'' <- (nub . concat . (++ [deps'])) <$> mapM flatten' deps'-				modify $ mappend (deps n d'')-				return d''--nubConcat :: Ord a => [a] -> [a] -> [a]-nubConcat xs ys = nub $ xs ++ ys+{-# LANGUAGE FlexibleContexts, TypeFamilies #-}
+
+module Data.Deps (
+	Deps(..), depsMap,
+	mapDeps,
+	dep, deps,
+	inverse,
+	DepsError(..), flatten,
+	selfDepend,
+	linearize
+	) where
+
+import Control.Lens
+import Control.Monad.State
+import Control.Monad.Except
+import Data.List (nub, intercalate)
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import Data.Maybe (fromMaybe)
+
+-- | Dependency map
+newtype Deps a = Deps {
+	_depsMap :: Map a [a] }
+
+depsMap :: Lens (Deps a) (Deps b) (Map a [a]) (Map b [b])
+depsMap = lens _depsMap (const Deps)
+
+instance Ord a => Monoid (Deps a) where
+	mempty = Deps mempty
+	mappend (Deps l) (Deps r) = Deps $ M.unionWith nubConcat l r
+
+instance Show a => Show (Deps a) where
+	show (Deps ds) = unlines [show d ++ " -> " ++ intercalate ", " (map show s) | (d, s) <- M.toList ds]
+
+type instance Index (Deps a) = a
+type instance IxValue (Deps a) = [a]
+
+instance Ord a => Ixed (Deps a) where
+	ix k = depsMap . ix k
+
+instance Ord a => At (Deps a) where
+	at k = depsMap . at k
+
+mapDeps :: Ord b => (a -> b) -> Deps a -> Deps b
+mapDeps f = Deps . M.mapKeys f . M.map (map f) . _depsMap
+
+-- | Make single dependency
+dep :: a -> a -> Deps a
+dep x y = deps x [y]
+
+-- | Make dependency for one target, note that order of dependencies is matter
+deps :: a -> [a] -> Deps a
+deps x ys = Deps $ M.singleton x ys
+
+-- | Inverse dependencies, i.e. make map where keys are dependencies and elements are targets depends on it
+inverse :: Ord a => Deps a -> Deps a
+inverse = mconcat . map (uncurry dep) . concatMap inverse' . M.toList . _depsMap where
+	inverse' :: (a, [a]) -> [(a, a)]
+	inverse' (m, ds) = zip ds (repeat m)
+
+newtype DepsError a =
+	CyclicDeps [a]
+	-- ^ Dependency cycle, list is cycle, where last item depends on first
+		deriving (Eq, Ord, Read)
+
+instance Show a => Show (DepsError a) where
+	show (CyclicDeps c) = "dependencies forms a cycle: " ++ concat [show d ++ " -> " | d <- c] ++ "..."
+
+-- | Flatten dependencies so that there will be no indirect dependencies
+flatten :: Ord a => Deps a -> Either (DepsError a) (Deps a)
+flatten s@(Deps ds) = fmap snd . flip execStateT mempty . mapM_ (flatten' s) . M.keys $ ds where
+	flatten' :: Ord a => Deps a -> a -> StateT ([a], Deps a) (Either (DepsError a)) [a]
+	flatten' s' n = do
+		path <- gets (view _1)
+		when (preview (reversed . each) path == Just n) $ throwError (CyclicDeps $ reverse path)
+		d <- gets (preview $ _2 . ix n)
+		case d of
+			Just d' -> return d'
+			Nothing -> pushPath n $ do
+				d'' <- (nub . concat . (++ [deps'])) <$> mapM (flatten' s') deps'
+				modify (over _2 $ mappend (deps n d''))
+				return d''
+				where
+					deps' = fromMaybe [] $ preview (ix n) s'
+	pushPath :: MonadState ([a], Deps a) m => a -> m b -> m b
+	pushPath p act = do
+		modify (over _1 (p:))
+		r <- act
+		modify (over _1 tail)
+		return r
+
+selfDepend :: Deps a -> Deps a
+selfDepend = Deps . M.mapWithKey (\s d -> d ++ [s]) . _depsMap
+
+-- | Linearize dependencies so that all items can be processed in this order,
+-- i.e. for each item all its dependencies goes before
+linearize :: Ord a => Deps a -> Either (DepsError a) [a]
+linearize = fmap (nub . concat . toListOf (depsMap . each) . selfDepend) . flatten
+
+nubConcat :: Ord a => [a] -> [a] -> [a]
+nubConcat xs ys = nub $ xs ++ ys
− src/Data/Group.hs
@@ -1,40 +0,0 @@-{-# LANGUAGE TypeSynonymInstances #-}--module Data.Group (-	Group(..),-	groupSum-	) where--import Data.List ((\\))-import Data.Map (Map)-import qualified Data.Map as M-import Data.Set (Set)-import qualified Data.Set as S---- | Group is monoid with invertibility--- But for our purposes we prefer two functions: `add` and `sub`.-class Eq a => Group a where-	add :: a -> a -> a-	sub :: a -> a -> a-	zero :: a--instance Eq a => Group [a] where-	add = (++)-	sub = (\\)-	zero = []--instance Ord a => Group (Set a) where-	add = S.union-	sub = S.difference-	zero = S.empty--instance (Ord k, Group a) => Group (Map k a) where-	add = M.unionWith add-	sub = M.differenceWith sub' where-		sub' x y = if z == zero then Nothing else Just z where-			z = sub x y-	zero = M.empty---- | Sums list-groupSum :: Group a => [a] -> a-groupSum = foldr add zero
src/Data/Help.hs view
@@ -1,21 +1,24 @@-module Data.Help (-	Help(..),-	indent, indentHelp, detailed, indented-	) where--class Help a where-	brief :: a -> String-	help :: a -> [String]--indent :: String -> String-indent = ('\t':)--indentHelp :: [String] -> [String]-indentHelp [] = []-indentHelp (h:hs) = h : map indent hs--detailed :: Help a => a -> [String]-detailed v = brief v : help v--indented :: Help a => a -> [String]-indented = indentHelp . detailed+module Data.Help (
+	Help(..),
+	indent, indentHelp, detailed, indented
+	) where
+
+import Data.Text (Text)
+import qualified Data.Text as T
+
+class Help a where
+	brief :: a -> Text
+	help :: a -> [Text]
+
+indent :: Text -> Text
+indent = T.cons '\t'
+
+indentHelp :: [Text] -> [Text]
+indentHelp [] = []
+indentHelp (h:hs) = h : map indent hs
+
+detailed :: Help a => a -> [Text]
+detailed v = brief v : help v
+
+indented :: Help a => a -> [Text]
+indented = indentHelp . detailed
+ src/Data/LookupTable.hs view
@@ -0,0 +1,46 @@+module Data.LookupTable (
+	LookupTable,
+	newLookupTable,
+	lookupTable, lookupTableM, cacheInTableM,
+	hasLookupTable,
+	cachedInTable
+	) where
+
+import Control.Monad.IO.Class
+import Control.Concurrent.MVar
+import Data.Map (Map)
+import qualified Data.Map as M
+
+-- | k-v table
+type LookupTable k v = MVar (Map k v)
+
+newLookupTable :: (Ord k, MonadIO m) => m (LookupTable k v)
+newLookupTable = liftIO $ newMVar mempty
+
+-- | Lookup, or insert if not exists
+lookupTable :: (Ord k, MonadIO m) => k -> v -> LookupTable k v -> m v
+lookupTable key value tbl = liftIO $ modifyMVar tbl $ \tbl' -> case M.lookup key tbl' of
+	Just value' -> return (tbl', value')
+	Nothing -> return (M.insert key value tbl', value)
+
+-- | Lookup, or insert if not exists
+lookupTableM :: (Ord k, MonadIO m) => k -> m v -> LookupTable k v -> m v
+lookupTableM key mvalue tbl = do
+	mv <- hasLookupTable key tbl
+	case mv of
+		Just value -> return value
+		Nothing -> do
+			value <- mvalue
+			lookupTable key value tbl
+
+-- | @lookupTableM@ with swapped args
+cacheInTableM :: (Ord k, MonadIO m) => LookupTable k v -> k -> m v -> m v
+cacheInTableM tbl key mvalue = lookupTableM key mvalue tbl
+
+-- | Just check existable
+hasLookupTable :: (Ord k, MonadIO m) => k -> LookupTable k v -> m (Maybe v)
+hasLookupTable key tbl = liftIO $ withMVar tbl $ return . M.lookup key
+
+-- | 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)
src/Data/Maybe/JustIf.hs view
@@ -1,16 +1,8 @@ module Data.Maybe.JustIf (
-	justIf, justWhen, soJust
+	justIf
 	) where
 
 -- | Return @Just@ if True
 justIf :: a -> Bool -> Maybe a
 x `justIf` True = Just x
 _ `justIf` False = Nothing
-
--- | Return @Just@ if @f x = True@
-justWhen :: a -> (a -> Bool) -> Maybe a
-x `justWhen` p = x `justIf` p x
-
--- | Flipped version of @justIf@
-soJust :: Bool -> a -> Maybe a
-soJust = flip justIf
src/HsDev.hs view
@@ -1,33 +1,31 @@-module HsDev (-	module Data.Default,--	module HsDev.Types,-	module HsDev.Error,-	module HsDev.Server.Base,-	module HsDev.Server.Commands,-	module HsDev.Client.Commands,-	module HsDev.Cache,-	module HsDev.Commands,-	module HsDev.Database,-	module HsDev.Inspect,-	module HsDev.Project,-	module HsDev.Scan,-	module HsDev.Symbols,-	module HsDev.Util-	) where--import Data.Default--import HsDev.Types-import HsDev.Error-import HsDev.Server.Base-import HsDev.Server.Commands-import HsDev.Client.Commands-import HsDev.Cache-import HsDev.Commands-import HsDev.Database-import HsDev.Inspect-import HsDev.Project-import HsDev.Scan-import HsDev.Symbols-import HsDev.Util+module HsDev (
+	module Data.Default,
+	module System.Directory.Paths,
+
+	module HsDev.Types,
+	module HsDev.Error,
+	module HsDev.Server.Base,
+	module HsDev.Server.Commands,
+	module HsDev.Client.Commands,
+	module HsDev.Inspect,
+	module HsDev.Project,
+	module HsDev.Scan,
+	module HsDev.Symbols,
+	module HsDev.Symbols.Resolve,
+	module HsDev.Util
+	) where
+
+import Data.Default
+import System.Directory.Paths
+
+import HsDev.Types
+import HsDev.Error
+import HsDev.Server.Base
+import HsDev.Server.Commands
+import HsDev.Client.Commands
+import HsDev.Inspect
+import HsDev.Project
+import HsDev.Scan
+import HsDev.Symbols
+import HsDev.Symbols.Resolve
+import HsDev.Util
− src/HsDev/Cache.hs
@@ -1,79 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module HsDev.Cache (-	escapePath,-	versionCache,-	packageDbCache,-	projectCache,-	standaloneCache,-	dump,-	load,-	writeVersion,-	readVersion,--	-- * Reexports-	Database-	) where--import Control.DeepSeq (force)-import Control.Exception-import Control.Lens (view)-import Data.Aeson (encode, eitherDecode)-import Data.Aeson.Encode.Pretty (encodePretty)-import qualified Data.ByteString.Lazy.Char8 as BS-import Data.Char (isAlphaNum)-import Data.List (intercalate)-import System.FilePath--import HsDev.PackageDb-import HsDev.Project-import HsDev.Database (Database)-import HsDev.Util (version)---- | Escape path-escapePath :: FilePath -> FilePath-escapePath = intercalate "." . map (filter isAlphaNum) . splitDirectories---- | Name of cache for version-versionCache :: FilePath-versionCache = "version" <.> "json"---- | Name of cache for cabal-packageDbCache :: PackageDb -> FilePath-packageDbCache GlobalDb = "global" <.> "json"-packageDbCache UserDb = "user" <.> "json"-packageDbCache (PackageDb p) = escapePath p <.> "json"---- | Name of cache for projects-projectCache :: Project -> FilePath-projectCache p = (escapePath . view projectPath $ p) <.> "json"---- | Name of cache for standalone files-standaloneCache :: FilePath-standaloneCache = "standalone" <.> "json"---- | Dump database to file-dump :: FilePath -> Database -> IO ()-dump file = BS.writeFile file . encodePretty---- | Load database from file, strict-load :: FilePath -> IO (Either String Database)-load file = handle onIO $ do-	cts <- BS.readFile file-	return $ force $ eitherDecode cts-	where-		onIO :: IOException -> IO (Either String Database)-		onIO _ = return $ Left $ "IO exception while reading cache from " ++ file---- | Write version-writeVersion :: FilePath -> IO ()-writeVersion file = BS.writeFile file $ encode version---- | Read version-readVersion :: FilePath -> IO (Maybe [Int])-readVersion file = handle onIO $ do-	cts <- BS.readFile file-	return $ either (const Nothing) id $ eitherDecode cts-	where-		onIO :: IOException -> IO (Maybe [Int])-		onIO _ = return Nothing
− src/HsDev/Cache/Structured.hs
@@ -1,72 +0,0 @@-module HsDev.Cache.Structured (-	dump, load,-	loadPackageDb, loadProject, loadFiles-	) where--import Control.DeepSeq-import Control.Exception-import Control.Lens (preview)-import Control.Monad.Except-import qualified Data.Map as M (assocs)-import System.Directory-import System.FilePath--import Data.Group (Group(zero))-import qualified HsDev.Cache as Cache-import HsDev.Database-import HsDev.Symbols hiding (loadProject)-import HsDev.Util---- | Write cache-dump :: FilePath -> Structured -> IO ()-dump dir db = do-	createDirectoryIfMissing True (dir </> "cabal")-	createDirectoryIfMissing True (dir </> "projects")-	forM_ (M.assocs $ structuredPackageDbs db) $ \(c, cdb) -> Cache.dump-		(dir </> "cabal" </> Cache.packageDbCache c)-		cdb-	forM_ (M.assocs $ structuredProjects db) $ \(p, pdb) -> Cache.dump-		(dir </> "projects" </> Cache.projectCache (project p))-		pdb-	files' <- either (const zero) id <$>-		handle wrapIO-		(Cache.load (dir </> Cache.standaloneCache))-	files' `deepseq` Cache.dump (dir </> Cache.standaloneCache) (files' `mappend` structuredFiles db)-	where-		wrapIO :: SomeException -> IO (Either String Database)-		wrapIO = return . Left . show---- | Load all cache-load :: FilePath -> IO (Either String Structured)-load dir = runExceptT $ join $ either throwError return <$> (structured <$> loadPackageDbs <*> loadProjects <*> loadStandaloneFiles) where-	loadPackageDbs = loadDir (dir </> "cabal")-	loadProjects = loadDir (dir </> "projects")-	loadStandaloneFiles = ExceptT $ Cache.load (dir </> Cache.standaloneCache)--	loadDir p = do-		fs <- liftE $ liftM (filter ((== ".json") . takeExtension)) $ directoryContents p-		mapM (ExceptT . Cache.load) fs---- | Load data from cache-loadData :: FilePath -> ExceptT String IO Database-loadData = liftExceptionM . ExceptT . Cache.load---- | Load cabal from cache-loadPackageDb :: PackageDb -> FilePath -> ExceptT String IO Structured-loadPackageDb c dir = do-	dat <- loadData (dir </> "cabal" </> Cache.packageDbCache c)-	ExceptT $ return $ structured [dat] [] mempty---- | Load project from cache-loadProject :: FilePath -> FilePath -> ExceptT String IO Structured-loadProject p dir = do-	dat <- loadData (dir </> "projects" </> Cache.projectCache (project p))-	ExceptT $ return $ structured [] [dat] mempty---- | Load standalone files-loadFiles :: (FilePath -> Bool) -> FilePath -> ExceptT String IO Structured-loadFiles f dir = do-	dat <- loadData (dir </> Cache.standaloneCache)-	ExceptT $ return $ structured [] [] $ filterDB f' (const False) dat-	where-		f' = maybe False f . preview (moduleIdLocation . moduleFile)
src/HsDev/Client/Commands.hs view
@@ -1,414 +1,622 @@-{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}--module HsDev.Client.Commands (-	runClient, runCommand-	) where--import Control.Applicative-import Control.Concurrent.MVar-import Control.Exception (displayException)-import Control.Lens (view, preview, _Just, (^..), each)-import Control.Monad-import Control.Monad.Except-import Control.Monad.Reader-import qualified Control.Monad.State as State-import Control.Monad.Catch (try, catch, bracket, SomeException(..))-import Data.Aeson hiding (Result, Error)-import Data.List-import Data.Foldable (toList)-import Data.Maybe-import qualified Data.Map as M-import Data.String (fromString)-import Data.Text (pack, unpack)-import qualified Data.Text as T (isInfixOf, isPrefixOf, isSuffixOf)-import System.Directory-import System.FilePath-import qualified System.Log.Simple as Log-import qualified System.Log.Simple.Base as Log-import Text.Regex.PCRE ((=~))--import qualified System.Directory.Watcher as W-import qualified Data.Async as A-import System.Directory.Paths-import Text.Format-import HsDev.Cache-import HsDev.Commands-import HsDev.Error-import qualified HsDev.Database.Async as DB-import HsDev.Server.Message as M-import HsDev.Server.Types-import HsDev.Sandbox hiding (findSandbox)-import qualified HsDev.Sandbox as S (findSandbox)-import HsDev.Symbols-import HsDev.Symbols.Resolve (resolveOne, scopeModule, exportsModule)-import HsDev.Symbols.Util-import qualified HsDev.Tools.AutoFix as AutoFix-import qualified HsDev.Tools.Cabal as Cabal-import HsDev.Tools.Ghc.Session-import qualified HsDev.Tools.Ghc.Compat as Compat-import qualified HsDev.Tools.Ghc.Check as Check-import qualified HsDev.Tools.Ghc.Types as Types-import qualified HsDev.Tools.Hayoo as Hayoo-import qualified HsDev.Tools.HLint as HLint-import qualified HsDev.Tools.Types as Tools-import HsDev.Util-import HsDev.Watcher--import qualified HsDev.Database.Update as Update--runClient :: (ToJSON a, ServerMonadBase m) => CommandOptions -> ClientM m a -> ServerM m Result-runClient copts = mapServerM toResult . runClientM where-	toResult :: (ToJSON a, ServerMonadBase m) => ReaderT CommandOptions m a -> m Result-	toResult act = liftM (either Error (Result . toJSON)) $ runReaderT (try act) copts-	mapServerM :: (m a -> n b) -> ServerM m a -> ServerM n b-	mapServerM f = ServerM . mapReaderT f . runServerM--toValue :: (ToJSON a, Monad m) => m a -> m Value-toValue = liftM toJSON--runCommand :: ServerMonadBase m => Command -> ClientM m Value-runCommand Ping = toValue $ return $ object ["message" .= ("pong" :: String)]-runCommand (Listen (Just l)) = case Log.level (pack l) of-	Nothing -> hsdevError $ OtherError $ "invalid log level: {}" ~~ l-	Just lev -> bracket (serverSetLogLevel lev) serverSetLogLevel $ \_ -> runCommand (Listen Nothing)-runCommand (Listen Nothing) = toValue $ do-	serverListen >>= mapM_ (\msg -> commandNotify (Notification $ object ["message" .= msg]))-runCommand (SetLogLevel l) = case Log.level (pack l) of-	Nothing -> hsdevError $ OtherError $ "invalid log level: {}" ~~ l-	Just lev -> toValue $ do-		lev' <- serverSetLogLevel lev-		Log.sendLog Log.Debug $ "log level changed from '{}' to '{}'" ~~ show lev' ~~ show lev-		Log.sendLog Log.Info $ "log level updated to: {}" ~~ show lev-runCommand (AddData cts) = toValue $ mapM_ updateData cts where-	updateData (AddedDatabase db) = toValue $ serverUpdateDB db-	updateData (AddedModule m) = toValue $ serverUpdateDB $ fromModule m-	updateData (AddedProject p) = toValue $ serverUpdateDB $ fromProject p-runCommand (Scan projs cabal sboxes fs paths' ghcs' docs' infer') = toValue $ do-	sboxes' <- getSandboxes sboxes-	updateProcess (Update.UpdateOptions [] ghcs' docs' infer') $ concat [-		map (\(FileSource f mcts) -> Update.scanFileContents ghcs' f mcts) fs,-		map (Update.scanProject ghcs') projs,-		map (Update.scanDirectory ghcs') paths',-		[Update.scanCabal ghcs' | cabal],-		map (Update.scanSandbox ghcs') sboxes']-runCommand (RefineDocs projs fs ms) = toValue $ do-	projects <- traverse findProject projs-	dbval <- getDb-	let-		filters = anyOf $ concat [-			map inProject projects,-			map inFile fs,-			map inModule ms]-		mods = selectModules (filters . view moduleId) dbval-	updateProcess (Update.UpdateOptions [] [] False False) [Update.scanDocs $ map (getInspected dbval) mods]-runCommand (InferTypes projs fs ms) = toValue $ do-	projects <- traverse findProject projs-	dbval <- getDb-	let-		filters = anyOf $ concat [-			map inProject projects,-			map inFile fs,-			map inModule ms]-		mods = selectModules (filters . view moduleId) dbval-	updateProcess (Update.UpdateOptions [] [] False False) [Update.inferModTypes $ map (getInspected dbval) mods]-runCommand (Remove projs cabal sboxes files) = toValue $ do-	db <- askSession sessionDatabase-	dbval <- getDb-	w <- askSession sessionWatcher-	projects <- traverse findProject projs-	sboxes' <- getSandboxes sboxes-	forM_ projects $ \proj -> do-		DB.clear db (return $ projectDB proj dbval)-		liftIO $ unwatchProject w proj-	dbPDbs <- mapM restorePackageDbStack $ databasePackageDbs dbval-	flip State.evalStateT dbPDbs $ do-		when cabal $ removePackageDbStack userDb-		forM_ sboxes' $ \sbox -> do-			pdbs <- lift $ sandboxPackageDbStack sbox-			removePackageDbStack pdbs-	forM_ files $ \file -> do-		DB.clear db (return $ filterDB (inFile file) (const False) dbval)-		let-			mloc = fmap (view moduleLocation) $ lookupFile file dbval-		maybe (return ()) (liftIO . unwatchModule w) mloc-	where-		-- We can safely remove package-db from db iff doesn't used by some of other package-dbs-		-- For example, we can't remove global-db if there are any other package-dbs, because all of them uses global-db-		-- We also can't remove stack snapshot package-db if there are some local package-db not yet removed-		canRemove pdbs = do-			from <- State.get-			return $ null $ filter (pdbs `isSubStack`) $ delete pdbs from-		-- Remove top of package-db stack if possible-		removePackageDb pdbs = do-			db <- lift $ askSession sessionDatabase-			dbval <- lift getDb-			w <- lift $ askSession sessionWatcher-			can <- canRemove pdbs-			when can $ do-				State.modify (delete pdbs)-				DB.clear db (return $ packageDbDB (topPackageDb pdbs) dbval)-				liftIO $ unwatchPackageDb w $ topPackageDb pdbs-		-- Remove package-db stack when possible-		removePackageDbStack = mapM_ removePackageDb . packageDbStacks-runCommand RemoveAll = toValue $ do-	db <- askSession sessionDatabase-	liftIO $ A.modifyAsync db A.Clear-	w <- askSession sessionWatcher-	wdirs <- liftIO $ readMVar (W.watcherDirs w)-	liftIO $ forM_ (M.toList wdirs) $ \(dir, (isTree, _)) -> (if isTree then W.unwatchTree else W.unwatchDir) w dir-runCommand (InfoModules fs) = toValue $ do-	dbval <- getDb-	filter' <- targetFilters fs-	return $ map (view moduleId) $ newestPackage $ selectModules (filter' . view moduleId) dbval-runCommand InfoPackages = toValue $ (ordNub . sort . 	mapMaybe (preview (moduleLocation . modulePackage . _Just)) . allModules) <$> getDb-runCommand InfoProjects = toValue $ (toList . databaseProjects) <$> getDb-runCommand InfoSandboxes = toValue $ databasePackageDbs <$> getDb-runCommand (InfoSymbol sq fs locals') = toValue $ do-	dbval <- liftM (localsDatabase locals') $ getDb-	filter' <- targetFilters fs-	return $ newestPackage $ filterMatch sq $-		concatMap moduleModuleDeclarations $ filter (filter' . view moduleId) $ allModules dbval-runCommand (InfoModule sq fs) = toValue $ do-	dbval <- getDb-	filter' <- targetFilters fs-	return $ newestPackage $ filterMatch sq $ filter (filter' . view moduleId) $ allModules dbval-runCommand (InfoResolve fpath exports) = toValue $ do-	dbval <- getSDb fpath-	let-		getScope-			| exports = exportsModule-			| otherwise = scopeModule-	m <- refineSourceModule fpath-	return $ getScope $ resolveOne dbval m-runCommand (InfoProject (Left projName)) = toValue $ findProject projName-runCommand (InfoProject (Right projPath)) = toValue $ liftIO $ searchProject projPath-runCommand (InfoSandbox sandbox') = toValue $ liftIO $ searchSandbox sandbox'-runCommand (Lookup nm fpath) = toValue $ do-	dbval <- getSDb fpath-	liftIO $ hsdevLift $ lookupSymbol dbval fpath nm-runCommand (Whois nm fpath) = toValue $ do-	dbval <- getSDb fpath-	liftIO $ hsdevLift $ whois dbval fpath nm-runCommand (ResolveScopeModules sq fpath) = toValue $ do-	dbval <- getSDb fpath-	liftM (filterMatch sq . map (view moduleId)) $ liftIO $ hsdevLift $ scopeModules dbval fpath-runCommand (ResolveScope sq global fpath) = toValue $ do-	dbval <- getSDb fpath-	liftM (filterMatch sq) $ liftIO $ hsdevLift $ scope dbval fpath global-runCommand (Complete input wide fpath) = toValue $ do-	dbval <- getSDb fpath-	liftIO $ hsdevLift $ completions dbval fpath input wide-runCommand (Hayoo hq p ps) = toValue $ liftM concat $ forM [p .. p + pred ps] $ \i -> liftM-	(mapMaybe Hayoo.hayooAsDeclaration . Hayoo.resultResult) $-	liftIO $ hsdevLift $ Hayoo.hayoo hq (Just i)-runCommand (CabalList packages) = toValue $ liftIO $ hsdevLift $ Cabal.cabalList packages-runCommand (Lint fs) = toValue $ do-	liftIO $ hsdevLift $ liftM concat $ mapM (\(FileSource f c) -> HLint.hlint f c) fs-runCommand (Check fs ghcs') = toValue $ Log.scope "check" $ do-	-- ensureUpToDate (Update.UpdateOptions [] ghcs' False False) fs-	let-		checkSome file fn = Log.scope "checkSome" $ do-			m <- setFileSourceSession ghcs' file-			inSessionGhc $ fn m-	liftM concat $ mapM (\(FileSource f c) -> checkSome f (\m -> Check.check ghcs' m c)) fs-runCommand (CheckLint fs ghcs') = toValue $ do-	-- ensureUpToDate (Update.UpdateOptions [] ghcs' False False) fs-	let-		checkSome file fn = Log.scope "checkSome" $ do-			m <- setFileSourceSession ghcs' file-			inSessionGhc $ fn m-	checkMsgs <- liftM concat $ mapM (\(FileSource f c) -> checkSome f (\m -> Check.check ghcs' m c)) fs-	lintMsgs <- liftIO $ hsdevLift $ liftM concat $ mapM (\(FileSource f c) -> HLint.hlint f c) fs-	return $ checkMsgs ++ lintMsgs-runCommand (Types fs ghcs') = toValue $ do-	-- ensureUpToDate (Update.UpdateOptions [] ghcs' False False) fs-	liftM concat $ forM fs $ \(FileSource file msrc) -> do-		m <- setFileSourceSession ghcs' file-		inSessionGhc $ Types.fileTypes ghcs' m msrc-runCommand (AutoFix (AutoFixShow ns)) = toValue $ return $ AutoFix.corrections ns-runCommand (AutoFix (AutoFixFix ns rest isPure)) = toValue $ do-	files <- liftM (ordNub . sort) $ mapM findPath $ mapMaybe (preview $ Tools.noteSource . moduleFile) ns-	let-		doFix :: FilePath -> Maybe String -> ([Tools.Note AutoFix.Correction], Maybe String)-		doFix file mcts = AutoFix.autoFix fCorrs (fUpCorrs, mcts) where-			findCorrs :: FilePath -> [Tools.Note AutoFix.Correction] -> [Tools.Note AutoFix.Correction]-			findCorrs f = filter ((== Just f) . preview (Tools.noteSource . moduleFile))-			fCorrs = findCorrs file ns-			fUpCorrs = findCorrs file rest-		runFix file-			| isPure = return $ fst $ doFix file Nothing-			| otherwise = do-				(corrs', Just cts') <- liftM (doFix file) $ liftIO $ Just <$> readFileUtf8 file-				liftIO $ writeFileUtf8 file cts'-				return corrs'-	liftM concat $ mapM runFix files-runCommand (GhcEval exprs mfile) = toValue $ do-	-- ensureUpToDate (Update.UpdateOptions [] [] False False) (maybeToList mfile)-	ghcw <- askSession sessionGhc-	case mfile of-		Nothing -> inSessionGhc ghciSession-		Just (FileSource f mcts) -> do-			m <- setFileSourceSession [] f-			inSessionGhc $ interpretModule m mcts-	async' <- liftIO $ pushTask ghcw $ do-		mapM (try . evaluate) exprs-	res <- waitAsync async'-	return $ map toValue' res-	where-		waitAsync :: CommandMonad m => Async a -> m a-		waitAsync a = liftIO (waitCatch a) >>= either (hsdevError . GhcError . displayException) return-		toValue' :: ToJSON a => Either SomeException a -> Value-		toValue' (Left (SomeException e)) = object ["fail" .= show e]-		toValue' (Right s) = toJSON s-runCommand Langs = toValue $ return $ Compat.languages-runCommand Flags = toValue $ return ["-f" ++ prefix ++ f |-	f <- Compat.flags,-	prefix <- ["", "no-"]]-runCommand (Link hold) = toValue $ commandLink >> when hold commandHold-runCommand Exit = toValue serverExit--targetFilters :: CommandMonad m => [TargetFilter] -> m (ModuleId -> Bool)-targetFilters fs = do-	fs_ <- mapM targetFilter fs-	return $ foldr (liftM2 (&&)) (const True) fs_--targetFilter :: CommandMonad m => TargetFilter -> m (ModuleId -> Bool)-targetFilter f = case f of-	TargetProject proj -> liftM inProject $ findProject proj-	TargetFile file -> liftM inFile $ refineSourceFile file-	TargetModule mname -> return $ inModule mname-	TargetDepsOf dep -> liftM inDeps $ findDep dep-	TargetPackageDb pdb -> return $ inPackageDb pdb-	TargetCabal -> return $ inPackageDbStack userDb-	TargetSandbox sbox -> liftM inPackageDbStack $ findSandbox sbox >>= sandboxPackageDbStack-	TargetPackage pkg -> liftM inPackage $ refinePackage pkg-	TargetSourced -> return byFile-	TargetStandalone -> return standalone---- Helper functions---- | Canonicalize paths-findPath :: (CommandMonad m, Paths a) => a -> m a-findPath = paths findPath' where-	findPath' :: CommandMonad m => FilePath -> m FilePath-	findPath' f = do-		r <- commandRoot-		liftIO $ canonicalizePath (normalise $ if isRelative f then r </> f else f)---- | Find sandbox by path-findSandbox :: CommandMonad m => FilePath -> m Sandbox-findSandbox fpath = do-	fpath' <- findPath fpath-	sbox <- liftIO $ S.findSandbox fpath'-	maybe (hsdevError $ FileNotFound fpath') return sbox---- | Get source file-refineSourceFile :: CommandMonad m => FilePath -> m FilePath-refineSourceFile fpath = do-	fpath' <- findPath fpath-	db' <- getDb-	maybe (hsdevError (NotInspected $ FileModule fpath' Nothing)) return $ do-		m' <- lookupFile fpath' db'-		preview (moduleLocation . moduleFile) m'---- | Get module by source-refineSourceModule :: CommandMonad m => FilePath -> m Module-refineSourceModule fpath = do-	fpath' <- findPath fpath-	db' <- getDb-	maybe (hsdevError (NotInspected $ FileModule fpath' Nothing)) return $ lookupFile fpath' db'---- | Set session by source-setFileSourceSession :: CommandMonad m => [String] -> FilePath -> m Module-setFileSourceSession opts fpath = do-	m <- refineSourceModule fpath-	inSessionGhc $ targetSession opts m-	return m---- | Ensure package exists-refinePackage :: CommandMonad m => String -> m String-refinePackage pkg = do-	db' <- getDb-	if pkg `elem` (allPackages db' ^.. each . packageName)-		then return pkg-		else hsdevError (PackageNotFound pkg)---- | Get list of enumerated sandboxes-getSandboxes :: CommandMonad m => [FilePath] -> m [Sandbox]-getSandboxes = traverse (findPath >=> findSandbox)---- | Find project by name or path-findProject :: CommandMonad m => String -> m Project-findProject proj = do-	db' <- getDb-	proj' <- liftM addCabal $ findPath proj-	let-		resultProj =-			refineProject db' (project proj') <|>-			find ((== proj) . view projectName) (databaseProjects db')-	maybe (hsdevError $ ProjectNotFound proj) return resultProj-	where-		addCabal p-			| takeExtension p == ".cabal" = p-			| otherwise = p </> (takeBaseName p <.> "cabal")---- | Find dependency: it may be source, project file or project name, also returns sandbox found-findDep :: CommandMonad m => String -> m (Project, Maybe FilePath, PackageDbStack)-findDep depName = do-	depPath <- findPath depName-	proj <- msum [-		do-			p <- liftIO (locateProject depPath)-			p' <- maybe (hsdevError $ ProjectNotFound depName) return p-			liftIO $ loadProject p',-		findProject depName]-	let-		src-			| takeExtension depPath == ".hs" = Just depPath-			| otherwise = Nothing-	pdbs <- searchPackageDbStack $ view projectPath proj-	return (proj, src, pdbs)---- FIXME: Doesn't work for file without project--- | Check if project or source depends from this module-inDeps :: (Project, Maybe FilePath, PackageDbStack) -> ModuleId -> Bool-inDeps (proj, src, pdbs) = liftM2 (&&) (restrictPackageDbStack pdbs) deps' where-	deps' = case src of-		Nothing -> inDepsOfProject proj-		Just src' -> inDepsOfFile proj src'---- | Bring locals to top scope to search within them if 'locals' flag set-localsDatabase :: Bool -> Database -> Database-localsDatabase True = databaseLocals-localsDatabase False = id---- | Get actual DB state-getDb :: SessionMonad m => m Database-getDb = askSession sessionDatabase >>= liftIO . DB.readAsync---- | Get DB with filtered sanxboxes for file-getSDb :: SessionMonad m => FilePath -> m Database-getSDb fpath = do-	dbval <- getDb-	pdbs <- searchPackageDbStack fpath-	return $ filterDB (restrictPackageDbStack pdbs) (const True) dbval---- | Run DB update action-updateProcess :: ServerMonadBase m => Update.UpdateOptions -> [Update.UpdateM m ()] -> ClientM m ()-updateProcess uopts acts = Update.runUpdate uopts $ mapM_ runAct acts where-	runAct act = catch act onError-	onError e = Log.sendLog Log.Error $ "{}" ~~ (e :: HsDevError)---- | Ensure file is up to date--- ensureUpToDate :: ServerMonadBase m => Update.UpdateOptions -> [FileSource] -> ClientM m ()--- ensureUpToDate uopts fs = updateProcess uopts [Update.scanFileContents (view Update.updateGhcOpts uopts) f mcts | FileSource f mcts <- fs]---- | Filter declarations with prefix and infix-filterMatch :: Symbol a => SearchQuery -> [a] -> [a]-filterMatch (SearchQuery q st) = filter match' where-	match' m = case st of-		SearchExact -> fromString q == symbolName m-		SearchPrefix -> fromString q `T.isPrefixOf` symbolName m-		SearchInfix -> fromString q `T.isInfixOf` symbolName m-		SearchSuffix -> fromString q `T.isSuffixOf` symbolName m-		SearchRegex -> unpack (symbolName m) =~ q+{-# LANGUAGE OverloadedStrings, FlexibleContexts, TypeOperators, TypeApplications #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module HsDev.Client.Commands (
+	runClient, runCommand
+	) where
+
+import Control.Arrow (second)
+import Control.Concurrent.MVar
+import Control.Exception (displayException)
+import Control.Lens hiding ((.=), (<.>))
+import Control.Monad
+import Control.Monad.Morph
+import Control.Monad.Except
+import Control.Monad.Reader
+import qualified Control.Monad.State as State
+import Control.Monad.Catch (try, catch, bracket, SomeException(..))
+import Data.Aeson hiding (Result, Error)
+import Data.List
+import Data.Maybe
+import qualified Data.Map.Strict as M
+import Data.Text (Text, pack, unpack)
+import qualified Data.Text as T (append)
+import System.Directory
+import System.FilePath
+import qualified System.Log.Simple as Log
+import qualified System.Log.Simple.Base as Log
+import Text.Read (readMaybe)
+
+import qualified System.Directory.Watcher as W
+import System.Directory.Paths
+import Text.Format
+import HsDev.Error
+import HsDev.Database.SQLite as SQLite
+import HsDev.Inspect (preload, asModule)
+import HsDev.Scan (upToDate, getFileContents)
+import HsDev.Server.Message as M
+import HsDev.Server.Types
+import HsDev.Sandbox hiding (findSandbox)
+import qualified HsDev.Sandbox as S (findSandbox)
+import HsDev.Symbols
+import qualified HsDev.Tools.AutoFix as AutoFix
+import qualified HsDev.Tools.Cabal as Cabal
+import HsDev.Tools.Ghc.Session
+import HsDev.Tools.Ghc.Worker (clearTargets)
+import qualified HsDev.Tools.Ghc.Compat as Compat
+import qualified HsDev.Tools.Ghc.Check as Check
+import qualified HsDev.Tools.Ghc.Types as Types
+import qualified HsDev.Tools.Hayoo as Hayoo
+import qualified HsDev.Tools.HDocs as HDocs
+import qualified HsDev.Tools.HLint as HLint
+import qualified HsDev.Tools.Types as Tools
+import HsDev.Util
+import HsDev.Watcher
+
+import qualified HsDev.Database.Update as Update
+
+runClient :: (ToJSON a, ServerMonadBase m) => CommandOptions -> ClientM m a -> ServerM m Result
+runClient copts = mapServerM toResult . runClientM where
+	toResult :: (ToJSON a, ServerMonadBase m) => ReaderT CommandOptions m a -> m Result
+	toResult act = liftM errorToResult $ runReaderT (try (try act)) copts
+	mapServerM :: (m a -> n b) -> ServerM m a -> ServerM n b
+	mapServerM f = ServerM . mapReaderT f . runServerM
+	errorToResult :: ToJSON a => Either SomeException (Either HsDevError a) -> Result
+	errorToResult = either (Error . UnhandledError . displayException) (either Error (Result . toJSON))
+
+toValue :: (ToJSON a, Monad m) => m a -> m Value
+toValue = liftM toJSON
+
+runCommand :: ServerMonadBase m => Command -> ClientM m Value
+runCommand Ping = toValue $ return $ object ["message" .= ("pong" :: String)]
+runCommand (Listen (Just l)) = case Log.level (pack l) of
+	Nothing -> hsdevError $ OtherError $ "invalid log level: {}" ~~ l
+	Just lev -> bracket (serverSetLogLevel lev) serverSetLogLevel $ \_ -> runCommand (Listen Nothing)
+runCommand (Listen Nothing) = toValue $ do
+	serverListen >>= mapM_ (commandNotify . Notification . toJSON)
+runCommand (SetLogLevel l) = case Log.level (pack l) of
+	Nothing -> hsdevError $ OtherError $ "invalid log level: {}" ~~ l
+	Just lev -> toValue $ do
+		lev' <- serverSetLogLevel lev
+		Log.sendLog Log.Debug $ "log level changed from '{}' to '{}'" ~~ show lev' ~~ show lev
+		Log.sendLog Log.Info $ "log level updated to: {}" ~~ show lev
+runCommand (Scan projs cabal sboxes fs paths' ghcs' docs' infer') = toValue $ do
+	sboxes' <- getSandboxes sboxes
+	updateProcess (Update.UpdateOptions [] ghcs' docs' infer') $ concat [
+		[Update.scanCabal ghcs' | cabal],
+		map (Update.scanSandbox ghcs') sboxes',
+		[Update.scanFiles (zip fs (repeat ghcs'))],
+		map (Update.scanProject ghcs') projs,
+		map (Update.scanDirectory ghcs') paths']
+runCommand (SetFileContents f mcts) = toValue $ serverSetFileContents f mcts
+runCommand (RefineDocs projs fs)
+	| HDocs.hdocsSupported = toValue $ do
+		projects <- traverse findProject projs
+		mods <- do
+			projMods <- liftM concat $ forM projects $ \proj -> do
+				ms <- loadModules "select id from modules where cabal == ? and json_extract(tags, '$.docs') is null"
+					(Only $ proj ^. projectCabal)
+				p <- SQLite.loadProject (proj ^. projectCabal)
+				return $ set (each . moduleId . moduleLocation . moduleProject) (Just p) ms
+			fileMods <- liftM concat $ forM fs $ \f ->
+				loadModules "select id from modules where file == ? and json_extract(tags, '$.docs') is null"
+					(Only f)
+			return $ projMods ++ fileMods
+		updateProcess def [Update.scanDocs mods]
+	| otherwise = hsdevError $ OtherError "docs not supported"
+runCommand (InferTypes projs fs) = toValue $ do
+	projects <- traverse findProject projs
+	mods <- do
+		projMods <- liftM concat $ forM projects $ \proj -> do
+			ms <- loadModules "select id from modules where cabal == ? and json_extract(tags, '$.types') is null"
+				(Only $ proj ^. projectCabal)
+			p <- SQLite.loadProject (proj ^. projectCabal)
+			return $ set (each . moduleId . moduleLocation . moduleProject) (Just p) ms
+		fileMods <- liftM concat $ forM fs $ \f ->
+			loadModules "select id from modules where file == ? and json_extract(tags, '$.types') is null"
+				(Only f)
+		return $ projMods ++ fileMods
+	updateProcess def [Update.inferModTypes mods]
+runCommand (Remove projs cabal sboxes files) = toValue $ withSqlConnection $ SQLite.transaction_ SQLite.Immediate $ do
+	let
+		-- We can safely remove package-db from db iff doesn't used by some of other package-dbs
+		-- For example, we can't remove global-db if there are any other package-dbs, because all of them uses global-db
+		-- We also can't remove stack snapshot package-db if there are some local package-db not yet removed
+		canRemove pdbs = do
+			from' <- State.get
+			return $ null $ filter (pdbs `isSubStack`) $ delete pdbs from'
+		-- Remove top of package-db stack if possible
+		removePackageDb' pdbs = do
+			w <- lift $ askSession sessionWatcher
+			can <- canRemove pdbs
+			when can $ do
+				State.modify (delete pdbs)
+				ms <- liftM (map fromOnly) $ query_
+					"select m.id from modules as m, package_dbs as ps where m.package_name == ps.package_name and m.package_version == ps.package_version;"
+				removePackageDb (topPackageDb pdbs)
+				mapM_ SQLite.removeModule ms
+				liftIO $ unwatchPackageDb w $ topPackageDb pdbs
+		-- Remove package-db stack when possible
+		removePackageDbStack = mapM_ removePackageDb' . packageDbStacks
+	w <- askSession sessionWatcher
+	projects <- traverse findProject projs
+	sboxes' <- getSandboxes sboxes
+	forM_ projects $ \proj -> do
+		ms <- liftM (map fromOnly) $ query "select id from modules where cabal == ?;" (Only $ proj ^. projectCabal)
+		SQLite.removeProject proj
+		mapM_ SQLite.removeModule ms
+		liftIO $ unwatchProject w proj
+
+	allPdbs <- liftM (map fromOnly) $ query_ @(Only PackageDb) "select package_db from package_dbs;"
+	dbPDbs <- inSessionGhc $ mapM restorePackageDbStack allPdbs
+	flip State.evalStateT dbPDbs $ do
+		when cabal $ removePackageDbStack userDb
+		forM_ sboxes' $ \sbox -> do
+			pdbs <- lift $ inSessionGhc $ sandboxPackageDbStack sbox
+			removePackageDbStack pdbs
+
+	forM_ files $ \file -> do
+		ms <- query @_ @(ModuleId :. Only Int) (toQuery $ qModuleId `mappend` select_ ["mu.id"] [] ["mu.file == ?"]) (Only file)
+		forM_ ms $ \(m :. Only i) -> do
+			SQLite.removeModule i
+			liftIO . unwatchModule w $ (m ^. moduleLocation)
+runCommand RemoveAll = toValue $ do
+	SQLite.purge
+	w <- askSession sessionWatcher
+	wdirs <- liftIO $ readMVar (W.watcherDirs w)
+	liftIO $ forM_ (M.toList wdirs) $ \(dir, (isTree, _)) -> (if isTree then W.unwatchTree else W.unwatchDir) w dir
+runCommand InfoPackages = toValue $
+	query_ @ModulePackage "select package_name, package_version from package_dbs;"
+runCommand InfoProjects = toValue $ do
+	ps <- query_ @(Only Int :. Project) "select p.id, p.name, p.cabal, p.version from projects as p;"
+	forM ps $ \(Only pid :. proj) -> do
+		libs <- query @_ @Library "select l.modules, b.depends, b.language, b.extensions, b.ghc_options, b.source_dirs, b.other_modules from libraries as l, build_infos as b where (l.project_id == ?) and (l.build_info_id == b.id);"
+			(Only pid)
+		exes <- query @_ @Executable "select e.name, e.path, b.depends, b.language, b.extensions, b.ghc_options, b.source_dirs, b.other_modules from executables as e, build_infos as b where (e.project_id == ?) and (e.build_info_id == b.id);"
+			(Only pid)
+		tsts <- query @_ @Test "select t.name, t.enabled, t.main, b.depends, b.language, b.extensions, b.ghc_options, b.source_dirs, b.other_modules from tests as t, build_infos as b where (t.project_id == ?) and (t.build_info_id == b.id);"
+			(Only pid)
+		return $
+			set (projectDescription . _Just . projectLibrary) (listToMaybe libs) .
+			set (projectDescription . _Just . projectExecutables) exes .
+			set (projectDescription . _Just . projectTests) tsts $
+			proj
+runCommand InfoSandboxes = toValue $ do
+	rs <- query_ @(Only PackageDb) "select distinct package_db from package_dbs;"
+	return [pdb | Only pdb <- rs]
+runCommand (InfoSymbol sq filters True _) = toValue $ do
+	let
+		(conds, params) = targetFilters "m" (Just "s") filters
+	rs <- queryNamed @SymbolId (toQuery $ qSymbolId `mappend` where_ ["s.name like :pattern escape '\\'"] `mappend` where_ conds)
+		([":pattern" := likePattern sq] ++ params)
+	return rs
+runCommand (InfoSymbol sq filters False _) = toValue $ do
+	let
+		(conds, params) = targetFilters "m" (Just "s") filters
+	queryNamed @Symbol (toQuery $ qSymbol `mappend` where_ ["s.name like :pattern escape '\\'"] `mappend` where_ conds)
+		([":pattern" := likePattern sq] ++ params)
+runCommand (InfoModule sq filters h _) = toValue $ do
+	let
+		(conds, params) = targetFilters "mu" Nothing filters
+	rs <- queryNamed @(Only Int :. ModuleId) (toQuery $ select_ ["mu.id"] [] [] `mappend` qModuleId `mappend` where_ ["mu.name like :pattern escape '\\'"] `mappend` where_ conds)
+		([":pattern" := likePattern sq] ++ params)
+	if h
+		then return (toJSON $ map (\(_ :. m) -> m) rs)
+		else liftM toJSON $ forM rs $ \(Only mid :. mheader) -> do
+			[(docs, fixities)] <- query @_ @(Maybe Text, Maybe Value) "select m.docs, m.fixities from modules as m where (m.id == ?);"
+				(Only mid)
+			let
+				fixities' = fromMaybe [] (fixities >>= fromJSON')
+			exports' <- query @_ @Symbol (toQuery $ qSymbol `mappend` select_ []
+				["exports as e"]
+				["e.module_id == ?", "e.symbol_id == s.id"])
+				(Only mid)
+			return $ Module mheader docs mempty 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 $ qSymbol `mappend` select_ []
+		["projects_modules_scope as pms", "modules as srcm", "exports as e"]
+		[
+			"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 == ?"])
+		(fpath ^. path, nm)
+runCommand (Whois nm fpath) = toValue $ do
+	let
+		q = nameModule $ toName nm
+		ident = nameIdent $ toName nm
+	query @_ @Symbol (toQuery $ qSymbol `mappend` select_ []
+		["modules as srcm", "scopes as sc"]
+		[
+			"srcm.id == sc.module_id",
+			"s.id == sc.symbol_id",
+			"srcm.file == ?",
+			"sc.qualifier is ?",
+			"sc.name == ?"])
+		(fpath ^. path, q, ident)
+runCommand (Whoat l c fpath) = toValue $ do
+	rs <- query @_ @Symbol (toQuery $ qSymbol `mappend` select_ []
+		["names as n", "modules as srcm"]
+		[
+			"srcm.id == n.module_id",
+			"m.name == n.resolved_module",
+			"s.name == n.resolved_name",
+			"m.id in (select srcm.id union select module_id from projects_modules_scope where (((cabal is null) and (srcm.cabal is null)) or (cabal == srcm.cabal)))",
+			"srcm.file == ?",
+			"(?, ?) between (n.line, n.column) and (n.line_to, n.column_to)"])
+		(fpath ^. path, l, c)
+	locals <- do
+		defs <- query @_ @(ModuleId :. (Text, Int, Int, Maybe Text)) (toQuery $ qModuleId `mappend` select_
+			["n.name", "n.def_line", "n.def_column", "n.inferred_type"]
+			["names as n"]
+			[
+				"mu.id == n.module_id",
+				"n.def_line is not null",
+				"n.def_column is not null",
+				"mu.file == ?",
+				"(?, ?) between (n.line, n.column) and (n.line_to, n.column_to)"])
+			(fpath ^. path, l, c)
+		return [
+			Symbol {
+				_symbolId = SymbolId nm mid,
+				_symbolDocs = Nothing,
+				_symbolPosition = Just (Position defLine defColumn),
+				_symbolInfo = Function ftype
+			} | (mid :. (nm, defLine, defColumn, ftype)) <- defs]
+	return $ rs ++ locals
+runCommand (ResolveScopeModules sq fpath) = toValue $ do
+	pids <- query @_ @(Only (Maybe Path)) "select m.cabal from modules as m where (m.file == ?);"
+		(Only $ fpath ^. path)
+	case pids of
+		[] -> hsdevError $ OtherError $ "module at {} not found" ~~ fpath
+		[Only proj] -> query @_ @ModuleId (toQuery $ qModuleId `mappend` select_ []
+			["projects_modules_scope as msc"]
+			[
+				"msc.module_id == mu.id",
+				"msc.cabal is ?",
+				"mu.name like ? escape '\\'"])
+			(proj, likePattern sq)
+		_ -> fail "Impossible happened: several projects for one module"
+runCommand (ResolveScope sq fpath) = toValue $ do
+	rs <- query @_ @(SymbolId :. Only (Maybe Text)) (toQuery $ qSymbolId `mappend` select_ ["sc.qualifier"]
+		["scopes as sc", "modules as srcm"]
+		[
+			"srcm.id == sc.module_id",
+			"sc.symbol_id == s.id",
+			"srcm.file == ?",
+			"s.name like ? escape '\\'"])
+		(fpath ^. path, likePattern sq)
+	return [ScopeSymbol q s | (s :. Only q) <- rs]
+runCommand (FindUsages nm) = toValue $ do
+	let
+		q = nameModule $ toName nm
+		ident = nameIdent $ toName nm
+	query @_ @SymbolUsage (toQuery $ qSymbol `mappend` qModuleId `mappend` select_
+		["n.line", "n.column"]
+		["names as n"]
+		[
+			"m.name == n.resolved_module",
+			"s.name == n.resolved_name",
+			"mu.id == n.module_id",
+			"n.resolved_module == ? or ? is null",
+			"n.resolved_name == ?"])
+		(q, q, ident)
+runCommand (Complete input True fpath) = toValue $
+	query @_ @Symbol (toQuery $ qSymbol `mappend` select_ []
+		["modules as srcm"]
+		[
+			"m.id in (select srcm.id union select module_id from projects_modules_scope where (((cabal is null) and (srcm.cabal is null)) or (cabal == srcm.cabal)))",
+			"msrc.file == ?",
+			"s.name like ? escape '\\'"])
+		(fpath ^. path, likePattern (SearchQuery input SearchPrefix))
+runCommand (Complete input False fpath) = toValue $
+	query @_ @Symbol (toQuery $ qSymbol `mappend` select_ []
+		["completions as c", "modules as srcm"]
+		[
+			"c.module_id == srcm.id",
+			"c.symbol_id == s.id",
+			"srcm.file == ?",
+			"c.completion like ? escape '\\'"])
+		(fpath ^. path, likePattern (SearchQuery input SearchPrefix))
+runCommand (Hayoo hq p ps) = toValue $ liftM concat $ forM [p .. p + pred ps] $ \i -> liftM
+	(mapMaybe Hayoo.hayooAsSymbol . Hayoo.resultResult) $
+	liftIO $ hsdevLift $ Hayoo.hayoo hq (Just i)
+runCommand (CabalList packages') = toValue $ liftIO $ hsdevLift $ Cabal.cabalList $ map unpack packages'
+runCommand (UnresolvedSymbols fs) = toValue $ liftM concat $ forM fs $ \f -> do
+	rs <- query @_ @(Maybe String, String, Int, Int) "select n.qualifier, n.name, n.line, n.column from modules as m, names as n where (m.id == n.module_id) and (m.file == ?) and (n.resolve_error is not null);"
+		(Only $ f ^. path)
+	return $ map (\(m, nm, line, column) -> object [
+		"qualifier" .= m,
+		"name" .= nm,
+		"line" .= line,
+		"column" .= column]) rs
+runCommand (Lint fs) = toValue $ liftM concat $ forM fs $ \fsrc -> do
+	FileSource f c <- actualFileContents fsrc
+	liftIO $ hsdevLift $ HLint.hlint (view path f) c
+runCommand (Check fs ghcs' clear) = toValue $ Log.scope "check" $ do
+	let
+		checkSome file fn = Log.scope "checkSome" $ do
+			Log.sendLog Log.Trace $ "setting file source session for {}" ~~ file
+			m <- setFileSourceSession ghcs' file
+			Log.sendLog Log.Trace $ "file source session set"
+			inSessionGhc $ do
+				when clear $ clearTargets
+				fn m
+	fs' <- mapM actualFileContents fs
+	liftM concat $ mapM (\(FileSource f c) -> checkSome f (\m -> Check.check m c)) fs'
+runCommand (CheckLint fs ghcs' clear) = toValue $ do
+	let
+		checkSome file fn = Log.scope "checkSome" $ do
+			Log.sendLog Log.Trace $ "setting file source session for {}" ~~ file
+			m <- setFileSourceSession ghcs' file
+			Log.sendLog Log.Trace $ "file source session set"
+			inSessionGhc $ do
+				when clear $ clearTargets
+				fn m
+	fs' <- mapM actualFileContents fs
+	checkMsgs <- liftM concat $ mapM (\(FileSource f c) -> checkSome f (\m -> Check.check m c)) fs'
+	lintMsgs <- liftIO $ hsdevLift $ liftM concat $ mapM (\(FileSource f c) -> HLint.hlint (view path f) c) fs'
+	return $ checkMsgs ++ lintMsgs
+runCommand (Types fs ghcs' clear) = toValue $ do
+	liftM concat $ forM fs $ \fsrc@(FileSource file msrc) -> do
+		mcached' <- getCached file msrc
+		FileSource _ msrc' <- actualFileContents fsrc
+		maybe (updateTypes file msrc') return mcached'
+	where
+		getCached :: ServerMonadBase m => Path -> Maybe Text -> ClientM m (Maybe [Tools.Note Types.TypedExpr])
+		getCached _ (Just _) = return Nothing
+		getCached file' Nothing = do
+			actual' <- sourceUpToDate file'
+			mid <- query @_ @((Bool, Int) :. ModuleId) (toQuery $ select_ ["json_extract(tags, '$.types') is 1", "mu.id"] [] [] `mappend` qModuleId `mappend` where_ ["mu.file = ?"])
+				(Only file')
+			when (length mid > 1) $ Log.sendLog Log.Warning $ "multiple modules with same file = {}" ~~ file'
+			when (null mid) $ hsdevError $ NotInspected $ FileModule file' Nothing
+			let
+				[(hasTypes', mid') :. modId] = mid
+			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 {
+						Tools._noteSource = modId ^. moduleLocation,
+						Tools._noteRegion = rgn,
+						Tools._noteLevel = Nothing,
+						Tools._note = set Types.typedExpr Nothing texpr }
+				else return Nothing
+
+		updateTypes file msrc = do
+			m <- setFileSourceSession ghcs' file
+			types' <- inSessionGhc $ do
+				when clear $ clearTargets
+				Types.fileTypes m msrc
+			updateProcess def [Update.setModTypes (m ^. moduleId) types']
+			return $ set (each . Tools.note . Types.typedExpr) Nothing types'
+runCommand (AutoFix ns) = toValue $ return $ AutoFix.corrections ns
+runCommand (Refactor ns rest isPure) = toValue $ do
+	files <- liftM (ordNub . sort) $ mapM findPath $ mapMaybe (preview $ Tools.noteSource . moduleFile) ns
+	let
+		runFix file = do
+			when (not isPure) $ do
+				liftIO $ readFileUtf8 (view path file) >>= writeFileUtf8 (view path file) . AutoFix.refact fixRefacts'
+			return newCorrs'
+			where
+				findCorrs :: Path -> [Tools.Note AutoFix.Refact] -> [Tools.Note AutoFix.Refact]
+				findCorrs f = filter ((== Just f) . preview (Tools.noteSource . moduleFile))
+				fixCorrs' = findCorrs file ns
+				upCorrs' = findCorrs file rest
+				fixRefacts' = fixCorrs' ^.. each . Tools.note
+				newCorrs' = AutoFix.update fixRefacts' upCorrs'
+	liftM concat $ mapM runFix files
+runCommand (Rename nm newName fpath) = toValue $ do
+	m <- refineSourceModule fpath
+	let
+		mname = m ^. moduleId . moduleName
+		makeNote mloc r = Tools.Note {
+			Tools._noteSource = mloc,
+			Tools._noteRegion = r,
+			Tools._noteLevel = Nothing,
+			Tools._note = AutoFix.Refact "rename" (AutoFix.replace (AutoFix.fromRegion r) newName) }
+
+	defRenames <- do
+		-- FIXME: Doesn't take scope into account. If you have modules with same names in different project, it will rename symbols from both
+		defRegions <- query @_ @Region "select n.line, n.column, n.line_to, n.column_to from names as n, modules as m where m.id == n.module_id and m.name == ? and n.name == ? and def_line is not null;" (
+			mname,
+			nm)
+		return $ map (makeNote (m ^. moduleId . moduleLocation)) defRegions
+
+	usageRenames <- do
+		-- FIXME: Same as above: doesn't take scope into account
+		usageRegions <- query @_ @(Only Path :. Region) "select m.file, n.line, n.column, n.line_to, n.column_to from names as n, modules as m where n.module_id == m.id and m.file is not null and n.resolved_module == ? and n.resolved_name == ?;" (
+			mname,
+			nm)
+		return $ map (\(Only p :. r) -> makeNote (FileModule p Nothing) r) usageRegions
+
+	return $ defRenames ++ usageRenames
+runCommand (GhcEval exprs mfile) = toValue $ do
+	ghcw <- askSession sessionGhc
+	mfile' <- traverse actualFileContents mfile
+	case mfile' of
+		Nothing -> inSessionGhc ghciSession
+		Just (FileSource f mcts) -> do
+			m <- setFileSourceSession [] f
+			inSessionGhc $ interpretModule m mcts
+	async' <- liftIO $ sendTask ghcw $ do
+		mapM (try . evaluate) exprs
+	res <- waitAsync async'
+	return $ map toValue' res
+	where
+		waitAsync :: CommandMonad m => Async a -> m a
+		waitAsync a = liftIO (waitCatch a) >>= either (hsdevError . GhcError . displayException) return
+		toValue' :: ToJSON a => Either SomeException a -> Value
+		toValue' (Left (SomeException e)) = object ["fail" .= show e]
+		toValue' (Right s) = toJSON s
+runCommand Langs = toValue $ return $ Compat.languages
+runCommand Flags = toValue $ return ["-f" ++ prefix ++ f |
+	f <- Compat.flags,
+	prefix <- ["", "no-"]]
+runCommand (Link hold) = toValue $ commandLink >> when hold commandHold
+runCommand StopGhc = toValue $ do
+	inSessionGhc $ do
+		ms <- findSessionBy (const True)
+		forM_ ms $ \s -> do
+			Log.sendLog Log.Trace $ "stopping session: {}" ~~ s
+			deleteSession s
+runCommand Exit = toValue serverExit
+
+-- TODO: Implement `targetFilter` for sql
+
+targetFilter :: Text -> Maybe Text -> TargetFilter -> (Text, [NamedParam])
+targetFilter mtable _ (TargetProject proj) = (
+	"{t}.cabal in (select cabal from projects where name == :project or cabal == :project)" ~~ ("t" ~% mtable),
+	[":project" := proj])
+targetFilter mtable _ (TargetFile f) = ("{t}.file == :file" ~~ ("t" ~% mtable), [":file" := f])
+targetFilter mtable Nothing (TargetModule nm) = ("{t}.name == :module_name" ~~ ("t" ~% mtable), [":module_name" := nm])
+targetFilter mtable (Just stable) (TargetModule nm) = (
+	"({t}.name == :module_name) or ({s}.id in (select e.symbol_id from exports as e, modules as em where e.module_id == em.id and em.name == :module_name))"
+		~~ ("t" ~% mtable)
+		~~ ("s" ~% stable),
+	[":module_name" := nm])
+targetFilter mtable _ (TargetPackage nm) = ("{t}.package_name == :package_name" ~~ ("t" ~% mtable), [":package_name" := nm])
+targetFilter mtable _ TargetInstalled = ("{t}.package_name is not null" ~~ ("t" ~% mtable), [])
+targetFilter mtable _ TargetSourced = ("{t}.file is not null" ~~ ("t" ~% mtable), [])
+targetFilter mtable _ TargetStandalone = ("{t}.file is not null and {t}.cabal is null" ~~ ("t" ~% mtable), [])
+
+targetFilters :: Text -> Maybe Text -> [TargetFilter] -> ([Text], [NamedParam])
+targetFilters mtable stable = second concat . unzip . map (targetFilter mtable stable)
+
+likePattern :: SearchQuery -> Text
+likePattern (SearchQuery input stype) = case stype of
+	SearchExact -> escapedInput
+	SearchPrefix -> escapedInput `T.append` "%"
+	SearchInfix -> "%" `T.append` escapedInput `T.append` "%"
+	SearchSuffix -> "%" `T.append` escapedInput
+	where
+		escapedInput = escapeLike input
+
+instance ToJSON Log.Message where
+	toJSON m = object [
+		"time" .= Log.messageTime m,
+		"level" .= show (Log.messageLevel m),
+		"component" .= show (Log.messageComponent m),
+		"scope" .= show (Log.messageScope m),
+		"text" .= Log.messageText m]
+
+instance FromJSON Log.Message where
+	parseJSON = withObject "log-message" $ \v -> Log.Message <$>
+		(v .:: "time") <*>
+		((v .:: "level") >>= maybe (fail "invalid level") return . readMaybe) <*>
+		(read <$> (v .:: "component")) <*>
+		(read <$> (v .:: "scope")) <*>
+		(v .:: "text")
+
+-- Helper functions
+
+-- | Canonicalize paths
+findPath :: (CommandMonad m, Paths a) => a -> m a
+findPath = paths findPath' where
+	findPath' :: CommandMonad m => FilePath -> m FilePath
+	findPath' f = do
+		r <- commandRoot
+		liftIO $ canonicalizePath (normalise $ if isRelative f then r </> f else f)
+
+-- | Find sandbox by path
+findSandbox :: CommandMonad m => Path -> m Sandbox
+findSandbox fpath = do
+	fpath' <- findPath fpath
+	sbox <- liftIO $ S.findSandbox fpath'
+	maybe (hsdevError $ FileNotFound fpath') return sbox
+
+-- | Check if source file up to date
+sourceUpToDate :: CommandMonad m => Path -> m Bool
+sourceUpToDate fpath = do
+	fpath' <- findPath fpath
+	insps <- query @_ @Inspection "select inspection_time, inspection_opts from modules where file = ?;" (Only fpath')
+	when (length insps > 1) $ Log.sendLog Log.Warning $ "multiple modules with same file = {}" ~~ fpath'
+	maybe
+		(return False)
+		(upToDate (FileModule fpath' Nothing) [])
+		(listToMaybe insps)
+
+-- | Get source file
+-- refineSourceFile :: CommandMonad m => Path -> m Path
+-- refineSourceFile fpath = do
+-- 	fpath' <- findPath fpath
+-- 	fs <- liftM (map fromOnly) $ query "select file from modules where file == ?;" (Only fpath')
+-- 	case fs of
+-- 		[] -> hsdevError (NotInspected $ FileModule fpath' Nothing)
+-- 		(f:_) -> do
+-- 			when (length fs > 1) $ Log.sendLog Log.Warning $ "multiple modules with same file = {}" ~~ fpath'
+-- 			return f
+
+-- | Get module by source
+refineSourceModule :: CommandMonad m => Path -> m Module
+refineSourceModule fpath = do
+	fpath' <- findPath fpath
+	ids <- query "select id, cabal from modules where file == ?;" (Only fpath')
+	case ids of
+		[] -> hsdevError (NotInspected $ FileModule fpath' Nothing)
+		((i, mcabal):_) -> do
+			when (length ids > 1) $ Log.sendLog Log.Warning $ "multiple modules with same file = {}" ~~ fpath'
+			m <- SQLite.loadModule i
+			case mcabal of
+				Nothing -> do
+					[insp] <- query @_ @Inspection "select inspection_time, inspection_opts from modules where id = ?;" (Only i)
+					fresh' <- upToDate (m ^. moduleId . moduleLocation) [] insp
+					if fresh'
+						then return m
+						else do
+							defs <- askSession sessionDefines
+							p' <- liftIO $ preload (m ^. moduleId . moduleName) defs [] (m ^. moduleId . moduleLocation) Nothing
+							return $ set moduleImports (p' ^. asModule . moduleImports) m
+				Just cabal' -> do
+					proj' <- SQLite.loadProject cabal'
+					return $ set (moduleId . moduleLocation . moduleProject) (Just proj') m
+
+-- | Get file contents
+actualFileContents :: CommandMonad m => FileSource -> m FileSource
+actualFileContents (FileSource fpath Nothing) = fmap (FileSource fpath) (fmap (fmap snd) $ getFileContents fpath)
+actualFileContents fcts = return fcts
+
+-- | Set session by source
+setFileSourceSession :: CommandMonad m => [String] -> Path -> m Module
+setFileSourceSession opts fpath = do
+	m <- refineSourceModule fpath
+	inSessionGhc $ targetSession opts m
+	return m
+
+-- | Ensure package exists
+-- refinePackage :: CommandMonad m => Text -> m Text
+-- refinePackage pkg = do
+-- 	[(Only exists)] <- query "select count(*) > 0 from package_dbs where package_name == ?;" (Only pkg)
+-- 	when (not exists) $ hsdevError (PackageNotFound pkg)
+-- 	return pkg
+
+-- | Get list of enumerated sandboxes
+getSandboxes :: CommandMonad m => [Path] -> m [Sandbox]
+getSandboxes = traverse (findPath >=> findSandbox)
+
+-- | Find project by name or path
+findProject :: CommandMonad m => Text -> m Project
+findProject proj = do
+	proj' <- liftM addCabal $ findPath proj
+	ps <- liftM (map fromOnly) $ query "select cabal from projects where (cabal == ?) or (name == ?);" (view path proj', proj)
+	case ps of
+		[] -> hsdevError $ ProjectNotFound proj
+		_ -> SQLite.loadProject (head ps)
+	where
+		addCabal p
+			| takeExtension (view path p) == ".cabal" = p
+			| otherwise = over path (\p' -> p' </> (takeBaseName p' <.> "cabal")) p
+
+-- | Run DB update action
+updateProcess :: ServerMonadBase m => Update.UpdateOptions -> [Update.UpdateM IO ()] -> ClientM m ()
+updateProcess uopts acts = hoist liftIO $ do
+	copts <- getOptions
+	inSessionUpdater $ hoist (flip runReaderT copts) $ runClientM $ mapM_ (Update.runUpdate uopts . runAct) acts
+	where
+		runAct act = catch act onError
+		onError e = Log.sendLog Log.Error $ "{}" ~~ (e :: HsDevError)
− src/HsDev/Commands.hs
@@ -1,193 +0,0 @@-{-# LANGUAGE RankNTypes, FlexibleContexts #-}--module HsDev.Commands (-	-- * Commands-	findDeclaration, findModule,-	fileModule,-	lookupSymbol,-	whois,-	scopeModules, scope,-	completions,-	moduleCompletions,--	-- * Filters-	checkModule, checkDeclaration, restrictPackageDb, restrictPackageDbStack, visibleFrom,-	splitIdentifier,--	-- * Helpers-	fileCtx, fileCtxMaybe,--	-- * Reexports-	module HsDev.Database,-	module HsDev.Symbols.Types,-	module Control.Monad.Except-	) where--import Control.Applicative-import Control.Lens (view, set, each, toListOf)-import Control.Monad.Except-import Data.List (delete)-import Data.Maybe-import Data.String (fromString)-import qualified Data.Text as T (isPrefixOf, split, unpack)-import System.Directory (canonicalizePath)--import System.Directory.Paths--import HsDev.Database-import HsDev.Project-import HsDev.Symbols-import HsDev.Symbols.Resolve-import HsDev.Symbols.Types-import HsDev.Symbols.Util-import HsDev.Tools.Base (matchRx, at_)-import HsDev.Util (liftE, ordNub)---- | Find declaration by name-findDeclaration :: Database -> String -> ExceptT String IO [ModuleDeclaration]-findDeclaration db ident = return $ selectDeclarations checkName db where-	checkName :: ModuleDeclaration -> Bool-	checkName m =-		(view (moduleDeclaration . declarationName) m == fromString iname) &&-		(maybe True ((view (declarationModuleId . moduleIdName) m ==) . fromString) qname)--	(qname, iname) = splitIdentifier ident---- | Find module by name-findModule :: Database -> String -> ExceptT String IO [Module]-findModule db mname = return $ selectModules ((== fromString mname) . view moduleName) db---- | Find module in file-fileModule :: Database -> FilePath -> ExceptT String IO Module-fileModule db src = do-	src' <- liftE $ canonicalizePath src-	maybe (throwError $ "File '" ++ src' ++ "' not found") return $ lookupFile src' db---- | Find project of module-getProject :: Database -> Project -> ExceptT String IO Project-getProject db p = do-	p' <- liftE $ canonicalize p-	maybe (throwError $ "Project " ++ view projectCabal p' ++ " not found") return $-		refineProject db p'---- | Lookup visible within project/cabal symbol-lookupSymbol :: Database -> FilePath -> String -> ExceptT String IO [ModuleDeclaration]-lookupSymbol db file ident = do-	(_, mthis, mproj) <- fileCtx db file-	liftM-		(filter $ checkModule $ allOf [-			visibleFrom mproj mthis,-			maybe (const True) inModule qname])-		(newestPackage <$> findDeclaration db iname)-	where-		(qname, iname) = splitIdentifier ident---- | Whois symbol in scope-whois :: Database -> FilePath -> String -> ExceptT String IO [Declaration]-whois db file ident = do-	(_, mthis, mproj) <- fileCtx db file-	return $-		newestPackage $ filter checkDecl $-		view moduleDeclarations $ scopeModule $ resolveOne (fileDeps file mproj db) $-		moduleLocals mthis-	where-		(qname, iname) = splitIdentifier ident-		checkDecl d = fmap fromString qname `elem` scopes d && view declarationName d == fromString iname---- | Accessible modules-scopeModules :: Database -> FilePath -> ExceptT String IO [Module]-scopeModules db file = do-	(file', mthis, mproj) <- fileCtxMaybe db file-	newestPackage <$> case mproj of-		Nothing -> return $ maybe id (:) mthis $ selectModules (installed . view moduleId) db-		Just proj -> let deps' = deps file' proj in-			return $ concatMap (\p -> selectModules (p . view moduleId) db) [-				inProject proj,-				\m -> any (`inPackage` m) deps']-	where-		deps f p = delete (view projectName p) $ concatMap (view infoDepends) $ fileTargets p f---- | Symbols in scope-scope :: Database -> FilePath -> Bool -> ExceptT String IO [Declaration]-scope db file False = do-	(_, mthis, mproj) <- fileCtx db file-	return $ view moduleDeclarations $ scopeModule $ resolveOne (fileDeps file mproj db) mthis-scope db file True = concatMap (view moduleDeclarations) <$> scopeModules db file---- | Completions-completions :: Database -> FilePath -> String -> Bool -> ExceptT String IO [Declaration]-completions db file prefix wide = do-	(_, mthis, mproj) <- fileCtx db file-	return $-		toListOf (each . minimalDecl) $ newestPackage $ filter checkDecl $-		view moduleDeclarations $ scopeModule $ resolveOne (fileDeps file mproj db) $-		dropImportLists mthis-	where-		(qname, iname) = splitIdentifier prefix-		checkDecl d = fmap fromString qname `elem` scopes d && fromString iname `T.isPrefixOf` view declarationName d-		dropImportLists m-			| wide = set (moduleImports . each . importList) Nothing m-			| otherwise = m---- | Module completions-moduleCompletions :: Database -> [Module] -> String -> ExceptT String IO [String]-moduleCompletions _ ms prefix = return $ map T.unpack $ ordNub $ completions' $ map (view moduleName) ms where-	completions' = mapMaybe getNext where-		getNext m-			| fromString prefix `T.isPrefixOf` m = listToMaybe $ map snd $ dropWhile (uncurry (==)) $ zip (T.split (== '.') $ fromString prefix) (T.split (== '.') m)-			| otherwise = Nothing---- | Check module-checkModule :: (ModuleId -> Bool) -> (ModuleDeclaration -> Bool)-checkModule = (. view declarationModuleId)---- | Check declaration-checkDeclaration :: (Declaration -> Bool) -> (ModuleDeclaration -> Bool)-checkDeclaration = (. view moduleDeclaration)---- | Allow only selected cabal sandbox-restrictPackageDb :: PackageDb -> ModuleId -> Bool-restrictPackageDb pdb m = inPackageDb pdb m || not (installed m)---- | Allow only selected cabal sandboxes-restrictPackageDbStack :: PackageDbStack -> ModuleId -> Bool-restrictPackageDbStack pdbs m = any (`inPackageDb` m) (packageDbs pdbs) || not (installed m)---- | Check whether module is visible from source file-visibleFrom :: Maybe Project -> Module -> ModuleId -> Bool-visibleFrom (Just p) this m = visible p (view moduleId this) m-visibleFrom Nothing this m = view moduleId this == m || installed m---- | Split identifier into module name and identifier itself-splitIdentifier :: String -> (Maybe String, String)-splitIdentifier name = fromMaybe (Nothing, name) $ do-	groups <- matchRx "(([A-Z][\\w']*\\.)*)(.*)" name-	return (dropDot <$> groups 1, groups `at_` 3)-	where-		dropDot :: String -> String-		dropDot "" = ""-		dropDot s = init s---- | Get context file and project-fileCtx :: Database -> FilePath -> ExceptT String IO (FilePath, Module, Maybe Project)-fileCtx db file = do-	file' <- liftE $ canonicalizePath file-	mthis <- fileModule db file'-	mproj <- traverse (getProject db) $ projectOf $ view moduleId mthis-	return (file', mthis, mproj)---- | Try get context file-fileCtxMaybe :: Database -> FilePath -> ExceptT String IO (FilePath, Maybe Module, Maybe Project)-fileCtxMaybe db file = ((\(f, m, p) -> (f, Just m, p)) <$> fileCtx db file) <|> onlyProj where-	onlyProj = do-		file' <- liftE $ canonicalizePath file-		mproj <- liftE $ locateProject file'-		mproj' <- traverse (getProject db) mproj-		return (file', Nothing, mproj')---- | Restrict only modules file depends on-fileDeps :: FilePath -> Maybe Project -> Database -> Database-fileDeps file mproj = filterDB fileDeps' (const True) where-	fileDeps' = liftM2 (||)-		(maybe (const True) inProject mproj)-		(\m -> any (`inDepsOfTarget` m) (fromMaybe [] $ fileTargets <$> mproj <*> pure file))
− src/HsDev/Database.hs
@@ -1,253 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module HsDev.Database (-	Database(..),-	databaseIntersection, nullDatabase, databaseLocals, databasePackageDbs, allModules, allDeclarations, allPackages,-	fromModule, fromProject,-	filterDB,-	projectDB, packageDbDB, packageDbStackDB, standaloneDB,-	selectModules, selectDeclarations, lookupModule, lookupInspected, lookupFile, refineProject,-	getInspected,--	append, remove,--	Structured(..),-	structured, structurize, merge,--	Map-	) where--import Control.Lens (set, view, preview, _Just, _Right, each, (^..))-import Control.Monad (msum, join)-import Control.DeepSeq (NFData(..))-import Data.Aeson-import Data.Either (rights)-import Data.Foldable (find)-import Data.Function (on)-import Data.Group (Group(..))-import Data.Map (Map)-import Data.Maybe-import qualified Data.Map as M--import HsDev.Symbols-import HsDev.Symbols.Util-import HsDev.Util ((.::), ordNub, mapBy)---- | HsDev database-data Database = Database {-	databaseModules :: [InspectedModule],-	databaseProjects :: [Project] }-		deriving (Eq, Ord)--instance NFData Database where-	rnf (Database ms ps) = rnf ms `seq` rnf ps--instance Group Database where-	add old new = Database {-		databaseModules = M.elems $ mapBy (view inspectedId) (databaseModules new) `M.union` mapBy (view inspectedId) (databaseModules old),-		databaseProjects = M.elems $ M.unionWith mergeProject-			(mapBy (view projectCabal) (databaseProjects new))-			(mapBy (view projectCabal) (databaseProjects old)) }-		where-			mergeProject pl pr = set projectDescription (msum [view projectDescription pl, view projectDescription pr]) pl-	sub old new = Database {-		databaseModules = M.elems $ mapBy (view inspectedId) (databaseModules old) `M.difference` mapBy (view inspectedId) (databaseModules new),-		databaseProjects = M.elems $ mapBy (view projectCabal) (databaseProjects old) `M.difference` mapBy (view projectCabal) (databaseProjects new) }-	zero = Database [] []--instance Monoid Database where-	mempty = zero-	mappend = add--instance ToJSON Database where-	toJSON (Database ms ps) = object [-		"modules" .= ms,-		"projects" .= ps]--instance FromJSON Database where-	parseJSON = withObject "database" $ \v -> Database <$>-		(v .:: "modules") <*>-		(v .:: "projects")---- | Database intersection, prefers first database data-databaseIntersection :: Database -> Database -> Database-databaseIntersection l r = mempty {-	databaseModules = M.elems $ mapBy (view inspectedId) (databaseModules l) `M.intersection` mapBy (view inspectedId) (databaseModules r),-	databaseProjects = M.elems $ mapBy (view projectCabal) (databaseProjects l) `M.intersection` mapBy (view projectCabal) (databaseProjects r) }---- | Check if database is empty-nullDatabase :: Database -> Bool-nullDatabase db = null (databaseModules db) && null (databaseProjects db)---- | Bring all locals to scope-databaseLocals :: Database -> Database-databaseLocals db = db {-	databaseModules = fmap (fmap moduleLocals) (databaseModules db) }---- | All scanned sandboxes-databasePackageDbs :: Database -> [PackageDb]-databasePackageDbs db = ordNub $ databaseModules db ^.. each . inspectionResult . _Right . moduleLocation . modulePackageDb---- | All modules-allModules :: Database -> [Module]-allModules = rights . fmap (view inspectionResult) . databaseModules---- | All declarations-allDeclarations :: Database -> [ModuleDeclaration]-allDeclarations db = do-	m <- allModules db-	moduleModuleDeclarations m---- | All packages-allPackages :: Database -> [ModulePackage]-allPackages = ordNub . mapMaybe (preview (moduleLocation . modulePackage . _Just)) . allModules---- | Make database from module-fromModule :: InspectedModule -> Database-fromModule m = zero {-	databaseModules = [m] }---- | Make database from project-fromProject :: Project -> Database-fromProject p = zero {-	databaseProjects = [p] }---- | Filter database by predicate-filterDB :: (ModuleId -> Bool) -> (Project -> Bool) -> Database -> Database-filterDB m p db = mempty {-	databaseModules = filter (either (const False) (m . view moduleId) . view inspectionResult) (databaseModules db),-	databaseProjects = filter p (databaseProjects db) }---- | Project database-projectDB :: Project -> Database -> Database-projectDB proj = filterDB (inProject proj) (((==) `on` view projectCabal) proj)---- | Package-db database-packageDbDB :: PackageDb -> Database -> Database-packageDbDB pdb = filterDB (inPackageDb pdb) (const False)--packageDbStackDB :: PackageDbStack -> Database -> Database-packageDbStackDB pdbs = filterDB (inPackageDbStack pdbs) (const False)---- | Standalone database-standaloneDB :: Database -> Database-standaloneDB = filterDB check' (const False) where-	check' m = standalone m && byFile m---- | Select module by predicate-selectModules :: (Module -> Bool) -> Database -> [Module]-selectModules p = filter p . allModules---- | Select declaration by predicate-selectDeclarations :: (ModuleDeclaration -> Bool) -> Database -> [ModuleDeclaration]-selectDeclarations p = filter p . allDeclarations---- | Lookup module by its location and name-lookupModule :: ModuleLocation -> Database -> Maybe Module-lookupModule mloc db = do-	m <- find ((== mloc) . view inspectedId) $ databaseModules db-	either (const Nothing) Just $ view inspectionResult m---- | Lookup inspected module-lookupInspected :: ModuleLocation -> Database -> Maybe InspectedModule-lookupInspected mloc db = find ((== mloc) . view inspectedId) $ databaseModules db---- | Lookup module by its source file-lookupFile :: FilePath -> Database -> Maybe Module-lookupFile f = listToMaybe . selectModules (inFile f . view moduleId)---- | Refine project-refineProject :: Database -> Project -> Maybe Project-refineProject db proj = find ((== view projectCabal proj) . view projectCabal) $ databaseProjects db---- | Get inspected module-getInspected :: Database -> Module -> InspectedModule-getInspected db m = fromMaybe err $ find ((== view moduleLocation m) . view inspectedId) $ databaseModules db where-	err = error "Impossible happened: getInspected"---- | Append database-append :: Database -> Database -> Database-append = add---- | Remove database-remove :: Database -> Database -> Database-remove = sub---- | Structured database-data Structured = Structured {-	structuredPackageDbs :: Map PackageDb Database,-	structuredProjects :: Map FilePath Database,-	structuredFiles :: Database }-		deriving (Eq, Ord)--instance NFData Structured where-	rnf (Structured cs ps fs) = rnf cs `seq` rnf ps `seq` rnf fs--instance Group Structured where-	add old new = Structured {-		structuredPackageDbs = structuredPackageDbs new `M.union` structuredPackageDbs old,-		structuredProjects = structuredProjects new `M.union` structuredProjects old,-		structuredFiles = structuredFiles old `add` structuredFiles new }-	sub old new = Structured {-		structuredPackageDbs = structuredPackageDbs old `M.difference` structuredPackageDbs new,-		structuredProjects = structuredProjects old `M.difference` structuredProjects new,-		structuredFiles = structuredFiles old `sub` structuredFiles new }-	zero = Structured zero zero zero--instance Monoid Structured where-	mempty = zero-	mappend = add--instance ToJSON Structured where-	toJSON (Structured cs ps fs) = object [-		"cabals" .= M.elems cs,-		"projects" .= M.elems ps,-		"files" .= fs]--instance FromJSON Structured where-	parseJSON = withObject "structured" $ \v -> join $-		either fail return <$> (structured <$>-			(v .:: "cabals") <*>-			(v .:: "projects") <*>-			(v .:: "files"))--structured :: [Database] -> [Database] -> Database -> Either String Structured-structured cs ps fs = Structured <$> mkMap keyCabal cs <*> mkMap keyProj ps <*> pure fs where-	mkMap :: Ord a => (Database -> Either String a) -> [Database] -> Either String (Map a Database)-	mkMap key dbs = do-		keys <- mapM key dbs-		return $ M.fromList $ zip keys dbs-	keyCabal :: Database -> Either String PackageDb-	keyCabal db = unique-		"No cabal"-		"Different module cabals"-		(ordNub <$> mapM getPackageDb (allModules db))-		where-			getPackageDb m = case view moduleLocation m of-				InstalledModule c _ _ -> Right c-				_ -> Left "Module have no cabal"-	keyProj :: Database -> Either String FilePath-	keyProj db = unique-		"No project"-		"Different module projects"-		(return (databaseProjects db ^.. each . projectCabal))-	-- Check that list results in one element-	unique :: String -> String -> Either String [a] -> Either String a-	unique _ _ (Left e) = Left e-	unique no _ (Right []) = Left no-	unique _ _ (Right [x]) = Right x-	unique _ much (Right _) = Left much--structurize :: Database -> Structured-structurize db = Structured cs ps fs where-	cs = M.fromList [(c, packageDbDB c db) | c <- ordNub (mapMaybe modPackageDb (allModules db))]-	ps = M.fromList [(pname, projectDB (project pname) db) | pname <- databaseProjects db ^.. each . projectCabal]-	fs = standaloneDB db--merge :: Structured -> Database-merge (Structured cs ps fs) = mconcat $ M.elems cs ++ M.elems ps ++ [fs]--modPackageDb :: Module -> Maybe PackageDb-modPackageDb m = case view moduleLocation m of-	InstalledModule c _ _ -> Just c-	_ -> Nothing
− src/HsDev/Database/Async.hs
@@ -1,30 +0,0 @@-module HsDev.Database.Async (
-	update, clear, wait,
-	module Data.Async
-	) where
-
-import Control.Concurrent.MVar
-import Control.Monad.IO.Class (MonadIO(..))
-import Control.DeepSeq (force)
-
-import Data.Async
-
-import HsDev.Database (Database)
-
-update :: MonadIO m => Async Database -> m Database -> m ()
-update db act = do
-	db' <- act
-	force db' `seq` liftIO (modifyAsync db (Append db'))
-
-clear :: MonadIO m => Async Database -> m Database -> m ()
-clear db act = do
-	db' <- act
-	force db' `seq` liftIO (modifyAsync db (Remove db'))
-
--- | This function is used to ensure that all previous updates were applied
-wait :: MonadIO m => Async Database -> m ()
-wait db = liftIO $ do
-	waitVar <- newEmptyMVar
-	modifyAsync db (Action $ \d -> putMVar waitVar () >> return d)
-	-- FIXME: what if async process is dead?
-	takeMVar waitVar
+ src/HsDev/Database/SQLite.hs view
@@ -0,0 +1,527 @@+{-# LANGUAGE OverloadedStrings, TypeOperators, TypeApplications #-}
+
+module HsDev.Database.SQLite (
+	initialize, purge,
+	privateMemory, sharedMemory,
+	query, query_, queryNamed, execute, execute_, executeMany, executeNamed,
+	withTemporaryTable,
+	updatePackageDb, removePackageDb, insertPackageDb,
+	updateProject, removeProject, insertProject, insertBuildInfo,
+	updateModule,
+	removeModuleContents, removeModule,
+	insertModuleSymbols,
+	upsertModule,
+	lookupModuleLocation, lookupModule,
+	lookupSymbol,
+	lastRow,
+
+	loadModule, loadModules,
+	loadProject,
+
+	-- * Utils
+	escapeLike,
+
+	-- * Reexports
+	module Database.SQLite.Simple,
+	module HsDev.Database.SQLite.Select,
+	module HsDev.Database.SQLite.Instances,
+	module HsDev.Database.SQLite.Transaction
+	) where
+
+import Control.Lens hiding ((.=))
+import Control.Monad
+import Control.Monad.Catch
+import Control.Monad.IO.Class
+import Data.Aeson hiding (Error)
+import Data.Generics.Uniplate.Operations
+import Data.List (intercalate)
+import Data.Maybe
+import Data.String
+import qualified Data.Set as S
+import Data.Text (Text)
+import qualified Data.Text as T
+import Database.SQLite.Simple hiding (query, query_, queryNamed, execute, execute_, executeNamed, executeMany, withTransaction)
+import qualified Database.SQLite.Simple as SQL (query, query_, queryNamed, execute, execute_, executeNamed, executeMany, withTransaction)
+import Distribution.Text (display)
+import Language.Haskell.Exts.Syntax hiding (Name, Module)
+import Language.Haskell.Extension ()
+import System.Log.Simple
+import Text.Format
+
+import System.Directory.Paths
+
+import HsDev.Database.SQLite.Instances
+import HsDev.Database.SQLite.Schema
+import HsDev.Database.SQLite.Select
+import HsDev.Database.SQLite.Transaction
+import qualified HsDev.Display as Display
+import HsDev.Error
+import HsDev.PackageDb.Types
+import HsDev.Project.Types
+import HsDev.Symbols.Name
+import HsDev.Symbols.Parsed
+import HsDev.Symbols.Types hiding (loadProject)
+import qualified HsDev.Symbols.Parsed as P
+import qualified HsDev.Symbols.Name as Name
+import HsDev.Server.Types
+import HsDev.Util
+
+-- | Initialize database
+initialize :: String -> IO Connection
+initialize p = do
+	conn <- open p
+	SQL.execute_ conn "pragma case_sensitive_like = true;"
+	[Only hasTables] <- SQL.query_ conn "select count(*) > 0 from sqlite_master where type == 'table';"
+	goodVersion <- if hasTables
+		then do
+			[Only equalVersion] <- SQL.query conn "select sum(json(value) == json(?)) > 0 from hsdev where option == 'version';" (Only $ toJSON version)
+			return equalVersion
+		else return True
+	unless goodVersion $
+		-- TODO: Completely drop schema to reinitialize
+		hsdevError $ OtherError "Not implemented: dropping schema of db"
+	when (not hasTables || not goodVersion) $ SQL.withTransaction conn $ do
+		mapM_ (SQL.execute_ conn) commands
+		SQL.execute @(Text, Value) conn "insert into hsdev values (?, ?);" ("version", toJSON version)
+	return conn
+
+purge :: SessionMonad m => m ()
+purge = do
+	tables <- query_ @(Only String) "select name from sqlite_master where type == 'table';"
+	forM_ tables $ \(Only table) ->
+		execute_ $ fromString $ "delete from {};" ~~ table
+
+-- | Private memory for db
+privateMemory :: String
+privateMemory = ":memory:"
+
+-- | Shared db in memory
+sharedMemory :: String
+sharedMemory = "file::memory:?cache=shared"
+
+-- | Retries for simple queries
+retried :: (MonadIO m, MonadCatch m) => m a -> m a
+retried = retry def
+
+query :: (ToRow q, FromRow r, SessionMonad m) => Query -> q -> m [r]
+query q' params = retried $ do
+	conn <- serverSqlDatabase
+	liftIO $ SQL.query conn q' params
+
+query_ :: (FromRow r, SessionMonad m) => Query -> m [r]
+query_ q' = retried $ do
+	conn <- serverSqlDatabase
+	liftIO $ SQL.query_ conn q'
+
+queryNamed :: (FromRow r, SessionMonad m) => Query -> [NamedParam] -> m [r]
+queryNamed q' ps' = retried $ do
+	conn <- serverSqlDatabase
+	liftIO $ SQL.queryNamed conn q' ps'
+
+execute :: (ToRow q, SessionMonad m) => Query -> q -> m ()
+execute q' params = retried $ do
+	conn <- serverSqlDatabase
+	liftIO $ SQL.execute conn q' params
+
+execute_ :: SessionMonad m => Query -> m ()
+execute_ q' = retried $ do
+	conn <- serverSqlDatabase
+	liftIO $ SQL.execute_ conn q'
+
+executeMany :: (ToRow q, SessionMonad m) => Query -> [q] -> m ()
+executeMany q' params = do
+	conn <- serverSqlDatabase
+	liftIO $ SQL.executeMany conn q' params
+
+executeNamed :: SessionMonad m => Query -> [NamedParam] -> m ()
+executeNamed q' ps' = do
+	conn <- serverSqlDatabase
+	liftIO $ SQL.executeNamed conn q' ps'
+
+withTemporaryTable :: SessionMonad m => String -> [String] -> m a -> m a
+withTemporaryTable tableName columns = bracket_ createTable dropTable where
+	createTable = execute_ $ fromString $ "create temporary table {} ({});" ~~ tableName ~~ (intercalate ", " columns)
+	dropTable = execute_ $ fromString $ "drop table {};" ~~ tableName
+
+updatePackageDb :: SessionMonad m => PackageDb -> [ModulePackage] -> m ()
+updatePackageDb pdb pkgs = scope "update-package-db" $ do
+	sendLog Trace $ "update package-db: {}" ~~ Display.display pdb
+	removePackageDb pdb
+	insertPackageDb pdb pkgs
+
+removePackageDb :: SessionMonad m => PackageDb -> m ()
+removePackageDb pdb = scope "remove-package-db" $
+	execute "delete from package_dbs where package_db == ?;" (Only pdb)
+
+insertPackageDb :: SessionMonad m => PackageDb -> [ModulePackage] -> m ()
+insertPackageDb pdb pkgs = scope "insert-package-db" $ forM_ pkgs $ \pkg ->
+	execute
+		"insert into package_dbs (package_db, package_name, package_version) values (?, ?, ?);"
+		(pdb, pkg ^. packageName, pkg ^. packageVersion)
+
+updateProject :: SessionMonad m => Project -> Maybe PackageDbStack -> m ()
+updateProject proj pdbs = scope "update-project" $ do
+	sendLog Trace $ "update project: {}" ~~ Display.display proj
+	removeProject proj
+	insertProject proj pdbs
+
+removeProject :: SessionMonad m => Project -> m ()
+removeProject proj = scope "remove-project" $ do
+	projId <- query @_ @(Only Int) "select id from projects where cabal == ?;" (Only $ proj ^. projectCabal)
+	case projId of
+		[] -> return ()
+		pids -> do
+			when (length pids > 1) $
+				sendLog Warning $ "multiple projects for cabal {} found" ~~ (proj ^. projectCabal)
+			forM_ pids $ \pid -> do
+				bids <- query @_ @(Only Int) "select build_info_id from targets where project_id == ?;" pid
+				execute "delete from projects where id == ?;" pid
+				execute "delete from libraries where project_id == ?;" pid
+				execute "delete from executables where project_id == ?;" pid
+				execute "delete from tests where project_id == ?;" pid
+				forM_ bids $ \bid -> execute "delete from build_infos where id == ?;" bid
+
+insertProject :: SessionMonad m => Project -> Maybe PackageDbStack -> m ()
+insertProject proj pdbs = scope "insert-project" $ do
+	execute "insert into projects (name, cabal, version, package_db_stack) values (?, ?, ?, ?);" (
+		proj ^. projectName,
+		proj ^. projectCabal . path,
+		proj ^? projectDescription . _Just . projectVersion,
+		fmap (encode . packageDbs) pdbs)
+	projId <- lastRow
+
+	forM_ (proj ^? projectDescription . _Just . projectLibrary . _Just) $ \lib -> do
+		buildInfoId <- insertBuildInfo $ lib ^. libraryBuildInfo
+		execute "insert into libraries (project_id, modules, build_info_id) values (?, ?, ?);"
+			(projId, encode $ lib ^. libraryModules, buildInfoId)
+
+	forM_ (proj ^.. projectDescription . _Just . projectExecutables . each) $ \exe -> do
+		buildInfoId <- insertBuildInfo $ exe ^. executableBuildInfo
+		execute "insert into executables (project_id, name, path, build_info_id) values (?, ?, ?, ?);"
+			(projId, exe ^. executableName, exe ^. executablePath . path, buildInfoId)
+
+	forM_ (proj ^.. projectDescription . _Just . projectTests . each) $ \test -> do
+		buildInfoId <- insertBuildInfo $ test ^. testBuildInfo
+		execute "insert into tests (project_id, name, enabled, main, build_info_id) values (?, ?, ?, ?, ?);"
+			(projId, test ^. testName, test ^. testEnabled, test ^? testMain . _Just . path, buildInfoId)
+
+insertBuildInfo :: SessionMonad m => Info -> m Int
+insertBuildInfo info = scope "insert-build-info" $ do
+	execute "insert into build_infos (depends, language, extensions, ghc_options, source_dirs, other_modules) values (?, ?, ?, ?, ?, ?);" (
+			encode $ info ^. infoDepends,
+			fmap display $ info ^. infoLanguage,
+			encode $ map display $ info ^. infoExtensions,
+			encode $ info ^. infoGHCOptions,
+			encode $ info ^.. infoSourceDirs . each . path,
+			encode $ info ^. infoOtherModules)
+	lastRow
+
+updateModule :: SessionMonad m => InspectedModule -> m ()
+updateModule im = scope "update-module" $ do
+	_ <- upsertModule im
+	insertModuleSymbols im
+
+removeModuleContents :: SessionMonad m => Int -> m ()
+removeModuleContents mid = 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)
+
+removeModule :: SessionMonad m => Int -> m ()
+removeModule mid = scope "remove-module" $ do
+	removeModuleContents mid
+	execute "delete from modules where id == ?;" (Only mid)
+
+upsertModule :: SessionMonad m => InspectedModule -> m Int
+upsertModule im = scope "upsert-module" $ do
+	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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"
+				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 == ?;"
+				(moduleData :. Only mid')
+			return mid'
+	where
+		moduleData = (
+			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 . 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);" [
+		":file" := im ^? inspectedKey . moduleFile . path,
+		":package_name" := im ^? inspectedKey . modulePackage . packageName,
+		":package_version" := im ^? inspectedKey . modulePackage . packageVersion,
+		":other_location" := im ^? inspectedKey . otherLocationName,
+		":installed_name" := im ^? inspectedKey . installedModuleName]
+	-- TODO: Delete obsolete symbols (note, that they can be referenced from another modules)
+	removeModuleContents mid
+	insertModuleImports mid
+	insertExportSymbols mid (im ^.. inspected . moduleExports . each)
+	insertScopeSymbols mid (im ^.. inspected . scopeSymbols)
+	maybe (return ()) (insertResolvedNames mid) (im ^? inspected . moduleSource . _Just)
+	where
+		insertModuleImports :: SessionMonad m => Int -> m ()
+		insertModuleImports mid = scope "insert-module-imports" $ do
+			case im ^? inspected . moduleSource . _Just of
+				Nothing -> return ()
+				Just psrc -> do
+					let
+						imps = childrenBi psrc :: [ImportDecl Ann]
+						importRow idecl@(ImportDecl _ mname qual _ _ _ alias specList) = (
+							mid,
+							idecl ^. pos . positionLine,
+							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 (?, ?, ?, ?, ?, ?, ?);"
+						(map importRow imps)
+			where
+				getModuleName (ModuleName _ s) = s
+				getHiding (ImportSpecList _ h _) = h
+
+				makeImportList (ImportSpecList _ _ specs) = encode $ map asJson specs
+				asJson (IVar _ nm) = object ["name" .= fromName_ (void nm), "what" .= str' "var"]
+				asJson (IAbs _ ns nm) = object ["name" .= fromName_ (void nm), "what" .= str' "abs", "ns" .= fromNamespace ns] where
+					fromNamespace :: Namespace l -> Maybe String
+					fromNamespace (NoNamespace _) = Nothing
+					fromNamespace (TypeNamespace _) = Just "type"
+					fromNamespace (PatternNamespace _) = Just "pat"
+				asJson (IThingAll _ nm) = object ["name" .= fromName_ (void nm), "what" .= str' "all"]
+				asJson (IThingWith _ nm cs) = object ["name" .= fromName_ (void nm), "what" .= str' "with", "list" .= map (fromName_ . void . toName') cs] where
+					toName' (VarName _ n') = n'
+					toName' (ConName _ n') = n'
+
+				str' :: String -> String
+				str' = id
+
+		insertExportSymbols :: SessionMonad m => Int -> [Symbol] -> m ()
+		insertExportSymbols _ [] = return ()
+		insertExportSymbols mid syms = scope "insert-export-symbols" $ withTemporaryTable "export_symbols" (symbolsColumns ++ idColumns) $ do
+			dumpSymbols "export_symbols" symbolsColumns syms
+			updateExistingSymbols "export_symbols"
+			insertMissingModules "export_symbols"
+			insertMissingSymbols "export_symbols"
+			execute "insert into exports (module_id, symbol_id) select ?, symbol_id from export_symbols;" (Only mid)
+
+		insertScopeSymbols :: SessionMonad m => Int -> [(Symbol, [Name])] -> m ()
+		insertScopeSymbols _ [] = return ()
+		insertScopeSymbols mid snames = scope "insert-scope-symbols" $ withTemporaryTable "scope_symbols" (symbolsColumns ++ scopeNameColumns ++ idColumns) $ do
+			dumpSymbols "scope_symbols" (symbolsColumns ++ scopeNameColumns)
+				[(s :. (Name.nameModule nm, Name.nameIdent nm)) | (s, nms) <- snames, nm <- nms]
+			updateExistingSymbols "scope_symbols"
+			insertMissingModules "scope_symbols"
+			insertMissingSymbols "scope_symbols"
+			execute "insert into scopes (module_id, qualifier, name, symbol_id) select ?, qualifier, ident, symbol_id from scope_symbols;" (Only mid)
+
+		insertResolvedNames :: SessionMonad m => Int -> Parsed -> m ()
+		insertResolvedNames mid p = scope "insert-resolved-names" $ do
+			insertNames
+			replaceQNames
+			where
+				insertNames = executeMany insertQuery namesData
+				replaceQNames = executeMany insertQuery qnamesData
+				insertQuery = "insert or replace into names (module_id, qualifier, name, line, column, line_to, column_to, def_line, def_column, resolved_module, resolved_name, resolve_error) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"
+				namesData = map toData $ p ^.. P.names
+				qnamesData = map toQData $ p ^.. P.qnames
+				toData name = (
+					mid,
+					Nothing :: Maybe Text,
+					Name.fromName_ $ void name,
+					name ^. P.pos . positionLine,
+					name ^. P.pos . positionColumn,
+					name ^. P.regionL . regionTo . positionLine,
+					name ^. P.regionL . regionTo . positionColumn)
+					:. (
+					name ^? P.defPos . positionLine,
+					name ^? P.defPos . positionColumn,
+					(name ^? P.resolvedName) >>= Name.nameModule,
+					Name.nameIdent <$> (name ^? P.resolvedName),
+					P.resolveError name)
+				toQData qname = (
+					mid,
+					Name.nameModule $ void qname,
+					Name.nameIdent $ void qname,
+					qname ^. P.pos . positionLine,
+					qname ^. P.pos . positionColumn,
+					qname ^. P.regionL . regionTo . positionLine,
+					qname ^. P.regionL . regionTo . positionColumn)
+					:. (
+					qname ^? P.defPos . positionLine,
+					qname ^? P.defPos . positionColumn,
+					(qname ^? P.resolvedName) >>= Name.nameModule,
+					Name.nameIdent <$> (qname ^? P.resolvedName),
+					P.resolveError qname)
+
+		symbolsColumns :: [String]
+		symbolsColumns = [
+			"name", "module_name",
+			"file", "cabal", "install_dirs", "package_name", "package_version", "installed_name", "other_location",
+			"docs",
+			"line", "column",
+			"what", "type", "parent", "constructors", "args", "context", "associate", "pat_type", "pat_constructor"]
+
+		scopeNameColumns :: [String]
+		scopeNameColumns = ["qualifier", "ident"]
+
+		idColumns :: [String]
+		idColumns = ["module_id", "symbol_id"]
+
+		-- Dumps symbol data to temporary row and fills with 'module_id' and 'symbol_id' fields
+		dumpSymbols :: (SessionMonad m, ToRow q) => String -> [String] -> [q] -> m ()
+		dumpSymbols tableName columnNames rows = scope "dump-symbols" $ do
+			executeMany (fromString ("insert into {} ({}) values ({});" ~~ tableName ~~ intercalate ", " columnNames ~~ intercalate ", " (replicate (length columnNames) "?"))) (map toRow rows)
+			updateModuleIds tableName
+			updateSymbolIds tableName
+
+		updateModuleIds :: SessionMonad m => String -> m ()
+		updateModuleIds tableName =
+			execute_ (fromString ("update {table} set module_id = (select m.id from modules as m where ((m.name is null and {table}.module_name is null) or m.name = {table}.module_name) and ((m.file is null and {table}.file is null) or m.file = {table}.file) and ((m.package_name is null and {table}.package_name is null) or m.package_name = {table}.package_name) and ((m.package_version is null and {table}.package_version is null) or m.package_version = {table}.package_version) and ((m.installed_name is null and {table}.installed_name is null) or m.installed_name = {table}.installed_name) and ((m.other_location is null and {table}.other_location is null) or m.other_location = {table}.other_location));" ~~ ("table" ~% tableName)))
+
+		updateSymbolIds :: SessionMonad m => String -> m ()
+		updateSymbolIds tableName =
+			execute_ (fromString ("update {table} set symbol_id = (select s.id from symbols as s where s.name = {table}.name and s.module_id = {table}.module_id);" ~~ ("table" ~% tableName)))
+
+		updateExistingSymbols :: SessionMonad m => String -> m ()
+		updateExistingSymbols tableName = scope "update-existing-symbols" $ do
+			execute_ (fromString ("replace into symbols (id, name, module_id, docs, line, column, what, type, parent, constructors, args, context, associate, pat_type, pat_constructor) select symbols.id, {table}.name, {table}.module_id, {table}.docs, {table}.line, {table}.column, {table}.what, {table}.type, {table}.parent, {table}.constructors, {table}.args, {table}.context, {table}.associate, {table}.pat_type, {table}.pat_constructor from {table} inner join symbols on (symbols.name == {table}.name) and (symbols.module_id == {table}.module_id);" ~~ ("table" ~% tableName)))
+
+		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))
+			updateModuleIds tableName
+
+		insertMissingSymbols :: SessionMonad m => String -> m ()
+		insertMissingSymbols tableName = scope "insert-missing-symbols" $ do
+			execute_ (fromString ("insert into symbols (name, module_id, docs, line, column, what, type, parent, constructors, args, context, associate, pat_type, pat_constructor) select distinct name, module_id, docs, line, column, what, type, parent, constructors, args, context, associate, pat_type, pat_constructor from {} where module_id is not null and symbol_id is null;" ~~ tableName))
+			updateSymbolIds tableName
+
+lookupModuleLocation :: SessionMonad m => ModuleLocation -> m (Maybe Int)
+lookupModuleLocation m = do
+	mids <- 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 ((installed_name is null and :installed_name is null) or installed_name = :installed_name) and ((other_location is null and :other_location is null) or other_location = :other_location);" [
+		":file" := m ^? moduleFile . path,
+		":package_name" := m ^? modulePackage . packageName,
+		":package_version" := m ^? modulePackage . packageVersion,
+		":installed_name" := m ^? installedModuleName,
+		":other_location" := m ^? otherLocationName]
+	when (length mids > 1) $ sendLog Warning  $ "different modules with location: {}" ~~ Display.display m
+	return $ listToMaybe [mid | Only mid <- mids]
+
+lookupModule :: SessionMonad m => ModuleId -> m (Maybe Int)
+lookupModule m = do
+	mids <- queryNamed "select id from modules where ((name is null and :name is null) or name = :name) and ((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 ((installed_name is null and :installed_name is null) or installed_name = :installed_name) and ((other_location is null and :other_location is null) or other_location = :other_location);" [
+		":name" := m ^. moduleName,
+		":file" := m ^? moduleLocation . moduleFile . path,
+		":package_name" := m ^? moduleLocation . modulePackage . packageName,
+		":package_version" := m ^? moduleLocation . modulePackage . packageVersion,
+		":installed_name" := m ^? moduleLocation . installedModuleName,
+		":other_location" := m ^? moduleLocation . otherLocationName]
+	when (length mids > 1) $ sendLog Warning  $ "different modules with same name and location: {}" ~~ (m ^. moduleName)
+	return $ listToMaybe [mid | Only mid <- mids]
+
+lookupSymbol :: SessionMonad m => Int -> SymbolId -> m (Maybe Int)
+lookupSymbol mid sym = do
+	sids <- query "select id from symbols where name == ? and module_id == ?;" (
+		sym ^. symbolName,
+		mid)
+	when (length sids > 1) $ sendLog Warning $ "different symbols with same module id: {}.{}" ~~ show mid ~~ (sym ^. symbolName)
+	return $ listToMaybe [sid | Only sid <- sids]
+
+lastRow :: SessionMonad m => m Int
+lastRow = do
+	[Only i] <- query_ "select last_insert_rowid();"
+	return i
+
+loadModule :: SessionMonad m => Int -> m Module
+loadModule mid = scope "load-module" $ do
+	ms <- query @_ @(ModuleId :. (Maybe Text, Maybe Value, Int)) (toQuery (qModuleId `mappend` select_ ["mu.docs", "mu.fixities", "mu.id"] [] [fromString "mu.id == ?"])) (Only mid)
+	case ms of
+		[] -> sqlFailure $ "module with id {} not found" ~~ mid
+		mods@((mid' :. (mdocs, mfixities, _)):_) -> do
+			when (length mods > 1) $ sendLog Warning $ "multiple modules with same id = {} found" ~~ mid
+			syms <- query @_ @Symbol (toQuery (qSymbol `mappend` select_ [] ["exports as e"] ["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)
+			return $ Module {
+				_moduleId = mid',
+				_moduleDocs = mdocs,
+				_moduleImports = map fromOnly inames,
+				_moduleExports = syms,
+				_moduleFixities = fromMaybe [] (mfixities >>= fromJSON'),
+				_moduleScope = mempty,
+				_moduleSource = Nothing }
+
+loadModules :: (SessionMonad m, ToRow q) => String -> q -> m [Module]
+loadModules selectExpr args = scope "load-modules" $ do
+	ms <- query @_ @(ModuleId :. (Maybe Text, Maybe Value, Int)) (toQuery (qModuleId `mappend` select_ ["mu.docs", "mu.fixities", "mu.id"] [] [fromString $ "mu.id in (" ++ selectExpr ++ ")"])) args
+	forM ms $ \(mid' :. (mdocs, mfixities, mid)) -> do
+		syms <- query @_ @Symbol (toQuery (qSymbol `mappend` select_ [] ["exports as e"] ["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)
+		return $ Module {
+			_moduleId = mid',
+			_moduleDocs = mdocs,
+			_moduleImports = map fromOnly inames,
+			_moduleExports = syms,
+			_moduleFixities = fromMaybe [] (mfixities >>= fromJSON'),
+			_moduleScope = mempty,
+			_moduleSource = Nothing }
+
+loadProject :: SessionMonad m => Path -> m Project
+loadProject cabal = scope "load-project" $ do
+	projs <- query @_ @(Only Int :. Project) "select id, name, cabal, version from projects where cabal == ?;" (Only $ view path cabal)
+	(Only pid :. proj) <- case projs of
+		[] -> sqlFailure $ "project with cabal {} not found" ~~ view path cabal
+		_ -> do
+			when (length projs > 1) $ sendLog Warning $ "multiple projects with same cabal = {} found" ~~ view path cabal
+			return $ head projs
+
+	libs <- query (toQuery $ select_ ["lib.modules"] ["libraries as lib"] [] `mappend` qBuildInfo `mappend` where_ [
+		"lib.build_info_id == bi.id",
+		"lib.project_id == ?"])
+			(Only pid)
+
+	exes <- query (toQuery $ select_ ["exe.name", "exe.path"] ["executables as exe"] [] `mappend` qBuildInfo `mappend` where_ [
+		"exe.build_info_id == bi.id",
+		"exe.project_id == ?"])
+			(Only pid)
+
+	tests <- query (toQuery $ select_ ["tst.name", "tst.enabled", "tst.main"] ["tests as tst"] [] `mappend` qBuildInfo `mappend` where_ [
+		"tst.build_info_id == bi.id",
+		"tst.project_id == ?"])
+			(Only pid)
+
+	return $
+		set (projectDescription . _Just . projectLibrary) (listToMaybe libs) .
+		set (projectDescription . _Just . projectExecutables) exes .
+		set (projectDescription . _Just . projectTests) tests $
+		proj
+
+escapeLike :: Text -> Text
+escapeLike = T.replace "\\" "\\\\" . T.replace "%" "\\%" . T.replace "_" "\\_"
+
+-- Util
+
+sqlFailure :: SessionMonad m => Text -> m a
+sqlFailure msg = do
+	sendLog Error msg
+	hsdevError $ SQLiteError $ T.unpack msg
+ src/HsDev/Database/SQLite/Instances.hs view
@@ -0,0 +1,226 @@+{-# LANGUAGE OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Database.SQLite.Instances (
+	JSON(..)
+	) where
+
+import Control.Lens ((^.), (^?), _Just)
+import Data.Aeson as A
+import Data.Maybe
+import Data.Foldable
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import qualified Data.ByteString.Lazy as L
+import Database.SQLite.Simple
+import Database.SQLite.Simple.ToField
+import Database.SQLite.Simple.FromField
+import Language.Haskell.Extension
+import Text.Format
+
+import System.Directory.Paths
+import HsDev.Symbols.Location
+import HsDev.Symbols.Types
+import HsDev.Tools.Ghc.Types
+import HsDev.Util
+
+instance ToField Value where
+	toField = SQLBlob . L.toStrict . encode
+
+instance FromField Value where
+	fromField fld = case fieldData fld of
+		SQLText s -> either fail return . eitherDecode . L.fromStrict . T.encodeUtf8 $ s
+		SQLBlob s -> either fail return . eitherDecode . L.fromStrict $ s
+		_ -> fail "invalid json field type"
+
+newtype JSON a = JSON { getJSON :: a }
+	deriving (Eq, Ord, Read, Show)
+
+instance ToJSON a => ToField (JSON a) where
+	toField = SQLBlob . L.toStrict . encode . getJSON
+
+instance FromJSON a => FromField (JSON a) where
+	fromField fld = case fieldData fld of
+		SQLText s -> either fail (return . JSON) . eitherDecode . L.fromStrict . T.encodeUtf8 $ s
+		SQLBlob s -> either fail (return . JSON) . eitherDecode . L.fromStrict $ s
+		_ -> fail "invalid json field type"
+
+instance FromRow Position where
+	fromRow = Position <$> field <*> field
+
+instance ToRow Position where
+	toRow (Position l c) = [toField l, toField c]
+
+instance FromRow Region where
+	fromRow = Region <$> fromRow <*> fromRow
+
+instance ToRow Region where
+	toRow (Region f t) = toRow f ++ toRow t
+
+instance FromRow ModulePackage where
+	fromRow = ModulePackage <$> field <*> (fromMaybe T.empty <$> field)
+
+instance ToRow ModulePackage where
+	toRow (ModulePackage name ver) = [toField name, toField ver]
+
+instance FromRow ModuleLocation where
+	fromRow = do
+		file <- field
+		cabal <- field
+		dirs <- field
+		pname <- field
+		pver <- field
+		iname <- field
+		other <- field
+
+		maybe (fail $ "Can't parse module location: {}" ~~ show (file, cabal, dirs, pname, pver, iname, other)) return $ msum [
+			FileModule <$> file <*> pure (project <$> cabal),
+			InstalledModule <$> maybe (pure []) fromJSON' dirs <*> (ModulePackage <$> pname <*> pver) <*> iname,
+			OtherLocation <$> other,
+			pure NoLocation]
+
+instance ToRow ModuleLocation where
+	toRow mloc = [
+		toField $ mloc ^? moduleFile,
+		toField $ mloc ^? moduleProject . _Just . projectCabal,
+		toField $ fmap toJSON $ mloc ^? moduleInstallDirs,
+		toField $ mloc ^? modulePackage . packageName,
+		toField $ mloc ^? modulePackage . packageVersion,
+		toField $ mloc ^? installedModuleName,
+		toField $ mloc ^? otherLocationName]
+
+instance FromRow ModuleId where
+	fromRow = ModuleId <$> field <*> fromRow
+
+instance ToRow ModuleId where
+	toRow mid = toField (mid ^. moduleName) : toRow (mid ^. moduleLocation)
+
+instance FromRow SymbolId where
+	fromRow = SymbolId <$> field <*> fromRow
+
+instance ToRow SymbolId where
+	toRow (SymbolId nm mid) = toField nm : toRow mid
+
+instance FromRow Symbol where
+	fromRow = Symbol <$> fromRow <*> field <*> pos <*> infoP where
+		pos = do
+			line <- field
+			column <- field
+			return $ Position <$> line <*> column
+		infoP = do
+			what <- str' <$> field
+			ty <- field
+			parent <- field
+			ctors <- field
+			args <- field
+			ctx <- field
+			assoc <- field
+			patTy <- field
+			patCtor <- field
+			maybe (fail $ "Can't parse symbol info: {}" ~~ show (what, ty, parent, ctors, args, ctx, assoc, patTy, patCtor)) return $ case what of
+				"function" -> return $ Function ty
+				"method" -> Method <$> pure ty <*> parent
+				"selector" -> Selector <$> pure ty <*> parent <*> (fromJSON' =<< ctors)
+				"ctor" -> Constructor <$> (fromJSON' =<< args) <*> parent
+				"type" -> Type <$> (fromJSON' =<< args) <*> (fromJSON' =<< ctx)
+				"newtype" -> NewType <$> (fromJSON' =<< args) <*> (fromJSON' =<< ctx)
+				"data" -> Data <$> (fromJSON' =<< args) <*> (fromJSON' =<< ctx)
+				"class" -> Class <$> (fromJSON' =<< args) <*> (fromJSON' =<< ctx)
+				"type-family" -> TypeFam <$> (fromJSON' =<< args) <*> (fromJSON' =<< ctx) <*> pure assoc
+				"data-family" -> DataFam <$> (fromJSON' =<< args) <*> (fromJSON' =<< ctx) <*> pure assoc
+				"pat-ctor" -> PatConstructor <$> (fromJSON' =<< args) <*> pure patTy
+				"pat-selector" -> PatSelector <$> pure ty <*> pure patTy <*> patCtor
+				_ -> Nothing
+		str' :: String -> String
+		str' = id
+
+instance ToRow Symbol where
+	toRow sym = concat [
+		toRow (sym ^. symbolId),
+		[toField $ sym ^. symbolDocs],
+		maybe [SQLNull, SQLNull] toRow (sym ^. symbolPosition),
+		info]
+		where
+			info = [
+				toField $ symbolType sym,
+				toField $ sym ^? symbolInfo . functionType . _Just,
+				toField $ msum [sym ^? symbolInfo . parentClass, sym ^? symbolInfo . parentType],
+				toField $ toJSON $ sym ^? symbolInfo . selectorConstructors,
+				toField $ toJSON $ sym ^? symbolInfo . typeArgs,
+				toField $ toJSON $ sym ^? symbolInfo . typeContext,
+				toField $ sym ^? symbolInfo . familyAssociate . _Just,
+				toField $ sym ^? symbolInfo . patternType . _Just,
+				toField $ sym ^? symbolInfo . patternConstructor]
+
+instance FromRow Project where
+	fromRow = do
+		name <- field
+		cabal <- field
+		ver <- field
+		return $ Project name (takeDir cabal) cabal $ Just $ ProjectDescription ver Nothing [] []
+
+instance FromRow Library where
+	fromRow = do
+		mods <- field >>= maybe (fail "Error parsing library modules") return . fromJSON'
+		binfo <- fromRow
+		return $ Library mods binfo
+
+instance FromRow Executable where
+	fromRow = Executable <$> field <*> field <*> fromRow
+
+instance FromRow Test where
+	fromRow = Test <$> field <*> field <*> field <*> fromRow
+
+instance FromRow Info where
+	fromRow = Info <$>
+		(field >>= maybe (fail "Error parsing depends") return . fromJSON') <*>
+		field <*>
+		(field >>= maybe (fail "Error parsing extensions") return . fromJSON') <*>
+		(field >>= maybe (fail "Error parsing ghc-options") return . fromJSON') <*>
+		(field >>= maybe (fail "Error parsing source-dirs") return . fromJSON') <*>
+		(field >>= maybe (fail "Error parsing other-modules") return . fromJSON')
+
+instance FromField Language where
+	fromField fld = case fieldData fld of
+		SQLText txt -> parseDT "Language" (T.unpack txt)
+		_ -> fail "Can't parse language, invalid type"
+
+instance ToField PackageDb where
+	toField GlobalDb = toField ("global-db" :: String)
+	toField UserDb = toField ("user-db" :: String)
+	toField (PackageDb p) = toField ("package-db:" ++ T.unpack p)
+
+instance FromField PackageDb where
+	fromField fld = do
+		s <- fromField fld
+		case s of
+			"global-db" -> return GlobalDb
+			"user-db" -> return UserDb
+			_ -> case T.stripPrefix "package-db:" s of
+				Just p' -> return $ PackageDb p'
+				Nothing -> fail $ "Can't parse package-db, invalid string: " ++ T.unpack s
+
+instance FromRow SymbolUsage where
+	fromRow = SymbolUsage <$> fromRow <*> fromRow <*> fromRow
+
+instance FromRow Inspection where
+	fromRow = do
+		tm <- field
+		opts <- field
+		case (tm, opts) of
+			(Nothing, Nothing) -> return InspectionNone
+			(_, Just opts') -> InspectionAt (maybe 0 (fromRational . (toRational :: Double -> Rational)) tm) <$>
+				maybe (fail "Error parsing inspection opts") return (fromJSON' opts')
+			(Just _, Nothing) -> fail "Error parsing inspection data, time is set, but flags are null"
+
+instance ToRow Inspection where
+	toRow InspectionNone = [SQLNull, SQLNull]
+	toRow (InspectionAt tm opts) = [
+		if tm == 0 then SQLNull else toField (fromRational (toRational tm) :: Double),
+		toField $ toJSON opts]
+
+instance FromRow TypedExpr where
+	fromRow = TypedExpr <$> field <*> field
+
+instance ToRow TypedExpr where
+	toRow (TypedExpr e t) = [toField e, toField t]
+ src/HsDev/Database/SQLite/Schema.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
+
+module HsDev.Database.SQLite.Schema (
+	schema, commands
+	) where
+
+import qualified Data.Text as T
+import Data.String
+import Database.SQLite.Simple (Query)
+
+import HsDev.Database.SQLite.Schema.TH
+
+schema :: T.Text
+schema = T.pack $schemaExp
+
+commands :: [Query]
+commands =
+	map (fromString . T.unpack) $
+	filter (not . T.null) $
+	map T.strip $ T.splitOn ";" schema
+ src/HsDev/Database/SQLite/Schema/TH.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE TemplateHaskell #-}
+
+module HsDev.Database.SQLite.Schema.TH (
+	schemaExp
+	) where
+
+import System.Directory
+import Language.Haskell.TH
+import Language.Haskell.TH.Syntax
+
+schemaExp :: ExpQ
+schemaExp = do
+	schemaFile <- runIO $ canonicalizePath "data/hsdev.sql"
+	addDependentFile schemaFile
+	s <- runIO (readFile "data/hsdev.sql")
+	[e| s |]
+ src/HsDev/Database/SQLite/Select.hs view
@@ -0,0 +1,111 @@+{-# LANGUAGE OverloadedStrings #-}
+
+module HsDev.Database.SQLite.Select (
+	Select(..), select_, where_, buildQuery, toQuery,
+	qSymbolId, qSymbol, qModuleLocation, qModuleId, qBuildInfo
+	) where
+
+import Data.String
+import Data.Text (Text)
+import qualified Data.Text as T
+import Database.SQLite.Simple
+import Text.Format
+
+data Select = Select {
+	selectColumns :: [Text],
+	selectTables :: [Text],
+	selectConditions :: [Text] }
+		deriving (Eq, Ord, Read, Show)
+
+instance Monoid Select where
+	mempty = Select mempty mempty mempty
+	Select lc lt lcond `mappend` Select rc rt rcond = Select
+		(lc `mappend` rc)
+		(lt `mappend` rt)
+		(lcond `mappend` rcond)
+
+select_ :: [Text] -> [Text] -> [Text] -> Select
+select_ = Select
+
+where_ :: [Text] -> Select
+where_ = select_ [] []
+
+buildQuery :: Select -> String
+buildQuery (Select cols tables conds) = "select {} from {} where {}"
+	~~ T.intercalate ", " cols
+	~~ T.intercalate ", " tables
+	~~ T.intercalate " and " (map (\cond -> T.concat ["(", cond, ")"]) conds)
+
+toQuery :: Select -> Query
+toQuery = fromString . buildQuery
+
+qSymbolId :: Select
+qSymbolId = select_
+	[
+		"s.name",
+		"m.name",
+		"m.file",
+		"m.cabal",
+		"m.install_dirs",
+		"m.package_name",
+		"m.package_version",
+		"m.installed_name",
+		"m.other_location"]
+	["modules as m", "symbols as s"]
+	["m.id == s.module_id"]
+
+qSymbol :: Select
+qSymbol = qSymbolId `mappend` select_ cols [] [] where
+	cols = [
+		"s.docs",
+		"s.line",
+		"s.column",
+		"s.what",
+		"s.type",
+		"s.parent",
+		"s.constructors",
+		"s.args",
+		"s.context",
+		"s.associate",
+		"s.pat_type",
+		"s.pat_constructor"]
+
+qModuleLocation :: Select
+qModuleLocation = select_
+	[
+		"ml.file",
+		"ml.cabal",
+		"ml.install_dirs",
+		"ml.package_name",
+		"ml.package_version",
+		"ml.installed_name",
+		"ml.other_location"]
+	["modules as ml"]
+	[]
+
+qModuleId :: Select
+qModuleId = select_
+	[
+		"mu.name",
+		"mu.file",
+		"mu.cabal",
+		"mu.install_dirs",
+		"mu.package_name",
+		"mu.package_version",
+		"mu.installed_name",
+		"mu.other_location"]
+	["modules as mu"]
+	[]
+
+qBuildInfo :: Select
+qBuildInfo = select_
+	[
+		"bi.depends",
+		"bi.language",
+		"bi.extensions",
+		"bi.ghc_options",
+		"bi.source_dirs",
+		"bi.other_modules"
+	]
+	["build_infos as bi"]
+	[]
+ src/HsDev/Database/SQLite/Transaction.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE TypeApplications, MultiWayIf, OverloadedStrings #-}
+
+module HsDev.Database.SQLite.Transaction (
+	TransactionType(..),
+	Retries(..), def, noRetry, retryForever, retryN,
+
+	-- * Transactions
+	withTransaction, beginTransaction, commitTransaction, rollbackTransaction,
+	transaction, transaction_,
+
+	-- * Retry functions
+	retry, retry_
+	) where
+
+import Control.Concurrent
+import Control.Monad.Catch
+import Control.Monad.IO.Class
+import Data.Default
+import Database.SQLite.Simple as SQL hiding (withTransaction)
+
+import HsDev.Server.Types (SessionMonad, serverSqlDatabase)
+
+-- | Three types of transactions
+data TransactionType = Deferred | Immediate | Exclusive
+	deriving (Eq, Ord, Read, Show)
+
+-- | Retry config
+data Retries = Retries {
+	retriesIntervals :: [Int],
+	retriesError :: SQLError -> Bool }
+
+instance Default Retries where
+	def = Retries (replicate 100 100000) $ \e -> sqlError e `elem` [ErrorBusy, ErrorLocked]
+
+-- | Don't retry
+noRetry :: Retries
+noRetry = Retries [] (const False)
+
+-- | Retry forever
+retryForever :: Int -> Retries
+retryForever interval = def { retriesIntervals = repeat interval }
+
+-- | Retry with interval N times
+retryN :: Int -> Int -> Retries
+retryN interval times = def { retriesIntervals = replicate times interval }
+
+-- | Run actions inside transaction
+withTransaction :: (MonadIO m, MonadMask m) => Connection -> TransactionType -> Retries -> m a -> m a
+withTransaction conn t rs act = mask $ \restore -> do
+	mretry restore (beginTransaction conn t)
+	(restore act <* mretry restore (commitTransaction conn)) `onException` rollbackTransaction conn
+	where
+		mretry restore' fn = mretry' (retriesIntervals rs) where
+			mretry' [] = fn
+			mretry' (tm:tms) = catch @_ @SQLError fn $ \e -> if
+				| retriesError rs e -> do
+						_ <- restore' $ liftIO $ threadDelay tm
+						mretry' tms
+				| otherwise -> throwM e
+
+-- | Begin transaction
+beginTransaction :: MonadIO m => Connection -> TransactionType -> m ()
+beginTransaction conn t = liftIO $ SQL.execute_ conn $ case t of
+	Deferred -> "begin transaction;"
+	Immediate -> "begin immediate transaction;"
+	Exclusive -> "begin exclusive transaction;"
+
+-- | Commit transaction
+commitTransaction :: MonadIO m => Connection -> m ()
+commitTransaction conn = liftIO $ SQL.execute_ conn "commit transaction;"
+
+-- | Rollback transaction
+rollbackTransaction :: MonadIO m => Connection -> m ()
+rollbackTransaction conn = liftIO $ SQL.execute_ conn "rollback transaction;"
+
+-- | Run transaction in @SessionMonad@
+transaction :: SessionMonad m => TransactionType -> Retries -> m a -> m a
+transaction t rs act = do
+	conn <- serverSqlDatabase
+	withTransaction conn t rs act
+
+-- | Transaction with default retries config
+transaction_ :: SessionMonad m => TransactionType -> m a -> m a
+transaction_ t = transaction t def
+
+-- | Retry operation
+retry :: (MonadIO m, MonadCatch m) => Retries -> m a -> m a
+retry rs = retry' (retriesIntervals rs) where
+	retry' [] fn = fn
+	retry' (tm:tms) fn = catch @_ @SQLError fn $ \e -> if
+		| retriesError rs e -> do
+			liftIO $ threadDelay tm
+			retry' tms fn
+		| otherwise -> throwM e
+
+-- | Retry with default params
+retry_ :: (MonadIO m, MonadCatch m) => m a -> m a
+retry_ = retry def
src/HsDev/Database/Update.hs view
@@ -1,447 +1,601 @@-{-# LANGUAGE FlexibleContexts, OverloadedStrings, MultiParamTypeClasses #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Database.Update (-	Status(..), Progress(..), Task(..), isStatus,-	UpdateOptions(..),--	UpdateM(..),-	runUpdate,--	postStatus, waiter, updater, loadCache, getCache, runTask, runTasks,-	readDB,--	scanModule, scanModules, scanFile, scanFileContents, scanCabal, prepareSandbox, scanSandbox, scanPackageDb, scanProjectFile, scanProjectStack, scanProject, scanDirectory, scanContents,-	scanDocs, inferModTypes,-	scan,-	updateEvent, processEvent,--	module HsDev.Database.Update.Types,--	module HsDev.Watcher,--	module Control.Monad.Except-	) where--import Control.Applicative ((<|>))-import Control.Arrow-import Control.Concurrent.Lifted (fork)-import Control.DeepSeq-import Control.Lens (preview, _Just, view, over, set, _1, mapMOf_, each, (^..), _head, _Right)-import Control.Monad.Catch (catch)-import Control.Monad.Except-import Control.Monad.Reader-import Control.Monad.Writer-import Data.Aeson-import Data.Aeson.Types-import Data.Foldable (toList)-import qualified Data.Map as M-import Data.Maybe (mapMaybe, isJust, fromMaybe)-import qualified Data.Text as T (unpack)-import System.Directory (canonicalizePath, doesFileExist)-import System.FilePath-import qualified System.Log.Simple as Log--import HsDev.Error-import qualified HsDev.Cache.Structured as Cache-import HsDev.Database-import HsDev.Database.Async hiding (Event)-import HsDev.Display-import HsDev.Inspect (inspectDocs, inspectDocsGhc)-import HsDev.Project-import HsDev.Sandbox-import HsDev.Stack-import HsDev.Symbols-import HsDev.Tools.Ghc.Session hiding (wait)-import HsDev.Tools.Ghc.Types (inferTypes)-import HsDev.Tools.HDocs-import qualified HsDev.Scan as S-import HsDev.Scan.Browse-import HsDev.Util (isParent, ordNub)-import qualified HsDev.Util as Util (withCurrentDirectory)-import HsDev.Server.Types (commandNotify, serverWriteCache, serverReadCache)-import HsDev.Server.Message-import HsDev.Database.Update.Types-import HsDev.Watcher-import Text.Format--onStatus :: UpdateMonad m => m ()-onStatus = asks (view updateTasks) >>= commandNotify . Notification . toJSON . reverse--childTask :: UpdateMonad m => Task -> m a -> m a-childTask t = local (over updateTasks (t:))--isStatus :: Value -> Bool-isStatus = isJust . parseMaybe (parseJSON :: Value -> Parser Task)--runUpdate :: ServerMonadBase m => UpdateOptions -> UpdateM m a -> ClientM m a-runUpdate uopts act = Log.scope "update" $ do-	(r, updatedMods) <- runWriterT (runUpdateM act' `runReaderT` uopts)-	db <- askSession sessionDatabase-	wait db-	dbval <- liftIO $ readAsync db-	let-		dbs = ordNub $ mapMaybe (preview modulePackageDb) updatedMods-		projs = ordNub $ mapMaybe (preview $ moduleProject . _Just) updatedMods-		stand = any moduleStandalone updatedMods--		modifiedDb = mconcat $ concat [-			map (`packageDbDB` dbval) dbs,-			map (`projectDB` dbval) projs,-			[standaloneDB dbval | stand]]-	serverWriteCache modifiedDb-	return r-	where-		act' = do-			(r, mlocs') <- liftM (second $ filter (isJust . preview moduleFile)) $ listen act-			db <- askSession sessionDatabase-			wait db-			let-				getMods :: (MonadIO m) => m [InspectedModule]-				getMods = do-					db' <- liftIO $ readAsync db-					return $ filter ((`elem` mlocs') . view inspectedId) $ toList $ databaseModules db'-			when (view updateDocs uopts) $ do-				Log.sendLog Log.Trace "forking inspecting source docs"-				void $ fork (getMods >>= waiter . mapM_ scanDocs_)-			when (view updateInfer uopts) $ do-				Log.sendLog Log.Trace "forking inferring types"-				void $ fork (getMods >>= waiter . mapM_ inferModTypes_)-			return r-		scanDocs_ :: UpdateMonad m => InspectedModule -> m ()-		scanDocs_ im = do-			im' <- (S.scanModify (\opts _ -> liftIO . inspectDocs opts) im) <|> return im-			updater $ fromModule im'-		inferModTypes_ :: UpdateMonad m => InspectedModule -> m ()-		inferModTypes_ im = do-			-- TODO: locate sandbox-			s <- getSession-			im' <- (S.scanModify (infer' s) im) <|> return im-			updater $ fromModule im'-		infer' :: UpdateMonad m => Session -> [String] -> PackageDbStack -> Module -> m Module-		infer' s opts _ m = case preview (moduleLocation . moduleFile) m of-			Nothing -> return m-			Just _ -> liftIO $ inWorker (sessionGhc s) $ do-				targetSession opts m-				inferTypes opts m Nothing---- | Post status-postStatus :: UpdateMonad m => Task -> m ()-postStatus t = childTask t onStatus---- | Wait DB to complete actions-waiter :: UpdateMonad m => m () -> m ()-waiter act = do-	db <- askSession sessionDatabase-	act-	wait db---- | Update task result to database-updater :: UpdateMonad m => Database -> m ()-updater db' = do-	db <- askSession sessionDatabase-	update db $ return $!! db'-	tell $!! map (view moduleLocation) $ allModules db'---- | Clear obsolete data from database-cleaner :: UpdateMonad m => m Database -> m ()-cleaner act = do-	db <- askSession sessionDatabase-	db' <- act-	clear db $ return $!! db'---- | Get data from cache without updating DB-loadCache :: UpdateMonad m => (FilePath -> ExceptT String IO Structured) -> m Database-loadCache act = do-	mdat <- serverReadCache act-	return $ fromMaybe mempty mdat---- | Load data from cache if not loaded yet and wait-getCache :: UpdateMonad m => (FilePath -> ExceptT String IO Structured) -> (Database -> Database) -> m Database-getCache act check = do-	dbval <- liftM check readDB-	if nullDatabase dbval-		then do-			db <- loadCache act-			waiter $ updater db-			return db-		else-			return dbval---- | Run one task-runTask :: (Display t, UpdateMonad m, NFData a) => String -> t -> m a -> m a-runTask action subj act = Log.scope "task" $ do-	postStatus $ set taskStatus StatusWorking task-	x <- childTask task act-	x `deepseq` postStatus (set taskStatus StatusOk task)-	return x-	`catch`-	(\e -> postStatus (set taskStatus (StatusError e) task) >> hsdevError e)-	where-		task = Task {-			_taskName = action,-			_taskStatus = StatusWorking,-			_taskSubjectType = displayType subj,-			_taskSubjectName = display subj,-			_taskProgress = Nothing }---- | Run many tasks with numeration-runTasks :: UpdateMonad m => [m ()] -> m ()-runTasks ts = zipWithM_ taskNum [1..] (map noErr ts) where-	total = length ts-	taskNum n = local setProgress where-		setProgress = set (updateTasks . _head . taskProgress) (Just (Progress n total))-	noErr v = v `mplus` return ()---- | Get database value-readDB :: SessionMonad m => m Database-readDB = askSession sessionDatabase >>= liftIO . readAsync---- | Scan module-scanModule :: UpdateMonad m => [String] -> ModuleLocation -> Maybe String -> m ()-scanModule opts mloc mcts = runTask "scanning" mloc $ Log.scope "module" $ do-	defs <- askSession sessionDefines-	im <- S.scanModule defs opts mloc mcts-	updater $ fromModule im-	_ <- return $ view inspectionResult im-	return ()---- | Scan modules-scanModules :: UpdateMonad m => [String] -> [S.ModuleToScan] -> m ()-scanModules opts ms = runTasks $-	[scanProjectFile opts p >> return () | p <- ps] ++-	[scanModule (opts ++ mopts) m mcts | (m, mopts, mcts) <- ms]-	where-		ps = ordNub $ mapMaybe (toProj . view _1) ms-		toProj (FileModule _ p) = fmap (view projectCabal) p-		toProj _ = Nothing---- | Scan source file-scanFile :: UpdateMonad m => [String] -> FilePath -> m ()-scanFile opts fpath = scanFileContents opts fpath Nothing---- | Scan source file with contents-scanFileContents :: UpdateMonad m => [String] -> FilePath -> Maybe String -> m ()-scanFileContents opts fpath mcts = Log.scope "file" $ hsdevLiftIO $ do-	dbval <- readDB-	fpath' <- liftIO $ canonicalizePath fpath-	ex <- liftIO $ doesFileExist fpath'-	mlocs <- if ex-		then do-			mloc <- case lookupFile fpath' dbval of-				Just m -> return $ view moduleLocation m-				Nothing -> do-					mproj <- liftIO $ locateProject fpath'-					return $ FileModule fpath' mproj-			return [(mloc, [], mcts)]-		else return []-	mapMOf_ (each . _1) (watch . flip watchModule) mlocs-	scan-		(Cache.loadFiles (== fpath'))-		(filterDB (inFile fpath') (const False) . standaloneDB)-		mlocs-		opts-		(scanModules opts)-	where-		inFile f = maybe False (== f) . preview (moduleIdLocation . moduleFile)---- | Scan cabal modules, doesn't rescan if already scanned-scanCabal :: UpdateMonad m => [String] -> m ()-scanCabal opts = Log.scope "cabal" $ do-	dbval <- readDB-	let-		scannedDbs = databasePackageDbs dbval-		unscannedDbs = filter ((`notElem` scannedDbs) . topPackageDb) $ reverse $ packageDbStacks userDb-	if null unscannedDbs-		then Log.sendLog Log.Trace $ "cabal (global-db and user-db) already scanned"-		else runTasks $ map (scanPackageDb opts) unscannedDbs---- | Prepare sandbox for scanning. This is used for stack project to build & configure.-prepareSandbox :: UpdateMonad m => Sandbox -> m ()-prepareSandbox sbox@(Sandbox StackWork fpath) = Log.scope "prepare" $ runTasks [-	runTask "building dependencies" sbox $ void $ Util.withCurrentDirectory dir $ buildDeps Nothing,-	runTask "configuring" sbox $ void $ Util.withCurrentDirectory dir $ configure Nothing]-	where-		dir = takeDirectory fpath-prepareSandbox _ = return ()---- | Scan sandbox modules, doesn't rescan if already scanned-scanSandbox :: UpdateMonad m => [String] -> Sandbox -> m ()-scanSandbox opts sbox = Log.scope "sandbox" $ do-	dbval <- readDB-	prepareSandbox sbox-	pdbs <- sandboxPackageDbStack sbox-	let-		scannedDbs = databasePackageDbs dbval-		unscannedDbs = filter ((`notElem` scannedDbs) . topPackageDb) $ reverse $ packageDbStacks pdbs-	if null unscannedDbs-		then Log.sendLog Log.Trace $ "sandbox already scanned"-		else runTasks $ map (scanPackageDb opts) unscannedDbs---- | Scan top of package-db stack, usable for rescan-scanPackageDb :: UpdateMonad m => [String] -> PackageDbStack -> m ()-scanPackageDb opts pdbs = runTask "scanning" (topPackageDb pdbs) $ Log.scope "package-db" $ do-	watch (\w -> watchPackageDb w pdbs opts)-	mlocs <- liftM-		(filter (\mloc -> preview modulePackageDb mloc == Just (topPackageDb pdbs))) $-		listModules opts pdbs-	scan (Cache.loadPackageDb (topPackageDb pdbs)) (packageDbDB (topPackageDb pdbs)) ((,,) <$> mlocs <*> pure [] <*> pure Nothing) opts $ \mlocs' -> do-		ms <- browseModules opts pdbs (mlocs' ^.. each . _1)-		docs <- liftIO $ hsdevLiftWith (ToolError "hdocs") $ hdocsCabal pdbs opts-		updater $ mconcat $ map (fromModule . fmap (setDocs' docs)) ms-	where-		setDocs' :: Map String (Map String String) -> Module -> Module-		setDocs' docs m = maybe m (`setDocs` m) $ M.lookup (T.unpack $ view moduleName m) docs---- | Scan project file-scanProjectFile :: UpdateMonad m => [String] -> FilePath -> m Project-scanProjectFile opts cabal = runTask "scanning" cabal $ do-	proj <- S.scanProjectFile opts cabal-	updater $ fromProject proj-	return proj---- | Scan project and related package-db stack-scanProjectStack :: UpdateMonad m => [String] -> FilePath -> m ()-scanProjectStack opts cabal = do-	proj <- scanProjectFile opts cabal-	scanProject opts cabal-	sbox <- liftIO $ projectSandbox (view projectPath proj)-	maybe (scanCabal opts) (scanSandbox opts) sbox---- | Scan project-scanProject :: UpdateMonad m => [String] -> FilePath -> m ()-scanProject opts cabal = runTask "scanning" (project cabal) $ Log.scope "project" $ do-	proj <- scanProjectFile opts cabal-	watch (\w -> watchProject w proj opts)-	S.ScanContents _ [(_, sources)] _ <- S.enumProject proj-	scan (Cache.loadProject $ view projectCabal proj) (projectDB proj) sources opts $ scanModules opts---- | Scan directory for source files and projects-scanDirectory :: UpdateMonad m => [String] -> FilePath -> m ()-scanDirectory opts dir = runTask "scanning" dir $ Log.scope "directory" $ do-	S.ScanContents standSrcs projSrcs pdbss <- S.enumDirectory dir-	runTasks [scanProject opts (view projectCabal p) | (p, _) <- projSrcs]-	runTasks $ map (scanPackageDb opts) pdbss -- TODO: Don't rescan-	mapMOf_ (each . _1) (watch . flip watchModule) standSrcs-	scan (Cache.loadFiles (dir `isParent`)) (filterDB inDir (const False) . standaloneDB) standSrcs opts $ scanModules opts-	where-		inDir = maybe False (dir `isParent`) . preview (moduleIdLocation . moduleFile)--scanContents :: UpdateMonad m => [String] -> S.ScanContents -> m ()-scanContents opts (S.ScanContents standSrcs projSrcs pdbss) = do-	dbval <- readDB-	let-		projs = databaseProjects dbval ^.. each . projectCabal-		pdbs = databasePackageDbs dbval-		files = allModules (standaloneDB dbval) ^.. each . moduleLocation . moduleFile-		srcs = standSrcs ^.. each . _1 . moduleFile-		inSrcs src = src `elem` srcs && src `notElem` files-		inFiles = maybe False inSrcs . preview (moduleIdLocation . moduleFile)-	mapMOf_ (each . _1 . projectCabal) (\p -> Log.sendLog Log.Trace ("scanning project: {}" ~~ p)) projSrcs-	runTasks [scanProject opts (view projectCabal p) | (p, _) <- projSrcs, view projectCabal p `notElem` projs]-	mapMOf_ (each . _1 . moduleFile) (\f -> Log.sendLog Log.Trace ("scanning file: {}" ~~ f)) standSrcs-	mapMOf_ (each . _1) (watch . flip watchModule) standSrcs-	scan (Cache.loadFiles inSrcs) (filterDB inFiles (const False) . standaloneDB) standSrcs opts $ scanModules opts-	mapMOf_ each (\s -> Log.sendLog Log.Trace ("scanning package-db: {}" ~~ topPackageDb s)) pdbss-	runTasks [scanPackageDb opts pdbs' | pdbs' <- pdbss, topPackageDb pdbs' `notElem` pdbs]---- | Scan docs for inspected modules-scanDocs :: UpdateMonad m => [InspectedModule] -> m ()-scanDocs ims = do-	-- w <- liftIO $ ghcWorker ["-haddock"] (return ())-	-- w <- askSession sessionGhc-	runTasks $ map scanDocs' ims-	where-		scanDocs' im-			| not $ hasTag RefinedDocsTag im = runTask "scanning docs" (view inspectedId im) $ Log.scope "docs" $ do-				Log.sendLog Log.Trace $ "Scanning docs for {}" ~~  view inspectedId im-				im' <- (liftM (setTag RefinedDocsTag) $ S.scanModify doScan im)-					<|> return im-				Log.sendLog Log.Trace $ "Docs for {} updated: documented {} declarations" ~~-					view inspectedId im' ~~-					length (im' ^.. inspectionResult . _Right . moduleDeclarations . each . declarationDocs . _Just)-				updater $ fromModule im'-			| otherwise = Log.sendLog Log.Trace $ "Docs for {} already scanned" ~~ view inspectedId im-		doScan _ _ m = do-			w <- askSession sessionGhc-			liftIO $ inWorker w $ do-				opts' <- getModuleOpts [] m-				haddockSession opts'-				liftGhc $ inspectDocsGhc opts' m--inferModTypes :: UpdateMonad m => [InspectedModule] -> m ()-inferModTypes = runTasks . map inferModTypes' where-	inferModTypes' im-		| not $ hasTag InferredTypesTag im = runTask "inferring types" (view inspectedId im) $ Log.scope "docs" $ do-			w <- askSession sessionGhc-			Log.sendLog Log.Trace $ "Inferring types for {}" ~~ view inspectedId im-			im' <- (liftM (setTag InferredTypesTag) $-				S.scanModify (\opts _ m -> liftIO (inWorker w (targetSession opts m >> inferTypes opts m Nothing))) im)-				<|> return im-			Log.sendLog Log.Trace $ "Types for {} inferred" ~~ view inspectedId im-			updater $ fromModule im'-		| otherwise = Log.sendLog Log.Trace $ "Types for {} already inferred" ~~ view inspectedId im---- | Generic scan function. Reads cache only if data is not already loaded, removes obsolete modules and rescans changed modules.-scan :: UpdateMonad m-	=> (FilePath -> ExceptT String IO Structured)-	-- ^ Read data from cache-	-> (Database -> Database)-	-- ^ Get data from database-	-> [S.ModuleToScan]-	-- ^ Actual modules. Other modules will be removed from database-	-> [String]-	-- ^ Extra scan options-	-> ([S.ModuleToScan] -> m ())-	-- ^ Function to update changed modules-	-> m ()-scan cache' part' mlocs opts act = Log.scope "scan" $ do-	dbval <- getCache cache' part'-	let-		obsolete = filterDB (\m -> view moduleIdLocation m `notElem` (mlocs ^.. each . _1)) (const False) dbval-	changed <- liftIO $ S.changedModules dbval opts mlocs-	cleaner $ return obsolete-	act changed--updateEvent :: ServerMonadBase m => Watched -> Event -> UpdateM m ()-updateEvent (WatchedProject proj projOpts) e-	| isSource e = do-		Log.sendLog Log.Info $ "File '{file}' in project {proj} changed"-			~~ ("file" ~% view eventPath e)-			~~ ("proj" ~% view projectName proj)-		dbval <- readDB-		let-			opts = fromMaybe [] $ do-				m <- lookupFile (view eventPath e) dbval-				preview (inspection . inspectionOpts) $ getInspected dbval m-		scanFile opts $ view eventPath e-	| isCabal e = do-		Log.sendLog Log.Info $ "Project {proj} changed"-			~~ ("proj" ~% view projectName proj)-		scanProject projOpts $ view projectCabal proj-	| otherwise = return ()-updateEvent (WatchedPackageDb pdbs opts) e-	| isConf e = do-		Log.sendLog Log.Info $ "Package db {package} changed"-			~~ ("package" ~% topPackageDb pdbs)-		scanPackageDb opts pdbs-	| otherwise = return ()-updateEvent WatchedModule e-	| isSource e = do-		Log.sendLog Log.Info $ "Module {file} changed"-			~~ ("file" ~% view eventPath e)-		dbval <- readDB-		let-			opts = fromMaybe [] $ do-				m <- lookupFile (view eventPath e) dbval-				preview (inspection . inspectionOpts) $ getInspected dbval m-		scanFile opts $ view eventPath e-	| otherwise = return ()--processEvent :: UpdateOptions -> Watched -> Event -> ClientM IO ()-processEvent uopts w e = runUpdate uopts $ updateEvent w e--watch :: SessionMonad m => (Watcher -> IO ()) -> m ()-watch f = do-	w <- askSession sessionWatcher-	liftIO $ f w+{-# LANGUAGE FlexibleContexts, OverloadedStrings, MultiParamTypeClasses, RankNTypes, TypeOperators #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Database.Update (
+	Status(..), Progress(..), Task(..),
+	UpdateOptions(..),
+
+	UpdateM(..),
+	runUpdate,
+
+	postStatus, updater, runTask, runTasks, runTasks_,
+
+	scanModules, scanFile, scanFiles, scanFileContents, scanCabal, prepareSandbox, scanSandbox, scanPackageDb, scanProjectFile, scanProjectStack, scanProject, scanDirectory, scanContents,
+	scanPackageDbStackDocs, scanDocs,
+	setModTypes, inferModTypes,
+	scan,
+	processEvents, updateEvents, applyUpdates,
+
+	module HsDev.Database.Update.Types,
+
+	module HsDev.Watcher,
+
+	module Control.Monad.Except
+	) where
+
+import qualified Control.Concurrent.Async as A
+import Control.Concurrent.MVar
+import Control.DeepSeq
+import Control.Exception (ErrorCall, evaluate, displayException)
+import Control.Lens hiding ((.=))
+import Control.Monad.Catch (catch, handle, MonadThrow)
+import Control.Monad.Except
+import Control.Monad.Reader
+import Control.Monad.Writer
+import Control.Monad.State (get, modify, evalStateT)
+import Data.Aeson
+import Data.List (intercalate)
+import qualified Data.Map.Strict as M
+import qualified Data.Set as S
+import Data.Maybe
+import Data.Text (Text)
+import qualified Data.Text as T
+import System.FilePath
+import qualified System.Log.Simple as Log
+
+import HsDev.Error
+import qualified HsDev.Database.SQLite as SQLite
+import HsDev.Display
+import HsDev.Inspect
+import HsDev.Inspect.Order
+import HsDev.PackageDb
+import HsDev.Project
+import HsDev.Sandbox
+import qualified HsDev.Stack as S
+import HsDev.Symbols
+import HsDev.Tools.Ghc.Session hiding (wait, evaluate)
+import HsDev.Tools.Ghc.Types (fileTypes, TypedExpr)
+import HsDev.Tools.Types
+import HsDev.Tools.HDocs
+import qualified HsDev.Scan as S
+import HsDev.Scan.Browse
+import HsDev.Util (ordNub, fromJSON')
+import qualified HsDev.Util as Util (withCurrentDirectory)
+import HsDev.Server.Types (commandNotify, inSessionGhc, FileSource(..))
+import HsDev.Server.Message
+import HsDev.Database.Update.Types
+import HsDev.Watcher
+import Text.Format
+import System.Directory.Paths
+
+onStatus :: UpdateMonad m => m ()
+onStatus = asks (view (updateOptions . updateTasks)) >>= commandNotify . Notification . toJSON . reverse
+
+childTask :: UpdateMonad m => Task -> m a -> m a
+childTask t = local (over (updateOptions . updateTasks) (t:))
+
+runUpdate :: ServerMonadBase m => UpdateOptions -> UpdateM m a -> ClientM m a
+runUpdate uopts act = Log.scope "update" $ do
+	(r, updatedMods) <- withUpdateState uopts $ \ust ->
+		runWriterT (runUpdateM act' `runReaderT` ust)
+	Log.sendLog Log.Debug $ "updated {} modules" ~~ length updatedMods
+	return r
+	where
+		act' = do
+			(r, _) <- listen act
+			-- (r, mlocs') <- listen act
+
+			-- dbs <- liftM S.unions $ forM mlocs' $ \mloc' -> do
+			-- 	mid <- SQLite.lookupModuleLocation mloc'
+			-- 	case mid of
+			-- 		Nothing -> return (S.empty :: S.Set PackageDb)
+			-- 		Just mid' -> liftM (S.fromList . map SQLite.fromOnly) $ SQLite.query (SQLite.toQuery $ SQLite.select_
+			-- 			["ps.package_db"]
+			-- 			["package_dbs as ps", "modules as m"]
+			-- 			["m.package_name == ps.package_name", "m.package_version == ps.package_version", "m.id == ?"]) (SQLite.Only mid')
+
+			-- If some sourced files depends on currently scanned package-dbs
+			-- We must resolve them and even rescan if there was errors scanning without
+			-- dependencies provided (lack of fixities can cause errors inspecting files)
+
+			-- sboxes = databaseSandboxes dbval
+			-- sboxOf :: Path -> Maybe Sandbox
+			-- sboxOf fpath = find (pathInSandbox fpath) sboxes
+			-- projsRows <- SQLite.query_ "select name, cabal, version, ifnull(package_db_stack, json('[]')) from projects;"
+			-- let
+			-- 	projs = [proj' | (proj' SQLite.:. (SQLite.Only (SQLite.JSON projPdbs))) <- projsRows,
+			-- 		not (S.null (S.fromList projPdbs `S.intersection` dbs))]
+
+			-- 	stands = []
+			-- 	-- HOWTO?
+			-- 	-- stands = do
+			-- 	-- 	sloc <- dbval ^.. standaloneSlice . modules . moduleId . moduleLocation
+			-- 	-- 	guard $ sboxUpdated $ sboxOf (sloc ^?! moduleFile)
+			-- 	-- 	guard (notElem sloc mlocs')
+			-- 	-- 	return (sloc, dbval ^.. databaseModules . ix sloc . inspection . inspectionOpts . each . unpacked, Nothing)
+
+			-- Log.sendLog Log.Trace $ "updated package-dbs: {}, have to rescan {} projects and {} files"
+			-- 	~~ intercalate ", " (map display $ S.toList dbs)
+			-- 	~~ length projs ~~ length stands
+			-- (_, rlocs') <- listen $ runTasks_ (scanModules [] stands : [scanProject [] (proj ^. projectCabal) | proj <- projs])
+			-- let
+			-- 	ulocs' = filter (isJust . preview moduleFile) (ordNub $ mlocs' ++ rlocs')
+			-- 	getMods :: (MonadIO m) => m [InspectedModule]
+			-- 	getMods = do
+			-- 		db' <- liftIO $ readAsync db
+			-- 		return $ filter ((`elem` ulocs') . view inspectedKey) $ toList $ view databaseModules db'
+
+			-- FIXME: Now it's broken since `Database` is not used anymore
+			when (view updateDocs uopts) $ do
+				Log.sendLog Log.Trace "forking inspecting source docs"
+				Log.sendLog Log.Warning "not implemented"
+				-- void $ fork (getMods >>= waiter . mapM_ scanDocs_)
+			when (view updateInfer uopts) $ do
+				Log.sendLog Log.Trace "forking inferring types"
+				Log.sendLog Log.Warning "not implemented"
+				-- void $ fork (getMods >>= waiter . mapM_ inferModTypes_)
+			return r
+		-- scanDocs_ :: UpdateMonad m => InspectedModule -> m ()
+		-- scanDocs_ im = do
+		-- 	im' <- (S.scanModify (\opts -> inSessionGhc . liftGhc . inspectDocsGhc opts) im) <|> return im
+		-- 	sendUpdateAction $ Log.scope "scan-docs" $ SQLite.updateModule im'
+		-- inferModTypes_ :: UpdateMonad m => InspectedModule -> m ()
+		-- inferModTypes_ im = do
+		-- 	-- TODO: locate sandbox
+		-- 	im' <- (S.scanModify infer' im) <|> return im
+		-- 	sendUpdateAction $ Log.scope "infer-types" $ SQLite.updateModule im'
+		-- infer' :: UpdateMonad m => [String] -> Module -> m Module
+		-- infer' opts m = case preview (moduleId . moduleLocation . moduleFile) m of
+		-- 	Nothing -> return m
+		-- 	Just _ -> inSessionGhc $ do
+		-- 		targetSession opts m
+		-- 		inferTypes opts m Nothing
+
+-- | Post status
+postStatus :: UpdateMonad m => Task -> m ()
+postStatus t = childTask t onStatus
+
+-- | Mark module as updated
+updater :: UpdateMonad m => [ModuleLocation] -> m ()
+updater mlocs = tell $!! mlocs
+
+-- | Run one task
+runTask :: (Display t, UpdateMonad m, NFData a) => String -> t -> m a -> m a
+runTask action subj act = Log.scope "task" $ do
+	postStatus $ set taskStatus StatusWorking task
+	x <- childTask task act
+	x `deepseq` postStatus (set taskStatus StatusOk task)
+	return x
+	`catch`
+	(\e -> postStatus (set taskStatus (StatusError e) task) >> hsdevError e)
+	where
+		task = Task {
+			_taskName = action,
+			_taskStatus = StatusWorking,
+			_taskSubjectType = displayType subj,
+			_taskSubjectName = display subj,
+			_taskProgress = Nothing }
+
+-- | Run many tasks with numeration
+runTasks :: UpdateMonad m => [m a] -> m [a]
+runTasks ts = liftM catMaybes $ zipWithM taskNum [1..] (map noErr ts) where
+	total = length ts
+	taskNum n = local setProgress where
+		setProgress = set (updateOptions . updateTasks . _head . taskProgress) (Just (Progress n total))
+	noErr v = hsdevIgnore Nothing (Just <$> v)
+
+-- | Run many tasks with numeration
+runTasks_ :: UpdateMonad m => [m ()] -> m ()
+runTasks_ = void . runTasks
+
+-- | Scan modules
+scanModules :: UpdateMonad m => [String] -> [S.ModuleToScan] -> m ()
+scanModules opts ms = Log.scope "scan-modules" $ mapM_ (uncurry scanModules') grouped where
+	scanModules' mproj ms' = do
+		pdbs <- maybe (return userDb) (inSessionGhc . getProjectPackageDbStack) mproj
+		case mproj of
+			Just proj -> sendUpdateAction $ SQLite.updateProject proj (Just pdbs)
+			Nothing -> return ()
+		updater $ ms' ^.. each . _1
+		defines <- askSession sessionDefines
+
+		let
+			pload (mloc, mopts, mcts) = runTask "preloading" mloc $ do
+				mfcts <- maybe (S.getFileContents (mloc ^?! moduleFile)) (const $ return Nothing) mcts
+				case (mfcts ^? _Just . _1) of
+					Just tm -> Log.sendLog Log.Trace $ "using edited file contents, mtime = {}" ~~ show tm
+					Nothing -> return ()
+				let
+					resetInspection = maybe id (set preloadedTime . fileContentsInspection_ (opts ++ mopts)) $ mfcts ^? _Just . _1
+					mcts' = mplus mcts (mfcts ^? _Just . _2)
+				p <- liftIO $ preload (mloc ^?! moduleFile) defines (opts ++ mopts) mloc mcts'
+				return $ resetInspection p
+
+		ploaded <- runTasks (map pload ms')
+		mlocs' <- forM ploaded $ \p -> do
+			let
+				mloc = p ^. preloadedId . moduleLocation
+				inspectedMod = Inspected (p ^. preloadedTime) mloc (tag OnlyHeaderTag) $ Right $ p ^. asModule
+			sendUpdateAction $ Log.scope "preloaded" $ SQLite.updateModule inspectedMod
+			return mloc
+		updater mlocs'
+
+		(sqlMods', sqlAenv') <- do
+			let
+				mprojectDeps = SQLite.buildQuery $ SQLite.select_
+					["ps.module_id"]
+					["projects_modules_scope as ps"]
+					["ps.cabal is ?"]
+			sqlMods' <- SQLite.loadModules mprojectDeps (SQLite.Only $ mproj ^? _Just . projectCabal)
+			return (sqlMods', mconcat (map moduleAnalyzeEnv sqlMods'))
+
+		Log.sendLog Log.Trace $ "resolving environment: {} modules" ~~ length sqlMods'
+		case order ploaded of
+			Left err -> Log.sendLog Log.Error ("failed order dependencies for files: {}" ~~ show err)
+			Right ordered -> do
+				ms'' <- flip evalStateT sqlAenv' $ runTasks (map inspect' ordered)
+				mlocs'' <- forM ms'' $ \im -> do
+					sendUpdateAction $ Log.scope "resolved" $ SQLite.updateModule im
+					return $ im ^. inspectedKey
+				updater mlocs''
+				where
+					inspect' pmod = runTask "scanning" (pmod ^. preloadedId . moduleLocation) $ Log.scope "module" $ do
+						aenv <- get
+						m <- either (hsdevError . InspectError) eval $ analyzePreloaded aenv pmod
+						modify (mappend (moduleAnalyzeEnv m))
+						return $ Inspected (pmod ^. preloadedTime) (m ^. moduleId . moduleLocation) mempty (Right m)
+	grouped = M.toList $ M.unionsWith (++) [M.singleton (m ^? _1 . moduleProject . _Just) [m] | m <- ms]
+	eval v = handle onError (v `deepseq` liftIO (evaluate v)) where
+		onError :: MonadThrow m => ErrorCall -> m a
+		onError = hsdevError . OtherError . displayException
+
+-- | Scan source file, resolve dependent modules
+scanFile :: UpdateMonad m => [String] -> Path -> m ()
+scanFile opts fpath = scanFiles [(FileSource fpath Nothing, opts)]
+
+-- | Scan source files, resolving dependent modules
+scanFiles :: UpdateMonad m => [(FileSource, [String])] -> m ()
+scanFiles fsrcs = runTask "scanning" ("files" :: String) $ Log.scope "files" $ hsdevLiftIO $ do
+	Log.sendLog Log.Trace $ "scanning {} files" ~~ length fsrcs
+	fpaths' <- traverse (liftIO . canonicalize) $ map (fileSource . fst) fsrcs
+	forM_ fpaths' $ \fpath' -> do
+		ex <- liftIO $ fileExists fpath'
+		unless ex $ hsdevError $ FileNotFound fpath'
+	mlocs <- forM fpaths' $ \fpath' -> do
+		mids <- SQLite.query (SQLite.toQuery $ SQLite.qModuleId `mappend` SQLite.where_ ["mu.file == ?"]) (SQLite.Only fpath')
+		if length mids > 1
+			then return (head mids ^. moduleLocation)
+			else do
+				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')
+	scan filesMods [(mloc, opts, mcts) | (mloc, (FileSource _ mcts, opts)) <- zip mlocs fsrcs] [] $ \mlocs' -> do
+		mapM_ (watch . flip watchModule) (map (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)
+
+-- | Scan source file with contents and resolve dependent modules
+scanFileContents :: UpdateMonad m => [String] -> Path -> Maybe Text -> m ()
+scanFileContents opts fpath mcts = scanFiles [(FileSource fpath mcts, opts)]
+
+-- | Scan cabal modules, doesn't rescan if already scanned
+scanCabal :: UpdateMonad m => [String] -> m ()
+scanCabal opts = Log.scope "cabal" $ scanPackageDbStack opts userDb
+
+-- | Prepare sandbox for scanning. This is used for stack project to build & configure.
+prepareSandbox :: UpdateMonad m => Sandbox -> m ()
+prepareSandbox sbox@(Sandbox StackWork fpath) = Log.scope "prepare" $ runTasks_ [
+	runTask "building dependencies" sbox $ void $ Util.withCurrentDirectory dir $ inSessionGhc $ S.buildDeps Nothing,
+	runTask "configuring" sbox $ void $ Util.withCurrentDirectory dir $ inSessionGhc $ S.configure Nothing]
+	where
+		dir = takeDirectory $ view path fpath
+prepareSandbox _ = return ()
+
+-- | Scan sandbox modules, doesn't rescan if already scanned
+scanSandbox :: UpdateMonad m => [String] -> Sandbox -> m ()
+scanSandbox opts sbox = Log.scope "sandbox" $ do
+	prepareSandbox sbox
+	pdbs <- inSessionGhc $ sandboxPackageDbStack sbox
+	scanPackageDbStack opts pdbs
+
+-- | Scan top of package-db stack, usable for rescan
+scanPackageDb :: UpdateMonad m => [String] -> PackageDbStack -> m ()
+scanPackageDb opts pdbs = runTask "scanning" (topPackageDb pdbs) $ Log.scope "package-db" $ do
+	pdbState <- liftIO $ readPackageDb (topPackageDb pdbs)
+	let
+		packageDbMods = S.fromList $ concat $ M.elems pdbState
+		packages' = M.keys pdbState
+	Log.sendLog Log.Trace $ "package-db state: {} modules" ~~ length packageDbMods
+	watch (\w -> watchPackageDb w pdbs opts)
+
+	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"
+		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))
+			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
+				sendUpdateAction $ do
+					mapM_ SQLite.updateModule ms
+					SQLite.updatePackageDb (topPackageDb pdbs) (M.keys pdbState)
+
+				when hdocsSupported $ scanPackageDbStackDocs opts pdbs
+
+				updater $ ms ^.. each . inspectedKey
+
+-- | Scan top of package-db stack, usable for rescan
+scanPackageDbStack :: UpdateMonad m => [String] -> PackageDbStack -> m ()
+scanPackageDbStack opts pdbs = runTask "scanning" pdbs $ Log.scope "package-db-stack" $ do
+	pdbStates <- liftIO $ mapM readPackageDb (packageDbs pdbs)
+	let
+		packageDbMods = S.fromList $ concat $ concatMap M.elems pdbStates
+		packages' = ordNub $ concatMap M.keys pdbStates
+	Log.sendLog Log.Trace $ "package-db-stack state: {} modules" ~~ length packageDbMods
+	watch (\w -> watchPackageDbStack w pdbs opts)
+
+	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"
+		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)
+			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
+				sendUpdateAction $ do
+					mapM_ SQLite.updateModule ms
+					sequence_ [SQLite.updatePackageDb pdb (M.keys pdbState) | (pdb, pdbState) <- zip (packageDbs pdbs) pdbStates]
+
+				-- BUG: I don't know why, but these steps leads to segfault on my PC:
+				-- > hsdev scan --cabal --project .
+				-- > hsdev check -f .\src\HsDev\Client\Commands.hs
+				-- But it works if docs are scanned, it also works from ghci
+				
+				-- needDocs <- asks (view updateDocs)
+				-- ms' <- if needDocs
+				-- 	then do
+				-- 		docs <- inSessionGhc $ hdocsCabal pdbs opts
+				-- 		return $ map (fmap $ setDocs' docs) ms
+				-- 	else return ms
+
+				when hdocsSupported $ scanPackageDbStackDocs opts pdbs
+
+				updater $ ms ^.. each . inspectedKey
+
+-- | Scan project file
+scanProjectFile :: UpdateMonad m => [String] -> Path -> m Project
+scanProjectFile opts cabal = runTask "scanning" cabal $ do
+	proj <- S.scanProjectFile opts cabal
+	pdbs <- inSessionGhc $ getProjectPackageDbStack proj
+	sendUpdateAction $ Log.scope "scan-project-file" $ SQLite.updateProject proj (Just pdbs)
+	return proj
+
+-- | 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))
+	if exist
+		then SQLite.loadProject (proj ^. projectCabal)
+		else runTask "scanning" (proj ^. projectCabal) $ do
+			proj' <- liftIO $ loadProject proj
+			pdbs <- inSessionGhc $ getProjectPackageDbStack proj'
+			sendUpdateAction $ Log.scope "refine-project-info" $ SQLite.updateProject proj' (Just pdbs)
+			return proj'
+
+-- | Get project info for module
+locateProjectInfo :: UpdateMonad m => Path -> m (Maybe Project)
+locateProjectInfo cabal = liftIO (locateProject (view path cabal)) >>= traverse refineProjectInfo
+
+-- | Scan project and related package-db stack
+scanProjectStack :: UpdateMonad m => [String] -> Path -> m ()
+scanProjectStack opts cabal = do
+	proj <- scanProjectFile opts cabal
+	scanProject opts cabal
+	sbox <- liftIO $ projectSandbox (view projectPath proj)
+	maybe (scanCabal opts) (scanSandbox opts) sbox
+
+-- | Scan project
+scanProject :: UpdateMonad m => [String] -> Path -> m ()
+scanProject opts cabal = runTask "scanning" (project $ view path cabal) $ Log.scope "project" $ do
+	proj <- scanProjectFile opts cabal
+	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'
+
+		-- Scan docs
+		-- inSessionGhc $ do
+		-- 	currentSession >>= maybe (return ()) (const clearTargets)
+
+		-- 	forM_ (maybe [] targetInfos (proj ^. projectDescription)) $ \tinfo' -> do
+		-- 		opts' <- getProjectTargetOpts [] proj (tinfo' ^. targetBuildInfo)
+		-- 		files' <- projectTargetFiles proj tinfo'
+		-- 		haddockSession opts'
+		-- 		docsMap <- liftGhc $ readProjectTargetDocs opts' proj files'
+		-- 		Log.sendLog Log.Debug $ "scanned logs for modules: {}, summary docs: {}" ~~ (intercalate "," (M.keys docsMap)) ~~ (sum $ map M.size $ M.elems docsMap)
+
+
+-- | Scan directory for source files and projects
+scanDirectory :: UpdateMonad m => [String] -> Path -> m ()
+scanDirectory opts dir = runTask "scanning" dir $ Log.scope "directory" $ do
+	S.ScanContents standSrcs projSrcs pdbss <- S.enumDirectory (view path dir)
+	runTasks_ [scanProject opts (view projectCabal p) | (p, _) <- projSrcs]
+	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` "%")
+	scan standaloneMods standSrcs opts $ scanModules opts
+
+scanContents :: UpdateMonad m => [String] -> S.ScanContents -> m ()
+scanContents opts (S.ScanContents standSrcs projSrcs pdbss) = do
+	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;"
+	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
+	scan filesMods standSrcs opts $ scanModules opts
+
+-- | Scan installed docs
+scanPackageDbStackDocs :: UpdateMonad m => [String] -> PackageDbStack -> m ()
+scanPackageDbStackDocs opts pdbs
+	| hdocsSupported = Log.scope "docs" $ do
+		docs <- inSessionGhc $ hdocsCabal pdbs opts
+		Log.sendLog Log.Trace $ "docs scanned: {} packages, {} modules total"
+			~~ length docs ~~ sum (map (M.size . snd) docs)
+		sendUpdateAction $ SQLite.executeMany "update symbols set docs = ? where name == ? and module_id in (select id from modules where name == ? and package_name == ? and package_version == ?);" $ do
+			(ModulePackage pname pver, pdocs) <- docs
+			(mname, mdocs) <- M.toList pdocs
+			(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"
+
+-- | Scan docs for inspected modules
+scanDocs :: UpdateMonad m => [Module] -> m ()
+scanDocs
+	| hdocsSupported = runTasks_ . map scanDocs'
+	| 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)
+			mid' <- maybe (hsdevError $ SQLiteError "module id not found") return mid
+			m' <- mapMOf (moduleId . moduleLocation . moduleProject . _Just) refineProjectInfo m
+			Log.sendLog Log.Trace $ "Scanning docs for {}" ~~ view (moduleId . moduleLocation) m'
+			docsMap <- inSessionGhc $ do
+				opts' <- getModuleOpts [] m'
+				currentSession >>= maybe (return ()) (const clearTargets)
+				-- Calling haddock with targets set sometimes cause errors
+				haddockSession opts'
+				readModuleDocs opts' m'
+			sendUpdateAction $ do
+				SQLite.executeMany "update symbols set docs = ? where name == ? and module_id == ?;"
+					[(doc, nm, mid') | (nm, doc) <- maybe [] M.toList docsMap]
+				SQLite.execute "update modules set tags = json_set(tags, '$.docs', 1) where id == ?;" (SQLite.Only mid')
+
+-- | Set inferred types for module
+setModTypes :: UpdateMonad m => ModuleId -> [Note TypedExpr] -> m ()
+setModTypes m ts = Log.scope "set-types" $ do
+	mid <- SQLite.lookupModule m
+	mid' <- maybe (hsdevError $ SQLiteError "module id not found") return mid
+	sendUpdateAction $ do
+		SQLite.executeMany "insert into types (module_id, line, column, line_to, column_to, expr, type) values (?, ?, ?, ?, ?, ?, ?);" [
+			(SQLite.Only mid' SQLite.:. view noteRegion n' SQLite.:. view note n') | n' <- ts]
+		SQLite.execute "update names set inferred_type = (select type from types as t where t.module_id = ? and names.line = t.line and names.column = t.column and names.line_to = t.line_to and names.column_to = t.column_to) where module_id == ?;"
+			(mid', mid')
+		SQLite.execute "update symbols set type = (select type from types as t where t.module_id = ? and symbols.line = t.line and symbols.column = t.column order by t.line_to, t.column_to) where module_id == ?;" (mid', mid')
+		SQLite.execute "update modules set tags = json_set(tags, '$.types', 1) where id == ?;" (SQLite.Only mid')
+
+-- | Infer types for modules
+inferModTypes :: UpdateMonad m => [Module] -> m ()
+inferModTypes = runTasks_ . map inferModTypes' where
+	inferModTypes' m = runTask "inferring types" (view (moduleId . moduleLocation) m) $ Log.scope "types" $ do
+		mid <- SQLite.lookupModule (m ^. moduleId)
+		_ <- maybe (hsdevError $ SQLiteError "module id not found") return mid
+		m' <- mapMOf (moduleId . moduleLocation . moduleProject . _Just) refineProjectInfo m
+		Log.sendLog Log.Trace $ "Inferring types for {}" ~~ view (moduleId . moduleLocation) m'
+
+		mcts <- fmap (fmap snd) $ S.getFileContents (m' ^?! moduleId . moduleLocation . moduleFile)
+		types' <- inSessionGhc $ do
+			opts' <- getModuleOpts [] m'
+			targetSession opts' m'
+			fileTypes m' mcts
+
+		setModTypes (m' ^. moduleId) types'
+
+-- | Generic scan function. Removed obsolete modules and calls callback on changed modules.
+scan :: UpdateMonad m
+	=> 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
+	-> [String]
+	-- ^ Extra scan options
+	-> ([S.ModuleToScan] -> m ())
+	-- ^ 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'
+	let
+		obsolete = M.filterWithKey (\k _ -> k `S.notMember` S.fromList (map (^. _1) mlocs)) mlocs'
+	changed <- S.changedModules (M.map snd mlocs') opts mlocs
+	sendUpdateAction $ Log.scope "remove-obsolete" $ forM_ (M.elems obsolete) $ SQLite.removeModule . fst
+	act changed
+
+processEvents :: ([(Watched, Event)] -> IO ()) -> MVar (A.Async ()) -> MVar [(Watched, Event)] -> [(Watched, Event)] -> ClientM IO ()
+processEvents handleEvents updaterTask eventsVar evs = Log.scope "event" $ do
+	Log.sendLog Log.Trace $ "events received: {}" ~~ intercalate ", " (evs ^.. each . _2 . eventPath)
+	l <- Log.askLog
+	liftIO $ do
+		modifyMVar_ eventsVar (return . (++evs))
+		modifyMVar_ updaterTask $ \task -> do
+			done <- fmap isJust $ poll task
+			if done
+				then do
+					Log.withLog l $ Log.sendLog Log.Trace "starting update thread"
+					A.async $ fix $ \loop -> do
+						updates <- modifyMVar eventsVar (\es -> return ([], es))
+						unless (null updates) $ handleEvents updates >> loop
+				else return task
+
+updateEvents :: ServerMonadBase m => [(Watched, Event)] -> UpdateM m ()
+updateEvents updates = Log.scope "updater" $ do
+	Log.sendLog Log.Trace $ "prepared to process {} events" ~~ length updates
+	files <- fmap concat $ forM updates $ \(w, e) -> case w of
+		WatchedProject proj projOpts
+			| isSource e -> do
+				Log.sendLog Log.Info $ "File '{file}' in project {proj} changed"
+					~~ ("file" ~% view eventPath e)
+					~~ ("proj" ~% view projectName proj)
+				[SQLite.Only mopts] <- SQLite.query "select inspection_opts from modules where file == ?;" (SQLite.Only $ view eventPath e)
+				opts <- maybe (return []) (maybe (parseErr' mopts) return . fromJSON') mopts
+				return [(FileSource (fromFilePath $ view eventPath e) Nothing, opts)]
+			| isCabal e -> do
+				Log.sendLog Log.Info $ "Project {proj} changed"
+					~~ ("proj" ~% view projectName proj)
+				scanProject projOpts $ view projectCabal proj
+				return []
+			| otherwise -> return []
+		WatchedPackageDb pdbs opts
+			| isConf e -> do
+				Log.sendLog Log.Info $ "Package db {package} changed"
+					~~ ("package" ~% topPackageDb pdbs)
+				scanPackageDb opts pdbs
+				return []
+			| otherwise -> return []
+		WatchedModule
+			| isSource e -> do
+				Log.sendLog Log.Info $ "Module {file} changed"
+					~~ ("file" ~% view eventPath e)
+				[SQLite.Only mopts] <- SQLite.query "select inspection_opts from modules where file == ?;" (SQLite.Only $ view eventPath e)
+				opts <- maybe (return []) (maybe (parseErr' mopts) return . fromJSON') mopts
+				return [(FileSource (fromFilePath $ view eventPath e) Nothing, opts)]
+			| otherwise -> return []
+	scanFiles files
+	where
+		parseErr' mopts' = do
+			Log.sendLog Log.Error $ "Error parsing inspection_opts: {}" ~~ show mopts'
+			hsdevError $ SQLiteError $ "Error parsing inspection_opts: {}" ~~ show mopts'
+
+applyUpdates :: UpdateOptions -> [(Watched, Event)] -> ClientM IO ()
+applyUpdates uopts = runUpdate uopts . updateEvents
+
+watch :: SessionMonad m => (Watcher -> IO ()) -> m ()
+watch f = do
+	w <- askSession sessionWatcher
+	liftIO $ f w
src/HsDev/Database/Update/Types.hs view
@@ -1,8 +1,11 @@ {-# LANGUAGE GeneralizedNewtypeDeriving, OverloadedStrings, FlexibleInstances, MultiParamTypeClasses, TypeFamilies, UndecidableInstances, ConstraintKinds, FlexibleContexts, TemplateHaskell #-}
 
 module HsDev.Database.Update.Types (
-	Status(..), Progress(..), Task(..), UpdateOptions(..), UpdateM(..), UpdateMonad,
-	taskName, taskStatus, taskSubjectType, taskSubjectName, taskProgress, updateTasks, updateGhcOpts, updateDocs, updateInfer,
+	Status(..), Progress(..), Task(..),
+	UpdateOptions(..), updateTasks, updateGhcOpts, updateDocs, updateInfer,
+	UpdateState(..), updateOptions, updateWorker, withUpdateState, sendUpdateAction,
+	UpdateM(..), UpdateMonad,
+	taskName, taskStatus, taskSubjectType, taskSubjectName, taskProgress,
 
 	module HsDev.Server.Types
 	) where
@@ -17,13 +20,17 @@ import Control.Monad.Writer
 import Control.Monad.Trans.Control
 import Data.Aeson
+import Data.Functor
 import Data.Default
 import qualified System.Log.Simple as Log
+import qualified System.Log.Simple.Monad as Log
 
-import HsDev.Server.Types (ServerMonadBase, Session(..), CommandOptions(..), SessionMonad(..), askSession, CommandMonad(..), ClientM(..))
+import Control.Concurrent.Worker
+import HsDev.Database.SQLite
+import HsDev.Server.Types hiding (Command(..))
 import HsDev.Symbols
 import HsDev.Types
-import HsDev.Util ((.::))
+import HsDev.Util ((.::), logAll)
 
 data Status = StatusWorking | StatusOk | StatusError HsDevError
 
@@ -34,8 +41,8 @@ 
 instance FromJSON Status where
 	parseJSON v = msum $ map ($ v) [
-		withText "status" $ \t -> guard (t == "working") *> return StatusWorking,
-		withText "status" $ \t -> guard (t == "ok") *> return StatusOk,
+		withText "status" $ \t -> guard (t == "working") $> StatusWorking,
+		withText "status" $ \t -> guard (t == "ok") $> StatusOk,
 		liftM StatusError . parseJSON,
 		fail "invalid status"]
 
@@ -87,11 +94,36 @@ 
 makeLenses ''UpdateOptions
 
-type UpdateMonad m = (CommandMonad m, MonadReader UpdateOptions m, MonadWriter [ModuleLocation] m)
+data UpdateState = UpdateState {
+	_updateOptions :: UpdateOptions,
+	_updateWorker :: Worker (ServerM IO) }
 
-newtype UpdateM m a = UpdateM { runUpdateM :: ReaderT UpdateOptions (WriterT [ModuleLocation] (ClientM m)) a }
-	deriving (Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadThrow, MonadCatch, MonadMask, Functor, MonadReader UpdateOptions, MonadWriter [ModuleLocation])
+makeLenses ''UpdateState
 
+withUpdateState :: SessionMonad m => UpdateOptions -> (UpdateState -> m a) -> m a
+withUpdateState uopts fn = do
+	session <- getSession
+	bracket (liftIO $ startWorker (withSession session . Log.component "sqlite" . Log.scope "update") enterTransaction logAll) (liftIO . joinWorker) $ \w ->
+		fn (UpdateState uopts w)
+	where
+		enterTransaction act = do
+			Log.sendLog Log.Trace "entering sqlite transaction"
+			transaction_ Immediate $ do
+				Log.sendLog Log.Debug "updating sql database"
+				_ <- act
+				Log.sendLog Log.Debug "sql database updated"
+
+type UpdateMonad m = (CommandMonad m, MonadReader UpdateState m, MonadWriter [ModuleLocation] m)
+
+sendUpdateAction :: UpdateMonad m => ServerM IO () -> m ()
+sendUpdateAction act = do
+	w <- asks _updateWorker
+	s <- Log.askScope
+	liftIO $ inWorker w $ Log.localLog (Log.subLog mempty s) $ act
+
+newtype UpdateM m a = UpdateM { runUpdateM :: ReaderT UpdateState (WriterT [ModuleLocation] (ClientM m)) a }
+	deriving (Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadThrow, MonadCatch, MonadMask, Functor, MonadReader UpdateState, MonadWriter [ModuleLocation])
+
 instance MonadTrans UpdateM where
 	lift = UpdateM . lift . lift . lift
 
@@ -101,6 +133,7 @@ 
 instance ServerMonadBase m => SessionMonad (UpdateM m) where
 	getSession = UpdateM $ lift $ lift getSession
+	localSession fn = UpdateM . hoist (hoist (localSession fn)) . runUpdateM
 
 instance ServerMonadBase m => CommandMonad (UpdateM m) where
 	getOptions = UpdateM $ lift $ lift getOptions
@@ -109,6 +142,6 @@ 	liftBase = UpdateM . liftBase
 
 instance MonadBaseControl b m => MonadBaseControl b (UpdateM m) where
-	type StM (UpdateM m) a = StM (ReaderT UpdateOptions (WriterT [ModuleLocation] (ClientM m))) a
+	type StM (UpdateM m) a = StM (ReaderT UpdateState (WriterT [ModuleLocation] (ClientM m))) a
 	liftBaseWith f = UpdateM $ liftBaseWith (\f' -> f (f' . runUpdateM))
 	restoreM = UpdateM . restoreM
src/HsDev/Display.hs view
@@ -1,55 +1,78 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Display (-	Display(..)-	) where--import Control.Lens (view)-import Data.Maybe (fromMaybe)--import Text.Format--import HsDev.PackageDb-import HsDev.Project-import HsDev.Sandbox-import HsDev.Symbols.Location--class Display a where-	display :: a -> String-	displayType :: a -> String--instance Display PackageDb where-	display GlobalDb = "global-db"-	display UserDb = "user-db"-	display (PackageDb p) = "package-db " ++ p-	displayType _ = "package-db"--instance Display ModuleLocation where-	display (FileModule f _) = f-	display (InstalledModule _ _ n) = n-	display (ModuleSource s) = fromMaybe "" s-	displayType _ = "module"--instance Display Project where-	display = view projectName-	displayType _ = "project"--instance Display Sandbox where-	display (Sandbox _ fpath) = fpath-	displayType (Sandbox CabalSandbox _) = "cabal-sandbox"-	displayType (Sandbox StackWork _) = "stack-work"--instance Display FilePath where-	display = id-	displayType _ = "path"--instance Formattable PackageDb where-	formattable = formattable . display--instance Formattable ModuleLocation where-	formattable = formattable . display--instance Formattable Project where+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Display (
+	Display(..)
+	) where
+
+import Control.Lens (view)
+import Data.List (intercalate)
+import Data.Text.Lens (unpacked)
+
+import Text.Format
+
+import System.Directory.Paths
+import HsDev.PackageDb
+import HsDev.Project
+import HsDev.Sandbox
+import HsDev.Symbols.Location
+import HsDev.Symbols.Types
+
+class Display a where
+	display :: a -> String
+	displayType :: a -> String
+
+instance Display PackageDb where
+	display GlobalDb = "global-db"
+	display UserDb = "user-db"
+	display (PackageDb p) = "package-db " ++ display p
+	displayType _ = "package-db"
+
+instance Display PackageDbStack where
+	display = intercalate "/" . map display . packageDbs
+	displayType _ = "package-db-stack"
+
+instance Display ModuleLocation where
+	display (FileModule f _) = display f
+	display (InstalledModule _ _ n) = view unpacked n
+	display (OtherLocation s) = view unpacked s
+	display NoLocation = "<no-location>"
+	displayType _ = "module"
+
+instance Display ModuleTag where
+	display InferredTypesTag = "types"
+	display RefinedDocsTag = "docs"
+	display OnlyHeaderTag = "header"
+	displayType _ = "module-tag"
+
+instance Display Project where
+	display = view (projectName . unpacked)
+	displayType _ = "project"
+
+instance Display Sandbox where
+	display (Sandbox _ fpath) = display fpath
+	displayType (Sandbox CabalSandbox _) = "cabal-sandbox"
+	displayType (Sandbox StackWork _) = "stack-work"
+
+instance Display FilePath where
+	display = id
+	displayType _ = "path"
+
+instance Display Path where
+	display = view path
+	displayType _ = "path"
+
+instance Formattable PackageDb where
+	formattable = formattable . display
+
+instance Formattable PackageDbStack where
+	formattable = formattable . display
+
+instance Formattable ModuleLocation where
+	formattable = formattable . display
+
+instance Formattable Project where
+	formattable = formattable . display
+
+instance Formattable Sandbox where
 	formattable = formattable . display-
src/HsDev/Error.hs view
@@ -1,63 +1,53 @@-{-# LANGUAGE FlexibleContexts #-}--module HsDev.Error (-	hsdevError, hsdevOtherError, hsdevLift, hsdevLiftWith, hsdevCatch, hsdevExcept, hsdevLiftIO, hsdevLiftIOWith, hsdevIgnore,-	hsdevHandle, hsdevLog,--	module HsDev.Types-	) where--import Prelude hiding (log)--import Control.Exception (IOException)-import Control.Monad.Catch-import Control.Monad.Except-import Data.String (fromString)-import System.Log.Simple (MonadLog, sendLog, Level)--import HsDev.Types---- | Throw `HsDevError`-hsdevError :: MonadThrow m => HsDevError -> m a-hsdevError = throwM---- | Throw as `OtherError`-hsdevOtherError :: (Exception e, MonadThrow m) => e -> m a-hsdevOtherError = hsdevError . OtherError . displayException---- | Throw as `OtherError`-hsdevLift :: MonadThrow m => ExceptT String m a -> m a-hsdevLift = hsdevLiftWith OtherError---- | Throw as some `HsDevError`-hsdevLiftWith :: MonadThrow m => (String -> HsDevError) -> ExceptT String m a -> m a-hsdevLiftWith ctor act = runExceptT act >>= either (hsdevError . ctor) return--hsdevCatch :: MonadCatch m => m a -> m (Either HsDevError a)-hsdevCatch = try--hsdevExcept :: MonadCatch m => m a -> ExceptT HsDevError m a-hsdevExcept = ExceptT . hsdevCatch---- | Rethrow IO exceptions as `HsDevError`-hsdevLiftIO :: MonadCatch m => m a -> m a-hsdevLiftIO = hsdevLiftIOWith IOFailed---- | Rethrow IO exceptions-hsdevLiftIOWith :: MonadCatch m => (String -> HsDevError) -> m a -> m a-hsdevLiftIOWith ctor act = catch act onError where-	onError :: MonadThrow m => IOException -> m a-	onError = hsdevError . ctor . displayException---- | Ignore hsdev exception-hsdevIgnore :: MonadCatch m => a -> m a -> m a-hsdevIgnore v act = hsdevCatch act >>= either (const $ return v) return---- | Handle hsdev exception-hsdevHandle :: MonadCatch m => (HsDevError -> m a) -> m a -> m a-hsdevHandle h act = hsdevCatch act >>= either h return---- | Log hsdev exception and rethrow-hsdevLog :: MonadLog m => Level -> m a -> m a-hsdevLog lev act = hsdevCatch act >>= either logError return where-	logError e = sendLog lev (fromString $ show e) >> hsdevError e+{-# LANGUAGE FlexibleContexts #-}
+
+module HsDev.Error (
+	hsdevError, hsdevOtherError, hsdevLift, hsdevLiftWith, hsdevCatch, hsdevLiftIO, hsdevLiftIOWith, hsdevIgnore,
+	hsdevHandle,
+
+	module HsDev.Types
+	) where
+
+import Prelude
+
+import Control.Exception (IOException)
+import Control.Monad.Catch
+import Control.Monad.Except
+
+import HsDev.Types
+
+-- | Throw `HsDevError`
+hsdevError :: MonadThrow m => HsDevError -> m a
+hsdevError = throwM
+
+-- | Throw as `OtherError`
+hsdevOtherError :: (Exception e, MonadThrow m) => e -> m a
+hsdevOtherError = hsdevError . OtherError . displayException
+
+-- | Throw as `OtherError`
+hsdevLift :: MonadThrow m => ExceptT String m a -> m a
+hsdevLift = hsdevLiftWith OtherError
+
+-- | Throw as some `HsDevError`
+hsdevLiftWith :: MonadThrow m => (String -> HsDevError) -> ExceptT String m a -> m a
+hsdevLiftWith ctor act = runExceptT act >>= either (hsdevError . ctor) return
+
+hsdevCatch :: MonadCatch m => m a -> m (Either HsDevError a)
+hsdevCatch = try
+
+-- | Rethrow IO exceptions as `HsDevError`
+hsdevLiftIO :: MonadCatch m => m a -> m a
+hsdevLiftIO = hsdevLiftIOWith IOFailed
+
+-- | Rethrow IO exceptions
+hsdevLiftIOWith :: MonadCatch m => (String -> HsDevError) -> m a -> m a
+hsdevLiftIOWith ctor act = catch act onError where
+	onError :: MonadThrow m => IOException -> m a
+	onError = hsdevError . ctor . displayException
+
+-- | Ignore hsdev exception
+hsdevIgnore :: MonadCatch m => a -> m a -> m a
+hsdevIgnore v act = hsdevCatch act >>= either (const $ return v) return
+
+-- | Handle hsdev exception
+hsdevHandle :: MonadCatch m => (HsDevError -> m a) -> m a -> m a
+hsdevHandle h act = hsdevCatch act >>= either h return
src/HsDev/Inspect.hs view
@@ -1,467 +1,450 @@-{-# LANGUAGE TypeSynonymInstances #-}--module HsDev.Inspect (-	analyzeModule, inspectDocsChunk, inspectDocs, inspectDocsGhc,-	inspectContents, contentsInspection,-	inspectFile, fileInspection,-	projectDirs, projectSources,-	inspectProject,-	getDefines,-	preprocess, preprocess_,--	module Control.Monad.Except-	) where--import Control.Arrow-import Control.Applicative-import Control.DeepSeq-import qualified Control.Exception as E-import Control.Lens (view, preview, set, over)-import Control.Lens.At (ix)-import Control.Monad-import Control.Monad.Except-import Data.Char (isSpace)-import Data.Function (on)-import Data.List-import Data.Map (Map)-import Data.Maybe (fromMaybe, mapMaybe, catMaybes, listToMaybe, isJust)-import Data.Ord (comparing)-import Data.String (IsString, fromString)-import Data.Text (Text)-import qualified Data.Text as T (unpack)-import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, getPOSIXTime)-import qualified Data.Map as M-import qualified Language.Haskell.Exts as H-import qualified Language.Preprocessor.Cpphs as Cpphs-import qualified System.Directory as Dir-import System.FilePath-import Data.Generics.Uniplate.Data-import HDocs.Haddock--import HsDev.Error-import HsDev.Symbols-import HsDev.Tools.Base-import HsDev.Tools.Ghc.Worker ()-import HsDev.Tools.HDocs (hdocsy, hdocs, hdocsProcess)-import HsDev.Util---- | Analize source contents-analyzeModule :: [String] -> Maybe FilePath -> String -> Either String Module-analyzeModule exts file source = case H.parseFileContentsWithMode (parseMode file exts) source' of-	H.ParseFailed loc reason -> Left $ "Parse failed at " ++ show loc ++ ": " ++ reason-	H.ParseOk (H.Module _ (Just (H.ModuleHead _ (H.ModuleName _ mname) _ mexports)) _ imports declarations) -> Right Module {-		_moduleName = fromString mname,-		_moduleDocs =  Nothing,-		_moduleLocation = ModuleSource Nothing,-		_moduleExports = fmap (concatMap getExports . getSpec) mexports,-		_moduleImports = map getImport imports,-		_moduleDeclarations = sortDeclarations $ getDecls declarations }-	_ -> Left "Unknown module"-	where-		-- Replace all tabs to spaces to make SrcLoc valid, otherwise it treats tab as 8 spaces-		source' = map untab source-		untab '\t' = ' '-		untab ch = ch-		getSpec (H.ExportSpecList _ es) = es---- | Analize source contents-analyzeModule_ :: [String] -> Maybe FilePath -> String -> Either String Module-analyzeModule_ exts file source = do-	mname <- parseModuleName source'-	return Module {-		_moduleName = fromString mname,-		_moduleDocs = Nothing,-		_moduleLocation = ModuleSource Nothing,-		_moduleExports = do-			H.PragmasAndModuleHead _ _ mhead <- parseModuleHead' source'-			H.ModuleHead _ _ _ mexports <- mhead-			H.ExportSpecList _ exports <- mexports-			return $ concatMap getExports exports,-		_moduleImports = map getImport $ mapMaybe (uncurry parseImport') parts,-		_moduleDeclarations = sortDeclarations $ getDecls $ mapMaybe (uncurry parseDecl') parts }-	where-		parts :: [(Int, String)]-		parts = zip offsets (map unlines parts') where-			parts' :: [[String]]-			parts' = unfoldr break' $ lines source'-			offsets = scanl (+) 0 $ map length parts'-		break' :: [String] -> Maybe ([String], [String])-		break' [] = Nothing-		break' (l:ls) = Just $ first (l:) $ span (maybe True isSpace . listToMaybe) ls--		parseModuleName :: String -> Either String String-		parseModuleName cts = maybe (Left "match fail") Right $ do-			g <- matchRx "^module\\s+([\\w\\.]+)" cts-			g 1--		parseDecl' :: Int -> String -> Maybe (H.Decl H.SrcSpanInfo)-		parseDecl' offset cts = maybeResult $ fmap (transformBi $ addOffset offset) $-			H.parseDeclWithMode (parseMode file exts) cts--		parseImport' :: Int -> String -> Maybe (H.ImportDecl H.SrcSpanInfo)-		parseImport' offset cts = maybeResult $ fmap (transformBi $ addOffset offset) $-			H.parseImportDeclWithMode (parseMode file exts) cts--		parseModuleHead' :: String -> Maybe (H.PragmasAndModuleHead H.SrcSpanInfo)-		parseModuleHead' = maybeResult . fmap H.unNonGreedy . H.parseWithMode (parseMode file exts)--		maybeResult :: H.ParseResult a -> Maybe a-		maybeResult (H.ParseFailed _ _) = Nothing-		maybeResult (H.ParseOk r) = Just r--		addOffset :: Int -> H.SrcSpan -> H.SrcSpan-		addOffset offset src = src { H.srcSpanStartLine = H.srcSpanStartLine src + offset, H.srcSpanEndLine = H.srcSpanEndLine src + offset }--		-- Replace all tabs to spaces to make SrcLoc valid, otherwise it treats tab as 8 spaces-		source' = map untab source-		untab '\t' = ' '-		untab ch = ch--parseMode :: Maybe FilePath -> [String] -> H.ParseMode-parseMode file exts = H.defaultParseMode {-	H.parseFilename = fromMaybe (H.parseFilename H.defaultParseMode) file,-	H.baseLanguage = H.Haskell2010,-	H.extensions = H.glasgowExts ++ map H.parseExtension exts,-	H.fixities = Just H.baseFixities }---- | Get exports-getExports :: H.ExportSpec H.SrcSpanInfo -> [Export]-getExports (H.EModuleContents _ (H.ModuleName _ m)) = [ExportModule $ fromString m]-getExports (H.EVar _ n) = [uncurry ExportName (identOfQName n) ThingNothing]-getExports (H.EAbs _ _ n) = [uncurry ExportName (identOfQName n) ThingNothing]-getExports (H.EThingWith _ _ n ns) = [uncurry ExportName (identOfQName n) $ ThingWith (map toStr ns)] where-	toStr :: H.CName H.SrcSpanInfo -> Text-	toStr (H.VarName _ cn) = identOfName cn-	toStr (H.ConName _ cn) = identOfName cn---- | Get import-getImport :: H.ImportDecl H.SrcSpanInfo -> Import-getImport d = Import-	(mname (H.importModule d))-	(H.importQualified d)-	(mname <$> H.importAs d)-	(importLst <$> H.importSpecs d)-	(Just $ toPosition $ H.ann d)-	where-		mname (H.ModuleName _ n) = fromString n-		importLst (H.ImportSpecList _ hiding specs) = ImportList hiding $ map impSpec specs-		impSpec (H.IVar _ n) = ImportSpec (identOfName n) ThingNothing-		impSpec (H.IAbs _ _ n) = ImportSpec (identOfName n) ThingNothing-		impSpec (H.IThingAll _ n) = ImportSpec (identOfName n) ThingAll-		impSpec (H.IThingWith _ n ns) = ImportSpec (identOfName n) $ ThingWith $ map identOfName (concatMap childrenBi ns :: [H.Name H.SrcSpanInfo])---- | Decl declarations-getDecls :: [H.Decl H.SrcSpanInfo] -> [Declaration]-getDecls decls =-	map mergeDecls .-	groupBy ((==) `on` view declarationName) .-	sortBy (comparing (view declarationName)) $-	concatMap getDecl decls ++ concatMap getDef decls-	where-		mergeDecls :: [Declaration] -> Declaration-		mergeDecls [] = error "Impossible"-		mergeDecls ds = Declaration-			(view declarationName $ head ds)-			Nothing-			Nothing-			(msum $ map (view declarationDocs) ds)-			(minimum <$> mapM (view declarationPosition) ds)-			(foldr1 mergeInfos $ map (view declaration) ds)--		mergeInfos :: DeclarationInfo -> DeclarationInfo -> DeclarationInfo-		mergeInfos (Function ln ld lr) (Function rn rd rr) = Function (ln `mplus` rn) (ld ++ rd) (lr `mplus` rr)-		mergeInfos l _ = l---- | Get local binds-getLocalDecls :: H.Decl H.SrcSpanInfo -> [Declaration]-getLocalDecls decl' = concatMap getDecls' binds' where-	binds' :: [H.Binds H.SrcSpanInfo]-	binds' = universeBi decl'-	getDecls' :: H.Binds H.SrcSpanInfo -> [Declaration]-	getDecls' (H.BDecls _ decls) = getDecls decls-	getDecls' _ = []---- | Get declaration and child declarations-getDecl :: H.Decl H.SrcSpanInfo -> [Declaration]-getDecl decl' = case decl' of-	H.TypeSig loc names typeSignature -> [mkFun loc n (Function (Just $ oneLinePrint typeSignature) [] Nothing) | n <- names]-	H.TypeDecl loc h _ -> [mkType loc (tyName h) Type (tyArgs h) `withDef` decl']-	H.DataDecl loc dataOrNew mctx h cons _ -> (mkType loc (tyName h) (ctor dataOrNew `withCtx` mctx) (tyArgs h) `withDef` decl') : concatMap (map (addRel $ tyName h) . getConDecl (tyName h) (tyArgs h)) cons-	H.GDataDecl loc dataOrNew mctx h _ gcons _ -> (mkType loc (tyName h) (ctor dataOrNew `withCtx` mctx) (tyArgs h) `withDef` decl') : concatMap (map (addRel $ tyName h) . getGConDecl) gcons-	H.ClassDecl loc ctx h _ _ -> [mkType loc (tyName h) (Class `withCtx` ctx) (tyArgs h) `withDef` decl']-	_ -> []-	where-		mkType :: H.SrcSpanInfo -> H.Name H.SrcSpanInfo -> (TypeInfo -> DeclarationInfo) -> [H.TyVarBind H.SrcSpanInfo] -> Declaration-		mkType loc n ctor' args = setPosition loc $ decl (identOfName n) $ ctor' $ TypeInfo Nothing (map oneLinePrint args) Nothing []--		withDef :: H.Pretty a => Declaration -> a -> Declaration-		withDef tyDecl' tyDef = set (declaration . typeInfo . typeInfoDefinition) (Just $ prettyPrint tyDef) tyDecl'--		withCtx :: (TypeInfo -> DeclarationInfo) -> Maybe (H.Context H.SrcSpanInfo) -> TypeInfo -> DeclarationInfo-		withCtx ctor' mctx = ctor' . set typeInfoContext (fmap makeCtx mctx)--		ctor :: H.DataOrNew H.SrcSpanInfo -> TypeInfo -> DeclarationInfo-		ctor (H.DataType _) = Data-		ctor (H.NewType _) = NewType--		makeCtx ctx = fromString $ oneLinePrint ctx--		addRel :: H.Name H.SrcSpanInfo -> Declaration -> Declaration-		addRel n = set (declaration . related) (Just $ identOfName n)--		tyName :: H.DeclHead H.SrcSpanInfo -> H.Name H.SrcSpanInfo-		tyName (H.DHead _ n) = n-		tyName (H.DHInfix _ _ n) = n-		tyName (H.DHParen _ h) = tyName h-		tyName (H.DHApp _ h _) = tyName h--		tyArgs :: H.DeclHead H.SrcSpanInfo -> [H.TyVarBind H.SrcSpanInfo]-		tyArgs = universeBi---- | Get constructor and record fields declarations-getConDecl :: H.Name H.SrcSpanInfo -> [H.TyVarBind H.SrcSpanInfo] -> H.QualConDecl H.SrcSpanInfo -> [Declaration]-getConDecl t as (H.QualConDecl loc _ _ cdecl) = case cdecl of-	H.ConDecl _ n cts -> [mkFun loc n (Function (Just $ oneLinePrint $ cts `tyFun` dataRes) [] Nothing)]-	H.InfixConDecl _ ct n cts -> [mkFun loc n (Function (Just $ oneLinePrint $ (ct : [cts]) `tyFun` dataRes) [] Nothing)]-	H.RecDecl _ n fields -> mkFun loc n (Function (Just $ oneLinePrint $ map tyField fields `tyFun` dataRes) [] Nothing) : concatMap (getRec loc dataRes) fields-	where-		dataRes :: H.Type H.SrcSpanInfo-		dataRes = foldr (H.TyApp loc . H.TyVar loc . nameOf) (H.TyCon loc (H.UnQual loc t)) as where-			nameOf :: H.TyVarBind H.SrcSpanInfo -> H.Name H.SrcSpanInfo-			nameOf (H.KindedVar _ n' _) = n'-			nameOf (H.UnkindedVar _ n') = n'--tyField :: H.FieldDecl H.SrcSpanInfo -> H.Type H.SrcSpanInfo-tyField (H.FieldDecl _ _ t) = t---- | Get GADT constructor and record fields declarations-getGConDecl :: H.GadtDecl H.SrcSpanInfo -> [Declaration]-getGConDecl (H.GadtDecl loc n mfields r) = mkFun loc n (Function (Just $ oneLinePrint $ map tyField (fromMaybe [] mfields) `tyFun` r) [] Nothing) : concatMap (getRec loc r) (fromMaybe [] mfields)---- | Get record field declaration-getRec :: H.SrcSpanInfo -> H.Type H.SrcSpanInfo -> H.FieldDecl H.SrcSpanInfo -> [Declaration]-getRec loc t (H.FieldDecl _ ns rt) = [mkFun loc n (Function (Just $ oneLinePrint $ H.TyFun loc t rt) [] Nothing) | n <- ns]---- | Get definitions-getDef :: H.Decl H.SrcSpanInfo -> [Declaration]-getDef (H.FunBind _ []) = []-getDef d@(H.FunBind _ (m : _)) = [setPosition (H.ann m) $ decl (identOfName $ identOfMatch m) fun] where-	identOfMatch (H.Match _ n _ _ _) = n-	identOfMatch (H.InfixMatch _ _ n _ _ _) = n-	fun = Function Nothing (getLocalDecls d) Nothing-getDef d@(H.PatBind loc pat _ _) = map (\name -> setPosition loc (decl (identOfName name) (Function Nothing (getLocalDecls d) Nothing))) (names pat) where-	names :: H.Pat H.SrcSpanInfo -> [H.Name H.SrcSpanInfo]-	names (H.PVar _ n) = [n]-	names (H.PNPlusK _ n _) = [n]-	names (H.PInfixApp _ l _ r) = names l ++ names r-	names (H.PApp _ _ ns) = concatMap names ns-	names (H.PTuple _ _ ns) = concatMap names ns-	names (H.PList _ ns) = concatMap names ns-	names (H.PParen _ n) = names n-	names (H.PRec _ _ pf) = concatMap fieldNames pf-	names (H.PAsPat _ n ns) = n : names ns-	names (H.PWildCard _) = []-	names (H.PIrrPat _ n) = names n-	names (H.PatTypeSig _ n _) = names n-	names (H.PViewPat _ _ n) = names n-	names (H.PBangPat _ n) = names n-	names _ = []--	fieldNames :: H.PatField H.SrcSpanInfo -> [H.Name H.SrcSpanInfo]-	fieldNames (H.PFieldPat _ _ n) = names n-	fieldNames (H.PFieldPun _ n) = case n of-		H.Qual _ _ n' -> [n']-		H.UnQual _ n' -> [n']-		_ -> []-	fieldNames (H.PFieldWildcard _) = []-getDef _ = []---- | Make function declaration by location, name and function type-mkFun :: H.SrcSpanInfo -> H.Name H.SrcSpanInfo -> DeclarationInfo -> Declaration-mkFun loc n = setPosition loc . decl (identOfName n)---- | Make function from arguments and result------ @[a, b, c...] `tyFun` r == a `TyFun` b `TyFun` c ... `TyFun` r@-tyFun :: [H.Type H.SrcSpanInfo] -> H.Type H.SrcSpanInfo -> H.Type H.SrcSpanInfo-tyFun as' r' = foldr (H.TyFun H.noSrcSpan) r' as'---- | Get name of qualified name-identOfQName :: H.QName H.SrcSpanInfo -> (Maybe Text, Text)-identOfQName (H.Qual _ (H.ModuleName _ mname) name) = (Just $ fromString mname, identOfName name)-identOfQName (H.UnQual _ name) = (Nothing, identOfName name)-identOfQName (H.Special _ sname) = (Nothing, fromString $ H.prettyPrint sname)---- | Get name of @H.Name@-identOfName :: H.Name H.SrcSpanInfo -> Text-identOfName name = fromString $ case name of-	H.Ident _ s -> s-	H.Symbol _ s -> s---- | Print something in one line-oneLinePrint :: (H.Pretty a, IsString s) => a -> s-oneLinePrint = fromString . H.prettyPrintStyleMode (H.style { H.mode = H.OneLineMode }) H.defaultMode---- | Print something-prettyPrint :: (H.Pretty a, IsString s) => a -> s-prettyPrint = fromString . H.prettyPrintStyleMode (H.style { H.mode = H.PageMode }) mode' where-	mode' = H.PPHsMode {-		H.classIndent = 4,-		H.doIndent = 4,-		H.multiIfIndent = 4,-		H.caseIndent = 4,-		H.letIndent = 4,-		H.whereIndent = 4,-		H.onsideIndent = 2,-		H.spacing = False,-		H.layout = H.PPOffsideRule,-		H.linePragmas = False }---- | Convert @H.SrcSpanInfo@ to @Position-toPosition :: H.SrcSpanInfo -> Position-toPosition s = Position (H.startLine s) (H.startColumn s)---- | Set @Declaration@ position-setPosition :: H.SrcSpanInfo -> Declaration -> Declaration-setPosition loc = set declarationPosition (Just $ toPosition loc)---- | Adds documentation to declaration-addDoc :: Map String String -> Declaration -> Declaration-addDoc docsMap decl' = set declarationDocs (preview (ix (view declarationName decl')) docsMap') decl' where-	docsMap' = M.mapKeys fromString . M.map fromString $ docsMap---- | Adds documentation to all declarations in module-addDocs :: Map String String -> Module -> Module-addDocs docsMap = over moduleDeclarations (map $ addDoc docsMap)---- | Extract files docs and set them to declarations-inspectDocsChunk :: [String] -> [Module] -> IO [Module]-inspectDocsChunk opts ms = hsdevLiftIOWith (ToolError "hdocs") $ do-	docsMaps <- hdocsy (map (view moduleLocation) ms) opts-	return $ zipWith addDocs docsMaps ms---- | Extract file docs and set them to module declarations-inspectDocs :: [String] -> Module -> IO Module-inspectDocs opts m = do-	let-		hdocsWorkaround = False-	docsMap <- if hdocsWorkaround-		then hdocsProcess (fromMaybe (T.unpack $ view moduleName m) (preview (moduleLocation . moduleFile) m)) opts-		else liftM Just $ hdocs (view moduleLocation m) opts-	return $ maybe id addDocs docsMap m---- | Like @inspectDocs@, but in @Ghc@ monad-inspectDocsGhc :: [String] -> Module -> Ghc Module-inspectDocsGhc opts m = case view moduleLocation m of-	FileModule fpath _ -> do-		docsMap <- liftM (fmap (formatDocs . snd) . listToMaybe) $ hsdevLift $ readSourcesGhc opts [fpath]-		return $ maybe id addDocs docsMap m-	_ -> hsdevError $ ModuleNotSource (view moduleLocation m)---- | Inspect contents-inspectContents :: String -> [(String, String)] -> [String] -> String -> ExceptT String IO InspectedModule-inspectContents name defines opts cts = inspect (ModuleSource $ Just name) (contentsInspection cts opts) $ do-	cts' <- lift $ preprocess_ defines exts name cts-	analyzed <- ExceptT $ return $ analyzeModule exts (Just name) cts' <|> analyzeModule_ exts (Just name) cts'-	return $ set moduleLocation (ModuleSource $ Just name) analyzed-	where-		exts = mapMaybe flagExtension opts--contentsInspection :: String -> [String] -> ExceptT String IO Inspection-contentsInspection _ _ = return InspectionNone -- crc or smth---- | Inspect file-inspectFile :: [(String, String)] -> [String] -> FilePath -> Maybe String -> IO InspectedModule-inspectFile defines opts file mcts = hsdevLiftIO $ do-	proj <- locateProject file-	absFilename <- Dir.canonicalizePath file-	ex <- Dir.doesFileExist absFilename-	unless ex $ hsdevError $ FileNotFound absFilename-	inspect (FileModule absFilename proj) ((if isJust mcts then fileContentsInspection else fileInspection) absFilename opts) $ do-		-- docsMap <- liftE $ if hdocsWorkaround-		-- 	then hdocsProcess absFilename opts-		-- 	else liftM Just $ hdocs (FileModule absFilename Nothing) opts-		forced <- hsdevLiftWith InspectError $ ExceptT $ E.handle onError $ do-			analyzed <- liftM (\s -> analyzeModule exts (Just absFilename) s <|> analyzeModule_ exts (Just absFilename) s) $-				maybe (readFileUtf8 absFilename >>= preprocess_ defines exts file) return mcts-			force analyzed `deepseq` return analyzed-		-- return $ setLoc absFilename proj . maybe id addDocs docsMap $ forced-		return $ set moduleLocation (FileModule absFilename proj) forced-	where-		onError :: E.ErrorCall -> IO (Either String Module)-		onError = return . Left . show--		exts = mapMaybe flagExtension opts---- | File inspection data-fileInspection :: FilePath -> [String] -> IO Inspection-fileInspection f opts = do-	tm <- Dir.getModificationTime f-	return $ InspectionAt (utcTimeToPOSIXSeconds tm) $ sort $ ordNub opts---- | File contents inspection data-fileContentsInspection :: FilePath -> [String] -> IO Inspection-fileContentsInspection _ opts = do-	tm <- getPOSIXTime-	return $ InspectionAt tm $ sort $ ordNub opts---- | Enumerate project dirs-projectDirs :: Project -> IO [Extensions FilePath]-projectDirs p = do-	p' <- loadProject p-	return $ ordNub $ map (fmap (normalise . (view projectPath p' </>))) $ maybe [] sourceDirs $ view projectDescription p'---- | Enumerate project source files-projectSources :: Project -> IO [Extensions FilePath]-projectSources p = do-	dirs <- projectDirs p-	let-		enumCabals = liftM (map takeDirectory . filter cabalFile) . traverseDirectory-		dirs' = map (view entity) dirs-	-- enum inner projects and dont consider them as part of this project-	subProjs <- liftM (delete (view projectPath p) . ordNub . concat) $ triesMap (enumCabals) dirs'-	let-		enumHs = liftM (filter thisProjectSource) . traverseDirectory-		thisProjectSource h = haskellSource h && not (any (`isParent` h) subProjs)-	liftM (ordNub . concat) $ triesMap (liftM sequenceA . traverse (enumHs)) dirs---- | Inspect project-inspectProject :: [(String, String)] -> [String] -> Project -> IO (Project, [InspectedModule])-inspectProject defines opts p = hsdevLiftIO $ do-	p' <- loadProject p-	srcs <- projectSources p'-	modules <- mapM inspectFile' srcs-	return (p', catMaybes modules)-	where-		inspectFile' exts = liftM return (inspectFile defines (opts ++ extensionsOpts exts) (view entity exts) Nothing) <|> return Nothing---- | Get actual defines-getDefines :: IO [(String, String)]-getDefines = E.handle onIO $ do-	tmp <- Dir.getTemporaryDirectory-	writeFile (tmp </> "defines.hs") ""-	_ <- runWait "ghc" ["-E", "-optP-dM", "-cpp", tmp </> "defines.hs"] ""-	cts <- readFileUtf8 (tmp </> "defines.hspp")-	Dir.removeFile (tmp </> "defines.hs")-	Dir.removeFile (tmp </> "defines.hspp")-	return $ mapMaybe (\g -> (,) <$> g 1 <*> g 2) $ mapMaybe (matchRx rx) $ lines cts-	where-		rx = "#define ([^\\s]+) (.*)"-		onIO :: E.IOException -> IO [(String, String)]-		onIO _ = return []--preprocess :: [(String, String)] -> FilePath -> String -> IO String-preprocess defines fpath cts = do-	cts' <- E.catch (Cpphs.cppIfdef fpath defines [] Cpphs.defaultBoolOptions cts) onIOError-	return $ unlines $ map snd cts'-	where-		onIOError :: E.IOException -> IO [(Cpphs.Posn, String)]-		onIOError _ = return []--preprocess_ :: [(String, String)] -> [String] -> FilePath -> String -> IO String-preprocess_ defines exts fpath cts-	| hasCPP = preprocess defines fpath cts-	| otherwise = return cts-	where-		exts' = map H.parseExtension exts ++ maybe [] snd (H.readExtensions cts)-		hasCPP = H.EnableExtension H.CPP `elem` exts'+{-# LANGUAGE CPP, TypeSynonymInstances, ImplicitParams, TemplateHaskell #-}
+
+module HsDev.Inspect (
+	Preloaded(..), preloadedId, preloadedMode, preloadedModule, asModule, preloadedTime, preloaded, preload,
+	AnalyzeEnv(..), analyzeEnv, analyzeFixities, analyzeRefine, moduleAnalyzeEnv,
+	analyzeResolve, analyzePreloaded,
+	inspectDocs, inspectDocsGhc,
+	inspectContents, contentsInspection,
+	inspectFile, sourceInspection, fileInspection, fileContentsInspection, fileContentsInspection_, installedInspection, moduleInspection,
+	projectDirs, projectSources,
+	getDefines,
+	preprocess, preprocess_,
+
+	module Control.Monad.Except
+	) where
+
+import Control.DeepSeq
+import qualified Control.Exception as E
+import Control.Lens
+import Control.Monad
+import Control.Monad.State
+import Control.Monad.Except
+import Data.Data (Data)
+import Data.Function (on)
+import Data.List
+import Data.Map.Strict (Map)
+import Data.Maybe (fromMaybe, mapMaybe)
+import Data.Ord (comparing)
+import Data.String (IsString, fromString)
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, getPOSIXTime, POSIXTime)
+import qualified Data.Map.Strict as M
+import qualified Language.Haskell.Exts as H
+import Language.Haskell.Exts.Fixity
+import qualified Language.Haskell.Names as N
+import qualified Language.Haskell.Names.Annotated as N
+import qualified Language.Haskell.Names.SyntaxUtils as N
+import qualified Language.Haskell.Names.Exports as N
+import qualified Language.Haskell.Names.Imports as N
+import qualified Language.Haskell.Names.ModuleSymbols as N
+import qualified Language.Haskell.Names.Open as N
+import qualified Language.Preprocessor.Cpphs as Cpphs
+import qualified System.Directory as Dir
+import System.FilePath
+import Text.Format
+import Data.Generics.Uniplate.Data
+
+import HsDev.Display ()
+import HsDev.Error
+import HsDev.Symbols
+import HsDev.Symbols.Name (fromModuleName_)
+import HsDev.Symbols.Resolve (refineSymbol, refineTable, RefineTable, symbolUniqId)
+import HsDev.Symbols.Parsed hiding (file)
+import qualified HsDev.Symbols.HaskellNames as HN
+import HsDev.Tools.Base
+import HsDev.Tools.Ghc.Worker (GhcM)
+import HsDev.Tools.HDocs (hdocs, hdocsProcess, readModuleDocs)
+import HsDev.Util
+import System.Directory.Paths
+
+-- | Preloaded module with contents and extensions
+data Preloaded = Preloaded {
+	_preloadedId :: ModuleId,
+	_preloadedMode :: H.ParseMode,
+	_preloadedModule :: H.Module H.SrcSpanInfo,
+	-- ^ Loaded module head without declarations
+	_preloadedTime :: Inspection,
+	_preloaded :: Text }
+
+instance NFData Preloaded where
+	rnf (Preloaded mid _ _ insp cts) = rnf mid `seq` rnf insp `seq` rnf cts
+
+asModule :: Lens' Preloaded Module
+asModule = lens g' s' where
+	g' p = Module {
+		_moduleId = _preloadedId p,
+		_moduleDocs = Nothing,
+		_moduleImports = map (fromModuleName_ . void . H.importModule) idecls,
+		_moduleExports = mempty,
+		_moduleFixities = mempty,
+		_moduleScope = mempty,
+		_moduleSource = Just $ fmap (N.Scoped N.None) $ _preloadedModule p }
+		where
+			H.Module _ _ _ idecls _ = _preloadedModule p
+	s' p m = p {
+		_preloadedId = _moduleId m,
+		_preloadedModule = maybe (_preloadedModule p) dropScope (_moduleSource m) }
+
+-- | Preload module - load head and imports to get actual extensions and dependencies
+preload :: Text -> [(String, String)] -> [String] -> ModuleLocation -> Maybe Text -> IO Preloaded
+preload name defines opts mloc@(FileModule fpath mproj) Nothing = do
+	cts <- readFileUtf8 (view path fpath)
+	insp <- fileInspection fpath opts
+	let
+		srcExts = fromMaybe (takeDir fpath `withExtensions` mempty) $ do
+			proj <- mproj
+			findSourceDir proj fpath
+	p' <- preload name defines (opts ++ extensionsOpts srcExts) mloc (Just cts)
+	return $ p' { _preloadedTime = insp }
+preload _ _ _ mloc Nothing = hsdevError $ InspectError $
+	format "preload called non-sourced module: {}" ~~ mloc
+preload name defines opts mloc (Just cts) = do
+	cts' <- preprocess_ defines exts fpath $ T.map untab cts
+	pragmas <- parseOk $ H.getTopPragmas (T.unpack cts')
+	let
+		fileExts = [H.parseExtension (T.unpack $ fromName_ $ void lang) | H.LanguagePragma _ langs <- pragmas, lang <- langs]
+		pmode = H.ParseMode {
+			H.parseFilename = view path fpath,
+			H.baseLanguage = H.Haskell2010,
+			H.extensions = ordNub (map H.parseExtension exts ++ fileExts),
+			H.ignoreLanguagePragmas = False,
+			H.ignoreLinePragmas = True,
+			H.fixities = Nothing,
+			H.ignoreFunctionArity = False }
+	H.ModuleHeadAndImports l mpragmas mhead mimps <- parseOk $ fmap H.unNonGreedy $ H.parseWithMode pmode (T.unpack cts')
+	when (H.isNullSpan $ H.srcInfoSpan l) $ hsdevError $ InspectError
+		(format "Error parsing module head and imports, file {}" ~~ view path fpath)
+	mname <- case mhead of
+		Just (H.ModuleHead _ (H.ModuleName _ nm) _ _) -> return $ fromString nm
+		_ -> hsdevError $ InspectError $ (format "Parsing module head and imports results in empty module name, file {}" ~~ view path fpath)
+	insp <- fileContentsInspection opts
+	return $ Preloaded {
+		_preloadedId = ModuleId mname mloc,
+		_preloadedMode = pmode,
+		_preloadedModule = H.Module l mhead mpragmas mimps [],
+		_preloadedTime = insp,
+		_preloaded = cts' }
+	where
+		fpath = fromMaybe name (mloc ^? moduleFile)
+		parseOk :: H.ParseResult a -> IO a
+		parseOk (H.ParseOk v) = return v
+		parseOk (H.ParseFailed loc err) = hsdevError $ InspectError $
+			format "Parse {} failed at {} with: {}" ~~ fpath ~~ show loc ~~ err
+		untab '\t' = ' '
+		untab ch = ch
+		exts = mapMaybe flagExtension opts
+
+data AnalyzeEnv = AnalyzeEnv {
+	_analyzeEnv :: N.Environment,
+	_analyzeFixities :: M.Map Name H.Fixity,
+	_analyzeRefine :: RefineTable }
+
+instance Monoid AnalyzeEnv where
+	mempty = AnalyzeEnv mempty mempty mempty
+	AnalyzeEnv lenv lf lt `mappend` AnalyzeEnv renv rf rt = AnalyzeEnv
+		(mappend lenv renv)
+		(mappend lf rf)
+		(mappend lt rt)
+
+moduleAnalyzeEnv :: Module -> AnalyzeEnv
+moduleAnalyzeEnv m = AnalyzeEnv
+	(environment m)
+	(m ^. fixitiesMap)
+	(refineTable (m ^.. exportedSymbols))
+
+-- | Resolve module imports/exports/scope
+analyzeResolve :: AnalyzeEnv -> Module -> Module
+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',
+		_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],
+		_moduleScope = M.map (map HN.fromSymbol) tbl,
+		_moduleSource = Just annotated }
+		where
+			getOpName (H.VarOp _ nm) = nm
+			getOpName (H.ConOp _ nm) = nm
+			fixName o = H.Qual () (H.ModuleName () (T.unpack $ m ^. moduleId . moduleName)) (void o)
+			itbl = N.importTable env msrc
+			tbl = N.moduleTable itbl msrc
+			syms = set (each . symbolId . symbolModule) (m ^. moduleId) $
+				getSymbols decls'
+			stbl = refineTable syms `mappend` rtable
+			-- Not using 'annotate' because we already computed needed tables
+			annotated = H.Module l mhead' mpragmas idecls' decls'
+			H.Module l mhead mpragmas idecls decls = fmap (\(N.Scoped _ v) -> N.Scoped N.None v) msrc
+			mhead' = fmap scopeHead mhead
+			scopeHead (H.ModuleHead lh mname mwarns mexports) = H.ModuleHead lh mname mwarns $
+				fmap (N.annotateExportSpecList tbl . dropScope) mexports
+			idecls' = N.annotateImportDecls mn env (fmap dropScope idecls)
+			decls' = map (N.annotateDecl (N.initialScope (N.dropAnn mn) tbl) . dropScope) decls
+			mn = dropScope $ N.getModuleName msrc
+
+-- | Inspect preloaded module
+analyzePreloaded :: AnalyzeEnv -> Preloaded -> Either String Module
+analyzePreloaded aenv@(AnalyzeEnv env gfixities _) p = case H.parseFileContentsWithMode (_preloadedMode p') (T.unpack $ _preloaded p') of
+	H.ParseFailed loc reason -> Left $ "Parse failed at " ++ show loc ++ ": " ++ reason
+	H.ParseOk m -> Right $ analyzeResolve aenv $ Module {
+		_moduleId = _preloadedId p',
+		_moduleDocs = Nothing,
+		_moduleImports = mempty,
+		_moduleExports = mempty,
+		_moduleFixities = mempty,
+		_moduleScope = mempty,
+		_moduleSource = Just $ fmap (N.Scoped N.None) m }
+	where
+		qimps = M.keys $ N.importTable env (_preloadedModule p)
+		p' = p { _preloadedMode = (_preloadedMode p) { H.fixities = Just (mapMaybe (`M.lookup` gfixities) qimps) } }
+
+-- | Get top symbols
+getSymbols :: [H.Decl Ann] -> [Symbol]
+getSymbols decls =
+	map mergeSymbols .
+	groupBy ((==) `on` symbolUniqId) .
+	sortBy (comparing symbolUniqId) $
+	concatMap getDecl decls
+	where
+		mergeSymbols :: [Symbol] -> Symbol
+		mergeSymbols [] = error "impossible"
+		mergeSymbols [s] = s
+		mergeSymbols ss@(s:_) = Symbol
+			(view symbolId s)
+			(msum $ map (view symbolDocs) ss)
+			(msum $ map (view symbolPosition) ss)
+			(foldr1 mergeInfo $ map (view symbolInfo) ss)
+
+		mergeInfo :: SymbolInfo -> SymbolInfo -> SymbolInfo
+		mergeInfo (Function lt) (Function rt) = Function $ lt `mplus` rt
+		mergeInfo (PatConstructor las lt) (PatConstructor ras rt) = PatConstructor (if null las then ras else las) (lt `mplus` rt)
+		mergeInfo (Selector lt lp lc) (Selector rt rp rc)
+			| lt == rt && lp == rp = Selector lt lp (nub $ lc ++ rc)
+			| otherwise = Selector lt lp lc
+		mergeInfo l _ = l
+
+
+-- | Get symbols from declarations
+getDecl :: H.Decl Ann -> [Symbol]
+getDecl decl' = case decl' of
+	H.TypeDecl _ h _ -> [mkSymbol (tyName h) (Type (tyArgs h) [])]
+	H.TypeFamDecl _ h _ _ -> [mkSymbol (tyName h) (TypeFam (tyArgs h) [] Nothing)]
+	H.ClosedTypeFamDecl _ h _ _ _ -> [mkSymbol (tyName h) (TypeFam (tyArgs h) [] Nothing)]
+	H.DataDecl _ dt mctx h dcons _ -> mkSymbol nm ((getCtor dt) (tyArgs h) (getCtx mctx)) : concatMap (getConDecl nm) dcons where
+		nm = tyName h
+	H.GDataDecl _ dt mctx h _ gcons _ -> mkSymbol nm ((getCtor dt) (tyArgs h) (getCtx mctx)) : concatMap (getGConDecl nm) gcons where
+		nm = tyName h
+	H.DataFamDecl _ mctx h _ -> [mkSymbol (tyName h) (DataFam (tyArgs h) (getCtx mctx) Nothing)]
+	H.ClassDecl _ mctx h _ clsDecls -> mkSymbol nm (Class (tyArgs h) (getCtx mctx)) : concatMap (getClassDecl nm) (fromMaybe [] clsDecls) where
+		nm = tyName h
+	H.TypeSig _ ns tsig -> [mkSymbol n (Function (Just $ oneLinePrint tsig)) | n <- ns]
+	H.PatSynSig _ ns mas _ _ t -> [mkSymbol n (PatConstructor (maybe [] (map prp) mas) (Just $ oneLinePrint t)) | n <- ns'] where
+#if MIN_VERSION_haskell_src_exts(1,20,0)
+		ns' = ns
+#else
+		ns' = [ns]
+#endif
+	H.FunBind _ ms -> [mkSymbol (matchName m) (Function Nothing) | m <- ms] where
+		matchName (H.Match _ n _ _ _) = n
+		matchName (H.InfixMatch _ _ n _ _ _) = n
+	H.PatBind _ p _ _ -> [mkSymbol n (Function Nothing) | n <- patNames p] where
+		patNames :: H.Pat Ann -> [H.Name Ann]
+		patNames = childrenBi
+	H.PatSyn _ p _ _ -> case p of
+		H.PInfixApp _ _ qn _ -> [mkSymbol (qToName qn) (PatConstructor [] Nothing)]
+		H.PApp _ qn _ -> [mkSymbol (qToName qn) (PatConstructor [] Nothing)]
+		H.PRec _ qn fs -> mkSymbol (qToName qn) (PatConstructor [] Nothing) :
+			[mkSymbol (qToName n) (PatSelector Nothing Nothing (prp $ qToName qn)) | n <- (universeBi fs :: [H.QName Ann])]
+		_ -> []
+		where
+			qToName (H.Qual _ _ n) = n
+			qToName (H.UnQual _ n) = n
+			qToName _ = error "invalid qname"
+	_ -> []
+	where
+		tyName :: H.DeclHead Ann -> H.Name Ann
+		tyName = head . universeBi
+		tyArgs :: Data (ast Ann) => ast Ann -> [Text]
+		tyArgs n = map prp (universeBi n :: [H.TyVarBind Ann])
+		getCtx :: Maybe (H.Context Ann) -> [Text]
+		getCtx mctx = map prp (universeBi mctx :: [H.Asst Ann])
+		getCtor (H.DataType _) = Data
+		getCtor (H.NewType _) = NewType
+
+getConDecl :: H.Name Ann -> H.QualConDecl Ann -> [Symbol]
+getConDecl ptype (H.QualConDecl _ _ _ cdecl) = case cdecl of
+	H.ConDecl _ n ts -> [mkSymbol n (Constructor (map prp ts) (prp ptype))]
+	H.InfixConDecl _ lt n rt -> [mkSymbol n (Constructor (map prp [lt, rt]) (prp ptype))]
+	H.RecDecl _ n fs -> mkSymbol n (Constructor [prp t | H.FieldDecl _ _ t <- fs] (prp ptype)) :
+		[mkSymbol fn (Selector (Just $ prp ft) (prp ptype) [prp n]) | H.FieldDecl _ fns ft <- fs, fn <- fns]
+
+getGConDecl :: H.Name Ann -> H.GadtDecl Ann -> [Symbol]
+getGConDecl _ (H.GadtDecl _ n Nothing t) = [mkSymbol n (Constructor (map prp as) (prp res))] where
+	(as, res) = tyFunSplit t
+	tyFunSplit = go [] where
+		go as' (H.TyFun _ arg' res') = go (arg' : as') res'
+		go as' t' = (reverse as', t')
+getGConDecl ptype (H.GadtDecl _ n (Just fs) t) = mkSymbol n (Constructor [prp ft | H.FieldDecl _ _ ft <- fs] (prp t)) :
+	[mkSymbol fn (Selector (Just $ prp ft) (prp ptype) [prp n]) | H.FieldDecl _ fns ft <- fs, fn <- fns]
+
+getClassDecl :: H.Name Ann -> H.ClassDecl Ann -> [Symbol]
+getClassDecl pclass (H.ClsDecl _ (H.TypeSig _ ns tsig)) = [mkSymbol n (Method (Just $ oneLinePrint tsig) (prp pclass)) | n <- ns]
+getClassDecl _ _ = []
+
+prp :: H.Pretty a => a -> Text
+prp = fromString . H.prettyPrint
+
+
+mkSymbol :: H.Name Ann -> SymbolInfo -> Symbol
+mkSymbol nm = Symbol (SymbolId (fromName_ $ void nm) (ModuleId (fromString "") noLocation)) Nothing (nm ^? binders . defPos)
+
+
+-- | Print something in one line
+oneLinePrint :: (H.Pretty a, IsString s) => a -> s
+oneLinePrint = fromString . H.prettyPrintStyleMode (H.style { H.mode = H.OneLineMode }) H.defaultMode
+
+-- | Adds documentation to declaration
+addDoc :: Map String String -> Symbol -> Symbol
+addDoc docsMap sym' = set symbolDocs (preview (ix (view (symbolId . symbolName) sym')) docsMap') sym' where
+	docsMap' = M.mapKeys fromString . M.map fromString $ docsMap
+
+-- | Adds documentation to all declarations in module
+addDocs :: Map String String -> Module -> Module
+addDocs docsMap = over moduleSymbols (addDoc docsMap)
+
+-- | Extract file docs and set them to module declarations
+inspectDocs :: [String] -> Module -> GhcM Module
+inspectDocs opts m = do
+	let
+		hdocsWorkaround = False
+	docsMap <- if hdocsWorkaround
+		then liftIO $ hdocsProcess (fromMaybe (T.unpack $ view (moduleId . moduleName) m) (preview (moduleId . moduleLocation . moduleFile . path) m)) opts
+		else liftM Just $ hdocs (view (moduleId . moduleLocation) m) opts
+	return $ maybe id addDocs docsMap m
+
+-- | Like @inspectDocs@, but in @Ghc@ monad
+inspectDocsGhc :: [String] -> Module -> GhcM Module
+inspectDocsGhc opts m = do
+	docsMap <- readModuleDocs opts m
+	return $ maybe id addDocs docsMap m
+
+-- | Inspect contents
+inspectContents :: Text -> [(String, String)] -> [String] -> Text -> ExceptT String IO InspectedModule
+inspectContents name defines opts cts = inspect (OtherLocation name) (contentsInspection cts opts) $ do
+	p <- lift $ preload name defines opts (OtherLocation name) (Just cts)
+	analyzed <- ExceptT $ return $ analyzePreloaded mempty p
+	return $ set (moduleId . moduleLocation) (OtherLocation name) analyzed
+
+contentsInspection :: Text -> [String] -> ExceptT String IO Inspection
+contentsInspection _ _ = return InspectionNone -- crc or smth
+
+-- | Inspect file
+inspectFile :: [(String, String)] -> [String] -> Path -> Maybe Project -> Maybe Text -> IO InspectedModule
+inspectFile defines opts file mproj mcts = hsdevLiftIO $ do
+	absFilename <- canonicalize file
+	ex <- fileExists absFilename
+	unless ex $ hsdevError $ FileNotFound absFilename
+	inspect (FileModule absFilename mproj) (sourceInspection absFilename mcts opts) $ do
+		-- docsMap <- liftE $ if hdocsWorkaround
+		-- 	then hdocsProcess absFilename opts
+		-- 	else liftM Just $ hdocs (FileModule absFilename Nothing) opts
+		forced <- hsdevLiftWith InspectError $ ExceptT $ E.handle onError $ do
+			p <- preload absFilename defines opts (FileModule absFilename mproj) mcts
+			return $!! analyzePreloaded mempty p
+		-- return $ setLoc absFilename mproj . maybe id addDocs docsMap $ forced
+		return $ set (moduleId . moduleLocation) (FileModule absFilename mproj) forced
+	where
+		onError :: E.ErrorCall -> IO (Either String Module)
+		onError = return . Left . show
+
+-- | Source inspection data, differs whether there are contents provided
+sourceInspection :: Path -> Maybe Text -> [String] -> IO Inspection
+sourceInspection f Nothing = fileInspection f
+sourceInspection _ (Just _) = fileContentsInspection
+
+-- | File inspection data
+fileInspection :: Path -> [String] -> IO Inspection
+fileInspection f opts = do
+	tm <- Dir.getModificationTime (view path f)
+	return $ InspectionAt (utcTimeToPOSIXSeconds tm) $ map fromString $ sort $ ordNub opts
+
+-- | File contents inspection data
+fileContentsInspection :: [String] -> IO Inspection
+fileContentsInspection opts = fileContentsInspection_ opts <$> getPOSIXTime
+
+-- | File contents inspection data
+fileContentsInspection_ :: [String] -> POSIXTime -> Inspection
+fileContentsInspection_ opts tm = 	InspectionAt tm $ map fromString $ sort $ ordNub opts
+
+-- | Installed module inspection data, just opts
+installedInspection :: [String] -> IO Inspection
+installedInspection opts = return $ InspectionAt 0 $ map fromString $ sort $ ordNub opts
+
+-- | Inspection by module location
+moduleInspection :: ModuleLocation -> [String] -> IO Inspection
+moduleInspection (FileModule fpath _) = fileInspection fpath
+moduleInspection _ = installedInspection
+
+-- | Enumerate project dirs
+projectDirs :: Project -> IO [Extensions Path]
+projectDirs p = do
+	p' <- loadProject p
+	return $ ordNub $ map (fmap (normPath . (view projectPath p' `subPath`))) $ maybe [] sourceDirs $ view projectDescription p'
+
+-- | Enumerate project source files
+projectSources :: Project -> IO [Extensions Path]
+projectSources p = do
+	dirs <- projectDirs p
+	let
+		enumCabals = liftM (map takeDirectory . filter cabalFile) . traverseDirectory
+		dirs' = map (view (entity . path)) dirs
+	-- enum inner projects and dont consider them as part of this project
+	subProjs <- liftM (map fromFilePath . delete (view (projectPath . path) p) . ordNub . concat) $ triesMap (enumCabals) dirs'
+	let
+		enumHs = liftM (filter thisProjectSource) . traverseDirectory
+		thisProjectSource h = haskellSource h && not (any (`isParent` fromFilePath h) subProjs)
+	liftM (ordNub . concat) $ triesMap (liftM sequenceA . traverse (liftM (map fromFilePath) . enumHs . view path)) dirs
+
+-- | Get actual defines
+getDefines :: IO [(String, String)]
+getDefines = E.handle onIO $ do
+	tmp <- Dir.getTemporaryDirectory
+	writeFile (tmp </> "defines.hs") ""
+	_ <- runWait "ghc" ["-E", "-optP-dM", "-cpp", tmp </> "defines.hs"] ""
+	cts <- readFileUtf8 (tmp </> "defines.hspp")
+	Dir.removeFile (tmp </> "defines.hs")
+	Dir.removeFile (tmp </> "defines.hspp")
+	return $ mapMaybe (\g -> (,) <$> g 1 <*> g 2) $ mapMaybe (matchRx rx . T.unpack) $ T.lines cts
+	where
+		rx = "#define ([^\\s]+) (.*)"
+		onIO :: E.IOException -> IO [(String, String)]
+		onIO _ = return []
+
+preprocess :: [(String, String)] -> Path -> Text -> IO Text
+preprocess defines fpath cts = do
+	cts' <- E.catch (Cpphs.cppIfdef (view path fpath) defines [] cppOpts (T.unpack cts)) onIOError
+	return $ T.unlines $ map (fromString . snd) cts'
+	where
+		onIOError :: E.IOException -> IO [(Cpphs.Posn, String)]
+		onIOError _ = return []
+
+		cppOpts = Cpphs.defaultBoolOptions {
+			Cpphs.locations = False,
+			Cpphs.hashline = False
+		}
+
+preprocess_ :: [(String, String)] -> [String] -> Path -> Text -> IO Text
+preprocess_ defines exts fpath cts
+	| hasCPP = preprocess defines fpath cts
+	| otherwise = return cts
+	where
+		exts' = map H.parseExtension exts ++ maybe [] snd (H.readExtensions $ T.unpack cts)
+		hasCPP = H.EnableExtension H.CPP `elem` exts'
+
+dropScope :: Functor f => f (N.Scoped l) -> f l
+dropScope = fmap (\(N.Scoped _ a) -> a)
+
+makeLenses ''Preloaded
+makeLenses ''AnalyzeEnv
+ src/HsDev/Inspect/Order.hs view
@@ -0,0 +1,39 @@+module HsDev.Inspect.Order (
+	order
+	) where
+
+import Control.Lens
+import Data.List
+import Data.Maybe
+import Data.String
+import qualified Data.Set as S
+import qualified Language.Haskell.Exts as H
+
+import Data.Deps
+import HsDev.Inspect
+import HsDev.Symbols.Types
+import System.Directory.Paths
+
+-- | Order source files so that dependencies goes first and we are able to resolve symbols and set fixities
+order :: [Preloaded] -> Either (DepsError Path) [Preloaded]
+order ps = do
+	order' <- linearize pdeps
+	return $ mapMaybe byFile order'
+	where
+		pdeps = mconcat $ map getDeps ps
+		byFile f = find (\p -> (_preloadedId p ^? moduleLocation . moduleFile) == Just f) ps
+		files = S.fromList $ map _preloadedId ps ^.. each . moduleLocation . moduleFile
+		getDeps :: Preloaded -> Deps Path
+		getDeps p = deps mfile [ifile | ifile <- ifiles, S.member ifile files] where
+			H.Module _ _ _ idecls _ = _preloadedModule p
+			imods = [fromString iname | H.ModuleName _ iname <- map H.importModule idecls]
+			mfile = _preloadedId p ^?! moduleLocation . moduleFile
+			projRoot = _preloadedId p ^? moduleLocation . moduleProject . _Just . projectPath
+			mroot = fromMaybe
+				(sourceModuleRoot (view moduleName $ _preloadedId p) mfile)
+				projRoot
+			dirs = do
+				proj <- _preloadedId p ^.. moduleLocation . moduleProject . _Just
+				i <- fileTargets proj mfile
+				view infoSourceDirs i
+			ifiles = [normPath (joinPaths [mroot, dir, importPath imod]) | imod <- imods, dir <- if null dirs then [fromFilePath "."] else dirs]
src/HsDev/PackageDb.hs view
@@ -1,106 +1,63 @@-{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}--module HsDev.PackageDb (-	PackageDb(..), packageDb,-	PackageDbStack(..), packageDbStack, globalDb, userDb, fromPackageDb, fromPackageDbs,-	topPackageDb, packageDbs, packageDbStacks,-	isSubStack,--	packageDbOpt, packageDbStackOpts-	) where--import Control.Applicative-import Control.Monad (guard)-import Control.Lens (makeLenses, each)-import Control.DeepSeq (NFData(..))-import Data.Aeson-import Data.List (tails, isSuffixOf)--import System.Directory.Paths-import HsDev.Util ((.::))--data PackageDb = GlobalDb | UserDb | PackageDb { _packageDb :: FilePath } deriving (Eq, Ord, Read, Show)--makeLenses ''PackageDb--instance NFData PackageDb where-	rnf GlobalDb = ()-	rnf UserDb = ()-	rnf (PackageDb p) = rnf p--instance ToJSON PackageDb where-	toJSON GlobalDb = "global-db"-	toJSON UserDb = "user-db"-	toJSON (PackageDb p) = object ["package-db" .= p]--instance FromJSON PackageDb where-	parseJSON v = globalP v <|> userP v <|> dbP v where-		globalP = withText "global-db" (\s -> guard (s == "global-db") >> return GlobalDb)-		userP = withText "user-db" (\s -> guard (s == "user-db") >> return UserDb)-		dbP = withObject "package-db" pathP where-			pathP obj = PackageDb <$> obj .:: "package-db"--instance Paths PackageDb where-	paths _ GlobalDb = pure GlobalDb-	paths _ UserDb = pure UserDb-	paths f (PackageDb p) = PackageDb <$> f p---- | Stack of PackageDb in reverse order-newtype PackageDbStack = PackageDbStack { _packageDbStack :: [PackageDb] } deriving (Eq, Ord, Read, Show)--makeLenses ''PackageDbStack--instance NFData PackageDbStack where-	rnf (PackageDbStack ps) = rnf ps--instance ToJSON PackageDbStack where-	toJSON (PackageDbStack ps) = toJSON ps--instance FromJSON PackageDbStack where-	parseJSON = fmap PackageDbStack . parseJSON--instance Paths PackageDbStack where-	paths f (PackageDbStack ps) = PackageDbStack <$> (each . paths) f ps---- | Global db stack-globalDb :: PackageDbStack-globalDb = PackageDbStack []---- | User db stack-userDb :: PackageDbStack-userDb = PackageDbStack [UserDb]---- | Make package-db from one package-db-fromPackageDb :: FilePath -> PackageDbStack-fromPackageDb = PackageDbStack . return . PackageDb---- | Make package-db stack from paths-fromPackageDbs :: [FilePath] -> PackageDbStack-fromPackageDbs = PackageDbStack . map PackageDb . reverse---- | Get top package-db for package-db stack-topPackageDb :: PackageDbStack -> PackageDb-topPackageDb (PackageDbStack []) = GlobalDb-topPackageDb (PackageDbStack (d:_)) = d---- | Get list of package-db in stack, adds additional global-db at bottom-packageDbs :: PackageDbStack -> [PackageDb]-packageDbs = (GlobalDb :) . reverse . _packageDbStack---- | Get stacks for each package-db in stack-packageDbStacks :: PackageDbStack -> [PackageDbStack]-packageDbStacks = map PackageDbStack . tails . _packageDbStack---- | Is one package-db stack substack of another-isSubStack :: PackageDbStack -> PackageDbStack -> Bool-isSubStack (PackageDbStack l) (PackageDbStack r) = l `isSuffixOf` r---- | Get ghc options for package-db-packageDbOpt :: PackageDb -> String-packageDbOpt GlobalDb = "-global-package-db"-packageDbOpt UserDb = "-user-package-db"-packageDbOpt (PackageDb p) = "-package-db " ++ p---- | Get ghc options for package-db stack-packageDbStackOpts :: PackageDbStack -> [String]-packageDbStackOpts (PackageDbStack ps) = "-no-user-package-db" : map packageDbOpt (reverse ps)+{-# LANGUAGE OverloadedStrings #-}
+
+module HsDev.PackageDb (
+	module HsDev.PackageDb.Types,
+
+	packageDbPath, readPackageDb
+	) where
+
+import Control.Lens
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import Data.Maybe (listToMaybe)
+import Data.Text (pack, unpack)
+import Data.Traversable
+import Distribution.InstalledPackageInfo
+import Distribution.Package
+import Distribution.Text (disp)
+import System.FilePath
+
+import HsDev.PackageDb.Types
+import HsDev.Error
+import HsDev.Symbols.Location
+import HsDev.Tools.Base
+import HsDev.Util (directoryContents, readFileUtf8)
+import System.Directory.Paths
+
+-- | Get path to package-db
+packageDbPath :: PackageDb -> IO Path
+packageDbPath GlobalDb = do
+	out <- fmap lines $ runTool_ "ghc-pkg" ["list", "--global"]
+	case out of
+		(fpath:_) -> return $ fromFilePath $ normalise fpath
+		[] -> hsdevError $ ToolError "ghc-pkg" "empty output, expecting path to global package-db"
+packageDbPath UserDb = do
+	out <- fmap lines $ runTool_ "ghc-pkg" ["list", "--user"]
+	case out of
+		(fpath:_) -> return $ fromFilePath $ normalise fpath
+		[] -> hsdevError $ ToolError "ghc-pkg" "empty output, expecting path to user package db"
+packageDbPath (PackageDb fpath) = return fpath
+
+-- | Read package-db conf files
+readPackageDb :: PackageDb -> IO (Map ModulePackage [ModuleLocation])
+readPackageDb pdb = do
+	p <- packageDbPath pdb
+	mlibdir <- fmap (listToMaybe . lines) $ runTool_ "ghc" ["--print-libdir"]
+	confs <- fmap (filter isConf) $ directoryContents (p ^. path)
+	fmap M.unions $ forM confs $ \conf -> do
+		cts <- readFileUtf8 conf
+		case parseInstalledPackageInfo (unpack cts) of
+			ParseFailed _ -> return M.empty  -- FIXME: Should log as warning
+			ParseOk _ res -> return $ over (each . each . moduleInstallDirs . each) (subst mlibdir) $ listMods res
+	where
+		isConf f = takeExtension f == ".conf"
+		listMods pinfo = M.singleton pname pmods where
+			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)
+		subst Nothing f = f
+		subst (Just libdir) f = case splitPaths f of
+			("$topdir":rest) -> joinPaths (fromFilePath libdir : rest)
+			_ -> f
+ src/HsDev/PackageDb/Types.hs view
@@ -0,0 +1,113 @@+{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
+
+module HsDev.PackageDb.Types (
+	PackageDb(..), packageDb,
+	PackageDbStack(..), packageDbStack, globalDb, userDb, fromPackageDbs,
+	topPackageDb, packageDbs, packageDbStacks,
+	isSubStack,
+
+	packageDbOpt, packageDbStackOpts
+	) where
+
+import Control.Applicative
+import Control.Monad (guard)
+import Control.Lens (makeLenses, each, (^.))
+import Control.DeepSeq (NFData(..))
+import Data.Aeson
+import Data.List (tails, isSuffixOf)
+import qualified Data.Text as T
+import Data.String
+
+import System.Directory.Paths
+
+data PackageDb = GlobalDb | UserDb | PackageDb { _packageDb :: Path } deriving (Eq, Ord)
+
+makeLenses ''PackageDb
+
+instance NFData PackageDb where
+	rnf GlobalDb = ()
+	rnf UserDb = ()
+	rnf (PackageDb p) = rnf p
+
+instance Show PackageDb where
+	show GlobalDb = "global-db"
+	show UserDb = "user-db"
+	show (PackageDb p) = "package-db:" ++ p ^. path
+
+instance ToJSON PackageDb where
+	toJSON GlobalDb = "global-db"
+	toJSON UserDb = "user-db"
+	toJSON (PackageDb p) = fromString $ "package-db:" ++ p ^. path
+
+instance FromJSON PackageDb where
+	parseJSON v = globalP v <|> userP v <|> dbP v where
+		globalP = withText "global-db" (\s -> guard (s == "global-db") >> return GlobalDb)
+		userP = withText "user-db" (\s -> guard (s == "user-db") >> return UserDb)
+		dbP = withText "package-db" $ \s -> case T.stripPrefix "package-db:" s of
+			Nothing -> fail ("Can't parse package-db: " ++ T.unpack s)
+			Just p' -> return $ PackageDb p'
+
+instance Paths PackageDb where
+	paths _ GlobalDb = pure GlobalDb
+	paths _ UserDb = pure UserDb
+	paths f (PackageDb p) = PackageDb <$> paths f p
+
+-- | Stack of PackageDb in reverse order
+newtype PackageDbStack = PackageDbStack { _packageDbStack :: [PackageDb] } deriving (Eq, Ord, Show)
+
+makeLenses ''PackageDbStack
+
+instance NFData PackageDbStack where
+	rnf (PackageDbStack ps) = rnf ps
+
+instance ToJSON PackageDbStack where
+	toJSON (PackageDbStack ps) = toJSON ps
+
+instance FromJSON PackageDbStack where
+	parseJSON = fmap PackageDbStack . parseJSON
+
+instance Paths PackageDbStack where
+	paths f (PackageDbStack ps) = PackageDbStack <$> (each . paths) f ps
+
+-- | Global db stack
+globalDb :: PackageDbStack
+globalDb = PackageDbStack []
+
+-- | User db stack
+userDb :: PackageDbStack
+userDb = PackageDbStack [UserDb]
+
+-- | Make package-db stack from paths
+fromPackageDbs :: [Path] -> PackageDbStack
+fromPackageDbs = PackageDbStack . map PackageDb . reverse
+
+-- | Get top package-db for package-db stack
+topPackageDb :: PackageDbStack -> PackageDb
+topPackageDb (PackageDbStack []) = GlobalDb
+topPackageDb (PackageDbStack (d:_)) = d
+
+-- | Get list of package-db in stack, adds additional global-db at bottom
+packageDbs :: PackageDbStack -> [PackageDb]
+packageDbs = (GlobalDb :) . reverse . _packageDbStack
+
+-- | Get stacks for each package-db in stack
+packageDbStacks :: PackageDbStack -> [PackageDbStack]
+packageDbStacks = map PackageDbStack . tails . _packageDbStack
+
+-- | Is one package-db stack substack of another
+isSubStack :: PackageDbStack -> PackageDbStack -> Bool
+isSubStack (PackageDbStack l) (PackageDbStack r) = l `isSuffixOf` r
+
+-- | Get ghc options for package-db
+packageDbOpt :: PackageDb -> String
+packageDbOpt GlobalDb = "-global-package-db"
+packageDbOpt UserDb = "-user-package-db"
+packageDbOpt (PackageDb p) = "-package-db " ++ p ^. path
+
+-- | Get ghc options for package-db stack
+packageDbStackOpts :: PackageDbStack -> [String]
+packageDbStackOpts (PackageDbStack ps)
+	| "-user-package-db" `elem` opts' = opts'
+	| otherwise = "-no-user-package-db" : opts'
+	where
+		opts' = map packageDbOpt (reverse ps)
src/HsDev/Project.hs view
@@ -1,167 +1,197 @@-{-# LANGUAGE CPP #-}--module HsDev.Project (-	module HsDev.Project.Types,--	infoSourceDirsDef,-	analyzeCabal,-	readProject, loadProject,-	withExtensions,-	infos, inTarget, fileTarget, fileTargets, findSourceDir, sourceDirs,-	targetOpts,--	-- * Helpers-	showExtension, flagExtension, extensionFlag,-	extensionsOpts-	) where--import Control.Arrow-import Control.Lens (Simple, Lens, view, lens)-import Control.Monad.Except-import Data.List-import Data.Maybe-import Distribution.Compiler (CompilerFlavor(GHC))-import qualified Distribution.Package as P-import qualified Distribution.PackageDescription as PD-import Distribution.PackageDescription.Parse-import Distribution.ModuleName (components)-import Distribution.Text (display)-import Language.Haskell.Extension-import System.FilePath--import HsDev.Project.Compat-import HsDev.Project.Types-import HsDev.Error-import HsDev.Util---- | infoSourceDirs lens with default-infoSourceDirsDef :: Simple Lens Info [FilePath]-infoSourceDirsDef = lens get' set' where-	get' i = case _infoSourceDirs i of-		[] -> ["."]-		dirs -> dirs-	set' i ["."] = i { _infoSourceDirs = [] }-	set' i dirs = i { _infoSourceDirs = dirs }---- | Analyze cabal file-analyzeCabal :: String -> Either String ProjectDescription-analyzeCabal source = case liftM flattenDescr $ parsePackageDesc source of-	ParseOk _ r -> Right ProjectDescription {-		_projectVersion = showVer $ P.pkgVersion $ PD.package r,-		_projectLibrary = fmap toLibrary $ PD.library r,-		_projectExecutables = fmap toExecutable $ PD.executables r,-		_projectTests = fmap toTest $ PD.testSuites r }-	ParseFailed e -> Left $ "Parse failed: " ++ show e-	where-		toLibrary lib = Library (map components $ PD.exposedModules lib) (toInfo $ PD.libBuildInfo lib)-		toExecutable exe = Executable (componentName $ PD.exeName exe) (PD.modulePath exe) (toInfo $ PD.buildInfo exe)-		toTest test = Test (componentName $ PD.testName test) (testSuiteEnabled test) (toInfo $ PD.testBuildInfo test)-		toInfo info = Info {-			_infoDepends = map pkgName (PD.targetBuildDepends info),-			_infoLanguage = PD.defaultLanguage info,-			_infoExtensions = PD.defaultExtensions info ++ PD.otherExtensions info ++ PD.oldExtensions info,-			_infoGHCOptions = fromMaybe [] $ lookup GHC (PD.options info),-			_infoSourceDirs = PD.hsSourceDirs info }--		pkgName :: P.Dependency -> String-		pkgName (P.Dependency dep _) = P.unPackageName dep--		flattenDescr :: PD.GenericPackageDescription -> PD.PackageDescription-		flattenDescr gpkg = pkg {-			PD.library = flip fmap mlib $ flattenCondTree-				(insertInfo PD.libBuildInfo (\i l -> l { PD.libBuildInfo = i })),-			PD.executables = flip fmap mexes $-				second (flattenCondTree (insertInfo PD.buildInfo (\i l -> l { PD.buildInfo = i }))) >>>-				(\(n, e) -> e { PD.exeName = n }),-			PD.testSuites = flip fmap mtests $-				second (flattenCondTree (insertInfo PD.testBuildInfo (\i l -> l { PD.testBuildInfo = i }))) >>>-				(\(n, t) -> t { PD.testName = n }) }-			where-				pkg = PD.packageDescription gpkg-				mlib = PD.condLibrary gpkg-				mexes = PD.condExecutables gpkg-				mtests = PD.condTestSuites gpkg--				insertInfo :: (a -> PD.BuildInfo) -> (PD.BuildInfo -> a -> a) -> [P.Dependency] -> a -> a-				insertInfo f s deps' x = s ((f x) { PD.targetBuildDepends = deps' }) x---- | Read project info from .cabal-readProject :: FilePath -> IO Project-readProject file = do-	source <- readFile file-	length source `seq` either (hsdevError . InspectCabalError file) (return . mkProject) $ analyzeCabal source-	where-		mkProject desc = (project file) {-			_projectDescription = Just desc }---- | Load project description-loadProject :: Project -> IO Project-loadProject p-	| isJust (_projectDescription p) = return p-	| otherwise = readProject (_projectCabal p)---- | Extensions for target-withExtensions :: a -> Info -> Extensions a-withExtensions x i = Extensions {-	_extensions = _infoExtensions i,-	_ghcOptions = _infoGHCOptions i,-	_entity = x }---- | Returns build targets infos-infos :: ProjectDescription -> [Info]-infos p =-	maybe [] (return . _libraryBuildInfo) (_projectLibrary p) ++-	map _executableBuildInfo (_projectExecutables p) ++-	map _testBuildInfo (_projectTests p)---- | Check if source related to target, source must be relative to project directory-inTarget :: FilePath -> Info -> Bool-inTarget src info = any (`isParent` src) $ view infoSourceDirsDef info---- | Get first target for source file-fileTarget :: Project -> FilePath -> Maybe Info-fileTarget p f = listToMaybe $ fileTargets p f---- | Get possible targets for source file--- There can be many candidates in case of module related to several executables or tests-fileTargets :: Project -> FilePath -> [Info]-fileTargets p f = case filter ((`isSuffixOf` f') . normalise . _executablePath) exes of-	[] -> filter (f' `inTarget`) $ maybe [] infos $ _projectDescription p-	exes' -> map _executableBuildInfo exes'-	where-		f' = makeRelative (_projectPath p) f-		exes = maybe [] _projectExecutables $ _projectDescription p---- | Finds source dir file belongs to-findSourceDir :: Project -> FilePath -> Maybe (Extensions FilePath)-findSourceDir p f = do-	info <- listToMaybe $ fileTargets p f-	fmap (`withExtensions` info) $ listToMaybe $ filter (`isParent` f) $ map (_projectPath p </>) $ view infoSourceDirsDef info---- | Returns source dirs for library, executables and tests-sourceDirs :: ProjectDescription -> [Extensions FilePath]-sourceDirs = ordNub . concatMap dirs . infos where-	dirs i = map (`withExtensions` i) $ view infoSourceDirsDef i---- | Get options for specific target-targetOpts :: Info -> [String]-targetOpts info' = concat [-	["-i" ++ s | s <- _infoSourceDirs info'],-	extensionsOpts $ withExtensions () info',-	["-package " ++ p | p <- _infoDepends info']]---- | Extension as flag name-showExtension :: Extension -> String-showExtension = display---- | Convert -Xext to ext-flagExtension :: String -> Maybe String-flagExtension = stripPrefix "-X"---- | Convert ext to -Xext-extensionFlag :: String -> String-extensionFlag = ("-X" ++)---- | Extensions as opts to GHC-extensionsOpts :: Extensions a -> [String]-extensionsOpts e = map (extensionFlag . showExtension) (_extensions e) ++ _ghcOptions e+{-# LANGUAGE OverloadedStrings, CPP #-}
+
+module HsDev.Project (
+	module HsDev.Project.Types,
+
+	infoSourceDirsDef, targetFiles, projectTargetFiles,
+	analyzeCabal,
+	readProject, loadProject,
+	withExtensions,
+	fileInTarget, fileTarget, fileTargets, findSourceDir, sourceDirs,
+	targetOpts,
+
+	-- * Helpers
+	showExtension, flagExtension, extensionFlag,
+	extensionsOpts
+	) where
+
+import Control.Arrow
+import Control.Lens hiding ((.=), (%=), (<.>), set')
+import Control.Monad.Except
+import Control.Monad.Loops
+import Data.List
+import Data.Maybe
+import Data.Text (Text, pack, unpack)
+import qualified Data.Text as T (intercalate)
+import Data.Text.Lens (unpacked)
+import Distribution.Compiler (CompilerFlavor(GHC))
+import qualified Distribution.Package as P
+import qualified Distribution.PackageDescription as PD
+import qualified Distribution.ModuleName as PD (toFilePath)
+import Distribution.PackageDescription.Parse
+import Distribution.ModuleName (components)
+import Distribution.Text (display)
+import Language.Haskell.Extension
+import System.FilePath
+import System.Log.Simple hiding (Level(..))
+import qualified System.Log.Simple as Log (Level(..))
+import Text.Format
+
+import System.Directory.Paths
+import HsDev.Project.Compat
+import HsDev.Project.Types
+import HsDev.Error
+import HsDev.Util
+
+-- | infoSourceDirs lens with default
+infoSourceDirsDef :: Lens' Info [Path]
+infoSourceDirsDef = lens get' set' where
+	get' i = case _infoSourceDirs i of
+		[] -> ["."]
+		dirs -> dirs
+	set' i ["."] = i { _infoSourceDirs = [] }
+	set' i dirs = i { _infoSourceDirs = dirs }
+
+-- | Get all source file names of target without prepending them with source-dirs
+targetFiles :: Target t => t -> [Path]
+targetFiles target' = concat [
+	maybeToList (targetMain target'),
+	map toFile $ targetModules target',
+	map toFile $ target' ^.. buildInfo . infoOtherModules . each]
+	where
+		toFile ps = fromFilePath (joinPath (ps ^.. each . unpacked) <.> "hs")
+
+-- | Get all source file names relative to project root
+projectTargetFiles :: (MonadLog m, Target t) => Project -> t -> m [Path]
+projectTargetFiles proj t = do
+	liftM concat $ forM files $ \file' -> do
+		candidate <- liftIO $ firstM (fileExists . absolutise (proj ^. projectPath)) [subPath srcDir file' | srcDir <- srcDirs]
+		case candidate of
+			Nothing -> do
+				sendLog Log.Warning $ "Unable to locate source file: {} in source-dirs: {}" ~~ file' ~~ (T.intercalate ", " srcDirs)
+				return []
+			Just file'' -> return [normPath file'']
+	where
+		files = targetFiles t
+		srcDirs = t ^.. buildInfo . infoSourceDirsDef . each
+
+-- | Analyze cabal file
+analyzeCabal :: String -> Either String ProjectDescription
+analyzeCabal source = case liftM flattenDescr $ parsePackageDesc source of
+	ParseOk _ r -> Right ProjectDescription {
+		_projectVersion = pack $ showVer $ P.pkgVersion $ PD.package r,
+		_projectLibrary = fmap toLibrary $ PD.library r,
+		_projectExecutables = fmap toExecutable $ PD.executables r,
+		_projectTests = fmap toTest $ PD.testSuites r }
+	ParseFailed e -> Left $ "Parse failed: " ++ show e
+	where
+		toLibrary lib = Library (map (map pack . components) $ PD.exposedModules lib) (toInfo $ PD.libBuildInfo lib)
+		toExecutable exe = Executable (componentName $ PD.exeName exe) (fromFilePath $ PD.modulePath exe) (toInfo $ PD.buildInfo exe)
+		toTest test = Test (componentName $ PD.testName test) (testSuiteEnabled test) (fmap fromFilePath mainFile) (toInfo $ PD.testBuildInfo test) where
+			mainFile = case PD.testInterface test of
+				PD.TestSuiteExeV10 _ fpath -> Just fpath
+				PD.TestSuiteLibV09 _ mname -> Just $ PD.toFilePath mname
+				_ -> Nothing
+		toInfo info = Info {
+			_infoDepends = map pkgName (PD.targetBuildDepends info),
+			_infoLanguage = PD.defaultLanguage info,
+			_infoExtensions = PD.defaultExtensions info ++ PD.otherExtensions info ++ PD.oldExtensions info,
+			_infoGHCOptions = maybe [] (map pack) $ lookup GHC (PD.options info),
+			_infoSourceDirs = map pack $ PD.hsSourceDirs info,
+			_infoOtherModules = map (map pack . components) (PD.otherModules info) }
+
+		pkgName :: P.Dependency -> Text
+		pkgName (P.Dependency dep _) = pack $ P.unPackageName dep
+
+		flattenDescr :: PD.GenericPackageDescription -> PD.PackageDescription
+		flattenDescr gpkg = pkg {
+			PD.library = flip fmap mlib $ flattenCondTree
+				(insertInfo PD.libBuildInfo (\i l -> l { PD.libBuildInfo = i })),
+			PD.executables = flip fmap mexes $
+				second (flattenCondTree (insertInfo PD.buildInfo (\i l -> l { PD.buildInfo = i }))) >>>
+				(\(n, e) -> e { PD.exeName = n }),
+			PD.testSuites = flip fmap mtests $
+				second (flattenCondTree (insertInfo PD.testBuildInfo (\i l -> l { PD.testBuildInfo = i }))) >>>
+				(\(n, t) -> t { PD.testName = n }) }
+			where
+				pkg = PD.packageDescription gpkg
+				mlib = PD.condLibrary gpkg
+				mexes = PD.condExecutables gpkg
+				mtests = PD.condTestSuites gpkg
+
+				insertInfo :: (a -> PD.BuildInfo) -> (PD.BuildInfo -> a -> a) -> [P.Dependency] -> a -> a
+				insertInfo f s deps' x = s ((f x) { PD.targetBuildDepends = deps' }) x
+
+-- | Read project info from .cabal
+readProject :: FilePath -> IO Project
+readProject file' = do
+	source <- readFile file'
+	length source `seq` either (hsdevError . InspectCabalError file') (return . mkProject) $ analyzeCabal source
+	where
+		mkProject desc = (project file') {
+			_projectDescription = Just desc }
+
+-- | Load project description
+loadProject :: Project -> IO Project
+loadProject p
+	| isJust (_projectDescription p) = return p
+	| otherwise = readProject (_projectCabal p ^. path)
+
+-- | Extensions for target
+withExtensions :: a -> Info -> Extensions a
+withExtensions x i = Extensions {
+	_extensions = _infoExtensions i,
+	_ghcOptions = _infoGHCOptions i,
+	_entity = x }
+
+-- | Check if source related to target, source must be relative to project directory
+fileInTarget :: Path -> Info -> Bool
+fileInTarget src info = any (`isParent` src) $ view infoSourceDirsDef info
+
+-- | Get first target for source file
+fileTarget :: Project -> Path -> Maybe Info
+fileTarget p f = listToMaybe $ fileTargets p f
+
+-- | Get possible targets for source file
+-- There can be many candidates in case of module related to several executables or tests
+fileTargets :: Project -> Path -> [Info]
+fileTargets p f = case filter ((`isParent` f') . view executablePath) exes of
+	[] -> filter (f' `fileInTarget`) (p ^.. projectDescription . _Just . infos)
+	exes' -> map _executableBuildInfo exes'
+	where
+		f' = relPathTo (_projectPath p) f
+		exes = p ^. projectDescription . _Just . projectExecutables
+
+-- | Finds source dir file belongs to
+findSourceDir :: Project -> Path -> Maybe (Extensions Path)
+findSourceDir p f = do
+	info <- listToMaybe $ fileTargets p f
+	fmap (`withExtensions` info) $ listToMaybe $ filter (`isParent` f) $ map (_projectPath p `subPath`) (info ^. infoSourceDirsDef)
+
+-- | Returns source dirs for library, executables and tests
+sourceDirs :: ProjectDescription -> [Extensions Path]
+sourceDirs = ordNub . concatMap dirs . toListOf infos where
+	dirs i = map (`withExtensions` i) (i ^. infoSourceDirsDef)
+
+-- | Get options for specific target
+targetOpts :: Info -> [String]
+targetOpts info' = concat [
+	["-i" ++ unpack s | s <- _infoSourceDirs info'],
+	extensionsOpts $ withExtensions () info',
+	["-package " ++ unpack p | p <- _infoDepends info']]
+
+-- | Extension as flag name
+showExtension :: Extension -> String
+showExtension = display
+
+-- | Convert -Xext to ext
+flagExtension :: String -> Maybe String
+flagExtension = stripPrefix "-X"
+
+-- | Convert ext to -Xext
+extensionFlag :: String -> String
+extensionFlag = ("-X" ++)
+
+-- | Extensions as opts to GHC
+extensionsOpts :: Extensions a -> [String]
+extensionsOpts e = map (extensionFlag . showExtension) (_extensions e) ++ map unpack (_ghcOptions e)
src/HsDev/Project/Compat.hs view
@@ -1,62 +1,63 @@-{-# LANGUAGE CPP #-}--module HsDev.Project.Compat (-	showVer, componentName, testSuiteEnabled,-	flattenCondTree,-	parsePackageDesc-	) where--import Data.Maybe (maybeToList)-import qualified Distribution.PackageDescription as PD-import Distribution.PackageDescription.Parse-import Distribution.Version (Version)-import Distribution.Text (display)-#if MIN_VERSION_Cabal(2,0,0)-import Distribution.Types.CondTree-#else -import Distribution.PackageDescription (CondTree(..))-#endif--#if MIN_VERSION_Cabal(2,0,0)-import Distribution.Types.UnqualComponentName-#else-import Data.Version (showVersion)-#endif--showVer :: Version -> String-#if MIN_VERSION_Cabal(2,0,0)-showVer = display-#else-showVer = showVersion-#endif--#if MIN_VERSION_Cabal(2,0,0)-componentName :: UnqualComponentName -> String-componentName = unUnqualComponentName-#else-componentName :: String -> String-componentName = id-#endif--testSuiteEnabled :: PD.TestSuite -> Bool-#if MIN_VERSION_Cabal(2,0,0)-testSuiteEnabled _ = True-#else-testSuiteEnabled = PD.testEnabled-#endif--flattenCondTree :: Monoid a => (c -> a -> a) -> CondTree v c a -> a-flattenCondTree f (PD.CondNode x cs cmps) = f cs x `mappend` mconcat (concatMap flattenBranch cmps) where-#if MIN_VERSION_Cabal(2,0,0)-	flattenBranch (CondBranch _ t mb) = go t mb-#else-	flattenBranch (_, t, mb) = go t mb-#endif-	go t mb = flattenCondTree f t : map (flattenCondTree f) (maybeToList mb)--parsePackageDesc :: String -> ParseResult PD.GenericPackageDescription-#if MIN_VERSION_Cabal(2,0,0)-parsePackageDesc = parseGenericPackageDescription-#else-parsePackageDesc = parsePackageDescription-#endif+{-# LANGUAGE CPP #-}
+
+module HsDev.Project.Compat (
+	showVer, componentName, testSuiteEnabled,
+	flattenCondTree,
+	parsePackageDesc
+	) where
+
+import Data.Maybe (maybeToList)
+import Data.Text (Text, pack)
+import qualified Distribution.PackageDescription as PD
+import Distribution.PackageDescription.Parse
+import Distribution.Version (Version)
+import Distribution.Text (display)
+#if MIN_VERSION_Cabal(2,0,0)
+import Distribution.Types.CondTree
+#else 
+import Distribution.PackageDescription (CondTree(..))
+#endif
+
+#if MIN_VERSION_Cabal(2,0,0)
+import Distribution.Types.UnqualComponentName
+#else
+import Data.Version (showVersion)
+#endif
+
+showVer :: Version -> String
+#if MIN_VERSION_Cabal(2,0,0)
+showVer = display
+#else
+showVer = showVersion
+#endif
+
+#if MIN_VERSION_Cabal(2,0,0)
+componentName :: UnqualComponentName -> Text
+componentName = pack . unUnqualComponentName
+#else
+componentName :: String -> Text
+componentName = pack
+#endif
+
+testSuiteEnabled :: PD.TestSuite -> Bool
+#if MIN_VERSION_Cabal(2,0,0)
+testSuiteEnabled _ = True
+#else
+testSuiteEnabled = PD.testEnabled
+#endif
+
+flattenCondTree :: Monoid a => (c -> a -> a) -> CondTree v c a -> a
+flattenCondTree f (PD.CondNode x cs cmps) = f cs x `mappend` mconcat (concatMap flattenBranch cmps) where
+#if MIN_VERSION_Cabal(2,0,0)
+	flattenBranch (CondBranch _ t mb) = go t mb
+#else
+	flattenBranch (_, t, mb) = go t mb
+#endif
+	go t mb = flattenCondTree f t : map (flattenCondTree f) (maybeToList mb)
+
+parsePackageDesc :: String -> ParseResult PD.GenericPackageDescription
+#if MIN_VERSION_Cabal(2,0,0)
+parsePackageDesc = parseGenericPackageDescription
+#else
+parsePackageDesc = parsePackageDescription
+#endif
src/HsDev/Project/Types.hs view
@@ -1,298 +1,352 @@-{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}--module HsDev.Project.Types (-	Project(..), projectName, projectPath, projectCabal, projectDescription, project, absolutiseProjectPaths, relativiseProjectPaths,-	ProjectDescription(..), projectVersion, projectLibrary, projectExecutables, projectTests,-	Target(..),-	Library(..), libraryModules, libraryBuildInfo,-	Executable(..), executableName, executablePath, executableBuildInfo,-	Test(..), testName, testEnabled, testBuildInfo,-	Info(..), infoDepends, infoLanguage, infoExtensions, infoGHCOptions, infoSourceDirs,-	Extensions(..), extensions, ghcOptions, entity,-	) where--import Control.Arrow-import Control.DeepSeq (NFData(..))-import Control.Lens (makeLenses)-import Data.Aeson-import Data.Aeson.Types (Parser)-import Data.List-import Data.Maybe-import Data.Monoid-import Data.Ord-import Distribution.Text (display, simpleParse)-import qualified Distribution.Text (Text)-import Language.Haskell.Extension-import Text.Format-import System.FilePath--import System.Directory.Paths-import HsDev.Util---- | Cabal project-data Project = Project {-	_projectName :: String,-	_projectPath :: FilePath,-	_projectCabal :: FilePath,-	_projectDescription :: Maybe ProjectDescription }-		deriving (Read)--instance NFData Project where-	rnf (Project n p c _) = rnf n `seq` rnf p `seq` rnf c--instance Eq Project where-	l == r = _projectCabal l == _projectCabal r--instance Ord Project where-	compare l r = compare (_projectName l, _projectCabal l) (_projectName r, _projectCabal r)--instance Show Project where-	show p = unlines $ [-		"project " ++ _projectName p,-		"\tcabal: " ++ _projectCabal p,-		"\tdescription:"] ++ concatMap (map (tab 2) . lines . show) (maybeToList $ _projectDescription p)--instance ToJSON Project where-	toJSON p = object [-		"name" .= _projectName p,-		"path" .= _projectPath p,-		"cabal" .= _projectCabal p,-		"description" .= _projectDescription p]--instance FromJSON Project where-	parseJSON = withObject "project" $ \v -> Project <$>-		v .:: "name" <*>-		v .:: "path" <*>-		v .:: "cabal" <*>-		v .:: "description"--instance Paths Project where-	paths f (Project nm p c desc) = Project nm <$> f p <*> f c <*> traverse (paths f) desc---- | Make project by .cabal file-project :: FilePath -> Project-project file = Project {-	_projectName = takeBaseName (takeDirectory cabal),-	_projectPath = takeDirectory cabal,-	_projectCabal = cabal,-	_projectDescription = Nothing }-	where-		file' = dropTrailingPathSeparator $ normalise file-		cabal-			| takeExtension file' == ".cabal" = file'-			| otherwise = file' </> (takeBaseName file' <.> "cabal")---- | Make paths absolute, not relative-absolutiseProjectPaths :: Project -> Project-absolutiseProjectPaths proj = absolutise (_projectPath proj) proj---- | Make paths relative-relativiseProjectPaths :: Project -> Project-relativiseProjectPaths proj = relativise (_projectPath proj) proj--data ProjectDescription = ProjectDescription {-	_projectVersion :: String,-	_projectLibrary :: Maybe Library,-	_projectExecutables :: [Executable],-	_projectTests :: [Test] }-		deriving (Eq, Read)--instance Show ProjectDescription where-	show pd = unlines $-		concatMap (lines . show) (maybeToList (_projectLibrary pd)) ++-		concatMap (lines . show) (_projectExecutables pd) ++-		concatMap (lines . show) (_projectTests pd)--instance ToJSON ProjectDescription where-	toJSON d = object [-		"version" .= _projectVersion d,-		"library" .= _projectLibrary d,-		"executables" .= _projectExecutables d,-		"tests" .= _projectTests d]--instance FromJSON ProjectDescription where-	parseJSON = withObject "project description" $ \v -> ProjectDescription <$>-		v .:: "version" <*>-		v .:: "library" <*>-		v .:: "executables" <*>-		v .:: "tests"--instance Paths ProjectDescription where-	paths f (ProjectDescription v lib exes tests) = ProjectDescription v <$> traverse (paths f) lib <*> traverse (paths f) exes <*> traverse (paths f) tests--class Target a where-	buildInfo :: a -> Info---- | Library in project-data Library = Library {-	_libraryModules :: [[String]],-	_libraryBuildInfo :: Info }-		deriving (Eq, Read)--instance Target Library where-	buildInfo = _libraryBuildInfo--instance Show Library where-	show l = unlines $-		["library", "\tmodules:"] ++-		(map (tab 2 . intercalate ".") $ _libraryModules l) ++-		(map (tab 1) . lines . show $ _libraryBuildInfo l)--instance ToJSON Library where-	toJSON l = object [-		"modules" .= fmap (intercalate ".") (_libraryModules l),-		"info" .= _libraryBuildInfo l]--instance FromJSON Library where-	parseJSON = withObject "library" $ \v -> Library <$> (fmap splitModule <$> v .:: "modules") <*> v .:: "info" where-		splitModule :: String -> [String]-		splitModule = takeWhile (not . null) . unfoldr (Just . second (drop 1) . break (== '.'))--instance Paths Library where-	paths f (Library ms info) = Library ms <$> paths f info---- | Executable-data Executable = Executable {-	_executableName :: String,-	_executablePath :: FilePath,-	_executableBuildInfo :: Info }-		deriving (Eq, Read)--instance Target Executable where-	buildInfo = _executableBuildInfo--instance Show Executable where-	show e = unlines $-		["executable " ++ _executableName e, "\tpath: " ++ _executablePath e] ++-		(map (tab 1) . lines . show $ _executableBuildInfo e)--instance ToJSON Executable where-	toJSON e = object [-		"name" .= _executableName e,-		"path" .= _executablePath e,-		"info" .= _executableBuildInfo e]--instance FromJSON Executable where-	parseJSON = withObject "executable" $ \v -> Executable <$>-		v .:: "name" <*>-		v .:: "path" <*>-		v .:: "info"--instance Paths Executable where-	paths f (Executable n p info) = Executable n <$> f p <*> paths f info---- | Test-data Test = Test {-	_testName :: String,-	_testEnabled :: Bool,-	_testBuildInfo :: Info }-		deriving (Eq, Read)--instance Target Test where-	buildInfo = _testBuildInfo--instance Show Test where-	show t = unlines $-		["test " ++ _testName t, "\tenabled: " ++ show (_testEnabled t)] ++-		(map (tab 1) . lines . show $ _testBuildInfo t)--instance ToJSON Test where-	toJSON t = object [-		"name" .= _testName t,-		"enabled" .= _testEnabled t,-		"info" .= _testBuildInfo t]--instance FromJSON Test where-	parseJSON = withObject "test" $ \v -> Test <$>-		v .:: "name" <*>-		v .:: "enabled" <*>-		v .:: "info"--instance Paths Test where-	paths f (Test n e info) = Test n e <$> paths f info---- | Build info-data Info = Info {-	_infoDepends :: [String],-	_infoLanguage :: Maybe Language,-	_infoExtensions :: [Extension],-	_infoGHCOptions :: [String],-	_infoSourceDirs :: [FilePath] }-		deriving (Eq, Read)--instance Monoid Info where-	mempty = Info [] Nothing [] [] []-	mappend l r = Info-		(ordNub $ _infoDepends l ++ _infoDepends r)-		(getFirst $ First (_infoLanguage l) `mappend` First (_infoLanguage r))-		(_infoExtensions l ++ _infoExtensions r)-		(_infoGHCOptions l ++ _infoGHCOptions r)-		(ordNub $ _infoSourceDirs l ++ _infoSourceDirs r)--instance Ord Info where-	compare l r = compare (_infoSourceDirs l, _infoDepends l, _infoGHCOptions l) (_infoSourceDirs r, _infoDepends r, _infoGHCOptions r)--instance Show Info where-	show i = unlines $ lang ++ exts ++ opts ++ sources where-		lang = maybe [] (\l -> ["default-language: " ++ display l]) $ _infoLanguage i-		exts-			| null (_infoExtensions i) = []-			| otherwise = "extensions:" : map (tab 1 . display) (_infoExtensions i)-		opts-			| null (_infoGHCOptions i) = []-			| otherwise = "ghc-options:" : map (tab 1) (_infoGHCOptions i)-		sources = "source-dirs:" : (map (tab 1) $ _infoSourceDirs i)--instance ToJSON Info where-	toJSON i = object [-		"build-depends" .= _infoDepends i,-		"language" .= fmap display (_infoLanguage i),-		"extensions" .= map display (_infoExtensions i),-		"ghc-options" .= _infoGHCOptions i,-		"source-dirs" .= _infoSourceDirs i]--instance FromJSON Info where-	parseJSON = withObject "info" $ \v -> Info <$>-		v .: "build-depends" <*>-		((v .:: "language") >>= traverse (parseDT "Language")) <*>-		((v .:: "extensions") >>= traverse (parseDT "Extension")) <*>-		v .:: "ghc-options" <*>-		v .:: "source-dirs"-		where-			parseDT :: Distribution.Text.Text a => String -> String -> Parser a-			parseDT typeName v = maybe err return (simpleParse v) where-				err = fail $ "Can't parse {}: {}" ~~ typeName ~~ v--instance Paths Info where-	paths f (Info deps lang exts opts dirs) = Info deps lang exts opts <$> traverse f dirs---- | Entity with project extensions-data Extensions a = Extensions {-	_extensions :: [Extension],-	_ghcOptions :: [String],-	_entity :: a }-		deriving (Eq, Read, Show)--instance Ord a => Ord (Extensions a) where-	compare = comparing _entity--instance Functor Extensions where-	fmap f (Extensions e o x) = Extensions e o (f x)--instance Applicative Extensions where-	pure = Extensions [] []-	(Extensions l lo f) <*> (Extensions r ro x) = Extensions (ordNub $ l ++ r) (ordNub $ lo ++ ro) (f x)--instance Foldable Extensions where-	foldMap f (Extensions _ _ x) = f x--instance Traversable Extensions where-	traverse f (Extensions e o x) = Extensions e o <$> f x--makeLenses ''Project-makeLenses ''ProjectDescription-makeLenses ''Library-makeLenses ''Executable-makeLenses ''Test-makeLenses ''Info-makeLenses ''Extensions+{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Project.Types (
+	Project(..), projectName, projectPath, projectCabal, projectDescription, project,
+	ProjectDescription(..), projectVersion, projectLibrary, projectExecutables, projectTests, infos, targetInfos,
+	Target(..), TargetInfo(..), targetInfoName, targetBuildInfo, targetInfoMain, targetInfoModules, targetInfo,
+	Library(..), libraryModules, libraryBuildInfo,
+	Executable(..), executableName, executablePath, executableBuildInfo,
+	Test(..), testName, testEnabled, testBuildInfo, testMain,
+	Info(..), infoDepends, infoLanguage, infoExtensions, infoGHCOptions, infoSourceDirs, infoOtherModules,
+	Extensions(..), extensions, ghcOptions, entity,
+	) where
+
+import Control.DeepSeq (NFData(..))
+import Control.Lens hiding ((.=), (<.>))
+import Data.Aeson
+import Data.Maybe
+import Data.Monoid
+import Data.Ord
+import Data.Text (Text)
+import qualified Data.Text as T
+import Distribution.Text (display)
+import Language.Haskell.Extension
+import System.FilePath
+
+import System.Directory.Paths
+import HsDev.Util
+
+-- | Cabal project
+data Project = Project {
+	_projectName :: Text,
+	_projectPath :: Path,
+	_projectCabal :: Path,
+	_projectDescription :: Maybe ProjectDescription }
+		deriving (Read)
+
+instance NFData Project where
+	rnf (Project n p c _) = rnf n `seq` rnf p `seq` rnf c
+
+instance Eq Project where
+	l == r = _projectCabal l == _projectCabal r
+
+instance Ord Project where
+	compare l r = compare (_projectName l, _projectCabal l) (_projectName r, _projectCabal r)
+
+instance Show Project where
+	show p = unlines $ [
+		"project " ++ _projectName p ^. path,
+		"\tcabal: " ++ _projectCabal p ^. path,
+		"\tdescription:"] ++ concatMap (map (tab 2) . lines . show) (maybeToList $ _projectDescription p)
+
+instance ToJSON Project where
+	toJSON p = object [
+		"name" .= _projectName p,
+		"path" .= _projectPath p,
+		"cabal" .= _projectCabal p,
+		"description" .= _projectDescription p]
+
+instance FromJSON Project where
+	parseJSON = withObject "project" $ \v -> Project <$>
+		v .:: "name" <*>
+		v .:: "path" <*>
+		v .:: "cabal" <*>
+		v .:: "description"
+
+instance Paths Project where
+	paths f (Project nm p c desc) = Project nm <$> paths f p <*> paths f c <*> traverse (paths f) desc
+
+-- | Make project by .cabal file
+project :: FilePath -> Project
+project file = Project {
+	_projectName = fromFilePath . takeBaseName . takeDirectory $ cabal,
+	_projectPath = fromFilePath . takeDirectory $ cabal,
+	_projectCabal = fromFilePath cabal,
+	_projectDescription = Nothing }
+	where
+		file' = dropTrailingPathSeparator $ normalise file
+		cabal
+			| takeExtension file' == ".cabal" = file'
+			| otherwise = file' </> (takeBaseName file' <.> "cabal")
+
+data ProjectDescription = ProjectDescription {
+	_projectVersion :: Text,
+	_projectLibrary :: Maybe Library,
+	_projectExecutables :: [Executable],
+	_projectTests :: [Test] }
+		deriving (Eq, Read)
+
+-- | Build target infos
+infos :: Traversal' ProjectDescription Info
+infos f desc = (\lib exes tests -> desc { _projectLibrary = lib, _projectExecutables = exes, _projectTests = tests }) <$>
+	(_Just . buildInfo) f (_projectLibrary desc) <*>
+	(each . buildInfo) f (_projectExecutables desc) <*>
+	(each . buildInfo) f (_projectTests desc)
+
+-- | Build target infos, more detailed
+targetInfos :: ProjectDescription -> [TargetInfo]
+targetInfos desc = concat [
+	map targetInfo $ maybeToList (_projectLibrary desc),
+	map targetInfo $ _projectExecutables desc,
+	map targetInfo $ _projectTests desc]
+
+instance Show ProjectDescription where
+	show pd = unlines $
+		concatMap (lines . show) (maybeToList (_projectLibrary pd)) ++
+		concatMap (lines . show) (_projectExecutables pd) ++
+		concatMap (lines . show) (_projectTests pd)
+
+instance ToJSON ProjectDescription where
+	toJSON d = object [
+		"version" .= _projectVersion d,
+		"library" .= _projectLibrary d,
+		"executables" .= _projectExecutables d,
+		"tests" .= _projectTests d]
+
+instance FromJSON ProjectDescription where
+	parseJSON = withObject "project description" $ \v -> ProjectDescription <$>
+		v .:: "version" <*>
+		v .:: "library" <*>
+		v .:: "executables" <*>
+		v .:: "tests"
+
+instance Paths ProjectDescription where
+	paths f (ProjectDescription v lib exes tests) = ProjectDescription v <$> traverse (paths f) lib <*> traverse (paths f) exes <*> traverse (paths f) tests
+
+class Target a where
+	targetName :: Traversal' a Text
+	buildInfo :: Lens' a Info
+	targetMain :: a -> Maybe Path
+	targetModules :: a -> [[Text]]
+
+data TargetInfo = TargetInfo {
+	_targetInfoName :: Maybe Text,
+	_targetBuildInfo :: Info,
+	_targetInfoMain :: Maybe Path,
+	_targetInfoModules :: [[Text]] }
+		deriving (Eq, Ord, Show)
+
+targetInfo :: Target a => a -> TargetInfo
+targetInfo t = TargetInfo (t ^? targetName) (t ^. buildInfo) (targetMain t) (targetModules t)
+
+instance Paths TargetInfo where
+	paths f (TargetInfo n i mp ms) = TargetInfo n <$> paths f i <*> traverse (paths f) mp <*> pure ms
+
+-- | Library in project
+data Library = Library {
+	_libraryModules :: [[Text]],
+	_libraryBuildInfo :: Info }
+		deriving (Eq, Read)
+
+instance Show Library where
+	show l = unlines $
+		["library", "\tmodules:"] ++
+		(map (tab 2 . T.unpack . T.intercalate ".") $ _libraryModules l) ++
+		(map (tab 1) . lines . show $ _libraryBuildInfo l)
+
+instance ToJSON Library where
+	toJSON l = object [
+		"modules" .= fmap (T.intercalate ".") (_libraryModules l),
+		"info" .= _libraryBuildInfo l]
+
+instance FromJSON Library where
+	parseJSON = withObject "library" $ \v -> Library <$> (fmap (T.split (== '.')) <$> v .:: "modules") <*> v .:: "info" where
+
+instance Paths Library where
+	paths f (Library ms info) = Library ms <$> paths f info
+
+-- | Executable
+data Executable = Executable {
+	_executableName :: Text,
+	_executablePath :: Path,
+	_executableBuildInfo :: Info }
+		deriving (Eq, Read)
+
+instance Show Executable where
+	show e = unlines $
+		["executable " ++ T.unpack (_executableName e), "\tpath: " ++ (_executablePath e ^. path)] ++
+		(map (tab 1) . lines . show $ _executableBuildInfo e)
+
+instance ToJSON Executable where
+	toJSON e = object [
+		"name" .= _executableName e,
+		"path" .= _executablePath e,
+		"info" .= _executableBuildInfo e]
+
+instance FromJSON Executable where
+	parseJSON = withObject "executable" $ \v -> Executable <$>
+		v .:: "name" <*>
+		v .:: "path" <*>
+		v .:: "info"
+
+instance Paths Executable where
+	paths f (Executable n p info) = Executable n <$> paths f p <*> paths f info
+
+-- | Test
+data Test = Test {
+	_testName :: Text,
+	_testEnabled :: Bool,
+	_testMain :: Maybe Path,
+	_testBuildInfo :: Info }
+		deriving (Eq, Read)
+
+instance Show Test where
+	show t = unlines $
+		["test " ++ T.unpack (_testName t), "\tenabled: " ++ show (_testEnabled t)] ++
+		maybe [] (\f -> ["\tmain-is: " ++ f ^. path]) (_testMain t) ++
+		(map (tab 1) . lines . show $ _testBuildInfo t)
+
+instance ToJSON Test where
+	toJSON t = object [
+		"name" .= _testName t,
+		"enabled" .= _testEnabled t,
+		"main" .= _testMain t,
+		"info" .= _testBuildInfo t]
+
+instance FromJSON Test where
+	parseJSON = withObject "test" $ \v -> Test <$>
+		v .:: "name" <*>
+		v .:: "enabled" <*>
+		v .::? "main" <*>
+		v .:: "info"
+
+instance Paths Test where
+	paths f (Test n e m info) = Test n e <$> traverse (paths f) m <*> paths f info
+
+-- | Build info
+data Info = Info {
+	_infoDepends :: [Text],
+	_infoLanguage :: Maybe Language,
+	_infoExtensions :: [Extension],
+	_infoGHCOptions :: [Text],
+	_infoSourceDirs :: [Path],
+	_infoOtherModules :: [[Text]] }
+		deriving (Eq, Read)
+
+instance Monoid Info where
+	mempty = Info [] Nothing [] [] [] []
+	mappend l r = Info
+		(ordNub $ _infoDepends l ++ _infoDepends r)
+		(getFirst $ First (_infoLanguage l) `mappend` First (_infoLanguage r))
+		(_infoExtensions l ++ _infoExtensions r)
+		(_infoGHCOptions l ++ _infoGHCOptions r)
+		(ordNub $ _infoSourceDirs l ++ _infoSourceDirs r)
+		(ordNub $ _infoOtherModules l ++ _infoOtherModules r)
+
+instance Ord Info where
+	compare l r = compare (_infoSourceDirs l, _infoDepends l, _infoGHCOptions l) (_infoSourceDirs r, _infoDepends r, _infoGHCOptions r)
+
+instance Show Info where
+	show i = unlines $ lang ++ exts ++ opts ++ sources ++ otherMods where
+		lang = maybe [] (\l -> ["default-language: " ++ display l]) $ _infoLanguage i
+		exts
+			| null (_infoExtensions i) = []
+			| otherwise = "extensions:" : map (tab 1 . display) (_infoExtensions i)
+		opts
+			| null (_infoGHCOptions i) = []
+			| otherwise = "ghc-options:" : map (tab 1 . T.unpack) (_infoGHCOptions i)
+		sources = "source-dirs:" : map (tab 1 . T.unpack) (_infoSourceDirs i)
+		otherMods = "other-modules:" : (map (tab 1 . T.unpack) . fmap (T.intercalate ".") $ _infoOtherModules i)
+
+instance ToJSON Info where
+	toJSON i = object [
+		"build-depends" .= _infoDepends i,
+		"language" .= _infoLanguage i,
+		"extensions" .= _infoExtensions i,
+		"ghc-options" .= _infoGHCOptions i,
+		"source-dirs" .= _infoSourceDirs i,
+		"other-modules" .= _infoOtherModules i]
+
+instance FromJSON Info where
+	parseJSON = withObject "info" $ \v -> Info <$>
+		v .: "build-depends" <*>
+		v .:: "language" <*>
+		v .:: "extensions" <*>
+		v .:: "ghc-options" <*>
+		v .:: "source-dirs" <*>
+		v .:: "other-modules"
+
+instance ToJSON Language where
+	toJSON = toJSON . display
+
+instance FromJSON Language where
+	parseJSON = withText "language" $ \txt -> parseDT "Language" (T.unpack txt)
+
+instance ToJSON Extension where
+	toJSON = toJSON . display
+
+instance FromJSON Extension where
+	parseJSON = withText "extension" $ \txt -> parseDT "Extension" (T.unpack txt)
+
+instance Paths Info where
+	paths f (Info deps lang exts opts dirs omods) = Info deps lang exts opts <$> traverse (paths f) dirs <*> pure omods
+
+-- | Entity with project extensions
+data Extensions a = Extensions {
+	_extensions :: [Extension],
+	_ghcOptions :: [Text],
+	_entity :: a }
+		deriving (Eq, Read, Show)
+
+instance Ord a => Ord (Extensions a) where
+	compare = comparing _entity
+
+instance Functor Extensions where
+	fmap f (Extensions e o x) = Extensions e o (f x)
+
+instance Applicative Extensions where
+	pure = Extensions [] []
+	(Extensions l lo f) <*> (Extensions r ro x) = Extensions (ordNub $ l ++ r) (ordNub $ lo ++ ro) (f x)
+
+instance Foldable Extensions where
+	foldMap f (Extensions _ _ x) = f x
+
+instance Traversable Extensions where
+	traverse f (Extensions e o x) = Extensions e o <$> f x
+
+makeLenses ''Project
+makeLenses ''ProjectDescription
+makeLenses ''TargetInfo
+makeLenses ''Library
+makeLenses ''Executable
+makeLenses ''Test
+makeLenses ''Info
+makeLenses ''Extensions
+
+instance Target Library where
+	targetName _ = pure
+	buildInfo = libraryBuildInfo
+	targetMain _ = Nothing
+	targetModules lib' = lib' ^.. libraryModules . each
+
+instance Target Executable where
+	targetName = executableName
+	buildInfo = executableBuildInfo
+	targetMain exe' = Just $ exe' ^. executablePath
+	targetModules _ = []
+
+instance Target Test where
+	targetName = testName
+	buildInfo = testBuildInfo
+	targetMain test' = fmap toPath (test' ^? testMain . _Just . path) where
+		toPath f
+			| haskellSource f = fromFilePath f
+			| otherwise = fromFilePath (f <.> "hs")
+	targetModules _ = []
+
+instance Target TargetInfo where
+	targetName = targetInfoName . _Just
+	buildInfo = targetBuildInfo
+	targetMain = _targetInfoMain
+	targetModules = _targetInfoModules
src/HsDev/Sandbox.hs view
@@ -1,161 +1,185 @@-{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}--module HsDev.Sandbox (-	SandboxType(..), Sandbox(..), sandboxType, sandbox,-	isSandbox, guessSandboxType, sandboxFromPath,-	findSandbox, searchSandbox, projectSandbox, sandboxPackageDbStack, searchPackageDbStack, restorePackageDbStack,--	-- * cabal-sandbox util-	cabalSandboxLib, cabalSandboxPackageDb,--	getModuleOpts-	) where--import Control.Applicative-import Control.Arrow-import Control.DeepSeq (NFData(..))-import Control.Monad.Trans.Maybe-import Control.Monad.Except-import Control.Lens (view, makeLenses)-import Data.Aeson-import Data.List (find)-import Data.Maybe (isJust, fromMaybe)-import qualified Data.Text as T (unpack)-import Distribution.Compiler-import Distribution.System-import qualified Distribution.Text as T (display)-import System.FilePath-import System.Directory-import System.Log.Simple (MonadLog(..))--import System.Directory.Paths-import HsDev.PackageDb-import HsDev.Scan.Browse (withPackages, browsePackages)-import HsDev.Stack-import HsDev.Symbols (moduleOpts)-import HsDev.Symbols.Types (Module(..), ModuleLocation(..), moduleLocation)-import HsDev.Tools.Ghc.Compat as Compat-import HsDev.Util (searchPath, directoryContents, cabalFile)--import qualified Packages as GHC--data SandboxType = CabalSandbox | StackWork deriving (Eq, Ord, Read, Show, Enum, Bounded)--data Sandbox = Sandbox { _sandboxType :: SandboxType, _sandbox :: FilePath } deriving (Eq, Ord)--makeLenses ''Sandbox--instance NFData SandboxType where-	rnf CabalSandbox = ()-	rnf StackWork = ()--instance NFData Sandbox where-	rnf (Sandbox t p) = rnf t `seq` rnf p--instance Show Sandbox where-	show (Sandbox _ p) = p--instance ToJSON Sandbox where-	toJSON (Sandbox _ p) = toJSON p--instance FromJSON Sandbox where-	parseJSON = withText "sandbox" sandboxPath where-		sandboxPath = maybe (fail "Not a sandbox") return . sandboxFromPath . T.unpack--instance Paths Sandbox where-	paths f (Sandbox st p) = Sandbox st <$> f p--isSandbox :: FilePath -> Bool-isSandbox = isJust . guessSandboxType--guessSandboxType :: FilePath -> Maybe SandboxType-guessSandboxType fpath-	| takeFileName fpath == ".cabal-sandbox" = Just CabalSandbox-	| takeFileName fpath == ".stack-work" = Just StackWork-	| otherwise = Nothing--sandboxFromPath :: FilePath -> Maybe Sandbox-sandboxFromPath fpath = Sandbox <$> guessSandboxType fpath <*> pure fpath---- | Find sandbox in path-findSandbox :: FilePath -> IO (Maybe Sandbox)-findSandbox fpath = do-	fpath' <- canonicalize fpath-	isDir <- doesDirectoryExist fpath'-	if isDir-		then do-			dirs <- liftM (fpath' :) $ directoryContents fpath'-			return $ msum $ map sandboxFromDir dirs-		else return Nothing-	where-		sandboxFromDir :: FilePath -> Maybe Sandbox-		sandboxFromDir fdir-			| takeFileName fdir == "stack.yaml" = sandboxFromPath (takeDirectory fdir </> ".stack-work")-			| otherwise = sandboxFromPath fdir---- | Search sandbox by parent directory-searchSandbox :: FilePath -> IO (Maybe Sandbox)-searchSandbox p = runMaybeT $ searchPath p (MaybeT . findSandbox)---- | Get project sandbox: search up for .cabal, then search for stack.yaml in current directory and cabal sandbox in current + parents-projectSandbox :: FilePath -> IO (Maybe Sandbox)-projectSandbox fpath = runMaybeT $ do-	p <- searchPath fpath (MaybeT . getCabalFile)-	MaybeT (findSandbox p) <|> searchPath p (MaybeT . findSbox')-	where-		getCabalFile = directoryContents >=> return . find cabalFile-		findSbox' = directoryContents >=> return . msum . map sandboxFromPath---- | Get package-db stack for sandbox-sandboxPackageDbStack :: MonadLog m => Sandbox -> m PackageDbStack-sandboxPackageDbStack (Sandbox CabalSandbox fpath) = do-	dir <- cabalSandboxPackageDb-	return $ PackageDbStack [PackageDb $ fpath </> dir]-sandboxPackageDbStack (Sandbox StackWork fpath) = liftM (view stackPackageDbStack) $ projectEnv $ takeDirectory fpath---- | Search package-db stack with user-db as default-searchPackageDbStack :: MonadLog m => FilePath -> m PackageDbStack-searchPackageDbStack p = do-	mbox <- liftIO $ projectSandbox p-	case mbox of-		Nothing -> return userDb-		Just sbox -> sandboxPackageDbStack sbox---- | Restore package-db stack by package-db-restorePackageDbStack :: MonadLog m => PackageDb -> m PackageDbStack-restorePackageDbStack GlobalDb = return globalDb-restorePackageDbStack UserDb = return userDb-restorePackageDbStack (PackageDb p) = liftM (fromMaybe $ fromPackageDb p) $ runMaybeT $ do-	sbox <- MaybeT $ liftIO $ searchSandbox p-	lift $ sandboxPackageDbStack sbox---- | Get actual sandbox build path: <arch>-<platform>-<compiler>-<version>-cabalSandboxLib :: MonadLog m => m FilePath-cabalSandboxLib = do-	res <- withPackages ["-no-user-package-db"] $-		return .-		map (GHC.packageNameString &&& GHC.packageVersion) .-		fromMaybe [] .-		Compat.pkgDatabase-	let-		compiler = T.display buildCompilerFlavor-		CompilerId _ version = buildCompilerId-		ver = maybe (T.display version) T.display $ lookup compiler res-	return $ T.display buildPlatform ++ "-" ++ compiler ++ "-" ++ ver---- | Get sandbox package-db: <arch>-<platform>-<compiler>-<version>-packages.conf.d-cabalSandboxPackageDb :: MonadLog m => m FilePath-cabalSandboxPackageDb = liftM (++ "-packages.conf.d") cabalSandboxLib---- | Options for GHC for module and project-getModuleOpts :: MonadLog m => [String] -> Module -> m [String]-getModuleOpts opts m = do-	pdbs <- case view moduleLocation m of-		FileModule fpath _ -> searchPackageDbStack fpath-		InstalledModule pdb _ _ -> restorePackageDbStack pdb-		ModuleSource _ -> return userDb-	pkgs <- browsePackages opts pdbs-	return $ concat [-		packageDbStackOpts pdbs,-		moduleOpts pkgs m,-		opts]+{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
+
+module HsDev.Sandbox (
+	SandboxType(..), Sandbox(..), sandboxType, sandbox,
+	isSandbox, guessSandboxType, sandboxFromPath,
+	findSandbox, searchSandbox, projectSandbox, sandboxPackageDbStack, searchPackageDbStack, restorePackageDbStack,
+
+	-- * cabal-sandbox util
+	cabalSandboxLib, cabalSandboxPackageDb,
+
+	getModuleOpts, getProjectTargetOpts,
+
+	getProjectSandbox,
+	getProjectPackageDbStack
+	) where
+
+import Control.Applicative
+import Control.Arrow
+import Control.DeepSeq (NFData(..))
+import Control.Monad
+import Control.Monad.Trans.Maybe
+import Control.Monad.Except
+import Control.Lens (view, makeLenses)
+import Data.Aeson
+import Data.List (find)
+import Data.Maybe (isJust, fromMaybe)
+import qualified Data.Text as T (unpack)
+import Distribution.Compiler
+import Distribution.System
+import qualified Distribution.Text as T (display)
+import System.FilePath
+import System.Log.Simple (MonadLog(..))
+
+import System.Directory.Paths
+import HsDev.PackageDb
+import HsDev.Project.Types
+import HsDev.Scan.Browse (browsePackages)
+import HsDev.Stack hiding (path)
+import HsDev.Symbols (moduleOpts, projectTargetOpts)
+import HsDev.Symbols.Types (moduleId, Module(..), ModuleLocation(..), moduleLocation)
+import HsDev.Tools.Ghc.Worker (GhcM, tmpSession)
+import HsDev.Tools.Ghc.Compat as Compat
+import HsDev.Util (searchPath, directoryContents, cabalFile)
+
+import qualified GHC
+import qualified Packages as GHC
+
+data SandboxType = CabalSandbox | StackWork deriving (Eq, Ord, Read, Show, Enum, Bounded)
+
+data Sandbox = Sandbox { _sandboxType :: SandboxType, _sandbox :: Path } deriving (Eq, Ord)
+
+makeLenses ''Sandbox
+
+instance NFData SandboxType where
+	rnf CabalSandbox = ()
+	rnf StackWork = ()
+
+instance NFData Sandbox where
+	rnf (Sandbox t p) = rnf t `seq` rnf p
+
+instance Show Sandbox where
+	show (Sandbox _ p) = T.unpack p
+
+instance ToJSON Sandbox where
+	toJSON (Sandbox _ p) = toJSON p
+
+instance FromJSON Sandbox where
+	parseJSON = withText "sandbox" sandboxPath where
+		sandboxPath = maybe (fail "Not a sandbox") return . sandboxFromPath
+
+instance Paths Sandbox where
+	paths f (Sandbox st p) = Sandbox st <$> paths f p
+
+isSandbox :: Path -> Bool
+isSandbox = isJust . guessSandboxType
+
+guessSandboxType :: Path -> Maybe SandboxType
+guessSandboxType fpath
+	| takeFileName (view path fpath) == ".cabal-sandbox" = Just CabalSandbox
+	| takeFileName (view path fpath) == ".stack-work" = Just StackWork
+	| otherwise = Nothing
+
+sandboxFromPath :: Path -> Maybe Sandbox
+sandboxFromPath fpath = Sandbox <$> guessSandboxType fpath <*> pure fpath
+
+-- | Find sandbox in path
+findSandbox :: Path -> IO (Maybe Sandbox)
+findSandbox fpath = do
+	fpath' <- canonicalize fpath
+	isDir <- dirExists fpath'
+	if isDir
+		then do
+			dirs <- liftM ((fpath' :) . map fromFilePath) $ directoryContents (view path fpath')
+			return $ msum $ map sandboxFromDir dirs
+		else return Nothing
+	where
+		sandboxFromDir :: Path -> Maybe Sandbox
+		sandboxFromDir fdir
+			| takeFileName (view path fdir) == "stack.yaml" = sandboxFromPath (fromFilePath (takeDirectory (view path fdir) </> ".stack-work"))
+			| otherwise = sandboxFromPath fdir
+
+-- | Search sandbox by parent directory
+searchSandbox :: Path -> IO (Maybe Sandbox)
+searchSandbox p = runMaybeT $ searchPath (view path p) (MaybeT . findSandbox . fromFilePath)
+
+-- | Get project sandbox: search up for .cabal, then search for stack.yaml in current directory and cabal sandbox in current + parents
+projectSandbox :: Path -> IO (Maybe Sandbox)
+projectSandbox fpath = runMaybeT $ do
+	p <- searchPath (view path fpath) (MaybeT . getCabalFile)
+	MaybeT (findSandbox $ fromFilePath p) <|> searchPath p (MaybeT . findSbox')
+	where
+		getCabalFile = directoryContents >=> return . find cabalFile
+		findSbox' = directoryContents >=> return . msum . map (sandboxFromPath . fromFilePath)
+
+-- | Get package-db stack for sandbox
+sandboxPackageDbStack :: Sandbox -> GhcM PackageDbStack
+sandboxPackageDbStack (Sandbox CabalSandbox fpath) = do
+	dir <- cabalSandboxPackageDb
+	return $ PackageDbStack [PackageDb $ fromFilePath $ view path fpath </> dir]
+sandboxPackageDbStack (Sandbox StackWork fpath) = liftM (view stackPackageDbStack) $ projectEnv $ takeDirectory (view path fpath)
+
+-- | Search package-db stack with user-db as default
+searchPackageDbStack :: Path -> GhcM PackageDbStack
+searchPackageDbStack p = do
+	mbox <- liftIO $ projectSandbox p
+	case mbox of
+		Nothing -> return userDb
+		Just sbox -> sandboxPackageDbStack sbox
+
+-- | Restore package-db stack by package-db
+restorePackageDbStack :: PackageDb -> GhcM PackageDbStack
+restorePackageDbStack GlobalDb = return globalDb
+restorePackageDbStack UserDb = return userDb
+restorePackageDbStack (PackageDb p) = liftM (fromMaybe $ fromPackageDbs [p]) $ runMaybeT $ do
+	sbox <- MaybeT $ liftIO $ searchSandbox p
+	lift $ sandboxPackageDbStack sbox
+
+-- | Get actual sandbox build path: <arch>-<platform>-<compiler>-<version>
+cabalSandboxLib :: GhcM FilePath
+cabalSandboxLib = do
+	tmpSession ["-no-user-package-db"]
+	df <- GHC.getSessionDynFlags
+	let
+		res =
+			map (GHC.packageNameString &&& GHC.packageVersion) .
+			fromMaybe [] . Compat.pkgDatabase $ df
+		compiler = T.display buildCompilerFlavor
+		CompilerId _ version = buildCompilerId
+		ver = maybe (T.display version) T.display $ lookup compiler res
+	return $ T.display buildPlatform ++ "-" ++ compiler ++ "-" ++ ver
+
+-- | Get sandbox package-db: <arch>-<platform>-<compiler>-<version>-packages.conf.d
+cabalSandboxPackageDb :: GhcM FilePath
+cabalSandboxPackageDb = liftM (++ "-packages.conf.d") cabalSandboxLib
+
+-- | Options for GHC for module and project
+getModuleOpts :: [String] -> Module -> GhcM [String]
+getModuleOpts opts m = do
+	pdbs <- case view (moduleId . moduleLocation) m of
+		FileModule fpath _ -> searchPackageDbStack fpath
+		InstalledModule _ _ _ -> return userDb
+		_ -> return userDb
+	pkgs <- browsePackages opts pdbs
+	return $ concat [
+		packageDbStackOpts pdbs,
+		moduleOpts pkgs m,
+		opts]
+
+-- | Options for GHC for project target
+getProjectTargetOpts :: [String] -> Project -> Info -> GhcM [String]
+getProjectTargetOpts opts proj t = do
+	pdbs <- searchPackageDbStack $ view projectPath proj
+	pkgs <- browsePackages opts pdbs
+	return $ concat [
+		packageDbStackOpts pdbs,
+		projectTargetOpts pkgs proj t,
+		opts]
+
+-- | Get sandbox of project (if any)
+getProjectSandbox :: MonadLog m => Project -> m (Maybe Sandbox)
+getProjectSandbox = liftIO . projectSandbox . view projectPath
+
+-- | Get project package-db stack
+getProjectPackageDbStack :: Project -> GhcM PackageDbStack
+getProjectPackageDbStack = getProjectSandbox >=> maybe (return userDb) sandboxPackageDbStack
src/HsDev/Scan.hs view
@@ -1,209 +1,251 @@-{-# LANGUAGE FlexibleInstances #-}--module HsDev.Scan (-	-- * Enumerate functions-	CompileFlag, ModuleToScan, ProjectToScan, PackageDbToScan, ScanContents(..),-	EnumContents(..),-	enumProject, enumSandbox, enumDirectory,--	-- * Scan-	scanProjectFile,-	scanModule, scanModify, upToDate, rescanModule, changedModule, changedModules,--	-- * Reexportss-	module HsDev.Database,-	module HsDev.Symbols.Types,-	module Control.Monad.Except,-	) where--import Control.DeepSeq-import Control.Lens (view, preview, set, over, each, _Right, _1, _2, _3, (^.), (^..))-import Control.Monad.Except-import Data.Maybe (catMaybes, fromMaybe, isJust, listToMaybe)-import Data.List (intercalate)-import System.Directory-import Text.Format--import HsDev.Error-import HsDev.Scan.Browse (browsePackages, browseModules)-import HsDev.Server.Types (FileSource(..), CommandMonad(..))-import HsDev.Sandbox-import HsDev.Symbols-import HsDev.Symbols.Types-import HsDev.Database-import HsDev.Display-import HsDev.Inspect-import HsDev.Util---- | Compile flags-type CompileFlag = String--- | Module with flags ready to scan-type ModuleToScan = (ModuleLocation, [CompileFlag], Maybe String)--- | Project ready to scan-type ProjectToScan = (Project, [ModuleToScan])--- | Package-db sandbox to scan (top of stack)-type PackageDbToScan = PackageDbStack---- | Scan info-data ScanContents = ScanContents {-	modulesToScan :: [ModuleToScan],-	projectsToScan :: [ProjectToScan],-	sandboxesToScan :: [PackageDbStack] }--instance NFData ScanContents where-	rnf (ScanContents ms ps ss) = rnf ms `seq` rnf ps `seq` rnf ss--instance Monoid ScanContents where-	mempty = ScanContents [] [] []-	mappend (ScanContents lm lp ls) (ScanContents rm rp rs) = ScanContents-		(uniqueBy (view _1) $ lm ++ rm)-		(uniqueBy (view _1) $ lp ++ rp)-		(ordNub $ ls ++ rs)--instance Formattable ScanContents where-	formattable (ScanContents ms ps cs) = formattable str where-		str :: String-		str = format "modules: {}, projects: {}, package-dbs: {}"-			~~ (intercalate ", " $ ms ^.. each . _1 . moduleFile)-			~~ (intercalate ", " $ ps ^.. each . _1 . projectPath)-			~~ (intercalate ", " $ map (display . topPackageDb) $ cs ^.. each)--class EnumContents a where-	enumContents :: CommandMonad m => a -> m ScanContents--instance EnumContents ModuleLocation where-	enumContents mloc = return $ ScanContents [(mloc, [], Nothing)] [] []--instance EnumContents (Extensions ModuleLocation) where-	enumContents ex = return $ ScanContents [(view entity ex, extensionsOpts ex, Nothing)] [] []--instance EnumContents Project where-	enumContents = enumProject--instance EnumContents PackageDbStack where-	enumContents pdbs = return $ ScanContents [] [] (packageDbStacks pdbs)--instance EnumContents Sandbox where-	enumContents = enumSandbox--instance {-# OVERLAPPING #-} EnumContents a => EnumContents [a] where-	enumContents = liftM mconcat . tries . map enumContents--instance {-# OVERLAPS #-} EnumContents FilePath where-	enumContents f-		| haskellSource f = hsdevLiftIO $ do-			mproj <- liftIO $ locateProject f-			case mproj of-				Nothing -> enumContents $ FileModule f Nothing-				Just proj -> do-					ScanContents _ [(_, mods)] _ <- enumContents proj-					return $ ScanContents (filter ((== Just f) . preview (_1 . moduleFile)) mods) [] []-		| otherwise = enumDirectory f--instance EnumContents FileSource where-	enumContents (FileSource f mcts)-		| haskellSource f = do-			ScanContents [(m, opts, _)] _ _ <- enumContents f-			return $ ScanContents [(m, opts, mcts)] [] []-		| otherwise = return mempty---- | Enum project sources-enumProject :: CommandMonad m => Project -> m ScanContents-enumProject p = hsdevLiftIO $ do-	p' <- liftIO $ loadProject p-	pdbs <- searchPackageDbStack (view projectPath p')-	pkgs <- liftM (map $ view (package . packageName)) $ browsePackages [] pdbs-	let-		projOpts :: FilePath -> [String]-		projOpts f = concatMap makeOpts $ fileTargets p' f where-			makeOpts :: Info -> [String]-			makeOpts i = concat [-				["-hide-all-packages"],-				["-package " ++ view projectName p'],-				["-package " ++ dep | dep <- view infoDepends i, dep `elem` pkgs]]-	srcs <- liftIO $ projectSources p'-	let-		mlocs = over each (\src -> over ghcOptions (++ projOpts (view entity src)) . over entity (\f -> FileModule f (Just p')) $ src) srcs-	mods <- liftM modulesToScan $ enumContents mlocs-	return $ ScanContents [] [(p', mods)] [] -- (sandboxCabals sboxes)---- | Enum sandbox-enumSandbox :: CommandMonad m => Sandbox -> m ScanContents-enumSandbox = sandboxPackageDbStack >=> enumContents---- | Enum directory modules-enumDirectory :: CommandMonad m => FilePath -> m ScanContents-enumDirectory dir = hsdevLiftIO $ do-	cts <- liftIO $ traverseDirectory dir-	let-		projects = filter cabalFile cts-		sources = filter haskellSource cts-	dirs <- liftIO $ filterM doesDirectoryExist cts-	sboxes <- liftM catMaybes $ triesMap (liftIO . findSandbox) dirs-	pdbs <- mapM enumSandbox sboxes-	projs <- liftM mconcat $ triesMap (enumProject . project) projects-	let-		projPaths = map (view projectPath . fst) $ projectsToScan projs-		standalone = map (`FileModule` Nothing) $ filter (\s -> not (any (`isParent` s) projPaths)) sources-	return $ mconcat [-		ScanContents [(s, [], Nothing) | s <- standalone] [] [],-		projs,-		mconcat pdbs]---- | Scan project file-scanProjectFile :: CommandMonad m => [String] -> FilePath -> m Project-scanProjectFile _ f = hsdevLiftIO $ do-	proj <- (liftIO $ locateProject f) >>= maybe (hsdevError $ FileNotFound f) return-	liftIO $ loadProject proj---- | Scan module-scanModule :: CommandMonad m => [(String, String)] -> [String] -> ModuleLocation -> Maybe String -> m InspectedModule-scanModule defines opts (FileModule f p) mcts = hsdevLiftIO $ liftM setProj $ liftIO $ inspectFile defines opts f mcts where-	setProj =-		set (inspectedId . moduleProject) p .-		set (inspectionResult . _Right . moduleLocation . moduleProject) p-scanModule _ opts mloc@(InstalledModule c _ n) _ = hsdevLiftIO $ do-	pdbs <- getDbs c-	ims <- browseModules opts pdbs [mloc]-	maybe (hsdevError $ BrowseNoModuleInfo n) return $ listToMaybe ims-	where-		getDbs :: CommandMonad m => PackageDb -> m PackageDbStack-		getDbs = maybe (return userDb) searchPackageDbStack . preview packageDb-scanModule _ _ (ModuleSource _) _ = hsdevError $ InspectError "Can inspect only modules in file or cabal"---- | Scan additional info and modify scanned module-scanModify :: CommandMonad m => ([String] -> PackageDbStack -> Module -> m Module) -> InspectedModule -> m InspectedModule-scanModify f im = traverse f' im where-	f' m = do-		pdbs <- case view moduleLocation m of-			-- TODO: Get actual sandbox stack-			FileModule fpath _ -> searchPackageDbStack fpath-			InstalledModule pdb _ _ -> maybe (return userDb) searchPackageDbStack $ preview packageDb pdb-			_ -> return userDb-		f (fromMaybe [] $ preview (inspection . inspectionOpts) im) pdbs m---- | Is inspected module up to date?-upToDate :: [String] -> InspectedModule -> IO Bool-upToDate opts im = case view inspectedId im of-	FileModule f _ -> liftM (== view inspection im) $ fileInspection f opts-	InstalledModule _ _ _ -> return $ view inspection im == InspectionAt 0 opts-	_ -> return False---- | Rescan inspected module-rescanModule :: CommandMonad m => [(String, String)] -> [String] -> InspectedModule -> m (Maybe InspectedModule)-rescanModule defines opts im = do-	up <- liftIO $ upToDate opts im-	if up-		then return Nothing-		else fmap Just $ scanModule defines opts (view inspectedId im) Nothing---- | Is module new or recently changed-changedModule :: Database -> [String] -> ModuleLocation -> IO Bool-changedModule db opts m = maybe (return True) (liftM not . liftIO . upToDate opts) m' where-	m' = lookupInspected m db---- | Returns new (to scan) and changed (to rescan) modules-changedModules :: Database -> [String] -> [ModuleToScan] -> IO [ModuleToScan]-changedModules db opts = filterM $ \m -> if isJust (m ^. _3)-	then return True-	else changedModule db (opts ++ (m ^. _2)) (m ^. _1)+{-# LANGUAGE FlexibleInstances, TypeOperators, TypeApplications, OverloadedStrings #-}
+
+module HsDev.Scan (
+	-- * Enumerate functions
+	CompileFlag, ModuleToScan, ProjectToScan, PackageDbToScan, ScanContents(..),
+	EnumContents(..),
+	enumRescan, enumDependent, enumProject, enumSandbox, enumDirectory,
+
+	-- * Scan
+	scanProjectFile,
+	scanModify,
+	upToDate, changedModules,
+	getFileContents,
+
+	-- * Reexportss
+	module HsDev.Symbols.Types,
+	module Control.Monad.Except,
+	) where
+
+import Control.DeepSeq
+import Control.Lens
+import Control.Monad.Except
+import Data.Deps
+import Data.Maybe (catMaybes, isJust, listToMaybe)
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import Data.List (intercalate)
+import Data.Text (Text)
+import Data.Text.Lens (unpacked)
+import qualified Data.Text as T
+import Data.Time.Clock.POSIX (POSIXTime)
+import Data.Traversable (for)
+import Data.String (IsString, fromString)
+import qualified Data.Set as S
+import System.Directory
+import Text.Format
+import qualified System.Log.Simple as Log
+
+import HsDev.Error
+import qualified HsDev.Database.SQLite as SQLite
+import HsDev.Database.SQLite.Select
+import HsDev.Scan.Browse (browsePackages)
+import HsDev.Server.Types (FileSource(..), SessionMonad(..), CommandMonad(..), inSessionGhc)
+import HsDev.Sandbox
+import HsDev.Symbols
+import HsDev.Symbols.Types
+import HsDev.Display
+import HsDev.Inspect
+import HsDev.Util
+import System.Directory.Paths
+
+-- | Compile flags
+type CompileFlag = String
+-- | Module with flags ready to scan
+type ModuleToScan = (ModuleLocation, [CompileFlag], Maybe Text)
+-- | Project ready to scan
+type ProjectToScan = (Project, [ModuleToScan])
+-- | Package-db sandbox to scan (top of stack)
+type PackageDbToScan = PackageDbStack
+
+-- | Scan info
+data ScanContents = ScanContents {
+	modulesToScan :: [ModuleToScan],
+	projectsToScan :: [ProjectToScan],
+	sandboxesToScan :: [PackageDbStack] }
+
+instance NFData ScanContents where
+	rnf (ScanContents ms ps ss) = rnf ms `seq` rnf ps `seq` rnf ss
+
+instance Monoid ScanContents where
+	mempty = ScanContents [] [] []
+	mappend (ScanContents lm lp ls) (ScanContents rm rp rs) = ScanContents
+		(uniqueBy (view _1) $ lm ++ rm)
+		(uniqueBy (view _1) $ lp ++ rp)
+		(ordNub $ ls ++ rs)
+
+instance Formattable ScanContents where
+	formattable (ScanContents ms ps cs) = formattable str where
+		str :: String
+		str = format "modules: {}, projects: {}, package-dbs: {}"
+			~~ (T.intercalate comma $ ms ^.. each . _1 . moduleFile)
+			~~ (T.intercalate comma $ ps ^.. each . _1 . projectPath)
+			~~ (intercalate comma $ map (display . topPackageDb) $ cs ^.. each)
+		comma :: IsString s => s
+		comma = fromString ", "
+
+class EnumContents a where
+	enumContents :: CommandMonad m => a -> m ScanContents
+
+instance EnumContents ModuleLocation where
+	enumContents mloc = return $ ScanContents [(mloc, [], Nothing)] [] []
+
+instance EnumContents (Extensions ModuleLocation) where
+	enumContents ex = return $ ScanContents [(view entity ex, extensionsOpts ex, Nothing)] [] []
+
+instance EnumContents Project where
+	enumContents = enumProject
+
+instance EnumContents PackageDbStack where
+	enumContents pdbs = return $ ScanContents [] [] (packageDbStacks pdbs)
+
+instance EnumContents Sandbox where
+	enumContents = enumSandbox
+
+instance {-# OVERLAPPABLE #-} EnumContents a => EnumContents [a] where
+	enumContents = liftM mconcat . tries . map enumContents
+
+instance {-# OVERLAPPING #-} EnumContents FilePath where
+	enumContents f
+		| haskellSource f = hsdevLiftIO $ do
+			mproj <- liftIO $ locateProject f
+			case mproj of
+				Nothing -> enumContents $ FileModule (fromFilePath f) Nothing
+				Just proj -> do
+					ScanContents _ [(_, mods)] _ <- enumContents proj
+					return $ ScanContents (filter ((== Just f) . preview (_1 . moduleFile . path)) mods) [] []
+		| otherwise = enumDirectory f
+
+instance {-# OVERLAPPING #-} EnumContents Path where
+	enumContents = enumContents . view path
+
+instance EnumContents FileSource where
+	enumContents (FileSource f mcts)
+		| haskellSource (view path f) = do
+			ScanContents [(m, opts, _)] _ _ <- enumContents f
+			return $ ScanContents [(m, opts, mcts)] [] []
+		| otherwise = return mempty
+
+-- | Enum rescannable (i.e. already scanned) file
+enumRescan :: CommandMonad m => FilePath -> m ScanContents
+enumRescan fpath = Log.scope "enum-rescan" $ do
+	ms <- SQLite.query @_ @(ModuleLocation SQLite.:. Inspection)
+		(toQuery $ qModuleLocation `mappend` select_ ["ml.inspection_time", "ml.inspection_opts"] [] [] `mappend` where_ ["ml.file == ?"]) (SQLite.Only fpath)
+	case ms of
+		[] -> do
+			Log.sendLog Log.Warning $ "file {} not found" ~~ fpath
+			return mempty
+		((mloc SQLite.:. insp):_) -> do
+			when (length ms > 1) $ Log.sendLog Log.Warning $ "several modules with file == {} found, taking first one" ~~ fpath
+			return $ ScanContents [(mloc, insp ^.. inspectionOpts . each . unpacked, Nothing)] [] []
+
+-- | Enum file dependent
+enumDependent :: CommandMonad m => FilePath -> m ScanContents
+enumDependent fpath = Log.scope "enum-dependent" $ do
+	ms <- SQLite.query @_ @ModuleId
+		(toQuery $ qModuleId `mappend` where_ ["mu.file == ?"]) (SQLite.Only fpath)
+	case ms of
+		[] -> do
+			Log.sendLog Log.Warning $ "file {} not found" ~~ fpath
+			return mempty
+		(mid:_) -> do
+			when (length ms > 1) $ Log.sendLog Log.Warning $ "several modules with file == {} found, taking first one" ~~ fpath
+			let
+				mcabal = mid ^? moduleLocation . moduleProject . _Just . projectCabal
+			depList <- SQLite.query @_ @(Path, Path) "select d.module_file, d.depends_file from sources_depends as d, projects_modules_scope as ps where ps.cabal is ? and ps.module_id == d.module_id;"
+				(SQLite.Only mcabal)
+			let
+				rdeps = inverse . either (const mempty) id . flatten . mconcat . map (uncurry dep) $ depList
+				dependent = rdeps ^. ix (fromFilePath fpath)
+			liftM mconcat $ mapM (enumRescan . view path) dependent
+
+-- | Enum project sources
+enumProject :: CommandMonad m => Project -> m ScanContents
+enumProject p = hsdevLiftIO $ do
+	p' <- liftIO $ loadProject p
+	pdbs <- inSessionGhc $ searchPackageDbStack (view projectPath p')
+	pkgs <- inSessionGhc $ liftM (S.fromList . map (view (package . packageName))) $ browsePackages [] pdbs
+	let
+		projOpts :: Path -> [Text]
+		projOpts f = map fromString $ concatMap makeOpts $ fileTargets p' f where
+			makeOpts :: Info -> [String]
+			makeOpts i = concat [
+				["-hide-all-packages"],
+				["-package " ++ view (projectName . path) p'],
+				["-package " ++ T.unpack dep' | dep' <- view infoDepends i, dep' `S.member` pkgs]]
+	srcs <- liftIO $ projectSources p'
+	let
+		mlocs = over each (\src -> over ghcOptions (++ projOpts (view entity src)) . over entity (\f -> FileModule f (Just p')) $ src) srcs
+	mods <- liftM modulesToScan $ enumContents mlocs
+	return $ ScanContents [] [(p', mods)] [] -- (sandboxCabals sboxes)
+
+-- | Enum sandbox
+enumSandbox :: CommandMonad m => Sandbox -> m ScanContents
+enumSandbox = (inSessionGhc . sandboxPackageDbStack) >=> enumContents
+
+-- | Enum directory modules
+enumDirectory :: CommandMonad m => FilePath -> m ScanContents
+enumDirectory dir = hsdevLiftIO $ do
+	cts <- liftIO $ traverseDirectory dir
+	let
+		projects = filter cabalFile cts
+		sources = filter haskellSource cts
+	dirs <- liftIO $ filterM doesDirectoryExist cts
+	sboxes <- liftM catMaybes $ triesMap (liftIO . findSandbox . fromFilePath) dirs
+	pdbs <- mapM enumSandbox sboxes
+	projs <- liftM mconcat $ triesMap (enumProject . project) projects
+	let
+		projPaths = map (view projectPath . fst) $ projectsToScan projs
+		standalone = map (`FileModule` Nothing) $ filter (\s -> not (any (`isParent` s) projPaths)) $ map fromFilePath sources
+	return $ mconcat [
+		ScanContents [(s, [], Nothing) | s <- standalone] [] [],
+		projs,
+		mconcat pdbs]
+
+-- | Scan project file
+scanProjectFile :: CommandMonad m => [String] -> Path -> m Project
+scanProjectFile _ f = hsdevLiftIO $ do
+	proj <- (liftIO $ locateProject (view path f)) >>= maybe (hsdevError $ FileNotFound f) return
+	liftIO $ loadProject proj
+
+-- | Scan additional info and modify scanned module
+scanModify :: CommandMonad m => ([String] -> Module -> m Module) -> InspectedModule -> m InspectedModule
+scanModify f im = traverse f' im where
+	f' m = f (toListOf (inspection . inspectionOpts . each . unpacked) im) m
+
+-- | Is inspected module up to date?
+upToDate :: SessionMonad m => ModuleLocation -> [String] -> Inspection -> m Bool
+upToDate mloc opts insp = do
+	insp' <- liftIO $ moduleInspection mloc opts
+	mfinsp <- fmap join $ for (mloc ^? moduleFile) $ \fpath -> do
+		tm <- SQLite.query @_ @(SQLite.Only Double) "select mtime from file_contents where file = ?;" (SQLite.Only fpath)
+		return $ fmap (fileContentsInspection_ opts . fromRational . toRational . SQLite.fromOnly) (listToMaybe tm)
+	let
+		lastInsp = maybe insp' (max insp') mfinsp
+	return $ fresh insp lastInsp
+
+-- | Returns new (to scan) and changed (to rescan) modules
+changedModules :: SessionMonad m => Map ModuleLocation Inspection -> [String] -> [ModuleToScan] -> m [ModuleToScan]
+changedModules inspMap opts = filterM $ \m -> if isJust (m ^. _3)
+	then return True
+	else maybe
+		(return True)
+		(liftM not . upToDate (m ^. _1) (opts ++ (m ^. _2)))
+		(M.lookup (m ^. _1) inspMap)
+
+-- | Returns file contents if it was set and still actual
+getFileContents :: SessionMonad m => Path -> m (Maybe (POSIXTime, Text))
+getFileContents fpath = do
+	mcts <- SQLite.query @_ @(Double, Text) "select mtime, contents from file_contents where file = ?;" (SQLite.Only fpath)
+	insp <- liftIO $ fileInspection fpath []
+	case listToMaybe mcts of
+		Nothing -> return Nothing
+		Just (tm, cts) -> do
+			let
+				tm' = fromRational (toRational tm)
+			fmtime <- maybe (hsdevError $ OtherError "impossible: inspection time not set after call to `fileInspection`") return $ insp ^? inspectionAt
+			if fmtime < tm'
+				then return (Just (tm', cts))
+				else do
+					SQLite.execute "delete from file_contents where file = ?;" (SQLite.Only fpath)
+					return Nothing
src/HsDev/Scan/Browse.hs view
@@ -1,248 +1,245 @@-module HsDev.Scan.Browse (-	-- * List all packages-	browsePackages, browsePackagesDeps,-	-- * Scan cabal modules-	listModules, browseModules, browse, browseDb,-	-- * Helpers-	withPackages, withPackages_,-	readPackage, readPackageConfig, ghcPackageDb, ghcModuleLocation,-	packageDbCandidate, packageDbCandidate_,-	packageConfigs, packageDbModules, lookupModule_,--	module Control.Monad.Except-	) where--import Control.Arrow-import Control.Lens (view, preview, _Just)-import Control.Monad.Catch (MonadCatch, catch, SomeException)-import Control.Monad.Except-import Data.List (isPrefixOf)-import Data.Maybe-import Data.String (fromString)-import Data.Version-import System.Directory-import System.FilePath-import System.Log.Simple.Monad (MonadLog)--import Data.Deps-import HsDev.PackageDb-import HsDev.Symbols-import HsDev.Error-import HsDev.Tools.Base (inspect)-import HsDev.Tools.Ghc.Worker (GhcM, runGhcM, SessionTarget(..), workerSession)-import HsDev.Tools.Ghc.Compat as Compat-import HsDev.Util (ordNub)--import qualified ConLike as GHC-import qualified DataCon as GHC-import qualified DynFlags as GHC-import qualified GHC-import qualified GHC.PackageDb as GHC-import qualified GhcMonad as GHC (liftIO)-import GhcMonad (GhcMonad)-import qualified GHC.Paths as GHC-import qualified Name as GHC-import qualified Outputable as GHC-import qualified Packages as GHC-import qualified TyCon as GHC-import qualified Type as GHC-import qualified Var as GHC-import qualified Pretty---- | Browse packages-browsePackages :: MonadLog m => [String] -> PackageDbStack -> m [PackageConfig]-browsePackages opts dbs = withPackages_ (packageDbStackOpts dbs ++ opts) $-	liftM (map readPackageConfig) packageConfigs---- | Get packages with deps-browsePackagesDeps :: MonadLog m => [String] -> PackageDbStack -> m (Deps PackageConfig)-browsePackagesDeps opts dbs = withPackages (packageDbStackOpts dbs ++ opts) $ \df -> do-	cfgs <- packageConfigs-	return $ mapDeps (toPkg df) $ mconcat $ map (uncurry deps) $-		map (Compat.unitId &&& Compat.depends df) cfgs-	where-		toPkg df' = readPackageConfig . getPackageDetails df'--listModules :: MonadLog m => [String] -> PackageDbStack -> m [ModuleLocation]-listModules opts dbs = withPackages_ (packageDbStackOpts dbs ++ opts) $ do-	ms <- packageDbModules-	pdbs <- mapM (liftIO . ghcPackageDb . fst) ms-	return [ghcModuleLocation pdb p m | (pdb, (p, m)) <- zip pdbs ms]--browseModules :: MonadLog m => [String] -> PackageDbStack -> [ModuleLocation] -> m [InspectedModule]-browseModules opts dbs mlocs = withPackages_ (packageDbStackOpts dbs ++ opts) $ do-	ms <- packageDbModules-	pdbs <- mapM (liftIO . ghcPackageDb . fst) ms-	liftM catMaybes $ sequence [browseModule' pdb p m | (pdb, (p, m)) <- zip pdbs ms, ghcModuleLocation pdb p m `elem` mlocs]-	where-		browseModule' :: PackageDb -> GHC.PackageConfig -> GHC.Module -> GhcM (Maybe InspectedModule)-		browseModule' pdb p m = tryT $ inspect (ghcModuleLocation pdb p m) (return $ InspectionAt 0 opts) (browseModule pdb p m)---- | Browse modules, if third argument is True - browse only modules in top of package-db stack-browse :: MonadLog m => [String] -> PackageDbStack -> m [InspectedModule]-browse opts dbs = listModules opts dbs >>= browseModules opts dbs---- | Browse modules in top of package-db stack-browseDb :: MonadLog m => [String] -> PackageDbStack -> m [InspectedModule]-browseDb opts dbs = listModules opts dbs >>= browseModules opts dbs . filter inTop where-	inTop = (== Just (topPackageDb dbs)) . preview modulePackageDb--browseModule :: PackageDb -> GHC.PackageConfig -> GHC.Module -> GhcM Module-browseModule pdb package' m = do-	df <- GHC.getSessionDynFlags-	mi <- GHC.getModuleInfo m >>= maybe (hsdevError $ BrowseNoModuleInfo thisModule) return-	ds <- mapM (toDecl df mi) (GHC.modInfoExports mi)-	return Module {-		_moduleName = fromString thisModule,-		_moduleDocs = Nothing,-		_moduleLocation = thisLoc df,-		_moduleExports = Just [ExportName Nothing (view declarationName d) ThingNothing | d <- ds],-		_moduleImports = [import_ iname | iname <- ordNub (mapMaybe (preview definedModule) ds), iname /= fromString thisModule],-		_moduleDeclarations = sortDeclarations ds }-	where-		thisModule = GHC.moduleNameString (GHC.moduleName m)-		thisLoc df = view moduleIdLocation $ mloc df m-		mloc df m' = ModuleId (fromString mname') $-			ghcModuleLocation pdb (fromMaybe package' $ GHC.lookupPackage df (moduleUnitId m')) m'-			where-				mname' = GHC.moduleNameString $ GHC.moduleName m'-		toDecl df minfo n = do-			tyInfo <- GHC.modInfoLookupName minfo n-			tyResult <- maybe (inModuleSource n) (return . Just) tyInfo-			dflag <- GHC.getSessionDynFlags-			let-				decl' = decl (fromString $ GHC.getOccString n) $ fromMaybe-					(Function Nothing [] Nothing)-					(tyResult >>= showResult dflag)-			return $ decl' `definedIn` mloc df (GHC.nameModule n)-		definedModule = declarationDefined . _Just . moduleIdName-		showResult :: GHC.DynFlags -> GHC.TyThing -> Maybe DeclarationInfo-		showResult dflags (GHC.AnId i) = Just $ Function (Just $ fromString $ formatType dflags GHC.varType i) [] Nothing-		showResult dflags (GHC.AConLike c) = case c of-			GHC.RealDataCon d -> Just $ Function (Just $ fromString $ formatType dflags GHC.dataConRepType d) [] Nothing-			GHC.PatSynCon p -> Just $ Function (Just $ fromString $ formatType dflags patSynType p) [] Nothing-		showResult _ (GHC.ATyCon t) = Just $ tcon $ TypeInfo Nothing (map (fromString . GHC.getOccString) $ GHC.tyConTyVars t) Nothing [] where-			tcon-				| GHC.isAlgTyCon t && not (GHC.isNewTyCon t) && not (GHC.isClassTyCon t) = Data-				| GHC.isNewTyCon t = NewType-				| GHC.isClassTyCon t = Class-				| GHC.isTypeSynonymTyCon t = Type-				| otherwise = Type-		showResult _ _ = Nothing--withInitializedPackages :: MonadLog m => [String] -> (GHC.DynFlags -> GhcM a) -> m a-withInitializedPackages ghcOpts cont = runGhcM (Just GHC.libdir) $ do-	workerSession $ SessionGhc ghcOpts-	fs <- GHC.getSessionDynFlags-	cleanupHandler fs $ do-		(fs', _, _) <- GHC.parseDynamicFlags fs (map GHC.noLoc ghcOpts)-		_ <- GHC.setSessionDynFlags fs'-		(result, _) <- GHC.liftIO $ GHC.initPackages fs'-		cont result--withPackages :: MonadLog m => [String] -> (GHC.DynFlags -> GhcM a) -> m a-withPackages ghcOpts cont = withInitializedPackages ghcOpts cont--withPackages_ :: MonadLog m => [String] -> GhcM a -> m a-withPackages_ ghcOpts act = withPackages ghcOpts (const act)--inModuleSource :: GhcMonad m => GHC.Name -> m (Maybe GHC.TyThing)-inModuleSource nm = GHC.getModuleInfo (GHC.nameModule nm) >> GHC.lookupGlobalName nm--formatType :: GHC.DynFlags -> (a -> GHC.Type) -> a -> String-formatType dflag f x = showOutputable dflag (removeForAlls $ f x)--removeForAlls :: GHC.Type -> GHC.Type-removeForAlls ty = removeForAlls' ty' tty' where-	ty'  = GHC.dropForAlls ty-	tty' = GHC.splitFunTy_maybe ty'--removeForAlls' :: GHC.Type -> Maybe (GHC.Type, GHC.Type) -> GHC.Type-removeForAlls' ty Nothing = ty-removeForAlls' ty (Just (pre, ftype))-	| GHC.isPredTy pre = GHC.mkFunTy pre (GHC.dropForAlls ftype)-	| otherwise = ty--showOutputable :: GHC.Outputable a => GHC.DynFlags -> a -> String-showOutputable dflag = unwords . lines . showUnqualifiedPage dflag . GHC.ppr--showUnqualifiedPage :: GHC.DynFlags -> GHC.SDoc -> String-showUnqualifiedPage dflag = renderStyle Pretty.LeftMode 0 . GHC.withPprStyleDoc dflag (Compat.unqualStyle dflag)--tryT :: MonadCatch m => m a -> m (Maybe a)-tryT act = catch (liftM Just act) (const (return Nothing) . (id :: SomeException -> SomeException))--readPackage :: GHC.PackageConfig -> ModulePackage-readPackage pc = ModulePackage (GHC.packageNameString pc) (showVersion (GHC.packageVersion pc))--readPackageConfig :: GHC.PackageConfig -> PackageConfig-readPackageConfig pc = PackageConfig-	(readPackage pc)-	(map (fromString . GHC.moduleNameString . Compat.exposedModuleName) $ GHC.exposedModules pc)-	(GHC.exposed pc)--ghcModuleLocation :: PackageDb -> GHC.PackageConfig -> GHC.Module -> ModuleLocation-ghcModuleLocation pdb p m = InstalledModule pdb (Just $ readPackage p) (GHC.moduleNameString $ GHC.moduleName m)--ghcPackageDb :: GHC.PackageConfig -> IO PackageDb-ghcPackageDb = maybe (return GlobalDb) packageDbCandidate_ . listToMaybe . GHC.libraryDirs---- | Get package-db for package library directory--- Haskish way--- global-db - library is in <path>/lib/<package> and there exists <path>/lib/package.conf.d--- user-db - library is in cabal user directory--- package-db---   cabal-sandbox - library in .../.cabal-sandbox/.../<platform-ghc-ver>/<package>---     then package-db is .../.cabal-sandbox/<platform-ghc-ver>-package.conf.d---   stack (snapshots or .stack-work) - library in <path>/lib/<platform-ghc-ver>/<package>---     then package-db is <path>/pkgdb-packageDbCandidate :: FilePath -> IO (Maybe PackageDb)-packageDbCandidate fpath = liftM Just (msum [global', user', sandbox', stack']) `mplus` return Nothing where-	global' = do-		guard (takeFileName (takeDirectory fpath') == "lib")-		guardExist (takeDirectory fpath' </> "package.conf.d")-		return GlobalDb-	user' = do-		cabalDir <- getAppUserDataDirectory "cabal"-		guard (splitDirectories cabalDir `isPrefixOf` splitDirectories fpath')-		return UserDb-	sandbox' = do-		guard (".cabal-sandbox" `elem` splitDirectories fpath')-		let-			sandboxPath = joinPath $ reverse $ dropWhile (/= ".cabal-sandbox") $ reverse $ splitDirectories fpath'-			platform = takeFileName (takeDirectory fpath')-			dbPath = sandboxPath </> (platform ++ "-packages.conf.d")-		guardExist dbPath-		return $ PackageDb dbPath-	stack' = do-		guard (takeFileName (takeDirectory (takeDirectory fpath')) == "lib")-		let-			pkgDb = takeDirectory (takeDirectory $ takeDirectory fpath') </> "pkgdb"-		guardExist pkgDb-		return $ PackageDb pkgDb-	guardExist = doesDirectoryExist >=> guard-	fpath' = normalise fpath---- | Use global as default-packageDbCandidate_ :: FilePath -> IO PackageDb-packageDbCandidate_ = packageDbCandidate >=> maybe (return GlobalDb) return--packageConfigs :: GhcM [GHC.PackageConfig]-packageConfigs = liftM (fromMaybe [] . pkgDatabase) GHC.getSessionDynFlags--packageDbModules :: GhcM [(GHC.PackageConfig, GHC.Module)]-packageDbModules = do-	pkgs <- packageConfigs-	dflags <- GHC.getSessionDynFlags-	return [(p, m) |-		p <- pkgs,-		mn <- map Compat.exposedModuleName (GHC.exposedModules p),-		m <- lookupModule_ dflags mn]---- Lookup module everywhere-lookupModule_ :: GHC.DynFlags -> GHC.ModuleName -> [GHC.Module]-lookupModule_ d mn = case GHC.lookupModuleWithSuggestions d mn Nothing of-	GHC.LookupFound m' _ -> [m']-	GHC.LookupMultiple ms -> map fst ms-	GHC.LookupHidden ls rs -> map fst $ ls ++ rs-	GHC.LookupNotFound _ -> []+module HsDev.Scan.Browse (
+	-- * List all packages
+	browsePackages, browsePackagesDeps,
+	-- * Scan cabal modules
+	listModules,
+	browseModules, browseModules',
+	-- * Helpers
+	readPackage, readPackageConfig, ghcModuleLocation,
+	packageConfigs, packageDbModules, lookupModule_,
+	modulesPackages, modulesPackagesGroups, withEachPackage,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Arrow
+import Control.Lens (preview)
+import Control.Monad.Catch (MonadCatch, catch, SomeException)
+import Control.Monad.Except
+import Data.Function (on)
+import Data.List (groupBy, sort)
+import Data.Maybe
+import Data.String (fromString)
+import Data.Text (Text)
+import qualified Data.Set as S
+import Data.Version
+import Language.Haskell.Exts.Fixity
+import Language.Haskell.Exts.Syntax (Assoc(..), QName(..), Name(Ident), ModuleName(..))
+
+import Data.Deps
+import Data.LookupTable
+import HsDev.PackageDb
+import HsDev.Symbols
+import HsDev.Error
+import HsDev.Tools.Base (inspect)
+import HsDev.Tools.Ghc.Worker (GhcM, tmpSession)
+import HsDev.Tools.Ghc.Compat as Compat
+import HsDev.Util (ordNub)
+
+import qualified ConLike as GHC
+import qualified DataCon as GHC
+import qualified DynFlags as GHC
+import qualified GHC
+import qualified GHC.PackageDb as GHC
+import qualified GhcMonad as GHC (liftIO)
+import qualified Name as GHC
+import qualified IdInfo as GHC
+import qualified Outputable as GHC
+import qualified Packages as GHC
+import qualified PatSyn as GHC
+import qualified TyCon as GHC
+import qualified Type as GHC
+import qualified Var as GHC
+import qualified Pretty
+
+-- | Browse packages
+browsePackages :: [String] -> PackageDbStack -> GhcM [PackageConfig]
+browsePackages opts dbs = do
+	tmpSession (packageDbStackOpts dbs ++ opts)
+	liftM (map readPackageConfig) packageConfigs
+
+-- | Get packages with deps
+browsePackagesDeps :: [String] -> PackageDbStack -> GhcM (Deps PackageConfig)
+browsePackagesDeps opts dbs = do
+	tmpSession (packageDbStackOpts dbs ++ opts)
+	df <- GHC.getSessionDynFlags
+	cfgs <- packageConfigs
+	return $ mapDeps (toPkg df) $ mconcat [deps (Compat.unitId cfg) (Compat.depends df cfg) | cfg <- cfgs]
+	where
+		toPkg df' = readPackageConfig . getPackageDetails df'
+
+-- | List modules from ghc, accepts ghc-opts, stack of package-db to get modules for
+-- and list of packages to explicitely expose them with '-package' flag,
+-- otherwise hidden packages won't be loaded
+listModules :: [String] -> PackageDbStack -> [ModulePackage] -> GhcM [ModuleLocation]
+listModules opts dbs pkgs = do
+	tmpSession (packageDbStackOpts dbs ++ opts ++ packagesOpts)
+	ms <- packageDbModules
+	return [ghcModuleLocation p m | (p, m) <- ms]
+	where
+		packagesOpts = ["-package " ++ show p | p <- pkgs]
+
+-- | Like @browseModules@, but groups modules by package and inspects each package separately
+-- Trying to fix error: when there are several same packages (of different version), only @Module@ from
+-- one of them can be lookuped and therefore modules from different version packages won't be actually inspected
+browseModules :: [String] -> PackageDbStack -> [ModuleLocation] -> GhcM [InspectedModule]
+browseModules opts dbs mlocs = do
+	tmpSession (packageDbStackOpts dbs ++ opts)
+	liftM concat . withEachPackage (const $ browseModules' opts) $ mlocs
+
+-- | Inspect installed modules, doesn't set session and package flags!
+browseModules' :: [String] -> [ModuleLocation] -> GhcM [InspectedModule]
+browseModules' opts mlocs = do
+	ms <- packageDbModules
+	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']
+	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 $ inspect (ghcModuleLocation p m) (return $ InspectionAt 0 (map fromString opts)) (browseModule modId' sym' p m)
+		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
+	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
+	let
+		dirAssoc GHC.InfixL = AssocLeft ()
+		dirAssoc GHC.InfixR = AssocRight ()
+		dirAssoc GHC.InfixN = AssocNone ()
+		fixName o = Qual () (ModuleName () thisModule) (Ident () (GHC.occNameString o))
+	return Module {
+		_moduleId = myModId,
+		_moduleDocs = Nothing,
+		_moduleImports = [],
+		_moduleExports = ds,
+		_moduleFixities = [Fixity (dirAssoc dir) pr (fixName oname) | (oname, (pr, dir)) <- map (second Compat.getFixity) (maybe [] GHC.mi_fixities (GHC.modInfoIface mi))],
+		_moduleScope = mempty,
+		_moduleSource = Nothing }
+	where
+		thisModule = GHC.moduleNameString (GHC.moduleName m)
+		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'
+		toDecl df minfo n = do
+			tyInfo <- GHC.modInfoLookupName minfo n
+			tyResult <- maybe (GHC.lookupName n) (return . Just) tyInfo
+			declModId <- mloc df (GHC.nameModule n)
+			return Symbol {
+				_symbolId = SymbolId (fromString $ GHC.getOccString n) declModId,
+				_symbolDocs = Nothing,
+				_symbolPosition = Nothing,
+				_symbolInfo = fromMaybe (Function Nothing) (tyResult >>= showResult df) }
+		showResult :: GHC.DynFlags -> GHC.TyThing -> Maybe SymbolInfo
+		showResult dflags (GHC.AnId i) = case GHC.idDetails i of
+			GHC.RecSelId p _ -> Just $ Selector (Just $ formatType dflags $ GHC.varType i) parent ctors where
+				parent = fromString $ Compat.recSelParent p
+				ctors = map fromString $ Compat.recSelCtors p
+			GHC.ClassOpId cls -> Just $ Method (Just $ formatType dflags $ GHC.varType i) (fromString $ GHC.getOccString cls)
+			_ -> Just $ Function (Just $ formatType dflags $GHC.varType i)
+		showResult dflags (GHC.AConLike c) = case c of
+			GHC.RealDataCon d -> Just $ Constructor
+				(map (formatType dflags) $ GHC.dataConOrigArgTys d)
+				(fromString $ GHC.getOccString (GHC.dataConTyCon d))
+			GHC.PatSynCon p -> Just $ PatConstructor
+				(map (formatType dflags) $ GHC.patSynArgs p)
+				Nothing
+			-- TODO: Deal with `patSynFieldLabels` and `patSynFieldType`
+		showResult dflags (GHC.ATyCon t)
+			| GHC.isTypeSynonymTyCon t = Just $ Type args ctx
+			| GHC.isPrimTyCon t = Just $ Type [] []
+			| GHC.isNewTyCon t = Just $ NewType args ctx
+			| GHC.isDataTyCon t = Just $ Data args ctx
+			| GHC.isClassTyCon t = Just $ Class args ctx
+			| GHC.isTypeFamilyTyCon t = Just $ TypeFam args ctx Nothing
+			| GHC.isDataFamilyTyCon t = Just $ DataFam args ctx Nothing
+			| otherwise = Just $ Type [] []
+			where
+				args = map (formatType dflags . GHC.mkTyVarTy) $ GHC.tyConTyVars t
+				ctx = case GHC.tyConClass_maybe t of
+					Nothing -> []
+					Just cls -> map (formatType dflags) $ GHC.classSCTheta cls
+		showResult _ _ = Nothing
+
+formatType :: GHC.DynFlags -> GHC.Type -> Text
+formatType dflag t = fromString $ showOutputable dflag (removeForAlls t)
+
+removeForAlls :: GHC.Type -> GHC.Type
+removeForAlls ty = removeForAlls' ty' tty' where
+	ty'  = GHC.dropForAlls ty
+	tty' = GHC.splitFunTy_maybe ty'
+
+removeForAlls' :: GHC.Type -> Maybe (GHC.Type, GHC.Type) -> GHC.Type
+removeForAlls' ty Nothing = ty
+removeForAlls' ty (Just (pre, ftype))
+	| GHC.isPredTy pre = GHC.mkFunTy pre (GHC.dropForAlls ftype)
+	| otherwise = ty
+
+showOutputable :: GHC.Outputable a => GHC.DynFlags -> a -> String
+showOutputable dflag = unwords . lines . showUnqualifiedPage dflag . GHC.ppr
+
+showUnqualifiedPage :: GHC.DynFlags -> GHC.SDoc -> String
+showUnqualifiedPage dflag = renderStyle Pretty.LeftMode 0 . GHC.withPprStyleDoc dflag (Compat.unqualStyle dflag)
+
+tryT :: MonadCatch m => m a -> m (Maybe a)
+tryT act = catch (fmap Just act) (const (return Nothing) . (id :: SomeException -> SomeException))
+
+readPackage :: GHC.PackageConfig -> ModulePackage
+readPackage pc = ModulePackage (fromString $ GHC.packageNameString pc) (fromString $ showVersion (GHC.packageVersion pc))
+
+readPackageConfig :: GHC.PackageConfig -> PackageConfig
+readPackageConfig pc = PackageConfig
+	(readPackage pc)
+	(map (fromString . GHC.moduleNameString . Compat.exposedModuleName) $ GHC.exposedModules pc)
+	(GHC.exposed pc)
+
+ghcModuleLocation :: GHC.PackageConfig -> GHC.Module -> 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
+	mname' = GHC.moduleNameString $ GHC.moduleName m
+
+packageConfigs :: GhcM [GHC.PackageConfig]
+packageConfigs = liftM (fromMaybe [] . pkgDatabase) GHC.getSessionDynFlags
+
+packageDbModules :: GhcM [(GHC.PackageConfig, GHC.Module)]
+packageDbModules = do
+	pkgs <- packageConfigs
+	dflags <- GHC.getSessionDynFlags
+	return [(p, m) |
+		p <- pkgs,
+		mn <- map Compat.exposedModuleName (GHC.exposedModules p),
+		m <- lookupModule_ dflags mn]
+
+-- Lookup module everywhere
+lookupModule_ :: GHC.DynFlags -> GHC.ModuleName -> [GHC.Module]
+lookupModule_ d mn = case GHC.lookupModuleWithSuggestions d mn Nothing of
+	GHC.LookupFound m' _ -> [m']
+	GHC.LookupMultiple ms -> map fst ms
+	GHC.LookupHidden ls rs -> map fst $ ls ++ rs
+	GHC.LookupNotFound _ -> []
+
+-- | Get modules packages
+modulesPackages :: [ModuleLocation] -> [ModulePackage]
+modulesPackages = ordNub . mapMaybe (preview modulePackage)
+
+-- | Group modules by packages
+modulesPackagesGroups :: [ModuleLocation] -> [(ModulePackage, [ModuleLocation])]
+modulesPackagesGroups = map (first head . unzip) . groupBy ((==) `on` fst) . sort . mapMaybe (\m -> (,) <$> preview modulePackage m <*> pure m)
+
+-- | Run action for each package with prepared '-package' flags
+withEachPackage :: (ModulePackage -> [ModuleLocation] -> GhcM a) -> [ModuleLocation] -> GhcM [a]
+withEachPackage act = mapM (uncurry act') . modulesPackagesGroups where
+	act' mpkg mlocs = setPackagesOpts >> act mpkg mlocs where
+		packagesOpts = "-hide-all-packages" : ["-package " ++ show p | p <- modulesPackages mlocs]
+		setPackagesOpts = void $ do
+			fs <- GHC.getSessionDynFlags
+			(fs', _, _) <- GHC.parseDynamicFlags (fs { GHC.packageFlags = [] }) (map GHC.noLoc packagesOpts)
+			(fs'', _) <- GHC.liftIO $ GHC.initPackages fs'
+			GHC.setSessionDynFlags fs''
src/HsDev/Server/Base.hs view
@@ -1,159 +1,395 @@-{-# LANGUAGE CPP, OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Server.Base (-	initLog, runServer, Server, startServer, inServer,-	withCache, writeCache, readCache,--	module HsDev.Server.Types,-	module HsDev.Server.Message-	) where--import Control.Applicative-import Control.Concurrent-import Control.Exception-import Control.Monad-import Control.Monad.Except-import Control.Monad.Reader-import Data.Default-import qualified Data.Map as M-import Data.Maybe-import Data.String-import Data.Text (Text)-import qualified Data.Text as T (pack, unpack)-import System.Log.Simple hiding (Level(..), Message)-import qualified System.Log.Simple.Base as Log-import System.Directory (removeDirectoryRecursive, createDirectoryIfMissing)-import System.FilePath--import qualified Control.Concurrent.FiniteChan as F-import System.Directory.Paths (canonicalize)-import qualified System.Directory.Watcher as Watcher-import Text.Format ((~~), (~%))--import qualified HsDev.Cache as Cache-import qualified HsDev.Cache.Structured as SC-import qualified HsDev.Client.Commands as Client-import HsDev.Database-import qualified HsDev.Database.Async as DB-import qualified HsDev.Database.Update as Update-import HsDev.Inspect (getDefines)-import HsDev.Tools.Ghc.Worker-import HsDev.Server.Types-import HsDev.Server.Message-import HsDev.Util--#if mingw32_HOST_OS-import System.Win32.FileMapping.NamePool-#endif---- | Inits log chan and returns functions (print message, wait channel)-initLog :: ServerOpts -> IO SessionLog-initLog sopts = do-	msgs <- F.newChan-	l <- newLog (logCfg [("", Log.level_ . T.pack . serverLogLevel $ sopts)]) $ concat [-		[handler text console | not $ serverSilent sopts],-		[handler text (chaner msgs)],-		[handler text (file f) | f <- maybeToList (serverLog sopts)]]-	let-		listenLog = F.dupChan msgs >>= F.readChan-	return $ SessionLog l listenLog (stopLog l)---- | Run server-runServer :: ServerOpts -> ServerM IO () -> IO ()-runServer sopts act = bracket (initLog sopts) sessionLogWait $ \slog -> Watcher.withWatcher $ \watcher -> withLog (sessionLogger slog) $ do-	waitSem <- liftIO $ newQSem 0-	db <- liftIO $ DB.newAsync-	withCache sopts () $ \cdir -> do-		sendLog Log.Trace $ "Checking cache version in {}" ~~ cdir -		ver <- liftIO $ Cache.readVersion $ cdir </> Cache.versionCache-		sendLog Log.Debug $ "Cache version: {}" ~~ strVersion ver-		unless (sameVersion (cutVersion version) (cutVersion ver)) $ ignoreIO $ do-			sendLog Log.Info $ "Cache version ({cache}) is incompatible with hsdev version ({hsdev}), removing cache ({dir})" ~~-				("cache" ~% strVersion ver) ~~-				("hsdev" ~% strVersion version) ~~-				("dir" ~% cdir)-			-- drop cache-			liftIO $ removeDirectoryRecursive cdir-		sendLog Log.Debug $ "Writing new cache version: {}" ~~ strVersion version-		liftIO $ createDirectoryIfMissing True cdir-		liftIO $ Cache.writeVersion $ cdir </> Cache.versionCache-	when (serverLoad sopts) $ withCache sopts () $ \cdir -> do-		sendLog Log.Info $ "Loading cache from {}" ~~ cdir-		dbCache <- liftA merge <$> liftIO (SC.load cdir)-		case dbCache of-			Left err -> sendLog Log.Error $ "Failed to load cache: {}" ~~ err-			Right dbCache' -> DB.update db (return dbCache')-#if mingw32_HOST_OS-	mmapPool <- Just <$> liftIO (createPool "hsdev")-#endif-	ghcw <- ghcWorker-	defs <- liftIO getDefines-	let-		session = Session-			db-			(writeCache sopts)-			(readCache sopts)-			slog-			watcher-#if mingw32_HOST_OS-			mmapPool-#endif-			ghcw-			(do-				withLog (sessionLogger slog) $ sendLog Log.Trace "stopping server"-				signalQSem waitSem)-			(waitQSem waitSem)-			defs-	_ <- liftIO $ forkIO $ Update.onEvent watcher $ \w e -> withSession session $-		void $ Client.runClient def $ Update.processEvent def w e-	liftIO $ runReaderT (runServerM act) session--type Server = Worker (ServerM IO)--startServer :: ServerOpts -> IO Server-startServer sopts = startWorker (runServer sopts) id id--inServer :: Server -> CommandOptions -> Command -> IO Result-inServer srv copts c = do-	c' <- canonicalize c-	inWorker srv (Client.runClient copts $ Client.runCommand c')--chaner :: F.Chan String -> Consumer Text-chaner ch = return $ F.putChan ch . T.unpack---- | Perform action on cache-withCache :: Monad m => ServerOpts -> a -> (FilePath -> m a) -> m a-withCache sopts v onCache = case serverCache sopts of-	Nothing -> return v-	Just cdir -> onCache cdir--writeCache :: SessionMonad m => ServerOpts -> Database -> m ()-writeCache sopts db = withCache sopts () $ \cdir -> do-	sendLog Log.Info $ "writing cache to {}" ~~ cdir-	logIO "cache writing exception: " (sendLog Log.Error . fromString) $ do-		let-			sd = structurize db-		liftIO $ SC.dump cdir sd-		forM_ (M.keys (structuredPackageDbs sd)) $ \c -> sendLog Log.Debug ("cache write: cabal {}" ~~ show c)-		forM_ (M.keys (structuredProjects sd)) $ \p -> sendLog Log.Debug ("cache write: project {}" ~~ p)-		case allModules (structuredFiles sd) of-			[] -> return ()-			ms -> sendLog Log.Debug $ "cache write: {} files" ~~ length ms-	sendLog Log.Info $ "cache saved to {}" ~~ cdir--readCache :: SessionMonad m => ServerOpts -> (FilePath -> ExceptT String IO Structured) -> m (Maybe Database)-readCache sopts act = do-	s <- getSession-	liftIO $ withSession s $ withCache sopts Nothing $ \fpath -> do-		res <- liftIO $ runExceptT $ act fpath-		either cacheErr cacheOk res-	where-		cacheErr e = sendLog Log.Error ("Error reading cache: {}" ~~ e) >> return Nothing-		cacheOk s = do-			forM_ (M.keys (structuredPackageDbs s)) $ \c -> sendLog Log.Debug ("cache read: cabal {}" ~~ show c)-			forM_ (M.keys (structuredProjects s)) $ \p -> sendLog Log.Debug ("cache read: project {}" ~~ p)-			case allModules (structuredFiles s) of-				[] -> return ()-				ms -> sendLog Log.Debug $ "cache read: {} files" ~~ length ms-			return $ Just $ merge s+{-# LANGUAGE CPP, OverloadedStrings, CPP, PatternGuards #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Server.Base (
+	initLog, runServer, Server,
+	setupServer, shutdownServer,
+	startServer, startServer_, stopServer, withServer, withServer_, inServer, clientCommand, parseCommand, readCommand,
+	sendServer, sendServer_,
+	findPath,
+	processRequest, processClient, processClientSocket,
+
+	unMmap, makeSocket, sockAddr,
+
+	module HsDev.Server.Types,
+	module HsDev.Server.Message
+	) where
+
+import Control.Concurrent
+import Control.Concurrent.Async
+import qualified Control.Concurrent.Chan as C
+import Control.Lens (set, traverseOf, view)
+import Control.Monad
+import Control.Monad.Loops
+import Control.Monad.Except
+import Control.Monad.Reader
+import Control.Monad.Catch (bracket_, bracket, finally)
+import Data.Aeson hiding (Result, Error)
+import Data.Default
+import qualified Data.ByteString.Char8 as BS
+import Data.ByteString.Lazy.Char8 (ByteString)
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Maybe
+import Data.String (fromString)
+import qualified Data.Text as T (pack)
+import Data.Time.Clock.POSIX
+import Options.Applicative (info, progDesc)
+import System.Log.Simple hiding (Level(..), Message)
+import qualified System.Log.Simple.Base as Log (level_)
+import qualified System.Log.Simple as Log
+import Network.Socket
+import qualified Network.Socket.ByteString as Net (send)
+import qualified Network.Socket.ByteString.Lazy as Net (getContents)
+import System.FilePath
+import System.IO
+import Text.Format ((~~))
+
+import Control.Concurrent.Util
+import qualified Control.Concurrent.FiniteChan as F
+import System.Directory.Paths
+import qualified System.Directory.Watcher as Watcher
+
+import qualified HsDev.Client.Commands as Client
+import qualified HsDev.Database.SQLite as SQLite
+import HsDev.Error
+import qualified HsDev.Database.Update as Update
+import HsDev.Inspect (getDefines)
+import HsDev.Tools.Ghc.Worker
+import HsDev.Server.Types
+import HsDev.Server.Message
+import HsDev.Symbols.Location (ModuleLocation(..))
+import qualified HsDev.Watcher as W
+import HsDev.Util
+
+#if mingw32_HOST_OS
+import Data.Aeson.Types hiding (Result, Error)
+import System.Win32.FileMapping.Memory (withMapFile, readMapFile)
+import System.Win32.FileMapping.NamePool
+#else
+import System.Posix.Files (removeLink)
+#endif
+
+-- | Inits log chan and returns functions (print message, wait channel)
+initLog :: ServerOpts -> IO SessionLog
+initLog sopts = do
+	msgs <- C.newChan
+	l <- newLog (logCfg [("", Log.level_ . T.pack . serverLogLevel $ sopts)]) $ concat [
+		[handler text coloredConsole | not $ serverSilent sopts],
+		[chaner msgs],
+		[handler text (file f) | f <- maybeToList (serverLog sopts)]]
+	let
+		listenLog = C.dupChan msgs >>= C.getChanContents
+	return $ SessionLog l listenLog (stopLog l)
+
+-- | Run server
+runServer :: ServerOpts -> ServerM IO () -> IO ()
+runServer sopts act = bracket (initLog sopts) sessionLogWait $ \slog -> Watcher.withWatcher $ \watcher -> withLog (sessionLogger slog) $ do
+	waitSem <- liftIO $ newQSem 0
+	sqlDb <- liftIO $ SQLite.initialize (fromMaybe SQLite.sharedMemory $ serverDbFile sopts)
+	clientChan <- liftIO F.newChan
+#if mingw32_HOST_OS
+	mmapPool <- Just <$> liftIO (createPool "hsdev")
+#endif
+	ghcw <- ghcWorker
+	defs <- liftIO getDefines
+
+	session <- liftIO $ fixIO $ \sess -> do
+		let
+			setFileCts fpath Nothing = withSession sess $ do
+				Log.sendLog Log.Trace $ "dropping file contents for {}" ~~ fpath
+				SQLite.execute "delete from file_contents where file = ?;" (SQLite.Only fpath)
+			setFileCts fpath (Just cts) = do
+				tm <- getPOSIXTime
+				withSession sess $ do
+					Log.sendLog Log.Trace $ "setting file contents for {} with mtime = {}" ~~ fpath ~~ show tm
+					SQLite.execute "insert or replace into file_contents (file, contents, mtime) values (?, ?, ?);" (fpath, cts, (fromRational (toRational tm) :: Double))
+				writeChan (W.watcherChan watcher) (W.WatchedModule, W.Event W.Modified (view path fpath) tm)
+
+		uw <- startWorker (withSession sess . withSqlConnection) id logAll
+
+		return $ Session
+			sqlDb
+			(fromMaybe SQLite.sharedMemory $ serverDbFile sopts)
+			slog
+			watcher
+			setFileCts
+#if mingw32_HOST_OS
+			mmapPool
+#endif
+			ghcw
+			uw
+			(do
+				withLog (sessionLogger slog) $ Log.sendLog Log.Trace "stopping server"
+				signalQSem waitSem)
+			(waitQSem waitSem)
+			clientChan
+			defs
+
+	_ <- fork $ do
+		emptyTask <- async $ return ()
+		updaterTask <- newMVar emptyTask
+		tasksVar <- newMVar []
+		Update.onEvents_ watcher $ \evs -> withSession session $
+			void $ Client.runClient def $ Update.processEvents (withSession session . inSessionUpdater . void . Client.runClient def . Update.applyUpdates def) updaterTask tasksVar evs
+	liftIO $ runReaderT (runServerM $ watchDb >> act) session
+
+-- | Set initial watch: package-dbs, projects and standalone sources
+watchDb :: SessionMonad m => m ()
+watchDb = do
+	w <- askSession sessionWatcher
+	-- TODO: Implement watching package-dbs
+	cabals <- SQLite.query_ "select cabal from projects;"
+	projects <- mapM (SQLite.loadProject . SQLite.fromOnly) cabals
+	liftIO $ mapM_ (\proj -> W.watchProject w proj []) projects
+
+	files <- SQLite.query_ "select file from modules where file is not null and cabal is null;"
+	liftIO $ mapM_ (\(SQLite.Only f) -> W.watchModule w (FileModule f Nothing)) files
+
+type Server = Worker (ServerM IO)
+
+-- | Start listening for incoming connections
+setupServer :: ServerOpts -> ServerM IO ()
+setupServer sopts = do
+	q <- liftIO $ newQSem 0
+	clientChan <- askSession sessionClients
+	session <- getSession
+	_ <- liftIO $ async $ withSession session $ Log.scope "listener" $ flip finally serverExit $
+		bracket (liftIO $ makeSocket (serverPort sopts)) (liftIO . close) $ \s -> do
+			liftIO $ do
+				setSocketOption s ReuseAddr 1
+				addr' <- inet_addr "127.0.0.1"
+				bind s (sockAddr (serverPort sopts) addr')
+				listen s maxListenQueue
+			forever $ logAsync (Log.sendLog Log.Fatal . fromString) $ logIO "exception: " (Log.sendLog Log.Error . fromString) $ do
+				Log.sendLog Log.Trace "accepting connection..."
+				liftIO $ signalQSem q
+				(s', addr') <- liftIO $ accept s
+				Log.sendLog Log.Trace $ "accepted {}" ~~ show addr'
+				fork $ withSession session $ Log.scope (T.pack $ show addr') $
+					logAsync (Log.sendLog Log.Fatal . fromString) $ logIO "exception: " (Log.sendLog Log.Error . fromString) $
+						flip finally (liftIO $ close s') $
+							bracket (liftIO newEmptyMVar) (liftIO . (`putMVar` ())) $ \done -> do
+								me <- liftIO myThreadId
+								let
+									timeoutWait = withSession session $ do
+										notDone <- liftIO $ isEmptyMVar done
+										when notDone $ do
+											Log.sendLog Log.Trace $ "waiting for {} to complete" ~~ show addr'
+											waitAsync <- liftIO $ async $ do
+												threadDelay 1000000
+												killThread me
+											liftIO $ void $ waitCatch waitAsync
+								liftIO $ void $ F.sendChan clientChan timeoutWait
+								processClientSocket (show addr') s'
+
+	Log.sendLog Log.Trace "waiting for starting accept thread..."
+	liftIO $ waitQSem q
+	logIO "error writing to stdout: " (Log.sendLog Log.Error . fromString) $ liftIO $ putStrLn $ "Server started at port {}" ~~ serverPort sopts
+	Log.sendLog Log.Info $ "Server started at port {}" ~~ serverPort sopts
+
+-- | Shutdown server
+shutdownServer :: ServerOpts -> ServerM IO ()
+shutdownServer sopts = do
+	Log.sendLog Log.Trace "waiting for accept thread..."
+	serverWait
+	Log.sendLog Log.Trace "accept thread stopped"
+	liftIO $ unlink (serverPort sopts)
+	Log.sendLog Log.Trace "waiting for clients..."
+	serverWaitClients
+	Log.sendLog Log.Info "server stopped"
+
+startServer :: ServerOpts -> IO Server
+startServer sopts = startWorker (runServer sopts) (bracket_ (setupServer sopts) (shutdownServer sopts)) logAll
+
+-- Tiny version with no network stuff
+startServer_ :: ServerOpts -> IO Server
+startServer_ sopts = startWorker (runServer sopts) id logAll
+
+stopServer :: Server -> IO ()
+stopServer s = sendServer_ s ["exit"] >> stopWorker s
+
+withServer :: ServerOpts -> (Server -> IO a) -> IO a
+withServer sopts = bracket (startServer sopts) stopServer
+
+withServer_ :: ServerOpts -> (Server -> IO a) -> IO a
+withServer_ sopts = bracket (startServer_ sopts) stopServer
+
+inServer :: Server -> ServerM IO a -> IO a
+inServer = inWorker
+
+clientCommand :: CommandOptions -> Command -> ServerM IO Result
+clientCommand copts c = do
+	c' <- liftIO $ canonicalize c
+	Client.runClient copts (Client.runCommand c')
+
+parseCommand :: [String] -> Either String Command
+parseCommand = parseArgs "hsdev" (info cmdP (progDesc "hsdev tool"))
+
+readCommand :: [String] -> Command
+readCommand = either error id . parseCommand
+
+sendServer :: Server -> CommandOptions -> [String] -> IO Result
+sendServer srv copts args = case parseCommand args of
+	Left e -> hsdevError $ RequestError e (unwords args)
+	Right c -> inServer srv (clientCommand copts c)
+
+sendServer_ :: Server -> [String] -> IO Result
+sendServer_ srv = sendServer srv def
+
+chaner :: C.Chan Log.Message -> Consumer Log.Message
+chaner ch = return $ C.writeChan ch
+
+findPath :: MonadIO m => CommandOptions -> FilePath -> m FilePath
+findPath copts f = liftIO $ canonicalize (normalise f') where
+	f' = absolutise (fromFilePath $ commandOptionsRoot copts) f
+
+-- | Process request, notifications can be sent during processing
+processRequest :: SessionMonad m => CommandOptions -> Command -> m Result
+processRequest copts c = do
+	c' <- paths (findPath copts) c
+	s <- getSession
+	withSession s $ Client.runClient copts $ Client.runCommand c'
+
+-- | Process client, listen for requests and process them
+processClient :: SessionMonad m => String -> F.Chan ByteString -> (ByteString -> IO ()) -> m ()
+processClient name rchan send' = do
+	Log.sendLog Log.Info "connected"
+	respChan <- liftIO newChan
+	fork $ getChanContents respChan >>= mapM_ (send' . encodeMessage)
+	linkVar <- liftIO $ newMVar $ return ()
+	s <- getSession
+	exit <- askSession sessionExit
+	let
+		answer :: SessionMonad m => Msg (Message Response) -> m ()
+		answer m = do
+			unless (isNotification $ view (msg . message) m) $
+				Log.sendLog Log.Trace $ "responsed << {}" ~~ ellipsis (fromUtf8 (encode $ view (msg . message) m))
+			liftIO $ writeChan respChan m
+			where
+				ellipsis :: String -> String
+				ellipsis str
+					| length str < 100 = str
+					| otherwise = take 100 str ++ "..."
+
+	flip finally (disconnected linkVar) $
+		whileJust_ (liftIO $ F.getChan rchan) $ \req' -> do
+			Log.sendLog Log.Trace $ "received >> {}" ~~ fromUtf8 req'
+			case decodeMessage req' of
+				Left em -> do
+					Log.sendLog Log.Warning $ "Invalid request {}" ~~ fromUtf8 req'
+					answer $ set msg (Message Nothing $ responseError $ RequestError "invalid request" $ fromUtf8 req') em
+				Right m -> fork $ withSession s $ Log.scope (T.pack name) $ Log.scope "req" $
+					Log.scope (T.pack $ fromMaybe "_" (view (msg . messageId) m)) $ do
+						resp' <- flip (traverseOf (msg . message)) m $ \(Request c cdir noFile tm silent) -> do
+							let
+								onNotify n
+									| silent = return ()
+									| otherwise = traverseOf (msg . message) (const $ mmap' noFile (Response $ Left n)) m >>= answer
+							Log.sendLog Log.Trace $ "requested >> {}" ~~ fromUtf8 (encode c)
+							resp <- liftIO $ fmap (Response . Right) $ handleTimeout tm $ hsdevLiftIO $ withSession s $
+								processRequest
+									CommandOptions {
+										commandOptionsRoot = cdir,
+										commandOptionsNotify = withSession s . onNotify,
+										commandOptionsLink = void (swapMVar linkVar exit),
+										commandOptionsHold = forever (F.getChan rchan) }
+									c
+							mmap' noFile resp
+						answer resp'
+	where
+		handleTimeout :: Int -> IO Result -> IO Result
+		handleTimeout 0 = id
+		handleTimeout tm = fmap (fromMaybe $ Error $ OtherError "timeout") . timeout tm
+
+		mmap' :: SessionMonad m => Bool -> Response -> m Response
+#if mingw32_HOST_OS
+		mmap' False r = do
+			mpool <- askSession sessionMmapPool
+			case mpool of
+				Just pool -> liftIO $ mmap pool r
+				Nothing -> return r
+#endif
+		mmap' _ r = return r
+
+		-- Call on disconnected, either no action or exit command
+		disconnected :: SessionMonad m => MVar (IO ()) -> m ()
+		disconnected var = do
+			Log.sendLog Log.Info "disconnected"
+			liftIO $ join $ takeMVar var
+
+-- | Process client by socket
+processClientSocket :: SessionMonad m => String -> Socket -> m ()
+processClientSocket name s = do
+	recvChan <- liftIO F.newChan
+	fork $ finally
+		(Net.getContents s >>= mapM_ (F.sendChan recvChan) . L.lines)
+		(F.closeChan recvChan)
+	processClient name recvChan (sendLine s)
+	where
+		-- NOTE: Network version of `sendAll` goes to infinite loop on client socket close
+		-- when server's send is blocked, see https://github.com/haskell/network/issues/155
+		-- After that issue fixed we may revert to `processClientHandle`
+		sendLine :: Socket -> ByteString -> IO ()
+		sendLine sock bs = sendAll sock $ L.toStrict $ L.snoc bs '\n'
+		sendAll :: Socket -> BS.ByteString -> IO ()
+		sendAll sock bs
+			| BS.null bs = return ()
+			| otherwise = do
+				sent <- Net.send sock bs
+				when (sent > 0) $ sendAll sock (BS.drop sent bs)
+
+#if mingw32_HOST_OS
+newtype MmapFile = MmapFile String
+
+instance ToJSON MmapFile where
+	toJSON (MmapFile f) = object ["file" .= f]
+
+instance FromJSON MmapFile where
+	parseJSON = withObject "file" $ \v -> MmapFile <$> v .:: "file"
+
+-- | Push message to mmap and return response which points to this mmap
+mmap :: Pool -> Response -> IO Response
+mmap mmapPool r
+	| L.length msg' <= 1024 = return r
+	| otherwise = do
+		rvar <- newEmptyMVar
+		_ <- forkIO $ flip finally (tryPutMVar rvar r) $ void $ withName mmapPool $ \mmapName -> runExceptT $ catchError
+			(withMapFile mmapName (L.toStrict msg') $ liftIO $ do
+				_ <- tryPutMVar rvar $ result $ MmapFile mmapName
+				-- give 10 seconds for client to read data
+				threadDelay 10000000)
+			(\_ -> liftIO $ void $ tryPutMVar rvar r)
+		takeMVar rvar
+	where
+		msg' = encode r
+#endif
+
+-- | If response points to mmap, get its contents and parse
+unMmap :: Response -> IO Response
+#if mingw32_HOST_OS
+unMmap (Response (Right (Result v)))
+	| Just (MmapFile f) <- parseMaybe parseJSON v = do
+		cts <- runExceptT (fmap L.fromStrict (readMapFile f))
+		case cts of
+			Left _ -> return $ responseError $ ResponseError "can't read map view of file" f
+			Right r' -> case eitherDecode r' of
+				Left e' -> return $ responseError $ ResponseError ("can't parse response: {}" ~~ e') (fromUtf8 r')
+				Right r'' -> return r''
+#endif
+unMmap r = return r
+
+makeSocket :: ConnectionPort -> IO Socket
+makeSocket (NetworkPort _) = socket AF_INET Stream defaultProtocol
+makeSocket (UnixPort _) = socket AF_UNIX Stream defaultProtocol
+
+sockAddr :: ConnectionPort -> HostAddress -> SockAddr
+sockAddr (NetworkPort p) addr = SockAddrInet (fromIntegral p) addr
+sockAddr (UnixPort s) _ = SockAddrUnix s
+
+unlink :: ConnectionPort -> IO ()
+unlink (NetworkPort _) = return ()
+#if mingw32_HOST_OS
+unlink (UnixPort _) = return ()
+#else
+unlink (UnixPort s) = removeLink s
+#endif
src/HsDev/Server/Commands.hs view
@@ -1,434 +1,180 @@-{-# LANGUAGE OverloadedStrings, CPP, PatternGuards, LambdaCase, TemplateHaskell #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Server.Commands (-	ServerCommand(..), ServerOpts(..), ClientOpts(..),-	Request(..),-	Msg, isLisp, msg, jsonMsg, lispMsg, encodeMessage, decodeMessage,-	sendCommand, runServerCommand,-	findPath,-	processRequest, processClient, processClientSocket,-	module HsDev.Server.Types-	) where--import Control.Applicative-import Control.Concurrent-import Control.Concurrent.Async-import Control.Lens (set, traverseOf, view, over, Lens', Lens, _1, _2, _Left)-import Control.Monad-import Control.Monad.Catch (bracket, finally)-import Data.Aeson hiding (Result, Error)-import Data.Aeson.Encode.Pretty-import qualified Data.ByteString.Char8 as BS-import Data.ByteString.Lazy.Char8 (ByteString)-import qualified Data.ByteString.Lazy.Char8 as L-import Data.Maybe-import Data.String (fromString)-import qualified Data.Text as T (pack)-import Network.Socket hiding (connect)-import qualified Network.Socket as Net hiding (send)-import qualified Network.Socket.ByteString as Net (send)-import qualified Network.Socket.ByteString.Lazy as Net (getContents)-import System.Directory-import System.Exit-import System.FilePath-import System.IO-import qualified System.Log.Simple as Log--import Control.Concurrent.Util-import qualified Control.Concurrent.FiniteChan as F-import Data.Lisp-import Text.Format ((~~), (~%))-import System.Directory.Paths--import qualified HsDev.Client.Commands as Client-import qualified HsDev.Database.Async as DB-import HsDev.Server.Base-import HsDev.Server.Types-import HsDev.Tools.Base (runTool_)-import HsDev.Error-import HsDev.Util-import HsDev.Version--#if mingw32_HOST_OS-import Data.Aeson.Types hiding (Result, Error)-import Data.Char-import Data.List-import System.Environment-import System.Win32.FileMapping.Memory (withMapFile, readMapFile)-import System.Win32.FileMapping.NamePool-import System.Win32.PowerShell (escape, quote, quoteDouble)-#else-import Control.Exception (SomeException, handle)-import System.Posix.Process-import System.Posix.Files (removeLink)-import System.Posix.IO-#endif--sendCommand :: ClientOpts -> Bool -> Command -> (Notification -> IO a) -> IO Result-sendCommand copts noFile c onNotification = do-	asyncAct <- async sendReceive-	res <- waitCatch asyncAct-	case res of-		Left e -> return $ Error $ OtherError (show e)-		Right r -> return r-	where-		sendReceive = do-			curDir <- getCurrentDirectory-			input <- if clientStdin copts-				then Just <$> L.getContents-				else return $ toUtf8 <$> Nothing -- arg "data" copts-			let-				parseData :: L.ByteString -> IO Value-				parseData cts = case eitherDecode cts of-					Left err -> putStrLn ("Invalid data: " ++ err) >> exitFailure-					Right v -> return v-			_ <- traverse parseData input -- FIXME: Not used!--			s <- makeSocket (clientPort copts)-			addr' <- inet_addr "127.0.0.1"-			Net.connect s (sockAddr (clientPort copts) addr')-			bracket (socketToHandle s ReadWriteMode) hClose $ \h -> do-				L.hPutStrLn h $ encode $ Message Nothing $ Request c curDir noFile (clientTimeout copts) (clientSilent copts)-				hFlush h-				peekResponse h--		peekResponse h = do-			resp <- hGetLineBS h-			parseResponse h resp--		parseResponse h str = case eitherDecode str of-			Left e -> return $ Error $ ResponseError ("can't parse: {}" ~~ e) (fromUtf8 str)-			Right (Message _ r) -> do-				Response r' <- unMmap r-				case r' of-					Left n -> onNotification n >> peekResponse h-					Right res -> return res--runServerCommand :: ServerCommand -> IO ()-runServerCommand Version = putStrLn $cabalVersion-runServerCommand (Start sopts) = do-#if mingw32_HOST_OS-	let-		args = "run" : serverOptsArgs sopts-	myExe <- getExecutablePath-	curDir <- getCurrentDirectory-	let-		-- one escape for start-process and other for callable process-		-- seems, that start-process just concats arguments into one string-		-- start-process foo 'bar baz' ⇒ foo bar baz -- not expected-		-- start-process foo '"bar baz"' ⇒ foo "bar baz" -- ok-		biescape = escape quote . escape quoteDouble-		script = "try {{ start-process {process} {args} -WindowStyle Hidden -WorkingDirectory {dir} }} catch {{ $_.Exception, $_.InvocationInfo.Line }}"-			~~ ("process" ~% escape quote myExe)-			~~ ("args" ~% intercalate ", " (map biescape args))-			~~ ("dir" ~% escape quote curDir)-	r <- runTool_ "powershell" [-		"-Command",-		script]-	if all isSpace r-		then putStrLn $ "Server started at port {}" ~~ serverPort sopts-		else mapM_ putStrLn [-			"Failed to start server",-			"\tCommand: {}" ~~ script,-			"\tResult: {}" ~~ r]-#else-	let-		forkError :: SomeException -> IO ()-		forkError e  = putStrLn $ "Failed to start server: {}" ~~ show e--		proxy :: IO ()-		proxy = do-			_ <- createSession-			_ <- forkProcess serverAction-			exitImmediately ExitSuccess--		serverAction :: IO ()-		serverAction = do-			mapM_ closeFd [stdInput, stdOutput, stdError]-			nullFd <- openFd "/dev/null" ReadWrite Nothing defaultFileFlags-			mapM_ (dupTo nullFd) [stdInput, stdOutput, stdError]-			closeFd nullFd-			runServerCommand (Run sopts)--	handle forkError $ do-		_ <- forkProcess proxy-		putStrLn $ "Server started at port {}" ~~ serverPort sopts-#endif-runServerCommand (Run sopts) = runServer sopts $ do-	q <- liftIO $ newQSem 0-	clientChan <- liftIO F.newChan-	session <- getSession-	_ <- liftIO $ async $ withSession session $ Log.scope "listener" $ flip finally serverExit $-		bracket (liftIO $ makeSocket (serverPort sopts)) (liftIO . close) $ \s -> do-			liftIO $ do-				setSocketOption s ReuseAddr 1-				addr' <- inet_addr "127.0.0.1"-				bind s (sockAddr (serverPort sopts) addr')-				listen s maxListenQueue-			forever $ logAsync (Log.sendLog Log.Fatal . fromString) $ logIO "exception: " (Log.sendLog Log.Error . fromString) $ do-				Log.sendLog Log.Trace "accepting connection..."-				liftIO $ signalQSem q-				(s', addr') <- liftIO $ accept s-				Log.sendLog Log.Trace $ "accepted {}" ~~ show addr'-				void $ liftIO $ forkIO $ withSession session $ Log.scope (T.pack $ show addr') $-					logAsync (Log.sendLog Log.Fatal . fromString) $ logIO "exception: " (Log.sendLog Log.Error . fromString) $-						flip finally (liftIO $ close s') $-							bracket (liftIO newEmptyMVar) (liftIO . (`putMVar` ())) $ \done -> do-								me <- liftIO myThreadId-								let-									timeoutWait = withSession session $ do-										notDone <- liftIO $ isEmptyMVar done-										when notDone $ do-											Log.sendLog Log.Trace $ "waiting for {} to complete" ~~ show addr'-											waitAsync <- liftIO $ async $ do-												threadDelay 1000000-												killThread me-											liftIO $ void $ waitCatch waitAsync-								liftIO $ F.putChan clientChan timeoutWait-								processClientSocket (show addr') s'--	Log.sendLog Log.Trace "waiting for starting accept thread..."-	liftIO $ waitQSem q-	liftIO $ putStrLn $ "Server started at port {}" ~~ serverPort sopts-	Log.sendLog Log.Info $ "server started at port {}" ~~ serverPort sopts-	Log.sendLog Log.Trace "waiting for accept thread..."-	serverWait-	Log.sendLog Log.Trace "accept thread stopped"-	liftIO $ unlink (serverPort sopts)-	askSession sessionDatabase >>= liftIO . DB.readAsync >>= writeCache sopts-	Log.sendLog Log.Trace "waiting for clients..."-	liftIO (F.stopChan clientChan) >>= sequence_-	Log.sendLog Log.Info "server stopped"-runServerCommand (Stop copts) = runServerCommand (Remote copts False Exit)-runServerCommand (Connect copts) = do-	curDir <- getCurrentDirectory-	s <- makeSocket $ clientPort copts-	addr' <- inet_addr "127.0.0.1"-	Net.connect s $ sockAddr (clientPort copts) addr'-	bracket (socketToHandle s ReadWriteMode) hClose $ \h -> forM_ [(1 :: Integer)..] $ \i -> ignoreIO $ do-		input' <- hGetLineBS stdin-		case decodeMsg input' of-			Left em -> L.putStrLn $ encodeMessage $ set msg (Message Nothing $ responseError $ OtherError "invalid command") em-			Right m -> do-				L.hPutStrLn h $ encodeMessage $ set msg (Message (Just $ show i) $ Request (view msg m) curDir True (clientTimeout copts) False) m-				waitResp h-	where-		waitResp h = do-			resp <- hGetLineBS h-			parseResp h resp--		parseResp h str = case decodeMessage str of-			Left em -> putStrLn $ "Can't decode response: {}" ~~ view msg em-			Right m -> do-				Response r' <- unMmap $ view (msg . message) m-				putStrLn $ "{id}: {response}"-					~~ ("id" ~% fromMaybe "_" (view (msg . messageId) m))-					~~ ("response" ~% fromUtf8 (encodeMsg $ set msg (Response r') m))-				case unResponse (view (msg . message) m) of-					Left _ -> waitResp h-					_ -> return ()-runServerCommand (Remote copts noFile c) = sendCommand copts noFile c printValue >>= printResult where-	printValue :: ToJSON a => a -> IO ()-	printValue = L.putStrLn . encodeValue-	printResult :: Result -> IO ()-	printResult (Result r) = printValue r-	printResult e = printValue e-	encodeValue :: ToJSON a => a -> L.ByteString-	encodeValue = if clientPretty copts then encodePretty else encode--findPath :: MonadIO m => CommandOptions -> FilePath -> m FilePath-findPath copts f = liftIO $ canonicalizePath (normalise f') where-	f'-		| isRelative f = commandOptionsRoot copts </> f-		| otherwise = f--type Msg a = (Bool, a)--isLisp :: Lens' (Msg a) Bool-isLisp = _1--msg :: Lens (Msg a) (Msg b) a b-msg = _2--jsonMsg :: a -> Msg a-jsonMsg = (,) False--lispMsg :: a -> Msg a-lispMsg = (,) True---- | Decode lisp or json-decodeMsg :: FromJSON a => ByteString -> Either (Msg String) (Msg a)-decodeMsg bstr = over _Left decodeType' decodeMsg' where-	decodeType'-		| isLisp' = lispMsg-		| otherwise = jsonMsg-	decodeMsg' = (lispMsg <$> decodeLisp bstr) <|> (jsonMsg <$> eitherDecode bstr)-	isLisp' = fromMaybe False $ mplus (try' eitherDecode False) (try' decodeLisp True)-	try' :: (ByteString -> Either String Value) -> Bool -> Maybe Bool-	try' f l = either (const Nothing) (const $ Just l) $ f bstr---- | Encode lisp or json-encodeMsg :: ToJSON a => Msg a -> ByteString-encodeMsg m-	| view isLisp m = encodeLisp $ view msg m-	| otherwise = encode $ view msg m---- | Decode lisp or json request-decodeMessage :: FromJSON a => ByteString -> Either (Msg String) (Msg (Message a))-decodeMessage = decodeMsg--encodeMessage :: ToJSON a => Msg (Message a) -> ByteString-encodeMessage = encodeMsg---- | Process request, notifications can be sent during processing-processRequest :: SessionMonad m => CommandOptions -> Command -> m Result-processRequest copts c = do-	c' <- paths (findPath copts) c-	s <- getSession-	withSession s $ Client.runClient copts $ Client.runCommand c'---- | Process client, listen for requests and process them-processClient :: SessionMonad m => String -> F.Chan ByteString -> (ByteString -> IO ()) -> m ()-processClient name rchan send' = do-	Log.sendLog Log.Info "connected"-	respChan <- liftIO newChan-	liftIO $ void $ forkIO $ getChanContents respChan >>= mapM_ (send' . encodeMessage)-	linkVar <- liftIO $ newMVar $ return ()-	s <- getSession-	exit <- askSession sessionExit-	let-		answer :: SessionMonad m => Msg (Message Response) -> m ()-		answer m = do-			unless (isNotification $ view (msg . message) m) $-				Log.sendLog Log.Trace $ "responsed << {}" ~~ ellipsis (fromUtf8 (encode $ view (msg . message) m))-			liftIO $ writeChan respChan m-			where-				ellipsis :: String -> String-				ellipsis str-					| length str < 100 = str-					| otherwise = take 100 str ++ "..."-	-- flip finally (disconnected linkVar) $ forever $ Log.scopeLog (commandLogger copts) (T.pack name) $ do-	reqs <- liftIO $ F.readChan rchan-	flip finally (disconnected linkVar) $-		forM_ reqs $ \req' -> do-			Log.sendLog Log.Trace $ "received >> {}" ~~ fromUtf8 req'-			case decodeMessage req' of-				Left em -> do-					Log.sendLog Log.Warning $ "Invalid request {}" ~~ fromUtf8 req'-					answer $ set msg (Message Nothing $ responseError $ RequestError "invalid request" $ fromUtf8 req') em-				Right m -> void $ liftIO $ forkIO $ withSession s $ Log.scope (T.pack name) $ Log.scope "req" $-					Log.scope (T.pack $ fromMaybe "_" (view (msg . messageId) m)) $ do-						resp' <- flip (traverseOf (msg . message)) m $ \(Request c cdir noFile tm silent) -> do-							let-								onNotify n-									| silent = return ()-									| otherwise = traverseOf (msg . message) (const $ mmap' noFile (Response $ Left n)) m >>= answer-							Log.sendLog Log.Trace $ "requested >> {}" ~~ fromUtf8 (encode c)-							resp <- liftIO $ fmap (Response . Right) $ handleTimeout tm $ hsdevLiftIO $ withSession s $-								processRequest-									CommandOptions {-										commandOptionsRoot = cdir,-										commandOptionsNotify = withSession s . onNotify,-										commandOptionsLink = void (swapMVar linkVar exit),-										commandOptionsHold = forever (F.getChan rchan) }-									c-							mmap' noFile resp-						answer resp'-	where-		handleTimeout :: Int -> IO Result -> IO Result-		handleTimeout 0 = id-		handleTimeout tm = fmap (fromMaybe $ Error $ OtherError "timeout") . timeout tm--		mmap' :: SessionMonad m => Bool -> Response -> m Response-#if mingw32_HOST_OS-		mmap' False r = do-			mpool <- askSession sessionMmapPool-			case mpool of-				Just pool -> liftIO $ mmap pool r-				Nothing -> return r-#endif-		mmap' _ r = return r--		-- Call on disconnected, either no action or exit command-		disconnected :: SessionMonad m => MVar (IO ()) -> m ()-		disconnected var = do-			Log.sendLog Log.Info "disconnected"-			liftIO $ join $ takeMVar var---- | Process client by socket-processClientSocket :: SessionMonad m => String -> Socket -> m ()-processClientSocket name s = do-	recvChan <- liftIO F.newChan-	liftIO $ void $ forkIO $ finally-		(Net.getContents s >>= mapM_ (F.putChan recvChan) . L.lines)-		(F.closeChan recvChan)-	processClient name recvChan (sendLine s)-	where-		-- NOTE: Network version of `sendAll` goes to infinite loop on client socket close-		-- when server's send is blocked, see https://github.com/haskell/network/issues/155-		-- After that issue fixed we may revert to `processClientHandle`-		sendLine :: Socket -> ByteString -> IO ()-		sendLine sock bs = sendAll sock $ L.toStrict $ L.snoc bs '\n'-		sendAll :: Socket -> BS.ByteString -> IO ()-		sendAll sock bs-			| BS.null bs = return ()-			| otherwise = do-				sent <- Net.send sock bs-				when (sent > 0) $ sendAll sock (BS.drop sent bs)--#if mingw32_HOST_OS-data MmapFile = MmapFile String--instance ToJSON MmapFile where-	toJSON (MmapFile f) = object ["file" .= f]--instance FromJSON MmapFile where-	parseJSON = withObject "file" $ \v -> MmapFile <$> v .:: "file"---- | Push message to mmap and return response which points to this mmap-mmap :: Pool -> Response -> IO Response-mmap mmapPool r-	| L.length msg' <= 1024 = return r-	| otherwise = do-		rvar <- newEmptyMVar-		_ <- forkIO $ flip finally (tryPutMVar rvar r) $ void $ withName mmapPool $ \mmapName -> runExceptT $ catchError-			(withMapFile mmapName (L.toStrict msg') $ liftIO $ do-				_ <- tryPutMVar rvar $ result $ MmapFile mmapName-				-- give 10 seconds for client to read data-				threadDelay 10000000)-			(\_ -> liftIO $ void $ tryPutMVar rvar r)-		takeMVar rvar-	where-		msg' = encode r-#endif---- | If response points to mmap, get its contents and parse-unMmap :: Response -> IO Response-#if mingw32_HOST_OS-unMmap (Response (Right (Result v)))-	| Just (MmapFile f) <- parseMaybe parseJSON v = do-		cts <- runExceptT (fmap L.fromStrict (readMapFile f))-		case cts of-			Left _ -> return $ responseError $ ResponseError "can't read map view of file" f-			Right r' -> case eitherDecode r' of-				Left e' -> return $ responseError $ ResponseError ("can't parse response: {}" ~~ e') (fromUtf8 r')-				Right r'' -> return r''-#endif-unMmap r = return r--makeSocket :: ConnectionPort -> IO Socket-makeSocket (NetworkPort _) = socket AF_INET Stream defaultProtocol-makeSocket (UnixPort _) = socket AF_UNIX Stream defaultProtocol--sockAddr :: ConnectionPort -> HostAddress -> SockAddr-sockAddr (NetworkPort p) addr = SockAddrInet (fromIntegral p) addr-sockAddr (UnixPort s) _ = SockAddrUnix s--unlink :: ConnectionPort -> IO ()-unlink (NetworkPort _) = return ()-#if mingw32_HOST_OS-unlink (UnixPort _) = return ()-#else-unlink (UnixPort s) = removeLink s-#endif+{-# LANGUAGE OverloadedStrings, CPP, LambdaCase, TemplateHaskell #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Server.Commands (
+	ServerCommand(..), ServerOpts(..), ClientOpts(..),
+	Request(..),
+	Msg, isLisp, msg, jsonMsg, lispMsg, encodeMessage, decodeMessage,
+	sendCommand, runServerCommand,
+	findPath,
+	processRequest, processClient, processClientSocket,
+	module HsDev.Server.Types
+	) where
+
+import Control.Concurrent.Async
+import Control.Lens (set, view)
+import Control.Monad
+import Control.Monad.Catch (bracket, bracket_)
+import Data.Aeson hiding (Result, Error)
+import qualified Data.Aeson as A
+import Data.Aeson.Encode.Pretty
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Maybe
+import Network.Socket hiding (connect)
+import qualified Network.Socket as Net hiding (send)
+import System.Directory
+import System.Exit
+import System.IO
+import qualified System.Log.Simple as Log
+
+import Text.Format ((~~), (~%))
+import Text.Format.Colored (coloredLine)
+
+import HsDev.Server.Base
+import HsDev.Server.Types
+import HsDev.Tools.Base (runTool_)
+import HsDev.Error
+import HsDev.Util
+import HsDev.Version
+
+#if mingw32_HOST_OS
+import Data.List
+import System.Environment
+import System.Win32.PowerShell (escape, quote, quoteDouble)
+#else
+import Control.Exception (SomeException, handle)
+import System.Posix.Process
+import System.Posix.Files (removeLink)
+import System.Posix.IO
+#endif
+
+sendCommand :: ClientOpts -> Bool -> Command -> (Notification -> IO a) -> IO Result
+sendCommand copts noFile c onNotification = do
+	asyncAct <- async sendReceive
+	res <- waitCatch asyncAct
+	case res of
+		Left e -> return $ Error $ OtherError (show e)
+		Right r -> return r
+	where
+		sendReceive = do
+			curDir <- getCurrentDirectory
+			input <- if clientStdin copts
+				then Just <$> L.getContents
+				else return $ toUtf8 <$> Nothing -- arg "data" copts
+			let
+				parseData :: L.ByteString -> IO Value
+				parseData cts = case eitherDecode cts of
+					Left err -> putStrLn ("Invalid data: " ++ err) >> exitFailure
+					Right v -> return v
+			_ <- traverse parseData input -- FIXME: Not used!
+
+			s <- makeSocket (clientPort copts)
+			addr' <- inet_addr "127.0.0.1"
+			Net.connect s (sockAddr (clientPort copts) addr')
+			bracket (socketToHandle s ReadWriteMode) hClose $ \h -> do
+				L.hPutStrLn h $ encode $ Message Nothing $ Request c curDir noFile (clientTimeout copts) (clientSilent copts)
+				hFlush h
+				peekResponse h
+
+		peekResponse h = do
+			resp <- hGetLineBS h
+			parseResponse h resp
+
+		parseResponse h str = case eitherDecode str of
+			Left e -> return $ Error $ ResponseError ("can't parse: {}" ~~ e) (fromUtf8 str)
+			Right (Message _ r) -> do
+				Response r' <- unMmap r
+				case r' of
+					Left n -> onNotification n >> peekResponse h
+					Right res -> return res
+
+runServerCommand :: ServerCommand -> IO ()
+runServerCommand Version = putStrLn $cabalVersion
+runServerCommand (Start sopts) = do
+#if mingw32_HOST_OS
+	let
+		args = "run" : serverOptsArgs sopts
+	myExe <- getExecutablePath
+	curDir <- getCurrentDirectory
+	let
+		-- one escape for start-process and other for callable process
+		-- seems, that start-process just concats arguments into one string
+		-- start-process foo 'bar baz' ⇒ foo bar baz -- not expected
+		-- start-process foo '"bar baz"' ⇒ foo "bar baz" -- ok
+		biescape = escape quote . escape quoteDouble
+		script = "try {{ start-process {process} {args} -WindowStyle Hidden -WorkingDirectory {dir} }} catch {{ $_.Exception, $_.InvocationInfo.Line }}"
+			~~ ("process" ~% escape quote myExe)
+			~~ ("args" ~% intercalate ", " (map biescape args))
+			~~ ("dir" ~% escape quote curDir)
+	_ <- runTool_ "powershell" [
+		"-NoProfile",
+		"-Command",
+		script]
+	putStrLn $ "Server started at port {}" ~~ serverPort sopts
+#else
+	let
+		forkError :: SomeException -> IO ()
+		forkError e  = putStrLn $ "Failed to start server: {}" ~~ show e
+
+		proxy :: IO ()
+		proxy = do
+			_ <- createSession
+			_ <- forkProcess serverAction
+			exitImmediately ExitSuccess
+
+		serverAction :: IO ()
+		serverAction = do
+			mapM_ closeFd [stdInput, stdOutput, stdError]
+			nullFd <- openFd "/dev/null" ReadWrite Nothing defaultFileFlags
+			mapM_ (dupTo nullFd) [stdInput, stdOutput, stdError]
+			closeFd nullFd
+			runServerCommand (Run sopts)
+
+	handle forkError $ do
+		_ <- forkProcess proxy
+		putStrLn $ "Server started at port {}" ~~ serverPort sopts
+#endif
+runServerCommand (Run sopts) = runServer sopts $ bracket_ (setupServer sopts) (shutdownServer sopts) $ return ()
+runServerCommand (Stop copts) = runServerCommand (Remote copts False Exit)
+runServerCommand (Connect copts) = do
+	curDir <- getCurrentDirectory
+	s <- makeSocket $ clientPort copts
+	addr' <- inet_addr "127.0.0.1"
+	Net.connect s $ sockAddr (clientPort copts) addr'
+	bracket (socketToHandle s ReadWriteMode) hClose $ \h -> forM_ [(1 :: Integer)..] $ \i -> ignoreIO $ do
+		input' <- hGetLineBS stdin
+		case decodeMsg input' of
+			Left em -> L.putStrLn $ encodeMessage $ set msg (Message Nothing $ responseError $ OtherError "invalid command") em
+			Right m -> do
+				L.hPutStrLn h $ encodeMessage $ set msg (Message (Just $ show i) $ Request (view msg m) curDir True (clientTimeout copts) False) m
+				waitResp h
+	where
+		waitResp h = do
+			resp <- hGetLineBS h
+			parseResp h resp
+
+		parseResp h str = case decodeMessage str of
+			Left em -> putStrLn $ "Can't decode response: {}" ~~ view msg em
+			Right m -> do
+				Response r' <- unMmap $ view (msg . message) m
+				putStrLn $ "{id}: {response}"
+					~~ ("id" ~% fromMaybe "_" (view (msg . messageId) m))
+					~~ ("response" ~% fromUtf8 (encodeMsg $ set msg (Response r') m))
+				case unResponse (view (msg . message) m) of
+					Left _ -> waitResp h
+					_ -> return ()
+runServerCommand (Remote copts noFile c@(Listen _)) = sendCommand copts noFile c printLog >>= noResult where
+	printLog :: Notification -> IO ()
+	printLog (Notification v) = case fromJSON v of
+		A.Error _ -> putStrLn "incorrect notification"
+		A.Success m -> coloredLine . Log.text $ m
+	noResult :: Result -> IO ()
+	noResult _ = return ()
+runServerCommand (Remote copts noFile c) = sendCommand copts noFile c printValue >>= printResult where
+	printValue :: ToJSON a => a -> IO ()
+	printValue = L.putStrLn . encodeValue
+	printResult :: Result -> IO ()
+	printResult (Result r) = printValue r
+	printResult e = printValue e
+	encodeValue :: ToJSON a => a -> L.ByteString
+	encodeValue = if clientPretty copts then encodePretty else encode
src/HsDev/Server/Message.hs view
@@ -1,115 +1,118 @@-{-# LANGUAGE OverloadedStrings, DeriveFunctor, TypeSynonymInstances, FlexibleInstances, TemplateHaskell #-}--module HsDev.Server.Message (-	Message(..), messageId, message,-	messagesById,-	Notification(..), Result(..), ResultPart(..),-	Response(..), isNotification, notification, result, responseError, resultPart,-	groupResponses, responsesById-	) where--import Control.Applicative-import Control.Lens (makeLenses)-import Control.Monad (join)-import Data.Aeson hiding (Error, Result)-import Data.Either (lefts, isRight)-import Data.List (unfoldr)--import HsDev.Types-import HsDev.Util ((.::), (.::?), objectUnion)---- | Message with id to link request and response-data Message a = Message {-	_messageId :: Maybe String,-	_message :: a }-		deriving (Eq, Ord, Show, Functor)--makeLenses ''Message--instance ToJSON a => ToJSON (Message a) where-	toJSON (Message i m) = object ["id" .= i] `objectUnion` toJSON m--instance FromJSON a => FromJSON (Message a) where-	parseJSON = withObject "message" $ \v ->-		Message <$> fmap join (v .::? "id") <*> parseJSON (Object v)--instance Foldable Message where-	foldMap f (Message _ m) = f m--instance Traversable Message where-	traverse f (Message i m) = Message i <$> f m---- | Get messages by id-messagesById :: Maybe String -> [Message a] -> [a]-messagesById i = map _message . filter ((== i) . _messageId)---- | Notification from server-data Notification = Notification Value deriving (Eq, Show)--instance ToJSON Notification where-	toJSON (Notification v) = object ["notify" .= v]--instance FromJSON Notification where-	parseJSON = withObject "notification" $ \v -> Notification <$> v .:: "notify"---- | Result from server-data Result =-	Result Value |-	-- ^ Result-	Error HsDevError-	-- ^ Error-		deriving (Show)--instance ToJSON Result where-	toJSON (Result r) = object ["result" .= r]-	toJSON (Error e) = toJSON e--instance FromJSON Result where-	parseJSON j = (withObject "result" (\v -> (Result <$> v .:: "result")) j) <|> (Error <$> parseJSON j)---- | Part of result list, returns via notification-data ResultPart = ResultPart Value--instance ToJSON ResultPart where-	toJSON (ResultPart r) = object ["result-part" .= r]--instance FromJSON ResultPart where-	parseJSON = withObject "result-part" $ \v -> ResultPart <$> v .:: "result-part"--newtype Response = Response { unResponse :: Either Notification Result } deriving (Show)--isNotification :: Response -> Bool-isNotification = either (const True) (const False) . unResponse--notification :: ToJSON a => a -> Response-notification = Response . Left . Notification . toJSON--result :: ToJSON a => a -> Response-result = Response . Right . Result . toJSON--responseError :: HsDevError -> Response-responseError = Response . Right . Error--resultPart :: ToJSON a => a  -> Notification-resultPart = Notification . toJSON . ResultPart . toJSON--instance ToJSON Response where-	toJSON (Response (Left n)) = toJSON n-	toJSON (Response (Right r)) = toJSON r--instance FromJSON Response where-	parseJSON v = Response <$> ((Left <$> parseJSON v) <|> (Right <$> parseJSON v))--groupResponses :: [Response] -> [([Notification], Result)]-groupResponses = unfoldr break' where-	break' :: [Response] -> Maybe (([Notification], Result), [Response])-	break' [] = Nothing-	break' cs =  Just ((lefts (map unResponse ns), r), drop 1 cs') where-		(ns, cs') = break (isRight . unResponse) cs-		r = case cs' of-			(Response (Right r') : _) -> r'-			[] -> Error $ OtherError "groupResponses: no result"-			_ -> error "groupResponses: impossible happened"--responsesById :: Maybe String -> [Message Response] -> [([Notification], Result)]-responsesById i = groupResponses . messagesById i+{-# LANGUAGE OverloadedStrings, DeriveFunctor, TypeSynonymInstances, FlexibleInstances, TemplateHaskell #-}
+
+module HsDev.Server.Message (
+	Message(..), messageId, message,
+	messagesById,
+	Notification(..), Result(..), ResultPart(..),
+	Response(..), isNotification, result, responseError,
+	groupResponses,
+	decodeMessage, encodeMessage,
+
+	module HsDev.Server.Message.Lisp
+	) where
+
+import Control.Applicative
+import Control.Lens (makeLenses)
+import Control.Monad (join)
+import Data.Aeson hiding (Error, Result)
+import Data.Either (lefts, isRight)
+import Data.List (unfoldr)
+import Data.ByteString.Lazy.Char8 (ByteString)
+
+import HsDev.Types
+import HsDev.Util ((.::), (.::?), objectUnion)
+import HsDev.Server.Message.Lisp
+
+-- | Message with id to link request and response
+data Message a = Message {
+	_messageId :: Maybe String,
+	_message :: a }
+		deriving (Eq, Ord, Show, Functor)
+
+makeLenses ''Message
+
+instance ToJSON a => ToJSON (Message a) where
+	toJSON (Message i m) = object ["id" .= i] `objectUnion` toJSON m
+
+instance FromJSON a => FromJSON (Message a) where
+	parseJSON = withObject "message" $ \v ->
+		Message <$> fmap join (v .::? "id") <*> parseJSON (Object v)
+
+instance Foldable Message where
+	foldMap f (Message _ m) = f m
+
+instance Traversable Message where
+	traverse f (Message i m) = Message i <$> f m
+
+-- | Get messages by id
+messagesById :: Maybe String -> [Message a] -> [a]
+messagesById i = map _message . filter ((== i) . _messageId)
+
+-- | Notification from server
+data Notification = Notification Value deriving (Eq, Show)
+
+instance ToJSON Notification where
+	toJSON (Notification v) = object ["notify" .= v]
+
+instance FromJSON Notification where
+	parseJSON = withObject "notification" $ \v -> Notification <$> v .:: "notify"
+
+-- | Result from server
+data Result =
+	Result Value |
+	-- ^ Result
+	Error HsDevError
+	-- ^ Error
+		deriving (Show)
+
+instance ToJSON Result where
+	toJSON (Result r) = object ["result" .= r]
+	toJSON (Error e) = toJSON e
+
+instance FromJSON Result where
+	parseJSON j = (withObject "result" (\v -> (Result <$> v .:: "result")) j) <|> (Error <$> parseJSON j)
+
+-- | Part of result list, returns via notification
+data ResultPart = ResultPart Value
+
+instance ToJSON ResultPart where
+	toJSON (ResultPart r) = object ["result-part" .= r]
+
+instance FromJSON ResultPart where
+	parseJSON = withObject "result-part" $ \v -> ResultPart <$> v .:: "result-part"
+
+newtype Response = Response { unResponse :: Either Notification Result } deriving (Show)
+
+isNotification :: Response -> Bool
+isNotification = either (const True) (const False) . unResponse
+
+result :: ToJSON a => a -> Response
+result = Response . Right . Result . toJSON
+
+responseError :: HsDevError -> Response
+responseError = Response . Right . Error
+
+instance ToJSON Response where
+	toJSON (Response (Left n)) = toJSON n
+	toJSON (Response (Right r)) = toJSON r
+
+instance FromJSON Response where
+	parseJSON v = Response <$> ((Left <$> parseJSON v) <|> (Right <$> parseJSON v))
+
+groupResponses :: [Response] -> [([Notification], Result)]
+groupResponses = unfoldr break' where
+	break' :: [Response] -> Maybe (([Notification], Result), [Response])
+	break' [] = Nothing
+	break' cs =  Just ((lefts (map unResponse ns), r), drop 1 cs') where
+		(ns, cs') = break (isRight . unResponse) cs
+		r = case cs' of
+			(Response (Right r') : _) -> r'
+			[] -> Error $ OtherError "groupResponses: no result"
+			_ -> error "groupResponses: impossible happened"
+
+-- | Decode lisp or json request
+decodeMessage :: FromJSON a => ByteString -> Either (Msg String) (Msg (Message a))
+decodeMessage = decodeMsg
+
+encodeMessage :: ToJSON a => Msg (Message a) -> ByteString
+encodeMessage = encodeMsg
+ src/HsDev/Server/Message/Lisp.hs view
@@ -0,0 +1,47 @@+module HsDev.Server.Message.Lisp (
+	Msg,
+	isLisp, msg,
+	jsonMsg, lispMsg,
+
+	decodeMsg, encodeMsg
+	) where
+
+import Control.Applicative
+import Control.Lens
+import Control.Monad
+import Data.Aeson
+import Data.Maybe
+import Data.ByteString.Lazy.Char8 (ByteString)
+
+import Data.Lisp
+
+type Msg a = (Bool, a)
+
+isLisp :: Lens' (Msg a) Bool
+isLisp = _1
+
+msg :: Lens (Msg a) (Msg b) a b
+msg = _2
+
+jsonMsg :: a -> Msg a
+jsonMsg = (,) False
+
+lispMsg :: a -> Msg a
+lispMsg = (,) True
+
+-- | Decode lisp or json
+decodeMsg :: FromJSON a => ByteString -> Either (Msg String) (Msg a)
+decodeMsg bstr = over _Left decodeType' decodeMsg' where
+	decodeType'
+		| isLisp' = lispMsg
+		| otherwise = jsonMsg
+	decodeMsg' = (lispMsg <$> decodeLisp bstr) <|> (jsonMsg <$> eitherDecode bstr)
+	isLisp' = fromMaybe False $ mplus (try' eitherDecode False) (try' decodeLisp True)
+	try' :: (ByteString -> Either String Value) -> Bool -> Maybe Bool
+	try' f l = either (const Nothing) (const $ Just l) $ f bstr
+
+-- | Encode lisp or json
+encodeMsg :: ToJSON a => Msg a -> ByteString
+encodeMsg m
+	| view isLisp m = encodeLisp $ view msg m
+	| otherwise = encode $ view msg m
src/HsDev/Server/Types.hs view
@@ -1,792 +1,813 @@-{-# LANGUAGE OverloadedStrings, CPP, TypeSynonymInstances, FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts, UndecidableInstances, MultiParamTypeClasses, TypeFamilies, ConstraintKinds #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Server.Types (-	ServerMonadBase,-	SessionLog(..), Session(..), SessionMonad(..), askSession, ServerM(..),-	CommandOptions(..), CommandMonad(..), askOptions, ClientM(..),-	withSession, serverListen, serverSetLogLevel, serverWait, serverUpdateDB, serverWriteCache, serverReadCache, inSessionGhc, serverExit, commandRoot, commandNotify, commandLink, commandHold,-	ServerCommand(..), ConnectionPort(..), ServerOpts(..), silentOpts, ClientOpts(..), serverOptsArgs, Request(..),--	Command(..), AddedContents(..),-	AutoFixCommand(..),-	FileSource(..), TargetFilter(..), SearchQuery(..), SearchType(..),-	FromCmd(..),-	) where--import Control.Applicative-import Control.Concurrent.Worker-import Control.Lens (each, view, set)-import Control.Monad.Base-import Control.Monad.Catch-import Control.Monad.Except-import Control.Monad.Reader-import Control.Monad.Trans.Control-import Data.Aeson hiding (Result(..), Error)-import qualified Data.Aeson.Types as A-import qualified Data.ByteString.Lazy.Char8 as L-import Data.Default-import Data.Maybe (fromMaybe)-import Data.Monoid-import Data.Foldable (asum)-import Options.Applicative-import System.Log.Simple--import System.Directory.Paths-import Text.Format (Formattable(..))--import HsDev.Database-import qualified HsDev.Database.Async as DB-import HsDev.Error (hsdevError)-import HsDev.Project-import HsDev.Symbols-import HsDev.Server.Message-import HsDev.Watcher.Types (Watcher)-import HsDev.Tools.Ghc.Worker (GhcWorker, GhcM)-import HsDev.Tools.Types (Note, OutputMessage)-import HsDev.Tools.AutoFix (Correction)-import HsDev.Types (HsDevError(..))-import HsDev.Util--#if mingw32_HOST_OS-import System.Win32.FileMapping.NamePool (Pool)-#endif--type ServerMonadBase m = (MonadIO m, MonadMask m, MonadBaseControl IO m, Alternative m, MonadPlus m)--data SessionLog = SessionLog {-	sessionLogger :: Log,-	sessionListenLog :: IO [String],-	sessionLogWait :: IO () }--data Session = Session {-	sessionDatabase :: DB.Async Database,-	sessionWriteCache :: Database -> ServerM IO (),-	sessionReadCache :: (FilePath -> ExceptT String IO Structured) -> ServerM IO (Maybe Database),-	sessionLog :: SessionLog,-	sessionWatcher :: Watcher,-#if mingw32_HOST_OS-	sessionMmapPool :: Maybe Pool,-#endif-	sessionGhc :: GhcWorker,-	sessionExit :: IO (),-	sessionWait :: IO (),-	sessionDefines :: [(String, String)] }--class (ServerMonadBase m, MonadLog m) => SessionMonad m where-	getSession :: m Session--askSession :: SessionMonad m => (Session -> a) -> m a-askSession f = liftM f getSession--newtype ServerM m a = ServerM { runServerM :: ReaderT Session m a }-	deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadReader Session, MonadTrans, MonadThrow, MonadCatch, MonadMask)--instance (MonadIO m, MonadMask m) => MonadLog (ServerM m) where-	askLog = ServerM $ asks (sessionLogger . sessionLog)-	localLog fn = ServerM . local setLog' . runServerM where-		setLog' sess = sess { sessionLog = (sessionLog sess) { sessionLogger = fn (sessionLogger (sessionLog sess)) } }--instance ServerMonadBase m => SessionMonad (ServerM m) where-	getSession = ask--instance MonadBase b m => MonadBase b (ServerM m) where-	liftBase = ServerM . liftBase--instance MonadBaseControl b m => MonadBaseControl b (ServerM m) where-	type StM (ServerM m) a = StM (ReaderT Session m) a-	liftBaseWith f = ServerM $ liftBaseWith (\f' -> f (f' . runServerM))-	restoreM = ServerM . restoreM--data CommandOptions = CommandOptions {-	commandOptionsRoot :: FilePath,-	commandOptionsNotify :: Notification -> IO (),-	commandOptionsLink :: IO (),-	commandOptionsHold :: IO () }--instance Default CommandOptions where-	def = CommandOptions "." (const $ return ()) (return ()) (return ())--class (SessionMonad m, MonadPlus m) => CommandMonad m where-	getOptions :: m CommandOptions--askOptions :: CommandMonad m => (CommandOptions -> a) -> m a-askOptions f = liftM f getOptions--newtype ClientM m a = ClientM { runClientM :: ServerM (ReaderT CommandOptions m) a }-	deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadThrow, MonadCatch, MonadMask)--instance MonadTrans ClientM where-	lift = ClientM . lift . lift--instance (MonadIO m, MonadMask m) => MonadLog (ClientM m) where-	askLog = ClientM askLog-	localLog fn = ClientM . localLog fn . runClientM--instance ServerMonadBase m => SessionMonad (ClientM m) where-	getSession = ClientM getSession--instance ServerMonadBase m => CommandMonad (ClientM m) where-	getOptions = ClientM $ lift ask--instance MonadBase b m => MonadBase b (ClientM m) where-	liftBase = ClientM . liftBase--instance MonadBaseControl b m => MonadBaseControl b (ClientM m) where-	type StM (ClientM m) a = StM (ServerM (ReaderT CommandOptions m)) a-	liftBaseWith f = ClientM $ liftBaseWith (\f' -> f (f' . runClientM))-	restoreM = ClientM . restoreM---- | Run action on session-withSession :: Session -> ServerM m a -> m a-withSession s act = runReaderT (runServerM act) s---- | Listen server's log-serverListen :: SessionMonad m => m [String]-serverListen = join . liftM liftIO $ askSession (sessionListenLog . sessionLog)---- | Set server's log config-serverSetLogLevel :: SessionMonad m => Level -> m Level-serverSetLogLevel lev = do-	l <- askSession (sessionLogger . sessionLog)-	cfg <- updateLogConfig l (set (componentCfg "") (Just lev))-	return $ fromMaybe def $ view (componentCfg "") cfg---- | Wait for server-serverWait :: SessionMonad m => m ()-serverWait = join . liftM liftIO $ askSession sessionWait---- | Update database-serverUpdateDB :: SessionMonad m => Database -> m ()-serverUpdateDB db = askSession sessionDatabase >>= (`DB.update` return db)---- | Server write cache-serverWriteCache :: SessionMonad m => Database -> m ()-serverWriteCache db = do-	s <- getSession-	write' <- askSession sessionWriteCache-	liftIO $ withSession s $ write' db---- | Server read cache-serverReadCache :: SessionMonad m => (FilePath -> ExceptT String IO Structured) -> m (Maybe Database)-serverReadCache act = do-	s <- getSession-	read' <- askSession sessionReadCache-	liftIO $ withSession s $ read' act---- | In ghc session-inSessionGhc :: SessionMonad m => GhcM a -> m a-inSessionGhc act = do-	ghcw <- askSession sessionGhc-	inWorkerWith (hsdevError . GhcError . displayException) ghcw act---- | Exit session-serverExit :: SessionMonad m => m ()-serverExit = join . liftM liftIO $ askSession sessionExit--commandRoot :: CommandMonad m => m FilePath-commandRoot = askOptions commandOptionsRoot--commandNotify :: CommandMonad m => Notification -> m ()-commandNotify n = join . liftM liftIO $ askOptions commandOptionsNotify <*> pure n--commandLink :: CommandMonad m => m ()-commandLink = join . liftM liftIO $ askOptions commandOptionsLink--commandHold :: CommandMonad m => m ()-commandHold = join . liftM liftIO $ askOptions commandOptionsHold---- | Server control command-data ServerCommand =-	Version |-	Start ServerOpts |-	Run ServerOpts |-	Stop ClientOpts |-	Connect ClientOpts |-	Remote ClientOpts Bool Command-		deriving (Show)--data ConnectionPort = NetworkPort Int | UnixPort String deriving (Eq, Read)--instance Default ConnectionPort where-	def = NetworkPort 4567--instance Show ConnectionPort where-	show (NetworkPort p) = show p-	show (UnixPort s) = "unix " ++ s--instance Formattable ConnectionPort---- | Server options-data ServerOpts = ServerOpts {-	serverPort :: ConnectionPort,-	serverTimeout :: Int,-	serverLog :: Maybe FilePath,-	serverLogLevel :: String,-	serverCache :: Maybe FilePath,-	serverLoad :: Bool,-	serverSilent :: Bool }-		deriving (Show)--instance Default ServerOpts where-	def = ServerOpts def 0 Nothing "info" Nothing False False---- | Silent server with no connection, useful for ghci-silentOpts :: ServerOpts-silentOpts = def { serverSilent = True }---- | Client options-data ClientOpts = ClientOpts {-	clientPort :: ConnectionPort,-	clientPretty :: Bool,-	clientStdin :: Bool,-	clientTimeout :: Int,-	clientSilent :: Bool }-		deriving (Show)--instance Default ClientOpts where-	def = ClientOpts def False False 0 False--instance FromCmd ServerCommand where-	cmdP = serv <|> remote where-		serv = subparser $ mconcat [-			cmd "version" "hsdev version" (pure Version),-			cmd "start" "start remote server" (Start <$> cmdP),-			cmd "run" "run server" (Run <$> cmdP),-			cmd "stop" "stop remote server" (Stop <$> cmdP),-			cmd "connect" "connect to send commands directly" (Connect <$> cmdP)]-		remote = Remote <$> cmdP <*> noFileFlag <*> cmdP--instance FromCmd ServerOpts where-	cmdP = ServerOpts <$>-		(connectionArg <|> pure (serverPort def)) <*>-		(timeoutArg <|> pure (serverTimeout def)) <*>-		optional logArg <*>-		(logLevelArg <|> pure (serverLogLevel def)) <*>-		optional cacheArg <*>-		loadFlag <*>-		serverSilentFlag--instance FromCmd ClientOpts where-	cmdP = ClientOpts <$>-		(connectionArg <|> pure (clientPort def)) <*>-		prettyFlag <*>-		stdinFlag <*>-		(timeoutArg <|> pure (clientTimeout def)) <*>-		silentFlag--portArg :: Parser ConnectionPort-connectionArg :: Parser ConnectionPort-timeoutArg :: Parser Int-logArg :: Parser FilePath-logLevelArg :: Parser String-cacheArg :: Parser FilePath-noFileFlag :: Parser Bool-loadFlag :: Parser Bool-prettyFlag :: Parser Bool-serverSilentFlag :: Parser Bool-stdinFlag :: Parser Bool-silentFlag :: Parser Bool--portArg = NetworkPort <$> option auto (long "port" <> metavar "number" <> help "connection port")-#if mingw32_HOST_OS-connectionArg = portArg-#else-unixArg :: Parser ConnectionPort-unixArg = UnixPort <$> strOption (long "unix" <> metavar "name" <> help "unix connection port")-connectionArg = portArg <|> unixArg-#endif-timeoutArg = option auto (long "timeout" <> metavar "msec" <> help "query timeout")-logArg = strOption (long "log" <> short 'l' <> metavar "file" <> help "log file")-logLevelArg = strOption (long "log-level" <> metavar "level" <> help "log level: trace/debug/info/warning/error/fatal")-cacheArg = strOption (long "cache" <> metavar "path" <> help "cache directory")-noFileFlag = switch (long "no-file" <> help "don't use mmap files")-loadFlag = switch (long "load" <> help "force load all data from cache on startup")-prettyFlag = switch (long "pretty" <> help "pretty json output")-serverSilentFlag = switch (long "silent" <> help "no stdout/stderr")-stdinFlag = switch (long "stdin" <> help "pass data to stdin")-silentFlag = switch (long "silent" <> help "supress notifications")--serverOptsArgs :: ServerOpts -> [String]-serverOptsArgs sopts = concat [-	portArgs (serverPort sopts),-	["--timeout", show $ serverTimeout sopts],-	marg "--log" (serverLog sopts),-	["--log-level", serverLogLevel sopts],-	marg "--cache" (serverCache sopts),-	["--load" | serverLoad sopts],-	["--silent" | serverSilent sopts]]-	where-		marg :: String -> Maybe String -> [String]-		marg n (Just v) = [n, v]-		marg _ _ = []-		portArgs :: ConnectionPort -> [String]-		portArgs (NetworkPort n) = ["--port", show n]-		portArgs (UnixPort s) = ["--unix", s]--data Request = Request {-	requestCommand :: Command,-	requestDirectory :: FilePath,-	requestNoFile :: Bool,-	requestTimeout :: Int,-	requestSilent :: Bool }-		deriving (Show)--instance ToJSON Request where-	toJSON (Request c dir f tm s) = object ["current-directory" .= dir, "no-file" .= f, "timeout" .= tm, "silent" .= s] `objectUnion` toJSON c--instance FromJSON Request where-	parseJSON = withObject "request" $ \v -> Request <$>-		parseJSON (Object v) <*>-		((v .:: "current-directory") <|> pure ".") <*>-		((v .:: "no-file") <|> pure False) <*>-		((v .:: "timeout") <|> pure 0) <*>-		((v .:: "silent") <|> pure False)---- | Command from client-data Command =-	Ping |-	Listen (Maybe String) |-	SetLogLevel String |-	AddData { addedContents :: [AddedContents] } |-	Scan {-		scanProjects :: [FilePath],-		scanCabal :: Bool,-		scanSandboxes :: [FilePath],-		scanFiles :: [FileSource],-		scanPaths :: [FilePath],-		scanGhcOpts :: [String],-		scanDocs :: Bool,-		scanInferTypes :: Bool } |-	RefineDocs {-		docsProjects :: [FilePath],-		docsFiles :: [FilePath],-		docsModules :: [String] } |-	InferTypes {-		inferProjects :: [FilePath],-		inferFiles :: [FilePath],-		inferModules :: [String] } |-	Remove {-		removeProjects :: [FilePath],-		removeCabal :: Bool,-		removeSandboxes :: [FilePath],-		removeFiles :: [FilePath] } |-	RemoveAll |-	InfoModules [TargetFilter] |-	InfoPackages |-	InfoProjects |-	InfoSandboxes |-	InfoSymbol SearchQuery [TargetFilter] Bool |-	InfoModule SearchQuery [TargetFilter] |-	InfoResolve FilePath Bool |-	InfoProject (Either String FilePath) |-	InfoSandbox FilePath |-	Lookup String FilePath |-	Whois String FilePath |-	ResolveScopeModules SearchQuery FilePath |-	ResolveScope SearchQuery Bool FilePath |-	Complete String Bool FilePath |-	Hayoo {-		hayooQuery :: String,-		hayooPage :: Int,-		hayooPages :: Int } |-	CabalList { cabalListPackages :: [String] } |-	Lint {-		lintFiles :: [FileSource] } |-	Check {-		checkFiles :: [FileSource],-		checkGhcOpts :: [String] } |-	CheckLint {-		checkLintFiles :: [FileSource],-		checkLintGhcOpts :: [String] } |-	Types {-		typesFiles :: [FileSource],-		typesGhcOpts :: [String] } |-	AutoFix { autoFixCommand :: AutoFixCommand } |-	GhcEval { ghcEvalExpressions :: [String], ghcEvalSource :: Maybe FileSource } |-	Langs |-	Flags |-	Link { linkHold :: Bool } |-	Exit-		deriving (Show)--data AddedContents =-	AddedDatabase Database |-	AddedModule InspectedModule |-	AddedProject Project--instance Show AddedContents where-	show = L.unpack . encode--data AutoFixCommand =-	AutoFixShow [Note OutputMessage] |-	AutoFixFix [Note Correction] [Note Correction] Bool-		deriving (Show)--data FileSource = FileSource { fileSource :: FilePath, fileContents :: Maybe String } deriving (Show)-data TargetFilter =-	TargetProject String |-	TargetFile FilePath |-	TargetModule String |-	TargetDepsOf String |-	TargetPackageDb PackageDb |-	TargetCabal |-	TargetSandbox FilePath |-	TargetPackage String |-	TargetSourced |-	TargetStandalone-		deriving (Eq, Show)-data SearchQuery = SearchQuery String SearchType deriving (Show)-data SearchType = SearchExact | SearchPrefix | SearchInfix | SearchSuffix | SearchRegex deriving (Show)--instance Paths Command where-	paths f (Scan projs c cs fs ps ghcs docs infer) = Scan <$>-		each f projs <*>-		pure c <*>-		(each . paths) f cs <*>-		(each . paths) f fs <*>-		each f ps <*>-		pure ghcs <*>-		pure docs <*>-		pure infer-	paths f (RefineDocs projs fs ms) = RefineDocs <$> each f projs <*> each f fs <*> pure ms-	paths f (InferTypes projs fs ms) = InferTypes <$> each f projs <*> each f fs <*> pure ms-	paths f (Remove projs c cs fs) = Remove <$> each f projs <*> pure c <*> (each . paths) f cs <*> each f fs-	paths _ RemoveAll = pure RemoveAll-	paths f (InfoModules t) = InfoModules <$> paths f t-	paths f (InfoSymbol q t l) = InfoSymbol <$> pure q <*> paths f t <*> pure l-	paths f (InfoModule q t) = InfoModule <$> pure q <*> paths f t-	paths f (InfoResolve fpath es) = InfoResolve <$> f fpath <*> pure es-	paths f (InfoProject (Right proj)) = InfoProject <$> (Right <$> f proj)-	paths f (InfoSandbox fpath) = InfoSandbox <$> f fpath-	paths f (Lookup n fpath) = Lookup <$> pure n <*> f fpath-	paths f (Whois n fpath) = Whois <$> pure n <*> f fpath-	paths f (ResolveScopeModules q fpath) = ResolveScopeModules q <$> f fpath-	paths f (ResolveScope q g fpath) = ResolveScope q g <$> f fpath-	paths f (Complete n g fpath) = Complete n g <$> f fpath-	paths f (Lint fs) = Lint <$> (each . paths) f fs-	paths f (Check fs ghcs) = Check <$> (each . paths) f fs <*> pure ghcs-	paths f (CheckLint fs ghcs) = CheckLint <$> (each . paths) f fs <*> pure ghcs-	paths f (Types fs ghcs) = Types <$> (each . paths) f fs <*> pure ghcs-	paths f (GhcEval e mf) = GhcEval e <$> traverse (paths f) mf-	paths _ c = pure c--instance Paths FileSource where-	paths f (FileSource fpath mcts) = FileSource <$> f fpath <*> pure mcts--instance Paths TargetFilter where-	paths f (TargetFile fpath) = TargetFile <$> f fpath-	paths f (TargetPackageDb pdb) = TargetPackageDb <$> paths f pdb-	paths f (TargetSandbox c) = TargetSandbox <$> paths f c-	paths _ t = pure t--instance Paths [TargetFilter] where-	paths = each . paths--instance FromCmd Command where-	cmdP = subparser $ mconcat [-		cmd "ping" "ping server" (pure Ping),-		cmd "listen" "listen server log" (Listen <$> optional logLevelArg),-		cmd "set-log" "set log level" (SetLogLevel <$> strArgument idm),-		cmd "add" "add info to database" (AddData <$> option readJSON idm),-		cmd "scan" "scan sources" $ Scan <$>-			many projectArg <*>-			cabalFlag <*>-			many sandboxArg <*>-			many cmdP <*>-			many (pathArg $ help "path") <*>-			ghcOpts <*>-			docsFlag <*>-			inferFlag,-		cmd "docs" "scan docs" $ RefineDocs <$> many projectArg <*> many fileArg <*> many moduleArg,-		cmd "infer" "infer types" $ InferTypes <$> many projectArg <*> many fileArg <*> many moduleArg,-		cmd "remove" "remove modules info" $ Remove <$>-			many projectArg <*>-			cabalFlag <*>-			many sandboxArg <*>-			many fileArg,-		cmd "remove-all" "remove all data" (pure RemoveAll),-		cmd "modules" "list modules" (InfoModules <$> many cmdP),-		cmd "packages" "list packages" (pure InfoPackages),-		cmd "projects" "list projects" (pure InfoProjects),-		cmd "sandboxes" "list sandboxes" (pure InfoSandboxes),-		cmd "symbol" "get symbol info" (InfoSymbol <$> cmdP <*> many cmdP <*> localsFlag),-		cmd "module" "get module info" (InfoModule <$> cmdP <*> many cmdP),-		cmd "resolve" "resolve module scope (or exports)" (InfoResolve <$> fileArg <*> exportsFlag),-		cmd "project" "get project info" (InfoProject <$> ((Left <$> projectArg) <|> (Right <$> pathArg idm))),-		cmd "sandbox" "get sandbox info" (InfoSandbox <$> pathArg (help "locate sandbox in parent of this path")),-		cmd "lookup" "lookup for symbol" (Lookup <$> strArgument idm <*> ctx),-		cmd "whois" "get info for symbol" (Whois <$> strArgument idm <*> ctx),-		cmd "scope" "get declarations accessible from module or within a project" (-			subparser (cmd "modules" "get modules accessible from module or within a project" (ResolveScopeModules <$> cmdP <*> ctx)) <|>-			ResolveScope <$> cmdP <*> globalFlag <*> ctx),-		cmd "complete" "show completions for input" (Complete <$> strArgument idm <*> wideFlag <*> ctx),-		cmd "hayoo" "find declarations online via Hayoo" (Hayoo <$> strArgument idm <*> hayooPageArg <*> hayooPagesArg),-		cmd "cabal" "cabal commands" (subparser $ cmd "list" "list cabal packages" (CabalList <$> many (strArgument idm))),-		cmd "lint" "lint source files or file contents" (Lint <$> many cmdP),-		cmd "check" "check source files or file contents" (Check <$> many cmdP <*> ghcOpts),-		cmd "check-lint" "check and lint source files or file contents" (CheckLint <$> many cmdP <*> ghcOpts),-		cmd "types" "get types for file expressions" (Types <$> many cmdP <*> ghcOpts),-		cmd "autofix" "autofix commands" (AutoFix <$> cmdP),-		cmd "ghc" "ghc commands" (subparser $ cmd "eval" "evaluate expression" (GhcEval <$> many (strArgument idm) <*> optional cmdP)),-		cmd "langs" "ghc language options" (pure Langs),-		cmd "flags" "ghc flags" (pure Flags),-		cmd "link" "link to server" (Link <$> holdFlag),-		cmd "exit" "exit" (pure Exit)]--instance FromCmd AutoFixCommand where-	cmdP = subparser $ mconcat [-		cmd "show" "generate corrections for check & lint messages" (AutoFixShow <$> option readJSON (long "data" <> metavar "message" <> help "messages to make fixes for")),-		cmd "fix" "fix errors and return rest corrections with updated regions" (AutoFixFix <$>-			option readJSON (long "data" <> metavar "message" <> help "messages to fix") <*>-			option readJSON (long "rest" <> metavar "correction" <> short 'r' <> help "update corrections") <*>-			pureFlag)]--instance FromCmd FileSource where-	cmdP = option readJSON (long "contents") <|> (FileSource <$> fileArg <*> pure Nothing)--instance FromCmd TargetFilter where-	cmdP = asum [-		TargetProject <$> projectArg,-		TargetFile <$> fileArg,-		TargetModule <$> moduleArg,-		TargetDepsOf <$> depsArg,-		TargetPackageDb <$> packageDbArg,-		flag' TargetCabal (long "cabal"),-		TargetSandbox <$> sandboxArg,-		TargetPackage <$> packageArg,-		flag' TargetSourced (long "src"),-		flag' TargetStandalone (long "stand")]--instance FromCmd SearchQuery where-	cmdP = SearchQuery <$> (strArgument idm <|> pure "") <*> asum [-		flag' SearchExact (long "exact"),-		flag' SearchRegex (long "regex"),-		flag' SearchInfix (long "infix"),-		flag' SearchSuffix (long "suffix"),-		pure SearchPrefix <* switch (long "prefix")]--readJSON :: FromJSON a => ReadM a-readJSON = str >>= maybe (readerError "Can't parse JSON argument") return . decode . L.pack--cabalFlag :: Parser Bool-ctx :: Parser FilePath-depsArg :: Parser String-docsFlag :: Parser Bool-exportsFlag :: Parser Bool-fileArg :: Parser FilePath-ghcOpts :: Parser [String]-globalFlag :: Parser Bool-hayooPageArg :: Parser Int-hayooPagesArg :: Parser Int-holdFlag :: Parser Bool-inferFlag :: Parser Bool-localsFlag :: Parser Bool-moduleArg :: Parser String-packageDbArg :: Parser PackageDb-packageArg :: Parser String-pathArg :: Mod OptionFields String -> Parser FilePath-projectArg :: Parser String-pureFlag :: Parser Bool-sandboxArg :: Parser FilePath-wideFlag :: Parser Bool--cabalFlag = switch (long "cabal")-ctx = fileArg-depsArg = strOption (long "deps" <> metavar "object" <> help "filter to such that in dependency of specified object (file or project)")-docsFlag = switch (long "docs" <> help "scan source file docs")-exportsFlag = switch (long "exports" <> short 'e' <> help "resolve module exports")-fileArg = strOption (long "file" <> metavar "path" <> short 'f')-ghcOpts = many (strOption (long "ghc" <> metavar "option" <> short 'g' <> help "options to pass to GHC"))-globalFlag = switch (long "global" <> help "scope of project")-hayooPageArg = option auto (long "page" <> metavar "n" <> short 'p' <> help "page number (0 by default)" <> value 0)-hayooPagesArg = option auto (long "pages" <> metavar "count" <> short 'n' <> help "pages count (1 by default)" <> value 1)-holdFlag = switch (long "hold" <> short 'h' <> help "don't return any response")-inferFlag = switch (long "infer" <> help "infer types")-localsFlag = switch (long "locals" <> short 'l' <> help "look in local declarations")-moduleArg = strOption (long "module" <> metavar "name" <> short 'm' <> help "module name")-packageArg = strOption (long "package" <> metavar "name" <> help "module package")-packageDbArg =-	flag' GlobalDb (long "global-db" <> help "global package-db") <|>-	flag' UserDb (long "user-db" <> help "user package-db") <|>-	(PackageDb <$> strOption (long "package-db" <> metavar "path" <> help "custom package-db"))-pathArg f = strOption (long "path" <> metavar "path" <> short 'p' <> f)-projectArg = strOption (long "project" <> long "proj" <> metavar "project")-pureFlag = switch (long "pure" <> help "don't modify actual file, just return result")-sandboxArg = strOption (long "sandbox" <> metavar "path" <> help "path to cabal sandbox")-wideFlag = switch (long "wide" <> short 'w' <> help "wide mode - complete as if there were no import lists")--instance ToJSON Command where-	toJSON Ping = cmdJson "ping" []-	toJSON (Listen lev) = cmdJson "listen" ["level" .= lev]-	toJSON (SetLogLevel lev) = cmdJson "set-log" ["level" .= lev]-	toJSON (AddData cts) = cmdJson "add" ["data" .= cts]-	toJSON (Scan projs cabal sboxes fs ps ghcs docs' infer') = cmdJson "scan" [-		"projects" .= projs,-		"cabal" .= cabal,-		"sandboxes" .= sboxes,-		"files" .= fs,-		"paths" .= ps,-		"ghc-opts" .= ghcs,-		"docs" .= docs',-		"infer" .= infer']-	toJSON (RefineDocs projs fs ms) = cmdJson "docs" ["projects" .= projs, "files" .= fs, "modules" .= ms]-	toJSON (InferTypes projs fs ms) = cmdJson "infer" ["projects" .= projs, "files" .= fs, "modules" .= ms]-	toJSON (Remove projs cabal sboxes fs) = cmdJson "remove" ["projects" .= projs, "cabal" .= cabal, "sandboxes" .= sboxes, "files" .= fs]-	toJSON RemoveAll = cmdJson "remove-all" []-	toJSON (InfoModules tf) = cmdJson "modules" ["filters" .= tf]-	toJSON InfoPackages = cmdJson "packages" []-	toJSON InfoProjects = cmdJson "projects" []-	toJSON InfoSandboxes = cmdJson "sandboxes" []-	toJSON (InfoSymbol q tf l) = cmdJson "symbol" ["query" .= q, "filters" .= tf, "locals" .= l]-	toJSON (InfoModule q tf) = cmdJson "module" ["query" .= q, "filters" .= tf]-	toJSON (InfoResolve f es) = cmdJson "resolve" ["file" .= f, "exports" .= es]-	toJSON (InfoProject p) = cmdJson "project" $ either (\pname -> ["name" .= pname]) (\ppath -> ["path" .= ppath]) p-	toJSON (InfoSandbox p) = cmdJson "sandbox" ["path" .= p]-	toJSON (Lookup n f) = cmdJson "lookup" ["name" .= n, "file" .= f]-	toJSON (Whois n f) = cmdJson "whois" ["name" .= n, "file" .= f]-	toJSON (ResolveScopeModules q f) = cmdJson "scope modules" ["query" .= q, "file" .= f]-	toJSON (ResolveScope q g f) = cmdJson "scope" ["query" .= q, "global" .= g, "file" .= f]-	toJSON (Complete q w f) = cmdJson "complete" ["prefix" .= q, "wide" .= w, "file" .= f]-	toJSON (Hayoo q p ps) = cmdJson "hayoo" ["query" .= q, "page" .= p, "pages" .= ps]-	toJSON (CabalList ps) = cmdJson "cabal list" ["packages" .= ps]-	toJSON (Lint fs) = cmdJson "lint" ["files" .= fs]-	toJSON (Check fs ghcs) = cmdJson "check" ["files" .= fs, "ghc-opts" .= ghcs]-	toJSON (CheckLint fs ghcs) = cmdJson "check-lint" ["files" .= fs, "ghc-opts" .= ghcs]-	toJSON (Types fs ghcs) = cmdJson "types" ["files" .= fs, "ghc-opts" .= ghcs]-	toJSON (AutoFix acmd) = toJSON acmd-	toJSON (GhcEval exprs f) = cmdJson "ghc eval" ["exprs" .= exprs, "file" .= f]-	toJSON Langs = cmdJson "langs" []-	toJSON Flags = cmdJson "flags" []-	toJSON (Link h) = cmdJson "link" ["hold" .= h]-	toJSON Exit = cmdJson "exit" []--instance FromJSON Command where-	parseJSON = withObject "command" $ \v -> asum [-		guardCmd "ping" v *> pure Ping,-		guardCmd "listen" v *> (Listen <$> v .::? "level"),-		guardCmd "set-log" v *> (SetLogLevel <$> v .:: "level"),-		guardCmd "add" v *> (AddData <$> v .:: "data"),-		guardCmd "scan" v *> (Scan <$>-			v .::?! "projects" <*>-			(v .:: "cabal" <|> pure False) <*>-			v .::?! "sandboxes" <*>-			v .::?! "files" <*>-			v .::?! "paths" <*>-			v .::?! "ghc-opts" <*>-			(v .:: "docs" <|> pure False) <*>-			(v .:: "infer" <|> pure False)),-		guardCmd "docs" v *> (RefineDocs <$> v .::?! "projects" <*> v .::?! "files" <*> v .::?! "modules"),-		guardCmd "infer" v *> (InferTypes <$> v .::?! "projects" <*> v .::?! "files" <*> v .::?! "modules"),-		guardCmd "remove" v *> (Remove <$>-			v .::?! "projects" <*>-			(v .:: "cabal" <|> pure False) <*>-			v .::?! "sandboxes" <*>-			v .::?! "files"),-		guardCmd "remove-all" v *> pure RemoveAll,-		guardCmd "modules" v *> (InfoModules <$> v .::?! "filters"),-		guardCmd "packages" v *> pure InfoPackages,-		guardCmd "projects" v *> pure InfoProjects,-		guardCmd "sandboxes" v *> pure InfoSandboxes,-		guardCmd "symbol" v *> (InfoSymbol <$> v .:: "query" <*> v .::?! "filters" <*> (v .:: "locals" <|> pure False)),-		guardCmd "module" v *> (InfoModule <$> v .:: "query" <*> v .::?! "filters"),-		guardCmd "resolve" v *> (InfoResolve <$> v .:: "file" <*> (v .:: "exports" <|> pure False)),-		guardCmd "project" v *> (InfoProject <$> asum [Left <$> v .:: "name", Right <$> v .:: "path"]),-		guardCmd "sandbox" v *> (InfoSandbox <$> v .:: "path"),-		guardCmd "lookup" v *> (Lookup <$> v .:: "name" <*> v .:: "file"),-		guardCmd "whois" v *> (Whois <$> v .:: "name" <*> v .:: "file"),-		guardCmd "scope modules" v *> (ResolveScopeModules <$> v .:: "query" <*> v .:: "file"),-		guardCmd "scope" v *> (ResolveScope <$> v .:: "query" <*> (v .:: "global" <|> pure False) <*> v .:: "file"),-		guardCmd "complete" v *> (Complete <$> v .:: "prefix" <*> (v .:: "wide" <|> pure False) <*> v .:: "file"),-		guardCmd "hayoo" v *> (Hayoo <$> v .:: "query" <*> (v .:: "page" <|> pure 0) <*> (v .:: "pages" <|> pure 1)),-		guardCmd "cabal list" v *> (CabalList <$> v .::?! "packages"),-		guardCmd "lint" v *> (Lint <$> v .::?! "files"),-		guardCmd "check" v *> (Check <$> v .::?! "files" <*> v .::?! "ghc-opts"),-		guardCmd "check-lint" v *> (CheckLint <$> v .::?! "files" <*> v .::?! "ghc-opts"),-		guardCmd "types" v *> (Types <$> v .::?! "files" <*> v .::?! "ghc-opts"),-		AutoFix <$> parseJSON (Object v),-		guardCmd "ghc eval" v *> (GhcEval <$> v .::?! "exprs" <*> v .::? "file"),-		guardCmd "langs" v *> pure Langs,-		guardCmd "flags" v *> pure Flags,-		guardCmd "link" v *> (Link <$> (v .:: "hold" <|> pure False)),-		guardCmd "exit" v *> pure Exit]--instance ToJSON AddedContents where-	toJSON (AddedDatabase db) = object ["database" .= db]-	toJSON (AddedModule im) = object ["module" .= im]-	toJSON (AddedProject p) = object ["project" .= p]--instance FromJSON AddedContents where-	parseJSON = withObject "added-contents" $ \v -> asum [-		AddedDatabase <$> v .:: "database",-		AddedModule <$> v .:: "module",-		AddedProject <$> v .:: "project"]--instance ToJSON AutoFixCommand where-	toJSON (AutoFixShow ns) = cmdJson "autofix show" ["messages" .= ns]-	toJSON (AutoFixFix ns rests pure') = cmdJson "autofix fix" ["messages" .= ns, "rest" .= rests, "pure" .= pure']--instance FromJSON AutoFixCommand where-	parseJSON = withObject "auto-fix-command" $ \v -> asum [-		guardCmd "autofix show" v *> (AutoFixShow <$> v .:: "messages"),-		guardCmd "autofix fix" v *> (AutoFixFix <$> v .:: "messages" <*> v .::?! "rest" <*> (v .:: "pure" <|> pure True))]--instance ToJSON FileSource where-	toJSON (FileSource fpath mcts) = object ["file" .= fpath, "contents" .= mcts]--instance FromJSON FileSource where-	parseJSON = withObject "file-contents" $ \v -> FileSource <$> v .:: "file" <*> v .::? "contents"--instance ToJSON TargetFilter where-	toJSON (TargetProject pname) = object ["project" .= pname]-	toJSON (TargetFile fpath) = object ["file" .= fpath]-	toJSON (TargetModule mname) = object ["module" .= mname]-	toJSON (TargetDepsOf dep) = object ["deps" .= dep]-	toJSON (TargetPackageDb pdb) = object ["db" .= pdb]-	toJSON TargetCabal = toJSON ("cabal" :: String)-	toJSON (TargetSandbox sbox) = object ["sandbox" .= sbox]-	toJSON (TargetPackage pname) = object ["package" .= pname]-	toJSON TargetSourced = toJSON ("sourced" :: String)-	toJSON TargetStandalone = toJSON ("standalone" :: String)--instance FromJSON TargetFilter where-	parseJSON j = obj j <|> str' where-		obj = withObject "target-filter" $ \v -> asum [-			TargetProject <$> v .:: "project",-			TargetFile <$> v .:: "file",-			TargetModule <$> v .:: "module",-			TargetDepsOf <$> v .:: "deps",-			TargetPackageDb <$> v .:: "db",-			TargetSandbox <$> v .:: "sandbox",-			TargetPackage <$> v .:: "package"]-		str' = do-			s <- parseJSON j :: A.Parser String-			case s of-				"cabal" -> return TargetCabal-				"sourced" -> return TargetSourced-				"standalone" -> return TargetStandalone-				_ -> empty--instance ToJSON SearchQuery where-	toJSON (SearchQuery q st) = object ["input" .= q, "type" .= st]--instance FromJSON SearchQuery where-	parseJSON = withObject "search-query" $ \v -> SearchQuery <$> (v .:: "input" <|> pure "") <*> (v .:: "type" <|> pure SearchPrefix)--instance ToJSON SearchType where-	toJSON SearchExact = toJSON ("exact" :: String)-	toJSON SearchPrefix = toJSON ("prefix" :: String)-	toJSON SearchInfix = toJSON ("infix" :: String)-	toJSON SearchSuffix = toJSON ("suffix" :: String)-	toJSON SearchRegex = toJSON ("regex" :: String)--instance FromJSON SearchType where-	parseJSON v = do-		str' <- parseJSON v :: A.Parser String-		case str' of-			"exact" -> return SearchExact-			"prefix" -> return SearchPrefix-			"infix" -> return SearchInfix-			"suffix" -> return SearchInfix-			"regex" -> return SearchRegex-			_ -> empty+{-# LANGUAGE OverloadedStrings, CPP, TypeSynonymInstances, FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts, UndecidableInstances, MultiParamTypeClasses, TypeFamilies, ConstraintKinds #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Server.Types (
+	ServerMonadBase,
+	SessionLog(..), Session(..), SessionMonad(..), askSession, ServerM(..),
+	CommandOptions(..), CommandMonad(..), askOptions, ClientM(..),
+	withSession, serverListen, serverSetLogLevel, serverWait, serverWaitClients,
+	serverSqlDatabase, openSqlConnection, closeSqlConnection, withSqlConnection, withSqlTransaction, serverSetFileContents, inSessionGhc, inSessionUpdater, serverExit, commandRoot, commandNotify, commandLink, commandHold,
+	ServerCommand(..), ConnectionPort(..), ServerOpts(..), silentOpts, ClientOpts(..), serverOptsArgs, Request(..),
+
+	Command(..),
+	FileSource(..), TargetFilter(..), SearchQuery(..), SearchType(..),
+	FromCmd(..),
+	) where
+
+import Control.Applicative
+import qualified Control.Concurrent.FiniteChan as F
+import Control.Lens (view, set)
+import Control.Monad.Base
+import Control.Monad.Catch
+import Control.Monad.Except
+import Control.Monad.Morph
+import Control.Monad.Reader
+import Control.Monad.State
+import Control.Monad.Writer
+import Control.Monad.Trans.Control
+import Data.Aeson hiding (Result(..), Error)
+import qualified Data.Aeson.Types as A
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Default
+import Data.Maybe (fromMaybe)
+import Data.Foldable (asum)
+import Data.Text (Text)
+import Data.String (fromString)
+import qualified Database.SQLite.Simple as SQL
+import Options.Applicative
+import System.Log.Simple as Log
+
+import Control.Concurrent.Worker
+import System.Directory.Paths
+import Text.Format (Formattable(..))
+
+import HsDev.Error (hsdevError)
+import HsDev.Server.Message
+import HsDev.Watcher.Types (Watcher)
+import HsDev.Tools.Ghc.Worker (GhcWorker, GhcM)
+import HsDev.Tools.Types (Note, OutputMessage)
+import HsDev.Tools.AutoFix (Refact)
+import HsDev.Types (HsDevError(..))
+import HsDev.Util
+
+#if mingw32_HOST_OS
+import System.Win32.FileMapping.NamePool (Pool)
+#endif
+
+type ServerMonadBase m = (MonadIO m, MonadMask m, MonadBaseControl IO m, Alternative m, MonadPlus m)
+
+data SessionLog = SessionLog {
+	sessionLogger :: Log,
+	sessionListenLog :: IO [Log.Message],
+	sessionLogWait :: IO () }
+
+data Session = Session {
+	sessionSqlDatabase :: SQL.Connection,
+	sessionSqlPath :: String,
+	sessionLog :: SessionLog,
+	sessionWatcher :: Watcher,
+	sessionFileContents :: Path -> Maybe Text -> IO (),
+#if mingw32_HOST_OS
+	sessionMmapPool :: Maybe Pool,
+#endif
+	sessionGhc :: GhcWorker,
+	sessionUpdater :: Worker (ServerM IO),
+	sessionExit :: IO (),
+	sessionWait :: IO (),
+	sessionClients :: F.Chan (IO ()),
+	sessionDefines :: [(String, String)] }
+
+class (ServerMonadBase m, MonadLog m) => SessionMonad m where
+	getSession :: m Session
+	localSession :: (Session -> Session) -> m a -> m a
+
+askSession :: SessionMonad m => (Session -> a) -> m a
+askSession f = liftM f getSession
+
+newtype ServerM m a = ServerM { runServerM :: ReaderT Session m a }
+	deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadReader Session, MonadTrans, MonadThrow, MonadCatch, MonadMask)
+
+instance (MonadIO m, MonadMask m) => MonadLog (ServerM m) where
+	askLog = ServerM $ asks (sessionLogger . sessionLog)
+	localLog fn = ServerM . local setLog' . runServerM where
+		setLog' sess = sess { sessionLog = (sessionLog sess) { sessionLogger = fn (sessionLogger (sessionLog sess)) } }
+
+instance ServerMonadBase m => SessionMonad (ServerM m) where
+	getSession = ask
+	localSession = local
+
+instance MonadBase b m => MonadBase b (ServerM m) where
+	liftBase = ServerM . liftBase
+
+instance MonadBaseControl b m => MonadBaseControl b (ServerM m) where
+	type StM (ServerM m) a = StM (ReaderT Session m) a
+	liftBaseWith f = ServerM $ liftBaseWith (\f' -> f (f' . runServerM))
+	restoreM = ServerM . restoreM
+
+instance MFunctor ServerM where
+	hoist fn = ServerM . hoist fn . runServerM
+
+instance SessionMonad m => SessionMonad (ReaderT r m) where
+	getSession = lift getSession
+	localSession = mapReaderT . localSession
+
+instance (SessionMonad m, Monoid w) => SessionMonad (WriterT w m) where
+	getSession = lift getSession
+	localSession = mapWriterT . localSession
+
+instance SessionMonad m => SessionMonad (StateT s m) where
+	getSession = lift getSession
+	localSession = mapStateT . localSession
+
+data CommandOptions = CommandOptions {
+	commandOptionsRoot :: FilePath,
+	commandOptionsNotify :: Notification -> IO (),
+	commandOptionsLink :: IO (),
+	commandOptionsHold :: IO () }
+
+instance Default CommandOptions where
+	def = CommandOptions "." (const $ return ()) (return ()) (return ())
+
+class (SessionMonad m, MonadPlus m) => CommandMonad m where
+	getOptions :: m CommandOptions
+
+askOptions :: CommandMonad m => (CommandOptions -> a) -> m a
+askOptions f = liftM f getOptions
+
+newtype ClientM m a = ClientM { runClientM :: ServerM (ReaderT CommandOptions m) a }
+	deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadThrow, MonadCatch, MonadMask)
+
+instance MonadTrans ClientM where
+	lift = ClientM . lift . lift
+
+instance (MonadIO m, MonadMask m) => MonadLog (ClientM m) where
+	askLog = ClientM askLog
+	localLog fn = ClientM . localLog fn . runClientM
+
+instance ServerMonadBase m => SessionMonad (ClientM m) where
+	getSession = ClientM getSession
+	localSession fn = ClientM . localSession fn . runClientM
+
+instance ServerMonadBase m => CommandMonad (ClientM m) where
+	getOptions = ClientM $ lift ask
+
+instance MonadBase b m => MonadBase b (ClientM m) where
+	liftBase = ClientM . liftBase
+
+instance MonadBaseControl b m => MonadBaseControl b (ClientM m) where
+	type StM (ClientM m) a = StM (ServerM (ReaderT CommandOptions m)) a
+	liftBaseWith f = ClientM $ liftBaseWith (\f' -> f (f' . runClientM))
+	restoreM = ClientM . restoreM
+
+instance MFunctor ClientM where
+	hoist fn = ClientM . hoist (hoist fn) . runClientM
+
+instance CommandMonad m => CommandMonad (ReaderT r m) where
+	getOptions = lift getOptions
+
+instance (CommandMonad m, Monoid w) => CommandMonad (WriterT w m) where
+	getOptions = lift getOptions
+
+instance CommandMonad m => CommandMonad (StateT s m) where
+	getOptions = lift getOptions
+
+-- | Run action on session
+withSession :: Session -> ServerM m a -> m a
+withSession s act = runReaderT (runServerM act) s
+
+-- | Listen server's log
+serverListen :: SessionMonad m => m [Log.Message]
+serverListen = join . liftM liftIO $ askSession (sessionListenLog . sessionLog)
+
+-- | Set server's log config
+serverSetLogLevel :: SessionMonad m => Level -> m Level
+serverSetLogLevel lev = do
+	l <- askSession (sessionLogger . sessionLog)
+	cfg <- updateLogConfig l (set (componentCfg "") (Just lev))
+	return $ fromMaybe def $ view (componentCfg "") cfg
+
+-- | Wait for server
+serverWait :: SessionMonad m => m ()
+serverWait = join . liftM liftIO $ askSession sessionWait
+
+-- | Wait while clients disconnects
+serverWaitClients :: SessionMonad m => m ()
+serverWaitClients = do
+	clientChan <- askSession sessionClients
+	liftIO (F.stopChan clientChan) >>= sequence_ . map liftIO
+
+-- | Get sql connection
+serverSqlDatabase :: SessionMonad m => m SQL.Connection
+serverSqlDatabase = askSession sessionSqlDatabase
+
+-- | Open new sql connection
+openSqlConnection :: SessionMonad m => m SQL.Connection
+openSqlConnection = do
+	p <- askSession sessionSqlPath
+	liftIO $ SQL.open p
+
+-- | Close sql connection
+closeSqlConnection :: SessionMonad m => SQL.Connection -> m ()
+closeSqlConnection = liftIO . SQL.close
+
+-- | Locally opens new connection, updating @Session@
+withSqlConnection :: SessionMonad m => m a -> m a
+withSqlConnection act = bracket openSqlConnection closeSqlConnection $ \conn ->
+	localSession (\sess -> sess { sessionSqlDatabase = conn }) act
+
+-- | With sql transaction
+withSqlTransaction :: SessionMonad m => ServerM IO a -> m a
+withSqlTransaction fn = do
+	conn <- serverSqlDatabase
+	sess <- getSession
+	liftIO $ SQL.withTransaction conn $ withSession sess fn
+
+-- | Set custom file contents
+serverSetFileContents :: SessionMonad m => Path -> Maybe Text -> m ()
+serverSetFileContents fpath mcts = do
+	setCts <- askSession sessionFileContents
+	liftIO $ setCts fpath mcts
+
+-- | In ghc session
+inSessionGhc :: SessionMonad m => GhcM a -> m a
+inSessionGhc act = do
+	ghcw <- askSession sessionGhc
+	inWorkerWith (hsdevError . GhcError . displayException) ghcw act
+
+-- | In updater
+inSessionUpdater :: SessionMonad m => ServerM IO a -> m a
+inSessionUpdater act = do
+	uw <- askSession sessionUpdater
+	inWorkerWith (hsdevError . OtherError . displayException) uw act
+
+-- | Exit session
+serverExit :: SessionMonad m => m ()
+serverExit = join . liftM liftIO $ askSession sessionExit
+
+commandRoot :: CommandMonad m => m FilePath
+commandRoot = askOptions commandOptionsRoot
+
+commandNotify :: CommandMonad m => Notification -> m ()
+commandNotify n = join . liftM liftIO $ askOptions commandOptionsNotify <*> pure n
+
+commandLink :: CommandMonad m => m ()
+commandLink = join . liftM liftIO $ askOptions commandOptionsLink
+
+commandHold :: CommandMonad m => m ()
+commandHold = join . liftM liftIO $ askOptions commandOptionsHold
+
+-- | Server control command
+data ServerCommand =
+	Version |
+	Start ServerOpts |
+	Run ServerOpts |
+	Stop ClientOpts |
+	Connect ClientOpts |
+	Remote ClientOpts Bool Command
+		deriving (Show)
+
+data ConnectionPort = NetworkPort Int | UnixPort String deriving (Eq, Read)
+
+instance Default ConnectionPort where
+	def = NetworkPort 4567
+
+instance Show ConnectionPort where
+	show (NetworkPort p) = show p
+	show (UnixPort s) = "unix " ++ s
+
+instance Formattable ConnectionPort
+
+-- | Server options
+data ServerOpts = ServerOpts {
+	serverPort :: ConnectionPort,
+	serverTimeout :: Int,
+	serverLog :: Maybe FilePath,
+	serverLogLevel :: String,
+	serverDbFile :: Maybe FilePath,
+	serverSilent :: Bool }
+		deriving (Show)
+
+instance Default ServerOpts where
+	def = ServerOpts def 0 Nothing "info" Nothing False
+
+-- | Silent server with no connection, useful for ghci
+silentOpts :: ServerOpts
+silentOpts = def { serverSilent = True }
+
+-- | Client options
+data ClientOpts = ClientOpts {
+	clientPort :: ConnectionPort,
+	clientPretty :: Bool,
+	clientStdin :: Bool,
+	clientTimeout :: Int,
+	clientSilent :: Bool }
+		deriving (Show)
+
+instance Default ClientOpts where
+	def = ClientOpts def False False 0 False
+
+instance FromCmd ServerCommand where
+	cmdP = serv <|> remote where
+		serv = subparser $ mconcat [
+			cmd "version" "hsdev version" (pure Version),
+			cmd "start" "start remote server" (Start <$> cmdP),
+			cmd "run" "run server" (Run <$> cmdP),
+			cmd "stop" "stop remote server" (Stop <$> cmdP),
+			cmd "connect" "connect to send commands directly" (Connect <$> cmdP)]
+		remote = Remote <$> cmdP <*> noFileFlag <*> cmdP
+
+instance FromCmd ServerOpts where
+	cmdP = ServerOpts <$>
+		(connectionArg <|> pure (serverPort def)) <*>
+		(timeoutArg <|> pure (serverTimeout def)) <*>
+		optional logArg <*>
+		(logLevelArg <|> pure (serverLogLevel def)) <*>
+		optional dbFileArg <*>
+		serverSilentFlag
+
+instance FromCmd ClientOpts where
+	cmdP = ClientOpts <$>
+		(connectionArg <|> pure (clientPort def)) <*>
+		prettyFlag <*>
+		stdinFlag <*>
+		(timeoutArg <|> pure (clientTimeout def)) <*>
+		silentFlag
+
+portArg :: Parser ConnectionPort
+connectionArg :: Parser ConnectionPort
+timeoutArg :: Parser Int
+logArg :: Parser FilePath
+logLevelArg :: Parser String
+noFileFlag :: Parser Bool
+prettyFlag :: Parser Bool
+serverSilentFlag :: Parser Bool
+stdinFlag :: Parser Bool
+silentFlag :: Parser Bool
+dbFileArg :: Parser FilePath
+
+portArg = NetworkPort <$> option auto (long "port" <> metavar "number" <> help "connection port")
+#if mingw32_HOST_OS
+connectionArg = portArg
+#else
+unixArg :: Parser ConnectionPort
+unixArg = UnixPort <$> strOption (long "unix" <> metavar "name" <> help "unix connection port")
+connectionArg = portArg <|> unixArg
+#endif
+timeoutArg = option auto (long "timeout" <> metavar "msec" <> help "query timeout")
+logArg = strOption (long "log" <> short 'l' <> metavar "file" <> help "log file")
+logLevelArg = strOption (long "log-level" <> metavar "level" <> help "log level: trace/debug/info/warning/error/fatal")
+noFileFlag = switch (long "no-file" <> help "don't use mmap files")
+prettyFlag = switch (long "pretty" <> help "pretty json output")
+serverSilentFlag = switch (long "silent" <> help "no stdout/stderr")
+stdinFlag = switch (long "stdin" <> help "pass data to stdin")
+silentFlag = switch (long "silent" <> help "supress notifications")
+dbFileArg = strOption (long "db" <> metavar "path" <> help "path to sql database")
+
+serverOptsArgs :: ServerOpts -> [String]
+serverOptsArgs sopts = concat [
+	portArgs (serverPort sopts),
+	["--timeout", show $ serverTimeout sopts],
+	marg "--log" (serverLog sopts),
+	["--log-level", serverLogLevel sopts],
+	marg "--db" (serverDbFile sopts),
+	["--silent" | serverSilent sopts]]
+	where
+		marg :: String -> Maybe String -> [String]
+		marg n (Just v) = [n, v]
+		marg _ _ = []
+		portArgs :: ConnectionPort -> [String]
+		portArgs (NetworkPort n) = ["--port", show n]
+		portArgs (UnixPort s) = ["--unix", s]
+
+data Request = Request {
+	requestCommand :: Command,
+	requestDirectory :: FilePath,
+	requestNoFile :: Bool,
+	requestTimeout :: Int,
+	requestSilent :: Bool }
+		deriving (Show)
+
+instance ToJSON Request where
+	toJSON (Request c dir f tm s) = object ["current-directory" .= dir, "no-file" .= f, "timeout" .= tm, "silent" .= s] `objectUnion` toJSON c
+
+instance FromJSON Request where
+	parseJSON = withObject "request" $ \v -> Request <$>
+		parseJSON (Object v) <*>
+		((v .:: "current-directory") <|> pure ".") <*>
+		((v .:: "no-file") <|> pure False) <*>
+		((v .:: "timeout") <|> pure 0) <*>
+		((v .:: "silent") <|> pure False)
+
+-- | Command from client
+data Command =
+	Ping |
+	Listen (Maybe String) |
+	SetLogLevel String |
+	Scan {
+		scanProjects :: [Path],
+		scanCabal :: Bool,
+		scanSandboxes :: [Path],
+		scanFiles :: [FileSource],
+		scanPaths :: [Path],
+		scanGhcOpts :: [String],
+		scanDocs :: Bool,
+		scanInferTypes :: Bool } |
+	SetFileContents Path (Maybe Text) |
+	RefineDocs {
+		docsProjects :: [Path],
+		docsFiles :: [Path] } |
+	InferTypes {
+		inferProjects :: [Path],
+		inferFiles :: [Path] } |
+	Remove {
+		removeProjects :: [Path],
+		removeCabal :: Bool,
+		removeSandboxes :: [Path],
+		removeFiles :: [Path] } |
+	RemoveAll |
+	InfoPackages |
+	InfoProjects |
+	InfoSandboxes |
+	InfoSymbol SearchQuery [TargetFilter] Bool Bool |
+	InfoModule SearchQuery [TargetFilter] Bool Bool |
+	InfoProject (Either Text Path) |
+	InfoSandbox Path |
+	Lookup Text Path |
+	Whois Text Path |
+	Whoat Int Int Path |
+	ResolveScopeModules SearchQuery Path |
+	ResolveScope SearchQuery Path |
+	FindUsages Text |
+	Complete Text Bool Path |
+	Hayoo {
+		hayooQuery :: String,
+		hayooPage :: Int,
+		hayooPages :: Int } |
+	CabalList { cabalListPackages :: [Text] } |
+	UnresolvedSymbols {
+		unresolvedFiles :: [Path] } |
+	Lint {
+		lintFiles :: [FileSource] } |
+	Check {
+		checkFiles :: [FileSource],
+		checkGhcOpts :: [String],
+		checkClear :: Bool } |
+	CheckLint {
+		checkLintFiles :: [FileSource],
+		checkLintGhcOpts :: [String],
+		checkLinkClear :: Bool } |
+	Types {
+		typesFiles :: [FileSource],
+		typesGhcOpts :: [String],
+		typesClear :: Bool } |
+	AutoFix [Note OutputMessage] |
+	Refactor [Note Refact] [Note Refact] Bool |
+	Rename Text Text Path |
+	GhcEval { ghcEvalExpressions :: [String], ghcEvalSource :: Maybe FileSource } |
+	Langs |
+	Flags |
+	Link { linkHold :: Bool } |
+	StopGhc |
+	Exit
+		deriving (Show)
+
+data FileSource = FileSource { fileSource :: Path, fileContents :: Maybe Text } deriving (Show)
+data TargetFilter =
+	TargetProject Text |
+	TargetFile Path |
+	TargetModule Text |
+	TargetPackage Text |
+	TargetInstalled |
+	TargetSourced |
+	TargetStandalone
+		deriving (Eq, Show)
+data SearchQuery = SearchQuery Text SearchType deriving (Show)
+data SearchType = SearchExact | SearchPrefix | SearchInfix | SearchSuffix deriving (Show)
+
+instance Paths Command where
+	paths f (Scan projs c cs fs ps ghcs docs infer) = Scan <$>
+		traverse (paths f) projs <*>
+		pure c <*>
+		traverse (paths f) cs <*>
+		traverse (paths f) fs <*>
+		traverse (paths f) ps <*>
+		pure ghcs <*>
+		pure docs <*>
+		pure infer
+	paths f (SetFileContents p cts) = SetFileContents <$> paths f p <*> pure cts
+	paths f (RefineDocs projs fs) = RefineDocs <$> traverse (paths f) projs <*> traverse (paths f) fs
+	paths f (InferTypes projs fs) = InferTypes <$> traverse (paths f) projs <*> traverse (paths f) fs
+	paths f (Remove projs c cs fs) = Remove <$> traverse (paths f) projs <*> pure c <*> traverse (paths f) cs <*> traverse (paths f) fs
+	paths _ RemoveAll = pure RemoveAll
+	paths f (InfoSymbol q t h l) = InfoSymbol <$> pure q <*> traverse (paths f) t <*> pure h <*> pure l
+	paths f (InfoModule q t h i) = InfoModule <$> pure q <*> traverse (paths f) t <*> pure h <*> pure i
+	paths f (InfoProject (Right proj)) = InfoProject <$> (Right <$> paths f proj)
+	paths f (InfoSandbox fpath) = InfoSandbox <$> paths f fpath
+	paths f (Lookup n fpath) = Lookup <$> pure n <*> paths f fpath
+	paths f (Whois n fpath) = Whois <$> pure n <*> paths f fpath
+	paths f (Whoat l c fpath) = Whoat <$> pure l <*> pure c <*> paths f fpath
+	paths f (ResolveScopeModules q fpath) = ResolveScopeModules q <$> paths f fpath
+	paths f (ResolveScope q fpath) = ResolveScope q <$> paths f fpath
+	paths _ (FindUsages nm) = pure $ FindUsages nm
+	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 (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 (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 _ c = pure c
+
+instance Paths FileSource where
+	paths f (FileSource fpath mcts) = FileSource <$> paths f fpath <*> pure mcts
+
+instance Paths TargetFilter where
+	paths f (TargetFile fpath) = TargetFile <$> paths f fpath
+	paths _ t = pure t
+
+instance FromCmd Command where
+	cmdP = subparser $ mconcat [
+		cmd "ping" "ping server" (pure Ping),
+		cmd "listen" "listen server log" (Listen <$> optional logLevelArg),
+		cmd "set-log" "set log level" (SetLogLevel <$> strArgument idm),
+		cmd "scan" "scan sources" $ Scan <$>
+			many projectArg <*>
+			cabalFlag <*>
+			many sandboxArg <*>
+			many cmdP <*>
+			many (pathArg $ help "path") <*>
+			ghcOpts <*>
+			docsFlag <*>
+			inferFlag,
+		cmd "set-file-contents" "set edited file contents, which will be used instead of contents in file until it updated" $
+			SetFileContents <$> fileArg <*> optional contentsArg,
+		cmd "docs" "scan docs" $ RefineDocs <$> many projectArg <*> many fileArg,
+		cmd "infer" "infer types" $ InferTypes <$> many projectArg <*> many fileArg,
+		cmd "remove" "remove modules info" $ Remove <$>
+			many projectArg <*>
+			cabalFlag <*>
+			many sandboxArg <*>
+			many fileArg,
+		cmd "remove-all" "remove all data" (pure RemoveAll),
+		cmd "packages" "list packages" (pure InfoPackages),
+		cmd "projects" "list projects" (pure InfoProjects),
+		cmd "sandboxes" "list sandboxes" (pure InfoSandboxes),
+		cmd "symbol" "get symbol info" (InfoSymbol <$> cmdP <*> many cmdP <*> headerFlag <*> localsFlag),
+		cmd "module" "get module info" (InfoModule <$> cmdP <*> many cmdP <*> headerFlag <*> inspectionFlag),
+		cmd "project" "get project info" (InfoProject <$> ((Left <$> projectArg) <|> (Right <$> pathArg idm))),
+		cmd "sandbox" "get sandbox info" (InfoSandbox <$> pathArg (help "locate sandbox in parent of this path")),
+		cmd "lookup" "lookup for symbol" (Lookup <$> textArgument idm <*> ctx),
+		cmd "whois" "get info for symbol" (Whois <$> textArgument idm <*> ctx),
+		cmd "whoat" "get info for symbol under cursor" (Whoat <$> argument auto (metavar "line") <*> argument auto (metavar "column") <*> ctx),
+		cmd "scope" "get declarations accessible from module or within a project" (
+			subparser (cmd "modules" "get modules accessible from module or within a project" (ResolveScopeModules <$> cmdP <*> ctx)) <|>
+			ResolveScope <$> cmdP <*> ctx),
+		cmd "usages" "find usages of fully qualified symbol (qualified with module its defined in)" (FindUsages <$> textArgument idm),
+		cmd "complete" "show completions for input" (Complete <$> textArgument idm <*> wideFlag <*> ctx),
+		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 "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 "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 <$>
+			option readJSON (long "data" <> metavar "message" <> help "messages to fix") <*>
+			option readJSON (long "rest" <> metavar "correction" <> short 'r' <> help "update corrections") <*>
+			pureFlag),
+		cmd "rename" "get rename refactors" (Rename <$> textArgument idm <*> textArgument idm <*> ctx),
+		cmd "ghc" "ghc commands" (subparser $ cmd "eval" "evaluate expression" (GhcEval <$> many (strArgument idm) <*> optional cmdP)),
+		cmd "langs" "ghc language options" (pure Langs),
+		cmd "flags" "ghc flags" (pure Flags),
+		cmd "link" "link to server" (Link <$> holdFlag),
+		cmd "stop-ghc" "stop ghc sessions" (pure StopGhc),
+		cmd "exit" "exit" (pure Exit)]
+
+instance FromCmd FileSource where
+	cmdP = option readJSON (long "contents") <|> (FileSource <$> fileArg <*> pure Nothing)
+
+instance FromCmd TargetFilter where
+	cmdP = asum [
+		TargetProject <$> projectArg,
+		TargetFile <$> fileArg,
+		TargetModule <$> moduleArg,
+		TargetPackage <$> packageArg,
+		flag' TargetInstalled (long "installed"),
+		flag' TargetSourced (long "src"),
+		flag' TargetStandalone (long "stand")]
+
+instance FromCmd SearchQuery where
+	cmdP = SearchQuery <$> (textArgument idm <|> pure "") <*> asum [
+		flag' SearchExact (long "exact"),
+		flag' SearchInfix (long "infix"),
+		flag' SearchSuffix (long "suffix"),
+		pure SearchPrefix <* switch (long "prefix")]
+
+readJSON :: FromJSON a => ReadM a
+readJSON = str >>= maybe (readerError "Can't parse JSON argument") return . decode . L.pack
+
+textOption :: Mod OptionFields String -> Parser Text
+textOption = fmap fromString . strOption
+
+textArgument :: Mod ArgumentFields String -> Parser Text
+textArgument = fmap fromString . strArgument
+
+cabalFlag :: Parser Bool
+clearFlag :: Parser Bool
+contentsArg :: Parser Text
+ctx :: Parser Path
+docsFlag :: Parser Bool
+fileArg :: Parser Path
+ghcOpts :: Parser [String]
+hayooPageArg :: Parser Int
+hayooPagesArg :: Parser Int
+headerFlag :: Parser Bool
+holdFlag :: Parser Bool
+inferFlag :: Parser Bool
+inspectionFlag :: Parser Bool
+localsFlag :: Parser Bool
+moduleArg :: Parser Text
+packageArg :: Parser Text
+pathArg :: Mod OptionFields String -> Parser Path
+projectArg :: Parser Path
+pureFlag :: Parser Bool
+sandboxArg :: Parser Path
+wideFlag :: Parser Bool
+
+cabalFlag = switch (long "cabal")
+clearFlag = switch (long "clear" <> short 'c' <> help "clear run, drop previous state")
+contentsArg = textOption (long "contents" <> help "text contents")
+ctx = fileArg
+docsFlag = switch (long "docs" <> help "scan source file docs")
+fileArg = textOption (long "file" <> metavar "path" <> short 'f')
+ghcOpts = many (strOption (long "ghc" <> metavar "option" <> short 'g' <> help "options to pass to GHC"))
+hayooPageArg = option auto (long "page" <> metavar "n" <> short 'p' <> help "page number (0 by default)" <> value 0)
+hayooPagesArg = option auto (long "pages" <> metavar "count" <> short 'n' <> help "pages count (1 by default)" <> value 1)
+headerFlag = switch (long "header" <> short 'h' <> help "show only header of module")
+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")
+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")
+pathArg f = textOption (long "path" <> metavar "path" <> short 'p' <> f)
+projectArg = textOption (long "project" <> long "proj" <> metavar "project")
+pureFlag = switch (long "pure" <> help "don't modify actual file, just return result")
+sandboxArg = textOption (long "sandbox" <> metavar "path" <> help "path to cabal sandbox")
+wideFlag = switch (long "wide" <> short 'w' <> help "wide mode - complete as if there were no import lists")
+
+instance ToJSON Command where
+	toJSON Ping = cmdJson "ping" []
+	toJSON (Listen lev) = cmdJson "listen" ["level" .= lev]
+	toJSON (SetLogLevel lev) = cmdJson "set-log" ["level" .= lev]
+	toJSON (Scan projs cabal sboxes fs ps ghcs docs' infer') = cmdJson "scan" [
+		"projects" .= projs,
+		"cabal" .= cabal,
+		"sandboxes" .= sboxes,
+		"files" .= fs,
+		"paths" .= ps,
+		"ghc-opts" .= ghcs,
+		"docs" .= docs',
+		"infer" .= infer']
+	toJSON (SetFileContents f cts) = cmdJson "set-file-contents" ["file" .= f, "contents" .= cts]
+	toJSON (RefineDocs projs fs) = cmdJson "docs" ["projects" .= projs, "files" .= fs]
+	toJSON (InferTypes projs fs) = cmdJson "infer" ["projects" .= projs, "files" .= fs]
+	toJSON (Remove projs cabal sboxes fs) = cmdJson "remove" ["projects" .= projs, "cabal" .= cabal, "sandboxes" .= sboxes, "files" .= fs]
+	toJSON RemoveAll = cmdJson "remove-all" []
+	toJSON InfoPackages = cmdJson "packages" []
+	toJSON InfoProjects = cmdJson "projects" []
+	toJSON InfoSandboxes = cmdJson "sandboxes" []
+	toJSON (InfoSymbol q tf h l) = cmdJson "symbol" ["query" .= q, "filters" .= tf, "header" .= h, "locals" .= l]
+	toJSON (InfoModule q tf h i) = cmdJson "module" ["query" .= q, "filters" .= tf, "header" .= h, "inspection" .= i]
+	toJSON (InfoProject p) = cmdJson "project" $ either (\pname -> ["name" .= pname]) (\ppath -> ["path" .= ppath]) p
+	toJSON (InfoSandbox p) = cmdJson "sandbox" ["path" .= p]
+	toJSON (Lookup n f) = cmdJson "lookup" ["name" .= n, "file" .= f]
+	toJSON (Whois n f) = cmdJson "whois" ["name" .= n, "file" .= f]
+	toJSON (Whoat l c f) = cmdJson "whoat" ["line" .= l, "column" .= c, "file" .= f]
+	toJSON (ResolveScopeModules q f) = cmdJson "scope modules" ["query" .= q, "file" .= f]
+	toJSON (ResolveScope q f) = cmdJson "scope" ["query" .= q, "file" .= f]
+	toJSON (FindUsages nm) = cmdJson "usages" ["name" .= nm]
+	toJSON (Complete q w f) = cmdJson "complete" ["prefix" .= q, "wide" .= w, "file" .= f]
+	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 (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 (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']
+	toJSON (Rename n n' f) = cmdJson "rename" ["name" .= n, "new-name" .= n', "file" .= f]
+	toJSON (GhcEval exprs f) = cmdJson "ghc eval" ["exprs" .= exprs, "file" .= f]
+	toJSON Langs = cmdJson "langs" []
+	toJSON Flags = cmdJson "flags" []
+	toJSON (Link h) = cmdJson "link" ["hold" .= h]
+	toJSON StopGhc = cmdJson "stop-ghc" []
+	toJSON Exit = cmdJson "exit" []
+
+instance FromJSON Command where
+	parseJSON = withObject "command" $ \v -> asum [
+		guardCmd "ping" v *> pure Ping,
+		guardCmd "listen" v *> (Listen <$> v .::? "level"),
+		guardCmd "set-log" v *> (SetLogLevel <$> v .:: "level"),
+		guardCmd "scan" v *> (Scan <$>
+			v .::?! "projects" <*>
+			(v .:: "cabal" <|> pure False) <*>
+			v .::?! "sandboxes" <*>
+			v .::?! "files" <*>
+			v .::?! "paths" <*>
+			v .::?! "ghc-opts" <*>
+			(v .:: "docs" <|> pure False) <*>
+			(v .:: "infer" <|> pure False)),
+		guardCmd "set-file-contents" v *> (SetFileContents <$> v .:: "file" <*> v .:: "contents"),
+		guardCmd "docs" v *> (RefineDocs <$> v .::?! "projects" <*> v .::?! "files"),
+		guardCmd "infer" v *> (InferTypes <$> v .::?! "projects" <*> v .::?! "files"),
+		guardCmd "remove" v *> (Remove <$>
+			v .::?! "projects" <*>
+			(v .:: "cabal" <|> pure False) <*>
+			v .::?! "sandboxes" <*>
+			v .::?! "files"),
+		guardCmd "remove-all" v *> pure RemoveAll,
+		guardCmd "packages" v *> pure InfoPackages,
+		guardCmd "projects" v *> pure InfoProjects,
+		guardCmd "sandboxes" v *> pure InfoSandboxes,
+		guardCmd "symbol" v *> (InfoSymbol <$> v .:: "query" <*> v .::?! "filters" <*> v .:: "header" <*> (v .:: "locals" <|> pure False)),
+		guardCmd "module" v *> (InfoModule <$> v .:: "query" <*> v .::?! "filters" <*> v .:: "header" <*> v .:: "inspection"),
+		guardCmd "project" v *> (InfoProject <$> asum [Left <$> v .:: "name", Right <$> v .:: "path"]),
+		guardCmd "sandbox" v *> (InfoSandbox <$> v .:: "path"),
+		guardCmd "lookup" v *> (Lookup <$> v .:: "name" <*> v .:: "file"),
+		guardCmd "whois" v *> (Whois <$> v .:: "name" <*> v .:: "file"),
+		guardCmd "whoat" v *> (Whoat <$> v .:: "line" <*> v .:: "column" <*> v .:: "file"),
+		guardCmd "scope modules" v *> (ResolveScopeModules <$> v .:: "query" <*> v .:: "file"),
+		guardCmd "scope" v *> (ResolveScope <$> v .:: "query" <*> v .:: "file"),
+		guardCmd "usages" v *> (FindUsages <$> v .:: "name"),
+		guardCmd "complete" v *> (Complete <$> v .:: "prefix" <*> (v .:: "wide" <|> pure False) <*> v .:: "file"),
+		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 "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 "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)),
+		guardCmd "rename" v *> (Rename <$> v .:: "name" <*> v .:: "new-name" <*> v .:: "file"),
+		guardCmd "ghc eval" v *> (GhcEval <$> v .::?! "exprs" <*> v .::? "file"),
+		guardCmd "langs" v *> pure Langs,
+		guardCmd "flags" v *> pure Flags,
+		guardCmd "link" v *> (Link <$> (v .:: "hold" <|> pure False)),
+		guardCmd "stop-ghc" v *> pure StopGhc,
+		guardCmd "exit" v *> pure Exit]
+
+instance ToJSON FileSource where
+	toJSON (FileSource fpath mcts) = object ["file" .= fpath, "contents" .= mcts]
+
+instance FromJSON FileSource where
+	parseJSON = withObject "file-contents" $ \v -> FileSource <$> v .:: "file" <*> v .::? "contents"
+
+instance ToJSON TargetFilter where
+	toJSON (TargetProject pname) = object ["project" .= pname]
+	toJSON (TargetFile fpath) = object ["file" .= fpath]
+	toJSON (TargetModule mname) = object ["module" .= mname]
+	toJSON (TargetPackage pname) = object ["package" .= pname]
+	toJSON TargetInstalled = toJSON ("installed" :: String)
+	toJSON TargetSourced = toJSON ("sourced" :: String)
+	toJSON TargetStandalone = toJSON ("standalone" :: String)
+
+instance FromJSON TargetFilter where
+	parseJSON j = obj j <|> str' where
+		obj = withObject "target-filter" $ \v -> asum [
+			TargetProject <$> v .:: "project",
+			TargetFile <$> v .:: "file",
+			TargetModule <$> v .:: "module",
+			TargetPackage <$> v .:: "package"]
+		str' = do
+			s <- parseJSON j :: A.Parser String
+			case s of
+				"installed" -> return TargetInstalled
+				"sourced" -> return TargetSourced
+				"standalone" -> return TargetStandalone
+				_ -> empty
+
+instance ToJSON SearchQuery where
+	toJSON (SearchQuery q st) = object ["input" .= q, "type" .= st]
+
+instance FromJSON SearchQuery where
+	parseJSON = withObject "search-query" $ \v -> SearchQuery <$> (v .:: "input" <|> pure "") <*> (v .:: "type" <|> pure SearchPrefix)
+
+instance ToJSON SearchType where
+	toJSON SearchExact = toJSON ("exact" :: String)
+	toJSON SearchPrefix = toJSON ("prefix" :: String)
+	toJSON SearchInfix = toJSON ("infix" :: String)
+	toJSON SearchSuffix = toJSON ("suffix" :: String)
+
+instance FromJSON SearchType where
+	parseJSON v = do
+		str' <- parseJSON v :: A.Parser String
+		case str' of
+			"exact" -> return SearchExact
+			"prefix" -> return SearchPrefix
+			"infix" -> return SearchInfix
+			"suffix" -> return SearchInfix
+			_ -> empty
src/HsDev/Stack.hs view
@@ -1,131 +1,133 @@-{-# LANGUAGE TemplateHaskell, RankNTypes #-}--module HsDev.Stack (-	stack, yaml,-	path, pathOf,-	build, buildDeps, configure,-	StackEnv(..), stackRoot, stackProject, stackConfig, stackGhc, stackSnapshot, stackLocal,-	getStackEnv, projectEnv,-	stackPackageDbStack,--	stackCompiler, stackArch,--	MaybeT(..)-	) where--import Control.Arrow-import Control.Lens (makeLenses, Lens', at, ix, lens, (^?), (^.))-import Control.Monad-import Control.Monad.Trans.Maybe-import Control.Monad.IO.Class-import Data.Char-import Data.Maybe-import Data.Map (Map)-import qualified Data.Map as M-import Distribution.Compiler-import Distribution.System-import qualified Distribution.Text as T (display)-import System.Directory-import System.Environment-import System.FilePath-import System.Log.Simple (MonadLog(..))--import qualified Packages as GHC--import HsDev.Error-import HsDev.PackageDb-import qualified HsDev.Tools.Ghc.Compat as Compat-import HsDev.Scan.Browse (withPackages)-import HsDev.Util as Util-import HsDev.Tools.Base (runTool_)---- | Get compiler version-stackCompiler :: MonadLog m => m String-stackCompiler = do-	res <- withPackages ["-no-user-package-db"] $-		return .-		map (GHC.packageNameString &&& GHC.packageVersion) .-		fromMaybe [] .-		Compat.pkgDatabase-	let-		compiler = T.display buildCompilerFlavor-		CompilerId _ version' = buildCompilerId-		ver = maybe (T.display version') T.display $ lookup compiler res-	return $ compiler ++ "-" ++ ver---- | Get arch for stack-stackArch :: String-stackArch = T.display buildArch---- | Invoke stack command, we are trying to get actual stack near current hsdev executable-stack :: MonadLog m => [String] -> m String-stack cmd' = hsdevLiftIO $ do-	curExe <- liftIO getExecutablePath-	stackExe <- Util.withCurrentDirectory (takeDirectory curExe) $-		liftIO (findExecutable "stack") >>= maybe (hsdevError $ ToolNotFound "stack") return-	comp <- stackCompiler-	liftIO $ runTool_ stackExe (["--compiler", comp, "--arch", stackArch] ++ cmd')---- | Make yaml opts-yaml :: Maybe FilePath -> [String]-yaml Nothing = []-yaml (Just y) = ["--stack-yaml", y]--type Paths = Map String FilePath---- | Stack path-path :: MonadLog m => Maybe FilePath -> m Paths-path mcfg = liftM (M.fromList . map breakPath . lines) $ stack ("path" : yaml mcfg) where-	breakPath :: String -> (String, FilePath)-	breakPath = second (dropWhile isSpace . drop 1) . break (== ':')---- | Get path for-pathOf :: String -> Lens' Paths (Maybe FilePath)-pathOf = at---- | Build stack project-build :: MonadLog m => [String] -> Maybe FilePath -> m ()-build opts mcfg = void $ stack $ "build" : (opts ++ yaml mcfg)---- | Build only dependencies-buildDeps :: MonadLog m => Maybe FilePath -> m ()-buildDeps = build ["--only-dependencies"]---- | Configure project-configure :: MonadLog m => Maybe FilePath -> m ()-configure = build ["--only-configure"]--data StackEnv = StackEnv {-	_stackRoot :: FilePath,-	_stackProject :: FilePath,-	_stackConfig :: FilePath,-	_stackGhc :: FilePath,-	_stackSnapshot :: FilePath,-	_stackLocal :: FilePath }--makeLenses ''StackEnv--getStackEnv :: Paths -> Maybe StackEnv-getStackEnv p = StackEnv <$>-	(p ^. pathOf "stack-root") <*>-	(p ^. pathOf "project-root") <*>-	(p ^. pathOf "config-location") <*>-	(p ^. pathOf "ghc-paths") <*>-	(p ^. pathOf "snapshot-pkg-db") <*>-	(p ^. pathOf "local-pkg-db")---- | Projects paths-projectEnv :: MonadLog m => FilePath -> m StackEnv-projectEnv p = hsdevLiftIO $ Util.withCurrentDirectory p $ do-	paths' <- path Nothing-	maybe (hsdevError $ ToolError "stack" ("can't get paths for " ++ p)) return $ getStackEnv paths'---- | Get package-db stack for stack environment-stackPackageDbStack :: Lens' StackEnv PackageDbStack-stackPackageDbStack = lens g s where-	g :: StackEnv -> PackageDbStack-	g env' = PackageDbStack $ map PackageDb [_stackLocal env', _stackSnapshot env']-	s :: StackEnv -> PackageDbStack -> StackEnv-	s env' pdbs = env' {-		_stackSnapshot = fromMaybe (_stackSnapshot env') $ pdbs ^? packageDbStack . ix 1 . packageDb,-		_stackLocal = fromMaybe (_stackLocal env') $ pdbs ^? packageDbStack . ix 0 . packageDb }+{-# LANGUAGE TemplateHaskell, RankNTypes #-}
+
+module HsDev.Stack (
+	stack, yaml,
+	path, pathOf,
+	build, buildDeps, configure,
+	StackEnv(..), stackRoot, stackProject, stackConfig, stackGhc, stackSnapshot, stackLocal,
+	getStackEnv, projectEnv,
+	stackPackageDbStack,
+
+	stackCompiler, stackArch,
+
+	MaybeT(..)
+	) where
+
+import Control.Arrow
+import Control.Lens (makeLenses, Lens', at, ix, lens, (^?), (^.))
+import Control.Monad
+import Control.Monad.Trans.Maybe
+import Control.Monad.IO.Class
+import Data.Char
+import Data.Maybe
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import Distribution.Compiler
+import Distribution.System
+import qualified Distribution.Text as T (display)
+import System.Directory
+import System.Environment
+import System.FilePath
+
+import qualified GHC
+import qualified Packages as GHC
+
+import HsDev.Error
+import HsDev.PackageDb
+import HsDev.Tools.Ghc.Worker (GhcM, tmpSession)
+import qualified HsDev.Tools.Ghc.Compat as Compat
+import HsDev.Util as Util
+import HsDev.Tools.Base (runTool_)
+import qualified System.Directory.Paths as P
+
+-- | Get compiler version
+stackCompiler :: GhcM String
+stackCompiler = do
+	tmpSession ["-no-user-package-db"]
+	df <- GHC.getSessionDynFlags
+	let
+		res =
+			map (GHC.packageNameString &&& GHC.packageVersion) .
+			fromMaybe [] .
+			Compat.pkgDatabase $ df
+		compiler = T.display buildCompilerFlavor
+		CompilerId _ version' = buildCompilerId
+		ver = maybe (T.display version') T.display $ lookup compiler res
+	return $ compiler ++ "-" ++ ver
+
+-- | Get arch for stack
+stackArch :: String
+stackArch = T.display buildArch
+
+-- | Invoke stack command, we are trying to get actual stack near current hsdev executable
+stack :: [String] -> GhcM String
+stack cmd' = hsdevLiftIO $ do
+	curExe <- liftIO getExecutablePath
+	stackExe <- Util.withCurrentDirectory (takeDirectory curExe) $
+		liftIO (findExecutable "stack") >>= maybe (hsdevError $ ToolNotFound "stack") return
+	comp <- stackCompiler
+	liftIO $ runTool_ stackExe (["--compiler", comp, "--arch", stackArch] ++ cmd')
+
+-- | Make yaml opts
+yaml :: Maybe FilePath -> [String]
+yaml Nothing = []
+yaml (Just y) = ["--stack-yaml", y]
+
+type PathsConf = Map String FilePath
+
+-- | Stack path
+path :: Maybe FilePath -> GhcM PathsConf
+path mcfg = liftM (M.fromList . map breakPath . lines) $ stack ("path" : yaml mcfg) where
+	breakPath :: String -> (String, FilePath)
+	breakPath = second (dropWhile isSpace . drop 1) . break (== ':')
+
+-- | Get path for
+pathOf :: String -> Lens' PathsConf (Maybe FilePath)
+pathOf = at
+
+-- | Build stack project
+build :: [String] -> Maybe FilePath -> GhcM ()
+build opts mcfg = void $ stack $ "build" : (opts ++ yaml mcfg)
+
+-- | Build only dependencies
+buildDeps :: Maybe FilePath -> GhcM ()
+buildDeps = build ["--only-dependencies"]
+
+-- | Configure project
+configure :: Maybe FilePath -> GhcM ()
+configure = build ["--only-configure"]
+
+data StackEnv = StackEnv {
+	_stackRoot :: FilePath,
+	_stackProject :: FilePath,
+	_stackConfig :: FilePath,
+	_stackGhc :: FilePath,
+	_stackSnapshot :: FilePath,
+	_stackLocal :: FilePath }
+
+makeLenses ''StackEnv
+
+getStackEnv :: PathsConf -> Maybe StackEnv
+getStackEnv p = StackEnv <$>
+	(p ^. pathOf "stack-root") <*>
+	(p ^. pathOf "project-root") <*>
+	(p ^. pathOf "config-location") <*>
+	(p ^. pathOf "ghc-paths") <*>
+	(p ^. pathOf "snapshot-pkg-db") <*>
+	(p ^. pathOf "local-pkg-db")
+
+-- | Projects paths
+projectEnv :: FilePath -> GhcM StackEnv
+projectEnv p = hsdevLiftIO $ Util.withCurrentDirectory p $ do
+	paths' <- path Nothing
+	maybe (hsdevError $ ToolError "stack" ("can't get paths for " ++ p)) return $ getStackEnv paths'
+
+-- | Get package-db stack for stack environment
+stackPackageDbStack :: Lens' StackEnv PackageDbStack
+stackPackageDbStack = lens g s where
+	g :: StackEnv -> PackageDbStack
+	g env' = PackageDbStack $ map (PackageDb . P.fromFilePath) [_stackLocal env', _stackSnapshot env']
+	s :: StackEnv -> PackageDbStack -> StackEnv
+	s env' pdbs = env' {
+		_stackSnapshot = fromMaybe (_stackSnapshot env') $ pdbs ^? packageDbStack . ix 1 . packageDb . P.path,
+		_stackLocal = fromMaybe (_stackLocal env') $ pdbs ^? packageDbStack . ix 0 . packageDb . P.path }
src/HsDev/Symbols.hs view
@@ -1,278 +1,127 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Symbols (-	-- * Information-	export,-	passThingSpec, passImportSpec, imported, exported,-	importNames, import_,-	Symbol(..),-	unnamedModuleId,-	sortDeclarations, moduleLocals,-	setDefinedIn, dropExternals, clearDefinedIn,-	moduleLocalDeclarations, moduleModuleDeclarations,-	Locals(..),-	decl, definedIn, declarationLocals, scopes,-	mergeExported,--	-- * Functions-	importQualifier,--	-- * Utility-	locateProject, searchProject,-	locateSourceDir,-	standaloneInfo,-	moduleOpts,--	-- * Modifiers-	addDeclaration,--	-- * Other-	unalias,--	-- * Tags-	setTag, hasTag, removeTag, dropTags,--	-- * Reexportss-	module HsDev.Symbols.Types,-	module HsDev.Symbols.Class,-	module HsDev.Symbols.Documented-	) where--import Control.Applicative-import Control.Arrow-import Control.Lens-import Control.Monad.Trans.Maybe-import Control.Monad.Except-import Data.Function (on)-import Data.List-import Data.Maybe (fromMaybe, listToMaybe, catMaybes)-import qualified Data.Map as M-import Data.Ord (comparing)-import qualified Data.Set as S-import Data.Text (Text)-import qualified Data.Text as T (concat)-import System.Directory-import System.FilePath--import HsDev.Symbols.Types-import HsDev.Symbols.Class-import HsDev.Symbols.Documented (Documented(..))-import HsDev.Util (searchPath, uniqueBy, directoryContents)---- | Get name of export-export :: Export -> Text-export (ExportName Nothing n _) = n-export (ExportName (Just q) n _) = T.concat [q, ".", n]-export (ExportModule m) = m---- | Does name pass thing spec-passThingSpec :: Text -> ThingPart -> Bool-passThingSpec _ ThingNothing = False-passThingSpec _ ThingAll = True-passThingSpec n (ThingWith ns) = n `elem` ns---- | Does declaration pass import-list spec-passImportSpec :: Declaration -> ImportSpec -> Bool-passImportSpec decl' (ImportSpec n p)-	| view declarationName decl' == n = True-	| preview (declaration . related . _Just) decl' == Just n = view declarationName decl' `passThingSpec` p-	| otherwise = False---- | Check whether declaration passes import list-imported :: Declaration -> ImportList -> Bool-imported decl' (ImportList hiding specs) = invert $ any (decl' `passImportSpec`) specs where-	invert = if hiding then not else id---- | Check whether declaration passes export-exported :: Declaration -> Export -> Bool-exported decl' (ExportName q n p)-	| view declarationName decl' == n = checkImport-	| preview (declaration . related . _Just) decl' == Just n = view declarationName decl' `passThingSpec` p-	| otherwise = False-	where-		checkImport = case q of-			Nothing -> any (not . view importIsQualified) $ fromMaybe [] $ view declarationImported decl'-			Just q' -> any ((q' `elem`) . importNames) $ fromMaybe [] $ view declarationImported decl'-exported decl' (ExportModule m) = any (unqualBy m) . fromMaybe [] . view declarationImported $ decl' where-	unqualBy :: Text -> Import -> Bool-	unqualBy m' i = m' `elem` importNames i && not (view importIsQualified i)---- | Get import module names - full and synonym-importNames :: Import -> [Text]-importNames i = view importModuleName i : maybe [] return (view importAs i)---- | Simple import-import_ :: Text -> Import-import_ n = Import n False Nothing Nothing Nothing---- | Imported module can be accessed via qualifier-importQualifier :: Maybe Text -> Import -> Bool-importQualifier Nothing i-	| not (view importIsQualified i) = True-	| otherwise = False-importQualifier (Just q) i-	| q == view importModuleName i = True-	| Just q == view importAs i = True-	| otherwise = False--unnamedModuleId :: ModuleLocation -> ModuleId-unnamedModuleId = ModuleId ""--sortDeclarations :: [Declaration] -> [Declaration]-sortDeclarations = sortBy (comparing (view declarationName))---- | Bring locals to top-moduleLocals :: Module -> Module-moduleLocals m = set moduleDeclarations (moduleLocalDeclarations m) m---- | Set all declaration `definedIn` to this module-setDefinedIn :: Module -> Module-setDefinedIn m = over moduleDeclarations (map (`definedIn` view moduleId m)) m---- | Drop all declarations, that not defined in this module-dropExternals :: Module -> Module-dropExternals m = over moduleDeclarations (filter ((/= Just (view moduleId m)) . view declarationDefined)) m---- | Clear `definedIn` information-clearDefinedIn :: Module -> Module-clearDefinedIn = over moduleDeclarations (map (set declarationDefined Nothing))---- | Get declarations with locals-moduleLocalDeclarations :: Module -> [Declaration]-moduleLocalDeclarations =-	sortDeclarations .-	concatMap declarationLocals' .-	view moduleDeclarations-	where-		declarationLocals' :: Declaration -> [Declaration]-		declarationLocals' d = d : declarationLocals d---- | Get list of declarations as ModuleDeclaration-moduleModuleDeclarations :: Module -> [ModuleDeclaration]-moduleModuleDeclarations m = [ModuleDeclaration (view moduleId m) d | d <- view moduleDeclarations m]--class Locals a where-	locals :: a -> [Declaration]-	where_ :: a -> [Declaration] -> a--instance Locals Declaration where-	locals = locals . view declaration-	where_ d ds = over declaration (`where_` ds) d--decl :: Text -> DeclarationInfo -> Declaration-decl n = Declaration n Nothing Nothing Nothing Nothing--definedIn :: Declaration -> ModuleId -> Declaration-definedIn d m = set declarationDefined (Just m) d--declarationLocals :: Declaration -> [Declaration]-declarationLocals d = locals $ view declaration d---- | Get scopes of @Declaration@, where @Nothing@ is global scope-scopes :: Declaration -> [Maybe Text]-scopes d = globalScope $ concatMap (map Just . importNames) is where-	is = fromMaybe [] $ view declarationImported d-	globalScope-		| any (not . view importIsQualified) is = (Nothing :)-		| otherwise = id--instance Locals DeclarationInfo where-	locals (Function _ ds _) = ds-	locals _ = []-	where_ (Function n s r) ds = Function n (s ++ ds) r-	where_ d _ = d---- | Merge @ModuleDeclaration@ into @ExportedDeclaration@-mergeExported :: [ModuleDeclaration] -> [ExportedDeclaration]-mergeExported =-	map merge' .-	groupBy ((==) `on` declId) .-	sortBy (comparing declId)-	where-		declId :: ModuleDeclaration -> (Text, Maybe ModuleId)-		declId = view moduleDeclaration >>> (view declarationName &&& view declarationDefined)-		merge' :: [ModuleDeclaration] -> ExportedDeclaration-		merge' [] = error "mergeExported: impossible"-		merge' ds@(d:_) = ExportedDeclaration (map (view declarationModuleId) ds) (view moduleDeclaration d)---- | Find project file is related to-locateProject :: FilePath -> IO (Maybe Project)-locateProject file = do-	file' <- canonicalizePath file-	isDir <- doesDirectoryExist file'-	if isDir then locateHere file' else locateParent (takeDirectory file')-	where-		locateHere path = do-			cts <- filter (not . null . takeBaseName) <$> directoryContents path-			return $ fmap (project . (path </>)) $ find ((== ".cabal") . takeExtension) cts-		locateParent dir = do-			cts <- filter (not . null . takeBaseName) <$> directoryContents dir-			case find ((== ".cabal") . takeExtension) cts of-				Nothing -> if isDrive dir then return Nothing else locateParent (takeDirectory dir)-				Just cabalf -> return $ Just $ project (dir </> cabalf)---- | Search project up-searchProject :: FilePath -> IO (Maybe Project)-searchProject file = runMaybeT $ searchPath file (MaybeT . locateProject) <|> mzero---- | Locate source dir of file-locateSourceDir :: FilePath -> IO (Maybe (Extensions FilePath))-locateSourceDir f = runMaybeT $ do-	file <- liftIO $ canonicalizePath f-	p <- MaybeT $ locateProject file-	proj <- lift $ loadProject p-	MaybeT $ return $ findSourceDir proj file---- | Make `Info` for standalone `Module`-standaloneInfo :: [PackageConfig] -> Module -> Info-standaloneInfo pkgs m = mempty { _infoDepends = pkgDeps ^.. each . package . packageName } where-	pkgDeps = catMaybes [M.lookup mdep pkgMap >>= listToMaybe | mdep <- "Prelude" : (m ^.. moduleImports . each . importModuleName)]-	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)---- | Options for GHC of module and project-moduleOpts :: [PackageConfig] -> Module -> [String]-moduleOpts pkgs m = case view moduleLocation m of-	FileModule file proj -> concat [-		hidePackages,-		targetOpts info']-		where-			infos' = maybe [standaloneInfo pkgs m] (`fileTargets` file) proj-			info' = over infoDepends (filter validDep) (mconcat $ selfInfo : infos')-			selfInfo-				| proj ^? _Just . projectName `elem` map Just (infos' ^.. each . infoDepends . each) = fromMaybe mempty $-					proj ^? _Just . projectDescription . _Just . projectLibrary . _Just . libraryBuildInfo-				| otherwise = mempty-			-- filter out unavailable packages such as unix under windows-			validDep d = d `elem` pkgs'-			pkgs' = pkgs ^.. each . package . packageName-			hidePackages-				| null (info' ^. infoDepends) = []-				| otherwise = ["-hide-all-packages"]-	_ -> []---- | Add declaration to module-addDeclaration :: Declaration -> Module -> Module-addDeclaration decl' = over moduleDeclarations (sortDeclarations . (decl' :))---- | Unalias import name-unalias :: Module -> Text -> [Text]-unalias m alias = [view importModuleName i | i <- view moduleImports m, view importAs i == Just alias]---- | Set tag to `Inspected`-setTag :: Ord t => t -> Inspected i t a -> Inspected i t a-setTag tag = over inspectionTags (S.insert tag)---- | Check whether `Inspected` has tag-hasTag :: Ord t => t -> Inspected i t a -> Bool-hasTag tag = has (inspectionTags . ix tag)---- | Drop tag from `Inspected`-removeTag :: Ord t => t -> Inspected i t a -> Inspected i t a-removeTag tag = over inspectionTags (S.delete tag)---- | Drop all tags-dropTags :: Inspected i t a -> Inspected i t a-dropTags = set inspectionTags S.empty+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Symbols (
+	-- * Utility
+	locateProject, searchProject,
+	locateSourceDir,
+	standaloneInfo,
+	moduleOpts, projectTargetOpts,
+
+	-- * Tags
+	setTag, hasTag, removeTag, dropTags,
+
+	-- * Reexportss
+	module HsDev.Symbols.Types,
+	module HsDev.Symbols.Class,
+	module HsDev.Symbols.Documented,
+	module HsDev.Symbols.HaskellNames
+	) where
+
+import Control.Applicative
+import Control.Lens
+import Control.Monad.Trans.Maybe
+import Control.Monad.Except
+import Data.List
+import Data.Maybe (fromMaybe, listToMaybe, catMaybes)
+import qualified Data.Map.Strict as M
+import qualified Data.Set as S
+import System.Directory
+import System.FilePath
+
+import HsDev.Symbols.Types
+import HsDev.Symbols.Class
+import HsDev.Symbols.Documented (Documented(..))
+import HsDev.Symbols.HaskellNames
+import HsDev.Util (searchPath, uniqueBy, directoryContents)
+import System.Directory.Paths
+
+-- | Find project file is related to
+locateProject :: FilePath -> IO (Maybe Project)
+locateProject file = do
+	file' <- canonicalizePath file
+	isDir <- doesDirectoryExist file'
+	if isDir then locateHere file' else locateParent (takeDirectory file')
+	where
+		locateHere p = do
+			cts <- filter (not . null . takeBaseName) <$> directoryContents p
+			return $ fmap (project . (p </>)) $ find ((== ".cabal") . takeExtension) cts
+		locateParent dir = do
+			cts <- filter (not . null . takeBaseName) <$> directoryContents dir
+			case find ((== ".cabal") . takeExtension) cts of
+				Nothing -> if isDrive dir then return Nothing else locateParent (takeDirectory dir)
+				Just cabalf -> return $ Just $ project (dir </> cabalf)
+
+-- | Search project up
+searchProject :: FilePath -> IO (Maybe Project)
+searchProject file = runMaybeT $ searchPath file (MaybeT . locateProject) <|> mzero
+
+-- | Locate source dir of file
+locateSourceDir :: FilePath -> IO (Maybe (Extensions Path))
+locateSourceDir f = runMaybeT $ do
+	file <- liftIO $ canonicalizePath f
+	p <- MaybeT $ locateProject file
+	proj <- lift $ loadProject p
+	MaybeT $ return $ findSourceDir proj (fromFilePath file)
+
+-- | Make `Info` for standalone `Module`
+standaloneInfo :: [PackageConfig] -> Module -> Info
+standaloneInfo pkgs m = mempty { _infoDepends = pkgDeps ^.. each . package . packageName } where
+	pkgDeps = catMaybes [M.lookup mdep pkgMap >>= listToMaybe | mdep <- "Prelude" : imps]
+	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)
+
+-- | Options for GHC of module and project
+moduleOpts :: [PackageConfig] -> Module -> [String]
+moduleOpts pkgs m = case view (moduleId . moduleLocation) m of
+	FileModule file proj -> concat [
+		hidePackages,
+		targetOpts absInfo]
+		where
+			infos' = maybe [standaloneInfo pkgs m] (`fileTargets` file) proj
+			info' = over infoDepends (filter validDep) (mconcat $ selfInfo : infos')
+			absInfo = maybe id (absolutise . view projectPath) proj info'
+			selfInfo
+				| proj ^? _Just . projectName `elem` map Just (infos' ^.. each . infoDepends . each) = fromMaybe mempty $
+					proj ^? _Just . projectDescription . _Just . projectLibrary . _Just . libraryBuildInfo
+				| otherwise = mempty
+			-- filter out unavailable packages such as unix under windows
+			validDep d = d `elem` pkgs'
+			pkgs' = pkgs ^.. each . package . packageName
+			hidePackages
+				| null (info' ^. infoDepends) = []
+				| otherwise = ["-hide-all-packages"]
+	_ -> []
+
+-- | Options for GHC of project
+projectTargetOpts :: [PackageConfig] -> Project -> Info -> [String]
+projectTargetOpts pkgs proj info = concat [hidePackages, targetOpts absInfo] where
+	info' = over infoDepends (filter validDep) (selfInfo `mappend` info)
+	absInfo = absolutise (view projectPath proj) info'
+	selfInfo
+		| proj ^. projectName `elem` (info ^.. infoDepends . each) = fromMaybe mempty $
+			proj ^? projectDescription . _Just . projectLibrary . _Just . libraryBuildInfo
+		| otherwise = mempty
+	validDep d = d `elem` pkgs'
+	pkgs' = pkgs ^.. each . package . packageName
+	hidePackages
+		| null (info' ^. infoDepends) = []
+		| otherwise = ["-hide-all-packages"]
+
+-- | Set tag to `Inspected`
+setTag :: Ord t => t -> Inspected i t a -> Inspected i t a
+setTag tag' = over inspectionTags (S.insert tag')
+
+-- | Check whether `Inspected` has tag
+hasTag :: Ord t => t -> Inspected i t a -> Bool
+hasTag tag' = has (inspectionTags . ix tag')
+
+-- | Drop tag from `Inspected`
+removeTag :: Ord t => t -> Inspected i t a -> Inspected i t a
+removeTag tag' = over inspectionTags (S.delete tag')
+
+-- | Drop all tags
+dropTags :: Inspected i t a -> Inspected i t a
+dropTags = set inspectionTags S.empty
src/HsDev/Symbols/Class.hs view
@@ -1,20 +1,30 @@-module HsDev.Symbols.Class (-	Symbol(..),-	symbolModuleLocation,--	module HsDev.Symbols.Location-	) where--import Control.Lens (view)-import Data.Text (Text)--import HsDev.Symbols.Location--class Symbol a where-	symbolName :: a -> Text-	symbolQualifiedName :: a -> Text-	symbolDocs :: a -> Maybe Text-	symbolLocation :: a -> Location--symbolModuleLocation :: Symbol a => a -> ModuleLocation-symbolModuleLocation = view locationModule . symbolLocation+module HsDev.Symbols.Class (
+	Sourced(..),
+	sourcedModuleName,
+
+	module HsDev.Symbols.Location
+	) where
+
+import Control.Lens (Lens', Traversal')
+import Data.Text (Text)
+
+import HsDev.Symbols.Location
+
+class Sourced a where
+	sourcedName :: Lens' a Text
+	sourcedDocs :: Traversal' a Text
+	sourcedModule :: Lens' a ModuleId
+	sourcedLocation :: Traversal' a Position
+	sourcedDocs _ = pure
+	sourcedLocation _ = pure
+
+instance Sourced ModuleId where
+	sourcedName = moduleName
+	sourcedModule = id
+
+instance Sourced SymbolId where
+	sourcedName = symbolName
+	sourcedModule = symbolModule
+
+sourcedModuleName :: Sourced a => Lens' a Text
+sourcedModuleName = sourcedModule . sourcedName
src/HsDev/Symbols/Documented.hs view
@@ -1,24 +1,62 @@-module HsDev.Symbols.Documented (-	Documented(..),-	defaultDetailed-	) where--import Data.Text (unpack)--import HsDev.Symbols.Class---- | Documented symbol-class Symbol a => Documented a where-	brief :: a -> String-	detailed :: a -> String-	detailed = unlines . defaultDetailed---- | Default detailed docs-defaultDetailed :: Documented a => a -> [String]-defaultDetailed s = header ++ docs ++ loc where-	header = [brief s, ""]-	docs = maybe [] (return . unpack) $ symbolDocs s-	loc-		| null mloc = []-		| otherwise = ["Defined at " ++ mloc]-	mloc = show (symbolLocation s)+{-# LANGUAGE DefaultSignatures, OverloadedStrings #-}
+
+module HsDev.Symbols.Documented (
+	Documented(..),
+	defaultDetailed
+	) where
+
+import Control.Lens (view, (^..), (^?))
+import Data.Maybe (maybeToList)
+import Data.Text (Text, pack)
+import qualified Data.Text as T
+
+import Text.Format
+import HsDev.Symbols.Class
+import HsDev.Project.Types
+
+-- | Documented symbol
+class Documented a where
+	brief :: a -> Text
+	detailed :: a -> Text
+	default detailed :: Sourced a => a -> Text
+	detailed = T.unlines . defaultDetailed
+
+-- | Default detailed docs
+defaultDetailed :: (Sourced a, Documented a) => a -> [Text]
+defaultDetailed s = concat [header, docs, loc] where
+	header = [brief s, ""]
+	docs = s ^.. sourcedDocs
+	loc = maybe [] (\l -> ["Defined at " `T.append` pack (show l)]) (s ^? sourcedLocation)
+
+instance Documented ModulePackage where
+	brief = pack . show
+	detailed = brief
+
+instance Documented ModuleLocation where
+	brief (FileModule f _) = f
+	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 l = brief l
+
+instance Documented Project where
+	brief p = format "{} ({})" ~~ view projectName p ~~ view projectPath p
+	detailed p = T.unlines (brief p : desc) where
+		desc = concat [
+			do
+				d <- mdescr
+				_ <- maybeToList $ view projectLibrary d
+				return "\tlibrary",
+			do
+				d <- mdescr
+				exe <- view projectExecutables d
+				return $ format "\texecutable: {}" ~~ view executableName exe,
+			do
+				d <- mdescr
+				test <- view projectTests d
+				return $ format "\ttest: {}" ~~ view testName test]
+		mdescr = maybeToList $ view projectDescription p
+ src/HsDev/Symbols/HaskellNames.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE FlexibleInstances #-}
+
+module HsDev.Symbols.HaskellNames (
+	ToEnvironment(..),
+	fromSymbol, toSymbol
+	) where
+
+import Control.Lens (view)
+import Data.String
+import qualified Data.Map.Strict as M
+import qualified Data.Text as T (unpack)
+import qualified Language.Haskell.Exts as H
+import qualified Language.Haskell.Names as N
+
+import HsDev.Symbols.Types
+
+class ToEnvironment a where
+	environment :: a -> N.Environment
+
+instance ToEnvironment Module where
+	environment m = M.singleton (H.ModuleName () (T.unpack $ view sourcedName m)) (map toSymbol $ view moduleExports m)
+
+instance ToEnvironment [Module] where
+	environment = M.unions . map environment
+
+fromSymbol :: N.Symbol -> Symbol
+fromSymbol s = Symbol sid Nothing Nothing info where
+	sid = SymbolId (fromName_ $ N.symbolName s) mid
+	mid = case N.symbolModule s of
+		H.ModuleName _ m -> ModuleId (fromString m) NoLocation
+	info = case s of
+		N.Value _ _ -> Function mempty
+		N.Method _ _ p -> Method mempty (fromName_ p)
+		N.Selector _ _ p cs -> Selector mempty (fromName_ p) (map fromName_ cs)
+		N.Constructor _ _ p -> Constructor mempty (fromName_ p)
+		N.Type _ _ -> Type mempty mempty
+		N.NewType _ _ -> NewType mempty mempty
+		N.Data _ _ -> Data mempty mempty
+		N.Class _ _ -> Class mempty mempty
+		N.TypeFam _ _ a -> TypeFam mempty mempty (fmap fromName_ a)
+		N.DataFam _ _ a -> DataFam mempty mempty (fmap fromName_ a)
+		N.PatternConstructor _ _ p -> PatConstructor mempty (fmap fromName_ p)
+		N.PatternSelector _ _ p c -> PatSelector mempty (fmap fromName_ p) (fromName_ c)
+
+toSymbol :: Symbol -> N.Symbol
+toSymbol s = case view symbolInfo s of
+	Function _ -> N.Value m n
+	Method _ p -> N.Method m n (toName_ p)
+	Selector _ p cs -> N.Selector m n (toName_ p) (map toName_ cs)
+	Constructor _ p -> N.Constructor m n (toName_ p)
+	Type _ _ -> N.Type m n
+	NewType _ _ -> N.NewType m n
+	Data _ _ -> N.Data m n
+	Class _ _ -> N.Class m n
+	TypeFam _ _ a -> N.TypeFam m n (fmap toName_ a)
+	DataFam _ _ a -> N.DataFam m n (fmap toName_ a)
+	PatConstructor _ p -> N.PatternConstructor m n (fmap toName_ p)
+	PatSelector _ p c -> N.PatternSelector m n (fmap toName_ p) (toName_ c)
+	where
+		m = toModuleName_ $ view sourcedModuleName s
+		n = toName_ $ view sourcedName s
src/HsDev/Symbols/Location.hs view
@@ -1,277 +1,353 @@-{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}--module HsDev.Symbols.Location (-	ModulePackage(..), PackageConfig(..), ModuleLocation(..), moduleStandalone, locationId, noLocation,-	Position(..), Region(..), region, regionAt, regionLines, regionStr,-	Location(..),--	packageName, packageVersion,-	package, packageModules, packageExposed,-	moduleFile, moduleProject, modulePackageDb, modulePackage, cabalModuleName, moduleSourceName,-	positionLine, positionColumn,-	regionFrom, regionTo,-	locationModule, locationPosition,--	sourceModuleRoot,-	importedModulePath,-	packageOpt,-	RecalcTabs(..),--	module HsDev.PackageDb-	) where--import Control.Applicative-import Control.DeepSeq (NFData(..))-import Control.Lens (makeLenses, preview, view)-import Control.Monad (join)-import Data.Aeson-import Data.Char (isSpace, isDigit)-import Data.List (intercalate, findIndex)-import Data.Maybe-import Data.Text (Text)-import qualified Data.Text as T (split, unpack)-import System.FilePath-import Text.Read (readMaybe)--import System.Directory.Paths-import HsDev.PackageDb-import HsDev.Project.Types-import HsDev.Util ((.::), (.::?), (.::?!), objectUnion)---- | Just package name and version without its location-data ModulePackage = ModulePackage {-	_packageName :: String,-	_packageVersion :: String }-		deriving (Eq, Ord)--makeLenses ''ModulePackage--instance NFData ModulePackage where-	rnf (ModulePackage n v) = rnf n `seq` rnf v--instance Show ModulePackage where-	show (ModulePackage n "") = n-	show (ModulePackage n v) = n ++ "-" ++ v--instance Read ModulePackage where-	readsPrec _ str = case pkg of-		"" -> []-		_ -> [(ModulePackage n v, str')]-		where-			(pkg, str') = break isSpace str-			(rv, rn) = span versionChar $ reverse pkg-			v = reverse rv-			n = reverse $ dropWhile (== '-') rn--			versionChar ch = isDigit ch || ch == '.'--instance ToJSON ModulePackage where-	toJSON (ModulePackage n v) = object [-		"name" .= n,-		"version" .= v]--instance FromJSON ModulePackage where-	parseJSON = withObject "module package" $ \v ->-		ModulePackage <$> (v .:: "name") <*> (v .:: "version")--data PackageConfig = PackageConfig {-	_package :: ModulePackage,-	_packageModules :: [Text],-	_packageExposed :: Bool }-		deriving (Eq, Ord, Read, Show)--makeLenses ''PackageConfig--instance NFData PackageConfig where-	rnf (PackageConfig p ms e) = rnf p `seq` rnf ms `seq` rnf e--instance ToJSON PackageConfig where-	toJSON (PackageConfig p ms e) = toJSON p `objectUnion` object ["modules" .= ms, "exposed" .= e]--instance FromJSON PackageConfig where-	parseJSON = withObject "package-config" $ \v -> PackageConfig <$>-		parseJSON (Object v) <*>-		(v .::?! "modules") <*>-		(v .:: "exposed" <|> pure False)---- | Location of module-data ModuleLocation =-	FileModule { _moduleFile :: FilePath, _moduleProject :: Maybe Project } |-	InstalledModule { _modulePackageDb :: PackageDb, _modulePackage :: Maybe ModulePackage, _cabalModuleName :: String } |-	ModuleSource { _moduleSourceName :: Maybe String }-		deriving (Eq, Ord)--makeLenses ''ModuleLocation--moduleStandalone :: ModuleLocation -> Bool-moduleStandalone = (== Just Nothing) . preview moduleProject--locationId :: ModuleLocation -> String-locationId (FileModule fpath _) = fpath-locationId (InstalledModule cabal mpack nm) = intercalate ":" [show cabal, maybe "" show mpack, nm]-locationId (ModuleSource msrc) = fromMaybe "" msrc--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 (ModuleSource m) = rnf m--instance Show ModuleLocation where-	show (FileModule f p) = f ++ maybe "" (" in " ++) (fmap (view projectPath) p)-	show (InstalledModule _ p n) = n ++ maybe "" (" in package " ++) (fmap show p)-	show (ModuleSource m) = fromMaybe "" m--instance ToJSON ModuleLocation where-	toJSON (FileModule f p) = object ["file" .= f, "project" .= fmap (view projectCabal) p]-	toJSON (InstalledModule c p n) = object ["db" .= c, "package" .= fmap show p, "name" .= n]-	toJSON (ModuleSource (Just s)) = object ["source" .= s]-	toJSON (ModuleSource Nothing) = object []--instance FromJSON ModuleLocation where-	parseJSON = withObject "module location" $ \v ->-		(FileModule <$> v .:: "file" <*> (fmap project <$> (v .:: "project"))) <|>-		(InstalledModule <$> v .:: "db" <*> fmap (join . fmap readMaybe) (v .:: "package") <*> v .:: "name") <|>-		(ModuleSource <$> v .::? "source")--instance Paths ModuleLocation where-	paths f (FileModule fpath p) = FileModule <$> f fpath <*> traverse (paths f) p-	paths f (InstalledModule c p n) = InstalledModule <$> paths f c <*> pure p <*> pure n-	paths _ (ModuleSource m) = pure $ ModuleSource m--noLocation :: ModuleLocation-noLocation = ModuleSource Nothing--data Position = Position {-	_positionLine :: Int,-	_positionColumn :: Int }-		deriving (Eq, Ord, Read)--makeLenses ''Position--instance NFData Position where-	rnf (Position l c) = rnf l `seq` rnf c--instance Show Position where- 	show (Position l c) = show l ++ ":" ++ show c--instance ToJSON Position where-	toJSON (Position l c) = object [-		"line" .= l,-		"column" .= c]--instance FromJSON Position where-	parseJSON = withObject "position" $ \v -> Position <$>-		v .:: "line" <*>-		v .:: "column"--data Region = Region {-	_regionFrom :: Position,-	_regionTo :: Position }-		deriving (Eq, Ord, Read)--makeLenses ''Region--region :: Position -> Position -> Region-region f t = Region (min f t) (max f t)--regionAt :: Position -> Region-regionAt f = region f f--regionLines :: Region -> Int-regionLines (Region f t) = succ (view positionLine t - view positionLine f)---- | Get string at region-regionStr :: Region -> String -> String-regionStr r@(Region f t) s = intercalate "\n" $ drop (pred $ view positionColumn f) fline : tl where-	s' = take (regionLines r) $ drop (pred (view positionLine f)) $ lines s-	(fline:tl) = init s' ++ [take (pred $ view positionColumn t) (last s')]--instance NFData Region where-	rnf (Region f t) = rnf f `seq` rnf t--instance Show Region where-	show (Region f t) = show f ++ "-" ++ show t--instance ToJSON Region where-	toJSON (Region f t) = object [-		"from" .= f,-		"to" .= t]--instance FromJSON Region where-	parseJSON = withObject "region" $ \v -> Region <$>-		v .:: "from" <*>-		v .:: "to"---- | Location of symbol-data Location = Location {-	_locationModule :: ModuleLocation,-	_locationPosition :: Maybe Position }-		deriving (Eq, Ord)--makeLenses ''Location--instance NFData Location where-	rnf (Location m p) = rnf m `seq` rnf p--instance Show Location where-	show (Location m p) = show m ++ ":" ++ show p--instance ToJSON Location where-	toJSON (Location ml p) = object [-		"module" .= ml,-		"pos" .= p]--instance FromJSON Location where-	parseJSON = withObject "location" $ \v -> Location <$>-		v .:: "module" <*>-		v .:: "pos"---- | Get source module root directory, i.e. for "...\src\Foo\Bar.hs" with module 'Foo.Bar' will return "...\src"-sourceModuleRoot :: Text -> FilePath -> FilePath-sourceModuleRoot mname = -	joinPath .-	reverse . drop (length $ T.split (== '.') mname) . reverse .-	splitDirectories---- | Get path of imported module--- >importedModulePath "Foo.Bar" "...\src\Foo\Bar.hs" "Quux.Blah" = "...\src\Quux\Blah.hs"-importedModulePath :: Text -> FilePath -> Text -> FilePath-importedModulePath mname file imp =-	(`addExtension` "hs") . joinPath .-	(++ ipath) . splitDirectories $-	sourceModuleRoot mname file-	where-		ipath = map T.unpack $ T.split (== '.') imp--packageOpt :: Maybe ModulePackage -> [String]-packageOpt = maybeToList . fmap (("-package " ++) . view packageName)---- | Recalc positions to interpret '\t' as one symbol instead of N-class RecalcTabs a where-	-- | Interpret '\t' as one symbol instead of N-	recalcTabs :: String -> Int -> a -> a-	-- | Inverse of `recalcTabs`: interpret '\t' as N symbols instead of 1-	calcTabs :: String -> Int -> a -> a--instance RecalcTabs Position where-	recalcTabs cts n (Position l c) = Position l c' where-		line = listToMaybe $ drop (pred l) $ lines cts-		c' = case line of-			Nothing -> c-			Just line' -> let sizes = map charSize line' in-				succ . fromMaybe (length sizes) .-				findIndex (>= pred c) .-				scanl (+) 0 $ sizes-		charSize :: Char -> Int-		charSize '\t' = n-		charSize _ = 1-	calcTabs cts n (Position l c) = Position l c' where-		line = listToMaybe $ drop (pred l) $ lines cts-		c' = maybe c (succ . sum . map charSize . take (pred c)) line-		charSize :: Char -> Int-		charSize '\t' = n-		charSize _ = 1--instance RecalcTabs Region where-	recalcTabs cts n (Region f t) = Region (recalcTabs cts n f) (recalcTabs cts n t)-	calcTabs cts n (Region f t) = Region (calcTabs cts n f) (calcTabs cts n t)+{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
+
+module HsDev.Symbols.Location (
+	ModulePackage(..), mkPackage, PackageConfig(..), ModuleLocation(..), locationId, noLocation,
+	ModuleId(..), moduleName, moduleLocation,
+	SymbolId(..), symbolName, symbolModule,
+	Position(..), Region(..), region, regionAt, regionLines, regionStr,
+	Location(..),
+
+	packageName, packageVersion,
+	package, packageModules, packageExposed,
+	moduleFile, moduleProject, moduleInstallDirs, modulePackage, installedModuleName, otherLocationName,
+	positionLine, positionColumn,
+	regionFrom, regionTo,
+	locationModule, locationPosition,
+
+	sourceModuleRoot,
+	importPath,
+	sourceRoot, sourceRoot_,
+	RecalcTabs(..),
+
+	module HsDev.PackageDb.Types
+	) where
+
+import Control.Applicative
+import Control.DeepSeq (NFData(..))
+import Control.Lens (makeLenses, view, preview, over)
+import Data.Aeson
+import Data.Char (isSpace, isDigit)
+import Data.List (findIndex)
+import Data.Maybe
+import Data.Text (Text, pack, unpack)
+import qualified Data.Text as T
+import System.FilePath
+import Text.Read (readMaybe)
+
+import System.Directory.Paths
+import HsDev.PackageDb.Types
+import HsDev.Project.Types
+import HsDev.Util ((.::), (.::?), (.::?!), objectUnion, noNulls)
+
+-- | Just package name and version without its location
+data ModulePackage = ModulePackage {
+	_packageName :: Text,
+	_packageVersion :: Text }
+		deriving (Eq, Ord)
+
+makeLenses ''ModulePackage
+
+mkPackage :: Text -> ModulePackage
+mkPackage n = ModulePackage n ""
+
+instance NFData ModulePackage where
+	rnf (ModulePackage n v) = rnf n `seq` rnf v
+
+instance Show ModulePackage where
+	show (ModulePackage n "") = unpack n
+	show (ModulePackage n v) = unpack n ++ "-" ++ unpack v
+
+instance Read ModulePackage where
+	readsPrec _ str = case pkg of
+		"" -> []
+		_ -> [(ModulePackage (pack n) (pack v), str')]
+		where
+			(pkg, str') = break isSpace str
+			(rv, rn) = span versionChar $ reverse pkg
+			v = reverse rv
+			n = reverse $ dropWhile (== '-') rn
+
+			versionChar ch = isDigit ch || ch == '.'
+
+instance ToJSON ModulePackage where
+	toJSON (ModulePackage n v) = object [
+		"name" .= n,
+		"version" .= v]
+
+instance FromJSON ModulePackage where
+	parseJSON = withObject "module package" $ \v ->
+		ModulePackage <$> (v .:: "name") <*> (v .:: "version")
+
+data PackageConfig = PackageConfig {
+	_package :: ModulePackage,
+	_packageModules :: [Text],
+	_packageExposed :: Bool }
+		deriving (Eq, Ord, Read, Show)
+
+makeLenses ''PackageConfig
+
+instance NFData PackageConfig where
+	rnf (PackageConfig p ms e) = rnf p `seq` rnf ms `seq` rnf e
+
+instance ToJSON PackageConfig where
+	toJSON (PackageConfig p ms e) = toJSON p `objectUnion` object ["modules" .= ms, "exposed" .= e]
+
+instance FromJSON PackageConfig where
+	parseJSON = withObject "package-config" $ \v -> PackageConfig <$>
+		parseJSON (Object v) <*>
+		(v .::?! "modules") <*>
+		(v .:: "exposed" <|> pure False)
+
+-- | Location of module
+data ModuleLocation =
+	FileModule { _moduleFile :: Path, _moduleProject :: Maybe Project } |
+	InstalledModule { _moduleInstallDirs :: [Path], _modulePackage :: ModulePackage, _installedModuleName :: Text } |
+	OtherLocation { _otherLocationName :: Text } |
+	NoLocation
+
+instance Eq ModuleLocation where
+	FileModule lfile _ == FileModule rfile _ = lfile == rfile
+	InstalledModule ldirs _ lname == InstalledModule rdirs _ rname = ldirs == rdirs && lname == rname
+	OtherLocation l == OtherLocation r = l == r
+	NoLocation == NoLocation = True
+	_ == _ = False
+
+instance Ord ModuleLocation where
+	compare l r = compare (locType l, locNames l) (locType r, locNames r) where
+		locType :: ModuleLocation -> Int
+		locType (FileModule _ _) = 0
+		locType (InstalledModule _ _ _) = 1
+		locType (OtherLocation _) = 2
+		locType NoLocation = 3
+		locNames (FileModule f _) = [f]
+		locNames (InstalledModule dirs _ nm) = nm : dirs
+		locNames (OtherLocation n) = [n]
+		locNames NoLocation = []
+
+makeLenses ''ModuleLocation
+
+locationId :: ModuleLocation -> Text
+locationId (FileModule fpath _) = fpath
+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 (OtherLocation s) = rnf s
+	rnf NoLocation = ()
+
+instance Show ModuleLocation where
+	show = unpack . locationId
+
+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 (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") <|>
+		(OtherLocation <$> v .:: "source") <|>
+		(pure NoLocation)
+		where
+			readPackage s = maybe (fail $ "can't parse package: " ++ s) return . readMaybe $ s
+
+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 _ (OtherLocation s) = pure $ OtherLocation s
+	paths _ NoLocation = pure NoLocation
+
+noLocation :: ModuleLocation
+noLocation = NoLocation
+
+data ModuleId = ModuleId {
+	_moduleName :: Text,
+	_moduleLocation :: ModuleLocation }
+		deriving (Eq, Ord)
+
+makeLenses ''ModuleId
+
+instance NFData ModuleId where
+	rnf (ModuleId n l) = rnf n `seq` rnf l
+
+instance Show ModuleId where
+	show (ModuleId n l) = show l ++ ":" ++ unpack n
+
+instance ToJSON ModuleId where
+	toJSON m = object $ noNulls [
+		"name" .= _moduleName m,
+		"location" .= _moduleLocation m]
+
+instance FromJSON ModuleId where
+	parseJSON = withObject "module-id" $ \v -> ModuleId <$>
+		(fromMaybe "" <$> (v .::? "name")) <*>
+		(fromMaybe NoLocation <$> (v .::? "location"))
+
+-- | Symbol
+data SymbolId = SymbolId {
+	_symbolName :: Text,
+	_symbolModule :: ModuleId }
+		deriving (Eq, Ord)
+
+makeLenses ''SymbolId
+
+instance NFData SymbolId where
+	rnf (SymbolId n m) = rnf n `seq` rnf m
+
+instance Show SymbolId where
+	show (SymbolId n m) = show m ++ ":" ++ unpack n
+
+instance ToJSON SymbolId where
+	toJSON s = object $ noNulls [
+		"name" .= _symbolName s,
+		"module" .= _symbolModule s]
+
+instance FromJSON SymbolId where
+	parseJSON = withObject "symbol-id" $ \v -> SymbolId <$>
+		(fromMaybe "" <$> (v .::? "name")) <*>
+		(fromMaybe (ModuleId "" NoLocation) <$> (v .::? "module"))
+
+data Position = Position {
+	_positionLine :: Int,
+	_positionColumn :: Int }
+		deriving (Eq, Ord, Read)
+
+makeLenses ''Position
+
+instance NFData Position where
+	rnf (Position l c) = rnf l `seq` rnf c
+
+instance Show Position where
+ 	show (Position l c) = show l ++ ":" ++ show c
+
+instance ToJSON Position where
+	toJSON (Position l c) = object [
+		"line" .= l,
+		"column" .= c]
+
+instance FromJSON Position where
+	parseJSON = withObject "position" $ \v -> Position <$>
+		v .:: "line" <*>
+		v .:: "column"
+
+data Region = Region {
+	_regionFrom :: Position,
+	_regionTo :: Position }
+		deriving (Eq, Ord, Read)
+
+makeLenses ''Region
+
+region :: Position -> Position -> Region
+region f t = Region (min f t) (max f t)
+
+regionAt :: Position -> Region
+regionAt f = region f f
+
+regionLines :: Region -> Int
+regionLines (Region f t) = succ (view positionLine t - view positionLine f)
+
+-- | Get string at region
+regionStr :: Region -> Text -> Text
+regionStr r@(Region f t) s = T.intercalate "\n" $ T.drop (pred $ view positionColumn f) fline : tl where
+	s' = take (regionLines r) $ drop (pred (view positionLine f)) $ T.lines s
+	(fline:tl) = init s' ++ [T.take (pred $ view positionColumn t) (last s')]
+
+instance NFData Region where
+	rnf (Region f t) = rnf f `seq` rnf t
+
+instance Show Region where
+	show (Region f t) = show f ++ "-" ++ show t
+
+instance ToJSON Region where
+	toJSON (Region f t) = object [
+		"from" .= f,
+		"to" .= t]
+
+instance FromJSON Region where
+	parseJSON = withObject "region" $ \v -> Region <$>
+		v .:: "from" <*>
+		v .:: "to"
+
+-- | Location of symbol
+data Location = Location {
+	_locationModule :: ModuleLocation,
+	_locationPosition :: Maybe Position }
+		deriving (Eq, Ord)
+
+makeLenses ''Location
+
+instance NFData Location where
+	rnf (Location m p) = rnf m `seq` rnf p
+
+instance Show Location where
+	show (Location m p) = show m ++ ":" ++ show p
+
+instance ToJSON Location where
+	toJSON (Location ml p) = object [
+		"module" .= ml,
+		"pos" .= p]
+
+instance FromJSON Location where
+	parseJSON = withObject "location" $ \v -> Location <$>
+		v .:: "module" <*>
+		v .::? "pos"
+
+-- | Get source module root directory, i.e. for "...\src\Foo\Bar.hs" with module 'Foo.Bar' will return "...\src"
+sourceModuleRoot :: Text -> Path -> Path
+sourceModuleRoot mname = over paths $
+	normalise . joinPath .
+	reverse . drop (length $ T.split (== '.') mname) . reverse .
+	splitDirectories
+
+-- | Path to module source
+-- >importPath "Quux.Blah" = "Quux/Blah.hs"
+importPath :: Text -> Path
+importPath = fromFilePath . (`addExtension` "hs") . joinPath . map unpack . T.split (== '.')
+
+-- | Root of sources, package dir or root directory of standalone modules
+sourceRoot :: ModuleId -> Maybe Path
+sourceRoot m = do
+	fpath <- preview (moduleLocation . moduleFile) m
+	mproj <- preview (moduleLocation . moduleProject) m
+	return $ maybe
+		(sourceModuleRoot (view moduleName m) fpath)
+		(view projectPath)
+		mproj
+
+sourceRoot_ :: ModuleId -> Path
+sourceRoot_ = fromMaybe (error "sourceRoot_: not a source location") . sourceRoot
+
+-- | Recalc positions to interpret '\t' as one symbol instead of N
+class RecalcTabs a where
+	-- | Interpret '\t' as one symbol instead of N
+	recalcTabs :: Text -> Int -> a -> a
+	-- | Inverse of `recalcTabs`: interpret '\t' as N symbols instead of 1
+	calcTabs :: Text -> Int -> a -> a
+
+instance RecalcTabs Position where
+	recalcTabs cts n (Position l c) = Position l c' where
+		line = listToMaybe $ drop (pred l) $ T.lines cts
+		c' = case line of
+			Nothing -> c
+			Just line' -> let sizes = map charSize (unpack line') in
+				succ . fromMaybe (length sizes) .
+				findIndex (>= pred c) .
+				scanl (+) 0 $ sizes
+		charSize :: Char -> Int
+		charSize '\t' = n
+		charSize _ = 1
+	calcTabs cts n (Position l c) = Position l c' where
+		line = listToMaybe $ drop (pred l) $ T.lines cts
+		c' = maybe c (succ . sum . map charSize . take (pred c) . unpack) line
+		charSize :: Char -> Int
+		charSize '\t' = n
+		charSize _ = 1
+
+instance RecalcTabs Region where
+	recalcTabs cts n (Region f t) = Region (recalcTabs cts n f) (recalcTabs cts n t)
+	calcTabs cts n (Region f t) = Region (calcTabs cts n f) (calcTabs cts n t)
+ src/HsDev/Symbols/Name.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE CPP, PatternSynonyms, ViewPatterns, OverloadedStrings #-}
+
+module HsDev.Symbols.Name (
+	Name, qualName, unqualName, nameModule, nameIdent, pattern Name, fromName_, toName_, toModuleName_, fromModuleName_, fromName, toName,
+	) where
+
+import Control.Arrow
+import Data.Char (isAlpha, isAlphaNum)
+import Data.Text (Text)
+import Data.String (fromString)
+import qualified Data.Text as T
+import Language.Haskell.Exts (QName(..), ModuleName(..), Boxed(..), SpecialCon(..))
+import qualified Language.Haskell.Exts as Exts (Name(..))
+
+-- | Qualified name
+type Name = QName ()
+
+qualName :: String -> String -> Name
+qualName m = Qual () (ModuleName () m) . toName_ . fromString
+
+unqualName :: String -> Name
+unqualName = UnQual () . toName_ . fromString
+
+nameModule :: Name -> Maybe Text
+nameModule (Qual _ (ModuleName _ m) _) = Just $ fromString m
+nameModule _ = Nothing
+
+nameIdent :: Name -> Text
+nameIdent (Qual _ _ n) = fromName_ n
+nameIdent (UnQual _ n) = fromName_ n
+nameIdent s = fromName s
+
+pattern Name :: Maybe Text -> Text -> Name
+pattern Name m n <- ((nameModule &&& nameIdent) -> (m, n)) where
+	Name Nothing n = UnQual () (Exts.Ident () (T.unpack n))
+	Name (Just m) n = Qual () (ModuleName () (T.unpack m)) (Exts.Ident () (T.unpack n))
+
+fromName_ :: Exts.Name () -> Text
+fromName_ (Exts.Ident _ s') = fromString s'
+fromName_ (Exts.Symbol _ s') = fromString s'
+
+toName_ :: Text -> Exts.Name ()
+toName_ txt
+	| T.null txt = Exts.Ident () ""
+	| isAlpha (T.head txt) && (T.all validChar $ T.tail txt) = Exts.Ident () . T.unpack $ txt
+	| otherwise = Exts.Symbol () . T.unpack $ txt
+	where
+		validChar ch = isAlphaNum ch || ch == '_'
+
+toModuleName_ :: Text -> ModuleName ()
+toModuleName_ = ModuleName () . T.unpack
+
+fromModuleName_ :: ModuleName () -> Text
+fromModuleName_ (ModuleName () m) = T.pack m
+
+toName :: Text -> Name
+toName "()" = Special () (UnitCon ())
+toName "[]" = Special () (ListCon ())
+toName "->" = Special () (FunCon ())
+toName "(:)" = Special () (Cons ())
+toName "(# #)" = Special () (UnboxedSingleCon ())
+toName tup
+	| T.all (== ',') noBraces = Special () (TupleCon () Boxed (succ $ T.length noBraces))
+	where
+		noBraces = T.dropAround (`elem` ['(', ')']) tup
+toName n = case T.split (== '.') n of
+	[n'] -> UnQual () (Exts.Ident () $ T.unpack n')
+	ns -> Qual () (ModuleName () (T.unpack $ T.intercalate "." $ init ns)) (toName_ $ last ns)
+
+fromName :: Name -> Text
+fromName (Qual _ (ModuleName _ m) n) = T.concat [fromString m, ".", fromName_ n]
+fromName (UnQual _ n) = fromName_ n
+fromName (Special _ c) = case c of
+	UnitCon _ -> "()"
+	ListCon _ -> "[]"
+	FunCon _ -> "->"
+	TupleCon _ _ i -> fromString $ "(" ++ replicate (pred i) ',' ++ ")"
+	Cons _ -> "(:)"
+	UnboxedSingleCon _ -> "(# #)"
+#if MIN_VERSION_haskell_src_exts(1,20,0)
+	ExprHole _ -> "_"
+#endif
+ src/HsDev/Symbols/Parsed.hs view
@@ -0,0 +1,197 @@+{-# LANGUAGE RankNTypes #-}
+
+module HsDev.Symbols.Parsed (
+	Ann, Parsed,
+	qnames, names, binders, locals, globals, references, unresolveds,
+	usages, named, imports, declarations, moduleNames,
+
+	annL, symbolL, file, pos, defPos, resolvedName,
+	isBinder, isLocal, isGlobal, isReference, isUnresolved, resolveError,
+	refsTo, refsToName,
+	nameInfoL, positionL, regionL, fileL,
+	symbolNameL,
+
+	prettyPrint
+	) where
+
+import Control.Lens
+import Data.Data
+import Data.Data.Lens
+import Data.Maybe (isJust)
+import Data.Text (Text)
+import Language.Haskell.Exts hiding (Name(..))
+import qualified Language.Haskell.Exts as E (Name(..))
+import Language.Haskell.Names
+
+import HsDev.Symbols.Name
+import HsDev.Symbols.Location (Position(..), positionLine, positionColumn, Region(..), region)
+
+-- | Annotation of parsed and resolved nodes
+type Ann = Scoped SrcSpanInfo
+
+-- | Parsed and resolved module
+type Parsed = Module Ann
+
+-- | Get all qualified names
+qnames :: Data (ast Ann) => Traversal' (ast Ann) (QName Ann)
+qnames = biplate
+
+-- | Get all names
+names :: Data (ast Ann) => Traversal' (ast Ann) (E.Name Ann)
+names = biplate
+
+-- | Get all binders
+binders :: Annotated ast => Traversal' (ast Ann) (ast Ann)
+binders = filtered isBinder
+
+-- | Get all names locally defined
+locals :: Annotated ast => Traversal' (ast Ann) (ast Ann)
+locals = filtered isLocal
+
+-- | Get all names, references global symbol
+globals :: Annotated ast => Traversal' (ast Ann) (ast Ann)
+globals = filtered isGlobal
+
+-- | Get all resolved references
+references :: Annotated ast => Traversal' (ast Ann) (ast Ann)
+references = filtered isReference
+
+-- | Get all names with not in scope error
+unresolveds :: Annotated ast => Traversal' (ast Ann) (ast Ann)
+unresolveds = filtered isUnresolved
+
+-- | Get all usages of symbol
+usages :: Annotated ast => Name -> Traversal' (ast Ann) (ast Ann)
+usages = filtered . refsTo
+
+-- | Get usages of symbols with unqualified name
+named :: Annotated ast => Text -> Traversal' (ast Ann) (ast Ann)
+named = filtered . refsToName
+
+-- | Get imports
+imports :: Data (ast Ann) => Traversal' (ast Ann) (ImportDecl Ann)
+imports = biplate
+
+-- | Get declarations
+declarations :: Data (ast Ann) => Traversal' (ast Ann) (Decl Ann)
+declarations = biplate
+
+-- | Get module names
+moduleNames :: Data (ast Ann) => Traversal' (ast Ann) (ModuleName Ann)
+moduleNames = biplate
+
+-- | Get annotation
+annL :: Annotated ast => Lens' (ast a) a
+annL = lens ann (\v a' -> amap (const a') v)
+
+-- | Get haskell-names symbols
+symbolL :: Data a => Traversal' a Symbol
+symbolL = biplate
+
+-- | Get source file
+file :: Annotated ast => Lens' (ast Ann) FilePath
+file = annL . fileL
+
+-- | Get source location
+pos :: Annotated ast => Lens' (ast Ann) Position
+pos = annL . positionL
+
+-- | Definition position, if binder - returns current position
+defPos :: Annotated ast => Traversal' (ast Ann) Position
+defPos = annL . defLoc' where
+	defLoc' :: Traversal' Ann Position
+	defLoc' f (Scoped (LocalValue s) i) = Scoped <$> (LocalValue <$> positionL f s) <*> pure i
+	defLoc' f (Scoped (TypeVar s) i) = Scoped <$> (TypeVar <$> positionL f s) <*> pure i
+	defLoc' f (Scoped ValueBinder i) = Scoped ValueBinder <$> positionL f i
+	defLoc' f (Scoped TypeBinder i) = Scoped TypeBinder <$> positionL f i
+	defLoc' _ s = pure s
+
+-- | Resolved global name
+resolvedName :: Annotated ast => Traversal' (ast Ann) Name
+resolvedName = annL . nameInfoL . symbolL . symbolNameL
+
+-- | Does ast node binds something
+isBinder :: Annotated ast => ast Ann -> Bool
+isBinder e = (e ^. annL . nameInfoL) `elem` [TypeBinder, ValueBinder]
+
+-- | Does ast node locally defined
+isLocal :: Annotated ast => ast Ann -> Bool
+isLocal e = case e ^. annL . nameInfoL of
+	LocalValue _ -> True
+	TypeVar _ -> True
+	_ -> False
+
+-- | Does ast node reference something
+isGlobal :: Annotated ast => ast Ann -> Bool
+isGlobal e = case e ^. annL . nameInfoL of
+	GlobalSymbol _ _ -> True
+	_ -> False
+
+-- | Does ast node reference something
+isReference :: Annotated ast => ast Ann -> Bool
+isReference e = isLocal e || isGlobal e
+
+-- | Is ast node not resolved
+isUnresolved :: Annotated ast => ast Ann -> Bool
+isUnresolved = isJust . resolveError
+
+-- | Resolve error
+resolveError :: Annotated ast => ast Ann -> Maybe String
+resolveError e = case e ^. annL . nameInfoL of
+	ScopeError err -> Just $ ppError err
+	_ -> Nothing
+
+-- | Node references to specified symbol
+refsTo :: Annotated ast => Name -> ast Ann -> Bool
+refsTo n a = Just n == a ^? resolvedName
+
+-- | Node references to specified unqualified name
+refsToName :: Annotated ast => Text -> ast Ann -> Bool
+refsToName n a = Just n == fmap nameIdent (a ^? resolvedName)
+
+nameInfoL :: Lens' (Scoped a) (NameInfo a)
+nameInfoL = lens g' s' where
+	g' (Scoped i _) = i
+	s' (Scoped _ s) i' = Scoped i' s
+
+positionL :: (SrcInfo isrc, Data isrc) => Lens' isrc Position
+positionL = lens g' s' where
+	g' i = Position l c where
+		SrcLoc _ l c = getPointLoc i
+	s' i (Position l c) = over biplate upd i where
+		Position sl sc = g' i -- Old location
+		-- main line: set new line and move column
+		-- other lines: just move line, because altering first line's column doesn't affect other lines
+		upd :: SrcLoc -> SrcLoc
+		upd (SrcLoc f' l' c')
+			| l' == sl = SrcLoc f' l (c' - sc + c)
+			| otherwise = SrcLoc f' (l' - sl + l) c'
+
+regionL :: Annotated ast => Lens' (ast Ann) Region
+regionL = lens g' s' where
+	g' i = case ann i of
+		Scoped _ sinfo -> toPos (srcSpanStart span') `region` toPos (srcSpanEnd span') where
+			span' = srcInfoSpan sinfo
+			toPos = uncurry Position
+	s' i (Region s e) = amap (fmap upd) i where
+		upd :: SrcSpanInfo -> SrcSpanInfo
+		upd sinfo = sinfo {
+			srcInfoSpan = (srcInfoSpan sinfo) {
+				srcSpanStartLine = s ^. positionLine,
+				srcSpanStartColumn = s ^. positionColumn,
+				srcSpanEndLine = e ^. positionLine,
+				srcSpanEndColumn = e ^. positionColumn },
+			srcInfoPoints = [] }
+
+fileL :: (SrcInfo isrc, Data isrc) => Lens' isrc FilePath
+fileL = lens g' s' where
+	g' = fileName
+	s' i f = set biplate f i
+
+-- | Get 'Symbol' as 'Name'
+symbolNameL :: Lens' Symbol Name
+symbolNameL = lens g' s' where
+	g' sym' = Qual () (symbolModule sym') (symbolName sym')
+	s' sym' (Qual _ m n) = sym' { symbolModule = m, symbolName = n }
+	s' sym' (UnQual _ n) = sym' { symbolName = n }
+	s' sym' _ = sym'
src/HsDev/Symbols/Resolve.hs view
@@ -1,176 +1,27 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, TemplateHaskell #-}--module HsDev.Symbols.Resolve (-	ResolveM(..),ResolvedTree, ResolvedModule(..), resolvedModule, resolvedScope, resolvedExports,-	scopeModule, exportsModule, resolvedTopScope,-	resolve, resolveOne, resolveModule, resolveImports, resolveImport,-	mergeImported-	) where--import Control.Applicative ((<|>))-import Control.Arrow-import Control.Lens (makeLenses, view, preview, set, _Just, over, each)-import Control.Monad.Reader-import Control.Monad.State-import Data.Function (on)-import Data.List (sortBy, groupBy, delete)-import qualified Data.Map as M-import Data.Maybe (fromMaybe, listToMaybe, catMaybes)-import Data.Maybe.JustIf-import Data.Ord (comparing)-import qualified Data.Set as S-import Data.String (fromString)-import Data.Text (Text)--import HsDev.Database-import HsDev.Symbols-import HsDev.Symbols.Util-import HsDev.Util (uniqueBy)---- | Map from name to modules-type ModuleMap = M.Map Text [Module]---- | Resolve monad uses existing @Database@ and @ResolvedTree@ as state.-newtype ResolveM a = ResolveM { runResolveM :: ReaderT (Database, ModuleMap) (State ResolvedTree) a }-	deriving (Functor, Applicative, Monad, MonadState ResolvedTree, MonadReader (Database, ModuleMap))---- | Tree of resolved modules-type ResolvedTree = Map ModuleId ResolvedModule---- | Module with declarations bringed to scope and with exported declarations-data ResolvedModule = ResolvedModule {-	_resolvedModule :: Module,-	_resolvedScope :: [Declaration],-	_resolvedExports :: [Declaration] }--makeLenses ''ResolvedModule---- | Make @Module@ with scope declarations-scopeModule :: ResolvedModule -> Module-scopeModule r = set moduleDeclarations (view resolvedScope r) (view resolvedModule r)---- | Make @Module@ with exported only declarations-exportsModule :: ResolvedModule -> Module-exportsModule r = set moduleDeclarations (view resolvedExports r) (view resolvedModule r)---- | Get top-level scope-resolvedTopScope :: ResolvedModule -> [Declaration]-resolvedTopScope = filter isTop . view resolvedScope where-	isTop :: Declaration -> Bool-	isTop = any (not . view importIsQualified) . fromMaybe [] . view declarationImported---- | Resolve modules, function is not IO, so all file names must be canonicalized-resolve :: Traversable t => Database -> t Module -> t ResolvedModule-resolve db = flip evalState M.empty . flip runReaderT (db, m) . runResolveM . traverse resolveModule where-	m :: ModuleMap-	m = M.fromList $ map ((view moduleName . head) &&& id) $-		groupBy ((==) `on` view moduleName) $-		sortBy (comparing (view moduleName)) $ allModules db---- | Resolve one module-resolveOne :: Database -> Module -> ResolvedModule-resolveOne db = fromMaybe (error "Resolve: impossible happened") . resolve db . Just---- | Resolve module-resolveModule :: Module -> ResolveM ResolvedModule-resolveModule m = gets (M.lookup $ view moduleId m) >>= maybe resolveModule' return where-	resolveModule' = save $ case view moduleLocation m of-		InstalledModule {} -> return ResolvedModule {-			_resolvedModule = m,-			_resolvedScope = map setSelfDefined $ view moduleDeclarations m,-			_resolvedExports = map setSelfDefined $ view moduleDeclarations m }-		_ -> do-			scope' <--				liftM (thisDecls ++) .-				resolveImports m .-				(import_ (fromString "Prelude") :) .-				view moduleImports $ m-			let-				exported' = case view moduleExports m of-					Nothing -> thisDecls-					Just exports' -> unique $ catMaybes $ mexported <$> scope' <*> exports'-			return $ ResolvedModule m (sortDeclarations scope') (sortDeclarations exported')-	thisDecls :: [Declaration]-	thisDecls = map (selfDefined . selfImport) $ view moduleDeclarations m-	setSelfDefined :: Declaration -> Declaration-	setSelfDefined =-		over (declaration . localDeclarations . each . declarationDefined) (<|> Just (view moduleId m)) .-		over declarationDefined (<|> Just (view moduleId m))-	selfDefined :: Declaration -> Declaration-	selfDefined = set declarationDefined (Just $ view moduleId m)-	selfImport :: Declaration -> Declaration-	selfImport = set declarationImported (Just [import_ $ view moduleName m])-	save :: ResolveM ResolvedModule -> ResolveM ResolvedModule-	save act = do-		rm <- act-		modify $ M.insert (view (resolvedModule . moduleId) rm) rm-		return rm-	unique :: [Declaration] -> [Declaration]-	unique = uniqueBy declId-	declId :: Declaration -> (Text, Maybe ModuleId)-	declId = view declarationName &&& view declarationDefined-	mexported decl' e' = decl' `justIf` exported decl' e'---- | Bring declarations into scope by imports-resolveImports :: Module -> [Import] -> ResolveM [Declaration]-resolveImports m is = do-	db <- asks fst-	let-		deps = maybe S.empty S.fromList $ do-			f <- preview (moduleLocation . moduleFile) m-			p <- preview (moduleLocation . moduleProject . _Just) m-			p' <- refineProject db p-			return $ delete (view projectName p') $ concatMap (view infoDepends) $ fileTargets p' f-	liftM (mergeImported . concat) $ mapM (resolveImport deps m) is---- | Bring declarations into scope, first parameter is set of visible packages-resolveImport :: S.Set String -> Module -> Import -> ResolveM [Declaration]-resolveImport deps m i = liftM (map $ setImport i) resolveImport' where-	resolveImport' :: ResolveM [Declaration]-	resolveImport' = do-		ms <- case view moduleLocation m of-			FileModule file proj -> do-				db <- asks fst-				let-					proj' = proj >>= refineProject db-				case proj' of-					Nothing -> selectImport i [-						inFile $ importedModulePath (view moduleName m) file (view importModuleName i),-						installed]-					Just p -> selectImport i [-						inProject p,-						inDeps]-			InstalledModule pdb _ _ -> selectImport i [inPackageDb pdb]-			ModuleSource _ -> selectImport i [installed]-		fromMaybe [] <$> traverse (liftM (filterImportList . view resolvedExports) . resolveModule) ms-	setImport :: Import -> Declaration -> Declaration-	setImport i' = set declarationImported (Just [i'])-	selectImport :: Import -> [ModuleId -> Bool] -> ResolveM (Maybe Module)-	selectImport i' fs = do-		modsMap <- asks snd-		let-			mods = fromMaybe [] $ M.lookup (view importModuleName i') modsMap-		return $-			listToMaybe $-			newestPackage $-			fromMaybe [] $-			listToMaybe $ dropWhile null-				[filter (f . view moduleId) mods | f <- fs]-	filterImportList :: [Declaration] -> [Declaration]-	filterImportList = case view importList i of-		Nothing -> id-		Just il -> filter (`imported` il)-	inDeps = maybe False (`S.member` deps) . preview (moduleIdLocation . modulePackage . _Just . packageName)---- | Merge imported declarations-mergeImported :: [Declaration] -> [Declaration]-mergeImported =-	map merge' .-	groupBy ((==) `on` declId) .-	sortBy (comparing declId)-	where-		declId :: Declaration -> (Text, Maybe ModuleId)-		declId = view declarationName &&& view declarationDefined-		merge' :: [Declaration] -> Declaration-		merge' [] = error "mergeImported: impossible"-		merge' ds@(d:_) = set declarationImported (mconcat $ map (view declarationImported) ds) d+{-# LANGUAGE RankNTypes #-}
+
+module HsDev.Symbols.Resolve (
+	RefineTable, refineTable, refineSymbol, refineSymbols,
+	symbolUniqId
+	) where
+
+import Control.Lens
+import qualified Data.Map.Strict as M
+import Data.Maybe (fromMaybe)
+import Data.Text (Text)
+
+import HsDev.Symbols
+
+type RefineTable = M.Map (Text, Text, SymbolInfo) Symbol
+
+refineTable :: [Symbol] -> RefineTable
+refineTable syms = M.fromList [(symbolUniqId s, s) | s <- syms]
+
+refineSymbol :: RefineTable -> Symbol -> Symbol
+refineSymbol tbl s = fromMaybe s $ M.lookup (symbolUniqId s) tbl
+
+refineSymbols :: RefineTable -> Module -> Module
+refineSymbols tbl = over moduleSymbols (refineSymbol tbl)
+
+symbolUniqId :: Symbol -> (Text, Text, SymbolInfo)
+symbolUniqId s = (view (symbolId . symbolName) s, view (symbolId . symbolModule . moduleName) s, nullifyInfo $ view symbolInfo s)
src/HsDev/Symbols/Types.hs view
@@ -1,620 +1,529 @@-{-# LANGUAGE TemplateHaskell, TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Symbols.Types (-	ThingPart(..),-	Export(..), exportQualified, exportName, exportPart, exportModule,-	ImportSpec(..), importSpecName, importSpecPart,-	ImportList(..), hidingList, importSpecs,-	Import(..), importModuleName, importIsQualified, importAs, importList, importPosition,-	ModuleId(..), moduleIdName, moduleIdLocation,-	Module(..), moduleName, moduleDocs, moduleLocation, moduleExports, moduleImports, moduleDeclarations, moduleContents, moduleId,-	Declaration(..), declarationName, declarationDefined, declarationImported, declarationDocs, declarationPosition, declaration, minimalDecl,-	TypeInfo(..), typeInfoContext, typeInfoArgs, typeInfoDefinition, typeInfoFunctions, showTypeInfo,-	DeclarationInfo(..), functionType, localDeclarations, related, typeInfo, declarationInfo, declarationTypeCtor, declarationTypeName,-	ModuleDeclaration(..), declarationModuleId, moduleDeclaration,-	ExportedDeclaration(..), exportedBy, exportedDeclaration,-	Inspection(..), inspectionAt, inspectionOpts,-	Inspected(..), inspection, inspectedId, inspectionTags, inspectionResult, noTags,-	ModuleTag(..),-	InspectedModule, notInspected,--	module HsDev.PackageDb,-	module HsDev.Project,-	module HsDev.Symbols.Class,-	module HsDev.Symbols.Documented-	) where--import Control.Applicative-import Control.Arrow-import Control.Lens (makeLenses, view, set, Simple, Lens, Lens', lens)-import Control.Monad-import Control.DeepSeq (NFData(..))-import Data.Aeson-import Data.List (intercalate)-import Data.Maybe (fromMaybe)-import Data.Function-import Data.Ord-import Data.Text (Text, unpack)-import qualified Data.Text as T-import Data.Set (Set)-import qualified Data.Set as S-import Data.Time.Clock.POSIX (POSIXTime)--import HsDev.PackageDb-import HsDev.Project-import HsDev.Symbols.Class-import HsDev.Symbols.Documented-import HsDev.Types-import HsDev.Util (tab, tabs, (.::), (.::?), (.::?!), noNulls)---- | What to export/import for data/class etc-data ThingPart = ThingNothing | ThingAll | ThingWith [Text] deriving (Eq, Ord)--instance NFData ThingPart where-	rnf ThingNothing = ()-	rnf ThingAll = ()-	rnf (ThingWith ns) = rnf ns--instance Show ThingPart where-	show ThingNothing = ""-	show ThingAll = "(..)"-	show (ThingWith ns) = "(" ++ intercalate ", " (map unpack ns) ++ ")"--instance ToJSON ThingPart where-	toJSON ThingNothing = toJSON ("nothing" :: String)-	toJSON ThingAll = toJSON ("all" :: String)-	toJSON (ThingWith ns) = object [-		"with" .= ns]--instance FromJSON ThingPart where-	parseJSON v = parse' <|> parseWith v where-		parse' = do-			s <- parseJSON v-			mplus-				(guard (s == ("nothing" :: String)) >> return ThingNothing)-				(guard (s == ("all" :: String)) >> return ThingAll)-		parseWith = withObject "export part" $ \v' -> ThingWith <$> v' .:: "with"---- | Module export-data Export =-	ExportName {-		_exportQualified :: Maybe Text,-		_exportName :: Text,-		_exportPart :: ThingPart } |-	ExportModule { _exportModule :: Text }-		deriving (Eq, Ord)--instance NFData Export where-	rnf (ExportName q n w) = rnf q `seq` rnf n `seq` rnf w-	rnf (ExportModule m) = rnf m--instance Show Export where-	show (ExportName Nothing n w) = unpack n ++ show w-	show (ExportName (Just q) n w) = unpack q ++ "." ++ unpack n ++ show w-	show (ExportModule m) = "module " ++ unpack m--instance ToJSON Export where-	toJSON (ExportName q n w) = object ["module" .= q, "name" .= n, "part" .= w]-	toJSON (ExportModule m) = object ["module" .= m]--instance FromJSON Export where-	parseJSON = withObject "export" $ \v ->-		(ExportName <$> (v .:: "module") <*> (v .:: "name") <*> (v .:: "part")) <|>-		(ExportModule <$> (v .:: "module"))---- | Import spec-data ImportSpec = ImportSpec {-	_importSpecName :: Text,-	_importSpecPart :: ThingPart }-		deriving (Eq, Ord)--instance NFData ImportSpec where-	rnf (ImportSpec n p) = rnf n `seq` rnf p--instance Show ImportSpec where-	show (ImportSpec n p) = unpack n ++ show p--instance ToJSON ImportSpec where-	toJSON (ImportSpec n p) = object ["name" .= n, "part" .= p]--instance FromJSON ImportSpec where-	parseJSON = withObject "import-spec" $ \v -> ImportSpec <$> (v .:: "name") <*> (v .:: "part")---- | Import list-data ImportList = ImportList {-	_hidingList :: Bool,-	_importSpecs :: [ImportSpec] }-		deriving (Eq, Ord)--instance NFData ImportList where-	rnf (ImportList h ls) = rnf h `seq` rnf ls--instance Show ImportList where-	show (ImportList h ls) = (if h then ("hiding " ++) else id) $ "(" ++ intercalate ", " (map show ls) ++ ")"--instance ToJSON ImportList where-	toJSON (ImportList h ls) = object [-		"hiding" .= h,-		"specs" .= ls]--instance FromJSON ImportList where-	parseJSON = withObject "import-list" $ \v -> ImportList <$>-		v .:: "hiding" <*>-		v .:: "specs"---- | Module import-data Import = Import {-	_importModuleName :: Text,-	_importIsQualified :: Bool,-	_importAs :: Maybe Text,-	_importList :: Maybe ImportList,-	_importPosition :: Maybe Position }-		deriving (Eq, Ord)--instance NFData Import where-	rnf (Import m q a il l) = rnf m `seq` rnf q `seq` rnf a `seq` rnf il `seq` rnf l--instance Show Import where-	show i = concat [-		"import ",-		if _importIsQualified i then "qualified " else "",-		unpack $ _importModuleName i,-		maybe "" ((" as " ++) . unpack) (_importAs i),-		maybe "" ((" " ++) . show) (_importList i)]--instance ToJSON Import where-	toJSON i = object $ noNulls [-		"name" .= _importModuleName i,-		"qualified" .= _importIsQualified i,-		"as" .= _importAs i,-		"import-list" .= _importList i,-		"pos" .= _importPosition i]--instance FromJSON Import where-	parseJSON = withObject "import" $ \v -> Import <$>-		v .:: "name" <*>-		v .:: "qualified" <*>-		v .::? "as" <*>-		v .::? "import-list" <*>-		v .::? "pos"---- | Module id-data ModuleId = ModuleId {-	_moduleIdName :: Text,-	_moduleIdLocation :: ModuleLocation }-		deriving (Eq, Ord)--instance NFData ModuleId where-	rnf (ModuleId n l) = rnf n `seq` rnf l--instance Show ModuleId where-	show (ModuleId n l) = "module " ++ unpack n ++ " from " ++ show l--instance ToJSON ModuleId where-	toJSON m = object [-		"name" .= _moduleIdName m,-		"location" .= _moduleIdLocation m]--instance FromJSON ModuleId where-	parseJSON = withObject "module id" $ \v -> ModuleId <$>-		v .:: "name" <*>-		v .:: "location"---- | Module-data Module = Module {-	_moduleName :: Text,-	_moduleDocs :: Maybe Text,-	_moduleLocation :: ModuleLocation,-	_moduleExports :: Maybe [Export],-	_moduleImports :: [Import],-	_moduleDeclarations :: [Declaration] }-		deriving (Ord)--instance ToJSON Module where-	toJSON m = object $ noNulls [-		"name" .= _moduleName m,-		"docs" .= _moduleDocs m,-		"location" .= _moduleLocation m,-		"exports" .= _moduleExports m,-		"imports" .= _moduleImports m,-		"declarations" .= _moduleDeclarations m]--instance FromJSON Module where-	parseJSON = withObject "module" $ \v -> Module <$>-		v .:: "name" <*>-		v .::? "docs" <*>-		v .:: "location" <*>-		v .::? "exports" <*>-		v .::?! "imports" <*>-		v .::?! "declarations"--instance NFData Module where-	rnf (Module n d s e i ds) = rnf n `seq` rnf d `seq` rnf s `seq` rnf e `seq` rnf i `seq` rnf ds--instance Eq Module where-	l == r = _moduleName l == _moduleName r && _moduleLocation l == _moduleLocation r--instance Show Module where-	show m = unlines $ filter (not . null) [-		"module " ++ unpack (_moduleName m),-		"\tlocation: " ++ show (_moduleLocation m),-		"\texports: " ++ maybe "*" (intercalate ", " . map show) (_moduleExports m),-		"\timports:",-		unlines $ map (tab 2 . show) $ _moduleImports m,-		"\tdeclarations:",-		unlines $ map (tabs 2 . show) $ _moduleDeclarations m,-		maybe "" (("\tdocs: " ++) . unpack) (_moduleDocs m)]--moduleId :: Simple Lens Module ModuleId-moduleId = lens-	(uncurry ModuleId . (_moduleName &&& _moduleLocation))-	(\m mi -> m { _moduleName = _moduleIdName mi, _moduleLocation = _moduleIdLocation mi })---- | Module contents-moduleContents :: Module -> [String]-moduleContents = map showDecl . _moduleDeclarations where-	showDecl d = brief d ++ maybe "" ((" -- " ++) . unpack) (_declarationDocs d)---- | Declaration-data Declaration = Declaration {-	_declarationName :: Text,-	_declarationDefined :: Maybe ModuleId, -- ^ Where declaration defined, @Nothing@ if here-	_declarationImported :: Maybe [Import], -- ^ Declaration imported with. @Nothing@ if unknown (cabal modules) or here (source file)-	_declarationDocs :: Maybe Text,-	_declarationPosition :: Maybe Position,-	_declaration :: DeclarationInfo }-		deriving (Eq, Ord)--instance NFData Declaration where-	rnf (Declaration n def is d l x) = rnf n `seq` rnf def `seq` rnf is `seq` rnf d `seq` rnf l `seq` rnf x--instance Show Declaration where-	show d = unlines $ filter (not . null) [-		brief d,-		maybe "" (("\tdocs: " ++) . unpack) $ _declarationDocs d,-		maybe "" (("\tdefined in: " ++) . show) $ _declarationDefined d,-		maybe "" (("\tlocation: " ++ ) . show) $ _declarationPosition d]--instance ToJSON Declaration where-	toJSON d = object $ noNulls [-		"name" .= _declarationName d,-		"defined" .= _declarationDefined d,-		"imported" .= _declarationImported d,-		"docs" .= _declarationDocs d,-		"pos" .= _declarationPosition d,-		"decl" .= _declaration d]--instance FromJSON Declaration where-	parseJSON = withObject "declaration" $ \v -> Declaration <$>-		v .:: "name" <*>-		v .::? "defined" <*>-		v .::? "imported" <*>-		v .::? "docs" <*>-		v .::? "pos" <*>-		v .:: "decl"---- | Minimal declaration info without defined, docs and position-minimalDecl :: Lens' Declaration Declaration-minimalDecl = lens to' from' where-	to' :: Declaration -> Declaration-	to' decl' = decl' { _declarationDefined = Nothing, _declarationDocs = Nothing, _declarationPosition = Nothing }-	from' :: Declaration -> Declaration -> Declaration-	from' decl' mdecl = decl' { _declarationName = _declarationName mdecl, _declarationImported = _declarationImported mdecl, _declaration = _declaration mdecl }---- | Common info for type, newtype, data and class-data TypeInfo = TypeInfo {-	_typeInfoContext :: Maybe Text, -- FIXME: Why not list of contexts?-	_typeInfoArgs :: [Text],-	_typeInfoDefinition :: Maybe Text,-	_typeInfoFunctions :: [Text] }-		deriving (Eq, Ord, Read, Show)--instance NFData TypeInfo where-	rnf (TypeInfo c a d f) = rnf c `seq` rnf a `seq` rnf d `seq` rnf f--instance ToJSON TypeInfo where-	toJSON t = object $ noNulls [-		"ctx" .= _typeInfoContext t,-		"args" .= _typeInfoArgs t,-		"def" .= _typeInfoDefinition t,-		"funs" .= _typeInfoFunctions t]--instance FromJSON TypeInfo where-	parseJSON = withObject "type info" $ \v -> TypeInfo <$>-		v .::? "ctx" <*>-		v .::?! "args" <*>-		v .::? "def" <*>-		v .::?! "funs"--showTypeInfo :: TypeInfo -> String -> String -> String-showTypeInfo ti pre name = concat [-	pre,-	maybe "" ((++ " =>") . unpack) (_typeInfoContext ti), " ",-	name, " ",-	unwords (map unpack $ _typeInfoArgs ti),-	maybe "" ((" = " ++) . unpack) (_typeInfoDefinition ti)]---- | Declaration info-data DeclarationInfo =-	Function { _functionType :: Maybe Text, _localDeclarations :: [Declaration], _related :: Maybe Text } |-	Type { _typeInfo :: TypeInfo } |-	NewType { _typeInfo :: TypeInfo } |-	Data { _typeInfo :: TypeInfo } |-	Class { _typeInfo :: TypeInfo }-		deriving (Ord)---- | Get function type of type info-declarationInfo :: DeclarationInfo -> Either (Maybe Text, [Declaration], Maybe Text) TypeInfo-declarationInfo (Function t ds r) = Left (t, ds, r)-declarationInfo (Type ti) = Right ti-declarationInfo (NewType ti) = Right ti-declarationInfo (Data ti) = Right ti-declarationInfo (Class ti) = Right ti--declarationTypeCtor :: String -> TypeInfo -> DeclarationInfo-declarationTypeCtor "type" = Type-declarationTypeCtor "newtype" = NewType-declarationTypeCtor "data" = Data-declarationTypeCtor "class" = Class-declarationTypeCtor _ = error "Invalid type constructor name"--declarationTypeName :: DeclarationInfo -> Maybe String-declarationTypeName (Type _) = Just "type"-declarationTypeName (NewType _) = Just "newtype"-declarationTypeName (Data _) = Just "data"-declarationTypeName (Class _) = Just "class"-declarationTypeName _ = Nothing--instance NFData DeclarationInfo where-	rnf (Function f ds r) = rnf f `seq` rnf ds `seq` rnf r-	rnf (Type i) = rnf i-	rnf (NewType i) = rnf i-	rnf (Data i) = rnf i-	rnf (Class i) = rnf i--instance Eq DeclarationInfo where-	(Function l lds lr) == (Function r rds rr) = l == r && lds == rds && lr == rr-	(Type _) == (Type _) = True-	(NewType _) == (NewType _) = True-	(Data _) == (Data _) = True-	(Class _) == (Class _) = True-	_ == _ = False--instance ToJSON DeclarationInfo where-	toJSON i = case declarationInfo i of-		Left (t, ds, r) -> object $ noNulls ["what" .= ("function" :: String), "type" .= t, "locals" .= ds, "related" .= r]-		Right ti -> object ["what" .= declarationTypeName i, "info" .= ti]--instance FromJSON DeclarationInfo where-	parseJSON = withObject "declaration info" $ \v -> do-		w <- fmap (id :: String -> String) $ v .:: "what"-		if w == "function"-			then Function <$> v .::? "type" <*> v .::?! "locals" <*> v .::? "related"-			else declarationTypeCtor w <$> v .:: "info"---- | Symbol in context of some module-data ModuleDeclaration = ModuleDeclaration {-	_declarationModuleId :: ModuleId,-	_moduleDeclaration :: Declaration }-		deriving (Eq, Ord)--instance NFData ModuleDeclaration where-	rnf (ModuleDeclaration m s) = rnf m `seq` rnf s--instance Show ModuleDeclaration where-	show (ModuleDeclaration m s) = unlines $ filter (not . null) [-		show s,-		"\tmodule: " ++ show (_moduleIdLocation m)]--instance ToJSON ModuleDeclaration where-	toJSON d = object [-		"module-id" .= _declarationModuleId d,-		"declaration" .= _moduleDeclaration d]--instance FromJSON ModuleDeclaration where-	parseJSON = withObject "module declaration" $ \v -> ModuleDeclaration <$>-		v .:: "module-id" <*>-		v .:: "declaration"---- | Symbol exported with-data ExportedDeclaration = ExportedDeclaration {-	_exportedBy :: [ModuleId],-	_exportedDeclaration :: Declaration }-		deriving (Eq, Ord)--instance NFData ExportedDeclaration where-	rnf (ExportedDeclaration m s) = rnf m `seq` rnf s--instance Show ExportedDeclaration where-	show (ExportedDeclaration m s) = unlines $ filter (not . null) [-		show s,-		"\tmodules: " ++ intercalate ", " (map (show . _moduleIdLocation) m)]--instance ToJSON ExportedDeclaration where-	toJSON d = object [-		"exported-by" .= _exportedBy d,-		"declaration" .= _exportedDeclaration d]--instance FromJSON ExportedDeclaration where-	parseJSON = withObject "exported declaration" $ \v -> ExportedDeclaration <$>-		v .:: "exported-by" <*>-		v .:: "declaration"---- | Inspection data-data Inspection =-	-- | No inspection-	InspectionNone |-	-- | Time and flags of inspection-	InspectionAt {-		_inspectionAt :: POSIXTime,-		_inspectionOpts :: [String] }-			deriving (Eq, Ord)--instance NFData Inspection where-	rnf InspectionNone = ()-	rnf (InspectionAt t fs) = rnf t `seq` rnf fs--instance Show Inspection where-	show InspectionNone = "none"-	show (InspectionAt tm fs) = "mtime " ++ show tm ++ ", flags [" ++ intercalate ", " fs ++ "]"--instance Read POSIXTime where-	readsPrec i = map (first (fromIntegral :: Integer -> POSIXTime)) . readsPrec i--instance Monoid Inspection where-	mempty = InspectionNone-	mappend InspectionNone r = r-	mappend l InspectionNone = l-	mappend (InspectionAt ltm lopts) (InspectionAt rtm ropts)-		| ltm >= rtm = InspectionAt ltm lopts-		| otherwise = InspectionAt rtm ropts--instance ToJSON Inspection where-	toJSON InspectionNone = object ["inspected" .= False]-	toJSON (InspectionAt tm fs) = object [-		"mtime" .= (floor tm :: Integer),-		"flags" .= fs]--instance FromJSON Inspection where-	parseJSON = withObject "inspection" $ \v ->-		((const InspectionNone :: Bool -> Inspection) <$> v .:: "inspected") <|>-		(InspectionAt <$> (fromInteger <$> v .:: "mtime") <*> (v .:: "flags"))---- | Inspected entity-data Inspected i t a = Inspected {-	_inspection :: Inspection,-	_inspectedId :: i,-	_inspectionTags :: Set t,-	_inspectionResult :: Either HsDevError a }--inspectedTup :: Inspected i t a -> (Inspection, i, Set t, Maybe a)-inspectedTup (Inspected insp i tags res) = (insp, i, tags, either (const Nothing) Just res)--instance (Eq i, Eq t, Eq a) => Eq (Inspected i t a) where-	(==) = (==) `on` inspectedTup--instance (Ord i, Ord t, Ord a) => Ord (Inspected i t a) where-	compare = comparing inspectedTup--instance Functor (Inspected i t) where-	fmap f insp = insp {-		_inspectionResult = fmap f (_inspectionResult insp) }--instance Foldable (Inspected i t) where-	foldMap f = either mempty f . _inspectionResult--instance Traversable (Inspected i t) where-	traverse f (Inspected insp i ts r) = Inspected insp i ts <$> either (pure . Left) (liftA Right . f) r--instance (NFData i, NFData t, NFData a) => NFData (Inspected i t a) where-	rnf (Inspected t i ts r) = rnf t `seq` rnf i `seq` rnf ts `seq` rnf r---- | Empty tags-noTags :: Set t-noTags = S.empty--data ModuleTag = InferredTypesTag | RefinedDocsTag deriving (Eq, Ord, Read, Show, Enum, Bounded)--instance NFData ModuleTag where-	rnf InferredTypesTag = ()-	rnf RefinedDocsTag = ()--instance ToJSON ModuleTag where-	toJSON InferredTypesTag = toJSON ("types" :: String)-	toJSON RefinedDocsTag = toJSON ("docs" :: String)--instance FromJSON ModuleTag where-	parseJSON = withText "module-tag" $ \txt -> msum [-		guard (txt == "types") >> return InferredTypesTag,-		guard (txt == "docs") >> return RefinedDocsTag]---- | Inspected module-type InspectedModule = Inspected ModuleLocation ModuleTag Module--instance Show InspectedModule where-	show (Inspected i mi ts m) = unlines [either showError show m, "\tinspected: " ++ show i, "\ttags: " ++ intercalate ", " (map show $ S.toList ts)] where-		showError :: HsDevError -> String-		showError e = unlines $ ("\terror: " ++ show e) : case mi of-			FileModule f p -> ["file: " ++ f, "project: " ++ maybe "" (view projectPath) p]-			InstalledModule c p n -> ["cabal: " ++ show c, "package: " ++ maybe "" show p, "name: " ++ n]-			ModuleSource src -> ["source: " ++ fromMaybe "" src]--instance ToJSON InspectedModule where-	toJSON im = object [-		"inspection" .= _inspection im,-		"location" .= _inspectedId im,-		"tags" .= S.toList (_inspectionTags im),-		either ("error" .=) ("module" .=) (_inspectionResult im)]--instance FromJSON InspectedModule where-	parseJSON = withObject "inspected module" $ \v -> Inspected <$>-		v .:: "inspection" <*>-		v .:: "location" <*>-		(S.fromList <$> (v .::?! "tags")) <*>-		((Left <$> v .:: "error") <|> (Right <$> v .:: "module"))--notInspected :: ModuleLocation -> InspectedModule-notInspected mloc = Inspected mempty mloc noTags (Left $ NotInspected mloc)--instance Symbol Module where-	symbolName = _moduleName-	symbolQualifiedName = _moduleName-	symbolDocs = _moduleDocs-	symbolLocation m = Location (_moduleLocation m) Nothing--instance Symbol ModuleId where-	symbolName = _moduleIdName-	symbolQualifiedName = _moduleIdName-	symbolDocs = const Nothing-	symbolLocation m = Location (_moduleIdLocation m) Nothing--instance Symbol Declaration where-	symbolName = _declarationName-	symbolQualifiedName = _declarationName-	symbolDocs = _declarationDocs-	symbolLocation d = Location (ModuleSource Nothing) (_declarationPosition d)--instance Symbol ModuleDeclaration where-	symbolName = _declarationName . _moduleDeclaration-	symbolQualifiedName d = qualifiedName (_declarationModuleId d) (_moduleDeclaration d) where-		qualifiedName :: ModuleId -> Declaration -> Text-		qualifiedName m' d' = T.concat [_moduleIdName m', ".", _declarationName d']-	symbolDocs = _declarationDocs . _moduleDeclaration-	symbolLocation d = set locationPosition (_declarationPosition $ _moduleDeclaration d)-		(symbolLocation . _declarationModuleId $ d)--instance Documented ModuleId where-	brief m = unpack (_moduleIdName m) ++ " in " ++ show (_moduleIdLocation m)--instance Documented Module where-	brief m = unpack (_moduleName m) ++ " in " ++ show (_moduleLocation m)-	detailed m = unlines $ header ++ docs ++ cts where-		header = [brief m, ""]-		docs = maybe [] (return . unpack) $ _moduleDocs m-		cts = moduleContents m--instance Documented Declaration where-	brief d = case declarationInfo $ _declaration d of-		Left (f, _, _) -> name ++ maybe "" ((" :: " ++) . unpack) f-		Right ti -> showTypeInfo ti (fromMaybe err $ declarationTypeName $ _declaration d) name-		where-			name = unpack $ _declarationName d-			err = error "Impossible happened: declarationTypeName"--instance Documented ModuleDeclaration where-	brief = brief . _moduleDeclaration--makeLenses ''Export-makeLenses ''ImportSpec-makeLenses ''ImportList-makeLenses ''Import-makeLenses ''ModuleId-makeLenses ''DeclarationInfo-makeLenses ''TypeInfo-makeLenses ''Declaration-makeLenses ''Module-makeLenses ''ModuleDeclaration-makeLenses ''ExportedDeclaration-makeLenses ''Inspection-makeLenses ''Inspected+{-# LANGUAGE CPP, TemplateHaskell, TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Symbols.Types (
+	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, symbolType, patternType, patternConstructor,
+	ScopeSymbol(..), scopeQualifier, scopeSymbol,
+	SymbolUsage(..), symbolUsed, symbolUsedIn, symbolUsedPosition,
+	infoOf, nullifyInfo,
+	Inspection(..), inspectionAt, inspectionOpts, fresh, Inspected(..), inspection, inspectedKey, inspectionTags, inspectionResult, inspected,
+	inspectedTup, noTags, tag, ModuleTag(..), InspectedModule, notInspected,
+
+	module HsDev.PackageDb.Types,
+	module HsDev.Project,
+	module HsDev.Symbols.Name,
+	module HsDev.Symbols.Class,
+	module HsDev.Symbols.Location,
+	module HsDev.Symbols.Documented
+	) where
+
+import Control.Arrow
+import Control.Applicative
+import Control.Lens hiding ((.=))
+import Control.Monad
+import Control.DeepSeq (NFData(..))
+import Data.Aeson
+import Data.Aeson.Types (Pair, Parser)
+import Data.List (intercalate)
+import Data.Maybe (catMaybes)
+import Data.Monoid (Any(..))
+import Data.Function
+import Data.Ord
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Set (Set)
+import qualified Data.Set as S
+import Data.Time.Clock.POSIX (POSIXTime)
+import Language.Haskell.Exts (QName(..), ModuleName(..), Boxed(..), SpecialCon(..), Fixity(..), Assoc(..))
+import qualified Language.Haskell.Exts as Exts (Name(..))
+import Text.Format
+
+import Control.Apply.Util (chain)
+import HsDev.PackageDb.Types
+import HsDev.Project
+import HsDev.Symbols.Name
+import HsDev.Symbols.Class
+import HsDev.Symbols.Location
+import HsDev.Symbols.Documented
+import HsDev.Symbols.Parsed
+import HsDev.Types
+import HsDev.Util ((.::), (.::?), (.::?!), noNulls, objectUnion)
+import System.Directory.Paths
+
+instance NFData l => NFData (ModuleName l) where
+	rnf (ModuleName l n) = rnf l `seq` rnf n
+
+instance NFData l => NFData (Exts.Name l) where
+	rnf (Exts.Ident l s) = rnf l `seq` rnf s
+	rnf (Exts.Symbol l s) = rnf l `seq` rnf s
+
+instance NFData Boxed where
+	rnf Boxed = ()
+	rnf Unboxed = ()
+
+instance NFData l => NFData (SpecialCon l) where
+	rnf (UnitCon l) = rnf l
+	rnf (ListCon l) = rnf l
+	rnf (FunCon l) = rnf l
+	rnf (TupleCon l b i) = rnf l `seq` rnf b `seq` rnf i
+	rnf (Cons l) = rnf l
+	rnf (UnboxedSingleCon l) = rnf l
+#if MIN_VERSION_haskell_src_exts(1,20,0)
+	rnf (ExprHole l) = rnf l
+#endif
+
+instance NFData l => NFData (QName l) where
+	rnf (Qual l m n) = rnf l `seq` rnf m `seq` rnf n
+	rnf (UnQual l n) = rnf l `seq` rnf n
+	rnf (Special l s) = rnf l `seq` rnf s
+
+-- | Module
+data Module = Module {
+	_moduleId :: ModuleId,
+	_moduleDocs :: Maybe Text,
+	_moduleImports :: [Text], -- 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
+	_moduleSource :: Maybe Parsed } -- source of module
+
+-- | Make each symbol appear only once
+moduleSymbols :: Traversal' Module Symbol
+moduleSymbols f m = getBack <$> (each . _1) f revList where
+	revList = M.toList $ M.unionsWith mappend $ concat [
+		[M.singleton sym ([], Any True) | sym <- _moduleExports m],
+		[M.singleton sym ([nm], Any False) | (nm, syms) <- M.toList (_moduleScope m), sym <- syms]]
+	getBack syms = m {
+		_moduleExports = [sym' | (sym', (_, Any True)) <- syms],
+		_moduleScope = M.unionsWith (++) [M.singleton n [sym'] | (sym', (ns, _)) <- syms, n <- ns] }
+
+exportedSymbols :: Traversal' Module Symbol
+exportedSymbols f m = (\e -> m { _moduleExports = e }) <$> traverse f (_moduleExports m)
+
+scopeSymbols :: Traversal' Module (Symbol, [Name])
+scopeSymbols f m = (\s -> m { _moduleScope = invMap s }) <$> traverse f (M.toList . invMap . M.toList $ _moduleScope m) where
+	invMap :: Ord b => [(a, [b])] -> Map b [a]
+	invMap es = M.unionsWith (++) [M.singleton v [k] | (k, vs) <- es, v <- vs]
+
+fixitiesMap :: Lens' Module (Map Name Fixity)
+fixitiesMap = lens g' s' where
+	g' m = mconcat [M.singleton n f | f@(Fixity _ _ n) <- _moduleFixities m]
+	s' m m' = m { _moduleFixities = M.elems m' }
+
+instance ToJSON (Assoc ()) where
+	toJSON (AssocNone _) = toJSON ("none" :: String)
+	toJSON (AssocLeft _) = toJSON ("left" :: String)
+	toJSON (AssocRight _) = toJSON ("right" :: String)
+
+instance FromJSON (Assoc ()) where
+	parseJSON = withText "assoc" $ \txt -> msum [
+		guard (txt == "none") >> return (AssocNone ()),
+		guard (txt == "left") >> return (AssocLeft ()),
+		guard (txt == "right") >> return (AssocRight ())]
+
+instance ToJSON Fixity where
+	toJSON (Fixity assoc pr n) = object $ noNulls [
+		"assoc" .= assoc,
+		"prior" .= pr,
+		"name" .= fromName n]
+
+instance FromJSON Fixity where
+	parseJSON = withObject "fixity" $ \v -> Fixity <$>
+		v .:: "assoc" <*>
+		v .:: "prior" <*>
+		(toName <$> v .:: "name")
+
+instance ToJSON Module where
+	toJSON m = object $ noNulls [
+		"id" .= _moduleId m,
+		"docs" .= _moduleDocs m,
+		"exports" .= _moduleExports m,
+		"fixities" .= _moduleFixities m]
+
+instance FromJSON Module where
+	parseJSON = withObject "module" $ \v -> Module <$>
+		v .:: "id" <*>
+		v .::? "docs" <*>
+		pure mempty <*>
+		v .::?! "exports" <*>
+		v .::?! "fixities" <*>
+		pure mempty <*>
+		pure Nothing
+
+instance NFData (Assoc ()) where
+	rnf (AssocNone _) = ()
+	rnf (AssocLeft _) = ()
+	rnf (AssocRight _) = ()
+
+instance NFData Fixity where
+	rnf (Fixity assoc pr n) = rnf assoc `seq` rnf pr `seq` rnf n
+
+instance NFData Module where
+	rnf (Module i d is e fs s msrc) = msrc `seq` rnf i `seq` rnf d `seq` rnf is `seq` rnf e `seq` rnf fs `seq` rnf s
+
+instance Eq Module where
+	l == r = _moduleId l == _moduleId r
+
+instance Ord Module where
+	compare l r = compare (_moduleId l) (_moduleId r)
+
+instance Show Module where
+	show = show . _moduleId
+
+data Symbol = Symbol {
+	_symbolId :: SymbolId,
+	_symbolDocs :: Maybe Text,
+	_symbolPosition :: Maybe Position,
+	_symbolInfo :: SymbolInfo }
+
+instance Eq Symbol where
+	l == r = (_symbolId l, symbolType l) == (_symbolId r, symbolType r)
+
+instance Ord Symbol where
+	compare l r = compare (_symbolId l, symbolType l) (_symbolId r, symbolType r)
+
+instance NFData Symbol where
+	rnf (Symbol i d l info) = rnf i `seq` rnf d `seq` rnf l `seq` rnf info
+
+instance Show Symbol where
+	show = show . _symbolId
+
+instance ToJSON Symbol where
+	toJSON s = object $ noNulls [
+		"id" .= _symbolId s,
+		"docs" .= _symbolDocs s,
+		"pos" .= _symbolPosition s,
+		"info" .= _symbolInfo s]
+
+instance FromJSON Symbol where
+	parseJSON = withObject "symbol" $ \v -> Symbol <$>
+		v .:: "id" <*>
+		v .::? "docs" <*>
+		v .::? "pos" <*>
+		v .:: "info"
+
+data SymbolInfo =
+	Function { _functionType :: Maybe Text } |
+	Method { _functionType :: Maybe Text, _parentClass :: Text } |
+	Selector { _functionType :: Maybe Text, _parentType :: Text, _selectorConstructors :: [Text] } |
+	Constructor { _typeArgs :: [Text], _parentType :: Text } |
+	Type { _typeArgs :: [Text], _typeContext :: [Text] } |
+	NewType { _typeArgs :: [Text], _typeContext :: [Text] } |
+	Data { _typeArgs :: [Text], _typeContext :: [Text] } |
+	Class { _typeArgs :: [Text], _typeContext :: [Text] } |
+	TypeFam { _typeArgs :: [Text], _typeContext :: [Text], _familyAssociate :: Maybe Text } |
+	DataFam { _typeArgs :: [Text], _typeContext :: [Text], _familyAssociate :: Maybe Text } |
+	PatConstructor { _typeArgs :: [Text], _patternType :: Maybe Text } |
+	PatSelector { _functionType :: Maybe Text, _patternType :: Maybe Text, _patternConstructor :: Text }
+		deriving (Eq, Ord, Read, Show)
+
+instance NFData SymbolInfo where
+	rnf (Function ft) = rnf ft
+	rnf (Method ft cls) = rnf ft `seq` rnf cls
+	rnf (Selector ft t cs) = rnf ft `seq` rnf t `seq` rnf cs
+	rnf (Constructor as t) = rnf as `seq` rnf t
+	rnf (Type as ctx) = rnf as `seq` rnf ctx
+	rnf (NewType as ctx) = rnf as `seq` rnf ctx
+	rnf (Data as ctx) = rnf as `seq` rnf ctx
+	rnf (Class as ctx) = rnf as `seq` rnf ctx
+	rnf (TypeFam as ctx a) = rnf as `seq` rnf ctx `seq` rnf a
+	rnf (DataFam as ctx a) = rnf as `seq` rnf ctx `seq` rnf a
+	rnf (PatConstructor as t) = rnf as `seq` rnf t
+	rnf (PatSelector ft t c) = rnf ft `seq` rnf t `seq` rnf c
+
+instance ToJSON SymbolInfo where
+	toJSON (Function ft) = object [what "function", "type" .= ft]
+	toJSON (Method ft cls) = object [what "method", "type" .= ft, "class" .= cls]
+	toJSON (Selector ft t cs) = object [what "selector", "type" .= ft, "parent" .= t, "constructors" .= cs]
+	toJSON (Constructor as t) = object [what "ctor", "args" .= as, "type" .= t]
+	toJSON (Type as ctx) = object [what "type", "args" .= as, "ctx" .= ctx]
+	toJSON (NewType as ctx) = object [what "newtype", "args" .= as, "ctx" .= ctx]
+	toJSON (Data as ctx) = object [what "data", "args" .= as, "ctx" .= ctx]
+	toJSON (Class as ctx) = object [what "class", "args" .= as, "ctx" .= ctx]
+	toJSON (TypeFam as ctx a) = object [what "type-family", "args" .= as, "ctx" .= ctx, "associate" .= a]
+	toJSON (DataFam as ctx a) = object [what "data-family", "args" .= as, "ctx" .= ctx, "associate" .= a]
+	toJSON (PatConstructor as t) = object [what "pat-ctor", "args" .= as, "pat-type" .= t]
+	toJSON (PatSelector ft t c) = object [what "pat-selector", "type" .= ft, "pat-type" .= t, "constructor" .= c]
+
+class EmptySymbolInfo a where
+	infoOf :: a -> SymbolInfo
+
+instance EmptySymbolInfo SymbolInfo where
+	infoOf = id
+
+instance (Monoid a, EmptySymbolInfo r) => EmptySymbolInfo (a -> r) where
+	infoOf f = infoOf $ f mempty
+
+symbolType :: Symbol -> String
+symbolType s = case _symbolInfo s of
+	Function{} -> "function"
+	Method{} -> "method"
+	Selector{} -> "selector"
+	Constructor{} -> "ctor"
+	Type{} -> "type"
+	NewType{} -> "newtype"
+	Data{} -> "data"
+	Class{} -> "class"
+	TypeFam{} -> "type-family"
+	DataFam{} -> "data-family"
+	PatConstructor{} -> "pat-ctor"
+	PatSelector{} -> "pat-selector"
+
+what :: String -> Pair
+what n = "what" .= n
+
+instance FromJSON SymbolInfo where
+	parseJSON = withObject "symbol info" $ \v -> msum [
+		gwhat "function" v >> (Function <$> v .::? "type"),
+		gwhat "method" v >> (Method <$> v .::? "type" <*> v .:: "class"),
+		gwhat "selector" v >> (Selector <$> v .::? "type" <*> v .:: "parent" <*> v .::?! "constructors"),
+		gwhat "ctor" v >> (Constructor <$> v .::?! "args" <*> v .:: "type"),
+		gwhat "type" v >> (Type <$> v .::?! "args" <*> v .::?! "ctx"),
+		gwhat "newtype" v >> (NewType <$> v .::?! "args" <*> v .::?! "ctx"),
+		gwhat "data" v >> (Data <$> v .::?! "args" <*> v .::?! "ctx"),
+		gwhat "class" v >> (Class <$> v .::?! "args" <*> v .::?! "ctx"),
+		gwhat "type-family" v >> (TypeFam <$> v .::?! "args" <*> v .::?! "ctx" <*> v .::? "associate"),
+		gwhat "data-family" v >> (DataFam <$> v .::?! "args" <*> v .::?! "ctx" <*> v .::? "associate"),
+		gwhat "pat-ctor" v >> (PatConstructor <$> v .::?! "args" <*> v .::? "pat-type"),
+		gwhat "pat-selector" v >> (PatSelector <$> v .::? "type" <*> v .::? "pat-type" <*> v .:: "constructor")]
+
+gwhat :: String -> Object -> Parser ()
+gwhat n v = do
+	s <- v .:: "what"
+	guard (s == n)
+
+-- | Symbol in scope with qualifier
+data ScopeSymbol = ScopeSymbol {
+	_scopeQualifier :: Maybe Text,
+	_scopeSymbol :: SymbolId }
+		deriving (Eq, Ord)
+
+instance Show ScopeSymbol where
+	show (ScopeSymbol q s) = maybe "" (\q' -> T.unpack q' ++ ".") q ++ show s
+
+instance ToJSON ScopeSymbol where
+	toJSON (ScopeSymbol q s) = toJSON s `objectUnion` object (noNulls ["qualifier" .= q])
+
+instance FromJSON ScopeSymbol where
+	parseJSON = withObject "scope-symbol" $ \v -> ScopeSymbol <$>
+		(v .::? "qualifier") <*>
+		parseJSON (Object v)
+
+-- | Symbol usage
+data SymbolUsage = SymbolUsage {
+	_symbolUsed :: Symbol,
+	_symbolUsedIn :: ModuleId,
+	_symbolUsedPosition :: Position }
+		deriving (Eq, Ord)
+
+instance Show SymbolUsage where
+	show (SymbolUsage s m p) = show s ++ " at " ++ show m ++ ":" ++ show p
+
+instance ToJSON SymbolUsage where
+	toJSON (SymbolUsage s m p) = object $ noNulls ["symbol" .= s, "in" .= m, "at" .= p]
+
+instance FromJSON SymbolUsage where
+	parseJSON = withObject "symbol-usage" $ \v -> SymbolUsage <$>
+		v .:: "symbol" <*>
+		v .:: "in" <*>
+		v .:: "at"
+
+-- | Inspection data
+data Inspection =
+	-- | No inspection
+	InspectionNone |
+	-- | Time and flags of inspection
+	InspectionAt {
+		_inspectionAt :: POSIXTime,
+		_inspectionOpts :: [Text] }
+			deriving (Eq, Ord)
+
+instance NFData Inspection where
+	rnf InspectionNone = ()
+	rnf (InspectionAt t fs) = rnf t `seq` rnf fs
+
+instance Show Inspection where
+	show InspectionNone = "none"
+	show (InspectionAt tm fs) = "mtime " ++ show tm ++ ", flags [" ++ intercalate ", " (map T.unpack fs) ++ "]"
+
+instance Read POSIXTime where
+	readsPrec i = map (first (fromIntegral :: Integer -> POSIXTime)) . readsPrec i
+
+instance Monoid Inspection where
+	mempty = InspectionNone
+	mappend InspectionNone r = r
+	mappend l InspectionNone = l
+	mappend (InspectionAt ltm lopts) (InspectionAt rtm ropts)
+		| ltm >= rtm = InspectionAt ltm lopts
+		| otherwise = InspectionAt rtm ropts
+
+instance ToJSON Inspection where
+	toJSON InspectionNone = object ["inspected" .= False]
+	toJSON (InspectionAt tm fs) = object [
+		"mtime" .= (fromRational (toRational tm) :: Double),
+		"flags" .= fs]
+
+instance FromJSON Inspection where
+	parseJSON = withObject "inspection" $ \v ->
+		((const InspectionNone :: Bool -> Inspection) <$> v .:: "inspected") <|>
+		(InspectionAt <$> ((fromRational . (toRational :: Double -> Rational)) <$> v .:: "mtime") <*> (v .:: "flags"))
+
+-- | Is left @Inspection@ fresh comparing to right one
+fresh :: Inspection -> Inspection -> Bool
+fresh InspectionNone InspectionNone = True
+fresh InspectionNone _ = False
+fresh _ InspectionNone = True
+fresh (InspectionAt tm _) (InspectionAt tm' _) = tm' - tm < 0.01
+
+-- | Inspected entity
+data Inspected k t a = Inspected {
+	_inspection :: Inspection,
+	_inspectedKey :: k,
+	_inspectionTags :: Set t,
+	_inspectionResult :: Either HsDevError a }
+
+inspectedTup :: Inspected k t a -> (Inspection, k, Set t, Maybe a)
+inspectedTup (Inspected insp i tags res) = (insp, i, tags, either (const Nothing) Just res)
+
+instance (Eq k, Eq t, Eq a) => Eq (Inspected k t a) where
+	(==) = (==) `on` inspectedTup
+
+instance (Ord k, Ord t, Ord a) => Ord (Inspected k t a) where
+	compare = comparing inspectedTup
+
+instance Functor (Inspected k t) where
+	fmap f insp = insp {
+		_inspectionResult = fmap f (_inspectionResult insp) }
+
+instance Foldable (Inspected k t) where
+	foldMap f = either mempty f . _inspectionResult
+
+instance Traversable (Inspected k t) where
+	traverse f (Inspected insp i ts r) = Inspected insp i ts <$> either (pure . Left) (liftA Right . f) r
+
+instance (NFData k, NFData t, NFData a) => NFData (Inspected k t a) where
+	rnf (Inspected t i ts r) = rnf t `seq` rnf i `seq` rnf ts `seq` rnf r
+
+instance (ToJSON k, ToJSON t, ToJSON a) => ToJSON (Inspected k t a) where
+	toJSON im = object [
+		"inspection" .= _inspection im,
+		"location" .= _inspectedKey im,
+		"tags" .= S.toList (_inspectionTags im),
+		either ("error" .=) ("result" .=) (_inspectionResult im)]
+
+instance (FromJSON k, Ord t, FromJSON t, FromJSON a) => FromJSON (Inspected k t a) where
+	parseJSON = withObject "inspected" $ \v -> Inspected <$>
+		v .:: "inspection" <*>
+		v .:: "location" <*>
+		(S.fromList <$> (v .::?! "tags")) <*>
+		((Left <$> v .:: "error") <|> (Right <$> v .:: "result"))
+
+-- | Empty tags
+noTags :: Set t
+noTags = S.empty
+
+-- | One tag
+tag :: t -> Set t
+tag = S.singleton
+
+data ModuleTag = InferredTypesTag | RefinedDocsTag | OnlyHeaderTag deriving (Eq, Ord, Read, Show, Enum, Bounded)
+
+instance NFData ModuleTag where
+	rnf InferredTypesTag = ()
+	rnf RefinedDocsTag = ()
+	rnf OnlyHeaderTag = ()
+
+instance ToJSON ModuleTag where
+	toJSON InferredTypesTag = toJSON ("types" :: String)
+	toJSON RefinedDocsTag = toJSON ("docs" :: String)
+	toJSON OnlyHeaderTag = toJSON ("header" :: String)
+
+instance FromJSON ModuleTag where
+	parseJSON = withText "module-tag" $ \txt -> msum [
+		guard (txt == "types") >> return InferredTypesTag,
+		guard (txt == "docs") >> return RefinedDocsTag,
+		guard (txt == "header") >> return OnlyHeaderTag]
+
+-- | Inspected module
+type InspectedModule = Inspected ModuleLocation ModuleTag Module
+
+instance Show InspectedModule where
+	show (Inspected i mi ts m) = unlines [either showError show m, "\tinspected: " ++ show i, "\ttags: " ++ intercalate ", " (map show $ S.toList ts)] where
+		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]
+			OtherLocation src -> ["other location: " ++ T.unpack src]
+			NoLocation -> ["no location"]
+
+notInspected :: ModuleLocation -> InspectedModule
+notInspected mloc = Inspected mempty mloc noTags (Left $ NotInspected mloc)
+
+instance Documented ModuleId where
+	brief m = brief $ _moduleLocation m
+	detailed = brief
+
+instance Documented SymbolId where
+	brief s = "{} from {}" ~~ _symbolName s ~~ brief (_symbolModule s)
+	detailed = brief
+
+instance Documented Module where
+	brief = brief . _moduleId
+	detailed m = T.unlines (brief m : info) where
+		info = [
+			"\texports: {}" ~~ T.intercalate ", " (map brief (_moduleExports m))]
+
+instance Documented Symbol where
+	brief = brief . _symbolId
+	detailed s = T.unlines [brief s, info] where
+		info = case _symbolInfo s of
+			Function t -> "\t" `T.append` T.intercalate ", " (catMaybes [Just "function", fmap ("type: {}" ~~) t])
+			Method t p -> "\t" `T.append` T.intercalate ", " (catMaybes [Just "method", fmap ("type: {}" ~~) t, Just $ "parent: {}" ~~ p])
+			Selector t p _ -> "\t" `T.append` T.intercalate ", " (catMaybes [Just "selector", fmap ("type: {}" ~~) t, Just $ "parent: {}" ~~ p])
+			Constructor args p -> "\t" `T.append` T.intercalate ", " ["constructor", "args: {}" ~~ T.unwords args, "parent: {}" ~~ p]
+			Type args ctx -> "\t" `T.append` T.intercalate ", " ["type", "args: {}" ~~ T.unwords args, "ctx: {}" ~~ T.unwords ctx]
+			NewType args ctx -> "\t" `T.append` T.intercalate ", " ["newtype", "args: {}" ~~ T.unwords args, "ctx: {}" ~~ T.unwords ctx]
+			Data args ctx -> "\t" `T.append` T.intercalate ", " ["data", "args: {}" ~~ T.unwords args, "ctx: {}" ~~ T.unwords ctx]
+			Class args ctx -> "\t" `T.append` T.intercalate ", " ["class", "args: {}" ~~ T.unwords args, "ctx: {}" ~~ T.unwords ctx]
+			TypeFam args ctx _ -> "\t" `T.append` T.intercalate ", " ["type family", "args: {}" ~~ T.unwords args, "ctx: {}" ~~ T.unwords ctx]
+			DataFam args ctx _ -> "\t" `T.append` T.intercalate ", " ["data family", "args: {}" ~~ T.unwords args, "ctx: {}" ~~ T.unwords ctx]
+			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 ''Module
+makeLenses ''Symbol
+makeLenses ''SymbolInfo
+makeLenses ''ScopeSymbol
+makeLenses ''SymbolUsage
+makeLenses ''Inspection
+makeLenses ''Inspected
+
+inspected :: Traversal (Inspected k t a) (Inspected k t b) a b
+inspected = inspectionResult . _Right
+
+nullifyInfo :: SymbolInfo -> SymbolInfo
+nullifyInfo = chain [
+	set functionType mempty,
+	set parentClass mempty,
+	set parentType mempty,
+	set selectorConstructors mempty,
+	set typeArgs mempty,
+	set typeContext mempty,
+	set familyAssociate mempty,
+	set patternType mempty,
+	set patternConstructor mempty]
+
+instance Sourced Module where
+	sourcedName = moduleId . moduleName
+	sourcedDocs = moduleDocs . _Just
+	sourcedModule = moduleId
+
+instance Sourced Symbol where
+	sourcedName = symbolId . symbolName
+	sourcedDocs = symbolDocs . _Just
+	sourcedModule = symbolId . symbolModule
+	sourcedLocation = symbolPosition . _Just
− src/HsDev/Symbols/Util.hs
@@ -1,203 +0,0 @@-module HsDev.Symbols.Util (-	projectOf, packageDbOf, packageOf,-	inProject, inDepsOfTarget, inDepsOfFile, inDepsOfProject, inPackageDb, inPackageDbStack, inPackage, inVersion, inFile, inModuleSource, inModule, byFile, installed, standalone,-	imports, qualifier, moduleImported, visible, inScope,-	newestPackage,-	sourceModule, visibleModule, preferredModule, uniqueModules,-	allOf, anyOf-	) where--import Control.Arrow ((***), (&&&), first)-import Control.Lens (view)-import Control.Monad (liftM)-import Data.Function (on)-import Data.Maybe-import Data.List (maximumBy, groupBy, sortBy, partition)-import Data.Ord (comparing)-import Data.String (fromString)-import System.FilePath (normalise)--import HsDev.Symbols-import HsDev.Util (ordNub)---- | Get module project-projectOf :: ModuleId -> Maybe Project-projectOf m = case view moduleIdLocation m of-	FileModule _ proj -> proj-	_ -> Nothing---- | Get module cabal-packageDbOf :: ModuleId -> Maybe PackageDb-packageDbOf m = case view moduleIdLocation m of-	InstalledModule c _ _ -> Just c-	_ -> Nothing---- | Get module package-packageOf :: ModuleId -> Maybe ModulePackage-packageOf m = case view moduleIdLocation m of-	InstalledModule _ package' _ -> package'-	_ -> Nothing---- | Check if module in project-inProject :: Project -> ModuleId -> Bool-inProject p m = projectOf m == Just p---- | Check if module in deps of project target-inDepsOfTarget :: Info -> ModuleId -> Bool-inDepsOfTarget i m = any (`inPackage` m) $ view infoDepends i---- | Check if module in deps of source-inDepsOfFile :: Project -> FilePath -> ModuleId -> Bool-inDepsOfFile p f m = any (`inDepsOfTarget` m) (fileTargets p f)---- | Check if module in deps of project-inDepsOfProject :: Project -> ModuleId -> Bool-inDepsOfProject = maybe (const False) (anyPackage . ordNub . concatMap (view infoDepends) . infos) . view projectDescription where-	anyPackage :: [String] -> ModuleId -> Bool-	anyPackage = liftM or . mapM inPackage---- | Check if module in package-db-inPackageDb :: PackageDb -> ModuleId -> Bool-inPackageDb c m = case view moduleIdLocation m of-	InstalledModule d _ _ -> d == c-	_ -> False---- | Check if module in one of sandboxes-inPackageDbStack :: PackageDbStack -> ModuleId -> Bool-inPackageDbStack dbs m = case view moduleIdLocation m of-	InstalledModule d _ _ -> d `elem` packageDbs dbs-	_ -> False---- | Check if module in package-inPackage :: String -> ModuleId -> Bool-inPackage p m = case view moduleIdLocation m of-	InstalledModule _ package' _ -> Just p == fmap (view packageName) package'-	_ -> False--inVersion :: String -> ModuleId -> Bool-inVersion v m = case view moduleIdLocation m of-	InstalledModule _ package' _ -> Just v == fmap (view packageVersion) package'-	_ -> False---- | Check if module in file-inFile :: FilePath -> ModuleId -> Bool-inFile fpath m = case view moduleIdLocation m of-	FileModule f _ -> f == normalise fpath-	_ -> False---- | Check if module in source-inModuleSource :: Maybe String -> ModuleId -> Bool-inModuleSource src m = case view moduleIdLocation m of-	ModuleSource src' -> src' == src-	_ -> False---- | Check if declaration is in module-inModule :: String -> ModuleId -> Bool-inModule mname m = fromString mname == view moduleIdName m---- | Check if module defined in file-byFile :: ModuleId -> Bool-byFile m = case view moduleIdLocation m of-	FileModule _ _ -> True-	_ -> False---- | Check if module got from cabal database-installed :: ModuleId -> Bool-installed m = case view moduleIdLocation m of-	InstalledModule _ _ _ -> True-	_ -> False---- | Check if module is standalone-standalone :: ModuleId -> Bool-standalone m = case view moduleIdLocation m of-	FileModule _ Nothing -> True-	_ -> False---- | Get list of imports-imports :: Module -> [Import]-imports = view moduleImports---- | Get list of imports, which can be accessed with specified qualifier or unqualified-qualifier :: Module -> Maybe String -> [Import]-qualifier m q = filter (importQualifier (fmap fromString q)) $-	import_ (fromString "Prelude") :-	import_ (view moduleName m) :-	imports m---- | Check if module imported via imports specified-moduleImported :: ModuleId -> [Import] -> Bool-moduleImported m = any (\i -> view moduleIdName m == view importModuleName i)---- | Check if module visible from this module within this project-visible :: Project -> ModuleId -> ModuleId -> Bool-visible p (ModuleId _ (FileModule src _)) m =-	inProject p m || any (`inPackage` m) deps || maybe False ((`elem` deps) . view projectName) (projectOf m)-	where-		deps = concatMap (view infoDepends) $ fileTargets p src-visible _ _ _ = False---- | Check if module is in scope with qualifier-inScope :: Module -> Maybe String -> ModuleId -> Bool-inScope this q m = m `moduleImported` qualifier this q---- | Select symbols with last package version-newestPackage :: Symbol a => [a] -> [a]-newestPackage =-	uncurry (++) .-	((selectNewest . groupPackages) *** map snd) .-	partition (isJust . fst) .-	map ((mpackage . symbolModuleLocation) &&& id)-	where-		mpackage (InstalledModule _ (Just p) _) = Just p-		mpackage _ = Nothing-		pname = fmap (view packageName) . fst-		pver = fmap (view packageVersion) . fst-		groupPackages :: [(Maybe ModulePackage, a)] -> [(Maybe ModulePackage, [a])]-		groupPackages = map (first head . unzip) . groupBy ((==) `on` fst) . sortBy (comparing fst)-		selectNewest :: [(Maybe ModulePackage, [a])] -> [a]-		selectNewest =-			concatMap (snd . maximumBy (comparing pver)) .-			groupBy ((==) `on` pname) .-			sortBy (comparing pname)---- | Select module, defined by sources-sourceModule :: Maybe Project -> [Module] -> Maybe Module-sourceModule proj ms = listToMaybe $ maybe (const []) (filter . (. view moduleId) . inProject) proj ms ++ filter (byFile . view moduleId) ms---- | Select module, visible in project or cabal--- TODO: PackageDbStack?-visibleModule :: PackageDb -> Maybe Project -> [Module] -> Maybe Module-visibleModule d proj ms = listToMaybe $ maybe (const []) (filter . (. view moduleId) . inProject) proj ms ++ filter (inPackageDb d . view moduleId) ms---- | Select preferred visible module--- TODO: PackageDbStack?-preferredModule :: PackageDb -> Maybe Project -> [ModuleId] -> Maybe ModuleId-preferredModule d proj ms = listToMaybe $ concatMap (`filter` ms) order where-	order = [-		maybe (const False) inProject proj,-		byFile,-		inPackageDb d,-		const True]---- | Remove duplicate modules, leave only `preferredModule`--- TODO: PackageDbStack?-uniqueModules :: PackageDb -> Maybe Project -> [ModuleId] -> [ModuleId]-uniqueModules d proj =-	mapMaybe (preferredModule d proj) .-	groupBy ((==) `on` view moduleIdName) .-	sortBy (comparing (view moduleIdName))---- | Select value, satisfying to all predicates-allOf :: [a -> Bool] -> a -> Bool-allOf ps x = all ($ x) ps---- | Select value, satisfying one of predicates-anyOf :: [a -> Bool] -> a -> Bool-anyOf ps x = any ($ x) ps---- | Is file info actual?---isActual :: Symbol a -> IO Bool---isActual = maybe (return False) checkStamp . symbolLocation where---	checkStamp l = do---		actualStamp <- getModificationTime (locationFile l)---		return $ Just actualStamp == locationTimeStamp l
src/HsDev/Tools/AutoFix.hs view
@@ -1,92 +1,67 @@-{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Tools.AutoFix (-	Correction(..), correctionMessage, corrector,-	corrections,-	autoFix,-	CorrectorMatch,-	correctors,-	match,-	findCorrector,--	module Data.Text.Region,-	module HsDev.Tools.Types-	) where--import Control.Applicative-import Control.Lens hiding ((.=), at)-import Data.Aeson-import Data.Maybe (listToMaybe, mapMaybe)-import Data.Text.Region hiding (Region(..))-import qualified Data.Text.Region as R--import HsDev.Symbols.Location (Position(..), Region(..))-import HsDev.Tools.Base-import HsDev.Tools.Types-import HsDev.Util ((.::))--data Correction = Correction {-	_correctionMessage :: String,-	_corrector :: Replace String }-		deriving (Eq, Show)--instance ToJSON Correction where-	toJSON (Correction msg cor) = object [-		"message" .= msg,-		"corrector" .= cor]--instance FromJSON Correction where-	parseJSON = withObject "correction" $ \v -> Correction <$>-		v .:: "message" <*>-		v .:: "corrector"--makeLenses ''Correction--corrections :: [Note OutputMessage] -> [Note Correction]-corrections = mapMaybe toCorrection where-	toCorrection :: Note OutputMessage -> Maybe (Note Correction)-	toCorrection n = useSuggestion <|> findCorrector n where-		-- Use existing suggestion-		useSuggestion :: Maybe (Note Correction)-		useSuggestion = do-			sugg <- view (note . messageSuggestion) n-			return $ set-				note-				(Correction-					(view (note . message) n)-					(replace (fromRegion $ view noteRegion n) sugg))-				n---- | Apply corrections-autoFix :: [Note Correction] -> ([Note Correction], Maybe String) -> ([Note Correction], Maybe String)-autoFix ns (upd, mcts) = (over (each . note . corrector . replaceRegion) (update act) upd, over _Just (apply act) mcts) where-	act = Edit (ns ^.. each . note . corrector)--type CorrectorMatch = Note OutputMessage -> Maybe (Note Correction)--correctors :: [CorrectorMatch]-correctors = [-	match "^The (?:qualified )?import of .([\\w\\.]+). is redundant" $ \_ rgn -> Correction -- There are different quotes in Windows/Linux-		"Redundant import"-		(cut-			(expandLines rgn)),-	match "^(.*?)\nFound:\n  (.*?)\nWhy not:\n  (.*?)$" $ \g rgn -> Correction-		(g `at` 1)-		(replace-			((rgn ^. regionFrom) `regionSize` pt 0 (length $ g `at` 2))-			(g `at` 3))]--match :: String -> ((Int -> Maybe String) -> R.Region -> Correction) -> CorrectorMatch-match pat f n = do-	g <- matchRx pat (view (note . message) n)-	return $ set note (f g (fromRegion $ view noteRegion n)) n--findCorrector :: Note OutputMessage -> Maybe (Note Correction)-findCorrector n = listToMaybe $ mapMaybe ($ n) correctors--fromRegion :: Region -> R.Region-fromRegion (Region f t) = fromPosition f `till` fromPosition t--fromPosition :: Position -> Point-fromPosition (Position l c) = pt (pred l) (pred c)+{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Tools.AutoFix (
+	corrections,
+	CorrectorMatch,
+	correctors,
+	match,
+	findCorrector,
+
+	module Data.Text.Region,
+	module HsDev.Tools.Refact,
+	module HsDev.Tools.Types
+	) where
+
+import Control.Applicative
+import Control.Lens hiding ((.=), at)
+import Data.Maybe (listToMaybe, mapMaybe)
+import Data.String (fromString)
+import Data.Text (Text)
+import Data.Text.Lens (unpacked)
+import Data.Text.Region hiding (Region(..), update)
+import qualified Data.Text.Region as R
+
+import HsDev.Tools.Refact
+import HsDev.Tools.Base
+import HsDev.Tools.Types
+
+instance Regioned a => Regioned (Note a) where
+	regions = note . regions
+
+corrections :: [Note OutputMessage] -> [Note Refact]
+corrections = mapMaybe toRefact where
+	toRefact :: Note OutputMessage -> Maybe (Note Refact)
+	toRefact n = useSuggestion <|> findCorrector n where
+		-- Use existing suggestion
+		useSuggestion :: Maybe (Note Refact)
+		useSuggestion = do
+			sugg <- view (note . messageSuggestion) n
+			return $ set
+				note
+				(Refact
+					(view (note . message) n)
+					(replace (fromRegion $ view noteRegion n) sugg))
+				n
+
+type CorrectorMatch = Note OutputMessage -> Maybe (Note Refact)
+
+correctors :: [CorrectorMatch]
+correctors = [
+	match "^The (?:qualified )?import of .([\\w\\.]+). is redundant" $ \_ rgn -> Refact -- There are different quotes in Windows/Linux
+		"Redundant import"
+		(cut
+			(expandLines rgn)),
+	match "^(.*?)\nFound:\n  (.*?)\nWhy not:\n  (.*?)$" $ \g rgn -> Refact
+		(g `at` 1)
+		(replace
+			((rgn ^. regionFrom) `regionSize` pt 0 (contentsLength $ g `at` 2))
+			(g `at` 3))]
+
+match :: String -> ((Int -> Maybe Text) -> R.Region -> Refact) -> CorrectorMatch
+match pat f n = do
+	g <- matchRx pat (view (note . message . unpacked) n)
+	return $ set note (f (fmap fromString . g) (fromRegion $ view noteRegion n)) n
+
+findCorrector :: Note OutputMessage -> Maybe (Note Refact)
+findCorrector n = listToMaybe $ mapMaybe ($ n) correctors
src/HsDev/Tools/Base.hs view
@@ -1,117 +1,101 @@-module HsDev.Tools.Base (-	runTool, runTool_,-	Result, ToolM,-	runWait, runWait_,-	tool, tool_,-	matchRx, splitRx, replaceRx,-	at, at_,-	inspect,-	-- * Read parse utils-	ReadM,-	readParse, parseReads, parseRead,--	module HsDev.Tools.Types-	) where--import Control.Lens (set)-import Control.Monad.Catch (MonadCatch)-import Control.Monad.Except-import Control.Monad.State-import Data.Array (assocs)-import Data.List (unfoldr, intercalate)-import Data.Maybe (fromMaybe, listToMaybe)-import System.Exit-import System.Process-import Text.Regex.PCRE ((=~), MatchResult(..))--import HsDev.Error-import HsDev.Tools.Types-import HsDev.Symbols-import HsDev.Util (liftIOErrors)---- | Run tool, throwing HsDevError on fail-runTool :: FilePath -> [String] -> String -> IO String-runTool name args input = hsdevLiftIOWith onIOError $ do-	(code, out, err) <- readProcessWithExitCode name args input-	case code of-		ExitFailure ecode -> hsdevError $ ToolError name $-			"exited with code " ++ show ecode ++ ": " ++ err-		ExitSuccess -> return out-	where-		onIOError s = ToolError name $ unlines [-			"args: [" ++ intercalate ", " args ++ "]",-			"stdin: " ++ input,-			"error: " ++ s]---- | Run tool with not stdin-runTool_ :: FilePath -> [String] -> IO String-runTool_ name args = runTool name args ""--type Result = Either String String-type ToolM a = ExceptT String IO a---- | Run command and wait for result-runWait :: FilePath -> [String] -> String -> IO Result-runWait name args input = do-	(code, out, err) <- readProcessWithExitCode name args input-	return $ if code == ExitSuccess && not (null out) then Right out else Left err---- | Run command with no input-runWait_ :: FilePath -> [String] -> IO Result-runWait_ name args = runWait name args ""---- | Tool-tool :: FilePath -> [String] -> String -> ToolM String-tool name args input = liftIOErrors $ ExceptT $ runWait name args input---- | Tool with no input-tool_ :: FilePath -> [String] -> ToolM String-tool_ name args = tool name args ""--matchRx :: String -> String -> Maybe (Int -> Maybe String)-matchRx pat str = if matched then Just look else Nothing where-	m :: MatchResult String-	m = str =~ pat-	matched = not $ null $ mrMatch m-	groups = filter (not . null . snd) $ assocs $ mrSubs m-	look i = lookup i groups--splitRx :: String -> String -> [String]-splitRx pat = unfoldr split' . Just where-	split' :: Maybe String -> Maybe (String, Maybe String)-	split' Nothing = Nothing-	split' (Just str) = case str =~ pat of-		(pre, "", "") -> Just (pre, Nothing)-		(pre, _, post) -> Just (pre, Just post)--replaceRx :: String -> String -> String -> String-replaceRx pat w = intercalate w . splitRx pat--at :: (Int -> Maybe String) -> Int -> String-at g i = fromMaybe (error $ "Can't find group " ++ show i) $ g i--at_ :: (Int -> Maybe String) -> Int -> String-at_ g = fromMaybe "" . g--inspect :: MonadCatch m => ModuleLocation -> m Inspection -> m Module -> m InspectedModule-inspect mloc insp act = execStateT inspect' (notInspected mloc) where-	inspect' = do-		r <- hsdevCatch $ hsdevLiftIO $ do-			i <- lift insp-			modify (set inspection i)-			lift act-		modify (set inspectionResult r)--type ReadM a = StateT String [] a---- | Parse readable value-readParse :: Read a => ReadM a-readParse = StateT reads---- | Run parser-parseReads :: String -> ReadM a -> [a]-parseReads = flip evalStateT---- | Run parser and select first result-parseRead :: String -> ReadM a -> Maybe a-parseRead s = listToMaybe . parseReads s+module HsDev.Tools.Base (
+	runTool, runTool_,
+	Result, ToolM,
+	runWait, runWait_,
+	tool, tool_,
+	matchRx, splitRx, replaceRx,
+	at, at_,
+	inspect,
+
+	module HsDev.Tools.Types
+	) where
+
+import Control.Lens (set)
+import Control.Monad.Catch (MonadCatch)
+import Control.Monad.Except
+import Control.Monad.State
+import Data.Array (assocs)
+import Data.List (unfoldr, intercalate)
+import Data.Maybe (fromMaybe)
+import Data.String
+import System.Exit
+import System.Process
+import Text.Regex.PCRE ((=~), MatchResult(..))
+
+import HsDev.Error
+import HsDev.Tools.Types
+import HsDev.Symbols
+import HsDev.Util (liftIOErrors)
+
+-- | Run tool, throwing HsDevError on fail
+runTool :: FilePath -> [String] -> String -> IO String
+runTool name args input = hsdevLiftIOWith onIOError $ do
+	(code, out, err) <- readProcessWithExitCode name args input
+	case code of
+		ExitFailure ecode -> hsdevError $ ToolError name $
+			"exited with code " ++ show ecode ++ ": " ++ err
+		ExitSuccess -> return out
+	where
+		onIOError s = ToolError name $ unlines [
+			"args: [" ++ intercalate ", " args ++ "]",
+			"stdin: " ++ input,
+			"error: " ++ s]
+
+-- | Run tool with not stdin
+runTool_ :: FilePath -> [String] -> IO String
+runTool_ name args = runTool name args ""
+
+type Result = Either String String
+type ToolM a = ExceptT String IO a
+
+-- | Run command and wait for result
+runWait :: FilePath -> [String] -> String -> IO Result
+runWait name args input = do
+	(code, out, err) <- readProcessWithExitCode name args input
+	return $ if code == ExitSuccess && not (null out) then Right out else Left err
+
+-- | Run command with no input
+runWait_ :: FilePath -> [String] -> IO Result
+runWait_ name args = runWait name args ""
+
+-- | Tool
+tool :: FilePath -> [String] -> String -> ToolM String
+tool name args input = liftIOErrors $ ExceptT $ runWait name args input
+
+-- | Tool with no input
+tool_ :: FilePath -> [String] -> ToolM String
+tool_ name args = tool name args ""
+
+matchRx :: String -> String -> Maybe (Int -> Maybe String)
+matchRx pat str = if matched then Just look else Nothing where
+	m :: MatchResult String
+	m = str =~ pat
+	matched = not $ null $ mrMatch m
+	groups = filter (not . null . snd) $ assocs $ mrSubs m
+	look i = lookup i groups
+
+splitRx :: String -> String -> [String]
+splitRx pat = unfoldr split' . Just where
+	split' :: Maybe String -> Maybe (String, Maybe String)
+	split' Nothing = Nothing
+	split' (Just str) = case str =~ pat of
+		(pre, "", "") -> Just (pre, Nothing)
+		(pre, _, post) -> Just (pre, Just post)
+
+replaceRx :: String -> String -> String -> String
+replaceRx pat w = intercalate w . splitRx pat
+
+at :: (Int -> Maybe a) -> Int -> a
+at g i = fromMaybe (error $ "Can't find group " ++ show i) $ g i
+
+at_ :: IsString s => (Int -> Maybe s) -> Int -> s
+at_ g = fromMaybe (fromString "") . g
+
+inspect :: MonadCatch m => ModuleLocation -> m Inspection -> m Module -> m InspectedModule
+inspect mloc insp act = execStateT inspect' (notInspected mloc) where
+	inspect' = do
+		r <- hsdevCatch $ hsdevLiftIO $ do
+			i <- lift insp
+			modify (set inspection i)
+			lift act
+		modify (set inspectionResult r)
src/HsDev/Tools/Cabal.hs view
@@ -1,85 +1,85 @@-{-# LANGUAGE OverloadedStrings, CPP #-}--module HsDev.Tools.Cabal (-	CabalPackage(..),-	cabalList,--	-- * Reexports-	Version, License(..)-	) where--import Control.Arrow-import Control.Monad-import Data.Aeson-import Data.Char (isSpace)-import Data.Maybe-import Distribution.License-import Distribution.Text-import Distribution.Version--import HsDev.Tools.Base-import HsDev.Util--data CabalPackage = CabalPackage {-	cabalPackageName :: String,-	cabalPackageSynopsis :: Maybe String,-	cabalPackageDefaultVersion :: Maybe Version,-	cabalPackageInstalledVersions :: [Version],-	cabalPackageHomepage :: Maybe String,-	cabalPackageLicense :: Maybe License }-		deriving (Eq, Read, Show)--instance ToJSON CabalPackage where-	toJSON cp = object [-		"name" .= cabalPackageName cp,-		"synopsis" .= cabalPackageSynopsis cp,-		"default-version" .= fmap display (cabalPackageDefaultVersion cp),-		"installed-versions" .= map display (cabalPackageInstalledVersions cp),-		"homepage" .= cabalPackageHomepage cp,-		"license" .= fmap display (cabalPackageLicense cp)]--instance FromJSON CabalPackage where-	parseJSON = withObject "cabal-package" $ \v -> CabalPackage <$>-		(v .:: "name") <*>-		(v .:: "synopsis") <*>-		((join . fmap simpleParse) <$> (v .:: "default-version")) <*>-		(mapMaybe simpleParse <$> (v .:: "installed-versions")) <*>-		(v .:: "homepage") <*>-		((join . fmap simpleParse) <$> (v .:: "license"))--cabalList :: [String] -> ToolM [CabalPackage]-cabalList queries = do-#if mingw32_HOST_OS-	rs <- liftM (split (all isSpace) . lines) $ tool_ "powershell" [-		"-Command",-		unwords (["&", "{", "chcp 65001 | out-null;", "cabal list"] ++ queries ++ ["}"])]-#else-	rs <- liftM (split (all isSpace) . lines) $ tool_ "cabal" ("list" : queries)-#endif-	return $ map toPackage $ mapMaybe parseFields rs-	where-		toPackage :: (String, [(String, String)]) -> CabalPackage-		toPackage (name, fs) = CabalPackage {-			cabalPackageName = name,-			cabalPackageSynopsis = lookup "Synopsis" fs,-			cabalPackageDefaultVersion = (lookup "Default available version" fs >>= simpleParse),-			cabalPackageInstalledVersions = fromMaybe [] (lookup "Installed versions" fs >>= mapM (simpleParse . trim) . split (== ',')),-			cabalPackageHomepage = lookup "Homepage" fs,-			cabalPackageLicense = lookup "License" fs >>= simpleParse }--		parseFields :: [String] -> Maybe (String, [(String, String)])-		parseFields [] = Nothing-		parseFields (('*':name):fs) = Just (trim name, mapMaybe parseField' fs) where-			parseField' :: String -> Maybe (String, String)-			parseField' str = case parseField str of-				(fname, Just fval) -> Just (fname, fval)-				_ -> Nothing-		parseFields _ = Nothing--		-- foo: bar → (foo, bar)-		parseField :: String -> (String, Maybe String)-		parseField = (trim *** (parseValue . trim . drop 1)) . break (== ':')-		-- [ ... ] → Nothing, ... → Just ...-		parseValue :: String -> Maybe String-		parseValue ('[':_) = Nothing-		parseValue v = Just v+{-# LANGUAGE OverloadedStrings, CPP #-}
+
+module HsDev.Tools.Cabal (
+	CabalPackage(..),
+	cabalList,
+
+	-- * Reexports
+	Version, License(..)
+	) where
+
+import Control.Arrow
+import Control.Monad
+import Data.Aeson
+import Data.Char (isSpace)
+import Data.Maybe
+import Distribution.License
+import Distribution.Text
+import Distribution.Version
+
+import HsDev.Tools.Base
+import HsDev.Util
+
+data CabalPackage = CabalPackage {
+	cabalPackageName :: String,
+	cabalPackageSynopsis :: Maybe String,
+	cabalPackageDefaultVersion :: Maybe Version,
+	cabalPackageInstalledVersions :: [Version],
+	cabalPackageHomepage :: Maybe String,
+	cabalPackageLicense :: Maybe License }
+		deriving (Eq, Read, Show)
+
+instance ToJSON CabalPackage where
+	toJSON cp = object [
+		"name" .= cabalPackageName cp,
+		"synopsis" .= cabalPackageSynopsis cp,
+		"default-version" .= fmap display (cabalPackageDefaultVersion cp),
+		"installed-versions" .= map display (cabalPackageInstalledVersions cp),
+		"homepage" .= cabalPackageHomepage cp,
+		"license" .= fmap display (cabalPackageLicense cp)]
+
+instance FromJSON CabalPackage where
+	parseJSON = withObject "cabal-package" $ \v -> CabalPackage <$>
+		(v .:: "name") <*>
+		(v .:: "synopsis") <*>
+		((join . fmap simpleParse) <$> (v .:: "default-version")) <*>
+		(mapMaybe simpleParse <$> (v .:: "installed-versions")) <*>
+		(v .:: "homepage") <*>
+		((join . fmap simpleParse) <$> (v .:: "license"))
+
+cabalList :: [String] -> ToolM [CabalPackage]
+cabalList queries = do
+#if mingw32_HOST_OS
+	rs <- liftM (split (all isSpace) . lines) $ tool_ "powershell" [
+		"-Command",
+		unwords (["&", "{", "chcp 65001 | out-null;", "cabal list"] ++ queries ++ ["}"])]
+#else
+	rs <- liftM (split (all isSpace) . lines) $ tool_ "cabal" ("list" : queries)
+#endif
+	return $ map toPackage $ mapMaybe parseFields rs
+	where
+		toPackage :: (String, [(String, String)]) -> CabalPackage
+		toPackage (name, fs) = CabalPackage {
+			cabalPackageName = name,
+			cabalPackageSynopsis = lookup "Synopsis" fs,
+			cabalPackageDefaultVersion = (lookup "Default available version" fs >>= simpleParse),
+			cabalPackageInstalledVersions = fromMaybe [] (lookup "Installed versions" fs >>= mapM (simpleParse . trim) . split (== ',')),
+			cabalPackageHomepage = lookup "Homepage" fs,
+			cabalPackageLicense = lookup "License" fs >>= simpleParse }
+
+		parseFields :: [String] -> Maybe (String, [(String, String)])
+		parseFields [] = Nothing
+		parseFields (('*':name):fs) = Just (trim name, mapMaybe parseField' fs) where
+			parseField' :: String -> Maybe (String, String)
+			parseField' str = case parseField str of
+				(fname, Just fval) -> Just (fname, fval)
+				_ -> Nothing
+		parseFields _ = Nothing
+
+		-- foo: bar → (foo, bar)
+		parseField :: String -> (String, Maybe String)
+		parseField = (trim *** (parseValue . trim . drop 1)) . break (== ':')
+		-- [ ... ] → Nothing, ... → Just ...
+		parseValue :: String -> Maybe String
+		parseValue ('[':_) = Nothing
+		parseValue v = Just v
src/HsDev/Tools/ClearImports.hs view
@@ -1,112 +1,113 @@-module HsDev.Tools.ClearImports (-	dumpMinimalImports, waitImports, cleanTmpImports,-	findMinimalImports,-	groupImports, splitImport,-	clearImports,--	module Control.Monad.Except-	) where--import Control.Arrow-import Control.Concurrent (threadDelay)-import Control.Exception-import Control.Monad.Except-import Data.Char-import Data.List-import Data.Maybe (mapMaybe)-import System.Directory-import System.FilePath-import qualified Language.Haskell.Exts as Exts--import GHC-import GHC.Paths (libdir)--import HsDev.Util-import HsDev.Tools.Ghc.Compat---- | Dump minimal imports-dumpMinimalImports :: [String] -> FilePath -> ExceptT String IO String-dumpMinimalImports opts f = do-	cur <- liftE getCurrentDirectory-	file <- liftE $ canonicalizePath f-	cts <- liftE $ readFileUtf8 file--	mname <- case Exts.parseFileContentsWithMode (pmode file) cts of-		Exts.ParseFailed loc err -> throwError $-			"Failed to parse file at " ++-			Exts.prettyPrint loc ++ ":" ++ err-		Exts.ParseOk (Exts.Module _ (Just (Exts.ModuleHead _ (Exts.ModuleName _ mname) _ _)) _ _ _) -> return mname-		_ -> throwError "Error"--	void $ liftE $ runGhc (Just libdir) $ do-		df <- getSessionDynFlags-		let-			df' = df {-				ghcLink = NoLink,-				hscTarget = HscNothing,-				dumpDir = Just cur,-				stubDir = Just cur,-				objectDir = Just cur,-				hiDir = Just cur }-		(df'', _, _) <- parseDynamicFlags df' (map noLoc ("-ddump-minimal-imports" : opts))-		_ <- setSessionDynFlags df''-		cleanupHandler df'' $ do-			t <- guessTarget file Nothing-			setTargets [t]-			load LoadAllTargets--	length mname `seq` return mname-	where-		pmode :: FilePath -> Exts.ParseMode-		pmode f' = Exts.defaultParseMode {-			Exts.parseFilename = f',-			Exts.baseLanguage = Exts.Haskell2010,-			Exts.extensions = Exts.glasgowExts ++ map Exts.parseExtension exts,-			Exts.fixities = Just Exts.baseFixities }-		exts = mapMaybe (stripPrefix "-X") opts---- | Read imports from file-waitImports :: FilePath -> IO [String]-waitImports f = retry 1000 $ do-	is <- liftM lines $ readFile f-	length is `seq` return is---- | Clean temporary files-cleanTmpImports :: FilePath -> IO ()-cleanTmpImports dir = do-	dumps <- liftM (filter ((== ".imports") . takeExtension)) $ directoryContents dir-	forM_ dumps $ handle ignoreIO' . retry 1000 . removeFile-	where-		ignoreIO' :: IOException -> IO ()-		ignoreIO' _ = return ()---- | Dump and read imports-findMinimalImports :: [String] -> FilePath -> ExceptT String IO [String]-findMinimalImports opts f = do-	file <- liftE $ canonicalizePath f-	mname <- dumpMinimalImports opts file-	is <- liftE $ waitImports (mname <.> "imports")-	tmp <- liftE getCurrentDirectory-	liftE $ cleanTmpImports tmp-	return is---- | Groups several lines related to one import by indents-groupImports :: [String] -> [[String]]-groupImports = unfoldr getPack where-	getPack [] = Nothing-	getPack (s:ss) = Just $ first (s:) $ break (null . takeWhile isSpace) ss---- | Split import to import and import-list-splitImport :: [String] -> (String, String)-splitImport = splitBraces . unwords . map trim where-	cut = twice $ reverse . drop 1-	twice f = f . f-	splitBraces = (trim *** (trim . cut)) . break (== '(')---- | Returns minimal imports for file specified-clearImports :: [String] -> FilePath -> ExceptT String IO [(String, String)]-clearImports opts = liftM (map splitImport . groupImports) . findMinimalImports opts---- | Retry action on fail-retry :: (MonadPlus m, MonadIO m) => Int -> m a -> m a-retry dt act = msum $ act : repeat ((liftIO (threadDelay dt) >>) act)+module HsDev.Tools.ClearImports (
+	dumpMinimalImports, waitImports, cleanTmpImports,
+	findMinimalImports,
+	groupImports, splitImport,
+	clearImports,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Arrow
+import Control.Concurrent (threadDelay)
+import Control.Exception
+import Control.Monad.Except
+import Data.Char
+import Data.List
+import Data.Maybe (mapMaybe)
+import Data.Text (unpack)
+import System.Directory
+import System.FilePath
+import qualified Language.Haskell.Exts as Exts
+
+import GHC
+import GHC.Paths (libdir)
+
+import HsDev.Util
+import HsDev.Tools.Ghc.Compat
+
+-- | Dump minimal imports
+dumpMinimalImports :: [String] -> FilePath -> ExceptT String IO String
+dumpMinimalImports opts f = do
+	cur <- liftE getCurrentDirectory
+	file <- liftE $ canonicalizePath f
+	cts <- liftE $ fmap unpack $ readFileUtf8 file
+
+	mname <- case Exts.parseFileContentsWithMode (pmode file) cts of
+		Exts.ParseFailed loc err -> throwError $
+			"Failed to parse file at " ++
+			Exts.prettyPrint loc ++ ":" ++ err
+		Exts.ParseOk (Exts.Module _ (Just (Exts.ModuleHead _ (Exts.ModuleName _ mname) _ _)) _ _ _) -> return mname
+		_ -> throwError "Error"
+
+	void $ liftE $ runGhc (Just libdir) $ do
+		df <- getSessionDynFlags
+		let
+			df' = df {
+				ghcLink = NoLink,
+				hscTarget = HscNothing,
+				dumpDir = Just cur,
+				stubDir = Just cur,
+				objectDir = Just cur,
+				hiDir = Just cur }
+		(df'', _, _) <- parseDynamicFlags df' (map noLoc ("-ddump-minimal-imports" : opts))
+		_ <- setSessionDynFlags df''
+		cleanupHandler df'' $ do
+			t <- guessTarget file Nothing
+			setTargets [t]
+			load LoadAllTargets
+
+	length mname `seq` return mname
+	where
+		pmode :: FilePath -> Exts.ParseMode
+		pmode f' = Exts.defaultParseMode {
+			Exts.parseFilename = f',
+			Exts.baseLanguage = Exts.Haskell2010,
+			Exts.extensions = Exts.glasgowExts ++ map Exts.parseExtension exts,
+			Exts.fixities = Just Exts.baseFixities }
+		exts = mapMaybe (stripPrefix "-X") opts
+
+-- | Read imports from file
+waitImports :: FilePath -> IO [String]
+waitImports f = retry 1000 $ do
+	is <- liftM lines $ readFile f
+	length is `seq` return is
+
+-- | Clean temporary files
+cleanTmpImports :: FilePath -> IO ()
+cleanTmpImports dir = do
+	dumps <- liftM (filter ((== ".imports") . takeExtension)) $ directoryContents dir
+	forM_ dumps $ handle ignoreIO' . retry 1000 . removeFile
+	where
+		ignoreIO' :: IOException -> IO ()
+		ignoreIO' _ = return ()
+
+-- | Dump and read imports
+findMinimalImports :: [String] -> FilePath -> ExceptT String IO [String]
+findMinimalImports opts f = do
+	file <- liftE $ canonicalizePath f
+	mname <- dumpMinimalImports opts file
+	is <- liftE $ waitImports (mname <.> "imports")
+	tmp <- liftE getCurrentDirectory
+	liftE $ cleanTmpImports tmp
+	return is
+
+-- | Groups several lines related to one import by indents
+groupImports :: [String] -> [[String]]
+groupImports = unfoldr getPack where
+	getPack [] = Nothing
+	getPack (s:ss) = Just $ first (s:) $ break (null . takeWhile isSpace) ss
+
+-- | Split import to import and import-list
+splitImport :: [String] -> (String, String)
+splitImport = splitBraces . unwords . map trim where
+	cut = twice $ reverse . drop 1
+	twice f = f . f
+	splitBraces = (trim *** (trim . cut)) . break (== '(')
+
+-- | Returns minimal imports for file specified
+clearImports :: [String] -> FilePath -> ExceptT String IO [(String, String)]
+clearImports opts = liftM (map splitImport . groupImports) . findMinimalImports opts
+
+-- | Retry action on fail
+retry :: (MonadPlus m, MonadIO m) => Int -> m a -> m a
+retry dt act = msum $ act : repeat ((liftIO (threadDelay dt) >>) act)
src/HsDev/Tools/Ghc/Check.hs view
@@ -1,88 +1,65 @@-{-# LANGUAGE OverloadedStrings #-}--module HsDev.Tools.Ghc.Check (-	checkFiles, check, checkFile, checkSource,--	Ghc,-	module HsDev.Tools.Types,-	module HsDev.Symbols.Types,-	PackageDb(..), PackageDbStack(..), Project(..),--	recalcNotesTabs,--	module Control.Monad.Except-	) where--import Control.Lens (preview, view, each, _Just, (^..))-import Control.Monad.Except-import Data.Maybe (fromMaybe)-import System.FilePath (makeRelative)-import System.Directory (doesDirectoryExist)-import System.Log.Simple (MonadLog(..), scope)--import GHC hiding (Warning, Module, moduleName)--import Control.Concurrent.FiniteChan-import HsDev.Error-import HsDev.PackageDb-import HsDev.Symbols.Location-import HsDev.Symbols.Types-import HsDev.Project (fileTarget)-import HsDev.Tools.Base-import HsDev.Tools.Ghc.Worker-import HsDev.Tools.Ghc.Compat as C-import HsDev.Tools.Types-import HsDev.Util (readFileUtf8, ordNub)---- | Check files and collect warnings and errors-checkFiles :: (MonadLog m, GhcMonad m) => [String] -> [FilePath] -> Maybe Project -> m [Note OutputMessage]-checkFiles opts files _ = scope "check-files" $ do-	ch <- liftIO newChan-	withFlags $ do-		modifyFlags $ C.setLogAction $ logToChan ch-		addCmdOpts opts-		clearTargets-		mapM (`makeTarget` Nothing) files >>= loadTargets-	notes <- liftIO $ stopChan ch-	liftIO $ recalcNotesTabs notes---- | Check module source-check :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m [Note OutputMessage]-check opts m msrc = scope "check" $ case view moduleLocation m of-	FileModule file proj -> do-		ch <- liftIO newChan-		let-			dir = fromMaybe-				(sourceModuleRoot (view moduleName m) file) $-				preview (_Just . projectPath) proj-		dirExist <- liftIO $ doesDirectoryExist dir-		withFlags $ (if dirExist then withCurrentDirectory dir else id) $ do-			addCmdOpts opts-			modifyFlags $ C.setLogAction $ logToChan ch-			clearTargets-			target <- makeTarget (makeRelative dir file) msrc-			loadTargets [target]-		notes <- liftIO $ stopChan ch-		liftIO $ recalcNotesTabs notes-	_ -> scope "check" $ hsdevError $ ModuleNotSource (view moduleLocation m)---- | Check module and collect warnings and errors-checkFile :: (MonadLog m, GhcMonad m) => [String] -> Module -> m [Note OutputMessage]-checkFile opts m = check opts m Nothing---- | Check module and collect warnings and errors-checkSource :: (MonadLog m, GhcMonad m) => [String] -> Module -> String -> m [Note OutputMessage]-checkSource opts m src = check opts m (Just src)---- Recalc tabs for notes-recalcNotesTabs :: [Note OutputMessage] -> IO [Note OutputMessage]-recalcNotesTabs notes = do-	cts <- mapM readFileUtf8 files-	let-		recalc' n = fromMaybe n $ do-			fname <- preview (noteSource . moduleFile) n-			cts' <- lookup fname (zip files cts)-			return $ recalcTabs cts' 8 n-	return $ map recalc' notes-	where-		files = ordNub $ notes ^.. each . noteSource . moduleFile+{-# LANGUAGE OverloadedStrings #-}
+
+module HsDev.Tools.Ghc.Check (
+	check,
+
+	Ghc,
+	module HsDev.Tools.Types,
+	module HsDev.Symbols.Types,
+	PackageDb(..), PackageDbStack(..), Project(..),
+
+	recalcNotesTabs,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Lens (preview, view, each, (^..), (^.))
+import Control.Monad.Except
+import Data.Maybe (fromMaybe)
+import Data.Text (Text)
+import System.Log.Simple (MonadLog(..), scope, sendLog, Level(Trace))
+
+import GHC hiding (Warning, Module, moduleName)
+
+import Control.Concurrent.FiniteChan
+import HsDev.Error
+import HsDev.PackageDb
+import HsDev.Symbols.Location
+import HsDev.Symbols.Types
+import HsDev.Tools.Base
+import HsDev.Tools.Ghc.Worker
+import HsDev.Tools.Ghc.Compat as C
+import HsDev.Tools.Types
+import HsDev.Util (readFileUtf8, ordNub)
+import System.Directory.Paths
+
+-- | Check module source
+check :: (MonadLog m, GhcMonad m) => Module -> Maybe Text -> m [Note OutputMessage]
+check m msrc = scope "check" $ case view (moduleId . moduleLocation) m of
+	FileModule file _ -> do
+		ch <- liftIO newChan
+		let
+			dir = sourceRoot_ (m ^. moduleId)
+		ex <- liftIO $ dirExists dir
+		sendLog Trace "loading targets"
+		withFlags $ (if ex then withCurrentDirectory (dir ^. path) else id) $ do
+			modifyFlags $ C.setLogAction $ logToChan ch
+			target <- makeTarget (relPathTo dir file) msrc
+			loadTargets [target]
+		notes <- liftIO $ stopChan ch
+		sendLog Trace "targets checked"
+		liftIO $ recalcNotesTabs notes
+	_ -> scope "check" $ hsdevError $ ModuleNotSource (view (moduleId . moduleLocation) m)
+
+-- Recalc tabs for notes
+recalcNotesTabs :: [Note OutputMessage] -> IO [Note OutputMessage]
+recalcNotesTabs notes = do
+	cts <- mapM (readFileUtf8 . view path) files
+	let
+		recalc' n = fromMaybe n $ do
+			fname <- preview (noteSource . moduleFile) n
+			cts' <- lookup fname (zip files cts)
+			return $ recalcTabs cts' 8 n
+	return $ map recalc' notes
+	where
+		files = ordNub $ notes ^.. each . noteSource . moduleFile
src/HsDev/Tools/Ghc/Compat.hs view
@@ -1,154 +1,192 @@-{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Tools.Ghc.Compat (-	pkgDatabase,-	UnitId, InstalledUnitId, toInstalledUnitId,-	unitId, moduleUnitId, depends, getPackageDetails, patSynType, cleanupHandler, renderStyle,-	LogAction, setLogAction,-	languages, flags,-	unqualStyle,-	exposedModuleName-	) where--import qualified DynFlags as GHC-import qualified ErrUtils-import qualified GHC-import qualified Module-import qualified Packages as GHC-import qualified PatSyn as GHC-import qualified Pretty-import Outputable--#if __GLASGOW_HASKELL__ == 710-import Exception (ExceptionMonad)-import Control.Monad.Reader-#endif--#if __GLASGOW_HASKELL__ <= 800-import qualified GHC.PackageDb as GHC-#endif--pkgDatabase :: GHC.DynFlags -> Maybe [GHC.PackageConfig]-#if __GLASGOW_HASKELL__ >= 800-pkgDatabase = fmap (concatMap snd) . GHC.pkgDatabase-#elif __GLASGOW_HASKELL__ == 710-pkgDatabase = GHC.pkgDatabase-#endif--#if __GLASGOW_HASKELL__ >= 800-type UnitId = Module.UnitId-#elif __GLASGOW_HASKELL__ == 710-type UnitId = Module.PackageKey-#endif--#if __GLASGOW_HASKELL__ == 802-type InstalledUnitId = Module.InstalledUnitId-#else-type InstalledUnitId = UnitId-#endif--toInstalledUnitId :: UnitId -> InstalledUnitId-#if __GLASGOW_HASKELL__ == 802-toInstalledUnitId = Module.toInstalledUnitId-#else-toInstalledUnitId = id-#endif--unitId :: GHC.PackageConfig -> InstalledUnitId-#if __GLASGOW_HASKELL__ >= 800-unitId = GHC.unitId-#elif __GLASGOW_HASKELL__ == 710-unitId = GHC.packageKey-#endif--moduleUnitId :: GHC.Module -> UnitId-#if __GLASGOW_HASKELL__ >= 800-moduleUnitId = GHC.moduleUnitId-#elif __GLASGOW_HASKELL__ == 710-moduleUnitId = GHC.modulePackageKey-#endif--depends :: GHC.DynFlags -> GHC.PackageConfig -> [InstalledUnitId]-#if __GLASGOW_HASKELL__ >= 800-depends _ = GHC.depends-#elif __GLASGOW_HASKELL__ == 710-depends df = map (GHC.resolveInstalledPackageId df) . GHC.depends-#endif--getPackageDetails :: GHC.DynFlags -> InstalledUnitId -> GHC.PackageConfig-#if __GLASGOW_HASKELL__ == 802-getPackageDetails = GHC.getInstalledPackageDetails-#else-getPackageDetails = GHC.getPackageDetails-#endif--patSynType :: GHC.PatSyn -> GHC.Type-patSynType p = GHC.patSynInstResTy p (GHC.patSynArgs p)--#if __GLASGOW_HASKELL__ >= 800-cleanupHandler :: GHC.DynFlags -> m a -> m a-cleanupHandler _ = id-#elif __GLASGOW_HASKELL__ == 710-cleanupHandler :: (ExceptionMonad m) => GHC.DynFlags -> m a -> m a-cleanupHandler = GHC.defaultCleanupHandler-#endif--renderStyle :: Pretty.Mode -> Int -> Pretty.Doc -> String-#if __GLASGOW_HASKELL__ >= 800-renderStyle m cols = Pretty.renderStyle (Pretty.Style m cols 1.5)-#elif __GLASGOW_HASKELL__ == 710-renderStyle = Pretty.showDoc-#endif--type LogAction = GHC.DynFlags -> GHC.Severity -> GHC.SrcSpan -> ErrUtils.MsgDoc -> IO ()--setLogAction :: LogAction -> GHC.DynFlags -> GHC.DynFlags-setLogAction act fs = fs { GHC.log_action = act' } where-	act' :: GHC.LogAction-#if __GLASGOW_HASKELL__ >= 800-	act' df _ sev src _ msg = act df sev src msg-#elif __GLASGOW_HASKELL__ == 710-	act' df sev src _ msg = act df sev src msg-#endif--#if __GLASGOW_HASKELL__ == 710-instance (Monad m, GHC.HasDynFlags m) => GHC.HasDynFlags (ReaderT r m) where-	getDynFlags = lift GHC.getDynFlags-#endif--flags :: [String]-#if __GLASGOW_HASKELL__ >= 800-flags = concat [-	[option | (GHC.FlagSpec option _ _ _) <- GHC.fFlags],-	["warn-" ++ option | (GHC.FlagSpec option _ _ _) <- GHC.wWarningFlags],-	[option | (GHC.FlagSpec option _ _ _) <- GHC.fLangFlags]]-#elif __GLASGOW_HASKELL__ >= 710-flags = concat [-	[option | (GHC.FlagSpec option _ _ _) <- GHC.fFlags],-	[option | (GHC.FlagSpec option _ _ _) <- GHC.fWarningFlags],-	[option | (GHC.FlagSpec option _ _ _) <- GHC.fLangFlags]]-#elif __GLASGOW_HASKELL__ >= 704-flags = concat [-	[option | (option, _, _) <- GHC.fFlags],-	[option | (option, _, _) <- GHC.fWarningFlags],-	[option | (option, _, _) <- GHC.fLangFlags]]-#endif--languages :: [String]-languages = GHC.supportedLanguagesAndExtensions--unqualStyle :: GHC.DynFlags -> PprStyle-#if __GLASGOW_HASKELL__ == 802-unqualStyle df = mkUserStyle df neverQualify AllTheWay-#else-unqualStyle _ = mkUserStyle neverQualify AllTheWay-#endif--#if __GLASGOW_HASKELL__ == 802-exposedModuleName :: (a, Maybe b) -> a-exposedModuleName = fst-#else-exposedModuleName :: GHC.ExposedModule unit mname -> mname-exposedModuleName = GHC.exposedName-#endif+{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Tools.Ghc.Compat (
+	pkgDatabase,
+	UnitId, InstalledUnitId, toInstalledUnitId,
+	unitId, moduleUnitId, depends, getPackageDetails, patSynType, cleanupHandler, renderStyle,
+	LogAction, setLogAction,
+	languages, flags,
+	recSelParent, recSelCtors,
+	getFixity,
+	unqualStyle,
+	exposedModuleName
+	) where
+
+import qualified BasicTypes
+import qualified DynFlags as GHC
+import qualified ErrUtils
+import qualified IdInfo
+import qualified GHC
+import qualified Module
+import qualified Name
+import qualified Packages as GHC
+import qualified PatSyn as GHC
+import qualified Pretty
+import Outputable
+
+#if __GLASGOW_HASKELL__ >= 800
+import Data.List (nub)
+#endif
+
+#if __GLASGOW_HASKELL__ == 800
+import qualified IdInfo
+#endif
+
+#if __GLASGOW_HASKELL__ == 710
+import Exception (ExceptionMonad)
+import Control.Monad.Reader
+#endif
+
+#if __GLASGOW_HASKELL__ <= 800
+import qualified GHC.PackageDb as GHC
+#endif
+
+pkgDatabase :: GHC.DynFlags -> Maybe [GHC.PackageConfig]
+#if __GLASGOW_HASKELL__ >= 800
+pkgDatabase = fmap (nub . concatMap snd) . GHC.pkgDatabase
+#elif __GLASGOW_HASKELL__ == 710
+pkgDatabase = GHC.pkgDatabase
+#endif
+
+#if __GLASGOW_HASKELL__ >= 800
+type UnitId = Module.UnitId
+#elif __GLASGOW_HASKELL__ == 710
+type UnitId = Module.PackageKey
+#endif
+
+#if __GLASGOW_HASKELL__ == 802
+type InstalledUnitId = Module.InstalledUnitId
+#else
+type InstalledUnitId = UnitId
+#endif
+
+toInstalledUnitId :: UnitId -> InstalledUnitId
+#if __GLASGOW_HASKELL__ == 802
+toInstalledUnitId = Module.toInstalledUnitId
+#else
+toInstalledUnitId = id
+#endif
+
+unitId :: GHC.PackageConfig -> InstalledUnitId
+#if __GLASGOW_HASKELL__ >= 800
+unitId = GHC.unitId
+#elif __GLASGOW_HASKELL__ == 710
+unitId = GHC.packageKey
+#endif
+
+moduleUnitId :: GHC.Module -> UnitId
+#if __GLASGOW_HASKELL__ >= 800
+moduleUnitId = GHC.moduleUnitId
+#elif __GLASGOW_HASKELL__ == 710
+moduleUnitId = GHC.modulePackageKey
+#endif
+
+depends :: GHC.DynFlags -> GHC.PackageConfig -> [InstalledUnitId]
+#if __GLASGOW_HASKELL__ >= 800
+depends _ = GHC.depends
+#elif __GLASGOW_HASKELL__ == 710
+depends df = map (GHC.resolveInstalledPackageId df) . GHC.depends
+#endif
+
+getPackageDetails :: GHC.DynFlags -> InstalledUnitId -> GHC.PackageConfig
+#if __GLASGOW_HASKELL__ == 802
+getPackageDetails = GHC.getInstalledPackageDetails
+#else
+getPackageDetails = GHC.getPackageDetails
+#endif
+
+patSynType :: GHC.PatSyn -> GHC.Type
+patSynType p = GHC.patSynInstResTy p (GHC.patSynArgs p)
+
+#if __GLASGOW_HASKELL__ >= 800
+cleanupHandler :: GHC.DynFlags -> m a -> m a
+cleanupHandler _ = id
+#elif __GLASGOW_HASKELL__ == 710
+cleanupHandler :: (ExceptionMonad m) => GHC.DynFlags -> m a -> m a
+cleanupHandler = GHC.defaultCleanupHandler
+#endif
+
+renderStyle :: Pretty.Mode -> Int -> Pretty.Doc -> String
+#if __GLASGOW_HASKELL__ >= 800
+renderStyle m cols = Pretty.renderStyle (Pretty.Style m cols 1.5)
+#elif __GLASGOW_HASKELL__ == 710
+renderStyle = Pretty.showDoc
+#endif
+
+type LogAction = GHC.DynFlags -> GHC.Severity -> GHC.SrcSpan -> ErrUtils.MsgDoc -> IO ()
+
+setLogAction :: LogAction -> GHC.DynFlags -> GHC.DynFlags
+setLogAction act fs = fs { GHC.log_action = act' } where
+	act' :: GHC.LogAction
+#if __GLASGOW_HASKELL__ >= 800
+	act' df _ sev src _ msg = act df sev src msg
+#elif __GLASGOW_HASKELL__ == 710
+	act' df sev src _ msg = act df sev src msg
+#endif
+
+#if __GLASGOW_HASKELL__ == 710
+instance (Monad m, GHC.HasDynFlags m) => GHC.HasDynFlags (ReaderT r m) where
+	getDynFlags = lift GHC.getDynFlags
+#endif
+
+flags :: [String]
+#if __GLASGOW_HASKELL__ >= 800
+flags = concat [
+	[option | (GHC.FlagSpec option _ _ _) <- GHC.fFlags],
+	["warn-" ++ option | (GHC.FlagSpec option _ _ _) <- GHC.wWarningFlags],
+	[option | (GHC.FlagSpec option _ _ _) <- GHC.fLangFlags]]
+#elif __GLASGOW_HASKELL__ >= 710
+flags = concat [
+	[option | (GHC.FlagSpec option _ _ _) <- GHC.fFlags],
+	[option | (GHC.FlagSpec option _ _ _) <- GHC.fWarningFlags],
+	[option | (GHC.FlagSpec option _ _ _) <- GHC.fLangFlags]]
+#elif __GLASGOW_HASKELL__ >= 704
+flags = concat [
+	[option | (option, _, _) <- GHC.fFlags],
+	[option | (option, _, _) <- GHC.fWarningFlags],
+	[option | (option, _, _) <- GHC.fLangFlags]]
+#endif
+
+#if __GLASGOW_HASKELL__ >= 800
+recSelParent :: IdInfo.RecSelParent -> String
+recSelParent (IdInfo.RecSelData p) = Name.getOccString p
+recSelParent (IdInfo.RecSelPatSyn p) = Name.getOccString p
+#else
+recSelParent :: GHC.TyCon -> String
+recSelParent = Name.getOccString
+#endif
+
+#if __GLASGOW_HASKELL__ >= 800
+recSelCtors :: IdInfo.RecSelParent -> [String]
+recSelCtors (IdInfo.RecSelData p) = map Name.getOccString (GHC.tyConDataCons p)
+recSelCtors (IdInfo.RecSelPatSyn p) = [Name.getOccString p]
+#else
+recSelCtors :: GHC.TyCon -> [String]
+recSelCtors = return . Name.getOccString
+#endif
+
+getFixity :: BasicTypes.Fixity -> (Int, BasicTypes.FixityDirection)
+#if __GLASGOW_HASKELL__ >= 800
+getFixity (BasicTypes.Fixity _ i d) = (i, d)
+#else
+getFixity (BasicTypes.Fixity i d) = (i, d)
+#endif
+
+languages :: [String]
+languages = GHC.supportedLanguagesAndExtensions
+
+unqualStyle :: GHC.DynFlags -> PprStyle
+#if __GLASGOW_HASKELL__ == 802
+unqualStyle df = mkUserStyle df neverQualify AllTheWay
+#else
+unqualStyle _ = mkUserStyle neverQualify AllTheWay
+#endif
+
+#if __GLASGOW_HASKELL__ == 802
+exposedModuleName :: (a, Maybe b) -> a
+exposedModuleName = fst
+#else
+exposedModuleName :: GHC.ExposedModule unit mname -> mname
+exposedModuleName = GHC.exposedName
+#endif
src/HsDev/Tools/Ghc/MGhc.hs view
@@ -1,204 +1,215 @@-{-# LANGUAGE UnicodeSyntax, GeneralizedNewtypeDeriving, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Tools.Ghc.MGhc (-	SessionState(..), sessionActive, sessionMap,-	MGhcT(..), runMGhcT, liftGhc,-	hasSession, findSession, findSessionBy, saveSession,-	initSession, newSession,-	switchSession, switchSession_,-	deleteSession, restoreSession, usingSession-	) where--import Control.Lens-import Control.Monad.Morph-import Control.Monad.Catch-import Control.Monad.Reader-import Control.Monad.State-import Data.Default-import Data.IORef-import Data.List (find)-import Data.Map (Map)-import qualified Data.Map as M-import Data.Maybe (fromMaybe, isJust)-import System.Log.Simple.Monad (MonadLog(..))--import DynFlags-import Exception hiding (catch, mask, uninterruptibleMask, bracket)-import GHC-import GhcMonad-import HscTypes-import Outputable-import SysTools--data SessionState s = SessionState {-	_sessionActive :: Maybe s,-	_sessionMap :: Map s HscEnv }--instance Default (SessionState s) where-	def = SessionState Nothing M.empty--sessionActive :: Lens' (SessionState s) (Maybe s)-sessionActive = lens g s where-	g = _sessionActive-	s st nm = st { _sessionActive = nm }--sessionMap :: Lens' (SessionState s) (Map s HscEnv)-sessionMap = lens g s where-	g = _sessionMap-	s st m = st { _sessionMap = m }--instance ExceptionMonad m => ExceptionMonad (StateT s m) where-	gcatch act onError = StateT $ \st -> gcatch (runStateT act st) (\e -> runStateT (onError e) st)-	gmask f = StateT $ gmask . f' where-		f' st' act' = runStateT (f act) st' where-			act st = StateT $ act' . runStateT st--instance ExceptionMonad m => ExceptionMonad (ReaderT r m) where-	gcatch act onError = ReaderT $ \v -> gcatch (runReaderT act v) (\e -> runReaderT (onError e) v)-	gmask f = ReaderT $ gmask . f' where-		f' v' act' = runReaderT (f act) v' where-			act v = ReaderT $ act' . runReaderT v---- | Multi-session ghc monad-newtype MGhcT s m a = MGhcT { unMGhcT :: GhcT (ReaderT (Maybe FilePath) (StateT (SessionState s) m)) a }-	deriving (Functor, Applicative, Monad, MonadIO, ExceptionMonad, HasDynFlags, GhcMonad, MonadState (SessionState s), MonadReader (Maybe FilePath), MonadThrow, MonadCatch, MonadMask, MonadLog)--instance MonadTrans GhcT where-	lift = liftGhcT--instance MFunctor GhcT where-	hoist fn = GhcT . (fn .) . unGhcT--instance MonadState st m => MonadState st (GhcT m) where-	get = lift get-	put = lift . put-	state = lift . state--instance MonadReader r m => MonadReader r (GhcT m) where-	ask = lift ask-	local f act = GhcT $ local f . unGhcT act--instance MonadThrow m => MonadThrow (GhcT m) where-	throwM = lift . throwM--instance MonadCatch m => MonadCatch (GhcT m) where-	catch act onError = GhcT $ \sess -> catch (unGhcT act sess) (flip unGhcT sess . onError)--instance MonadMask m => MonadMask (GhcT m) where-	mask f = GhcT $ \s -> mask $ \g -> unGhcT (f $ q g) s where-		q g' act = GhcT $ g' . unGhcT act-	uninterruptibleMask f = GhcT $ \s -> uninterruptibleMask $ \g -> unGhcT (f $ q g) s where-		q g' act = GhcT $ g' . unGhcT act---- | Run multi-session ghc-runMGhcT :: (MonadIO m, ExceptionMonad m, Ord s) => Maybe FilePath -> MGhcT s m a -> m a-runMGhcT lib act = do-	ref <- liftIO $ newIORef (panic "empty session")-	let-		session = Session ref-	flip evalStateT def $ flip runReaderT lib $ flip unGhcT session $ unMGhcT $ act `gfinally` cleanup-	where-		cleanup :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m ()-		cleanup = do-			void saveSession-			sessions <- gets (M.elems . view sessionMap)-			liftIO $ mapM_ cleanupSession sessions-			modify (set sessionMap M.empty)---- | Lift `Ghc` monad onto `MGhc`-liftGhc :: MonadIO m => Ghc a -> MGhcT s m a-liftGhc (Ghc act) = MGhcT $ GhcT $ liftIO . act---- | Does session exist-hasSession :: (MonadIO m, Ord s) => s -> MGhcT s m Bool-hasSession key = do-	msess <- gets (preview (sessionMap . ix key))-	return $ isJust msess---- | Find session-findSession :: (MonadIO m, Ord s) => s -> MGhcT s m (Maybe s)-findSession key = do-	mkeys <- gets (M.keys . view sessionMap)-	return $ find (== key) mkeys---- | Find session by-findSessionBy :: MonadIO m => (s -> Bool) -> MGhcT s m [s]-findSessionBy p = do-	mkeys <- gets (M.keys . view sessionMap)-	return $ filter p mkeys---- | Save current session-saveSession :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m (Maybe s)-saveSession = do-	key <- gets (view sessionActive)-	case key of-		Just key' -> do-			sess <- getSession-			modify (set (sessionMap . at key') (Just sess))-		Nothing -> return ()-	return key---- | Initialize new session-initSession :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m ()-initSession = do-	lib <- ask-	initGhcMonad lib-	void saveSession--activateSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m (Maybe HscEnv)-activateSession key = do-	void saveSession-	modify (set sessionActive $ Just key)-	gets (view (sessionMap . at key))---- | Create new named session, deleting existing session-newSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m ()-newSession key = do-	msess <- activateSession key-	maybe (return ()) (liftIO . cleanupSession) msess-	initSession---- | Switch to session, creating if not exist, returns True if session was created-switchSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m Bool-switchSession key = do-	msess <- activateSession key-	case msess of-		Nothing -> initSession >> return True-		Just sess -> setSession sess >> return False---- | Switch to session, creating if not exist and initializing with passed function-switchSession_ :: (MonadIO m, ExceptionMonad m, Ord s) => s -> Maybe (MGhcT s m ()) -> MGhcT s m ()-switchSession_ key f = do-	new <- switchSession key-	when new $ fromMaybe (return ()) f---- | Delete existing session-deleteSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m ()-deleteSession key = do-	cur <- saveSession-	when (cur == Just key) $-		modify (set sessionActive Nothing)-	msess <- gets (view (sessionMap . at key))-	modify (set (sessionMap . at key) Nothing)-	case msess of-		Nothing -> return ()-		Just sess -> liftIO $ cleanupSession sess---- | Save and restore session-restoreSession :: (MonadIO m, MonadMask m, ExceptionMonad m, Ord s) => MGhcT s m a -> MGhcT s m a-restoreSession act = bracket saveSession (maybe (return ()) (void . switchSession)) $ const act---- | Run action using session, restoring session back-usingSession :: (MonadIO m, MonadMask m, ExceptionMonad m, Ord s) => s -> MGhcT s m a -> MGhcT s m a-usingSession key act = restoreSession $ do-	void $ switchSession key-	act---- | Cleanup session-cleanupSession :: HscEnv -> IO ()-cleanupSession env = do-	cleanTempFiles df-	cleanTempDirs df-	where-		df = hsc_dflags env+{-# LANGUAGE UnicodeSyntax, GeneralizedNewtypeDeriving, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Tools.Ghc.MGhc (
+	SessionState(..), sessionActive, sessionMap,
+	MGhcT(..), runMGhcT, liftGhc,
+	currentSession, hasSession, findSession, findSessionBy, saveSession,
+	initSession, newSession,
+	switchSession, switchSession_,
+	deleteSession, restoreSession, usingSession, tempSession
+	) where
+
+import Control.Lens
+import Control.Monad.Morph
+import Control.Monad.Catch
+import Control.Monad.Reader
+import Control.Monad.State
+import Data.Default
+import Data.IORef
+import Data.List (find)
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import Data.Maybe (fromMaybe, isJust)
+import System.Log.Simple.Monad (MonadLog(..))
+
+import DynFlags
+import Exception hiding (catch, mask, uninterruptibleMask, bracket, finally)
+import GHC
+import GHCi
+import GhcMonad
+import HscTypes
+import Outputable
+import SysTools
+
+data SessionState s = SessionState {
+	_sessionActive :: Maybe s,
+	_sessionMap :: Map s HscEnv }
+
+instance Default (SessionState s) where
+	def = SessionState Nothing M.empty
+
+sessionActive :: Lens' (SessionState s) (Maybe s)
+sessionActive = lens g s where
+	g = _sessionActive
+	s st nm = st { _sessionActive = nm }
+
+sessionMap :: Lens' (SessionState s) (Map s HscEnv)
+sessionMap = lens g s where
+	g = _sessionMap
+	s st m = st { _sessionMap = m }
+
+instance ExceptionMonad m => ExceptionMonad (StateT s m) where
+	gcatch act onError = StateT $ \st -> gcatch (runStateT act st) (\e -> runStateT (onError e) st)
+	gmask f = StateT $ gmask . f' where
+		f' st' act' = runStateT (f act) st' where
+			act st = StateT $ act' . runStateT st
+
+instance ExceptionMonad m => ExceptionMonad (ReaderT r m) where
+	gcatch act onError = ReaderT $ \v -> gcatch (runReaderT act v) (\e -> runReaderT (onError e) v)
+	gmask f = ReaderT $ gmask . f' where
+		f' v' act' = runReaderT (f act) v' where
+			act v = ReaderT $ act' . runReaderT v
+
+-- | Multi-session ghc monad
+newtype MGhcT s m a = MGhcT { unMGhcT :: GhcT (ReaderT (Maybe FilePath) (StateT (SessionState s) m)) a }
+	deriving (Functor, Applicative, Monad, MonadIO, ExceptionMonad, HasDynFlags, GhcMonad, MonadState (SessionState s), MonadReader (Maybe FilePath), MonadThrow, MonadCatch, MonadMask, MonadLog)
+
+instance MonadTrans GhcT where
+	lift = liftGhcT
+
+instance MFunctor GhcT where
+	hoist fn = GhcT . (fn .) . unGhcT
+
+instance MonadState st m => MonadState st (GhcT m) where
+	get = lift get
+	put = lift . put
+	state = lift . state
+
+instance MonadReader r m => MonadReader r (GhcT m) where
+	ask = lift ask
+	local f act = GhcT $ local f . unGhcT act
+
+instance MonadThrow m => MonadThrow (GhcT m) where
+	throwM = lift . throwM
+
+instance MonadCatch m => MonadCatch (GhcT m) where
+	catch act onError = GhcT $ \sess -> catch (unGhcT act sess) (flip unGhcT sess . onError)
+
+instance MonadMask m => MonadMask (GhcT m) where
+	mask f = GhcT $ \s -> mask $ \g -> unGhcT (f $ q g) s where
+		q g' act = GhcT $ g' . unGhcT act
+	uninterruptibleMask f = GhcT $ \s -> uninterruptibleMask $ \g -> unGhcT (f $ q g) s where
+		q g' act = GhcT $ g' . unGhcT act
+
+-- | Run multi-session ghc
+runMGhcT :: (MonadIO m, ExceptionMonad m, Ord s) => Maybe FilePath -> MGhcT s m a -> m a
+runMGhcT lib act = do
+	ref <- liftIO $ newIORef (panic "empty session")
+	let
+		session = Session ref
+	flip evalStateT def $ flip runReaderT lib $ flip unGhcT session $ unMGhcT $ act `gfinally` cleanup
+	where
+		cleanup :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m ()
+		cleanup = do
+			void saveSession
+			sessions <- gets (M.elems . view sessionMap)
+			liftIO $ mapM_ cleanupSession sessions
+			modify (set sessionMap M.empty)
+
+-- | Lift `Ghc` monad onto `MGhc`
+liftGhc :: MonadIO m => Ghc a -> MGhcT s m a
+liftGhc (Ghc act) = MGhcT $ GhcT $ liftIO . act
+
+currentSession :: MonadIO m => MGhcT s m (Maybe s)
+currentSession = gets (view sessionActive)
+
+-- | Does session exist
+hasSession :: (MonadIO m, Ord s) => s -> MGhcT s m Bool
+hasSession key = do
+	msess <- gets (preview (sessionMap . ix key))
+	return $ isJust msess
+
+-- | Find session
+findSession :: (MonadIO m, Ord s) => s -> MGhcT s m (Maybe s)
+findSession key = do
+	mkeys <- gets (M.keys . view sessionMap)
+	return $ find (== key) mkeys
+
+-- | Find session by
+findSessionBy :: MonadIO m => (s -> Bool) -> MGhcT s m [s]
+findSessionBy p = do
+	mkeys <- gets (M.keys . view sessionMap)
+	return $ filter p mkeys
+
+-- | Save current session
+saveSession :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m (Maybe s)
+saveSession = do
+	key <- currentSession
+	case key of
+		Just key' -> do
+			sess <- getSession
+			modify (set (sessionMap . at key') (Just sess))
+		Nothing -> return ()
+	return key
+
+-- | Initialize new session
+initSession :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m ()
+initSession = do
+	lib <- ask
+	initGhcMonad lib
+	void saveSession
+
+activateSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m (Maybe HscEnv)
+activateSession key = do
+	void saveSession
+	modify (set sessionActive $ Just key)
+	gets (view (sessionMap . at key))
+
+-- | Create new named session, deleting existing session
+newSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m ()
+newSession key = do
+	msess <- activateSession key
+	maybe (return ()) (liftIO . cleanupSession) msess
+	initSession
+
+-- | Switch to session, creating if not exist, returns True if session was created
+switchSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m Bool
+switchSession key = do
+	msess <- activateSession key
+	case msess of
+		Nothing -> initSession >> return True
+		Just sess -> setSession sess >> return False
+
+-- | Switch to session, creating if not exist and initializing with passed function
+switchSession_ :: (MonadIO m, ExceptionMonad m, Ord s) => s -> Maybe (MGhcT s m ()) -> MGhcT s m ()
+switchSession_ key f = do
+	new <- switchSession key
+	when new $ fromMaybe (return ()) f
+
+-- | Delete existing session
+deleteSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m ()
+deleteSession key = do
+	cur <- saveSession
+	when (cur == Just key) $
+		modify (set sessionActive Nothing)
+	msess <- gets (view (sessionMap . at key))
+	modify (set (sessionMap . at key) Nothing)
+	case msess of
+		Nothing -> return ()
+		Just sess -> liftIO $ cleanupSession sess
+
+-- | Save and restore session
+restoreSession :: (MonadIO m, MonadMask m, ExceptionMonad m, Ord s) => MGhcT s m a -> MGhcT s m a
+restoreSession act = bracket saveSession (maybe (return ()) (void . switchSession)) $ const act
+
+-- | Run action using session, restoring session back
+usingSession :: (MonadIO m, MonadMask m, ExceptionMonad m, Ord s) => s -> MGhcT s m a -> MGhcT s m a
+usingSession key act = restoreSession $ do
+	void $ switchSession key
+	act
+
+-- | Run with temporary session, like @usingSession@, but deletes self session
+tempSession :: (MonadIO m, MonadMask m, ExceptionMonad m, Ord s) => s -> MGhcT s m a -> MGhcT s m a
+tempSession key act = do
+	exist' <- hasSession key
+	usingSession key act `finally` when (not exist') (deleteSession key)
+
+-- | Cleanup session
+cleanupSession :: HscEnv -> IO ()
+cleanupSession env = do
+	cleanTempFiles df
+	cleanTempDirs df
+	stopIServ env
+	where
+		df = hsc_dflags env
src/HsDev/Tools/Ghc/Prelude.hs view
@@ -1,5 +1,5 @@ module HsDev.Tools.Ghc.Prelude (
-	reduce, one, trim,
+	reduce, trim,
 	-- * Regexes
 	rx, srx, splitRx,
 	-- * Case
@@ -21,10 +21,6 @@ -- | Reduce list to one element
 reduce :: ([a] -> a) -> [a] -> [a]
 reduce = (return .)
-
--- | Make list from single element
-one :: a -> [a]
-one = return
 
 -- | Trim string
 trim :: String -> String
src/HsDev/Tools/Ghc/Session.hs view
@@ -1,49 +1,42 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Tools.Ghc.Session (-	ghcSession, ghciSession, haddockSession, targetSession, interpretModule,--	module HsDev.Tools.Ghc.Worker-	) where--import Control.Lens-import Data.Text (unpack)-import System.FilePath--import Control.Concurrent.Worker-import HsDev.Symbols.Types-import HsDev.Sandbox (getModuleOpts)-import HsDev.Tools.Ghc.Worker--import qualified GHC---- | Get ghc session-ghcSession :: [String] -> GhcM ()-ghcSession = workerSession . SessionGhc---- | Get ghci session-ghciSession :: GhcM ()-ghciSession = workerSession SessionGhci---- | Get haddock session with flags-haddockSession :: [String] -> GhcM ()-haddockSession opts = ghcSession ("-haddock" : opts)---- | Session for module-targetSession :: [String] -> Module -> GhcM ()-targetSession opts m = do-	opts' <- getModuleOpts opts m-	ghcSession ("-Wall" : opts')---- | Interpret file-interpretModule :: Module -> Maybe String -> GhcM ()-interpretModule m mcts = do-	targetSession [] m-	let-		f = preview (moduleLocation . moduleFile) m-	case f of-		Nothing -> return ()-		Just f' -> withCurrentDirectory (takeDirectory f') $ do-			t <- makeTarget (takeFileName f') mcts-			loadTargets [t]-			GHC.setContext [GHC.IIModule $ GHC.mkModuleName $ unpack $ view moduleName m]+{-# LANGUAGE OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Tools.Ghc.Session (
+	targetSession, interpretModule,
+
+	module HsDev.Tools.Ghc.Worker
+	) where
+
+import Control.Lens
+import Data.Maybe (isJust)
+import Data.Text (Text, unpack)
+import System.Log.Simple
+
+import Control.Concurrent.Worker
+import System.Directory.Paths
+import HsDev.Symbols.Types
+import HsDev.Sandbox (getModuleOpts)
+import HsDev.Tools.Ghc.Worker
+
+import qualified GHC
+
+-- | Session for module
+targetSession :: [String] -> Module -> GhcM ()
+targetSession opts m = do
+	opts' <- getModuleOpts opts m
+	ghcSession ("-Wall" : opts')
+
+-- | Interpret file
+interpretModule :: Module -> Maybe Text -> GhcM ()
+interpretModule m mcts
+	| isJust mpath = do
+		let
+			rootDir = maybe (takeDir fpath) (view projectPath) (m ^? moduleId . moduleLocation . moduleProject . _Just)
+		withCurrentDirectory (view path rootDir) $ do
+			t <- makeTarget (relPathTo rootDir fpath) mcts
+			loadTargets [t]
+			GHC.setContext [GHC.IIModule . GHC.mkModuleName . unpack . view (moduleId . moduleName) $ m]
+	| otherwise = return ()
+	where
+		mpath = m ^? moduleId . moduleLocation . moduleFile
+		Just fpath = mpath
src/HsDev/Tools/Ghc/Types.hs view
@@ -1,143 +1,141 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, RankNTypes, TemplateHaskell, OverloadedStrings #-}--module HsDev.Tools.Ghc.Types (-	TypedExpr(..), typedExpr, typedType,-	moduleTypes, fileTypes,-	setModuleTypes, inferTypes-	) where--import Control.DeepSeq-import Control.Lens (over, view, set, each, preview, makeLenses, _Just)-import Control.Monad-import Control.Monad.IO.Class-import Data.Aeson-import Data.Generics-import Data.List (find)-import Data.Maybe-import Data.String (fromString)-import System.Directory-import System.FilePath-import System.Log.Simple (MonadLog(..), scope)--import GHC hiding (exprType, Module, moduleName)-import GHC.SYB.Utils (everythingStaged, Stage(TypeChecker))-import GhcPlugins (mkFunTys)-import CoreUtils-import Desugar (deSugarExpr)-import TcHsSyn (hsPatType)-import Outputable-import PprTyThing-import qualified Pretty--import System.Directory.Paths (canonicalize)-import HsDev.Error-import HsDev.Symbols-import HsDev.Tools.Ghc.Worker as Ghc-import HsDev.Tools.Ghc.Compat-import HsDev.Tools.Types-import HsDev.Util--class HasType a where-	getType :: GhcMonad m => TypecheckedModule -> a -> m (Maybe (SrcSpan, Type))--instance HasType (LHsExpr Id) where-	getType _ e = do-		env <- getSession-		mbe <- liftIO $ liftM snd $ deSugarExpr env e-		return $ do-			ex <- mbe-			return (getLoc e, exprType ex)--instance HasType (LHsBind Id) where-	getType _ (L spn FunBind { fun_matches = m}) = return $ Just (spn, typ) where-		typ = mkFunTys (mg_arg_tys m) (mg_res_ty m)-	getType _ _ = return Nothing--instance HasType (LPat Id) where-	getType _ (L spn pat) = return $ Just (spn, hsPatType pat)--locatedTypes :: Typeable a => TypecheckedSource -> [Located a]-locatedTypes = types' p where-	types' :: Typeable r => (r -> Bool) -> GenericQ [r]-	types' p' = everythingStaged TypeChecker (++) [] ([] `mkQ` (\x -> [x | p' x]))-	p (L spn _) = isGoodSrcSpan spn--moduleTypes :: GhcMonad m => FilePath -> m [(SrcSpan, Type)]-moduleTypes fpath = do-	fpath' <- liftIO $ canonicalize fpath-	mg <- getModuleGraph-	[m] <- liftIO $ flip filterM mg $ \m -> do-		mfile <- traverse (liftIO . canonicalize) $ ml_hs_file (ms_location m)-		return (Just fpath' == mfile)-	p <- parseModule m-	tm <- typecheckModule p-	let-		ts = tm_typechecked_source tm-	liftM (catMaybes . concat) $ sequence [-		mapM (getType tm) (locatedTypes ts :: [LHsBind Id]),-		mapM (getType tm) (locatedTypes ts :: [LHsExpr Id]),-		mapM (getType tm) (locatedTypes ts :: [LPat Id])]--data TypedExpr = TypedExpr {-	_typedExpr :: String,-	_typedType :: String }-		deriving (Eq, Ord, Read, Show)--makeLenses ''TypedExpr--instance NFData TypedExpr where-	rnf (TypedExpr e t) = rnf e `seq` rnf t--instance ToJSON TypedExpr where-	toJSON (TypedExpr e t) = object [-		"expr" .= e,-		"type" .= t]--instance FromJSON TypedExpr where-	parseJSON = withObject "typed-expr" $ \v -> TypedExpr <$>-		v .:: "expr" <*>-		v .:: "type"---- | Get all types in module-fileTypes :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m [Note TypedExpr]-fileTypes opts m msrc = scope "types" $ case view moduleLocation m of-	FileModule file proj -> do-		file' <- liftIO $ canonicalize file-		cts <- maybe (liftIO $ readFileUtf8 file') return msrc-		let-			dir = fromMaybe-				(sourceModuleRoot (view moduleName m) file') $-				preview (_Just . projectPath) proj-		dirExist <- liftIO $ doesDirectoryExist dir-		withFlags $ (if dirExist then Ghc.withCurrentDirectory dir else id) $ do-			addCmdOpts opts-			target <- makeTarget (makeRelative dir file') msrc-			loadTargets [target]-			ts <- moduleTypes file'-			df <- getSessionDynFlags-			return $ map (setExpr cts . recalcTabs cts 8 . uncurry (toNote df)) ts-	_ -> hsdevError $ ModuleNotSource (view moduleLocation m)-	where-		toNote :: DynFlags -> SrcSpan -> Type -> Note String-		toNote df spn tp = Note {-			_noteSource = noLocation,-			_noteRegion = spanRegion spn,-			_noteLevel = Nothing,-			_note = showType df tp }-		setExpr :: String -> Note String -> Note TypedExpr-		setExpr cts n = over note (TypedExpr (regionStr (view noteRegion n) cts)) n-		showType :: DynFlags -> Type -> String-		showType df = renderStyle Pretty.OneLineMode 80 . withPprStyleDoc df (unqualStyle df) . pprTypeForUser---- | Set types to module-setModuleTypes :: [Note TypedExpr] -> Module -> Module-setModuleTypes ts = over (moduleDeclarations . each) setType where-	setType :: Declaration -> Declaration-	setType d = fromMaybe d $ do-		pos <- view declarationPosition d-		tnote <- find ((== pos) . view (noteRegion . regionFrom)) ts-		return $ set (declaration . functionType) (Just $ fromString $ view (note . typedType) tnote) d---- | Infer types in module-inferTypes :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m Module-inferTypes opts m msrc = scope "infer" $ liftM (`setModuleTypes` m) $ fileTypes opts m msrc+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, RankNTypes, TemplateHaskell, OverloadedStrings #-}
+
+module HsDev.Tools.Ghc.Types (
+	TypedExpr(..), typedExpr, typedType,
+	moduleTypes, fileTypes,
+	setModuleTypes, inferTypes
+	) where
+
+import Control.DeepSeq
+import Control.Lens (over, view, set, each, preview, makeLenses, _Just)
+import Control.Monad
+import Control.Monad.IO.Class
+import Data.Aeson
+import Data.Generics
+import Data.List (find)
+import Data.Maybe
+import Data.String (fromString)
+import Data.Text (Text)
+import System.Log.Simple (MonadLog(..), scope)
+
+import GHC hiding (exprType, Module, moduleName)
+import GHC.SYB.Utils (everythingStaged, Stage(TypeChecker))
+import GhcPlugins (mkFunTys)
+import CoreUtils
+import Desugar (deSugarExpr)
+import TcHsSyn (hsPatType)
+import Outputable
+import PprTyThing
+import qualified Pretty
+
+import System.Directory.Paths
+import HsDev.Error
+import HsDev.Symbols
+import HsDev.Tools.Ghc.Worker as Ghc
+import HsDev.Tools.Ghc.Compat
+import HsDev.Tools.Types
+import HsDev.Util
+
+class HasType a where
+	getType :: GhcMonad m => TypecheckedModule -> a -> m (Maybe (SrcSpan, Type))
+
+instance HasType (LHsExpr Id) where
+	getType _ e = do
+		env <- getSession
+		mbe <- liftIO $ liftM snd $ deSugarExpr env e
+		return $ do
+			ex <- mbe
+			return (getLoc e, exprType ex)
+
+instance HasType (LHsBind Id) where
+	getType _ (L _ FunBind { fun_id = fid, fun_matches = m}) = return $ Just (getLoc fid, typ) where
+		typ = mkFunTys (mg_arg_tys m) (mg_res_ty m)
+	getType _ _ = return Nothing
+
+instance HasType (LPat Id) where
+	getType _ (L spn pat) = return $ Just (spn, hsPatType pat)
+
+locatedTypes :: Typeable a => TypecheckedSource -> [Located a]
+locatedTypes = types' p where
+	types' :: Typeable r => (r -> Bool) -> GenericQ [r]
+	types' p' = everythingStaged TypeChecker (++) [] ([] `mkQ` (\x -> [x | p' x]))
+	p (L spn _) = isGoodSrcSpan spn
+
+moduleTypes :: GhcMonad m => Path -> m [(SrcSpan, Type)]
+moduleTypes fpath = do
+	fpath' <- liftIO $ canonicalize fpath
+	mg <- getModuleGraph
+	[m] <- liftIO $ flip filterM mg $ \m -> do
+		mfile <- traverse (liftIO . canonicalize) $ ml_hs_file (ms_location m)
+		return (Just (view path fpath') == mfile)
+	p <- parseModule m
+	tm <- typecheckModule p
+	let
+		ts = tm_typechecked_source tm
+	liftM (catMaybes . concat) $ sequence [
+		mapM (getType tm) (locatedTypes ts :: [LHsBind Id]),
+		mapM (getType tm) (locatedTypes ts :: [LHsExpr Id]),
+		mapM (getType tm) (locatedTypes ts :: [LPat Id])]
+
+data TypedExpr = TypedExpr {
+	_typedExpr :: Maybe Text,
+	_typedType :: Text }
+		deriving (Eq, Ord, Read, Show)
+
+makeLenses ''TypedExpr
+
+instance NFData TypedExpr where
+	rnf (TypedExpr e t) = rnf e `seq` rnf t
+
+instance ToJSON TypedExpr where
+	toJSON (TypedExpr e t) = object $ noNulls [
+		"expr" .= e,
+		"type" .= t]
+
+instance FromJSON TypedExpr where
+	parseJSON = withObject "typed-expr" $ \v -> TypedExpr <$>
+		v .::? "expr" <*>
+		v .:: "type"
+
+-- | Get all types in module
+fileTypes :: (MonadLog m, GhcMonad m) => Module -> Maybe Text -> m [Note TypedExpr]
+fileTypes m msrc = scope "types" $ case view (moduleId . moduleLocation) m of
+	FileModule file proj -> do
+		file' <- liftIO $ canonicalize file
+		cts <- maybe (liftIO $ readFileUtf8 (view path file')) return msrc
+		let
+			dir = fromMaybe
+				(sourceModuleRoot (view (moduleId . moduleName) m) file') $
+				preview (_Just . projectPath) proj
+		ex <- liftIO $ dirExists dir
+		(if ex then Ghc.withCurrentDirectory (view path dir) else id) $ do
+			target <- makeTarget (relPathTo dir file') msrc
+			loadTargets [target]
+			ts <- moduleTypes file'
+			df <- getSessionDynFlags
+			return $ map (setExpr cts . recalcTabs cts 8 . uncurry (toNote df)) ts
+	_ -> hsdevError $ ModuleNotSource (view (moduleId . moduleLocation) m)
+	where
+		toNote :: DynFlags -> SrcSpan -> Type -> Note Text
+		toNote df spn tp = Note {
+			_noteSource = noLocation,
+			_noteRegion = spanRegion spn,
+			_noteLevel = Nothing,
+			_note = fromString $ showType df tp }
+		setExpr :: Text -> Note Text -> Note TypedExpr
+		setExpr cts n = over note (TypedExpr (Just (regionStr (view noteRegion n) cts))) n
+		showType :: DynFlags -> Type -> String
+		showType df = renderStyle Pretty.OneLineMode 80 . withPprStyleDoc df (unqualStyle df) . pprTypeForUser
+
+-- | Set types to module
+setModuleTypes :: [Note TypedExpr] -> Module -> Module
+setModuleTypes ts = over (moduleScope . each . each) setType . over (moduleExports . each) setType where
+	setType :: Symbol -> Symbol
+	setType d = fromMaybe d $ do
+		pos <- view symbolPosition d
+		tnote <- find ((== pos) . view (noteRegion . regionFrom)) ts
+		return $ set (symbolInfo . functionType) (Just $ view (note . typedType) tnote) d
+
+-- | Infer types in module
+inferTypes :: (MonadLog m, GhcMonad m) => Module -> Maybe Text -> m Module
+inferTypes m msrc = scope "infer" $ liftM (`setModuleTypes` m) $ fileTypes m msrc
src/HsDev/Tools/Ghc/Worker.hs view
@@ -1,249 +1,267 @@-{-# LANGUAGE PatternGuards, OverloadedStrings, FlexibleContexts #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Tools.Ghc.Worker (-	-- * Workers-	SessionTarget(..),-	GhcM, GhcWorker, MGhcT(..), runGhcM,-	ghcWorker,-	workerSession,--	-- * Initializers and actions-	ghcRun,-	withFlags, modifyFlags, addCmdOpts, setCmdOpts,-	importModules, preludeModules,-	evaluate,-	clearTargets, makeTarget, loadTargets,-	-- * Utils-	listPackages, spanRegion,-	withCurrentDirectory,-	logToChan, logToNull,--	Ghc,-	LogT(..),--	module HsDev.Tools.Ghc.MGhc,-	module Control.Concurrent.Worker-	) where--import Control.Monad-import Control.Monad.Except-import Control.Monad.Reader-import Control.Monad.Catch-import Data.Dynamic-import Data.List (intercalate)-import Data.Maybe-import Data.Time.Clock (getCurrentTime)-import Data.Version (showVersion)-import System.Directory (getCurrentDirectory, setCurrentDirectory)-import qualified  System.Log.Simple as Log-import System.Log.Simple.Monad (MonadLog(..), LogT(..), withLog)-import Text.Read (readMaybe)-import Text.Format hiding (withFlags)--import Exception (ExceptionMonad(..))-import GHC hiding (Warning, Module, moduleName, pkgDatabase)-import GHC.Paths-import Outputable-import FastString (unpackFS)-import Packages-import StringBuffer--import Control.Concurrent.FiniteChan-import Control.Concurrent.Worker-import System.Directory.Paths-import HsDev.Symbols.Location (Position(..), Region(..), region, ModulePackage, ModuleLocation(..))-import HsDev.Tools.Types-import HsDev.Tools.Ghc.Compat hiding (setLogAction)-import qualified HsDev.Tools.Ghc.Compat as C (setLogAction)-import HsDev.Tools.Ghc.MGhc--data SessionTarget =-	SessionGhci |-	SessionGhc [String]--instance Show SessionTarget where-	show SessionGhci = "ghci"-	show (SessionGhc opts) = "ghc " ++ intercalate ", " opts--instance Formattable SessionTarget--instance Eq SessionTarget where-	SessionGhci == SessionGhci = True-	SessionGhc lopts == SessionGhc ropts = lopts == ropts-	_ == _ = False--instance Ord SessionTarget where-	compare l r = compare (isGhci l) (isGhci r) where-		isGhci SessionGhci = True-		isGhci _ = False--type GhcM a = MGhcT SessionTarget (LogT IO) a--type GhcWorker = Worker (MGhcT SessionTarget (LogT IO))--instance (Monad m, GhcMonad m) => GhcMonad (ReaderT r m) where-	getSession = lift getSession-	setSession = lift . setSession--instance ExceptionMonad m => ExceptionMonad (LogT m) where-	gcatch act onError = LogT $ gcatch (runLogT act) (runLogT . onError)-	gmask f = LogT $ gmask f' where-		f' g' = runLogT $ f (LogT . g' . runLogT)--instance MonadThrow Ghc where-	throwM = liftIO . throwM--runGhcM :: MonadLog m => Maybe FilePath -> GhcM a -> m a-runGhcM dir act = do-	l <- Log.askLog-	liftIO $ withLog l $ runMGhcT dir act---- | Multi-session ghc worker-ghcWorker :: MonadLog m => m GhcWorker-ghcWorker = do-	l <- Log.askLog-	liftIO $ startWorker (withLog l . runGhcM (Just libdir)) id (Log.scope "ghc")---- | Create session with options-workerSession :: SessionTarget -> GhcM ()-workerSession SessionGhci = do-	Log.sendLog Log.Trace $ "session: {}" ~~ SessionGhci-	switchSession_ SessionGhci $ Just $ ghcRun [] (importModules preludeModules)-workerSession s@(SessionGhc opts) = do-	ms <- findSessionBy isGhcSession-	forM_ ms $ \s'@(SessionGhc opts') -> when (opts /= opts') $ do-		Log.sendLog Log.Trace $ "killing session: {}" ~~ s'-		deleteSession s'-	Log.sendLog Log.Trace $ "session: {}" ~~ s-	switchSession_ s $ Just $ ghcRun opts (return ())-	where-		isGhcSession (SessionGhc _) = True-		isGhcSession _ = False---- | Run ghc-ghcRun :: GhcMonad m => [String] -> m a -> m a-ghcRun opts f = do-	fs <- getSessionDynFlags-	cleanupHandler fs $ do-		(fs', _, _) <- parseDynamicFlags fs (map noLoc opts)-		let fs'' = fs' {-			ghcMode = CompManager,-			ghcLink = LinkInMemory,-			hscTarget = HscInterpreted }-			-- ghcLink = NoLink,-			-- hscTarget = HscNothing }-		void $ setSessionDynFlags fs''-		modifyFlags $ C.setLogAction logToNull-		f---- | Alter @DynFlags@ temporary-withFlags :: GhcMonad m => m a -> m a-withFlags = gbracket getSessionDynFlags (\fs -> setSessionDynFlags fs >> return ()) . const---- | Update @DynFlags@-modifyFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()-modifyFlags f = do-	fs <- getSessionDynFlags-	let-		fs' = f fs-	_ <- setSessionDynFlags fs'-	_ <- liftIO $ initPackages fs'-	return ()---- | Add options without reinit session-addCmdOpts :: (MonadLog m, GhcMonad m) => [String] -> m ()-addCmdOpts opts = do-	Log.sendLog Log.Trace $ "setting ghc options: {}" ~~ unwords opts-	fs <- getSessionDynFlags-	(fs', _, _) <- parseDynamicFlags fs (map noLoc opts)-	let fs'' = fs' {-		ghcMode = CompManager,-		ghcLink = LinkInMemory,-		hscTarget = HscInterpreted }-		-- ghcLink = NoLink,-		-- hscTarget = HscNothing }-	void $ setSessionDynFlags fs''---- | Set options after session reinit-setCmdOpts :: (MonadLog m, GhcMonad m) => [String] -> m ()-setCmdOpts opts = do-	Log.sendLog Log.Trace $ "restarting ghc session with: {}" ~~ unwords opts-	initGhcMonad (Just libdir)-	addCmdOpts opts-	modifyFlags $ C.setLogAction logToNull---- | Import some modules-importModules :: GhcMonad m => [String] -> m ()-importModules mods = mapM parseImportDecl ["import " ++ m | m <- mods] >>= setContext . map IIDecl---- | Default interpreter modules-preludeModules :: [String]-preludeModules = ["Prelude", "Data.List", "Control.Monad", "HsDev.Tools.Ghc.Prelude"]---- | Evaluate expression-evaluate :: GhcMonad m => String -> m String-evaluate expr = liftM fromDynamic (dynCompileExpr $ "show ({})" ~~ expr) >>=-	maybe (fail "evaluate fail") return---- | Clear loaded targets-clearTargets :: GhcMonad m => m ()-clearTargets = loadTargets []---- | Make target with its source code optional-makeTarget :: GhcMonad m => String -> Maybe String -> m Target-makeTarget name Nothing = guessTarget name Nothing-makeTarget name (Just cts) = do-	t <- guessTarget name Nothing-	tm <- liftIO getCurrentTime-	return t { targetContents = Just (stringToStringBuffer cts, tm) }---- | Load all targets-loadTargets :: GhcMonad m => [Target] -> m ()-loadTargets ts = setTargets ts >> load LoadAllTargets >> return ()---- | Get list of installed packages-listPackages :: GhcMonad m => m [ModulePackage]-listPackages = liftM (mapMaybe readPackage . fromMaybe [] . pkgDatabase) getSessionDynFlags--readPackage :: PackageConfig -> Maybe ModulePackage-readPackage pc = readMaybe $ packageNameString pc ++ "-" ++ showVersion (packageVersion pc)---- | Get region of @SrcSpan@-spanRegion :: SrcSpan -> Region-spanRegion (RealSrcSpan s) = Position (srcSpanStartLine s) (srcSpanStartCol s) `region` Position (srcSpanEndLine s) (srcSpanEndCol s)-spanRegion _ = Position 0 0 `region` Position 0 0---- | Set current directory and restore it after action-withCurrentDirectory :: GhcMonad m => FilePath -> m a -> m a-withCurrentDirectory dir act = gbracket (liftIO getCurrentDirectory) (liftIO . setCurrentDirectory) $-	const (liftIO (setCurrentDirectory dir) >> act)---- | Log  ghc warnings and errors as to chan--- You may have to apply recalcTabs on result notes-logToChan :: Chan (Note OutputMessage) -> LogAction-logToChan ch fs sev src msg-	| Just sev' <- checkSev sev = do-		src' <- canonicalize srcMod-		putChan ch $ Note {-			_noteSource = src',-			_noteRegion = spanRegion src,-			_noteLevel = Just sev',-			_note = OutputMessage {-				_message = showSDoc fs msg,-				_messageSuggestion = Nothing } }-	| otherwise = return ()-	where-		checkSev SevWarning = Just Warning-		checkSev SevError = Just Error-		checkSev SevFatal = Just Error-		checkSev _ = Nothing-		srcMod = case src of-			RealSrcSpan s' -> FileModule (unpackFS $ srcSpanFile s') Nothing-			_ -> ModuleSource Nothing---- | Don't log ghc warnings and errors-logToNull :: LogAction-logToNull _ _ _ _ = return ()---- TODO: Load target by @ModuleLocation@, which may cause updating @DynFlags@+{-# LANGUAGE PatternGuards, OverloadedStrings, FlexibleContexts #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Tools.Ghc.Worker (
+	-- * Workers
+	SessionType(..), SessionConfig(..),
+	GhcM, GhcWorker, MGhcT(..), runGhcM,
+	ghcWorker,
+	workerSession, ghcSession, ghciSession, haddockSession, tmpSession,
+
+	-- * Initializers and actions
+	ghcRun, ghcRunWith, interpretedFlags, noLinkFlags,
+	withFlags, modifyFlags,
+	importModules, preludeModules,
+	evaluate,
+	clearTargets, makeTarget, loadTargets,
+	loadInteractive, reload,
+	-- * Utils
+	spanRegion,
+	withCurrentDirectory,
+	logToChan, logToNull,
+
+	Ghc,
+	LogT(..),
+
+	module HsDev.Tools.Ghc.MGhc,
+	module Control.Concurrent.Worker
+	) where
+
+import Control.Lens (view, over)
+import Control.Monad
+import Control.Monad.Except
+import Control.Monad.Reader
+import Control.Monad.Catch
+import Data.Dynamic
+import Data.Time.Clock (getCurrentTime)
+import Data.String (fromString)
+import Data.Text (Text)
+import qualified Data.Text as T
+import System.Directory (getCurrentDirectory, setCurrentDirectory)
+import System.FilePath
+import qualified System.Log.Simple as Log
+import System.Log.Simple.Monad (MonadLog(..), LogT(..), withLog)
+import Text.Format hiding (withFlags)
+
+import Exception (ExceptionMonad(..), ghandle)
+import GHC hiding (Warning, Module)
+import GHC.Paths
+import Outputable
+import FastString (unpackFS)
+import StringBuffer
+
+import Control.Concurrent.FiniteChan
+import Control.Concurrent.Worker
+import System.Directory.Paths
+import HsDev.Symbols.Location (Position(..), Region(..), region, ModuleLocation(..))
+import HsDev.Tools.Types
+import HsDev.Tools.Ghc.Compat
+import qualified HsDev.Tools.Ghc.Compat as C (setLogAction)
+import HsDev.Tools.Ghc.MGhc
+
+data SessionType = SessionGhci | SessionGhc | SessionHaddock | SessionTmp deriving (Eq, Ord)
+data SessionConfig = SessionConfig SessionType [String] deriving (Eq, Ord)
+
+instance Show SessionType where
+	show SessionGhci = "ghci"
+	show SessionGhc = "ghc"
+	show SessionHaddock = "haddock"
+	show SessionTmp = "tmp"
+
+instance Show SessionConfig where
+	show (SessionConfig t opts) = unwords (show t : opts)
+
+instance Formattable SessionConfig
+
+type GhcM a = MGhcT SessionConfig (LogT IO) a
+
+type GhcWorker = Worker (MGhcT SessionConfig (LogT IO))
+
+instance (Monad m, GhcMonad m) => GhcMonad (ReaderT r m) where
+	getSession = lift getSession
+	setSession = lift . setSession
+
+instance ExceptionMonad m => ExceptionMonad (LogT m) where
+	gcatch act onError = LogT $ gcatch (runLogT act) (runLogT . onError)
+	gmask f = LogT $ gmask f' where
+		f' g' = runLogT $ f (LogT . g' . runLogT)
+
+instance MonadThrow Ghc where
+	throwM = liftIO . throwM
+
+runGhcM :: MonadLog m => Maybe FilePath -> GhcM a -> m a
+runGhcM dir act = do
+	l <- Log.askLog
+	liftIO $ withLog l $ runMGhcT dir act
+
+-- | Multi-session ghc worker
+ghcWorker :: MonadLog m => m GhcWorker
+ghcWorker = do
+	l <- Log.askLog
+	liftIO $ startWorker (withLog l . runGhcM (Just libdir)) (Log.scope "ghc") (ghandle logErr)
+	where
+		logErr :: MonadLog m => SomeException -> m ()
+		logErr e = Log.sendLog Log.Warning ("exception in ghc worker task: {}" ~~ displayException e)
+
+-- | Create session with options
+workerSession :: SessionType -> [String] -> GhcM ()
+workerSession ty opts = do
+	ms <- findSessionBy toKill
+	forM_ ms $ \s' -> do
+		Log.sendLog Log.Trace $ "killing session: {}" ~~ s'
+		deleteSession s'
+	Log.sendLog Log.Trace $ "session: {}" ~~ SessionConfig ty opts
+	switchSession_ (SessionConfig ty opts) $ Just initialize
+	where
+		toKill (SessionConfig ty' opts') = or [
+			(ty == ty' && opts /= opts'),
+			(ty /= ty' && ty' `elem` [SessionTmp, SessionHaddock] && ty /= SessionTmp)]
+		initialize = case ty of
+			SessionGhci -> ghcRun opts (importModules preludeModules)
+			SessionGhc -> ghcRun opts (return ())
+			SessionTmp -> ghcRun opts (return ())
+			SessionHaddock -> ghcRunWith noLinkFlags ("-haddock" : opts) (return ())
+
+-- | Get ghc session
+ghcSession :: [String] -> GhcM ()
+ghcSession = workerSession SessionGhc
+
+-- | Get ghci session
+ghciSession :: GhcM ()
+ghciSession = workerSession SessionGhci []
+
+-- | Get haddock session with flags
+haddockSession :: [String] -> GhcM ()
+haddockSession = workerSession SessionHaddock
+
+-- | Get haddock session with flags
+tmpSession :: [String] -> GhcM ()
+tmpSession = workerSession SessionTmp
+
+-- | Run ghc
+ghcRun :: GhcMonad m => [String] -> m a -> m a
+ghcRun = ghcRunWith interpretedFlags
+
+-- | Run ghc
+ghcRunWith :: GhcMonad m => (DynFlags -> DynFlags) -> [String] -> m a -> m a
+ghcRunWith onFlags opts act = do
+	fs <- getSessionDynFlags
+	cleanupHandler fs $ do
+		(fs', _, _) <- parseDynamicFlags fs (map noLoc opts)
+		void $ setSessionDynFlags $ onFlags fs'
+		modifyFlags $ C.setLogAction logToNull
+		act
+
+interpretedFlags :: DynFlags -> DynFlags
+interpretedFlags fs = fs {
+	ghcMode = CompManager,
+	ghcLink = LinkInMemory,
+	hscTarget = HscInterpreted }
+
+noLinkFlags :: DynFlags -> DynFlags
+noLinkFlags fs = fs {
+	ghcMode = CompManager,
+	ghcLink = NoLink,
+	hscTarget = HscNothing }
+
+-- | Alter @DynFlags@ temporary
+withFlags :: GhcMonad m => m a -> m a
+withFlags = gbracket getSessionDynFlags (\fs -> setSessionDynFlags fs >> return ()) . const
+
+-- | Update @DynFlags@
+modifyFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()
+modifyFlags f = do
+	fs <- getSessionDynFlags
+	let
+		fs' = f fs
+	_ <- setSessionDynFlags fs'
+	-- _ <- liftIO $ initPackages fs'
+	return ()
+
+-- | Import some modules
+importModules :: GhcMonad m => [String] -> m ()
+importModules mods = mapM parseImportDecl ["import " ++ m | m <- mods] >>= setContext . map IIDecl
+
+-- | Default interpreter modules
+preludeModules :: [String]
+preludeModules = ["Prelude", "Data.List", "Control.Monad", "HsDev.Tools.Ghc.Prelude"]
+
+-- | Evaluate expression
+evaluate :: GhcMonad m => String -> m String
+evaluate expr = liftM fromDynamic (dynCompileExpr $ "show ({})" ~~ expr) >>=
+	maybe (fail "evaluate fail") return
+
+-- | Clear loaded targets
+clearTargets :: GhcMonad m => m ()
+clearTargets = loadTargets []
+
+-- | Make target with its source code optional
+makeTarget :: GhcMonad m => Text -> Maybe Text -> m Target
+makeTarget name Nothing = guessTarget (T.unpack name) Nothing
+makeTarget name (Just cts) = do
+	t <- guessTarget (T.unpack name) Nothing
+	tm <- liftIO getCurrentTime
+	return t { targetContents = Just (stringToStringBuffer $ T.unpack cts, tm) }
+
+-- | Load all targets
+loadTargets :: GhcMonad m => [Target] -> m ()
+loadTargets ts = setTargets ts >> load LoadAllTargets >> return ()
+
+-- | Load and set interactive context
+loadInteractive :: GhcMonad m => Path -> Maybe Text -> m ()
+loadInteractive fpath mcts = do
+	fpath' <- liftIO $ canonicalize fpath
+	withCurrentDirectory (view path $ takeDir fpath') $ do
+		t <- makeTarget (over path takeFileName fpath') mcts
+		loadTargets [t]
+		g <- getModuleGraph
+		setContext [IIModule (ms_mod_name m) | m <- g]
+
+-- | Reload targets
+reload :: GhcMonad m => m ()
+reload = do
+	ts <- getTargets
+	ctx <- getContext
+	setContext []
+	clearTargets
+	setTargets ts
+	setContext ctx
+
+-- | Get region of @SrcSpan@
+spanRegion :: SrcSpan -> Region
+spanRegion (RealSrcSpan s) = Position (srcSpanStartLine s) (srcSpanStartCol s) `region` Position (srcSpanEndLine s) (srcSpanEndCol s)
+spanRegion _ = Position 0 0 `region` Position 0 0
+
+-- | Set current directory and restore it after action
+withCurrentDirectory :: GhcMonad m => FilePath -> m a -> m a
+withCurrentDirectory dir act = gbracket (liftIO getCurrentDirectory) (liftIO . setCurrentDirectory) $
+	const (liftIO (setCurrentDirectory dir) >> act)
+
+-- | Log  ghc warnings and errors as to chan
+-- You may have to apply recalcTabs on result notes
+logToChan :: Chan (Note OutputMessage) -> LogAction
+logToChan ch fs sev src msg
+	| Just sev' <- checkSev sev = do
+		src' <- canonicalize srcMod
+		void $ sendChan ch Note {
+			_noteSource = src',
+			_noteRegion = spanRegion src,
+			_noteLevel = Just sev',
+			_note = OutputMessage {
+				_message = fromString $ showSDoc fs msg,
+				_messageSuggestion = Nothing } }
+	| otherwise = return ()
+	where
+		checkSev SevWarning = Just Warning
+		checkSev SevError = Just Error
+		checkSev SevFatal = Just Error
+		checkSev _ = Nothing
+		srcMod = case src of
+			RealSrcSpan s' -> FileModule (fromFilePath $ unpackFS $ srcSpanFile s') Nothing
+			_ -> NoLocation
+
+-- | Don't log ghc warnings and errors
+logToNull :: LogAction
+logToNull _ _ _ _ = return ()
+
+-- TODO: Load target by @ModuleLocation@, which may cause updating @DynFlags@
src/HsDev/Tools/HDocs.hs view
@@ -1,60 +1,179 @@-module HsDev.Tools.HDocs (-	hdocsy, hdocs, hdocsCabal,-	setDocs,-	loadDocs,--	hdocsProcess,--	module Control.Monad.Except-	) where--import Control.DeepSeq-import Control.Lens (set, view, over)-import Control.Monad ()-import Control.Monad.Except--import Data.Aeson (decode)-import qualified Data.ByteString.Lazy.Char8 as L (pack)-import Data.Map (Map)-import qualified Data.Map as M-import Data.String (fromString)--import qualified HDocs.Module as HDocs-import qualified HDocs.Haddock as HDocs (readSource, readSources_)--import HsDev.Tools.Base-import HsDev.Symbols---- | Get docs for modules-hdocsy :: [ModuleLocation] -> [String] -> IO [Map String String]-hdocsy mlocs opts = runExceptT (docs' mlocs) >>= return . either (const $ replicate (length mlocs) M.empty) (map $ force . HDocs.formatDocs) where-	docs' :: [ModuleLocation] -> ExceptT String IO [HDocs.ModuleDocMap]-	docs' ms = liftM (map snd) $ HDocs.readSources_ opts $ map (view moduleFile) ms---- | Get docs for module-hdocs :: ModuleLocation -> [String] -> IO (Map String String)-hdocs mloc opts = runExceptT (docs' mloc) >>= return . either (const M.empty) (force . HDocs.formatDocs) where-	docs' :: ModuleLocation -> ExceptT String IO HDocs.ModuleDocMap-	docs' (FileModule fpath _) = liftM snd $ HDocs.readSource opts fpath-	docs' (InstalledModule _ _ mname) = HDocs.moduleDocs opts mname-	docs' _ = throwError $ "Can't get docs for: " ++ show mloc---- | Get all docs-hdocsCabal :: PackageDbStack -> [String] -> ExceptT String IO (Map String (Map String String))-hdocsCabal pdbs opts = liftM (M.map $ force . HDocs.formatDocs) $ HDocs.installedDocs (packageDbStackOpts pdbs ++ opts)---- | Set docs for module-setDocs :: Map String String -> Module -> Module-setDocs d = over moduleDeclarations (map setDoc) where-	setDoc decl' = set declarationDocs (M.lookup (view declarationName decl') d') decl'-	d' = M.mapKeys fromString . M.map fromString $ d---- | Load docs for module-loadDocs :: [String] -> Module -> IO Module-loadDocs opts m = do-	d <- hdocs (view moduleLocation m) opts-	return $ setDocs d m--hdocsProcess :: String -> [String] -> IO (Maybe (Map String String))-hdocsProcess mname opts = liftM (decode . L.pack . last . lines) $ runTool_ "hdocs" opts' where-	opts' = mname : concat [["-g", opt] | opt <- opts]+{-# LANGUAGE CPP, OverloadedStrings #-}
+
+module HsDev.Tools.HDocs (
+	hdocsy, hdocs, hdocsPackage, hdocsCabal,
+	setSymbolDocs, setDocs, setModuleDocs,
+
+	hdocsProcess,
+
+	readDocs, readModuleDocs, readProjectTargetDocs,
+
+	hdocsSupported,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Lens
+import Control.Monad ()
+import Control.Monad.Except
+
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import Data.Text (Text)
+#ifdef NODOCS
+import qualified System.Log.Simple as Log
+#endif
+
+#ifndef NODOCS
+import Control.DeepSeq
+import Data.Aeson (decode)
+import qualified Data.ByteString.Lazy.Char8 as L (pack)
+import Data.String (fromString)
+import qualified Data.Text as T
+
+import qualified HDocs.Module as HDocs
+import qualified HDocs.Haddock as HDocs
+
+import qualified GHC
+#endif
+
+import qualified PackageConfig as P
+
+import Data.LookupTable
+#ifndef NODOCS
+import HsDev.Error
+import HsDev.Scan.Browse (packageConfigs, readPackage)
+import HsDev.Tools.Base
+#endif
+import HsDev.Symbols
+import HsDev.Tools.Ghc.Worker
+import System.Directory.Paths
+
+-- | Get docs for modules
+hdocsy :: [ModuleLocation] -> [String] -> GhcM [Map String String]
+#ifndef NODOCS
+hdocsy mlocs opts = (map $ force . HDocs.formatDocs) <$> docs' mlocs where
+	docs' :: [ModuleLocation] -> GhcM [HDocs.ModuleDocMap]
+	docs' ms = do
+		haddockSession opts
+		liftGhc $ hsdevLiftWith (ToolError "hdocs") $
+			liftM (map snd) $ HDocs.readSourcesGhc opts $ map (view (moduleFile . path)) ms
+#else
+hdocsy _ _ = notSupported >> return mempty
+#endif
+
+-- | Get docs for module
+hdocs :: ModuleLocation -> [String] -> GhcM (Map String String)
+#ifndef NODOCS
+hdocs mloc opts = (force . HDocs.formatDocs) <$> docs' mloc where
+	docs' :: ModuleLocation -> GhcM HDocs.ModuleDocMap
+	docs' mloc' = do
+		haddockSession opts
+		liftGhc $ case mloc' of
+			(FileModule fpath _) -> hsdevLiftWith (ToolError "hdocs") $ liftM snd $ HDocs.readSourceGhc opts (view path fpath)
+			(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'
+#else
+hdocs _ _ = notSupported >> return mempty
+#endif
+
+-- | Get docs for package
+hdocsPackage :: P.PackageConfig -> GhcM (Map Text (Map Text Text))
+#ifndef NODOCS
+hdocsPackage p = do
+	ifaces <-
+		liftIO . hsdevLiftWith (ToolError "hdocs") .
+		liftM concat . mapM ((`mplus` return []) . HDocs.readInstalledInterfaces) $
+		P.haddockInterfaces p
+	let
+		idocs = HDocs.installedInterfacesDocs ifaces
+		iexports = M.fromList $ map (HDocs.exportsDocs idocs) ifaces
+		docs = M.map HDocs.formatDocs iexports
+		tdocs = M.map (M.map fromString . M.mapKeys fromString) . M.mapKeys fromString $ docs
+	return $!! tdocs
+#else
+hdocsPackage _ = notSupported >> return mempty
+#endif
+
+-- | Get all docs
+hdocsCabal :: PackageDbStack -> [String] -> GhcM [(ModulePackage, (Map Text (Map Text Text)))]
+#ifndef NODOCS
+hdocsCabal pdbs opts = do
+	haddockSession (packageDbStackOpts pdbs ++ opts)
+	pkgs <- packageConfigs
+	forM pkgs $ \pkg -> do
+		pkgDocs' <- hdocsPackage pkg
+		return (readPackage pkg, pkgDocs')
+#else
+hdocsCabal _ _ = notSupported >> return mempty
+#endif
+
+-- | Set docs for module
+setSymbolDocs :: MonadIO m => LookupTable (Text, Text) (Maybe Text) -> Map Text Text -> Symbol -> m Symbol
+setSymbolDocs tbl d sym = do
+	symDocs <- cacheInTableM tbl (symName, symMod) (return $ M.lookup symName d)
+	return $ set symbolDocs symDocs sym
+	where
+		symName = view (symbolId . symbolName) sym
+		symMod = view (symbolId . symbolModule . moduleName) sym
+
+-- | Set docs for module symbols
+setDocs :: MonadIO m => LookupTable (Text, Text) (Maybe Text) -> Map Text Text -> Module -> m Module
+setDocs tbl d = mapMOf (moduleExports . each) setDoc >=> mapMOf (moduleScope . each . each) setDoc where
+	setDoc = setSymbolDocs tbl d
+
+-- | Set docs for modules
+setModuleDocs :: MonadIO m => LookupTable (Text, Text) (Maybe Text) -> Map Text (Map Text Text) -> Module -> m Module
+setModuleDocs tbl docs m = maybe return (setDocs tbl) (M.lookup (view (moduleId . moduleName) m) docs) $ m
+
+hdocsProcess :: String -> [String] -> IO (Maybe (Map String String))
+#ifndef NODOCS
+hdocsProcess mname opts = liftM (decode . L.pack . last . lines) $ runTool_ "hdocs" opts' where
+	opts' = mname : concat [["-g", opt] | opt <- opts]
+#else
+hdocsProcess _ _ = return mempty
+#endif
+
+-- | Read docs for one module
+readDocs :: Text -> [String] -> Path -> GhcM (Maybe (Map String String))
+#ifndef NODOCS
+readDocs mname opts fpath = do
+	docs <- liftGhc $ hsdevLift $ HDocs.readSourcesGhc opts [view path fpath]
+	return $ fmap HDocs.formatDocs $ lookup (T.unpack mname) docs
+#else
+readDocs _ _ _ = notSupported >> return mempty
+#endif
+
+-- | Read docs for one module
+readModuleDocs :: [String] -> Module -> GhcM (Maybe (Map String String))
+#ifndef NODOCS
+readModuleDocs opts m = case view (moduleId . moduleLocation) m of
+	FileModule fpath _ -> withCurrentDirectory (sourceRoot_ (m ^. moduleId) ^. path) $ do
+		readDocs (m ^. moduleId . moduleName) opts fpath
+	_ -> hsdevError $ ModuleNotSource (view (moduleId . moduleLocation) m)
+#else
+readModuleDocs _ _ = notSupported >> return mempty
+#endif
+
+readProjectTargetDocs :: [String] -> Project -> [Path] -> GhcM (Map String (Map String String))
+#ifndef NODOCS
+readProjectTargetDocs opts proj fpaths = withCurrentDirectory (proj ^. projectPath . path) $ do
+	docs <- liftGhc $ hsdevLift $ HDocs.readSourcesGhc opts (fpaths ^.. each . path)
+	return $ M.map HDocs.formatDocs $ M.fromList docs
+#else
+readProjectTargetDocs _ _ _ = notSupported >> return mempty
+#endif
+
+#ifdef NODOCS
+notSupported :: Log.MonadLog m => m ()
+notSupported = Log.sendLog Log.Warning "compiled without hdocs support"
+#endif
+
+hdocsSupported :: Bool
+#ifndef NODOCS
+hdocsSupported = True
+#else
+hdocsSupported = False
+#endif
src/HsDev/Tools/HLint.hs view
@@ -1,97 +1,99 @@-module HsDev.Tools.HLint (-	hlint, hlintFile, hlintSource,--	module Control.Monad.Except-	) where--import Control.Arrow-import Control.Lens (over, view, _Just)-import Control.Monad.Except-import Data.Char-import Data.List-import Data.Maybe (mapMaybe)-import Data.Ord-import Language.Haskell.HLint3 (autoSettings, parseModuleEx, applyHints, Idea(..), parseErrorMessage, ParseFlags(..), CppFlags(..))-import Language.Haskell.Exts.SrcLoc-import qualified Language.Haskell.HLint3 as HL (Severity(..))--import System.Directory.Paths (canonicalize)-import HsDev.Symbols.Location-import HsDev.Tools.Base-import HsDev.Util (readFileUtf8, split)--hlint :: FilePath -> Maybe String -> ExceptT String IO [Note OutputMessage]-hlint file msrc = do-	file' <- liftIO $ canonicalize file-	cts <- maybe (liftIO $ readFileUtf8 file') return msrc-	(flags, classify, hint) <- liftIO autoSettings-	p <- liftIO $ parseModuleEx (flags { cppFlags = CppSimple }) file' (Just cts)-	m <- either (throwError . parseErrorMessage) return p-	return $ map (recalcTabs cts 8 . indentIdea cts . fromIdea) $ applyHints classify hint [m]--hlintFile :: FilePath -> ExceptT String IO [Note OutputMessage]-hlintFile f = hlint f Nothing--hlintSource :: FilePath -> String -> ExceptT String IO [Note OutputMessage]-hlintSource f = hlint f . Just--fromIdea :: Idea -> Note OutputMessage-fromIdea idea = Note {-	_noteSource = FileModule (srcSpanFilename src) Nothing,-	_noteRegion = Region (Position (srcSpanStartLine src) (srcSpanStartColumn src)) (Position (srcSpanEndLine src) (srcSpanEndColumn src)),-	_noteLevel = Just $ case ideaSeverity idea of-		HL.Warning -> Warning-		HL.Error -> Error-		_ -> Hint,-	_note = OutputMessage {-		_message = ideaHint idea,-		_messageSuggestion = ideaTo idea } }-	where-		src = ideaSpan idea--indentIdea :: String -> Note OutputMessage -> Note OutputMessage-indentIdea cts idea = case analyzeIndent cts of-	Nothing -> idea-	Just i -> over (note . messageSuggestion . _Just) (indent' i) idea-	where-		indent' i' = intercalate "\n" . indentTail . map (uncurry (++) . first (concat . (`replicate` i') . (`div` 2) . length) . span isSpace) . split (== '\n')-		indentTail [] = []-		indentTail (h : hs) = h : map (firstIndent ++) hs-		firstIndent = takeWhile isSpace firstLine-		firstLine = regionStr (Position firstLineNum 1 `region` Position (succ firstLineNum) 1) cts-		firstLineNum = view (noteRegion . regionFrom . positionLine) idea---- | Indent in source-data Indent = Spaces Int | Tabs deriving (Eq, Ord)--instance Show Indent where-	show (Spaces n) = replicate n ' '-	show Tabs = "\t"---- | Analyze source indentation to convert suggestion to same indentation--- Returns one indent-analyzeIndent :: String -> Maybe String-analyzeIndent =-	fmap show . selectIndent . map fst . dropUnusual .-	sortBy (comparing $ negate . snd) .-	map (head &&& length) .-	group . sort .-	mapMaybe (guessIndent . takeWhile isSpace) . lines-	where-		selectIndent :: [Indent] -> Maybe Indent-		selectIndent [] = Nothing-		selectIndent (Tabs : _) = Just Tabs-		selectIndent indents = Just $ Spaces $ foldr1 gcd $ mapMaybe spaces indents where-			spaces :: Indent -> Maybe Int-			spaces Tabs = Nothing-			spaces (Spaces n) = Just n-		dropUnusual :: [(Indent, Int)] -> [(Indent, Int)]-		dropUnusual [] = []-		dropUnusual is@((_, freq):_) = takeWhile ((> freq `div` 5) . snd) is---- | Guess indent of one line-guessIndent :: String -> Maybe Indent-guessIndent s-	| all (== ' ') s = Just $ Spaces $ length s-	| all (== '\t') s = Just Tabs-	| otherwise = Nothing+module HsDev.Tools.HLint (
+	hlint,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Arrow
+import Control.Lens (over, view, _Just)
+import Control.Monad.Except
+import Data.Char
+import Data.List
+import Data.Maybe (mapMaybe)
+import Data.Text (Text)
+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.Exts.SrcLoc
+import qualified Language.Haskell.HLint3 as HL (Severity(..))
+
+import System.Directory.Paths
+import HsDev.Symbols.Location
+import HsDev.Tools.Base
+import HsDev.Util (readFileUtf8)
+
+hlint :: FilePath -> Maybe Text -> ExceptT String IO [Note OutputMessage]
+hlint file msrc = do
+	file' <- liftIO $ canonicalize file
+	cts <- maybe (liftIO $ readFileUtf8 file') return msrc
+	(flags, classify, hint) <- liftIO autoSettings
+	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) $
+		filter (not . ignoreIdea) $
+		applyHints classify hint [m]
+
+ignoreIdea :: Idea -> Bool
+ignoreIdea idea = ideaSeverity idea == HL.Ignore
+
+fromIdea :: Idea -> Note OutputMessage
+fromIdea idea = Note {
+	_noteSource = FileModule (fromFilePath $ srcSpanFilename src) Nothing,
+	_noteRegion = Region (Position (srcSpanStartLine src) (srcSpanStartColumn src)) (Position (srcSpanEndLine src) (srcSpanEndColumn src)),
+	_noteLevel = Just $ case ideaSeverity idea of
+		HL.Warning -> Warning
+		HL.Error -> Error
+		_ -> Hint,
+	_note = OutputMessage {
+		_message = fromString $ ideaHint idea,
+		_messageSuggestion = fmap fromString $ ideaTo idea } }
+	where
+		src = ideaSpan idea
+
+indentIdea :: Text -> Note OutputMessage -> Note OutputMessage
+indentIdea cts idea = case analyzeIndent cts of
+	Nothing -> idea
+	Just i -> over (note . messageSuggestion . _Just) (indent' i) idea
+	where
+		indent' i' = T.intercalate (fromString "\n") . indentTail . map (uncurry T.append . first ((`T.replicate` i') . (`div` 2) . T.length) . T.span isSpace) . T.split (== '\n')
+		indentTail [] = []
+		indentTail (h : hs) = h : map (firstIndent `T.append`) hs
+		firstIndent = T.takeWhile isSpace firstLine
+		firstLine = regionStr (Position firstLineNum 1 `region` Position (succ firstLineNum) 1) cts
+		firstLineNum = view (noteRegion . regionFrom . positionLine) idea
+
+-- | Indent in source
+data Indent = Spaces Int | Tabs deriving (Eq, Ord)
+
+instance Show Indent where
+	show (Spaces n) = replicate n ' '
+	show Tabs = "\t"
+
+-- | Analyze source indentation to convert suggestion to same indentation
+-- Returns one indent
+analyzeIndent :: Text -> Maybe Text
+analyzeIndent =
+	fmap (fromString . show) . selectIndent . map fst . dropUnusual .
+	sortBy (comparing $ negate . snd) .
+	map (head &&& length) .
+	group . sort .
+	mapMaybe (guessIndent . T.takeWhile isSpace) . T.lines
+	where
+		selectIndent :: [Indent] -> Maybe Indent
+		selectIndent [] = Nothing
+		selectIndent (Tabs : _) = Just Tabs
+		selectIndent indents = Just $ Spaces $ foldr1 gcd $ mapMaybe spaces indents where
+			spaces :: Indent -> Maybe Int
+			spaces Tabs = Nothing
+			spaces (Spaces n) = Just n
+		dropUnusual :: [(Indent, Int)] -> [(Indent, Int)]
+		dropUnusual [] = []
+		dropUnusual is@((_, freq):_) = takeWhile ((> freq `div` 5) . snd) is
+
+-- | Guess indent of one line
+guessIndent :: Text -> Maybe Indent
+guessIndent s
+	| T.all (== ' ') s = Just $ Spaces $ T.length s
+	| T.all (== '\t') s = Just Tabs
+	| otherwise = Nothing
src/HsDev/Tools/Hayoo.hs view
@@ -1,135 +1,139 @@-{-# LANGUAGE OverloadedStrings #-}--module HsDev.Tools.Hayoo (-	-- * Types-	HayooResult(..), HayooSymbol(..),-	hayooAsDeclaration,-	-- * Search help online-	hayoo,-	-- * Utils-	untagDescription,--	-- * Reexportss-	module Control.Monad.Except-	) where--import Control.Arrow-import Control.Applicative-import Control.Monad.Except--import Data.Aeson-import qualified Data.ByteString.Lazy.Char8 as L-import Data.Either-import Network.HTTP-import Data.String (fromString)--import HsDev.Symbols-import HsDev.Tools.Base (replaceRx)-import HsDev.Util---- | Hayoo response-data HayooResult = HayooResult {-	resultMax :: Int,-	resultOffset :: Int,-	resultCount :: Int,-	resultResult :: [HayooSymbol] }-		deriving (Eq, Ord, Read, Show)---- | Hayoo symbol-data HayooSymbol = HayooSymbol {-	resultUri :: String,-	tag :: String,-	hayooPackage :: String,-	hayooName :: String,-	hayooSource :: String,-	hayooDescription :: String,-	hayooSignature :: String,-	hayooModules :: [String],-	hayooScore :: Double,-	hayooType :: String }-		deriving (Eq, Ord, Read, Show)--newtype HayooValue = HayooValue { hayooValue :: Either Value HayooSymbol }--instance FromJSON HayooResult where-	parseJSON = withObject "hayoo response" $ \v -> HayooResult <$>-		(v .:: "max") <*>-		(v .:: "offset") <*>-		(v .:: "count") <*>-		((rights . map hayooValue) <$> (v .:: "result"))--instance Symbol HayooSymbol where-	symbolName = fromString . hayooName-	symbolQualifiedName f = fromString $ case hayooModules f of-		[] -> hayooName f-		(m:_) -> m ++ "." ++ hayooName f-	symbolDocs = Just . fromString . hayooDescription-	symbolLocation r = Location (ModuleSource $ Just $ resultUri r) Nothing--instance Documented HayooSymbol where-	brief f-		| hayooType f == "function" = hayooName f ++ " :: " ++ hayooSignature f-		| otherwise = hayooType f ++ " " ++ hayooName f-	detailed f = unlines $ defaultDetailed f ++ online where-		online = [-			"", "Hayoo online documentation", "",-			"Package: " ++ hayooPackage f,-			"Hackage URL: " ++ resultUri f]--instance FromJSON HayooSymbol where-	parseJSON = withObject "symbol" $ \v -> HayooSymbol <$>-		(v .:: "resultUri") <*>-		(v .:: "tag") <*>-		(v .:: "resultPackage") <*>-		(v .:: "resultName") <*>-		(v .:: "resultSource") <*>-		(v .:: "resultDescription") <*>-		(v .:: "resultSignature") <*>-		(v .:: "resultModules") <*>-		(v .:: "resultScore") <*>-		(v .:: "resultType")--instance FromJSON HayooValue where-	parseJSON v = HayooValue <$> ((Right <$> parseJSON v) <|> pure (Left v))---- | 'HayooFunction' as 'Declaration'-hayooAsDeclaration :: HayooSymbol -> Maybe ModuleDeclaration-hayooAsDeclaration f-	| hayooType f `elem` ["function", "type", "newtype", "data", "class"] = Just ModuleDeclaration {-		_declarationModuleId = ModuleId {-			_moduleIdName = fromString $ head $ hayooModules f,-			_moduleIdLocation = ModuleSource (Just $ resultUri f) },-		_moduleDeclaration = Declaration {-			_declarationName = fromString $ hayooName f,-			_declarationDefined = Nothing,-			_declarationImported = Nothing,-			_declarationDocs = Just (fromString $ addOnline $ untagDescription $ hayooDescription f),-			_declarationPosition = Nothing,-			_declaration = declInfo } }-	| otherwise = Nothing-	where-		-- Add other info-		addOnline d = unlines [-			d, "",-			"Hayoo online documentation",-			"",-			"Package: " ++ hayooPackage f,-			"Hackage URL: " ++ resultUri f]--		declInfo-			| hayooType f == "function" = Function (Just $ fromString $ hayooSignature f) [] Nothing-			| hayooType f `elem` ["type", "newtype", "data", "class"] = declarationTypeCtor (hayooType f) $ TypeInfo Nothing [] Nothing []-			| otherwise = error "Impossible"---- | Search hayoo-hayoo :: String -> Maybe Int -> ExceptT String IO HayooResult-hayoo q page = do-	resp <- ExceptT $ (show +++ rspBody) <$> simpleHTTP (getRequest $ maybe id addPage page $ "http://hayoo.fh-wedel.de/json/?query=" ++ urlEncode q)-	ExceptT $ return $ eitherDecode $ L.pack resp-	where-		addPage :: Int -> String -> String-		addPage p s = s ++ "&page=" ++ show p---- | Remove tags in description-untagDescription :: String -> String-untagDescription = replaceRx "</?\\w+[^>]*>" ""+{-# LANGUAGE OverloadedStrings #-}
+
+module HsDev.Tools.Hayoo (
+	-- * Types
+	HayooResult(..), HayooSymbol(..),
+	hayooAsSymbol,
+	-- * Search help online
+	hayoo,
+	-- * Utils
+	untagDescription,
+
+	-- * Reexportss
+	module Control.Monad.Except
+	) where
+
+import Control.Arrow
+import Control.Applicative
+import Control.Lens (lens)
+import Control.Monad.Except
+
+import Data.Aeson
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Either
+import Data.Maybe (listToMaybe, fromJust)
+import Network.HTTP
+import Data.String (fromString)
+import qualified Data.Text as T (unpack, unlines)
+
+import HsDev.Symbols
+import HsDev.Tools.Base (replaceRx)
+import HsDev.Util
+
+-- | Hayoo response
+data HayooResult = HayooResult {
+	resultMax :: Int,
+	resultOffset :: Int,
+	resultCount :: Int,
+	resultResult :: [HayooSymbol] }
+		deriving (Eq, Ord, Read, Show)
+
+-- | Hayoo symbol
+data HayooSymbol = HayooSymbol {
+	resultUri :: String,
+	tag :: String,
+	hayooPackage :: String,
+	hayooName :: String,
+	hayooSource :: String,
+	hayooDescription :: String,
+	hayooSignature :: String,
+	hayooModules :: [String],
+	hayooScore :: Double,
+	hayooType :: String }
+		deriving (Eq, Ord, Read, Show)
+
+newtype HayooValue = HayooValue { hayooValue :: Either Value HayooSymbol }
+
+instance FromJSON HayooResult where
+	parseJSON = withObject "hayoo response" $ \v -> HayooResult <$>
+		(v .:: "max") <*>
+		(v .:: "offset") <*>
+		(v .:: "count") <*>
+		((rights . map hayooValue) <$> (v .:: "result"))
+
+instance Sourced HayooSymbol where
+	sourcedName = lens g' s' where
+		g' = fromString . hayooName
+		s' sym n = sym { hayooName = T.unpack n }
+	sourcedModule = lens g' s' where
+		g' h = ModuleId nm (OtherLocation $ fromString $ resultUri h) where
+			nm = maybe mempty fromString $ listToMaybe $ hayooModules h
+		s' h _ = h
+	sourcedDocs f h = (\d' -> h { hayooDescription = T.unpack d' }) <$> f (fromString $ hayooDescription h)
+
+instance Documented HayooSymbol where
+	brief f
+		| hayooType f == "function" = fromString $ hayooName f ++ " :: " ++ hayooSignature f
+		| otherwise = fromString $ hayooType f ++ " " ++ hayooName f
+	detailed f = T.unlines $ defaultDetailed f ++ map fromString online where
+		online = [
+			"", "Hayoo online documentation", "",
+			"Package: " ++ hayooPackage f,
+			"Hackage URL: " ++ resultUri f]
+
+instance FromJSON HayooSymbol where
+	parseJSON = withObject "symbol" $ \v -> HayooSymbol <$>
+		(v .:: "resultUri") <*>
+		(v .:: "tag") <*>
+		(v .:: "resultPackage") <*>
+		(v .:: "resultName") <*>
+		(v .:: "resultSource") <*>
+		(v .:: "resultDescription") <*>
+		(v .:: "resultSignature") <*>
+		(v .:: "resultModules") <*>
+		(v .:: "resultScore") <*>
+		(v .:: "resultType")
+
+instance FromJSON HayooValue where
+	parseJSON v = HayooValue <$> ((Right <$> parseJSON v) <|> pure (Left v))
+
+-- | 'HayooFunction' as 'Symbol'
+hayooAsSymbol :: HayooSymbol -> Maybe Symbol
+hayooAsSymbol f
+	| hayooType f `elem` ["function", "type", "newtype", "data", "class"] = Just Symbol {
+		_symbolId = SymbolId {
+			_symbolName = fromString $ hayooName f,
+			_symbolModule = ModuleId {
+				_moduleName = fromString $ head $ hayooModules f,
+				_moduleLocation = OtherLocation (fromString $ resultUri f) } },
+		_symbolDocs = Just (fromString $ addOnline $ untagDescription $ hayooDescription f),
+		_symbolPosition = Nothing,
+		_symbolInfo = info }
+	| otherwise = Nothing
+	where
+		-- Add other info
+		addOnline d = unlines [
+			d, "",
+			"Hayoo online documentation",
+			"",
+			"Package: " ++ hayooPackage f,
+			"Hackage URL: " ++ resultUri f]
+
+		info
+			| hayooType f == "function" = Function (Just $ fromString $ hayooSignature f)
+			| hayooType f `elem` ["type", "newtype", "data", "class"] = (fromJust $ lookup (hayooType f) ctors) [] []
+			| otherwise = error "Impossible"
+		ctors = [("type", Type), ("newtype", NewType), ("data", Data), ("class", Class)]
+
+-- | Search hayoo
+hayoo :: String -> Maybe Int -> ExceptT String IO HayooResult
+hayoo q page = do
+	resp <- ExceptT $ (show +++ rspBody) <$> simpleHTTP (getRequest $ maybe id addPage page $ "http://hayoo.fh-wedel.de/json/?query=" ++ urlEncode q)
+	ExceptT $ return $ eitherDecode $ L.pack resp
+	where
+		addPage :: Int -> String -> String
+		addPage p s = s ++ "&page=" ++ show p
+
+-- | Remove tags in description
+untagDescription :: String -> String
+untagDescription = replaceRx "</?\\w+[^>]*>" ""
+ src/HsDev/Tools/Refact.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
+
+module HsDev.Tools.Refact (
+	Refact(..), refactMessage, refactAction,
+	refact, update,
+
+	replace, cut, paste,
+
+	fromRegion, fromPosition
+	) where
+
+import Control.Lens hiding ((.=))
+import Data.Aeson
+import Data.Text (Text)
+import Data.Text.Region hiding (Region(..), update)
+import qualified Data.Text.Region as R
+
+import HsDev.Symbols.Location
+import HsDev.Util
+
+data Refact = Refact {
+	_refactMessage :: Text,
+	_refactAction :: Replace Text }
+		deriving (Eq, Show)
+
+instance ToJSON Refact where
+	toJSON (Refact msg cor) = object [
+		"message" .= msg,
+		"action" .= cor]
+
+instance FromJSON Refact where
+	parseJSON = withObject "correction" $ \v -> Refact <$>
+		v .:: "message" <*>
+		v .:: "action"
+
+makeLenses ''Refact
+
+instance Regioned Refact where
+	regions = refactAction . regions
+
+refact :: [Refact] -> Text -> Text
+refact rs = apply act where
+	act = Edit (rs ^.. each . refactAction)
+
+update :: Regioned a => [Refact] -> [a] -> [a]
+update rs = map (R.update act) where
+	act = Edit (rs ^.. each . refactAction)
+
+fromRegion :: Region -> R.Region
+fromRegion (Region f t) = fromPosition f `till` fromPosition t
+
+fromPosition :: Position -> Point
+fromPosition (Position l c) = pt (pred l) (pred c)
src/HsDev/Tools/Types.hs view
@@ -1,99 +1,100 @@-{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}--module HsDev.Tools.Types (-	Severity(..),-	Note(..), noteSource, noteRegion, noteLevel, note,-	OutputMessage(..), message, messageSuggestion, outputMessage-	) where--import Control.DeepSeq (NFData(..))-import Control.Lens (makeLenses)-import Control.Monad-import Data.Aeson hiding (Error)--import System.Directory.Paths-import HsDev.Symbols.Location-import HsDev.Util ((.::), (.::?))---- | Note severity-data Severity = Error | Warning | Hint deriving (Enum, Bounded, Eq, Ord, Read, Show)--instance NFData Severity where-	rnf Error = ()-	rnf Warning = ()-	rnf Hint = ()--instance ToJSON Severity where-	toJSON Error = toJSON ("error" :: String)-	toJSON Warning = toJSON ("warning" :: String)-	toJSON Hint = toJSON ("hint" :: String)--instance FromJSON Severity where-	parseJSON v = do-		s <- parseJSON v-		msum [-			guard (s == ("error" :: String)) >> return Error,-			guard (s == ("warning" :: String)) >> return Warning,-			guard (s == ("hint" :: String)) >> return Hint,-			fail $ "Unknown severity: " ++ s]---- | Note over some region-data Note a = Note {-	_noteSource :: ModuleLocation,-	_noteRegion :: Region,-	_noteLevel :: Maybe Severity,-	_note :: a }-		deriving (Eq, Show)--makeLenses ''Note--instance Functor Note where-	fmap f (Note s r l n) = Note s r l (f n)--instance NFData a => NFData (Note a) where-	rnf (Note s r l n) = rnf s `seq` rnf r `seq` rnf l `seq` rnf n--instance ToJSON a => ToJSON (Note a) where-	toJSON (Note s r l n) = object [-		"source" .= s,-		"region" .= r,-		"level" .= l,-		"note" .= n]--instance FromJSON a => FromJSON (Note a) where-	parseJSON = withObject "note" $ \v -> Note <$>-		v .:: "source" <*>-		v .:: "region" <*>-		v .::? "level" <*>-		v .:: "note"--instance RecalcTabs (Note a) where-	recalcTabs cts n' (Note s r l n) = Note s (recalcTabs cts n' r) l n-	calcTabs cts n' (Note s r l n) = Note s (calcTabs cts n' r) l n--instance Paths (Note a) where-	paths f (Note s r l n) = Note <$> paths f s <*> pure r <*> pure l <*> pure n---- | Output message from some tool (ghc, ghc-mod, hlint) with optional suggestion-data OutputMessage = OutputMessage {-	_message :: String,-	_messageSuggestion :: Maybe String }-		deriving (Eq, Ord, Read, Show)--instance NFData OutputMessage where-	rnf (OutputMessage m s) = rnf m `seq` rnf s--instance ToJSON OutputMessage where-	toJSON (OutputMessage m s) = object [-		"message" .= m,-		"suggestion" .= s]--instance FromJSON OutputMessage where-	parseJSON = withObject "output-message" $ \v -> OutputMessage <$>-		v .:: "message" <*>-		v .:: "suggestion"--outputMessage :: String -> OutputMessage-outputMessage msg = OutputMessage msg Nothing--makeLenses ''OutputMessage+{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
+
+module HsDev.Tools.Types (
+	Severity(..),
+	Note(..), noteSource, noteRegion, noteLevel, note,
+	OutputMessage(..), message, messageSuggestion, outputMessage
+	) where
+
+import Control.DeepSeq (NFData(..))
+import Control.Lens (makeLenses)
+import Control.Monad
+import Data.Aeson hiding (Error)
+import Data.Text (Text)
+
+import System.Directory.Paths
+import HsDev.Symbols.Location
+import HsDev.Util ((.::), (.::?), noNulls)
+
+-- | Note severity
+data Severity = Error | Warning | Hint deriving (Enum, Bounded, Eq, Ord, Read, Show)
+
+instance NFData Severity where
+	rnf Error = ()
+	rnf Warning = ()
+	rnf Hint = ()
+
+instance ToJSON Severity where
+	toJSON Error = toJSON ("error" :: String)
+	toJSON Warning = toJSON ("warning" :: String)
+	toJSON Hint = toJSON ("hint" :: String)
+
+instance FromJSON Severity where
+	parseJSON v = do
+		s <- parseJSON v
+		msum [
+			guard (s == ("error" :: String)) >> return Error,
+			guard (s == ("warning" :: String)) >> return Warning,
+			guard (s == ("hint" :: String)) >> return Hint,
+			fail $ "Unknown severity: " ++ s]
+
+-- | Note over some region
+data Note a = Note {
+	_noteSource :: ModuleLocation,
+	_noteRegion :: Region,
+	_noteLevel :: Maybe Severity,
+	_note :: a }
+		deriving (Eq, Show)
+
+makeLenses ''Note
+
+instance Functor Note where
+	fmap f (Note s r l n) = Note s r l (f n)
+
+instance NFData a => NFData (Note a) where
+	rnf (Note s r l n) = rnf s `seq` rnf r `seq` rnf l `seq` rnf n
+
+instance ToJSON a => ToJSON (Note a) where
+	toJSON (Note s r l n) = object $ noNulls [
+		"source" .= s,
+		"region" .= r,
+		"level" .= l,
+		"note" .= n]
+
+instance FromJSON a => FromJSON (Note a) where
+	parseJSON = withObject "note" $ \v -> Note <$>
+		v .:: "source" <*>
+		v .:: "region" <*>
+		v .::? "level" <*>
+		v .:: "note"
+
+instance RecalcTabs (Note a) where
+	recalcTabs cts n' (Note s r l n) = Note s (recalcTabs cts n' r) l n
+	calcTabs cts n' (Note s r l n) = Note s (calcTabs cts n' r) l n
+
+instance Paths (Note a) where
+	paths f (Note s r l n) = Note <$> paths f s <*> pure r <*> pure l <*> pure n
+
+-- | Output message from some tool (ghc, ghc-mod, hlint) with optional suggestion
+data OutputMessage = OutputMessage {
+	_message :: Text,
+	_messageSuggestion :: Maybe Text }
+		deriving (Eq, Ord, Read, Show)
+
+instance NFData OutputMessage where
+	rnf (OutputMessage m s) = rnf m `seq` rnf s
+
+instance ToJSON OutputMessage where
+	toJSON (OutputMessage m s) = object [
+		"message" .= m,
+		"suggestion" .= s]
+
+instance FromJSON OutputMessage where
+	parseJSON = withObject "output-message" $ \v -> OutputMessage <$>
+		v .:: "message" <*>
+		v .:: "suggestion"
+
+outputMessage :: Text -> OutputMessage
+outputMessage msg = OutputMessage msg Nothing
+
+makeLenses ''OutputMessage
src/HsDev/Types.hs view
@@ -1,112 +1,124 @@-{-# LANGUAGE OverloadedStrings #-}--module HsDev.Types (-	HsDevError(..)-	) where--import Control.Exception-import Control.DeepSeq (NFData(..))-import Data.Aeson-import Data.Aeson.Types (Pair, Parser)-import Data.Typeable-import Text.Format--import HsDev.Symbols.Location---- | hsdev exception type-data HsDevError =-	ModuleNotSource ModuleLocation |-	BrowseNoModuleInfo String |-	FileNotFound FilePath |-	ToolNotFound String |-	ProjectNotFound String |-	PackageNotFound String |-	ToolError String String |-	NotInspected ModuleLocation |-	InspectError String |-	InspectCabalError FilePath String |-	IOFailed String |-	GhcError String |-	RequestError String String |-	ResponseError String String |-	OtherError String-		deriving (Typeable)--instance NFData HsDevError where-	rnf (ModuleNotSource mloc) = rnf mloc-	rnf (BrowseNoModuleInfo m) = rnf m-	rnf (FileNotFound f) = rnf f-	rnf (ToolNotFound t) = rnf t-	rnf (ProjectNotFound p) = rnf p-	rnf (PackageNotFound p) = rnf p-	rnf (ToolError t e) = rnf t `seq` rnf e-	rnf (NotInspected mloc) = rnf mloc-	rnf (InspectError e) = rnf e-	rnf (InspectCabalError c e) = rnf c `seq` rnf e-	rnf (IOFailed e) = rnf e-	rnf (GhcError e) = rnf e-	rnf (RequestError e r) = rnf e `seq` rnf r-	rnf (ResponseError e r) = rnf e `seq` rnf r-	rnf (OtherError e) = rnf e--instance Show HsDevError where-	show (ModuleNotSource mloc) = format "module is not source: {}" ~~ show mloc-	show (BrowseNoModuleInfo m) = format "can't find module info for {}" ~~ m-	show (FileNotFound f) = format "file '{}' not found" ~~ f-	show (ToolNotFound t) = format "tool '{}' not found" ~~ t-	show (ProjectNotFound p) = format "project '{}' not found" ~~ p-	show (PackageNotFound p) = format "package '{}' not found" ~~ p-	show (ToolError t e) = format "tool '{}' failed: {}" ~~ t ~~ e-	show (NotInspected mloc) = "module not inspected: {}" ~~ show mloc-	show (InspectError e) = format "failed to inspect: {}" ~~ e-	show (InspectCabalError c e) = format "failed to inspect cabal {}: {}" ~~ c ~~ e-	show (IOFailed e) = format "io exception: {}" ~~ e-	show (GhcError e) = format "ghc exception: {}" ~~ e-	show (RequestError e r) = format "request error: {}, request: {}" ~~ e ~~ r-	show (ResponseError e r) = format "response error: {}, response: {}" ~~ e ~~ r-	show (OtherError e) = e--instance Formattable HsDevError where--jsonErr :: String -> [Pair] -> Value-jsonErr e = object . (("error" .= e) :)--instance ToJSON HsDevError where-	toJSON (ModuleNotSource mloc) = jsonErr "module is not source" ["module" .= mloc]-	toJSON (BrowseNoModuleInfo m) = jsonErr "no module info" ["module" .= m]-	toJSON (FileNotFound f) = jsonErr "file not found" ["file" .= f]-	toJSON (ToolNotFound t) = jsonErr "tool not found" ["tool" .= t]-	toJSON (ProjectNotFound p) = jsonErr "project not found" ["project" .= p]-	toJSON (PackageNotFound p) = jsonErr "package not found" ["package" .= p]-	toJSON (ToolError t e) = jsonErr "tool error" ["tool" .= t, "msg" .= e]-	toJSON (NotInspected mloc) = jsonErr "module not inspected" ["module" .= mloc]-	toJSON (InspectError e) = jsonErr "inspect error" ["msg" .= e]-	toJSON (InspectCabalError c e) = jsonErr "inspect cabal error" ["cabal" .= c, "msg" .= e]-	toJSON (IOFailed e) = jsonErr "io error" ["msg" .= e]-	toJSON (GhcError e) = jsonErr "ghc error" ["msg" .= e]-	toJSON (RequestError e r) = jsonErr "request error" ["msg" .= e, "request" .= r]-	toJSON (ResponseError e r) = jsonErr "response error" ["msg" .= e, "response" .= r]-	toJSON (OtherError e) = jsonErr "other error" ["msg" .= e]--instance FromJSON HsDevError where-	parseJSON = withObject "hsdev-error" $ \v -> do-		err <- v .: "error" :: Parser String-		case err of-			"module is not source" -> ModuleNotSource <$> v .: "module"-			"no module info" -> BrowseNoModuleInfo <$> v .: "module"-			"file not found" -> FileNotFound <$> v .: "file"-			"tool not found" -> ToolNotFound <$> v .: "tool"-			"project not found" -> ProjectNotFound <$> v .: "project"-			"package not found" -> PackageNotFound <$> v .: "package"-			"tool error" -> ToolError <$> v .: "tool" <*> v .: "msg"-			"module not inspected" -> NotInspected <$> v .: "module"-			"inspect error" -> InspectError <$> v .: "msg"-			"inspect cabal error" -> InspectCabalError <$> v .: "cabal" <*> v .: "msg"-			"io error" -> IOFailed <$> v .: "msg"-			"ghc error" -> GhcError <$> v .: "msg"-			"request error" -> RequestError <$> v .: "msg" <*> v .: "request"-			"response error" -> ResponseError <$> v .: "msg" <*> v .: "response"-			"other error" -> OtherError <$> v .: "msg"-			_ -> fail "invalid error"--instance Exception HsDevError+{-# LANGUAGE OverloadedStrings #-}
+
+module HsDev.Types (
+	HsDevError(..)
+	) where
+
+import Control.Exception
+import Control.DeepSeq (NFData(..))
+import Data.Aeson
+import Data.Aeson.Types (Pair, Parser)
+import Data.Typeable
+import Data.Text (Text)
+import Text.Format
+
+import HsDev.Symbols.Location
+import System.Directory.Paths
+
+-- | hsdev exception type
+data HsDevError =
+	ModuleNotSource ModuleLocation |
+	BrowseNoModuleInfo String |
+	FileNotFound Path |
+	ToolNotFound String |
+	ProjectNotFound Text |
+	PackageNotFound Text |
+	ToolError String String |
+	NotInspected ModuleLocation |
+	InspectError String |
+	InspectCabalError FilePath String |
+	IOFailed String |
+	GhcError String |
+	RequestError String String |
+	ResponseError String String |
+	SQLiteError String |
+	OtherError String |
+	UnhandledError String
+		deriving (Typeable)
+
+instance NFData HsDevError where
+	rnf (ModuleNotSource mloc) = rnf mloc
+	rnf (BrowseNoModuleInfo m) = rnf m
+	rnf (FileNotFound f) = rnf f
+	rnf (ToolNotFound t) = rnf t
+	rnf (ProjectNotFound p) = rnf p
+	rnf (PackageNotFound p) = rnf p
+	rnf (ToolError t e) = rnf t `seq` rnf e
+	rnf (NotInspected mloc) = rnf mloc
+	rnf (InspectError e) = rnf e
+	rnf (InspectCabalError c e) = rnf c `seq` rnf e
+	rnf (IOFailed e) = rnf e
+	rnf (GhcError e) = rnf e
+	rnf (RequestError e r) = rnf e `seq` rnf r
+	rnf (ResponseError e r) = rnf e `seq` rnf r
+	rnf (SQLiteError e) = rnf e
+	rnf (OtherError e) = rnf e
+	rnf (UnhandledError e) = rnf e
+
+instance Show HsDevError where
+	show (ModuleNotSource mloc) = format "module is not source: {}" ~~ show mloc
+	show (BrowseNoModuleInfo m) = format "can't find module info for {}" ~~ m
+	show (FileNotFound f) = format "file '{}' not found" ~~ f
+	show (ToolNotFound t) = format "tool '{}' not found" ~~ t
+	show (ProjectNotFound p) = format "project '{}' not found" ~~ p
+	show (PackageNotFound p) = format "package '{}' not found" ~~ p
+	show (ToolError t e) = format "tool '{}' failed: {}" ~~ t ~~ e
+	show (NotInspected mloc) = "module not inspected: {}" ~~ show mloc
+	show (InspectError e) = format "failed to inspect: {}" ~~ e
+	show (InspectCabalError c e) = format "failed to inspect cabal {}: {}" ~~ c ~~ e
+	show (IOFailed e) = format "io exception: {}" ~~ e
+	show (GhcError e) = format "ghc exception: {}" ~~ e
+	show (RequestError e r) = format "request error: {}, request: {}" ~~ e ~~ r
+	show (ResponseError e r) = format "response error: {}, response: {}" ~~ e ~~ r
+	show (SQLiteError e) = format "sqlite error: {}" ~~ e
+	show (OtherError e) = e
+	show (UnhandledError e) = e
+
+instance Formattable HsDevError where
+
+jsonErr :: String -> [Pair] -> Value
+jsonErr e = object . (("error" .= e) :)
+
+instance ToJSON HsDevError where
+	toJSON (ModuleNotSource mloc) = jsonErr "module is not source" ["module" .= mloc]
+	toJSON (BrowseNoModuleInfo m) = jsonErr "no module info" ["module" .= m]
+	toJSON (FileNotFound f) = jsonErr "file not found" ["file" .= f]
+	toJSON (ToolNotFound t) = jsonErr "tool not found" ["tool" .= t]
+	toJSON (ProjectNotFound p) = jsonErr "project not found" ["project" .= p]
+	toJSON (PackageNotFound p) = jsonErr "package not found" ["package" .= p]
+	toJSON (ToolError t e) = jsonErr "tool error" ["tool" .= t, "msg" .= e]
+	toJSON (NotInspected mloc) = jsonErr "module not inspected" ["module" .= mloc]
+	toJSON (InspectError e) = jsonErr "inspect error" ["msg" .= e]
+	toJSON (InspectCabalError c e) = jsonErr "inspect cabal error" ["cabal" .= c, "msg" .= e]
+	toJSON (IOFailed e) = jsonErr "io error" ["msg" .= e]
+	toJSON (GhcError e) = jsonErr "ghc error" ["msg" .= e]
+	toJSON (RequestError e r) = jsonErr "request error" ["msg" .= e, "request" .= r]
+	toJSON (ResponseError e r) = jsonErr "response error" ["msg" .= e, "response" .= r]
+	toJSON (SQLiteError e) = jsonErr "sqlite error" ["msg" .= e]
+	toJSON (OtherError e) = jsonErr "other error" ["msg" .= e]
+	toJSON (UnhandledError e) = jsonErr "unhandled error" ["msg" .= e]
+
+instance FromJSON HsDevError where
+	parseJSON = withObject "hsdev-error" $ \v -> do
+		err <- v .: "error" :: Parser String
+		case err of
+			"module is not source" -> ModuleNotSource <$> v .: "module"
+			"no module info" -> BrowseNoModuleInfo <$> v .: "module"
+			"file not found" -> FileNotFound <$> v .: "file"
+			"tool not found" -> ToolNotFound <$> v .: "tool"
+			"project not found" -> ProjectNotFound <$> v .: "project"
+			"package not found" -> PackageNotFound <$> v .: "package"
+			"tool error" -> ToolError <$> v .: "tool" <*> v .: "msg"
+			"module not inspected" -> NotInspected <$> v .: "module"
+			"inspect error" -> InspectError <$> v .: "msg"
+			"inspect cabal error" -> InspectCabalError <$> v .: "cabal" <*> v .: "msg"
+			"io error" -> IOFailed <$> v .: "msg"
+			"ghc error" -> GhcError <$> v .: "msg"
+			"request error" -> RequestError <$> v .: "msg" <*> v .: "request"
+			"response error" -> ResponseError <$> v .: "msg" <*> v .: "response"
+			"sqlite error" -> SQLiteError <$> v .: "msg"
+			"other error" -> OtherError <$> v .: "msg"
+			"unhandled error" -> UnhandledError <$> v .: "msg"
+			_ -> fail "invalid error"
+
+instance Exception HsDevError
src/HsDev/Util.hs view
@@ -1,340 +1,309 @@-{-# LANGUAGE FlexibleContexts, OverloadedStrings, TemplateHaskell, CPP #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module HsDev.Util (-	withCurrentDirectory,-	directoryContents,-	traverseDirectory, searchPath,-	isParent,-	haskellSource,-	cabalFile,-	-- * String utils-	tab, tabs, trim, split,-	-- * Other utils-	ordNub, uniqueBy, mapBy,-	-- * Helper-	(.::), (.::?), (.::?!), objectUnion, jsonUnion, noNulls,-	-- * Exceptions-	liftException, liftE, liftEIO, tries, triesMap, liftExceptionM, liftIOErrors,-	eitherT,-	liftThrow,-	-- * UTF-8-	fromUtf8, toUtf8, readFileUtf8, writeFileUtf8,-	-- * IO-	hGetLineBS, logException, logIO, ignoreIO, logAsync,-	-- * Async-	liftAsync,-	-- * Command line-	FromCmd(..),-	cmdJson, withCmd, guardCmd,-	withHelp, cmd, parseArgs,-	-- * Version stuff-	version, cutVersion, sameVersion, strVersion,--	-- * Reexportss-	module Control.Monad.Except,-	MonadIO(..)-	) where--import Control.Applicative-import Control.Arrow (second, left, (&&&))-import Control.Exception-import Control.Concurrent.Async-import Control.Monad-import Control.Monad.Except-import qualified Control.Monad.Catch as C-import Data.Aeson hiding (Result(..), Error)-import qualified Data.Aeson.Types as A-import Data.Char (isSpace)-import Data.List (isPrefixOf, unfoldr, intercalate)-import qualified Data.Map as M-import Data.Maybe (catMaybes, fromMaybe)-import Data.Monoid ((<>))-import qualified Data.Set as Set-import qualified Data.HashMap.Strict as HM (HashMap, toList, union)-import qualified Data.ByteString.Char8 as B-import Data.ByteString.Lazy (ByteString)-import qualified Data.ByteString.Lazy.Char8 as L-import Data.Text (Text)-import qualified Data.Text.Lazy as T-import qualified Data.Text.Lazy.Encoding as T-import Options.Applicative-import qualified System.Directory as Dir-import System.FilePath-import System.IO-import Text.Read (readMaybe)--#if !MIN_VERSION_directory(1,2,6)-#if mingw32_HOST_OS-import qualified System.Win32 as Win32-import Data.Bits ((.&.))-#else-import qualified System.Posix as Posix-#endif-#endif--import HsDev.Version---- | Run action with current directory set-withCurrentDirectory :: (MonadIO m, C.MonadMask m) => FilePath -> m a -> m a-withCurrentDirectory cur act = C.bracket (liftIO Dir.getCurrentDirectory) (liftIO . Dir.setCurrentDirectory) $-	const (liftIO (Dir.setCurrentDirectory cur) >> act)---- | Is directory symbolic link-dirIsSymLink :: FilePath -> IO Bool-#if MIN_VERSION_directory(1,3,0)-dirIsSymLink = Dir.pathIsSymbolicLink-#elif MIN_VERSION_directory(1,2,6)-dirIsSymLink = Dir.isSymbolicLink-#else-dirIsSymLink path = do-#if mingw32_HOST_OS-	isReparsePoint <$> Win32.getFileAttributes path-	where-		fILE_ATTRIBUTE_REPARSE_POINT = 0x400-		isReparsePoint attr = attr .&. fILE_ATTRIBUTE_REPARSE_POINT /= 0-#else-	Posix.isSymbolicLink <$> Posix.getSymbolicLinkStatus path-#endif-#endif---- | Get directory contents safely: no fail, ignoring symbolic links, also prepends paths with dir name-directoryContents :: FilePath -> IO [FilePath]-directoryContents p = handle ignore $ do-	b <- Dir.doesDirectoryExist p-	isLink <- dirIsSymLink p-	if b && (not isLink)-		then liftM (map (p </>) . filter (`notElem` [".", ".."])) (Dir.getDirectoryContents p)-		else return []-	where-		ignore :: SomeException -> IO [FilePath]-		ignore _ = return []---- | Collect all file names in directory recursively-traverseDirectory :: FilePath -> IO [FilePath]-traverseDirectory path = handle onError $ do-	cts <- directoryContents path-	liftM concat $ forM cts $ \c -> do-		isDir <- Dir.doesDirectoryExist c-		if isDir-			then (c :) <$> traverseDirectory c-			else return [c]-	where-		onError :: IOException -> IO [FilePath]-		onError _ = return []---- | Search something up-searchPath :: (MonadIO m, MonadPlus m) => FilePath -> (FilePath -> m a) -> m a-searchPath path f = do-	path' <- liftIO $ Dir.canonicalizePath path-	isDir <- liftIO $ Dir.doesDirectoryExist path'-	search' (if isDir then path' else takeDirectory path')-	where-		search' dir-			| isDrive dir = f dir-			| otherwise = f dir `mplus` search' (takeDirectory dir)---- | Is one path parent of another-isParent :: FilePath -> FilePath -> Bool-isParent dir file = norm dir `isPrefixOf` norm file where-	norm = dropDot . splitDirectories . normalise-	dropDot ("." : chs) = chs-	dropDot chs = chs---- | Is haskell source?-haskellSource :: FilePath -> Bool-haskellSource f = takeExtension f `elem` [".hs", ".lhs"]---- | Is cabal file?-cabalFile :: FilePath -> Bool-cabalFile f = takeExtension f == ".cabal"---- | Add N tabs to line-tab :: Int -> String -> String-tab n s = replicate n '\t' ++ s---- | Add N tabs to multiline-tabs :: Int -> String -> String-tabs n = unlines . map (tab n) . lines---- | Trim string-trim :: String -> String-trim = p . p where-	p = reverse . dropWhile isSpace---- | Split list-split :: (a -> Bool) -> [a] -> [[a]]-split p = takeWhile (not . null) . unfoldr (Just . second (drop 1) . break p)---- | nub is quadratic, https://github.com/nh2/haskell-ordnub/#ordnub-ordNub :: Ord a => [a] -> [a]-ordNub = go Set.empty where-	go _ [] = []-	go s (x:xs)-		| x `Set.member` s = go s xs-		| otherwise = x : go (Set.insert x s) xs--uniqueBy :: Ord b => (a -> b) -> [a] -> [a]-uniqueBy f = M.elems . mapBy f--mapBy :: Ord b => (a -> b) -> [a] -> M.Map b a-mapBy f = M.fromList . map (f &&& id)---- | Workaround, sometimes we get HM.lookup "foo" v == Nothing, but lookup "foo" (HM.toList v) == Just smth-(.::) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser a-v .:: name = maybe (fail $ "key " ++ show name ++ " not present") parseJSON $ lookup name $ HM.toList v---- | Returns @Nothing@ when key doesn't exist or value is @Null@-(.::?) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser (Maybe a)-v .::? name = fmap join $ traverse parseJSON $ lookup name $ HM.toList v---- | Same as @.::?@ for list, returns empty list for non-existant key or @Null@ value-(.::?!) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser [a]-v .::?! name = fromMaybe [] <$> (v .::? name)---- | Union two JSON objects-objectUnion :: Value -> Value -> Value-objectUnion (Object l) (Object r) = Object $ HM.union l r-objectUnion (Object l) _ = Object l-objectUnion _ (Object r) = Object r-objectUnion _ _ = Null---- | Union two JSON objects-jsonUnion :: (ToJSON a, ToJSON b) => a -> b -> Value-jsonUnion x y = objectUnion (toJSON x) (toJSON y)---- | No Nulls in JSON object-noNulls :: [A.Pair] -> [A.Pair]-noNulls = filter (not . isNull . snd) where-	isNull Null = True-	isNull v = v == A.emptyArray || v == A.emptyObject---- | Lift IO exception to ExceptT-liftException :: C.MonadCatch m => m a -> ExceptT String m a-liftException = ExceptT . liftM (left $ \(SomeException e) -> displayException e) . C.try---- | Same as @liftException@-liftE :: C.MonadCatch m => m a -> ExceptT String m a-liftE = liftException---- | @liftE@ for IO-liftEIO :: (C.MonadCatch m, MonadIO m) => IO a -> ExceptT String m a-liftEIO = liftE . liftIO---- | Run actions ignoring errors-tries :: MonadPlus m => [m a] -> m [a]-tries acts = liftM catMaybes $ sequence [liftM Just act `mplus` return Nothing | act <- acts]--triesMap :: MonadPlus m => (a -> m b) -> [a] -> m [b]-triesMap f = tries . map f---- | Lift IO exception to MonadError-liftExceptionM :: (C.MonadCatch m, MonadError String m) => m a -> m a-liftExceptionM act = C.catch act onError where-	onError = throwError . (\(SomeException e) -> displayException e)---- | Lift IO exceptions to ExceptT-liftIOErrors :: C.MonadCatch m => ExceptT String m a -> ExceptT String m a-liftIOErrors act = liftException (runExceptT act) >>= either throwError return--eitherT :: MonadError String m => Either String a -> m a-eitherT = either throwError return---- | Throw error as exception-liftThrow :: (Show e, MonadError e m, C.MonadCatch m) => m a -> m a-liftThrow act = catchError act (C.throwM . userError . show)--fromUtf8 :: ByteString -> String-fromUtf8 = T.unpack . T.decodeUtf8--toUtf8 :: String -> ByteString-toUtf8 = T.encodeUtf8 . T.pack---- | Read file in UTF8-readFileUtf8 :: FilePath -> IO String-readFileUtf8 f = withFile f ReadMode $ \h -> do-	hSetEncoding h utf8-	cts <- hGetContents h-	length cts `seq` return cts--writeFileUtf8 :: FilePath -> String -> IO ()-writeFileUtf8 f cts = withFile f WriteMode $ \h -> do-	hSetEncoding h utf8-	hPutStr h cts--hGetLineBS :: Handle -> IO ByteString-hGetLineBS = fmap L.fromStrict . B.hGetLine--logException :: String -> (String -> IO ()) -> IO () -> IO ()-logException pre out = handle onErr where-	onErr :: SomeException -> IO ()-	onErr e = out $ pre ++ displayException e--logIO :: C.MonadCatch m => String -> (String -> m ()) -> m () -> m ()-logIO pre out = flip C.catch (onIO out) where-	onIO :: (String -> a) -> IOException -> a-	onIO out' e = out' $ pre ++ displayException e--logAsync :: (MonadIO m, C.MonadCatch m) => (String -> m ()) -> m () -> m ()-logAsync out = flip C.catch (onAsync out) where-	onAsync :: (MonadIO m, C.MonadThrow m) => (String -> m ()) -> AsyncException -> m ()-	onAsync out' e = out' (displayException e) >> C.throwM e--ignoreIO :: C.MonadCatch m => m () -> m ()-ignoreIO = C.handle ignore' where-	ignore' :: Monad m => IOException -> m ()-	ignore' _ = return ()--liftAsync :: (C.MonadThrow m, C.MonadCatch m, MonadIO m) => IO (Async a) -> ExceptT String m a-liftAsync = liftExceptionM . ExceptT . liftIO . liftM (left displayException) . join . liftM waitCatch--class FromCmd a where-	cmdP :: Parser a--cmdJson :: String -> [A.Pair] -> Value-cmdJson nm ps = object $ ("command" .= nm) : ps--withCmd :: String -> (Object -> A.Parser a) -> Value -> A.Parser a-withCmd nm fn = withObject ("command " ++ nm) $ \v -> guardCmd nm v *> fn v--guardCmd :: String -> Object -> A.Parser ()-guardCmd nm obj = do-	cmdName <- obj .:: "command"-	guard (nm == cmdName)---- | Add help command to parser-withHelp :: Parser a -> Parser a-withHelp = (helper' <*>) where-	helper' = abortOption ShowHelpText $ long "help" <> short '?' <> help "show help" <> hidden---- | Subcommand-cmd :: String -> String -> Parser a -> Mod CommandFields a-cmd n d p = command n (info (withHelp p) (progDesc d))---- | Parse arguments or return help-parseArgs :: String -> ParserInfo a -> [String] -> Either String a-parseArgs nm p = handle' . execParserPure (prefs mempty) (p { infoParser = withHelp (infoParser p) }) where-	handle' :: ParserResult a -> Either String a-	handle' (Success r) = Right r-	handle' (Failure f) = Left $ fst $ renderFailure f nm-	handle' _ = Left "error: completion invoked result"---- instance Log.MonadLog m => Log.MonadLog (ExceptT e m) where--- 	askLog = lift Log.askLog---- | Get hsdev version as list of integers-version :: Maybe [Int]-version = mapM readMaybe $ split (== '.') $cabalVersion---- | Cut version to contain only significant numbers (currently 3)-cutVersion :: Maybe [Int] -> Maybe [Int]-cutVersion = fmap (take 3)---- | Check if version is the same-sameVersion :: Maybe [Int] -> Maybe [Int] -> Bool-sameVersion l r = fromMaybe False $ liftA2 (==) l r---- | Version to string-strVersion :: Maybe [Int] -> String-strVersion Nothing = "unknown"-strVersion (Just vers) = intercalate "." $ map show vers+{-# LANGUAGE FlexibleContexts, OverloadedStrings, TemplateHaskell, CPP #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Util (
+	withCurrentDirectory,
+	directoryContents,
+	traverseDirectory, searchPath,
+	haskellSource,
+	cabalFile,
+	-- * String utils
+	tab,
+	trim, split,
+	-- * Other utils
+	ordNub, uniqueBy, mapBy,
+	-- * Helper
+	(.::), (.::?), (.::?!), objectUnion, noNulls, fromJSON',
+	-- * Exceptions
+	liftException, liftE, tries, triesMap, liftExceptionM, liftIOErrors,
+	logAll,
+	-- * UTF-8
+	fromUtf8, toUtf8, readFileUtf8, writeFileUtf8,
+	-- * IO
+	hGetLineBS, logIO, ignoreIO, logAsync,
+	-- * Command line
+	FromCmd(..),
+	cmdJson, guardCmd,
+	withHelp, cmd, parseArgs,
+	-- * Version stuff
+	version,
+	-- * Parse
+	parseDT,
+
+	-- * Reexportss
+	module Control.Monad.Except,
+	MonadIO(..)
+	) where
+
+import Control.Arrow (second, left, (&&&))
+import Control.Exception
+import Control.DeepSeq
+import Control.Monad
+import Control.Monad.Except
+import qualified Control.Monad.Catch as C
+import Data.Aeson hiding (Result(..), Error)
+import qualified Data.Aeson.Types as A
+import Data.Char (isSpace)
+import Data.List (unfoldr)
+import qualified Data.Map.Strict as M
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Monoid ((<>))
+import qualified Data.Set as Set
+import qualified Data.HashMap.Strict as HM (HashMap, toList, union)
+import qualified Data.ByteString.Char8 as B
+import Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Text (Text)
+import qualified Data.Text.IO as ST
+import qualified Data.Text.Lazy as T
+import qualified Data.Text.Lazy.Encoding as T
+import Distribution.Text (simpleParse)
+import qualified Distribution.Text (Text)
+import Options.Applicative
+import qualified System.Directory as Dir
+import System.FilePath
+import System.Log.Simple
+import System.IO
+import Text.Format
+import Text.Read (readMaybe)
+
+#if !MIN_VERSION_directory(1,2,6)
+#if mingw32_HOST_OS
+import qualified System.Win32 as Win32
+import Data.Bits ((.&.))
+#else
+import qualified System.Posix as Posix
+#endif
+#endif
+
+import HsDev.Version
+
+-- | Run action with current directory set
+withCurrentDirectory :: (MonadIO m, C.MonadMask m) => FilePath -> m a -> m a
+withCurrentDirectory cur act = C.bracket (liftIO Dir.getCurrentDirectory) (liftIO . Dir.setCurrentDirectory) $
+	const (liftIO (Dir.setCurrentDirectory cur) >> act)
+
+-- | Is directory symbolic link
+dirIsSymLink :: FilePath -> IO Bool
+#if MIN_VERSION_directory(1,3,0)
+dirIsSymLink = Dir.pathIsSymbolicLink
+#elif MIN_VERSION_directory(1,2,6)
+dirIsSymLink = Dir.isSymbolicLink
+#else
+dirIsSymLink path = do
+#if mingw32_HOST_OS
+	isReparsePoint <$> Win32.getFileAttributes path
+	where
+		fILE_ATTRIBUTE_REPARSE_POINT = 0x400
+		isReparsePoint attr = attr .&. fILE_ATTRIBUTE_REPARSE_POINT /= 0
+#else
+	Posix.isSymbolicLink <$> Posix.getSymbolicLinkStatus path
+#endif
+#endif
+
+-- | Get directory contents safely: no fail, ignoring symbolic links, also prepends paths with dir name
+directoryContents :: FilePath -> IO [FilePath]
+directoryContents p = handle ignore $ do
+	b <- Dir.doesDirectoryExist p
+	isLink <- dirIsSymLink p
+	if b && (not isLink)
+		then liftM (map (p </>) . filter (`notElem` [".", ".."])) (Dir.getDirectoryContents p)
+		else return []
+	where
+		ignore :: SomeException -> IO [FilePath]
+		ignore _ = return []
+
+-- | Collect all file names in directory recursively
+traverseDirectory :: FilePath -> IO [FilePath]
+traverseDirectory p = handle onError $ do
+	cts <- directoryContents p
+	liftM concat $ forM cts $ \c -> do
+		isDir <- Dir.doesDirectoryExist c
+		if isDir
+			then (c :) <$> traverseDirectory c
+			else return [c]
+	where
+		onError :: IOException -> IO [FilePath]
+		onError _ = return []
+
+-- | Search something up
+searchPath :: (MonadIO m, MonadPlus m) => FilePath -> (FilePath -> m a) -> m a
+searchPath p f = do
+	p' <- liftIO $ Dir.canonicalizePath p
+	isDir <- liftIO $ Dir.doesDirectoryExist p'
+	search' (if isDir then p' else takeDirectory p')
+	where
+		search' dir
+			| isDrive dir = f dir
+			| otherwise = f dir `mplus` search' (takeDirectory dir)
+
+-- | Is haskell source?
+haskellSource :: FilePath -> Bool
+haskellSource f = takeExtension f `elem` [".hs", ".lhs"]
+
+-- | Is cabal file?
+cabalFile :: FilePath -> Bool
+cabalFile f = takeExtension f == ".cabal"
+
+-- | Add N tabs to line
+tab :: Int -> String -> String
+tab n s = replicate n '\t' ++ s
+
+-- | Trim string
+trim :: String -> String
+trim = p . p where
+	p = reverse . dropWhile isSpace
+
+-- | Split list
+split :: (a -> Bool) -> [a] -> [[a]]
+split p = takeWhile (not . null) . unfoldr (Just . second (drop 1) . break p)
+
+-- | nub is quadratic, https://github.com/nh2/haskell-ordnub/#ordnub
+ordNub :: Ord a => [a] -> [a]
+ordNub = go Set.empty where
+	go _ [] = []
+	go s (x:xs)
+		| x `Set.member` s = go s xs
+		| otherwise = x : go (Set.insert x s) xs
+
+uniqueBy :: Ord b => (a -> b) -> [a] -> [a]
+uniqueBy f = M.elems . mapBy f
+
+mapBy :: Ord b => (a -> b) -> [a] -> M.Map b a
+mapBy f = M.fromList . map (f &&& id)
+
+-- | Workaround, sometimes we get HM.lookup "foo" v == Nothing, but lookup "foo" (HM.toList v) == Just smth
+(.::) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser a
+v .:: name = maybe (fail $ "key " ++ show name ++ " not present") parseJSON $ lookup name $ HM.toList v
+
+-- | Returns @Nothing@ when key doesn't exist or value is @Null@
+(.::?) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser (Maybe a)
+v .::? name = fmap join $ traverse parseJSON $ lookup name $ HM.toList v
+
+-- | Same as @.::?@ for list, returns empty list for non-existant key or @Null@ value
+(.::?!) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser [a]
+v .::?! name = fromMaybe [] <$> (v .::? name)
+
+-- | Union two JSON objects
+objectUnion :: Value -> Value -> Value
+objectUnion (Object l) (Object r) = Object $ HM.union l r
+objectUnion (Object l) _ = Object l
+objectUnion _ (Object r) = Object r
+objectUnion _ _ = Null
+
+-- | No Nulls in JSON object
+noNulls :: [A.Pair] -> [A.Pair]
+noNulls = filter (not . isNull . snd) where
+	isNull Null = True
+	isNull v = v == A.emptyArray || v == A.emptyObject || v == A.String ""
+
+-- | Try convert json to value
+fromJSON' :: FromJSON a => Value -> Maybe a
+fromJSON' v = case fromJSON v of
+	A.Success r -> Just r
+	_ -> Nothing
+
+-- | Lift IO exception to ExceptT
+liftException :: C.MonadCatch m => m a -> ExceptT String m a
+liftException = ExceptT . liftM (left $ \(SomeException e) -> displayException e) . C.try
+
+-- | Same as @liftException@
+liftE :: C.MonadCatch m => m a -> ExceptT String m a
+liftE = liftException
+
+-- | Run actions ignoring errors
+tries :: MonadPlus m => [m a] -> m [a]
+tries acts = liftM catMaybes $ sequence [liftM Just act `mplus` return Nothing | act <- acts]
+
+triesMap :: MonadPlus m => (a -> m b) -> [a] -> m [b]
+triesMap f = tries . map f
+
+-- | Lift IO exception to MonadError
+liftExceptionM :: (C.MonadCatch m, MonadError String m) => m a -> m a
+liftExceptionM act = C.catch act onError where
+	onError = throwError . (\(SomeException e) -> displayException e)
+
+-- | Lift IO exceptions to ExceptT
+liftIOErrors :: C.MonadCatch m => ExceptT String m a -> ExceptT String m a
+liftIOErrors act = liftException (runExceptT act) >>= either throwError return
+
+-- | Log exceptions and ignore
+logAll :: (MonadLog m, C.MonadCatch m) => m () -> m ()
+logAll = C.handleAll logExc' where
+	logExc' e = sendLog Warning $ "exception: {}" ~~ displayException e
+
+fromUtf8 :: ByteString -> String
+fromUtf8 = T.unpack . T.decodeUtf8
+
+toUtf8 :: String -> ByteString
+toUtf8 = T.encodeUtf8 . T.pack
+
+-- | Read file in UTF8
+readFileUtf8 :: FilePath -> IO Text
+readFileUtf8 f = withFile f ReadMode $ \h -> do
+	hSetEncoding h utf8
+	cts <- ST.hGetContents h
+	cts `deepseq` return cts
+
+writeFileUtf8 :: FilePath -> Text -> IO ()
+writeFileUtf8 f cts = withFile f WriteMode $ \h -> do
+	hSetEncoding h utf8
+	ST.hPutStr h cts
+
+hGetLineBS :: Handle -> IO ByteString
+hGetLineBS = fmap L.fromStrict . B.hGetLine
+
+logIO :: C.MonadCatch m => String -> (String -> m ()) -> m () -> m ()
+logIO pre out = flip C.catch (onIO out) where
+	onIO :: (String -> a) -> IOException -> a
+	onIO out' e = out' $ pre ++ displayException e
+
+logAsync :: (MonadIO m, C.MonadCatch m) => (String -> m ()) -> m () -> m ()
+logAsync out = flip C.catch (onAsync out) where
+	onAsync :: (MonadIO m, C.MonadThrow m) => (String -> m ()) -> AsyncException -> m ()
+	onAsync out' e = out' (displayException e) >> C.throwM e
+
+ignoreIO :: C.MonadCatch m => m () -> m ()
+ignoreIO = C.handle ignore' where
+	ignore' :: Monad m => IOException -> m ()
+	ignore' _ = return ()
+
+class FromCmd a where
+	cmdP :: Parser a
+
+cmdJson :: String -> [A.Pair] -> Value
+cmdJson nm ps = object $ ("command" .= nm) : ps
+
+guardCmd :: String -> Object -> A.Parser ()
+guardCmd nm obj = do
+	cmdName <- obj .:: "command"
+	guard (nm == cmdName)
+
+-- | Add help command to parser
+withHelp :: Parser a -> Parser a
+withHelp = (helper' <*>) where
+	helper' = abortOption ShowHelpText $ long "help" <> short '?' <> help "show help" <> hidden
+
+-- | Subcommand
+cmd :: String -> String -> Parser a -> Mod CommandFields a
+cmd n d p = command n (info (withHelp p) (progDesc d))
+
+-- | Parse arguments or return help
+parseArgs :: String -> ParserInfo a -> [String] -> Either String a
+parseArgs nm p = handle' . execParserPure (prefs mempty) (p { infoParser = withHelp (infoParser p) }) where
+	handle' :: ParserResult a -> Either String a
+	handle' (Success r) = Right r
+	handle' (Failure f) = Left $ fst $ renderFailure f nm
+	handle' _ = Left "error: completion invoked result"
+
+-- instance Log.MonadLog m => Log.MonadLog (ExceptT e m) where
+-- 	askLog = lift Log.askLog
+
+-- | Get hsdev version as list of integers
+version :: Maybe [Int]
+version = mapM readMaybe $ split (== '.') $cabalVersion
+
+-- | Parse Distribution.Text
+parseDT :: (Monad m, Distribution.Text.Text a) => String -> String -> m a
+parseDT typeName v = maybe err return (simpleParse v) where
+	err = fail $ "Can't parse {}: {}" ~~ typeName ~~ v
src/HsDev/Watcher.hs view
@@ -1,73 +1,73 @@-module HsDev.Watcher (-	watchProject, watchModule, watchPackageDb, watchPackageDbStack,-	unwatchProject, unwatchModule, unwatchPackageDb,-	isSource, isCabal, isConf,--	module System.Directory.Watcher,-	module HsDev.Watcher.Types-	) where--import Control.Lens (view)-import Control.Monad (void)-import System.FilePath (takeDirectory, takeExtension, (</>))--import System.Directory.Watcher hiding (Watcher)-import HsDev.Project-import HsDev.Symbols-import HsDev.Watcher.Types-import HsDev.Util---- | Watch for project sources changes-watchProject :: Watcher -> Project -> [String] -> IO ()-watchProject w proj opts = do-	mapM_ (\dir -> watchTree w dir isSource (WatchedProject proj opts)) dirs-	watchDir w projDir isCabal (WatchedProject proj opts)-	where-		dirs = map ((projDir </>) . view entity) $ maybe [] sourceDirs $ view projectDescription proj-		projDir = view projectPath proj---- | Watch for standalone source-watchModule :: Watcher -> ModuleLocation -> IO ()-watchModule w (FileModule f Nothing) = watchDir w (takeDirectory f) isSource WatchedModule-watchModule w (FileModule _ (Just proj)) = watchProject w proj []-watchModule _ _ = return ()---- | Watch for top of package-db stack-watchPackageDb :: Watcher -> PackageDbStack -> [String] -> IO ()-watchPackageDb w pdbs opts = case topPackageDb pdbs of-	GlobalDb -> return () -- TODO: Watch for global package-db-	UserDb -> return () -- TODO: Watch for user package-db-	(PackageDb pdb) -> watchTree w pdb isConf (WatchedPackageDb pdbs opts)---- | Watch for package-db stack-watchPackageDbStack :: Watcher -> PackageDbStack -> [String] -> IO ()-watchPackageDbStack w pdbs opts = mapM_ (\pdbs' -> watchPackageDb w pdbs' opts) $ packageDbStacks pdbs--unwatchProject :: Watcher -> Project -> IO ()-unwatchProject w proj = do-	mapM_ (unwatchTree w) dirs-	void $ unwatchDir w projDir-	where-		dirs = map ((projDir </>) . view entity) $ maybe [] sourceDirs $ view projectDescription proj-		projDir = view projectPath proj--unwatchModule :: Watcher -> ModuleLocation -> IO ()-unwatchModule w (FileModule f Nothing) = void $ unwatchDir w (takeDirectory f)-unwatchModule _ (FileModule _ (Just _)) = return ()-unwatchModule _ (InstalledModule _ _ _) = return ()-unwatchModule _ _ = return ()---- | Unwatch package-db-unwatchPackageDb :: Watcher -> PackageDb -> IO ()-unwatchPackageDb _ GlobalDb = return () -- TODO: Unwatch global package-db-unwatchPackageDb _ UserDb = return () -- TODO: Unwatch user package-db-unwatchPackageDb w (PackageDb pdb) = void $ unwatchTree w pdb--isSource :: Event -> Bool-isSource = haskellSource . view eventPath--isCabal :: Event -> Bool-isCabal = cabalFile . view eventPath--isConf :: Event -> Bool-isConf (Event _ f _) = takeExtension f == ".conf"+module HsDev.Watcher (
+	watchProject, watchModule, watchPackageDb, watchPackageDbStack,
+	unwatchProject, unwatchModule, unwatchPackageDb,
+	isSource, isCabal, isConf,
+
+	module System.Directory.Watcher,
+	module HsDev.Watcher.Types
+	) where
+
+import Control.Lens (view)
+import System.FilePath (takeDirectory, takeExtension, (</>))
+
+import System.Directory.Watcher hiding (Watcher)
+import System.Directory.Paths
+import HsDev.Project
+import HsDev.Symbols
+import HsDev.Watcher.Types
+import HsDev.Util
+
+-- | Watch for project sources changes
+watchProject :: Watcher -> Project -> [String] -> IO ()
+watchProject w proj opts = do
+	mapM_ (\dir -> watchTree w dir isSource (WatchedProject proj opts)) dirs
+	watchDir w projDir isCabal (WatchedProject proj opts)
+	where
+		dirs = map ((projDir </>) . view (entity . path)) $ maybe [] sourceDirs $ view projectDescription proj
+		projDir = view (projectPath . path) proj
+
+-- | Watch for standalone source
+watchModule :: Watcher -> ModuleLocation -> IO ()
+watchModule w (FileModule f Nothing) = watchDir w (takeDirectory $ view path f) isSource WatchedModule
+watchModule w (FileModule _ (Just proj)) = watchProject w proj []
+watchModule _ _ = return ()
+
+-- | Watch for top of package-db stack
+watchPackageDb :: Watcher -> PackageDbStack -> [String] -> IO ()
+watchPackageDb w pdbs opts = case topPackageDb pdbs of
+	GlobalDb -> return () -- TODO: Watch for global package-db
+	UserDb -> return () -- TODO: Watch for user package-db
+	(PackageDb pdb) -> watchTree w (view path pdb) isConf (WatchedPackageDb pdbs opts)
+
+-- | Watch for package-db stack
+watchPackageDbStack :: Watcher -> PackageDbStack -> [String] -> IO ()
+watchPackageDbStack w pdbs opts = mapM_ (\pdbs' -> watchPackageDb w pdbs' opts) $ packageDbStacks pdbs
+
+unwatchProject :: Watcher -> Project -> IO ()
+unwatchProject w proj = do
+	mapM_ (unwatchTree w) dirs
+	void $ unwatchDir w projDir
+	where
+		dirs = map ((projDir </>) . view (entity . path)) $ maybe [] sourceDirs $ view projectDescription proj
+		projDir = view (projectPath . path) proj
+
+unwatchModule :: Watcher -> ModuleLocation -> IO ()
+unwatchModule w (FileModule f Nothing) = void $ unwatchDir w (takeDirectory $ view path f)
+unwatchModule _ (FileModule _ (Just _)) = return ()
+unwatchModule _ (InstalledModule _ _ _) = return ()
+unwatchModule _ _ = return ()
+
+-- | Unwatch package-db
+unwatchPackageDb :: Watcher -> PackageDb -> IO ()
+unwatchPackageDb _ GlobalDb = return () -- TODO: Unwatch global package-db
+unwatchPackageDb _ UserDb = return () -- TODO: Unwatch user package-db
+unwatchPackageDb w (PackageDb pdb) = void $ unwatchTree w (view path pdb)
+
+isSource :: Event -> Bool
+isSource = haskellSource . view eventPath
+
+isCabal :: Event -> Bool
+isCabal = cabalFile . view eventPath
+
+isConf :: Event -> Bool
+isConf (Event _ f _) = takeExtension f == ".conf"
src/System/Directory/Paths.hs view
@@ -1,31 +1,85 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}--module System.Directory.Paths (-	Paths(..),-	canonicalize,-	absolutise,-	relativise-	) where--import Control.Lens-import System.Directory-import System.FilePath---- | Something with paths inside-class Paths a where-	paths :: Traversal' a FilePath--instance Paths FilePath where-	paths = id---- | Canonicalize all paths-canonicalize :: Paths a => a -> IO a-canonicalize = paths canonicalizePath---- | Absolutise paths-absolutise :: Paths a => FilePath -> a -> a-absolutise parent = over paths (parent </>)---- | Relativise paths-relativise :: Paths a => FilePath -> a -> a-relativise parent = over paths (makeRelative parent)+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
+
+module System.Directory.Paths (
+	Path, path, fromFilePath, joinPaths, splitPaths,
+	normPath, subPath, relPathTo,
+	dirExists, fileExists, takeDir,
+	isParent,
+	Paths(..),
+	canonicalize,
+	absolutise,
+	relativise
+	) where
+
+import Control.Lens
+import Data.List
+import Data.Text (Text, pack)
+import Data.Text.Lens (unpacked)
+import System.Directory
+import System.FilePath
+
+-- | Takes much less memory than 'FilePath'
+type Path = Text
+
+path :: Lens' Path FilePath
+path = unpacked
+
+fromFilePath :: FilePath -> Path
+fromFilePath = pack
+
+joinPaths :: [Path] -> Path
+joinPaths = fromFilePath . joinPath . map (view path)
+
+splitPaths :: Path -> [Path]
+splitPaths = map fromFilePath . splitDirectories . view path
+
+normPath :: Path -> Path
+normPath = over path normalise
+
+subPath :: Path -> Path -> Path
+subPath p ch = fromFilePath (view path p </> view path ch)
+
+-- | Make path relative
+relPathTo :: Path -> Path -> Path
+relPathTo base p = fromFilePath $ makeRelative (view path base) (view path p)
+
+dirExists :: Path -> IO Bool
+dirExists = doesDirectoryExist . view path
+
+fileExists :: Path -> IO Bool
+fileExists = doesFileExist . view path
+
+takeDir :: Path -> Path
+takeDir = over path takeDirectory
+
+-- | Is one path parent of another
+isParent :: Path -> Path -> Bool
+isParent dir file = norm dir `isPrefixOf` norm file where
+	norm = dropDot . splitDirectories . normalise . view path
+	dropDot ("." : chs) = chs
+	dropDot chs = chs
+
+-- | Something with paths inside
+class Paths a where
+	paths :: Traversal' a FilePath
+
+instance Paths FilePath where
+	paths = id
+
+instance Paths Path where
+	paths = unpacked
+
+-- | Canonicalize all paths
+canonicalize :: Paths a => a -> IO a
+canonicalize = paths canonicalizePath
+
+-- | Absolutise paths
+absolutise :: Paths a => Path -> a -> a
+absolutise parent = over paths addRoot where
+	addRoot p
+		| isRelative p = (parent ^. path) </> p
+		| otherwise = p
+
+-- | Relativise paths
+relativise :: Paths a => Path -> a -> a
+relativise parent = over paths (makeRelative (parent ^. path))
src/System/Directory/Watcher.hs view
@@ -1,151 +1,174 @@-{-# LANGUAGE PatternGuards, TemplateHaskell #-}--module System.Directory.Watcher (-	EventType(..), Event(..), eventType, eventPath, eventTime,-	Watcher(..),-	withWatcher,-	watchDir, watchDir_, unwatchDir, isWatchingDir,-	watchTree, watchTree_, unwatchTree, isWatchingTree,-	-- * Working with events-	readEvent, events, onEvent-	) where--import Control.Lens (makeLenses)-import Control.Arrow-import Control.Concurrent.MVar-import Control.Concurrent.Chan-import Control.Monad-import Data.Map (Map)-import qualified Data.Map as M-import Data.Maybe (isJust)-import Data.String (fromString)-import Data.Time.Clock.POSIX-import System.FilePath (takeDirectory, isDrive)-import System.Directory-import qualified System.FSNotify as FS---- | Event type-data EventType = Added | Modified | Removed deriving (Eq, Ord, Enum, Bounded, Read, Show)---- | Event-data Event = Event {-	_eventType :: EventType,-	_eventPath :: FilePath,-	_eventTime :: POSIXTime }--makeLenses ''Event---- | Directories watcher-data Watcher a = Watcher {-	-- | Map from directory to watch stopper-	watcherDirs :: MVar (Map FilePath (Bool, IO ())),-	watcherMan :: FS.WatchManager,-	watcherChan :: Chan (a, Event) }---- | Create watcher-withWatcher :: (Watcher a -> IO b) -> IO b-withWatcher act = FS.withManager $ \man -> do-	ch <- newChan-	dirs <- newMVar M.empty-	act $ Watcher dirs man ch---- | Watch directory-watchDir :: Watcher a -> FilePath -> (Event -> Bool) -> a -> IO ()-watchDir w f p v = do-	e <- doesDirectoryExist f-	when e $ do-		f' <- canonicalizePath f-		watching <- isWatchingDir w f'-		unless watching $ do-			stop <- FS.watchDir-				(watcherMan w)-				(fromString f')-				(p . fromEvent)-				(writeChan (watcherChan w) . (,) v . fromEvent)-			modifyMVar_ (watcherDirs w) $ return . M.insert f' (False, stop)--watchDir_ :: Watcher () -> FilePath -> (Event -> Bool) -> IO ()-watchDir_ w f p = watchDir w f p ()---- | Unwatch directory, return @False@, if not watched-unwatchDir :: Watcher a -> FilePath -> IO Bool-unwatchDir w f = do-	f' <- canonicalizePath f-	stop <- modifyMVar (watcherDirs w) $ return . (M.delete f' &&& M.lookup f')-	maybe (return ()) snd stop-	return $ isJust stop---- | Check if we are watching dir-isWatchingDir :: Watcher a -> FilePath -> IO Bool-isWatchingDir w f = do-	f' <- canonicalizePath f-	dirs <- readMVar (watcherDirs w)-	return $ isWatchingDir' dirs f' || isWatchingParents' dirs f'---- | Watch directory tree-watchTree :: Watcher a -> FilePath -> (Event -> Bool) -> a -> IO ()-watchTree w f p v = do-	e <- doesDirectoryExist f-	when e $ do-		f' <- canonicalizePath f-		watching <- isWatchingTree w f'-		unless watching $ do-			stop <- FS.watchTree-				(watcherMan w)-				(fromString f')-				(p . fromEvent)-				(writeChan (watcherChan w) . (,) v . fromEvent)-			modifyMVar_ (watcherDirs w) $ return . M.insert f' (True, stop)--watchTree_ :: Watcher () -> FilePath -> (Event -> Bool) -> IO ()-watchTree_ w f p = watchTree w f p ()---- | Unwatch directory tree-unwatchTree :: Watcher a -> FilePath -> IO Bool-unwatchTree w f = do-	f' <- canonicalizePath f-	stop <- modifyMVar (watcherDirs w) $ return . (M.delete f' &&& M.lookup f')-	maybe (return ()) snd stop-	return $ isJust stop---- | Check if we are watching tree-isWatchingTree :: Watcher a -> FilePath -> IO Bool-isWatchingTree w f = do-	f' <- canonicalizePath f-	dirs <- readMVar (watcherDirs w)-	return $ isWatchingTree' dirs f' || isWatchingParents' dirs f'---- | Read next event-readEvent :: Watcher a -> IO (a, Event)-readEvent = readChan . watcherChan---- | Get lazy list of events-events :: Watcher a -> IO [(a, Event)]-events = getChanContents . watcherChan---- | Process all events-onEvent :: Watcher a -> (a -> Event -> IO ()) -> IO ()-onEvent w act = events w >>= mapM_ (uncurry act)--fromEvent :: FS.Event -> Event-fromEvent e = Event t (FS.eventPath e) (utcTimeToPOSIXSeconds $ FS.eventTime e) where-	t = case e of-		FS.Added _ _ -> Added-		FS.Modified _ _ -> Modified-		FS.Removed _ _ -> Removed--isWatchingDir' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool-isWatchingDir' m dir-	| Just (_, _) <- M.lookup dir m = True-	| isDrive dir = False-	| otherwise = isWatchingDir' m (takeDirectory dir)--isWatchingTree' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool-isWatchingTree' m dir-	| Just (True, _) <- M.lookup dir m = True-	| isDrive dir = False-	| otherwise = isWatchingTree' m (takeDirectory dir)--isWatchingParents' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool-isWatchingParents' m dir = or (map (isWatchingTree' m) parents) where-	parents = takeWhile (not . isDrive) $ iterate takeDirectory dir+{-# LANGUAGE PatternGuards, TemplateHaskell #-}
+
+module System.Directory.Watcher (
+	EventType(..), Event(..), eventType, eventPath, eventTime,
+	Watcher(..),
+	withWatcher,
+	watchDir, watchDir_, unwatchDir, isWatchingDir,
+	watchTree, watchTree_, unwatchTree, isWatchingTree,
+	-- * Working with events
+	readEvent, eventGroup, onEvents, onEvents_
+	) where
+
+import Control.Lens (makeLenses)
+import Control.Arrow
+import Control.Concurrent
+import Control.Concurrent.Async
+import Control.Concurrent.STM
+import Control.Monad
+import Data.Map.Strict (Map)
+import qualified Data.Map.Strict as M
+import Data.Maybe (isJust)
+import Data.Ratio ((%))
+import Data.String (fromString)
+import Data.Time.Clock (NominalDiffTime)
+import Data.Time.Clock.POSIX
+import System.FilePath (takeDirectory, isDrive)
+import System.Directory
+import qualified System.FSNotify as FS
+
+import HsDev.Util (uniqueBy)
+
+-- | Event type
+data EventType = Added | Modified | Removed deriving (Eq, Ord, Enum, Bounded, Read, Show)
+
+-- | Event
+data Event = Event {
+	_eventType :: EventType,
+	_eventPath :: FilePath,
+	_eventTime :: POSIXTime }
+		deriving (Eq, Ord, Show)
+
+makeLenses ''Event
+
+-- | Directories watcher
+data Watcher a = Watcher {
+	-- | Map from directory to watch stopper
+	watcherDirs :: MVar (Map FilePath (Bool, IO ())),
+	watcherMan :: FS.WatchManager,
+	watcherChan :: Chan (a, Event) }
+
+-- | Create watcher
+withWatcher :: (Watcher a -> IO b) -> IO b
+withWatcher act = FS.withManager $ \man -> do
+	ch <- newChan
+	dirs <- newMVar M.empty
+	act $ Watcher dirs man ch
+
+-- | Watch directory
+watchDir :: Watcher a -> FilePath -> (Event -> Bool) -> a -> IO ()
+watchDir w f p v = do
+	e <- doesDirectoryExist f
+	when e $ do
+		f' <- canonicalizePath f
+		watching <- isWatchingDir w f'
+		unless watching $ do
+			stop <- FS.watchDir
+				(watcherMan w)
+				(fromString f')
+				(p . fromEvent)
+				(writeChan (watcherChan w) . (,) v . fromEvent)
+			modifyMVar_ (watcherDirs w) $ return . M.insert f' (False, stop)
+
+watchDir_ :: Watcher () -> FilePath -> (Event -> Bool) -> IO ()
+watchDir_ w f p = watchDir w f p ()
+
+-- | Unwatch directory, return @False@, if not watched
+unwatchDir :: Watcher a -> FilePath -> IO Bool
+unwatchDir w f = do
+	f' <- canonicalizePath f
+	stop <- modifyMVar (watcherDirs w) $ return . (M.delete f' &&& M.lookup f')
+	maybe (return ()) snd stop
+	return $ isJust stop
+
+-- | Check if we are watching dir
+isWatchingDir :: Watcher a -> FilePath -> IO Bool
+isWatchingDir w f = do
+	f' <- canonicalizePath f
+	dirs <- readMVar (watcherDirs w)
+	return $ isWatchingDir' dirs f'
+
+-- | Watch directory tree
+watchTree :: Watcher a -> FilePath -> (Event -> Bool) -> a -> IO ()
+watchTree w f p v = do
+	e <- doesDirectoryExist f
+	when e $ do
+		f' <- canonicalizePath f
+		watching <- isWatchingTree w f'
+		unless watching $ do
+			stop <- FS.watchTree
+				(watcherMan w)
+				(fromString f')
+				(p . fromEvent)
+				(writeChan (watcherChan w) . (,) v . fromEvent)
+			modifyMVar_ (watcherDirs w) $ return . M.insert f' (True, stop)
+
+watchTree_ :: Watcher () -> FilePath -> (Event -> Bool) -> IO ()
+watchTree_ w f p = watchTree w f p ()
+
+-- | Unwatch directory tree
+unwatchTree :: Watcher a -> FilePath -> IO Bool
+unwatchTree w f = do
+	f' <- canonicalizePath f
+	stop <- modifyMVar (watcherDirs w) $ return . (M.delete f' &&& M.lookup f')
+	maybe (return ()) snd stop
+	return $ isJust stop
+
+-- | Check if we are watching tree
+isWatchingTree :: Watcher a -> FilePath -> IO Bool
+isWatchingTree w f = do
+	f' <- canonicalizePath f
+	dirs <- readMVar (watcherDirs w)
+	return $ isWatchingTree' dirs f'
+
+-- | Read next event
+readEvent :: Watcher a -> IO (a, Event)
+readEvent = readChan . watcherChan
+
+-- | Get event group
+eventGroup :: Watcher a -> NominalDiffTime -> ([(a, Event)] -> IO ()) -> IO ()
+eventGroup w tm onGroup = do
+	groupVar <- newTMVarIO []
+	syncVar <- newEmptyTMVarIO
+	_ <- async $ forever $ do
+		ev <- readChan (watcherChan w)
+		_ <- atomically $ tryPutTMVar syncVar ()
+		atomically $ do
+			evs <- takeTMVar groupVar
+			putTMVar groupVar (ev : evs)
+	forever $ do
+		_ <- atomically $ takeTMVar syncVar
+		threadDelay $ floor (tm * 1e6)
+		evs' <- atomically $ do
+			evs <- takeTMVar groupVar
+			putTMVar groupVar []
+			_ <- tryTakeTMVar syncVar
+			return $ reverse evs
+		onGroup $ uniqueBy (\(_, ev') -> (_eventType ev', _eventPath ev')) evs'
+
+-- | Process all events
+onEvents :: Watcher a -> NominalDiffTime -> ([(a, Event)] -> IO ()) -> IO ()
+onEvents = eventGroup
+
+-- | Process all events
+onEvents_ :: Watcher a -> ([(a, Event)] -> IO ()) -> IO ()
+onEvents_ w = onEvents w (fromRational (1 % 5))
+
+fromEvent :: FS.Event -> Event
+fromEvent e = Event t (FS.eventPath e) (utcTimeToPOSIXSeconds $ FS.eventTime e) where
+	t = case e of
+		FS.Added _ _ -> Added
+		FS.Modified _ _ -> Modified
+		FS.Removed _ _ -> Removed
+
+isWatchingDir' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool
+isWatchingDir' m dir
+	| Just (_, _) <- M.lookup dir m = True
+	| isDrive dir = False
+	| otherwise = isWatchingTree' m (takeDirectory dir)
+
+isWatchingTree' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool
+isWatchingTree' m dir
+	| Just (True, _) <- M.lookup dir m = True
+	| isDrive dir = False
+	| otherwise = isWatchingTree' m (takeDirectory dir)
src/System/Win32/FileMapping/NamePool.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE LambdaCase #-}
+
 module System.Win32.FileMapping.NamePool (
 	Pool(..),
 	createPool, withName
@@ -25,7 +27,7 @@ -- | Use free name from pool
 withName :: Pool -> (String -> IO a) -> IO a
 withName p = bracket getName freeName where
-	getName = modifyMVar (poolFreeNames p) $ \names -> case names of
+	getName = modifyMVar (poolFreeNames p) $ \case
 		[] -> liftM ((,) []) $ poolNewName p
-		(n:ns) -> return (ns, n)
+		(n : ns) -> return (ns, n)
 	freeName name = modifyMVar_ (poolFreeNames p) (return . (name:))
src/System/Win32/PowerShell.hs view
@@ -1,214 +1,36 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, GeneralizedNewtypeDeriving, LambdaCase #-}--module System.Win32.PowerShell (-	-- * Run PowerShell-	ps,-	-- * PowerShell functions-	startProcess, codepage, outNull,-	-- * PowerShell monad-	PS(..), seqPS, emit, emit_, (=:), invoke,-	-- * Expression-	Args(..), CmdLet(..), Expr(..), compile,-	raw, name, lit, var, bra, cbra, (.=), flag, named, call, cmdlet, lambda, (.|), foreach, filter,-	-- * Convertible-	ToPS(..),-	-- * Escape functions-	translate, translateArg,-	quote, quoteDouble,-	escape-	) where--import Prelude hiding (filter)--import Control.Monad-import Control.Monad.Writer-import Data.Char (isAlphaNum)-import Data.List (intercalate)-import qualified Data.List as List (filter)-import Data.Map (Map)-import qualified Data.Map as M--import HsDev.Tools.Base (ToolM, tool_)--ps :: PS a -> ToolM String-ps script = tool_ "powershell" ["-Command", compile $ seqPS script]---- start-process-startProcess :: String -> [String] -> CmdLet-startProcess n as = cmdlet "start-process" [name n, lit $ intercalate ", " (map translateArg as)] [named "WindowStyle" $ lit "Hidden"]---- chcp-codepage :: Int -> CmdLet-codepage n = cmdlet "chcp" [lit n] []---- out-null-outNull :: CmdLet-outNull = cmdlet "out-null" [] []--newtype PS a = PS { unPS :: Writer [Expr] a }-	deriving (Applicative, Functor, Monad, MonadWriter [Expr])--seqPS :: PS a -> Expr-seqPS (PS act) = case execWriter act of-	[] -> error "No expressions"-	es -> foldr1 Sequence es--emit :: Expr -> PS Expr-emit e = PS $ tell [e] >> return e--emit_ :: Expr -> PS ()-emit_ = void . emit--infixr 6 =:-(=:) :: String -> Expr -> PS Expr-n =: expr = emit_ (n .= expr) >> return (var n)---- | Invoke cmdlet-invoke :: CmdLet -> PS ()-invoke = emit_ . Invoke---- | Positional and named args-data Args = Args [Expr] (Map String (Maybe Expr))--instance Monoid Args where-	mempty = Args [] M.empty-	mappend (Args lp ln) (Args rp rn) = Args (lp ++ rp) (M.union ln rn)---- | Call to cmdlet-data CmdLet = CmdLet {-	cmdLet :: Expr,-	cmdArgs :: Args }---- | Expression-data Expr =-	Emit String |-	-- ^ native expression-	Literal String |-	-- ^ literal-	Var String |-	-- ^ $name-	Bracket Expr |-	-- ^ (expr)-	Assign [String] [Expr] |-	-- ^ $x, $y, ... = expr1, expr2, ...-	Invoke CmdLet |-	-- ^ cmd args... named...-	Lambda [String] ([Expr] -> Expr) |-	-- ^ { param($...); expr }-	Sequence Expr Expr |-	-- ^ expr1; expr2-	Pipe Expr CmdLet-	-- ^ expr | cmd--compile :: Expr -> String-compile (Emit s) = s-compile (Literal l) = l-compile (Var v) = '$':v-compile (Bracket e) = "(" ++ compile e ++ ")"-compile (Assign vs es) = intercalate ", " (map ('$':) vs) ++ " = " ++ intercalate ", " (map (compile . cbra) es)-compile (Invoke (CmdLet e (Args p ns))) = unwords $ List.filter (not . null) [invoke', p', ns'] where-	invoke' = case e of-		Var n -> n-		_ -> unwords ["&", compile $ cbra e]-	p' = unwords $ map (compile . cbra) p-	ns' = unwords $ concatMap named' $ M.toList ns where-		named' :: (String, Maybe Expr) -> [String]-		named' (n, Just v) = ['-':n, compile $ cbra v]-		named' (n, Nothing) = ['-':n]--- { param($File); $args[0]; }-compile (Lambda ns body) = unwords ["{", param', body', "}"] where-	param' = "param(" ++ intercalate "," ns ++ ");"-	body' = compile $ body (map var ns)-compile (Sequence l r) = compile l ++ "; " ++ compile r-compile (Pipe e c) = compile e ++ " | " ++ compile (Invoke c)--raw :: String -> Expr-raw = Emit--name :: String -> Expr-name = Literal--lit :: ToPS a => a -> Expr-lit = Literal . toPS--var :: String -> Expr-var = Var--bra :: Expr -> Expr-bra = Bracket--cbra :: Expr -> Expr-cbra e@(Literal _) = e-cbra v@(Var _) = v-cbra b = bra b--infixr 6 .= -(.=) :: String -> Expr -> Expr-v .= e = Assign [v] [e]--flag :: String -> (String, Maybe Expr)-flag n = (n, Nothing)--named :: String -> Expr -> (String, Maybe Expr)-named n e = (n, Just e)--call :: Expr -> [Expr] -> [(String, Maybe Expr)] -> CmdLet-call f pos named' = CmdLet f $ Args pos (M.fromList named')--cmdlet :: String -> [Expr] -> [(String, Maybe Expr)] -> CmdLet-cmdlet n = call (name n)--lambda :: [String] -> ([Expr] -> Expr) -> Expr-lambda = Lambda--infixr 6 .|-(.|) :: Expr -> CmdLet -> Expr-e .| c = Pipe e c--foreach :: (Expr -> Expr) -> CmdLet-foreach f = cmdlet "%" [f $ var "_"] []--filter :: (Expr -> Expr) -> CmdLet-filter p = cmdlet "?" [p $ var "_"] []--class ToPS a where-	toPS :: a -> String--instance ToPS Int where-	toPS = show--instance ToPS String where-	toPS = translate--instance ToPS Bool where-	toPS True = "$true"-	toPS False = "$false"--instance {-# OVERLAPPABLE #-} ToPS a => ToPS [a] where-	toPS = intercalate ", " . map toPS--translate :: String -> String-translate s-	| all (\ch -> isAlphaNum ch || ch `elem` "-_") s = s-	| otherwise = '"' : snd (foldr escape' (True, "\"") s)-	where-		escape' '"' (_, s') = (True, '\\' : '"' : s')-		escape' '\\' (True, s') = (True, '\\' : '\\' : s')-		escape' '\\' (False, s') = (False, '\\' : s')-		escape' c (_, s') = (False, c : s')--translateArg :: String -> String-translateArg s-	| all isAlphaNum s = s-	| otherwise = "'" ++ translate s ++ "'"--quote :: String -> String-quote s = "'" ++ concatMap (\case { '\'' -> "''"; ch -> [ch] }) s ++ "'"--quoteDouble :: String -> String-quoteDouble s = "\"" ++ concatMap (\case { '"' -> "\"\""; ch -> [ch] }) s ++ "\""--escape :: (String -> String) -> String -> String-escape fn s-	| all (\ch -> isAlphaNum ch || ch `elem` "-_") s = s-	| otherwise = fn s+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, LambdaCase #-}
+
+module System.Win32.PowerShell (
+	-- * Escape functions
+	translate, translateArg,
+	quote, quoteDouble,
+	escape
+	) where
+
+import Data.Char (isAlphaNum)
+
+translate :: String -> String
+translate s
+	| all (\ch -> isAlphaNum ch || ch `elem` "-_") s = s
+	| otherwise = '"' : snd (foldr escape' (True, "\"") s)
+	where
+		escape' '"' (_, s') = (True, '\\' : '"' : s')
+		escape' '\\' (True, s') = (True, '\\' : '\\' : s')
+		escape' '\\' (False, s') = (False, '\\' : s')
+		escape' c (_, s') = (False, c : s')
+
+translateArg :: String -> String
+translateArg s
+	| all isAlphaNum s = s
+	| otherwise = "'" ++ translate s ++ "'"
+
+quote :: String -> String
+quote s = "'" ++ concatMap (\case { '\'' -> "''"; ch -> [ch] }) s ++ "'"
+
+quoteDouble :: String -> String
+quoteDouble s = "\"" ++ concatMap (\case { '"' -> "\"\""; ch -> [ch] }) s ++ "\""
+
+escape :: (String -> String) -> String -> String
+escape fn s
+	| all (\ch -> isAlphaNum ch || ch `elem` "-_") s = s
+	| otherwise = fn s
tests/Test.hs view
@@ -1,47 +1,100 @@-{-# LANGUAGE OverloadedStrings #-}--module Main (-	main-	) where--import Control.Lens-import Control.Exception (displayException)-import Data.Aeson hiding (Error)-import Data.Aeson.Lens-import Data.Default-import HsDev-import Test.Hspec-import System.FilePath-import System.Directory--call :: Server -> Command -> IO (Maybe Value)-call srv c = do-	r <- inServer srv def c-	case r of-		Result v -> return $ Just v-		Error e -> do-			expectationFailure $ "command result error: " ++ displayException e-			return Nothing--exports :: Maybe Value -> [String]-exports v = v ^.. key "exports" . traverseArray . each . key "name" . each . _Just--main :: IO ()-main = hspec $ do-	describe "scan project" $ do-		dir <- runIO getCurrentDirectory-		s <- runIO $ startServer (def { serverSilent = True })-		it "should scan project" $ do-			_ <- call s $ Scan [dir </> "tests/test-package"] False [] [] [] [] False False-			one <- call s $ InfoResolve (dir </> "tests/test-package/ModuleOne.hs") True-			when (["test", "forkIO", "untypedFoo"] /= exports one) $-				expectationFailure "invalid exports of ModuleOne.hs"-			two <- call s $ InfoResolve (dir </> "tests/test-package/ModuleTwo.hs") True-			when (["untypedFoo", "twice", "overloadedStrings"] /= exports two) $-				expectationFailure "invalid exports of ModuleTwo.hs"-		it "should pass extensions when checkings" $ do-			checks <- call s (Check [FileSource (dir </> "tests/test-package/ModuleTwo.hs") Nothing] [])-			when (("error" :: String) `elem` (checks ^.. traverseArray . key "level" . _Just)) $-				expectationFailure "there should be no errors, only warnings"-		_ <- runIO $ call s Exit-		return ()+{-# LANGUAGE OverloadedStrings #-}
+
+module Main (
+	main
+	) where
+
+import Control.Lens
+import Control.Exception (displayException)
+import Data.Aeson hiding (Error)
+import Data.Aeson.Lens
+import Data.Default
+import Data.List
+import Data.Maybe
+import Data.String (fromString)
+import qualified Data.Set as S
+import Data.Text (unpack)
+import System.FilePath
+import System.Directory
+import Test.Hspec
+
+import HsDev
+import HsDev.Database.SQLite
+
+send :: Server -> [String] -> IO (Maybe Value)
+send srv args = do
+	r <- sendServer_ srv args
+	case r of
+		Result v -> return $ Just v
+		Error e -> do
+			expectationFailure $ "command result error: " ++ displayException e
+			return Nothing
+
+exports :: Maybe Value -> S.Set String
+exports v = mkSet (v ^.. traverseArray . key "exports" . traverseArray . key "id" . key "name" . _Just)
+
+rgn :: Maybe Value -> Maybe Region
+rgn v = Region <$> (pos (v ^. key "from")) <*> (pos (v ^. key "to")) where
+	pos :: Maybe Value -> Maybe Position
+	pos v' = Position <$> (v' ^. key "line") <*> (v' ^. key "column")
+
+mkSet :: [String] -> S.Set String
+mkSet = S.fromList
+
+main :: IO ()
+main = hspec $ do
+	describe "scan project" $ do
+		dir <- runIO getCurrentDirectory
+		s <- runIO $ startServer_ (def { serverSilent = True })
+		it "should load data" $ do
+			inserts <- liftM lines $ readFile (dir </> "tests/data/base.sql")
+			inServer s $ mapM_ (execute_ . fromString) inserts
+		it "should scan project" $ do
+			void $ send s ["scan", "--project", dir </> "tests/test-package"]
+		it "should resolve export list" $ do
+			one <- send s ["module", "ModuleOne", "--exact"]
+			exports one `shouldBe` mkSet ["test", "newChan", "untypedFoo"]
+			two <- send s ["module", "ModuleTwo", "--exact"]
+			exports two `shouldBe` mkSet ["untypedFoo", "twice", "overloadedStrings"]
+		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`)
+		it "should return types and docs" $ do
+			test' <- send s ["whois", "test", "--file", dir </> "tests/test-package/ModuleOne.hs"]
+			let
+				testType :: Maybe String
+				testType = test' ^. traverseArray . key "info" . key "type"
+			testType `shouldBe` Just "IO ()"
+		it "should infer types" $ do
+			ts <- send s ["types", "--file", dir </> "tests/test-package/ModuleOne.hs"]
+			let
+				untypedFooRgn = Region (Position 14 1) (Position 14 11)
+				exprs :: [(Region, String)]
+				exprs = do
+					note' <- ts ^.. traverseArray
+					rgn' <- maybeToList $ rgn (note' ^. key "region")
+					return (rgn', note' ^. key "note" . key "type" . _Just)
+			lookup untypedFooRgn exprs `shouldBe` Just "a -> a -> a" -- FIXME: Where's Num constraint?
+		it "should return symbol under location" $ do
+			_ <- send s ["infer", "--file", dir </> "tests/test-package/ModuleTwo.hs"]
+			who' <- send s ["whoat", "13", "15", "--file", dir </> "tests/test-package/ModuleTwo.hs"]
+			let
+				whoName :: Maybe String
+				whoName = who' ^. traverseArray . key "id" . key "name"
+				whoType :: Maybe String
+				whoType = who' ^. traverseArray . key "info" . key "type"
+			whoName `shouldBe` Just "f"
+			whoType `shouldBe` Just "a -> a"
+		it "should use modified file contents" $ do
+			modifiedContents <- fmap unpack $ readFileUtf8 $ dir </> "tests/data/ModuleTwo.modified.hs"
+			void $ send s ["set-file-contents", "--file", dir </> "tests/test-package/ModuleTwo.hs", "--contents", modifiedContents]
+			-- Call scan to wait until file updated
+			void $ send s ["scan", "--file", dir </> "tests/test-package/ModuleTwo.hs"]
+		it "should rescan module with modified contents" $ do
+			two <- send s ["module", "ModuleTwo", "--exact"]
+			exports two `shouldBe` mkSet ["untypedFoo", "twice", "overloadedStrings", "useUntypedFoo"]
+		it "should use modified source in `check` command" $ do
+			checks <- send s ["check", "--file", dir </> "tests/test-package/ModuleTwo.hs"]
+			(checks ^.. traverseArray . key "note" . key "message" . _Just) `shouldSatisfy` (any ("Defined but not used" `isPrefixOf`))
+		_ <- runIO $ send s ["exit"]
+		return ()
+ tests/data/ModuleTwo.modified.hs view
@@ -0,0 +1,25 @@+module ModuleTwo (
+	untypedFoo,
+	twice,
+	overloadedStrings,
+	useUntypedFoo
+	) where
+
+import ModuleOne (untypedFoo)
+
+import Data.String
+
+-- | 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
@@ -0,0 +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 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/ModuleOne.hs view
@@ -1,14 +1,14 @@-module ModuleOne (-	test,-	forkIO,-	untypedFoo-	) where--import Control.Concurrent (forkIO)---- | Some test function-test :: IO ()-test = return ()---- | Some function without type-untypedFoo x y = x + y+module ModuleOne (
+	test,
+	newChan,
+	untypedFoo
+	) where
+
+import Control.Concurrent.Chan (newChan)
+
+-- | Some test function
+test :: IO ()
+test = return ()
+
+-- | Some function without type
+untypedFoo x y = x + y
− tools/Tool.hs
@@ -1,48 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}
-
-module Tool (
-	-- * Tool
-	ToolM, toolMain, printExceptT, printResult, runToolClient,
-
-	ClientM,
-
-	toJSON, Value,
-	module Options.Applicative,
-	module Data.Monoid,
-	module HsDev.Util
-	) where
-
-import Control.Monad.Except (ExceptT, runExceptT)
-import Control.Monad.IO.Class
-import Control.Monad (liftM)
-import Data.Aeson
-import qualified Data.ByteString.Lazy.Char8 as L (putStrLn)
-import Data.Default
-import Data.Monoid
-import Options.Applicative
-import System.Environment
-import System.IO
-
-import HsDev.Client.Commands
-import HsDev.Server.Base
-import HsDev.Tools.Base (ToolM)
-import HsDev.Util (cmd, parseArgs)
-
--- | Run tool with commands
-toolMain :: String -> String -> Parser a -> (a -> IO ()) -> IO ()
-toolMain name d p act = do
-	hSetBuffering stdout LineBuffering
-	hSetEncoding stdout utf8
-	hSetEncoding stdin utf8
-	res <- liftM (parseArgs name (info p (progDesc d))) getArgs
-	either putStrLn act res
-
-printExceptT :: ExceptT String IO () -> IO ()
-printExceptT act = runExceptT act >>= either putStrLn return
-
-printResult :: (ToJSON a, MonadIO m) => m a -> m ()
-printResult act = act >>= liftIO . L.putStrLn . encode
-
-runToolClient :: ToJSON a => ClientM IO a -> IO ()
-runToolClient act = runServer silentOpts $
-	runClient def act >>= liftIO . L.putStrLn . encode
− tools/hsautofix.hs
@@ -1,87 +0,0 @@-{-# LANGUAGE LambdaCase #-}--module Main (-	main-	) where--import Control.Lens (preview)-import Control.Arrow ((***))-import Control.Monad (liftM)-import Control.Monad.IO.Class (liftIO)-import Control.Monad.Except (throwError)-import Data.Aeson hiding (Error)-import Data.List (partition, sort)-import Data.Maybe (mapMaybe)-import System.FilePath (normalise)-import System.Directory (canonicalizePath)-import Text.Read (readMaybe)--import System.Directory.Paths (canonicalize)-import HsDev.Symbols (moduleFile)-import HsDev.Symbols.Location (ModuleLocation(..), regionAt, Position(..))-import HsDev.Tools.Base-import HsDev.Tools.Ghc.Check (recalcNotesTabs)-import HsDev.Tools.AutoFix-import HsDev.Util (toUtf8, liftE, readFileUtf8, writeFileUtf8, ordNub)--import Tool--data FixCmd = ShowCmd Bool | FixCmd [Int] Bool--parseOutputMessages :: String -> [Note OutputMessage]-parseOutputMessages = mapMaybe parseOutputMessage . lines--parseOutputMessage :: String -> Maybe (Note OutputMessage)-parseOutputMessage s = do-	groups <- matchRx "^(.+):(\\d+):(\\d+):(\\s*(Warning|Error):)?\\s*(.*)$" s-	l <- readMaybe (groups `at` 2)-	c <- readMaybe (groups `at` 3)-	return Note {-		_noteSource = FileModule (normalise (groups `at` 1)) Nothing,-		_noteRegion = regionAt (Position l c),-		_noteLevel = Just $ if groups 5 == Just "Warning" then Warning else Error,-		_note = outputMessage $ nullToNL (groups `at` 6) }---- | Replace NULL with newline-nullToNL :: String -> String-nullToNL = map $ \case-	'\0' -> '\n'-	ch -> ch--fixP :: Parser FixCmd-fixP = subparser $ mconcat [-	cmd "show" "show what can be auto-fixed" (ShowCmd <$> switch (long "json" <> help "output messages in JSON format")),-	cmd "fix" "fix selected errors" (FixCmd <$>-		many (argument auto (metavar "N" <> help "indices of corrections to apply")) <*>-		switch (long "pure" <> help "don't modify files, just return updated corrections"))]--main :: IO ()-main = toolMain "hsautofix" "automatically fix some errors" fixP (printExceptT . printResult . go) where-	go (ShowCmd isJson) = do-		input <- liftE getContents-		msgs <- if isJson-			then maybe (throwError "Can't parse messages") return $ decode (toUtf8 input)-			else liftIO $ recalcNotesTabs (parseOutputMessages input)-		mapM (liftE . canonicalize) $ corrections msgs-	go (FixCmd ns pure') = do-		input <- liftE getContents-		corrs <- maybe (throwError "Can't parse messages") return $ decode (toUtf8 input)-		let-			check i = i `elem` ns || null ns-			(fixCorrs, upCorrs) = (map snd *** map snd) $-				partition (check . fst) $ zip [1..] corrs-		files <- liftE $ mapM canonicalizePath $ ordNub $ sort $ mapMaybe (preview $ noteSource . moduleFile) corrs-		let-			doFix :: FilePath -> Maybe String -> ([Note Correction], Maybe String)-			doFix file mcts = autoFix fixCorrs' (upCorrs', mcts) where-				findCorrs :: FilePath -> [Note Correction] -> [Note Correction]-				findCorrs f = filter ((== Just f) . preview (noteSource . moduleFile))-				fixCorrs' = findCorrs file fixCorrs-				upCorrs' = findCorrs file upCorrs-			runFix file-				| pure' = return $ fst $ doFix file Nothing-				| otherwise = do-					(corrs', Just cts') <- liftM (doFix file) $ liftE $ liftM Just $ readFileUtf8 file-					liftE $ writeFileUtf8 file cts'-					return corrs'-		liftM concat $ mapM runFix files
− tools/hscabal.hs
@@ -1,10 +0,0 @@-module Main (-	main-	) where--import HsDev.Tools.Cabal--import Tool--main :: IO ()-main = toolMain "hscabal" "cabal tool" (many (strArgument (metavar "package"))) $ printExceptT . printResult . cabalList
− tools/hsclearimports.hs
@@ -1,44 +0,0 @@-module Main (-	main-	) where--import Control.Lens (view)-import Control.Exception (finally)-import Control.Monad.Except-import System.Directory--import HsDev.Tools.ClearImports (clearImports)-import HsDev.Symbols (locateSourceDir)-import HsDev.Project (entity)--import Tool--data Opts = Opts {-	optsFile :: String,-	optsGHC :: [String],-	optsHideImportList :: Bool,-	optsMaxImportList :: Maybe Int }--opts :: Parser Opts-opts = Opts <$>-	strArgument (metavar "file" <> help "file to clear imports in") <*>-	many (strOption (long "ghc" <> short 'g' <> metavar "GHC_OPT" <> help "options for GHC")) <*>-	switch (long "hide-import-list" <> help "hide import list") <*>-	optional (option auto (long "max-import-list" <> metavar "N" <> help "hide long import lists"))--main :: IO ()-main = toolMain "hsclearimports" "clears imports in haskell source" opts $ \opts' -> do-	file <- canonicalizePath (optsFile opts')-	mroot <- liftM (fmap $ view entity) $ locateSourceDir file-	cur <- getCurrentDirectory-	flip finally (setCurrentDirectory cur) $ do-		maybe (return ()) setCurrentDirectory mroot-		void $ runExceptT $ catchError-			(clearImports (optsGHC opts') file >>= mapM_ (liftIO . putStrLn . format opts'))-			(\e -> liftIO (putStrLn $ "Error: " ++ e))-	where-		format :: Opts -> (String, String) -> String-		format as (imp, lst)-			| optsHideImportList as = imp-			| maybe False (length lst >) (optsMaxImportList as) = imp-			| otherwise = imp ++ " (" ++ lst ++ ")"
− tools/hshayoo.hs
@@ -1,22 +0,0 @@-module Main (-	main-	) where--import Data.Maybe--import HsDev.Tools.Hayoo--import Tool--data HayooOpts = HayooOpts String Int Int--hayooOpts :: Parser HayooOpts-hayooOpts = HayooOpts <$>-	strArgument (help "hayoo query") <*>-	(option auto (long "page" <> short 'p' <> help "page number (0 by default") <|> pure 0) <*>-	(option auto (long "pages" <> short 'n' <> help "pages count (1 by default") <|> pure 1)--main :: IO ()-main = toolMain "hshayoo" "hayoo search" hayooOpts $ \(HayooOpts q page pages) -> printExceptT $ printResult $-	liftM concat $ forM [page .. page + pred pages] $ \i ->-		liftM (mapMaybe hayooAsDeclaration . resultResult) $ hayoo q (Just i)
− tools/hsinspect.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE OverloadedStrings, ViewPatterns #-}--module Main (-	main-	) where--import Control.Monad (liftM, (>=>))-import Control.Monad.IO.Class-import System.Directory (canonicalizePath)-import System.FilePath (takeExtension)--import HsDev.Error-import HsDev.Inspect (inspectContents, inspectDocs, getDefines)-import HsDev.PackageDb-import HsDev.Project (readProject)-import HsDev.Scan (scanModule, scanModify)-import HsDev.Symbols.Location (ModuleLocation(..))-import HsDev.Tools.Ghc.Types (inferTypes)-import HsDev.Tools.Ghc.Worker-import HsDev.Tools.Ghc.Session (ghcSession)--import Tool--data Opts = Opts (Maybe String) [String]--opts :: Parser Opts-opts = Opts <$>-	optional (strArgument (metavar "what" <> help "depending of what <what> is, inspect installed module, source file (.hs), cabal file (.cabal) or contents, passes as input if no <what> specified")) <*>-	many (strOption (metavar "GHC_OPT" <> long "ghc" <> short 'g' <> help "options to pass to GHC"))--main :: IO ()-main = toolMain "hsinspect" "haskell inspect" opts (runToolClient . inspect') where-	inspect' :: Opts -> ClientM IO Value-	inspect' (Opts Nothing ghcs) = do-		cts <- liftIO getContents-		defs <- liftIO getDefines-		liftM toJSON $ liftIO $ hsdevLift $ inspectContents "stdin" defs ghcs cts-	inspect' (Opts (Just fname@(takeExtension -> ".hs")) ghcs) = do-		fname' <- liftIO $ canonicalizePath fname-		defs <- liftIO getDefines-		im <- scanModule defs ghcs (FileModule fname' Nothing) Nothing-		ghc <- ghcWorker	-		let-			scanAdditional =-				scanModify' (\opts' _ -> liftIO . inspectDocs opts') >=>-				scanModify' (\opts' _ m -> liftIO (inWorker ghc (ghcSession opts' >> inferTypes opts' m Nothing)))-		toJSON <$> scanAdditional im-	inspect' (Opts (Just fcabal@(takeExtension -> ".cabal")) _) = do-		fcabal' <- liftIO $ canonicalizePath fcabal-		toJSON <$> liftIO (readProject fcabal')-	inspect' (Opts (Just mname) ghcs) = toJSON <$> scanModule [] ghcs (InstalledModule UserDb Nothing mname) Nothing-	scanModify' f im = scanModify f im <|> return im