packages feed

hsdev 0.1.2.5 → 0.1.3.0

raw patch · 37 files changed

+1293/−532 lines, 37 filesdep +arraydep +exceptionsdep +monad-controldep −MonadCatchIO-transformersdep −regex-posixdep −regexprdep ~ghc-modPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: array, exceptions, monad-control, regex-pcre-builtin, uniplate

Dependencies removed: MonadCatchIO-transformers, regex-posix, regexpr

Dependency ranges changed: ghc-mod

API changes (from Hackage documentation)

- Control.Concurrent.Worker: ignoreException :: MonadCatchIO m => m () -> m ()
- Control.Concurrent.Worker: sendWork :: Worker a -> a -> IO ()
- Control.Concurrent.Worker: worker :: MonadIO m => (m () -> IO ()) -> ((s -> m ()) -> m ()) -> (s -> a -> m b) -> IO (Worker a)
- Control.Concurrent.Worker: worker_ :: MonadIO m => (m () -> IO ()) -> (m () -> m ()) -> (a -> m b) -> IO (Worker a)
- HsDev.Database.Update: instance MonadCatchIO m => MonadCatchIO (UpdateDB m)
- HsDev.Tools.Base: match :: String -> String -> Maybe (Int -> Maybe String)
- HsDev.Tools.Ghc.Worker: try :: Ghc a -> Ghc (Either String a)
- HsDev.Tools.Ghc.Worker: waitGhc :: Worker (Ghc ()) -> Ghc a -> ErrorT String IO a
- HsDev.Tools.GhcMod: waitGhcMod :: Worker (GhcModT IO ()) -> GhcModT IO a -> ErrorT String IO a
+ Control.Concurrent.Task: Task :: MVar (Maybe (SomeException -> IO ())) -> MVar (Either SomeException a) -> Task a
+ Control.Concurrent.Task: TaskCancelled :: TaskException
+ Control.Concurrent.Task: TaskKilled :: TaskException
+ Control.Concurrent.Task: data Task a
+ Control.Concurrent.Task: data TaskException
+ Control.Concurrent.Task: forkTask :: IO a -> IO (Task a)
+ Control.Concurrent.Task: instance Bounded TaskException
+ Control.Concurrent.Task: instance Enum TaskException
+ Control.Concurrent.Task: instance Eq TaskException
+ Control.Concurrent.Task: instance Exception TaskException
+ Control.Concurrent.Task: instance Ord TaskException
+ Control.Concurrent.Task: instance Read TaskException
+ Control.Concurrent.Task: instance Show TaskException
+ Control.Concurrent.Task: instance Typeable TaskException
+ Control.Concurrent.Task: runTask :: (MonadCatch m, MonadIO m, MonadIO n) => (m () -> n ()) -> m a -> n (Task a)
+ Control.Concurrent.Task: taskCancel :: Task a -> IO Bool
+ Control.Concurrent.Task: taskCancelled :: Task a -> IO Bool
+ Control.Concurrent.Task: taskDone :: Task a -> IO Bool
+ Control.Concurrent.Task: taskFailed :: Task a -> IO Bool
+ Control.Concurrent.Task: taskKill :: Task a -> IO ()
+ Control.Concurrent.Task: taskResult :: Task a -> MVar (Either SomeException a)
+ Control.Concurrent.Task: taskRunning :: Task a -> IO Bool
+ Control.Concurrent.Task: taskStart :: Task a -> MVar (Maybe (SomeException -> IO ()))
+ Control.Concurrent.Task: taskStarted :: Task a -> IO Bool
+ Control.Concurrent.Task: taskStopped :: Task a -> IO Bool
+ Control.Concurrent.Task: taskWait :: Task a -> IO (Either SomeException a)
+ Control.Concurrent.Task: taskWaitStart :: Task a -> IO Bool
+ Control.Concurrent.Worker: pushTask :: (MonadCatch m, MonadIO m) => Worker m -> m a -> IO (Task a)
+ Control.Concurrent.Worker: sendTask :: (MonadCatch m, MonadIO m) => Worker m -> m a -> IO (Task a)
+ Control.Concurrent.Worker: startWorker :: MonadIO m => (m () -> IO ()) -> (m () -> m ()) -> IO (Worker m)
+ Control.Concurrent.Worker: workerRestart :: Worker m -> IO Bool
+ Control.Concurrent.Worker: workerTask :: Worker m -> MVar (Task ())
+ HsDev.Cabal: isPackageDb :: FilePath -> Bool
+ HsDev.Database: refineProject :: Database -> Project -> Maybe Project
+ HsDev.Database.Update: databaseCacheWriter :: Settings -> Database -> IO ()
+ HsDev.Database.Update: instance Monad m => MonadWriter [ModuleLocation] (UpdateDB m)
+ HsDev.Database.Update: instance MonadCatch m => MonadCatch (UpdateDB m)
+ HsDev.Database.Update: instance MonadThrow m => MonadThrow (UpdateDB m)
+ HsDev.Project: instance Eq a => Eq (Extensions a)
+ HsDev.Project: instance Read a => Read (Extensions a)
+ HsDev.Project: instance Show a => Show (Extensions a)
+ HsDev.Server.Message: ResultPart :: Value -> ResultPart
+ HsDev.Server.Message: data ResultPart
+ HsDev.Server.Message: instance FromJSON ResultPart
+ HsDev.Server.Message: instance ToJSON ResultPart
+ HsDev.Server.Message: resultPart :: ToJSON a => a -> Notification
+ HsDev.Symbols: ExportModule :: Text -> Export
+ HsDev.Symbols: ExportName :: (Maybe Text) -> Text -> Export
+ HsDev.Symbols: ImportList :: Bool -> [Text] -> ImportList
+ HsDev.Symbols: clearDefinedIn :: Module -> Module
+ HsDev.Symbols: data Export
+ HsDev.Symbols: data ImportList
+ HsDev.Symbols: decl :: Text -> DeclarationInfo -> Declaration
+ HsDev.Symbols: declarationDefined :: Declaration -> Maybe ModuleId
+ HsDev.Symbols: declarationImported :: Declaration -> Maybe [Import]
+ HsDev.Symbols: definedIn :: Declaration -> ModuleId -> Declaration
+ HsDev.Symbols: dropExternals :: Module -> Module
+ HsDev.Symbols: export :: Export -> Text
+ HsDev.Symbols: hidingList :: ImportList -> Bool
+ HsDev.Symbols: importList :: Import -> Maybe ImportList
+ HsDev.Symbols: importName :: Import -> Text
+ HsDev.Symbols: importSpec :: ImportList -> [Text]
+ HsDev.Symbols: import_ :: Text -> Import
+ HsDev.Symbols: importedModulePath :: Text -> FilePath -> Text -> FilePath
+ HsDev.Symbols: instance Eq Export
+ HsDev.Symbols: instance Eq ImportList
+ HsDev.Symbols: instance FromJSON Export
+ HsDev.Symbols: instance FromJSON ImportList
+ HsDev.Symbols: instance NFData Export
+ HsDev.Symbols: instance NFData ImportList
+ HsDev.Symbols: instance Ord Export
+ HsDev.Symbols: instance Ord ImportList
+ HsDev.Symbols: instance Show Export
+ HsDev.Symbols: instance Show ImportList
+ HsDev.Symbols: instance ToJSON Export
+ HsDev.Symbols: instance ToJSON ImportList
+ HsDev.Symbols: passImportList :: ImportList -> Text -> Bool
+ HsDev.Symbols: scopes :: Declaration -> [Maybe Text]
+ HsDev.Symbols: setDefinedIn :: Module -> Module
+ HsDev.Symbols: sortDeclarations :: [Declaration] -> [Declaration]
+ HsDev.Symbols: sourceModuleRoot :: Text -> FilePath -> FilePath
+ HsDev.Symbols: unnamedModuleId :: ModuleLocation -> ModuleId
+ HsDev.Symbols.Location: moduleCabal_ :: ModuleLocation -> Maybe Cabal
+ HsDev.Symbols.Location: moduleProject_ :: ModuleLocation -> Maybe Project
+ HsDev.Symbols.Location: moduleStandalone :: ModuleLocation -> Bool
+ HsDev.Symbols.Location: recalcTabs :: String -> Position -> Position
+ HsDev.Symbols.Resolve: ResolveM :: ReaderT Database (State ResolvedTree) a -> ResolveM a
+ HsDev.Symbols.Resolve: ResolvedModule :: Module -> [Declaration] -> [Declaration] -> ResolvedModule
+ HsDev.Symbols.Resolve: data ResolvedModule
+ HsDev.Symbols.Resolve: exported :: [Declaration] -> Export -> [Declaration]
+ HsDev.Symbols.Resolve: exportsModule :: ResolvedModule -> Module
+ HsDev.Symbols.Resolve: instance Applicative ResolveM
+ HsDev.Symbols.Resolve: instance Functor ResolveM
+ HsDev.Symbols.Resolve: instance Monad ResolveM
+ HsDev.Symbols.Resolve: instance MonadReader Database ResolveM
+ HsDev.Symbols.Resolve: instance MonadState ResolvedTree ResolveM
+ HsDev.Symbols.Resolve: mergeImported :: [Declaration] -> [Declaration]
+ HsDev.Symbols.Resolve: newtype ResolveM a
+ HsDev.Symbols.Resolve: resolve :: (Traversable t, Foldable t) => Database -> t Module -> t ResolvedModule
+ HsDev.Symbols.Resolve: resolveImport :: Module -> Import -> ResolveM [Declaration]
+ HsDev.Symbols.Resolve: resolveModule :: Module -> ResolveM ResolvedModule
+ HsDev.Symbols.Resolve: resolveOne :: Database -> Module -> ResolvedModule
+ HsDev.Symbols.Resolve: resolvedExports :: ResolvedModule -> [Declaration]
+ HsDev.Symbols.Resolve: resolvedModule :: ResolvedModule -> Module
+ HsDev.Symbols.Resolve: resolvedScope :: ResolvedModule -> [Declaration]
+ HsDev.Symbols.Resolve: resolvedTopScope :: ResolvedModule -> [Declaration]
+ HsDev.Symbols.Resolve: runResolveM :: ResolveM a -> ReaderT Database (State ResolvedTree) a
+ HsDev.Symbols.Resolve: scopeModule :: ResolvedModule -> Module
+ HsDev.Symbols.Resolve: type ResolvedTree = Map ModuleId ResolvedModule
+ HsDev.Symbols.Util: cabalOf :: ModuleId -> Maybe Cabal
+ HsDev.Symbols.Util: inDepsOf :: Info -> ModuleId -> Bool
+ HsDev.Tools.Base: matchRx :: String -> String -> Maybe (Int -> Maybe String)
+ HsDev.Tools.Base: replaceRx :: String -> String -> String -> String
+ HsDev.Tools.Base: splitRx :: String -> String -> [String]
+ HsDev.Tools.Ghc.Worker: instance MonadCatch Ghc
+ HsDev.Tools.Ghc.Worker: instance MonadThrow Ghc
+ HsDev.Tools.GhcMod: dispatch :: FilePath -> GhcModT IO a -> ReaderT WorkerMap IO (Task a)
+ HsDev.Tools.GhcMod: flags :: ErrorT String IO [String]
+ HsDev.Tools.GhcMod: instance MonadCatch (GhcModT IO)
+ HsDev.Tools.GhcMod: instance MonadThrow (GhcModT IO)
+ HsDev.Tools.GhcMod: langs :: ErrorT String IO [String]
+ HsDev.Tools.GhcMod: type WorkerMap = MVar (Map FilePath (Worker (GhcModT IO)))
+ HsDev.Util: liftTask :: MonadIO m => IO (Task a) -> ErrorT String m a
+ HsDev.Util: withCurrentDirectory :: FilePath -> IO a -> IO a
- Control.Concurrent.Worker: Worker :: (a -> IO ()) -> Chan a -> Worker a
+ Control.Concurrent.Worker: Worker :: Chan (m ()) -> MVar (Task ()) -> IO Bool -> Worker m
- Control.Concurrent.Worker: data Worker a
+ Control.Concurrent.Worker: data Worker m
- Control.Concurrent.Worker: stopWorker :: Worker a -> IO ()
+ Control.Concurrent.Worker: stopWorker :: Worker m -> IO ()
- Control.Concurrent.Worker: workerChan :: Worker a -> Chan a
+ Control.Concurrent.Worker: workerChan :: Worker m -> Chan (m ())
- HsDev.Commands: completions :: Database -> Cabal -> FilePath -> String -> ErrorT String IO [ModuleDeclaration]
+ HsDev.Commands: completions :: Database -> Cabal -> FilePath -> String -> Bool -> ErrorT String IO [ModuleDeclaration]
- HsDev.Database: filterDB :: (Module -> Bool) -> (Project -> Bool) -> Database -> Database
+ HsDev.Database: filterDB :: (ModuleId -> Bool) -> (Project -> Bool) -> Database -> Database
- HsDev.Database.Update: Settings :: Async Database -> ((FilePath -> ErrorT String IO Structured) -> IO (Maybe Database)) -> (Task -> IO ()) -> [String] -> Settings
+ HsDev.Database.Update: Settings :: Async Database -> ((FilePath -> ErrorT String IO Structured) -> IO (Maybe Database)) -> (Database -> IO ()) -> (Task -> IO ()) -> [String] -> Settings
- HsDev.Database.Update: loadCache :: (MonadIO m, MonadReader Settings m) => (FilePath -> ErrorT String IO Structured) -> m ()
+ HsDev.Database.Update: loadCache :: (MonadIO m, MonadReader Settings m, MonadWriter [ModuleLocation] m) => (FilePath -> ErrorT String IO Structured) -> m ()
- HsDev.Database.Update: scanCabal :: MonadCatchIO m => [String] -> Cabal -> ErrorT String (UpdateDB m) ()
+ HsDev.Database.Update: scanCabal :: (MonadIO m, MonadCatch m) => [String] -> Cabal -> ErrorT String (UpdateDB m) ()
- HsDev.Database.Update: scanDirectory :: MonadCatchIO m => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
+ HsDev.Database.Update: scanDirectory :: (MonadIO m, MonadCatch m) => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
- HsDev.Database.Update: scanFile :: MonadCatchIO m => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
+ HsDev.Database.Update: scanFile :: (MonadIO m, MonadCatch m) => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
- HsDev.Database.Update: scanModule :: MonadCatchIO m => [String] -> ModuleLocation -> ErrorT String (UpdateDB m) ()
+ HsDev.Database.Update: scanModule :: (MonadIO m, MonadCatch m) => [String] -> ModuleLocation -> ErrorT String (UpdateDB m) ()
- HsDev.Database.Update: scanModules :: MonadCatchIO m => [String] -> [ModuleToScan] -> ErrorT String (UpdateDB m) ()
+ HsDev.Database.Update: scanModules :: (MonadIO m, MonadCatch m) => [String] -> [ModuleToScan] -> ErrorT String (UpdateDB m) ()
- HsDev.Database.Update: scanProject :: MonadCatchIO m => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
+ HsDev.Database.Update: scanProject :: (MonadIO m, MonadCatch m) => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
- HsDev.Database.Update: scanProjectFile :: MonadCatchIO m => [String] -> FilePath -> ErrorT String (UpdateDB m) Project
+ HsDev.Database.Update: scanProjectFile :: (MonadIO m, MonadCatch m) => [String] -> FilePath -> ErrorT String (UpdateDB m) Project
- HsDev.Database.Update: updateDB :: Monad m => Settings -> ErrorT String (UpdateDB m) () -> m ()
+ HsDev.Database.Update: updateDB :: MonadIO m => Settings -> ErrorT String (UpdateDB m) () -> m ()
- HsDev.Database.Update: updater :: (MonadIO m, MonadReader Settings m) => m Database -> m ()
+ HsDev.Database.Update: updater :: (MonadIO m, MonadReader Settings m, MonadWriter [ModuleLocation] m) => m Database -> m ()
- HsDev.Server.Types: CommandOptions :: Async Database -> (Database -> IO ()) -> ((FilePath -> ErrorT String IO Structured) -> IO (Maybe Database)) -> FilePath -> (String -> IO ()) -> (([String] -> IO ()) -> IO ()) -> IO () -> Worker (Ghc ()) -> Worker (FilePath, GhcModT IO ()) -> (Notification -> IO ()) -> IO () -> IO () -> IO () -> CommandOptions
+ HsDev.Server.Types: CommandOptions :: Async Database -> (Database -> IO ()) -> ((FilePath -> ErrorT String IO Structured) -> IO (Maybe Database)) -> FilePath -> (String -> IO ()) -> (([String] -> IO ()) -> IO ()) -> IO () -> Worker Ghc -> Worker (ReaderT WorkerMap IO) -> (Notification -> IO ()) -> IO () -> IO () -> IO () -> CommandOptions
- HsDev.Server.Types: commandGhc :: CommandOptions -> Worker (Ghc ())
+ HsDev.Server.Types: commandGhc :: CommandOptions -> Worker Ghc
- HsDev.Server.Types: commandGhcMod :: CommandOptions -> Worker (FilePath, GhcModT IO ())
+ HsDev.Server.Types: commandGhcMod :: CommandOptions -> Worker (ReaderT WorkerMap IO)
- HsDev.Symbols: Declaration :: String -> Maybe String -> Maybe Position -> DeclarationInfo -> Declaration
+ HsDev.Symbols: Declaration :: Text -> Maybe ModuleId -> Maybe [Import] -> Maybe Text -> Maybe Position -> DeclarationInfo -> Declaration
- HsDev.Symbols: Function :: Maybe String -> [Declaration] -> DeclarationInfo
+ HsDev.Symbols: Function :: Maybe Text -> [Declaration] -> DeclarationInfo
- HsDev.Symbols: Import :: String -> Bool -> Maybe String -> Maybe Position -> Import
+ HsDev.Symbols: Import :: Text -> Bool -> Maybe Text -> Maybe ImportList -> Maybe Position -> Import
- HsDev.Symbols: Module :: String -> Maybe String -> ModuleLocation -> [String] -> [Import] -> Map String Declaration -> Module
+ HsDev.Symbols: Module :: Text -> Maybe Text -> ModuleLocation -> Maybe [Export] -> [Import] -> [Declaration] -> Module
- HsDev.Symbols: ModuleId :: String -> ModuleLocation -> ModuleId
+ HsDev.Symbols: ModuleId :: Text -> ModuleLocation -> ModuleId
- HsDev.Symbols: TypeInfo :: Maybe String -> [String] -> Maybe String -> TypeInfo
+ HsDev.Symbols: TypeInfo :: Maybe Text -> [Text] -> Maybe Text -> TypeInfo
- HsDev.Symbols: declarationDocs :: Declaration -> Maybe String
+ HsDev.Symbols: declarationDocs :: Declaration -> Maybe Text
- HsDev.Symbols: declarationInfo :: DeclarationInfo -> Either (Maybe String, [Declaration]) TypeInfo
+ HsDev.Symbols: declarationInfo :: DeclarationInfo -> Either (Maybe Text, [Declaration]) TypeInfo
- HsDev.Symbols: declarationName :: Declaration -> String
+ HsDev.Symbols: declarationName :: Declaration -> Text
- HsDev.Symbols: functionType :: DeclarationInfo -> Maybe String
+ HsDev.Symbols: functionType :: DeclarationInfo -> Maybe Text
- HsDev.Symbols: importAs :: Import -> Maybe String
+ HsDev.Symbols: importAs :: Import -> Maybe Text
- HsDev.Symbols: importModuleName :: Import -> String
+ HsDev.Symbols: importModuleName :: Import -> Text
- HsDev.Symbols: importQualifier :: Maybe String -> Import -> Bool
+ HsDev.Symbols: importQualifier :: Maybe Text -> Import -> Bool
- HsDev.Symbols: moduleDeclarations :: Module -> Map String Declaration
+ HsDev.Symbols: moduleDeclarations :: Module -> [Declaration]
- HsDev.Symbols: moduleDocs :: Module -> Maybe String
+ HsDev.Symbols: moduleDocs :: Module -> Maybe Text
- HsDev.Symbols: moduleExports :: Module -> [String]
+ HsDev.Symbols: moduleExports :: Module -> Maybe [Export]
- HsDev.Symbols: moduleIdName :: ModuleId -> String
+ HsDev.Symbols: moduleIdName :: ModuleId -> Text
- HsDev.Symbols: moduleLocalDeclarations :: Module -> Map String Declaration
+ HsDev.Symbols: moduleLocalDeclarations :: Module -> [Declaration]
- HsDev.Symbols: moduleName :: Module -> String
+ HsDev.Symbols: moduleName :: Module -> Text
- HsDev.Symbols: qualifiedName :: ModuleId -> Declaration -> String
+ HsDev.Symbols: qualifiedName :: ModuleId -> Declaration -> Text
- HsDev.Symbols: symbolDocs :: Symbol a => a -> Maybe String
+ HsDev.Symbols: symbolDocs :: Symbol a => a -> Maybe Text
- HsDev.Symbols: symbolName :: Symbol a => a -> String
+ HsDev.Symbols: symbolName :: Symbol a => a -> Text
- HsDev.Symbols: symbolQualifiedName :: Symbol a => a -> String
+ HsDev.Symbols: symbolQualifiedName :: Symbol a => a -> Text
- HsDev.Symbols: typeInfoArgs :: TypeInfo -> [String]
+ HsDev.Symbols: typeInfoArgs :: TypeInfo -> [Text]
- HsDev.Symbols: typeInfoContext :: TypeInfo -> Maybe String
+ HsDev.Symbols: typeInfoContext :: TypeInfo -> Maybe Text
- HsDev.Symbols: typeInfoDefinition :: TypeInfo -> Maybe String
+ HsDev.Symbols: typeInfoDefinition :: TypeInfo -> Maybe Text
- HsDev.Symbols: unalias :: Module -> String -> [String]
+ HsDev.Symbols: unalias :: Module -> Text -> [Text]
- HsDev.Symbols.Class: symbolDocs :: Symbol a => a -> Maybe String
+ HsDev.Symbols.Class: symbolDocs :: Symbol a => a -> Maybe Text
- HsDev.Symbols.Class: symbolName :: Symbol a => a -> String
+ HsDev.Symbols.Class: symbolName :: Symbol a => a -> Text
- HsDev.Symbols.Class: symbolQualifiedName :: Symbol a => a -> String
+ HsDev.Symbols.Class: symbolQualifiedName :: Symbol a => a -> Text
- HsDev.Tools.Ghc.Worker: ghcWorker :: IO (Worker (Ghc ()))
+ HsDev.Tools.Ghc.Worker: ghcWorker :: IO (Worker Ghc)
- HsDev.Tools.GhcMod: ghcModMultiWorker :: IO (Worker (FilePath, GhcModT IO ()))
+ HsDev.Tools.GhcMod: ghcModMultiWorker :: IO (Worker (ReaderT WorkerMap IO))
- HsDev.Tools.GhcMod: ghcModWorker :: Either Project Cabal -> IO (Worker (GhcModT IO ()))
+ HsDev.Tools.GhcMod: ghcModWorker :: Either Project Cabal -> IO (Worker (GhcModT IO))
- HsDev.Tools.GhcMod: info :: [String] -> Cabal -> FilePath -> Maybe Project -> String -> String -> GhcModT IO Declaration
+ HsDev.Tools.GhcMod: info :: [String] -> Cabal -> FilePath -> String -> GhcModT IO Declaration
- HsDev.Tools.GhcMod: runGhcMod :: (IOish m, MonadCatchIO m) => Options -> GhcModT m a -> ErrorT String m a
+ HsDev.Tools.GhcMod: runGhcMod :: (IOish m, MonadCatch m) => Options -> GhcModT m a -> ErrorT String m a
- HsDev.Tools.GhcMod: typeOf :: [String] -> Cabal -> FilePath -> Maybe Project -> String -> Int -> Int -> GhcModT IO [TypedRegion]
+ HsDev.Tools.GhcMod: typeOf :: [String] -> Cabal -> FilePath -> Int -> Int -> GhcModT IO [TypedRegion]
- HsDev.Tools.GhcMod: waitMultiGhcMod :: Worker (FilePath, GhcModT IO ()) -> FilePath -> GhcModT IO a -> ErrorT String IO a
+ HsDev.Tools.GhcMod: waitMultiGhcMod :: Worker (ReaderT WorkerMap IO) -> FilePath -> GhcModT IO a -> ErrorT String IO a
- HsDev.Tools.GhcMod.InferType: inferType :: [String] -> Cabal -> FilePath -> Maybe Project -> String -> Declaration -> GhcModT IO Declaration
+ HsDev.Tools.GhcMod.InferType: inferType :: [String] -> Cabal -> FilePath -> Declaration -> GhcModT IO Declaration
- HsDev.Util: liftException :: MonadCatchIO m => m a -> ErrorT String m a
+ HsDev.Util: liftException :: MonadCatch m => m a -> ErrorT String m a
- HsDev.Util: liftExceptionM :: (MonadCatchIO m, Error e, MonadError e m) => m a -> m a
+ HsDev.Util: liftExceptionM :: (MonadCatch m, Error e, MonadError e m) => m a -> m a
- HsDev.Util: liftIOErrors :: MonadCatchIO m => ErrorT String m a -> ErrorT String m a
+ HsDev.Util: liftIOErrors :: MonadCatch m => ErrorT String m a -> ErrorT String m a

