hdevtools 0.1.0.4 → 0.1.0.5
raw patch · 18 files changed
+414/−4 lines, 18 filesdep ~ghc
Dependency ranges changed: ghc
Files
- AUTHORS +1/−0
- README.md +182/−0
- hdevtools.cabal +2/−2
- src/CommandArgs.hs +30/−2
- src/CommandLoop.hs +24/−0
- src/Main.hs +5/−0
- src/Types.hs +1/−0
- test_all_hsenv.sh +2/−0
- test_hsenv.sh +22/−0
- tests/Child.hs +6/−0
- tests/Parent.hs +4/−0
- tests/SampleError.hs +9/−0
- tests/Simple.hs +7/−0
- tests/test_module_file.sh +13/−0
- tests/test_runner.sh +59/−0
- tests/test_sample_error.sh +16/−0
- tests/test_simple_check.sh +11/−0
- tests/test_start_stop.sh +20/−0
+ AUTHORS view
@@ -0,0 +1,1 @@+Bit Connor <mutantlemon@gmail.com>
+ README.md view
@@ -0,0 +1,182 @@+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 [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 ###++I encourage the community to develop integration plugins for Emacs and other+editors. In the mean time, please see the manual integration information below.++### 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.++### 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], but has the advantage that due to its+client-server architecture it is much faster.++[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/
hdevtools.cabal view
@@ -1,5 +1,5 @@ name: hdevtools-version: 0.1.0.4+version: 0.1.0.5 synopsis: Persistent GHC powered background server for FAST haskell development tools description: 'hdevtools' is a backend for text editor plugins, to allow for things such as@@ -60,7 +60,7 @@ build-depends: base == 4.*, cmdargs, directory,- ghc,+ ghc >= 7.2, ghc-paths, syb, network,
src/CommandArgs.hs view
@@ -8,6 +8,8 @@ import System.Console.CmdArgs.Implicit import System.Environment (getProgName)+import System.Info (arch, os)+import qualified Config #ifdef CABAL import Data.Version (showVersion)@@ -22,6 +24,13 @@ "unknown-version (not built with cabal)" #endif +fullVersion :: String+fullVersion =+ concat+ [ programVersion+ , " (ghc-", Config.cProjectVersion, "-", arch, "-", os, ")"+ ]+ data HDevTools = Admin { socket :: Maybe FilePath@@ -35,6 +44,11 @@ , ghcOpts :: [String] , file :: String }+ | ModuleFile+ { socket :: Maybe FilePath+ , ghcOpts :: [String]+ , module_ :: String+ } | Info { socket :: Maybe FilePath , ghcOpts :: [String]@@ -66,6 +80,13 @@ , file = "" } +dummyModuleFile :: HDevTools+dummyModuleFile = ModuleFile+ { socket = Nothing+ , ghcOpts = []+ , module_ = ""+ }+ dummyInfo :: HDevTools dummyInfo = Info { socket = Nothing@@ -99,6 +120,13 @@ , file := def += typFile += argPos 0 += opt "" ] += help "Check a haskell source file for errors and warnings" +moduleFile :: Annotate Ann+moduleFile = record dummyModuleFile+ [ socket := def += typFile += help "socket file to use"+ , ghcOpts := def += typ "OPTION" += help "ghc options"+ , module_ := def += typ "MODULE" += argPos 0+ ] += help "Get the haskell source file corresponding to a module name"+ info :: Annotate Ann info = record dummyInfo [ socket := def += typFile += help "socket file to use"@@ -117,11 +145,11 @@ ] += help "Get the type of the expression at the specified line and column" full :: String -> Annotate Ann-full progName = modes_ [admin += auto, check, info, type_]+full progName = modes_ [admin += auto, check, moduleFile, info, type_] += helpArg [name "h", groupname "Help"] += versionArg [groupname "Help"] += program progName- += summary (progName ++ ": " ++ programVersion)+ += summary (progName ++ ": " ++ fullVersion) loadHDevTools :: IO HDevTools loadHDevTools = do
src/CommandLoop.hs view
@@ -6,6 +6,7 @@ import Control.Monad (when) import Data.IORef+import Data.List (find) import MonadUtils (MonadIO, liftIO) import System.Exit (ExitCode(ExitFailure, ExitSuccess)) import qualified ErrUtils@@ -109,6 +110,29 @@ liftIO $ case flag of GHC.Succeeded -> clientSend (ClientExit ExitSuccess) GHC.Failed -> clientSend (ClientExit (ExitFailure 1))+runCommand _ clientSend (CmdModuleFile moduleName) = do+ moduleGraph <- GHC.getModuleGraph+ case find (moduleSummaryMatchesModuleName moduleName) moduleGraph of+ Nothing ->+ liftIO $ mapM_ clientSend+ [ ClientStderr "Module not found"+ , ClientExit (ExitFailure 1)+ ]+ Just modSummary ->+ case GHC.ml_hs_file (GHC.ms_location modSummary) of+ Nothing ->+ liftIO $ mapM_ clientSend+ [ ClientStderr "Module does not have a source file"+ , ClientExit (ExitFailure 1)+ ]+ Just file ->+ liftIO $ mapM_ clientSend+ [ ClientStdout file+ , ClientExit ExitSuccess+ ]+ where+ moduleSummaryMatchesModuleName modName modSummary =+ modName == (GHC.moduleNameString . GHC.moduleName . GHC.ms_mod) modSummary runCommand state clientSend (CmdInfo file identifier) = do result <- withWarnings state False $ getIdentifierInfo file identifier
src/Main.hs view
@@ -23,6 +23,7 @@ case args of Admin {} -> doAdmin sock args Check {} -> doCheck sock args+ ModuleFile {} -> doModuleFile sock args Info {} -> doInfo sock args Type {} -> doType sock args @@ -39,6 +40,10 @@ progName <- getProgName hPutStrLn stderr "You must provide a command. See:" hPutStrLn stderr $ progName ++ " --help"++doModuleFile :: FilePath -> HDevTools -> IO ()+doModuleFile sock args =+ serverCommand sock (CmdModuleFile (module_ args)) (ghcOpts args) doFileCommand :: String -> (HDevTools -> Command) -> FilePath -> HDevTools -> IO () doFileCommand cmdName cmd sock args
src/Types.hs view
@@ -21,6 +21,7 @@ data Command = CmdCheck FilePath+ | CmdModuleFile String | CmdInfo FilePath String | CmdType FilePath (Int, Int) deriving (Read, Show)
+ test_all_hsenv.sh view
@@ -0,0 +1,2 @@+#!/bin/sh+./test_hsenv.sh `echo .hsenv_* | sed -e 's/.hsenv_//g'`
+ test_hsenv.sh view
@@ -0,0 +1,22 @@+#!/bin/bash++set -e++if [ $# -lt 1 ]+then+ echo "Usage:"+ echo "$0 <hsenv_name> [<hsenv_name2> ...]"+ exit 2+fi++for i in $*+do+ source .hsenv_$i/bin/activate+ cabal build+ export HDEVTOOLS=./dist_$i/build/hdevtools/hdevtools+ ./tests/test_runner.sh+ deactivate_hsenv+done++echo+echo 'All Tests Passed in:' $*
+ tests/Child.hs view
@@ -0,0 +1,6 @@+module Child where++import Parent++child :: String+child = "child of " ++ parent
+ tests/Parent.hs view
@@ -0,0 +1,4 @@+module Parent where++parent :: String+parent = "parent"
+ tests/SampleError.hs view
@@ -0,0 +1,9 @@+-- Sample Module used for testing++-- This module should cause a compilation error:+--+-- Sample2.hs:9:1: parse error (possibly incorrect indentation)++module SampleError where++a = foo
+ tests/Simple.hs view
@@ -0,0 +1,7 @@+-- Sample Module used for testing++-- This module contains no errors or warnings+module Sample1 where++increment :: Int -> Int+increment x = x + 1
+ tests/test_module_file.sh view
@@ -0,0 +1,13 @@+#!/bin/sh++set -e++SOCK=`mktemp -u`++$HDEVTOOLS check --socket=$SOCK Child.hs++PARENT=`$HDEVTOOLS modulefile --socket=$SOCK Parent`++[ "$PARENT" = "./Parent.hs" ]++$HDEVTOOLS --socket=$SOCK --stop-server
+ tests/test_runner.sh view
@@ -0,0 +1,59 @@+#!/bin/sh++set -e++ALL_TESTS="\+ test_start_stop.sh \+ test_simple_check.sh \+ test_sample_error.sh \+ test_module_file.sh \+ "++if [ ! $HDEVTOOLS ]+then+ echo 'You must set the HDEVTOOLS environment variable to the path of the hdevtools binary'+ exit 1+fi++case "$HDEVTOOLS" in+ */*)+ # Convert relative path to absolute:+ export HDEVTOOLS=`pwd`/$HDEVTOOLS+esac++echo $HDEVTOOLS++if [ $# -ne 0 ]+then+ TESTS=$*+else+ TESTS=$ALL_TESTS+ echo 'Running All Tests'+fi++echo '------------------------------------------------------------------------'++cd `dirname $0`++ERRORS=0+for i in $TESTS+do+ echo $i+ echo+ if sh $i+ then+ echo 'Test OK'+ else+ echo 'Test FAILED'+ ERRORS=`expr $ERRORS + 1`+ fi+ echo '------------------------------------------------------------------------'+done++if [ $ERRORS = 0 ]+then+ echo 'All Tests OK'+else+ echo $ERRORS 'FAILED Tests'+fi+exit $ERRORS
+ tests/test_sample_error.sh view
@@ -0,0 +1,16 @@+#!/bin/sh++set -e++SOCK=`mktemp -u`++EXPECTED_ERRORS='SampleError.hs:9:5: Not in scope: `foo'\'''++if ERRORS=`$HDEVTOOLS check --socket=$SOCK SampleError.hs`+then+ false+elsh+ [ "$ERRORS" = "$EXPECTED_ERRORS" ]+fi++$HDEVTOOLS --socket=$SOCK --stop-server
+ tests/test_simple_check.sh view
@@ -0,0 +1,11 @@+#!/bin/sh++set -e++SOCK=`mktemp -u`++ERRORS=`$HDEVTOOLS check --socket=$SOCK Simple.hs`++[ -z "$ERRORS" ]++$HDEVTOOLS --socket=$SOCK --stop-server
+ tests/test_start_stop.sh view
@@ -0,0 +1,20 @@+#!/bin/sh++set -e++SOCK=`mktemp -u`++echo '> Starting the server'+$HDEVTOOLS --socket=$SOCK --start-server++echo '> Checking status'+$HDEVTOOLS --socket=$SOCK --status++echo '> Checking that the socket file exists'+if [ ! -S $SOCK ]; then false; fi++echo '> Stopping the server'+$HDEVTOOLS --socket=$SOCK --stop-server++echo '> Checking that the socket file no longer exists'+if [ -e $SOCK ]; then false; fi