packages feed

hsimport 0.4 → 0.5

raw patch · 28 files changed

+651/−159 lines, 28 filesdep +dyredep ~lensPVP ok

version bump matches the API change (PVP)

Dependencies added: dyre

Dependency ranges changed: lens

API changes (from Hackage documentation)

- HsImport: data HsImportArgs
- HsImport: hsImportArgs :: IO HsImportArgs
- HsImport.Main: hsImport :: ImportSpec -> IO ()
+ HsImport: After :: ImportDecl -> ImportPos
+ HsImport: Before :: ImportDecl -> ImportPos
+ HsImport: data ImportPos
+ HsImport.Config: Config :: (ImportDecl -> String) -> (ImportDecl -> [ImportDecl] -> Maybe ImportPos) -> Maybe String -> Config
+ HsImport.Config: configError :: Config -> Maybe String
+ HsImport.Config: data Config
+ HsImport.Config: defaultConfig :: Config
+ HsImport.Config: findImportPos :: Config -> ImportDecl -> [ImportDecl] -> Maybe ImportPos
+ HsImport.Config: prettyPrint :: Config -> ImportDecl -> String
+ HsImport.ImportPos: After :: ImportDecl -> ImportPos
+ HsImport.ImportPos: Before :: ImportDecl -> ImportPos
+ HsImport.ImportPos: bestMatchingImport :: ModuleName -> [ImportDecl] -> Maybe ImportDecl
+ HsImport.ImportPos: data ImportPos
+ HsImport.ImportPos: findImportPos :: ImportDecl -> [ImportDecl] -> Maybe ImportPos
+ HsImport.ImportPos: instance Eq ImportPos
+ HsImport.ImportPos: instance Show ImportPos
+ HsImport.ImportPos: matchingImports :: ModuleName -> [ImportDecl] -> [ImportDecl]
+ HsImport.Main: hsimport :: Config -> IO ()
+ HsImport.Main: hsimport_ :: Config -> ImportSpec -> IO ()

Files

