hsimport 0.9.0 → 0.10.0
raw patch · 18 files changed
+358/−37 lines, 18 filesdep ~attoparsecdep ~basedep ~cmdargsPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: attoparsec, base, cmdargs, directory, dyre, haskell-src-exts, ilist, microlens, mtl, split, text
API changes (from Hackage documentation)
Files
- CHANGELOG +4/−0
- hsimport.cabal +44/−7
- lib/HsImport/HsImportSpec.hs +2/−2
- lib/HsImport/Parse.hs +34/−17
- lib/HsImport/Types.hs +10/−1
- tests/Main.hs +61/−10
- tests/goldenFiles/ModuleTest37.hs +22/−0
- tests/goldenFiles/ParseTest1.hs +16/−0
- tests/goldenFiles/ParseTest2.hs +12/−0
- tests/goldenFiles/ReplaceCppTest1.hs +18/−0
- tests/goldenFiles/ReplaceCppTest2.hs +21/−0
- tests/goldenFiles/ReplaceCppTest3.hs +12/−0
- tests/inputFiles/ModuleTest37.hs +21/−0
- tests/inputFiles/ParseTest1.hs +18/−0
- tests/inputFiles/ParseTest2.hs +12/−0
- tests/inputFiles/ReplaceCppTest1.hs +18/−0
- tests/inputFiles/ReplaceCppTest2.hs +21/−0
- tests/inputFiles/ReplaceCppTest3.hs +12/−0
CHANGELOG view
@@ -1,3 +1,7 @@+0.10.0+------+* Ensure that no imports are placed inbetween cpp directives+ 0.9.0 ----- * Support haddock comments
hsimport.cabal view
@@ -1,13 +1,13 @@-cabal-version: >=1.9.2+cabal-version: 2.0 name: hsimport-version: 0.9.0+version: 0.10.0 license: BSD3 license-file: LICENSE maintainer: daniel.trstenjak@gmail.com author: Daniel Trstenjak tested-with: ghc ==7.6.2 ghc ==7.6.3 ghc ==7.8.3 ghc ==7.10.1 ghc ==7.10.2 ghc ==8.0.1 ghc ==8.2.1 ghc ==8.4.2 ghc ==8.6.3-synopsis: A command line program for extending the import list of a Haskell source file.+synopsis: Extend the import list of a Haskell source file description: A command line program for extending the import list of a Haskell source file. category: Utils, Development@@ -37,11 +37,12 @@ HsImport.ModuleImport HsImport.SymbolImport HsImport.ImportChange- HsImport.Parse HsImport.PrettyPrint HsImport.Utils HsImport.Types+ HsImport.Parse Paths_hsimport+ default-language: Haskell2010 ghc-options: -W build-depends: base >=3 && <5,@@ -56,20 +57,56 @@ directory >=1.2.0.1 && <1.4, dyre ==0.8.* +library library-internal+ exposed-modules:+ HsImport+ HsImport.Main+ HsImport.Config+ HsImport.Args+ HsImport.ImportPos+ HsImport.HsImportSpec+ HsImport.ModuleImport+ HsImport.SymbolImport+ HsImport.ImportChange+ HsImport.PrettyPrint+ HsImport.Utils+ HsImport.Types+ HsImport.Parse+ Paths_hsimport+ autogen-modules:+ Paths_hsimport+ cpp-options: -DCABAL+ hs-source-dirs: lib+ default-language: Haskell2010+ ghc-options: -W+ build-depends:+ base -any,+ cmdargs -any,+ haskell-src-exts -any,+ ilist -any,+ microlens -any,+ mtl -any,+ text -any,+ split -any,+ attoparsec -any,+ directory -any,+ dyre -any+ executable hsimport main-is: Main.hs cpp-options: -DCABAL hs-source-dirs: exe+ default-language: Haskell2010 ghc-options: -W- ghc-prof-options: -fprof-auto -rtsopts build-depends: base >=3 && <5, hsimport -any -test-suite hsimport-tests+test-suite tests type: exitcode-stdio-1.0 main-is: Main.hs hs-source-dirs: tests+ default-language: Haskell2010 ghc-options: -W build-depends: base >=3 && <5,@@ -77,4 +114,4 @@ tasty-golden >=2.2.0.1 && <2.4, filepath >=1.3.0.1 && <1.5, haskell-src-exts >=1.18.0 && <1.22,- hsimport -any+ library-internal -any
lib/HsImport/HsImportSpec.hs view
@@ -29,7 +29,7 @@ | otherwise = do result <- parseFile $ Args.inputSrcFile args case result of- Right (HS.ParseOk hsModule) -> return $ Right $+ Right (ParseResult (HS.ParseOk hsModule) _) -> return $ Right $ HsImportSpec { sourceFile = Args.inputSrcFile args , parsedSrcFile = hsModule , moduleImport = module_@@ -37,7 +37,7 @@ , saveToFile = saveToFile } - Right (HS.ParseFailed srcLoc error) -> return $ Left (show srcLoc ++ error)+ Right (ParseResult (HS.ParseFailed srcLoc error) _) -> return $ Left (show srcLoc ++ error) Left error -> return $ Left error
lib/HsImport/Parse.hs view
@@ -2,11 +2,11 @@ module HsImport.Parse ( parseFile+ , replaceCpp ) where import qualified Data.Text.IO as TIO import qualified Data.Text as T-import Data.Maybe (fromMaybe) import Data.List (isPrefixOf) import qualified Language.Haskell.Exts as HS import Control.Exception (catch, SomeException)@@ -19,53 +19,70 @@ parseFile :: FilePath -> IO (Either Error ParseResult) parseFile file = do- srcFile <- unlines. replaceCPPByEmptyLine . lines . T.unpack <$> TIO.readFile file+ srcFile <- replaceCpp . T.unpack <$> TIO.readFile file catch (do let result = parseFileContents srcFile case result of- HS.ParseOk _ -> return $ Right result+ HS.ParseOk _ -> return . Right $ ParseResult result Nothing HS.ParseFailed srcLoc _ -> do srcResult <- parseInvalidSource (lines srcFile) (HS.srcLine srcLoc)- return $ Right $ fromMaybe result srcResult)+ return $ case srcResult of+ Just srcRes -> Right srcRes+ Nothing -> Right $ ParseResult result Nothing) (\(e :: SomeException) -> do let srcLines = lines srcFile srcResult <- parseInvalidSource srcLines (length srcLines) return $ maybe (Left $ show e) Right srcResult)+++-- | replace the complete cpp directive, from #ifdef till #endif, by empty lines+replaceCpp :: String -> String+replaceCpp contents = unlines . reverse $ go (lines contents) [] 0 where- -- | replace CPP directives by an empty line- replaceCPPByEmptyLine = map $ \line ->- if "#" `isPrefixOf` line- then ""- else line+ go [] newLines _ = newLines+ go lines newLines ifdefLevel = go lines' newLines' ifdefLevel'+ where+ currLine = head lines+ hasCpp = "#" `isPrefixOf` currLine+ hasIf = "#if" `isPrefixOf` currLine+ hasEndIf = "#endif" `isPrefixOf` currLine+ ifdefLevel' = ifdefLevel + (if hasIf then 1 else 0) - (if hasEndIf then 1 else 0)+ lines' = tail lines+ inCpp = hasCpp || (max ifdefLevel ifdefLevel') > 0+ newLines' = (if inCpp then "" else currLine) : newLines +type HsParseResult = HS.ParseResult Module++ -- | tries to find the maximal part of the source file (from the beginning) that contains -- valid/complete Haskell code parseInvalidSource :: [String] -> Int -> IO (Maybe ParseResult) parseInvalidSource srcLines firstInvalidLine = do parseInvalidSource' 1 firstInvalidLine Nothing 0 where- parseInvalidSource' :: Int -> Int -> Maybe (Int, ParseResult) -> Int -> IO (Maybe ParseResult)+ parseInvalidSource' :: Int -> Int -> Maybe (Int, HsParseResult) -> Int -> IO (Maybe ParseResult) parseInvalidSource' lastValidLine currLastLine maxParseOk iteration | null srcLines || lastValidLine >= currLastLine = return Nothing | iteration >= 10- = return (snd <$> maxParseOk)+ = return $ case maxParseOk of+ Just (lastLine, result) -> Just $ ParseResult result (Just lastLine)+ Nothing -> Nothing | otherwise = do catch (case parseFileContents source of result@(HS.ParseOk _)- | nextLine == currLastLine ->- return $ Just result- | otherwise ->- parseInvalidSource' nextLine currLastLine (maxParseOk' result) iteration'+ | nextLine == currLastLine -> return . Just $ ParseResult result (Just lastValidLine)+ | otherwise -> parseInvalidSource' nextLine currLastLine (maxParseOk' result) iteration' HS.ParseFailed srcLoc _ | HS.srcLine srcLoc == firstInvalidLine -> parseInvalidSource' lastValidLine nextLine maxParseOk iteration'- | otherwise ->++ | otherwise -> parseInvalidSource' nextLine currLastLine maxParseOk iteration') (\(_ :: SomeException) -> parseInvalidSource' lastValidLine nextLine maxParseOk iteration')@@ -83,7 +100,7 @@ _ -> Just (nextLine, nextResult) -parseFileContents :: String -> ParseResult+parseFileContents :: String -> HsParseResult parseFileContents contents = let result = HS.parseFileContentsWithComments parseMode contents in case result of
lib/HsImport/Types.hs view
@@ -14,8 +14,17 @@ type Name = HS.Name Annotation type Module = HS.Module Annotation type ModuleName = String-type ParseResult = HS.ParseResult Module type Error = String++data ParseResult = ParseResult+ { -- | the parse result+ result :: HS.ParseResult Module++ -- | if the source file isn't completely parsable, because e.g.+ -- it contains incomplete Haskell code, then 'lastValidLine'+ -- contains the last line till the source is parsable+ , lastValidLine :: Maybe Int+ } firstSrcLine :: Annotation -> SrcLine firstSrcLine = minimum . map HS.srcSpanStartLine . srcSpans
tests/Main.hs view
@@ -8,11 +8,13 @@ import Data.List (intercalate) import qualified Language.Haskell.Exts as HS import qualified HsImport as HI+import qualified HsImport.Parse as HIP+import HsImport.Types main = defaultMain tests tests :: TestTree-tests = testGroup "Tests" [moduleTests, symbolTests]+tests = testGroup "Tests" [moduleTests, symbolTests, replaceCppTests, parseTests] moduleTests :: TestTree moduleTests = testGroup "Module Tests"@@ -43,16 +45,17 @@ , test "ModuleTest24" $ HI.defaultArgs { HI.moduleName = "Control.Monad", HI.qualifiedName = "CM" } , test "ModuleTest25" $ HI.defaultArgs { HI.moduleName = "Control.Monad", HI.qualifiedName = "Control.Monad" } , test "ModuleTest26" $ HI.defaultArgs { HI.moduleName = "Control.Monad" }- , test_ "ModuleTest27" (HI.defaultConfig { HI.findImportPos = importPosBeforeFirst }) (HI.defaultArgs { HI.moduleName = "Control.Monad" })- , test_ "ModuleTest28" (HI.defaultConfig { HI.findImportPos = importPosAfterFirst }) (HI.defaultArgs { HI.moduleName = "Control.Monad" })- , test_ "ModuleTest29" (HI.defaultConfig { HI.findImportPos = importPosBeforeLast }) (HI.defaultArgs { HI.moduleName = "Control.Monad" })- , test_ "ModuleTest30" (HI.defaultConfig { HI.findImportPos = importPosAfterLast }) (HI.defaultArgs { HI.moduleName = "Control.Monad" })+ , configTest "ModuleTest27" (HI.defaultConfig { HI.findImportPos = importPosBeforeFirst }) (HI.defaultArgs { HI.moduleName = "Control.Monad" })+ , configTest "ModuleTest28" (HI.defaultConfig { HI.findImportPos = importPosAfterFirst }) (HI.defaultArgs { HI.moduleName = "Control.Monad" })+ , configTest "ModuleTest29" (HI.defaultConfig { HI.findImportPos = importPosBeforeLast }) (HI.defaultArgs { HI.moduleName = "Control.Monad" })+ , configTest "ModuleTest30" (HI.defaultConfig { HI.findImportPos = importPosAfterLast }) (HI.defaultArgs { HI.moduleName = "Control.Monad" }) , test "ModuleTest31" $ HI.defaultArgs { HI.moduleName = "Control.Monad", HI.as = "CM" } , test "ModuleTest32" $ HI.defaultArgs { HI.moduleName = "Control.Monad" } , test "ModuleTest33" $ HI.defaultArgs { HI.moduleName = "Control.Monad" } , test "ModuleTest34" $ HI.defaultArgs { HI.moduleName = "Control.Monad" } , test "ModuleTest35" $ HI.defaultArgs { HI.moduleName = "Control.Monad" } , test "ModuleTest36" $ HI.defaultArgs { HI.moduleName = "Control.Monad" }+ , test "ModuleTest37" $ HI.defaultArgs { HI.moduleName = "Control.Monad" } ] @@ -87,19 +90,34 @@ , test "SymbolTest26" $ HI.defaultArgs { HI.moduleName = "Foo", HI.symbolName = "bar" } , test "SymbolTest27" $ HI.defaultArgs { HI.moduleName = "Ugah.Blub", HI.symbolName = "g" } , test "SymbolTest28" $ HI.defaultArgs { HI.moduleName = "Ugah.Blub", HI.symbolName = "d" }- , test_ "SymbolTest29" (HI.defaultConfig { HI.prettyPrint = prettyPrint }) (HI.defaultArgs { HI.moduleName = "X.Y", HI.symbolName = "x" })- , test_ "SymbolTest30" (HI.defaultConfig { HI.prettyPrint = prettyPrint }) (HI.defaultArgs { HI.moduleName = "X.Y", HI.symbolName = "x" })+ , configTest "SymbolTest29" (HI.defaultConfig { HI.prettyPrint = prettyPrint }) (HI.defaultArgs { HI.moduleName = "X.Y", HI.symbolName = "x" })+ , configTest "SymbolTest30" (HI.defaultConfig { HI.prettyPrint = prettyPrint }) (HI.defaultArgs { HI.moduleName = "X.Y", HI.symbolName = "x" }) , test "SymbolTest31" $ HI.defaultArgs { HI.moduleName = "Ugah.Blub", HI.symbolName = "d" } , test "SymbolTest32" $ HI.defaultArgs { HI.moduleName = "Control.Foo", HI.symbolName = "foo" } ] +replaceCppTests :: TestTree+replaceCppTests = testGroup "ReplaceCpp Tests"+ [ replaceCppTest "ReplaceCppTest1"+ , replaceCppTest "ReplaceCppTest2"+ , replaceCppTest "ReplaceCppTest3"+ ]+++parseTests :: TestTree+parseTests = testGroup "Parse Tests"+ [ parseTest "ParseTest1"+ , parseTest "ParseTest2"+ ]++ test :: String -> HI.HsImportArgs -> TestTree-test testName args = test_ testName HI.defaultConfig args+test testName args = configTest testName HI.defaultConfig args -test_ :: String -> HI.Config -> HI.HsImportArgs -> TestTree-test_ testName config args =+configTest :: String -> HI.Config -> HI.HsImportArgs -> TestTree+configTest testName config args = goldenVsFileDiff testName diff goldenFile outputFile command where command = do@@ -107,6 +125,39 @@ case maybeErr of Just err -> hPutStrLn stderr ("hsimport: " ++ err) _ -> return ()++ diff ref new = ["diff", "-u", ref, new]++ goldenFile = "tests" </> "goldenFiles" </> testName <.> "hs"+ outputFile = "tests" </> "outputFiles" </> testName <.> "hs"+ inputFile = "tests" </> "inputFiles" </> testName <.> "hs"+++replaceCppTest :: String -> TestTree+replaceCppTest testName =+ goldenVsFileDiff testName diff goldenFile outputFile command+ where+ command = do+ contents <- readFile inputFile+ writeFile outputFile $ HIP.replaceCpp contents++ diff ref new = ["diff", "-u", ref, new]++ goldenFile = "tests" </> "goldenFiles" </> testName <.> "hs"+ outputFile = "tests" </> "outputFiles" </> testName <.> "hs"+ inputFile = "tests" </> "inputFiles" </> testName <.> "hs"+++parseTest :: String -> TestTree+parseTest testName =+ goldenVsFileDiff testName diff goldenFile outputFile command+ where+ command = do+ Right (ParseResult (HS.ParseOk _) lastValidLine) <- HIP.parseFile inputFile+ contents <- readFile inputFile+ case lastValidLine of+ Just line -> writeFile outputFile $ unlines . take line . lines $ contents+ Nothing -> writeFile outputFile contents diff ref new = ["diff", "-u", ref, new]
+ tests/goldenFiles/ModuleTest37.hs view
@@ -0,0 +1,22 @@+{-# Language PatternGuards #-}+module Blub+ ( blub+ , foo+ , bar+ ) where+#include "Foo.h"+#ifdef FOO+import Control.Applicative+#endif++import Control.BiFunctor+import Control.Monad++f :: Int -> Int+-- ^ Some Haddock doc+-- One line more+f = (+ 3)++g :: Int -> Int+g =+ where
+ tests/goldenFiles/ParseTest1.hs view
@@ -0,0 +1,16 @@+{-# Language PatternGuards #-}+module Blub+ ( blub+ , foo+ , bar+ ) where+#include "Foo.h"+import Ugah.Foo+import Control.Applicative+#ifdef FOO+import Ugah.Blub+#endif+f :: Int -> Int+f = (+ 3)++g :: Int -> Int
+ tests/goldenFiles/ParseTest2.hs view
@@ -0,0 +1,12 @@+{-# Language PatternGuards #-}+module Blub+ ( f+ ) where+#include "Foo.h"+import Ugah.Foo+import Control.Applicative+#ifdef FOO+import Ugah.Blub+#endif+f :: Int -> Int+f i = i + 3
+ tests/goldenFiles/ReplaceCppTest1.hs view
@@ -0,0 +1,18 @@+{-# Language PatternGuards #-}+module Blub+ ( blub+ , foo+ , bar+ ) where++import Ugah.Foo+import Control.Applicative++++f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+ where
+ tests/goldenFiles/ReplaceCppTest2.hs view
@@ -0,0 +1,21 @@+{-# Language PatternGuards #-}+module Blub+ ( blub+ , foo+ , bar+ ) where++import Ugah.Foo+import Control.Applicative+++++++f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+ where
+ tests/goldenFiles/ReplaceCppTest3.hs view
@@ -0,0 +1,12 @@+{-# Language PatternGuards #-}+module Blub+ ( f+ ) where++import Ugah.Foo+import Control.Applicative++++f :: Int -> Int+f i = i + 3
+ tests/inputFiles/ModuleTest37.hs view
@@ -0,0 +1,21 @@+{-# Language PatternGuards #-}+module Blub+ ( blub+ , foo+ , bar+ ) where+#include "Foo.h"+#ifdef FOO+import Control.Applicative+#endif++import Control.BiFunctor++f :: Int -> Int+-- ^ Some Haddock doc+-- One line more+f = (+ 3)++g :: Int -> Int+g =+ where
+ tests/inputFiles/ParseTest1.hs view
@@ -0,0 +1,18 @@+{-# Language PatternGuards #-}+module Blub+ ( blub+ , foo+ , bar+ ) where+#include "Foo.h"+import Ugah.Foo+import Control.Applicative+#ifdef FOO+import Ugah.Blub+#endif+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+ where
+ tests/inputFiles/ParseTest2.hs view
@@ -0,0 +1,12 @@+{-# Language PatternGuards #-}+module Blub+ ( f+ ) where+#include "Foo.h"+import Ugah.Foo+import Control.Applicative+#ifdef FOO+import Ugah.Blub+#endif+f :: Int -> Int+f i = i + 3
+ tests/inputFiles/ReplaceCppTest1.hs view
@@ -0,0 +1,18 @@+{-# Language PatternGuards #-}+module Blub+ ( blub+ , foo+ , bar+ ) where+#include "Foo.h"+import Ugah.Foo+import Control.Applicative+#ifdef FOO+import Ugah.Blub+#endif+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+ where
+ tests/inputFiles/ReplaceCppTest2.hs view
@@ -0,0 +1,21 @@+{-# Language PatternGuards #-}+module Blub+ ( blub+ , foo+ , bar+ ) where+#include "Foo.h"+import Ugah.Foo+import Control.Applicative+#ifdef FOO+import Ugah.Blub+#ifdef GOO+import Ugah.Blub+#endif+#endif+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+ where
+ tests/inputFiles/ReplaceCppTest3.hs view
@@ -0,0 +1,12 @@+{-# Language PatternGuards #-}+module Blub+ ( f+ ) where+#include "Foo.h"+import Ugah.Foo+import Control.Applicative+#ifdef FOO+import Ugah.Blub+#endif+f :: Int -> Int+f i = i + 3