hsimport (empty) → 0.1
raw patch · 9 files changed
+307/−0 lines, 9 filesdep +attoparsecdep +basedep +cmdargssetup-changed
Dependencies added: attoparsec, base, cmdargs, directory, filepath, haskell-src-exts, lens, mtl, process, split, tasty, tasty-golden, text
Files
- HsImport.hs +11/−0
- HsImport/Args.hs +33/−0
- HsImport/ImportSpec.hs +56/−0
- HsImport/Main.hs +46/−0
- LICENSE +24/−0
- Main.hs +13/−0
- Setup.hs +2/−0
- hsimport.cabal +57/−0
- tests/Main.hs +65/−0
+ HsImport.hs view
@@ -0,0 +1,11 @@++module HsImport+ ( HsImportArgs+ , hsImportArgs+ , module HsImport.ImportSpec+ , module HsImport.Main+ ) where++import HsImport.Args+import HsImport.ImportSpec+import HsImport.Main
+ HsImport/Args.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE DeriveDataTypeable #-}++module HsImport.Args + ( HsImportArgs(..)+ , hsImportArgs+ ) where++import System.Console.CmdArgs+++data HsImportArgs = HsImportArgs + { moduleName :: String+ , symbolName :: String+ , inputSrcFile :: FilePath+ , outputSrcFile :: FilePath+ } deriving (Data, Typeable, Show, Eq)+++hsImportArgs :: IO HsImportArgs+hsImportArgs = cmdArgs $ HsImportArgs + { moduleName = def &= help "The module to import"+ , symbolName = def &= help "The symbol to import, if empty, the entire module is imported"+ , outputSrcFile = def &= help "Save modified source file to file, if empty, the source file is modified inplace" &= typFile+ , inputSrcFile = def &= args &= typ "SOURCEFILE"+ }+ &= program "hsimport"+ &= summary summaryInfo+ &= help "A command line program for extending the import list of a Haskell source file."+ &= helpArg [explicit, name "help", name "h"]+ &= versionArg [explicit, name "version", name "v", summary versionInfo]+ where+ versionInfo = "hsimport version 0.1"+ summaryInfo = ""
+ HsImport/ImportSpec.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE TemplateHaskell, PatternGuards #-}++module HsImport.ImportSpec+ ( ImportSpec(..)+ , sourceFile+ , parsedSrcFile+ , moduleToImport+ , symbolToImport+ , saveToFile+ , hsImportSpec+ ) where++import Control.Lens+import qualified Language.Haskell.Exts as HS+import qualified HsImport.Args as Args+import HsImport.Args (HsImportArgs)++data ImportSpec = ImportSpec + { _sourceFile :: FilePath+ , _parsedSrcFile :: HS.Module+ , _moduleToImport :: String+ , _symbolToImport :: Maybe String+ , _saveToFile :: Maybe FilePath+ } deriving (Show) ++makeLenses ''ImportSpec+++type Error = String+hsImportSpec :: HsImportArgs -> IO (Either Error ImportSpec)+hsImportSpec args+ | Just error <- checkArgs args = return $ Left error+ | otherwise = do+ result <- HS.parseFile $ Args.inputSrcFile args+ case result of+ HS.ParseOk modul -> return $ Right $+ ImportSpec (Args.inputSrcFile args) modul+ (Args.moduleName args) symbolName saveToFile++ HS.ParseFailed _ error -> return $ Left error++ where+ symbolName =+ case Args.symbolName args of+ "" -> Nothing+ sym -> Just sym++ saveToFile =+ case Args.outputSrcFile args of+ "" -> Nothing+ fp -> Just fp++ checkArgs args+ | null . Args.inputSrcFile $ args = Just "Missing source file!"+ | null . Args.moduleName $ args = Just "Missing module name!"+ | otherwise = Nothing
+ HsImport/Main.hs view
@@ -0,0 +1,46 @@+{-# Language PatternGuards #-}++module HsImport.Main+ ( hsImport+ ) where++import Control.Lens+import System.Directory (copyFile)+import qualified Data.Text as T+import qualified Data.Text.IO as TIO+import qualified Language.Haskell.Exts as HS+import HsImport.ImportChange+import HsImport.ImportSpec+++hsImport :: ImportSpec -> IO ()+hsImport spec =+ case importChange (spec ^. moduleToImport) (spec ^. symbolToImport) (spec ^. parsedSrcFile) of+ ReplaceImport importDecl -> do+ let numDrops = HS.srcLine . HS.importLoc $ importDecl+ numTakes = max 0 (numDrops - 1)++ modifyImports importDecl (spec ^. sourceFile) (outputFile spec) numTakes numDrops++ AddImport importDecl -> do+ let numTakes = HS.srcLine . HS.importLoc $ importDecl+ numDrops = numTakes++ modifyImports importDecl (spec ^. sourceFile) (outputFile spec) numTakes numDrops++ NoImportChange+ | Just saveTo <- spec ^. saveToFile -> copyFile (spec ^. sourceFile) saveTo+ | otherwise -> return ()+ where+ modifyImports importDecl inputFile outputFile numTakes numDrops = do+ file <- TIO.readFile inputFile+ let importLine = HS.prettyPrint importDecl+ lines_ = lines . T.unpack $ file+ lines_' = take numTakes lines_ ++ [importLine] ++ drop numDrops lines_+ file' = T.pack . unlines $ lines_'++ TIO.writeFile outputFile file'++ outputFile spec+ | Just file <- spec ^. saveToFile = file+ | otherwise = spec ^. sourceFile
+ LICENSE view
@@ -0,0 +1,24 @@+Copyright (c) 2011, Daniel Trstenjak+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:+ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+ * Neither the name of the <organization> nor the+ names of its contributors may be used to endorse or promote products+ derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL DANIEL TRSTENJAK BE LIABLE FOR ANY+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Main.hs view
@@ -0,0 +1,13 @@++module Main where++import System.Exit (exitFailure, exitSuccess)+import HsImport++main :: IO ()+main = do+ args <- hsImportArgs+ maybeSpec <- hsImportSpec args+ case maybeSpec of+ Left error -> putStrLn ("hsimport: " ++ error) >> exitFailure+ Right spec -> hsImport spec >> exitSuccess
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hsimport.cabal view
@@ -0,0 +1,57 @@+Name: hsimport+Version: 0.1+License: BSD3+License-file: LICENSE+Author: Daniel Trstenjak+Maintainer: daniel.trstenjak@gmail.com+Build-Type: Simple+Category: Utils+Cabal-Version: >=1.6+Synopsis: A command line program for extending the import list of a Haskell source file.+Description: A command line program for extending the import list of a Haskell source file.++source-repository head+ type: git+ location: https://github.com/dan-t/hsimport++Executable hsimport+ Main-is: Main.hs+ other-modules: HsImport+ HsImport.Args+ HsImport.ImportSpec+ HsImport.Main+ ghc-options: -W -funbox-strict-fields+ Build-Depends: base >= 3 && < 5,+ cmdargs >= 0.10.5 && < 0.11,+ haskell-src-exts >= 1.14.0 && < 1.15,+ lens >= 3.9.2 && < 4.0,+ mtl >= 2.1.2 && < 2.2,+ text >= 0.11.3.1 && < 0.12,+ split >= 0.2.2 && < 0.3,+ attoparsec >= 0.10.4.0 && < 0.11,+ directory >= 1.2.0.1 && < 1.3++library+ ghc-options: -W -funbox-strict-fields+ exposed-modules: HsImport+ HsImport.Args+ HsImport.ImportSpec+ HsImport.Main+ Build-Depends: base >= 3 && < 5,+ cmdargs >= 0.10.5 && < 0.11,+ haskell-src-exts >= 1.14.0 && < 1.15,+ lens >= 3.9.2 && < 4.0,+ mtl >= 2.1.2 && < 2.2,+ text >= 0.11.3.1 && < 0.12,+ split >= 0.2.2 && < 0.3,+ attoparsec >= 0.10.4.0 && < 0.11,+ directory >= 1.2.0.1 && < 1.3++Executable hsimport-tests+ Main-is: tests/Main.hs+ ghc-options: -W -funbox-strict-fields+ Build-Depends: base >= 3 && < 5,+ tasty >= 0.6 && < 0.7,+ tasty-golden >= 2.2.0.1 && < 2.3,+ filepath >= 1.3.0.1 && < 1.4,+ process >= 1.1.0.2 && < 1.2
+ tests/Main.hs view
@@ -0,0 +1,65 @@++module Main where++import Test.Tasty+import Test.Tasty.Golden+import System.Process+import System.FilePath+import Data.List (intercalate)++main = defaultMain tests++tests :: TestTree+tests = testGroup "Tests" [moduleTests, symbolTests]++moduleTests :: TestTree+moduleTests = testGroup "Module Tests"+ [ hsImportTest "ModuleTest1" "Foo.Bar" ""+ , hsImportTest "ModuleTest2" "Foo.Bar.Blub" ""+ , hsImportTest "ModuleTest3" "Control.Monad" ""+ , hsImportTest "ModuleTest4" "Ugah.Argh2" ""+ , hsImportTest "ModuleTest5" "Control.Monad" ""+ , hsImportTest "ModuleTest6" "Control.Monad" ""+ , hsImportTest "ModuleTest7" "Control.Monad" ""+ , hsImportTest "ModuleTest8" "Control.Monad" ""+ , hsImportTest "ModuleTest9" "Control.Monad" ""+ ]+++symbolTests :: TestTree+symbolTests = testGroup "Symbol Tests"+ [ hsImportTest "SymbolTest1" "Foo.Bar" "foo"+ , hsImportTest "SymbolTest2" "Foo.Bar.Blub" "foo"+ , hsImportTest "SymbolTest3" "Control.Monad" "when"+ , hsImportTest "SymbolTest4" "Ugah.Argh2" "argh"+ , hsImportTest "SymbolTest5" "Control.Monad" "when"+ , hsImportTest "SymbolTest6" "Control.Monad" "unless"+ , hsImportTest "SymbolTest7" "Control.Monad" "unless"+ , hsImportTest "SymbolTest8" "Control.Monad" "unless"+ , hsImportTest "SymbolTest9" "Control.Monad" "unless"+ , hsImportTest "SymbolTest10" "Control.Applicative" "<$>"+ , hsImportTest "SymbolTest11" "Control.Applicative" "<$>"+ , hsImportTest "SymbolTest12" "Control.Applicative" "<$>"+ , hsImportTest "SymbolTest13" "Control.Applicative" "<*"+ , hsImportTest "SymbolTest14" "Control.Applicative" "<*"+ , hsImportTest "SymbolTest15" "Control.Applicative" "*>"+ ]+++hsImportTest :: String -> String -> String -> TestTree+hsImportTest testName moduleName symbolName =+ goldenVsFile testName goldenFile outputFile command+ where+ command = do+ handle <- runCommand $ "hsimport " ++ params+ waitForProcess handle+ return ()++ params = intercalate " " [moduleParam, symbolParam, outputParam, inputFile]+ moduleParam = "-m '" ++ moduleName ++ "'"+ symbolParam = if null symbolName then "" else "-s '" ++ symbolName ++ "'"+ outputParam = "-o '" ++ outputFile ++ "'"++ goldenFile = "tests" </> "goldenFiles" </> testName <.> "hs"+ outputFile = "tests" </> "outputFiles" </> testName <.> "hs"+ inputFile = "tests" </> "inputFiles" </> testName <.> "hs"