Files

hsdev.cabal view
@@ -2,7 +2,7 @@ --  see http://haskell.org/cabal/users-guide/
 
 name:                hsdev
-version:             0.1.2.5
+version:             0.1.3.0
 synopsis:            Haskell development library and tool with support of autocompletion, symbol info, go to declaration, find references etc.
 description:
   Haskell development library and tool with support of autocompletion, symbol info, go to declaration, find references, hayoo search etc.
@@ -22,6 +22,7 @@   exposed-modules:
     Control.Apply.Util
     Control.Concurrent.FiniteChan
+    Control.Concurrent.Task
     Control.Concurrent.Worker
     Control.Concurrent.Util
     Data.Async
@@ -49,6 +50,7 @@     HsDev.Symbols.Class
     HsDev.Symbols.Location
     HsDev.Symbols.Documented
+    HsDev.Symbols.Resolve
     HsDev.Symbols.Util
     HsDev.Tools.Base
     HsDev.Tools.Cabal
@@ -81,34 +83,37 @@     base >= 4.7 && < 5,
     aeson >= 0.7.0,
     aeson-pretty >= 0.7.0,
+    array >= 0.5.0,
+    attoparsec >= 0.11.0,
     bytestring >= 0.10.0,
     Cabal >= 1.18.0,
     containers >= 0.5.0,
     deepseq >= 1.3.0,
     directory >= 1.2.0,
+    exceptions >= 0.6.0,
     filepath >= 1.3.0,
     ghc >= 7.8.1,
-    ghc-mod >= 5.1.0.0 && < 5.2.0.0,
+    ghc-mod >= 5.2.0.0,
     ghc-paths >= 0.1.0,
     haddock-api >= 2.15.0,
     haskell-src-exts >= 1.16.0,
     hdocs >= 0.4.0,
     HTTP >= 4000.2.0,
-    MonadCatchIO-transformers >= 0.3.0,
+    -- ghc-mod workaround
+    monad-control >= 0.3 && < 1.0,
     monad-loops >= 0.4,
     mtl >= 2.1.0,
     network >= 2.4.0,
-    transformers >= 0.3.0,
     process >= 1.2.0,
-    regexpr >= 0.5.0,
-    regex-posix >= 0.95,
+    regex-pcre-builtin >= 0.94,
     scientific >= 0.3,
-    time >= 1.4.0,
-    attoparsec >= 0.11.0,
     template-haskell,
+    text >= 1.1.0,
+    time >= 1.4.0,
+    transformers >= 0.3.0,
+    uniplate >= 1.6.0,
     unordered-containers >= 0.2.0,
-    vector >= 0.10.0,
-    text >= 1.1.0
+    vector >= 0.10.0
 
 executable hsdev
   main-is: hsdev.hs
@@ -124,13 +129,12 @@     containers >= 0.5.0,
     deepseq >= 1.3.0,
     directory >= 1.2.0,
+    exceptions >= 0.6.0,
     filepath >= 1.3.0,
     monad-loops >= 0.4.0,
-    MonadCatchIO-transformers >= 0.3.0,
     mtl >= 2.1.0,
     network >= 2.4.0,
     process >= 1.2.0,
-    unordered-containers >= 0.2.0,
     text >= 1.1.0,
     transformers >= 0.3.0,
     unordered-containers >= 0.2.0
@@ -148,6 +152,7 @@     aeson-pretty >= 0.7.0,
     bytestring >= 0.10.0,
     containers >= 0.5.0,
+    filepath >= 1.3.0,
     mtl >= 2.1.0,
     text >= 1.1.0,
     transformers >= 0.3.0,
@@ -164,10 +169,10 @@     hsdev,
     aeson >= 0.7.0,
     aeson-pretty >= 0.7.0,
+    containers >= 0.5.0,
     directory >= 1.2.0,
     ghc >= 7.8.1,
     haskell-src-exts >= 1.16.0,
-    containers >= 0.5.0,
     mtl >= 2.1.0,
     text >= 1.1.0,
     unordered-containers >= 0.2.0
+ src/Control/Concurrent/Task.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE DeriveDataTypeable #-}
+
+module Control.Concurrent.Task (
+	Task(..), TaskException(..),
+	taskStarted, taskRunning, taskStopped, taskDone, taskFailed, taskCancelled,
+	taskWaitStart, taskWait, taskKill, taskCancel,
+	runTask, forkTask
+	) where
+
+import Control.Applicative
+import Control.Concurrent
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Monad.Catch
+import Data.Either
+import Data.Maybe
+import Data.Traversable
+import Data.Typeable
+
+-- | Task result
+data Task a = Task {
+	taskStart :: MVar (Maybe (SomeException -> IO ())),
+	taskResult :: MVar (Either SomeException a) }
+
+data TaskException = TaskCancelled | TaskKilled deriving (Eq, Ord, Enum, Bounded, Read, Show, Typeable)
+
+instance Exception TaskException
+
+taskStarted :: Task a -> IO Bool
+taskStarted = fmap (maybe False isJust) . tryReadMVar . taskStart
+
+taskRunning :: Task a -> IO Bool
+taskRunning t = (&&) <$> taskStarted t <*> (not <$> taskStopped t)
+
+taskStopped :: Task a -> IO Bool
+taskStopped = fmap not . isEmptyMVar . taskResult
+
+taskDone :: Task a -> IO Bool
+taskDone = fmap (maybe False isRight) . tryReadMVar . taskResult
+
+taskFailed :: Task a -> IO Bool
+taskFailed = fmap (maybe False isLeft) . tryReadMVar . taskResult
+
+taskCancelled :: Task a -> IO Bool
+taskCancelled = fmap (maybe False isNothing) . tryReadMVar . taskStart
+
+-- | Wait until task starts or be cancelled, returns True if started
+taskWaitStart :: Task a -> IO Bool
+taskWaitStart = (`withMVar` (return . isJust)) . taskStart
+
+-- | Wait for task
+taskWait :: Task a -> IO (Either SomeException a)
+taskWait = takeMVar . taskResult
+
+-- | Kill task
+taskKill :: Task a -> IO ()
+taskKill =
+	tryTakeMVar . taskStart >=>
+	void . traverse ($ toException TaskKilled) . join
+
+-- | Cancel task if it is not started yet
+taskCancel :: Task a -> IO Bool
+taskCancel t = do
+	aborted <- tryPutMVar (taskStart t) Nothing
+	when aborted $ void $ tryPutMVar (taskResult t) (Left $ toException TaskCancelled)
+	return aborted
+
+runTask :: (MonadCatch m, MonadIO m, MonadIO n) => (m () -> n ()) -> m a -> n (Task a)
+runTask f act = do
+	throwVar <- liftIO newEmptyMVar
+	resultVar <- liftIO newEmptyMVar
+	f $ handle (liftIO . putMVar resultVar . Left) $ do
+		th <- liftIO myThreadId
+		ok <- liftIO $ tryPutMVar throwVar (Just $ throwTo th)
+		when ok $ act >>= liftIO . putMVar resultVar . Right
+	return $ Task throwVar resultVar
+
+-- | Run task in separate thread
+forkTask :: IO a -> IO (Task a)
+forkTask = runTask (void . forkIO)
src/Control/Concurrent/Worker.hs view
@@ -1,38 +1,45 @@ module Control.Concurrent.Worker (
 	Worker(..),
-	worker_, worker,
+	startWorker,
+	sendTask, pushTask,
 	stopWorker,
-	ignoreException
+
+	module Control.Concurrent.Task
 	) where
 
-import Control.Exception (SomeException)
-import Control.Monad (void)
+import Control.Concurrent.MVar
+import Control.Monad (void, when)
 import Control.Monad.IO.Class
-import Control.Monad.CatchIO
+import Control.Monad.Catch
 
-import Control.Concurrent (forkIO)
 import Control.Concurrent.FiniteChan
-
-data Worker a = Worker {
-	sendWork :: a -> IO (),
-	workerChan :: Chan a }
+import Control.Concurrent.Task
 
-worker_ :: MonadIO m => (m () -> IO ()) -> (m () -> m ()) -> (a -> m b) -> IO (Worker a)
-worker_ run initialize work = worker run initialize' work' where
-	initialize' f = initialize $ f ()
-	work' _ = work
+data Worker m = Worker {
+	workerChan :: Chan (m ()),
+	workerTask :: MVar (Task ()),
+	workerRestart :: IO Bool }
 
-worker :: MonadIO m => (m () -> IO ()) -> ((s -> m ()) -> m ()) -> (s -> a -> m b) -> IO (Worker a)
-worker run initialize work = do
+startWorker :: MonadIO m => (m () -> IO ()) -> (m () -> m ()) -> IO (Worker m)
+startWorker run initialize = do
 	ch <- newChan
-	void $ forkIO $ run $ initialize $ \s ->
-		liftIO (readChan ch) >>= mapM_ (work s)
-	return $ Worker (putChan ch) ch
+	taskVar <- newEmptyMVar
+	let
+		start = forkTask $ run $ initialize processWork
+		processWork = liftIO (getChan ch) >>= maybe (return ()) (>> processWork)
+	start >>= putMVar taskVar
+	let
+		restart = do
+			f <- readMVar taskVar >>= taskFailed
+			when f (start >>= void . swapMVar taskVar)
+			return f
+	return $ Worker ch taskVar restart
 
-stopWorker :: Worker a -> IO ()
-stopWorker = closeChan . workerChan
+sendTask :: (MonadCatch m, MonadIO m) => Worker m -> m a -> IO (Task a)
+sendTask w = runTask $ putChan (workerChan w)
 
-ignoreException :: MonadCatchIO m => m () -> m ()
-ignoreException act = catch act onError where
-	onError :: MonadCatchIO m => SomeException -> m ()
-	onError _ = return ()
+pushTask :: (MonadCatch m, MonadIO m) => Worker m -> m a -> IO (Task a)
+pushTask w act = workerRestart w >> sendTask w act
+
+stopWorker :: Worker m -> IO ()
+stopWorker = closeChan . workerChan
src/HsDev/Cabal.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE OverloadedStrings, MultiWayIf #-}
 
 module HsDev.Cabal (
 	Cabal(..), sandbox,
-	findPackageDb, locateSandbox, getSandbox,
+	isPackageDb, findPackageDb, locateSandbox, getSandbox,
 	cabalOpt
 	) where
 
@@ -43,25 +43,25 @@ 		sandboxP = withObject "sandbox" sandboxPath where
 			sandboxPath obj = fmap Sandbox $ obj .: "sandbox"
 
--- | Find -package-db path for sandbox
+-- | Is -package-db file
+isPackageDb :: FilePath -> Bool
+isPackageDb p = cabalDev p || cabalSandbox p where
+	cabalDev dir = "packages-" `isPrefixOf` dir && ".conf" `isSuffixOf` dir
+	cabalSandbox dir = "-packages.conf.d" `isSuffixOf` dir
+
+-- | Find -package-db path for sandbox directory or package-db file itself
 findPackageDb :: FilePath -> IO (Maybe FilePath)
 findPackageDb sand = do
 	sand' <- canonicalizePath sand
 	isDir <- doesDirectoryExist sand'
-	if isDir then locateHere sand' else locateParent (takeDirectory sand')
-	where
-		locateHere path = do
-			cts <- filter (not . null . takeBaseName) <$> getDirectoryContents path
-			return $ fmap (path </>) $ find cabalDev cts <|> find cabalSandbox cts
-		locateParent dir = do
-			cts <- filter (not . null . takeBaseName) <$> getDirectoryContents dir
-			case find cabalDev cts <|> find cabalSandbox cts of
-				Nothing -> if isDrive dir then return Nothing else locateParent (takeDirectory dir)
-				Just packagef -> return $ Just (dir </> packagef)
-		cabalDev p = "packages-" `isPrefixOf` p && ".conf" `isSuffixOf` p
-		cabalSandbox p = "-packages.conf.d" `isSuffixOf` p
+	if
+		| isDir && isPackageDb sand' -> return $ Just sand'
+		| isDir -> do
+			cts <- filter (not . null . takeBaseName) <$> getDirectoryContents sand'
+			return $ fmap (sand' </>) $ find isPackageDb cts
+		| otherwise -> return Nothing
 
--- | Create sandbox by parent directory
+-- | Create sandbox by directory or package-db file
 locateSandbox :: FilePath -> ErrorT String IO Cabal
 locateSandbox p = liftIO (findPackageDb p) >>= maybe
 	(throwError $ "Can't locate package-db in sandbox: " ++ p)
src/HsDev/Cache/Structured.hs view
@@ -4,6 +4,7 @@ 	) where
 
 import Control.Applicative
+import Control.DeepSeq
 import Control.Exception
 import Control.Monad.Error
 import qualified Data.Map as M (assocs)
@@ -32,8 +33,7 @@ 	files' <- either (const zero) id <$>
 		handle wrapIO
 		(Cache.load (dir </> Cache.standaloneCache))
-	Cache.dump (dir </> Cache.standaloneCache) $
-		files' `mappend` structuredFiles db
+	files' `deepseq` Cache.dump (dir </> Cache.standaloneCache) (files' `mappend` structuredFiles db)
 	where
 		wrapIO :: SomeException -> IO (Either String Database)
 		wrapIO = return . Left . show
@@ -71,5 +71,5 @@ 	dat <- loadData (dir </> Cache.standaloneCache)
 	ErrorT $ return $ structured [] [] $ filterDB inFiles (const False) dat
 	where
-		inFiles = maybe False (`elem` fs') . moduleSource . moduleLocation
+		inFiles = maybe False (`elem` fs') . moduleSource . moduleIdLocation
 		fs' = map normalise fs
src/HsDev/Client/Commands.hs view
@@ -8,6 +8,7 @@ import Control.Arrow
 import Control.Monad
 import Control.Monad.Error
+import Control.Monad.Catch (try, SomeException(..))
 import Control.Monad.Trans.Maybe
 import Data.Aeson hiding (Result, Error)
 import Data.Aeson.Encode.Pretty
@@ -16,7 +17,9 @@ import Data.Maybe
 import Data.Monoid
 import qualified Data.Map as M
+import Data.String (fromString)
 import Data.Text (unpack)
+import qualified Data.Text as T (isInfixOf, isPrefixOf)
 import Data.Traversable (traverse)
 import System.Directory
 import System.FilePath
@@ -27,6 +30,7 @@ import HsDev.Database
 import HsDev.Project
 import HsDev.Symbols
+import HsDev.Symbols.Resolve (resolveOne, scopeModule, exportsModule)
 import HsDev.Symbols.Util
 import HsDev.Util
 import HsDev.Scan
@@ -66,7 +70,7 @@ 		ghcOpts])
 		"rescan sources"
 		rescan',
-	cmd' "remove" [] (sandboxes ++ [
+	cmdList' "remove" [] (sandboxes ++ [
 		projectArg `desc` "module project",
 		fileArg `desc` "module source file",
 		moduleArg,
@@ -75,16 +79,16 @@ 		"remove modules info"
 		remove',
 	-- Context free commands
-	cmd' "modules" [] (sandboxes ++ [
+	cmdList' "modules" [] (sandboxes ++ [
 		manyReq $ projectArg `desc` "projects to list modules from",
 		noLastArg,
 		manyReq packageArg,
 		sourced, standaloned])
 		"list modules"
 		listModules',
-	cmd' "packages" [] [] "list packages" listPackages',
-	cmd' "projects" [] [] "list projects" listProjects',
-	cmd' "symbol" ["name"] (matches ++ sandboxes ++ [
+	cmdList' "packages" [] [] "list packages" listPackages',
+	cmdList' "projects" [] [] "list projects" listProjects',
+	cmdList' "symbol" ["name"] (matches ++ sandboxes ++ [
 		projectArg `desc` "related project",
 		fileArg `desc` "source file",
 		moduleArg, localsArg,
@@ -100,24 +104,33 @@ 		sourced])
 		"get module info"
 		modul',
+	cmd' "resolve" [] (sandboxes ++ [
+		moduleArg, localsArg,
+		projectArg `desc` "module project",
+		fileArg `desc` "module source file",
+		exportsArg])
+		"resolve module scope (or exports)"
+		resolve',
 	cmd' "project" [] [
 		projectArg `desc` "project path or name"]
 		"get project info"
 		project',
 	-- Context commands
-	cmd' "lookup" ["symbol"] ctx "lookup for symbol" lookup',
-	cmd' "whois" ["symbol"] ctx "get info for symbol" whois',
-	cmd' "scope modules" [] ctx "get modules accessible from module or within a project" scopeModules',
-	cmd' "scope" [] (ctx ++ matches ++ [globalArg]) "get declarations accessible from module or within a project" scope',
-	cmd' "complete" ["input"] ctx "show completions for input" complete',
+	cmdList' "lookup" ["symbol"] ctx "lookup for symbol" lookup',
+	cmdList' "whois" ["symbol"] ctx "get info for symbol" whois',
+	cmdList' "scope modules" [] ctx "get modules accessible from module or within a project" scopeModules',
+	cmdList' "scope" [] (ctx ++ matches ++ [globalArg]) "get declarations accessible from module or within a project" scope',
+	cmdList' "complete" ["input"] (ctx ++ [wideArg]) "show completions for input" complete',
 	-- Tool commands
-	cmd' "hayoo" ["query"] hayooArgs "find declarations online via Hayoo" hayoo',
-	cmd' "cabal list" ["packages..."] [] "list cabal packages" cabalList',
-	cmd' "ghc-mod type" ["line", "column"] (ctx ++ [ghcOpts]) "infer type with 'ghc-mod type'" ghcmodType',
-	cmd' "ghc-mod check" ["files..."] [sandboxArg, ghcOpts] "check source files" ghcmodCheck',
-	cmd' "ghc-mod lint" ["file"] [hlintOpts] "lint source file" ghcmodLint',
+	cmdList' "hayoo" ["query"] hayooArgs "find declarations online via Hayoo" hayoo',
+	cmdList' "cabal list" ["packages..."] [] "list cabal packages" cabalList',
+	cmdList' "ghc-mod lang" [] [] "get LANGUAGE pragmas" ghcmodLang',
+	cmdList' "ghc-mod flags" [] [] "get OPTIONS_GHC pragmas" ghcmodFlags',
+	cmdList' "ghc-mod type" ["line", "column"] (ctx ++ [ghcOpts]) "infer type with 'ghc-mod type'" ghcmodType',
+	cmdList' "ghc-mod check" ["files..."] [sandboxArg, ghcOpts] "check source files" ghcmodCheck',
+	cmdList' "ghc-mod lint" ["file"] [hlintOpts] "lint source file" ghcmodLint',
 	-- Ghc commands
