packages feed

hdevtools 0.1.2.2 → 0.1.3.0

raw patch · 10 files changed

+307/−73 lines, 10 files

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Changelog +## 0.1.3.0 - 2016-02-29++ * Improved performance in stack projects: The stack configuration is+   updated only when the passed path implies a different `stack.yaml`.+   Performance improved substantially from roughly ~1s to ~0.3s.+ ## 0.1.2.2 - 2016-01-11   * Added type checking support for tests and benchmarks in stack projects.
LICENSE view
@@ -1,4 +1,4 @@-Copyright (C) 2015 The hdevtools Authors (see AUTHORS file)+Copyright (C) 2016 The hdevtools Authors (see AUTHORS file)  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal
+ README.md view
@@ -0,0 +1,230 @@+hdevtools+=========++Persistent GHC powered background server for FAST Haskell development tools+++About+-----++`hdevtools` is a backend for text editor plugins, to allow for things such as+syntax and type checking of Haskell code, and retrieving type information, all+directly from within your text editor.++The advantage that `hdevtools` has over competitors, is that it runs silently+in a persistent background process, and therefore is able to keeps all of your+Haskell modules and dependent libraries loaded in memory. This way, when you+change only a single source file, only it needs to be reloaded and rechecked,+instead of having to reload everything.++This makes `hdevtools` very fast for checking syntax and type errors (runs just+as fast as the `:reload` command in GHCi).++In fact, syntax and type checking is so fast, that you can safely enable auto+checking on every save. Even for huge projects, checking is nearly instant.++Once you start using `hdevtools` and you get used to having your errors shown+to you instantly (without having to switch back and forth between GHCi and your+editor), and shown directly on your code, in your editor (without having to+wait forever for GHC to run) you will wonder how you ever lived without it.++In addition to checking Haskell source code for errors, `hdevtools` has tools+for getting info about identifiers, and getting type information for snippets+of code.++Text Editor Integration+-----------------------++`hdevtools` is designed to be integrated into text editors. The list of current+editor plugins that supply this integration is below.++But before you do anything, you must first install `hdevtools` itself. The+easiest way is from [Stackage][14] using [stack][15]:+```+$ stack install hdevtools+```++**Note:** As stack manages matching GHC versions, the appropriate compiler has+to be available to `hdevtools`. Thus hdevtools has to be started with `stack exec`,+for example using the alias:+```+$ alias hdevtools="stack exec --no-ghc-package-path hdevtools --"+```++Alternatively one can install from [Hackage][1] via cabal install:+```+$ cabal install hdevtools+```++Then you should install one or more of the following editor plugins:++### Vim - [Syntastic][2] ###++[Syntastic][2] is a popular syntax checking plugin for Vim, and is the+recommended Vim integration of `hdevtools` syntax and type checking. Recent+versions of Syntastic(since Sep. 2012) have builtin support for `hdevtools`.++Simply install `hdevtools` (as above) and [Syntastic][2], and it will+automatically check your Haskell files.++[Syntastic][2] will respect the `g:hdevtools_options` variable (the same one as+used by [vim-hdevtools][3], see below). See the section "Specifying GHC+Options" below for details how to use it.++### Vim - [vim-hdevtools][3] ###++In addition to Syntastic, it is recommended that you also use+[`vim-hdevtools`][3] for additional functionality.++[`vim-hdevtools`][3] offers integration with the rest of the `hdevtools` tools,+including retrieving info about the identifier under the cursor, and getting+the type of the code under the cursor. Refer to its documentation for more+details.++### Emacs - [flycheck][5] ###++[Flycheck][5] is a modern batteries-included+syntax checker for Emacs, and there is a [flycheck-hdevtools][6] checker available.++### Atom - [linter][8] ###++There are *two* packages for the [Atom](https://atom.io) editor:+++ [linter-hdevtools][8] quickly finds and underlines type errors in Haskell files,++ [hover-tooltips-hdevtools][9] displays the types of identifiers under the mouse.++### Sublime - [SublimeLinter][10]++[SublimeLinter][10] is a plugin for Sublime Text 3 that provides a framework+for linting code. The [SublimeLinter-contrib-hdevtools][11] plugin uses+`hdevtools` to typecheck Haskell code.++### Manual Editor Integration for any Editor ###++Most editors allow you to run a `make` command, and will then parse the output+for errors and show line numbers, allowing you to jump between errors.++The `hdevtools check` command is suitable for such usage.++For example, in Vim something like this will work:++    :let &makeprg='hdevtools check %'++(Vim will replace the `%` character with the name of the current file). Then+you can run++    :make++And Vim will invoke `hdevtools` to check the current file for errors, and then+show a list of them and allow jumping to them.++See the "Command Line Usage" section below for more information.++Command Line Usage+------------------++Note: When using one of the above editor plugins, you don't really need to know+this.++### Available Commands and Help ###++For the list of commands available, run:++    $ hdevtools --help++To get help for a specific command, run:++    $ hdevtools [COMMAND] --help++For example:++    $ hdevtools check --help++### The `hdevtools` background process ###++The first time `hdevtools` runs a command, it will spawn a background process+that will remain running forever. You can check the status of this background+process by running:++    $ hdevtools --status++You can shutdown the background process by running:++    $ hdevtools --stop-server++Communication with the background process is done through a unix socket file.+The default name is `.hdevtools.sock`, in the current directory. This allows+you to use `hdevtools` with multiple projects simultaneously, without the+background processes getting in the way of each other.++You can use a different socket file name with the `--socket` option, which+should be used for each invocation of `hdevtools`. Remember that when telling+`hdevtools` to check a Haskell file, paths are relative to the path of the+background process, not your current directory. This can cause problems, and+therefore it is recommended that you leave the socket file as the default, and+always run `hdevtools` from the same directory.++You can specify the path to a target file with the `--path` option. This is+useful for integration with IDEs that submit a *copy* of the original source+file (in a temporary directory) to `hdevtools` making it impossible to extract+the `.cabal` information for the file's project. In such cases, you can run as:++    $ hdevtools check -p /path/to/file.hs /tmp/file.hs++and `hdevtools` will use the given path to obtain the `.cabal` information.+++### Specifying GHC Options ###++For most non-trivial projects, you will need to tell `hdevtools` about+additional GHC options that your project requires.++All `hdevtools` commands accept a `-g` flag for this purpose.++For example:++* Your project source code is in the directory `src`+* You want to use the GHC option `-Wall`+* You want to hide the package `transformers` to prevent conflicts++Invoke `hdevtools` with something like this:++    $ hdevtools check -g -isrc -g -Wall -g -hide-package -g transformers Foo.hs++Notice that a `-g` flag is inserted before each GHC option. Don't try to string+multiple GHC options together after a single `-g` flag:++This won't work:++    $ hdevtools check -g '-hide-package transformers' Foo.hs++The Vim plugins allow setting GHC options in the `g:hdevtools_options`+variable.  For example, for the above project, put the following in your+`.vimrc`:++    let g:hdevtools_options = '-g -isrc -g -Wall -g -hide-package -g transformers'++In general, you will need to pass to `hdevtools` the same GHC options that you+would pass to GHCi.++Credits+-------++* `hdevtools` was inspired by [ghcmod][4].+* development moved here from [bitc/hdevtools][12] and [schell/hdevtools][13].++[1]: http://hackage.haskell.org/package/hdevtools+[2]: https://github.com/scrooloose/syntastic+[3]: https://github.com/bitc/vim-hdevtools+[4]: http://www.mew.org/~kazu/proj/ghc-mod/en/+[5]: https://github.com/flycheck/flycheck+[6]: https://github.com/flycheck/flycheck-hdevtools+[7]: https://atom.io+[8]: https://atom.io/packages/linter-hdevtools+[9]: https://atom.io/packages/hover-tooltips-hdevtools+[10]: sublimelinter.com+[11]: https://packagecontrol.io/packages/SublimeLinter-contrib-hdevtools+[12]: https://github.com/bitc/hdevtools+[13]: https://github.com/schell/hdevtools+[14]: https://www.stackage.org/package/hdevtools+[15]: haskellstack.org
hdevtools.cabal view
@@ -1,6 +1,17 @@ name:                hdevtools-version:             0.1.2.2+version:             0.1.3.0 synopsis:            Persistent GHC powered background server for FAST haskell development tools+license:             MIT+license-file:        LICENSE+author:              Bit Connor+maintainer:          Sebastian Nagel <sebastian.nagel@ncoding.at>, +                     Ranjit Jhala <jhala@cs.ucsd.edu>+copyright:           See AUTHORS file+category:            Development+homepage:            https://github.com/hdevtools/hdevtools/+bug-reports:         https://github.com/hdevtools/hdevtools/issues/+build-type:          Simple+cabal-version:       >=1.8 description:     'hdevtools' is a backend for text editor plugins, to allow for things such as     syntax and type checking of Haskell code, and retrieving type information, all@@ -27,18 +38,9 @@     for getting info about identifiers, and getting type information for snippets     of code. -license:             MIT-license-file:        LICENSE-author:              Bit Connor-maintainer:          Sebastian Nagel <sebastian.nagel@ncoding.at>, -                     Ranjit Jhala <jhala@cs.ucsd.edu>-copyright:           See AUTHORS file-category:            Development-homepage:            https://github.com/hdevtools/hdevtools/-bug-reports:         https://github.com/hdevtools/hdevtools/issues/-build-type:          Simple-cabal-version:       >=1.8-extra-source-files:  CHANGELOG.md+extra-source-files:  +  CHANGELOG.md+  README.md  source-repository head   type:                git
src/Cabal.hs view
@@ -168,7 +168,6 @@                                        , ghcOptPackages = overNubListR (filter (\(_, pkgId, _) -> Just (pkgName pkgId) /= mbLibName)) $ (ghcOptPackages ghcOpts')                                        , ghcOptSourcePath = overNubListR (map (baseDir </>)) (ghcOptSourcePath ghcOpts')                                        }-                putStrLn "configuring"                 (ghcInfo,_,_) <- GHC.configure silent Nothing Nothing defaultProgramConfiguration                  return $ Right $ renderGhcOptions ghcInfo ghcOpts@@ -219,7 +218,7 @@   where     pkgDbKey = "package-db:"     parse = head . filter (pkgDbKey `isPrefixOf`) . lines-    extractValue = fst . break isSpace . dropWhile isSpace . drop (length pkgDbKey)+    extractValue = fst . break (`elem` "\n\r") . dropWhile isSpace . drop (length pkgDbKey)   findCabalFile :: FilePath -> IO (Maybe FilePath)
src/CommandLoop.hs view
@@ -3,7 +3,7 @@     ( newCommandLoopState     , Config(..)     , CabalConfig(..)-    , newConfig+    , updateConfig     , startCommandLoop     ) where @@ -49,7 +49,7 @@     , cabalConfigOpts :: [String]     , cabalConfigLastUpdatedAt :: EpochTime     }-    deriving Eq+    deriving (Eq, Show)  mkCabalConfig :: FilePath -> [String] -> IO CabalConfig mkCabalConfig path opts = do@@ -64,17 +64,23 @@     , configCabal   :: Maybe CabalConfig     , configStack   :: Maybe StackConfig     }-    deriving Eq+    deriving (Eq, Show) -newConfig :: CommandExtra -> IO Config-newConfig cmdExtra = do-    mbCabalConfig <- traverse (\path -> mkCabalConfig path (ceCabalOptions cmdExtra)) $ ceCabalConfig cmdExtra-    mbStackConfig <- getStackConfig cmdExtra+updateConfig :: Maybe Config -> CommandExtra -> IO Config+updateConfig mConfig cmdExtra = do+    mbCabalConfig <- traverse (\path -> mkCabalConfig path (ceCabalOptions cmdExtra)) $+      ceCabalFilePath cmdExtra +    mbStackConfig <- if (stackYaml <$> msc) == (ceStackYamlPath cmdExtra)+      then return msc+      else getStackConfig cmdExtra+     return $ Config { configGhcOpts = "-O0" : ceGhcOptions cmdExtra                     , configCabal = mbCabalConfig                     , configStack = mbStackConfig                     }+ where+  msc = mConfig >>= configStack  type CommandObj = (Command, Config) 
src/Main.hs view
@@ -17,6 +17,7 @@ import CommandArgs import Daemonize (daemonize) import Server (startServer, createListenSocket)+import Stack (findStackYaml) import Types (Command(..), CommandExtra(..), emptyCommandExtra)  absoluteFilePath :: FilePath -> IO FilePath@@ -56,11 +57,13 @@     let argPath = pathArg args     dir  <- maybe getCurrentDirectory (return . takeDirectory) argPath     mCabalFile <- findCabalFile dir >>= traverse absoluteFilePath+    mStackYaml <- findStackYaml dir     let extra = emptyCommandExtra-                    { ceGhcOptions  = ghcOpts args-                    , ceCabalConfig = mCabalFile-                    , cePath        = argPath+                    { cePath = argPath+                    , ceGhcOptions  = ghcOpts args+                    , ceCabalFilePath = mCabalFile                     , ceCabalOptions = cabalOpts args+                    , ceStackYamlPath = mStackYaml                     }     let defaultSocketPath = maybe "" takeDirectory mCabalFile </> defaultSocketFile     let sock = fromMaybe defaultSocketPath $ socket args
src/Server.hs view
@@ -10,7 +10,7 @@ import System.IO (Handle, hClose, hFlush, hGetLine, hPutStrLn) import System.IO.Error (ioeGetErrorType, isAlreadyInUseError, isDoesNotExistError) -import CommandLoop (newCommandLoopState, Config, newConfig, startCommandLoop)+import CommandLoop (newCommandLoopState, Config, updateConfig, startCommandLoop) import Types (ClientDirective(..), Command, emptyCommandExtra, ServerDirective(..)) import Util (readMaybe) @@ -38,8 +38,9 @@     go sock = do         state <- newCommandLoopState         currentClient <- newIORef Nothing-        config <- newConfig emptyCommandExtra-        startCommandLoop state (clientSend currentClient) (getNextCommand currentClient sock) config Nothing+        configRef <- newIORef Nothing+        config <- updateConfig Nothing emptyCommandExtra+        startCommandLoop state (clientSend currentClient) (getNextCommand currentClient sock configRef) config Nothing      removeSocketFile :: IO ()     removeSocketFile = do@@ -60,8 +61,8 @@     ignoreEPipe = handleJust (guard . isEPipe) (const $ return ())     isEPipe = (==ResourceVanished) . ioeGetErrorType -getNextCommand :: IORef (Maybe Handle) -> Socket -> IO (Maybe (Command, Config))-getNextCommand currentClient sock = do+getNextCommand :: IORef (Maybe Handle) -> Socket -> IORef (Maybe Config) -> IO (Maybe (Command, Config))+getNextCommand currentClient sock config = do     checkCurrent <- readIORef currentClient     case checkCurrent of         Just h -> hClose h@@ -74,16 +75,18 @@         Nothing -> do             clientSend currentClient $ ClientUnexpectedError $                 "The client sent an invalid message to the server: " ++ show msg-            getNextCommand currentClient sock+            getNextCommand currentClient sock config         Just (SrvCommand cmd cmdExtra) -> do-            config <- newConfig cmdExtra-            return $ Just (cmd, config)+            curConfig <- readIORef config+            config' <- updateConfig curConfig cmdExtra+            writeIORef config (Just config')+            return $ Just (cmd, config')         Just SrvStatus -> do             mapM_ (clientSend currentClient) $                 [ ClientStdout "Server is running."                 , ClientExit ExitSuccess                 ]-            getNextCommand currentClient sock+            getNextCommand currentClient sock config         Just SrvExit -> do             mapM_ (clientSend currentClient) $                 [ ClientStdout "Shutting down server."
src/Stack.hs view
@@ -2,7 +2,7 @@ module Stack       ( -- * The bits of information needed from `stack`         StackConfig (..)-        -- * Run `stack exec` to compute @StackConfig@+      , findStackYaml       , getStackConfig       ) where @@ -21,45 +21,28 @@ import Control.Exception import Types ---- | This module adds support for `stack`, as follows:---   1. Figure out if the target-file is in a stack project,---   2. If `stack` in available in PATH, run `stack exec` to extract---      `StackConfig`---   3. The `StackConfig` is used to suitably alter the cabal ConfigFlags in---      Cabal.hs-- -- TODO: Move into Types?-data StackConfig = StackConfig { stackDist :: FilePath+data StackConfig = StackConfig { stackYaml :: FilePath+                               , stackDist :: FilePath                                , stackDbs  :: [FilePath]                                }                    deriving (Eq, Show) ----------------------------------------------------------------------------------getStackConfig :: CommandExtra -> IO (Maybe StackConfig)----------------------------------------------------------------------------------getStackConfig ce = case cePath ce of-                      Nothing -> return Nothing-                      Just p  -> getStackConfig' p--getStackConfig' :: FilePath -> IO (Maybe StackConfig)-getStackConfig' p = do-  mbYaml <- getStackYaml p-  case mbYaml of-    Nothing -> return Nothing-    Just _  -> do mdbs <- getStackDbs p-                  mdst <- getStackDist p-                  return $ StackConfig <$> mdst <*> mdbs-----------------------------------------------------------------------------------getStackYaml :: FilePath -> IO (Maybe FilePath)----------------------------------------------------------------------------------getStackYaml p = listToMaybe <$> filterM doesFileExist paths+-- | Search for a @stack.yaml@ upwards in given file path tree.+findStackYaml :: FilePath -> IO (Maybe FilePath)+findStackYaml p = listToMaybe <$> filterM doesFileExist paths   where     paths      = [ d </> "stack.yaml" | d <- pathsToRoot dir]     dir        = takeDirectory p +-- | Run @stack path@ to compute @StackConfig@+getStackConfig :: CommandExtra -> IO (Maybe StackConfig)+getStackConfig CommandExtra { ceStackYamlPath = Nothing } = return Nothing+getStackConfig CommandExtra { ceStackYamlPath = Just p } = do+    dbs <- getStackDbs p+    dist <- getStackDist p+    return $ StackConfig p <$> dist <*> dbs+ pathsToRoot :: FilePath -> [FilePath] pathsToRoot p   | p == parent = [p]@@ -84,7 +67,7 @@                        Just pp -> Just <$> extractDbs pp                        Nothing -> return Nothing   where-    cmd       = "stack --verbosity quiet exec printenv GHC_PACKAGE_PATH"+    cmd       = "stack path --ghc-package-path"  extractDbs :: String -> IO [FilePath] extractDbs = filterM doesDirectoryExist . stringPaths
src/Types.hs view
@@ -9,17 +9,19 @@ import System.Exit (ExitCode)  data CommandExtra = CommandExtra-  { ceGhcOptions  :: [String]-  , ceCabalConfig :: Maybe FilePath-  , cePath        :: Maybe FilePath+  { cePath :: Maybe FilePath+  , ceGhcOptions :: [String]+  , ceCabalFilePath :: Maybe FilePath   , ceCabalOptions :: [String]+  , ceStackYamlPath :: Maybe FilePath   } deriving (Read, Show)  emptyCommandExtra :: CommandExtra-emptyCommandExtra = CommandExtra { ceGhcOptions  = []-                                 , ceCabalConfig = Nothing-                                 , cePath        = Nothing+emptyCommandExtra = CommandExtra { cePath = Nothing+                                 , ceGhcOptions  = []+                                 , ceCabalFilePath = Nothing                                  , ceCabalOptions = []+                                 , ceStackYamlPath = Nothing                                  }  data ServerDirective