README.md view
@@ -38,6 +38,51 @@     $> hsimport -m 'Data.Maybe' -s 'Maybe' -w 'Just' -w 'Nothing'     => import Data.Maybe (Maybe(Just, Nothing)) +Configuration+-------------++You can configure how the import declarations are pretty printed and where they're placed+by writing a configuration file like:++    -- ~/.config/hsimport/hsimport.hs+    import qualified Language.Haskell.Exts as HS+    import HsImport++    main :: IO ()+    main = hsimport $ defaultConfig { prettyPrint = prettyPrint, findImportPos = findImportPos }+       where+          -- This is a bogus implementation of prettyPrint, because it doesn't handle the+          -- qualified import case nor does it considers any explicitely imported or hidden symbols.+          prettyPrint :: HS.ImportDecl -> String+          prettyPrint (HS.ImportDecl { HS.importModule = HS.ModuleName modName }) =+             "import " ++ modName++          -- This findImportPos implementation will always add the new import declaration+          -- at the end of the current ones. The data type ImportPos has the two constructors+          -- After and Before.+          findImportPos :: HS.ImportDecl -> [HS.ImportDecl] -> Maybe ImportPos+          findImportPos _         []             = Nothing+          findImportPos newImport currentImports = Just . After . last $ currentImports++The position of the configuration file depends on the result of `getUserConfigDir "hsimport"`,+which is a function from the package [xdg-basedir](<https://hackage.haskell.org/package/xdg-basedir>),+on linux like systems it is `~/.config/hsimport/hsimport.hs`.++If you've modified the configuration file, then the next call of `hsimport` will ensure a rebuild.+If you've installed `hsimport` with `cabal install`, without using a sandbox, then this should just work.++If you've build `hsimport` inside of a sandbox, then you most likely have to temporary modify the+`GHC_PACKAGE_PATH` for the next call of `hsimport`, to point `ghc` to the global database and+to the package database of the sandbox.++    # global package database+    $> export GLOBAL_PKG_DB=/usr/lib/ghc/package.conf.d/++    # hsimport sandbox package database+    $> export SANDBOX_PKG_DB=/home/you/hsimport-build-dir/.cabal-sandbox/*-packages.conf.d/++    $> GHC_PACKAGE_PATH=$GLOBAL_PKG_DB:$SANDBOX_PKG_DB hsimport --help+ Text Editor Integration ----------------------- 
exe/Main.hs view
@@ -1,14 +1,7 @@  module Main where -import System.Exit (exitFailure, exitSuccess)-import System.IO (hPutStrLn, stderr) import HsImport  main :: IO ()-main = do-   args      <- hsImportArgs-   maybeSpec <- hsImportSpec args-   case maybeSpec of-        Left  error -> hPutStrLn stderr ("hsimport: " ++ error) >> exitFailure-        Right spec  -> hsImport spec                            >> exitSuccess+main = hsimport defaultConfig
hsimport.cabal view
@@ -1,5 +1,5 @@ name: hsimport-version: 0.4+version: 0.5 cabal-version: >=1.9.2 build-type: Simple license: BSD3@@ -25,18 +25,21 @@         base >=3 && <5,         cmdargs >=0.10.5 && <0.11,         haskell-src-exts >=1.14.0 && <1.16,-        lens >=3.9.2 && <4.3,+        lens >=3.9.2 && <4.4,         mtl >=2.1.2 && <2.3,         text >=0.11.3.1 && <1.2,         split >=0.2.2 && <0.3,         attoparsec >=0.10.4.0 && <0.13,-        directory >=1.2.0.1 && <1.3+        directory >=1.2.0.1 && <1.3,+        dyre ==0.8.*     exposed-modules:         HsImport+        HsImport.Main+        HsImport.Config         HsImport.Args         HsImport.ImportSpec         HsImport.Symbol-        HsImport.Main+        HsImport.ImportPos     exposed: True     buildable: True     cpp-options: -DCABAL@@ -44,6 +47,8 @@     other-modules:         HsImport.ImportChange         HsImport.Parse+        HsImport.PrettyPrint+        HsImport.Utils         Paths_hsimport     ghc-options: -W  @@ -55,6 +60,7 @@     buildable: True     cpp-options: -DCABAL     hs-source-dirs: exe+    ghc-prof-options: -prof -fprof-auto -rtsopts     ghc-options: -W   test-suite hsimport-tests@@ -63,6 +69,7 @@         tasty >=0.6 && <0.9,         tasty-golden >=2.2.0.1 && <2.3,         filepath >=1.3.0.1 && <1.4,+        haskell-src-exts >=1.14.0 && <1.16,         hsimport -any     type: exitcode-stdio-1.0     main-is: Main.hs
lib/HsImport.hs view
@@ -1,13 +1,10 @@  module HsImport-   ( HsImportArgs-   , hsImportArgs-   , module HsImport.ImportSpec-   , module HsImport.Symbol-   , module HsImport.Main+   ( module HsImport.Main+   , module HsImport.Config+   , ImportPos(..)    ) where -import HsImport.Args-import HsImport.ImportSpec-import HsImport.Symbol import HsImport.Main+import HsImport.Config+import HsImport.ImportPos (ImportPos(..))
+ lib/HsImport/Config.hs view
@@ -0,0 +1,27 @@++module HsImport.Config+   ( Config(..)+   , defaultConfig+   ) where++import qualified Language.Haskell.Exts as HS+import qualified HsImport.PrettyPrint as PP+import qualified HsImport.ImportPos as IP++-- | User definable configuration for hsImport.+data Config = Config+   { -- | function for pretty printing of the import declarations+     prettyPrint :: HS.ImportDecl -> String+     -- | function for finding the position of new import declarations+   , findImportPos :: HS.ImportDecl -> [HS.ImportDecl] -> Maybe IP.ImportPos+     -- | error during configuration of hsimport+   , configError :: Maybe String+   }+++defaultConfig :: Config+defaultConfig = Config+   { prettyPrint   = PP.prettyPrint+   , findImportPos = IP.findImportPos+   , configError   = Nothing+   }
lib/HsImport/ImportChange.hs view
@@ -7,20 +7,21 @@  import Data.Maybe import Data.List (find, (\\))-import Data.List.Split (splitOn) import Control.Lens import qualified Language.Haskell.Exts as HS import qualified Data.Attoparsec.Text as A-import Data.Monoid (mconcat) import HsImport.Symbol (Symbol(..))+import HsImport.ImportPos (matchingImports)+import HsImport.Utils -type SrcLine      = Int-type ImportString = String+type SrcLine = Int -data ImportChange = ReplaceImportAt SrcLine ImportString-                  | AddImportAfter SrcLine ImportString-                  | AddImportAtEnd ImportString-                  | NoImportChange+-- | How the import declarations should be changed+data ImportChange = ReplaceImportAt SrcLine HS.ImportDecl -- ^ replace the import declaration at SrcLine+                  | AddImportAfter SrcLine HS.ImportDecl  -- ^ add import declaration after SrcLine+                  | AddImportAtEnd HS.ImportDecl          -- ^ add import declaration at end of source file+                  | FindImportPos HS.ImportDecl           -- ^ search for an insert position for the import declaration+                  | NoImportChange                        -- ^ no changes of the import declarations                   deriving (Show)  @@ -42,105 +43,58 @@  importModule :: String -> HS.Module -> ImportChange importModule moduleName module_-   | matching@(_:_) <- matchingImports moduleName module_ =+   | matching@(_:_) <- matchingImports moduleName (importDecls module_) =       if any entireModuleImported matching          then NoImportChange-         else AddImportAfter (srcLine . last $ matching) (HS.prettyPrint $ importDecl moduleName)+         else FindImportPos $ importDecl moduleName -   | Just bestMatch <- bestMatchingImport moduleName module_ =-      AddImportAfter (srcLine bestMatch) (HS.prettyPrint $ importDecl moduleName)+   | not $ null (importDecls module_) =+      FindImportPos $ importDecl moduleName     | otherwise =       case srcLineForNewImport module_ of-           Just srcLine -> AddImportAfter srcLine (HS.prettyPrint $ importDecl moduleName)-           Nothing      -> AddImportAtEnd (HS.prettyPrint $ importDecl moduleName)+           Just srcLine -> AddImportAfter srcLine (importDecl moduleName)+           Nothing      -> AddImportAtEnd (importDecl moduleName)   importModuleWithSymbol :: String -> Symbol -> HS.Module -> ImportChange importModuleWithSymbol moduleName symbol module_-   | matching@(_:_) <- matchingImports moduleName module_ =+   | matching@(_:_) <- matchingImports moduleName (importDecls module_) =       if any entireModuleImported matching || any (symbolImported symbol) matching          then NoImportChange          else case find hasImportedSymbols matching of                    Just impDecl ->-                      ReplaceImportAt (srcLine impDecl) (prettyPrint $ addSymbol impDecl symbol)+                      ReplaceImportAt (srcLine impDecl) (addSymbol impDecl symbol)                     Nothing      ->-                      AddImportAfter (srcLine . last $ matching)-                                     (prettyPrint $ importDeclWithSymbol moduleName symbol)+                      FindImportPos $ importDeclWithSymbol moduleName symbol -   | Just bestMatch <- bestMatchingImport moduleName module_ =-      AddImportAfter (srcLine bestMatch) (prettyPrint $ importDeclWithSymbol moduleName symbol)+   | not $ null (importDecls module_) =+      FindImportPos $ importDeclWithSymbol moduleName symbol     | otherwise =       case srcLineForNewImport module_ of-           Just srcLine -> AddImportAfter srcLine (prettyPrint $ importDeclWithSymbol moduleName symbol)-           Nothing      -> AddImportAtEnd (prettyPrint $ importDeclWithSymbol moduleName symbol)+           Just srcLine -> AddImportAfter srcLine (importDeclWithSymbol moduleName symbol)+           Nothing      -> AddImportAtEnd (importDeclWithSymbol moduleName symbol)    where       addSymbol (id@HS.ImportDecl {HS.importSpecs = specs}) symbol =          id {HS.importSpecs = specs & _Just . _2 %~ (++ [importSpec symbol])} -      prettyPrint importDecl =-         -- remove newlines from pretty printed ImportDecl-         case lines $ HS.prettyPrint importDecl of-              (fst : []  ) -> fst-              (fst : rest) -> mconcat $ fst : (map (' ' :) . map (dropWhile (== ' ')) $ rest)-              _            -> "" - importQualifiedModule :: String -> String -> HS.Module -> ImportChange importQualifiedModule moduleName qualifiedName module_-   | matching@(_:_) <- matchingImports moduleName module_ =+   | matching@(_:_) <- matchingImports moduleName (importDecls module_) =       if any (hasQualifiedImport qualifiedName) matching          then NoImportChange-         else AddImportAfter (srcLine . last $ matching) (HS.prettyPrint $ qualifiedImportDecl moduleName qualifiedName)+         else FindImportPos $ qualifiedImportDecl moduleName qualifiedName -   | Just bestMatch <- bestMatchingImport moduleName module_ =-      AddImportAfter (srcLine bestMatch) (HS.prettyPrint $ qualifiedImportDecl moduleName qualifiedName)+   | not $ null (importDecls module_) =+      FindImportPos $ qualifiedImportDecl moduleName qualifiedName     | otherwise =       case srcLineForNewImport module_ of-           Just srcLine -> AddImportAfter srcLine (HS.prettyPrint $ qualifiedImportDecl moduleName qualifiedName)-           Nothing      -> AddImportAtEnd (HS.prettyPrint $ qualifiedImportDecl moduleName qualifiedName)---matchingImports :: String -> HS.Module -> [HS.ImportDecl]-matchingImports moduleName (HS.Module _ _ _ _ _ imports _) =-   [ i -   | i@HS.ImportDecl {HS.importModule = HS.ModuleName name} <- imports-   , moduleName == name-   ] ---bestMatchingImport :: String -> HS.Module -> Maybe HS.ImportDecl-bestMatchingImport moduleName (HS.Module _ _ _ _ _ imports _) =-   case ifoldl' computeMatches Nothing splittedMods of-        Just (idx, _) -> Just $ imports !! idx-        _             -> Nothing-   where-      computeMatches :: Int -> Maybe (Int, Int) -> [String] -> Maybe (Int, Int)-      computeMatches idx matches mod =-         let num' = numMatches splittedMod mod-             in case matches of-                     Just (_, num) | num' >= num -> Just (idx, num')-                                   | otherwise   -> matches--                     Nothing | num' > 0  -> Just (idx, num')-                             | otherwise -> Nothing-         where-            numMatches = loop 0-               where-                  loop num (a:as) (b:bs)-                     | a == b    = loop (num + 1) as bs-                     | otherwise = num--                  loop num [] _ = num-                  loop num _ [] = num--      splittedMod  = splitOn "." moduleName-      splittedMods = [ splitOn "." name -                     | HS.ImportDecl {HS.importModule = HS.ModuleName name} <- imports-                     ]  +           Just srcLine -> AddImportAfter srcLine (qualifiedImportDecl moduleName qualifiedName)+           Nothing      -> AddImportAtEnd (qualifiedImportDecl moduleName qualifiedName)   entireModuleImported :: HS.ImportDecl -> Bool@@ -164,7 +118,7 @@    , any (imports symbol) hsSymbols    = True -   | otherwise +   | otherwise    = False    where       imports (Symbol symName)             (HS.IVar name)                    = symName == nameString name@@ -241,40 +195,3 @@    = Just $ max 0 (HS.srcLine sLoc - 1)     | otherwise = Nothing---srcLine :: HS.ImportDecl -> SrcLine-srcLine = HS.srcLine . HS.importLoc---declSrcLoc :: HS.Decl -> Maybe HS.SrcLoc-declSrcLoc decl =-   case decl of-        HS.TypeDecl srcLoc _ _ _          -> Just srcLoc-        HS.TypeFamDecl srcLoc _ _ _       -> Just srcLoc-        HS.DataDecl srcLoc _ _ _ _ _ _    -> Just srcLoc-        HS.GDataDecl srcLoc _ _ _ _ _ _ _ -> Just srcLoc-        HS.DataFamDecl srcLoc _ _ _ _     -> Just srcLoc-        HS.TypeInsDecl srcLoc _ _         -> Just srcLoc-        HS.DataInsDecl srcLoc _ _ _ _     -> Just srcLoc-        HS.GDataInsDecl srcLoc _ _ _ _ _  -> Just srcLoc-        HS.ClassDecl srcLoc _ _ _ _ _     -> Just srcLoc-        HS.InstDecl srcLoc _ _ _ _        -> Just srcLoc-        HS.DerivDecl srcLoc _ _ _         -> Just srcLoc-        HS.InfixDecl srcLoc _ _ _         -> Just srcLoc-        HS.DefaultDecl srcLoc _           -> Just srcLoc-        HS.SpliceDecl srcLoc _            -> Just srcLoc-        HS.TypeSig srcLoc _ _             -> Just srcLoc-        HS.FunBind _                      -> Nothing-        HS.PatBind srcLoc _ _ _ _         -> Just srcLoc-        HS.ForImp srcLoc _ _ _ _ _        -> Just srcLoc-        HS.ForExp srcLoc _ _ _ _          -> Just srcLoc-        HS.RulePragmaDecl srcLoc _        -> Just srcLoc-        HS.DeprPragmaDecl srcLoc _        -> Just srcLoc-        HS.WarnPragmaDecl srcLoc _        -> Just srcLoc-        HS.InlineSig srcLoc _ _ _         -> Just srcLoc-        HS.InlineConlikeSig srcLoc _ _    -> Just srcLoc-        HS.SpecSig srcLoc _ _ _           -> Just srcLoc-        HS.SpecInlineSig srcLoc _ _ _ _   -> Just srcLoc-        HS.InstSig srcLoc _ _ _           -> Just srcLoc-        HS.AnnPragma srcLoc _             -> Just srcLoc
+ lib/HsImport/ImportPos.hs view
@@ -0,0 +1,71 @@++module HsImport.ImportPos+   ( findImportPos+   , ImportPos(..)+   , matchingImports+   , bestMatchingImport+   ) where++import qualified Language.Haskell.Exts as HS+import Data.List.Split (splitOn)+import Control.Lens+import Control.Applicative ((<$>))++type ModuleName = String++-- | Where a new import declaration should be added.+data ImportPos = Before HS.ImportDecl -- ^ before the specified import declaration+               | After  HS.ImportDecl -- ^ after the specified import declaration+               deriving (Show, Eq)+++-- | Returns the position where the import declaration for the+--   new import should be put into the list of import declarations.+findImportPos :: HS.ImportDecl -> [HS.ImportDecl] -> Maybe ImportPos+findImportPos newImport imports = After <$> bestMatchingImport name imports+   where+      HS.ModuleName name = HS.importModule newImport+++-- | Returns all import declarations having the same module name.+matchingImports :: ModuleName -> [HS.ImportDecl] -> [HS.ImportDecl]+matchingImports moduleName imports =+   [ i+   | i@HS.ImportDecl {HS.importModule = HS.ModuleName name} <- imports+   , moduleName == name+   ]+++-- | Returns the best matching import declaration for the given module name.+--   E.g. if the module name is "Foo.Bar.Boo", then "Foo.Bar" is considered+--   better matching than "Foo".+bestMatchingImport :: ModuleName -> [HS.ImportDecl] -> Maybe HS.ImportDecl+bestMatchingImport _          []      = Nothing+bestMatchingImport moduleName imports =+   case ifoldl' computeMatches Nothing splittedMods of+        Just (idx, _) -> Just $ imports !! idx+        _             -> Nothing+   where+      computeMatches :: Int -> Maybe (Int, Int) -> [String] -> Maybe (Int, Int)+      computeMatches idx matches mod =+         let num' = numMatches splittedMod mod+             in case matches of+                     Just (_, num) | num' >= num -> Just (idx, num')+                                   | otherwise   -> matches++                     Nothing | num' > 0  -> Just (idx, num')+                             | otherwise -> Nothing+         where+            numMatches = loop 0+               where+                  loop num (a:as) (b:bs)+                     | a == b    = loop (num + 1) as bs+                     | otherwise = num++                  loop num [] _ = num+                  loop num _ [] = num++      splittedMod  = splitOn "." moduleName+      splittedMods = [ splitOn "." name+                     | HS.ImportDecl {HS.importModule = HS.ModuleName name} <- imports+                     ]
lib/HsImport/Main.hs view
@@ -1,22 +1,50 @@ {-# Language PatternGuards #-}  module HsImport.Main-   ( hsImport+   ( hsimport+   , hsimport_    ) where  import Control.Lens import Control.Applicative ((<$>)) import Control.Monad (when)+import System.Exit (exitFailure, exitSuccess)+import System.IO (hPutStrLn, stderr) import Data.Maybe (isJust) import Data.List (foldl') import qualified Data.Text as T import qualified Data.Text.IO as TIO+import qualified Config.Dyre as Dyre import HsImport.ImportChange import HsImport.ImportSpec+import HsImport.ImportPos (ImportPos(..))+import qualified HsImport.Args as Args+import HsImport.Config+import HsImport.Utils+import qualified HsImport.Parse as P  -hsImport :: ImportSpec -> IO ()-hsImport spec = do+hsimport = Dyre.wrapMain $ Dyre.defaultParams+   { Dyre.projectName = "hsimport"+   , Dyre.realMain    = realMain+   , Dyre.showError   = \config err -> config { configError = Just err }+   }+   where+      realMain :: Config -> IO ()+      realMain config = do+         case configError config of+              Just error -> hPutStrLn stderr ("hsimport: " ++ error) >> exitFailure+              _          -> return ()++         args      <- Args.hsImportArgs+         maybeSpec <- hsImportSpec args+         case maybeSpec of+              Left  error -> hPutStrLn stderr ("hsimport: " ++ error) >> exitFailure+              Right spec  -> hsimport_ config spec                    >> exitSuccess+++hsimport_ :: Config -> ImportSpec -> IO ()+hsimport_ Config { prettyPrint = prettyPrint, findImportPos = findImportPos } spec = do    let impChanges = importChanges (spec ^. moduleToImport)                                   (spec ^. symbolToImport)                                   (spec ^. qualifiedName)@@ -30,21 +58,36 @@    where       applyChanges = foldl' applyChange -      applyChange srcLines (ReplaceImportAt srcLine importStr) =-         let numDrops   = srcLine-             numTakes   = max 0 (numDrops - 1)-             in take numTakes srcLines ++ [importStr] ++ drop numDrops srcLines+      applyChange srcLines (ReplaceImportAt srcLine importDecl) =+         let numTakes = max 0 (srcLine - 1)+             numDrops = lastImportSrcLine srcLine srcLines+             in take numTakes srcLines ++ [prettyPrint importDecl] ++ drop numDrops srcLines -      applyChange srcLines (AddImportAfter srcLine importStr) =-         let numTakes   = srcLine-             numDrops   = numTakes-             in take numTakes srcLines ++ [importStr] ++ drop numDrops srcLines+      applyChange srcLines (AddImportAfter srcLine importDecl) =+         let numTakes = lastImportSrcLine srcLine srcLines+             numDrops = numTakes+             in take numTakes srcLines ++ [prettyPrint importDecl] ++ drop numDrops srcLines -      applyChange srcLines (AddImportAtEnd importStr) =-         srcLines ++ [importStr]+      applyChange srcLines (AddImportAtEnd importDecl) =+         srcLines ++ [prettyPrint importDecl] +      applyChange srcLines (FindImportPos importDecl) =+         case findImportPos importDecl allImportDecls of+              Just (After impDecl)  -> applyChange srcLines (AddImportAfter (srcLine impDecl) importDecl)+              Just (Before impDecl) -> applyChange srcLines (AddImportAfter (max 0 (srcLine impDecl - 1)) importDecl)+              _                     -> applyChange srcLines (AddImportAfter (srcLine . last $ allImportDecls) importDecl)+       applyChange srcLines NoImportChange = srcLines        outputFile spec          | Just file <- spec ^. saveToFile = file          | otherwise                       = spec ^. sourceFile++      lastImportSrcLine fstLine srcLines+         | Just lastLine <- P.lastImportSrcLine $ drop (max 0 (fstLine - 1)) srcLines+         = fstLine + (lastLine - 1)++         | otherwise+         = fstLine++      allImportDecls = importDecls $ spec ^. parsedSrcFile
lib/HsImport/Parse.hs view
@@ -2,6 +2,7 @@  module HsImport.Parse    ( parseFile+   , lastImportSrcLine    ) where  import qualified Data.Text.IO as TIO@@ -22,12 +23,12 @@                   HS.ParseOk _ -> return $ Right result                    HS.ParseFailed srcLoc _ -> do-                     srcResult <- parseInvalidSource (lines srcFile) 0 (HS.srcLine srcLoc)+                     srcResult <- parseInvalidSource (lines srcFile) (HS.srcLine srcLoc) 0 (HS.srcLine srcLoc)                      return $ Right $ fromMaybe result srcResult)           (\(e :: SomeException) -> do             let srcLines = lines srcFile-            srcResult <- parseInvalidSource srcLines 0 (length srcLines)+            srcResult <- parseInvalidSource srcLines (length srcLines) 0 (length srcLines)             return $ maybe (Left $ show e) Right srcResult)    where       -- | replace CPP directives by a fake comment@@ -37,25 +38,61 @@             else line  +type SrcLine = Int++-- | Expects that '[String]' starts with an import declaration and returns+--   the last source line of the import declaration, so this function+--   is for the handling of multine line import declarations.+lastImportSrcLine :: [String] -> Maybe SrcLine+lastImportSrcLine srcLines+   | null srcLines+   = Nothing++   | "import" `isPrefixOf` head srcLines+   = parseImport 1++   | otherwise+   = Nothing++   where+      parseImport lastLine+         | lastLine <= numSrcLines+         = case parseFileContents source of+                HS.ParseOk _       -> Just lastLine+                HS.ParseFailed _ _ -> parseImport (lastLine + 1)++         | otherwise+         = Nothing++         where+            source = unlines $ take lastLine srcLines++      numSrcLines = length srcLines++ -- | tries to find the maximal part of the source file (from the beginning) that contains --   valid/complete Haskell code-parseInvalidSource :: [String] -> Int -> Int -> IO (Maybe (HS.ParseResult HS.Module))-parseInvalidSource srcLines lastValidLine firstInvalidLine-   | null srcLines || lastValidLine >= firstInvalidLine = return Nothing+parseInvalidSource :: [String] -> Int -> Int -> Int -> IO (Maybe (HS.ParseResult HS.Module))+parseInvalidSource srcLines firstInvalidLine lastValidLine currLastLine+   | null srcLines || lastValidLine >= currLastLine = return Nothing    | otherwise =       catch (case parseFileContents source of                   result@(HS.ParseOk _)-                     | (nextLine + 1) == firstInvalidLine ->+                     | (nextLine + 1) == currLastLine ->                         return $ Just result-                     | otherwise                          ->-                        parseInvalidSource srcLines nextLine firstInvalidLine+                     | otherwise                      ->+                        parseInvalidSource srcLines firstInvalidLine nextLine currLastLine -                  HS.ParseFailed _ _ -> parseInvalidSource srcLines lastValidLine nextLine)+                  HS.ParseFailed srcLoc _+                     | HS.srcLine srcLoc == firstInvalidLine ->+                        parseInvalidSource srcLines firstInvalidLine lastValidLine nextLine+                     | otherwise                             ->+                        parseInvalidSource srcLines firstInvalidLine nextLine currLastLine) -            (\(_ :: SomeException) -> parseInvalidSource srcLines lastValidLine nextLine)+            (\(_ :: SomeException) -> parseInvalidSource srcLines firstInvalidLine lastValidLine nextLine)    where       source   = unlines $ take (nextLine + 1) srcLines-      nextLine = lastValidLine + (floor ((realToFrac (firstInvalidLine - lastValidLine) / 2) :: Double) :: Int)+      nextLine = lastValidLine + (floor ((realToFrac (currLastLine - lastValidLine) / 2) :: Double) :: Int)   parseFileContents :: String -> HS.ParseResult HS.Module
+ lib/HsImport/PrettyPrint.hs view
@@ -0,0 +1,16 @@++module HsImport.PrettyPrint+   ( prettyPrint+   ) where++import Data.Monoid (mconcat)+import qualified Language.Haskell.Exts as HS+++prettyPrint :: HS.ImportDecl -> String+prettyPrint importDecl =+   -- remove newlines from pretty printed ImportDecl+   case lines $ HS.prettyPrint importDecl of+        (fst : []  ) -> fst+        (fst : rest) -> mconcat $ fst : (map (' ' :) . map (dropWhile (== ' ')) $ rest)+        _            -> ""
+ lib/HsImport/Utils.hs view
@@ -0,0 +1,50 @@++module HsImport.Utils+   ( srcLine+   , declSrcLoc+   , importDecls+   ) where++import qualified Language.Haskell.Exts as HS++type SrcLine = Int++srcLine :: HS.ImportDecl -> SrcLine+srcLine = HS.srcLine . HS.importLoc+++declSrcLoc :: HS.Decl -> Maybe HS.SrcLoc+declSrcLoc decl =+   case decl of+        HS.TypeDecl srcLoc _ _ _          -> Just srcLoc+        HS.TypeFamDecl srcLoc _ _ _       -> Just srcLoc+        HS.DataDecl srcLoc _ _ _ _ _ _    -> Just srcLoc+        HS.GDataDecl srcLoc _ _ _ _ _ _ _ -> Just srcLoc+        HS.DataFamDecl srcLoc _ _ _ _     -> Just srcLoc+        HS.TypeInsDecl srcLoc _ _         -> Just srcLoc+        HS.DataInsDecl srcLoc _ _ _ _     -> Just srcLoc+        HS.GDataInsDecl srcLoc _ _ _ _ _  -> Just srcLoc+        HS.ClassDecl srcLoc _ _ _ _ _     -> Just srcLoc+        HS.InstDecl srcLoc _ _ _ _        -> Just srcLoc+        HS.DerivDecl srcLoc _ _ _         -> Just srcLoc+        HS.InfixDecl srcLoc _ _ _         -> Just srcLoc+        HS.DefaultDecl srcLoc _           -> Just srcLoc+        HS.SpliceDecl srcLoc _            -> Just srcLoc+        HS.TypeSig srcLoc _ _             -> Just srcLoc+        HS.FunBind _                      -> Nothing+        HS.PatBind srcLoc _ _ _ _         -> Just srcLoc+        HS.ForImp srcLoc _ _ _ _ _        -> Just srcLoc+        HS.ForExp srcLoc _ _ _ _          -> Just srcLoc+        HS.RulePragmaDecl srcLoc _        -> Just srcLoc+        HS.DeprPragmaDecl srcLoc _        -> Just srcLoc+        HS.WarnPragmaDecl srcLoc _        -> Just srcLoc+        HS.InlineSig srcLoc _ _ _         -> Just srcLoc+        HS.InlineConlikeSig srcLoc _ _    -> Just srcLoc+        HS.SpecSig srcLoc _ _ _           -> Just srcLoc+        HS.SpecInlineSig srcLoc _ _ _ _   -> Just srcLoc+        HS.InstSig srcLoc _ _ _           -> Just srcLoc+        HS.AnnPragma srcLoc _             -> Just srcLoc+++importDecls :: HS.Module -> [HS.ImportDecl]+importDecls (HS.Module _ _ _ _ _ imports _) = imports
tests/Main.hs view
@@ -5,8 +5,13 @@ import Test.Tasty.Golden import System.FilePath import System.IO (hPutStrLn, stderr)-import HsImport (hsImport, hsImportSpec)+import Data.List (intercalate)+import qualified Language.Haskell.Exts as HS+import HsImport.Main (hsimport_)+import HsImport.ImportSpec (hsImportSpec)+import qualified HsImport.Config as C import qualified HsImport.Args as A+import qualified HsImport.ImportPos as P  main = defaultMain tests @@ -42,6 +47,10 @@    , test "ModuleTest24" $ A.defaultArgs { A.moduleName = "Control.Monad", A.qualifiedName = "CM" }    , test "ModuleTest25" $ A.defaultArgs { A.moduleName = "Control.Monad", A.qualifiedName = "Control.Monad" }    , test "ModuleTest26" $ A.defaultArgs { A.moduleName = "Control.Monad" }+   , test_ "ModuleTest27" (C.defaultConfig { C.findImportPos = importPosBeforeFirst }) (A.defaultArgs { A.moduleName = "Control.Monad" })+   , test_ "ModuleTest28" (C.defaultConfig { C.findImportPos = importPosAfterFirst }) (A.defaultArgs { A.moduleName = "Control.Monad" })+   , test_ "ModuleTest29" (C.defaultConfig { C.findImportPos = importPosBeforeLast }) (A.defaultArgs { A.moduleName = "Control.Monad" })+   , test_ "ModuleTest30" (C.defaultConfig { C.findImportPos = importPosAfterLast }) (A.defaultArgs { A.moduleName = "Control.Monad" })    ]  @@ -73,21 +82,62 @@    , test "SymbolTest23" $ A.defaultArgs { A.moduleName = "Data.Maybe", A.symbolName = "Maybe", A.all = True, A.with = ["Just"] }    , test "SymbolTest24" $ A.defaultArgs { A.moduleName = "Data.Maybe", A.symbolName = "Maybe", A.with = ["Just"] }    , test "SymbolTest25" $ A.defaultArgs { A.moduleName = "Data.Maybe", A.symbolName = "Maybe", A.with = ["Nothing", "Just"] }+   , test "SymbolTest26" $ A.defaultArgs { A.moduleName = "Foo", A.symbolName = "bar" }+   , test "SymbolTest27" $ A.defaultArgs { A.moduleName = "Ugah.Blub", A.symbolName = "g" }+   , test "SymbolTest28" $ A.defaultArgs { A.moduleName = "Ugah.Blub", A.symbolName = "d" }+   , test_ "SymbolTest29" (C.defaultConfig { C.prettyPrint = prettyPrint }) (A.defaultArgs { A.moduleName = "X.Y", A.symbolName = "x" })    ]   test :: String -> A.HsImportArgs -> TestTree-test testName args =+test testName args = test_ testName C.defaultConfig args+++test_ :: String -> C.Config -> A.HsImportArgs -> TestTree+test_ testName config args =    goldenVsFileDiff testName diff goldenFile outputFile command    where       command = do          spec <- hsImportSpec (args { A.inputSrcFile = inputFile, A.outputSrcFile = outputFile })          case spec of               Left error  -> hPutStrLn stderr ("hsimport: " ++ error)-              Right spec_ -> hsImport spec_+              Right spec_ -> hsimport_ config spec_        diff ref new = ["diff", "-u", ref, new]        goldenFile = "tests" </> "goldenFiles" </> testName <.> "hs"       outputFile = "tests" </> "outputFiles" </> testName <.> "hs"       inputFile  = "tests" </> "inputFiles"  </> testName <.> "hs"+++prettyPrint :: HS.ImportDecl -> String+prettyPrint HS.ImportDecl { HS.importModule = HS.ModuleName modName, HS.importSpecs = Just (False, syms) } =+   "import " ++ modName ++ " ( " ++ ppSyms ++ " )"+   where+      ppSyms = intercalate " , " symNames+      symNames = map symName syms++      symName (HS.IVar (HS.Ident name)) = name+      symName _ = ""++prettyPrint _ = "Uupps"+++importPosAfterLast :: HS.ImportDecl -> [HS.ImportDecl] -> Maybe P.ImportPos+importPosAfterLast _ []      = Nothing+importPosAfterLast _ imports = Just . P.After . last $ imports+++importPosBeforeLast :: HS.ImportDecl -> [HS.ImportDecl] -> Maybe P.ImportPos+importPosBeforeLast _ []      = Nothing+importPosBeforeLast _ imports = Just . P.Before . last $ imports+++importPosAfterFirst :: HS.ImportDecl -> [HS.ImportDecl] -> Maybe P.ImportPos+importPosAfterFirst _ []      = Nothing+importPosAfterFirst _ imports = Just . P.After . head $ imports+++importPosBeforeFirst :: HS.ImportDecl -> [HS.ImportDecl] -> Maybe P.ImportPos+importPosBeforeFirst _ []      = Nothing+importPosBeforeFirst _ imports = Just . P.Before . head $ imports
+ tests/goldenFiles/ModuleTest27.hs view
@@ -0,0 +1,18 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Control.Monad+import Ugah.Foo ( a+                , b+                )+import Control.Applicative+import Ugah.Blub+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+   where
+ tests/goldenFiles/ModuleTest28.hs view
@@ -0,0 +1,18 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Ugah.Foo ( a+                , b+                )+import Control.Monad+import Control.Applicative+import Ugah.Blub+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+   where
+ tests/goldenFiles/ModuleTest29.hs view
@@ -0,0 +1,18 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Ugah.Foo+import Control.Applicative ( a+                           , b+                           )+import Control.Monad+import Ugah.Blub+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+   where
+ tests/goldenFiles/ModuleTest30.hs view
@@ -0,0 +1,18 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Ugah.Foo+import Control.Applicative+import Ugah.Blub ( a+                 , b+                 )+import Control.Monad+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+   where
+ tests/goldenFiles/SymbolTest26.hs view
@@ -0,0 +1,3 @@+import Foo (foo, bar)+boo :: Int+boo = 3
+ tests/goldenFiles/SymbolTest27.hs view
@@ -0,0 +1,15 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Ugah.Foo+import Control.Applicative+import Ugah.Blub (a, b, c, d, e, f, g)+f :: Int -> Int+f = (+ 3)++g :: Int+g =+   where
+ tests/goldenFiles/SymbolTest28.hs view
@@ -0,0 +1,13 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Control.Applicative+import Ugah.Blub (a, b, c, d)+f :: Int -> Int+f = (+ 3)++r :: Int -> Int+r =
+ tests/goldenFiles/SymbolTest29.hs view
@@ -0,0 +1,17 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Control.Applicative+import Ugah.Blub ( a+                 , b+                 , c+                 )+import X.Y ( x )+f :: Int -> Int+f = (+ 3)++r :: Int -> Int+r =
+ tests/inputFiles/ModuleTest27.hs view
@@ -0,0 +1,17 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Ugah.Foo ( a+                , b+                )+import Control.Applicative+import Ugah.Blub+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+   where
+ tests/inputFiles/ModuleTest28.hs view
@@ -0,0 +1,17 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Ugah.Foo ( a+                , b+                )+import Control.Applicative+import Ugah.Blub+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+   where
+ tests/inputFiles/ModuleTest29.hs view
@@ -0,0 +1,17 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Ugah.Foo+import Control.Applicative ( a+                           , b+                           )+import Ugah.Blub+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+   where
+ tests/inputFiles/ModuleTest30.hs view
@@ -0,0 +1,17 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Ugah.Foo+import Control.Applicative+import Ugah.Blub ( a+                 , b+                 )+f :: Int -> Int+f = (+ 3)++g :: Int -> Int+g =+   where
+ tests/inputFiles/SymbolTest26.hs view
@@ -0,0 +1,3 @@+import Foo (foo)+boo :: Int+boo = 3
+ tests/inputFiles/SymbolTest27.hs view
@@ -0,0 +1,16 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Ugah.Foo+import Control.Applicative+import Ugah.Blub (a, b, c,+                  d, e, f)+f :: Int -> Int+f = (+ 3)++g :: Int+g =+   where
+ tests/inputFiles/SymbolTest28.hs view
@@ -0,0 +1,16 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Control.Applicative+import Ugah.Blub ( a+                 , b+                 , c+                 )+f :: Int -> Int+f = (+ 3)++r :: Int -> Int+r =
+ tests/inputFiles/SymbolTest29.hs view
@@ -0,0 +1,16 @@+{-# Language PatternGuards #-}+module Blub+   ( blub+   , foo+   , bar+   ) where+import Control.Applicative+import Ugah.Blub ( a+                 , b+                 , c+                 )+f :: Int -> Int+f = (+ 3)++r :: Int -> Int+r =