diff --git a/data/stylish-haskell.yaml b/data/stylish-haskell.yaml index 08e111f6..0ee3018b 100644 --- a/data/stylish-haskell.yaml +++ b/data/stylish-haskell.yaml @@ -275,21 +275,25 @@ steps: # Default: false space_surround: false - # Enabling this argument will use the new GHC lib parse to format imports. + # Post qualify option moves any qualifies found in import declarations + # to the end of the declaration. This also adjust padding for any + # unqualified import declarations. # - # This currently assumes a few things, it will assume that you want post - # qualified imports. It is also not as feature complete as the old - # imports formatting. + # - true: Qualified as is moved to the end of the + # declaration. # - # It does not remove redundant lines or merge lines. As such, the full - # feature scope is still pending. + # > import Data.Bar + # > import Data.Foo qualified as F # - # It _is_ however, a fine alternative if you are using features that are - # not parseable by haskell src extensions and you're comfortable with the - # presets. + # - false: Qualified remains in the default location and unqualified + # imports are padded to align with qualified imports. + # + # > import Data.Bar + # > import qualified Data.Foo as F # # Default: false - ghc_lib_parser: false + post_qualify: false + # Language pragmas - language_pragmas: diff --git a/lib/Language/Haskell/Stylish/Config.hs b/lib/Language/Haskell/Stylish/Config.hs index cc5cf31a..5cf4950e 100644 --- a/lib/Language/Haskell/Stylish/Config.hs +++ b/lib/Language/Haskell/Stylish/Config.hs @@ -289,6 +289,7 @@ parseImports config o = fmap (Imports.step columns) $ Imports.Options <*> (o A..:? "list_padding" >>= maybe (pure $ def Imports.listPadding) parseListPadding) <*> o A..:? "separate_lists" A..!= def Imports.separateLists <*> o A..:? "space_surround" A..!= def Imports.spaceSurround + <*> o A..:? "post_qualify" A..!= def Imports.postQualified where def f = f Imports.defaultOptions diff --git a/lib/Language/Haskell/Stylish/Step/Imports.hs b/lib/Language/Haskell/Stylish/Step/Imports.hs index db4132bc..48f3f053 100644 --- a/lib/Language/Haskell/Stylish/Step/Imports.hs +++ b/lib/Language/Haskell/Stylish/Step/Imports.hs @@ -61,6 +61,7 @@ data Options = Options , listPadding :: ListPadding , separateLists :: Bool , spaceSurround :: Bool + , postQualified :: Bool } deriving (Eq, Show) defaultOptions :: Options @@ -73,6 +74,7 @@ defaultOptions = Options , listPadding = LPConstant 4 , separateLists = True , spaceSurround = False + , postQualified = False } data ListPadding @@ -143,8 +145,8 @@ formatImports -> NonEmpty (Located Import) -> Lines formatImports maxCols options m moduleStats rawGroup = runPrinter_ (PrinterConfig maxCols) [] m do - let - + let + group = NonEmpty.sortWith unLocated rawGroup & mergeImports @@ -177,25 +179,31 @@ printQualified Options{..} padNames stats (L _ decl) = do when (isSafe decl) (putText "safe" >> space) - case (isQualified decl, isAnyQualified stats) of - (True, _) -> putText "qualified" >> space - (_, True) -> putText " " >> space - _ -> pure () - - moduleNamePosition <- length <$> getCurrentLine - forM_ (ideclPkgQual decl') $ \pkg -> putText (stringLiteral pkg) >> space - putText (moduleName decl) - - -- Only print spaces if something follows. - when padNames $ - when (isJust (ideclAs decl') || isHiding decl || - not (null $ ideclHiding decl')) $ - putText $ - replicate (isLongestImport stats - importModuleNameLength decl) ' ' + let + module_ = do + moduleNamePosition <- length <$> getCurrentLine + forM_ (ideclPkgQual decl') $ \pkg -> putText (stringLiteral pkg) >> space + putText (moduleName decl) + -- Only print spaces if something follows. + when padNames $ + when (isJust (ideclAs decl') || isHiding decl || + not (null $ ideclHiding decl')) $ + putText $ + replicate (isLongestImport stats - importModuleNameLength decl) ' ' + pure moduleNamePosition + + moduleNamePosition <- + case (postQualified, isQualified decl, isAnyQualified stats) of + (False, True , _ ) -> putText "qualified" *> space *> module_ + (False, _ , True) -> putText " " *> space *> module_ + (True , True , _ ) -> module_ <* space <* putText "qualified" + _ -> module_ beforeAliasPosition <- length <$> getCurrentLine - forM_ (ideclAs decl') \(L _ name) -> + + forM_ (ideclAs decl') \(L _ name) -> do space >> putText "as" >> space >> putText (moduleNameString name) + afterAliasPosition <- length <$> getCurrentLine when (isHiding decl) (space >> putText "hiding") diff --git a/tests/Language/Haskell/Stylish/Step/Imports/Tests.hs b/tests/Language/Haskell/Stylish/Step/Imports/Tests.hs index 6889db4c..479c9db6 100644 --- a/tests/Language/Haskell/Stylish/Step/Imports/Tests.hs +++ b/tests/Language/Haskell/Stylish/Step/Imports/Tests.hs @@ -62,6 +62,13 @@ tests = testGroup "Language.Haskell.Stylish.Step.Imports.Tests" , testCase "case 28" case28 , testCase "case 29" case29 , testCase "case 30" case30 + , testCase "case 31" case31 + , testCase "case 32" case32 + , testCase "case 33" case33 + , testCase "case 34" case34 + , testCase "case 35" case35 + , testCase "case 36" case36 + , testCase "case 37" case37 ] @@ -190,7 +197,7 @@ case07 = assertSnippet (step (Just 80) $ fromImportAlign File) case08 :: Assertion case08 = let - options = Options Global WithAlias True Inline Inherit (LPConstant 4) True False + options = Options Global WithAlias True Inline Inherit (LPConstant 4) True False False in assertSnippet (step (Just 80) options) input [ "module Herp where" @@ -214,7 +221,7 @@ case08 = case08b :: Assertion case08b = let - options = Options Global WithModuleName True Inline Inherit (LPConstant 4) True False + options = Options Global WithModuleName True Inline Inherit (LPConstant 4) True False False in assertSnippet (step (Just 80) options) input ["module Herp where" @@ -237,7 +244,7 @@ case08b = case09 :: Assertion case09 = let - options = Options Global WithAlias True Multiline Inherit (LPConstant 4) True False + options = Options Global WithAlias True Multiline Inherit (LPConstant 4) True False False in assertSnippet (step (Just 80) options) input [ "module Herp where" @@ -272,7 +279,7 @@ case09 = case10 :: Assertion case10 = let - options = Options Group WithAlias True Multiline Inherit (LPConstant 4) True False + options = Options Group WithAlias True Multiline Inherit (LPConstant 4) True False False in assertSnippet (step (Just 40) options) input [ "module Herp where" @@ -313,7 +320,7 @@ case10 = case11 :: Assertion case11 = let - options = Options Group NewLine True Inline Inherit (LPConstant 4) True False + options = Options Group NewLine True Inline Inherit (LPConstant 4) True False False in assertSnippet (step (Just 80) options) input [ "module Herp where" @@ -340,7 +347,7 @@ case11 = case11b :: Assertion case11b = let - options = Options Group WithModuleName True Inline Inherit (LPConstant 4) True False + options = Options Group WithModuleName True Inline Inherit (LPConstant 4) True False False in assertSnippet (step (Just 80) options) input [ "module Herp where" @@ -363,7 +370,7 @@ case11b = case12 :: Assertion case12 = let - options = Options Group NewLine True Inline Inherit (LPConstant 2) True False + options = Options Group NewLine True Inline Inherit (LPConstant 2) True False False in assertSnippet (step (Just 80) options) [ "import Data.List (map)" @@ -377,7 +384,7 @@ case12 = case12b :: Assertion case12b = let - options = Options Group WithModuleName True Inline Inherit (LPConstant 2) True False + options = Options Group WithModuleName True Inline Inherit (LPConstant 2) True False False in assertSnippet (step (Just 80) options) ["import Data.List (map)"] @@ -388,7 +395,7 @@ case12b = case13 :: Assertion case13 = let - options = Options None WithAlias True InlineWithBreak Inherit (LPConstant 4) True False + options = Options None WithAlias True InlineWithBreak Inherit (LPConstant 4) True False False in assertSnippet (step (Just 80) options) [ "import qualified Data.List as List (concat, foldl, foldr, head, init," @@ -402,7 +409,7 @@ case13 = case13b :: Assertion case13b = let - options = Options None WithModuleName True InlineWithBreak Inherit (LPConstant 4) True False + options = Options None WithModuleName True InlineWithBreak Inherit (LPConstant 4) True False False in assertSnippet (step (Just 80) options) [ "import qualified Data.List as List (concat, foldl, foldr, head, init," @@ -418,7 +425,7 @@ case13b = case14 :: Assertion case14 = let - options = Options None WithAlias True InlineWithBreak Inherit (LPConstant 10) True False + options = Options None WithAlias True InlineWithBreak Inherit (LPConstant 10) True False False in assertSnippet (step (Just 80) options) [ "import qualified Data.List as List (concat, map, null, reverse, tail, (++))" @@ -431,7 +438,7 @@ case14 = case15 :: Assertion case15 = let - options = Options None AfterAlias True Multiline Inherit (LPConstant 4) True False + options = Options None AfterAlias True Multiline Inherit (LPConstant 4) True False False in assertSnippet (step (Just 80) options) [ "import Data.Acid (AcidState)" @@ -456,7 +463,7 @@ case15 = case16 :: Assertion case16 = let - options = Options None AfterAlias True Multiline Inherit (LPConstant 4) False False + options = Options None AfterAlias True Multiline Inherit (LPConstant 4) False False False in assertSnippet (step (Just 80) options) [ "import Data.Acid (AcidState)" @@ -479,7 +486,7 @@ case16 = case17 :: Assertion case17 = let - options = Options None AfterAlias True Multiline Inherit (LPConstant 4) True False + options = Options None AfterAlias True Multiline Inherit (LPConstant 4) True False False in assertSnippet (step (Just 80) options) [ "import Control.Applicative (Applicative ((<*>),pure))" @@ -496,7 +503,7 @@ case17 = case18 :: Assertion case18 = let - options = Options None AfterAlias True InlineToMultiline Inherit (LPConstant 4) True False + options = Options None AfterAlias True InlineToMultiline Inherit (LPConstant 4) True False False in assertSnippet (step (Just 40) options) [ "import Data.Foo as Foo (Bar, Baz, Foo)" @@ -523,7 +530,7 @@ case18 = case19 :: Assertion case19 = let - options = Options Global NewLine True InlineWithBreak RightAfter (LPConstant 17) True False + options = Options Global NewLine True InlineWithBreak RightAfter (LPConstant 17) True False False in assertSnippet (step (Just 40) options) case19input ---------------------------------------- @@ -539,7 +546,7 @@ case19 = case19b :: Assertion case19b = let - options = Options File NewLine True InlineWithBreak RightAfter (LPConstant 17) True False + options = Options File NewLine True InlineWithBreak RightAfter (LPConstant 17) True False False in assertSnippet (step (Just 40) options) case19input ---------------------------------------- @@ -554,7 +561,7 @@ case19b = case19c :: Assertion case19c = let - options = Options File NewLine True InlineWithBreak RightAfter LPModuleName True False + options = Options File NewLine True InlineWithBreak RightAfter LPModuleName True False False in assertSnippet (step (Just 40) options) case19input ---------------------------------------- @@ -569,7 +576,7 @@ case19c = case19d :: Assertion case19d = let - options = Options Global NewLine True InlineWithBreak RightAfter LPModuleName True False + options = Options Global NewLine True InlineWithBreak RightAfter LPModuleName True False False in assertSnippet (step (Just 40) options) case19input ---------------------------------------- @@ -665,7 +672,7 @@ case22 = assertSnippet (step (Just 80) defaultOptions) case23 :: Assertion case23 = let - options = Options None AfterAlias False Inline Inherit (LPConstant 4) True True + options = Options None AfterAlias False Inline Inherit (LPConstant 4) True True False in assertSnippet (step (Just 40) options) [ "import Data.Acid (AcidState)" @@ -690,7 +697,7 @@ case23 = case23b :: Assertion case23b = let - options = Options None WithModuleName False Inline Inherit (LPConstant 4) True True + options = Options None WithModuleName False Inline Inherit (LPConstant 4) True True False in assertSnippet (step (Just 40) options) [ "import Data.Acid (AcidState)" @@ -716,7 +723,7 @@ case23b = case24 :: Assertion case24 = let - options = Options None AfterAlias False InlineWithBreak Inherit (LPConstant 4) True True + options = Options None AfterAlias False InlineWithBreak Inherit (LPConstant 4) True True False in assertSnippet (step (Just 40) options) [ "import Data.Acid (AcidState)" @@ -740,7 +747,7 @@ case24 = case25 :: Assertion case25 = let - options = Options Group AfterAlias False Multiline Inherit (LPConstant 4) False False + options = Options Group AfterAlias False Multiline Inherit (LPConstant 4) False False False in assertSnippet (step (Just 80) options) [ "import Data.Acid (AcidState)" @@ -807,7 +814,7 @@ case28 = assertSnippet (step (Just 80) $ fromImportAlign Global) , "import Data.Set (empty, nub)" ] [ "import Control.Monad" - , "import qualified Data.Aeson as JSON" + , "import qualified Data.Aeson as JSON" , "import Data.Default.Class (Default (def))" , "" , "import Data.Maybe (Maybe (Just, Nothing))" @@ -842,3 +849,77 @@ case30 :: Assertion case30 = assertSnippet (step Nothing defaultOptions {separateLists = False}) ["import Data.Monoid (Monoid (..))"] ["import Data.Monoid (Monoid(..))"] + +-------------------------------------------------------------------------------- +case31 :: Assertion +case31 = assertSnippet (step Nothing defaultOptions {postQualified = True}) + ["import Data.Monoid (Monoid (..))"] + ["import Data.Monoid (Monoid (..))"] + +-------------------------------------------------------------------------------- +case32 :: Assertion +case32 = assertSnippet (step Nothing defaultOptions {postQualified = True}) + ["import qualified Data.Monoid as M"] + ["import Data.Monoid qualified as M"] + +-------------------------------------------------------------------------------- +case33 :: Assertion +case33 = assertSnippet (step Nothing defaultOptions {postQualified = True}) + [ "import Data.Default.Class (Default(def))" + , "import qualified Data.Aeson as JSON" + , "import qualified Data.Aeson as JSON" + , "import Control.Monad" + , "import Control.Monad" + , "" + , "import Data.Maybe (Maybe (Just, Nothing))" + , "import qualified Data.Maybe.Extra (Maybe(Just, Nothing))" + , "" + , "import Data.Foo (Foo (Foo,Bar), Goo(Goo))" + , "import Data.Foo (Foo (Foo,Bar))" + , "import Data.Set (empty, intersect)" + , "import Data.Set (empty, nub)" + ] + [ "import Control.Monad" + , "import Data.Aeson qualified as JSON" + , "import Data.Default.Class (Default (def))" + , "" + , "import Data.Maybe (Maybe (Just, Nothing))" + , "import Data.Maybe.Extra qualified (Maybe (Just, Nothing))" + , "" + , "import Data.Foo (Foo (Bar, Foo), Goo (Goo))" + , "import Data.Set (empty, intersect, nub)" + ] + +-------------------------------------------------------------------------------- +case34 :: Assertion +case34 = assertSnippet (step Nothing defaultOptions {postQualified = True}) + [ "import qualified Data.Aeson as JSON (Value)" + ] + [ "import Data.Aeson qualified as JSON (Value)" + ] + +-------------------------------------------------------------------------------- +case35 :: Assertion +case35 = assertSnippet (step Nothing defaultOptions {postQualified = True}) + [ "import Data.Aeson qualified as JSON (Value)" + ] + [ "import Data.Aeson qualified as JSON (Value)" + ] + +-------------------------------------------------------------------------------- +case36 :: Assertion +case36 = assertSnippet (step Nothing defaultOptions {postQualified = True}) + [ "import qualified Data.Aeson as JSON (Value)" + , "import qualified Data.Aeson as JSON (encode, decode)" + ] + [ "import Data.Aeson qualified as JSON (Value, decode, encode)" + ] + +-------------------------------------------------------------------------------- +case37 :: Assertion +case37 = assertSnippet (step Nothing defaultOptions {postQualified = True}) + [ "import Data.Aeson qualified as JSON (Value)" + , "import Data.Aeson qualified as JSON (encode, decode)" + ] + [ "import Data.Aeson qualified as JSON (Value, decode, encode)" + ]