-	cmd' "ghc eval" ["expr..."] [] "evaluate expression" ghcEval',
+	cmdList' "ghc eval" ["expr..."] [] "evaluate expression" ghcEval',
 	-- Dump/load commands
 	cmd' "dump" [] (sandboxes ++ [
 		cacheDir, cacheFile,
@@ -140,12 +153,26 @@ 					Left (CommandError e ds) -> return $ Error e $ M.fromList $ map (first unpack) ds
 					Right r' -> return $ Result $ toJSON r'
 
+		cmdList' :: ToJSON a => String -> [String] -> [Opt] -> String -> ([String] -> Opts String -> CommandActionT [a]) -> Cmd CommandAction
+		cmdList' nm pos named descr act = cmd' nm pos (named ++ [splitRes]) descr act' where
+			splitRes = flag "split-result" `desc` "split result list and return it with notifications"
+			act' args os copts = do
+				rs <- act args os' copts
+				if flagSet "split-result" isSplit
+					then do
+						liftIO $ mapM_ (commandNotify copts . resultPart) rs
+						return []
+					else return rs
+				where
+					(isSplit, os') = splitOpts [splitRes] os
+
 		-- Command arguments and flags
 		allFlag d = flag "all" `short` ['a'] `desc` d
-		cacheDir = pathArg `desc` "cache path"
-		cacheFile = fileArg `desc` "cache file"
+		cacheDir = req "cache-dir" "path" `desc` "cache path"
+		cacheFile = req "cache-file" "path" `desc` "cache file"
 		ctx = [fileArg `desc` "source file", sandboxArg]
 		dataArg = req "data" "contents" `desc` "data to pass to command"
+		exportsArg = flag "exports" `short` ['e'] `desc` "resolve module exports"
 		fileArg = req "file" "path" `short` ['f']
 		findArg = req "find" "query" `desc` "infix match"
 		ghcOpts = list "ghc" "option" `short` ['g'] `desc` "options to pass to GHC"
@@ -156,7 +183,7 @@ 		hlintOpts = list "hlint" "option" `short` ['h'] `desc` "options to pass to hlint"
 		holdArg = flag "hold" `short` ['h'] `desc` "don't return any response"
 		localsArg = flag "locals" `short` ['l'] `desc` "look in local declarations"
-		noLastArg = flag "no-last" `desc` "don't select last package version"
+		noLastArg = flag "no-last" `desc` "select not only last version packages"
 		matches = [prefixArg, findArg]
 		moduleArg = req "module" "name" `short` ['m'] `desc` "module name"
 		packageArg = req "package" "name" `desc` "module package"
@@ -171,6 +198,7 @@ 			sandboxList]
 		sourced = flag "src" `desc` "source files"
 		standaloned = flag "stand" `desc` "standalone files"
+		wideArg = flag "wide" `short` ['f'] `desc` "wide mode - complete as if there were no import lists"
 
 		-- | Ping server
 		ping' :: [String] -> Opts String -> CommandActionT Value
@@ -199,7 +227,7 @@ 				updateData (ResultModuleDeclaration md) = do
 					let
 						ModuleId mname mloc = declarationModuleId md
-						defMod = Module mname Nothing mloc [] mempty mempty
+						defMod = Module mname Nothing mloc mempty mempty mempty
 						defInspMod = Inspected InspectionNone mloc (Right defMod)
 						dbmod = maybe
 							defInspMod
@@ -209,7 +237,7 @@ 							inspectionResult = fmap (addDeclaration $ moduleDeclaration md) (inspectionResult dbmod) }
 					DB.update (dbVar copts) $ return $ fromModule updatedMod
 				updateData (ResultModuleId (ModuleId mname mloc)) = when (M.notMember mloc $ databaseModules dbval) $
-					DB.update (dbVar copts) $ return $ fromModule $ Inspected InspectionNone mloc (Right $ Module mname Nothing mloc [] mempty mempty)
+					DB.update (dbVar copts) $ return $ fromModule $ Inspected InspectionNone mloc (Right $ Module mname Nothing mloc mempty mempty mempty)
 				updateData (ResultModule m) = DB.update (dbVar copts) $ return $ fromModule $ Inspected InspectionNone (moduleLocation m) (Right m)
 				updateData (ResultInspectedModule m) = DB.update (dbVar copts) $ return $ fromModule m
 				updateData (ResultProject p) = DB.update (dbVar copts) $ return $ fromProject p
@@ -381,6 +409,32 @@ 				[m] -> return m
 				ms' -> commandError "Ambiguous modules" ["modules" .= (map moduleId ms')]
 
+		-- | Resolve module scope
+		resolve' :: [String] -> Opts String -> CommandActionT Module
+		resolve' _ as copts = do
+			dbval <- liftM (localsDatabase as) $ getDb copts
+			proj <- mapErrorT (fmap $ strMsg +++ id) $ traverse (findProject copts) $ arg "project" as
+			cabal <- mapErrorT (fmap $ strMsg +++ id) $ getCabal copts as
+			file' <- mapErrorT (fmap $ strMsg +++ id) $ traverse (findPath copts) $ arg "file" as
+			let
+				filters = allOf $ catMaybes [
+					fmap inProject proj,
+					fmap inFile file',
+					fmap inModule (arg "module" as),
+					Just byFile]
+			rs <- mapErrorT (fmap $ strMsg +++ id) $
+				(newest as . filter (filters . moduleId)) <$> maybe
+					(return $ allModules dbval)
+					(mapErrorStr . findModule dbval)
+					(arg "module" as)
+			let
+				cabaldb = filterDB (restrictCabal cabal) (const True) dbval
+				getScope = if flagSet "exports" as then exportsModule else scopeModule
+			case rs of
+				[] -> commandError "Module not found" []
+				[m] -> return $ getScope $ resolveOne cabaldb m
+				ms' -> commandError "Ambiguous modules" ["modules" .= (map moduleId ms')]
+
 		-- | Get project info
 		project' :: [String] -> Opts String -> CommandActionT Project
 		project' _ as copts = do
@@ -425,7 +479,7 @@ 		complete' [input] as copts = do
 			dbval <- getDb copts
 			(srcFile, cabal) <- getCtx copts as
-			mapErrorStr $ completions dbval cabal srcFile input
+			mapErrorStr $ completions dbval cabal srcFile input (flagSet "wide" as)
 		complete' _ _ _ = commandError "Invalid arguments" []
 
 		-- | Hayoo
@@ -443,6 +497,14 @@ 		cabalList' :: [String] -> Opts String -> CommandActionT [Cabal.CabalPackage]
 		cabalList' qs _ _ = mapErrorStr $ Cabal.cabalList qs
 
+		-- | Ghc-mod lang
+		ghcmodLang' :: [String] -> Opts String -> CommandActionT [String]
+		ghcmodLang' _ _ _ = mapErrorStr GhcMod.langs
+
+		-- | Ghc-mod flags
+		ghcmodFlags' :: [String] -> Opts String -> CommandActionT [String]
+		ghcmodFlags' _ _ _ = mapErrorStr GhcMod.flags
+
 		-- | Ghc-mod type
 		ghcmodType' :: [String] -> Opts String -> CommandActionT [GhcMod.TypedRegion]
 		ghcmodType' [line] as copts = ghcmodType' [line, "1"] as copts
@@ -451,9 +513,9 @@ 			column' <- maybe (commandError "column must be a number" []) return $ readMaybe column
 			dbval <- getDb copts
 			(srcFile, cabal) <- getCtx copts as
