diff --git a/hsdev.cabal b/hsdev.cabal
--- a/hsdev.cabal
+++ b/hsdev.cabal
@@ -1,311 +1,318 @@
-name:                hsdev
-version:             0.2.5.0
-synopsis:            Haskell development library
-description:
-  Haskell development library and tool with support of autocompletion, symbol info, go to declaration, find references, hayoo search etc.
-homepage:            https://github.com/mvoidex/hsdev
-license:             BSD3
-license-file:        LICENSE
-author:              Alexandr `Voidex` Ruchkin
-maintainer:          voidex@live.com
--- copyright:
-category:            Development
-build-type:          Simple
-cabal-version:       >=1.8
-extra-source-files:
-    tests/test-package/*.hs
-    tests/test-package/test-package.cabal
-
-source-repository head
-  type: git
-  location: git://github.com/mvoidex/hsdev.git
-
-library
-  hs-source-dirs: src
-  ghc-options: -Wall -fno-warn-tabs
-  exposed-modules:
-    Control.Apply.Util
-    Control.Concurrent.FiniteChan
-    Control.Concurrent.Worker
-    Control.Concurrent.Util
-    Data.Async
-    Data.Deps
-    Data.Group
-    Data.Help
-    Data.Lisp
-    Data.Maybe.JustIf
-    HsDev
-    HsDev.Cache
-    HsDev.Cache.Structured
-    HsDev.Client.Commands
-    HsDev.Commands
-    HsDev.Database
-    HsDev.Database.Async
-    HsDev.Database.Update
-    HsDev.Database.Update.Types
-    HsDev.Display
-    HsDev.Error
-    HsDev.Inspect
-    HsDev.PackageDb
-    HsDev.Project
-    HsDev.Project.Types
-    HsDev.Scan
-    HsDev.Scan.Browse
-    HsDev.Server.Base
-    HsDev.Server.Commands
-    HsDev.Server.Message
-    HsDev.Server.Types
-    HsDev.Sandbox
-    HsDev.Stack
-    HsDev.Symbols
-    HsDev.Symbols.Class
-    HsDev.Symbols.Location
-    HsDev.Symbols.Documented
-    HsDev.Symbols.Resolve
-    HsDev.Symbols.Types
-    HsDev.Symbols.Util
-    HsDev.Tools.AutoFix
-    HsDev.Tools.Base
-    HsDev.Tools.Cabal
-    HsDev.Tools.ClearImports
-    HsDev.Tools.Ghc.Check
-    HsDev.Tools.Ghc.Compat
-    HsDev.Tools.Ghc.MGhc
-    HsDev.Tools.Ghc.Prelude
-    HsDev.Tools.Ghc.Types
-    HsDev.Tools.Ghc.Worker
-    HsDev.Tools.Ghc.Session
-    HsDev.Tools.Hayoo
-    HsDev.Tools.HDocs
-    HsDev.Tools.HLint
-    HsDev.Tools.Types
-    HsDev.Types
-    HsDev.Util
-    HsDev.Version
-    HsDev.Watcher
-    HsDev.Watcher.Types
-    System.Directory.Paths
-    System.Directory.Watcher
-
-  if os(windows)
-    build-depends:
-      Win32 >= 2.3.0
-    exposed-modules:
-      System.Win32.PowerShell
-      System.Win32.FileMapping.NamePool
-      System.Win32.FileMapping.Memory
-  else
-    build-depends:
-      unix >= 2.7.0
-
-  if impl(ghc >= 8.0)
-    build-depends:
-      haddock-api >= 2.17.0 && < 2.18.0,
-      ghc >= 8.0.0 && < 8.1.0,
-      ghc-boot,
-      directory >= 1.2.6.0
-  if impl(ghc < 8.0)
-    build-depends:
-      haddock-api >= 2.16.0 && < 2.17.0,
-      ghc >= 7.10.0 && < 7.11.0,
-      bin-package-db,
-      directory == 1.2.2.*
-
-  build-depends:
-    base >= 4.7 && < 5,
-    aeson >= 0.7.0,
-    aeson-pretty >= 0.7.0,
-    array >= 0.5.0,
-    async >= 2.0,
-    attoparsec >= 0.11.0,
-    bytestring >= 0.10.0,
-    Cabal >= 1.22.0,
-    containers >= 0.5.0,
-    cpphs >= 1.19.0,
-    data-default >= 0.5.0,
-    deepseq >= 1.4.0,
-    exceptions >= 0.6.0,
-    filepath >= 1.4.0,
-    fsnotify >= 0.2.1,
-    ghc-paths >= 0.1.0,
-    ghc-syb-utils >= 0.2.3,
-    haskell-src-exts >= 1.18.0 && < 1.20.0,
-    hdocs >= 0.5.0,
-    hformat == 0.3.*,
-    hlint >= 1.9.13 && < 2.1,
-    HTTP >= 4000.2.0,
-    lens >= 4.8,
-    lifted-base >= 0.2,
-    monad-control >= 1.0,
-    monad-loops >= 0.4,
-    mmorph == 1.0.*,
-    mtl >= 2.2.0,
-    network >= 2.6.0,
-    optparse-applicative >= 0.11,
-    process >= 1.2.0,
-    regex-pcre-builtin >= 0.94,
-    scientific >= 0.3,
-    simple-log == 0.9.*,
-    syb >= 0.5.1,
-    template-haskell,
-    text >= 1.2.0,
-    text-region == 0.3.*,
-    time >= 1.5.0,
-    transformers >= 0.4.0,
-    transformers-base >= 0.4.0,
-    uniplate >= 1.6.0,
-    unordered-containers >= 0.2.0,
-    vector >= 0.10.0
-
-executable hsdev
-  main-is: hsdev.hs
-  hs-source-dirs: tools
-  ghc-options: -threaded -Wall -fno-warn-tabs "-with-rtsopts=-N4"
-
-  build-depends:
-    base >= 4.7 && < 5,
-    hsdev,
-    aeson >= 0.7.0,
-    aeson-pretty >= 0.7.0,
-    bytestring >= 0.10.0,
-    containers >= 0.5.0,
-    deepseq >= 1.4.0,
-    directory,
-    exceptions >= 0.6.0,
-    filepath >= 1.4.0,
-    monad-loops >= 0.4.0,
-    mtl >= 2.2.0,
-    network >= 2.6.0,
-    optparse-applicative >= 0.11,
-    process >= 1.2.0,
-    text >= 1.2.0,
-    transformers >= 0.4.0,
-    unordered-containers >= 0.2.0
-
-executable hsinspect
-  main-is: hsinspect.hs
-  hs-source-dirs: tools
-  ghc-options: -Wall -fno-warn-tabs
-  other-modules:
-    Tool
-  build-depends:
-    base >= 4.7 && < 5,
-    hsdev,
-    aeson >= 0.7.0,
-    aeson-pretty >= 0.7.0,
-    bytestring >= 0.10.0,
-    containers >= 0.5.0,
-    data-default >= 0.5.0,
-    directory,
-    filepath >= 1.4.0,
-    mtl >= 2.2.0,
-    optparse-applicative >= 0.11,
-    text >= 1.2.0,
-    transformers >= 0.4.0,
-    unordered-containers >= 0.2.0,
-    vector >= 0.10.0
-
-executable hsclearimports
-  main-is: hsclearimports.hs
-  hs-source-dirs: tools
-  ghc-options: -Wall -fno-warn-tabs
-  other-modules:
-    Tool
-  build-depends:
-    base >= 4.7 && < 5,
-    hsdev,
-    aeson >= 0.7.0,
-    aeson-pretty >= 0.7.0,
-    bytestring >= 0.10.0,
-    containers >= 0.5.0,
-    data-default >= 0.5.0,
-    directory,
-    haskell-src-exts >= 1.18.0 && < 1.20.0,
-    lens >= 4.8,
-    mtl >= 2.2.0,
-    optparse-applicative >= 0.11,
-    text >= 1.2.0,
-    transformers >= 0.4.0,
-    unordered-containers >= 0.2.0
-
-executable hscabal
-  main-is: hscabal.hs
-  hs-source-dirs: tools
-  ghc-options: -Wall -fno-warn-tabs
-  other-modules:
-    Tool
-  build-depends:
-    base >= 4.7 && < 5,
-    hsdev,
-    aeson >= 0.7.0,
-    aeson-pretty >= 0.7.0,
-    bytestring >= 0.10.0,
-    containers >= 0.5.0,
-    data-default >= 0.5.0,
-    mtl >= 2.2.0,
-    optparse-applicative >= 0.11,
-    text >= 1.2.0,
-    transformers >= 0.4.0,
-    unordered-containers >= 0.2.0
-
-executable hshayoo
-  main-is: hshayoo.hs
-  hs-source-dirs: tools
-  ghc-options: -Wall -fno-warn-tabs
-  other-modules:
-    Tool
-  build-depends:
-    base >= 4.7 && < 5,
-    hsdev,
-    aeson >= 0.7.0,
-    aeson-pretty >= 0.7.0,
-    bytestring >= 0.10.0,
-    containers >= 0.5.0,
-    data-default >= 0.5.0,
-    mtl >= 2.2.0,
-    optparse-applicative >= 0.11,
-    text >= 1.2.0,
-    transformers >= 0.4.0,
-    unordered-containers >= 0.2.0
-
-executable hsautofix
-  main-is: hsautofix.hs
-  hs-source-dirs: tools
-  ghc-options: -Wall -fno-warn-tabs
-  other-modules:
-    Tool
-  build-depends:
-    base >= 4.7 && < 5,
-    hsdev,
-    aeson >= 0.7.0,
-    aeson-pretty >= 0.7.0,
-    bytestring >= 0.10.0,
-    data-default >= 0.5.0,
-    directory,
-    filepath >= 1.4.0,
-    lens >= 4.8,
-    mtl >= 2.2.0,
-    optparse-applicative >= 0.11,
-    transformers >= 0.4.0
-
-test-suite test
-  main-is: Test.hs
-  hs-source-dirs: tests
-  ghc-options: -threaded -Wall -fno-warn-tabs
-  type: exitcode-stdio-1.0
-  build-depends:
-    base >= 4.7 && < 5,
-    hsdev,
-    aeson >= 0.7.0,
-    aeson-lens >= 0.5.0,
-    async >= 2.0,
-    data-default >= 0.5.0,
-    deepseq >= 1.4.0,
-    directory,
-    filepath >= 1.4.0,
-    containers >= 0.5.0,
-    hformat == 0.3.*,
-    hspec,
-    lens >= 4.8,
-    mtl >= 2.2.0,
-    text >= 1.2.0
+name:                hsdev
+version:             0.2.5.1
+synopsis:            Haskell development library
+description:
+  Haskell development library and tool with support of autocompletion, symbol info, go to declaration, find references, hayoo search etc.
+homepage:            https://github.com/mvoidex/hsdev
+license:             BSD3
+license-file:        LICENSE
+author:              Alexandr `Voidex` Ruchkin
+maintainer:          voidex@live.com
+-- copyright:
+category:            Development
+build-type:          Simple
+cabal-version:       >=1.8
+extra-source-files:
+    tests/test-package/*.hs
+    tests/test-package/test-package.cabal
+
+source-repository head
+  type: git
+  location: git://github.com/mvoidex/hsdev.git
+
+library
+  hs-source-dirs: src
+  ghc-options: -Wall -fno-warn-tabs
+  exposed-modules:
+    Control.Apply.Util
+    Control.Concurrent.FiniteChan
+    Control.Concurrent.Worker
+    Control.Concurrent.Util
+    Data.Async
+    Data.Deps
+    Data.Group
+    Data.Help
+    Data.Lisp
+    Data.Maybe.JustIf
+    HsDev
+    HsDev.Cache
+    HsDev.Cache.Structured
+    HsDev.Client.Commands
+    HsDev.Commands
+    HsDev.Database
+    HsDev.Database.Async
+    HsDev.Database.Update
+    HsDev.Database.Update.Types
+    HsDev.Display
+    HsDev.Error
+    HsDev.Inspect
+    HsDev.PackageDb
+    HsDev.Project
+    HsDev.Project.Compat
+    HsDev.Project.Types
+    HsDev.Scan
+    HsDev.Scan.Browse
+    HsDev.Server.Base
+    HsDev.Server.Commands
+    HsDev.Server.Message
+    HsDev.Server.Types
+    HsDev.Sandbox
+    HsDev.Stack
+    HsDev.Symbols
+    HsDev.Symbols.Class
+    HsDev.Symbols.Location
+    HsDev.Symbols.Documented
+    HsDev.Symbols.Resolve
+    HsDev.Symbols.Types
+    HsDev.Symbols.Util
+    HsDev.Tools.AutoFix
+    HsDev.Tools.Base
+    HsDev.Tools.Cabal
+    HsDev.Tools.ClearImports
+    HsDev.Tools.Ghc.Check
+    HsDev.Tools.Ghc.Compat
+    HsDev.Tools.Ghc.MGhc
+    HsDev.Tools.Ghc.Prelude
+    HsDev.Tools.Ghc.Types
+    HsDev.Tools.Ghc.Worker
+    HsDev.Tools.Ghc.Session
+    HsDev.Tools.Hayoo
+    HsDev.Tools.HDocs
+    HsDev.Tools.HLint
+    HsDev.Tools.Types
+    HsDev.Types
+    HsDev.Util
+    HsDev.Version
+    HsDev.Watcher
+    HsDev.Watcher.Types
+    System.Directory.Paths
+    System.Directory.Watcher
+
+  if os(windows)
+    build-depends:
+      Win32 >= 2.3.0
+    exposed-modules:
+      System.Win32.PowerShell
+      System.Win32.FileMapping.NamePool
+      System.Win32.FileMapping.Memory
+  else
+    build-depends:
+      unix >= 2.7.0
+
+  if impl(ghc >= 8.2) && impl(ghc < 8.3)
+    build-depends:
+      haddock-api >= 2.18.0 && < 2.19.0,
+      ghc == 8.2.*,
+      ghc-boot,
+      directory >= 1.2.6.0
+  if impl(ghc >= 8.0) && impl(ghc < 8.2)
+    build-depends:
+      haddock-api >= 2.17.0 && < 2.18.0,
+      ghc >= 8.0.0 && < 8.1.0,
+      ghc-boot,
+      directory >= 1.2.6.0
+  if impl(ghc < 8.0)
+    build-depends:
+      haddock-api >= 2.16.0 && < 2.17.0,
+      ghc >= 7.10.0 && < 7.11.0,
+      bin-package-db,
+      directory == 1.2.2.*
+
+  build-depends:
+    base >= 4.7 && < 5,
+    aeson >= 0.7.0,
+    aeson-pretty >= 0.7.0,
+    array >= 0.5.0,
+    async >= 2.0,
+    attoparsec >= 0.11.0,
+    bytestring >= 0.10.0,
+    Cabal >= 1.22.0,
+    containers >= 0.5.0,
+    cpphs >= 1.19.0,
+    data-default >= 0.5.0,
+    deepseq >= 1.4.0,
+    exceptions >= 0.6.0,
+    filepath >= 1.4.0,
+    fsnotify >= 0.2.1,
+    ghc-paths >= 0.1.0,
+    ghc-syb-utils >= 0.2.3,
+    haskell-src-exts >= 1.18.0 && < 1.20.0,
+    hdocs >= 0.5.0,
+    hformat == 0.3.*,
+    hlint >= 1.9.13 && < 2.1,
+    HTTP >= 4000.2.0,
+    lens >= 4.8,
+    lifted-base >= 0.2,
+    monad-control >= 1.0,
+    monad-loops >= 0.4,
+    mmorph >= 1.0 && < 1.2,
+    mtl >= 2.2.0,
+    network >= 2.6.0,
+    optparse-applicative >= 0.11,
+    process >= 1.2.0,
+    regex-pcre-builtin >= 0.94,
+    scientific >= 0.3,
+    simple-log == 0.9.*,
+    syb >= 0.5.1,
+    template-haskell,
+    text >= 1.2.0,
+    text-region == 0.3.*,
+    time >= 1.5.0,
+    transformers >= 0.4.0,
+    transformers-base >= 0.4.0,
+    uniplate >= 1.6.0,
+    unordered-containers >= 0.2.0,
+    vector >= 0.10.0
+
+executable hsdev
+  main-is: hsdev.hs
+  hs-source-dirs: tools
+  ghc-options: -threaded -Wall -fno-warn-tabs "-with-rtsopts=-N4"
+
+  build-depends:
+    base >= 4.7 && < 5,
+    hsdev,
+    aeson >= 0.7.0,
+    aeson-pretty >= 0.7.0,
+    bytestring >= 0.10.0,
+    containers >= 0.5.0,
+    deepseq >= 1.4.0,
+    directory,
+    exceptions >= 0.6.0,
+    filepath >= 1.4.0,
+    monad-loops >= 0.4.0,
+    mtl >= 2.2.0,
+    network >= 2.6.0,
+    optparse-applicative >= 0.11,
+    process >= 1.2.0,
+    text >= 1.2.0,
+    transformers >= 0.4.0,
+    unordered-containers >= 0.2.0
+
+executable hsinspect
+  main-is: hsinspect.hs
+  hs-source-dirs: tools
+  ghc-options: -Wall -fno-warn-tabs
+  other-modules:
+    Tool
+  build-depends:
+    base >= 4.7 && < 5,
+    hsdev,
+    aeson >= 0.7.0,
+    aeson-pretty >= 0.7.0,
+    bytestring >= 0.10.0,
+    containers >= 0.5.0,
+    data-default >= 0.5.0,
+    directory,
+    filepath >= 1.4.0,
+    mtl >= 2.2.0,
+    optparse-applicative >= 0.11,
+    text >= 1.2.0,
+    transformers >= 0.4.0,
+    unordered-containers >= 0.2.0,
+    vector >= 0.10.0
+
+executable hsclearimports
+  main-is: hsclearimports.hs
+  hs-source-dirs: tools
+  ghc-options: -Wall -fno-warn-tabs
+  other-modules:
+    Tool
+  build-depends:
+    base >= 4.7 && < 5,
+    hsdev,
+    aeson >= 0.7.0,
+    aeson-pretty >= 0.7.0,
+    bytestring >= 0.10.0,
+    containers >= 0.5.0,
+    data-default >= 0.5.0,
+    directory,
+    haskell-src-exts >= 1.18.0 && < 1.20.0,
+    lens >= 4.8,
+    mtl >= 2.2.0,
+    optparse-applicative >= 0.11,
+    text >= 1.2.0,
+    transformers >= 0.4.0,
+    unordered-containers >= 0.2.0
+
+executable hscabal
+  main-is: hscabal.hs
+  hs-source-dirs: tools
+  ghc-options: -Wall -fno-warn-tabs
+  other-modules:
+    Tool
+  build-depends:
+    base >= 4.7 && < 5,
+    hsdev,
+    aeson >= 0.7.0,
+    aeson-pretty >= 0.7.0,
+    bytestring >= 0.10.0,
+    containers >= 0.5.0,
+    data-default >= 0.5.0,
+    mtl >= 2.2.0,
+    optparse-applicative >= 0.11,
+    text >= 1.2.0,
+    transformers >= 0.4.0,
+    unordered-containers >= 0.2.0
+
+executable hshayoo
+  main-is: hshayoo.hs
+  hs-source-dirs: tools
+  ghc-options: -Wall -fno-warn-tabs
+  other-modules:
+    Tool
+  build-depends:
+    base >= 4.7 && < 5,
+    hsdev,
+    aeson >= 0.7.0,
+    aeson-pretty >= 0.7.0,
+    bytestring >= 0.10.0,
+    containers >= 0.5.0,
+    data-default >= 0.5.0,
+    mtl >= 2.2.0,
+    optparse-applicative >= 0.11,
+    text >= 1.2.0,
+    transformers >= 0.4.0,
+    unordered-containers >= 0.2.0
+
+executable hsautofix
+  main-is: hsautofix.hs
+  hs-source-dirs: tools
+  ghc-options: -Wall -fno-warn-tabs
+  other-modules:
+    Tool
+  build-depends:
+    base >= 4.7 && < 5,
+    hsdev,
+    aeson >= 0.7.0,
+    aeson-pretty >= 0.7.0,
+    bytestring >= 0.10.0,
+    data-default >= 0.5.0,
+    directory,
+    filepath >= 1.4.0,
+    lens >= 4.8,
+    mtl >= 2.2.0,
+    optparse-applicative >= 0.11,
+    transformers >= 0.4.0
+
+test-suite test
+  main-is: Test.hs
+  hs-source-dirs: tests
+  ghc-options: -threaded -Wall -fno-warn-tabs
+  type: exitcode-stdio-1.0
+  build-depends:
+    base >= 4.7 && < 5,
+    hsdev,
+    aeson >= 0.7.0,
+    aeson-lens >= 0.5.0,
+    async >= 2.0,
+    data-default >= 0.5.0,
+    deepseq >= 1.4.0,
+    directory,
+    filepath >= 1.4.0,
+    containers >= 0.5.0,
+    hformat == 0.3.*,
+    hspec,
+    lens >= 4.8,
+    mtl >= 2.2.0,
+    text >= 1.2.0
diff --git a/src/Data/Deps.hs b/src/Data/Deps.hs
--- a/src/Data/Deps.hs
+++ b/src/Data/Deps.hs
@@ -1,70 +1,70 @@
-{-# LANGUAGE FlexibleContexts, TypeFamilies #-}
-
-module Data.Deps (
-	Deps(..), depsMap,
-	mapDeps,
-	dep, deps,
-	inverse, flatten
-	) where
-
-import Control.Lens
-import Control.Monad.State
-import Data.List (nub)
-import Data.Map (Map)
-import qualified Data.Map as M
-import Data.Maybe (fromMaybe)
-
--- | Dependency map
-data Deps a = Deps {
-	_depsMap :: Map a [a] }
-
-depsMap :: Lens (Deps a) (Deps b) (Map a [a]) (Map b [b])
-depsMap = lens _depsMap (const Deps)
-
-instance Ord a => Monoid (Deps a) where
-	mempty = Deps mempty
-	mappend (Deps l) (Deps r) = Deps $ M.unionWith nubConcat l r
-
-type instance Index (Deps a) = a
-type instance IxValue (Deps a) = [a]
-
-instance Ord a => Ixed (Deps a) where
-	ix k = depsMap . ix k
-
-instance Ord a => At (Deps a) where
-	at k = depsMap . at k
-
-mapDeps :: Ord b => (a -> b) -> Deps a -> Deps b
-mapDeps f = Deps . M.mapKeys f . M.map (map f) . _depsMap
-
--- | Make single dependency
-dep :: a -> a -> Deps a
-dep x y = deps x [y]
-
--- | Make dependency for one target, note that order of dependencies is matter
-deps :: a -> [a] -> Deps a
-deps x ys = Deps $ M.singleton x ys
-
--- | Inverse dependencies, i.e. make map where keys are dependencies and elements are targets depends on it
-inverse :: Ord a => Deps a -> Deps a
-inverse = mconcat . map (uncurry dep) . concatMap inverse' . M.toList . _depsMap where
-	inverse' :: (a, [a]) -> [(a, a)]
-	inverse' (m, ds) = zip ds (repeat m)
-
--- | Flatten dependencies so that there will be no indirect dependencies
-flatten :: Ord a => Deps a -> Deps a
-flatten (Deps ds) = flip execState mempty . mapM_ flatten' . M.keys $ ds where
-	-- flatten' :: a -> State (Deps a) [a]
-	flatten' n = do
-		d <- gets (M.lookup n . _depsMap)
-		case d of
-			Just d' -> return d'
-			Nothing -> do
-				let
-					deps' = fromMaybe [] $ M.lookup n ds
-				d'' <- (nub . concat . (++ [deps'])) <$> mapM flatten' deps'
-				modify $ mappend (deps n d'')
-				return d''
-
-nubConcat :: Ord a => [a] -> [a] -> [a]
-nubConcat xs ys = nub $ xs ++ ys
+{-# LANGUAGE FlexibleContexts, TypeFamilies #-}
+
+module Data.Deps (
+	Deps(..), depsMap,
+	mapDeps,
+	dep, deps,
+	inverse, flatten
+	) where
+
+import Control.Lens
+import Control.Monad.State
+import Data.List (nub)
+import Data.Map (Map)
+import qualified Data.Map as M
+import Data.Maybe (fromMaybe)
+
+-- | Dependency map
+data Deps a = Deps {
+	_depsMap :: Map a [a] }
+
+depsMap :: Lens (Deps a) (Deps b) (Map a [a]) (Map b [b])
+depsMap = lens _depsMap (const Deps)
+
+instance Ord a => Monoid (Deps a) where
+	mempty = Deps mempty
+	mappend (Deps l) (Deps r) = Deps $ M.unionWith nubConcat l r
+
+type instance Index (Deps a) = a
+type instance IxValue (Deps a) = [a]
+
+instance Ord a => Ixed (Deps a) where
+	ix k = depsMap . ix k
+
+instance Ord a => At (Deps a) where
+	at k = depsMap . at k
+
+mapDeps :: Ord b => (a -> b) -> Deps a -> Deps b
+mapDeps f = Deps . M.mapKeys f . M.map (map f) . _depsMap
+
+-- | Make single dependency
+dep :: a -> a -> Deps a
+dep x y = deps x [y]
+
+-- | Make dependency for one target, note that order of dependencies is matter
+deps :: a -> [a] -> Deps a
+deps x ys = Deps $ M.singleton x ys
+
+-- | Inverse dependencies, i.e. make map where keys are dependencies and elements are targets depends on it
+inverse :: Ord a => Deps a -> Deps a
+inverse = mconcat . map (uncurry dep) . concatMap inverse' . M.toList . _depsMap where
+	inverse' :: (a, [a]) -> [(a, a)]
+	inverse' (m, ds) = zip ds (repeat m)
+
+-- | Flatten dependencies so that there will be no indirect dependencies
+flatten :: Ord a => Deps a -> Deps a
+flatten (Deps ds) = flip execState mempty . mapM_ flatten' . M.keys $ ds where
+	-- flatten' :: a -> State (Deps a) [a]
+	flatten' n = do
+		d <- gets (M.lookup n . _depsMap)
+		case d of
+			Just d' -> return d'
+			Nothing -> do
+				let
+					deps' = fromMaybe [] $ M.lookup n ds
+				d'' <- (nub . concat . (++ [deps'])) <$> mapM flatten' deps'
+				modify $ mappend (deps n d'')
+				return d''
+
+nubConcat :: Ord a => [a] -> [a] -> [a]
+nubConcat xs ys = nub $ xs ++ ys
diff --git a/src/Data/Group.hs b/src/Data/Group.hs
--- a/src/Data/Group.hs
+++ b/src/Data/Group.hs
@@ -1,40 +1,40 @@
-{-# LANGUAGE TypeSynonymInstances #-}
-
-module Data.Group (
-	Group(..),
-	groupSum
-	) where
-
-import Data.List ((\\))
-import Data.Map (Map)
-import qualified Data.Map as M
-import Data.Set (Set)
-import qualified Data.Set as S
-
--- | Group is monoid with invertibility
--- But for our purposes we prefer two functions: `add` and `sub`.
-class Eq a => Group a where
-	add :: a -> a -> a
-	sub :: a -> a -> a
-	zero :: a
-
-instance Eq a => Group [a] where
-	add = (++)
-	sub = (\\)
-	zero = []
-
-instance Ord a => Group (Set a) where
-	add = S.union
-	sub = S.difference
-	zero = S.empty
-
-instance (Ord k, Group a) => Group (Map k a) where
-	add = M.unionWith add
-	sub = M.differenceWith sub' where
-		sub' x y = if z == zero then Nothing else Just z where
-			z = sub x y
-	zero = M.empty
-
--- | Sums list
-groupSum :: Group a => [a] -> a
-groupSum = foldr add zero
+{-# LANGUAGE TypeSynonymInstances #-}
+
+module Data.Group (
+	Group(..),
+	groupSum
+	) where
+
+import Data.List ((\\))
+import Data.Map (Map)
+import qualified Data.Map as M
+import Data.Set (Set)
+import qualified Data.Set as S
+
+-- | Group is monoid with invertibility
+-- But for our purposes we prefer two functions: `add` and `sub`.
+class Eq a => Group a where
+	add :: a -> a -> a
+	sub :: a -> a -> a
+	zero :: a
+
+instance Eq a => Group [a] where
+	add = (++)
+	sub = (\\)
+	zero = []
+
+instance Ord a => Group (Set a) where
+	add = S.union
+	sub = S.difference
+	zero = S.empty
+
+instance (Ord k, Group a) => Group (Map k a) where
+	add = M.unionWith add
+	sub = M.differenceWith sub' where
+		sub' x y = if z == zero then Nothing else Just z where
+			z = sub x y
+	zero = M.empty
+
+-- | Sums list
+groupSum :: Group a => [a] -> a
+groupSum = foldr add zero
diff --git a/src/Data/Help.hs b/src/Data/Help.hs
--- a/src/Data/Help.hs
+++ b/src/Data/Help.hs
@@ -1,21 +1,21 @@
-module Data.Help (
-	Help(..),
-	indent, indentHelp, detailed, indented
-	) where
-
-class Help a where
-	brief :: a -> String
-	help :: a -> [String]
-
-indent :: String -> String
-indent = ('\t':)
-
-indentHelp :: [String] -> [String]
-indentHelp [] = []
-indentHelp (h:hs) = h : map indent hs
-
-detailed :: Help a => a -> [String]
-detailed v = brief v : help v
-
-indented :: Help a => a -> [String]
-indented = indentHelp . detailed
+module Data.Help (
+	Help(..),
+	indent, indentHelp, detailed, indented
+	) where
+
+class Help a where
+	brief :: a -> String
+	help :: a -> [String]
+
+indent :: String -> String
+indent = ('\t':)
+
+indentHelp :: [String] -> [String]
+indentHelp [] = []
+indentHelp (h:hs) = h : map indent hs
+
+detailed :: Help a => a -> [String]
+detailed v = brief v : help v
+
+indented :: Help a => a -> [String]
+indented = indentHelp . detailed
diff --git a/src/HsDev.hs b/src/HsDev.hs
--- a/src/HsDev.hs
+++ b/src/HsDev.hs
@@ -1,33 +1,33 @@
-module HsDev (
-	module Data.Default,
-
-	module HsDev.Types,
-	module HsDev.Error,
-	module HsDev.Server.Base,
-	module HsDev.Server.Commands,
-	module HsDev.Client.Commands,
-	module HsDev.Cache,
-	module HsDev.Commands,
-	module HsDev.Database,
-	module HsDev.Inspect,
-	module HsDev.Project,
-	module HsDev.Scan,
-	module HsDev.Symbols,
-	module HsDev.Util
-	) where
-
-import Data.Default
-
-import HsDev.Types
-import HsDev.Error
-import HsDev.Server.Base
-import HsDev.Server.Commands
-import HsDev.Client.Commands
-import HsDev.Cache
-import HsDev.Commands
-import HsDev.Database
-import HsDev.Inspect
-import HsDev.Project
-import HsDev.Scan
-import HsDev.Symbols
-import HsDev.Util
+module HsDev (
+	module Data.Default,
+
+	module HsDev.Types,
+	module HsDev.Error,
+	module HsDev.Server.Base,
+	module HsDev.Server.Commands,
+	module HsDev.Client.Commands,
+	module HsDev.Cache,
+	module HsDev.Commands,
+	module HsDev.Database,
+	module HsDev.Inspect,
+	module HsDev.Project,
+	module HsDev.Scan,
+	module HsDev.Symbols,
+	module HsDev.Util
+	) where
+
+import Data.Default
+
+import HsDev.Types
+import HsDev.Error
+import HsDev.Server.Base
+import HsDev.Server.Commands
+import HsDev.Client.Commands
+import HsDev.Cache
+import HsDev.Commands
+import HsDev.Database
+import HsDev.Inspect
+import HsDev.Project
+import HsDev.Scan
+import HsDev.Symbols
+import HsDev.Util
diff --git a/src/HsDev/Cache.hs b/src/HsDev/Cache.hs
--- a/src/HsDev/Cache.hs
+++ b/src/HsDev/Cache.hs
@@ -1,79 +1,79 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module HsDev.Cache (
-	escapePath,
-	versionCache,
-	packageDbCache,
-	projectCache,
-	standaloneCache,
-	dump,
-	load,
-	writeVersion,
-	readVersion,
-
-	-- * Reexports
-	Database
-	) where
-
-import Control.DeepSeq (force)
-import Control.Exception
-import Control.Lens (view)
-import Data.Aeson (encode, eitherDecode)
-import Data.Aeson.Encode.Pretty (encodePretty)
-import qualified Data.ByteString.Lazy.Char8 as BS
-import Data.Char (isAlphaNum)
-import Data.List (intercalate)
-import System.FilePath
-
-import HsDev.PackageDb
-import HsDev.Project
-import HsDev.Database (Database)
-import HsDev.Util (version)
-
--- | Escape path
-escapePath :: FilePath -> FilePath
-escapePath = intercalate "." . map (filter isAlphaNum) . splitDirectories
-
--- | Name of cache for version
-versionCache :: FilePath
-versionCache = "version" <.> "json"
-
--- | Name of cache for cabal
-packageDbCache :: PackageDb -> FilePath
-packageDbCache GlobalDb = "global" <.> "json"
-packageDbCache UserDb = "user" <.> "json"
-packageDbCache (PackageDb p) = escapePath p <.> "json"
-
--- | Name of cache for projects
-projectCache :: Project -> FilePath
-projectCache p = (escapePath . view projectPath $ p) <.> "json"
-
--- | Name of cache for standalone files
-standaloneCache :: FilePath
-standaloneCache = "standalone" <.> "json"
-
--- | Dump database to file
-dump :: FilePath -> Database -> IO ()
-dump file = BS.writeFile file . encodePretty
-
--- | Load database from file, strict
-load :: FilePath -> IO (Either String Database)
-load file = handle onIO $ do
-	cts <- BS.readFile file
-	return $ force $ eitherDecode cts
-	where
-		onIO :: IOException -> IO (Either String Database)
-		onIO _ = return $ Left $ "IO exception while reading cache from " ++ file
-
--- | Write version
-writeVersion :: FilePath -> IO ()
-writeVersion file = BS.writeFile file $ encode version
-
--- | Read version
-readVersion :: FilePath -> IO (Maybe [Int])
-readVersion file = handle onIO $ do
-	cts <- BS.readFile file
-	return $ either (const Nothing) id $ eitherDecode cts
-	where
-		onIO :: IOException -> IO (Maybe [Int])
-		onIO _ = return Nothing
+{-# LANGUAGE OverloadedStrings #-}
+
+module HsDev.Cache (
+	escapePath,
+	versionCache,
+	packageDbCache,
+	projectCache,
+	standaloneCache,
+	dump,
+	load,
+	writeVersion,
+	readVersion,
+
+	-- * Reexports
+	Database
+	) where
+
+import Control.DeepSeq (force)
+import Control.Exception
+import Control.Lens (view)
+import Data.Aeson (encode, eitherDecode)
+import Data.Aeson.Encode.Pretty (encodePretty)
+import qualified Data.ByteString.Lazy.Char8 as BS
+import Data.Char (isAlphaNum)
+import Data.List (intercalate)
+import System.FilePath
+
+import HsDev.PackageDb
+import HsDev.Project
+import HsDev.Database (Database)
+import HsDev.Util (version)
+
+-- | Escape path
+escapePath :: FilePath -> FilePath
+escapePath = intercalate "." . map (filter isAlphaNum) . splitDirectories
+
+-- | Name of cache for version
+versionCache :: FilePath
+versionCache = "version" <.> "json"
+
+-- | Name of cache for cabal
+packageDbCache :: PackageDb -> FilePath
+packageDbCache GlobalDb = "global" <.> "json"
+packageDbCache UserDb = "user" <.> "json"
+packageDbCache (PackageDb p) = escapePath p <.> "json"
+
+-- | Name of cache for projects
+projectCache :: Project -> FilePath
+projectCache p = (escapePath . view projectPath $ p) <.> "json"
+
+-- | Name of cache for standalone files
+standaloneCache :: FilePath
+standaloneCache = "standalone" <.> "json"
+
+-- | Dump database to file
+dump :: FilePath -> Database -> IO ()
+dump file = BS.writeFile file . encodePretty
+
+-- | Load database from file, strict
+load :: FilePath -> IO (Either String Database)
+load file = handle onIO $ do
+	cts <- BS.readFile file
+	return $ force $ eitherDecode cts
+	where
+		onIO :: IOException -> IO (Either String Database)
+		onIO _ = return $ Left $ "IO exception while reading cache from " ++ file
+
+-- | Write version
+writeVersion :: FilePath -> IO ()
+writeVersion file = BS.writeFile file $ encode version
+
+-- | Read version
+readVersion :: FilePath -> IO (Maybe [Int])
+readVersion file = handle onIO $ do
+	cts <- BS.readFile file
+	return $ either (const Nothing) id $ eitherDecode cts
+	where
+		onIO :: IOException -> IO (Maybe [Int])
+		onIO _ = return Nothing
diff --git a/src/HsDev/Cache/Structured.hs b/src/HsDev/Cache/Structured.hs
--- a/src/HsDev/Cache/Structured.hs
+++ b/src/HsDev/Cache/Structured.hs
@@ -1,72 +1,72 @@
-module HsDev.Cache.Structured (
-	dump, load,
-	loadPackageDb, loadProject, loadFiles
-	) where
-
-import Control.DeepSeq
-import Control.Exception
-import Control.Lens (preview)
-import Control.Monad.Except
-import qualified Data.Map as M (assocs)
-import System.Directory
-import System.FilePath
-
-import Data.Group (Group(zero))
-import qualified HsDev.Cache as Cache
-import HsDev.Database
-import HsDev.Symbols hiding (loadProject)
-import HsDev.Util
-
--- | Write cache
-dump :: FilePath -> Structured -> IO ()
-dump dir db = do
-	createDirectoryIfMissing True (dir </> "cabal")
-	createDirectoryIfMissing True (dir </> "projects")
-	forM_ (M.assocs $ structuredPackageDbs db) $ \(c, cdb) -> Cache.dump
-		(dir </> "cabal" </> Cache.packageDbCache c)
-		cdb
-	forM_ (M.assocs $ structuredProjects db) $ \(p, pdb) -> Cache.dump
-		(dir </> "projects" </> Cache.projectCache (project p))
-		pdb
-	files' <- either (const zero) id <$>
-		handle wrapIO
-		(Cache.load (dir </> Cache.standaloneCache))
-	files' `deepseq` Cache.dump (dir </> Cache.standaloneCache) (files' `mappend` structuredFiles db)
-	where
-		wrapIO :: SomeException -> IO (Either String Database)
-		wrapIO = return . Left . show
-
--- | Load all cache
-load :: FilePath -> IO (Either String Structured)
-load dir = runExceptT $ join $ either throwError return <$> (structured <$> loadPackageDbs <*> loadProjects <*> loadStandaloneFiles) where
-	loadPackageDbs = loadDir (dir </> "cabal")
-	loadProjects = loadDir (dir </> "projects")
-	loadStandaloneFiles = ExceptT $ Cache.load (dir </> Cache.standaloneCache)
-
-	loadDir p = do
-		fs <- liftE $ liftM (filter ((== ".json") . takeExtension)) $ directoryContents p
-		mapM (ExceptT . Cache.load) fs
-
--- | Load data from cache
-loadData :: FilePath -> ExceptT String IO Database
-loadData = liftExceptionM . ExceptT . Cache.load
-
--- | Load cabal from cache
-loadPackageDb :: PackageDb -> FilePath -> ExceptT String IO Structured
-loadPackageDb c dir = do
-	dat <- loadData (dir </> "cabal" </> Cache.packageDbCache c)
-	ExceptT $ return $ structured [dat] [] mempty
-
--- | Load project from cache
-loadProject :: FilePath -> FilePath -> ExceptT String IO Structured
-loadProject p dir = do
-	dat <- loadData (dir </> "projects" </> Cache.projectCache (project p))
-	ExceptT $ return $ structured [] [dat] mempty
-
--- | Load standalone files
-loadFiles :: (FilePath -> Bool) -> FilePath -> ExceptT String IO Structured
-loadFiles f dir = do
-	dat <- loadData (dir </> Cache.standaloneCache)
-	ExceptT $ return $ structured [] [] $ filterDB f' (const False) dat
-	where
-		f' = maybe False f . preview (moduleIdLocation . moduleFile)
+module HsDev.Cache.Structured (
+	dump, load,
+	loadPackageDb, loadProject, loadFiles
+	) where
+
+import Control.DeepSeq
+import Control.Exception
+import Control.Lens (preview)
+import Control.Monad.Except
+import qualified Data.Map as M (assocs)
+import System.Directory
+import System.FilePath
+
+import Data.Group (Group(zero))
+import qualified HsDev.Cache as Cache
+import HsDev.Database
+import HsDev.Symbols hiding (loadProject)
+import HsDev.Util
+
+-- | Write cache
+dump :: FilePath -> Structured -> IO ()
+dump dir db = do
+	createDirectoryIfMissing True (dir </> "cabal")
+	createDirectoryIfMissing True (dir </> "projects")
+	forM_ (M.assocs $ structuredPackageDbs db) $ \(c, cdb) -> Cache.dump
+		(dir </> "cabal" </> Cache.packageDbCache c)
+		cdb
+	forM_ (M.assocs $ structuredProjects db) $ \(p, pdb) -> Cache.dump
+		(dir </> "projects" </> Cache.projectCache (project p))
+		pdb
+	files' <- either (const zero) id <$>
+		handle wrapIO
+		(Cache.load (dir </> Cache.standaloneCache))
+	files' `deepseq` Cache.dump (dir </> Cache.standaloneCache) (files' `mappend` structuredFiles db)
+	where
+		wrapIO :: SomeException -> IO (Either String Database)
+		wrapIO = return . Left . show
+
+-- | Load all cache
+load :: FilePath -> IO (Either String Structured)
+load dir = runExceptT $ join $ either throwError return <$> (structured <$> loadPackageDbs <*> loadProjects <*> loadStandaloneFiles) where
+	loadPackageDbs = loadDir (dir </> "cabal")
+	loadProjects = loadDir (dir </> "projects")
+	loadStandaloneFiles = ExceptT $ Cache.load (dir </> Cache.standaloneCache)
+
+	loadDir p = do
+		fs <- liftE $ liftM (filter ((== ".json") . takeExtension)) $ directoryContents p
+		mapM (ExceptT . Cache.load) fs
+
+-- | Load data from cache
+loadData :: FilePath -> ExceptT String IO Database
+loadData = liftExceptionM . ExceptT . Cache.load
+
+-- | Load cabal from cache
+loadPackageDb :: PackageDb -> FilePath -> ExceptT String IO Structured
+loadPackageDb c dir = do
+	dat <- loadData (dir </> "cabal" </> Cache.packageDbCache c)
+	ExceptT $ return $ structured [dat] [] mempty
+
+-- | Load project from cache
+loadProject :: FilePath -> FilePath -> ExceptT String IO Structured
+loadProject p dir = do
+	dat <- loadData (dir </> "projects" </> Cache.projectCache (project p))
+	ExceptT $ return $ structured [] [dat] mempty
+
+-- | Load standalone files
+loadFiles :: (FilePath -> Bool) -> FilePath -> ExceptT String IO Structured
+loadFiles f dir = do
+	dat <- loadData (dir </> Cache.standaloneCache)
+	ExceptT $ return $ structured [] [] $ filterDB f' (const False) dat
+	where
+		f' = maybe False f . preview (moduleIdLocation . moduleFile)
diff --git a/src/HsDev/Client/Commands.hs b/src/HsDev/Client/Commands.hs
--- a/src/HsDev/Client/Commands.hs
+++ b/src/HsDev/Client/Commands.hs
@@ -1,414 +1,414 @@
-{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
-
-module HsDev.Client.Commands (
-	runClient, runCommand
-	) where
-
-import Control.Applicative
-import Control.Concurrent.MVar
-import Control.Exception (displayException)
-import Control.Lens (view, preview, _Just, (^..), each)
-import Control.Monad
-import Control.Monad.Except
-import Control.Monad.Reader
-import qualified Control.Monad.State as State
-import Control.Monad.Catch (try, catch, bracket, SomeException(..))
-import Data.Aeson hiding (Result, Error)
-import Data.List
-import Data.Foldable (toList)
-import Data.Maybe
-import qualified Data.Map as M
-import Data.String (fromString)
-import Data.Text (pack, unpack)
-import qualified Data.Text as T (isInfixOf, isPrefixOf, isSuffixOf)
-import System.Directory
-import System.FilePath
-import qualified System.Log.Simple as Log
-import qualified System.Log.Simple.Base as Log
-import Text.Regex.PCRE ((=~))
-
-import qualified System.Directory.Watcher as W
-import qualified Data.Async as A
-import System.Directory.Paths
-import Text.Format
-import HsDev.Cache
-import HsDev.Commands
-import HsDev.Error
-import qualified HsDev.Database.Async as DB
-import HsDev.Server.Message as M
-import HsDev.Server.Types
-import HsDev.Sandbox hiding (findSandbox)
-import qualified HsDev.Sandbox as S (findSandbox)
-import HsDev.Symbols
-import HsDev.Symbols.Resolve (resolveOne, scopeModule, exportsModule)
-import HsDev.Symbols.Util
-import qualified HsDev.Tools.AutoFix as AutoFix
-import qualified HsDev.Tools.Cabal as Cabal
-import HsDev.Tools.Ghc.Session
-import qualified HsDev.Tools.Ghc.Compat as Compat
-import qualified HsDev.Tools.Ghc.Check as Check
-import qualified HsDev.Tools.Ghc.Types as Types
-import qualified HsDev.Tools.Hayoo as Hayoo
-import qualified HsDev.Tools.HLint as HLint
-import qualified HsDev.Tools.Types as Tools
-import HsDev.Util
-import HsDev.Watcher
-
-import qualified HsDev.Database.Update as Update
-
-runClient :: (ToJSON a, ServerMonadBase m) => CommandOptions -> ClientM m a -> ServerM m Result
-runClient copts = mapServerM toResult . runClientM where
-	toResult :: (ToJSON a, ServerMonadBase m) => ReaderT CommandOptions m a -> m Result
-	toResult act = liftM (either Error (Result . toJSON)) $ runReaderT (try act) copts
-	mapServerM :: (m a -> n b) -> ServerM m a -> ServerM n b
-	mapServerM f = ServerM . mapReaderT f . runServerM
-
-toValue :: (ToJSON a, Monad m) => m a -> m Value
-toValue = liftM toJSON
-
-runCommand :: ServerMonadBase m => Command -> ClientM m Value
-runCommand Ping = toValue $ return $ object ["message" .= ("pong" :: String)]
-runCommand (Listen (Just l)) = case Log.level (pack l) of
-	Nothing -> hsdevError $ OtherError $ "invalid log level: {}" ~~ l
-	Just lev -> bracket (serverSetLogLevel lev) serverSetLogLevel $ \_ -> runCommand (Listen Nothing)
-runCommand (Listen Nothing) = toValue $ do
-	serverListen >>= mapM_ (\msg -> commandNotify (Notification $ object ["message" .= msg]))
-runCommand (SetLogLevel l) = case Log.level (pack l) of
-	Nothing -> hsdevError $ OtherError $ "invalid log level: {}" ~~ l
-	Just lev -> toValue $ do
-		lev' <- serverSetLogLevel lev
-		Log.sendLog Log.Debug $ "log level changed from '{}' to '{}'" ~~ show lev' ~~ show lev
-		Log.sendLog Log.Info $ "log level updated to: {}" ~~ show lev
-runCommand (AddData cts) = toValue $ mapM_ updateData cts where
-	updateData (AddedDatabase db) = toValue $ serverUpdateDB db
-	updateData (AddedModule m) = toValue $ serverUpdateDB $ fromModule m
-	updateData (AddedProject p) = toValue $ serverUpdateDB $ fromProject p
-runCommand (Scan projs cabal sboxes fs paths' ghcs' docs' infer') = toValue $ do
-	sboxes' <- getSandboxes sboxes
-	updateProcess (Update.UpdateOptions [] ghcs' docs' infer') $ concat [
-		map (\(FileSource f mcts) -> Update.scanFileContents ghcs' f mcts) fs,
-		map (Update.scanProject ghcs') projs,
-		map (Update.scanDirectory ghcs') paths',
-		[Update.scanCabal ghcs' | cabal],
-		map (Update.scanSandbox ghcs') sboxes']
-runCommand (RefineDocs projs fs ms) = toValue $ do
-	projects <- traverse findProject projs
-	dbval <- getDb
-	let
-		filters = anyOf $ concat [
-			map inProject projects,
-			map inFile fs,
-			map inModule ms]
-		mods = selectModules (filters . view moduleId) dbval
-	updateProcess (Update.UpdateOptions [] [] False False) [Update.scanDocs $ map (getInspected dbval) mods]
-runCommand (InferTypes projs fs ms) = toValue $ do
-	projects <- traverse findProject projs
-	dbval <- getDb
-	let
-		filters = anyOf $ concat [
-			map inProject projects,
-			map inFile fs,
-			map inModule ms]
-		mods = selectModules (filters . view moduleId) dbval
-	updateProcess (Update.UpdateOptions [] [] False False) [Update.inferModTypes $ map (getInspected dbval) mods]
-runCommand (Remove projs cabal sboxes files) = toValue $ do
-	db <- askSession sessionDatabase
-	dbval <- getDb
-	w <- askSession sessionWatcher
-	projects <- traverse findProject projs
-	sboxes' <- getSandboxes sboxes
-	forM_ projects $ \proj -> do
-		DB.clear db (return $ projectDB proj dbval)
-		liftIO $ unwatchProject w proj
-	dbPDbs <- mapM restorePackageDbStack $ databasePackageDbs dbval
-	flip State.evalStateT dbPDbs $ do
-		when cabal $ removePackageDbStack userDb
-		forM_ sboxes' $ \sbox -> do
-			pdbs <- lift $ sandboxPackageDbStack sbox
-			removePackageDbStack pdbs
-	forM_ files $ \file -> do
-		DB.clear db (return $ filterDB (inFile file) (const False) dbval)
-		let
-			mloc = fmap (view moduleLocation) $ lookupFile file dbval
-		maybe (return ()) (liftIO . unwatchModule w) mloc
-	where
-		-- We can safely remove package-db from db iff doesn't used by some of other package-dbs
-		-- For example, we can't remove global-db if there are any other package-dbs, because all of them uses global-db
-		-- We also can't remove stack snapshot package-db if there are some local package-db not yet removed
-		canRemove pdbs = do
-			from <- State.get
-			return $ null $ filter (pdbs `isSubStack`) $ delete pdbs from
-		-- Remove top of package-db stack if possible
-		removePackageDb pdbs = do
-			db <- lift $ askSession sessionDatabase
-			dbval <- lift getDb
-			w <- lift $ askSession sessionWatcher
-			can <- canRemove pdbs
-			when can $ do
-				State.modify (delete pdbs)
-				DB.clear db (return $ packageDbDB (topPackageDb pdbs) dbval)
-				liftIO $ unwatchPackageDb w $ topPackageDb pdbs
-		-- Remove package-db stack when possible
-		removePackageDbStack = mapM_ removePackageDb . packageDbStacks
-runCommand RemoveAll = toValue $ do
-	db <- askSession sessionDatabase
-	liftIO $ A.modifyAsync db A.Clear
-	w <- askSession sessionWatcher
-	wdirs <- liftIO $ readMVar (W.watcherDirs w)
-	liftIO $ forM_ (M.toList wdirs) $ \(dir, (isTree, _)) -> (if isTree then W.unwatchTree else W.unwatchDir) w dir
-runCommand (InfoModules fs) = toValue $ do
-	dbval <- getDb
-	filter' <- targetFilters fs
-	return $ map (view moduleId) $ newestPackage $ selectModules (filter' . view moduleId) dbval
-runCommand InfoPackages = toValue $ (ordNub . sort . 	mapMaybe (preview (moduleLocation . modulePackage . _Just)) . allModules) <$> getDb
-runCommand InfoProjects = toValue $ (toList . databaseProjects) <$> getDb
-runCommand InfoSandboxes = toValue $ databasePackageDbs <$> getDb
-runCommand (InfoSymbol sq fs locals') = toValue $ do
-	dbval <- liftM (localsDatabase locals') $ getDb
-	filter' <- targetFilters fs
-	return $ newestPackage $ filterMatch sq $
-		concatMap moduleModuleDeclarations $ filter (filter' . view moduleId) $ allModules dbval
-runCommand (InfoModule sq fs) = toValue $ do
-	dbval <- getDb
-	filter' <- targetFilters fs
-	return $ newestPackage $ filterMatch sq $ filter (filter' . view moduleId) $ allModules dbval
-runCommand (InfoResolve fpath exports) = toValue $ do
-	dbval <- getSDb fpath
-	let
-		getScope
-			| exports = exportsModule
-			| otherwise = scopeModule
-	m <- refineSourceModule fpath
-	return $ getScope $ resolveOne dbval m
-runCommand (InfoProject (Left projName)) = toValue $ findProject projName
-runCommand (InfoProject (Right projPath)) = toValue $ liftIO $ searchProject projPath
-runCommand (InfoSandbox sandbox') = toValue $ liftIO $ searchSandbox sandbox'
-runCommand (Lookup nm fpath) = toValue $ do
-	dbval <- getSDb fpath
-	liftIO $ hsdevLift $ lookupSymbol dbval fpath nm
-runCommand (Whois nm fpath) = toValue $ do
-	dbval <- getSDb fpath
-	liftIO $ hsdevLift $ whois dbval fpath nm
-runCommand (ResolveScopeModules sq fpath) = toValue $ do
-	dbval <- getSDb fpath
-	liftM (filterMatch sq . map (view moduleId)) $ liftIO $ hsdevLift $ scopeModules dbval fpath
-runCommand (ResolveScope sq global fpath) = toValue $ do
-	dbval <- getSDb fpath
-	liftM (filterMatch sq) $ liftIO $ hsdevLift $ scope dbval fpath global
-runCommand (Complete input wide fpath) = toValue $ do
-	dbval <- getSDb fpath
-	liftIO $ hsdevLift $ completions dbval fpath input wide
-runCommand (Hayoo hq p ps) = toValue $ liftM concat $ forM [p .. p + pred ps] $ \i -> liftM
-	(mapMaybe Hayoo.hayooAsDeclaration . Hayoo.resultResult) $
-	liftIO $ hsdevLift $ Hayoo.hayoo hq (Just i)
-runCommand (CabalList packages) = toValue $ liftIO $ hsdevLift $ Cabal.cabalList packages
-runCommand (Lint fs) = toValue $ do
-	liftIO $ hsdevLift $ liftM concat $ mapM (\(FileSource f c) -> HLint.hlint f c) fs
-runCommand (Check fs ghcs') = toValue $ Log.scope "check" $ do
-	-- ensureUpToDate (Update.UpdateOptions [] ghcs' False False) fs
-	let
-		checkSome file fn = Log.scope "checkSome" $ do
-			m <- setFileSourceSession ghcs' file
-			inSessionGhc $ fn m
-	liftM concat $ mapM (\(FileSource f c) -> checkSome f (\m -> Check.check ghcs' m c)) fs
-runCommand (CheckLint fs ghcs') = toValue $ do
-	-- ensureUpToDate (Update.UpdateOptions [] ghcs' False False) fs
-	let
-		checkSome file fn = Log.scope "checkSome" $ do
-			m <- setFileSourceSession ghcs' file
-			inSessionGhc $ fn m
-	checkMsgs <- liftM concat $ mapM (\(FileSource f c) -> checkSome f (\m -> Check.check ghcs' m c)) fs
-	lintMsgs <- liftIO $ hsdevLift $ liftM concat $ mapM (\(FileSource f c) -> HLint.hlint f c) fs
-	return $ checkMsgs ++ lintMsgs
-runCommand (Types fs ghcs') = toValue $ do
-	-- ensureUpToDate (Update.UpdateOptions [] ghcs' False False) fs
-	liftM concat $ forM fs $ \(FileSource file msrc) -> do
-		m <- setFileSourceSession ghcs' file
-		inSessionGhc $ Types.fileTypes ghcs' m msrc
-runCommand (AutoFix (AutoFixShow ns)) = toValue $ return $ AutoFix.corrections ns
-runCommand (AutoFix (AutoFixFix ns rest isPure)) = toValue $ do
-	files <- liftM (ordNub . sort) $ mapM findPath $ mapMaybe (preview $ Tools.noteSource . moduleFile) ns
-	let
-		doFix :: FilePath -> Maybe String -> ([Tools.Note AutoFix.Correction], Maybe String)
-		doFix file mcts = AutoFix.autoFix fCorrs (fUpCorrs, mcts) where
-			findCorrs :: FilePath -> [Tools.Note AutoFix.Correction] -> [Tools.Note AutoFix.Correction]
-			findCorrs f = filter ((== Just f) . preview (Tools.noteSource . moduleFile))
-			fCorrs = findCorrs file ns
-			fUpCorrs = findCorrs file rest
-		runFix file
-			| isPure = return $ fst $ doFix file Nothing
-			| otherwise = do
-				(corrs', Just cts') <- liftM (doFix file) $ liftIO $ Just <$> readFileUtf8 file
-				liftIO $ writeFileUtf8 file cts'
-				return corrs'
-	liftM concat $ mapM runFix files
-runCommand (GhcEval exprs mfile) = toValue $ do
-	-- ensureUpToDate (Update.UpdateOptions [] [] False False) (maybeToList mfile)
-	ghcw <- askSession sessionGhc
-	case mfile of
-		Nothing -> inSessionGhc ghciSession
-		Just (FileSource f mcts) -> do
-			m <- setFileSourceSession [] f
-			inSessionGhc $ interpretModule m mcts
-	async' <- liftIO $ pushTask ghcw $ do
-		mapM (try . evaluate) exprs
-	res <- waitAsync async'
-	return $ map toValue' res
-	where
-		waitAsync :: CommandMonad m => Async a -> m a
-		waitAsync a = liftIO (waitCatch a) >>= either (hsdevError . GhcError . displayException) return
-		toValue' :: ToJSON a => Either SomeException a -> Value
-		toValue' (Left (SomeException e)) = object ["fail" .= show e]
-		toValue' (Right s) = toJSON s
-runCommand Langs = toValue $ return $ Compat.languages
-runCommand Flags = toValue $ return ["-f" ++ prefix ++ f |
-	f <- Compat.flags,
-	prefix <- ["", "no-"]]
-runCommand (Link hold) = toValue $ commandLink >> when hold commandHold
-runCommand Exit = toValue serverExit
-
-targetFilters :: CommandMonad m => [TargetFilter] -> m (ModuleId -> Bool)
-targetFilters fs = do
-	fs_ <- mapM targetFilter fs
-	return $ foldr (liftM2 (&&)) (const True) fs_
-
-targetFilter :: CommandMonad m => TargetFilter -> m (ModuleId -> Bool)
-targetFilter f = case f of
-	TargetProject proj -> liftM inProject $ findProject proj
-	TargetFile file -> liftM inFile $ refineSourceFile file
-	TargetModule mname -> return $ inModule mname
-	TargetDepsOf dep -> liftM inDeps $ findDep dep
-	TargetPackageDb pdb -> return $ inPackageDb pdb
-	TargetCabal -> return $ inPackageDbStack userDb
-	TargetSandbox sbox -> liftM inPackageDbStack $ findSandbox sbox >>= sandboxPackageDbStack
-	TargetPackage pkg -> liftM inPackage $ refinePackage pkg
-	TargetSourced -> return byFile
-	TargetStandalone -> return standalone
-
--- Helper functions
-
--- | Canonicalize paths
-findPath :: (CommandMonad m, Paths a) => a -> m a
-findPath = paths findPath' where
-	findPath' :: CommandMonad m => FilePath -> m FilePath
-	findPath' f = do
-		r <- commandRoot
-		liftIO $ canonicalizePath (normalise $ if isRelative f then r </> f else f)
-
--- | Find sandbox by path
-findSandbox :: CommandMonad m => FilePath -> m Sandbox
-findSandbox fpath = do
-	fpath' <- findPath fpath
-	sbox <- liftIO $ S.findSandbox fpath'
-	maybe (hsdevError $ FileNotFound fpath') return sbox
-
--- | Get source file
-refineSourceFile :: CommandMonad m => FilePath -> m FilePath
-refineSourceFile fpath = do
-	fpath' <- findPath fpath
-	db' <- getDb
-	maybe (hsdevError (NotInspected $ FileModule fpath' Nothing)) return $ do
-		m' <- lookupFile fpath' db'
-		preview (moduleLocation . moduleFile) m'
-
--- | Get module by source
-refineSourceModule :: CommandMonad m => FilePath -> m Module
-refineSourceModule fpath = do
-	fpath' <- findPath fpath
-	db' <- getDb
-	maybe (hsdevError (NotInspected $ FileModule fpath' Nothing)) return $ lookupFile fpath' db'
-
--- | Set session by source
-setFileSourceSession :: CommandMonad m => [String] -> FilePath -> m Module
-setFileSourceSession opts fpath = do
-	m <- refineSourceModule fpath
-	inSessionGhc $ targetSession opts m
-	return m
-
--- | Ensure package exists
-refinePackage :: CommandMonad m => String -> m String
-refinePackage pkg = do
-	db' <- getDb
-	if pkg `elem` (allPackages db' ^.. each . packageName)
-		then return pkg
-		else hsdevError (PackageNotFound pkg)
-
--- | Get list of enumerated sandboxes
-getSandboxes :: CommandMonad m => [FilePath] -> m [Sandbox]
-getSandboxes = traverse (findPath >=> findSandbox)
-
--- | Find project by name or path
-findProject :: CommandMonad m => String -> m Project
-findProject proj = do
-	db' <- getDb
-	proj' <- liftM addCabal $ findPath proj
-	let
-		resultProj =
-			refineProject db' (project proj') <|>
-			find ((== proj) . view projectName) (databaseProjects db')
-	maybe (hsdevError $ ProjectNotFound proj) return resultProj
-	where
-		addCabal p
-			| takeExtension p == ".cabal" = p
-			| otherwise = p </> (takeBaseName p <.> "cabal")
-
--- | Find dependency: it may be source, project file or project name, also returns sandbox found
-findDep :: CommandMonad m => String -> m (Project, Maybe FilePath, PackageDbStack)
-findDep depName = do
-	depPath <- findPath depName
-	proj <- msum [
-		do
-			p <- liftIO (locateProject depPath)
-			p' <- maybe (hsdevError $ ProjectNotFound depName) return p
-			liftIO $ loadProject p',
-		findProject depName]
-	let
-		src
-			| takeExtension depPath == ".hs" = Just depPath
-			| otherwise = Nothing
-	pdbs <- searchPackageDbStack $ view projectPath proj
-	return (proj, src, pdbs)
-
--- FIXME: Doesn't work for file without project
--- | Check if project or source depends from this module
-inDeps :: (Project, Maybe FilePath, PackageDbStack) -> ModuleId -> Bool
-inDeps (proj, src, pdbs) = liftM2 (&&) (restrictPackageDbStack pdbs) deps' where
-	deps' = case src of
-		Nothing -> inDepsOfProject proj
-		Just src' -> inDepsOfFile proj src'
-
--- | Bring locals to top scope to search within them if 'locals' flag set
-localsDatabase :: Bool -> Database -> Database
-localsDatabase True = databaseLocals
-localsDatabase False = id
-
--- | Get actual DB state
-getDb :: SessionMonad m => m Database
-getDb = askSession sessionDatabase >>= liftIO . DB.readAsync
-
--- | Get DB with filtered sanxboxes for file
-getSDb :: SessionMonad m => FilePath -> m Database
-getSDb fpath = do
-	dbval <- getDb
-	pdbs <- searchPackageDbStack fpath
-	return $ filterDB (restrictPackageDbStack pdbs) (const True) dbval
-
--- | Run DB update action
-updateProcess :: ServerMonadBase m => Update.UpdateOptions -> [Update.UpdateM m ()] -> ClientM m ()
-updateProcess uopts acts = Update.runUpdate uopts $ mapM_ runAct acts where
-	runAct act = catch act onError
-	onError e = Log.sendLog Log.Error $ "{}" ~~ (e :: HsDevError)
-
--- | Ensure file is up to date
--- ensureUpToDate :: ServerMonadBase m => Update.UpdateOptions -> [FileSource] -> ClientM m ()
--- ensureUpToDate uopts fs = updateProcess uopts [Update.scanFileContents (view Update.updateGhcOpts uopts) f mcts | FileSource f mcts <- fs]
-
--- | Filter declarations with prefix and infix
-filterMatch :: Symbol a => SearchQuery -> [a] -> [a]
-filterMatch (SearchQuery q st) = filter match' where
-	match' m = case st of
-		SearchExact -> fromString q == symbolName m
-		SearchPrefix -> fromString q `T.isPrefixOf` symbolName m
-		SearchInfix -> fromString q `T.isInfixOf` symbolName m
-		SearchSuffix -> fromString q `T.isSuffixOf` symbolName m
-		SearchRegex -> unpack (symbolName m) =~ q
+{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
+
+module HsDev.Client.Commands (
+	runClient, runCommand
+	) where
+
+import Control.Applicative
+import Control.Concurrent.MVar
+import Control.Exception (displayException)
+import Control.Lens (view, preview, _Just, (^..), each)
+import Control.Monad
+import Control.Monad.Except
+import Control.Monad.Reader
+import qualified Control.Monad.State as State
+import Control.Monad.Catch (try, catch, bracket, SomeException(..))
+import Data.Aeson hiding (Result, Error)
+import Data.List
+import Data.Foldable (toList)
+import Data.Maybe
+import qualified Data.Map as M
+import Data.String (fromString)
+import Data.Text (pack, unpack)
+import qualified Data.Text as T (isInfixOf, isPrefixOf, isSuffixOf)
+import System.Directory
+import System.FilePath
+import qualified System.Log.Simple as Log
+import qualified System.Log.Simple.Base as Log
+import Text.Regex.PCRE ((=~))
+
+import qualified System.Directory.Watcher as W
+import qualified Data.Async as A
+import System.Directory.Paths
+import Text.Format
+import HsDev.Cache
+import HsDev.Commands
+import HsDev.Error
+import qualified HsDev.Database.Async as DB
+import HsDev.Server.Message as M
+import HsDev.Server.Types
+import HsDev.Sandbox hiding (findSandbox)
+import qualified HsDev.Sandbox as S (findSandbox)
+import HsDev.Symbols
+import HsDev.Symbols.Resolve (resolveOne, scopeModule, exportsModule)
+import HsDev.Symbols.Util
+import qualified HsDev.Tools.AutoFix as AutoFix
+import qualified HsDev.Tools.Cabal as Cabal
+import HsDev.Tools.Ghc.Session
+import qualified HsDev.Tools.Ghc.Compat as Compat
+import qualified HsDev.Tools.Ghc.Check as Check
+import qualified HsDev.Tools.Ghc.Types as Types
+import qualified HsDev.Tools.Hayoo as Hayoo
+import qualified HsDev.Tools.HLint as HLint
+import qualified HsDev.Tools.Types as Tools
+import HsDev.Util
+import HsDev.Watcher
+
+import qualified HsDev.Database.Update as Update
+
+runClient :: (ToJSON a, ServerMonadBase m) => CommandOptions -> ClientM m a -> ServerM m Result
+runClient copts = mapServerM toResult . runClientM where
+	toResult :: (ToJSON a, ServerMonadBase m) => ReaderT CommandOptions m a -> m Result
+	toResult act = liftM (either Error (Result . toJSON)) $ runReaderT (try act) copts
+	mapServerM :: (m a -> n b) -> ServerM m a -> ServerM n b
+	mapServerM f = ServerM . mapReaderT f . runServerM
+
+toValue :: (ToJSON a, Monad m) => m a -> m Value
+toValue = liftM toJSON
+
+runCommand :: ServerMonadBase m => Command -> ClientM m Value
+runCommand Ping = toValue $ return $ object ["message" .= ("pong" :: String)]
+runCommand (Listen (Just l)) = case Log.level (pack l) of
+	Nothing -> hsdevError $ OtherError $ "invalid log level: {}" ~~ l
+	Just lev -> bracket (serverSetLogLevel lev) serverSetLogLevel $ \_ -> runCommand (Listen Nothing)
+runCommand (Listen Nothing) = toValue $ do
+	serverListen >>= mapM_ (\msg -> commandNotify (Notification $ object ["message" .= msg]))
+runCommand (SetLogLevel l) = case Log.level (pack l) of
+	Nothing -> hsdevError $ OtherError $ "invalid log level: {}" ~~ l
+	Just lev -> toValue $ do
+		lev' <- serverSetLogLevel lev
+		Log.sendLog Log.Debug $ "log level changed from '{}' to '{}'" ~~ show lev' ~~ show lev
+		Log.sendLog Log.Info $ "log level updated to: {}" ~~ show lev
+runCommand (AddData cts) = toValue $ mapM_ updateData cts where
+	updateData (AddedDatabase db) = toValue $ serverUpdateDB db
+	updateData (AddedModule m) = toValue $ serverUpdateDB $ fromModule m
+	updateData (AddedProject p) = toValue $ serverUpdateDB $ fromProject p
+runCommand (Scan projs cabal sboxes fs paths' ghcs' docs' infer') = toValue $ do
+	sboxes' <- getSandboxes sboxes
+	updateProcess (Update.UpdateOptions [] ghcs' docs' infer') $ concat [
+		map (\(FileSource f mcts) -> Update.scanFileContents ghcs' f mcts) fs,
+		map (Update.scanProject ghcs') projs,
+		map (Update.scanDirectory ghcs') paths',
+		[Update.scanCabal ghcs' | cabal],
+		map (Update.scanSandbox ghcs') sboxes']
+runCommand (RefineDocs projs fs ms) = toValue $ do
+	projects <- traverse findProject projs
+	dbval <- getDb
+	let
+		filters = anyOf $ concat [
+			map inProject projects,
+			map inFile fs,
+			map inModule ms]
+		mods = selectModules (filters . view moduleId) dbval
+	updateProcess (Update.UpdateOptions [] [] False False) [Update.scanDocs $ map (getInspected dbval) mods]
+runCommand (InferTypes projs fs ms) = toValue $ do
+	projects <- traverse findProject projs
+	dbval <- getDb
+	let
+		filters = anyOf $ concat [
+			map inProject projects,
+			map inFile fs,
+			map inModule ms]
+		mods = selectModules (filters . view moduleId) dbval
+	updateProcess (Update.UpdateOptions [] [] False False) [Update.inferModTypes $ map (getInspected dbval) mods]
+runCommand (Remove projs cabal sboxes files) = toValue $ do
+	db <- askSession sessionDatabase
+	dbval <- getDb
+	w <- askSession sessionWatcher
+	projects <- traverse findProject projs
+	sboxes' <- getSandboxes sboxes
+	forM_ projects $ \proj -> do
+		DB.clear db (return $ projectDB proj dbval)
+		liftIO $ unwatchProject w proj
+	dbPDbs <- mapM restorePackageDbStack $ databasePackageDbs dbval
+	flip State.evalStateT dbPDbs $ do
+		when cabal $ removePackageDbStack userDb
+		forM_ sboxes' $ \sbox -> do
+			pdbs <- lift $ sandboxPackageDbStack sbox
+			removePackageDbStack pdbs
+	forM_ files $ \file -> do
+		DB.clear db (return $ filterDB (inFile file) (const False) dbval)
+		let
+			mloc = fmap (view moduleLocation) $ lookupFile file dbval
+		maybe (return ()) (liftIO . unwatchModule w) mloc
+	where
+		-- We can safely remove package-db from db iff doesn't used by some of other package-dbs
+		-- For example, we can't remove global-db if there are any other package-dbs, because all of them uses global-db
+		-- We also can't remove stack snapshot package-db if there are some local package-db not yet removed
+		canRemove pdbs = do
+			from <- State.get
+			return $ null $ filter (pdbs `isSubStack`) $ delete pdbs from
+		-- Remove top of package-db stack if possible
+		removePackageDb pdbs = do
+			db <- lift $ askSession sessionDatabase
+			dbval <- lift getDb
+			w <- lift $ askSession sessionWatcher
+			can <- canRemove pdbs
+			when can $ do
+				State.modify (delete pdbs)
+				DB.clear db (return $ packageDbDB (topPackageDb pdbs) dbval)
+				liftIO $ unwatchPackageDb w $ topPackageDb pdbs
+		-- Remove package-db stack when possible
+		removePackageDbStack = mapM_ removePackageDb . packageDbStacks
+runCommand RemoveAll = toValue $ do
+	db <- askSession sessionDatabase
+	liftIO $ A.modifyAsync db A.Clear
+	w <- askSession sessionWatcher
+	wdirs <- liftIO $ readMVar (W.watcherDirs w)
+	liftIO $ forM_ (M.toList wdirs) $ \(dir, (isTree, _)) -> (if isTree then W.unwatchTree else W.unwatchDir) w dir
+runCommand (InfoModules fs) = toValue $ do
+	dbval <- getDb
+	filter' <- targetFilters fs
+	return $ map (view moduleId) $ newestPackage $ selectModules (filter' . view moduleId) dbval
+runCommand InfoPackages = toValue $ (ordNub . sort . 	mapMaybe (preview (moduleLocation . modulePackage . _Just)) . allModules) <$> getDb
+runCommand InfoProjects = toValue $ (toList . databaseProjects) <$> getDb
+runCommand InfoSandboxes = toValue $ databasePackageDbs <$> getDb
+runCommand (InfoSymbol sq fs locals') = toValue $ do
+	dbval <- liftM (localsDatabase locals') $ getDb
+	filter' <- targetFilters fs
+	return $ newestPackage $ filterMatch sq $
+		concatMap moduleModuleDeclarations $ filter (filter' . view moduleId) $ allModules dbval
+runCommand (InfoModule sq fs) = toValue $ do
+	dbval <- getDb
+	filter' <- targetFilters fs
+	return $ newestPackage $ filterMatch sq $ filter (filter' . view moduleId) $ allModules dbval
+runCommand (InfoResolve fpath exports) = toValue $ do
+	dbval <- getSDb fpath
+	let
+		getScope
+			| exports = exportsModule
+			| otherwise = scopeModule
+	m <- refineSourceModule fpath
+	return $ getScope $ resolveOne dbval m
+runCommand (InfoProject (Left projName)) = toValue $ findProject projName
+runCommand (InfoProject (Right projPath)) = toValue $ liftIO $ searchProject projPath
+runCommand (InfoSandbox sandbox') = toValue $ liftIO $ searchSandbox sandbox'
+runCommand (Lookup nm fpath) = toValue $ do
+	dbval <- getSDb fpath
+	liftIO $ hsdevLift $ lookupSymbol dbval fpath nm
+runCommand (Whois nm fpath) = toValue $ do
+	dbval <- getSDb fpath
+	liftIO $ hsdevLift $ whois dbval fpath nm
+runCommand (ResolveScopeModules sq fpath) = toValue $ do
+	dbval <- getSDb fpath
+	liftM (filterMatch sq . map (view moduleId)) $ liftIO $ hsdevLift $ scopeModules dbval fpath
+runCommand (ResolveScope sq global fpath) = toValue $ do
+	dbval <- getSDb fpath
+	liftM (filterMatch sq) $ liftIO $ hsdevLift $ scope dbval fpath global
+runCommand (Complete input wide fpath) = toValue $ do
+	dbval <- getSDb fpath
+	liftIO $ hsdevLift $ completions dbval fpath input wide
+runCommand (Hayoo hq p ps) = toValue $ liftM concat $ forM [p .. p + pred ps] $ \i -> liftM
+	(mapMaybe Hayoo.hayooAsDeclaration . Hayoo.resultResult) $
+	liftIO $ hsdevLift $ Hayoo.hayoo hq (Just i)
+runCommand (CabalList packages) = toValue $ liftIO $ hsdevLift $ Cabal.cabalList packages
+runCommand (Lint fs) = toValue $ do
+	liftIO $ hsdevLift $ liftM concat $ mapM (\(FileSource f c) -> HLint.hlint f c) fs
+runCommand (Check fs ghcs') = toValue $ Log.scope "check" $ do
+	-- ensureUpToDate (Update.UpdateOptions [] ghcs' False False) fs
+	let
+		checkSome file fn = Log.scope "checkSome" $ do
+			m <- setFileSourceSession ghcs' file
+			inSessionGhc $ fn m
+	liftM concat $ mapM (\(FileSource f c) -> checkSome f (\m -> Check.check ghcs' m c)) fs
+runCommand (CheckLint fs ghcs') = toValue $ do
+	-- ensureUpToDate (Update.UpdateOptions [] ghcs' False False) fs
+	let
+		checkSome file fn = Log.scope "checkSome" $ do
+			m <- setFileSourceSession ghcs' file
+			inSessionGhc $ fn m
+	checkMsgs <- liftM concat $ mapM (\(FileSource f c) -> checkSome f (\m -> Check.check ghcs' m c)) fs
+	lintMsgs <- liftIO $ hsdevLift $ liftM concat $ mapM (\(FileSource f c) -> HLint.hlint f c) fs
+	return $ checkMsgs ++ lintMsgs
+runCommand (Types fs ghcs') = toValue $ do
+	-- ensureUpToDate (Update.UpdateOptions [] ghcs' False False) fs
+	liftM concat $ forM fs $ \(FileSource file msrc) -> do
+		m <- setFileSourceSession ghcs' file
+		inSessionGhc $ Types.fileTypes ghcs' m msrc
+runCommand (AutoFix (AutoFixShow ns)) = toValue $ return $ AutoFix.corrections ns
+runCommand (AutoFix (AutoFixFix ns rest isPure)) = toValue $ do
+	files <- liftM (ordNub . sort) $ mapM findPath $ mapMaybe (preview $ Tools.noteSource . moduleFile) ns
+	let
+		doFix :: FilePath -> Maybe String -> ([Tools.Note AutoFix.Correction], Maybe String)
+		doFix file mcts = AutoFix.autoFix fCorrs (fUpCorrs, mcts) where
+			findCorrs :: FilePath -> [Tools.Note AutoFix.Correction] -> [Tools.Note AutoFix.Correction]
+			findCorrs f = filter ((== Just f) . preview (Tools.noteSource . moduleFile))
+			fCorrs = findCorrs file ns
+			fUpCorrs = findCorrs file rest
+		runFix file
+			| isPure = return $ fst $ doFix file Nothing
+			| otherwise = do
+				(corrs', Just cts') <- liftM (doFix file) $ liftIO $ Just <$> readFileUtf8 file
+				liftIO $ writeFileUtf8 file cts'
+				return corrs'
+	liftM concat $ mapM runFix files
+runCommand (GhcEval exprs mfile) = toValue $ do
+	-- ensureUpToDate (Update.UpdateOptions [] [] False False) (maybeToList mfile)
+	ghcw <- askSession sessionGhc
+	case mfile of
+		Nothing -> inSessionGhc ghciSession
+		Just (FileSource f mcts) -> do
+			m <- setFileSourceSession [] f
+			inSessionGhc $ interpretModule m mcts
+	async' <- liftIO $ pushTask ghcw $ do
+		mapM (try . evaluate) exprs
+	res <- waitAsync async'
+	return $ map toValue' res
+	where
+		waitAsync :: CommandMonad m => Async a -> m a
+		waitAsync a = liftIO (waitCatch a) >>= either (hsdevError . GhcError . displayException) return
+		toValue' :: ToJSON a => Either SomeException a -> Value
+		toValue' (Left (SomeException e)) = object ["fail" .= show e]
+		toValue' (Right s) = toJSON s
+runCommand Langs = toValue $ return $ Compat.languages
+runCommand Flags = toValue $ return ["-f" ++ prefix ++ f |
+	f <- Compat.flags,
+	prefix <- ["", "no-"]]
+runCommand (Link hold) = toValue $ commandLink >> when hold commandHold
+runCommand Exit = toValue serverExit
+
+targetFilters :: CommandMonad m => [TargetFilter] -> m (ModuleId -> Bool)
+targetFilters fs = do
+	fs_ <- mapM targetFilter fs
+	return $ foldr (liftM2 (&&)) (const True) fs_
+
+targetFilter :: CommandMonad m => TargetFilter -> m (ModuleId -> Bool)
+targetFilter f = case f of
+	TargetProject proj -> liftM inProject $ findProject proj
+	TargetFile file -> liftM inFile $ refineSourceFile file
+	TargetModule mname -> return $ inModule mname
+	TargetDepsOf dep -> liftM inDeps $ findDep dep
+	TargetPackageDb pdb -> return $ inPackageDb pdb
+	TargetCabal -> return $ inPackageDbStack userDb
+	TargetSandbox sbox -> liftM inPackageDbStack $ findSandbox sbox >>= sandboxPackageDbStack
+	TargetPackage pkg -> liftM inPackage $ refinePackage pkg
+	TargetSourced -> return byFile
+	TargetStandalone -> return standalone
+
+-- Helper functions
+
+-- | Canonicalize paths
+findPath :: (CommandMonad m, Paths a) => a -> m a
+findPath = paths findPath' where
+	findPath' :: CommandMonad m => FilePath -> m FilePath
+	findPath' f = do
+		r <- commandRoot
+		liftIO $ canonicalizePath (normalise $ if isRelative f then r </> f else f)
+
+-- | Find sandbox by path
+findSandbox :: CommandMonad m => FilePath -> m Sandbox
+findSandbox fpath = do
+	fpath' <- findPath fpath
+	sbox <- liftIO $ S.findSandbox fpath'
+	maybe (hsdevError $ FileNotFound fpath') return sbox
+
+-- | Get source file
+refineSourceFile :: CommandMonad m => FilePath -> m FilePath
+refineSourceFile fpath = do
+	fpath' <- findPath fpath
+	db' <- getDb
+	maybe (hsdevError (NotInspected $ FileModule fpath' Nothing)) return $ do
+		m' <- lookupFile fpath' db'
+		preview (moduleLocation . moduleFile) m'
+
+-- | Get module by source
+refineSourceModule :: CommandMonad m => FilePath -> m Module
+refineSourceModule fpath = do
+	fpath' <- findPath fpath
+	db' <- getDb
+	maybe (hsdevError (NotInspected $ FileModule fpath' Nothing)) return $ lookupFile fpath' db'
+
+-- | Set session by source
+setFileSourceSession :: CommandMonad m => [String] -> FilePath -> m Module
+setFileSourceSession opts fpath = do
+	m <- refineSourceModule fpath
+	inSessionGhc $ targetSession opts m
+	return m
+
+-- | Ensure package exists
+refinePackage :: CommandMonad m => String -> m String
+refinePackage pkg = do
+	db' <- getDb
+	if pkg `elem` (allPackages db' ^.. each . packageName)
+		then return pkg
+		else hsdevError (PackageNotFound pkg)
+
+-- | Get list of enumerated sandboxes
+getSandboxes :: CommandMonad m => [FilePath] -> m [Sandbox]
+getSandboxes = traverse (findPath >=> findSandbox)
+
+-- | Find project by name or path
+findProject :: CommandMonad m => String -> m Project
+findProject proj = do
+	db' <- getDb
+	proj' <- liftM addCabal $ findPath proj
+	let
+		resultProj =
+			refineProject db' (project proj') <|>
+			find ((== proj) . view projectName) (databaseProjects db')
+	maybe (hsdevError $ ProjectNotFound proj) return resultProj
+	where
+		addCabal p
+			| takeExtension p == ".cabal" = p
+			| otherwise = p </> (takeBaseName p <.> "cabal")
+
+-- | Find dependency: it may be source, project file or project name, also returns sandbox found
+findDep :: CommandMonad m => String -> m (Project, Maybe FilePath, PackageDbStack)
+findDep depName = do
+	depPath <- findPath depName
+	proj <- msum [
+		do
+			p <- liftIO (locateProject depPath)
+			p' <- maybe (hsdevError $ ProjectNotFound depName) return p
+			liftIO $ loadProject p',
+		findProject depName]
+	let
+		src
+			| takeExtension depPath == ".hs" = Just depPath
+			| otherwise = Nothing
+	pdbs <- searchPackageDbStack $ view projectPath proj
+	return (proj, src, pdbs)
+
+-- FIXME: Doesn't work for file without project
+-- | Check if project or source depends from this module
+inDeps :: (Project, Maybe FilePath, PackageDbStack) -> ModuleId -> Bool
+inDeps (proj, src, pdbs) = liftM2 (&&) (restrictPackageDbStack pdbs) deps' where
+	deps' = case src of
+		Nothing -> inDepsOfProject proj
+		Just src' -> inDepsOfFile proj src'
+
+-- | Bring locals to top scope to search within them if 'locals' flag set
+localsDatabase :: Bool -> Database -> Database
+localsDatabase True = databaseLocals
+localsDatabase False = id
+
+-- | Get actual DB state
+getDb :: SessionMonad m => m Database
+getDb = askSession sessionDatabase >>= liftIO . DB.readAsync
+
+-- | Get DB with filtered sanxboxes for file
+getSDb :: SessionMonad m => FilePath -> m Database
+getSDb fpath = do
+	dbval <- getDb
+	pdbs <- searchPackageDbStack fpath
+	return $ filterDB (restrictPackageDbStack pdbs) (const True) dbval
+
+-- | Run DB update action
+updateProcess :: ServerMonadBase m => Update.UpdateOptions -> [Update.UpdateM m ()] -> ClientM m ()
+updateProcess uopts acts = Update.runUpdate uopts $ mapM_ runAct acts where
+	runAct act = catch act onError
+	onError e = Log.sendLog Log.Error $ "{}" ~~ (e :: HsDevError)
+
+-- | Ensure file is up to date
+-- ensureUpToDate :: ServerMonadBase m => Update.UpdateOptions -> [FileSource] -> ClientM m ()
+-- ensureUpToDate uopts fs = updateProcess uopts [Update.scanFileContents (view Update.updateGhcOpts uopts) f mcts | FileSource f mcts <- fs]
+
+-- | Filter declarations with prefix and infix
+filterMatch :: Symbol a => SearchQuery -> [a] -> [a]
+filterMatch (SearchQuery q st) = filter match' where
+	match' m = case st of
+		SearchExact -> fromString q == symbolName m
+		SearchPrefix -> fromString q `T.isPrefixOf` symbolName m
+		SearchInfix -> fromString q `T.isInfixOf` symbolName m
+		SearchSuffix -> fromString q `T.isSuffixOf` symbolName m
+		SearchRegex -> unpack (symbolName m) =~ q
diff --git a/src/HsDev/Commands.hs b/src/HsDev/Commands.hs
--- a/src/HsDev/Commands.hs
+++ b/src/HsDev/Commands.hs
@@ -1,193 +1,193 @@
-{-# LANGUAGE RankNTypes, FlexibleContexts #-}
-
-module HsDev.Commands (
-	-- * Commands
-	findDeclaration, findModule,
-	fileModule,
-	lookupSymbol,
-	whois,
-	scopeModules, scope,
-	completions,
-	moduleCompletions,
-
-	-- * Filters
-	checkModule, checkDeclaration, restrictPackageDb, restrictPackageDbStack, visibleFrom,
-	splitIdentifier,
-
-	-- * Helpers
-	fileCtx, fileCtxMaybe,
-
-	-- * Reexports
-	module HsDev.Database,
-	module HsDev.Symbols.Types,
-	module Control.Monad.Except
-	) where
-
-import Control.Applicative
-import Control.Lens (view, set, each, toListOf)
-import Control.Monad.Except
-import Data.List (delete)
-import Data.Maybe
-import Data.String (fromString)
-import qualified Data.Text as T (isPrefixOf, split, unpack)
-import System.Directory (canonicalizePath)
-
-import System.Directory.Paths
-
-import HsDev.Database
-import HsDev.Project
-import HsDev.Symbols
-import HsDev.Symbols.Resolve
-import HsDev.Symbols.Types
-import HsDev.Symbols.Util
-import HsDev.Tools.Base (matchRx, at_)
-import HsDev.Util (liftE, ordNub)
-
--- | Find declaration by name
-findDeclaration :: Database -> String -> ExceptT String IO [ModuleDeclaration]
-findDeclaration db ident = return $ selectDeclarations checkName db where
-	checkName :: ModuleDeclaration -> Bool
-	checkName m =
-		(view (moduleDeclaration . declarationName) m == fromString iname) &&
-		(maybe True ((view (declarationModuleId . moduleIdName) m ==) . fromString) qname)
-
-	(qname, iname) = splitIdentifier ident
-
--- | Find module by name
-findModule :: Database -> String -> ExceptT String IO [Module]
-findModule db mname = return $ selectModules ((== fromString mname) . view moduleName) db
-
--- | Find module in file
-fileModule :: Database -> FilePath -> ExceptT String IO Module
-fileModule db src = do
-	src' <- liftE $ canonicalizePath src
-	maybe (throwError $ "File '" ++ src' ++ "' not found") return $ lookupFile src' db
-
--- | Find project of module
-getProject :: Database -> Project -> ExceptT String IO Project
-getProject db p = do
-	p' <- liftE $ canonicalize p
-	maybe (throwError $ "Project " ++ view projectCabal p' ++ " not found") return $
-		refineProject db p'
-
--- | Lookup visible within project/cabal symbol
-lookupSymbol :: Database -> FilePath -> String -> ExceptT String IO [ModuleDeclaration]
-lookupSymbol db file ident = do
-	(_, mthis, mproj) <- fileCtx db file
-	liftM
-		(filter $ checkModule $ allOf [
-			visibleFrom mproj mthis,
-			maybe (const True) inModule qname])
-		(newestPackage <$> findDeclaration db iname)
-	where
-		(qname, iname) = splitIdentifier ident
-
--- | Whois symbol in scope
-whois :: Database -> FilePath -> String -> ExceptT String IO [Declaration]
-whois db file ident = do
-	(_, mthis, mproj) <- fileCtx db file
-	return $
-		newestPackage $ filter checkDecl $
-		view moduleDeclarations $ scopeModule $ resolveOne (fileDeps file mproj db) $
-		moduleLocals mthis
-	where
-		(qname, iname) = splitIdentifier ident
-		checkDecl d = fmap fromString qname `elem` scopes d && view declarationName d == fromString iname
-
--- | Accessible modules
-scopeModules :: Database -> FilePath -> ExceptT String IO [Module]
-scopeModules db file = do
-	(file', mthis, mproj) <- fileCtxMaybe db file
-	newestPackage <$> case mproj of
-		Nothing -> return $ maybe id (:) mthis $ selectModules (installed . view moduleId) db
-		Just proj -> let deps' = deps file' proj in
-			return $ concatMap (\p -> selectModules (p . view moduleId) db) [
-				inProject proj,
-				\m -> any (`inPackage` m) deps']
-	where
-		deps f p = delete (view projectName p) $ concatMap (view infoDepends) $ fileTargets p f
-
--- | Symbols in scope
-scope :: Database -> FilePath -> Bool -> ExceptT String IO [Declaration]
-scope db file False = do
-	(_, mthis, mproj) <- fileCtx db file
-	return $ view moduleDeclarations $ scopeModule $ resolveOne (fileDeps file mproj db) mthis
-scope db file True = concatMap (view moduleDeclarations) <$> scopeModules db file
-
--- | Completions
-completions :: Database -> FilePath -> String -> Bool -> ExceptT String IO [Declaration]
-completions db file prefix wide = do
-	(_, mthis, mproj) <- fileCtx db file
-	return $
-		toListOf (each . minimalDecl) $ newestPackage $ filter checkDecl $
-		view moduleDeclarations $ scopeModule $ resolveOne (fileDeps file mproj db) $
-		dropImportLists mthis
-	where
-		(qname, iname) = splitIdentifier prefix
-		checkDecl d = fmap fromString qname `elem` scopes d && fromString iname `T.isPrefixOf` view declarationName d
-		dropImportLists m
-			| wide = set (moduleImports . each . importList) Nothing m
-			| otherwise = m
-
--- | Module completions
-moduleCompletions :: Database -> [Module] -> String -> ExceptT String IO [String]
-moduleCompletions _ ms prefix = return $ map T.unpack $ ordNub $ completions' $ map (view moduleName) ms where
-	completions' = mapMaybe getNext where
-		getNext m
-			| fromString prefix `T.isPrefixOf` m = listToMaybe $ map snd $ dropWhile (uncurry (==)) $ zip (T.split (== '.') $ fromString prefix) (T.split (== '.') m)
-			| otherwise = Nothing
-
--- | Check module
-checkModule :: (ModuleId -> Bool) -> (ModuleDeclaration -> Bool)
-checkModule = (. view declarationModuleId)
-
--- | Check declaration
-checkDeclaration :: (Declaration -> Bool) -> (ModuleDeclaration -> Bool)
-checkDeclaration = (. view moduleDeclaration)
-
--- | Allow only selected cabal sandbox
-restrictPackageDb :: PackageDb -> ModuleId -> Bool
-restrictPackageDb pdb m = inPackageDb pdb m || not (installed m)
-
--- | Allow only selected cabal sandboxes
-restrictPackageDbStack :: PackageDbStack -> ModuleId -> Bool
-restrictPackageDbStack pdbs m = any (`inPackageDb` m) (packageDbs pdbs) || not (installed m)
-
--- | Check whether module is visible from source file
-visibleFrom :: Maybe Project -> Module -> ModuleId -> Bool
-visibleFrom (Just p) this m = visible p (view moduleId this) m
-visibleFrom Nothing this m = view moduleId this == m || installed m
-
--- | Split identifier into module name and identifier itself
-splitIdentifier :: String -> (Maybe String, String)
-splitIdentifier name = fromMaybe (Nothing, name) $ do
-	groups <- matchRx "(([A-Z][\\w']*\\.)*)(.*)" name
-	return (dropDot <$> groups 1, groups `at_` 3)
-	where
-		dropDot :: String -> String
-		dropDot "" = ""
-		dropDot s = init s
-
--- | Get context file and project
-fileCtx :: Database -> FilePath -> ExceptT String IO (FilePath, Module, Maybe Project)
-fileCtx db file = do
-	file' <- liftE $ canonicalizePath file
-	mthis <- fileModule db file'
-	mproj <- traverse (getProject db) $ projectOf $ view moduleId mthis
-	return (file', mthis, mproj)
-
--- | Try get context file
-fileCtxMaybe :: Database -> FilePath -> ExceptT String IO (FilePath, Maybe Module, Maybe Project)
-fileCtxMaybe db file = ((\(f, m, p) -> (f, Just m, p)) <$> fileCtx db file) <|> onlyProj where
-	onlyProj = do
-		file' <- liftE $ canonicalizePath file
-		mproj <- liftE $ locateProject file'
-		mproj' <- traverse (getProject db) mproj
-		return (file', Nothing, mproj')
-
--- | Restrict only modules file depends on
-fileDeps :: FilePath -> Maybe Project -> Database -> Database
-fileDeps file mproj = filterDB fileDeps' (const True) where
-	fileDeps' = liftM2 (||)
-		(maybe (const True) inProject mproj)
-		(\m -> any (`inDepsOfTarget` m) (fromMaybe [] $ fileTargets <$> mproj <*> pure file))
+{-# LANGUAGE RankNTypes, FlexibleContexts #-}
+
+module HsDev.Commands (
+	-- * Commands
+	findDeclaration, findModule,
+	fileModule,
+	lookupSymbol,
+	whois,
+	scopeModules, scope,
+	completions,
+	moduleCompletions,
+
+	-- * Filters
+	checkModule, checkDeclaration, restrictPackageDb, restrictPackageDbStack, visibleFrom,
+	splitIdentifier,
+
+	-- * Helpers
+	fileCtx, fileCtxMaybe,
+
+	-- * Reexports
+	module HsDev.Database,
+	module HsDev.Symbols.Types,
+	module Control.Monad.Except
+	) where
+
+import Control.Applicative
+import Control.Lens (view, set, each, toListOf)
+import Control.Monad.Except
+import Data.List (delete)
+import Data.Maybe
+import Data.String (fromString)
+import qualified Data.Text as T (isPrefixOf, split, unpack)
+import System.Directory (canonicalizePath)
+
+import System.Directory.Paths
+
+import HsDev.Database
+import HsDev.Project
+import HsDev.Symbols
+import HsDev.Symbols.Resolve
+import HsDev.Symbols.Types
+import HsDev.Symbols.Util
+import HsDev.Tools.Base (matchRx, at_)
+import HsDev.Util (liftE, ordNub)
+
+-- | Find declaration by name
+findDeclaration :: Database -> String -> ExceptT String IO [ModuleDeclaration]
+findDeclaration db ident = return $ selectDeclarations checkName db where
+	checkName :: ModuleDeclaration -> Bool
+	checkName m =
+		(view (moduleDeclaration . declarationName) m == fromString iname) &&
+		(maybe True ((view (declarationModuleId . moduleIdName) m ==) . fromString) qname)
+
+	(qname, iname) = splitIdentifier ident
+
+-- | Find module by name
+findModule :: Database -> String -> ExceptT String IO [Module]
+findModule db mname = return $ selectModules ((== fromString mname) . view moduleName) db
+
+-- | Find module in file
+fileModule :: Database -> FilePath -> ExceptT String IO Module
+fileModule db src = do
+	src' <- liftE $ canonicalizePath src
+	maybe (throwError $ "File '" ++ src' ++ "' not found") return $ lookupFile src' db
+
+-- | Find project of module
+getProject :: Database -> Project -> ExceptT String IO Project
+getProject db p = do
+	p' <- liftE $ canonicalize p
+	maybe (throwError $ "Project " ++ view projectCabal p' ++ " not found") return $
+		refineProject db p'
+
+-- | Lookup visible within project/cabal symbol
+lookupSymbol :: Database -> FilePath -> String -> ExceptT String IO [ModuleDeclaration]
+lookupSymbol db file ident = do
+	(_, mthis, mproj) <- fileCtx db file
+	liftM
+		(filter $ checkModule $ allOf [
+			visibleFrom mproj mthis,
+			maybe (const True) inModule qname])
+		(newestPackage <$> findDeclaration db iname)
+	where
+		(qname, iname) = splitIdentifier ident
+
+-- | Whois symbol in scope
+whois :: Database -> FilePath -> String -> ExceptT String IO [Declaration]
+whois db file ident = do
+	(_, mthis, mproj) <- fileCtx db file
+	return $
+		newestPackage $ filter checkDecl $
+		view moduleDeclarations $ scopeModule $ resolveOne (fileDeps file mproj db) $
+		moduleLocals mthis
+	where
+		(qname, iname) = splitIdentifier ident
+		checkDecl d = fmap fromString qname `elem` scopes d && view declarationName d == fromString iname
+
+-- | Accessible modules
+scopeModules :: Database -> FilePath -> ExceptT String IO [Module]
+scopeModules db file = do
+	(file', mthis, mproj) <- fileCtxMaybe db file
+	newestPackage <$> case mproj of
+		Nothing -> return $ maybe id (:) mthis $ selectModules (installed . view moduleId) db
+		Just proj -> let deps' = deps file' proj in
+			return $ concatMap (\p -> selectModules (p . view moduleId) db) [
+				inProject proj,
+				\m -> any (`inPackage` m) deps']
+	where
+		deps f p = delete (view projectName p) $ concatMap (view infoDepends) $ fileTargets p f
+
+-- | Symbols in scope
+scope :: Database -> FilePath -> Bool -> ExceptT String IO [Declaration]
+scope db file False = do
+	(_, mthis, mproj) <- fileCtx db file
+	return $ view moduleDeclarations $ scopeModule $ resolveOne (fileDeps file mproj db) mthis
+scope db file True = concatMap (view moduleDeclarations) <$> scopeModules db file
+
+-- | Completions
+completions :: Database -> FilePath -> String -> Bool -> ExceptT String IO [Declaration]
+completions db file prefix wide = do
+	(_, mthis, mproj) <- fileCtx db file
+	return $
+		toListOf (each . minimalDecl) $ newestPackage $ filter checkDecl $
+		view moduleDeclarations $ scopeModule $ resolveOne (fileDeps file mproj db) $
+		dropImportLists mthis
+	where
+		(qname, iname) = splitIdentifier prefix
+		checkDecl d = fmap fromString qname `elem` scopes d && fromString iname `T.isPrefixOf` view declarationName d
+		dropImportLists m
+			| wide = set (moduleImports . each . importList) Nothing m
+			| otherwise = m
+
+-- | Module completions
+moduleCompletions :: Database -> [Module] -> String -> ExceptT String IO [String]
+moduleCompletions _ ms prefix = return $ map T.unpack $ ordNub $ completions' $ map (view moduleName) ms where
+	completions' = mapMaybe getNext where
+		getNext m
+			| fromString prefix `T.isPrefixOf` m = listToMaybe $ map snd $ dropWhile (uncurry (==)) $ zip (T.split (== '.') $ fromString prefix) (T.split (== '.') m)
+			| otherwise = Nothing
+
+-- | Check module
+checkModule :: (ModuleId -> Bool) -> (ModuleDeclaration -> Bool)
+checkModule = (. view declarationModuleId)
+
+-- | Check declaration
+checkDeclaration :: (Declaration -> Bool) -> (ModuleDeclaration -> Bool)
+checkDeclaration = (. view moduleDeclaration)
+
+-- | Allow only selected cabal sandbox
+restrictPackageDb :: PackageDb -> ModuleId -> Bool
+restrictPackageDb pdb m = inPackageDb pdb m || not (installed m)
+
+-- | Allow only selected cabal sandboxes
+restrictPackageDbStack :: PackageDbStack -> ModuleId -> Bool
+restrictPackageDbStack pdbs m = any (`inPackageDb` m) (packageDbs pdbs) || not (installed m)
+
+-- | Check whether module is visible from source file
+visibleFrom :: Maybe Project -> Module -> ModuleId -> Bool
+visibleFrom (Just p) this m = visible p (view moduleId this) m
+visibleFrom Nothing this m = view moduleId this == m || installed m
+
+-- | Split identifier into module name and identifier itself
+splitIdentifier :: String -> (Maybe String, String)
+splitIdentifier name = fromMaybe (Nothing, name) $ do
+	groups <- matchRx "(([A-Z][\\w']*\\.)*)(.*)" name
+	return (dropDot <$> groups 1, groups `at_` 3)
+	where
+		dropDot :: String -> String
+		dropDot "" = ""
+		dropDot s = init s
+
+-- | Get context file and project
+fileCtx :: Database -> FilePath -> ExceptT String IO (FilePath, Module, Maybe Project)
+fileCtx db file = do
+	file' <- liftE $ canonicalizePath file
+	mthis <- fileModule db file'
+	mproj <- traverse (getProject db) $ projectOf $ view moduleId mthis
+	return (file', mthis, mproj)
+
+-- | Try get context file
+fileCtxMaybe :: Database -> FilePath -> ExceptT String IO (FilePath, Maybe Module, Maybe Project)
+fileCtxMaybe db file = ((\(f, m, p) -> (f, Just m, p)) <$> fileCtx db file) <|> onlyProj where
+	onlyProj = do
+		file' <- liftE $ canonicalizePath file
+		mproj <- liftE $ locateProject file'
+		mproj' <- traverse (getProject db) mproj
+		return (file', Nothing, mproj')
+
+-- | Restrict only modules file depends on
+fileDeps :: FilePath -> Maybe Project -> Database -> Database
+fileDeps file mproj = filterDB fileDeps' (const True) where
+	fileDeps' = liftM2 (||)
+		(maybe (const True) inProject mproj)
+		(\m -> any (`inDepsOfTarget` m) (fromMaybe [] $ fileTargets <$> mproj <*> pure file))
diff --git a/src/HsDev/Database.hs b/src/HsDev/Database.hs
--- a/src/HsDev/Database.hs
+++ b/src/HsDev/Database.hs
@@ -1,253 +1,253 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module HsDev.Database (
-	Database(..),
-	databaseIntersection, nullDatabase, databaseLocals, databasePackageDbs, allModules, allDeclarations, allPackages,
-	fromModule, fromProject,
-	filterDB,
-	projectDB, packageDbDB, packageDbStackDB, standaloneDB,
-	selectModules, selectDeclarations, lookupModule, lookupInspected, lookupFile, refineProject,
-	getInspected,
-
-	append, remove,
-
-	Structured(..),
-	structured, structurize, merge,
-
-	Map
-	) where
-
-import Control.Lens (set, view, preview, _Just, _Right, each, (^..))
-import Control.Monad (msum, join)
-import Control.DeepSeq (NFData(..))
-import Data.Aeson
-import Data.Either (rights)
-import Data.Foldable (find)
-import Data.Function (on)
-import Data.Group (Group(..))
-import Data.Map (Map)
-import Data.Maybe
-import qualified Data.Map as M
-
-import HsDev.Symbols
-import HsDev.Symbols.Util
-import HsDev.Util ((.::), ordNub, mapBy)
-
--- | HsDev database
-data Database = Database {
-	databaseModules :: [InspectedModule],
-	databaseProjects :: [Project] }
-		deriving (Eq, Ord)
-
-instance NFData Database where
-	rnf (Database ms ps) = rnf ms `seq` rnf ps
-
-instance Group Database where
-	add old new = Database {
-		databaseModules = M.elems $ mapBy (view inspectedId) (databaseModules new) `M.union` mapBy (view inspectedId) (databaseModules old),
-		databaseProjects = M.elems $ M.unionWith mergeProject
-			(mapBy (view projectCabal) (databaseProjects new))
-			(mapBy (view projectCabal) (databaseProjects old)) }
-		where
-			mergeProject pl pr = set projectDescription (msum [view projectDescription pl, view projectDescription pr]) pl
-	sub old new = Database {
-		databaseModules = M.elems $ mapBy (view inspectedId) (databaseModules old) `M.difference` mapBy (view inspectedId) (databaseModules new),
-		databaseProjects = M.elems $ mapBy (view projectCabal) (databaseProjects old) `M.difference` mapBy (view projectCabal) (databaseProjects new) }
-	zero = Database [] []
-
-instance Monoid Database where
-	mempty = zero
-	mappend = add
-
-instance ToJSON Database where
-	toJSON (Database ms ps) = object [
-		"modules" .= ms,
-		"projects" .= ps]
-
-instance FromJSON Database where
-	parseJSON = withObject "database" $ \v -> Database <$>
-		(v .:: "modules") <*>
-		(v .:: "projects")
-
--- | Database intersection, prefers first database data
-databaseIntersection :: Database -> Database -> Database
-databaseIntersection l r = mempty {
-	databaseModules = M.elems $ mapBy (view inspectedId) (databaseModules l) `M.intersection` mapBy (view inspectedId) (databaseModules r),
-	databaseProjects = M.elems $ mapBy (view projectCabal) (databaseProjects l) `M.intersection` mapBy (view projectCabal) (databaseProjects r) }
-
--- | Check if database is empty
-nullDatabase :: Database -> Bool
-nullDatabase db = null (databaseModules db) && null (databaseProjects db)
-
--- | Bring all locals to scope
-databaseLocals :: Database -> Database
-databaseLocals db = db {
-	databaseModules = fmap (fmap moduleLocals) (databaseModules db) }
-
--- | All scanned sandboxes
-databasePackageDbs :: Database -> [PackageDb]
-databasePackageDbs db = ordNub $ databaseModules db ^.. each . inspectionResult . _Right . moduleLocation . modulePackageDb
-
--- | All modules
-allModules :: Database -> [Module]
-allModules = rights . fmap (view inspectionResult) . databaseModules
-
--- | All declarations
-allDeclarations :: Database -> [ModuleDeclaration]
-allDeclarations db = do
-	m <- allModules db
-	moduleModuleDeclarations m
-
--- | All packages
-allPackages :: Database -> [ModulePackage]
-allPackages = ordNub . mapMaybe (preview (moduleLocation . modulePackage . _Just)) . allModules
-
--- | Make database from module
-fromModule :: InspectedModule -> Database
-fromModule m = zero {
-	databaseModules = [m] }
-
--- | Make database from project
-fromProject :: Project -> Database
-fromProject p = zero {
-	databaseProjects = [p] }
-
--- | Filter database by predicate
-filterDB :: (ModuleId -> Bool) -> (Project -> Bool) -> Database -> Database
-filterDB m p db = mempty {
-	databaseModules = filter (either (const False) (m . view moduleId) . view inspectionResult) (databaseModules db),
-	databaseProjects = filter p (databaseProjects db) }
-
--- | Project database
-projectDB :: Project -> Database -> Database
-projectDB proj = filterDB (inProject proj) (((==) `on` view projectCabal) proj)
-
--- | Package-db database
-packageDbDB :: PackageDb -> Database -> Database
-packageDbDB pdb = filterDB (inPackageDb pdb) (const False)
-
-packageDbStackDB :: PackageDbStack -> Database -> Database
-packageDbStackDB pdbs = filterDB (inPackageDbStack pdbs) (const False)
-
--- | Standalone database
-standaloneDB :: Database -> Database
-standaloneDB = filterDB check' (const False) where
-	check' m = standalone m && byFile m
-
--- | Select module by predicate
-selectModules :: (Module -> Bool) -> Database -> [Module]
-selectModules p = filter p . allModules
-
--- | Select declaration by predicate
-selectDeclarations :: (ModuleDeclaration -> Bool) -> Database -> [ModuleDeclaration]
-selectDeclarations p = filter p . allDeclarations
-
--- | Lookup module by its location and name
-lookupModule :: ModuleLocation -> Database -> Maybe Module
-lookupModule mloc db = do
-	m <- find ((== mloc) . view inspectedId) $ databaseModules db
-	either (const Nothing) Just $ view inspectionResult m
-
--- | Lookup inspected module
-lookupInspected :: ModuleLocation -> Database -> Maybe InspectedModule
-lookupInspected mloc db = find ((== mloc) . view inspectedId) $ databaseModules db
-
--- | Lookup module by its source file
-lookupFile :: FilePath -> Database -> Maybe Module
-lookupFile f = listToMaybe . selectModules (inFile f . view moduleId)
-
--- | Refine project
-refineProject :: Database -> Project -> Maybe Project
-refineProject db proj = find ((== view projectCabal proj) . view projectCabal) $ databaseProjects db
-
--- | Get inspected module
-getInspected :: Database -> Module -> InspectedModule
-getInspected db m = fromMaybe err $ find ((== view moduleLocation m) . view inspectedId) $ databaseModules db where
-	err = error "Impossible happened: getInspected"
-
--- | Append database
-append :: Database -> Database -> Database
-append = add
-
--- | Remove database
-remove :: Database -> Database -> Database
-remove = sub
-
--- | Structured database
-data Structured = Structured {
-	structuredPackageDbs :: Map PackageDb Database,
-	structuredProjects :: Map FilePath Database,
-	structuredFiles :: Database }
-		deriving (Eq, Ord)
-
-instance NFData Structured where
-	rnf (Structured cs ps fs) = rnf cs `seq` rnf ps `seq` rnf fs
-
-instance Group Structured where
-	add old new = Structured {
-		structuredPackageDbs = structuredPackageDbs new `M.union` structuredPackageDbs old,
-		structuredProjects = structuredProjects new `M.union` structuredProjects old,
-		structuredFiles = structuredFiles old `add` structuredFiles new }
-	sub old new = Structured {
-		structuredPackageDbs = structuredPackageDbs old `M.difference` structuredPackageDbs new,
-		structuredProjects = structuredProjects old `M.difference` structuredProjects new,
-		structuredFiles = structuredFiles old `sub` structuredFiles new }
-	zero = Structured zero zero zero
-
-instance Monoid Structured where
-	mempty = zero
-	mappend = add
-
-instance ToJSON Structured where
-	toJSON (Structured cs ps fs) = object [
-		"cabals" .= M.elems cs,
-		"projects" .= M.elems ps,
-		"files" .= fs]
-
-instance FromJSON Structured where
-	parseJSON = withObject "structured" $ \v -> join $
-		either fail return <$> (structured <$>
-			(v .:: "cabals") <*>
-			(v .:: "projects") <*>
-			(v .:: "files"))
-
-structured :: [Database] -> [Database] -> Database -> Either String Structured
-structured cs ps fs = Structured <$> mkMap keyCabal cs <*> mkMap keyProj ps <*> pure fs where
-	mkMap :: Ord a => (Database -> Either String a) -> [Database] -> Either String (Map a Database)
-	mkMap key dbs = do
-		keys <- mapM key dbs
-		return $ M.fromList $ zip keys dbs
-	keyCabal :: Database -> Either String PackageDb
-	keyCabal db = unique
-		"No cabal"
-		"Different module cabals"
-		(ordNub <$> mapM getPackageDb (allModules db))
-		where
-			getPackageDb m = case view moduleLocation m of
-				InstalledModule c _ _ -> Right c
-				_ -> Left "Module have no cabal"
-	keyProj :: Database -> Either String FilePath
-	keyProj db = unique
-		"No project"
-		"Different module projects"
-		(return (databaseProjects db ^.. each . projectCabal))
-	-- Check that list results in one element
-	unique :: String -> String -> Either String [a] -> Either String a
-	unique _ _ (Left e) = Left e
-	unique no _ (Right []) = Left no
-	unique _ _ (Right [x]) = Right x
-	unique _ much (Right _) = Left much
-
-structurize :: Database -> Structured
-structurize db = Structured cs ps fs where
-	cs = M.fromList [(c, packageDbDB c db) | c <- ordNub (mapMaybe modPackageDb (allModules db))]
-	ps = M.fromList [(pname, projectDB (project pname) db) | pname <- databaseProjects db ^.. each . projectCabal]
-	fs = standaloneDB db
-
-merge :: Structured -> Database
-merge (Structured cs ps fs) = mconcat $ M.elems cs ++ M.elems ps ++ [fs]
-
-modPackageDb :: Module -> Maybe PackageDb
-modPackageDb m = case view moduleLocation m of
-	InstalledModule c _ _ -> Just c
-	_ -> Nothing
+{-# LANGUAGE OverloadedStrings #-}
+
+module HsDev.Database (
+	Database(..),
+	databaseIntersection, nullDatabase, databaseLocals, databasePackageDbs, allModules, allDeclarations, allPackages,
+	fromModule, fromProject,
+	filterDB,
+	projectDB, packageDbDB, packageDbStackDB, standaloneDB,
+	selectModules, selectDeclarations, lookupModule, lookupInspected, lookupFile, refineProject,
+	getInspected,
+
+	append, remove,
+
+	Structured(..),
+	structured, structurize, merge,
+
+	Map
+	) where
+
+import Control.Lens (set, view, preview, _Just, _Right, each, (^..))
+import Control.Monad (msum, join)
+import Control.DeepSeq (NFData(..))
+import Data.Aeson
+import Data.Either (rights)
+import Data.Foldable (find)
+import Data.Function (on)
+import Data.Group (Group(..))
+import Data.Map (Map)
+import Data.Maybe
+import qualified Data.Map as M
+
+import HsDev.Symbols
+import HsDev.Symbols.Util
+import HsDev.Util ((.::), ordNub, mapBy)
+
+-- | HsDev database
+data Database = Database {
+	databaseModules :: [InspectedModule],
+	databaseProjects :: [Project] }
+		deriving (Eq, Ord)
+
+instance NFData Database where
+	rnf (Database ms ps) = rnf ms `seq` rnf ps
+
+instance Group Database where
+	add old new = Database {
+		databaseModules = M.elems $ mapBy (view inspectedId) (databaseModules new) `M.union` mapBy (view inspectedId) (databaseModules old),
+		databaseProjects = M.elems $ M.unionWith mergeProject
+			(mapBy (view projectCabal) (databaseProjects new))
+			(mapBy (view projectCabal) (databaseProjects old)) }
+		where
+			mergeProject pl pr = set projectDescription (msum [view projectDescription pl, view projectDescription pr]) pl
+	sub old new = Database {
+		databaseModules = M.elems $ mapBy (view inspectedId) (databaseModules old) `M.difference` mapBy (view inspectedId) (databaseModules new),
+		databaseProjects = M.elems $ mapBy (view projectCabal) (databaseProjects old) `M.difference` mapBy (view projectCabal) (databaseProjects new) }
+	zero = Database [] []
+
+instance Monoid Database where
+	mempty = zero
+	mappend = add
+
+instance ToJSON Database where
+	toJSON (Database ms ps) = object [
+		"modules" .= ms,
+		"projects" .= ps]
+
+instance FromJSON Database where
+	parseJSON = withObject "database" $ \v -> Database <$>
+		(v .:: "modules") <*>
+		(v .:: "projects")
+
+-- | Database intersection, prefers first database data
+databaseIntersection :: Database -> Database -> Database
+databaseIntersection l r = mempty {
+	databaseModules = M.elems $ mapBy (view inspectedId) (databaseModules l) `M.intersection` mapBy (view inspectedId) (databaseModules r),
+	databaseProjects = M.elems $ mapBy (view projectCabal) (databaseProjects l) `M.intersection` mapBy (view projectCabal) (databaseProjects r) }
+
+-- | Check if database is empty
+nullDatabase :: Database -> Bool
+nullDatabase db = null (databaseModules db) && null (databaseProjects db)
+
+-- | Bring all locals to scope
+databaseLocals :: Database -> Database
+databaseLocals db = db {
+	databaseModules = fmap (fmap moduleLocals) (databaseModules db) }
+
+-- | All scanned sandboxes
+databasePackageDbs :: Database -> [PackageDb]
+databasePackageDbs db = ordNub $ databaseModules db ^.. each . inspectionResult . _Right . moduleLocation . modulePackageDb
+
+-- | All modules
+allModules :: Database -> [Module]
+allModules = rights . fmap (view inspectionResult) . databaseModules
+
+-- | All declarations
+allDeclarations :: Database -> [ModuleDeclaration]
+allDeclarations db = do
+	m <- allModules db
+	moduleModuleDeclarations m
+
+-- | All packages
+allPackages :: Database -> [ModulePackage]
+allPackages = ordNub . mapMaybe (preview (moduleLocation . modulePackage . _Just)) . allModules
+
+-- | Make database from module
+fromModule :: InspectedModule -> Database
+fromModule m = zero {
+	databaseModules = [m] }
+
+-- | Make database from project
+fromProject :: Project -> Database
+fromProject p = zero {
+	databaseProjects = [p] }
+
+-- | Filter database by predicate
+filterDB :: (ModuleId -> Bool) -> (Project -> Bool) -> Database -> Database
+filterDB m p db = mempty {
+	databaseModules = filter (either (const False) (m . view moduleId) . view inspectionResult) (databaseModules db),
+	databaseProjects = filter p (databaseProjects db) }
+
+-- | Project database
+projectDB :: Project -> Database -> Database
+projectDB proj = filterDB (inProject proj) (((==) `on` view projectCabal) proj)
+
+-- | Package-db database
+packageDbDB :: PackageDb -> Database -> Database
+packageDbDB pdb = filterDB (inPackageDb pdb) (const False)
+
+packageDbStackDB :: PackageDbStack -> Database -> Database
+packageDbStackDB pdbs = filterDB (inPackageDbStack pdbs) (const False)
+
+-- | Standalone database
+standaloneDB :: Database -> Database
+standaloneDB = filterDB check' (const False) where
+	check' m = standalone m && byFile m
+
+-- | Select module by predicate
+selectModules :: (Module -> Bool) -> Database -> [Module]
+selectModules p = filter p . allModules
+
+-- | Select declaration by predicate
+selectDeclarations :: (ModuleDeclaration -> Bool) -> Database -> [ModuleDeclaration]
+selectDeclarations p = filter p . allDeclarations
+
+-- | Lookup module by its location and name
+lookupModule :: ModuleLocation -> Database -> Maybe Module
+lookupModule mloc db = do
+	m <- find ((== mloc) . view inspectedId) $ databaseModules db
+	either (const Nothing) Just $ view inspectionResult m
+
+-- | Lookup inspected module
+lookupInspected :: ModuleLocation -> Database -> Maybe InspectedModule
+lookupInspected mloc db = find ((== mloc) . view inspectedId) $ databaseModules db
+
+-- | Lookup module by its source file
+lookupFile :: FilePath -> Database -> Maybe Module
+lookupFile f = listToMaybe . selectModules (inFile f . view moduleId)
+
+-- | Refine project
+refineProject :: Database -> Project -> Maybe Project
+refineProject db proj = find ((== view projectCabal proj) . view projectCabal) $ databaseProjects db
+
+-- | Get inspected module
+getInspected :: Database -> Module -> InspectedModule
+getInspected db m = fromMaybe err $ find ((== view moduleLocation m) . view inspectedId) $ databaseModules db where
+	err = error "Impossible happened: getInspected"
+
+-- | Append database
+append :: Database -> Database -> Database
+append = add
+
+-- | Remove database
+remove :: Database -> Database -> Database
+remove = sub
+
+-- | Structured database
+data Structured = Structured {
+	structuredPackageDbs :: Map PackageDb Database,
+	structuredProjects :: Map FilePath Database,
+	structuredFiles :: Database }
+		deriving (Eq, Ord)
+
+instance NFData Structured where
+	rnf (Structured cs ps fs) = rnf cs `seq` rnf ps `seq` rnf fs
+
+instance Group Structured where
+	add old new = Structured {
+		structuredPackageDbs = structuredPackageDbs new `M.union` structuredPackageDbs old,
+		structuredProjects = structuredProjects new `M.union` structuredProjects old,
+		structuredFiles = structuredFiles old `add` structuredFiles new }
+	sub old new = Structured {
+		structuredPackageDbs = structuredPackageDbs old `M.difference` structuredPackageDbs new,
+		structuredProjects = structuredProjects old `M.difference` structuredProjects new,
+		structuredFiles = structuredFiles old `sub` structuredFiles new }
+	zero = Structured zero zero zero
+
+instance Monoid Structured where
+	mempty = zero
+	mappend = add
+
+instance ToJSON Structured where
+	toJSON (Structured cs ps fs) = object [
+		"cabals" .= M.elems cs,
+		"projects" .= M.elems ps,
+		"files" .= fs]
+
+instance FromJSON Structured where
+	parseJSON = withObject "structured" $ \v -> join $
+		either fail return <$> (structured <$>
+			(v .:: "cabals") <*>
+			(v .:: "projects") <*>
+			(v .:: "files"))
+
+structured :: [Database] -> [Database] -> Database -> Either String Structured
+structured cs ps fs = Structured <$> mkMap keyCabal cs <*> mkMap keyProj ps <*> pure fs where
+	mkMap :: Ord a => (Database -> Either String a) -> [Database] -> Either String (Map a Database)
+	mkMap key dbs = do
+		keys <- mapM key dbs
+		return $ M.fromList $ zip keys dbs
+	keyCabal :: Database -> Either String PackageDb
+	keyCabal db = unique
+		"No cabal"
+		"Different module cabals"
+		(ordNub <$> mapM getPackageDb (allModules db))
+		where
+			getPackageDb m = case view moduleLocation m of
+				InstalledModule c _ _ -> Right c
+				_ -> Left "Module have no cabal"
+	keyProj :: Database -> Either String FilePath
+	keyProj db = unique
+		"No project"
+		"Different module projects"
+		(return (databaseProjects db ^.. each . projectCabal))
+	-- Check that list results in one element
+	unique :: String -> String -> Either String [a] -> Either String a
+	unique _ _ (Left e) = Left e
+	unique no _ (Right []) = Left no
+	unique _ _ (Right [x]) = Right x
+	unique _ much (Right _) = Left much
+
+structurize :: Database -> Structured
+structurize db = Structured cs ps fs where
+	cs = M.fromList [(c, packageDbDB c db) | c <- ordNub (mapMaybe modPackageDb (allModules db))]
+	ps = M.fromList [(pname, projectDB (project pname) db) | pname <- databaseProjects db ^.. each . projectCabal]
+	fs = standaloneDB db
+
+merge :: Structured -> Database
+merge (Structured cs ps fs) = mconcat $ M.elems cs ++ M.elems ps ++ [fs]
+
+modPackageDb :: Module -> Maybe PackageDb
+modPackageDb m = case view moduleLocation m of
+	InstalledModule c _ _ -> Just c
+	_ -> Nothing
diff --git a/src/HsDev/Database/Update.hs b/src/HsDev/Database/Update.hs
--- a/src/HsDev/Database/Update.hs
+++ b/src/HsDev/Database/Update.hs
@@ -1,447 +1,447 @@
-{-# LANGUAGE FlexibleContexts, OverloadedStrings, MultiParamTypeClasses #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Database.Update (
-	Status(..), Progress(..), Task(..), isStatus,
-	UpdateOptions(..),
-
-	UpdateM(..),
-	runUpdate,
-
-	postStatus, waiter, updater, loadCache, getCache, runTask, runTasks,
-	readDB,
-
-	scanModule, scanModules, scanFile, scanFileContents, scanCabal, prepareSandbox, scanSandbox, scanPackageDb, scanProjectFile, scanProjectStack, scanProject, scanDirectory, scanContents,
-	scanDocs, inferModTypes,
-	scan,
-	updateEvent, processEvent,
-
-	module HsDev.Database.Update.Types,
-
-	module HsDev.Watcher,
-
-	module Control.Monad.Except
-	) where
-
-import Control.Applicative ((<|>))
-import Control.Arrow
-import Control.Concurrent.Lifted (fork)
-import Control.DeepSeq
-import Control.Lens (preview, _Just, view, over, set, _1, mapMOf_, each, (^..), _head, _Right)
-import Control.Monad.Catch (catch)
-import Control.Monad.Except
-import Control.Monad.Reader
-import Control.Monad.Writer
-import Data.Aeson
-import Data.Aeson.Types
-import Data.Foldable (toList)
-import qualified Data.Map as M
-import Data.Maybe (mapMaybe, isJust, fromMaybe)
-import qualified Data.Text as T (unpack)
-import System.Directory (canonicalizePath, doesFileExist)
-import System.FilePath
-import qualified System.Log.Simple as Log
-
-import HsDev.Error
-import qualified HsDev.Cache.Structured as Cache
-import HsDev.Database
-import HsDev.Database.Async hiding (Event)
-import HsDev.Display
-import HsDev.Inspect (inspectDocs, inspectDocsGhc)
-import HsDev.Project
-import HsDev.Sandbox
-import HsDev.Stack
-import HsDev.Symbols
-import HsDev.Tools.Ghc.Session hiding (wait)
-import HsDev.Tools.Ghc.Types (inferTypes)
-import HsDev.Tools.HDocs
-import qualified HsDev.Scan as S
-import HsDev.Scan.Browse
-import HsDev.Util (isParent, ordNub)
-import qualified HsDev.Util as Util (withCurrentDirectory)
-import HsDev.Server.Types (commandNotify, serverWriteCache, serverReadCache)
-import HsDev.Server.Message
-import HsDev.Database.Update.Types
-import HsDev.Watcher
-import Text.Format
-
-onStatus :: UpdateMonad m => m ()
-onStatus = asks (view updateTasks) >>= commandNotify . Notification . toJSON . reverse
-
-childTask :: UpdateMonad m => Task -> m a -> m a
-childTask t = local (over updateTasks (t:))
-
-isStatus :: Value -> Bool
-isStatus = isJust . parseMaybe (parseJSON :: Value -> Parser Task)
-
-runUpdate :: ServerMonadBase m => UpdateOptions -> UpdateM m a -> ClientM m a
-runUpdate uopts act = Log.scope "update" $ do
-	(r, updatedMods) <- runWriterT (runUpdateM act' `runReaderT` uopts)
-	db <- askSession sessionDatabase
-	wait db
-	dbval <- liftIO $ readAsync db
-	let
-		dbs = ordNub $ mapMaybe (preview modulePackageDb) updatedMods
-		projs = ordNub $ mapMaybe (preview $ moduleProject . _Just) updatedMods
-		stand = any moduleStandalone updatedMods
-
-		modifiedDb = mconcat $ concat [
-			map (`packageDbDB` dbval) dbs,
-			map (`projectDB` dbval) projs,
-			[standaloneDB dbval | stand]]
-	serverWriteCache modifiedDb
-	return r
-	where
-		act' = do
-			(r, mlocs') <- liftM (second $ filter (isJust . preview moduleFile)) $ listen act
-			db <- askSession sessionDatabase
-			wait db
-			let
-				getMods :: (MonadIO m) => m [InspectedModule]
-				getMods = do
-					db' <- liftIO $ readAsync db
-					return $ filter ((`elem` mlocs') . view inspectedId) $ toList $ databaseModules db'
-			when (view updateDocs uopts) $ do
-				Log.sendLog Log.Trace "forking inspecting source docs"
-				void $ fork (getMods >>= waiter . mapM_ scanDocs_)
-			when (view updateInfer uopts) $ do
-				Log.sendLog Log.Trace "forking inferring types"
-				void $ fork (getMods >>= waiter . mapM_ inferModTypes_)
-			return r
-		scanDocs_ :: UpdateMonad m => InspectedModule -> m ()
-		scanDocs_ im = do
-			im' <- (S.scanModify (\opts _ -> liftIO . inspectDocs opts) im) <|> return im
-			updater $ fromModule im'
-		inferModTypes_ :: UpdateMonad m => InspectedModule -> m ()
-		inferModTypes_ im = do
-			-- TODO: locate sandbox
-			s <- getSession
-			im' <- (S.scanModify (infer' s) im) <|> return im
-			updater $ fromModule im'
-		infer' :: UpdateMonad m => Session -> [String] -> PackageDbStack -> Module -> m Module
-		infer' s opts _ m = case preview (moduleLocation . moduleFile) m of
-			Nothing -> return m
-			Just _ -> liftIO $ inWorker (sessionGhc s) $ do
-				targetSession opts m
-				inferTypes opts m Nothing
-
--- | Post status
-postStatus :: UpdateMonad m => Task -> m ()
-postStatus t = childTask t onStatus
-
--- | Wait DB to complete actions
-waiter :: UpdateMonad m => m () -> m ()
-waiter act = do
-	db <- askSession sessionDatabase
-	act
-	wait db
-
--- | Update task result to database
-updater :: UpdateMonad m => Database -> m ()
-updater db' = do
-	db <- askSession sessionDatabase
-	update db $ return $!! db'
-	tell $!! map (view moduleLocation) $ allModules db'
-
--- | Clear obsolete data from database
-cleaner :: UpdateMonad m => m Database -> m ()
-cleaner act = do
-	db <- askSession sessionDatabase
-	db' <- act
-	clear db $ return $!! db'
-
--- | Get data from cache without updating DB
-loadCache :: UpdateMonad m => (FilePath -> ExceptT String IO Structured) -> m Database
-loadCache act = do
-	mdat <- serverReadCache act
-	return $ fromMaybe mempty mdat
-
--- | Load data from cache if not loaded yet and wait
-getCache :: UpdateMonad m => (FilePath -> ExceptT String IO Structured) -> (Database -> Database) -> m Database
-getCache act check = do
-	dbval <- liftM check readDB
-	if nullDatabase dbval
-		then do
-			db <- loadCache act
-			waiter $ updater db
-			return db
-		else
-			return dbval
-
--- | Run one task
-runTask :: (Display t, UpdateMonad m, NFData a) => String -> t -> m a -> m a
-runTask action subj act = Log.scope "task" $ do
-	postStatus $ set taskStatus StatusWorking task
-	x <- childTask task act
-	x `deepseq` postStatus (set taskStatus StatusOk task)
-	return x
-	`catch`
-	(\e -> postStatus (set taskStatus (StatusError e) task) >> hsdevError e)
-	where
-		task = Task {
-			_taskName = action,
-			_taskStatus = StatusWorking,
-			_taskSubjectType = displayType subj,
-			_taskSubjectName = display subj,
-			_taskProgress = Nothing }
-
--- | Run many tasks with numeration
-runTasks :: UpdateMonad m => [m ()] -> m ()
-runTasks ts = zipWithM_ taskNum [1..] (map noErr ts) where
-	total = length ts
-	taskNum n = local setProgress where
-		setProgress = set (updateTasks . _head . taskProgress) (Just (Progress n total))
-	noErr v = v `mplus` return ()
-
--- | Get database value
-readDB :: SessionMonad m => m Database
-readDB = askSession sessionDatabase >>= liftIO . readAsync
-
--- | Scan module
-scanModule :: UpdateMonad m => [String] -> ModuleLocation -> Maybe String -> m ()
-scanModule opts mloc mcts = runTask "scanning" mloc $ Log.scope "module" $ do
-	defs <- askSession sessionDefines
-	im <- S.scanModule defs opts mloc mcts
-	updater $ fromModule im
-	_ <- return $ view inspectionResult im
-	return ()
-
--- | Scan modules
-scanModules :: UpdateMonad m => [String] -> [S.ModuleToScan] -> m ()
-scanModules opts ms = runTasks $
-	[scanProjectFile opts p >> return () | p <- ps] ++
-	[scanModule (opts ++ mopts) m mcts | (m, mopts, mcts) <- ms]
-	where
-		ps = ordNub $ mapMaybe (toProj . view _1) ms
-		toProj (FileModule _ p) = fmap (view projectCabal) p
-		toProj _ = Nothing
-
--- | Scan source file
-scanFile :: UpdateMonad m => [String] -> FilePath -> m ()
-scanFile opts fpath = scanFileContents opts fpath Nothing
-
--- | Scan source file with contents
-scanFileContents :: UpdateMonad m => [String] -> FilePath -> Maybe String -> m ()
-scanFileContents opts fpath mcts = Log.scope "file" $ hsdevLiftIO $ do
-	dbval <- readDB
-	fpath' <- liftIO $ canonicalizePath fpath
-	ex <- liftIO $ doesFileExist fpath'
-	mlocs <- if ex
-		then do
-			mloc <- case lookupFile fpath' dbval of
-				Just m -> return $ view moduleLocation m
-				Nothing -> do
-					mproj <- liftIO $ locateProject fpath'
-					return $ FileModule fpath' mproj
-			return [(mloc, [], mcts)]
-		else return []
-	mapMOf_ (each . _1) (watch . flip watchModule) mlocs
-	scan
-		(Cache.loadFiles (== fpath'))
-		(filterDB (inFile fpath') (const False) . standaloneDB)
-		mlocs
-		opts
-		(scanModules opts)
-	where
-		inFile f = maybe False (== f) . preview (moduleIdLocation . moduleFile)
-
--- | Scan cabal modules, doesn't rescan if already scanned
-scanCabal :: UpdateMonad m => [String] -> m ()
-scanCabal opts = Log.scope "cabal" $ do
-	dbval <- readDB
-	let
-		scannedDbs = databasePackageDbs dbval
-		unscannedDbs = filter ((`notElem` scannedDbs) . topPackageDb) $ reverse $ packageDbStacks userDb
-	if null unscannedDbs
-		then Log.sendLog Log.Trace $ "cabal (global-db and user-db) already scanned"
-		else runTasks $ map (scanPackageDb opts) unscannedDbs
-
--- | Prepare sandbox for scanning. This is used for stack project to build & configure.
-prepareSandbox :: UpdateMonad m => Sandbox -> m ()
-prepareSandbox sbox@(Sandbox StackWork fpath) = Log.scope "prepare" $ runTasks [
-	runTask "building dependencies" sbox $ void $ Util.withCurrentDirectory dir $ buildDeps Nothing,
-	runTask "configuring" sbox $ void $ Util.withCurrentDirectory dir $ configure Nothing]
-	where
-		dir = takeDirectory fpath
-prepareSandbox _ = return ()
-
--- | Scan sandbox modules, doesn't rescan if already scanned
-scanSandbox :: UpdateMonad m => [String] -> Sandbox -> m ()
-scanSandbox opts sbox = Log.scope "sandbox" $ do
-	dbval <- readDB
-	prepareSandbox sbox
-	pdbs <- sandboxPackageDbStack sbox
-	let
-		scannedDbs = databasePackageDbs dbval
-		unscannedDbs = filter ((`notElem` scannedDbs) . topPackageDb) $ reverse $ packageDbStacks pdbs
-	if null unscannedDbs
-		then Log.sendLog Log.Trace $ "sandbox already scanned"
-		else runTasks $ map (scanPackageDb opts) unscannedDbs
-
--- | Scan top of package-db stack, usable for rescan
-scanPackageDb :: UpdateMonad m => [String] -> PackageDbStack -> m ()
-scanPackageDb opts pdbs = runTask "scanning" (topPackageDb pdbs) $ Log.scope "package-db" $ do
-	watch (\w -> watchPackageDb w pdbs opts)
-	mlocs <- liftM
-		(filter (\mloc -> preview modulePackageDb mloc == Just (topPackageDb pdbs))) $
-		listModules opts pdbs
-	scan (Cache.loadPackageDb (topPackageDb pdbs)) (packageDbDB (topPackageDb pdbs)) ((,,) <$> mlocs <*> pure [] <*> pure Nothing) opts $ \mlocs' -> do
-		ms <- browseModules opts pdbs (mlocs' ^.. each . _1)
-		docs <- liftIO $ hsdevLiftWith (ToolError "hdocs") $ hdocsCabal pdbs opts
-		updater $ mconcat $ map (fromModule . fmap (setDocs' docs)) ms
-	where
-		setDocs' :: Map String (Map String String) -> Module -> Module
-		setDocs' docs m = maybe m (`setDocs` m) $ M.lookup (T.unpack $ view moduleName m) docs
-
--- | Scan project file
-scanProjectFile :: UpdateMonad m => [String] -> FilePath -> m Project
-scanProjectFile opts cabal = runTask "scanning" cabal $ do
-	proj <- S.scanProjectFile opts cabal
-	updater $ fromProject proj
-	return proj
-
--- | Scan project and related package-db stack
-scanProjectStack :: UpdateMonad m => [String] -> FilePath -> m ()
-scanProjectStack opts cabal = do
-	proj <- scanProjectFile opts cabal
-	scanProject opts cabal
-	sbox <- liftIO $ projectSandbox (view projectPath proj)
-	maybe (scanCabal opts) (scanSandbox opts) sbox
-
--- | Scan project
-scanProject :: UpdateMonad m => [String] -> FilePath -> m ()
-scanProject opts cabal = runTask "scanning" (project cabal) $ Log.scope "project" $ do
-	proj <- scanProjectFile opts cabal
-	watch (\w -> watchProject w proj opts)
-	S.ScanContents _ [(_, sources)] _ <- S.enumProject proj
-	scan (Cache.loadProject $ view projectCabal proj) (projectDB proj) sources opts $ scanModules opts
-
--- | Scan directory for source files and projects
-scanDirectory :: UpdateMonad m => [String] -> FilePath -> m ()
-scanDirectory opts dir = runTask "scanning" dir $ Log.scope "directory" $ do
-	S.ScanContents standSrcs projSrcs pdbss <- S.enumDirectory dir
-	runTasks [scanProject opts (view projectCabal p) | (p, _) <- projSrcs]
-	runTasks $ map (scanPackageDb opts) pdbss -- TODO: Don't rescan
-	mapMOf_ (each . _1) (watch . flip watchModule) standSrcs
-	scan (Cache.loadFiles (dir `isParent`)) (filterDB inDir (const False) . standaloneDB) standSrcs opts $ scanModules opts
-	where
-		inDir = maybe False (dir `isParent`) . preview (moduleIdLocation . moduleFile)
-
-scanContents :: UpdateMonad m => [String] -> S.ScanContents -> m ()
-scanContents opts (S.ScanContents standSrcs projSrcs pdbss) = do
-	dbval <- readDB
-	let
-		projs = databaseProjects dbval ^.. each . projectCabal
-		pdbs = databasePackageDbs dbval
-		files = allModules (standaloneDB dbval) ^.. each . moduleLocation . moduleFile
-		srcs = standSrcs ^.. each . _1 . moduleFile
-		inSrcs src = src `elem` srcs && src `notElem` files
-		inFiles = maybe False inSrcs . preview (moduleIdLocation . moduleFile)
-	mapMOf_ (each . _1 . projectCabal) (\p -> Log.sendLog Log.Trace ("scanning project: {}" ~~ p)) projSrcs
-	runTasks [scanProject opts (view projectCabal p) | (p, _) <- projSrcs, view projectCabal p `notElem` projs]
-	mapMOf_ (each . _1 . moduleFile) (\f -> Log.sendLog Log.Trace ("scanning file: {}" ~~ f)) standSrcs
-	mapMOf_ (each . _1) (watch . flip watchModule) standSrcs
-	scan (Cache.loadFiles inSrcs) (filterDB inFiles (const False) . standaloneDB) standSrcs opts $ scanModules opts
-	mapMOf_ each (\s -> Log.sendLog Log.Trace ("scanning package-db: {}" ~~ topPackageDb s)) pdbss
-	runTasks [scanPackageDb opts pdbs' | pdbs' <- pdbss, topPackageDb pdbs' `notElem` pdbs]
-
--- | Scan docs for inspected modules
-scanDocs :: UpdateMonad m => [InspectedModule] -> m ()
-scanDocs ims = do
-	-- w <- liftIO $ ghcWorker ["-haddock"] (return ())
-	-- w <- askSession sessionGhc
-	runTasks $ map scanDocs' ims
-	where
-		scanDocs' im
-			| not $ hasTag RefinedDocsTag im = runTask "scanning docs" (view inspectedId im) $ Log.scope "docs" $ do
-				Log.sendLog Log.Trace $ "Scanning docs for {}" ~~  view inspectedId im
-				im' <- (liftM (setTag RefinedDocsTag) $ S.scanModify doScan im)
-					<|> return im
-				Log.sendLog Log.Trace $ "Docs for {} updated: documented {} declarations" ~~
-					view inspectedId im' ~~
-					length (im' ^.. inspectionResult . _Right . moduleDeclarations . each . declarationDocs . _Just)
-				updater $ fromModule im'
-			| otherwise = Log.sendLog Log.Trace $ "Docs for {} already scanned" ~~ view inspectedId im
-		doScan _ _ m = do
-			w <- askSession sessionGhc
-			liftIO $ inWorker w $ do
-				opts' <- getModuleOpts [] m
-				haddockSession opts'
-				liftGhc $ inspectDocsGhc opts' m
-
-inferModTypes :: UpdateMonad m => [InspectedModule] -> m ()
-inferModTypes = runTasks . map inferModTypes' where
-	inferModTypes' im
-		| not $ hasTag InferredTypesTag im = runTask "inferring types" (view inspectedId im) $ Log.scope "docs" $ do
-			w <- askSession sessionGhc
-			Log.sendLog Log.Trace $ "Inferring types for {}" ~~ view inspectedId im
-			im' <- (liftM (setTag InferredTypesTag) $
-				S.scanModify (\opts _ m -> liftIO (inWorker w (targetSession opts m >> inferTypes opts m Nothing))) im)
-				<|> return im
-			Log.sendLog Log.Trace $ "Types for {} inferred" ~~ view inspectedId im
-			updater $ fromModule im'
-		| otherwise = Log.sendLog Log.Trace $ "Types for {} already inferred" ~~ view inspectedId im
-
--- | Generic scan function. Reads cache only if data is not already loaded, removes obsolete modules and rescans changed modules.
-scan :: UpdateMonad m
-	=> (FilePath -> ExceptT String IO Structured)
-	-- ^ Read data from cache
-	-> (Database -> Database)
-	-- ^ Get data from database
-	-> [S.ModuleToScan]
-	-- ^ Actual modules. Other modules will be removed from database
-	-> [String]
-	-- ^ Extra scan options
-	-> ([S.ModuleToScan] -> m ())
-	-- ^ Function to update changed modules
-	-> m ()
-scan cache' part' mlocs opts act = Log.scope "scan" $ do
-	dbval <- getCache cache' part'
-	let
-		obsolete = filterDB (\m -> view moduleIdLocation m `notElem` (mlocs ^.. each . _1)) (const False) dbval
-	changed <- liftIO $ S.changedModules dbval opts mlocs
-	cleaner $ return obsolete
-	act changed
-
-updateEvent :: ServerMonadBase m => Watched -> Event -> UpdateM m ()
-updateEvent (WatchedProject proj projOpts) e
-	| isSource e = do
-		Log.sendLog Log.Info $ "File '{file}' in project {proj} changed"
-			~~ ("file" ~% view eventPath e)
-			~~ ("proj" ~% view projectName proj)
-		dbval <- readDB
-		let
-			opts = fromMaybe [] $ do
-				m <- lookupFile (view eventPath e) dbval
-				preview (inspection . inspectionOpts) $ getInspected dbval m
-		scanFile opts $ view eventPath e
-	| isCabal e = do
-		Log.sendLog Log.Info $ "Project {proj} changed"
-			~~ ("proj" ~% view projectName proj)
-		scanProject projOpts $ view projectCabal proj
-	| otherwise = return ()
-updateEvent (WatchedPackageDb pdbs opts) e
-	| isConf e = do
-		Log.sendLog Log.Info $ "Package db {package} changed"
-			~~ ("package" ~% topPackageDb pdbs)
-		scanPackageDb opts pdbs
-	| otherwise = return ()
-updateEvent WatchedModule e
-	| isSource e = do
-		Log.sendLog Log.Info $ "Module {file} changed"
-			~~ ("file" ~% view eventPath e)
-		dbval <- readDB
-		let
-			opts = fromMaybe [] $ do
-				m <- lookupFile (view eventPath e) dbval
-				preview (inspection . inspectionOpts) $ getInspected dbval m
-		scanFile opts $ view eventPath e
-	| otherwise = return ()
-
-processEvent :: UpdateOptions -> Watched -> Event -> ClientM IO ()
-processEvent uopts w e = runUpdate uopts $ updateEvent w e
-
-watch :: SessionMonad m => (Watcher -> IO ()) -> m ()
-watch f = do
-	w <- askSession sessionWatcher
-	liftIO $ f w
+{-# LANGUAGE FlexibleContexts, OverloadedStrings, MultiParamTypeClasses #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Database.Update (
+	Status(..), Progress(..), Task(..), isStatus,
+	UpdateOptions(..),
+
+	UpdateM(..),
+	runUpdate,
+
+	postStatus, waiter, updater, loadCache, getCache, runTask, runTasks,
+	readDB,
+
+	scanModule, scanModules, scanFile, scanFileContents, scanCabal, prepareSandbox, scanSandbox, scanPackageDb, scanProjectFile, scanProjectStack, scanProject, scanDirectory, scanContents,
+	scanDocs, inferModTypes,
+	scan,
+	updateEvent, processEvent,
+
+	module HsDev.Database.Update.Types,
+
+	module HsDev.Watcher,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Applicative ((<|>))
+import Control.Arrow
+import Control.Concurrent.Lifted (fork)
+import Control.DeepSeq
+import Control.Lens (preview, _Just, view, over, set, _1, mapMOf_, each, (^..), _head, _Right)
+import Control.Monad.Catch (catch)
+import Control.Monad.Except
+import Control.Monad.Reader
+import Control.Monad.Writer
+import Data.Aeson
+import Data.Aeson.Types
+import Data.Foldable (toList)
+import qualified Data.Map as M
+import Data.Maybe (mapMaybe, isJust, fromMaybe)
+import qualified Data.Text as T (unpack)
+import System.Directory (canonicalizePath, doesFileExist)
+import System.FilePath
+import qualified System.Log.Simple as Log
+
+import HsDev.Error
+import qualified HsDev.Cache.Structured as Cache
+import HsDev.Database
+import HsDev.Database.Async hiding (Event)
+import HsDev.Display
+import HsDev.Inspect (inspectDocs, inspectDocsGhc)
+import HsDev.Project
+import HsDev.Sandbox
+import HsDev.Stack
+import HsDev.Symbols
+import HsDev.Tools.Ghc.Session hiding (wait)
+import HsDev.Tools.Ghc.Types (inferTypes)
+import HsDev.Tools.HDocs
+import qualified HsDev.Scan as S
+import HsDev.Scan.Browse
+import HsDev.Util (isParent, ordNub)
+import qualified HsDev.Util as Util (withCurrentDirectory)
+import HsDev.Server.Types (commandNotify, serverWriteCache, serverReadCache)
+import HsDev.Server.Message
+import HsDev.Database.Update.Types
+import HsDev.Watcher
+import Text.Format
+
+onStatus :: UpdateMonad m => m ()
+onStatus = asks (view updateTasks) >>= commandNotify . Notification . toJSON . reverse
+
+childTask :: UpdateMonad m => Task -> m a -> m a
+childTask t = local (over updateTasks (t:))
+
+isStatus :: Value -> Bool
+isStatus = isJust . parseMaybe (parseJSON :: Value -> Parser Task)
+
+runUpdate :: ServerMonadBase m => UpdateOptions -> UpdateM m a -> ClientM m a
+runUpdate uopts act = Log.scope "update" $ do
+	(r, updatedMods) <- runWriterT (runUpdateM act' `runReaderT` uopts)
+	db <- askSession sessionDatabase
+	wait db
+	dbval <- liftIO $ readAsync db
+	let
+		dbs = ordNub $ mapMaybe (preview modulePackageDb) updatedMods
+		projs = ordNub $ mapMaybe (preview $ moduleProject . _Just) updatedMods
+		stand = any moduleStandalone updatedMods
+
+		modifiedDb = mconcat $ concat [
+			map (`packageDbDB` dbval) dbs,
+			map (`projectDB` dbval) projs,
+			[standaloneDB dbval | stand]]
+	serverWriteCache modifiedDb
+	return r
+	where
+		act' = do
+			(r, mlocs') <- liftM (second $ filter (isJust . preview moduleFile)) $ listen act
+			db <- askSession sessionDatabase
+			wait db
+			let
+				getMods :: (MonadIO m) => m [InspectedModule]
+				getMods = do
+					db' <- liftIO $ readAsync db
+					return $ filter ((`elem` mlocs') . view inspectedId) $ toList $ databaseModules db'
+			when (view updateDocs uopts) $ do
+				Log.sendLog Log.Trace "forking inspecting source docs"
+				void $ fork (getMods >>= waiter . mapM_ scanDocs_)
+			when (view updateInfer uopts) $ do
+				Log.sendLog Log.Trace "forking inferring types"
+				void $ fork (getMods >>= waiter . mapM_ inferModTypes_)
+			return r
+		scanDocs_ :: UpdateMonad m => InspectedModule -> m ()
+		scanDocs_ im = do
+			im' <- (S.scanModify (\opts _ -> liftIO . inspectDocs opts) im) <|> return im
+			updater $ fromModule im'
+		inferModTypes_ :: UpdateMonad m => InspectedModule -> m ()
+		inferModTypes_ im = do
+			-- TODO: locate sandbox
+			s <- getSession
+			im' <- (S.scanModify (infer' s) im) <|> return im
+			updater $ fromModule im'
+		infer' :: UpdateMonad m => Session -> [String] -> PackageDbStack -> Module -> m Module
+		infer' s opts _ m = case preview (moduleLocation . moduleFile) m of
+			Nothing -> return m
+			Just _ -> liftIO $ inWorker (sessionGhc s) $ do
+				targetSession opts m
+				inferTypes opts m Nothing
+
+-- | Post status
+postStatus :: UpdateMonad m => Task -> m ()
+postStatus t = childTask t onStatus
+
+-- | Wait DB to complete actions
+waiter :: UpdateMonad m => m () -> m ()
+waiter act = do
+	db <- askSession sessionDatabase
+	act
+	wait db
+
+-- | Update task result to database
+updater :: UpdateMonad m => Database -> m ()
+updater db' = do
+	db <- askSession sessionDatabase
+	update db $ return $!! db'
+	tell $!! map (view moduleLocation) $ allModules db'
+
+-- | Clear obsolete data from database
+cleaner :: UpdateMonad m => m Database -> m ()
+cleaner act = do
+	db <- askSession sessionDatabase
+	db' <- act
+	clear db $ return $!! db'
+
+-- | Get data from cache without updating DB
+loadCache :: UpdateMonad m => (FilePath -> ExceptT String IO Structured) -> m Database
+loadCache act = do
+	mdat <- serverReadCache act
+	return $ fromMaybe mempty mdat
+
+-- | Load data from cache if not loaded yet and wait
+getCache :: UpdateMonad m => (FilePath -> ExceptT String IO Structured) -> (Database -> Database) -> m Database
+getCache act check = do
+	dbval <- liftM check readDB
+	if nullDatabase dbval
+		then do
+			db <- loadCache act
+			waiter $ updater db
+			return db
+		else
+			return dbval
+
+-- | Run one task
+runTask :: (Display t, UpdateMonad m, NFData a) => String -> t -> m a -> m a
+runTask action subj act = Log.scope "task" $ do
+	postStatus $ set taskStatus StatusWorking task
+	x <- childTask task act
+	x `deepseq` postStatus (set taskStatus StatusOk task)
+	return x
+	`catch`
+	(\e -> postStatus (set taskStatus (StatusError e) task) >> hsdevError e)
+	where
+		task = Task {
+			_taskName = action,
+			_taskStatus = StatusWorking,
+			_taskSubjectType = displayType subj,
+			_taskSubjectName = display subj,
+			_taskProgress = Nothing }
+
+-- | Run many tasks with numeration
+runTasks :: UpdateMonad m => [m ()] -> m ()
+runTasks ts = zipWithM_ taskNum [1..] (map noErr ts) where
+	total = length ts
+	taskNum n = local setProgress where
+		setProgress = set (updateTasks . _head . taskProgress) (Just (Progress n total))
+	noErr v = v `mplus` return ()
+
+-- | Get database value
+readDB :: SessionMonad m => m Database
+readDB = askSession sessionDatabase >>= liftIO . readAsync
+
+-- | Scan module
+scanModule :: UpdateMonad m => [String] -> ModuleLocation -> Maybe String -> m ()
+scanModule opts mloc mcts = runTask "scanning" mloc $ Log.scope "module" $ do
+	defs <- askSession sessionDefines
+	im <- S.scanModule defs opts mloc mcts
+	updater $ fromModule im
+	_ <- return $ view inspectionResult im
+	return ()
+
+-- | Scan modules
+scanModules :: UpdateMonad m => [String] -> [S.ModuleToScan] -> m ()
+scanModules opts ms = runTasks $
+	[scanProjectFile opts p >> return () | p <- ps] ++
+	[scanModule (opts ++ mopts) m mcts | (m, mopts, mcts) <- ms]
+	where
+		ps = ordNub $ mapMaybe (toProj . view _1) ms
+		toProj (FileModule _ p) = fmap (view projectCabal) p
+		toProj _ = Nothing
+
+-- | Scan source file
+scanFile :: UpdateMonad m => [String] -> FilePath -> m ()
+scanFile opts fpath = scanFileContents opts fpath Nothing
+
+-- | Scan source file with contents
+scanFileContents :: UpdateMonad m => [String] -> FilePath -> Maybe String -> m ()
+scanFileContents opts fpath mcts = Log.scope "file" $ hsdevLiftIO $ do
+	dbval <- readDB
+	fpath' <- liftIO $ canonicalizePath fpath
+	ex <- liftIO $ doesFileExist fpath'
+	mlocs <- if ex
+		then do
+			mloc <- case lookupFile fpath' dbval of
+				Just m -> return $ view moduleLocation m
+				Nothing -> do
+					mproj <- liftIO $ locateProject fpath'
+					return $ FileModule fpath' mproj
+			return [(mloc, [], mcts)]
+		else return []
+	mapMOf_ (each . _1) (watch . flip watchModule) mlocs
+	scan
+		(Cache.loadFiles (== fpath'))
+		(filterDB (inFile fpath') (const False) . standaloneDB)
+		mlocs
+		opts
+		(scanModules opts)
+	where
+		inFile f = maybe False (== f) . preview (moduleIdLocation . moduleFile)
+
+-- | Scan cabal modules, doesn't rescan if already scanned
+scanCabal :: UpdateMonad m => [String] -> m ()
+scanCabal opts = Log.scope "cabal" $ do
+	dbval <- readDB
+	let
+		scannedDbs = databasePackageDbs dbval
+		unscannedDbs = filter ((`notElem` scannedDbs) . topPackageDb) $ reverse $ packageDbStacks userDb
+	if null unscannedDbs
+		then Log.sendLog Log.Trace $ "cabal (global-db and user-db) already scanned"
+		else runTasks $ map (scanPackageDb opts) unscannedDbs
+
+-- | Prepare sandbox for scanning. This is used for stack project to build & configure.
+prepareSandbox :: UpdateMonad m => Sandbox -> m ()
+prepareSandbox sbox@(Sandbox StackWork fpath) = Log.scope "prepare" $ runTasks [
+	runTask "building dependencies" sbox $ void $ Util.withCurrentDirectory dir $ buildDeps Nothing,
+	runTask "configuring" sbox $ void $ Util.withCurrentDirectory dir $ configure Nothing]
+	where
+		dir = takeDirectory fpath
+prepareSandbox _ = return ()
+
+-- | Scan sandbox modules, doesn't rescan if already scanned
+scanSandbox :: UpdateMonad m => [String] -> Sandbox -> m ()
+scanSandbox opts sbox = Log.scope "sandbox" $ do
+	dbval <- readDB
+	prepareSandbox sbox
+	pdbs <- sandboxPackageDbStack sbox
+	let
+		scannedDbs = databasePackageDbs dbval
+		unscannedDbs = filter ((`notElem` scannedDbs) . topPackageDb) $ reverse $ packageDbStacks pdbs
+	if null unscannedDbs
+		then Log.sendLog Log.Trace $ "sandbox already scanned"
+		else runTasks $ map (scanPackageDb opts) unscannedDbs
+
+-- | Scan top of package-db stack, usable for rescan
+scanPackageDb :: UpdateMonad m => [String] -> PackageDbStack -> m ()
+scanPackageDb opts pdbs = runTask "scanning" (topPackageDb pdbs) $ Log.scope "package-db" $ do
+	watch (\w -> watchPackageDb w pdbs opts)
+	mlocs <- liftM
+		(filter (\mloc -> preview modulePackageDb mloc == Just (topPackageDb pdbs))) $
+		listModules opts pdbs
+	scan (Cache.loadPackageDb (topPackageDb pdbs)) (packageDbDB (topPackageDb pdbs)) ((,,) <$> mlocs <*> pure [] <*> pure Nothing) opts $ \mlocs' -> do
+		ms <- browseModules opts pdbs (mlocs' ^.. each . _1)
+		docs <- liftIO $ hsdevLiftWith (ToolError "hdocs") $ hdocsCabal pdbs opts
+		updater $ mconcat $ map (fromModule . fmap (setDocs' docs)) ms
+	where
+		setDocs' :: Map String (Map String String) -> Module -> Module
+		setDocs' docs m = maybe m (`setDocs` m) $ M.lookup (T.unpack $ view moduleName m) docs
+
+-- | Scan project file
+scanProjectFile :: UpdateMonad m => [String] -> FilePath -> m Project
+scanProjectFile opts cabal = runTask "scanning" cabal $ do
+	proj <- S.scanProjectFile opts cabal
+	updater $ fromProject proj
+	return proj
+
+-- | Scan project and related package-db stack
+scanProjectStack :: UpdateMonad m => [String] -> FilePath -> m ()
+scanProjectStack opts cabal = do
+	proj <- scanProjectFile opts cabal
+	scanProject opts cabal
+	sbox <- liftIO $ projectSandbox (view projectPath proj)
+	maybe (scanCabal opts) (scanSandbox opts) sbox
+
+-- | Scan project
+scanProject :: UpdateMonad m => [String] -> FilePath -> m ()
+scanProject opts cabal = runTask "scanning" (project cabal) $ Log.scope "project" $ do
+	proj <- scanProjectFile opts cabal
+	watch (\w -> watchProject w proj opts)
+	S.ScanContents _ [(_, sources)] _ <- S.enumProject proj
+	scan (Cache.loadProject $ view projectCabal proj) (projectDB proj) sources opts $ scanModules opts
+
+-- | Scan directory for source files and projects
+scanDirectory :: UpdateMonad m => [String] -> FilePath -> m ()
+scanDirectory opts dir = runTask "scanning" dir $ Log.scope "directory" $ do
+	S.ScanContents standSrcs projSrcs pdbss <- S.enumDirectory dir
+	runTasks [scanProject opts (view projectCabal p) | (p, _) <- projSrcs]
+	runTasks $ map (scanPackageDb opts) pdbss -- TODO: Don't rescan
+	mapMOf_ (each . _1) (watch . flip watchModule) standSrcs
+	scan (Cache.loadFiles (dir `isParent`)) (filterDB inDir (const False) . standaloneDB) standSrcs opts $ scanModules opts
+	where
+		inDir = maybe False (dir `isParent`) . preview (moduleIdLocation . moduleFile)
+
+scanContents :: UpdateMonad m => [String] -> S.ScanContents -> m ()
+scanContents opts (S.ScanContents standSrcs projSrcs pdbss) = do
+	dbval <- readDB
+	let
+		projs = databaseProjects dbval ^.. each . projectCabal
+		pdbs = databasePackageDbs dbval
+		files = allModules (standaloneDB dbval) ^.. each . moduleLocation . moduleFile
+		srcs = standSrcs ^.. each . _1 . moduleFile
+		inSrcs src = src `elem` srcs && src `notElem` files
+		inFiles = maybe False inSrcs . preview (moduleIdLocation . moduleFile)
+	mapMOf_ (each . _1 . projectCabal) (\p -> Log.sendLog Log.Trace ("scanning project: {}" ~~ p)) projSrcs
+	runTasks [scanProject opts (view projectCabal p) | (p, _) <- projSrcs, view projectCabal p `notElem` projs]
+	mapMOf_ (each . _1 . moduleFile) (\f -> Log.sendLog Log.Trace ("scanning file: {}" ~~ f)) standSrcs
+	mapMOf_ (each . _1) (watch . flip watchModule) standSrcs
+	scan (Cache.loadFiles inSrcs) (filterDB inFiles (const False) . standaloneDB) standSrcs opts $ scanModules opts
+	mapMOf_ each (\s -> Log.sendLog Log.Trace ("scanning package-db: {}" ~~ topPackageDb s)) pdbss
+	runTasks [scanPackageDb opts pdbs' | pdbs' <- pdbss, topPackageDb pdbs' `notElem` pdbs]
+
+-- | Scan docs for inspected modules
+scanDocs :: UpdateMonad m => [InspectedModule] -> m ()
+scanDocs ims = do
+	-- w <- liftIO $ ghcWorker ["-haddock"] (return ())
+	-- w <- askSession sessionGhc
+	runTasks $ map scanDocs' ims
+	where
+		scanDocs' im
+			| not $ hasTag RefinedDocsTag im = runTask "scanning docs" (view inspectedId im) $ Log.scope "docs" $ do
+				Log.sendLog Log.Trace $ "Scanning docs for {}" ~~  view inspectedId im
+				im' <- (liftM (setTag RefinedDocsTag) $ S.scanModify doScan im)
+					<|> return im
+				Log.sendLog Log.Trace $ "Docs for {} updated: documented {} declarations" ~~
+					view inspectedId im' ~~
+					length (im' ^.. inspectionResult . _Right . moduleDeclarations . each . declarationDocs . _Just)
+				updater $ fromModule im'
+			| otherwise = Log.sendLog Log.Trace $ "Docs for {} already scanned" ~~ view inspectedId im
+		doScan _ _ m = do
+			w <- askSession sessionGhc
+			liftIO $ inWorker w $ do
+				opts' <- getModuleOpts [] m
+				haddockSession opts'
+				liftGhc $ inspectDocsGhc opts' m
+
+inferModTypes :: UpdateMonad m => [InspectedModule] -> m ()
+inferModTypes = runTasks . map inferModTypes' where
+	inferModTypes' im
+		| not $ hasTag InferredTypesTag im = runTask "inferring types" (view inspectedId im) $ Log.scope "docs" $ do
+			w <- askSession sessionGhc
+			Log.sendLog Log.Trace $ "Inferring types for {}" ~~ view inspectedId im
+			im' <- (liftM (setTag InferredTypesTag) $
+				S.scanModify (\opts _ m -> liftIO (inWorker w (targetSession opts m >> inferTypes opts m Nothing))) im)
+				<|> return im
+			Log.sendLog Log.Trace $ "Types for {} inferred" ~~ view inspectedId im
+			updater $ fromModule im'
+		| otherwise = Log.sendLog Log.Trace $ "Types for {} already inferred" ~~ view inspectedId im
+
+-- | Generic scan function. Reads cache only if data is not already loaded, removes obsolete modules and rescans changed modules.
+scan :: UpdateMonad m
+	=> (FilePath -> ExceptT String IO Structured)
+	-- ^ Read data from cache
+	-> (Database -> Database)
+	-- ^ Get data from database
+	-> [S.ModuleToScan]
+	-- ^ Actual modules. Other modules will be removed from database
+	-> [String]
+	-- ^ Extra scan options
+	-> ([S.ModuleToScan] -> m ())
+	-- ^ Function to update changed modules
+	-> m ()
+scan cache' part' mlocs opts act = Log.scope "scan" $ do
+	dbval <- getCache cache' part'
+	let
+		obsolete = filterDB (\m -> view moduleIdLocation m `notElem` (mlocs ^.. each . _1)) (const False) dbval
+	changed <- liftIO $ S.changedModules dbval opts mlocs
+	cleaner $ return obsolete
+	act changed
+
+updateEvent :: ServerMonadBase m => Watched -> Event -> UpdateM m ()
+updateEvent (WatchedProject proj projOpts) e
+	| isSource e = do
+		Log.sendLog Log.Info $ "File '{file}' in project {proj} changed"
+			~~ ("file" ~% view eventPath e)
+			~~ ("proj" ~% view projectName proj)
+		dbval <- readDB
+		let
+			opts = fromMaybe [] $ do
+				m <- lookupFile (view eventPath e) dbval
+				preview (inspection . inspectionOpts) $ getInspected dbval m
+		scanFile opts $ view eventPath e
+	| isCabal e = do
+		Log.sendLog Log.Info $ "Project {proj} changed"
+			~~ ("proj" ~% view projectName proj)
+		scanProject projOpts $ view projectCabal proj
+	| otherwise = return ()
+updateEvent (WatchedPackageDb pdbs opts) e
+	| isConf e = do
+		Log.sendLog Log.Info $ "Package db {package} changed"
+			~~ ("package" ~% topPackageDb pdbs)
+		scanPackageDb opts pdbs
+	| otherwise = return ()
+updateEvent WatchedModule e
+	| isSource e = do
+		Log.sendLog Log.Info $ "Module {file} changed"
+			~~ ("file" ~% view eventPath e)
+		dbval <- readDB
+		let
+			opts = fromMaybe [] $ do
+				m <- lookupFile (view eventPath e) dbval
+				preview (inspection . inspectionOpts) $ getInspected dbval m
+		scanFile opts $ view eventPath e
+	| otherwise = return ()
+
+processEvent :: UpdateOptions -> Watched -> Event -> ClientM IO ()
+processEvent uopts w e = runUpdate uopts $ updateEvent w e
+
+watch :: SessionMonad m => (Watcher -> IO ()) -> m ()
+watch f = do
+	w <- askSession sessionWatcher
+	liftIO $ f w
diff --git a/src/HsDev/Display.hs b/src/HsDev/Display.hs
--- a/src/HsDev/Display.hs
+++ b/src/HsDev/Display.hs
@@ -1,55 +1,55 @@
-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Display (
-	Display(..)
-	) where
-
-import Control.Lens (view)
-import Data.Maybe (fromMaybe)
-
-import Text.Format
-
-import HsDev.PackageDb
-import HsDev.Project
-import HsDev.Sandbox
-import HsDev.Symbols.Location
-
-class Display a where
-	display :: a -> String
-	displayType :: a -> String
-
-instance Display PackageDb where
-	display GlobalDb = "global-db"
-	display UserDb = "user-db"
-	display (PackageDb p) = "package-db " ++ p
-	displayType _ = "package-db"
-
-instance Display ModuleLocation where
-	display (FileModule f _) = f
-	display (InstalledModule _ _ n) = n
-	display (ModuleSource s) = fromMaybe "" s
-	displayType _ = "module"
-
-instance Display Project where
-	display = view projectName
-	displayType _ = "project"
-
-instance Display Sandbox where
-	display (Sandbox _ fpath) = fpath
-	displayType (Sandbox CabalSandbox _) = "cabal-sandbox"
-	displayType (Sandbox StackWork _) = "stack-work"
-
-instance Display FilePath where
-	display = id
-	displayType _ = "path"
-
-instance Formattable PackageDb where
-	formattable = formattable . display
-
-instance Formattable ModuleLocation where
-	formattable = formattable . display
-
-instance Formattable Project where
-	formattable = formattable . display
-
+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Display (
+	Display(..)
+	) where
+
+import Control.Lens (view)
+import Data.Maybe (fromMaybe)
+
+import Text.Format
+
+import HsDev.PackageDb
+import HsDev.Project
+import HsDev.Sandbox
+import HsDev.Symbols.Location
+
+class Display a where
+	display :: a -> String
+	displayType :: a -> String
+
+instance Display PackageDb where
+	display GlobalDb = "global-db"
+	display UserDb = "user-db"
+	display (PackageDb p) = "package-db " ++ p
+	displayType _ = "package-db"
+
+instance Display ModuleLocation where
+	display (FileModule f _) = f
+	display (InstalledModule _ _ n) = n
+	display (ModuleSource s) = fromMaybe "" s
+	displayType _ = "module"
+
+instance Display Project where
+	display = view projectName
+	displayType _ = "project"
+
+instance Display Sandbox where
+	display (Sandbox _ fpath) = fpath
+	displayType (Sandbox CabalSandbox _) = "cabal-sandbox"
+	displayType (Sandbox StackWork _) = "stack-work"
+
+instance Display FilePath where
+	display = id
+	displayType _ = "path"
+
+instance Formattable PackageDb where
+	formattable = formattable . display
+
+instance Formattable ModuleLocation where
+	formattable = formattable . display
+
+instance Formattable Project where
+	formattable = formattable . display
+
diff --git a/src/HsDev/Error.hs b/src/HsDev/Error.hs
--- a/src/HsDev/Error.hs
+++ b/src/HsDev/Error.hs
@@ -1,63 +1,63 @@
-{-# LANGUAGE FlexibleContexts #-}
-
-module HsDev.Error (
-	hsdevError, hsdevOtherError, hsdevLift, hsdevLiftWith, hsdevCatch, hsdevExcept, hsdevLiftIO, hsdevLiftIOWith, hsdevIgnore,
-	hsdevHandle, hsdevLog,
-
-	module HsDev.Types
-	) where
-
-import Prelude hiding (log)
-
-import Control.Exception (IOException)
-import Control.Monad.Catch
-import Control.Monad.Except
-import Data.String (fromString)
-import System.Log.Simple (MonadLog, sendLog, Level)
-
-import HsDev.Types
-
--- | Throw `HsDevError`
-hsdevError :: MonadThrow m => HsDevError -> m a
-hsdevError = throwM
-
--- | Throw as `OtherError`
-hsdevOtherError :: (Exception e, MonadThrow m) => e -> m a
-hsdevOtherError = hsdevError . OtherError . displayException
-
--- | Throw as `OtherError`
-hsdevLift :: MonadThrow m => ExceptT String m a -> m a
-hsdevLift = hsdevLiftWith OtherError
-
--- | Throw as some `HsDevError`
-hsdevLiftWith :: MonadThrow m => (String -> HsDevError) -> ExceptT String m a -> m a
-hsdevLiftWith ctor act = runExceptT act >>= either (hsdevError . ctor) return
-
-hsdevCatch :: MonadCatch m => m a -> m (Either HsDevError a)
-hsdevCatch = try
-
-hsdevExcept :: MonadCatch m => m a -> ExceptT HsDevError m a
-hsdevExcept = ExceptT . hsdevCatch
-
--- | Rethrow IO exceptions as `HsDevError`
-hsdevLiftIO :: MonadCatch m => m a -> m a
-hsdevLiftIO = hsdevLiftIOWith IOFailed
-
--- | Rethrow IO exceptions
-hsdevLiftIOWith :: MonadCatch m => (String -> HsDevError) -> m a -> m a
-hsdevLiftIOWith ctor act = catch act onError where
-	onError :: MonadThrow m => IOException -> m a
-	onError = hsdevError . ctor . displayException
-
--- | Ignore hsdev exception
-hsdevIgnore :: MonadCatch m => a -> m a -> m a
-hsdevIgnore v act = hsdevCatch act >>= either (const $ return v) return
-
--- | Handle hsdev exception
-hsdevHandle :: MonadCatch m => (HsDevError -> m a) -> m a -> m a
-hsdevHandle h act = hsdevCatch act >>= either h return
-
--- | Log hsdev exception and rethrow
-hsdevLog :: MonadLog m => Level -> m a -> m a
-hsdevLog lev act = hsdevCatch act >>= either logError return where
-	logError e = sendLog lev (fromString $ show e) >> hsdevError e
+{-# LANGUAGE FlexibleContexts #-}
+
+module HsDev.Error (
+	hsdevError, hsdevOtherError, hsdevLift, hsdevLiftWith, hsdevCatch, hsdevExcept, hsdevLiftIO, hsdevLiftIOWith, hsdevIgnore,
+	hsdevHandle, hsdevLog,
+
+	module HsDev.Types
+	) where
+
+import Prelude hiding (log)
+
+import Control.Exception (IOException)
+import Control.Monad.Catch
+import Control.Monad.Except
+import Data.String (fromString)
+import System.Log.Simple (MonadLog, sendLog, Level)
+
+import HsDev.Types
+
+-- | Throw `HsDevError`
+hsdevError :: MonadThrow m => HsDevError -> m a
+hsdevError = throwM
+
+-- | Throw as `OtherError`
+hsdevOtherError :: (Exception e, MonadThrow m) => e -> m a
+hsdevOtherError = hsdevError . OtherError . displayException
+
+-- | Throw as `OtherError`
+hsdevLift :: MonadThrow m => ExceptT String m a -> m a
+hsdevLift = hsdevLiftWith OtherError
+
+-- | Throw as some `HsDevError`
+hsdevLiftWith :: MonadThrow m => (String -> HsDevError) -> ExceptT String m a -> m a
+hsdevLiftWith ctor act = runExceptT act >>= either (hsdevError . ctor) return
+
+hsdevCatch :: MonadCatch m => m a -> m (Either HsDevError a)
+hsdevCatch = try
+
+hsdevExcept :: MonadCatch m => m a -> ExceptT HsDevError m a
+hsdevExcept = ExceptT . hsdevCatch
+
+-- | Rethrow IO exceptions as `HsDevError`
+hsdevLiftIO :: MonadCatch m => m a -> m a
+hsdevLiftIO = hsdevLiftIOWith IOFailed
+
+-- | Rethrow IO exceptions
+hsdevLiftIOWith :: MonadCatch m => (String -> HsDevError) -> m a -> m a
+hsdevLiftIOWith ctor act = catch act onError where
+	onError :: MonadThrow m => IOException -> m a
+	onError = hsdevError . ctor . displayException
+
+-- | Ignore hsdev exception
+hsdevIgnore :: MonadCatch m => a -> m a -> m a
+hsdevIgnore v act = hsdevCatch act >>= either (const $ return v) return
+
+-- | Handle hsdev exception
+hsdevHandle :: MonadCatch m => (HsDevError -> m a) -> m a -> m a
+hsdevHandle h act = hsdevCatch act >>= either h return
+
+-- | Log hsdev exception and rethrow
+hsdevLog :: MonadLog m => Level -> m a -> m a
+hsdevLog lev act = hsdevCatch act >>= either logError return where
+	logError e = sendLog lev (fromString $ show e) >> hsdevError e
diff --git a/src/HsDev/Inspect.hs b/src/HsDev/Inspect.hs
--- a/src/HsDev/Inspect.hs
+++ b/src/HsDev/Inspect.hs
@@ -1,467 +1,467 @@
-{-# LANGUAGE TypeSynonymInstances #-}
-
-module HsDev.Inspect (
-	analyzeModule, inspectDocsChunk, inspectDocs, inspectDocsGhc,
-	inspectContents, contentsInspection,
-	inspectFile, fileInspection,
-	projectDirs, projectSources,
-	inspectProject,
-	getDefines,
-	preprocess, preprocess_,
-
-	module Control.Monad.Except
-	) where
-
-import Control.Arrow
-import Control.Applicative
-import Control.DeepSeq
-import qualified Control.Exception as E
-import Control.Lens (view, preview, set, over)
-import Control.Lens.At (ix)
-import Control.Monad
-import Control.Monad.Except
-import Data.Char (isSpace)
-import Data.Function (on)
-import Data.List
-import Data.Map (Map)
-import Data.Maybe (fromMaybe, mapMaybe, catMaybes, listToMaybe, isJust)
-import Data.Ord (comparing)
-import Data.String (IsString, fromString)
-import Data.Text (Text)
-import qualified Data.Text as T (unpack)
-import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, getPOSIXTime)
-import qualified Data.Map as M
-import qualified Language.Haskell.Exts as H
-import qualified Language.Preprocessor.Cpphs as Cpphs
-import qualified System.Directory as Dir
-import System.FilePath
-import Data.Generics.Uniplate.Data
-import HDocs.Haddock
-
-import HsDev.Error
-import HsDev.Symbols
-import HsDev.Tools.Base
-import HsDev.Tools.Ghc.Worker ()
-import HsDev.Tools.HDocs (hdocsy, hdocs, hdocsProcess)
-import HsDev.Util
-
--- | Analize source contents
-analyzeModule :: [String] -> Maybe FilePath -> String -> Either String Module
-analyzeModule exts file source = case H.parseFileContentsWithMode (parseMode file exts) source' of
-	H.ParseFailed loc reason -> Left $ "Parse failed at " ++ show loc ++ ": " ++ reason
-	H.ParseOk (H.Module _ (Just (H.ModuleHead _ (H.ModuleName _ mname) _ mexports)) _ imports declarations) -> Right Module {
-		_moduleName = fromString mname,
-		_moduleDocs =  Nothing,
-		_moduleLocation = ModuleSource Nothing,
-		_moduleExports = fmap (concatMap getExports . getSpec) mexports,
-		_moduleImports = map getImport imports,
-		_moduleDeclarations = sortDeclarations $ getDecls declarations }
-	_ -> Left "Unknown module"
-	where
-		-- Replace all tabs to spaces to make SrcLoc valid, otherwise it treats tab as 8 spaces
-		source' = map untab source
-		untab '\t' = ' '
-		untab ch = ch
-		getSpec (H.ExportSpecList _ es) = es
-
--- | Analize source contents
-analyzeModule_ :: [String] -> Maybe FilePath -> String -> Either String Module
-analyzeModule_ exts file source = do
-	mname <- parseModuleName source'
-	return Module {
-		_moduleName = fromString mname,
-		_moduleDocs = Nothing,
-		_moduleLocation = ModuleSource Nothing,
-		_moduleExports = do
-			H.PragmasAndModuleHead _ _ mhead <- parseModuleHead' source'
-			H.ModuleHead _ _ _ mexports <- mhead
-			H.ExportSpecList _ exports <- mexports
-			return $ concatMap getExports exports,
-		_moduleImports = map getImport $ mapMaybe (uncurry parseImport') parts,
-		_moduleDeclarations = sortDeclarations $ getDecls $ mapMaybe (uncurry parseDecl') parts }
-	where
-		parts :: [(Int, String)]
-		parts = zip offsets (map unlines parts') where
-			parts' :: [[String]]
-			parts' = unfoldr break' $ lines source'
-			offsets = scanl (+) 0 $ map length parts'
-		break' :: [String] -> Maybe ([String], [String])
-		break' [] = Nothing
-		break' (l:ls) = Just $ first (l:) $ span (maybe True isSpace . listToMaybe) ls
-
-		parseModuleName :: String -> Either String String
-		parseModuleName cts = maybe (Left "match fail") Right $ do
-			g <- matchRx "^module\\s+([\\w\\.]+)" cts
-			g 1
-
-		parseDecl' :: Int -> String -> Maybe (H.Decl H.SrcSpanInfo)
-		parseDecl' offset cts = maybeResult $ fmap (transformBi $ addOffset offset) $
-			H.parseDeclWithMode (parseMode file exts) cts
-
-		parseImport' :: Int -> String -> Maybe (H.ImportDecl H.SrcSpanInfo)
-		parseImport' offset cts = maybeResult $ fmap (transformBi $ addOffset offset) $
-			H.parseImportDeclWithMode (parseMode file exts) cts
-
-		parseModuleHead' :: String -> Maybe (H.PragmasAndModuleHead H.SrcSpanInfo)
-		parseModuleHead' = maybeResult . fmap H.unNonGreedy . H.parseWithMode (parseMode file exts)
-
-		maybeResult :: H.ParseResult a -> Maybe a
-		maybeResult (H.ParseFailed _ _) = Nothing
-		maybeResult (H.ParseOk r) = Just r
-
-		addOffset :: Int -> H.SrcSpan -> H.SrcSpan
-		addOffset offset src = src { H.srcSpanStartLine = H.srcSpanStartLine src + offset, H.srcSpanEndLine = H.srcSpanEndLine src + offset }
-
-		-- Replace all tabs to spaces to make SrcLoc valid, otherwise it treats tab as 8 spaces
-		source' = map untab source
-		untab '\t' = ' '
-		untab ch = ch
-
-parseMode :: Maybe FilePath -> [String] -> H.ParseMode
-parseMode file exts = H.defaultParseMode {
-	H.parseFilename = fromMaybe (H.parseFilename H.defaultParseMode) file,
-	H.baseLanguage = H.Haskell2010,
-	H.extensions = H.glasgowExts ++ map H.parseExtension exts,
-	H.fixities = Just H.baseFixities }
-
--- | Get exports
-getExports :: H.ExportSpec H.SrcSpanInfo -> [Export]
-getExports (H.EModuleContents _ (H.ModuleName _ m)) = [ExportModule $ fromString m]
-getExports (H.EVar _ n) = [uncurry ExportName (identOfQName n) ThingNothing]
-getExports (H.EAbs _ _ n) = [uncurry ExportName (identOfQName n) ThingNothing]
-getExports (H.EThingWith _ _ n ns) = [uncurry ExportName (identOfQName n) $ ThingWith (map toStr ns)] where
-	toStr :: H.CName H.SrcSpanInfo -> Text
-	toStr (H.VarName _ cn) = identOfName cn
-	toStr (H.ConName _ cn) = identOfName cn
-
--- | Get import
-getImport :: H.ImportDecl H.SrcSpanInfo -> Import
-getImport d = Import
-	(mname (H.importModule d))
-	(H.importQualified d)
-	(mname <$> H.importAs d)
-	(importLst <$> H.importSpecs d)
-	(Just $ toPosition $ H.ann d)
-	where
-		mname (H.ModuleName _ n) = fromString n
-		importLst (H.ImportSpecList _ hiding specs) = ImportList hiding $ map impSpec specs
-		impSpec (H.IVar _ n) = ImportSpec (identOfName n) ThingNothing
-		impSpec (H.IAbs _ _ n) = ImportSpec (identOfName n) ThingNothing
-		impSpec (H.IThingAll _ n) = ImportSpec (identOfName n) ThingAll
-		impSpec (H.IThingWith _ n ns) = ImportSpec (identOfName n) $ ThingWith $ map identOfName (concatMap childrenBi ns :: [H.Name H.SrcSpanInfo])
-
--- | Decl declarations
-getDecls :: [H.Decl H.SrcSpanInfo] -> [Declaration]
-getDecls decls =
-	map mergeDecls .
-	groupBy ((==) `on` view declarationName) .
-	sortBy (comparing (view declarationName)) $
-	concatMap getDecl decls ++ concatMap getDef decls
-	where
-		mergeDecls :: [Declaration] -> Declaration
-		mergeDecls [] = error "Impossible"
-		mergeDecls ds = Declaration
-			(view declarationName $ head ds)
-			Nothing
-			Nothing
-			(msum $ map (view declarationDocs) ds)
-			(minimum <$> mapM (view declarationPosition) ds)
-			(foldr1 mergeInfos $ map (view declaration) ds)
-
-		mergeInfos :: DeclarationInfo -> DeclarationInfo -> DeclarationInfo
-		mergeInfos (Function ln ld lr) (Function rn rd rr) = Function (ln `mplus` rn) (ld ++ rd) (lr `mplus` rr)
-		mergeInfos l _ = l
-
--- | Get local binds
-getLocalDecls :: H.Decl H.SrcSpanInfo -> [Declaration]
-getLocalDecls decl' = concatMap getDecls' binds' where
-	binds' :: [H.Binds H.SrcSpanInfo]
-	binds' = universeBi decl'
-	getDecls' :: H.Binds H.SrcSpanInfo -> [Declaration]
-	getDecls' (H.BDecls _ decls) = getDecls decls
-	getDecls' _ = []
-
--- | Get declaration and child declarations
-getDecl :: H.Decl H.SrcSpanInfo -> [Declaration]
-getDecl decl' = case decl' of
-	H.TypeSig loc names typeSignature -> [mkFun loc n (Function (Just $ oneLinePrint typeSignature) [] Nothing) | n <- names]
-	H.TypeDecl loc h _ -> [mkType loc (tyName h) Type (tyArgs h) `withDef` decl']
-	H.DataDecl loc dataOrNew mctx h cons _ -> (mkType loc (tyName h) (ctor dataOrNew `withCtx` mctx) (tyArgs h) `withDef` decl') : concatMap (map (addRel $ tyName h) . getConDecl (tyName h) (tyArgs h)) cons
-	H.GDataDecl loc dataOrNew mctx h _ gcons _ -> (mkType loc (tyName h) (ctor dataOrNew `withCtx` mctx) (tyArgs h) `withDef` decl') : concatMap (map (addRel $ tyName h) . getGConDecl) gcons
-	H.ClassDecl loc ctx h _ _ -> [mkType loc (tyName h) (Class `withCtx` ctx) (tyArgs h) `withDef` decl']
-	_ -> []
-	where
-		mkType :: H.SrcSpanInfo -> H.Name H.SrcSpanInfo -> (TypeInfo -> DeclarationInfo) -> [H.TyVarBind H.SrcSpanInfo] -> Declaration
-		mkType loc n ctor' args = setPosition loc $ decl (identOfName n) $ ctor' $ TypeInfo Nothing (map oneLinePrint args) Nothing []
-
-		withDef :: H.Pretty a => Declaration -> a -> Declaration
-		withDef tyDecl' tyDef = set (declaration . typeInfo . typeInfoDefinition) (Just $ prettyPrint tyDef) tyDecl'
-
-		withCtx :: (TypeInfo -> DeclarationInfo) -> Maybe (H.Context H.SrcSpanInfo) -> TypeInfo -> DeclarationInfo
-		withCtx ctor' mctx = ctor' . set typeInfoContext (fmap makeCtx mctx)
-
-		ctor :: H.DataOrNew H.SrcSpanInfo -> TypeInfo -> DeclarationInfo
-		ctor (H.DataType _) = Data
-		ctor (H.NewType _) = NewType
-
-		makeCtx ctx = fromString $ oneLinePrint ctx
-
-		addRel :: H.Name H.SrcSpanInfo -> Declaration -> Declaration
-		addRel n = set (declaration . related) (Just $ identOfName n)
-
-		tyName :: H.DeclHead H.SrcSpanInfo -> H.Name H.SrcSpanInfo
-		tyName (H.DHead _ n) = n
-		tyName (H.DHInfix _ _ n) = n
-		tyName (H.DHParen _ h) = tyName h
-		tyName (H.DHApp _ h _) = tyName h
-
-		tyArgs :: H.DeclHead H.SrcSpanInfo -> [H.TyVarBind H.SrcSpanInfo]
-		tyArgs = universeBi
-
--- | Get constructor and record fields declarations
-getConDecl :: H.Name H.SrcSpanInfo -> [H.TyVarBind H.SrcSpanInfo] -> H.QualConDecl H.SrcSpanInfo -> [Declaration]
-getConDecl t as (H.QualConDecl loc _ _ cdecl) = case cdecl of
-	H.ConDecl _ n cts -> [mkFun loc n (Function (Just $ oneLinePrint $ cts `tyFun` dataRes) [] Nothing)]
-	H.InfixConDecl _ ct n cts -> [mkFun loc n (Function (Just $ oneLinePrint $ (ct : [cts]) `tyFun` dataRes) [] Nothing)]
-	H.RecDecl _ n fields -> mkFun loc n (Function (Just $ oneLinePrint $ map tyField fields `tyFun` dataRes) [] Nothing) : concatMap (getRec loc dataRes) fields
-	where
-		dataRes :: H.Type H.SrcSpanInfo
-		dataRes = foldr (H.TyApp loc . H.TyVar loc . nameOf) (H.TyCon loc (H.UnQual loc t)) as where
-			nameOf :: H.TyVarBind H.SrcSpanInfo -> H.Name H.SrcSpanInfo
-			nameOf (H.KindedVar _ n' _) = n'
-			nameOf (H.UnkindedVar _ n') = n'
-
-tyField :: H.FieldDecl H.SrcSpanInfo -> H.Type H.SrcSpanInfo
-tyField (H.FieldDecl _ _ t) = t
-
--- | Get GADT constructor and record fields declarations
-getGConDecl :: H.GadtDecl H.SrcSpanInfo -> [Declaration]
-getGConDecl (H.GadtDecl loc n mfields r) = mkFun loc n (Function (Just $ oneLinePrint $ map tyField (fromMaybe [] mfields) `tyFun` r) [] Nothing) : concatMap (getRec loc r) (fromMaybe [] mfields)
-
--- | Get record field declaration
-getRec :: H.SrcSpanInfo -> H.Type H.SrcSpanInfo -> H.FieldDecl H.SrcSpanInfo -> [Declaration]
-getRec loc t (H.FieldDecl _ ns rt) = [mkFun loc n (Function (Just $ oneLinePrint $ H.TyFun loc t rt) [] Nothing) | n <- ns]
-
--- | Get definitions
-getDef :: H.Decl H.SrcSpanInfo -> [Declaration]
-getDef (H.FunBind _ []) = []
-getDef d@(H.FunBind _ (m : _)) = [setPosition (H.ann m) $ decl (identOfName $ identOfMatch m) fun] where
-	identOfMatch (H.Match _ n _ _ _) = n
-	identOfMatch (H.InfixMatch _ _ n _ _ _) = n
-	fun = Function Nothing (getLocalDecls d) Nothing
-getDef d@(H.PatBind loc pat _ _) = map (\name -> setPosition loc (decl (identOfName name) (Function Nothing (getLocalDecls d) Nothing))) (names pat) where
-	names :: H.Pat H.SrcSpanInfo -> [H.Name H.SrcSpanInfo]
-	names (H.PVar _ n) = [n]
-	names (H.PNPlusK _ n _) = [n]
-	names (H.PInfixApp _ l _ r) = names l ++ names r
-	names (H.PApp _ _ ns) = concatMap names ns
-	names (H.PTuple _ _ ns) = concatMap names ns
-	names (H.PList _ ns) = concatMap names ns
-	names (H.PParen _ n) = names n
-	names (H.PRec _ _ pf) = concatMap fieldNames pf
-	names (H.PAsPat _ n ns) = n : names ns
-	names (H.PWildCard _) = []
-	names (H.PIrrPat _ n) = names n
-	names (H.PatTypeSig _ n _) = names n
-	names (H.PViewPat _ _ n) = names n
-	names (H.PBangPat _ n) = names n
-	names _ = []
-
-	fieldNames :: H.PatField H.SrcSpanInfo -> [H.Name H.SrcSpanInfo]
-	fieldNames (H.PFieldPat _ _ n) = names n
-	fieldNames (H.PFieldPun _ n) = case n of
-		H.Qual _ _ n' -> [n']
-		H.UnQual _ n' -> [n']
-		_ -> []
-	fieldNames (H.PFieldWildcard _) = []
-getDef _ = []
-
--- | Make function declaration by location, name and function type
-mkFun :: H.SrcSpanInfo -> H.Name H.SrcSpanInfo -> DeclarationInfo -> Declaration
-mkFun loc n = setPosition loc . decl (identOfName n)
-
--- | Make function from arguments and result
---
--- @[a, b, c...] `tyFun` r == a `TyFun` b `TyFun` c ... `TyFun` r@
-tyFun :: [H.Type H.SrcSpanInfo] -> H.Type H.SrcSpanInfo -> H.Type H.SrcSpanInfo
-tyFun as' r' = foldr (H.TyFun H.noSrcSpan) r' as'
-
--- | Get name of qualified name
-identOfQName :: H.QName H.SrcSpanInfo -> (Maybe Text, Text)
-identOfQName (H.Qual _ (H.ModuleName _ mname) name) = (Just $ fromString mname, identOfName name)
-identOfQName (H.UnQual _ name) = (Nothing, identOfName name)
-identOfQName (H.Special _ sname) = (Nothing, fromString $ H.prettyPrint sname)
-
--- | Get name of @H.Name@
-identOfName :: H.Name H.SrcSpanInfo -> Text
-identOfName name = fromString $ case name of
-	H.Ident _ s -> s
-	H.Symbol _ s -> s
-
--- | Print something in one line
-oneLinePrint :: (H.Pretty a, IsString s) => a -> s
-oneLinePrint = fromString . H.prettyPrintStyleMode (H.style { H.mode = H.OneLineMode }) H.defaultMode
-
--- | Print something
-prettyPrint :: (H.Pretty a, IsString s) => a -> s
-prettyPrint = fromString . H.prettyPrintStyleMode (H.style { H.mode = H.PageMode }) mode' where
-	mode' = H.PPHsMode {
-		H.classIndent = 4,
-		H.doIndent = 4,
-		H.multiIfIndent = 4,
-		H.caseIndent = 4,
-		H.letIndent = 4,
-		H.whereIndent = 4,
-		H.onsideIndent = 2,
-		H.spacing = False,
-		H.layout = H.PPOffsideRule,
-		H.linePragmas = False }
-
--- | Convert @H.SrcSpanInfo@ to @Position
-toPosition :: H.SrcSpanInfo -> Position
-toPosition s = Position (H.startLine s) (H.startColumn s)
-
--- | Set @Declaration@ position
-setPosition :: H.SrcSpanInfo -> Declaration -> Declaration
-setPosition loc = set declarationPosition (Just $ toPosition loc)
-
--- | Adds documentation to declaration
-addDoc :: Map String String -> Declaration -> Declaration
-addDoc docsMap decl' = set declarationDocs (preview (ix (view declarationName decl')) docsMap') decl' where
-	docsMap' = M.mapKeys fromString . M.map fromString $ docsMap
-
--- | Adds documentation to all declarations in module
-addDocs :: Map String String -> Module -> Module
-addDocs docsMap = over moduleDeclarations (map $ addDoc docsMap)
-
--- | Extract files docs and set them to declarations
-inspectDocsChunk :: [String] -> [Module] -> IO [Module]
-inspectDocsChunk opts ms = hsdevLiftIOWith (ToolError "hdocs") $ do
-	docsMaps <- hdocsy (map (view moduleLocation) ms) opts
-	return $ zipWith addDocs docsMaps ms
-
--- | Extract file docs and set them to module declarations
-inspectDocs :: [String] -> Module -> IO Module
-inspectDocs opts m = do
-	let
-		hdocsWorkaround = False
-	docsMap <- if hdocsWorkaround
-		then hdocsProcess (fromMaybe (T.unpack $ view moduleName m) (preview (moduleLocation . moduleFile) m)) opts
-		else liftM Just $ hdocs (view moduleLocation m) opts
-	return $ maybe id addDocs docsMap m
-
--- | Like @inspectDocs@, but in @Ghc@ monad
-inspectDocsGhc :: [String] -> Module -> Ghc Module
-inspectDocsGhc opts m = case view moduleLocation m of
-	FileModule fpath _ -> do
-		docsMap <- liftM (fmap (formatDocs . snd) . listToMaybe) $ hsdevLift $ readSourcesGhc opts [fpath]
-		return $ maybe id addDocs docsMap m
-	_ -> hsdevError $ ModuleNotSource (view moduleLocation m)
-
--- | Inspect contents
-inspectContents :: String -> [(String, String)] -> [String] -> String -> ExceptT String IO InspectedModule
-inspectContents name defines opts cts = inspect (ModuleSource $ Just name) (contentsInspection cts opts) $ do
-	cts' <- lift $ preprocess_ defines exts name cts
-	analyzed <- ExceptT $ return $ analyzeModule exts (Just name) cts' <|> analyzeModule_ exts (Just name) cts'
-	return $ set moduleLocation (ModuleSource $ Just name) analyzed
-	where
-		exts = mapMaybe flagExtension opts
-
-contentsInspection :: String -> [String] -> ExceptT String IO Inspection
-contentsInspection _ _ = return InspectionNone -- crc or smth
-
--- | Inspect file
-inspectFile :: [(String, String)] -> [String] -> FilePath -> Maybe String -> IO InspectedModule
-inspectFile defines opts file mcts = hsdevLiftIO $ do
-	proj <- locateProject file
-	absFilename <- Dir.canonicalizePath file
-	ex <- Dir.doesFileExist absFilename
-	unless ex $ hsdevError $ FileNotFound absFilename
-	inspect (FileModule absFilename proj) ((if isJust mcts then fileContentsInspection else fileInspection) absFilename opts) $ do
-		-- docsMap <- liftE $ if hdocsWorkaround
-		-- 	then hdocsProcess absFilename opts
-		-- 	else liftM Just $ hdocs (FileModule absFilename Nothing) opts
-		forced <- hsdevLiftWith InspectError $ ExceptT $ E.handle onError $ do
-			analyzed <- liftM (\s -> analyzeModule exts (Just absFilename) s <|> analyzeModule_ exts (Just absFilename) s) $
-				maybe (readFileUtf8 absFilename >>= preprocess_ defines exts file) return mcts
-			force analyzed `deepseq` return analyzed
-		-- return $ setLoc absFilename proj . maybe id addDocs docsMap $ forced
-		return $ set moduleLocation (FileModule absFilename proj) forced
-	where
-		onError :: E.ErrorCall -> IO (Either String Module)
-		onError = return . Left . show
-
-		exts = mapMaybe flagExtension opts
-
--- | File inspection data
-fileInspection :: FilePath -> [String] -> IO Inspection
-fileInspection f opts = do
-	tm <- Dir.getModificationTime f
-	return $ InspectionAt (utcTimeToPOSIXSeconds tm) $ sort $ ordNub opts
-
--- | File contents inspection data
-fileContentsInspection :: FilePath -> [String] -> IO Inspection
-fileContentsInspection _ opts = do
-	tm <- getPOSIXTime
-	return $ InspectionAt tm $ sort $ ordNub opts
-
--- | Enumerate project dirs
-projectDirs :: Project -> IO [Extensions FilePath]
-projectDirs p = do
-	p' <- loadProject p
-	return $ ordNub $ map (fmap (normalise . (view projectPath p' </>))) $ maybe [] sourceDirs $ view projectDescription p'
-
--- | Enumerate project source files
-projectSources :: Project -> IO [Extensions FilePath]
-projectSources p = do
-	dirs <- projectDirs p
-	let
-		enumCabals = liftM (map takeDirectory . filter cabalFile) . traverseDirectory
-		dirs' = map (view entity) dirs
-	-- enum inner projects and dont consider them as part of this project
-	subProjs <- liftM (delete (view projectPath p) . ordNub . concat) $ triesMap (enumCabals) dirs'
-	let
-		enumHs = liftM (filter thisProjectSource) . traverseDirectory
-		thisProjectSource h = haskellSource h && not (any (`isParent` h) subProjs)
-	liftM (ordNub . concat) $ triesMap (liftM sequenceA . traverse (enumHs)) dirs
-
--- | Inspect project
-inspectProject :: [(String, String)] -> [String] -> Project -> IO (Project, [InspectedModule])
-inspectProject defines opts p = hsdevLiftIO $ do
-	p' <- loadProject p
-	srcs <- projectSources p'
-	modules <- mapM inspectFile' srcs
-	return (p', catMaybes modules)
-	where
-		inspectFile' exts = liftM return (inspectFile defines (opts ++ extensionsOpts exts) (view entity exts) Nothing) <|> return Nothing
-
--- | Get actual defines
-getDefines :: IO [(String, String)]
-getDefines = E.handle onIO $ do
-	tmp <- Dir.getTemporaryDirectory
-	writeFile (tmp </> "defines.hs") ""
-	_ <- runWait "ghc" ["-E", "-optP-dM", "-cpp", tmp </> "defines.hs"] ""
-	cts <- readFileUtf8 (tmp </> "defines.hspp")
-	Dir.removeFile (tmp </> "defines.hs")
-	Dir.removeFile (tmp </> "defines.hspp")
-	return $ mapMaybe (\g -> (,) <$> g 1 <*> g 2) $ mapMaybe (matchRx rx) $ lines cts
-	where
-		rx = "#define ([^\\s]+) (.*)"
-		onIO :: E.IOException -> IO [(String, String)]
-		onIO _ = return []
-
-preprocess :: [(String, String)] -> FilePath -> String -> IO String
-preprocess defines fpath cts = do
-	cts' <- E.catch (Cpphs.cppIfdef fpath defines [] Cpphs.defaultBoolOptions cts) onIOError
-	return $ unlines $ map snd cts'
-	where
-		onIOError :: E.IOException -> IO [(Cpphs.Posn, String)]
-		onIOError _ = return []
-
-preprocess_ :: [(String, String)] -> [String] -> FilePath -> String -> IO String
-preprocess_ defines exts fpath cts
-	| hasCPP = preprocess defines fpath cts
-	| otherwise = return cts
-	where
-		exts' = map H.parseExtension exts ++ maybe [] snd (H.readExtensions cts)
-		hasCPP = H.EnableExtension H.CPP `elem` exts'
+{-# LANGUAGE TypeSynonymInstances #-}
+
+module HsDev.Inspect (
+	analyzeModule, inspectDocsChunk, inspectDocs, inspectDocsGhc,
+	inspectContents, contentsInspection,
+	inspectFile, fileInspection,
+	projectDirs, projectSources,
+	inspectProject,
+	getDefines,
+	preprocess, preprocess_,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Arrow
+import Control.Applicative
+import Control.DeepSeq
+import qualified Control.Exception as E
+import Control.Lens (view, preview, set, over)
+import Control.Lens.At (ix)
+import Control.Monad
+import Control.Monad.Except
+import Data.Char (isSpace)
+import Data.Function (on)
+import Data.List
+import Data.Map (Map)
+import Data.Maybe (fromMaybe, mapMaybe, catMaybes, listToMaybe, isJust)
+import Data.Ord (comparing)
+import Data.String (IsString, fromString)
+import Data.Text (Text)
+import qualified Data.Text as T (unpack)
+import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, getPOSIXTime)
+import qualified Data.Map as M
+import qualified Language.Haskell.Exts as H
+import qualified Language.Preprocessor.Cpphs as Cpphs
+import qualified System.Directory as Dir
+import System.FilePath
+import Data.Generics.Uniplate.Data
+import HDocs.Haddock
+
+import HsDev.Error
+import HsDev.Symbols
+import HsDev.Tools.Base
+import HsDev.Tools.Ghc.Worker ()
+import HsDev.Tools.HDocs (hdocsy, hdocs, hdocsProcess)
+import HsDev.Util
+
+-- | Analize source contents
+analyzeModule :: [String] -> Maybe FilePath -> String -> Either String Module
+analyzeModule exts file source = case H.parseFileContentsWithMode (parseMode file exts) source' of
+	H.ParseFailed loc reason -> Left $ "Parse failed at " ++ show loc ++ ": " ++ reason
+	H.ParseOk (H.Module _ (Just (H.ModuleHead _ (H.ModuleName _ mname) _ mexports)) _ imports declarations) -> Right Module {
+		_moduleName = fromString mname,
+		_moduleDocs =  Nothing,
+		_moduleLocation = ModuleSource Nothing,
+		_moduleExports = fmap (concatMap getExports . getSpec) mexports,
+		_moduleImports = map getImport imports,
+		_moduleDeclarations = sortDeclarations $ getDecls declarations }
+	_ -> Left "Unknown module"
+	where
+		-- Replace all tabs to spaces to make SrcLoc valid, otherwise it treats tab as 8 spaces
+		source' = map untab source
+		untab '\t' = ' '
+		untab ch = ch
+		getSpec (H.ExportSpecList _ es) = es
+
+-- | Analize source contents
+analyzeModule_ :: [String] -> Maybe FilePath -> String -> Either String Module
+analyzeModule_ exts file source = do
+	mname <- parseModuleName source'
+	return Module {
+		_moduleName = fromString mname,
+		_moduleDocs = Nothing,
+		_moduleLocation = ModuleSource Nothing,
+		_moduleExports = do
+			H.PragmasAndModuleHead _ _ mhead <- parseModuleHead' source'
+			H.ModuleHead _ _ _ mexports <- mhead
+			H.ExportSpecList _ exports <- mexports
+			return $ concatMap getExports exports,
+		_moduleImports = map getImport $ mapMaybe (uncurry parseImport') parts,
+		_moduleDeclarations = sortDeclarations $ getDecls $ mapMaybe (uncurry parseDecl') parts }
+	where
+		parts :: [(Int, String)]
+		parts = zip offsets (map unlines parts') where
+			parts' :: [[String]]
+			parts' = unfoldr break' $ lines source'
+			offsets = scanl (+) 0 $ map length parts'
+		break' :: [String] -> Maybe ([String], [String])
+		break' [] = Nothing
+		break' (l:ls) = Just $ first (l:) $ span (maybe True isSpace . listToMaybe) ls
+
+		parseModuleName :: String -> Either String String
+		parseModuleName cts = maybe (Left "match fail") Right $ do
+			g <- matchRx "^module\\s+([\\w\\.]+)" cts
+			g 1
+
+		parseDecl' :: Int -> String -> Maybe (H.Decl H.SrcSpanInfo)
+		parseDecl' offset cts = maybeResult $ fmap (transformBi $ addOffset offset) $
+			H.parseDeclWithMode (parseMode file exts) cts
+
+		parseImport' :: Int -> String -> Maybe (H.ImportDecl H.SrcSpanInfo)
+		parseImport' offset cts = maybeResult $ fmap (transformBi $ addOffset offset) $
+			H.parseImportDeclWithMode (parseMode file exts) cts
+
+		parseModuleHead' :: String -> Maybe (H.PragmasAndModuleHead H.SrcSpanInfo)
+		parseModuleHead' = maybeResult . fmap H.unNonGreedy . H.parseWithMode (parseMode file exts)
+
+		maybeResult :: H.ParseResult a -> Maybe a
+		maybeResult (H.ParseFailed _ _) = Nothing
+		maybeResult (H.ParseOk r) = Just r
+
+		addOffset :: Int -> H.SrcSpan -> H.SrcSpan
+		addOffset offset src = src { H.srcSpanStartLine = H.srcSpanStartLine src + offset, H.srcSpanEndLine = H.srcSpanEndLine src + offset }
+
+		-- Replace all tabs to spaces to make SrcLoc valid, otherwise it treats tab as 8 spaces
+		source' = map untab source
+		untab '\t' = ' '
+		untab ch = ch
+
+parseMode :: Maybe FilePath -> [String] -> H.ParseMode
+parseMode file exts = H.defaultParseMode {
+	H.parseFilename = fromMaybe (H.parseFilename H.defaultParseMode) file,
+	H.baseLanguage = H.Haskell2010,
+	H.extensions = H.glasgowExts ++ map H.parseExtension exts,
+	H.fixities = Just H.baseFixities }
+
+-- | Get exports
+getExports :: H.ExportSpec H.SrcSpanInfo -> [Export]
+getExports (H.EModuleContents _ (H.ModuleName _ m)) = [ExportModule $ fromString m]
+getExports (H.EVar _ n) = [uncurry ExportName (identOfQName n) ThingNothing]
+getExports (H.EAbs _ _ n) = [uncurry ExportName (identOfQName n) ThingNothing]
+getExports (H.EThingWith _ _ n ns) = [uncurry ExportName (identOfQName n) $ ThingWith (map toStr ns)] where
+	toStr :: H.CName H.SrcSpanInfo -> Text
+	toStr (H.VarName _ cn) = identOfName cn
+	toStr (H.ConName _ cn) = identOfName cn
+
+-- | Get import
+getImport :: H.ImportDecl H.SrcSpanInfo -> Import
+getImport d = Import
+	(mname (H.importModule d))
+	(H.importQualified d)
+	(mname <$> H.importAs d)
+	(importLst <$> H.importSpecs d)
+	(Just $ toPosition $ H.ann d)
+	where
+		mname (H.ModuleName _ n) = fromString n
+		importLst (H.ImportSpecList _ hiding specs) = ImportList hiding $ map impSpec specs
+		impSpec (H.IVar _ n) = ImportSpec (identOfName n) ThingNothing
+		impSpec (H.IAbs _ _ n) = ImportSpec (identOfName n) ThingNothing
+		impSpec (H.IThingAll _ n) = ImportSpec (identOfName n) ThingAll
+		impSpec (H.IThingWith _ n ns) = ImportSpec (identOfName n) $ ThingWith $ map identOfName (concatMap childrenBi ns :: [H.Name H.SrcSpanInfo])
+
+-- | Decl declarations
+getDecls :: [H.Decl H.SrcSpanInfo] -> [Declaration]
+getDecls decls =
+	map mergeDecls .
+	groupBy ((==) `on` view declarationName) .
+	sortBy (comparing (view declarationName)) $
+	concatMap getDecl decls ++ concatMap getDef decls
+	where
+		mergeDecls :: [Declaration] -> Declaration
+		mergeDecls [] = error "Impossible"
+		mergeDecls ds = Declaration
+			(view declarationName $ head ds)
+			Nothing
+			Nothing
+			(msum $ map (view declarationDocs) ds)
+			(minimum <$> mapM (view declarationPosition) ds)
+			(foldr1 mergeInfos $ map (view declaration) ds)
+
+		mergeInfos :: DeclarationInfo -> DeclarationInfo -> DeclarationInfo
+		mergeInfos (Function ln ld lr) (Function rn rd rr) = Function (ln `mplus` rn) (ld ++ rd) (lr `mplus` rr)
+		mergeInfos l _ = l
+
+-- | Get local binds
+getLocalDecls :: H.Decl H.SrcSpanInfo -> [Declaration]
+getLocalDecls decl' = concatMap getDecls' binds' where
+	binds' :: [H.Binds H.SrcSpanInfo]
+	binds' = universeBi decl'
+	getDecls' :: H.Binds H.SrcSpanInfo -> [Declaration]
+	getDecls' (H.BDecls _ decls) = getDecls decls
+	getDecls' _ = []
+
+-- | Get declaration and child declarations
+getDecl :: H.Decl H.SrcSpanInfo -> [Declaration]
+getDecl decl' = case decl' of
+	H.TypeSig loc names typeSignature -> [mkFun loc n (Function (Just $ oneLinePrint typeSignature) [] Nothing) | n <- names]
+	H.TypeDecl loc h _ -> [mkType loc (tyName h) Type (tyArgs h) `withDef` decl']
+	H.DataDecl loc dataOrNew mctx h cons _ -> (mkType loc (tyName h) (ctor dataOrNew `withCtx` mctx) (tyArgs h) `withDef` decl') : concatMap (map (addRel $ tyName h) . getConDecl (tyName h) (tyArgs h)) cons
+	H.GDataDecl loc dataOrNew mctx h _ gcons _ -> (mkType loc (tyName h) (ctor dataOrNew `withCtx` mctx) (tyArgs h) `withDef` decl') : concatMap (map (addRel $ tyName h) . getGConDecl) gcons
+	H.ClassDecl loc ctx h _ _ -> [mkType loc (tyName h) (Class `withCtx` ctx) (tyArgs h) `withDef` decl']
+	_ -> []
+	where
+		mkType :: H.SrcSpanInfo -> H.Name H.SrcSpanInfo -> (TypeInfo -> DeclarationInfo) -> [H.TyVarBind H.SrcSpanInfo] -> Declaration
+		mkType loc n ctor' args = setPosition loc $ decl (identOfName n) $ ctor' $ TypeInfo Nothing (map oneLinePrint args) Nothing []
+
+		withDef :: H.Pretty a => Declaration -> a -> Declaration
+		withDef tyDecl' tyDef = set (declaration . typeInfo . typeInfoDefinition) (Just $ prettyPrint tyDef) tyDecl'
+
+		withCtx :: (TypeInfo -> DeclarationInfo) -> Maybe (H.Context H.SrcSpanInfo) -> TypeInfo -> DeclarationInfo
+		withCtx ctor' mctx = ctor' . set typeInfoContext (fmap makeCtx mctx)
+
+		ctor :: H.DataOrNew H.SrcSpanInfo -> TypeInfo -> DeclarationInfo
+		ctor (H.DataType _) = Data
+		ctor (H.NewType _) = NewType
+
+		makeCtx ctx = fromString $ oneLinePrint ctx
+
+		addRel :: H.Name H.SrcSpanInfo -> Declaration -> Declaration
+		addRel n = set (declaration . related) (Just $ identOfName n)
+
+		tyName :: H.DeclHead H.SrcSpanInfo -> H.Name H.SrcSpanInfo
+		tyName (H.DHead _ n) = n
+		tyName (H.DHInfix _ _ n) = n
+		tyName (H.DHParen _ h) = tyName h
+		tyName (H.DHApp _ h _) = tyName h
+
+		tyArgs :: H.DeclHead H.SrcSpanInfo -> [H.TyVarBind H.SrcSpanInfo]
+		tyArgs = universeBi
+
+-- | Get constructor and record fields declarations
+getConDecl :: H.Name H.SrcSpanInfo -> [H.TyVarBind H.SrcSpanInfo] -> H.QualConDecl H.SrcSpanInfo -> [Declaration]
+getConDecl t as (H.QualConDecl loc _ _ cdecl) = case cdecl of
+	H.ConDecl _ n cts -> [mkFun loc n (Function (Just $ oneLinePrint $ cts `tyFun` dataRes) [] Nothing)]
+	H.InfixConDecl _ ct n cts -> [mkFun loc n (Function (Just $ oneLinePrint $ (ct : [cts]) `tyFun` dataRes) [] Nothing)]
+	H.RecDecl _ n fields -> mkFun loc n (Function (Just $ oneLinePrint $ map tyField fields `tyFun` dataRes) [] Nothing) : concatMap (getRec loc dataRes) fields
+	where
+		dataRes :: H.Type H.SrcSpanInfo
+		dataRes = foldr (H.TyApp loc . H.TyVar loc . nameOf) (H.TyCon loc (H.UnQual loc t)) as where
+			nameOf :: H.TyVarBind H.SrcSpanInfo -> H.Name H.SrcSpanInfo
+			nameOf (H.KindedVar _ n' _) = n'
+			nameOf (H.UnkindedVar _ n') = n'
+
+tyField :: H.FieldDecl H.SrcSpanInfo -> H.Type H.SrcSpanInfo
+tyField (H.FieldDecl _ _ t) = t
+
+-- | Get GADT constructor and record fields declarations
+getGConDecl :: H.GadtDecl H.SrcSpanInfo -> [Declaration]
+getGConDecl (H.GadtDecl loc n mfields r) = mkFun loc n (Function (Just $ oneLinePrint $ map tyField (fromMaybe [] mfields) `tyFun` r) [] Nothing) : concatMap (getRec loc r) (fromMaybe [] mfields)
+
+-- | Get record field declaration
+getRec :: H.SrcSpanInfo -> H.Type H.SrcSpanInfo -> H.FieldDecl H.SrcSpanInfo -> [Declaration]
+getRec loc t (H.FieldDecl _ ns rt) = [mkFun loc n (Function (Just $ oneLinePrint $ H.TyFun loc t rt) [] Nothing) | n <- ns]
+
+-- | Get definitions
+getDef :: H.Decl H.SrcSpanInfo -> [Declaration]
+getDef (H.FunBind _ []) = []
+getDef d@(H.FunBind _ (m : _)) = [setPosition (H.ann m) $ decl (identOfName $ identOfMatch m) fun] where
+	identOfMatch (H.Match _ n _ _ _) = n
+	identOfMatch (H.InfixMatch _ _ n _ _ _) = n
+	fun = Function Nothing (getLocalDecls d) Nothing
+getDef d@(H.PatBind loc pat _ _) = map (\name -> setPosition loc (decl (identOfName name) (Function Nothing (getLocalDecls d) Nothing))) (names pat) where
+	names :: H.Pat H.SrcSpanInfo -> [H.Name H.SrcSpanInfo]
+	names (H.PVar _ n) = [n]
+	names (H.PNPlusK _ n _) = [n]
+	names (H.PInfixApp _ l _ r) = names l ++ names r
+	names (H.PApp _ _ ns) = concatMap names ns
+	names (H.PTuple _ _ ns) = concatMap names ns
+	names (H.PList _ ns) = concatMap names ns
+	names (H.PParen _ n) = names n
+	names (H.PRec _ _ pf) = concatMap fieldNames pf
+	names (H.PAsPat _ n ns) = n : names ns
+	names (H.PWildCard _) = []
+	names (H.PIrrPat _ n) = names n
+	names (H.PatTypeSig _ n _) = names n
+	names (H.PViewPat _ _ n) = names n
+	names (H.PBangPat _ n) = names n
+	names _ = []
+
+	fieldNames :: H.PatField H.SrcSpanInfo -> [H.Name H.SrcSpanInfo]
+	fieldNames (H.PFieldPat _ _ n) = names n
+	fieldNames (H.PFieldPun _ n) = case n of
+		H.Qual _ _ n' -> [n']
+		H.UnQual _ n' -> [n']
+		_ -> []
+	fieldNames (H.PFieldWildcard _) = []
+getDef _ = []
+
+-- | Make function declaration by location, name and function type
+mkFun :: H.SrcSpanInfo -> H.Name H.SrcSpanInfo -> DeclarationInfo -> Declaration
+mkFun loc n = setPosition loc . decl (identOfName n)
+
+-- | Make function from arguments and result
+--
+-- @[a, b, c...] `tyFun` r == a `TyFun` b `TyFun` c ... `TyFun` r@
+tyFun :: [H.Type H.SrcSpanInfo] -> H.Type H.SrcSpanInfo -> H.Type H.SrcSpanInfo
+tyFun as' r' = foldr (H.TyFun H.noSrcSpan) r' as'
+
+-- | Get name of qualified name
+identOfQName :: H.QName H.SrcSpanInfo -> (Maybe Text, Text)
+identOfQName (H.Qual _ (H.ModuleName _ mname) name) = (Just $ fromString mname, identOfName name)
+identOfQName (H.UnQual _ name) = (Nothing, identOfName name)
+identOfQName (H.Special _ sname) = (Nothing, fromString $ H.prettyPrint sname)
+
+-- | Get name of @H.Name@
+identOfName :: H.Name H.SrcSpanInfo -> Text
+identOfName name = fromString $ case name of
+	H.Ident _ s -> s
+	H.Symbol _ s -> s
+
+-- | Print something in one line
+oneLinePrint :: (H.Pretty a, IsString s) => a -> s
+oneLinePrint = fromString . H.prettyPrintStyleMode (H.style { H.mode = H.OneLineMode }) H.defaultMode
+
+-- | Print something
+prettyPrint :: (H.Pretty a, IsString s) => a -> s
+prettyPrint = fromString . H.prettyPrintStyleMode (H.style { H.mode = H.PageMode }) mode' where
+	mode' = H.PPHsMode {
+		H.classIndent = 4,
+		H.doIndent = 4,
+		H.multiIfIndent = 4,
+		H.caseIndent = 4,
+		H.letIndent = 4,
+		H.whereIndent = 4,
+		H.onsideIndent = 2,
+		H.spacing = False,
+		H.layout = H.PPOffsideRule,
+		H.linePragmas = False }
+
+-- | Convert @H.SrcSpanInfo@ to @Position
+toPosition :: H.SrcSpanInfo -> Position
+toPosition s = Position (H.startLine s) (H.startColumn s)
+
+-- | Set @Declaration@ position
+setPosition :: H.SrcSpanInfo -> Declaration -> Declaration
+setPosition loc = set declarationPosition (Just $ toPosition loc)
+
+-- | Adds documentation to declaration
+addDoc :: Map String String -> Declaration -> Declaration
+addDoc docsMap decl' = set declarationDocs (preview (ix (view declarationName decl')) docsMap') decl' where
+	docsMap' = M.mapKeys fromString . M.map fromString $ docsMap
+
+-- | Adds documentation to all declarations in module
+addDocs :: Map String String -> Module -> Module
+addDocs docsMap = over moduleDeclarations (map $ addDoc docsMap)
+
+-- | Extract files docs and set them to declarations
+inspectDocsChunk :: [String] -> [Module] -> IO [Module]
+inspectDocsChunk opts ms = hsdevLiftIOWith (ToolError "hdocs") $ do
+	docsMaps <- hdocsy (map (view moduleLocation) ms) opts
+	return $ zipWith addDocs docsMaps ms
+
+-- | Extract file docs and set them to module declarations
+inspectDocs :: [String] -> Module -> IO Module
+inspectDocs opts m = do
+	let
+		hdocsWorkaround = False
+	docsMap <- if hdocsWorkaround
+		then hdocsProcess (fromMaybe (T.unpack $ view moduleName m) (preview (moduleLocation . moduleFile) m)) opts
+		else liftM Just $ hdocs (view moduleLocation m) opts
+	return $ maybe id addDocs docsMap m
+
+-- | Like @inspectDocs@, but in @Ghc@ monad
+inspectDocsGhc :: [String] -> Module -> Ghc Module
+inspectDocsGhc opts m = case view moduleLocation m of
+	FileModule fpath _ -> do
+		docsMap <- liftM (fmap (formatDocs . snd) . listToMaybe) $ hsdevLift $ readSourcesGhc opts [fpath]
+		return $ maybe id addDocs docsMap m
+	_ -> hsdevError $ ModuleNotSource (view moduleLocation m)
+
+-- | Inspect contents
+inspectContents :: String -> [(String, String)] -> [String] -> String -> ExceptT String IO InspectedModule
+inspectContents name defines opts cts = inspect (ModuleSource $ Just name) (contentsInspection cts opts) $ do
+	cts' <- lift $ preprocess_ defines exts name cts
+	analyzed <- ExceptT $ return $ analyzeModule exts (Just name) cts' <|> analyzeModule_ exts (Just name) cts'
+	return $ set moduleLocation (ModuleSource $ Just name) analyzed
+	where
+		exts = mapMaybe flagExtension opts
+
+contentsInspection :: String -> [String] -> ExceptT String IO Inspection
+contentsInspection _ _ = return InspectionNone -- crc or smth
+
+-- | Inspect file
+inspectFile :: [(String, String)] -> [String] -> FilePath -> Maybe String -> IO InspectedModule
+inspectFile defines opts file mcts = hsdevLiftIO $ do
+	proj <- locateProject file
+	absFilename <- Dir.canonicalizePath file
+	ex <- Dir.doesFileExist absFilename
+	unless ex $ hsdevError $ FileNotFound absFilename
+	inspect (FileModule absFilename proj) ((if isJust mcts then fileContentsInspection else fileInspection) absFilename opts) $ do
+		-- docsMap <- liftE $ if hdocsWorkaround
+		-- 	then hdocsProcess absFilename opts
+		-- 	else liftM Just $ hdocs (FileModule absFilename Nothing) opts
+		forced <- hsdevLiftWith InspectError $ ExceptT $ E.handle onError $ do
+			analyzed <- liftM (\s -> analyzeModule exts (Just absFilename) s <|> analyzeModule_ exts (Just absFilename) s) $
+				maybe (readFileUtf8 absFilename >>= preprocess_ defines exts file) return mcts
+			force analyzed `deepseq` return analyzed
+		-- return $ setLoc absFilename proj . maybe id addDocs docsMap $ forced
+		return $ set moduleLocation (FileModule absFilename proj) forced
+	where
+		onError :: E.ErrorCall -> IO (Either String Module)
+		onError = return . Left . show
+
+		exts = mapMaybe flagExtension opts
+
+-- | File inspection data
+fileInspection :: FilePath -> [String] -> IO Inspection
+fileInspection f opts = do
+	tm <- Dir.getModificationTime f
+	return $ InspectionAt (utcTimeToPOSIXSeconds tm) $ sort $ ordNub opts
+
+-- | File contents inspection data
+fileContentsInspection :: FilePath -> [String] -> IO Inspection
+fileContentsInspection _ opts = do
+	tm <- getPOSIXTime
+	return $ InspectionAt tm $ sort $ ordNub opts
+
+-- | Enumerate project dirs
+projectDirs :: Project -> IO [Extensions FilePath]
+projectDirs p = do
+	p' <- loadProject p
+	return $ ordNub $ map (fmap (normalise . (view projectPath p' </>))) $ maybe [] sourceDirs $ view projectDescription p'
+
+-- | Enumerate project source files
+projectSources :: Project -> IO [Extensions FilePath]
+projectSources p = do
+	dirs <- projectDirs p
+	let
+		enumCabals = liftM (map takeDirectory . filter cabalFile) . traverseDirectory
+		dirs' = map (view entity) dirs
+	-- enum inner projects and dont consider them as part of this project
+	subProjs <- liftM (delete (view projectPath p) . ordNub . concat) $ triesMap (enumCabals) dirs'
+	let
+		enumHs = liftM (filter thisProjectSource) . traverseDirectory
+		thisProjectSource h = haskellSource h && not (any (`isParent` h) subProjs)
+	liftM (ordNub . concat) $ triesMap (liftM sequenceA . traverse (enumHs)) dirs
+
+-- | Inspect project
+inspectProject :: [(String, String)] -> [String] -> Project -> IO (Project, [InspectedModule])
+inspectProject defines opts p = hsdevLiftIO $ do
+	p' <- loadProject p
+	srcs <- projectSources p'
+	modules <- mapM inspectFile' srcs
+	return (p', catMaybes modules)
+	where
+		inspectFile' exts = liftM return (inspectFile defines (opts ++ extensionsOpts exts) (view entity exts) Nothing) <|> return Nothing
+
+-- | Get actual defines
+getDefines :: IO [(String, String)]
+getDefines = E.handle onIO $ do
+	tmp <- Dir.getTemporaryDirectory
+	writeFile (tmp </> "defines.hs") ""
+	_ <- runWait "ghc" ["-E", "-optP-dM", "-cpp", tmp </> "defines.hs"] ""
+	cts <- readFileUtf8 (tmp </> "defines.hspp")
+	Dir.removeFile (tmp </> "defines.hs")
+	Dir.removeFile (tmp </> "defines.hspp")
+	return $ mapMaybe (\g -> (,) <$> g 1 <*> g 2) $ mapMaybe (matchRx rx) $ lines cts
+	where
+		rx = "#define ([^\\s]+) (.*)"
+		onIO :: E.IOException -> IO [(String, String)]
+		onIO _ = return []
+
+preprocess :: [(String, String)] -> FilePath -> String -> IO String
+preprocess defines fpath cts = do
+	cts' <- E.catch (Cpphs.cppIfdef fpath defines [] Cpphs.defaultBoolOptions cts) onIOError
+	return $ unlines $ map snd cts'
+	where
+		onIOError :: E.IOException -> IO [(Cpphs.Posn, String)]
+		onIOError _ = return []
+
+preprocess_ :: [(String, String)] -> [String] -> FilePath -> String -> IO String
+preprocess_ defines exts fpath cts
+	| hasCPP = preprocess defines fpath cts
+	| otherwise = return cts
+	where
+		exts' = map H.parseExtension exts ++ maybe [] snd (H.readExtensions cts)
+		hasCPP = H.EnableExtension H.CPP `elem` exts'
diff --git a/src/HsDev/PackageDb.hs b/src/HsDev/PackageDb.hs
--- a/src/HsDev/PackageDb.hs
+++ b/src/HsDev/PackageDb.hs
@@ -1,106 +1,106 @@
-{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
-
-module HsDev.PackageDb (
-	PackageDb(..), packageDb,
-	PackageDbStack(..), packageDbStack, globalDb, userDb, fromPackageDb, fromPackageDbs,
-	topPackageDb, packageDbs, packageDbStacks,
-	isSubStack,
-
-	packageDbOpt, packageDbStackOpts
-	) where
-
-import Control.Applicative
-import Control.Monad (guard)
-import Control.Lens (makeLenses, each)
-import Control.DeepSeq (NFData(..))
-import Data.Aeson
-import Data.List (tails, isSuffixOf)
-
-import System.Directory.Paths
-import HsDev.Util ((.::))
-
-data PackageDb = GlobalDb | UserDb | PackageDb { _packageDb :: FilePath } deriving (Eq, Ord, Read, Show)
-
-makeLenses ''PackageDb
-
-instance NFData PackageDb where
-	rnf GlobalDb = ()
-	rnf UserDb = ()
-	rnf (PackageDb p) = rnf p
-
-instance ToJSON PackageDb where
-	toJSON GlobalDb = "global-db"
-	toJSON UserDb = "user-db"
-	toJSON (PackageDb p) = object ["package-db" .= p]
-
-instance FromJSON PackageDb where
-	parseJSON v = globalP v <|> userP v <|> dbP v where
-		globalP = withText "global-db" (\s -> guard (s == "global-db") >> return GlobalDb)
-		userP = withText "user-db" (\s -> guard (s == "user-db") >> return UserDb)
-		dbP = withObject "package-db" pathP where
-			pathP obj = PackageDb <$> obj .:: "package-db"
-
-instance Paths PackageDb where
-	paths _ GlobalDb = pure GlobalDb
-	paths _ UserDb = pure UserDb
-	paths f (PackageDb p) = PackageDb <$> f p
-
--- | Stack of PackageDb in reverse order
-newtype PackageDbStack = PackageDbStack { _packageDbStack :: [PackageDb] } deriving (Eq, Ord, Read, Show)
-
-makeLenses ''PackageDbStack
-
-instance NFData PackageDbStack where
-	rnf (PackageDbStack ps) = rnf ps
-
-instance ToJSON PackageDbStack where
-	toJSON (PackageDbStack ps) = toJSON ps
-
-instance FromJSON PackageDbStack where
-	parseJSON = fmap PackageDbStack . parseJSON
-
-instance Paths PackageDbStack where
-	paths f (PackageDbStack ps) = PackageDbStack <$> (each . paths) f ps
-
--- | Global db stack
-globalDb :: PackageDbStack
-globalDb = PackageDbStack []
-
--- | User db stack
-userDb :: PackageDbStack
-userDb = PackageDbStack [UserDb]
-
--- | Make package-db from one package-db
-fromPackageDb :: FilePath -> PackageDbStack
-fromPackageDb = PackageDbStack . return . PackageDb
-
--- | Make package-db stack from paths
-fromPackageDbs :: [FilePath] -> PackageDbStack
-fromPackageDbs = PackageDbStack . map PackageDb . reverse
-
--- | Get top package-db for package-db stack
-topPackageDb :: PackageDbStack -> PackageDb
-topPackageDb (PackageDbStack []) = GlobalDb
-topPackageDb (PackageDbStack (d:_)) = d
-
--- | Get list of package-db in stack, adds additional global-db at bottom
-packageDbs :: PackageDbStack -> [PackageDb]
-packageDbs = (GlobalDb :) . reverse . _packageDbStack
-
--- | Get stacks for each package-db in stack
-packageDbStacks :: PackageDbStack -> [PackageDbStack]
-packageDbStacks = map PackageDbStack . tails . _packageDbStack
-
--- | Is one package-db stack substack of another
-isSubStack :: PackageDbStack -> PackageDbStack -> Bool
-isSubStack (PackageDbStack l) (PackageDbStack r) = l `isSuffixOf` r
-
--- | Get ghc options for package-db
-packageDbOpt :: PackageDb -> String
-packageDbOpt GlobalDb = "-global-package-db"
-packageDbOpt UserDb = "-user-package-db"
-packageDbOpt (PackageDb p) = "-package-db " ++ p
-
--- | Get ghc options for package-db stack
-packageDbStackOpts :: PackageDbStack -> [String]
-packageDbStackOpts (PackageDbStack ps) = "-no-user-package-db" : map packageDbOpt (reverse ps)
+{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
+
+module HsDev.PackageDb (
+	PackageDb(..), packageDb,
+	PackageDbStack(..), packageDbStack, globalDb, userDb, fromPackageDb, fromPackageDbs,
+	topPackageDb, packageDbs, packageDbStacks,
+	isSubStack,
+
+	packageDbOpt, packageDbStackOpts
+	) where
+
+import Control.Applicative
+import Control.Monad (guard)
+import Control.Lens (makeLenses, each)
+import Control.DeepSeq (NFData(..))
+import Data.Aeson
+import Data.List (tails, isSuffixOf)
+
+import System.Directory.Paths
+import HsDev.Util ((.::))
+
+data PackageDb = GlobalDb | UserDb | PackageDb { _packageDb :: FilePath } deriving (Eq, Ord, Read, Show)
+
+makeLenses ''PackageDb
+
+instance NFData PackageDb where
+	rnf GlobalDb = ()
+	rnf UserDb = ()
+	rnf (PackageDb p) = rnf p
+
+instance ToJSON PackageDb where
+	toJSON GlobalDb = "global-db"
+	toJSON UserDb = "user-db"
+	toJSON (PackageDb p) = object ["package-db" .= p]
+
+instance FromJSON PackageDb where
+	parseJSON v = globalP v <|> userP v <|> dbP v where
+		globalP = withText "global-db" (\s -> guard (s == "global-db") >> return GlobalDb)
+		userP = withText "user-db" (\s -> guard (s == "user-db") >> return UserDb)
+		dbP = withObject "package-db" pathP where
+			pathP obj = PackageDb <$> obj .:: "package-db"
+
+instance Paths PackageDb where
+	paths _ GlobalDb = pure GlobalDb
+	paths _ UserDb = pure UserDb
+	paths f (PackageDb p) = PackageDb <$> f p
+
+-- | Stack of PackageDb in reverse order
+newtype PackageDbStack = PackageDbStack { _packageDbStack :: [PackageDb] } deriving (Eq, Ord, Read, Show)
+
+makeLenses ''PackageDbStack
+
+instance NFData PackageDbStack where
+	rnf (PackageDbStack ps) = rnf ps
+
+instance ToJSON PackageDbStack where
+	toJSON (PackageDbStack ps) = toJSON ps
+
+instance FromJSON PackageDbStack where
+	parseJSON = fmap PackageDbStack . parseJSON
+
+instance Paths PackageDbStack where
+	paths f (PackageDbStack ps) = PackageDbStack <$> (each . paths) f ps
+
+-- | Global db stack
+globalDb :: PackageDbStack
+globalDb = PackageDbStack []
+
+-- | User db stack
+userDb :: PackageDbStack
+userDb = PackageDbStack [UserDb]
+
+-- | Make package-db from one package-db
+fromPackageDb :: FilePath -> PackageDbStack
+fromPackageDb = PackageDbStack . return . PackageDb
+
+-- | Make package-db stack from paths
+fromPackageDbs :: [FilePath] -> PackageDbStack
+fromPackageDbs = PackageDbStack . map PackageDb . reverse
+
+-- | Get top package-db for package-db stack
+topPackageDb :: PackageDbStack -> PackageDb
+topPackageDb (PackageDbStack []) = GlobalDb
+topPackageDb (PackageDbStack (d:_)) = d
+
+-- | Get list of package-db in stack, adds additional global-db at bottom
+packageDbs :: PackageDbStack -> [PackageDb]
+packageDbs = (GlobalDb :) . reverse . _packageDbStack
+
+-- | Get stacks for each package-db in stack
+packageDbStacks :: PackageDbStack -> [PackageDbStack]
+packageDbStacks = map PackageDbStack . tails . _packageDbStack
+
+-- | Is one package-db stack substack of another
+isSubStack :: PackageDbStack -> PackageDbStack -> Bool
+isSubStack (PackageDbStack l) (PackageDbStack r) = l `isSuffixOf` r
+
+-- | Get ghc options for package-db
+packageDbOpt :: PackageDb -> String
+packageDbOpt GlobalDb = "-global-package-db"
+packageDbOpt UserDb = "-user-package-db"
+packageDbOpt (PackageDb p) = "-package-db " ++ p
+
+-- | Get ghc options for package-db stack
+packageDbStackOpts :: PackageDbStack -> [String]
+packageDbStackOpts (PackageDbStack ps) = "-no-user-package-db" : map packageDbOpt (reverse ps)
diff --git a/src/HsDev/Project.hs b/src/HsDev/Project.hs
--- a/src/HsDev/Project.hs
+++ b/src/HsDev/Project.hs
@@ -1,164 +1,167 @@
-module HsDev.Project (
-	module HsDev.Project.Types,
-
-	infoSourceDirsDef,
-	analyzeCabal,
-	readProject, loadProject,
-	withExtensions,
-	infos, inTarget, fileTarget, fileTargets, findSourceDir, sourceDirs,
-	targetOpts,
-
-	-- * Helpers
-	showExtension, flagExtension, extensionFlag,
-	extensionsOpts
-	) where
-
-import Control.Arrow
-import Control.Lens (Simple, Lens, view, lens)
-import Control.Monad.Except
-import Data.List
-import Data.Maybe
-import Data.Version (showVersion)
-import Distribution.Compiler (CompilerFlavor(GHC))
-import qualified Distribution.Package as P
-import qualified Distribution.PackageDescription as PD
-import Distribution.PackageDescription.Parse
-import Distribution.ModuleName (components)
-import Distribution.Text (display)
-import Language.Haskell.Extension
-import System.FilePath
-
-import HsDev.Project.Types
-import HsDev.Error
-import HsDev.Util
-
--- | infoSourceDirs lens with default
-infoSourceDirsDef :: Simple Lens Info [FilePath]
-infoSourceDirsDef = lens get' set' where
-	get' i = case _infoSourceDirs i of
-		[] -> ["."]
-		dirs -> dirs
-	set' i ["."] = i { _infoSourceDirs = [] }
-	set' i dirs = i { _infoSourceDirs = dirs }
-
--- | Analyze cabal file
-analyzeCabal :: String -> Either String ProjectDescription
-analyzeCabal source = case liftM flattenDescr $ parsePackageDescription source of
-	ParseOk _ r -> Right ProjectDescription {
-		_projectVersion = showVersion $ P.pkgVersion $ PD.package r,
-		_projectLibrary = fmap toLibrary $ PD.library r,
-		_projectExecutables = fmap toExecutable $ PD.executables r,
-		_projectTests = fmap toTest $ PD.testSuites r }
-	ParseFailed e -> Left $ "Parse failed: " ++ show e
-	where
-		toLibrary (PD.Library exposeds _ _ _ _ info) = Library (map components exposeds) (toInfo info)
-		toExecutable (PD.Executable name path info) = Executable name path (toInfo info)
-		toTest (PD.TestSuite name _ info enabled) = Test name enabled (toInfo info)
-		toInfo info = Info {
-			_infoDepends = map pkgName (PD.targetBuildDepends info),
-			_infoLanguage = PD.defaultLanguage info,
-			_infoExtensions = PD.defaultExtensions info ++ PD.otherExtensions info ++ PD.oldExtensions info,
-			_infoGHCOptions = fromMaybe [] $ lookup GHC (PD.options info),
-			_infoSourceDirs = PD.hsSourceDirs info }
-
-		pkgName :: P.Dependency -> String
-		pkgName (P.Dependency (P.PackageName s) _) = s
-
-		flattenDescr :: PD.GenericPackageDescription -> PD.PackageDescription
-		flattenDescr (PD.GenericPackageDescription pkg _ mlib mexes mtests _) = pkg {
-			PD.library = flip fmap mlib $ flattenTree
-				(insertInfo PD.libBuildInfo (\i l -> l { PD.libBuildInfo = i })),
-			PD.executables = flip fmap mexes $
-				second (flattenTree (insertInfo PD.buildInfo (\i l -> l { PD.buildInfo = i }))) >>>
-				(\(n, e) -> e { PD.exeName = n }),
-			PD.testSuites = flip fmap mtests $
-				second (flattenTree (insertInfo PD.testBuildInfo (\i l -> l { PD.testBuildInfo = i }))) >>>
-				(\(n, t) -> t { PD.testName = n }) }
-			where
-				insertInfo :: (a -> PD.BuildInfo) -> (PD.BuildInfo -> a -> a) -> [P.Dependency] -> a -> a
-				insertInfo f s deps' x = s ((f x) { PD.targetBuildDepends = deps' }) x
-
-		flattenTree :: Monoid a => (c -> a -> a) -> PD.CondTree v c a -> a
-		flattenTree f (PD.CondNode x cs cmps) = f cs x `mappend` mconcat (concatMap flattenBranch cmps) where
-			flattenBranch (_, t, mb) = flattenTree f t : map (flattenTree f) (maybeToList mb)
-
--- | Read project info from .cabal
-readProject :: FilePath -> IO Project
-readProject file = do
-	source <- readFile file
-	length source `seq` either (hsdevError . InspectCabalError file) (return . mkProject) $ analyzeCabal source
-	where
-		mkProject desc = (project file) {
-			_projectDescription = Just desc }
-
--- | Load project description
-loadProject :: Project -> IO Project
-loadProject p
-	| isJust (_projectDescription p) = return p
-	| otherwise = readProject (_projectCabal p)
-
--- | Extensions for target
-withExtensions :: a -> Info -> Extensions a
-withExtensions x i = Extensions {
-	_extensions = _infoExtensions i,
-	_ghcOptions = _infoGHCOptions i,
-	_entity = x }
-
--- | Returns build targets infos
-infos :: ProjectDescription -> [Info]
-infos p =
-	maybe [] (return . _libraryBuildInfo) (_projectLibrary p) ++
-	map _executableBuildInfo (_projectExecutables p) ++
-	map _testBuildInfo (_projectTests p)
-
--- | Check if source related to target, source must be relative to project directory
-inTarget :: FilePath -> Info -> Bool
-inTarget src info = any (`isParent` src) $ view infoSourceDirsDef info
-
--- | Get first target for source file
-fileTarget :: Project -> FilePath -> Maybe Info
-fileTarget p f = listToMaybe $ fileTargets p f
-
--- | Get possible targets for source file
--- There can be many candidates in case of module related to several executables or tests
-fileTargets :: Project -> FilePath -> [Info]
-fileTargets p f = case filter ((`isSuffixOf` f') . normalise . _executablePath) exes of
-	[] -> filter (f' `inTarget`) $ maybe [] infos $ _projectDescription p
-	exes' -> map _executableBuildInfo exes'
-	where
-		f' = makeRelative (_projectPath p) f
-		exes = maybe [] _projectExecutables $ _projectDescription p
-
--- | Finds source dir file belongs to
-findSourceDir :: Project -> FilePath -> Maybe (Extensions FilePath)
-findSourceDir p f = do
-	info <- listToMaybe $ fileTargets p f
-	fmap (`withExtensions` info) $ listToMaybe $ filter (`isParent` f) $ map (_projectPath p </>) $ view infoSourceDirsDef info
-
--- | Returns source dirs for library, executables and tests
-sourceDirs :: ProjectDescription -> [Extensions FilePath]
-sourceDirs = ordNub . concatMap dirs . infos where
-	dirs i = map (`withExtensions` i) $ view infoSourceDirsDef i
-
--- | Get options for specific target
-targetOpts :: Info -> [String]
-targetOpts info' = concat [
-	["-i" ++ s | s <- _infoSourceDirs info'],
-	extensionsOpts $ withExtensions () info',
-	["-package " ++ p | p <- _infoDepends info']]
-
--- | Extension as flag name
-showExtension :: Extension -> String
-showExtension = display
-
--- | Convert -Xext to ext
-flagExtension :: String -> Maybe String
-flagExtension = stripPrefix "-X"
-
--- | Convert ext to -Xext
-extensionFlag :: String -> String
-extensionFlag = ("-X" ++)
-
--- | Extensions as opts to GHC
-extensionsOpts :: Extensions a -> [String]
-extensionsOpts e = map (extensionFlag . showExtension) (_extensions e) ++ _ghcOptions e
+{-# LANGUAGE CPP #-}
+
+module HsDev.Project (
+	module HsDev.Project.Types,
+
+	infoSourceDirsDef,
+	analyzeCabal,
+	readProject, loadProject,
+	withExtensions,
+	infos, inTarget, fileTarget, fileTargets, findSourceDir, sourceDirs,
+	targetOpts,
+
+	-- * Helpers
+	showExtension, flagExtension, extensionFlag,
+	extensionsOpts
+	) where
+
+import Control.Arrow
+import Control.Lens (Simple, Lens, view, lens)
+import Control.Monad.Except
+import Data.List
+import Data.Maybe
+import Distribution.Compiler (CompilerFlavor(GHC))
+import qualified Distribution.Package as P
+import qualified Distribution.PackageDescription as PD
+import Distribution.PackageDescription.Parse
+import Distribution.ModuleName (components)
+import Distribution.Text (display)
+import Language.Haskell.Extension
+import System.FilePath
+
+import HsDev.Project.Compat
+import HsDev.Project.Types
+import HsDev.Error
+import HsDev.Util
+
+-- | infoSourceDirs lens with default
+infoSourceDirsDef :: Simple Lens Info [FilePath]
+infoSourceDirsDef = lens get' set' where
+	get' i = case _infoSourceDirs i of
+		[] -> ["."]
+		dirs -> dirs
+	set' i ["."] = i { _infoSourceDirs = [] }
+	set' i dirs = i { _infoSourceDirs = dirs }
+
+-- | Analyze cabal file
+analyzeCabal :: String -> Either String ProjectDescription
+analyzeCabal source = case liftM flattenDescr $ parsePackageDesc source of
+	ParseOk _ r -> Right ProjectDescription {
+		_projectVersion = showVer $ P.pkgVersion $ PD.package r,
+		_projectLibrary = fmap toLibrary $ PD.library r,
+		_projectExecutables = fmap toExecutable $ PD.executables r,
+		_projectTests = fmap toTest $ PD.testSuites r }
+	ParseFailed e -> Left $ "Parse failed: " ++ show e
+	where
+		toLibrary lib = Library (map components $ PD.exposedModules lib) (toInfo $ PD.libBuildInfo lib)
+		toExecutable exe = Executable (componentName $ PD.exeName exe) (PD.modulePath exe) (toInfo $ PD.buildInfo exe)
+		toTest test = Test (componentName $ PD.testName test) (testSuiteEnabled test) (toInfo $ PD.testBuildInfo test)
+		toInfo info = Info {
+			_infoDepends = map pkgName (PD.targetBuildDepends info),
+			_infoLanguage = PD.defaultLanguage info,
+			_infoExtensions = PD.defaultExtensions info ++ PD.otherExtensions info ++ PD.oldExtensions info,
+			_infoGHCOptions = fromMaybe [] $ lookup GHC (PD.options info),
+			_infoSourceDirs = PD.hsSourceDirs info }
+
+		pkgName :: P.Dependency -> String
+		pkgName (P.Dependency dep _) = P.unPackageName dep
+
+		flattenDescr :: PD.GenericPackageDescription -> PD.PackageDescription
+		flattenDescr gpkg = pkg {
+			PD.library = flip fmap mlib $ flattenCondTree
+				(insertInfo PD.libBuildInfo (\i l -> l { PD.libBuildInfo = i })),
+			PD.executables = flip fmap mexes $
+				second (flattenCondTree (insertInfo PD.buildInfo (\i l -> l { PD.buildInfo = i }))) >>>
+				(\(n, e) -> e { PD.exeName = n }),
+			PD.testSuites = flip fmap mtests $
+				second (flattenCondTree (insertInfo PD.testBuildInfo (\i l -> l { PD.testBuildInfo = i }))) >>>
+				(\(n, t) -> t { PD.testName = n }) }
+			where
+				pkg = PD.packageDescription gpkg
+				mlib = PD.condLibrary gpkg
+				mexes = PD.condExecutables gpkg
+				mtests = PD.condTestSuites gpkg
+
+				insertInfo :: (a -> PD.BuildInfo) -> (PD.BuildInfo -> a -> a) -> [P.Dependency] -> a -> a
+				insertInfo f s deps' x = s ((f x) { PD.targetBuildDepends = deps' }) x
+
+-- | Read project info from .cabal
+readProject :: FilePath -> IO Project
+readProject file = do
+	source <- readFile file
+	length source `seq` either (hsdevError . InspectCabalError file) (return . mkProject) $ analyzeCabal source
+	where
+		mkProject desc = (project file) {
+			_projectDescription = Just desc }
+
+-- | Load project description
+loadProject :: Project -> IO Project
+loadProject p
+	| isJust (_projectDescription p) = return p
+	| otherwise = readProject (_projectCabal p)
+
+-- | Extensions for target
+withExtensions :: a -> Info -> Extensions a
+withExtensions x i = Extensions {
+	_extensions = _infoExtensions i,
+	_ghcOptions = _infoGHCOptions i,
+	_entity = x }
+
+-- | Returns build targets infos
+infos :: ProjectDescription -> [Info]
+infos p =
+	maybe [] (return . _libraryBuildInfo) (_projectLibrary p) ++
+	map _executableBuildInfo (_projectExecutables p) ++
+	map _testBuildInfo (_projectTests p)
+
+-- | Check if source related to target, source must be relative to project directory
+inTarget :: FilePath -> Info -> Bool
+inTarget src info = any (`isParent` src) $ view infoSourceDirsDef info
+
+-- | Get first target for source file
+fileTarget :: Project -> FilePath -> Maybe Info
+fileTarget p f = listToMaybe $ fileTargets p f
+
+-- | Get possible targets for source file
+-- There can be many candidates in case of module related to several executables or tests
+fileTargets :: Project -> FilePath -> [Info]
+fileTargets p f = case filter ((`isSuffixOf` f') . normalise . _executablePath) exes of
+	[] -> filter (f' `inTarget`) $ maybe [] infos $ _projectDescription p
+	exes' -> map _executableBuildInfo exes'
+	where
+		f' = makeRelative (_projectPath p) f
+		exes = maybe [] _projectExecutables $ _projectDescription p
+
+-- | Finds source dir file belongs to
+findSourceDir :: Project -> FilePath -> Maybe (Extensions FilePath)
+findSourceDir p f = do
+	info <- listToMaybe $ fileTargets p f
+	fmap (`withExtensions` info) $ listToMaybe $ filter (`isParent` f) $ map (_projectPath p </>) $ view infoSourceDirsDef info
+
+-- | Returns source dirs for library, executables and tests
+sourceDirs :: ProjectDescription -> [Extensions FilePath]
+sourceDirs = ordNub . concatMap dirs . infos where
+	dirs i = map (`withExtensions` i) $ view infoSourceDirsDef i
+
+-- | Get options for specific target
+targetOpts :: Info -> [String]
+targetOpts info' = concat [
+	["-i" ++ s | s <- _infoSourceDirs info'],
+	extensionsOpts $ withExtensions () info',
+	["-package " ++ p | p <- _infoDepends info']]
+
+-- | Extension as flag name
+showExtension :: Extension -> String
+showExtension = display
+
+-- | Convert -Xext to ext
+flagExtension :: String -> Maybe String
+flagExtension = stripPrefix "-X"
+
+-- | Convert ext to -Xext
+extensionFlag :: String -> String
+extensionFlag = ("-X" ++)
+
+-- | Extensions as opts to GHC
+extensionsOpts :: Extensions a -> [String]
+extensionsOpts e = map (extensionFlag . showExtension) (_extensions e) ++ _ghcOptions e
diff --git a/src/HsDev/Project/Compat.hs b/src/HsDev/Project/Compat.hs
new file mode 100644
--- /dev/null
+++ b/src/HsDev/Project/Compat.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE CPP #-}
+
+module HsDev.Project.Compat (
+	showVer, componentName, testSuiteEnabled,
+	flattenCondTree,
+	parsePackageDesc
+	) where
+
+import Data.Maybe (maybeToList)
+import qualified Distribution.PackageDescription as PD
+import Distribution.PackageDescription.Parse
+import Distribution.Version (Version)
+import Distribution.Text (display)
+#if MIN_VERSION_Cabal(2,0,0)
+import Distribution.Types.CondTree
+#else 
+import Distribution.PackageDescription (CondTree(..))
+#endif
+
+#if MIN_VERSION_Cabal(2,0,0)
+import Distribution.Types.UnqualComponentName
+#else
+import Data.Version (showVersion)
+#endif
+
+showVer :: Version -> String
+#if MIN_VERSION_Cabal(2,0,0)
+showVer = display
+#else
+showVer = showVersion
+#endif
+
+#if MIN_VERSION_Cabal(2,0,0)
+componentName :: UnqualComponentName -> String
+componentName = unUnqualComponentName
+#else
+componentName :: String -> String
+componentName = id
+#endif
+
+testSuiteEnabled :: PD.TestSuite -> Bool
+#if MIN_VERSION_Cabal(2,0,0)
+testSuiteEnabled _ = True
+#else
+testSuiteEnabled = PD.testEnabled
+#endif
+
+flattenCondTree :: Monoid a => (c -> a -> a) -> CondTree v c a -> a
+flattenCondTree f (PD.CondNode x cs cmps) = f cs x `mappend` mconcat (concatMap flattenBranch cmps) where
+#if MIN_VERSION_Cabal(2,0,0)
+	flattenBranch (CondBranch _ t mb) = go t mb
+#else
+	flattenBranch (_, t, mb) = go t mb
+#endif
+	go t mb = flattenCondTree f t : map (flattenCondTree f) (maybeToList mb)
+
+parsePackageDesc :: String -> ParseResult PD.GenericPackageDescription
+#if MIN_VERSION_Cabal(2,0,0)
+parsePackageDesc = parseGenericPackageDescription
+#else
+parsePackageDesc = parsePackageDescription
+#endif
diff --git a/src/HsDev/Project/Types.hs b/src/HsDev/Project/Types.hs
--- a/src/HsDev/Project/Types.hs
+++ b/src/HsDev/Project/Types.hs
@@ -1,298 +1,298 @@
-{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
-
-module HsDev.Project.Types (
-	Project(..), projectName, projectPath, projectCabal, projectDescription, project, absolutiseProjectPaths, relativiseProjectPaths,
-	ProjectDescription(..), projectVersion, projectLibrary, projectExecutables, projectTests,
-	Target(..),
-	Library(..), libraryModules, libraryBuildInfo,
-	Executable(..), executableName, executablePath, executableBuildInfo,
-	Test(..), testName, testEnabled, testBuildInfo,
-	Info(..), infoDepends, infoLanguage, infoExtensions, infoGHCOptions, infoSourceDirs,
-	Extensions(..), extensions, ghcOptions, entity,
-	) where
-
-import Control.Arrow
-import Control.DeepSeq (NFData(..))
-import Control.Lens (makeLenses)
-import Data.Aeson
-import Data.Aeson.Types (Parser)
-import Data.List
-import Data.Maybe
-import Data.Monoid
-import Data.Ord
-import Distribution.Text (display, simpleParse)
-import qualified Distribution.Text (Text)
-import Language.Haskell.Extension
-import Text.Format
-import System.FilePath
-
-import System.Directory.Paths
-import HsDev.Util
-
--- | Cabal project
-data Project = Project {
-	_projectName :: String,
-	_projectPath :: FilePath,
-	_projectCabal :: FilePath,
-	_projectDescription :: Maybe ProjectDescription }
-		deriving (Read)
-
-instance NFData Project where
-	rnf (Project n p c _) = rnf n `seq` rnf p `seq` rnf c
-
-instance Eq Project where
-	l == r = _projectCabal l == _projectCabal r
-
-instance Ord Project where
-	compare l r = compare (_projectName l, _projectCabal l) (_projectName r, _projectCabal r)
-
-instance Show Project where
-	show p = unlines $ [
-		"project " ++ _projectName p,
-		"\tcabal: " ++ _projectCabal p,
-		"\tdescription:"] ++ concatMap (map (tab 2) . lines . show) (maybeToList $ _projectDescription p)
-
-instance ToJSON Project where
-	toJSON p = object [
-		"name" .= _projectName p,
-		"path" .= _projectPath p,
-		"cabal" .= _projectCabal p,
-		"description" .= _projectDescription p]
-
-instance FromJSON Project where
-	parseJSON = withObject "project" $ \v -> Project <$>
-		v .:: "name" <*>
-		v .:: "path" <*>
-		v .:: "cabal" <*>
-		v .:: "description"
-
-instance Paths Project where
-	paths f (Project nm p c desc) = Project nm <$> f p <*> f c <*> traverse (paths f) desc
-
--- | Make project by .cabal file
-project :: FilePath -> Project
-project file = Project {
-	_projectName = takeBaseName (takeDirectory cabal),
-	_projectPath = takeDirectory cabal,
-	_projectCabal = cabal,
-	_projectDescription = Nothing }
-	where
-		file' = dropTrailingPathSeparator $ normalise file
-		cabal
-			| takeExtension file' == ".cabal" = file'
-			| otherwise = file' </> (takeBaseName file' <.> "cabal")
-
--- | Make paths absolute, not relative
-absolutiseProjectPaths :: Project -> Project
-absolutiseProjectPaths proj = absolutise (_projectPath proj) proj
-
--- | Make paths relative
-relativiseProjectPaths :: Project -> Project
-relativiseProjectPaths proj = relativise (_projectPath proj) proj
-
-data ProjectDescription = ProjectDescription {
-	_projectVersion :: String,
-	_projectLibrary :: Maybe Library,
-	_projectExecutables :: [Executable],
-	_projectTests :: [Test] }
-		deriving (Eq, Read)
-
-instance Show ProjectDescription where
-	show pd = unlines $
-		concatMap (lines . show) (maybeToList (_projectLibrary pd)) ++
-		concatMap (lines . show) (_projectExecutables pd) ++
-		concatMap (lines . show) (_projectTests pd)
-
-instance ToJSON ProjectDescription where
-	toJSON d = object [
-		"version" .= _projectVersion d,
-		"library" .= _projectLibrary d,
-		"executables" .= _projectExecutables d,
-		"tests" .= _projectTests d]
-
-instance FromJSON ProjectDescription where
-	parseJSON = withObject "project description" $ \v -> ProjectDescription <$>
-		v .:: "version" <*>
-		v .:: "library" <*>
-		v .:: "executables" <*>
-		v .:: "tests"
-
-instance Paths ProjectDescription where
-	paths f (ProjectDescription v lib exes tests) = ProjectDescription v <$> traverse (paths f) lib <*> traverse (paths f) exes <*> traverse (paths f) tests
-
-class Target a where
-	buildInfo :: a -> Info
-
--- | Library in project
-data Library = Library {
-	_libraryModules :: [[String]],
-	_libraryBuildInfo :: Info }
-		deriving (Eq, Read)
-
-instance Target Library where
-	buildInfo = _libraryBuildInfo
-
-instance Show Library where
-	show l = unlines $
-		["library", "\tmodules:"] ++
-		(map (tab 2 . intercalate ".") $ _libraryModules l) ++
-		(map (tab 1) . lines . show $ _libraryBuildInfo l)
-
-instance ToJSON Library where
-	toJSON l = object [
-		"modules" .= fmap (intercalate ".") (_libraryModules l),
-		"info" .= _libraryBuildInfo l]
-
-instance FromJSON Library where
-	parseJSON = withObject "library" $ \v -> Library <$> (fmap splitModule <$> v .:: "modules") <*> v .:: "info" where
-		splitModule :: String -> [String]
-		splitModule = takeWhile (not . null) . unfoldr (Just . second (drop 1) . break (== '.'))
-
-instance Paths Library where
-	paths f (Library ms info) = Library ms <$> paths f info
-
--- | Executable
-data Executable = Executable {
-	_executableName :: String,
-	_executablePath :: FilePath,
-	_executableBuildInfo :: Info }
-		deriving (Eq, Read)
-
-instance Target Executable where
-	buildInfo = _executableBuildInfo
-
-instance Show Executable where
-	show e = unlines $
-		["executable " ++ _executableName e, "\tpath: " ++ _executablePath e] ++
-		(map (tab 1) . lines . show $ _executableBuildInfo e)
-
-instance ToJSON Executable where
-	toJSON e = object [
-		"name" .= _executableName e,
-		"path" .= _executablePath e,
-		"info" .= _executableBuildInfo e]
-
-instance FromJSON Executable where
-	parseJSON = withObject "executable" $ \v -> Executable <$>
-		v .:: "name" <*>
-		v .:: "path" <*>
-		v .:: "info"
-
-instance Paths Executable where
-	paths f (Executable n p info) = Executable n <$> f p <*> paths f info
-
--- | Test
-data Test = Test {
-	_testName :: String,
-	_testEnabled :: Bool,
-	_testBuildInfo :: Info }
-		deriving (Eq, Read)
-
-instance Target Test where
-	buildInfo = _testBuildInfo
-
-instance Show Test where
-	show t = unlines $
-		["test " ++ _testName t, "\tenabled: " ++ show (_testEnabled t)] ++
-		(map (tab 1) . lines . show $ _testBuildInfo t)
-
-instance ToJSON Test where
-	toJSON t = object [
-		"name" .= _testName t,
-		"enabled" .= _testEnabled t,
-		"info" .= _testBuildInfo t]
-
-instance FromJSON Test where
-	parseJSON = withObject "test" $ \v -> Test <$>
-		v .:: "name" <*>
-		v .:: "enabled" <*>
-		v .:: "info"
-
-instance Paths Test where
-	paths f (Test n e info) = Test n e <$> paths f info
-
--- | Build info
-data Info = Info {
-	_infoDepends :: [String],
-	_infoLanguage :: Maybe Language,
-	_infoExtensions :: [Extension],
-	_infoGHCOptions :: [String],
-	_infoSourceDirs :: [FilePath] }
-		deriving (Eq, Read)
-
-instance Monoid Info where
-	mempty = Info [] Nothing [] [] []
-	mappend l r = Info
-		(ordNub $ _infoDepends l ++ _infoDepends r)
-		(getFirst $ First (_infoLanguage l) `mappend` First (_infoLanguage r))
-		(_infoExtensions l ++ _infoExtensions r)
-		(_infoGHCOptions l ++ _infoGHCOptions r)
-		(ordNub $ _infoSourceDirs l ++ _infoSourceDirs r)
-
-instance Ord Info where
-	compare l r = compare (_infoSourceDirs l, _infoDepends l, _infoGHCOptions l) (_infoSourceDirs r, _infoDepends r, _infoGHCOptions r)
-
-instance Show Info where
-	show i = unlines $ lang ++ exts ++ opts ++ sources where
-		lang = maybe [] (\l -> ["default-language: " ++ display l]) $ _infoLanguage i
-		exts
-			| null (_infoExtensions i) = []
-			| otherwise = "extensions:" : map (tab 1 . display) (_infoExtensions i)
-		opts
-			| null (_infoGHCOptions i) = []
-			| otherwise = "ghc-options:" : map (tab 1) (_infoGHCOptions i)
-		sources = "source-dirs:" : (map (tab 1) $ _infoSourceDirs i)
-
-instance ToJSON Info where
-	toJSON i = object [
-		"build-depends" .= _infoDepends i,
-		"language" .= fmap display (_infoLanguage i),
-		"extensions" .= map display (_infoExtensions i),
-		"ghc-options" .= _infoGHCOptions i,
-		"source-dirs" .= _infoSourceDirs i]
-
-instance FromJSON Info where
-	parseJSON = withObject "info" $ \v -> Info <$>
-		v .: "build-depends" <*>
-		((v .:: "language") >>= traverse (parseDT "Language")) <*>
-		((v .:: "extensions") >>= traverse (parseDT "Extension")) <*>
-		v .:: "ghc-options" <*>
-		v .:: "source-dirs"
-		where
-			parseDT :: Distribution.Text.Text a => String -> String -> Parser a
-			parseDT typeName v = maybe err return (simpleParse v) where
-				err = fail $ "Can't parse {}: {}" ~~ typeName ~~ v
-
-instance Paths Info where
-	paths f (Info deps lang exts opts dirs) = Info deps lang exts opts <$> traverse f dirs
-
--- | Entity with project extensions
-data Extensions a = Extensions {
-	_extensions :: [Extension],
-	_ghcOptions :: [String],
-	_entity :: a }
-		deriving (Eq, Read, Show)
-
-instance Ord a => Ord (Extensions a) where
-	compare = comparing _entity
-
-instance Functor Extensions where
-	fmap f (Extensions e o x) = Extensions e o (f x)
-
-instance Applicative Extensions where
-	pure = Extensions [] []
-	(Extensions l lo f) <*> (Extensions r ro x) = Extensions (ordNub $ l ++ r) (ordNub $ lo ++ ro) (f x)
-
-instance Foldable Extensions where
-	foldMap f (Extensions _ _ x) = f x
-
-instance Traversable Extensions where
-	traverse f (Extensions e o x) = Extensions e o <$> f x
-
-makeLenses ''Project
-makeLenses ''ProjectDescription
-makeLenses ''Library
-makeLenses ''Executable
-makeLenses ''Test
-makeLenses ''Info
-makeLenses ''Extensions
+{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
+
+module HsDev.Project.Types (
+	Project(..), projectName, projectPath, projectCabal, projectDescription, project, absolutiseProjectPaths, relativiseProjectPaths,
+	ProjectDescription(..), projectVersion, projectLibrary, projectExecutables, projectTests,
+	Target(..),
+	Library(..), libraryModules, libraryBuildInfo,
+	Executable(..), executableName, executablePath, executableBuildInfo,
+	Test(..), testName, testEnabled, testBuildInfo,
+	Info(..), infoDepends, infoLanguage, infoExtensions, infoGHCOptions, infoSourceDirs,
+	Extensions(..), extensions, ghcOptions, entity,
+	) where
+
+import Control.Arrow
+import Control.DeepSeq (NFData(..))
+import Control.Lens (makeLenses)
+import Data.Aeson
+import Data.Aeson.Types (Parser)
+import Data.List
+import Data.Maybe
+import Data.Monoid
+import Data.Ord
+import Distribution.Text (display, simpleParse)
+import qualified Distribution.Text (Text)
+import Language.Haskell.Extension
+import Text.Format
+import System.FilePath
+
+import System.Directory.Paths
+import HsDev.Util
+
+-- | Cabal project
+data Project = Project {
+	_projectName :: String,
+	_projectPath :: FilePath,
+	_projectCabal :: FilePath,
+	_projectDescription :: Maybe ProjectDescription }
+		deriving (Read)
+
+instance NFData Project where
+	rnf (Project n p c _) = rnf n `seq` rnf p `seq` rnf c
+
+instance Eq Project where
+	l == r = _projectCabal l == _projectCabal r
+
+instance Ord Project where
+	compare l r = compare (_projectName l, _projectCabal l) (_projectName r, _projectCabal r)
+
+instance Show Project where
+	show p = unlines $ [
+		"project " ++ _projectName p,
+		"\tcabal: " ++ _projectCabal p,
+		"\tdescription:"] ++ concatMap (map (tab 2) . lines . show) (maybeToList $ _projectDescription p)
+
+instance ToJSON Project where
+	toJSON p = object [
+		"name" .= _projectName p,
+		"path" .= _projectPath p,
+		"cabal" .= _projectCabal p,
+		"description" .= _projectDescription p]
+
+instance FromJSON Project where
+	parseJSON = withObject "project" $ \v -> Project <$>
+		v .:: "name" <*>
+		v .:: "path" <*>
+		v .:: "cabal" <*>
+		v .:: "description"
+
+instance Paths Project where
+	paths f (Project nm p c desc) = Project nm <$> f p <*> f c <*> traverse (paths f) desc
+
+-- | Make project by .cabal file
+project :: FilePath -> Project
+project file = Project {
+	_projectName = takeBaseName (takeDirectory cabal),
+	_projectPath = takeDirectory cabal,
+	_projectCabal = cabal,
+	_projectDescription = Nothing }
+	where
+		file' = dropTrailingPathSeparator $ normalise file
+		cabal
+			| takeExtension file' == ".cabal" = file'
+			| otherwise = file' </> (takeBaseName file' <.> "cabal")
+
+-- | Make paths absolute, not relative
+absolutiseProjectPaths :: Project -> Project
+absolutiseProjectPaths proj = absolutise (_projectPath proj) proj
+
+-- | Make paths relative
+relativiseProjectPaths :: Project -> Project
+relativiseProjectPaths proj = relativise (_projectPath proj) proj
+
+data ProjectDescription = ProjectDescription {
+	_projectVersion :: String,
+	_projectLibrary :: Maybe Library,
+	_projectExecutables :: [Executable],
+	_projectTests :: [Test] }
+		deriving (Eq, Read)
+
+instance Show ProjectDescription where
+	show pd = unlines $
+		concatMap (lines . show) (maybeToList (_projectLibrary pd)) ++
+		concatMap (lines . show) (_projectExecutables pd) ++
+		concatMap (lines . show) (_projectTests pd)
+
+instance ToJSON ProjectDescription where
+	toJSON d = object [
+		"version" .= _projectVersion d,
+		"library" .= _projectLibrary d,
+		"executables" .= _projectExecutables d,
+		"tests" .= _projectTests d]
+
+instance FromJSON ProjectDescription where
+	parseJSON = withObject "project description" $ \v -> ProjectDescription <$>
+		v .:: "version" <*>
+		v .:: "library" <*>
+		v .:: "executables" <*>
+		v .:: "tests"
+
+instance Paths ProjectDescription where
+	paths f (ProjectDescription v lib exes tests) = ProjectDescription v <$> traverse (paths f) lib <*> traverse (paths f) exes <*> traverse (paths f) tests
+
+class Target a where
+	buildInfo :: a -> Info
+
+-- | Library in project
+data Library = Library {
+	_libraryModules :: [[String]],
+	_libraryBuildInfo :: Info }
+		deriving (Eq, Read)
+
+instance Target Library where
+	buildInfo = _libraryBuildInfo
+
+instance Show Library where
+	show l = unlines $
+		["library", "\tmodules:"] ++
+		(map (tab 2 . intercalate ".") $ _libraryModules l) ++
+		(map (tab 1) . lines . show $ _libraryBuildInfo l)
+
+instance ToJSON Library where
+	toJSON l = object [
+		"modules" .= fmap (intercalate ".") (_libraryModules l),
+		"info" .= _libraryBuildInfo l]
+
+instance FromJSON Library where
+	parseJSON = withObject "library" $ \v -> Library <$> (fmap splitModule <$> v .:: "modules") <*> v .:: "info" where
+		splitModule :: String -> [String]
+		splitModule = takeWhile (not . null) . unfoldr (Just . second (drop 1) . break (== '.'))
+
+instance Paths Library where
+	paths f (Library ms info) = Library ms <$> paths f info
+
+-- | Executable
+data Executable = Executable {
+	_executableName :: String,
+	_executablePath :: FilePath,
+	_executableBuildInfo :: Info }
+		deriving (Eq, Read)
+
+instance Target Executable where
+	buildInfo = _executableBuildInfo
+
+instance Show Executable where
+	show e = unlines $
+		["executable " ++ _executableName e, "\tpath: " ++ _executablePath e] ++
+		(map (tab 1) . lines . show $ _executableBuildInfo e)
+
+instance ToJSON Executable where
+	toJSON e = object [
+		"name" .= _executableName e,
+		"path" .= _executablePath e,
+		"info" .= _executableBuildInfo e]
+
+instance FromJSON Executable where
+	parseJSON = withObject "executable" $ \v -> Executable <$>
+		v .:: "name" <*>
+		v .:: "path" <*>
+		v .:: "info"
+
+instance Paths Executable where
+	paths f (Executable n p info) = Executable n <$> f p <*> paths f info
+
+-- | Test
+data Test = Test {
+	_testName :: String,
+	_testEnabled :: Bool,
+	_testBuildInfo :: Info }
+		deriving (Eq, Read)
+
+instance Target Test where
+	buildInfo = _testBuildInfo
+
+instance Show Test where
+	show t = unlines $
+		["test " ++ _testName t, "\tenabled: " ++ show (_testEnabled t)] ++
+		(map (tab 1) . lines . show $ _testBuildInfo t)
+
+instance ToJSON Test where
+	toJSON t = object [
+		"name" .= _testName t,
+		"enabled" .= _testEnabled t,
+		"info" .= _testBuildInfo t]
+
+instance FromJSON Test where
+	parseJSON = withObject "test" $ \v -> Test <$>
+		v .:: "name" <*>
+		v .:: "enabled" <*>
+		v .:: "info"
+
+instance Paths Test where
+	paths f (Test n e info) = Test n e <$> paths f info
+
+-- | Build info
+data Info = Info {
+	_infoDepends :: [String],
+	_infoLanguage :: Maybe Language,
+	_infoExtensions :: [Extension],
+	_infoGHCOptions :: [String],
+	_infoSourceDirs :: [FilePath] }
+		deriving (Eq, Read)
+
+instance Monoid Info where
+	mempty = Info [] Nothing [] [] []
+	mappend l r = Info
+		(ordNub $ _infoDepends l ++ _infoDepends r)
+		(getFirst $ First (_infoLanguage l) `mappend` First (_infoLanguage r))
+		(_infoExtensions l ++ _infoExtensions r)
+		(_infoGHCOptions l ++ _infoGHCOptions r)
+		(ordNub $ _infoSourceDirs l ++ _infoSourceDirs r)
+
+instance Ord Info where
+	compare l r = compare (_infoSourceDirs l, _infoDepends l, _infoGHCOptions l) (_infoSourceDirs r, _infoDepends r, _infoGHCOptions r)
+
+instance Show Info where
+	show i = unlines $ lang ++ exts ++ opts ++ sources where
+		lang = maybe [] (\l -> ["default-language: " ++ display l]) $ _infoLanguage i
+		exts
+			| null (_infoExtensions i) = []
+			| otherwise = "extensions:" : map (tab 1 . display) (_infoExtensions i)
+		opts
+			| null (_infoGHCOptions i) = []
+			| otherwise = "ghc-options:" : map (tab 1) (_infoGHCOptions i)
+		sources = "source-dirs:" : (map (tab 1) $ _infoSourceDirs i)
+
+instance ToJSON Info where
+	toJSON i = object [
+		"build-depends" .= _infoDepends i,
+		"language" .= fmap display (_infoLanguage i),
+		"extensions" .= map display (_infoExtensions i),
+		"ghc-options" .= _infoGHCOptions i,
+		"source-dirs" .= _infoSourceDirs i]
+
+instance FromJSON Info where
+	parseJSON = withObject "info" $ \v -> Info <$>
+		v .: "build-depends" <*>
+		((v .:: "language") >>= traverse (parseDT "Language")) <*>
+		((v .:: "extensions") >>= traverse (parseDT "Extension")) <*>
+		v .:: "ghc-options" <*>
+		v .:: "source-dirs"
+		where
+			parseDT :: Distribution.Text.Text a => String -> String -> Parser a
+			parseDT typeName v = maybe err return (simpleParse v) where
+				err = fail $ "Can't parse {}: {}" ~~ typeName ~~ v
+
+instance Paths Info where
+	paths f (Info deps lang exts opts dirs) = Info deps lang exts opts <$> traverse f dirs
+
+-- | Entity with project extensions
+data Extensions a = Extensions {
+	_extensions :: [Extension],
+	_ghcOptions :: [String],
+	_entity :: a }
+		deriving (Eq, Read, Show)
+
+instance Ord a => Ord (Extensions a) where
+	compare = comparing _entity
+
+instance Functor Extensions where
+	fmap f (Extensions e o x) = Extensions e o (f x)
+
+instance Applicative Extensions where
+	pure = Extensions [] []
+	(Extensions l lo f) <*> (Extensions r ro x) = Extensions (ordNub $ l ++ r) (ordNub $ lo ++ ro) (f x)
+
+instance Foldable Extensions where
+	foldMap f (Extensions _ _ x) = f x
+
+instance Traversable Extensions where
+	traverse f (Extensions e o x) = Extensions e o <$> f x
+
+makeLenses ''Project
+makeLenses ''ProjectDescription
+makeLenses ''Library
+makeLenses ''Executable
+makeLenses ''Test
+makeLenses ''Info
+makeLenses ''Extensions
diff --git a/src/HsDev/Sandbox.hs b/src/HsDev/Sandbox.hs
--- a/src/HsDev/Sandbox.hs
+++ b/src/HsDev/Sandbox.hs
@@ -1,161 +1,161 @@
-{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
-
-module HsDev.Sandbox (
-	SandboxType(..), Sandbox(..), sandboxType, sandbox,
-	isSandbox, guessSandboxType, sandboxFromPath,
-	findSandbox, searchSandbox, projectSandbox, sandboxPackageDbStack, searchPackageDbStack, restorePackageDbStack,
-
-	-- * cabal-sandbox util
-	cabalSandboxLib, cabalSandboxPackageDb,
-
-	getModuleOpts
-	) where
-
-import Control.Applicative
-import Control.Arrow
-import Control.DeepSeq (NFData(..))
-import Control.Monad.Trans.Maybe
-import Control.Monad.Except
-import Control.Lens (view, makeLenses)
-import Data.Aeson
-import Data.List (find)
-import Data.Maybe (isJust, fromMaybe)
-import qualified Data.Text as T (unpack)
-import Distribution.Compiler
-import Distribution.System
-import qualified Distribution.Text as T (display)
-import System.FilePath
-import System.Directory
-import System.Log.Simple (MonadLog(..))
-
-import System.Directory.Paths
-import HsDev.PackageDb
-import HsDev.Scan.Browse (withPackages, browsePackages)
-import HsDev.Stack
-import HsDev.Symbols (moduleOpts)
-import HsDev.Symbols.Types (Module(..), ModuleLocation(..), moduleLocation)
-import HsDev.Tools.Ghc.Compat as Compat
-import HsDev.Util (searchPath, directoryContents, cabalFile)
-
-import qualified Packages as GHC
-
-data SandboxType = CabalSandbox | StackWork deriving (Eq, Ord, Read, Show, Enum, Bounded)
-
-data Sandbox = Sandbox { _sandboxType :: SandboxType, _sandbox :: FilePath } deriving (Eq, Ord)
-
-makeLenses ''Sandbox
-
-instance NFData SandboxType where
-	rnf CabalSandbox = ()
-	rnf StackWork = ()
-
-instance NFData Sandbox where
-	rnf (Sandbox t p) = rnf t `seq` rnf p
-
-instance Show Sandbox where
-	show (Sandbox _ p) = p
-
-instance ToJSON Sandbox where
-	toJSON (Sandbox _ p) = toJSON p
-
-instance FromJSON Sandbox where
-	parseJSON = withText "sandbox" sandboxPath where
-		sandboxPath = maybe (fail "Not a sandbox") return . sandboxFromPath . T.unpack
-
-instance Paths Sandbox where
-	paths f (Sandbox st p) = Sandbox st <$> f p
-
-isSandbox :: FilePath -> Bool
-isSandbox = isJust . guessSandboxType
-
-guessSandboxType :: FilePath -> Maybe SandboxType
-guessSandboxType fpath
-	| takeFileName fpath == ".cabal-sandbox" = Just CabalSandbox
-	| takeFileName fpath == ".stack-work" = Just StackWork
-	| otherwise = Nothing
-
-sandboxFromPath :: FilePath -> Maybe Sandbox
-sandboxFromPath fpath = Sandbox <$> guessSandboxType fpath <*> pure fpath
-
--- | Find sandbox in path
-findSandbox :: FilePath -> IO (Maybe Sandbox)
-findSandbox fpath = do
-	fpath' <- canonicalize fpath
-	isDir <- doesDirectoryExist fpath'
-	if isDir
-		then do
-			dirs <- liftM (fpath' :) $ directoryContents fpath'
-			return $ msum $ map sandboxFromDir dirs
-		else return Nothing
-	where
-		sandboxFromDir :: FilePath -> Maybe Sandbox
-		sandboxFromDir fdir
-			| takeFileName fdir == "stack.yaml" = sandboxFromPath (takeDirectory fdir </> ".stack-work")
-			| otherwise = sandboxFromPath fdir
-
--- | Search sandbox by parent directory
-searchSandbox :: FilePath -> IO (Maybe Sandbox)
-searchSandbox p = runMaybeT $ searchPath p (MaybeT . findSandbox)
-
--- | Get project sandbox: search up for .cabal, then search for stack.yaml in current directory and cabal sandbox in current + parents
-projectSandbox :: FilePath -> IO (Maybe Sandbox)
-projectSandbox fpath = runMaybeT $ do
-	p <- searchPath fpath (MaybeT . getCabalFile)
-	MaybeT (findSandbox p) <|> searchPath p (MaybeT . findSbox')
-	where
-		getCabalFile = directoryContents >=> return . find cabalFile
-		findSbox' = directoryContents >=> return . msum . map sandboxFromPath
-
--- | Get package-db stack for sandbox
-sandboxPackageDbStack :: MonadLog m => Sandbox -> m PackageDbStack
-sandboxPackageDbStack (Sandbox CabalSandbox fpath) = do
-	dir <- cabalSandboxPackageDb
-	return $ PackageDbStack [PackageDb $ fpath </> dir]
-sandboxPackageDbStack (Sandbox StackWork fpath) = liftM (view stackPackageDbStack) $ projectEnv $ takeDirectory fpath
-
--- | Search package-db stack with user-db as default
-searchPackageDbStack :: MonadLog m => FilePath -> m PackageDbStack
-searchPackageDbStack p = do
-	mbox <- liftIO $ projectSandbox p
-	case mbox of
-		Nothing -> return userDb
-		Just sbox -> sandboxPackageDbStack sbox
-
--- | Restore package-db stack by package-db
-restorePackageDbStack :: MonadLog m => PackageDb -> m PackageDbStack
-restorePackageDbStack GlobalDb = return globalDb
-restorePackageDbStack UserDb = return userDb
-restorePackageDbStack (PackageDb p) = liftM (fromMaybe $ fromPackageDb p) $ runMaybeT $ do
-	sbox <- MaybeT $ liftIO $ searchSandbox p
-	lift $ sandboxPackageDbStack sbox
-
--- | Get actual sandbox build path: <arch>-<platform>-<compiler>-<version>
-cabalSandboxLib :: MonadLog m => m FilePath
-cabalSandboxLib = do
-	res <- withPackages ["-no-user-package-db"] $
-		return .
-		map (GHC.packageNameString &&& GHC.packageVersion) .
-		fromMaybe [] .
-		Compat.pkgDatabase
-	let
-		compiler = T.display buildCompilerFlavor
-		CompilerId _ version = buildCompilerId
-		ver = maybe (T.display version) T.display $ lookup compiler res
-	return $ T.display buildPlatform ++ "-" ++ compiler ++ "-" ++ ver
-
--- | Get sandbox package-db: <arch>-<platform>-<compiler>-<version>-packages.conf.d
-cabalSandboxPackageDb :: MonadLog m => m FilePath
-cabalSandboxPackageDb = liftM (++ "-packages.conf.d") cabalSandboxLib
-
--- | Options for GHC for module and project
-getModuleOpts :: MonadLog m => [String] -> Module -> m [String]
-getModuleOpts opts m = do
-	pdbs <- case view moduleLocation m of
-		FileModule fpath _ -> searchPackageDbStack fpath
-		InstalledModule pdb _ _ -> restorePackageDbStack pdb
-		ModuleSource _ -> return userDb
-	pkgs <- browsePackages opts pdbs
-	return $ concat [
-		packageDbStackOpts pdbs,
-		moduleOpts pkgs m,
-		opts]
+{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
+
+module HsDev.Sandbox (
+	SandboxType(..), Sandbox(..), sandboxType, sandbox,
+	isSandbox, guessSandboxType, sandboxFromPath,
+	findSandbox, searchSandbox, projectSandbox, sandboxPackageDbStack, searchPackageDbStack, restorePackageDbStack,
+
+	-- * cabal-sandbox util
+	cabalSandboxLib, cabalSandboxPackageDb,
+
+	getModuleOpts
+	) where
+
+import Control.Applicative
+import Control.Arrow
+import Control.DeepSeq (NFData(..))
+import Control.Monad.Trans.Maybe
+import Control.Monad.Except
+import Control.Lens (view, makeLenses)
+import Data.Aeson
+import Data.List (find)
+import Data.Maybe (isJust, fromMaybe)
+import qualified Data.Text as T (unpack)
+import Distribution.Compiler
+import Distribution.System
+import qualified Distribution.Text as T (display)
+import System.FilePath
+import System.Directory
+import System.Log.Simple (MonadLog(..))
+
+import System.Directory.Paths
+import HsDev.PackageDb
+import HsDev.Scan.Browse (withPackages, browsePackages)
+import HsDev.Stack
+import HsDev.Symbols (moduleOpts)
+import HsDev.Symbols.Types (Module(..), ModuleLocation(..), moduleLocation)
+import HsDev.Tools.Ghc.Compat as Compat
+import HsDev.Util (searchPath, directoryContents, cabalFile)
+
+import qualified Packages as GHC
+
+data SandboxType = CabalSandbox | StackWork deriving (Eq, Ord, Read, Show, Enum, Bounded)
+
+data Sandbox = Sandbox { _sandboxType :: SandboxType, _sandbox :: FilePath } deriving (Eq, Ord)
+
+makeLenses ''Sandbox
+
+instance NFData SandboxType where
+	rnf CabalSandbox = ()
+	rnf StackWork = ()
+
+instance NFData Sandbox where
+	rnf (Sandbox t p) = rnf t `seq` rnf p
+
+instance Show Sandbox where
+	show (Sandbox _ p) = p
+
+instance ToJSON Sandbox where
+	toJSON (Sandbox _ p) = toJSON p
+
+instance FromJSON Sandbox where
+	parseJSON = withText "sandbox" sandboxPath where
+		sandboxPath = maybe (fail "Not a sandbox") return . sandboxFromPath . T.unpack
+
+instance Paths Sandbox where
+	paths f (Sandbox st p) = Sandbox st <$> f p
+
+isSandbox :: FilePath -> Bool
+isSandbox = isJust . guessSandboxType
+
+guessSandboxType :: FilePath -> Maybe SandboxType
+guessSandboxType fpath
+	| takeFileName fpath == ".cabal-sandbox" = Just CabalSandbox
+	| takeFileName fpath == ".stack-work" = Just StackWork
+	| otherwise = Nothing
+
+sandboxFromPath :: FilePath -> Maybe Sandbox
+sandboxFromPath fpath = Sandbox <$> guessSandboxType fpath <*> pure fpath
+
+-- | Find sandbox in path
+findSandbox :: FilePath -> IO (Maybe Sandbox)
+findSandbox fpath = do
+	fpath' <- canonicalize fpath
+	isDir <- doesDirectoryExist fpath'
+	if isDir
+		then do
+			dirs <- liftM (fpath' :) $ directoryContents fpath'
+			return $ msum $ map sandboxFromDir dirs
+		else return Nothing
+	where
+		sandboxFromDir :: FilePath -> Maybe Sandbox
+		sandboxFromDir fdir
+			| takeFileName fdir == "stack.yaml" = sandboxFromPath (takeDirectory fdir </> ".stack-work")
+			| otherwise = sandboxFromPath fdir
+
+-- | Search sandbox by parent directory
+searchSandbox :: FilePath -> IO (Maybe Sandbox)
+searchSandbox p = runMaybeT $ searchPath p (MaybeT . findSandbox)
+
+-- | Get project sandbox: search up for .cabal, then search for stack.yaml in current directory and cabal sandbox in current + parents
+projectSandbox :: FilePath -> IO (Maybe Sandbox)
+projectSandbox fpath = runMaybeT $ do
+	p <- searchPath fpath (MaybeT . getCabalFile)
+	MaybeT (findSandbox p) <|> searchPath p (MaybeT . findSbox')
+	where
+		getCabalFile = directoryContents >=> return . find cabalFile
+		findSbox' = directoryContents >=> return . msum . map sandboxFromPath
+
+-- | Get package-db stack for sandbox
+sandboxPackageDbStack :: MonadLog m => Sandbox -> m PackageDbStack
+sandboxPackageDbStack (Sandbox CabalSandbox fpath) = do
+	dir <- cabalSandboxPackageDb
+	return $ PackageDbStack [PackageDb $ fpath </> dir]
+sandboxPackageDbStack (Sandbox StackWork fpath) = liftM (view stackPackageDbStack) $ projectEnv $ takeDirectory fpath
+
+-- | Search package-db stack with user-db as default
+searchPackageDbStack :: MonadLog m => FilePath -> m PackageDbStack
+searchPackageDbStack p = do
+	mbox <- liftIO $ projectSandbox p
+	case mbox of
+		Nothing -> return userDb
+		Just sbox -> sandboxPackageDbStack sbox
+
+-- | Restore package-db stack by package-db
+restorePackageDbStack :: MonadLog m => PackageDb -> m PackageDbStack
+restorePackageDbStack GlobalDb = return globalDb
+restorePackageDbStack UserDb = return userDb
+restorePackageDbStack (PackageDb p) = liftM (fromMaybe $ fromPackageDb p) $ runMaybeT $ do
+	sbox <- MaybeT $ liftIO $ searchSandbox p
+	lift $ sandboxPackageDbStack sbox
+
+-- | Get actual sandbox build path: <arch>-<platform>-<compiler>-<version>
+cabalSandboxLib :: MonadLog m => m FilePath
+cabalSandboxLib = do
+	res <- withPackages ["-no-user-package-db"] $
+		return .
+		map (GHC.packageNameString &&& GHC.packageVersion) .
+		fromMaybe [] .
+		Compat.pkgDatabase
+	let
+		compiler = T.display buildCompilerFlavor
+		CompilerId _ version = buildCompilerId
+		ver = maybe (T.display version) T.display $ lookup compiler res
+	return $ T.display buildPlatform ++ "-" ++ compiler ++ "-" ++ ver
+
+-- | Get sandbox package-db: <arch>-<platform>-<compiler>-<version>-packages.conf.d
+cabalSandboxPackageDb :: MonadLog m => m FilePath
+cabalSandboxPackageDb = liftM (++ "-packages.conf.d") cabalSandboxLib
+
+-- | Options for GHC for module and project
+getModuleOpts :: MonadLog m => [String] -> Module -> m [String]
+getModuleOpts opts m = do
+	pdbs <- case view moduleLocation m of
+		FileModule fpath _ -> searchPackageDbStack fpath
+		InstalledModule pdb _ _ -> restorePackageDbStack pdb
+		ModuleSource _ -> return userDb
+	pkgs <- browsePackages opts pdbs
+	return $ concat [
+		packageDbStackOpts pdbs,
+		moduleOpts pkgs m,
+		opts]
diff --git a/src/HsDev/Scan.hs b/src/HsDev/Scan.hs
--- a/src/HsDev/Scan.hs
+++ b/src/HsDev/Scan.hs
@@ -1,209 +1,209 @@
-{-# LANGUAGE FlexibleInstances #-}
-
-module HsDev.Scan (
-	-- * Enumerate functions
-	CompileFlag, ModuleToScan, ProjectToScan, PackageDbToScan, ScanContents(..),
-	EnumContents(..),
-	enumProject, enumSandbox, enumDirectory,
-
-	-- * Scan
-	scanProjectFile,
-	scanModule, scanModify, upToDate, rescanModule, changedModule, changedModules,
-
-	-- * Reexportss
-	module HsDev.Database,
-	module HsDev.Symbols.Types,
-	module Control.Monad.Except,
-	) where
-
-import Control.DeepSeq
-import Control.Lens (view, preview, set, over, each, _Right, _1, _2, _3, (^.), (^..))
-import Control.Monad.Except
-import Data.Maybe (catMaybes, fromMaybe, isJust, listToMaybe)
-import Data.List (intercalate)
-import System.Directory
-import Text.Format
-
-import HsDev.Error
-import HsDev.Scan.Browse (browsePackages, browseModules)
-import HsDev.Server.Types (FileSource(..), CommandMonad(..))
-import HsDev.Sandbox
-import HsDev.Symbols
-import HsDev.Symbols.Types
-import HsDev.Database
-import HsDev.Display
-import HsDev.Inspect
-import HsDev.Util
-
--- | Compile flags
-type CompileFlag = String
--- | Module with flags ready to scan
-type ModuleToScan = (ModuleLocation, [CompileFlag], Maybe String)
--- | Project ready to scan
-type ProjectToScan = (Project, [ModuleToScan])
--- | Package-db sandbox to scan (top of stack)
-type PackageDbToScan = PackageDbStack
-
--- | Scan info
-data ScanContents = ScanContents {
-	modulesToScan :: [ModuleToScan],
-	projectsToScan :: [ProjectToScan],
-	sandboxesToScan :: [PackageDbStack] }
-
-instance NFData ScanContents where
-	rnf (ScanContents ms ps ss) = rnf ms `seq` rnf ps `seq` rnf ss
-
-instance Monoid ScanContents where
-	mempty = ScanContents [] [] []
-	mappend (ScanContents lm lp ls) (ScanContents rm rp rs) = ScanContents
-		(uniqueBy (view _1) $ lm ++ rm)
-		(uniqueBy (view _1) $ lp ++ rp)
-		(ordNub $ ls ++ rs)
-
-instance Formattable ScanContents where
-	formattable (ScanContents ms ps cs) = formattable str where
-		str :: String
-		str = format "modules: {}, projects: {}, package-dbs: {}"
-			~~ (intercalate ", " $ ms ^.. each . _1 . moduleFile)
-			~~ (intercalate ", " $ ps ^.. each . _1 . projectPath)
-			~~ (intercalate ", " $ map (display . topPackageDb) $ cs ^.. each)
-
-class EnumContents a where
-	enumContents :: CommandMonad m => a -> m ScanContents
-
-instance EnumContents ModuleLocation where
-	enumContents mloc = return $ ScanContents [(mloc, [], Nothing)] [] []
-
-instance EnumContents (Extensions ModuleLocation) where
-	enumContents ex = return $ ScanContents [(view entity ex, extensionsOpts ex, Nothing)] [] []
-
-instance EnumContents Project where
-	enumContents = enumProject
-
-instance EnumContents PackageDbStack where
-	enumContents pdbs = return $ ScanContents [] [] (packageDbStacks pdbs)
-
-instance EnumContents Sandbox where
-	enumContents = enumSandbox
-
-instance {-# OVERLAPPING #-} EnumContents a => EnumContents [a] where
-	enumContents = liftM mconcat . tries . map enumContents
-
-instance {-# OVERLAPS #-} EnumContents FilePath where
-	enumContents f
-		| haskellSource f = hsdevLiftIO $ do
-			mproj <- liftIO $ locateProject f
-			case mproj of
-				Nothing -> enumContents $ FileModule f Nothing
-				Just proj -> do
-					ScanContents _ [(_, mods)] _ <- enumContents proj
-					return $ ScanContents (filter ((== Just f) . preview (_1 . moduleFile)) mods) [] []
-		| otherwise = enumDirectory f
-
-instance EnumContents FileSource where
-	enumContents (FileSource f mcts)
-		| haskellSource f = do
-			ScanContents [(m, opts, _)] _ _ <- enumContents f
-			return $ ScanContents [(m, opts, mcts)] [] []
-		| otherwise = return mempty
-
--- | Enum project sources
-enumProject :: CommandMonad m => Project -> m ScanContents
-enumProject p = hsdevLiftIO $ do
-	p' <- liftIO $ loadProject p
-	pdbs <- searchPackageDbStack (view projectPath p')
-	pkgs <- liftM (map $ view (package . packageName)) $ browsePackages [] pdbs
-	let
-		projOpts :: FilePath -> [String]
-		projOpts f = concatMap makeOpts $ fileTargets p' f where
-			makeOpts :: Info -> [String]
-			makeOpts i = concat [
-				["-hide-all-packages"],
-				["-package " ++ view projectName p'],
-				["-package " ++ dep | dep <- view infoDepends i, dep `elem` pkgs]]
-	srcs <- liftIO $ projectSources p'
-	let
-		mlocs = over each (\src -> over ghcOptions (++ projOpts (view entity src)) . over entity (\f -> FileModule f (Just p')) $ src) srcs
-	mods <- liftM modulesToScan $ enumContents mlocs
-	return $ ScanContents [] [(p', mods)] [] -- (sandboxCabals sboxes)
-
--- | Enum sandbox
-enumSandbox :: CommandMonad m => Sandbox -> m ScanContents
-enumSandbox = sandboxPackageDbStack >=> enumContents
-
--- | Enum directory modules
-enumDirectory :: CommandMonad m => FilePath -> m ScanContents
-enumDirectory dir = hsdevLiftIO $ do
-	cts <- liftIO $ traverseDirectory dir
-	let
-		projects = filter cabalFile cts
-		sources = filter haskellSource cts
-	dirs <- liftIO $ filterM doesDirectoryExist cts
-	sboxes <- liftM catMaybes $ triesMap (liftIO . findSandbox) dirs
-	pdbs <- mapM enumSandbox sboxes
-	projs <- liftM mconcat $ triesMap (enumProject . project) projects
-	let
-		projPaths = map (view projectPath . fst) $ projectsToScan projs
-		standalone = map (`FileModule` Nothing) $ filter (\s -> not (any (`isParent` s) projPaths)) sources
-	return $ mconcat [
-		ScanContents [(s, [], Nothing) | s <- standalone] [] [],
-		projs,
-		mconcat pdbs]
-
--- | Scan project file
-scanProjectFile :: CommandMonad m => [String] -> FilePath -> m Project
-scanProjectFile _ f = hsdevLiftIO $ do
-	proj <- (liftIO $ locateProject f) >>= maybe (hsdevError $ FileNotFound f) return
-	liftIO $ loadProject proj
-
--- | Scan module
-scanModule :: CommandMonad m => [(String, String)] -> [String] -> ModuleLocation -> Maybe String -> m InspectedModule
-scanModule defines opts (FileModule f p) mcts = hsdevLiftIO $ liftM setProj $ liftIO $ inspectFile defines opts f mcts where
-	setProj =
-		set (inspectedId . moduleProject) p .
-		set (inspectionResult . _Right . moduleLocation . moduleProject) p
-scanModule _ opts mloc@(InstalledModule c _ n) _ = hsdevLiftIO $ do
-	pdbs <- getDbs c
-	ims <- browseModules opts pdbs [mloc]
-	maybe (hsdevError $ BrowseNoModuleInfo n) return $ listToMaybe ims
-	where
-		getDbs :: CommandMonad m => PackageDb -> m PackageDbStack
-		getDbs = maybe (return userDb) searchPackageDbStack . preview packageDb
-scanModule _ _ (ModuleSource _) _ = hsdevError $ InspectError "Can inspect only modules in file or cabal"
-
--- | Scan additional info and modify scanned module
-scanModify :: CommandMonad m => ([String] -> PackageDbStack -> Module -> m Module) -> InspectedModule -> m InspectedModule
-scanModify f im = traverse f' im where
-	f' m = do
-		pdbs <- case view moduleLocation m of
-			-- TODO: Get actual sandbox stack
-			FileModule fpath _ -> searchPackageDbStack fpath
-			InstalledModule pdb _ _ -> maybe (return userDb) searchPackageDbStack $ preview packageDb pdb
-			_ -> return userDb
-		f (fromMaybe [] $ preview (inspection . inspectionOpts) im) pdbs m
-
--- | Is inspected module up to date?
-upToDate :: [String] -> InspectedModule -> IO Bool
-upToDate opts im = case view inspectedId im of
-	FileModule f _ -> liftM (== view inspection im) $ fileInspection f opts
-	InstalledModule _ _ _ -> return $ view inspection im == InspectionAt 0 opts
-	_ -> return False
-
--- | Rescan inspected module
-rescanModule :: CommandMonad m => [(String, String)] -> [String] -> InspectedModule -> m (Maybe InspectedModule)
-rescanModule defines opts im = do
-	up <- liftIO $ upToDate opts im
-	if up
-		then return Nothing
-		else fmap Just $ scanModule defines opts (view inspectedId im) Nothing
-
--- | Is module new or recently changed
-changedModule :: Database -> [String] -> ModuleLocation -> IO Bool
-changedModule db opts m = maybe (return True) (liftM not . liftIO . upToDate opts) m' where
-	m' = lookupInspected m db
-
--- | Returns new (to scan) and changed (to rescan) modules
-changedModules :: Database -> [String] -> [ModuleToScan] -> IO [ModuleToScan]
-changedModules db opts = filterM $ \m -> if isJust (m ^. _3)
-	then return True
-	else changedModule db (opts ++ (m ^. _2)) (m ^. _1)
+{-# LANGUAGE FlexibleInstances #-}
+
+module HsDev.Scan (
+	-- * Enumerate functions
+	CompileFlag, ModuleToScan, ProjectToScan, PackageDbToScan, ScanContents(..),
+	EnumContents(..),
+	enumProject, enumSandbox, enumDirectory,
+
+	-- * Scan
+	scanProjectFile,
+	scanModule, scanModify, upToDate, rescanModule, changedModule, changedModules,
+
+	-- * Reexportss
+	module HsDev.Database,
+	module HsDev.Symbols.Types,
+	module Control.Monad.Except,
+	) where
+
+import Control.DeepSeq
+import Control.Lens (view, preview, set, over, each, _Right, _1, _2, _3, (^.), (^..))
+import Control.Monad.Except
+import Data.Maybe (catMaybes, fromMaybe, isJust, listToMaybe)
+import Data.List (intercalate)
+import System.Directory
+import Text.Format
+
+import HsDev.Error
+import HsDev.Scan.Browse (browsePackages, browseModules)
+import HsDev.Server.Types (FileSource(..), CommandMonad(..))
+import HsDev.Sandbox
+import HsDev.Symbols
+import HsDev.Symbols.Types
+import HsDev.Database
+import HsDev.Display
+import HsDev.Inspect
+import HsDev.Util
+
+-- | Compile flags
+type CompileFlag = String
+-- | Module with flags ready to scan
+type ModuleToScan = (ModuleLocation, [CompileFlag], Maybe String)
+-- | Project ready to scan
+type ProjectToScan = (Project, [ModuleToScan])
+-- | Package-db sandbox to scan (top of stack)
+type PackageDbToScan = PackageDbStack
+
+-- | Scan info
+data ScanContents = ScanContents {
+	modulesToScan :: [ModuleToScan],
+	projectsToScan :: [ProjectToScan],
+	sandboxesToScan :: [PackageDbStack] }
+
+instance NFData ScanContents where
+	rnf (ScanContents ms ps ss) = rnf ms `seq` rnf ps `seq` rnf ss
+
+instance Monoid ScanContents where
+	mempty = ScanContents [] [] []
+	mappend (ScanContents lm lp ls) (ScanContents rm rp rs) = ScanContents
+		(uniqueBy (view _1) $ lm ++ rm)
+		(uniqueBy (view _1) $ lp ++ rp)
+		(ordNub $ ls ++ rs)
+
+instance Formattable ScanContents where
+	formattable (ScanContents ms ps cs) = formattable str where
+		str :: String
+		str = format "modules: {}, projects: {}, package-dbs: {}"
+			~~ (intercalate ", " $ ms ^.. each . _1 . moduleFile)
+			~~ (intercalate ", " $ ps ^.. each . _1 . projectPath)
+			~~ (intercalate ", " $ map (display . topPackageDb) $ cs ^.. each)
+
+class EnumContents a where
+	enumContents :: CommandMonad m => a -> m ScanContents
+
+instance EnumContents ModuleLocation where
+	enumContents mloc = return $ ScanContents [(mloc, [], Nothing)] [] []
+
+instance EnumContents (Extensions ModuleLocation) where
+	enumContents ex = return $ ScanContents [(view entity ex, extensionsOpts ex, Nothing)] [] []
+
+instance EnumContents Project where
+	enumContents = enumProject
+
+instance EnumContents PackageDbStack where
+	enumContents pdbs = return $ ScanContents [] [] (packageDbStacks pdbs)
+
+instance EnumContents Sandbox where
+	enumContents = enumSandbox
+
+instance {-# OVERLAPPING #-} EnumContents a => EnumContents [a] where
+	enumContents = liftM mconcat . tries . map enumContents
+
+instance {-# OVERLAPS #-} EnumContents FilePath where
+	enumContents f
+		| haskellSource f = hsdevLiftIO $ do
+			mproj <- liftIO $ locateProject f
+			case mproj of
+				Nothing -> enumContents $ FileModule f Nothing
+				Just proj -> do
+					ScanContents _ [(_, mods)] _ <- enumContents proj
+					return $ ScanContents (filter ((== Just f) . preview (_1 . moduleFile)) mods) [] []
+		| otherwise = enumDirectory f
+
+instance EnumContents FileSource where
+	enumContents (FileSource f mcts)
+		| haskellSource f = do
+			ScanContents [(m, opts, _)] _ _ <- enumContents f
+			return $ ScanContents [(m, opts, mcts)] [] []
+		| otherwise = return mempty
+
+-- | Enum project sources
+enumProject :: CommandMonad m => Project -> m ScanContents
+enumProject p = hsdevLiftIO $ do
+	p' <- liftIO $ loadProject p
+	pdbs <- searchPackageDbStack (view projectPath p')
+	pkgs <- liftM (map $ view (package . packageName)) $ browsePackages [] pdbs
+	let
+		projOpts :: FilePath -> [String]
+		projOpts f = concatMap makeOpts $ fileTargets p' f where
+			makeOpts :: Info -> [String]
+			makeOpts i = concat [
+				["-hide-all-packages"],
+				["-package " ++ view projectName p'],
+				["-package " ++ dep | dep <- view infoDepends i, dep `elem` pkgs]]
+	srcs <- liftIO $ projectSources p'
+	let
+		mlocs = over each (\src -> over ghcOptions (++ projOpts (view entity src)) . over entity (\f -> FileModule f (Just p')) $ src) srcs
+	mods <- liftM modulesToScan $ enumContents mlocs
+	return $ ScanContents [] [(p', mods)] [] -- (sandboxCabals sboxes)
+
+-- | Enum sandbox
+enumSandbox :: CommandMonad m => Sandbox -> m ScanContents
+enumSandbox = sandboxPackageDbStack >=> enumContents
+
+-- | Enum directory modules
+enumDirectory :: CommandMonad m => FilePath -> m ScanContents
+enumDirectory dir = hsdevLiftIO $ do
+	cts <- liftIO $ traverseDirectory dir
+	let
+		projects = filter cabalFile cts
+		sources = filter haskellSource cts
+	dirs <- liftIO $ filterM doesDirectoryExist cts
+	sboxes <- liftM catMaybes $ triesMap (liftIO . findSandbox) dirs
+	pdbs <- mapM enumSandbox sboxes
+	projs <- liftM mconcat $ triesMap (enumProject . project) projects
+	let
+		projPaths = map (view projectPath . fst) $ projectsToScan projs
+		standalone = map (`FileModule` Nothing) $ filter (\s -> not (any (`isParent` s) projPaths)) sources
+	return $ mconcat [
+		ScanContents [(s, [], Nothing) | s <- standalone] [] [],
+		projs,
+		mconcat pdbs]
+
+-- | Scan project file
+scanProjectFile :: CommandMonad m => [String] -> FilePath -> m Project
+scanProjectFile _ f = hsdevLiftIO $ do
+	proj <- (liftIO $ locateProject f) >>= maybe (hsdevError $ FileNotFound f) return
+	liftIO $ loadProject proj
+
+-- | Scan module
+scanModule :: CommandMonad m => [(String, String)] -> [String] -> ModuleLocation -> Maybe String -> m InspectedModule
+scanModule defines opts (FileModule f p) mcts = hsdevLiftIO $ liftM setProj $ liftIO $ inspectFile defines opts f mcts where
+	setProj =
+		set (inspectedId . moduleProject) p .
+		set (inspectionResult . _Right . moduleLocation . moduleProject) p
+scanModule _ opts mloc@(InstalledModule c _ n) _ = hsdevLiftIO $ do
+	pdbs <- getDbs c
+	ims <- browseModules opts pdbs [mloc]
+	maybe (hsdevError $ BrowseNoModuleInfo n) return $ listToMaybe ims
+	where
+		getDbs :: CommandMonad m => PackageDb -> m PackageDbStack
+		getDbs = maybe (return userDb) searchPackageDbStack . preview packageDb
+scanModule _ _ (ModuleSource _) _ = hsdevError $ InspectError "Can inspect only modules in file or cabal"
+
+-- | Scan additional info and modify scanned module
+scanModify :: CommandMonad m => ([String] -> PackageDbStack -> Module -> m Module) -> InspectedModule -> m InspectedModule
+scanModify f im = traverse f' im where
+	f' m = do
+		pdbs <- case view moduleLocation m of
+			-- TODO: Get actual sandbox stack
+			FileModule fpath _ -> searchPackageDbStack fpath
+			InstalledModule pdb _ _ -> maybe (return userDb) searchPackageDbStack $ preview packageDb pdb
+			_ -> return userDb
+		f (fromMaybe [] $ preview (inspection . inspectionOpts) im) pdbs m
+
+-- | Is inspected module up to date?
+upToDate :: [String] -> InspectedModule -> IO Bool
+upToDate opts im = case view inspectedId im of
+	FileModule f _ -> liftM (== view inspection im) $ fileInspection f opts
+	InstalledModule _ _ _ -> return $ view inspection im == InspectionAt 0 opts
+	_ -> return False
+
+-- | Rescan inspected module
+rescanModule :: CommandMonad m => [(String, String)] -> [String] -> InspectedModule -> m (Maybe InspectedModule)
+rescanModule defines opts im = do
+	up <- liftIO $ upToDate opts im
+	if up
+		then return Nothing
+		else fmap Just $ scanModule defines opts (view inspectedId im) Nothing
+
+-- | Is module new or recently changed
+changedModule :: Database -> [String] -> ModuleLocation -> IO Bool
+changedModule db opts m = maybe (return True) (liftM not . liftIO . upToDate opts) m' where
+	m' = lookupInspected m db
+
+-- | Returns new (to scan) and changed (to rescan) modules
+changedModules :: Database -> [String] -> [ModuleToScan] -> IO [ModuleToScan]
+changedModules db opts = filterM $ \m -> if isJust (m ^. _3)
+	then return True
+	else changedModule db (opts ++ (m ^. _2)) (m ^. _1)
diff --git a/src/HsDev/Scan/Browse.hs b/src/HsDev/Scan/Browse.hs
--- a/src/HsDev/Scan/Browse.hs
+++ b/src/HsDev/Scan/Browse.hs
@@ -1,251 +1,248 @@
-module HsDev.Scan.Browse (
-	-- * List all packages
-	browsePackages, browsePackagesDeps,
-	-- * Scan cabal modules
-	listModules, browseModules, browse, browseDb,
-	-- * Helpers
-	withPackages, withPackages_,
-	readPackage, readPackageConfig, ghcPackageDb, ghcModuleLocation,
-	packageDbCandidate, packageDbCandidate_,
-	packageConfigs, packageDbModules, lookupModule_,
-
-	module Control.Monad.Except
-	) where
-
-import Control.Arrow
-import Control.Lens (view, preview, _Just)
-import Control.Monad.Catch (MonadCatch, catch, SomeException)
-import Control.Monad.Except
-import Data.List (isPrefixOf)
-import Data.Maybe
-import Data.String (fromString)
-import Data.Version
-import System.Directory
-import System.FilePath
-import System.Log.Simple.Monad (MonadLog)
-
-import Data.Deps
-import HsDev.PackageDb
-import HsDev.Symbols
-import HsDev.Error
-import HsDev.Tools.Base (inspect)
-import HsDev.Tools.Ghc.Worker (GhcM, runGhcM, SessionTarget(..), workerSession)
-import HsDev.Tools.Ghc.Compat as Compat
-import HsDev.Util (ordNub)
-
-import qualified ConLike as GHC
-import qualified DataCon as GHC
-import qualified DynFlags as GHC
-import qualified GHC
-import qualified GHC.PackageDb as GHC
-import qualified GhcMonad as GHC (liftIO)
-import GhcMonad (GhcMonad)
-import qualified GHC.Paths as GHC
-import qualified Name as GHC
-import qualified Outputable as GHC
-import qualified Packages as GHC
-import qualified TyCon as GHC
-import qualified Type as GHC
-import qualified Var as GHC
-import qualified Pretty
-
--- | Browse packages
-browsePackages :: MonadLog m => [String] -> PackageDbStack -> m [PackageConfig]
-browsePackages opts dbs = withPackages_ (packageDbStackOpts dbs ++ opts) $
-	liftM (map readPackageConfig) packageConfigs
-
--- | Get packages with deps
-browsePackagesDeps :: MonadLog m => [String] -> PackageDbStack -> m (Deps PackageConfig)
-browsePackagesDeps opts dbs = withPackages (packageDbStackOpts dbs ++ opts) $ \df -> do
-	cfgs <- packageConfigs
-	return $ mapDeps (toPkg df) $ mconcat $ map (uncurry deps) $
-		map (Compat.unitId &&& Compat.depends df) cfgs
-	where
-		toPkg df' = readPackageConfig . getPackageDetails df'
-
-listModules :: MonadLog m => [String] -> PackageDbStack -> m [ModuleLocation]
-listModules opts dbs = withPackages_ (packageDbStackOpts dbs ++ opts) $ do
-	ms <- packageDbModules
-	pdbs <- mapM (liftIO . ghcPackageDb . fst) ms
-	return [ghcModuleLocation pdb p m | (pdb, (p, m)) <- zip pdbs ms]
-
-browseModules :: MonadLog m => [String] -> PackageDbStack -> [ModuleLocation] -> m [InspectedModule]
-browseModules opts dbs mlocs = withPackages_ (packageDbStackOpts dbs ++ opts) $ do
-	ms <- packageDbModules
-	pdbs <- mapM (liftIO . ghcPackageDb . fst) ms
-	liftM catMaybes $ sequence [browseModule' pdb p m | (pdb, (p, m)) <- zip pdbs ms, ghcModuleLocation pdb p m `elem` mlocs]
-	where
-		browseModule' :: PackageDb -> GHC.PackageConfig -> GHC.Module -> GhcM (Maybe InspectedModule)
-		browseModule' pdb p m = tryT $ inspect (ghcModuleLocation pdb p m) (return $ InspectionAt 0 opts) (browseModule pdb p m)
-
--- | Browse modules, if third argument is True - browse only modules in top of package-db stack
-browse :: MonadLog m => [String] -> PackageDbStack -> m [InspectedModule]
-browse opts dbs = listModules opts dbs >>= browseModules opts dbs
-
--- | Browse modules in top of package-db stack
-browseDb :: MonadLog m => [String] -> PackageDbStack -> m [InspectedModule]
-browseDb opts dbs = listModules opts dbs >>= browseModules opts dbs . filter inTop where
-	inTop = (== Just (topPackageDb dbs)) . preview modulePackageDb
-
-browseModule :: PackageDb -> GHC.PackageConfig -> GHC.Module -> GhcM Module
-browseModule pdb package' m = do
-	df <- GHC.getSessionDynFlags
-	mi <- GHC.getModuleInfo m >>= maybe (hsdevError $ BrowseNoModuleInfo thisModule) return
-	ds <- mapM (toDecl df mi) (GHC.modInfoExports mi)
-	return Module {
-		_moduleName = fromString thisModule,
-		_moduleDocs = Nothing,
-		_moduleLocation = thisLoc df,
-		_moduleExports = Just [ExportName Nothing (view declarationName d) ThingNothing | d <- ds],
-		_moduleImports = [import_ iname | iname <- ordNub (mapMaybe (preview definedModule) ds), iname /= fromString thisModule],
-		_moduleDeclarations = sortDeclarations ds }
-	where
-		thisModule = GHC.moduleNameString (GHC.moduleName m)
-		thisLoc df = view moduleIdLocation $ mloc df m
-		mloc df m' = ModuleId (fromString mname') $
-			ghcModuleLocation pdb (fromMaybe package' $ GHC.lookupPackage df (moduleUnitId m')) m'
-			where
-				mname' = GHC.moduleNameString $ GHC.moduleName m'
-		toDecl df minfo n = do
-			tyInfo <- GHC.modInfoLookupName minfo n
-			tyResult <- maybe (inModuleSource n) (return . Just) tyInfo
-			dflag <- GHC.getSessionDynFlags
-			let
-				decl' = decl (fromString $ GHC.getOccString n) $ fromMaybe
-					(Function Nothing [] Nothing)
-					(tyResult >>= showResult dflag)
-			return $ decl' `definedIn` mloc df (GHC.nameModule n)
-		definedModule = declarationDefined . _Just . moduleIdName
-		showResult :: GHC.DynFlags -> GHC.TyThing -> Maybe DeclarationInfo
-		showResult dflags (GHC.AnId i) = Just $ Function (Just $ fromString $ formatType dflags GHC.varType i) [] Nothing
-		showResult dflags (GHC.AConLike c) = case c of
-			GHC.RealDataCon d -> Just $ Function (Just $ fromString $ formatType dflags GHC.dataConRepType d) [] Nothing
-			GHC.PatSynCon p -> Just $ Function (Just $ fromString $ formatType dflags patSynType p) [] Nothing
-		showResult _ (GHC.ATyCon t) = Just $ tcon $ TypeInfo Nothing (map (fromString . GHC.getOccString) $ GHC.tyConTyVars t) Nothing [] where
-			tcon
-				| GHC.isAlgTyCon t && not (GHC.isNewTyCon t) && not (GHC.isClassTyCon t) = Data
-				| GHC.isNewTyCon t = NewType
-				| GHC.isClassTyCon t = Class
-				| GHC.isTypeSynonymTyCon t = Type
-				| otherwise = Type
-		showResult _ _ = Nothing
-
-withInitializedPackages :: MonadLog m => [String] -> (GHC.DynFlags -> GhcM a) -> m a
-withInitializedPackages ghcOpts cont = runGhcM (Just GHC.libdir) $ do
-	workerSession $ SessionGhc ghcOpts
-	fs <- GHC.getSessionDynFlags
-	cleanupHandler fs $ do
-		(fs', _, _) <- GHC.parseDynamicFlags fs (map GHC.noLoc ghcOpts)
-		_ <- GHC.setSessionDynFlags fs'
-		(result, _) <- GHC.liftIO $ GHC.initPackages fs'
-		cont result
-
-withPackages :: MonadLog m => [String] -> (GHC.DynFlags -> GhcM a) -> m a
-withPackages ghcOpts cont = withInitializedPackages ghcOpts cont
-
-withPackages_ :: MonadLog m => [String] -> GhcM a -> m a
-withPackages_ ghcOpts act = withPackages ghcOpts (const act)
-
-inModuleSource :: GhcMonad m => GHC.Name -> m (Maybe GHC.TyThing)
-inModuleSource nm = GHC.getModuleInfo (GHC.nameModule nm) >> GHC.lookupGlobalName nm
-
-formatType :: GHC.DynFlags -> (a -> GHC.Type) -> a -> String
-formatType dflag f x = showOutputable dflag (removeForAlls $ f x)
-
-removeForAlls :: GHC.Type -> GHC.Type
-removeForAlls ty = removeForAlls' ty' tty' where
-	ty'  = GHC.dropForAlls ty
-	tty' = GHC.splitFunTy_maybe ty'
-
-removeForAlls' :: GHC.Type -> Maybe (GHC.Type, GHC.Type) -> GHC.Type
-removeForAlls' ty Nothing = ty
-removeForAlls' ty (Just (pre, ftype))
-	| GHC.isPredTy pre = GHC.mkFunTy pre (GHC.dropForAlls ftype)
-	| otherwise = ty
-
-showOutputable :: GHC.Outputable a => GHC.DynFlags -> a -> String
-showOutputable dflag = unwords . lines . showUnqualifiedPage dflag . GHC.ppr
-
-showUnqualifiedPage :: GHC.DynFlags -> GHC.SDoc -> String
-showUnqualifiedPage dflag = renderStyle Pretty.LeftMode 0 . GHC.withPprStyleDoc dflag styleUnqualified
-
-styleUnqualified :: GHC.PprStyle
-styleUnqualified = GHC.mkUserStyle GHC.neverQualify GHC.AllTheWay
-
-tryT :: MonadCatch m => m a -> m (Maybe a)
-tryT act = catch (liftM Just act) (const (return Nothing) . (id :: SomeException -> SomeException))
-
-readPackage :: GHC.PackageConfig -> ModulePackage
-readPackage pc = ModulePackage (GHC.packageNameString pc) (showVersion (GHC.packageVersion pc))
-
-readPackageConfig :: GHC.PackageConfig -> PackageConfig
-readPackageConfig pc = PackageConfig
-	(readPackage pc)
-	(map (fromString . GHC.moduleNameString . GHC.exposedName) $ GHC.exposedModules pc)
-	(GHC.exposed pc)
-
-ghcModuleLocation :: PackageDb -> GHC.PackageConfig -> GHC.Module -> ModuleLocation
-ghcModuleLocation pdb p m = InstalledModule pdb (Just $ readPackage p) (GHC.moduleNameString $ GHC.moduleName m)
-
-ghcPackageDb :: GHC.PackageConfig -> IO PackageDb
-ghcPackageDb = maybe (return GlobalDb) packageDbCandidate_ . listToMaybe . GHC.libraryDirs
-
--- | Get package-db for package library directory
--- Haskish way
--- global-db - library is in <path>/lib/<package> and there exists <path>/lib/package.conf.d
--- user-db - library is in cabal user directory
--- package-db
---   cabal-sandbox - library in .../.cabal-sandbox/.../<platform-ghc-ver>/<package>
---     then package-db is .../.cabal-sandbox/<platform-ghc-ver>-package.conf.d
---   stack (snapshots or .stack-work) - library in <path>/lib/<platform-ghc-ver>/<package>
---     then package-db is <path>/pkgdb
-packageDbCandidate :: FilePath -> IO (Maybe PackageDb)
-packageDbCandidate fpath = liftM Just (msum [global', user', sandbox', stack']) `mplus` return Nothing where
-	global' = do
-		guard (takeFileName (takeDirectory fpath') == "lib")
-		guardExist (takeDirectory fpath' </> "package.conf.d")
-		return GlobalDb
-	user' = do
-		cabalDir <- getAppUserDataDirectory "cabal"
-		guard (splitDirectories cabalDir `isPrefixOf` splitDirectories fpath')
-		return UserDb
-	sandbox' = do
-		guard (".cabal-sandbox" `elem` splitDirectories fpath')
-		let
-			sandboxPath = joinPath $ reverse $ dropWhile (/= ".cabal-sandbox") $ reverse $ splitDirectories fpath'
-			platform = takeFileName (takeDirectory fpath')
-			dbPath = sandboxPath </> (platform ++ "-packages.conf.d")
-		guardExist dbPath
-		return $ PackageDb dbPath
-	stack' = do
-		guard (takeFileName (takeDirectory (takeDirectory fpath')) == "lib")
-		let
-			pkgDb = takeDirectory (takeDirectory $ takeDirectory fpath') </> "pkgdb"
-		guardExist pkgDb
-		return $ PackageDb pkgDb
-	guardExist = doesDirectoryExist >=> guard
-	fpath' = normalise fpath
-
--- | Use global as default
-packageDbCandidate_ :: FilePath -> IO PackageDb
-packageDbCandidate_ = packageDbCandidate >=> maybe (return GlobalDb) return
-
-packageConfigs :: GhcM [GHC.PackageConfig]
-packageConfigs = liftM (fromMaybe [] . pkgDatabase) GHC.getSessionDynFlags
-
-packageDbModules :: GhcM [(GHC.PackageConfig, GHC.Module)]
-packageDbModules = do
-	pkgs <- packageConfigs
-	dflags <- GHC.getSessionDynFlags
-	return [(p, m) |
-		p <- pkgs,
-		mn <- map GHC.exposedName (GHC.exposedModules p),
-		m <- lookupModule_ dflags mn]
-
--- Lookup module everywhere
-lookupModule_ :: GHC.DynFlags -> GHC.ModuleName -> [GHC.Module]
-lookupModule_ d mn = case GHC.lookupModuleWithSuggestions d mn Nothing of
-	GHC.LookupFound m' _ -> [m']
-	GHC.LookupMultiple ms -> map fst ms
-	GHC.LookupHidden ls rs -> map fst $ ls ++ rs
-	GHC.LookupNotFound _ -> []
+module HsDev.Scan.Browse (
+	-- * List all packages
+	browsePackages, browsePackagesDeps,
+	-- * Scan cabal modules
+	listModules, browseModules, browse, browseDb,
+	-- * Helpers
+	withPackages, withPackages_,
+	readPackage, readPackageConfig, ghcPackageDb, ghcModuleLocation,
+	packageDbCandidate, packageDbCandidate_,
+	packageConfigs, packageDbModules, lookupModule_,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Arrow
+import Control.Lens (view, preview, _Just)
+import Control.Monad.Catch (MonadCatch, catch, SomeException)
+import Control.Monad.Except
+import Data.List (isPrefixOf)
+import Data.Maybe
+import Data.String (fromString)
+import Data.Version
+import System.Directory
+import System.FilePath
+import System.Log.Simple.Monad (MonadLog)
+
+import Data.Deps
+import HsDev.PackageDb
+import HsDev.Symbols
+import HsDev.Error
+import HsDev.Tools.Base (inspect)
+import HsDev.Tools.Ghc.Worker (GhcM, runGhcM, SessionTarget(..), workerSession)
+import HsDev.Tools.Ghc.Compat as Compat
+import HsDev.Util (ordNub)
+
+import qualified ConLike as GHC
+import qualified DataCon as GHC
+import qualified DynFlags as GHC
+import qualified GHC
+import qualified GHC.PackageDb as GHC
+import qualified GhcMonad as GHC (liftIO)
+import GhcMonad (GhcMonad)
+import qualified GHC.Paths as GHC
+import qualified Name as GHC
+import qualified Outputable as GHC
+import qualified Packages as GHC
+import qualified TyCon as GHC
+import qualified Type as GHC
+import qualified Var as GHC
+import qualified Pretty
+
+-- | Browse packages
+browsePackages :: MonadLog m => [String] -> PackageDbStack -> m [PackageConfig]
+browsePackages opts dbs = withPackages_ (packageDbStackOpts dbs ++ opts) $
+	liftM (map readPackageConfig) packageConfigs
+
+-- | Get packages with deps
+browsePackagesDeps :: MonadLog m => [String] -> PackageDbStack -> m (Deps PackageConfig)
+browsePackagesDeps opts dbs = withPackages (packageDbStackOpts dbs ++ opts) $ \df -> do
+	cfgs <- packageConfigs
+	return $ mapDeps (toPkg df) $ mconcat $ map (uncurry deps) $
+		map (Compat.unitId &&& Compat.depends df) cfgs
+	where
+		toPkg df' = readPackageConfig . getPackageDetails df'
+
+listModules :: MonadLog m => [String] -> PackageDbStack -> m [ModuleLocation]
+listModules opts dbs = withPackages_ (packageDbStackOpts dbs ++ opts) $ do
+	ms <- packageDbModules
+	pdbs <- mapM (liftIO . ghcPackageDb . fst) ms
+	return [ghcModuleLocation pdb p m | (pdb, (p, m)) <- zip pdbs ms]
+
+browseModules :: MonadLog m => [String] -> PackageDbStack -> [ModuleLocation] -> m [InspectedModule]
+browseModules opts dbs mlocs = withPackages_ (packageDbStackOpts dbs ++ opts) $ do
+	ms <- packageDbModules
+	pdbs <- mapM (liftIO . ghcPackageDb . fst) ms
+	liftM catMaybes $ sequence [browseModule' pdb p m | (pdb, (p, m)) <- zip pdbs ms, ghcModuleLocation pdb p m `elem` mlocs]
+	where
+		browseModule' :: PackageDb -> GHC.PackageConfig -> GHC.Module -> GhcM (Maybe InspectedModule)
+		browseModule' pdb p m = tryT $ inspect (ghcModuleLocation pdb p m) (return $ InspectionAt 0 opts) (browseModule pdb p m)
+
+-- | Browse modules, if third argument is True - browse only modules in top of package-db stack
+browse :: MonadLog m => [String] -> PackageDbStack -> m [InspectedModule]
+browse opts dbs = listModules opts dbs >>= browseModules opts dbs
+
+-- | Browse modules in top of package-db stack
+browseDb :: MonadLog m => [String] -> PackageDbStack -> m [InspectedModule]
+browseDb opts dbs = listModules opts dbs >>= browseModules opts dbs . filter inTop where
+	inTop = (== Just (topPackageDb dbs)) . preview modulePackageDb
+
+browseModule :: PackageDb -> GHC.PackageConfig -> GHC.Module -> GhcM Module
+browseModule pdb package' m = do
+	df <- GHC.getSessionDynFlags
+	mi <- GHC.getModuleInfo m >>= maybe (hsdevError $ BrowseNoModuleInfo thisModule) return
+	ds <- mapM (toDecl df mi) (GHC.modInfoExports mi)
+	return Module {
+		_moduleName = fromString thisModule,
+		_moduleDocs = Nothing,
+		_moduleLocation = thisLoc df,
+		_moduleExports = Just [ExportName Nothing (view declarationName d) ThingNothing | d <- ds],
+		_moduleImports = [import_ iname | iname <- ordNub (mapMaybe (preview definedModule) ds), iname /= fromString thisModule],
+		_moduleDeclarations = sortDeclarations ds }
+	where
+		thisModule = GHC.moduleNameString (GHC.moduleName m)
+		thisLoc df = view moduleIdLocation $ mloc df m
+		mloc df m' = ModuleId (fromString mname') $
+			ghcModuleLocation pdb (fromMaybe package' $ GHC.lookupPackage df (moduleUnitId m')) m'
+			where
+				mname' = GHC.moduleNameString $ GHC.moduleName m'
+		toDecl df minfo n = do
+			tyInfo <- GHC.modInfoLookupName minfo n
+			tyResult <- maybe (inModuleSource n) (return . Just) tyInfo
+			dflag <- GHC.getSessionDynFlags
+			let
+				decl' = decl (fromString $ GHC.getOccString n) $ fromMaybe
+					(Function Nothing [] Nothing)
+					(tyResult >>= showResult dflag)
+			return $ decl' `definedIn` mloc df (GHC.nameModule n)
+		definedModule = declarationDefined . _Just . moduleIdName
+		showResult :: GHC.DynFlags -> GHC.TyThing -> Maybe DeclarationInfo
+		showResult dflags (GHC.AnId i) = Just $ Function (Just $ fromString $ formatType dflags GHC.varType i) [] Nothing
+		showResult dflags (GHC.AConLike c) = case c of
+			GHC.RealDataCon d -> Just $ Function (Just $ fromString $ formatType dflags GHC.dataConRepType d) [] Nothing
+			GHC.PatSynCon p -> Just $ Function (Just $ fromString $ formatType dflags patSynType p) [] Nothing
+		showResult _ (GHC.ATyCon t) = Just $ tcon $ TypeInfo Nothing (map (fromString . GHC.getOccString) $ GHC.tyConTyVars t) Nothing [] where
+			tcon
+				| GHC.isAlgTyCon t && not (GHC.isNewTyCon t) && not (GHC.isClassTyCon t) = Data
+				| GHC.isNewTyCon t = NewType
+				| GHC.isClassTyCon t = Class
+				| GHC.isTypeSynonymTyCon t = Type
+				| otherwise = Type
+		showResult _ _ = Nothing
+
+withInitializedPackages :: MonadLog m => [String] -> (GHC.DynFlags -> GhcM a) -> m a
+withInitializedPackages ghcOpts cont = runGhcM (Just GHC.libdir) $ do
+	workerSession $ SessionGhc ghcOpts
+	fs <- GHC.getSessionDynFlags
+	cleanupHandler fs $ do
+		(fs', _, _) <- GHC.parseDynamicFlags fs (map GHC.noLoc ghcOpts)
+		_ <- GHC.setSessionDynFlags fs'
+		(result, _) <- GHC.liftIO $ GHC.initPackages fs'
+		cont result
+
+withPackages :: MonadLog m => [String] -> (GHC.DynFlags -> GhcM a) -> m a
+withPackages ghcOpts cont = withInitializedPackages ghcOpts cont
+
+withPackages_ :: MonadLog m => [String] -> GhcM a -> m a
+withPackages_ ghcOpts act = withPackages ghcOpts (const act)
+
+inModuleSource :: GhcMonad m => GHC.Name -> m (Maybe GHC.TyThing)
+inModuleSource nm = GHC.getModuleInfo (GHC.nameModule nm) >> GHC.lookupGlobalName nm
+
+formatType :: GHC.DynFlags -> (a -> GHC.Type) -> a -> String
+formatType dflag f x = showOutputable dflag (removeForAlls $ f x)
+
+removeForAlls :: GHC.Type -> GHC.Type
+removeForAlls ty = removeForAlls' ty' tty' where
+	ty'  = GHC.dropForAlls ty
+	tty' = GHC.splitFunTy_maybe ty'
+
+removeForAlls' :: GHC.Type -> Maybe (GHC.Type, GHC.Type) -> GHC.Type
+removeForAlls' ty Nothing = ty
+removeForAlls' ty (Just (pre, ftype))
+	| GHC.isPredTy pre = GHC.mkFunTy pre (GHC.dropForAlls ftype)
+	| otherwise = ty
+
+showOutputable :: GHC.Outputable a => GHC.DynFlags -> a -> String
+showOutputable dflag = unwords . lines . showUnqualifiedPage dflag . GHC.ppr
+
+showUnqualifiedPage :: GHC.DynFlags -> GHC.SDoc -> String
+showUnqualifiedPage dflag = renderStyle Pretty.LeftMode 0 . GHC.withPprStyleDoc dflag (Compat.unqualStyle dflag)
+
+tryT :: MonadCatch m => m a -> m (Maybe a)
+tryT act = catch (liftM Just act) (const (return Nothing) . (id :: SomeException -> SomeException))
+
+readPackage :: GHC.PackageConfig -> ModulePackage
+readPackage pc = ModulePackage (GHC.packageNameString pc) (showVersion (GHC.packageVersion pc))
+
+readPackageConfig :: GHC.PackageConfig -> PackageConfig
+readPackageConfig pc = PackageConfig
+	(readPackage pc)
+	(map (fromString . GHC.moduleNameString . Compat.exposedModuleName) $ GHC.exposedModules pc)
+	(GHC.exposed pc)
+
+ghcModuleLocation :: PackageDb -> GHC.PackageConfig -> GHC.Module -> ModuleLocation
+ghcModuleLocation pdb p m = InstalledModule pdb (Just $ readPackage p) (GHC.moduleNameString $ GHC.moduleName m)
+
+ghcPackageDb :: GHC.PackageConfig -> IO PackageDb
+ghcPackageDb = maybe (return GlobalDb) packageDbCandidate_ . listToMaybe . GHC.libraryDirs
+
+-- | Get package-db for package library directory
+-- Haskish way
+-- global-db - library is in <path>/lib/<package> and there exists <path>/lib/package.conf.d
+-- user-db - library is in cabal user directory
+-- package-db
+--   cabal-sandbox - library in .../.cabal-sandbox/.../<platform-ghc-ver>/<package>
+--     then package-db is .../.cabal-sandbox/<platform-ghc-ver>-package.conf.d
+--   stack (snapshots or .stack-work) - library in <path>/lib/<platform-ghc-ver>/<package>
+--     then package-db is <path>/pkgdb
+packageDbCandidate :: FilePath -> IO (Maybe PackageDb)
+packageDbCandidate fpath = liftM Just (msum [global', user', sandbox', stack']) `mplus` return Nothing where
+	global' = do
+		guard (takeFileName (takeDirectory fpath') == "lib")
+		guardExist (takeDirectory fpath' </> "package.conf.d")
+		return GlobalDb
+	user' = do
+		cabalDir <- getAppUserDataDirectory "cabal"
+		guard (splitDirectories cabalDir `isPrefixOf` splitDirectories fpath')
+		return UserDb
+	sandbox' = do
+		guard (".cabal-sandbox" `elem` splitDirectories fpath')
+		let
+			sandboxPath = joinPath $ reverse $ dropWhile (/= ".cabal-sandbox") $ reverse $ splitDirectories fpath'
+			platform = takeFileName (takeDirectory fpath')
+			dbPath = sandboxPath </> (platform ++ "-packages.conf.d")
+		guardExist dbPath
+		return $ PackageDb dbPath
+	stack' = do
+		guard (takeFileName (takeDirectory (takeDirectory fpath')) == "lib")
+		let
+			pkgDb = takeDirectory (takeDirectory $ takeDirectory fpath') </> "pkgdb"
+		guardExist pkgDb
+		return $ PackageDb pkgDb
+	guardExist = doesDirectoryExist >=> guard
+	fpath' = normalise fpath
+
+-- | Use global as default
+packageDbCandidate_ :: FilePath -> IO PackageDb
+packageDbCandidate_ = packageDbCandidate >=> maybe (return GlobalDb) return
+
+packageConfigs :: GhcM [GHC.PackageConfig]
+packageConfigs = liftM (fromMaybe [] . pkgDatabase) GHC.getSessionDynFlags
+
+packageDbModules :: GhcM [(GHC.PackageConfig, GHC.Module)]
+packageDbModules = do
+	pkgs <- packageConfigs
+	dflags <- GHC.getSessionDynFlags
+	return [(p, m) |
+		p <- pkgs,
+		mn <- map Compat.exposedModuleName (GHC.exposedModules p),
+		m <- lookupModule_ dflags mn]
+
+-- Lookup module everywhere
+lookupModule_ :: GHC.DynFlags -> GHC.ModuleName -> [GHC.Module]
+lookupModule_ d mn = case GHC.lookupModuleWithSuggestions d mn Nothing of
+	GHC.LookupFound m' _ -> [m']
+	GHC.LookupMultiple ms -> map fst ms
+	GHC.LookupHidden ls rs -> map fst $ ls ++ rs
+	GHC.LookupNotFound _ -> []
diff --git a/src/HsDev/Server/Base.hs b/src/HsDev/Server/Base.hs
--- a/src/HsDev/Server/Base.hs
+++ b/src/HsDev/Server/Base.hs
@@ -1,159 +1,159 @@
-{-# LANGUAGE CPP, OverloadedStrings #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Server.Base (
-	initLog, runServer, Server, startServer, inServer,
-	withCache, writeCache, readCache,
-
-	module HsDev.Server.Types,
-	module HsDev.Server.Message
-	) where
-
-import Control.Applicative
-import Control.Concurrent
-import Control.Exception
-import Control.Monad
-import Control.Monad.Except
-import Control.Monad.Reader
-import Data.Default
-import qualified Data.Map as M
-import Data.Maybe
-import Data.String
-import Data.Text (Text)
-import qualified Data.Text as T (pack, unpack)
-import System.Log.Simple hiding (Level(..), Message)
-import qualified System.Log.Simple.Base as Log
-import System.Directory (removeDirectoryRecursive, createDirectoryIfMissing)
-import System.FilePath
-
-import qualified Control.Concurrent.FiniteChan as F
-import System.Directory.Paths (canonicalize)
-import qualified System.Directory.Watcher as Watcher
-import Text.Format ((~~), (~%))
-
-import qualified HsDev.Cache as Cache
-import qualified HsDev.Cache.Structured as SC
-import qualified HsDev.Client.Commands as Client
-import HsDev.Database
-import qualified HsDev.Database.Async as DB
-import qualified HsDev.Database.Update as Update
-import HsDev.Inspect (getDefines)
-import HsDev.Tools.Ghc.Worker
-import HsDev.Server.Types
-import HsDev.Server.Message
-import HsDev.Util
-
-#if mingw32_HOST_OS
-import System.Win32.FileMapping.NamePool
-#endif
-
--- | Inits log chan and returns functions (print message, wait channel)
-initLog :: ServerOpts -> IO SessionLog
-initLog sopts = do
-	msgs <- F.newChan
-	l <- newLog (logCfg [("", Log.level_ . T.pack . serverLogLevel $ sopts)]) $ concat [
-		[handler text console | not $ serverSilent sopts],
-		[handler text (chaner msgs)],
-		[handler text (file f) | f <- maybeToList (serverLog sopts)]]
-	let
-		listenLog = F.dupChan msgs >>= F.readChan
-	return $ SessionLog l listenLog (stopLog l)
-
--- | Run server
-runServer :: ServerOpts -> ServerM IO () -> IO ()
-runServer sopts act = bracket (initLog sopts) sessionLogWait $ \slog -> Watcher.withWatcher $ \watcher -> withLog (sessionLogger slog) $ do
-	waitSem <- liftIO $ newQSem 0
-	db <- liftIO $ DB.newAsync
-	withCache sopts () $ \cdir -> do
-		sendLog Log.Trace $ "Checking cache version in {}" ~~ cdir 
-		ver <- liftIO $ Cache.readVersion $ cdir </> Cache.versionCache
-		sendLog Log.Debug $ "Cache version: {}" ~~ strVersion ver
-		unless (sameVersion (cutVersion version) (cutVersion ver)) $ ignoreIO $ do
-			sendLog Log.Info $ "Cache version ({cache}) is incompatible with hsdev version ({hsdev}), removing cache ({dir})" ~~
-				("cache" ~% strVersion ver) ~~
-				("hsdev" ~% strVersion version) ~~
-				("dir" ~% cdir)
-			-- drop cache
-			liftIO $ removeDirectoryRecursive cdir
-		sendLog Log.Debug $ "Writing new cache version: {}" ~~ strVersion version
-		liftIO $ createDirectoryIfMissing True cdir
-		liftIO $ Cache.writeVersion $ cdir </> Cache.versionCache
-	when (serverLoad sopts) $ withCache sopts () $ \cdir -> do
-		sendLog Log.Info $ "Loading cache from {}" ~~ cdir
-		dbCache <- liftA merge <$> liftIO (SC.load cdir)
-		case dbCache of
-			Left err -> sendLog Log.Error $ "Failed to load cache: {}" ~~ err
-			Right dbCache' -> DB.update db (return dbCache')
-#if mingw32_HOST_OS
-	mmapPool <- Just <$> liftIO (createPool "hsdev")
-#endif
-	ghcw <- ghcWorker
-	defs <- liftIO getDefines
-	let
-		session = Session
-			db
-			(writeCache sopts)
-			(readCache sopts)
-			slog
-			watcher
-#if mingw32_HOST_OS
-			mmapPool
-#endif
-			ghcw
-			(do
-				withLog (sessionLogger slog) $ sendLog Log.Trace "stopping server"
-				signalQSem waitSem)
-			(waitQSem waitSem)
-			defs
-	_ <- liftIO $ forkIO $ Update.onEvent watcher $ \w e -> withSession session $
-		void $ Client.runClient def $ Update.processEvent def w e
-	liftIO $ runReaderT (runServerM act) session
-
-type Server = Worker (ServerM IO)
-
-startServer :: ServerOpts -> IO Server
-startServer sopts = startWorker (runServer sopts) id id
-
-inServer :: Server -> CommandOptions -> Command -> IO Result
-inServer srv copts c = do
-	c' <- canonicalize c
-	inWorker srv (Client.runClient copts $ Client.runCommand c')
-
-chaner :: F.Chan String -> Consumer Text
-chaner ch = return $ F.putChan ch . T.unpack
-
--- | Perform action on cache
-withCache :: Monad m => ServerOpts -> a -> (FilePath -> m a) -> m a
-withCache sopts v onCache = case serverCache sopts of
-	Nothing -> return v
-	Just cdir -> onCache cdir
-
-writeCache :: SessionMonad m => ServerOpts -> Database -> m ()
-writeCache sopts db = withCache sopts () $ \cdir -> do
-	sendLog Log.Info $ "writing cache to {}" ~~ cdir
-	logIO "cache writing exception: " (sendLog Log.Error . fromString) $ do
-		let
-			sd = structurize db
-		liftIO $ SC.dump cdir sd
-		forM_ (M.keys (structuredPackageDbs sd)) $ \c -> sendLog Log.Debug ("cache write: cabal {}" ~~ show c)
-		forM_ (M.keys (structuredProjects sd)) $ \p -> sendLog Log.Debug ("cache write: project {}" ~~ p)
-		case allModules (structuredFiles sd) of
-			[] -> return ()
-			ms -> sendLog Log.Debug $ "cache write: {} files" ~~ length ms
-	sendLog Log.Info $ "cache saved to {}" ~~ cdir
-
-readCache :: SessionMonad m => ServerOpts -> (FilePath -> ExceptT String IO Structured) -> m (Maybe Database)
-readCache sopts act = do
-	s <- getSession
-	liftIO $ withSession s $ withCache sopts Nothing $ \fpath -> do
-		res <- liftIO $ runExceptT $ act fpath
-		either cacheErr cacheOk res
-	where
-		cacheErr e = sendLog Log.Error ("Error reading cache: {}" ~~ e) >> return Nothing
-		cacheOk s = do
-			forM_ (M.keys (structuredPackageDbs s)) $ \c -> sendLog Log.Debug ("cache read: cabal {}" ~~ show c)
-			forM_ (M.keys (structuredProjects s)) $ \p -> sendLog Log.Debug ("cache read: project {}" ~~ p)
-			case allModules (structuredFiles s) of
-				[] -> return ()
-				ms -> sendLog Log.Debug $ "cache read: {} files" ~~ length ms
-			return $ Just $ merge s
+{-# LANGUAGE CPP, OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Server.Base (
+	initLog, runServer, Server, startServer, inServer,
+	withCache, writeCache, readCache,
+
+	module HsDev.Server.Types,
+	module HsDev.Server.Message
+	) where
+
+import Control.Applicative
+import Control.Concurrent
+import Control.Exception
+import Control.Monad
+import Control.Monad.Except
+import Control.Monad.Reader
+import Data.Default
+import qualified Data.Map as M
+import Data.Maybe
+import Data.String
+import Data.Text (Text)
+import qualified Data.Text as T (pack, unpack)
+import System.Log.Simple hiding (Level(..), Message)
+import qualified System.Log.Simple.Base as Log
+import System.Directory (removeDirectoryRecursive, createDirectoryIfMissing)
+import System.FilePath
+
+import qualified Control.Concurrent.FiniteChan as F
+import System.Directory.Paths (canonicalize)
+import qualified System.Directory.Watcher as Watcher
+import Text.Format ((~~), (~%))
+
+import qualified HsDev.Cache as Cache
+import qualified HsDev.Cache.Structured as SC
+import qualified HsDev.Client.Commands as Client
+import HsDev.Database
+import qualified HsDev.Database.Async as DB
+import qualified HsDev.Database.Update as Update
+import HsDev.Inspect (getDefines)
+import HsDev.Tools.Ghc.Worker
+import HsDev.Server.Types
+import HsDev.Server.Message
+import HsDev.Util
+
+#if mingw32_HOST_OS
+import System.Win32.FileMapping.NamePool
+#endif
+
+-- | Inits log chan and returns functions (print message, wait channel)
+initLog :: ServerOpts -> IO SessionLog
+initLog sopts = do
+	msgs <- F.newChan
+	l <- newLog (logCfg [("", Log.level_ . T.pack . serverLogLevel $ sopts)]) $ concat [
+		[handler text console | not $ serverSilent sopts],
+		[handler text (chaner msgs)],
+		[handler text (file f) | f <- maybeToList (serverLog sopts)]]
+	let
+		listenLog = F.dupChan msgs >>= F.readChan
+	return $ SessionLog l listenLog (stopLog l)
+
+-- | Run server
+runServer :: ServerOpts -> ServerM IO () -> IO ()
+runServer sopts act = bracket (initLog sopts) sessionLogWait $ \slog -> Watcher.withWatcher $ \watcher -> withLog (sessionLogger slog) $ do
+	waitSem <- liftIO $ newQSem 0
+	db <- liftIO $ DB.newAsync
+	withCache sopts () $ \cdir -> do
+		sendLog Log.Trace $ "Checking cache version in {}" ~~ cdir 
+		ver <- liftIO $ Cache.readVersion $ cdir </> Cache.versionCache
+		sendLog Log.Debug $ "Cache version: {}" ~~ strVersion ver
+		unless (sameVersion (cutVersion version) (cutVersion ver)) $ ignoreIO $ do
+			sendLog Log.Info $ "Cache version ({cache}) is incompatible with hsdev version ({hsdev}), removing cache ({dir})" ~~
+				("cache" ~% strVersion ver) ~~
+				("hsdev" ~% strVersion version) ~~
+				("dir" ~% cdir)
+			-- drop cache
+			liftIO $ removeDirectoryRecursive cdir
+		sendLog Log.Debug $ "Writing new cache version: {}" ~~ strVersion version
+		liftIO $ createDirectoryIfMissing True cdir
+		liftIO $ Cache.writeVersion $ cdir </> Cache.versionCache
+	when (serverLoad sopts) $ withCache sopts () $ \cdir -> do
+		sendLog Log.Info $ "Loading cache from {}" ~~ cdir
+		dbCache <- liftA merge <$> liftIO (SC.load cdir)
+		case dbCache of
+			Left err -> sendLog Log.Error $ "Failed to load cache: {}" ~~ err
+			Right dbCache' -> DB.update db (return dbCache')
+#if mingw32_HOST_OS
+	mmapPool <- Just <$> liftIO (createPool "hsdev")
+#endif
+	ghcw <- ghcWorker
+	defs <- liftIO getDefines
+	let
+		session = Session
+			db
+			(writeCache sopts)
+			(readCache sopts)
+			slog
+			watcher
+#if mingw32_HOST_OS
+			mmapPool
+#endif
+			ghcw
+			(do
+				withLog (sessionLogger slog) $ sendLog Log.Trace "stopping server"
+				signalQSem waitSem)
+			(waitQSem waitSem)
+			defs
+	_ <- liftIO $ forkIO $ Update.onEvent watcher $ \w e -> withSession session $
+		void $ Client.runClient def $ Update.processEvent def w e
+	liftIO $ runReaderT (runServerM act) session
+
+type Server = Worker (ServerM IO)
+
+startServer :: ServerOpts -> IO Server
+startServer sopts = startWorker (runServer sopts) id id
+
+inServer :: Server -> CommandOptions -> Command -> IO Result
+inServer srv copts c = do
+	c' <- canonicalize c
+	inWorker srv (Client.runClient copts $ Client.runCommand c')
+
+chaner :: F.Chan String -> Consumer Text
+chaner ch = return $ F.putChan ch . T.unpack
+
+-- | Perform action on cache
+withCache :: Monad m => ServerOpts -> a -> (FilePath -> m a) -> m a
+withCache sopts v onCache = case serverCache sopts of
+	Nothing -> return v
+	Just cdir -> onCache cdir
+
+writeCache :: SessionMonad m => ServerOpts -> Database -> m ()
+writeCache sopts db = withCache sopts () $ \cdir -> do
+	sendLog Log.Info $ "writing cache to {}" ~~ cdir
+	logIO "cache writing exception: " (sendLog Log.Error . fromString) $ do
+		let
+			sd = structurize db
+		liftIO $ SC.dump cdir sd
+		forM_ (M.keys (structuredPackageDbs sd)) $ \c -> sendLog Log.Debug ("cache write: cabal {}" ~~ show c)
+		forM_ (M.keys (structuredProjects sd)) $ \p -> sendLog Log.Debug ("cache write: project {}" ~~ p)
+		case allModules (structuredFiles sd) of
+			[] -> return ()
+			ms -> sendLog Log.Debug $ "cache write: {} files" ~~ length ms
+	sendLog Log.Info $ "cache saved to {}" ~~ cdir
+
+readCache :: SessionMonad m => ServerOpts -> (FilePath -> ExceptT String IO Structured) -> m (Maybe Database)
+readCache sopts act = do
+	s <- getSession
+	liftIO $ withSession s $ withCache sopts Nothing $ \fpath -> do
+		res <- liftIO $ runExceptT $ act fpath
+		either cacheErr cacheOk res
+	where
+		cacheErr e = sendLog Log.Error ("Error reading cache: {}" ~~ e) >> return Nothing
+		cacheOk s = do
+			forM_ (M.keys (structuredPackageDbs s)) $ \c -> sendLog Log.Debug ("cache read: cabal {}" ~~ show c)
+			forM_ (M.keys (structuredProjects s)) $ \p -> sendLog Log.Debug ("cache read: project {}" ~~ p)
+			case allModules (structuredFiles s) of
+				[] -> return ()
+				ms -> sendLog Log.Debug $ "cache read: {} files" ~~ length ms
+			return $ Just $ merge s
diff --git a/src/HsDev/Server/Commands.hs b/src/HsDev/Server/Commands.hs
--- a/src/HsDev/Server/Commands.hs
+++ b/src/HsDev/Server/Commands.hs
@@ -1,434 +1,434 @@
-{-# LANGUAGE OverloadedStrings, CPP, PatternGuards, LambdaCase, TemplateHaskell #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Server.Commands (
-	ServerCommand(..), ServerOpts(..), ClientOpts(..),
-	Request(..),
-	Msg, isLisp, msg, jsonMsg, lispMsg, encodeMessage, decodeMessage,
-	sendCommand, runServerCommand,
-	findPath,
-	processRequest, processClient, processClientSocket,
-	module HsDev.Server.Types
-	) where
-
-import Control.Applicative
-import Control.Concurrent
-import Control.Concurrent.Async
-import Control.Lens (set, traverseOf, view, over, Lens', Lens, _1, _2, _Left)
-import Control.Monad
-import Control.Monad.Catch (bracket, finally)
-import Data.Aeson hiding (Result, Error)
-import Data.Aeson.Encode.Pretty
-import qualified Data.ByteString.Char8 as BS
-import Data.ByteString.Lazy.Char8 (ByteString)
-import qualified Data.ByteString.Lazy.Char8 as L
-import Data.Maybe
-import Data.String (fromString)
-import qualified Data.Text as T (pack)
-import Network.Socket hiding (connect)
-import qualified Network.Socket as Net hiding (send)
-import qualified Network.Socket.ByteString as Net (send)
-import qualified Network.Socket.ByteString.Lazy as Net (getContents)
-import System.Directory
-import System.Exit
-import System.FilePath
-import System.IO
-import qualified System.Log.Simple as Log
-
-import Control.Concurrent.Util
-import qualified Control.Concurrent.FiniteChan as F
-import Data.Lisp
-import Text.Format ((~~), (~%))
-import System.Directory.Paths
-
-import qualified HsDev.Client.Commands as Client
-import qualified HsDev.Database.Async as DB
-import HsDev.Server.Base
-import HsDev.Server.Types
-import HsDev.Tools.Base (runTool_)
-import HsDev.Error
-import HsDev.Util
-import HsDev.Version
-
-#if mingw32_HOST_OS
-import Data.Aeson.Types hiding (Result, Error)
-import Data.Char
-import Data.List
-import System.Environment
-import System.Win32.FileMapping.Memory (withMapFile, readMapFile)
-import System.Win32.FileMapping.NamePool
-import System.Win32.PowerShell (escape, quote, quoteDouble)
-#else
-import Control.Exception (SomeException, handle)
-import System.Posix.Process
-import System.Posix.Files (removeLink)
-import System.Posix.IO
-#endif
-
-sendCommand :: ClientOpts -> Bool -> Command -> (Notification -> IO a) -> IO Result
-sendCommand copts noFile c onNotification = do
-	asyncAct <- async sendReceive
-	res <- waitCatch asyncAct
-	case res of
-		Left e -> return $ Error $ OtherError (show e)
-		Right r -> return r
-	where
-		sendReceive = do
-			curDir <- getCurrentDirectory
-			input <- if clientStdin copts
-				then Just <$> L.getContents
-				else return $ toUtf8 <$> Nothing -- arg "data" copts
-			let
-				parseData :: L.ByteString -> IO Value
-				parseData cts = case eitherDecode cts of
-					Left err -> putStrLn ("Invalid data: " ++ err) >> exitFailure
-					Right v -> return v
-			_ <- traverse parseData input -- FIXME: Not used!
-
-			s <- makeSocket (clientPort copts)
-			addr' <- inet_addr "127.0.0.1"
-			Net.connect s (sockAddr (clientPort copts) addr')
-			bracket (socketToHandle s ReadWriteMode) hClose $ \h -> do
-				L.hPutStrLn h $ encode $ Message Nothing $ Request c curDir noFile (clientTimeout copts) (clientSilent copts)
-				hFlush h
-				peekResponse h
-
-		peekResponse h = do
-			resp <- hGetLineBS h
-			parseResponse h resp
-
-		parseResponse h str = case eitherDecode str of
-			Left e -> return $ Error $ ResponseError ("can't parse: {}" ~~ e) (fromUtf8 str)
-			Right (Message _ r) -> do
-				Response r' <- unMmap r
-				case r' of
-					Left n -> onNotification n >> peekResponse h
-					Right res -> return res
-
-runServerCommand :: ServerCommand -> IO ()
-runServerCommand Version = putStrLn $cabalVersion
-runServerCommand (Start sopts) = do
-#if mingw32_HOST_OS
-	let
-		args = "run" : serverOptsArgs sopts
-	myExe <- getExecutablePath
-	curDir <- getCurrentDirectory
-	let
-		-- one escape for start-process and other for callable process
-		-- seems, that start-process just concats arguments into one string
-		-- start-process foo 'bar baz' ⇒ foo bar baz -- not expected
-		-- start-process foo '"bar baz"' ⇒ foo "bar baz" -- ok
-		biescape = escape quote . escape quoteDouble
-		script = "try {{ start-process {process} {args} -WindowStyle Hidden -WorkingDirectory {dir} }} catch {{ $_.Exception, $_.InvocationInfo.Line }}"
-			~~ ("process" ~% escape quote myExe)
-			~~ ("args" ~% intercalate ", " (map biescape args))
-			~~ ("dir" ~% escape quote curDir)
-	r <- runTool_ "powershell" [
-		"-Command",
-		script]
-	if all isSpace r
-		then putStrLn $ "Server started at port {}" ~~ serverPort sopts
-		else mapM_ putStrLn [
-			"Failed to start server",
-			"\tCommand: {}" ~~ script,
-			"\tResult: {}" ~~ r]
-#else
-	let
-		forkError :: SomeException -> IO ()
-		forkError e  = putStrLn $ "Failed to start server: {}" ~~ show e
-
-		proxy :: IO ()
-		proxy = do
-			_ <- createSession
-			_ <- forkProcess serverAction
-			exitImmediately ExitSuccess
-
-		serverAction :: IO ()
-		serverAction = do
-			mapM_ closeFd [stdInput, stdOutput, stdError]
-			nullFd <- openFd "/dev/null" ReadWrite Nothing defaultFileFlags
-			mapM_ (dupTo nullFd) [stdInput, stdOutput, stdError]
-			closeFd nullFd
-			runServerCommand (Run sopts)
-
-	handle forkError $ do
-		_ <- forkProcess proxy
-		putStrLn $ "Server started at port {}" ~~ serverPort sopts
-#endif
-runServerCommand (Run sopts) = runServer sopts $ do
-	q <- liftIO $ newQSem 0
-	clientChan <- liftIO F.newChan
-	session <- getSession
-	_ <- liftIO $ async $ withSession session $ Log.scope "listener" $ flip finally serverExit $
-		bracket (liftIO $ makeSocket (serverPort sopts)) (liftIO . close) $ \s -> do
-			liftIO $ do
-				setSocketOption s ReuseAddr 1
-				addr' <- inet_addr "127.0.0.1"
-				bind s (sockAddr (serverPort sopts) addr')
-				listen s maxListenQueue
-			forever $ logAsync (Log.sendLog Log.Fatal . fromString) $ logIO "exception: " (Log.sendLog Log.Error . fromString) $ do
-				Log.sendLog Log.Trace "accepting connection..."
-				liftIO $ signalQSem q
-				(s', addr') <- liftIO $ accept s
-				Log.sendLog Log.Trace $ "accepted {}" ~~ show addr'
-				void $ liftIO $ forkIO $ withSession session $ Log.scope (T.pack $ show addr') $
-					logAsync (Log.sendLog Log.Fatal . fromString) $ logIO "exception: " (Log.sendLog Log.Error . fromString) $
-						flip finally (liftIO $ close s') $
-							bracket (liftIO newEmptyMVar) (liftIO . (`putMVar` ())) $ \done -> do
-								me <- liftIO myThreadId
-								let
-									timeoutWait = withSession session $ do
-										notDone <- liftIO $ isEmptyMVar done
-										when notDone $ do
-											Log.sendLog Log.Trace $ "waiting for {} to complete" ~~ show addr'
-											waitAsync <- liftIO $ async $ do
-												threadDelay 1000000
-												killThread me
-											liftIO $ void $ waitCatch waitAsync
-								liftIO $ F.putChan clientChan timeoutWait
-								processClientSocket (show addr') s'
-
-	Log.sendLog Log.Trace "waiting for starting accept thread..."
-	liftIO $ waitQSem q
-	liftIO $ putStrLn $ "Server started at port {}" ~~ serverPort sopts
-	Log.sendLog Log.Info $ "server started at port {}" ~~ serverPort sopts
-	Log.sendLog Log.Trace "waiting for accept thread..."
-	serverWait
-	Log.sendLog Log.Trace "accept thread stopped"
-	liftIO $ unlink (serverPort sopts)
-	askSession sessionDatabase >>= liftIO . DB.readAsync >>= writeCache sopts
-	Log.sendLog Log.Trace "waiting for clients..."
-	liftIO (F.stopChan clientChan) >>= sequence_
-	Log.sendLog Log.Info "server stopped"
-runServerCommand (Stop copts) = runServerCommand (Remote copts False Exit)
-runServerCommand (Connect copts) = do
-	curDir <- getCurrentDirectory
-	s <- makeSocket $ clientPort copts
-	addr' <- inet_addr "127.0.0.1"
-	Net.connect s $ sockAddr (clientPort copts) addr'
-	bracket (socketToHandle s ReadWriteMode) hClose $ \h -> forM_ [(1 :: Integer)..] $ \i -> ignoreIO $ do
-		input' <- hGetLineBS stdin
-		case decodeMsg input' of
-			Left em -> L.putStrLn $ encodeMessage $ set msg (Message Nothing $ responseError $ OtherError "invalid command") em
-			Right m -> do
-				L.hPutStrLn h $ encodeMessage $ set msg (Message (Just $ show i) $ Request (view msg m) curDir True (clientTimeout copts) False) m
-				waitResp h
-	where
-		waitResp h = do
-			resp <- hGetLineBS h
-			parseResp h resp
-
-		parseResp h str = case decodeMessage str of
-			Left em -> putStrLn $ "Can't decode response: {}" ~~ view msg em
-			Right m -> do
-				Response r' <- unMmap $ view (msg . message) m
-				putStrLn $ "{id}: {response}"
-					~~ ("id" ~% fromMaybe "_" (view (msg . messageId) m))
-					~~ ("response" ~% fromUtf8 (encodeMsg $ set msg (Response r') m))
-				case unResponse (view (msg . message) m) of
-					Left _ -> waitResp h
-					_ -> return ()
-runServerCommand (Remote copts noFile c) = sendCommand copts noFile c printValue >>= printResult where
-	printValue :: ToJSON a => a -> IO ()
-	printValue = L.putStrLn . encodeValue
-	printResult :: Result -> IO ()
-	printResult (Result r) = printValue r
-	printResult e = printValue e
-	encodeValue :: ToJSON a => a -> L.ByteString
-	encodeValue = if clientPretty copts then encodePretty else encode
-
-findPath :: MonadIO m => CommandOptions -> FilePath -> m FilePath
-findPath copts f = liftIO $ canonicalizePath (normalise f') where
-	f'
-		| isRelative f = commandOptionsRoot copts </> f
-		| otherwise = f
-
-type Msg a = (Bool, a)
-
-isLisp :: Lens' (Msg a) Bool
-isLisp = _1
-
-msg :: Lens (Msg a) (Msg b) a b
-msg = _2
-
-jsonMsg :: a -> Msg a
-jsonMsg = (,) False
-
-lispMsg :: a -> Msg a
-lispMsg = (,) True
-
--- | Decode lisp or json
-decodeMsg :: FromJSON a => ByteString -> Either (Msg String) (Msg a)
-decodeMsg bstr = over _Left decodeType' decodeMsg' where
-	decodeType'
-		| isLisp' = lispMsg
-		| otherwise = jsonMsg
-	decodeMsg' = (lispMsg <$> decodeLisp bstr) <|> (jsonMsg <$> eitherDecode bstr)
-	isLisp' = fromMaybe False $ mplus (try' eitherDecode False) (try' decodeLisp True)
-	try' :: (ByteString -> Either String Value) -> Bool -> Maybe Bool
-	try' f l = either (const Nothing) (const $ Just l) $ f bstr
-
--- | Encode lisp or json
-encodeMsg :: ToJSON a => Msg a -> ByteString
-encodeMsg m
-	| view isLisp m = encodeLisp $ view msg m
-	| otherwise = encode $ view msg m
-
--- | Decode lisp or json request
-decodeMessage :: FromJSON a => ByteString -> Either (Msg String) (Msg (Message a))
-decodeMessage = decodeMsg
-
-encodeMessage :: ToJSON a => Msg (Message a) -> ByteString
-encodeMessage = encodeMsg
-
--- | Process request, notifications can be sent during processing
-processRequest :: SessionMonad m => CommandOptions -> Command -> m Result
-processRequest copts c = do
-	c' <- paths (findPath copts) c
-	s <- getSession
-	withSession s $ Client.runClient copts $ Client.runCommand c'
-
--- | Process client, listen for requests and process them
-processClient :: SessionMonad m => String -> F.Chan ByteString -> (ByteString -> IO ()) -> m ()
-processClient name rchan send' = do
-	Log.sendLog Log.Info "connected"
-	respChan <- liftIO newChan
-	liftIO $ void $ forkIO $ getChanContents respChan >>= mapM_ (send' . encodeMessage)
-	linkVar <- liftIO $ newMVar $ return ()
-	s <- getSession
-	exit <- askSession sessionExit
-	let
-		answer :: SessionMonad m => Msg (Message Response) -> m ()
-		answer m = do
-			unless (isNotification $ view (msg . message) m) $
-				Log.sendLog Log.Trace $ "responsed << {}" ~~ ellipsis (fromUtf8 (encode $ view (msg . message) m))
-			liftIO $ writeChan respChan m
-			where
-				ellipsis :: String -> String
-				ellipsis str
-					| length str < 100 = str
-					| otherwise = take 100 str ++ "..."
-	-- flip finally (disconnected linkVar) $ forever $ Log.scopeLog (commandLogger copts) (T.pack name) $ do
-	reqs <- liftIO $ F.readChan rchan
-	flip finally (disconnected linkVar) $
-		forM_ reqs $ \req' -> do
-			Log.sendLog Log.Trace $ "received >> {}" ~~ fromUtf8 req'
-			case decodeMessage req' of
-				Left em -> do
-					Log.sendLog Log.Warning $ "Invalid request {}" ~~ fromUtf8 req'
-					answer $ set msg (Message Nothing $ responseError $ RequestError "invalid request" $ fromUtf8 req') em
-				Right m -> void $ liftIO $ forkIO $ withSession s $ Log.scope (T.pack name) $ Log.scope "req" $
-					Log.scope (T.pack $ fromMaybe "_" (view (msg . messageId) m)) $ do
-						resp' <- flip (traverseOf (msg . message)) m $ \(Request c cdir noFile tm silent) -> do
-							let
-								onNotify n
-									| silent = return ()
-									| otherwise = traverseOf (msg . message) (const $ mmap' noFile (Response $ Left n)) m >>= answer
-							Log.sendLog Log.Trace $ "requested >> {}" ~~ fromUtf8 (encode c)
-							resp <- liftIO $ fmap (Response . Right) $ handleTimeout tm $ hsdevLiftIO $ withSession s $
-								processRequest
-									CommandOptions {
-										commandOptionsRoot = cdir,
-										commandOptionsNotify = withSession s . onNotify,
-										commandOptionsLink = void (swapMVar linkVar exit),
-										commandOptionsHold = forever (F.getChan rchan) }
-									c
-							mmap' noFile resp
-						answer resp'
-	where
-		handleTimeout :: Int -> IO Result -> IO Result
-		handleTimeout 0 = id
-		handleTimeout tm = fmap (fromMaybe $ Error $ OtherError "timeout") . timeout tm
-
-		mmap' :: SessionMonad m => Bool -> Response -> m Response
-#if mingw32_HOST_OS
-		mmap' False r = do
-			mpool <- askSession sessionMmapPool
-			case mpool of
-				Just pool -> liftIO $ mmap pool r
-				Nothing -> return r
-#endif
-		mmap' _ r = return r
-
-		-- Call on disconnected, either no action or exit command
-		disconnected :: SessionMonad m => MVar (IO ()) -> m ()
-		disconnected var = do
-			Log.sendLog Log.Info "disconnected"
-			liftIO $ join $ takeMVar var
-
--- | Process client by socket
-processClientSocket :: SessionMonad m => String -> Socket -> m ()
-processClientSocket name s = do
-	recvChan <- liftIO F.newChan
-	liftIO $ void $ forkIO $ finally
-		(Net.getContents s >>= mapM_ (F.putChan recvChan) . L.lines)
-		(F.closeChan recvChan)
-	processClient name recvChan (sendLine s)
-	where
-		-- NOTE: Network version of `sendAll` goes to infinite loop on client socket close
-		-- when server's send is blocked, see https://github.com/haskell/network/issues/155
-		-- After that issue fixed we may revert to `processClientHandle`
-		sendLine :: Socket -> ByteString -> IO ()
-		sendLine sock bs = sendAll sock $ L.toStrict $ L.snoc bs '\n'
-		sendAll :: Socket -> BS.ByteString -> IO ()
-		sendAll sock bs
-			| BS.null bs = return ()
-			| otherwise = do
-				sent <- Net.send sock bs
-				when (sent > 0) $ sendAll sock (BS.drop sent bs)
-
-#if mingw32_HOST_OS
-data MmapFile = MmapFile String
-
-instance ToJSON MmapFile where
-	toJSON (MmapFile f) = object ["file" .= f]
-
-instance FromJSON MmapFile where
-	parseJSON = withObject "file" $ \v -> MmapFile <$> v .:: "file"
-
--- | Push message to mmap and return response which points to this mmap
-mmap :: Pool -> Response -> IO Response
-mmap mmapPool r
-	| L.length msg' <= 1024 = return r
-	| otherwise = do
-		rvar <- newEmptyMVar
-		_ <- forkIO $ flip finally (tryPutMVar rvar r) $ void $ withName mmapPool $ \mmapName -> runExceptT $ catchError
-			(withMapFile mmapName (L.toStrict msg') $ liftIO $ do
-				_ <- tryPutMVar rvar $ result $ MmapFile mmapName
-				-- give 10 seconds for client to read data
-				threadDelay 10000000)
-			(\_ -> liftIO $ void $ tryPutMVar rvar r)
-		takeMVar rvar
-	where
-		msg' = encode r
-#endif
-
--- | If response points to mmap, get its contents and parse
-unMmap :: Response -> IO Response
-#if mingw32_HOST_OS
-unMmap (Response (Right (Result v)))
-	| Just (MmapFile f) <- parseMaybe parseJSON v = do
-		cts <- runExceptT (fmap L.fromStrict (readMapFile f))
-		case cts of
-			Left _ -> return $ responseError $ ResponseError "can't read map view of file" f
-			Right r' -> case eitherDecode r' of
-				Left e' -> return $ responseError $ ResponseError ("can't parse response: {}" ~~ e') (fromUtf8 r')
-				Right r'' -> return r''
-#endif
-unMmap r = return r
-
-makeSocket :: ConnectionPort -> IO Socket
-makeSocket (NetworkPort _) = socket AF_INET Stream defaultProtocol
-makeSocket (UnixPort _) = socket AF_UNIX Stream defaultProtocol
-
-sockAddr :: ConnectionPort -> HostAddress -> SockAddr
-sockAddr (NetworkPort p) addr = SockAddrInet (fromIntegral p) addr
-sockAddr (UnixPort s) _ = SockAddrUnix s
-
-unlink :: ConnectionPort -> IO ()
-unlink (NetworkPort _) = return ()
-#if mingw32_HOST_OS
-unlink (UnixPort _) = return ()
-#else
-unlink (UnixPort s) = removeLink s
-#endif
+{-# LANGUAGE OverloadedStrings, CPP, PatternGuards, LambdaCase, TemplateHaskell #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Server.Commands (
+	ServerCommand(..), ServerOpts(..), ClientOpts(..),
+	Request(..),
+	Msg, isLisp, msg, jsonMsg, lispMsg, encodeMessage, decodeMessage,
+	sendCommand, runServerCommand,
+	findPath,
+	processRequest, processClient, processClientSocket,
+	module HsDev.Server.Types
+	) where
+
+import Control.Applicative
+import Control.Concurrent
+import Control.Concurrent.Async
+import Control.Lens (set, traverseOf, view, over, Lens', Lens, _1, _2, _Left)
+import Control.Monad
+import Control.Monad.Catch (bracket, finally)
+import Data.Aeson hiding (Result, Error)
+import Data.Aeson.Encode.Pretty
+import qualified Data.ByteString.Char8 as BS
+import Data.ByteString.Lazy.Char8 (ByteString)
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Maybe
+import Data.String (fromString)
+import qualified Data.Text as T (pack)
+import Network.Socket hiding (connect)
+import qualified Network.Socket as Net hiding (send)
+import qualified Network.Socket.ByteString as Net (send)
+import qualified Network.Socket.ByteString.Lazy as Net (getContents)
+import System.Directory
+import System.Exit
+import System.FilePath
+import System.IO
+import qualified System.Log.Simple as Log
+
+import Control.Concurrent.Util
+import qualified Control.Concurrent.FiniteChan as F
+import Data.Lisp
+import Text.Format ((~~), (~%))
+import System.Directory.Paths
+
+import qualified HsDev.Client.Commands as Client
+import qualified HsDev.Database.Async as DB
+import HsDev.Server.Base
+import HsDev.Server.Types
+import HsDev.Tools.Base (runTool_)
+import HsDev.Error
+import HsDev.Util
+import HsDev.Version
+
+#if mingw32_HOST_OS
+import Data.Aeson.Types hiding (Result, Error)
+import Data.Char
+import Data.List
+import System.Environment
+import System.Win32.FileMapping.Memory (withMapFile, readMapFile)
+import System.Win32.FileMapping.NamePool
+import System.Win32.PowerShell (escape, quote, quoteDouble)
+#else
+import Control.Exception (SomeException, handle)
+import System.Posix.Process
+import System.Posix.Files (removeLink)
+import System.Posix.IO
+#endif
+
+sendCommand :: ClientOpts -> Bool -> Command -> (Notification -> IO a) -> IO Result
+sendCommand copts noFile c onNotification = do
+	asyncAct <- async sendReceive
+	res <- waitCatch asyncAct
+	case res of
+		Left e -> return $ Error $ OtherError (show e)
+		Right r -> return r
+	where
+		sendReceive = do
+			curDir <- getCurrentDirectory
+			input <- if clientStdin copts
+				then Just <$> L.getContents
+				else return $ toUtf8 <$> Nothing -- arg "data" copts
+			let
+				parseData :: L.ByteString -> IO Value
+				parseData cts = case eitherDecode cts of
+					Left err -> putStrLn ("Invalid data: " ++ err) >> exitFailure
+					Right v -> return v
+			_ <- traverse parseData input -- FIXME: Not used!
+
+			s <- makeSocket (clientPort copts)
+			addr' <- inet_addr "127.0.0.1"
+			Net.connect s (sockAddr (clientPort copts) addr')
+			bracket (socketToHandle s ReadWriteMode) hClose $ \h -> do
+				L.hPutStrLn h $ encode $ Message Nothing $ Request c curDir noFile (clientTimeout copts) (clientSilent copts)
+				hFlush h
+				peekResponse h
+
+		peekResponse h = do
+			resp <- hGetLineBS h
+			parseResponse h resp
+
+		parseResponse h str = case eitherDecode str of
+			Left e -> return $ Error $ ResponseError ("can't parse: {}" ~~ e) (fromUtf8 str)
+			Right (Message _ r) -> do
+				Response r' <- unMmap r
+				case r' of
+					Left n -> onNotification n >> peekResponse h
+					Right res -> return res
+
+runServerCommand :: ServerCommand -> IO ()
+runServerCommand Version = putStrLn $cabalVersion
+runServerCommand (Start sopts) = do
+#if mingw32_HOST_OS
+	let
+		args = "run" : serverOptsArgs sopts
+	myExe <- getExecutablePath
+	curDir <- getCurrentDirectory
+	let
+		-- one escape for start-process and other for callable process
+		-- seems, that start-process just concats arguments into one string
+		-- start-process foo 'bar baz' ⇒ foo bar baz -- not expected
+		-- start-process foo '"bar baz"' ⇒ foo "bar baz" -- ok
+		biescape = escape quote . escape quoteDouble
+		script = "try {{ start-process {process} {args} -WindowStyle Hidden -WorkingDirectory {dir} }} catch {{ $_.Exception, $_.InvocationInfo.Line }}"
+			~~ ("process" ~% escape quote myExe)
+			~~ ("args" ~% intercalate ", " (map biescape args))
+			~~ ("dir" ~% escape quote curDir)
+	r <- runTool_ "powershell" [
+		"-Command",
+		script]
+	if all isSpace r
+		then putStrLn $ "Server started at port {}" ~~ serverPort sopts
+		else mapM_ putStrLn [
+			"Failed to start server",
+			"\tCommand: {}" ~~ script,
+			"\tResult: {}" ~~ r]
+#else
+	let
+		forkError :: SomeException -> IO ()
+		forkError e  = putStrLn $ "Failed to start server: {}" ~~ show e
+
+		proxy :: IO ()
+		proxy = do
+			_ <- createSession
+			_ <- forkProcess serverAction
+			exitImmediately ExitSuccess
+
+		serverAction :: IO ()
+		serverAction = do
+			mapM_ closeFd [stdInput, stdOutput, stdError]
+			nullFd <- openFd "/dev/null" ReadWrite Nothing defaultFileFlags
+			mapM_ (dupTo nullFd) [stdInput, stdOutput, stdError]
+			closeFd nullFd
+			runServerCommand (Run sopts)
+
+	handle forkError $ do
+		_ <- forkProcess proxy
+		putStrLn $ "Server started at port {}" ~~ serverPort sopts
+#endif
+runServerCommand (Run sopts) = runServer sopts $ do
+	q <- liftIO $ newQSem 0
+	clientChan <- liftIO F.newChan
+	session <- getSession
+	_ <- liftIO $ async $ withSession session $ Log.scope "listener" $ flip finally serverExit $
+		bracket (liftIO $ makeSocket (serverPort sopts)) (liftIO . close) $ \s -> do
+			liftIO $ do
+				setSocketOption s ReuseAddr 1
+				addr' <- inet_addr "127.0.0.1"
+				bind s (sockAddr (serverPort sopts) addr')
+				listen s maxListenQueue
+			forever $ logAsync (Log.sendLog Log.Fatal . fromString) $ logIO "exception: " (Log.sendLog Log.Error . fromString) $ do
+				Log.sendLog Log.Trace "accepting connection..."
+				liftIO $ signalQSem q
+				(s', addr') <- liftIO $ accept s
+				Log.sendLog Log.Trace $ "accepted {}" ~~ show addr'
+				void $ liftIO $ forkIO $ withSession session $ Log.scope (T.pack $ show addr') $
+					logAsync (Log.sendLog Log.Fatal . fromString) $ logIO "exception: " (Log.sendLog Log.Error . fromString) $
+						flip finally (liftIO $ close s') $
+							bracket (liftIO newEmptyMVar) (liftIO . (`putMVar` ())) $ \done -> do
+								me <- liftIO myThreadId
+								let
+									timeoutWait = withSession session $ do
+										notDone <- liftIO $ isEmptyMVar done
+										when notDone $ do
+											Log.sendLog Log.Trace $ "waiting for {} to complete" ~~ show addr'
+											waitAsync <- liftIO $ async $ do
+												threadDelay 1000000
+												killThread me
+											liftIO $ void $ waitCatch waitAsync
+								liftIO $ F.putChan clientChan timeoutWait
+								processClientSocket (show addr') s'
+
+	Log.sendLog Log.Trace "waiting for starting accept thread..."
+	liftIO $ waitQSem q
+	liftIO $ putStrLn $ "Server started at port {}" ~~ serverPort sopts
+	Log.sendLog Log.Info $ "server started at port {}" ~~ serverPort sopts
+	Log.sendLog Log.Trace "waiting for accept thread..."
+	serverWait
+	Log.sendLog Log.Trace "accept thread stopped"
+	liftIO $ unlink (serverPort sopts)
+	askSession sessionDatabase >>= liftIO . DB.readAsync >>= writeCache sopts
+	Log.sendLog Log.Trace "waiting for clients..."
+	liftIO (F.stopChan clientChan) >>= sequence_
+	Log.sendLog Log.Info "server stopped"
+runServerCommand (Stop copts) = runServerCommand (Remote copts False Exit)
+runServerCommand (Connect copts) = do
+	curDir <- getCurrentDirectory
+	s <- makeSocket $ clientPort copts
+	addr' <- inet_addr "127.0.0.1"
+	Net.connect s $ sockAddr (clientPort copts) addr'
+	bracket (socketToHandle s ReadWriteMode) hClose $ \h -> forM_ [(1 :: Integer)..] $ \i -> ignoreIO $ do
+		input' <- hGetLineBS stdin
+		case decodeMsg input' of
+			Left em -> L.putStrLn $ encodeMessage $ set msg (Message Nothing $ responseError $ OtherError "invalid command") em
+			Right m -> do
+				L.hPutStrLn h $ encodeMessage $ set msg (Message (Just $ show i) $ Request (view msg m) curDir True (clientTimeout copts) False) m
+				waitResp h
+	where
+		waitResp h = do
+			resp <- hGetLineBS h
+			parseResp h resp
+
+		parseResp h str = case decodeMessage str of
+			Left em -> putStrLn $ "Can't decode response: {}" ~~ view msg em
+			Right m -> do
+				Response r' <- unMmap $ view (msg . message) m
+				putStrLn $ "{id}: {response}"
+					~~ ("id" ~% fromMaybe "_" (view (msg . messageId) m))
+					~~ ("response" ~% fromUtf8 (encodeMsg $ set msg (Response r') m))
+				case unResponse (view (msg . message) m) of
+					Left _ -> waitResp h
+					_ -> return ()
+runServerCommand (Remote copts noFile c) = sendCommand copts noFile c printValue >>= printResult where
+	printValue :: ToJSON a => a -> IO ()
+	printValue = L.putStrLn . encodeValue
+	printResult :: Result -> IO ()
+	printResult (Result r) = printValue r
+	printResult e = printValue e
+	encodeValue :: ToJSON a => a -> L.ByteString
+	encodeValue = if clientPretty copts then encodePretty else encode
+
+findPath :: MonadIO m => CommandOptions -> FilePath -> m FilePath
+findPath copts f = liftIO $ canonicalizePath (normalise f') where
+	f'
+		| isRelative f = commandOptionsRoot copts </> f
+		| otherwise = f
+
+type Msg a = (Bool, a)
+
+isLisp :: Lens' (Msg a) Bool
+isLisp = _1
+
+msg :: Lens (Msg a) (Msg b) a b
+msg = _2
+
+jsonMsg :: a -> Msg a
+jsonMsg = (,) False
+
+lispMsg :: a -> Msg a
+lispMsg = (,) True
+
+-- | Decode lisp or json
+decodeMsg :: FromJSON a => ByteString -> Either (Msg String) (Msg a)
+decodeMsg bstr = over _Left decodeType' decodeMsg' where
+	decodeType'
+		| isLisp' = lispMsg
+		| otherwise = jsonMsg
+	decodeMsg' = (lispMsg <$> decodeLisp bstr) <|> (jsonMsg <$> eitherDecode bstr)
+	isLisp' = fromMaybe False $ mplus (try' eitherDecode False) (try' decodeLisp True)
+	try' :: (ByteString -> Either String Value) -> Bool -> Maybe Bool
+	try' f l = either (const Nothing) (const $ Just l) $ f bstr
+
+-- | Encode lisp or json
+encodeMsg :: ToJSON a => Msg a -> ByteString
+encodeMsg m
+	| view isLisp m = encodeLisp $ view msg m
+	| otherwise = encode $ view msg m
+
+-- | Decode lisp or json request
+decodeMessage :: FromJSON a => ByteString -> Either (Msg String) (Msg (Message a))
+decodeMessage = decodeMsg
+
+encodeMessage :: ToJSON a => Msg (Message a) -> ByteString
+encodeMessage = encodeMsg
+
+-- | Process request, notifications can be sent during processing
+processRequest :: SessionMonad m => CommandOptions -> Command -> m Result
+processRequest copts c = do
+	c' <- paths (findPath copts) c
+	s <- getSession
+	withSession s $ Client.runClient copts $ Client.runCommand c'
+
+-- | Process client, listen for requests and process them
+processClient :: SessionMonad m => String -> F.Chan ByteString -> (ByteString -> IO ()) -> m ()
+processClient name rchan send' = do
+	Log.sendLog Log.Info "connected"
+	respChan <- liftIO newChan
+	liftIO $ void $ forkIO $ getChanContents respChan >>= mapM_ (send' . encodeMessage)
+	linkVar <- liftIO $ newMVar $ return ()
+	s <- getSession
+	exit <- askSession sessionExit
+	let
+		answer :: SessionMonad m => Msg (Message Response) -> m ()
+		answer m = do
+			unless (isNotification $ view (msg . message) m) $
+				Log.sendLog Log.Trace $ "responsed << {}" ~~ ellipsis (fromUtf8 (encode $ view (msg . message) m))
+			liftIO $ writeChan respChan m
+			where
+				ellipsis :: String -> String
+				ellipsis str
+					| length str < 100 = str
+					| otherwise = take 100 str ++ "..."
+	-- flip finally (disconnected linkVar) $ forever $ Log.scopeLog (commandLogger copts) (T.pack name) $ do
+	reqs <- liftIO $ F.readChan rchan
+	flip finally (disconnected linkVar) $
+		forM_ reqs $ \req' -> do
+			Log.sendLog Log.Trace $ "received >> {}" ~~ fromUtf8 req'
+			case decodeMessage req' of
+				Left em -> do
+					Log.sendLog Log.Warning $ "Invalid request {}" ~~ fromUtf8 req'
+					answer $ set msg (Message Nothing $ responseError $ RequestError "invalid request" $ fromUtf8 req') em
+				Right m -> void $ liftIO $ forkIO $ withSession s $ Log.scope (T.pack name) $ Log.scope "req" $
+					Log.scope (T.pack $ fromMaybe "_" (view (msg . messageId) m)) $ do
+						resp' <- flip (traverseOf (msg . message)) m $ \(Request c cdir noFile tm silent) -> do
+							let
+								onNotify n
+									| silent = return ()
+									| otherwise = traverseOf (msg . message) (const $ mmap' noFile (Response $ Left n)) m >>= answer
+							Log.sendLog Log.Trace $ "requested >> {}" ~~ fromUtf8 (encode c)
+							resp <- liftIO $ fmap (Response . Right) $ handleTimeout tm $ hsdevLiftIO $ withSession s $
+								processRequest
+									CommandOptions {
+										commandOptionsRoot = cdir,
+										commandOptionsNotify = withSession s . onNotify,
+										commandOptionsLink = void (swapMVar linkVar exit),
+										commandOptionsHold = forever (F.getChan rchan) }
+									c
+							mmap' noFile resp
+						answer resp'
+	where
+		handleTimeout :: Int -> IO Result -> IO Result
+		handleTimeout 0 = id
+		handleTimeout tm = fmap (fromMaybe $ Error $ OtherError "timeout") . timeout tm
+
+		mmap' :: SessionMonad m => Bool -> Response -> m Response
+#if mingw32_HOST_OS
+		mmap' False r = do
+			mpool <- askSession sessionMmapPool
+			case mpool of
+				Just pool -> liftIO $ mmap pool r
+				Nothing -> return r
+#endif
+		mmap' _ r = return r
+
+		-- Call on disconnected, either no action or exit command
+		disconnected :: SessionMonad m => MVar (IO ()) -> m ()
+		disconnected var = do
+			Log.sendLog Log.Info "disconnected"
+			liftIO $ join $ takeMVar var
+
+-- | Process client by socket
+processClientSocket :: SessionMonad m => String -> Socket -> m ()
+processClientSocket name s = do
+	recvChan <- liftIO F.newChan
+	liftIO $ void $ forkIO $ finally
+		(Net.getContents s >>= mapM_ (F.putChan recvChan) . L.lines)
+		(F.closeChan recvChan)
+	processClient name recvChan (sendLine s)
+	where
+		-- NOTE: Network version of `sendAll` goes to infinite loop on client socket close
+		-- when server's send is blocked, see https://github.com/haskell/network/issues/155
+		-- After that issue fixed we may revert to `processClientHandle`
+		sendLine :: Socket -> ByteString -> IO ()
+		sendLine sock bs = sendAll sock $ L.toStrict $ L.snoc bs '\n'
+		sendAll :: Socket -> BS.ByteString -> IO ()
+		sendAll sock bs
+			| BS.null bs = return ()
+			| otherwise = do
+				sent <- Net.send sock bs
+				when (sent > 0) $ sendAll sock (BS.drop sent bs)
+
+#if mingw32_HOST_OS
+data MmapFile = MmapFile String
+
+instance ToJSON MmapFile where
+	toJSON (MmapFile f) = object ["file" .= f]
+
+instance FromJSON MmapFile where
+	parseJSON = withObject "file" $ \v -> MmapFile <$> v .:: "file"
+
+-- | Push message to mmap and return response which points to this mmap
+mmap :: Pool -> Response -> IO Response
+mmap mmapPool r
+	| L.length msg' <= 1024 = return r
+	| otherwise = do
+		rvar <- newEmptyMVar
+		_ <- forkIO $ flip finally (tryPutMVar rvar r) $ void $ withName mmapPool $ \mmapName -> runExceptT $ catchError
+			(withMapFile mmapName (L.toStrict msg') $ liftIO $ do
+				_ <- tryPutMVar rvar $ result $ MmapFile mmapName
+				-- give 10 seconds for client to read data
+				threadDelay 10000000)
+			(\_ -> liftIO $ void $ tryPutMVar rvar r)
+		takeMVar rvar
+	where
+		msg' = encode r
+#endif
+
+-- | If response points to mmap, get its contents and parse
+unMmap :: Response -> IO Response
+#if mingw32_HOST_OS
+unMmap (Response (Right (Result v)))
+	| Just (MmapFile f) <- parseMaybe parseJSON v = do
+		cts <- runExceptT (fmap L.fromStrict (readMapFile f))
+		case cts of
+			Left _ -> return $ responseError $ ResponseError "can't read map view of file" f
+			Right r' -> case eitherDecode r' of
+				Left e' -> return $ responseError $ ResponseError ("can't parse response: {}" ~~ e') (fromUtf8 r')
+				Right r'' -> return r''
+#endif
+unMmap r = return r
+
+makeSocket :: ConnectionPort -> IO Socket
+makeSocket (NetworkPort _) = socket AF_INET Stream defaultProtocol
+makeSocket (UnixPort _) = socket AF_UNIX Stream defaultProtocol
+
+sockAddr :: ConnectionPort -> HostAddress -> SockAddr
+sockAddr (NetworkPort p) addr = SockAddrInet (fromIntegral p) addr
+sockAddr (UnixPort s) _ = SockAddrUnix s
+
+unlink :: ConnectionPort -> IO ()
+unlink (NetworkPort _) = return ()
+#if mingw32_HOST_OS
+unlink (UnixPort _) = return ()
+#else
+unlink (UnixPort s) = removeLink s
+#endif
diff --git a/src/HsDev/Server/Message.hs b/src/HsDev/Server/Message.hs
--- a/src/HsDev/Server/Message.hs
+++ b/src/HsDev/Server/Message.hs
@@ -1,115 +1,115 @@
-{-# LANGUAGE OverloadedStrings, DeriveFunctor, TypeSynonymInstances, FlexibleInstances, TemplateHaskell #-}
-
-module HsDev.Server.Message (
-	Message(..), messageId, message,
-	messagesById,
-	Notification(..), Result(..), ResultPart(..),
-	Response(..), isNotification, notification, result, responseError, resultPart,
-	groupResponses, responsesById
-	) where
-
-import Control.Applicative
-import Control.Lens (makeLenses)
-import Control.Monad (join)
-import Data.Aeson hiding (Error, Result)
-import Data.Either (lefts, isRight)
-import Data.List (unfoldr)
-
-import HsDev.Types
-import HsDev.Util ((.::), (.::?), objectUnion)
-
--- | Message with id to link request and response
-data Message a = Message {
-	_messageId :: Maybe String,
-	_message :: a }
-		deriving (Eq, Ord, Show, Functor)
-
-makeLenses ''Message
-
-instance ToJSON a => ToJSON (Message a) where
-	toJSON (Message i m) = object ["id" .= i] `objectUnion` toJSON m
-
-instance FromJSON a => FromJSON (Message a) where
-	parseJSON = withObject "message" $ \v ->
-		Message <$> fmap join (v .::? "id") <*> parseJSON (Object v)
-
-instance Foldable Message where
-	foldMap f (Message _ m) = f m
-
-instance Traversable Message where
-	traverse f (Message i m) = Message i <$> f m
-
--- | Get messages by id
-messagesById :: Maybe String -> [Message a] -> [a]
-messagesById i = map _message . filter ((== i) . _messageId)
-
--- | Notification from server
-data Notification = Notification Value deriving (Eq, Show)
-
-instance ToJSON Notification where
-	toJSON (Notification v) = object ["notify" .= v]
-
-instance FromJSON Notification where
-	parseJSON = withObject "notification" $ \v -> Notification <$> v .:: "notify"
-
--- | Result from server
-data Result =
-	Result Value |
-	-- ^ Result
-	Error HsDevError
-	-- ^ Error
-		deriving (Show)
-
-instance ToJSON Result where
-	toJSON (Result r) = object ["result" .= r]
-	toJSON (Error e) = toJSON e
-
-instance FromJSON Result where
-	parseJSON j = (withObject "result" (\v -> (Result <$> v .:: "result")) j) <|> (Error <$> parseJSON j)
-
--- | Part of result list, returns via notification
-data ResultPart = ResultPart Value
-
-instance ToJSON ResultPart where
-	toJSON (ResultPart r) = object ["result-part" .= r]
-
-instance FromJSON ResultPart where
-	parseJSON = withObject "result-part" $ \v -> ResultPart <$> v .:: "result-part"
-
-newtype Response = Response { unResponse :: Either Notification Result } deriving (Show)
-
-isNotification :: Response -> Bool
-isNotification = either (const True) (const False) . unResponse
-
-notification :: ToJSON a => a -> Response
-notification = Response . Left . Notification . toJSON
-
-result :: ToJSON a => a -> Response
-result = Response . Right . Result . toJSON
-
-responseError :: HsDevError -> Response
-responseError = Response . Right . Error
-
-resultPart :: ToJSON a => a  -> Notification
-resultPart = Notification . toJSON . ResultPart . toJSON
-
-instance ToJSON Response where
-	toJSON (Response (Left n)) = toJSON n
-	toJSON (Response (Right r)) = toJSON r
-
-instance FromJSON Response where
-	parseJSON v = Response <$> ((Left <$> parseJSON v) <|> (Right <$> parseJSON v))
-
-groupResponses :: [Response] -> [([Notification], Result)]
-groupResponses = unfoldr break' where
-	break' :: [Response] -> Maybe (([Notification], Result), [Response])
-	break' [] = Nothing
-	break' cs =  Just ((lefts (map unResponse ns), r), drop 1 cs') where
-		(ns, cs') = break (isRight . unResponse) cs
-		r = case cs' of
-			(Response (Right r') : _) -> r'
-			[] -> Error $ OtherError "groupResponses: no result"
-			_ -> error "groupResponses: impossible happened"
-
-responsesById :: Maybe String -> [Message Response] -> [([Notification], Result)]
-responsesById i = groupResponses . messagesById i
+{-# LANGUAGE OverloadedStrings, DeriveFunctor, TypeSynonymInstances, FlexibleInstances, TemplateHaskell #-}
+
+module HsDev.Server.Message (
+	Message(..), messageId, message,
+	messagesById,
+	Notification(..), Result(..), ResultPart(..),
+	Response(..), isNotification, notification, result, responseError, resultPart,
+	groupResponses, responsesById
+	) where
+
+import Control.Applicative
+import Control.Lens (makeLenses)
+import Control.Monad (join)
+import Data.Aeson hiding (Error, Result)
+import Data.Either (lefts, isRight)
+import Data.List (unfoldr)
+
+import HsDev.Types
+import HsDev.Util ((.::), (.::?), objectUnion)
+
+-- | Message with id to link request and response
+data Message a = Message {
+	_messageId :: Maybe String,
+	_message :: a }
+		deriving (Eq, Ord, Show, Functor)
+
+makeLenses ''Message
+
+instance ToJSON a => ToJSON (Message a) where
+	toJSON (Message i m) = object ["id" .= i] `objectUnion` toJSON m
+
+instance FromJSON a => FromJSON (Message a) where
+	parseJSON = withObject "message" $ \v ->
+		Message <$> fmap join (v .::? "id") <*> parseJSON (Object v)
+
+instance Foldable Message where
+	foldMap f (Message _ m) = f m
+
+instance Traversable Message where
+	traverse f (Message i m) = Message i <$> f m
+
+-- | Get messages by id
+messagesById :: Maybe String -> [Message a] -> [a]
+messagesById i = map _message . filter ((== i) . _messageId)
+
+-- | Notification from server
+data Notification = Notification Value deriving (Eq, Show)
+
+instance ToJSON Notification where
+	toJSON (Notification v) = object ["notify" .= v]
+
+instance FromJSON Notification where
+	parseJSON = withObject "notification" $ \v -> Notification <$> v .:: "notify"
+
+-- | Result from server
+data Result =
+	Result Value |
+	-- ^ Result
+	Error HsDevError
+	-- ^ Error
+		deriving (Show)
+
+instance ToJSON Result where
+	toJSON (Result r) = object ["result" .= r]
+	toJSON (Error e) = toJSON e
+
+instance FromJSON Result where
+	parseJSON j = (withObject "result" (\v -> (Result <$> v .:: "result")) j) <|> (Error <$> parseJSON j)
+
+-- | Part of result list, returns via notification
+data ResultPart = ResultPart Value
+
+instance ToJSON ResultPart where
+	toJSON (ResultPart r) = object ["result-part" .= r]
+
+instance FromJSON ResultPart where
+	parseJSON = withObject "result-part" $ \v -> ResultPart <$> v .:: "result-part"
+
+newtype Response = Response { unResponse :: Either Notification Result } deriving (Show)
+
+isNotification :: Response -> Bool
+isNotification = either (const True) (const False) . unResponse
+
+notification :: ToJSON a => a -> Response
+notification = Response . Left . Notification . toJSON
+
+result :: ToJSON a => a -> Response
+result = Response . Right . Result . toJSON
+
+responseError :: HsDevError -> Response
+responseError = Response . Right . Error
+
+resultPart :: ToJSON a => a  -> Notification
+resultPart = Notification . toJSON . ResultPart . toJSON
+
+instance ToJSON Response where
+	toJSON (Response (Left n)) = toJSON n
+	toJSON (Response (Right r)) = toJSON r
+
+instance FromJSON Response where
+	parseJSON v = Response <$> ((Left <$> parseJSON v) <|> (Right <$> parseJSON v))
+
+groupResponses :: [Response] -> [([Notification], Result)]
+groupResponses = unfoldr break' where
+	break' :: [Response] -> Maybe (([Notification], Result), [Response])
+	break' [] = Nothing
+	break' cs =  Just ((lefts (map unResponse ns), r), drop 1 cs') where
+		(ns, cs') = break (isRight . unResponse) cs
+		r = case cs' of
+			(Response (Right r') : _) -> r'
+			[] -> Error $ OtherError "groupResponses: no result"
+			_ -> error "groupResponses: impossible happened"
+
+responsesById :: Maybe String -> [Message Response] -> [([Notification], Result)]
+responsesById i = groupResponses . messagesById i
diff --git a/src/HsDev/Server/Types.hs b/src/HsDev/Server/Types.hs
--- a/src/HsDev/Server/Types.hs
+++ b/src/HsDev/Server/Types.hs
@@ -1,792 +1,792 @@
-{-# LANGUAGE OverloadedStrings, CPP, TypeSynonymInstances, FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts, UndecidableInstances, MultiParamTypeClasses, TypeFamilies, ConstraintKinds #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Server.Types (
-	ServerMonadBase,
-	SessionLog(..), Session(..), SessionMonad(..), askSession, ServerM(..),
-	CommandOptions(..), CommandMonad(..), askOptions, ClientM(..),
-	withSession, serverListen, serverSetLogLevel, serverWait, serverUpdateDB, serverWriteCache, serverReadCache, inSessionGhc, serverExit, commandRoot, commandNotify, commandLink, commandHold,
-	ServerCommand(..), ConnectionPort(..), ServerOpts(..), silentOpts, ClientOpts(..), serverOptsArgs, Request(..),
-
-	Command(..), AddedContents(..),
-	AutoFixCommand(..),
-	FileSource(..), TargetFilter(..), SearchQuery(..), SearchType(..),
-	FromCmd(..),
-	) where
-
-import Control.Applicative
-import Control.Concurrent.Worker
-import Control.Lens (each, view, set)
-import Control.Monad.Base
-import Control.Monad.Catch
-import Control.Monad.Except
-import Control.Monad.Reader
-import Control.Monad.Trans.Control
-import Data.Aeson hiding (Result(..), Error)
-import qualified Data.Aeson.Types as A
-import qualified Data.ByteString.Lazy.Char8 as L
-import Data.Default
-import Data.Maybe (fromMaybe)
-import Data.Monoid
-import Data.Foldable (asum)
-import Options.Applicative
-import System.Log.Simple
-
-import System.Directory.Paths
-import Text.Format (Formattable(..))
-
-import HsDev.Database
-import qualified HsDev.Database.Async as DB
-import HsDev.Error (hsdevError)
-import HsDev.Project
-import HsDev.Symbols
-import HsDev.Server.Message
-import HsDev.Watcher.Types (Watcher)
-import HsDev.Tools.Ghc.Worker (GhcWorker, GhcM)
-import HsDev.Tools.Types (Note, OutputMessage)
-import HsDev.Tools.AutoFix (Correction)
-import HsDev.Types (HsDevError(..))
-import HsDev.Util
-
-#if mingw32_HOST_OS
-import System.Win32.FileMapping.NamePool (Pool)
-#endif
-
-type ServerMonadBase m = (MonadIO m, MonadMask m, MonadBaseControl IO m, Alternative m, MonadPlus m)
-
-data SessionLog = SessionLog {
-	sessionLogger :: Log,
-	sessionListenLog :: IO [String],
-	sessionLogWait :: IO () }
-
-data Session = Session {
-	sessionDatabase :: DB.Async Database,
-	sessionWriteCache :: Database -> ServerM IO (),
-	sessionReadCache :: (FilePath -> ExceptT String IO Structured) -> ServerM IO (Maybe Database),
-	sessionLog :: SessionLog,
-	sessionWatcher :: Watcher,
-#if mingw32_HOST_OS
-	sessionMmapPool :: Maybe Pool,
-#endif
-	sessionGhc :: GhcWorker,
-	sessionExit :: IO (),
-	sessionWait :: IO (),
-	sessionDefines :: [(String, String)] }
-
-class (ServerMonadBase m, MonadLog m) => SessionMonad m where
-	getSession :: m Session
-
-askSession :: SessionMonad m => (Session -> a) -> m a
-askSession f = liftM f getSession
-
-newtype ServerM m a = ServerM { runServerM :: ReaderT Session m a }
-	deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadReader Session, MonadTrans, MonadThrow, MonadCatch, MonadMask)
-
-instance (MonadIO m, MonadMask m) => MonadLog (ServerM m) where
-	askLog = ServerM $ asks (sessionLogger . sessionLog)
-	localLog fn = ServerM . local setLog' . runServerM where
-		setLog' sess = sess { sessionLog = (sessionLog sess) { sessionLogger = fn (sessionLogger (sessionLog sess)) } }
-
-instance ServerMonadBase m => SessionMonad (ServerM m) where
-	getSession = ask
-
-instance MonadBase b m => MonadBase b (ServerM m) where
-	liftBase = ServerM . liftBase
-
-instance MonadBaseControl b m => MonadBaseControl b (ServerM m) where
-	type StM (ServerM m) a = StM (ReaderT Session m) a
-	liftBaseWith f = ServerM $ liftBaseWith (\f' -> f (f' . runServerM))
-	restoreM = ServerM . restoreM
-
-data CommandOptions = CommandOptions {
-	commandOptionsRoot :: FilePath,
-	commandOptionsNotify :: Notification -> IO (),
-	commandOptionsLink :: IO (),
-	commandOptionsHold :: IO () }
-
-instance Default CommandOptions where
-	def = CommandOptions "." (const $ return ()) (return ()) (return ())
-
-class (SessionMonad m, MonadPlus m) => CommandMonad m where
-	getOptions :: m CommandOptions
-
-askOptions :: CommandMonad m => (CommandOptions -> a) -> m a
-askOptions f = liftM f getOptions
-
-newtype ClientM m a = ClientM { runClientM :: ServerM (ReaderT CommandOptions m) a }
-	deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadThrow, MonadCatch, MonadMask)
-
-instance MonadTrans ClientM where
-	lift = ClientM . lift . lift
-
-instance (MonadIO m, MonadMask m) => MonadLog (ClientM m) where
-	askLog = ClientM askLog
-	localLog fn = ClientM . localLog fn . runClientM
-
-instance ServerMonadBase m => SessionMonad (ClientM m) where
-	getSession = ClientM getSession
-
-instance ServerMonadBase m => CommandMonad (ClientM m) where
-	getOptions = ClientM $ lift ask
-
-instance MonadBase b m => MonadBase b (ClientM m) where
-	liftBase = ClientM . liftBase
-
-instance MonadBaseControl b m => MonadBaseControl b (ClientM m) where
-	type StM (ClientM m) a = StM (ServerM (ReaderT CommandOptions m)) a
-	liftBaseWith f = ClientM $ liftBaseWith (\f' -> f (f' . runClientM))
-	restoreM = ClientM . restoreM
-
--- | Run action on session
-withSession :: Session -> ServerM m a -> m a
-withSession s act = runReaderT (runServerM act) s
-
--- | Listen server's log
-serverListen :: SessionMonad m => m [String]
-serverListen = join . liftM liftIO $ askSession (sessionListenLog . sessionLog)
-
--- | Set server's log config
-serverSetLogLevel :: SessionMonad m => Level -> m Level
-serverSetLogLevel lev = do
-	l <- askSession (sessionLogger . sessionLog)
-	cfg <- updateLogConfig l (set (componentCfg "") (Just lev))
-	return $ fromMaybe def $ view (componentCfg "") cfg
-
--- | Wait for server
-serverWait :: SessionMonad m => m ()
-serverWait = join . liftM liftIO $ askSession sessionWait
-
--- | Update database
-serverUpdateDB :: SessionMonad m => Database -> m ()
-serverUpdateDB db = askSession sessionDatabase >>= (`DB.update` return db)
-
--- | Server write cache
-serverWriteCache :: SessionMonad m => Database -> m ()
-serverWriteCache db = do
-	s <- getSession
-	write' <- askSession sessionWriteCache
-	liftIO $ withSession s $ write' db
-
--- | Server read cache
-serverReadCache :: SessionMonad m => (FilePath -> ExceptT String IO Structured) -> m (Maybe Database)
-serverReadCache act = do
-	s <- getSession
-	read' <- askSession sessionReadCache
-	liftIO $ withSession s $ read' act
-
--- | In ghc session
-inSessionGhc :: SessionMonad m => GhcM a -> m a
-inSessionGhc act = do
-	ghcw <- askSession sessionGhc
-	inWorkerWith (hsdevError . GhcError . displayException) ghcw act
-
--- | Exit session
-serverExit :: SessionMonad m => m ()
-serverExit = join . liftM liftIO $ askSession sessionExit
-
-commandRoot :: CommandMonad m => m FilePath
-commandRoot = askOptions commandOptionsRoot
-
-commandNotify :: CommandMonad m => Notification -> m ()
-commandNotify n = join . liftM liftIO $ askOptions commandOptionsNotify <*> pure n
-
-commandLink :: CommandMonad m => m ()
-commandLink = join . liftM liftIO $ askOptions commandOptionsLink
-
-commandHold :: CommandMonad m => m ()
-commandHold = join . liftM liftIO $ askOptions commandOptionsHold
-
--- | Server control command
-data ServerCommand =
-	Version |
-	Start ServerOpts |
-	Run ServerOpts |
-	Stop ClientOpts |
-	Connect ClientOpts |
-	Remote ClientOpts Bool Command
-		deriving (Show)
-
-data ConnectionPort = NetworkPort Int | UnixPort String deriving (Eq, Read)
-
-instance Default ConnectionPort where
-	def = NetworkPort 4567
-
-instance Show ConnectionPort where
-	show (NetworkPort p) = show p
-	show (UnixPort s) = "unix " ++ s
-
-instance Formattable ConnectionPort
-
--- | Server options
-data ServerOpts = ServerOpts {
-	serverPort :: ConnectionPort,
-	serverTimeout :: Int,
-	serverLog :: Maybe FilePath,
-	serverLogLevel :: String,
-	serverCache :: Maybe FilePath,
-	serverLoad :: Bool,
-	serverSilent :: Bool }
-		deriving (Show)
-
-instance Default ServerOpts where
-	def = ServerOpts def 0 Nothing "info" Nothing False False
-
--- | Silent server with no connection, useful for ghci
-silentOpts :: ServerOpts
-silentOpts = def { serverSilent = True }
-
--- | Client options
-data ClientOpts = ClientOpts {
-	clientPort :: ConnectionPort,
-	clientPretty :: Bool,
-	clientStdin :: Bool,
-	clientTimeout :: Int,
-	clientSilent :: Bool }
-		deriving (Show)
-
-instance Default ClientOpts where
-	def = ClientOpts def False False 0 False
-
-instance FromCmd ServerCommand where
-	cmdP = serv <|> remote where
-		serv = subparser $ mconcat [
-			cmd "version" "hsdev version" (pure Version),
-			cmd "start" "start remote server" (Start <$> cmdP),
-			cmd "run" "run server" (Run <$> cmdP),
-			cmd "stop" "stop remote server" (Stop <$> cmdP),
-			cmd "connect" "connect to send commands directly" (Connect <$> cmdP)]
-		remote = Remote <$> cmdP <*> noFileFlag <*> cmdP
-
-instance FromCmd ServerOpts where
-	cmdP = ServerOpts <$>
-		(connectionArg <|> pure (serverPort def)) <*>
-		(timeoutArg <|> pure (serverTimeout def)) <*>
-		optional logArg <*>
-		(logLevelArg <|> pure (serverLogLevel def)) <*>
-		optional cacheArg <*>
-		loadFlag <*>
-		serverSilentFlag
-
-instance FromCmd ClientOpts where
-	cmdP = ClientOpts <$>
-		(connectionArg <|> pure (clientPort def)) <*>
-		prettyFlag <*>
-		stdinFlag <*>
-		(timeoutArg <|> pure (clientTimeout def)) <*>
-		silentFlag
-
-portArg :: Parser ConnectionPort
-connectionArg :: Parser ConnectionPort
-timeoutArg :: Parser Int
-logArg :: Parser FilePath
-logLevelArg :: Parser String
-cacheArg :: Parser FilePath
-noFileFlag :: Parser Bool
-loadFlag :: Parser Bool
-prettyFlag :: Parser Bool
-serverSilentFlag :: Parser Bool
-stdinFlag :: Parser Bool
-silentFlag :: Parser Bool
-
-portArg = NetworkPort <$> option auto (long "port" <> metavar "number" <> help "connection port")
-#if mingw32_HOST_OS
-connectionArg = portArg
-#else
-unixArg :: Parser ConnectionPort
-unixArg = UnixPort <$> strOption (long "unix" <> metavar "name" <> help "unix connection port")
-connectionArg = portArg <|> unixArg
-#endif
-timeoutArg = option auto (long "timeout" <> metavar "msec" <> help "query timeout")
-logArg = strOption (long "log" <> short 'l' <> metavar "file" <> help "log file")
-logLevelArg = strOption (long "log-level" <> metavar "level" <> help "log level: trace/debug/info/warning/error/fatal")
-cacheArg = strOption (long "cache" <> metavar "path" <> help "cache directory")
-noFileFlag = switch (long "no-file" <> help "don't use mmap files")
-loadFlag = switch (long "load" <> help "force load all data from cache on startup")
-prettyFlag = switch (long "pretty" <> help "pretty json output")
-serverSilentFlag = switch (long "silent" <> help "no stdout/stderr")
-stdinFlag = switch (long "stdin" <> help "pass data to stdin")
-silentFlag = switch (long "silent" <> help "supress notifications")
-
-serverOptsArgs :: ServerOpts -> [String]
-serverOptsArgs sopts = concat [
-	portArgs (serverPort sopts),
-	["--timeout", show $ serverTimeout sopts],
-	marg "--log" (serverLog sopts),
-	["--log-level", serverLogLevel sopts],
-	marg "--cache" (serverCache sopts),
-	["--load" | serverLoad sopts],
-	["--silent" | serverSilent sopts]]
-	where
-		marg :: String -> Maybe String -> [String]
-		marg n (Just v) = [n, v]
-		marg _ _ = []
-		portArgs :: ConnectionPort -> [String]
-		portArgs (NetworkPort n) = ["--port", show n]
-		portArgs (UnixPort s) = ["--unix", s]
-
-data Request = Request {
-	requestCommand :: Command,
-	requestDirectory :: FilePath,
-	requestNoFile :: Bool,
-	requestTimeout :: Int,
-	requestSilent :: Bool }
-		deriving (Show)
-
-instance ToJSON Request where
-	toJSON (Request c dir f tm s) = object ["current-directory" .= dir, "no-file" .= f, "timeout" .= tm, "silent" .= s] `objectUnion` toJSON c
-
-instance FromJSON Request where
-	parseJSON = withObject "request" $ \v -> Request <$>
-		parseJSON (Object v) <*>
-		((v .:: "current-directory") <|> pure ".") <*>
-		((v .:: "no-file") <|> pure False) <*>
-		((v .:: "timeout") <|> pure 0) <*>
-		((v .:: "silent") <|> pure False)
-
--- | Command from client
-data Command =
-	Ping |
-	Listen (Maybe String) |
-	SetLogLevel String |
-	AddData { addedContents :: [AddedContents] } |
-	Scan {
-		scanProjects :: [FilePath],
-		scanCabal :: Bool,
-		scanSandboxes :: [FilePath],
-		scanFiles :: [FileSource],
-		scanPaths :: [FilePath],
-		scanGhcOpts :: [String],
-		scanDocs :: Bool,
-		scanInferTypes :: Bool } |
-	RefineDocs {
-		docsProjects :: [FilePath],
-		docsFiles :: [FilePath],
-		docsModules :: [String] } |
-	InferTypes {
-		inferProjects :: [FilePath],
-		inferFiles :: [FilePath],
-		inferModules :: [String] } |
-	Remove {
-		removeProjects :: [FilePath],
-		removeCabal :: Bool,
-		removeSandboxes :: [FilePath],
-		removeFiles :: [FilePath] } |
-	RemoveAll |
-	InfoModules [TargetFilter] |
-	InfoPackages |
-	InfoProjects |
-	InfoSandboxes |
-	InfoSymbol SearchQuery [TargetFilter] Bool |
-	InfoModule SearchQuery [TargetFilter] |
-	InfoResolve FilePath Bool |
-	InfoProject (Either String FilePath) |
-	InfoSandbox FilePath |
-	Lookup String FilePath |
-	Whois String FilePath |
-	ResolveScopeModules SearchQuery FilePath |
-	ResolveScope SearchQuery Bool FilePath |
-	Complete String Bool FilePath |
-	Hayoo {
-		hayooQuery :: String,
-		hayooPage :: Int,
-		hayooPages :: Int } |
-	CabalList { cabalListPackages :: [String] } |
-	Lint {
-		lintFiles :: [FileSource] } |
-	Check {
-		checkFiles :: [FileSource],
-		checkGhcOpts :: [String] } |
-	CheckLint {
-		checkLintFiles :: [FileSource],
-		checkLintGhcOpts :: [String] } |
-	Types {
-		typesFiles :: [FileSource],
-		typesGhcOpts :: [String] } |
-	AutoFix { autoFixCommand :: AutoFixCommand } |
-	GhcEval { ghcEvalExpressions :: [String], ghcEvalSource :: Maybe FileSource } |
-	Langs |
-	Flags |
-	Link { linkHold :: Bool } |
-	Exit
-		deriving (Show)
-
-data AddedContents =
-	AddedDatabase Database |
-	AddedModule InspectedModule |
-	AddedProject Project
-
-instance Show AddedContents where
-	show = L.unpack . encode
-
-data AutoFixCommand =
-	AutoFixShow [Note OutputMessage] |
-	AutoFixFix [Note Correction] [Note Correction] Bool
-		deriving (Show)
-
-data FileSource = FileSource { fileSource :: FilePath, fileContents :: Maybe String } deriving (Show)
-data TargetFilter =
-	TargetProject String |
-	TargetFile FilePath |
-	TargetModule String |
-	TargetDepsOf String |
-	TargetPackageDb PackageDb |
-	TargetCabal |
-	TargetSandbox FilePath |
-	TargetPackage String |
-	TargetSourced |
-	TargetStandalone
-		deriving (Eq, Show)
-data SearchQuery = SearchQuery String SearchType deriving (Show)
-data SearchType = SearchExact | SearchPrefix | SearchInfix | SearchSuffix | SearchRegex deriving (Show)
-
-instance Paths Command where
-	paths f (Scan projs c cs fs ps ghcs docs infer) = Scan <$>
-		each f projs <*>
-		pure c <*>
-		(each . paths) f cs <*>
-		(each . paths) f fs <*>
-		each f ps <*>
-		pure ghcs <*>
-		pure docs <*>
-		pure infer
-	paths f (RefineDocs projs fs ms) = RefineDocs <$> each f projs <*> each f fs <*> pure ms
-	paths f (InferTypes projs fs ms) = InferTypes <$> each f projs <*> each f fs <*> pure ms
-	paths f (Remove projs c cs fs) = Remove <$> each f projs <*> pure c <*> (each . paths) f cs <*> each f fs
-	paths _ RemoveAll = pure RemoveAll
-	paths f (InfoModules t) = InfoModules <$> paths f t
-	paths f (InfoSymbol q t l) = InfoSymbol <$> pure q <*> paths f t <*> pure l
-	paths f (InfoModule q t) = InfoModule <$> pure q <*> paths f t
-	paths f (InfoResolve fpath es) = InfoResolve <$> f fpath <*> pure es
-	paths f (InfoProject (Right proj)) = InfoProject <$> (Right <$> f proj)
-	paths f (InfoSandbox fpath) = InfoSandbox <$> f fpath
-	paths f (Lookup n fpath) = Lookup <$> pure n <*> f fpath
-	paths f (Whois n fpath) = Whois <$> pure n <*> f fpath
-	paths f (ResolveScopeModules q fpath) = ResolveScopeModules q <$> f fpath
-	paths f (ResolveScope q g fpath) = ResolveScope q g <$> f fpath
-	paths f (Complete n g fpath) = Complete n g <$> f fpath
-	paths f (Lint fs) = Lint <$> (each . paths) f fs
-	paths f (Check fs ghcs) = Check <$> (each . paths) f fs <*> pure ghcs
-	paths f (CheckLint fs ghcs) = CheckLint <$> (each . paths) f fs <*> pure ghcs
-	paths f (Types fs ghcs) = Types <$> (each . paths) f fs <*> pure ghcs
-	paths f (GhcEval e mf) = GhcEval e <$> traverse (paths f) mf
-	paths _ c = pure c
-
-instance Paths FileSource where
-	paths f (FileSource fpath mcts) = FileSource <$> f fpath <*> pure mcts
-
-instance Paths TargetFilter where
-	paths f (TargetFile fpath) = TargetFile <$> f fpath
-	paths f (TargetPackageDb pdb) = TargetPackageDb <$> paths f pdb
-	paths f (TargetSandbox c) = TargetSandbox <$> paths f c
-	paths _ t = pure t
-
-instance Paths [TargetFilter] where
-	paths = each . paths
-
-instance FromCmd Command where
-	cmdP = subparser $ mconcat [
-		cmd "ping" "ping server" (pure Ping),
-		cmd "listen" "listen server log" (Listen <$> optional logLevelArg),
-		cmd "set-log" "set log level" (SetLogLevel <$> strArgument idm),
-		cmd "add" "add info to database" (AddData <$> option readJSON idm),
-		cmd "scan" "scan sources" $ Scan <$>
-			many projectArg <*>
-			cabalFlag <*>
-			many sandboxArg <*>
-			many cmdP <*>
-			many (pathArg $ help "path") <*>
-			ghcOpts <*>
-			docsFlag <*>
-			inferFlag,
-		cmd "docs" "scan docs" $ RefineDocs <$> many projectArg <*> many fileArg <*> many moduleArg,
-		cmd "infer" "infer types" $ InferTypes <$> many projectArg <*> many fileArg <*> many moduleArg,
-		cmd "remove" "remove modules info" $ Remove <$>
-			many projectArg <*>
-			cabalFlag <*>
-			many sandboxArg <*>
-			many fileArg,
-		cmd "remove-all" "remove all data" (pure RemoveAll),
-		cmd "modules" "list modules" (InfoModules <$> many cmdP),
-		cmd "packages" "list packages" (pure InfoPackages),
-		cmd "projects" "list projects" (pure InfoProjects),
-		cmd "sandboxes" "list sandboxes" (pure InfoSandboxes),
-		cmd "symbol" "get symbol info" (InfoSymbol <$> cmdP <*> many cmdP <*> localsFlag),
-		cmd "module" "get module info" (InfoModule <$> cmdP <*> many cmdP),
-		cmd "resolve" "resolve module scope (or exports)" (InfoResolve <$> fileArg <*> exportsFlag),
-		cmd "project" "get project info" (InfoProject <$> ((Left <$> projectArg) <|> (Right <$> pathArg idm))),
-		cmd "sandbox" "get sandbox info" (InfoSandbox <$> pathArg (help "locate sandbox in parent of this path")),
-		cmd "lookup" "lookup for symbol" (Lookup <$> strArgument idm <*> ctx),
-		cmd "whois" "get info for symbol" (Whois <$> strArgument idm <*> ctx),
-		cmd "scope" "get declarations accessible from module or within a project" (
-			subparser (cmd "modules" "get modules accessible from module or within a project" (ResolveScopeModules <$> cmdP <*> ctx)) <|>
-			ResolveScope <$> cmdP <*> globalFlag <*> ctx),
-		cmd "complete" "show completions for input" (Complete <$> strArgument idm <*> wideFlag <*> ctx),
-		cmd "hayoo" "find declarations online via Hayoo" (Hayoo <$> strArgument idm <*> hayooPageArg <*> hayooPagesArg),
-		cmd "cabal" "cabal commands" (subparser $ cmd "list" "list cabal packages" (CabalList <$> many (strArgument idm))),
-		cmd "lint" "lint source files or file contents" (Lint <$> many cmdP),
-		cmd "check" "check source files or file contents" (Check <$> many cmdP <*> ghcOpts),
-		cmd "check-lint" "check and lint source files or file contents" (CheckLint <$> many cmdP <*> ghcOpts),
-		cmd "types" "get types for file expressions" (Types <$> many cmdP <*> ghcOpts),
-		cmd "autofix" "autofix commands" (AutoFix <$> cmdP),
-		cmd "ghc" "ghc commands" (subparser $ cmd "eval" "evaluate expression" (GhcEval <$> many (strArgument idm) <*> optional cmdP)),
-		cmd "langs" "ghc language options" (pure Langs),
-		cmd "flags" "ghc flags" (pure Flags),
-		cmd "link" "link to server" (Link <$> holdFlag),
-		cmd "exit" "exit" (pure Exit)]
-
-instance FromCmd AutoFixCommand where
-	cmdP = subparser $ mconcat [
-		cmd "show" "generate corrections for check & lint messages" (AutoFixShow <$> option readJSON (long "data" <> metavar "message" <> help "messages to make fixes for")),
-		cmd "fix" "fix errors and return rest corrections with updated regions" (AutoFixFix <$>
-			option readJSON (long "data" <> metavar "message" <> help "messages to fix") <*>
-			option readJSON (long "rest" <> metavar "correction" <> short 'r' <> help "update corrections") <*>
-			pureFlag)]
-
-instance FromCmd FileSource where
-	cmdP = option readJSON (long "contents") <|> (FileSource <$> fileArg <*> pure Nothing)
-
-instance FromCmd TargetFilter where
-	cmdP = asum [
-		TargetProject <$> projectArg,
-		TargetFile <$> fileArg,
-		TargetModule <$> moduleArg,
-		TargetDepsOf <$> depsArg,
-		TargetPackageDb <$> packageDbArg,
-		flag' TargetCabal (long "cabal"),
-		TargetSandbox <$> sandboxArg,
-		TargetPackage <$> packageArg,
-		flag' TargetSourced (long "src"),
-		flag' TargetStandalone (long "stand")]
-
-instance FromCmd SearchQuery where
-	cmdP = SearchQuery <$> (strArgument idm <|> pure "") <*> asum [
-		flag' SearchExact (long "exact"),
-		flag' SearchRegex (long "regex"),
-		flag' SearchInfix (long "infix"),
-		flag' SearchSuffix (long "suffix"),
-		pure SearchPrefix <* switch (long "prefix")]
-
-readJSON :: FromJSON a => ReadM a
-readJSON = str >>= maybe (readerError "Can't parse JSON argument") return . decode . L.pack
-
-cabalFlag :: Parser Bool
-ctx :: Parser FilePath
-depsArg :: Parser String
-docsFlag :: Parser Bool
-exportsFlag :: Parser Bool
-fileArg :: Parser FilePath
-ghcOpts :: Parser [String]
-globalFlag :: Parser Bool
-hayooPageArg :: Parser Int
-hayooPagesArg :: Parser Int
-holdFlag :: Parser Bool
-inferFlag :: Parser Bool
-localsFlag :: Parser Bool
-moduleArg :: Parser String
-packageDbArg :: Parser PackageDb
-packageArg :: Parser String
-pathArg :: Mod OptionFields String -> Parser FilePath
-projectArg :: Parser String
-pureFlag :: Parser Bool
-sandboxArg :: Parser FilePath
-wideFlag :: Parser Bool
-
-cabalFlag = switch (long "cabal")
-ctx = fileArg
-depsArg = strOption (long "deps" <> metavar "object" <> help "filter to such that in dependency of specified object (file or project)")
-docsFlag = switch (long "docs" <> help "scan source file docs")
-exportsFlag = switch (long "exports" <> short 'e' <> help "resolve module exports")
-fileArg = strOption (long "file" <> metavar "path" <> short 'f')
-ghcOpts = many (strOption (long "ghc" <> metavar "option" <> short 'g' <> help "options to pass to GHC"))
-globalFlag = switch (long "global" <> help "scope of project")
-hayooPageArg = option auto (long "page" <> metavar "n" <> short 'p' <> help "page number (0 by default)" <> value 0)
-hayooPagesArg = option auto (long "pages" <> metavar "count" <> short 'n' <> help "pages count (1 by default)" <> value 1)
-holdFlag = switch (long "hold" <> short 'h' <> help "don't return any response")
-inferFlag = switch (long "infer" <> help "infer types")
-localsFlag = switch (long "locals" <> short 'l' <> help "look in local declarations")
-moduleArg = strOption (long "module" <> metavar "name" <> short 'm' <> help "module name")
-packageArg = strOption (long "package" <> metavar "name" <> help "module package")
-packageDbArg =
-	flag' GlobalDb (long "global-db" <> help "global package-db") <|>
-	flag' UserDb (long "user-db" <> help "user package-db") <|>
-	(PackageDb <$> strOption (long "package-db" <> metavar "path" <> help "custom package-db"))
-pathArg f = strOption (long "path" <> metavar "path" <> short 'p' <> f)
-projectArg = strOption (long "project" <> long "proj" <> metavar "project")
-pureFlag = switch (long "pure" <> help "don't modify actual file, just return result")
-sandboxArg = strOption (long "sandbox" <> metavar "path" <> help "path to cabal sandbox")
-wideFlag = switch (long "wide" <> short 'w' <> help "wide mode - complete as if there were no import lists")
-
-instance ToJSON Command where
-	toJSON Ping = cmdJson "ping" []
-	toJSON (Listen lev) = cmdJson "listen" ["level" .= lev]
-	toJSON (SetLogLevel lev) = cmdJson "set-log" ["level" .= lev]
-	toJSON (AddData cts) = cmdJson "add" ["data" .= cts]
-	toJSON (Scan projs cabal sboxes fs ps ghcs docs' infer') = cmdJson "scan" [
-		"projects" .= projs,
-		"cabal" .= cabal,
-		"sandboxes" .= sboxes,
-		"files" .= fs,
-		"paths" .= ps,
-		"ghc-opts" .= ghcs,
-		"docs" .= docs',
-		"infer" .= infer']
-	toJSON (RefineDocs projs fs ms) = cmdJson "docs" ["projects" .= projs, "files" .= fs, "modules" .= ms]
-	toJSON (InferTypes projs fs ms) = cmdJson "infer" ["projects" .= projs, "files" .= fs, "modules" .= ms]
-	toJSON (Remove projs cabal sboxes fs) = cmdJson "remove" ["projects" .= projs, "cabal" .= cabal, "sandboxes" .= sboxes, "files" .= fs]
-	toJSON RemoveAll = cmdJson "remove-all" []
-	toJSON (InfoModules tf) = cmdJson "modules" ["filters" .= tf]
-	toJSON InfoPackages = cmdJson "packages" []
-	toJSON InfoProjects = cmdJson "projects" []
-	toJSON InfoSandboxes = cmdJson "sandboxes" []
-	toJSON (InfoSymbol q tf l) = cmdJson "symbol" ["query" .= q, "filters" .= tf, "locals" .= l]
-	toJSON (InfoModule q tf) = cmdJson "module" ["query" .= q, "filters" .= tf]
-	toJSON (InfoResolve f es) = cmdJson "resolve" ["file" .= f, "exports" .= es]
-	toJSON (InfoProject p) = cmdJson "project" $ either (\pname -> ["name" .= pname]) (\ppath -> ["path" .= ppath]) p
-	toJSON (InfoSandbox p) = cmdJson "sandbox" ["path" .= p]
-	toJSON (Lookup n f) = cmdJson "lookup" ["name" .= n, "file" .= f]
-	toJSON (Whois n f) = cmdJson "whois" ["name" .= n, "file" .= f]
-	toJSON (ResolveScopeModules q f) = cmdJson "scope modules" ["query" .= q, "file" .= f]
-	toJSON (ResolveScope q g f) = cmdJson "scope" ["query" .= q, "global" .= g, "file" .= f]
-	toJSON (Complete q w f) = cmdJson "complete" ["prefix" .= q, "wide" .= w, "file" .= f]
-	toJSON (Hayoo q p ps) = cmdJson "hayoo" ["query" .= q, "page" .= p, "pages" .= ps]
-	toJSON (CabalList ps) = cmdJson "cabal list" ["packages" .= ps]
-	toJSON (Lint fs) = cmdJson "lint" ["files" .= fs]
-	toJSON (Check fs ghcs) = cmdJson "check" ["files" .= fs, "ghc-opts" .= ghcs]
-	toJSON (CheckLint fs ghcs) = cmdJson "check-lint" ["files" .= fs, "ghc-opts" .= ghcs]
-	toJSON (Types fs ghcs) = cmdJson "types" ["files" .= fs, "ghc-opts" .= ghcs]
-	toJSON (AutoFix acmd) = toJSON acmd
-	toJSON (GhcEval exprs f) = cmdJson "ghc eval" ["exprs" .= exprs, "file" .= f]
-	toJSON Langs = cmdJson "langs" []
-	toJSON Flags = cmdJson "flags" []
-	toJSON (Link h) = cmdJson "link" ["hold" .= h]
-	toJSON Exit = cmdJson "exit" []
-
-instance FromJSON Command where
-	parseJSON = withObject "command" $ \v -> asum [
-		guardCmd "ping" v *> pure Ping,
-		guardCmd "listen" v *> (Listen <$> v .::? "level"),
-		guardCmd "set-log" v *> (SetLogLevel <$> v .:: "level"),
-		guardCmd "add" v *> (AddData <$> v .:: "data"),
-		guardCmd "scan" v *> (Scan <$>
-			v .::?! "projects" <*>
-			(v .:: "cabal" <|> pure False) <*>
-			v .::?! "sandboxes" <*>
-			v .::?! "files" <*>
-			v .::?! "paths" <*>
-			v .::?! "ghc-opts" <*>
-			(v .:: "docs" <|> pure False) <*>
-			(v .:: "infer" <|> pure False)),
-		guardCmd "docs" v *> (RefineDocs <$> v .::?! "projects" <*> v .::?! "files" <*> v .::?! "modules"),
-		guardCmd "infer" v *> (InferTypes <$> v .::?! "projects" <*> v .::?! "files" <*> v .::?! "modules"),
-		guardCmd "remove" v *> (Remove <$>
-			v .::?! "projects" <*>
-			(v .:: "cabal" <|> pure False) <*>
-			v .::?! "sandboxes" <*>
-			v .::?! "files"),
-		guardCmd "remove-all" v *> pure RemoveAll,
-		guardCmd "modules" v *> (InfoModules <$> v .::?! "filters"),
-		guardCmd "packages" v *> pure InfoPackages,
-		guardCmd "projects" v *> pure InfoProjects,
-		guardCmd "sandboxes" v *> pure InfoSandboxes,
-		guardCmd "symbol" v *> (InfoSymbol <$> v .:: "query" <*> v .::?! "filters" <*> (v .:: "locals" <|> pure False)),
-		guardCmd "module" v *> (InfoModule <$> v .:: "query" <*> v .::?! "filters"),
-		guardCmd "resolve" v *> (InfoResolve <$> v .:: "file" <*> (v .:: "exports" <|> pure False)),
-		guardCmd "project" v *> (InfoProject <$> asum [Left <$> v .:: "name", Right <$> v .:: "path"]),
-		guardCmd "sandbox" v *> (InfoSandbox <$> v .:: "path"),
-		guardCmd "lookup" v *> (Lookup <$> v .:: "name" <*> v .:: "file"),
-		guardCmd "whois" v *> (Whois <$> v .:: "name" <*> v .:: "file"),
-		guardCmd "scope modules" v *> (ResolveScopeModules <$> v .:: "query" <*> v .:: "file"),
-		guardCmd "scope" v *> (ResolveScope <$> v .:: "query" <*> (v .:: "global" <|> pure False) <*> v .:: "file"),
-		guardCmd "complete" v *> (Complete <$> v .:: "prefix" <*> (v .:: "wide" <|> pure False) <*> v .:: "file"),
-		guardCmd "hayoo" v *> (Hayoo <$> v .:: "query" <*> (v .:: "page" <|> pure 0) <*> (v .:: "pages" <|> pure 1)),
-		guardCmd "cabal list" v *> (CabalList <$> v .::?! "packages"),
-		guardCmd "lint" v *> (Lint <$> v .::?! "files"),
-		guardCmd "check" v *> (Check <$> v .::?! "files" <*> v .::?! "ghc-opts"),
-		guardCmd "check-lint" v *> (CheckLint <$> v .::?! "files" <*> v .::?! "ghc-opts"),
-		guardCmd "types" v *> (Types <$> v .::?! "files" <*> v .::?! "ghc-opts"),
-		AutoFix <$> parseJSON (Object v),
-		guardCmd "ghc eval" v *> (GhcEval <$> v .::?! "exprs" <*> v .::? "file"),
-		guardCmd "langs" v *> pure Langs,
-		guardCmd "flags" v *> pure Flags,
-		guardCmd "link" v *> (Link <$> (v .:: "hold" <|> pure False)),
-		guardCmd "exit" v *> pure Exit]
-
-instance ToJSON AddedContents where
-	toJSON (AddedDatabase db) = object ["database" .= db]
-	toJSON (AddedModule im) = object ["module" .= im]
-	toJSON (AddedProject p) = object ["project" .= p]
-
-instance FromJSON AddedContents where
-	parseJSON = withObject "added-contents" $ \v -> asum [
-		AddedDatabase <$> v .:: "database",
-		AddedModule <$> v .:: "module",
-		AddedProject <$> v .:: "project"]
-
-instance ToJSON AutoFixCommand where
-	toJSON (AutoFixShow ns) = cmdJson "autofix show" ["messages" .= ns]
-	toJSON (AutoFixFix ns rests pure') = cmdJson "autofix fix" ["messages" .= ns, "rest" .= rests, "pure" .= pure']
-
-instance FromJSON AutoFixCommand where
-	parseJSON = withObject "auto-fix-command" $ \v -> asum [
-		guardCmd "autofix show" v *> (AutoFixShow <$> v .:: "messages"),
-		guardCmd "autofix fix" v *> (AutoFixFix <$> v .:: "messages" <*> v .::?! "rest" <*> (v .:: "pure" <|> pure True))]
-
-instance ToJSON FileSource where
-	toJSON (FileSource fpath mcts) = object ["file" .= fpath, "contents" .= mcts]
-
-instance FromJSON FileSource where
-	parseJSON = withObject "file-contents" $ \v -> FileSource <$> v .:: "file" <*> v .::? "contents"
-
-instance ToJSON TargetFilter where
-	toJSON (TargetProject pname) = object ["project" .= pname]
-	toJSON (TargetFile fpath) = object ["file" .= fpath]
-	toJSON (TargetModule mname) = object ["module" .= mname]
-	toJSON (TargetDepsOf dep) = object ["deps" .= dep]
-	toJSON (TargetPackageDb pdb) = object ["db" .= pdb]
-	toJSON TargetCabal = toJSON ("cabal" :: String)
-	toJSON (TargetSandbox sbox) = object ["sandbox" .= sbox]
-	toJSON (TargetPackage pname) = object ["package" .= pname]
-	toJSON TargetSourced = toJSON ("sourced" :: String)
-	toJSON TargetStandalone = toJSON ("standalone" :: String)
-
-instance FromJSON TargetFilter where
-	parseJSON j = obj j <|> str' where
-		obj = withObject "target-filter" $ \v -> asum [
-			TargetProject <$> v .:: "project",
-			TargetFile <$> v .:: "file",
-			TargetModule <$> v .:: "module",
-			TargetDepsOf <$> v .:: "deps",
-			TargetPackageDb <$> v .:: "db",
-			TargetSandbox <$> v .:: "sandbox",
-			TargetPackage <$> v .:: "package"]
-		str' = do
-			s <- parseJSON j :: A.Parser String
-			case s of
-				"cabal" -> return TargetCabal
-				"sourced" -> return TargetSourced
-				"standalone" -> return TargetStandalone
-				_ -> empty
-
-instance ToJSON SearchQuery where
-	toJSON (SearchQuery q st) = object ["input" .= q, "type" .= st]
-
-instance FromJSON SearchQuery where
-	parseJSON = withObject "search-query" $ \v -> SearchQuery <$> (v .:: "input" <|> pure "") <*> (v .:: "type" <|> pure SearchPrefix)
-
-instance ToJSON SearchType where
-	toJSON SearchExact = toJSON ("exact" :: String)
-	toJSON SearchPrefix = toJSON ("prefix" :: String)
-	toJSON SearchInfix = toJSON ("infix" :: String)
-	toJSON SearchSuffix = toJSON ("suffix" :: String)
-	toJSON SearchRegex = toJSON ("regex" :: String)
-
-instance FromJSON SearchType where
-	parseJSON v = do
-		str' <- parseJSON v :: A.Parser String
-		case str' of
-			"exact" -> return SearchExact
-			"prefix" -> return SearchPrefix
-			"infix" -> return SearchInfix
-			"suffix" -> return SearchInfix
-			"regex" -> return SearchRegex
-			_ -> empty
+{-# LANGUAGE OverloadedStrings, CPP, TypeSynonymInstances, FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts, UndecidableInstances, MultiParamTypeClasses, TypeFamilies, ConstraintKinds #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Server.Types (
+	ServerMonadBase,
+	SessionLog(..), Session(..), SessionMonad(..), askSession, ServerM(..),
+	CommandOptions(..), CommandMonad(..), askOptions, ClientM(..),
+	withSession, serverListen, serverSetLogLevel, serverWait, serverUpdateDB, serverWriteCache, serverReadCache, inSessionGhc, serverExit, commandRoot, commandNotify, commandLink, commandHold,
+	ServerCommand(..), ConnectionPort(..), ServerOpts(..), silentOpts, ClientOpts(..), serverOptsArgs, Request(..),
+
+	Command(..), AddedContents(..),
+	AutoFixCommand(..),
+	FileSource(..), TargetFilter(..), SearchQuery(..), SearchType(..),
+	FromCmd(..),
+	) where
+
+import Control.Applicative
+import Control.Concurrent.Worker
+import Control.Lens (each, view, set)
+import Control.Monad.Base
+import Control.Monad.Catch
+import Control.Monad.Except
+import Control.Monad.Reader
+import Control.Monad.Trans.Control
+import Data.Aeson hiding (Result(..), Error)
+import qualified Data.Aeson.Types as A
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Default
+import Data.Maybe (fromMaybe)
+import Data.Monoid
+import Data.Foldable (asum)
+import Options.Applicative
+import System.Log.Simple
+
+import System.Directory.Paths
+import Text.Format (Formattable(..))
+
+import HsDev.Database
+import qualified HsDev.Database.Async as DB
+import HsDev.Error (hsdevError)
+import HsDev.Project
+import HsDev.Symbols
+import HsDev.Server.Message
+import HsDev.Watcher.Types (Watcher)
+import HsDev.Tools.Ghc.Worker (GhcWorker, GhcM)
+import HsDev.Tools.Types (Note, OutputMessage)
+import HsDev.Tools.AutoFix (Correction)
+import HsDev.Types (HsDevError(..))
+import HsDev.Util
+
+#if mingw32_HOST_OS
+import System.Win32.FileMapping.NamePool (Pool)
+#endif
+
+type ServerMonadBase m = (MonadIO m, MonadMask m, MonadBaseControl IO m, Alternative m, MonadPlus m)
+
+data SessionLog = SessionLog {
+	sessionLogger :: Log,
+	sessionListenLog :: IO [String],
+	sessionLogWait :: IO () }
+
+data Session = Session {
+	sessionDatabase :: DB.Async Database,
+	sessionWriteCache :: Database -> ServerM IO (),
+	sessionReadCache :: (FilePath -> ExceptT String IO Structured) -> ServerM IO (Maybe Database),
+	sessionLog :: SessionLog,
+	sessionWatcher :: Watcher,
+#if mingw32_HOST_OS
+	sessionMmapPool :: Maybe Pool,
+#endif
+	sessionGhc :: GhcWorker,
+	sessionExit :: IO (),
+	sessionWait :: IO (),
+	sessionDefines :: [(String, String)] }
+
+class (ServerMonadBase m, MonadLog m) => SessionMonad m where
+	getSession :: m Session
+
+askSession :: SessionMonad m => (Session -> a) -> m a
+askSession f = liftM f getSession
+
+newtype ServerM m a = ServerM { runServerM :: ReaderT Session m a }
+	deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadReader Session, MonadTrans, MonadThrow, MonadCatch, MonadMask)
+
+instance (MonadIO m, MonadMask m) => MonadLog (ServerM m) where
+	askLog = ServerM $ asks (sessionLogger . sessionLog)
+	localLog fn = ServerM . local setLog' . runServerM where
+		setLog' sess = sess { sessionLog = (sessionLog sess) { sessionLogger = fn (sessionLogger (sessionLog sess)) } }
+
+instance ServerMonadBase m => SessionMonad (ServerM m) where
+	getSession = ask
+
+instance MonadBase b m => MonadBase b (ServerM m) where
+	liftBase = ServerM . liftBase
+
+instance MonadBaseControl b m => MonadBaseControl b (ServerM m) where
+	type StM (ServerM m) a = StM (ReaderT Session m) a
+	liftBaseWith f = ServerM $ liftBaseWith (\f' -> f (f' . runServerM))
+	restoreM = ServerM . restoreM
+
+data CommandOptions = CommandOptions {
+	commandOptionsRoot :: FilePath,
+	commandOptionsNotify :: Notification -> IO (),
+	commandOptionsLink :: IO (),
+	commandOptionsHold :: IO () }
+
+instance Default CommandOptions where
+	def = CommandOptions "." (const $ return ()) (return ()) (return ())
+
+class (SessionMonad m, MonadPlus m) => CommandMonad m where
+	getOptions :: m CommandOptions
+
+askOptions :: CommandMonad m => (CommandOptions -> a) -> m a
+askOptions f = liftM f getOptions
+
+newtype ClientM m a = ClientM { runClientM :: ServerM (ReaderT CommandOptions m) a }
+	deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadThrow, MonadCatch, MonadMask)
+
+instance MonadTrans ClientM where
+	lift = ClientM . lift . lift
+
+instance (MonadIO m, MonadMask m) => MonadLog (ClientM m) where
+	askLog = ClientM askLog
+	localLog fn = ClientM . localLog fn . runClientM
+
+instance ServerMonadBase m => SessionMonad (ClientM m) where
+	getSession = ClientM getSession
+
+instance ServerMonadBase m => CommandMonad (ClientM m) where
+	getOptions = ClientM $ lift ask
+
+instance MonadBase b m => MonadBase b (ClientM m) where
+	liftBase = ClientM . liftBase
+
+instance MonadBaseControl b m => MonadBaseControl b (ClientM m) where
+	type StM (ClientM m) a = StM (ServerM (ReaderT CommandOptions m)) a
+	liftBaseWith f = ClientM $ liftBaseWith (\f' -> f (f' . runClientM))
+	restoreM = ClientM . restoreM
+
+-- | Run action on session
+withSession :: Session -> ServerM m a -> m a
+withSession s act = runReaderT (runServerM act) s
+
+-- | Listen server's log
+serverListen :: SessionMonad m => m [String]
+serverListen = join . liftM liftIO $ askSession (sessionListenLog . sessionLog)
+
+-- | Set server's log config
+serverSetLogLevel :: SessionMonad m => Level -> m Level
+serverSetLogLevel lev = do
+	l <- askSession (sessionLogger . sessionLog)
+	cfg <- updateLogConfig l (set (componentCfg "") (Just lev))
+	return $ fromMaybe def $ view (componentCfg "") cfg
+
+-- | Wait for server
+serverWait :: SessionMonad m => m ()
+serverWait = join . liftM liftIO $ askSession sessionWait
+
+-- | Update database
+serverUpdateDB :: SessionMonad m => Database -> m ()
+serverUpdateDB db = askSession sessionDatabase >>= (`DB.update` return db)
+
+-- | Server write cache
+serverWriteCache :: SessionMonad m => Database -> m ()
+serverWriteCache db = do
+	s <- getSession
+	write' <- askSession sessionWriteCache
+	liftIO $ withSession s $ write' db
+
+-- | Server read cache
+serverReadCache :: SessionMonad m => (FilePath -> ExceptT String IO Structured) -> m (Maybe Database)
+serverReadCache act = do
+	s <- getSession
+	read' <- askSession sessionReadCache
+	liftIO $ withSession s $ read' act
+
+-- | In ghc session
+inSessionGhc :: SessionMonad m => GhcM a -> m a
+inSessionGhc act = do
+	ghcw <- askSession sessionGhc
+	inWorkerWith (hsdevError . GhcError . displayException) ghcw act
+
+-- | Exit session
+serverExit :: SessionMonad m => m ()
+serverExit = join . liftM liftIO $ askSession sessionExit
+
+commandRoot :: CommandMonad m => m FilePath
+commandRoot = askOptions commandOptionsRoot
+
+commandNotify :: CommandMonad m => Notification -> m ()
+commandNotify n = join . liftM liftIO $ askOptions commandOptionsNotify <*> pure n
+
+commandLink :: CommandMonad m => m ()
+commandLink = join . liftM liftIO $ askOptions commandOptionsLink
+
+commandHold :: CommandMonad m => m ()
+commandHold = join . liftM liftIO $ askOptions commandOptionsHold
+
+-- | Server control command
+data ServerCommand =
+	Version |
+	Start ServerOpts |
+	Run ServerOpts |
+	Stop ClientOpts |
+	Connect ClientOpts |
+	Remote ClientOpts Bool Command
+		deriving (Show)
+
+data ConnectionPort = NetworkPort Int | UnixPort String deriving (Eq, Read)
+
+instance Default ConnectionPort where
+	def = NetworkPort 4567
+
+instance Show ConnectionPort where
+	show (NetworkPort p) = show p
+	show (UnixPort s) = "unix " ++ s
+
+instance Formattable ConnectionPort
+
+-- | Server options
+data ServerOpts = ServerOpts {
+	serverPort :: ConnectionPort,
+	serverTimeout :: Int,
+	serverLog :: Maybe FilePath,
+	serverLogLevel :: String,
+	serverCache :: Maybe FilePath,
+	serverLoad :: Bool,
+	serverSilent :: Bool }
+		deriving (Show)
+
+instance Default ServerOpts where
+	def = ServerOpts def 0 Nothing "info" Nothing False False
+
+-- | Silent server with no connection, useful for ghci
+silentOpts :: ServerOpts
+silentOpts = def { serverSilent = True }
+
+-- | Client options
+data ClientOpts = ClientOpts {
+	clientPort :: ConnectionPort,
+	clientPretty :: Bool,
+	clientStdin :: Bool,
+	clientTimeout :: Int,
+	clientSilent :: Bool }
+		deriving (Show)
+
+instance Default ClientOpts where
+	def = ClientOpts def False False 0 False
+
+instance FromCmd ServerCommand where
+	cmdP = serv <|> remote where
+		serv = subparser $ mconcat [
+			cmd "version" "hsdev version" (pure Version),
+			cmd "start" "start remote server" (Start <$> cmdP),
+			cmd "run" "run server" (Run <$> cmdP),
+			cmd "stop" "stop remote server" (Stop <$> cmdP),
+			cmd "connect" "connect to send commands directly" (Connect <$> cmdP)]
+		remote = Remote <$> cmdP <*> noFileFlag <*> cmdP
+
+instance FromCmd ServerOpts where
+	cmdP = ServerOpts <$>
+		(connectionArg <|> pure (serverPort def)) <*>
+		(timeoutArg <|> pure (serverTimeout def)) <*>
+		optional logArg <*>
+		(logLevelArg <|> pure (serverLogLevel def)) <*>
+		optional cacheArg <*>
+		loadFlag <*>
+		serverSilentFlag
+
+instance FromCmd ClientOpts where
+	cmdP = ClientOpts <$>
+		(connectionArg <|> pure (clientPort def)) <*>
+		prettyFlag <*>
+		stdinFlag <*>
+		(timeoutArg <|> pure (clientTimeout def)) <*>
+		silentFlag
+
+portArg :: Parser ConnectionPort
+connectionArg :: Parser ConnectionPort
+timeoutArg :: Parser Int
+logArg :: Parser FilePath
+logLevelArg :: Parser String
+cacheArg :: Parser FilePath
+noFileFlag :: Parser Bool
+loadFlag :: Parser Bool
+prettyFlag :: Parser Bool
+serverSilentFlag :: Parser Bool
+stdinFlag :: Parser Bool
+silentFlag :: Parser Bool
+
+portArg = NetworkPort <$> option auto (long "port" <> metavar "number" <> help "connection port")
+#if mingw32_HOST_OS
+connectionArg = portArg
+#else
+unixArg :: Parser ConnectionPort
+unixArg = UnixPort <$> strOption (long "unix" <> metavar "name" <> help "unix connection port")
+connectionArg = portArg <|> unixArg
+#endif
+timeoutArg = option auto (long "timeout" <> metavar "msec" <> help "query timeout")
+logArg = strOption (long "log" <> short 'l' <> metavar "file" <> help "log file")
+logLevelArg = strOption (long "log-level" <> metavar "level" <> help "log level: trace/debug/info/warning/error/fatal")
+cacheArg = strOption (long "cache" <> metavar "path" <> help "cache directory")
+noFileFlag = switch (long "no-file" <> help "don't use mmap files")
+loadFlag = switch (long "load" <> help "force load all data from cache on startup")
+prettyFlag = switch (long "pretty" <> help "pretty json output")
+serverSilentFlag = switch (long "silent" <> help "no stdout/stderr")
+stdinFlag = switch (long "stdin" <> help "pass data to stdin")
+silentFlag = switch (long "silent" <> help "supress notifications")
+
+serverOptsArgs :: ServerOpts -> [String]
+serverOptsArgs sopts = concat [
+	portArgs (serverPort sopts),
+	["--timeout", show $ serverTimeout sopts],
+	marg "--log" (serverLog sopts),
+	["--log-level", serverLogLevel sopts],
+	marg "--cache" (serverCache sopts),
+	["--load" | serverLoad sopts],
+	["--silent" | serverSilent sopts]]
+	where
+		marg :: String -> Maybe String -> [String]
+		marg n (Just v) = [n, v]
+		marg _ _ = []
+		portArgs :: ConnectionPort -> [String]
+		portArgs (NetworkPort n) = ["--port", show n]
+		portArgs (UnixPort s) = ["--unix", s]
+
+data Request = Request {
+	requestCommand :: Command,
+	requestDirectory :: FilePath,
+	requestNoFile :: Bool,
+	requestTimeout :: Int,
+	requestSilent :: Bool }
+		deriving (Show)
+
+instance ToJSON Request where
+	toJSON (Request c dir f tm s) = object ["current-directory" .= dir, "no-file" .= f, "timeout" .= tm, "silent" .= s] `objectUnion` toJSON c
+
+instance FromJSON Request where
+	parseJSON = withObject "request" $ \v -> Request <$>
+		parseJSON (Object v) <*>
+		((v .:: "current-directory") <|> pure ".") <*>
+		((v .:: "no-file") <|> pure False) <*>
+		((v .:: "timeout") <|> pure 0) <*>
+		((v .:: "silent") <|> pure False)
+
+-- | Command from client
+data Command =
+	Ping |
+	Listen (Maybe String) |
+	SetLogLevel String |
+	AddData { addedContents :: [AddedContents] } |
+	Scan {
+		scanProjects :: [FilePath],
+		scanCabal :: Bool,
+		scanSandboxes :: [FilePath],
+		scanFiles :: [FileSource],
+		scanPaths :: [FilePath],
+		scanGhcOpts :: [String],
+		scanDocs :: Bool,
+		scanInferTypes :: Bool } |
+	RefineDocs {
+		docsProjects :: [FilePath],
+		docsFiles :: [FilePath],
+		docsModules :: [String] } |
+	InferTypes {
+		inferProjects :: [FilePath],
+		inferFiles :: [FilePath],
+		inferModules :: [String] } |
+	Remove {
+		removeProjects :: [FilePath],
+		removeCabal :: Bool,
+		removeSandboxes :: [FilePath],
+		removeFiles :: [FilePath] } |
+	RemoveAll |
+	InfoModules [TargetFilter] |
+	InfoPackages |
+	InfoProjects |
+	InfoSandboxes |
+	InfoSymbol SearchQuery [TargetFilter] Bool |
+	InfoModule SearchQuery [TargetFilter] |
+	InfoResolve FilePath Bool |
+	InfoProject (Either String FilePath) |
+	InfoSandbox FilePath |
+	Lookup String FilePath |
+	Whois String FilePath |
+	ResolveScopeModules SearchQuery FilePath |
+	ResolveScope SearchQuery Bool FilePath |
+	Complete String Bool FilePath |
+	Hayoo {
+		hayooQuery :: String,
+		hayooPage :: Int,
+		hayooPages :: Int } |
+	CabalList { cabalListPackages :: [String] } |
+	Lint {
+		lintFiles :: [FileSource] } |
+	Check {
+		checkFiles :: [FileSource],
+		checkGhcOpts :: [String] } |
+	CheckLint {
+		checkLintFiles :: [FileSource],
+		checkLintGhcOpts :: [String] } |
+	Types {
+		typesFiles :: [FileSource],
+		typesGhcOpts :: [String] } |
+	AutoFix { autoFixCommand :: AutoFixCommand } |
+	GhcEval { ghcEvalExpressions :: [String], ghcEvalSource :: Maybe FileSource } |
+	Langs |
+	Flags |
+	Link { linkHold :: Bool } |
+	Exit
+		deriving (Show)
+
+data AddedContents =
+	AddedDatabase Database |
+	AddedModule InspectedModule |
+	AddedProject Project
+
+instance Show AddedContents where
+	show = L.unpack . encode
+
+data AutoFixCommand =
+	AutoFixShow [Note OutputMessage] |
+	AutoFixFix [Note Correction] [Note Correction] Bool
+		deriving (Show)
+
+data FileSource = FileSource { fileSource :: FilePath, fileContents :: Maybe String } deriving (Show)
+data TargetFilter =
+	TargetProject String |
+	TargetFile FilePath |
+	TargetModule String |
+	TargetDepsOf String |
+	TargetPackageDb PackageDb |
+	TargetCabal |
+	TargetSandbox FilePath |
+	TargetPackage String |
+	TargetSourced |
+	TargetStandalone
+		deriving (Eq, Show)
+data SearchQuery = SearchQuery String SearchType deriving (Show)
+data SearchType = SearchExact | SearchPrefix | SearchInfix | SearchSuffix | SearchRegex deriving (Show)
+
+instance Paths Command where
+	paths f (Scan projs c cs fs ps ghcs docs infer) = Scan <$>
+		each f projs <*>
+		pure c <*>
+		(each . paths) f cs <*>
+		(each . paths) f fs <*>
+		each f ps <*>
+		pure ghcs <*>
+		pure docs <*>
+		pure infer
+	paths f (RefineDocs projs fs ms) = RefineDocs <$> each f projs <*> each f fs <*> pure ms
+	paths f (InferTypes projs fs ms) = InferTypes <$> each f projs <*> each f fs <*> pure ms
+	paths f (Remove projs c cs fs) = Remove <$> each f projs <*> pure c <*> (each . paths) f cs <*> each f fs
+	paths _ RemoveAll = pure RemoveAll
+	paths f (InfoModules t) = InfoModules <$> paths f t
+	paths f (InfoSymbol q t l) = InfoSymbol <$> pure q <*> paths f t <*> pure l
+	paths f (InfoModule q t) = InfoModule <$> pure q <*> paths f t
+	paths f (InfoResolve fpath es) = InfoResolve <$> f fpath <*> pure es
+	paths f (InfoProject (Right proj)) = InfoProject <$> (Right <$> f proj)
+	paths f (InfoSandbox fpath) = InfoSandbox <$> f fpath
+	paths f (Lookup n fpath) = Lookup <$> pure n <*> f fpath
+	paths f (Whois n fpath) = Whois <$> pure n <*> f fpath
+	paths f (ResolveScopeModules q fpath) = ResolveScopeModules q <$> f fpath
+	paths f (ResolveScope q g fpath) = ResolveScope q g <$> f fpath
+	paths f (Complete n g fpath) = Complete n g <$> f fpath
+	paths f (Lint fs) = Lint <$> (each . paths) f fs
+	paths f (Check fs ghcs) = Check <$> (each . paths) f fs <*> pure ghcs
+	paths f (CheckLint fs ghcs) = CheckLint <$> (each . paths) f fs <*> pure ghcs
+	paths f (Types fs ghcs) = Types <$> (each . paths) f fs <*> pure ghcs
+	paths f (GhcEval e mf) = GhcEval e <$> traverse (paths f) mf
+	paths _ c = pure c
+
+instance Paths FileSource where
+	paths f (FileSource fpath mcts) = FileSource <$> f fpath <*> pure mcts
+
+instance Paths TargetFilter where
+	paths f (TargetFile fpath) = TargetFile <$> f fpath
+	paths f (TargetPackageDb pdb) = TargetPackageDb <$> paths f pdb
+	paths f (TargetSandbox c) = TargetSandbox <$> paths f c
+	paths _ t = pure t
+
+instance Paths [TargetFilter] where
+	paths = each . paths
+
+instance FromCmd Command where
+	cmdP = subparser $ mconcat [
+		cmd "ping" "ping server" (pure Ping),
+		cmd "listen" "listen server log" (Listen <$> optional logLevelArg),
+		cmd "set-log" "set log level" (SetLogLevel <$> strArgument idm),
+		cmd "add" "add info to database" (AddData <$> option readJSON idm),
+		cmd "scan" "scan sources" $ Scan <$>
+			many projectArg <*>
+			cabalFlag <*>
+			many sandboxArg <*>
+			many cmdP <*>
+			many (pathArg $ help "path") <*>
+			ghcOpts <*>
+			docsFlag <*>
+			inferFlag,
+		cmd "docs" "scan docs" $ RefineDocs <$> many projectArg <*> many fileArg <*> many moduleArg,
+		cmd "infer" "infer types" $ InferTypes <$> many projectArg <*> many fileArg <*> many moduleArg,
+		cmd "remove" "remove modules info" $ Remove <$>
+			many projectArg <*>
+			cabalFlag <*>
+			many sandboxArg <*>
+			many fileArg,
+		cmd "remove-all" "remove all data" (pure RemoveAll),
+		cmd "modules" "list modules" (InfoModules <$> many cmdP),
+		cmd "packages" "list packages" (pure InfoPackages),
+		cmd "projects" "list projects" (pure InfoProjects),
+		cmd "sandboxes" "list sandboxes" (pure InfoSandboxes),
+		cmd "symbol" "get symbol info" (InfoSymbol <$> cmdP <*> many cmdP <*> localsFlag),
+		cmd "module" "get module info" (InfoModule <$> cmdP <*> many cmdP),
+		cmd "resolve" "resolve module scope (or exports)" (InfoResolve <$> fileArg <*> exportsFlag),
+		cmd "project" "get project info" (InfoProject <$> ((Left <$> projectArg) <|> (Right <$> pathArg idm))),
+		cmd "sandbox" "get sandbox info" (InfoSandbox <$> pathArg (help "locate sandbox in parent of this path")),
+		cmd "lookup" "lookup for symbol" (Lookup <$> strArgument idm <*> ctx),
+		cmd "whois" "get info for symbol" (Whois <$> strArgument idm <*> ctx),
+		cmd "scope" "get declarations accessible from module or within a project" (
+			subparser (cmd "modules" "get modules accessible from module or within a project" (ResolveScopeModules <$> cmdP <*> ctx)) <|>
+			ResolveScope <$> cmdP <*> globalFlag <*> ctx),
+		cmd "complete" "show completions for input" (Complete <$> strArgument idm <*> wideFlag <*> ctx),
+		cmd "hayoo" "find declarations online via Hayoo" (Hayoo <$> strArgument idm <*> hayooPageArg <*> hayooPagesArg),
+		cmd "cabal" "cabal commands" (subparser $ cmd "list" "list cabal packages" (CabalList <$> many (strArgument idm))),
+		cmd "lint" "lint source files or file contents" (Lint <$> many cmdP),
+		cmd "check" "check source files or file contents" (Check <$> many cmdP <*> ghcOpts),
+		cmd "check-lint" "check and lint source files or file contents" (CheckLint <$> many cmdP <*> ghcOpts),
+		cmd "types" "get types for file expressions" (Types <$> many cmdP <*> ghcOpts),
+		cmd "autofix" "autofix commands" (AutoFix <$> cmdP),
+		cmd "ghc" "ghc commands" (subparser $ cmd "eval" "evaluate expression" (GhcEval <$> many (strArgument idm) <*> optional cmdP)),
+		cmd "langs" "ghc language options" (pure Langs),
+		cmd "flags" "ghc flags" (pure Flags),
+		cmd "link" "link to server" (Link <$> holdFlag),
+		cmd "exit" "exit" (pure Exit)]
+
+instance FromCmd AutoFixCommand where
+	cmdP = subparser $ mconcat [
+		cmd "show" "generate corrections for check & lint messages" (AutoFixShow <$> option readJSON (long "data" <> metavar "message" <> help "messages to make fixes for")),
+		cmd "fix" "fix errors and return rest corrections with updated regions" (AutoFixFix <$>
+			option readJSON (long "data" <> metavar "message" <> help "messages to fix") <*>
+			option readJSON (long "rest" <> metavar "correction" <> short 'r' <> help "update corrections") <*>
+			pureFlag)]
+
+instance FromCmd FileSource where
+	cmdP = option readJSON (long "contents") <|> (FileSource <$> fileArg <*> pure Nothing)
+
+instance FromCmd TargetFilter where
+	cmdP = asum [
+		TargetProject <$> projectArg,
+		TargetFile <$> fileArg,
+		TargetModule <$> moduleArg,
+		TargetDepsOf <$> depsArg,
+		TargetPackageDb <$> packageDbArg,
+		flag' TargetCabal (long "cabal"),
+		TargetSandbox <$> sandboxArg,
+		TargetPackage <$> packageArg,
+		flag' TargetSourced (long "src"),
+		flag' TargetStandalone (long "stand")]
+
+instance FromCmd SearchQuery where
+	cmdP = SearchQuery <$> (strArgument idm <|> pure "") <*> asum [
+		flag' SearchExact (long "exact"),
+		flag' SearchRegex (long "regex"),
+		flag' SearchInfix (long "infix"),
+		flag' SearchSuffix (long "suffix"),
+		pure SearchPrefix <* switch (long "prefix")]
+
+readJSON :: FromJSON a => ReadM a
+readJSON = str >>= maybe (readerError "Can't parse JSON argument") return . decode . L.pack
+
+cabalFlag :: Parser Bool
+ctx :: Parser FilePath
+depsArg :: Parser String
+docsFlag :: Parser Bool
+exportsFlag :: Parser Bool
+fileArg :: Parser FilePath
+ghcOpts :: Parser [String]
+globalFlag :: Parser Bool
+hayooPageArg :: Parser Int
+hayooPagesArg :: Parser Int
+holdFlag :: Parser Bool
+inferFlag :: Parser Bool
+localsFlag :: Parser Bool
+moduleArg :: Parser String
+packageDbArg :: Parser PackageDb
+packageArg :: Parser String
+pathArg :: Mod OptionFields String -> Parser FilePath
+projectArg :: Parser String
+pureFlag :: Parser Bool
+sandboxArg :: Parser FilePath
+wideFlag :: Parser Bool
+
+cabalFlag = switch (long "cabal")
+ctx = fileArg
+depsArg = strOption (long "deps" <> metavar "object" <> help "filter to such that in dependency of specified object (file or project)")
+docsFlag = switch (long "docs" <> help "scan source file docs")
+exportsFlag = switch (long "exports" <> short 'e' <> help "resolve module exports")
+fileArg = strOption (long "file" <> metavar "path" <> short 'f')
+ghcOpts = many (strOption (long "ghc" <> metavar "option" <> short 'g' <> help "options to pass to GHC"))
+globalFlag = switch (long "global" <> help "scope of project")
+hayooPageArg = option auto (long "page" <> metavar "n" <> short 'p' <> help "page number (0 by default)" <> value 0)
+hayooPagesArg = option auto (long "pages" <> metavar "count" <> short 'n' <> help "pages count (1 by default)" <> value 1)
+holdFlag = switch (long "hold" <> short 'h' <> help "don't return any response")
+inferFlag = switch (long "infer" <> help "infer types")
+localsFlag = switch (long "locals" <> short 'l' <> help "look in local declarations")
+moduleArg = strOption (long "module" <> metavar "name" <> short 'm' <> help "module name")
+packageArg = strOption (long "package" <> metavar "name" <> help "module package")
+packageDbArg =
+	flag' GlobalDb (long "global-db" <> help "global package-db") <|>
+	flag' UserDb (long "user-db" <> help "user package-db") <|>
+	(PackageDb <$> strOption (long "package-db" <> metavar "path" <> help "custom package-db"))
+pathArg f = strOption (long "path" <> metavar "path" <> short 'p' <> f)
+projectArg = strOption (long "project" <> long "proj" <> metavar "project")
+pureFlag = switch (long "pure" <> help "don't modify actual file, just return result")
+sandboxArg = strOption (long "sandbox" <> metavar "path" <> help "path to cabal sandbox")
+wideFlag = switch (long "wide" <> short 'w' <> help "wide mode - complete as if there were no import lists")
+
+instance ToJSON Command where
+	toJSON Ping = cmdJson "ping" []
+	toJSON (Listen lev) = cmdJson "listen" ["level" .= lev]
+	toJSON (SetLogLevel lev) = cmdJson "set-log" ["level" .= lev]
+	toJSON (AddData cts) = cmdJson "add" ["data" .= cts]
+	toJSON (Scan projs cabal sboxes fs ps ghcs docs' infer') = cmdJson "scan" [
+		"projects" .= projs,
+		"cabal" .= cabal,
+		"sandboxes" .= sboxes,
+		"files" .= fs,
+		"paths" .= ps,
+		"ghc-opts" .= ghcs,
+		"docs" .= docs',
+		"infer" .= infer']
+	toJSON (RefineDocs projs fs ms) = cmdJson "docs" ["projects" .= projs, "files" .= fs, "modules" .= ms]
+	toJSON (InferTypes projs fs ms) = cmdJson "infer" ["projects" .= projs, "files" .= fs, "modules" .= ms]
+	toJSON (Remove projs cabal sboxes fs) = cmdJson "remove" ["projects" .= projs, "cabal" .= cabal, "sandboxes" .= sboxes, "files" .= fs]
+	toJSON RemoveAll = cmdJson "remove-all" []
+	toJSON (InfoModules tf) = cmdJson "modules" ["filters" .= tf]
+	toJSON InfoPackages = cmdJson "packages" []
+	toJSON InfoProjects = cmdJson "projects" []
+	toJSON InfoSandboxes = cmdJson "sandboxes" []
+	toJSON (InfoSymbol q tf l) = cmdJson "symbol" ["query" .= q, "filters" .= tf, "locals" .= l]
+	toJSON (InfoModule q tf) = cmdJson "module" ["query" .= q, "filters" .= tf]
+	toJSON (InfoResolve f es) = cmdJson "resolve" ["file" .= f, "exports" .= es]
+	toJSON (InfoProject p) = cmdJson "project" $ either (\pname -> ["name" .= pname]) (\ppath -> ["path" .= ppath]) p
+	toJSON (InfoSandbox p) = cmdJson "sandbox" ["path" .= p]
+	toJSON (Lookup n f) = cmdJson "lookup" ["name" .= n, "file" .= f]
+	toJSON (Whois n f) = cmdJson "whois" ["name" .= n, "file" .= f]
+	toJSON (ResolveScopeModules q f) = cmdJson "scope modules" ["query" .= q, "file" .= f]
+	toJSON (ResolveScope q g f) = cmdJson "scope" ["query" .= q, "global" .= g, "file" .= f]
+	toJSON (Complete q w f) = cmdJson "complete" ["prefix" .= q, "wide" .= w, "file" .= f]
+	toJSON (Hayoo q p ps) = cmdJson "hayoo" ["query" .= q, "page" .= p, "pages" .= ps]
+	toJSON (CabalList ps) = cmdJson "cabal list" ["packages" .= ps]
+	toJSON (Lint fs) = cmdJson "lint" ["files" .= fs]
+	toJSON (Check fs ghcs) = cmdJson "check" ["files" .= fs, "ghc-opts" .= ghcs]
+	toJSON (CheckLint fs ghcs) = cmdJson "check-lint" ["files" .= fs, "ghc-opts" .= ghcs]
+	toJSON (Types fs ghcs) = cmdJson "types" ["files" .= fs, "ghc-opts" .= ghcs]
+	toJSON (AutoFix acmd) = toJSON acmd
+	toJSON (GhcEval exprs f) = cmdJson "ghc eval" ["exprs" .= exprs, "file" .= f]
+	toJSON Langs = cmdJson "langs" []
+	toJSON Flags = cmdJson "flags" []
+	toJSON (Link h) = cmdJson "link" ["hold" .= h]
+	toJSON Exit = cmdJson "exit" []
+
+instance FromJSON Command where
+	parseJSON = withObject "command" $ \v -> asum [
+		guardCmd "ping" v *> pure Ping,
+		guardCmd "listen" v *> (Listen <$> v .::? "level"),
+		guardCmd "set-log" v *> (SetLogLevel <$> v .:: "level"),
+		guardCmd "add" v *> (AddData <$> v .:: "data"),
+		guardCmd "scan" v *> (Scan <$>
+			v .::?! "projects" <*>
+			(v .:: "cabal" <|> pure False) <*>
+			v .::?! "sandboxes" <*>
+			v .::?! "files" <*>
+			v .::?! "paths" <*>
+			v .::?! "ghc-opts" <*>
+			(v .:: "docs" <|> pure False) <*>
+			(v .:: "infer" <|> pure False)),
+		guardCmd "docs" v *> (RefineDocs <$> v .::?! "projects" <*> v .::?! "files" <*> v .::?! "modules"),
+		guardCmd "infer" v *> (InferTypes <$> v .::?! "projects" <*> v .::?! "files" <*> v .::?! "modules"),
+		guardCmd "remove" v *> (Remove <$>
+			v .::?! "projects" <*>
+			(v .:: "cabal" <|> pure False) <*>
+			v .::?! "sandboxes" <*>
+			v .::?! "files"),
+		guardCmd "remove-all" v *> pure RemoveAll,
+		guardCmd "modules" v *> (InfoModules <$> v .::?! "filters"),
+		guardCmd "packages" v *> pure InfoPackages,
+		guardCmd "projects" v *> pure InfoProjects,
+		guardCmd "sandboxes" v *> pure InfoSandboxes,
+		guardCmd "symbol" v *> (InfoSymbol <$> v .:: "query" <*> v .::?! "filters" <*> (v .:: "locals" <|> pure False)),
+		guardCmd "module" v *> (InfoModule <$> v .:: "query" <*> v .::?! "filters"),
+		guardCmd "resolve" v *> (InfoResolve <$> v .:: "file" <*> (v .:: "exports" <|> pure False)),
+		guardCmd "project" v *> (InfoProject <$> asum [Left <$> v .:: "name", Right <$> v .:: "path"]),
+		guardCmd "sandbox" v *> (InfoSandbox <$> v .:: "path"),
+		guardCmd "lookup" v *> (Lookup <$> v .:: "name" <*> v .:: "file"),
+		guardCmd "whois" v *> (Whois <$> v .:: "name" <*> v .:: "file"),
+		guardCmd "scope modules" v *> (ResolveScopeModules <$> v .:: "query" <*> v .:: "file"),
+		guardCmd "scope" v *> (ResolveScope <$> v .:: "query" <*> (v .:: "global" <|> pure False) <*> v .:: "file"),
+		guardCmd "complete" v *> (Complete <$> v .:: "prefix" <*> (v .:: "wide" <|> pure False) <*> v .:: "file"),
+		guardCmd "hayoo" v *> (Hayoo <$> v .:: "query" <*> (v .:: "page" <|> pure 0) <*> (v .:: "pages" <|> pure 1)),
+		guardCmd "cabal list" v *> (CabalList <$> v .::?! "packages"),
+		guardCmd "lint" v *> (Lint <$> v .::?! "files"),
+		guardCmd "check" v *> (Check <$> v .::?! "files" <*> v .::?! "ghc-opts"),
+		guardCmd "check-lint" v *> (CheckLint <$> v .::?! "files" <*> v .::?! "ghc-opts"),
+		guardCmd "types" v *> (Types <$> v .::?! "files" <*> v .::?! "ghc-opts"),
+		AutoFix <$> parseJSON (Object v),
+		guardCmd "ghc eval" v *> (GhcEval <$> v .::?! "exprs" <*> v .::? "file"),
+		guardCmd "langs" v *> pure Langs,
+		guardCmd "flags" v *> pure Flags,
+		guardCmd "link" v *> (Link <$> (v .:: "hold" <|> pure False)),
+		guardCmd "exit" v *> pure Exit]
+
+instance ToJSON AddedContents where
+	toJSON (AddedDatabase db) = object ["database" .= db]
+	toJSON (AddedModule im) = object ["module" .= im]
+	toJSON (AddedProject p) = object ["project" .= p]
+
+instance FromJSON AddedContents where
+	parseJSON = withObject "added-contents" $ \v -> asum [
+		AddedDatabase <$> v .:: "database",
+		AddedModule <$> v .:: "module",
+		AddedProject <$> v .:: "project"]
+
+instance ToJSON AutoFixCommand where
+	toJSON (AutoFixShow ns) = cmdJson "autofix show" ["messages" .= ns]
+	toJSON (AutoFixFix ns rests pure') = cmdJson "autofix fix" ["messages" .= ns, "rest" .= rests, "pure" .= pure']
+
+instance FromJSON AutoFixCommand where
+	parseJSON = withObject "auto-fix-command" $ \v -> asum [
+		guardCmd "autofix show" v *> (AutoFixShow <$> v .:: "messages"),
+		guardCmd "autofix fix" v *> (AutoFixFix <$> v .:: "messages" <*> v .::?! "rest" <*> (v .:: "pure" <|> pure True))]
+
+instance ToJSON FileSource where
+	toJSON (FileSource fpath mcts) = object ["file" .= fpath, "contents" .= mcts]
+
+instance FromJSON FileSource where
+	parseJSON = withObject "file-contents" $ \v -> FileSource <$> v .:: "file" <*> v .::? "contents"
+
+instance ToJSON TargetFilter where
+	toJSON (TargetProject pname) = object ["project" .= pname]
+	toJSON (TargetFile fpath) = object ["file" .= fpath]
+	toJSON (TargetModule mname) = object ["module" .= mname]
+	toJSON (TargetDepsOf dep) = object ["deps" .= dep]
+	toJSON (TargetPackageDb pdb) = object ["db" .= pdb]
+	toJSON TargetCabal = toJSON ("cabal" :: String)
+	toJSON (TargetSandbox sbox) = object ["sandbox" .= sbox]
+	toJSON (TargetPackage pname) = object ["package" .= pname]
+	toJSON TargetSourced = toJSON ("sourced" :: String)
+	toJSON TargetStandalone = toJSON ("standalone" :: String)
+
+instance FromJSON TargetFilter where
+	parseJSON j = obj j <|> str' where
+		obj = withObject "target-filter" $ \v -> asum [
+			TargetProject <$> v .:: "project",
+			TargetFile <$> v .:: "file",
+			TargetModule <$> v .:: "module",
+			TargetDepsOf <$> v .:: "deps",
+			TargetPackageDb <$> v .:: "db",
+			TargetSandbox <$> v .:: "sandbox",
+			TargetPackage <$> v .:: "package"]
+		str' = do
+			s <- parseJSON j :: A.Parser String
+			case s of
+				"cabal" -> return TargetCabal
+				"sourced" -> return TargetSourced
+				"standalone" -> return TargetStandalone
+				_ -> empty
+
+instance ToJSON SearchQuery where
+	toJSON (SearchQuery q st) = object ["input" .= q, "type" .= st]
+
+instance FromJSON SearchQuery where
+	parseJSON = withObject "search-query" $ \v -> SearchQuery <$> (v .:: "input" <|> pure "") <*> (v .:: "type" <|> pure SearchPrefix)
+
+instance ToJSON SearchType where
+	toJSON SearchExact = toJSON ("exact" :: String)
+	toJSON SearchPrefix = toJSON ("prefix" :: String)
+	toJSON SearchInfix = toJSON ("infix" :: String)
+	toJSON SearchSuffix = toJSON ("suffix" :: String)
+	toJSON SearchRegex = toJSON ("regex" :: String)
+
+instance FromJSON SearchType where
+	parseJSON v = do
+		str' <- parseJSON v :: A.Parser String
+		case str' of
+			"exact" -> return SearchExact
+			"prefix" -> return SearchPrefix
+			"infix" -> return SearchInfix
+			"suffix" -> return SearchInfix
+			"regex" -> return SearchRegex
+			_ -> empty
diff --git a/src/HsDev/Stack.hs b/src/HsDev/Stack.hs
--- a/src/HsDev/Stack.hs
+++ b/src/HsDev/Stack.hs
@@ -1,131 +1,131 @@
-{-# LANGUAGE TemplateHaskell, RankNTypes #-}
-
-module HsDev.Stack (
-	stack, yaml,
-	path, pathOf,
-	build, buildDeps, configure,
-	StackEnv(..), stackRoot, stackProject, stackConfig, stackGhc, stackSnapshot, stackLocal,
-	getStackEnv, projectEnv,
-	stackPackageDbStack,
-
-	stackCompiler, stackArch,
-
-	MaybeT(..)
-	) where
-
-import Control.Arrow
-import Control.Lens (makeLenses, Lens', at, ix, lens, (^?), (^.))
-import Control.Monad
-import Control.Monad.Trans.Maybe
-import Control.Monad.IO.Class
-import Data.Char
-import Data.Maybe
-import Data.Map (Map)
-import qualified Data.Map as M
-import Distribution.Compiler
-import Distribution.System
-import qualified Distribution.Text as T (display)
-import System.Directory
-import System.Environment
-import System.FilePath
-import System.Log.Simple (MonadLog(..))
-
-import qualified Packages as GHC
-
-import HsDev.Error
-import HsDev.PackageDb
-import qualified HsDev.Tools.Ghc.Compat as Compat
-import HsDev.Scan.Browse (withPackages)
-import HsDev.Util as Util
-import HsDev.Tools.Base (runTool_)
-
--- | Get compiler version
-stackCompiler :: MonadLog m => m String
-stackCompiler = do
-	res <- withPackages ["-no-user-package-db"] $
-		return .
-		map (GHC.packageNameString &&& GHC.packageVersion) .
-		fromMaybe [] .
-		Compat.pkgDatabase
-	let
-		compiler = T.display buildCompilerFlavor
-		CompilerId _ version' = buildCompilerId
-		ver = maybe (T.display version') T.display $ lookup compiler res
-	return $ compiler ++ "-" ++ ver
-
--- | Get arch for stack
-stackArch :: String
-stackArch = T.display buildArch
-
--- | Invoke stack command, we are trying to get actual stack near current hsdev executable
-stack :: MonadLog m => [String] -> m String
-stack cmd' = hsdevLiftIO $ do
-	curExe <- liftIO getExecutablePath
-	stackExe <- Util.withCurrentDirectory (takeDirectory curExe) $
-		liftIO (findExecutable "stack") >>= maybe (hsdevError $ ToolNotFound "stack") return
-	comp <- stackCompiler
-	liftIO $ runTool_ stackExe (["--compiler", comp, "--arch", stackArch] ++ cmd')
-
--- | Make yaml opts
-yaml :: Maybe FilePath -> [String]
-yaml Nothing = []
-yaml (Just y) = ["--stack-yaml", y]
-
-type Paths = Map String FilePath
-
--- | Stack path
-path :: MonadLog m => Maybe FilePath -> m Paths
-path mcfg = liftM (M.fromList . map breakPath . lines) $ stack ("path" : yaml mcfg) where
-	breakPath :: String -> (String, FilePath)
-	breakPath = second (dropWhile isSpace . drop 1) . break (== ':')
-
--- | Get path for
-pathOf :: String -> Lens' Paths (Maybe FilePath)
-pathOf = at
-
--- | Build stack project
-build :: MonadLog m => [String] -> Maybe FilePath -> m ()
-build opts mcfg = void $ stack $ "build" : (opts ++ yaml mcfg)
-
--- | Build only dependencies
-buildDeps :: MonadLog m => Maybe FilePath -> m ()
-buildDeps = build ["--only-dependencies"]
-
--- | Configure project
-configure :: MonadLog m => Maybe FilePath -> m ()
-configure = build ["--only-configure"]
-
-data StackEnv = StackEnv {
-	_stackRoot :: FilePath,
-	_stackProject :: FilePath,
-	_stackConfig :: FilePath,
-	_stackGhc :: FilePath,
-	_stackSnapshot :: FilePath,
-	_stackLocal :: FilePath }
-
-makeLenses ''StackEnv
-
-getStackEnv :: Paths -> Maybe StackEnv
-getStackEnv p = StackEnv <$>
-	(p ^. pathOf "stack-root") <*>
-	(p ^. pathOf "project-root") <*>
-	(p ^. pathOf "config-location") <*>
-	(p ^. pathOf "ghc-paths") <*>
-	(p ^. pathOf "snapshot-pkg-db") <*>
-	(p ^. pathOf "local-pkg-db")
-
--- | Projects paths
-projectEnv :: MonadLog m => FilePath -> m StackEnv
-projectEnv p = hsdevLiftIO $ Util.withCurrentDirectory p $ do
-	paths' <- path Nothing
-	maybe (hsdevError $ ToolError "stack" ("can't get paths for " ++ p)) return $ getStackEnv paths'
-
--- | Get package-db stack for stack environment
-stackPackageDbStack :: Lens' StackEnv PackageDbStack
-stackPackageDbStack = lens g s where
-	g :: StackEnv -> PackageDbStack
-	g env' = PackageDbStack $ map PackageDb [_stackLocal env', _stackSnapshot env']
-	s :: StackEnv -> PackageDbStack -> StackEnv
-	s env' pdbs = env' {
-		_stackSnapshot = fromMaybe (_stackSnapshot env') $ pdbs ^? packageDbStack . ix 1 . packageDb,
-		_stackLocal = fromMaybe (_stackLocal env') $ pdbs ^? packageDbStack . ix 0 . packageDb }
+{-# LANGUAGE TemplateHaskell, RankNTypes #-}
+
+module HsDev.Stack (
+	stack, yaml,
+	path, pathOf,
+	build, buildDeps, configure,
+	StackEnv(..), stackRoot, stackProject, stackConfig, stackGhc, stackSnapshot, stackLocal,
+	getStackEnv, projectEnv,
+	stackPackageDbStack,
+
+	stackCompiler, stackArch,
+
+	MaybeT(..)
+	) where
+
+import Control.Arrow
+import Control.Lens (makeLenses, Lens', at, ix, lens, (^?), (^.))
+import Control.Monad
+import Control.Monad.Trans.Maybe
+import Control.Monad.IO.Class
+import Data.Char
+import Data.Maybe
+import Data.Map (Map)
+import qualified Data.Map as M
+import Distribution.Compiler
+import Distribution.System
+import qualified Distribution.Text as T (display)
+import System.Directory
+import System.Environment
+import System.FilePath
+import System.Log.Simple (MonadLog(..))
+
+import qualified Packages as GHC
+
+import HsDev.Error
+import HsDev.PackageDb
+import qualified HsDev.Tools.Ghc.Compat as Compat
+import HsDev.Scan.Browse (withPackages)
+import HsDev.Util as Util
+import HsDev.Tools.Base (runTool_)
+
+-- | Get compiler version
+stackCompiler :: MonadLog m => m String
+stackCompiler = do
+	res <- withPackages ["-no-user-package-db"] $
+		return .
+		map (GHC.packageNameString &&& GHC.packageVersion) .
+		fromMaybe [] .
+		Compat.pkgDatabase
+	let
+		compiler = T.display buildCompilerFlavor
+		CompilerId _ version' = buildCompilerId
+		ver = maybe (T.display version') T.display $ lookup compiler res
+	return $ compiler ++ "-" ++ ver
+
+-- | Get arch for stack
+stackArch :: String
+stackArch = T.display buildArch
+
+-- | Invoke stack command, we are trying to get actual stack near current hsdev executable
+stack :: MonadLog m => [String] -> m String
+stack cmd' = hsdevLiftIO $ do
+	curExe <- liftIO getExecutablePath
+	stackExe <- Util.withCurrentDirectory (takeDirectory curExe) $
+		liftIO (findExecutable "stack") >>= maybe (hsdevError $ ToolNotFound "stack") return
+	comp <- stackCompiler
+	liftIO $ runTool_ stackExe (["--compiler", comp, "--arch", stackArch] ++ cmd')
+
+-- | Make yaml opts
+yaml :: Maybe FilePath -> [String]
+yaml Nothing = []
+yaml (Just y) = ["--stack-yaml", y]
+
+type Paths = Map String FilePath
+
+-- | Stack path
+path :: MonadLog m => Maybe FilePath -> m Paths
+path mcfg = liftM (M.fromList . map breakPath . lines) $ stack ("path" : yaml mcfg) where
+	breakPath :: String -> (String, FilePath)
+	breakPath = second (dropWhile isSpace . drop 1) . break (== ':')
+
+-- | Get path for
+pathOf :: String -> Lens' Paths (Maybe FilePath)
+pathOf = at
+
+-- | Build stack project
+build :: MonadLog m => [String] -> Maybe FilePath -> m ()
+build opts mcfg = void $ stack $ "build" : (opts ++ yaml mcfg)
+
+-- | Build only dependencies
+buildDeps :: MonadLog m => Maybe FilePath -> m ()
+buildDeps = build ["--only-dependencies"]
+
+-- | Configure project
+configure :: MonadLog m => Maybe FilePath -> m ()
+configure = build ["--only-configure"]
+
+data StackEnv = StackEnv {
+	_stackRoot :: FilePath,
+	_stackProject :: FilePath,
+	_stackConfig :: FilePath,
+	_stackGhc :: FilePath,
+	_stackSnapshot :: FilePath,
+	_stackLocal :: FilePath }
+
+makeLenses ''StackEnv
+
+getStackEnv :: Paths -> Maybe StackEnv
+getStackEnv p = StackEnv <$>
+	(p ^. pathOf "stack-root") <*>
+	(p ^. pathOf "project-root") <*>
+	(p ^. pathOf "config-location") <*>
+	(p ^. pathOf "ghc-paths") <*>
+	(p ^. pathOf "snapshot-pkg-db") <*>
+	(p ^. pathOf "local-pkg-db")
+
+-- | Projects paths
+projectEnv :: MonadLog m => FilePath -> m StackEnv
+projectEnv p = hsdevLiftIO $ Util.withCurrentDirectory p $ do
+	paths' <- path Nothing
+	maybe (hsdevError $ ToolError "stack" ("can't get paths for " ++ p)) return $ getStackEnv paths'
+
+-- | Get package-db stack for stack environment
+stackPackageDbStack :: Lens' StackEnv PackageDbStack
+stackPackageDbStack = lens g s where
+	g :: StackEnv -> PackageDbStack
+	g env' = PackageDbStack $ map PackageDb [_stackLocal env', _stackSnapshot env']
+	s :: StackEnv -> PackageDbStack -> StackEnv
+	s env' pdbs = env' {
+		_stackSnapshot = fromMaybe (_stackSnapshot env') $ pdbs ^? packageDbStack . ix 1 . packageDb,
+		_stackLocal = fromMaybe (_stackLocal env') $ pdbs ^? packageDbStack . ix 0 . packageDb }
diff --git a/src/HsDev/Symbols.hs b/src/HsDev/Symbols.hs
--- a/src/HsDev/Symbols.hs
+++ b/src/HsDev/Symbols.hs
@@ -1,278 +1,278 @@
-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Symbols (
-	-- * Information
-	export,
-	passThingSpec, passImportSpec, imported, exported,
-	importNames, import_,
-	Symbol(..),
-	unnamedModuleId,
-	sortDeclarations, moduleLocals,
-	setDefinedIn, dropExternals, clearDefinedIn,
-	moduleLocalDeclarations, moduleModuleDeclarations,
-	Locals(..),
-	decl, definedIn, declarationLocals, scopes,
-	mergeExported,
-
-	-- * Functions
-	importQualifier,
-
-	-- * Utility
-	locateProject, searchProject,
-	locateSourceDir,
-	standaloneInfo,
-	moduleOpts,
-
-	-- * Modifiers
-	addDeclaration,
-
-	-- * Other
-	unalias,
-
-	-- * Tags
-	setTag, hasTag, removeTag, dropTags,
-
-	-- * Reexportss
-	module HsDev.Symbols.Types,
-	module HsDev.Symbols.Class,
-	module HsDev.Symbols.Documented
-	) where
-
-import Control.Applicative
-import Control.Arrow
-import Control.Lens
-import Control.Monad.Trans.Maybe
-import Control.Monad.Except
-import Data.Function (on)
-import Data.List
-import Data.Maybe (fromMaybe, listToMaybe, catMaybes)
-import qualified Data.Map as M
-import Data.Ord (comparing)
-import qualified Data.Set as S
-import Data.Text (Text)
-import qualified Data.Text as T (concat)
-import System.Directory
-import System.FilePath
-
-import HsDev.Symbols.Types
-import HsDev.Symbols.Class
-import HsDev.Symbols.Documented (Documented(..))
-import HsDev.Util (searchPath, uniqueBy, directoryContents)
-
--- | Get name of export
-export :: Export -> Text
-export (ExportName Nothing n _) = n
-export (ExportName (Just q) n _) = T.concat [q, ".", n]
-export (ExportModule m) = m
-
--- | Does name pass thing spec
-passThingSpec :: Text -> ThingPart -> Bool
-passThingSpec _ ThingNothing = False
-passThingSpec _ ThingAll = True
-passThingSpec n (ThingWith ns) = n `elem` ns
-
--- | Does declaration pass import-list spec
-passImportSpec :: Declaration -> ImportSpec -> Bool
-passImportSpec decl' (ImportSpec n p)
-	| view declarationName decl' == n = True
-	| preview (declaration . related . _Just) decl' == Just n = view declarationName decl' `passThingSpec` p
-	| otherwise = False
-
--- | Check whether declaration passes import list
-imported :: Declaration -> ImportList -> Bool
-imported decl' (ImportList hiding specs) = invert $ any (decl' `passImportSpec`) specs where
-	invert = if hiding then not else id
-
--- | Check whether declaration passes export
-exported :: Declaration -> Export -> Bool
-exported decl' (ExportName q n p)
-	| view declarationName decl' == n = checkImport
-	| preview (declaration . related . _Just) decl' == Just n = view declarationName decl' `passThingSpec` p
-	| otherwise = False
-	where
-		checkImport = case q of
-			Nothing -> any (not . view importIsQualified) $ fromMaybe [] $ view declarationImported decl'
-			Just q' -> any ((q' `elem`) . importNames) $ fromMaybe [] $ view declarationImported decl'
-exported decl' (ExportModule m) = any (unqualBy m) . fromMaybe [] . view declarationImported $ decl' where
-	unqualBy :: Text -> Import -> Bool
-	unqualBy m' i = m' `elem` importNames i && not (view importIsQualified i)
-
--- | Get import module names - full and synonym
-importNames :: Import -> [Text]
-importNames i = view importModuleName i : maybe [] return (view importAs i)
-
--- | Simple import
-import_ :: Text -> Import
-import_ n = Import n False Nothing Nothing Nothing
-
--- | Imported module can be accessed via qualifier
-importQualifier :: Maybe Text -> Import -> Bool
-importQualifier Nothing i
-	| not (view importIsQualified i) = True
-	| otherwise = False
-importQualifier (Just q) i
-	| q == view importModuleName i = True
-	| Just q == view importAs i = True
-	| otherwise = False
-
-unnamedModuleId :: ModuleLocation -> ModuleId
-unnamedModuleId = ModuleId ""
-
-sortDeclarations :: [Declaration] -> [Declaration]
-sortDeclarations = sortBy (comparing (view declarationName))
-
--- | Bring locals to top
-moduleLocals :: Module -> Module
-moduleLocals m = set moduleDeclarations (moduleLocalDeclarations m) m
-
--- | Set all declaration `definedIn` to this module
-setDefinedIn :: Module -> Module
-setDefinedIn m = over moduleDeclarations (map (`definedIn` view moduleId m)) m
-
--- | Drop all declarations, that not defined in this module
-dropExternals :: Module -> Module
-dropExternals m = over moduleDeclarations (filter ((/= Just (view moduleId m)) . view declarationDefined)) m
-
--- | Clear `definedIn` information
-clearDefinedIn :: Module -> Module
-clearDefinedIn = over moduleDeclarations (map (set declarationDefined Nothing))
-
--- | Get declarations with locals
-moduleLocalDeclarations :: Module -> [Declaration]
-moduleLocalDeclarations =
-	sortDeclarations .
-	concatMap declarationLocals' .
-	view moduleDeclarations
-	where
-		declarationLocals' :: Declaration -> [Declaration]
-		declarationLocals' d = d : declarationLocals d
-
--- | Get list of declarations as ModuleDeclaration
-moduleModuleDeclarations :: Module -> [ModuleDeclaration]
-moduleModuleDeclarations m = [ModuleDeclaration (view moduleId m) d | d <- view moduleDeclarations m]
-
-class Locals a where
-	locals :: a -> [Declaration]
-	where_ :: a -> [Declaration] -> a
-
-instance Locals Declaration where
-	locals = locals . view declaration
-	where_ d ds = over declaration (`where_` ds) d
-
-decl :: Text -> DeclarationInfo -> Declaration
-decl n = Declaration n Nothing Nothing Nothing Nothing
-
-definedIn :: Declaration -> ModuleId -> Declaration
-definedIn d m = set declarationDefined (Just m) d
-
-declarationLocals :: Declaration -> [Declaration]
-declarationLocals d = locals $ view declaration d
-
--- | Get scopes of @Declaration@, where @Nothing@ is global scope
-scopes :: Declaration -> [Maybe Text]
-scopes d = globalScope $ concatMap (map Just . importNames) is where
-	is = fromMaybe [] $ view declarationImported d
-	globalScope
-		| any (not . view importIsQualified) is = (Nothing :)
-		| otherwise = id
-
-instance Locals DeclarationInfo where
-	locals (Function _ ds _) = ds
-	locals _ = []
-	where_ (Function n s r) ds = Function n (s ++ ds) r
-	where_ d _ = d
-
--- | Merge @ModuleDeclaration@ into @ExportedDeclaration@
-mergeExported :: [ModuleDeclaration] -> [ExportedDeclaration]
-mergeExported =
-	map merge' .
-	groupBy ((==) `on` declId) .
-	sortBy (comparing declId)
-	where
-		declId :: ModuleDeclaration -> (Text, Maybe ModuleId)
-		declId = view moduleDeclaration >>> (view declarationName &&& view declarationDefined)
-		merge' :: [ModuleDeclaration] -> ExportedDeclaration
-		merge' [] = error "mergeExported: impossible"
-		merge' ds@(d:_) = ExportedDeclaration (map (view declarationModuleId) ds) (view moduleDeclaration d)
-
--- | Find project file is related to
-locateProject :: FilePath -> IO (Maybe Project)
-locateProject file = do
-	file' <- canonicalizePath file
-	isDir <- doesDirectoryExist file'
-	if isDir then locateHere file' else locateParent (takeDirectory file')
-	where
-		locateHere path = do
-			cts <- filter (not . null . takeBaseName) <$> directoryContents path
-			return $ fmap (project . (path </>)) $ find ((== ".cabal") . takeExtension) cts
-		locateParent dir = do
-			cts <- filter (not . null . takeBaseName) <$> directoryContents dir
-			case find ((== ".cabal") . takeExtension) cts of
-				Nothing -> if isDrive dir then return Nothing else locateParent (takeDirectory dir)
-				Just cabalf -> return $ Just $ project (dir </> cabalf)
-
--- | Search project up
-searchProject :: FilePath -> IO (Maybe Project)
-searchProject file = runMaybeT $ searchPath file (MaybeT . locateProject) <|> mzero
-
--- | Locate source dir of file
-locateSourceDir :: FilePath -> IO (Maybe (Extensions FilePath))
-locateSourceDir f = runMaybeT $ do
-	file <- liftIO $ canonicalizePath f
-	p <- MaybeT $ locateProject file
-	proj <- lift $ loadProject p
-	MaybeT $ return $ findSourceDir proj file
-
--- | Make `Info` for standalone `Module`
-standaloneInfo :: [PackageConfig] -> Module -> Info
-standaloneInfo pkgs m = mempty { _infoDepends = pkgDeps ^.. each . package . packageName } where
-	pkgDeps = catMaybes [M.lookup mdep pkgMap >>= listToMaybe | mdep <- "Prelude" : (m ^.. moduleImports . each . importModuleName)]
-	pkgMap = M.unionsWith mergePkgs [M.singleton m' [p] | p <- pkgs, m' <- view packageModules p]
-	mergePkgs ls rs = if null es then hs else es where
-		(es, hs) = partition (view packageExposed) $ uniqueBy (view package) (ls ++ rs)
-
--- | Options for GHC of module and project
-moduleOpts :: [PackageConfig] -> Module -> [String]
-moduleOpts pkgs m = case view moduleLocation m of
-	FileModule file proj -> concat [
-		hidePackages,
-		targetOpts info']
-		where
-			infos' = maybe [standaloneInfo pkgs m] (`fileTargets` file) proj
-			info' = over infoDepends (filter validDep) (mconcat $ selfInfo : infos')
-			selfInfo
-				| proj ^? _Just . projectName `elem` map Just (infos' ^.. each . infoDepends . each) = fromMaybe mempty $
-					proj ^? _Just . projectDescription . _Just . projectLibrary . _Just . libraryBuildInfo
-				| otherwise = mempty
-			-- filter out unavailable packages such as unix under windows
-			validDep d = d `elem` pkgs'
-			pkgs' = pkgs ^.. each . package . packageName
-			hidePackages
-				| null (info' ^. infoDepends) = []
-				| otherwise = ["-hide-all-packages"]
-	_ -> []
-
--- | Add declaration to module
-addDeclaration :: Declaration -> Module -> Module
-addDeclaration decl' = over moduleDeclarations (sortDeclarations . (decl' :))
-
--- | Unalias import name
-unalias :: Module -> Text -> [Text]
-unalias m alias = [view importModuleName i | i <- view moduleImports m, view importAs i == Just alias]
-
--- | Set tag to `Inspected`
-setTag :: Ord t => t -> Inspected i t a -> Inspected i t a
-setTag tag = over inspectionTags (S.insert tag)
-
--- | Check whether `Inspected` has tag
-hasTag :: Ord t => t -> Inspected i t a -> Bool
-hasTag tag = has (inspectionTags . ix tag)
-
--- | Drop tag from `Inspected`
-removeTag :: Ord t => t -> Inspected i t a -> Inspected i t a
-removeTag tag = over inspectionTags (S.delete tag)
-
--- | Drop all tags
-dropTags :: Inspected i t a -> Inspected i t a
-dropTags = set inspectionTags S.empty
+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Symbols (
+	-- * Information
+	export,
+	passThingSpec, passImportSpec, imported, exported,
+	importNames, import_,
+	Symbol(..),
+	unnamedModuleId,
+	sortDeclarations, moduleLocals,
+	setDefinedIn, dropExternals, clearDefinedIn,
+	moduleLocalDeclarations, moduleModuleDeclarations,
+	Locals(..),
+	decl, definedIn, declarationLocals, scopes,
+	mergeExported,
+
+	-- * Functions
+	importQualifier,
+
+	-- * Utility
+	locateProject, searchProject,
+	locateSourceDir,
+	standaloneInfo,
+	moduleOpts,
+
+	-- * Modifiers
+	addDeclaration,
+
+	-- * Other
+	unalias,
+
+	-- * Tags
+	setTag, hasTag, removeTag, dropTags,
+
+	-- * Reexportss
+	module HsDev.Symbols.Types,
+	module HsDev.Symbols.Class,
+	module HsDev.Symbols.Documented
+	) where
+
+import Control.Applicative
+import Control.Arrow
+import Control.Lens
+import Control.Monad.Trans.Maybe
+import Control.Monad.Except
+import Data.Function (on)
+import Data.List
+import Data.Maybe (fromMaybe, listToMaybe, catMaybes)
+import qualified Data.Map as M
+import Data.Ord (comparing)
+import qualified Data.Set as S
+import Data.Text (Text)
+import qualified Data.Text as T (concat)
+import System.Directory
+import System.FilePath
+
+import HsDev.Symbols.Types
+import HsDev.Symbols.Class
+import HsDev.Symbols.Documented (Documented(..))
+import HsDev.Util (searchPath, uniqueBy, directoryContents)
+
+-- | Get name of export
+export :: Export -> Text
+export (ExportName Nothing n _) = n
+export (ExportName (Just q) n _) = T.concat [q, ".", n]
+export (ExportModule m) = m
+
+-- | Does name pass thing spec
+passThingSpec :: Text -> ThingPart -> Bool
+passThingSpec _ ThingNothing = False
+passThingSpec _ ThingAll = True
+passThingSpec n (ThingWith ns) = n `elem` ns
+
+-- | Does declaration pass import-list spec
+passImportSpec :: Declaration -> ImportSpec -> Bool
+passImportSpec decl' (ImportSpec n p)
+	| view declarationName decl' == n = True
+	| preview (declaration . related . _Just) decl' == Just n = view declarationName decl' `passThingSpec` p
+	| otherwise = False
+
+-- | Check whether declaration passes import list
+imported :: Declaration -> ImportList -> Bool
+imported decl' (ImportList hiding specs) = invert $ any (decl' `passImportSpec`) specs where
+	invert = if hiding then not else id
+
+-- | Check whether declaration passes export
+exported :: Declaration -> Export -> Bool
+exported decl' (ExportName q n p)
+	| view declarationName decl' == n = checkImport
+	| preview (declaration . related . _Just) decl' == Just n = view declarationName decl' `passThingSpec` p
+	| otherwise = False
+	where
+		checkImport = case q of
+			Nothing -> any (not . view importIsQualified) $ fromMaybe [] $ view declarationImported decl'
+			Just q' -> any ((q' `elem`) . importNames) $ fromMaybe [] $ view declarationImported decl'
+exported decl' (ExportModule m) = any (unqualBy m) . fromMaybe [] . view declarationImported $ decl' where
+	unqualBy :: Text -> Import -> Bool
+	unqualBy m' i = m' `elem` importNames i && not (view importIsQualified i)
+
+-- | Get import module names - full and synonym
+importNames :: Import -> [Text]
+importNames i = view importModuleName i : maybe [] return (view importAs i)
+
+-- | Simple import
+import_ :: Text -> Import
+import_ n = Import n False Nothing Nothing Nothing
+
+-- | Imported module can be accessed via qualifier
+importQualifier :: Maybe Text -> Import -> Bool
+importQualifier Nothing i
+	| not (view importIsQualified i) = True
+	| otherwise = False
+importQualifier (Just q) i
+	| q == view importModuleName i = True
+	| Just q == view importAs i = True
+	| otherwise = False
+
+unnamedModuleId :: ModuleLocation -> ModuleId
+unnamedModuleId = ModuleId ""
+
+sortDeclarations :: [Declaration] -> [Declaration]
+sortDeclarations = sortBy (comparing (view declarationName))
+
+-- | Bring locals to top
+moduleLocals :: Module -> Module
+moduleLocals m = set moduleDeclarations (moduleLocalDeclarations m) m
+
+-- | Set all declaration `definedIn` to this module
+setDefinedIn :: Module -> Module
+setDefinedIn m = over moduleDeclarations (map (`definedIn` view moduleId m)) m
+
+-- | Drop all declarations, that not defined in this module
+dropExternals :: Module -> Module
+dropExternals m = over moduleDeclarations (filter ((/= Just (view moduleId m)) . view declarationDefined)) m
+
+-- | Clear `definedIn` information
+clearDefinedIn :: Module -> Module
+clearDefinedIn = over moduleDeclarations (map (set declarationDefined Nothing))
+
+-- | Get declarations with locals
+moduleLocalDeclarations :: Module -> [Declaration]
+moduleLocalDeclarations =
+	sortDeclarations .
+	concatMap declarationLocals' .
+	view moduleDeclarations
+	where
+		declarationLocals' :: Declaration -> [Declaration]
+		declarationLocals' d = d : declarationLocals d
+
+-- | Get list of declarations as ModuleDeclaration
+moduleModuleDeclarations :: Module -> [ModuleDeclaration]
+moduleModuleDeclarations m = [ModuleDeclaration (view moduleId m) d | d <- view moduleDeclarations m]
+
+class Locals a where
+	locals :: a -> [Declaration]
+	where_ :: a -> [Declaration] -> a
+
+instance Locals Declaration where
+	locals = locals . view declaration
+	where_ d ds = over declaration (`where_` ds) d
+
+decl :: Text -> DeclarationInfo -> Declaration
+decl n = Declaration n Nothing Nothing Nothing Nothing
+
+definedIn :: Declaration -> ModuleId -> Declaration
+definedIn d m = set declarationDefined (Just m) d
+
+declarationLocals :: Declaration -> [Declaration]
+declarationLocals d = locals $ view declaration d
+
+-- | Get scopes of @Declaration@, where @Nothing@ is global scope
+scopes :: Declaration -> [Maybe Text]
+scopes d = globalScope $ concatMap (map Just . importNames) is where
+	is = fromMaybe [] $ view declarationImported d
+	globalScope
+		| any (not . view importIsQualified) is = (Nothing :)
+		| otherwise = id
+
+instance Locals DeclarationInfo where
+	locals (Function _ ds _) = ds
+	locals _ = []
+	where_ (Function n s r) ds = Function n (s ++ ds) r
+	where_ d _ = d
+
+-- | Merge @ModuleDeclaration@ into @ExportedDeclaration@
+mergeExported :: [ModuleDeclaration] -> [ExportedDeclaration]
+mergeExported =
+	map merge' .
+	groupBy ((==) `on` declId) .
+	sortBy (comparing declId)
+	where
+		declId :: ModuleDeclaration -> (Text, Maybe ModuleId)
+		declId = view moduleDeclaration >>> (view declarationName &&& view declarationDefined)
+		merge' :: [ModuleDeclaration] -> ExportedDeclaration
+		merge' [] = error "mergeExported: impossible"
+		merge' ds@(d:_) = ExportedDeclaration (map (view declarationModuleId) ds) (view moduleDeclaration d)
+
+-- | Find project file is related to
+locateProject :: FilePath -> IO (Maybe Project)
+locateProject file = do
+	file' <- canonicalizePath file
+	isDir <- doesDirectoryExist file'
+	if isDir then locateHere file' else locateParent (takeDirectory file')
+	where
+		locateHere path = do
+			cts <- filter (not . null . takeBaseName) <$> directoryContents path
+			return $ fmap (project . (path </>)) $ find ((== ".cabal") . takeExtension) cts
+		locateParent dir = do
+			cts <- filter (not . null . takeBaseName) <$> directoryContents dir
+			case find ((== ".cabal") . takeExtension) cts of
+				Nothing -> if isDrive dir then return Nothing else locateParent (takeDirectory dir)
+				Just cabalf -> return $ Just $ project (dir </> cabalf)
+
+-- | Search project up
+searchProject :: FilePath -> IO (Maybe Project)
+searchProject file = runMaybeT $ searchPath file (MaybeT . locateProject) <|> mzero
+
+-- | Locate source dir of file
+locateSourceDir :: FilePath -> IO (Maybe (Extensions FilePath))
+locateSourceDir f = runMaybeT $ do
+	file <- liftIO $ canonicalizePath f
+	p <- MaybeT $ locateProject file
+	proj <- lift $ loadProject p
+	MaybeT $ return $ findSourceDir proj file
+
+-- | Make `Info` for standalone `Module`
+standaloneInfo :: [PackageConfig] -> Module -> Info
+standaloneInfo pkgs m = mempty { _infoDepends = pkgDeps ^.. each . package . packageName } where
+	pkgDeps = catMaybes [M.lookup mdep pkgMap >>= listToMaybe | mdep <- "Prelude" : (m ^.. moduleImports . each . importModuleName)]
+	pkgMap = M.unionsWith mergePkgs [M.singleton m' [p] | p <- pkgs, m' <- view packageModules p]
+	mergePkgs ls rs = if null es then hs else es where
+		(es, hs) = partition (view packageExposed) $ uniqueBy (view package) (ls ++ rs)
+
+-- | Options for GHC of module and project
+moduleOpts :: [PackageConfig] -> Module -> [String]
+moduleOpts pkgs m = case view moduleLocation m of
+	FileModule file proj -> concat [
+		hidePackages,
+		targetOpts info']
+		where
+			infos' = maybe [standaloneInfo pkgs m] (`fileTargets` file) proj
+			info' = over infoDepends (filter validDep) (mconcat $ selfInfo : infos')
+			selfInfo
+				| proj ^? _Just . projectName `elem` map Just (infos' ^.. each . infoDepends . each) = fromMaybe mempty $
+					proj ^? _Just . projectDescription . _Just . projectLibrary . _Just . libraryBuildInfo
+				| otherwise = mempty
+			-- filter out unavailable packages such as unix under windows
+			validDep d = d `elem` pkgs'
+			pkgs' = pkgs ^.. each . package . packageName
+			hidePackages
+				| null (info' ^. infoDepends) = []
+				| otherwise = ["-hide-all-packages"]
+	_ -> []
+
+-- | Add declaration to module
+addDeclaration :: Declaration -> Module -> Module
+addDeclaration decl' = over moduleDeclarations (sortDeclarations . (decl' :))
+
+-- | Unalias import name
+unalias :: Module -> Text -> [Text]
+unalias m alias = [view importModuleName i | i <- view moduleImports m, view importAs i == Just alias]
+
+-- | Set tag to `Inspected`
+setTag :: Ord t => t -> Inspected i t a -> Inspected i t a
+setTag tag = over inspectionTags (S.insert tag)
+
+-- | Check whether `Inspected` has tag
+hasTag :: Ord t => t -> Inspected i t a -> Bool
+hasTag tag = has (inspectionTags . ix tag)
+
+-- | Drop tag from `Inspected`
+removeTag :: Ord t => t -> Inspected i t a -> Inspected i t a
+removeTag tag = over inspectionTags (S.delete tag)
+
+-- | Drop all tags
+dropTags :: Inspected i t a -> Inspected i t a
+dropTags = set inspectionTags S.empty
diff --git a/src/HsDev/Symbols/Class.hs b/src/HsDev/Symbols/Class.hs
--- a/src/HsDev/Symbols/Class.hs
+++ b/src/HsDev/Symbols/Class.hs
@@ -1,20 +1,20 @@
-module HsDev.Symbols.Class (
-	Symbol(..),
-	symbolModuleLocation,
-
-	module HsDev.Symbols.Location
-	) where
-
-import Control.Lens (view)
-import Data.Text (Text)
-
-import HsDev.Symbols.Location
-
-class Symbol a where
-	symbolName :: a -> Text
-	symbolQualifiedName :: a -> Text
-	symbolDocs :: a -> Maybe Text
-	symbolLocation :: a -> Location
-
-symbolModuleLocation :: Symbol a => a -> ModuleLocation
-symbolModuleLocation = view locationModule . symbolLocation
+module HsDev.Symbols.Class (
+	Symbol(..),
+	symbolModuleLocation,
+
+	module HsDev.Symbols.Location
+	) where
+
+import Control.Lens (view)
+import Data.Text (Text)
+
+import HsDev.Symbols.Location
+
+class Symbol a where
+	symbolName :: a -> Text
+	symbolQualifiedName :: a -> Text
+	symbolDocs :: a -> Maybe Text
+	symbolLocation :: a -> Location
+
+symbolModuleLocation :: Symbol a => a -> ModuleLocation
+symbolModuleLocation = view locationModule . symbolLocation
diff --git a/src/HsDev/Symbols/Documented.hs b/src/HsDev/Symbols/Documented.hs
--- a/src/HsDev/Symbols/Documented.hs
+++ b/src/HsDev/Symbols/Documented.hs
@@ -1,24 +1,24 @@
-module HsDev.Symbols.Documented (
-	Documented(..),
-	defaultDetailed
-	) where
-
-import Data.Text (unpack)
-
-import HsDev.Symbols.Class
-
--- | Documented symbol
-class Symbol a => Documented a where
-	brief :: a -> String
-	detailed :: a -> String
-	detailed = unlines . defaultDetailed
-
--- | Default detailed docs
-defaultDetailed :: Documented a => a -> [String]
-defaultDetailed s = header ++ docs ++ loc where
-	header = [brief s, ""]
-	docs = maybe [] (return . unpack) $ symbolDocs s
-	loc
-		| null mloc = []
-		| otherwise = ["Defined at " ++ mloc]
-	mloc = show (symbolLocation s)
+module HsDev.Symbols.Documented (
+	Documented(..),
+	defaultDetailed
+	) where
+
+import Data.Text (unpack)
+
+import HsDev.Symbols.Class
+
+-- | Documented symbol
+class Symbol a => Documented a where
+	brief :: a -> String
+	detailed :: a -> String
+	detailed = unlines . defaultDetailed
+
+-- | Default detailed docs
+defaultDetailed :: Documented a => a -> [String]
+defaultDetailed s = header ++ docs ++ loc where
+	header = [brief s, ""]
+	docs = maybe [] (return . unpack) $ symbolDocs s
+	loc
+		| null mloc = []
+		| otherwise = ["Defined at " ++ mloc]
+	mloc = show (symbolLocation s)
diff --git a/src/HsDev/Symbols/Location.hs b/src/HsDev/Symbols/Location.hs
--- a/src/HsDev/Symbols/Location.hs
+++ b/src/HsDev/Symbols/Location.hs
@@ -1,277 +1,277 @@
-{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
-
-module HsDev.Symbols.Location (
-	ModulePackage(..), PackageConfig(..), ModuleLocation(..), moduleStandalone, locationId, noLocation,
-	Position(..), Region(..), region, regionAt, regionLines, regionStr,
-	Location(..),
-
-	packageName, packageVersion,
-	package, packageModules, packageExposed,
-	moduleFile, moduleProject, modulePackageDb, modulePackage, cabalModuleName, moduleSourceName,
-	positionLine, positionColumn,
-	regionFrom, regionTo,
-	locationModule, locationPosition,
-
-	sourceModuleRoot,
-	importedModulePath,
-	packageOpt,
-	RecalcTabs(..),
-
-	module HsDev.PackageDb
-	) where
-
-import Control.Applicative
-import Control.DeepSeq (NFData(..))
-import Control.Lens (makeLenses, preview, view)
-import Control.Monad (join)
-import Data.Aeson
-import Data.Char (isSpace, isDigit)
-import Data.List (intercalate, findIndex)
-import Data.Maybe
-import Data.Text (Text)
-import qualified Data.Text as T (split, unpack)
-import System.FilePath
-import Text.Read (readMaybe)
-
-import System.Directory.Paths
-import HsDev.PackageDb
-import HsDev.Project.Types
-import HsDev.Util ((.::), (.::?), (.::?!), objectUnion)
-
--- | Just package name and version without its location
-data ModulePackage = ModulePackage {
-	_packageName :: String,
-	_packageVersion :: String }
-		deriving (Eq, Ord)
-
-makeLenses ''ModulePackage
-
-instance NFData ModulePackage where
-	rnf (ModulePackage n v) = rnf n `seq` rnf v
-
-instance Show ModulePackage where
-	show (ModulePackage n "") = n
-	show (ModulePackage n v) = n ++ "-" ++ v
-
-instance Read ModulePackage where
-	readsPrec _ str = case pkg of
-		"" -> []
-		_ -> [(ModulePackage n v, str')]
-		where
-			(pkg, str') = break isSpace str
-			(rv, rn) = span versionChar $ reverse pkg
-			v = reverse rv
-			n = reverse $ dropWhile (== '-') rn
-
-			versionChar ch = isDigit ch || ch == '.'
-
-instance ToJSON ModulePackage where
-	toJSON (ModulePackage n v) = object [
-		"name" .= n,
-		"version" .= v]
-
-instance FromJSON ModulePackage where
-	parseJSON = withObject "module package" $ \v ->
-		ModulePackage <$> (v .:: "name") <*> (v .:: "version")
-
-data PackageConfig = PackageConfig {
-	_package :: ModulePackage,
-	_packageModules :: [Text],
-	_packageExposed :: Bool }
-		deriving (Eq, Ord, Read, Show)
-
-makeLenses ''PackageConfig
-
-instance NFData PackageConfig where
-	rnf (PackageConfig p ms e) = rnf p `seq` rnf ms `seq` rnf e
-
-instance ToJSON PackageConfig where
-	toJSON (PackageConfig p ms e) = toJSON p `objectUnion` object ["modules" .= ms, "exposed" .= e]
-
-instance FromJSON PackageConfig where
-	parseJSON = withObject "package-config" $ \v -> PackageConfig <$>
-		parseJSON (Object v) <*>
-		(v .::?! "modules") <*>
-		(v .:: "exposed" <|> pure False)
-
--- | Location of module
-data ModuleLocation =
-	FileModule { _moduleFile :: FilePath, _moduleProject :: Maybe Project } |
-	InstalledModule { _modulePackageDb :: PackageDb, _modulePackage :: Maybe ModulePackage, _cabalModuleName :: String } |
-	ModuleSource { _moduleSourceName :: Maybe String }
-		deriving (Eq, Ord)
-
-makeLenses ''ModuleLocation
-
-moduleStandalone :: ModuleLocation -> Bool
-moduleStandalone = (== Just Nothing) . preview moduleProject
-
-locationId :: ModuleLocation -> String
-locationId (FileModule fpath _) = fpath
-locationId (InstalledModule cabal mpack nm) = intercalate ":" [show cabal, maybe "" show mpack, nm]
-locationId (ModuleSource msrc) = fromMaybe "" msrc
-
-instance NFData ModuleLocation where
-	rnf (FileModule f p) = rnf f `seq` rnf p
-	rnf (InstalledModule d p n) = rnf d `seq` rnf p `seq` rnf n
-	rnf (ModuleSource m) = rnf m
-
-instance Show ModuleLocation where
-	show (FileModule f p) = f ++ maybe "" (" in " ++) (fmap (view projectPath) p)
-	show (InstalledModule _ p n) = n ++ maybe "" (" in package " ++) (fmap show p)
-	show (ModuleSource m) = fromMaybe "" m
-
-instance ToJSON ModuleLocation where
-	toJSON (FileModule f p) = object ["file" .= f, "project" .= fmap (view projectCabal) p]
-	toJSON (InstalledModule c p n) = object ["db" .= c, "package" .= fmap show p, "name" .= n]
-	toJSON (ModuleSource (Just s)) = object ["source" .= s]
-	toJSON (ModuleSource Nothing) = object []
-
-instance FromJSON ModuleLocation where
-	parseJSON = withObject "module location" $ \v ->
-		(FileModule <$> v .:: "file" <*> (fmap project <$> (v .:: "project"))) <|>
-		(InstalledModule <$> v .:: "db" <*> fmap (join . fmap readMaybe) (v .:: "package") <*> v .:: "name") <|>
-		(ModuleSource <$> v .::? "source")
-
-instance Paths ModuleLocation where
-	paths f (FileModule fpath p) = FileModule <$> f fpath <*> traverse (paths f) p
-	paths f (InstalledModule c p n) = InstalledModule <$> paths f c <*> pure p <*> pure n
-	paths _ (ModuleSource m) = pure $ ModuleSource m
-
-noLocation :: ModuleLocation
-noLocation = ModuleSource Nothing
-
-data Position = Position {
-	_positionLine :: Int,
-	_positionColumn :: Int }
-		deriving (Eq, Ord, Read)
-
-makeLenses ''Position
-
-instance NFData Position where
-	rnf (Position l c) = rnf l `seq` rnf c
-
-instance Show Position where
- 	show (Position l c) = show l ++ ":" ++ show c
-
-instance ToJSON Position where
-	toJSON (Position l c) = object [
-		"line" .= l,
-		"column" .= c]
-
-instance FromJSON Position where
-	parseJSON = withObject "position" $ \v -> Position <$>
-		v .:: "line" <*>
-		v .:: "column"
-
-data Region = Region {
-	_regionFrom :: Position,
-	_regionTo :: Position }
-		deriving (Eq, Ord, Read)
-
-makeLenses ''Region
-
-region :: Position -> Position -> Region
-region f t = Region (min f t) (max f t)
-
-regionAt :: Position -> Region
-regionAt f = region f f
-
-regionLines :: Region -> Int
-regionLines (Region f t) = succ (view positionLine t - view positionLine f)
-
--- | Get string at region
-regionStr :: Region -> String -> String
-regionStr r@(Region f t) s = intercalate "\n" $ drop (pred $ view positionColumn f) fline : tl where
-	s' = take (regionLines r) $ drop (pred (view positionLine f)) $ lines s
-	(fline:tl) = init s' ++ [take (pred $ view positionColumn t) (last s')]
-
-instance NFData Region where
-	rnf (Region f t) = rnf f `seq` rnf t
-
-instance Show Region where
-	show (Region f t) = show f ++ "-" ++ show t
-
-instance ToJSON Region where
-	toJSON (Region f t) = object [
-		"from" .= f,
-		"to" .= t]
-
-instance FromJSON Region where
-	parseJSON = withObject "region" $ \v -> Region <$>
-		v .:: "from" <*>
-		v .:: "to"
-
--- | Location of symbol
-data Location = Location {
-	_locationModule :: ModuleLocation,
-	_locationPosition :: Maybe Position }
-		deriving (Eq, Ord)
-
-makeLenses ''Location
-
-instance NFData Location where
-	rnf (Location m p) = rnf m `seq` rnf p
-
-instance Show Location where
-	show (Location m p) = show m ++ ":" ++ show p
-
-instance ToJSON Location where
-	toJSON (Location ml p) = object [
-		"module" .= ml,
-		"pos" .= p]
-
-instance FromJSON Location where
-	parseJSON = withObject "location" $ \v -> Location <$>
-		v .:: "module" <*>
-		v .:: "pos"
-
--- | Get source module root directory, i.e. for "...\src\Foo\Bar.hs" with module 'Foo.Bar' will return "...\src"
-sourceModuleRoot :: Text -> FilePath -> FilePath
-sourceModuleRoot mname = 
-	joinPath .
-	reverse . drop (length $ T.split (== '.') mname) . reverse .
-	splitDirectories
-
--- | Get path of imported module
--- >importedModulePath "Foo.Bar" "...\src\Foo\Bar.hs" "Quux.Blah" = "...\src\Quux\Blah.hs"
-importedModulePath :: Text -> FilePath -> Text -> FilePath
-importedModulePath mname file imp =
-	(`addExtension` "hs") . joinPath .
-	(++ ipath) . splitDirectories $
-	sourceModuleRoot mname file
-	where
-		ipath = map T.unpack $ T.split (== '.') imp
-
-packageOpt :: Maybe ModulePackage -> [String]
-packageOpt = maybeToList . fmap (("-package " ++) . view packageName)
-
--- | Recalc positions to interpret '\t' as one symbol instead of N
-class RecalcTabs a where
-	-- | Interpret '\t' as one symbol instead of N
-	recalcTabs :: String -> Int -> a -> a
-	-- | Inverse of `recalcTabs`: interpret '\t' as N symbols instead of 1
-	calcTabs :: String -> Int -> a -> a
-
-instance RecalcTabs Position where
-	recalcTabs cts n (Position l c) = Position l c' where
-		line = listToMaybe $ drop (pred l) $ lines cts
-		c' = case line of
-			Nothing -> c
-			Just line' -> let sizes = map charSize line' in
-				succ . fromMaybe (length sizes) .
-				findIndex (>= pred c) .
-				scanl (+) 0 $ sizes
-		charSize :: Char -> Int
-		charSize '\t' = n
-		charSize _ = 1
-	calcTabs cts n (Position l c) = Position l c' where
-		line = listToMaybe $ drop (pred l) $ lines cts
-		c' = maybe c (succ . sum . map charSize . take (pred c)) line
-		charSize :: Char -> Int
-		charSize '\t' = n
-		charSize _ = 1
-
-instance RecalcTabs Region where
-	recalcTabs cts n (Region f t) = Region (recalcTabs cts n f) (recalcTabs cts n t)
-	calcTabs cts n (Region f t) = Region (calcTabs cts n f) (calcTabs cts n t)
+{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
+
+module HsDev.Symbols.Location (
+	ModulePackage(..), PackageConfig(..), ModuleLocation(..), moduleStandalone, locationId, noLocation,
+	Position(..), Region(..), region, regionAt, regionLines, regionStr,
+	Location(..),
+
+	packageName, packageVersion,
+	package, packageModules, packageExposed,
+	moduleFile, moduleProject, modulePackageDb, modulePackage, cabalModuleName, moduleSourceName,
+	positionLine, positionColumn,
+	regionFrom, regionTo,
+	locationModule, locationPosition,
+
+	sourceModuleRoot,
+	importedModulePath,
+	packageOpt,
+	RecalcTabs(..),
+
+	module HsDev.PackageDb
+	) where
+
+import Control.Applicative
+import Control.DeepSeq (NFData(..))
+import Control.Lens (makeLenses, preview, view)
+import Control.Monad (join)
+import Data.Aeson
+import Data.Char (isSpace, isDigit)
+import Data.List (intercalate, findIndex)
+import Data.Maybe
+import Data.Text (Text)
+import qualified Data.Text as T (split, unpack)
+import System.FilePath
+import Text.Read (readMaybe)
+
+import System.Directory.Paths
+import HsDev.PackageDb
+import HsDev.Project.Types
+import HsDev.Util ((.::), (.::?), (.::?!), objectUnion)
+
+-- | Just package name and version without its location
+data ModulePackage = ModulePackage {
+	_packageName :: String,
+	_packageVersion :: String }
+		deriving (Eq, Ord)
+
+makeLenses ''ModulePackage
+
+instance NFData ModulePackage where
+	rnf (ModulePackage n v) = rnf n `seq` rnf v
+
+instance Show ModulePackage where
+	show (ModulePackage n "") = n
+	show (ModulePackage n v) = n ++ "-" ++ v
+
+instance Read ModulePackage where
+	readsPrec _ str = case pkg of
+		"" -> []
+		_ -> [(ModulePackage n v, str')]
+		where
+			(pkg, str') = break isSpace str
+			(rv, rn) = span versionChar $ reverse pkg
+			v = reverse rv
+			n = reverse $ dropWhile (== '-') rn
+
+			versionChar ch = isDigit ch || ch == '.'
+
+instance ToJSON ModulePackage where
+	toJSON (ModulePackage n v) = object [
+		"name" .= n,
+		"version" .= v]
+
+instance FromJSON ModulePackage where
+	parseJSON = withObject "module package" $ \v ->
+		ModulePackage <$> (v .:: "name") <*> (v .:: "version")
+
+data PackageConfig = PackageConfig {
+	_package :: ModulePackage,
+	_packageModules :: [Text],
+	_packageExposed :: Bool }
+		deriving (Eq, Ord, Read, Show)
+
+makeLenses ''PackageConfig
+
+instance NFData PackageConfig where
+	rnf (PackageConfig p ms e) = rnf p `seq` rnf ms `seq` rnf e
+
+instance ToJSON PackageConfig where
+	toJSON (PackageConfig p ms e) = toJSON p `objectUnion` object ["modules" .= ms, "exposed" .= e]
+
+instance FromJSON PackageConfig where
+	parseJSON = withObject "package-config" $ \v -> PackageConfig <$>
+		parseJSON (Object v) <*>
+		(v .::?! "modules") <*>
+		(v .:: "exposed" <|> pure False)
+
+-- | Location of module
+data ModuleLocation =
+	FileModule { _moduleFile :: FilePath, _moduleProject :: Maybe Project } |
+	InstalledModule { _modulePackageDb :: PackageDb, _modulePackage :: Maybe ModulePackage, _cabalModuleName :: String } |
+	ModuleSource { _moduleSourceName :: Maybe String }
+		deriving (Eq, Ord)
+
+makeLenses ''ModuleLocation
+
+moduleStandalone :: ModuleLocation -> Bool
+moduleStandalone = (== Just Nothing) . preview moduleProject
+
+locationId :: ModuleLocation -> String
+locationId (FileModule fpath _) = fpath
+locationId (InstalledModule cabal mpack nm) = intercalate ":" [show cabal, maybe "" show mpack, nm]
+locationId (ModuleSource msrc) = fromMaybe "" msrc
+
+instance NFData ModuleLocation where
+	rnf (FileModule f p) = rnf f `seq` rnf p
+	rnf (InstalledModule d p n) = rnf d `seq` rnf p `seq` rnf n
+	rnf (ModuleSource m) = rnf m
+
+instance Show ModuleLocation where
+	show (FileModule f p) = f ++ maybe "" (" in " ++) (fmap (view projectPath) p)
+	show (InstalledModule _ p n) = n ++ maybe "" (" in package " ++) (fmap show p)
+	show (ModuleSource m) = fromMaybe "" m
+
+instance ToJSON ModuleLocation where
+	toJSON (FileModule f p) = object ["file" .= f, "project" .= fmap (view projectCabal) p]
+	toJSON (InstalledModule c p n) = object ["db" .= c, "package" .= fmap show p, "name" .= n]
+	toJSON (ModuleSource (Just s)) = object ["source" .= s]
+	toJSON (ModuleSource Nothing) = object []
+
+instance FromJSON ModuleLocation where
+	parseJSON = withObject "module location" $ \v ->
+		(FileModule <$> v .:: "file" <*> (fmap project <$> (v .:: "project"))) <|>
+		(InstalledModule <$> v .:: "db" <*> fmap (join . fmap readMaybe) (v .:: "package") <*> v .:: "name") <|>
+		(ModuleSource <$> v .::? "source")
+
+instance Paths ModuleLocation where
+	paths f (FileModule fpath p) = FileModule <$> f fpath <*> traverse (paths f) p
+	paths f (InstalledModule c p n) = InstalledModule <$> paths f c <*> pure p <*> pure n
+	paths _ (ModuleSource m) = pure $ ModuleSource m
+
+noLocation :: ModuleLocation
+noLocation = ModuleSource Nothing
+
+data Position = Position {
+	_positionLine :: Int,
+	_positionColumn :: Int }
+		deriving (Eq, Ord, Read)
+
+makeLenses ''Position
+
+instance NFData Position where
+	rnf (Position l c) = rnf l `seq` rnf c
+
+instance Show Position where
+ 	show (Position l c) = show l ++ ":" ++ show c
+
+instance ToJSON Position where
+	toJSON (Position l c) = object [
+		"line" .= l,
+		"column" .= c]
+
+instance FromJSON Position where
+	parseJSON = withObject "position" $ \v -> Position <$>
+		v .:: "line" <*>
+		v .:: "column"
+
+data Region = Region {
+	_regionFrom :: Position,
+	_regionTo :: Position }
+		deriving (Eq, Ord, Read)
+
+makeLenses ''Region
+
+region :: Position -> Position -> Region
+region f t = Region (min f t) (max f t)
+
+regionAt :: Position -> Region
+regionAt f = region f f
+
+regionLines :: Region -> Int
+regionLines (Region f t) = succ (view positionLine t - view positionLine f)
+
+-- | Get string at region
+regionStr :: Region -> String -> String
+regionStr r@(Region f t) s = intercalate "\n" $ drop (pred $ view positionColumn f) fline : tl where
+	s' = take (regionLines r) $ drop (pred (view positionLine f)) $ lines s
+	(fline:tl) = init s' ++ [take (pred $ view positionColumn t) (last s')]
+
+instance NFData Region where
+	rnf (Region f t) = rnf f `seq` rnf t
+
+instance Show Region where
+	show (Region f t) = show f ++ "-" ++ show t
+
+instance ToJSON Region where
+	toJSON (Region f t) = object [
+		"from" .= f,
+		"to" .= t]
+
+instance FromJSON Region where
+	parseJSON = withObject "region" $ \v -> Region <$>
+		v .:: "from" <*>
+		v .:: "to"
+
+-- | Location of symbol
+data Location = Location {
+	_locationModule :: ModuleLocation,
+	_locationPosition :: Maybe Position }
+		deriving (Eq, Ord)
+
+makeLenses ''Location
+
+instance NFData Location where
+	rnf (Location m p) = rnf m `seq` rnf p
+
+instance Show Location where
+	show (Location m p) = show m ++ ":" ++ show p
+
+instance ToJSON Location where
+	toJSON (Location ml p) = object [
+		"module" .= ml,
+		"pos" .= p]
+
+instance FromJSON Location where
+	parseJSON = withObject "location" $ \v -> Location <$>
+		v .:: "module" <*>
+		v .:: "pos"
+
+-- | Get source module root directory, i.e. for "...\src\Foo\Bar.hs" with module 'Foo.Bar' will return "...\src"
+sourceModuleRoot :: Text -> FilePath -> FilePath
+sourceModuleRoot mname = 
+	joinPath .
+	reverse . drop (length $ T.split (== '.') mname) . reverse .
+	splitDirectories
+
+-- | Get path of imported module
+-- >importedModulePath "Foo.Bar" "...\src\Foo\Bar.hs" "Quux.Blah" = "...\src\Quux\Blah.hs"
+importedModulePath :: Text -> FilePath -> Text -> FilePath
+importedModulePath mname file imp =
+	(`addExtension` "hs") . joinPath .
+	(++ ipath) . splitDirectories $
+	sourceModuleRoot mname file
+	where
+		ipath = map T.unpack $ T.split (== '.') imp
+
+packageOpt :: Maybe ModulePackage -> [String]
+packageOpt = maybeToList . fmap (("-package " ++) . view packageName)
+
+-- | Recalc positions to interpret '\t' as one symbol instead of N
+class RecalcTabs a where
+	-- | Interpret '\t' as one symbol instead of N
+	recalcTabs :: String -> Int -> a -> a
+	-- | Inverse of `recalcTabs`: interpret '\t' as N symbols instead of 1
+	calcTabs :: String -> Int -> a -> a
+
+instance RecalcTabs Position where
+	recalcTabs cts n (Position l c) = Position l c' where
+		line = listToMaybe $ drop (pred l) $ lines cts
+		c' = case line of
+			Nothing -> c
+			Just line' -> let sizes = map charSize line' in
+				succ . fromMaybe (length sizes) .
+				findIndex (>= pred c) .
+				scanl (+) 0 $ sizes
+		charSize :: Char -> Int
+		charSize '\t' = n
+		charSize _ = 1
+	calcTabs cts n (Position l c) = Position l c' where
+		line = listToMaybe $ drop (pred l) $ lines cts
+		c' = maybe c (succ . sum . map charSize . take (pred c)) line
+		charSize :: Char -> Int
+		charSize '\t' = n
+		charSize _ = 1
+
+instance RecalcTabs Region where
+	recalcTabs cts n (Region f t) = Region (recalcTabs cts n f) (recalcTabs cts n t)
+	calcTabs cts n (Region f t) = Region (calcTabs cts n f) (calcTabs cts n t)
diff --git a/src/HsDev/Symbols/Resolve.hs b/src/HsDev/Symbols/Resolve.hs
--- a/src/HsDev/Symbols/Resolve.hs
+++ b/src/HsDev/Symbols/Resolve.hs
@@ -1,176 +1,176 @@
-{-# LANGUAGE GeneralizedNewtypeDeriving, TemplateHaskell #-}
-
-module HsDev.Symbols.Resolve (
-	ResolveM(..),ResolvedTree, ResolvedModule(..), resolvedModule, resolvedScope, resolvedExports,
-	scopeModule, exportsModule, resolvedTopScope,
-	resolve, resolveOne, resolveModule, resolveImports, resolveImport,
-	mergeImported
-	) where
-
-import Control.Applicative ((<|>))
-import Control.Arrow
-import Control.Lens (makeLenses, view, preview, set, _Just, over, each)
-import Control.Monad.Reader
-import Control.Monad.State
-import Data.Function (on)
-import Data.List (sortBy, groupBy, delete)
-import qualified Data.Map as M
-import Data.Maybe (fromMaybe, listToMaybe, catMaybes)
-import Data.Maybe.JustIf
-import Data.Ord (comparing)
-import qualified Data.Set as S
-import Data.String (fromString)
-import Data.Text (Text)
-
-import HsDev.Database
-import HsDev.Symbols
-import HsDev.Symbols.Util
-import HsDev.Util (uniqueBy)
-
--- | Map from name to modules
-type ModuleMap = M.Map Text [Module]
-
--- | Resolve monad uses existing @Database@ and @ResolvedTree@ as state.
-newtype ResolveM a = ResolveM { runResolveM :: ReaderT (Database, ModuleMap) (State ResolvedTree) a }
-	deriving (Functor, Applicative, Monad, MonadState ResolvedTree, MonadReader (Database, ModuleMap))
-
--- | Tree of resolved modules
-type ResolvedTree = Map ModuleId ResolvedModule
-
--- | Module with declarations bringed to scope and with exported declarations
-data ResolvedModule = ResolvedModule {
-	_resolvedModule :: Module,
-	_resolvedScope :: [Declaration],
-	_resolvedExports :: [Declaration] }
-
-makeLenses ''ResolvedModule
-
--- | Make @Module@ with scope declarations
-scopeModule :: ResolvedModule -> Module
-scopeModule r = set moduleDeclarations (view resolvedScope r) (view resolvedModule r)
-
--- | Make @Module@ with exported only declarations
-exportsModule :: ResolvedModule -> Module
-exportsModule r = set moduleDeclarations (view resolvedExports r) (view resolvedModule r)
-
--- | Get top-level scope
-resolvedTopScope :: ResolvedModule -> [Declaration]
-resolvedTopScope = filter isTop . view resolvedScope where
-	isTop :: Declaration -> Bool
-	isTop = any (not . view importIsQualified) . fromMaybe [] . view declarationImported
-
--- | Resolve modules, function is not IO, so all file names must be canonicalized
-resolve :: Traversable t => Database -> t Module -> t ResolvedModule
-resolve db = flip evalState M.empty . flip runReaderT (db, m) . runResolveM . traverse resolveModule where
-	m :: ModuleMap
-	m = M.fromList $ map ((view moduleName . head) &&& id) $
-		groupBy ((==) `on` view moduleName) $
-		sortBy (comparing (view moduleName)) $ allModules db
-
--- | Resolve one module
-resolveOne :: Database -> Module -> ResolvedModule
-resolveOne db = fromMaybe (error "Resolve: impossible happened") . resolve db . Just
-
--- | Resolve module
-resolveModule :: Module -> ResolveM ResolvedModule
-resolveModule m = gets (M.lookup $ view moduleId m) >>= maybe resolveModule' return where
-	resolveModule' = save $ case view moduleLocation m of
-		InstalledModule {} -> return ResolvedModule {
-			_resolvedModule = m,
-			_resolvedScope = map setSelfDefined $ view moduleDeclarations m,
-			_resolvedExports = map setSelfDefined $ view moduleDeclarations m }
-		_ -> do
-			scope' <-
-				liftM (thisDecls ++) .
-				resolveImports m .
-				(import_ (fromString "Prelude") :) .
-				view moduleImports $ m
-			let
-				exported' = case view moduleExports m of
-					Nothing -> thisDecls
-					Just exports' -> unique $ catMaybes $ mexported <$> scope' <*> exports'
-			return $ ResolvedModule m (sortDeclarations scope') (sortDeclarations exported')
-	thisDecls :: [Declaration]
-	thisDecls = map (selfDefined . selfImport) $ view moduleDeclarations m
-	setSelfDefined :: Declaration -> Declaration
-	setSelfDefined =
-		over (declaration . localDeclarations . each . declarationDefined) (<|> Just (view moduleId m)) .
-		over declarationDefined (<|> Just (view moduleId m))
-	selfDefined :: Declaration -> Declaration
-	selfDefined = set declarationDefined (Just $ view moduleId m)
-	selfImport :: Declaration -> Declaration
-	selfImport = set declarationImported (Just [import_ $ view moduleName m])
-	save :: ResolveM ResolvedModule -> ResolveM ResolvedModule
-	save act = do
-		rm <- act
-		modify $ M.insert (view (resolvedModule . moduleId) rm) rm
-		return rm
-	unique :: [Declaration] -> [Declaration]
-	unique = uniqueBy declId
-	declId :: Declaration -> (Text, Maybe ModuleId)
-	declId = view declarationName &&& view declarationDefined
-	mexported decl' e' = decl' `justIf` exported decl' e'
-
--- | Bring declarations into scope by imports
-resolveImports :: Module -> [Import] -> ResolveM [Declaration]
-resolveImports m is = do
-	db <- asks fst
-	let
-		deps = maybe S.empty S.fromList $ do
-			f <- preview (moduleLocation . moduleFile) m
-			p <- preview (moduleLocation . moduleProject . _Just) m
-			p' <- refineProject db p
-			return $ delete (view projectName p') $ concatMap (view infoDepends) $ fileTargets p' f
-	liftM (mergeImported . concat) $ mapM (resolveImport deps m) is
-
--- | Bring declarations into scope, first parameter is set of visible packages
-resolveImport :: S.Set String -> Module -> Import -> ResolveM [Declaration]
-resolveImport deps m i = liftM (map $ setImport i) resolveImport' where
-	resolveImport' :: ResolveM [Declaration]
-	resolveImport' = do
-		ms <- case view moduleLocation m of
-			FileModule file proj -> do
-				db <- asks fst
-				let
-					proj' = proj >>= refineProject db
-				case proj' of
-					Nothing -> selectImport i [
-						inFile $ importedModulePath (view moduleName m) file (view importModuleName i),
-						installed]
-					Just p -> selectImport i [
-						inProject p,
-						inDeps]
-			InstalledModule pdb _ _ -> selectImport i [inPackageDb pdb]
-			ModuleSource _ -> selectImport i [installed]
-		fromMaybe [] <$> traverse (liftM (filterImportList . view resolvedExports) . resolveModule) ms
-	setImport :: Import -> Declaration -> Declaration
-	setImport i' = set declarationImported (Just [i'])
-	selectImport :: Import -> [ModuleId -> Bool] -> ResolveM (Maybe Module)
-	selectImport i' fs = do
-		modsMap <- asks snd
-		let
-			mods = fromMaybe [] $ M.lookup (view importModuleName i') modsMap
-		return $
-			listToMaybe $
-			newestPackage $
-			fromMaybe [] $
-			listToMaybe $ dropWhile null
-				[filter (f . view moduleId) mods | f <- fs]
-	filterImportList :: [Declaration] -> [Declaration]
-	filterImportList = case view importList i of
-		Nothing -> id
-		Just il -> filter (`imported` il)
-	inDeps = maybe False (`S.member` deps) . preview (moduleIdLocation . modulePackage . _Just . packageName)
-
--- | Merge imported declarations
-mergeImported :: [Declaration] -> [Declaration]
-mergeImported =
-	map merge' .
-	groupBy ((==) `on` declId) .
-	sortBy (comparing declId)
-	where
-		declId :: Declaration -> (Text, Maybe ModuleId)
-		declId = view declarationName &&& view declarationDefined
-		merge' :: [Declaration] -> Declaration
-		merge' [] = error "mergeImported: impossible"
-		merge' ds@(d:_) = set declarationImported (mconcat $ map (view declarationImported) ds) d
+{-# LANGUAGE GeneralizedNewtypeDeriving, TemplateHaskell #-}
+
+module HsDev.Symbols.Resolve (
+	ResolveM(..),ResolvedTree, ResolvedModule(..), resolvedModule, resolvedScope, resolvedExports,
+	scopeModule, exportsModule, resolvedTopScope,
+	resolve, resolveOne, resolveModule, resolveImports, resolveImport,
+	mergeImported
+	) where
+
+import Control.Applicative ((<|>))
+import Control.Arrow
+import Control.Lens (makeLenses, view, preview, set, _Just, over, each)
+import Control.Monad.Reader
+import Control.Monad.State
+import Data.Function (on)
+import Data.List (sortBy, groupBy, delete)
+import qualified Data.Map as M
+import Data.Maybe (fromMaybe, listToMaybe, catMaybes)
+import Data.Maybe.JustIf
+import Data.Ord (comparing)
+import qualified Data.Set as S
+import Data.String (fromString)
+import Data.Text (Text)
+
+import HsDev.Database
+import HsDev.Symbols
+import HsDev.Symbols.Util
+import HsDev.Util (uniqueBy)
+
+-- | Map from name to modules
+type ModuleMap = M.Map Text [Module]
+
+-- | Resolve monad uses existing @Database@ and @ResolvedTree@ as state.
+newtype ResolveM a = ResolveM { runResolveM :: ReaderT (Database, ModuleMap) (State ResolvedTree) a }
+	deriving (Functor, Applicative, Monad, MonadState ResolvedTree, MonadReader (Database, ModuleMap))
+
+-- | Tree of resolved modules
+type ResolvedTree = Map ModuleId ResolvedModule
+
+-- | Module with declarations bringed to scope and with exported declarations
+data ResolvedModule = ResolvedModule {
+	_resolvedModule :: Module,
+	_resolvedScope :: [Declaration],
+	_resolvedExports :: [Declaration] }
+
+makeLenses ''ResolvedModule
+
+-- | Make @Module@ with scope declarations
+scopeModule :: ResolvedModule -> Module
+scopeModule r = set moduleDeclarations (view resolvedScope r) (view resolvedModule r)
+
+-- | Make @Module@ with exported only declarations
+exportsModule :: ResolvedModule -> Module
+exportsModule r = set moduleDeclarations (view resolvedExports r) (view resolvedModule r)
+
+-- | Get top-level scope
+resolvedTopScope :: ResolvedModule -> [Declaration]
+resolvedTopScope = filter isTop . view resolvedScope where
+	isTop :: Declaration -> Bool
+	isTop = any (not . view importIsQualified) . fromMaybe [] . view declarationImported
+
+-- | Resolve modules, function is not IO, so all file names must be canonicalized
+resolve :: Traversable t => Database -> t Module -> t ResolvedModule
+resolve db = flip evalState M.empty . flip runReaderT (db, m) . runResolveM . traverse resolveModule where
+	m :: ModuleMap
+	m = M.fromList $ map ((view moduleName . head) &&& id) $
+		groupBy ((==) `on` view moduleName) $
+		sortBy (comparing (view moduleName)) $ allModules db
+
+-- | Resolve one module
+resolveOne :: Database -> Module -> ResolvedModule
+resolveOne db = fromMaybe (error "Resolve: impossible happened") . resolve db . Just
+
+-- | Resolve module
+resolveModule :: Module -> ResolveM ResolvedModule
+resolveModule m = gets (M.lookup $ view moduleId m) >>= maybe resolveModule' return where
+	resolveModule' = save $ case view moduleLocation m of
+		InstalledModule {} -> return ResolvedModule {
+			_resolvedModule = m,
+			_resolvedScope = map setSelfDefined $ view moduleDeclarations m,
+			_resolvedExports = map setSelfDefined $ view moduleDeclarations m }
+		_ -> do
+			scope' <-
+				liftM (thisDecls ++) .
+				resolveImports m .
+				(import_ (fromString "Prelude") :) .
+				view moduleImports $ m
+			let
+				exported' = case view moduleExports m of
+					Nothing -> thisDecls
+					Just exports' -> unique $ catMaybes $ mexported <$> scope' <*> exports'
+			return $ ResolvedModule m (sortDeclarations scope') (sortDeclarations exported')
+	thisDecls :: [Declaration]
+	thisDecls = map (selfDefined . selfImport) $ view moduleDeclarations m
+	setSelfDefined :: Declaration -> Declaration
+	setSelfDefined =
+		over (declaration . localDeclarations . each . declarationDefined) (<|> Just (view moduleId m)) .
+		over declarationDefined (<|> Just (view moduleId m))
+	selfDefined :: Declaration -> Declaration
+	selfDefined = set declarationDefined (Just $ view moduleId m)
+	selfImport :: Declaration -> Declaration
+	selfImport = set declarationImported (Just [import_ $ view moduleName m])
+	save :: ResolveM ResolvedModule -> ResolveM ResolvedModule
+	save act = do
+		rm <- act
+		modify $ M.insert (view (resolvedModule . moduleId) rm) rm
+		return rm
+	unique :: [Declaration] -> [Declaration]
+	unique = uniqueBy declId
+	declId :: Declaration -> (Text, Maybe ModuleId)
+	declId = view declarationName &&& view declarationDefined
+	mexported decl' e' = decl' `justIf` exported decl' e'
+
+-- | Bring declarations into scope by imports
+resolveImports :: Module -> [Import] -> ResolveM [Declaration]
+resolveImports m is = do
+	db <- asks fst
+	let
+		deps = maybe S.empty S.fromList $ do
+			f <- preview (moduleLocation . moduleFile) m
+			p <- preview (moduleLocation . moduleProject . _Just) m
+			p' <- refineProject db p
+			return $ delete (view projectName p') $ concatMap (view infoDepends) $ fileTargets p' f
+	liftM (mergeImported . concat) $ mapM (resolveImport deps m) is
+
+-- | Bring declarations into scope, first parameter is set of visible packages
+resolveImport :: S.Set String -> Module -> Import -> ResolveM [Declaration]
+resolveImport deps m i = liftM (map $ setImport i) resolveImport' where
+	resolveImport' :: ResolveM [Declaration]
+	resolveImport' = do
+		ms <- case view moduleLocation m of
+			FileModule file proj -> do
+				db <- asks fst
+				let
+					proj' = proj >>= refineProject db
+				case proj' of
+					Nothing -> selectImport i [
+						inFile $ importedModulePath (view moduleName m) file (view importModuleName i),
+						installed]
+					Just p -> selectImport i [
+						inProject p,
+						inDeps]
+			InstalledModule pdb _ _ -> selectImport i [inPackageDb pdb]
+			ModuleSource _ -> selectImport i [installed]
+		fromMaybe [] <$> traverse (liftM (filterImportList . view resolvedExports) . resolveModule) ms
+	setImport :: Import -> Declaration -> Declaration
+	setImport i' = set declarationImported (Just [i'])
+	selectImport :: Import -> [ModuleId -> Bool] -> ResolveM (Maybe Module)
+	selectImport i' fs = do
+		modsMap <- asks snd
+		let
+			mods = fromMaybe [] $ M.lookup (view importModuleName i') modsMap
+		return $
+			listToMaybe $
+			newestPackage $
+			fromMaybe [] $
+			listToMaybe $ dropWhile null
+				[filter (f . view moduleId) mods | f <- fs]
+	filterImportList :: [Declaration] -> [Declaration]
+	filterImportList = case view importList i of
+		Nothing -> id
+		Just il -> filter (`imported` il)
+	inDeps = maybe False (`S.member` deps) . preview (moduleIdLocation . modulePackage . _Just . packageName)
+
+-- | Merge imported declarations
+mergeImported :: [Declaration] -> [Declaration]
+mergeImported =
+	map merge' .
+	groupBy ((==) `on` declId) .
+	sortBy (comparing declId)
+	where
+		declId :: Declaration -> (Text, Maybe ModuleId)
+		declId = view declarationName &&& view declarationDefined
+		merge' :: [Declaration] -> Declaration
+		merge' [] = error "mergeImported: impossible"
+		merge' ds@(d:_) = set declarationImported (mconcat $ map (view declarationImported) ds) d
diff --git a/src/HsDev/Symbols/Types.hs b/src/HsDev/Symbols/Types.hs
--- a/src/HsDev/Symbols/Types.hs
+++ b/src/HsDev/Symbols/Types.hs
@@ -1,620 +1,620 @@
-{-# LANGUAGE TemplateHaskell, TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Symbols.Types (
-	ThingPart(..),
-	Export(..), exportQualified, exportName, exportPart, exportModule,
-	ImportSpec(..), importSpecName, importSpecPart,
-	ImportList(..), hidingList, importSpecs,
-	Import(..), importModuleName, importIsQualified, importAs, importList, importPosition,
-	ModuleId(..), moduleIdName, moduleIdLocation,
-	Module(..), moduleName, moduleDocs, moduleLocation, moduleExports, moduleImports, moduleDeclarations, moduleContents, moduleId,
-	Declaration(..), declarationName, declarationDefined, declarationImported, declarationDocs, declarationPosition, declaration, minimalDecl,
-	TypeInfo(..), typeInfoContext, typeInfoArgs, typeInfoDefinition, typeInfoFunctions, showTypeInfo,
-	DeclarationInfo(..), functionType, localDeclarations, related, typeInfo, declarationInfo, declarationTypeCtor, declarationTypeName,
-	ModuleDeclaration(..), declarationModuleId, moduleDeclaration,
-	ExportedDeclaration(..), exportedBy, exportedDeclaration,
-	Inspection(..), inspectionAt, inspectionOpts,
-	Inspected(..), inspection, inspectedId, inspectionTags, inspectionResult, noTags,
-	ModuleTag(..),
-	InspectedModule, notInspected,
-
-	module HsDev.PackageDb,
-	module HsDev.Project,
-	module HsDev.Symbols.Class,
-	module HsDev.Symbols.Documented
-	) where
-
-import Control.Applicative
-import Control.Arrow
-import Control.Lens (makeLenses, view, set, Simple, Lens, Lens', lens)
-import Control.Monad
-import Control.DeepSeq (NFData(..))
-import Data.Aeson
-import Data.List (intercalate)
-import Data.Maybe (fromMaybe)
-import Data.Function
-import Data.Ord
-import Data.Text (Text, unpack)
-import qualified Data.Text as T
-import Data.Set (Set)
-import qualified Data.Set as S
-import Data.Time.Clock.POSIX (POSIXTime)
-
-import HsDev.PackageDb
-import HsDev.Project
-import HsDev.Symbols.Class
-import HsDev.Symbols.Documented
-import HsDev.Types
-import HsDev.Util (tab, tabs, (.::), (.::?), (.::?!), noNulls)
-
--- | What to export/import for data/class etc
-data ThingPart = ThingNothing | ThingAll | ThingWith [Text] deriving (Eq, Ord)
-
-instance NFData ThingPart where
-	rnf ThingNothing = ()
-	rnf ThingAll = ()
-	rnf (ThingWith ns) = rnf ns
-
-instance Show ThingPart where
-	show ThingNothing = ""
-	show ThingAll = "(..)"
-	show (ThingWith ns) = "(" ++ intercalate ", " (map unpack ns) ++ ")"
-
-instance ToJSON ThingPart where
-	toJSON ThingNothing = toJSON ("nothing" :: String)
-	toJSON ThingAll = toJSON ("all" :: String)
-	toJSON (ThingWith ns) = object [
-		"with" .= ns]
-
-instance FromJSON ThingPart where
-	parseJSON v = parse' <|> parseWith v where
-		parse' = do
-			s <- parseJSON v
-			mplus
-				(guard (s == ("nothing" :: String)) >> return ThingNothing)
-				(guard (s == ("all" :: String)) >> return ThingAll)
-		parseWith = withObject "export part" $ \v' -> ThingWith <$> v' .:: "with"
-
--- | Module export
-data Export =
-	ExportName {
-		_exportQualified :: Maybe Text,
-		_exportName :: Text,
-		_exportPart :: ThingPart } |
-	ExportModule { _exportModule :: Text }
-		deriving (Eq, Ord)
-
-instance NFData Export where
-	rnf (ExportName q n w) = rnf q `seq` rnf n `seq` rnf w
-	rnf (ExportModule m) = rnf m
-
-instance Show Export where
-	show (ExportName Nothing n w) = unpack n ++ show w
-	show (ExportName (Just q) n w) = unpack q ++ "." ++ unpack n ++ show w
-	show (ExportModule m) = "module " ++ unpack m
-
-instance ToJSON Export where
-	toJSON (ExportName q n w) = object ["module" .= q, "name" .= n, "part" .= w]
-	toJSON (ExportModule m) = object ["module" .= m]
-
-instance FromJSON Export where
-	parseJSON = withObject "export" $ \v ->
-		(ExportName <$> (v .:: "module") <*> (v .:: "name") <*> (v .:: "part")) <|>
-		(ExportModule <$> (v .:: "module"))
-
--- | Import spec
-data ImportSpec = ImportSpec {
-	_importSpecName :: Text,
-	_importSpecPart :: ThingPart }
-		deriving (Eq, Ord)
-
-instance NFData ImportSpec where
-	rnf (ImportSpec n p) = rnf n `seq` rnf p
-
-instance Show ImportSpec where
-	show (ImportSpec n p) = unpack n ++ show p
-
-instance ToJSON ImportSpec where
-	toJSON (ImportSpec n p) = object ["name" .= n, "part" .= p]
-
-instance FromJSON ImportSpec where
-	parseJSON = withObject "import-spec" $ \v -> ImportSpec <$> (v .:: "name") <*> (v .:: "part")
-
--- | Import list
-data ImportList = ImportList {
-	_hidingList :: Bool,
-	_importSpecs :: [ImportSpec] }
-		deriving (Eq, Ord)
-
-instance NFData ImportList where
-	rnf (ImportList h ls) = rnf h `seq` rnf ls
-
-instance Show ImportList where
-	show (ImportList h ls) = (if h then ("hiding " ++) else id) $ "(" ++ intercalate ", " (map show ls) ++ ")"
-
-instance ToJSON ImportList where
-	toJSON (ImportList h ls) = object [
-		"hiding" .= h,
-		"specs" .= ls]
-
-instance FromJSON ImportList where
-	parseJSON = withObject "import-list" $ \v -> ImportList <$>
-		v .:: "hiding" <*>
-		v .:: "specs"
-
--- | Module import
-data Import = Import {
-	_importModuleName :: Text,
-	_importIsQualified :: Bool,
-	_importAs :: Maybe Text,
-	_importList :: Maybe ImportList,
-	_importPosition :: Maybe Position }
-		deriving (Eq, Ord)
-
-instance NFData Import where
-	rnf (Import m q a il l) = rnf m `seq` rnf q `seq` rnf a `seq` rnf il `seq` rnf l
-
-instance Show Import where
-	show i = concat [
-		"import ",
-		if _importIsQualified i then "qualified " else "",
-		unpack $ _importModuleName i,
-		maybe "" ((" as " ++) . unpack) (_importAs i),
-		maybe "" ((" " ++) . show) (_importList i)]
-
-instance ToJSON Import where
-	toJSON i = object $ noNulls [
-		"name" .= _importModuleName i,
-		"qualified" .= _importIsQualified i,
-		"as" .= _importAs i,
-		"import-list" .= _importList i,
-		"pos" .= _importPosition i]
-
-instance FromJSON Import where
-	parseJSON = withObject "import" $ \v -> Import <$>
-		v .:: "name" <*>
-		v .:: "qualified" <*>
-		v .::? "as" <*>
-		v .::? "import-list" <*>
-		v .::? "pos"
-
--- | Module id
-data ModuleId = ModuleId {
-	_moduleIdName :: Text,
-	_moduleIdLocation :: ModuleLocation }
-		deriving (Eq, Ord)
-
-instance NFData ModuleId where
-	rnf (ModuleId n l) = rnf n `seq` rnf l
-
-instance Show ModuleId where
-	show (ModuleId n l) = "module " ++ unpack n ++ " from " ++ show l
-
-instance ToJSON ModuleId where
-	toJSON m = object [
-		"name" .= _moduleIdName m,
-		"location" .= _moduleIdLocation m]
-
-instance FromJSON ModuleId where
-	parseJSON = withObject "module id" $ \v -> ModuleId <$>
-		v .:: "name" <*>
-		v .:: "location"
-
--- | Module
-data Module = Module {
-	_moduleName :: Text,
-	_moduleDocs :: Maybe Text,
-	_moduleLocation :: ModuleLocation,
-	_moduleExports :: Maybe [Export],
-	_moduleImports :: [Import],
-	_moduleDeclarations :: [Declaration] }
-		deriving (Ord)
-
-instance ToJSON Module where
-	toJSON m = object $ noNulls [
-		"name" .= _moduleName m,
-		"docs" .= _moduleDocs m,
-		"location" .= _moduleLocation m,
-		"exports" .= _moduleExports m,
-		"imports" .= _moduleImports m,
-		"declarations" .= _moduleDeclarations m]
-
-instance FromJSON Module where
-	parseJSON = withObject "module" $ \v -> Module <$>
-		v .:: "name" <*>
-		v .::? "docs" <*>
-		v .:: "location" <*>
-		v .::? "exports" <*>
-		v .::?! "imports" <*>
-		v .::?! "declarations"
-
-instance NFData Module where
-	rnf (Module n d s e i ds) = rnf n `seq` rnf d `seq` rnf s `seq` rnf e `seq` rnf i `seq` rnf ds
-
-instance Eq Module where
-	l == r = _moduleName l == _moduleName r && _moduleLocation l == _moduleLocation r
-
-instance Show Module where
-	show m = unlines $ filter (not . null) [
-		"module " ++ unpack (_moduleName m),
-		"\tlocation: " ++ show (_moduleLocation m),
-		"\texports: " ++ maybe "*" (intercalate ", " . map show) (_moduleExports m),
-		"\timports:",
-		unlines $ map (tab 2 . show) $ _moduleImports m,
-		"\tdeclarations:",
-		unlines $ map (tabs 2 . show) $ _moduleDeclarations m,
-		maybe "" (("\tdocs: " ++) . unpack) (_moduleDocs m)]
-
-moduleId :: Simple Lens Module ModuleId
-moduleId = lens
-	(uncurry ModuleId . (_moduleName &&& _moduleLocation))
-	(\m mi -> m { _moduleName = _moduleIdName mi, _moduleLocation = _moduleIdLocation mi })
-
--- | Module contents
-moduleContents :: Module -> [String]
-moduleContents = map showDecl . _moduleDeclarations where
-	showDecl d = brief d ++ maybe "" ((" -- " ++) . unpack) (_declarationDocs d)
-
--- | Declaration
-data Declaration = Declaration {
-	_declarationName :: Text,
-	_declarationDefined :: Maybe ModuleId, -- ^ Where declaration defined, @Nothing@ if here
-	_declarationImported :: Maybe [Import], -- ^ Declaration imported with. @Nothing@ if unknown (cabal modules) or here (source file)
-	_declarationDocs :: Maybe Text,
-	_declarationPosition :: Maybe Position,
-	_declaration :: DeclarationInfo }
-		deriving (Eq, Ord)
-
-instance NFData Declaration where
-	rnf (Declaration n def is d l x) = rnf n `seq` rnf def `seq` rnf is `seq` rnf d `seq` rnf l `seq` rnf x
-
-instance Show Declaration where
-	show d = unlines $ filter (not . null) [
-		brief d,
-		maybe "" (("\tdocs: " ++) . unpack) $ _declarationDocs d,
-		maybe "" (("\tdefined in: " ++) . show) $ _declarationDefined d,
-		maybe "" (("\tlocation: " ++ ) . show) $ _declarationPosition d]
-
-instance ToJSON Declaration where
-	toJSON d = object $ noNulls [
-		"name" .= _declarationName d,
-		"defined" .= _declarationDefined d,
-		"imported" .= _declarationImported d,
-		"docs" .= _declarationDocs d,
-		"pos" .= _declarationPosition d,
-		"decl" .= _declaration d]
-
-instance FromJSON Declaration where
-	parseJSON = withObject "declaration" $ \v -> Declaration <$>
-		v .:: "name" <*>
-		v .::? "defined" <*>
-		v .::? "imported" <*>
-		v .::? "docs" <*>
-		v .::? "pos" <*>
-		v .:: "decl"
-
--- | Minimal declaration info without defined, docs and position
-minimalDecl :: Lens' Declaration Declaration
-minimalDecl = lens to' from' where
-	to' :: Declaration -> Declaration
-	to' decl' = decl' { _declarationDefined = Nothing, _declarationDocs = Nothing, _declarationPosition = Nothing }
-	from' :: Declaration -> Declaration -> Declaration
-	from' decl' mdecl = decl' { _declarationName = _declarationName mdecl, _declarationImported = _declarationImported mdecl, _declaration = _declaration mdecl }
-
--- | Common info for type, newtype, data and class
-data TypeInfo = TypeInfo {
-	_typeInfoContext :: Maybe Text, -- FIXME: Why not list of contexts?
-	_typeInfoArgs :: [Text],
-	_typeInfoDefinition :: Maybe Text,
-	_typeInfoFunctions :: [Text] }
-		deriving (Eq, Ord, Read, Show)
-
-instance NFData TypeInfo where
-	rnf (TypeInfo c a d f) = rnf c `seq` rnf a `seq` rnf d `seq` rnf f
-
-instance ToJSON TypeInfo where
-	toJSON t = object $ noNulls [
-		"ctx" .= _typeInfoContext t,
-		"args" .= _typeInfoArgs t,
-		"def" .= _typeInfoDefinition t,
-		"funs" .= _typeInfoFunctions t]
-
-instance FromJSON TypeInfo where
-	parseJSON = withObject "type info" $ \v -> TypeInfo <$>
-		v .::? "ctx" <*>
-		v .::?! "args" <*>
-		v .::? "def" <*>
-		v .::?! "funs"
-
-showTypeInfo :: TypeInfo -> String -> String -> String
-showTypeInfo ti pre name = concat [
-	pre,
-	maybe "" ((++ " =>") . unpack) (_typeInfoContext ti), " ",
-	name, " ",
-	unwords (map unpack $ _typeInfoArgs ti),
-	maybe "" ((" = " ++) . unpack) (_typeInfoDefinition ti)]
-
--- | Declaration info
-data DeclarationInfo =
-	Function { _functionType :: Maybe Text, _localDeclarations :: [Declaration], _related :: Maybe Text } |
-	Type { _typeInfo :: TypeInfo } |
-	NewType { _typeInfo :: TypeInfo } |
-	Data { _typeInfo :: TypeInfo } |
-	Class { _typeInfo :: TypeInfo }
-		deriving (Ord)
-
--- | Get function type of type info
-declarationInfo :: DeclarationInfo -> Either (Maybe Text, [Declaration], Maybe Text) TypeInfo
-declarationInfo (Function t ds r) = Left (t, ds, r)
-declarationInfo (Type ti) = Right ti
-declarationInfo (NewType ti) = Right ti
-declarationInfo (Data ti) = Right ti
-declarationInfo (Class ti) = Right ti
-
-declarationTypeCtor :: String -> TypeInfo -> DeclarationInfo
-declarationTypeCtor "type" = Type
-declarationTypeCtor "newtype" = NewType
-declarationTypeCtor "data" = Data
-declarationTypeCtor "class" = Class
-declarationTypeCtor _ = error "Invalid type constructor name"
-
-declarationTypeName :: DeclarationInfo -> Maybe String
-declarationTypeName (Type _) = Just "type"
-declarationTypeName (NewType _) = Just "newtype"
-declarationTypeName (Data _) = Just "data"
-declarationTypeName (Class _) = Just "class"
-declarationTypeName _ = Nothing
-
-instance NFData DeclarationInfo where
-	rnf (Function f ds r) = rnf f `seq` rnf ds `seq` rnf r
-	rnf (Type i) = rnf i
-	rnf (NewType i) = rnf i
-	rnf (Data i) = rnf i
-	rnf (Class i) = rnf i
-
-instance Eq DeclarationInfo where
-	(Function l lds lr) == (Function r rds rr) = l == r && lds == rds && lr == rr
-	(Type _) == (Type _) = True
-	(NewType _) == (NewType _) = True
-	(Data _) == (Data _) = True
-	(Class _) == (Class _) = True
-	_ == _ = False
-
-instance ToJSON DeclarationInfo where
-	toJSON i = case declarationInfo i of
-		Left (t, ds, r) -> object $ noNulls ["what" .= ("function" :: String), "type" .= t, "locals" .= ds, "related" .= r]
-		Right ti -> object ["what" .= declarationTypeName i, "info" .= ti]
-
-instance FromJSON DeclarationInfo where
-	parseJSON = withObject "declaration info" $ \v -> do
-		w <- fmap (id :: String -> String) $ v .:: "what"
-		if w == "function"
-			then Function <$> v .::? "type" <*> v .::?! "locals" <*> v .::? "related"
-			else declarationTypeCtor w <$> v .:: "info"
-
--- | Symbol in context of some module
-data ModuleDeclaration = ModuleDeclaration {
-	_declarationModuleId :: ModuleId,
-	_moduleDeclaration :: Declaration }
-		deriving (Eq, Ord)
-
-instance NFData ModuleDeclaration where
-	rnf (ModuleDeclaration m s) = rnf m `seq` rnf s
-
-instance Show ModuleDeclaration where
-	show (ModuleDeclaration m s) = unlines $ filter (not . null) [
-		show s,
-		"\tmodule: " ++ show (_moduleIdLocation m)]
-
-instance ToJSON ModuleDeclaration where
-	toJSON d = object [
-		"module-id" .= _declarationModuleId d,
-		"declaration" .= _moduleDeclaration d]
-
-instance FromJSON ModuleDeclaration where
-	parseJSON = withObject "module declaration" $ \v -> ModuleDeclaration <$>
-		v .:: "module-id" <*>
-		v .:: "declaration"
-
--- | Symbol exported with
-data ExportedDeclaration = ExportedDeclaration {
-	_exportedBy :: [ModuleId],
-	_exportedDeclaration :: Declaration }
-		deriving (Eq, Ord)
-
-instance NFData ExportedDeclaration where
-	rnf (ExportedDeclaration m s) = rnf m `seq` rnf s
-
-instance Show ExportedDeclaration where
-	show (ExportedDeclaration m s) = unlines $ filter (not . null) [
-		show s,
-		"\tmodules: " ++ intercalate ", " (map (show . _moduleIdLocation) m)]
-
-instance ToJSON ExportedDeclaration where
-	toJSON d = object [
-		"exported-by" .= _exportedBy d,
-		"declaration" .= _exportedDeclaration d]
-
-instance FromJSON ExportedDeclaration where
-	parseJSON = withObject "exported declaration" $ \v -> ExportedDeclaration <$>
-		v .:: "exported-by" <*>
-		v .:: "declaration"
-
--- | Inspection data
-data Inspection =
-	-- | No inspection
-	InspectionNone |
-	-- | Time and flags of inspection
-	InspectionAt {
-		_inspectionAt :: POSIXTime,
-		_inspectionOpts :: [String] }
-			deriving (Eq, Ord)
-
-instance NFData Inspection where
-	rnf InspectionNone = ()
-	rnf (InspectionAt t fs) = rnf t `seq` rnf fs
-
-instance Show Inspection where
-	show InspectionNone = "none"
-	show (InspectionAt tm fs) = "mtime " ++ show tm ++ ", flags [" ++ intercalate ", " fs ++ "]"
-
-instance Read POSIXTime where
-	readsPrec i = map (first (fromIntegral :: Integer -> POSIXTime)) . readsPrec i
-
-instance Monoid Inspection where
-	mempty = InspectionNone
-	mappend InspectionNone r = r
-	mappend l InspectionNone = l
-	mappend (InspectionAt ltm lopts) (InspectionAt rtm ropts)
-		| ltm >= rtm = InspectionAt ltm lopts
-		| otherwise = InspectionAt rtm ropts
-
-instance ToJSON Inspection where
-	toJSON InspectionNone = object ["inspected" .= False]
-	toJSON (InspectionAt tm fs) = object [
-		"mtime" .= (floor tm :: Integer),
-		"flags" .= fs]
-
-instance FromJSON Inspection where
-	parseJSON = withObject "inspection" $ \v ->
-		((const InspectionNone :: Bool -> Inspection) <$> v .:: "inspected") <|>
-		(InspectionAt <$> (fromInteger <$> v .:: "mtime") <*> (v .:: "flags"))
-
--- | Inspected entity
-data Inspected i t a = Inspected {
-	_inspection :: Inspection,
-	_inspectedId :: i,
-	_inspectionTags :: Set t,
-	_inspectionResult :: Either HsDevError a }
-
-inspectedTup :: Inspected i t a -> (Inspection, i, Set t, Maybe a)
-inspectedTup (Inspected insp i tags res) = (insp, i, tags, either (const Nothing) Just res)
-
-instance (Eq i, Eq t, Eq a) => Eq (Inspected i t a) where
-	(==) = (==) `on` inspectedTup
-
-instance (Ord i, Ord t, Ord a) => Ord (Inspected i t a) where
-	compare = comparing inspectedTup
-
-instance Functor (Inspected i t) where
-	fmap f insp = insp {
-		_inspectionResult = fmap f (_inspectionResult insp) }
-
-instance Foldable (Inspected i t) where
-	foldMap f = either mempty f . _inspectionResult
-
-instance Traversable (Inspected i t) where
-	traverse f (Inspected insp i ts r) = Inspected insp i ts <$> either (pure . Left) (liftA Right . f) r
-
-instance (NFData i, NFData t, NFData a) => NFData (Inspected i t a) where
-	rnf (Inspected t i ts r) = rnf t `seq` rnf i `seq` rnf ts `seq` rnf r
-
--- | Empty tags
-noTags :: Set t
-noTags = S.empty
-
-data ModuleTag = InferredTypesTag | RefinedDocsTag deriving (Eq, Ord, Read, Show, Enum, Bounded)
-
-instance NFData ModuleTag where
-	rnf InferredTypesTag = ()
-	rnf RefinedDocsTag = ()
-
-instance ToJSON ModuleTag where
-	toJSON InferredTypesTag = toJSON ("types" :: String)
-	toJSON RefinedDocsTag = toJSON ("docs" :: String)
-
-instance FromJSON ModuleTag where
-	parseJSON = withText "module-tag" $ \txt -> msum [
-		guard (txt == "types") >> return InferredTypesTag,
-		guard (txt == "docs") >> return RefinedDocsTag]
-
--- | Inspected module
-type InspectedModule = Inspected ModuleLocation ModuleTag Module
-
-instance Show InspectedModule where
-	show (Inspected i mi ts m) = unlines [either showError show m, "\tinspected: " ++ show i, "\ttags: " ++ intercalate ", " (map show $ S.toList ts)] where
-		showError :: HsDevError -> String
-		showError e = unlines $ ("\terror: " ++ show e) : case mi of
-			FileModule f p -> ["file: " ++ f, "project: " ++ maybe "" (view projectPath) p]
-			InstalledModule c p n -> ["cabal: " ++ show c, "package: " ++ maybe "" show p, "name: " ++ n]
-			ModuleSource src -> ["source: " ++ fromMaybe "" src]
-
-instance ToJSON InspectedModule where
-	toJSON im = object [
-		"inspection" .= _inspection im,
-		"location" .= _inspectedId im,
-		"tags" .= S.toList (_inspectionTags im),
-		either ("error" .=) ("module" .=) (_inspectionResult im)]
-
-instance FromJSON InspectedModule where
-	parseJSON = withObject "inspected module" $ \v -> Inspected <$>
-		v .:: "inspection" <*>
-		v .:: "location" <*>
-		(S.fromList <$> (v .::?! "tags")) <*>
-		((Left <$> v .:: "error") <|> (Right <$> v .:: "module"))
-
-notInspected :: ModuleLocation -> InspectedModule
-notInspected mloc = Inspected mempty mloc noTags (Left $ NotInspected mloc)
-
-instance Symbol Module where
-	symbolName = _moduleName
-	symbolQualifiedName = _moduleName
-	symbolDocs = _moduleDocs
-	symbolLocation m = Location (_moduleLocation m) Nothing
-
-instance Symbol ModuleId where
-	symbolName = _moduleIdName
-	symbolQualifiedName = _moduleIdName
-	symbolDocs = const Nothing
-	symbolLocation m = Location (_moduleIdLocation m) Nothing
-
-instance Symbol Declaration where
-	symbolName = _declarationName
-	symbolQualifiedName = _declarationName
-	symbolDocs = _declarationDocs
-	symbolLocation d = Location (ModuleSource Nothing) (_declarationPosition d)
-
-instance Symbol ModuleDeclaration where
-	symbolName = _declarationName . _moduleDeclaration
-	symbolQualifiedName d = qualifiedName (_declarationModuleId d) (_moduleDeclaration d) where
-		qualifiedName :: ModuleId -> Declaration -> Text
-		qualifiedName m' d' = T.concat [_moduleIdName m', ".", _declarationName d']
-	symbolDocs = _declarationDocs . _moduleDeclaration
-	symbolLocation d = set locationPosition (_declarationPosition $ _moduleDeclaration d)
-		(symbolLocation . _declarationModuleId $ d)
-
-instance Documented ModuleId where
-	brief m = unpack (_moduleIdName m) ++ " in " ++ show (_moduleIdLocation m)
-
-instance Documented Module where
-	brief m = unpack (_moduleName m) ++ " in " ++ show (_moduleLocation m)
-	detailed m = unlines $ header ++ docs ++ cts where
-		header = [brief m, ""]
-		docs = maybe [] (return . unpack) $ _moduleDocs m
-		cts = moduleContents m
-
-instance Documented Declaration where
-	brief d = case declarationInfo $ _declaration d of
-		Left (f, _, _) -> name ++ maybe "" ((" :: " ++) . unpack) f
-		Right ti -> showTypeInfo ti (fromMaybe err $ declarationTypeName $ _declaration d) name
-		where
-			name = unpack $ _declarationName d
-			err = error "Impossible happened: declarationTypeName"
-
-instance Documented ModuleDeclaration where
-	brief = brief . _moduleDeclaration
-
-makeLenses ''Export
-makeLenses ''ImportSpec
-makeLenses ''ImportList
-makeLenses ''Import
-makeLenses ''ModuleId
-makeLenses ''DeclarationInfo
-makeLenses ''TypeInfo
-makeLenses ''Declaration
-makeLenses ''Module
-makeLenses ''ModuleDeclaration
-makeLenses ''ExportedDeclaration
-makeLenses ''Inspection
-makeLenses ''Inspected
+{-# LANGUAGE TemplateHaskell, TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Symbols.Types (
+	ThingPart(..),
+	Export(..), exportQualified, exportName, exportPart, exportModule,
+	ImportSpec(..), importSpecName, importSpecPart,
+	ImportList(..), hidingList, importSpecs,
+	Import(..), importModuleName, importIsQualified, importAs, importList, importPosition,
+	ModuleId(..), moduleIdName, moduleIdLocation,
+	Module(..), moduleName, moduleDocs, moduleLocation, moduleExports, moduleImports, moduleDeclarations, moduleContents, moduleId,
+	Declaration(..), declarationName, declarationDefined, declarationImported, declarationDocs, declarationPosition, declaration, minimalDecl,
+	TypeInfo(..), typeInfoContext, typeInfoArgs, typeInfoDefinition, typeInfoFunctions, showTypeInfo,
+	DeclarationInfo(..), functionType, localDeclarations, related, typeInfo, declarationInfo, declarationTypeCtor, declarationTypeName,
+	ModuleDeclaration(..), declarationModuleId, moduleDeclaration,
+	ExportedDeclaration(..), exportedBy, exportedDeclaration,
+	Inspection(..), inspectionAt, inspectionOpts,
+	Inspected(..), inspection, inspectedId, inspectionTags, inspectionResult, noTags,
+	ModuleTag(..),
+	InspectedModule, notInspected,
+
+	module HsDev.PackageDb,
+	module HsDev.Project,
+	module HsDev.Symbols.Class,
+	module HsDev.Symbols.Documented
+	) where
+
+import Control.Applicative
+import Control.Arrow
+import Control.Lens (makeLenses, view, set, Simple, Lens, Lens', lens)
+import Control.Monad
+import Control.DeepSeq (NFData(..))
+import Data.Aeson
+import Data.List (intercalate)
+import Data.Maybe (fromMaybe)
+import Data.Function
+import Data.Ord
+import Data.Text (Text, unpack)
+import qualified Data.Text as T
+import Data.Set (Set)
+import qualified Data.Set as S
+import Data.Time.Clock.POSIX (POSIXTime)
+
+import HsDev.PackageDb
+import HsDev.Project
+import HsDev.Symbols.Class
+import HsDev.Symbols.Documented
+import HsDev.Types
+import HsDev.Util (tab, tabs, (.::), (.::?), (.::?!), noNulls)
+
+-- | What to export/import for data/class etc
+data ThingPart = ThingNothing | ThingAll | ThingWith [Text] deriving (Eq, Ord)
+
+instance NFData ThingPart where
+	rnf ThingNothing = ()
+	rnf ThingAll = ()
+	rnf (ThingWith ns) = rnf ns
+
+instance Show ThingPart where
+	show ThingNothing = ""
+	show ThingAll = "(..)"
+	show (ThingWith ns) = "(" ++ intercalate ", " (map unpack ns) ++ ")"
+
+instance ToJSON ThingPart where
+	toJSON ThingNothing = toJSON ("nothing" :: String)
+	toJSON ThingAll = toJSON ("all" :: String)
+	toJSON (ThingWith ns) = object [
+		"with" .= ns]
+
+instance FromJSON ThingPart where
+	parseJSON v = parse' <|> parseWith v where
+		parse' = do
+			s <- parseJSON v
+			mplus
+				(guard (s == ("nothing" :: String)) >> return ThingNothing)
+				(guard (s == ("all" :: String)) >> return ThingAll)
+		parseWith = withObject "export part" $ \v' -> ThingWith <$> v' .:: "with"
+
+-- | Module export
+data Export =
+	ExportName {
+		_exportQualified :: Maybe Text,
+		_exportName :: Text,
+		_exportPart :: ThingPart } |
+	ExportModule { _exportModule :: Text }
+		deriving (Eq, Ord)
+
+instance NFData Export where
+	rnf (ExportName q n w) = rnf q `seq` rnf n `seq` rnf w
+	rnf (ExportModule m) = rnf m
+
+instance Show Export where
+	show (ExportName Nothing n w) = unpack n ++ show w
+	show (ExportName (Just q) n w) = unpack q ++ "." ++ unpack n ++ show w
+	show (ExportModule m) = "module " ++ unpack m
+
+instance ToJSON Export where
+	toJSON (ExportName q n w) = object ["module" .= q, "name" .= n, "part" .= w]
+	toJSON (ExportModule m) = object ["module" .= m]
+
+instance FromJSON Export where
+	parseJSON = withObject "export" $ \v ->
+		(ExportName <$> (v .:: "module") <*> (v .:: "name") <*> (v .:: "part")) <|>
+		(ExportModule <$> (v .:: "module"))
+
+-- | Import spec
+data ImportSpec = ImportSpec {
+	_importSpecName :: Text,
+	_importSpecPart :: ThingPart }
+		deriving (Eq, Ord)
+
+instance NFData ImportSpec where
+	rnf (ImportSpec n p) = rnf n `seq` rnf p
+
+instance Show ImportSpec where
+	show (ImportSpec n p) = unpack n ++ show p
+
+instance ToJSON ImportSpec where
+	toJSON (ImportSpec n p) = object ["name" .= n, "part" .= p]
+
+instance FromJSON ImportSpec where
+	parseJSON = withObject "import-spec" $ \v -> ImportSpec <$> (v .:: "name") <*> (v .:: "part")
+
+-- | Import list
+data ImportList = ImportList {
+	_hidingList :: Bool,
+	_importSpecs :: [ImportSpec] }
+		deriving (Eq, Ord)
+
+instance NFData ImportList where
+	rnf (ImportList h ls) = rnf h `seq` rnf ls
+
+instance Show ImportList where
+	show (ImportList h ls) = (if h then ("hiding " ++) else id) $ "(" ++ intercalate ", " (map show ls) ++ ")"
+
+instance ToJSON ImportList where
+	toJSON (ImportList h ls) = object [
+		"hiding" .= h,
+		"specs" .= ls]
+
+instance FromJSON ImportList where
+	parseJSON = withObject "import-list" $ \v -> ImportList <$>
+		v .:: "hiding" <*>
+		v .:: "specs"
+
+-- | Module import
+data Import = Import {
+	_importModuleName :: Text,
+	_importIsQualified :: Bool,
+	_importAs :: Maybe Text,
+	_importList :: Maybe ImportList,
+	_importPosition :: Maybe Position }
+		deriving (Eq, Ord)
+
+instance NFData Import where
+	rnf (Import m q a il l) = rnf m `seq` rnf q `seq` rnf a `seq` rnf il `seq` rnf l
+
+instance Show Import where
+	show i = concat [
+		"import ",
+		if _importIsQualified i then "qualified " else "",
+		unpack $ _importModuleName i,
+		maybe "" ((" as " ++) . unpack) (_importAs i),
+		maybe "" ((" " ++) . show) (_importList i)]
+
+instance ToJSON Import where
+	toJSON i = object $ noNulls [
+		"name" .= _importModuleName i,
+		"qualified" .= _importIsQualified i,
+		"as" .= _importAs i,
+		"import-list" .= _importList i,
+		"pos" .= _importPosition i]
+
+instance FromJSON Import where
+	parseJSON = withObject "import" $ \v -> Import <$>
+		v .:: "name" <*>
+		v .:: "qualified" <*>
+		v .::? "as" <*>
+		v .::? "import-list" <*>
+		v .::? "pos"
+
+-- | Module id
+data ModuleId = ModuleId {
+	_moduleIdName :: Text,
+	_moduleIdLocation :: ModuleLocation }
+		deriving (Eq, Ord)
+
+instance NFData ModuleId where
+	rnf (ModuleId n l) = rnf n `seq` rnf l
+
+instance Show ModuleId where
+	show (ModuleId n l) = "module " ++ unpack n ++ " from " ++ show l
+
+instance ToJSON ModuleId where
+	toJSON m = object [
+		"name" .= _moduleIdName m,
+		"location" .= _moduleIdLocation m]
+
+instance FromJSON ModuleId where
+	parseJSON = withObject "module id" $ \v -> ModuleId <$>
+		v .:: "name" <*>
+		v .:: "location"
+
+-- | Module
+data Module = Module {
+	_moduleName :: Text,
+	_moduleDocs :: Maybe Text,
+	_moduleLocation :: ModuleLocation,
+	_moduleExports :: Maybe [Export],
+	_moduleImports :: [Import],
+	_moduleDeclarations :: [Declaration] }
+		deriving (Ord)
+
+instance ToJSON Module where
+	toJSON m = object $ noNulls [
+		"name" .= _moduleName m,
+		"docs" .= _moduleDocs m,
+		"location" .= _moduleLocation m,
+		"exports" .= _moduleExports m,
+		"imports" .= _moduleImports m,
+		"declarations" .= _moduleDeclarations m]
+
+instance FromJSON Module where
+	parseJSON = withObject "module" $ \v -> Module <$>
+		v .:: "name" <*>
+		v .::? "docs" <*>
+		v .:: "location" <*>
+		v .::? "exports" <*>
+		v .::?! "imports" <*>
+		v .::?! "declarations"
+
+instance NFData Module where
+	rnf (Module n d s e i ds) = rnf n `seq` rnf d `seq` rnf s `seq` rnf e `seq` rnf i `seq` rnf ds
+
+instance Eq Module where
+	l == r = _moduleName l == _moduleName r && _moduleLocation l == _moduleLocation r
+
+instance Show Module where
+	show m = unlines $ filter (not . null) [
+		"module " ++ unpack (_moduleName m),
+		"\tlocation: " ++ show (_moduleLocation m),
+		"\texports: " ++ maybe "*" (intercalate ", " . map show) (_moduleExports m),
+		"\timports:",
+		unlines $ map (tab 2 . show) $ _moduleImports m,
+		"\tdeclarations:",
+		unlines $ map (tabs 2 . show) $ _moduleDeclarations m,
+		maybe "" (("\tdocs: " ++) . unpack) (_moduleDocs m)]
+
+moduleId :: Simple Lens Module ModuleId
+moduleId = lens
+	(uncurry ModuleId . (_moduleName &&& _moduleLocation))
+	(\m mi -> m { _moduleName = _moduleIdName mi, _moduleLocation = _moduleIdLocation mi })
+
+-- | Module contents
+moduleContents :: Module -> [String]
+moduleContents = map showDecl . _moduleDeclarations where
+	showDecl d = brief d ++ maybe "" ((" -- " ++) . unpack) (_declarationDocs d)
+
+-- | Declaration
+data Declaration = Declaration {
+	_declarationName :: Text,
+	_declarationDefined :: Maybe ModuleId, -- ^ Where declaration defined, @Nothing@ if here
+	_declarationImported :: Maybe [Import], -- ^ Declaration imported with. @Nothing@ if unknown (cabal modules) or here (source file)
+	_declarationDocs :: Maybe Text,
+	_declarationPosition :: Maybe Position,
+	_declaration :: DeclarationInfo }
+		deriving (Eq, Ord)
+
+instance NFData Declaration where
+	rnf (Declaration n def is d l x) = rnf n `seq` rnf def `seq` rnf is `seq` rnf d `seq` rnf l `seq` rnf x
+
+instance Show Declaration where
+	show d = unlines $ filter (not . null) [
+		brief d,
+		maybe "" (("\tdocs: " ++) . unpack) $ _declarationDocs d,
+		maybe "" (("\tdefined in: " ++) . show) $ _declarationDefined d,
+		maybe "" (("\tlocation: " ++ ) . show) $ _declarationPosition d]
+
+instance ToJSON Declaration where
+	toJSON d = object $ noNulls [
+		"name" .= _declarationName d,
+		"defined" .= _declarationDefined d,
+		"imported" .= _declarationImported d,
+		"docs" .= _declarationDocs d,
+		"pos" .= _declarationPosition d,
+		"decl" .= _declaration d]
+
+instance FromJSON Declaration where
+	parseJSON = withObject "declaration" $ \v -> Declaration <$>
+		v .:: "name" <*>
+		v .::? "defined" <*>
+		v .::? "imported" <*>
+		v .::? "docs" <*>
+		v .::? "pos" <*>
+		v .:: "decl"
+
+-- | Minimal declaration info without defined, docs and position
+minimalDecl :: Lens' Declaration Declaration
+minimalDecl = lens to' from' where
+	to' :: Declaration -> Declaration
+	to' decl' = decl' { _declarationDefined = Nothing, _declarationDocs = Nothing, _declarationPosition = Nothing }
+	from' :: Declaration -> Declaration -> Declaration
+	from' decl' mdecl = decl' { _declarationName = _declarationName mdecl, _declarationImported = _declarationImported mdecl, _declaration = _declaration mdecl }
+
+-- | Common info for type, newtype, data and class
+data TypeInfo = TypeInfo {
+	_typeInfoContext :: Maybe Text, -- FIXME: Why not list of contexts?
+	_typeInfoArgs :: [Text],
+	_typeInfoDefinition :: Maybe Text,
+	_typeInfoFunctions :: [Text] }
+		deriving (Eq, Ord, Read, Show)
+
+instance NFData TypeInfo where
+	rnf (TypeInfo c a d f) = rnf c `seq` rnf a `seq` rnf d `seq` rnf f
+
+instance ToJSON TypeInfo where
+	toJSON t = object $ noNulls [
+		"ctx" .= _typeInfoContext t,
+		"args" .= _typeInfoArgs t,
+		"def" .= _typeInfoDefinition t,
+		"funs" .= _typeInfoFunctions t]
+
+instance FromJSON TypeInfo where
+	parseJSON = withObject "type info" $ \v -> TypeInfo <$>
+		v .::? "ctx" <*>
+		v .::?! "args" <*>
+		v .::? "def" <*>
+		v .::?! "funs"
+
+showTypeInfo :: TypeInfo -> String -> String -> String
+showTypeInfo ti pre name = concat [
+	pre,
+	maybe "" ((++ " =>") . unpack) (_typeInfoContext ti), " ",
+	name, " ",
+	unwords (map unpack $ _typeInfoArgs ti),
+	maybe "" ((" = " ++) . unpack) (_typeInfoDefinition ti)]
+
+-- | Declaration info
+data DeclarationInfo =
+	Function { _functionType :: Maybe Text, _localDeclarations :: [Declaration], _related :: Maybe Text } |
+	Type { _typeInfo :: TypeInfo } |
+	NewType { _typeInfo :: TypeInfo } |
+	Data { _typeInfo :: TypeInfo } |
+	Class { _typeInfo :: TypeInfo }
+		deriving (Ord)
+
+-- | Get function type of type info
+declarationInfo :: DeclarationInfo -> Either (Maybe Text, [Declaration], Maybe Text) TypeInfo
+declarationInfo (Function t ds r) = Left (t, ds, r)
+declarationInfo (Type ti) = Right ti
+declarationInfo (NewType ti) = Right ti
+declarationInfo (Data ti) = Right ti
+declarationInfo (Class ti) = Right ti
+
+declarationTypeCtor :: String -> TypeInfo -> DeclarationInfo
+declarationTypeCtor "type" = Type
+declarationTypeCtor "newtype" = NewType
+declarationTypeCtor "data" = Data
+declarationTypeCtor "class" = Class
+declarationTypeCtor _ = error "Invalid type constructor name"
+
+declarationTypeName :: DeclarationInfo -> Maybe String
+declarationTypeName (Type _) = Just "type"
+declarationTypeName (NewType _) = Just "newtype"
+declarationTypeName (Data _) = Just "data"
+declarationTypeName (Class _) = Just "class"
+declarationTypeName _ = Nothing
+
+instance NFData DeclarationInfo where
+	rnf (Function f ds r) = rnf f `seq` rnf ds `seq` rnf r
+	rnf (Type i) = rnf i
+	rnf (NewType i) = rnf i
+	rnf (Data i) = rnf i
+	rnf (Class i) = rnf i
+
+instance Eq DeclarationInfo where
+	(Function l lds lr) == (Function r rds rr) = l == r && lds == rds && lr == rr
+	(Type _) == (Type _) = True
+	(NewType _) == (NewType _) = True
+	(Data _) == (Data _) = True
+	(Class _) == (Class _) = True
+	_ == _ = False
+
+instance ToJSON DeclarationInfo where
+	toJSON i = case declarationInfo i of
+		Left (t, ds, r) -> object $ noNulls ["what" .= ("function" :: String), "type" .= t, "locals" .= ds, "related" .= r]
+		Right ti -> object ["what" .= declarationTypeName i, "info" .= ti]
+
+instance FromJSON DeclarationInfo where
+	parseJSON = withObject "declaration info" $ \v -> do
+		w <- fmap (id :: String -> String) $ v .:: "what"
+		if w == "function"
+			then Function <$> v .::? "type" <*> v .::?! "locals" <*> v .::? "related"
+			else declarationTypeCtor w <$> v .:: "info"
+
+-- | Symbol in context of some module
+data ModuleDeclaration = ModuleDeclaration {
+	_declarationModuleId :: ModuleId,
+	_moduleDeclaration :: Declaration }
+		deriving (Eq, Ord)
+
+instance NFData ModuleDeclaration where
+	rnf (ModuleDeclaration m s) = rnf m `seq` rnf s
+
+instance Show ModuleDeclaration where
+	show (ModuleDeclaration m s) = unlines $ filter (not . null) [
+		show s,
+		"\tmodule: " ++ show (_moduleIdLocation m)]
+
+instance ToJSON ModuleDeclaration where
+	toJSON d = object [
+		"module-id" .= _declarationModuleId d,
+		"declaration" .= _moduleDeclaration d]
+
+instance FromJSON ModuleDeclaration where
+	parseJSON = withObject "module declaration" $ \v -> ModuleDeclaration <$>
+		v .:: "module-id" <*>
+		v .:: "declaration"
+
+-- | Symbol exported with
+data ExportedDeclaration = ExportedDeclaration {
+	_exportedBy :: [ModuleId],
+	_exportedDeclaration :: Declaration }
+		deriving (Eq, Ord)
+
+instance NFData ExportedDeclaration where
+	rnf (ExportedDeclaration m s) = rnf m `seq` rnf s
+
+instance Show ExportedDeclaration where
+	show (ExportedDeclaration m s) = unlines $ filter (not . null) [
+		show s,
+		"\tmodules: " ++ intercalate ", " (map (show . _moduleIdLocation) m)]
+
+instance ToJSON ExportedDeclaration where
+	toJSON d = object [
+		"exported-by" .= _exportedBy d,
+		"declaration" .= _exportedDeclaration d]
+
+instance FromJSON ExportedDeclaration where
+	parseJSON = withObject "exported declaration" $ \v -> ExportedDeclaration <$>
+		v .:: "exported-by" <*>
+		v .:: "declaration"
+
+-- | Inspection data
+data Inspection =
+	-- | No inspection
+	InspectionNone |
+	-- | Time and flags of inspection
+	InspectionAt {
+		_inspectionAt :: POSIXTime,
+		_inspectionOpts :: [String] }
+			deriving (Eq, Ord)
+
+instance NFData Inspection where
+	rnf InspectionNone = ()
+	rnf (InspectionAt t fs) = rnf t `seq` rnf fs
+
+instance Show Inspection where
+	show InspectionNone = "none"
+	show (InspectionAt tm fs) = "mtime " ++ show tm ++ ", flags [" ++ intercalate ", " fs ++ "]"
+
+instance Read POSIXTime where
+	readsPrec i = map (first (fromIntegral :: Integer -> POSIXTime)) . readsPrec i
+
+instance Monoid Inspection where
+	mempty = InspectionNone
+	mappend InspectionNone r = r
+	mappend l InspectionNone = l
+	mappend (InspectionAt ltm lopts) (InspectionAt rtm ropts)
+		| ltm >= rtm = InspectionAt ltm lopts
+		| otherwise = InspectionAt rtm ropts
+
+instance ToJSON Inspection where
+	toJSON InspectionNone = object ["inspected" .= False]
+	toJSON (InspectionAt tm fs) = object [
+		"mtime" .= (floor tm :: Integer),
+		"flags" .= fs]
+
+instance FromJSON Inspection where
+	parseJSON = withObject "inspection" $ \v ->
+		((const InspectionNone :: Bool -> Inspection) <$> v .:: "inspected") <|>
+		(InspectionAt <$> (fromInteger <$> v .:: "mtime") <*> (v .:: "flags"))
+
+-- | Inspected entity
+data Inspected i t a = Inspected {
+	_inspection :: Inspection,
+	_inspectedId :: i,
+	_inspectionTags :: Set t,
+	_inspectionResult :: Either HsDevError a }
+
+inspectedTup :: Inspected i t a -> (Inspection, i, Set t, Maybe a)
+inspectedTup (Inspected insp i tags res) = (insp, i, tags, either (const Nothing) Just res)
+
+instance (Eq i, Eq t, Eq a) => Eq (Inspected i t a) where
+	(==) = (==) `on` inspectedTup
+
+instance (Ord i, Ord t, Ord a) => Ord (Inspected i t a) where
+	compare = comparing inspectedTup
+
+instance Functor (Inspected i t) where
+	fmap f insp = insp {
+		_inspectionResult = fmap f (_inspectionResult insp) }
+
+instance Foldable (Inspected i t) where
+	foldMap f = either mempty f . _inspectionResult
+
+instance Traversable (Inspected i t) where
+	traverse f (Inspected insp i ts r) = Inspected insp i ts <$> either (pure . Left) (liftA Right . f) r
+
+instance (NFData i, NFData t, NFData a) => NFData (Inspected i t a) where
+	rnf (Inspected t i ts r) = rnf t `seq` rnf i `seq` rnf ts `seq` rnf r
+
+-- | Empty tags
+noTags :: Set t
+noTags = S.empty
+
+data ModuleTag = InferredTypesTag | RefinedDocsTag deriving (Eq, Ord, Read, Show, Enum, Bounded)
+
+instance NFData ModuleTag where
+	rnf InferredTypesTag = ()
+	rnf RefinedDocsTag = ()
+
+instance ToJSON ModuleTag where
+	toJSON InferredTypesTag = toJSON ("types" :: String)
+	toJSON RefinedDocsTag = toJSON ("docs" :: String)
+
+instance FromJSON ModuleTag where
+	parseJSON = withText "module-tag" $ \txt -> msum [
+		guard (txt == "types") >> return InferredTypesTag,
+		guard (txt == "docs") >> return RefinedDocsTag]
+
+-- | Inspected module
+type InspectedModule = Inspected ModuleLocation ModuleTag Module
+
+instance Show InspectedModule where
+	show (Inspected i mi ts m) = unlines [either showError show m, "\tinspected: " ++ show i, "\ttags: " ++ intercalate ", " (map show $ S.toList ts)] where
+		showError :: HsDevError -> String
+		showError e = unlines $ ("\terror: " ++ show e) : case mi of
+			FileModule f p -> ["file: " ++ f, "project: " ++ maybe "" (view projectPath) p]
+			InstalledModule c p n -> ["cabal: " ++ show c, "package: " ++ maybe "" show p, "name: " ++ n]
+			ModuleSource src -> ["source: " ++ fromMaybe "" src]
+
+instance ToJSON InspectedModule where
+	toJSON im = object [
+		"inspection" .= _inspection im,
+		"location" .= _inspectedId im,
+		"tags" .= S.toList (_inspectionTags im),
+		either ("error" .=) ("module" .=) (_inspectionResult im)]
+
+instance FromJSON InspectedModule where
+	parseJSON = withObject "inspected module" $ \v -> Inspected <$>
+		v .:: "inspection" <*>
+		v .:: "location" <*>
+		(S.fromList <$> (v .::?! "tags")) <*>
+		((Left <$> v .:: "error") <|> (Right <$> v .:: "module"))
+
+notInspected :: ModuleLocation -> InspectedModule
+notInspected mloc = Inspected mempty mloc noTags (Left $ NotInspected mloc)
+
+instance Symbol Module where
+	symbolName = _moduleName
+	symbolQualifiedName = _moduleName
+	symbolDocs = _moduleDocs
+	symbolLocation m = Location (_moduleLocation m) Nothing
+
+instance Symbol ModuleId where
+	symbolName = _moduleIdName
+	symbolQualifiedName = _moduleIdName
+	symbolDocs = const Nothing
+	symbolLocation m = Location (_moduleIdLocation m) Nothing
+
+instance Symbol Declaration where
+	symbolName = _declarationName
+	symbolQualifiedName = _declarationName
+	symbolDocs = _declarationDocs
+	symbolLocation d = Location (ModuleSource Nothing) (_declarationPosition d)
+
+instance Symbol ModuleDeclaration where
+	symbolName = _declarationName . _moduleDeclaration
+	symbolQualifiedName d = qualifiedName (_declarationModuleId d) (_moduleDeclaration d) where
+		qualifiedName :: ModuleId -> Declaration -> Text
+		qualifiedName m' d' = T.concat [_moduleIdName m', ".", _declarationName d']
+	symbolDocs = _declarationDocs . _moduleDeclaration
+	symbolLocation d = set locationPosition (_declarationPosition $ _moduleDeclaration d)
+		(symbolLocation . _declarationModuleId $ d)
+
+instance Documented ModuleId where
+	brief m = unpack (_moduleIdName m) ++ " in " ++ show (_moduleIdLocation m)
+
+instance Documented Module where
+	brief m = unpack (_moduleName m) ++ " in " ++ show (_moduleLocation m)
+	detailed m = unlines $ header ++ docs ++ cts where
+		header = [brief m, ""]
+		docs = maybe [] (return . unpack) $ _moduleDocs m
+		cts = moduleContents m
+
+instance Documented Declaration where
+	brief d = case declarationInfo $ _declaration d of
+		Left (f, _, _) -> name ++ maybe "" ((" :: " ++) . unpack) f
+		Right ti -> showTypeInfo ti (fromMaybe err $ declarationTypeName $ _declaration d) name
+		where
+			name = unpack $ _declarationName d
+			err = error "Impossible happened: declarationTypeName"
+
+instance Documented ModuleDeclaration where
+	brief = brief . _moduleDeclaration
+
+makeLenses ''Export
+makeLenses ''ImportSpec
+makeLenses ''ImportList
+makeLenses ''Import
+makeLenses ''ModuleId
+makeLenses ''DeclarationInfo
+makeLenses ''TypeInfo
+makeLenses ''Declaration
+makeLenses ''Module
+makeLenses ''ModuleDeclaration
+makeLenses ''ExportedDeclaration
+makeLenses ''Inspection
+makeLenses ''Inspected
diff --git a/src/HsDev/Symbols/Util.hs b/src/HsDev/Symbols/Util.hs
--- a/src/HsDev/Symbols/Util.hs
+++ b/src/HsDev/Symbols/Util.hs
@@ -1,203 +1,203 @@
-module HsDev.Symbols.Util (
-	projectOf, packageDbOf, packageOf,
-	inProject, inDepsOfTarget, inDepsOfFile, inDepsOfProject, inPackageDb, inPackageDbStack, inPackage, inVersion, inFile, inModuleSource, inModule, byFile, installed, standalone,
-	imports, qualifier, moduleImported, visible, inScope,
-	newestPackage,
-	sourceModule, visibleModule, preferredModule, uniqueModules,
-	allOf, anyOf
-	) where
-
-import Control.Arrow ((***), (&&&), first)
-import Control.Lens (view)
-import Control.Monad (liftM)
-import Data.Function (on)
-import Data.Maybe
-import Data.List (maximumBy, groupBy, sortBy, partition)
-import Data.Ord (comparing)
-import Data.String (fromString)
-import System.FilePath (normalise)
-
-import HsDev.Symbols
-import HsDev.Util (ordNub)
-
--- | Get module project
-projectOf :: ModuleId -> Maybe Project
-projectOf m = case view moduleIdLocation m of
-	FileModule _ proj -> proj
-	_ -> Nothing
-
--- | Get module cabal
-packageDbOf :: ModuleId -> Maybe PackageDb
-packageDbOf m = case view moduleIdLocation m of
-	InstalledModule c _ _ -> Just c
-	_ -> Nothing
-
--- | Get module package
-packageOf :: ModuleId -> Maybe ModulePackage
-packageOf m = case view moduleIdLocation m of
-	InstalledModule _ package' _ -> package'
-	_ -> Nothing
-
--- | Check if module in project
-inProject :: Project -> ModuleId -> Bool
-inProject p m = projectOf m == Just p
-
--- | Check if module in deps of project target
-inDepsOfTarget :: Info -> ModuleId -> Bool
-inDepsOfTarget i m = any (`inPackage` m) $ view infoDepends i
-
--- | Check if module in deps of source
-inDepsOfFile :: Project -> FilePath -> ModuleId -> Bool
-inDepsOfFile p f m = any (`inDepsOfTarget` m) (fileTargets p f)
-
--- | Check if module in deps of project
-inDepsOfProject :: Project -> ModuleId -> Bool
-inDepsOfProject = maybe (const False) (anyPackage . ordNub . concatMap (view infoDepends) . infos) . view projectDescription where
-	anyPackage :: [String] -> ModuleId -> Bool
-	anyPackage = liftM or . mapM inPackage
-
--- | Check if module in package-db
-inPackageDb :: PackageDb -> ModuleId -> Bool
-inPackageDb c m = case view moduleIdLocation m of
-	InstalledModule d _ _ -> d == c
-	_ -> False
-
--- | Check if module in one of sandboxes
-inPackageDbStack :: PackageDbStack -> ModuleId -> Bool
-inPackageDbStack dbs m = case view moduleIdLocation m of
-	InstalledModule d _ _ -> d `elem` packageDbs dbs
-	_ -> False
-
--- | Check if module in package
-inPackage :: String -> ModuleId -> Bool
-inPackage p m = case view moduleIdLocation m of
-	InstalledModule _ package' _ -> Just p == fmap (view packageName) package'
-	_ -> False
-
-inVersion :: String -> ModuleId -> Bool
-inVersion v m = case view moduleIdLocation m of
-	InstalledModule _ package' _ -> Just v == fmap (view packageVersion) package'
-	_ -> False
-
--- | Check if module in file
-inFile :: FilePath -> ModuleId -> Bool
-inFile fpath m = case view moduleIdLocation m of
-	FileModule f _ -> f == normalise fpath
-	_ -> False
-
--- | Check if module in source
-inModuleSource :: Maybe String -> ModuleId -> Bool
-inModuleSource src m = case view moduleIdLocation m of
-	ModuleSource src' -> src' == src
-	_ -> False
-
--- | Check if declaration is in module
-inModule :: String -> ModuleId -> Bool
-inModule mname m = fromString mname == view moduleIdName m
-
--- | Check if module defined in file
-byFile :: ModuleId -> Bool
-byFile m = case view moduleIdLocation m of
-	FileModule _ _ -> True
-	_ -> False
-
--- | Check if module got from cabal database
-installed :: ModuleId -> Bool
-installed m = case view moduleIdLocation m of
-	InstalledModule _ _ _ -> True
-	_ -> False
-
--- | Check if module is standalone
-standalone :: ModuleId -> Bool
-standalone m = case view moduleIdLocation m of
-	FileModule _ Nothing -> True
-	_ -> False
-
--- | Get list of imports
-imports :: Module -> [Import]
-imports = view moduleImports
-
--- | Get list of imports, which can be accessed with specified qualifier or unqualified
-qualifier :: Module -> Maybe String -> [Import]
-qualifier m q = filter (importQualifier (fmap fromString q)) $
-	import_ (fromString "Prelude") :
-	import_ (view moduleName m) :
-	imports m
-
--- | Check if module imported via imports specified
-moduleImported :: ModuleId -> [Import] -> Bool
-moduleImported m = any (\i -> view moduleIdName m == view importModuleName i)
-
--- | Check if module visible from this module within this project
-visible :: Project -> ModuleId -> ModuleId -> Bool
-visible p (ModuleId _ (FileModule src _)) m =
-	inProject p m || any (`inPackage` m) deps || maybe False ((`elem` deps) . view projectName) (projectOf m)
-	where
-		deps = concatMap (view infoDepends) $ fileTargets p src
-visible _ _ _ = False
-
--- | Check if module is in scope with qualifier
-inScope :: Module -> Maybe String -> ModuleId -> Bool
-inScope this q m = m `moduleImported` qualifier this q
-
--- | Select symbols with last package version
-newestPackage :: Symbol a => [a] -> [a]
-newestPackage =
-	uncurry (++) .
-	((selectNewest . groupPackages) *** map snd) .
-	partition (isJust . fst) .
-	map ((mpackage . symbolModuleLocation) &&& id)
-	where
-		mpackage (InstalledModule _ (Just p) _) = Just p
-		mpackage _ = Nothing
-		pname = fmap (view packageName) . fst
-		pver = fmap (view packageVersion) . fst
-		groupPackages :: [(Maybe ModulePackage, a)] -> [(Maybe ModulePackage, [a])]
-		groupPackages = map (first head . unzip) . groupBy ((==) `on` fst) . sortBy (comparing fst)
-		selectNewest :: [(Maybe ModulePackage, [a])] -> [a]
-		selectNewest =
-			concatMap (snd . maximumBy (comparing pver)) .
-			groupBy ((==) `on` pname) .
-			sortBy (comparing pname)
-
--- | Select module, defined by sources
-sourceModule :: Maybe Project -> [Module] -> Maybe Module
-sourceModule proj ms = listToMaybe $ maybe (const []) (filter . (. view moduleId) . inProject) proj ms ++ filter (byFile . view moduleId) ms
-
--- | Select module, visible in project or cabal
--- TODO: PackageDbStack?
-visibleModule :: PackageDb -> Maybe Project -> [Module] -> Maybe Module
-visibleModule d proj ms = listToMaybe $ maybe (const []) (filter . (. view moduleId) . inProject) proj ms ++ filter (inPackageDb d . view moduleId) ms
-
--- | Select preferred visible module
--- TODO: PackageDbStack?
-preferredModule :: PackageDb -> Maybe Project -> [ModuleId] -> Maybe ModuleId
-preferredModule d proj ms = listToMaybe $ concatMap (`filter` ms) order where
-	order = [
-		maybe (const False) inProject proj,
-		byFile,
-		inPackageDb d,
-		const True]
-
--- | Remove duplicate modules, leave only `preferredModule`
--- TODO: PackageDbStack?
-uniqueModules :: PackageDb -> Maybe Project -> [ModuleId] -> [ModuleId]
-uniqueModules d proj =
-	mapMaybe (preferredModule d proj) .
-	groupBy ((==) `on` view moduleIdName) .
-	sortBy (comparing (view moduleIdName))
-
--- | Select value, satisfying to all predicates
-allOf :: [a -> Bool] -> a -> Bool
-allOf ps x = all ($ x) ps
-
--- | Select value, satisfying one of predicates
-anyOf :: [a -> Bool] -> a -> Bool
-anyOf ps x = any ($ x) ps
-
--- | Is file info actual?
---isActual :: Symbol a -> IO Bool
---isActual = maybe (return False) checkStamp . symbolLocation where
---	checkStamp l = do
---		actualStamp <- getModificationTime (locationFile l)
---		return $ Just actualStamp == locationTimeStamp l
+module HsDev.Symbols.Util (
+	projectOf, packageDbOf, packageOf,
+	inProject, inDepsOfTarget, inDepsOfFile, inDepsOfProject, inPackageDb, inPackageDbStack, inPackage, inVersion, inFile, inModuleSource, inModule, byFile, installed, standalone,
+	imports, qualifier, moduleImported, visible, inScope,
+	newestPackage,
+	sourceModule, visibleModule, preferredModule, uniqueModules,
+	allOf, anyOf
+	) where
+
+import Control.Arrow ((***), (&&&), first)
+import Control.Lens (view)
+import Control.Monad (liftM)
+import Data.Function (on)
+import Data.Maybe
+import Data.List (maximumBy, groupBy, sortBy, partition)
+import Data.Ord (comparing)
+import Data.String (fromString)
+import System.FilePath (normalise)
+
+import HsDev.Symbols
+import HsDev.Util (ordNub)
+
+-- | Get module project
+projectOf :: ModuleId -> Maybe Project
+projectOf m = case view moduleIdLocation m of
+	FileModule _ proj -> proj
+	_ -> Nothing
+
+-- | Get module cabal
+packageDbOf :: ModuleId -> Maybe PackageDb
+packageDbOf m = case view moduleIdLocation m of
+	InstalledModule c _ _ -> Just c
+	_ -> Nothing
+
+-- | Get module package
+packageOf :: ModuleId -> Maybe ModulePackage
+packageOf m = case view moduleIdLocation m of
+	InstalledModule _ package' _ -> package'
+	_ -> Nothing
+
+-- | Check if module in project
+inProject :: Project -> ModuleId -> Bool
+inProject p m = projectOf m == Just p
+
+-- | Check if module in deps of project target
+inDepsOfTarget :: Info -> ModuleId -> Bool
+inDepsOfTarget i m = any (`inPackage` m) $ view infoDepends i
+
+-- | Check if module in deps of source
+inDepsOfFile :: Project -> FilePath -> ModuleId -> Bool
+inDepsOfFile p f m = any (`inDepsOfTarget` m) (fileTargets p f)
+
+-- | Check if module in deps of project
+inDepsOfProject :: Project -> ModuleId -> Bool
+inDepsOfProject = maybe (const False) (anyPackage . ordNub . concatMap (view infoDepends) . infos) . view projectDescription where
+	anyPackage :: [String] -> ModuleId -> Bool
+	anyPackage = liftM or . mapM inPackage
+
+-- | Check if module in package-db
+inPackageDb :: PackageDb -> ModuleId -> Bool
+inPackageDb c m = case view moduleIdLocation m of
+	InstalledModule d _ _ -> d == c
+	_ -> False
+
+-- | Check if module in one of sandboxes
+inPackageDbStack :: PackageDbStack -> ModuleId -> Bool
+inPackageDbStack dbs m = case view moduleIdLocation m of
+	InstalledModule d _ _ -> d `elem` packageDbs dbs
+	_ -> False
+
+-- | Check if module in package
+inPackage :: String -> ModuleId -> Bool
+inPackage p m = case view moduleIdLocation m of
+	InstalledModule _ package' _ -> Just p == fmap (view packageName) package'
+	_ -> False
+
+inVersion :: String -> ModuleId -> Bool
+inVersion v m = case view moduleIdLocation m of
+	InstalledModule _ package' _ -> Just v == fmap (view packageVersion) package'
+	_ -> False
+
+-- | Check if module in file
+inFile :: FilePath -> ModuleId -> Bool
+inFile fpath m = case view moduleIdLocation m of
+	FileModule f _ -> f == normalise fpath
+	_ -> False
+
+-- | Check if module in source
+inModuleSource :: Maybe String -> ModuleId -> Bool
+inModuleSource src m = case view moduleIdLocation m of
+	ModuleSource src' -> src' == src
+	_ -> False
+
+-- | Check if declaration is in module
+inModule :: String -> ModuleId -> Bool
+inModule mname m = fromString mname == view moduleIdName m
+
+-- | Check if module defined in file
+byFile :: ModuleId -> Bool
+byFile m = case view moduleIdLocation m of
+	FileModule _ _ -> True
+	_ -> False
+
+-- | Check if module got from cabal database
+installed :: ModuleId -> Bool
+installed m = case view moduleIdLocation m of
+	InstalledModule _ _ _ -> True
+	_ -> False
+
+-- | Check if module is standalone
+standalone :: ModuleId -> Bool
+standalone m = case view moduleIdLocation m of
+	FileModule _ Nothing -> True
+	_ -> False
+
+-- | Get list of imports
+imports :: Module -> [Import]
+imports = view moduleImports
+
+-- | Get list of imports, which can be accessed with specified qualifier or unqualified
+qualifier :: Module -> Maybe String -> [Import]
+qualifier m q = filter (importQualifier (fmap fromString q)) $
+	import_ (fromString "Prelude") :
+	import_ (view moduleName m) :
+	imports m
+
+-- | Check if module imported via imports specified
+moduleImported :: ModuleId -> [Import] -> Bool
+moduleImported m = any (\i -> view moduleIdName m == view importModuleName i)
+
+-- | Check if module visible from this module within this project
+visible :: Project -> ModuleId -> ModuleId -> Bool
+visible p (ModuleId _ (FileModule src _)) m =
+	inProject p m || any (`inPackage` m) deps || maybe False ((`elem` deps) . view projectName) (projectOf m)
+	where
+		deps = concatMap (view infoDepends) $ fileTargets p src
+visible _ _ _ = False
+
+-- | Check if module is in scope with qualifier
+inScope :: Module -> Maybe String -> ModuleId -> Bool
+inScope this q m = m `moduleImported` qualifier this q
+
+-- | Select symbols with last package version
+newestPackage :: Symbol a => [a] -> [a]
+newestPackage =
+	uncurry (++) .
+	((selectNewest . groupPackages) *** map snd) .
+	partition (isJust . fst) .
+	map ((mpackage . symbolModuleLocation) &&& id)
+	where
+		mpackage (InstalledModule _ (Just p) _) = Just p
+		mpackage _ = Nothing
+		pname = fmap (view packageName) . fst
+		pver = fmap (view packageVersion) . fst
+		groupPackages :: [(Maybe ModulePackage, a)] -> [(Maybe ModulePackage, [a])]
+		groupPackages = map (first head . unzip) . groupBy ((==) `on` fst) . sortBy (comparing fst)
+		selectNewest :: [(Maybe ModulePackage, [a])] -> [a]
+		selectNewest =
+			concatMap (snd . maximumBy (comparing pver)) .
+			groupBy ((==) `on` pname) .
+			sortBy (comparing pname)
+
+-- | Select module, defined by sources
+sourceModule :: Maybe Project -> [Module] -> Maybe Module
+sourceModule proj ms = listToMaybe $ maybe (const []) (filter . (. view moduleId) . inProject) proj ms ++ filter (byFile . view moduleId) ms
+
+-- | Select module, visible in project or cabal
+-- TODO: PackageDbStack?
+visibleModule :: PackageDb -> Maybe Project -> [Module] -> Maybe Module
+visibleModule d proj ms = listToMaybe $ maybe (const []) (filter . (. view moduleId) . inProject) proj ms ++ filter (inPackageDb d . view moduleId) ms
+
+-- | Select preferred visible module
+-- TODO: PackageDbStack?
+preferredModule :: PackageDb -> Maybe Project -> [ModuleId] -> Maybe ModuleId
+preferredModule d proj ms = listToMaybe $ concatMap (`filter` ms) order where
+	order = [
+		maybe (const False) inProject proj,
+		byFile,
+		inPackageDb d,
+		const True]
+
+-- | Remove duplicate modules, leave only `preferredModule`
+-- TODO: PackageDbStack?
+uniqueModules :: PackageDb -> Maybe Project -> [ModuleId] -> [ModuleId]
+uniqueModules d proj =
+	mapMaybe (preferredModule d proj) .
+	groupBy ((==) `on` view moduleIdName) .
+	sortBy (comparing (view moduleIdName))
+
+-- | Select value, satisfying to all predicates
+allOf :: [a -> Bool] -> a -> Bool
+allOf ps x = all ($ x) ps
+
+-- | Select value, satisfying one of predicates
+anyOf :: [a -> Bool] -> a -> Bool
+anyOf ps x = any ($ x) ps
+
+-- | Is file info actual?
+--isActual :: Symbol a -> IO Bool
+--isActual = maybe (return False) checkStamp . symbolLocation where
+--	checkStamp l = do
+--		actualStamp <- getModificationTime (locationFile l)
+--		return $ Just actualStamp == locationTimeStamp l
diff --git a/src/HsDev/Tools/AutoFix.hs b/src/HsDev/Tools/AutoFix.hs
--- a/src/HsDev/Tools/AutoFix.hs
+++ b/src/HsDev/Tools/AutoFix.hs
@@ -1,92 +1,92 @@
-{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Tools.AutoFix (
-	Correction(..), correctionMessage, corrector,
-	corrections,
-	autoFix,
-	CorrectorMatch,
-	correctors,
-	match,
-	findCorrector,
-
-	module Data.Text.Region,
-	module HsDev.Tools.Types
-	) where
-
-import Control.Applicative
-import Control.Lens hiding ((.=), at)
-import Data.Aeson
-import Data.Maybe (listToMaybe, mapMaybe)
-import Data.Text.Region hiding (Region(..))
-import qualified Data.Text.Region as R
-
-import HsDev.Symbols.Location (Position(..), Region(..))
-import HsDev.Tools.Base
-import HsDev.Tools.Types
-import HsDev.Util ((.::))
-
-data Correction = Correction {
-	_correctionMessage :: String,
-	_corrector :: Replace String }
-		deriving (Eq, Show)
-
-instance ToJSON Correction where
-	toJSON (Correction msg cor) = object [
-		"message" .= msg,
-		"corrector" .= cor]
-
-instance FromJSON Correction where
-	parseJSON = withObject "correction" $ \v -> Correction <$>
-		v .:: "message" <*>
-		v .:: "corrector"
-
-makeLenses ''Correction
-
-corrections :: [Note OutputMessage] -> [Note Correction]
-corrections = mapMaybe toCorrection where
-	toCorrection :: Note OutputMessage -> Maybe (Note Correction)
-	toCorrection n = useSuggestion <|> findCorrector n where
-		-- Use existing suggestion
-		useSuggestion :: Maybe (Note Correction)
-		useSuggestion = do
-			sugg <- view (note . messageSuggestion) n
-			return $ set
-				note
-				(Correction
-					(view (note . message) n)
-					(replace (fromRegion $ view noteRegion n) sugg))
-				n
-
--- | Apply corrections
-autoFix :: [Note Correction] -> ([Note Correction], Maybe String) -> ([Note Correction], Maybe String)
-autoFix ns (upd, mcts) = (over (each . note . corrector . replaceRegion) (update act) upd, over _Just (apply act) mcts) where
-	act = Edit (ns ^.. each . note . corrector)
-
-type CorrectorMatch = Note OutputMessage -> Maybe (Note Correction)
-
-correctors :: [CorrectorMatch]
-correctors = [
-	match "^The (?:qualified )?import of .([\\w\\.]+). is redundant" $ \_ rgn -> Correction -- There are different quotes in Windows/Linux
-		"Redundant import"
-		(cut
-			(expandLines rgn)),
-	match "^(.*?)\nFound:\n  (.*?)\nWhy not:\n  (.*?)$" $ \g rgn -> Correction
-		(g `at` 1)
-		(replace
-			((rgn ^. regionFrom) `regionSize` pt 0 (length $ g `at` 2))
-			(g `at` 3))]
-
-match :: String -> ((Int -> Maybe String) -> R.Region -> Correction) -> CorrectorMatch
-match pat f n = do
-	g <- matchRx pat (view (note . message) n)
-	return $ set note (f g (fromRegion $ view noteRegion n)) n
-
-findCorrector :: Note OutputMessage -> Maybe (Note Correction)
-findCorrector n = listToMaybe $ mapMaybe ($ n) correctors
-
-fromRegion :: Region -> R.Region
-fromRegion (Region f t) = fromPosition f `till` fromPosition t
-
-fromPosition :: Position -> Point
-fromPosition (Position l c) = pt (pred l) (pred c)
+{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Tools.AutoFix (
+	Correction(..), correctionMessage, corrector,
+	corrections,
+	autoFix,
+	CorrectorMatch,
+	correctors,
+	match,
+	findCorrector,
+
+	module Data.Text.Region,
+	module HsDev.Tools.Types
+	) where
+
+import Control.Applicative
+import Control.Lens hiding ((.=), at)
+import Data.Aeson
+import Data.Maybe (listToMaybe, mapMaybe)
+import Data.Text.Region hiding (Region(..))
+import qualified Data.Text.Region as R
+
+import HsDev.Symbols.Location (Position(..), Region(..))
+import HsDev.Tools.Base
+import HsDev.Tools.Types
+import HsDev.Util ((.::))
+
+data Correction = Correction {
+	_correctionMessage :: String,
+	_corrector :: Replace String }
+		deriving (Eq, Show)
+
+instance ToJSON Correction where
+	toJSON (Correction msg cor) = object [
+		"message" .= msg,
+		"corrector" .= cor]
+
+instance FromJSON Correction where
+	parseJSON = withObject "correction" $ \v -> Correction <$>
+		v .:: "message" <*>
+		v .:: "corrector"
+
+makeLenses ''Correction
+
+corrections :: [Note OutputMessage] -> [Note Correction]
+corrections = mapMaybe toCorrection where
+	toCorrection :: Note OutputMessage -> Maybe (Note Correction)
+	toCorrection n = useSuggestion <|> findCorrector n where
+		-- Use existing suggestion
+		useSuggestion :: Maybe (Note Correction)
+		useSuggestion = do
+			sugg <- view (note . messageSuggestion) n
+			return $ set
+				note
+				(Correction
+					(view (note . message) n)
+					(replace (fromRegion $ view noteRegion n) sugg))
+				n
+
+-- | Apply corrections
+autoFix :: [Note Correction] -> ([Note Correction], Maybe String) -> ([Note Correction], Maybe String)
+autoFix ns (upd, mcts) = (over (each . note . corrector . replaceRegion) (update act) upd, over _Just (apply act) mcts) where
+	act = Edit (ns ^.. each . note . corrector)
+
+type CorrectorMatch = Note OutputMessage -> Maybe (Note Correction)
+
+correctors :: [CorrectorMatch]
+correctors = [
+	match "^The (?:qualified )?import of .([\\w\\.]+). is redundant" $ \_ rgn -> Correction -- There are different quotes in Windows/Linux
+		"Redundant import"
+		(cut
+			(expandLines rgn)),
+	match "^(.*?)\nFound:\n  (.*?)\nWhy not:\n  (.*?)$" $ \g rgn -> Correction
+		(g `at` 1)
+		(replace
+			((rgn ^. regionFrom) `regionSize` pt 0 (length $ g `at` 2))
+			(g `at` 3))]
+
+match :: String -> ((Int -> Maybe String) -> R.Region -> Correction) -> CorrectorMatch
+match pat f n = do
+	g <- matchRx pat (view (note . message) n)
+	return $ set note (f g (fromRegion $ view noteRegion n)) n
+
+findCorrector :: Note OutputMessage -> Maybe (Note Correction)
+findCorrector n = listToMaybe $ mapMaybe ($ n) correctors
+
+fromRegion :: Region -> R.Region
+fromRegion (Region f t) = fromPosition f `till` fromPosition t
+
+fromPosition :: Position -> Point
+fromPosition (Position l c) = pt (pred l) (pred c)
diff --git a/src/HsDev/Tools/Base.hs b/src/HsDev/Tools/Base.hs
--- a/src/HsDev/Tools/Base.hs
+++ b/src/HsDev/Tools/Base.hs
@@ -1,117 +1,117 @@
-module HsDev.Tools.Base (
-	runTool, runTool_,
-	Result, ToolM,
-	runWait, runWait_,
-	tool, tool_,
-	matchRx, splitRx, replaceRx,
-	at, at_,
-	inspect,
-	-- * Read parse utils
-	ReadM,
-	readParse, parseReads, parseRead,
-
-	module HsDev.Tools.Types
-	) where
-
-import Control.Lens (set)
-import Control.Monad.Catch (MonadCatch)
-import Control.Monad.Except
-import Control.Monad.State
-import Data.Array (assocs)
-import Data.List (unfoldr, intercalate)
-import Data.Maybe (fromMaybe, listToMaybe)
-import System.Exit
-import System.Process
-import Text.Regex.PCRE ((=~), MatchResult(..))
-
-import HsDev.Error
-import HsDev.Tools.Types
-import HsDev.Symbols
-import HsDev.Util (liftIOErrors)
-
--- | Run tool, throwing HsDevError on fail
-runTool :: FilePath -> [String] -> String -> IO String
-runTool name args input = hsdevLiftIOWith onIOError $ do
-	(code, out, err) <- readProcessWithExitCode name args input
-	case code of
-		ExitFailure ecode -> hsdevError $ ToolError name $
-			"exited with code " ++ show ecode ++ ": " ++ err
-		ExitSuccess -> return out
-	where
-		onIOError s = ToolError name $ unlines [
-			"args: [" ++ intercalate ", " args ++ "]",
-			"stdin: " ++ input,
-			"error: " ++ s]
-
--- | Run tool with not stdin
-runTool_ :: FilePath -> [String] -> IO String
-runTool_ name args = runTool name args ""
-
-type Result = Either String String
-type ToolM a = ExceptT String IO a
-
--- | Run command and wait for result
-runWait :: FilePath -> [String] -> String -> IO Result
-runWait name args input = do
-	(code, out, err) <- readProcessWithExitCode name args input
-	return $ if code == ExitSuccess && not (null out) then Right out else Left err
-
--- | Run command with no input
-runWait_ :: FilePath -> [String] -> IO Result
-runWait_ name args = runWait name args ""
-
--- | Tool
-tool :: FilePath -> [String] -> String -> ToolM String
-tool name args input = liftIOErrors $ ExceptT $ runWait name args input
-
--- | Tool with no input
-tool_ :: FilePath -> [String] -> ToolM String
-tool_ name args = tool name args ""
-
-matchRx :: String -> String -> Maybe (Int -> Maybe String)
-matchRx pat str = if matched then Just look else Nothing where
-	m :: MatchResult String
-	m = str =~ pat
-	matched = not $ null $ mrMatch m
-	groups = filter (not . null . snd) $ assocs $ mrSubs m
-	look i = lookup i groups
-
-splitRx :: String -> String -> [String]
-splitRx pat = unfoldr split' . Just where
-	split' :: Maybe String -> Maybe (String, Maybe String)
-	split' Nothing = Nothing
-	split' (Just str) = case str =~ pat of
-		(pre, "", "") -> Just (pre, Nothing)
-		(pre, _, post) -> Just (pre, Just post)
-
-replaceRx :: String -> String -> String -> String
-replaceRx pat w = intercalate w . splitRx pat
-
-at :: (Int -> Maybe String) -> Int -> String
-at g i = fromMaybe (error $ "Can't find group " ++ show i) $ g i
-
-at_ :: (Int -> Maybe String) -> Int -> String
-at_ g = fromMaybe "" . g
-
-inspect :: MonadCatch m => ModuleLocation -> m Inspection -> m Module -> m InspectedModule
-inspect mloc insp act = execStateT inspect' (notInspected mloc) where
-	inspect' = do
-		r <- hsdevCatch $ hsdevLiftIO $ do
-			i <- lift insp
-			modify (set inspection i)
-			lift act
-		modify (set inspectionResult r)
-
-type ReadM a = StateT String [] a
-
--- | Parse readable value
-readParse :: Read a => ReadM a
-readParse = StateT reads
-
--- | Run parser
-parseReads :: String -> ReadM a -> [a]
-parseReads = flip evalStateT
-
--- | Run parser and select first result
-parseRead :: String -> ReadM a -> Maybe a
-parseRead s = listToMaybe . parseReads s
+module HsDev.Tools.Base (
+	runTool, runTool_,
+	Result, ToolM,
+	runWait, runWait_,
+	tool, tool_,
+	matchRx, splitRx, replaceRx,
+	at, at_,
+	inspect,
+	-- * Read parse utils
+	ReadM,
+	readParse, parseReads, parseRead,
+
+	module HsDev.Tools.Types
+	) where
+
+import Control.Lens (set)
+import Control.Monad.Catch (MonadCatch)
+import Control.Monad.Except
+import Control.Monad.State
+import Data.Array (assocs)
+import Data.List (unfoldr, intercalate)
+import Data.Maybe (fromMaybe, listToMaybe)
+import System.Exit
+import System.Process
+import Text.Regex.PCRE ((=~), MatchResult(..))
+
+import HsDev.Error
+import HsDev.Tools.Types
+import HsDev.Symbols
+import HsDev.Util (liftIOErrors)
+
+-- | Run tool, throwing HsDevError on fail
+runTool :: FilePath -> [String] -> String -> IO String
+runTool name args input = hsdevLiftIOWith onIOError $ do
+	(code, out, err) <- readProcessWithExitCode name args input
+	case code of
+		ExitFailure ecode -> hsdevError $ ToolError name $
+			"exited with code " ++ show ecode ++ ": " ++ err
+		ExitSuccess -> return out
+	where
+		onIOError s = ToolError name $ unlines [
+			"args: [" ++ intercalate ", " args ++ "]",
+			"stdin: " ++ input,
+			"error: " ++ s]
+
+-- | Run tool with not stdin
+runTool_ :: FilePath -> [String] -> IO String
+runTool_ name args = runTool name args ""
+
+type Result = Either String String
+type ToolM a = ExceptT String IO a
+
+-- | Run command and wait for result
+runWait :: FilePath -> [String] -> String -> IO Result
+runWait name args input = do
+	(code, out, err) <- readProcessWithExitCode name args input
+	return $ if code == ExitSuccess && not (null out) then Right out else Left err
+
+-- | Run command with no input
+runWait_ :: FilePath -> [String] -> IO Result
+runWait_ name args = runWait name args ""
+
+-- | Tool
+tool :: FilePath -> [String] -> String -> ToolM String
+tool name args input = liftIOErrors $ ExceptT $ runWait name args input
+
+-- | Tool with no input
+tool_ :: FilePath -> [String] -> ToolM String
+tool_ name args = tool name args ""
+
+matchRx :: String -> String -> Maybe (Int -> Maybe String)
+matchRx pat str = if matched then Just look else Nothing where
+	m :: MatchResult String
+	m = str =~ pat
+	matched = not $ null $ mrMatch m
+	groups = filter (not . null . snd) $ assocs $ mrSubs m
+	look i = lookup i groups
+
+splitRx :: String -> String -> [String]
+splitRx pat = unfoldr split' . Just where
+	split' :: Maybe String -> Maybe (String, Maybe String)
+	split' Nothing = Nothing
+	split' (Just str) = case str =~ pat of
+		(pre, "", "") -> Just (pre, Nothing)
+		(pre, _, post) -> Just (pre, Just post)
+
+replaceRx :: String -> String -> String -> String
+replaceRx pat w = intercalate w . splitRx pat
+
+at :: (Int -> Maybe String) -> Int -> String
+at g i = fromMaybe (error $ "Can't find group " ++ show i) $ g i
+
+at_ :: (Int -> Maybe String) -> Int -> String
+at_ g = fromMaybe "" . g
+
+inspect :: MonadCatch m => ModuleLocation -> m Inspection -> m Module -> m InspectedModule
+inspect mloc insp act = execStateT inspect' (notInspected mloc) where
+	inspect' = do
+		r <- hsdevCatch $ hsdevLiftIO $ do
+			i <- lift insp
+			modify (set inspection i)
+			lift act
+		modify (set inspectionResult r)
+
+type ReadM a = StateT String [] a
+
+-- | Parse readable value
+readParse :: Read a => ReadM a
+readParse = StateT reads
+
+-- | Run parser
+parseReads :: String -> ReadM a -> [a]
+parseReads = flip evalStateT
+
+-- | Run parser and select first result
+parseRead :: String -> ReadM a -> Maybe a
+parseRead s = listToMaybe . parseReads s
diff --git a/src/HsDev/Tools/Cabal.hs b/src/HsDev/Tools/Cabal.hs
--- a/src/HsDev/Tools/Cabal.hs
+++ b/src/HsDev/Tools/Cabal.hs
@@ -1,85 +1,85 @@
-{-# LANGUAGE OverloadedStrings, CPP #-}
-
-module HsDev.Tools.Cabal (
-	CabalPackage(..),
-	cabalList,
-
-	-- * Reexports
-	Version(..), License(..)
-	) where
-
-import Control.Arrow
-import Control.Monad
-import Data.Aeson
-import Data.Char (isSpace)
-import Data.Maybe
-import Distribution.License
-import Distribution.Text
-import Distribution.Version
-
-import HsDev.Tools.Base
-import HsDev.Util
-
-data CabalPackage = CabalPackage {
-	cabalPackageName :: String,
-	cabalPackageSynopsis :: Maybe String,
-	cabalPackageDefaultVersion :: Maybe Version,
-	cabalPackageInstalledVersions :: [Version],
-	cabalPackageHomepage :: Maybe String,
-	cabalPackageLicense :: Maybe License }
-		deriving (Eq, Read, Show)
-
-instance ToJSON CabalPackage where
-	toJSON cp = object [
-		"name" .= cabalPackageName cp,
-		"synopsis" .= cabalPackageSynopsis cp,
-		"default-version" .= fmap display (cabalPackageDefaultVersion cp),
-		"installed-versions" .= map display (cabalPackageInstalledVersions cp),
-		"homepage" .= cabalPackageHomepage cp,
-		"license" .= fmap display (cabalPackageLicense cp)]
-
-instance FromJSON CabalPackage where
-	parseJSON = withObject "cabal-package" $ \v -> CabalPackage <$>
-		(v .:: "name") <*>
-		(v .:: "synopsis") <*>
-		((join . fmap simpleParse) <$> (v .:: "default-version")) <*>
-		(mapMaybe simpleParse <$> (v .:: "installed-versions")) <*>
-		(v .:: "homepage") <*>
-		((join . fmap simpleParse) <$> (v .:: "license"))
-
-cabalList :: [String] -> ToolM [CabalPackage]
-cabalList queries = do
-#if mingw32_HOST_OS
-	rs <- liftM (split (all isSpace) . lines) $ tool_ "powershell" [
-		"-Command",
-		unwords (["&", "{", "chcp 65001 | out-null;", "cabal list"] ++ queries ++ ["}"])]
-#else
-	rs <- liftM (split (all isSpace) . lines) $ tool_ "cabal" ("list" : queries)
-#endif
-	return $ map toPackage $ mapMaybe parseFields rs
-	where
-		toPackage :: (String, [(String, String)]) -> CabalPackage
-		toPackage (name, fs) = CabalPackage {
-			cabalPackageName = name,
-			cabalPackageSynopsis = lookup "Synopsis" fs,
-			cabalPackageDefaultVersion = (lookup "Default available version" fs >>= simpleParse),
-			cabalPackageInstalledVersions = fromMaybe [] (lookup "Installed versions" fs >>= mapM (simpleParse . trim) . split (== ',')),
-			cabalPackageHomepage = lookup "Homepage" fs,
-			cabalPackageLicense = lookup "License" fs >>= simpleParse }
-
-		parseFields :: [String] -> Maybe (String, [(String, String)])
-		parseFields [] = Nothing
-		parseFields (('*':name):fs) = Just (trim name, mapMaybe parseField' fs) where
-			parseField' :: String -> Maybe (String, String)
-			parseField' str = case parseField str of
-				(fname, Just fval) -> Just (fname, fval)
-				_ -> Nothing
-		parseFields _ = Nothing
-
-		-- foo: bar → (foo, bar)
-		parseField :: String -> (String, Maybe String)
-		parseField = (trim *** (parseValue . trim . drop 1)) . break (== ':')
-		-- [ ... ] → Nothing, ... → Just ...
-		parseValue :: String -> Maybe String
-		parseValue ('[':_) = Nothing
-		parseValue v = Just v
+{-# LANGUAGE OverloadedStrings, CPP #-}
+
+module HsDev.Tools.Cabal (
+	CabalPackage(..),
+	cabalList,
+
+	-- * Reexports
+	Version, License(..)
+	) where
+
+import Control.Arrow
+import Control.Monad
+import Data.Aeson
+import Data.Char (isSpace)
+import Data.Maybe
+import Distribution.License
+import Distribution.Text
+import Distribution.Version
+
+import HsDev.Tools.Base
+import HsDev.Util
+
+data CabalPackage = CabalPackage {
+	cabalPackageName :: String,
+	cabalPackageSynopsis :: Maybe String,
+	cabalPackageDefaultVersion :: Maybe Version,
+	cabalPackageInstalledVersions :: [Version],
+	cabalPackageHomepage :: Maybe String,
+	cabalPackageLicense :: Maybe License }
+		deriving (Eq, Read, Show)
+
+instance ToJSON CabalPackage where
+	toJSON cp = object [
+		"name" .= cabalPackageName cp,
+		"synopsis" .= cabalPackageSynopsis cp,
+		"default-version" .= fmap display (cabalPackageDefaultVersion cp),
+		"installed-versions" .= map display (cabalPackageInstalledVersions cp),
+		"homepage" .= cabalPackageHomepage cp,
+		"license" .= fmap display (cabalPackageLicense cp)]
+
+instance FromJSON CabalPackage where
+	parseJSON = withObject "cabal-package" $ \v -> CabalPackage <$>
+		(v .:: "name") <*>
+		(v .:: "synopsis") <*>
+		((join . fmap simpleParse) <$> (v .:: "default-version")) <*>
+		(mapMaybe simpleParse <$> (v .:: "installed-versions")) <*>
+		(v .:: "homepage") <*>
+		((join . fmap simpleParse) <$> (v .:: "license"))
+
+cabalList :: [String] -> ToolM [CabalPackage]
+cabalList queries = do
+#if mingw32_HOST_OS
+	rs <- liftM (split (all isSpace) . lines) $ tool_ "powershell" [
+		"-Command",
+		unwords (["&", "{", "chcp 65001 | out-null;", "cabal list"] ++ queries ++ ["}"])]
+#else
+	rs <- liftM (split (all isSpace) . lines) $ tool_ "cabal" ("list" : queries)
+#endif
+	return $ map toPackage $ mapMaybe parseFields rs
+	where
+		toPackage :: (String, [(String, String)]) -> CabalPackage
+		toPackage (name, fs) = CabalPackage {
+			cabalPackageName = name,
+			cabalPackageSynopsis = lookup "Synopsis" fs,
+			cabalPackageDefaultVersion = (lookup "Default available version" fs >>= simpleParse),
+			cabalPackageInstalledVersions = fromMaybe [] (lookup "Installed versions" fs >>= mapM (simpleParse . trim) . split (== ',')),
+			cabalPackageHomepage = lookup "Homepage" fs,
+			cabalPackageLicense = lookup "License" fs >>= simpleParse }
+
+		parseFields :: [String] -> Maybe (String, [(String, String)])
+		parseFields [] = Nothing
+		parseFields (('*':name):fs) = Just (trim name, mapMaybe parseField' fs) where
+			parseField' :: String -> Maybe (String, String)
+			parseField' str = case parseField str of
+				(fname, Just fval) -> Just (fname, fval)
+				_ -> Nothing
+		parseFields _ = Nothing
+
+		-- foo: bar → (foo, bar)
+		parseField :: String -> (String, Maybe String)
+		parseField = (trim *** (parseValue . trim . drop 1)) . break (== ':')
+		-- [ ... ] → Nothing, ... → Just ...
+		parseValue :: String -> Maybe String
+		parseValue ('[':_) = Nothing
+		parseValue v = Just v
diff --git a/src/HsDev/Tools/ClearImports.hs b/src/HsDev/Tools/ClearImports.hs
--- a/src/HsDev/Tools/ClearImports.hs
+++ b/src/HsDev/Tools/ClearImports.hs
@@ -1,112 +1,112 @@
-module HsDev.Tools.ClearImports (
-	dumpMinimalImports, waitImports, cleanTmpImports,
-	findMinimalImports,
-	groupImports, splitImport,
-	clearImports,
-
-	module Control.Monad.Except
-	) where
-
-import Control.Arrow
-import Control.Concurrent (threadDelay)
-import Control.Exception
-import Control.Monad.Except
-import Data.Char
-import Data.List
-import Data.Maybe (mapMaybe)
-import System.Directory
-import System.FilePath
-import qualified Language.Haskell.Exts as Exts
-
-import GHC
-import GHC.Paths (libdir)
-
-import HsDev.Util
-import HsDev.Tools.Ghc.Compat
-
--- | Dump minimal imports
-dumpMinimalImports :: [String] -> FilePath -> ExceptT String IO String
-dumpMinimalImports opts f = do
-	cur <- liftE getCurrentDirectory
-	file <- liftE $ canonicalizePath f
-	cts <- liftE $ readFileUtf8 file
-
-	mname <- case Exts.parseFileContentsWithMode (pmode file) cts of
-		Exts.ParseFailed loc err -> throwError $
-			"Failed to parse file at " ++
-			Exts.prettyPrint loc ++ ":" ++ err
-		Exts.ParseOk (Exts.Module _ (Just (Exts.ModuleHead _ (Exts.ModuleName _ mname) _ _)) _ _ _) -> return mname
-		_ -> throwError "Error"
-
-	void $ liftE $ runGhc (Just libdir) $ do
-		df <- getSessionDynFlags
-		let
-			df' = df {
-				ghcLink = NoLink,
-				hscTarget = HscNothing,
-				dumpDir = Just cur,
-				stubDir = Just cur,
-				objectDir = Just cur,
-				hiDir = Just cur }
-		(df'', _, _) <- parseDynamicFlags df' (map noLoc ("-ddump-minimal-imports" : opts))
-		_ <- setSessionDynFlags df''
-		cleanupHandler df'' $ do
-			t <- guessTarget file Nothing
-			setTargets [t]
-			load LoadAllTargets
-
-	length mname `seq` return mname
-	where
-		pmode :: FilePath -> Exts.ParseMode
-		pmode f' = Exts.defaultParseMode {
-			Exts.parseFilename = f',
-			Exts.baseLanguage = Exts.Haskell2010,
-			Exts.extensions = Exts.glasgowExts ++ map Exts.parseExtension exts,
-			Exts.fixities = Just Exts.baseFixities }
-		exts = mapMaybe (stripPrefix "-X") opts
-
--- | Read imports from file
-waitImports :: FilePath -> IO [String]
-waitImports f = retry 1000 $ do
-	is <- liftM lines $ readFile f
-	length is `seq` return is
-
--- | Clean temporary files
-cleanTmpImports :: FilePath -> IO ()
-cleanTmpImports dir = do
-	dumps <- liftM (filter ((== ".imports") . takeExtension)) $ directoryContents dir
-	forM_ dumps $ handle ignoreIO' . retry 1000 . removeFile
-	where
-		ignoreIO' :: IOException -> IO ()
-		ignoreIO' _ = return ()
-
--- | Dump and read imports
-findMinimalImports :: [String] -> FilePath -> ExceptT String IO [String]
-findMinimalImports opts f = do
-	file <- liftE $ canonicalizePath f
-	mname <- dumpMinimalImports opts file
-	is <- liftE $ waitImports (mname <.> "imports")
-	tmp <- liftE getCurrentDirectory
-	liftE $ cleanTmpImports tmp
-	return is
-
--- | Groups several lines related to one import by indents
-groupImports :: [String] -> [[String]]
-groupImports = unfoldr getPack where
-	getPack [] = Nothing
-	getPack (s:ss) = Just $ first (s:) $ break (null . takeWhile isSpace) ss
-
--- | Split import to import and import-list
-splitImport :: [String] -> (String, String)
-splitImport = splitBraces . unwords . map trim where
-	cut = twice $ reverse . drop 1
-	twice f = f . f
-	splitBraces = (trim *** (trim . cut)) . break (== '(')
-
--- | Returns minimal imports for file specified
-clearImports :: [String] -> FilePath -> ExceptT String IO [(String, String)]
-clearImports opts = liftM (map splitImport . groupImports) . findMinimalImports opts
-
--- | Retry action on fail
-retry :: (MonadPlus m, MonadIO m) => Int -> m a -> m a
-retry dt act = msum $ act : repeat ((liftIO (threadDelay dt) >>) act)
+module HsDev.Tools.ClearImports (
+	dumpMinimalImports, waitImports, cleanTmpImports,
+	findMinimalImports,
+	groupImports, splitImport,
+	clearImports,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Arrow
+import Control.Concurrent (threadDelay)
+import Control.Exception
+import Control.Monad.Except
+import Data.Char
+import Data.List
+import Data.Maybe (mapMaybe)
+import System.Directory
+import System.FilePath
+import qualified Language.Haskell.Exts as Exts
+
+import GHC
+import GHC.Paths (libdir)
+
+import HsDev.Util
+import HsDev.Tools.Ghc.Compat
+
+-- | Dump minimal imports
+dumpMinimalImports :: [String] -> FilePath -> ExceptT String IO String
+dumpMinimalImports opts f = do
+	cur <- liftE getCurrentDirectory
+	file <- liftE $ canonicalizePath f
+	cts <- liftE $ readFileUtf8 file
+
+	mname <- case Exts.parseFileContentsWithMode (pmode file) cts of
+		Exts.ParseFailed loc err -> throwError $
+			"Failed to parse file at " ++
+			Exts.prettyPrint loc ++ ":" ++ err
+		Exts.ParseOk (Exts.Module _ (Just (Exts.ModuleHead _ (Exts.ModuleName _ mname) _ _)) _ _ _) -> return mname
+		_ -> throwError "Error"
+
+	void $ liftE $ runGhc (Just libdir) $ do
+		df <- getSessionDynFlags
+		let
+			df' = df {
+				ghcLink = NoLink,
+				hscTarget = HscNothing,
+				dumpDir = Just cur,
+				stubDir = Just cur,
+				objectDir = Just cur,
+				hiDir = Just cur }
+		(df'', _, _) <- parseDynamicFlags df' (map noLoc ("-ddump-minimal-imports" : opts))
+		_ <- setSessionDynFlags df''
+		cleanupHandler df'' $ do
+			t <- guessTarget file Nothing
+			setTargets [t]
+			load LoadAllTargets
+
+	length mname `seq` return mname
+	where
+		pmode :: FilePath -> Exts.ParseMode
+		pmode f' = Exts.defaultParseMode {
+			Exts.parseFilename = f',
+			Exts.baseLanguage = Exts.Haskell2010,
+			Exts.extensions = Exts.glasgowExts ++ map Exts.parseExtension exts,
+			Exts.fixities = Just Exts.baseFixities }
+		exts = mapMaybe (stripPrefix "-X") opts
+
+-- | Read imports from file
+waitImports :: FilePath -> IO [String]
+waitImports f = retry 1000 $ do
+	is <- liftM lines $ readFile f
+	length is `seq` return is
+
+-- | Clean temporary files
+cleanTmpImports :: FilePath -> IO ()
+cleanTmpImports dir = do
+	dumps <- liftM (filter ((== ".imports") . takeExtension)) $ directoryContents dir
+	forM_ dumps $ handle ignoreIO' . retry 1000 . removeFile
+	where
+		ignoreIO' :: IOException -> IO ()
+		ignoreIO' _ = return ()
+
+-- | Dump and read imports
+findMinimalImports :: [String] -> FilePath -> ExceptT String IO [String]
+findMinimalImports opts f = do
+	file <- liftE $ canonicalizePath f
+	mname <- dumpMinimalImports opts file
+	is <- liftE $ waitImports (mname <.> "imports")
+	tmp <- liftE getCurrentDirectory
+	liftE $ cleanTmpImports tmp
+	return is
+
+-- | Groups several lines related to one import by indents
+groupImports :: [String] -> [[String]]
+groupImports = unfoldr getPack where
+	getPack [] = Nothing
+	getPack (s:ss) = Just $ first (s:) $ break (null . takeWhile isSpace) ss
+
+-- | Split import to import and import-list
+splitImport :: [String] -> (String, String)
+splitImport = splitBraces . unwords . map trim where
+	cut = twice $ reverse . drop 1
+	twice f = f . f
+	splitBraces = (trim *** (trim . cut)) . break (== '(')
+
+-- | Returns minimal imports for file specified
+clearImports :: [String] -> FilePath -> ExceptT String IO [(String, String)]
+clearImports opts = liftM (map splitImport . groupImports) . findMinimalImports opts
+
+-- | Retry action on fail
+retry :: (MonadPlus m, MonadIO m) => Int -> m a -> m a
+retry dt act = msum $ act : repeat ((liftIO (threadDelay dt) >>) act)
diff --git a/src/HsDev/Tools/Ghc/Check.hs b/src/HsDev/Tools/Ghc/Check.hs
--- a/src/HsDev/Tools/Ghc/Check.hs
+++ b/src/HsDev/Tools/Ghc/Check.hs
@@ -1,88 +1,88 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module HsDev.Tools.Ghc.Check (
-	checkFiles, check, checkFile, checkSource,
-
-	Ghc,
-	module HsDev.Tools.Types,
-	module HsDev.Symbols.Types,
-	PackageDb(..), PackageDbStack(..), Project(..),
-
-	recalcNotesTabs,
-
-	module Control.Monad.Except
-	) where
-
-import Control.Lens (preview, view, each, _Just, (^..))
-import Control.Monad.Except
-import Data.Maybe (fromMaybe)
-import System.FilePath (makeRelative)
-import System.Directory (doesDirectoryExist)
-import System.Log.Simple (MonadLog(..), scope)
-
-import GHC hiding (Warning, Module, moduleName)
-
-import Control.Concurrent.FiniteChan
-import HsDev.Error
-import HsDev.PackageDb
-import HsDev.Symbols.Location
-import HsDev.Symbols.Types
-import HsDev.Project (fileTarget)
-import HsDev.Tools.Base
-import HsDev.Tools.Ghc.Worker
-import HsDev.Tools.Ghc.Compat
-import HsDev.Tools.Types
-import HsDev.Util (readFileUtf8, ordNub)
-
--- | Check files and collect warnings and errors
-checkFiles :: (MonadLog m, GhcMonad m) => [String] -> [FilePath] -> Maybe Project -> m [Note OutputMessage]
-checkFiles opts files _ = scope "check-files" $ do
-	ch <- liftIO newChan
-	withFlags $ do
-		modifyFlags $ setLogAction $ logToChan ch
-		addCmdOpts opts
-		clearTargets
-		mapM (`makeTarget` Nothing) files >>= loadTargets
-	notes <- liftIO $ stopChan ch
-	liftIO $ recalcNotesTabs notes
-
--- | Check module source
-check :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m [Note OutputMessage]
-check opts m msrc = scope "check" $ case view moduleLocation m of
-	FileModule file proj -> do
-		ch <- liftIO newChan
-		let
-			dir = fromMaybe
-				(sourceModuleRoot (view moduleName m) file) $
-				preview (_Just . projectPath) proj
-		dirExist <- liftIO $ doesDirectoryExist dir
-		withFlags $ (if dirExist then withCurrentDirectory dir else id) $ do
-			addCmdOpts opts
-			modifyFlags $ setLogAction $ logToChan ch
-			clearTargets
-			target <- makeTarget (makeRelative dir file) msrc
-			loadTargets [target]
-		notes <- liftIO $ stopChan ch
-		liftIO $ recalcNotesTabs notes
-	_ -> scope "check" $ hsdevError $ ModuleNotSource (view moduleLocation m)
-
--- | Check module and collect warnings and errors
-checkFile :: (MonadLog m, GhcMonad m) => [String] -> Module -> m [Note OutputMessage]
-checkFile opts m = check opts m Nothing
-
--- | Check module and collect warnings and errors
-checkSource :: (MonadLog m, GhcMonad m) => [String] -> Module -> String -> m [Note OutputMessage]
-checkSource opts m src = check opts m (Just src)
-
--- Recalc tabs for notes
-recalcNotesTabs :: [Note OutputMessage] -> IO [Note OutputMessage]
-recalcNotesTabs notes = do
-	cts <- mapM readFileUtf8 files
-	let
-		recalc' n = fromMaybe n $ do
-			fname <- preview (noteSource . moduleFile) n
-			cts' <- lookup fname (zip files cts)
-			return $ recalcTabs cts' 8 n
-	return $ map recalc' notes
-	where
-		files = ordNub $ notes ^.. each . noteSource . moduleFile
+{-# LANGUAGE OverloadedStrings #-}
+
+module HsDev.Tools.Ghc.Check (
+	checkFiles, check, checkFile, checkSource,
+
+	Ghc,
+	module HsDev.Tools.Types,
+	module HsDev.Symbols.Types,
+	PackageDb(..), PackageDbStack(..), Project(..),
+
+	recalcNotesTabs,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Lens (preview, view, each, _Just, (^..))
+import Control.Monad.Except
+import Data.Maybe (fromMaybe)
+import System.FilePath (makeRelative)
+import System.Directory (doesDirectoryExist)
+import System.Log.Simple (MonadLog(..), scope)
+
+import GHC hiding (Warning, Module, moduleName)
+
+import Control.Concurrent.FiniteChan
+import HsDev.Error
+import HsDev.PackageDb
+import HsDev.Symbols.Location
+import HsDev.Symbols.Types
+import HsDev.Project (fileTarget)
+import HsDev.Tools.Base
+import HsDev.Tools.Ghc.Worker
+import HsDev.Tools.Ghc.Compat as C
+import HsDev.Tools.Types
+import HsDev.Util (readFileUtf8, ordNub)
+
+-- | Check files and collect warnings and errors
+checkFiles :: (MonadLog m, GhcMonad m) => [String] -> [FilePath] -> Maybe Project -> m [Note OutputMessage]
+checkFiles opts files _ = scope "check-files" $ do
+	ch <- liftIO newChan
+	withFlags $ do
+		modifyFlags $ C.setLogAction $ logToChan ch
+		addCmdOpts opts
+		clearTargets
+		mapM (`makeTarget` Nothing) files >>= loadTargets
+	notes <- liftIO $ stopChan ch
+	liftIO $ recalcNotesTabs notes
+
+-- | Check module source
+check :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m [Note OutputMessage]
+check opts m msrc = scope "check" $ case view moduleLocation m of
+	FileModule file proj -> do
+		ch <- liftIO newChan
+		let
+			dir = fromMaybe
+				(sourceModuleRoot (view moduleName m) file) $
+				preview (_Just . projectPath) proj
+		dirExist <- liftIO $ doesDirectoryExist dir
+		withFlags $ (if dirExist then withCurrentDirectory dir else id) $ do
+			addCmdOpts opts
+			modifyFlags $ C.setLogAction $ logToChan ch
+			clearTargets
+			target <- makeTarget (makeRelative dir file) msrc
+			loadTargets [target]
+		notes <- liftIO $ stopChan ch
+		liftIO $ recalcNotesTabs notes
+	_ -> scope "check" $ hsdevError $ ModuleNotSource (view moduleLocation m)
+
+-- | Check module and collect warnings and errors
+checkFile :: (MonadLog m, GhcMonad m) => [String] -> Module -> m [Note OutputMessage]
+checkFile opts m = check opts m Nothing
+
+-- | Check module and collect warnings and errors
+checkSource :: (MonadLog m, GhcMonad m) => [String] -> Module -> String -> m [Note OutputMessage]
+checkSource opts m src = check opts m (Just src)
+
+-- Recalc tabs for notes
+recalcNotesTabs :: [Note OutputMessage] -> IO [Note OutputMessage]
+recalcNotesTabs notes = do
+	cts <- mapM readFileUtf8 files
+	let
+		recalc' n = fromMaybe n $ do
+			fname <- preview (noteSource . moduleFile) n
+			cts' <- lookup fname (zip files cts)
+			return $ recalcTabs cts' 8 n
+	return $ map recalc' notes
+	where
+		files = ordNub $ notes ^.. each . noteSource . moduleFile
diff --git a/src/HsDev/Tools/Ghc/Compat.hs b/src/HsDev/Tools/Ghc/Compat.hs
--- a/src/HsDev/Tools/Ghc/Compat.hs
+++ b/src/HsDev/Tools/Ghc/Compat.hs
@@ -1,113 +1,154 @@
-{-# LANGUAGE CPP #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Tools.Ghc.Compat (
-	pkgDatabase, UnitId, unitId, moduleUnitId, depends, getPackageDetails, patSynType, cleanupHandler, renderStyle,
-	LogAction, setLogAction,
-	languages, flags
-	) where
-
-import qualified DynFlags as GHC
-import qualified ErrUtils
-import qualified GHC
-import qualified Module
-import qualified Packages as GHC
-import qualified PatSyn as GHC
-import qualified Pretty
-
-#if __GLASGOW_HASKELL__ == 710
-import Exception (ExceptionMonad)
-import Control.Monad.Reader
-#endif
-
-pkgDatabase :: GHC.DynFlags -> Maybe [GHC.PackageConfig]
-#if __GLASGOW_HASKELL__ == 800
-pkgDatabase = fmap (concatMap snd) . GHC.pkgDatabase
-#elif __GLASGOW_HASKELL__ == 710
-pkgDatabase = GHC.pkgDatabase
-#endif
-
-#if __GLASGOW_HASKELL__ == 800
-type UnitId = Module.UnitId
-#elif __GLASGOW_HASKELL__ == 710
-type UnitId = Module.PackageKey
-#endif
-
-unitId :: GHC.PackageConfig -> UnitId
-#if __GLASGOW_HASKELL__ == 800
-unitId = GHC.unitId
-#elif __GLASGOW_HASKELL__ == 710
-unitId = GHC.packageKey
-#endif
-
-moduleUnitId :: GHC.Module -> UnitId
-#if __GLASGOW_HASKELL__ == 800
-moduleUnitId = GHC.moduleUnitId
-#elif __GLASGOW_HASKELL__ == 710
-moduleUnitId = GHC.modulePackageKey
-#endif
-
-depends :: GHC.DynFlags -> GHC.PackageConfig -> [UnitId]
-#if __GLASGOW_HASKELL__ == 800
-depends _ = GHC.depends
-#elif __GLASGOW_HASKELL__ == 710
-depends df = map (GHC.resolveInstalledPackageId df) . GHC.depends
-#endif
-
-getPackageDetails :: GHC.DynFlags -> UnitId -> GHC.PackageConfig
-getPackageDetails = GHC.getPackageDetails
-
-patSynType :: GHC.PatSyn -> GHC.Type
-patSynType p = GHC.patSynInstResTy p (GHC.patSynArgs p)
-
-#if __GLASGOW_HASKELL__ == 800
-cleanupHandler :: GHC.DynFlags -> m a -> m a
-cleanupHandler _ = id
-#elif __GLASGOW_HASKELL__ == 710
-cleanupHandler :: (ExceptionMonad m) => GHC.DynFlags -> m a -> m a
-cleanupHandler = GHC.defaultCleanupHandler
-#endif
-
-renderStyle :: Pretty.Mode -> Int -> Pretty.Doc -> String
-#if __GLASGOW_HASKELL__ == 800
-renderStyle m cols = Pretty.renderStyle (Pretty.Style m cols 1.5)
-#elif __GLASGOW_HASKELL__ == 710
-renderStyle = Pretty.showDoc
-#endif
-
-type LogAction = GHC.DynFlags -> GHC.Severity -> GHC.SrcSpan -> ErrUtils.MsgDoc -> IO ()
-
-setLogAction :: LogAction -> GHC.DynFlags -> GHC.DynFlags
-setLogAction act fs = fs { GHC.log_action = act' } where
-	act' :: GHC.LogAction
-#if __GLASGOW_HASKELL__ == 800
-	act' df _ sev src _ msg = act df sev src msg
-#elif __GLASGOW_HASKELL__ == 710
-	act' df sev src _ msg = act df sev src msg
-#endif
-
-#if __GLASGOW_HASKELL__ == 710
-instance (Monad m, GHC.HasDynFlags m) => GHC.HasDynFlags (ReaderT r m) where
-	getDynFlags = lift GHC.getDynFlags
-#endif
-
-flags :: [String]
-#if __GLASGOW_HASKELL__ >= 800
-flags = concat [
-	[option | (GHC.FlagSpec option _ _ _) <- GHC.fFlags],
-	["warn-" ++ option | (GHC.FlagSpec option _ _ _) <- GHC.wWarningFlags],
-	[option | (GHC.FlagSpec option _ _ _) <- GHC.fLangFlags]]
-#elif __GLASGOW_HASKELL__ >= 710
-flags = concat [
-	[option | (GHC.FlagSpec option _ _ _) <- GHC.fFlags],
-	[option | (GHC.FlagSpec option _ _ _) <- GHC.fWarningFlags],
-	[option | (GHC.FlagSpec option _ _ _) <- GHC.fLangFlags]]
-#elif __GLASGOW_HASKELL__ >= 704
-flags = concat [
-	[option | (option, _, _) <- GHC.fFlags],
-	[option | (option, _, _) <- GHC.fWarningFlags],
-	[option | (option, _, _) <- GHC.fLangFlags]]
-#endif
-
-languages :: [String]
-languages = GHC.supportedLanguagesAndExtensions
+{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Tools.Ghc.Compat (
+	pkgDatabase,
+	UnitId, InstalledUnitId, toInstalledUnitId,
+	unitId, moduleUnitId, depends, getPackageDetails, patSynType, cleanupHandler, renderStyle,
+	LogAction, setLogAction,
+	languages, flags,
+	unqualStyle,
+	exposedModuleName
+	) where
+
+import qualified DynFlags as GHC
+import qualified ErrUtils
+import qualified GHC
+import qualified Module
+import qualified Packages as GHC
+import qualified PatSyn as GHC
+import qualified Pretty
+import Outputable
+
+#if __GLASGOW_HASKELL__ == 710
+import Exception (ExceptionMonad)
+import Control.Monad.Reader
+#endif
+
+#if __GLASGOW_HASKELL__ <= 800
+import qualified GHC.PackageDb as GHC
+#endif
+
+pkgDatabase :: GHC.DynFlags -> Maybe [GHC.PackageConfig]
+#if __GLASGOW_HASKELL__ >= 800
+pkgDatabase = fmap (concatMap snd) . GHC.pkgDatabase
+#elif __GLASGOW_HASKELL__ == 710
+pkgDatabase = GHC.pkgDatabase
+#endif
+
+#if __GLASGOW_HASKELL__ >= 800
+type UnitId = Module.UnitId
+#elif __GLASGOW_HASKELL__ == 710
+type UnitId = Module.PackageKey
+#endif
+
+#if __GLASGOW_HASKELL__ == 802
+type InstalledUnitId = Module.InstalledUnitId
+#else
+type InstalledUnitId = UnitId
+#endif
+
+toInstalledUnitId :: UnitId -> InstalledUnitId
+#if __GLASGOW_HASKELL__ == 802
+toInstalledUnitId = Module.toInstalledUnitId
+#else
+toInstalledUnitId = id
+#endif
+
+unitId :: GHC.PackageConfig -> InstalledUnitId
+#if __GLASGOW_HASKELL__ >= 800
+unitId = GHC.unitId
+#elif __GLASGOW_HASKELL__ == 710
+unitId = GHC.packageKey
+#endif
+
+moduleUnitId :: GHC.Module -> UnitId
+#if __GLASGOW_HASKELL__ >= 800
+moduleUnitId = GHC.moduleUnitId
+#elif __GLASGOW_HASKELL__ == 710
+moduleUnitId = GHC.modulePackageKey
+#endif
+
+depends :: GHC.DynFlags -> GHC.PackageConfig -> [InstalledUnitId]
+#if __GLASGOW_HASKELL__ >= 800
+depends _ = GHC.depends
+#elif __GLASGOW_HASKELL__ == 710
+depends df = map (GHC.resolveInstalledPackageId df) . GHC.depends
+#endif
+
+getPackageDetails :: GHC.DynFlags -> InstalledUnitId -> GHC.PackageConfig
+#if __GLASGOW_HASKELL__ == 802
+getPackageDetails = GHC.getInstalledPackageDetails
+#else
+getPackageDetails = GHC.getPackageDetails
+#endif
+
+patSynType :: GHC.PatSyn -> GHC.Type
+patSynType p = GHC.patSynInstResTy p (GHC.patSynArgs p)
+
+#if __GLASGOW_HASKELL__ >= 800
+cleanupHandler :: GHC.DynFlags -> m a -> m a
+cleanupHandler _ = id
+#elif __GLASGOW_HASKELL__ == 710
+cleanupHandler :: (ExceptionMonad m) => GHC.DynFlags -> m a -> m a
+cleanupHandler = GHC.defaultCleanupHandler
+#endif
+
+renderStyle :: Pretty.Mode -> Int -> Pretty.Doc -> String
+#if __GLASGOW_HASKELL__ >= 800
+renderStyle m cols = Pretty.renderStyle (Pretty.Style m cols 1.5)
+#elif __GLASGOW_HASKELL__ == 710
+renderStyle = Pretty.showDoc
+#endif
+
+type LogAction = GHC.DynFlags -> GHC.Severity -> GHC.SrcSpan -> ErrUtils.MsgDoc -> IO ()
+
+setLogAction :: LogAction -> GHC.DynFlags -> GHC.DynFlags
+setLogAction act fs = fs { GHC.log_action = act' } where
+	act' :: GHC.LogAction
+#if __GLASGOW_HASKELL__ >= 800
+	act' df _ sev src _ msg = act df sev src msg
+#elif __GLASGOW_HASKELL__ == 710
+	act' df sev src _ msg = act df sev src msg
+#endif
+
+#if __GLASGOW_HASKELL__ == 710
+instance (Monad m, GHC.HasDynFlags m) => GHC.HasDynFlags (ReaderT r m) where
+	getDynFlags = lift GHC.getDynFlags
+#endif
+
+flags :: [String]
+#if __GLASGOW_HASKELL__ >= 800
+flags = concat [
+	[option | (GHC.FlagSpec option _ _ _) <- GHC.fFlags],
+	["warn-" ++ option | (GHC.FlagSpec option _ _ _) <- GHC.wWarningFlags],
+	[option | (GHC.FlagSpec option _ _ _) <- GHC.fLangFlags]]
+#elif __GLASGOW_HASKELL__ >= 710
+flags = concat [
+	[option | (GHC.FlagSpec option _ _ _) <- GHC.fFlags],
+	[option | (GHC.FlagSpec option _ _ _) <- GHC.fWarningFlags],
+	[option | (GHC.FlagSpec option _ _ _) <- GHC.fLangFlags]]
+#elif __GLASGOW_HASKELL__ >= 704
+flags = concat [
+	[option | (option, _, _) <- GHC.fFlags],
+	[option | (option, _, _) <- GHC.fWarningFlags],
+	[option | (option, _, _) <- GHC.fLangFlags]]
+#endif
+
+languages :: [String]
+languages = GHC.supportedLanguagesAndExtensions
+
+unqualStyle :: GHC.DynFlags -> PprStyle
+#if __GLASGOW_HASKELL__ == 802
+unqualStyle df = mkUserStyle df neverQualify AllTheWay
+#else
+unqualStyle _ = mkUserStyle neverQualify AllTheWay
+#endif
+
+#if __GLASGOW_HASKELL__ == 802
+exposedModuleName :: (a, Maybe b) -> a
+exposedModuleName = fst
+#else
+exposedModuleName :: GHC.ExposedModule unit mname -> mname
+exposedModuleName = GHC.exposedName
+#endif
diff --git a/src/HsDev/Tools/Ghc/MGhc.hs b/src/HsDev/Tools/Ghc/MGhc.hs
--- a/src/HsDev/Tools/Ghc/MGhc.hs
+++ b/src/HsDev/Tools/Ghc/MGhc.hs
@@ -1,204 +1,204 @@
-{-# LANGUAGE UnicodeSyntax, GeneralizedNewtypeDeriving, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Tools.Ghc.MGhc (
-	SessionState(..), sessionActive, sessionMap,
-	MGhcT(..), runMGhcT, liftGhc,
-	hasSession, findSession, findSessionBy, saveSession,
-	initSession, newSession,
-	switchSession, switchSession_,
-	deleteSession, restoreSession, usingSession
-	) where
-
-import Control.Lens
-import Control.Monad.Morph
-import Control.Monad.Catch
-import Control.Monad.Reader
-import Control.Monad.State
-import Data.Default
-import Data.IORef
-import Data.List (find)
-import Data.Map (Map)
-import qualified Data.Map as M
-import Data.Maybe (fromMaybe, isJust)
-import System.Log.Simple.Monad (MonadLog(..))
-
-import DynFlags
-import Exception hiding (catch, mask, uninterruptibleMask, bracket)
-import GHC
-import GhcMonad
-import HscTypes
-import Outputable
-import SysTools
-
-data SessionState s = SessionState {
-	_sessionActive :: Maybe s,
-	_sessionMap :: Map s HscEnv }
-
-instance Default (SessionState s) where
-	def = SessionState Nothing M.empty
-
-sessionActive :: Lens' (SessionState s) (Maybe s)
-sessionActive = lens g s where
-	g = _sessionActive
-	s st nm = st { _sessionActive = nm }
-
-sessionMap :: Lens' (SessionState s) (Map s HscEnv)
-sessionMap = lens g s where
-	g = _sessionMap
-	s st m = st { _sessionMap = m }
-
-instance ExceptionMonad m => ExceptionMonad (StateT s m) where
-	gcatch act onError = StateT $ \st -> gcatch (runStateT act st) (\e -> runStateT (onError e) st)
-	gmask f = StateT $ gmask . f' where
-		f' st' act' = runStateT (f act) st' where
-			act st = StateT $ act' . runStateT st
-
-instance ExceptionMonad m => ExceptionMonad (ReaderT r m) where
-	gcatch act onError = ReaderT $ \v -> gcatch (runReaderT act v) (\e -> runReaderT (onError e) v)
-	gmask f = ReaderT $ gmask . f' where
-		f' v' act' = runReaderT (f act) v' where
-			act v = ReaderT $ act' . runReaderT v
-
--- | Multi-session ghc monad
-newtype MGhcT s m a = MGhcT { unMGhcT :: GhcT (ReaderT (Maybe FilePath) (StateT (SessionState s) m)) a }
-	deriving (Functor, Applicative, Monad, MonadIO, ExceptionMonad, HasDynFlags, GhcMonad, MonadState (SessionState s), MonadReader (Maybe FilePath), MonadThrow, MonadCatch, MonadMask, MonadLog)
-
-instance MonadTrans GhcT where
-	lift = liftGhcT
-
-instance MFunctor GhcT where
-	hoist fn = GhcT . (fn .) . unGhcT
-
-instance MonadState st m => MonadState st (GhcT m) where
-	get = lift get
-	put = lift . put
-	state = lift . state
-
-instance MonadReader r m => MonadReader r (GhcT m) where
-	ask = lift ask
-	local f act = GhcT $ local f . unGhcT act
-
-instance MonadThrow m => MonadThrow (GhcT m) where
-	throwM = lift . throwM
-
-instance MonadCatch m => MonadCatch (GhcT m) where
-	catch act onError = GhcT $ \sess -> catch (unGhcT act sess) (flip unGhcT sess . onError)
-
-instance MonadMask m => MonadMask (GhcT m) where
-	mask f = GhcT $ \s -> mask $ \g -> unGhcT (f $ q g) s where
-		q g' act = GhcT $ g' . unGhcT act
-	uninterruptibleMask f = GhcT $ \s -> uninterruptibleMask $ \g -> unGhcT (f $ q g) s where
-		q g' act = GhcT $ g' . unGhcT act
-
--- | Run multi-session ghc
-runMGhcT :: (MonadIO m, ExceptionMonad m, Ord s) => Maybe FilePath -> MGhcT s m a -> m a
-runMGhcT lib act = do
-	ref <- liftIO $ newIORef (panic "empty session")
-	let
-		session = Session ref
-	flip evalStateT def $ flip runReaderT lib $ flip unGhcT session $ unMGhcT $ act `gfinally` cleanup
-	where
-		cleanup :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m ()
-		cleanup = do
-			void saveSession
-			sessions <- gets (M.elems . view sessionMap)
-			liftIO $ mapM_ cleanupSession sessions
-			modify (set sessionMap M.empty)
-
--- | Lift `Ghc` monad onto `MGhc`
-liftGhc :: MonadIO m => Ghc a -> MGhcT s m a
-liftGhc (Ghc act) = MGhcT $ GhcT $ liftIO . act
-
--- | Does session exist
-hasSession :: (MonadIO m, Ord s) => s -> MGhcT s m Bool
-hasSession key = do
-	msess <- gets (preview (sessionMap . ix key))
-	return $ isJust msess
-
--- | Find session
-findSession :: (MonadIO m, Ord s) => s -> MGhcT s m (Maybe s)
-findSession key = do
-	mkeys <- gets (M.keys . view sessionMap)
-	return $ find (== key) mkeys
-
--- | Find session by
-findSessionBy :: MonadIO m => (s -> Bool) -> MGhcT s m [s]
-findSessionBy p = do
-	mkeys <- gets (M.keys . view sessionMap)
-	return $ filter p mkeys
-
--- | Save current session
-saveSession :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m (Maybe s)
-saveSession = do
-	key <- gets (view sessionActive)
-	case key of
-		Just key' -> do
-			sess <- getSession
-			modify (set (sessionMap . at key') (Just sess))
-		Nothing -> return ()
-	return key
-
--- | Initialize new session
-initSession :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m ()
-initSession = do
-	lib <- ask
-	initGhcMonad lib
-	void saveSession
-
-activateSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m (Maybe HscEnv)
-activateSession key = do
-	void saveSession
-	modify (set sessionActive $ Just key)
-	gets (view (sessionMap . at key))
-
--- | Create new named session, deleting existing session
-newSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m ()
-newSession key = do
-	msess <- activateSession key
-	maybe (return ()) (liftIO . cleanupSession) msess
-	initSession
-
--- | Switch to session, creating if not exist, returns True if session was created
-switchSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m Bool
-switchSession key = do
-	msess <- activateSession key
-	case msess of
-		Nothing -> initSession >> return True
-		Just sess -> setSession sess >> return False
-
--- | Switch to session, creating if not exist and initializing with passed function
-switchSession_ :: (MonadIO m, ExceptionMonad m, Ord s) => s -> Maybe (MGhcT s m ()) -> MGhcT s m ()
-switchSession_ key f = do
-	new <- switchSession key
-	when new $ fromMaybe (return ()) f
-
--- | Delete existing session
-deleteSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m ()
-deleteSession key = do
-	cur <- saveSession
-	when (cur == Just key) $
-		modify (set sessionActive Nothing)
-	msess <- gets (view (sessionMap . at key))
-	modify (set (sessionMap . at key) Nothing)
-	case msess of
-		Nothing -> return ()
-		Just sess -> liftIO $ cleanupSession sess
-
--- | Save and restore session
-restoreSession :: (MonadIO m, MonadMask m, ExceptionMonad m, Ord s) => MGhcT s m a -> MGhcT s m a
-restoreSession act = bracket saveSession (maybe (return ()) (void . switchSession)) $ const act
-
--- | Run action using session, restoring session back
-usingSession :: (MonadIO m, MonadMask m, ExceptionMonad m, Ord s) => s -> MGhcT s m a -> MGhcT s m a
-usingSession key act = restoreSession $ do
-	void $ switchSession key
-	act
-
--- | Cleanup session
-cleanupSession :: HscEnv -> IO ()
-cleanupSession env = do
-	cleanTempFiles df
-	cleanTempDirs df
-	where
-		df = hsc_dflags env
+{-# LANGUAGE UnicodeSyntax, GeneralizedNewtypeDeriving, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Tools.Ghc.MGhc (
+	SessionState(..), sessionActive, sessionMap,
+	MGhcT(..), runMGhcT, liftGhc,
+	hasSession, findSession, findSessionBy, saveSession,
+	initSession, newSession,
+	switchSession, switchSession_,
+	deleteSession, restoreSession, usingSession
+	) where
+
+import Control.Lens
+import Control.Monad.Morph
+import Control.Monad.Catch
+import Control.Monad.Reader
+import Control.Monad.State
+import Data.Default
+import Data.IORef
+import Data.List (find)
+import Data.Map (Map)
+import qualified Data.Map as M
+import Data.Maybe (fromMaybe, isJust)
+import System.Log.Simple.Monad (MonadLog(..))
+
+import DynFlags
+import Exception hiding (catch, mask, uninterruptibleMask, bracket)
+import GHC
+import GhcMonad
+import HscTypes
+import Outputable
+import SysTools
+
+data SessionState s = SessionState {
+	_sessionActive :: Maybe s,
+	_sessionMap :: Map s HscEnv }
+
+instance Default (SessionState s) where
+	def = SessionState Nothing M.empty
+
+sessionActive :: Lens' (SessionState s) (Maybe s)
+sessionActive = lens g s where
+	g = _sessionActive
+	s st nm = st { _sessionActive = nm }
+
+sessionMap :: Lens' (SessionState s) (Map s HscEnv)
+sessionMap = lens g s where
+	g = _sessionMap
+	s st m = st { _sessionMap = m }
+
+instance ExceptionMonad m => ExceptionMonad (StateT s m) where
+	gcatch act onError = StateT $ \st -> gcatch (runStateT act st) (\e -> runStateT (onError e) st)
+	gmask f = StateT $ gmask . f' where
+		f' st' act' = runStateT (f act) st' where
+			act st = StateT $ act' . runStateT st
+
+instance ExceptionMonad m => ExceptionMonad (ReaderT r m) where
+	gcatch act onError = ReaderT $ \v -> gcatch (runReaderT act v) (\e -> runReaderT (onError e) v)
+	gmask f = ReaderT $ gmask . f' where
+		f' v' act' = runReaderT (f act) v' where
+			act v = ReaderT $ act' . runReaderT v
+
+-- | Multi-session ghc monad
+newtype MGhcT s m a = MGhcT { unMGhcT :: GhcT (ReaderT (Maybe FilePath) (StateT (SessionState s) m)) a }
+	deriving (Functor, Applicative, Monad, MonadIO, ExceptionMonad, HasDynFlags, GhcMonad, MonadState (SessionState s), MonadReader (Maybe FilePath), MonadThrow, MonadCatch, MonadMask, MonadLog)
+
+instance MonadTrans GhcT where
+	lift = liftGhcT
+
+instance MFunctor GhcT where
+	hoist fn = GhcT . (fn .) . unGhcT
+
+instance MonadState st m => MonadState st (GhcT m) where
+	get = lift get
+	put = lift . put
+	state = lift . state
+
+instance MonadReader r m => MonadReader r (GhcT m) where
+	ask = lift ask
+	local f act = GhcT $ local f . unGhcT act
+
+instance MonadThrow m => MonadThrow (GhcT m) where
+	throwM = lift . throwM
+
+instance MonadCatch m => MonadCatch (GhcT m) where
+	catch act onError = GhcT $ \sess -> catch (unGhcT act sess) (flip unGhcT sess . onError)
+
+instance MonadMask m => MonadMask (GhcT m) where
+	mask f = GhcT $ \s -> mask $ \g -> unGhcT (f $ q g) s where
+		q g' act = GhcT $ g' . unGhcT act
+	uninterruptibleMask f = GhcT $ \s -> uninterruptibleMask $ \g -> unGhcT (f $ q g) s where
+		q g' act = GhcT $ g' . unGhcT act
+
+-- | Run multi-session ghc
+runMGhcT :: (MonadIO m, ExceptionMonad m, Ord s) => Maybe FilePath -> MGhcT s m a -> m a
+runMGhcT lib act = do
+	ref <- liftIO $ newIORef (panic "empty session")
+	let
+		session = Session ref
+	flip evalStateT def $ flip runReaderT lib $ flip unGhcT session $ unMGhcT $ act `gfinally` cleanup
+	where
+		cleanup :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m ()
+		cleanup = do
+			void saveSession
+			sessions <- gets (M.elems . view sessionMap)
+			liftIO $ mapM_ cleanupSession sessions
+			modify (set sessionMap M.empty)
+
+-- | Lift `Ghc` monad onto `MGhc`
+liftGhc :: MonadIO m => Ghc a -> MGhcT s m a
+liftGhc (Ghc act) = MGhcT $ GhcT $ liftIO . act
+
+-- | Does session exist
+hasSession :: (MonadIO m, Ord s) => s -> MGhcT s m Bool
+hasSession key = do
+	msess <- gets (preview (sessionMap . ix key))
+	return $ isJust msess
+
+-- | Find session
+findSession :: (MonadIO m, Ord s) => s -> MGhcT s m (Maybe s)
+findSession key = do
+	mkeys <- gets (M.keys . view sessionMap)
+	return $ find (== key) mkeys
+
+-- | Find session by
+findSessionBy :: MonadIO m => (s -> Bool) -> MGhcT s m [s]
+findSessionBy p = do
+	mkeys <- gets (M.keys . view sessionMap)
+	return $ filter p mkeys
+
+-- | Save current session
+saveSession :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m (Maybe s)
+saveSession = do
+	key <- gets (view sessionActive)
+	case key of
+		Just key' -> do
+			sess <- getSession
+			modify (set (sessionMap . at key') (Just sess))
+		Nothing -> return ()
+	return key
+
+-- | Initialize new session
+initSession :: (MonadIO m, ExceptionMonad m, Ord s) => MGhcT s m ()
+initSession = do
+	lib <- ask
+	initGhcMonad lib
+	void saveSession
+
+activateSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m (Maybe HscEnv)
+activateSession key = do
+	void saveSession
+	modify (set sessionActive $ Just key)
+	gets (view (sessionMap . at key))
+
+-- | Create new named session, deleting existing session
+newSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m ()
+newSession key = do
+	msess <- activateSession key
+	maybe (return ()) (liftIO . cleanupSession) msess
+	initSession
+
+-- | Switch to session, creating if not exist, returns True if session was created
+switchSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m Bool
+switchSession key = do
+	msess <- activateSession key
+	case msess of
+		Nothing -> initSession >> return True
+		Just sess -> setSession sess >> return False
+
+-- | Switch to session, creating if not exist and initializing with passed function
+switchSession_ :: (MonadIO m, ExceptionMonad m, Ord s) => s -> Maybe (MGhcT s m ()) -> MGhcT s m ()
+switchSession_ key f = do
+	new <- switchSession key
+	when new $ fromMaybe (return ()) f
+
+-- | Delete existing session
+deleteSession :: (MonadIO m, ExceptionMonad m, Ord s) => s -> MGhcT s m ()
+deleteSession key = do
+	cur <- saveSession
+	when (cur == Just key) $
+		modify (set sessionActive Nothing)
+	msess <- gets (view (sessionMap . at key))
+	modify (set (sessionMap . at key) Nothing)
+	case msess of
+		Nothing -> return ()
+		Just sess -> liftIO $ cleanupSession sess
+
+-- | Save and restore session
+restoreSession :: (MonadIO m, MonadMask m, ExceptionMonad m, Ord s) => MGhcT s m a -> MGhcT s m a
+restoreSession act = bracket saveSession (maybe (return ()) (void . switchSession)) $ const act
+
+-- | Run action using session, restoring session back
+usingSession :: (MonadIO m, MonadMask m, ExceptionMonad m, Ord s) => s -> MGhcT s m a -> MGhcT s m a
+usingSession key act = restoreSession $ do
+	void $ switchSession key
+	act
+
+-- | Cleanup session
+cleanupSession :: HscEnv -> IO ()
+cleanupSession env = do
+	cleanTempFiles df
+	cleanTempDirs df
+	where
+		df = hsc_dflags env
diff --git a/src/HsDev/Tools/Ghc/Session.hs b/src/HsDev/Tools/Ghc/Session.hs
--- a/src/HsDev/Tools/Ghc/Session.hs
+++ b/src/HsDev/Tools/Ghc/Session.hs
@@ -1,49 +1,49 @@
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Tools.Ghc.Session (
-	ghcSession, ghciSession, haddockSession, targetSession, interpretModule,
-
-	module HsDev.Tools.Ghc.Worker
-	) where
-
-import Control.Lens
-import Data.Text (unpack)
-import System.FilePath
-
-import Control.Concurrent.Worker
-import HsDev.Symbols.Types
-import HsDev.Sandbox (getModuleOpts)
-import HsDev.Tools.Ghc.Worker
-
-import qualified GHC
-
--- | Get ghc session
-ghcSession :: [String] -> GhcM ()
-ghcSession = workerSession . SessionGhc
-
--- | Get ghci session
-ghciSession :: GhcM ()
-ghciSession = workerSession SessionGhci
-
--- | Get haddock session with flags
-haddockSession :: [String] -> GhcM ()
-haddockSession opts = ghcSession ("-haddock" : opts)
-
--- | Session for module
-targetSession :: [String] -> Module -> GhcM ()
-targetSession opts m = do
-	opts' <- getModuleOpts opts m
-	ghcSession ("-Wall" : opts')
-
--- | Interpret file
-interpretModule :: Module -> Maybe String -> GhcM ()
-interpretModule m mcts = do
-	targetSession [] m
-	let
-		f = preview (moduleLocation . moduleFile) m
-	case f of
-		Nothing -> return ()
-		Just f' -> withCurrentDirectory (takeDirectory f') $ do
-			t <- makeTarget (takeFileName f') mcts
-			loadTargets [t]
-			GHC.setContext [GHC.IIModule $ GHC.mkModuleName $ unpack $ view moduleName m]
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Tools.Ghc.Session (
+	ghcSession, ghciSession, haddockSession, targetSession, interpretModule,
+
+	module HsDev.Tools.Ghc.Worker
+	) where
+
+import Control.Lens
+import Data.Text (unpack)
+import System.FilePath
+
+import Control.Concurrent.Worker
+import HsDev.Symbols.Types
+import HsDev.Sandbox (getModuleOpts)
+import HsDev.Tools.Ghc.Worker
+
+import qualified GHC
+
+-- | Get ghc session
+ghcSession :: [String] -> GhcM ()
+ghcSession = workerSession . SessionGhc
+
+-- | Get ghci session
+ghciSession :: GhcM ()
+ghciSession = workerSession SessionGhci
+
+-- | Get haddock session with flags
+haddockSession :: [String] -> GhcM ()
+haddockSession opts = ghcSession ("-haddock" : opts)
+
+-- | Session for module
+targetSession :: [String] -> Module -> GhcM ()
+targetSession opts m = do
+	opts' <- getModuleOpts opts m
+	ghcSession ("-Wall" : opts')
+
+-- | Interpret file
+interpretModule :: Module -> Maybe String -> GhcM ()
+interpretModule m mcts = do
+	targetSession [] m
+	let
+		f = preview (moduleLocation . moduleFile) m
+	case f of
+		Nothing -> return ()
+		Just f' -> withCurrentDirectory (takeDirectory f') $ do
+			t <- makeTarget (takeFileName f') mcts
+			loadTargets [t]
+			GHC.setContext [GHC.IIModule $ GHC.mkModuleName $ unpack $ view moduleName m]
diff --git a/src/HsDev/Tools/Ghc/Types.hs b/src/HsDev/Tools/Ghc/Types.hs
--- a/src/HsDev/Tools/Ghc/Types.hs
+++ b/src/HsDev/Tools/Ghc/Types.hs
@@ -1,145 +1,143 @@
-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, RankNTypes, TemplateHaskell, OverloadedStrings #-}
-
-module HsDev.Tools.Ghc.Types (
-	TypedExpr(..), typedExpr, typedType,
-	moduleTypes, fileTypes,
-	setModuleTypes, inferTypes
-	) where
-
-import Control.DeepSeq
-import Control.Lens (over, view, set, each, preview, makeLenses, _Just)
-import Control.Monad
-import Control.Monad.IO.Class
-import Data.Aeson
-import Data.Generics
-import Data.List (find)
-import Data.Maybe
-import Data.String (fromString)
-import System.Directory
-import System.FilePath
-import System.Log.Simple (MonadLog(..), scope)
-
-import GHC hiding (exprType, Module, moduleName)
-import GHC.SYB.Utils (everythingStaged, Stage(TypeChecker))
-import GhcPlugins (mkFunTys)
-import CoreUtils
-import Desugar (deSugarExpr)
-import TcHsSyn (hsPatType)
-import Outputable
-import PprTyThing
-import qualified Pretty
-
-import System.Directory.Paths (canonicalize)
-import HsDev.Error
-import HsDev.Symbols
-import HsDev.Tools.Ghc.Worker as Ghc
-import HsDev.Tools.Ghc.Compat
-import HsDev.Tools.Types
-import HsDev.Util
-
-class HasType a where
-	getType :: GhcMonad m => TypecheckedModule -> a -> m (Maybe (SrcSpan, Type))
-
-instance HasType (LHsExpr Id) where
-	getType _ e = do
-		env <- getSession
-		mbe <- liftIO $ liftM snd $ deSugarExpr env e
-		return $ do
-			ex <- mbe
-			return (getLoc e, exprType ex)
-
-instance HasType (LHsBind Id) where
-	getType _ (L spn FunBind { fun_matches = m}) = return $ Just (spn, typ) where
-		typ = mkFunTys (mg_arg_tys m) (mg_res_ty m)
-	getType _ _ = return Nothing
-
-instance HasType (LPat Id) where
-	getType _ (L spn pat) = return $ Just (spn, hsPatType pat)
-
-locatedTypes :: Typeable a => TypecheckedSource -> [Located a]
-locatedTypes = types' p where
-	types' :: Typeable r => (r -> Bool) -> GenericQ [r]
-	types' p' = everythingStaged TypeChecker (++) [] ([] `mkQ` (\x -> [x | p' x]))
-	p (L spn _) = isGoodSrcSpan spn
-
-moduleTypes :: GhcMonad m => FilePath -> m [(SrcSpan, Type)]
-moduleTypes fpath = do
-	fpath' <- liftIO $ canonicalize fpath
-	mg <- getModuleGraph
-	[m] <- liftIO $ flip filterM mg $ \m -> do
-		mfile <- traverse (liftIO . canonicalize) $ ml_hs_file (ms_location m)
-		return (Just fpath' == mfile)
-	p <- parseModule m
-	tm <- typecheckModule p
-	let
-		ts = tm_typechecked_source tm
-	liftM (catMaybes . concat) $ sequence [
-		mapM (getType tm) (locatedTypes ts :: [LHsBind Id]),
-		mapM (getType tm) (locatedTypes ts :: [LHsExpr Id]),
-		mapM (getType tm) (locatedTypes ts :: [LPat Id])]
-
-data TypedExpr = TypedExpr {
-	_typedExpr :: String,
-	_typedType :: String }
-		deriving (Eq, Ord, Read, Show)
-
-makeLenses ''TypedExpr
-
-instance NFData TypedExpr where
-	rnf (TypedExpr e t) = rnf e `seq` rnf t
-
-instance ToJSON TypedExpr where
-	toJSON (TypedExpr e t) = object [
-		"expr" .= e,
-		"type" .= t]
-
-instance FromJSON TypedExpr where
-	parseJSON = withObject "typed-expr" $ \v -> TypedExpr <$>
-		v .:: "expr" <*>
-		v .:: "type"
-
--- | Get all types in module
-fileTypes :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m [Note TypedExpr]
-fileTypes opts m msrc = scope "types" $ case view moduleLocation m of
-	FileModule file proj -> do
-		file' <- liftIO $ canonicalize file
-		cts <- maybe (liftIO $ readFileUtf8 file') return msrc
-		let
-			dir = fromMaybe
-				(sourceModuleRoot (view moduleName m) file') $
-				preview (_Just . projectPath) proj
-		dirExist <- liftIO $ doesDirectoryExist dir
-		withFlags $ (if dirExist then Ghc.withCurrentDirectory dir else id) $ do
-			addCmdOpts opts
-			target <- makeTarget (makeRelative dir file') msrc
-			loadTargets [target]
-			ts <- moduleTypes file'
-			df <- getSessionDynFlags
-			return $ map (setExpr cts . recalcTabs cts 8 . uncurry (toNote df)) ts
-	_ -> hsdevError $ ModuleNotSource (view moduleLocation m)
-	where
-		toNote :: DynFlags -> SrcSpan -> Type -> Note String
-		toNote df spn tp = Note {
-			_noteSource = noLocation,
-			_noteRegion = spanRegion spn,
-			_noteLevel = Nothing,
-			_note = showType df tp }
-		setExpr :: String -> Note String -> Note TypedExpr
-		setExpr cts n = over note (TypedExpr (regionStr (view noteRegion n) cts)) n
-		showType :: DynFlags -> Type -> String
-		showType df = renderStyle Pretty.OneLineMode 80 . withPprStyleDoc df unqualStyle . pprTypeForUser
-		unqualStyle :: PprStyle
-		unqualStyle = mkUserStyle neverQualify AllTheWay
-
--- | Set types to module
-setModuleTypes :: [Note TypedExpr] -> Module -> Module
-setModuleTypes ts = over (moduleDeclarations . each) setType where
-	setType :: Declaration -> Declaration
-	setType d = fromMaybe d $ do
-		pos <- view declarationPosition d
-		tnote <- find ((== pos) . view (noteRegion . regionFrom)) ts
-		return $ set (declaration . functionType) (Just $ fromString $ view (note . typedType) tnote) d
-
--- | Infer types in module
-inferTypes :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m Module
-inferTypes opts m msrc = scope "infer" $ liftM (`setModuleTypes` m) $ fileTypes opts m msrc
+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, RankNTypes, TemplateHaskell, OverloadedStrings #-}
+
+module HsDev.Tools.Ghc.Types (
+	TypedExpr(..), typedExpr, typedType,
+	moduleTypes, fileTypes,
+	setModuleTypes, inferTypes
+	) where
+
+import Control.DeepSeq
+import Control.Lens (over, view, set, each, preview, makeLenses, _Just)
+import Control.Monad
+import Control.Monad.IO.Class
+import Data.Aeson
+import Data.Generics
+import Data.List (find)
+import Data.Maybe
+import Data.String (fromString)
+import System.Directory
+import System.FilePath
+import System.Log.Simple (MonadLog(..), scope)
+
+import GHC hiding (exprType, Module, moduleName)
+import GHC.SYB.Utils (everythingStaged, Stage(TypeChecker))
+import GhcPlugins (mkFunTys)
+import CoreUtils
+import Desugar (deSugarExpr)
+import TcHsSyn (hsPatType)
+import Outputable
+import PprTyThing
+import qualified Pretty
+
+import System.Directory.Paths (canonicalize)
+import HsDev.Error
+import HsDev.Symbols
+import HsDev.Tools.Ghc.Worker as Ghc
+import HsDev.Tools.Ghc.Compat
+import HsDev.Tools.Types
+import HsDev.Util
+
+class HasType a where
+	getType :: GhcMonad m => TypecheckedModule -> a -> m (Maybe (SrcSpan, Type))
+
+instance HasType (LHsExpr Id) where
+	getType _ e = do
+		env <- getSession
+		mbe <- liftIO $ liftM snd $ deSugarExpr env e
+		return $ do
+			ex <- mbe
+			return (getLoc e, exprType ex)
+
+instance HasType (LHsBind Id) where
+	getType _ (L spn FunBind { fun_matches = m}) = return $ Just (spn, typ) where
+		typ = mkFunTys (mg_arg_tys m) (mg_res_ty m)
+	getType _ _ = return Nothing
+
+instance HasType (LPat Id) where
+	getType _ (L spn pat) = return $ Just (spn, hsPatType pat)
+
+locatedTypes :: Typeable a => TypecheckedSource -> [Located a]
+locatedTypes = types' p where
+	types' :: Typeable r => (r -> Bool) -> GenericQ [r]
+	types' p' = everythingStaged TypeChecker (++) [] ([] `mkQ` (\x -> [x | p' x]))
+	p (L spn _) = isGoodSrcSpan spn
+
+moduleTypes :: GhcMonad m => FilePath -> m [(SrcSpan, Type)]
+moduleTypes fpath = do
+	fpath' <- liftIO $ canonicalize fpath
+	mg <- getModuleGraph
+	[m] <- liftIO $ flip filterM mg $ \m -> do
+		mfile <- traverse (liftIO . canonicalize) $ ml_hs_file (ms_location m)
+		return (Just fpath' == mfile)
+	p <- parseModule m
+	tm <- typecheckModule p
+	let
+		ts = tm_typechecked_source tm
+	liftM (catMaybes . concat) $ sequence [
+		mapM (getType tm) (locatedTypes ts :: [LHsBind Id]),
+		mapM (getType tm) (locatedTypes ts :: [LHsExpr Id]),
+		mapM (getType tm) (locatedTypes ts :: [LPat Id])]
+
+data TypedExpr = TypedExpr {
+	_typedExpr :: String,
+	_typedType :: String }
+		deriving (Eq, Ord, Read, Show)
+
+makeLenses ''TypedExpr
+
+instance NFData TypedExpr where
+	rnf (TypedExpr e t) = rnf e `seq` rnf t
+
+instance ToJSON TypedExpr where
+	toJSON (TypedExpr e t) = object [
+		"expr" .= e,
+		"type" .= t]
+
+instance FromJSON TypedExpr where
+	parseJSON = withObject "typed-expr" $ \v -> TypedExpr <$>
+		v .:: "expr" <*>
+		v .:: "type"
+
+-- | Get all types in module
+fileTypes :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m [Note TypedExpr]
+fileTypes opts m msrc = scope "types" $ case view moduleLocation m of
+	FileModule file proj -> do
+		file' <- liftIO $ canonicalize file
+		cts <- maybe (liftIO $ readFileUtf8 file') return msrc
+		let
+			dir = fromMaybe
+				(sourceModuleRoot (view moduleName m) file') $
+				preview (_Just . projectPath) proj
+		dirExist <- liftIO $ doesDirectoryExist dir
+		withFlags $ (if dirExist then Ghc.withCurrentDirectory dir else id) $ do
+			addCmdOpts opts
+			target <- makeTarget (makeRelative dir file') msrc
+			loadTargets [target]
+			ts <- moduleTypes file'
+			df <- getSessionDynFlags
+			return $ map (setExpr cts . recalcTabs cts 8 . uncurry (toNote df)) ts
+	_ -> hsdevError $ ModuleNotSource (view moduleLocation m)
+	where
+		toNote :: DynFlags -> SrcSpan -> Type -> Note String
+		toNote df spn tp = Note {
+			_noteSource = noLocation,
+			_noteRegion = spanRegion spn,
+			_noteLevel = Nothing,
+			_note = showType df tp }
+		setExpr :: String -> Note String -> Note TypedExpr
+		setExpr cts n = over note (TypedExpr (regionStr (view noteRegion n) cts)) n
+		showType :: DynFlags -> Type -> String
+		showType df = renderStyle Pretty.OneLineMode 80 . withPprStyleDoc df (unqualStyle df) . pprTypeForUser
+
+-- | Set types to module
+setModuleTypes :: [Note TypedExpr] -> Module -> Module
+setModuleTypes ts = over (moduleDeclarations . each) setType where
+	setType :: Declaration -> Declaration
+	setType d = fromMaybe d $ do
+		pos <- view declarationPosition d
+		tnote <- find ((== pos) . view (noteRegion . regionFrom)) ts
+		return $ set (declaration . functionType) (Just $ fromString $ view (note . typedType) tnote) d
+
+-- | Infer types in module
+inferTypes :: (MonadLog m, GhcMonad m) => [String] -> Module -> Maybe String -> m Module
+inferTypes opts m msrc = scope "infer" $ liftM (`setModuleTypes` m) $ fileTypes opts m msrc
diff --git a/src/HsDev/Tools/Ghc/Worker.hs b/src/HsDev/Tools/Ghc/Worker.hs
--- a/src/HsDev/Tools/Ghc/Worker.hs
+++ b/src/HsDev/Tools/Ghc/Worker.hs
@@ -1,248 +1,249 @@
-{-# LANGUAGE PatternGuards, OverloadedStrings, FlexibleContexts #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Tools.Ghc.Worker (
-	-- * Workers
-	SessionTarget(..),
-	GhcM, GhcWorker, MGhcT(..), runGhcM,
-	ghcWorker,
-	workerSession,
-
-	-- * Initializers and actions
-	ghcRun,
-	withFlags, modifyFlags, addCmdOpts, setCmdOpts,
-	importModules, preludeModules,
-	evaluate,
-	clearTargets, makeTarget, loadTargets,
-	-- * Utils
-	listPackages, spanRegion,
-	withCurrentDirectory,
-	logToChan, logToNull,
-
-	Ghc,
-	LogT(..),
-
-	module HsDev.Tools.Ghc.MGhc,
-	module Control.Concurrent.Worker
-	) where
-
-import Control.Monad
-import Control.Monad.Except
-import Control.Monad.Reader
-import Control.Monad.Catch
-import Data.Dynamic
-import Data.List (intercalate)
-import Data.Maybe
-import Data.Time.Clock (getCurrentTime)
-import Data.Version (showVersion)
-import System.Directory (getCurrentDirectory, setCurrentDirectory)
-import qualified  System.Log.Simple as Log
-import System.Log.Simple.Monad (MonadLog(..), LogT(..), withLog)
-import Text.Read (readMaybe)
-import Text.Format hiding (withFlags)
-
-import Exception (ExceptionMonad(..))
-import GHC hiding (Warning, Module, moduleName, pkgDatabase)
-import GHC.Paths
-import Outputable
-import FastString (unpackFS)
-import Packages
-import StringBuffer
-
-import Control.Concurrent.FiniteChan
-import Control.Concurrent.Worker
-import System.Directory.Paths
-import HsDev.Symbols.Location (Position(..), Region(..), region, ModulePackage, ModuleLocation(..))
-import HsDev.Tools.Types
-import HsDev.Tools.Ghc.Compat
-import HsDev.Tools.Ghc.MGhc
-
-data SessionTarget =
-	SessionGhci |
-	SessionGhc [String]
-
-instance Show SessionTarget where
-	show SessionGhci = "ghci"
-	show (SessionGhc opts) = "ghc " ++ intercalate ", " opts
-
-instance Formattable SessionTarget
-
-instance Eq SessionTarget where
-	SessionGhci == SessionGhci = True
-	SessionGhc lopts == SessionGhc ropts = lopts == ropts
-	_ == _ = False
-
-instance Ord SessionTarget where
-	compare l r = compare (isGhci l) (isGhci r) where
-		isGhci SessionGhci = True
-		isGhci _ = False
-
-type GhcM a = MGhcT SessionTarget (LogT IO) a
-
-type GhcWorker = Worker (MGhcT SessionTarget (LogT IO))
-
-instance (Monad m, GhcMonad m) => GhcMonad (ReaderT r m) where
-	getSession = lift getSession
-	setSession = lift . setSession
-
-instance ExceptionMonad m => ExceptionMonad (LogT m) where
-	gcatch act onError = LogT $ gcatch (runLogT act) (runLogT . onError)
-	gmask f = LogT $ gmask f' where
-		f' g' = runLogT $ f (LogT . g' . runLogT)
-
-instance MonadThrow Ghc where
-	throwM = liftIO . throwM
-
-runGhcM :: MonadLog m => Maybe FilePath -> GhcM a -> m a
-runGhcM dir act = do
-	l <- Log.askLog
-	liftIO $ withLog l $ runMGhcT dir act
-
--- | Multi-session ghc worker
-ghcWorker :: MonadLog m => m GhcWorker
-ghcWorker = do
-	l <- Log.askLog
-	liftIO $ startWorker (withLog l . runGhcM (Just libdir)) id (Log.scope "ghc")
-
--- | Create session with options
-workerSession :: SessionTarget -> GhcM ()
-workerSession SessionGhci = do
-	Log.sendLog Log.Trace $ "session: {}" ~~ SessionGhci
-	switchSession_ SessionGhci $ Just $ ghcRun [] (importModules preludeModules)
-workerSession s@(SessionGhc opts) = do
-	ms <- findSessionBy isGhcSession
-	forM_ ms $ \s'@(SessionGhc opts') -> when (opts /= opts') $ do
-		Log.sendLog Log.Trace $ "killing session: {}" ~~ s'
-		deleteSession s'
-	Log.sendLog Log.Trace $ "session: {}" ~~ s
-	switchSession_ s $ Just $ ghcRun opts (return ())
-	where
-		isGhcSession (SessionGhc _) = True
-		isGhcSession _ = False
-
--- | Run ghc
-ghcRun :: GhcMonad m => [String] -> m a -> m a
-ghcRun opts f = do
-	fs <- getSessionDynFlags
-	cleanupHandler fs $ do
-		(fs', _, _) <- parseDynamicFlags fs (map noLoc opts)
-		let fs'' = fs' {
-			ghcMode = CompManager,
-			ghcLink = LinkInMemory,
-			hscTarget = HscInterpreted }
-			-- ghcLink = NoLink,
-			-- hscTarget = HscNothing }
-		void $ setSessionDynFlags fs''
-		modifyFlags $ setLogAction logToNull
-		f
-
--- | Alter @DynFlags@ temporary
-withFlags :: GhcMonad m => m a -> m a
-withFlags = gbracket getSessionDynFlags (\fs -> setSessionDynFlags fs >> return ()) . const
-
--- | Update @DynFlags@
-modifyFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()
-modifyFlags f = do
-	fs <- getSessionDynFlags
-	let
-		fs' = f fs
-	_ <- setSessionDynFlags fs'
-	_ <- liftIO $ initPackages fs'
-	return ()
-
--- | Add options without reinit session
-addCmdOpts :: (MonadLog m, GhcMonad m) => [String] -> m ()
-addCmdOpts opts = do
-	Log.sendLog Log.Trace $ "setting ghc options: {}" ~~ unwords opts
-	fs <- getSessionDynFlags
-	(fs', _, _) <- parseDynamicFlags fs (map noLoc opts)
-	let fs'' = fs' {
-		ghcMode = CompManager,
-		ghcLink = LinkInMemory,
-		hscTarget = HscInterpreted }
-		-- ghcLink = NoLink,
-		-- hscTarget = HscNothing }
-	void $ setSessionDynFlags fs''
-
--- | Set options after session reinit
-setCmdOpts :: (MonadLog m, GhcMonad m) => [String] -> m ()
-setCmdOpts opts = do
-	Log.sendLog Log.Trace $ "restarting ghc session with: {}" ~~ unwords opts
-	initGhcMonad (Just libdir)
-	addCmdOpts opts
-	modifyFlags $ setLogAction logToNull
-
--- | Import some modules
-importModules :: GhcMonad m => [String] -> m ()
-importModules mods = mapM parseImportDecl ["import " ++ m | m <- mods] >>= setContext . map IIDecl
-
--- | Default interpreter modules
-preludeModules :: [String]
-preludeModules = ["Prelude", "Data.List", "Control.Monad", "HsDev.Tools.Ghc.Prelude"]
-
--- | Evaluate expression
-evaluate :: GhcMonad m => String -> m String
-evaluate expr = liftM fromDynamic (dynCompileExpr $ "show ({})" ~~ expr) >>=
-	maybe (fail "evaluate fail") return
-
--- | Clear loaded targets
-clearTargets :: GhcMonad m => m ()
-clearTargets = loadTargets []
-
--- | Make target with its source code optional
-makeTarget :: GhcMonad m => String -> Maybe String -> m Target
-makeTarget name Nothing = guessTarget name Nothing
-makeTarget name (Just cts) = do
-	t <- guessTarget name Nothing
-	tm <- liftIO getCurrentTime
-	return t { targetContents = Just (stringToStringBuffer cts, tm) }
-
--- | Load all targets
-loadTargets :: GhcMonad m => [Target] -> m ()
-loadTargets ts = setTargets ts >> load LoadAllTargets >> return ()
-
--- | Get list of installed packages
-listPackages :: GhcMonad m => m [ModulePackage]
-listPackages = liftM (mapMaybe readPackage . fromMaybe [] . pkgDatabase) getSessionDynFlags
-
-readPackage :: PackageConfig -> Maybe ModulePackage
-readPackage pc = readMaybe $ packageNameString pc ++ "-" ++ showVersion (packageVersion pc)
-
--- | Get region of @SrcSpan@
-spanRegion :: SrcSpan -> Region
-spanRegion (RealSrcSpan s) = Position (srcSpanStartLine s) (srcSpanStartCol s) `region` Position (srcSpanEndLine s) (srcSpanEndCol s)
-spanRegion _ = Position 0 0 `region` Position 0 0
-
--- | Set current directory and restore it after action
-withCurrentDirectory :: GhcMonad m => FilePath -> m a -> m a
-withCurrentDirectory dir act = gbracket (liftIO getCurrentDirectory) (liftIO . setCurrentDirectory) $
-	const (liftIO (setCurrentDirectory dir) >> act)
-
--- | Log  ghc warnings and errors as to chan
--- You may have to apply recalcTabs on result notes
-logToChan :: Chan (Note OutputMessage) -> LogAction
-logToChan ch fs sev src msg
-	| Just sev' <- checkSev sev = do
-		src' <- canonicalize srcMod
-		putChan ch $ Note {
-			_noteSource = src',
-			_noteRegion = spanRegion src,
-			_noteLevel = Just sev',
-			_note = OutputMessage {
-				_message = showSDoc fs msg,
-				_messageSuggestion = Nothing } }
-	| otherwise = return ()
-	where
-		checkSev SevWarning = Just Warning
-		checkSev SevError = Just Error
-		checkSev SevFatal = Just Error
-		checkSev _ = Nothing
-		srcMod = case src of
-			RealSrcSpan s' -> FileModule (unpackFS $ srcSpanFile s') Nothing
-			_ -> ModuleSource Nothing
-
--- | Don't log ghc warnings and errors
-logToNull :: LogAction
-logToNull _ _ _ _ = return ()
-
--- TODO: Load target by @ModuleLocation@, which may cause updating @DynFlags@
+{-# LANGUAGE PatternGuards, OverloadedStrings, FlexibleContexts #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Tools.Ghc.Worker (
+	-- * Workers
+	SessionTarget(..),
+	GhcM, GhcWorker, MGhcT(..), runGhcM,
+	ghcWorker,
+	workerSession,
+
+	-- * Initializers and actions
+	ghcRun,
+	withFlags, modifyFlags, addCmdOpts, setCmdOpts,
+	importModules, preludeModules,
+	evaluate,
+	clearTargets, makeTarget, loadTargets,
+	-- * Utils
+	listPackages, spanRegion,
+	withCurrentDirectory,
+	logToChan, logToNull,
+
+	Ghc,
+	LogT(..),
+
+	module HsDev.Tools.Ghc.MGhc,
+	module Control.Concurrent.Worker
+	) where
+
+import Control.Monad
+import Control.Monad.Except
+import Control.Monad.Reader
+import Control.Monad.Catch
+import Data.Dynamic
+import Data.List (intercalate)
+import Data.Maybe
+import Data.Time.Clock (getCurrentTime)
+import Data.Version (showVersion)
+import System.Directory (getCurrentDirectory, setCurrentDirectory)
+import qualified  System.Log.Simple as Log
+import System.Log.Simple.Monad (MonadLog(..), LogT(..), withLog)
+import Text.Read (readMaybe)
+import Text.Format hiding (withFlags)
+
+import Exception (ExceptionMonad(..))
+import GHC hiding (Warning, Module, moduleName, pkgDatabase)
+import GHC.Paths
+import Outputable
+import FastString (unpackFS)
+import Packages
+import StringBuffer
+
+import Control.Concurrent.FiniteChan
+import Control.Concurrent.Worker
+import System.Directory.Paths
+import HsDev.Symbols.Location (Position(..), Region(..), region, ModulePackage, ModuleLocation(..))
+import HsDev.Tools.Types
+import HsDev.Tools.Ghc.Compat hiding (setLogAction)
+import qualified HsDev.Tools.Ghc.Compat as C (setLogAction)
+import HsDev.Tools.Ghc.MGhc
+
+data SessionTarget =
+	SessionGhci |
+	SessionGhc [String]
+
+instance Show SessionTarget where
+	show SessionGhci = "ghci"
+	show (SessionGhc opts) = "ghc " ++ intercalate ", " opts
+
+instance Formattable SessionTarget
+
+instance Eq SessionTarget where
+	SessionGhci == SessionGhci = True
+	SessionGhc lopts == SessionGhc ropts = lopts == ropts
+	_ == _ = False
+
+instance Ord SessionTarget where
+	compare l r = compare (isGhci l) (isGhci r) where
+		isGhci SessionGhci = True
+		isGhci _ = False
+
+type GhcM a = MGhcT SessionTarget (LogT IO) a
+
+type GhcWorker = Worker (MGhcT SessionTarget (LogT IO))
+
+instance (Monad m, GhcMonad m) => GhcMonad (ReaderT r m) where
+	getSession = lift getSession
+	setSession = lift . setSession
+
+instance ExceptionMonad m => ExceptionMonad (LogT m) where
+	gcatch act onError = LogT $ gcatch (runLogT act) (runLogT . onError)
+	gmask f = LogT $ gmask f' where
+		f' g' = runLogT $ f (LogT . g' . runLogT)
+
+instance MonadThrow Ghc where
+	throwM = liftIO . throwM
+
+runGhcM :: MonadLog m => Maybe FilePath -> GhcM a -> m a
+runGhcM dir act = do
+	l <- Log.askLog
+	liftIO $ withLog l $ runMGhcT dir act
+
+-- | Multi-session ghc worker
+ghcWorker :: MonadLog m => m GhcWorker
+ghcWorker = do
+	l <- Log.askLog
+	liftIO $ startWorker (withLog l . runGhcM (Just libdir)) id (Log.scope "ghc")
+
+-- | Create session with options
+workerSession :: SessionTarget -> GhcM ()
+workerSession SessionGhci = do
+	Log.sendLog Log.Trace $ "session: {}" ~~ SessionGhci
+	switchSession_ SessionGhci $ Just $ ghcRun [] (importModules preludeModules)
+workerSession s@(SessionGhc opts) = do
+	ms <- findSessionBy isGhcSession
+	forM_ ms $ \s'@(SessionGhc opts') -> when (opts /= opts') $ do
+		Log.sendLog Log.Trace $ "killing session: {}" ~~ s'
+		deleteSession s'
+	Log.sendLog Log.Trace $ "session: {}" ~~ s
+	switchSession_ s $ Just $ ghcRun opts (return ())
+	where
+		isGhcSession (SessionGhc _) = True
+		isGhcSession _ = False
+
+-- | Run ghc
+ghcRun :: GhcMonad m => [String] -> m a -> m a
+ghcRun opts f = do
+	fs <- getSessionDynFlags
+	cleanupHandler fs $ do
+		(fs', _, _) <- parseDynamicFlags fs (map noLoc opts)
+		let fs'' = fs' {
+			ghcMode = CompManager,
+			ghcLink = LinkInMemory,
+			hscTarget = HscInterpreted }
+			-- ghcLink = NoLink,
+			-- hscTarget = HscNothing }
+		void $ setSessionDynFlags fs''
+		modifyFlags $ C.setLogAction logToNull
+		f
+
+-- | Alter @DynFlags@ temporary
+withFlags :: GhcMonad m => m a -> m a
+withFlags = gbracket getSessionDynFlags (\fs -> setSessionDynFlags fs >> return ()) . const
+
+-- | Update @DynFlags@
+modifyFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()
+modifyFlags f = do
+	fs <- getSessionDynFlags
+	let
+		fs' = f fs
+	_ <- setSessionDynFlags fs'
+	_ <- liftIO $ initPackages fs'
+	return ()
+
+-- | Add options without reinit session
+addCmdOpts :: (MonadLog m, GhcMonad m) => [String] -> m ()
+addCmdOpts opts = do
+	Log.sendLog Log.Trace $ "setting ghc options: {}" ~~ unwords opts
+	fs <- getSessionDynFlags
+	(fs', _, _) <- parseDynamicFlags fs (map noLoc opts)
+	let fs'' = fs' {
+		ghcMode = CompManager,
+		ghcLink = LinkInMemory,
+		hscTarget = HscInterpreted }
+		-- ghcLink = NoLink,
+		-- hscTarget = HscNothing }
+	void $ setSessionDynFlags fs''
+
+-- | Set options after session reinit
+setCmdOpts :: (MonadLog m, GhcMonad m) => [String] -> m ()
+setCmdOpts opts = do
+	Log.sendLog Log.Trace $ "restarting ghc session with: {}" ~~ unwords opts
+	initGhcMonad (Just libdir)
+	addCmdOpts opts
+	modifyFlags $ C.setLogAction logToNull
+
+-- | Import some modules
+importModules :: GhcMonad m => [String] -> m ()
+importModules mods = mapM parseImportDecl ["import " ++ m | m <- mods] >>= setContext . map IIDecl
+
+-- | Default interpreter modules
+preludeModules :: [String]
+preludeModules = ["Prelude", "Data.List", "Control.Monad", "HsDev.Tools.Ghc.Prelude"]
+
+-- | Evaluate expression
+evaluate :: GhcMonad m => String -> m String
+evaluate expr = liftM fromDynamic (dynCompileExpr $ "show ({})" ~~ expr) >>=
+	maybe (fail "evaluate fail") return
+
+-- | Clear loaded targets
+clearTargets :: GhcMonad m => m ()
+clearTargets = loadTargets []
+
+-- | Make target with its source code optional
+makeTarget :: GhcMonad m => String -> Maybe String -> m Target
+makeTarget name Nothing = guessTarget name Nothing
+makeTarget name (Just cts) = do
+	t <- guessTarget name Nothing
+	tm <- liftIO getCurrentTime
+	return t { targetContents = Just (stringToStringBuffer cts, tm) }
+
+-- | Load all targets
+loadTargets :: GhcMonad m => [Target] -> m ()
+loadTargets ts = setTargets ts >> load LoadAllTargets >> return ()
+
+-- | Get list of installed packages
+listPackages :: GhcMonad m => m [ModulePackage]
+listPackages = liftM (mapMaybe readPackage . fromMaybe [] . pkgDatabase) getSessionDynFlags
+
+readPackage :: PackageConfig -> Maybe ModulePackage
+readPackage pc = readMaybe $ packageNameString pc ++ "-" ++ showVersion (packageVersion pc)
+
+-- | Get region of @SrcSpan@
+spanRegion :: SrcSpan -> Region
+spanRegion (RealSrcSpan s) = Position (srcSpanStartLine s) (srcSpanStartCol s) `region` Position (srcSpanEndLine s) (srcSpanEndCol s)
+spanRegion _ = Position 0 0 `region` Position 0 0
+
+-- | Set current directory and restore it after action
+withCurrentDirectory :: GhcMonad m => FilePath -> m a -> m a
+withCurrentDirectory dir act = gbracket (liftIO getCurrentDirectory) (liftIO . setCurrentDirectory) $
+	const (liftIO (setCurrentDirectory dir) >> act)
+
+-- | Log  ghc warnings and errors as to chan
+-- You may have to apply recalcTabs on result notes
+logToChan :: Chan (Note OutputMessage) -> LogAction
+logToChan ch fs sev src msg
+	| Just sev' <- checkSev sev = do
+		src' <- canonicalize srcMod
+		putChan ch $ Note {
+			_noteSource = src',
+			_noteRegion = spanRegion src,
+			_noteLevel = Just sev',
+			_note = OutputMessage {
+				_message = showSDoc fs msg,
+				_messageSuggestion = Nothing } }
+	| otherwise = return ()
+	where
+		checkSev SevWarning = Just Warning
+		checkSev SevError = Just Error
+		checkSev SevFatal = Just Error
+		checkSev _ = Nothing
+		srcMod = case src of
+			RealSrcSpan s' -> FileModule (unpackFS $ srcSpanFile s') Nothing
+			_ -> ModuleSource Nothing
+
+-- | Don't log ghc warnings and errors
+logToNull :: LogAction
+logToNull _ _ _ _ = return ()
+
+-- TODO: Load target by @ModuleLocation@, which may cause updating @DynFlags@
diff --git a/src/HsDev/Tools/HDocs.hs b/src/HsDev/Tools/HDocs.hs
--- a/src/HsDev/Tools/HDocs.hs
+++ b/src/HsDev/Tools/HDocs.hs
@@ -1,60 +1,60 @@
-module HsDev.Tools.HDocs (
-	hdocsy, hdocs, hdocsCabal,
-	setDocs,
-	loadDocs,
-
-	hdocsProcess,
-
-	module Control.Monad.Except
-	) where
-
-import Control.DeepSeq
-import Control.Lens (set, view, over)
-import Control.Monad ()
-import Control.Monad.Except
-
-import Data.Aeson (decode)
-import qualified Data.ByteString.Lazy.Char8 as L (pack)
-import Data.Map (Map)
-import qualified Data.Map as M
-import Data.String (fromString)
-
-import qualified HDocs.Module as HDocs
-import qualified HDocs.Haddock as HDocs (readSource, readSources_)
-
-import HsDev.Tools.Base
-import HsDev.Symbols
-
--- | Get docs for modules
-hdocsy :: [ModuleLocation] -> [String] -> IO [Map String String]
-hdocsy mlocs opts = runExceptT (docs' mlocs) >>= return . either (const $ replicate (length mlocs) M.empty) (map $ force . HDocs.formatDocs) where
-	docs' :: [ModuleLocation] -> ExceptT String IO [HDocs.ModuleDocMap]
-	docs' ms = liftM (map snd) $ HDocs.readSources_ opts $ map (view moduleFile) ms
-
--- | Get docs for module
-hdocs :: ModuleLocation -> [String] -> IO (Map String String)
-hdocs mloc opts = runExceptT (docs' mloc) >>= return . either (const M.empty) (force . HDocs.formatDocs) where
-	docs' :: ModuleLocation -> ExceptT String IO HDocs.ModuleDocMap
-	docs' (FileModule fpath _) = liftM snd $ HDocs.readSource opts fpath
-	docs' (InstalledModule _ _ mname) = HDocs.moduleDocs opts mname
-	docs' _ = throwError $ "Can't get docs for: " ++ show mloc
-
--- | Get all docs
-hdocsCabal :: PackageDbStack -> [String] -> ExceptT String IO (Map String (Map String String))
-hdocsCabal pdbs opts = liftM (M.map $ force . HDocs.formatDocs) $ HDocs.installedDocs (packageDbStackOpts pdbs ++ opts)
-
--- | Set docs for module
-setDocs :: Map String String -> Module -> Module
-setDocs d = over moduleDeclarations (map setDoc) where
-	setDoc decl' = set declarationDocs (M.lookup (view declarationName decl') d') decl'
-	d' = M.mapKeys fromString . M.map fromString $ d
-
--- | Load docs for module
-loadDocs :: [String] -> Module -> IO Module
-loadDocs opts m = do
-	d <- hdocs (view moduleLocation m) opts
-	return $ setDocs d m
-
-hdocsProcess :: String -> [String] -> IO (Maybe (Map String String))
-hdocsProcess mname opts = liftM (decode . L.pack . last . lines) $ runTool_ "hdocs" opts' where
-	opts' = mname : concat [["-g", opt] | opt <- opts]
+module HsDev.Tools.HDocs (
+	hdocsy, hdocs, hdocsCabal,
+	setDocs,
+	loadDocs,
+
+	hdocsProcess,
+
+	module Control.Monad.Except
+	) where
+
+import Control.DeepSeq
+import Control.Lens (set, view, over)
+import Control.Monad ()
+import Control.Monad.Except
+
+import Data.Aeson (decode)
+import qualified Data.ByteString.Lazy.Char8 as L (pack)
+import Data.Map (Map)
+import qualified Data.Map as M
+import Data.String (fromString)
+
+import qualified HDocs.Module as HDocs
+import qualified HDocs.Haddock as HDocs (readSource, readSources_)
+
+import HsDev.Tools.Base
+import HsDev.Symbols
+
+-- | Get docs for modules
+hdocsy :: [ModuleLocation] -> [String] -> IO [Map String String]
+hdocsy mlocs opts = runExceptT (docs' mlocs) >>= return . either (const $ replicate (length mlocs) M.empty) (map $ force . HDocs.formatDocs) where
+	docs' :: [ModuleLocation] -> ExceptT String IO [HDocs.ModuleDocMap]
+	docs' ms = liftM (map snd) $ HDocs.readSources_ opts $ map (view moduleFile) ms
+
+-- | Get docs for module
+hdocs :: ModuleLocation -> [String] -> IO (Map String String)
+hdocs mloc opts = runExceptT (docs' mloc) >>= return . either (const M.empty) (force . HDocs.formatDocs) where
+	docs' :: ModuleLocation -> ExceptT String IO HDocs.ModuleDocMap
+	docs' (FileModule fpath _) = liftM snd $ HDocs.readSource opts fpath
+	docs' (InstalledModule _ _ mname) = HDocs.moduleDocs opts mname
+	docs' _ = throwError $ "Can't get docs for: " ++ show mloc
+
+-- | Get all docs
+hdocsCabal :: PackageDbStack -> [String] -> ExceptT String IO (Map String (Map String String))
+hdocsCabal pdbs opts = liftM (M.map $ force . HDocs.formatDocs) $ HDocs.installedDocs (packageDbStackOpts pdbs ++ opts)
+
+-- | Set docs for module
+setDocs :: Map String String -> Module -> Module
+setDocs d = over moduleDeclarations (map setDoc) where
+	setDoc decl' = set declarationDocs (M.lookup (view declarationName decl') d') decl'
+	d' = M.mapKeys fromString . M.map fromString $ d
+
+-- | Load docs for module
+loadDocs :: [String] -> Module -> IO Module
+loadDocs opts m = do
+	d <- hdocs (view moduleLocation m) opts
+	return $ setDocs d m
+
+hdocsProcess :: String -> [String] -> IO (Maybe (Map String String))
+hdocsProcess mname opts = liftM (decode . L.pack . last . lines) $ runTool_ "hdocs" opts' where
+	opts' = mname : concat [["-g", opt] | opt <- opts]
diff --git a/src/HsDev/Tools/HLint.hs b/src/HsDev/Tools/HLint.hs
--- a/src/HsDev/Tools/HLint.hs
+++ b/src/HsDev/Tools/HLint.hs
@@ -1,97 +1,97 @@
-module HsDev.Tools.HLint (
-	hlint, hlintFile, hlintSource,
-
-	module Control.Monad.Except
-	) where
-
-import Control.Arrow
-import Control.Lens (over, view, _Just)
-import Control.Monad.Except
-import Data.Char
-import Data.List
-import Data.Maybe (mapMaybe)
-import Data.Ord
-import Language.Haskell.HLint3 (autoSettings, parseModuleEx, applyHints, Idea(..), parseErrorMessage, ParseFlags(..), CppFlags(..))
-import Language.Haskell.Exts.SrcLoc
-import qualified Language.Haskell.HLint3 as HL (Severity(..))
-
-import System.Directory.Paths (canonicalize)
-import HsDev.Symbols.Location
-import HsDev.Tools.Base
-import HsDev.Util (readFileUtf8, split)
-
-hlint :: FilePath -> Maybe String -> ExceptT String IO [Note OutputMessage]
-hlint file msrc = do
-	file' <- liftIO $ canonicalize file
-	cts <- maybe (liftIO $ readFileUtf8 file') return msrc
-	(flags, classify, hint) <- liftIO autoSettings
-	p <- liftIO $ parseModuleEx (flags { cppFlags = CppSimple }) file' (Just cts)
-	m <- either (throwError . parseErrorMessage) return p
-	return $ map (recalcTabs cts 8 . indentIdea cts . fromIdea) $ applyHints classify hint [m]
-
-hlintFile :: FilePath -> ExceptT String IO [Note OutputMessage]
-hlintFile f = hlint f Nothing
-
-hlintSource :: FilePath -> String -> ExceptT String IO [Note OutputMessage]
-hlintSource f = hlint f . Just
-
-fromIdea :: Idea -> Note OutputMessage
-fromIdea idea = Note {
-	_noteSource = FileModule (srcSpanFilename src) Nothing,
-	_noteRegion = Region (Position (srcSpanStartLine src) (srcSpanStartColumn src)) (Position (srcSpanEndLine src) (srcSpanEndColumn src)),
-	_noteLevel = Just $ case ideaSeverity idea of
-		HL.Warning -> Warning
-		HL.Error -> Error
-		_ -> Hint,
-	_note = OutputMessage {
-		_message = ideaHint idea,
-		_messageSuggestion = ideaTo idea } }
-	where
-		src = ideaSpan idea
-
-indentIdea :: String -> Note OutputMessage -> Note OutputMessage
-indentIdea cts idea = case analyzeIndent cts of
-	Nothing -> idea
-	Just i -> over (note . messageSuggestion . _Just) (indent' i) idea
-	where
-		indent' i' = intercalate "\n" . indentTail . map (uncurry (++) . first (concat . (`replicate` i') . (`div` 2) . length) . span isSpace) . split (== '\n')
-		indentTail [] = []
-		indentTail (h : hs) = h : map (firstIndent ++) hs
-		firstIndent = takeWhile isSpace firstLine
-		firstLine = regionStr (Position firstLineNum 1 `region` Position (succ firstLineNum) 1) cts
-		firstLineNum = view (noteRegion . regionFrom . positionLine) idea
-
--- | Indent in source
-data Indent = Spaces Int | Tabs deriving (Eq, Ord)
-
-instance Show Indent where
-	show (Spaces n) = replicate n ' '
-	show Tabs = "\t"
-
--- | Analyze source indentation to convert suggestion to same indentation
--- Returns one indent
-analyzeIndent :: String -> Maybe String
-analyzeIndent =
-	fmap show . selectIndent . map fst . dropUnusual .
-	sortBy (comparing $ negate . snd) .
-	map (head &&& length) .
-	group . sort .
-	mapMaybe (guessIndent . takeWhile isSpace) . lines
-	where
-		selectIndent :: [Indent] -> Maybe Indent
-		selectIndent [] = Nothing
-		selectIndent (Tabs : _) = Just Tabs
-		selectIndent indents = Just $ Spaces $ foldr1 gcd $ mapMaybe spaces indents where
-			spaces :: Indent -> Maybe Int
-			spaces Tabs = Nothing
-			spaces (Spaces n) = Just n
-		dropUnusual :: [(Indent, Int)] -> [(Indent, Int)]
-		dropUnusual [] = []
-		dropUnusual is@((_, freq):_) = takeWhile ((> freq `div` 5) . snd) is
-
--- | Guess indent of one line
-guessIndent :: String -> Maybe Indent
-guessIndent s
-	| all (== ' ') s = Just $ Spaces $ length s
-	| all (== '\t') s = Just Tabs
-	| otherwise = Nothing
+module HsDev.Tools.HLint (
+	hlint, hlintFile, hlintSource,
+
+	module Control.Monad.Except
+	) where
+
+import Control.Arrow
+import Control.Lens (over, view, _Just)
+import Control.Monad.Except
+import Data.Char
+import Data.List
+import Data.Maybe (mapMaybe)
+import Data.Ord
+import Language.Haskell.HLint3 (autoSettings, parseModuleEx, applyHints, Idea(..), parseErrorMessage, ParseFlags(..), CppFlags(..))
+import Language.Haskell.Exts.SrcLoc
+import qualified Language.Haskell.HLint3 as HL (Severity(..))
+
+import System.Directory.Paths (canonicalize)
+import HsDev.Symbols.Location
+import HsDev.Tools.Base
+import HsDev.Util (readFileUtf8, split)
+
+hlint :: FilePath -> Maybe String -> ExceptT String IO [Note OutputMessage]
+hlint file msrc = do
+	file' <- liftIO $ canonicalize file
+	cts <- maybe (liftIO $ readFileUtf8 file') return msrc
+	(flags, classify, hint) <- liftIO autoSettings
+	p <- liftIO $ parseModuleEx (flags { cppFlags = CppSimple }) file' (Just cts)
+	m <- either (throwError . parseErrorMessage) return p
+	return $ map (recalcTabs cts 8 . indentIdea cts . fromIdea) $ applyHints classify hint [m]
+
+hlintFile :: FilePath -> ExceptT String IO [Note OutputMessage]
+hlintFile f = hlint f Nothing
+
+hlintSource :: FilePath -> String -> ExceptT String IO [Note OutputMessage]
+hlintSource f = hlint f . Just
+
+fromIdea :: Idea -> Note OutputMessage
+fromIdea idea = Note {
+	_noteSource = FileModule (srcSpanFilename src) Nothing,
+	_noteRegion = Region (Position (srcSpanStartLine src) (srcSpanStartColumn src)) (Position (srcSpanEndLine src) (srcSpanEndColumn src)),
+	_noteLevel = Just $ case ideaSeverity idea of
+		HL.Warning -> Warning
+		HL.Error -> Error
+		_ -> Hint,
+	_note = OutputMessage {
+		_message = ideaHint idea,
+		_messageSuggestion = ideaTo idea } }
+	where
+		src = ideaSpan idea
+
+indentIdea :: String -> Note OutputMessage -> Note OutputMessage
+indentIdea cts idea = case analyzeIndent cts of
+	Nothing -> idea
+	Just i -> over (note . messageSuggestion . _Just) (indent' i) idea
+	where
+		indent' i' = intercalate "\n" . indentTail . map (uncurry (++) . first (concat . (`replicate` i') . (`div` 2) . length) . span isSpace) . split (== '\n')
+		indentTail [] = []
+		indentTail (h : hs) = h : map (firstIndent ++) hs
+		firstIndent = takeWhile isSpace firstLine
+		firstLine = regionStr (Position firstLineNum 1 `region` Position (succ firstLineNum) 1) cts
+		firstLineNum = view (noteRegion . regionFrom . positionLine) idea
+
+-- | Indent in source
+data Indent = Spaces Int | Tabs deriving (Eq, Ord)
+
+instance Show Indent where
+	show (Spaces n) = replicate n ' '
+	show Tabs = "\t"
+
+-- | Analyze source indentation to convert suggestion to same indentation
+-- Returns one indent
+analyzeIndent :: String -> Maybe String
+analyzeIndent =
+	fmap show . selectIndent . map fst . dropUnusual .
+	sortBy (comparing $ negate . snd) .
+	map (head &&& length) .
+	group . sort .
+	mapMaybe (guessIndent . takeWhile isSpace) . lines
+	where
+		selectIndent :: [Indent] -> Maybe Indent
+		selectIndent [] = Nothing
+		selectIndent (Tabs : _) = Just Tabs
+		selectIndent indents = Just $ Spaces $ foldr1 gcd $ mapMaybe spaces indents where
+			spaces :: Indent -> Maybe Int
+			spaces Tabs = Nothing
+			spaces (Spaces n) = Just n
+		dropUnusual :: [(Indent, Int)] -> [(Indent, Int)]
+		dropUnusual [] = []
+		dropUnusual is@((_, freq):_) = takeWhile ((> freq `div` 5) . snd) is
+
+-- | Guess indent of one line
+guessIndent :: String -> Maybe Indent
+guessIndent s
+	| all (== ' ') s = Just $ Spaces $ length s
+	| all (== '\t') s = Just Tabs
+	| otherwise = Nothing
diff --git a/src/HsDev/Tools/Hayoo.hs b/src/HsDev/Tools/Hayoo.hs
--- a/src/HsDev/Tools/Hayoo.hs
+++ b/src/HsDev/Tools/Hayoo.hs
@@ -1,135 +1,135 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module HsDev.Tools.Hayoo (
-	-- * Types
-	HayooResult(..), HayooSymbol(..),
-	hayooAsDeclaration,
-	-- * Search help online
-	hayoo,
-	-- * Utils
-	untagDescription,
-
-	-- * Reexportss
-	module Control.Monad.Except
-	) where
-
-import Control.Arrow
-import Control.Applicative
-import Control.Monad.Except
-
-import Data.Aeson
-import qualified Data.ByteString.Lazy.Char8 as L
-import Data.Either
-import Network.HTTP
-import Data.String (fromString)
-
-import HsDev.Symbols
-import HsDev.Tools.Base (replaceRx)
-import HsDev.Util
-
--- | Hayoo response
-data HayooResult = HayooResult {
-	resultMax :: Int,
-	resultOffset :: Int,
-	resultCount :: Int,
-	resultResult :: [HayooSymbol] }
-		deriving (Eq, Ord, Read, Show)
-
--- | Hayoo symbol
-data HayooSymbol = HayooSymbol {
-	resultUri :: String,
-	tag :: String,
-	hayooPackage :: String,
-	hayooName :: String,
-	hayooSource :: String,
-	hayooDescription :: String,
-	hayooSignature :: String,
-	hayooModules :: [String],
-	hayooScore :: Double,
-	hayooType :: String }
-		deriving (Eq, Ord, Read, Show)
-
-newtype HayooValue = HayooValue { hayooValue :: Either Value HayooSymbol }
-
-instance FromJSON HayooResult where
-	parseJSON = withObject "hayoo response" $ \v -> HayooResult <$>
-		(v .:: "max") <*>
-		(v .:: "offset") <*>
-		(v .:: "count") <*>
-		((rights . map hayooValue) <$> (v .:: "result"))
-
-instance Symbol HayooSymbol where
-	symbolName = fromString . hayooName
-	symbolQualifiedName f = fromString $ case hayooModules f of
-		[] -> hayooName f
-		(m:_) -> m ++ "." ++ hayooName f
-	symbolDocs = Just . fromString . hayooDescription
-	symbolLocation r = Location (ModuleSource $ Just $ resultUri r) Nothing
-
-instance Documented HayooSymbol where
-	brief f
-		| hayooType f == "function" = hayooName f ++ " :: " ++ hayooSignature f
-		| otherwise = hayooType f ++ " " ++ hayooName f
-	detailed f = unlines $ defaultDetailed f ++ online where
-		online = [
-			"", "Hayoo online documentation", "",
-			"Package: " ++ hayooPackage f,
-			"Hackage URL: " ++ resultUri f]
-
-instance FromJSON HayooSymbol where
-	parseJSON = withObject "symbol" $ \v -> HayooSymbol <$>
-		(v .:: "resultUri") <*>
-		(v .:: "tag") <*>
-		(v .:: "resultPackage") <*>
-		(v .:: "resultName") <*>
-		(v .:: "resultSource") <*>
-		(v .:: "resultDescription") <*>
-		(v .:: "resultSignature") <*>
-		(v .:: "resultModules") <*>
-		(v .:: "resultScore") <*>
-		(v .:: "resultType")
-
-instance FromJSON HayooValue where
-	parseJSON v = HayooValue <$> ((Right <$> parseJSON v) <|> pure (Left v))
-
--- | 'HayooFunction' as 'Declaration'
-hayooAsDeclaration :: HayooSymbol -> Maybe ModuleDeclaration
-hayooAsDeclaration f
-	| hayooType f `elem` ["function", "type", "newtype", "data", "class"] = Just ModuleDeclaration {
-		_declarationModuleId = ModuleId {
-			_moduleIdName = fromString $ head $ hayooModules f,
-			_moduleIdLocation = ModuleSource (Just $ resultUri f) },
-		_moduleDeclaration = Declaration {
-			_declarationName = fromString $ hayooName f,
-			_declarationDefined = Nothing,
-			_declarationImported = Nothing,
-			_declarationDocs = Just (fromString $ addOnline $ untagDescription $ hayooDescription f),
-			_declarationPosition = Nothing,
-			_declaration = declInfo } }
-	| otherwise = Nothing
-	where
-		-- Add other info
-		addOnline d = unlines [
-			d, "",
-			"Hayoo online documentation",
-			"",
-			"Package: " ++ hayooPackage f,
-			"Hackage URL: " ++ resultUri f]
-
-		declInfo
-			| hayooType f == "function" = Function (Just $ fromString $ hayooSignature f) [] Nothing
-			| hayooType f `elem` ["type", "newtype", "data", "class"] = declarationTypeCtor (hayooType f) $ TypeInfo Nothing [] Nothing []
-			| otherwise = error "Impossible"
-
--- | Search hayoo
-hayoo :: String -> Maybe Int -> ExceptT String IO HayooResult
-hayoo q page = do
-	resp <- ExceptT $ (show +++ rspBody) <$> simpleHTTP (getRequest $ maybe id addPage page $ "http://hayoo.fh-wedel.de/json/?query=" ++ urlEncode q)
-	ExceptT $ return $ eitherDecode $ L.pack resp
-	where
-		addPage :: Int -> String -> String
-		addPage p s = s ++ "&page=" ++ show p
-
--- | Remove tags in description
-untagDescription :: String -> String
-untagDescription = replaceRx "</?\\w+[^>]*>" ""
+{-# LANGUAGE OverloadedStrings #-}
+
+module HsDev.Tools.Hayoo (
+	-- * Types
+	HayooResult(..), HayooSymbol(..),
+	hayooAsDeclaration,
+	-- * Search help online
+	hayoo,
+	-- * Utils
+	untagDescription,
+
+	-- * Reexportss
+	module Control.Monad.Except
+	) where
+
+import Control.Arrow
+import Control.Applicative
+import Control.Monad.Except
+
+import Data.Aeson
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Either
+import Network.HTTP
+import Data.String (fromString)
+
+import HsDev.Symbols
+import HsDev.Tools.Base (replaceRx)
+import HsDev.Util
+
+-- | Hayoo response
+data HayooResult = HayooResult {
+	resultMax :: Int,
+	resultOffset :: Int,
+	resultCount :: Int,
+	resultResult :: [HayooSymbol] }
+		deriving (Eq, Ord, Read, Show)
+
+-- | Hayoo symbol
+data HayooSymbol = HayooSymbol {
+	resultUri :: String,
+	tag :: String,
+	hayooPackage :: String,
+	hayooName :: String,
+	hayooSource :: String,
+	hayooDescription :: String,
+	hayooSignature :: String,
+	hayooModules :: [String],
+	hayooScore :: Double,
+	hayooType :: String }
+		deriving (Eq, Ord, Read, Show)
+
+newtype HayooValue = HayooValue { hayooValue :: Either Value HayooSymbol }
+
+instance FromJSON HayooResult where
+	parseJSON = withObject "hayoo response" $ \v -> HayooResult <$>
+		(v .:: "max") <*>
+		(v .:: "offset") <*>
+		(v .:: "count") <*>
+		((rights . map hayooValue) <$> (v .:: "result"))
+
+instance Symbol HayooSymbol where
+	symbolName = fromString . hayooName
+	symbolQualifiedName f = fromString $ case hayooModules f of
+		[] -> hayooName f
+		(m:_) -> m ++ "." ++ hayooName f
+	symbolDocs = Just . fromString . hayooDescription
+	symbolLocation r = Location (ModuleSource $ Just $ resultUri r) Nothing
+
+instance Documented HayooSymbol where
+	brief f
+		| hayooType f == "function" = hayooName f ++ " :: " ++ hayooSignature f
+		| otherwise = hayooType f ++ " " ++ hayooName f
+	detailed f = unlines $ defaultDetailed f ++ online where
+		online = [
+			"", "Hayoo online documentation", "",
+			"Package: " ++ hayooPackage f,
+			"Hackage URL: " ++ resultUri f]
+
+instance FromJSON HayooSymbol where
+	parseJSON = withObject "symbol" $ \v -> HayooSymbol <$>
+		(v .:: "resultUri") <*>
+		(v .:: "tag") <*>
+		(v .:: "resultPackage") <*>
+		(v .:: "resultName") <*>
+		(v .:: "resultSource") <*>
+		(v .:: "resultDescription") <*>
+		(v .:: "resultSignature") <*>
+		(v .:: "resultModules") <*>
+		(v .:: "resultScore") <*>
+		(v .:: "resultType")
+
+instance FromJSON HayooValue where
+	parseJSON v = HayooValue <$> ((Right <$> parseJSON v) <|> pure (Left v))
+
+-- | 'HayooFunction' as 'Declaration'
+hayooAsDeclaration :: HayooSymbol -> Maybe ModuleDeclaration
+hayooAsDeclaration f
+	| hayooType f `elem` ["function", "type", "newtype", "data", "class"] = Just ModuleDeclaration {
+		_declarationModuleId = ModuleId {
+			_moduleIdName = fromString $ head $ hayooModules f,
+			_moduleIdLocation = ModuleSource (Just $ resultUri f) },
+		_moduleDeclaration = Declaration {
+			_declarationName = fromString $ hayooName f,
+			_declarationDefined = Nothing,
+			_declarationImported = Nothing,
+			_declarationDocs = Just (fromString $ addOnline $ untagDescription $ hayooDescription f),
+			_declarationPosition = Nothing,
+			_declaration = declInfo } }
+	| otherwise = Nothing
+	where
+		-- Add other info
+		addOnline d = unlines [
+			d, "",
+			"Hayoo online documentation",
+			"",
+			"Package: " ++ hayooPackage f,
+			"Hackage URL: " ++ resultUri f]
+
+		declInfo
+			| hayooType f == "function" = Function (Just $ fromString $ hayooSignature f) [] Nothing
+			| hayooType f `elem` ["type", "newtype", "data", "class"] = declarationTypeCtor (hayooType f) $ TypeInfo Nothing [] Nothing []
+			| otherwise = error "Impossible"
+
+-- | Search hayoo
+hayoo :: String -> Maybe Int -> ExceptT String IO HayooResult
+hayoo q page = do
+	resp <- ExceptT $ (show +++ rspBody) <$> simpleHTTP (getRequest $ maybe id addPage page $ "http://hayoo.fh-wedel.de/json/?query=" ++ urlEncode q)
+	ExceptT $ return $ eitherDecode $ L.pack resp
+	where
+		addPage :: Int -> String -> String
+		addPage p s = s ++ "&page=" ++ show p
+
+-- | Remove tags in description
+untagDescription :: String -> String
+untagDescription = replaceRx "</?\\w+[^>]*>" ""
diff --git a/src/HsDev/Tools/Types.hs b/src/HsDev/Tools/Types.hs
--- a/src/HsDev/Tools/Types.hs
+++ b/src/HsDev/Tools/Types.hs
@@ -1,99 +1,99 @@
-{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
-
-module HsDev.Tools.Types (
-	Severity(..),
-	Note(..), noteSource, noteRegion, noteLevel, note,
-	OutputMessage(..), message, messageSuggestion, outputMessage
-	) where
-
-import Control.DeepSeq (NFData(..))
-import Control.Lens (makeLenses)
-import Control.Monad
-import Data.Aeson hiding (Error)
-
-import System.Directory.Paths
-import HsDev.Symbols.Location
-import HsDev.Util ((.::), (.::?))
-
--- | Note severity
-data Severity = Error | Warning | Hint deriving (Enum, Bounded, Eq, Ord, Read, Show)
-
-instance NFData Severity where
-	rnf Error = ()
-	rnf Warning = ()
-	rnf Hint = ()
-
-instance ToJSON Severity where
-	toJSON Error = toJSON ("error" :: String)
-	toJSON Warning = toJSON ("warning" :: String)
-	toJSON Hint = toJSON ("hint" :: String)
-
-instance FromJSON Severity where
-	parseJSON v = do
-		s <- parseJSON v
-		msum [
-			guard (s == ("error" :: String)) >> return Error,
-			guard (s == ("warning" :: String)) >> return Warning,
-			guard (s == ("hint" :: String)) >> return Hint,
-			fail $ "Unknown severity: " ++ s]
-
--- | Note over some region
-data Note a = Note {
-	_noteSource :: ModuleLocation,
-	_noteRegion :: Region,
-	_noteLevel :: Maybe Severity,
-	_note :: a }
-		deriving (Eq, Show)
-
-makeLenses ''Note
-
-instance Functor Note where
-	fmap f (Note s r l n) = Note s r l (f n)
-
-instance NFData a => NFData (Note a) where
-	rnf (Note s r l n) = rnf s `seq` rnf r `seq` rnf l `seq` rnf n
-
-instance ToJSON a => ToJSON (Note a) where
-	toJSON (Note s r l n) = object [
-		"source" .= s,
-		"region" .= r,
-		"level" .= l,
-		"note" .= n]
-
-instance FromJSON a => FromJSON (Note a) where
-	parseJSON = withObject "note" $ \v -> Note <$>
-		v .:: "source" <*>
-		v .:: "region" <*>
-		v .::? "level" <*>
-		v .:: "note"
-
-instance RecalcTabs (Note a) where
-	recalcTabs cts n' (Note s r l n) = Note s (recalcTabs cts n' r) l n
-	calcTabs cts n' (Note s r l n) = Note s (calcTabs cts n' r) l n
-
-instance Paths (Note a) where
-	paths f (Note s r l n) = Note <$> paths f s <*> pure r <*> pure l <*> pure n
-
--- | Output message from some tool (ghc, ghc-mod, hlint) with optional suggestion
-data OutputMessage = OutputMessage {
-	_message :: String,
-	_messageSuggestion :: Maybe String }
-		deriving (Eq, Ord, Read, Show)
-
-instance NFData OutputMessage where
-	rnf (OutputMessage m s) = rnf m `seq` rnf s
-
-instance ToJSON OutputMessage where
-	toJSON (OutputMessage m s) = object [
-		"message" .= m,
-		"suggestion" .= s]
-
-instance FromJSON OutputMessage where
-	parseJSON = withObject "output-message" $ \v -> OutputMessage <$>
-		v .:: "message" <*>
-		v .:: "suggestion"
-
-outputMessage :: String -> OutputMessage
-outputMessage msg = OutputMessage msg Nothing
-
-makeLenses ''OutputMessage
+{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
+
+module HsDev.Tools.Types (
+	Severity(..),
+	Note(..), noteSource, noteRegion, noteLevel, note,
+	OutputMessage(..), message, messageSuggestion, outputMessage
+	) where
+
+import Control.DeepSeq (NFData(..))
+import Control.Lens (makeLenses)
+import Control.Monad
+import Data.Aeson hiding (Error)
+
+import System.Directory.Paths
+import HsDev.Symbols.Location
+import HsDev.Util ((.::), (.::?))
+
+-- | Note severity
+data Severity = Error | Warning | Hint deriving (Enum, Bounded, Eq, Ord, Read, Show)
+
+instance NFData Severity where
+	rnf Error = ()
+	rnf Warning = ()
+	rnf Hint = ()
+
+instance ToJSON Severity where
+	toJSON Error = toJSON ("error" :: String)
+	toJSON Warning = toJSON ("warning" :: String)
+	toJSON Hint = toJSON ("hint" :: String)
+
+instance FromJSON Severity where
+	parseJSON v = do
+		s <- parseJSON v
+		msum [
+			guard (s == ("error" :: String)) >> return Error,
+			guard (s == ("warning" :: String)) >> return Warning,
+			guard (s == ("hint" :: String)) >> return Hint,
+			fail $ "Unknown severity: " ++ s]
+
+-- | Note over some region
+data Note a = Note {
+	_noteSource :: ModuleLocation,
+	_noteRegion :: Region,
+	_noteLevel :: Maybe Severity,
+	_note :: a }
+		deriving (Eq, Show)
+
+makeLenses ''Note
+
+instance Functor Note where
+	fmap f (Note s r l n) = Note s r l (f n)
+
+instance NFData a => NFData (Note a) where
+	rnf (Note s r l n) = rnf s `seq` rnf r `seq` rnf l `seq` rnf n
+
+instance ToJSON a => ToJSON (Note a) where
+	toJSON (Note s r l n) = object [
+		"source" .= s,
+		"region" .= r,
+		"level" .= l,
+		"note" .= n]
+
+instance FromJSON a => FromJSON (Note a) where
+	parseJSON = withObject "note" $ \v -> Note <$>
+		v .:: "source" <*>
+		v .:: "region" <*>
+		v .::? "level" <*>
+		v .:: "note"
+
+instance RecalcTabs (Note a) where
+	recalcTabs cts n' (Note s r l n) = Note s (recalcTabs cts n' r) l n
+	calcTabs cts n' (Note s r l n) = Note s (calcTabs cts n' r) l n
+
+instance Paths (Note a) where
+	paths f (Note s r l n) = Note <$> paths f s <*> pure r <*> pure l <*> pure n
+
+-- | Output message from some tool (ghc, ghc-mod, hlint) with optional suggestion
+data OutputMessage = OutputMessage {
+	_message :: String,
+	_messageSuggestion :: Maybe String }
+		deriving (Eq, Ord, Read, Show)
+
+instance NFData OutputMessage where
+	rnf (OutputMessage m s) = rnf m `seq` rnf s
+
+instance ToJSON OutputMessage where
+	toJSON (OutputMessage m s) = object [
+		"message" .= m,
+		"suggestion" .= s]
+
+instance FromJSON OutputMessage where
+	parseJSON = withObject "output-message" $ \v -> OutputMessage <$>
+		v .:: "message" <*>
+		v .:: "suggestion"
+
+outputMessage :: String -> OutputMessage
+outputMessage msg = OutputMessage msg Nothing
+
+makeLenses ''OutputMessage
diff --git a/src/HsDev/Types.hs b/src/HsDev/Types.hs
--- a/src/HsDev/Types.hs
+++ b/src/HsDev/Types.hs
@@ -1,112 +1,112 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module HsDev.Types (
-	HsDevError(..)
-	) where
-
-import Control.Exception
-import Control.DeepSeq (NFData(..))
-import Data.Aeson
-import Data.Aeson.Types (Pair, Parser)
-import Data.Typeable
-import Text.Format
-
-import HsDev.Symbols.Location
-
--- | hsdev exception type
-data HsDevError =
-	ModuleNotSource ModuleLocation |
-	BrowseNoModuleInfo String |
-	FileNotFound FilePath |
-	ToolNotFound String |
-	ProjectNotFound String |
-	PackageNotFound String |
-	ToolError String String |
-	NotInspected ModuleLocation |
-	InspectError String |
-	InspectCabalError FilePath String |
-	IOFailed String |
-	GhcError String |
-	RequestError String String |
-	ResponseError String String |
-	OtherError String
-		deriving (Typeable)
-
-instance NFData HsDevError where
-	rnf (ModuleNotSource mloc) = rnf mloc
-	rnf (BrowseNoModuleInfo m) = rnf m
-	rnf (FileNotFound f) = rnf f
-	rnf (ToolNotFound t) = rnf t
-	rnf (ProjectNotFound p) = rnf p
-	rnf (PackageNotFound p) = rnf p
-	rnf (ToolError t e) = rnf t `seq` rnf e
-	rnf (NotInspected mloc) = rnf mloc
-	rnf (InspectError e) = rnf e
-	rnf (InspectCabalError c e) = rnf c `seq` rnf e
-	rnf (IOFailed e) = rnf e
-	rnf (GhcError e) = rnf e
-	rnf (RequestError e r) = rnf e `seq` rnf r
-	rnf (ResponseError e r) = rnf e `seq` rnf r
-	rnf (OtherError e) = rnf e
-
-instance Show HsDevError where
-	show (ModuleNotSource mloc) = format "module is not source: {}" ~~ show mloc
-	show (BrowseNoModuleInfo m) = format "can't find module info for {}" ~~ m
-	show (FileNotFound f) = format "file '{}' not found" ~~ f
-	show (ToolNotFound t) = format "tool '{}' not found" ~~ t
-	show (ProjectNotFound p) = format "project '{}' not found" ~~ p
-	show (PackageNotFound p) = format "package '{}' not found" ~~ p
-	show (ToolError t e) = format "tool '{}' failed: {}" ~~ t ~~ e
-	show (NotInspected mloc) = "module not inspected: {}" ~~ show mloc
-	show (InspectError e) = format "failed to inspect: {}" ~~ e
-	show (InspectCabalError c e) = format "failed to inspect cabal {}: {}" ~~ c ~~ e
-	show (IOFailed e) = format "io exception: {}" ~~ e
-	show (GhcError e) = format "ghc exception: {}" ~~ e
-	show (RequestError e r) = format "request error: {}, request: {}" ~~ e ~~ r
-	show (ResponseError e r) = format "response error: {}, response: {}" ~~ e ~~ r
-	show (OtherError e) = e
-
-instance Formattable HsDevError where
-
-jsonErr :: String -> [Pair] -> Value
-jsonErr e = object . (("error" .= e) :)
-
-instance ToJSON HsDevError where
-	toJSON (ModuleNotSource mloc) = jsonErr "module is not source" ["module" .= mloc]
-	toJSON (BrowseNoModuleInfo m) = jsonErr "no module info" ["module" .= m]
-	toJSON (FileNotFound f) = jsonErr "file not found" ["file" .= f]
-	toJSON (ToolNotFound t) = jsonErr "tool not found" ["tool" .= t]
-	toJSON (ProjectNotFound p) = jsonErr "project not found" ["project" .= p]
-	toJSON (PackageNotFound p) = jsonErr "package not found" ["package" .= p]
-	toJSON (ToolError t e) = jsonErr "tool error" ["tool" .= t, "msg" .= e]
-	toJSON (NotInspected mloc) = jsonErr "module not inspected" ["module" .= mloc]
-	toJSON (InspectError e) = jsonErr "inspect error" ["msg" .= e]
-	toJSON (InspectCabalError c e) = jsonErr "inspect cabal error" ["cabal" .= c, "msg" .= e]
-	toJSON (IOFailed e) = jsonErr "io error" ["msg" .= e]
-	toJSON (GhcError e) = jsonErr "ghc error" ["msg" .= e]
-	toJSON (RequestError e r) = jsonErr "request error" ["msg" .= e, "request" .= r]
-	toJSON (ResponseError e r) = jsonErr "response error" ["msg" .= e, "response" .= r]
-	toJSON (OtherError e) = jsonErr "other error" ["msg" .= e]
-
-instance FromJSON HsDevError where
-	parseJSON = withObject "hsdev-error" $ \v -> do
-		err <- v .: "error" :: Parser String
-		case err of
-			"module is not source" -> ModuleNotSource <$> v .: "module"
-			"no module info" -> BrowseNoModuleInfo <$> v .: "module"
-			"file not found" -> FileNotFound <$> v .: "file"
-			"tool not found" -> ToolNotFound <$> v .: "tool"
-			"project not found" -> ProjectNotFound <$> v .: "project"
-			"package not found" -> PackageNotFound <$> v .: "package"
-			"tool error" -> ToolError <$> v .: "tool" <*> v .: "msg"
-			"module not inspected" -> NotInspected <$> v .: "module"
-			"inspect error" -> InspectError <$> v .: "msg"
-			"inspect cabal error" -> InspectCabalError <$> v .: "cabal" <*> v .: "msg"
-			"io error" -> IOFailed <$> v .: "msg"
-			"ghc error" -> GhcError <$> v .: "msg"
-			"request error" -> RequestError <$> v .: "msg" <*> v .: "request"
-			"response error" -> ResponseError <$> v .: "msg" <*> v .: "response"
-			"other error" -> OtherError <$> v .: "msg"
-			_ -> fail "invalid error"
-
-instance Exception HsDevError
+{-# LANGUAGE OverloadedStrings #-}
+
+module HsDev.Types (
+	HsDevError(..)
+	) where
+
+import Control.Exception
+import Control.DeepSeq (NFData(..))
+import Data.Aeson
+import Data.Aeson.Types (Pair, Parser)
+import Data.Typeable
+import Text.Format
+
+import HsDev.Symbols.Location
+
+-- | hsdev exception type
+data HsDevError =
+	ModuleNotSource ModuleLocation |
+	BrowseNoModuleInfo String |
+	FileNotFound FilePath |
+	ToolNotFound String |
+	ProjectNotFound String |
+	PackageNotFound String |
+	ToolError String String |
+	NotInspected ModuleLocation |
+	InspectError String |
+	InspectCabalError FilePath String |
+	IOFailed String |
+	GhcError String |
+	RequestError String String |
+	ResponseError String String |
+	OtherError String
+		deriving (Typeable)
+
+instance NFData HsDevError where
+	rnf (ModuleNotSource mloc) = rnf mloc
+	rnf (BrowseNoModuleInfo m) = rnf m
+	rnf (FileNotFound f) = rnf f
+	rnf (ToolNotFound t) = rnf t
+	rnf (ProjectNotFound p) = rnf p
+	rnf (PackageNotFound p) = rnf p
+	rnf (ToolError t e) = rnf t `seq` rnf e
+	rnf (NotInspected mloc) = rnf mloc
+	rnf (InspectError e) = rnf e
+	rnf (InspectCabalError c e) = rnf c `seq` rnf e
+	rnf (IOFailed e) = rnf e
+	rnf (GhcError e) = rnf e
+	rnf (RequestError e r) = rnf e `seq` rnf r
+	rnf (ResponseError e r) = rnf e `seq` rnf r
+	rnf (OtherError e) = rnf e
+
+instance Show HsDevError where
+	show (ModuleNotSource mloc) = format "module is not source: {}" ~~ show mloc
+	show (BrowseNoModuleInfo m) = format "can't find module info for {}" ~~ m
+	show (FileNotFound f) = format "file '{}' not found" ~~ f
+	show (ToolNotFound t) = format "tool '{}' not found" ~~ t
+	show (ProjectNotFound p) = format "project '{}' not found" ~~ p
+	show (PackageNotFound p) = format "package '{}' not found" ~~ p
+	show (ToolError t e) = format "tool '{}' failed: {}" ~~ t ~~ e
+	show (NotInspected mloc) = "module not inspected: {}" ~~ show mloc
+	show (InspectError e) = format "failed to inspect: {}" ~~ e
+	show (InspectCabalError c e) = format "failed to inspect cabal {}: {}" ~~ c ~~ e
+	show (IOFailed e) = format "io exception: {}" ~~ e
+	show (GhcError e) = format "ghc exception: {}" ~~ e
+	show (RequestError e r) = format "request error: {}, request: {}" ~~ e ~~ r
+	show (ResponseError e r) = format "response error: {}, response: {}" ~~ e ~~ r
+	show (OtherError e) = e
+
+instance Formattable HsDevError where
+
+jsonErr :: String -> [Pair] -> Value
+jsonErr e = object . (("error" .= e) :)
+
+instance ToJSON HsDevError where
+	toJSON (ModuleNotSource mloc) = jsonErr "module is not source" ["module" .= mloc]
+	toJSON (BrowseNoModuleInfo m) = jsonErr "no module info" ["module" .= m]
+	toJSON (FileNotFound f) = jsonErr "file not found" ["file" .= f]
+	toJSON (ToolNotFound t) = jsonErr "tool not found" ["tool" .= t]
+	toJSON (ProjectNotFound p) = jsonErr "project not found" ["project" .= p]
+	toJSON (PackageNotFound p) = jsonErr "package not found" ["package" .= p]
+	toJSON (ToolError t e) = jsonErr "tool error" ["tool" .= t, "msg" .= e]
+	toJSON (NotInspected mloc) = jsonErr "module not inspected" ["module" .= mloc]
+	toJSON (InspectError e) = jsonErr "inspect error" ["msg" .= e]
+	toJSON (InspectCabalError c e) = jsonErr "inspect cabal error" ["cabal" .= c, "msg" .= e]
+	toJSON (IOFailed e) = jsonErr "io error" ["msg" .= e]
+	toJSON (GhcError e) = jsonErr "ghc error" ["msg" .= e]
+	toJSON (RequestError e r) = jsonErr "request error" ["msg" .= e, "request" .= r]
+	toJSON (ResponseError e r) = jsonErr "response error" ["msg" .= e, "response" .= r]
+	toJSON (OtherError e) = jsonErr "other error" ["msg" .= e]
+
+instance FromJSON HsDevError where
+	parseJSON = withObject "hsdev-error" $ \v -> do
+		err <- v .: "error" :: Parser String
+		case err of
+			"module is not source" -> ModuleNotSource <$> v .: "module"
+			"no module info" -> BrowseNoModuleInfo <$> v .: "module"
+			"file not found" -> FileNotFound <$> v .: "file"
+			"tool not found" -> ToolNotFound <$> v .: "tool"
+			"project not found" -> ProjectNotFound <$> v .: "project"
+			"package not found" -> PackageNotFound <$> v .: "package"
+			"tool error" -> ToolError <$> v .: "tool" <*> v .: "msg"
+			"module not inspected" -> NotInspected <$> v .: "module"
+			"inspect error" -> InspectError <$> v .: "msg"
+			"inspect cabal error" -> InspectCabalError <$> v .: "cabal" <*> v .: "msg"
+			"io error" -> IOFailed <$> v .: "msg"
+			"ghc error" -> GhcError <$> v .: "msg"
+			"request error" -> RequestError <$> v .: "msg" <*> v .: "request"
+			"response error" -> ResponseError <$> v .: "msg" <*> v .: "response"
+			"other error" -> OtherError <$> v .: "msg"
+			_ -> fail "invalid error"
+
+instance Exception HsDevError
diff --git a/src/HsDev/Util.hs b/src/HsDev/Util.hs
--- a/src/HsDev/Util.hs
+++ b/src/HsDev/Util.hs
@@ -1,338 +1,340 @@
-{-# LANGUAGE FlexibleContexts, OverloadedStrings, TemplateHaskell, CPP #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module HsDev.Util (
-	withCurrentDirectory,
-	directoryContents,
-	traverseDirectory, searchPath,
-	isParent,
-	haskellSource,
-	cabalFile,
-	-- * String utils
-	tab, tabs, trim, split,
-	-- * Other utils
-	ordNub, uniqueBy, mapBy,
-	-- * Helper
-	(.::), (.::?), (.::?!), objectUnion, jsonUnion, noNulls,
-	-- * Exceptions
-	liftException, liftE, liftEIO, tries, triesMap, liftExceptionM, liftIOErrors,
-	eitherT,
-	liftThrow,
-	-- * UTF-8
-	fromUtf8, toUtf8, readFileUtf8, writeFileUtf8,
-	-- * IO
-	hGetLineBS, logException, logIO, ignoreIO, logAsync,
-	-- * Async
-	liftAsync,
-	-- * Command line
-	FromCmd(..),
-	cmdJson, withCmd, guardCmd,
-	withHelp, cmd, parseArgs,
-	-- * Version stuff
-	version, cutVersion, sameVersion, strVersion,
-
-	-- * Reexportss
-	module Control.Monad.Except,
-	MonadIO(..)
-	) where
-
-import Control.Applicative
-import Control.Arrow (second, left, (&&&))
-import Control.Exception
-import Control.Concurrent.Async
-import Control.Monad
-import Control.Monad.Except
-import qualified Control.Monad.Catch as C
-import Data.Aeson hiding (Result(..), Error)
-import qualified Data.Aeson.Types as A
-import Data.Char (isSpace)
-import Data.List (isPrefixOf, unfoldr, intercalate)
-import qualified Data.Map as M
-import Data.Maybe (catMaybes, fromMaybe)
-import Data.Monoid ((<>))
-import qualified Data.Set as Set
-import qualified Data.HashMap.Strict as HM (HashMap, toList, union)
-import qualified Data.ByteString.Char8 as B
-import Data.ByteString.Lazy (ByteString)
-import qualified Data.ByteString.Lazy.Char8 as L
-import Data.Text (Text)
-import qualified Data.Text.Lazy as T
-import qualified Data.Text.Lazy.Encoding as T
-import Options.Applicative
-import qualified System.Directory as Dir
-import System.FilePath
-import System.IO
-import Text.Read (readMaybe)
-
-#if !MIN_VERSION_directory(1,2,6)
-#if mingw32_HOST_OS
-import qualified System.Win32 as Win32
-import Data.Bits ((.&.))
-#else
-import qualified System.Posix as Posix
-#endif
-#endif
-
-import HsDev.Version
-
--- | Run action with current directory set
-withCurrentDirectory :: (MonadIO m, C.MonadMask m) => FilePath -> m a -> m a
-withCurrentDirectory cur act = C.bracket (liftIO Dir.getCurrentDirectory) (liftIO . Dir.setCurrentDirectory) $
-	const (liftIO (Dir.setCurrentDirectory cur) >> act)
-
--- | Is directory symbolic link
-dirIsSymLink :: FilePath -> IO Bool
-#if MIN_VERSION_directory(1,2,6)
-dirIsSymLink = Dir.isSymbolicLink
-#else
-dirIsSymLink path = do
-#if mingw32_HOST_OS
-	isReparsePoint <$> Win32.getFileAttributes path
-	where
-		fILE_ATTRIBUTE_REPARSE_POINT = 0x400
-		isReparsePoint attr = attr .&. fILE_ATTRIBUTE_REPARSE_POINT /= 0
-#else
-	Posix.isSymbolicLink <$> Posix.getSymbolicLinkStatus path
-#endif
-#endif
-
--- | Get directory contents safely: no fail, ignoring symbolic links, also prepends paths with dir name
-directoryContents :: FilePath -> IO [FilePath]
-directoryContents p = handle ignore $ do
-	b <- Dir.doesDirectoryExist p
-	isLink <- dirIsSymLink p
-	if b && (not isLink)
-		then liftM (map (p </>) . filter (`notElem` [".", ".."])) (Dir.getDirectoryContents p)
-		else return []
-	where
-		ignore :: SomeException -> IO [FilePath]
-		ignore _ = return []
-
--- | Collect all file names in directory recursively
-traverseDirectory :: FilePath -> IO [FilePath]
-traverseDirectory path = handle onError $ do
-	cts <- directoryContents path
-	liftM concat $ forM cts $ \c -> do
-		isDir <- Dir.doesDirectoryExist c
-		if isDir
-			then (c :) <$> traverseDirectory c
-			else return [c]
-	where
-		onError :: IOException -> IO [FilePath]
-		onError _ = return []
-
--- | Search something up
-searchPath :: (MonadIO m, MonadPlus m) => FilePath -> (FilePath -> m a) -> m a
-searchPath path f = do
-	path' <- liftIO $ Dir.canonicalizePath path
-	isDir <- liftIO $ Dir.doesDirectoryExist path'
-	search' (if isDir then path' else takeDirectory path')
-	where
-		search' dir
-			| isDrive dir = f dir
-			| otherwise = f dir `mplus` search' (takeDirectory dir)
-
--- | Is one path parent of another
-isParent :: FilePath -> FilePath -> Bool
-isParent dir file = norm dir `isPrefixOf` norm file where
-	norm = dropDot . splitDirectories . normalise
-	dropDot ("." : chs) = chs
-	dropDot chs = chs
-
--- | Is haskell source?
-haskellSource :: FilePath -> Bool
-haskellSource f = takeExtension f `elem` [".hs", ".lhs"]
-
--- | Is cabal file?
-cabalFile :: FilePath -> Bool
-cabalFile f = takeExtension f == ".cabal"
-
--- | Add N tabs to line
-tab :: Int -> String -> String
-tab n s = replicate n '\t' ++ s
-
--- | Add N tabs to multiline
-tabs :: Int -> String -> String
-tabs n = unlines . map (tab n) . lines
-
--- | Trim string
-trim :: String -> String
-trim = p . p where
-	p = reverse . dropWhile isSpace
-
--- | Split list
-split :: (a -> Bool) -> [a] -> [[a]]
-split p = takeWhile (not . null) . unfoldr (Just . second (drop 1) . break p)
-
--- | nub is quadratic, https://github.com/nh2/haskell-ordnub/#ordnub
-ordNub :: Ord a => [a] -> [a]
-ordNub = go Set.empty where
-	go _ [] = []
-	go s (x:xs)
-		| x `Set.member` s = go s xs
-		| otherwise = x : go (Set.insert x s) xs
-
-uniqueBy :: Ord b => (a -> b) -> [a] -> [a]
-uniqueBy f = M.elems . mapBy f
-
-mapBy :: Ord b => (a -> b) -> [a] -> M.Map b a
-mapBy f = M.fromList . map (f &&& id)
-
--- | Workaround, sometimes we get HM.lookup "foo" v == Nothing, but lookup "foo" (HM.toList v) == Just smth
-(.::) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser a
-v .:: name = maybe (fail $ "key " ++ show name ++ " not present") parseJSON $ lookup name $ HM.toList v
-
--- | Returns @Nothing@ when key doesn't exist or value is @Null@
-(.::?) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser (Maybe a)
-v .::? name = fmap join $ traverse parseJSON $ lookup name $ HM.toList v
-
--- | Same as @.::?@ for list, returns empty list for non-existant key or @Null@ value
-(.::?!) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser [a]
-v .::?! name = fromMaybe [] <$> (v .::? name)
-
--- | Union two JSON objects
-objectUnion :: Value -> Value -> Value
-objectUnion (Object l) (Object r) = Object $ HM.union l r
-objectUnion (Object l) _ = Object l
-objectUnion _ (Object r) = Object r
-objectUnion _ _ = Null
-
--- | Union two JSON objects
-jsonUnion :: (ToJSON a, ToJSON b) => a -> b -> Value
-jsonUnion x y = objectUnion (toJSON x) (toJSON y)
-
--- | No Nulls in JSON object
-noNulls :: [A.Pair] -> [A.Pair]
-noNulls = filter (not . isNull . snd) where
-	isNull Null = True
-	isNull v = v == A.emptyArray || v == A.emptyObject
-
--- | Lift IO exception to ExceptT
-liftException :: C.MonadCatch m => m a -> ExceptT String m a
-liftException = ExceptT . liftM (left $ \(SomeException e) -> displayException e) . C.try
-
--- | Same as @liftException@
-liftE :: C.MonadCatch m => m a -> ExceptT String m a
-liftE = liftException
-
--- | @liftE@ for IO
-liftEIO :: (C.MonadCatch m, MonadIO m) => IO a -> ExceptT String m a
-liftEIO = liftE . liftIO
-
--- | Run actions ignoring errors
-tries :: MonadPlus m => [m a] -> m [a]
-tries acts = liftM catMaybes $ sequence [liftM Just act `mplus` return Nothing | act <- acts]
-
-triesMap :: MonadPlus m => (a -> m b) -> [a] -> m [b]
-triesMap f = tries . map f
-
--- | Lift IO exception to MonadError
-liftExceptionM :: (C.MonadCatch m, MonadError String m) => m a -> m a
-liftExceptionM act = C.catch act onError where
-	onError = throwError . (\(SomeException e) -> displayException e)
-
--- | Lift IO exceptions to ExceptT
-liftIOErrors :: C.MonadCatch m => ExceptT String m a -> ExceptT String m a
-liftIOErrors act = liftException (runExceptT act) >>= either throwError return
-
-eitherT :: MonadError String m => Either String a -> m a
-eitherT = either throwError return
-
--- | Throw error as exception
-liftThrow :: (Show e, MonadError e m, C.MonadCatch m) => m a -> m a
-liftThrow act = catchError act (C.throwM . userError . show)
-
-fromUtf8 :: ByteString -> String
-fromUtf8 = T.unpack . T.decodeUtf8
-
-toUtf8 :: String -> ByteString
-toUtf8 = T.encodeUtf8 . T.pack
-
--- | Read file in UTF8
-readFileUtf8 :: FilePath -> IO String
-readFileUtf8 f = withFile f ReadMode $ \h -> do
-	hSetEncoding h utf8
-	cts <- hGetContents h
-	length cts `seq` return cts
-
-writeFileUtf8 :: FilePath -> String -> IO ()
-writeFileUtf8 f cts = withFile f WriteMode $ \h -> do
-	hSetEncoding h utf8
-	hPutStr h cts
-
-hGetLineBS :: Handle -> IO ByteString
-hGetLineBS = fmap L.fromStrict . B.hGetLine
-
-logException :: String -> (String -> IO ()) -> IO () -> IO ()
-logException pre out = handle onErr where
-	onErr :: SomeException -> IO ()
-	onErr e = out $ pre ++ displayException e
-
-logIO :: C.MonadCatch m => String -> (String -> m ()) -> m () -> m ()
-logIO pre out = flip C.catch (onIO out) where
-	onIO :: (String -> a) -> IOException -> a
-	onIO out' e = out' $ pre ++ displayException e
-
-logAsync :: (MonadIO m, C.MonadCatch m) => (String -> m ()) -> m () -> m ()
-logAsync out = flip C.catch (onAsync out) where
-	onAsync :: (MonadIO m, C.MonadThrow m) => (String -> m ()) -> AsyncException -> m ()
-	onAsync out' e = out' (displayException e) >> C.throwM e
-
-ignoreIO :: C.MonadCatch m => m () -> m ()
-ignoreIO = C.handle ignore' where
-	ignore' :: Monad m => IOException -> m ()
-	ignore' _ = return ()
-
-liftAsync :: (C.MonadThrow m, C.MonadCatch m, MonadIO m) => IO (Async a) -> ExceptT String m a
-liftAsync = liftExceptionM . ExceptT . liftIO . liftM (left displayException) . join . liftM waitCatch
-
-class FromCmd a where
-	cmdP :: Parser a
-
-cmdJson :: String -> [A.Pair] -> Value
-cmdJson nm ps = object $ ("command" .= nm) : ps
-
-withCmd :: String -> (Object -> A.Parser a) -> Value -> A.Parser a
-withCmd nm fn = withObject ("command " ++ nm) $ \v -> guardCmd nm v *> fn v
-
-guardCmd :: String -> Object -> A.Parser ()
-guardCmd nm obj = do
-	cmdName <- obj .:: "command"
-	guard (nm == cmdName)
-
--- | Add help command to parser
-withHelp :: Parser a -> Parser a
-withHelp = (helper' <*>) where
-	helper' = abortOption ShowHelpText $ long "help" <> short '?' <> help "show help" <> hidden
-
--- | Subcommand
-cmd :: String -> String -> Parser a -> Mod CommandFields a
-cmd n d p = command n (info (withHelp p) (progDesc d))
-
--- | Parse arguments or return help
-parseArgs :: String -> ParserInfo a -> [String] -> Either String a
-parseArgs nm p = handle' . execParserPure (prefs mempty) (p { infoParser = withHelp (infoParser p) }) where
-	handle' :: ParserResult a -> Either String a
-	handle' (Success r) = Right r
-	handle' (Failure f) = Left $ fst $ renderFailure f nm
-	handle' _ = Left "error: completion invoked result"
-
--- instance Log.MonadLog m => Log.MonadLog (ExceptT e m) where
--- 	askLog = lift Log.askLog
-
--- | Get hsdev version as list of integers
-version :: Maybe [Int]
-version = mapM readMaybe $ split (== '.') $cabalVersion
-
--- | Cut version to contain only significant numbers (currently 3)
-cutVersion :: Maybe [Int] -> Maybe [Int]
-cutVersion = fmap (take 3)
-
--- | Check if version is the same
-sameVersion :: Maybe [Int] -> Maybe [Int] -> Bool
-sameVersion l r = fromMaybe False $ liftA2 (==) l r
-
--- | Version to string
-strVersion :: Maybe [Int] -> String
-strVersion Nothing = "unknown"
-strVersion (Just vers) = intercalate "." $ map show vers
+{-# LANGUAGE FlexibleContexts, OverloadedStrings, TemplateHaskell, CPP #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module HsDev.Util (
+	withCurrentDirectory,
+	directoryContents,
+	traverseDirectory, searchPath,
+	isParent,
+	haskellSource,
+	cabalFile,
+	-- * String utils
+	tab, tabs, trim, split,
+	-- * Other utils
+	ordNub, uniqueBy, mapBy,
+	-- * Helper
+	(.::), (.::?), (.::?!), objectUnion, jsonUnion, noNulls,
+	-- * Exceptions
+	liftException, liftE, liftEIO, tries, triesMap, liftExceptionM, liftIOErrors,
+	eitherT,
+	liftThrow,
+	-- * UTF-8
+	fromUtf8, toUtf8, readFileUtf8, writeFileUtf8,
+	-- * IO
+	hGetLineBS, logException, logIO, ignoreIO, logAsync,
+	-- * Async
+	liftAsync,
+	-- * Command line
+	FromCmd(..),
+	cmdJson, withCmd, guardCmd,
+	withHelp, cmd, parseArgs,
+	-- * Version stuff
+	version, cutVersion, sameVersion, strVersion,
+
+	-- * Reexportss
+	module Control.Monad.Except,
+	MonadIO(..)
+	) where
+
+import Control.Applicative
+import Control.Arrow (second, left, (&&&))
+import Control.Exception
+import Control.Concurrent.Async
+import Control.Monad
+import Control.Monad.Except
+import qualified Control.Monad.Catch as C
+import Data.Aeson hiding (Result(..), Error)
+import qualified Data.Aeson.Types as A
+import Data.Char (isSpace)
+import Data.List (isPrefixOf, unfoldr, intercalate)
+import qualified Data.Map as M
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Monoid ((<>))
+import qualified Data.Set as Set
+import qualified Data.HashMap.Strict as HM (HashMap, toList, union)
+import qualified Data.ByteString.Char8 as B
+import Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Text (Text)
+import qualified Data.Text.Lazy as T
+import qualified Data.Text.Lazy.Encoding as T
+import Options.Applicative
+import qualified System.Directory as Dir
+import System.FilePath
+import System.IO
+import Text.Read (readMaybe)
+
+#if !MIN_VERSION_directory(1,2,6)
+#if mingw32_HOST_OS
+import qualified System.Win32 as Win32
+import Data.Bits ((.&.))
+#else
+import qualified System.Posix as Posix
+#endif
+#endif
+
+import HsDev.Version
+
+-- | Run action with current directory set
+withCurrentDirectory :: (MonadIO m, C.MonadMask m) => FilePath -> m a -> m a
+withCurrentDirectory cur act = C.bracket (liftIO Dir.getCurrentDirectory) (liftIO . Dir.setCurrentDirectory) $
+	const (liftIO (Dir.setCurrentDirectory cur) >> act)
+
+-- | Is directory symbolic link
+dirIsSymLink :: FilePath -> IO Bool
+#if MIN_VERSION_directory(1,3,0)
+dirIsSymLink = Dir.pathIsSymbolicLink
+#elif MIN_VERSION_directory(1,2,6)
+dirIsSymLink = Dir.isSymbolicLink
+#else
+dirIsSymLink path = do
+#if mingw32_HOST_OS
+	isReparsePoint <$> Win32.getFileAttributes path
+	where
+		fILE_ATTRIBUTE_REPARSE_POINT = 0x400
+		isReparsePoint attr = attr .&. fILE_ATTRIBUTE_REPARSE_POINT /= 0
+#else
+	Posix.isSymbolicLink <$> Posix.getSymbolicLinkStatus path
+#endif
+#endif
+
+-- | Get directory contents safely: no fail, ignoring symbolic links, also prepends paths with dir name
+directoryContents :: FilePath -> IO [FilePath]
+directoryContents p = handle ignore $ do
+	b <- Dir.doesDirectoryExist p
+	isLink <- dirIsSymLink p
+	if b && (not isLink)
+		then liftM (map (p </>) . filter (`notElem` [".", ".."])) (Dir.getDirectoryContents p)
+		else return []
+	where
+		ignore :: SomeException -> IO [FilePath]
+		ignore _ = return []
+
+-- | Collect all file names in directory recursively
+traverseDirectory :: FilePath -> IO [FilePath]
+traverseDirectory path = handle onError $ do
+	cts <- directoryContents path
+	liftM concat $ forM cts $ \c -> do
+		isDir <- Dir.doesDirectoryExist c
+		if isDir
+			then (c :) <$> traverseDirectory c
+			else return [c]
+	where
+		onError :: IOException -> IO [FilePath]
+		onError _ = return []
+
+-- | Search something up
+searchPath :: (MonadIO m, MonadPlus m) => FilePath -> (FilePath -> m a) -> m a
+searchPath path f = do
+	path' <- liftIO $ Dir.canonicalizePath path
+	isDir <- liftIO $ Dir.doesDirectoryExist path'
+	search' (if isDir then path' else takeDirectory path')
+	where
+		search' dir
+			| isDrive dir = f dir
+			| otherwise = f dir `mplus` search' (takeDirectory dir)
+
+-- | Is one path parent of another
+isParent :: FilePath -> FilePath -> Bool
+isParent dir file = norm dir `isPrefixOf` norm file where
+	norm = dropDot . splitDirectories . normalise
+	dropDot ("." : chs) = chs
+	dropDot chs = chs
+
+-- | Is haskell source?
+haskellSource :: FilePath -> Bool
+haskellSource f = takeExtension f `elem` [".hs", ".lhs"]
+
+-- | Is cabal file?
+cabalFile :: FilePath -> Bool
+cabalFile f = takeExtension f == ".cabal"
+
+-- | Add N tabs to line
+tab :: Int -> String -> String
+tab n s = replicate n '\t' ++ s
+
+-- | Add N tabs to multiline
+tabs :: Int -> String -> String
+tabs n = unlines . map (tab n) . lines
+
+-- | Trim string
+trim :: String -> String
+trim = p . p where
+	p = reverse . dropWhile isSpace
+
+-- | Split list
+split :: (a -> Bool) -> [a] -> [[a]]
+split p = takeWhile (not . null) . unfoldr (Just . second (drop 1) . break p)
+
+-- | nub is quadratic, https://github.com/nh2/haskell-ordnub/#ordnub
+ordNub :: Ord a => [a] -> [a]
+ordNub = go Set.empty where
+	go _ [] = []
+	go s (x:xs)
+		| x `Set.member` s = go s xs
+		| otherwise = x : go (Set.insert x s) xs
+
+uniqueBy :: Ord b => (a -> b) -> [a] -> [a]
+uniqueBy f = M.elems . mapBy f
+
+mapBy :: Ord b => (a -> b) -> [a] -> M.Map b a
+mapBy f = M.fromList . map (f &&& id)
+
+-- | Workaround, sometimes we get HM.lookup "foo" v == Nothing, but lookup "foo" (HM.toList v) == Just smth
+(.::) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser a
+v .:: name = maybe (fail $ "key " ++ show name ++ " not present") parseJSON $ lookup name $ HM.toList v
+
+-- | Returns @Nothing@ when key doesn't exist or value is @Null@
+(.::?) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser (Maybe a)
+v .::? name = fmap join $ traverse parseJSON $ lookup name $ HM.toList v
+
+-- | Same as @.::?@ for list, returns empty list for non-existant key or @Null@ value
+(.::?!) :: FromJSON a => HM.HashMap Text Value -> Text -> A.Parser [a]
+v .::?! name = fromMaybe [] <$> (v .::? name)
+
+-- | Union two JSON objects
+objectUnion :: Value -> Value -> Value
+objectUnion (Object l) (Object r) = Object $ HM.union l r
+objectUnion (Object l) _ = Object l
+objectUnion _ (Object r) = Object r
+objectUnion _ _ = Null
+
+-- | Union two JSON objects
+jsonUnion :: (ToJSON a, ToJSON b) => a -> b -> Value
+jsonUnion x y = objectUnion (toJSON x) (toJSON y)
+
+-- | No Nulls in JSON object
+noNulls :: [A.Pair] -> [A.Pair]
+noNulls = filter (not . isNull . snd) where
+	isNull Null = True
+	isNull v = v == A.emptyArray || v == A.emptyObject
+
+-- | Lift IO exception to ExceptT
+liftException :: C.MonadCatch m => m a -> ExceptT String m a
+liftException = ExceptT . liftM (left $ \(SomeException e) -> displayException e) . C.try
+
+-- | Same as @liftException@
+liftE :: C.MonadCatch m => m a -> ExceptT String m a
+liftE = liftException
+
+-- | @liftE@ for IO
+liftEIO :: (C.MonadCatch m, MonadIO m) => IO a -> ExceptT String m a
+liftEIO = liftE . liftIO
+
+-- | Run actions ignoring errors
+tries :: MonadPlus m => [m a] -> m [a]
+tries acts = liftM catMaybes $ sequence [liftM Just act `mplus` return Nothing | act <- acts]
+
+triesMap :: MonadPlus m => (a -> m b) -> [a] -> m [b]
+triesMap f = tries . map f
+
+-- | Lift IO exception to MonadError
+liftExceptionM :: (C.MonadCatch m, MonadError String m) => m a -> m a
+liftExceptionM act = C.catch act onError where
+	onError = throwError . (\(SomeException e) -> displayException e)
+
+-- | Lift IO exceptions to ExceptT
+liftIOErrors :: C.MonadCatch m => ExceptT String m a -> ExceptT String m a
+liftIOErrors act = liftException (runExceptT act) >>= either throwError return
+
+eitherT :: MonadError String m => Either String a -> m a
+eitherT = either throwError return
+
+-- | Throw error as exception
+liftThrow :: (Show e, MonadError e m, C.MonadCatch m) => m a -> m a
+liftThrow act = catchError act (C.throwM . userError . show)
+
+fromUtf8 :: ByteString -> String
+fromUtf8 = T.unpack . T.decodeUtf8
+
+toUtf8 :: String -> ByteString
+toUtf8 = T.encodeUtf8 . T.pack
+
+-- | Read file in UTF8
+readFileUtf8 :: FilePath -> IO String
+readFileUtf8 f = withFile f ReadMode $ \h -> do
+	hSetEncoding h utf8
+	cts <- hGetContents h
+	length cts `seq` return cts
+
+writeFileUtf8 :: FilePath -> String -> IO ()
+writeFileUtf8 f cts = withFile f WriteMode $ \h -> do
+	hSetEncoding h utf8
+	hPutStr h cts
+
+hGetLineBS :: Handle -> IO ByteString
+hGetLineBS = fmap L.fromStrict . B.hGetLine
+
+logException :: String -> (String -> IO ()) -> IO () -> IO ()
+logException pre out = handle onErr where
+	onErr :: SomeException -> IO ()
+	onErr e = out $ pre ++ displayException e
+
+logIO :: C.MonadCatch m => String -> (String -> m ()) -> m () -> m ()
+logIO pre out = flip C.catch (onIO out) where
+	onIO :: (String -> a) -> IOException -> a
+	onIO out' e = out' $ pre ++ displayException e
+
+logAsync :: (MonadIO m, C.MonadCatch m) => (String -> m ()) -> m () -> m ()
+logAsync out = flip C.catch (onAsync out) where
+	onAsync :: (MonadIO m, C.MonadThrow m) => (String -> m ()) -> AsyncException -> m ()
+	onAsync out' e = out' (displayException e) >> C.throwM e
+
+ignoreIO :: C.MonadCatch m => m () -> m ()
+ignoreIO = C.handle ignore' where
+	ignore' :: Monad m => IOException -> m ()
+	ignore' _ = return ()
+
+liftAsync :: (C.MonadThrow m, C.MonadCatch m, MonadIO m) => IO (Async a) -> ExceptT String m a
+liftAsync = liftExceptionM . ExceptT . liftIO . liftM (left displayException) . join . liftM waitCatch
+
+class FromCmd a where
+	cmdP :: Parser a
+
+cmdJson :: String -> [A.Pair] -> Value
+cmdJson nm ps = object $ ("command" .= nm) : ps
+
+withCmd :: String -> (Object -> A.Parser a) -> Value -> A.Parser a
+withCmd nm fn = withObject ("command " ++ nm) $ \v -> guardCmd nm v *> fn v
+
+guardCmd :: String -> Object -> A.Parser ()
+guardCmd nm obj = do
+	cmdName <- obj .:: "command"
+	guard (nm == cmdName)
+
+-- | Add help command to parser
+withHelp :: Parser a -> Parser a
+withHelp = (helper' <*>) where
+	helper' = abortOption ShowHelpText $ long "help" <> short '?' <> help "show help" <> hidden
+
+-- | Subcommand
+cmd :: String -> String -> Parser a -> Mod CommandFields a
+cmd n d p = command n (info (withHelp p) (progDesc d))
+
+-- | Parse arguments or return help
+parseArgs :: String -> ParserInfo a -> [String] -> Either String a
+parseArgs nm p = handle' . execParserPure (prefs mempty) (p { infoParser = withHelp (infoParser p) }) where
+	handle' :: ParserResult a -> Either String a
+	handle' (Success r) = Right r
+	handle' (Failure f) = Left $ fst $ renderFailure f nm
+	handle' _ = Left "error: completion invoked result"
+
+-- instance Log.MonadLog m => Log.MonadLog (ExceptT e m) where
+-- 	askLog = lift Log.askLog
+
+-- | Get hsdev version as list of integers
+version :: Maybe [Int]
+version = mapM readMaybe $ split (== '.') $cabalVersion
+
+-- | Cut version to contain only significant numbers (currently 3)
+cutVersion :: Maybe [Int] -> Maybe [Int]
+cutVersion = fmap (take 3)
+
+-- | Check if version is the same
+sameVersion :: Maybe [Int] -> Maybe [Int] -> Bool
+sameVersion l r = fromMaybe False $ liftA2 (==) l r
+
+-- | Version to string
+strVersion :: Maybe [Int] -> String
+strVersion Nothing = "unknown"
+strVersion (Just vers) = intercalate "." $ map show vers
diff --git a/src/HsDev/Watcher.hs b/src/HsDev/Watcher.hs
--- a/src/HsDev/Watcher.hs
+++ b/src/HsDev/Watcher.hs
@@ -1,73 +1,73 @@
-module HsDev.Watcher (
-	watchProject, watchModule, watchPackageDb, watchPackageDbStack,
-	unwatchProject, unwatchModule, unwatchPackageDb,
-	isSource, isCabal, isConf,
-
-	module System.Directory.Watcher,
-	module HsDev.Watcher.Types
-	) where
-
-import Control.Lens (view)
-import Control.Monad (void)
-import System.FilePath (takeDirectory, takeExtension, (</>))
-
-import System.Directory.Watcher hiding (Watcher)
-import HsDev.Project
-import HsDev.Symbols
-import HsDev.Watcher.Types
-import HsDev.Util
-
--- | Watch for project sources changes
-watchProject :: Watcher -> Project -> [String] -> IO ()
-watchProject w proj opts = do
-	mapM_ (\dir -> watchTree w dir isSource (WatchedProject proj opts)) dirs
-	watchDir w projDir isCabal (WatchedProject proj opts)
-	where
-		dirs = map ((projDir </>) . view entity) $ maybe [] sourceDirs $ view projectDescription proj
-		projDir = view projectPath proj
-
--- | Watch for standalone source
-watchModule :: Watcher -> ModuleLocation -> IO ()
-watchModule w (FileModule f Nothing) = watchDir w (takeDirectory f) isSource WatchedModule
-watchModule w (FileModule _ (Just proj)) = watchProject w proj []
-watchModule _ _ = return ()
-
--- | Watch for top of package-db stack
-watchPackageDb :: Watcher -> PackageDbStack -> [String] -> IO ()
-watchPackageDb w pdbs opts = case topPackageDb pdbs of
-	GlobalDb -> return () -- TODO: Watch for global package-db
-	UserDb -> return () -- TODO: Watch for user package-db
-	(PackageDb pdb) -> watchTree w pdb isConf (WatchedPackageDb pdbs opts)
-
--- | Watch for package-db stack
-watchPackageDbStack :: Watcher -> PackageDbStack -> [String] -> IO ()
-watchPackageDbStack w pdbs opts = mapM_ (\pdbs' -> watchPackageDb w pdbs' opts) $ packageDbStacks pdbs
-
-unwatchProject :: Watcher -> Project -> IO ()
-unwatchProject w proj = do
-	mapM_ (unwatchTree w) dirs
-	void $ unwatchDir w projDir
-	where
-		dirs = map ((projDir </>) . view entity) $ maybe [] sourceDirs $ view projectDescription proj
-		projDir = view projectPath proj
-
-unwatchModule :: Watcher -> ModuleLocation -> IO ()
-unwatchModule w (FileModule f Nothing) = void $ unwatchDir w (takeDirectory f)
-unwatchModule _ (FileModule _ (Just _)) = return ()
-unwatchModule _ (InstalledModule _ _ _) = return ()
-unwatchModule _ _ = return ()
-
--- | Unwatch package-db
-unwatchPackageDb :: Watcher -> PackageDb -> IO ()
-unwatchPackageDb _ GlobalDb = return () -- TODO: Unwatch global package-db
-unwatchPackageDb _ UserDb = return () -- TODO: Unwatch user package-db
-unwatchPackageDb w (PackageDb pdb) = void $ unwatchTree w pdb
-
-isSource :: Event -> Bool
-isSource = haskellSource . view eventPath
-
-isCabal :: Event -> Bool
-isCabal = cabalFile . view eventPath
-
-isConf :: Event -> Bool
-isConf (Event _ f _) = takeExtension f == ".conf"
+module HsDev.Watcher (
+	watchProject, watchModule, watchPackageDb, watchPackageDbStack,
+	unwatchProject, unwatchModule, unwatchPackageDb,
+	isSource, isCabal, isConf,
+
+	module System.Directory.Watcher,
+	module HsDev.Watcher.Types
+	) where
+
+import Control.Lens (view)
+import Control.Monad (void)
+import System.FilePath (takeDirectory, takeExtension, (</>))
+
+import System.Directory.Watcher hiding (Watcher)
+import HsDev.Project
+import HsDev.Symbols
+import HsDev.Watcher.Types
+import HsDev.Util
+
+-- | Watch for project sources changes
+watchProject :: Watcher -> Project -> [String] -> IO ()
+watchProject w proj opts = do
+	mapM_ (\dir -> watchTree w dir isSource (WatchedProject proj opts)) dirs
+	watchDir w projDir isCabal (WatchedProject proj opts)
+	where
+		dirs = map ((projDir </>) . view entity) $ maybe [] sourceDirs $ view projectDescription proj
+		projDir = view projectPath proj
+
+-- | Watch for standalone source
+watchModule :: Watcher -> ModuleLocation -> IO ()
+watchModule w (FileModule f Nothing) = watchDir w (takeDirectory f) isSource WatchedModule
+watchModule w (FileModule _ (Just proj)) = watchProject w proj []
+watchModule _ _ = return ()
+
+-- | Watch for top of package-db stack
+watchPackageDb :: Watcher -> PackageDbStack -> [String] -> IO ()
+watchPackageDb w pdbs opts = case topPackageDb pdbs of
+	GlobalDb -> return () -- TODO: Watch for global package-db
+	UserDb -> return () -- TODO: Watch for user package-db
+	(PackageDb pdb) -> watchTree w pdb isConf (WatchedPackageDb pdbs opts)
+
+-- | Watch for package-db stack
+watchPackageDbStack :: Watcher -> PackageDbStack -> [String] -> IO ()
+watchPackageDbStack w pdbs opts = mapM_ (\pdbs' -> watchPackageDb w pdbs' opts) $ packageDbStacks pdbs
+
+unwatchProject :: Watcher -> Project -> IO ()
+unwatchProject w proj = do
+	mapM_ (unwatchTree w) dirs
+	void $ unwatchDir w projDir
+	where
+		dirs = map ((projDir </>) . view entity) $ maybe [] sourceDirs $ view projectDescription proj
+		projDir = view projectPath proj
+
+unwatchModule :: Watcher -> ModuleLocation -> IO ()
+unwatchModule w (FileModule f Nothing) = void $ unwatchDir w (takeDirectory f)
+unwatchModule _ (FileModule _ (Just _)) = return ()
+unwatchModule _ (InstalledModule _ _ _) = return ()
+unwatchModule _ _ = return ()
+
+-- | Unwatch package-db
+unwatchPackageDb :: Watcher -> PackageDb -> IO ()
+unwatchPackageDb _ GlobalDb = return () -- TODO: Unwatch global package-db
+unwatchPackageDb _ UserDb = return () -- TODO: Unwatch user package-db
+unwatchPackageDb w (PackageDb pdb) = void $ unwatchTree w pdb
+
+isSource :: Event -> Bool
+isSource = haskellSource . view eventPath
+
+isCabal :: Event -> Bool
+isCabal = cabalFile . view eventPath
+
+isConf :: Event -> Bool
+isConf (Event _ f _) = takeExtension f == ".conf"
diff --git a/src/System/Directory/Paths.hs b/src/System/Directory/Paths.hs
--- a/src/System/Directory/Paths.hs
+++ b/src/System/Directory/Paths.hs
@@ -1,31 +1,31 @@
-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
-
-module System.Directory.Paths (
-	Paths(..),
-	canonicalize,
-	absolutise,
-	relativise
-	) where
-
-import Control.Lens
-import System.Directory
-import System.FilePath
-
--- | Something with paths inside
-class Paths a where
-	paths :: Traversal' a FilePath
-
-instance Paths FilePath where
-	paths = id
-
--- | Canonicalize all paths
-canonicalize :: Paths a => a -> IO a
-canonicalize = paths canonicalizePath
-
--- | Absolutise paths
-absolutise :: Paths a => FilePath -> a -> a
-absolutise parent = over paths (parent </>)
-
--- | Relativise paths
-relativise :: Paths a => FilePath -> a -> a
-relativise parent = over paths (makeRelative parent)
+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
+
+module System.Directory.Paths (
+	Paths(..),
+	canonicalize,
+	absolutise,
+	relativise
+	) where
+
+import Control.Lens
+import System.Directory
+import System.FilePath
+
+-- | Something with paths inside
+class Paths a where
+	paths :: Traversal' a FilePath
+
+instance Paths FilePath where
+	paths = id
+
+-- | Canonicalize all paths
+canonicalize :: Paths a => a -> IO a
+canonicalize = paths canonicalizePath
+
+-- | Absolutise paths
+absolutise :: Paths a => FilePath -> a -> a
+absolutise parent = over paths (parent </>)
+
+-- | Relativise paths
+relativise :: Paths a => FilePath -> a -> a
+relativise parent = over paths (makeRelative parent)
diff --git a/src/System/Directory/Watcher.hs b/src/System/Directory/Watcher.hs
--- a/src/System/Directory/Watcher.hs
+++ b/src/System/Directory/Watcher.hs
@@ -1,151 +1,151 @@
-{-# LANGUAGE PatternGuards, TemplateHaskell #-}
-
-module System.Directory.Watcher (
-	EventType(..), Event(..), eventType, eventPath, eventTime,
-	Watcher(..),
-	withWatcher,
-	watchDir, watchDir_, unwatchDir, isWatchingDir,
-	watchTree, watchTree_, unwatchTree, isWatchingTree,
-	-- * Working with events
-	readEvent, events, onEvent
-	) where
-
-import Control.Lens (makeLenses)
-import Control.Arrow
-import Control.Concurrent.MVar
-import Control.Concurrent.Chan
-import Control.Monad
-import Data.Map (Map)
-import qualified Data.Map as M
-import Data.Maybe (isJust)
-import Data.String (fromString)
-import Data.Time.Clock.POSIX
-import System.FilePath (takeDirectory, isDrive)
-import System.Directory
-import qualified System.FSNotify as FS
-
--- | Event type
-data EventType = Added | Modified | Removed deriving (Eq, Ord, Enum, Bounded, Read, Show)
-
--- | Event
-data Event = Event {
-	_eventType :: EventType,
-	_eventPath :: FilePath,
-	_eventTime :: POSIXTime }
-
-makeLenses ''Event
-
--- | Directories watcher
-data Watcher a = Watcher {
-	-- | Map from directory to watch stopper
-	watcherDirs :: MVar (Map FilePath (Bool, IO ())),
-	watcherMan :: FS.WatchManager,
-	watcherChan :: Chan (a, Event) }
-
--- | Create watcher
-withWatcher :: (Watcher a -> IO b) -> IO b
-withWatcher act = FS.withManager $ \man -> do
-	ch <- newChan
-	dirs <- newMVar M.empty
-	act $ Watcher dirs man ch
-
--- | Watch directory
-watchDir :: Watcher a -> FilePath -> (Event -> Bool) -> a -> IO ()
-watchDir w f p v = do
-	e <- doesDirectoryExist f
-	when e $ do
-		f' <- canonicalizePath f
-		watching <- isWatchingDir w f'
-		unless watching $ do
-			stop <- FS.watchDir
-				(watcherMan w)
-				(fromString f')
-				(p . fromEvent)
-				(writeChan (watcherChan w) . (,) v . fromEvent)
-			modifyMVar_ (watcherDirs w) $ return . M.insert f' (False, stop)
-
-watchDir_ :: Watcher () -> FilePath -> (Event -> Bool) -> IO ()
-watchDir_ w f p = watchDir w f p ()
-
--- | Unwatch directory, return @False@, if not watched
-unwatchDir :: Watcher a -> FilePath -> IO Bool
-unwatchDir w f = do
-	f' <- canonicalizePath f
-	stop <- modifyMVar (watcherDirs w) $ return . (M.delete f' &&& M.lookup f')
-	maybe (return ()) snd stop
-	return $ isJust stop
-
--- | Check if we are watching dir
-isWatchingDir :: Watcher a -> FilePath -> IO Bool
-isWatchingDir w f = do
-	f' <- canonicalizePath f
-	dirs <- readMVar (watcherDirs w)
-	return $ isWatchingDir' dirs f' || isWatchingParents' dirs f'
-
--- | Watch directory tree
-watchTree :: Watcher a -> FilePath -> (Event -> Bool) -> a -> IO ()
-watchTree w f p v = do
-	e <- doesDirectoryExist f
-	when e $ do
-		f' <- canonicalizePath f
-		watching <- isWatchingTree w f'
-		unless watching $ do
-			stop <- FS.watchTree
-				(watcherMan w)
-				(fromString f')
-				(p . fromEvent)
-				(writeChan (watcherChan w) . (,) v . fromEvent)
-			modifyMVar_ (watcherDirs w) $ return . M.insert f' (True, stop)
-
-watchTree_ :: Watcher () -> FilePath -> (Event -> Bool) -> IO ()
-watchTree_ w f p = watchTree w f p ()
-
--- | Unwatch directory tree
-unwatchTree :: Watcher a -> FilePath -> IO Bool
-unwatchTree w f = do
-	f' <- canonicalizePath f
-	stop <- modifyMVar (watcherDirs w) $ return . (M.delete f' &&& M.lookup f')
-	maybe (return ()) snd stop
-	return $ isJust stop
-
--- | Check if we are watching tree
-isWatchingTree :: Watcher a -> FilePath -> IO Bool
-isWatchingTree w f = do
-	f' <- canonicalizePath f
-	dirs <- readMVar (watcherDirs w)
-	return $ isWatchingTree' dirs f' || isWatchingParents' dirs f'
-
--- | Read next event
-readEvent :: Watcher a -> IO (a, Event)
-readEvent = readChan . watcherChan
-
--- | Get lazy list of events
-events :: Watcher a -> IO [(a, Event)]
-events = getChanContents . watcherChan
-
--- | Process all events
-onEvent :: Watcher a -> (a -> Event -> IO ()) -> IO ()
-onEvent w act = events w >>= mapM_ (uncurry act)
-
-fromEvent :: FS.Event -> Event
-fromEvent e = Event t (FS.eventPath e) (utcTimeToPOSIXSeconds $ FS.eventTime e) where
-	t = case e of
-		FS.Added _ _ -> Added
-		FS.Modified _ _ -> Modified
-		FS.Removed _ _ -> Removed
-
-isWatchingDir' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool
-isWatchingDir' m dir
-	| Just (_, _) <- M.lookup dir m = True
-	| isDrive dir = False
-	| otherwise = isWatchingDir' m (takeDirectory dir)
-
-isWatchingTree' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool
-isWatchingTree' m dir
-	| Just (True, _) <- M.lookup dir m = True
-	| isDrive dir = False
-	| otherwise = isWatchingTree' m (takeDirectory dir)
-
-isWatchingParents' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool
-isWatchingParents' m dir = or (map (isWatchingTree' m) parents) where
-	parents = takeWhile (not . isDrive) $ iterate takeDirectory dir
+{-# LANGUAGE PatternGuards, TemplateHaskell #-}
+
+module System.Directory.Watcher (
+	EventType(..), Event(..), eventType, eventPath, eventTime,
+	Watcher(..),
+	withWatcher,
+	watchDir, watchDir_, unwatchDir, isWatchingDir,
+	watchTree, watchTree_, unwatchTree, isWatchingTree,
+	-- * Working with events
+	readEvent, events, onEvent
+	) where
+
+import Control.Lens (makeLenses)
+import Control.Arrow
+import Control.Concurrent.MVar
+import Control.Concurrent.Chan
+import Control.Monad
+import Data.Map (Map)
+import qualified Data.Map as M
+import Data.Maybe (isJust)
+import Data.String (fromString)
+import Data.Time.Clock.POSIX
+import System.FilePath (takeDirectory, isDrive)
+import System.Directory
+import qualified System.FSNotify as FS
+
+-- | Event type
+data EventType = Added | Modified | Removed deriving (Eq, Ord, Enum, Bounded, Read, Show)
+
+-- | Event
+data Event = Event {
+	_eventType :: EventType,
+	_eventPath :: FilePath,
+	_eventTime :: POSIXTime }
+
+makeLenses ''Event
+
+-- | Directories watcher
+data Watcher a = Watcher {
+	-- | Map from directory to watch stopper
+	watcherDirs :: MVar (Map FilePath (Bool, IO ())),
+	watcherMan :: FS.WatchManager,
+	watcherChan :: Chan (a, Event) }
+
+-- | Create watcher
+withWatcher :: (Watcher a -> IO b) -> IO b
+withWatcher act = FS.withManager $ \man -> do
+	ch <- newChan
+	dirs <- newMVar M.empty
+	act $ Watcher dirs man ch
+
+-- | Watch directory
+watchDir :: Watcher a -> FilePath -> (Event -> Bool) -> a -> IO ()
+watchDir w f p v = do
+	e <- doesDirectoryExist f
+	when e $ do
+		f' <- canonicalizePath f
+		watching <- isWatchingDir w f'
+		unless watching $ do
+			stop <- FS.watchDir
+				(watcherMan w)
+				(fromString f')
+				(p . fromEvent)
+				(writeChan (watcherChan w) . (,) v . fromEvent)
+			modifyMVar_ (watcherDirs w) $ return . M.insert f' (False, stop)
+
+watchDir_ :: Watcher () -> FilePath -> (Event -> Bool) -> IO ()
+watchDir_ w f p = watchDir w f p ()
+
+-- | Unwatch directory, return @False@, if not watched
+unwatchDir :: Watcher a -> FilePath -> IO Bool
+unwatchDir w f = do
+	f' <- canonicalizePath f
+	stop <- modifyMVar (watcherDirs w) $ return . (M.delete f' &&& M.lookup f')
+	maybe (return ()) snd stop
+	return $ isJust stop
+
+-- | Check if we are watching dir
+isWatchingDir :: Watcher a -> FilePath -> IO Bool
+isWatchingDir w f = do
+	f' <- canonicalizePath f
+	dirs <- readMVar (watcherDirs w)
+	return $ isWatchingDir' dirs f' || isWatchingParents' dirs f'
+
+-- | Watch directory tree
+watchTree :: Watcher a -> FilePath -> (Event -> Bool) -> a -> IO ()
+watchTree w f p v = do
+	e <- doesDirectoryExist f
+	when e $ do
+		f' <- canonicalizePath f
+		watching <- isWatchingTree w f'
+		unless watching $ do
+			stop <- FS.watchTree
+				(watcherMan w)
+				(fromString f')
+				(p . fromEvent)
+				(writeChan (watcherChan w) . (,) v . fromEvent)
+			modifyMVar_ (watcherDirs w) $ return . M.insert f' (True, stop)
+
+watchTree_ :: Watcher () -> FilePath -> (Event -> Bool) -> IO ()
+watchTree_ w f p = watchTree w f p ()
+
+-- | Unwatch directory tree
+unwatchTree :: Watcher a -> FilePath -> IO Bool
+unwatchTree w f = do
+	f' <- canonicalizePath f
+	stop <- modifyMVar (watcherDirs w) $ return . (M.delete f' &&& M.lookup f')
+	maybe (return ()) snd stop
+	return $ isJust stop
+
+-- | Check if we are watching tree
+isWatchingTree :: Watcher a -> FilePath -> IO Bool
+isWatchingTree w f = do
+	f' <- canonicalizePath f
+	dirs <- readMVar (watcherDirs w)
+	return $ isWatchingTree' dirs f' || isWatchingParents' dirs f'
+
+-- | Read next event
+readEvent :: Watcher a -> IO (a, Event)
+readEvent = readChan . watcherChan
+
+-- | Get lazy list of events
+events :: Watcher a -> IO [(a, Event)]
+events = getChanContents . watcherChan
+
+-- | Process all events
+onEvent :: Watcher a -> (a -> Event -> IO ()) -> IO ()
+onEvent w act = events w >>= mapM_ (uncurry act)
+
+fromEvent :: FS.Event -> Event
+fromEvent e = Event t (FS.eventPath e) (utcTimeToPOSIXSeconds $ FS.eventTime e) where
+	t = case e of
+		FS.Added _ _ -> Added
+		FS.Modified _ _ -> Modified
+		FS.Removed _ _ -> Removed
+
+isWatchingDir' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool
+isWatchingDir' m dir
+	| Just (_, _) <- M.lookup dir m = True
+	| isDrive dir = False
+	| otherwise = isWatchingDir' m (takeDirectory dir)
+
+isWatchingTree' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool
+isWatchingTree' m dir
+	| Just (True, _) <- M.lookup dir m = True
+	| isDrive dir = False
+	| otherwise = isWatchingTree' m (takeDirectory dir)
+
+isWatchingParents' :: Map FilePath (Bool, IO ()) -> FilePath -> Bool
+isWatchingParents' m dir = or (map (isWatchingTree' m) parents) where
+	parents = takeWhile (not . isDrive) $ iterate takeDirectory dir
diff --git a/src/System/Win32/PowerShell.hs b/src/System/Win32/PowerShell.hs
--- a/src/System/Win32/PowerShell.hs
+++ b/src/System/Win32/PowerShell.hs
@@ -1,214 +1,214 @@
-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, GeneralizedNewtypeDeriving, LambdaCase #-}
-
-module System.Win32.PowerShell (
-	-- * Run PowerShell
-	ps,
-	-- * PowerShell functions
-	startProcess, codepage, outNull,
-	-- * PowerShell monad
-	PS(..), seqPS, emit, emit_, (=:), invoke,
-	-- * Expression
-	Args(..), CmdLet(..), Expr(..), compile,
-	raw, name, lit, var, bra, cbra, (.=), flag, named, call, cmdlet, lambda, (.|), foreach, filter,
-	-- * Convertible
-	ToPS(..),
-	-- * Escape functions
-	translate, translateArg,
-	quote, quoteDouble,
-	escape
-	) where
-
-import Prelude hiding (filter)
-
-import Control.Monad
-import Control.Monad.Writer
-import Data.Char (isAlphaNum)
-import Data.List (intercalate)
-import qualified Data.List as List (filter)
-import Data.Map (Map)
-import qualified Data.Map as M
-
-import HsDev.Tools.Base (ToolM, tool_)
-
-ps :: PS a -> ToolM String
-ps script = tool_ "powershell" ["-Command", compile $ seqPS script]
-
--- start-process
-startProcess :: String -> [String] -> CmdLet
-startProcess n as = cmdlet "start-process" [name n, lit $ intercalate ", " (map translateArg as)] [named "WindowStyle" $ lit "Hidden"]
-
--- chcp
-codepage :: Int -> CmdLet
-codepage n = cmdlet "chcp" [lit n] []
-
--- out-null
-outNull :: CmdLet
-outNull = cmdlet "out-null" [] []
-
-newtype PS a = PS { unPS :: Writer [Expr] a }
-	deriving (Applicative, Functor, Monad, MonadWriter [Expr])
-
-seqPS :: PS a -> Expr
-seqPS (PS act) = case execWriter act of
-	[] -> error "No expressions"
-	es -> foldr1 Sequence es
-
-emit :: Expr -> PS Expr
-emit e = PS $ tell [e] >> return e
-
-emit_ :: Expr -> PS ()
-emit_ = void . emit
-
-infixr 6 =:
-(=:) :: String -> Expr -> PS Expr
-n =: expr = emit_ (n .= expr) >> return (var n)
-
--- | Invoke cmdlet
-invoke :: CmdLet -> PS ()
-invoke = emit_ . Invoke
-
--- | Positional and named args
-data Args = Args [Expr] (Map String (Maybe Expr))
-
-instance Monoid Args where
-	mempty = Args [] M.empty
-	mappend (Args lp ln) (Args rp rn) = Args (lp ++ rp) (M.union ln rn)
-
--- | Call to cmdlet
-data CmdLet = CmdLet {
-	cmdLet :: Expr,
-	cmdArgs :: Args }
-
--- | Expression
-data Expr =
-	Emit String |
-	-- ^ native expression
-	Literal String |
-	-- ^ literal
-	Var String |
-	-- ^ $name
-	Bracket Expr |
-	-- ^ (expr)
-	Assign [String] [Expr] |
-	-- ^ $x, $y, ... = expr1, expr2, ...
-	Invoke CmdLet |
-	-- ^ cmd args... named...
-	Lambda [String] ([Expr] -> Expr) |
-	-- ^ { param($...); expr }
-	Sequence Expr Expr |
-	-- ^ expr1; expr2
-	Pipe Expr CmdLet
-	-- ^ expr | cmd
-
-compile :: Expr -> String
-compile (Emit s) = s
-compile (Literal l) = l
-compile (Var v) = '$':v
-compile (Bracket e) = "(" ++ compile e ++ ")"
-compile (Assign vs es) = intercalate ", " (map ('$':) vs) ++ " = " ++ intercalate ", " (map (compile . cbra) es)
-compile (Invoke (CmdLet e (Args p ns))) = unwords $ List.filter (not . null) [invoke', p', ns'] where
-	invoke' = case e of
-		Var n -> n
-		_ -> unwords ["&", compile $ cbra e]
-	p' = unwords $ map (compile . cbra) p
-	ns' = unwords $ concatMap named' $ M.toList ns where
-		named' :: (String, Maybe Expr) -> [String]
-		named' (n, Just v) = ['-':n, compile $ cbra v]
-		named' (n, Nothing) = ['-':n]
--- { param($File); $args[0]; }
-compile (Lambda ns body) = unwords ["{", param', body', "}"] where
-	param' = "param(" ++ intercalate "," ns ++ ");"
-	body' = compile $ body (map var ns)
-compile (Sequence l r) = compile l ++ "; " ++ compile r
-compile (Pipe e c) = compile e ++ " | " ++ compile (Invoke c)
-
-raw :: String -> Expr
-raw = Emit
-
-name :: String -> Expr
-name = Literal
-
-lit :: ToPS a => a -> Expr
-lit = Literal . toPS
-
-var :: String -> Expr
-var = Var
-
-bra :: Expr -> Expr
-bra = Bracket
-
-cbra :: Expr -> Expr
-cbra e@(Literal _) = e
-cbra v@(Var _) = v
-cbra b = bra b
-
-infixr 6 .= 
-(.=) :: String -> Expr -> Expr
-v .= e = Assign [v] [e]
-
-flag :: String -> (String, Maybe Expr)
-flag n = (n, Nothing)
-
-named :: String -> Expr -> (String, Maybe Expr)
-named n e = (n, Just e)
-
-call :: Expr -> [Expr] -> [(String, Maybe Expr)] -> CmdLet
-call f pos named' = CmdLet f $ Args pos (M.fromList named')
-
-cmdlet :: String -> [Expr] -> [(String, Maybe Expr)] -> CmdLet
-cmdlet n = call (name n)
-
-lambda :: [String] -> ([Expr] -> Expr) -> Expr
-lambda = Lambda
-
-infixr 6 .|
-(.|) :: Expr -> CmdLet -> Expr
-e .| c = Pipe e c
-
-foreach :: (Expr -> Expr) -> CmdLet
-foreach f = cmdlet "%" [f $ var "_"] []
-
-filter :: (Expr -> Expr) -> CmdLet
-filter p = cmdlet "?" [p $ var "_"] []
-
-class ToPS a where
-	toPS :: a -> String
-
-instance ToPS Int where
-	toPS = show
-
-instance ToPS String where
-	toPS = translate
-
-instance ToPS Bool where
-	toPS True = "$true"
-	toPS False = "$false"
-
-instance {-# OVERLAPPABLE #-} ToPS a => ToPS [a] where
-	toPS = intercalate ", " . map toPS
-
-translate :: String -> String
-translate s
-	| all (\ch -> isAlphaNum ch || ch `elem` "-_") s = s
-	| otherwise = '"' : snd (foldr escape' (True, "\"") s)
-	where
-		escape' '"' (_, s') = (True, '\\' : '"' : s')
-		escape' '\\' (True, s') = (True, '\\' : '\\' : s')
-		escape' '\\' (False, s') = (False, '\\' : s')
-		escape' c (_, s') = (False, c : s')
-
-translateArg :: String -> String
-translateArg s
-	| all isAlphaNum s = s
-	| otherwise = "'" ++ translate s ++ "'"
-
-quote :: String -> String
-quote s = "'" ++ concatMap (\case { '\'' -> "''"; ch -> [ch] }) s ++ "'"
-
-quoteDouble :: String -> String
-quoteDouble s = "\"" ++ concatMap (\case { '"' -> "\"\""; ch -> [ch] }) s ++ "\""
-
-escape :: (String -> String) -> String -> String
-escape fn s
-	| all (\ch -> isAlphaNum ch || ch `elem` "-_") s = s
-	| otherwise = fn s
+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, GeneralizedNewtypeDeriving, LambdaCase #-}
+
+module System.Win32.PowerShell (
+	-- * Run PowerShell
+	ps,
+	-- * PowerShell functions
+	startProcess, codepage, outNull,
+	-- * PowerShell monad
+	PS(..), seqPS, emit, emit_, (=:), invoke,
+	-- * Expression
+	Args(..), CmdLet(..), Expr(..), compile,
+	raw, name, lit, var, bra, cbra, (.=), flag, named, call, cmdlet, lambda, (.|), foreach, filter,
+	-- * Convertible
+	ToPS(..),
+	-- * Escape functions
+	translate, translateArg,
+	quote, quoteDouble,
+	escape
+	) where
+
+import Prelude hiding (filter)
+
+import Control.Monad
+import Control.Monad.Writer
+import Data.Char (isAlphaNum)
+import Data.List (intercalate)
+import qualified Data.List as List (filter)
+import Data.Map (Map)
+import qualified Data.Map as M
+
+import HsDev.Tools.Base (ToolM, tool_)
+
+ps :: PS a -> ToolM String
+ps script = tool_ "powershell" ["-Command", compile $ seqPS script]
+
+-- start-process
+startProcess :: String -> [String] -> CmdLet
+startProcess n as = cmdlet "start-process" [name n, lit $ intercalate ", " (map translateArg as)] [named "WindowStyle" $ lit "Hidden"]
+
+-- chcp
+codepage :: Int -> CmdLet
+codepage n = cmdlet "chcp" [lit n] []
+
+-- out-null
+outNull :: CmdLet
+outNull = cmdlet "out-null" [] []
+
+newtype PS a = PS { unPS :: Writer [Expr] a }
+	deriving (Applicative, Functor, Monad, MonadWriter [Expr])
+
+seqPS :: PS a -> Expr
+seqPS (PS act) = case execWriter act of
+	[] -> error "No expressions"
+	es -> foldr1 Sequence es
+
+emit :: Expr -> PS Expr
+emit e = PS $ tell [e] >> return e
+
+emit_ :: Expr -> PS ()
+emit_ = void . emit
+
+infixr 6 =:
+(=:) :: String -> Expr -> PS Expr
+n =: expr = emit_ (n .= expr) >> return (var n)
+
+-- | Invoke cmdlet
+invoke :: CmdLet -> PS ()
+invoke = emit_ . Invoke
+
+-- | Positional and named args
+data Args = Args [Expr] (Map String (Maybe Expr))
+
+instance Monoid Args where
+	mempty = Args [] M.empty
+	mappend (Args lp ln) (Args rp rn) = Args (lp ++ rp) (M.union ln rn)
+
+-- | Call to cmdlet
+data CmdLet = CmdLet {
+	cmdLet :: Expr,
+	cmdArgs :: Args }
+
+-- | Expression
+data Expr =
+	Emit String |
+	-- ^ native expression
+	Literal String |
+	-- ^ literal
+	Var String |
+	-- ^ $name
+	Bracket Expr |
+	-- ^ (expr)
+	Assign [String] [Expr] |
+	-- ^ $x, $y, ... = expr1, expr2, ...
+	Invoke CmdLet |
+	-- ^ cmd args... named...
+	Lambda [String] ([Expr] -> Expr) |
+	-- ^ { param($...); expr }
+	Sequence Expr Expr |
+	-- ^ expr1; expr2
+	Pipe Expr CmdLet
+	-- ^ expr | cmd
+
+compile :: Expr -> String
+compile (Emit s) = s
+compile (Literal l) = l
+compile (Var v) = '$':v
+compile (Bracket e) = "(" ++ compile e ++ ")"
+compile (Assign vs es) = intercalate ", " (map ('$':) vs) ++ " = " ++ intercalate ", " (map (compile . cbra) es)
+compile (Invoke (CmdLet e (Args p ns))) = unwords $ List.filter (not . null) [invoke', p', ns'] where
+	invoke' = case e of
+		Var n -> n
+		_ -> unwords ["&", compile $ cbra e]
+	p' = unwords $ map (compile . cbra) p
+	ns' = unwords $ concatMap named' $ M.toList ns where
+		named' :: (String, Maybe Expr) -> [String]
+		named' (n, Just v) = ['-':n, compile $ cbra v]
+		named' (n, Nothing) = ['-':n]
+-- { param($File); $args[0]; }
+compile (Lambda ns body) = unwords ["{", param', body', "}"] where
+	param' = "param(" ++ intercalate "," ns ++ ");"
+	body' = compile $ body (map var ns)
+compile (Sequence l r) = compile l ++ "; " ++ compile r
+compile (Pipe e c) = compile e ++ " | " ++ compile (Invoke c)
+
+raw :: String -> Expr
+raw = Emit
+
+name :: String -> Expr
+name = Literal
+
+lit :: ToPS a => a -> Expr
+lit = Literal . toPS
+
+var :: String -> Expr
+var = Var
+
+bra :: Expr -> Expr
+bra = Bracket
+
+cbra :: Expr -> Expr
+cbra e@(Literal _) = e
+cbra v@(Var _) = v
+cbra b = bra b
+
+infixr 6 .= 
+(.=) :: String -> Expr -> Expr
+v .= e = Assign [v] [e]
+
+flag :: String -> (String, Maybe Expr)
+flag n = (n, Nothing)
+
+named :: String -> Expr -> (String, Maybe Expr)
+named n e = (n, Just e)
+
+call :: Expr -> [Expr] -> [(String, Maybe Expr)] -> CmdLet
+call f pos named' = CmdLet f $ Args pos (M.fromList named')
+
+cmdlet :: String -> [Expr] -> [(String, Maybe Expr)] -> CmdLet
+cmdlet n = call (name n)
+
+lambda :: [String] -> ([Expr] -> Expr) -> Expr
+lambda = Lambda
+
+infixr 6 .|
+(.|) :: Expr -> CmdLet -> Expr
+e .| c = Pipe e c
+
+foreach :: (Expr -> Expr) -> CmdLet
+foreach f = cmdlet "%" [f $ var "_"] []
+
+filter :: (Expr -> Expr) -> CmdLet
+filter p = cmdlet "?" [p $ var "_"] []
+
+class ToPS a where
+	toPS :: a -> String
+
+instance ToPS Int where
+	toPS = show
+
+instance ToPS String where
+	toPS = translate
+
+instance ToPS Bool where
+	toPS True = "$true"
+	toPS False = "$false"
+
+instance {-# OVERLAPPABLE #-} ToPS a => ToPS [a] where
+	toPS = intercalate ", " . map toPS
+
+translate :: String -> String
+translate s
+	| all (\ch -> isAlphaNum ch || ch `elem` "-_") s = s
+	| otherwise = '"' : snd (foldr escape' (True, "\"") s)
+	where
+		escape' '"' (_, s') = (True, '\\' : '"' : s')
+		escape' '\\' (True, s') = (True, '\\' : '\\' : s')
+		escape' '\\' (False, s') = (False, '\\' : s')
+		escape' c (_, s') = (False, c : s')
+
+translateArg :: String -> String
+translateArg s
+	| all isAlphaNum s = s
+	| otherwise = "'" ++ translate s ++ "'"
+
+quote :: String -> String
+quote s = "'" ++ concatMap (\case { '\'' -> "''"; ch -> [ch] }) s ++ "'"
+
+quoteDouble :: String -> String
+quoteDouble s = "\"" ++ concatMap (\case { '"' -> "\"\""; ch -> [ch] }) s ++ "\""
+
+escape :: (String -> String) -> String -> String
+escape fn s
+	| all (\ch -> isAlphaNum ch || ch `elem` "-_") s = s
+	| otherwise = fn s
diff --git a/tests/Test.hs b/tests/Test.hs
--- a/tests/Test.hs
+++ b/tests/Test.hs
@@ -1,47 +1,47 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module Main (
-	main
-	) where
-
-import Control.Lens
-import Control.Exception (displayException)
-import Data.Aeson hiding (Error)
-import Data.Aeson.Lens
-import Data.Default
-import HsDev
-import Test.Hspec
-import System.FilePath
-import System.Directory
-
-call :: Server -> Command -> IO (Maybe Value)
-call srv c = do
-	r <- inServer srv def c
-	case r of
-		Result v -> return $ Just v
-		Error e -> do
-			expectationFailure $ "command result error: " ++ displayException e
-			return Nothing
-
-exports :: Maybe Value -> [String]
-exports v = v ^.. key "exports" . traverseArray . each . key "name" . each . _Just
-
-main :: IO ()
-main = hspec $ do
-	describe "scan project" $ do
-		dir <- runIO getCurrentDirectory
-		s <- runIO $ startServer (def { serverSilent = True })
-		it "should scan project" $ do
-			_ <- call s $ Scan [dir </> "tests/test-package"] False [] [] [] [] False False
-			one <- call s $ InfoResolve (dir </> "tests/test-package/ModuleOne.hs") True
-			when (["test", "forkIO", "untypedFoo"] /= exports one) $
-				expectationFailure "invalid exports of ModuleOne.hs"
-			two <- call s $ InfoResolve (dir </> "tests/test-package/ModuleTwo.hs") True
-			when (["untypedFoo", "twice", "overloadedStrings"] /= exports two) $
-				expectationFailure "invalid exports of ModuleTwo.hs"
-		it "should pass extensions when checkings" $ do
-			checks <- call s (Check [FileSource (dir </> "tests/test-package/ModuleTwo.hs") Nothing] [])
-			when (("error" :: String) `elem` (checks ^.. traverseArray . key "level" . _Just)) $
-				expectationFailure "there should be no errors, only warnings"
-		_ <- runIO $ call s Exit
-		return ()
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main (
+	main
+	) where
+
+import Control.Lens
+import Control.Exception (displayException)
+import Data.Aeson hiding (Error)
+import Data.Aeson.Lens
+import Data.Default
+import HsDev
+import Test.Hspec
+import System.FilePath
+import System.Directory
+
+call :: Server -> Command -> IO (Maybe Value)
+call srv c = do
+	r <- inServer srv def c
+	case r of
+		Result v -> return $ Just v
+		Error e -> do
+			expectationFailure $ "command result error: " ++ displayException e
+			return Nothing
+
+exports :: Maybe Value -> [String]
+exports v = v ^.. key "exports" . traverseArray . each . key "name" . each . _Just
+
+main :: IO ()
+main = hspec $ do
+	describe "scan project" $ do
+		dir <- runIO getCurrentDirectory
+		s <- runIO $ startServer (def { serverSilent = True })
+		it "should scan project" $ do
+			_ <- call s $ Scan [dir </> "tests/test-package"] False [] [] [] [] False False
+			one <- call s $ InfoResolve (dir </> "tests/test-package/ModuleOne.hs") True
+			when (["test", "forkIO", "untypedFoo"] /= exports one) $
+				expectationFailure "invalid exports of ModuleOne.hs"
+			two <- call s $ InfoResolve (dir </> "tests/test-package/ModuleTwo.hs") True
+			when (["untypedFoo", "twice", "overloadedStrings"] /= exports two) $
+				expectationFailure "invalid exports of ModuleTwo.hs"
+		it "should pass extensions when checkings" $ do
+			checks <- call s (Check [FileSource (dir </> "tests/test-package/ModuleTwo.hs") Nothing] [])
+			when (("error" :: String) `elem` (checks ^.. traverseArray . key "level" . _Just)) $
+				expectationFailure "there should be no errors, only warnings"
+		_ <- runIO $ call s Exit
+		return ()
diff --git a/tests/test-package/ModuleOne.hs b/tests/test-package/ModuleOne.hs
--- a/tests/test-package/ModuleOne.hs
+++ b/tests/test-package/ModuleOne.hs
@@ -1,14 +1,14 @@
-module ModuleOne (
-	test,
-	forkIO,
-	untypedFoo
-	) where
-
-import Control.Concurrent (forkIO)
-
--- | Some test function
-test :: IO ()
-test = return ()
-
--- | Some function without type
-untypedFoo x y = x + y
+module ModuleOne (
+	test,
+	forkIO,
+	untypedFoo
+	) where
+
+import Control.Concurrent (forkIO)
+
+-- | Some test function
+test :: IO ()
+test = return ()
+
+-- | Some function without type
+untypedFoo x y = x + y
diff --git a/tools/hsautofix.hs b/tools/hsautofix.hs
--- a/tools/hsautofix.hs
+++ b/tools/hsautofix.hs
@@ -1,87 +1,87 @@
-{-# LANGUAGE LambdaCase #-}
-
-module Main (
-	main
-	) where
-
-import Control.Lens (preview)
-import Control.Arrow ((***))
-import Control.Monad (liftM)
-import Control.Monad.IO.Class (liftIO)
-import Control.Monad.Except (throwError)
-import Data.Aeson hiding (Error)
-import Data.List (partition, sort)
-import Data.Maybe (mapMaybe)
-import System.FilePath (normalise)
-import System.Directory (canonicalizePath)
-import Text.Read (readMaybe)
-
-import System.Directory.Paths (canonicalize)
-import HsDev.Symbols (moduleFile)
-import HsDev.Symbols.Location (ModuleLocation(..), regionAt, Position(..))
-import HsDev.Tools.Base
-import HsDev.Tools.Ghc.Check (recalcNotesTabs)
-import HsDev.Tools.AutoFix
-import HsDev.Util (toUtf8, liftE, readFileUtf8, writeFileUtf8, ordNub)
-
-import Tool
-
-data FixCmd = ShowCmd Bool | FixCmd [Int] Bool
-
-parseOutputMessages :: String -> [Note OutputMessage]
-parseOutputMessages = mapMaybe parseOutputMessage . lines
-
-parseOutputMessage :: String -> Maybe (Note OutputMessage)
-parseOutputMessage s = do
-	groups <- matchRx "^(.+):(\\d+):(\\d+):(\\s*(Warning|Error):)?\\s*(.*)$" s
-	l <- readMaybe (groups `at` 2)
-	c <- readMaybe (groups `at` 3)
-	return Note {
-		_noteSource = FileModule (normalise (groups `at` 1)) Nothing,
-		_noteRegion = regionAt (Position l c),
-		_noteLevel = Just $ if groups 5 == Just "Warning" then Warning else Error,
-		_note = outputMessage $ nullToNL (groups `at` 6) }
-
--- | Replace NULL with newline
-nullToNL :: String -> String
-nullToNL = map $ \case
-	'\0' -> '\n'
-	ch -> ch
-
-fixP :: Parser FixCmd
-fixP = subparser $ mconcat [
-	cmd "show" "show what can be auto-fixed" (ShowCmd <$> switch (long "json" <> help "output messages in JSON format")),
-	cmd "fix" "fix selected errors" (FixCmd <$>
-		many (argument auto (metavar "N" <> help "indices of corrections to apply")) <*>
-		switch (long "pure" <> help "don't modify files, just return updated corrections"))]
-
-main :: IO ()
-main = toolMain "hsautofix" "automatically fix some errors" fixP (printExceptT . printResult . go) where
-	go (ShowCmd isJson) = do
-		input <- liftE getContents
-		msgs <- if isJson
-			then maybe (throwError "Can't parse messages") return $ decode (toUtf8 input)
-			else liftIO $ recalcNotesTabs (parseOutputMessages input)
-		mapM (liftE . canonicalize) $ corrections msgs
-	go (FixCmd ns pure') = do
-		input <- liftE getContents
-		corrs <- maybe (throwError "Can't parse messages") return $ decode (toUtf8 input)
-		let
-			check i = i `elem` ns || null ns
-			(fixCorrs, upCorrs) = (map snd *** map snd) $
-				partition (check . fst) $ zip [1..] corrs
-		files <- liftE $ mapM canonicalizePath $ ordNub $ sort $ mapMaybe (preview $ noteSource . moduleFile) corrs
-		let
-			doFix :: FilePath -> Maybe String -> ([Note Correction], Maybe String)
-			doFix file mcts = autoFix fixCorrs' (upCorrs', mcts) where
-				findCorrs :: FilePath -> [Note Correction] -> [Note Correction]
-				findCorrs f = filter ((== Just f) . preview (noteSource . moduleFile))
-				fixCorrs' = findCorrs file fixCorrs
-				upCorrs' = findCorrs file upCorrs
-			runFix file
-				| pure' = return $ fst $ doFix file Nothing
-				| otherwise = do
-					(corrs', Just cts') <- liftM (doFix file) $ liftE $ liftM Just $ readFileUtf8 file
-					liftE $ writeFileUtf8 file cts'
-					return corrs'
-		liftM concat $ mapM runFix files
+{-# LANGUAGE LambdaCase #-}
+
+module Main (
+	main
+	) where
+
+import Control.Lens (preview)
+import Control.Arrow ((***))
+import Control.Monad (liftM)
+import Control.Monad.IO.Class (liftIO)
+import Control.Monad.Except (throwError)
+import Data.Aeson hiding (Error)
+import Data.List (partition, sort)
+import Data.Maybe (mapMaybe)
+import System.FilePath (normalise)
+import System.Directory (canonicalizePath)
+import Text.Read (readMaybe)
+
+import System.Directory.Paths (canonicalize)
+import HsDev.Symbols (moduleFile)
+import HsDev.Symbols.Location (ModuleLocation(..), regionAt, Position(..))
+import HsDev.Tools.Base
+import HsDev.Tools.Ghc.Check (recalcNotesTabs)
+import HsDev.Tools.AutoFix
+import HsDev.Util (toUtf8, liftE, readFileUtf8, writeFileUtf8, ordNub)
+
+import Tool
+
+data FixCmd = ShowCmd Bool | FixCmd [Int] Bool
+
+parseOutputMessages :: String -> [Note OutputMessage]
+parseOutputMessages = mapMaybe parseOutputMessage . lines
+
+parseOutputMessage :: String -> Maybe (Note OutputMessage)
+parseOutputMessage s = do
+	groups <- matchRx "^(.+):(\\d+):(\\d+):(\\s*(Warning|Error):)?\\s*(.*)$" s
+	l <- readMaybe (groups `at` 2)
+	c <- readMaybe (groups `at` 3)
+	return Note {
+		_noteSource = FileModule (normalise (groups `at` 1)) Nothing,
+		_noteRegion = regionAt (Position l c),
+		_noteLevel = Just $ if groups 5 == Just "Warning" then Warning else Error,
+		_note = outputMessage $ nullToNL (groups `at` 6) }
+
+-- | Replace NULL with newline
+nullToNL :: String -> String
+nullToNL = map $ \case
+	'\0' -> '\n'
+	ch -> ch
+
+fixP :: Parser FixCmd
+fixP = subparser $ mconcat [
+	cmd "show" "show what can be auto-fixed" (ShowCmd <$> switch (long "json" <> help "output messages in JSON format")),
+	cmd "fix" "fix selected errors" (FixCmd <$>
+		many (argument auto (metavar "N" <> help "indices of corrections to apply")) <*>
+		switch (long "pure" <> help "don't modify files, just return updated corrections"))]
+
+main :: IO ()
+main = toolMain "hsautofix" "automatically fix some errors" fixP (printExceptT . printResult . go) where
+	go (ShowCmd isJson) = do
+		input <- liftE getContents
+		msgs <- if isJson
+			then maybe (throwError "Can't parse messages") return $ decode (toUtf8 input)
+			else liftIO $ recalcNotesTabs (parseOutputMessages input)
+		mapM (liftE . canonicalize) $ corrections msgs
+	go (FixCmd ns pure') = do
+		input <- liftE getContents
+		corrs <- maybe (throwError "Can't parse messages") return $ decode (toUtf8 input)
+		let
+			check i = i `elem` ns || null ns
+			(fixCorrs, upCorrs) = (map snd *** map snd) $
+				partition (check . fst) $ zip [1..] corrs
+		files <- liftE $ mapM canonicalizePath $ ordNub $ sort $ mapMaybe (preview $ noteSource . moduleFile) corrs
+		let
+			doFix :: FilePath -> Maybe String -> ([Note Correction], Maybe String)
+			doFix file mcts = autoFix fixCorrs' (upCorrs', mcts) where
+				findCorrs :: FilePath -> [Note Correction] -> [Note Correction]
+				findCorrs f = filter ((== Just f) . preview (noteSource . moduleFile))
+				fixCorrs' = findCorrs file fixCorrs
+				upCorrs' = findCorrs file upCorrs
+			runFix file
+				| pure' = return $ fst $ doFix file Nothing
+				| otherwise = do
+					(corrs', Just cts') <- liftM (doFix file) $ liftE $ liftM Just $ readFileUtf8 file
+					liftE $ writeFileUtf8 file cts'
+					return corrs'
+		liftM concat $ mapM runFix files
diff --git a/tools/hscabal.hs b/tools/hscabal.hs
--- a/tools/hscabal.hs
+++ b/tools/hscabal.hs
@@ -1,10 +1,10 @@
-module Main (
-	main
-	) where
-
-import HsDev.Tools.Cabal
-
-import Tool
-
-main :: IO ()
-main = toolMain "hscabal" "cabal tool" (many (strArgument (metavar "package"))) $ printExceptT . printResult . cabalList
+module Main (
+	main
+	) where
+
+import HsDev.Tools.Cabal
+
+import Tool
+
+main :: IO ()
+main = toolMain "hscabal" "cabal tool" (many (strArgument (metavar "package"))) $ printExceptT . printResult . cabalList
diff --git a/tools/hsclearimports.hs b/tools/hsclearimports.hs
--- a/tools/hsclearimports.hs
+++ b/tools/hsclearimports.hs
@@ -1,44 +1,44 @@
-module Main (
-	main
-	) where
-
-import Control.Lens (view)
-import Control.Exception (finally)
-import Control.Monad.Except
-import System.Directory
-
-import HsDev.Tools.ClearImports (clearImports)
-import HsDev.Symbols (locateSourceDir)
-import HsDev.Project (entity)
-
-import Tool
-
-data Opts = Opts {
-	optsFile :: String,
-	optsGHC :: [String],
-	optsHideImportList :: Bool,
-	optsMaxImportList :: Maybe Int }
-
-opts :: Parser Opts
-opts = Opts <$>
-	strArgument (metavar "file" <> help "file to clear imports in") <*>
-	many (strOption (long "ghc" <> short 'g' <> metavar "GHC_OPT" <> help "options for GHC")) <*>
-	switch (long "hide-import-list" <> help "hide import list") <*>
-	optional (option auto (long "max-import-list" <> metavar "N" <> help "hide long import lists"))
-
-main :: IO ()
-main = toolMain "hsclearimports" "clears imports in haskell source" opts $ \opts' -> do
-	file <- canonicalizePath (optsFile opts')
-	mroot <- liftM (fmap $ view entity) $ locateSourceDir file
-	cur <- getCurrentDirectory
-	flip finally (setCurrentDirectory cur) $ do
-		maybe (return ()) setCurrentDirectory mroot
-		void $ runExceptT $ catchError
-			(clearImports (optsGHC opts') file >>= mapM_ (liftIO . putStrLn . format opts'))
-			(\e -> liftIO (putStrLn $ "Error: " ++ e))
-	where
-		format :: Opts -> (String, String) -> String
-		format as (imp, lst)
-			| optsHideImportList as = imp
-			| maybe False (length lst >) (optsMaxImportList as) = imp
-			| otherwise = imp ++ " (" ++ lst ++ ")"
+module Main (
+	main
+	) where
+
+import Control.Lens (view)
+import Control.Exception (finally)
+import Control.Monad.Except
+import System.Directory
+
+import HsDev.Tools.ClearImports (clearImports)
+import HsDev.Symbols (locateSourceDir)
+import HsDev.Project (entity)
+
+import Tool
+
+data Opts = Opts {
+	optsFile :: String,
+	optsGHC :: [String],
+	optsHideImportList :: Bool,
+	optsMaxImportList :: Maybe Int }
+
+opts :: Parser Opts
+opts = Opts <$>
+	strArgument (metavar "file" <> help "file to clear imports in") <*>
+	many (strOption (long "ghc" <> short 'g' <> metavar "GHC_OPT" <> help "options for GHC")) <*>
+	switch (long "hide-import-list" <> help "hide import list") <*>
+	optional (option auto (long "max-import-list" <> metavar "N" <> help "hide long import lists"))
+
+main :: IO ()
+main = toolMain "hsclearimports" "clears imports in haskell source" opts $ \opts' -> do
+	file <- canonicalizePath (optsFile opts')
+	mroot <- liftM (fmap $ view entity) $ locateSourceDir file
+	cur <- getCurrentDirectory
+	flip finally (setCurrentDirectory cur) $ do
+		maybe (return ()) setCurrentDirectory mroot
+		void $ runExceptT $ catchError
+			(clearImports (optsGHC opts') file >>= mapM_ (liftIO . putStrLn . format opts'))
+			(\e -> liftIO (putStrLn $ "Error: " ++ e))
+	where
+		format :: Opts -> (String, String) -> String
+		format as (imp, lst)
+			| optsHideImportList as = imp
+			| maybe False (length lst >) (optsMaxImportList as) = imp
+			| otherwise = imp ++ " (" ++ lst ++ ")"
diff --git a/tools/hshayoo.hs b/tools/hshayoo.hs
--- a/tools/hshayoo.hs
+++ b/tools/hshayoo.hs
@@ -1,22 +1,22 @@
-module Main (
-	main
-	) where
-
-import Data.Maybe
-
-import HsDev.Tools.Hayoo
-
-import Tool
-
-data HayooOpts = HayooOpts String Int Int
-
-hayooOpts :: Parser HayooOpts
-hayooOpts = HayooOpts <$>
-	strArgument (help "hayoo query") <*>
-	(option auto (long "page" <> short 'p' <> help "page number (0 by default") <|> pure 0) <*>
-	(option auto (long "pages" <> short 'n' <> help "pages count (1 by default") <|> pure 1)
-
-main :: IO ()
-main = toolMain "hshayoo" "hayoo search" hayooOpts $ \(HayooOpts q page pages) -> printExceptT $ printResult $
-	liftM concat $ forM [page .. page + pred pages] $ \i ->
-		liftM (mapMaybe hayooAsDeclaration . resultResult) $ hayoo q (Just i)
+module Main (
+	main
+	) where
+
+import Data.Maybe
+
+import HsDev.Tools.Hayoo
+
+import Tool
+
+data HayooOpts = HayooOpts String Int Int
+
+hayooOpts :: Parser HayooOpts
+hayooOpts = HayooOpts <$>
+	strArgument (help "hayoo query") <*>
+	(option auto (long "page" <> short 'p' <> help "page number (0 by default") <|> pure 0) <*>
+	(option auto (long "pages" <> short 'n' <> help "pages count (1 by default") <|> pure 1)
+
+main :: IO ()
+main = toolMain "hshayoo" "hayoo search" hayooOpts $ \(HayooOpts q page pages) -> printExceptT $ printResult $
+	liftM concat $ forM [page .. page + pred pages] $ \i ->
+		liftM (mapMaybe hayooAsDeclaration . resultResult) $ hayoo q (Just i)
diff --git a/tools/hsinspect.hs b/tools/hsinspect.hs
--- a/tools/hsinspect.hs
+++ b/tools/hsinspect.hs
@@ -1,52 +1,52 @@
-{-# LANGUAGE OverloadedStrings, ViewPatterns #-}
-
-module Main (
-	main
-	) where
-
-import Control.Monad (liftM, (>=>))
-import Control.Monad.IO.Class
-import System.Directory (canonicalizePath)
-import System.FilePath (takeExtension)
-
-import HsDev.Error
-import HsDev.Inspect (inspectContents, inspectDocs, getDefines)
-import HsDev.PackageDb
-import HsDev.Project (readProject)
-import HsDev.Scan (scanModule, scanModify)
-import HsDev.Symbols.Location (ModuleLocation(..))
-import HsDev.Tools.Ghc.Types (inferTypes)
-import HsDev.Tools.Ghc.Worker
-import HsDev.Tools.Ghc.Session (ghcSession)
-
-import Tool
-
-data Opts = Opts (Maybe String) [String]
-
-opts :: Parser Opts
-opts = Opts <$>
-	optional (strArgument (metavar "what" <> help "depending of what <what> is, inspect installed module, source file (.hs), cabal file (.cabal) or contents, passes as input if no <what> specified")) <*>
-	many (strOption (metavar "GHC_OPT" <> long "ghc" <> short 'g' <> help "options to pass to GHC"))
-
-main :: IO ()
-main = toolMain "hsinspect" "haskell inspect" opts (runToolClient . inspect') where
-	inspect' :: Opts -> ClientM IO Value
-	inspect' (Opts Nothing ghcs) = do
-		cts <- liftIO getContents
-		defs <- liftIO getDefines
-		liftM toJSON $ liftIO $ hsdevLift $ inspectContents "stdin" defs ghcs cts
-	inspect' (Opts (Just fname@(takeExtension -> ".hs")) ghcs) = do
-		fname' <- liftIO $ canonicalizePath fname
-		defs <- liftIO getDefines
-		im <- scanModule defs ghcs (FileModule fname' Nothing) Nothing
-		ghc <- ghcWorker	
-		let
-			scanAdditional =
-				scanModify' (\opts' _ -> liftIO . inspectDocs opts') >=>
-				scanModify' (\opts' _ m -> liftIO (inWorker ghc (ghcSession opts' >> inferTypes opts' m Nothing)))
-		toJSON <$> scanAdditional im
-	inspect' (Opts (Just fcabal@(takeExtension -> ".cabal")) _) = do
-		fcabal' <- liftIO $ canonicalizePath fcabal
-		toJSON <$> liftIO (readProject fcabal')
-	inspect' (Opts (Just mname) ghcs) = toJSON <$> scanModule [] ghcs (InstalledModule UserDb Nothing mname) Nothing
-	scanModify' f im = scanModify f im <|> return im
+{-# LANGUAGE OverloadedStrings, ViewPatterns #-}
+
+module Main (
+	main
+	) where
+
+import Control.Monad (liftM, (>=>))
+import Control.Monad.IO.Class
+import System.Directory (canonicalizePath)
+import System.FilePath (takeExtension)
+
+import HsDev.Error
+import HsDev.Inspect (inspectContents, inspectDocs, getDefines)
+import HsDev.PackageDb
+import HsDev.Project (readProject)
+import HsDev.Scan (scanModule, scanModify)
+import HsDev.Symbols.Location (ModuleLocation(..))
+import HsDev.Tools.Ghc.Types (inferTypes)
+import HsDev.Tools.Ghc.Worker
+import HsDev.Tools.Ghc.Session (ghcSession)
+
+import Tool
+
+data Opts = Opts (Maybe String) [String]
+
+opts :: Parser Opts
+opts = Opts <$>
+	optional (strArgument (metavar "what" <> help "depending of what <what> is, inspect installed module, source file (.hs), cabal file (.cabal) or contents, passes as input if no <what> specified")) <*>
+	many (strOption (metavar "GHC_OPT" <> long "ghc" <> short 'g' <> help "options to pass to GHC"))
+
+main :: IO ()
+main = toolMain "hsinspect" "haskell inspect" opts (runToolClient . inspect') where
+	inspect' :: Opts -> ClientM IO Value
+	inspect' (Opts Nothing ghcs) = do
+		cts <- liftIO getContents
+		defs <- liftIO getDefines
+		liftM toJSON $ liftIO $ hsdevLift $ inspectContents "stdin" defs ghcs cts
+	inspect' (Opts (Just fname@(takeExtension -> ".hs")) ghcs) = do
+		fname' <- liftIO $ canonicalizePath fname
+		defs <- liftIO getDefines
+		im <- scanModule defs ghcs (FileModule fname' Nothing) Nothing
+		ghc <- ghcWorker	
+		let
+			scanAdditional =
+				scanModify' (\opts' _ -> liftIO . inspectDocs opts') >=>
+				scanModify' (\opts' _ m -> liftIO (inWorker ghc (ghcSession opts' >> inferTypes opts' m Nothing)))
+		toJSON <$> scanAdditional im
+	inspect' (Opts (Just fcabal@(takeExtension -> ".cabal")) _) = do
+		fcabal' <- liftIO $ canonicalizePath fcabal
+		toJSON <$> liftIO (readProject fcabal')
+	inspect' (Opts (Just mname) ghcs) = toJSON <$> scanModule [] ghcs (InstalledModule UserDb Nothing mname) Nothing
+	scanModify' f im = scanModify f im <|> return im