-			(srcFile', m, mproj) <- mapErrorStr $ fileCtx dbval srcFile
+			(srcFile', _, _) <- mapErrorStr $ fileCtx dbval srcFile
 			mapErrorStr $ GhcMod.waitMultiGhcMod (commandGhcMod copts) srcFile' $
-				GhcMod.typeOf (listArg "ghc" as) cabal srcFile' mproj (moduleName m) line' column'
+				GhcMod.typeOf (listArg "ghc" as) cabal srcFile' line' column'
 		ghcmodType' [] _ _ = commandError "Specify line" []
 		ghcmodType' _ _ _ = commandError "Too much arguments" []
 
@@ -479,11 +541,11 @@ 
 		-- | Evaluate expression
 		ghcEval' :: [String] -> Opts String -> CommandActionT [Value]
-		ghcEval' exprs _ copts = mapErrorStr $ liftM (map toValue) $
-			waitGhc (commandGhc copts) $ mapM (try . evaluate) exprs
+		ghcEval' exprs _ copts = mapErrorStr $ liftM (map toValue) $ liftTask $
+			pushTask (commandGhc copts) $ mapM (try . evaluate) exprs
 			where
-				toValue :: Either String String -> Value
-				toValue (Left e) = object ["fail" .= e]
+				toValue :: Either SomeException String -> Value
+				toValue (Left (SomeException e)) = object ["fail" .= show e]
 				toValue (Right s) = toJSON s
 
 		-- | Dump database info
@@ -501,7 +563,7 @@ 					if flagSet "stand" as then standaloneDB dbval else mempty,
 					mconcat $ map (`cabalDB` dbval) cabals,
 					mconcat $ map (`projectDB` dbval) ps',
-					filterDB (\m -> any (`inFile` moduleId m) fs') (const False) dbval]
+					filterDB (\m -> any (`inFile` m) fs') (const False) dbval]
 
 			void $ runMaybeT $ msum [
 				do
@@ -632,7 +694,7 @@ 	| flagSet "locals" as = databaseLocals
 	| otherwise = id
 
--- | Select newest packages if 'no-last' flag set
+-- | Select newest packages if 'no-last' flag not set
 newest :: Symbol a => Opts String -> [a] -> [a]
 newest as
 	| flagSet "no-last" as = id
@@ -659,6 +721,7 @@ 	settings = Update.Settings
 		(commandDatabase copts)
 		(commandReadCache copts)
+		(commandWriteCache copts)
 		(commandNotify copts . Notification . toJSON)
 		(listArg "ghc" as)
 
@@ -672,7 +735,7 @@ 	Nothing -> id
 	Just str -> filter (match' str)
 	where
-		match' str m = str `isInfixOf` declarationName (moduleDeclaration m)
+		match' str m = fromString str `T.isInfixOf` declarationName (moduleDeclaration m)
 
 -- | Filter declarations with prefix match
 prefMatch :: Opts String -> [ModuleDeclaration] -> [ModuleDeclaration]
@@ -681,6 +744,5 @@ 	Just (qname, pref) -> filter (match' qname pref)
 	where
 		match' qname pref m =
-			pref `isPrefixOf` declarationName (moduleDeclaration m) &&
-			maybe True (== moduleIdName (declarationModuleId m)) qname
-
+			fromString pref `T.isPrefixOf` declarationName (moduleDeclaration m) &&
+			maybe True (== moduleIdName (declarationModuleId m)) (fmap fromString qname)
src/HsDev/Commands.hs view
@@ -19,17 +19,19 @@ 	) where
 
 import Control.Applicative
-import Control.Arrow (Arrow(second))
 import Control.Monad.Error
 import Data.List
 import Data.Maybe
 import qualified Data.Map as M (lookup)
+import Data.String (fromString)
+import qualified Data.Text as T (isPrefixOf, split, unpack)
 import Data.Traversable (traverse)
 import System.Directory (canonicalizePath)
 
 import HsDev.Database
 import HsDev.Project
 import HsDev.Symbols
+import HsDev.Symbols.Resolve
 import HsDev.Symbols.Util
 
 -- | Find declaration by name
@@ -37,14 +39,14 @@ findDeclaration db ident = return $ selectDeclarations checkName db where
 	checkName :: ModuleDeclaration -> Bool
 	checkName m =
-		(declarationName (moduleDeclaration m) == iname) &&
-		(maybe True (moduleIdName (declarationModuleId m) ==) qname)
+		(declarationName (moduleDeclaration m) == fromString iname) &&
+		(maybe True ((moduleIdName (declarationModuleId m) ==) . fromString) qname)
 
 	(qname, iname) = splitIdentifier ident
 
 -- | Find module by name
 findModule :: Database -> String -> ErrorT String IO [Module]
-findModule db mname = return $ selectModules ((== mname) . moduleName) db
+findModule db mname = return $ selectModules ((== fromString mname) . moduleName) db
 
 -- | Find module in file
 fileModule :: Database -> FilePath -> ErrorT String IO Module
@@ -59,7 +61,7 @@ 	maybe (throwError $ "Project " ++ p' ++ " not found") return $
 		M.lookup p' $ databaseProjects db
 
--- | Lookup visible symbol
+-- | Lookup visible within project symbol
 lookupSymbol :: Database -> Cabal -> FilePath -> String -> ErrorT String IO [ModuleDeclaration]
 lookupSymbol db cabal file ident = do
 	(_, mthis, mproj) <- fileCtx db file
@@ -75,14 +77,15 @@ -- | Whois symbol in scope
 whois :: Database -> Cabal -> FilePath -> String -> ErrorT String IO [ModuleDeclaration]
 whois db cabal file ident = do
-	(_, mthis, _) <- fileCtx db file
-	liftM
-		(filter $ checkModule $ allOf [
-			restrictCabal cabal,
-			inScope mthis qname])
-		(newestPackage <$> findDeclaration db iname)
+	(_, mthis, mproj) <- fileCtx db file
+	return $
+		newestPackage $ filter (checkDecl . moduleDeclaration) $
+		moduleModuleDeclarations $ scopeModule $
+		resolveOne (fileDeps file cabal mproj db) $
+		moduleLocals mthis
 	where
 		(qname, iname) = splitIdentifier ident
+		checkDecl d = fmap fromString qname `elem` scopes d && declarationName d == fromString iname
 
 -- | Accessible modules
 scopeModules :: Database -> Cabal -> FilePath -> ErrorT String IO [Module]
@@ -100,35 +103,33 @@ -- | Symbols in scope
 scope :: Database -> Cabal -> FilePath -> Bool -> ErrorT String IO [ModuleDeclaration]
 scope db cabal file False = do
-	(_, mthis, _) <- fileCtx db file
-	depModules <- liftM (filter ((`imported` (moduleImports' mthis)) . moduleId)) $
-		scopeModules db cabal file
-	return $ concatMap moduleModuleDeclarations $ mthis : depModules
+	(_, mthis, mproj) <- fileCtx db file
+	return $ moduleModuleDeclarations $ scopeModule $ resolveOne (fileDeps file cabal mproj db) mthis
 scope db cabal file True = concatMap moduleModuleDeclarations <$> scopeModules db cabal file
 
 -- | Completions
-completions :: Database -> Cabal -> FilePath -> String -> ErrorT String IO [ModuleDeclaration]
-completions db cabal file prefix = do
-	(_, mthis, _) <- fileCtx db file
-	decls <- scope db cabal file False
-	return [decl |
-		decl <- decls,
-		imp <- filter ((== moduleIdName (declarationModuleId decl)) . importModuleName) $
-			moduleImports' mthis,
-		qname `elem` catMaybes [
-			if not (importIsQualified imp) then Just Nothing else Nothing,
-			Just $ Just $ importModuleName imp,
-			fmap Just $ importAs imp],
-		iname `isPrefixOf` (declarationName . moduleDeclaration $ decl)]
+completions :: Database -> Cabal -> FilePath -> String -> Bool -> ErrorT String IO [ModuleDeclaration]
+completions db cabal file prefix wide = do
+	(_, mthis, mproj) <- fileCtx db file
+	return $
+		newestPackage $ filter (checkDecl . moduleDeclaration) $
+		moduleModuleDeclarations $ scopeModule $
+		resolveOne (fileDeps file cabal mproj db) $
+		dropImportLists mthis
 	where
 		(qname, iname) = splitIdentifier prefix
+		checkDecl d = fmap fromString qname `elem` scopes d && fromString iname `T.isPrefixOf` declarationName d
+		dropImportLists m
+			| wide = m { moduleImports = map dropList (moduleImports m) }
+			| otherwise = m
+		dropList i = i { importList = Nothing }
 
 -- | Module completions
 moduleCompletions :: Database -> [Module] -> String -> ErrorT String IO [String]
-moduleCompletions _ ms prefix = return $ nub $ completions' $ map moduleName ms where
+moduleCompletions _ ms prefix = return $ map T.unpack $ nub $ completions' $ map moduleName ms where
 	completions' = mapMaybe getNext where
 		getNext m
-			| prefix `isPrefixOf` m = listToMaybe $ map snd $ dropWhile (uncurry (==)) $ zip (splitBy '.' prefix) (splitBy '.' m)
+			| fromString prefix `T.isPrefixOf` m = listToMaybe $ map snd $ dropWhile (uncurry (==)) $ zip (T.split (== '.') $ fromString prefix) (T.split (== '.') m)
 			| otherwise = Nothing
 
 -- | Check module
@@ -148,12 +149,12 @@ visibleFrom (Just p) this m = visible p (moduleId this) m
 visibleFrom Nothing this m = (moduleId this) == m || byCabal m
 
-splitBy :: Char -> String -> [String]
-splitBy ch = takeWhile (not . null) . unfoldr (Just . second (drop 1) . break (== ch))
-
 -- | Get module imports with Prelude and self import
 moduleImports' :: Module -> [Import]
-moduleImports' m = Import "Prelude" False Nothing Nothing : Import (moduleName m) False Nothing Nothing : moduleImports m
+moduleImports' m =
+	import_ (fromString "Prelude") :
+	import_ (moduleName m) :
+	moduleImports m
 
 -- | Split identifier into module name and identifier itself
 splitIdentifier :: String -> (Maybe String, String)
@@ -179,3 +180,12 @@ 		mproj <- liftIO $ locateProject file'
 		mproj' <- traverse (getProject db) mproj
 		return (file', Nothing, mproj')
+
+-- | Restrict only modules file depends on
+fileDeps :: FilePath -> Cabal -> Maybe Project -> Database -> Database
+fileDeps file cabal mproj = filterDB fileDeps' (const True) where
+	fileDeps' = liftM2 (||)
+		(maybe (const True) inProject mproj)
+		(liftM2 (&&)
+			(restrictCabal cabal)
+			(maybe (const True) inDepsOf (join $ fileTarget <$> mproj <*> pure file)))
src/HsDev/Database.hs view
@@ -6,7 +6,7 @@ 	fromModule, fromProject,
 	filterDB,
 	projectDB, cabalDB, standaloneDB,
-	selectModules, selectDeclarations, lookupModule, lookupFile,
+	selectModules, selectDeclarations, lookupModule, lookupFile, refineProject,
 	getInspected,
 
 	append, remove,
@@ -107,22 +107,22 @@ 	databaseProjects = M.singleton (projectCabal p) p }
 
 -- | Filter database by predicate
-filterDB :: (Module -> Bool) -> (Project -> Bool) -> Database -> Database
+filterDB :: (ModuleId -> Bool) -> (Project -> Bool) -> Database -> Database
 filterDB m p db = mempty {
-	databaseModules = M.filter (either (const False) m . inspectionResult) (databaseModules db),
+	databaseModules = M.filter (either (const False) (m . moduleId) . inspectionResult) (databaseModules db),
 	databaseProjects = M.filter p (databaseProjects db) }
 
 -- | Project database
 projectDB :: Project -> Database -> Database
-projectDB proj = filterDB (inProject proj . moduleId) (((==) `on` projectCabal) proj)
+projectDB proj = filterDB (inProject proj) (((==) `on` projectCabal) proj)
 
 -- | Cabal database
 cabalDB :: Cabal -> Database -> Database
-cabalDB cabal = filterDB (inCabal cabal . moduleId) (const False)
+cabalDB cabal = filterDB (inCabal cabal) (const False)
 
 -- | Standalone database
 standaloneDB :: Database -> Database
-standaloneDB db = filterDB (check' . moduleId) (const False) db where
+standaloneDB db = filterDB (check') (const False) db where
 	check' m = noProject m && byFile m
 	noProject m = all (not . flip inProject m) ps
 	ps = M.elems $ databaseProjects db
@@ -144,6 +144,10 @@ -- | Lookup module by its source file
 lookupFile :: FilePath -> Database -> Maybe Module
 lookupFile f = listToMaybe . selectModules (inFile f . moduleId)
+
+-- | Refine project
+refineProject :: Database -> Project -> Maybe Project
+refineProject db proj = M.lookup (projectCabal proj) $ databaseProjects db
 
 -- | Get inspected module
 getInspected :: Database -> Module -> InspectedModule
src/HsDev/Database/Update.hs view
@@ -17,9 +17,10 @@ 	) where
 
 import Control.Applicative
-import Control.Monad.CatchIO
+import Control.Monad.Catch
 import Control.Monad.Error
 import Control.Monad.Reader
+import Control.Monad.Writer
 import Data.Aeson
 import Data.Aeson.Types
 import qualified Data.HashMap.Strict as HM
@@ -27,7 +28,7 @@ import Data.Map (Map)
 import qualified Data.Map as M
 import Data.Maybe (mapMaybe, isJust)
-import Data.Monoid
+import qualified Data.Text as T (unpack)
 import System.Directory (canonicalizePath)
 
 import qualified HsDev.Cache.Structured as Cache
@@ -96,16 +97,30 @@ data Settings = Settings {
 	database :: Async Database,
 	databaseCacheReader :: (FilePath -> ErrorT String IO Structured) -> IO (Maybe Database),
+	databaseCacheWriter :: Database -> IO (),
 	onStatus :: Task -> IO (),
 	ghcOptions :: [String] }
 
-newtype UpdateDB m a = UpdateDB { runUpdateDB :: ReaderT Settings m a }
-	deriving (Applicative, Monad, MonadIO, MonadCatchIO, Functor, MonadReader Settings)
+newtype UpdateDB m a = UpdateDB { runUpdateDB :: ReaderT Settings (WriterT [ModuleLocation] m) a }
+	deriving (Applicative, Monad, MonadIO, MonadThrow, MonadCatch, Functor, MonadReader Settings, MonadWriter [ModuleLocation])
 
 -- | Run `UpdateDB` monad
-updateDB :: Monad m => Settings -> ErrorT String (UpdateDB m) () -> m ()
-updateDB sets act = runUpdateDB (runErrorT act >> return ()) `runReaderT` sets
+updateDB :: MonadIO m => Settings -> ErrorT String (UpdateDB m) () -> m ()
+updateDB sets act = do
+	updatedMods <- execWriterT (runUpdateDB (runErrorT act >> return ()) `runReaderT` sets)
+	wait $ database sets
+	dbval <- liftIO $ readAsync $ database sets
+	let
+		cabals = nub $ mapMaybe moduleCabal_ updatedMods
+		projs = nub $ mapMaybe moduleProject_ updatedMods
+		stand = any moduleStandalone updatedMods
 
+		modifiedDb = mconcat $ concat [
+			map (`cabalDB` dbval) cabals,
+			map (`projectDB` dbval) projs,
+			[standaloneDB dbval | stand]]
+	liftIO $ databaseCacheWriter sets modifiedDb
+
 -- | Post status
 postStatus :: (MonadIO m, MonadReader Settings m) => Task -> m ()
 postStatus s = do
@@ -120,13 +135,15 @@ 	wait db
 
 -- | Update task result to database
-updater :: (MonadIO m, MonadReader Settings m) => m Database -> m ()
+updater :: (MonadIO m, MonadReader Settings m, MonadWriter [ModuleLocation] m) => m Database -> m ()
 updater act = do
 	db <- asks database
-	act >>= update db . return
+	db' <- act
+	update db $ return db'
+	tell $ map moduleLocation $ allModules db'
 
 -- | Load data from cache and wait
-loadCache :: (MonadIO m, MonadReader Settings m) => (FilePath -> ErrorT String IO Structured) -> m ()
+loadCache :: (MonadIO m, MonadReader Settings m, MonadWriter [ModuleLocation] m) => (FilePath -> ErrorT String IO Structured) -> m ()
 loadCache act = do
 	cacheReader <- asks databaseCacheReader
 	mdat <- liftIO $ cacheReader act
@@ -167,7 +184,7 @@ readDB = asks database >>= liftIO . readAsync
 
 -- | Scan module
-scanModule :: MonadCatchIO m => [String] -> ModuleLocation -> ErrorT String (UpdateDB m) ()
+scanModule :: (MonadIO m, MonadCatch m) => [String] -> ModuleLocation -> ErrorT String (UpdateDB m) ()
 scanModule opts mloc = runTask "scanning" (subject mloc ["module" .= mloc]) $ do
 	im <- liftErrorT $ S.scanModule opts mloc
 	updater $ return $ fromModule im
@@ -175,10 +192,10 @@ 	return ()
 
 -- | Scan modules
-scanModules :: MonadCatchIO m => [String] -> [S.ModuleToScan] -> ErrorT String (UpdateDB m) ()
+scanModules :: (MonadIO m, MonadCatch m) => [String] -> [S.ModuleToScan] -> ErrorT String (UpdateDB m) ()
 scanModules opts ms = do
 	dbval <- readDB
-	ms' <- liftErrorT $ filterM (S.changedModule dbval opts . fst) ms
+	ms' <- liftErrorT $ filterM (\m -> S.changedModule dbval (opts ++ snd m) (fst m)) ms
 	runTasks $
 		[scanProjectFile opts p >> return () | p <- ps] ++
 		[scanModule (opts ++ snd m) (fst m) | m <- ms']
@@ -188,18 +205,23 @@ 		toProj _ = Nothing
 
 -- | Scan source file
-scanFile :: MonadCatchIO m => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
+scanFile :: (MonadIO m, MonadCatch m) => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
 scanFile opts fpath = do
+	dbval <- readDB
 	fpath' <- liftIO $ canonicalizePath fpath
-	mproj <- liftIO $ locateProject fpath'
+	mloc <- case lookupFile fpath' dbval of
+		Just m -> return $ moduleLocation m
+		Nothing -> do
+			mproj <- liftIO $ locateProject fpath'
+			return $ FileModule fpath' mproj
+	dirty <- liftErrorT $ S.changedModule dbval opts mloc
 	let
-		mtarget = mproj >>= (`fileTarget` fpath')
+		mtarget = moduleProject_ mloc >>= (`fileTarget` fpath')
 		fileExts = maybe [] (extensionsOpts . infoExtensions) mtarget
-
-	scanModule (opts ++ fileExts) (FileModule fpath' mproj)
+	when dirty $ scanModule (opts ++ fileExts) mloc
 
 -- | Scan cabal modules
-scanCabal :: MonadCatchIO m => [String] -> Cabal -> ErrorT String (UpdateDB m) ()
+scanCabal :: (MonadIO m, MonadCatch m) => [String] -> Cabal -> ErrorT String (UpdateDB m) ()
 scanCabal opts cabalSandbox = runTask "scanning" (subject cabalSandbox ["sandbox" .= cabalSandbox]) $ do
 	loadCache $ Cache.loadCabal cabalSandbox
 	dbval <- readDB
@@ -210,17 +232,17 @@ 	updater $ return $ 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 (moduleName m) docs
+		setDocs' docs m = maybe m (`setDocs` m) $ M.lookup (T.unpack $ moduleName m) docs
 
 -- | Scan project file
-scanProjectFile :: MonadCatchIO m => [String] -> FilePath -> ErrorT String (UpdateDB m) Project
+scanProjectFile :: (MonadIO m, MonadCatch m) => [String] -> FilePath -> ErrorT String (UpdateDB m) Project
 scanProjectFile opts cabal = runTask "scanning" (subject cabal ["file" .= cabal]) $ do
 	proj <- liftErrorT $ S.scanProjectFile opts cabal
 	updater $ return $ fromProject proj
 	return proj
 
 -- | Scan project
-scanProject :: MonadCatchIO m => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
+scanProject :: (MonadIO m, MonadCatch m) => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
 scanProject opts cabal = runTask "scanning" (subject (project cabal) ["project" .= cabal]) $ do
 	proj <- scanProjectFile opts cabal
 	loadCache $ Cache.loadProject $ projectCabal proj
@@ -228,7 +250,7 @@ 	scanModules opts sources
 
 -- | Scan directory for source files and projects
-scanDirectory :: MonadCatchIO m => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
+scanDirectory :: (MonadIO m, MonadCatch m) => [String] -> FilePath -> ErrorT String (UpdateDB m) ()
 scanDirectory opts dir = runTask "scanning" (subject dir ["path" .= dir]) $ do
 	(projSrcs, standSrcs) <- runTask "getting list of sources" [] $
 		liftErrorT $ S.enumDirectory dir
src/HsDev/Inspect.hs view
@@ -14,9 +14,12 @@ import qualified Control.Exception as E
 import Control.Monad
 import Control.Monad.Error
-import Data.List (intercalate, find)
+import Data.Function (on)
+import Data.List
 import Data.Map (Map)
 import Data.Maybe (fromMaybe, mapMaybe, catMaybes)
+import Data.Ord (comparing)
+import Data.String (fromString)
 import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
 import Data.Traversable (traverse, sequenceA)
 import qualified Data.Map as M
@@ -25,6 +28,7 @@ import qualified System.Directory as Dir
 import System.IO
 import System.FilePath
+import Data.Generics.Uniplate.Data
 
 import qualified Name (Name, getOccString, occNameString)
 import qualified Module (moduleNameString)
@@ -42,71 +46,100 @@ analyzeModule :: [String] -> Maybe FilePath -> String -> Either String Module
 analyzeModule exts file source = case H.parseFileContentsWithMode pmode source' of
 		H.ParseFailed loc reason -> Left $ "Parse failed at " ++ show loc ++ ": " ++ reason
-		H.ParseOk (H.Module _ (H.ModuleName mname) _ _ _ imports declarations) -> Right $ Module {
-			moduleName = mname,
+		H.ParseOk (H.Module _ (H.ModuleName mname) _ _ mexports imports declarations) -> Right Module {
+			moduleName = fromString mname,
 			moduleDocs =  Nothing,
 			moduleLocation = ModuleSource Nothing,
-			moduleExports = [],
+			moduleExports = fmap (concatMap getExports) mexports,
 			moduleImports = map getImport imports,
-			moduleDeclarations = M.fromList $ map (declarationName &&& id) $ getDecls declarations }
+			moduleDeclarations = sortDeclarations $ getDecls declarations }
 	where
 		pmode :: H.ParseMode
 		pmode = H.defaultParseMode {
 			H.parseFilename = fromMaybe (H.parseFilename H.defaultParseMode) file,
 			H.baseLanguage = H.Haskell2010,
-			H.extensions = map H.parseExtension exts }
+			H.extensions = H.glasgowExts ++ map H.parseExtension exts,
+			H.fixities = Just H.baseFixities }
 
 		-- 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
 
+getExports :: H.ExportSpec -> [Export]
+getExports (H.EModuleContents (H.ModuleName m)) = [ExportModule $ fromString m]
+getExports e = map (uncurry ExportName . (fmap fromString *** fromString) . identOfQName) $ childrenBi e
+
 getImport :: H.ImportDecl -> Import
-getImport d = Import (mname (H.importModule d)) (H.importQualified d) (fmap mname $ H.importAs d) (Just $ toPosition $ H.importLoc d) where
-	mname (H.ModuleName n) = n
+getImport d = Import
+	(mname (H.importModule d))
+	(H.importQualified d)
+	(mname <$> H.importAs d)
+	(importLst <$> H.importSpecs d)
+	(Just $ toPosition $ H.importLoc d)
+	where
+		mname (H.ModuleName n) = fromString n
+		importLst (hiding, specs) = ImportList hiding $ map (fromString . identOfName) (concatMap childrenBi specs :: [H.Name])
 
 getDecls :: [H.Decl] -> [Declaration]
-getDecls decls = map addLocals declInfos ++ filter noInfo defs where
-	declInfos = concatMap getDecl decls
-	addLocals :: Declaration -> Declaration
-	addLocals decl = decl `where_` maybe [] locals def where
-		def = find (\d -> declarationName d == declarationName decl) defs
-	defs = concatMap getDef decls
-	noInfo :: Declaration -> Bool
-	noInfo d = declarationName d `notElem` names
-	names = map declarationName declInfos
+getDecls decls =
+	map mergeDecls .
+	groupBy ((==) `on` declarationName) .
+	sortBy (comparing declarationName) $
+	concatMap getDecl decls ++ concatMap getDef decls
+	where
+		mergeDecls :: [Declaration] -> Declaration
+		mergeDecls [] = error "Impossible"
+		mergeDecls ds = Declaration
+			(declarationName $ head ds)
+			Nothing
+			Nothing
+			(msum $ map declarationDocs ds)
+			(minimum <$> mapM declarationPosition ds)
+			(foldr1 mergeInfos $ map declaration ds)
 
+		mergeInfos :: DeclarationInfo -> DeclarationInfo -> DeclarationInfo
+		mergeInfos (Function ln ld) (Function rn rd) = Function (ln `mplus` rn) (ld ++ rd)
+		mergeInfos l _ = l
+
 getBinds :: H.Binds -> [Declaration]
 getBinds (H.BDecls decls) = getDecls decls
 getBinds _ = []
 
 getDecl :: H.Decl -> [Declaration]
-getDecl decl = case decl of
-	H.TypeSig loc names typeSignature -> map
-		(\n -> setPosition loc (Declaration (identOfName n) Nothing Nothing (Function (Just $ oneLinePrint typeSignature) [])))
-		names
-	H.TypeDecl loc n args _ -> [setPosition loc $ Declaration (identOfName n) Nothing Nothing (Type $ TypeInfo Nothing (map oneLinePrint args) Nothing)]
-	H.DataDecl loc dataOrNew ctx n args _ _ -> [setPosition loc $ Declaration (identOfName n) Nothing Nothing (ctor dataOrNew $ TypeInfo (makeCtx ctx) (map oneLinePrint args) Nothing)]
-	H.GDataDecl loc dataOrNew ctx n args _ _ _ -> [setPosition loc $ Declaration (identOfName n) Nothing Nothing (ctor dataOrNew $ TypeInfo (makeCtx ctx) (map oneLinePrint args) Nothing)]
-	H.ClassDecl loc ctx n args _ _ -> [setPosition loc $ Declaration (identOfName n) Nothing Nothing (Class $ TypeInfo (makeCtx ctx) (map oneLinePrint args) Nothing)]
+getDecl decl' = case decl' of
+	H.TypeSig loc names typeSignature -> [mkFun loc n (Function (Just $ fromString $ oneLinePrint typeSignature) []) | n <- names]
+	H.TypeDecl loc n args _ -> [mkType loc n Type args]
+	H.DataDecl loc dataOrNew ctx n args _ _ -> [mkType loc n (ctor dataOrNew `withCtx` ctx) args]
+	H.GDataDecl loc dataOrNew ctx n args _ _ _ -> [mkType loc n (ctor dataOrNew `withCtx` ctx) args]
+	H.ClassDecl loc ctx n args _ _ -> [mkType loc n (Class `withCtx` ctx) args]
 	_ -> []
 	where
+		mkFun :: H.SrcLoc -> H.Name -> DeclarationInfo -> Declaration
+		mkFun loc n = setPosition loc . decl (fromString $ identOfName n)
+
+		mkType :: H.SrcLoc -> H.Name -> (TypeInfo -> DeclarationInfo) -> [H.TyVarBind] -> Declaration
+		mkType loc n ctor' args = setPosition loc $ decl (fromString $ identOfName n) $ ctor' $ TypeInfo Nothing (map (fromString . oneLinePrint) args) Nothing
+
+		withCtx :: (TypeInfo -> DeclarationInfo) -> H.Context -> TypeInfo -> DeclarationInfo
+		withCtx ctor' ctx tinfo = ctor' (tinfo { typeInfoContext = makeCtx ctx })
+
 		ctor :: H.DataOrNew -> TypeInfo -> DeclarationInfo
 		ctor H.DataType = Data
 		ctor H.NewType = NewType
 
 		makeCtx [] = Nothing
-		makeCtx ctx = Just $ intercalate ", " $ map oneLinePrint ctx
+		makeCtx ctx = Just $ fromString $ intercalate ", " $ map oneLinePrint ctx
 
 		oneLinePrint :: H.Pretty a => a -> String
 		oneLinePrint = H.prettyPrintStyleMode (H.style { H.mode = H.OneLineMode }) H.defaultMode
 
 getDef :: H.Decl -> [Declaration]
 getDef (H.FunBind []) = []
-getDef (H.FunBind matches@(H.Match loc n _ _ _ _ : _)) = [setPosition loc $ Declaration (identOfName n) Nothing Nothing fun] where
+getDef (H.FunBind matches@(H.Match loc n _ _ _ _ : _)) = [setPosition loc $ decl (fromString $ identOfName n) fun] where
 	fun = Function Nothing $ concatMap (getBinds . matchBinds) matches
 	matchBinds (H.Match _ _ _ _ _ binds) = binds
-getDef (H.PatBind loc pat _ binds) = map (\name -> setPosition loc (Declaration (identOfName name) Nothing Nothing (Function Nothing $ getBinds binds))) (names pat) where
+getDef (H.PatBind loc pat _ binds) = map (\name -> setPosition loc (decl (fromString $ identOfName name) (Function Nothing $ getBinds binds))) (names pat) where
 	names :: H.Pat -> [H.Name]
 	names (H.PVar n) = [n]
 	names (H.PNPlusK n _) = [n]
@@ -133,6 +166,11 @@ 	fieldNames H.PFieldWildcard = []
 getDef _ = []
 
+identOfQName :: H.QName -> (Maybe String, String)
+identOfQName (H.Qual (H.ModuleName mname) name) = (Just mname, identOfName name)
+identOfQName (H.UnQual name) = (Nothing, identOfName name)
+identOfQName (H.Special sname) = (Nothing, H.prettyPrint sname)
+
 identOfName :: H.Name -> String
 identOfName name = case name of
 	H.Ident s -> s
@@ -148,7 +186,7 @@ documentationMap :: Doc.Interface -> Map String String
 documentationMap iface = M.fromList $ concatMap toDoc $ Doc.ifaceExportItems iface where
 	toDoc :: Doc.ExportItem Name.Name -> [(String, String)]
-	toDoc (Doc.ExportDecl decl docs _ _ _ _) = maybe [] (zip (extractNames decl) . repeat) $ extractDocs docs
+	toDoc (Doc.ExportDecl decl' docs _ _ _ _) = maybe [] (zip (extractNames decl') . repeat) $ extractDocs docs
 	toDoc _ = []
 
 	extractNames :: HsDecls.LHsDecl Name.Name -> [String]
@@ -161,7 +199,7 @@ 		_ -> []
 
 	extractDocs :: Doc.DocForDecl Name.Name -> Maybe String
-	extractDocs (mbDoc, _) = fmap printDoc $ Doc.documentationDoc mbDoc where
+	extractDocs (mbDoc, _) = printDoc <$> Doc.documentationDoc mbDoc where
 		printDoc :: Doc.Doc Name.Name -> String
 		printDoc Doc.DocEmpty = ""
 		printDoc (Doc.DocAppend l r) = printDoc l ++ printDoc r
@@ -191,11 +229,12 @@ 
 -- | Adds documentation to declaration
 addDoc :: Map String String -> Declaration -> Declaration
-addDoc docsMap decl = decl { declarationDocs = M.lookup (declarationName decl) docsMap }
+addDoc docsMap decl' = decl' { declarationDocs = M.lookup (declarationName decl') docsMap' } where
+	docsMap' = M.mapKeys fromString . M.map fromString $ docsMap
 
 -- | Adds documentation to all declarations in module
 addDocs :: Map String String -> Module -> Module
-addDocs docsMap m = m { moduleDeclarations = M.map (addDoc docsMap) (moduleDeclarations m) }
+addDocs docsMap m = m { moduleDeclarations = map (addDoc docsMap) (moduleDeclarations m) }
 
 -- | Inspect contents
 inspectContents :: String -> [String] -> String -> ErrorT String IO InspectedModule
@@ -221,9 +260,9 @@ 	proj <- liftIO $ locateProject file
 	absFilename <- liftIO $ Dir.canonicalizePath file
 	inspect (FileModule absFilename proj) (fileInspection absFilename opts) $ do
-		docsMap <- case hdocsWorkaround of
-			True -> liftIO $ hdocsProcess absFilename opts
-			False -> liftIO $ fmap (fmap documentationMap . lookup absFilename) $ do
+		docsMap <- liftIO $ if hdocsWorkaround
+			then hdocsProcess absFilename opts
+			else fmap (fmap documentationMap . lookup absFilename) $ do
 				is <- E.catch (Doc.createInterfaces ([Doc.Flag_Verbosity "0", Doc.Flag_NoWarnings] ++ map Doc.Flag_OptGhc opts) [absFilename]) noReturn
 				forM is $ \i -> do
 					mfile <- Dir.canonicalizePath $ Doc.ifaceOrigFilename i
@@ -244,21 +283,27 @@ fileInspection :: FilePath -> [String] -> ErrorT String IO Inspection
 fileInspection f opts = do
 	tm <- liftIO $ Dir.getModificationTime f
-	return $ InspectionAt (utcTimeToPOSIXSeconds tm) opts
+	return $ InspectionAt (utcTimeToPOSIXSeconds tm) $ sort $ nub opts
 
 -- | Enumerate project dirs
 projectDirs :: Project -> ErrorT String IO [Extensions FilePath]
 projectDirs p = do
 	p' <- loadProject p
-	return $ map (fmap (projectPath p' </>)) $ maybe [] sourceDirs $ projectDescription p'
+	return $ nub $ map (fmap (normalise . (projectPath p' </>))) $ maybe [] sourceDirs $ projectDescription p'
 
 -- | Enumerate project source files
 projectSources :: Project -> ErrorT String IO [Extensions FilePath]
 projectSources p = do
 	dirs <- projectDirs p
 	let
-		enumHs = liftM (filter haskellSource) . traverseDirectory
-	liftIO $ liftM concat $ mapM (liftM sequenceA . traverse (liftIO . enumHs)) dirs
+		enumCabals = liftM (map takeDirectory . filter cabalFile) . traverseDirectory
+		dirs' = map entity dirs
+	-- enum inner projects and dont consider them as part of this project
+	subProjs <- liftIO $ liftM (delete (projectPath p) . nub . concat) $ mapM (liftIO . enumCabals) dirs'
+	let
+		enumHs = liftM (filter thisProjectSource) . traverseDirectory
+		thisProjectSource h = haskellSource h && not (any (`isParent` h) subProjs)
+	liftIO $ liftM (nub . concat) $ mapM (liftM sequenceA . traverse (liftIO . enumHs)) dirs
 
 -- | Inspect project
 inspectProject :: [String] -> Project -> ErrorT String IO (Project, [InspectedModule])
src/HsDev/Project.hs view
@@ -282,6 +282,7 @@ data Extensions a = Extensions {
 	extensions :: [Extension],
 	entity :: a }
+		deriving (Eq, Read, Show)
 
 instance Functor Extensions where
 	fmap f (Extensions e x) = Extensions e (f x)
@@ -326,7 +327,7 @@ 
 -- | Returns source dirs for library, executables and tests
 sourceDirs :: ProjectDescription -> [Extensions FilePath]
-sourceDirs = concatMap dirs . infos where
+sourceDirs = nub . concatMap dirs . infos where
 	dirs i = map (`withExtensions` i) $ infoSourceDirs i
 
 parseDT :: Distribution.Text.Text a => String -> String -> Parser a
src/HsDev/Scan.hs view
@@ -60,7 +60,9 @@ 
 -- | Scan module
 scanModule :: [String] -> ModuleLocation -> ErrorT String IO InspectedModule
-scanModule opts (FileModule f _) = inspectFile opts f >>= traverse (runGhcMod defaultOptions . inferTypes opts Cabal)
+scanModule opts (FileModule f _) = inspectFile opts f >>= traverse infer' where
+	infer' m = mapErrorT (withCurrentDirectory (sourceModuleRoot (moduleName m) f)) $
+		runGhcMod defaultOptions $ inferTypes opts Cabal m
 scanModule opts (CabalModule c p n) = browse opts c n p
 scanModule _ (ModuleSource _) = throwError "Can inspect only modules in file or cabal"
 
src/HsDev/Scan/Browse.hs view
@@ -3,10 +3,10 @@ 	browseFilter, browse
 	) where
 
-import Control.Arrow
 import Control.Monad.Error
+import Data.List (nub)
 import Data.Maybe
-import qualified Data.Map as M
+import Data.String (fromString)
 import Text.Read (readMaybe)
 
 import HsDev.Cabal
@@ -50,30 +50,37 @@ browseModule cabal m = do
 	mi <- lift (GHC.getModuleInfo m) >>= maybe (throwError "Can't find module info") return
 	ds <- mapM (toDecl mi) (GHC.modInfoExports mi)
-	return $ Module {
-		moduleName = GHC.moduleNameString (GHC.moduleName m),
+	let
+		thisModule = GHC.moduleNameString (GHC.moduleName m)
+	return Module {
+		moduleName = fromString thisModule,
 		moduleDocs = Nothing,
-		moduleLocation = mloc,
-		moduleExports = [],
-		moduleImports = [],
-		moduleDeclarations = M.fromList (map (declarationName &&& id) ds) }
+		moduleLocation = thisLoc,
+		moduleExports = Just $ map (ExportName Nothing . declarationName) ds,
+		moduleImports = [import_ iname | iname <- nub (mapMaybe definedModule ds), iname /= fromString thisModule],
+		moduleDeclarations = sortDeclarations ds }
 	where
-		mloc = CabalModule cabal (readMaybe $ GHC.packageIdString $ GHC.modulePackageId m) (GHC.moduleNameString $ GHC.moduleName m)
+		thisLoc = moduleIdLocation $ mloc m
+		mloc m' = ModuleId (fromString mname') $
+			CabalModule cabal (readMaybe $ GHC.packageIdString $ GHC.modulePackageId m') mname'
+			where
+				mname' = GHC.moduleNameString $ GHC.moduleName m'
 		toDecl minfo n = do
 			tyInfo <- lift $ GHC.modInfoLookupName minfo n
 			tyResult <- lift $ maybe (inModuleSource n) (return . Just) tyInfo
 			dflag <- lift GHC.getSessionDynFlags
-			return $ Declaration
-				(GHC.getOccString n)
-				Nothing
-				Nothing
-				(fromMaybe (Function Nothing []) (tyResult >>= showResult dflag))
+			let
+				decl' = decl (fromString $ GHC.getOccString n) $ fromMaybe
+					(Function Nothing [])
+					(tyResult >>= showResult dflag)
+			return $ decl' `definedIn` mloc (GHC.nameModule n)
+		definedModule = fmap moduleIdName . declarationDefined
 		showResult :: GHC.DynFlags -> GHC.TyThing -> Maybe DeclarationInfo
-		showResult dflags (GHC.AnId i) = Just $ Function (Just $ formatType dflags GHC.varType i) []
+		showResult dflags (GHC.AnId i) = Just $ Function (Just $ fromString $ formatType dflags GHC.varType i) []
 		showResult dflags (GHC.AConLike c) = case c of
-			GHC.RealDataCon d -> Just $ Function (Just $ formatType dflags GHC.dataConRepType d) []
-			GHC.PatSynCon p -> Just $ Function (Just $ formatType dflags GHC.patSynType p) []
-		showResult _ (GHC.ATyCon t) = Just $ tcon $ TypeInfo Nothing (map GHC.getOccString $ GHC.tyConTyVars t) Nothing where
+			GHC.RealDataCon d -> Just $ Function (Just $ fromString $ formatType dflags GHC.dataConRepType d) []
+			GHC.PatSynCon p -> Just $ Function (Just $ fromString $ formatType dflags GHC.patSynType p) []
+		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
@@ -124,4 +131,4 @@ styleUnqualified = GHC.mkUserStyle GHC.neverQualify GHC.AllTheWay
 
 tryT :: (Monad m, Error e) => ErrorT e m a -> ErrorT e m (Maybe a)
-tryT act = catchError (liftM Just $ act) (const $ return Nothing)
+tryT act = catchError (liftM Just act) (const $ return Nothing)
src/HsDev/Server/Commands.hs view
@@ -21,6 +21,7 @@ import Control.Monad.Error
 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.Either (isLeft)
@@ -29,7 +30,9 @@ import Data.Monoid
 import Data.Traversable (traverse)
 import Network.Socket hiding (connect)
-import qualified Network.Socket as Net
+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.IO
@@ -40,6 +43,7 @@ import qualified Control.Concurrent.FiniteChan as F
 import Data.Lisp
 import System.Console.Cmd hiding (run)
+import Text.Format ((~~), (%))
 
 import qualified HsDev.Cache.Structured as SC
 import qualified HsDev.Client.Commands as Client
@@ -59,7 +63,7 @@ import System.Process
 import System.Win32.FileMapping.Memory (withMapFile, readMapFile)
 import System.Win32.FileMapping.NamePool
-import System.Win32.PowerShell (translateArg)
+import System.Win32.PowerShell (escape, quote, quoteDouble)
 #else
 import System.Posix.Process
 import System.Posix.IO
@@ -87,17 +91,26 @@ 			let
 				args = ["run"] ++ toArgs (Args [] 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 $ $ -WindowStyle Hidden -WorkingDirectory $ } catch { $$_.Exception, $$_.InvocationInfo.Line }" ~~ (
+					escape quote myExe %
+					(intercalate ", " (map biescape args)) %
+					escape quote curDir)
 			r <- readProcess "powershell" [
 				"-Command",
-				unwords [
-					"&", "{", "start-process",
-					translateArg myExe,
-					intercalate ", " (map translateArg args),
-					"-WindowStyle Hidden",
-					"}"]] ""
+				script] ""
 			if all isSpace r
 				then putStrLn $ "Server started at port " ++ (fromJust $ arg "port" sopts)
-				else putStrLn $ "Failed to start server: " ++ r
+				else mapM_ putStrLn [
+					"Failed to start server",
+					"\tCommand: " ++ script,
+					"\tResult: " ++ r]
 #else
 			let
 				forkError :: SomeException -> IO ()
@@ -131,9 +144,8 @@ 				s <- socket AF_INET Stream defaultProtocol
 				addr' <- inet_addr "127.0.0.1"
 				Net.connect s $ SockAddrInet (fromIntegral $ fromJust $ iarg "port" sopts) addr'
-				bracket (socketToHandle s ReadWriteMode) hClose $ \h ->
-					processClientHandle s h (copts {
-						commandExit = killThread me })
+				flip finally (close s) $ processClientSocket s (copts {
+					commandExit = killThread me })
 			| otherwise = runServer sopts $ \copts -> do
 				commandLog copts $ "Server started at port " ++ fromJust (arg "port" sopts)
 
@@ -155,23 +167,23 @@ 					forever $ logIO "accept client exception: " (commandLog copts) $ do
 						s' <- fst <$> accept s
 						void $ forkIO $ logIO (show s' ++ " exception: ") (commandLog copts) $
-							bracket (socketToHandle s' ReadWriteMode) hClose $ \h ->
-							bracket newEmptyMVar (`putMVar` ()) $ \done -> do
-								me <- myThreadId
-								let
-									timeoutWait = do
-										notDone <- isEmptyMVar done
-										when notDone $ do
-											void $ forkIO $ do
-												threadDelay 1000000
-												void $ tryPutMVar done ()
-												killThread me
-											takeMVar done
-									waitForever = forever $ hGetLineBS h
-								F.putChan clientChan timeoutWait
-								processClientHandle s' h (copts {
-									commandHold = waitForever,
-									commandExit = serverStop })
+							flip finally (close s') $
+								bracket newEmptyMVar (`putMVar` ()) $ \done -> do
+									me <- myThreadId
+									let
+										timeoutWait = do
+											notDone <- isEmptyMVar done
+											when notDone $ do
+												void $ forkIO $ do
+													threadDelay 1000000
+													void $ tryPutMVar done ()
+													killThread me
+												takeMVar done
+										-- waitForever = forever $ hGetLineBS h
+									F.putChan clientChan timeoutWait
+									processClientSocket s' (copts {
+										-- commandHold = waitForever,
+										commandExit = serverStop })
 
 				takeMVar waitListen
 				DB.readAsync (commandDatabase copts) >>= writeCache sopts (commandLog copts)
@@ -283,14 +295,14 @@ 			s <- socket AF_INET Stream defaultProtocol
 			addr' <- inet_addr "127.0.0.1"
 			Net.connect s (SockAddrInet (fromIntegral $ fromJust $ iarg "port" copts) addr')
-			h <- socketToHandle s ReadWriteMode
-			L.hPutStrLn h $ encode $ Message Nothing $ reqCall `M.withOpts` [
-				"current-directory" %-- curDir,
-				"data" %-? (fromUtf8 . encode <$> stdinData),
-				"timeout" %-? (iarg "timeout" copts :: Maybe Integer),
-				if flagSet "silent" copts then hoist "silent" else mempty]
-			hFlush h
-			peekResponse h
+			bracket (socketToHandle s ReadWriteMode) hClose $ \h -> do
+				L.hPutStrLn h $ encode $ Message Nothing $ reqCall `M.withOpts` [
+					"current-directory" %-- curDir,
+					"data" %-? (fromUtf8 . encode <$> stdinData),
+					"timeout" %-? (iarg "timeout" copts :: Maybe Integer),
+					if flagSet "silent" copts then hoist "silent" else mempty]
+				hFlush h
+				peekResponse h
 
 		peekResponse h = do
 			resp <- hGetLineBS h
@@ -452,10 +464,38 @@ 			commandLog copts $ name ++ " disconnected"
 			join $ takeMVar var
 
+{-
 -- | Process client by Handle
 processClientHandle :: Show a => a -> Handle -> CommandOptions -> IO ()
 processClientHandle n h = processClient (show n) (hGetLineBS h) (\s -> L.hPutStrLn h s >> hFlush h)
+-}
 
+-- | Process client by socket
+processClientSocket :: Socket -> CommandOptions -> IO ()
+processClientSocket s copts = do
+	recvChan <- F.newChan
+	void $ forkIO $ finally
+		(Net.getContents s >>= mapM_ (F.putChan recvChan) . L.lines)
+		(F.closeChan recvChan)
+	processClient (show s) (getChan_ recvChan) (sendLine s) (copts {
+		commandHold = forever (getChan_ recvChan) })
+	where
+		getChan_ :: F.Chan a -> IO a
+		getChan_ = F.getChan >=> maybe noData return
+		noData :: IO a
+		noData = throwIO $ userError "Receive chan closed"
+		-- 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)
+
 -- | Perform action on cache
 withCache :: Opts String -> a -> (FilePath -> IO a) -> IO a
 withCache sopts v onCache = case arg "cache" sopts of
@@ -466,7 +506,14 @@ writeCache sopts logMsg' d = withCache sopts () $ \cdir -> do
 	logMsg' $ "writing cache to " ++ cdir
 	logIO "cache writing exception: " logMsg' $ do
-		SC.dump cdir $ structurize d
+		let
+			sd = structurize d
+		SC.dump cdir sd
+		forM_ (M.keys (structuredCabals sd)) $ \c -> logMsg' ("cache write: cabal " ++ show c)
+		forM_ (M.keys (structuredProjects sd)) $ \p -> logMsg' ("cache write: project " ++ p)
+		case allModules (structuredFiles sd) of
+			[] -> return ()
+			ms -> logMsg' $ "cache write: " ++ show (length ms) ++ " files"
 	logMsg' $ "cache saved to " ++ cdir
 
 readCache :: Opts String -> (String -> IO ()) -> (FilePath -> ErrorT String IO Structured) -> IO (Maybe Database)
src/HsDev/Server/Message.hs view
@@ -5,8 +5,8 @@ 	messagesById,
 	Request(..), requestToArgs,
 	withOpts, withoutOpts,
-	Notification(..), Result(..),
-	Response, isNotification, notification, result, responseError,
+	Notification(..), Result(..), ResultPart(..),
+	Response, isNotification, notification, result, responseError, resultPart,
 	groupResponses, responsesById
 	) where
 
@@ -39,7 +39,7 @@ 
 instance FromJSON a => FromJSON (Message a) where
 	parseJSON = withObject "message" $ \v ->
-		Message <$> (fmap join (v .::? "id")) <*> parseJSON (Object v)
+		Message <$> fmap join (v .::? "id") <*> parseJSON (Object v)
 
 instance Foldable Message where
 	foldMap f (Message _ m) = f m
@@ -110,6 +110,15 @@ 		Result <$> v .:: "result",
 		Error <$> v .:: "error" <*> v .:: "details"]
 
+-- | 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"
+
 type Response = Either Notification Result
 
 isNotification :: Response -> Bool
@@ -123,6 +132,9 @@ 
 responseError :: String -> [Pair] -> Response
 responseError e ds = Right $ Error e $ M.fromList $ map (first unpack) ds
+
+resultPart :: ToJSON a => a  -> Notification
+resultPart = Notification . toJSON . ResultPart . toJSON
 
 instance ToJSON Response where
 	toJSON (Left n) = toJSON n
src/HsDev/Server/Types.hs view
@@ -8,6 +8,7 @@ 
 import Control.Applicative
 import Control.Monad.Error
+import Control.Monad.Reader
 import Data.Aeson hiding (Result, Error)
 import Data.Aeson.Types (Pair)
 import qualified Data.HashMap.Strict as HM (null)
@@ -18,7 +19,7 @@ import HsDev.Project
 import HsDev.Symbols
 import HsDev.Server.Message
-import HsDev.Tools.GhcMod (OutputMessage, TypedRegion, GhcModT)
+import HsDev.Tools.GhcMod (OutputMessage, TypedRegion, WorkerMap)
 import HsDev.Tools.Ghc.Worker (Worker, Ghc)
 
 #if mingw32_HOST_OS
@@ -36,8 +37,8 @@ #if mingw32_HOST_OS
 	commandMmapPool :: Maybe Pool,
 #endif
-	commandGhc :: Worker (Ghc ()),
-	commandGhcMod :: Worker (FilePath, GhcModT IO ()),
+	commandGhc :: Worker Ghc,
+	commandGhcMod :: Worker (ReaderT WorkerMap IO),
 	commandNotify :: Notification -> IO (),
 	commandLink :: IO (),
 	commandHold :: IO (),
src/HsDev/Symbols.hs view
@@ -3,11 +3,16 @@ 
 module HsDev.Symbols (
 	-- * Information
-	Import(..),
+	Export(..), export,
+	ImportList(..), passImportList,
+	Import(..), importName, import_,
 	Symbol(..),
-	ModuleId(..), Module(..), moduleLocals, moduleLocalDeclarations, moduleModuleDeclarations, moduleId,
+	ModuleId(..), unnamedModuleId,
+	Module(..), sortDeclarations, moduleLocals,
+	setDefinedIn, dropExternals, clearDefinedIn,
+	moduleLocalDeclarations, moduleModuleDeclarations, moduleId,
 	Locals(..),
-	Declaration(..), declarationLocals,
+	Declaration(..), decl, definedIn, declarationLocals, scopes,
 	TypeInfo(..),
 	DeclarationInfo(..),
 	ModuleDeclaration(..),
@@ -24,6 +29,8 @@ 	Canonicalize(..),
 	locateProject,
 	locateSourceDir,
+	sourceModuleRoot,
+	importedModulePath,
 
 	-- * Modifiers
 	addDeclaration,
@@ -43,12 +50,13 @@ import Control.Monad.Error
 import Data.Aeson
 import Data.List
-import Data.Map (Map)
 import Data.Maybe (fromMaybe)
-import qualified Data.Map as M
 import Data.Monoid (Monoid(mempty))
+import Data.Ord (comparing)
 import Data.Time.Clock.POSIX (POSIXTime)
 import Data.Foldable (Foldable(..))
+import Data.Text (Text, unpack)
+import qualified Data.Text as T (concat, split, unpack)
 import Data.Traversable (Traversable(..))
 import System.Directory
 import System.FilePath
@@ -58,25 +66,88 @@ import HsDev.Project
 import HsDev.Util (tab, tabs, (.::))
 
+-- | Module export
+data Export = ExportName (Maybe Text) Text | ExportModule Text
+	deriving (Eq, Ord)
+
+instance NFData Export where
+	rnf (ExportName q n) = rnf q `seq` rnf n
+	rnf (ExportModule m) = rnf m
+
+instance Show Export where
+	show (ExportName Nothing n) = unpack n
+	show (ExportName (Just q) n) = unpack q ++ "." ++ unpack n
+	show (ExportModule m) = "module " ++ unpack m
+
+instance ToJSON Export where
+	toJSON (ExportName q n) = object ["module" .= q, "name" .= n]
+	toJSON (ExportModule m) = object ["module" .= m]
+
+instance FromJSON Export where
+	parseJSON = withObject "export" $ \v ->
+		(ExportName <$> (v .:: "module") <*> (v .:: "name")) <|>
+		(ExportModule <$> (v .:: "module"))
+
+-- | 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
+
+-- | Import list
+data ImportList = ImportList {
+	hidingList :: Bool,
+	importSpec :: [Text] }
+		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 unpack ls) ++ ")"
+
+instance ToJSON ImportList where
+	toJSON (ImportList h ls) = object [
+		"hiding" .= h,
+		"spec" .= ls]
+
+instance FromJSON ImportList where
+	parseJSON = withObject "import-list" $ \v -> ImportList <$>
+		v .:: "hiding" <*>
+		v .:: "spec"
+
+-- | Check whether name pass import list
+passImportList :: ImportList -> Text -> Bool
+passImportList (ImportList hiding names) n
+	| hiding = n `notElem` names
+	| otherwise = n `elem` names
+
 -- | Module import
 data Import = Import {
-	importModuleName :: String,
+	importModuleName :: Text,
 	importIsQualified :: Bool,
-	importAs :: Maybe String,
+	importAs :: Maybe Text,
+	importList :: Maybe ImportList,
 	importPosition :: Maybe Position }
 		deriving (Eq, Ord)
 
 instance NFData Import where
-	rnf (Import m q a l) = rnf m `seq` rnf q `seq` rnf a `seq` rnf l
+	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 = "import " ++ (if importIsQualified i then "qualified " else "") ++ importModuleName i ++ maybe "" (" as " ++) (importAs i)
+	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 [
 		"name" .= importModuleName i,
 		"qualified" .= importIsQualified i,
 		"as" .= importAs i,
+		"import-list" .= importList i,
 		"pos" .= importPosition i]
 
 instance FromJSON Import where
@@ -84,10 +155,19 @@ 		v .:: "name" <*>
 		v .:: "qualified" <*>
 		v .:: "as" <*>
+		v .:: "import-list" <*>
 		v .:: "pos"
 
+-- | Get import module name
+importName :: Import -> Text
+importName i = fromMaybe (importModuleName i) $ importAs i
+
+-- | Simple import
+import_ :: Text -> Import
+import_ n = Import n False Nothing Nothing Nothing
+
 -- | Imported module can be accessed via qualifier
-importQualifier :: Maybe String -> Import -> Bool
+importQualifier :: Maybe Text -> Import -> Bool
 importQualifier Nothing i
 	| not (importIsQualified i) = True
 	| otherwise = False
@@ -123,7 +203,7 @@ 
 -- | Module id
 data ModuleId = ModuleId {
-	moduleIdName :: String,
+	moduleIdName :: Text,
 	moduleIdLocation :: ModuleLocation }
 		deriving (Eq, Ord)
 
@@ -131,7 +211,7 @@ 	rnf (ModuleId n l) = rnf n `seq` rnf l
 
 instance Show ModuleId where
-	show (ModuleId n l) = "module " ++ n ++ " from " ++ show l
+	show (ModuleId n l) = "module " ++ unpack n ++ " from " ++ show l
 
 instance ToJSON ModuleId where
 	toJSON m = object [
@@ -143,14 +223,17 @@ 		v .:: "name" <*>
 		v .:: "location"
 
+unnamedModuleId :: ModuleLocation -> ModuleId
+unnamedModuleId = ModuleId ""
+
 -- | Module
 data Module = Module {
-	moduleName :: String,
-	moduleDocs :: Maybe String,
+	moduleName :: Text,
+	moduleDocs :: Maybe Text,
 	moduleLocation :: ModuleLocation,
-	moduleExports :: [String],
+	moduleExports :: Maybe [Export],
 	moduleImports :: [Import],
-	moduleDeclarations :: Map String Declaration }
+	moduleDeclarations :: [Declaration] }
 		deriving (Ord)
 
 instance ToJSON Module where
@@ -160,7 +243,7 @@ 		"location" .= moduleLocation m,
 		"exports" .= moduleExports m,
 		"imports" .= moduleImports m,
-		"declarations" .= M.elems (moduleDeclarations m)]
+		"declarations" .= moduleDeclarations m]
 
 instance FromJSON Module where
 	parseJSON = withObject "module" $ \v -> Module <$>
@@ -169,7 +252,7 @@ 		v .:: "location" <*>
 		v .:: "exports" <*>
 		v .:: "imports" <*>
-		((M.fromList . map (declarationName &&& id)) <$>v .:: "declarations")
+		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
@@ -179,26 +262,42 @@ 
 instance Show Module where
 	show m = unlines $ filter (not . null) [
-		"module " ++ moduleName m,
+		"module " ++ unpack (moduleName m),
 		"\tlocation: " ++ show (moduleLocation m),
-		"\texports: " ++ intercalate ", " (moduleExports m),
+		"\texports: " ++ maybe "*" (intercalate ", " . map show) (moduleExports m),
 		"\timports:",
 		unlines $ map (tab 2 . show) $ moduleImports m,
 		"\tdeclarations:",
-		unlines $ map (tabs 2 . show) $ M.elems (moduleDeclarations m),
-		maybe "" ("\tdocs: " ++) (moduleDocs m)]
+		unlines $ map (tabs 2 . show) $ moduleDeclarations m,
+		maybe "" (("\tdocs: " ++) . unpack) (moduleDocs m)]
 
+sortDeclarations :: [Declaration] -> [Declaration]
+sortDeclarations = sortBy (comparing declarationName)
+
 -- | Bring locals to top
 moduleLocals :: Module -> Module
 moduleLocals m = m { moduleDeclarations = moduleLocalDeclarations m }
 
+-- | Set all declaration `definedIn` to this module
+setDefinedIn :: Module -> Module
+setDefinedIn m = m {
+	moduleDeclarations = map (`definedIn` moduleId m) (moduleDeclarations m) }
+
+-- | Drop all declarations, that not defined in this module
+dropExternals :: Module -> Module
+dropExternals m = m {
+	moduleDeclarations = filter ((/= Just (moduleId m)) . declarationDefined) (moduleDeclarations m) }
+
+-- | Clear `definedIn` information
+clearDefinedIn :: Module -> Module
+clearDefinedIn m = m {
+	moduleDeclarations = map (\d -> d { declarationDefined = Nothing }) (moduleDeclarations m) }
+
 -- | Get declarations with locals
-moduleLocalDeclarations :: Module -> Map String Declaration
+moduleLocalDeclarations :: Module -> [Declaration]
 moduleLocalDeclarations =
-	M.fromList .
-	map (declarationName &&& id) . 
+	sortDeclarations .
 	concatMap declarationLocals' .
-	M.elems .
 	moduleDeclarations
 	where
 		declarationLocals' :: Declaration -> [Declaration]
@@ -206,7 +305,7 @@ 
 -- | Get list of declarations as ModuleDeclaration
 moduleModuleDeclarations :: Module -> [ModuleDeclaration]
-moduleModuleDeclarations m = [ModuleDeclaration (moduleId m) d | d <- M.elems (moduleDeclarations m)]
+moduleModuleDeclarations m = [ModuleDeclaration (moduleId m) d | d <- moduleDeclarations m]
 
 -- Make ModuleId by Module
 moduleId :: Module -> ModuleId
@@ -220,24 +319,29 @@ 
 -- | Declaration
 data Declaration = Declaration {
-	declarationName :: String,
-	declarationDocs :: Maybe String,
+	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 d l x) = rnf n `seq` rnf d `seq` rnf l `seq` rnf x
+	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: " ++) $ declarationDocs d,
+		maybe "" (("\tdocs: " ++) . unpack) $ declarationDocs d,
+		maybe "" (("\tdefined in: " ++) . show) $ declarationDefined d,
 		maybe "" (("\tlocation: " ++ ) . show) $ declarationPosition d]
 
 instance ToJSON Declaration where
 	toJSON d = object [
 		"name" .= declarationName d,
+		"defined" .= declarationDefined d,
+		"imported" .= declarationImported d,
 		"docs" .= declarationDocs d,
 		"pos" .= declarationPosition d,
 		"decl" .= declaration d]
@@ -245,23 +349,39 @@ instance FromJSON Declaration where
 	parseJSON = withObject "declaration" $ \v -> Declaration <$>
 		v .:: "name" <*>
+		v .:: "defined" <*>
+		v .:: "imported" <*>
 		v .:: "docs" <*>
 		v .:: "pos" <*>
 		v .:: "decl"
 
 instance Locals Declaration where
 	locals = locals . declaration
-	where_ d ds = d { declaration = where_ (declaration d) ds }
+	where_ d ds = d { declaration = declaration d `where_` ds }
 
+decl :: Text -> DeclarationInfo -> Declaration
+decl n = Declaration n Nothing Nothing Nothing Nothing
+
+definedIn :: Declaration -> ModuleId -> Declaration
+definedIn d m = d { declarationDefined = Just m }
+
 declarationLocals :: Declaration -> [Declaration]
 declarationLocals d = map prefix' $ locals $ declaration d where
-	prefix' decl = decl { declarationName = declarationName decl }
+	prefix' decl' = decl' { declarationName = declarationName decl' }
 
--- | Common info for type/newtype/data/class
+-- | Get scopes of @Declaration@, where @Nothing@ is global scope
+scopes :: Declaration -> [Maybe Text]
+scopes d = globalScope $ map (Just . importName) is where
+	is = fromMaybe [] $ declarationImported d
+	globalScope
+		| any (not . importIsQualified) is = (Nothing :)
+		| otherwise = id
+
+-- | Common info for type, newtype, data and class
 data TypeInfo = TypeInfo {
-	typeInfoContext :: Maybe String,
-	typeInfoArgs :: [String],
-	typeInfoDefinition :: Maybe String }
+	typeInfoContext :: Maybe Text,
+	typeInfoArgs :: [Text],
+	typeInfoDefinition :: Maybe Text }
 		deriving (Eq, Ord, Read, Show)
 
 instance NFData TypeInfo where
@@ -280,11 +400,16 @@ 		v .:: "def"
 
 showTypeInfo :: TypeInfo -> String -> String -> String
-showTypeInfo ti pre name = pre ++ maybe "" (++ " =>") (typeInfoContext ti) ++ " " ++ name ++ " " ++ unwords (typeInfoArgs ti) ++ maybe "" (" = " ++) (typeInfoDefinition ti)
+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 String, localDeclarations :: [Declaration] } |
+	Function { functionType :: Maybe Text, localDeclarations :: [Declaration] } |
 	Type { typeInfo :: TypeInfo } |
 	NewType { newTypeInfo :: TypeInfo } |
 	Data { dataInfo :: TypeInfo } |
@@ -325,7 +450,7 @@ 	where_ d _ = d
 
 -- | Get function type of type info
-declarationInfo :: DeclarationInfo -> Either (Maybe String, [Declaration]) TypeInfo
+declarationInfo :: DeclarationInfo -> Either (Maybe Text, [Declaration]) TypeInfo
 declarationInfo (Function t ds) = Left (t, ds)
 declarationInfo (Type ti) = Right ti
 declarationInfo (NewType ti) = Right ti
@@ -375,9 +500,10 @@ 		v .:: "declaration"
 
 -- | Returns qualified name of symbol
-qualifiedName :: ModuleId -> Declaration -> String
-qualifiedName m d = moduleIdName m ++ "." ++ declarationName d
+qualifiedName :: ModuleId -> Declaration -> Text
+qualifiedName m d = T.concat [moduleIdName m, ".", declarationName d]
 
+-- | Canonicalize all paths within something
 class Canonicalize a where
 	canonicalize :: a -> IO a
 
@@ -417,31 +543,48 @@ 	proj <- MaybeT $ fmap (either (const Nothing) Just) $ runErrorT $ loadProject p
 	MaybeT $ return $ findSourceDir proj file
 
+-- | 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
+
 -- | Add declaration to module
 addDeclaration :: Declaration -> Module -> Module
-addDeclaration decl m = m { moduleDeclarations = decls' } where
-	decls' = M.insert (declarationName decl) decl $ moduleDeclarations m
+addDeclaration decl' m = m { moduleDeclarations = decls' } where
+	decls' = sortDeclarations $ decl' : moduleDeclarations m
 
 -- | Unalias import name
-unalias :: Module -> String -> [String]
+unalias :: Module -> Text -> [Text]
 unalias m alias = [importModuleName i | i <- moduleImports m, importAs i == Just alias]
 
 instance Documented ModuleId where
-	brief m = moduleIdName m ++ " in " ++ show (moduleIdLocation m)
+	brief m = unpack (moduleIdName m) ++ " in " ++ show (moduleIdLocation m)
 
 instance Documented Module where
-	brief m = moduleName m ++ " in " ++ show (moduleLocation m)
+	brief m = unpack (moduleName m) ++ " in " ++ show (moduleLocation m)
 	detailed m = unlines $ header ++ docs ++ cts where
 		header = [brief m, ""]
-		docs = maybe [] return $ moduleDocs 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 "" (" :: " ++) f
+		Left (f, _) -> name ++ maybe "" ((" :: " ++) . unpack) f
 		Right ti -> showTypeInfo ti (fromMaybe err $ declarationTypeName $ declaration d) name
 		where
-			name = declarationName d
+			name = unpack $ declarationName d
 			err = error "Impossible happened: declarationTypeName"
 
 instance Documented ModuleDeclaration where
@@ -449,8 +592,8 @@ 
 -- | Module contents
 moduleContents :: Module -> [String]
-moduleContents = map showDecl . M.elems . moduleDeclarations where
-	showDecl d = brief d ++ maybe "" (" -- " ++) (declarationDocs d)
+moduleContents = map showDecl . moduleDeclarations where
+	showDecl d = brief d ++ maybe "" ((" -- " ++) . unpack) (declarationDocs d)
 
 -- | Inspection data
 data Inspection =
src/HsDev/Symbols/Class.hs view
@@ -5,12 +5,14 @@ 	module HsDev.Symbols.Location
 	) where
 
+import Data.Text (Text)
+
 import HsDev.Symbols.Location
 
 class Symbol a where
-	symbolName :: a -> String
-	symbolQualifiedName :: a -> String
-	symbolDocs :: a -> Maybe String
+	symbolName :: a -> Text
+	symbolQualifiedName :: a -> Text
+	symbolDocs :: a -> Maybe Text
 	symbolLocation :: a -> Location
 
 symbolModuleLocation :: Symbol a => a -> ModuleLocation
src/HsDev/Symbols/Documented.hs view
@@ -3,6 +3,8 @@ 	defaultDetailed
 	) where
 
+import Data.Text (unpack)
+
 import HsDev.Symbols.Class
 
 -- | Documented symbol
@@ -15,7 +17,7 @@ defaultDetailed :: Documented a => a -> [String]
 defaultDetailed s = header ++ docs ++ loc where
 	header = [brief s, ""]
-	docs = maybe [] return $ symbolDocs s
+	docs = maybe [] (return . unpack) $ symbolDocs s
 	loc
 		| null mloc = []
 		| otherwise = ["Defined at " ++ mloc]
src/HsDev/Symbols/Location.hs view
@@ -1,11 +1,12 @@ {-# LANGUAGE OverloadedStrings #-}
 
 module HsDev.Symbols.Location (
-	ModulePackage(..), ModuleLocation(..), moduleSource, moduleCabalPackage,
+	ModulePackage(..), ModuleLocation(..), moduleSource, moduleProject_, moduleStandalone, moduleCabal_, moduleCabalPackage,
 	Position(..), Region(..), region, regionLines, regionStr,
 	Location(..),
 
 	packageOpt,
+	recalcTabs,
 
 	module HsDev.Cabal
 	) where
@@ -15,7 +16,7 @@ import Control.Monad (join)
 import Data.Aeson
 import Data.Char (isSpace, isDigit)
-import Data.List (intercalate)
+import Data.List (intercalate, findIndex)
 import Data.Maybe
 import Text.Read (readMaybe)
 
@@ -67,6 +68,18 @@ moduleSource (FileModule f _) = Just f
 moduleSource _ = Nothing
 
+moduleProject_ :: ModuleLocation -> Maybe Project
+moduleProject_ (FileModule _ p) = p
+moduleProject_ _ = Nothing
+
+moduleStandalone :: ModuleLocation -> Bool
+moduleStandalone (FileModule _ Nothing) = True
+moduleStandalone _ = False
+
+moduleCabal_ :: ModuleLocation -> Maybe Cabal
+moduleCabal_ (CabalModule c _ _) = Just c
+moduleCabal_ _ = Nothing
+
 moduleCabalPackage :: ModuleLocation -> Maybe ModulePackage
 moduleCabalPackage (CabalModule _ p _) = p
 moduleCabalPackage _ = Nothing
@@ -88,8 +101,8 @@ 
 instance FromJSON ModuleLocation where
 	parseJSON = withObject "module location" $ \v ->
-		(FileModule <$> v .:: "file" <*> ((fmap project) <$> (v .:: "project"))) <|>
-		(CabalModule <$> v .:: "cabal" <*> (fmap (join . fmap readMaybe) (v .:: "package")) <*> v .:: "name") <|>
+		(FileModule <$> v .:: "file" <*> (fmap project <$> (v .:: "project"))) <|>
+		(CabalModule <$> v .:: "cabal" <*> fmap (join . fmap readMaybe) (v .:: "package") <*> v .:: "name") <|>
 		(ModuleSource <$> v .:: "source")
 
 data Position = Position {
@@ -174,3 +187,17 @@ 
 packageOpt :: Maybe ModulePackage -> [String]
 packageOpt = maybeToList . fmap (("-package " ++) . packageName)
+
+-- | Recalc position to interpret '\t' as one symbol instead of 8
+recalcTabs :: String -> Position -> Position
+recalcTabs cts (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' = 8
+	charSize _ = 1
+ src/HsDev/Symbols/Resolve.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module HsDev.Symbols.Resolve (
+	ResolveM(..), ResolvedTree, ResolvedModule(..), scopeModule, exportsModule, resolvedTopScope,
+	resolve, resolveOne, resolveModule, exported, resolveImport,
+	mergeImported
+	) where
+
+import Control.Applicative
+import Control.Arrow
+import Control.Monad.Reader
+import Control.Monad.State
+import Data.Foldable (Foldable)
+import Data.Function (on)
+import Data.List (sortBy, groupBy, find)
+import Data.Map (Map)
+import qualified Data.Map as M
+import Data.Maybe (fromMaybe, maybeToList)
+import Data.Monoid (mconcat, mappend)
+import Data.Ord (comparing)
+import Data.String (fromString)
+import Data.Text (Text)
+import Data.Traversable (Traversable, traverse)
+
+import HsDev.Database
+import HsDev.Project
+import HsDev.Symbols
+import HsDev.Symbols.Util
+
+-- | Resolve monad uses existing @Database@ and @ResolvedTree@ as state.
+newtype ResolveM a = ResolveM { runResolveM :: ReaderT Database (State ResolvedTree) a }
+	deriving (Functor, Applicative, Monad, MonadState ResolvedTree, MonadReader Database)
+
+-- | 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] }
+
+-- | Make @Module@ with scope declarations
+scopeModule :: ResolvedModule -> Module
+scopeModule r = (resolvedModule r) { moduleDeclarations = resolvedScope r }
+
+-- | Make @Module@ with exported only declarations
+exportsModule :: ResolvedModule -> Module
+exportsModule r = (resolvedModule r) { moduleDeclarations = resolvedExports r }
+
+-- | Get top-level scope
+resolvedTopScope :: ResolvedModule -> [Declaration]
+resolvedTopScope = filter isTop . resolvedScope where
+	isTop :: Declaration -> Bool
+	isTop = any (not . importIsQualified) . fromMaybe [] . declarationImported
+
+-- | Resolve modules, function is not IO, so all file names must be canonicalized
+resolve :: (Traversable t, Foldable t) => Database -> t Module -> t ResolvedModule
+resolve db = flip evalState M.empty . flip runReaderT db . runResolveM . traverse resolveModule
+
+-- | 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 $ moduleId m) >>= maybe resolveModule' return where
+	resolveModule' = save $ case moduleLocation m of
+		CabalModule {} -> return ResolvedModule {
+			resolvedModule = m,
+			resolvedScope = moduleDeclarations m,
+			resolvedExports = moduleDeclarations m }
+		_ -> do
+			scope' <-
+				liftM ((thisDecls ++) . mergeImported . concat) .
+				mapM (resolveImport m) .
+				(import_ (fromString "Prelude") :) .
+				moduleImports $ m
+			let
+				exports' =
+					concatMap (exported scope') .
+					fromMaybe [] .
+					moduleExports $ m
+			return $ ResolvedModule m (sortDeclarations scope') (sortDeclarations exports')
+	thisDecls :: [Declaration]
+	thisDecls = map selfImport $ moduleDeclarations m
+	selfImport :: Declaration -> Declaration
+	selfImport d = d { declarationImported = Just [import_ $ moduleName m] }
+	save :: ResolveM ResolvedModule -> ResolveM ResolvedModule
+	save act = do
+		rm <- act
+		modify $ M.insert (moduleId (resolvedModule rm)) rm
+		return rm
+
+-- | Select declarations exported with @Export@
+exported :: [Declaration] -> Export -> [Declaration]
+exported ds (ExportName q n) = maybeToList $ find isExported ds where
+	isExported :: Declaration -> Bool
+	isExported decl' = declarationName decl' == n && case q of
+		Nothing -> any (not . importIsQualified) $ fromMaybe [] $ declarationImported decl'
+		Just q' -> any ((== q') . importName) $ fromMaybe [] $ declarationImported decl'
+exported ds (ExportModule m) =
+	filter (any (unqualBy m) . fromMaybe [] . declarationImported) ds
+	where
+		unqualBy :: Text -> Import -> Bool
+		unqualBy m' i = importName i == m' && not (importIsQualified i)
+
+-- | Bring declarations into scope
+resolveImport :: Module -> Import -> ResolveM [Declaration]
+resolveImport m i = liftM (map $ setImport i) resolveImport' where
+	resolveImport' :: ResolveM [Declaration]
+	resolveImport' = do
+		ms <- case moduleLocation m of
+			FileModule file proj -> do
+				db <- ask
+				let
+					proj' = proj >>= refineProject db
+				case proj' of
+					Nothing -> selectImport i [
+						inFile $ importedModulePath (moduleName m) file (importModuleName i),
+						byCabal]
+					Just p -> selectImport i [
+						inProject p,
+						inDepsOf' file p]
+			CabalModule cabal _ _ -> selectImport i [inCabal cabal]
+			ModuleSource _ -> selectImport i [byCabal]
+		liftM (filterImportList . concatMap resolvedExports) $ mapM resolveModule ms
+	setImport :: Import -> Declaration -> Declaration
+	setImport i' d' = d' { declarationImported = Just [i'] `mappend` declarationImported d' }
+	selectImport :: Import -> [ModuleId -> Bool] -> ResolveM [Module]
+	selectImport i' fs = liftM (selectModules select') ask where
+		select' md = moduleName md == importModuleName i' && any ($ moduleId md) (byImport i' : fs)
+	filterImportList :: [Declaration] -> [Declaration]
+	filterImportList = case importList i of
+		Nothing -> id
+		Just il -> filter (passImportList il . declarationName)
+	byImport :: Import -> ModuleId -> Bool
+	byImport i' m' = importModuleName i' == moduleIdName m'
+	deps f p = maybe [] infoDepends $ fileTarget p f
+	inDepsOf' f p m' = any (`inPackage` m') (deps f p)
+
+-- | Merge imported declarations
+mergeImported :: [Declaration] -> [Declaration]
+mergeImported =
+	map merge' .
+	groupBy ((==) `on` declId) .
+	sortBy (comparing declId)
+	where
+		declId :: Declaration -> (Text, Maybe ModuleId)
+		declId = declarationName &&& declarationDefined
+		merge' :: [Declaration] -> Declaration
+		merge' [] = error "mergeImported: impossible"
+		merge' ds@(d:_) = d { declarationImported = mconcat $ map declarationImported ds }
src/HsDev/Symbols/Util.hs view
@@ -1,6 +1,6 @@ module HsDev.Symbols.Util (
-	packageOf, projectOf,
-	inProject, inCabal, inPackage, inVersion, inFile, inModuleSource, inModule, byFile, byCabal, standalone,
+	projectOf, cabalOf, packageOf,
+	inProject, inDepsOf, inCabal, inPackage, inVersion, inFile, inModuleSource, inModule, byFile, byCabal, standalone,
 	imports, qualifier, imported, visible, inScope,
 	newestPackage,
 	sourceModule, visibleModule, preferredModule, uniqueModules,
@@ -12,6 +12,7 @@ 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
@@ -23,6 +24,12 @@ 	FileModule _ proj -> proj
 	_ -> Nothing
 
+-- | Get module cabal
+cabalOf :: ModuleId -> Maybe Cabal
+cabalOf m = case moduleIdLocation m of
+	CabalModule c _ _ -> Just c
+	_ -> Nothing
+
 -- | Get module package
 packageOf :: ModuleId -> Maybe ModulePackage
 packageOf m = case moduleIdLocation m of
@@ -33,6 +40,10 @@ inProject :: Project -> ModuleId -> Bool
 inProject p m = projectOf m == Just p
 
+-- | Check if module in deps of project target
+inDepsOf :: Info -> ModuleId -> Bool
+inDepsOf i m = any (`inPackage` m) $ infoDepends i
+
 -- | Check if module in cabal
 inCabal :: Cabal -> ModuleId -> Bool
 inCabal c m = case moduleIdLocation m of
@@ -64,7 +75,7 @@ 
 -- | Check if declaration is in module
 inModule :: String -> ModuleId -> Bool
-inModule mname m = mname == moduleIdName m
+inModule mname m = fromString mname == moduleIdName m
 
 -- | Check if module defined in file
 byFile :: ModuleId -> Bool
@@ -90,7 +101,10 @@ 
 -- | Get list of imports, which can be accessed with specified qualifier or unqualified
 qualifier :: Module -> Maybe String -> [Import]
-qualifier m q = filter (importQualifier q) $ (Import "Prelude" False Nothing Nothing : Import (moduleName m) False Nothing Nothing : imports m)
+qualifier m q = filter (importQualifier (fmap fromString q)) $
+	import_ (fromString "Prelude") :
+	import_ (moduleName m) :
+	imports m
 
 -- | Check if module imported via imports specified
 imported :: ModuleId -> [Import] -> Bool
src/HsDev/Tools/Base.hs view
@@ -2,7 +2,7 @@ 	Result, ToolM,
 	runWait, runWait_,
 	tool, tool_,
-	match,
+	matchRx, splitRx, replaceRx,
 	at,
 	inspect,
 	-- * Read parse utils
@@ -12,10 +12,12 @@ 
 import Control.Monad.Error
 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.RegexPR (matchRegexPR)
+import Text.Regex.PCRE ((=~), MatchResult(..))
 
 import HsDev.Symbols
 import HsDev.Util (liftIOErrors)
@@ -41,10 +43,24 @@ tool_ :: FilePath -> [String] -> ToolM String
 tool_ name args = tool name args ""
 
-match :: String -> String -> Maybe (Int -> Maybe String)
-match pat str = do
-	(_, groups) <- matchRegexPR pat str
-	return $ \i -> lookup i groups
+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
src/HsDev/Tools/Ghc/Prelude.hs view
@@ -4,7 +4,8 @@ 	) where
 
 import Data.Char (isSpace)
-import Text.RegexPR
+import Data.List (intercalate, unfoldr)
+import Text.Regex.PCRE
 
 reduce :: ([a] -> a) -> [a] -> [a]
 reduce = (return .)
@@ -17,7 +18,17 @@ 	p = reverse . dropWhile isSpace
 
 rx :: String -> String -> Maybe String
-rx r = fmap (fst . fst) . matchRegexPR r
+rx r s = case r =~ s of
+	"" -> Nothing
+	res -> Just res
 
 srx :: String -> String -> String -> String
-srx = gsubRegexPR
+srx r w = intercalate w . splitRx r
+
+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)
src/HsDev/Tools/Ghc/Worker.hs view
@@ -1,29 +1,26 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
 module HsDev.Tools.Ghc.Worker (
 	ghcWorker,
-	waitGhc,
 	evaluate,
-	try,
 
 	Ghc,
 
 	module Control.Concurrent.Worker
 	) where
 
-import Control.Arrow (left)
-import Control.Concurrent
-import Control.Exception (SomeException)
 import Control.Monad
 import Control.Monad.Error
+import Control.Monad.Catch
 import Data.Dynamic
-import Exception (gtry)
 import GHC
 import GHC.Paths
 import Packages
 
 import Control.Concurrent.Worker
 
-ghcWorker :: IO (Worker (Ghc ()))
-ghcWorker = worker_ (runGhc (Just libdir)) ghcInit try where
+ghcWorker :: IO (Worker Ghc)
+ghcWorker = startWorker (runGhc (Just libdir)) ghcInit where
 	ghcInit f = do
 		fs <- getSessionDynFlags
 		defaultCleanupHandler fs $ do
@@ -39,15 +36,12 @@ 	startMods :: [String]
 	startMods = ["Prelude", "Data.List", "Control.Monad", "HsDev.Tools.Ghc.Prelude"]
 
-waitGhc :: Worker (Ghc ()) -> Ghc a -> ErrorT String IO a
-waitGhc w act = ErrorT $ do
-	var <- newEmptyMVar
-	sendWork w $ try act >>= liftIO . putMVar var
-	takeMVar var
-
 evaluate :: String -> Ghc String
 evaluate expr = liftM fromDynamic (dynCompileExpr $ "show (" ++ expr ++ ")") >>=
 	maybe (fail "evaluate fail") return
 
-try :: Ghc a -> Ghc (Either String a)
-try = liftM (left (show :: SomeException -> String)) . gtry
+instance MonadThrow Ghc where
+	throwM = liftIO . throwM
+
+instance MonadCatch Ghc where
+	catch = gcatch
src/HsDev/Tools/GhcMod.hs view
@@ -1,8 +1,10 @@-{-# LANGUAGE OverloadedStrings, ConstraintKinds, FlexibleContexts, LambdaCase #-}
+{-# LANGUAGE OverloadedStrings, ConstraintKinds, FlexibleContexts, LambdaCase, FlexibleInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
 
 module HsDev.Tools.GhcMod (
 	list,
 	browse, browseInspection,
+	langs, flags,
 	info,
 	TypedRegion(..),
 	typeOf,
@@ -14,8 +16,8 @@ 
 	locateGhcModEnv, ghcModEnvPath,
 	ghcModWorker,
-	ghcModMultiWorker,
-	waitGhcMod,
+	WorkerMap,
+	ghcModMultiWorker, dispatch,
 	waitMultiGhcMod,
 
 	GhcModT,
@@ -24,16 +26,19 @@ 
 import Control.Applicative
 import Control.Arrow
-import Control.Concurrent (newEmptyMVar, putMVar, takeMVar, newMVar, modifyMVar_)
+import Control.Concurrent
 import Control.DeepSeq
-import Control.Exception (SomeException, bracket)
+import Control.Exception (SomeException(..))
 import Control.Monad.Error
-import Control.Monad.CatchIO (MonadCatchIO)
+import Control.Monad.Catch (MonadThrow(..), MonadCatch(..))
+import Control.Monad.Reader
 import Data.Aeson
 import Data.Char
+import Data.List (nub, sort)
 import Data.Maybe
 import qualified Data.Map as M
-import Exception (gtry)
+import Data.String (fromString)
+import Exception (gcatch)
 import GHC (getSessionDynFlags, defaultCleanupHandler)
 import System.Directory
 import System.FilePath (normalise)
@@ -48,7 +53,7 @@ import HsDev.Project
 import HsDev.Symbols
 import HsDev.Tools.Base
-import HsDev.Util ((.::), liftIOErrors)
+import HsDev.Util ((.::), liftIOErrors, withCurrentDirectory)
 
 list :: [String] -> Cabal -> ErrorT String IO [ModuleLocation]
 list opts cabal = runGhcMod (GhcMod.defaultOptions { GhcMod.ghcUserOptions = opts }) $ do
@@ -59,50 +64,86 @@ 		splitPackage = second (drop 1) . break isSpace
 
 browse :: [String] -> Cabal -> String -> Maybe ModulePackage -> ErrorT String IO InspectedModule
-browse opts cabal mname mpackage = inspect mloc (return $ browseInspection opts) $ runGhcMod
-	(GhcMod.defaultOptions { GhcMod.detailed = True, GhcMod.ghcUserOptions = packageOpt mpackage ++ opts }) $ do
-		ts <- lines <$> GhcMod.browse mpkgname
-		return $ Module {
-			moduleName = mname,
+browse opts cabal mname mpackage = inspect thisLoc (return $ browseInspection opts) $ runGhcMod
+	(GhcMod.defaultOptions { GhcMod.detailed = True, GhcMod.qualified = True, GhcMod.ghcUserOptions = packageOpt mpackage ++ opts }) $ do
+		ds <- (mapMaybe parseDecl . lines) <$> GhcMod.browse mpkgname
+		return Module {
+			moduleName = fromString mname,
 			moduleDocs = Nothing,
-			moduleLocation = mloc,
-			moduleExports = [],
-			moduleImports = [],
-			moduleDeclarations = decls ts }
+			moduleLocation = thisLoc,
+			moduleExports = Just $ map (ExportName Nothing . declarationName) ds,
+			moduleImports = [import_ iname |
+				iname <- nub (mapMaybe definedModule ds),
+				iname /= fromString mname],
+			moduleDeclarations = sortDeclarations ds }
 	where
 		mpkgname = maybe mname (\p -> packageName p ++ ":" ++ mname) mpackage
-		mloc = CabalModule cabal mpackage mname
-		decls rs = M.fromList $ map (declarationName &&& id) $ mapMaybe parseDecl rs
-		parseFunction s = do
-			groups <- match "(\\w+)\\s+::\\s+(.*)" s
-			return $ Declaration (groups `at` 1) Nothing Nothing (Function (Just $ groups `at` 2) [])
-		parseType s = do
-			groups <- match "(class|type|data|newtype)\\s+(\\w+)(\\s+(\\w+(\\s+\\w+)*))?" s
+		thisLoc = moduleIdLocation $ mloc mname
+		mloc mname' = ModuleId (fromString mname') $ CabalModule cabal Nothing mname'
+		parseDecl s = do
+			groups <- matchRx rx s
 			let
-				args = maybe [] words $ groups 3
-			return $ Declaration (groups `at` 2) Nothing Nothing (declarationTypeCtor (groups `at` 1) $ TypeInfo Nothing args Nothing)
-		parseDecl s = parseFunction s `mplus` parseType s
+				rdecl = decl (fromString $ groups `at` 3) $ case groups 5 of
+					Nothing -> Function (Just $ fromString $ groups `at` 4) []
+					Just k -> declarationTypeCtor k $
+						TypeInfo Nothing (maybe [] (map fromString . words) $ groups 7) Nothing
+			return $ rdecl `definedIn` mloc (init $ groups `at` 1)
+		definedModule = fmap moduleIdName . declarationDefined
+		-- groups:
+		-- 1: "<module>."
+		-- 3: "<name>"
+		-- 4: "<type>" or "<rest>"
+		-- 5: "<kind>" (class, type, data or newtype)
+		-- 6: "<name>"
+		-- 7: " <args>"
+		rx = "^((\\w+\\.)*)(\\w+)\\s+::\\s+((class|type|data|newtype)\\s+(\\w+)((\\s+\\w+)*)?|.*)$"
 
 browseInspection :: [String] -> Inspection
-browseInspection = InspectionAt 0
+browseInspection = InspectionAt 0 . sort . nub
 
-info :: [String] -> Cabal -> FilePath -> Maybe Project -> String -> String -> GhcModT IO Declaration
-info opts cabal file _ _ sname = do
+langs :: ErrorT String IO [String]
+langs = runGhcMod GhcMod.defaultOptions $ (lines . nullToNL) <$> GhcMod.languages
+
+flags :: ErrorT String IO [String]
+flags = runGhcMod GhcMod.defaultOptions $ (lines . nullToNL) <$> GhcMod.flags
+
+info :: [String] -> Cabal -> FilePath -> String -> GhcModT IO Declaration
+info opts cabal file sname = do
+	fileCts <- liftIO $ readFile file
 	rs <- withOptions (\o -> o { GhcMod.ghcUserOptions = cabalOpt cabal ++ opts }) $
-		GhcMod.info file sname
-	toDecl rs
+		liftM nullToNL $ GhcMod.info file sname
+	toDecl fileCts rs
 	where
-		toDecl s = maybe (throwError $ strMsg $ "Can't parse info: '" ++ s ++ "'") return $ parseData s `mplus` parseFunction s
+		toDecl fstr s =
+			liftM (recalcDeclTabs fstr) .
+			maybe (throwError $ strMsg $ "Can't parse info: '" ++ sname ++ "'") return $
+			parseData s `mplus` parseFunction s
+		recalcDeclTabs :: String -> Declaration -> Declaration
+		recalcDeclTabs fstr d = d { declarationPosition = fmap (recalcTabs fstr) (declarationPosition d) }
 		parseFunction s = do
-			groups <- match (sname ++ "\\s+::\\s+(.*?)(\\s+--(.*))?$") s
-			return $ Declaration sname Nothing Nothing (Function (Just $ groups `at` 1) [])
+			groups <- matchRx (sname ++ "\\s+::\\s+(.*?)(\\s+-- Defined (at (.*)|in `(.*)'))?$") s
+			return (decl (fromString sname) (Function (Just $ fromString $ groups `at` 1) [])) {
+				declarationDefined = unnamedModuleId <$>
+					((groups 4 >>= parseSrc) <|> (mkMod <$> groups 5)),
+				declarationPosition = groups 4 >>= parsePos }
 		parseData s = do
-			groups <- match "(newtype|type|data)\\s+((.*)=>\\s+)?(\\S+)\\s+((\\w+\\s+)*)=(\\s*(.*)\\s+-- Defined)?" s
+			groups <- matchRx "(newtype|type|data)\\s+((.*)=>\\s+)?(\\S+)\\s+((\\w+\\s+)*)=(\\s*(.*)\\s+-- Defined (at (.*)|in `(.*)'))?" s
 			let
-				args = maybe [] words $ groups 5
-				ctx = fmap trim $ groups 3
-				def = groups 8
-			return $ Declaration sname Nothing Nothing (declarationTypeCtor (groups `at` 1) $ TypeInfo ctx args def)
+				args = maybe [] (map fromString . words) $ groups 5
+				ctx = fmap (fromString . trim) $ groups 3
+				def = fmap fromString $ groups 8
+			return (decl (fromString sname) (declarationTypeCtor (groups `at` 1) $ TypeInfo ctx args def)) {
+				declarationDefined = unnamedModuleId <$>
+					((groups 10 >>= parseSrc) <|> (mkMod <$> groups 11)),
+				declarationPosition = groups 10 >>= parsePos }
+		parseSrc src = case splitRx ":(?=\\d)" src of
+			[srcFile, _, _] -> Just $ FileModule srcFile Nothing
+			_ -> Nothing
+		-- FIXME: Position is returned by ghc-mod and it interprets tab as several spaces instead of one symbol
+		parsePos src = case splitRx ":(?=\\d)" src of
+			[_, line, column] ->  Position <$> readMaybe line <*> readMaybe column
+			_ -> Nothing
+		mkMod = CabalModule cabal Nothing
 		trim = p . p where
 			p = reverse . dropWhile isSpace
 
@@ -127,20 +168,20 @@ 		v .:: "expr" <*>
 		v .:: "type"
 
-typeOf :: [String] -> Cabal -> FilePath -> Maybe Project -> String -> Int -> Int -> GhcModT IO [TypedRegion]
-typeOf opts cabal file _ _ line col = withOptions (\o -> o { GhcMod.ghcUserOptions = cabalOpt cabal ++ opts }) $ do
+typeOf :: [String] -> Cabal -> FilePath -> Int -> Int -> GhcModT IO [TypedRegion]
+typeOf opts cabal file line col = withOptions (\o -> o { GhcMod.ghcUserOptions = cabalOpt cabal ++ opts }) $ do
 	fileCts <- liftIO $ readFile file
 	ts <- lines <$> GhcMod.types file line col
 	return $ mapMaybe (toRegionType fileCts) ts
 	where
 		toRegionType :: String -> String -> Maybe TypedRegion
 		toRegionType fstr s = do
-			(r, tp) <- parseRead s $ (,) <$> parseRegion <*> readParse
+			(r, tp) <- parseRead s $ (,) <$> parseRegion fstr <*> readParse
 			return $ TypedRegion r (regionStr r fstr) tp
-		parseRegion :: ReadM Region
-		parseRegion = Region <$> parsePosition <*> parsePosition
-		parsePosition :: ReadM Position
-		parsePosition = Position <$> readParse <*> readParse
+		parseRegion :: String -> ReadM Region
+		parseRegion fstr = Region <$> parsePosition fstr <*> parsePosition fstr
+		parsePosition :: String -> ReadM Position
+		parsePosition fstr = recalcTabs fstr <$> (Position <$> readParse <*> readParse)
 
 data OutputMessageLevel = WarningMessage | ErrorMessage deriving (Eq, Ord, Bounded, Enum, Read, Show)
 
@@ -181,18 +222,20 @@ 
 parseOutputMessage :: String -> Maybe OutputMessage
 parseOutputMessage s = do
-	groups <- match "^(.+):(\\d+):(\\d+):(\\s*(Warning|Error):)?\\s*(.*)$" s
-	return $ OutputMessage {
+	groups <- matchRx "^(.+):(\\d+):(\\d+):(\\s*(Warning|Error):)?\\s*(.*)$" s
+	return OutputMessage {
 		errorLocation = Location {
 			locationModule = FileModule (normalise (groups `at` 1)) Nothing,
 			locationPosition = Position <$> readMaybe (groups `at` 2) <*> readMaybe (groups `at` 3) },
 		errorLevel = if groups 5 == Just "Warning" then WarningMessage else ErrorMessage,
-		errorMessage = map nullToNL (groups `at` 6) }
-	where
-		nullToNL = \case
-			'\0' -> '\n'
-			ch -> ch
+		errorMessage = nullToNL (groups `at` 6) }
 
+-- | Replace NULL with newline
+nullToNL :: String -> String
+nullToNL = map $ \case
+	'\0' -> '\n'
+	ch -> ch
+
 check :: [String] -> Cabal -> [FilePath] -> Maybe Project -> GhcModT IO [OutputMessage]
 check opts cabal files _ = withOptions (\o -> o { GhcMod.ghcUserOptions = cabalOpt cabal ++ opts }) $ do
 	msgs <- lines <$> GhcMod.checkSyntax files
@@ -203,7 +246,7 @@ 	msgs <- lines <$> GhcMod.lint file
 	return $ mapMaybe parseOutputMessage msgs
 
-runGhcMod :: (GhcMod.IOish m, MonadCatchIO m) => GhcMod.Options -> GhcModT m a -> ErrorT String m a
+runGhcMod :: (GhcMod.IOish m, MonadCatch m) => GhcMod.Options -> GhcModT m a -> ErrorT String m a
 runGhcMod opts act = liftIOErrors $ ErrorT $ liftM (left show . fst) $ runGhcModT opts act
 
 locateGhcModEnv :: FilePath -> IO (Either Project Cabal)
@@ -215,13 +258,14 @@ ghcModEnvPath defaultPath = either projectPath (fromMaybe defaultPath . sandbox)
 
 -- | Create ghc-mod worker for project or for sandbox
-ghcModWorker :: Either Project Cabal -> IO (Worker (GhcModT IO ()))
+ghcModWorker :: Either Project Cabal -> IO (Worker (GhcModT IO))
 ghcModWorker p = do
 	home <- getHomeDirectory
-	worker_ (runGhcModT'' $ ghcModEnvPath home p) id try
+	startWorker (runGhcModT'' $ ghcModEnvPath home p) id
 	where
 		makeEnv :: FilePath -> IO GhcMod.GhcModEnv
 		makeEnv = GhcMod.newGhcModEnv GhcMod.defaultOptions
+		-- TODO: Uncomment comment below after ghc-mod exports neccessary functions
 		functionNotExported = True
 		runGhcModT'' :: FilePath -> GhcModT IO () -> IO ()
 		runGhcModT'' cur act
@@ -234,35 +278,36 @@ 					defaultCleanupHandler dflags $ do
 						--GhcMod.initializeFlagsWithCradle GhcMod.defaultOptions (GhcMod.gmCradle env')
 						act
-		withCurrentDirectory :: FilePath -> IO a -> IO a
-		withCurrentDirectory cur act = bracket getCurrentDirectory setCurrentDirectory $
-			const (setCurrentDirectory cur >> act)
 
+type WorkerMap = MVar (M.Map FilePath (Worker (GhcModT IO)))
+
 -- | Manage many ghc-mod workers for each project/sandbox
-ghcModMultiWorker :: IO (Worker (FilePath, GhcModT IO ()))
-ghcModMultiWorker = worker id initMultiGhcMod multiWork where
-	initMultiGhcMod f = newMVar M.empty >>= f
-	multiWork ghcMods (file, act) = do
-		home <- getHomeDirectory
-		env' <- locateGhcModEnv file
-		let
-			envPath' = ghcModEnvPath home env'
-		modifyMVar_ ghcMods $ \ghcModsMap -> do
-			w <- maybe (ghcModWorker env') return $ M.lookup envPath' ghcModsMap
-			sendWork w act
-			return $ M.insert envPath' w ghcModsMap
+ghcModMultiWorker :: IO (Worker (ReaderT WorkerMap IO))
+ghcModMultiWorker = newMVar M.empty >>= \m -> startWorker (`runReaderT` m) id
 
-waitGhcMod :: Worker (GhcModT IO ()) -> GhcModT IO a -> ErrorT String IO a
-waitGhcMod w act = ErrorT $ do
-	var <- newEmptyMVar
-	sendWork w $ try act >>= liftIO . putMVar var
-	takeMVar var
+instance MonadThrow (GhcModT IO) where
+	throwM = lift . throwM
 
-waitMultiGhcMod :: Worker (FilePath, GhcModT IO ()) -> FilePath -> GhcModT IO a -> ErrorT String IO a
-waitMultiGhcMod w f act = ErrorT $ do
-	var <- newEmptyMVar
-	sendWork w (f, try act >>= liftIO . putMVar var)
-	takeMVar var
+instance MonadCatch (GhcModT IO) where
+	catch = gcatch
 
-try :: GhcModT IO a -> GhcModT IO (Either String a)
-try = liftM (left (show :: SomeException -> String)) . gtry
+dispatch :: FilePath -> GhcModT IO a -> ReaderT WorkerMap IO (Task a)
+dispatch file act = do
+	mvar <- ask
+	home <- liftIO getHomeDirectory
+	env' <- liftIO $ locateGhcModEnv file
+	let
+		envPath' = ghcModEnvPath home env'
+	liftIO $ modifyMVar mvar $ \wmap -> do
+		w <- maybe (ghcModWorker env') return $ M.lookup envPath' wmap
+		t <- pushTask w act
+		return (M.insert envPath' w wmap, t)
+
+waitMultiGhcMod :: Worker (ReaderT WorkerMap IO) -> FilePath -> GhcModT IO a -> ErrorT String IO a
+waitMultiGhcMod w f =
+	liftIO . pushTask w . dispatch f >=>
+	asErrorT . taskWait >=>
+	asErrorT . taskWait
+	where
+		asErrorT :: Monad m => m (Either SomeException a) -> ErrorT String m a
+		asErrorT = ErrorT . liftM (left (\(SomeException e) -> show e))
src/HsDev/Tools/GhcMod/InferType.hs view
@@ -3,11 +3,14 @@ 	GhcModT
 	) where
 
+import Control.Applicative
 import Control.Monad.Error
+import Data.Maybe (listToMaybe)
+import Data.String (fromString)
+import qualified Data.Text as T (unpack)
 import Data.Traversable (traverse)
 
 import HsDev.Cabal
-import HsDev.Project
 import HsDev.Symbols
 import HsDev.Tools.GhcMod
 
@@ -17,28 +20,34 @@ untyped _ = False
 
 -- | Infer type of declaration
-inferType :: [String] -> Cabal -> FilePath -> Maybe Project -> String -> Declaration -> GhcModT IO Declaration
-inferType opts cabal src mproj mname decl
-	| untyped (declaration decl) = infer
-	| otherwise = return decl
+inferType :: [String] -> Cabal -> FilePath -> Declaration -> GhcModT IO Declaration
+inferType opts cabal src decl'
+	| untyped (declaration decl') = infer
+	| otherwise = return decl'
 	where
 		infer = do
-			inferred <- liftM declaration $ info opts cabal src mproj mname (declarationName decl)
-			return decl {
-				declaration = setType (declaration decl) (getType inferred) }
+			inferred <- ((getType . declaration) <$> byInfo) <|> byTypeOf
+			return decl' {
+				declaration = setType (declaration decl') inferred }
 
+		byInfo = info opts cabal src (T.unpack $ declarationName decl')
+		byTypeOf = case declarationPosition decl' of
+			Nothing -> fail "No position"
+			Just (Position l c) -> (fmap typedType . listToMaybe) <$> typeOf opts cabal src l c
+
 		setType :: DeclarationInfo -> Maybe String -> DeclarationInfo
-		setType (Function _ ds) newType = Function newType ds
+		setType (Function _ ds) newType = Function (fmap fromString newType) ds
 		setType dinfo _ = dinfo
 
 		getType :: DeclarationInfo -> Maybe String
-		getType (Function fType _) = fType
+		getType (Function fType _) = fmap T.unpack fType
 		getType _ = Nothing
 
 -- | Infer types for module
 inferTypes :: [String] -> Cabal -> Module -> GhcModT IO Module
 inferTypes opts cabal m = case moduleLocation m of
-	FileModule src p -> do
-		inferredDecls <- traverse (inferType opts cabal src p (moduleName m)) $ moduleDeclarations m
+	FileModule src _ -> do
+		inferredDecls <- traverse (\d -> inferType opts cabal src d <|> return d) $
+			moduleDeclarations m
 		return m { moduleDeclarations = inferredDecls }
 	_ -> throwError $ strMsg "Type infer works only for source files"
src/HsDev/Tools/HDocs.hs view
@@ -14,7 +14,8 @@ import qualified Data.ByteString.Lazy.Char8 as L (pack)
 import Data.Map (Map)
 import qualified Data.Map as M
-import Data.Maybe ()
+import Data.String (fromString)
+import Data.Text (unpack)
 import System.Process (readProcess)
 
 import qualified HDocs.Module as HDocs
@@ -34,13 +35,14 @@ 
 -- | Set docs for module
 setDocs :: Map String String -> Module -> Module
-setDocs d m = m { moduleDeclarations = M.mapWithKey setDoc $ moduleDeclarations m } where
-	setDoc name decl = decl { declarationDocs = M.lookup name d }
+setDocs d m = m { moduleDeclarations = map setDoc $ moduleDeclarations m } where
+	setDoc decl' = decl' { declarationDocs = M.lookup (declarationName decl') d' }
+	d' = M.mapKeys fromString . M.map fromString $ d
 
 -- | Load docs for module
 loadDocs :: [String] -> Module -> IO Module
 loadDocs opts m = do
-	d <- hdocs (moduleName m) opts
+	d <- hdocs (unpack $ moduleName m) opts
 	return $ setDocs d m
 
 hdocsProcess :: String -> [String] -> IO (Maybe (Map String String))
src/HsDev/Tools/Hayoo.hs view
@@ -18,10 +18,11 @@ import qualified Data.ByteString.Lazy.Char8 as L
 import Data.Either
 import Network.HTTP
-import Text.RegexPR (gsubRegexPR)
+import Data.String (fromString)
 
 import HsDev.Symbols
 import HsDev.Symbols.Documented
+import HsDev.Tools.Base (replaceRx)
 import HsDev.Util
 
 -- | Hayoo response
@@ -56,11 +57,11 @@ 		((rights . map hayooValue) <$> (v .:: "result"))
 
 instance Symbol HayooSymbol where
-	symbolName = hayooName
-	symbolQualifiedName f = case hayooModules f of
+	symbolName = fromString . hayooName
+	symbolQualifiedName f = fromString $ case hayooModules f of
 		[] -> hayooName f
 		(m:_) -> m ++ "." ++ hayooName f
-	symbolDocs = Just . hayooDescription
+	symbolDocs = Just . fromString . hayooDescription
 	symbolLocation r = Location (ModuleSource $ Just $ resultUri r) Nothing where
 
 instance Documented HayooSymbol where
@@ -94,11 +95,13 @@ hayooAsDeclaration f
 	| hayooType f `elem` ["function", "type", "newtype", "data", "class"] = Just ModuleDeclaration {
 		declarationModuleId = ModuleId {
-			moduleIdName = head $ hayooModules f,
+			moduleIdName = fromString $ head $ hayooModules f,
 			moduleIdLocation = ModuleSource (Just $ resultUri f) },
 		moduleDeclaration = Declaration {
-			declarationName = hayooName f,
-			declarationDocs = Just (addOnline $ untagDescription $ hayooDescription f),
+			declarationName = fromString $ hayooName f,
+			declarationDefined = Nothing,
+			declarationImported = Nothing,
+			declarationDocs = Just (fromString $ addOnline $ untagDescription $ hayooDescription f),
 			declarationPosition = Nothing,
 			declaration = declInfo } }
 	| otherwise = Nothing
@@ -112,7 +115,7 @@ 			"Hackage URL: " ++ resultUri f]
 
 		declInfo
-			| hayooType f == "function" = Function (Just $ hayooSignature f) []
+			| hayooType f == "function" = Function (Just $ fromString $ hayooSignature f) []
 			| hayooType f `elem` ["type", "newtype", "data", "class"] = declarationTypeCtor (hayooType f) $ TypeInfo Nothing [] Nothing
 			| otherwise = error "Impossible"
 
@@ -127,4 +130,4 @@ 
 -- | Remove tags in description
 untagDescription :: String -> String
-untagDescription = gsubRegexPR "</?\\w+[^>]*>" ""
+untagDescription = replaceRx "</?\\w+[^>]*>" ""
src/HsDev/Util.hs view
@@ -1,4 +1,5 @@ module HsDev.Util (
+	withCurrentDirectory,
 	directoryContents,
 	traverseDirectory,
 	isParent,
@@ -14,14 +15,16 @@ 	-- * UTF-8
 	fromUtf8, toUtf8,
 	-- * IO
-	hGetLineBS, logException, logIO, ignoreIO
+	hGetLineBS, logException, logIO, ignoreIO,
+	-- * Task
+	liftTask
 	) where
 
-import Control.Arrow (second)
+import Control.Arrow (second, left)
 import Control.Exception
 import Control.Monad
 import Control.Monad.Error
-import qualified Control.Monad.CatchIO as C
+import qualified Control.Monad.Catch as C
 import Data.Aeson
 import Data.Aeson.Types (Parser)
 import Data.Char (isSpace)
@@ -38,6 +41,13 @@ import System.FilePath
 import System.IO (Handle)
 
+import Control.Concurrent.Task
+
+-- | Run action with current directory set
+withCurrentDirectory :: FilePath -> IO a -> IO a
+withCurrentDirectory cur act = bracket getCurrentDirectory setCurrentDirectory $
+	const (setCurrentDirectory cur >> act)
+
 -- | Get directory contents safely
 directoryContents :: FilePath -> IO [FilePath]
 directoryContents p = handle ignore $ do
@@ -107,17 +117,16 @@ objectUnion _ _ = Null
 
 -- | Lift IO exception to ErrorT
-liftException :: C.MonadCatchIO m => m a -> ErrorT String m a
-liftException act = ErrorT $ C.catch (liftM Right act) onError where
-	onError = return . Left . (show :: SomeException -> String)
+liftException :: C.MonadCatch m => m a -> ErrorT String m a
+liftException = ErrorT . liftM (left $ \(SomeException e) -> show e) . C.try
 
 -- | Lift IO exception to MonadError
-liftExceptionM :: (C.MonadCatchIO m, Error e, MonadError e m) => m a -> m a
+liftExceptionM :: (C.MonadCatch m, Error e, MonadError e m) => m a -> m a
 liftExceptionM act = C.catch act onError where
-	onError = throwError . strMsg . (show :: SomeException -> String)
+	onError = throwError . strMsg . (\(SomeException e) -> show e)
 
 -- | Lift IO exceptions to ErrorT
-liftIOErrors :: C.MonadCatchIO m => ErrorT String m a -> ErrorT String m a
+liftIOErrors :: C.MonadCatch m => ErrorT String m a -> ErrorT String m a
 liftIOErrors act = liftException (runErrorT act) >>= either throwError return
 
 eitherT :: (Monad m, Error e, MonadError e m) => Either String a -> m a
@@ -144,3 +153,6 @@ 
 ignoreIO :: IO () -> IO ()
 ignoreIO = handle (const (return ()) :: IOException -> IO ())
+
+liftTask :: MonadIO m => IO (Task a) -> ErrorT String m a
+liftTask = ErrorT . liftM (left (\(SomeException e) -> show e)) . liftIO . join . liftM taskWait
src/System/Console/Cmd.hs view
@@ -9,7 +9,7 @@ 
 import Control.Arrow (Arrow((&&&)))
 import Control.Monad ()
-import Data.List (stripPrefix)
+import Data.List (stripPrefix, isPrefixOf)
 import Control.Monad.Error
 import Data.Map ()
 import Data.Maybe
@@ -123,9 +123,15 @@ 			namedArgs = Opts $ M.delete "help" $ getOpts $ namedArgs a }
 		| otherwise = a
 	onHelp (Args [] _) = Right $ HelpUsage [tool ++ " " ++ brief c | c <- (helpcmd:cmds)]
-	onHelp (Args cmdname _) = case filter ((cmdname ==) . words . cmdName) (helpcmd:cmds) of
-		[] -> Left $ "Unknown command: " ++ unwords cmdname
+	onHelp (Args cmdname _) = case filter ((cmdname `isPrefixOf`) . words . cmdName) (helpcmd:cmds) of
+		[] -> Left $ unlines $ ("Unknown command: " ++ unwords cmdname) : tryOut
 		helps -> Right $ HelpCommands $ map (cmdName &&& (addHeader . indented)) helps
+		where
+			pre = unwords cmdname
+			maybeCmds = filter (pre `isPrefixOf`) $ map cmdName (helpcmd:cmds)
+			tryOut = case maybeCmds of
+				[] -> []
+				_ -> "\tMaybe you mean:" : map ("\t\t" ++) maybeCmds
 	addHeader [] = []
 	addHeader (h:hs) = (tool ++ " " ++ h) : hs
 
src/System/Win32/FileMapping/Memory.hs view
@@ -3,7 +3,8 @@ 	withMapFile, readMapFile
 	) where
 
-import Control.Monad.CatchIO (bracket)
+import Control.Arrow (left)
+import Control.Monad.Catch
 import Control.Monad.Cont
 import Control.Monad.Error
 import Data.ByteString.Char8
@@ -14,25 +15,25 @@ import System.Win32.Types
 import System.Win32.Mem
 
-createMap :: Maybe HANDLE -> ProtectFlags -> DDWORD -> Maybe String -> ContT r (ErrorT String IO) HANDLE
+createMap :: Maybe HANDLE -> ProtectFlags -> DDWORD -> Maybe String -> ContT r IO HANDLE
 createMap mh pf sz mn = ContT $ bracket
 	(verify iNVALID_HANDLE_VALUE "Invalid handle" $
-		liftIO (createFileMapping mh pf sz mn))
-	(liftIO . closeHandle)
+		createFileMapping mh pf sz mn)
+	closeHandle
 
-openMap :: FileMapAccess -> Bool -> Maybe String -> ContT r (ErrorT String IO) HANDLE
+openMap :: FileMapAccess -> Bool -> Maybe String -> ContT r IO HANDLE
 openMap f i mn = ContT $ bracket
 	(verify iNVALID_HANDLE_VALUE "Invalid handle" $
-		liftIO (openFileMapping f i mn))
-	(liftIO . closeHandle)
+		openFileMapping f i mn)
+	closeHandle
 
-mapFile :: HANDLE -> FileMapAccess -> DDWORD -> SIZE_T -> ErrorT String IO (Ptr a)
-mapFile h f off sz = verify nullPtr "null pointer" $ liftIO (mapViewOfFile h f off sz) 
+mapFile :: HANDLE -> FileMapAccess -> DDWORD -> SIZE_T -> IO (Ptr a)
+mapFile h f off sz = verify nullPtr "null pointer" $ mapViewOfFile h f off sz
 
 -- | Write data to named map view of file
 withMapFile :: String -> ByteString -> IO a -> ErrorT String IO a
-withMapFile name str act = flip runContT return $ do
-	p <- ContT $ \f -> ErrorT (BS.useAsCString str (runErrorT . f))
+withMapFile name str act = liftE $ flip runContT return $ do
+	p <- ContT $ BS.useAsCString str
 	h <- createMap Nothing pAGE_READWRITE (fromIntegral len) (Just name)
 	ptr <- lift $ mapFile h fILE_MAP_ALL_ACCESS 0 0
 	liftIO $ do
@@ -44,14 +45,17 @@ 
 -- | Read data from named map view of file
 readMapFile :: String -> ErrorT String IO ByteString
-readMapFile name = flip runContT return $ do
+readMapFile name = liftE $ flip runContT return $ do
 	h <- openMap fILE_MAP_ALL_ACCESS True (Just name)
 	ptr <- lift $ mapFile h fILE_MAP_ALL_ACCESS 0 0
 	liftIO $ BS.packCString ptr
 
-verify :: (Error e, MonadError e m, Eq a) => a -> String -> m a -> m a
+verify :: Eq a => a -> String -> IO a -> IO a
 verify v str act = do
 	x <- act
 	if x == v
-		then throwError (strMsg str)
+		then ioError (userError str)
 		else return x
+
+liftE :: MonadCatch m => m a -> ErrorT String m a
+liftE = ErrorT . liftM (left (\(SomeException e) -> show e)) . try
src/System/Win32/PowerShell.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverlappingInstances, GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverlappingInstances, GeneralizedNewtypeDeriving, LambdaCase #-}
 
 module System.Win32.PowerShell (
 	-- * Run PowerShell
@@ -9,11 +9,13 @@ 	PS(..), seqPS, emit, emit_, (=:), invoke,
 	-- * Expression
 	Args(..), CmdLet(..), Expr(..), compile,
-	raw, name, lit, var, bra, (.=), flag, named, call, cmdlet, lambda, (.|), foreach, filter,
+	raw, name, lit, var, bra, cbra, (.=), flag, named, call, cmdlet, lambda, (.|), foreach, filter,
 	-- * Convertible
 	ToPS(..),
 	-- * Escape functions
-	translate, translateArg
+	translate, translateArg,
+	quote, quoteDouble,
+	escape
 	) where
 
 import Prelude hiding (filter)
@@ -23,6 +25,7 @@ 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
 
@@ -103,16 +106,16 @@ compile (Literal l) = l
 compile (Var v) = '$':v
 compile (Bracket e) = "(" ++ compile e ++ ")"
-compile (Assign vs es) = intercalate ", " (map ('$':) vs) ++ " = " ++ intercalate ", " (map (compile . bra) es)
-compile (Invoke (CmdLet e (Args p ns))) = unwords [invoke', p', ns'] where
+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 $ bra e]
-	p' = intercalate " " $ map (compile . bra) p
+		_ -> unwords ["&", compile $ cbra e]
+	p' = intercalate " " $ map (compile . cbra) p
 	ns' = intercalate " " $ concatMap named' $ M.toList ns where
 		named' :: (String, Maybe Expr) -> [String]
-		named' (n, Just v) = [n, compile $ bra v]
-		named' (n, Nothing) = [n]
+		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 ++ ");"
@@ -135,6 +138,11 @@ 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]
@@ -181,13 +189,27 @@ 	toPS = intercalate ", " . map toPS
 
 translate :: String -> String
-translate s = '"' : 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')
+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
src/Text/Format.hs view
@@ -10,8 +10,8 @@ 	) where
 
 import Control.Arrow (first)
-import Data.List (delete, isSuffixOf)
-import Text.Regex.Posix
+import Data.List (delete, isPrefixOf)
+import Text.Regex.PCRE
 
 class Format a where
 	format :: a -> String
@@ -51,12 +51,14 @@ 			name = case gs of
 				_:name':_ -> name'
 				_ -> ""
-		(arg', args') <- split' name
-		post' <- post %~ args'
-		return $ concat [
-			pre,
-			if "$" `isSuffixOf` pre then name else arg',
-			post']
+		if null name && "$" `isPrefixOf` post
+			then do
+				post' <- tail post %~ hargs
+				return $ pre ++ "$" ++ post'
+			else do
+				(arg', args') <- split' name
+				post' <- post %~ args'
+				return $ pre ++ arg' ++ post'
 	where
 		args = hole hargs
 
tools/Tool.hs view
@@ -31,10 +31,7 @@ 	hSetBuffering stdout LineBuffering
 	hSetEncoding stdout utf8
 	hSetEncoding stdin utf8
-	as <- getArgs
-	case as of
-		[] -> usage name toolCmds
-		_ -> run toolCmds unknownCmd onError as
+	getArgs >>= run toolCmds unknownCmd onError
 	where
 		onError :: String -> IO ()
 		onError = putStrLn
tools/hsdev.hs view
@@ -5,6 +5,7 @@ 	) where
 
 import Control.Applicative
+import Control.Exception
 import Control.Monad
 import Network.Socket (withSocketsDo)
 import System.Environment (getArgs)
@@ -21,7 +22,7 @@ import HsDev.Version
 
 main :: IO ()
-main = withSocketsDo $ do
+main = handle logErr' $ withSocketsDo $ do
 	hSetBuffering stdout LineBuffering
 	hSetEncoding stdout utf8
 	as <- getArgs
@@ -30,17 +31,12 @@ 		exitSuccess
 	let
 		asr = if last as == "-?" then "help" : init as else as 
-	run mainCommands onDef onError asr
+	run mainCommands (onError "Unknown command") onError asr
 	where
 		onError :: String -> IO ()
-		onError errs = do
-			putStrLn errs
-			exitFailure
+		onError errs = putStrLn errs >> exitFailure
 
-		onDef :: IO ()
-		onDef = do
-			putStrLn "Unknown command"
-			exitFailure
+		logErr' (SomeException e) = putStrLn $ "exception " ++ show e
 
 mainCommands :: [Cmd (IO ())]
 mainCommands = withHelp "hsdev" (printWith putStrLn) $ concat [
tools/hsinspect.hs view
@@ -1,10 +1,15 @@-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE OverloadedStrings, ViewPatterns #-}
 
 module Main (
 	main
 	) where
 
+import Control.Applicative
+import Control.Monad (liftM)
 import Control.Monad.IO.Class
+import Data.Aeson (toJSON)
+import System.FilePath (takeExtension)
+
 import HsDev.Project (readProject)
 import HsDev.Scan (scanModule)
 import HsDev.Inspect (inspectContents)
@@ -14,22 +19,13 @@ 
 main :: IO ()
 main = toolMain "hsinspect" [
-	jsonCmd "module" ["module name"] [ghcOpts] "inspect installed module" inspectModule',
-	jsonCmd "file" ["source file"] [ghcOpts] "inspect file" inspectFile',
-	jsonCmd "input" [] [ghcOpts] "inspect input" inspectInput',
-	jsonCmd_ "cabal" ["project file"] "inspect .cabal file" inspectCabal']
+	jsonCmd "" ["what"] [ghcOpts] "depending of what <what> is, inspect installed module, source file (.hs), cabal file (.cabal) or contents, passes as input if no <what> specified" inspect']
 	where
 		ghcOpts = list "ghc" "option" `short` ['g'] `desc` "options to pass to GHC"
 		ghcs = listArg "ghc"
 
-		inspectModule' (Args [mname] opts) = scanModule (ghcs opts) (CabalModule Cabal Nothing mname)
-		inspectModule' _ = toolError "Specify module name"
-
-		inspectFile' (Args [fname] opts) = scanModule (ghcs opts) (FileModule fname Nothing)
-		inspectFile' _ = toolError "Specify source file name"
-
-		inspectInput' (Args [] opts) = liftIO getContents >>= inspectContents "stdin" (ghcs opts)
-		inspectInput' _ = toolError "Invalid arguments"
-
-		inspectCabal' [fcabal] = readProject fcabal
-		inspectCabal' _ = toolError "Specify project .cabal file"
+		inspect' (Args [] opts) = liftIO getContents >>= liftM toJSON . inspectContents "stdin" (ghcs opts)
+		inspect' (Args [fname@(takeExtension -> ".hs")] opts) = toJSON <$> scanModule (ghcs opts) (FileModule fname Nothing)
+		inspect' (Args [fcabal@(takeExtension -> ".cabal")] _) = toJSON <$> readProject fcabal
+		inspect' (Args [mname] opts) = toJSON <$> scanModule (ghcs opts) (CabalModule Cabal Nothing mname)
+		inspect' _ = toolError "Specify module name or file name (.hs or .cabal)"